在设计espnow协议的时候,考虑到我需要在esp32,Linux设备,web上使用相同的数据结构,那就需要考虑一下,是否使用一个通用的跨平台序列化数据结构。这时候我想起了protobuf,这个就是我需要的。
我之前在esp32上使用过protobuf(nanopb),用起来也是比较简单的。
这里我先初步大致写了一下espnow自定义协议需要使用的一些数据结构。
syntax = "proto3";
import "options/gorm.proto";
import "protos/SMN_type.proto";
package smn;
option go_package = "./smn";
// Shrimp Mesh NetInfo Data
// 网络数据包结构体
message NetInfo {
option (gorm.opts) = { ormable: true };
uint32 device_id = 1 [(gorm.field).tag = {type: "uuid" primary_key: true}]; // 节点ID
uint32 group_id = 2; // 组ID
uint32 level = 3; // 节点层级
uint32 status = 4; // 设备状态
SMNDevice device_type = 10; // 设备类型
bytes node_mac = 11; // 节点设备mac地址
uint32 rssi = 15; // 信号强度
uint32 response_time = 16; // 平均响应时间
uint32 packet_loss_rate = 20; // 丢包率
Bandwidth bandwidth_type = 24; // 设备带宽类型
uint32 heartbeat_interval = 25; // 设备心跳间隔时间(ms级)
uint32 latest_heartbeat_time = 26; // 最后心跳上报时间
}
// SystemInfo Data
// 系统信息数据包结构体
message SystemInfo {
uint32 device_id = 1; // 节点ID
uint32 cpu_temperature = 2; // cpu温度
uint32 cpu0_load = 3; // cpu0使用率
uint32 cpu1_load = 4; // cpu1使用率
uint32 ram_load = 5; // 内存使用率
uint32 free_ram = 6; // 剩余内存
uint32 uptime = 10; // 上电时间(秒级)
}
// EspnowDevice Data
// 设备信息数据包结构体
message EspnowDevice {
uint32 device_id = 1; // 节点ID
string device_name = 2; // 设备名称
string alias_name = 4; // 设备别名
string manufacturer = 10; // 厂家
string model = 11; // 设备型号
string hardware_version = 15; // 硬件版本
string firmware_version = 20; // 固件版本
}
// Device Model
// 设备模型数据结构体
message DeviceModel {
DeviceModelType type = 1; // 设备类型
uint32 num = 2; // 有多少个
}
// Device Model Data
// 设备模型清单
message DeviceModelList {
DeviceModel data = 1; // 设备类型
}
以及基本的类型和枚举定义
syntax = "proto3";
package smn;
option go_package = "./smn";
// Shrimp Mesh Network Command
// 层级:物理层 → 网络层 → 应用层
enum SMNCommand {
// 设备发现与连接管理
ADV_BROADCAST = 0; // 广播
SCAN_DEVICES = 1; // 扫描设备
DEVICE_AUTHORIZATION = 2; // 设备授权
// 安全配对与密钥交换
PAIR_NET = 10; // 设备配网
KEY_UPDATE = 12; // 动态更新会话密钥
// 数据路由与转发
ROUTE_DISCOVERY = 20; // 路由发现请求
ROUTE_REPLY = 21; // 路由响应
DATA_FORWARD = 22; // 数据包转发
// 属性协议(类似BLE ATT层)
ATT_READ = 30; // 读取节点属性
ATT_WRITE = 31; // 写入属性
ATT_NOTIFY = 32; // 异步通知
ATT_INDICATE = 33; // 指示
// 网络维护与QoS
HEARTBEAT = 50; // 心跳包
TOPOLOGY_UPDATE = 51; // 拓扑更新广播
PRIORITY_DATA = 52; // 高优先级数据
TIME_SYNC = 53; // 设备校时
// 观察者相关指令
OBSERVER_REGISTER = 70; // 注册OBS设备
OBSERVER_REPORT = 71; // OBS信息上报
DATA_SUBSCRIBE = 72; // 订阅特定数据流
DIAGNOSTICS_TRIGGER = 73; // 触发诊断
}
// Shrimp Mesh Network Device
// 设备类型:路由、节点、观察者等等
enum SMNDevice {
SMN_EMPTY = 0; // 空
ROUTER = 1; // 路由节点
LEAF = 2; // 叶节点
LP_LEAF = 4; // 低功耗叶节点
FRIEND = 8; // 友邻节点,负责存储低功耗设备的信息
PROXY = 16; // 代理节点,BLE或者蓝牙访问
OBSERVER = 32; // 观察者
GATEWAY = 64; // 网关
COORDINATOR = 128; // 调整者(用于升级设备或者抓取设备debug信息)
}
// Network Device Bandwidth Type
// 设备带宽类型:独占、重度、轻量等等
enum Bandwidth {
DEDICATED = 0; // 独占型设备,如烧录器,串口转发,网络摄像头等等
HEAVY = 1; // 重度带宽占用
MODERATE = 2; // 中等带宽占用
LIGHTWEIGHT = 8; // 轻量级,如常见物联设备,灯,开关等
MINIMAL = 10; // 极低占用,如温湿度传感器、节能门铃等等
}
// Device Model Type
// 设备模型类型:
enum DeviceModelType {
TYPE_EMPTY = 0; // 空
// 常见开关类型
LIGHT = 1; // 灯
COLOR_LIGHT = 2; // 彩色灯
SWITCH = 3; // 开关
// 传感器类(30-49)
TEMPERATURE_SENSOR = 30; // 温度传感器
HUMIDITY_SENSOR = 31; // 湿度传感器
SMOKE_SENSOR = 32; // 烟雾传感器
MOTION_SENSOR = 33; // 人体传感器
WATER_LEAK_SENSOR = 34; // 水浸传感器
WATER_LEVEL_SENSOR = 35; // 水位传感器
// 屏幕类型设备
OLED_SCREEN = 50; // 单色OLED屏
COLOR_LCD = 51; // 彩色屏幕
// 时钟类设备
CLOCK = 60; // 时钟设备
// 风扇类型设备
FAN_SIMPLE = 70; // 简单风扇类型设备
FAN_PWM = 71; // PWM风扇类型设备
// 加热类型
PCB_HOT_PLATE = 90; // PCB加热台
// 开发板类型
LED = 100; // 板载LED
COLORLED = 101; // 板载彩色LED
GPIO_CONTROLLER = 102; // GPIO控制器
}
// Shrimp Mesh Heatbeat Data
// 心跳数据包结构体
message Heatbeat {
uint32 device_id = 1; // 节点ID
uint32 group_id = 2; // 组ID
uint32 level = 3; // 节点层级
SMNDevice device_type = 10; // 设备类型
bytes node_mac = 11; // 节点设备mac地址
}
// TimeInfo Data
// 时间数据包结构体
message TimeInfo {
uint32 device_id = 1; // 节点ID
uint32 time = 2; // Unix时间戳
uint32 msec = 3; // 毫秒数据
uint32 sntp_time = 4; // SNTP校时时间
}
安装protoc编译器,使用protobuf的话,需要安装这个编译器,用来生成对应语言的序列化代码。
我们在github上的链接上,下载对应平台的压缩包。
https://github.com/protocolbuffers/protobuf/releases

