Bluetooth Low Energy (BLE) tiene un límite conocido de 20Bytes en la longitud de las cadenas enviadas. Hay formas de anular este límite.
Hardware
En este tutorial, enviaremos datos desde una aplicación Android, desarrollada con React Native, a un ESP32. Los métodos descritos también pueden ser válidos para otros dispositivos.
- Dispositivo Android
- ESP32 BLE
BLE limitado a 20 bytes
La aplicación desarrollada bajo React Native es perfectamente capaz de enviar cadenas de caracteres largas en forma de paquete de 20bytes por defecto. Sin modificar el tutorial anterior, este es el valor recibido en el lado del ESP32. Sólo se almacena en memoria el último paquete.
01:48:39.734 -> ESP32BLE server ready 01:48:40.493 -> Server address:3c:61:05:31:5f:12 01:48:41.672 -> Client connected 01:48:48.169 -> Charac value: Helloworldthisisalon 01:48:48.253 -> Charac value: gstringletseewhatcom 01:48:48.374 -> Charac value: esout; 01:48:48.374 -> this is the last packet
Resumen de métodos para enviar cadenas largas a través de BLE
- Recuperar varios paquetes (en el lado del receptor)
- Enviar la cadena larga en forma de paquetes pequeños (lado emisor)
- Aumentar el límite de MTU de 23 bytes a 512 bytes
Recuperar varios paquetes
Si el dispositivo que envía los mensajes es capaz de gestionar el envío de cadenas de caracteres que superan el límite de la MTU, como es el caso de nuestro ejemplo, sólo queda configurar el módulo receptor para que concatene los distintos paquetes recibidos. Para ello, utilizaremos un carácter de fin de cadena, como «;».
En la función onWrite de la característica del módulo ESP32BLE, añadimos un test de fin de cadena y una variable longMsg para que actúe como buffer.
std::string longMsg; class MyCallbacks: public BLECharacteristicCallbacks { void onWrite(BLECharacteristic *pCharacteristic) { std::string value = pCharacteristic->getValue(); if (value.length() > 0) { Serial.print("Charac value: "); for (int i = 0; i < value.length(); i++) Serial.print(value[i]); Serial.println(); if(value.find(";") != -1){ //Serial.println("this is the last packet"); longMsg = longMsg + value; Serial.print("longMsg: "); for (int i = 0; i < longMsg.length(); i++) Serial.print(longMsg[i]); Serial.println(); //do whatever you want with longMsg then erase longMsg = ""; }else{ longMsg = longMsg + value; } } } };
Envío de paquetes de tamaño predefinido
Si el dispositivo que envía los datos no gestiona mensajes de tamaño superior a la MTU, es posible dividir la cadena de caracteres en paquetes de tamaño predefinido (chunksize). Este método permite controlar el tamaño de los paquetes enviados.
En el proyecto React Native, estamos creando una función que transformará la cadena de caracteres largos en un array de bytes que enviaremos en paquetes.
const sendLongMessage = async (data: string, chunksize: number) => { console.log("data : ",data) var bytes = convertStringToByteArray(data) var times = Math.ceil(bytes.length/chunksize); var packets = []; console.log("split data ",times," times"); //console.log("bytes: ",bytes) var i=0 for(var time=0;time<times;time++){ var packet = bytes.slice(time*chunksize, (time+1)*chunksize) packets.push(packet) console.log(time," : ",byteArrayToString(packet)) sendMessage(byteArrayToString(packet)); await delay(100); } }
N.B.: se puede introducir una función de retardo para dar tiempo a que se envíe el mensaje. delay(100)
const delay = (ms: number) => new Promise( resolve => setTimeout(resolve, ms) );
Aumentar el límite de MTU de la aplicación
Por defecto, la librería react-native-ble-manager envía paquetes de 20bytes. Existe un método para especificar que el módulo BLE debe modificar el tamaño de la Unidad Máxima de Transmisión (MTU)
En el código de la aplicación, durante la conexión, antes de recuperar los servicios, añada una solicitud de MTU
BleManager.requestMTU(device.id, 512).then( (mtu) => { setMTU(mtu); } ) //request the MTU size in bytes
Al enviar el mensaje, puedes especificar el tamaño máximo del mensaje en bytes, «mtu», en la función BLEManager.write().
BleManager.write( selectedDevice.id, serviceid, caracid, buffer.toJSON().data, mtu // or 512 ).then(() => { // Success code console.log("Write: " + buffer.toJSON().data); })
En el lado ESP32, hay un método en la biblioteca BLEDevice para definir el tamaño de MTU.
BLEDevice::setMTU(512); //between 23 and 517 (bluetooth 4.2)
N.B.: En este ejemplo (Android+ESP32), la única forma de aumentar el límite es definir el número máximo de bytes en la función BLEManager.write(). Dejaremos el resto de información que pueda ser útil para otra configuración (iOS, HM10, etc.).
Una vez modificado el código, cuando devolvamos la misma cadena de caracteres, recibiremos su valor completo en la función BLE
Aplicaciones
- Aumentar el tamaño de los mensajes enviados al módulo BLE
- Imitar el funcionamiento de Bluetooth classic de forma puntual