蘇州永節(jié)電子科技有限公司
設(shè)為首頁(yè) | 收藏本站
新聞詳情

單片機(jī)開發(fā)使用無(wú)線網(wǎng)絡(luò)模塊

瀏覽數(shù):24

ESP8266是我第一個(gè),也是唯一一個(gè)接觸過的無(wú)線網(wǎng)絡(luò)模塊,我非常喜歡!大部分網(wǎng)上的教程都是ARM架構(gòu)單片機(jī)配用ESP8266。我比較摳門,給大家上51版的。
     我記得當(dāng)時(shí)和老板承諾可以做成無(wú)線通信的時(shí)候我也不是很確定,心里一直打鼓。好在最后做出來(lái)了,很有成就感!所以今天的代碼案例是我把我之前在公司寫的項(xiàng)目代碼簡(jiǎn)化之后分享給大家的。雖然結(jié)構(gòu)有點(diǎn)惡心,但應(yīng)該還是入得了眼的。
如果有沒看懂的地方,大家評(píng)論區(qū)告訴我。
需求
利用ESP8266芯片,通過無(wú)線網(wǎng)絡(luò)同電腦建立TCP連接。接收電腦傳來(lái)的消息并顯示在LCD上。
每條信息以\結(jié)尾。
每條信息字符數(shù)不超過32。
信息內(nèi)容只能是ASCII表內(nèi)的字符。
清單

硬件

學(xué)習(xí)板

簡(jiǎn)介
由于這次要用到WIFI模塊,沒法用Proteus模擬,所以就上真家伙。
自己焊板子就太麻煩了,所以就買學(xué)習(xí)板(其實(shí)我不會(huì)焊)。
QX-MINI51

這是我入的學(xué)習(xí)板,你也可以入其他的,如果把握不大就跟我入一樣的。
這個(gè)是我在淘寶上小挑了一陣子選的板子:QX-MINI51。尺寸不大不小,想玩的基本都有。配備的單片機(jī)是STC89C52。
淘寶鏈接

電路
下面是QX-MINI51在本節(jié)會(huì)用到的電路圖
STC89C52

使用89C52芯片,和我們之前用的單片機(jī)一樣。
供電&USB串口

CH340芯片是用于普通串口和USB轉(zhuǎn)換的,其上的RXD為串口輸入,TXD為串口輸出。UD+和UD-對(duì)應(yīng)USB數(shù)據(jù)線。
雖說(shuō)是用了USB,其實(shí)和普通串口通信線用法一致,只是人家成品方便我們使用封裝成了USB。這里不深究。
流水燈

流水燈在本節(jié)中的角色是輔助調(diào)試(具體的看后面),這里有用的信息就是讓你知道這8個(gè)流水燈接哪了。
ESP8266

簡(jiǎn)介
主角,簡(jiǎn)而言之就是Wi-Fi模塊,屬于網(wǎng)絡(luò)層以上的設(shè)備。擁有MAC地址和IP地址,支持UDP和TCP。
ESP8266

這款的型號(hào)是ESP8266-01,其他的和QX-MINI51配合使用不太方便。
淘寶鏈接
參數(shù)
型號(hào)        主芯片        無(wú)線標(biāo)準(zhǔn)        工作電壓        安全機(jī)制        支持模式
ESP8266-01        ESP8266        IEEE 802.11b/g/n        3.3V        WEP/WPA-PSK/WPA2-PSK        STA、AP、STA+AP
STA 模式:ESP8266模塊通過路由器連接互聯(lián)網(wǎng),手機(jī)或電腦通過互聯(lián)網(wǎng)實(shí)現(xiàn)對(duì)設(shè)備的遠(yuǎn)程控制。
AP 模式:ESP8266模塊作為熱點(diǎn),實(shí)現(xiàn)手機(jī)或電腦直接與模塊通信,實(shí)現(xiàn)局域網(wǎng)無(wú)線控制。
STA+AP 模式:兩種模式的共存模式,即可以通過互聯(lián)網(wǎng)控制可實(shí)現(xiàn)無(wú)縫切換,方便操作。
引腳圖

上圖對(duì)8個(gè)針腳進(jìn)行說(shuō)明。

UTXD        GND        CH_PD        GPIO2        GPIO16        GPIO0        VCC        URXD
發(fā)送        接地        高電平工作        |||電源        接收                       
本例只用到這五個(gè)引腳(畫“\”的不用),其他引腳的說(shuō)明資料請(qǐng)自行到淘寶鏈接處下載。

