什么是 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. 写一个
.proto:syntax = "proto3";
message SimpleMessage {
int32 lucky_number = 1;
} -
2. 用 protoc 和 nanopb 插件生成代码:
# 二进制包用户
generator-bin/nanopb_generator simple.proto -
3. 在项目里加上头文件和 pb_*.c 三个源码,再把生成的
.pb.h/.pb.c扔进来。 -
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