}
if(sta==3){
//登录MQTT
_mqtt.Init(rxbuf,sizeof(rxbuf),txbuf,sizeof(txbuf));
if(_mqtt.Connect(
"123456|securemode=3,signmethod=hmacsha1,timestamp=789|",//ClientID
"FY-STM32&a1ugBNniFGU",//Username
"b447b9f26938d8eba6b2a4878066aae91839600c"//Password
) != 0){
Oled_ShowString(0,3*16,"Enter MQTT OK!",8,16,1);
// printf("Enter MQTT OK!\r\n"); sta++; } else{ Oled_ShowString(0,3*16,"Enter MQTT Error",8,16,1); // printf("Enter MQTT Error!\r\n"); sta=0; } Oled_RefreshGram(); }
if(sta==4){
Oled_Clear();
//订阅主题
if(_mqtt.SubscribeTopic("/sys/a1ugBNniFGU/FY-STM32/thing/service/property/set",0,1) != 0){
Oled_ShowString(0,0*16,"Subscribe OK!",8,16,1);
// printf("SubscribeTopic OK!\r\n"); } else{ Oled_ShowString(0,016,"Subscribe Error!",8,16,1); // printf("SubscribeTopic Error!\r\n"); } Oled_RefreshGram(); } Delay_ms(1000); Oled_Clear(); Oled_ShowString(32,016,"Mars Tech",8,16,1); Oled_ShowString(0,116," VCC: 0000 mV",8,16,1); Oled_ShowString(0,216,"Temp: 00.0 C",8,16,1); Oled_ShowString(0,316," LUX: 0000 ",8,16,1); Oled_RefreshGram(); sta = 0; while(1) { if(++cnt_2s>=200){ cnt_2s=0; Adc_GetValue(); temperature = Ntc_GetTemp(adc_ntc)0.1f; Oled_ShowNum(86,116,battery,4,8,16); Oled_ShowNum(86,216,(u8)temperature,2,8,16); Oled_ShowNum(86+38,216,(u16)(temperature10)%10,1,8,16); Oled_ShowNum(86,316,adc_light,4,8,16);
Oled_RefreshGram();
}
if(++cnt_5s>=500){//
cnt_5s=0;
sprintf(mqtt_message,
"{\"method\":\"thing.service.property.set\",\"id\":\"630262306\",\"params\":{\
\"CurrentTemperature\":%.1f,\
\"hue\":%d,\
\"mlux\":%d\
},\"version\":\"1.0.0\"}",
temperature,
rand()%0x00ffffff,
adc_light
);
_mqtt.PublishData("/sys/a1ugBNniFGU/FY-STM32/thing/event/property/post",mqtt_message,0);
}
if(_mqtt.rxlen){
Mqtt_Progress(_mqtt.rxbuf,_mqtt.rxlen);
memset(_mqtt.rxbuf,0,_mqtt.rxlen);
_mqtt.rxlen = 0;
}
Delay_ms(10);
}
}
void Adc_GetValue(void) { u32 sum[ADC_CH_MAX]; memset(sum,0,sizeof(sum)); for(u8 ch=0; ch<ADC_CH_MAX; ch++) { for(u8 filter=0; filter<ADC_FILTER; filter++) { sum[ch] += _adc.buf[filter][ch];//计算累加和 } } adc_light = sum[ADC_LIGHT]/ADC_FILTER; adc_ntc = sum[ADC_NTC]/ADC_FILTER; battery = sum[ADC_VCC]/ADC_FILTER3300/409511; //mV }
u8 temp; void USART2_IRQHandler(void) { static u8 rxlen = 0;
if(USART_GetITStatus(USART2, USART_IT_RXNE) != RESET) //判断接收数据寄存器是否有数据
{
temp = USART2->DR;
// USART1->DR = temp;//这里用作串口1打印WIFI模块发送的数据 if(rxlen>=255) rxlen=0; rxbuf[rxlen++] = temp; rxlen%=sizeof(rxbuf); }
if(USART_GetITStatus(USART2, USART_IT_IDLE))
{
temp = USART2->DR;
temp = USART2->SR;
_mqtt.rxlen = rxlen;
// Mqtt_Progress(rxbuf,rxlen);//主循环做异步处理 rxlen=0; } } //void *StrStr(void *dest,void *src);
u8 *p; void Mqtt_Progress(u8 *buf,u16 len) { char *keyStr = "hue"; u8 keyStrLen = strlen(keyStr)-1; u8 i,j; for(i=0;i<len-keyStrLen;i++) { for(j=0;j<keyStrLen;j++) { if(buf[i+j] != keyStr[j]) break; } if(j==keyStrLen) break; } if(i<=len-keyStrLen) { u16 temp = 0; p = &buf[i];
while(*p != ':') p++;
p++;
while(*p != '}')
{
temp = temp *10 + (*p - '0');
p++;
}
printf("receive message, hue = %d \r\n",temp);
// Usart1_SendBuf(buf,len); } }
/*END OF FILE/
net.c部分
#include "fy_network.h"
//#define _DEBUG_NET
static u8 Check(void); static u8 Init(u8 prx,u16 rxlen,u8 ptx,u16 txlen); static void Restore(void); static u8 ConnectAP(char ssid,char pswd); static u8 ConnectServer(char* mode,char* ip,u16 port); static u8 DisconnectServer(void); static u8 OpenTransmission(void); static void CloseTransmission(void); static void SendString(char* str); static void SendBuf(u8* buf,u16 len);
_typdef_net _net= { 0,0, 0,0, Check, Init, Restore, ConnectAP, ConnectServer, DisconnectServer, OpenTransmission, CloseTransmission, SendString, SendBuf };
/**
-
功能:初始化ESP8266
-
参数:None
-
返回值:初始化结果,非0为初始化成功,0为失败 */ static u8 Init(u8 *prx,u16 rxlen,u8 *ptx,u16 txlen) { _net.rxbuf = prx;_net.rxlen = rxlen; _net.txbuf = ptx;_net.txlen = txlen;
memset(_net.rxbuf,0,_net.rxlen); memset(_net.txbuf,0,_net.txlen);
_net.CloseTransmission(); //退出透传 Delay_ms(500); _net.SendString("AT+RST\r\n"); //重启ESP8266 Delay_ms(800); if(_net.Check()==0) //使用AT指令检查ESP8266是否存在 { return 0; }
memset(_net.rxbuf,0,_net.rxlen); //清空接收缓冲 _net.SendString("ATE0\r\n"); //关闭回显 if(FindStr((char*)_net.rxbuf,"OK",500)==0) //设置不成功 { return 0;
} return 1; //设置成功
}
/**
- 功能:恢复出厂设置
- 参数:None
- 返回值:None
- 说明:此时ESP8266中的用户设置将全部丢失回复成出厂状态
*/
static void Restore(void)
{
_net.CloseTransmission(); //退出透传
Delay_ms(500);
_net.SendString("AT+RESTORE\r\n");//恢复出厂
// NVIC_SystemReset(); //同时重启单片机
}
/**
- 功能:检查ESP8266是否正常
- 参数:None
- 返回值:ESP8266返回状态
-
非0 ESP8266正常 -
0 ESP8266有问题
/ static u8 Check(void) { u8 check_cnt=5; while(check_cnt--) { memset(_net.rxbuf,0,_net.rxlen); //清空接收缓冲 _net.SendString("AT\r\n"); //发送AT握手指令 if(FindStr((char)_net.rxbuf,"OK",200) != 0) { return 1; } } return 0; }
/**
- 功能:连接热点
- 参数:
-
ssid:热点名 -
pwd:热点密码 - 返回值:
-
连接结果,非0连接成功,0连接失败 - 说明:
-
失败的原因有以下几种(UART通信和ESP8266正常情况下) -
1. WIFI名和密码不正确 -
2. 路由器连接设备太多,未能给ESP8266分配IP
/
static u8 ConnectAP(char ssid,char* pswd)
{
u8 cnt=5;
while(cnt--)
{
memset(_net.rxbuf,0,_net.rxlen);
_net.SendString("AT+CWMODE_CUR=1\r\n"); //设置为STATION模式
if(FindStr((char*)_net.rxbuf,"OK",200) != 0)
{
break;
}
}
if(cnt == 0)
return 0;
cnt=2;
while(cnt--)
{
memset(_net.txbuf,0,_net.txlen); //清空发送缓冲
memset(_net.rxbuf,0,_net.rxlen); //清空接收缓冲
sprintf((char*)_net.txbuf,"AT+CWJAP_CUR=\"%s\",\"%s\"\r\n",ssid,pswd);//连接目标AP
_net.SendString((char*)_net.txbuf);
if(FindStr((char*)_net.rxbuf,"OK",8000)!=0) //连接成功且分配到IP
{
return 1;
}
}
return 0;
}
/**
- 功能:使用指定协议(TCP/UDP)连接到服务器
- 参数:
-
mode:协议类型 "TCP","UDP" -
ip:目标服务器IP -
port:目标是服务器端口号 - 返回值:
-
连接结果,非0连接成功,0连接失败 - 说明:
-
失败的原因有以下几种(UART通信和ESP8266正常情况下) -
1. 远程服务器IP和端口号有误 -
2. 未连接AP -
3. 服务器端禁止添加(一般不会发生)
/ static u8 ConnectServer(char mode,char* ip,u16 port) { u8 cnt;
_net.CloseTransmission(); //多次连接需退出透传
Delay_ms(500);
//连接服务器
cnt=2;
while(cnt--)
{
memset(_net.rxbuf,0,_net.rxlen);
memset(_net.txbuf,0,_net.txlen);
sprintf((char*)_net.txbuf,"AT+CIPSTART=\"%s\",\"%s\",%d\r\n",mode,ip,port);
_net.SendString((char*)_net.txbuf);
if(FindStr((char*)_net.rxbuf,"CONNECT",8000) !=0 )
{
break;
}
}
if(cnt == 0)
return 0;
//设置透传模式
if(_net.OpenTransmission()==0) return 0;
//开启发送状态
cnt=2;
while(cnt--)
{
memset(_net.rxbuf,0,_net.rxlen);
_net.SendString("AT+CIPSEND\r\n");//开始处于透传发送状态
if(FindStr((char*)_net.rxbuf,">",200)!=0)
{
return 1;
}
}
return 0;
}
/**
- 功能:主动和服务器断开连接
- 参数:None
- 返回值:
-
连接结果,非0断开成功,0断开失败
*/
static u8 DisconnectServer(void)
{
u8 cnt;
_net.CloseTransmission(); //退出透传
Delay_ms(500);
while(cnt--)
{
memset(_net.rxbuf,0,_net.rxlen);
_net.SendString("AT+CIPCLOSE\r\n");//关闭链接
if(FindStr((char*)_net.rxbuf,"CLOSED",200)!=0)//操作成功,和服务器成功断开
{
break;
}
}
if(cnt) return 1;
return 0;
}
/**
- 功能:透传模式下的数据发送函数
- 参数:
-
buffer:待发送数据 - 返回值:None / static void SendBuf(u8 buf,u16 len) { memset(_net.rxbuf,0,_net.rxlen); #ifdef _DEBUG_NET Usart1_SendBuf(buf,len); #endif Net_SendBuf(buf,len); }
/**
- 功能:透传模式下的数据发送函数
- 参数:
-
buffer:待发送数据 - 返回值:None / static void SendString(char str) { memset(_net.rxbuf,0,_net.rxlen); #ifdef _DEBUG_NET Usart1_SendString(str); #endif Net_SendString(str); }
static u8 OpenTransmission(void)
{
//设置透传模式
u8 cnt=2;
while(cnt--)
{
memset(_net.rxbuf,0,_net.rxlen);
_net.SendString("AT+CIPMODE=1\r\n");
if(FindStr((char*)_net.rxbuf,"OK",200)!=0)
{
return 1;
}
}
return 0;
}
//退出透传
static void CloseTransmission(void)
{
_net.SendString("+++"); Delay_ms(50);
_net.SendString("+++"); Delay_ms(50);
}
/*END OF FILE/
net.h部分
#ifndef __FY_NETWORK_H #define __FY_NETWORK_H
#include "fy_includes.h"
/连接AP宏定义/ #define SSID "ssid" #define PWD "password"
/连接服务器宏定义/ #define TCP "TCP" #define UDP "UDP" #define IP "122.114.122.174" #define PORT 40915
#define Net_SendString(str) Usart2_SendString(str) #define Net_SendBuf(buf,len) Usart2_SendBuf(buf,len)
typedef struct { u8 *rxbuf;u16 rxlen; u8 *txbuf;u16 txlen;
u8 (*Check)(void);
u8 (*Init)(u8 *prx,u16 rxlen,u8 *ptx,u16 txlen);
void (*Restore)(void);
u8 (*ConnectAP)(char *ssid,char *pswd);
u8 (*ConnectServer)(char* mode,char *ip,u16 port);
u8 (*DisconnectServer)(void);
u8 (*OpenTransmission)(void);
void (*CloseTransmission)(void);
void (*SendString)(char *str);
void (*SendBuf)(u8 *buf,u16 len);
}_typdef_net;
extern _typdef_net _net;
#endif
/*END OF FILE/
mqtt.c部分
#include "fy_mqtt.h"
//#define _DEBUG_MQTT
typedef enum { //名字 值 报文流动方向 描述 M_RESERVED1 =0 , // 禁止 保留 M_CONNECT , // 客户端到服务端 客户端请求连接服务端 M_CONNACK , // 服务端到客户端 连接报文确认 M_PUBLISH , // 两个方向都允许 发布消息 M_PUBACK , // 两个方向都允许 QoS 1消息发布收到确认 M_PUBREC , // 两个方向都允许 发布收到(保证交付第一步) M_PUBREL , // 两个方向都允许 发布释放(保证交付第二步) M_PUBCOMP , // 两个方向都允许 QoS 2消息发布完成(保证交互第三步) M_SUBSCRIBE , // 客户端到服务端 客户端订阅请求 M_SUBACK , // 服务端到客户端 订阅请求报文确认 M_UNSUBSCRIBE , // 客户端到服务端 客户端取消订阅请求 M_UNSUBACK , // 服务端到客户端 取消订阅报文确认 M_PINGREQ , // 客户端到服务端 心跳请求 M_PINGRESP , // 服务端到客户端 心跳响应 M_DISCONNECT , // 客户端到服务端 客户端断开连接 M_RESERVED2 , // 禁止 保留 }_typdef_mqtt_message;
//连接成功服务器回应 20 02 00 00 //客户端主动断开连接 e0 00 const u8 parket_connetAck[] = {0x20,0x02,0x00,0x00}; const u8 parket_disconnet[] = {0xe0,0x00}; const u8 parket_heart[] = {0xc0,0x00}; const u8 parket_heart_reply[] = {0xc0,0x00}; const u8 parket_subAck[] = {0x90,0x03};
static void Mqtt_SendBuf(u8 *buf,u16 len);
static void Init(u8 *prx,u16 rxlen,u8 *ptx,u16 txlen); static u8 Connect(char *ClientID,char *Username,char *Password); static u8 SubscribeTopic(char *topic,u8 qos,u8 whether); static u8 PublishData(char *topic, char *message, u8 qos); static void SentHeart(void); static void Disconnect(void);
_typdef_mqtt _mqtt = { 0,0, 0,0, Init, Connect, SubscribeTopic, PublishData, SentHeart, Disconnect, };
static void Init(u8 *prx,u16 rxlen,u8 *ptx,u16 txlen) { _mqtt.rxbuf = prx;_mqtt.rxlen = rxlen; _mqtt.txbuf = ptx;_mqtt.txlen = txlen;
memset(_mqtt.rxbuf,0,_mqtt.rxlen);
memset(_mqtt.txbuf,0,_mqtt.txlen);
//无条件先主动断开
_mqtt.Disconnect();Delay_ms(100);
_mqtt.Disconnect();Delay_ms(100);
}
//连接服务器的打包函数 static u8 Connect(char *ClientID,char *Username,char *Password) { int ClientIDLen = strlen(ClientID); int UsernameLen = strlen(Username); int PasswordLen = strlen(Password); int DataLen; _mqtt.txlen=0; //可变报头+Payload 每个字段包含两个字节的长度标识 DataLen = 10 + (ClientIDLen+2) + (UsernameLen+2) + (PasswordLen+2);
//固定报头
//控制报文类型
_mqtt.txbuf[_mqtt.txlen++] = 0x10; //MQTT Message Type CONNECT
//剩余长度(不包括固定头部)
do
{
u8 encodedByte = DataLen % 128;
DataLen = DataLen / 128;
// if there are more data to encode, set the top bit of this byte
if ( DataLen > 0 )
encodedByte = encodedByte | 128;
_mqtt.txbuf[_mqtt.txlen++] = encodedByte;
}while ( DataLen > 0 );
//可变报头
//协议名
_mqtt.txbuf[_mqtt.txlen++] = 0; // Protocol Name Length MSB
_mqtt.txbuf[_mqtt.txlen++] = 4; // Protocol Name Length LSB
_mqtt.txbuf[_mqtt.txlen++] = 'M'; // ASCII Code for M
_mqtt.txbuf[_mqtt.txlen++] = 'Q'; // ASCII Code for Q
_mqtt.txbuf[_mqtt.txlen++] = 'T'; // ASCII Code for T
_mqtt.txbuf[_mqtt.txlen++] = 'T'; // ASCII Code for T
//协议级别
_mqtt.txbuf[_mqtt.txlen++] = 4; // MQTT Protocol version = 4
//连接标志
_mqtt.txbuf[_mqtt.txlen++] = 0xc2; // conn flags
_mqtt.txbuf[_mqtt.txlen++] = 0; // Keep-alive Time Length MSB
_mqtt.txbuf[_mqtt.txlen++] = 60; // Keep-alive Time Length LSB 60S心跳包
_mqtt.txbuf[_mqtt.txlen++] = BYTE1(ClientIDLen);// Client ID length MSB
_mqtt.txbuf[_mqtt.txlen++] = BYTE0(ClientIDLen);// Client ID length LSB
memcpy(&_mqtt.txbuf[_mqtt.txlen],ClientID,ClientIDLen);
_mqtt.txlen += ClientIDLen;
if(UsernameLen > 0)
{
_mqtt.txbuf[_mqtt.txlen++] = BYTE1(UsernameLen); //username length MSB
_mqtt.txbuf[_mqtt.txlen++] = BYTE0(UsernameLen); //username length LSB
memcpy(&_mqtt.txbuf[_mqtt.txlen],Username,UsernameLen);
_mqtt.txlen += UsernameLen;
}
if(PasswordLen > 0)
{
_mqtt.txbuf[_mqtt.txlen++] = BYTE1(PasswordLen); //password length MSB
_mqtt.txbuf[_mqtt.txlen++] = BYTE0(PasswordLen); //password length LSB
memcpy(&_mqtt.txbuf[_mqtt.txlen],Password,PasswordLen);
_mqtt.txlen += PasswordLen;
}
u8 cnt=2;
u8 wait;
while(cnt--)
{
memset(_mqtt.rxbuf,0,_mqtt.rxlen);
Mqtt_SendBuf(_mqtt.txbuf,_mqtt.txlen);
wait=30;//等待3s时间
while(wait--)
{
//CONNECT
if(_mqtt.rxbuf[0]==parket_connetAck[0] && _mqtt.rxbuf[1]==parket_connetAck[1]) //连接成功
{
return 1;//连接成功
}
Delay_ms(100);
}
}
return 0;
}
//MQTT订阅/取消订阅数据打包函数
//topic 主题
//qos 消息等级
//whether 订阅/取消订阅请求包
static u8 SubscribeTopic(char *topic,u8 qos,u8 whether)
{
_mqtt.txlen=0;
int topiclen = strlen(topic);
int DataLen = 2 + (topiclen+2) + (whether?1:0);//可变报头的长度(2字节)加上有效载荷的长度
//固定报头
//控制报文类型
if(whether) _mqtt.txbuf[_mqtt.txlen++] = 0x82; //消息类型和标志订阅
else _mqtt.txbuf[_mqtt.txlen++] = 0xA2; //取消订阅
//剩余长度
do
{
u8 encodedByte = DataLen % 128;
DataLen = DataLen / 128;
// if there are more data to encode, set the top bit of this byte
if ( DataLen > 0 )
encodedByte = encodedByte | 128;
_mqtt.txbuf[_mqtt.txlen++] = encodedByte;
}while ( DataLen > 0 );
收集整理了一份《2024年最新物联网嵌入式全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升的朋友。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人
都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!