stm32连接EMQX上云之ESP01s
1、下载MQTT固件烧录到esp8266
下载链接:https://pan.baidu.com/s/1cpC7kKuWKyll242xaOq96A?pwd=gs2w
2、用到的库以及一些定义
-
将宏定义需要替换的WIFI命名和密码替换
-
配置设备的ID,用户名和密码
-
服务器的IP和端口
-
包头和包尾
#include "stm32f10x.h" // Device header
#include "ESP8266.h"
#include "Usart.h"
#include "Delay.h"
#include "OLED.h"
#include <string.h>
#include <stdio.h>
#include "LED.h"
#define ESP8266_WIFI_INFO "AT+CWJAP=\"WIFI名\",\"WIFI密码\"\r\n"
#define ESP8266_MQTT_USERINFO "AT+MQTTUSERCFG=0,1,\"用户ID\",\"用户名\",\"用户密码\",0,0,\"\"\r\n"
#define ESP8266_MQTT_INFO "AT+MQTTCONN=0,\"服务器IP\",1883,0\r\n"
#define HEAD "<HEAD>"
#define TAIL "<TAIL>"
uint8_t ESP8266_Buf[100];
uint16_t ESP8266_Cnt,ESP8266_CntPre;
3、ESP8266初始化
这一部分就是通过AT指令连接WIFI,连接到自己服务器搭建的EMQX.
*ESP866的初始化*/
void ESP8266_Init(void)
{
ESP8266_Clear();
UsartPrintf(USART_DEBUG, " 1.AT+RST\r\n");
OLED_Clear();
OLED_ShowString(1,1,"1.AT+RST...");
while(ESP8266_SendCmd("AT+RST\r\n","OK"))
Delay_ms(1000);
UsartPrintf(USART_DEBUG, "2. CWMODE\r\n");
OLED_ShowString(2,1,"2.CWMODE...");
while(ESP8266_SendCmd("AT+CWMODE=1\r\n", "OK"))
Delay_ms(1000);
UsartPrintf(USART_DEBUG, "3. AT+CWDHCP\r\n");
OLED_ShowString(3,1,"3.AT+CWDHCP...");
while(ESP8266_SendCmd("AT+CWDHCP=1,1\r\n", "OK"))
Delay_ms(200);
UsartPrintf(USART_DEBUG, "4. CWJAP\r\n");
OLED_ShowString(4,1,"4.CWJAP...");
while(ESP8266_SendCmd(ESP8266_WIFI_INFO, "GOT IP"))
Delay_ms(200);
UsartPrintf(USART_DEBUG, "Try Config MQTT\r\n");
OLED_Clear();
OLED_ShowString(1,1,"Try Config MQTT");
while(ESP8266_SendCmd(ESP8266_MQTT_USERINFO, "OK"))
Delay_ms(300);
UsartPrintf(USART_DEBUG, "Try Connect MQTT\r\n");
OLED_ShowString(2,2,"Try Connect MQTT...");
while(ESP8266_SendCmd(ESP8266_MQTT_INFO, "OK"))
Delay_ms(300);
UsartPrintf(USART_DEBUG, "ESP8266 Init OK\r\n");
OLED_Clear();
OLED_ShowString(1,1,"ESP8266 Init OK");
Delay_ms(200);
}
在这个函数中有一个频繁用到的函数,主要是用来判断发送AT指令后返回的值是否正确,如果返回值不是所期望的那样就会重新发送
/*向ESP8266发送指令校验*/
_Bool ESP8266_SendCmd(char *cmd,char *res)
{
uint8_t timeout = 200;
Usart_SendString(USART2,(uint8_t *)cmd,strlen((const char *)cmd));
// UsartPrintf(USART_DEBUG,"send success");
while(timeout--)
{//在这循环200次等待数据收发完毕
if(ESP8266_WaitReceive() == REV_OK)
{
if(strstr((const char *)ESP8266_Buf,res) != NULL)//用于检查收到的回应是否正确
{
// UsartPrintf(USART_DEBUG, "REV_OK\r\n");
ESP8266_Clear();
return 0;
}
}
Delay_ms(10);
// UsartPrintf(USART_DEBUG, "REV_Wait\r\n");
}
return 1;
}
清除缓冲区以及判断数据是否收发完毕的函数也给上
/*清空ESP8266缓冲区*/
void ESP8266_Clear(void)
{
memset(ESP8266_Buf, 0, sizeof(ESP8266_Buf));//将ESP8266Buf区的所有字节均设置为0
ESP8266_Cnt = 0;
ESP8266_CntPre = 0;
}
/*判断数据是否收发完毕*/
_Bool ESP8266_WaitReceive(void)
{
if(ESP8266_Cnt == 0)
return REV_WAIT;
if(ESP8266_Cnt == ESP8266_CntPre)
return REV_OK;
ESP8266_CntPre = ESP8266_Cnt;//每次调用此函数都会把Cnt的值赋给CntPre,一旦相等说明数据收发完毕
return REV_WAIT;
}
3、ESP8266订阅主题
通过简单的AT指令即可实现订阅主题,函数传参为订阅名称和通话质量
/*ESP8266通过mqtt订阅Topic*/
void ESP8266_Subscribe(char *topic,uint8_t Qos)
{
char Sub_Buf[30];
sprintf(Sub_Buf,"AT+MQTTSUB=0,\"%s\",%u\r\n",topic,Qos);
while(ESP8266_SendCmd(Sub_Buf,"OK"))
Delay_ms(100);
UsartPrintf(USART_DEBUG,"%s SUB OK\n",topic);
}
4、ESP8266发布消息
ESP8266通过AT指令发布消息,总共4个参数,第一个是要发送的主题名,另外三个是我需要用到的数据。(注:其实也可以将需要的数据存放到一个数组中,这样的话就只用定义一个指针就可以了)
/*ESP8266通过MQTT发布消息*/
void ESP8266_SendData(char *topic,uint8_t temp,uint8_t humi,uint8_t led)//
{
char Send_Buf[100];
sprintf(Send_Buf,"AT+MQTTPUB=0,\"%s\",\"Temp:%u humi:%u Led: %s\",0,0\r\n",topic,temp,humi,led==1?"ON ":"OFF");
while(ESP8266_SendCmd(Send_Buf, "OK"))
Delay_ms(300);
UsartPrintf(USART2,"AT+MQTTPUB=0,\"ESP8266\",\"1234\",0,0\r\n");
}
5、ESP8266接收订阅消息
ESP8266通过上方的订阅函数订阅了主题后,就会收到来自那个主题的消息。我采用的方法是我会将串口ESP8266接收到所有消息放到一个Buf区中,然后解析这个Buf区就可以得到我所需要的数据。解析的函数如下,我截取了包头包尾之间的我需要的数据,包头包尾,在前方直接宏定义了
/*ESP8266接受订阅消息*/
void ESP8266_RecData(char *Buf,int Size)
{
int HeadFound = 0;
int HeadIndex = -1;
int TailIndex = -1;
for(int i = 0;i < Size;i++)
{
if(strncmp(&Buf[i],HEAD,strlen(HEAD)) == 0)
{
HeadFound = 1;
HeadIndex = i;
i += strlen(HEAD) - 1;//跳过包头再遍历包尾
}
if(HeadFound && strncmp(&Buf[i],TAIL,strlen(TAIL)) == 0)
{
TailIndex = i;
break;//跳出for循环
}
}
if(HeadFound && TailIndex != -1)
{
int packetsize = TailIndex - HeadIndex - strlen(HEAD);
char packet[packetsize + 1];
strncpy(packet,&Buf[HeadIndex + strlen(HEAD)],packetsize);
packet[packetsize] = '\0';
// UsartPrintf(USART_DEBUG,packet);
ESP8266_Clear();
if(strstr(packet,"LED ON") != NULL)
{
LED_Control(LED_ON);
}
else if(strstr(packet,"LED OFF") != NULL)
{
LED_Control(LED_OFF);
}
}
else
{
// UsartPrintf(USART_DEBUG,"未找到完整字符串\n");
}
}
在这一部分用到了较多的String库中的函数,有兴趣可以了解一下。
6、串口中断函数
/*串口2的中断函数*/
void USART2_IRQHandler(void)
{
if(USART_GetITStatus(USART2,USART_IT_RXNE) == SET)
{
if(ESP8266_Cnt >= sizeof(ESP8266_Buf))
ESP8266_Cnt = 0;
ESP8266_Buf[ESP8266_Cnt++] = USART_ReceiveData(USART2);
USART_ClearFlag(USART2,USART_FLAG_RXNE);
}
}