用ESP 8266模块AT指令开发无线通信系统

最近一直在做关于气浮台的项目,里面有一个小环节就是需要把设备的数据传输下来,因为之前对通信几乎是小白,加上时间比较紧,凡是涉及到底层的东西都不敢碰,最后比较了一番选了ESP 8266这个模块来开发,通过AT指令进行开发,用的是C语言,运行在PC 104上(当然普通PC更没问题了),大概五天时间就做完了,下面介绍一下详细内容。

(一)ESP 8266模块介绍

这个模块的详细资料网上很容易找到,在此就不详述了,简单说几点吧。

这个模块开发有两种方式:第一种是用官方SDK来开发,适合对硬件有一定了解的朋友入手,因为这个模块本身的功能其实很强大,只用来通讯有点小题大做的感觉,但是这种方式不适合新手,入手难度有点高;第二种就是AT指令开发,很简单,拿一般的串口助手就可以调试。(注意调试的时候一定要先按回车再发送)

?

?

这个模块总共有三种工作方式:AP,STATION,AP+STATION。因为我需要完成的是多个设备数据传输,因此透传就不考虑了,这里我用的是一个模块用作热点同时开启服务器(用AP+STATION),通过串口接在终端上收数据;其他的模块通过串口接在设备上(用STATION)。相当于组建了一个小的局域网,基于TCP协议的WiFi通信。

这里再单独提一下,用AT指令开发有一个很头疼的地方在于指令的返回格式不统一,所以程序里面的判断条件会比较多。后面我会仔细的总结一下,其他的信息大家可以去找用户手册,里面对模块的介绍以及AT指令都比较完整。

(二)用C语言实现WIN 32下的串口通讯

这一步说白了就是怎么用C语言去完成串口助手最基本的功能,但是也必须要仔细,很多地方容易出错。

1. 首先打开串口,Createfile函数的具体用法在此不详述了,不熟悉的朋友可以去百度。

