外设驱动库开发笔记27:ESP8266无线通讯驱动

我们的物联网产品所使用的平台都支持无线通讯,而且无线通讯本身更的成本较低,受到大家的欢迎。在本篇文章中,我们将详细讨论并实现ESP8266无线通讯模块的驱动。

1、功能概述

ESP8266是由乐鑫公司出品的一款物联网芯片,因为价格较低,性能稳定等收到很大关注。

该芯片可工作于三种WIFI模式下,分别是:station模式,AP模式以及混合模式,通过AT指令进行控制,显影的指令格式为:AT+CWMODE=<mode>mode的取值决定设定的模式:

mode1时,ESP8266工作于station 模式:ESP8266 模块通过路由器连接互联网,手机或电脑通过互联网实现对设备的远程控制。

mode2时,ESP8266工作于softAP 模式:ESP8266 模块作为热点,手机或电脑直接与模块连接,实现局域网无线控制。

mode3时,ESP8266工作于softAP + station 模式:两种模式的共存模式,即可以通过互联网控制可实现无缝切换,方便操作。

ESP8266拥有2种传输模式,即正常模式和透传模式。而传输模式的配置也是采用AT指令,具体格式为:AT+CIPMODE=<mode>。其中mode 取值0时,为普通传输模式;而mode 取值1时,为透传模式,仅支持TCP单连接和 UDP固定通信对端的情况。在正常模式下,每次发送数据前都必须先发送指令AT+CIPSEND=<param>。而在透传模式下,我们就不需要在每次发送数据前都发送指令AT+CIPSEND=<param>了,只需要发送一次AT+CIPSEND,之后发送的所有内容全部当成是数据了。但这又存在一个问题,我们想要发送命令该如何呢?那么就需要发送数据"+++"来退出透传模式。

ESP8266有几种不同的使用方式,最为常见的就是使用AT指令进行操作。ESP8266AT指令分为基础AT指令、WiFi功能AT指令和TCP/IP相关AT指令3个方面。这些指令从使用功能上讲可分为4类:

按照相应的格式发送不同的AT指令就可以实现ESP8266的数据通讯了。

2、驱动设计与实现

ESP8266无线通讯模块是常用的通讯模块,我们已经描述了其功能及通讯方式,接下来我们将设计并实现其驱动程序。

2.1、对象定义

在使用一个对象之前我们需要获得一个对象。同样的我们想要ESP8266无线通讯模块就需要先定义ESP8266无线通讯模块的对象。

2.1.1、对象的抽象

我们要得到ESP8266无线通讯模块对象,需要先分析其基本特性。一般来说,一个对象至少包含两方面的特性:属性与操作。接下来我们就来从这两个方面思考一下ESP8266无线通讯模块的对象。

先来考虑属性,作为属性肯定是用于标识或记录对象特征的东西。我们来考虑ESP8266无线通讯模块对象属性。我们考虑到ESP8266WIFI模式以及数据传输模式决定了其工作方式,在使用过程中有时我们也需要了解这两个模式的配置是什么,所以我们将其作为对象的属性已记录这两个模式配置。我们每一个ESP8266对象都需要接收数据,所以要有一个接受缓存区,我们定义了一个结构体变量来作为对象接收缓冲区。

接着我们还需要考虑ESP8266无线通讯模块对象的操作问题。我们想要使用ESP8266对象实现我们的功能,就需要发送命令或数据以及接收数据。串口接收数据我们一般使用中断方式,所以定义了缓冲区,不再需要特定的操作。串口发送消息需要实现,但这依赖于具体的硬件平台,所以我们将其作为对象的操作。此外,我们使用串口通讯时,需要控制时序就离不开延时函数,而延时操作一般都依赖于具体的软硬件平台,所以我们将延时函数作为对象的一个操作。

根据上述我们对ESP8266无线通讯模块的分析,我们可以定义ESP8266无线通讯模块的对象类型如下:

/*定义ESP8266对象*/
typedef struct Esp8266Object {Esp8266CWModeType cwMode;     //WIFI模式Esp8266CIPModeType cipMode;   //传输模式,正常或透传struct EspRxBuffer{uint8_t queue[Esp8266RxBufferLength];   //数据存储队列uint8_t lengthRecieving;               //正在接收的数据长度uint8_t lengthRecieved;                //已经接收的数据长度}rxBuffer;void (*SendData)(uint8_t *sData,uint16_t sSize);//数据发送函数指针void (*Delayms)(volatile uint32_t nTime);     //延时操作指针
}Esp8266ObjectObject;

