nanopb: The Tool for Embedded Systems to Master Protocol Buffers

What is nanopb?nanopb is a Protocol Buffers implementation specifically designed for resource-constrained devices (such as microcontrollers and IoT chips). It is written entirely in ANSI C, with a code size of just a few KB, consuming almost no memory, and boasts excellent compiler compatibility. Want to efficiently transfer structured data between an MCU and a server? nanopb can help you achieve that.

What pain points does it address?

Pain Point Traditional Approach Benefits of nanopb
Large code size The official protobuf C++ implementation is over several hundred KB Only a few KB, suitable for chips with 32 KB Flash
Many runtime dependencies Requires dynamic memory and complex libraries Completely based on static allocation, zero heap
Cross-platform portability issues Compiler features are not uniform, high porting costs Pure ANSI C, can be compiled by almost all compilers
Manual serialization is cumbersome Handwritten byte streams are prone to errors Automatically generates .pb.c/.pb.h, with one-click encoding and decoding API calls
Debugging difficulties Binaries are not easily readable, slow error localization Supports optional debug macros to print field names for quick localization

Installation & Environment Setup

  1. 1. Download the source code or binary package
    git clone https://github.com/nanopb/nanopb.git
    # Or directly download the precompiled zip package
  2. 2. Prepare protoc and plugins
  • • The official packages for Windows/macOS/Linux already include <span>protoc</span> and <span>nanopb_generator</span>.
  • • If compiling yourself, you need to install Python + <span>pip install protobuf grpcio-tools</span>.
  • 3. Add the generated C files to your project Drag <span>pb_encode.c pb_decode.c pb_common.c</span> into your project directory, ensuring the compiler can find them.
  • Tip: If you are using PlatformIO, CMake, or Makefile, nanopb has already provided corresponding templates (in the <span>extra/</span> directory), just copy-paste them.

    Quick Start: One-Step Completion

    1. 1. Write .proto (for example, <span>msg.proto</span>)
      syntax = "proto2";
      
      message SensorData {
        required uint32 id   = 1;
        required float  temp = 2;
        optional bool   alarm = 3 [default = false];
      }
    2. 2. Generate code
      python generator/nanopb_generator.py msg.proto
      # Generates msg.pb.c / msg.pb.h
    3. 3. Call in your code
      #include "msg.pb.h"
      
      // Encoding
      SensorData data = SensorData_init_default;
      data.id = 123;
      data.temp = 36.5f;
      uint8_t buffer[64];
      pb_ostream_t stream = pb_ostream_from_buffer(buffer, sizeof(buffer));
      bool ok = pb_encode(&amp;stream, SensorData_fields, &amp;data);
      // stream.bytes_written contains the serialized bytes
      
      // Decoding
      SensorData recv = SensorData_init_default;
      pb_istream_t istream = pb_istream_from_buffer(buffer, stream.bytes_written);
      ok = pb_decode(&amp;istream, SensorData_fields, &amp;recv);

      With just a few lines, the structure directly maps to protobuf, making it easy and efficient.

    Overview of Advantages and Disadvantages

    Advantages Disadvantages
    Very small footprint: a few KB of code, suitable for low-end MCUs Incomplete functionality: does not support advanced features of proto3 such as all-optional and map
    No heap: fully statically allocated, safe and reliable Generates many files: each .proto generates .pb.c/.pb.h, leading to code bloat
    Cross-platform: can be compiled by almost all C compilers Learning curve: needs understanding of .proto syntax and nanopb options file
    Customizable: controls field compression, default values, etc. via <span>.options</span> Debug information must be manually enabled: debug macros are off by default, error localization needs to be manually turned on
    Rich ecosystem: complete integration scripts for CMake, Make, PlatformIO, Arduino, etc. No support for C++ class encapsulation: can only use C API, object-oriented programming requires manual encapsulation

    Conclusion

    nanopb is truly a protobuf version tailored for embedded systems. It compresses a protocol that was originally only usable on servers and PCs into a few KB C library, allowing microcontrollers to easily implement structured communication. Although it does not support the latest advanced features of proto3, it is already powerful enough for most IoT, sensor, and Bluetooth scenarios. Once you master the steps of writing and generating <span>.proto</span>, the subsequent encoding/decoding is almost a straightforward operation—saving time, effort, and memory.

    If you are struggling with data interaction between MCUs and the cloud, or want to add a reliable serialization solution to your Arduino/ESP32 projects, nanopb is worth a try. Once you try it out, when the code runs, you will realize: embedded systems can also master “protocol buffers”!

    Project address: https://github.com/nanopb/nanopb

    Leave a Comment