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

本文主要是介绍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;//降低单片机时钟对外界的电磁辐射(EMDSCON =  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'){//字符串的结尾是\0sendByte(*str);str++;}
}void main()
{D5=1;//配置C51串口的通信方式UartInit();while(1){								//像心跳包//Delay1000ms();//往发送缓冲区写入数据,即完成数据的发送//sendString("Hello,Jessie!\r\n");//串口中要写\r\nsendString(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; //读数据给cmdi++;if(i==SIZE)	 //使用循环缓冲区的方式存储数据{i=0;}if(strstr(cmd,"open")){D5=0;		//点亮D5i=0;memset(cmd,'\0',SIZE);}if(strstr(cmd,"close")){D5=1;		//熄灭D5i=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;//降低单片机时钟对外界的电磁辐射(EMDSCON =  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'){//字符串的结尾是\0sendByte(*str);str++;}
}void main()
{int mark=0;D5=1;//配置C51串口的通信方式UartInit();while(1){				//Delay1000ms();//往发送缓冲区写入数据,即完成数据的发送//sendString("Hello,Jessie!\r\n");//串口中要写\r\nif(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; //读数据给cmdif(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;//降低单片机时钟对外界的电磁辐射(EMDSCON =  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'){//字符串的结尾是\0sendByte(*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; 	 //读数据给tmpif(tmp=='W'||tmp=='O'||tmp=='L'||tmp=='b'){//busy p...i=0;}buffer[i++]=tmp;//入网成功的判断依据WIFI GOT IPif(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);}

这篇关于STC89C51基础及项目第8天:蓝牙、WiFi、ESP8266-01S、AT指令、透传、TCP服务器的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/416307

相关文章

服务器集群同步时间手记

1.时间服务器配置(必须root用户) (1)检查ntp是否安装 [root@node1 桌面]# rpm -qa|grep ntpntp-4.2.6p5-10.el6.centos.x86_64fontpackages-filesystem-1.41-1.1.el6.noarchntpdate-4.2.6p5-10.el6.centos.x86_64 (2)修改ntp配置文件 [r

这15个Vue指令,让你的项目开发爽到爆

1. V-Hotkey 仓库地址: github.com/Dafrok/v-ho… Demo: 戳这里 https://dafrok.github.io/v-hotkey 安装: npm install --save v-hotkey 这个指令可以给组件绑定一个或多个快捷键。你想要通过按下 Escape 键后隐藏某个组件,按住 Control 和回车键再显示它吗?小菜一碟: <template

如何用Docker运行Django项目

本章教程,介绍如何用Docker创建一个Django,并运行能够访问。 一、拉取镜像 这里我们使用python3.11版本的docker镜像 docker pull python:3.11 二、运行容器 这里我们将容器内部的8080端口,映射到宿主机的80端口上。 docker run -itd --name python311 -p

零基础学习Redis(10) -- zset类型命令使用

zset是有序集合,内部除了存储元素外,还会存储一个score,存储在zset中的元素会按照score的大小升序排列,不同元素的score可以重复,score相同的元素会按照元素的字典序排列。 1. zset常用命令 1.1 zadd  zadd key [NX | XX] [GT | LT]   [CH] [INCR] score member [score member ...]

在cscode中通过maven创建java项目

在cscode中创建java项目 可以通过博客完成maven的导入 建立maven项目 使用快捷键 Ctrl + Shift + P 建立一个 Maven 项目 1 Ctrl + Shift + P 打开输入框2 输入 "> java create"3 选择 maven4 选择 No Archetype5 输入 域名6 输入项目名称7 建立一个文件目录存放项目,文件名一般为项目名8 确定

Vue3项目开发——新闻发布管理系统(六)

文章目录 八、首页设计开发1、页面设计2、登录访问拦截实现3、用户基本信息显示①封装用户基本信息获取接口②用户基本信息存储③用户基本信息调用④用户基本信息动态渲染 4、退出功能实现①注册点击事件②添加退出功能③数据清理 5、代码下载 八、首页设计开发 登录成功后,系统就进入了首页。接下来,也就进行首页的开发了。 1、页面设计 系统页面主要分为三部分,左侧为系统的菜单栏,右侧

【Linux 从基础到进阶】Ansible自动化运维工具使用

Ansible自动化运维工具使用 Ansible 是一款开源的自动化运维工具,采用无代理架构(agentless),基于 SSH 连接进行管理,具有简单易用、灵活强大、可扩展性高等特点。它广泛用于服务器管理、应用部署、配置管理等任务。本文将介绍 Ansible 的安装、基本使用方法及一些实际运维场景中的应用,旨在帮助运维人员快速上手并熟练运用 Ansible。 1. Ansible的核心概念

Linux服务器Java启动脚本

Linux服务器Java启动脚本 1、初版2、优化版本3、常用脚本仓库 本文章介绍了如何在Linux服务器上执行Java并启动jar包, 通常我们会使用nohup直接启动,但是还是需要手动停止然后再次启动, 那如何更优雅的在服务器上启动jar包呢,让我们一起探讨一下吧。 1、初版 第一个版本是常用的做法,直接使用nohup后台启动jar包, 并将日志输出到当前文件夹n

AI基础 L9 Local Search II 局部搜索

Local Beam search 对于当前的所有k个状态,生成它们的所有可能后继状态。 检查生成的后继状态中是否有任何状态是解决方案。 如果所有后继状态都不是解决方案,则从所有后继状态中选择k个最佳状态。 当达到预设的迭代次数或满足某个终止条件时,算法停止。 — Choose k successors randomly, biased towards good ones — Close

SpringBoot项目是如何启动

启动步骤 概念 运行main方法,初始化SpringApplication 从spring.factories读取listener ApplicationContentInitializer运行run方法读取环境变量,配置信息创建SpringApplication上下文预初始化上下文,将启动类作为配置类进行读取调用 refresh 加载 IOC容器,加载所有的自动配置类,创建容器在这个过程