2.1.2、对象初始化

我们知道,一个对象仅作声明是不能使用的,我们需要先对其进行初始化,所以这里我们来考虑ESP8266无线通讯模块对象的初始化函数。一般来说,初始化函数需要处理几个方面的问题。一是检查输入参数是否合理;二是为对象的属性赋初值;三是对对象作必要的初始化配置。据此我们设计ESP8266无线通讯模块对象的初始化函数如下:

/*ESP8266对象初始化*/
void Esp8266Initialization(Esp8266ObjectObject *esp,    //ESP8266对象Esp8266CWModeType cwMode,    //WIFI模式Esp8266CIPModeType cipMode,  //传输模式,正常或透传char *wifiName,              //WIFI名称char *wifiPassword,          //WIFI密码ESP8266SendDataType send,    //发送函数指针ESP8266DelaymsType delayms   //毫秒延时函数)
{char cwjap[50];char cwsap[50];if((esp==NULL)||(send==NULL)||(delayms==NULL)){return;}esp->SendData=send;esp->Delayms=delayms;esp->cwMode=cwMode;esp->cipMode=cipMode;esp->rxBuffer.lengthRecieved=0;ClearReciveBuffer(esp);//设置工作模式 1:station模式   2:AP模式  3:兼容 AP+station模式if(Esp8266SendCommmand(esp,cwModeCmd[esp->cwMode],"OK",50)==Esp8266_TxFial){return;}//让Wifi模块重启的命令if(Esp8266SendCommmand(esp,"AT+RST","OK",20)==Esp8266_TxFial){return;}esp->Delayms(3000);         //延时3S等待重启成功if(esp->cwMode==Esp8266_StationMode){sprintf(cwjap,"AT+CWJAP_CUR=\"%s\",\"%s\"\r\n",wifiName,wifiPassword);//让模块连接上自己的路由if(Esp8266SendCommmand(esp,cwjap,"OK",600)==Esp8266_TxFial){return;}if(esp->cipMode==Esp8266_TransMode){if(Esp8266EnterTrans(esp)==Esp8266_TxFial){return;}}else{//=0:单路连接模式     =1:多路连接模式if(Esp8266SendCommmand(esp,"AT+CIPMUX=0\r\n","OK",20)==Esp8266_TxFial){return;}}}else if(esp->cwMode==Esp8266_SoftAPMode){sprintf(cwsap,"AT+CWSAP_CUR=\"%s\",\"%s\"\r\n",wifiName,wifiPassword);//设置模块的WIFI名和密码if(Esp8266SendCommmand(esp,cwsap,"OK",600)==Esp8266_TxFial){return;}}else if(esp->cwMode==Esp8266_MixedMode){//尚未使用,有待添加}
}

2.2、对象操作

我们已经完成了ESP8266无线通讯模块对象类型的定义和对象初始化函数的设计。但我们的主要目标是获取对象的信息,接下来我们还要实现面向ESP8266无线通讯模块的各类操作。

对于ESP8266来说,发送命令主要是AT命令,这是与发送数据完全不同的操作,所以我们设计了一个专用于命令发送的操作函数。

/*ESP8266发送命令*/
static Esp8266TxStatusType Esp8266SendCommmand(Esp8266ObjectObject *esp,char *cmd,char *ack,uint16_t timeOut)
{esp->SendData((unsigned char *)cmd, strlen((const char *)cmd));       //写命令到网络设备if(ack&&timeOut){while(timeOut--)      //等待超时{if(ChecRecieveFinished(esp) == Esp8266_RxFinish)    //如果数据接收完成{if(strstr((const char *)esp->rxBuffer.queue,ack) != NULL)       //如果检索到关键词{ClearReciveBuffer(esp);return Esp8266_RxSucceed;}}esp->Delayms(10);}}return Esp8266_TxFial;
}

ESP8266在发送数据时,因发送模式的不同会有一定区别。在透传模式下只需要发送数据就好了。而在普通模式下,需要先发送AT命令再发送发送数据。所以我们可设计数据发送函数如下:

/*ESP8266发送数据*/
void Esp8266SendData(Esp8266ObjectObject *esp,uint8_t *sData,uint16_t sSize)
{if(esp->cipMode==Esp8266_TransMode){esp->SendData(sData,sSize);}else{char cmd[32];esp->Delayms(50);ClearReciveBuffer(esp);sprintf(cmd,"AT+CIPSEND=%d\r\n",sSize);if(Esp8266SendCommmand(esp,cmd, ">", 1)==Esp8266_RxSucceed)     //收到‘>’时可以发送数据{esp->SendData(sData,sSize);}}
}

3、驱动的使用

我们已经设计并实现了ESP8266无线通讯模块的驱动程序。接下来我们将设计一个简单的应用以验证驱动的设计是否符合要求。

3.1、声明并初始化对象

使用基于对象的操作我们需要先得到这个对象,所以我们先要使用前面定义的ESP8266无线通讯模块对象类型声明一个ESP8266无线通讯模块对象变量,具体操作格式如下:

Esp8266ObjectObject esp;

声明了这个对象变量并不能立即使用,我们还需要使用驱动中定义的初始化函数对这个变量进行初始化。这个初始化函数所需要的输入参数如下:

       Esp8266ObjectObject *esp,    //ESP8266对象

Esp8266CWModeType cwMode,    //WIFI模式

Esp8266CIPModeType cipMode,  //传输模式,正常或透传

char *wifiName,              //WIFI名称

char *wifiPassword,          //WIFI密码

ESP8266SendDataType send,    //发送函数指针

ESP8266DelaymsType delayms   //毫秒延时函数

对于这些参数,对象变量我们已经定义了。而WIFI模式与传输模式均为枚举,根据实际情况选择就好了。同样WIFI名称和WIFI密码更具实际使用情况输入,注意时字符串就可以了。最主要的是我们需要定义几个函数,并将函数指针作为参数。这几个函数的类型如下:

/*定义ESP8266数据发送指针类型*/
typedef void (*ESP8266SendDataType)(uint8_t *sData,uint16_t sSize);/*延时操作指针*/
typedef void (*ESP8266DelaymsType)(volatile uint32_t nTime);   

对于这几个函数我们根据样式定义就可以了,具体的操作可能与使用的硬件平台有关系。实际上我们主要需要关注的是串口发送函数。具体函数定义如下:

/*串口数据发送*/
static void SendDataForEsp8266(uint8_t *txData,uint16_t length)
{HAL_UART_Transmit(&esp8266huart,txData,length,1000);
}

对于延时函数我们可以采用各种方法实现。我们采用的STM32平台和HAL库则可以直接使用HAL_Delay()函数。于是我们可以调用初始化函数如下:

/*ESP8266对象初始化*/Esp8266Initialization(&esp,                   //ESP8266对象Esp8266_StationMode,    //WIFI模式Esp8266_TransMode,      //传输模式,正常或透传wifiName,               //WIFI名称wifiPassword,           //WIFI密码SendDataForEsp8266,     //发送函数指针HAL_Delay               //毫秒延时函数);

3.2、基于对象进行操作

我们定义了对象变量并使用初始化函数给其作了初始化。接着我们就来考虑操作这一对象获取我们想要的数据。我们在驱动中已经将获取数据并转换为转换值的比例值,接下来我们使用这一驱动开发我们的应用实例。

/*ESP8266数据通讯*/
void Esp8266DataCommunication(void)
{uint8_t sData[16]={0x10,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0A,0x0B,0x0C,0x0D,0x0E,0x0F};Esp8266SendData(&esp,sData,16);
}

4、应用总结

在这一篇中我们设计并实现了ESP8266无线模块的驱动,并基于次驱动程序设计了一个简单的验证应用。测试结果是符合我们的预期的,说明我们设计的驱动没有问题。

       在使用驱动程序时需要注意,这一驱动只是实现了ESP8266的基本功能,所以要想实现更复杂的功能是可以在驱动基础上扩展的。后续我们也会根据使用的需要进一步扩充驱动。当然这个驱动是基于AT指令来实现操作的,扩充这个驱动程序的功能也需要使用AT指令来实现。

       本驱动程序在设计时,考虑使用串口中断来接收数据,所以我们为对象设计了一个接收数据缓存结构。在设计应用时需在串口中断服务函数中向缓存种添加数据。

欢迎关注:

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/499331.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

外设驱动库开发笔记28:W5500以太网控制器

以太网通讯是一种被广泛使用的数据通讯方式。在嵌入式应用中也经常使用&#xff0c;但协议栈的实现并不是一件容易的事。不过有些以太网控制器就带有协议栈&#xff0c;如W5500。在本篇中我们将讨论如何设计并实现W5500以太网控制器的驱动。 1、功能概述 W5500是WIZnet开发的…

外设驱动库开发笔记29:DS17887实时时钟驱动

一些时候&#xff0c;在我们的嵌入式产品中需要记录时间&#xff0c;所以我们就需要获取时钟&#xff0c;当然实现的方式多种多样&#xff0c;有的MCU本身就有这一功能&#xff0c;不过精度较低。当我们的应用要求较高时就需要使用专门的实时时钟芯片&#xff0c;如DS17887。在…

外设驱动库开发笔记30:宇电AI-BUS通讯驱动

嵌入式系统通常都会与外部设备进行通讯&#xff0c;这就涉及到通讯协议的问题。这些通讯协议有的是标准协议有的厂家自定义的协议&#xff0c;如宇电的AI-BUS。在本篇中&#xff0c;我们将讨论AI-BUS的驱动&#xff0c;以便于与宇电设备的通讯。 1、功能概述 宇电的设备使用基…

步进电机驱动技术3:基于ULN2003的步进电机驱动

在我们的项目中&#xff0c;经常使用到低电压小功率的步进电机&#xff0c;此类步进电机若采用驱动器控制不断成本高也过于复杂&#xff0c;我们可以直接使用场效应管或者达林顿管来实现对其的驱动。在本篇中&#xff0c;我们就来讨论一下基于ULN2003A达林顿管实现对步进电机的…

通讯接口应用笔记2:MAX3160实现多协议通讯

在一些应用需求中&#xff0c;我们需要对外部提供串行通讯端口&#xff0c;但这些端口所通讯的目标设备各有不同&#xff0c;接口协议也有RS232以及RS485和RS422多种。面对这种情况&#xff0c;我们当然可以同时设计多个串口以适应不同需要&#xff0c;但无疑对硬件资源是一种浪…

电机速度曲线规划1:梯形速度曲线设计与实现

电机驱动是很常见的应用&#xff0c;在很多系统中我们都会碰到需要改变电机的速度以实现相应的控制功能&#xff0c;这就涉及到电机速度曲线规划的问题。在这篇中我们就来简单讨论一下电机的梯形曲线规划的问题。 1、基本原理 梯形速度曲线控制算法是工业控制领域应用最为广泛…

文件系统应用笔记之一:FatFS在STM32F4上的移植

在实现如U盘文件读写&#xff0c;SD卡的文件读写等工作时&#xff0c;我们往往需要一个文件系统来支持我们的工作。特别在一些MCU应用中&#xff0c;文件系统的加入能明显改善系统交互的友好性。在这一篇中&#xff0c;我们就来讨论FatFS文件系统在STM32F4上的移植和应用。 1、…

通讯接口应用笔记3:使用W5500实现Modbus TCP服务器

前面我们设计实现了W5500的驱动程序&#xff0c;也讲解了驱动的使用方式。在最近一次的项目应用中&#xff0c;正好有一个使用W5500实现TCP通讯的需求&#xff0c;所以我们就使用该驱动程序轻松实现。这一篇中我们就来说一说基于我们W5500通讯驱动程序实现TCP通讯的过程。 1、…

电机速度曲线规划2:S形速度曲线设计与实现

电机驱动是很常见的应用&#xff0c;在很多系统中我们都会碰到需要改变电机的速度以实现相应的控制功能&#xff0c;这就涉及到电机速度曲线规划的问题。在这篇中我们就来简单讨论一下电机的S型曲线规划的问题。 1、基本原理 S型速度曲线控制算法是工业控制领域另一种常用的加…

USB应用开发笔记之一:STM32上实现USB主机读写U盘

在项目应用中&#xff0c;经常会有对外交换数据的需求。USB接口读写U盘无疑是一种颇为方便的选择。在这一篇中&#xff0c;我们就来讨论如何在STM32上实现USB主机读写U盘文件的方法。 1、应用概述 在我们的产品上有这样一个需求&#xff0c;希望通过大容量的U盘存取数据。我们…

Modbus协议栈综合实例设计

自我们开源了我们的Modbus协议栈之后&#xff0c;就一直有朋友来信说希望提供示例。这次我们整理了几个例子以供参考。 1、应用实例规划 在这次的实例中&#xff0c;我们使用的目标板拥有一个以太网接口、一个RS232串行接口和一个RS485串行接口&#xff0c;所以我们规划实现&a…

ThreadX应用开发笔记之二:移植ThreadX到STM32H7平台

前面我们将ThreadX成功移植到了STM32F4平台&#xff0c;但这只是我们的部分应用。我们希望将ThreadX的优势发挥到我们的更多应用中&#xff0c;所以在这一篇中我们就来实现将ThreadX移植到STM32H7平台中。 1、前期准备 在开始将ThreadX移植到STM32H7平台之前&#xff0c;我们需…

外设驱动库开发笔记31:S-Modlue远红外气体传感器驱动

在气体分析类产品中&#xff0c;我们经常会用到远红外气体传感器。我们就在碳氢类气体成分分析中使用了S-Modlue远红外气体传感器。接下来&#xff0c;我们将讨论S-Modlue远红外气体传感器驱动的设计与实现。 1、功能概述 S-MODULE EVO 使用非分散红外检测技术NDIR&#xff0c…

外设驱动库开发笔记32:HLPM025K3 PM2.5传感器驱动

现在人们对大气环境及室内环境都比较关注。PM2.5在生活中也是常见的词汇。在有些产品中就要求检测PM2.5的数值。检测PM2.5的手段多种多样&#xff0c;在要求不高时我们通常可以采用激光模块。在这一篇中&#xff0c;我们将讨论HLPM025K3 PM2.5传感器驱动的设计与实现。 1、功能…

外设驱动库开发笔记33:LCD1602液晶显示屏驱动

LCD1602是一种工业字符型液晶&#xff0c;能够同时显示16x02即32个字符。LCD1602液晶显示的原理是利用液晶的物理特性&#xff0c;通过电压对其显示区域进行控制&#xff0c;即可以显示出图形。在这一章我们就来讨论LCD1602液晶显示屏驱动的设计与实现。 1、功能概述 LCD1602液…

滤波器开发之四:基于算术平均的中值滤波器

在信号采集系统中&#xff0c;除了我们感兴趣的数据外&#xff0c;难免会有一些来自于环境的干扰信号。但我们总希望我们得到的数据是纯净而真实的&#xff0c;为了达到这个目标&#xff0c;我们不得不想办法去除这些干扰信号&#xff0c;于是滤波器就成为我们必不可少的帮手。…

LwIP应用开发笔记之十一:LwIP带操作系统UDP服务器

我们已经实现了在FreeRTOS系统上的LwIP的移植工作&#xff0c;但只是简单的在系统平台上跑了起来。我们还希望能做更多的事情&#xff0c;这一节我们就在FreeRTOS系统上实现基于LwIP的UDP服务器。 1、UDP协议简述 UDP协议全称是用户数据报协议&#xff0c;在网络中它与TCP协议…

滤波器开发之五:基于算术平均的限幅滤波器

通过AD采集数据时&#xff0c;我们总是希望采集到的数据是纯净而真实的&#xff0c;而实际上环境中存在太多的干扰信号&#xff0c;为了让我们得到的数据尽可能地接近实际值&#xff0c;我们需要降低这些干扰信号的影响。所以软件实现的数字滤波器应运而生&#xff0c;这一篇我…

外设驱动库开发笔记34:OLED显示屏驱动

现在OLED显示屏在嵌入式系统中应用的越来越多。对于一些显示信息不太复杂&#xff0c;以显示信息为主的需求&#xff0c;我们一般会选择OLED显示屏。在这一篇中&#xff0c;我们将讨论OLED显示屏驱动的设计与实现。 1、功能概述 从使用的情况来说&#xff0c;较为常用的是0.96…

外设驱动库开发笔记35:迪文触摸屏驱动

有些时候嵌入式系统也需要显示更为复杂的图形&#xff0c;需要更丰富的数据展示。为此&#xff0c;我们需要更大&#xff0c;色彩更丰富&#xff0c;带触屏的显示屏&#xff0c;当然性价比更高就最好了。在我们的项目中遇到此类需求&#xff0c;我们有时会选择DWIN触摸屏。在本…