nanopb:一个用 ANSI C 写成,适合资源有限嵌入式系统的超迷你版 Protocol Buffers 库

什么是 nanopb?
nanopb,顾名思义就是“nano”+“protobuf”,一个超迷你版的 Protocol Buffers 库,用 ANSI C 写成。适合内存、ROM 都很紧张的嵌入式系统,像那些只有几十 KB RAM 的小单片机。平时我们都用 Google's protobuf,有时候可怕的代码体积和动态内存分配就让人头大。nanopb 就专治这些痛点。

痛点在哪里?
你可能遇到过这些事儿:

  • • 资源吃紧:常规 protobuf 生成的 C++/Java 库,占用几十 KB 甚至几百 KB,MCU 根本扛不住。

  • • 构建复杂:需要动态分配 / STL,移植成本高。

  • • 可定制性差:消息定义改了,整个库都得重编译,灵活性不够。

nanopb 轻量到只有 pb_encode.c、pb_decode.c、pb_common.c 三个源文件,完全不开动态内存,用栈或者固定 buffer 就能完事儿。

核心特性一览表

特性

描述

体积超小

编译后几 KB 级别

零动态内存

全部用固定 buffer,避免碎片和分配失败

ANSI C 兼容

不依赖 STL、RTTI、exceptions,几乎所有编译器都能编

可定制字段选项

支持 .options 文件,压缩、默认值、回调等

多种构建系统支持

Makefile、CMake、SCons、Bazel、Conan、PlatformIO……

测试覆盖

支持 host 测试、STM32、AVR 模拟等

怎么使用?

  1. 1. 写一个 .proto:

    syntax = "proto3";
    message SimpleMessage {
      int32 lucky_number = 1;
    }
  2. 2. 用 protoc 和 nanopb 插件生成代码:

    # 二进制包用户
    generator-bin/nanopb_generator simple.proto
  3. 3. 在项目里加上头文件和 pb_*.c 三个源码,再把生成的 .pb.h/.pb.c 扔进来。

  4. 4. 编写编码/解码逻辑,就像下面这样:

#include <stdio.h>
#include <pb_encode.h>
#include <pb_decode.h>
#include "simple.pb.h"

int main(){
    uint8_t buffer[128];
    size_t message_length;
    bool status;

// 编码
    {
        SimpleMessage msg =SimpleMessage_init_zero;
        msg.lucky_number =42;
        pb_ostream_t os = pb_ostream_from_buffer(buffer,sizeof(buffer));
        status = pb_encode(&os,SimpleMessage_fields,&msg);
        message_length = os.bytes_written;
        if(!status){
            printf("编码失败: %s\n", PB_GET_ERROR(&os));
            return 1;
        }
    }

// 解码
   {
        SimpleMessage msg =SimpleMessage_init_zero;
        pb_istream_t is = pb_istream_from_buffer(buffer, message_length);
        status = pb_decode(&is,SimpleMessage_fields,&msg);
        if(!status){
             printf("解码失败: %s\n", PB_GET_ERROR(&is));
             return 1;
        }
        printf("幸运数字:%d\n",(int)msg.lucky_number);
    }

    return 0;
}

优缺点随手一列

优点

缺点

体量小(几 KB)

功能不如官方 protobuf 全面

无动态分配,实时性更友好

一些高级特性(如 gRPC)不支持

ANSI C,易移植

需要手动管理 buffer 长度和对齐

支持多种编译/打包系统

对 C++ 更友好的使用体验欠佳

支持扩展 options,自定义程度高

文档分散,需要多翻官网、GitHub READMEs

深入一点:.options 文件
nanopb 允许你写个 yourmessage.options,对每个字段定制:

SimpleMessage.lucky_number:
  max_count: 1
  default: 7
  json_name: "luckyNum"

这样你不仅能定制长度、默认值,还能打 JSON 序列化功能——不过那得自己写回调。

测试和移植
nanopb 自带 SCons 测试套件,写完改了参数想跑一遍,直接:

cd tests
scons

支持在 STM32 开发板、AVR 模拟器上跑,有 demo。对于资源有更严格要求的场景,比如 FreeRTOS、Zephyr、Arduino,用在 LoRa、BLE、Zigbee 数据收发,都没问题。

总结
要给小内存设备做高效、可移植、低风险的数据打包,nanopb 是个“神器”级别的选择。它没那么多花里胡哨,但基础的序列化、反序列化做得很扎实。如果你正在为设备间通信头疼,赶紧试试 nanopb,体积分分钟腰斩,性能杠杠的。

项目地址:https://github.com/nanopb/nanopb

发表评论