Linux下Protobuf之Nanopb安装和使用

前言

在最近弄的蓝牙项目中,发现有些平台的内存非常小。如果使用前面介绍的protobuf-c进行数据的序列化,会因为内存限制没有办法跑起来;

对于嵌入式C语音需要进行数据的序列化Nanopb支持是比较好的,使用起来也相对比较简单;Nanopb编解码的接口是统一的(pb_encodepb_decode),但是protobuf-c会为每一个不同的message生产一个编解码的接口。

Nanopb下载与安装

下载地址:https://jpa.kapsi.fi/nanopb/download/
我这里下载的是Linux版本:nanopb-0.3.9.2-linux-x86.tar.gz
在这里插入图片描述
我们在下载解压后,下面的几个文件将会在编解码时会用到:pb_common.cpb_common.hpb_decode.cpb_decode.hpb_encode.cpb_encode.hpb.h,这些文件放在我们需要移植的平台上。
在这里插入图片描述

定义.proto文件

  • 我这里随便定义了一个UserInformation.proto.
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;
}
  • .protooptimize_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:

// *******************************
// *** 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
    在这里插入图片描述
  • 编译C++ code命令:./generator-bin/protoc --cpp_out=./ UserInformation.proto
    在这里插入图片描述

测试demo code

对user data进行封包,主要4个步骤:

  • 定义并初始化UserInformation userInfo变量;
  • userInfo进行赋值;
  • 使用pb_ostream_from_buffer生成encode需要的pb_ostream_t,里面包含encode后输出的buffer addr;
  • 使用pb_encode进行编码;
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进行解码;
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函数进行调用

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;
}

运行的结果如下:
在这里插入图片描述

完整的工程代码

gitee地址: https://gitee.com/dianqi0901zc/nanopb_test

  • 6
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值