前言
實現功能概要
STM32控制WI-Fi模塊以AT指令TCP透傳方式連接MQTT服務器, 實現MQTT通信控制.
測試准備工作(詳細下載步驟請參考 硬件使用說明 )
一,下載單片機程序
工程目錄: STM32F10xTemplate\Progect
hex文件目錄: STM32F10xTemplate\Progect\Progect
二,安裝APP軟件
三,調整波動開關位置,STM32和Wi-Fi通信
四,
V2.4版本需要短接STM32的PB2和Wi-Fi模塊的RST引腳(為了做項目穩定可靠,請使用單片機硬件復位Wi-Fi)
V2.4版本需要短接STM32的PB2和Wi-Fi模塊的RST引腳
V2.4版本需要短接STM32的PB2和Wi-Fi模塊的RST引腳
V2.5.1版本內部默認PB2連接了Wi-Fi模塊的RST引腳,不需要短接
V2.5.1版本內部默認PB2連接了Wi-Fi模塊的RST引腳,不需要短接
V2.5.1版本內部默認PB2連接了Wi-Fi模塊的RST引腳,不需要短接
開始測試
一.打開手機APP,點擊右上角菜單 "添加設備" ,手動輸入自家路由器密碼.(路由器名稱為自動獲取,不需要用戶填寫)
二.長按PB5大約4S,等待指示燈快閃,松開PB5,Wi-Fi模塊進入配網狀態
三.點擊APP的搜索設備按鈕,開始搜索設備,搜索成功,將自動跳轉到主頁面,並顯示設備
四.單片機控制Wi-Fi連接上MQTT服務器以后,指示燈1S閃耀
五.點擊設備進入,設備控制頁面,頁面顯示當前溫濕度數據,顯示當前設備的狀態
六.遠程控制繼電器吸合
七.遠程控制繼電器斷開
八.請自行控制家電(最大支持10A,注意安全!)
關於程序
整個程序是STM32使用AT指令控制Wi-Fi模塊實現SmartConfig配網和MQTT通信控制
程序的整體結構: https://www.cnblogs.com/yangfengwu/p/11669323.html
程序的按鍵處理: https://www.cnblogs.com/yangfengwu/p/11669354.html
串口接收數據 : https://www.cnblogs.com/yangfengwu/p/11669373.html
配置AT指令模板(阻塞版): https://www.cnblogs.com/yangfengwu/p/11673439.html
配置AT指令模板(非阻塞版): https://www.cnblogs.com/yangfengwu/p/11674814.html
SmartConfig實現部分
一,AT指令配置模塊啟動SmartConfig的程序處理模板是:配置AT指令模板(阻塞版)
二,按鍵按下3S以后 變量 SmartConfigFlage = 1;
定時器里面開始控制 指示燈100Ms閃耀
三,AT指令控制Wi-Fi模塊執行SmartConfig 配網程序部分
三,SmartConfig執行流程-連接路由器
實際上啟用SmartConfig指令是 AT+CWSTARTSMART=3\r\n
最后的參數 1-SmartConfig配網 2-微信Airkiss配網 3-SmartConfig配網+微信Airkiss配網
下面進入了 while(1) 循環 我設置的30S超時
實際上此時Wi-Fi模塊正在監聽APP在空氣中發出的無線信號
下圖只要執行了搜索設備,APP就在不停的發出無線信號
Wi-Fi模塊接收到APP發出的路由器信息以后,就會根據信息去連接路由器
Wi-Fi模塊連接上了路由器以后便會返回 WIFI CONNECTED 和 WIFI GOT IP
注:只要配網一次,以后Wi-Fi模塊便會自動連接此路由器,不需要重復配網!
四,SmartConfig執行流程-等待路由器把自己的MAC信息返回給APP
為了讓APP確定Wi-Fi模塊確實連接上了路由器,Wi-Fi連接上路由器以后
需要返回給APP自己的MAC地址和自己連接路由器后分得的IP地址
所以延時了5S時間,讓Wi-Fi模塊把信息發給APP
下圖中,顯示的就是所配網的Wi-Fi模塊的MAC地址信息
當然MAC地址很有用(全球唯一),通信的時候可以用來區分設備.
MQTT實現部分
一,前言
對於初學者而言,如果不了解MQTT,可先看后面的關於MQTT的教程,看會以后
再來看此部分!
MQTT處理,采用官方C語言MQTT包+本人二次封裝.(方便大家快速的移植使用)
Wi-Fi模塊發布的主題: device/設備MAC
Wi-Fi模塊訂閱的主題: user/設備MAC
APP通過SmartConfig獲取Wi-Fi的MAC,然后設置
訂閱的主題:device/設備MAC
發布的主題:user/設備MAC
二,連接TCP服務器(MQTT服務器)
AT指令配置模塊連接TCP的程序處理模板是:配置AT指令模板(非阻塞版)
配置Wi-Fi模塊連接TCP服務器是使用的 "AT+SAVETRANSLINK=1,\"%s\",%s,\"TCP\"\r\n",IP,Port
這個指令配置好以后,Wi-Fi模塊便是透傳模式,而且是自動連接
(串口接收的數據,自動發給TCP服務器)
(從TCP服務器接收的數據自動發給串口)
三,連接MQTT
SendConfigFunction(NULL,FunctionParseConnectMqtt,NULL,NULL,FunctionParseConnectMqttAck,0);break;
打包發送連接MQTT服務器是這個函數 FunctionParseConnectMqtt
注:調用MqttConnectMqtt函數以后,最終打包完成后的數據存儲在 MqttSendData數組里面
故:在最后的時候是調用 UsartOutStr(MqttSendData,MainLen);
四,判斷是夠連接成功
FunctionParseConnectMqttAck
具體咱再后面說怎么判斷的,這個涉及到官方MQTT庫的數據處理方式
五,訂閱主題
SendConfigFunction(NULL,FunctionParseMqttSubscribe,NULL,NULL,FunctionParseMqttSubscribeAck,CompareValue);break;
訂閱主題看這個函數 FunctionParseMqttSubscribe
此函數一次性可以訂閱多個主題,列如訂閱兩個主題
char SubTopic1[10]="aaaaa";
char SubTopic2[10]="bbbbb";
MQTTString MQTTStringSubTopic[2] = MQTTString_initializer;
int MQTTStringSubTopicQos[2] = {0,1};
MQTTStringSubTopic[0].cstring = SubTopic1;
MQTTStringSubTopic[1].cstring = SubTopic2;
MainLen = MqttSubscribe(MQTTStringSubTopic,MQTTStringSubTopicQos,2,0,1);
UsartOutStr(MqttSendData,MainLen);
注:我設置的一次性最多訂閱3個主題
定義的主題個數大於了此變量,用戶需要增大此變量
實際訂閱的主題個數 <= MaxSubTopicCount
六,發布消息
6.1,定義發布的主題變量
MQTTPublishTopicStruct MqttPublishTopicStruct = MQTTPublishTopicStruct_initializer;
該變量在主函數中定義
6.2,使用該變量
打包數據函數 MqttPublish
6.3,定義多個發布的主題變量
char Publish1[10]="qqqqq";//發布的主題
char Publish1Msg[8]="11223344";//發布的主題,攜帶的消息
char Publish2[10]="wwww";//發布的主題
char Publish2Msg[6]="000000";//發布的主題,攜帶的消息
MQTTPublishTopicStruct MqttPublishTopicStruct1 = MQTTPublishTopicStruct_initializer;
MQTTPublishTopicStruct MqttPublishTopicStruct2 = MQTTPublishTopicStruct_initializer;
MqttPublishTopicStruct1.topicName.cstring = Publish1;// 發布的主題
MainLen = MqttPublish(MqttPublishTopicStruct1,Publish1Msg,8);
UsartOutStr(MqttSendData,MainLen);//發送消息
MqttPublishTopicStruct1.topicName.cstring = Publish2;// 發布的主題
MainLen = MqttPublish(MqttPublishTopicStruct2,Publish2Msg,6);
UsartOutStr(MqttSendData,MainLen);//發送消息
七,處理MQTT接收的消息
7.1 判斷MQTT返回的是什么數據都是調用 MQTTPacket_read函數做判斷
7.2注意這個函數填入的第三個參數 (函數)
7.3 官方提供的MQTT庫,所有的數據判斷處理都是利用該函數
該函數的寫法固定
注意:由於模塊配置了透傳模式,Wi-Fi接收的數據直接通過串口發給了單片機
單片機接收的數據存在了 Usart1ReadBuff 數組里面
上面的函數中 才會寫 memcpy(buf,&Usart1ReadBuff[MqttAnalyzeStruct.Len],count);
7.4
if(MQTTPacket_read(MqttAnalyzeStruct.buff,MQTTAnalyzeBuffLen, transport_getdata) == XXXXX)
實際上 MQTTPacket_read函數 就是利用 transport_getdata函數提取數據
然后把提取的數據存在傳進來的 buff 里面
如果想具體提取數據則
八,心跳包
8.1 實際上 客戶端發送的心跳包數據是 C0 00 服務器返回的是 D0 00
8.2 但是對於沒有監聽過協議的用戶,可調用 MQTTSerialize_pingreq 獲取心跳包數據
APP程序
APP目錄部分說明