一、系統方案
手機APP通過ESP8266 WIFI模塊與51單片機通信控制四路繼電器。下位機由單片機、ESP8266模塊和繼電器模塊組成,上位機由Android手機APP承擔。我們在APP上發送繼電器的開關控制指令,ESP8266將收到的數據發送給單片機,從而實現對繼電器進行開關控制。
二、硬體設計
ESP8266模塊作為一個透傳模塊使用,RXD、TXD分別連接51單片機的TXD和RXD,VCC和EN管腳接3.3V電壓,GND接地,只需要連接這些管腳,ESP8266模塊就可以正常工作了。
單片機的P2^0,P2^1,P2^2,P2^3輸出高低電瓶控制四路繼電器,繼電器模塊是從網上購買的已經焊接好的模塊,其他地方為手工萬用板焊接。
三、單片機軟體設計
單片機代碼主要是串口初始化、ESP8266的初始化和串口中斷。
1.串口和ESP8266初始化:
/**
*發送單個字符
*/
void sendChar(uchar a)
{
SBUF = a;
while(TI==0);
TI=0;
}
/**
*發送字符串
*/
void sendString(uchar *s)
{
while(*s!='')
{
sendChar(*s);
s++;
}
}
/**
*初始化ESP8266模塊
*/
void initEsp()
{
TMOD=0x20; //定時器1工作在方式2
TH1 = 0xfd; //波特率9600
TL1 = 0xfd;
SM0=0; //串口工作在方式1
SM1=1;
EA = 1; //開總中斷
REN = 1; //使能串口
TR1 = 1; //定時器1開始計時
delayms(200);
sendString("AT+CWMODE=2rn"); //AP模式
delayms(200);
sendString("AT+CIPMUX=1rn"); //允許多連接
delayms(200);
sendString("AT+CIPSERVER=1rn"); //建立TCP Server
delayms(200);
ES = 1; //開串口中斷
}
sendString("AT+CWMODE=2rn") ----- 單片機發送AT指令到ESP8266模塊,AT+CWMODE=2是將ESP8266設置為AP模式,rn是換行,因為AT指令加換行才能生效。
sendString("AT+CIPMUX=1rn") ---- 允許多連接
sendString("AT+CIPSERVER=1rn") ---- 建立TCP Server
2.串口中斷函數,負責處理App發送給單片機的指令:
/**
* 串口中斷函數,負責處理App發送給單片機的指令
*/
void uart() interrupt 4
{
if(RI == 1)
{
RI = 0; //清除串口接收標誌位
receiveTable[i]=SBUF;
if(receiveTable[0]=='+')
{
i++;
}
else
{
i=0;
}
if(i==10)
{
i=0;
switch(receiveTable[9])
{
case '1': //打開繼電器
JDQ4=0;
break;
case '2': //關閉繼電器
JDQ4=1;
break;
case '3':
JDQ3=0;
break;
case '4':
JDQ3=1;
break;
case '5':
JDQ2=0;
break;
case '6':
JDQ2=1;
break;
case '7':
JDQ1=0;
break;
case '8':
JDQ1=1;
break;
}
}
}
}
esp8266在收到數據並轉發給單片機時的數據格式:+IPD, , <收到的字符長度> :收到的字符,比如+IPD,0,5:hello,其中+PID是固定的;0代表的是TCP客戶端編號,esp8266最多支持5個客戶端同時連接,也就是說客戶端編號是0到4,在本設計中由於只有一個客戶端與esp8266相連,所以客戶端編號是0;5代表收到的字符長度;hello是收到的字符。在本例中esp8266發送給單片機的數據是+IPD,0,1:1,我們把接收到的字符串緩存到字符數組中,所以在處理收到的數據邏輯中,首先判斷是否是以'+'開始的,否則視作無效數據,然後判斷數組中的第十個數據,因為第十個數據才是上位機發送過來的數據。
四、Android APP軟體設計
Android APP是藉助Android Studio來開發的,界面比較清新。esp8266默認的IP位址是192.168.4.1,埠號是333。四個開關控制四路繼電器,其中長按開關的名字可以編輯開關名稱,APP界面截圖如下所示:
負責連接ESP8266的按鈕點擊回調方法:
/**
* 連接按鈕點擊事件回調方法
* @param v
*/
@Override
public void onClick(View v) {
if(v.getId()==R.id.btn_connect){
if (mSocket == null || !mSocket.isConnected()) {
new Thread(){
@Override
public void run() {
try {
mSocket = new Socket("192.168.4.1", 333);
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();
}
});
}
}
}.start();
}
if (mSocket != null && mSocket.isConnected()) {
try {
mSocket.close();
mBtnConnect.setText("連接");
mSocket = null;
} catch (IOException e) {
e.printStackTrace();
mSocket = null;
}
}
}
}
滑動開關點擊回調方法,發送指令到單片機控制繼電器的開關:
/**
* 滑動按鈕監聽事件,發送指令到單片機控制繼電器開關
* @param buttonView
* @param isChecked
*/
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
switch (buttonView.getId()) {
case R.id.switch1:
if (isChecked) {
//turn on
Log.d(TAG, "onCheckedChanged: send1");
sendData("1");
} else {
//turn off
Log.d(TAG, "onCheckedChanged: send2");
sendData("2");
}
break;
case R.id.switch2:
if (isChecked) {
//turn on
Log.d(TAG, "onCheckedChanged: send3");
sendData("3");
} else {
//turn off
Log.d(TAG, "onCheckedChanged: send4");
sendData("4");
}
break;
....
....
....
}
}
本文完!
找元器件現貨上唯樣商城