(1)实验平台:

普中Hi3861鸿蒙物联网WIFI套件https://item.taobao.com/item.htm?id=829136021914(2)资料下载:普中科技-各型号产品资料下载链接


        如今物联网市场异常火爆, WIFI 是物联网中非常重要的角色, 现在基本上家家户户都有 WIFI 网络, 通过 WIFI 接入到互联网, 成了智能家居产品普遍的选择。 Hi3861 内部已集成 WIFI 功能, 可以说它就是为 WIFI 无线连接而生的。 本章来学习 WIFI 的 MQTT 网络通信应用, 与华为 IoTDA 云端接入控制, 使用 Hi3861开发 WIFI 是非常简单而美妙的, 让大家在学习物联网中变的简单有趣。 本章分为如下几部分内容:

 

31.1 实验介绍

31.1.1 实验简介

31.1.1.1 华为 IoTDA 介绍

31.1.1.2 接入 IoTDA 方法步骤

31.1.2 实验目的

31.1.3 WIFI 函数使用

31.2 硬件设计

31.3 软件设计

31.4 实验现象


31.1 实验介绍

31.1.1 实验简介

31.1.1.1 华为 IoTDA 介绍

        华为 IoTDA, 即华为云物联网平台设备接入服务(IoT Device Access) , 是华为提供的一个强大的物联网平台, 旨在帮助物联网行业用户快速完成设备联网及行业应用集成。 提供海量设备连接上云、 设备和云端双向消息通信、 批量设备管理、 远程控制和监控、 OTA 升级、 设备联动规则等能力。

        使用物联网平台构建一个完整的物联网解决方案主要包括 3 部分: 物联网平台、 业务应用和设备。

        ●物联网平台作为连接业务应用和设备的中间层, 屏蔽了各种复杂的设备接口, 实现设备的快速接入; 同时提供强大的开放能力, 支撑行业用户构建各种物联网解决方案。

        ●设备可以通过固网、 2G/3G/4G/5G、 NB-IoT、 Wifi 等多种网络接入物联网平台, 并使用 LWM2M/CoAP、 MQTT、 HTTPS 等主流协议或行业协议将业务数据上报到平台, 平台也可以将控制命令下发给设备。

        ●业务应用通过调用物联网平台提供的 API, 实现设备数据采集、 命令下发、设备管理等业务场景。

        有关华为云端使用资料可进入官网学习:https://support.huaweicloud.com/iothub/#

31.1.1.2 接入 IoTDA 方法步骤

        要接入 IoTDA, 主要完成如下步骤:

        ●注册设备: 在 IoTDA 控制台中, 注册设备实例, 获取设备的唯一标识(如设备 ID 和密钥) 。

        ●设备连接: 通过 Hi3861 开发板上的固件程序, 使用 MQTT 或其他支持的协议连接到 IoTDA 平台。 连接时, 需要提供设备 ID、 密钥以及 IoTDA 平台的接入点信息。

        ●数据上报: 设备成功连接到 IoTDA 后, 根据产品模型中定义的属性, 定期或按需上报设备数据。

        步骤 1: 注册华为云账号并登录

        1、 打开华为云物联网平台的网址: www.huaweicloud.com, 打开网址后, 点击右上角的“注册” 。

        2、 在“注册” 页面中, 填入手机号进行注册, 所有信息填写完成后, 点击“注册” 按钮, 注册成功后, 进入下一步。

        3、 注册完成后, 再次进入华为云官网进行登录, 在登入界面, 填入刚刚注册的“手机号” 和“密码” , 然后点击登入即可。

        4、 登入成功后, 点击右上角的“控制台” 。

        5、 在控制台的左上角, 因为标准版免费试用仅针对企业用户开放, 个人用户建议使用北京四基础版, 所以选择地区为“北京四” , 然后点击左侧“服务列表” 。

        6、 在“服务列表” 的搜索框中搜索“IoTDA” , 然后点击“设备接入 IoTDA”。

        7、 在设备接入服务页面中, 点击“接入信息” , 将“MQTT 协议设备接入 IP地址和端口号” 记录下来。

        步骤 2: 创建新产品

        1、 点击左侧的产品, 点击创建产品按钮。

        2、 选择所属的资源, 产品名称填写 pz_hi3861_led, 名称可自定义, 数据格式默认, 所属行业/子行业和设备类型任意, 点击确定。

        3、 在左侧菜单栏中, 找到所有设备, 点击注册设备。

        4、 选择所属资源空间, 所属产品, 填写设备标识码为 hi3861_test, 密钥由系统自动产生。

        注意: 生成好的设备 ID 和设备秘钥一定到妥善保管, 只生成这一份。 后面会用到。

        步骤 3: 创建产品模型

        1、 点击左侧“产品” , 点击新创建好的产品名称“hi3861_led” 。

        2、 点击自定义模型。

        3、 填写服务 ID 和服务类型。

        4、 在此服务下“添加属性” , 用于上传开发板上的数据, 以 LED 和温度为例, 其他传感器也是类似的方法。

        5、 展示创建完成的属性列表。

        6、 在此服务下“添加命令” , 用于控制开发板上可以控制的设备, 以 LED为例, 其他传感器也是类似的方法。

        7、 展示创建完成的命令列表。

        步骤 4: 修改替换程序中 MQTT 接入 IOTDA 的相关账号信息

        1、 将生成好的信息(产品 ID、 clientId、 username、 password) 用于修改工程 template.c 文件前部 MQTT 相关配置。

        产品 ID 位置如下:

        clientId、 username、 password 位置如下:

        程序替换位置如下:

        2、 修改华为云平台的 MQTT 服务器的 IP 地址, 打开 Windows 下 cmd 控制端,在控制端输入: ping+网址, 注意 ping 的网址是注册的产品的接入信息。

        3、 修改文件中 IP 地址和端口号。

        至此, 完成了 IoTDA 接入的步骤。 注意: 每个人注册的账号后添加产品和设备, 最终生成的这些信息会不同, 因此要学会替换对应的信息。

