# nanopb_test **Repository Path**: conquerhero/nanopb_test ## Basic Information - **Project Name**: nanopb_test - **Description**: 介绍nanopb_test的使用 - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 2 - **Created**: 2023-12-12 - **Last Updated**: 2023-12-12 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README @[toc] # 前言 在最近弄的**蓝牙**项目中,发现有些平台的内存非常小。如果使用前面介绍的**protobuf-c**进行数据的序列化,会因为内存限制没有办法跑起来; 对于嵌入式C语音需要进行数据的序列化**Nanopb**支持是比较好的,使用起来也相对比较简单;**Nanopb**编解码的接口是统一的(`pb_encode`和`pb_decode`),但是**protobuf-c**会为每一个不同的`message`生产一个编解码的接口。 # Nanopb下载与安装 下载地址:https://jpa.kapsi.fi/nanopb/download/ 我这里下载的是Linux版本:`nanopb-0.3.9.2-linux-x86.tar.gz` ![在这里插入图片描述](https://img-blog.csdnimg.cn/20190130150212782.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L1pIT05HQ0FJMDkwMQ==,size_16,color_FFFFFF,t_70) 我们在下载解压后,下面的几个文件将会在编解码时会用到:`pb_common.c`、`pb_common.h`、`pb_decode.c`、`pb_decode.h`、`pb_encode.c`、`pb_encode.h`、`pb.h`,这些文件放在我们需要移植的平台上。 ![在这里插入图片描述](https://img-blog.csdnimg.cn/20190130152717150.png) # 定义.proto文件 - 我这里随便定义了一个`UserInformation.proto`. ```c syntax = "proto3"; option optimize_for = LITE_RUNTIME; enum UserStatus { UNKNOWN = 0; IDLE = 1; BUSY = 2; } message UserInformation { string name = 1; uint32 age = 2; string phone = 3; UserStatus stat = 4; string email = 5; } ``` - 在`.proto`中**optimize_for**的参数: `option optimize_for = LITE_RUNTIME;` **optimize_for**是文件级别的选项,Protocol Buffer定义三种优化级别`SPEED/CODE_SIZE/LITE_RUNTIME`。缺省情况下是`SPEED`。 **SPEED**: 表示生成的代码运行效率高,但是由此生成的代码编译后会占用更多的空间。 **CODE_SIZE**: 和`SPEED`恰恰相反,代码运行效率较低,但是由此生成的代码编译后会占用更少的空间,通常用于资源有限的平台,如Mobile。 **LITE_RUNTIME**: 生成的代码执行效率高,同时生成代码编译后的所占用的空间也是非常少。这是以牺牲Protocol Buffer提供的反射功能为代价的。因此我们在C++中链接Protocol Buffer库时仅需链接libprotobuf-lite,而非libprotobuf。在Java中仅需包含protobuf-java-2.4.1-lite.jar,而非protobuf-java-2.4.1.jar。 >注:对于LITE_MESSAGE选项而言,其生成的代码均将继承自MessageLite,而非Message。 - **特别注意**:`Nanopb`编译器不支持`string`的动态定义,`Protobuf-c`编译出来是`char *`指针类型,但是`Nanopb`编译一定是`char`数组类型。所以,这里需要定义一个`UserInformation.options`: ```c // ******************************* // *** UserInformation options *** // ******************************* // UserInformation.name max_size:20 UserInformation.phone max_size:16 UserInformation.email max_size:30 ``` # 编译.proto文件 - 编译C code命令:`./generator-bin/protoc --nanopb_out=./ UserInformation.proto` ![在这里插入图片描述](https://img-blog.csdnimg.cn/20190130162917628.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L1pIT05HQ0FJMDkwMQ==,size_16,color_FFFFFF,t_70) - 编译C++ code命令:`./generator-bin/protoc --cpp_out=./ UserInformation.proto` ![在这里插入图片描述](https://img-blog.csdnimg.cn/20190130163147412.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L1pIT05HQ0FJMDkwMQ==,size_16,color_FFFFFF,t_70) # 测试demo code 对user data进行封包,主要4个步骤: - 定义并初始化`UserInformation userInfo`变量; - 对`userInfo`进行赋值; - 使用`pb_ostream_from_buffer`生成encode需要的`pb_ostream_t`,里面包含encode后输出的buffer addr; - 使用`pb_encode`进行编码; ```c static int pack_user_data(uint8_t *buffer) { //Initialize the UserInformation structure; UserInformation userInfo = UserInformation_init_zero; // Initialize buffer memset(buffer, 0, DATA_BUFFER_SIZE); strcpy(userInfo.name, "Benjamin"); userInfo.age = 18; strcpy(userInfo.phone, "0755-12345678"); userInfo.stat = UserStatus_IDLE; strcpy(userInfo.email, "ZhangSan@123.com"); // encode userInfo data pb_ostream_t enc_stream; enc_stream = pb_ostream_from_buffer(buffer, DATA_BUFFER_SIZE); if (!pb_encode(&enc_stream, UserInformation_fields, &userInfo)) { //encode error happened printf("pb encode error in %s [%s]\n", __func__,PB_GET_ERROR(&enc_stream)); return -1; } return enc_stream.bytes_written; } ``` 对user data进行解包,主要3个步骤: - 定义并初始化`UserInformation userInfo`变量; - 使用`pb_istream_from_buffer`生成decode需要的`pb_istream_t`,里面包含将要decode的buffer addr; - 使用`pb_decode`进行解码; ```c static int unpack_user_data(const uint8_t *buffer, size_t len) { UserInformation userInfo; // decode userInfo data pb_istream_t dec_stream; dec_stream = pb_istream_from_buffer(buffer, len); if (!pb_decode(&dec_stream, UserInformation_fields, &userInfo)) { printf("pb decode error in %s\n", __func__); return -1; } printf("Unpack: %s %d %s %s\n", userInfo.name, userInfo.age, userInfo.phone, userInfo.email); return 0; } ``` main函数进行调用 ```c int main() { uint8_t buffer[DATA_BUFFER_SIZE]; int lenght = pack_user_data(buffer); if(lenght<0){ printf("main: pack_user_data is fail!!!\n"); return -1; } printf("User data len: %d\n",lenght); unpack_user_data(buffer, lenght); return 0; } ``` 运行的结果如下: ![在这里插入图片描述](https://img-blog.csdnimg.cn/20190130184044887.png) # 完整的工程代码 gitee地址: https://gitee.com/dianqi0901zc/nanopb_test > 本博客地址:https://blog.csdn.net/ZHONGCAI0901/article/details/86705381