拿到NodeMCU,首先需要的是刷固件,由于我这块WIFI模组上的USB转串口不能使用,所以需要单独使用CP2102连线ESP8266的GPIO0等引脚,其中GPIO0为默认高电平代表从FLASH启动,GPIO0为低电平代表进入系统升级状态进而可以经过串口升级内部固件。我这边刷的是安信可公司提供的一个集成AT指令的固件,当然也可以根据需要去刷相应的固件。烧写界面图及相应设置如下:
- -- init.lua
- print('Setting up WIFI...')
- wifi.setmode(wifi.STATION)
- wifi.sta.config('A+B', '123456')
- wifi.sta.connect()
- tmr.alarm(1, 1000, tmr.ALARM_AUTO, function()
- if wifi.sta.getip() == nil then
- print('Waiting for IP ...')
- else
- print('IP is ' .. wifi.sta.getip())
- tmr.stop(1)
- end
- end)
由于一些原因,咱们这次使用AT指令的方式去操作ESP8266。(需先刷写带AT指令的固件)
AT指令呢,其实就是终端设备与PC应用之间的连接与通信的指令,英文全称“Attention”。每条AT命令行中只能包含一条AT指令,对于发送而言,除了AT两个字符外,最多可以接收1056个字符的长度(包括了空字符)。AT指令示例:
其他更多AT指令可参考AT指令使用说明文档。
本次使用的是SC95F8617的UART与ESP8266通信(MCU的TX接8266的RX,MCU的RX接8266的TX)完成AT指令的收发,通信的波特率是115200bps。调试工具可以选择任何一个串口助手收发AT指令,我推荐选用安信可串口调试助手,因为上面包含了经常使用的AT指令,使用起来快捷方便。其实ESPlorer里面也可以调试呢。我这边为了调试方便,引了一根线从MCU的RX接入CP2102的RXD,然后使用仿真的方式调试。
先说一下模块的工作模式AP Station AP+Station
下面讲一下在不同模式下使用不同协议的常用AT指令配置:
(1) AP 模式下作为 TCP server
AT+CWMODE=2 // 开启 AP 模式
AT+CWSAP="ESP8266","0123456789",11,0 //设置模块的 wifi 和密码
AT+CIPSERVER=1,8080 //设置模块服务器端口,即建立了WIFI热点,默认的 ip 为 192.168.4.1,端口为自己设定的 8080(默认为 333)
AT+CIPSEND=0,X //进入数据发送模式为X个字节,X值自己设定
> //进入发送模式
(2)STA 模式下作为 TCP server(
本次我使用的模式
)
AT+CWMODE=1 //设置模组为STA模式。
AT+CWLAP //查询附近 WIFI
AT+CWJAP="SSID","Password" //连接 WIFI,SSID和Password需替换
AT+CIFSR //查看路由器分配给模组的IP地址,例如 192.168.1.101
AT+CIPMUX=1 //打开多连接
AT+CIPSERVER=1,8080 //设置模块服务器端口
AT+CIPSEND=0,16 //进入数据发送模式为16个字节
(3)STA模式下 TCP Client 透传模式
AT+CWMODE=1 //设置模组为STA模式
AT+CWLAP //查询附近 WIFI
AT+CWJAP="SSID","Password" //连接路由器的 WIFI
AT+CIFSR //查看路由器分配给模组的IP地址,例如 192.168.1.101
AT+CIPMUX=0 //设置单连接
AT+CIPMODE=1 //设置透传模式
AT+CIPSTART="TCP","192.168.1.108",8080 //连接手机端建立的TCP服务器
AT+CIPSEND //开始发送数据
> // 进入发送模式
Hello // 发送数据
+++ // 注意退出透传,直接发送。取消发送新行
值得注意的是:透传只能在单连接模式下进行,所以在建立连接之前一定要用(AT+CIPMUX=0设置单连接),但是模块处于服务器模式下时,必须要多链接,由于冲突,所以模块开启服务器模式不能做 TCP透传。
(4)UDP 多连接模式
AT+CWMODE= 1 //设置 STA模式
AT+CWLAP // 查询附近 WIFI
AT+CWJAP="SSID","Password" // 连接 WIFI
AT+CIFSR // 查看模块当前的 IP
AT+CIPMUX=1 // 打开模块多连接
AT+CIPSTART=0,"UDP","255.255.255.255",50000,1000, 0 //是建立 UDP 连接,其中手机 UDP server 设置 50000,UDP client 设置的端口 1000 AT+CIPSEND=0,11 // 模块发送数据模式为 9 个字节
(5) 模块通过数据外网透传
AT+CWMODE=3 //设置 AP 和 STA 共存模式
AT+CWLAP //查询附近 WIFI
AT+CWJAP="CMCC","12345678" //连接 wifi
AT+CIPMUX=0 // 设置单连接
AT+CIPMODE=1 //设置透传模式
AT+CIPSTART="TCP","115.29.109.104",6602 //连接外网服务器
AT+CIPSEND //发送数据
>
(6)当然了也可以使用智能配网,通过以下两种方式进行配网:乐鑫ESP-Touch APP和微信Airkiss。更多AT指令可以通过附件查看哦。
//******************这是一条分界线************************//
接下来该讲一下我的实现方法啦:1、UART的配置我是参考了Demo程序,波特率设为115200bps,记得TXD和RXD要设置
成输入带上拉模式,这个我入坑了,后面才发现的。
UART的发送不使能中断,在中断里接收AT指令的返回结果,由于MCU-UART接收的数据长度不固定,所以我这边设置了最长接收100个字节的Buffer,超过了就不存入,还有,由于很多返回结果只有OK或其他指令,我这边也设置了假设接收到了“OK”字符就标记为接收完成。整个的一个UART中断服务函数和字节发送、字符串发送函数如下:
- bit comm_flag1=0,comm_flag2=0;
- u8 first_flag=1;
- void UART_Interrupt(void) interrupt 4 using 1
- {
- u8 res=0;
- if(RI)
- {
- RI=0;
- res = SBUF;
- if((UART_RX_STATUS&(1<<15))==0)//接收完的一批数据,还没有被处理,则不再接收其他数据
- {
- if(UART_RX_STATUS < 100)//还可以接收数据
- {
- if((con_state==1))
- { if(res == ':') first_flag=0;
- if(first_flag==0)
- {
- if(res == 'T')
- {
- u16 Get_Current_Temp=0;
- Get_Current_Temp=DS18B20_GetTemp();
- Get_Current_Temp=Get_Current_Temp*0.0625;
- con_state=0;
- ESP8266_SendCmd("AT+CIPSEND=0,16","OK",20);
- {
- //u8 *p;
- u16 Get_Current_Temp=0;
- //p=malloc(18);
- Get_Current_Temp=DS18B20_GetTemp();
- Get_Current_Temp=Get_Current_Temp*0.0625;
- //sprintf((char*)p,"Current_Temp:%2d C\r\n",Get_Current_Temp);
- ESP8266_SendCmd("Today is Moday\r\n","OK",20);
- //free(p);
- }
- }
- }
- }
- else
- {
- UART_RX_BUFFER[UART_RX_STATUS++]=res;//记录接收到的值
- if(res == 'O') //判断是否回传"OK"
- {
- comm_flag1=1;
- }
- if(res == 'K') //判断是否回传"OK"
- {
- comm_flag2=1;
- }
- if(comm_flag1&comm_flag2)
- {
- comm_flag1=comm_flag2=0;
- UART_RX_STATUS |= 1<<15; //强制标记接收完成
- }
- }
- }
- else
- {
- UART_RX_STATUS |= 1<<15; //强制标记接收完成
- }
- }
- }
- if(TI)
- {
- TI=0;
- }
-
- }
- //********************************************
- // 函数名 :void SendData(u8 DATA)
- // 功能 :Uart发送一个Byte
- //********************************************
- void UART0_SendByte(u8 dat)
- {
- EUART = 0;
- TI = 0;
- SBUF = dat;
- while(!TI);
- TI = 0;
- EUART = 1;
- }
- void UART_SendString(u8* buf)
- {
- while(*buf!='\0')
- {
- UART0_SendByte(*buf);
- buf++;
- }
- }
申明:由于C51编译器里面不知道是不是不能通过sprintf来将数字转化为字符,所以远程接收温度值暂时有问题,不过接收字符串等文本形式还是可以的。后期呢,我会想下解决的方法,其实我最喜欢用sprintf了,嘿嘿!
2、接下来就是进入对ESP8266的一些操作啦,我这边先是使用while等待通过AT指令对其复位和关闭回显操作,关于回显是啥咱们下期讲,复位之后需延时几秒。代码如下
其中呢,这个ESP8266_SendCmd既可以发送Cmd也可以发送dat用,里面的UART_RX_STATUS(u16)用来作为接收的判断,最高位用来作为接收数据的标记(1:有数据,0:无有效数据),其他位作为接收数据的长度,函数方法如下:
- //ESP8266发送命令后,检测接收到的应答
- //str:期待的应答结果
- //返回值:0,没有得到期待的应答结果
- // 其他,期待应答结果的位置(str的位置)
- u8* ESP8266_CheckCmd(u8 *str)
- {
- char *strx=0;
- if(UART_RX_STATUS&0X8000) //接收到一次数据了
- {
- UART_RX_BUFFER[UART_RX_STATUS&0X7FFF]=0;//添加结束符
- strx=strstr((const char*)UART_RX_BUFFER,(const char*)str);
- }
- return (u8*)strx;
- }
- //向ESP8266发送命令
- //cmd:发送的命令字符串
- //ack:期待的应答结果,如果为空,则表示不需要等待应答
- //waittime:等待时间(单位:10ms)
- //返回值:0,发送成功(得到了期待的应答结果)
- // 1,发送失败
- u8 ESP8266_SendCmd(u8 *cmd,u8 *ack,u16 wait_time)
- {
- u8 res = 0;
- UART_RX_STATUS = 0;
- UART_SendString(cmd); //向WIFI模块发送控制指令
- Delay_us(5);
- UART0_SendByte('\r'); //回车
- Delay_us(5);
- UART0_SendByte('\n'); //换行
- /////////////////////////////////////////
- if(ack&&wait_time) //需要等待应答
- {
- while(--wait_time) //等待倒计时
- {
- Delay_ms(10);
- if(UART_RX_STATUS&0X8000)//接收到期待的应答结果
- {
- if(ESP8266_CheckCmd(ack))
- {
- // printf("ack:%s\r\n",(u8*)ack);
- break;//得到有效数据
- }
- UART_RX_STATUS=0;
- }
- }
- if(wait_time==0) res=1;
- }
- return res;
- }
- //ESP8266退出透传模式
- //返回值:0,退出成功;
- // 1,退出失败
- u8 ESP8266_QuitTrans(void)
- {
- UART_SendString('+');
- Delay_ms(15); //大于串口组帧时间(10ms)
- UART_SendString('+');
- Delay_ms(15); //大于串口组帧时间(10ms)
- UART_SendString('+');
- Delay_ms(500); //等待500ms
- return ESP8266_SendCmd("AT","OK",20);//退出透传判断.
- }
接下来呢,就是配置网络等的操作啦,不多说先上程序吧:
- //ESP8266模块 STA 模式下作 TCP server 配置
- void ESP8266_STA_TCP_Server_Config(u16 x,u16 y,u8 wanip)
- {
- u8 *p1,*p2;
- u8 res=0;
- u8 ipbuf[16];
- p1=malloc(32); //申请32字节内存
- p2=malloc(8);
- POINT_COLOR=BLUE;
- con_state = 0;
- res|=ESP8266_SendCmd("AT+CWMODE_DEF=1","OK",20);
- res|=ESP8266_SendCmd("AT+RST","OK",20);
- Delay_ms(1000);//延时2s等待模块重启
- Delay_ms(1000);//
- Delay_ms(1000);
- Delay_ms(1000);
- res|=ESP8266_SendCmd("AT+CWJAP="A+B","12345678"","WIFI GOT IP",1000);
- res|=ESP8266_SendCmd("AT+CIPMUX=1","OK",20); //0:单连接,1:多连接
- //sprintf((char*)p,"AT+CIPSERVER=1,%s",(u8*)portnum);
- res|=ESP8266_SendCmd("AT+CIPSERVER=1,8080","OK",20); //开启Server模式,端口号为8080
- //res|=ESP8266_SendCmd("AT","OK",20);
- //ESP8266_SendCmd("AT+CIPSTART=0,"TCP","192.168.1.101",5000","OK",1000);
- res|=ESP8266_SendCmd("AT+CIPSTO=0","OK",20); //永远不超时
- ESP8266_Get_WanIP(ipbuf);
- //sprintf((char*)p,"IP地址:%s 端口:%s",ipbuf,(u8*)portnum);
- //ESP8266_SendCmd("AT+CIPMODE=1","OK",200); //传输模式为:透传
- if(res==0)//Server开启
- {
- LCD_ShowString(0,35,"IP:");
- sprintf((char*)p1,"%s",ipbuf);
- LCD_ShowString(0,50,p1);
- free(p1);
- sprintf((char*)p2,"Port:%s","8080");
- LCD_ShowString(0,65,p2);
- free(p2);
- LCD_ShowString(x,y,"TCPServer OK");
- }
- else
- {
- LCD_ShowString(0,35,"NetWork_ERR");
- }
- UART_RX_STATUS=0;
- }
我选择的是STA模式下作为TCP Server
,STA成功之后,然后ESP8266开启Server模式,端口号我这边固定设置为8080,设置完成之后,路由器会给ESP8266分配一个IP地址,然后通过查询ESP8266的本地连接可以获得STAIP地址和STAMAC,获得的IP和端口通过LCD屏显示,如开篇首图,查询IP的函数如下:
- //获取Client ip地址
- //ipbuf:ip地址输出缓存区
- void ESP8266_Get_WanIP(u8* ipbuf)
- {
- u8 *p,*p1;
- if(ESP8266_SendCmd("AT+CIFSR","OK",50))//获取WAN IP地址失败
- {
- ipbuf[0]=0;
- return;
- }
- p=ESP8266_CheckCmd(""");
- p1=(u8*)strstr((const char*)(p+1),""");
- *p1=0;
- sprintf((char*)ipbuf,"%s",p+1);
- }
成功获取IP之后呢,可以通过这个TCP Server的IP和Port进行与Client的连接了。
接下来呢,快到末尾了,就是while(1)函数,里面会不断采集温度数据(也可以加入其他采集数据)并通过LCD屏显示刷新。TCP的连接接收发送已经放在UART中断函数里了,我这边是客户端通过发送字符“T”就能获取到相应的数据。
程序的结束,只是调试的开始,由于不能使用sprintf,不得不放弃了很多;调试的路上一路披荆斩棘,翻阅了很多规格书资料。调试呢,我这边边仿真边通过串口助手查看ESP8266的回传指令,我的一个调试过程可参考如下:
最后呢,成功的实现了可以通过手机发送命令去获取信息啦。使用的是网络调试助手,手机配置为TCP Client,IP为192.168.1.108,端口为8080,成功连接后就可以通信啦,通信截图如下;
啊哦,Monday这个单词好像拼错了!忘记改了。
本次开发呢,由于第一次使用WIFI模块,操作上也存在很多不足,不过后面我会慢慢改进的;
还有想问广大网友有没有C51里数字快速转字符串的方法(不使用sprintf),有的话帮忙告知一下。
最后感谢赛元提供的开发核心板,真的很不错呢,主要是Flash容量大,不用担心Code的大小问题,还有一个就是外设资源很丰富,开发上手快。
本次更新到此结束,期待我们下次再见!
还请各位大神不吝赐教,我将虚心向各位学习!
夜已深,附件后面再上传吧!
生活不止眼前的苟且,还有我的代码和单片机!加油!