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

How to receive advertising data on specific UUID? #57

Open
ethan8051 opened this issue Jul 2, 2024 · 11 comments
Open

How to receive advertising data on specific UUID? #57

ethan8051 opened this issue Jul 2, 2024 · 11 comments

Comments

@ethan8051
Copy link

Hello,

I'm working on a Flutter project using the universal_ble package for Bluetooth Low Energy communication. I'm trying to figure out how to receive advertising data specifically for the UUID 0x181A

Does the universal_ble package support receiving advertising data for specific UUIDs?

I'd appreciate any guidance, code examples, or documentation references that could help me implement this functionality.

Thank you in advance for your help!

@rohitsangwan01
Copy link
Contributor

@ethan8051 you can use scan filter to get only those devices which have this specific id in their advertising data

@ethan8051
Copy link
Author

ethan8051 commented Jul 9, 2024

Thank you for your response.

I've attempted to implement a scan filter to capture broadcast data, but it appears to be ineffective for certain devices. I've verified using the nRF Connect app that these devices are indeed broadcasting packets.

Furthermore, I have an additional question: Assuming I successfully obtain a list of devices based on the specified UUID, how can I extract the actual data content from the broadcast, rather than just the device information and UUID?

I'm aiming to interface with this device's firmware:
https://github.com/pvvx/ATC_MiThermometer?tab=readme-ov-file#bluetooth-advertising-formats

Any insights or guidance on these issues would be greatly appreciated. Thank you in advance for your help.

@rohitsangwan01
Copy link
Contributor

@ethan8051 can you provide your minimum sample code, have you tried example app ?
in example app you can add scan filter here and whatever we add in scan filter, only those devices will be discovered

also it would be helpful to see the Nrf Connect app's screenshot of the device's advertising data which you are expecting to be discovered by this plugin

and for now you can obtains these information of a discovered device, you can let us know if something is missing in this

@ethan8051
Copy link
Author

ethan8051 commented Jul 10, 2024

Thank you for your response.

The attached image is a screenshot from nRF Connect that I've been using. I'm trying to read the advertisement data from the 181A service.

However, when I attempt to add the UUID 181A to the scan filter's withServices, I'm unable to detect any devices.

Information of a discovered device:

I/flutter ( 7465): name: ATC_DD7A81
I/flutter ( 7465): rssi: -36
I/flutter ( 7465): isPaired: false
I/flutter ( 7465): isSystemDevice: null
I/flutter ( 7465): manufacturerData: []
I/flutter ( 7465): manufacturerDataHead: []
I/flutter ( 7465): services: []

I've included my source code below for reference:

import 'dart:async';

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

void main() {
  runApp(const MyApp());
}

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

  @override
  State<MyApp> createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  // List<BleScanResult> deviceList = [];
  Map<String, BleScanResult> deviceList = {};
  bool isScanning = false;
  late Timer scanTimer;

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

    requestPermission();

    // Set a scan result handler
    UniversalBle.onScanResult = (scanResult) {

      setState(() {
        deviceList[scanResult.deviceId] = scanResult;
      });
    };

    startScan();
  }

  Future<void> startScan() async {
    if (isScanning) return;

    setState(() {
      isScanning = true;
      deviceList.clear();
    });

    await UniversalBle.startScan(
      scanFilter: ScanFilter(
        // withNamePrefix: ["Meta"],
        // withServices: ["0000181a-0000-1000-8000-00805f9b34fb"],
        // withServices: ["181A"],
      ),
    );

    scanTimer = Timer(const Duration(seconds: 10), () {
      // stopScan();
    });
  }

  Future<void> stopScan() async {
    if (!isScanning) return;

    await UniversalBle.stopScan();
    scanTimer.cancel();

    setState(() {
      isScanning = false;
    });
  }

  Future requestPermission() async {
    Map<Permission, PermissionStatus> statuses = await [
      Permission.location,
      Permission.bluetooth,
      Permission.bluetoothConnect,
      Permission.bluetoothScan,
      Permission.bluetoothAdvertise
    ].request();
    print("Permission.location: ${statuses[Permission.location]}");
    print(
        "Permission.bluetoothConnect: ${statuses[Permission.bluetoothConnect]}");
    print("Permission.bluetoothScan: ${statuses[Permission.bluetoothScan]}");
    print(
        "Permission.bluetoothAdvertise: ${statuses[Permission.bluetoothAdvertise]}");
  }

  @override
  Widget build(BuildContext context) {

    deviceList.forEach((k,v) => print("got key $k with ${v.manufacturerData}"));

    final sortedDeviceList = deviceList.values.toList()
      ..sort((a, b) => b.rssi!.compareTo(a.rssi ?? 0));

    return MaterialApp(
      home: SafeArea(
        child: Scaffold(
          floatingActionButton: FloatingActionButton(
            onPressed: isScanning ? stopScan : startScan,
            child: Icon(isScanning ? Icons.stop : Icons.search),
          ),
          body: Column(
            children: [
              Expanded(
                child: ListView.builder(
                  itemCount: sortedDeviceList.length,
                  itemBuilder: (context, index) {
                    final device = sortedDeviceList.elementAt(index);
                    return ListTile(
                      leading: const Icon(Icons.device_hub),
                      title: Text("[${device.rssi}] ${device.name ?? "Unknow"}"),
                      subtitle: Text((device.manufacturerData ?? []).toString()),
                    );
                  },
                ),
              ),
            ],
          ),
        ),
      ),
    );
  }
}

