兩塊STM32分別控制一塊ESP8266,實現兩機信息交互
1.前言
2019年上半年,為了准備一個機器人比賽,就去研究了一下ESP8266 WIFI模塊。模塊本身已經被封裝得很好了,可是在搭載到單片機上出現了很多問題,借此機會總結一下:單片機配置流程和中間可能遇到的一些坑。
2.思路
-
做什么:單片機 控制 ESP8266。
-
怎么做:a.STM32核心板提供ESP8266所需要的硬件環境; b.程序中寫好特定格式的命令,通過串口發送給ESP8266。
-
技術路線:
3.硬件
我用的是下面這塊裸板,為的是能直接焊在板子上,節省空間。
根據模塊提供的手冊,對引腳進行配置,一般來說我們使用運行模式。
模式 | CH_PD(EN) | RST | GPIO15 | GPIO0 | GPIO2 | TXD0 |
---|---|---|---|---|---|---|
運行模式 | 高 | 高 | 低 | 高 | 高 | 高 |
下載模式 | 高 | 高 | 低 | 低 | 高 | 高 |
對應的電路設計可以參考下面這張圖
注意:
1.模塊最終能有效運行的關鍵因素就是上圖中的引腳是否電平配置到位,如果模塊接入電路,通
電時,模塊上面自帶的藍色LED小燈沒有閃爍一下,那就說明模塊硬件環境沒有配置好(或者
模塊燒毀)。建議挨個檢查引腳的電壓,可能遇到的情況有a.電壓不足b.完全沒有電壓c.過
壓。那么你就要根據電路圖,原理圖找找是否存在斷路,分壓和短路等情況了。
2.我的是裸板,我的是裸板,沒有VCC,GND,RX,TX等六個引腳直接引出。像下面這樣:
圖中的裸板已經搭載在一個配套電路上了,實現了我上面說的電路設計,拿來通上電直接用,對
剛接觸的人來說特別友好,不過我是為了集成化,節約空間,就直接拿裸板焊自己板子上,所以
需要自己配置引腳(特別坑,當初也是直接VCC,GND直接懟,結果沒卵用),所以你買的什么
模塊,就看店家提供的資料,別人的不一定適合自己。
3.模塊GND要和STM32的GND相連。
4.軟件
先利用電腦通過串口發送信息給 ESP8266模塊,熟悉一下流程。
選擇正確的 COM 號(WIFI與你電腦相連的串口),然后設置波特率為 115200,勾選發送新行 (必選!即自動添加回車換行功能)然后發送 AT指令 到 ESP8266模塊。
然后理清楚一下服務器(主機)和客戶端(從機)分別需要執行什么AT指令。
服務器(主機) | AT指令 | 客戶端(從機) | AT指令 |
---|---|---|---|
檢測模塊是否在線 | AT | 檢測模塊是否在線 | AT |
修改服務器WIFI名和密碼 | AT+CWSAP="ALIENTEK","15902020353",11,3 | 設置 WIFI 模式1 | AT+CWMODE=1 |
設置 WIFI 模式2 | AT+CWMODE=2 | 重啟生效 | AT+RST |
重啟生效 | AT+RST | 連接服務器的WIFI | AT+CWJAP="ALIENTEK","15902020353" |
建立服務器指令 | AT+CIPSERVER=1,8089 | 連接服務器 | AT+CIPSTART="TCP","你的服務器 IP 地址",8089 |
獲取服務器IP地址 | AT+CIFSR | 開啟透傳模式 | AT+CIPMODE=1 |
開始透傳 | AT+CIPSEND |
說明:
a. 我修改服務器WIFI名為ALIENTEK,密碼15902020353,修改完后復位,可以在電腦上找到這個WIFI名,
這也是后面客戶端要連接服務器時所用到的賬號密碼。
b. WIFI模式分為3種:1為station(客戶端);2為access point(服務器);3為1和2的兼容模式
c. 如果想要一個服務器與多個客戶端通信,可以在服務器復位后插入AT+CIPMUX=1(啟動多連接)即可。那
問題來了,服務器怎么知道自己要通信的客戶端是誰呢?可以這樣,先讓客戶端連接上服務器WIFI,對客
戶端使用AT+CIPSTATUS,就會返回當前模塊的攔截狀態和連接參數 STATUS: +CIPSTATUS:,,<remote_ip>,, <local_port>, 。id就是我們要的值,然后AT+CIPSND =0,5就可以針對0號客戶端發送5個字節的數據了。
d. 結束透傳的話需要發送"+++"(該指令必須在開啟透傳模式下使用 )
關於AT指令的使用細則網上已經有很多資料,我不再贅述,可以參考這篇文章 兩個ESP8266一個作為服務器一個作為客戶端實現互相通訊
多操作兩遍,摸清楚流程,在電腦上能實現兩者互相通信后,接下來就是在單片機上寫好程序了。在單片機上寫好上面AT指令,通過串口發送給ESP8266模塊。上面我說到硬件最大的問題就是電壓有沒有給到位,而代碼中最關鍵的問題有兩個,一是格式,二是速度。
軟件我是這樣寫的:
/*************************************************
服務器邏輯代碼
*************************************************/
void atk_8266_server(void)
{
atk_8266_send_cmd("AT","OK",20); //檢查WIFI模塊是否在線
atk_8266_send_cmd("AT+CWMODE=2","OK",20); //1.Station模式2.Ap模式3.兼容1.2兩個模式
atk_8266_send_cmd("AT+RST","OK",200); //重啟復位,模式生效
atk_8266_send_cmd("AT+CIPMUX=1","OK",20); //啟動多連接指令
atk_8266_send_cmd("AT+CIPSERVER=1,8089","OK",20);//建立服務器server指令
USART2_RX_STA=0;
while(1) //等待倒計時
{
delay_ms(100);
if(USART2_RX_STA&0X8000) //接收到期待的應答結果
{
if(atk_8266_check_cmd("start"))//服務器接收數據"start"
{
actions();
break;
}
USART2_RX_STA=0;
}
}
}
/*************************************************
向ESP8266發送命令
cmd:發送的命令字符串
ack:期待的應答結果,如果為空,則表示不需要等待應答
waittime:等待時間(單位:10ms)
返回值:0,發送成功(得到了期待的應答結果)
1,發送失敗
*************************************************/
u8 atk_8266_send_cmd(u8 *cmd,u8 *ack,u16 waittime)
{
u8 res=0;
USART2_RX_STA=0;
USART2_printf("%s\r\n",cmd);//發送命令
//delay_ms(100);
//USART2_printf("%s\r\n",cmd);如果碰到長指令發送無效,可以嘗試發送兩遍,親測有效!!!
if(ack&&waittime) //需要等待應答
{
while(--waittime) //等待倒計時
{
delay_ms(10);
if(USART2_RX_STA&0X8000)//接收到期待的應答結果
{
if(atk_8266_check_cmd(ack))
{
//printf("ack:%s\r\n",(u8*)ack);
break; //得到有效數據
}
USART2_RX_STA=0;
}
}
if(waittime==0)res=1;
}
return res;
}
/*************************************************
ESP8266發送命令后,檢測接收到的應答
str:期待的應答結果
返回值:0,沒有得到期待的應答結果
其他,期待應答結果的位置(str的位置)
*************************************************/
u8* atk_8266_check_cmd(u8 *str)
{
char *strx=0;
if(USART2_RX_STA&0X8000)//接收到一次數據了
{
USART2_RX_BUF[USART2_RX_STA&0X7FFF]=0;//添加結束符
strx=strstr((const char*)USART2_RX_BUF,(const char*)str);
}
return (u8*)strx;
}
/*************************************************
ESP8266退出透傳模式
返回值:0,退出成功;
1,退出失敗
*************************************************/
u8 atk_8266_quit_trans(void)
{
while((USART2->SR&0X40)==0);//等待發送空
USART2->DR='+';
delay_ms(15);//大於串口組幀時間(10ms)
while((USART2->SR&0X40)==0);//等待發送空
USART2->DR='+';
delay_ms(15);//大於串口組幀時間(10ms)
while((USART2->SR&0X40)==0);//等待發送空
USART2->DR='+';
delay_ms(500);//等待500ms
return atk_8266_send_cmd("AT","OK",20);//退出透傳判斷.
}
xxxxxxxxxx
/*************************************************
客戶端代碼
*************************************************/
void atk_8266_test(void)
{
atk_8266_send_cmd("AT+CWMODE=1","OK",20);//1.Station模式 2.Ap模式 3.兼容1.2兩個模式
atk_8266_send_cmd("AT+RST","OK",200); //重啟復位
atk_8266_send_cmd("AT+CWJAP=\"ALIENTEK\",\"15902020353\"","OK",2000); //連接WIFI
atk_8266_send_cmd("AT+CIPSTART=\"TCP\",\"192.168.4.1\",8089","OK",1000);//連接服務器
atk_8266_send_cmd("AT+CIPSEND=5","OK",20);//發送5個字節數據
atk_8266_send_cmd("start","OK",100); //發送"start"
}
注意:
a.格式問題基本如上,上面的AT指令可以通過串口發送,ESP8266也能識別。
b.速度問題,我必須強調一下,單片機發送命令的速度是非常快的,遠快於8266模塊接收到指令並返回給單片機
這個過程。有可能一連串指令中有一個反應慢了點(客戶端WIFI連接服務器WIFI時耗用的時間在1秒到10秒不
等),單片機就可能會略過這個指令,導致WIFI連接不上。但對於單片機來說,你又無法直接看到到底是哪個指
令失效,我的做法比較笨,有兩個思路:一是在程序中每個指令若執行成功就會有返回值,並通過串口打印在電
腦上;二是將單片機控制的ESP8266和電腦串口控制的ESP8266進行通信,這樣你能確保電腦控制的一定不會出
錯,同時連接時產生的一些信息會打印在串口助手窗口上,有利於排查錯誤。總之,每個指令的等待時間不一
樣,需要自己不斷調整。
c.如果單獨發一條格式正確的長指令(比如修改WIFI名指令),也可能會出現沒有接收成功的問題。有一個不清楚
問題在哪的辦法:延時一兩百毫秒,再發送一遍。(慚愧,沒搞清楚為什么這樣弄一下就會有用,如果有知道的
大佬麻煩在評論區指出。)
5.總結
a.凡是不能打敗你的,都使你更加強大,但也不能太折磨自己
終於理解嵌入式為什么坑了,配個WIFI花了大半時間在硬件上,板子設計缺陷,焊接錯誤,8266電路配置不到位,整得我死去活來的,說多了都是自閉。最終還是對硬件,串口有了更好的掌握。可能先買模塊組裝比較好,或者有人帶帶上手比較快,不然太折磨人了。
b.由易入難
沒有在電腦上通過串口控制ESP8266模塊的經驗,會對在單片機上配置帶來沒有可參照標准現象,8266實際反應速度等 相對精准的參考依據,就像在黑暗中只能用手摸着路回家了,其難度可想而知。所以還是要借用別人成熟的項目經驗,自己模仿着來操作,會少踩很多坑,也會少做很多無用功。
買開發板搭配視頻學習>尋找自己感興趣且別人有教程的項目>積累足夠經驗開始自己開發。像我這個新手直接硬剛真的是遭罪啊。