bluetooth-le icon indicating copy to clipboard operation
bluetooth-le copied to clipboard

Calling twice BleClient.write() one after another

Open vovagorodok opened this issue 3 years ago • 8 comments
trafficstars

Describe the bug Something happens when await BleClient.write() called twice, one after another and second write is not performs at all

To Reproduce Steps to reproduce the behavior: Was used following method to send string to LE device:

  async sendMsgToDevice(msg: string) {
    console.log(`vova: sendMsgToDevice: ${msg}`);
    await BleClient.write(this.getDeviceId(), this.uuids!.srv, this.uuids!.txCh, textToDataView(msg))
  }

Most of time when we call sendMsgToDevice() two times is correct behavior:

I/Capacitor/Console: File: http://localhost/main.js - Line 12840 - Msg: vova: sendMsgToDevice: b8d8
I/Capacitor/Console: File: http://localhost/main.js - Line 12840 - Msg: vova: sendMsgToDevice: go
V/Capacitor/Plugin: To native (Capacitor plugin): callbackId: 20997892, pluginId: BluetoothLe, methodName: write
V/Capacitor: callback: 20997892, pluginId: BluetoothLe, methodName: write, methodData:
D/Device: resolve: write|f5351050-b2c9-11ec-a0c0-b3bc53b08d33|f53513ca-b2c9-11ec-a0c1-639b8957db99 Characteristic successfully written.
V/Capacitor/Plugin: To native (Capacitor plugin): callbackId: 20997894, pluginId: BluetoothLe, methodName: write
V/Capacitor: callback: 20997894, pluginId: BluetoothLe, methodName: write, methodData: {"deviceId":"34:AB:95:8F:62:5A","service":"f5351050-b2c9-11ec-a0c0-b3bc53b08d33","characteristic":"f53513ca-b2c9-11ec-a0c1-639b8957db99","value":"67 6f"}
D/Device: resolve: write|f5351050-b2c9-11ec-a0c0-b3bc53b08d33|f53513ca-b2c9-11ec-a0c1-639b8957db99 Characteristic successfully written.

and go is sent after b8d8. But happens when go is not sent to LE device. Than in logs I see only:

I/Capacitor/Console: File: http://localhost/main.js - Line 12840 - Msg: vova: sendMsgToDevice: f3e5
I/Capacitor/Console: File: http://localhost/main.js - Line 12840 - Msg: vova: sendMsgToDevice: go
V/Capacitor/Plugin: To native (Capacitor plugin): callbackId: 20997930, pluginId: BluetoothLe, methodName: write
V/Capacitor: callback: 20997930, pluginId: BluetoothLe, methodName: write, methodData: {"deviceId":"34:AB:95:8F:62:5A","service":"f5351050-b2c9-11ec-a0c0-b3bc53b08d33","characteristic":"f53513ca-b2c9-11ec-a0c1-639b8957db99","value":"66 33 65 35"}
D/Device: resolve: write|f5351050-b2c9-11ec-a0c0-b3bc53b08d33|f53513ca-b2c9-11ec-a0c1-639b8957db99 Characteristic successfully written.

Expected behavior Each string is sent/written in same order

Screenshots N/A

Plugin version:

  • @capacitor-community/bluetooth-le: 1.7.0

Smartphone (please complete the following information):

  • Device: Samsung A3 or any other
  • OS: Android
  • Browser N/A
  • Version N/A

Additional context N/A

vovagorodok avatar May 30 '22 22:05 vovagorodok

Adding mutex solves issue:

  async sendMsgToDevice(msg: string) {
    await this.lock.acquire()
    console.log(`vova: sendMsgToDevice: ${msg}`);
    await BleClient.write(this.getDeviceId(), this.uuids!.srv, this.uuids!.txCh, textToDataView(msg))
    this.lock.release()
  }

And we see:

I/Capacitor/Console: File: http://localhost/main.js - Line 12949 - Msg: vova: sendMsgToDevice: f1b5
D/Device: resolve: write|f5351050-b2c9-11ec-a0c0-b3bc53b08d33|f53513ca-b2c9-11ec-a0c1-639b8957db99 Characteristic successfully written.
I/Capacitor/Console: File: http://localhost/main.js - Line 12949 - Msg: vova: sendMsgToDevice: go
V/Capacitor/Plugin: To native (Capacitor plugin): callbackId: 100409782, pluginId: BluetoothLe, methodName: write
V/Capacitor: callback: 100409782, pluginId: BluetoothLe, methodName: write, methodData: {"deviceId":"34:AB:95:8F:62:5A","service":"f5351050-b2c9-11ec-a0c0-b3bc53b08d33","characteristic":"f53513ca-b2c9-11ec-a0c1-639b8957db99","value":"67 6f"}
D/Device: resolve: write|f5351050-b2c9-11ec-a0c0-b3bc53b08d33|f53513ca-b2c9-11ec-a0c1-639b8957db99 Characteristic successfully written.

Should BleClient.write() be multi-thread protected?

vovagorodok avatar May 30 '22 23:05 vovagorodok

Thank you for using this plugin.

I cannot reproduce the behavior you're describing. The BleClient has an internal queue which should handle this. Are you by any chance calling BleClient.disableQueue() (which is not recommended)?

pwespi avatar May 31 '22 19:05 pwespi

All is by default (queue is enabled I guess). Issue reproduces even with BleClient.disableQueue(). It happens not immediately, but after a while (a couple of chess games). And than I see this behavior each time when BleClient.write() is called immediately one after another (additionally two BleClient.write() called in the same thread/function).

I think You can reproduce it by:

async reproduce() {
  while (true) {
    await BleClient.write(..., textToDataView("text1"))
    await BleClient.write(..., textToDataView("text2"))
  }
}

And wait when only "text1" will be received on peripheral side

vovagorodok avatar Jun 01 '22 12:06 vovagorodok

Or this one:

/*not async*/ reproduce() {
  while (true) {
    write("text1")
    write("text2")
  }
}

async write(msg: string) {
  await BleClient.write(..., textToDataView(msg))
}

vovagorodok avatar Jun 01 '22 12:06 vovagorodok

I still cannot reproduce it. I can write 30 times in a row and every write goes through in the correct order.

Do you get any errors? Anything else in the logs that looks suspicious?

If I disable the queue, I get the error "Error: Writing characteristic failed." which is expected, because Android cannot handle simultaneous operations. (That's why the queue should not be disabled on Android).

A write call has a timeout of 5 seconds by default, so if your code calls the seconds write, there should at least be a timeout error.

pwespi avatar Jun 01 '22 19:06 pwespi

I still cannot reproduce it. I can write 30 times in a row and every write goes through in the correct order.

I think in my case it occurs after >~80 writes

Do you get any errors? Anything else in the logs that looks suspicious?

No, all logs that I see are attached here

Are you trying both methods of reproduction with async and non async reproduce()?

vovagorodok avatar Jun 01 '22 22:06 vovagorodok

Are you trying both methods of reproduction with async and non async reproduce()?

Yes, I tried both async and non async and got the same successful result (in case the queue is enabled).

I think in my case it occurs after >~80 writes

Also 200 writes is no problem in my tests.

pwespi avatar Jun 02 '22 18:06 pwespi

Hi, I have been able to reproduce this. Having several users of our app reporting issues, whilst most fine, I had initially put it down to faulty hardware. It seems all devices are Android 11 and budget devices. We picked up a cheap TCL 10" Android device, split our write down into smaller chunks and loop through. If we send too fast the device receiving errors out after 4-5 runs, around 80-150 writes.

I have put a small 20ms wait after each write and the issue has completely gone away. I hope this is helpful.

Thanks again for the plugin.

RichyFennell avatar Sep 16 '22 08:09 RichyFennell

Currently I don't see how this could be fixed in the plugin, as it's an issue with specific Android devices. Therefore I'm closing this issue for now. If anyone has an idea how this could be fixed, let me know and I'm happy to take another look.

pwespi avatar Apr 02 '23 15:04 pwespi