espCom = CreateFile("COM3", GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
if (espCom == INVALID_HANDLE_VALUE)
{
	printf("open COM3 failed\n");
	exit(2);
}

提醒一下大家,如果设备的串口不是COM1-COM9,比如是COM10,那么函数第一个不能写成“COM10”了,得写成“\\\\.\\COM10”,因为COM10以上的串口对于文件名系统而言只是一般的文件,而非串行设备。

2. 完成串口相应的配置工作

espTimeOuts.ReadIntervalTimeout = 500;//MAXDWORD; //5000;
espTimeOuts.ReadTotalTimeoutConstant = 5000; //0;//1000;
espTimeOuts.ReadTotalTimeoutMultiplier = 500;// 0;//500;
espTimeOuts.WriteTotalTimeoutConstant = 2000;
espTimeOuts.WriteTotalTimeoutMultiplier = 500;
if (!SetCommTimeouts(espCom, &espTimeOuts))
{
	printf("写入超时参数错误\n");
	exit(3);
}
if (!SetupComm(espCom, 1024, 1024))
{
	printf("设置串口读写缓冲区失败\n");
	exit(4);
}
if (!GetCommState(espCom, &espdcb))
{
	printf("获取串口属性失败\n");
	exit(5);
}
espdcb.BaudRate = BAUD_RATE;
espdcb.ByteSize = 8;
espdcb.Parity = NOPARITY;
espdcb.StopBits = ONESTOPBIT;
if (!SetCommState(espCom, &espdcb))
{
	printf("设置串口参数出错\n");
	exit(6);
}
printf("无线通信串口打开成功!\n");
PurgeComm(espCom, PURGE_TXCLEAR | PURGE_RXCLEAR | PURGE_RXABORT | PURGE_TXABORT);  //清空缓冲区

  

里面总共涉及到了5个工作,对应的函数介绍不理解请自行百度,代码只简单地注释了一下。其中容易出问题的在于SetCommTimeouts的时间设置,读取时间间隔、延时这些大家一定要仔细,初始参数可以就按上面的取,但如果出现乱码或者读取不完整的情况,先调一调这几个参数,每个人的需求不同,以上参数也可能不同。

3. 接下来分别建立读和写的线程

为了方便后面的介绍,这里先把一些代码定义贴出来(以下介绍将分为服务器和客户端两部分)

(1)这是客户端的定义

HANDLE espCom;
COMMTIMEOUTS espTimeOuts;
COMSTAT comstat;
DCB espdcb;
unsigned int esp_order = 0;  //指令执行顺序
char ESP_RXBUFF[200];
char ESP_SENDDATA[200] = {};   //数据发送包
float ESP_SYS_DATA[20] = {};   //系统数据
BOOL ESP_RTS=0;   //数据发送请求标志

//AT指令集
const char *esp_com_AT = { "AT\r\n" };    //test
const char *esp_com_AT_CWMODE = { "AT+CWMODE=1\r\n" };        //Station模式
const char *esp_com_AT_CIPMUX = { "AT+CIPMUX=1\r\n" };        //多连接模式
const char *esp_com_AT_CIPSERVER = { "AT+CIPSERVER=0\r\n" };    //关闭服务器
const char *esp_com_AT_CWJAP = { "AT+CWJAP=\"esp\",\"123456\"\r\n" };   //连接esp网络
const char *esp_com_AT_CIPSTART = { "AT+CIPSTART=\"TCP\",\"192.168.4.1\",5000\r\n" };            //加入服务器
const char *esp_com_AT_CIPSEND = { "AT+CIPSEND=40\r\n" };     //请求发送数据
const char *esp_com_AT_RST = { "AT+RST\r\n" };   //ESP8266重启
int esp_AT_len = strlen(esp_com_AT);
int esp_AT_CWMODE_len = strlen(esp_com_AT_CWMODE);
int esp_AT_CIPMUX_len = strlen(esp_com_AT_CIPMUX);
int esp_AT_CIPSERVER_len = strlen(esp_com_AT_CIPSERVER);
int esp_AT_CWJAP_len = strlen(esp_com_AT_CWJAP);
int esp_AT_CIPSTART_len = strlen(esp_com_AT_CIPSTART);
int esp_AT_CIPSEND_len = strlen(esp_com_AT_CIPSEND);
int esp_AT_RST_len = strlen(esp_com_AT_RST);

  

主函数中开启读写线程

HANDLE hThread1 = CreateThread(NULL, 0, ReadThread, 0, 0, NULL);   //读线程
HANDLE hThread2 = CreateThread(NULL, 0, WriteThread, 0, 0, NULL);  //写线程
CloseHandle(hThread1);
CloseHandle(hThread2);

  

函数具体内容。总的来说就是依靠esp_order来一条一条发送指令,在确保指令执行后再发送下一条。(在这里希望大家把需要用到的指令都在串口助手上试一遍,特别是返回的内容一定要看清楚,大部分指令都是返回XXXXXXXXXXX   OK,这一部分只需要检测OK就能确保指令执行了,但有些特殊的就需要单独设置了,比如重启指令最后会返回一串乱码+ready)

int ESP_ReceiveChar()    //读命令
{
	DWORD ESP_READ_COUNT;
	BOOL bReadResult;
	BOOL bResult;
	DWORD dwError;

	for (;;)
	{
		bResult = ClearCommError(espCom, &dwError, &comstat);
		if (comstat.cbInQue == 0)
			continue;
		bReadResult = ReadFile(espCom, ESP_RXBUFF, 200, &ESP_READ_COUNT, NULL);
		if (!bReadResult)
		{
			printf("读串口失败!\n");
			return FALSE;
		}

		if ((esp_order == 0)||(esp_order>=4))           //检查测试指令以及后续指令的返回情况
		{
			if (ESP_RXBUFF[ESP_READ_COUNT - 4] == ‘O‘)
			{
				printf("%s\r", ESP_RXBUFF);
				esp_order++;
			}
		}
		else
		{
			if (ESP_RXBUFF[ESP_READ_COUNT - 4] == ‘d‘)        //重启指令检查
			{
				printf("ready\n");
				esp_order++;
			}
		}
		if ((ESP_RXBUFF[ESP_READ_COUNT - 4] == ‘I‘) || (ESP_RXBUFF[ESP_READ_COUNT - 5] == ‘T‘))     //检查自动连接WiFi的情况
		{
			printf("%s\r", ESP_RXBUFF);
			esp_order++;
		}
		if (ESP_RXBUFF[ESP_READ_COUNT - 2] == ‘>‘)      //数据发送请求成功标志
		{
			printf("%s", ESP_RXBUFF);
			esp_order++;
		}
		memset(ESP_RXBUFF, 0, 200);//清空缓冲区
		PurgeComm(espCom, PURGE_RXCLEAR | PURGE_RXABORT);
	}

	return 0;

}

DWORD WINAPI ReadThread(LPVOID pParam)     //读线程
{
	ESP_ReceiveChar();
	return 0;
}

int ESP_WriteChar(const char* WriteBuffer, DWORD NumToSend)       //写命令
{
	COMSTAT ComStat;
	DWORD dwErrorFlags;
	BOOL bWriteStat;
	DWORD BytesSent;
	ClearCommError(espCom, &dwErrorFlags, &ComStat);
	bWriteStat = WriteFile(espCom, WriteBuffer, NumToSend, &BytesSent, NULL);
	if (!bWriteStat)
		printf("写串口失败");
	if (BytesSent != NumToSend)
		printf("WARNING: WriteFile() error.. Bytes Sent: %d; MessageLength: %d\n", BytesSent, NumToSend);

	PurgeComm(espCom, PURGE_TXCLEAR | PURGE_TXABORT);
	return true;
}

DWORD WINAPI WriteThread(LPVOID pParam)          //写线程
{
	while (espCom != INVALID_HANDLE_VALUE)
	{
		Sleep(1000);
		if (esp_order == 0)
			ESP_WriteChar(esp_com_AT, esp_AT_len);
		if (esp_order == 1)
			ESP_WriteChar(esp_com_AT_RST, esp_AT_RST_len);
		if (esp_order == 4)
			ESP_WriteChar(esp_com_AT_CIPSTART, esp_AT_CIPSTART_len);
		if ((esp_order >= 5) && (esp_order % 2 != 0) && (ESP_RTS == 0))       //发送数据请求
		{
			ESP_RTS = 1;
			ESP_WriteChar(esp_com_AT_CIPSEND, esp_AT_CIPSEND_len);
			//Sleep(500);
			//ESP_WriteChar(ESP_SENDDATA_TEST, strlen(ESP_SENDDATA_TEST));
		}
		if ((esp_order >= 5) && (esp_order % 2 == 0) && (ESP_RTS == 1))        //数据发送
		{
			ESP_RTS = 0;
			memset(ESP_SENDDATA, 0, 200);
			unsigned char esp_ls[4];
			for (int i = 0, j = 0; i < 10; i++, j += 4)
			{
				memcpy(esp_ls, &ESP_SYS_DATA[i], sizeof(float));
				ESP_SENDDATA[j] = esp_ls[0];
				ESP_SENDDATA[j + 1] = esp_ls[1];
				ESP_SENDDATA[j + 2] = esp_ls[2];
				ESP_SENDDATA[j + 3] = esp_ls[3];
			}

			//检查发送数据
			float final[10];
			for (int i = 0, j = 0; i < 10; i++, j += 4)
			{
				memcpy(&final[i], &ESP_SENDDATA[j], sizeof(float));
			}
			for (int i = 0; i<10; i++)
				printf("%f  ", final[i]);
			printf("\n");

			ESP_WriteChar(ESP_SENDDATA, 40);
		}
	}
	return true;
}

!!!注意:以上代码执行的前提是先按以下指令在串口助手进行设置

AT+CWMODE=1
AT+CIPMUX=1
AT+CIPSERVER=0
AT+CWJAP="esp","123456"     //这个是默认的WiFi,也可以自己设置名字密码
AT+CIPSTART="TCP","192.168.4.1",5000      //ip地址需要在服务器那个模块上进行查询(后面关于服务器的代码有),5000是开服务器的时候设置的端口

(2)服务器的定义

HANDLE espCom;
COMMTIMEOUTS espTimeOuts;
COMSTAT comstat;
DCB espdcb;
unsigned int esp_order = 0;   //指令执行顺序
char ESP_RXBUFF[200];    //读入数据缓冲区
//AT指令集
const char *esp_com_AT = { "AT\r\n" };    //TEST
const char *esp_com_AT_CWMODE = { "AT+CWMODE=3\r\n" };        //AP+Station
const char *esp_com_AT_CIPMUX = { "AT+CIPMUX=1\r\n" };        //多连接模式
const char *esp_com_AT_CIPSERVER = { "AT+CIPSERVER=1,5000\r\n" };    //开启服务器
const char *esp_com_AT_CIFSR = { "AT+CIFSR\r\n" };            //查询IP地址
int esp_AT_len = strlen(esp_com_AT);
int esp_AT_CWMODE_len = strlen(esp_com_AT_CWMODE);
int esp_AT_CIPMUX_len = strlen(esp_com_AT_CIPMUX);
int esp_AT_CIPSERVER_len = strlen(esp_com_AT_CIPSERVER);
int esp_AT_CIFSR_len = strlen(esp_com_AT_CIFSR);

  

读写线程的开启与客户端相同,下面贴出具体函数

int ESP_ReceiveChar()    //读指令
{
	DWORD ESP_READ_COUNT;
	BOOL bReadResult;
	BOOL bResult;
	DWORD dwError;

	for (;;)
	{
		bResult = ClearCommError(espCom, &dwError, &comstat);   //清除串口error
		if (comstat.cbInQue == 0)
			continue;
		bReadResult = ReadFile(espCom, ESP_RXBUFF, 200, &ESP_READ_COUNT, NULL);
		if (!bReadResult)
		{
			printf("读串口失败!\n");
			return FALSE;
		}

		if (esp_order == 6)     //注意两个if调用顺序
		{
			if (ESP_RXBUFF[ESP_READ_COUNT - 5] == ‘E‘)     //显示client连接情况
				printf("%s\n", ESP_RXBUFF);
			else     //client发送的数据
			{
				//printf("原始:%s\n", ESP_RXBUFF);
				float final[10];
				for (int i = 0; i <= 11; i++)    //ESP_RXBUFF头字节
				{
					printf("%c", ESP_RXBUFF[i]);
				}
				printf("\n");
				for (int i = 0, j = 12; i < 10; i++, j += 4)    //数据包
				{
					memcpy(&final[i], &ESP_RXBUFF[j], sizeof(float));
				}
				for (int i = 0; i < 10; i++)
					printf("%f ", final[i]);
				printf("\n");
			}
		}

		if (ESP_RXBUFF[ESP_READ_COUNT - 4] == ‘O‘)    //指令执行情况
		{
			printf("%s\r", ESP_RXBUFF);
			esp_order++;
			//printf("%d\n",strlen(ESP_RXBUFF));//AT指令下返回字符串长度为11
		}
		memset(ESP_RXBUFF, 0, 200);//清空缓冲区
		//printf("缓冲区长度:%d\n", strlen(ESP_RXBUFF));
		PurgeComm(espCom, PURGE_RXCLEAR | PURGE_RXABORT);
	}

	return 0;
}

DWORD WINAPI ReadThread(LPVOID pParam)     //读线程
{
	ESP_ReceiveChar();
	return 0;
}

int ESP_WriteChar(const char* WriteBuffer, DWORD NumToSend)        //写指令
{
	COMSTAT ComStat;
	DWORD dwErrorFlags;
	BOOL bWriteStat;
	DWORD BytesSent;
	ClearCommError(espCom, &dwErrorFlags, &ComStat);
	bWriteStat = WriteFile(espCom, WriteBuffer, NumToSend, &BytesSent, NULL);
	if (!bWriteStat)
	{
		printf("写串口失败");
	}

	if (BytesSent != NumToSend)
	{
		printf("WARNING: WriteFile() error.. Bytes Sent: %d; MessageLength: %d\n", BytesSent, NumToSend);
	}
	PurgeComm(espCom, PURGE_TXCLEAR | PURGE_TXABORT);
	return true;
}

DWORD WINAPI WriteThread(LPVOID pParam)    //写线程
{
	while (espCom != INVALID_HANDLE_VALUE)
	{
		Sleep(1000);
		if (esp_order == 0)
			ESP_WriteChar(esp_com_AT, esp_AT_len);
		else if (esp_order == 1)
			ESP_WriteChar(esp_com_AT_CWMODE, esp_AT_CWMODE_len);
		else if (esp_order == 2)
			ESP_WriteChar(esp_com_AT_CIPMUX, esp_AT_CIPMUX_len);
		else if (esp_order == 3)
			ESP_WriteChar(esp_com_AT_CIPSERVER, esp_AT_CIPSERVER_len);
		else if (esp_order == 4)
		{
			esp_order++;
			ESP_WriteChar(esp_com_AT_CIFSR, esp_AT_CIFSR_len);
		}
	}
	return true;
}

  

代码有点绕,简单解释一下吧。先按照所需的AT指令进行设置,然后就是接收客户端发来的数据,我上面的发送内容是设备数据,都是float类型,通过memcpy换到char数组然后发送出去。可以对照客户端的代码看一下。

这是实际测试效果

?

(三)总结

ESP 8266是一个很容易上手的无线通信模块,在物联网领域用的很多。用AT指令可以加快开发速度,但是如果对传输要求较高,时间比较充裕也可以去采用SDK,效果会更好。上面的代码又不太明白的朋友可以留言或者私信,有空我会尽可能帮助大家。

原文地址:https://www.cnblogs.com/qi-zhang/p/11375182.html

时间: 2024-11-13 09:53:05

用ESP 8266模块AT指令开发无线通信系统的相关文章

2017.7.1 慕课网-Java从零打造企业级电商项目实战:用户模块设计与开发

2. 用户模块设计与开发 2.1 要实现的功能 2.2 mmall_user表 2.3 用户模块接口设计 (1)门户-用户接口 http://git.oschina.net/imooccode/happymmallwiki/wikis/%E9%97%A8%E6%88%B7_%E7%94%A8%E6%88%B7%E6%8E%A5%E5%8F%A3 (2)后台-用户接口 http://git.oschina.net/imooccode/happymmallwiki/wikis/%E5%90%8E%E

HTTP扫盲及nginx基础性模块常用指令整理

第一部分:HTTP基础知识 在介绍nginx常用模块中的指令时,先来回顾一下http的相关知识: 1.http的工作原理 http的工作原理大致是这样的: a).客户端与服务器先建立一个TCP连接: b).客户端通过已建立的TCP连接向服务端发送一个http请求报文: c).服务器收到请求报文后开始解析报文.定位所请求的资源,读取资源并封装成响应报文后发送给客户端: d).如果没有启用持久连接,服务器端主动断开tcp连接,客户端被动关闭:如果启用了持久连接,那该tcp连接保持一段时间后,在该时间

