使用stm32+esp8266-01s与电脑进行mqtt交互

注意:

本文以简单易理解易实现为主,仅实现最基本的交互通信功能,性能和稳定性暂无考虑。

需要材料:

硬件:stm32及下载线、esp8266-01s(wifi模块)

软件:emqx、keil

可选:wireshark,python

开始:

  1. 配置stm32工程

首先,我们需要一个stm32的基础工程,为了调试需要,我们需要两个usart串口分别与电脑和wifi模块进行通信。

打开stm32cube 需要配置的有RCC、SYS、USART、时钟、project manager几部分。

 

 

 

 

 

 

 配置好usart1和2之后,引脚如图,将wifi模块按照tx-rx;rx-tx;EN、3v3接3.3v;GND接GND;其余悬空接入单片机。

ESP-01实物图

至此,stm32基础工程和硬件配置完毕。

 2、配置emqx

 在官网下载windows版本的emqx,下载后解压,在解压的bin目录下打开cmd,输入

emqx  console即可。

 

至此emqx配置完毕,可以在 http://127.0.0.1:18083通过控制台查看mqtt。

控制台账号为admin        密码为public

3、初始化wifi模块-AT指令

至此,基本条件已经配置完毕,可以开始代码部分的编写了。

详细的AT指令可以在乐鑫官网查看AT 命令集 — ESP-AT 用户指南 文档 (espressif.com)

但我相信你绝对不愿意去看也不一定能理解官网的AT指令集,这里我按照必要的配置流程梳理一下我们会用到的AT指令。

AT指令作用
+++关闭透传
AT+RESTORE\r\n恢复出厂设置
AT\r\nAT查询
ATE0\r\n关闭回显
AT+CWMODE_CUR=1\r\n设置透传模式
AT+CWJAP_CUR="****","*********"\r\n连接wifi,后跟wifi的用户名和密码
AT+CIPSTART="TCP","xxx.xxx.x.x",1883\r\n配置连接的设备和端口号,注意这里的端口号是数字而不是字符串
AT+CIPMODE=1\r\n设置为AP模式
AT+CIPSEND\r\n开始发送数据

这些做完之后,wifi模块就配置好了,可以开始发送数据了。但是我们这个时候还不知道如何去发送数据,这个时候我们可以去查看mqtt的协议然后自己组包,但是学习mqtt协议也是很大的工作量;我们也可以使用mqtt的库,通过调用接口的方式来实现我们想要的功能,但我找不到库(xs);还有最后一种方法,我们可以通过空中数据来了解mqtt交互的时候传递了哪些数据。

我们可以使用python来实现一个简单的mqtt通信,这个也可以用来测试mqtt服务器是否配置完成。代码如下:

from paho.mqtt import client as mqtt_client
import random,time

url = "127.0.0.1"
port = 1883
topic = "my/test"

client_id = f'python-mqtt-{random.randint(0,100)}'

def mqtt_connect():
    def on_connect(client,userdata,flags,rc):
        if rc == 0:
            print("connect success")
        else:
            print("connect failed ",rc)
    client = mqtt_client.Client(client_id)
    client.on_connect = on_connect
    client.connect(url,port)
    return client

def publish(client):
    msg_count = 0
    while True:
        time.sleep(1)
        msg = f"message:{msg_count}"
        result = client.publish(topic,msg)
        if result[0] == 0:
            print("send msg{",msg,"}success")
        else:
            print("send msg{",msg,"}failed")
        msg_count+=1

def run():
    client = mqtt_connect()
    client.loop_start()
    publish(client)

if __name__ == "__main__":
    run()
from paho.mqtt import client as mqtt_client
import random,time

url = "127.0.0.1"
port = 1883
topic = "my_test"

client_id = f'python-mqtt-{random.randint(0,100)}'

def commect_mqtt():
    def on_connect(client,data,flags,rc):
        if rc == 0:
            print("connect success")
        else:
            print("connect failed ",rc)

    client = mqtt_client.Client(client_id)
    client.on_connect = on_connect
    client.connect(url,port)
    return client

def subscribe(client):
    def on_message(client,userdata,msg):
        print("receive",msg.payload.decode(),"from{",msg.topic,"}topic")
    client.subscribe(topic)
    client.on_message = on_message
    return

def run():
    client = commect_mqtt()
    subscribe(client)
    client.loop_forever()

if __name__ == "__main__":
    run()

上述分别为发布和订阅mqtt服务的python例程,那么我们要怎样模拟一次数据交互呢?我们可以先使用服务器中的虚拟客户端。

