ESP32学习笔记(八) WiFi

ESP32学习笔记(八) WiFi

重头戏来了,esp32的特色就是他的wifi功能

ESP32有三种工作模式,分别为:AP,STA,AP混合STA 并且也具有SMARTLINK功能。

1 WiFi相关知识

1.1 网络分层

WiFi属于网络的知识 所以从网络讲起

网络分层的几种方式

标准的七层网络分层,也就是OSI七层模型TCP/IP五层模型TCP/IP四层模型是从OSI七层优化而来。

image-20230104214640562

看一下层级及其对应的协议

image-20230104214721316

看一下层级及其对应的物理设备

image-20230104214740805

我们学习一下最普遍的TCP/IP五层协议

1.1.1 物理层

顾名思义,用物理手段将电脑连接起来,基本上是用双绞线、光纤、无线电波的方式来实现物理层。网络设备连接起来之后,就可以基于它来发送高低电压(电信号)进行通信,高电压对应数字1,低电压对应数字0。0、1信号本身没有任何的现实意义,所有需要用另一层用来规定不同0、1组合的意义。

1.1.2 数据链路层

单纯的电信号0和1没有任何意义,必须规定电信号多少位一组,每组什么意思。数据链路层的功能就是通过规定一套协议来定义电信号的分组方式,以及规定不同的组代表什么意思,从而双方计算机都能够进行识别,这个协议就是“以太网协议”。

以太网协议规定,一组电信号构成一个数据包,我们把这个数据包称之为。每一个桢由包头(Head)和数据(Data)两部分组成

其中Head包含数据包的一些说明信息,包括发送者、接收者、数据类型;Data则是数据包的具体内容。

Head部分包含固定的18个字节:

  • 发送者/源地址,6个字节;
  • 接收者/目标地址,6个字节;
  • 数据类型,6个字节。

Data部分的长度

最短为46字节,最长为1500字节。因此,整个”帧”最短为64字节,最长为1518字节。如果数据很长,就必须分割成多个帧进行发送。

数据帧定义好了 包头部分的地址如何定义 这就有了MAC地址

1.1.2.1 MAC地址

以太网规定,连入网络的所有设备都必须具有网卡数据包的发送地址和接收地址指的就是网卡地址,也就是MAC地址。

MAC地址作为网络中计算机设备的唯一标识,从计算机在厂商生产出来就被十六进制的数标识为MAC地址,MAC地址理论上是独一无二的。这也是为什么很多软件校验设备唯一性时,会用到MAC地址。

MAC地址长度为48位2进制(6字节),通常由12位16进制数表示(前六位是厂商编号,后六位是流水线号)。

image-20230104221442837

有了MAC地址,在同一网络内的两台主机就可以通信了。通信方式是广播

1.1.2.2 通信方式:广播

之前学的can总线,也是广播方式 可以对照一下学习

image-20230104230454395

在同一个子网中,计算机1要向计算机4发送一个数据包,数据包中包含接收方的MAC地址。计算机1向本网络内所有计算机都发送(以广播的方式),这时同一子网中的每台计算机 (包括2、3)都会收到这个数据包的。然后每台计算机都会把数据包的MAC地址取出来,与自身的MAC地址进行对比,如果两者相同,则接受这个数据包,否则就丢弃这个数据包。计算机4发现包含自己的MAC地址,于是就进行响应。

1.1.3 网络层

以太网通过广播这种很原始的形式,解决了两台计算机之间的通信问题。但很明显,它不是把数据包准确的送达接收方,而是向网络中所有的计算机发送数据包。

而我们所处的网络是由无数个子网络构成的。即便忽略掉子网的存在,如果所有的通信都采用以太网的广播方式,那么一台机器发送的包全世界都会收到,整个网络是会奔溃的。

而实现的网络又是由无数个子网络构成的,以太网采用广播方式发送数据包,效率低且发送的数据只能局限在发送者所在的子网络。

此时就需要另想办法,如果是同一个子网络,就采用广播方式发送,如果不是在同一个子网 , 那就通过网关和路由向不同广播域/子网分发数据包。所以就有了网络层,它是处理分组在网络中的活动,比如分组的选路。

网络层引入一套新的协议用来区分不同的广播域/子网,于是就有了IP 协议。

1.1.3.1 IP协议

网络地址协议,叫做IP协议。相对于上面讲到的MAC地址, IP 地址可以理解成逻辑地址,也就是说 MAC 地址是物理上的地址,是固定的。IP 地址是动态分配的,是不固定的。现在广泛采用的IPv4地址,同时IPv6也在不断的发展壮大。

