ESP32学习笔记(基于IDF):OneNET物模型数据交互(控制小灯)
目录
- 前言
- 一、工程配置、代码编写
- 二、比较重要的地方
- 三、测试与实验
前言
ESP32学习笔记(基于IDF):ESP32接入OneNET
本文基于上篇笔记的延续,上篇笔记成功把ESP32连接上了OneNET,本篇文章尝试在OneNET平台上给ESP32下发指令,然后控制ESP32的小灯,然后实际上我的ESP32没有接小灯硬件,所以对下发的指令做打印处理,以演示效果,重点是把ESP32根OneNET的通路打通。
一、工程配置、代码编写
1、 在iot目录下新增onenet_dm.c和onenet_dm.h这两个文件

2、onenet_dm.c文件下,写入如下代码
#include "onenet_dm.h"
#include "esp_log.h"
#include <string.h>static int led_brightness = 0; //LED亮度
static int led_status = 0; //LED开关状态static int ws2812_red = 0; //RGB红色通道
static int ws2812_green = 0; //RGB绿色通道
static int ws2812_blue = 0; //RGB蓝色通道void onenet_dm_init(void)
{}
void onenet_property_handle(cJSON* property)
{// {// "id": "123",// "version": "1.0",// "params": {// "Brightness":"50"// "LightSwitch":true// "RGBColor":{// "Red":100,// "Green":100,// "Blue":100,// }// }// }// 先判断传入的property是否为空,避免空指针访问if (property == NULL) {ESP_LOGE("onenet", "property is NULL, skip handle");return;}//从CJSON中解析”param“字段cJSON* param_js = cJSON_GetObjectItem(property,"params");if (param_js == NULL) {ESP_LOGE("onenet", "params not found in property");return;}if(param_js){cJSON* name_js = param_js->child;while(name_js){if(strcmp(name_js->string,"Brightness") == 0){led_brightness = cJSON_GetNumberValue(name_js);ESP_LOGI("onenet", "Set Brightness to %d", led_brightness);}else if(strcmp(name_js->string,"LightSwitch") == 0) //开关数据{if(cJSON_IsTrue(name_js)){led_status = 1;ESP_LOGI("onenet", "Turn LED ON");}else{led_status = 0;ESP_LOGI("onenet", "Turn LED OFF");}}else if(strcmp(name_js->string,"RGBColor") == 0){ws2812_red = cJSON_GetNumberValue(cJSON_GetObjectItem(name_js,"Red"));ws2812_green = cJSON_GetNumberValue(cJSON_GetObjectItem(name_js,"Green"));ws2812_blue = cJSON_GetNumberValue(cJSON_GetObjectItem(name_js,"Blue"));ESP_LOGI("onenet", "Set RGB to (%d, %d, %d)", ws2812_red, ws2812_green, ws2812_blue);}name_js = name_js->next;}}
}cJSON* onenet_property_upload(void)
{// {// "id": "123",// "version": "1.0",// "params": {// "Brightness":// {// "value":50 // }// "LightSwitch":// {// "value":true// }// "RGBColor":{// "value":{ // "Red":100,// "Green":100,// "Blue":100,// }// }// }// }//创建根JSON对象cJSON* root = cJSON_CreateObject();cJSON_AddStringToObject(root,"id","123");cJSON_AddStringToObject(root,"version","1.0");cJSON* param_js = cJSON_AddObjectToObject(root,"params");//亮度cJSON* led_brightness_js = cJSON_AddObjectToObject(param_js,"Brightness");cJSON_AddNumberToObject(led_brightness_js,"value",led_brightness);//开关cJSON* lightness_js = cJSON_AddObjectToObject(param_js,"LightSwitch");cJSON_AddBoolToObject(lightness_js,"value",led_status);//RGB值cJSON* color_js = cJSON_AddObjectToObject(param_js,"RGBColor");cJSON* color_value_js = cJSON_AddObjectToObject(color_js,"value");cJSON_AddNumberToObject(color_value_js,"Red",ws2812_red);cJSON_AddNumberToObject(color_value_js,"Green",ws2812_green);cJSON_AddNumberToObject(color_value_js,"Blue",ws2812_blue);return root;
}
3、onenet_dm.h文件下,写入如下代码
#ifndef _ONENET_DM_H
#define _ONENET_DM_H
#include "cJSON.h"void onenet_dm_init(void);
void onenet_property_handle(cJSON* property);cJSON* onenet_property_upload(void);#endif
4、然后onenet_mqtt.c文件较上一篇笔记有所改动,不过我这里就不单独贴改动部分了,也是直接贴全部源码

