草庐IT

基于51单片机的WIFI模块的简单通信

Strange_Head 2023-08-11 原文

文章目录

ESP-01S-WIFI模块

他与蓝牙模块类似,但比蓝牙模块强大实用。
蓝牙,ESP-01s,Zigbee, NB-Iot等通信模块都是基于AT指令的设计。

ESP-01s出厂波特率正常是115200, 注意:AT指令,控制类都要加回车,数据传输时不加回车

此外我们还需要用到的一个模块。(USB转TTL)用来白盒测试,方便调试

AT指令

AT指令集是从终端设备 (单片机)(Terminal Equipment,TE)或数据终端设备(Data TerminalEquipment,DTE)向终端适配器(Terminal Adapter,TA)或数据电路终端设备(Data CircuitTerminal Equipment,DCE)发送的。

其对所传输的数据包大小有定义:即对于AT指令的发送,除AT两个字符外,最多可以接收1056个字符的长度(包括最后的空字符)。

每个AT命令行中只能包含一条AT指令;对于由终端设备主动向PC端报告的URC指示或者response响应,也要求一行最多有一个,不允许上报的一行中有多条指示或者响应。AT指令以回车作为结尾,响应或上报以回车换行为结尾。

AT指令不需要记住,用到时候查即可,所有AT指令均是英文半角输入,不然报错

用到的AT指令

均可通过手册查到

AT	//上电后发送AT指令测试通信及模块功能是否正常

OK	//返回OK则正常
AT+UART=9600,8,1,0,0	//通过一下命令配置成9600波特率,8个数据位,1个停止位,后两位默认0

OK
AT+CWMODE=3 //1. 是station(设备)模式 2.是AP(路由)模式 3.是双模

OK
AT+CWJAP="TP-LINK_3E30","18650711783" //指令

WIFI CONNECTED //结果
WIFI GOT IP //结果
AT+CIFSR //指令		查询当前连接的IP地址
+CIFSR:APIP,"192.168.4.1"				//自己作为路由器的ip
+CIFSR:APMAC,"4e:75:25:0d:ae:2f"		//mac地址
+CIFSR:STAIP,"192.168.0.148"			//自己作为客户的ip
+CIFSR:STAMAC,"4c:75:25:0d:ae:2f"		//mac地址
OK
AT+CIPSTART="TCP","192.168.0.113",8888 //指令,注意双引号逗号都要半角(英文)输入
CONNECT //结果:成功
OK //结果:成功
AT+CIPSEND=4 // 设置即将发送数据的长度 (这里是4个字节)
>CLCA // 看到大于号后,输入消息,CLCA,不要带回车
Response :SEND OK //结果:成功
//注意,这种情况下,每次发送前都要先发送AT+CIPSEND=长度 的指令,再发数据!
AT+CIPMODE=1 //开启透传模式
Response :OK
AT+CIPSEND //带回车
Response: > //这个时候随意发送接收数据咯
//在透传发送数据过程中,若识别到单独的⼀包数据 “+++”,则退出透传发送

软件下载

链接:https://pan.baidu.com/s/1mOY_ZgvDt3JvY1qD1aIvpQ
提取码:o42a
–来自百度网盘超级会员V4的分享

解压密码:Strange_Head

打开即用

如果是新WIFI设备,例如波特率9600,双模等等需要配置

先这样接线配置

打开串口调试助手和网络调试助手
WIFI一开始默认波特率是115200,这个先选115200,如果已插入USB转TTL,则开启串口处于高亮,且可以选择对应串口

网络调试助手,选择如下:

windows用命令提示符cmd,ipconfig指令可以知道自己的ip地址,对应输入即可。
端口号随便,最好是5000以上。这里查找到的后面就是自己服务器的ip地址和端口号
点击连接即可,等待WIFI接入。

在串口调试用AT指令配置(可以尝试直接通信)

  • AT+UART=9600,8,1,0,0//波特率设置9600
  • AT+CWMODE=3 //1. 是station(设备)模式 2.是AP(路由)模式 3.是双模

如果想直接通信继续操作:

  • AT+CWJAP="自己的WIFI名字","自己的WIFI密码" //指令
  • AT+CIPSTART="TCP","服务器IP地址",服务器端口号 //连接服务器,指令,注意双引号逗号都要半角(英文)输入
  • AT+CIPSEND=4 // 设置即将发送数据的长度 (这里是4个字节)
  • 发送数据,发送完毕之后需要重新执行AT+CIPSEND=4,循环下去,如果不想循环可以执行透传命令
  • AT+CIPMODE=1 //开启透传模式
  • AT+CIPSEND //带回车

细心可以发现,每次成功执行返回OK,那么可以利用这个OK,实现单片机与WIFI模块通信

与单片机通信接线

如何接线


