一、系統(tǒng)方案 手機(jī)APP通過ESP8266 WIFI模塊與51單片機(jī)通信控制LED燈、蜂鳴器的開關(guān)。下位機(jī)由單片機(jī)、ESP8266模塊和LED燈組成,上位機(jī)由Android手機(jī)APP承擔(dān)。我們?cè)贏PP上發(fā)送LED燈、蜂鳴器的開關(guān)控制指令,ESP8266將收到的數(shù)據(jù)發(fā)送給單片機(jī),從而實(shí)現(xiàn)對(duì)LED燈、蜂鳴器進(jìn)行開關(guān)控制。 設(shè)計(jì)好的實(shí)物是這個(gè)樣子: 二、硬件設(shè)計(jì)
ESP8266模塊作為一個(gè)透?jìng)髂K使用,RXD、TXD分別連接51單片機(jī)的TXD和RXD,VCC接3.3V電壓,GND接地,只需要連接這些管腳,ESP8266模塊就可以正常工作了。在單片機(jī)P16口上連接蜂鳴器、P26、P27連接兩個(gè)3mm的LED燈,我們的目的是通過手機(jī)APP上的開關(guān)控制著LED燈的亮滅以及蜂鳴器打開與關(guān)閉。
原理圖如下: 三、單片機(jī)軟件設(shè)計(jì) 硬件的連接不復(fù)雜,接下來主要是單片機(jī)和手機(jī)APP代碼的編寫。 單片機(jī)代碼主要是串口初始化、串口中斷和ESP8266的初始化。首先是串口初始化: TMOD=0x20; //定時(shí)器1工作在方式2 TH1 = 0xfd; //波特率9600 TL1 = 0xfd; SM0=0; //串口工作在方式1 SM1=1; EA = 1; //開總中斷 REN = 1; //使能串口 TR1 = 1; //定時(shí)器1開始計(jì)時(shí) 然后是ESP8266初始化:
delayms(1000); //延時(shí) sendString("AT+CWMODE=2\r\n"); //設(shè)置ESP8266工作在AP模式下 delayms(1000); sendString("AT+CIPMUX=1\r\n"); //允許多連接 delayms(1000); sendString("AT+CIPSERVER=1,8080\r\n"); //建立服務(wù)器 delayms(1000); ES = 1; //esp8266初始化之后開串口中斷 貼上51單片機(jī)負(fù)責(zé)串口發(fā)送的兩個(gè)函數(shù):
//發(fā)送一個(gè)字節(jié) void sendChar(uchar a) { SBUF = a; while(TI==0); TI=0; } //發(fā)送字符串 void sendString(uchar *s) { while(*s!='\0') { sendChar(*s); s++; }
}
在串口中斷中處理接收到的數(shù)據(jù): 1:打開蜂鳴器 2:關(guān)閉蜂鳴器 3:打開紅燈 4:關(guān)閉紅燈 5:打開綠燈 6:關(guān)閉綠燈 esp8266在收到數(shù)據(jù)并轉(zhuǎn)發(fā)給單片機(jī)時(shí)的數(shù)據(jù)格式:+IPD,<client號(hào)>,<收到的字符長(zhǎng)度>:收到的字符,比如+IPD,0,5:hello,其中+PID是固定的;0代表的是TCP客戶端編號(hào),esp8266最多支持5個(gè)客戶端同時(shí)連接,也就是說客戶端編號(hào)是0到4,在本設(shè)計(jì)中由于只有一個(gè)客戶端與esp8266相連,所以客戶端編號(hào)是0;5代表收到的字符長(zhǎng)度;hello是收到的字符。在本例中esp8266發(fā)送給單片機(jī)的數(shù)據(jù)是+IPD,0,1:1,我們把接收到的字符串緩存到字符數(shù)組中,所以在處理收到的數(shù)據(jù)邏輯中,首先判斷是否是以'+'開始的,否則視作無效數(shù)據(jù),然后判斷數(shù)組中的第十個(gè)數(shù)據(jù),因?yàn)榈谑畟(gè)數(shù)據(jù)才是手機(jī)APP發(fā)送過來的數(shù)據(jù)。 void uart() interrupt 4 { if(RI == 1) { RI = 0; //清除串口接收標(biāo)志位 receiveTable=SBUF; if(receiveTable[0]=='+') { i++; } else { i=0; } if(i==10) { i=0; switch(receiveTable[9]) { case '1': BEEP=0; break; case '2': BEEP=1; break; case '3': RedLED=0; break; case '4': RedLED=1; break; case '5': GreenLED=0; break; case '6': GreenLED=1; break; } }
} }
四、Android APP軟件設(shè)計(jì) Android APP是借助Android Studio來開發(fā)的,界面比較簡(jiǎn)單,通過編輯框輸入esp8266的IP地址和端口號(hào),esp8266默認(rèn)的IP地址是192.168.4.1,端口號(hào)是8080,這些都可以通過AT指令進(jìn)行修改。布局頁(yè)面的xml代碼我們就不貼了,熟悉Android開發(fā)的讀者很快就能根據(jù)截圖編寫出來,放上一個(gè)APP界面的截圖:
我們主要看一下邏輯代碼部分: 首先是控件的初始化和按鈕點(diǎn)擊事件回調(diào)的綁定 @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mBtnConnect = (Button) findViewById(R.id.btn_connect); mEtIP = (EditText) findViewById(R.id.et_ip); mEtPort = (EditText) findViewById(R.id.et_port); mBtnRedOn = (Button) findViewById(R.id.btn_red_on); mBtnRedOff = (Button) findViewById(R.id.btn_red_off); mBtnYellowOn = (Button) findViewById(R.id.btn_yellow_on); mBtnYellowOff = (Button) findViewById(R.id.btn_yellow_off); mBtnBlueOn = (Button) findViewById(R.id.btn_blue_on); mBtnBlueOff = (Button) findViewById(R.id.btn_blue_off); mBtnConnect.setOnClickListener(this); mBtnRedOn.setOnClickListener(this); mBtnRedOff.setOnClickListener(this); mBtnYellowOn.setOnClickListener(this); mBtnYellowOff.setOnClickListener(this); mBtnBlueOn.setOnClickListener(this); mBtnBlueOff.setOnClickListener(this); } 按鈕的點(diǎn)擊回調(diào)方法: public void onClick(View v) { switch (v.getId()) { case R.id.btn_connect: //連接 if (mSocket == null || !mSocket.isConnected()) { String ip = mEtIP.getText().toString(); int port = Integer.valueOf(mEtPort.getText().toString()); mConnectThread = new ConnectThread(ip, port); mConnectThread.start(); } if (mSocket != null && mSocket.isConnected()) { try { mSocket.close(); mBtnConnect.setText("連接"); } catch (IOException e) { e.printStackTrace(); } }
break; case R.id.btn_red_on: if (out != null) { out.print("1"); out.flush(); } break; case R.id.btn_red_off: if (out != null) { out.print("2"); out.flush(); } break; case R.id.btn_yellow_on: if (out != null) { out.print("3"); out.flush(); } break; case R.id.btn_yellow_off: if (out != null) { out.print("4"); out.flush(); } break; case R.id.btn_blue_on: if (out != null) { out.print("5"); out.flush(); } break; case R.id.btn_blue_off: if (out != null) { out.print("6"); out.flush(); } break; } }
負(fù)責(zé)連接esp8266的線程:
private class ConnectThread extends Thread { private String ip; private int port; public ConnectThread(String ip, int port) { this.ip = ip; this.port = port; }
@Override public void run() { try { mSocket = new Socket(ip, port); out = new PrintStream(mSocket.getOutputStream()); runOnUiThread(new Runnable() { @Override public void run() { mBtnConnect.setText("斷開"); } }); new HeartBeatThread().start(); } catch (IOException e) { e.printStackTrace(); runOnUiThread(new Runnable() { @Override public void run() { Toast.makeText(MainActivity.this, "連接失敗", Toast.LENGTH_SHORT).show(); } }); } } }
|