Hi3861 OpenHarmony嵌入式应用入门--MQTT

MQTT 是机器对机器(M2M)/物联网(IoT)连接协议。它被设计为一个极其轻量级的发布/订阅消息传输 协议。对于需要较小代码占用空间和/或网络带宽非常宝贵的远程连接非常有用,是专为受限设备和低带宽、 高延迟或不可靠的网络而设计。这些原则也使该协议成为新兴的“机器到机器”(M2M)或物联网(IoT)世界 的连接设备,以及带宽和电池功率非常高的移动应用的理想选择。例如,它已被用于通过卫星链路与代理 通信的传感器、与医疗服务提供者的拨号连接,以及一系列家庭自动化和小型设备场景。它也是移动应用 的理想选择,因为它体积小,功耗低,数据包最小,并且可以有效地将信息分配给一个或多个接收器。M QTT 通信模型如下图所示:

前提需要在电脑上运行起来mqtt的服务器,emqx软件非常合适,他还带了mqtt的客户端,很方便调试,具体下载和安装方法见下面博文,也可以去emqx官网下载最新版进行免费试用。

MQTT:windows最简单搭建mqtt服务端及本地客户端测试_emqx-windows-4.3.6-CSDN博客

修改网络参数

在Hi3861开发板上运行上述四个测试程序之前,需要根据你的无线路由、Linux系统IP修改 net_params.h文件的相关代码:

  • PARAM_HOTSPOT_SSID 修改为你的热点名称
  • PARAM_HOTSPOT_PSK 修改为你的热点密码;
  • PARAM_SERVER_ADDR 修改为你的服务器IP地址;
  • PARAM_SERVER_PORT 修改为你的服务器端口号;
  • SERVER_IP_ADDR 修改为你的mqtt服务器IP地址;
  • SERVER_IP_PORT 修改为mqtt的tcp端口,默认为1883;
  • MQTT_TOPIC_SUB 订阅ID;
  • MQTT_TOPIC_PUB 发布的ID;
  • MQTT_CLIENT_ID MQTT的客户端ID;
  • MQTT_USER_NAME MQTT的用户名;
  • MQTT_PASSWORD MQTT的密码;

代码编写

修改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",
  ]
}

创建D:\DevEcoProjects\test\src\vendor\rtplay\rt_hi3861\demo\network_15_mqtt文件夹

文件夹中创建D:\DevEcoProjects\test\src\vendor\rtplay\rt_hi3861\demo\network_15_mqtt\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("network_mqtt_example") {
    sources = [
        "network_mqtt_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",
        "//vendor/hqyj/fs_hi3861/common/bsp/include",
        "//third_party/paho.mqtt.embedded-c/MQTTPacket/src",
        "//third_party/cJSON",
    ]
}

文件夹中创建D:\DevEcoProjects\test\src\vendor\rtplay\rt_hi3861\demo\network_15_mqtt\network_mqtt.h文件,文件主要包含mqtt的函数。

/*
 * 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.
 */

#ifndef NETWORK_MQTT_H
#define NETWORK_MQTT_H

/**
 * @brief MQTT  连接MQTT服务器
 * @return Returns {0} 成功;
 *         Returns {-1} 失败.
 */
int MQTTClient_connectServer(const char *ip_addr, int ip_port);
/**
 * @brief MQTT  断开连接MQTT服务器
 * @return Returns {0} 成功;
 *         Returns {-1} 失败.
 */
int MQTTClient_unConnectServer(void);
/**
 * @brief MQTT  订阅MQTT主题
 * @return Returns {0} 成功;
 *         Returns {-1} 失败.
 */
int MQTTClient_subscribe(char *subTopic);
/**
 * @brief MQTT 客户端的初始化
 * @param clientID  客户端ID
 * @param userName  用户名
 * @param password  密码
 * @return Returns {0} 成功;
 *         Returns {-1} 失败.
 */
int MQTTClient_init(char *clientID, char *userName, char *password);
/**
 * @brief MQTT 发布消息
 * @param pub_Topic 具有发布权限的主题名称
 * @param payloadData  发布数据
 * @param payloadLen 发布数据的长度
 * @return Returns {0} 成功;
 *         Returns {-1} 失败.
 */
int MQTTClient_pub(char *pub_Topic, unsigned char *payloadData, int payloadLen);
/**
 * @brief MQTT  接收消息
 * @param callback 当接收到消息之后,将消息传到到回调函数中
 * @return Returns {0} 成功;
 *         Returns {-1} 失败.
 */
