STM32F103C8T6+ESP-01S+MQTT server realizes data upload and reception (1)
- foreword
- Handwritten simple library functions
-
- constant definition
- global variable declaration
- Serial port initialization
- Disable AT command echo
- Set the working mode of esp8266
- Network distribution in a mechanical way (not recommended)
- Intelligent distribution network (recommended method)
- Configure the client ID, user name, and password of MQTT
- Connect to the MQTT server
- subscribe topic
- post topic
- AT command execution feedback detection
- Determine whether it is a topic message subscribed by MQTT
- clear flag
- MQTT initialization
Article Directory
STM32F103C8T6+ESP-01S+MQTT server realizes data upload and reception (2)
foreword
ESP-01S firmware burning
ESP-01S recommends using Anxinke official website firmware 1471, and the burning configuration is as shown in the figure below.
Main AT commands
Please replace the content inside <>
AT+CWMODE=1
#设置模组进入STA模式
AT+CWJAP="<ssid>","<password>"
#连接wifi
AT+MQTTUSERCFG=0,1,"<Client ID>","<账号>","<密码>",0,0,""
#设置MQTT连接所需要的的参数,包括用户ID(不为空)、账号(admin)以及密码(public)
AT+MQTTCONN=0,"<服务器地址>",<端口号>,<是否重连标志>
#连接mqtt服务器,设置服务器地址,端口号和是否重连标志(0或1)
AT+MQTTPUB=0,"<topic名>","<topic内容>",<QOS>,<retain>
#发布一条topic
AT+MQTTSUB=0,"<topic名>",<QOS>
#订阅一条topic
Handwritten simple library functions
constant definition
enum ON_OFF{
OFF,
ON
};
enum Qos{
MAX_ONE,
MIN_ONE,
ONLY_ONE
};
enum BackFlag{
UNKNOW,
SUCC,
FAULT
};
enum ESP_MODE{
STA=1,
AP,
AP_STA
};
enum ATBACK_KEYWORDS{
NO,
WIFI_GOT,
};
#define ERROR "\r\nERROR\r\n"
#define OK "\r\nOK\r\n"
#define FAIL "\r\nFAIL\r\n"
#define WIFI_GOT_IP "WIFI GOT IP\r\n"
#define WIFI_CONNECTED "WIFI CONNECTED\r\n"
global variable declaration
enum BackFlag back_flag=UNKNOW; //串口发送AT指令反馈标志 UNKNOW:未发送AT指令或者还未反馈、SUCC:8266反馈OK、FAULT:8266反馈ERROR
char USART_ReceiveString[300]; //串口接收到的数据
enum ATBACK_KEYWORDS ATBack_KeyWords; //用于判断WIFI连接状态
Serial port initialization
Use PA9 and PA10 to connect the RXD and TXD pins of ESP8266 respectively.
void usart_init(uint32_t bound) //115200
{
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1|RCC_APB2Periph_GPIOA|RCC_APB2Periph_AFIO, ENABLE);
//USART1_TX PA.9
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
GPIO_Init(GPIOA, &GPIO_InitStructure);
NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
USART_InitStructure.USART_BaudRate = bound;
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(USART1, &USART_InitStructure);
USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);
USART_ITConfig(USART1, USART_IT_IDLE, ENABLE);
USART_Cmd(USART1, ENABLE);
}
Disable AT command echo
Since the AT commands sent every time will be sent back by the 8266 through the serial port, in order to relieve the pressure on the serial port and better detect the OK or ERROR feedback from the 8266, it is recommended to turn off the AT command echo. The call of the function has no effect, no test is carried out, and it is only for theoretical explanation. (In order to ensure the correct initialization of 8266, you need to use clearBackFlag() to clear the feedback flag of 8266 before and after sending the AT command, and the subsequent code is the same)
void AT_Init(){
clearBackFlag();
printf("ATE0\r\n");
while(back_flag!=SUCC){
if(back_flag==FAULT){
printf("ATE0\r\n");
}
}
clearBackFlag();
}
Set the working mode of esp8266
Set to STA mode, 8266 as a device to connect to other AP hotspots or WIFI.
void Esp_Mode(enum ESP_MODE mode){
clearBackFlag();
printf("AT+CWMODE=%d\r\n",mode);//sta mode
while(back_flag!=SUCC){
if(back_flag==FAULT){
printf("AT+CWMODE=%d\r\n",mode);
}
}
clearBackFlag();
}
Network distribution in a mechanical way (not recommended)
After the system is powered on, it will wait for 5 seconds. If it is already connected to wifi, it does not need to configure the network, otherwise use a fixed wifi password to connect to a fixed wifi
void WIFI_Connect(char ssid[],char wifi_password[]){
delay_s(5);
if(ATBack_KeyWords!=WIFI_GOT){
Esp_Mode(STA);
clearBackFlag();
printf("AT+CWJAP=\"%s\",\"%s\"\r\n",ssid,wifi_password);
while(back_flag!=SUCC){
if(back_flag==FAULT){
printf("AT+CWJAP=\"%s\",\"%s\"\r\n",ssid,wifi_password);
}
}
clearBackFlag();
}else{
clearATBackKeyWords();
}
}
Intelligent distribution network (recommended method)
Use WIFI_Connect_Wechat() to call the first three functions for smart distribution.
After the system is powered on, it will wait for 5 seconds. If it is connected to wifi, it does not need to configure the network, otherwise it will start the intelligent network configuration. It can be ended early after success, and the led goes out to indicate the end of the distribution network.
void SmartConfig_ON(){
//开始智能配网
clearBackFlag();
printf("AT+CWSTARTSMART=3\r\n");
while(back_flag!=SUCC){
if(back_flag==FAULT){
printf("AT+CWSTARTSMART=3\r\n");
}
}
clearBackFlag();
}
uint16_t SmartConfig(){
//微信配网过程中 最长持续60s
uint16_t wifi_delay_i=0;
while(ATBack_KeyWords!=WIFI_GOT&&wifi_delay_i<20){
delay_s(3);
wifi_delay_i+=1;
}
return wifi_delay_i<10;
}
void SmartConfig_OFF(){
//停止微信配网
clearBackFlag();
printf("AT+CWSTOPSMART\r\n");
while(back_flag!=SUCC){
if(back_flag==FAULT){
printf("AT+CWSTOPSMART\r\n");
}
}
clearBackFlag();
}
void WIFI_Connect_Wechat(){
delay_s(5);
if(ATBack_KeyWords!=WIFI_GOT){
//如果没有自动连上wifi
Esp_Mode(STA);
SmartConfig_ON();
led_on();
SmartConfig();
led_off();
SmartConfig_OFF();
}else{
clearATBackKeyWords();
}
}
Configure the client ID, user name, and password of MQTT
It should be noted that the Client ID cannot be repeated. It is recommended to set the Client ID with the timestamp.
void Mqtt_UserCFG(char *client_id,char *username,char *password){
clearBackFlag();
printf("AT+MQTTUSERCFG=0,1,\"%s\",\"%s\",\"%s\",0,0,\"\"\r\n",client_id,username,password);
while(back_flag!=SUCC){
if(back_flag==FAULT){
printf("AT+MQTTUSERCFG=0,1,\"%s\",\"%s\",\"%s\",0,0,\"\"\r\n",client_id,username,password);
}
}
clearBackFlag();
}
Connect to the MQTT server
Connect to the MQTT server through ip and port number.
void Mqtt_Connect(char *ip,char* port,enum ON_OFF reconnect){
clearBackFlag();
printf("AT+MQTTCONN=0,\"%s\",%s,%d\r\n",ip,port,reconnect);
while(back_flag!=SUCC){
if(back_flag==FAULT){
printf("AT+MQTTCONN=0,\"%s\",%s,%d\r\n",ip,port,reconnect);
}
}
clearBackFlag();
}
subscribe topic
void Mqtt_SubTopic(char *Topic_Name,enum Qos qos){
clearBackFlag();
printf("AT+MQTTSUB=0,\"%s\",%d\r\n",Topic_Name,qos);
while(back_flag!=SUCC){
if(back_flag==FAULT){
printf("AT+MQTTSUB=0,\"%s\",%d\r\n",Topic_Name,qos);
}
}
clearBackFlag();
}
post topic
Two functions for posting topics, the difference is that the first one has 8266 feedback judgment, if the feedback ERROR will continue to send; the second one has no feedback judgment.
void Mqtt_PubTopic(char *Topic_Name,char* data,enum Qos qos,enum ON_OFF retain){
clearBackFlag();
printf("AT+MQTTPUB=0,\"%s\",\"%s\",%d,%d\r\n",Topic_Name,data,qos,retain);
while(back_flag!=SUCC){
if(back_flag==FAULT){
printf("AT+MQTTPUB=0,\"%s\",\"%s\",%d,%d\r\n",Topic_Name,data,qos,retain);
}
}
clearBackFlag();
}
void Mqtt_PubTopic_UsartIT(char *Topic_Name,char* data,enum Qos qos,enum ON_OFF retain){
printf("AT+MQTTPUB=0,\"%s\",\"%s\",%d,%d\r\n",Topic_Name,data,qos,retain);
}
AT command execution feedback detection
It is used to detect the result returned after the AT command is executed.
uint16_t checkOK(char* res_txt,char*match_txt){
uint16_t res_len,mat_len,flag=1;
res_len=strlen(res_txt);
mat_len=strlen(match_txt);
for(int i=res_len-mat_len,j=0;i<res_len&&j<mat_len;i++,j++){
if(res_txt[i]!=match_txt[j]){
flag=0;
break;
}
}
return flag;
}
Determine whether it is a topic message subscribed by MQTT
When the serial port receives data, the current function can be used to detect whether it is an mqtt message. Serial port data can be classified and processed.
uint16_t info_type(char USART_ReceiveString[]){
//1为mqtt消息 0为普通消息
uint16_t i,flag=1;
char head[13]="+MQTTSUBRECV"; //12+1 +MQTTSUBRECV+'\0'
for(i=0;USART_ReceiveString[i]!='\0'&&i<12;i++){
if(USART_ReceiveString[i]!=head[i]){
flag=0;
break;
}
}
return flag;
}
clear flag
It is mainly used to assist other functions before and after sending AT commands.
void clearBackFlag(){
back_flag=UNKNOW;
}
void clearATBackKeyWords(){
ATBack_KeyWords=NO;
}
MQTT initialization
This function integrates the MQTT initialization function. Subscribe to the test/stm32_server topic message, and finally publish a test/stm32_client topic with the content {"type":"init", "msg":"success"}, indicating that the initialization is complete.
void mqtt_init(char *ip,char* port,char *username,char *password,char *client_id){
delay_s(1);
AT_Init();
WIFI_Connect_Wechat();
Mqtt_UserCFG(client_id,username,password);
Mqtt_Connect(ip,port,ON);
Mqtt_SubTopic("test/stm32_server",MAX_ONE);
Mqtt_PubTopic("test/stm32_client","{\\\"type\\\":\\\"init\\\"\\,\\\"msg\\\":\\\"success\\\"}",ONLY_ONE,OFF);
}