使用STM32F103C8T6 + ESP-01S实现MQTT服务器的数据上传与接收(第一部分)

STM32F103C8T6+ESP-01S+MQTT服务器实现数据的上传和接收(一)

  • 前言
  • ESP-01S固件烧录
  • 主要AT指令
  • 手写简单的库函数
  • 常量定义
  • 全局变量声明
  • 串口初始化
  • 关闭AT指令回显
  • 设置esp8266的工作模式
  • 死板方式配网(不推荐)
  • 智能配网(推荐方式)
  • 配置MQTT的client ID、用户名、密码
  • 连接MQTT服务器
  • 订阅主题
  • 发布主题
  • AT指令执行反馈检测
  • 判断是否是MQTT订阅的话题消息
  • 清除标志位
  • MQTT初始化

  • 文章目录

    STM32F103C8T6+ESP-01S+MQTT服务器实现数据的上传和接收(二)

    前言

    ESP-01S固件烧录

    ESP-01S推荐使用安信可官网1471号固件,烧录配置如下图。Alt

    主要AT指令

    请替换其中<>里面的内容

    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
    

    手写简单的库函数

    常量定义

    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"
    

    全局变量声明

    enum BackFlag back_flag=UNKNOW;         //串口发送AT指令反馈标志  UNKNOW:未发送AT指令或者还未反馈、SUCC:8266反馈OK、FAULT:8266反馈ERROR
    char USART_ReceiveString[300];         //串口接收到的数据
    enum ATBACK_KEYWORDS ATBack_KeyWords;  //用于判断WIFI连接状态
    

    串口初始化

    使用PA9、PA10分别连接ESP8266的RXD、TXD引脚。

    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);                   
    
    }
    

    关闭AT指令回显

    由于每次发送的AT指令都会被8266通过串口再发送回来,为了缓解串口的压力,并且更好的检测到8266反馈的OK或者ERROR,建议关掉AT指令回显,不关掉理论上对库函数的调用不造成影响,没有进行测试,只作理论上说明。(为保证初始化8266正确进行,发送AT指令前后需要使用clearBackFlag()清除8266的反馈标志位,后续代码同理)

    void AT_Init(){
    	clearBackFlag();    
    	printf("ATE0\r\n");
    	while(back_flag!=SUCC){
    		if(back_flag==FAULT){
    			printf("ATE0\r\n");
    		}
    	}
    	clearBackFlag();
    }
    

    设置esp8266的工作模式

    设置为STA模式,8266作为设备连接其他AP热点或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();
    }
    

    死板方式配网(不推荐)

    系统上电后会等待5s,如果已经连上wifi就不需要进行配网,否则使用固定的wifi密码连接固定的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();
    	}
    }
    

    智能配网(推荐方式)

    通过使用WIFI_Connect_Wechat()调用前面三个函数进行智能配网。
    系统上电后会等待5s,如果连上了wifi就不需要进行配网,否则开始进行智能配网,当stm32f103c8t6板载led亮起后,开始进入最长1分钟的智能配网时间,配网成功后可提前结束,led熄灭表示配网结束。

    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();
    	}
    }
    

    配置MQTT的client ID、用户名、密码

    需要注意Client ID不能重复,建议配合时间戳设置Client ID。

    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();
    }
    

    连接MQTT服务器

    通过ip、端口号连接MQTT服务器。

    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();
    }
    

    订阅主题

    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();
    }
    

    发布主题

    两种用于发布主题的函数,区别是第一种带有8266的反馈判断,如果反馈ERROR会继续发送;第二种没有反馈判断。

    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指令执行反馈检测

    用于检测AT指令执行后返回结果。

    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;
    }
    

    判断是否是MQTT订阅的话题消息

    当串口接收到数据后,可用当前函数来检测是否是mqtt消息。可对串口数据进行分类处理。

    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;
    }
    

    清除标志位

    主要用于发送AT指令前后,辅助其他函数。

    void clearBackFlag(){
    	back_flag=UNKNOW;
    }
    void clearATBackKeyWords(){
    	ATBack_KeyWords=NO;
    }
    

    MQTT初始化

    该函数整合了MQTT初始化函数。订阅test/stm32_server主题的消息,最后会发布一条test/stm32_client话题,内容为{“type”:“init”,“msg”:“success”},表示初始化完成。

    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);
    }
    
    物联沃分享整理
    物联沃-IOTWORD物联网 » 使用STM32F103C8T6 + ESP-01S实现MQTT服务器的数据上传与接收(第一部分)

    发表评论