AT指令
操作ESP8266芯片是靠AT指令的,類似之前的操作LCD1602(《51單片機(jī)實(shí)戰(zhàn):液晶顯示器のLCD1602》)。操作LCD1602的指令都是一個(gè)字節(jié)的十六進(jìn)制碼,比較難記和理解。AT指令是字符串形式的指令,一般都是單詞縮寫,對(duì)可笑的人類比較友好。
之前和LCD1602交互是靠并口傳輸,而這次是用串口傳輸(串口傳輸簡(jiǎn)例:《51單片機(jī)實(shí)戰(zhàn):與計(jì)算機(jī)異步串行通信》),所以這次的Wi-Fi通信是建立在串口通信基礎(chǔ)上的。

AT指令集下載鏈接
AT指令使用示例下載鏈接

波特率
注意,這個(gè)模塊的默認(rèn)波特率是115200,本例也是根據(jù)這個(gè)波特率進(jìn)行演示的。若想改變波特率請(qǐng)使用以下語(yǔ)句進(jìn)行修改:

AT+CIOBAUD=<baudrate>,<databits>,<stopbits>,<parity>,<flow control>
如:

AT+CIOBAUD=9600,8,1,0,0  
軟件

程序        說(shuō)明        下載
UartAssist        串口調(diào)試助手,用來(lái)給單片機(jī)發(fā)送消息        度娘網(wǎng)盤
NetAssist        網(wǎng)絡(luò)調(diào)試助手,在電腦端建立TCP連接與單片機(jī)的ESP8266進(jìn)行通信        度娘網(wǎng)盤
STC-ISP        STC單片機(jī)工具集,很強(qiáng)大,可燒錄程序,可串口調(diào)試等等        度娘網(wǎng)盤
CH340驅(qū)動(dòng)        CH340的USB驅(qū)動(dòng),如果沒有這個(gè)驅(qū)動(dòng),你的電腦可能識(shí)別不到單片機(jī)(識(shí)別到CH340就相當(dāng)于識(shí)別到單片機(jī)的串口。)        度娘網(wǎng)盤
51單片機(jī)波特率初值計(jì)算工具        用于計(jì)算在各種波特率和晶振頻率等參數(shù)下計(jì)時(shí)器的初值,屬于輔助工具,省的還得算。        度娘網(wǎng)盤
分析

調(diào)試

