物联网实战--入门篇之(六)嵌入式-WIFI驱动(ESP8266)

4.4 数据发送

4.5 主函数调用

4.6 网络连接ID分配

五、总结


一、WIFI简介

WIFI在我们生活中太常见了,手机电脑都可以用WiFi连接路由器进行上网,那么在单片机领域又是基于什么物理器件联网的呢?最常见的WIFI模块是ESP8266,以及性能更好的ESP32,还有比较新的BL602等等,种类比较多,那么我们净化器这个项目选择的是成熟稳定且便宜的ESP8266。它可以标准模式下连接路由器,自身也可以作为热点供别人连接,性能还是很强悍的。

ESP8266文档中心在这儿ESP8266文档中心 | 安信可科技,在这里我们采用AT指令的方式对齐进行驱动,具体文档可以按下图方式下载。AT指令是一个比较规范的底层通讯协议,也没什么神秘的,就是一个比较固定的格式,AT+具体指令=参数  这种模式,AT指令的好处是简单易懂,字符串的形式比较明了,对应的缺点就是没有很灵活,要根据输出内容处理字符串信息,有时候返回的信息不充分或者不完整,对开发人员的程序稳定性有一定的考验。

二、基础网络知识

这里简要说明下网络的基础知识,主要协议分为TCP和UDP,TCP是比较可靠的连接,数据包会有重发机制,发送方没收到确认就会重新发送,而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秒。

从起始状态开始,配置一些固定的参数,比如STA+AP两种模式都启用,上电自动连接WIFI以及配置WIFI的用户名和密码,这里参数都是存储到模块的内部FLASH的,比较耗时,所以热点AP的用户名和密码到下一个状态去设置。设置完后再次复位下模块,进入初始化阶段。


/*		
================================================================================
描述 : 网络注册函数
输入 : 
输出 : 
================================================================================
*/
void drv_esp8266_reg_process(void)
{
	static u32 last_sec_time=0, wait_time=2;
    static char cmd_buff[100]={0};
	u32 now_sec_time=drv_get_sec_counter();
	if(now_sec_time-last_sec_time>wait_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_DEF=3");//WiFi模式  STA+AP
				delay_os(200); 
				drv_esp8266_send_at("CWAUTOCONN=1");//上电自动连接
				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.state=ESP8266_INIT;		
        wait_time=3;			
				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_time=3;
				g_sEsp8266Work.state=ESP8266_WIFI_CONNECT;			
				break;
			}          
			case ESP8266_WIFI_CONNECT://等待WIFI连接成功
			{
        drv_esp8266_send_at("CIPSTATUS");//查询网络连接信息
				wait_time=5;
        break;
			}
			case ESP8266_NET_CFG://网络配置
			{
        printf("### ESP8266_NET_CFG\n");
        drv_esp8266_send_at("CIPMODE=0");//非透传模式
        delay_os(200);    
        
				drv_esp8266_send_at("CIPMUX=1");//使能多连接
				delay_os(200);
        if(g_sEsp8266Work.listen_port>0)
        {
          sprintf(cmd_buff, "CIPSERVER=1,%d", g_sEsp8266Work.listen_port);
          drv_esp8266_send_at(cmd_buff);	 //建立TCP服务器
          delay_os(200);             
        }       
     
        g_sEsp8266Work.state=ESP8266_STATE_OK;	
				wait_time=2;
        break;
			}      
			case ESP8266_STATE_OK:
			{
        drv_esp8266_connect_process();
        drv_esp8266_send_at("CIPSTATUS");//查询网络连接信息
				wait_time=5;
        break;
			}      
		}
		last_sec_time=drv_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_time=drv_get_sec_counter();
  for(u8 i=0; i<MAX_LINK_NUM; i++)
  {
    Esp8266ClientStruct *pClient=&g_sEsp8266Work.client_list[i];
    if(pClient->dst_port>0)//有连接需求
    {
      if(pClient->conn_state==0)
      {
        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_state=1;
          pClient->keep_time=now_sec_time;
          UART_Clear(g_sEsp8266Work.pUART);//清理串口数据     
        }
        else if(strstr(pData, ",CONNECT")!=NULL)
## 最后

**自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。**

**深知大多数Java工程师,想要提升技能,往往是自己摸索成长,自己不成体系的自学效果低效漫长且无助。**

**因此收集整理了一份《2024年嵌入式&物联网开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。**

![img](https://img-blog.csdnimg.cn/img_convert/1724c49b6491aa34d836a1d8d621b349.png)

![img](https://img-blog.csdnimg.cn/img_convert/f04ae03af34c9f25a210efa5a54c4a9c.jpeg)

![img](https://img-blog.csdnimg.cn/img_convert/16e5bb338af11b163b01ce2bac192e04.png)

 ![img](https://img-blog.csdnimg.cn/img_convert/fd43fe6f4c5fa13e2b749bf7bdc3e751.png)

![img](https://img-blog.csdnimg.cn/img_convert/06b668f18e4df7cb2b0a523cfa194cab.png)

![img](https://img-blog.csdnimg.cn/img_convert/7f0b9654ea402728ca1c423116376407.png)

![](https://img-blog.csdnimg.cn/img_convert/35e2403f024a72eaee1ca5fa1a2fc53b.png)

 

**既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上嵌入式&物联网开发知识点,真正体系化!**

[**如果你觉得这些内容对你有帮助,需要这份全套学习资料的朋友可以戳我获取!!**](https://bbs.csdn.net/topics/618654289)

**由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新**!!


链图片转存中...(img-xDVOlLal-1715761292854)]

 

**既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上嵌入式&物联网开发知识点,真正体系化!**

[**如果你觉得这些内容对你有帮助,需要这份全套学习资料的朋友可以戳我获取!!**](https://bbs.csdn.net/topics/618654289)

**由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新**!!


  • 4
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值