当前位置:   article > 正文

STM32+ESP8266连接阿里云物联网平台_stm32,esp8266和阿里云

stm32,esp8266和阿里云

一、准备

硬件:

PC电脑、无线节点模块、ST-LINK仿真器、MiniUSB线

软件:

阿里云物联网平台、KEIL、串口工具

二、原理

2.1、建立连接的流程

与阿里云物联网平台的方式主要是MQTT协议,首先通过无线模块的AT指令与物联网平台建立TCP连接,随后向物联网平台发送MQTT连接请求报文,在物联网回复连接确认报文后,则成功建立MQTT连接,随后向平台发送订阅用于属性设置的主题的请求报文,成功订阅后进入下一步。需要注意的是,MQTT连接需要定时发送心跳报文,不然会超时断开连接。

2.2、串口

在本次演示中使用了两个串口分别是 USART2 和 USART3 串口,其中串口2用于打印运行信息,串口3用于向阿里云物联网平台发送MQTT协议报文和接收物联网平台的回复报文。

三、代码分析

3.1、首先实现对ESP8266模块的操作,主要利用模块底板上STM32的串口3与ESP8266 串口进行通信,发送AT指令的方式控制Wifi模块,包括Wifi模块配置、Wifi连接热点、Wifi使用指定协议连接到服务器等功能的实现。

  1. /**
  2. * 功能:检查ESP8266是否正常
  3. * 参数:None
  4. * 返回值:ESP8266返回状态
  5. * 非0 ESP8266正常
  6. * 0 ESP8266有问题
  7. */
  8. uint8_t ESP8266_Check(void)
  9. {
  10. uint8_t check_cnt=5;
  11. while(check_cnt--)
  12. {
  13. memset(ESP8266_rxbuf,0,sizeof(ESP8266_rxbuf)); //清空接收缓冲
  14. ESP8266_ATSendString("AT\r\n"); //发送AT握手指令
  15. if(FindStr((char*)ESP8266_rxbuf,"OK",200) != 0)
  16. {
  17. return 1;
  18. }
  19. }
  20. return 0;
  21. }
  22. /**
  23. * 功能:初始化ESP8266
  24. * 参数:None
  25. * 返回值:初始化结果,非0为初始化成功,0为失败
  26. */
  27. uint8_t ESP8266_Init(void)
  28. {
  29. //清空发送和接收数组
  30. memset(ESP8266_txbuf,0,sizeof(ESP8266_txbuf));
  31. memset(ESP8266_rxbuf,0,sizeof(ESP8266_rxbuf));
  32. ESP8266_ExitUnvarnishedTrans(); //退出透传
  33. delay_ms(500);
  34. ESP8266_ATSendString("AT+RST\r\n");
  35. delay_ms(800);
  36. if(ESP8266_Check()==0) //使用AT指令检查ESP8266是否存在
  37. {
  38. return 0;
  39. }
  40. memset(ESP8266_rxbuf,0,sizeof(ESP8266_rxbuf)); //清空接收缓冲
  41. ESP8266_ATSendString("ATE0\r\n"); //关闭回显
  42. if(FindStr((char*)ESP8266_rxbuf,"OK",500)==0) //设置不成功
  43. {
  44. return 0;
  45. }
  46. return 1; //设置成功
  47. }
  48. /**
  49. * 功能:恢复出厂设置
  50. * 参数:None
  51. * 返回值:None
  52. * 说明:此时ESP8266中的用户设置将全部丢失回复成出厂状态
  53. */
  54. void ESP8266_Restore(void)
  55. {
  56. ESP8266_ExitUnvarnishedTrans(); //退出透传
  57. delay_ms(500);
  58. ESP8266_ATSendString("AT+RESTORE\r\n"); //恢复出厂
  59. }
  60. /**
  61. * 功能:连接热点
  62. * 参数:
  63. * ssid:热点名
  64. * pwd:热点密码
  65. * 返回值:
  66. * 连接结果,非0连接成功,0连接失败
  67. * 说明:
  68. * 失败的原因有以下几种(UART通信和ESP8266正常情况下)
  69. * 1. WIFI名和密码不正确
  70. * 2. 路由器连接设备太多,未能给ESP8266分配IP
  71. */
  72. uint8_t ESP8266_ConnectAP(char* ssid,char* pswd)
  73. {
  74. uint8_t cnt=5;
  75. while(cnt--)
  76. {
  77. memset(ESP8266_rxbuf,0,sizeof(ESP8266_rxbuf));
  78. ESP8266_ATSendString("AT+CWMODE_CUR=1\r\n"); //设置为STATION模式
  79. if(FindStr((char*)ESP8266_rxbuf,"OK",200) != 0)
  80. {
  81. break;
  82. }
  83. }
  84. if(cnt == 0)
  85. return 0;
  86. cnt=2;
  87. while(cnt--)
  88. {
  89. memset(ESP8266_txbuf,0,sizeof(ESP8266_txbuf));//清空发送缓冲
  90. memset(ESP8266_rxbuf,0,sizeof(ESP8266_rxbuf));//清空接收缓冲
  91. sprintf((char*)ESP8266_txbuf,"AT+CWJAP_CUR=\"%s\",\"%s\"\r\n",ssid,pswd);//连接目标AP
  92. ESP8266_ATSendString((char*)ESP8266_txbuf);
  93. if(FindStr((char*)ESP8266_rxbuf,"OK",8000)!=0) //连接成功且分配到IP
  94. {
  95. return 1;
  96. }
  97. }
  98. return 0;
  99. }
  100. //开启透传模式
  101. static uint8_t ESP8266_OpenTransmission(void)
  102. {
  103. //设置透传模式
  104. uint8_t cnt=2;
  105. while(cnt--)
  106. {
  107. memset(ESP8266_rxbuf,0,sizeof(ESP8266_rxbuf));
  108. ESP8266_ATSendString("AT+CIPMODE=1\r\n");
  109. if(FindStr((char*)ESP8266_rxbuf,"OK",200)!=0)
  110. {
  111. return 1;
  112. }
  113. }
  114. return 0;
  115. }
  116. /**
  117. * 功能:使用指定协议(TCP/UDP)连接到服务器
  118. * 参数:
  119. * mode:协议类型 "TCP","UDP"
  120. * ip:目标服务器IP
  121. * port:目标是服务器端口号
  122. * 返回值:
  123. * 连接结果,非0连接成功,0连接失败
  124. * 说明:
  125. * 失败的原因有以下几种(UART通信和ESP8266正常情况下)
  126. * 1. 远程服务器IP和端口号有误
  127. * 2. 未连接AP
  128. * 3. 服务器端禁止添加(一般不会发生)
  129. */
  130. uint8_t ESP8266_ConnectServer(char* mode,char* ip,uint16_t port)
  131. {
  132. uint8_t cnt;
  133. ESP8266_ExitUnvarnishedTrans(); //多次连接需退出透传
  134. delay_ms(500);
  135. //连接服务器
  136. cnt=2;
  137. while(cnt--)
  138. {
  139. memset(ESP8266_txbuf,0,sizeof(ESP8266_txbuf));//清空发送缓冲
  140. memset(ESP8266_rxbuf,0,sizeof(ESP8266_rxbuf));//清空接收缓冲
  141. sprintf((char*)ESP8266_txbuf,"AT+CIPSTART=\"%s\",\"%s\",%d\r\n",mode,ip,port);
  142. ESP8266_ATSendString((char*)ESP8266_txbuf);
  143. if(FindStr((char*)ESP8266_rxbuf,"CONNECT",500) !=0 )
  144. {
  145. break;
  146. }
  147. }
  148. if(cnt == 0)
  149. return 0;
  150. //设置透传模式
  151. if(ESP8266_OpenTransmission()==0) return 0;
  152. //开启发送状态
  153. cnt=2;
  154. while(cnt--)
  155. {
  156. memset(ESP8266_rxbuf,0,sizeof(ESP8266_rxbuf)); //清空接收缓冲
  157. ESP8266_ATSendString("AT+CIPSEND\r\n");//开始处于透传发送状态
  158. if(FindStr((char*)ESP8266_rxbuf,">",200)!=0)
  159. {
  160. return 1;
  161. }
  162. }
  163. return 0;
  164. }
  165. /**
  166. * 功能:主动和服务器断开连接
  167. * 参数:None
  168. * 返回值:
  169. * 连接结果,非0断开成功,0断开失败
  170. */
  171. uint8_t DisconnectServer(void)
  172. {
  173. uint8_t cnt;
  174. ESP8266_ExitUnvarnishedTrans(); //退出透传
  175. delay_ms(500);
  176. while(cnt--)
  177. {
  178. memset(ESP8266_rxbuf,0,sizeof(ESP8266_rxbuf)); //清空接收缓冲
  179. ESP8266_ATSendString("AT+CIPCLOSE\r\n");//关闭链接
  180. if(FindStr((char*)ESP8266_rxbuf,"CLOSED",200)!=0)//操作成功,和服务器成功断开
  181. {
  182. break;
  183. }
  184. }
  185. if(cnt) return 1;
  186. return 0;
  187. }

