精华内容
下载资源
问答
  • wifi怎么配
    千次阅读 热门讨论
    2021-10-13 19:08:05

    蓝牙配网:

    顾名思义,通过蓝牙给WIFI配网,过程相对较为简单,首先设备上具有WIFI和蓝牙两种功能。然后通过手机的小程序或者APP连接产品蓝牙,连接之后发送WIFI连接所需要的SSID和PASSID。产品接收到蓝牙信息后,产品WIFI通过SSID和PASSID去进行WIFI的连接。

    流程示意图:

    WIFI自身配网:

    此过程也比较简单,首先设置当前WIFI为AP+STA模式。然后通过手机的小程序或者APP连接上产品在AP模式下的WIFI。连接之后发送产品需要连接的WIFI的SSID和PASSID。产品接收到SSID和PASSID信息后,产品WIFI通过SSID和PASSID去进行WIFI的连接。

    流程示意图:

     SmartConfig一键配网:

    1、手机的APP或者小程序先连接上一个现场的局域网网络也就是一个WIFI。

     2、被配网的设备需要开启混杂模式混杂模式又叫偷听模式,允许节点接收它能侦听到的所有数据包。

    3、手机的APP或者小程序通过连接的局域网WIFI,进行UDP数据的广播或者组播进行一个循环发送,数据包里面包含了需要连接WIFI的SSID和PASSID。

    4、被配网的设备通过 UDP 包(长度)获取配置信息捕捉到 需要连接WIFI的SSID和PASSID,最后连接路由器。(广播根据 UDP 包长度,组播根据 IP 地址信息)

     流程示意图:

    知识扩展:

     1、TCP和UDP连接过程:

    TCP编程的服务器端一般步骤是:
    1、创建一个socket,用函数socket(); 
    2、设置socket属性,用函数setsockopt(); * 可选 
    3、绑定IP地址、端口等信息到socket上,用函数bind(); 
    4、开启监听,用函数listen(); 
    5、接收客户端上来的连接,用函数accept(); 
    6、收发数据,用函数send()和recv(),或者read()和write(); 
    7、关闭网络连接; 
    8、关闭监听; 

    TCP编程的客户端一般步骤是: 
    1、创建一个socket,用函数socket(); 
    2、设置socket属性,用函数setsockopt();* 可选 
    3、绑定IP地址、端口等信息到socket上,用函数bind();* 可选 
    4、设置要连接的对方的IP地址和端口等属性; 
    5、连接服务器,用函数connect(); 
    6、收发数据,用函数send()和recv(),或者read()和write(); 
    7、关闭网络连接;


    UDP编程的服务器端一般步骤是: 
    1、创建一个socket,用函数socket(); 
    2、设置socket属性,用函数setsockopt();* 可选 
    3、绑定IP地址、端口等信息到socket上,用函数bind(); 
    4、循环接收数据,用函数recvfrom(); 
    5、关闭网络连接; 

    UDP编程的客户端一般步骤是: 
    1、创建一个socket,用函数socket(); 
    2、设置socket属性,用函数setsockopt();* 可选 
    3、绑定IP地址、端口等信息到socket上,用函数bind();* 可选 
    4、设置对方的IP地址和端口等属性; 
    5、发送数据,用函数sendto(); 
    6、关闭网络连接;

    2、单播、广播、组播

    1、单播:单台主机与单台主机之间的通信;

    2、广播:单台主机与网络中所有主机的通信;

    3、组播(多播):单台主机与选定的一组主机的通信;

     多播首先要知道的是只有UDP有多播,没有TCP多播。因为多播的重点是高效的把同一个包尽可能多的发送到不同的,甚至可能是未知的设备。但是TCP连接可能要求丢包重发或者延时或重组顺序,这些操作可能非常消耗资源,不适于许多使用多播的应用场景。(同时多播不知道发出的包是不是已经到达,这个也导致不能使用TCP)。

    同样广播也是属于UDP,TCP属于端对端的通信,通信要求比较严格。

    更多相关内容
  • esp32 wifi配网 stm32

    2022-06-23 15:38:30
    esp32 web网。通过连接esp32wifi,输入esp32要连接的wifi名称和密码进行网。
  • 每次断网时,通过串口捕捉到串口没有收到wifi模块的AT应答信息,初步论断认为是wifi固件没有回应答导致网络断网。下面一起来看看
  • 参考:智能设备WIFI配网方式汇总 地址:https://blog.csdn.net/beauytlife_1985/article/details/87539350?spm=1001.2014.3001.5502 当前很多物联网设备大都无没有人机交互界面,也就没有像手机或者PC那样有wifi的...

    参考:智能设备WIFI配网方式汇总
    地址:https://blog.csdn.net/beauytlife_1985/article/details/87539350?spm=1001.2014.3001.5502
    参考:Wifi设备配网问题
    地址:https://blog.csdn.net/boazheng/article/details/90906184

    概述

    当前很多物联网设备大都无没有人机交互界面,也就没有像手机或者PC那样有wifi的配置界面,汇总了一下设备入网的方式大概有如下几种:

    1:AP 模式流程如下

    1. 将Dev手动设置为AP模式,手机为正常的station模式
    2. 手机连接到Dev的AP热点,组成局域网,手机将WiFi的SSID和密码发送给Dev
    3. Dev收到WiFi的SSID和密码后,切换回station模式,连接WiFi

    在这里插入图片描述

    2:一键配置模式:即为通常所说的smartconfig 或者smartlink具体流程如下

    1. 手机正常连接家庭局域网WiFi
    2. 通过Dev按钮将设备的WiFi模块设置到smartconfig模式,开始监听局域网广播数据包
    3. 登录设备对应的APP,输入WiFi的SSID和密码(APP如果有第三方开发需要WiFi模组提供相关封装好的SDK)
    4. APP将WiFi的SSID和密码封装为UDP数据包,并在局域网内广播
    5. Dev收到该数据包后,解析出WiFi相关信息
    6. 手机正常入网获取到IP地址,将IP地址Mac地址等信息返回给APP

    3:蓝牙模式:Dev启动开启蓝牙,APP连接设备蓝牙,输入WiFi的SSID和密码并发送给Dev
    4:语音方式:Dev包含语音识别模块,用户根据语音提示读出 WiFi的SSID和密码
    5:Web方式:类似路由器配置很少用
    6:AT命令方式:SSID和密码作为AT命令的参数,直接配置,简单可靠,但不适合普通用户使用

    WiFi模块在出厂时一般都支持smartlink和AP 模式,但是AP设置比较复杂,使用较少,当前常用的是第一键配置模式,但是同时会保留AP模式作为备用
    一键配置模式在实际应用中各个WiFi模组提供商对广播数据包的封装格式以及数据包交互流程存在一些差异,没有完全统一。所以在使用不同厂家的的WiFi模组开发时,对于APP开发需要调用相应的smartlink相关SDK来封装和发送WiFi的SSID和密码,从而实现Dev入网一键配置功能。

    配网与绑定概念

    【Wifi配网概念】:Wifi设备需要连接上路由器(也就是Wifi热点,又称作AP)才能上网,实现数据上报和远程控制。我们将Wifi设备获取Wifi热点的名字和密码的过程称做配网。

    【Wifi设备绑定概念】:Wifi设备有唯一的标识码MAC地址(类似身份证,一人一个,不重复),用户的账号需要在服务器绑定这个设备的MAC地址后,才有权限在账号下进行控制。我们将手机APP获取设备MAC地址,再将其与用户手机APP账号一起发到服务器绑定的过程称作绑定设备。

    只要完成这2个步骤,我们的Wifi设备就可以配网绑定成功,用户就可以正常使用了。

    相反,如果用户添加设备失败,基本就是在这2个环节上出现问题。

    Wifi配网方式介绍

    由于配网失败导致退货的问题一直是Wifi设备的一个痛点,所以各大厂家一直在研究Wifi配网技术。

    目前配网技术有以下几种

    一键配网(Smartconfig)

    设备热点配网(Soft AP)

    蓝牙配网(Bleconfig)

    零配(Zeroconfig)

    手机热点配网(Phone as AP)

    路由器配网(WPS)

    下面是配网方式的简图:

    在这里插入图片描述

    一键配网(Smartconfig)

    在这里插入图片描述
    如果你想知道你家或者你的客户家的Wifi设备为什么配不上网,只要了解罪魁祸首——一键配网技术就行。

    因为目前除了小米米家的产品,这个一键配网的技术基本占据了市面上90%以上的Wifi设备,各大物联网平台像京东微联、阿里小智、阿里飞燕、苏宁智能、华为hilink、微信硬件、 各大第三方物联网平台APP、各大模组厂家都标配一键配网技术,也是制造业企业踩坑的重要原因。

    当然很多物联网平台在客户一键配网失败之后,会让客户使用其他配网方式来解决配网成功率问题。但是一键配网依旧是平台让用户首选的配网方式。

    【技术原理】

    一键配网原理是通过手机或路由器发送UDP广播包的形式,将路由器的名字和密码广播出去。Wifi设备在进入配网的时候,其实就是在空中抓取广播包,抓到包之后解析就可以获取到路由器的名字和密码,然后连接上路由器。

    Wifi设备连接上路由器之后,会在局域网内广播自己的MAC地址,由于手机也是连接着同一台路由器,所以手机APP可以接收到Wifi设备在局域网下面的广播,进而获取到Wifi设备的MAC地址。然后将MAC地址和用户账号发送到服务器进行绑定。

    【问题点】

    1.很多路由器不支持UDP广播功能,例如wifi放大器、或者一些默认关闭UDP广播的路由器,会导致发广播包失败,导致WIFI设备无法获取到广播包。

    2.当5G和2.4G同频的时候,如果当前手机连接的是5G频段,那发出来的广播包是5G的,而目前所有WIFI设备都只支持2.4G,导致无法获取广播包。

    3.如果是2018年以前做的WIFI产品可能还会遇到解包错误等问题,就是WIFI模块获取到数据包了,但是解析出来的密码错误,导致无法链接上路由器。例如微信airkiss就是2016年的产品,跟后面新出的smartconfig对比,成功率还要差上一截。

    4.就算经历千辛万险获取到路由器名字和密码,很多路由器由于不支持局域网通讯(例如路由器的访客网络),或者当前环境下面有几个路由器名字都是一样的,导致手机和WIFI设备不是连接在同一个路由器下面,都会导致wifi设备广播的MAC地址无法被手机APP获取到,进而导致绑定失败。

    【解决方法】

    1.已经出货的产品,配网方式基本是换不了的,因为这涉及到Wifi模块、手机APP、服务器三方的更改,可能性极低。

    2.遇到客诉除了让客户排除以上各种情况外,可以让客户将设备拉到另一个环境,用手机设置成与要配网的路由器同个名字密码的Wifi热点,将其配网绑定成功之后,再拿回到要使用的地方。

    3.新产品不要再用一键配网了。

    【很多人会好奇这种有问题的技术是怎么来的?】

    那是因为2014年Wifi设备刚开始普及之前,大家都是使用老的AP配网方式,步骤非常复杂,要7个步骤(这里不细说了)。所以当一键配网技术出现的时候,由于其操作只要1个步骤,用户操作简便,优势明显,所以迅速成为Wifi设备配网的主流。

    而随后这么多年一键配网技术遇到各种投诉,各大物联网平台也推出了“弥补”方式:

    微信硬件在airkiss2.0之后就停掉了微信硬件的发展;京东微联则增加了设备热点配网方式来弥补;阿里智能则增加手机热点配网方式弥补;最新的天猫精灵平台,则用零配的方式来解决配网问题;其他第三方物联网平台的APP则推出的各自的设备热点配网方式给大家选择。

    但是一键配网仍是首选方案,因为很多物联网平台都没有将其他配网技术的体验做得很完善,很多销售也不懂技术,不会引导企业使用好的配网方式。

    这里要称赞下小米米家物联网平台,因为小米米家开始没多久,就把一键配网技术干掉了,所有Wifi设备都是使用设备热点配网或者蓝牙配网,没有一键配网的设备存在。个人觉得小米在物联网的钻研跟其他物联网平台的投入不是一个等级的,这也是小米IOT生态链能发展这么大的原因之一,要不小米智能产品出货量这么大,像其他平台那么高的退货率一定把公司拖垮。

    设备热点配网(Soft AP)

    在这里插入图片描述
    如果你要做新的Wifi产品,建议了解下设备热点配网技术,这是保证不增加成本的前提下,保证基本100%成功率的方法。也是小米米家大部分设备的配网方式。而且2019年又有新的技术突破,可以在体验上媲美一键配网技术。

    【技术原理】

    Wifi设备进入配网状态,实际是变成AP模式,也就是Wifi设备会有一个Wifi热点出来。手机通过连接上Wifi模块的热点,将路由器名字和密码直接发送给Wifi设备,同时从Wifi设备那边拿到MAC地址,然后发送到云端绑定。

    【技术点】

    1.由于是手机直接连接Wifi模块通讯,这个通讯方式配网绑定的过程都没有路由器参与,所以没有路由器兼容性问题,对2.4G/5G同名路由器、同一场景下多个Wifi同名路由器等情况没有影响。

    2.由于手机直接获取MAC地址,所以绑定设备一定要物联网云平台做外网绑定的方式。否则会出现用户输错密码,Wifi设备绑定成功,但是Wifi设备一直无法上线的情况。

    3.所以包括后面讲的其他配网方式,都是需要云服务器配网做绑定接口,否则就不是一个完整的Wifi配网解决方案。

    4.苹果IOS11.0系统以下的手机,无法自动切换热点,需要用户进入Wifi列表页面,自动选择新热点。这是设备热点配网没有普及的原因之一,因为没有一键配网方便。

    5.没有额外的条件和额外的成本增加

    蓝牙配网(Bleconfig)

    在这里插入图片描述
    如果你是土豪,或者你的设备本身就有蓝牙,那可以直接用蓝牙通道来发送和接受配网绑定信息。

    【技术原理】

    跟设备热点方式一样,只是通讯方式从Wifi连接通讯变成用蓝牙通讯

    【技术点】

    1.同设备热点配网技术1.2.3点

    2.配网成功率还要考虑设备蓝牙模块的手机兼容性。

    3.可以直接发现设备,体验方便。

    4.增加蓝牙成本

    5.有兴趣买个小米的床头灯体验下就知道了~

    如果你还想了解其他配网技术,可以接着看看。

    零配(Zeroconfig)

    在这里插入图片描述
    这个代表应用就是天猫精灵的音响了,Wifi设备在进入配网之后,跟天猫精灵说“找队友”或“添加智能设备”,天猫精灵就会将这个Wifi设备配网绑定。

    【技术原理】

    Wifi设备进入配网状态的时候,会将自己的MAC地址通过Sniffer报文的方式发送出去,这个时候路由器下面支持零配的设备(例如天猫精灵),就可以获取到需要配网设备的MAC地址,同时天猫精灵会将自己保存的路由器名字和密码通过Sniffer报文发给设备。同时等待设备连接上网络,进行外网绑定。

    【技术点】

    1.前提是路由器下面需要有一台支持零配的设备。如果没有,这个游戏就没的玩了。所以天猫精灵配网是比较合适,纯手机APP配网不太实际。

    2.这个方式由于零配设备自己保存了路由器的名字和密码,所以减少了用户输入路由器密码的步骤。

    3.总结:除了陪天猫精灵一起玩,手机APP配网目前看应用还不实际。

    手机热点配网(Phone as AP)

    在这里插入图片描述
    这个配网方式我就看过阿里智能做过,叫AHA,部分设备在Smartconfig失败之后60s会进入AHA状态。

    【技术原理】

    将手机设置成一个特定名字和密码的Wifi热点,然后让设备自己连接手机,再发送和接受配网绑定信息。

    【技术点】

    1.实际应用过程,很多用户是不知道怎么设置自己手机热点名字,甚至都不知道怎么打开手机Wifi热点。

    2.所以不适合消费类大规模推广。

    路由器配网(WPS)

    在这里插入图片描述
    【技术原理】

    有些路由器有WPS的按钮,按下这个按钮,同时触发Wifi设备进入WPS配网模式,就可以让Wifi设备连接上路由器了。至于怎么绑定设备嘛,估计还是得通过局域网发现。

    【技术点】

    1.有客户在用这种方式,但是都是些工业和商用场合。

    2.消费类基本没看到这样用。

    3.不是所有的路由器都有WPS功能,不适合消费类大规模推广。

    总结

    1.新产品不要用一键配网(Smartconfig),尽量选用设备热点配网(Soft AP)

    2.剩下简单用一个表格总结下,方便大家对比
    在这里插入图片描述
    (各种Wifi配网技术适用场景优劣势对比)

    ——————

    最后关于Wifi设备联网做几点解释和说明:

    1.这篇文章讲的Wifi设备,都是使用串口Wifi模块方案的设备,这类设备没有显示屏和多余的按钮来输入Wifi路由器名字和密码,所以只能借助手机来完成Wifi配网工作。那些跑安卓、X86的设备,例如POS机这种设备本身有操作界面可以输入SSID和KEY,他们不存在配网的问题。

    2.如果一个物联网方案商只谈Wifi配网的成功率,不谈服务器绑定的成功率,那一定是有问题的。因为实际产品体验是要用户绑定好wifi设备才能算成功,只讲一半的方案,就不是一个完整的物联网方案。所以一个完整的物联网配网方案,是需要云服务器一起参与的。

    3.为方便理解,以上配网流程省略了一些信息,实际通讯交互内容会更多。例如发送MAC地址的时候,其实还会包括很多产品ID、产品秘钥等产品信息。这里简化内容,方便大家理解。

    4.很多客户会问我有没有5G的Wifi模块,可以这样说,目前市面上暂时还没看到大规模量产的5G串口wifi模块,而且暂时也不建议使用,原因有几个:

    a.很多客户找5G的模块的主要原因是国外客户用5G网络,我们2.4G的wifi模块配网失败,导致了客户退货,所以客户希望能找到5G的wifi模块来解决这个问题。但是如上文所说,解决配网问题的本质是要换配网方式,单纯增加5G频段也是解决不了问题的。

    b.有5G频段的路由器都具备2.4G频段,而有2.4G频段的路由器不一定具备5G频段。所以单5G频段的模块,问题一定更多。

    c.如果选择2.4G和5G双模的Wifi模块,这类模块必定成本增加很多,而且需要同时有两个天线,还要再增加额外的成本。而且2个频段在一起的模块,联网逻辑怎么处理,目前还没有看到量产的案例。

    d.5G频段的wifi模块,因为5G的频率高,波长小,衍射能力差,导致5G的Wifi穿墙能力和通讯距离比2.4G要差,所以也不建议使用5G模块。大家如果家里有双频的路由器可以试试看,跟路由器之间隔1-2堵墙,手机可以明显看到5G的热点信号比2.4G要弱。

    结论:暂时不建议使用5Gwifi模块

    展开全文
  • 我们购买智能家居产品后,买回来拆箱后第一件事通常就是给这个新的硬件进行网,所谓网,也...本文将介绍基于 MicroPython 来实现的 ESP32/ESP8266 Wifi配网。 准备工作 在开始代码之前,需要先准备以下: 刷好 M

    我们购买智能家居产品后,买回来拆箱后第一件事通常就是给这个新的硬件进行配网,所谓配网,也就是让这个新的物联网设备联入我们的局域网内,让这个物联网设备可以进行网络通讯。我们在上一篇文章《MicroPython(ESP32/ESP8266) 实现web控制GPIO》中已经了解到了如何使用ESP32和ESP8266通过联网来实现在Web中控制板载的 LED 灯开关。本文将介绍基于 MicroPython 来实现的 ESP32/ESP8266 Wifi配网。

    准备工作

    在开始代码之前,需要先准备以下:

    配网流程

    回想以下我们的智能家居物联网设备,以小米生态圈的设备为例,新设备开箱通电后,一般是打开米家APP,然后搜索到新买的设备,然后需要手动将wifi连接到这个设配上,然后在 APP 中填入 SSID 和 wifi密码信息,等待传输,传输完成后,就算完成配网,在 APP 的界面中就可以看到新的设备了。

    在这里插入图片描述

    配网的流程总结如上图所示。然而我们的使用当中,配网通常只发生在新设备加入或者网络环境改变的时候才需要,正常情况下设备重启,是不需要每一次都要来一次配网操作的。所以一般情况下,在一次配网之后,我们会将我们的Wifi信息保存下来,设备重启后如果有存在的配网信息,会自动直接联网。

    在这里插入图片描述

    而针对我们整个开发版的程序,我们可以在 main.py 执行在开始,就先执行网络检查,然后根据是否成功联网来判断是否需要配网操作,流程如下:

    在这里插入图片描述

    MicroPython Wifi 操作

    上文梳理了整个配网过程的流程。在这个流程中,最开始的步骤就是判断网络是否连接。以下将介绍如何使用 MicroPython 操作开发板的 Wifi。

    我们开发板(ESP32/ESP8266)的wifi有AP和STA模式,AP就是开发版上创建一个热点,其他设备连接到AP上,而STA模式和我们普通的手机电脑使用Wifi联网类似。这里的要点就是我们需要检查STA模式下开发版是否能正常联网,如果不能,我们利用开发板的AP模式,让我们的其他设备连接开发板,把我们局域网Wifi的配置信息告知开发板,从而使开发板能正常联网。

    import network
    wlan_sta = network.WLAN(network.STA_IF)
    wlan_sta.isconnected()
    

    通过调用 isconnected() 函数,可以获取到开发板是否正常联网,如果正常联网,返回结果会是 True 否则为 False

    wlan_sta.scan()
    

    scan() 函数扫描设备附近可以搜索到的 Wifi,会返回一个列表,列表中每一条为可连接wifi的信息。

    [(b'WifiSSID', b'LPw\xb7\xs8\x94', 1, -48, 3, 0),...]
    

    以上是省略了部分信息的返回值,可以看到,每一条记录中有6个信息,它们分别代表了 SSID名称 BSSID(MAC地址) 频道 RSSI信号强度 加密模式 是否隐藏 。其中加密模式,包含了 WEP、WPA-PSK、WPA2-PSK、WPA/WPA2-PSK等。

    接下来,我们就可以尝试连接Wifi。

    wlan_sta.connect('ssid', 'password')
    wlan_sta.isconnected()
    

    如果连接成功,则返回 True 。如果需要断开连接,可以使用 disconnect() 函数。

    wlan_sta.disconnect()
    

    MicroPython AP操作

    完成了 Wifi 连接和检查网络是否正常后,我们开始解决利用 AP 配网的问题。

    先看代码:

    import network
    import socket
    
    wlan_ap = network.WLAN(network.AP_IF)
    wlan_ap.active(True)
    
    
    wlan_ap.config(essid='MyESP8266',authmode=0)
    server_socket = socket.socket()
    server_socket.bind(('0.0.0.0', 80))
    server_socket.listen(3)
    
    def web_page():
        return b"""<html>
                        <head>
                            <title>MYESP8266 AP Test</title>
                        </head>
                        <body>
                            <h1>This is MyESP8266 AP Test Page.</h1>
                        </body>
                    </html>"""
    
    while True:
        conn, addr = server_socket.accept()
        print('Connection: %s ' % str(addr))
        response = web_page()
        conn.send('HTTP/1.1 200 OK\n')
        conn.send('Content-Type: text/html\n')
        conn.send('Connection: close\n\n')
        conn.sendall(response)
        conn.close()
    

    从上面的代码我们可以看到,当我们创建好AP后,就打开一个 socket ,并且绑定80端口开始监听,然后开启一个循环,当接受到连接后就给客户端发送页面代码。如果对 socket 不了解的,可以参考《快速了解Python socket编程》

    这时用手机或者电脑的 wifi 连接 SSID 名为 MYESP8266 的 Wifi 热点,因为我们authmode选择了 open 所以不需要密码。连接成功后,用浏览器打开地址 192.168.4.1 ,就可以看到我们上面的页面。

    针对 MicroPython 的 Web 编程

    我们一般情况下,如果要进行 Web 开发,通常会使用 Flask 或者 Django 之类的框架。而针对开发版这种运算能力有限的硬件,也有对应的框架可以用。但我们这里为了能深入的了解,就通过自己完成最基本的功能来了解整个程序的运行方式。

    封装HTML响应

    根据上面的示例代码,我们可以了解到,如果要在客户端正常显示页面,我们需要先给客户端发一个HTTP的Header信息,然后再发送具体的页面内容。所以,为了方便日后的调用,我们对上面的代码进行修改:

    import network
    import socket
    
    wlan_ap = network.WLAN(network.AP_IF)
    wlan_ap.active(True)
    
    
    wlan_ap.config(essid='MyESP8266',authmode=0)
    server_socket = socket.socket()
    server_socket.bind(('0.0.0.0', 80))
    server_socket.listen(3)
    
    def send_header(conn, status_code=200, content_length=None ):
        conn.sendall("HTTP/1.0 {} OK\r\n".format(status_code))
        conn.sendall("Content-Type: text/html\r\n")
        if content_length is not None:
          conn.sendall("Content-Length: {}\r\n".format(content_length))
        conn.sendall("\r\n")
    
    def send_response(conn, payload, status_code=200):
        content_length = len(payload)
        send_header(conn, status_code, content_length)
        if content_length > 0:
            conn.sendall(payload)
        conn.close()
    
    def config_page():
        return b"""<html>
                        <head>
                            <title>MYESP8266 AP Test</title>
                            <meta charset="UTF-8">
                            <meta name="viewport" content="width=device-width, initial-scale=1">
                        </head>
                        <body>
                            <h1>Wifi 配网</h1>
                            <form action="configure" method="post">
                                <div>
                                    <label>SSID</label>
                                    <input type="text" name="ssid">
                                </div>
                                <div>
                                    <label>PASSWORD</label>
                                    <input type="password" name="password">
                                </div>
                                <input type="submit" value="连接">
                            <form>
                        </body>
                    </html>"""
    
    while True:
        conn, addr = server_socket.accept()
        print('Connection: %s ' % str(addr))
        
        try:
            conn.settimeout(3)
            request = b""
        
            try:
                while "\r\n\r\n" not in request:
                    request += conn.recv(512)
            except OSError:
                pass
    		
    		print(request)
    		
    		response = config_page()
            send_response(conn, response)
    
    	finally:
            conn.close()
    
    
    

    我们添加了三个函数,分别为 send_header() send_response() config_page() 。其中 send_header() 把我们需要发送的 Header 信息打包,config_page() 则是创建我们的 HTML 页面,最后由 send_response() 将其整合,发送给客户端。

    在这里插入图片描述

    运行代码,如果正常,用手机连接开发板的AP,打开 192.168.4.1 ,就可以看到上图的页面。

    路由

    上面的代码中,页面中有一个 form ,里面可以输入 SSID 和 Wifi 密码,当我们输入完成后,点击连接,将会将我们输入的内容 POST 到 /configure 路径中。处理这个问题,在 Web 框架中,会有现成的路由模块,但这里我们需要自己用代码进行处理。

    我们的代码中,当客户端连接后,我们的开发板会接受来自客户端传来的信息——request ,打印这个变量看看客户端传来的内存:

    # 连接 192.168.4.1
    Connection: ('192.168.4.2', 44794) 
    b'GET / HTTP/1.1\r\nUser-Agent: Dalvik/2.1.0 (Linux; U; Android 9; MIX 2 MIUI/20.6.18)\r\nHost: 192.168.4.1\r\nConnection: Keep-Alive\r\nAccept-Encoding: gzip\r\n\r\n'
    
    # 连接 192.168.4.1/test
    b'GET /test HTTP/1.1\r\nHost: 192.168.4.1\r\nConnection: keep-alive\r\nCache-Control: max-age=0\r\nUpgrade-Insecure-Requests: 1\r\nUser-Agent: Mozilla/5.0 (Linux; Android 9; MIX 2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.82 Mobile Safari/537.36\r\nAccept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9\r\nAccept-Encoding: gzip, deflate\r\nAccept-Language: zh-CN,zh;q=0.9,en;q=0.8\r\n\r\n'
    
    # 输入信息,点击连接按钮
    b'POST /configure HTTP/1.1\r\nHost: 192.168.4.1\r\nConnection: keep-alive\r\nContent-Length: 26\r\nCache-Control: max-age=0\r\nUpgrade-Insecure-Requests: 1\r\nOrigin: http://192.168.4.1\r\nContent-Type: application/x-www-form-urlencoded\r\nUser-Agent: Mozilla/5.0 (Linux; Android 9; MIX 2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.82 Mobile Safari/537.36\r\nAccept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9\r\nReferer: http://192.168.4.1/\r\nAccept-Encoding: gzip, deflate\r\nAccept-Language: zh-CN,zh;q=0.9,en;q=0.8\r\n\r\nssid=xdbdh&password=ddjxdj'
    

    可以看到,当我们连接不同的地址,开发板接受到的信息是不同的,我们就可以通过正则表达式来抓去不同的内容即可实现类似 Web 框架路由的功能。

    try:
                url = ure.search("(?:GET|POST) /(.*?)(?:\\?.*?)? HTTP", request).group(1).decode("utf-8").rstrip("/")
            except Exception:
                url = ure.search("(?:GET|POST) /(.*?)(?:\\?.*?)? HTTP", request).group(1).rstrip("/")
            print("URL is {}".format(url))
    
    

    我们将上面 print() 函数替换乘上面的代码,再尝试上面三个地址:

    # 连接 192.168.4.1
    URL is 
    # 连接 192.168.4.1/test
    URL is test
    # 输入信息,点击连接按钮
    URL is configure
    

    这样,我们的精简版路由功能就完成了。

    POST 传参获取

    解决了页面显示和路由,剩下就是如何获取 POST 的传参了。我们再看一次当我们使用 POST 时,返回过来的信息:

    b'POST /configure HTTP/1.1\r\n
    Host: 192.168.4.1\r\n
    Connection: keep-alive\r\n
    Content-Length: 26\r\n
    Cache-Control: max-age=0\r\n
    Upgrade-Insecure-Requests: 1\r\n
    Origin: http://192.168.4.1\r\n
    Content-Type: application/x-www-form-urlencoded\r\nUser-Agent: Mozilla/5.0 (Linux; Android 9; MIX 2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.82 Mobile Safari/537.36\r\nAccept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9\r\nReferer: http://192.168.4.1/\r\n
    Accept-Encoding: gzip, deflate\r\nAccept-Language: zh-CN,zh;q=0.9,en;q=0.8\r\n\r\n
    ssid=xdbdh&password=ddjxdj'
    

    可以看到,信息开头是 POST 方法,然后结 ssid=....&password=... 就是我们传过来的参数,和处理路由的方法类似,我们使用正则表达式过滤一下,即可获取到我们需要的 ssid 和 Wifi 密码了。

    # POST 参数解析
    def get_wifi_conf(request):
        match = ure.search("ssid=([^&]*)&password=(.*)", request)
        
        if match is None:
            return False
        
        try:
            ssid = match.group(1).decode("utf-8").replace("%3F", "?").replace("%21", "!")
            password = match.group(2).decode("utf-8").replace("%3F", "?").replace("%21", "!")
        except Exception:
            ssid = match.group(1).replace("%3F", "?").replace("%21", "!")
            password = match.group(2).replace("%3F", "?").replace("%21", "!")
    
        if len(ssid) == 0:
            return False
        return (ssid, password)
    
    

    我们再修改一下代码,添加一个新页面,用来显示 ssid 和 Wifi 密码,来确认我们的路由功能和 POST 参数正常获取。

    def wifi_conf_page(ssid, passwd):
        return b"""<html>
                        <head>
                            <title>Wifi Conf Info</title>
                            <meta charset="UTF-8">
                            <meta name="viewport" content="width=device-width, initial-scale=1">
                        </head>
                        <body>
                            <h1>Post data:</h1>
                            <p>SSID: %s</p>
                            <p>PASSWD: %s</p>
                            <a href="/">Return Configure Page</a>
                        </body>
                    </html>""" % (ssid, passwd)
    

    修改后的代码:

    # 前面相同的部分省略
    while True:
        conn, addr = server_socket.accept()
        print('Connection: %s ' % str(addr))
        
        try:
            conn.settimeout(3)
            request = b""
        
            try:
                while "\r\n\r\n" not in request:
                    request += conn.recv(512)
            except OSError:
                pass
        
            # url process
            try:
                url = ure.search("(?:GET|POST) /(.*?)(?:\\?.*?)? HTTP", request).group(1).decode("utf-8").rstrip("/")
            except Exception:
                url = ure.search("(?:GET|POST) /(.*?)(?:\\?.*?)? HTTP", request).group(1).rstrip("/")
            print("URL is {}".format(url))
            
            if url == "":
                response = config_page()
                send_response(conn, response)
            elif url == "configure":
                ret = get_wifi_conf(request)
                response = wifi_conf_page(ret[0], ret[1])
                send_response(conn, response)
            
        finally:
            conn.close()
    

    执行代码,输入 ssid 和密码后,点击连接,应该能跳转到新页面并且显示刚才输入的 ssid 和密码。点击返回,能重新跳回信息输入的页面。

    在这里插入图片描述

    Wifi连接

    《MicroPython(ESP32/ESP8266) 实现web控制GPIO》中,我们已经介绍了如何通过 connect() 方法来连接我们已知的 Wifi。接下来,我们要做的也很简单,就是创建一个 do_connect() 方法来处理我们上面传过来的 ssid 和密码。

    def do_connect(ssid, password):
        wlan_sta.active(True)
        if wlan_sta.isconnected():
            return None
        print('Connect to %s' % ssid)
        wlan_sta.connect(ssid, password)
        for retry in range(100):
            connected = wlan_sta.isconnected()
            if connected:
                break
            time.sleep(0.1)
            print('.', end='')
        if connected:
            print('\nConnected : ', wlan_sta.ifconfig())
        else:
            print('\nFailed. Not Connected to: ' + ssid)
        return connected
    

    可以看到,这个函数会接受传来的 wifi 配置参数,进行连接,如果成功,会返回 True。然后我们还还需要一个执行连接的方法,这个方法用于连接成功,就自动获取连如局域网后的ip地址。

    def handle_wifi_configure(ssid, password):
        if do_connect(ssid, password):
            new_ip = wlan_sta.ifconfig()[0]
            return new_ip
        else:
            print('connect fail')
            return False
    

    这些都完成后,我们只需要把开发板 AP 联网配置部分封装好,成为一个 start_ap() 方法,即可:

    # response 的方法都为创建 HTML 代码方法,这里省略
    # 可以在文末完整代码中查看
    def startAP():
        global server_socket
        stop()
        wlan_ap.active(True)
        wlan_ap.config(essid='MyEsp8266',authmode=0)
        
        server_socket = socket.socket()
        server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        server_socket.bind(('0.0.0.0', 80))
        server_socket.listen(3)
        
        
        while not wlan_sta.isconnected():
            conn, addr = server_socket.accept()
            print('Connection: %s ' % str(addr))
        
            try:
                conn.settimeout(3)
                request = b""
        
                try:
                    while "\r\n\r\n" not in request:
                        request += conn.recv(512)
                except OSError:
                    pass
        
                # url process
                try:
                    url = ure.search("(?:GET|POST) /(.*?)(?:\\?.*?)? HTTP", request).group(1).decode("utf-8").rstrip("/")
                except Exception:
                    url = ure.search("(?:GET|POST) /(.*?)(?:\\?.*?)? HTTP", request).group(1).rstrip("/")
                print("URL is {}".format(url))
            
                if url == "":
                    response = config_page()
                    send_response(conn, response)
                elif url == "configure":
                    ret = get_wifi_conf(request)
                    ret = handle_wifi_configure(ret[0], ret[1])
                    if ret is not None:
                        response = connect_sucess(ret)
                        send_response(conn, response)
                        print('connect sucess')
                elif url == "disconnect":
                    wlan_sta.disconnect()
            
            finally:
                conn.close()
        wlan_ap.active(False)
        print('ap exit')
    

    这里我们实现的功能为让开发板创建AP,生成一个 Wifi 信息的配置页面,然后通过路由来处理输入和参数,最后执行 Wifi 联网,如果连接成功,即退出循环,关闭 AP 热点。

    我们从用手机输入完成点击连接后,如果连接成功,将会自动返回成功连接的页面:

    在这里插入图片描述

    到这里,我们的 wifi 配网就已经基本完成了。

    总结

    本文开始先从配网的需求、流程进行分析,然后一步步分别介绍 MicroPython Wifi的操作,AP的使用以及简单的 Web 实现,然后将上述的要点结合我们的配网需求,完成完整的设配配网代码开发。

    但是,文章为了比较清晰的展示内容,因此在代码上可能会显得比较冗长和繁复,有很大的优化空间。大家可以根据自己的实际情况,对代码进行进一步的优化和调整,以下给出几个可以调整方法:

    • 优化代码结构,模块化部分功能

    • 将 web 部分整合成一个模块,比如带有 html 模板渲染功能的模块、路由模块灯

    • 尝试在用户体验上优化配网的流程

    • 其他创新的需求等……

    此外,还存还存在一个问题,就是可能因为 ESP8266 的内存和算力问题,代码运行的时候有时会出错和跳出,需要重启或者断电,但同样的代码在 ESP32 开发板上,却没有问题。可能是 MircoPython 的问题,也有可能是因为代码设计问题,这方面需要进一步研究和尝试。

    物联网开发涉及到很多硬件和软件的问题,但是在实践中,经常会遇到各种奇怪的问题,这很可能打击了学习的热情,加上网上的教程和示例不多,初学者更容易遇到问题解决不了而不得不放弃。本文尽可能的详细解释代码和原理,但由于水平经验有限,难免会有所疏漏, 望读者见谅,并且欢迎大家一起来交流进步。

    希望本文对你有用。如果你觉得文章对你用,记得关注收藏。你的关注和收藏是继续更新的动力哦。

    附:完整代码

    import network
    import socket
    import ure
    import time
    
    NETWORK_PROFILES = 'wifi.dat'
    
    wlan_ap = network.WLAN(network.AP_IF)
    wlan_sta = network.WLAN(network.STA_IF)
    
    server_socket = None
    
    
    def send_header(conn, status_code=200, content_length=None ):
        conn.sendall("HTTP/1.0 {} OK\r\n".format(status_code))
        conn.sendall("Content-Type: text/html\r\n")
        if content_length is not None:
          conn.sendall("Content-Length: {}\r\n".format(content_length))
        conn.sendall("\r\n")
    
    def send_response(conn, payload, status_code=200):
        content_length = len(payload)
        send_header(conn, status_code, content_length)
        if content_length > 0:
            conn.sendall(payload)
        conn.close()
    
    def config_page():
        return b"""<html>
                        <head>
                            <title>MYESP8266 AP Test</title>
                            <meta charset="UTF-8">
                            <meta name="viewport" content="width=device-width, initial-scale=1">
                        </head>
                        <body>
                            <h1>Wifi 配网</h1>
                            <form action="configure" method="post">
                                <div>
                                    <label>SSID</label>
                                    <input type="text" name="ssid">
                                </div>
                                <div>
                                    <label>PASSWORD</label>
                                    <input type="password" name="password">
                                </div>
                                <input type="submit" value="连接">
                            <form>
                        </body>
                    </html>"""
    
    
    def wifi_conf_page(ssid, passwd):
        return b"""<html>
                        <head>
                            <title>Wifi Conf Info</title>
                            <meta charset="UTF-8">
                            <meta name="viewport" content="width=device-width, initial-scale=1">
                        </head>
                        <body>
                            <h1>Post data:</h1>
                            <p>SSID: %s</p>
                            <p>PASSWD: %s</p>
                            <a href="/">Return Configure Page</a>
                        </body>
                    </html>""" % (ssid, passwd)
    
    def connect_sucess(new_ip):
        return b"""<html>
                        <head>
                            <title>Connect Sucess!</title>
                            <meta charset="UTF-8">
                            <meta name="viewport" content="width=device-width, initial-scale=1">
                        </head>
                        <body>
                            <p>Wifi Connect Sucess</p>
                            <p>IP Address: %s</p>
                            <a href="http://%s">Home</a>
                            <a href="/disconnect">Disconnect</a>
                        </body>
                   </html>""" % (new_ip, new_ip)
    
    def get_wifi_conf(request):
        match = ure.search("ssid=([^&]*)&password=(.*)", request)
        
        if match is None:
            return False
        
        try:
            ssid = match.group(1).decode("utf-8").replace("%3F", "?").replace("%21", "!")
            password = match.group(2).decode("utf-8").replace("%3F", "?").replace("%21", "!")
        except Exception:
            ssid = match.group(1).replace("%3F", "?").replace("%21", "!")
            password = match.group(2).replace("%3F", "?").replace("%21", "!")
    
        if len(ssid) == 0:
            return False
        return (ssid, password)
    
    
    def handle_wifi_configure(ssid, password):
        if do_connect(ssid, password):
    #         try:
    #             profiles = read_profiles()
    #         except OSError:
    #             profiles = {}
    #         profiles[ssid] = password
    #         write_profiles(profiles)
    #         
    #         time.sleep(5)
    #         
            new_ip = wlan_sta.ifconfig()[0]
            return new_ip
        else:
            print('connect fail')
            return False
    
    def check_wlan_connected():
        if wlan_sta.isconnected():
            return True
        else:
            return False
        
    def do_connect(ssid, password):
        wlan_sta.active(True)
        if wlan_sta.isconnected():
            return None
        print('Connect to %s' % ssid)
        wlan_sta.connect(ssid, password)
        for retry in range(100):
            connected = wlan_sta.isconnected()
            if connected:
                break
            time.sleep(0.1)
            print('.', end='')
        if connected:
            print('\nConnected : ', wlan_sta.ifconfig())
        else:
            print('\nFailed. Not Connected to: ' + ssid)
        return connected
    
    def read_profiles():
        with open(NETWORK_PROFILES) as f:
            lines = f.readlines()
        profiles = {}
        for line in lines:
            ssid, password = line.strip("\n").split(";")
            profiles[ssid] = password
        return profiles
    
    
    def write_profiles(profiles):
        lines = []
        for ssid, password in profiles.items():
            lines.append("%s;%s\n" % (ssid, password))
        with open(NETWORK_PROFILES, "w") as f:
            f.write(''.join(lines))
          
    def stop():
        global server_socket
        
        if server_socket:
            server_socket.close()
            server_socket = None
    
    def startAP():
        global server_socket
        stop()
        wlan_ap.active(True)
        wlan_ap.config(essid='MyEsp8266',authmode=0)
        
        server_socket = socket.socket()
        server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        server_socket.bind(('0.0.0.0', 80))
        server_socket.listen(3)
        
        
        while not wlan_sta.isconnected():
            conn, addr = server_socket.accept()
            print('Connection: %s ' % str(addr))
        
            try:
                conn.settimeout(3)
                request = b""
        
                try:
                    while "\r\n\r\n" not in request:
                        request += conn.recv(512)
                except OSError:
                    pass
        
                # url process
                try:
                    url = ure.search("(?:GET|POST) /(.*?)(?:\\?.*?)? HTTP", request).group(1).decode("utf-8").rstrip("/")
                except Exception:
                    url = ure.search("(?:GET|POST) /(.*?)(?:\\?.*?)? HTTP", request).group(1).rstrip("/")
                print("URL is {}".format(url))
            
                if url == "":
                    response = config_page()
                    send_response(conn, response)
                elif url == "configure":
                    ret = get_wifi_conf(request)
                    ret = handle_wifi_configure(ret[0], ret[1])
                    if ret is not None:
                        response = connect_sucess(ret)
                        send_response(conn, response)
                        print('connect sucess')
                elif url == "disconnect":
                    wlan_sta.disconnect()
            
            finally:
                conn.close()
        wlan_ap.active(False)
        print('ap exit')
    
    def home():
        global server_socket
        stop()
        wlan_sta.active(True)
        ip_addr = wlan_sta.ifconfig()[0]
        print('wifi connected')
        server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        server_socket.bind(('0.0.0.0', 80))
        server_socket.listen(3)
            
        while check_wlan_connected():
            conn, addr = server_socket.accept()
            try:
                conn.settimeout(3)
                request = b""
                    
                try:
                    while "\r\n\r\n" not in request:
                        request += conn.recv(512)
                except OSError:
                    pass
                    
                # url process
                try:
                    url = ure.search("(?:GET|POST) /(.*?)(?:\\?.*?)? HTTP", request).group(1).decode("utf-8").rstrip("/")
                except Exception:
                    url = ure.search("(?:GET|POST) /(.*?)(?:\\?.*?)? HTTP", request).group(1).rstrip("/")
            
                if url == "":
                    response = connect_sucess(ip_addr)
                    send_response(conn, response)
                elif url == "disconnect":
                    wlan_sta.disconnect()             
            finally:
                conn.close()
                    
        wlan_sta.active(False)
        print('sta exit')
    
    
    def main():
        while True:
            if not check_wlan_connected():
                startAP()
            else:
                home()
                
                    
    main()
    
    展开全文
  • 基于STM32的wifi模块,工程直接编译可以通过,调试实测通信正常,可以适合初学者借鉴。
  • ESP32网,wifi配网模式,可清除以前网信息。移植方便!可改改函数用于无线调参。

    不想了解代码实现,可直接到代码部分copy!ESP32亲测可用。

    使用设备ESP32开发板(ESP32-WROOM-32)

    搜了好多别人写的资料,很多都是copy+copy,也没有什么解释。啪,代码放在那里,自己看吧。

    我不是说他们写的都是垃圾哈 ~      那我承认,我就是这么想的,有的说是教程也啥都没写

    不能怪人家不能怪人家,咱还是自己学!!!

    咱的代码也是copy了再改的,没脸说人家。

    让我自己写的话,那不叫写代码,那叫写bug。

    在学习中进步!!


    正题开始了

    设备热点配网(Soft AP)

    1. ESP32配置为AP模式
    2. 手机or电脑连接ESP32热点,发送WiFi和密码
    3. ESP32设置为STA模式

    很抱歉C++的基础不好,没封装成库的形式,创建类后有一部分函数总是报错,没有解决。

    但是也分成了文件的形式,要是拷贝使用也非常方便。

    说明:LED显示配网状态

    LED闪烁:表示正在尝试连接网络。

    LED常亮:表示网络连接成功

    LED常灭:表示等待配网

    LED闪烁5次:表示已清除wifi信息

    配网状态下ESP32开发板还挺温乎的!


    配网页面

    实话实说,这是我抄的!!

    但是我忘记在哪里抄的了。

     (我的手机截图)


    配网完成界面


    网页的HTML

    我是在这个网站看嫖来的配网界面好不好看的。

    在线的,目前免费,试了一试还挺好用的

    在这个软件中,新建工程复制进去这个代码就能看网页效果了。

    配网界面HTML原代码

    因为我也不懂哈 ~ 有什么错误我可能不知道。但是显示的网页是正常的。

    <!DOCTYPE html>
    <html>
        <head>
            <title>WIFI
                
            </title>
            <meta name="viewport" content="width=device-width, initial-scale=1">
        </head>
        
        <style type="text/css">
            .input{display: block; margin-top: 10px;}
            .input span{width: 100px; float: left; float: left; height: 36px; line-height: 36px;}
            .input input{height: 30px;width: 200px;}
            .btn{width: 120px; height: 35px; background-color: #000000; border:0px; color:#ffffff; margin-top:15px; margin-left:100px;}
        </style>
        
        <body>
            <form method="POST" action="configwifi">
                <label class="input">
                    <span>
                        WiFi SSID  
                    </span>
                    <input type="text" name="ssid">
                </label>
                <label class="input">
                    <span>
                        WiFi PASS
                    </span> 
                    <input type="text"  name="pass">
                </label>
                <input class="btn" type="submit" name="submit" value="Submie">         
                <p>
                    <span> Nearby wifi:
                </P>
            </form>
        </body>
    </html>

    串口打印

    红线上为配网前打印

    红线后为配网后打印

    以上就是代码之外的资料了 ~ 


    现在开始代码分析

    本次工程包含三个文件main.ino、WiFiUser.cpp、WiFiUser.h

    有关配网的函数都在WiFiUser.cpp、WiFiUser.h文件中,可直接添加这两个文件到工程目录下进行调用。

    如果不想学习怎么实现,可直接复制这两个文件,两分钟内实现配网操作!

    由于官方命名原因,ESP8266的库文件需要更改为ESP8266+库文件的形式,但函数可通用。(此条说明未验证)

    • 如果想了解实现原理,需配合串口打印信息来分析。

    WiFiUser.h

    很遗憾没有设置成类的形式,以后要改!!

    在此文件夹下,声明了函数。可以见得,include此文件,只需要在自己写的函数中调用3个函数即可实现配网全操作。5个文件中一个是LED、一个是清除wifi保存在flash的信息、只有三个才是配网所需函数!

    #ifndef __WIFIUSER_H__
    #define __WIFIUSER_H__
    
    #include <WiFi.h>
    #include <DNSServer.h>
    #include <WebServer.h>
    #include <ESPmDNS.h>      //用于设备域名 MDNS.begin("esp32")
    #include <esp_wifi.h>     //用于esp_wifi_restore() 删除保存的wifi信息
    
    extern const int LED;                         //设置LED引脚
    extern const char* HOST_NAME;                 //设置设备名
    extern int connectTimeOut_s;                 //WiFi连接超时时间,单位秒
    
    //===========需要调用的函数===========
    void checkConnect(bool reConnect);    //检测wifi是否已经连接
    void restoreWiFi();                   //删除保存的wifi信息
    void LEDinit();                       //LED初始化
    void checkDNS_HTTP();                 //检测客户端DNS&HTTP请求
    void connectToWiFi(int timeOut_s);    //连接WiFi
    
    //===========内部函数===========
    void handleRoot();                    //处理网站根目录的访问请求
    void handleConfigWifi() ;             //提交数据后的提示页面
    void handleNotFound();                //处理404情况的函数'handleNotFound'
    void initSoftAP();                    //进入AP模式
    void initDNS();                       //开启DNS服务器
    void initWebServer();                 //初始化WebServer
    bool scanWiFi();                      //扫描附近的WiFi,为了显示在配网界面
    void wifiConfig();                    //配置配网功能
    void blinkLED(int led, int n, int t); //LED闪烁函数        //用不上LED可删除
    
    #endif

    WiFiUser.cpp

    这就是所有有关配网的函数的定义了 ~

    这里有一点应该注意的是:HTML放在代码中时在  "  前应该加  \  (转义符)

    #include "WiFiUser.h"
    
    const byte DNS_PORT = 53;                  //设置DNS端口号
    const int webPort = 80;                    //设置Web端口号
    
    const char* AP_SSID  = "ESP32-4_1";        //设置AP热点名称
    //const char* AP_PASS  = "";               //这里不设置设置AP热点密码
    
    const char* HOST_NAME = "MY_ESP32";        //设置设备名
    String scanNetworksID = "";                //用于储存扫描到的WiFi ID
    
    IPAddress apIP(192, 168, 4, 1);            //设置AP的IP地址
    
    String wifi_ssid = "";                     //暂时存储wifi账号密码
    String wifi_pass = "";                     //暂时存储wifi账号密码
    
    const int LED = 2;                         //设置LED引脚
    
    DNSServer dnsServer;                       //创建dnsServer实例
    WebServer server(webPort);                 //开启web服务, 创建TCP SERVER,参数: 端口号,最大连接数
    
    #define ROOT_HTML  "<!DOCTYPE html><html><head><title>WIFI</title><meta name=\"viewport\" content=\"width=device-width, initial-scale=1\"></head><style type=\"text/css\">.input{display: block; margin-top: 10px;}.input span{width: 100px; float: left; float: left; height: 36px; line-height: 36px;}.input input{height: 30px;width: 200px;}.btn{width: 120px; height: 35px; background-color: #000000; border:0px; color:#ffffff; margin-top:15px; margin-left:100px;}</style><body><form method=\"POST\" action=\"configwifi\"><label class=\"input\"><span>WiFi SSID</span><input type=\"text\" name=\"ssid\" value=\"\"></label><label class=\"input\"><span>WiFi PASS</span> <input type=\"text\"  name=\"pass\"></label><input class=\"btn\" type=\"submit\" name=\"submit\" value=\"Submie\"> <p><span> Nearby wifi:</P></form>"
    
    /*
     * 处理网站根目录的访问请求
     */
    void handleRoot() 
    {
      if (server.hasArg("selectSSID")) {
        server.send(200, "text/html", ROOT_HTML + scanNetworksID + "</body></html>");   //scanNetWprksID是扫描到的wifi
      } else {
        server.send(200, "text/html", ROOT_HTML + scanNetworksID + "</body></html>");   
      }
    }
    
    /*
     * 提交数据后的提示页面
     */
    void handleConfigWifi()               //返回http状态
    {
      if (server.hasArg("ssid"))          //判断是否有账号参数
      {
        Serial.print("got ssid:");
        wifi_ssid = server.arg("ssid");   //获取html表单输入框name名为"ssid"的内容
     
        Serial.println(wifi_ssid);
      } 
      else                                //没有参数
      { 
        Serial.println("error, not found ssid");
        server.send(200, "text/html", "<meta charset='UTF-8'>error, not found ssid"); //返回错误页面
        return;
      }
      //密码与账号同理
      if (server.hasArg("pass")) 
      {
        Serial.print("got password:");
        wifi_pass = server.arg("pass");  //获取html表单输入框name名为"pwd"的内容
        Serial.println(wifi_pass);
      } 
      else 
      {
        Serial.println("error, not found password");
        server.send(200, "text/html", "<meta charset='UTF-8'>error, not found password");
        return;
      }
      server.send(200, "text/html", "<meta charset='UTF-8'>SSID:" + wifi_ssid + "<br />password:" + wifi_pass + "<br />已取得WiFi信息,正在尝试连接,请手动关闭此页面。"); //返回保存成功页面
      delay(2000);
      WiFi.softAPdisconnect(true);     //参数设置为true,设备将直接关闭接入点模式,即关闭设备所建立的WiFi网络。
      server.close();                  //关闭web服务
      WiFi.softAPdisconnect();         //在不输入参数的情况下调用该函数,将关闭接入点模式,并将当前配置的AP热点网络名和密码设置为空值.
      Serial.println("WiFi Connect SSID:" + wifi_ssid + "  PASS:" + wifi_pass);
    
      if (WiFi.status() != WL_CONNECTED)    //wifi没有连接成功
      {
        Serial.println("开始调用连接函数connectToWiFi()..");
        connectToWiFi(connectTimeOut_s);
      } 
      else {
        Serial.println("提交的配置信息自动连接成功..");
      }
    }
    
    /*
     * 处理404情况的函数'handleNotFound'
     */
    void handleNotFound()           // 当浏览器请求的网络资源无法在服务器找到时通过此自定义函数处理
    {           
      handleRoot();                 //访问不存在目录则返回配置页面
      //   server.send(404, "text/plain", "404: Not found");
    }
    
    /*
     * 进入AP模式
     */
    void initSoftAP() {
      WiFi.mode(WIFI_AP);                                           //配置为AP模式
      WiFi.softAPConfig(apIP, apIP, IPAddress(255, 255, 255, 0));   //设置AP热点IP和子网掩码
      if (WiFi.softAP(AP_SSID))                                     //开启AP热点,如需要密码则添加第二个参数
      {                           
        //打印相关信息
        Serial.println("ESP-32S SoftAP is right.");
        Serial.print("Soft-AP IP address = ");
        Serial.println(WiFi.softAPIP());                                                //接入点ip
        Serial.println(String("MAC address = ")  + WiFi.softAPmacAddress().c_str());    //接入点mac
      } 
      else                                                  //开启AP热点失败
      { 
        Serial.println("WiFiAP Failed");
        delay(1000);
        Serial.println("restart now...");
        ESP.restart();                                      //重启复位esp32
      }
    }
    
    /*
     * 开启DNS服务器
     */
    void initDNS() 
    {
      if (dnsServer.start(DNS_PORT, "*", apIP))   //判断将所有地址映射到esp32的ip上是否成功
      {
        Serial.println("start dnsserver success.");
      } else {
        Serial.println("start dnsserver failed.");
      }
    }
    
    /*
     * 初始化WebServer
     */
    void initWebServer() 
    {
      if (MDNS.begin("esp32"))      //给设备设定域名esp32,完整的域名是esp32.local
      {
        Serial.println("MDNS responder started");
      }
      //必须添加第二个参数HTTP_GET,以下面这种格式去写,否则无法强制门户
      server.on("/", HTTP_GET, handleRoot);                      //  当浏览器请求服务器根目录(网站首页)时调用自定义函数handleRoot处理,设置主页回调函数,必须添加第二个参数HTTP_GET,否则无法强制门户
      server.on("/configwifi", HTTP_POST, handleConfigWifi);     //  当浏览器请求服务器/configwifi(表单字段)目录时调用自定义函数handleConfigWifi处理
                                                                
      server.onNotFound(handleNotFound);                         //当浏览器请求的网络资源无法在服务器找到时调用自定义函数handleNotFound处理
     
      server.begin();                                           //启动TCP SERVER
    
      Serial.println("WebServer started!");
    }
    
    /*
     * 扫描附近的WiFi,为了显示在配网界面
     */
    bool scanWiFi() {
      Serial.println("scan start");
      Serial.println("--------->");
      // 扫描附近WiFi
      int n = WiFi.scanNetworks();
      Serial.println("scan done");
      if (n == 0) {
        Serial.println("no networks found");
        scanNetworksID = "no networks found";
        return false;
      } else {
        Serial.print(n);
        Serial.println(" networks found");
        for (int i = 0; i < n; ++i) {
          // Print SSID and RSSI for each network found
          Serial.print(i + 1);
          Serial.print(": ");
          Serial.print(WiFi.SSID(i));
          Serial.print(" (");
          Serial.print(WiFi.RSSI(i));
          Serial.print(")");
          Serial.println((WiFi.encryptionType(i) == WIFI_AUTH_OPEN) ? " " : "*");
          scanNetworksID += "<P>" + WiFi.SSID(i) + "</P>";
          delay(10);
        }
        return true;
      }
    }
    
    /*
     * 连接WiFi
     */
    void connectToWiFi(int timeOut_s) {
      WiFi.hostname(HOST_NAME);             //设置设备名
      Serial.println("进入connectToWiFi()函数");
      WiFi.mode(WIFI_STA);                        //设置为STA模式并连接WIFI
      WiFi.setAutoConnect(true);                  //设置自动连接    
      
      if (wifi_ssid != "")                        //wifi_ssid不为空,意味着从网页读取到wifi
      {
        Serial.println("用web配置信息连接.");
        WiFi.begin(wifi_ssid.c_str(), wifi_pass.c_str()); //c_str(),获取该字符串的指针
        wifi_ssid = "";
        wifi_pass = "";
      } 
      else                                        //未从网页读取到wifi
      {
        Serial.println("用nvs保存的信息连接.");
        WiFi.begin();                             //begin()不传入参数,默认连接上一次连接成功的wifi
      }
     
      int Connect_time = 0;                       //用于连接计时,如果长时间连接不成功,复位设备
      while (WiFi.status() != WL_CONNECTED)       //等待WIFI连接成功
      {  
        Serial.print(".");                        //一共打印30个点点
        digitalWrite(LED, !digitalRead(LED));     
        delay(500);
        Connect_time ++;
                                           
        if (Connect_time > 2 * timeOut_s)         //长时间连接不上,重新进入配网页面
        { 
          digitalWrite(LED, LOW);
          Serial.println("");                     //主要目的是为了换行符
          Serial.println("WIFI autoconnect fail, start AP for webconfig now...");
          wifiConfig();                           //开始配网功能
          return;                                 //跳出 防止无限初始化
        }
      }
      
      if (WiFi.status() == WL_CONNECTED)          //如果连接成功
      {
        Serial.println("WIFI connect Success");
        Serial.printf("SSID:%s", WiFi.SSID().c_str());
        Serial.printf(", PSW:%s\r\n", WiFi.psk().c_str());
        Serial.print("LocalIP:");
        Serial.print(WiFi.localIP());
        Serial.print(" ,GateIP:");
        Serial.println(WiFi.gatewayIP());
        Serial.print("WIFI status is:");
        Serial.print(WiFi.status());
        digitalWrite(LED, HIGH);
        server.stop();                            //停止开发板所建立的网络服务器。
      }
    }
    
    /*
     * 配置配网功能
     */
    void wifiConfig() 
    {
      initSoftAP();   
      initDNS();        
      initWebServer();  
      scanWiFi();       
    }
    
    
    /*
     * 删除保存的wifi信息,这里的删除是删除存储在flash的信息。删除后wifi读不到上次连接的记录,需重新配网
     */
    void restoreWiFi() {
      delay(500);
      esp_wifi_restore();  //删除保存的wifi信息
      Serial.println("连接信息已清空,准备重启设备..");
      delay(10);
      blinkLED(LED, 5, 500); //LED闪烁5次         //关于LED,不需要可删除 
      digitalWrite(LED, LOW);                    //关于LED,不需要可删除
    }
    
    /*
     * 检查wifi是否已经连接
     */
    void checkConnect(bool reConnect) 
    {
      if (WiFi.status() != WL_CONNECTED)           //wifi连接失败
      {
        if (digitalRead(LED) != LOW) 
          digitalWrite(LED, LOW);
        if (reConnect == true && WiFi.getMode() != WIFI_AP && WiFi.getMode() != WIFI_AP_STA ) 
        {
          Serial.println("WIFI未连接.");
          Serial.println("WiFi Mode:");
          Serial.println(WiFi.getMode());
          Serial.println("正在连接WiFi...");
          connectToWiFi(connectTimeOut_s);          //连接wifi函数 
        }
      } 
      else if (digitalRead(LED) != HIGH)  
        digitalWrite(LED, HIGH);                    //wifi连接成功
    }
    
    /*
     * LED闪烁函数        //用不上LED可删除
     */
    void blinkLED(int led, int n, int t) 
    {
      for (int i = 0; i < 2 * n; i++) 
      {
        digitalWrite(led, !digitalRead(led));
        delay(t);
      }
    }
    
    
    /*
     * LED初始化
     */
    void LEDinit()
    {
      pinMode(LED, OUTPUT);                 //配置LED口为输出口
      digitalWrite(LED, LOW);               //初始灯灭
    }
    
    /*
     * 检测客户端DNS&HTTP请求
     */
    void checkDNS_HTTP()
    {
      dnsServer.processNextRequest();   //检查客户端DNS请求
      server.handleClient();            //检查客户端(浏览器)http请求
    }
    
    

    main.ino

    #include "WiFiUser.h"
    
    const int resetPin = 0;                    //设置重置按键引脚,用于删除WiFi信息
    int connectTimeOut_s = 15;                 //WiFi连接超时时间,单位秒
    
    void setup() 
    {
      pinMode(resetPin, INPUT_PULLUP);     //按键上拉输入模式(默认高电平输入,按下时下拉接到低电平)
      Serial.begin(115200);                //波特率
      
      LEDinit();                           //LED用于显示WiFi状态
      connectToWiFi(connectTimeOut_s);     //连接wifi,传入的是wifi连接等待时间15s
    }
     
    void loop() 
    {
      if (!digitalRead(resetPin)) //长按5秒(P0)清除网络配置信息
      {
        delay(5000);              //哈哈哈哈,这样不准确
        if (!digitalRead(resetPin)) 
        {
          Serial.println("\n按键已长按5秒,正在清空网络连保存接信息.");
          restoreWiFi();     //删除保存的wifi信息
          ESP.restart();              //重启复位esp32
          Serial.println("已重启设备.");//有机会读到这里吗?
        }
      }
      
      checkDNS_HTTP();                  //检测客户端DNS&HTTP请求,也就是检查配网页面那部分
      checkConnect(true);               //检测网络连接状态,参数true表示如果断开重新连接
     
      delay(30); 
    }
    
    

    不想了解代码实现的就不用往下看了,没必要没必要

    代码详细解释

    从setup()中调用了connectToWiFi(connectTimeOut_s);函数,进入连接wifi的入口。

    connectToWiFi(int timeOut_s)        //连接wifi

    /*
     * 连接WiFi
     */
    void connectToWiFi(int timeOut_s) 
    {
      WiFi.hostname(HOST_NAME);                   //设置设备名
      Serial.println("进入connectToWiFi()函数");
      WiFi.mode(WIFI_STA);                        //设置为STA模式并连接WIFI
      WiFi.setAutoConnect(true);                  //设置自动连接    
      
      if (wifi_ssid != "")                        //wifi_ssid不为空,意味着从网页读取到wifi
      {
        Serial.println("用web配置信息连接.");
        WiFi.begin(wifi_ssid.c_str(), wifi_pass.c_str()); //c_str(),获取该字符串的指针
        wifi_ssid = "";
        wifi_pass = "";
      } 
      else                                        //未从网页读取到wifi
      {
        Serial.println("用nvs保存的信息连接.");
        WiFi.begin();                             //begin()不传入参数,默认连接上一次连接成功的wifi
      }
     
      int Connect_time = 0;                       //用于连接计时,如果长时间连接不成功,复位设备
      while (WiFi.status() != WL_CONNECTED)       //等待WIFI连接成功
      {  
        Serial.print(".");                        //一共打印30个点点
        digitalWrite(LED, !digitalRead(LED));     
        delay(500);
        Connect_time ++;
                                           
        if (Connect_time > 2 * timeOut_s)         //长时间连接不上,重新进入配网页面
        { 
          
          digitalWrite(LED, LOW);
          Serial.println("");                     //主要目的是为了换行符
          Serial.println("WIFI autoconnect fail, start AP for webconfig now...");
          wifiConfig();                           //开始配网功能
          return;                                 //跳出 防止无限初始化
     
        }
      }
      
      if (WiFi.status() == WL_CONNECTED)          //如果连接成功
      {
        Serial.println("WIFI connect Success");
        Serial.printf("SSID:%s", WiFi.SSID().c_str());
        Serial.printf(", PSW:%s\r\n", WiFi.psk().c_str());
        Serial.print("LocalIP:");
        Serial.print(WiFi.localIP());
        Serial.print(" ,GateIP:");
        Serial.println(WiFi.gatewayIP());
        Serial.print("WIFI status is:");
     
        Serial.print(WiFi.status());
        digitalWrite(LED, HIGH);
        server.stop();                            //停止开发板所建立的网络服务器。
      }
    }

    本函数先检测wifi_ssid是否读进了参数,尝试用wifi_ssid方式用上次连接成功保存的参数两种方式尝试连接,等待连接后未连接成功将调用wifiConfig()开始配网,连接成功则打印连接数据。

    我要是每个函数都拿出来讲是不是很烦人啊~


    WiFi.hostname(HOST_NAME);                   //设置设备名

    此函数定义在ESP32官方库的WiFiGenerice库中

    函数定义:输入参数手动给ESP32模块设置hostname(主机名)

    当联网成功后打开路由器界面,可查看到连接设备:

     说明我们设置的没问题。


    WiFi.mode(WIFI_STA);                        //设置为STA模式并连接WIFI

    此函数定义在WiFiGenerice库中

    函数定义:配置WiFi的工作模式

    工作模式一共有三种:接入点模式(AP)、无线终端模式(Station)以及混合模式。

    • WIFI_OFF,(关闭WiFi)
    • WIFI_STA,(无线终端模式)
    • WIFI_AP, (接入点模式)
    • WIFI_AP_STA,(接入点-无线终端双模式)

    WiFi.setAutoConnect(true);                  //设置自动连接    

    此函数定义在WiFiSTA库中

    函数定义:激活ESP32模块的自动连接模式。模块会在通电后自动连接到最近连接过的WiFi接入点。

    ture:启动自动连接。false:禁用自动连接。


     WiFi.begin(wifi_ssid.c_str(), wifi_pass.c_str()); //c_str(),获取该字符串的指针

    此函数定义在WiFiSTA库中

    函数定义:此函数用于连接WiFi,需要传入的函数是指针类型的。如果不传入参数,默认连接已保存的wifi。


    WiFi.status() != WL_CONNECTED

    此函数定义在WiFiSTA库中

    函数定义:判断wifi连接状态

    • WL_IDLE_STATUS:0,正在尝试连接
    • WL_NO_SSID_AVAIL:1,没有找到SSID网络
    • WL_SCAN_COMPLETED:2,网络扫描完毕
    • WL_CONNECTED:3,网络连接成功
    • WL_CONNECT_FAILED:4,连接失败
    • WL_CONNECTION_LOST:5,连接丢失
    • WL_DISCONNECTED:6,未连接
    //WebServer server(webPort); 
    server.stop();

    此函定义在WebServer库中

    函数定义:本函数用于停止开发板所建立的网络服务器。


    wifiConfig()        //配置配网功能

    /*
     * 配置配网功能
     */
    void wifiConfig() 
    {
      initSoftAP();   
      initDNS();        
      initWebServer();  
      scanWiFi();       
    }
    
    

    此函数调用四个函数实现配网功能的开启。

    initSoftAP()        //进入AP模式

    /*
     * 进入AP模式
     */
    void initSoftAP() {
      WiFi.mode(WIFI_AP);                                           //配置为AP模式
      WiFi.softAPConfig(apIP, apIP, IPAddress(255, 255, 255, 0));   //设置AP热点IP和子网掩码
      if (WiFi.softAP(AP_SSID))                                     //开启AP热点,如需要密码则添加第二个参数
      {                           
        //打印相关信息
        Serial.println("ESP-32S SoftAP is right.");
        Serial.print("Soft-AP IP address = ");
        Serial.println(WiFi.softAPIP());                                                //接入点ip
        Serial.println(String("MAC address = ")  + WiFi.softAPmacAddress().c_str());    //接入点mac
      } 
      else                                                  //开启AP热点失败
      { 
        Serial.println("WiFiAP Failed");
        delay(1000);
        Serial.println("restart now...");
        ESP.restart();                                      //重启复位esp32
      }
    }
    

    主要是配置为AP热点模式


    WiFi.softAPConfig(apIP, apIP, IPAddress(255, 255, 255, 0));   //设置AP热点IP和子网掩码

    此函数定义在WiFiAP库

    函数定义:配置接入点网络信息

    三个参数分别为:接入点ip地址,网关ip地址,子虚掩码(均为IPAddress类型)

    网关_百度百科         IP地址_百度百科        子网掩码_百度百科

    不懂就照着写吧!


    WiFi.softAP(AP_SSID)

    此函数定义在WiFiAP库

    函数定义:启动校验式wifi网络或开放式wifi网络。

    bool softAP(const char* ssid, const char* passphrase = NULL, int channel = 1, int ssid_hidden = 0, int max_connection = 4, bool ftm_responder = false);

    此函数参数有:网络名称、密码、信道、wifi是否隐藏标志变量、最大允许接入数量(最多4个)

    其他都默认就行。密码默认NULL

    一般用不到其他功能。


    WiFi.softAPmacAddress()

    此函数定义在WiFiAP库

    函数定义:建立wifi接入点后,我们可以使用本函数获取开发板的mac地址。

    另一种使用方法

    WiFi.softAPmacAddress(macAddr)

     读mac地址:

    macAddr[0]:macAddr[1]:macAddr[2]:macAddr[3]:macAddr[4]:macAddr[5]这种形式!!

    initDNS()        //开启DNS服务器

    void initDNS() 
    {
      if (dnsServer.start(DNS_PORT, "*", apIP))   //判断将所有地址映射到esp32的ip上是否成功
      {
        Serial.println("start dnsserver success.");
      } else {
        Serial.println("start dnsserver failed.");
      }
    }

    DNS:域名服务器。域名服务器_百度百科

    此函数没有考虑开启失败的问题!!


    dnsServer.start(DNS_PORT, "*", apIP)

    此函数定义在DNSServer库

    函数定义:以无线终端模式工作时,调用本函数可以启动ESP32模块的DNS服务

    函数有三个参数:

    • DNS服务端口号(默认53)
    • 映射的域名,也就是开启服务后可以直接访问的用于代替IP地址的域名
    • 映射的IP地址

    返回值:是否开启成功

    这里的  *  替换成你想要的网站,例如:www.example.com 。可以替换成你想要的网址。当做项目时,替换成一个能代表你的网址就显得专业了~哈哈哈哈!实际访问的还是配网的ip地址。


    initWebServer()        //设置访问功能

    void initWebServer() 
    {
      if (MDNS.begin("esp32"))      //给设备设定域名esp32,完整的域名是esp32.local
      {
        Serial.println("MDNS responder started");
      }
      //必须添加第二个参数HTTP_GET,以下面这种格式去写,否则无法强制门户
      //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
      server.on("/", HTTP_GET, handleRoot);                      //  当浏览器请求服务器根目录(网站首页)时调用自定义函数handleRoot处理,设置主页回调函数,必须添加第二个参数HTTP_GET,否则无法强制门户
      server.on("/configwifi", HTTP_POST, handleConfigWifi);     //  当浏览器请求服务器/configwifi(表单字段)目录时调用自定义函数handleConfigWifi处理
                                                                
      server.onNotFound(handleNotFound);                         //当浏览器请求的网络资源无法在服务器找到时调用自定义函数handleNotFound处理
     
      server.begin();                                           //启动TCP SERVER
     
      Serial.println("WebServer started!");
    }

    此函数创建了网络服务器。


    MDNS.begin("esp32")        //给设备设定域名esp32,完整的域名是esp32.local

     多播DNS

    域名_百度百科

    这个问题我不会!尴尬了!!


    server.on("/", HTTP_GET, handleRoot);                      //  当浏览器请求服务器根目录(网站首页)时调用自定义函数handleRoot处理,设置主页回调函数,必须添加第二个参数HTTP_GET,否则无法强制门户

    此函数定义在 WebServer库

    函数定义:每当有客户端向服务器发送HTTP请求时,我们可以利用on函数来设置HTTP请求回调函数。

    通过HTTP请求回调函数,我们可以让ESP32服务器生成响应信息并发送给HTTP请求客户端。

    第二个参数为数据传输方法。

    关键字:

    •      HTTP_ANY
    •      HTTP_GET
    •      HTTP_POST
    •      HTTP_PUT
    •      HTTP_PATCH
    •      HTTP_DELETE
    •      HTTP_OPTIONS

    server.onNotFound(handleNotFound);                         //当浏览器请求的网络资源无法在服务器找到时调用自定义函数handleNotFound处理

    此函数定义在 WebServer库

    函数定义:每当有客户端向服务器发送HTTP请求时,我们可以利用onNotFound函数来设置HTTP请求无效地址的回调函数。

    参数:处理无效地址请求的回调函数


      server.begin();                                           //启动TCP SERVER

    此函数定义在 WebServer库

    函数定义:启动开发板所建立的网络服务器。

    scanWiFi()        //扫描附近的WiFi,为了显示在配网界面

    /*
     * 扫描附近的WiFi,为了显示在配网界面
     */
    bool scanWiFi() {
      Serial.println("scan start");
      Serial.println("--------->");
      // 扫描附近WiFi
      int n = WiFi.scanNetworks();
      Serial.println("scan done");
      if (n == 0) {
        Serial.println("no networks found");
        scanNetworksID = "no networks found";
        return false;
      } else {
        Serial.print(n);
        Serial.println(" networks found");
        for (int i = 0; i < n; ++i) {
          // Print SSID and RSSI for each network found
          Serial.print(i + 1);
          Serial.print(": ");
          Serial.print(WiFi.SSID(i));
          Serial.print(" (");
          Serial.print(WiFi.RSSI(i));
          Serial.print(")");
          Serial.println((WiFi.encryptionType(i) == WIFI_AUTH_OPEN) ? " " : "*");
          scanNetworksID += "<P>" + WiFi.SSID(i) + "</P>";
          delay(10);
        }
        return true;
      }
    }

    此函数扫描可搜索到的wifi,附加在HTML代码后。


    WiFi.scanNetworks()

    此函数定义在WiFiScan库

    函数定义:扫描到ESP8266开发板所在环境中的可用WIFI网络,并且将WiFi网络信息保存到内存中。通过调用SSID、RSSI等函数,我们还可以得到这些扫描到的WIFI的更多信息

    此函数一次性得到完整的WiFi列表,并且将WiFi列表保存在内存中。

    返回值:扫描到可用的网络数量


    (WiFi.encryptionType(i) == WIFI_AUTH_OPEN

    此函数定义在WiFiScan库

    函数定义:通过此函数,我们可以获取扫描到的WiFi网络信息中的网络加密类型。

    参数为扫描到的wifi的序列号

    返回值:返回的wifi加密类型


    WiFi.SSID(i)

     此函数定义在WiFiScan库

    函数定义:获取扫描到的WiFi网络信息中的网络名称(SSID)信息。

    返回:扫描到的WiFi网络信息中的网络名称(SSID)信息(类型:String)

    handleRoot()处理网站根目录的访问请求

    /*
     * 处理网站根目录的访问请求
     */
    void handleRoot() 
    {
      if (server.hasArg("selectSSID")) {
        server.send(200, "text/html", ROOT_HTML + scanNetworksID + "</body></html>");   //scanNetWprksID是扫描到的wifi
      } else {
        server.send(200, "text/html", ROOT_HTML + scanNetworksID + "</body></html>");   
      }
    }

    server.hasArg("selectSSID")

    此函数定义在WebServer库中

    函数定义:可以获取客户端向ESP32物联网服务器发送的请求信息中有无指定的参数。

    传入参数:需要确认的请求体中的参数名

    返回值:是否存在指定参数


    server.send(200, "text/html", ROOT_HTML + scanNetworksID + "</body></html>"); 
                                    //这里拆开是为了添加上wifi目录

    此函数定义在WebServer库中

    函数定义:如果有客户端向ESP8266服务器发送HTTP请求,ESP8266服务器可使用本函数向客户端发送响应信息。

    参数:响应状态码,响应内容类型,响应内容

    handleConfigWifi()        //提交数据后的提示页面

    /*
     * 提交数据后的提示页面
     */
    void handleConfigWifi()               //返回http状态
    {
      
     
      if (server.hasArg("ssid"))          //判断是否有账号参数
      {
        Serial.print("got ssid:");
        wifi_ssid = server.arg("ssid");   //获取html表单输入框name名为"ssid"的内容
     
        Serial.println(wifi_ssid);
      } 
      else                                //没有参数
      { 
        Serial.println("error, not found ssid");
        server.send(200, "text/html", "<meta charset='UTF-8'>error, not found ssid"); //返回错误页面
        return;
      }
      //密码与账号同理
      if (server.hasArg("pass")) 
      {
        Serial.print("got password:");
        wifi_pass = server.arg("pass");  //获取html表单输入框name名为"pwd"的内容
        Serial.println(wifi_pass);
      } 
      else 
      {
        Serial.println("error, not found password");
        server.send(200, "text/html", "<meta charset='UTF-8'>error, not found password");
        return;
      }
      
      server.send(200, "text/html", "<meta charset='UTF-8'>SSID:" + wifi_ssid + "<br />password:" + wifi_pass + "<br />已取得WiFi信息,正在尝试连接,请手动关闭此页面。"); //返回保存成功页面
      delay(2000);
      
      WiFi.softAPdisconnect(true);     //参数设置为true,设备将直接关闭接入点模式,即关闭设备所建立的WiFi网络。
      server.close();                  //关闭web服务
      WiFi.softAPdisconnect();         //在不输入参数的情况下调用该函数,将关闭接入点模式,并将当前配置的AP热点网络名和密码设置为空值.
      
      Serial.println("WiFi Connect SSID:" + wifi_ssid + "  PASS:" + wifi_pass);
      
      if (WiFi.status() != WL_CONNECTED)    //wifi没有连接成功
      {
        Serial.println("开始调用连接函数connectToWiFi()..");
        connectToWiFi(connectTimeOut_s);
      } 
      else {
        Serial.println("提交的配置信息自动连接成功..");
      }
    }

     读输入的参数,写入wifi账号密码。


     wifi_ssid = server.arg("ssid");   //获取html表单输入框name名为"ssid"的内容

    此函数定义在WebServer库中

    函数定义:获取客户端向ESP8266物联网服务器发送的指定参数的数值。

    参数:请求体中的参数名(参数类型: String)

    返回值:指定参数的数值(类型:String)


    WiFi.softAPdisconnect(true);     //参数设置为true,设备将直接关闭接入点模式,即关闭设备所建立的WiFi网络。
    

    此函数定义在WiFiAP库

    函数定义:可用于关闭开发板的接入点模式,即关闭开发板所建立的WiFi网络。

    传入参数:

    参数类型为bool。如果将该参数设置为true,设备将直接关闭接入点模式。

    在不输入参数的情况下调用该函数,设备会将当前配置的网络名和密码设置为空值。

    返回值:返回设置是否成功的结果。设置成功返回true。数据类型为bool型。


    server.close();                  //关闭web服务
    

    此函数定义在WebServer库

    函数定义:本函数用于停止ESP32开发板所建立的网络服务器。

    handleNotFound()        //当浏览器请求的网络资源无法在服务器找到时通过此自定义函数处理

    /*
     * 处理404情况的函数'handleNotFound'
     */
    void handleNotFound() {           // 当浏览器请求的网络资源无法在服务器找到时通过此自定义函数处理
      handleRoot();                 //访问不存在目录则返回配置页面
      //   server.send(404, "text/plain", "404: Not found");
    }
    

    restoreWiFi()        //删除保存的wifi信息

    /*
     * 删除保存的wifi信息,这里的删除是删除存储在flash的信息。删除后wifi读不到上次连接的记录,需重新配网
     */
    void restoreWiFi() {
      delay(500);
      esp_wifi_restore();  //删除保存的wifi信息
      Serial.println("连接信息已清空,准备重启设备..");
      delay(10);
      blinkLED(LED, 5, 500); //LED闪烁5次         //关于LED,不需要可删除 
      digitalWrite(LED, LOW);                    //关于LED,不需要可删除
    }
    

    #include <esp_wifi.h>     //用于esp_wifi_restore() 删除保存的wifi信息
    
    esp_wifi_restore();  //删除保存的wifi信息

    checkConnect(bool reConnect)        //检测wifi是否连接

    /*
     * 检查wifi是否已经连接
     */
    void checkConnect(bool reConnect) 
    {
      if (WiFi.status() != WL_CONNECTED)           //wifi连接失败
      {
        if (digitalRead(LED) != LOW) 
          digitalWrite(LED, LOW);
        if (reConnect == true && WiFi.getMode() != WIFI_AP && WiFi.getMode() != WIFI_AP_STA ) 
        {
          Serial.println("WIFI未连接.");
          Serial.println("WiFi Mode:");
          Serial.println(WiFi.getMode());
          Serial.println("正在连接WiFi...");
          connectToWiFi(connectTimeOut_s);          //连接wifi函数 
        }
      } 
      else if (digitalRead(LED) != HIGH)  
        digitalWrite(LED, HIGH);                    //wifi连接成功
    }

    里面包含的函数上面都说过了。

    checkDNS_HTTP()        //检测客户端DNS&HTTP请求

    /*
     * 检测客户端DNS&HTTP请求
     */
    void checkDNS_HTTP()
    {
      dnsServer.processNextRequest();   //检查客户端DNS请求
      server.handleClient();            //检查客户端(浏览器)http请求
    }

    要放在loop函数中,检测http和dns消息。然后 server.on 函数定义的回调函数。

    LED函数

    /*
     * LED闪烁函数        //用不上LED可删除
     */
    void blinkLED(int led, int n, int t) 
    {
      for (int i = 0; i < 2 * n; i++) 
      {
        digitalWrite(led, !digitalRead(led));
        delay(t);
      }
    }
    
    
    /*
     * LED初始化
     */
    void LEDinit()
    {
      pinMode(LED, OUTPUT);                 //配置LED口为输出口
      digitalWrite(LED, LOW);               //初始灯灭
    }
    

    这一部分没啥好说的了吧 ~

    呼 ~ 累死我了!!

    终于写完了,不怕以后忘记了!!!哈哈哈哈哈哈

    如果你看到这里了,觉得本文对你有帮助,那就帮我 ~~~~

    哈哈哈哈 ~  开心吗 ~ 学习到小知识真的很开心呢~

    加油鸭!!!!!!!!

    展开全文
  • Android 蓝牙Wifi配

    千次阅读 2022-01-21 19:08:58
    蓝牙网:GitHub - EspressifApp/EspBlufiForAndroid: EspBlufi is based on the BLUFI protocol, which connect with IOT devices for BLE data communication, realizes device config network, and custom data ...
  • 流程 注册微信公众号(测试号也可以) 登录 修改并记录一些参数 点击设置 设置Token URL(服务器地址) Token 跟流程 2 一样就好了 token.php 上传服务器 然后启用 ...form=%E8%
  • 准确性98%以上。 接口非常简单,3分钟就可以让你的应用增加声波通讯功能 抗干扰性强,基本上无论外界怎么干扰,信号都是准确的 可自己任意调整通信频段,支持低频有声频段,也支持高频无声频段,无声频段可混音任意...
  • 在我们使用智能设备时经常会看到很多设备是接入WiFi的,刚买来的时候是需要连接WiFi的,配WiFi的方式有很多种,我遇到的比较多的是smartConfig或 声纹网,如果是有触摸屏的话很多都是屏幕上直接输入WiFi密码即可。...
  • 经过上一篇的WiFI入门篇,我们知道了WiFi初始化方式 和学会了WiFi的几种工作方式, 在实际应用中,环境复杂多变,在固件中输入SSID 的方式太不通用了, 所以肯定是需要学习一下如何在不同的环境中联网,就是所谓的...
  • ESP32-C3入门教程 蓝牙篇③——基于微信小程序和Esp Blufi实现 WiFi配
  • WIFI类智能家居设备网模块测试点及测试用例
  • stat_sys_wifi_signal_4.png,ic_launcher.png,stat_sys_wifi_signal_3.png,stat_sys_wifi_signal_0.png,stat_sys_wifi_signal_1.png,stat_sys_wifi_signal_2.png,values-sw600dp,dimens.xml,layout,activity_main....
  • wifi 网方式总结

    千次阅读 2019-06-02 04:56:57
    本文汇总介绍了目前市面上各种WIFI模块的常见的网方式的原理和实现,对比了各种网方式的优势和局限,得出结论,WEB网是最直接、最简单、最灵活、约束条件最少的网方式,应该成为WIFI模块网的标配。...
  • 文章目录一.Smartconfig与Airkiss...…\esp-idf\examples\wifi\smart_config:这里展示了如何智能网 …\esp-idf\examples\protocols\sockets\tcp_client:这里展示了如何用socket套接字做客户端的tcp连接,并收发数
  • esp32 arduino开发WiFi配网、自动重连例程
  • WifiVoice,AndroidManifest.xml,res,layout,item_wifi_list.xml,activity_main.xml,drawable-ldpi,menu,main.xml,drawable-mdpi,stat_sys_wifi_signal_4.png,stat_sys_wifi_signal_0.png,stat_sys_wifi_signal_1.png...
  • wifi 一键网资料

    2019-01-16 15:30:44
    一键网用的apk ,oneshot 用于WIFI UDP 广播 初始化
  • wifi配网过程的详细介绍

    千次阅读 2021-07-27 15:10:03
    WIFI 无线电波传输距离(无穷远) 1,信号接收强度 RSS=Pt+Gr+Gt-Lc-Lbf RSS=接收信号强度 Pt=发射功率(规定不能发射超火100mw的) Gr=接收天线增益 Gt=发射天线增益 Lc=电缆和栏头的衰耗 Lbf=自由空间损耗 2,wifi...
  • wifi配网原理

    2019-01-16 17:46:25
    怎么理解网呢,简单说就像你家里来了一个新朋友,你要把自家的wifi账号密码告诉她。 主流的网方式分为两大类,一类是手动网,一类是自动网(个人理解)。 手动网:  手动网就是主动...
  • 智能设备WIFI配网方式汇总

    千次阅读 2019-02-17 16:17:54
    当前很多物联网设备大都无没有人机交互界面,也就没有像手机或者PC那样有wifi的配置界面,汇总了一下设备入网的方式大概有如下几种: 1:AP 模式流程如下 1. 将Dev设置为AP模式,手机为正常的station模式 2. 手机...
  • 【福利帖】7日玩转ESP32——(第5日) 通过串口的WiFi
  • 物联网Wi-Fi配网方式,你知道几种?.pdf
  • } #endif // TLS_CONFIG_AIRKISS_MODE_ONESHOT 上面的代码中,airkiss_ssid和airkiss_pwd是全局变量,看下面源码截图: 这一篇写到这里,下面讲一键网的实用性操作,题目《联盛德W801系列2-WIFI一键网,信息...
  • WIFI网方式

    2022-07-13 17:37:38
    WIFI配
  • HaaS物联网应用开发课程
  • 根据微信官方提供API能力中的WiFi、UDP能力,可以用来开发Soft AP网 startWifi() { console.log('开始wifi接口'); wx.startWifi({ complete (res) { console.log(res) } }) }, connectWifi() {...
  • 不可否认我们已经全面进IoT时代入,各种智能硬件到处可见,比如:智能玩具、智能摄像机、智能...可以使用声波通信技术来完美解决自动wifi配网的问题。声波通信指用声波来进行短距离传输信息,发送端通过声波库把文本

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 37,907
精华内容 15,162
关键字:

wifi怎么配