int MQTTClient_sub(void);

extern int8_t(*p_MQTTClient_sub_callback)(unsigned char *topic, unsigned char *payload);

#endif // !NETWORK_MQTT_H

文件夹中创建D:\DevEcoProjects\test\src\vendor\rtplay\rt_hi3861\demo\network_15_mqtt\network_mqtt.c文件,文件主要包含mqtt的函数实现。

/*
 * 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 <unistd.h>
#include <string.h>
#include <stdlib.h>
#include "lwip/netifapi.h"
#include "lwip/sockets.h"
#include "wifi_device.h"
#include "ohos_init.h"
#include "MQTTPacket.h"
#include "network_mqtt.h"

#define MQTT_BUFF_MAX_SIZE 512

int g_tcp_socket_fd = 0; // 网络套接字
unsigned char mqttBuff[MQTT_BUFF_MAX_SIZE] = {0};

// 发送网络数据
static int transport_sendPacketBuffer(unsigned char *buf, int buflen)
{
    int rc = send(g_tcp_socket_fd, buf, buflen, 0);

    return (rc <= 0) ? 0 : 1;
}
// 接收网络数据
static int transport_getdata(unsigned char *buf, int count)
{
    int rc = recv(g_tcp_socket_fd, buf, count, 0);
    return rc;
}

// 连接服务器
int MQTTClient_connectServer(const char *ip_addr, int ip_port)
{
    if (ip_addr == NULL) {
        return -1;
    }

    int res = 0;                        // 函数返回值
    struct sockaddr_in tcpServerConfig; // tcp服务器信息

    // 创建TCP套接字
    g_tcp_socket_fd = socket(AF_INET, SOCK_STREAM, 0);
    if (g_tcp_socket_fd < 0) {
        printf("Failed to create Socket\r\n");
    }

    // 连接TCP服务器
    tcpServerConfig.sin_family = AF_INET;                 // IPV4
    tcpServerConfig.sin_port = htons(ip_port);            // 填写服务器的IP端口号
    tcpServerConfig.sin_addr.s_addr = inet_addr(ip_addr); // 填写服务器的IP地址

    res = connect(g_tcp_socket_fd, (struct sockaddr *)&tcpServerConfig, sizeof(tcpServerConfig)); // 连接服务器
    if (res == -1) {
        printf("Failed to connect to the server\r\n");
        return -1;
    }
    printf("Connection to server successful\r\n");

    return 0;
}

// 断开TCP服务器 0:成功, -1:失败
int MQTTClient_unConnectServer(void)
{
    int ret = 0;
    printf("Server shut down successfully\r\n");
    ret = close(g_tcp_socket_fd);
    g_tcp_socket_fd = 0;
    return ret;
}

// mqtt客户端 订阅主题
int MQTTClient_subscribe(char *subTopic)
{
    if (subTopic == NULL) {
        printf("Incorrect parameters\r\n");
        return -1;
    }

    int len = 0, res = 0;
    int subcount = 0, granted_qos = 0, req_qos = 0;
    unsigned short submsgid = 0;
    MQTTString topicString = MQTTString_initializer;

    /* subscribe */
    topicString.cstring = subTopic;
    len = MQTTSerialize_subscribe(mqttBuff, sizeof(mqttBuff), 0, 1, 1, &topicString, &req_qos);
    if (len <= 0) {
        printf("MQTTSerialize_subscribe Error %d\r\n", len);
        return -1;
    }
    res = transport_sendPacketBuffer(mqttBuff, len);
    if (res != 1) {
        printf("transport_sendPacketBuffer Error %d\r\n", res);
        return -1;
    }

    sleep(1);
    memset_s(mqttBuff, sizeof(mqttBuff), 0, sizeof(mqttBuff));

    /* wait for suback */
    if (MQTTPacket_read(mqttBuff, sizeof(mqttBuff), transport_getdata) != SUBACK) {
        printf("MQTTPacket_read Error\r\n");
        return -1;
    }

    if (MQTTDeserialize_suback(&submsgid, 1, &subcount, &granted_qos, mqttBuff, sizeof(mqttBuff)) != 1) {
        printf("MQTTDeserialize_suback Error\r\n");
        return -1;
    }

    printf("MQTT subscribed to topics successfully\r\n");

    return 0;
}

// 保持在线时长 60s
#define MQTT_KEEP_ALIVE 60
#define MQTT_DELAY_TIME 3

