文章目录
前言
最近项目上有用到把传感器数据封装成Protobuf协议再上传到平台解析的需求,嵌入式这边用到的是SMT32L4平台,这就涉及到在STM32环境下移植和封装Protobuf协议了。之前使用过nanopb,nanopb是也是一个轻量的、支持C语言的Protobuf,但是使用过程极其麻烦,不同的数据有不同的处理方法,比如,string类型,分为固定长度和动态长度,不同的处理方法难度大不一样。后来使用protobuf-c翻译器成功实现了Protobuf封装协议和数据传输。
本文通过Protobuf封装数据,进行序列化之后,通过串口传到平台进行解析。
一、Protobuf简介
Protocol Buffers,是Google公司开发的一种数据格式,类似于XML能够将结构化数据序列化,可用于数据存储、通信协议等方面。protobuf支持一些主流的语言,唯独没有支持C,所以诞生了第三方的protobuf-c。数据序列化协议用于将数据结构序列化成可方便存储、传输的格式,ProtoBuf是一个被广泛使用的序列化协议,它具有比json和xml体积小、使用方便、扩展性和兼容性好等特点。
二、使用步骤
2.1 Protobuf翻译器介绍
2.2 利用Protobuf.c编辑器生成.c和.h文件
将自己的.proto文件放在bin目录下,例如,我的.proto文件为lora.proto和mcs.proto。具体步骤如下:
注意:在命令行输入gen之后,有可能提示缺少.dll文件,这需要将该dll文件下载下来,在放入C:\Windows\System32中。
lora.proto和mcs.proto文件分别为:
lora.proto:
syntax = "proto3";
package proto;
option go_package = "./model/proto";
import "mcs/mcs.proto"; //引入的Data类型的结构体文件路径
message Sensor {
string version = 1; // 协议版本,必填 v1 1.0
MessageType message_type = 2; //消息类型,必填
repeated SensorComponent type = 3; // 传感器组成
ProjectName name = 4; // 传感器名称,传感器型号和传感器名字至少填一个
SensorModel model = 5; // 传感器型号, 传感器型号和传感器名字至少填一个
string Sn = 6; // 传感器唯一标识,必填,只要可以唯一标识设备即可 3011310001
repeated SensorHealthStatus status = 7; // 传感器状态,如果正常,不填写该字段
int64 publish_time = 8; //消息发布时间
// 心跳包才发送
string description = 9; // 传感器描述
double longitude = 10; // 经度
double latitude = 11; //纬度
string software_version = 12; // 传感器软件版本
string hardware_version = 13; // 传感器硬件版本
// 数据包
mcs.Data data = 14; // 数据信息
}
enum ProjectName {
PROJECT_NAME_CUSTOM = 0;
PROJECT_NAME_FQPL = 1;
PROJECT_DLCGYMCS = 2;
}
enum SensorModel {
SENSOR_MODEL_DEFAULT = 0;
SENSOR_MODEL_MCS600 = 100;
SENSOR_MODEL_MCS1000 = 110;
}
// 传感器类型
enum SensorComponent {
SENSOR_TYPE_CUSTOM = 0; // 自定义传感器
SENSOR_TYPE_TEMPERATURE = 1; // 温度传感器
SENSOR_TYPE_HUMIDITY = 2; // 湿度传感器
SENSOR_TYPE_PRESSURE = 3; // 压力传感器
SENSOR_TYPE_LIGHT = 4; // 光照传感器
SENSOR_TYPE_CURRENT = 5; // 电流传感器
SENSOR_TYPE_VOLTAGE = 6; // 电压传感器
SENSOR_TYPE_POWER = 7; // 功率传感器
SENSOR_TYPE_ENERGY = 8; // 电能传感器
SENSOR_TYPE_POWER_FACTOR = 9; // 功率因数传感器
SENSOR_TYPE_POSITION = 10; // 位置传感器
SENSOR_TYPE_VELOCITY = 11; // 速度传感器
SENSOR_TYPE_MOTION = 12; // 运动传感器
SENSOR_TYPE_DISTANCE = 13; // 距离传感器
SENSOR_TYPE_ANGLE = 14; // 角度传感器
SENSOR_TYPE_ROTATION = 15; // 旋转传感器
SENSOR_TYPE_DIRECTION = 16; // 方向传感器
SENSOR_TYPE_ALTITUDE = 17; // 海拔传感器
SENSOR_TYPE_DEPTH = 18; // 深度传感器
SENSOR_TYPE_VOLUME = 19; // 容量传感器
SENSOR_TYPE_DENSITY = 20; // 密度传感器
SENSOR_TYPE_FORCE = 21; // 力传感器,拉力应力等
SENSOR_TYPE_ENERGY_STORED = 22; // 储能传感器
SENSOR_TYPE_FLOW = 23; // 流量传感器
SENSOR_TYPE_IMAGE = 24; // 图像传感器
SENSOR_TYPE_MAGNETIC = 25; // 磁场传感器
SENSOR_TYPE_INFRARED = 26; // 红外传感器
SENSOR_TYPE_COLOR = 27; // 颜色传感器
SENSOR_TYPE_SOUND = 28; // 声音传感器
}
// 传感器状态
enum SensorHealthStatus {
SENSOR_STATUS_CUSTOM = 0; // 自定义状态
SENSOR_STATUS_LOW_BATTERY = 1; // 低电量
SENSOR_STATUS_NETWORK_WARNING = 2; // 网络流量告警
SENSOR_STATUS_TEMPERATURE_WARNING = 3; // 温度告警
}
//消息类型
enum MessageType {
HEARTBEAT = 0;//心跳
DATA = 1;//数据
CHECK_TIME = 2; //对时
}
mcs.proto文件:
syntax = "proto3";
package mcs;
option go_package = "./model/proto";
message Data {
int64 sample_time = 1; //采样时间
float ground_current_a_amplitude = 2; //A相地线电流幅值
float ground_current_a_phase = 3; //A相地线电流相位
float ground_current_b_amplitude = 4; //B相地线电流幅值
float ground_current_b_phase = 5; //B相地线电流相位
float ground_current_c_amplitude = 6; //C相地线电流幅值
float ground_current_c_phase = 7; //C相地线电流相位
float conductor_current_a_amplitude = 8; //A相芯线电流幅值
float conductor_current_a_phase = 9; //A相芯线电流相位
float conductor_current_b_amplitude = 10; //B相芯线电流幅值
float conductor_current_b_phase = 11; //B相芯线电流相位
float conductor_current_c_amplitude = 12; //C相芯线电流幅值
float conductor_current_c_phase = 13; //C相芯线电流相位
}
2.3 Keil5配置与编写
在自己的工程目录下创建一个Protobuf文件夹:
将2.2第4步中生成的.c和.h文件以及2.1中protobuf-c文件夹中的protobuf-c.c和protobuf-c.h文件一起复制到Protobuf文件夹中。
打开MDK5:
1.按照如下步骤操作:
2.之后再加入路径:
3.添加头文件
#include "lora.pb-c.h"
#include "protobuf-c.h"
#include "mcs.pb-c.h"
4.再编译
5.程序编写
Proto__Sensor Sen = PROTO__SENSOR__INIT;
Proto__Sensor *msg = NULL;
Mcs__Data Data = MCS__DATA__INIT;
uint8_t sensor_pack_buffer[512];
uint32_t Sen_len;
static void init_data(Mcs__Data* p)
{
// p->sample_time = (int)buf;
}
static void init_sensor(Proto__Sensor* p)
{
p->version = "1.0";
p->message_type = PROTO__MESSAGE_TYPE__DATA;
// p->type = Type;
// p->n_type = PROTO__SENSOR_COMPONENT__SENSOR_TYPE_CURRENT;
p->name = PROTO__PROJECT_NAME__PROJECT_NAME_FQPL;
p->sn = "3011310001";
p->publish_time = (int)buf;
p->data = &Data;
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_USART1_UART_Init();
MX_RTC_Init();
while (1)
{
/* USER CODE END WHILE */
init_data(&Data);
init_sensor(&Sen);
Data.ground_current_a_amplitude = 1.3;
Data.ground_current_a_phase = 2.4;
Sen_len = proto__sensor__get_packed_size(&Sen);
proto__sensor__pack(&Sen,sensor_pack_buffer);
printf(sensor_pack_buffer);
HAL_Delay(1000);
/* USER CODE BEGIN 3 */
}
}
2.4 结果与分析
2.4.1 Protobuf封装序列化之后结果(16进制hex)
2.4.2 hex数据解码结果
2.4.3 分析
通过解码发现,Protobuf封装的数据经过通信协议传输到平台与解码得到的数据一样,则Protobuf移植成功。
有需要Protobuf编译器或代码的朋友可私信联系我!