完整的 Ubuntu 上安装 nanopb 并实现简单 protobuf 通信的教程。nanopb 是专为嵌入式系统优化的轻量级 C 语言 Protobuf 实现,零动态内存分配,非常适合资源受限环境。


一、安装 nanopb

方法 A:下载二进制包(推荐,最简单)

nanopb 官网提供包含 protoc 编译器的完整二进制包:

# 下载最新版(以 0.4.8 为例,可到 https://jpa.kapsi.fi/nanopb/download/ 查看最新版本)
wget https://jpa.kapsi.fi/nanopb/download/nanopb-0.4.8-linux-x86.tar.gz
​
# 解压到 /opt
tar -zxvf nanopb-0.4.8-linux-x86.tar.gz
sudo mv nanopb-0.4.8-linux-x86 /opt/nanopb
​
# 添加到环境变量(可选,方便使用)
echo 'export PATH=$PATH:/opt/nanopb/generator-bin' >> ~/.bashrc
source ~/.bashrc

方法 B:使用系统包管理器

# 安装 protobuf 编译器和 Python 支持
sudo apt-get update
sudo apt-get install -y build-essential protobuf-compiler python3-protobuf
​
# 克隆 nanopb 源码
git clone https://github.com/nanopb/nanopb.git
cd nanopb

二、创建 Protobuf 定义文件

创建 simple.proto,定义一个简单的传感器消息:

syntax = "proto2";
​
import "nanopb.proto";
​
message SensorData {
    required int32 id = 1;
    required float temperature = 2;
    optional string name = 3 [(nanopb).max_size = 40];
    repeated int32 readings = 4 [(nanopb).max_count = 10];
}

关键注意事项

  • 使用 proto2 语法(nanopb 对 proto2 支持更完善)

  • 字符串和重复字段必须指定最大长度/数量(使用 (nanopb).max_size(nanopb).max_count

  • import "nanopb.proto" 是为了使用 nanopb 特定的选项


三、生成 C 代码

方法 A(二进制包用户):

# 使用 nanopb 自带的 protoc
/opt/nanopb/generator-bin/protoc --nanopb_out=. simple.proto

方法 B(系统 protoc 用户):

# 使用系统 protoc,需指定 nanopb 插件路径
protoc --plugin=protoc-gen-nanopb=nanopb/generator/protoc-gen-nanopb --nanopb_out=. simple.proto

执行后会生成两个文件:

  • simple.pb.h:结构体定义和元数据

  • simple.pb.c:字段定义


四、编写测试代码

创建 main.c,实现编码和解码:

#include <stdio.h>
#include <string.h>
#include "simple.pb.h"
#include "pb_encode.h"
#include "pb_decode.h"
​
int main(void)
{
    uint8_t buffer[128];
    size_t message_length;
    bool status;
​
    /* ========== 编码阶段 ========== */
    {
        SensorData message = SensorData_init_zero;
        pb_ostream_t stream = pb_ostream_from_buffer(buffer, sizeof(buffer));
​
        // 填充数据
        message.id = 42;
        message.temperature = 23.5;
        strcpy(message.name, "TemperatureSensor");
        
        // 填充数组
        message.readings_count = 3;
        message.readings[0] = 200;
        message.readings[1] = 210;
        message.readings[2] = 205;
​
        // 编码
        status = pb_encode(&stream, SensorData_fields, &message);
        message_length = stream.bytes_written;
​
        if (!status) {
            printf("编码失败: %s\n", PB_GET_ERROR(&stream));
            return 1;
        }
​
        printf("编码成功,长度: %zu 字节\n", message_length);
        printf("原始字节: ");
        for (size_t i = 0; i < message_length; i++) {
            printf("%02X ", buffer[i]);
        }
        printf("\n\n");
    }
​
    /* ========== 解码阶段 ========== */
    {
        SensorData message = SensorData_init_zero;
        pb_istream_t stream = pb_istream_from_buffer(buffer, message_length);
​
        // 解码
        status = pb_decode(&stream, SensorData_fields, &message);
​
        if (!status) {
            printf("解码失败: %s\n", PB_GET_ERROR(&stream));
            return 1;
        }
​
        // 打印结果
        printf("解码成功:\n");
        printf("  ID: %d\n", message.id);
        printf("  温度: %.1f\n", message.temperature);
        printf("  名称: %s\n", message.name);
        printf("  读数(%d个): ", message.readings_count);
        for (uint8_t i = 0; i < message.readings_count; i++) {
            printf("%d ", message.readings[i]);
        }
        printf("\n");
    }
​
    return 0;
}

五、编译运行

1. 准备核心库文件

将 nanopb 的核心文件复制到你的项目目录(或用路径引用):

  • pb.h

  • pb_common.c / pb_common.h

  • pb_encode.c / pb_encode.h

  • pb_decode.c / pb_decode.h

  • 生成的 simple.pb.c / simple.pb.h

2. 编译

gcc -o test_protobuf main.c simple.pb.c pb_encode.c pb_decode.c pb_common.c -I. -Wall

3. 运行

./test_protobuf

预期输出

编码成功,长度: 21 字节
原始字节: 08 2A 15 00 00 BC 41 1A 11 54 65 6D 70 65 72 61 74 75 72 65 53 65 6E 73 6F 72 22 06 C8 01 D2 01 CD 01 
​
解码成功:
  ID: 42
  温度: 23.5
  名称: TemperatureSensor
  读数(3个): 200 210 205 

六、项目文件结构参考

my_nanopb_project/
├── simple.proto          # Protobuf 定义
├── simple.pb.h          # 生成的头文件
├── simple.pb.c          # 生成的源文件
├── main.c               # 你的应用程序
├── pb.h                 # nanopb 核心库
├── pb_common.c/h
├── pb_encode.c/h
└── pb_decode.c/h

关键特性说明

  1. 零动态内存:所有内存分配都是静态的,基于你在 .proto 中指定的 max_sizemax_count

  2. 小体积:典型情况下编解码器仅需 2-10KB Flash,适合 MCU

  3. 字段选项:更多高级选项(如固定长度数组、回调函数处理大数据)可参考nanopb 文档

如果你需要处理更复杂的消息(如嵌套结构、字符串回调、oneof 等),可以参考 nanopb 自带的 examples/simpleexamples/network_server 示例。

 

 

 

 

 

1. protobuf 在嵌入式ARM平台的应用(c语言版) - 云的边界 - 博客园

2. protobuf在嵌入式linux下的移植及c语言调用-阿里云开发者社区

3. 嵌入式Linux下Protobuf库的编译与应用 - hczyydqq - 博客园

4. Linux环境下Protobuf完整安装和使用教程_linux protoc找不到-CSDN博客

5. 【protobuf】Windows与Linux下的安装详解-腾讯云开发者社区-腾讯云

posted on 2025-07-01 23:30  轩~邈  阅读(49)  评论(0)    收藏  举报