文章框架
文章框架前言
扯单一周目BOSS出现!请速速讨伐!
ESP8266是我第一个,也是唯一一个接触过的无线网络模块,我非常喜欢!大部分网上的教程都是ARM架构单片机配用ESP8266。我比较抠门,给大家上51版的。
我记得当时和老板承诺可以做成无线通信的时候我也不是很确定,心里一直打鼓。好在最后做出来了,很有成就感!所以今天的代码案例是我把我之前在公司写的项目代码简化之后分享给大家的。虽然结构有点恶心,但应该还是入得了眼的。
如果有没看懂的地方,大家评论区告诉我。
需求
利用ESP8266芯片,通过无线网络同电脑建立TCP连接。接收电脑传来的消息并显示在LCD上。
每条信息以
\
结尾。
每条信息字符数不超过32。
信息内容只能是ASCII表内的字符。
清单
硬件
学习板
-
简介
QX-MINI51
由于这次要用到WIFI模块,没法用Proteus模拟,所以就上真家伙。
自己焊板子就太麻烦了,所以就买学习板(其实我不会焊)。
这是我入的学习板,你也可以入其他的,如果把握不大就跟我入一样的。
这个是我在淘宝上小挑了一阵子选的板子:QX-MINI51。尺寸不大不小,想玩的基本都有。配备的单片机是STC89C52。 -
电路
STC89C52
下面是QX-MINI51在本节会用到的电路图
使用89C52芯片,和我们之前用的单片机一样。
供电&USB串口
CH340芯片是用于普通串口和USB转换的,其上的RXD
为串口输入,TXD
为串口输出。UD+
和UD-
对应USB数据线。
虽说是用了USB,其实和普通串口通信线用法一致,只是人家成品方便我们使用封装成了USB。这里不深究。
流水灯
流水灯在本节中的角色是辅助调试(具体的看后面),这里有用的信息就是让你知道这8个流水灯接哪了。
ESP8266
-
简介
主角,简而言之就是Wi-Fi模块,属于网络层以上的设备。拥有MAC地址和IP地址,支持UDP和TCP。
ESP8266
这款的型号是ESP8266-01,其他的和QX-MINI51配合使用不太方便。
淘宝链接 - 参数
型号 | 主芯片 | 无线标准 | 工作电压 | 安全机制 | 支持模式 |
---|---|---|---|---|---|
ESP8266-01 | ESP8266 | IEEE 802.11b/g/n | 3.3V | WEP/WPA-PSK/WPA2-PSK | STA、AP、STA+AP |
引脚图STA 模式:ESP8266模块通过路由器连接互联网,手机或电脑通过互联网实现对设备的远程控制。
AP 模式:ESP8266模块作为热点,实现手机或电脑直接与模块通信,实现局域网无线控制。
STA+AP 模式:两种模式的共存模式,即可以通过互联网控制可实现无缝切换,方便操作。
上图对8个针脚进行说明。
UTXD | GND | CH_PD | GPIO2 | GPIO16 | GPIO0 | VCC | URXD |
---|---|---|---|---|---|---|---|
发送 | 接地 | 高电平工作 | |||电源 | 接收 |
本例只用到这五个引脚(画“\”的不用),其他引脚的说明资料请自行到淘宝链接处下载。
-
AT指令
操作ESP8266芯片是靠AT指令的,类似之前的操作LCD1602(《51单片机实战:液晶显示器のLCD1602》)。操作LCD1602的指令都是一个字节的十六进制码,比较难记和理解。AT指令是字符串形式的指令,一般都是单词缩写,对可笑的人类比较友好。
之前和LCD1602交互是靠并口传输,而这次是用串口传输(串口传输简例:《51单片机实战:与计算机异步串行通信》),所以这次的Wi-Fi通信是建立在串口通信基础上的。 -
波特率
注意,这个模块的默认波特率是115200
,本例也是根据这个波特率进行演示的。若想改变波特率请使用以下语句进行修改:
AT+CIOBAUD=<baudrate>,<databits>,<stopbits>,<parity>,<flow control>
如:
AT+CIOBAUD=9600,8,1,0,0
软件
程序 | 说明 | 下载 |
---|---|---|
UartAssist | 串口调试助手,用来给单片机发送消息 | 度娘网盘 |
NetAssist | 网络调试助手,在电脑端建立TCP连接与单片机的ESP8266进行通信 | 度娘网盘 |
STC-ISP | STC单片机工具集,很强大,可烧录程序,可串口调试等等 | 度娘网盘 |
CH340驱动 | CH340的USB驱动,如果没有这个驱动,你的电脑可能识别不到单片机(识别到CH340就相当于识别到单片机的串口。) | 度娘网盘 |
51单片机波特率初值计算工具 | 用于计算在各种波特率和晶振频率等参数下计时器的初值,属于辅助工具,省的还得算。 | 度娘网盘 |
分析
调试
在开始用单片机直接和无线模块通信之前,首先要绕过单片机直接和无线模块通信以确定其可以使用和接入默认网络(接入一个热点后,模块每次断电后启动都会自动连接该热点),这样可以让单片机少做很多事情(要知道我们用的是51单片机,硬件资源极其有限,能省则省)。
-
TXD/RXD反接
首先要绕过单片机直接给ESP8266下指令,就要用电脑的串口。但ESP-01是针脚接口,所以我们可以利用QX-MINI51上的针脚和USB接口。回看前面QX-MINI51的主控芯片和USB的电路图,可以发现,单片机的串口引脚是被并联式的暴露再外的,分别为:
P30 - P31
和USB
,前者为针脚接口,后者为USB接口。且它们的RXD和TXD的数据信息是一摸一样的,注意并不是两个独立的串口,是同一个串口。比方说,单片机要往出发送一个1,P30针脚和USB都会是往出发送一个1。所以我们利用这个特点,将ESP8266的TXD接到P31(单片机的TXD),RXD接到P30(单片机的RXD),这样就等于让ESP8266直接和USB打交道,也就是和电脑直接打交道了。
为什么叫反接,我一般管RXD对TXD叫正接。嗯…
接线图
强势秀一波画工!
我觉得应该可以看得懂吧,ESP8266的引脚说明请看前面的引脚图。看红色号码对应接线,其中4、5、6号引脚不用。
这个图就很难看出线是怎么连的了,所以你要忍受我丑陋的画工。
-
接入网络
打开串口调试助手,调好参数
串口调试助手,设置参数
其中串口号你连的哪个串口就设置哪个串口号,我是连的COM3。
另一个要注意的就是波特率要115200(ESP-01的默认波特率,也可以统一改为9600)。
打开串口后,给开发板上电。你的串口调试助手会有信息出来(文本显示,不要十六进制显示)。
?諄MEM CHECK FAIL!!!
d{$弬s
Ai-Thinker Technology Co. Ltd.
invalid
显示的信息类似上面,你可以先给个测试命令AT
看看是否可以接受指令
注意!每个指令后要跟回车再发送!
如果返回OK
则说明指令可以被接收并识别。
如果下面的指令都会返回ERROR
(在没有给错指令的情况下),可以尝试AT+RST
重启模块。
1. 更改模式
指令:AT+CWMODE?
一般情况下,第一次使用会返回2
,也就是AP模式,我们不用它发热点,所以要改回Station模式(模式1)。
指令:AT+CWMODE?
一般情况下,第一次使用会返回2
,也就是AP模式,我们不用它发热点,所以要改回Station模式(模式1)。
指令:AT+CWMODE=1
返回:OK
则成功
2. 接入热点(连Wi-Fi)
指令:AT+CWJAP=<SSID>,<Password>
参数:<SSID>处填写热点名称,<Password>处填写密码。两者都要用双引号括起来。
例如:AT+CWJAP="CMCC","123456"
指令:AT+CWJAP=<SSID>,<Password>
参数:<SSID>处填写热点名称,<Password>处填写密码。两者都要用双引号括起来。
例如:AT+CWJAP="CMCC","123456"
返回:WIFI CONNECTED
:连接到热点
返回:WIFI GOT IP
:分配到IP,走到这一步,就算已经连入到热点了。
如果忘记SSID了,想看一下可以使用下面的指令,列出广播的SSID(隐藏的不会显示)。
指令:AT+CWLAP
3. 连接TCP服务器
首先打开NetAssist,设置TCP Server,然后建立连接(注意防火墙)。注意,Server必须在Client所在内网或其外网(我的是在同一个内网)。
首先打开NetAssist,设置TCP Server,然后建立连接(注意防火墙)。注意,Server必须在Client所在内网或其外网(我的是在同一个内网)。
网络调试助手
指令:AT+CIPSTART=<Type>,<DomainName>,<Port>
参数:<Type>处写TCP或UDP,<DomainName>处写域名,<Port>处写端口号。
本例:AT+CIPSTART="TCP","192.168.1.110",1234
返回:
CONNECT
OK
说明连接成功。
在NetAssist中,数据接收框的下面有一个连接对象,点开后发现除了All Connections
之外,多了一个客户,就确定客户连接到服务器了。
在下方文本框输入信息后发送,可在串口调试助手中看到ESP8266所接收到的信息
UartAssist收到的信息:
+IPD,10:Hello 简书
到这里就说明网络连接及建立TCP都可以顺利完成,在下面的单片机操作中就会变得方便很多。
注意,ESP8266每次断电后重新上电,最多只会自动连到之前连接的热点,但不会自动连接到TCP服务器,所以,建立连接要交给单片机来做。
问题
-
波特率
初值计算器
本例要使用的波特率为115200的,不是9600。引申出来的问题就是:算初值(方法详见:《51单片机实战:与计算机异步串行通信》 - 知识点 - 波特率 - 溢出率)。
手算真的很麻烦,因为在参数固定的情况下,算起来已经很烦了,更何况那些参数可能还会变得情况。
所以上神器:
-
反馈及识别
我们要做的是让单片机在收到ESP8266回馈的WIFI GOT IP
后发出连接TCP服务器的指令。
发送指令不用多说,只要记得在后面跟"换行(CR)"和"新行(NL)"字符就行(这两个是不一样的)。
1. 关键字符配对
如果是电脑程序的高级语言编程,这个问题就不存在了。但是我们给单片机写程序就要牢记它的一些点(《扯会儿单片机开发:开始》),比如硬件资源十分紧张。
一个是因为它存储空间很小,另一个是因为晶振太慢,所以我们要想办法缩减配对时间。因为单片机的使用往往很单一,所以策略都是根据情况来设定的。比如本例,单片机接收的回馈无非那么几种,所以我制定的策略就是关键字符配对。比如识别
WIFI GOT IP
,我只是别第一个字符W
和第6个字符G
,就说明我收到的就是这个回馈信息。如果收到第一个是W
,只有这条指令的第六个字符是G
,所以就可以确定。如果是出现干扰,可能性也是比较低的,我们的交互是在网络层的,下层也可以把出错的报文挡掉。总体来说还是蛮可靠的,如果你有更棒的方法,请在评论区告诉我,大家一起学习进步。
2. 利用流水灯
上面就是理论工作,已经做得7788了。但实际经验告诉我,如果的代码哪里也出岔子了在51单片机开发中真的比较难发现,他不像高级程序语言可以报错,可以try - catch
。但这个就别想了,所以我们要自己想办法,让他可以反馈我们的程序至少是正常运行的,这样可以节省很多时间。我在这里介绍的我的方法是利用LED,或成组的流水灯(更好)。承租的流水灯一般是8个,可以直接显示一个字节的信息,可能有人问为什么不用LCD?你若会LCD或者看过《51单片机实战:液晶显示器のLCD1602》就会发现,LCD本身的开发就有点复杂了。你很难保证这个子程序运行正常,更别提用它抓错了。只能是不推荐哈,我觉得不可靠。LED的话就很简单了,就是个关开,一般不会出错。
QX-MINI51开发板上自带流水灯,你要是入的其他开发板,一般都是有的。如果没有,就单买LED回来(如果不知道去哪买,链接)。
我一般会怎么做呢,先让流水灯直接显示串口接收的数据,为了知道我们至少是能接收到东西的。然后利用上面的关键字符识别,收到
WIFI CONNECTED
亮一号灯,收到WIFI GOT IP
亮二号灯,收到CONNECT OK
亮三号灯。这样,根据灯亮灭的情况就能确定哪一步出了问题,然后定位到代码或者设置。还是那句话,如果你有更棒的方法,欢迎在评论区交流。
到这里就做完了所有理论上的准备工作,也就是理论上我们已经可以实现这个程序了,下面代码实现。
代码
说下这次代码比较新颖的地方,一个是用到了.c
和.h
文件组合的模块形式,另一个是用到了函数指针(函数名也被括起来的那个)。前者这种写法是为了将各子功能模块化,.h
文件里的内容相当于面向对象编程里的public
,.c
文件一个是实现.h
内的函数,另一个就是隐藏函数和变量,相当于private
。后者是用来充当高级语言中的“事件”,让函数调用变得更加灵活,其具体用法请自己查资料或者看相关C语言书籍。
总而言之,这次的代码是模块化和事件化的,类似面向对象的编码风格。
-
准备
因为用于通讯,所以我把ASCII内所有的特殊字符都写到一个头文件里。虽然本例只用到其中的几个,但以后重复利用这个头文件。ASCII.h
#ifndef __ASCIIS__
#define __ASCIIS__
#define NUL 0x00 // NULL
#define SOH 0x01 // Start of Heading
#define STX 0x02 // Start of Text
#define ETX 0x03 // End of Text
#define EOT 0x04 // End of Transmission
#define ENQ 0x05 // Enquiry
#define ACK 0x06 // Acknowledge
#define BEL 0x07 // Bell
#define BS 0x08 // Backspace
#define HT 0x09 // Horizontal Tab
#define LF 0x0A // Line Feed
#define NL 0x0A // New Line
#define VT 0x0B // Vertical Tab
#define FF 0x0C // Form Feed
#define NP 0x0C // New Page
#define CR 0x0D // Carriage Return
#define SO 0x0E // Shift Out
#define SI 0x0F // Shift In
#define DLE 0x10 // Data Link Escape
#define DC1 0x11 // Device Control 1
#define DC2 0x12 // Device Control 2
#define DC3 0x13 // Device Control 3
#define DC4 0x14 // Device Control 4
#define NAK 0x15 // Negative Acknowledge
#define SYN 0x16 // Synchronous Idle
#define ETB 0x17 // End of Transmission Block
#define CAN 0x18 // Cancel
#define EM 0x19 // End of Medium
#define SUB 0x1A // Substitute
#define ESC 0x1B // Escape
#define FS 0x1C // File Separator
#define GS 0x1D // Group Separator
#define RS 0x1E // Record Separator
#define US 0x1F // Unit Separator
#define SP 0x20 // Space
#endif
-
LCD
这里都是关于LCD1602显示器的主要代码,与文章《51单片机实战:液晶显示器のLCD1602》所讲的一样,这里就不给出注释了。
lcd1602.h
#ifndef __LCD1602__
#define __LCD1602__
typedef bit BOOL;
void LCD_writeCmd(unsigned char cmd); //写命令
void LCD_writeData(unsigned char dat); //写数据
void LCD_writeLine(unsigned char *line); //写行数据
void LCD_init(); //初始化
void delay(unsigned int z); //粗略的延时器
#endif
lcd1602.c
#include <reg52.h>
#include "lcd1602.h"
#define LCD_CLEAR 0x01
#define LCD_Display_Mode 0X38
#define DISPLAY_OFF 0x08
#define DISPLAY_ON_NO_CURSOR 0x0c
#define DISPLAY_ON_WITH_CURSOR_NO_BLINK 0x0e
#define DISPLAY_ON_WITH_CURSOR_BLINK 0x0f
#define AUTO_BACK_STEP 0x04
#define AUTO_NEXT_STEP 0x06
#define AUTO_DISPLAY_MOVE_LEFT 0x07
#define AUTO_DISPLAY_MOVE_RIGHT 0x05
#define ALL_MOVE_LEFT 0x18
#define ALL_MOVE_RIGHT 0x1c
#define CURSOR_MOVE_LEFT 0x10
#define CURSOR_MOVE_RIGHT 0x14
#define FIRST_ROW 0x80
#define SECOND_ROW FIRST_ROW+0x40
sbit enable = P0^5;
sbit RS = P0^7;
sbit RW = P0^6;
void delay(unsigned int z)
{
unsigned int x,y;
for(x=z;x>0;x--)
for(y=220;y>0;y--);
}
void LCD_writeCmd(unsigned char cmd){
RS = 0;
P2 = cmd;
delay(5);
enable = 1;
delay(5);
enable = 0;
}
void LCD_writeData(unsigned char dat)
{
RS = 1;
P2 = dat;
delay(5);
enable = 1;
delay(5);
enable = 0;
}
void LCD_writeLine(unsigned char *line){
unsigned char i=0;
BOOL flag = 0;
LCD_writeCmd(LCD_CLEAR); //每次送来信息都清屏,可以一直刷新显示送来的信息。
LCD_writeCmd(FIRST_ROW);
while(line[i] != '\0'){
LCD_writeData(line[i++]);
if(i>15 && flag == 0){
LCD_writeCmd(SECOND_ROW);
flag = 1;
}
delay(5);
}
}
void LCD_init()
{
RW = 0;
enable = 0;
LCD_writeCmd(LCD_Display_Mode);
LCD_writeCmd(DISPLAY_ON_NO_CURSOR);
LCD_writeCmd(AUTO_NEXT_STEP);
LCD_writeCmd(LCD_CLEAR);
}
-
串口通信
这里用于单片机和ESP8266交互,与文章《51单片机实战:与计算机异步串行通信》相似。其中的不同点前面已经说过。
stc52ser.h
#ifndef __STC52_SER__
#define __STC52_SER__
extern void (*SerialPort_Event_ByteReceived)(unsigned char byte); //事件:串口接收到字节
void SerialPort_Init_Low(); //初始化为11.0592MHz下的9600波特率
void SerialPort_Init_High(); //初始化为22.1184下的115200波特率
void SerialPort_SendByte(unsigned char byte); //发送一个字节
void SerialPort_SendData(unsigned char* bytes); //发送一组字节
#endif
stc52ser.c
#include <reg52.h>
#include "ASCIIS.h"
#include "stc52ser.h"
//Byte Received Event
void (*SerialPort_Event_ByteReceived)(unsigned char byte);
//initialize registers pertinent to serial port
void SerialPort_Init_Low(){
//set and run Timer1
//mode2: 8bit, auto reload initial value
//9600bps and 11.0592MHz => 0xfd(initial value)
TMOD = 0x20;
TH1 = 0xfd;
TL1 = 0xfd;
TR1 = 1;
//set serial port configuration and enable receive
//mode1: asyc 10bit(8 data bit), alterable baud rate
SM0 = 0;
SM1 = 1;
REN = 1;
//set interruption
//enable all and serial port interruption
EA = 1;
ES = 1;
}
//initialize registers pertinent to serial port for esp8266
void SerialPort_Init_High(){
//SMOD = 1
PCON |= 0x80;
//set and run Timer1
//mode2: 8bit, auto reload initial value
//115200bps and 22.1184MHz => 0xfd(initial value)
TMOD = 0x20;
TH1 = 0xff;
TL1 = 0xff;
TR1 = 1;
//set serial port configuration and enable receive
//mode1: asyc 10bit(8 data bit), alterable baud rate
SM0 = 0;
SM1 = 1;
REN = 1;
//set interruption
//enable all and serial port interruption
EA = 1;
ES = 1;
}
//Send a byte
void SerialPort_SendByte(unsigned char byte){
ES = 0;
SBUF = byte;
while(!TI);
// transmit interrupt
TI = 0;
ES = 1;
}
//Send a data of byte sequence end by 'EOT'
void SerialPort_SendData(unsigned char* bytes){
int i = 0;
while(bytes[i] != EOT){
SerialPort_SendByte(bytes[i]);
i++;
}
}
//Occured when byte received
void receivedInterruped() interrupt 4 {
TR0 = 0;
(*SerialPort_Event_ByteReceived)(SBUF);
while(!RI);
RI = 0;
}
简单说一下,这里留了两个初始化函数,SerialPort_Init_Low()
用于11.0592MHz下的9600波特率,SerialPort_Init_High()
用于22.1184MHz下的115200波特率(此例用这个)。这样写只是为了以后可以重用(软工狗的矫情)。
-
无线
这里是最主要的代码,都是关于ESP8266的,也是作为一个模块给主函数调用。
esp8266.h
#ifndef __ESP8266__
#define __ESP8266__
extern void (*ESP01_Event_WifiConnected)(); //事件:Wi-Fi已连接
extern void (*ESP01_Event_IpGot)(); //事件:IP地址已获得
extern void (*ESP01_Event_TcpServerConnected)(); //事件:已连接到TCP服务器
extern void (*ESP01_Event_MsgReceived)(unsigned char* head); //事件:已获得消息,head为消息数组头
void ESP01_Init(); //无线模块初始化
void ESP01_ConnectToTCPServer(); //连接TCP服务器
#endif
esp8266.c
#include <reg52.h>
#include "stc52ser.h"
#include "ASCIIS.h"
#include "esp8266.h"
#define BUFFER_MAX_SIZE 99 //缓冲区大小
unsigned char buffer[BUFFER_MAX_SIZE]; //缓冲区:用于存放从ESP8266接收来的各种信息
//连接到TCP服务器的指令:AT+CIPSTART="TCP","192.168.1.110",1234。后面的CR和NL是AT指令的固定结尾,EOT用于SerialPort_SendData发送时识别结尾。
code unsigned char cmd_connectToTCPServer[] = {0x41, 0x54, 0x2B, 0x43, 0x49, 0x50, 0x53, 0x54, 0x41, 0x52, 0x54, 0x3D, 0x22, 0x54, 0x43, 0x50, 0x22, 0x2C, 0x22, 0x31, 0x39, 0x32, 0x2E, 0x31, 0x36, 0x38, 0x2E, 0x31, 0x2E, 0x31, 0x31, 0x30, 0x22, 0x2C, 0x31, 0x32, 0x33, 0x34, CR, NL, EOT};
int counter = 0; //用于ESP8266的执行步骤计数
int writeIndex = 0; //缓冲区写索引
void (*ESP01_Event_WifiConnected)();
void (*ESP01_Event_IpGot)();
void (*ESP01_Event_TcpServerConnected)();
void (*ESP01_Event_MsgReceived)(unsigned char* head);
//注意:下面代码推荐从后往前看,从注释标"1. "处开始。
void prepareForData(unsigned char byte); //因为第四步和第三步会相互调用,所以这里只是做了个声明(C语言的矫情点)。
//4. 将信息插入到缓冲区并送给单片机。
void insertDataIntoBuffer(unsigned char byte){
if(byte == '\\'){
//检测到'\'后,将信息送出到单片机
buffer[writeIndex] = '\0';
(*ESP01_Event_MsgReceived)(buffer);
SerialPort_Event_ByteReceived = &prepareForData; //回到第三步,准备接收下一条信息
writeIndex = 0;
return;
}
buffer[writeIndex++] = byte;
}
//3. 准备信息:这里是过度步骤,前面可以观察到,ESP8266在接收发来的信息时是有个头的,这里的作用就是去头。
void prepareForData(unsigned char byte){
if(byte == ':'){
SerialPort_Event_ByteReceived = &insertDataIntoBuffer;
writeIndex = 0;
}
}
//识别回馈指令:用于识别接收到的是WIFI CONNECTED(连上热点)还是WIFI IP GOT(获得IP)还是CONNECT(连上TCP服务器)
void parseCmd(){
switch(counter){
case 1:
if(buffer[0] == 'W' && buffer[5] == 'C'){
(*ESP01_Event_WifiConnected)();
counter += 1;
}
break;
case 2:
if(buffer[0] == 'W' && buffer[5] == 'G'){
(*ESP01_Event_IpGot)();
counter += 1;
}
break;
case 3:
if(buffer[0] == 'A' && buffer[3] == 'C')
counter += 1;
break;
case 4:
if(buffer[0] == 'C' && buffer[3] == 'N' && buffer[6] == 'T'){
(*ESP01_Event_TcpServerConnected)();
SerialPort_Event_ByteReceived = &prepareForData; //连接到TCP服务器后,进入第三步。
}
}
}
//2. 这里开始向缓冲区存储信息,用于识别。
void insertBuffer(unsigned char byte){
if(byte == NL){
//收到尾(NL)后,将缓冲区的回馈信息送去识别
parseCmd();
writeIndex = 0;
return;
}
buffer[writeIndex++] = byte;
}
//1. 接收头:头是无用信息,但我们要通过头里面的一些字符,推算出什么时候到达第二步(WIFI CONNECTED)
void headerReceived(unsigned char byte){
if(byte == NL){
//头内有5个NL,只要数够5个,下一个就是第二步的内容了。
if(++counter == 5){
SerialPort_Event_ByteReceived = &insertBuffer; //跳到第二步
counter = 1;
}
}
}
//同.h中的声明
void ESP01_Init(){
SerialPort_Init_High();
SerialPort_Event_ByteReceived = &headerReceived; //事件注册
}
//同.h中的声明
void ESP01_ConnectToTCPServer(){
SerialPort_SendData(cmd_connectToTCPServer);
}
- 主函数
main.c
#include <reg52.h>
#include "lcd1602.h"
#include "esp8266.h"
//连接到Wi-Fi后,亮第一个灯
void EventHandler_WifiConnected(){
P1 &= 0xFE;
}
//获得IP后,亮第二个灯
void EventHandler_IpGot(){
P1 &= 0xFD;
ESP01_ConnectToTCPServer();
}
//连接到TCP服务器后,亮第三个灯
void EventHandler_TcpServerConnected(){
P1 &= 0xFB;
}
//将ESP8266送来的信息,送去LCD显示。
void EventHandler_MsgReceived(unsigned char* head){
LCD_writeLine(head);
}
//初始化
void init(){
ESP01_Event_WifiConnected = &EventHandler_WifiConnected; //事件注册
ESP01_Event_IpGot = &EventHandler_IpGot; //事件注册
ESP01_Event_TcpServerConnected = &EventHandler_TcpServerConnected; //事件注册
ESP01_Event_MsgReceived = &EventHandler_MsgReceived; //事件注册
ESP01_Init();
LCD_init();
}
void main(){
init();
while(1);
}
-
编译
编译前要设置一下目标参数,如下图。
目标设置
注意这里要改成XDATA,不然编译通不过的。
效果
开始前请确定在同一个网络下,并且服务端已开启。
初始化效果我只是把单片机连到移动电源上了。
服务端为了能让1602第一行显示
Hello
,第二行显示World
,中间可以留了11个空格。
客户端
结语
猴!到这里这个程序就算完成了。这个比那些用手机控制开关灯要复杂一些。所以你只要掌握了这个例子,那些都不在话下了。
扯单一周目BOSS正式刷完,这个系列的文章将会暂告一段落,因为接下来笔者又要去考试了,还有什么噼里啪啦科三学车,烦。消失一小阵子后我会再次诈尸的!
恭喜你获得一周目BOSS神装:物联网神技!
网友评论
//连接到TCP服务器的指令:AT+CIPSTART="TCP","192.168.1.110",1234。。。
code unsigned char cmd_connectToTCPServer[] = {0x41, 0x54, 0x2B, 0x43, 0x49, 0x50, 0x53, 0x54, 0x41,。。。 0x524, CR, NL, EOT};
在下有一点问题,这个8266的波特率是115200,我用的是stc89c51单片机,,他的晶振能到115200吗?如果不幸的话你感觉是调8266的号还是改单片机的比较合适?
1. 可以;
2. esp8266是网络层设备,http是什么东西他肯定是不懂的。他通信是依赖tcp,主动请求连接esp8266也是可以的(作主机)。但是单片机往往都是连接在子网里,服务器暴露在外网,外网找子网一般是找不到的,倒也有一些特殊手段可以做到。所以建议你重新设计一下请求方式,最好让子网设备主动请求外网设备
Build target 'Target 1'
assembling STARTUP.A51...
compiling main.c...
compiling stc52ser.c...
compiling lcd1602.c...
compiling esp8266.c...
linking...
*** ERROR L104: MULTIPLE PUBLIC DEFINITIONS
SYMBOL: _SERIALPORT_EVENT_BYTERECEIVED
MODULE: esp8266.obj (ESP8266)
*** WARNING L13: RECURSIVE CALL TO SEGMENT
SEGMENT: ?PR?_PREPAREFORDATA?ESP8266
CALLER: ?PR?_INSERTDATAINTOBUFFER?ESP8266
*** WARNING L16: UNCALLED SEGMENT, IGNORED FOR OVERLAY PROCESS
SEGMENT: ?PR?SERIALPORT_INIT_LOW?STC52SER
Program Size: data=9.1 xdata=125 code=1197
Target not created
1,可以用11.0592Mhz+9600波特率来工作吗?为什么要用115200的波特率。
2,在店铺还送了一个CH340的USB转串口的小模块,不知道是做什么用的,只是前期的一个测试功能吗?
3,博主说的报错用Led显示是极好的。但是加printf打印一句错误信息可以吗?(这个只是个人的一个补充而已啦。。。没啥重要的)
谢谢
2. CH340就是我上面的图片(标记“供电&USB串口”,第四张)中所示的那个模块。串口(详见我的另一篇文章:《51单片机实战:与计算机异步串行通信》)就是指的单片机上的RXD和TXD,让它变成用USB的方式通信,就需要这个芯片(这个芯片已经嵌上去了)。
3. 首先呢,printf是面向标准输出设备的打印函数。这个案例里面是没有标准输出设备的(LCD1602也不是),所以不能。