don / cordova-plugin-ble-central

Bluetooth Low Energy (BLE) Central plugin for Apache Cordova (aka PhoneGap)
Apache License 2.0
942 stars 603 forks source link

ble.Read returns buffer length 3, ble.Notify returns buffer length 1. Notifications work and I always get the correct value in nRF connect app. #955

Closed mrSilkie closed 1 year ago

mrSilkie commented 1 year ago

I have an array of the services on the device, then each service is an array of characteristics. This lets me iterate through all of the characteristics and first read the value, then begin the notifications.

Here is the console log of what I'm seeing.

Changing Weight BUFFER = {"0":0,"1":36,"2":0} Changing Weight = 0.18kg Changing Weight BUFFER = {"0":0} Changing Weight = 0kg

The first value, of length 3 is correct and was retreived from the read function.

The second value, of length 1, is incorrect and was retreived from notification.

I assume that both read and write return the same bufferArray type and should return the same value (if it has not changed between readings). However, this is not what I'm seeing.

I've even tried to say 'if buffer.length < 3, then ble.read()' and the return was a buffer of length 1. I am a bit stuck here. I am using ESP32 and nkolban's BLE library for Arduino. It could be that my settings on my device are wrong and is why I'm not getting the correct buffer back. However, it works in nRF connect so I am a bit stuck here.

Any advice?

peitschie commented 1 year ago

Hi @mrSilkie,

Are you able to share some code examples of what your read and startNotification snippets look like? Also, just to confirm, is this Android only, or iOS too? What version of the plugin are you currently using?

I assume that both read and write return the same bufferArray type and should return the same value (if it has not changed between readings). However, this is not what I'm seeing.

There's a slight difference between the two callbacks, in that the notification callback includes a sequence number along with the data as a second argument to the data handler: https://github.com/don/cordova-plugin-ble-central/blob/master/src/android/SequentialCallbackContext.java#L39

But, I wouldn't have thought that this impacted what you're describing.

mrSilkie commented 1 year ago

Hi @peitschie

I think you've hit the nail on the head. The method for processing the read and notification are the same. which is why one works and the other probable doesn't. In my original post, the console log is a JSON.stringify of the incomming data so I expect that the console log would have revealed the nested elements.

To begin the read / notify

  async subscribeToCharacteristics(peripheral) {
    for (const service in this.stateMap) {
      for (const characteristic in this.stateMap[service]) {
        // IGNORE VARIABLES NOT ASSIGNED TO UUID
        if(characteristic === 'pumpType' || characteristic === 'pumping' || characteristic === 'pumpAmount') {
          continue;
        }

        this.ble.read(peripheral.id, service, characteristic).then(data => {
          const buffer = new Uint8Array(data);
          // Msg: Reading 75c8226c-283f-11ed-bf69-0242ac120001:75c8226c-283f-11ed-a261-0242ac120021 : 0s - 0.00kg - 48 115 32 45 32 48 46 48 48 107 103
          console.log('Reading ' + service + ':' + characteristic + ' : ' + new TextDecoder().decode(buffer) + ' - ' + Array.from(buffer).join(' '));
          this.processNotification(service, characteristic, buffer);
        },
          error => {

          });
        //const characteristicUUID = this.stateMap[service][characteristic];
        this.ble.startNotification(peripheral.id, service, characteristic).subscribe(
          data => {
            this.ngZone.run(() => {
              const buffer = new Uint8Array(data);
              this.processNotification(service, characteristic, buffer);
            });
          },
          error => console.error(`Error starting notifications on ${service}:${characteristic}`, error)
        );
      }
    }
  }

Notice how there is no difference between the input into processNotification for the Read and Notify cases. The startNotification should call buffer[0] if I'm correct.

In processNotification()

    if (serviceUUID === ServiceNames.serviceScale) {
      if (characteristicUUID === CharacteristicNames.characteristicScaleMeasurement && buffer.length === 3) {
        const arrayBuffer = new Uint8Array(2);
        arrayBuffer[0] = buffer[2];
        arrayBuffer[1] = buffer[1];
        console.log('Changing Weight BUFFER = ' + JSON.stringify(buffer));
        // convert arrayBuffer to weightInt
        const dataView = new DataView(arrayBuffer.buffer);
        const weightInt = dataView.getUint16(0, false);
        const weightFloat = weightInt * 0.005;
        console.log('Changing Weight = ' + weightFloat + 'kg');
        this.stateMap[ServiceNames.serviceScale][CharacteristicNames.characteristicScaleMeasurement] = weightFloat;
        return;
      }
    }

I am running Android using the package '@awesome-cordova-plugins/ble/ngx' with 5.45.0

peitschie commented 1 year ago

The startNotification should call buffer[0] if I'm correct.

Agreed, that sounds like a reasonable tweak based on what I've seen with other bug reports. I'll be interested to hear how the change goes!

mrSilkie commented 1 year ago

Not sure what changes I made to get it to work but this is the new startNotification function

this.ble.startNotification(peripheral.id, service, characteristic).subscribe(
          data => {
            this.ngZone.run(() => {
              const buffer = new Uint8Array(data[0]);
              console.log('Notification RX: ' + service + ':' + characteristic + ' = ' + buffer);
              this.processNotification(service, characteristic, buffer);
            });
          },
          error => console.error(`Error starting notifications on ${service}:${characteristic}`, error)
        );

I copied the change from here.

Simply changing the value to data[0] didn't make it work though, the real magic is the 'const buffer = new Uint8Array(data[0]);' line

peitschie commented 1 year ago

Thanks for the update! Glad you've figured out the issue 🙂