一、系统方案
手机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; .... .... .... } }
本文完!
版权声明:本文为CSDN博主 daxiniot 的原创文章,
遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://blog.csdn.net/tongxin082/article/details/108685179