【FireBeetle 2 ESP32 C6开发板】1、移植nanopb到esp32上
<div class='showpostmsg'> 本帖最后由 walker2048 于 2024-5-13 15:36 编辑### 前言
很早以前就对espnow感兴趣,近段时间才开始玩。在使用espnow的过程中,每一次传输的数据长度只有250字节(最大情况下)。如果使用Json之类的常见序列化信息传输方式,这个数据长度有点尴尬。这时候,我想起去年尝试GRPC的时候,偶然间学习到的一个内容,也就是Protobuf。
![](https://pica.zhimg.com/v2-6fbb22690047c24bb3da00e6bfcca01e_720w.jpg?source=172ae18b)
Google Protocol Buffer( 简称 Protobuf) 是 Google 公司内部的混合语言数据标准,目前已经正在使用的有超过 48,162 种报文格式定义和超过 12,183 个 .proto 文件。他们用于 RPC 系统和持续数据存储系统。
Protocol Buffers 是一种轻便高效的结构化数据存储格式,可以用于结构化数据串行化,或者说序列化。它很适合做数据存储或 RPC 数据交换格式。可用于通讯协议、数据存储等领域的语言无关、平台无关、可扩展的序列化结构数据格式。
#### nanopb简介
但是Google官方的Protobuf工具并不能生成直接用于小型单片机使用的C语言代码,我找到了github上大佬实现的一个小型化的Protobuf支持库,也就是nanopb。它是一个轻量级的Protobuf库,虽然功能并不是非常强大,用它来做一些扁平化的数据序列化是非常不错的选择。
#### 一、移植代码
在官方github仓库下(https://github.com/nanopb/nanopb),可以直接找到需要的几个文件。
#### 二、添加CMakeList.txt文件
为了让esp-idf顺利编译nanopb,我们需要添加CMake组件配置文件,文件内容如下
这里添加了nanopb的源码文件,以及simple案例的代码文件。宏定义抄了OpenHarmony开源组件的定义和nanopb源码的定义,我也没详细搞清楚到底是什么。
```c
idf_component_register(SRCS "pb_common.c" "pb_decode.c" "pb_encode.c" "simple.c" "simple.pb.c"
INCLUDE_DIRS .)
add_definitions("-D_GNU_SOURCE" "-D_HAS_EXCEPTIONS=0" "-DHAVE_CONFIG_H" "-DPB_ENABLE_MALLOC")
```
#### 三、编译运行
编译后可以看到,程序是可以正常运行的,也顺利将数据结构封包和解包了,包长度109,比JSON原始数据长度要小一半多。
并且flash体积也只增加了12K,可以说以非常小的体积实现了对应的功能。
并且封包的数据,使用常规的Protocol Buffer解析程序,也能解析到传输的内容,已经达到了我的目的(最小代码修改情况下,实现小体积和跨平台的数据序列化)。
#### 四、代码分析
以下是这个案例的数据报文定义:
```c
// simple.proto
syntax = "proto3";
import "nanopb.proto";
message SimpleMessage {
int32 luckyNumber = 1;
int32 unluckyNumber = 2;
string ip = 3[(nanopb).max_length = 16];
}
message SimpleStruct {
repeated SimpleMessage msg =1;
}
```
在这里定义了一个简单的消息数据结构,包含了两个int32长度的数字,以及一个长度16的ip字符串。然后再定义了一个可以包含零到多个简单消息数据的SimpleStruct数据。
测试代码如下:
由于我们定义了一个数量不确定的SimpleStruct数据,这个数据结构就不是扁平化的了,它不能直接用nanopb的解包和封包函数,需要另行编写回调函数来处理这个数据结构的封包和解包。
而SimpleMessage这个数据结构是固定的,它可以直接使用nanopb的原有函数直接处理。
```c
#include <stdio.h>
#include <pb_encode.h>
#include <pb_decode.h>
#include "simple.pb.h"
typedef struct {
SimpleMessage message;
int32_t num;
} MsgList;
void MsgListAddMsg(MsgList* list, int32_t lucky, int32_t unlucky, char* str){
if(list->num < 12){
list->message.luckyNumber = lucky;
list->message.unluckyNumber = unlucky;
strcpy(list->message.ip, str);
list->num++;
}
}
bool MsgList_encode(pb_ostream_t *ostream, const pb_field_t *field, void * const *arg)
{
MsgList* source = (MsgList*)(*arg);
// encode all numbers
for (int i = 0; i < source->num; i++)
{
if (!pb_encode_tag_for_field(ostream, field))
{
const char * error = PB_GET_ERROR(ostream);
printf("SimpleMessage_encode_numbers error: %s", error);
return false;
}
if (!pb_encode_submessage(ostream, SimpleMessage_fields, &source->message))
{
const char * error = PB_GET_ERROR(ostream);
printf("SimpleMessage_encode_numbers error: %s", error);
return false;
}
}
return true;
}
bool MsgList_decode_single_message(pb_istream_t *istream, const pb_field_t *field, void **arg)
{
MsgList * dest = (MsgList*)(*arg);
SimpleMessage msg = SimpleMessage_init_zero;
if (istream != NULL && field->tag == SimpleStruct_msg_tag)
{
if (!pb_decode(istream, SimpleMessage_fields, &msg))
{
const char * error = PB_GET_ERROR(istream);
printf("MsgList decode single_message error: %s", error);
return false;
}
memcpy(&dest->message, &msg, sizeof(msg));
printf("Your lucky number was %ld, unlucky is %ld, ip is %s!\n", msg.luckyNumber, msg.unluckyNumber, msg.ip);
};
return true;
}
int pbtest()
{
/* This is the buffer where we will store our message. */
uint8_t buffer;
size_t message_length;
bool status;
/* Encode our message */
{
/* Allocate space on the stack to store the message data.
*
* Nanopb generates simple struct definitions for all the messages.
* - check out the contents of simple.pb.h!
* It is a good idea to always initialize your structures
* so that you do not have garbage data from RAM in there.
*/
SimpleStruct stu = SimpleStruct_init_zero;
/* Create a stream that will write to our buffer. */
pb_ostream_t stream = pb_ostream_from_buffer(buffer, sizeof(buffer));
/* Fill in the lucky number */
MsgList list = {};
MsgListAddMsg(&list, 14, 3, "192.168.50.40");
MsgListAddMsg(&list, 18, 31, "192.168.50.144");
MsgListAddMsg(&list, 31, 40, "192.168.50.131");
MsgListAddMsg(&list, 42, 12, "192.168.50.142");
MsgListAddMsg(&list, 25, 51, "192.168.50.141");
for(int i=0; i< list.num; i++){
printf("lucky number was %ld, unlucky is %ld, ip is %s!\n", list.message.luckyNumber, list.message.unluckyNumber, list.message.ip);
}
stu.msg.arg = &list;
stu.msg.funcs.encode = MsgList_encode;
/* Now we are ready to encode the message! */
status = pb_encode(&stream, SimpleStruct_fields, &stu);
message_length = stream.bytes_written;
/* Then just check for any errors.. */
if (!status)
{
printf("Encoding failed: %s\n", PB_GET_ERROR(&stream));
return 1;
}
}
/* Now we could transmit the message over network, store it in a file or
* wrap it to a pigeon's leg.
*/
/* But because we are lazy, we will just decode it immediately. */
{
/* Allocate space for the decoded message. */
SimpleStruct stu = SimpleStruct_init_zero;
MsgList newlist = {};
stu.msg.arg = &newlist;
stu.msg.funcs.decode = MsgList_decode_single_message;
/* Create a stream that reads from the buffer. */
pb_istream_t stream = pb_istream_from_buffer(buffer, message_length);
printf("Encoded length is %d\n", message_length);
for(int i = 0; i < message_length; i++){
printf("%02x", buffer);
}
printf("\nEnd of buffer\n");
/* Now we are ready to decode the message. */
status = pb_decode(&stream, SimpleStruct_fields, &stu);
/* Check for errors... */
if (!status)
{
printf("Decoding failed: %s\n", PB_GET_ERROR(&stream));
return 1;
}
}
return 0;
}
```</div><script> var loginstr = '<div class="locked">查看本帖全部内容,请<a href="javascript:;" style="color:#e60000" class="loginf">登录</a>或者<a href="https://bbs.eeworld.com.cn/member.php?mod=register_eeworld.php&action=wechat" style="color:#e60000" target="_blank">注册</a></div>';
if(parseInt(discuz_uid)==0){
} </script><script type="text/javascript">(function(d,c){var a=d.createElement("script"),m=d.getElementsByTagName("script"),eewurl="//counter.eeworld.com.cn/pv/count/";a.src=eewurl+c;m.parentNode.insertBefore(a,m)})(document,523)</script>
页:
[1]