#include "onenet_mqtt.h"
#include "onenet_token.h"
#include "mqtt_client.h"
#include "esp_log.h"
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include "cJSON.h"
#include "onenet_dm.h"#define TAG "onenet"static esp_mqtt_client_handle_t mqtt_handle = NULL;static void onenet_property_ack(const char* id,int code,const char* msg);static void onenet_subscribe(void);static void mqtt_event_handler(void *handler_args, esp_event_base_t base, int32_t event_id, void *event_data)
{esp_mqtt_event_handle_t event = event_data;switch ((esp_mqtt_event_id_t)event_id) {case MQTT_EVENT_CONNECTED:ESP_LOGI(TAG, "MQTT_EVENT_CONNECTED");onenet_subscribe();cJSON* property_js = onenet_property_upload();char* data = cJSON_PrintUnformatted(property_js);onenet_post_property_data(data);cJSON_free(data);cJSON_Delete(property_js);break;case MQTT_EVENT_DISCONNECTED:ESP_LOGI(TAG, "MQTT_EVENT_DISCONNECTED");break;case MQTT_EVENT_SUBSCRIBED:ESP_LOGI(TAG, "MQTT_EVENT_SUBSCRIBED, msg_id=%d", event->msg_id);break;case MQTT_EVENT_UNSUBSCRIBED:ESP_LOGI(TAG, "MQTT_EVENT_UNSUBSCRIBED, msg_id=%d", event->msg_id);break;case MQTT_EVENT_PUBLISHED:ESP_LOGI(TAG, "MQTT_EVENT_PUBLISHED, msg_id=%d", event->msg_id);break;case MQTT_EVENT_DATA:ESP_LOGI(TAG, "MQTT_EVENT_DATA");printf("TOPIC=%.*s\r\n", event->topic_len, event->topic);printf("DATA=%.*s\r\n", event->data_len, event->data);if(strstr(event->topic,"property/set")){cJSON* property = cJSON_Parse(event->data);cJSON* id_js = cJSON_GetObjectItem(property,"id");onenet_property_handle(property);onenet_property_ack(cJSON_GetStringValue(id_js),200,"success");cJSON_Delete(property);}break;case MQTT_EVENT_ERROR:ESP_LOGI(TAG, "MQTT_EVENT_ERROR");break;default:ESP_LOGI(TAG, "Other event id:%d", event->event_id);break;}
}esp_err_t onenet_start(void)
{esp_mqtt_client_config_t mqtt_config;memset(&mqtt_config,0,sizeof(esp_mqtt_client_config_t));mqtt_config.broker.address.uri = "mqtt://mqtts.heclouds.com";mqtt_config.broker.address.port = 1883;mqtt_config.credentials.client_id = ONENET_DEVICE_NAME;mqtt_config.credentials.username = ONENET_PRODUCT_ID;static char token[256];dev_token_generate(token,SIG_METHOD_SHA256,2232933411,ONENET_PRODUCT_ID,NULL,ONENET_PRODUCT_ACCESS_KEY);mqtt_config.credentials.authentication.password = token;//将鉴权信息打印出来ESP_LOGI(TAG,"onenet connect->clientId:%s,username:%s,password:%s",mqtt_config.credentials.client_id,mqtt_config.credentials.username,mqtt_config.credentials.authentication.password);mqtt_handle = esp_mqtt_client_init(&mqtt_config);esp_mqtt_client_register_event(mqtt_handle,ESP_EVENT_ANY_ID,mqtt_event_handler,NULL);return esp_mqtt_client_start(mqtt_handle);
}static void onenet_property_ack(const char* id, int code, const char* msg)
{char topic[128];snprintf(topic,128,"$sys/%s/%s/thing/property/set_reply",ONENET_PRODUCT_ID,ONENET_DEVICE_NAME);cJSON* reply_js = cJSON_CreateObject();cJSON_AddStringToObject(reply_js,"id",id);cJSON_AddNumberToObject(reply_js,"code",code);cJSON_AddStringToObject(reply_js,"msg",msg);char* data = cJSON_PrintUnformatted(reply_js);esp_mqtt_client_publish(mqtt_handle,topic,data,strlen(data),1,0);cJSON_free(data);cJSON_Delete(reply_js);
}static void onenet_subscribe(void)
{char topic[128];sniprintf(topic,128,"$sys/%s/%s/thing/property/post/reply",ONENET_PRODUCT_ID,ONENET_DEVICE_NAME);esp_mqtt_client_subscribe_single(mqtt_handle,topic,1);sniprintf(topic,128,"$sys/%s/%s/thing/property/set",ONENET_PRODUCT_ID,ONENET_DEVICE_NAME);esp_mqtt_client_subscribe_single(mqtt_handle,topic,1);
}esp_err_t onenet_post_property_data(const char* data)
{char topic[128];snprintf(topic,128,"$sys/%s/%s/thing/property/post",ONENET_PRODUCT_ID,ONENET_DEVICE_NAME);ESP_LOGI(TAG,"Upload topic:%s,payload:%s",topic,data);return esp_mqtt_client_publish(mqtt_handle,topic,data,strlen(data),1,0);
}
5、在onenet_mqtt.h文件下,写入如下代码