// mqtt客户端 初始化
int MQTTClient_init(char *clientID, char *userName, char *password)
{
    if (clientID == NULL || userName == NULL || password == NULL) {
        printf("Incorrect parameters\r\n");
        return -1;
    }

    int res = 0, len = 0, i = 0;
    int mqtt_read_len = 10;

    unsigned char sessionPresent = 0, connack_rc = 0;
    MQTTPacket_connectData mqttData = MQTTPacket_connectData_initializer;

    // 初始化MQTT客户端
    mqttData.clientID.cstring = clientID;
    mqttData.username.cstring = userName;
    mqttData.password.cstring = password;
    mqttData.cleansession = true;  // 是否初始化的时候,清除上一次的对话
    mqttData.keepAliveInterval = MQTT_KEEP_ALIVE;

    // 组MQTT消息包
    len = MQTTSerialize_connect(mqttBuff, sizeof(mqttBuff), &mqttData);
    if (len <= 0) {
        printf("MQTTSerialize_connect Error %d\r\n", res);
        return -1;
    }
    res = transport_sendPacketBuffer(mqttBuff, len);
    if (res != 1) {
        printf("transport_sendPacketBuffer Error %d\r\n", res);
        return -1;
    }

    sleep(MQTT_DELAY_TIME);

    /* 打印发送出去的数据帧,调试用 */
    printf("MQTT_sendPacket:  \r\n");
    for (i = 0; i < len; i++) {
        printf("%x ", mqttBuff[i]);
    }
    printf("\r\n");

    memset_s(mqttBuff, sizeof(mqttBuff), 0, sizeof(mqttBuff));

    /* wait for connack */
    if (MQTTPacket_read(mqttBuff, sizeof(mqttBuff), transport_getdata) != CONNACK) {
        printf("MQTTPacket_read != CONNACK\r\n");
    }

    printf("MQTT_recvPacket:  \r\n");
    /* 打印服务器返回的消息,调试用 */
    for (i = 0; i < mqtt_read_len; i++) {
        printf("%x ", mqttBuff[i]);
    }
    printf("\r\n");

    if (MQTTDeserialize_connack(&sessionPresent, &connack_rc, mqttBuff, sizeof(mqttBuff)) != 1 || connack_rc != 0) {
        printf("Unable to connect, return code %d\r\n", connack_rc);
        memset_s(mqttBuff, sizeof(mqttBuff), 0, sizeof(mqttBuff));
        return -1;
    } else {
        printf("MQTT initialized successfully\r\n");
    }
    memset_s(mqttBuff, sizeof(mqttBuff), 0, sizeof(mqttBuff));
    return 0;
}

#define MQTT_PUB_DATA_TIME (100 * 1000)

int MQTTClient_pub(char *pub_Topic, unsigned char *payloadData, int payloadLen)
{
    if (payloadData == NULL) {
        printf("Incorrect parameters\r\n");
        return -1;
    }

    printf("pubTopic: %s\n", pub_Topic);
    printf("pubData: %s\n", payloadData);

    int ret = 0, len = 0;
    unsigned short retry_count = 5; // 重发次数
    unsigned char sendBuff[MQTT_BUFF_MAX_SIZE] = {0};
    MQTTString topicString = MQTTString_initializer;

    topicString.cstring = pub_Topic;
    len = MQTTSerialize_publish(sendBuff, sizeof(sendBuff), 0, 0, 0, 0, topicString,
                                payloadData,
                                payloadLen);
    while (--retry_count > 0) {
        ret = transport_sendPacketBuffer(sendBuff, len);
        if (ret == 1) {
            break;
        }

        printf("Send MQTT_Data Fail\r\n");
        usleep(MQTT_PUB_DATA_TIME);
    }

    if (!retry_count && ret != 1) {
        printf("transport_sendPacketBuffer Error %d\r\n", ret);
        return -1;
    }

    // printf("send==>%s", payloadData);
    return 0;
}
unsigned char mqtt_topic[200];
int8_t (*p_MQTTClient_sub_callback)(unsigned char *topic, unsigned char *payload);