3.2、根据MQTT协议工作原理,编写MQTT协议驱动文件,包括MQTT协议的连接服务器的打包、订阅/取消订阅数据打包、发布数据打包等功能。

  1. //连接成功服务器回应 20 02 00 00
  2. //客户端主动断开连接 e0 00
  3. const uint8_t parket_connetAck[] = {0x20,0x02,0x00,0x00};
  4. const uint8_t parket_disconnet[] = {0xe0,0x00};
  5. const uint8_t parket_heart[] = {0xc0,0x00};
  6. const uint8_t parket_heart_reply[] = {0xc0,0x00};
  7. const uint8_t parket_subAck[] = {0x90,0x03};
  8. volatile uint16_t MQTT_TxLen;
  9. //MQTT发送数据
  10. void MQTT_SendBuf(uint8_t *buf,uint16_t len)
  11. {
  12. ESP8266_ATSendBuf(buf,len);
  13. }
  14. //发送MQTT心跳
  15. void MQTT_SentHeart()
  16. {
  17. MQTT_SendBuf((uint8_t *)parket_heart,sizeof(parket_heart));
  18. }
  19. //MQTT无条件断开
  20. void MQTT_Disconnect()
  21. {
  22. MQTT_SendBuf((uint8_t *)parket_disconnet,sizeof(parket_disconnet));
  23. }
  24. //MQTT连续服务器的打包函数
  25. uint8_t MQTT_Connect(char *ClientID,char *Username,char *Password)
  26. {
  27. int ClientIDLen = strlen(ClientID);
  28. int UsernameLen = strlen(Username);
  29. int PasswordLen = strlen(Password);
  30. int DataLen;
  31. MQTT_TxLen=0;
  32. //可变报头+Payload 每个字段包含两个字节的长度标识
  33. DataLen = 10 + (ClientIDLen+2) + (UsernameLen+2) + (PasswordLen+2);
  34. //固定报头
  35. //控制报文类型
  36. ESP8266_txbuf[MQTT_TxLen++] = 0x10; //MQTT Message Type CONNECT
  37. //剩余长度(不包括固定头部)
  38. do
  39. {
  40. uint8_t encodedByte = DataLen % 128;
  41. DataLen = DataLen / 128;
  42. // if there are more data to encode, set the top bit of this byte
  43. if ( DataLen > 0 )
  44. encodedByte = encodedByte | 128;
  45. ESP8266_txbuf[MQTT_TxLen++] = encodedByte;
  46. }while ( DataLen > 0 );
  47. //可变报头
  48. //协议名
  49. ESP8266_txbuf[MQTT_TxLen++] = 0; // Protocol Name Length MSB
  50. ESP8266_txbuf[MQTT_TxLen++] = 4; // Protocol Name Length LSB
  51. ESP8266_txbuf[MQTT_TxLen++] = 'M'; // ASCII Code for M
  52. ESP8266_txbuf[MQTT_TxLen++] = 'Q'; // ASCII Code for Q
  53. ESP8266_txbuf[MQTT_TxLen++] = 'T'; // ASCII Code for T
  54. ESP8266_txbuf[MQTT_TxLen++] = 'T'; // ASCII Code for T
  55. //协议级别
  56. ESP8266_txbuf[MQTT_TxLen++] = 4; // MQTT Protocol version = 4
  57. //连接标志
  58. ESP8266_txbuf[MQTT_TxLen++] = 0xc2; // conn flags
  59. ESP8266_txbuf[MQTT_TxLen++] = 0; // Keep-alive Time Length MSB
  60. ESP8266_txbuf[MQTT_TxLen++] = 60; // Keep-alive Time Length LSB 60S心跳包
  61. ESP8266_txbuf[MQTT_TxLen++] = BYTE1(ClientIDLen);// Client ID length MSB
  62. ESP8266_txbuf[MQTT_TxLen++] = BYTE0(ClientIDLen);// Client ID length LSB
  63. memcpy(&ESP8266_txbuf[MQTT_TxLen],ClientID,ClientIDLen);
  64. MQTT_TxLen += ClientIDLen;
  65. if(UsernameLen > 0)
  66. {
  67. ESP8266_txbuf[MQTT_TxLen++] = BYTE1(UsernameLen); //username length MSB
  68. ESP8266_txbuf[MQTT_TxLen++] = BYTE0(UsernameLen); //username length LSB
  69. memcpy(&ESP8266_txbuf[MQTT_TxLen],Username,UsernameLen);
  70. MQTT_TxLen += UsernameLen;
  71. }
  72. if(PasswordLen > 0)
  73. {
  74. ESP8266_txbuf[MQTT_TxLen++] = BYTE1(PasswordLen); //password length MSB
  75. ESP8266_txbuf[MQTT_TxLen++] = BYTE0(PasswordLen); //password length LSB
  76. memcpy(&ESP8266_txbuf[MQTT_TxLen],Password,PasswordLen);
  77. MQTT_TxLen += PasswordLen;
  78. }
  79. //uint8_t cnt=2;
  80. uint8_t wait;
  81. //while(cnt--)
  82. {
  83. memset(ESP8266_rxbuf,0,sizeof(ESP8266_rxbuf));
  84. MQTT_SendBuf(ESP8266_txbuf,MQTT_TxLen);
  85. wait=30;//等待3s时间
  86. while(wait--)
  87. {
  88. //CONNECT
  89. if(ESP8266_rxbuf[0]==parket_connetAck[0] && ESP8266_rxbuf[1]==parket_connetAck[1]) //连接成功
  90. {
  91. return 1;//连接成功
  92. }
  93. delay_ms(100);
  94. }
  95. }
  96. return 0;
  97. }
  98. /*
  99. * MQTT订阅/取消订阅数据打包函数
  100. * topic 主题
  101. * qos 消息等级
  102. * whether 订阅/取消订阅请求包
  103. */
  104. uint8_t MQTT_SubscribeTopic(char *topic,uint8_t qos,uint8_t whether)
  105. {
  106. int topiclen;
  107. int DataLen;
  108. uint8_t cnt;
  109. uint8_t wait;
  110. MQTT_TxLen=0;
  111. cnt=2;
  112. topiclen = strlen(topic);
  113. DataLen = 2 + (topiclen+2) + (whether?1:0);//可变报头的长度(2字节)加上有效载荷的长度
  114. //固定报头
  115. //控制报文类型
  116. if(whether) ESP8266_txbuf[MQTT_TxLen++] = 0x82; //消息类型和标志订阅
  117. else ESP8266_txbuf[MQTT_TxLen++] = 0xA2; //取消订阅
  118. //剩余长度
  119. do
  120. {
  121. uint8_t encodedByte = DataLen % 128;
  122. DataLen = DataLen / 128;
  123. // if there are more data to encode, set the top bit of this byte
  124. if ( DataLen > 0 )
  125. encodedByte = encodedByte | 128;
  126. ESP8266_txbuf[MQTT_TxLen++] = encodedByte;
  127. }while ( DataLen > 0 );
  128. //可变报头
  129. ESP8266_txbuf[MQTT_TxLen++] = 0; //消息标识符 MSB
  130. ESP8266_txbuf[MQTT_TxLen++] = 0x01; //消息标识符 LSB
  131. //有效载荷
  132. ESP8266_txbuf[MQTT_TxLen++] = BYTE1(topiclen);//主题长度 MSB
  133. ESP8266_txbuf[MQTT_TxLen++] = BYTE0(topiclen);//主题长度 LSB
  134. memcpy(&ESP8266_txbuf[MQTT_TxLen],topic,topiclen);
  135. MQTT_TxLen += topiclen;
  136. if(whether)
  137. {
  138. ESP8266_txbuf[MQTT_TxLen++] = qos;//QoS级别
  139. }
  140. while(cnt--)
  141. {
  142. memset(ESP8266_rxbuf,0,sizeof(ESP8266_rxbuf));
  143. MQTT_SendBuf(ESP8266_txbuf,MQTT_TxLen);
  144. wait=30;//等待3s时间
  145. while(wait--)
  146. {
  147. if(ESP8266_rxbuf[0]==parket_subAck[0] && ESP8266_rxbuf[1]==parket_subAck[1]) //订阅成功
  148. {
  149. return 1;//订阅成功
  150. }
  151. delay_ms(100);
  152. }
  153. }
  154. if(cnt) return 1; //订阅成功
  155. return 0;
  156. }
  157. /*
  158. * MQTT发布数据打包函数
  159. * topic 主题
  160. * message 消息
  161. * qos 消息等级
  162. */
  163. uint8_t MQTT_PublishData(char *topic, char *message, uint8_t qos)
  164. {
  165. int topicLength = strlen(topic);
  166. int messageLength = strlen(message);
  167. static uint16_t id=0;
  168. int DataLen;
  169. MQTT_TxLen=0;
  170. //有效载荷的长度这样计算:用固定报头中的剩余长度字段的值减去可变报头的长度
  171. //QOS为0时没有标识符
  172. //数据长度 主题名 报文标识符 有效载荷
  173. if(qos) DataLen = (2+topicLength) + 2 + messageLength;
  174. else DataLen = (2+topicLength) + messageLength;
  175. //固定报头
  176. //控制报文类型
  177. ESP8266_txbuf[MQTT_TxLen++] = 0x30; // MQTT Message Type PUBLISH
  178. //剩余长度
  179. do
  180. {
  181. uint8_t encodedByte = DataLen % 128;
  182. DataLen = DataLen / 128;
  183. // if there are more data to encode, set the top bit of this byte
  184. if ( DataLen > 0 )
  185. encodedByte = encodedByte | 128;
  186. ESP8266_txbuf[MQTT_TxLen++] = encodedByte;
  187. }while ( DataLen > 0 );
  188. ESP8266_txbuf[MQTT_TxLen++] = BYTE1(topicLength); //主题长度MSB
  189. ESP8266_txbuf[MQTT_TxLen++] = BYTE0(topicLength); //主题长度LSB
  190. memcpy(&ESP8266_txbuf[MQTT_TxLen],topic,topicLength); //拷贝主题
  191. MQTT_TxLen += topicLength;
  192. //报文标识符
  193. if(qos)
  194. {
  195. ESP8266_txbuf[MQTT_TxLen++] = BYTE1(id);
  196. ESP8266_txbuf[MQTT_TxLen++] = BYTE0(id);
  197. id++;
  198. }
  199. memcpy(&ESP8266_txbuf[MQTT_TxLen],message,messageLength);
  200. MQTT_TxLen += messageLength;
  201. MQTT_SendBuf(ESP8266_txbuf,MQTT_TxLen);
  202. return MQTT_TxLen;
  203. }

