Quando si integra strumentazione di terzi, una delle cause di errore più insidiose sta nell’ordine dei byte, non nell’algoritmo che li elabora. Lo strumento dichiara di esporre una grandezza fisica come float a 32 bit su un registro; noi la leggiamo e otteniamo 1.7e+38, oppure -0.0000000000000000003, oppure un NaN pulito. Il valore vero era 23,7. Nessuno dei due lati ha torto sul protocollo: ci siamo solo capiti male su come quei byte vanno rimontati.
Il caso
Catena tipica: un misuratore espone una lettura su Modbus, due registri a 16 bit consecutivi che insieme formano un float a 32 bit. Il nostro lato legge i registri, li concatena, li interpreta come float e stampa un numero che non ha senso fisico. Prima reazione, sbagliata, è dubitare del proprio codice di parsing dell’algoritmo. La reazione giusta è scendere al livello del singolo byte e chiedersi due cose distinte: in che ordine arrivano i byte, e come quei byte codificano un numero. Sono due problemi indipendenti, e quando i valori letti sono implausibili conviene escludere l’ordine dei byte prima di mettere in dubbio la codifica.
Endianness: lo stesso numero, byte in ordine diverso
Un intero o un float a 32 bit sono quattro byte. L’endianness è la convenzione su quale byte viene per primo in memoria o sul filo. Big-endian mette per primo il byte più significativo (MSB), nell’ordine in cui scriveremmo il numero a mano; little-endian mette per primo quello meno significativo (LSB). I quattro byte 41 BD 70 A4 interpretati big-endian valgono 23,68; gli stessi byte letti little-endian, cioè A4 70 BD 41, sono un float completamente diverso e privo di senso.
Modbus complica le cose perché lavora a registri di 16 bit big-endian, ma non specifica l’ordine fra i due registri di un float a 32 bit. Così nascono in pratica fino a quattro varianti — ABCD, CDAB, BADC, DCBA — a seconda che il costruttore inverta i byte, le word, entrambi o nessuno dei due (il famigerato word swap). Il datasheet, se è fatto bene, dice quale; se è fatto male, lo scopri a mano. Per questo, davanti a numeri assurdi, la prima mossa è provare sistematicamente i quattro byte-order: se una delle permutazioni restituisce il valore atteso, il problema era l’endianness e non la decodifica.
IEEE 754: come i byte diventano un numero
Una volta messi i byte nell’ordine giusto, resta il secondo problema: cosa significano. Un float a 32 bit segue lo standard IEEE 754 a singola precisione, che divide i 32 bit in tre campi: 1 bit di segno, 8 bit di esponente (con bias 127), 23 bit di mantissa (con un bit implicito a 1 davanti). Il valore è circa (-1)^segno · 1,mantissa · 2^(esponente−127). Capire questa decomposizione è ciò che permette di distinguere un errore di endianness da un valore legittimamente piccolo o grande: se il campo esponente è tutto a 1 il valore è un infinito o un NaN. Su un dato che dovrebbe essere una grandezza fisica misurata è un segnale: le cause possibili sono l’ordine dei byte, un disallineamento del frame o un campo non valido dichiarato dallo strumento, e vanno escluse in quest’ordine perché la prima costa meno da verificare.
Sugli interi lo stesso genere di errore si presenta col segno. I valori negativi si codificano in complemento a due: si invertono tutti i bit e si somma 1. Leggere un registro a 16 bit come unsigned quando lo strumento lo intende signed fa apparire un -3 come 65533 — di nuovo un numero “assurdo” che in realtà è solo interpretato con la convenzione sbagliata. E come per i float, su un valore multi-registro l’ordine dei byte va risolto prima di applicare il complemento a due.
Il metodo, non l’aneddoto
La lezione che ci portiamo dietro non è “attenzione a Modbus”. È un metodo: quando un valore numerico letto da un protocollo o da un registro è assurdo, si scende al byte grezzo e si separano i due livelli. Primo, l’ordine dei byte — big o little, con eventuale word swap. Secondo, la codifica — IEEE 754 per i float, complemento a due per gli interi con segno. Risolti nell’ordine giusto, il numero torna; e la risposta sta di regola nel datasheet, in una riga sull’ordine dei byte o sul formato del campo.
Per ispezionare a mano quattro byte qualsiasi nei diversi ordini e codifiche abbiamo messo online un Data Format Inspector; il dettaglio su endianness, IEEE 754 e complemento a due è nella wiki dedicata.
Stai integrando strumentazione su Modbus, seriale o un protocollo binario custom e i numeri non tornano? Parliamone.