在開始用單片機(jī)直接和無(wú)線模塊通信之前,首先要繞過單片機(jī)直接和無(wú)線模塊通信以確定其可以使用和接入默認(rèn)網(wǎng)絡(luò)(接入一個(gè)熱點(diǎn)后,模塊每次斷電后啟動(dòng)都會(huì)自動(dòng)連接該熱點(diǎn)),這樣可以讓單片機(jī)少做很多事情(要知道我們用的是51單片機(jī),硬件資源極其有限,能省則?。?/span>

TXD/RXD反接
首先要繞過單片機(jī)直接給ESP8266下指令,就要用電腦的串口。但ESP-01是針腳接口,所以我們可以利用QX-MINI51上的針腳和USB接口。

回看前面QX-MINI51的主控芯片和USB的電路圖,可以發(fā)現(xiàn),單片機(jī)的串口引腳是被并聯(lián)式的暴露再外的,分別為:P30 - P31和USB,前者為針腳接口,后者為USB接口。且它們的RXD和TXD的數(shù)據(jù)信息是一摸一樣的,注意并不是兩個(gè)獨(dú)立的串口,是同一個(gè)串口。比方說(shuō),單片機(jī)要往出發(fā)送一個(gè)1,P30針腳和USB都會(huì)是往出發(fā)送一個(gè)1。

所以我們利用這個(gè)特點(diǎn),將ESP8266的TXD接到P31(單片機(jī)的TXD),RXD接到P30(單片機(jī)的RXD),這樣就等于讓ESP8266直接和USB打交道,也就是和電腦直接打交道了。

為什么叫反接,我一般管RXD對(duì)TXD叫正接。嗯…
接線圖

強(qiáng)勢(shì)秀一波畫工!
我覺得應(yīng)該可以看得懂吧,ESP8266的引腳說(shuō)明請(qǐng)看前面的引腳圖??醇t色號(hào)碼對(duì)應(yīng)接線,其中4、5、6號(hào)引腳不用。
實(shí)物圖

這個(gè)圖就很難看出線是怎么連的了,所以你要忍受我丑陋的畫工。

接入網(wǎng)絡(luò)
打開串口調(diào)試助手,調(diào)好參數(shù)
串口調(diào)試助手,設(shè)置參數(shù)

其中串口號(hào)你連的哪個(gè)串口就設(shè)置哪個(gè)串口號(hào),我是連的COM3。
另一個(gè)要注意的就是波特率要115200(ESP-01的默認(rèn)波特率,也可以統(tǒng)一改為9600)。
打開串口后,給開發(fā)板上電。你的串口調(diào)試助手會(huì)有信息出來(lái)(文本顯示,不要十六進(jìn)制顯示)。
?諄MEM CHECK FAIL!!!
d{$弬s
Ai-Thinker Technology Co. Ltd.

invalid
顯示的信息類似上面,你可以先給個(gè)測(cè)試命令A(yù)T看看是否可以接受指令

注意!每個(gè)指令后要跟回車再發(fā)送!
如果返回OK則說(shuō)明指令可以被接收并識(shí)別。

如果下面的指令都會(huì)返回ERROR(在沒有給錯(cuò)指令的情況下),可以嘗試AT+RST重啟模塊。

1. 更改模式
指令:AT+CWMODE?
一般情況下,第一次使用會(huì)返回2,也就是AP模式,我們不用它發(fā)熱點(diǎn),所以要改回Station模式(模式1)。

指令:AT+CWMODE?
一般情況下,第一次使用會(huì)返回2,也就是AP模式,我們不用它發(fā)熱點(diǎn),所以要改回Station模式(模式1)。

指令:AT+CWMODE=1
返回:OK則成功

2. 接入熱點(diǎn)(連Wi-Fi)
指令:AT+CWJAP=<SSID>,<Password>
參數(shù):<SSID>處填寫熱點(diǎn)名稱,<Password>處填寫密碼。兩者都要用雙引號(hào)括起來(lái)。
例如:AT+CWJAP="CMCC","123456"

指令:AT+CWJAP=<SSID>,<Password>
參數(shù):<SSID>處填寫熱點(diǎn)名稱,<Password>處填寫密碼。兩者都要用雙引號(hào)括起來(lái)。
例如:AT+CWJAP="CMCC","123456"

返回:WIFI CONNECTED:連接到熱點(diǎn)
返回:WIFI GOT IP:分配到IP,走到這一步,就算已經(jīng)連入到熱點(diǎn)了。

如果忘記SSID了,想看一下可以使用下面的指令,列出廣播的SSID(隱藏的不會(huì)顯示)。
指令:AT+CWLAP

3. 連接TCP服務(wù)器
首先打開NetAssist,設(shè)置TCP Server,然后建立連接(注意防火墻)。注意,Server必須在Client所在內(nèi)網(wǎng)或其外網(wǎng)(我的是在同一個(gè)內(nèi)網(wǎng))。
網(wǎng)絡(luò)調(diào)試助手
首先打開NetAssist,設(shè)置TCP Server,然后建立連接(注意防火墻)。注意,Server必須在Client所在內(nèi)網(wǎng)或其外網(wǎng)(我的是在同一個(gè)內(nèi)網(wǎng))。


網(wǎng)絡(luò)調(diào)試助手
指令:AT+CIPSTART=<Type>,<DomainName>,<Port>
參數(shù):<Type>處寫TCP或UDP,<DomainName>處寫域名,<Port>處寫端口號(hào)。
本例:AT+CIPSTART="TCP","192.168.1.110",1234
返回:

CONNECT

OK
說(shuō)明連接成功。
在NetAssist中,數(shù)據(jù)接收框的下面有一個(gè)連接對(duì)象,點(diǎn)開后發(fā)現(xiàn)除了All Connections之外,多了一個(gè)客戶,就確定客戶連接到服務(wù)器了。
NetAssist

在下方文本框輸入信息后發(fā)送,可在串口調(diào)試助手中看到ESP8266所接收到的信息
UartAssist收到的信息:+IPD,10:Hello 簡(jiǎn)書
到這里就說(shuō)明網(wǎng)絡(luò)連接及建立TCP都可以順利完成,在下面的單片機(jī)操作中就會(huì)變得方便很多。

注意,ESP8266每次斷電后重新上電,最多只會(huì)自動(dòng)連到之前連接的熱點(diǎn),但不會(huì)自動(dòng)連接到TCP服務(wù)器,所以,建立連接要交給單片機(jī)來(lái)做。
問題

波特率
本例要使用的波特率為115200的,不是9600。引申出來(lái)的問題就是:算初值(方法詳見:《51單片機(jī)實(shí)戰(zhàn):與計(jì)算機(jī)異步串行通信》 - 知識(shí)點(diǎn) - 波特率 - 溢出率)。
手算真的很麻煩,因?yàn)樵趨?shù)固定的情況下,算起來(lái)已經(jīng)很煩了,更何況那些參數(shù)可能還會(huì)變得情況。
所以上神器:
初值計(jì)算器
反饋及識(shí)別
我們要做的是讓單片機(jī)在收到ESP8266回饋的WIFI GOT IP后發(fā)出連接TCP服務(wù)器的指令。
發(fā)送指令不用多說(shuō),只要記得在后面跟"換行(CR)"和"新行(NL)"字符就行(這兩個(gè)是不一樣的)。
1. 關(guān)鍵字符配對(duì)
如果是電腦程序的高級(jí)語(yǔ)言編程,這個(gè)問題就不存在了。但是我們給單片機(jī)寫程序就要牢記它的一些點(diǎn)(《扯會(huì)兒?jiǎn)纹瑱C(jī)開發(fā):開始》),比如硬件資源十分緊張。
一個(gè)是因?yàn)樗鎯?chǔ)空間很小,另一個(gè)是因?yàn)榫д裉?,所以我們要想辦法縮減配對(duì)時(shí)間。因?yàn)閱纹瑱C(jī)的使用往往很單一,所以策略都是根據(jù)情況來(lái)設(shè)定的。比如本例,單片機(jī)接收的回饋無(wú)非那么幾種,所以我制定的策略就是關(guān)鍵字符配對(duì)。

