基于EMQX+MQTT+ESP32+Openharmony的开发实例

EMQX介绍

EMQ X 是基于 Erlang/OTP 平台开发的 MQTT 消息服务器,是开源社区中最流行的 MQTT 消息服务器。EMQX 是开源百万级分布式 MQTT 消息服务器(MQTT Messaging Broker),用于支持各种接入标准 MQTT 协议的设备,实现从设备端到服务器端的消息传递,以及从服务器端到设备端的设备控制消息转发。从而实现物联网设备的数据采集,和对设备的操作和控制。

以下是EMQX的官方资料,这里为大家已经收集好了

官方文档: 产品概览 | EMQX 4.3 文档

安装包: https://packages.emqx.net/MQTTX/v1.11.1/MQTTX-Setup-1.11.1-ia32.exe

安装的客户端软件为MQTTX

配置MQTTX

将MQTXX安装到PC端后,需要对其进行一系列的配置:

修改客户端语言
新建MQTT服务器

点击左侧工具栏新建一个MQTT服务器

MQTT服务器配置

新建页中带*号的是必配项

  1. 这里的名称只是在MQTTX中用于区分不同mqtt连接的名字,可以任意命名
  2. 服务器地址是支持MQTT服务的服务器地址这里默认是EMQX官方提供的一个免费服务器
  3. mqtt服务的默认端口为1883,无需修改
  4. 点击右上角 connect即可连接上Broker
MQTT主题订阅

MQTT采用发布/订阅消息模型,在这个模型中

发布者将消息发送到Broker,Broker根据订阅者订阅的主题,将消息分发给相应的订阅者。

点击右侧消息框"添加订阅"即可实现主题订阅。

我们这里订阅主题为"wangziyvle"

订阅成功在右侧出现已订阅的主题

MQTT主题发布

修改右下角框出来的主题名为刚刚订阅的主题表示我们将下面文本框的内容发布到该主题中

配置成功的效果应该是实现了消息回显功能

为什么会消息回显?

实际上MQTT并不限制一个客户端只能是发布者或只能是订阅者 - 一个客户端可以订阅自己所发布的主题(如上所示)

这意味着同一个客户端可以在不同的上下文中既是信息的生产者也是消费者。这意味着同一个客户端可以在不同的上下文中既是信息的生产者也是消费者。

编程实现

虽然实现了串口回显,但上述的一切都只是在应用软件上的自娱自乐,我们该如何通过编程实现MQTT通信?

做过相关尝试的大家应该都知道,难度不在于MQTT客户端编程,最难的点在于移植MQTT库

虽然目前我也不会,但是通过神秘力量获得了一位大佬移植适配好的MQTT库

这里我们基于前面的LwIP连接TCP服务器的代码进行修改后成功实现了MQTT服务

mqtt_test.c
#include <stdio.h>
#include <unistd.h>
#include "ohos_init.h"
#include "cmsis_os2.h"

#include "lwip/ip_addr.h"
#include "lwip/netifapi.h"
#include "lwip/sockets.h"

#include "MQTTClient.h"

static MQTTClient mq_client;

unsigned char *onenet_mqtt_buf;
unsigned char *onenet_mqtt_readbuf;
int buf_size;

Network n;
MQTTPacket_connectData data = MQTTPacket_connectData_initializer;

// 消息回调函数
void mqtt_callback(MessageData *msg_data)
{
	size_t res_len = 0;
	uint8_t *response_buf = NULL;
	char topicname[45] = {"$crsp/"};

	printf("topic %.*s receive a message\r\n", msg_data->topicName->lenstring.len, msg_data->topicName->lenstring.data);

	printf("message is %.*s\r\n", msg_data->message->payloadlen, msg_data->message->payload);
}