int MQTTClient_sub(void)
{
    int qos, payloadlen_in;
    unsigned char dup, retained;
    unsigned short msgid;
    unsigned char *payload_in;
    MQTTString receivedTopic;

    memset_s(mqttBuff, sizeof(mqttBuff), 0, sizeof(mqttBuff));
    // $oc/devices/63ad5a6cc4efcc747bd75973_lamp/sys/commands/request_id=42c20ffb-0885-4f6e-97b5-45d8f613efaf
    if (MQTTPacket_read(mqttBuff, sizeof(mqttBuff), transport_getdata) == PUBLISH) {
        MQTTDeserialize_publish(&dup, &qos, &retained, &msgid, &receivedTopic,
                                &payload_in, &payloadlen_in, mqttBuff, sizeof(mqttBuff));

        printf("data: %s\n", receivedTopic.lenstring.data);
        printf("length: %d\n", strlen(receivedTopic.lenstring.data) - payloadlen_in);
        printf("payload_length: %d\n", payloadlen_in);
        memcpy_s(mqtt_topic, sizeof(mqtt_topic),
                 receivedTopic.lenstring.data, strlen(receivedTopic.lenstring.data) - payloadlen_in);
        printf("topic: %s\n", mqtt_topic);
        printf("payload: %s\n", payload_in);

        p_MQTTClient_sub_callback(mqtt_topic, payload_in);
    }
}

文件夹中创建D:\DevEcoProjects\test\src\vendor\rtplay\rt_hi3861\demo\network_15_mqtt\wifi_connecter.h文件,该头文件包含wifi连接的宏。文件同tcp_12_client\wifi_connecter.h

文件夹中创建D:\DevEcoProjects\test\src\vendor\rtplay\rt_hi3861\demo\network_15_mqtt\wifi_connecter.c文件,文件同tcp_12_client\wifi_connecter.c

文件夹中创建D:\DevEcoProjects\test\src\vendor\rtplay\rt_hi3861\demo\network_15_mqtt\network_mqtt_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 <unistd.h>
#include <string.h>

#include "ohos_init.h"
#include "cmsis_os2.h"
#include "network_mqtt.h"
#include "wifi_connecter.h"

#define SERVER_IP_ADDR "192.168.137.1"
#define SERVER_IP_PORT 1883
#define MQTT_TOPIC_SUB "subTopic"
#define MQTT_TOPIC_PUB "pubTopic"
#define MQTT_CLIENT_ID "mqtt_client_123"
#define MQTT_USER_NAME "rtplay"
#define MQTT_PASSWORD "password"
#define TASK_STACK_SIZE (1024 * 5)
#define TASK_INIT_TIME 2 // s
#define MQTT_RECV_TASK_TIME (200 * 1000) // us
#define DELAY_TICKS_10     (10)

osThreadId_t mqtt_send_task_id;   // mqtt订阅数据任务
osThreadId_t mqtt_recv_task_id;   // mqtt发布数据任务
int8_t mqtt_sub_payload_callback(unsigned char *topic, unsigned char *payload)
{
    printf("[info] topic:[%s]    recv<== %s\r\n", topic, payload);
}
void mqtt_recv_task(void)
{
    while (1) {
        MQTTClient_sub();
        usleep(MQTT_RECV_TASK_TIME);
    }
}
void mqtt_send_task(void)
{
    // 连接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;

    osDelay(DELAY_TICKS_10);

    int netId = ConnectToHotspot(&config);

    // 连接MQTT服务器
    if (MQTTClient_connectServer(SERVER_IP_ADDR, SERVER_IP_PORT) != 0) {
        printf("[error] MQTTClient_connectServer\r\n");
    } else {
        printf("[success] MQTTClient_connectServer\r\n");
    }
    sleep(TASK_INIT_TIME);

    // 初始化MQTT客户端
    if (MQTTClient_init(MQTT_CLIENT_ID, MQTT_USER_NAME, MQTT_PASSWORD) != 0) {
        printf("[error] MQTTClient_init\r\n");
    } else {
        printf("[success] MQTTClient_init\r\n");
    }
    sleep(TASK_INIT_TIME);

    // 订阅Topic
    if (MQTTClient_subscribe(MQTT_TOPIC_SUB) != 0) {
        printf("[error] MQTTClient_subscribe\r\n");
    } else {
        printf("[success] MQTTClient_subscribe\r\n");
    }
    sleep(TASK_INIT_TIME);

    osThreadAttr_t options;
    options.name = "mqtt_recv_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_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);
    }

    while (1) {
        MQTTClient_pub(MQTT_TOPIC_PUB, "hello world!!!", strlen("hello world!!!"));
        sleep(TASK_INIT_TIME);
    }
}

