chipweinberger / flutter_blue_plus

Flutter plugin for connecting and communicationg with Bluetooth Low Energy devices, on Android, iOS, macOS
Other
781 stars 471 forks source link

[Help]: Receiving incomplete message #641

Closed JEAPI-DEV closed 1 year ago

JEAPI-DEV commented 1 year ago

Requirements

Have you checked this problem on the example app?

No

FlutterBluePlus Version

1.16.10

Flutter Version

Framework • revision 796c8ef792 (4 months ago) • 2023-06-13 15:51:02 -0700 Engine • revision 45f6e00911 Tools • Dart 3.0.5 • DevTools 2.23.1

What OS?

Android

OS Version

Android 12 SKQ1.210908.001

Bluetooth Module

TI CC2541

What is your problem?

Hi I'm back again. So I wanted to add a Feature to my app which would allow me to use values of an MPU6050 to rotate a Cube in an Android app. But the thing is for some reason I'm getting a lot of wrong messages which don't make sense. Tested if this also happened in other apps like the Serial Bluetooth Terminal but everything works fine in there.

Here is the Arduino code:

#include <Arduino.h>

float oldmpuX = 0;
float oldmpuY = 0;
float oldmpuZ = 0;

#include <MPU6050_tockn.h>
#include <Wire.h>

MPU6050 mpu6050(Wire);

void setup() {
  Serial.begin(9600);
  Wire.begin();
  mpu6050.begin();
  mpu6050.calcGyroOffsets(true);
}

void loop()
{   
    delay(200);
    mpu6050.update(); // Update sensor data without delay
    delay(200);

    oldmpuX = mpu6050.getAngleX();
    oldmpuY = mpu6050.getAngleY();
    oldmpuZ = mpu6050.getAngleZ();

    // Create the string to send over serial
    String sendingcontent = "RX=X" + String(oldmpuX) + "|Y" + String(oldmpuY) + "|Z" + String(oldmpuZ);

    Serial.println(sendingcontent); // Send data over serial

    delay(400);
}

Here is the main class which handles finding the correct Characteristic sending Messages and receiving them.

import 'dart:async';
import 'dart:convert';
import 'dart:io';
import 'package:flutter_blue_plus/flutter_blue_plus.dart';

class BluetoothManager {
  late BluetoothDevice device;
  BluetoothCharacteristic? _selectedCharacteristic;

  String _messagebox = "";
  String _messagebuffer = "";
  String _messagebuffertemp = "";
  String get messagebox => _messagebox;
  String get messagebuffertemp => _messagebuffertemp;
  String get messagebuffer => _messagebuffer;
  set messagebuffer (String value) => _messagebuffer = value;
  set messagebuffertemp (String value) => _messagebuffertemp = value;
  // get selected characteristic
  BluetoothCharacteristic? get selectedCharacteristic => _selectedCharacteristic;
  // _messagebuffertemp
  bool run = true;
  late StreamSubscription<List<int>> listener;
  StreamSubscription? _listener;

  BluetoothManager(this.device);

  String MPUvalue = "";
  String POTvalue = "";
  String DHTvalue = "";
  String PIRHCvalue = "";

  // get messagebuffertemp and check if the values changes from the original
  // if it does then update the messagebuffer with the new value
  // if it does not then do nothing and dont update the messagebuffer
  Future<void> updatebuffer() async{
    // fix problem X=DHT24.40|60.00
    if(messagebuffertemp.startsWith("X=")){
      messagebuffertemp = "R$messagebuffertemp";
    }

    if(messagebuffertemp.contains("POT")){
      if(!POTvalue.contains(messagebuffertemp)){
        messagebuffer = messagebuffertemp;
      }
      POTvalue = messagebuffertemp;
    }
    else if(messagebuffertemp.contains("DHT")){
      if(!DHTvalue.contains(messagebuffertemp)){
        messagebuffer = messagebuffertemp;
      }
      DHTvalue = messagebuffertemp;
    }
    else if (messagebuffertemp.contains("P") && messagebuffertemp.contains("S")) {
      if(!PIRHCvalue.contains(messagebuffertemp)){
        messagebuffer = messagebuffertemp;
      }
      PIRHCvalue = messagebuffertemp;
    }
    else{
      messagebuffer = messagebuffertemp;
    }
  }

