【单片机笔记】STM32+ESP8266通过AT指令WIFI连接阿里云MQTT服务器

2023-11-13

        上一篇使用USB转串口的方式通过ESP8266wifi模块的方式成功连接上了阿里云,现在就要通过单片机来替换电脑上位机了,这样单片机自动的去调用并发送串口数据更加方便,也更加符合一个产品的开发。板载的传感器有NTC温度,光强,这两个主要用来设备上传到平台,另外一个是RGB的灯,这个主要是用来平台下发设备的接收和解析。这里为了直观我直接用串口打印出来。只要数据部分对了其他的都好说。
网页上的运行状态图

温度曲线图

光强曲线图

颜色色值曲线图,这里其实是通过单片机随机函数生成的一个数据,所以变化也是比较大的,为了直观我也把这个值上传到平台。

平台下发数据、设备解析测试。

这里要注意一下,最开始我通过strstr方法来找出关键字“hue”,但是测了下不行,后来找到原因,估计是平台下发的josn数据包中含有多个0,比方说设备收到的是“123456\0789”,这里的‘\0’其实hex就是0x00,也正好是字符串的结尾标识符,这样通过strstr要找出“789”就找不到的,因为再者之前字符串就认为已经是断了,后来自己写了一个查找函数,解决!

网络部分和MQTT部分我都做了封装,非常方便参考和移植,不多说直接贴上代码:

main.c部分

