最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

How to Detect iBeacons (Apple & Feasycom Manufacturer ID 0x0A2D) Using flutter_blue_plus in Flutter? - Stack Overflow

programmeradmin10浏览0评论

I am a student learning Flutter and currently working on a project involving BLE (Bluetooth Low Energy) communication. My goal is to detect and receive data from iBeacons, specifically:

Apple iBeacons Feasycom beacons with Manufacturer ID 0x0A2D I am using the flutter_blue_plus package for BLE scanning, but I am facing challenges in detecting and differentiating these beacons correctly.

Here is what I have tried so far:

I successfully implemented BLE scanning, but identifying iBeacons and parsing their data (especially Feasycom with the manufacturer ID 0x0A2D) has been difficult. I attempted to filter by manufacturer-specific data without consistent results. Questions: How can I reliably detect both Apple and Feasycom iBeacons using flutter_blue_plus? What is the correct way to filter beacons by manufacturer-specific data in Flutter? Is there a better approach or package recommendation for cross-platform (Android) iBeacon detection in Flutter? Any code examples or detailed explanations would be highly appreciated.

Thank you in advance!

import 'package:flutter/material.dart';
import 'package:flutter_blue_plus/flutter_blue_plus.dart';
import 'package:permission_handler/permission_handler.dart';

void main() => runApp(const MyApp());

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'BLE Scanner',
      theme: ThemeData(primarySwatch: Colors.blue, useMaterial3: true),
      home: const HomeScreen(),
    );
  }
}

class HomeScreen extends StatefulWidget {
  const HomeScreen({super.key});

  @override
  State<HomeScreen> createState() => _HomeScreenState();
}

class _HomeScreenState extends State<HomeScreen> {
  late FlutterBluePlus flutterBlue;
  Map<String, ScanResult> beaconResults = {};
  bool isScanning = false;

  @override
  void initState() {
    super.initState();
    flutterBlue = FlutterBluePlus();
    requestPermissions();
  }

  Future<void> requestPermissions() async {
    await [
      Permission.bluetoothScan,
      Permission.bluetoothConnect,
      Permission.locationWhenInUse,
    ].request();
  }

  void startScan() {
    setState(() {
      isScanning = true;
      beaconResults.clear();
    });

    flutterBlue.startScan(
      withServices: [],
      timeout: const Duration(seconds: 10),
    );

    flutterBlue.scanResults.listen((results) {
      for (ScanResult result in results) {
        if (isTargetBeacon(result)) {
          setState(() {
            beaconResults[result.device.id.id] = result;
          });
        }
      }
    }).onDone(() {
      setState(() {
        isScanning = false;
      });
    });
  }

  
  bool isTargetBeacon(ScanResult result) {
    final manufacturerData = result.advertisementData.manufacturerData;

    if (manufacturerData.isEmpty) return false;

    const feasycomId = 0x0A2D;
    const appleIBeaconId = 0x004C;

    if (manufacturerData.containsKey(feasycomId) || manufacturerData.containsKey(appleIBeaconId)) {
      final data = manufacturerData[feasycomId] ?? manufacturerData[appleIBeaconId];
      if (data != null) {
        debugPrint('Beacon Data: ${data.map((byte) => byte.toRadixString(16)).toList()}');
      }
      return true;
    }
    return false;
  }

  Future<void> connectToDevice(BluetoothDevice device) async {
    try {
      await device.connect();
      ScaffoldMessenger.of(context).showSnackBar(
        SnackBar(content: Text('Connected to ${device.name} (${device.id})')),
      );
    } catch (e) {
      ScaffoldMessenger.of(context).showSnackBar(
        SnackBar(content: Text('Error connecting: $e')),
      );
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('BLE Scanner'),
        actions: [
          IconButton(
            icon: Icon(isScanning ? Icons.stop : Icons.refresh),
            onPressed: isScanning ? null : startScan,
          ),
        ],
      ),
      body: beaconResults.isEmpty
          ? Center(
              child: Text(
                isScanning
                    ? 'Scanning for BLE devices...'
                    : 'No BLE devices found. Tap refresh to scan again.',
                style: const TextStyle(fontSize: 16),
              ),
            )
          : ListView(
              children: beaconResults.values.map((result) {
                final device = result.device;
                final rssi = result.rssi;
                return ListTile(
                  title: Text(device.name.isNotEmpty ? device.name : 'Unknown Device'),
                  subtitle: Text('MAC: ${device.id}, RSSI: $rssi dBm'),
                  trailing: const Icon(Icons.bluetooth),
                  onTap: () async {
                    await connectToDevice(device);
                  },
                );
              }).toList(),
            ),
    );
  }
}