  Future<void> selectCharacteristic(bool recv) async {
    await device.connect();
    List<BluetoothService> services = await device.discoverServices();
    for (BluetoothService service in services) {
      for (BluetoothCharacteristic characteristic in service.characteristics) {
        if (characteristic.properties.write && characteristic.properties.notify) {
          _selectedCharacteristic = characteristic;
          break;
        } else {
          print('Characteristic ${characteristic.uuid} does not support write operation');
        }
      }
    }
    if(recv){
      await startReceivingMessages(recv);
      //updateMesasges();
    }
  }

void dispose() {
  run = false;
  _listener?.cancel(); // Cancel the listener
  // reset all valibles 
  _messagebox = "";
  _messagebuffer = "";
  _messagebuffertemp = "";
  MPUvalue = "";
  POTvalue = "";
  DHTvalue = "";
  PIRHCvalue = "";
}

  Future<void> sendMessage(String message) async {
    List<int> bytes = utf8.encode("TX=" + message);
    try {
        await _selectedCharacteristic!.write(bytes, withoutResponse: false);
        print("Message sent: $message");
      } catch (e) {
        sendMessage(message);
      }
    await Future.delayed(Duration(seconds: 2));

  }

  // update value
  Future<void> updateMesasges() async {
    // while(run){
    //   try {
    //     // delay by 100ms
    //     await Future.delayed(Duration(milliseconds: 120));
    //     List<int> readValue = await _selectedCharacteristic!.read();
    //     if(!utf8.decode(readValue).contains(_messagebuffertemp)){ 
    //     }
    //   } catch (e) { }
    // }

    // update value only if new value is avalible
    while(run){
      try {
        // delay by 100ms
        await Future.delayed(Duration(milliseconds: 120));
        List<int> readValue = await _selectedCharacteristic!.read();
        if(!utf8.decode(readValue).contains(_messagebuffertemp)){ 
          _messagebuffertemp = utf8.decode(readValue);
          updatebuffer();
        }
      } catch (e) { }
    }
  }

  // set mtu size
  Future<void> setMTU(int mtu) async {
    try {
      await device.requestMtu(mtu);
    } catch (e) {
      print("Failed to set MTU: $e");
    }
  }

Future<void> startReceivingMessages(bool recv) async {
  try {
    if (Platform.isAndroid) {
        await device.requestMtu(223);
    }

    if (_selectedCharacteristic != null) {
      _listener?.cancel(); // Cancel any existing listeners

      _listener = _selectedCharacteristic!.onValueReceived.listen((value) {
          // if (value.isNotEmpty) {
          //   String message = utf8.decode(value);
          //   _messagebuffertemp = "$message\n";
          //   //updatebuffer();
          //    // Add a read operation
          //   print("Received message: $message");
          // } else {
          //   print("Received empty value");
          // }
          // make sure that the package wait till the message is fully sent
          // before receiving a new message
          if (value.isNotEmpty) {
            String message = utf8.decode(value);
            _messagebuffertemp = "$message\n";
            //updatebuffer();
            // Add a read operation
            print("Received message: $message");
          } else {
            print("Received empty value");
          }
      });

      await _selectedCharacteristic!.setNotifyValue(true);
      // remove 3 first characters from string and return the rest (TX=)
    } else {
      print("No characteristic selected for receiving messages");
    }
  } catch (e) {
    // print("Failed to receive messages: $e");
    selectCharacteristic(recv);
  }
}

  void stopReceivingMessages() {
   _selectedCharacteristic?.value.drain();
  }
}

We also have the devicescreen.dart, where the connected device is shown and some other screens:

import 'package:flutter/material.dart';
import 'package:flutter_blue_plus/flutter_blue_plus.dart';
import 'package:funduinoblecontroller/bluetooth/bluetoothmanager.dart';
import 'package:funduinoblecontroller/bluetoothgui/controller.dart';
import 'package:funduinoblecontroller/bluetoothgui/cubescreenfancy.dart';
import 'package:funduinoblecontroller/bluetoothgui/cubescreenfast.dart';
import 'package:funduinoblecontroller/bluetoothgui/mpucube.dart';

class DeviceScreen extends StatefulWidget {
  final BluetoothDevice device;
  const DeviceScreen({super.key, required this.device});