#ifndef _ONENET_MQTT_H_
#define _ONENET_MQTT_H_
#include "esp_err.h"//产品ID
#define ONENET_PRODUCT_ID "E3Sgq5fGjC"
//产品密钥
#define ONENET_PRODUCT_ACCESS_KEY "a82bv2/LWRePdSfT+cmRDw2Jt+8bl3NZJxOxbdN3O6M="
//设备名称
#define ONENET_DEVICE_NAME "esp32led01"esp_err_t onenet_start(void);esp_err_t onenet_post_property_data(const char* data);#endif
6、在onenet.c文件下,写入如下代码

#include <stdio.h>
#include "onenet_mqtt.h"
#include "wifi_manager.h"
#include "nvs_flash.h"
#include "freertos/FreeRTOS.h"
#include "freertos/event_groups.h"
#include "freertos/task.h"
#include "onenet_dm.h"static EventGroupHandle_t wifi_ev = NULL;
#define WIFI_CONNECT_BIT BIT0/*** wifi事件回调函数* @param ev wifi事件* @return 无*/
static void wifi_state_callback(WIFI_STATE state)
{if(state == WIFI_STATE_CONNECTED){xEventGroupSetBits(wifi_ev,WIFI_CONNECT_BIT);}
}void app_main(void)
{// 1. 初始化 NVS 闪存(非易失性存储)// NVS 用于保存 Wi-Fi 密码、设备配置等需要断电后保留的数据nvs_flash_init();// 2. 创建事件组(用于同步 Wi-Fi 连接状态)// 事件组是 FreeRTOS 提供的同步机制,这里用于标记 Wi-Fi 是否连接成功wifi_ev = xEventGroupCreate();// 3. 初始化 Wi-Fi 管理器,并注册状态回调函数// wifi_manager_init:初始化 Wi-Fi 相关资源(如配置 Wi-Fi 模式、注册事件处理等)// wifi_state_callback:自定义回调函数,用于接收 Wi-Fi 连接/断开等状态通知onenet_dm_init();wifi_manager_init(wifi_state_callback);// 4. 调用 Wi-Fi 管理器连接指定的 Wi-Fi 热点// 参数:Wi-Fi 热点名称(SSID)为 "Lymow",密码为 "1415926535"wifi_manager_connect("Lymow","1415926535");//连接的WIFI用户名跟密码EventBits_t ev;while(1){//等待WIFI连接成功,然后才开始连接onenetev = xEventGroupWaitBits(wifi_ev,WIFI_CONNECT_BIT,pdTRUE,pdFALSE,pdMS_TO_TICKS(10*1000));if(ev & WIFI_CONNECT_BIT){// Wi-Fi 已就绪,启动 OneNet 平台连接(如 MQTT 连接)onenet_start();}}
}
7、CMakeLists.txt文件,配置如下

二、比较重要的地方
1、onenet_dm.c里面实现三个重要函数:1、初始化物模型数据 2、处理onenet下行数据 3、处理esp32上发数据
#ifndef _ONENET_DM_H
#define _ONENET_DM_H
#include "cJSON.h"void onenet_dm_init(void); //初始化物模型数据
void onenet_property_handle(cJSON* property); //处理onenet给ESP32下发的数据cJSON* onenet_property_upload(void); //处理ESP32给onenet上报的数据#endif

2、onenet下行数据格式是这样的,所以我们需要按这个格式解析,可以对着这个格式看代码,代码里只关注了params这个参数,根据这个参数里对应的值来控制相应的硬件

3、ESP32上报的数据格式长这样,对着这个格式看上报函数的代码

4、这部分代码的作用是订阅OneNET平台的主题,订阅的两个主题分别是属性上报回复、属性设置指令
static void onenet_subscribe(void)
{char topic[128];sniprintf(topic,128,"$sys/%s/%s/thing/property/post/reply",ONENET_PRODUCT_ID,ONENET_DEVICE_NAME);esp_mqtt_client_subscribe_single(mqtt_handle,topic,1);sniprintf(topic,128,"$sys/%s/%s/thing/property/set",ONENET_PRODUCT_ID,ONENET_DEVICE_NAME);esp_mqtt_client_subscribe_single(mqtt_handle,topic,1);
}
三、测试与实验
1、在工程目录下输入以下指令,进行编译、下载、调试
idf.py flash monitor

2、下载完成后,可以看到,ESP32先向OneNET上报了一包小灯属性值,然后OneNET接到后则下发一包”成功收到“的消息提示

3、在OneNET平台上则可以看到ESP23上报上来的属性了

4、试试用OneNET平台下发指令去控制下等的明亮度

5、设置后,可以看到ESP32回复接收成功

6、查看ESP32调试台这边打印,则可以看到明亮度变成了29(以打印模拟硬件实现)