int mqtt_connect(void)
{
	int rc = 0;

	NetworkInit(&n);

	NetworkConnect(&n, "44.232.241.40", 1883); /*Broker: broker.emqx.io TCP 端口:1883*/

	buf_size = 4096 + 1024;
	onenet_mqtt_buf = (unsigned char *)malloc(buf_size);
	onenet_mqtt_readbuf = (unsigned char *)malloc(buf_size);
	if (!(onenet_mqtt_buf && onenet_mqtt_readbuf))
	{
		printf("No memory for MQTT client buffer!");
		return -2;
	}

	MQTTClientInit(&mq_client, &n, 1000, onenet_mqtt_buf, buf_size, onenet_mqtt_readbuf, buf_size);

	MQTTStartTask(&mq_client);

	data.keepAliveInterval = 30;
	data.cleansession = 1;
	data.clientID.cstring = "ohos_hi3861";
	data.username.cstring = "123456";
	data.password.cstring = "222222";
	data.cleansession = 1;

	mq_client.defaultMessageHandler = mqtt_callback;

	// 连接服务器
	rc = MQTTConnect(&mq_client, &data);

	// 订阅消息,并设置回调函数
	MQTTSubscribe(&mq_client, "wangzilvle", 0, mqtt_callback);

	while (1)
	{
		MQTTMessage message;

		message.qos = QOS1;
		message.retained = 0;
		message.payload = (void *)"openharmony";
		message.payloadlen = strlen("openharmony");

		// 发送消息
		if (MQTTPublish(&mq_client, "wangzilvle", &message) < 0)
		{
			printf("MQTTPublish faild !\r\n");
		}
		sleep(5);
	}

	return 0;
}