然后按照上图方式创建一个虚拟客户端,

先运行第二个python历程,向my_test发送报文,报文内容为123。这样就可以在python显示框中看到我们发送过来的123了。

再运行第一个,也就是发布的python例程,可以在服务器端看到发布的报文。

 已经有了通信过程,接下来就需要抓包了。下载并打开wireshark,

 在如图所示过滤器下进行抓包,然后重新运行我们的第一个python例程,并在服务器端点击发送,之后就可以点击红色按键暂停抓包了,我们来看看我们抓到的数据:

 connect command:连接请求

 cnnect ack:连接回应

 这张就是连接请求的报文。

再来运行第二个python例程,同样方式进行抓包:

 有一个connect command,这个就是连接请求,服务器会返回一个connect ack。

向后翻,有一个public message,这个就是发布消息。 

 点选进去后,点击红框这一行,下面对应的蓝色框内即为发布的数据,从上到下以此为连接、订阅、发布消息。

有了数据包之后,我们就可以尝试通过串口连接wifi模块直接进行连接发布尝试了。

4、mqtt包简介​​​​​​迈向物联网第一步——MQTT理论知识详解_哔哩哔哩_bilibili

虽然但是,我们还是先来大概认识一下mqtt数据包吧,通过wireshark抓的数据包我们可能看的出来这些报文的含义,再次稍微总结一下。

连接:10 1a 00 04 4d 51 54 54 04 02 00 3c 00 0e 70 79 74 68 6f 6e 2d 6d 71 74 74 2d 38 35

共28位,这个数据长度并非固定,以这29位为例进行举例,其中标红的均可不修改。

位数报文含义
110连接请求固定报头
21a十进制为26后续字节长度固定报头
3-800 04 4d 51 54 54ASCII为00 04 MQTT协议名可变报头
904协议级别可变报头
1002连接标志可变报头
11-1200 3c十进制是60保活时间可变报头
13-1400 0e十进制为14设备名长度负载
15-2870 79 74 68 6f 6e 2d 6d 71 74 74 2d 38 35

ASCII为

python-mqtt-85

设备名负载

发布:30 0c 00 07 6d 79 5f  74 65 73 74 31 32 33

位数报文含义
130发布请求固定报头
20c十进制为12后续字节长度固定报头
3-400 07十进制为7主题的长度可变报头
5-116d 79 5f  74 65 73 74ASCII为my/test主题名可变报头
12-1431 32 33ASCII为123发送的内容负载

订阅:82 0c 00 01 00 07 6d 79 5f 74 65 73 74 00

位数报文含义
182订阅请求固定报头
20c十进制为12后续字节长度固定报头
3-400 01msgid可变报头
5-600 07十进制为7主题长度可变报头
7-136d 79 5f  74 65 73 74ASCII为my/test主题名负载
1400质量要求Qos负载

心跳:c0 00  emmm,这个就先记住就好。

5、了解完报文之后,终于可以开始代码的编写了。

配置一些宏定义,上半部分为AT指令,下半部分为接收消息的buff。

#define START								"+++"								//退出透传模式
#define RESTORE							"AT+RESTORE\r\n"		//恢复出厂设置
#define AT									"AT\r\n"						//
#define	ATE0								"ATE0\r\n"					//关闭回显
#define CWMODE							"AT+CWMODE_CUR=1\r\n"		//STA模式
#define CWJAP								"AT+CWJAP_CUR=\"****\",\"*********\"\r\n"		//连接WIFI
#define CIPSTART						"AT+CIPSTART=\"TCP\",\"xxx.xxx.xx.xx\",1883\r\n"	//连接服务器
#define CIPMODE							"AT+CIPMODE=1\r\n"		//开启透传模式
#define CIPSEND							"AT+CIPSEND\r\n"			//开启发送模式

#define DATA_LEN						(50)
#define LOST_LEN						(200)
#define LOG_LEN							(50)
#define TIMEOUT							(1000)

我们先定义一个log函数,用来在串口一打印调试信息,我们将串口一连接在电脑上,可以在电脑上观察log来调试代码,这一段记得加头文件   #include "stdarg.h"

void log_printf(const char *format, ...)
{
	char buff[LOG_LEN] = {0};
	va_list pArgs ;
	
	va_start (pArgs, format) ;
	vsnprintf (buff, LOG_LEN, format, pArgs);
	va_end (pArgs) ;
	
	HAL_UART_Transmit(&huart1, (uint8_t *)buff, strlen(buff), HAL_MAX_DELAY);
	
	return;
}