只用到4根线,VCC,GND,TX,RX

  • VCC电源,5v,3.3v均可
  • GND电源负极(地)
  • TX发出消息端
  • RX接受消息端

这样接线的好处是方便调试,可以直观的看到WIFI模块收到的消息并反馈到网络调试助手,而WIFI模块发出的消息可以通过串口调试助手看到同样的单片机也有一根下载线连着电脑,也可以监测单片机自身的首发状态
但需要通过串口自己模拟WIFI消息给单片机发信号

不需要信息展示时候,直接把WIFI模块的TX接到单片机的RX即可。

代码(当作客户端接入自己家路由器,单片机通过路由器与电脑通信)

#include <reg51.h>
#include <string.h>

sbit led = P3^7;

//根据自己的需求自己配置
code char Set_Connete[]  = "AT+CWJAP=\"自己的路由器名字\",\"自己的路由器密码\"\r\n"; //连接路由器的AT指令
code char Set_netServer[]= "AT+CIPSTART=\"TCP(类型)\",\"自己的服务器IP\",自己的服务器端口号\r\n";	//连接服务器

char Set_Send0[]	= "AT+CIPMODE=1\r\n";		//设置透传
char Set_Send1[]	= "AT+CIPSEND\r\n";			//开始发送

char QUIT[]			= "+++";					//关闭发送,目前没用到

char cmd[12];

char ok = 0;
char AT_NET = 0;

static int i = 0;
	
void delay(unsigned int ms)
{
	unsigned int i;
	unsigned int j;
	for(i=0;i<ms;i++)
	for(j=0;j<115;j++);
}

void Init_Time0()
{
	SCON = 0x50;		//设置串口工作模式1,且可以接受消息
	PCON = 0x80;		//开启倍频
	TMOD = 0x20;		//设置定时器8位重新装填
	TH1 = 0xFA;			//9600波特率计算
	TL1 = 0xFA;
	TR1 = 1;			//启动定时器0
	EA = 1;				//开启总中断开关
	ES = 1;				//开启串口中断
	
}

//中断处理
void Operate_Serial() interrupt 4
{
	//发送处理
	if(TI);
	//接受处理
	if(RI){
		//将RI复位
		RI = 0;
		//如果SBUF是以下这几个字符则从零开始
		if(SBUF == 'O'  || SBUF == 'L' || SBUF == 'M'){
			i = 0;
		}	
		cmd[i++] = SBUF;	//以此放入值
		if(cmd[0] == 'O' && cmd[1] == 'K'){		//根据例子每次执行成功会返回OK,对OK进行检测
			ok = 1;
			memset(cmd,'\0',12);
		}
		if(cmd[0] == 'L'){			//亮灯
			
		}
		if(cmd[0] == 'M'){			//灭灯
			led = 1;
		}
		
		if(i == 12) i = 0;			//防止溢出归零
	}
}

//字节发送
void Send_Byte(char dat)		
{
		SBUF = dat;
		while(!TI);
		TI = 0;
}

//字符串发送
void SendString(char *dat)
{
	while(*dat != '\0'){
		Send_Byte(*dat);
		dat++;
	}

}

//入网操作
void Connet_Net()
{
	char count = 0;
	delay(2000);
	SendString(Set_Connete);
	//等待入网,灯的状态为闪烁
	while(!ok){
			count++;
			led = 0;
			delay(500);
			led = 1;
			delay(500);
			if(count >= 10){
				SendString(Set_Connete);
				count = 0;
			}
	}
	//每次过后ok的值为1,所以要进行复位
	ok = 0;
	//连接服务器
	SendString(Set_netServer);
	while(!ok);
	ok = 0;
	//开启透传
	SendString(Set_Send0);
	while(!ok);
	ok = 0;
	//开始发送
	SendString(Set_Send1);
	while(!ok);
	ok = 0;
	led = 0;
}


void main()
{
	//初始化,设置波特率9600
	Init_Time0();
	//入网
	Connet_Net();
	
	while(1){
		//每秒发送Strange_Head hello!!!,心跳包,在网络调试助手可以看到
		SendString("Strange_Head hello!!!\r\n");
		delay(1000);
	}
}

执行结果

通过单片机发送AT指令实现通信

WIFI当作服务器

自己电脑连接WIFI模块,并用网络调试助手,连接对应IP,端口
需要设置参数

//1 配置成服务器
AT+CWMODE=2
Response :OK
//2 使能多链接
AT+CIPMUX=1
Response :OK
//3 建立TCPServer
AT+CIPSERVER=1 // default port = 333
Response :OK
//4 发送数据
AT+CIPSEND=0,4 // 发送4个字节在连接0通道上
abcd //输入数据,不带回车
Response :SEND OK
//• 接收数据
+IPD, 0, n: xxxxxxxxxx //+IPD是固定字符串 0是通道,n是数据长度,xxx是数据
//断开连接
AT+CIPCLOSE=0
Response :0, CLOSED OK

