X
首页
技术
模拟电子
单片机
半导体
电源管理
嵌入式
传感器
最能打国产芯
应用
汽车电子
工业控制
家用电子
手机便携
安防电子
医疗电子
网络通信
测试测量
物联网
最能打国产芯
大学堂
首页
直播
专题
TI 培训
论坛
汽车电子
国产芯片
电机驱动控制
电源技术
单片机
模拟电子
PCB设计
电子竞赛
DIY/开源
嵌入式系统
医疗电子
颁奖专区
【厂商专区】
【电子技术】
【创意与实践】
【行业应用】
【休息一下】
最能打国产芯
活动中心
直播
发现活动
颁奖区
电子头条
参考设计
下载中心
分类资源
文集
排行榜
电路图
Datasheet
最能打国产芯
RF/无线
[原创] 【FireBeetle 2 ESP32 C6开发板】1、移植nanopb到esp32上
walker2048
2024-5-13 15:31
楼主
### 前言 很早以前就对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
#include
#include
#include "simple.pb.h" typedef struct { SimpleMessage message[12]; int32_t num; } MsgList; void MsgListAddMsg(MsgList* list, int32_t lucky, int32_t unlucky, char* str){ if(list->num < 12){ list->message[list->num].luckyNumber = lucky; list->message[list->num].unluckyNumber = unlucky; strcpy(list->message[list->num].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[dest->num], &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[256]; 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; } ```
本帖最后由 walker2048 于 2024-5-13 15:36 编辑
点赞
回复评论
暂无评论,赶紧抢沙发吧
最新活动
有奖直播报名中!抢占工业4.1先机 文晔科技日等你来!
2024 瑞萨电子MCU/MPU工业技术研讨会——深圳、上海站, 火热报名中
罗姆有奖直播 | 重点解析双极型晶体管的实用选型方法和使用方法
STM32N6终于要发布了,ST首款带有NPU的MCU到底怎么样,欢迎小伙们来STM32全球线上峰会寻找答案!
免费下载 | 安森美电动汽车充电白皮书,看碳化硅如何缓解“里程焦虑”!
是德科技有奖直播 | 应对未来高速算力芯片的设计与测试挑战
随便看看
请教由积分器和带通滤波器组成的电路
程昌南同志的AT91SAM7S64调试笔记
什么产品能在27楼看清楼下的一辆车?
关于2812的PDPINT的问题
跪求~~~2008年黑龙江省电子设计大赛赛题A题分析或者设计文档
无线网状网络的网状拓扑的兼容性设计考量
打算用msp430,听说不能被破解,不知真假?
通过vnc软件远程连接WIN7与LINUX的问题
情有独钟,迷情电子三五年(一)
当年导师和ADI合作的大作《你好,放大器》非常适合大学生和初级工程师,推荐下载
合兴集团招聘机械工程师—汽车连接器
不负等待,STM32视频号及B站账号开通啦!
经过整流滤波稳压电路后,输出电压加了负载后电压下降
win ce下键盘锁有什么实现思路?
去饱和保护的问题
同轴线相关资料PDF
3月31日ARM/Linux/WinCE讲座(上海)
关于modelsim仿真问题!
什么是可见光线性光敏传感器?
【机智云Gokit3测评】+手机配网
电子工程世界版权所有
京B2-20211791
京ICP备10001474号-1
京公网安备 11010802033920号
回复
写回复
收藏
回复