#include "fy_includes.h"#include "fy_tlink.h"/*晶振使用的是16M 其他频率在system_stm32f10x.c中修改使用printf需要在fy_includes.h修改串口重定向为#define PRINTF_USART USART1 */ _typdef_adc _adc;u16 adc_light;u16 adc_ntc;u16 battery;float temperature;u8 led_sta;u8 sta;void Adc_GetValue(void); u8 txbuf[256];u8 rxbuf[256]; char mqtt_message[200];  void Mqtt_Progress(u8 *buf,u16 len); void UsartTrance(void){    while(1)    {       Led_Tog();      Delay_ms(500);  }} int main(void){  u8 cnt_2s=0;    u16 cnt_5s=0;   NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);    Systick_Configuration();    Led_Configuration();//   UsartTrance();  Key_Configuration();    Adc_Configuration();    Adc_DMA_Configuration((u32)&_adc.buf,ADC_FILTER*ADC_CH_MAX);    Usart1_Configuration(115200);   Usart2_Configuration(115200);   Oled_Configuration();   Usart1_SendString("  usart1 is ok!\r\n");// Usart2_SendString("  usart2 is ok!\r\n");   Delay_ms(200);  //  UsartTrance();  sta=0;      //检查ESP8266    if(_net.Init(rxbuf,sizeof(rxbuf),txbuf,sizeof(txbuf))!=0){       Oled_ShowString(0,0*16,"Net Init OK!",8,16,1);//        printf("Net Init OK!\r\n");     sta++;  }   else{       Oled_ShowString(0,0*16,"Net Init Error!",8,16,1);//     printf("Net Init Error!\r\n");      sta=0;  }   Oled_RefreshGram();     if(sta==1){     //连接热点      if(_net.ConnectAP("ssid","password")!=0){           Oled_ShowString(0,1*16,"Conncet AP OK!",8,16,1);//          printf("Conncet AP OK!\r\n");           sta++;      }       else {          Oled_ShowString(0,1*16,"Conncet AP Error!",8,16,1);//           printf("Conncet AP Error!\r\n");            sta=0;      }       Oled_RefreshGram(); }       if(sta==2){         //连接TCP     if(_net.ConnectServer("TCP","a1ugBNniFGU.iot-as-mqtt.cn-shanghai.aliyuncs.com",1883)!=0) {          Oled_ShowString(0,2*16,"Conet Server OK!",8,16,1);//            printf("Conncet Server OK!\r\n");           sta++;      }       else{           Oled_ShowString(0,2*16,"Server Error!",8,16,1);//           printf("Conncet Server Error!\r\n");                sta=0;      }       Oled_RefreshGram(); }       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,0*16,"Subscribe Error!",8,16,1);//            printf("SubscribeTopic Error!\r\n");        }       Oled_RefreshGram();         }   Delay_ms(1000); Oled_Clear();               Oled_ShowString(32,0*16,"Mars Tech",8,16,1);    Oled_ShowString(0,1*16," VCC: 0000 mV",8,16,1); Oled_ShowString(0,2*16,"Temp: 00.0 C",8,16,1);  Oled_ShowString(0,3*16," 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(8*6,1*16,battery,4,8,16);                      Oled_ShowNum(8*6,2*16,(u8)temperature,2,8,16);          Oled_ShowNum(8*6+3*8,2*16,(u16)(temperature*10)%10,1,8,16);                     Oled_ShowNum(8*6,3*16,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_FILTER*3300/4095*11;     //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 00const 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 );         //可变报头    _mqtt.txbuf[_mqtt.txlen++] = 0;               //消息标识符 MSB    _mqtt.txbuf[_mqtt.txlen++] = 0x01;           //消息标识符 LSB //有效载荷    _mqtt.txbuf[_mqtt.txlen++] = BYTE1(topiclen);//主题长度 MSB    _mqtt.txbuf[_mqtt.txlen++] = BYTE0(topiclen);//主题长度 LSB    memcpy(&_mqtt.txbuf[_mqtt.txlen],topic,topiclen);    _mqtt.txlen += topiclen;        if(whether)    {        _mqtt.txbuf[_mqtt.txlen++] = qos;//QoS级别    }      u8 cnt=2;   u8 wait;    while(cnt--)    {       memset(_mqtt.rxbuf,0,_mqtt.rxlen);      Mqtt_SendBuf(_mqtt.txbuf,_mqtt.txlen);      wait=30;//等待3s时间        while(wait--)       {           if(_mqtt.rxbuf[0]==parket_subAck[0] && _mqtt.rxbuf[1]==parket_subAck[1]) //订阅成功                     {               return 1;//订阅成功         }           Delay_ms(100);                  }   }   if(cnt) return 1;   //订阅成功  return 0;} //MQTT发布数据打包函数//topic   主题 //message 消息//qos     消息等级 static u8 PublishData(char *topic, char *message, u8 qos){      int topicLength = strlen(topic);        int messageLength = strlen(message);         static u16 id=0;  int DataLen;    _mqtt.txlen=0;  //有效载荷的长度这样计算:用固定报头中的剩余长度字段的值减去可变报头的长度  //QOS为0时没有标识符   //数据长度             主题名   报文标识符   有效载荷    if(qos)    DataLen = (2+topicLength) + 2 + messageLength;           else   DataLen = (2+topicLength) + messageLength;        //固定报头    //控制报文类型    _mqtt.txbuf[_mqtt.txlen++] = 0x30;    // MQTT Message Type PUBLISH      //剩余长度  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++] = BYTE1(topicLength);//主题长度MSB    _mqtt.txbuf[_mqtt.txlen++] = BYTE0(topicLength);//主题长度LSB  memcpy(&_mqtt.txbuf[_mqtt.txlen],topic,topicLength);//拷贝主题    _mqtt.txlen += topicLength;           //报文标识符    if(qos)    {        _mqtt.txbuf[_mqtt.txlen++] = BYTE1(id);        _mqtt.txbuf[_mqtt.txlen++] = BYTE0(id);        id++;    } memcpy(&_mqtt.txbuf[_mqtt.txlen],message,messageLength);    _mqtt.txlen += messageLength;           Mqtt_SendBuf(_mqtt.txbuf,_mqtt.txlen);    return _mqtt.txlen;} static void SentHeart(void){ Mqtt_SendBuf((u8 *)parket_heart,sizeof(parket_heart));} static void Disconnect(void){   Mqtt_SendBuf((u8 *)parket_disconnet,sizeof(parket_disconnet));} static void Mqtt_SendBuf(u8 *buf,u16 len){  #ifdef _DEBUG_MQTT  Usart1_SendBuf(buf,len);    #endif  Usart2_SendBuf(buf,len);}    /*********************************************END OF FILE********************************************/

mqtt.h部分