通过IP协议发送的数据,就叫做IP数据包 整个数据包的总长度最大为65535字节

包头:包括版本、长度、IP地址等信息 长度为20到60字节

数据:具体内容

1.1.3.2 IPv4地址

32位的二进制数组成,一般把它分成4段的十进制表示,地址范围为0.0.0.0~255.255.255.255。

IP地址分成两部分:网络部分(标识子网)和主机部分(标识主机)。网络部分和主机部分所占用的二进制位数是不固定的

IP地址段只是标识了IP地址的种类,从网络部分或主机部分都无法辨识一个IP所处的子网。如果两个IP的网络部分相同,则说明它们处于同一个子网中。例如192.168.33.1和192.168.33.2,如果它们的网络部分为24位,主机部分为8位,网络部分都为 192.168.33,处于同一个子网中。

但如果像192.16.10.1与192.16.10.2,并不知道网络部分和主机部分各几位,就不能确定是否处于同一子网。于是就有了子网掩码

1.1.3.3 子网掩码

子网掩码就是用来标识同一局域网中的 IP 地址的信息的。子网掩码也是由 32 个二进制位组成的,但是只能用 0 或 1 来表示,如11111111.11111111.11111111.00000000。

子网掩码的网络部分全部为1,主机部分全部为0。两台计算机的 IP 地址分别和子网掩码进行一种AND 运算(两个数位都为1,运算结果为1,否则为0),如果结果相同,两台计算机就在同一局域网中,否则就不在同一局域网中。

比如IP地址为192.16.10.1,如果已知网络部分是前24位,主机部分是后8位,那么子网络掩码就是11111111.11111111.11111111.00000000,写成十进制就是255.255.255.0。

假设192.168.33.1和192.168.33.2的子码掩码都为255.255.255.0,把IP与子码掩码相与,结果都为192.168.33.0,那么它们说明处于同一个子网中。

1.1.3.4 ARP协议

可以判断计算机的子网了 知道其确切ip地址了 怎么获得mac地址? **为什么要获得mac地址?**分明ip地址已经可以对应唯一的计算机了???????

有了IP协议,可以判断计算机属于哪个子网了。在链路层时我们通过MAC地址进行通信的,现在只有目标的IP地址,如何才能拿到它的MAC地址呢?这就又要用到ARP协议。

ARP协议可以通过广播的方式发送数据包,获取目标主机的MAC地址。具体实现为:发送端主机通过广播的方式发送数据包,所有主机接收后拆开包,如果发现目标IP为自己就响应,返回MAC地址。(根据ip地址发的数据 都能根据ip地址确定计算机了 为啥还有mac地址多此一举????)

数据包格式:(发送端的MAC地址,目标MAC地址,发送端IP地址,目标IP地址,数据)。

1.1.4 传输层

经过数据链路层和网络层的支持,我们已经可以正常在两台计算机之间进行通讯了,但是计算机会同时运行着许多程序,比如同时开着QQ与WX,那么怎么区分消息是QQ的还是WX的呢?

通常我们会看到计算机上运行的不同程序都会分配不同的端口,而传输层的功能就是建立端口到端口的通信,使得数据能够正确的传送给不同的应用程序。

端口是每一个使用网卡的程序的编号,每一个端口号标志一个可执行程序。端口号是0到65535之间的一个整数,正好16个二进制位,0-1023为系统占用端口,其他应用程序只能选用大于1023的端口。

至此,用IP + 端口,已经能实现唯一确定互联网上一个程序,进而实现网络间的程序通信了。

传输层最常见的两大协议是 TCP(Transmission Control Protocol,传输控制协议) 协议和 UDP(User Data Protocol,用户数据报协议) 协议。

UDP协议就是在数据前面加上端口号,“包头”部分主要定义了发出端口和接收端口,一共只有8个字节。“数据”部分就是具体的内容。总长度不超过65,535字节,正好放进一个IP数据包。

UDP协议简单且容易实现,这是它的优点。但同样也有缺点,就是进行通信时不知道对方是否接收到数据了。此时需要再定义一套规则,让其可以和对方进行确认,那么TCP出现了。

TCP数据包没有长度限制,理论上可以无限长,可是为了保证网络的效率,通常TCP数据包的长度不会超过IP数据包的长度,也就是小于65,535字节,这也确保单个TCP数据包不必再分割。像我们通常说 TCP 三次握手和四次挥手,就是传输层完成的。关于TCP实现部分的内容比较多,我们这里就不再拓展了。

TCP的优点是能够确保数据不会遗失,安全可靠的传输。缺点是过程复杂、实现困难、消耗较多的资源。

1.1.5 应用层

