目录
一、系统方案
手机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!='\0')
{
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=2\r\n"); //AP模式
delayms(200);
sendString("AT+CIPMUX=1\r\n"); //允许多连接
delayms(200);
sendString("AT+CIPSERVER=1\r\n"); //建立TCP Server
delayms(200);
ES = 1; //开串口中断
}
sendString("AT+CWMODE=2\r\n") ----- 单片机发送AT指令到ESP8266模块,AT+CWMODE=2是将ESP8266设置为AP模式,\r\n是换行,因为AT指令加换行才能生效。
sendString("AT+CIPMUX=1\r\n") ---- 允许多连接
sendString("AT+CIPSERVER=1\r\n") ---- 建立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,<client号>,<收到的字符长度>:收到的字符,比如+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;
....
....
....
}
}
本文完!