华为云物联网平台(IoT 设备接入云服务)提供海量设备的接入和管理能力,可以将自己的 IoT 设备 联接到华为云,支撑设备数据采集上云和云端下发命令给设备进行远程控制,配合华为云物联网平台的服 务实现设备与设备之间的控制,设备与物联网平台之间的数据信息交互。
在写代码之前先准备好华为云的环境。
注册华为云账号,这里就不介绍了,有手机就行,需要进行实名认证。
在控制台选择北京四,因为其他的可能需要企业才能使用,作为个人开发者就用北京四吧。
创建设备实例。
能看到设备实例的信息
我们要是用的接入方式-mqtt
通过接入地址查询IP。
记住这个IP地址。
创建新产品
注册设备
注意:生成好的设备 ID 和设备秘钥一定到妥善保管,只生成这一份。
创建产品模型
填写服务ID和服务类型
在此服务下“添加属性”,用于上传开发板上的数据,以风扇为例,其他的传感器也是类似的方法。
创建了两个
在此服务下“添加命令”,用于控制开发板上可以控制的设备,以风扇为例,其他的传感器也是类似的方法。
根据设备信息,生成用户名和密码
1.打开由华为云提供的生成用户名和密码的网址,进行生成
Huaweicloud IoTDA Mqtt ClientId Generator
2.填写生成好的设备ID和设备秘钥,点击按钮,生成用户名和密码
注意:下面的图片提供了方法。
这两个就是刚刚注册设备时候要求保存的,应该被保存在一个txt中了。
3.将生成好的信息(ClientD、Usemame、Password),修改文件“D:\DevEcoProjects\test\src\vendor\hqyj\fs_hi3861\demo\cloud_01_HuaWei_IoTDA\cloud_huawei_iotda_example.c”,下面内容直接替换。
ip用刚刚查询的mqtt地址。
数据结构参考设备属性上报_设备接入 IoTDA (huaweicloud.com)
向云端设置属性的数据格式如下所示:
{ "services": [{ "service_id": "attribute", "properties": { "status": "ON", "color": 60 } } ] }
代码编写
修改D:\DevEcoProjects\test\src\vendor\rtplay\rt_hi3861\demo\BUILD.gn文件
# Copyright (c) 2023 Beijing HuaQing YuanJian Education Technology Co., Ltd # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. import("//build/lite/config/component/lite_component.gni") lite_component("demo") { features = [ #"base_00_helloworld:base_helloworld_example", #"base_01_led:base_led_example", #"base_02_loopkey:base_loopkey_example", #"base_03_irqkey:base_irqkey_example", #"base_04_adc:base_adc_example", #"base_05_pwm:base_pwm_example", #"base_06_ssd1306:base_ssd1306_example", #"kernel_01_task:kernel_task_example", #"kernel_02_timer:kernel_timer_example", #"kernel_03_event:kernel_event_example", #"kernel_04_mutex:kernel_mutex_example", #"kernel_05_semaphore_as_mutex:kernel_semaphore_as_mutex_example", #"kernel_06_semaphore_for_sync:kernel_semaphore_for_sync_example", #"kernel_07_semaphore_for_count:kernel_semaphore_for_count_example", #"kernel_08_message_queue:kernel_message_queue_example", #"wifi_09_hotspot:wifi_hotspot_example", #"wifi_10_sta:wifi_sta_example", #"tcp_11_server:tcp_server_example", #"tcp_12_client:tcp_client_example", #"udp_13_server:udp_server_example", #"udp_14_client:udp_client_example", #"network_15_mqtt:network_mqtt_example", #"network_16_sntp:network_sntp_example", #"network_17_httpd:network_httpd_example", "cloud_18_HuaWei_IoTDA:cloud_huawei_iotda_example", ] }
创建D:\DevEcoProjects\test\src\vendor\rtplay\rt_hi3861\demo\cloud_18_HuaWei_IoTDA文件夹
文件夹中创建D:\DevEcoProjects\test\src\vendor\rtplay\rt_hi3861\demo\cloud_18_HuaWei_IoTDA\BUILD.gn文件
# Copyright (c) 2023 Beijing HuaQing YuanJian Education Technology Co., Ltd # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. static_library("cloud_huawei_iotda_example") { sources = [ "cloud_huawei_iotda_example.c", "network_mqtt.c", "wifi_connecter.c", "//third_party/paho.mqtt.embedded-c/MQTTPacket/src/MQTTConnectClient.c", "//third_party/paho.mqtt.embedded-c/MQTTPacket/src/MQTTConnectServer.c", "//third_party/paho.mqtt.embedded-c/MQTTPacket/src/MQTTDeserializePublish.c", "//third_party/paho.mqtt.embedded-c/MQTTPacket/src/MQTTFormat.c", "//third_party/paho.mqtt.embedded-c/MQTTPacket/src/MQTTPacket.c", "//third_party/paho.mqtt.embedded-c/MQTTPacket/src/MQTTSerializePublish.c", "//third_party/paho.mqtt.embedded-c/MQTTPacket/src/MQTTSubscribeClient.c", "//third_party/paho.mqtt.embedded-c/MQTTPacket/src/MQTTSubscribeServer.c", "//third_party/paho.mqtt.embedded-c/MQTTPacket/src/MQTTUnsubscribeServer.c", "//third_party/paho.mqtt.embedded-c/MQTTPacket/src/MQTTUnsubscribeClient.c", ] include_dirs = [ "//utils/native/lite/include", "//kernel/liteos_m/kal/cmsis", "//base/iot_hardware/peripheral/interfaces/kits", "//foundation/communication/wifi_lite/interfaces/wifiservice", "//third_party/paho.mqtt.embedded-c/MQTTPacket/src", "//third_party/cJSON", ] }
文件夹中创建D:\DevEcoProjects\test\src\vendor\rtplay\rt_hi3861\demo\cloud_18_HuaWei_IoTDA\wifi_connecter.h文件,该头文件包含wifi连接的宏。文件同network_16_sntp\wifi_connecter.h
文件夹中创建D:\DevEcoProjects\test\src\vendor\rtplay\rt_hi3861\demo\cloud_18_HuaWei_IoTDA\wifi_connecter.c文件,文件同network_16_sntp\wifi_connecter.c
文件夹中创建D:\DevEcoProjects\test\src\vendor\rtplay\rt_hi3861\demo\cloud_18_HuaWei_IoTDA\network_mqtt.h文件,文件同network_15_mqtt\network_mqtt.h文件,文件主要包含mqtt的函数。
文件夹中创建D:\DevEcoProjects\test\src\vendor\rtplay\rt_hi3861\demo\cloud_18_HuaWei_IoTDA\network_mqtt.c文件,文件同network_15_mqtt\network_mqtt.h文件,文件主要包含mqtt的函数实现。
文件夹中创建D:\DevEcoProjects\test\src\vendor\rtplay\rt_hi3861\demo\cloud_18_HuaWei_IoTDA\cloud_huawei_iotda_example.c文件
/* * Copyright (c) 2023 Beijing HuaQing YuanJian Education Technology Co., Ltd * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include "cJSON.h" #include "cmsis_os2.h" #include "hi_gpio.h" #include "hi_io.h" #include "iot_errno.h" #include "iot_gpio.h" #include "iot_pwm.h" #include "network_mqtt.h" #include "ohos_init.h" #include "wifi_connecter.h" // 设备密码 fs12345678 // 设备ID #define DEVICE_ID "668651baa559ef622666a845_hi3861" // MQTT客户端ID #define MQTT_CLIENT_ID "668651baa559ef622666a845_hi3861_0_0_2024070407" // MQTT用户名 #define MQTT_USER_NAME "668651baa559ef622666a845_hi3861" // MQTT密码 #define MQTT_PASS_WORD "d5b00ad95964f8016c9e6f79c8269265720d770acd7e97bf73cf30a339b04997" // 华为云平台的IP地址 #define SERVER_IP_ADDR "117.78.5.125" // 华为云平台的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 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) #define MsgQueueObjectNumber 16 // 定义消息队列对象的个数 typedef struct message_sensorData { uint8_t status; // LED灯当前的状态 uint8_t color; // 颜色值 } msg_sensorData_t; msg_sensorData_t sensorData = {0}; // 传感器的数据 osThreadId_t mqtt_send_task_id; // mqtt 发布数据任务ID osThreadId_t mqtt_recv_task_id; // mqtt 接收数据任务ID osThreadId_t key_detection_task_id; // 按键检测任务ID #define MQTT_SEND_TASK_TIME 10 // s #define MQTT_RECV_TASK_TIME 1 // s #define TASK_INIT_TIME 2 // s #define DISPLAY_BUFF_MAX 64 #define MQTT_DATA_MAX 256 #define RED_LED_PIN_NAME HI_IO_NAME_GPIO_10 #define RED_LED_PIN_FUNCTION WIFI_IOT_IO_FUNC_GPIO_10_GPIO #define GREEN_LED_PIN_NAME HI_IO_NAME_GPIO_2 #define GREEN_LED_PIN_FUNCTION WIFI_IOT_IO_FUNC_GPIO_2_GPIO #define BLUE_LED_PIN_NAME HI_IO_NAME_GPIO_7 #define BLUE_LED_PIN_FUNCTION WIFI_IOT_IO_FUNC_GPIO_7_GPIO #define PWM0_PORT_NUM (0) #define PWM1_PORT_NUM (1) #define PWM2_PORT_NUM (2) #define PWM_FREQ_DIVITION 64000 #define KEY1 HI_IO_NAME_GPIO_6 // 对应按键key1 #define KEY2 HI_IO_NAME_GPIO_5 // 对应按键key2 #define TASK_DELAY_TIME (200 * 1000) static int g_netId = -1; uint8_t publish_topic[MQTT_DATA_MAX] = {0}; uint8_t mqtt_data[MQTT_DATA_MAX] = {0}; unsigned short duty0 = 0, duty1 = 0, duty2 = 0; // pwm占空比记录变量 hi_gpio_value val1, val1_last, val2, val2_last; // GPIO的状态值 /** * @brief 灯光开关 */ void led_switch(uint8_t status) { if (status == 1) { if (duty0 == 0) duty0 = 100; if (duty1 == 0) duty1 = 100; if (duty2 == 0) duty2 = 100; IoTPwmStart(PWM0_PORT_NUM, duty0, PWM_FREQ_DIVITION); IoTPwmStart(PWM1_PORT_NUM, duty1, PWM_FREQ_DIVITION); IoTPwmStart(PWM2_PORT_NUM, duty2, PWM_FREQ_DIVITION); } else { IoTPwmStart(PWM0_PORT_NUM, 0, PWM_FREQ_DIVITION); IoTPwmStart(PWM1_PORT_NUM, 0, PWM_FREQ_DIVITION); IoTPwmStart(PWM2_PORT_NUM, 0, PWM_FREQ_DIVITION); } } /** * @brief 设置灯光颜色 */ void led_color(uint8_t r, uint8_t g, uint8_t b) { printf("led_color: %d %d %d\r\n", r, g, b); IoTPwmStart(PWM0_PORT_NUM, b, PWM_FREQ_DIVITION); IoTPwmStart(PWM1_PORT_NUM, r, PWM_FREQ_DIVITION); IoTPwmStart(PWM2_PORT_NUM, g, PWM_FREQ_DIVITION); } /** * @brief 组JSON数据 */ int Packaged_json_data(void) { cJSON *root = NULL, *array = NULL, *services = NULL; cJSON *properties = NULL; int ret = 0; // 组JSON数据 root = cJSON_CreateObject(); // 创建一个对象 services = cJSON_CreateArray(); cJSON_AddItemToObject(root, "services", services); array = cJSON_CreateObject(); cJSON_AddStringToObject(array, "service_id", "attribute"); properties = cJSON_CreateObject(); cJSON_AddItemToObject(array, "properties", properties); cJSON_AddStringToObject(properties, "status", sensorData.status ? "ON" : "OFF"); cJSON_AddNumberToObject(properties, "color", (int)sensorData.color); cJSON_AddItemToArray(services, array); // 将对象添加到数组中 /* 格式化打印创建的带数组的JSON对象 */ char *str_print = cJSON_PrintUnformatted(root); if (str_print != NULL) { // printf("%s\n", str_print); if (strcpy_s(mqtt_data, strlen(str_print) + 1, str_print) == 0) { ret = 0; } else { ret = -1; } cJSON_free(str_print); } else { ret = -1; } if (root != NULL) { cJSON_Delete(root); } else { ret = -1; } properties = str_print = root = array = services = NULL; return ret; } /** * @brief MQTT 上报消息 */ void mqtt_client_pub(void) { // 组Topic memset_s(publish_topic, MQTT_DATA_MAX, 0, MQTT_DATA_MAX); if (sprintf_s(publish_topic, MQTT_DATA_MAX, MQTT_TOPIC_PUB_PROPERTIES, DEVICE_ID) > 0) { // 组JSON数据 Packaged_json_data(); // 发布消息 MQTTClient_pub(publish_topic, mqtt_data, strlen((char *)mqtt_data)); } } /** * @brief MQTT 周期发布消息任务 */ void mqtt_send_task(void) { while (1) { mqtt_client_pub(); sleep(MQTT_SEND_TASK_TIME); } } int get_jsonData_value(const cJSON *const object, uint8_t *value) { cJSON *json_value = NULL; int ret = -1; json_value = cJSON_GetObjectItem(object, "value"); if (json_value) { if (!strcmp(json_value->valuestring, "ON")) { *value = 1; json_value = NULL; ret = 0; // 0为成功 } else if (!strcmp(json_value->valuestring, "OFF")) { *value = 0; json_value = NULL; ret = 0; } } json_value = NULL; return ret; // -1为失败 } /** * @brief 解析JSON数据 */ int Parsing_json_data(const char *payload) { cJSON *root = NULL, *command_name = NULL, *paras = NULL, *value = NULL; cJSON *red = NULL, *green = NULL, *blue = NULL; int ret_code = 1; root = cJSON_Parse((const char *)payload); if (root) { // 解析JSON数据 command_name = cJSON_GetObjectItem(root, "command_name"); paras = cJSON_GetObjectItem(root, "paras"); if (command_name) { if (!strcmp(command_name->valuestring, "led")) { ret_code = get_jsonData_value(paras, &sensorData.status); // 操作硬件 led_switch(sensorData.status); } else if (!strcmp(command_name->valuestring, "RGB")) { red = cJSON_GetObjectItem(paras, "red"); green = cJSON_GetObjectItem(paras, "green"); blue = cJSON_GetObjectItem(paras, "blue"); led_color(red->valueint, green->valueint, blue->valueint); ret_code = 0; // 0为成功 } } } cJSON_Delete(root); root = command_name = paras = value = red = green = blue = NULL; return ret_code; } // 向云端发送返回值 void send_cloud_request_code(const char *request_id, int ret_code, int request_len) { char *request_topic = (char *)malloc(strlen(MALLOC_MQTT_TOPIC_PUB_COMMANDS_REQ) + strlen(DEVICE_ID) + request_len + 1); if (request_topic != NULL) { memset_s(request_topic, strlen(DEVICE_ID) + strlen(MALLOC_MQTT_TOPIC_PUB_COMMANDS_REQ) + request_len + 1, 0, strlen(DEVICE_ID) + strlen(MALLOC_MQTT_TOPIC_PUB_COMMANDS_REQ) + request_len + 1); if (sprintf_s(request_topic, strlen(DEVICE_ID) + strlen(MALLOC_MQTT_TOPIC_PUB_COMMANDS_REQ) + request_len + 1, MQTT_TOPIC_PUB_COMMANDS_REQ, DEVICE_ID, request_id) > 0) { if (ret_code == 0) { MQTTClient_pub(request_topic, "{\"result_code\":0}", strlen("{\"result_code\":0}")); } else if (ret_code == 1) { MQTTClient_pub(request_topic, "{\"result_code\":1}", strlen("{\"result_code\":1}")); } } free(request_topic); request_topic = NULL; } } /** * @brief MQTT接收数据的回调函数 */ int8_t mqttClient_sub_callback(unsigned char *topic, unsigned char *payload) { if ((topic == NULL) || (payload == NULL)) { return -1; } else { printf("topic: %s\r\n", topic); printf("payload: %s\r\n", payload); // 提取出topic中的request_id char request_id[50] = {0}; int ret_code = 1; // 1为失败 if (0 == strcpy_s(request_id, sizeof(request_id), topic + strlen(DEVICE_ID) + strlen("$oc/devices//sys/commands/request_id="))) { printf("request_id: %s\r\n", request_id); // 解析JSON数据 ret_code = Parsing_json_data(payload); send_cloud_request_code(request_id, ret_code, sizeof(request_id)); } } return 0; } /** * @brief MQTT 接收消息任务 */ void mqtt_recv_task(void) { while (1) { MQTTClient_sub(); sleep(MQTT_RECV_TASK_TIME); } } /** * @brief 按键检测任务 */ void key_detection_task(void) { while (1) { hi_gpio_get_input_val(KEY1, &val1); // 获取GPIO引脚的状态 if (val1 != val1_last) { // 当GPIO状态改变的时候, 打印输出 printf("key1Value: %s\r\n", val1 ? "HI_GPIO_VALUE_1" : "HI_GPIO_VALUE_0"); val1_last = val1; if (!val1_last) { // 按键被按下 if (sensorData.status) { sensorData.status = 0; led_switch(sensorData.status); mqtt_client_pub(); } else { sensorData.status = 1; led_switch(sensorData.status); mqtt_client_pub(); } } printf("key1Value: end\r\n"); } hi_gpio_get_input_val(KEY2, &val2); // 获取GPIO引脚的状态 if (val2 != val2_last) { // 当GPIO状态改变的时候, 打印输出 printf("key2Value: %s\r\n", val2 ? "HI_GPIO_VALUE_1" : "HI_GPIO_VALUE_0"); val2_last = val2; } // 200ms一检测 usleep(TASK_DELAY_TIME); // 200ms sleep } } static void network_wifi_mqtt_example(void) { // 外设的初始化 // 灯光pwm初始化 hi_io_set_func(RED_LED_PIN_NAME, HI_IO_FUNC_GPIO_10_PWM1_OUT); hi_io_set_func(GREEN_LED_PIN_NAME, HI_IO_FUNC_GPIO_2_PWM2_OUT); hi_io_set_func(BLUE_LED_PIN_NAME, HI_IO_FUNC_GPIO_7_PWM0_OUT); IoTPwmInit(PWM0_PORT_NUM); IoTPwmInit(PWM1_PORT_NUM); IoTPwmInit(PWM2_PORT_NUM); // 按键初始化 hi_gpio_init(); // GPIO初始化 hi_io_set_pull(KEY1, HI_IO_PULL_UP); // 设置GPIO上拉 hi_io_set_func(KEY1, HI_IO_FUNC_GPIO_6_GPIO); // 设置IO16为GPIO功能 hi_gpio_set_dir(KEY1, HI_GPIO_DIR_IN); // 设置GPIO为输入模式 hi_io_set_pull(KEY2, HI_IO_PULL_UP); // 设置GPIO上拉 hi_io_set_func(KEY2, HI_IO_FUNC_GPIO_5_GPIO); // 设置IO15为GPIO功能 hi_gpio_set_dir(KEY2, HI_GPIO_DIR_IN); // 设置GPIO为输入模式 p_MQTTClient_sub_callback = &mqttClient_sub_callback; // 连接WiFi WifiDeviceConfig config = {0}; // 准备AP的配置参数 // strcpy(config.ssid, PARAM_HOTSPOT_SSID); // strcpy(config.preSharedKey, PARAM_HOTSPOT_PSK); strcpy_s(config.ssid, WIFI_MAX_SSID_LEN, PARAM_HOTSPOT_SSID); strcpy_s(config.preSharedKey, WIFI_MAX_KEY_LEN, PARAM_HOTSPOT_PSK); config.securityType = PARAM_HOTSPOT_TYPE; g_netId = ConnectToHotspot(&config); printf("netId = %d\r\n", g_netId); sleep(TASK_INIT_TIME); // 连接MQTT服务器 if (MQTTClient_connectServer(SERVER_IP_ADDR, SERVER_IP_PORT) != WIFI_SUCCESS) { printf("[error] mqttClient_connectServer\r\n"); } sleep(TASK_INIT_TIME); // 初始化MQTT客户端 if (MQTTClient_init(MQTT_CLIENT_ID, MQTT_USER_NAME, MQTT_PASS_WORD) != WIFI_SUCCESS) { printf("[error] mqttClient_init\r\n"); } sleep(TASK_INIT_TIME); // 订阅主题 if (MQTTClient_subscribe(MQTT_TOPIC_SUB_COMMANDS) != WIFI_SUCCESS) { printf("[error] mqttClient_subscribe\r\n"); } sleep(TASK_INIT_TIME); mqtt_client_pub(); // 连接后上报一次消息 // 创建线程 osThreadAttr_t options; options.name = "mqtt_send_task"; options.attr_bits = 0; options.cb_mem = NULL; options.cb_size = 0; options.stack_mem = NULL; options.stack_size = TASK_STACK_SIZE; options.priority = osPriorityNormal; mqtt_send_task_id = osThreadNew((osThreadFunc_t)mqtt_send_task, NULL, &options); if (mqtt_send_task_id != NULL) { printf("ID = %d, Create mqtt_send_task_id is OK!\r\n", mqtt_send_task_id); } options.name = "mqtt_recv_task"; options.stack_size = TASK_STACK_SIZE; mqtt_recv_task_id = osThreadNew((osThreadFunc_t)mqtt_recv_task, NULL, &options); if (mqtt_recv_task_id != NULL) { printf("ID = %d, Create mqtt_recv_task_id is OK!\r\n", mqtt_recv_task_id); } options.name = "key_detection_task"; options.stack_size = TASK_STACK_SIZE; key_detection_task_id = osThreadNew((osThreadFunc_t)key_detection_task, NULL, &options); if (key_detection_task_id != NULL) { printf("ID = %d, Create key_detection_task is OK!\r\n", key_detection_task_id); } } SYS_RUN(network_wifi_mqtt_example);
其中有三个任务,分别是mqtt的周期上报,模拟温度计类的需要定时上报的功能,mqtt接收任务,用来处理服务器下发的控制命令,还有一个任务用来检测按键,当key1按下时切换灯的开关。
使用build,编译成功后,使用upload进行烧录。
这是可以从设备信息中看到设备在线,并且能看到设备上报的状态。
通过服务器下发设备控制命令,举例控制设备灯开关。
设备收到服务器发来的数据。
同时服务器也会更新设备上报的状态。