通过AT指令初始化wifi模块

bool with_ok(uint8_t *buff, uint16_t len)
{
	if (len == 0) {
		log_printf("with_ok input len error\r\n");
	}
	
	int i = 0;
	for(i = 0; i < len - 1; i++) {
		if((*(buff+i) == 'O') && ((*(buff+i+1)) == 'K')) {
				return true;
		}
	}
	return false;
}

void esp_init(void)
{
	log_printf("esp_init begin\r\n");
	uint8_t recv[DATA_LEN] = {0};
	uint8_t lost[LOST_LEN] = {0};
	uint8_t retry = 0;

	for(retry = 3; retry; retry--) {
		memset(recv, 0, DATA_LEN);
		HAL_UART_Transmit(&huart2, (uint8_t *)START, sizeof(START)-1, HAL_MAX_DELAY);				//start   -1 is \0  need to retry
		HAL_UART_Receive(&huart2, recv, DATA_LEN, TIMEOUT);
		if ((recv[0] != '+') || (recv[1] != '+') || (recv[2] != '+')) {
			if (retry) {
				continue;
			} else {
				log_printf("esp_init START error\r\n");
				return;
			}
		} else {
			break;
		}
	}
	log_printf("esp_init START success\r\n");
	
	memset(recv, 0, DATA_LEN);
	HAL_UART_Transmit(&huart2, (uint8_t *)RESTORE, sizeof(RESTORE)-1, HAL_MAX_DELAY);				//RESTORE    -1 is \0  need lost buff
	HAL_UART_Receive(&huart2, recv, DATA_LEN, TIMEOUT);
	if (!with_ok(recv, DATA_LEN)) {
		log_printf("esp_init RESTORE error\r\n");
		return;
	} else {
		log_printf("esp_init RESTORE success\r\n");
	}

	memset(recv, 0, DATA_LEN);
	while(HAL_UART_Receive(&huart2, lost, LOST_LEN, TIMEOUT) == HAL_OK);
	HAL_UART_Transmit(&huart2, (uint8_t *)AT, sizeof(AT)-1, HAL_MAX_DELAY);				//AT    -1 is \0 
	HAL_UART_Receive(&huart2, recv, DATA_LEN, TIMEOUT);
	if (!with_ok(recv, DATA_LEN)) {
		log_printf("esp_init AT error\r\n");
		return;
	} else {
		log_printf("esp_init AT success\r\n");
	}
	
	memset(recv, 0, DATA_LEN);
	HAL_UART_Transmit(&huart2, (uint8_t *)ATE0, sizeof(ATE0)-1, HAL_MAX_DELAY);				//ATE0   -1 is \0 
	HAL_UART_Receive(&huart2, recv, DATA_LEN, TIMEOUT);
	if (!with_ok(recv, DATA_LEN)) {
		log_printf("esp_init ATE0 error\r\n");
		return;
	} else {
		log_printf("esp_init ATE0 success\r\n");
	}
	
	memset(recv, 0, DATA_LEN);
	HAL_UART_Transmit(&huart2, (uint8_t *)CWMODE, sizeof(CWMODE)-1, HAL_MAX_DELAY);				//CWMODE    -1 is \0 
	HAL_UART_Receive(&huart2, recv, DATA_LEN, TIMEOUT);
	if (!with_ok(recv, DATA_LEN)) {
		log_printf("esp_init CWMODE error\r\n");
		return;
	} else {
		log_printf("esp_init CWMODE success\r\n");
	}
	
	memset(recv, 0, DATA_LEN);
	HAL_UART_Transmit(&huart2, (uint8_t *)CWJAP, sizeof(CWJAP)-1, HAL_MAX_DELAY);				//CWJAP    -1 is \0 			need more timeout
	HAL_UART_Receive(&huart2, recv, DATA_LEN, 8 * TIMEOUT);
	if (!with_ok(recv, DATA_LEN)) {
		log_printf("esp_init CWJAP error\r\n");
		return;
	} else {
		log_printf("esp_init CWJAP success\r\n");
	}
	
	memset(recv, 0, DATA_LEN);
	HAL_UART_Transmit(&huart2, (uint8_t *)CIPSTART, sizeof(CIPSTART)-1, HAL_MAX_DELAY);				//CIPSTART    -1 is \0 
	HAL_UART_Receive(&huart2, recv, DATA_LEN, TIMEOUT);
	if (!with_ok(recv, DATA_LEN)) {
		log_printf("esp_init CIPSTART error\r\n");
		return;
	} else {
		log_printf("esp_init CIPSTART success\r\n");
	}
	
	memset(recv, 0, DATA_LEN);
	HAL_UART_Transmit(&huart2, (uint8_t *)CIPMODE, sizeof(CIPMODE)-1, HAL_MAX_DELAY);				//CIPMODE    -1 is \0 
	HAL_UART_Receive(&huart2, recv, DATA_LEN, TIMEOUT);
	if (!with_ok(recv, DATA_LEN)) {
		log_printf("esp_init CIPMODE error\r\n");
		return;
	} else {
		log_printf("esp_init CIPMODE success\r\n");
	}
	
	memset(recv, 0, DATA_LEN);
	HAL_UART_Transmit(&huart2, (uint8_t *)CIPSEND, sizeof(CIPSEND)-1, HAL_MAX_DELAY);				//CIPSEND    -1 is \0 
	HAL_UART_Receive(&huart2, recv, DATA_LEN, TIMEOUT);
	if (!with_ok(recv, DATA_LEN)) {
		log_printf("esp_init CIPSEND error\r\n");
		return;
	} else {
		log_printf("esp_init CIPSEND start\r\n");
	}
	
	return;
}