#ifndef __FY_MQTT_H_#define __FY_MQTT_H_ #include "fy_includes.h"   typedef struct{    u8 *rxbuf;u16 rxlen;    u8 *txbuf;u16 txlen;    void (*Init)(u8 *prx,u16 rxlen,u8 *ptx,u16 txlen);  u8 (*Connect)(char *ClientID,char *Username,char *Password);    u8 (*SubscribeTopic)(char *topic,u8 qos,u8 whether);    u8 (*PublishData)(char *topic, char *message, u8 qos);  void (*SendHeart)(void);    void (*Disconnect)(void);}_typdef_mqtt; extern _typdef_mqtt _mqtt; #endif 

完整工程:https://download.csdn.net/download/qq997758497/11239490

By Urien 2019年6月13日 14:59:29

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

【单片机笔记】STM32+ESP8266通过AT指令WIFI连接阿里云MQTT服务器 的相关文章

  • background-position设置百分比“失效了”!!

    1 background position设置百分比的计算原理 当指定百分比值的时候 实际上执行了以下的计算公式 该公式可以用数学方式定义图片和容器相对位置重合 这是当background size auto 时 百分比有效 contain
  • 组装一台1u服务器

    1 服务器的内存条都是带校验功能的 2 服务器cpu可以多个 一般电脑只有一个
  • NAS个人云存储服务器搭建

    NAS Network Attached Storage 网络附属存储 通过网络提供数据访问服务 可以理解为长时间连网的存储设备 其功能基本和市面上的各种云盘相似 它以数据为中心 将存储设备与服务器彻底分离 集中管理数据 从而释放带家 提高
  • layui 时间选择器laydate开始时间结束时间限制,及设置默认时分秒

    这个时间控件实现了 开始时间和结束时间都不得超过当前时间 结束时间大于开始时间并且小于当前时间 开始时间默认时分秒为00 00 00 结束时间默认时分秒为23 59 59 其他官方自带功能 项目中的需求是 结束时间要大于开始时间 包括时分秒
  • 全网页截图教程,如何截图截全屏

    系统自身的截屏快捷键 台式键盘的电脑 全屏 Ctrl Print Screen 当前窗口 Alt Print Screen 笔记本截图快捷键 FN Prt sc 浏览器自带的 非常好用 在浏览器打开要截取全网页为图片的那个网页 打开那个网页
  • Unity—常用API(续Time类)

    今天整理了Time类 一张很有意思的理解API的图 此图灵感来源于 如何理解API API 是如何工作的 仁杰兄的博客 CSDN博客 api 目录 Time 练习 使用Text制作倒计时预制体 在Update每帧执行的方法中 个别语句实现指

