深圳网站制作公司报价,织梦网站如何做软件下载,设计师 个人网站,谷歌浏览器引擎入口目录
一、WIFI简介
二、基础网络知识
三、思路讲解
四、代码分析 4.1 状态机制 4.2 客户端连接 4.3 应用数据接收处理 4.4 数据发送 4.5 主函数调用 4.6 网络连接ID分配
五、总结 一、WIFI简介 WIFI在我们生活中太常见了#xff0c;手机电脑都可以用WiFi连接路由器进行上…目录
一、WIFI简介
二、基础网络知识
三、思路讲解
四、代码分析 4.1 状态机制 4.2 客户端连接 4.3 应用数据接收处理 4.4 数据发送 4.5 主函数调用 4.6 网络连接ID分配
五、总结 一、WIFI简介 WIFI在我们生活中太常见了手机电脑都可以用WiFi连接路由器进行上网那么在单片机领域又是基于什么物理器件联网的呢最常见的WIFI模块是ESP8266以及性能更好的ESP32还有比较新的BL602等等种类比较多那么我们净化器这个项目选择的是成熟稳定且便宜的ESP8266。它可以标准模式下连接路由器自身也可以作为热点供别人连接性能还是很强悍的。 ESP8266文档中心在这儿ESP8266文档中心 | 安信可科技在这里我们采用AT指令的方式对齐进行驱动具体文档可以按下图方式下载。AT指令是一个比较规范的底层通讯协议也没什么神秘的就是一个比较固定的格式AT具体指令参数 这种模式AT指令的好处是简单易懂字符串的形式比较明了对应的缺点就是没有很灵活要根据输出内容处理字符串信息有时候返回的信息不充分或者不完整对开发人员的程序稳定性有一定的考验。 二、基础网络知识 这里简要说明下网络的基础知识主要协议分为TCP和UDPTCP是比较可靠的连接数据包会有重发机制发送方没收到确认就会重新发送而UDP就不管那么多了按照目标地址发过去就是了有没有收到就不管了。 通常要连接一个服务器需要的信息有目标服务器的IP地址、要连接的端口以及所使用的协议三个如下图所示。其中IP地址也可以用域名代替这样模块内部就是需要多个步骤把域名发往域名服务器解析成具体的IP地址目标端口就是一个数值服务器需要打开这个端口客户端才能连接成功和发送数据否则会一直连接错误协议就是上面所说的TCP和UDP了我们这里一般都是使用TCP的后面会讲解的MQTT是基于TCP连接的用UDP的也有比如NB-Iot的Coap协议。 基本的网络知识就这样了没有很复杂会用就行如果要深入整个网络知识体系那就学海无涯了一本TCP/IP协议知识的书比枕头还厚个人学习推荐LWIP。 三、思路讲解 既然是驱动程序必然要有比较好的通用性和移植性。ESP8266的基本使用流程是配置WIFI模式以及SSID和密码然后等到模块连接到指定的路由器上连接完成后再进行网络方面的设置比如可以多连接、非透传模式和TCP服务器的建立等等AT手册里有很多不一定全用根据自己的需求增删最后就是根据应用层的目标服务器信息进行连接和收发数据了。 整体来讲逻辑不会很复杂但是细节很多。比如 1、ESP8266主体流程要怎么运行这个过程最好不要有阻塞(就是延时了)这样会影响其它部分代码的运行 2、WIFI的名称和密码以及热点名称和密码要如何设置保存方式下比较耗时的 3、如何确定当前的网络状态以及不同的状态要执行什么动作比如WIFI突然断开了怎么办 4、作为TCP客户端连接时候如何确保连接成功并且不会重复连接 5、如何解决TCP本质上已经断开了但是模块没有提示的问题即假连接此时没法收发数据的 6、ESP8266最多只有5个连接资源客户端和服务端如何分配 7、如何处理AT指令返回的信息。 针对以上提出的一些问题通过代码分析进行逐一解答。
四、代码分析 4.1 状态机制 首先从整体思路上来讲就是利用状态机的方式去执行不同的网络状态下的动作也就是C语言里的switch语句了这里定义了下图所示的一些状态具体有注释。 然后就是设计不同状态下的动作了也就是网络注册过程这里使用switch语句进行状态跳转函数内部间隔运行时间wait_time可以自定义正常是2秒。 从起始状态开始配置一些固定的参数比如STAAP两种模式都启用上电自动连接WIFI以及配置WIFI的用户名和密码这里参数都是存储到模块的内部FLASH的比较耗时所以热点AP的用户名和密码到下一个状态去设置。设置完后再次复位下模块进入初始化阶段。 /* 描述 : 网络注册函数
输入 :
输出 : */
void drv_esp8266_reg_process(void)
{static u32 last_sec_time0, wait_time2;static char cmd_buff[100]{0};u32 now_sec_timedrv_get_sec_counter();if(now_sec_time-last_sec_timewait_time){switch(g_sEsp8266Work.state){case ESP8266_STATE_START:{delay_os(2000);drv_esp8266_uart_send(ATE0\r\n);delay_os(200); drv_esp8266_send_at(CWMODE_DEF3);//WiFi模式 STAAPdelay_os(200); drv_esp8266_send_at(CWAUTOCONN1);//上电自动连接delay_os(200); if(strlen(g_sEsp8266Work.sta_ssid)0){sprintf(cmd_buff, CWJAP_DEF\%s\,\%s\, g_sEsp8266Work.sta_ssid, g_sEsp8266Work.sta_passwd);drv_esp8266_send_at(cmd_buff);delay_os(1000); } drv_esp8266_send_at(RST);//复位模块 g_sEsp8266Work.stateESP8266_INIT; wait_time3; break;}case ESP8266_INIT:{drv_esp8266_uart_send(ATE0\r\n);delay_os(200); if(strlen(g_sEsp8266Work.ap_ssid)0){sprintf(cmd_buff, CWSAP_DEF\%s\,\%s\,5,3,4,0, g_sEsp8266Work.ap_ssid, g_sEsp8266Work.ap_passwd);drv_esp8266_send_at(cmd_buff);delay_os(1000); } wait_time3;g_sEsp8266Work.stateESP8266_WIFI_CONNECT; break;} case ESP8266_WIFI_CONNECT://等待WIFI连接成功{drv_esp8266_send_at(CIPSTATUS);//查询网络连接信息wait_time5;break;}case ESP8266_NET_CFG://网络配置{printf(### ESP8266_NET_CFG\n);drv_esp8266_send_at(CIPMODE0);//非透传模式delay_os(200); drv_esp8266_send_at(CIPMUX1);//使能多连接delay_os(200);if(g_sEsp8266Work.listen_port0){sprintf(cmd_buff, CIPSERVER1,%d, g_sEsp8266Work.listen_port);drv_esp8266_send_at(cmd_buff); //建立TCP服务器delay_os(200); } g_sEsp8266Work.stateESP8266_STATE_OK; wait_time2;break;} case ESP8266_STATE_OK:{drv_esp8266_connect_process();drv_esp8266_send_at(CIPSTATUS);//查询网络连接信息wait_time5;break;} }last_sec_timedrv_get_sec_counter();}
} 初始化阶段主要设置热点AP的用户名和密码剩下的就是等待模块自己连接上路由器了如果没有指定名称的路由器那就只能一直在这里等待了。在这期间驱动会主动去查询网络状态即drv_esp8266_send_at(CIPSTATUS)我们需要根据模块的返回信息自己去判断网络状态具体手册说明和代码解析如下图所示。 WIFI连接成功后就是配置一些网络信息了在这里根据自己的需求配置了非透传模式、多连接和建立服务器三个内容。 至此整个网络注册流程也就完成了最后就是间隔查询网络状态如果变化去做相对应的动作就行了。 4.2 客户端连接 网络可以用后最麻烦的还是TCP的连接了首先要说明的是客户端连接的结构体定义如下所示。ESP8266最多只有5个连接我们这里就定义了5个客户端的数组结构体内部除了必要的网络信息外还有连接状态、心跳周期和保活时间等参数这是为了连接的稳定而设计了当网络因为不可控因素断开后模块又没有具体返回信息这时可以根据应用层的保活时间来判断是否需要重新连接。 具体代码如下有连接需求的就进行连接操作在这里先对模块的返回信息做个及时处理这样多个连接时才不会分不清是哪个连接的返回信息比如ALREADY CONNECTED信息它没有具体的识别参数。 /* 描述 : 客户端连接管理任务
输入 :
输出 : */
void drv_esp8266_connect_process(void)
{u32 now_sec_timedrv_get_sec_counter();for(u8 i0; iMAX_LINK_NUM; i){Esp8266ClientStruct *pClientg_sEsp8266Work.client_list[i];if(pClient-dst_port0)//有连接需求{if(pClient-conn_state0){drv_esp8266_client_connect(pClient-sock_id, pClient-type, pClient-dst_addr, pClient-dst_port);delay_os(1000);char *pData(char*)g_sEsp8266Work.pUART-pBuff;
// printf(***8266 recv%s\n, pData); if(strstr(pData, ALREADY CONNECTED)!NULL){printf(sock_id%d, already connected!\n, i);pClient-conn_state1;pClient-keep_timenow_sec_time;UART_Clear(g_sEsp8266Work.pUART);//清理串口数据 }else if(strstr(pData, ,CONNECT)!NULL){printf(sock_id%d, new connected!\n, i);pClient-conn_state1;pClient-keep_timenow_sec_time;UART_Clear(g_sEsp8266Work.pUART);//清理串口数据 }else if(strstr(pData, ,CLOSED)!NULL){printf(sock_id%d, error close!\n, i);pClient-conn_state0;UART_Clear(g_sEsp8266Work.pUART);//清理串口数据 } }else{int det_timenow_sec_time-pClient-keep_time;if(det_timepClient-heart_time)//心跳超时{printf(sock_id%d, heart time out!\n, i);drv_esp8266_close(pClient-sock_id);}}} } } 同时已经连接成功的就要检测是否保活超时超时就要重连了保活的keep_time在收到数据时都会自动更新为最新时间。 这个连接函数是在网络状态正常的状态下调用的调用间隔是5秒。 4.3 应用数据接收处理 数据接收部分首先涉及的就是UART篇章的串口接收了首先也是利用接收长度判断是否接收完成完了之后通过关键字IPD,判断是否为接收的数据随即一步步解析连接ID和数据长度最后就可以把应用层数据拿去接收处理了。 这里要重点讲下接收处理函数它的定义如下图所示属于回调函数这样做的好处是应用层可以根据具体需求设计自己的处理函数保证了驱动程序的通用性。 净化器这个项目的WIFI接收处理函数是在应用层的MQTT文件内注册的具体如下因为我们在应用层是采用ESP8266的网络连接ID3来建立MQTT连接的所以这里在app_esp8266_recv函数中把连接ID为3的数据保存进MQTT的环形缓冲区内这个环形缓冲区是MQTT使用的内容这里暂时给他理解成一个缓存空间即可。至于后续怎么处理那是MQTT的事情了至此ESP8266的数据接收任务也就完成了。 4.4 数据发送 ESP8266的数据发送较为简单先发送相关AT指令然后立即发送数据内容即可。 4.5 主函数调用 剩下的就是提供运行主程序供应用层调用就行了这里的运行节奏比较快正常20ms运行一次。 4.6 网络连接ID分配 细心的同学会发现这个项目我的连接ID是3不是从0开始这是为什么呢因为我们模块是有热点的可以作为服务器那么其它设备连接我们的时候模块内部会自动占用一个连接ID而且正常是从0开始的如果有作为服务器的需求那么就要提前做好准备自己的使用的连接ID最好从高位开始ESP8266的连接ID是0~4总的5个为了保险起见我们可以选择3或4这样就比较不会冲突了。这里的核心还是开发者自己要提前规划好使用的连接ID。
五、总结 ESP8266总体来讲不复杂就是细节比较多驱动程序要做到稳定好用不容易像使用AT指令的通讯模块都有这个特点比如以后可能会用到的4G模块那个相对更复杂些。 在这里主要是想通过代码解读的方式让大家理解背后的设计思想既然是以项目为中心的教程那稳定通用是我们考虑的主要因素在其它教程应该比较少有考虑到后续问题大多只是带大家连个阿里云或者其他什么平台就完事了对网络不稳定、连接意外断开、代码阻塞等问题都没有过多说明这里主要就是要让大家学会技术以外的设计思想。 本项目的交流QQ群:701889554 写于2024-3-31