|
EDA365欢迎您登录!
您需要 登录 才可以下载或查看,没有帐号?注册
x
一、系统方案7 w- t9 M1 K! b* N6 K
2 x/ N5 B s! N1 X5 k+ n
' F$ ]- R' ?' }. g' `
手机APP通过ESP8266 WIFI模块与51单片机通信控制LED灯的开关。下位机由单片机、ESP8266模块和LED灯组成,上位机由Android手机APP承担。我们在APP上发送LED灯的开关控制指令,ESP8266将收到的数据发送给单片机,从而实现对LED灯进行开关控制。5 ^( L/ n' t- {* e6 l; j
# R6 i. f, M7 m/ s$ W
/ c, Y% B: \5 h2 ?# K( f
4 f- f* K6 B2 w+ I
. o. M# B8 z+ a. }0 R1 X5 O设计好的实物是这个样子:
' @6 `+ b1 \# y. P$ K; u% Q
9 ~8 M& j. C4 l1 Z
6 ~3 |* [$ W( B1 N$ j/ C, D3 L- g! D% n
+ V; v* x9 v- B
二、硬件设计2 ?6 h* p$ p5 O
b4 N$ _5 J& g3 r! [5 v. }: z7 x1 c1 t+ K
ESP8266模块作为一个透传模块使用,RXD、TXD分别连接51单片机的TXD和RXD,VCC和CH_PD管脚接3.3V电压,GND接地,只需要连接这些管脚,ESP8266模块就可以正常工作了。在单片机P2口上连接了三个5mm的LED灯,分别是红黄蓝三种颜色,我们的目的是通过手机APP上的开关控制着三个LED灯的亮灭。
& k+ I, E0 p4 X |2 W: K
" N; G; ], A8 I- F( b" y. c
( M( b$ a- y w$ M! Q( ^4 j
4 X" @5 g8 v( T3 Z/ ^; ~4 x3 _# N- i. A
ESP8266实物管脚图; I( S6 \9 y5 o: A
, E/ F% x+ ~( E6 h/ \
; `. ~& N* T7 d" p3 y: }5 H6 R- f9 y9 u9 A S
) E! U" D7 L2 o8 s8 c. a o/ ? w三、单片机软件设计
9 I9 u) u9 ]6 K3 q! F" S) L
/ N# W6 S I8 k/ F* v' e5 v- l, y0 L0 f
硬件的连接不复杂,接下来主要是单片机和手机APP代码的编写。1 L1 F; x. ~- p( @, v2 E9 S6 a
, ~* b+ ~; \5 W ~+ v4 q% l. O
0 {8 b. N8 ]* Q3 U8 S3 S单片机代码主要是串口初始化、串口中断和ESP8266的初始化。首先是串口初始化:
! ^2 ~( V9 O& h% I& w0 a& H
9 z$ \) r3 F$ S; \/ `4 B
, w) X- u- c& O1 j) P3 w# S5 L+ V TMOD=0x20; //定时器1工作在方式2
6 c1 E+ `0 ?# i& u# D* O TH1 = 0xfd; //波特率9600& M4 C" u* F) }% m
TL1 = 0xfd;) @" a; c/ i# p
SM0=0; //串口工作在方式10 g& T$ @- p9 x% b* t6 q! i
SM1=1;
5 f @/ D6 |, v- m' d EA = 1; //开总中断5 |. G7 R6 Y* J' l0 {, C; h( |
REN = 1; //使能串口! {# s1 H( Z& r
TR1 = 1; //定时器1开始计时# v$ g W; G2 Q. b: \+ f1 @) [
然后是ESP8266初始化:
" o$ k8 V. Y/ n" j" S4 d5 r' D ~5 Z; h, F
0 A8 ~: q' {8 F) f delayms(1000); //延时. S9 B; o& F0 o
sendString("AT+CWMODE=2\r\n"); //设置ESP8266工作在AP模式下/ S8 M$ G2 l/ C. a$ }
delayms(1000);
8 d1 l2 J1 j8 K0 D: Q) H5 { sendString("AT+CIPMUX=1\r\n"); //允许多连接, e! ?8 V. k5 s- R
delayms(1000); 5 P# T7 k0 U' T( ~9 o
sendString("AT+CIPSERVER=1\r\n"); //建立服务器, B! b% X- N/ ~2 Q/ m+ {1 ]
delayms(1000);
7 N5 k8 j9 m( m9 |/ \$ W ES = 1; //esp8266初始化之后开串口中断# E F: _( ~5 b/ }* u! v9 B
贴上51单片机负责串口发送的两个函数:" a+ V, U- {9 p- J6 D
+ ]: M, H) K7 x
! X( U' X, B5 v( Y) b0 b6 n: U
//发送一个字节
d* V" D. w0 h* ?6 X: Ovoid sendChar(uchar a)% m( z) ]' E& ?0 U
{1 m2 Q7 g& P' K8 _( t& J) O
SBUF = a;$ a; T/ a) W' g7 W1 ^( ?
while(TI==0);
6 n+ ^0 i8 \" p, v# I8 @; w& h TI=0;
8 a1 m" x5 X' \1 w
8 p/ f, I7 R2 r. j0 E& H}
0 ?! s9 Y k' E/ Y9 S3 b
- x& L D$ m$ _0 C/ H2 ]//发送字符串
( E% q/ k0 r7 [# k! V; T X. Lvoid sendString(uchar *s). |- b/ ?- `1 z/ r' _7 J
{2 d4 A2 Q+ V! N. K. S$ d
while(*s!='\0')
, c& E: C, G* J {
: _. z" w; H& d) p+ Z i/ ] sendChar(*s);
8 t5 G7 `' x# ]# j) y$ w5 M s++;3 K( K& V# t* I" n/ _
}, ^' r/ k2 T) M/ W$ o
" U9 J# d& i) J: E+ s}$ ~( U0 L1 m1 H
在串口中断中处理接收到的数据:
" F" v+ x2 @8 y) D- A/ B
7 \/ @0 M$ d, a( Q G( q- w1 I& h7 l
1:打开红色灯 2:关闭红色灯
* L9 J: i7 z6 x7 a8 `9 N C' x- o: A5 D; g' b$ ?/ l! ^/ s
3 \# E& U2 z* O( k$ Z/ z3:打开黄色灯 4:关闭黄色灯2 L; E" w( G a2 G" H2 b& \" P
1 ^: d# |. Q: ^8 I9 F
( f$ Z6 `; P( Q) D5:打开蓝色灯 6:关闭蓝色灯
4 I* n: L$ A9 q+ U6 o- F
I$ R6 d" Y8 v2 x. x
9 l3 i' d% s% z( jesp8266在收到数据并转发给单片机时的数据格式:+IPD,<client号>,<收到的字符长度>:收到的字符,比如+IPD,0,5:hello,其中+PID是固定的;0代表的是TCP客户端编号,esp8266最多支持5个客户端同时连接,也就是说客户端编号是0到4,在本设计中由于只有一个客户端与esp8266相连,所以客户端编号是0;5代表收到的字符长度;hello是收到的字符。在本例中esp8266发送给单片机的数据是+IPD,0,1:1,我们把接收到的字符串缓存到字符数组中,所以在处理收到的数据逻辑中,首先判断是否是以'+'开始的,否则视作无效数据,然后判断数组中的第十个数据,因为第十个数据才是上位机发送过来的数据。
$ E+ c& S( i( j8 X' v7 o3 T& H2 `/ c. T! w1 y8 m# x, |
( I& A, k- h) r# \1 T( }5 d. `
void uart() interrupt 4
$ j1 q. d9 p R' Z. E{
7 A2 K# }# {5 D) d1 K if(RI == 1)
H1 C1 ?0 Q& o3 m+ @ {' V a7 }8 F, m" l
RI = 0; //清除串口接收标志位, y; ~9 Z: Q/ s6 p4 f3 j1 T% v
receiveTable=SBUF;% P) K" b5 G2 a" v7 P; T* H
if(receiveTable[0]=='+')! W8 t* c: X5 f2 y/ _3 I1 g
{
. ~5 V3 E4 ]9 l* { i++;
3 k5 M+ ] b, ~4 V9 V" K }, Q! v5 C2 k! n* [7 [( t
else# o8 C/ X9 W- W' z2 W% ~+ u; M3 f
{
: S' w0 g, s7 X3 A5 ^ i=0;3 Z/ @+ z) y( U4 C( k1 d
}' f/ x6 G3 V3 z8 U" L. B
if(i==10)
/ C8 o1 T( B+ o+ b* j {7 Y* \7 Q" S/ s+ i6 J, ~* y# L
i=0;) p" Q( G4 B" O
switch(receiveTable[9])& y3 w4 Y) {* m7 _# j
{* m- y8 c% ^* f" E
case '1':- s/ @+ k7 J( G
RedLED=0;1 m: Q& Z+ w- n9 Q0 K! j
break;
7 R; Y' A- k: A8 }8 v- t% u' E0 a# u case '2': ?% w1 p/ H" | r$ D! K; ~8 m& F5 t
RedLED=1;5 x5 E' l3 k# Z- Y/ _6 y
break;: N$ h4 w2 I. c; E4 r
case '3':+ c4 @# ^* x0 C# a: A
YellowLED=0;' @7 z) N9 o, g. Y/ T. c
break;
W6 ~1 M n/ q6 c! \: z+ @ case '4':
) W( e4 i+ I0 h& m; p T. { YellowLED=1;0 O7 Z4 M* v: [* X' X9 C8 T
break;
3 t: D0 l. V+ R case '5':' ]7 d2 S; `! v- {. P8 g
BlueLED=0;4 u8 e ]% S8 b% A# ]3 K
break;
! p, v" Z/ C3 V9 A4 U! i- \ case '6':2 q; ^3 X) W _6 P+ z
BlueLED=1;' F6 L2 P0 j3 F& s' P
break;
1 p5 Z7 ?, J( D1 o X }/ e2 S& j2 Y5 [8 g$ k! [1 }
}; r' Y8 o% w1 q: Y5 D) U- w
0 q1 `- ]8 ~4 @
}$ j' l0 T+ d8 W
}
! b' l& Z( `( t四、Android APP软件设计
9 u+ F% ~! {7 L, R* q) a& S1 U6 H G
; B; Z0 ~; T$ w+ y6 X' Z7 G6 `
Android APP是借助Android Studio来开发的,界面比较简单,通过编辑框输入esp8266的IP地址和端口号,esp8266默认的IP地址是192.168.4.1,端口号是333,这些都可以通过AT指令进行修改。布局页面的xml代码我们就不贴了,熟悉Android开发的读者很快就能根据截图编写出来,放上一个APP界面的截图:- g9 _1 ^5 d& N" D* _
3 K) O" @: f* T; p" P
+ z% @2 {& S" l
1 A$ B4 w8 z: N0 X" N
( V" o8 y) U8 ~: g2 e% ]$ H$ D+ d* {' q" ] d
1 @2 i$ b b7 y我们主要看一下逻辑代码部分:2 q( j1 l" Z( v; Z& A: H
, G/ X9 j, G. D7 U; z
0 X) ^$ r. q- x1 h0 C% J首先是控件的初始化和按钮点击事件回调的绑定9 s: `2 [- n, U& I0 g4 a
& o& s. i1 d3 x4 h5 H- `) s: i( U7 i W! {
@Override
6 a1 {/ c' ~( k3 R2 N6 h: K protected void onCreate(Bundle savedInstanceState) {! c' C+ e( x: _ J0 I
super.onCreate(savedInstanceState);
2 v7 o) U- C8 j- F' Z2 f) k% ~ setContentView(R.layout.activity_main);! M) i* a" k+ ~ g* ]& O; X0 a. k
mBtnConnect = (Button) findViewById(R.id.btn_connect); ~1 [) b2 J' [1 g; S/ o& V4 Q
mEtIP = (EditText) findViewById(R.id.et_ip);) o% y# }2 e# u8 h# ~
mEtPort = (EditText) findViewById(R.id.et_port);1 b$ T4 v/ h8 q7 }- I4 ^
mBtnRedOn = (Button) findViewById(R.id.btn_red_on);
( q5 \5 w! F% H$ E mBtnRedOff = (Button) findViewById(R.id.btn_red_off);8 p' e1 V9 C+ @# c9 {. A
mBtnYellowOn = (Button) findViewById(R.id.btn_yellow_on);
# V0 I6 y. E5 h, j$ } mBtnYellowOff = (Button) findViewById(R.id.btn_yellow_off);6 V5 E4 {0 i9 a2 z7 a
mBtnBlueOn = (Button) findViewById(R.id.btn_blue_on);
: `& X" [- n- J: H0 i; G mBtnBlueOff = (Button) findViewById(R.id.btn_blue_off);
, E9 h$ D' u" x! t+ B: j4 \ mBtnConnect.setOnClickListener(this);3 q- @. ]/ a: u* w' o( d
mBtnRedOn.setOnClickListener(this);
% q+ U% T" o/ h% u3 O0 |3 s3 y1 b mBtnRedOff.setOnClickListener(this);
+ t7 l1 X# S/ H. N mBtnYellowOn.setOnClickListener(this);
& J0 l' ~# m* L' s4 b5 ^; i6 | mBtnYellowOff.setOnClickListener(this); r# `4 f e# F. q4 \) \
mBtnBlueOn.setOnClickListener(this);
% o1 M( c% H& ?0 o' p; Q S mBtnBlueOff.setOnClickListener(this);# H# r5 Q( a+ Q4 ?. M
' _! K* {, r5 t) W9 G5 T
}! r* h, b: ~7 [0 D" D2 s
按钮的点击回调方法:" N6 J8 B9 V: O U7 Q
. K, v2 A! v; t) o( w0 R" d3 b+ K0 k2 Y5 W% B" g" t/ i
public void onClick(View v) {
- {" w# P- x, x; U D9 P switch (v.getId()) {" D0 U" Q3 Z$ @* `5 M+ K7 N" \
case R.id.btn_connect:
% ?3 { A; |8 z5 E2 l //连接
; O# U* K7 \* Y7 N' E/ t, p' f% S if (mSocket == null || !mSocket.isConnected()) {
" {; b: M5 y0 \ String ip = mEtIP.getText().toString();
n1 V9 H! L+ i4 C6 H5 i& W0 d8 a int port = Integer.valueOf(mEtPort.getText().toString());5 d: P6 \& `- b) B- ?( Z9 |
mConnectThread = new ConnectThread(ip, port);
6 p+ r) X0 C* s6 e6 o' ^* u mConnectThread.start();3 B5 x; [ |3 L3 i/ x+ ?
}
& b- t% e5 v) n% o7 j if (mSocket != null && mSocket.isConnected()) {
0 \& F! Q& g; ?2 x try {
7 C5 o0 ?+ k7 o9 Q mSocket.close();9 k5 S- Z( c0 Y0 Y& a1 Z) w
mBtnConnect.setText("连接");
- d; P# \; E) n } catch (IOException e) {
2 t% q8 n+ y8 a% p( L/ @ e.printStackTrace();; W# |" t! p3 l' T7 e
}, ~, e; b( w, B# N
}7 E- T2 O+ F( _5 f9 M" I' j
/ ~- J/ G2 }/ e* W6 ?2 J) i& l# ?; s
break;/ `+ f* [7 W. M+ L6 V6 j6 Z( J1 }
case R.id.btn_red_on: //开红灯, p. S5 y* ]( }
if (out != null) {
- m$ [7 s0 c2 q G, E5 g out.print("1");$ l& }- P* s% w3 @3 {
out.flush();
. m A" e* ^. {* U }) S1 m9 R- a$ W
break;! @, r# S1 a j7 ?
case R.id.btn_red_off: //关红灯
% X; z4 f5 Y$ ?/ s6 O' q if (out != null) {5 G, \* J8 C4 m6 H: A6 i. `1 a
out.print("2");8 y7 Z. t/ h* o& O+ a; I& v
out.flush();
1 T- V" C$ f& g }( {# v, m( O( Y! D8 P$ X- |8 H- t! P7 o R
break;
. F2 D9 u: P3 ~ y- K' M case R.id.btn_yellow_on:
" {9 G# [. G8 O: \ if (out != null) {9 P: ?2 R$ S+ V3 t" p
out.print("3");1 O5 n" I3 V3 ~) @* q, S! X
out.flush();' l7 D; Y) p+ X7 g# H/ O+ }
}" X8 A% ^5 p$ E9 k# C
break;
6 m% \9 [4 n7 ^/ y8 p( v7 e case R.id.btn_yellow_off:
$ S4 A: `" p: z1 Y9 O0 K. E if (out != null) {6 f# [% T" w$ p+ P) e) \
out.print("4");
1 a: }3 Z5 ^$ N3 \ out.flush();6 a3 X' q7 n( y+ r+ X
}3 O, H: G( ]! O% C1 j( a5 i
break;
v0 M. ]1 y E- ? case R.id.btn_blue_on:! P% g# ~( S$ X4 Q% q! z7 H
if (out != null) {; v3 P) d; r8 s& u7 z0 y0 R; q( i0 a6 P4 S
out.print("5");
; r! K/ R; ]6 `$ p* v M! \7 V out.flush();
, D- {% N! @ ]4 r. ?8 n }
: J. T0 Y) G6 O1 ?; @ break;2 i, V0 o+ `, Z( d
case R.id.btn_blue_off:# g! K% u6 L3 N y9 E. T' N5 Q1 |0 t
if (out != null) {
: r2 l E/ k d" \ O/ D out.print("6");1 m9 z. z4 y1 r5 e
out.flush();+ |+ E; O& C* x
}# R0 \' F9 I, Q
break;
( [ D' M$ J6 Y% o2 G c# T }
0 Y' ?9 o+ Y/ n, g$ d0 u' @5 Y3 X7 K }8 y- J" M% }) Q% r* I
6 Q5 R/ F$ C3 Q9 ~* W
负责连接esp8266的线程:- L, G2 @8 [8 Q" ]' m9 O' {
+ N5 i/ L/ [. M$ m! G9 a( k& v; h
3 I% B$ g B) T
private class ConnectThread extends Thread {- X% F+ F _9 `1 a! `
private String ip;& C+ H5 t( p' ^9 T; F1 k
private int port;
' K5 \% u, W6 T- _ # U) G0 h+ t H
public ConnectThread(String ip, int port) {$ G: G; G0 N4 {) t# l c
this.ip = ip;/ F6 v8 j9 J; a \8 O' Y
this.port = port;0 x4 H$ x% ~# }. n+ ~& Z0 I- c
}
! s4 k1 d& i; v % |. \8 U. A6 @2 p
@Override% G s+ b0 u( b7 D2 J
public void run() {8 n; Y8 K- l$ i i( z8 E+ E+ \
try {
- T& ]# D1 [7 s1 Y. }( `; G; S mSocket = new Socket(ip, port);
, }1 Y& P K- t1 L8 C, k out = new PrintStream(mSocket.getOutputStream());
& y) w( e! x: e) y% }$ d runOnUiThread(new Runnable() {
; ?: m b, ]! K, x; M$ Y# B @Override' M* V. o1 ?. K- Z O) t
public void run() {
) ?5 C' h8 q s, c( P( E mBtnConnect.setText("断开");
; S/ M0 x$ a8 C. t }5 C2 \" K# Z5 ^9 R1 R, e
});
! |! D; H* x8 i7 q1 x new HeartBeatThread().start();/ K( ?0 [$ Y. _6 P9 y3 @
} catch (IOException e) {6 f K( x( {" s1 o3 k. y
e.printStackTrace();
% o9 @: f* E$ r" F! A/ L) _ runOnUiThread(new Runnable() {
+ Z9 r8 j7 {, u @Override# g: ]& A/ ]- {
public void run() {" u8 q* F6 d% l. |, L( O% X. y n
Toast.makeText(MainActivity.this, "连接失败", Toast.LENGTH_SHORT).show();) c2 V) R0 W0 E+ s
}
7 z0 J, u: l! w- m1 F% [ });
$ d* i5 c) k+ Y- U" ^, d }5 X/ E: s: E, O: r! R7 W9 Y- t3 @
}% r7 i% ` i7 p5 t5 A% W5 J8 v2 C
}
* M6 {4 \4 {) [) Q6 |( p) F. E
- H% p% ~1 s5 w) A) p" f& o+ y6 j$ c5 {; k
% y! u' b# K1 c) Y
/ h! {4 |2 Q; D5 Q* [* |/ @" \ |
|