  @override
  State<DeviceScreen> createState() => _DeviceScreenState();
}

class _DeviceScreenState extends State<DeviceScreen> {
  BluetoothDevice get device => widget.device;
  List<BluetoothService> _services = [];
  TextEditingController _sendController = TextEditingController();
  Map<Guid, dynamic> _characteristicValues = {};
  late BluetoothManager _manager;
  String messagebox = "";
  String oldmessage = "";

  @override
  void initState() {
    super.initState();
    _manager = BluetoothManager(device);
    connectToDevice();
    _manager.selectCharacteristic(true);
  }

  Future<void> connectToDevice() async {
    await device.connect();
    List<BluetoothService> services = await device.discoverServices();
    setState(() {
      _services = services;
    });
  }
  // dispose disconnect from device
  @override
  void dispose() {
    device.disconnect();
    _manager.dispose();
    super.dispose();
  }

  // read vauls

  @override
  Widget build(BuildContext context) {
    return WillPopScope(
      onWillPop: () async {
        Navigator.pop(context);
        _manager.dispose();
        return true;
      },
      child: Scaffold(
        appBar: AppBar(
          title: Text('Verbunden mit ${device.name}'),
        ),
        body: SingleChildScrollView(
          child: Column(
            children: [
              Container(
                margin: EdgeInsets.all(10),
                child: Container(
                  alignment: Alignment.center,
                  // button navigation to controller
                  child: ElevatedButton(
                    onPressed: () {
                      Navigator.push(
                        context,
                        MaterialPageRoute(
                          builder: (context) => ControllerPage(device: device, blemanager: _manager),
                        ),
                      );
                    },
                    child: Text('FunBot Controller'),
                  ),
                ),
              ),
              // cube page
              Container(
                child: Column(
                  children: [
                    Container(
                      margin: EdgeInsets.all(10),
                      child: ElevatedButton(
                        onPressed: () {
                          Navigator.push(
                            context,
                            MaterialPageRoute(
                              builder: (context) => FunduinoCubeFancy(blemanager: _manager),
                            ),
                          );
                        },
                        child: Text('Cube Fancy'),
                      ),
                    ),
                    Container(
                      margin: EdgeInsets.all(10),
                      child: ElevatedButton(
                        onPressed: () {
                          Navigator.push(
                            context,
                            MaterialPageRoute(
                              builder: (context) => MPUCube(blemanager: _manager),
                            ),
                          );
                        },
                        child: Text('Mpu View 3D'),
                      ),
                    ),
                  ],
                ),
              ),                // container box for message
            ],
          ),
        ),
        bottomSheet: Container(
          child: Container(
            padding: EdgeInsets.all(10),
            child: TextField(
              controller: _sendController,
              decoration: InputDecoration(
                labelText: 'Sende eigene Befehle',
                suffixIcon: IconButton(
                  icon: Icon(Icons.send),
                  onPressed: () async {
                    await _manager.sendMessage(_sendController.text);
                    _sendController.clear();
                    setState(() {
                      messagebox += _manager.messagebuffer + "\n";
                    });
                  },
                ),
              ),
            ),
          ),
        ),
      )
    );
  }
}

The problems also arise on the other screens, but the main isse is with the mpucube.dart screen, where its important that the data does not take an ethernity to be recived correctly.

import 'dart:convert';
import 'dart:math';
import 'package:flutter/material.dart';
import 'package:flutter_cube/flutter_cube.dart';
import 'package:funduinoblecontroller/bluetooth/bluetoothmanager.dart';

class MPUCube extends StatefulWidget {
  BluetoothManager blemanager;
  MPUCube({Key? key, required this.blemanager}) : super(key: key);

  @override
  _MPUCubeState createState() => _MPUCubeState();
}

class _MPUCubeState extends State<MPUCube> {
  double roll = 0;
  double pitch = 0;
  double yaw = 0;
  bool run = true;