比如識(shí)別WIFI GOT IP,我只是別第一個(gè)字符W和第6個(gè)字符G,就說(shuō)明我收到的就是這個(gè)回饋信息。如果收到第一個(gè)是W,只有這條指令的第六個(gè)字符是G,所以就可以確定。如果是出現(xiàn)干擾,可能性也是比較低的,我們的交互是在網(wǎng)絡(luò)層的,下層也可以把出錯(cuò)的報(bào)文擋掉。

總體來(lái)說(shuō)還是蠻可靠的,如果你有更棒的方法,請(qǐng)?jiān)谠u(píng)論區(qū)告訴我,大家一起學(xué)習(xí)進(jìn)步。

2. 利用流水燈
上面就是理論工作,已經(jīng)做得7788了。但實(shí)際經(jīng)驗(yàn)告訴我,如果的代碼哪里也出岔子了在51單片機(jī)開發(fā)中真的比較難發(fā)現(xiàn),他不像高級(jí)程序語(yǔ)言可以報(bào)錯(cuò),可以try - catch。但這個(gè)就別想了,所以我們要自己想辦法,讓他可以反饋我們的程序至少是正常運(yùn)行的,這樣可以節(jié)省很多時(shí)間。

我在這里介紹的我的方法是利用LED,或成組的流水燈(更好)。承租的流水燈一般是8個(gè),可以直接顯示一個(gè)字節(jié)的信息,可能有人問為什么不用LCD?你若會(huì)LCD或者看過《51單片機(jī)實(shí)戰(zhàn):液晶顯示器のLCD1602》就會(huì)發(fā)現(xiàn),LCD本身的開發(fā)就有點(diǎn)復(fù)雜了。你很難保證這個(gè)子程序運(yùn)行正常,更別提用它抓錯(cuò)了。只能是不推薦哈,我覺得不可靠。LED的話就很簡(jiǎn)單了,就是個(gè)關(guān)開,一般不會(huì)出錯(cuò)。

QX-MINI51開發(fā)板上自帶流水燈,你要是入的其他開發(fā)板,一般都是有的。如果沒有,就單買LED回來(lái)(如果不知道去哪買,鏈接)。

我一般會(huì)怎么做呢,先讓流水燈直接顯示串口接收的數(shù)據(jù),為了知道我們至少是能接收到東西的。然后利用上面的關(guān)鍵字符識(shí)別,收到WIFI CONNECTED亮一號(hào)燈,收到WIFI GOT IP亮二號(hào)燈,收到CONNECT OK亮三號(hào)燈。這樣,根據(jù)燈亮滅的情況就能確定哪一步出了問題,然后定位到代碼或者設(shè)置。