31.1.2 实验目的

        在华为云 IoTDA 物联网平台中可以看到开发板上传的温度数据, 并且可以控制开发板中的 LED 灯。

31.1.3 WIFI 函数使用

        要连接华为云, 采用的是 MQTT 协议, 关于 MQTT 协议相关函数使用, 可参考MQTT 实验章节内容。

31.2 硬件设计

        由于 Hi3861 内置 WIFI 功能, 所以直接在开发板上使用即可, 无需额外连接。

31.3 软件设计

        将前面章节创建好的工程模板, 复制一份, 重命名为 27_wifi_huawei_iotda,如下所示:

        (1) 修改 demo 文件夹下的 BUILD.gn 文件, 如下所示:

        (2) 添加工程编译文件路径, 如下所示

        (3) 修改 template.c 文件, 代码如下:

/**
 ****************************************************************************************************
 * @file        template.c
 * @author      普中科技
 * @version     V1.0
 * @date        2024-06-05
 * @brief       WIFI 华为IoTDA设备接入实验
 * @license     Copyright (c) 2024-2034, 深圳市普中科技有限公司
 ****************************************************************************************************
 * @attention
 *
 * 实验平台:普中-Hi3861
 * 在线视频:https://space.bilibili.com/2146492485
 * 公司网址:www.prechin.cn
 * 购买地址:
 *
 ****************************************************************************************************
 * 实验现象:开发板连接路由器热点,在浏览器输入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 "普中科技"
#define WIFI_PAWD "88888888.@"

//注册华为云后需要更新的信息
// 设备ID
#define DEVICE_ID "66a1f950a559ef62266a19c3"
// MQTT客户端ID
#define MQTT_CLIENT_ID "66a1f950a559ef62266a19c3_hi3861_led_0_0_2024072507" 
// MQTT用户名
#define MQTT_USER_NAME "66a1f950a559ef62266a19c3_hi3861_led"
// MQTT密码
#define MQTT_PASS_WORD "50518dcd2eb6c22e11190dee757a076fd81b92772fcabadc99b9143b5a13d5a5"
// 华为云平台的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 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 mqtt_data[MQTT_DATA_MAX] = {0};


/**
 * @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, "led", sensorData.led ? "ON" : "OFF");
    cJSON_AddNumberToObject(properties, "temperature", (int)sensorData.temperature);
    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;
}

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.led);
            } 
        }
    }
    cJSON_Delete(root);
    root = command_name = paras = value = red = green = blue = NULL;
    (sensorData.led == 1) ? LED(1) : LED(0);

    return ret_code;
}


/**
 * @brief MQTT  发布消息任务
 */
void mqtt_send_task(void)
{
    while (1) 
    {
        // 获取传感器的数据
        sensorData.temperature=ds18b20_gettemperture();

        // 组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));
        }
    }
    sleep(MQTT_SEND_TASK_TIME);
}

// 向云端发送返回值
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);
    }
}

//硬件初始化
void hardware_init(void)
{
    led_init();//LED初始化
    ds18b20_init();//DS18B20初始化

    p_MQTTClient_sub_callback = &mqttClient_sub_callback;

    // 连接WiFi
    if (WiFi_connectHotspots(WIFI_SSID, WIFI_PAWD) != WIFI_SUCCESS) {
        printf("[error] connectWiFiHotspots\r\n");
    }
    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);
}

//任务创建
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);

31.4 实验现象

        将程序下载到开发板内(可参考“2.2.5 程序下载运行”章节) , 打开串口调试助手“\5--开发工具\4-串口调试助手\UartAssist.exe” , 波特率设置为115200, 实验现象: 在华为云 IoTDA 物联网平台中可以看到开发板上传的温度数据, 并且可以控制开发板中的 LED 灯。

        1、 打开串口调试助手, 可以看到连接服务器, 创建 MQTT 客户端成功信息。

        2、 登入华为云 IoTD 平台, 可以看到设备已经在线。

        3、 下面测试命令下发功能, 操作如下:

Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