I tried using the flutter_blue_plus package to scan BLE devices and filter by manufacturer-specific data for both Apple iBeacons and Feasycom beacons (with manufacturer ID 0x0A2D). I expected the scan to detect and differentiate these beacons, providing UUID, major, and minor values. However, the scan either didn't detect the beacons or failed to parse the iBeacon data correctly.

I am a student learning Flutter and currently working on a project involving BLE (Bluetooth Low Energy) communication. My goal is to detect and receive data from iBeacons, specifically:

Apple iBeacons Feasycom beacons with Manufacturer ID 0x0A2D I am using the flutter_blue_plus package for BLE scanning, but I am facing challenges in detecting and differentiating these beacons correctly.

Here is what I have tried so far:

I successfully implemented BLE scanning, but identifying iBeacons and parsing their data (especially Feasycom with the manufacturer ID 0x0A2D) has been difficult. I attempted to filter by manufacturer-specific data without consistent results. Questions: How can I reliably detect both Apple and Feasycom iBeacons using flutter_blue_plus? What is the correct way to filter beacons by manufacturer-specific data in Flutter? Is there a better approach or package recommendation for cross-platform (Android) iBeacon detection in Flutter? Any code examples or detailed explanations would be highly appreciated.

Thank you in advance!

import 'package:flutter/material.dart';
import 'package:flutter_blue_plus/flutter_blue_plus.dart';
import 'package:permission_handler/permission_handler.dart';

void main() => runApp(const MyApp());

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'BLE Scanner',
      theme: ThemeData(primarySwatch: Colors.blue, useMaterial3: true),
      home: const HomeScreen(),
    );
  }
}

class HomeScreen extends StatefulWidget {
  const HomeScreen({super.key});

  @override
  State<HomeScreen> createState() => _HomeScreenState();
}

class _HomeScreenState extends State<HomeScreen> {
  late FlutterBluePlus flutterBlue;
  Map<String, ScanResult> beaconResults = {};
  bool isScanning = false;

  @override
  void initState() {
    super.initState();
    flutterBlue = FlutterBluePlus();
    requestPermissions();
  }

  Future<void> requestPermissions() async {
    await [
      Permission.bluetoothScan,
      Permission.bluetoothConnect,
      Permission.locationWhenInUse,
    ].request();
  }

  void startScan() {
    setState(() {
      isScanning = true;
      beaconResults.clear();
    });

    flutterBlue.startScan(
      withServices: [],
      timeout: const Duration(seconds: 10),
    );

    flutterBlue.scanResults.listen((results) {
      for (ScanResult result in results) {
        if (isTargetBeacon(result)) {
          setState(() {
            beaconResults[result.device.id.id] = result;
          });
        }
      }
    }).onDone(() {
      setState(() {
        isScanning = false;
      });
    });
  }

  
  bool isTargetBeacon(ScanResult result) {
    final manufacturerData = result.advertisementData.manufacturerData;

    if (manufacturerData.isEmpty) return false;

    const feasycomId = 0x0A2D;
    const appleIBeaconId = 0x004C;

    if (manufacturerData.containsKey(feasycomId) || manufacturerData.containsKey(appleIBeaconId)) {
      final data = manufacturerData[feasycomId] ?? manufacturerData[appleIBeaconId];
      if (data != null) {
        debugPrint('Beacon Data: ${data.map((byte) => byte.toRadixString(16)).toList()}');
      }
      return true;
    }
    return false;
  }

