Toggle navigation
首页
问答
文章
积分商城
专家
专区
更多专区...
文档中心
返回主站
搜索
提问
会员
中心
登录
注册
onenet
星火1号_spark_星火一号_开发板
新版OneNet的星火1号开发板例程适配(下)
5.00
发布于 2025-05-21 16:28:20 浏览:110
订阅该版
[tocm] 话接上回:https://club.rt-thread.org/ask/article/334988f1ff1605fe.html # 创建应答 既然OneNet需要应答,那我们就回复他。于是我们在mqtt_usr_callback函数中添加以下代码段: ## 获取命令ID ```c // 获取命令ID cJSON *id_json = cJSON_GetObjectItem(root, "id"); if (!id_json || !cJSON_IsString(id_json)) { LOG_E("No valid id field"); cJSON_Delete(root); return; } char *cmd_id = id_json->valuestring; LOG_D("Command ID: %s", cmd_id); ``` 这里从接收到的JSON消息中提取了"id"字段,这个ID要在回复消息中原样返回,用于云平台匹配请求和响应的关系。 ## 构建回复主题 ```c // 构建回复主题 char reply_topic[128] = {0}; char *base_topic = strstr((const char*)msg_data->topicName->lenstring.data, "$sys"); if (base_topic) { int topic_len = strlen(base_topic); strncpy(reply_topic, base_topic, topic_len); // 将 "/set" 替换为 "/set_reply" char *set_pos = strstr(reply_topic, "/set"); if (set_pos) { strcpy(set_pos, "/set_reply"); } } else { // 创建一个默认的回复主题 const char *device_id = onenet_mqtt.onenet_info->device_id; const char *product_id = onenet_mqtt.onenet_info->pro_id; snprintf(reply_topic, sizeof(reply_topic), "$sys/%s/%s/thing/property/set_reply", product_id, device_id); } ``` 这段代码做了两件事: 1. 尝试从原始主题中提取基础部分(以"$sys"开头); 2. 将主题中的"/set"替换为"/set_reply"。 如果无法从原始主题构建回复主题,则使用设备信息构建一个默认的回复主题。这里使用了onenet_mqtt.onenet_info结构体中的设备ID和产品ID。 ## 构建回复消息 回复消息是一个JSON格式的字符串,包含命令ID、状态码和消息: ```c // 构建回复消息 cJSON *resp_root = cJSON_CreateObject(); cJSON_AddStringToObject(resp_root, "id", cmd_id); cJSON_AddNumberToObject(resp_root, "code", 200); cJSON_AddStringToObject(resp_root, "msg", "success"); char *response_str = cJSON_PrintUnformatted(resp_root); ``` 随后将 JSON 对象转换为紧凑的字符串格式。 ## 发送应答 ```c // 发送回复 LOG_D("Sending reply to topic: %s", reply_topic); LOG_D("Reply content: %s", response_str); onenet_mqtt_publish(reply_topic, (uint8_t *)response_str, strlen(response_str)); ``` 这里调用了onenet_mqtt_publish函数来发布消息。 ## 释放资源 ```c // 释放资源 cJSON_Delete(resp_root); if (response_str) { cJSON_free(response_str); } ``` ## 查看效果  这时我们回到云平台下达命令,可以看到设备已经成功响应,云平台也能在几秒间连续下发了。 更新后的完整函数如下: ```c static void mqtt_usr_callback(MQTTClient *c, MessageData *msg_data) { LOG_D("Enter mqtt_usr_callback!"); // 打印接收到的主题和消息 LOG_D("topic %.*s receive a message", msg_data->topicName->lenstring.len, msg_data->topicName->lenstring.data); LOG_D("message: %.*s", msg_data->message->payloadlen, (char*)msg_data->message->payload); // 检查是否是属性设置命令 if (strstr((const char*)msg_data->topicName->lenstring.data, "/thing/property/set")) { // 解析JSON cJSON *root = cJSON_Parse((const char *)msg_data->message->payload); if (!root) { LOG_E("Invalid JSON"); return; } // 获取命令ID,这是必需的用于回复 cJSON *id_json = cJSON_GetObjectItem(root, "id"); if (!id_json || !cJSON_IsString(id_json)) { LOG_E("No valid id field"); cJSON_Delete(root); return; } char *cmd_id = id_json->valuestring; LOG_D("Command ID: %s", cmd_id); // 获取params中的LED命令 cJSON *data = cJSON_GetObjectItem(root, "params"); if (!data) { LOG_E("No params field"); cJSON_Delete(root); return; } cJSON *led = cJSON_GetObjectItem(data, "LED"); if (!led || !cJSON_IsString(led)) { LOG_E("No valid LED field in params"); cJSON_Delete(root); return; } // 获取LED命令字符串 - 例如"00 01" char *led_value = led->valuestring; LOG_D("LED command: %s", led_value); uint8_t recv_data[2] = {0}; // 手动解析字符串 if (strlen(led_value) >= 4 && led_value[2] == ' ') { // 解析第一个字节 (例如 "00") char byte1[3] = {led_value[0], led_value[1], '\0'}; // 解析第二个字节 (例如 "01") char byte2[3] = {led_value[3], led_value[4], '\0'}; recv_data[0] = strtol(byte1, NULL, 16); recv_data[1] = strtol(byte2, NULL, 16); LOG_D("Parsed LED command: %02x %02x", recv_data[0], recv_data[1]); // 准备响应数据的指针 uint8_t *resp_data = NULL; size_t resp_size = 0; // 调用现有回调函数处理命令 if (onenet_mqtt.cmd_rsp_cb) { onenet_mqtt.cmd_rsp_cb(recv_data, 2, &resp_data, &resp_size); } // 释放资源 if (resp_data) { ONENET_FREE(resp_data); } // 构建回复主题 char reply_topic[128] = {0}; char *base_topic = strstr((const char*)msg_data->topicName->lenstring.data, "$sys"); if (base_topic) { int topic_len = strlen(base_topic); strncpy(reply_topic, base_topic, topic_len); // 将 "/set" 替换为 "/set_reply" char *set_pos = strstr(reply_topic, "/set"); if (set_pos) { strcpy(set_pos, "/set_reply"); } } else { // 创建一个默认的回复主题 const char *device_id = onenet_mqtt.onenet_info->device_id; const char *product_id = onenet_mqtt.onenet_info->pro_id; snprintf(reply_topic, sizeof(reply_topic), "$sys/%s/%s/thing/property/set_reply", product_id, device_id); } // 构建回复消息 cJSON *resp_root = cJSON_CreateObject(); cJSON_AddStringToObject(resp_root, "id", cmd_id); cJSON_AddNumberToObject(resp_root, "code", 200); cJSON_AddStringToObject(resp_root, "msg", "success"); char *response_str = cJSON_PrintUnformatted(resp_root); // 发送回复 LOG_D("Sending reply to topic: %s", reply_topic); LOG_D("Reply content: %s", response_str); onenet_mqtt_publish(reply_topic, (uint8_t *)response_str, strlen(response_str)); // 释放资源 cJSON_Delete(resp_root); if (response_str) { cJSON_free(response_str); } } else { LOG_E("Invalid LED command format"); } cJSON_Delete(root); } } ``` 各位复制后直接替换即可。 # 发送实时数据 接下来,试着将环境的实时温度、湿度、光照强度数据发送到云平台。 ## 物模型修改  来到云平台的“产品开发”页面,将物模型都更新为如图属性。 ## 例程整合 RT-Thread Studio中为我们提供了丰富的例程,因此我们只需要对其进行裁剪粘贴便可实现大部分的常用功能,具体可参考《星火1号用户手册》。  此次用到的例程分别为:“03_driver_temp_humi”和“03_driver_als_ps”。 例程创建完成后,我们首先打开项目工程的RT-Thread Settings。RT-Thread在此贴心地为我们提供了图形化配置界面。  我们甚至可以将其中一个拖动到旁边来进行分屏,以此直观地对比两个工程间的配置差异。 ### 配置Settings 1. 如图依次操作,分别添加“aht10”与“ap3216c”软件包  2. 下滑到Drivers界面,点击“传感器”图标进行使能,旁边出现的“配置项”可进行详细选项配置,这一步暂时不用  3. 使能“软件模拟I2C”,再进入配置项  4. 进入界面后如图依次点击使能  最后,千万别忘了Ctrl+S将我们的配置更新到项目工程!  点开“rtconfig.h”,再次手动输入token。 ### 项目裁剪 - 分别打开温湿度与光照强度的例程代码,简单理清逻辑后将所需代码复制过来。  切记:不要忘记引用这两个外设的头文件! - 创建变量与用于发送的字符串以及初始化总线和传感器 我们需要对代码进行修改来适配,具体代码如下: ```c float humidity, temperature, brightness; char temp_str[16]; char humi_str[16]; char bright_str[16]; aht10_device_t dev1; ap3216c_device_t dev2; /* 总线名称 */ const char *i2c_bus_name1 = "i2c2"; /* 初始化 ap3216c */ dev2 = ap3216c_init(i2c_bus_name1); if (dev2 == RT_NULL) { LOG_E("ap3216c initializes failure."); } const char *i2c_bus_name2 = "i2c3"; /* 等待传感器正常工作 */ rt_thread_mdelay(2000); /* 初始化 aht10 */ dev1 = aht10_init(i2c_bus_name2); if (dev1 == RT_NULL) { LOG_E("aht10 initializes failure"); } ``` - 数据获取解析并装载到字符串 修改后适配的代码如下: ```c temperature = aht10_read_temperature(dev1); humidity = aht10_read_humidity(dev1); brightness = ap3216c_read_ambient_light(dev2); // 四舍五入到两位小数 temperature = (float)(((int)(temperature * 100 + 0.5)) / 100.0); humidity = (float)(((int)(humidity * 100 + 0.5)) / 100.0); brightness = (float)(((int)(brightness * 100 + 0.5)) / 100.0); // 手动分离整数和小数部分 int temp_int = (int)temperature; int temp_dec = (int)((temperature - temp_int) * 100); int humi_int = (int)humidity; int humi_dec = (int)((humidity - humi_int) * 100); int bright_int = (int)brightness; int bright_dec = (int)((brightness - bright_int) * 100); // 构造字符串 rt_snprintf(temp_str, sizeof(temp_str), "%d.%02d", temp_int, temp_dec); rt_snprintf(humi_str, sizeof(humi_str), "%d.%02d", humi_int, humi_dec); rt_snprintf(bright_str, sizeof(bright_str), "%d.%02d", bright_int, bright_dec); ``` - 上传数据到云平台 ```c // 使用字符串上传 if (onenet_mqtt_upload_string("temperature", temp_str) < 0) { LOG_E("temperature upload has an error, stop uploading"); break; } else { LOG_D("buffer : {\"temperature\":\"%s\"}", temp_str); } rt_thread_mdelay(500); if (onenet_mqtt_upload_string("humidity", humi_str) < 0) { LOG_E("humidity upload has an error, stop uploading"); break; } else { LOG_D("buffer : {\"humidity\":\"%s\"}", humi_str); } rt_thread_mdelay(500); if (onenet_mqtt_upload_string("brightness", bright_str) < 0) { LOG_E("brightness upload has an error, stop uploading"); // 处理上传失败逻辑 } else { LOG_D("buffer : {\"brightness\":\"%s\"}", bright_str); } ``` 修改后的完整函数如下: ```c static void onenet_upload_entry(void *parameter) { float humidity, temperature, brightness; char temp_str[16]; char humi_str[16]; char bright_str[16]; aht10_device_t dev1; ap3216c_device_t dev2; /* 总线名称 */ const char *i2c_bus_name1 = "i2c2"; /* 初始化 ap3216c */ dev2 = ap3216c_init(i2c_bus_name1); if (dev2 == RT_NULL) { LOG_E("ap3216c initializes failure."); } const char *i2c_bus_name2 = "i2c3"; /* 等待传感器正常工作 */ rt_thread_mdelay(2000); /* 初始化 aht10 */ dev1 = aht10_init(i2c_bus_name2); if (dev1 == RT_NULL) { LOG_E("aht10 initializes failure"); } while (1) { temperature = aht10_read_temperature(dev1); humidity = aht10_read_humidity(dev1); brightness = ap3216c_read_ambient_light(dev2); // 四舍五入到两位小数 temperature = (float)(((int)(temperature * 100 + 0.5)) / 100.0); humidity = (float)(((int)(humidity * 100 + 0.5)) / 100.0); brightness = (float)(((int)(brightness * 100 + 0.5)) / 100.0); // 手动分离整数和小数部分 int temp_int = (int)temperature; int temp_dec = (int)((temperature - temp_int) * 100); int humi_int = (int)humidity; int humi_dec = (int)((humidity - humi_int) * 100); int bright_int = (int)brightness; int bright_dec = (int)((brightness - bright_int) * 100); // 构造字符串 rt_snprintf(temp_str, sizeof(temp_str), "%d.%02d", temp_int, temp_dec); rt_snprintf(humi_str, sizeof(humi_str), "%d.%02d", humi_int, humi_dec); rt_snprintf(bright_str, sizeof(bright_str), "%d.%02d", bright_int, bright_dec); // 使用字符串上传 if (onenet_mqtt_upload_string("temperature", temp_str) < 0) { LOG_E("temperature upload has an error, stop uploading"); break; } else { LOG_D("buffer : {\"temperature\":\"%s\"}", temp_str); } rt_thread_mdelay(500); if (onenet_mqtt_upload_string("humidity", humi_str) < 0) { LOG_E("humidity upload has an error, stop uploading"); break; } else { LOG_D("buffer : {\"humidity\":\"%s\"}", humi_str); } rt_thread_mdelay(500); if (onenet_mqtt_upload_string("brightness", bright_str) < 0) { LOG_E("brightness upload has an error, stop uploading"); // 处理上传失败逻辑 } else { LOG_D("buffer : {\"brightness\":\"%s\"}", bright_str); } rt_thread_delay(rt_tick_from_millisecond(5 * 1000)); } } ``` 这里的操作较为基础,暂且略去讲解。但是! 再次提醒:不要忘记引用两个外设的头文件!这两句不在上面的代码中。  如果编译后出现了如图报错,那就是忘记引用头文件了。 # 预期现象展示  打开串口终端,熟悉的版本和调试信息都已经打印,一切正常。  输入指令,一切都成功执行,星火1号开发板已经开始向OneNet云平台发送实时环境数据。  来到OneNet云平台界面,物模型都成功接收到了实时数据。  回到Studio的终端界面,在msh中输入指令来使能云平台对板载LED的控制。 根据日志可见:开发板在定时发送实时环境数据的同时完成了对云平台所下达命令的指令。  云平台的日志也显示指令下达与应答上传一切正常。 板上的LED也随着指令正常变换。 至此,预定目标全都顺利完成! # 项目工程分享 通过网盘分享的文件:OneNet例程修改版.zip 链接: https://pan.baidu.com/s/1S_PyIzbMw6NJGKPCCTZtKw?pwd=RTT1 提取码: RTT1
2
条评论
默认排序
按发布时间排序
登录
注册新账号
关于作者
Rick98
这家伙很懒,什么也没写!
文章
3
回答
0
被采纳
0
关注TA
发私信
相关文章
1
studio中onenet包使能自动注册设备功能,编译缺少代码
2
rt-thread如何上传GPS的经纬度到onenet?
3
MQTT 在“ read 0:1, break “后断开重连
4
ONENET+esp8266连接报错
5
有哪位实现了ONENET包获取onenet数据源数据吗
6
请问一下使用BC26对接ONENET的步骤是什么呀
7
AT Client receive failed???
8
onenet示例程序运行异常
9
如何使用OneNet软件包上传2个以上数据流
10
OneNET 浮点数据上传,但小数部分却被截掉
推荐文章
1
RT-Thread应用项目汇总
2
玩转RT-Thread系列教程
3
国产MCU移植系列教程汇总,欢迎查看!
4
机器人操作系统 (ROS2) 和 RT-Thread 通信
5
五分钟玩转RT-Thread新社区
6
【技术三千问】之《玩转ART-Pi》,看这篇就够了!干货汇总
7
关于STM32H7开发板上使用SDIO接口驱动SD卡挂载文件系统的问题总结
8
STM32的“GPU”——DMA2D实例详解
9
RT-Thread隐藏的宝藏之completion
10
【ART-PI】RT-Thread 开启RTC 与 Alarm组件
热门标签
RT-Thread Studio
串口
Env
LWIP
SPI
AT
Bootloader
Hardfault
CAN总线
FinSH
ART-Pi
DMA
USB
文件系统
RT-Thread
SCons
RT-Thread Nano
线程
MQTT
STM32
FAL
RTC
rt-smart
I2C_IIC
cubemx
UART
ESP8266
WIZnet_W5500
ota在线升级
BSP
PWM
flash
packages_软件包
freemodbus
潘多拉开发板_Pandora
ADC
GD32
定时器
编译报错
flashDB
keil_MDK
socket
中断
rt_mq_消息队列_msg_queue
Debug
ulog
SFUD
msh
C++_cpp
at_device
本月问答贡献
出出啊
1524
个答案
343
次被采纳
小小李sunny
1444
个答案
290
次被采纳
张世争
821
个答案
179
次被采纳
crystal266
555
个答案
162
次被采纳
whj467467222
1222
个答案
149
次被采纳
本月文章贡献
Pai同学
8
篇文章
10
次点赞
Rick98
3
篇文章
10
次点赞
RTT_逍遥
2
篇文章
10
次点赞
加缪
1
篇文章
2
次点赞
河南理工大学恁带劲儿
1
篇文章
2
次点赞
回到
顶部
发布
问题
投诉
建议
回到
底部