Winform开发框架之通用高级查询模块--SNF快速开发平台3.3-Spring.Net.Framework

最近项目确实忙,但也是一直忙于有关项目和框架技术的事情,也一直致力于改善我的WInform开发框架.使得自己及客户使用起来更加方便,更加友好,更加高效. 在很多程序模块中都很常见,也是给客户扩展查询的一个很好的补充,由于我一直希望我的Winform开发框架能够精益求精,所以做了这个模块,希望对今后我自己所有的项目以及框架本身,都能高效的使用. 1.通用高级查询模块的用途及介绍 既然称之为通用查询模块,那么他就不能与具体的表字段有耦合关系,但是要实现具体的查询,必须通过某种方式进行属性传递,实现更

谷歌拟在新兴市场资助和开发无线网络

据国外媒体报道,谷歌正在采取多方面的努力措施,以此在新兴市场创建并运营属于自己的无线网络,从而将新兴市场上的10多亿用户连接到互联网之中. 据知情人士透露的消息称,按照计划,谷歌的这些无线网络将服务撒哈拉沙漠以南的非洲和东南亚等地区的用户,特别是服务诸多大城市之外地区的用户,因为这些区域目前还无法使用无线互联网服务.与此同时,这些知情人士还称,谷歌的无线网络还可能会用来提高大城市区域的网速. 消息人士透露称,谷歌计划与新兴市场的当地电信公司和设备供应商合作,以此创建这些网络,同时还将创建一些商业

ESA2GJK1DH1K升级篇: 移植远程更新程序到STM32F103RET6型号的单片机,基于(GPRS模块AT指令TCP透传方式)

前言 上节实现远程更新是更新的STM32F103C8T6的单片机 GPRS网络(Air202/SIM800)升级STM32: 测试STM32远程乒乓升级,基于(GPRS模块AT指令TCP透传方式),定时访问升级 这节将告诉大家如何移植到其它型号的单片机. 这一节以 STM32F103RET6 (512KB Flash 64KB RAM) 为例 我使用我的这块板子 大家测试的时候可以按照下面的方式接到自己的GPRS模块(Air202 / SIM800) 单片机串口1 接到GPRS的AT指令配置串口

基于上海拜安光纤传感分析仪OSA搭建无线通信系统

0.前言 项目现场需要通过光纤测量应变,并需要远程实时监控.打算搭建一个无线通信系统,基于上海拜安光纤传感分析仪(OSA). 购买了一套OSA,和上海正伟科技有限公司的4G传输模块(MQ). 1.本地搭建通信 1.1设备介绍 上海拜安光纤解调设备(OSA),FT310系列,有RJ45端口和RS232协议端口. 上海正伟无限发送设备(MQ),有RS232协议端口. 拜安提供了以TCP/IP协议通信的上位机,连上网线即可收发数据并显示. 拜安也提供了以Modbus TCP协议通信的串口方式. 1.2

