STM32 | MQTT+esp8266(第十四天)
点击上方"蓝字"关注我们
01、MQTT
>>>知 识 点:
1.esp8266 wifi的AT指令的使用
2.mqtt的连接、发布、订阅等
3.添加了cJSON
*说 明:
1.通过阿里云物联网平台能够获取开发板的D1、D2、D3灯和温湿度值
2.通过阿里云物联网平台能够控制开发板的D1、D2、D3灯的亮灭
3.D4灯用于表示当前连接服务器的状态。亮-已连接;灭-已断开
4.按键1用于重连阿里云物联网平台
5.按键2用于断开阿里云物联网平台
增加了cJSON对阿里云物联网平台的数据解析
ESP8266引脚.jpg
MQTT记录.txt
{
"ProductKey": "gqeaE1iKozV",
"DeviceName": "TESTDEVICE01",
"DeviceSecret": "df5a9a5c4a0b6c22e3acdf5e091a7ea7"
}
Broker Address:gqeaE1iKozV.iot-as-mqtt.cn-shanghai.aliyuncs.com
Broker Port :1883
Client ID :00001|securemode=3,signmethod=hmacsha1|
User Name:TESTDEVICE01&gqeaE1iKozV
password:C8F156AB9166B4C1000419F693A2095E3E23E3A2
属性上报:/sys/gqeaE1iKozV/${deviceName}/thing/event/property/post
属性设置:/sys/gqeaE1iKozV/${deviceName}/thing/service/property/set
//根据自己的设备名,填入属性信息即可
属性上报:/sys/gqeaE1iKozV/TESTDEVICE01/thing/event/property/post
属性设置:/sys/gqeaE1iKozV/TESTDEVICE01/thing/service/property/set
{
"method":"thing.service.property.set",
"id":"00001",
"params":{
"CurrentTemperature":20.0,
"CurrentHumidity":60.0,
"switch_led_r":1,
"switch_led_g":1,
"switch_led_b":0,
},
"version":"1.0.0"
}
阿里网络地址.txt
https://iot.console.aliyun.com/product
02、led.h
#ifndef __LED_H__#define __LED_H__#define CONNECT_MQTT_LED(x) PEout(14)=(x)?0:1extern void led_init(void);#endif
03、led.c
#include "stm32f4xx.h"#include "sys.h"void led_init(void){GPIO_InitTypeDef GPIO_InitStructure;//打开端口E的硬件时钟,就是对端口E供电RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOE,ENABLE);//打开端口F的硬件时钟,就是对端口F供电RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOF,ENABLE);//配置GPIOF的第9 10根GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9|GPIO_Pin_10;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;GPIO_InitStructure.GPIO_Speed = GPIO_High_Speed;GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;//初始化GPIO_Init(GPIOF,&GPIO_InitStructure);//配置GPIOE的第13 14根GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13|GPIO_Pin_14;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;GPIO_InitStructure.GPIO_Speed = GPIO_High_Speed;GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;//初始化GPIO_Init(GPIOE,&GPIO_InitStructure);PFout(9)=PFout(10)=1;PEout(13)=PEout(14)=1;}
04、key.h
#ifndef __KEY_H__#define __KEY_H__extern void key_init(void);extern uint32_t key_sta_get(void);#endif
05、key.c
#include "stm32f4xx.h"#include "sys.h"void key_init(void){GPIO_InitTypeDef GPIO_InitStructure;//打开端口A的硬件时钟,就是对端口A供电RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA,ENABLE);//打开端口E的硬件时钟,就是对端口E供电RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOE,ENABLE);//配置GPIOA的第0根引脚GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN;GPIO_InitStructure.GPIO_Speed = GPIO_High_Speed;GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;//初始化GPIO_Init(GPIOA,&GPIO_InitStructure);//配置GPIOE的第2 3 4根引脚GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2|GPIO_Pin_3|GPIO_Pin_4;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN;GPIO_InitStructure.GPIO_Speed = GPIO_High_Speed;GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;//初始化GPIO_Init(GPIOE,&GPIO_InitStructure);}uint32_t key_sta_get(void){uint32_t key_sta=0;if(PAin(0) == 0)key_sta|=1<<0;if(PEin(2) == 0)key_sta|=1<<1;if(PEin(3) == 0)key_sta|=1<<2;if(PEin(4) == 0)key_sta|=1<<3;return key_sta;}
06、esp8266.h
#ifndef __ESP8266_H__#define __ESP8266_H__#include "stm32f4xx.h"#define EN_DEBUG_ESP8266 0//添加WIFI热点宏定义,此处根据自己的wifi作调整//可以使用手机的热点#define WIFI_SSID "Phonewifi"#define WIFI_PASSWORD "12345678"//#define WIFI_SSID "AASD"//#define WIFI_PASSWORD "12345678"extern uint8_t g_esp8266_tx_buf[512];extern volatile uint8_t g_esp8266_rx_buf[512];extern volatile uint32_t g_esp8266_rx_cnt;extern volatile uint32_t g_esp8266_rx_end;extern volatile uint32_t g_esp8266_transparent_transmission_sta;extern void esp8266_init(void);extern int32_t esp8266_self_test(void);extern int32_t esp8266_exit_transparent_transmission (void);extern int32_t esp8266_entry_transparent_transmission(void);extern int32_t esp8266_connect_ap(char* ssid,char* pswd);extern int32_t esp8266_connect_server(char* mode,char* ip,uint16_t port);extern int32_t esp8266_disconnect_server(void);extern void esp8266_send_bytes(uint8_t *buf,uint32_t len);extern void esp8266_send_str(char *buf);extern void esp8266_send_at(char *str);extern int32_t esp8266_enable_multiple_id(uint32_t b);extern int32_t esp8266_create_server(uint16_t port);extern int32_t esp8266_close_server(uint16_t port);extern int32_t esp8266_enable_echo(uint32_t b);extern int32_t esp8266_reset(void);#endif
07、esp8266.c
#include "stm32f4xx.h"#include "sys.h"#include "delay.h"#include "usart.h"#include <string.h>#include <stdlib.h>#include <stdio.h>uint8_t g_esp8266_tx_buf[512];volatile uint8_t g_esp8266_rx_buf[512];volatile uint32_t g_esp8266_rx_cnt=0;volatile uint32_t g_esp8266_rx_end=0;volatile uint32_t g_esp8266_transparent_transmission_sta=0;void esp8266_init(void){usart3_init(115200);}void esp8266_send_at(char *str){//清空接收缓冲区memset((void *)g_esp8266_rx_buf,0, sizeof g_esp8266_rx_buf);//清空接收计数值g_esp8266_rx_cnt = 0;//串口3发送数据usart3_send_str(str);}void esp8266_send_bytes(uint8_t *buf,uint32_t len){usart3_send_bytes(buf,len);}void esp8266_send_str(char *buf){usart3_send_str(buf);}/* 查找接收数据包中的字符串 */int32_t esp8266_find_str_in_rx_packet(char *str,uint32_t timeout){char *dest = str;char *src = (char *)&g_esp8266_rx_buf;//等待串口接收完毕或超时退出while((strstr(src,dest)==NULL) && timeout){delay_ms(1);timeout--;}if(timeout)return 0;return -1;}/* 自检程序 */int32_t esp8266_self_test(void){esp8266_send_at("AT\r\n");return esp8266_find_str_in_rx_packet("OK",1000);}/*** 功能:连接热点* 参数:* ssid:热点名* pwd:热点密码* 返回值:* 连接结果,非0连接成功,0连接失败* 说明:* 失败的原因有以下几种(UART通信和ESP8266正常情况下)* 1. WIFI名和密码不正确* 2. 路由器连接设备太多,未能给ESP8266分配IP*/int32_t esp8266_connect_ap(char* ssid,char* pswd){#if 0//不建议使用以下sprintf,占用过多的栈char buf[128]={0};sprintf(buf,"AT+CWJAP_CUR=\"%s\",\"%s\"\r\n",ssid,pswd);#endif//设置为STATION模式esp8266_send_at("AT+CWMODE_CUR=1\r\n");if(esp8266_find_str_in_rx_packet("OK",1000))return -1;//连接目标AP//sprintf(buf,"AT+CWJAP_CUR=\"%s\",\"%s\"\r\n",ssid,pswd);esp8266_send_at("AT+CWJAP_CUR=");esp8266_send_at("\"");esp8266_send_at(ssid);esp8266_send_at("\"");esp8266_send_at(",");esp8266_send_at("\"");esp8266_send_at(pswd);esp8266_send_at("\"");esp8266_send_at("\r\n");if(esp8266_find_str_in_rx_packet("OK",5000))if(esp8266_find_str_in_rx_packet("CONNECT",5000))return -2;return 0;}/* 退出透传模式 */int32_t esp8266_exit_transparent_transmission (void){esp8266_send_at ("+++");//退出透传模式,发送下一条AT指令要间隔1秒delay_s(1);//记录当前esp8266工作在非透传模式g_esp8266_transparent_transmission_sta = 0;return 0;}/* 进入透传模式 */int32_t esp8266_entry_transparent_transmission(void){//进入透传模式esp8266_send_at("AT+CIPMODE=1\r\n");if(esp8266_find_str_in_rx_packet("OK",5000))return -1;delay_s(2);//开启发送状态esp8266_send_at("AT+CIPSEND\r\n");if(esp8266_find_str_in_rx_packet("OK",5000))return -2;//记录当前esp8266工作在透传模式g_esp8266_transparent_transmission_sta = 1;return 0;}/*** 功能:使用指定协议(TCP/UDP)连接到服务器* 参数:* mode:协议类型 "TCP","UDP"* ip:目标服务器IP* port:目标是服务器端口号* 返回值:* 连接结果,非0连接成功,0连接失败* 说明:* 失败的原因有以下几种(UART通信和ESP8266正常情况下)* 1. 远程服务器IP和端口号有误* 2. 未连接AP* 3. 服务器端禁止添加(一般不会发生)*/int32_t esp8266_connect_server(char* mode,char* ip,uint16_t port){#if 0//使用MQTT传递的ip地址过长,不建议使用以下方法,否则导致栈溢出//AT+CIPSTART="TCP","a10tC4OAAPc.iot-as-mqtt.cn-shanghai.aliyuncs.com",1883,该字符串占用内存过多了char buf[128]={0};//连接服务器sprintf((char*)buf,"AT+CIPSTART=\"%s\",\"%s\",%d\r\n",mode,ip,port);esp8266_send_at(buf);#elsechar buf[16]={0};esp8266_send_at("AT+CIPSTART=");esp8266_send_at("\""); esp8266_send_at(mode); esp8266_send_at("\"");esp8266_send_at(",");esp8266_send_at("\""); esp8266_send_at(ip); esp8266_send_at("\"");esp8266_send_at(",");sprintf(buf,"%d",port);esp8266_send_at(buf);esp8266_send_at("\r\n");#endifif(esp8266_find_str_in_rx_packet("CONNECT",5000))if(esp8266_find_str_in_rx_packet("OK",5000))return -1;return 0;}/* 断开服务器 */int32_t esp8266_disconnect_server(void){esp8266_send_at("AT+CIPCLOSE\r\n");if(esp8266_find_str_in_rx_packet("CLOSED",5000))if(esp8266_find_str_in_rx_packet("OK",5000))return -1;return 0;}/* 使能多链接 */int32_t esp8266_enable_multiple_id(uint32_t b){char buf[32]={0};sprintf(buf,"AT+CIPMUX=%d\r\n", b);esp8266_send_at(buf);if(esp8266_find_str_in_rx_packet("OK",5000))return -1;return 0;}/* 创建服务器 */int32_t esp8266_create_server(uint16_t port){char buf[32]={0};sprintf(buf,"AT+CIPSERVER=1,%d\r\n", port);esp8266_send_at(buf);if(esp8266_find_str_in_rx_packet("OK",5000))return -1;return 0;}/* 关闭服务器 */int32_t esp8266_close_server(uint16_t port){char buf[32]={0};sprintf(buf,"AT+CIPSERVER=0,%d\r\n", port);esp8266_send_at(buf);if(esp8266_find_str_in_rx_packet("OK",5000))return -1;return 0;}/* 回显打开或关闭 */int32_t esp8266_enable_echo(uint32_t b){if(b)esp8266_send_at("ATE1\r\n");elseesp8266_send_at("ATE0\r\n");if(esp8266_find_str_in_rx_packet("OK",5000))return -1;return 0;}/* 复位 */int32_t esp8266_reset(void){esp8266_send_at("AT+RST\r\n");if(esp8266_find_str_in_rx_packet("OK",10000))return -1;return 0;}
08、esp8266_mqtt.h
#ifndef __ES8266_MQTT_H#define __ES8266_MQTT_H#include "stm32f4xx.h"//此处是阿里云服务器的公共实例登陆配置-------------------------------------注意修改为自己的云服务设备信息!!!!#define MQTT_BROKERADDRESS "gbfzwEQBsLm.iot-as-mqtt.cn-shanghai.aliyuncs.com"#define MQTT_CLIENTID "00001|securemode=3,signmethod=hmacsha1|"#define MQTT_USARNAME "TESTDEVICE01&gbfzwEQBsLm"#define MQTT_PASSWD "4CBC23E827DF6833CAAAE8CC471C3AACF0B6DBED"#define MQTT_PUBLISH_TOPIC "/sys/gbfzwEQBsLm/TESTDEVICE01/thing/event/property/post"#define MQTT_SUBSCRIBE_TOPIC "/sys/gbfzwEQBsLm/TESTDEVICE01/thing/service/property/set"//此处是阿里云服务器的企业实例登陆配置-------------------------------------注意修改为自己的云服务设备信息!!!!//#define MQTT_BROKERADDRESS "iot-060a065f.mqtt.iothub.aliyuncs.com"//#define MQTT_CLIENTID "0001|securemode=3,signmethod=hmacsha1|"//#define MQTT_USARNAME "smartdevice&g850YXdgU5r"//#define MQTT_PASSWD "A8F93BD31F6085B1AB2AE3CC311E38971B15885D"//#define MQTT_PUBLISH_TOPIC "/sys/g850YXdgU5r/smartdevice/thing/event/property/post"//#define MQTT_SUBSCRIBE_TOPIC "/sys/g850YXdgU5r/smartdevice/thing/service/property/set"#define BYTE0(dwTemp) (*( char *)(&dwTemp))#define BYTE1(dwTemp) (*((char *)(&dwTemp) + 1))#define BYTE2(dwTemp) (*((char *)(&dwTemp) + 2))#define BYTE3(dwTemp) (*((char *)(&dwTemp) + 3))//MQTT连接服务器extern int32_t mqtt_connect(char *client_id,char *user_name,char *password);//MQTT消息订阅extern int32_t mqtt_subscribe_topic(char *topic,uint8_t qos,uint8_t whether);//MQTT消息发布extern uint32_t mqtt_publish_data(char *topic, char *message, uint8_t qos);//MQTT发送心跳包extern int32_t mqtt_send_heart(void);extern int32_t esp8266_mqtt_init(void);extern void mqtt_disconnect(void);extern void mqtt_report_devices_status(void);#endif
09、esp8266_mqtt.c
#include "stm32f4xx.h"#include <string.h>#include <stdlib.h>#include <stdio.h>#include "sys.h"#include "delay.h"#include "usart.h"#include "dht11.h"#include "esp8266.h"#include "esp8266_mqtt.h"const uint8_t g_packet_heart_reply[2] = {0xc0,0x00};char g_mqtt_msg[526];uint32_t g_mqtt_tx_len;//MQTT发送数据void mqtt_send_bytes(uint8_t *buf,uint32_t len){esp8266_send_bytes(buf,len);}//发送心跳包int32_t mqtt_send_heart(void){uint8_t buf[2]={0xC0,0x00};uint32_t cnt=2;uint32_t wait=0;#if 0mqtt_send_bytes(buf,2);return 0;#elsewhile(cnt--){mqtt_send_bytes(buf,2);memset((void *)g_esp8266_rx_buf,0,sizeof(g_esp8266_rx_buf));g_esp8266_rx_cnt=0;wait=3000;//等待3s时间while(wait--){delay_ms(1);//检查心跳响应固定报头if((g_esp8266_rx_buf[0]==0xD0) && (g_esp8266_rx_buf[1]==0x00)){printf("心跳响应确认成功,服务器在线\r\n");return 0;}}}printf("心跳响应确认失败,服务器离线\r\n");return -1;#endif}//MQTT无条件断开void mqtt_disconnect(void){uint8_t buf[2]={0xe0,0x00};mqtt_send_bytes(buf,2);esp8266_disconnect_server();}//MQTT连接服务器的打包函数int32_t mqtt_connect(char *client_id,char *user_name,char *password){uint32_t client_id_len = strlen(client_id);uint32_t user_name_len = strlen(user_name);uint32_t password_len = strlen(password);uint32_t data_len;uint32_t cnt=2;uint32_t wait=0;g_mqtt_tx_len=0;//可变报头+Payload 每个字段包含两个字节的长度标识data_len = 10 + (client_id_len+2) + (user_name_len+2) + (password_len+2);//固定报头//控制报文类型g_esp8266_tx_buf[g_mqtt_tx_len++] = 0x10; //MQTT Message Type CONNECT//剩余长度(不包括固定头部)do{uint8_t encodedByte = data_len % 128;data_len = data_len / 128;// if there are more data to encode, set the top bit of this byteif ( data_len > 0 )encodedByte = encodedByte | 128;g_esp8266_tx_buf[g_mqtt_tx_len++] = encodedByte;} while ( data_len > 0 );//可变报头//协议名g_esp8266_tx_buf[g_mqtt_tx_len++] = 0; // Protocol Name Length MSBg_esp8266_tx_buf[g_mqtt_tx_len++] = 4; // Protocol Name Length LSBg_esp8266_tx_buf[g_mqtt_tx_len++] = 'M'; // ASCII Code for Mg_esp8266_tx_buf[g_mqtt_tx_len++] = 'Q'; // ASCII Code for Qg_esp8266_tx_buf[g_mqtt_tx_len++] = 'T'; // ASCII Code for Tg_esp8266_tx_buf[g_mqtt_tx_len++] = 'T'; // ASCII Code for T//协议级别g_esp8266_tx_buf[g_mqtt_tx_len++] = 4; // MQTT Protocol version = 4//连接标志g_esp8266_tx_buf[g_mqtt_tx_len++] = 0xc2; // conn flagsg_esp8266_tx_buf[g_mqtt_tx_len++] = 0; // Keep-alive Time Length MSBg_esp8266_tx_buf[g_mqtt_tx_len++] = 60; // Keep-alive Time Length LSB 60S心跳包g_esp8266_tx_buf[g_mqtt_tx_len++] = BYTE1(client_id_len);// Client ID length MSBg_esp8266_tx_buf[g_mqtt_tx_len++] = BYTE0(client_id_len);// Client ID length LSBmemcpy(&g_esp8266_tx_buf[g_mqtt_tx_len],client_id,client_id_len);g_mqtt_tx_len += client_id_len;if(user_name_len > 0){g_esp8266_tx_buf[g_mqtt_tx_len++] = BYTE1(user_name_len); //user_name length MSBg_esp8266_tx_buf[g_mqtt_tx_len++] = BYTE0(user_name_len); //user_name length LSBmemcpy(&g_esp8266_tx_buf[g_mqtt_tx_len],user_name,user_name_len);g_mqtt_tx_len += user_name_len;}if(password_len > 0){g_esp8266_tx_buf[g_mqtt_tx_len++] = BYTE1(password_len); //password length MSBg_esp8266_tx_buf[g_mqtt_tx_len++] = BYTE0(password_len); //password length LSBmemcpy(&g_esp8266_tx_buf[g_mqtt_tx_len],password,password_len);g_mqtt_tx_len += password_len;}while(cnt--){memset((void *)g_esp8266_rx_buf,0,sizeof(g_esp8266_rx_buf));g_esp8266_rx_cnt=0;mqtt_send_bytes(g_esp8266_tx_buf,g_mqtt_tx_len);wait=3000;//等待3s时间while(wait--){delay_ms(1);//检查连接确认固定报头if((g_esp8266_rx_buf[0]==0x20) && (g_esp8266_rx_buf[1]==0x02)){if(g_esp8266_rx_buf[3] == 0x00){printf("连接已被服务器端接受,连接确认成功\r\n");return 0;//连接成功}else{switch(g_esp8266_rx_buf[3]){case 1:printf("连接已拒绝,不支持的协议版本\r\n");break;case 2:printf("连接已拒绝,不合格的客户端标识符\r\n");break;case 3:printf("连接已拒绝,服务端不可用\r\n");break;case 4:printf("连接已拒绝,无效的用户或密码\r\n");break;case 5:printf("连接已拒绝,未授权\r\n");break;default:printf("未知响应\r\n");break;}return 0;}}}}return -1;}//MQTT订阅/取消订阅数据打包函数//topic 主题//qos 消息等级//whether 订阅/取消订阅请求包int32_t mqtt_subscribe_topic(char *topic,uint8_t qos,uint8_t whether){uint32_t cnt=2;uint32_t wait=0;uint32_t topiclen = strlen(topic);uint32_t data_len = 2 + (topiclen+2) + (whether?1:0);//可变报头的长度(2字节)加上有效载荷的长度g_mqtt_tx_len=0;//固定报头//控制报文类型if(whether)g_esp8266_tx_buf[g_mqtt_tx_len++] = 0x82; //消息类型和标志订阅elseg_esp8266_tx_buf[g_mqtt_tx_len++] = 0xA2; //取消订阅//剩余长度do{uint8_t encodedByte = data_len % 128;data_len = data_len / 128;// if there are more data to encode, set the top bit of this byteif ( data_len > 0 )encodedByte = encodedByte | 128;g_esp8266_tx_buf[g_mqtt_tx_len++] = encodedByte;} while ( data_len > 0 );//可变报头g_esp8266_tx_buf[g_mqtt_tx_len++] = 0; //消息标识符 MSBg_esp8266_tx_buf[g_mqtt_tx_len++] = 0x01; //消息标识符 LSB//有效载荷g_esp8266_tx_buf[g_mqtt_tx_len++] = BYTE1(topiclen);//主题长度 MSBg_esp8266_tx_buf[g_mqtt_tx_len++] = BYTE0(topiclen);//主题长度 LSBmemcpy(&g_esp8266_tx_buf[g_mqtt_tx_len],topic,topiclen);g_mqtt_tx_len += topiclen;if(whether){g_esp8266_tx_buf[g_mqtt_tx_len++] = qos;//QoS级别}while(cnt--){g_esp8266_rx_cnt=0;memset((void *)g_esp8266_rx_buf,0,sizeof(g_esp8266_rx_buf));mqtt_send_bytes(g_esp8266_tx_buf,g_mqtt_tx_len);wait=3000;//等待3s时间while(wait--){delay_ms(1);//检查订阅确认报头if(g_esp8266_rx_buf[0]==0x90){printf("订阅主题确认成功\r\n");//获取剩余长度if(g_esp8266_rx_buf[1]==3){printf("Success - Maximum QoS 0 is %02X\r\n",g_esp8266_rx_buf[2]);printf("Success - Maximum QoS 2 is %02X\r\n",g_esp8266_rx_buf[3]);printf("Failure is %02X\r\n",g_esp8266_rx_buf[4]);}//获取剩余长度if(g_esp8266_rx_buf[1]==2){printf("Success - Maximum QoS 0 is %02X\r\n",g_esp8266_rx_buf[2]);printf("Success - Maximum QoS 2 is %02X\r\n",g_esp8266_rx_buf[3]);}//获取剩余长度if(g_esp8266_rx_buf[1]==1){printf("Success - Maximum QoS 0 is %02X\r\n",g_esp8266_rx_buf[2]);}return 0;//订阅成功}}}if(cnt)return 0; //订阅成功return -1;}//MQTT发布数据打包函数//topic 主题//message 消息//qos 消息等级uint32_t mqtt_publish_data(char *topic, char *message, uint8_t qos){staticuint16_t id=0;uint32_t topicLength = strlen(topic);uint32_t messageLength = strlen(message);uint32_t data_len;uint8_t encodedByte;g_mqtt_tx_len=0;//有效载荷的长度这样计算:用固定报头中的剩余长度字段的值减去可变报头的长度//QOS为0时没有标识符//数据长度 主题名 报文标识符 有效载荷if(qos) data_len = (2+topicLength) + 2 + messageLength;else data_len = (2+topicLength) + messageLength;//固定报头//控制报文类型g_esp8266_tx_buf[g_mqtt_tx_len++] = 0x30; // MQTT Message Type PUBLISH//剩余长度do{encodedByte = data_len % 128;data_len = data_len / 128;// if there are more data to encode, set the top bit of this byteif ( data_len > 0 )encodedByte = encodedByte | 128;g_esp8266_tx_buf[g_mqtt_tx_len++] = encodedByte;} while ( data_len > 0 );g_esp8266_tx_buf[g_mqtt_tx_len++] = BYTE1(topicLength);//主题长度MSBg_esp8266_tx_buf[g_mqtt_tx_len++] = BYTE0(topicLength);//主题长度LSBmemcpy(&g_esp8266_tx_buf[g_mqtt_tx_len],topic,topicLength);//拷贝主题g_mqtt_tx_len += topicLength;//报文标识符if(qos){g_esp8266_tx_buf[g_mqtt_tx_len++] = BYTE1(id);g_esp8266_tx_buf[g_mqtt_tx_len++] = BYTE0(id);id++;}memcpy(&g_esp8266_tx_buf[g_mqtt_tx_len],message,messageLength);g_mqtt_tx_len += messageLength;mqtt_send_bytes(g_esp8266_tx_buf,g_mqtt_tx_len);//咱们的Qos等级设置的是00,因此阿里云物联网平台是没有返回响应信息的return g_mqtt_tx_len;}//设备状态上报void mqtt_report_devices_status(void){uint8_t led_1_sta = GPIO_ReadOutputDataBit(GPIOF,GPIO_Pin_9) ? 0:1;uint8_t led_2_sta = GPIO_ReadOutputDataBit(GPIOF,GPIO_Pin_10) ? 0:1;uint8_t led_3_sta = GPIO_ReadOutputDataBit(GPIOE,GPIO_Pin_13) ? 0:1;//把开发板相关的状态变量利用sprintf函数存放到一个数组里,再把该数组利用MQTT协议打包成消息报文//sprintf(str,"a=%d",a);//需要更改“temperature”和“CurrentHumidity”为对应的平台设备信息;sprintf(g_mqtt_msg,"{\"method\":\"thing.service.property.set\",\"id\":\"0001\",\"params\":{\\"CurrentTemperature\":%.1f,\\"CurrentHumidity\":%.1f,\\"switch_led_r\":%d,\\"switch_led_g\":%d,\\"switch_led_b\":%d,\},\"version\":\"1.0.0\"}",g_temp,g_humi,led_1_sta,led_2_sta,led_3_sta);//上报信息到平台服务器mqtt_publish_data(MQTT_PUBLISH_TOPIC,g_mqtt_msg,0);}int32_t esp8266_mqtt_init(void){int32_t rt;//esp8266初始化esp8266_init();// printf("esp8266_init");//退出透传模式,才能输入AT指令rt=esp8266_exit_transparent_transmission();if(rt){printf("esp8266_exit_transparent_transmission fail\r\n");return -1;}printf("esp8266_exit_transparent_transmission success\r\n");delay_s(2);//复位模块rt=esp8266_reset();if(rt){printf("esp8266_reset fail\r\n");return -2;}printf("esp8266_reset success\r\n");delay_s(2);//关闭回显rt=esp8266_enable_echo(0);if(rt){printf("esp8266_enable_echo(0) fail\r\n");return -3;}printf("esp8266_enable_echo(0)success\r\n");delay_s(2);//连接热点rt = esp8266_connect_ap(WIFI_SSID,WIFI_PASSWORD);if(rt){printf("esp8266_connect_ap fail\r\n");return -4;}printf("esp8266_connect_ap success\r\n");delay_s(2);rt =esp8266_connect_server("TCP",MQTT_BROKERADDRESS,1883);if(rt){printf("esp8266_connect_server fail\r\n");return -5;}printf("esp8266_connect_server success\r\n");delay_s(2);//进入透传模式rt =esp8266_entry_transparent_transmission();if(rt){printf("esp8266_entry_transparent_transmission fail\r\n");return -6;}printf("esp8266_entry_transparent_transmission success\r\n");delay_s(2);if(mqtt_connect(MQTT_CLIENTID, MQTT_USARNAME, MQTT_PASSWD)){printf("mqtt_connect fail\r\n");return -7;}printf("mqtt_connect success\r\n");delay_s(2);if(mqtt_subscribe_topic(MQTT_SUBSCRIBE_TOPIC,0,1)){printf("mqtt_subscribe_topic fail\r\n");return -8;}printf("mqtt_subscribe_topic success\r\n");return 0;}
10、usart.h
#ifndef __USART_H__#define __USART_H__extern volatile uint8_t g_usart1_rx_buf[512];extern volatile uint32_t g_usart1_rx_cnt;extern volatile uint32_t g_usart1_rx_end;extern void usart1_init(uint32_t baud);extern void usart3_init(uint32_t baud);extern void usart3_send_str(char *str);extern void usart3_send_bytes(uint8_t *buf,uint32_t len);#endif
11、usart.c
#include "stm32f4xx.h"#include "sys.h"#include "usart.h"#include "esp8266.h"#include <stdio.h>#include <string.h>#include <stdlib.h>static USART_InitTypeDef USART_InitStructure;static GPIO_InitTypeDef GPIO_InitStructure;static NVIC_InitTypeDef NVIC_InitStructure;volatile uint8_t g_usart1_rx_buf[512];volatile uint32_t g_usart1_rx_cnt=0;volatile uint32_t g_usart1_rx_end=0;#pragma import(__use_no_semihosting_swi)struct __FILE { int handle; /* Add whatever you need here */ };FILE __stdout;FILE __stdin;int fputc(int ch, FILE *f){USART_SendData(USART1,ch);//等待数据发送成功while(USART_GetFlagStatus(USART1,USART_FLAG_TXE)==RESET);USART_ClearFlag(USART1,USART_FLAG_TXE);return ch;}void _sys_exit(int return_code) {}void _ttywrch(int ch){ch = ch;}void usart1_init(uint32_t baud){//使能端口A硬件时钟RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA,ENABLE);//使能串口1硬件时钟RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);//配置PA9、PA10为复用功能引脚GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9|GPIO_Pin_10;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;GPIO_InitStructure.GPIO_Speed = GPIO_High_Speed;GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;GPIO_Init(GPIOA,&GPIO_InitStructure);//将PA9、PA10连接到USART1的硬件GPIO_PinAFConfig(GPIOA, GPIO_PinSource9, GPIO_AF_USART1);GPIO_PinAFConfig(GPIOA, GPIO_PinSource10, GPIO_AF_USART1);//配置USART1的相关参数:波特率、数据位、校验位USART_InitStructure.USART_BaudRate = baud;//波特率USART_InitStructure.USART_WordLength = USART_WordLength_8b;//8位数据位USART_InitStructure.USART_StopBits = USART_StopBits_1;//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);NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;NVIC_Init(&NVIC_InitStructure);//使能串口1工作USART_Cmd(USART1,ENABLE);}void usart3_init(uint32_t baud){//使能端口B硬件时钟RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB,ENABLE);//使能串口3硬件时钟RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART3,ENABLE);//配置PB10、PB11为复用功能引脚GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10|GPIO_Pin_11;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;GPIO_InitStructure.GPIO_Speed = GPIO_High_Speed;GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;GPIO_Init(GPIOB,&GPIO_InitStructure);//将PB10、PB11连接到USART3的硬件GPIO_PinAFConfig(GPIOB, GPIO_PinSource10, GPIO_AF_USART3);GPIO_PinAFConfig(GPIOB, GPIO_PinSource11, GPIO_AF_USART3);//配置USART1的相关参数:波特率、数据位、校验位USART_InitStructure.USART_BaudRate = baud;//波特率USART_InitStructure.USART_WordLength = USART_WordLength_8b;//8位数据位USART_InitStructure.USART_StopBits = USART_StopBits_1;//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(USART3, &USART_InitStructure);//使能串口接收到数据触发中断USART_ITConfig(USART3, USART_IT_RXNE, ENABLE);NVIC_InitStructure.NVIC_IRQChannel = USART3_IRQn;NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;NVIC_Init(&NVIC_InitStructure);//使能串口3工作USART_Cmd(USART3,ENABLE);}void usart3_send_str(char *str){char *p = str;while(*p!='\0'){USART_SendData(USART3,*p);p++;//等待数据发送成功while(USART_GetFlagStatus(USART3,USART_FLAG_TXE)==RESET);USART_ClearFlag(USART3,USART_FLAG_TXE);}}void usart3_send_bytes(uint8_t *buf,uint32_t len){uint8_t *p = buf;while(len--){USART_SendData(USART3,*p);p++;//等待数据发送成功while(USART_GetFlagStatus(USART3,USART_FLAG_TXE)==RESET);USART_ClearFlag(USART3,USART_FLAG_TXE);}}void USART1_IRQHandler(void){uint8_t d=0;//检测是否接收到数据if (USART_GetITStatus(USART1, USART_IT_RXNE) == SET){d=USART_ReceiveData(USART1);g_usart1_rx_buf[g_usart1_rx_cnt++]=d;if(g_usart1_rx_cnt >= sizeof g_usart1_rx_buf){g_usart1_rx_end=1;}#if EN_DEBUG_ESP8266//将接收到的数据发给串口3USART_SendData(USART3,d);while(USART_GetFlagStatus(USART3,USART_FLAG_TXE)==RESET);#endif//清空标志位,可以响应新的中断请求USART_ClearITPendingBit(USART1, USART_IT_RXNE);}}void USART3_IRQHandler(void){uint8_t d=0;//检测是否接收到数据if (USART_GetITStatus(USART3, USART_IT_RXNE) == SET){d=USART_ReceiveData(USART3);g_esp8266_rx_buf[g_esp8266_rx_cnt++]=d;if(g_esp8266_rx_cnt >= sizeof g_esp8266_rx_buf){g_esp8266_rx_end=1;}#if EN_DEBUG_ESP8266//将接收到的数据返发给PCUSART_SendData(USART1,d);//while(USART_GetFlagStatus(USART1,USART_FLAG_TXE)==RESET);#endif//清空标志位,可以响应新的中断请求USART_ClearITPendingBit(USART3, USART_IT_RXNE);}}
12、main.c
/*****************************************************************名 称:基于stm32f4的mqtt*作 者:陈堪才*创建日期:2021/05/22*知 识 点:1.esp8266 wifi的AT指令的使用2.mqtt的连接、发布、订阅等3.添加了cJSON*说 明:1.通过阿里云物联网平台能够获取开发板的D1、D2、D3灯和温湿度值2.通过阿里云物联网平台能够控制开发板的D1、D2、D3灯的亮灭3.D4灯用于表示当前连接服务器的状态。亮-已连接;灭-已断开4.按键1用于重连阿里云物联网平台5.按键2用于断开阿里云物联网平台*修改日期:2021/05/27,增加了cJSON对阿里云物联网平台的数据解析*****************************************************************/#include "stm32f4xx.h"#include "sys.h"#include "usart.h"#include "esp8266.h"#include "esp8266_mqtt.h"#include "delay.h"#include "led.h"#include "beep.h"#include "dht11.h"#include "key.h"#include "tim.h"#include "cjson.h"#include <stdio.h>#include <string.h>#include <stdlib.h>void mqtt_cjson_parse(char *pbuf){cJSON *json , *json_params, *json_id, *json_led, *json_method;char *p = pbuf;//解析数据包json = cJSON_Parse(p);if (!json){cJSON_Delete(json);json=NULL;return;}//根据method键获取值json_method = cJSON_GetObjectItem(json ,"method");if(json_method->type == cJSON_String){printf("method:%s\r\n", json_method->valuestring);}//根据id键获取值json_id = cJSON_GetObjectItem(json , "id");if(json_id->type == cJSON_String){printf("id:%s\r\n", json_id->valuestring);}//根据params键获取值json_params = cJSON_GetObjectItem(json , "params");if(json_params){//根据switch_led_r键获取值json_led=cJSON_GetObjectItem(json_params , "switch_led_r");if(json_led->type == cJSON_Number){PFout(9) = !json_led->valueint;printf("switch_led_r:%d\r\n", json_led->valueint);}//根据switch_led_g键获取值json_led=cJSON_GetObjectItem(json_params , "switch_led_g");if(json_led->type == cJSON_Number){PFout(10) = !json_led->valueint;printf("switch_led_g:%d\r\n", json_led->valueint);}//根据switch_led_b键获取值json_led=cJSON_GetObjectItem(json_params , "switch_led_b");if(json_led->type == cJSON_Number){PEout(13) = !json_led->valueint;printf("switch_led_b:%d\r\n", json_led->valueint);}}cJSON_Delete(json);json=NULL;}int main(void){uint32_t i=0;uint32_t delay_1ms_cnt=0;uint8_t buf[5]={20,05,56,8,20};uint32_t key_sta=0;int32_t rt=0;Delay_Init();//led初始化led_init();//beep初始化beep_init();//温湿度传感器初始化dht11_init();//按键检测key_init();//定时器初始化tim3_init();//串口1初始化波特率为115200bpsusart1_init(115200);//串口延迟一会,确保芯片内部完成全部初始化,printf无乱码输出delay_ms(500);//打印开机信息printf("This is esp8266 mqtt with aliyun test by teacher.chen\r\n");while(esp8266_mqtt_init()){printf("esp8266_mqtt_init ...");delay_s(1);}//连接服务器状态指示灯,点亮-连接成功CONNECT_MQTT_LED(1);printf("esp8266 connect aliyun with mqtt success\r\n");while(1){//检查接收到数据if(g_esp8266_rx_end && g_esp8266_transparent_transmission_sta){#if 1printf("g_esp8266_rx_buf:%s\n",g_esp8266_rx_buf);for(i=0;i<g_esp8266_rx_cnt;i++){/*由于mqtt协议发布消息数据包 = 0x30+剩余长度+01+00+Topic主题名+json内容,例如通过阿里云物联网平台发送如下0x30 0xE2 0x01 0x00 /thing/service/property/set{"method":"thing.service.property.set","id":"408723893","params":{"switch_led_g":1,"version":"1.0.0"}传给cJSON时必须全为字符串,不能有0x00,否则遇到0x00会导致直接结束cJSON的。因此需要自行查找'{'开头的json内容*/if(g_esp8266_rx_buf[i] == '{'){mqtt_cjson_parse((char *)&g_esp8266_rx_buf[i]);break;}}#elsefor(i=0;i<g_esp8266_rx_cnt;i++){//判断的关键字符是否为 1"//核心数据,即{"switch_led_r":1}中的“1”if((g_esp8266_rx_buf[i]==0x31) && (g_esp8266_rx_buf[i+1]==0x22)){//判断控制变量if( g_esp8266_rx_buf[i+3]=='1' )PFout(9)=0;//控制灯亮elsePFout(9)=1;//控制灯灭}//判断的关键字符是否为 2"//核心数据,即{"switch_led_g":1}中的“1”if((g_esp8266_rx_buf[i]==0x32) && (g_esp8266_rx_buf[i+1]==0x22)){//判断控制变量if( g_esp8266_rx_buf[i+3]=='1' )PFout(10)=0;//控制灯亮elsePFout(10)=1;//控制灯灭}//判断的关键字符是否为 3"//核心数据,即{"switch_led_b":1}中的“1”if(g_esp8266_rx_buf[i]==0x33 && g_esp8266_rx_buf[i+1]==0x22){//判断控制变量if( g_esp8266_rx_buf[i+3]=='1' )PEout(13)=0;//控制灯亮elsePEout(13)=1;//控制灯灭}}#endif//清空接收缓冲区、接收计数值、接收结束标志位memset((void *)g_esp8266_rx_buf,0,sizeof g_esp8266_rx_buf);g_esp8266_rx_cnt=0;g_esp8266_rx_end=0;}delay_1ms_cnt++;delay_ms(1);//6秒时间到达if((delay_1ms_cnt % 6000) ==0){if(0 == dht11_read(buf)){g_temp = (float)buf[2]+(float)buf[3]/10;g_humi = (float)buf[0]+(float)buf[1]/10;}//上报设备状态mqtt_report_devices_status();}//60秒时间到达if((delay_1ms_cnt % 60000) ==0){/* 设备端在保活时间间隔内(保护时间在mqtt_connect设置为60s),至少需要发送一次报文,包括ping请求。连接保活时间的取值范围为30秒~1200秒。建议取值300秒以上。从物联网平台发送CONNACK响应CONNECT消息时,开始心跳计时。收到PUBLISH、SUBSCRIBE、PING或 PUBACK消息时,会重置计时器。*///发送心跳包,过于频繁发送心跳包,服务器将会持续一段时间不发送响应信息[可选]rt = mqtt_send_heart();if(rt == 0)CONNECT_MQTT_LED(1);elseCONNECT_MQTT_LED(0);}//按键检测if(key_sta_get()){delay_ms(50);key_sta=key_sta_get();if(key_sta & 0x01){printf("connect aliyun mqtt\r\n");//重连阿里云物联网平台rt = esp8266_mqtt_init();if(rt == 0)CONNECT_MQTT_LED(1);elseCONNECT_MQTT_LED(0);}if(key_sta & 0x02){printf("disconnect aliyun mqtt\r\n");//断开阿里云物联网平台mqtt_disconnect();CONNECT_MQTT_LED(0);}}}}
总结
>>>
选择MQTT库:可以使用像Eclipse Paho、Mosquitto等开源MQTT库,针对嵌入式开发的有很多,诸如MQTT-C、mqtt_client等。
设置开发环境:根据你的STM32开发板,设置好开发环境,比如STM32CubeIDE或Keil等。
网络连接:确保你的STM32能够通过Wi-Fi、以太网等方式连接到互联网。通常会用到ESP8266、ESP32等Wi-Fi模块或以太网模块(如W5500)。
添加MQTT库:将选定的MQTT库添加到你的STM32项目中。如果是使用HAL库的项目,记得根据库的要求配置时钟、GPIO等。
实现MQTT客户端:
初始化网络和MQTT库。
连接到MQTT代理(Broker)。
订阅主题,发布消息。
处理接收到的消息。
故我在
点击下方卡片 关注我
↓↓↓