多次尝试发送+++,等待串口准备就绪,发送恢复出厂设置, 在这之后需要丢弃多余的串口信息,发送AT、ATE0、CWMODE、CWJAP ,此时会比较慢,继续发送CIPSTART 、CIPMODE、CIPSEND,完成wifi模块初始化。

接下来连接mqtt、发送心跳、发送消息。

void mqtt_init(void)
{
	uint8_t load[] = {0x10, 0x1a, 0x00, 0x04, 0x4d, 0x51, 0x54, 0x54, 0x04, 0x02, 0x00, 0x3c, 0x00, 0x0e, 0x70, 0x79, 0x74, 0x68, 0x6f, 0x6e, 0x2d, 0x6d, 0x71, 0x74, 0x74, 0x2d, 0x31, 0x39};
	uint8_t recv[4] = {0};
	int8_t retry = 5;
	while(retry--) {
		HAL_UART_Transmit(&huart2, load, sizeof(load), HAL_MAX_DELAY);
		HAL_UART_Receive(&huart2, recv, 4, TIMEOUT);
		if((recv[0] == 0x20) && (recv[1] == 0x02) && (recv[2] == 0x00) && (recv[3] == 0x00) ) {
			break;
		}
	}
	
	if(retry > 0) {
		log_printf("mqtt load success\r\n");
		mqtt_send_alive();
	} else {
		log_printf("mqtt load failed!\r\n");
	}
	
	return;
}
void mqtt_send_alive(void)
{
	uint8_t alive[] = {0xc0, 0x00};
	uint8_t recv[2] = {0};
	int8_t retry = 10;
	
	while(retry--) {
		HAL_UART_Transmit(&huart2, alive, sizeof(alive), HAL_MAX_DELAY);
		HAL_UART_Receive(&huart2, recv, 4, TIMEOUT);
				if((recv[0] == 0xD0) && (recv[1] == 0x00)) {
				break;
			}
	}
	if (retry < 0) {
		log_printf("mqtt disconnected\r\n");
	}
}


void mqtt_send_data(uint8_t *key, uint8_t *data, uint16_t len)
{
	uint8_t send[] = {0x30, 0x0e, 0x00, 0x09, 0x6d, 0x73, 0x7a, 0x79, 0x5f, 0x74, 0x65, 0x73, 0x74, 0x31, 0x32, 0x33};
	HAL_UART_Transmit(&huart2, send, sizeof(send), HAL_MAX_DELAY);
}

这里发送数据我是直接写死了,仅作调试使用,后续根据需要自由组包。

最后,我们在初始化的时候初始化wifi模块和mqtt,在循环里面发送心跳和数据进行测试,在服务器上观察是否接收到消息。

	esp_init();
	mqtt_init();
			mqtt_send_alive();
			mqtt_send_data(NULL,NULL,0);

至此,就算搞定了。