终于说到应用层了,应用层算是我们最经常看到的一层了。应用层的功能就是规定了应用程序的数据格式。我们经常用的电子邮件、HTTP协议以及FTP数据的格式,就是在应用层定义的。

应用层的主要协议有:FTP(文件传送协议、21端口)、Telnet(远程登录协议)、DNS(域名解析协议)、SMTP(邮件传送协议、25端口),POP3协议(邮局协议),HTTP协议(Hyper Text Transfer Protocol、80端口)。

1.1.6 数据传输总结

image-20230104232741735

举个例子,比如我们登录微信 输入自己的账号和密码 这个数据(账号和密码)就需要发送给微信的服务器 然后服务器发给我们微信的登录之后的界面数据

应用层 自己的http包头+账号/密码

传输层 自己的tcp包头+自己的http包头+账号/密码

网络层 自己的IP包头+自己的tcp包头+自己的http包头+账号/密码

数据链路层 自己的MAC头+自己的IP包头+自己的tcp包头+自己的http包头+账号/密码 含有目标MAC 怎么确认目标MAC???目标MAC是个啥???可以将数据从子网,传输到外面的互联网???然后可以传输到微信的服务器 说明自己的IP包头既含有自己的地址 也含有目标地址

那自己的MAC包头只含有目标地址 那数据回来时怎么确定自己的自己的MAC地址 奥 通过上文的ARP协议 串起来了√√√

这是我暂时的理解

数据传输过程:
1、应用程序准备数据
2、传输层:将数据分块并进行编号
3、网络层:添加目标IP地址和源IP地址
4、数据链路层:利用自己的子网掩码判断自己处于哪一网段,并判断目标IP地址处于哪一网段,如果处于同一网段,则使用ARP广播协议解析目标IP地址的MAC地址,否则,将数据传给交换机(Switch),让交换机基于PC4的网关地址将数据传给符合的Route。
5、物理层:将数据帧拆分为Bit流。

2 ESP32+WiFi

要用ESP32进行WiFi开发 需要包含#include <WiFi.h>库,这个库向用户公开了一个WiFi对象,可以直接调用

这个对象是WiFiClass类的 这个类公开继承于WiFiGenericClass WiFiSTAClass WiFiScanClass WiFiAPClass

2.1 STA模式

2.1.1 相关API

/**
 * Start Wifi connection
 * if passphrase is set the most secure supported mode will be automatically selected
 * @param ssid const char*          Pointer to the SSID string.  即WiFi名
 * @param passphrase const char *   Optional. Passphrase. Valid characters in a passphrase must be between ASCII 32-126 (decimal).
 * @param bssid uint8_t[6]          Optional. BSSID / MAC of AP
 * @param channel                   Optional. Channel of AP
 * @param connect                   Optional. call connect
 * @return
 */
wl_status_t WiFiSTAClass::begin(const char* ssid, const char *passphrase, int32_t channel, const uint8_t* bssid, bool connect)

    
    
/**
 * is STA interface connected?
 * @return true if STA is connected to an AP
 */
bool WiFiSTAClass::isConnected()
    
    
/**
 * Change IP configuration settings disabling the dhcp client	更改禁用 dhcp 客户端的 IP 配置设置
 * @param local_ip   Static ip configuration					当地IP
 * @param gateway    Static gateway configuration				网关
 * @param subnet     Static Subnet mask							子网掩码
 * @param dns1       Static DNS server 1						DNS服务器1
 * @param dns2       Static DNS server 2						DNS服务器2
 */
bool WiFiSTAClass::config(IPAddress local_ip, IPAddress gateway, IPAddress subnet, IPAddress dns1, IPAddress dns2)
    
    

/**
 * Disconnect from the network.												断开连接
 * @param wifioff `true` to turn the Wi-Fi radio off.						为true则还将关闭网络功能, 默认false
 * @param eraseap `true` to erase the AP configuration from the NVS memory.	为true则清空AP热点配置信息, 默认false 
 * @return `true` when successful.
 */
bool WiFiSTAClass::disconnect(bool wifioff, bool eraseap)   
    
    
    
/**
 * Function used to set the automatic reconnection if the connection is lost. 	设置自动重连
 * @param autoReconnect `true` to enable this option.
 * @return true 
 */
bool WiFiSTAClass::setAutoReconnect(bool autoReconnect) 
    
    
    
/**
 * Deprecated. Setting the ESP32 station to connect to the AP (which is recorded)	设置连过一次自动连接
 * automatically or not when powered on. Enable auto-connect by default.
 * @deprecated use `setAutoReconnect`
 * @param autoConnect bool
 * @return if saved
 */