還是那句話,如果你有更棒的方法,歡迎在評(píng)論區(qū)交流。

到這里就做完了所有理論上的準(zhǔn)備工作,也就是理論上我們已經(jīng)可以實(shí)現(xiàn)這個(gè)程序了,下面代碼實(shí)現(xiàn)。

代碼

說(shuō)下這次代碼比較新穎的地方,一個(gè)是用到了.c和.h文件組合的模塊形式,另一個(gè)是用到了函數(shù)指針(函數(shù)名也被括起來(lái)的那個(gè))。前者這種寫法是為了將各子功能模塊化,.h文件里的內(nèi)容相當(dāng)于面向?qū)ο缶幊汤锏膒ublic,.c文件一個(gè)是實(shí)現(xiàn).h內(nèi)的函數(shù),另一個(gè)就是隱藏函數(shù)和變量,相當(dāng)于private。后者是用來(lái)充當(dāng)高級(jí)語(yǔ)言中的“事件”,讓函數(shù)調(diào)用變得更加靈活,其具體用法請(qǐng)自己查資料或者看相關(guān)C語(yǔ)言書籍。

總而言之,這次的代碼是模塊化和事件化的,類似面向?qū)ο蟮木幋a風(fēng)格。
準(zhǔn)備
因?yàn)橛糜谕ㄓ崳晕野袮SCII內(nèi)所有的特殊字符都寫到一個(gè)頭文件里。雖然本例只用到其中的幾個(gè),但以后重復(fù)利用這個(gè)頭文件。

ASCII.h

#ifndef __ASCIIS__
#define __ASCIIS__

#define NUL 0x00 // NULL
#define SOH 0x01 // Start of Heading
#define STX 0x02 // Start of Text
#define ETX 0x03 // End of Text
#define EOT 0x04 // End of Transmission
#define ENQ 0x05 // Enquiry
#define ACK 0x06 // Acknowledge
#define BEL 0x07 // Bell
#define BS 0x08 // Backspace
#define HT 0x09 // Horizontal Tab
#define LF 0x0A // Line Feed
#define NL 0x0A // New Line
#define VT 0x0B // Vertical Tab
#define FF 0x0C // Form Feed
#define NP 0x0C // New Page
#define CR 0x0D // Carriage Return
#define SO 0x0E // Shift Out
#define SI 0x0F // Shift In
#define DLE 0x10 // Data Link Escape
#define DC1 0x11 // Device Control 1
#define DC2 0x12 // Device Control 2
#define DC3 0x13 // Device Control 3
#define DC4 0x14 // Device Control 4
#define NAK 0x15 // Negative Acknowledge
#define SYN 0x16 // Synchronous Idle
#define ETB 0x17 // End of Transmission Block
#define CAN 0x18 // Cancel
#define EM 0x19 // End of Medium
#define SUB 0x1A // Substitute
#define ESC 0x1B // Escape
#define FS 0x1C // File Separator
#define GS 0x1D // Group Separator
#define RS 0x1E // Record Separator
#define US 0x1F // Unit Separator
#define SP 0x20 // Space

#endif
LCD
這里都是關(guān)于LCD1602顯示器的主要代碼,與文章《51單片機(jī)實(shí)戰(zhàn):液晶顯示器のLCD1602》所講的一樣,這里就不給出注釋了。
lcd1602.h

#ifndef __LCD1602__
#define __LCD1602__

typedef bit BOOL;

void LCD_writeCmd(unsigned char cmd);   //寫命令
void LCD_writeData(unsigned char dat);   //寫數(shù)據(jù)
void LCD_writeLine(unsigned char *line);   //寫行數(shù)據(jù)
void LCD_init();   //初始化
void delay(unsigned int z);   //粗略的延時(shí)器

#endif
lcd1602.c

#include <reg52.h>
#include "lcd1602.h"

#define LCD_CLEAR 0x01
#define LCD_Display_Mode 0X38

#define DISPLAY_OFF 0x08
#define DISPLAY_ON_NO_CURSOR 0x0c
#define DISPLAY_ON_WITH_CURSOR_NO_BLINK 0x0e
#define DISPLAY_ON_WITH_CURSOR_BLINK 0x0f

#define AUTO_BACK_STEP 0x04
#define AUTO_NEXT_STEP 0x06
#define AUTO_DISPLAY_MOVE_LEFT 0x07
#define AUTO_DISPLAY_MOVE_RIGHT 0x05

