00. 目录
文章目录
01. 华为云物联网平台概述
华为云物联网平台设备接入云服务(IoTDA)提供海量设备的接入和管理能力,将物理设备联接到云,支撑设备数据采集上云和云端下发命令给设备进行远程控制,配合华为云其他产品,帮助您快速构筑物联网解决方案。
使用物联网平台构建一个完整的物联网解决方案主要包括3部分:物联网平台、业务应用和设备。
-
物联网平台作为连接业务应用和设备的中间层,屏蔽了各种复杂的设备接口,实现设备的快速接入;同时提供强大的开放能力,支撑行业用户构建各种物联网解决方案。
-
设备可以通过固网、2G/3G/4G/5G、NB-IoT、Wifi等多种网络接入物联网平台,并使用LWM2M/CoAP、MQTT、HTTPS等主流协议或行业协议将业务数据上报到平台,平台也可以将控制命令下发给设备。
-
业务应用通过调用物联网平台提供的API,实现设备数据采集、命令下发、设备管理等业务场景。
02. 设备接入
2.1 登入成功后,点击右上角的“控制台”
2.2 选择华南-广州,然后点击左侧“服务列表”。
2.3 在“服务列表”的搜索框中搜索“IoTDA”,然后点击“设备接入 IoTDA”。
2.4 IoTDA实例中选择详情
2.5 将“MQTT 协议设备接入 IP地址和端口号”记录下来。
03. 创建产品
3.1 点击左侧的产品,点击创建产品按钮。
3.2 单击左侧导航栏“产品”,单击页面左侧的**“创建产品”**。根据页面提示填写参数,然后单击“确定”,完成产品的创建。
3.3 创建产品成功,点击确定
04. 注册设备
4.1 访问设备接入服务,单击“管理控制台”进入设备接入控制台。选择您的实例,单击实例卡片进入。
4.2 在左侧导航栏选择“设备 > 所有设备”,单击“注册设备”,按照如下表格填写参数后,单击“确定”。
4.3 单设备注册,填写标识码,密钥自动生成即可
4.4 设备注册成功后,请妥善保管好设备ID和密钥,用于设备接入平台认证。
若密钥丢失,可以按照设备认证凭证管理中的步骤更新设备密钥,无法找回注册设备时生成的密钥。
用户可在设备列表删除不再使用的设备。删除设备不支持撤回,请谨慎操作。
05. 创建产品模型
5.1 点击左侧“产品”,点击新创建好的产品名称“SmartLED”。
5.2 点击自定义模型
5.3 填写服务 ID 和服务类型
- “服务ID”:采用首字母大写的命名方式。比如:WaterMeter、StreetLight。
- “服务类型”:建议和服务ID保持一致。
- “服务描述”:比如路灯上报的环境光强度和路灯开关状态的属性。
5.4 在此服务下“添加属性”,用于上传开发板上的数据,以 LED 和温度为例,其他传感器也是类似的方法。
5.5 在展开的页面单击“新增属性”,在弹出窗口中配置属性的各项参数,然后单击“确定”。
5.6 展示创建完成的属性列表
5.7 在此服务下“添加命令”,用于控制开发板上可以控制的设备,以 LED为例,其他传感器也是类似的方法。
单击“添加命令”,在弹出窗口中配置命令。
- “命令名称”:建议采用全大写形式,单词间用下划线连接的命名方式,如DISCOVERY,CHANGE_STATUS。
- “下发参数”:单击“新增输入参数”,在弹出窗口中配置下发命令字段的各项参数,然后“确定”。
5.8 展示创建完成的命令列表。
06. 查看接入信息
6.1 将生成好的信息(产品 ID、clientId、username、password)用于修改工程 template.c 文件前部 MQTT 相关配置。
clientId、username、password 位置如下
MQTT连接参数
程序替换位置如下:
6.2 修改华为云平台的 MQTT 服务器的 IP 地址,打开 Windows 下 cmd 控制端,
在控制端输入:ping+网址,注意 ping 的网址是注册的产品的接入信息。
07. 数据格式
属性上报JSON格式
{
"services": [
{
"service_id": "LEDAttribute",
"properties": {
"led": "OFF",
"temperature": 28
}
}
]
}
命令下发JSON格式
{
"paras" : {
"state" : "OFF"
},
"service_id" : "LEDAttribute",
"command_name" : "ledSwitch"
}
命令下发响应JSON格式
{
"result_code": 0,
}
08. 硬件设计
由于 Hi3861 内置 WIFI 功能,所以直接在开发板上使用即可,无需额外连接。
09. 软件设计
template.c
/**
****************************************************************************************************
****************************************************************************************************
* 实验现象:开发板连接路由器热点,在浏览器输入www.huaweicloud.com,注册账号,添加产品和设备等属性,
* 打开串口调试助手,观察输出结果,连接好后,设备变为在线状态,可显示温度值,也可通过云端控制
* 板载LED亮灭。
*
****************************************************************************************************
*/
#include <stdio.h>
#include <unistd.h>
#include "ohos_init.h"
#include "cmsis_os2.h"
#include "bsp_led.h"
// #include "bsp_ds18b20.h"
#include "bsp_wifi.h"
#include "bsp_mqtt.h"
#include "lwip/netifapi.h"
#include "lwip/sockets.h"
#include "lwip/api_shell.h"
#include "cJSON.h"
//WIFI连接热点和密码
#define WIFI_SSID "IOT"
#define WIFI_PAWD "iot12345678"
// #define WIFI_SSID "uplooking"
// #define WIFI_PAWD "1234567890"
//注册华为云后需要更新的信息
// 设备ID
#define DEVICE_ID "67cee8ab80c3872914b13a1a_Hi3861Led"
// MQTT客户端ID
#define MQTT_CLIENT_ID "67cee8ab80c3872914b13a1a_Hi3861Led_0_0_2025031101"
// MQTT用户名
#define MQTT_USER_NAME "67cee8ab80c3872914b13a1a_Hi3861Led"
// MQTT密码
#define MQTT_PASS_WORD "bd9581c03bdc22c44d66b43cd58688ecc60965e7dd5d241eddf44cae53f5208c"
// 华为云平台的IP地址
#define SERVER_IP_ADDR "116.205.178.237"
// 华为云平台的IP端口号
#define SERVER_IP_PORT 1883
// 订阅 接收控制命令的主题
#define MQTT_TOPIC_SUB_COMMANDS "$oc/devices/%s/sys/commands/#"
// 发布 成功接收到控制命令后的主题
#define MQTT_TOPIC_PUB_COMMANDS_REQ "$oc/devices/%s/sys/commands/response/request_id=%s"
#define MALLOC_MQTT_TOPIC_PUB_COMMANDS_REQ "$oc/devices//sys/commands/response/request_id="
#define MALLOC_MQTT_TOPIC_SUB_COMMANDS_REQUEST "$oc/devices//sys/commands/request_id="
// 发布 设备属性数据的主题
#define MQTT_TOPIC_PUB_PROPERTIES "$oc/devices/%s/sys/properties/report"
#define MALLOC_MQTT_TOPIC_PUB_PROPERTIES "$oc/devices//sys/properties/report"
#define TASK_STACK_SIZE (1024 * 10)
typedef struct message_sensorData {
uint8_t led; // LED灯当前的状态
float temperature; // 当前的温度值
} msg_sensorData_t;
msg_sensorData_t sensorData = {0}; // 传感器的数据
osThreadId_t mqtt_send_task_id; // mqtt 发布数据任务ID
osThreadId_t mqtt_recv_task_id; // mqtt 接收数据任务ID
#define MQTT_SEND_TASK_TIME 3 // s
#define MQTT_RECV_TASK_TIME 1 // s
#define TASK_INIT_TIME 2 // s
#define DISPLAY_BUFF_MAX 64
#define MQTT_DATA_MAX 256
uint8_t publish_topic[MQTT_DATA_MAX] = {0};
uint8_t subscribe_topic[MQTT_DATA_MAX] = {0};
uint8_t mqtt_data[MQTT_DATA_MAX] = {0};
int get_jsonData_value(const cJSON *const object, uint8_t *value)
{
cJSON *json_value = NULL;
int ret = -1;
json_value = cJSON_GetObjectItem(object, "state");
if (json_value)
{
if (strcmp(json_value->valuestring, "ON") == 0)
{
*value = 1;
json_value = NULL;
ret = 0;
}
else if (strcmp(json_value->valuestring, "OFF") == 0)
{
*value = 0;
json_value = NULL;
ret = 0;
}
}
json_value = NULL;
return ret;
}
//解析JSON数据 命令下发数据
int Parse_json_data(const char *payload)
{
int ret = 1;
cJSON *root = NULL;
cJSON *command_name = NULL;
cJSON *paras = NULL;
if (NULL == payload)
{
return 1;
}
root = cJSON_Parse(payload);
if (NULL != root)
{
//解析command_name
command_name = cJSON_GetObjectItem(root, "command_name");
paras = cJSON_GetObjectItem(root, "paras");
if (NULL != command_name)
{
if (strcmp(command_name->valuestring, "ledSwitch") == 0)
{
ret = get_jsonData_value(paras, &sensorData.led);
}
}
}
cJSON_Delete(root);
root = NULL;
command_name = NULL;
paras = NULL;
(sensorData.led == 1) ? LED(1) : LED(0);
return ret;
}
// 向云端发送命令响应
void send_cloud_request_code(const char *request_id, int ret_code, int request_len)
{
char *request_topic = NULL;
int size = 0;
size = strlen(MALLOC_MQTT_TOPIC_PUB_COMMANDS_REQ) + strlen(DEVICE_ID) + request_len + 1;
request_topic = malloc(size);
if (NULL != request_topic)
{
memset(request_topic, 0, size);
sprintf(request_topic, MQTT_TOPIC_PUB_COMMANDS_REQ, DEVICE_ID, request_id);
if (0 == ret_code)
{
MQTTClient_pub(request_topic, "{\"result_code\": 0}", strlen("{\"result_code\": 0}"));
}
else
{
MQTTClient_pub(request_topic, "{\"result_code\": 1}", strlen("{\"result_code\": 1}"));
}
free(request_topic);
request_topic = NULL;
}
else
{
printf("send_cloud_request_code failed....\n");
}
}
/**
* @brief MQTT接收数据的回调函数
*/
int8_t mqttClient_sub_callback(unsigned char *topic, unsigned char *payload)
{
int ret = 0;
char request_id[50] = {0};
if (NULL == topic || NULL == payload)
{
return -1;
}
else
{
printf("topic: %s\n", topic);
printf("payload: %s\n", payload);
//提取出topic中的request_id
//$oc/devices/67cee8ab80c3872914b13a1a_Hi3861Led/sys/commands/request_id=3e635a06-dc56-4152-8dc7-18f995e84516
memset(request_id, 0, sizeof(request_id));
strcpy(request_id, topic + strlen(DEVICE_ID) + strlen(MALLOC_MQTT_TOPIC_SUB_COMMANDS_REQUEST));
printf("request_id = %s\n", request_id);
//解析JSON数据
ret = 0;
ret = Parse_json_data(payload);
//平台下发命令后,需要设备及时将命令的执行结果返回给平台,如果设备没回响应,平台会认为命令执行超时。
send_cloud_request_code(request_id, ret, strlen(request_id));
}
return 0;
}
/**
* @brief 组JSON数据
*/
int Packaged_json_data(void)
{
cJSON *root = NULL;
cJSON *arrayElem0 = NULL;
cJSON *services = NULL;
cJSON *properties = NULL;
char *str = NULL;
int ret = 0;
properties = cJSON_CreateObject();
cJSON_AddStringToObject(properties, "led", sensorData.led ? "ON" : "OFF");
cJSON_AddNumberToObject(properties, "temperature", (int)sensorData.temperature);
arrayElem0 = cJSON_CreateObject();
cJSON_AddStringToObject(arrayElem0, "service_id", "LEDAttribute");
cJSON_AddItemToObject(arrayElem0, "properties", properties);
services = cJSON_CreateArray();
cJSON_AddItemToArray(services, arrayElem0);
//创建JSON对象
root = cJSON_CreateObject();
cJSON_AddItemToObject(root, "services", services);
//格式化打印创建的JSON对象
str = cJSON_PrintUnformatted(root);
if (NULL != str)
{
//调试使用
printf("%s\n", str);
memset(mqtt_data, 0, MQTT_DATA_MAX);
strcpy((char*)mqtt_data, str);
cJSON_free(str);
ret = 0;
}
else
{
ret = -1;
}
if (NULL != root)
{
cJSON_free(root);
}
str = NULL;
root = NULL;
arrayElem0 = NULL;
services = NULL;
properties = NULL;
return ret;
}
/**
* @brief MQTT 发布消息任务 属性上报
*/
void mqtt_send_task(void)
{
while(1)
{
//传感器数据
sensorData.temperature = 28;
//属性上报 $oc/devices/{device_id}/sys/properties/report
memset(publish_topic, 0, MQTT_DATA_MAX);
if (sprintf(publish_topic, MQTT_TOPIC_PUB_PROPERTIES, DEVICE_ID) > 0)
{
//JSON数据
Packaged_json_data();
//发布消息
MQTTClient_pub(publish_topic, mqtt_data, strlen((char*)mqtt_data));
}
sleep(MQTT_SEND_TASK_TIME);
}
}
/**
* @brief MQTT 订阅主题 命令控制下发
*/
void mqtt_recv_task(void)
{
while(1)
{
MQTTClient_sub();
sleep(MQTT_RECV_TASK_TIME);
}
}
//初始化
void hardware_init(void)
{
//LED初始化
led_init();
//设置订阅消息回调
p_MQTTClient_sub_callback = mqttClient_sub_callback;
//连接WIFI
if (WiFi_connectHotspots(WIFI_SSID, WIFI_PAWD) != 0)
{
printf("WiFi_connectHotspots failed....\n");
return;
}
sleep(TASK_INIT_TIME);
//连接到MQTT服务器
if (MQTTClient_connectServer(SERVER_IP_ADDR, SERVER_IP_PORT) != WIFI_SUCCESS)
{
printf("MQTTClient_connectServer failed...\n");
return;
}
sleep(TASK_INIT_TIME);
//初始化MQTT客户端
if (MQTTClient_init(MQTT_CLIENT_ID, MQTT_USER_NAME, MQTT_PASS_WORD) != WIFI_SUCCESS)
{
printf("MQTTClient_init failed....\n");
return;
}
sleep(TASK_INIT_TIME);
//订阅主题
//$oc/devices/{device_id}/sys/commands/#
sprintf(subscribe_topic, MQTT_TOPIC_SUB_COMMANDS, DEVICE_ID);
printf("subtopic: %s\n", subscribe_topic);
if (MQTTClient_subscribe(subscribe_topic) != WIFI_SUCCESS)
{
printf("MQTTClient_subscribe failed..\n");
return;
}
sleep(TASK_INIT_TIME);
}
//任务创建
void wifi_iotda_task_create(void)
{
osThreadAttr_t taskOptions;
taskOptions.name = "mqtt_send_task"; // 任务的名字
taskOptions.attr_bits = 0; // 属性位
taskOptions.cb_mem = NULL; // 堆空间地址
taskOptions.cb_size = 0; // 堆空间大小
taskOptions.stack_mem = NULL; // 栈空间地址
taskOptions.stack_size = TASK_STACK_SIZE; // 栈空间大小 单位:字节
taskOptions.priority = osPriorityNormal; // 任务的优先级
mqtt_send_task_id = osThreadNew((osThreadFunc_t)mqtt_send_task, NULL, &taskOptions); // 创建任务
if (mqtt_send_task_id != NULL)
{
printf("ID = %d, mqtt_send_task_id Create OK!\n", mqtt_send_task_id);
}
taskOptions.name = "mqtt_recv_task";
taskOptions.stack_size = TASK_STACK_SIZE;
mqtt_recv_task_id = osThreadNew((osThreadFunc_t)mqtt_recv_task, NULL, &taskOptions);
if (mqtt_recv_task_id != NULL) {
printf("ID = %d, Create mqtt_recv_task_id is OK!\r\n", mqtt_recv_task_id);
}
}
/**
* @description: 初始化并创建任务
* @param {*}
* @return {*}
*/
static void template_demo(void)
{
printf("-Hi3861开发板--WIFI 华为IoTDA设备接入实验\r\n");
hardware_init();//硬件初始化
wifi_iotda_task_create();//任务创建
}
SYS_RUN(template_demo);
10. 实验现象
在华为云 IoTDA 物联网平台中可以看到开发板上传的温度数据,并且可以控制开发板中的 LED 灯。