Emill / node-ble-host

A Bluetooth Low Energy host implementation for Node.js
ISC License
55 stars 15 forks source link

How to write large amount of data? #6

Closed PhantomRay closed 2 years ago

PhantomRay commented 2 years ago

First of all, this is an excellent library for raspberry pi!

I am experimenting the features, however not so sure to write large data, e.g. 10KB.

Could you please give an example?

Thanks.

Emill commented 2 years ago

Sure. Could you explain your setup?

Do you use this library on both devices (central and peripheral)? Or do to need to interact with some specific Bluetooth stack on the other end? In that case, does this library run peripheral or central role?

PhantomRay commented 2 years ago

Hi Emill I only use it in raspberry pi in peripheral mode. Same code as in README. Then on the client side, I use flutter to talk to it. Also I use Bluetooth Inspector link to interact with it.

PhantomRay commented 2 years ago

Also, I came across a problem in iOS (not an issue on android). In iOS it spit this error and won't actually write(send) data to raspberry pi.

CoreBluetooth[WARNING] Characteristic <CBCharacteristic: 0x178081f90 UUID = 249C2001-00D7-4D91-AC75-22D57AE2FFB8, Value = (null), Properties = 0x28, Notifying = YES, Broadcasting = NO> does not specify the "Write Without Response" property - ignoring response-less write**

It's mentioned and explained here https://stackoverflow.com/questions/25672376/ios-ble-write-without-response-property-ignoring-response-less-write

I feel it's an issue on the ble-host side.

Emill commented 2 years ago

If you see https://github.com/Emill/node-ble-host/blob/master/docs/api/gatt-server.md#characteristicproperties, add write-without-response to your characteristic declaration. Then you can start sending "Write Without Response" packets. You need to split your 20 kB data into smaller chunks according to the MTU.

Let me know if you have further questions or need more details.

PhantomRay commented 2 years ago

Good to know. Thanks Emill.

I will try on my end.

PhantomRay commented 2 years ago

Hi @Emill Just wondering is there any sample code to deal with splitting data into chunks?

If I manually do this, I would consider to concatenate the chunks, checksum etc to ensure data integrity.

Your help and guidance is much appreciated.

Emill commented 2 years ago

I have never used Flutter, which is a UI toolkit. Even if you use Flutter for your UI, the actual BLE code must in the end be written in Java/Kotlin for Android and ObjC/Swift for iOS. If you then want your BLE logic in Flutter, you must write a bridge to the native code. Luckily, there are often available flutter libs you can use where people have already implemented this bridge. Since I'm not familiar with either Flutter or any of these pre-made plugins, I can't give you any code examples in Flutter.

What I can say though, is that in native code, you use the https://developer.apple.com/documentation/corebluetooth/cbperipheral/1620312-maximumwritevaluelengthfortype?language=objc method to retrieve the maximum size you can write each time. I suggest you use "write without response" for maximum throughput since those do not require a complete roundtrip per packet. For Android, you use the https://developer.android.com/reference/android/bluetooth/BluetoothGattCallback#onMtuChanged(android.bluetooth.BluetoothGatt,%20int,%20int) callback which you will receive after calling https://developer.android.com/reference/android/bluetooth/BluetoothGatt#requestMtu(int), for example with 517 as argument. The maximum size you can write in each packet is then the resulting MTU-3, but never larger than 512.

Another option would be to use L2CAP CoC, where you can write data messages up to 65535 bytes. This is supported by this library and can be used in later Android and iOS versions. See https://developer.android.com/reference/android/bluetooth/BluetoothDevice.html#createL2capChannel(int), https://developer.android.com/reference/android/bluetooth/BluetoothDevice.html#createInsecureL2capChannel(int) and https://developer.apple.com/documentation/corebluetooth/cbperipheral/2880151-openl2capchannel for more info.

PhantomRay commented 2 years ago

Hi Emill Thank you very much for your detailed message. I guess my question is not language specific. So any language is ok. Js/Java/python etc.

I just don’t want to reinvent the wheel if there is a good practice there already. My main concern is do I have to worry about data integrity when combining chunks of data. Or I don’t have to worry about it at all because the underlying layer will ensure it?

I ask it in another way - is there an example for file transfer in any language when talking to a service using this library?

Kind regards

Emill commented 2 years ago

Lower layers already include CRC, so you generally don't have to worry about bits getting corrupt or similar.

I don't know any existing file library that already does this, but generally just write a packet, then wait for the callback before writing the next packet.

On Android, you use the writeCharacteristic method, then wait for the onCharacteristicWrite callback. On iOS, you use the writeValue method, then wait for the peripheralIsReadyToSendWriteWithoutResponse callback. This is to make sure you don't write packets faster than the remote device can keep up with, since the BLE methods are otherwise always asynchronous.

PhantomRay commented 2 years ago

I think that's all I need to know. Very good information.

This is the best library I came across for raspberry pi using node. In fact other libraries cannot even run. Tried bleno and forks, very buggy.

Cheers!

Emill commented 2 years ago

Thanks so much :)

PhantomRay commented 2 years ago

Hi @Emill I have one question, when I split data into chunks, and send them to the other end, how does the other party know it has received all chunks?

Emill commented 2 years ago

You can use any strategy to deal with that.

For example, send a first chunk only containing an integer how many chunks will follow.