When I was analyzing the source code of an opensource software, I found an interesting technique to parse incoming data in a very efficient way, using a struct.
This technique works if the received data has a fixed length and structure; for example it applies very well to data “packets”.
When receiving, the program normally stores incoming data in a buffer in the RAM memory. For example let’s assume that the packet has a length of 14 bytes:
char rx_buffer[14]; |
Your specific protocol defines the following fields in the packet:
[checklist]
- address, 5 bytes
- temperature value, 3 bytes
- humidity value, 3 bytes
- TX counter, 1 bytes
- checksum, 2 bytes
[/checklist]
You can define a struct that represents the structure of the packet:
typedef struct { uint8_t address[5]; uint8_t temperature[3]; uint8_t humidity[3]; uint8_t tx; uint8_t checksum[2]; } my_packet; |
The technique consists in applying, as if it were a template, the struct to the buffer:
in this way the received bytes are automatically divided into the different fields:
Therefore, to parse the data you only need to cast the buffer:
my_packet* parsed_data = (my_packet*)rx_buffer; |
and access the struct fields:
parsed_data->address; parsed_data->temperature; [...] |
Padding and packed
You still have a problem: to make it more efficient to access the memory, compliers normally align the variables based on their size. This means that – when a compiler allocates memory for your struct – some empty space (named padding) could be added between the different variables:
The presence of the padding space causes a misalignment if you apply the struct to the buffer with the received data (which are instead continous). Fortunately, it is possible to ask the compiler not to insert padding using the packed attribute:
typedef struct { uint8_t address[5]; uint8_t temperature[3]; uint8_t humidity[3]; uint8_t tx; uint8_t checksum[2]; }__attribute__((packed)) my_packet; |
If you remember to add this attribute, the technique works perfectly!
Isn’t parsed_data a pointer? To access the struct field, shouldn’t you use
parsed_data->address
instead of
parsed_data.address
you’re absolutely right! thanks
mi sembra una valida alternativa a json
Veramente interessante, grazie!