在嵌入式设备上使用 Protocol Buffers (Protobuf) 进行数据序列化和解析

Protocol Buffers (Protobuf) 是 Google 开发的二进制序列化格式,适合嵌入式设备上的数据交换,因为它高效、紧凑。但在资源受限的嵌入式系统(如 ARM-based MCU)上,标准 Protobuf 库可能太大(内存占用高),推荐使用轻量版如 nanopb(C 语言实现,易移植到 C++)。如果你的设备资源充足(如运行 Linux 的嵌入式板,如 Raspberry Pi),可使用标准 Protobuf C++ 库。下面我详细介绍整个流程:从 proto 文件生成 C++ 代码,到交叉编译和移植。假设你的主机是 Linux(如 Ubuntu),目标设备是 ARM 架构(如 Cortex-M 或 Cortex-A)。如果设备是无 OS 的 MCU,优先 nanopb;否则用标准版。

1. 准备工作
  • 安装 Protobuf 工具
    • 在主机上安装 Protobuf 编译器(protoc)。用包管理器:
      sudo apt update
      sudo apt install protobuf-compiler libprotobuf-dev  # Ubuntu/Debian
      
      或从 GitHub 下载源码编译(https://github.com/protocolbuffers/protobuf)。
  • 安装交叉编译工具链:根据目标设备架构安装 toolchain。例如,对于 ARM Cortex-A(如 Raspberry Pi):
    sudo apt install gcc-arm-linux-gnueabihf g++-arm-linux-gnueabihf
    
    对于 Cortex-M(如 STM32),用 arm-none-eabi-gcc(从 ARM 官网下载)。
  • 选择 Protobuf 变体
    • 标准 Protobuf C++:适合有 OS 的设备,功能全但体积大。
    • nanopb:推荐嵌入式无 OS 设备,轻量(几 KB),支持 C++ 包装。下载:https://github.com/nanopb/nanopb。
2. 编写 Proto 文件

创建一个 .proto 文件定义数据结构。例如,名为 message.proto

syntax = "proto3";  // 或 proto2,根据需求

package mypackage;

message SensorData {
  int32 temperature = 1;
  float humidity = 2;
  string timestamp = 3;
  repeated uint8 raw_data = 4;  // 支持重复字段
}
  • 保存为 message.proto。这定义了一个消息结构,用于序列化/解析传感器数据。
3. 根据 Proto 文件生成 C++ 文件

使用 protoc 生成 C++ 头文件(.pb.h)和源文件(.pb.cc)。生成的代码包含序列化(Encode)和解析(Decode)函数。

  • 标准 Protobuf 生成

    protoc --cpp_out=. message.proto
    
    • 输出:message.pb.hmessage.pb.cc
    • 在 C++ 代码中使用:
      #include "message.pb.h"
      #include <fstream>  // 用于文件 I/O 测试
      
      int main() {
        mypackage::SensorData data;
        data.set_temperature(25);
        data.set_humidity(60.5);
        data.set_timestamp("2026-02-05");
      
        // 序列化到字符串
        std::string serialized;
        data.SerializeToString(&serialized);
      
        // 解析回来
        mypackage::SensorData parsed;
        parsed.ParseFromString(serialized);
        // 输出 parsed.temperature() 等
        return 0;
      }
      
  • nanopb 生成(推荐嵌入式)

    1. 下载 nanopb 并解压到本地目录(如 ~/nanopb)。
    2. 用 nanopb 生成器:
      python ~/nanopb/generator/nanopb_generator.py message.proto
      
      • 输出:message.pb.hmessage.pb.c(C 风格,但易用 C++ 包装)。
    3. 在 C++ 代码中使用(需包含 nanopb 头文件):
      #include "message.pb.h"
      #include <pb_encode.h>  // nanopb 库
      #include <pb_decode.h>
      
      int main() {
        SensorData data = SensorData_init_zero;
        data.temperature = 25;
        data.humidity = 60.5;
      
        uint8_t buffer[128];
        pb_ostream_t stream = pb_ostream_from_buffer(buffer, sizeof(buffer));
        pb_encode(&stream, SensorData_fields, &data);  // 序列化
      
        // 解析
        SensorData parsed = SensorData_init_zero;
        pb_istream_t istream = pb_istream_from_buffer(buffer, stream.bytes_written);
        pb_decode(&istream, SensorData_fields, &parsed);
        return 0;
      }
      
    • nanopb 支持选项文件(.options)自定义字段大小,优化内存。
4. 交叉编译源码

将生成的 .pb.cc/.pb.c 和你的应用代码交叉编译成目标设备的可执行文件或库。假设项目目录有 main.cpp、message.pb.h、message.pb.cc。

  • 标准 Protobuf 交叉编译

    1. 下载 Protobuf 源码(https://github.com/protocolbuffers/protobuf),交叉编译库:
      ./configure --host=arm-linux-gnueabihf --prefix=/usr/arm-linux-gnueabihf  # 调整为你的 toolchain
      make -j4
      make install
      
    2. 编译你的应用:
      arm-linux-gnueabihf-g++ -o app main.cpp message.pb.cc -lprotobuf -pthread  # 链接 Protobuf 库
      
      • 如果是静态链接:加 -static 避免运行时依赖。
  • nanopb 交叉编译

    1. nanopb 无需编译库(纯头文件),只需包含其源文件。
    2. 编译应用:
      arm-none-eabi-g++ -o app.elf main.cpp message.pb.c ~/nanopb/pb_encode.c ~/nanopb/pb_decode.c ~/nanopb/pb_common.c \
        -I~/nanopb -mcpu=cortex-m4 -mthumb  # 调整 CPU 架构
      
      • 对于 MCU,生成 .elf 或 .bin 文件,用于烧录。
  • 常见优化

    • -Os 优化大小,-g0 去除调试信息。
    • 处理依赖:确保 toolchain 有标准库(如 newlib for bare-metal)。
    • 测试:在主机上先用本地 g++ 编译运行,验证序列化/解析正确。
5. 移植到嵌入式设备
  • 传输文件:用 scp/rsync 或串口工具(如 minicom)将编译好的二进制(app 或 app.elf)传到设备。
    • 示例:scp app root@192.168.1.100:/usr/bin/
  • 运行
    • 有 OS 设备:SSH 登录,./app 执行。
    • 无 OS MCU:用烧录工具(如 OpenOCD、ST-Link)上传 .bin/.hex 文件,重启设备。
  • 调试
    • 用 GDB 交叉调试:arm-linux-gnueabihf-gdb app + target remote :3333(需 OpenOCD server)。
    • 监控内存:嵌入式上 Protobuf 序列化后数据紧凑,检查 buffer 大小避免溢出。
  • 注意事项
    • 资源限制:标准 Protobuf 需 ~100KB+ 内存;nanopb 只需几 KB。测试动态分配(避免在 MCU 上用 new)。
    • 端序:Protobuf 是小端序,确保设备一致或用 htonl/ntohl 处理。
    • 依赖:无 OS 设备避免 STL,用纯 C。
    • 安全:序列化数据易篡改,添加校验和。
    • 如果设备是特定平台(如 ESP32),用平台 SDK 集成(如 Arduino-Protobuf)。

这个流程适用于大多数嵌入式场景。如果你的设备具体型号(如 STM32F4),或遇到错误,提供更多细节我可以细化!

Logo

openvela 操作系统专为 AIoT 领域量身定制,以轻量化、标准兼容、安全性和高度可扩展性为核心特点。openvela 以其卓越的技术优势,已成为众多物联网设备和 AI 硬件的技术首选,涵盖了智能手表、运动手环、智能音箱、耳机、智能家居设备以及机器人等多个领域。

更多推荐