I often have the need to exchange some data between MCUs (on a point-to-point serial link or on a multi-drop bus network topology), or between one MCU and an application running on a full OS (Linux, Windows) written in a high-level language (C++, Python).
In both cases I think protobuf has some advantages (over my custom solutions), mainly because it is future-proof and more fields can be added without breaking compatibility with older version of messages.
When the messages are managed by a Python application, it will be very easy to decode and encode it.
Of course the firmware running on one MCU can't use dynamic memory allocation (malloc) and the RAM memory is often limited. So I looked at nanopb implementation of protobuf.
With my custom solution, when I have to serialize some data I write:
serialize_u8(get_data1());
serialize_str(get_data2());
etc
serialize_...() functions directly write the encoded data to an output buffer, mostly a transmit FIFO buffer of the UART peripheral.
With nanopb I have to create the instance of the message in RAM and only after that, encode it:
MyMessage msg;
msg.data1 = get_data1();
msg.has_data1 = true;
strcpy(msg.data2, get_data2());
msg.has_data2 = true;
pb_encode(&ostream, &msg);
In this example, I made the assumption data2 field is a fixed-length string.
If the message contains many fields, during serialization I need an additional memory space to temporarily save the message instance (msg variable).
Of course, it could be an automatic variable allocated on the stack by the compiler (so not consuming the RAM for global variables), but a stack overflow could occur if the message contains many fields.
Moreover, I understood you can't define a field as required in proto3, so every field is doubled with its has_... boolean field counterpart.
Is there a way to avoid this additional memory requirement?
Just to alleviate the RAM space needed when encoding/decoding, I'm thinking to group the fields of a long message in a few sub-messages.
message LongMessage {
message Group1 {
...
}
message Group2 {
...
}
Group1 grp1 = 1;
Group2 grp2 = 2;
}
Is it possible to encode/decode one submessage at a time?
Group1 grp1;
grp1.field1 = get_data...();
...
pb_encode(&ostream, Group1_fields, &grp1);
Group2 grp2;
grp2.field1 = get_data...();
...
pb_encode(&ostream, Group2_fields, &grp2);
I tried something, but it seems it doesn't work as expected.