Byte, char, hex e parsing di una risposta da un sensore bluetooth

Ciao a tutti, sto impazzendo per capire come parsare una risposta di un sensore bluetooth LE. Tecnicamente è una EIR, Extended Inquiry Response e dovrebbe contenere tutti i dati che mi servono.

La risposta è in uno struct così definito

typedef struct {
  LGATTAddress bd_addr;
  int32_t rssi;
  uint8_t eir_len;
  uint8_t eir[256];
} LGATTDeviceInfo;

Se stampo LGATTDeviceInfo.eir esce questa cosa:

216986953656E736F72209FFBECA2C24603E3A0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000

Qui dentro ci sono tutti i dati che mi servono ma devo capire se c’è una logica per estrarli.

Il sensore letto dall’app di iOS è questo:

A me servono esclusivamente il nome del dispositivo e i manufacturer data.

Il nome del dispositivo è iSensor, e corrisponde a 6953656E736F72.

I manufacturer data come si vede dallo screenshot sono BECA2C02-46028785. La prima parte non cambia mai, è l’identificativo univoco del sensore (BECA2C02) mentre la seconda parte è parzialmente variabile.

Essendo un sensore magnetico di antifurto cicla in questo modo

<beca2c02 46025856> - > open
<beca2c02 46005955> - > close
<beca2c02 46025a58> - > open
<beca2c02 46005b57> - > close
<beca2c02 46025c5a> - > open
<beca2c02 46005d59> - > close
<beca2c02 46025e5c> - > open
<beca2c02 46005f5b> - > close

in pratica 00/02 definiscono apertura e chiusura, poi ci sono degli incrementali che cambiano ad ogni apertura chiusura. A me sostanzialmente interessa capire se il sensore è quello che dico io (quindi usando BECA2C02 ) e verificare se lo stato è 02.

Quindi nell’eir ho

21698 | non so cosa siano
6953656E736F72 | device name, iSensor
209FF | non so cosa siano
BECA2C2 | dovrebbe essere la prima parte ma manca uno 0 davanti al due per diventare BECA2C02
4603E3A | dovrebbe essere la seconda parte ma al posto di 4603E3A dovrei avere 46 00 3E3A oppure 46 02 3E3A se il contatto fosse aperto.

Qualcuno ha idea di che formato siano e di come convertire il tutto per permettermi di lavorare questo eir e di fare dei condizionali sul device e sullo stato?

Hai già provato a vedere nelle specifiche pubblicate da www.bluetooth.org? Ossia: https://www.bluetooth.com/specifications/adopted-specifications

Questo è il documento: Core_v5.0.pdf (26 Mbyte)

Il formato della risposta EIR lo vedo descritto a pagina 2039 (Vol 3, Part C, Section 8 ).

Ho visto ma sono una capra con ste cose... ora provo a capire meglio

docdoc: Hai già provato a vedere nelle specifiche pubblicate da www.bluetooth.org? Ossia: https://www.bluetooth.com/specifications/adopted-specifications

Questo è il documento: Core_v5.0.pdf (26 Mbyte)

Il formato della risposta EIR lo vedo descritto a pagina 2039 (Vol 3, Part C, Section 8 ).

Allora l'ho studiato un attimino ma credo che mi manchino le basi.

Parto dal documento dell'ai che mi dice che la risposta è fatta così

typedef struct {
  LGATTAddress bd_addr;
  int32_t rssi;
  uint8_t eir_len;
  uint8_t eir[256];
} LGATTDeviceInfo;

Prendo alcune eir e i dati sono questi

21613FF4C0CE8E1B87D83CB43431A739F4496E0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
Advertising data lenght: 23

2167FF4C0102B00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
Advertising data lenght: 11

216320A0109524543414D3532474A5830313235315125005002A0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
Advertising data lenght: 33

Ho provato a coppie ma non capisco da dove arrivi quel 23, 11, 33!!!!

Beh io non ho mai studiato questo argomento, ma la prima cosa che non mi quadra è come hai ottenuto quei codici esadecimali. Se sono la trascrizione dell'array uint8_t eir[256] visto che le stringhe che hai riportato hanno lunghezze diverse, ho l'impressione che tu abbia convertito semplicemente in hex senza leading zero, ossia i valori da 0 a F sono rappresentati con un solo carattere tra 0 ed F invece di 00 - 0F per cui alcuni valori non te li ritrovi!

Infatti leggo nelle specifiche:

The significant part contains a sequence of data structures. Each data structure shall have a length field of one octet, which contains the Length value, and a data field of Length octets. The first n octets of the data field contain the extended inquiry response (EIR) data type. The content of the remaining Length - n octets in the data field depends on the value of the EIR data type and is called the EIR data.

Quindi stando a quanto posso capire, ogni struttura è composta da un byte con la lunghezza della struttura, che a sua volta è composta da EIR data type ("n" byte) seguito da EIR data.

Prendiamo uno dei dati:

216986953656E736F72209FFBECA2C24603E3A0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000

21 = lunghezza della struttura 1 (0x21= 33 byte) quindi prendiamo i successivi 33 byte. Ma già qui qualcosa non quadra, perché il device name parte dalla quarta cifra hex ossia è:

698*6953656E736*...

mentre secondo me dovrebbe essere qualcosa del tipo:

69*086953656E736*...

Verifica quindi bene COME hai stampato quei valori, prendi SEMPRE due caratteri nella conversione in esadecimale e riposta qui il risultato corretto.

Ecco cosa non tornava!!!

[LGATTC ino]dev address : [8:46:be:ca:2c:2] rssi [-63]