static void network_wifi_mqtt_example(void)
{
    printf("Enter network_wifi_mqtt_example()!\r\n");

    p_MQTTClient_sub_callback = &mqtt_sub_payload_callback;

    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);
    }
}
SYS_RUN(network_wifi_mqtt_example);

使用build,编译成功后,使用upload进行烧录。

首先运行emqx

设置中文

运行后电脑ap会看到设备

串口会输出

网页中客户端会看到模块

主题也会看到

创建websocket连接服务

客户端已经有了两个

订阅模块的消息

可以收到模块发送的消息

发送模块订阅的消息

模块能收到消息

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/782943.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

PFC电路中MOS管的选取2

上面这种驱动方式叫推挽驱动&#xff0c;或者图腾柱驱动 当芯片驱动脚 DRV为高电平时&#xff0c;此时回路中的源是芯片的 DRV引脚&#xff0c;芯片驱动电流从左往右流动&#xff0c;通过 R1&#xff0c;通过Q1的be脚&#xff0c;通过R3、R4给MOS管Q4的Cgs结电容充电 不过值得…

Mybatis-Plus一文详解BaseMapper和Service 使用

Mybatis-Plus简介 MyBatis-Plus (opens new window)&#xff08;简称 MP&#xff09;是一个MyBatis (opens new window)的增强工具&#xff0c;在 MyBatis 的基础上只做增强不做改变&#xff0c;为简化开发、提高效率而生。 特性&#xff1a; 无侵入&#xff1a;只做增强不做…

idea 默认路径修改

1.查看 idea 的安装路径&#xff08;右键点击 idea 图标&#xff0c;查看路径 &#xff09; “C:\Program Files\JetBrains\IntelliJ IDEA 2021.3.1\bin\idea64.exe” 在 bin 目录查看 idea.properties 文件&#xff0c;修改以下四个路径文件 # idea.config.path${user.home}/…

Linux的前世今生

Unix的起源和发展 1969年&#xff0c;AT&T贝尔实验室的Ken Thompson和Dennis Ritchie等人开发了Unix操作系统。Unix的设计理念强调小而简洁的工具&#xff0c;文本流和系统模块化&#xff0c;这些理念后来成为Linux开发的重要基础。1973年&#xff0c;Unix用C语言重新编写…

昇思25天学习打卡营第17天 | K近邻算法实现红酒聚类

内容介绍&#xff1a; K近邻算法&#xff08;K-Nearest-Neighbor, KNN&#xff09;是一种用于分类和回归的非参数统计方法&#xff0c;是机器学习最基础的算法之一。它正是基于以上思想&#xff1a;要确定一个样本的类别&#xff0c;可以计算它与所有训练样本的距离&#xff0…

LVGL移植与VS模拟器使用

一、移植文件介绍 二、移植部分 第一步&#xff1a;创建LVGL文件夹 第二步&#xff1a; 构造LVGL文件夹&#xff1a;LVGL - GUI - lvgl - 第三步&#xff1a;添加文件 3.1 从examples中添加2个.c文件 3.2 从src中添加文件 draw文件 extra文件 第四步&#xff1a; 三、Ke…

分享2个iPhone恢复照片的方法,赶紧码住收藏!

手机已经成为我们生活中不可或缺的一部分&#xff0c;它不仅仅是通讯工具&#xff0c;更是我们记录生活点滴的重要工具之一。然而&#xff0c;正如其他任何设备一样&#xff0c;iPhone上存储的照片有时也会不小心被删除或丢失。 别担心&#xff0c;即使你误删了重要的照片&…

网安加·百家讲坛 | 马云卓:漏洞扫描工具漏洞报告对比

作者简介&#xff1a;马云卓&#xff0c;某安全公司安全专家&#xff0c;持有注册信息安全专业人员及渗透测试工程师&#xff08;CISP-PTE&#xff09;和项目管理专业人士&#xff08;PMP&#xff09;证书&#xff0c;拥有丰富的行业经验&#xff0c;长期专注于网络安全攻防技术…

用SOLIDWORKS批量打印工程图纸,没有难度

在工程师完成产品设计后&#xff0c;一般需要打印纸质工程图&#xff0c;如果打印的数量比较多&#xff0c;效率就会比较低&#xff0c;其实SOLIDWORKS软件提供了专用工具用来处理工作量比较大且重复性的工作&#xff0c;这个工具就是SOLIDWORKS Task Scheduler。 SOLIDWORKS T…

css实现鼠标禁用(鼠标滑过显示红色禁止符号)

css实现鼠标禁用&#xff08;鼠标滑过显示红色禁止符号&#xff09; 创作背景css鼠标禁用 创作背景 从本文开始&#xff0c;将会用三篇文章来一步一步实现 vueantdts实战后台管理系统中table表格的不可控操作。中间会补充两篇css知识文章 &#xff0c;方便后续功能的实现。 实…

面向对象编程:定义、特点、应用场景、优缺点及示例代码

目录 前言1. 面向对象编程的定义2. 面向对象编程的特点2.1 封装2.2 继承2.3 多态2.4 抽象 3. 面向对象编程的应用场景3.1 大型软件系统3.2 GUI应用程序3.3 游戏开发 4. 面向对象编程的优缺点4.1 优点4.2 缺点 5. 代表性的编程语言5.1 Java5.2 C5.3 Python 6. 示例代码结语 前言…

【爱上C++】vector用法详解

文章目录 一:vector简介二:vector的创建和初始化三:vector的遍历1.[]下标2.at()3.迭代器遍历4.范围for 四:vector的空间1.size2.max_size3.capacity4.reserve5.resize6.empty 五:vector的增删查改1.push_back2.pop_back3.find4.insert5.erase6.swap7.assign Hello~同学们好&…

ESP32CAM物联网教学10

ESP32CAM物联网教学10 MicroPython 应用体验 小智偶然地发现&#xff0c;有一种新兴的编程模式MicroPython&#xff0c;也能编写ESP32Cam的应用程序了&#xff0c;于是欣然地体验了一把。 编程环境搭建 小智偶然地从下面这家店铺买了一块ESP32Cam&#xff0c;并从客服那里得到…

【人工智能】-- 智能家居

个人主页&#xff1a;欢迎来到 Papicatch的博客 课设专栏 &#xff1a;学生成绩管理系统 专业知识专栏&#xff1a; 专业知识 文章目录 &#x1f349;引言 &#x1f349;基于深度卷积神经网络的表情识别 &#x1f348;流程图 &#x1f348;模型设计 &#x1f34d;网络架…

复旦微JFMVU3P-2FFVC1517 FPGA+AI全国产化人工智能数据处理平台,适用于雷达与中频信号采集、视频图像采集

板载FPGA实时处理器&#xff1a;JFMVU3P-2FFVC1517支持1个FMC&#xff08;HPC&#xff09;扩展接口支持2路QSFP光纤接口支持x8 Gen3 PCIE主机接口&#xff0c;系统带宽&#xff1e;5GByte/s支持1个R45自适应千兆以太网口支持1个GPIO/RS422接口 基于复旦微16nm工艺JFM9VU3P FPG…

【Linux】记录一起网站劫持事件

故事很短&#xff0c;处理也简单。权当记录一下&#xff0c;各位安全大大们手下留情。 最近一位客户遇到官网被劫持的情况&#xff0c;想我们帮忙解决一下&#xff08;本来不关我们的事&#xff0c;毕竟情面在这…还是无偿地协助一下&#xff09;&#xff0c;经过三四轮“谦让…

Java-SpringBoot启动报端口被占用,如何找到占用端口的进程并杀掉

背景 当我们本地启动多个项目&#xff0c;可能会出现端口被占用的情况&#xff0c;当然有时候可能idea窗口关闭&#xff0c;但是进程并没有kill掉&#xff0c;导致再次启动项目时也会报端口被占用的错误。 通常的做法是打开任务管理器&#xff0c;然后kill掉对应的进程。 首先…

“除了C盘都不见了“:现象解析、恢复策略与预防之道

现象概述&#xff1a;非系统盘突然消失之谜 在日常的计算机使用中&#xff0c;不少用户可能遭遇过一个令人措手不及的问题——“除了C盘都不见了”。这一现象发生时&#xff0c;用户惊讶地发现除了作为系统盘的C盘外&#xff0c;原本存放着各类文档、图片、视频等个人资料的D盘…

在一行中实现每个盒子间隔相等

达成效果&#xff1a; 1. 使用justify-content: space-evenly; <!DOCTYPE html> <html lang"zh-cn"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"…

Nginx Lua Waf 插件一键部署

文章目录 一、场景说明二、脚本职责三、参数说明四、操作示例五、注意事项 一、场景说明 本自动化脚本旨在为提高研发、测试、运维快速部署应用环境而编写。 脚本遵循拿来即用的原则快速完成 CentOS 系统各应用环境部署工作。 统一研发、测试、生产环境的部署模式、部署结构、…