void mqtt_test(void)
{
	mqtt_connect();
}
mqtt_test.h
/*
 * Copyright (c) 2020 HiSilicon (Shanghai) Technologies CO., LIMITED.
 * 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 __MQTT_TEST_H__
#define __MQTT_TEST_H__

void mqtt_test(void);

#endif /* __MQTT_TEST_H__ */
wifi_connect.c
/*
 * Copyright (c) 2022 Hunan OpenValley Digital Industry Development 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 <string.h>
#include <unistd.h>
#include "securec.h"
#include "cmsis_os2.h"
#include "ohos_init.h"
#include "wifi_device.h"
#include "lwip/ip4_addr.h"
#include "lwip/netif.h"
#include "lwip/netifapi.h"
#include "wifi_error_code.h"

#define DEF_TIMEOUT 15
#define ONE_SECOND 1
#define SELECT_WLAN_PORT "wlan0"
#define SELECT_WIFI_SECURITYTYPE WIFI_SEC_TYPE_OPEN
#define STD_TIMEZONE_OFFSET (+8)
#define OD_DELAY_100 100
#define OD_DELAY_200 200

static int g_staScanSuccess = 0;
static int g_ConnectSuccess = 0;
static int ssid_count = 0;
static WifiErrorCode wifi_error;
static WifiEvent g_wifiEventHandler = {0};
static int wifi_sta_init_state = 0;
int sock_fd;
int addr_length;
const int timeZone = 8;

static void WiFiInit(void);
static void WaitScanResult(void);
static int WaitConnectResult(void);
static void OnWifiScanStateChangedHandler(int state, int size);
static void OnWifiConnectionChangedHandler(int state, WifiLinkedInfo *info);
static void OnHotspotStaJoinHandler(StationInfo *info);
static void OnHotspotStateChangedHandler(int state);
static void OnHotspotStaLeaveHandler(StationInfo *info);

void DisableWIFI(void)
{
    DisableWifi();
}

static void OnHotspotStaJoinHandler(StationInfo *info)
{
    (void)info;
    printf("STA join AP\n");
    return;
}

static void OnHotspotStaLeaveHandler(StationInfo *info)
{
    (void)info;
    printf("HotspotStaLeave:info is null.\n");
    return;
}

static void OnHotspotStateChangedHandler(int state)
{
    printf("HotspotStateChanged:state is %d.\n", state);
    return;
}

static void WiFiInit(void)
{
    printf("<--Wifi Init-->\r\n");
    g_wifiEventHandler.OnWifiScanStateChanged = OnWifiScanStateChangedHandler;
    g_wifiEventHandler.OnWifiConnectionChanged = OnWifiConnectionChangedHandler;
    g_wifiEventHandler.OnHotspotStaJoin = OnHotspotStaJoinHandler;
    g_wifiEventHandler.OnHotspotStaLeave = OnHotspotStaLeaveHandler;
    g_wifiEventHandler.OnHotspotStateChanged = OnHotspotStateChangedHandler;
    wifi_error = RegisterWifiEvent(&g_wifiEventHandler);
    if (wifi_error != WIFI_SUCCESS) {
        printf("register wifi event fail!\r\n");
    } else {
        printf("register wifi event succeed!\r\n");
    }
}

static void OnWifiScanStateChangedHandler(int state, int size)
{
    (void)state;
    if (size > 0) {
        ssid_count = size;
        g_staScanSuccess = 1;
    }
    return;
}

static int result;
int WifiConnect(const char *ssid, const char *psk)
{
    WifiScanInfo *info = NULL;
    unsigned int size = WIFI_SCAN_HOTSPOT_LIMIT;
    static struct netif *g_lwip_netif = NULL;
    WifiDeviceConfig select_ap_config = {0};
    osDelay(OD_DELAY_200);
    printf("<--System Init-->\r\n");
    WiFiInit();
    if (EnableWifi() != WIFI_SUCCESS) {
        printf("EnableWifi failed, wifi_error = %d\n", wifi_error);
        return -1;
    }
    if (IsWifiActive() == 0) {
        printf("Wifi station is not actived.\n");
        return -1;
    }
    info = malloc(sizeof(WifiScanInfo) * WIFI_SCAN_HOTSPOT_LIMIT);
    if (info == NULL) {
        printf("faild to create wifiscanInfo.\n");
        return -1;
    }
    do {
        ssid_count = 0;
        g_staScanSuccess = 0;
        Scan();
        WaitScanResult();
        wifi_error = GetScanInfoList(info, &size);
    } while (g_staScanSuccess != 1);
    strcpy_s(select_ap_config.ssid, sizeof(select_ap_config.ssid), ssid);
    printf("[%s][%s] \r\n", select_ap_config.ssid, select_ap_config.preSharedKey);
    select_ap_config.securityType = SELECT_WIFI_SECURITYTYPE;
    if (AddDeviceConfig(&select_ap_config, &result) == WIFI_SUCCESS) {
        if (ConnectTo(result) == WIFI_SUCCESS && WaitConnectResult() == 1) {
            printf("WiFi connect succeed!\r\n");
            wifi_sta_init_state = 1;
        }
    }
    osDelay(OD_DELAY_100);
    return 0;
}

static int WaitConnectResult(void)
{
    int ConnectTimeout = DEF_TIMEOUT;
    while (ConnectTimeout > 0) {
        sleep(1);
        ConnectTimeout--;
        if (g_ConnectSuccess == 1) {
            printf("WaitConnectResult:wait success[%d]s\n", (DEF_TIMEOUT - ConnectTimeout));
            break;
        }
    }
    if (ConnectTimeout <= 0) {
        printf("WaitConnectResult:timeout!\n");
        return 0;
    }
    return 1;
}

static void OnWifiConnectionChangedHandler(int state, WifiLinkedInfo *info)
{
    (void)info;
    if (state > 0) {
        g_ConnectSuccess = 1;
        printf("callback function for wifi connect\r\n");
    } else {
        g_ConnectSuccess = 0;
        printf("connect wifi_error, please check password, state:%d, try connect again\r\n", state);
        esp_wifi_connect();
    }
    return;
}

static void WaitScanResult(void)
{
    int scanTimeout = DEF_TIMEOUT;
    while (scanTimeout > 0) {
        sleep(ONE_SECOND);
        scanTimeout--;
        if (g_staScanSuccess == 1) {
            printf("WaitScanResult:wait success[%d]s\n", (DEF_TIMEOUT - scanTimeout));
            break;
        }
    }
    if (scanTimeout <= 0) {
        printf("WaitScanResult:timeout!\n");
    }
}
wifi_example.c
/*
 * Copyright (c) 2022 Hunan OpenValley Digital Industry Development 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 "securec.h"
#include "cmsis_os2.h"
#include "ohos_run.h"
#include "lwip/sockets.h"
#include "lwip/ip_addr.h"
#include "wifi_device.h"

#define OPEN_WIFI_NAME "test"
#define SERVER_IP "192.168.8.140"
#define SERVER_PORT 8080
#define OD_DELAY_1000 1000
#define OD_DELAY_100 100
#define RECV_LEN 511
#define STACK_SIZE 4096
#define PRIORITY 25

osThreadId_t wifi_test_id = NULL;

void wifi_test(void)
{
    int sock = -1;
    struct sockaddr_in client_addr;
    char recv_data[512] = {0};
    int recv_data_len;
    WifiConnect(OPEN_WIFI_NAME);
    printf("start wifi_test test\r\n");

    mqtt_test();
}

static void wifi_test_example(void)
{
    osThreadAttr_t attr;
    attr.name = "wifi_test";
    attr.attr_bits = 0U;
    attr.cb_mem = NULL;
    attr.cb_size = 0U;
    attr.stack_mem = NULL;
    attr.stack_size = STACK_SIZE;
    attr.priority = PRIORITY;
    wifi_test_id = osThreadNew((osThreadFunc_t)wifi_test, NULL, &attr);
    if (wifi_test_id == NULL)
    {
        printf("Failed to create wifi_test thread!\n");
    }
}

OHOS_APP_RUN(wifi_test_example);
BUILD.gn
# Copyright (c) 2022 Hunan OpenValley Digital Industry Development 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("//kernel/liteos_m/liteos.gni")

module_name = get_path_info(rebase_path("."), "name")
kernel_module(module_name) {
    sources = [
        "wifi_example.c",
        "wifi_connect.c",
        "mqtt_test.c",

        "./mqtt-sdk/MQTTClient-C/src/liteOS/MQTTLiteOS.c",
        "./mqtt-sdk/MQTTClient-C/src/MQTTClient.c",

        "./mqtt-sdk/MQTTPacket/src/MQTTConnectClient.c",
        "./mqtt-sdk/MQTTPacket/src/MQTTConnectServer.c",
        "./mqtt-sdk/MQTTPacket/src/MQTTDeserializePublish.c",
        "./mqtt-sdk/MQTTPacket/src/MQTTFormat.c",
        "./mqtt-sdk/MQTTPacket/src/MQTTPacket.c",
        "./mqtt-sdk/MQTTPacket/src/MQTTSerializePublish.c",
        "./mqtt-sdk/MQTTPacket/src/MQTTSubscribeClient.c",
        "./mqtt-sdk/MQTTPacket/src/MQTTSubscribeServer.c",
        "./mqtt-sdk/MQTTPacket/src/MQTTUnsubscribeClient.c",
        "./mqtt-sdk/MQTTPacket/src/MQTTUnsubscribeServer.c",
    ]
    include_dirs = [
        "//foundation/communication/wifi_lite/interfaces/wifiservice",
        "//device/board/esp/esp32/liteos_m/hals/driver/wifi_lite",
        "./mqtt-sdk/MQTTPacket/src",
        "./mqtt-sdk/MQTTClient-C/src",
        "./mqtt-sdk/MQTTClient-C/src/liteOS",
        "//kernel/liteos_m/components/cmsis/2.0",
    ]
}

编译并烧录

在源码根目录下使用hb工具对写好的代码进行编译

选择mini级系统

同理 产品选择esp公司下的esp32

选择完毕后在源码根目录下执行hb build -f 进行编译

编译完成后会有如下界面,并且编译后的代码固件位于:out\esp32\esp32

使用烧录软件进行烧录:

点击start,等待烧录完成

实验现象

重启esp32开发板后可以在串口助手看到:

发布功能:ESP32每五秒向主题"wangziyvle"发布一次"openharmony"

订阅功能:通过MQTTX发送"hello openharmony!"至主题"wangziyvle"

串口助手会打印出接收到的数据

注意!!!单纯的复制代码跑是跑不通的!!!需要结合移植的MQTT库才能成功运行

完整的工程目录如下:

本项目代码已上传至gitee:ESP32_Oh: ESP32移植Openharmony****

相关推荐
国产化创客1 小时前
物联网网关Web服务器--Boa服务器移植与测试
物联网·国产化
绝迹的星1 小时前
我的世界-与门、或门、非门等基本门电路实现
嵌入式硬件
程序猿阿伟2 小时前
《AI与鸿蒙Next:建筑设计可视化的革新力量》
人工智能·华为·harmonyos
fcf19905013 小时前
重温STM32之环境安装
stm32·单片机·嵌入式硬件
程序猿阿伟4 小时前
《开源与合作:驱动鸿蒙Next系统中人工智能技术创新发展的双引擎》
人工智能·开源·harmonyos
程亦寻6 小时前
物联网与前沿技术融合分析
人工智能·物联网·区块链·量子计算
云山工作室6 小时前
基于单片机的直流电机控制系统(论文+源码)
stm32·单片机·嵌入式硬件·毕业设计·毕设
飞火流星020277 小时前
物联网在烟草行业的应用
物联网·iot·烟草行业的应用·烟䓍大数据·烟䓍云平台·物联网烟䓍融合应用
艾格北峰8 小时前
STM32 物联网智能家居 (五) 设备子系统之点亮LED灯
arm开发·stm32·单片机·嵌入式硬件·物联网·架构·智能家居