Search code examples
flutterdartbluetooth-lowenergyzlib

Decode BLE devices payload in Flutter


I am receiving data from my BLE device using the flutter_reactive_ble package.

I have the following method :

_broadcastCharacteristSubscription = _flutterReactiveBle
          .subscribeToCharacteristic(_broadCastCharacteristic!)
          .listen((event) {
        Uint8List data = Uint8List.fromList(event); 
        _handleBytesReceived(data);
      });

This data is byte stuffed and compressed into chunks.

I have done the byte stuffing to reconstitute the compressed content that looks like this :

I/flutter (13164): Current payload before decompression : xUO�� ��>�Ew�gL

Now i need to decompress this, so i have tried using ZLibDecoder because my client told me they use Zlib for compression :

enter image description here

So i have implemented the following :

  void _handleBytesReceived(Uint8List data) {
    String decoded = _decoder.convert(data);
    print("Ascii string compressed data : $decoded");
    if (currentPayload.isEmpty) {
      currentPayload = decoded;
      print("Current payload is now : $currentPayload");
    }
    else {
      for (var element in decoded.characters) {
        if (element != "~") {
          currentPayload += element;
        }
        else {
          currentPayload = currentPayload.replaceAll("~", "");
          print("Current payload before decompression : $currentPayload");
          currentPayload = _unzipPayload(currentPayload);
          print("Current payload after : $currentPayload");
          currentPayload = "";
          break;
        }
      }
    }
  }

  String _unzipPayload(String encoded) {
    return String.fromCharCodes(ZLibCodec(level: 6).decode(encoded.codeUnits));
  }

And i am getting the following error :

I/flutter (13164): Error from outside Flutter : FormatException: Filter error, bad data
  I/flutter (13164):  #0      _FilterImpl.processed (dart:io-patch/filter_patch.dart:13:71)
  I/flutter (13164): #1      _FilterSink.addSlice (dart:io/data_transformer.dart:505:29)
  I/flutter (13164): #2      _FilterSink.add (dart:io/data_transformer.dart:489:5)
  I/flutter (13164): #3      ZLibDecoder.convert (dart:io/data_transformer.dart:363:9)
  I/flutter (13164): #4      Codec.decode (dart:convert/codec.dart:30:34)
  I/flutter (13164): #5      ReactiveBleAPI._unzipPayload (package:myappname/api/reactive_ble_api.dart:213:53)
  I/flutter (13164): #6      ReactiveBleAPI._handleBytesReceived (package:myappname/api/reactive_ble_api.dart:203:28)
  I/flutter (13164): #7      ReactiveBleAPI._connectToDevice.<anonymous closure> (package:myappname/api/reactive_ble_api.dart:171:9)
  I/flutter (13164): #8      _RootZone.runUnaryGuarded (dart:async/zone.dart:1594:10)
  I/flutter (13164): #9      _BufferingStreamSubscription._sendData (dart:async/stream_impl.dart:339:11)
  I/flutter (13164): #10     _BufferingStreamSubscription._add (dart:async/stream_impl.dart:271:7)
  I/flutter (13164): #11     _SyncStreamControllerDispatch._sendData (dart:async/stream_controller.dart:776:19)
  I/flutter (13164): #12     _StreamController._add (dart:async/stream_controller.dart:650:7)
  I/flutter (13164): #13     _RootZone.runUnaryGuarded (dart:async/zone.dart:1594:10)
  I/flutter (13164): #14     _BufferingStreamSubscription._sendData (dart:async/stream_impl.dart:339:11)
  I/flutter (13164): #15     _DelayedData.perform (dart:async/stream_impl.dart:515:14)
  I/flutter (13164): #16     _PendingEvents.handleNext (dart:async/stream_impl.dart:620:11)
  I/flutter (13164): #17     _PendingEvents.schedule.<anonymous closure> (dart:async/stream_impl.dart:591:7)
  I/flutter (13164): #18     _microtaskLoop (dart:async/schedule_microtask.dart:40:21)
  I/flutter (13164): #19     _startMicrotaskLoop (dart:async/schedule_microtask.dart:49:5)

The decompression seems to failed, and i am pretty sure that the data is not corrupted unless i have got it wrong with my above conversions.

Am i doing something wrong with the currentPayload.codeUnits for example ? or in any part of it ?

EDIT : Here is the new code without trying to decode the initial bytes from the BLE (removing String decoded = _decoder.convert(data) as suggested in the comments :

void _resetPayload() {
    print("Current payload before compression : $currentPayload");
    List<int> finalList = [];
    for (var element in currentPayload) {
      if (element != 126) {
        finalList.add(element);
      }
    }
    String output = _unzipPayload(Uint8List.fromList(finalList));
    print("Current payload after compression : $output");
    currentPayload = Uint8List(0);
  }

  void _handleBytesReceived(Uint8List data) {
    print("Raw data : $data");
    //String decoded = _decoder.convert(data);
    // print("Decoded ascii data : $decoded");
    if (currentPayload.isEmpty) {
      currentPayload = data;
    }
    print("Current payload is : $currentPayload");
    for (var element in data.toList()) {
      if (currentPayload.contains(126)) {
        if (element == 126) {
          _resetPayload();
        }
        else {
          currentPayload.add(element);
        }
      }
    }
  }

  String _unzipPayload(Uint8List encoded) {
    try {
      List<int> decoded = _codec.decode(encoded);
      print("Decoded : $decoded");
      String result =  String.fromCharCodes(decoded);
      print("Result : $result");
      return result;
    } catch (error) {
      print("Error unzipping $error");
    }
    return "";
  }

And here are the logs i am getting with the above code for example :

I/flutter (25368): Raw data : [126, 120, 1, 85, 79, 209, 10, 131, 48, 12, 252, 151, 60, 23, 113, 67, 157, 245, 103, 36]
I/flutter (25368): Current payload is : [126, 120, 1, 85, 79, 209, 10, 131, 48, 12, 252, 151, 60, 23, 113, 67, 157, 245, 103, 36]
I/flutter (25368): Current payload before compression : [120, 1, 85, 79, 209, 10, 131, 48, 12, 252, 151, 60, 23, 113, 67, 157, 245, 103, 36]
I/flutter (25368): Decoded : []
I/flutter (25368): Result : 
I/flutter (25368): Current payload after compression : 
D/BluetoothGatt(25368): onConnectionUpdated() - Device=C4:DE:E2:90:A6:DE interval=36 latency=0 timeout=500 status=0
I/flutter (25368): Raw data : [126, 120, 1, 85, 140, 65, 10, 133, 48, 16, 67, 239, 146, 117, 23, 174, 123, 21, 145, 207]
I/flutter (25368): Current payload is : [126, 120, 1, 85, 140, 65, 10, 133, 48, 16, 67, 239, 146, 117, 23, 174, 123, 21, 145, 207]
I/flutter (25368): Current payload before compression : [120, 1, 85, 140, 65, 10, 133, 48, 16, 67, 239, 146, 117, 23, 174, 123, 21, 145, 207]
I/flutter (25368): Decoded : []
I/flutter (25368): Result : 
I/flutter (25368): Current payload after compression : 

FINAL EDIT / ANSWER : Payload was empty because i was not requesting a higher MTU, not enough bytes were transferred. I have set it to 180 according to my BLE device.


Solution

  • You seem to be treating a stream of bytes as if they were characters. This is not a String (that's why you're seeing �; that's the decoder telling you it's not valid UTF-8). You need to operate on the Uint8List directly. You should not call String decoded = _decoder.convert(data) .