Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

IBeacon RadarScanView the ibeacon Visualization is Intermittent #140

Open
micfio opened this issue Sep 2, 2024 · 0 comments
Open

IBeacon RadarScanView the ibeacon Visualization is Intermittent #140

micfio opened this issue Sep 2, 2024 · 0 comments

Comments

@micfio
Copy link

micfio commented Sep 2, 2024

Hello I Have This Code in flutter:
`import 'dart:async';
import 'dart:math';
import 'package:flutter/material.dart';
import 'package:flutter_beacon/flutter_beacon.dart';
import 'package:permission_handler/permission_handler.dart';
import 'package:simple_sensor/simple_sensor.dart';

void main() async {
WidgetsFlutterBinding.ensureInitialized();
await flutterBeacon.initializeScanning; // Initialize flutter_beacon
runApp(MyApp());
}

class MyApp extends StatelessWidget {
@OverRide
Widget build(BuildContext context) {
return MaterialApp(
title: 'Beacon Locator',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: RadarScreen(),
);
}
}

class RadarScreen extends StatefulWidget {
@OverRide
_RadarScreenState createState() => _RadarScreenState();
}

class _RadarScreenState extends State {
final StreamController _beaconEventsController =
StreamController.broadcast();
List _beacons = [];
bool _haveDetected = false;

// Sensors
StreamSubscription? _accelerometerSubscription;
StreamSubscription? _magnetometerSubscription;
StreamSubscription? _beaconSubscription;

List _accelerometerValues = [0.0, 0.0, 0.0];
List _magnetometerValues = [0.0, 0.0, 0.0];
double _bearing = 0.0;

final AngleLowpassFilter _angleLowpassFilter = AngleLowpassFilter();

final Region _beaconRegion = Region(
identifier: 'Exit 3p LA',
proximityUUID: 'FDA50693-A4E2-4FB1-AFCF-C6EB07647820');

@OverRide
void initState() {
super.initState();
requestPermissions().then(() {
_startSensors();
_startBeaconScan();
}).catchError((e) {
print("Permission request failed: $e");
});
}

@OverRide
void dispose() {
_stopSensors();
_beaconSubscription?.cancel();
_beaconEventsController.close();
super.dispose();
}

Future _requestPermissions() async {
var status = await [
Permission.bluetoothScan,
Permission.location,
].request();

if (status[Permission.location]?.isDenied ?? true) {
  throw Exception("Location permission is required to scan beacons.");
}

}

void _startSensors() {
_accelerometerSubscription =
simpleSensor.accelerometer.listen((AccelerometerEvent event) {
setState(() {
_accelerometerValues = [event.x, event.y, event.z];
_calculateBearing();
});
});

_magnetometerSubscription =
    simpleSensor.magnetometer.listen((MagnetometerEvent event) {
  setState(() {
    _magnetometerValues = [event.x, event.y, event.z];
    _calculateBearing();
  });
});

}

void _stopSensors() {
_accelerometerSubscription?.cancel();
_magnetometerSubscription?.cancel();
}

void _calculateBearing() {
if (_accelerometerValues.isEmpty || _magnetometerValues.isEmpty) return;

double ax = _accelerometerValues[0];
double ay = _accelerometerValues[1];
double az = _accelerometerValues[2];
double mx = _magnetometerValues[0];
double my = _magnetometerValues[1];
double mz = _magnetometerValues[2];

double roll = atan2(ay, az);
double pitch = atan2(-ax, sqrt(ay * ay + az * az));

double declination = 0; // Adjust based on your location
double azimuth = atan2(
    my * cos(roll) - mz * sin(roll),
    mx * cos(pitch) +
        my * sin(roll) * sin(pitch) +
        mz * cos(roll) * sin(pitch));

_angleLowpassFilter.add(azimuth);
double azimuthInDegrees =
    (_angleLowpassFilter.average() * (180 / pi) + 360) % 360;
setState(() {
  _bearing = azimuthInDegrees;
});

}

Future _startBeaconScan() async {
final regions = [
_beaconRegion,
];

_beaconSubscription = flutterBeacon.ranging(regions).listen(
  (RangingResult result) {
    List<BeaconData> updatedBeacons = [];
    for (var beacon in result.beacons) {
      final beaconData =
          BeaconData.fromBeacon(beacon, _bearing, _beaconRegion.identifier);
      updatedBeacons.add(beaconData);
    }
    setState(() {
      _beacons = updatedBeacons;
      _haveDetected = true;
    });
  },
  onError: (error) {
    print("Error ranging beacons: $error");
  },
);

}

@OverRide
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Beacon Locator'),
centerTitle: true,
),
body: Center(
child: CustomPaint(
painter: RadarPainter(
bearing: _bearing,
beacons: _beacons,
haveDetected: _haveDetected,
),
child: Container(
width: 300,
height: 300,
),
),
),
);
}
}

class RadarPainter extends CustomPainter {
final double bearing;
final List beacons;
final bool haveDetected;
final double maxDistance = 15.0;

RadarPainter({
required this.bearing,
required this.beacons,
required this.haveDetected,
});

@OverRide
void paint(Canvas canvas, Size size) {
final center = Offset(size.width / 2, size.height / 2);
final radius = min(size.width, size.height) / 2 - 8;

final gridPaint = Paint()
  ..color = Colors.orangeAccent
  ..style = PaintingStyle.stroke
  ..strokeWidth = 2.0;

canvas.drawCircle(center, radius, gridPaint);
canvas.drawCircle(center, radius * 0.75, gridPaint);
canvas.drawCircle(center, radius * 0.5, gridPaint);
canvas.drawCircle(center, radius * 0.25, gridPaint);

canvas.drawLine(Offset(center.dx, center.dy - radius),
    Offset(center.dx, center.dy + radius), gridPaint);
canvas.drawLine(Offset(center.dx - radius, center.dy),
    Offset(center.dx + radius, center.dy), gridPaint);

canvas.drawLine(Offset(center.dx - 4, center.dy - 4),
    Offset(center.dx + 4, center.dy + 4), gridPaint);
canvas.drawLine(Offset(center.dx - 4, center.dy + 4),
    Offset(center.dx + 4, center.dy - 4), gridPaint);

_drawDistanceLabels(canvas, center, radius);

if (haveDetected) {
  final beaconPaint = Paint()
    ..color = Colors.red
    ..style = PaintingStyle.fill;

  final textStyle = TextStyle(
    color: Colors.green,
    fontSize: 12,
  );

  for (var beacon in beacons) {
    double distance = beacon.distance;
    if (distance > maxDistance) continue;

    double angle = (beacon.angle - bearing) * (pi / 180);

    double beaconX =
        center.dx + (distance / maxDistance) * radius * sin(angle);
    double beaconY =
        center.dy - (distance / maxDistance) * radius * cos(angle);

    canvas.drawCircle(Offset(beaconX, beaconY), 8, beaconPaint);

    final textSpan = TextSpan(
      text: beacon.identifier,
      style: textStyle,
    );

    final textPainter = TextPainter(
      text: textSpan,
      textAlign: TextAlign.center,
      textDirection: TextDirection.ltr,
    )..layout();

    textPainter.paint(
        canvas,
        Offset(beaconX - textPainter.width / 2,
            beaconY - textPainter.height - 10));
  }
}

}

void _drawDistanceLabels(Canvas canvas, Offset center, double radius) {
final textStyle = TextStyle(
color: Colors.black,
fontSize: 12,
);

final textPainter = TextPainter(
  textAlign: TextAlign.center,
  textDirection: TextDirection.ltr,
);

for (int i = 1; i <= 4; i++) {
  double dist = (maxDistance / 4) * i;
  String text = '${dist.toStringAsFixed(1)}m';
  textPainter.text = TextSpan(text: text, style: textStyle);
  textPainter.layout();
  double textHeight = textPainter.height;
  canvas.save();
  canvas.translate(
      center.dx, center.dy - (radius / 4) * i + textHeight / 2);
  textPainter.paint(canvas, Offset(-textPainter.width / 2, 0));
  canvas.restore();
}

}

@OverRide
bool shouldRepaint(covariant CustomPainter oldDelegate) => true;
}

class AngleLowpassFilter {
final List _values = [];
final int _windowSize = 10;

void add(double value) {
_values.add(value);
if (_values.length > _windowSize) {
_values.removeAt(0);
}
}

double average() {
if (_values.isEmpty) return 0.0;
return _values.reduce((a, b) => a + b) / _values.length;
}
}

class BeaconData {
final double distance;
final double angle;
final String identifier;

BeaconData({
required this.distance,
required this.angle,
required this.identifier,
});

factory BeaconData.fromBeacon(
Beacon beacon, double bearing, String regionIdentifier) {
double beaconAngle = calculateAngle(beacon, bearing);

return BeaconData(
  distance: beacon.accuracy,
  angle: beaconAngle,
  identifier: regionIdentifier,
);

}

static double calculateAngle(Beacon beacon, double bearing) {
double rssi = beacon.rssi.toDouble();
double maxRssi = -30; // RSSI at 1 meter (example)
double minRssi = -100; // RSSI at max distance (example)

double normalizedRssi = (rssi - minRssi) / (maxRssi - minRssi);
double angle = normalizedRssi * 360.0;
double adjustedAngle = (bearing + angle) % 360;

return adjustedAngle;

}
}
`
The visualization of iBeacons on radar sometime is intermittent . Can Someone test the code for to know if depending on FLutter_Beacon plugin or by another piece of my code ? Thanks in Advances

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant