【赛元95F】基于SC95F8617开发记录(三)
轻轻的我又来了,带来的是故事和酒。
来,咱们干完这杯接着说。上一节讲完了如何驱动LCD屏,现在咱们来探索一下如何驱动WIFI模块与之(MCU)通信。
直接开门见山吧,本次使用的WIFI模块是基于ESP8266的NodeMCU,引脚图对应参考如下:
拿到NodeMCU,首先需要的是刷固件,由于我这块WIFI模组上的USB转串口不能使用,所以需要单独使用CP2102连线ESP8266的GPIO0等引脚,其中GPIO0为默认高电平代表从FLASH启动,GPIO0为低电平代表进入系统升级状态进而可以经过串口升级内部固件。我这边刷的是安信可公司提供的一个集成AT指令的固件,当然也可以根据需要去刷相应的固件。烧写界面图及相应设置如下:
如果遇到升级固件失败,可以试着把波特率调低一点,不过等待的时间就需要更长啦。 成功升级固件之后呢,可以通过ESPlorer(需要安装JDK环境)编辑基于lua的脚本语言代码上传到NodeMCU,我这边建议在写代码前可以看看网上一些大神的资料,附上链接吧:https://www.jianshu.com/p/ecc6a8ec024d,里面清楚的讲述了从环境的配置到Lua语言的基础再到各个功能模块的使用,真的非常详细。以下呢,是一段init.lua设置ESP8266工作在STA模式的代码示例: -- 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的大小问题,还有一个就是外设资源很丰富,开发上手快。
本次更新到此结束,期待我们下次再见!
还请各位大神不吝赐教,我将虚心向各位学习!
夜已深,附件后面再上传吧!
生活不止眼前的苟且,还有我的代码和单片机!加油!
|