@rohitsangwan01
Copy link
Contributor

@ethan8051 a quick thing to test, try to scan all devices without any filter, and print the BleDevice info of required device, and please share that, so that we can know if our plugin is discovering these required services in Advertising data

@rohitsangwan01
Copy link
Contributor

@ethan8051 i just noticed that you have already mentioned the discovered device

I/flutter ( 7465): name: ATC_DD7A81
I/flutter ( 7465): rssi: -36
I/flutter ( 7465): isPaired: false
I/flutter ( 7465): isSystemDevice: null
I/flutter ( 7465): manufacturerData: []
I/flutter ( 7465): manufacturerDataHead: []
I/flutter ( 7465): services: []

we can see that services list is empty, probably the platform on which you are trying to filter the device with this service
does not gets this services in advertisement data

on Which platform you are trying with UniversalBle
and on which platform you are testing with NrfConnect app ?

and if both platforms are different, i would suggest to run the same code on the platform on which you are testing with NrfConnect app

@ethan8051
Copy link
Author

ethan8051 commented Jul 12, 2024

@rohitsangwan01

Thank you for your response.

I scanned with filtering turned off.
UniversalBle and the NRF Connect app were run on the same Android 13 Samsung phone.

In addition, I have tried using ESP32 with Arduino + NimBLE to implement a device for broadcast testing. I set the broadcast service UUID to 181A and filled in fixed data for broadcasting. However, the result is still the same I am unable to obtain the data.

@rohitsangwan01
Copy link
Contributor

rohitsangwan01 commented Jul 15, 2024

@ethan8051 So when you start scan without filters, and you discover your device, and when you print it like this

I/flutter ( 7465): name: ATC_DD7A81
I/flutter ( 7465): rssi: -36
I/flutter ( 7465): isPaired: false
I/flutter ( 7465): isSystemDevice: null
I/flutter ( 7465): manufacturerData: []
I/flutter ( 7465): manufacturerDataHead: []
I/flutter ( 7465): services: []

do you get services list empty in discovered BleDevice object ?
if the services list is empty, that means, android probably not detecting those services as a part of advertising data, which also means that service filter will ignore this device

and if the services list is empty in universalBle but not in nrf connect, then that might be a bug in plugin,
also can you cross check on any other platform ?

@ethan8051
Copy link
Author

ethan8051 commented Jul 17, 2024

@rohitsangwan01 Thank you for your response.

The results of executing UniversalBle on Linux are as follows:

[UniversalBle] BleAdapter: ethan-System-Product-Name - 00:1A:00:00:00:00
flutter: name: ATC_DD7A81
flutter: rssi: -44
flutter: isPaired: false
flutter: isSystemDevice: null
flutter: manufacturerData: []
flutter: manufacturerDataHead: []
flutter: services: []
[UniversalBle] UnhandledDevicePropertyChanged ATC_DD7A81 A4:C1:00:00:00:00: ServiceData
flutter: name: ATC_DD7A81
flutter: rssi: -43
flutter: isPaired: false
flutter: isSystemDevice: null
flutter: manufacturerData: []
flutter: manufacturerDataHead: []
flutter: services: []

Additionally, I found that if I don't set any ScanFilter at all, the device won't appear in the list

scanFilter: ScanFilter(
    withNamePrefix: ["ATC"],
),

I tried using Python + bleak to scan for devices, this is my source code.

import asyncio
import bleak


async def main():
    devices = await bleak.BleakScanner.discover()
    for device in devices:
        if("ATC" not in device.name):
            continue

        print(f"name: {device.name}")
        print(f"details: {device.details}")


asyncio.run(main())

Execution result, it is possible to successfully obtain the 181A ServiceData.:

name: ATC_DD7A81
details:{
   "path":"/org/bluez/hci0/dev_A4_C1_00_00_00_00",
   "props":{
      "Address":"A4:C1:00:00:00:00",
      "AddressType":"public",
      "Name":"ATC_DD7A81",
      "Alias":"ATC_DD7A81",
      "Paired":false,
      "Trusted":false,
      "Blocked":false,
      "LegacyPairing":false,
      "RSSI":-51,
      "Connected":false,
      "UUIDs":[],
      "Adapter":"/org/bluez/hci0",
      "ServiceData":{
         "0000181a-0000-1000-8000-00805f9b34fb":"bytearray(b""\\x81z\\xdd8\\xc1\\xa4I\nz\\x17D\\x0ba\\xe7\\x04"")"
      },
      "ServicesResolved":false
    }
}

@rohitsangwan01
Copy link
Contributor

@ethan8051 we are using UUIDS list for displaying services list in UniversalBle

but from your Bleak response, i can see that services are discovered in ServicesData
We are not using ServicesData in UniversalBle on any platform

can you check if the Peripheral you are using can send services in UUIDs List as well ?

@rohitsangwan01
Copy link
Contributor

rohitsangwan01 commented Jul 17, 2024

Additionally, I found that if I don't set any ScanFilter at all, the device won't appear in the list
scanFilter: ScanFilter(
withNamePrefix: ["ATC"],
),

You mean, only this device does not appear, or no device discovers without filter ?

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

2 participants