注意:

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

需要材料:

硬件: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;其余悬空接入单片机。

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

2、配置emqx

  • https://docs.emqx.net/broker/latest/cn/

在官网下载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\n AT查询
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,timeurl = "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_connectclient.connect(url,port)return clientdef publish(client):msg_count = 0while 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+=1def run():client = mqtt_connect()client.loop_start()publish(client)if __name__ == "__main__":run()
from paho.mqtt import client as mqtt_client
import random,timeurl = "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_connectclient.connect(url,port)return clientdef subscribe(client):def on_message(client,userdata,msg):print("receive",msg.payload.decode(),"from{",msg.topic,"}topic")client.subscribe(topic)client.on_message = on_messagereturndef 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位为例进行举例,其中标红的均可不修改。

位数 报文 含义
1 10 连接请求 固定报头
2 1a 十进制为26 后续字节长度 固定报头
3-8 00 04 4d 51 54 54 ASCII为00 04 MQTT 协议名 可变报头
9 04 协议级别 可变报头
10 02 连接标志 可变报头
11-12 00 3c 十进制是60 保活时间 可变报头
13-14 00 0e 十进制为14 设备名长度 负载
15-28 70 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

位数 报文 含义
1 30 发布请求 固定报头
2 0c 十进制为12 后续字节长度 固定报头
3-4 00 07 十进制为7 主题的长度 可变报头
5-11 6d 79 5f  74 65 73 74 ASCII为my/test 主题名 可变报头
12-14 31 32 33 ASCII为123 发送的内容 负载

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

位数 报文 含义
1 82 订阅请求 固定报头
2 0c 十进制为12 后续字节长度 固定报头
3-4 00 01 msgid 可变报头
5-6 00 07 十进制为7 主题长度 可变报头
7-13 6d 79 5f  74 65 73 74 ASCII为my/test 主题名 负载
14 00 质量要求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 retryHAL_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 buffHAL_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 timeoutHAL_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);

至此,就算搞定了。

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

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

  1. stm32 esp8266 ota升级-自建mqtt和文件服务器全量升级

    stm32 esp8266 ota系列文章: stm32 esp8266 ota-快速搭建web服务器之docker安装openresty stm32 esp8266 ota升级-tcp模拟http ...

  2. stm32 esp8266配网-smartConfig和BT串口方式配网

    stm32 esp8266 ota系列文章: stm32 esp8266 ota-快速搭建web服务器之docker安装openresty stm32 esp8266 ota升级-tcp模拟http ...

  3. 阿里云MQTT + STM32 + MQTT + ESP8266 01S WIFI 实现远程继电器控制开关和采集温湿度 登录阿里云网站,进入物联网云平台

    单片机型号: STM32F103C8T6 WIFI型号: ESP8266 01S WIFI 运行协议: TCP STM32运行MQTT协议 登录阿里云网站,进入物联网云平台 进入阿里云官网并登录账号后 ...

  4. STM32+ESP8266+MQTT微信小程序SoftAP一键配网接入腾讯物联网平台

    STM32+ESP8266+MQTT微信小程序SoftAP一键配网接入腾讯物联网平台   Wi-Fi 配网,指由外部向 Wi-Fi 设备提供 SSID 和密码(PSW),让 Wi-Fi 设备可以连接指 ...

  5. 【开源】手机APP通过IoT点亮LED灯(STM32+ESP8266+阿里云+MQTT+Android)

    (STM32+ESP8266+阿里云+MQTT+Android) 本项目利用Android Studio编写了连接阿里云和MQTT的APP,并在UI界面简单设计了标题,连接按钮,点灯按钮,灭灯按钮.如 ...

  6. STM32+ESP8266连接电脑Qt网络上位机——QT篇

    本文简单介绍下手写网络调试器并连接ESP8266模块 上篇:  STM32+ESP8266连接电脑Qt网络上位机--准备工作 目录 一.部分Qt代码及实现过程 二.实现过程--使用ESP8266连接上 ...

  7. STM32+ESP8266+MQTT协议连接腾讯物联网开发平台

    一.环境介绍 单片机采用:STM32F103C8T6 上网方式:采用ESP8266,也可以使用其他设备代替,只要支持TCP协议即可.比如:GSM模块.有线网卡等. 开发软件:keil5 物联网平台: ...

  8. STM32+ESP8266(ESP-12F)物联网温度计-移植paho MQTT协议连接阿里云

    STM32+ESP8266(ESP-12F)物联网温度计-移植paho MQTT协议连接阿里云 目录 STM32+ESP8266(ESP-12F)物联网温度计-移植paho MQTT协议连接阿里云 一 ...

  9. STM32+ESP8266+MQTT协议连接阿里云物联网平台

    一.环境介绍 单片机采用:STM32F103C8T6 上网方式:采用ESP8266,也可以使用其他设备代替,只要支持TCP协议即可.比如:GSM模块.有线网卡等. 开发软件:keil5 硬件连接功能: ...

最新文章

  1. 微服务网关Kong 1.0正式发布!提供100+项功能
  2. VS2010插件编写学习总结
  3. Py之pyecharts:python包之数据可视化包pyecharts简介、安装、使用方法之详细攻略
  4. 手机app常见bug积累
  5. C++中如何判断文件是否存在
  6. C++中的cin cout
  7. 【UAV】串级 PID 控制原理及应用
  8. 解决Ubuntu下载缓慢问题
  9. 阿里国际站出口通升级金品诚企详细流程
  10. OpenCV threshold函数详解
  11. 集成学习方法及思想总结
  12. keba驱动器_KEBA控制器说明书
  13. 自动化爬虫selenium基础教程
  14. 英语语法---四种句子类型的介绍
  15. C语言学习笔记—链表(四)链表的删除
  16. 安兔兔 android 4.4,适配智能电视 安兔兔评测V4.4.3发布
  17. 文献阅读-Clinical and Biological subtypes of B-cell lymphoma revealed by microenvironment signature
  18. 独领风骚,卡兹特投影仪大热香港电子展
  19. 掌机发展简史及未来趋势分析
  20. i.MX6ULL开发板【终结者】硬件资源说明

热门文章

  1. 前端框架vue3的node安装及项目构建的4种方法
  2. php 支付宝实名认证
  3. uni-app项目配置UrlSchemes在外部打开APP
  4. openssl 1.0.2k-fips 升级到 openssl-3.0.3
  5. 微信聊天自动解析html文本,微信小程序纯文本实现@功能
  6. 多时点DID实证流程笔记(Aggregate Effects from Public Works: Evidence from India)
  7. 转java通过身份证号码获取出生日期、性别、年龄
  8. 从零搭建Spring Boot脚手架:手写Mybatis通用Mapper4
  9. 从零搭建Spring Boot脚手架:增加通用的功能2
  10. Holt Winter时间序列模型