bool WiFiSTAClass::setAutoConnect(bool autoConnect)    
    
    
    
/**
 * Start scan WiFi networks available			这个功能默认在阻塞模式下运行,程序会扫描WIFI,期间什么事情都做不了, 可以通过参数设置改为异步模式.
 * @param async         run in async mode		true 异步		default 阻塞  好像就是同步???
 * @param show_hidden   show hidden networks	
 * @return Number of discovered networks		返回扫描到的WIFI的数量
 */
int16_t WiFiScanClass::scanNetworks(bool async, bool show_hidden, bool passive, uint32_t max_ms_per_chan, uint8_t channel, const char * ssid, const uint8_t * bssid)  
//只返回了扫描到的WIFI的数量, 那结果怎么得到? 原来他已经将扫描到的WIFI存入了WIFI单例对应的数组中.
//比如 SSID,都存入了 WiFi.SSID中,可以通过索引值遍历获取. 其他属性亦然.   
    
    
/**
 * set new mode	
 * @param m WiFiMode_t
 */
bool WiFiGenericClass::mode(wifi_mode_t m)   
typedef enum {													//可选的模式
    WIFI_MODE_NULL = 0,  /**< null mode */
    WIFI_MODE_STA,       /**< WiFi station mode */
    WIFI_MODE_AP,        /**< WiFi soft-AP mode */
    WIFI_MODE_APSTA,     /**< WiFi station + soft-AP mode */
    WIFI_MODE_MAX
} wifi_mode_t;    



/**
 * called to get the scan state in Async mode		异步模式下才需要用这个
 * @return scan result or status
 *          -1 if scan not fin						扫描未完成返回-1
 *          -2 if scan not triggered				根本没扫描返回-2
 */											      //完成了返回扫描结果  WIFI的数量
int16_t WiFiScanClass::scanComplete()
    
    

/**
 * delete last scan result from RAM				清除扫描结果  前文不是说扫描结果保存在WiFi.SSID中	访问的哪个SSID怎么确认???
 */
void WiFiScanClass::scanDelete()    
    
    
    
/**
 * Return the SSID discovered during the network scan.									看来是访问的是这一个 怎么实现的???
 * @param i     specify from which network item want to get the information				遍历的方法
 * @return       ssid string of the specified item on the networks scanned list			返回WiFi名
 */
String WiFiScanClass::SSID(uint8_t i)
{
    wifi_ap_record_t* it = reinterpret_cast<wifi_ap_record_t*>(_getScanInfoByIndex(i));
    if(!it) {
        return String();
    }
    return String(reinterpret_cast<const char*>(it->ssid));
}    


/**
 * Return the current SSID associated with the network				返回正在连接的WiFi名
 * @return SSID
 */