3.3、最后在main.c中编写函数调用这两个文件里的函数以实现与阿里云物联网平台的MQTT协议连接。

  1. //MQTT初始化函数
  2. void ES8266_MQTT_Init(void)
  3. {
  4. uint8_t status=0;
  5. //初始化
  6. if(ESP8266_Init())
  7. {
  8. user_main_info("ESP8266初始化成功!\r\n");
  9. status++;
  10. }
  11. else Enter_ErrorMode(0);
  12. //连接热点
  13. if(status==1)
  14. {
  15. if(ESP8266_ConnectAP(WIFI_NAME,WIFI_PASSWD))
  16. {
  17. user_main_info("ESP8266连接热点成功!\r\n");
  18. status++;
  19. }
  20. else Enter_ErrorMode(1);
  21. }
  22. //连接阿里云IOT服务器
  23. if(status==2)
  24. {
  25. if(ESP8266_ConnectServer("TCP",MQTT_BROKERADDRESS,1883)!=0)
  26. {
  27. user_main_info("ESP8266连接阿里云服务器成功!\r\n");
  28. status++;
  29. }
  30. else Enter_ErrorMode(2);
  31. }
  32. //登陆MQTT
  33. if(status==3)
  34. {
  35. if(MQTT_Connect(MQTT_CLIENTID, MQTT_USARNAME, MQTT_PASSWD) != 0)
  36. {
  37. user_main_info("ESP8266阿里云MQTT登陆成功!\r\n");
  38. status++;
  39. }
  40. else Enter_ErrorMode(3);
  41. }
  42. //订阅主题
  43. if(status==4)
  44. {
  45. if(MQTT_SubscribeTopic(MQTT_SUBSCRIBE_TOPIC,0,1) != 0)
  46. {
  47. user_main_info("ESP8266阿里云MQTT订阅主题成功!\r\n");
  48. }
  49. else Enter_ErrorMode(4);
  50. }
  51. }

四、写在最后

为了大家能够更好的理解代码,本文只写出了用于建立连接的关键函数以供大家参考,其余细节就不在此赘述了,祝你们在2024年前途似锦,事业高飞~

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/weixin_40725706/article/detail/470148
推荐阅读
相关标签
  

闽ICP备14008679号