  Future<void> getMessages() async {
    while (run) {
      await Future.delayed(Duration(milliseconds: 40));
      try {
        // Replace with your actual bluetooth reading logic
        await Future.delayed(Duration(milliseconds: 40));
        try {
        List<int> readValue = await widget.blemanager.selectedCharacteristic!.read();
          if(!utf8.decode(readValue).contains(widget.blemanager.messagebuffertemp)){ 
            widget.blemanager.messagebuffertemp = utf8.decode(readValue);
          await widget.blemanager.updatebuffer();
        }
      } catch (e) { }

      String message = widget.blemanager.messagebuffer;

        RegExp exp = RegExp(r'([XYZ])([+-]?\d+(\.\d+)?)');
        Iterable<RegExpMatch> matches = exp.allMatches(message);

        Map<String, double> values = {};
        for (var match in matches) {
          String? value = match.group(1); // X, Y, or Z
          String? count = match.group(2); // value
          values[value!] = double.parse(count!);
        }

        if (values.containsKey("X")) {
          roll = values["X"]!;
        }

        if (values.containsKey("Y")) {
          pitch = values["Y"]!;
        }

        if (values.containsKey("Z")) {
          yaw = values["Z"]!;
        }
        if (roll != 0 || pitch != 0 || yaw != 0) {
          try{
            setState(() {});
          }catch (e){
            run = false;
          }
        }
      } catch (e) {
        run = false;
      }
    }
  }

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

  @override
  Widget build(BuildContext context) {
    return WillPopScope(
      onWillPop: () async {
        run = false;
        Navigator.pop(context);
        return false;
      },
      child: Cube(
        onSceneCreated: (Scene scene) {
          scene.world.add(
            Object(
              fileName: 'assets/cube.obj',
              isAsset: true,
              scale: Vector3(0.5, 0.5, 0.5),
              position: Vector3(0, 0, 0),
              rotation: Vector3(
                roll * pi / 180,
                pitch * pi / 180,
                yaw * pi / 180,
              ),
            ),
          );
        },
      ),
    );
  }
}

Logs