String WiFiSTAClass::SSID() const
{
    if(WiFiGenericClass::getMode() == WIFI_MODE_NULL){
        return String();
    }
    wifi_ap_record_t info;
    if(!esp_wifi_sta_get_ap_info(&info)) {
        return String(reinterpret_cast<char*>(info.ssid));
    }
    return String();

2.1.2 扫描WiFi

同步方式

流程

#include <Arduino.h>
#include "WiFi.h"
 
String ssid;
String password;
void setup()
{
  Serial.begin(115200);
  WiFi.mode(WIFI_MODE_STA);
  WiFi.disconnect(); //断开可能的连接
  delay(100);
  Serial.println("开始扫描WIFI:");
  int n = WiFi.scanNetworks();
  if (n)
  {
    Serial.print("扫描到");
    Serial.print(n);
    Serial.println("个WIFI");
    for (size_t i = 0; i < n; i++)
    {
      Serial.print("WiFi的名称(SSID):");
      Serial.println(WiFi.SSID(i));
      Serial.print("WiFi的信号强度(RSSI):");
      Serial.println(WiFi.RSSI(i));
      Serial.print("WiFi加密与否:");
      Serial.println(WiFi.encryptionType(i) == WIFI_AUTH_OPEN ? "未加密" : "加密");
 
      if (WiFi.SSID(i) == "anny")
      {
        ssid = WiFi.SSID(i);
        password = "20141208";
      }
    }
  }
  else
  {
    Serial.println("没扫描到WIFI");
  }
  WiFi.begin(ssid.c_str(), password.c_str());
  while (!WiFi.isConnected())
  {
    Serial.print(".");
  }
  Serial.println("连接成功");
}
void loop()
{
}
异步方式

这是一个博主的写法

采用异步扫描方式, 每隔0.5秒询问一下WiFi.scanComplete();判断是否扫描完毕.
如果扫描完毕,置标志位,停定时器.
触发连接程序, 连接完成后

#include <Arduino.h>
#include "WiFi.h"
#include "Ticker.h"
 
String ssid = "";
String password = "";
Ticker t1;
 
void aysncScanHandler()
{
  if (!WiFi.isConnected())
  {
    int wifiScanStatus = WiFi.scanComplete();
    switch (wifiScanStatus)
    {
    case -1:
      Serial.println("扫描中...");
      break;
    case -2:
      Serial.println("未进行扫描...");
      break;
    default:
      Serial.println("扫描完成");
      for (size_t i = 0; i < wifiScanStatus; i++)
      {
        Serial.print("WiFi的名称(SSID):");
        Serial.println(WiFi.SSID(i));
        Serial.print("WiFi的信号强度(RSSI):");
        Serial.println(WiFi.RSSI(i));
        Serial.print("WiFi加密与否:");
        Serial.println(WiFi.encryptionType(i) == WIFI_AUTH_OPEN ? "未加密" : "加密");
 
        if (WiFi.SSID(i) == "anny")
        {
          ssid = WiFi.SSID(i);
          password = "20141208";
        };
      }
      WiFi.scanDelete();
      t1.detach();
      break;
    }
  }
}
 
void connectAnny()
{
  WiFi.begin();
  while (!WiFi.isConnected())
  {
    Serial.print(".");
  }
  Serial.println("连接成功");
}
 
void setup()
{
  Serial.begin(115200);
  WiFi.mode(WIFI_MODE_STA);
  WiFi.disconnect(); //断开可能的连接
  delay(100);
  Serial.println("开始扫描WIFI:");
  WiFi.scanNetworks(true);
  t1.attach(0.5, aysncScanHandler);
}
void loop()
{
  if (!WiFi.isConnected())
  {
    if (ssid == "anny")
    {
      Serial.println(ssid);
      connectAnny();
    }
  }
}

2.1.3 STA总结

我将这个博主的代码进行了封装

欢迎到我的个人博客中下载

小吕的博客

image-20230106151658729

2.2 AP模式

ESP32 创建的 AP 热点,最多可以被 4 个 STA 设备连接 默认四个 可以修改

2.2.1 相关API

/**
 * Set up an access point
 * @param ssid              Pointer to the SSID (max 63 char).
 * @param passphrase        (for WPA2 min 8 char, for open use NULL)
 * @param channel           WiFi channel number, 1 - 13.
 * @param ssid_hidden       Network cloaking (0 = broadcast SSID, 1 = hide SSID)
 * @param max_connection    Max simultaneous connected clients, 1 - 4.
*/
bool WiFiAPClass::softAP(const char* ssid, const char* passphrase, int channel, int ssid_hidden, int max_connection, bool ftm_responder)
    
    
    
/**
 * Configure access point
 * @param local_ip      access point IP
 * @param gateway       gateway IP
 * @param subnet        subnet mask
 */
bool WiFiAPClass::softAPConfig(IPAddress local_ip, IPAddress gateway, IPAddress subnet, IPAddress dhcp_lease_start) 
    
    
    
    
/**
 * Disconnect from the network (close AP)
 * @param wifioff disable mode?
 * @return one value of wl_status_t enum
 */
bool WiFiAPClass::softAPdisconnect(bool wifioff)    
    
    
/**
 * Set the softAP    interface Host name.
 * @param  hostname  pointer to const string
 * @return true on   success
 */
bool WiFiAPClass::softAPsetHostname(const char * hostname)    

2.2.2

2.3 智能配网

  • 2
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
ESP32的FreeRTOS使用上与传统的FreeRTOS有一些区别。在ESP32中,基本不需要单独配置FreeRTOSConfig.h文件,因为ESP-IDF中的menuconfig功能可以对所有涉及到的内容进行配置,使用起来更加直观和便利。主要的数据类型说明中,有一个重要的数据类型是TickType_t。 在ESP32的魔改版FreeRTOS中,很少使用正经的事件集,而是使用ESP-IDF提供的更方便的事件循环。这使得在ESP32中使用事件循环更加方便。 另外,ESP32的分区表是采用二进制格式而不是CSV文件。ESP-IDF提供了gen_esp32part.py工具来配置和构建分区表。默认情况下,使用的是默认分区表。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [【ESP32+freeRTOS学习笔记-(一)freeRTOS介绍】](https://blog.csdn.net/weixin_45499326/article/details/128226443)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] - *2* *3* [ESPIDF开发ESP32学习笔记ESP32上的FreeRTOS】](https://blog.csdn.net/qq_40500005/article/details/114794039)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值