点击上方"蓝字"关注我们
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
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);#else char 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"); #endif if(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"); else esp8266_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 0 mqtt_send_bytes(buf,2); return 0;#else while(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 byte if ( 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 MSB g_esp8266_tx_buf[g_mqtt_tx_len++] = 4; // Protocol Name Length LSB g_esp8266_tx_buf[g_mqtt_tx_len++] = 'M'; // ASCII Code for M g_esp8266_tx_buf[g_mqtt_tx_len++] = 'Q'; // ASCII Code for Q g_esp8266_tx_buf[g_mqtt_tx_len++] = 'T'; // ASCII Code for T g_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 flags g_esp8266_tx_buf[g_mqtt_tx_len++] = 0; // Keep-alive Time Length MSB g_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 MSB g_esp8266_tx_buf[g_mqtt_tx_len++] = BYTE0(client_id_len);// Client ID length LSB memcpy(&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 MSB g_esp8266_tx_buf[g_mqtt_tx_len++] = BYTE0(user_name_len); //user_name length LSB memcpy(&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 MSB g_esp8266_tx_buf[g_mqtt_tx_len++] = BYTE0(password_len); //password length LSB memcpy(&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; //消息类型和标志订阅 else g_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 byte if ( 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; //消息标识符 MSB g_esp8266_tx_buf[g_mqtt_tx_len++] = 0x01; //消息标识符 LSB //有效载荷 g_esp8266_tx_buf[g_mqtt_tx_len++] = BYTE1(topiclen);//主题长度 MSB g_esp8266_tx_buf[g_mqtt_tx_len++] = BYTE0(topiclen);//主题长度 LSB memcpy(&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){static uint16_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 byte if ( 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);//主题长度MSB g_esp8266_tx_buf[g_mqtt_tx_len++] = BYTE0(topicLength);//主题长度LSB memcpy(&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 //将接收到的数据发给串口3 USART_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 //将接收到的数据返发给PC USART_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初始化波特率为115200bps usart1_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 1 printf("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; } }#else for(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;//控制灯亮 else PFout(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;//控制灯亮 else PFout(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;//控制灯亮 else PEout(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); else CONNECT_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); else CONNECT_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)。
订阅主题,发布消息。
处理接收到的消息。
故我在
点击下方卡片 关注我
↓↓↓