D/[FBP-Android](28130): [FBP] onMethodCall: startScan
D/BluetoothAdapter(28130): isLeEnabled(): ON
D/BluetoothLeScanner(28130): onScannerRegistered() - status=0 scannerId=9 mScannerId=0
5
D/permissions_handler(28130): No permissions found in manifest for: []3
D/[FBP-Android](28130): [FBP] onMethodCall: stopScan
D/BluetoothAdapter(28130): isLeEnabled(): ON
D/[FBP-Android](28130): [FBP] onMethodCall: connect
D/BluetoothGatt(28130): connect() - device: 01:23:45:67:90:9D, auto: false, eattSupport: false
D/BluetoothGatt(28130): registerApp()
D/BluetoothGatt(28130): registerApp() - UUID=12176374-9d5b-41e3-9c66-6932294c115c
D/BluetoothGatt(28130): onClientRegistered() - status=0 clientIf=9
D/BluetoothGatt(28130): onClientConnectionState() - status=0 clientIf=9 device=01:23:45:67:90:9D
D/[FBP-Android](28130): [FBP] onConnectionStateChange: status: 0 (SUCCESS) newState: connected
D/[FBP-Android](28130): [FBP] onMethodCall: connect
D/[FBP-Android](28130): [FBP] already connected
D/[FBP-Android](28130): [FBP] onMethodCall: discoverServices
D/BluetoothGatt(28130): discoverServices() - device: 01:23:45:67:90:9D
D/BluetoothGatt(28130): onConnectionUpdated() - Device=01:23:45:67:90:9D interval=6 latency=0 timeout=500 status=0
D/BluetoothGatt(28130): onSearchComplete() = Device=01:23:45:67:90:9D Status=0
D/[FBP-Android](28130): [FBP] onServicesDiscovered: count: 4 status: 0
D/[FBP-Android](28130): [FBP] onMethodCall: discoverServices
D/BluetoothGatt(28130): discoverServices() - device: 01:23:45:67:90:9D
D/BluetoothGatt(28130): onSearchComplete() = Device=01:23:45:67:90:9D Status=0
D/[FBP-Android](28130): [FBP] onServicesDiscovered: count: 4 status: 0
D/BluetoothGatt(28130): onConnectionUpdated() - Device=01:23:45:67:90:9D interval=36 latency=0 timeout=500 status=0
I/flutter (28130): Characteristic 00002a00-0000-1000-8000-00805f9b34fb does not support write operation
I/flutter (28130): Characteristic 00002a01-0000-1000-8000-00805f9b34fb does not support write operation
I/flutter (28130): Characteristic 00002a02-0000-1000-8000-00805f9b34fb does not support write operation
I/flutter (28130): Characteristic 00002a03-0000-1000-8000-00805f9b34fb does not support write operation
I/flutter (28130): Characteristic 00002a04-0000-1000-8000-00805f9b34fb does not support write operation
I/flutter (28130): Characteristic 00002a05-0000-1000-8000-00805f9b34fb does not support write operation
I/flutter (28130): Characteristic 00002a23-0000-1000-8000-00805f9b34fb does not support write operation
I/flutter (28130): Characteristic 00002a24-0000-1000-8000-00805f9b34fb does not support write operation
I/flutter (28130): Characteristic 00002a25-0000-1000-8000-00805f9b34fb does not support write operation
I/flutter (28130): Characteristic 00002a26-0000-1000-8000-00805f9b34fb does not support write operation
I/flutter (28130): Characteristic 00002a27-0000-1000-8000-00805f9b34fb does not support write operation
I/flutter (28130): Characteristic 00002a28-0000-1000-8000-00805f9b34fb does not support write operation
I/flutter (28130): Characteristic 00002a29-0000-1000-8000-00805f9b34fb does not support write operation
I/flutter (28130): Characteristic 00002a2a-0000-1000-8000-00805f9b34fb does not support write operation
I/flutter (28130): Characteristic 00002a50-0000-1000-8000-00805f9b34fb does not support write operation D/[FBP-Android](28130): [FBP] onMethodCall: requestMtu
D/BluetoothGatt(28130): configureMTU() - device: 01:23:45:67:90:9D mtu: 223
D/BluetoothGatt(28130): onConfigureMTU() - Device=01:23:45:67:90:9D mtu=23 status=0
D/[FBP-Android](28130): [FBP] onMtuChanged: mtu: 23 status: 0
D/[FBP-Android](28130): [FBP] onMethodCall: setNotification
D/BluetoothGatt(28130): setCharacteristicNotification() - uuid: 0000ffe1-0000-1000-8000-00805f9b34fb enable: true
D/[FBP-Android](28130): [FBP] onDescriptorWrite: uuid: 00002902-0000-1000-8000-00805f9b34fb status: 0
D/[FBP-Android](28130): [FBP] onMethodCall: readCharacteristic
D/[FBP-Android](28130): [FBP] onCharacteristicRead: uuid: 0000ffe1-0000-1000-8000-00805f9b34fb status: 0
2
I/flutter (28130): Received message: .76
D/[FBP-Android](28130): [FBP] onMethodCall: readCharacteristic
D/[FBP-Android](28130): [FBP] onCharacteristicRead: uuid: 0000ffe1-0000-1000-8000-00805f9b34fb status: 0
2
I/flutter (28130): Received message: .76
D/[FBP-Android](28130): [FBP] onMethodCall: readCharacteristic
D/[FBP-Android](28130): [FBP] onCharacteristicRead: uuid: 0000ffe1-0000-1000-8000-00805f9b34fb status: 0
2
I/flutter (28130): Received message: .87
D/[FBP-Android](28130): [FBP] onMethodCall: readCharacteristic
D/[FBP-Android](28130): [FBP] onCharacteristicRead: uuid: 0000ffe1-0000-1000-8000-00805f9b34fb status: 0
2
I/flutter (28130): Received message: .87
D/[FBP-Android](28130): [FBP] onMethodCall: readCharacteristic
D/[FBP-Android](28130): [FBP] onCharacteristicRead: uuid: 0000ffe1-0000-1000-8000-00805f9b34fb status: 0
2
I/flutter (28130): Received message: .87
D/[FBP-Android](28130): [FBP] onMethodCall: readCharacteristic
D/[FBP-Android](28130): [FBP] onCharacteristicRead: uuid: 0000ffe1-0000-1000-8000-00805f9b34fb status: 0
2
I/flutter (28130): Received message: RX=X-3.97|Y-1.55|Z-1
D/[FBP-Android](28130): [FBP] onMethodCall: readCharacteristic
D/[FBP-Android](28130): [FBP] onCharacteristicRead: uuid: 0000ffe1-0000-1000-8000-00805f9b34fb status: 0
2
I/flutter (28130): Received message: .76
D/[FBP-Android](28130): [FBP] onMethodCall: readCharacteristic
D/[FBP-Android](28130): [FBP] onCharacteristicRead: uuid: 0000ffe1-0000-1000-8000-00805f9b34fb status: 0
2
I/flutter (28130): Received message: .76
D/[FBP-Android](28130): [FBP] onMethodCall: readCharacteristic
D/[FBP-Android](28130): [FBP] onCharacteristicRead: uuid: 0000ffe1-0000-1000-8000-00805f9b34fb status: 0
2
I/flutter (28130): Received message: .76
D/[FBP-Android](28130): [FBP] onMethodCall: readCharacteristic
D/[FBP-Android](28130): [FBP] onCharacteristicRead: uuid: 0000ffe1-0000-1000-8000-00805f9b34fb status: 0
2
I/flutter (28130): Received message: .76
D/[FBP-Android](28130): [FBP] onMethodCall: readCharacteristic
D/[FBP-Android](28130): [FBP] onCharacteristicRead: uuid: 0000ffe1-0000-1000-8000-00805f9b34fb status: 0
2
I/flutter (28130): Received message: .87
D/[FBP-Android](28130): [FBP] onMethodCall: readCharacteristic
D/[FBP-Android](28130): [FBP] onCharacteristicRead: uuid: 0000ffe1-0000-1000-8000-00805f9b34fb status: 0
2
I/flutter (28130): Received message: .87
D/[FBP-Android](28130): [FBP] onMethodCall: readCharacteristic
D/[FBP-Android](28130): [FBP] onCharacteristicRead: uuid: 0000ffe1-0000-1000-8000-00805f9b34fb status: 0
2
I/flutter (28130): Received message: .87
D/[FBP-Android](28130): [FBP] onMethodCall: readCharacteristic
D/[FBP-Android](28130): [FBP] onCharacteristicRead: uuid: 0000ffe1-0000-1000-8000-00805f9b34fb status: 0
2
I/flutter (28130): Received message: .87
D/[FBP-Android](28130): [FBP] onMethodCall: readCharacteristic
D/[FBP-Android](28130): [FBP] onCharacteristicRead: uuid: 0000ffe1-0000-1000-8000-00805f9b34fb status: 0
2
I/flutter (28130): Received message: RX=X-4.09|Y-1.44|Z-1
D/[FBP-Android](28130): [FBP] onMethodCall: readCharacteristic
D/[FBP-Android](28130): [FBP] onCharacteristicRead: uuid: 0000ffe1-0000-1000-8000-00805f9b34fb status: 0
2
I/flutter (28130): Received message: .86
D/[FBP-Android](28130): [FBP] onMethodCall: readCharacteristic
D/[FBP-Android](28130): [FBP] onCharacteristicRead: uuid: 0000ffe1-0000-1000-8000-00805f9b34fb status: 0
2
I/flutter (28130): Received message: .86
D/[FBP-Android](28130): [FBP] onMethodCall: readCharacteristic
D/[FBP-Android](28130): [FBP] onCharacteristicRead: uuid: 0000ffe1-0000-1000-8000-00805f9b34fb status: 0
2
I/flutter (28130): Received message: .86
D/[FBP-Android](28130): [FBP] onMethodCall: readCharacteristic
D/[FBP-Android](28130): [FBP] onCharacteristicRead: uuid: 0000ffe1-0000-1000-8000-00805f9b34fb status: 0
2
I/flutter (28130): Received message: .86
JEAPI-DEV commented 1 year ago

Note the send messages from the Arduino look like this: RX=X-3.42|Y-1.48|Z-8.01 RX=X-3.31|Y-1.44|Z-8.05 RX=X-3.30|Y-1.48|Z-8.11 RX=X-3.35|Y-1.45|Z-8.14 RX=X-3.45|Y-1.54|Z-8.20 RX=X-3.36|Y-1.47|Z-8.16 RX=X-3.50|Y-1.43|Z-8.15 RX=X-3.50|Y-1.35|Z-8.22 RX=X-3.74|Y-1.26|Z-8.22

chipweinberger commented 1 year ago

you need to increase android MTU. see README

chipweinberger commented 1 year ago

btw, did you read the common problems section?

onValueReceived (or lastValueStream) data is split up

in your question, you specified that you read the common problems section.