【ESP32日记002】透传通讯协议的解析实现及JSON数据的解析

最近项目上增加了一个联网功能,以实现设备在手机上的远程交互、数据上报、指令下发控制等功能。使用的是华为的鸿蒙透传协议,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循环中进行判断,以回调函数的方式进行数据处理,这样代码效率更高‘可读性会更好。
 

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是一个简单的ESP32数据透传的程序,其中使用了ESP-IDF框架: ```c #include <stdio.h> #include <string.h> #include "freertos/FreeRTOS.h" #include "freertos/task.h" #include "freertos/semphr.h" #include "esp_system.h" #include "esp_wifi.h" #include "esp_event_loop.h" #include "esp_log.h" #include "esp_err.h" #include "esp_http_client.h" #define WIFI_SSID "your_wifi_ssid" #define WIFI_PASS "your_wifi_password" #define SERVER_URL "http://your_server_url" // 服务器地址 static const char *TAG = "data_passthrough"; static char data_buf[100]; // 数据缓存区 static SemaphoreHandle_t data_mutex; // 数据互斥锁 /* WiFi事件处理程序 */ static esp_err_t wifi_event_handler(void *ctx, system_event_t *event) { switch(event->event_id) { case SYSTEM_EVENT_STA_START: esp_wifi_connect(); ESP_LOGI(TAG, "WiFi STA started"); break; case SYSTEM_EVENT_STA_CONNECTED: break; case SYSTEM_EVENT_STA_GOT_IP: ESP_LOGI(TAG, "Got IP: %s", ip4addr_ntoa(&event->event_info.got_ip.ip_info.ip)); break; case SYSTEM_EVENT_STA_DISCONNECTED: esp_wifi_connect(); break; default: break; } return ESP_OK; } /* HTTP事件处理程序 */ static esp_err_t http_event_handler(esp_http_client_event_t *evt) { switch(evt->event_id) { case HTTP_EVENT_ERROR: ESP_LOGI(TAG, "HTTP ERROR"); break; case HTTP_EVENT_ON_CONNECTED: ESP_LOGI(TAG, "HTTP CONNECTED"); break; case HTTP_EVENT_ON_FINISH: ESP_LOGI(TAG, "HTTP FINISHED"); break; case HTTP_EVENT_ON_DATA: ESP_LOGI(TAG, "HTTP DATA RECEIVED"); if (evt->data_len < sizeof(data_buf) - 1) { strncpy(data_buf, (char *)evt->data, evt->data_len); data_buf[evt->data_len] = '\0'; xSemaphoreGive(data_mutex); // 释放数据互斥锁 } break; default: break; } return ESP_OK; } /* 数据透传任务 */ static void data_passthrough_task(void *pvParameters) { while (1) { if (xSemaphoreTake(data_mutex, portMAX_DELAY) == pdTRUE) { // 获取数据互斥锁 ESP_LOGI(TAG, "Data received: %s", data_buf); esp_http_client_config_t config = { .url = SERVER_URL, .event_handler = http_event_handler, }; esp_http_client_handle_t client = esp_http_client_init(&config); esp_err_t err = esp_http_client_perform(client); // 发送HTTP请求 if (err == ESP_OK) { ESP_LOGI(TAG, "Data sent to server"); } else { ESP_LOGI(TAG, "Failed to send data to server"); } esp_http_client_cleanup(client); } } } void app_main() { // 初始化WiFi tcpip_adapter_init(); wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); esp_wifi_init(&cfg); esp_wifi_set_mode(WIFI_MODE_STA); esp_wifi_set_config(ESP_IF_WIFI_STA, &wifi_config); esp_wifi_start(); ESP_LOGI(TAG, "WiFi STA configured"); // 注册WiFi事件处理程序 ESP_ERROR_CHECK(esp_event_loop_init(wifi_event_handler, NULL)); // 初始化数据互斥锁 data_mutex = xSemaphoreCreateMutex(); // 创建数据透传任务 xTaskCreate(data_passthrough_task, "data_passthrough_task", 4096, NULL, 5, NULL); } ``` 这个程序首先连接到一个WiFi热点,然后等待从服务器接收数据,一旦接收到数据,就将其发送到指定的服务器地址。可以根据实际需要修改WiFi SSID、密码和服务器地址等参数。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值