ESP8266 AT指令开发(基于STC89C52单片机): 测试下诱人的程序(51单片机,8266,MQTT远程通信控制)

前言 实现的功能,APP通过SmartConfig给Wi-Fi模块配网并绑定设备,然后通过MQTT远程控制开发板的继电器, 简而言之: 51单片机+ESP8266用AT指令实现实现MQTT,(连接的本人云服务器上的MQTT服务器软件) ESP8266订阅的主题:device/Wi-Fi的MAC地址 ESP8266发布的主题:user/Wi-Fi的MAC地址 下载单片机程序 安装APP 调整拨动开关-51单片机和ESP8266通信 跳线帽 打开APP 选择添加设备 手机连接自家的路由器,输入路由器

两个HC-05蓝牙模块互相绑定构成无线串口模块

HC-05 嵌入式蓝牙串口通讯模块(以下简称模块)具有两种工作模式:命令响应工作模式和自动连接工作模式,在自动连接工作模式下模块又可分为主(Master).从(Slave)和回环(Loopback)三种工作角色.当模块处于自动连接工作模式时,将自动根据事先设定的方式连接的数据传输:当模块处于命令响应工作模式时能执行下述所有 AT 命令,用户可向模块发送各种 AT 指令,为模块设定控制参数或发布控制命令.通过控制模块外部引脚(PIO11)输入电平,可以实现模块工作状态的动态转换. 1.HC-05蓝

ESA2GJK1DH1K升级篇: STM32远程乒乓升级,基于(Wi-Fi模块AT指令TCP透传方式),MQTT通信控制升级(含有数据校验)

前言 这一节实现的功能是使用MQTT通信控制模块去升级 其实和这一节实现的功能一样  https://www.cnblogs.com/yangfengwu/p/11854595.html 这一节还是着重讲解一下如何移植升级升级程序到自己的项目 我做的单片机远程升级封装文件的目的是希望大家直接移植到自己的项目使用! 准备一个已经实现了TCP的工程,拷贝升级处理文件 1.准备的工程 2.把BootLoader需要用到的文件拷贝到自己的工程 拷贝到自己的项目里面 整理下工程 1.自行添加到工程,还有设