#define ALL_MOVE_LEFT 0x18
#define ALL_MOVE_RIGHT 0x1c
#define CURSOR_MOVE_LEFT 0x10
#define CURSOR_MOVE_RIGHT 0x14

#define FIRST_ROW 0x80
#define SECOND_ROW FIRST_ROW+0x40

sbit enable = P0^5;
sbit RS = P0^7;
sbit RW = P0^6;

void delay(unsigned int z)
{
    unsigned int x,y;
    for(x=z;x>0;x--)
        for(y=220;y>0;y--);
}

void LCD_writeCmd(unsigned char cmd){
    RS = 0;
    P2 = cmd;
    delay(5);
    enable = 1;
    delay(5);
    enable = 0;
}

void LCD_writeData(unsigned char dat)
{
    RS = 1;
    P2 = dat;
    delay(5);
    enable = 1;
    delay(5);
    enable = 0;
}

void LCD_writeLine(unsigned char *line){
    unsigned char i=0;
    BOOL flag = 0;
    LCD_writeCmd(LCD_CLEAR);   //每次送來(lái)信息都清屏,可以一直刷新顯示送來(lái)的信息。
    LCD_writeCmd(FIRST_ROW);
    while(line[ i] != '\0'){
        LCD_writeData(line[i++]);
        if(i>15 && flag == 0){
            LCD_writeCmd(SECOND_ROW);
            flag = 1;
        }
        delay(5);
    }
}

void LCD_init()
{
    RW = 0;
    enable = 0;
    LCD_writeCmd(LCD_Display_Mode);
    LCD_writeCmd(DISPLAY_ON_NO_CURSOR);
    LCD_writeCmd(AUTO_NEXT_STEP);
    LCD_writeCmd(LCD_CLEAR);
}
串口通信
這里用于單片機(jī)和ESP8266交互,與文章《51單片機(jī)實(shí)戰(zhàn):與計(jì)算機(jī)異步串行通信》相似。其中的不同點(diǎn)前面已經(jīng)說(shuō)過。
stc52ser.h

#ifndef __STC52_SER__
#define __STC52_SER__

extern void (*SerialPort_Event_ByteReceived)(unsigned char byte);   //事件:串口接收到字節(jié)

void SerialPort_Init_Low();   //初始化為11.0592MHz下的9600波特率
void SerialPort_Init_High();   //初始化為22.1184下的115200波特率
void SerialPort_SendByte(unsigned char byte);   //發(fā)送一個(gè)字節(jié)
void SerialPort_SendData(unsigned char* bytes);   //發(fā)送一組字節(jié)

#endif
stc52ser.c

#include <reg52.h>
#include "ASCIIS.h"
#include "stc52ser.h"

//Byte Received Event
void (*SerialPort_Event_ByteReceived)(unsigned char byte);

//initialize registers pertinent to serial port
void SerialPort_Init_Low(){
    //set and run Timer1
    //mode2: 8bit, auto reload initial value
    //9600bps and 11.0592MHz => 0xfd(initial value)
    TMOD = 0x20;
    TH1 = 0xfd;
    TL1 = 0xfd;
    TR1 = 1;
   
    //set serial port configuration and enable receive
    //mode1: asyc 10bit(8 data bit), alterable baud rate
    SM0 = 0;
    SM1 = 1;
    REN = 1;
   
    //set interruption
    //enable all and serial port interruption
    EA = 1;
    ES = 1;
}

//initialize registers pertinent to serial port for esp8266
void SerialPort_Init_High(){
    //SMOD = 1
    PCON |= 0x80;
   
    //set and run Timer1
    //mode2: 8bit, auto reload initial value
    //115200bps and 22.1184MHz => 0xfd(initial value)
    TMOD = 0x20;
    TH1 = 0xff;
    TL1 = 0xff;
    TR1 = 1;
   
    //set serial port configuration and enable receive
    //mode1: asyc 10bit(8 data bit), alterable baud rate
    SM0 = 0;
    SM1 = 1;
    REN = 1;
   
    //set interruption
    //enable all and serial port interruption
    EA = 1;
    ES = 1;
}

//Send a byte
void SerialPort_SendByte(unsigned char byte){
    ES = 0;
    SBUF = byte;
    while(!TI);
    // transmit interrupt
    TI = 0;
    ES = 1;
}

