Wednesday 28 September 2016

Interoperability between Nanopb and googles Protocol Buffers

As part of a project to build an AVR controlled toaster oven reflow controller I decided to use Googles Protocol Buffers and Nanopb to serialise the command and control data between the .net pc software and the atmega32u2 based control board. The details of the toaster oven reflow controller will be covered in a separate blog entry.
There are where a number of compatibility issues between the two libraries that I found workarounds for. I didn't find a huge amount of information about this topic on line so I decided to share what I found.

Version Compatibility

The first issue is one of Protocol Buffers and Nanopb protocol version compatibility. Nanopb only supports version 2, proto2 definitions, and the current .net Google.Protobuf only supports version 3, proto3 definitions.
For nanopb a message must be defined as follows:
syntax = "proto2";

message Request {

    enum RequestType {
        REQUESTONE = 0;
        REQUESTTWO = 1;
    }

    required RequestType Command = 1;
}
For Google Protocol Buffers the corresponding message must be defined as follows:
syntax = "proto3";

message Request {

    enum RequestType {
       REQUESTONE = 0;
       REQUESTTWO = 1;
    }

    RequestType Command = 1;
}
The version 3.0.0 release of Google Protocol Buffers proto2 is not supported, the required keyword has been deprecated as it not supported in proto3.

Stack space and error strings

Nanopb is designed to run on memory constrained devices. The atmega32u2 device has 1024 bytes of SRAM available for stack and heap storage. I discovered during the course of testing the controller that the code on the atmega32u2 device could not decode messages that contained more than 3 or 4 fields. Using the debugger I determined that this was due to stack overrun. Luckily the only side effect as there was not a huge amount of free SRAM memory available on the device. Looking at the memory usage of the Nanopb code I could see that there is some overhead in the definition of the fields for a message. Each field defined consumes 10 bytes of SRAM for the number of fields defined in the messages that was 240 bytes.

When the atmega32u2 code is complied the static analysis of the compiler indicates the following:

Program Memory Usage     :    17732 bytes   54.1 % Full
Data Memory Usage         :    747 bytes   72.9 % Full

Looking through the code for Nanopb I noticed that the error strings in the stream methods where not loaded from flash memory. Which is default behaviour in AVR code unless steps are taken to instruct the compiler, using the PROGMEM macro to locate the literal string constants in flash.

PB_RETURN_ERROR(stream, "end-of-stream");

Launching the debugger and browsing through the IRAM of the device shows the strings taking up memory.

image
Nanopb does support turning off error messages by using a compiler flag PB_NO_ERRMSG. Defining this flag for the build results in the following:

Program Memory Usage     :    16248 bytes   49.6 % Full
Data Memory Usage         :    377 bytes   36.8 % Full

Basically a small, ~5% reduction in flash memory but a 50% reduction in SRAM usage.

No comments:

Post a Comment