代码

//ESP-01s工作在路由模式,课程查询路由器IP地址192.168.4.1,使用的服务器默认端口号333
#include <reg51.h>
#include <string.h>

sbit led = P3^7;

//根据自己的需求自己配置
//1 工作在路由模式
char LYMO[] = "AT+CWMODE=2\r\n";
//2 使能多链接
char DLJ[] = "AT+CIPMUX=1\r\n";
//3 建立TCPServer
char JLFW[] = "AT+CIPSERVER=1\r\n"; // default port = 333
//发送数据
char FSSJ[] = "AT+CIPSEND=0,20\r\n";

char cmd[12];

char ok = 0;
char AT_NET = 0;

static int i = 0;
	
void delay(unsigned int ms)
{
	unsigned int i;
	unsigned int j;
	for(i=0;i<ms;i++)
	for(j=0;j<115;j++);
}

void Init_Time0()
{
	SCON = 0x50;		//设置串口工作模式1,且可以接受消息
	PCON = 0x80;		//开启倍频
	TMOD = 0x20;		//设置定时器8位重新装填
	TH1 = 0xFA;			//9600波特率计算
	TL1 = 0xFA;
	TR1 = 1;			//启动定时器0
	EA = 1;				//开启总中断开关
	ES = 1;				//开启串口中断
	
}

//中断处理
void Operate_Serial() interrupt 4
{
	//发送处理
	if(TI);
	//接受处理
	if(RI){
		//将RI复位
		RI = 0;
		//如果SBUF是以下这几个字符则从零开始
		if(SBUF == 'O'  || SBUF == 'L' || SBUF == 'M'){
			i = 0;
		}	
		cmd[i++] = SBUF;	//以此放入值
		if(cmd[0] == 'O' && cmd[1] == 'K'){		//根据例子每次执行成功会返回OK,对OK进行检测
			ok = 1;
			memset(cmd,'\0',12);
		}
		if(cmd[0] == 'L'){			//亮灯
			
		}
		if(cmd[0] == 'M'){			//灭灯
			led = 1;
		}
		
		if(i == 12) i = 0;			//防止溢出归零
	}
}

//字节发送
void Send_Byte(char dat)		
{
		SBUF = dat;
		while(!TI);
		TI = 0;
}

//字符串发送
void SendString(char *dat)
{
	while(*dat != '\0'){
		Send_Byte(*dat);
		dat++;
	}

}

void Connet_Net()
{
	char count = 0;
	delay(2000);
	sendString(LYMO);
	while(!OK);
	OK = 0;
	sendString(DLJ);
	while(!OK);
	OK = 0;
	sendString(JLFW);
	while(!OK);
	OK = 0;
}


void main()
{
	//初始化,设置波特率9600
	Init_Time0();
	//入网
	Connet_Net();
	
	while(1){
		sendString(FSSJ);
		delay(1000);
		//每秒发送Strange_Head hello!!!,心跳包,在网络调试助手可以看到
		SendString("Strange_Head hello!!!\r\n");
		delay(1000);
	}
}

结束

如果对你有帮助,点赞支持一下把。
转载联系作者