//Send a data of byte sequence end by 'EOT'
void SerialPort_SendData(unsigned char* bytes){
    int i = 0;
    while(bytes[ i] != EOT){
        SerialPort_SendByte(bytes[ i]);
        i++;
    }
}


//Occured when byte received
void receivedInterruped() interrupt 4 {
    TR0 = 0;
    (*SerialPort_Event_ByteReceived)(SBUF);
    while(!RI);
    RI = 0;
}
簡(jiǎn)單說(shuō)一下,這里留了兩個(gè)初始化函數(shù),SerialPort_Init_Low()用于11.0592MHz下的9600波特率,SerialPort_Init_High()用于22.1184MHz下的115200波特率(此例用這個(gè))。這樣寫只是為了以后可以重用(軟工狗的矯情)。

無(wú)線
這里是最主要的代碼,都是關(guān)于ESP8266的,也是作為一個(gè)模塊給主函數(shù)調(diào)用。
esp8266.h

#ifndef __ESP8266__
#define __ESP8266__

extern void (*ESP01_Event_WifiConnected)();    //事件:Wi-Fi已連接
extern void (*ESP01_Event_IpGot)();    //事件:IP地址已獲得
extern void (*ESP01_Event_TcpServerConnected)();    //事件:已連接到TCP服務(wù)器
extern void (*ESP01_Event_MsgReceived)(unsigned char* head);    //事件:已獲得消息,head為消息數(shù)組頭

void ESP01_Init();   //無(wú)線模塊初始化
void ESP01_ConnectToTCPServer();   //連接TCP服務(wù)器

#endif
esp8266.c

#include <reg52.h>
#include "stc52ser.h"
#include "ASCIIS.h"
#include "esp8266.h"

#define BUFFER_MAX_SIZE 99   //緩沖區(qū)大小
unsigned char buffer[BUFFER_MAX_SIZE];   //緩沖區(qū):用于存放從ESP8266接收來(lái)的各種信息

//連接到TCP服務(wù)器的指令:AT+CIPSTART="TCP","192.168.1.110",1234。后面的CR和NL是AT指令的固定結(jié)尾,EOT用于SerialPort_SendData發(fā)送時(shí)識(shí)別結(jié)尾。
code unsigned char cmd_connectToTCPServer[] = {0x41, 0x54, 0x2B, 0x43, 0x49, 0x50, 0x53, 0x54, 0x41, 0x52, 0x54, 0x3D, 0x22, 0x54, 0x43, 0x50, 0x22, 0x2C, 0x22, 0x31, 0x39, 0x32, 0x2E, 0x31, 0x36, 0x38, 0x2E, 0x31, 0x2E, 0x31, 0x31, 0x30, 0x22, 0x2C, 0x31, 0x32, 0x33, 0x34, CR, NL, EOT};
int counter = 0;    //用于ESP8266的執(zhí)行步驟計(jì)數(shù)
int writeIndex = 0;    //緩沖區(qū)寫索引

void (*ESP01_Event_WifiConnected)();
void (*ESP01_Event_IpGot)();
void (*ESP01_Event_TcpServerConnected)();
void (*ESP01_Event_MsgReceived)(unsigned char* head);

//注意:下面代碼推薦從后往前看,從注釋標(biāo)"1. "處開始。

void prepareForData(unsigned char byte);    //因?yàn)榈谒牟胶偷谌綍?huì)相互調(diào)用,所以這里只是做了個(gè)聲明(C語(yǔ)言的矯情點(diǎn))。

//4. 將信息插入到緩沖區(qū)并送給單片機(jī)。
void insertDataIntoBuffer(unsigned char byte){
    if(byte == '\\'){
        //檢測(cè)到'\'后,將信息送出到單片機(jī)
        buffer[writeIndex] = '\0';
        (*ESP01_Event_MsgReceived)(buffer);
        SerialPort_Event_ByteReceived = &prepareForData;    //回到第三步,準(zhǔn)備接收下一條信息
        writeIndex = 0;
        return;
    }
    buffer[writeIndex++] = byte;
}

//3. 準(zhǔn)備信息:這里是過度步驟,前面可以觀察到,ESP8266在接收發(fā)來(lái)的信息時(shí)是有個(gè)頭的,這里的作用就是去頭。
void prepareForData(unsigned char byte){
    if(byte == ':'){
        SerialPort_Event_ByteReceived = &insertDataIntoBuffer;
        writeIndex = 0;
    }
}