下载解压缩后,需要添加对应的环境变量
# protocexport
PROTOC_PATH=$MYBIN/tools/bin/protocexport
PATH=$PROTOC_PATH/bin:$PATH
然后,需要安装protobuf的go插件(生成常规的序列化数据结构)
go get -u github.com/golang/protobuf/protoc-gen-go
以及gorm的插件(生成gorm对应的数据库)
go get github.com/infobloxopen/protoc-gen-gorm
然后,我们需要编写一个脚本,来完成对应文件的生成工作。
protoc \
-I. \
-I$PROTOC_PATH/include/ \
-I$(go env GOPATH)/pkg/mod/github.com/infobloxopen/protoc-gen-gorm@v1.1.4/proto/ \
-I$(go env GOPATH)/pkg/mod/github.com/infobloxopen/protoc-gen-gorm@v1.1.4/third_party/proto/ \
--go_out=. \
--gorm_out=. \
protos/SMN_proto.proto protos/SMN_type.proto
对应参数需要反复测试和尝试,找到对应的文件才能使用,网络上的资料也是比较少的,所以,写成脚本测试会更简单一些(也方便记录,万一以后忘记了就麻烦了)。
生成的文件如截图
gorm的存储数据结构
常规的数据结构和序列化、解序列方法
然后使用这些,就可以简单写一些保存espnow设备数据的服务程序了。
更多回帖