有关基于51单片机的WIFI模块的简单通信的更多相关文章

  1. ruby - 在 Ruby 中使用匿名模块 - 2

    假设我做了一个模块如下:m=Module.newdoclassCendend三个问题:除了对m的引用之外,还有什么方法可以访问C和m中的其他内容?我可以在创建匿名模块后为其命名吗(就像我输入“module...”一样)?如何在使用完匿名模块后将其删除,使其定义的常量不再存在? 最佳答案 三个答案:是的,使用ObjectSpace.此代码使c引用你的类(class)C不引用m:c=nilObjectSpace.each_object{|obj|c=objif(Class===objandobj.name=~/::C$/)}当然这取决于

  2. ruby-on-rails - Ruby net/ldap 模块中的内存泄漏 - 2

    作为我的Rails应用程序的一部分,我编写了一个小导入程序,它从我们的LDAP系统中吸取数据并将其塞入一个用户表中。不幸的是,与LDAP相关的代码在遍历我们的32K用户时泄漏了大量内存,我一直无法弄清楚如何解决这个问题。这个问题似乎在某种程度上与LDAP库有关,因为当我删除对LDAP内容的调用时,内存使用情况会很好地稳定下来。此外,不断增加的对象是Net::BER::BerIdentifiedString和Net::BER::BerIdentifiedArray,它们都是LDAP库的一部分。当我运行导入时,内存使用量最终达到超过1GB的峰值。如果问题存在,我需要找到一些方法来更正我的代

  3. ruby-on-rails - Rails 应用程序之间的通信 - 2

    我构建了两个需要相互通信和发送文件的Rails应用程序。例如,一个Rails应用程序会发送请求以查看其他应用程序数据库中的表。然后另一个应用程序将呈现该表的json并将其发回。我还希望一个应用程序将存储在其公共(public)目录中的文本文件发送到另一个应用程序的公共(public)目录。我从来没有做过这样的事情,所以我什至不知道从哪里开始。任何帮助,将不胜感激。谢谢! 最佳答案 无论Rails是什么,几乎所有Web应用程序都有您的要求,大多数现代Web应用程序都需要相互通信。但是有一个小小的理解需要你坚持下去,网站不应直接访问彼此

  4. ruby-on-rails - 在混合/模块中覆盖模型的属性访问器 - 2

    我有一个包含模块的模型。我想在模块中覆盖模型的访问器方法。例如:classBlah这显然行不通。有什么想法可以实现吗? 最佳答案 您的代码看起来是正确的。我们正在毫无困难地使用这个确切的模式。如果我没记错的话,Rails使用#method_missing作为属性setter,因此您的模块将优先,阻止ActiveRecord的setter。如果您正在使用ActiveSupport::Concern(参见thisblogpost),那么您的实例方法需要进入一个特殊的模块:classBlah

  5. ruby - 简单获取法拉第超时 - 2

    有没有办法在这个简单的get方法中添加超时选项?我正在使用法拉第3.3。Faraday.get(url)四处寻找,我只能先发起连接后应用超时选项,然后应用超时选项。或者有什么简单的方法?这就是我现在正在做的:conn=Faraday.newresponse=conn.getdo|req|req.urlurlreq.options.timeout=2#2secondsend 最佳答案 试试这个:conn=Faraday.newdo|conn|conn.options.timeout=20endresponse=conn.get(url

  6. ruby - 当使用::指定模块时,为什么 Ruby 不在更高范围内查找类? - 2

    我刚刚被困在这个问题上一段时间了。以这个基地为例:moduleTopclassTestendmoduleFooendend稍后,我可以通过这样做在Foo中定义扩展Test的类:moduleTopmoduleFooclassSomeTest但是,如果我尝试通过使用::指定模块来最小化缩进:moduleTop::FooclassFailure这失败了:NameError:uninitializedconstantTop::Foo::Test这是一个错误,还是仅仅是Ruby解析变量名的方式的逻辑结果? 最佳答案 Isthisabug,or

  7. ruby - 获取模块中定义的所有常量的值 - 2

    我想获取模块中定义的所有常量的值:moduleLettersA='apple'.freezeB='boy'.freezeendconstants给了我常量的名字:Letters.constants(false)#=>[:A,:B]如何获取它们的值的数组,即["apple","boy"]? 最佳答案 为了做到这一点,请使用mapLetters.constants(false).map&Letters.method(:const_get)这将返回["a","b"]第二种方式:Letters.constants(false).map{|c

  8. ruby - 模块嵌套代码风格偏好 - 2

    我的假设是moduleAmoduleBendend和moduleA::Bend是一样的。我能够从thisblog找到解决方案,thisSOthread和andthisSOthread.为什么以及什么时候应该更喜欢紧凑语法A::B而不是另一个,因为它显然有一个缺点?我有一种直觉,它可能与性能有关,因为在更多命名空间中查找常量需要更多计算。但是我无法通过对普通类进行基准测试来验证这一点。 最佳答案 这两种写作方法经常被混淆。首先要说的是,据我所知,没有可衡量的性能差异。(在下面的书面示例中不断查找)最明显的区别,可能也是最著名的,是你的

  9. ruby - 用 Ruby 编写一个简单的网络服务器 - 2

    我想在Ruby中创建一个用于开发目的的极其简单的Web服务器(不,不想使用现成的解决方案)。代码如下:#!/usr/bin/rubyrequire'socket'server=TCPServer.new('127.0.0.1',8080)whileconnection=server.acceptheaders=[]length=0whileline=connection.getsheaders想法是从命令行运行这个脚本,提供另一个脚本,它将在其标准输入上获取请求,并在其标准输出上返回完整的响应。到目前为止一切顺利,但事实证明这真的很脆弱,因为它在第二个请求上中断并出现错误:/usr/b

  10. ruby-on-rails - 使用 config.threadsafe 时从 lib/加载模块/类的正确方法是什么!选项? - 2

    我一直致力于让我们的Rails2.3.8应用程序在JRuby下正确运行。一切正常,直到我启用config.threadsafe!以实现JRuby提供的并发性。这导致lib/中的模块和类不再自动加载。使用config.threadsafe!启用:$rubyscript/runner-eproduction'pSim::Sim200Provisioner'/Users/amchale/.rvm/gems/jruby-1.5.1@web-services/gems/activesupport-2.3.8/lib/active_support/dependencies.rb:105:in`co

随机推荐