最近项目上增加了一个联网功能,以实现设备在手机上的远程交互、数据上报、指令下发控制等功能。使用的是华为的鸿蒙透传协议,MCU上需要实现对数据的接收、解析和应答等处理。实现的功能代码如下:
1、数据的接收,使用的是串口,这里代码就不再贴出来,调用的接口:
String data = Serial.readStringUntil('\n'); // 读取数据,直到遇到“\n”结束;
2、消息数据拆解:
透传协议的约定:命令字,数据长度,json数据
具体实现代码如下:
2.1、创建一个结构体,用于缓存解析出来的数据:
//WIFI模组TLV消息缓存结构体
typedef struct{
char command[100]; // 命令字不超过99个字符,因为字符串最后要“\0”
int number;
char parameter[1000]; // 参数不超过999个字符,因为字符串最后要“\0”
} TLV_Data;
2.2、实现解析数据函数,并将数据保存到结构体中
//消息解析,用于解析信息数据,拆分为关键字、长度,参数(即消息体)
void parseCommand(char *str, TLV_Data *cmd) {
// 初始化cmd结构体的字段
memset(cmd->command, 0, sizeof(cmd->command));
cmd->number = 0;
memset(cmd->parameter, 0, sizeof(cmd->parameter));
char *token = strtok(str, ",");
if (token != NULL) {
// 使用strncpy来复制命令字,并确保以'\0'结尾
strncpy(cmd->command, token, strlen(token));
token = strtok(NULL, ",");
if (token != NULL) {
cmd->number = atoi(token);
token = strtok(NULL, "\n");
if (token != NULL) {
strncpy(cmd->parameter, token, strlen(token));
}
}
}
// 不需要额外的错误处理,因为已经初始化了cmd结构体的字段
}
2.3、数据拆解后,就可以很方便的对数据有效性进行判断,这里数据有效性判断部分的代码就不贴了。
3、JSON数据的解析
消息体是一个JSON数据结构,因此考虑使用arduinoJSON库进行处理,实现上比较方便,具体代码如下,这里只取其中一段代码段:
if (cmd1.number != 0)
{
/* *****************************模块请求putChar数据,根据模块请求的数据类型回复数据 ******************************* */
char new_str[cmd1.number + 1]; // 新字符数组长度为n+1,为了确保以'\0'结尾
// 使用字符串函数strncpy()复制前n个字符到新的字符数组中
strncpy(new_str, cmd1.parameter, cmd1.number);
// 确保新的字符数组以空字符'\0'结尾
new_str[cmd1.number] = '\0';
// 解析json数据
StaticJsonDocument<256> doc; //声明一个JsonDocument对象
DeserializationError error = deserializeJson(doc, new_str); //反序列化JSON数据
if (!error) //检查反序列化是否成功
{
if (doc.containsKey("Immediate") && doc["Immediate"].containsKey("Immediate"))
{
/************* 下发Immediate设置*****************/ //即时出水开关控制 0-关 1-开 putChar,29,{"Immediate":{"Immediate":1}}
//normal_water.real_lock 即时感应出水功能开关 0:启用 1禁用
int value11 = doc["Immediate"]["Immediate"];
if (value11 == 0)
{
/*0-关*/
normal_water.real_lock = 1; //锁定
}
else
{
/* 1-开 */
normal_water.real_lock = 0; //解锁
}
// Serial.printf("Immediate.Immediate = ");
// Serial.println(value11);
//需要保存的信息,处理写在下方:
nvs_uint8_t[5] = normal_water.real_lock; //nvs_uint8_t[10] tof0, tof1, tof2, tof3距离 + 照明模式(0:关闭 1:auto 2:常亮) + 即时出水(0:启用 1禁用)+ 唤醒距离 + 预留3
preferences.begin("myApp", false);
preferences.putBytes("nvs_uint8_t", nvs_uint8_t, sizeof(nvs_uint8_t));
preferences.end();
}
}
}
以上代码在项目上测试,稳定运行OK。
当协议号较多时,使用if直接进行判断的话,会导致if判断多层嵌套、代码段太长不方便阅读等问题,后续你改为增加一个结构体,在for循环中进行判断,以回调函数的方式进行数据处理,这样代码效率更高‘可读性会更好。