STC89C51基础及项目第8天:蓝牙、WiFi、ESP8266-01S、AT指令、透传、TCP服务器

1. 通过蓝牙控制LED(217.62)

2. 蓝牙概述(218.63)

  • www.hc01.com/HCbluetooth.apk
  • 蓝牙模块,蓝牙串口模块

串口透传技术

透传即透明传送,是指在数据的传输过程中,通过无线的方式这组数据不发生任何形式的改变,仿佛传输过程是透明的一样,同时保证传输的质量,原封不动地到了最终接收者手里。

  • 以太网、蓝牙、Zigbee、GPRS 等模块玩法一样,对嵌入式程序员来说,不需关心通讯模块内部数据及协议栈工作原理,只要通过串口编程获得数据即可
    在这里插入图片描述

2. AT指令修改蓝牙名字(219.64)

  1. 正确连接ttl-usb工具
  • VCC–>3.3V/5V
  • GND–>GND
  • TXD–>RXD
  • RXD–>TXD
  1. 波特率设置为9600,不要勾选发送新行
  2. 关闭手机的蓝牙以及蓝牙调试助手
  3. 发送AT+NAME=Jessie-BLE
    在这里插入图片描述

3. WiFi模块课程目标概述(220.65)

  • ESP8266 官网
    • https://www.espressif.com.cn/zh-hans/products/socs/esp8266
  • ESP8266-01S
    在这里插入图片描述

4. wifi模块的AT指令联网数据交互(221.66)

  • 蓝牙、ESP-01s、Zigbee、NB-Iot 等通信模块都是基于 AT 指令的设计
  • 接线:
    在这里插入图片描述

4.1 AT指令

  • AT 指令集 是从终端设备(Terminal Equipment,TE)或数据终端设备(Data Terminal Equipment,DTE)向终端适配器(Terminal Adapter,TA)或数据电路终端设备(Data Circuit Terminal Equipment,DCE)发送的。
  • 其对所传输的数据包大小有定义:即对于AT指令的发送,除AT两个字符外,最多可以接收1056个字符的长度(包括最后的空字符)。
  • 每个AT命令行中只能包含一条AT指令。对于由终端设备主动向 PC 端报告的 URC 指示或者 response 响应,也要求一行最多有一个,不允许上报的一行中有多条指示或者响应。AT指令以回车作为结尾,响应或上报以回车换行为结尾。

4.2 初始配置和验证

ESP-01s出厂波特率正常是115200, 注意:AT指令、控制类都要加回车(即勾选发送新行),数据传输时不加回车

  • 上电后,通过串口输出一串系统开机信息,购买的部分模块可能电压不稳,导致乱码,以 ready 为准(也可能不是 ready,本人的是 invalid)

    • AT+RST(重启,上次如有连接WiFi,此次会自动连接WiFi)
      在这里插入图片描述
  • 上电后发送 AT指令测试通信及模块功能是否正常

  • AT 在这里插入图片描述

  • 通过命令配置成9600波特率

    • AT+UART=9600,8,1,0,0
      在这里插入图片描述

4.3 入网设置(AT指令不用特意去记忆,需要时看手册即可)

  • 设置工作模式
AT+CWMODE=3 //1. 是station(设备)模式 2.是AP(路由)模式 3.是双模
OK
  • 以设备模式接入家中路由器配置
AT+CWJAP="Jessie","12345678" //指令
WIFI CONNECTED //结果
WIFI GOT IP //结果
  • 查询IP地址
AT+CIFSR //指令
+CIFSR:APIP,"192.168.4.1"//esp作为路由器的网关
+CIFSR:APMAC,"4a:3f:da:6a:a5:4b"
+CIFSR:STAIP,"192.168.2.37"
+CIFSR:STAMAC,"48:3f:da:6a:a5:4b"
OK

4.4 连接到 TCP server

  1. 开关网络助手,设立TCP服务器

    • 端口号设置为1到65535中的任意一个

    在这里插入图片描述2. 连接服务器

AT+CIPSTART="TCP","192.168.0.113",8888 //指令,注意双引号逗号都要半角(英文)输入,带回车
CONNECT //结果:成功
OK //结果:成功
  1. 发送数据
AT+CIPSEND=4 // 设置即将发送数据的长度 (这里是4个字节),带回车
>CLCA // 看到大于号后,输入消息,ABCD,不要带回车(取消勾选换新行)
Response :SEND OK //结果:成功
//这种每次发送前都要先发送AT+CIPSEND=长度 的指令,再发数据。即每发一次数据,发一次此指令

4.5 透传

  • 上一节每次发送数据都要进行字符长度设定,如果设置成透传,就有点像蓝牙模块的玩法,在4.3.2步之后
AT+CIPMODE=1 //开启透传模式,带回车
Response :OK
AT+CIPSEND //带回车
Response: > //这个时候随意发送接收数据咯
  • 退出透传模式
    • 在透传发送数据过程中,若识别到单独的⼀包数据 “+++”,则退出透传发送//退出消息互传,进入at指令模式

4.6 问题解决

  • 指令报错:双向传输,esp这边可以接收,但是发送时如遇到link is not valid:
    • 原因是连接方挂了,通信断开。重新连接TCP服务器就解决了。
      在这里插入图片描述
  • 指令报错:AT+CIPSEND ERROR,开启透传,开始发送时报错
    • 重启,重新连接TCP服务器
      在这里插入图片描述

5. 单片机发送AT指令实现联网(222.67)

  1. 先用串口助手验证单片机中的AT指令代码是否写的正确:(直接单片机连电脑)
  • 可对比英文符号、AT字符大小写格式等
    在这里插入图片描述
  1. 确认无误后,“白盒测试”,看电脑中串口助手的接收窗结果:
  • 单片机串口中的TX-esp8266的RX,8266的TX-电脑的RX
    在这里插入图片描述
    1. 代码(15./wifi模块01_AT指令)
#include "reg52.h"
#include "intrins.h"//<.h>会首先在标准库路径下查找头文件,
#include <string.h>										//而".h"会首先在当前工作目录或指定的包含路径下查找。

#define SIZE 12
sfr AUXR = 0x8E;
sbit D5  = P3^7;
char cmd[SIZE];

char reset[] 				= "AT+RST\r\n";//重启
code char connIn[]  = "AT+CWJAP=\"Jessie\",\"88888888\"\r\n";/* 转义:在每个"之前加\ 连接WiFi网络*/
code char connSer[] = "AT+CIPSTART=\"TCP\",\"192.168.2.9\",8888\r\n";//记得发送新行	连接TCP服务器
//overflow溢出时,用关键字code解决
char tcMode[]  		  = "AT+CIPMODE=1\r\n";//打开透传模式
char dataTrans[]	  = "AT+CIPSEND\r\n";	 //开始数据传送

void UartInit() 
{
	AUXR =  0x01;//降低单片机时钟对外界的电磁辐射(EMD
	
	SCON =  0x50;//选择串口工作方式1,REN使能接收//PCON默认的复位就是0,所以可以不用写
	
	TMOD &= 0x0F;//高八位清0,第八位不变
	TMOD |= 0x20;//高八位变为0010,第八位不变//定时器1工作在8位自动重装载
	
	TH1   = 0xFD;
	TL1   = 0xFD;//9600波特率根据公式 算出的定时器1的初始值
	
	TR1   = 1;//定时器1开始工作
	
	EA    = 1;//开启总中断
	ES		= 1;//开启串口中断
}

void Delay1000ms()		//@11.0592MHz
{
	unsigned char i, j, k;

	_nop_();
	i = 8;
	j = 1;
	k = 243;
	do
	{
		do
		{
			while (--k);
		} while (--j);
	} while (--i);
}

void sendByte(char data_msg)
{
	SBUF=data_msg;   //移位寄存器会耗时
	//Delay10ms();   //不准确的延时
	//while(TI == 0);//智能延时
	while(!TI);      //等待串口发送完成
	TI=0;
}
void sendString(char* str)//直接指向字符串的内存地址
{
	while(*str != '\0'){//字符串的结尾是\0
		sendByte(*str);
		str++;
	}
}

void main()
{
	D5=1;
	
	//配置C51串口的通信方式
	UartInit();
	while(1)
	{								//像心跳包
		//Delay1000ms();
		//往发送缓冲区写入数据,即完成数据的发送
		//sendString("Hello,Jessie!\r\n");//串口中要写\r\n
		
		sendString(reset);    //重启
		Delay1000ms();
		Delay1000ms();
		Delay1000ms();
		Delay1000ms();
		Delay1000ms();
		sendString(connIn);   //连接路由器网络
		Delay1000ms();
		Delay1000ms();
		Delay1000ms();
		Delay1000ms();
		Delay1000ms();
		sendString(connSer);  //连接TCP服务器
		Delay1000ms();
		Delay1000ms();
		Delay1000ms();
		Delay1000ms();
		Delay1000ms();
		sendString(tcMode);   //打开透传模式
		Delay1000ms();
		Delay1000ms();
		Delay1000ms();
		Delay1000ms();
		Delay1000ms();
		sendString(dataTrans);//开始数据传送
		Delay1000ms();
		Delay1000ms();
		Delay1000ms();
		Delay1000ms();
		Delay1000ms();
	}
}

void UartHepler() interrupt 4//中断有点像多任务线程
{
	static int i=0;  //静态变量,只被初始化一次
	
	if(RI)				   //中断处理函数中,对于接收中断的响应
	{
			RI=0;        //须软件复位,清除接收中断标志位
			cmd[i]=SBUF; //读数据给cmd
			i++;
			if(i==SIZE)	 //使用循环缓冲区的方式存储数据
			{
				i=0;
			}
			if(strstr(cmd,"open")){
				D5=0;		//点亮D5
				i=0;
				memset(cmd,'\0',SIZE);
			}
			if(strstr(cmd,"close")){
				D5=1;		//熄灭D5
				i=0;
				memset(cmd,'\0',SIZE);
			}
	}
	
	if(TI);
}

6. 通过网络TCP通信控制LED(223.68)

  • 代码(15./wifi模块02_通过TCP通信点灯)
#include "reg52.h"
#include "intrins.h"//<.h>会首先在标准库路径下查找头文件,
#include <string.h>										//而".h"会首先在当前工作目录或指定的包含路径下查找。

#define SIZE 12
sfr AUXR = 0x8E;
sbit D5  = P3^7;
char cmd[SIZE];

char reset[] 				= "AT+RST\r\n";//重启
code char connIn[]  = "AT+CWJAP=\"Jessie\",\"88888888\"\r\n";/* 转义:在每个"之前加\ 连接WiFi网络*/
code char connSer[] = "AT+CIPSTART=\"TCP\",\"192.168.2.9\",8888\r\n";//记得发送新行	连接TCP服务器
//overflow溢出时,用关键字code解决
char tcMode[]  		  = "AT+CIPMODE=1\r\n";//打开透传模式
char dataTrans[]	  = "AT+CIPSEND\r\n";	 //开始数据传送

void UartInit() 
{
	AUXR =  0x01;//降低单片机时钟对外界的电磁辐射(EMD
	
	SCON =  0x50;//选择串口工作方式1,REN使能接收//PCON默认的复位就是0,所以可以不用写
	
	TMOD &= 0x0F;//高八位清0,第八位不变
	TMOD |= 0x20;//高八位变为0010,第八位不变//定时器1工作在8位自动重装载
	
	TH1   = 0xFD;
	TL1   = 0xFD;//9600波特率根据公式 算出的定时器1的初始值
	
	TR1   = 1;//定时器1开始工作
	
	EA    = 1;//开启总中断
	ES		= 1;//开启串口中断
}

void Delay1000ms()		//@11.0592MHz
{
	unsigned char i, j, k;

	_nop_();
	i = 8;
	j = 1;
	k = 243;
	do
	{
		do
		{
			while (--k);
		} while (--j);
	} while (--i);
}

void sendByte(char data_msg)
{
	SBUF=data_msg;   //移位寄存器会耗时
	//Delay10ms();   //不准确的延时
	//while(TI == 0);//智能延时
	while(!TI);      //等待串口发送完成
	TI=0;
}
void sendString(char* str)//直接指向字符串的内存地址
{
	while(*str != '\0'){//字符串的结尾是\0
		sendByte(*str);
		str++;
	}
}

void main()
{
	int mark=0;
	D5=1;
	
	//配置C51串口的通信方式
	UartInit();
	
	while(1)
	{				
		//Delay1000ms();
		//往发送缓冲区写入数据,即完成数据的发送
		//sendString("Hello,Jessie!\r\n");//串口中要写\r\n
		
		if(mark==0){
			sendString(reset);    //重启
			Delay1000ms();
			Delay1000ms();
			Delay1000ms();
			Delay1000ms();
			Delay1000ms();
			sendString(connIn);   //连接路由器网络
			Delay1000ms();
			Delay1000ms();
			Delay1000ms();
			Delay1000ms();
			Delay1000ms();
			sendString(connSer);  //连接TCP服务器
			Delay1000ms();
			Delay1000ms();
			Delay1000ms();
			Delay1000ms();
			Delay1000ms();
			sendString(tcMode);   //打开透传模式
			Delay1000ms();
			Delay1000ms();
			Delay1000ms();
			Delay1000ms();
			Delay1000ms();
			sendString(dataTrans);//开始数据传送
			Delay1000ms();
			Delay1000ms();
			Delay1000ms();
			Delay1000ms();
			Delay1000ms();
			mark=1;
		}else{
			sendString("Hello,Jessie!\r\n");
			Delay1000ms();
		}
	}
}

void UartHepler() interrupt 4//中断有点像多任务线程
{
	if(RI)				   //中断处理函数中,对于接收中断的响应
	{
			RI=0;        //须软件复位,清除接收中断标志位
			cmd[0]=SBUF; //读数据给cmd
			
			if(cmd[0]=='1'){
				D5=0;		//点亮D5
			}
			if(cmd[0]=='0'){
				D5=1;		//熄灭D5
			}
	}
	if(TI);
}

7. 白盒方式看到连接不上的原因,调试手段(224.69)

  • 此时8266串口助手和网络调试助手都只能接收,因为8266的tx没有接到上官一号
    在这里插入图片描述
    在这里插入图片描述

8. 优化8266使用,监测AT执行结果(225.70)

  • 模拟AT指令给上官一号
    在这里插入图片描述
  • 代码(15./wifi模块03_优化连接过程)
#include "reg52.h"
#include "intrins.h"//<.h>会首先在标准库路径下查找头文件,
#include <string.h>										//而".h"会首先在当前工作目录或指定的包含路径下查找。

#define SIZE 12
sfr AUXR = 0x8E;
sbit D5  = P3^7;
sbit D6  = P3^6;

char buffer[SIZE];

code char connIn[]  = "AT+CWJAP=\"Jessie\",\"88888888\"\r\n";/* 转义:在每个"之前加\ 连接WiFi网络*/
code char connSer[] = "AT+CIPSTART=\"TCP\",\"192.168.2.9\",8888\r\n";//记得发送新行	连接TCP服务器
char tcMode[]  		  = "AT+CIPMODE=1\r\n";//打开透传模式
char dataTrans[]	  = "AT+CIPSEND\r\n";	 //开始数据传送
char reset[] = "AT+RST\r\n";	//重启模块指令

char AT_ConnIn_flag = 0;		//WIFI GOT IP返回值的标志位
char AT_OK_flag		= 0;		//OK返回值的标志位

void UartInit() 
{
	AUXR =  0x01;//降低单片机时钟对外界的电磁辐射(EMD
	
	SCON =  0x50;//选择串口工作方式1,REN使能接收//PCON默认的复位就是0,所以可以不用写
	
	TMOD &= 0x0F;//高八位清0,第八位不变
	TMOD |= 0x20;//高八位变为0010,第八位不变//定时器1工作在8位自动重装载
	
	TH1   = 0xFD;
	TL1   = 0xFD;//9600波特率根据公式 算出的定时器1的初始值
	
	TR1   = 1;//定时器1开始工作
	
	EA    = 1;//开启总中断
	ES		= 1;//开启串口中断
}

void Delay1000ms()		//@11.0592MHz
{
	unsigned char i, j, k;

	_nop_();
	i = 8;
	j = 1;
	k = 243;
	do
	{
		do
		{
			while (--k);
		} while (--j);
	} while (--i);
}

void sendByte(char data_msg)
{
	SBUF=data_msg;   //移位寄存器会耗时
	while(!TI);      //等待串口发送完成
	TI=0;
}
void sendString(char* str)//直接指向字符串的内存地址
{
	while(*str != '\0'){//字符串的结尾是\0
		sendByte(*str);
		str++;
	}
}

void main()
{
	D5=1;
	D6=1;
	
	//配置C51串口的通信方式
	UartInit();
	Delay1000ms();		  //给espwifi模块上电时间
	
	sendString(connIn);   //发送联网AT指令并等待成功
	while(!AT_ConnIn_flag);//若出现卡在“WIFI GOT IP OK”,注释此行
	while(!AT_OK_flag);	
	//AT_ConnIn_flag=1;//若卡在联网标志位,手动开启D5灯
	AT_OK_flag=0;
	
	sendString(connSer);  //发送连服务器AT指令并等待成功
	while(!AT_OK_flag);	
	AT_OK_flag=0;
	
	sendString(tcMode);   //发送透传模式AT指令并等待成功
	while(!AT_OK_flag);	
	AT_OK_flag=0;
	
	sendString(dataTrans);//发送数据传输AT指令并等待成功
	while(!AT_OK_flag);	

	if(AT_ConnIn_flag){
		D5=0;			  //点亮D5,代表入网成功
	}
	if(AT_OK_flag){
		D6=0;    		  //点亮D6,代表连接服务器并打开透传模式成功
	}
	
	while(1){				
		Delay1000ms();//心跳包
		sendString("Hello,Jessie!\r\n");
	}
}

void UartHepler() interrupt 4//中断有点像多任务线程
{
	static int i = 0;
	char tmp;
	char a;
	
	if(RI){				 //中断处理函数中,对于接收中断的响应
		RI=0;        //须软件复位,清除接收中断标志位
		tmp=SBUF; 	 //读数据给tmp
		
		if(tmp=='W'||tmp=='O'||tmp=='L'||tmp=='b'){//busy p...
			i=0;
		}
		buffer[i++]=tmp;
		//入网成功的判断依据WIFI GOT IP
		if(buffer[0]=='W' && buffer[5]=='G'){
			AT_ConnIn_flag=1;
			memset(buffer,'\0',SIZE);
		}
		//连接服务器等OK返回值指令的判断
		if(buffer[0]=='O' && buffer[1]=='K'){
			AT_OK_flag=1;
			memset(buffer,'\0',SIZE);
		}
		//出现busy...p字样捕获
		if(buffer[0]== 'b' && buffer[1]=='u'){
				for(a=0;a<5;a++){
					D5 = 0;
					Delay1000ms();
					D5 = 1;
					Delay1000ms();
				}
				sendString(reset);
				memset(buffer, '\0', SIZE);
			}
		//灯控指令
		if(buffer[0]=='L' && buffer[2]=='1'){
			D5=0;
			memset(buffer,'\0',SIZE);
		}
		if(buffer[0]=='L' && buffer[2]=='0'){
			D5=1;
			memset(buffer,'\0',SIZE);
		}
		if(i==12)i=0;
	}
	if(TI);
}
  • 若左边8266不返回OK,删除“AT+RST”重启代码;
    若串口助手模拟指令发OK给单片机无反应,可重启STC软件;建议每次重新上电前,都关闭串口
    在这里插入图片描述
  • 若出现直接跳过“连接TCP服务器”情况,或tcp busy,可重启网络调试助手软件
  • 若出现卡在 “WIFI GOT IP OK”,注释掉网络连接的标志位即可

在这里插入图片描述

分析和解决bug

  • 会概率性的出现,模拟指令发送给单片机“WIFI GOT IP”,无反应
  • 分析:W、F、G 字眼,都被识别到,系统将他们都视为了 buffer[0],导致系统卡死

尝试一

  • 尝试将每种情况都单独做if
    在这里插入图片描述
  • 结果:逻辑会乱,失败
    在这里插入图片描述

尝试二

  • 用switchcase语句做每种情况
    在这里插入图片描述
    结果:直接死在WiFi GOT IP,失败

尝试三

  • 删除F的情况,仅输入“WIFI G”
    在这里插入图片描述
    在这里插入图片描述
    结果:成功
    在这里插入图片描述

9. 优化8266,捕获联网失败的状态(226.71)

  • 加入“busy p…”的情况,出现bp时,灯闪、WIFI重启
  • 代码(15./wifi模块03_优化连接过程)
//出现busy...p字样捕获
		if(buffer[0]== 'b' && buffer[1]=='u'){
				for(a=0;a<5;a++){
					D5 = 0;
					Delay1000ms();
					D5 = 1;
					Delay1000ms();
				}
				sendString(reset);
				memset(buffer, '\0', SIZE);
			}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值