Skip to content

IBeacon RadarScanView the ibeacon Visualization is Intermittent #140

@micfio

Description

@micfio

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions