Closed v11 closed 1 year ago
ios传入的UUID格式是两位 9CF1 android的是00009cf1-0000-1000-8000-00805f9b34fb 我这边特别处理下貌似可以了
Not sure what you mean. Can i just switch to this shorter ID?
Uuid.parse("9CF1")
Now i get the following exception:
_Exception (Exception: GenericFailure<CharacteristicValueUpdateError>(code: CharacteristicValueUpdateError.unknown, message: "A service C201 is not found in the peripheral E8CB3A98-9F28-F36A-2DA5-BD8A951130E6 (make sure it has been discovered)"))
Could you (or someone else) be a bit more specific? I have read that in IOS you need to have this 16bit UUID. But is it for the service or for the characteristic or both?
Here is my full code:
// Summary
// This view is using a bluetooth (ble) connection to get network names (ssid) from the device.
// Check status with checkBluetoothStatus(). Status needs to be ready.
// Start scanning for bluetooth devices with startBluetoothScan()
// Connect to the device with connectBluetoothDevice()
// Multiple bluetooth errors will be handled:
// - No bluetooth
// - No location services (Android only)
// - bluetooth not authorized
// Read from ble device with readFromBluetoothDevice()
// Decode data and save ssid into a list ssidList
// Show list of networks in the view
import 'dart:async';
import 'dart:convert';
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:flutter_reactive_ble/flutter_reactive_ble.dart';
import 'package:mynotes/components/spacer.dart';
import 'package:mynotes/views/setup/views/setup_03_wifi_password_view.dart';
import 'package:mynotes/views/setup/services/setup_constants.dart';
import 'package:mynotes/views/setup/services/ssid_name.dart';
import 'package:mynotes/views/setup/services/setup_services.dart';
import 'package:app_settings/app_settings.dart';
import 'package:page_route_transition/page_route_transition.dart';
class SetupWifiView extends StatefulWidget {
final SetupType setupType;
const SetupWifiView({
super.key,
required this.setupType,
});
@override
State<SetupWifiView> createState() => _SetupWifiViewState();
}
class _SetupWifiViewState extends State<SetupWifiView> with WidgetsBindingObserver {
// BLE: A Universally Unique Identifier (UUID) is a globally unique 128-bit (16-byte) number that is used to identify profiles, services, and data types in a Generic Attribute (GATT) profile.
final Uuid serviceUuid = Uuid.parse("4fafc201-1fb5-459e-8fcc-c5c9c331914b");
final Uuid characteristicUuid = Uuid.parse("beb5483e-36e1-4688-b7f5-ea07361b26a8");
// BLE: GUI state management: Location Services
bool isBleStatusUnauthorized = false;
// BLE: GUI state management: Location Services
bool isLocationServicesDisabled = false;
// BLE: GUI state management: Not used yet
bool isBluetoothPoweredOff = false;
// BLE: GUI state management: BLE device is connected
bool isBluetoothConnected = false;
// BLE: GUI state management: BLE device has sent Data
bool hasBluetoothData = false;
// BLE: GUI state management: BLE device has sent Data
bool isBottomSheet = false;
// BLE: Bluetooth related variables
final flutterReactiveBle = FlutterReactiveBle();
late DiscoveredDevice bleDevice;
StreamSubscription<BleStatus>? statusStream;
StreamSubscription<DiscoveredDevice>? scanStream;
StreamSubscription<ConnectionStateUpdate>? connectedStream;
// BLE: List of wifi SSIDname instances
List<SSIDname>? ssidList;
// Device ID (mac address)
late String macaddress;
@override
void initState() {
super.initState();
print('02 wifi view in ${widget.setupType}');
// Get notified when App is paused/resume
WidgetsBinding.instance.addObserver(this);
// Check BLE Status
checkBluetoothStatus();
}
@override
void dispose() {
super.dispose();
// Remove binding
WidgetsBinding.instance.removeObserver(this);
// Disconnect the ble device
disconnectDevice();
}
@override
Future<void> didChangeAppLifecycleState(AppLifecycleState state) async {
// This functions gets called when the app is paused/resume
super.didChangeAppLifecycleState(state);
if (state == AppLifecycleState.inactive) {
print('app observer: app inactive');
// Disconnect the ble device
disconnectDevice();
} else if (state == AppLifecycleState.resumed) {
print('app observer: app resumed');
// Disconnect the ble device
checkBluetoothStatus();
}
}
Future<void> checkBluetoothStatus() async {
// Reset all state
setState(() {
isBleStatusUnauthorized = false;
isLocationServicesDisabled = false;
isBluetoothPoweredOff = false;
isBluetoothConnected = false;
hasBluetoothData = false;
});
// It is important to initialize bluetooth completetly
// On iOS in release mode, this will lead to errors if this is not done.
statusStream = flutterReactiveBle.statusStream.listen((event) {
switch (event) {
case BleStatus.unauthorized:
{
// Location services are Android only
print('bluetooth not authorized');
setState(() {
isBleStatusUnauthorized = true;
isLocationServicesDisabled = false;
isBluetoothPoweredOff = false;
isBluetoothConnected = false;
hasBluetoothData = false;
});
break;
}
case BleStatus.locationServicesDisabled:
{
// Location services are Android only
print('location services off');
setState(() {
isBleStatusUnauthorized = false;
isLocationServicesDisabled = true;
isBluetoothPoweredOff = false;
isBluetoothConnected = false;
hasBluetoothData = false;
});
break;
}
case BleStatus.poweredOff:
{
print('bluetooth is off');
setState(() {
isBleStatusUnauthorized = false;
isBluetoothPoweredOff = true;
isLocationServicesDisabled = false;
isBluetoothConnected = false;
hasBluetoothData = false;
});
break;
}
case BleStatus.ready:
{
print('bluetooth is ready');
setState(() {
isBleStatusUnauthorized = false;
isBluetoothPoweredOff = false;
isLocationServicesDisabled = false;
isBluetoothConnected = false;
hasBluetoothData = false;
});
// Now start scanning
startBluetoothScanning();
break;
}
default:
}
});
}
Future<void> startBluetoothScanning() async {
scanStream = flutterReactiveBle.scanForDevices(withServices: []).listen((device) {
print('scanning for ble devices ...');
// Search for ble device name
if (device.name == 'Inklay') {
print('inklay found');
// Assign device
bleDevice = device;
// Connect to device
connectBluetoothDevice(device);
}
});
}
Future<void> connectBluetoothDevice(DiscoveredDevice device) async {
// Stop scanning
print('scanning stopped');
await scanStream!.cancel();
// Connect to device and listen to state change
Stream<ConnectionStateUpdate> currentConnectionStream = flutterReactiveBle.connectToAdvertisingDevice(
id: device.id,
prescanDuration: const Duration(seconds: 2),
withServices: [],
);
print('connect to inklay ...');
// Connect to device and listen to state change
connectedStream = currentConnectionStream.listen((event) {
switch (event.connectionState) {
// Connected
case DeviceConnectionState.connected:
{
print('connected to inklay');
setState(() {
isBluetoothConnected = true;
});
// Read bluetooth data
readFromBluetoothDevice(device);
break;
}
// Disconnect
case DeviceConnectionState.disconnected:
{
print('device disconnected');
// Navigate back to Bluetooth Welcome Screen
// Navigator.pop(context);
break;
}
default:
}
});
}
disconnectDevice() {
// This function disconnects the app from the device and cancel all the subscribed streams
print('disconnect device');
// Stop checking for status updates
if (statusStream != null) {
print('- bluetooth status stream stopped');
statusStream!.cancel();
statusStream == null;
}
// Stop scanning
if (scanStream != null) {
print('- scanning stopped');
scanStream!.cancel();
scanStream == null;
}
// Cancel stream to disconnect a ble device
if (connectedStream != null) {
print('- device disconnected');
connectedStream!.cancel();
connectedStream == null;
}
}
Future<void> readFromBluetoothDevice(DiscoveredDevice device) async {
// This function reads ssid network names from the device
print('read from bluetooth device ...');
// Characteristic
final characteristic = QualifiedCharacteristic(serviceId: serviceUuid, characteristicId: characteristicUuid, deviceId: device.id);
// Read characteristic
// Needs to be decoded: [91, 123, 34, 115, 115, 105, 100, 34, 58, 34, 76, 117, 107, 97, 115 ... ]
final response = await flutterReactiveBle.readCharacteristic(characteristic);
if (response.isNotEmpty) {
// try {
if (!response.toString().contains("[]")) {
// Decoding
print("response : " + response.toString());
// [{macaddress: 24:6F:28:D1:5D:DC}, {"ssid":"Lukas-Wi-Fi"},{"ssid":"mkt-45633"},{"ssid":"MatchX_MX190x_RHYM"},{"ssid":"UPC9541478_2GEXT"}]
var readval = DecimalToText(response.toString(), "0x${serviceUuid.toString().toUpperCase().substring(4, 8)}");
print("readval : " + readval.toString());
print("hello");
// {"tags": [{macaddress: 24:6F:28:D1:5D:DC}, {"ssid":"Lukas-Wi-Fi"},{"ssid":"mkt-45633"},{"ssid":"MatchX_MX190x_RHYM"},{"ssid":"UPC9541478_2GEXT"}]}
String jstring = '{"tags": ' + readval.toString() + '}';
print("jstring : " + jstring.toString());
print("hello");
// [{macaddress: 24:6F:28:D1:5D:DC}, {ssid: Lukas-Wi-Fi}, {ssid: mkt-45633}, {ssid: MatchX_MX190x_RHYM}, {ssid: UPC9541478_2GEXT}]
// List: Cecodes strings to JSON objects.
var tagObjsJson = jsonDecode(jstring)['tags'] as List;
print("tagObjsJson : " + tagObjsJson.toString());
// Get Macadress and remove from List
// print("Macaddress: " + tagObjsJson[0]['macaddress'].toString());
macaddress = tagObjsJson[0]['macaddress'].toString();
tagObjsJson.removeAt(0);
// Create List with ssid instance
List<SSIDname> tagObjs = tagObjsJson.map((tagJson) => SSIDname.fromJson(tagJson)).toList();
ssidList = tagObjs;
print('read successful');
// Assign list
setState(() {
hasBluetoothData = true;
});
}
// } catch (e) {
// print('error decoding');
// }
}
}
Widget widgetWifiNetwork(SSIDname? htResultNetwork) {
return Container(
padding: const EdgeInsets.all(15),
margin: const EdgeInsets.only(bottom: 2),
decoration: const BoxDecoration(
border: Border(
bottom: BorderSide(
color: Color.fromARGB(255, 185, 185, 185),
width: 1.0,
),
)),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text('${htResultNetwork?.ssid}'),
ElevatedButton(
onPressed: () {
// Disconnect
disconnectDevice();
Navigator.push(
context,
PageRouteTransitionBuilder(
page: SetupWifiPasswordView(
ssid: htResultNetwork!.ssid,
macaddress: macaddress,
setupType: widget.setupType,
),
effect: TransitionEffect.none),
);
},
child: const Text('Connect'),
),
],
),
);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: SetupType.wifi == widget.setupType ? const Text('Change Wi-Fi') : const Text('Inklay Setup'),
),
body: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
//
// State 0
// How to enable this state:
// Android: Remove location permission from app info
// IOS: Remove bluetooth access from app settings
Visibility(
visible: isBleStatusUnauthorized,
child: Container(
padding: const EdgeInsets.all(15),
// child: const Text('Please check your Bluetooth permissions in settings.'),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Platform.isAndroid
? const Text('Please enable Location permission in App Info.')
: const Text('Please allow Bluetooth access in App Settings.'),
const ComponentsSpacer.small(),
ElevatedButton(
onPressed: (() {
AppSettings.openAppSettings();
}),
child: const Text('Open App Settings'),
),
],
),
),
),
//
// State 0
Visibility(
visible: !isBleStatusUnauthorized,
child: Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
//
// 1
// How to enable this state:
// Android: Turn off settings > location > use location
// IOS: Not available
Visibility(
visible: isLocationServicesDisabled,
child: Container(
padding: const EdgeInsets.all(15),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text('Please turn on Location.'),
const ComponentsSpacer.small(),
// Works IOS
ElevatedButton(
onPressed: (() {
AppSettings.openLocationSettings();
}),
child: const Text('Open Location Settings'),
),
],
),
),
),
//
// 1
Visibility(
visible: !isLocationServicesDisabled,
child: Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
//
// 2
// Bluetooth OFF
Visibility(
visible: isBluetoothPoweredOff,
child: Container(
padding: const EdgeInsets.all(15.0),
child: const Text('Please turn on Bluetooth'),
),
),
//
// 2
// Bluetooth ON
Visibility(
visible: !isBluetoothPoweredOff,
child: Container(
padding: const EdgeInsets.all(15.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
//
// 3
Visibility(
visible: !isBluetoothConnected,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: const [
Text('BLE: Connecting ...'),
SizedBox(
height: 15.0,
width: 15.0,
child: CircularProgressIndicator(
strokeWidth: 2,
),
),
],
),
),
//
// 3
Visibility(
visible: isBluetoothConnected,
child: const Text('BLE: Connected to Inklay'),
),
],
),
),
),
// Instructions: Only show when data is not available
Visibility(
visible: !hasBluetoothData && !isBleStatusUnauthorized && !isLocationServicesDisabled && !isBluetoothPoweredOff,
child: Expanded(
child: Container(
// height: double.infinity,
decoration: const BoxDecoration(
// color: Colors.red,
border: Border(
top: BorderSide(
color: Color.fromARGB(255, 185, 185, 185),
width: 1.0,
),
),
),
padding: const EdgeInsets.all(15),
child: Column(
// shrinkWrap: true,
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: const [
Text(
'Enable Bluetooth paring',
style: TextStyle(fontWeight: FontWeight.bold),
),
ComponentsSpacer.small(),
Text('1. Remove the magnetic front cover.'),
Text('2. Press and hold the power button for 2 seconds.'),
Text('3. The LED will turn blue.'),
],
),
),
),
),
// Wifi networks: Only show when data is available
Visibility(
visible: hasBluetoothData,
child: Expanded(
child: Container(
decoration: const BoxDecoration(
border: Border(
top: BorderSide(
color: Color.fromARGB(255, 185, 185, 185),
width: 1.0,
),
),
),
child: ListView.builder(
padding: const EdgeInsets.all(0.0),
itemCount: ssidList?.length,
itemBuilder: (BuildContext context, int index) {
return widgetWifiNetwork(ssidList?[index]);
},
),
),
),
),
],
),
),
),
],
),
),
),
],
),
);
}
}
after case DeviceConnectionState.connected: try to discoverServices,like below
var resUUID = await flutterReactiveBle.discoverServices(id);
resUUID.forEach((element) {
debugPrint("resUUID" + element.serviceId.toString());
});
This are the services:
I/flutter (11430): resUUID 00001801-0000-1000-8000-00805f9b34fb
I/flutter (11430): resUUID 00001800-0000-1000-8000-00805f9b34fb
I/flutter (11430): resUUID 4fafc201-1fb5-459e-8fcc-c5c9c331914b
And what should i do with this services?
I think, the problem was related to a buffer which was too small. The JSON response did not have a complete body including closing brackets, which then failed on decoding.
First of all, thank you very much for this library.
Describe the bug I have an app where i read a characteristic from a BLE device (ESP32). The BLE device is scanning wi-fi networks and provides this data over the characteristic. Via my app i read this characteristic.
Everything is working fine. But yesterday I have experienced that sometimes (in a case where a lot of networks are disovered), not the complete data gets transmitted to my app. In the data, then some closing brackets are missinng and the decoding fails.
I have debugged this issue with the BT inspector app and there the full data is available and can be read.
In the flutter_reactive_ble package it is stated the follwing:
I am wondering if this is the issue. But i have no idea how to workaround.
Data i recieve:
Data i expect to recieve:
This is how i read the data:
Smartphone / tablet
Peripheral device
Additional context