  Future<void> connectToDevice(BluetoothDevice device) async {
    try {
      await device.connect();
      ScaffoldMessenger.of(context).showSnackBar(
        SnackBar(content: Text('Connected to ${device.name} (${device.id})')),
      );
    } catch (e) {
      ScaffoldMessenger.of(context).showSnackBar(
        SnackBar(content: Text('Error connecting: $e')),
      );
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('BLE Scanner'),
        actions: [
          IconButton(
            icon: Icon(isScanning ? Icons.stop : Icons.refresh),
            onPressed: isScanning ? null : startScan,
          ),
        ],
      ),
      body: beaconResults.isEmpty
          ? Center(
              child: Text(
                isScanning
                    ? 'Scanning for BLE devices...'
                    : 'No BLE devices found. Tap refresh to scan again.',
                style: const TextStyle(fontSize: 16),
              ),
            )
          : ListView(
              children: beaconResults.values.map((result) {
                final device = result.device;
                final rssi = result.rssi;
                return ListTile(
                  title: Text(device.name.isNotEmpty ? device.name : 'Unknown Device'),
                  subtitle: Text('MAC: ${device.id}, RSSI: $rssi dBm'),
                  trailing: const Icon(Icons.bluetooth),
                  onTap: () async {
                    await connectToDevice(device);
                  },
                );
              }).toList(),
            ),
    );
  }
}

I tried using the flutter_blue_plus package to scan BLE devices and filter by manufacturer-specific data for both Apple iBeacons and Feasycom beacons (with manufacturer ID 0x0A2D). I expected the scan to detect and differentiate these beacons, providing UUID, major, and minor values. However, the scan either didn't detect the beacons or failed to parse the iBeacon data correctly.

Share Improve this question asked Feb 5 at 7:52 nima alihosseininima alihosseini 1
Add a comment  | 

1 Answer 1

Reset to default 0

FlutterBluePlus is a singleton and should be accessed via FlutterBluePlus.instance, not by creating a new instance.

final FlutterBluePlus flutterBlue = FlutterBluePlus.instance;

By default, flutter_blue_plus only scans for devices with service UUIDs. iBeacons do not advertise services, but instead use manufacturer data.

Enable full advertisement data scanning.

flutterBlue.startScan(
  withAdvertise: true, // Ensures full advertisement packets are captured
  timeout: const Duration(seconds: 10),
);

Your current onDone() usage is incorrect because listen() does not return a Future. Instead, you need to use await inside an async function.

void startScan() {
  setState(() {
    isScanning = true;
    beaconResults.clear();
  });

  flutterBlue.startScan(
    withAdvertise: true,
    timeout: const Duration(seconds: 10),
  );

  flutterBlue.scanResults.listen((results) {
    for (ScanResult result in results) {
      if (isTargetBeacon(result)) {
        setState(() {
          beaconResults[result.device.id.id] = result;
        });
      }
    }
  });

  Future.delayed(const Duration(seconds: 10), () {
    setState(() {
      isScanning = false;
    });
  });
}

In isTargetBeacon(), you're using 0x0A2D for Feasycom, but the correct ID should be 0x0711. Try this too.

const feasycomId = 0x0711;

connectToDevice() Should Handle Disconnection** When connecting to a device, it's a good practice to disconnect before reconnecting.

Future<void> connectToDevice(BluetoothDevice device) async {
  try {
    if (device.isConnected) {
      await device.disconnect();
    }
    await device.connect();
    ScaffoldMessenger.of(context).showSnackBar(
      SnackBar(content: Text('Connected to ${device.name} (${device.id})')),
    );
  } catch (e) {
    ScaffoldMessenger.of(context).showSnackBar(
      SnackBar(content: Text('Error connecting: $e')),
    );
  }
}

If you want to extract UUID, Major, Minor, and TxPower, update isTargetBeacon():

bool isTargetBeacon(ScanResult result) {
  final manufacturerData = result.advertisementData.manufacturerData;
  const appleIBeaconId = 0x004C;
  const feasycomId = 0x0711;

  for (var entry in manufacturerData.entries) {
    int manufacturerId = entry.key;
    List<int> rawData = entry.value;

    if (manufacturerId == appleIBeaconId || manufacturerId == feasycomId) {
      if (rawData.length >= 23) {
        String uuid = rawData.sublist(2, 18).map((e) => e.toRadixString(16).padLeft(2, '0')).join();
        int major = (rawData[18] << 8) | rawData[19];
        int minor = (rawData[20] << 8) | rawData[21];
        int txPower = rawData[22];

        debugPrint("iBeacon detected: UUID=$uuid, Major=$major, Minor=$minor, TxPower=$txPower");
        return true;
      }
    }
  }
  return false;
}

