|
EDA365欢迎您登录!
您需要 登录 才可以下载或查看,没有帐号?注册
x
一、系统方案) B2 e! A& _: N. t; H
! D9 p( r& m. c3 M3 L: |: x
: w6 G" A$ k! @. A/ O. h+ X
手机APP通过ESP8266 WIFI模块与51单片机通信控制LED灯的开关。下位机由单片机、ESP8266模块和LED灯组成,上位机由Android手机APP承担。我们在APP上发送LED灯的开关控制指令,ESP8266将收到的数据发送给单片机,从而实现对LED灯进行开关控制。
; y5 i8 p- n0 E3 {
6 {2 C- U5 d0 w! Q# O' s0 _9 U- l0 D: O2 c
" k# K. Z4 l0 I# V. t: ~6 d8 C3 N. t6 X& \! |' H: `: m" w {
设计好的实物是这个样子:
9 H S2 y) X4 I% i4 p% U% t4 y0 X" @+ r, ]
5 k/ u8 Q, z8 O& |) T8 J+ ?' N
7 [, r; x& ~, B7 u( g" ~: i% ]" o' F5 k b; i( o/ s
二、硬件设计- b% x9 U2 {; s
4 {5 T" s" N& r- Y, H
# o) I' w# ?* B- w4 _1 r& P5 SESP8266模块作为一个透传模块使用,RXD、TXD分别连接51单片机的TXD和RXD,VCC和CH_PD管脚接3.3V电压,GND接地,只需要连接这些管脚,ESP8266模块就可以正常工作了。在单片机P2口上连接了三个5mm的LED灯,分别是红黄蓝三种颜色,我们的目的是通过手机APP上的开关控制着三个LED灯的亮灭。
3 ]& q( [& T, |; M2 b/ j+ }
& C( c5 ?' g9 g
8 {2 d& b9 F3 R% J; G. _) N- S
# R! S$ Y3 j7 o, K+ |6 b. E0 a7 [, ~
ESP8266实物管脚图% R: k# x/ }2 @5 o
/ T& q+ O" D0 D- }/ e
3 a q. q: p0 j4 }+ @* @+ h9 F1 E- w- \6 e) G" o% k9 ?( X2 ^" w4 u
5 M+ m3 v3 W. R! h9 p
三、单片机软件设计9 x U1 y0 T4 Y0 d0 S/ K
( ^. h# h" A6 z9 I- h: [, F( \( r) w3 Y4 D% `# s1 i: S% h* O, y
硬件的连接不复杂,接下来主要是单片机和手机APP代码的编写。
+ y* d! X+ Z% q7 E( `
5 \+ w+ ?3 U. `) H% e
: A5 ~' q% E w, S" h1 Z# g3 }单片机代码主要是串口初始化、串口中断和ESP8266的初始化。首先是串口初始化:
6 h2 S7 O& ~- z7 G6 {6 l) b+ f$ w- N( L# b4 n9 T+ l7 ^
; P; {8 d9 D/ O0 f TMOD=0x20; //定时器1工作在方式2) i1 ^# L8 g7 q4 h* y8 z* }
TH1 = 0xfd; //波特率9600
0 K* r2 E% {* o6 q! c TL1 = 0xfd;
* T) p+ J' b/ p SM0=0; //串口工作在方式1
9 Y7 k# U0 V+ [ SM1=1;" C* z& Q) U; f2 T% V+ v
EA = 1; //开总中断
7 K/ f5 W8 i' g, |8 } t: `2 e( r REN = 1; //使能串口
9 X+ r: H! a8 E! u' f/ P% ^& E TR1 = 1; //定时器1开始计时- a5 _0 c* H' t9 L
然后是ESP8266初始化:4 x* r! z- v& u% E7 c' G: h9 M
4 V2 Y5 i9 }. n5 f# h* \" E5 X
7 K3 G9 }( w2 \ delayms(1000); //延时
0 O" z6 p/ V Q C* | sendString("AT+CWMODE=2\r\n"); //设置ESP8266工作在AP模式下
2 T" p* U" @5 b9 T; l+ E delayms(1000); 3 J& f: ` p/ T3 g
sendString("AT+CIPMUX=1\r\n"); //允许多连接* D8 N$ Z( f; p3 G& p- C
delayms(1000); 7 g4 V/ @1 T5 R
sendString("AT+CIPSERVER=1\r\n"); //建立服务器1 Z [( x' z# e9 }& P: f' \' X
delayms(1000);
% P$ J! l! d) ?! \2 S ES = 1; //esp8266初始化之后开串口中断
5 J9 G! ~% G8 p9 h5 h贴上51单片机负责串口发送的两个函数:0 o: f/ T" z4 I
[$ u( O/ [; L- x* R$ e
! j7 l1 _$ H, r+ x/ d# m
//发送一个字节5 ^ B2 t0 d6 o$ [3 _
void sendChar(uchar a)$ o( B: Q* k) z' P
{
$ i; S+ B9 q( n- _7 _8 G* o) Z SBUF = a;+ W+ ^9 {: ]! P" E
while(TI==0);
7 F' m% o8 j7 f% t& G5 Y- Z( z TI=0;
/ w6 s Q7 ~* W$ R6 R
, I6 R" j' ]: P+ x( z \}
7 t6 A$ o, u( `5 V 4 G( X6 i3 q# g, o! j
//发送字符串7 O, {5 J* f* k v9 `2 s
void sendString(uchar *s)
7 o* |1 |4 _1 D: q4 W+ k{
9 c& N: ^/ `# b4 o3 V while(*s!='\0')
8 a8 W+ Z8 D( w; |% ?/ ^9 h {9 B; S' u) b1 z+ @6 _: D
sendChar(*s);9 Z2 x# l! m+ P
s++;8 y8 n5 g+ }% s1 Z# U
}
$ D! F( E1 Z+ r- B0 H $ A) N( ?% e9 ^0 B
}5 k+ a$ \6 K- l) A ?1 l' E( {
在串口中断中处理接收到的数据:
# T! v2 E2 N& M: M0 }8 K& }: K f& T6 h+ F9 A( W) E: a6 c
! k. \1 H1 N, G$ B1:打开红色灯 2:关闭红色灯7 N/ `6 t" H, [0 W; E4 }: [5 H5 I2 ]
. F! [: ^; y$ q" H7 N' B# }1 i! s$ S; ^2 j! B4 q7 J
3:打开黄色灯 4:关闭黄色灯2 l7 P9 s' ^4 G$ z- i1 U; Y% R( Q
, g0 E/ J( @' V" R1 \! [; y) V0 H( k" G; y; X
5:打开蓝色灯 6:关闭蓝色灯
' ^3 }4 q( c9 V
3 n+ R0 Y( C) G: o$ O' f G6 v
% d8 D, R" M5 u! ?) a+ \esp8266在收到数据并转发给单片机时的数据格式:+IPD,<client号>,<收到的字符长度>:收到的字符,比如+IPD,0,5:hello,其中+PID是固定的;0代表的是TCP客户端编号,esp8266最多支持5个客户端同时连接,也就是说客户端编号是0到4,在本设计中由于只有一个客户端与esp8266相连,所以客户端编号是0;5代表收到的字符长度;hello是收到的字符。在本例中esp8266发送给单片机的数据是+IPD,0,1:1,我们把接收到的字符串缓存到字符数组中,所以在处理收到的数据逻辑中,首先判断是否是以'+'开始的,否则视作无效数据,然后判断数组中的第十个数据,因为第十个数据才是上位机发送过来的数据。1 `2 h" `+ C: r8 {' z$ b
8 m: l5 U: T0 {
0 ]! O- B% t( u# a8 Wvoid uart() interrupt 48 O* S6 X E" A5 o; ?
{$ L8 F3 o! @! A3 c5 e* R8 a* g
if(RI == 1) ( V9 ], j1 y7 J0 y
{
/ |7 E) q! |9 Y1 c: {9 j! e& p8 O RI = 0; //清除串口接收标志位. v4 ?5 K" N* K- k& r& [* Y4 R
receiveTable=SBUF;
% \0 W' T/ f) e: g: | if(receiveTable[0]=='+')
- y: L1 Z1 S7 R, |4 \0 Z1 { {0 \# Q7 U0 o+ F/ z8 I& \
i++;
5 R" S% J6 \% \) o }
: ^- C o0 b! s, K( N else* p% B" L# z$ B+ @* w; l# A+ O
{. s. U8 s6 O; b- e0 n
i=0;; [8 q0 _4 c6 j7 l( m3 R: f
}
% Y6 b2 \3 o7 `/ V5 l if(i==10)6 @. i; m- n0 B& n
{
- t. m5 e' Z2 v& ^& x i=0;4 }( j7 I; J- m, s3 k
switch(receiveTable[9]). g/ z/ \1 \1 N h3 D
{' W( m" G' m4 r
case '1':
* S. V* x2 s1 c9 \, W% B# } RedLED=0;( ?) |; y" B4 J" j1 V2 U2 U1 o
break;
- v; @) Z) e' Q9 N, B case '2':
( Q4 b6 C$ O) n2 `8 P7 w* F RedLED=1;+ ]) y0 n& k( o+ @4 I/ h
break;
- } S3 U% H9 W' }0 g- B! b6 h case '3':
, V" k( r+ y+ W( i) y1 V YellowLED=0;$ g" E5 n7 i) y
break;
8 \7 A5 F9 ~$ F: Q6 s% B. z case '4':
2 B& F7 e! n& c3 g! g" G3 x YellowLED=1;
9 h( e. V& y1 q; a( q break;
6 e) @, }' C) y( M- |% v case '5':
: a! O& n) |0 x9 ?+ H BlueLED=0;& y l( ]- B" m% u# d
break;
- X% O7 V; q: `; k, s% ~, C case '6':, f$ r) K8 G6 q
BlueLED=1;
' ]/ f, ^3 ~+ F2 Q break;
$ Y% T2 M4 w }) [0 p4 J# [. @ }
1 H9 h3 n% q' A8 Z }# h* Y! B) Y, I. c! J
/ ~+ {" _4 q8 [2 `$ l
}
, T3 o; k- ]: U, Q) Q' w' Y+ f}
3 ^# W. g# M3 m4 v7 r% s. Y% h- {四、Android APP软件设计
6 {! g8 T3 a3 o: s- A4 H; {$ o. c2 R) ]: d8 k- b$ M
5 g% h: C; f. [+ h/ e' F8 u$ {Android APP是借助Android Studio来开发的,界面比较简单,通过编辑框输入esp8266的IP地址和端口号,esp8266默认的IP地址是192.168.4.1,端口号是333,这些都可以通过AT指令进行修改。布局页面的xml代码我们就不贴了,熟悉Android开发的读者很快就能根据截图编写出来,放上一个APP界面的截图:
! V7 y& [5 ~3 n9 r2 a( T! p) ]9 [) p5 N9 N
( f4 V' z1 Z$ b
# K! I% T, r% P
0 M7 Y1 h# `. y( L. d' _ E3 q! H) i7 E7 m, s/ v u" G f
9 l5 W2 i0 \. n( Q% @. k* V" u我们主要看一下逻辑代码部分:
l) H! X6 }% z% C6 `3 f2 d. R
" E& j! L9 L( e9 ^
% i I* }* |0 Q6 q) D首先是控件的初始化和按钮点击事件回调的绑定8 A( Q/ |+ m; z: t2 ~
0 O1 P9 M) L4 w6 p: E
3 Z! U7 ]8 d3 d% v+ P% F) Q' _
@Override
; m) [5 s8 b1 y( [; L2 L protected void onCreate(Bundle savedInstanceState) {2 z# Y" g: c/ m6 z2 f. f" G3 T
super.onCreate(savedInstanceState);* }+ L# d+ M: R B# g6 B. C% L
setContentView(R.layout.activity_main);! U) d: [9 K- f" i1 s
mBtnConnect = (Button) findViewById(R.id.btn_connect);
3 G7 N8 O2 ?* g( m9 n/ V8 a3 F1 u mEtIP = (EditText) findViewById(R.id.et_ip);7 D* Z% d- @) x: a
mEtPort = (EditText) findViewById(R.id.et_port);
0 m+ U* j7 x' H% h7 G+ D) _* s mBtnRedOn = (Button) findViewById(R.id.btn_red_on);/ B4 E0 y/ b3 T9 p& J2 w5 O
mBtnRedOff = (Button) findViewById(R.id.btn_red_off);
) E! F; @; g7 K1 J8 Z mBtnYellowOn = (Button) findViewById(R.id.btn_yellow_on);
. u7 f0 H! M# x" }. K5 ? mBtnYellowOff = (Button) findViewById(R.id.btn_yellow_off);
6 x2 G( D: p e' U/ r* B mBtnBlueOn = (Button) findViewById(R.id.btn_blue_on);# r! T# v% d2 J' i2 H
mBtnBlueOff = (Button) findViewById(R.id.btn_blue_off);
9 ?; f5 b/ k* x: f* C/ y/ R mBtnConnect.setOnClickListener(this);
' C1 y* U2 G3 f7 C mBtnRedOn.setOnClickListener(this);
1 l$ S( U; o5 n, i y) O$ K2 s mBtnRedOff.setOnClickListener(this);
! B( j& c0 i/ h- @3 X7 c mBtnYellowOn.setOnClickListener(this);
; ?" j2 j1 i. W9 C+ a mBtnYellowOff.setOnClickListener(this);2 L- A* m+ z& u5 x- H: K2 Y
mBtnBlueOn.setOnClickListener(this);$ u3 ] q* S, y. C2 l: z3 I
mBtnBlueOff.setOnClickListener(this);
3 E$ D1 I" u/ [; N
! g+ o1 z8 \4 S/ U. S( M' _( f' M U/ r }! C( H) f$ ?; p; R5 w
按钮的点击回调方法:
& A# s/ j c3 S8 o* D9 d/ o
5 V/ I {1 j E) d$ x+ K- Q$ W' i! s0 b N5 `" Q
public void onClick(View v) {9 T* q4 Q! ?! Q0 U/ D$ K! E' B2 e& ` |
switch (v.getId()) {
" p0 X( R. F/ x9 t case R.id.btn_connect:
6 U1 ]% R0 I$ n% O7 W% |3 i# J //连接9 | Q9 ]9 l( E& t
if (mSocket == null || !mSocket.isConnected()) {
' Y6 |" V2 [/ N& ?8 |# K B String ip = mEtIP.getText().toString();
& X) y2 Q8 \: ^9 V int port = Integer.valueOf(mEtPort.getText().toString());( a, g. d {6 C8 \3 }! g" t
mConnectThread = new ConnectThread(ip, port);
! Y/ e( U* j2 ^6 t mConnectThread.start();1 R& A5 g& @8 N6 i I
}) |% |9 k& j* o+ o
if (mSocket != null && mSocket.isConnected()) {
1 v$ m* d, u$ l* c8 D: L! O0 B# }0 z try {
+ t6 k9 T2 W5 _ mSocket.close();
! b7 Z! _% n2 g5 e; Z mBtnConnect.setText("连接");" `3 _, @- @' Y! m) d* i
} catch (IOException e) {
$ y# }. B/ ?. q) F e.printStackTrace();
! Z2 h- s6 ~7 z }: v* d. t" o3 k# P6 c
}
2 b6 W. |+ `1 Z6 T& e U; F1 w$ c9 l8 W+ q" l) \/ o
break;
) K- S4 F4 I* @' v' G( B8 Y7 V' ~ case R.id.btn_red_on: //开红灯
& R* o$ c7 {0 x8 b( K if (out != null) {8 g% M6 u$ r8 j- { u" W: r$ V+ V0 X
out.print("1");$ `8 u0 O) w0 m: j+ \0 }, Y5 A
out.flush();
' f& K, M/ r' Z2 {# q/ ~ }5 C- Y) \1 g, k3 @3 c+ @; Z7 |
break;2 O1 I1 T9 H& c* k e% J$ H E
case R.id.btn_red_off: //关红灯0 }) q) r2 p1 @6 K+ O' G
if (out != null) {
, y4 Y7 g/ J. k# W out.print("2");
/ m U! z% O. H out.flush();
* L. N( c2 ?( z% P* Z4 x }- ?, } a) J1 v" M( n3 t1 p
break;
& H3 d9 p) V2 v/ l8 b case R.id.btn_yellow_on:, U( k6 j& q9 p2 i% s
if (out != null) {
9 M6 R: V: k/ W( e out.print("3");
7 C" S8 q- N/ k9 u out.flush();
* U) {2 S- ^9 O# g) d+ \ }
' _# m0 `5 y1 f _- Z J3 f break;% x. n- D2 x2 p9 V: W
case R.id.btn_yellow_off:" O, ~8 N* ^( V& l5 e
if (out != null) {
- M1 t; d) m; V5 p0 U out.print("4");
3 Q9 G8 H1 a! Z! K% M: g out.flush();
9 x( L9 c# w& ?& N" T }9 K) A! D9 C! d: B1 N$ E) f
break;
# N5 L7 |4 q) p. d case R.id.btn_blue_on:+ q# o% s; B$ x/ ]9 X) A( ~
if (out != null) {) h w7 D: t, n( z+ O# Z
out.print("5");0 y! p' Y1 M; J3 R& V5 ^
out.flush();
V" g! y: M2 b4 ^3 ~ K1 l* K }
; J. _( X' o; H+ X9 S' k2 X% z break;
# g, H- s5 d6 C# a% J case R.id.btn_blue_off:% S$ i/ L7 O* G# y% b! O' ~3 ^% g
if (out != null) {
0 N, G J- Q1 z, Z4 G3 L out.print("6");) L6 U# t6 \, t5 u
out.flush();
+ Q5 a7 {: f6 P" m }
1 d8 ?* y6 a9 Y' F' x5 q9 S break;2 I4 X+ s8 z6 ?& V# c& _+ x& @6 z
}4 j- y% ?( }& h6 e9 H
}
: \% d" a& ?, v' a 6 t9 ?) ]% x! H# U$ U
负责连接esp8266的线程:
5 b: \! o6 `3 a, v- m
4 c5 J/ c' A# ~5 g
. A, o& Y" V& o* p+ t" I, `private class ConnectThread extends Thread {
+ i7 I. B3 O' W5 j private String ip;' u6 B8 m% ]$ J: A: z$ }: l
private int port;2 R- t+ b% N$ o: R. u) b* o g. O
/ b) J% y' u+ W- z2 X- T/ G public ConnectThread(String ip, int port) {0 t, ]9 }& C8 u( [/ w
this.ip = ip;
) M( b" x$ s) y3 T1 r: J( b this.port = port;* n- N/ T( [. v, O9 P* k N
}
, z! X- v; u7 J3 b! o! s 4 @7 u3 X, |* W. Z
@Override
( s6 B! G) t# X2 e. f2 _3 l public void run() {" }4 b/ \6 C8 f5 t4 P" L
try {6 z! N" V+ P# T
mSocket = new Socket(ip, port);
- m7 z. s' D* E5 a9 F$ d( G out = new PrintStream(mSocket.getOutputStream());
/ b+ l; g1 e f | runOnUiThread(new Runnable() {) W- u+ Y. N1 K1 u( w+ O: s
@Override# e, r4 a7 p4 B+ P1 Q6 n9 |+ R
public void run() {
5 A% m* y1 `: y( A; X2 D mBtnConnect.setText("断开");9 u$ j; w' Y7 z! o* M% ^1 Q/ Z
}9 r7 o; G. T; W
});1 y3 R6 Q( I; y
new HeartBeatThread().start();& }2 }7 h+ o. |' u9 E
} catch (IOException e) {4 u- o) ^; M3 v# U; R; b
e.printStackTrace();: n0 L* [, x1 V- e' l
runOnUiThread(new Runnable() {
5 N* L% Z' ^4 j8 | @Override
, `9 @+ h# j6 B7 O: M0 M F, \6 @ public void run() {% o1 [4 ]! K; m6 P
Toast.makeText(MainActivity.this, "连接失败", Toast.LENGTH_SHORT).show();
7 p% e1 |' {, O }
6 j* m9 y- b' {) U' g, p });
9 U& e6 A; l. r! ]1 ?) s, v }9 E& ^+ l( w! k C
}7 d( U" g% d9 O! f; i
}' Y" t. |' y# x
( {/ E l" m7 Y8 A5 j, W/ [% z! {% X0 O7 w1 U
$ ^, y/ }7 v; a! q$ d- d
1 t/ y4 J% i! l7 P
|
|