随机推荐

  • ubuntu学习(四)----文件写入操作编程

    1 write函数的详解 ssize t write int fd const void buf size t count 参数说明 fd 是文件描述符 write所对应的是写 即就是1 buf 通常是一个字符串 需要写入的字符串 coun
  • 浅析芝麻信用分征信体系

    在互联网大佬中 能将未雨绸缪之功力炼至极致的非马云莫属了 其所扔下的每一个棋子都会前思三招 后推九步 每一步商业布局都像是一枚运气极佳的的种子总能找到温润肥沃的土壤而后破土而发 十年遮风 百年纳凉 马老板曾苦心孤诣费尽心机地想把淘宝由一家广
  • 教你自己搭建一个ip池(绝对超好用!!!!)

    随着我们爬虫的速度越来越快 很多时候 有人发现 数据爬不了啦 打印出来一看 不返回数据 而且还甩一句话 是不是很熟悉啊 要想想看 人是怎么访问网站的 发请求 对 那么就会带有 request headers 那么当你疯狂请求别人的网站时候
  • .NET/C#下html转PDF,PDF加水印,PDF转图片

    一 添加OpenHtmlToPdf iTextSharp O2S Components PDFRender4NET引用 1 OpenHtmlToPdf 是一个 NET库 开源的 用于将HTML文档呈现为PDF格式 github地址 http
  • hibernate derby 配置文件

    hibernate cfg xml
  • Python数据分析——上海市二手房价格分析

    自学数据分析与机器学习已有两月 近期房价问题引人深思 即兴做个上海市房价的数据分析小项目 上网一查上海市新楼盘价格 高的不忍直视 索性退而求其次 分析上海二手房的价格 一 数据收集 常规做法是编写网络爬虫程序 爬取相关网站的数据信息 捷径是
  • 如何把glb格式模型gltf格式模型导入3dmax和C4D,U3D,UE4这些主流软件中

    咱有时候去glbxz com添加链接描述 官网下载免费glb格式模型 gltf模型下载时候是没有通用格式 例如fbx obj 这个时候3dmax和C4D直接打开导入是不行的 也可以制作glb模型 扣扣 424081801 这个时候 咱们用
  • AI工具汇总

    大家好 我是可夫小子 关注AIGC 读书和自媒体 ChatGPT已经火了这么久 我也写不了少玩ChatGPT的方法 昨天OpenAI又推出了苹果手机的APP 我也介绍下载和安装的攻略 但根据读者反馈 仍然还是有许多同学没能用上 今天我就把我
  • VS2015远程编译Linux项目

    用VS2015开发Linux程序详细教程 配置篇 crazytea的博客 CSDN博客 linux 程序开发VS2015推出了跨平台开发 其中包括了对Linux程序开发的支持 最近刚好需要开发Linux程序 对其进行了一些研究 首先介绍下涉
  • redis的安装与配置

    第一章 redis 1 1redis的概述 1 2关系型数据库与非关系数据库 1 3关系型数据库和非关系型数据库区别 1 4redis优点与缺点 第二章redis的安装 2 1 YUM安装 2 2下载编译安装 2 2 1关闭防火墙 2 2
  • element左侧导航栏el-menu,菜单栏文字超出不折行问题

    在CSS样式中加上这些样式就可以了 el submenu title display flex align items center el submenu title span white space normal word break b
  • C# 面向对象05 StringBuilder的用法

    好处 相比普通的 string处理 提高了字符串的处理速度 注意点 使用时需要使用对象的方式 StringBuilder world new StringBuilder using System using System Diagnosti
  • SimpleDateFormat模式字符串格式

    SimpleDateFormat模式字符串 new SimpleDateFormat String parm parm为一个字符串 表示格式 时间模式 字母 时间元素 表示 示例 y 年 Year 1996 96 M 年中的月份 Month
  • PyTorch基础之模型保存与重载模块、可视化模块讲解(附源码)

    训练模型时 在众多训练好的模型中会有几个较好的模型 我们希望储存这些模型对应的参数值 避免后续难以训练出更好的结果 同时也方便我们复现这些模型 用于之后的研究 PyTorch提供了模型的保存与重载模块 包括torch save 和torch
  • jvm内存模型与垃圾回收(下)

    上篇地址 jvm内存模型与垃圾回收 上 1 垃圾回收相关算法 标记清除 标记整理 复制 这三个看上面的文章 1 1 分代收集算法 将不同生命周期的对象采用不同的收集方式 以便提高回收效率 一般是将Java堆分为新生代和老年代 这样可以根据各
  • 接口与实现

    public interface Computable 接口体包含常量和抽象方法 没有普通方法 int MAX 46 常量 int f int x 抽象方法 默认权限friendly public class China implement
  • 【C++】使用Windows操作系统的API在控制台输出绿色的文本

    2023年8月21日 周一下午 include
  • linux桌面远程控制,在LINUX下远程控制WIN桌面

    rdesktop 是个在veket下访问视窗系统 远程桌面的客户端程式 当前 rdesktop 所支持的 视窗系统 系列版本包括 NT 2000 XP 和2003 通过使用 rdesktop 所实现的远程桌面协议 RDP 你能在veket系
  • 企业微信群机器人开发

    准备工作 已经注册了有效的企业微信账号 并且在客户端上已经登录 现有或者新建有效的包含多名企业微信成员的群聊 创建群聊机器人 右键群聊 gt 管理聊天信息 gt 添加群机器人 使用群机器人 在终端某个群组添加机器人之后 创建者可以在机器人详
  • 【单片机笔记】STM32+ESP8266通过AT指令WIFI连接阿里云MQTT服务器

    上一篇使用USB转串口的方式通过ESP8266wifi模块的方式成功连接上了阿里云 现在就要通过单片机来替换电脑上位机了 这样单片机自动的去调用并发送串口数据更加方便 也更加符合一个产品的开发 板载的传感器有NTC温度 光强 这两个主要用来