Try this full version :

import 'package:flutter/material.dart';
import 'package:flutter_blue_plus/flutter_blue_plus.dart';
import 'package:permission_handler/permission_handler.dart';

void main() => runApp(const MyApp());

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'BLE Scanner',
      theme: ThemeData(primarySwatch: Colors.blue, useMaterial3: true),
      home: const HomeScreen(),
    );
  }
}

class HomeScreen extends StatefulWidget {
  const HomeScreen({super.key});

  @override
  State<HomeScreen> createState() => _HomeScreenState();
}

class _HomeScreenState extends State<HomeScreen> {
  final FlutterBluePlus flutterBlue = FlutterBluePlus.instance;
  Map<String, ScanResult> beaconResults = {};
  bool isScanning = false;

  @override
  void initState() {
    super.initState();
    requestPermissions();
  }

  Future<void> requestPermissions() async {
    await [
      Permission.bluetoothScan,
      Permission.bluetoothConnect,
      Permission.bluetooth,
      Permission.locationWhenInUse,
    ].request();
  }

  void startScan() {
    setState(() {
      isScanning = true;
      beaconResults.clear();
    });

    flutterBlue.startScan(
      withAdvertise: true,
      timeout: const Duration(seconds: 10),
    );

    flutterBlue.scanResults.listen((results) {
      for (ScanResult result in results) {
        if (isTargetBeacon(result)) {
          setState(() {
            beaconResults[result.device.id.id] = result;
          });
        }
      }
    });

    Future.delayed(const Duration(seconds: 10), () {
      setState(() {
        isScanning = false;
      });
    });
  }

  bool isTargetBeacon(ScanResult result) {
    final manufacturerData = result.advertisementData.manufacturerData;
    const appleIBeaconId = 0x004C;
    const feasycomId = 0x0711;

    for (var entry in manufacturerData.entries) {
      int manufacturerId = entry.key;
      List<int> rawData = entry.value;

      if (manufacturerId == appleIBeaconId || manufacturerId == feasycomId) {
        if (rawData.length >= 23) {
          String uuid = rawData.sublist(2, 18).map((e) => e.toRadixString(16).padLeft(2, '0')).join();
          int major = (rawData[18] << 8) | rawData[19];
          int minor = (rawData[20] << 8) | rawData[21];
          int txPower = rawData[22];

          debugPrint("iBeacon detected: UUID=$uuid, Major=$major, Minor=$minor, TxPower=$txPower");
          return true;
        }
      }
    }
    return false;
  }

  Future<void> connectToDevice(BluetoothDevice device) async {
    try {
      if (device.isConnected) {
        await device.disconnect();
      }
      await device.connect();
      ScaffoldMessenger.of(context).showSnackBar(
        SnackBar(content: Text('Connected to ${device.name} (${device.id})')),
      );
    } catch (e) {
      ScaffoldMessenger.of(context).showSnackBar(
        SnackBar(content: Text('Error connecting: $e')),
      );
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('BLE Scanner'),
        actions: [
          IconButton(
            icon: Icon(isScanning ? Icons.stop : Icons.refresh),
            onPressed: isScanning ? null : startScan,
          ),
        ],
      ),
      body: beaconResults.isEmpty
          ? Center(child: Text(isScanning ? 'Scanning...' : 'No BLE devices found.'))
          : ListView(
              children: beaconResults.values.map((result) {
                final device = result.device;
                final rssi = result.rssi;
                return ListTile(
                  title: Text(device.name.isNotEmpty ? device.name : 'Unknown Device'),
                  subtitle: Text('MAC: ${device.id}, RSSI: $rssi dBm'),
                  trailing: const Icon(Icons.bluetooth),
                  onTap: () async {
                    await connectToDevice(device);
                  },
                );
              }).toList(),
            ),
    );
  }
}

与本文相关的文章

发布评论

评论列表(0)

  1. 暂无评论