最后,本文仅为自己学习理解过程的呈现,若有疑误欢迎批评指正,若有建议欢迎互相交流。

  • 7
    点赞
  • 57
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
### 回答1: STM32使用ESP8266连接OneNET使用MQTT协议的步骤如下: 1. 首先,确保STM32ESP8266之间的硬件连接正确。将ESP8266的TX引脚连接到STM32的RX引脚,将ESP8266的RX引脚连接到STM32的TX引脚,并将GND引脚连接到共地。 2. 接下来,需要编写STM32的固件代码。首先,需要初始化串口通信接口,并将其配置为与ESP8266的通信接口相匹配。其次,需要配置STM32的GPIO引脚,将其用于控制ESP8266的工作模式切换(例如,将ESP8266切换到STA模式或AP模式)。然后,使用串口通信接口与ESP8266进行AT指令的交互,以设置ESP8266的连接参数和网络配置。最后,使用STM32MQTT库来实现与OneNET的连接和数据传输。 3. 在ESP8266上,需要使用AT指令配置ESP8266的网络连接和MQTT参数。可以使用AT+CWMODE指令将ESP8266切换到STA模式,并使用AT+CWJAP指令连接到WiFi网络。然后,使用AT+CIPSTART指令建立与OneNET的MQTT服务器的连接,并使用AT+CIPSEND指令发送MQTT消息。 4. 一旦STM32成功连接到OneNET的MQTT服务器,就可以使用STM32MQTT库来发送和接收数据。可以使用MQTT的发布(Publish)和订阅(Subscribe)功能,向OneNET发送数据或接收来自OneNET的数据。 总结来说,使用STM32连接到OneNET的MQTT服务器需要进行硬件连接和固件代码的编写,而ESP8266则需要使用AT指令配置网络连接和MQTT参数。之后,STM32可以使用MQTT库来实现与OneNET的数据传输。 ### 回答2: 要使用STM32ESP8266连接OneNet并使用MQTT协议,我们可以采取以下步骤: 1. 在STM32上配置串口通信:将STM32通过UART配置为与ESP8266进行串口通信的方式。设置合适的波特率、数据位、停止位和奇偶校验位等。 2. 连接ESP8266和STM32:根据ESP8266的硬件连接方式(一般为串口连接),将ESP8266的TX线连接到STM32的RX线,将ESP8266的RX线连接到STM32的TX线。还需将ESP8266的VCC和GND引脚分别连接到STM32的电源线和地线。 3. 配置ESP8266连接OneNet:ESP8266需要连接到OneNet,并使用MQTT协议进行通信。通过AT指令,配置ESP8266的WiFi连接,设置SSID和密码。然后使用AT+CIPSTART指令连接到OneNet的MQTT服务器。 4. 配置STM32发送和接收数据:在STM32上配置串口发送和接收功能,以便与ESP8266进行通信。使用UART发送指令字节流给ESP8266并接收ESP8266的响应。 5. 使用MQTT协议与OneNet通信:在STM32上通过串口向ESP8266发送MQTT协议指令,例如建立连接(CONNECT),订阅主题(SUBSCRIBE),发布消息(PUBLISH),取消订阅(UNSUBSCRIBE)等。根据需求进行合适的MQTT操作。 6. 处理OneNet的响应和数据:在STM32上解析和处理来自OneNet的响应和数据。根据MQTT协议,您可以接收和解析来自OneNet的订阅消息。 通过以上步骤,您就可以在STM32使用ESP8266连接OneNet并使用MQTT协议进行通信和数据传输。 ### 回答3: STM32是一种常用的微控制器,而ESP8266是一种常用的Wi-Fi模块。这两者可以结合使用,通过使用MQTT协议连接到OneNet平台。 首先,我们需要在STM32上配置USART或SPI接口与ESP8266通信。然后,我们可以使用AT指令集来与ESP8266进行通信。通过发送相应的AT指令,我们可以实现与ESP8266的Wi-Fi连接。这将使STM32具备互联网连接功能。 接下来,我们需要使用MQTT协议与OneNet平台进行连接。我们可以使用一个MQTT客户端库,例如MQTTFX或Paho,来处理MQTT通信。在STM32上,我们可以使用相应的库或手动实现MQTT协议来处理与OneNet的通信。 在连接OneNet之前,我们需要在OneNet平台上创建一个设备,并获取相应的设备ID和API密钥。这些信息将用于在我们的STM32代码中进行身份验证和连接。 一旦连接到OneNet,我们可以通过使用MQTT发布者/订阅者机制在设备和OneNet之间进行通信。我们可以发布传感器数据或接收来自OneNet的命令。这样,我们可以实现远程监控和控制功能。 在代码实现方面,我们需要处理与ESP8266的通信、MQTT协议的处理以及与OneNet的通信。我们可以使用适当的库和API来简化代码实现过程。 综上所述,通过将STM32ESP8266和OneNet相结合,使用MQTT协议进行通信,我们可以实现STM32与OneNet平台之间的连接和数据传输。这为物联网应用提供了一个便捷的方式,使得我们可以远程监控和控制STM32设备。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值