//識(shí)別回饋指令:用于識(shí)別接收到的是WIFI CONNECTED(連上熱點(diǎn))還是WIFI IP GOT(獲得IP)還是CONNECT(連上TCP服務(wù)器)
void parseCmd(){
    switch(counter){
        case 1:
            if(buffer[0] == 'W' && buffer[5] == 'C'){
                (*ESP01_Event_WifiConnected)();
                counter += 1;
            }
            break;
        case 2:
            if(buffer[0] == 'W' && buffer[5] == 'G'){
                (*ESP01_Event_IpGot)();
                counter += 1;
            }
            break;
        case 3:
            if(buffer[0] == 'A' && buffer[3] == 'C')
                counter += 1;
            break;
        case 4:
            if(buffer[0] == 'C' && buffer[3] == 'N' && buffer[6] == 'T'){
                (*ESP01_Event_TcpServerConnected)();
                SerialPort_Event_ByteReceived = &prepareForData;   //連接到TCP服務(wù)器后,進(jìn)入第三步。
            }
    }
}

//2. 這里開始向緩沖區(qū)存儲(chǔ)信息,用于識(shí)別。
void insertBuffer(unsigned char byte){
    if(byte == NL){
        //收到尾(NL)后,將緩沖區(qū)的回饋信息送去識(shí)別
        parseCmd();
        writeIndex = 0;
        return;
    }
    buffer[writeIndex++] = byte;
}

//1. 接收頭:頭是無(wú)用信息,但我們要通過頭里面的一些字符,推算出什么時(shí)候到達(dá)第二步(WIFI CONNECTED)
void headerReceived(unsigned char byte){
    if(byte == NL){
        //頭內(nèi)有5個(gè)NL,只要數(shù)夠5個(gè),下一個(gè)就是第二步的內(nèi)容了。
        if(++counter == 5){
            SerialPort_Event_ByteReceived = &insertBuffer;   //跳到第二步
            counter = 1;
        }
    }
}

//同.h中的聲明
void ESP01_Init(){
    SerialPort_Init_High();
    SerialPort_Event_ByteReceived = &headerReceived;   //事件注冊(cè)
}

//同.h中的聲明
void ESP01_ConnectToTCPServer(){
    SerialPort_SendData(cmd_connectToTCPServer);
}
主函數(shù)
main.c

#include <reg52.h>
#include "lcd1602.h"
#include "esp8266.h"

//連接到Wi-Fi后,亮第一個(gè)燈
void EventHandler_WifiConnected(){
    P1 &= 0xFE;
}

//獲得IP后,亮第二個(gè)燈
void EventHandler_IpGot(){
    P1 &= 0xFD;
    ESP01_ConnectToTCPServer();
}

//連接到TCP服務(wù)器后,亮第三個(gè)燈
void EventHandler_TcpServerConnected(){
    P1 &= 0xFB;
}

//將ESP8266送來(lái)的信息,送去LCD顯示。
void EventHandler_MsgReceived(unsigned char* head){
    LCD_writeLine(head);
}

//初始化
void init(){
    ESP01_Event_WifiConnected = &EventHandler_WifiConnected;   //事件注冊(cè)
    ESP01_Event_IpGot = &EventHandler_IpGot;   //事件注冊(cè)
    ESP01_Event_TcpServerConnected = &EventHandler_TcpServerConnected;   //事件注冊(cè)
    ESP01_Event_MsgReceived = &EventHandler_MsgReceived;   //事件注冊(cè)
    ESP01_Init();
    LCD_init();
}

void main(){
    init();
    while(1);
}
編譯
編譯前要設(shè)置一下目標(biāo)參數(shù),如下圖。
目標(biāo)設(shè)置

注意這里要改成XDATA,不然編譯通不過的。
效果

開始前請(qǐng)確定在同一個(gè)網(wǎng)絡(luò)下,并且服務(wù)端已開啟。

初始化效果

我只是把單片機(jī)連到移動(dòng)電源上了。

服務(wù)端

為了能讓1602第一行顯示Hello,第二行顯示W(wǎng)orld,中間可以留了11個(gè)空格。
客戶端
結(jié)語(yǔ)

猴!到這里這個(gè)程序就算完成了。這個(gè)比那些用手機(jī)控制開關(guān)燈要復(fù)雜一些。所以你只要掌握了這個(gè)例子,那些都不在話下了