STM32F103+ESP-01S+MQTT协议连接华为云端(附踩坑记录)
STM32F103+ESP-01S+MQTT协议连接华为云端(附踩坑记录)
一、物料准备
硬件:
- STM32F103C8T6最小核心板
- ESP01S WIFI模块
软件:
- Keil
- esp32固件烧写软件
- 华为云服务器(个人免费使用,每天消息上限)
二、调试过程
调试总体思路:
烧写官方的MQTT固件(这样可以省去协议的手动组装,直接调用AT指令+传参就行了);
单片机的硬线连接,做好usart配置;
wifi连接热点;
MQTT连接云服务器
ESP01S固件烧写
ESP01S的芯片其实也是8266
硬线+usart配置
首先是硬件接线+USART配置,我用的是STM32F103的USART2,USART的配置基本都是通用的,注意单片机的串口引脚和所在时钟就行了。如果要接入FreeRTOS的话,注意NVIC的配置的优先级分组和整体保持一致就行了(一般是第4组)NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4); // 4位抢占优先级,0位子优先级,代码如下:
// USART2_TX:PA2,USART2_RX:PA3,
void USART2_Config(int baud)
{
USART_InitTypeDef USART_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
GPIO_InitTypeDef GPIO_InitStructure;
/********* RCC配置 ******************/
// 打开USART2时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_AFIO, ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2,ENABLE);
/********* NVIC配置 **********************************/
NVIC_InitStructure.NVIC_IRQChannel = USART2_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 12; // 抢占优先级12(>11)
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; // 子优先级无效(分组4)
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
/********* 引脚配置 **********************************/
// 映射引脚和复用功能
//RX端口
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(GPIOA, &GPIO_InitStructure);
//TX端口
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_Init(GPIOA, &GPIO_InitStructure);
/********* 串口配置 ************************************/
USART_InitStructure.USART_BaudRate = baud;
USART_InitStructure.USART_WordLength = USART_WordLength_8b;
USART_InitStructure.USART_StopBits = USART_StopBits_1;
USART_InitStructure.USART_Parity = USART_Parity_No;
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
USART_Init(USART2, &USART_InitStructure);
// 配置中断
USART_ITConfig(USART2, USART_IT_RXNE, ENABLE);
// 打开usart
USART_Cmd(USART2, ENABLE);
}
/***************************************** usart中断服务 ********************************/
uint8_t esp8266_buf[ESP8266_BUF_SIZE];
uint16_t esp8266_cnt = 0;
//前台程序就是中断服务程序,该程序是不需要手动调用的,当中断触发之后CPU会自动跳转过来执行该函数
void USART2_IRQHandler(void)
{
//uint8_t data;
//判断中断是否发生
if (USART_GetITStatus(USART2, USART_IT_RXNE) == SET)
{
//从USART2中接收一个字节
esp8266_buf[esp8266_cnt++] = USART_ReceiveData(USART2); //一次只能接收一个字节
//data = USART_ReceiveData(USART2);
//把接收到的数据转发出去
//USART_SendData(USART1,data);
}
}
- wifi入网
esp-01s的波特率设置为115200,所以为了调试方便,USART1的波特率也调整为115200,同时xcom的波特率也调整为115200,然后按照以下示例即可发送成功测试
官方指令集参考:
https://docs.espressif.com/projects/esp-at/zh_CN/latest/esp32/AT_Command_Set/index.html
#define ESP8266_WIFI_INFO "AT+CWJAP=\"wifiusername\",\"wifipassword\"\r\n"
/**
* @brief 向ESP8266发送AT指令并检查响应
* @param cmd: 要发送的AT指令字符串
* @param resp: 期望的响应字符串
* @retval 0: 成功(接收到期望响应); 1: 失败
*/
uint8_t ESP8266_SendCmd(char *cmd, char *resp)
{
uint16_t timeout = 0;
// 清空接收缓冲区
ESP8266_Clear();
// 发送AT指令到ESP8266(通过USART2)
USART2_Send_Str(cmd);
// 等待响应,超时时间为5秒(500*10ms)
while(timeout++ < 500)
{
delay_ms(10); // 等待10ms
// 检查是否接收到期望的响应
if(strstr((char *)esp8266_buf, resp) != NULL)
{
USART1_Send_Str((char *)esp8266_buf);
return 0; // 成功
}
}
return 1;
}
//连接wifi
void ESP_Config(void)
{
ESP8266_Clear();
// while(ESP8266_SendCmd("AT\r\n", "OK"))
// delay_ms(50);
//1、复位
while(ESP8266_SendCmd("AT+RST\r\n", "OK"))
delay_ms(50);
//2、设置工作站STA模式
while(ESP8266_SendCmd("AT+CWMODE=1\r\n", "OK"))
delay_ms(50);
//3、连接wifi
while(ESP8266_SendCmd(ESP8266_WIFI_INFO, "GOT IP"))
delay_ms(50);
}
注意末尾都要加"\r\n",字符串该转义的转义
这里连接上了可以试着测试个天气应用的api
- MQTT连接
官方MQTT指令集(中文)非常详细:MQTT AT 命令集 - ESP32 - — ESP-AT 用户指南 latest 文档
完整的连接发布订阅代码:
void ESP8266_Clear(void)
{
memset(esp8266_buf, 0, sizeof(esp8266_buf));
esp8266_cnt = 0;
}
bool ESP8266_recv(char *substr)
{
// 检查是否接收到期望的响应
if(strstr((char *)esp8266_buf, substr) != NULL)
{
USART1_Send_Str("收到风扇控制命令");
USART1_Send_Str((char *)esp8266_buf);
ESP8266_Clear();
return true;
}
return false;
}
/**
* @brief 向ESP8266发送AT指令并检查响应
* @param cmd: 要发送的AT指令字符串
* @param resp: 期望的响应字符串
* @retval 0: 成功(接收到期望响应); 1: 失败
*/
uint8_t ESP8266_SendCmd(char *cmd, char *resp)
{
uint16_t timeout = 0;
// 清空接收缓冲区
ESP8266_Clear();
// 发送AT指令到ESP8266(通过USART2)
USART2_Send_Str(cmd);
// 等待响应,超时时间为5秒(500*10ms)
while(timeout++ < 500)
{
delay_ms(10); // 等待10ms
// 检查是否接收到期望的响应
if(strstr((char *)esp8266_buf, resp) != NULL)
{
USART1_Send_Str((char *)esp8266_buf);
return 0; // 成功
}
}
return 1;
}
void ESP_PUB(char *cmd)
{
ESP8266_SendCmd(cmd, "OK"); //判断OK是否是cmd的子串
}
//连接wifi、连接云端服务器
void ESP_Config(void)
{
ESP8266_Clear();
// while(ESP8266_SendCmd("AT\r\n", "OK"))
// delay_ms(50);
//1、复位
while(ESP8266_SendCmd("AT+RST\r\n", "OK"))
delay_ms(50);
//2、设置工作站STA模式
while(ESP8266_SendCmd("AT+CWMODE=1\r\n", "OK"))
delay_ms(50);
//3、连接wifi
while(ESP8266_SendCmd(ESP8266_WIFI_INFO, "GOT IP"))
delay_ms(50);
//4、配置用户参数
while(ESP8266_SendCmd("AT+MQTTUSERCFG=0,1,\"monitor_123\",\"monitor\",\"XXXXXXXXXXXXXXXXXX\",0,0,\"\"\r\n", "OK"))
delay_ms(50);
//5、连接云服务器
while(ESP8266_SendCmd("AT+MQTTCONN=0,\"XXXXXXXXXXXXXX",1883,0\r\n", "OK"))
delay_ms(50);
//6、订阅app控制风扇的主题
while(ESP8266_SendCmd("AT+MQTTSUB=0,\"control_fan\",1\r\n", "OK"))
delay_ms(50);
}
// 修改云端属性(暂未启用,只通过/mesasge/up进行分发)
void ESP_PUB_Properties(float temper, float humidity, bool isAlarming)
{
sprintf(
ts,
"AT+MQTTPUB=0,\"$oc/devices/monitor/sys/properties/report\",\"{\\\"services\\\":[{\\\"service_id\\\":\\\"Monitor\\\"\\,\\\"properties\\\":{\\\"temper\\\":10\\,\\\"humidity\\\":30\\,\\\"isAlarming\\\":true}}]}\",1,0\r\n"
);
ESP_PUB(ts);
}
// 发布消息
void ESP_PUB_Message(float temper, float humidity, bool isAlarming,bool isFanNormallyOpen)
{
memset(ts,0,300);
sprintf(
ts,
"AT+MQTTPUB=0,\"$oc/devices/monitor1/sys/messages/up\",\"{\\\"temper\\\":%.1f\\,\\\"humidity\\\":%.1f\\,\\\"isAlarming\\\":%s\\,\\\"isFanNormallyOpen\\\":%s}\",1,0\r\n",
temper,
humidity,
isAlarming?"true":"false",
isFanNormallyOpen?"true":"false"
);
ESP_PUB(ts);
}
注意这里组装消息的时候有个大坑,卡了我非常之久:
// 修改云端属性(暂未启用,只通过/mesasge/up进行分发)
void ESP_PUB_Properties(float temper, float humidity, bool isAlarming)
{
sprintf(
ts,
"AT+MQTTPUB=0,\"$oc/devices/monitor/sys/properties/report\",\"{\\\"services\\\":[{\\\"service_id\\\":\\\"Monitor\\\"\\,\\\"properties\\\":{\\\"temper\\\":10\\,\\\"humidity\\\":30\\,\\\"isAlarming\\\":true}}]}\",1,0\r\n"
);
ESP_PUB(ts);
}
假设要发布的JSON对象:
// 华为云属性更新的固定格式
{
"services":[
{
"service_id":"Monitor",
"properties":{
"temper":10,
"humidity":30
}
}
]
}
// 去除format
{"services":[{"service_id":"Monitor","properties":{"temper":10,"humidity":30}}]}
那跟据MQTT文档需要发送的指令则是
// 官方示例
AT+CWMODE=1
AT+CWJAP="ssid","password"
AT+MQTTUSERCFG=0,1,"ESP32","espressif","1234567890",0,0,""
AT+MQTTCONN=0,"192.168.10.234",1883,0
AT+MQTTPUB=0,"topic","\"{\"timestamp\":\"20201121085253\"}\"",0,0 // 发送此命令时,请注意特殊字符是否需要转义。
// 我们的指令进行第一次转义,仔细看,这里我们将","也进行了转义!!!
AT+MQTTPUB=0,"$oc/devices/monitor/sys/properties/report","{\"services\":[{\"service_id\":\"Monitor\"\,\"properties\":{\"temper\":10\,\"humidity\":30}}]}"
注意,这里我们不仅将数据中引号进行了转义,同时对","也进行了转义,这里转义","的原因是因为需要告诉ESP的固件程序,这个逗号是我数据中的逗号,而不是指令中的逗号!!!
指令有了,我们现在要通过sprintf进行数据的组装,所以要进行第二次转义,而这次的转义,是要将所有的"\"和引号进行转义,不用转义逗号,所以最终就变成了
"AT+MQTTPUB=0,\"$oc/devices/monitor/sys/properties/report\",\"{\\\"services\\\":[{\\\"service_id\\\":\\\"Monitor\\\"\\,\\\"properties\\\":{\\\"temper\\\":10\\,\\\"humidity\\\":30}}]}\""
这就是为什么要发布的数据中的逗号前有两个反斜杠
STM32F103+ESP-01S+MQTT协议连接华为云端(附踩坑记录)的更多相关文章
- jvisualvm连接远程应用终于成功,附踩大坑记录!!(二:jmx方式)
一.问题概述 参考前一篇: jvisualvm连接远程应用终于成功,附踩大坑记录!!(一:jstatd方式) 这篇主要讲讲jmx方式. 二.启动前设置jmx参数 我这边拿tomcat举例,其余java ...
- jvisualvm连接远程应用终于成功,附踩大坑记录!!(一:jstatd方式)
一.问题概述 连接远程java应用除了jstatd方式,还有jmx方式.不必拘泥于一种,一种不行可以果断尝试另一种,兴许就行了. 姊妹篇在这: jvisualvm连接远程应用终于成功,附踩大坑记录!! ...
- 踩坑记录:Redis的lettuce连接池不生效
踩坑记录:Redis的lettuce连接池不生效 一.lettuce客户端 lettuce客户端 Lettuce 和 Jedis 的都是连接Redis Server的客户端程序.Jedis在实现上是直 ...
- 华为云kafka POC 踩坑记录
2019/03/08 18:29 最近在进行华为云相关POC验证,个人主要负责华为云DMS kafka相关.大致数据流程是,从DIS取出数据,进行解析处理,然后放入kafka,再从kafka中取出数据 ...
- 踩坑记录-连接 MongoDB Compass Community 报错
在控制台输入 mongod 启动 mongodb服务,地址栏输入http://localhost:27017/ 能看到下图,表示服务启动成功. 打开"MongoDB Compass Comm ...
- Android实现推送方式解决方案 - 长连接+心跳机制(MQTT协议)
本文介绍在Android中实现推送方式的基础知识及相关解决方案.推送功能在手机开发中应用的场景是越来起来了,不说别的,就我们手机上的新闻客户端就时不j时的推送过来新的消息,很方便的阅读最新的新闻信息. ...
- 【物联网云端对接-3】通过MQTT协议与微软Azure IoT Hub进行云端通信
在上一篇文章<通过MQTT协议与阿里云物联网套件进行云端通信>中,我们介绍了通过MQTT对接阿里云的物联网套件.其实同样的代码,稍加调整也可以对接到微软Azure IoT hub上,不过需 ...
- MQTT 协议学习:007-Keep Alive 连接保活 与 对应报文(PINGREQ、PINGRESP)
背景 keep alive 是 CONNECT 报文中可变头的一部分. 我们提到过 Broker 需要知道 Client 是否非正常地断开了和它的连接,以发送遗愿消息.实际上 Client 也需要能够 ...
- 基于MQTT协议的云端proxy远程登陆
这篇文件是建立在一下两篇文章基础上完成的 很多重复的内容不会在这章提到 https://www.cnblogs.com/y-c-y/p/11685405.html telnet协议相关 https:/ ...
- 海鑫智圣:物联网漫谈之MQTT协议
什么是MQTT协议 MQTT(消息队列遥测传输协议)是IBM在1999年专门针对物联网等应用场景来制订的轻量级双向消息传输协议,它主要是为了解决物联网上使用到的设备的互相通信的问题,以及这些设备与后端 ...
随机推荐
- STM32在使用Clion平台开发时调试失败 SystemClock_Config 返回 HAL_ERROR
问题记录 在尝试使用Clion在STM32平台上开发调试时,需要通过OpenOCD结合ST-Link等调试器进行烧录和调试.但通过STM32CubeMX生成代码后,发现出现以下现象: 程序能够正常编译 ...
- Qt图像处理技术四:图像二值化
Qt图像处理技术四:图像二值化 github 如果你觉得有用的话,期待你的小星星 实战应用项目: github :https://github.com/dependon/simple-image-fi ...
- 洛谷 P5012 水の数列
洛谷 P5012 水の数列 Problem 给你一个长度为\(n(n\le10^6)\)的数列,有\(T(T\le 10^3)\)组询问,每一组询问查询区间\([l,r]\),请选择一个\(x\),将 ...
- 给定 (u,v),如何 O(1) 求 lca(u,v) 的孩子 u',v',且分别为 u,v 的祖先或本身
问题描述 一棵树,\(q\) 次询问给定 \(u,v\),保证 \(u,v\) 不为祖孙关系.设 \(p=\operatorname{lca}(u,v)\),求 \(p\) 的一个孩子 \(u'\) ...
- JavaWeb 中的 HTTP 基础知识
概念:超文本传输协议,规定了浏览器和服务器之间数据传输的规则. 特点: 基于TCP协议,面向连接,更安全 基于请求-响应模型,一次请求对应一次响应 是无状态的协议,对事务处理没有记忆能力.每次请求-响 ...
- helmfile使用
说明 使用helmfile时,我们首先得了解helm的使用,以及如何开发一个helm chart. helm是kubernetes的包管理工具.在实际的使用场景中我们涉及同时部署多个chart.区分不 ...
- go 进阶训练营 微服务可用性(中)笔记
过载保护 令牌桶算法 存放固定容量令牌的桶,按照固定速率往桶里添加令牌 https://pkg.go.dev/golang.org/x/time/rate 漏桶算法 作为计量工具(The Leaky ...
- .NET 10 支持Linux 的Shebang(Hashbang)
.NET 10 Preview 5 带来的C# 文件脚本化运行,在 Linux/Unix 系统中通过 #!/usr/bin/dotnet run 支持 Shebang(Hashbang) 的详细说明: ...
- 一文看懂——SimSolid的优势
在当今日益复杂的工程设计领域,大型装配体的分析变得愈发重要.装配体是由多个零件.部件或子装配体按照一定规则和技术要求组合而成的整体,其性能的好坏直接影响到整个产品的质量和可靠性. 然而,传统的有限元分 ...
- 【Playwright + Python】系列(十七)揭秘 Playwright 处理 Handles:开启高效自动化之门
哈喽,大家好,我是六哥!今天来跟大家聊一聊Playwright 处理 Handles的方法,面向对象为功能测试及零基础小白,这里我尽量用大白话的方式举例讲解,力求所有人都能看懂,建议大家先收藏,以免后 ...