2 1 6 9 8 69 53 65 6E 73 6F 72 20 9 FF BE CA 2C 2 46 2 5F 5D 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 
Advertising data lenght: 23

Ora torna?

Molto meglio infatti…
Ma il print fallo convertendo sempre in 2 cifre hex, è tutto più leggibile, usa questa:

void PrintHex8(uint8_t *data, uint8_t length) // prints 8-bit data in hex with leading zeroes
{
       for (int i=0; i<length; i++) { 
         if (data[i]<0x10) {Serial.print("0");} 
         Serial.print(data[i],HEX); 
         Serial.print(" "); 
       }
}

Ora, giusto per non farti tutto il lavoro io, se quindi ti metto i byte (tranne gli zeri finali) in questo modo a te cosa suggerisce? :slight_smile:

02 01 06
09 08 69 53 65 6E 73 6F 72 20
09 FF BE CA 2C 02 46 02 5F 5D

In realtà è ancora leggermente diverso

|500x196

il nome parte dopo 0x08 i manufacturer data dopo 0xFF e

Ora vedo di mettere con memcpy in uno struct i dati che mi servono!

typedef struct {
  string name;
  byte battery;
  byte status;
  byte device[8];
}typeBLEInfo;

Potrebbe andare?

robypez: In realtà è ancora leggermente diverso il nome parte dopo 0x08 i manufacturer data dopo 0xFF

Ma certo che inizia dopo 0x08 così come il primo inizia dopo 0x01 e il terzo dopo 0xFF, perché dici che è "leggermente diverso"? Nella specifica (e anche nell'immagine che hai mandato tu stesso) vedo che il secondo byte è marcato "length" ossia la lunghezza del blocco.

Quindi:

02 01->1 byte: 06 09 08->8 byte: 69 53 65 6E 73 6F 72 20 09 FF->255 byte (ossia tutto il resto): BE CA 2C 02 46 02 5F 5D 00 eccetera...

Eh, io non ci ho ancora capito moltissimo!!! E non trovo neppure un qualcosa che mi spiega la cosa!!! :frowning:

Scusa, rettifico per correggere parzialmente quello che avevo scritto un po’ troppo di fretta e chiarire meglio: il primo byte è “length”, il secondo indica il tipo di blocco (come indicato nell’immagine che hai postato, ad esempio 08 significa “SHORTENED_NAME” ossia nome) seguito dai dati del blocco.

Quindi tu ti fai un ciclo per analizzare in sequenza il byte array a partire dal primo byte, quello indica la lunghezza del blocco che estrarrai e interpreterai, per poi passare ai successivi, fino a quando il primo byte analizzato è “00” che significa che è finito il dato.
Provo a buttarti giù due righe di codice ma non solo non ti assicuro che possa funzionare, ma DEVI tu iniziare a scrivere codice, che poi qui eventualmente ti vediamo e correggiamo!

...
for (int p = 0; p<=255; p++) 
{
  // Inizio blocco
  int lungh = LGATTDeviceInfo.eir[p];
  uint8_t buf[256];
  for (int i=0; i<=255; i++) 
  {
    if ( i < lungh )
      buf[i] = LGATTDeviceInfo.eir[p+i+1];
    else
      buf[i] = 0;
    p += lungh;
    // ora ho in "lungh" la lunghezza del blocco, in "buf[]" il buffer dati
    switch ( buf[0] ) {
    case 0x01: // GAP_AD_TYPE_FLAGS
      // ignoro il blocco (??)
      break;
    case 0x08: // GAP_AD_TYPE_SHORTENED_NAME
      // Leggo il nome
      // Converto il byte array in string e memorizzo in DeviceName
      // ...
      break;
    case 0xFF: // GAP_AD_TYPE_MANU_SPECIFIC_DATA
      // Leggo i dati sensore, 4 byte per ID sensore, 4 byte per i dati
      // ...
      break;
    }
  }
}

Incredibile ma sono riuscito a farlo funzionare

int AlarmSupport::alarmTriggered(int num)
{
  LGATTDeviceInfo info = {0};
   typeBLEInfo mInfo;
   int numberAlert = 0;
   const uint8_t device_filter[] = { 0xbe, 0xca, 0x2c, 0x02};
   
   for (int i = 0; i < num; i++)
   {
      c.getScanResult(i, info);

      int eir_lenght = info.eir_len;
      int lenSensorName = info.eir[8];
      int lenManufacturerData = info.eir[8];
      
      memcpy(&mInfo, &info, eir_lenght);
      memcpy(&mInfo.nameBLE, &info.eir[5], 8); 
      memcpy(&mInfo.sensor_code, &info.eir[15], 8);

      if (memcmp(mInfo.sensor_code, device_filter, 4) == 0) {

        if(mInfo.sensor_code[5] == 0x02 ){
          memcpy(&BLEInfo[numberAlert], &mInfo, 32);
          numberAlert++;
          Serial.println("\n<<<<<< Aperto >>>>>>\n");     
        } else if(mInfo.sensor_code[5] == 0x00) {
         Serial.println("\n<<<<<< Chiuso >>>>>>\n");       
       }
      }
   }
  return numberAlert;
}

Adesso ho messo hardcoded il codice del sensore, il prossimo step è inserire i codici su un file di testo che leggo al boot così filtro solo per quelli.

Comunque va!

Programma iniziato

<<<<<< Chiuso >>>>>>

<<<<<< Aperto >>>>>>

<<<<<< Chiuso >>>>>>

<<<<<< Aperto >>>>>>