ESP32 WIFI通信

① ESP32 Wifi基础操作

② ESP32 自动重连接

③ NTP网络校时

④ UDP/TCP通信

1 ESP32 Wifi基础操作

1.1 ESP32 WIFI简介

WiFi 是一种无线网络技术,允许设备在一定范围内通过无线信号进行通信和数据传输,无需依赖有线连接。它基于 IEEE 802.11 标准,使智能设备能够方便地接入互联网或相互之间进行数据交换。

WiFi网络的基本构成包括无线接入点AP(Access Point站点STA(Station)。在WiFi网络中,AP是无线网络的创建者和协调者,负责管理和协调整个网络中的数据传输,而STA代表连接到网络的终端设备,每一个终端设备如PC或手机都可以被视为STA。

ESP32-S3 集成 2.4 GHz Wi-Fi (802.11 b/g/n),支持 40 MHz 带宽;其低功耗蓝牙子系统支持 Bluetooth 5 (LE) 和 Bluetooth Mesh,可通过 Coded PHY 与广播扩展实现远距离通信。它还支持 2 Mbps PHY,用于提高传输速度和数据吞吐量。ESP32-S3 的 Wi-Fi 和 Bluetooth LE 射频性能优越,在高温下也能稳定工作。支持多种 WiFi 模式:

  • Station 模式(STA) :ESP32 可以像普通 WiFi 设备一样连接到 WiFi 路由器或热点,作为客户端获取网络访问权限,实现与互联网或其他网络设备的通信。这种模式适用于需要接入现有 WiFi 网络的场景,例如将 ESP32 连接到家中的 WiFi 路由器,以便访问云服务或与其他联网设备交互。
  • Access Point 模式(AP) :ESP32 可以作为 WiFi 热点,创建一个 WiFi 网络,允许其他设备连接到它。这在没有现有 WiFi 网络或需要为其他设备提供网络接入服务时非常有用,例如在物联网项目中,多个传感器或设备可以通过连接到 ESP32 创建的 AP 进行数据传输。
  • STA+AP 模式 :ESP32 同时支持 Station 和 Access Point 模式。这意味着它可以一边连接到一个 WiFi 路由器获取网络访问权限,一边作为热点为其他设备提供 WiFi 连接服务。这种模式在需要同时接入外部网络并与其他设备进行本地通信的场景中具有很大优势,例如在智能网关设备中,ESP32 可以通过 STA 模式连接到互联网,同时通过 AP 模式与附近的传感器或控制器进行通信。

1.2 ESP32 STA模式实战

以下是一个Arduino ESP32 WiFi STA模式的例程,能够连接WiFi,有超时停止尝试连接并通过串口打印提示、连接成功后打印路由器信息和本机网络信息。

#include <Arduino.h>
#include <WiFi.h>

const char* ssid = "your_SSID";   // WiFi网络名称
const char* password = "your_PASSWORD"; // WiFi密码
const int connectTimeout = 20;    // 连接超时时间(秒)

void setup() {
  Serial.begin(115200);
  Serial.println();
  Serial.print("Connecting to ");
  Serial.println(ssid);

  WiFi.begin(ssid, password);
  unsigned long start_time = millis();

  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
    if ((millis() - start_time) / 1000 > connectTimeout) {
      Serial.println();
      Serial.println("Connection timed out.");
      break;
    }
  }

  if (WiFi.status() == WL_CONNECTED) {
    Serial.println("");
    Serial.println("WiFi connected");
    Serial.println("Network Info:");
    Serial.print("SSID: ");
    Serial.println(WiFi.SSID());
    Serial.print("BSSID: ");
    Serial.print(WiFi.BSSID()[0], HEX);
    for (int i = 1; i < 6; i++) {
      Serial.print(":");
      Serial.print(WiFi.BSSID()[i], HEX);
    }
    Serial.println();
    Serial.print("RSSI: ");
    Serial.print(WiFi.RSSI());
    Serial.println(" dBm");
    Serial.println("Local Network Info:");
    Serial.print("IP Address: ");
    Serial.println(WiFi.localIP());
    Serial.print("Subnet Mask: ");
    Serial.println(WiFi.subnetMask());
    Serial.print("Gateway IP: ");
    Serial.println(WiFi.gatewayIP());
    Serial.print("DNS IP: ");
    Serial.println(WiFi.dnsIP());
  } else {
    Serial.println("WiFi connection failed");
  }
}

void loop() {
  // Your code here
}
函数作用参数返回值注意事项
WiFi.begin()开始连接到 WiFi 网络ssid:WiFi 网络名称(字符串类型);password:WiFi 密码(字符串类型)需要确保 WiFi 模块处于 STA 模式。在调用该函数后,通常需要用循环配合 WiFi.status() 来判断是否连接成功
WiFi.status()获取 WiFi 连接状态WL\_CONNECTED:已连接;WL\_IDLE\_STATUS:空闲;WL\_NO\_SSID\_AVAIL:没有可用的 SSID;WL\_SCAN\_COMPLETED:扫描完成;WL\_CONNECT\_FAILED:连接失败等可以通过循环检测该状态来判断 WiFi 连接是否成功,以及在连接丢失后进行相应的处理
WiFi.localIP()获取本地 IP 地址返回一个 IPAddress 类型的本地 IP 地址在 WiFi 连接成功后调用该函数来获取设备在局域网中的 IP 地址,用于后续的网络通信
WiFi.SSID()获取当前连接的 WiFi 网络名称返回当前连接的 WiFi 网络名称(字符串类型)在 WiFi 连接成功后调用该函数来获取设备所连接的 WiFi 网络名称,用于显示网络信息等
WiFi.BSSID()获取当前连接的 WiFi 网络的 BSSID返回一个数组,包含 6 个字节的 BSSID 值BSSID 是无线接入点(AP)的 MAC 地址,每个 AP 都有一个唯一的 BSSID。在 WiFi 连接成功后调用该函数来获取设备所连接的 AP 的 BSSID,用于显示网络信息等
WiFi.RSSI()获取当前连接的 WiFi 信号强度返回一个整数值,表示 WiFi 信号强度(单位:dBm)RSSI 值越小,表示信号强度越强。通常,RSSI 值在 -30 到 -90 dBm 之间,-30 表示很强的信号,-90 表示信号较弱。在 WiFi 连接成功后调用该函数来获取设备所连接的 WiFi 信号强度,用于显示网络信息等
WiFi.encryptionType()获取当前连接的 WiFi 加密类型返回一个整数值,表示 WiFi 加密类型(如 WEP、WPA、WPA2 等)通过该函数可以获取 WiFi 网络的加密方式,用于显示网络信息等
WiFi.subnetMask()获取子网掩码返回子网掩码(IPAddress 类型)在 WiFi 连接成功后调用该函数来获取设备所在网络的子网掩码,用于显示网络信息等
WiFi.gatewayIP()获取网关 IP 地址返回网关 IP 地址(IPAddress 类型)在 WiFi 连接成功后调用该函数来获取设备所在网络的网关 IP 地址,用于显示网络信息等
WiFi.dnsIP()获取 DNS 服务器 IP 地址返回 DNS 服务器 IP 地址(IPAddress 类型)在 WiFi 连接成功后调用该函数来获取设备所在网络的 DNS 服务器 IP 地址,用于显示网络信息等
  • 例程开始时,通过 WiFi.begin(ssid, password) 开始连接到指定的 WiFi 网络,并记录开始连接的时间。
  • 在一个循环中,使用 WiFi.status() 判断 WiFi 是否连接成功。如果在设定的超时时间内仍未连接成功,则打印超时提示并退出循环。
  • 如果 WiFi 连接成功,则通过调用上述的 WiFi 操作函数获取并打印路由器信息(如 SSID、BSSID、RSSI、加密类型)和本机网络信息(如本地 IP 地址、子网掩码、网关 IP 地址、DNS 服务器 IP 地址)。

注意事项

  • 在使用该例程之前,请确保将代码中的 "your_SSID""your_PASSWORD" 替换为实际的 WiFi 网络名称和密码。
  • 该例程假设 WiFi 模块已经正确连接到 ESP32,并且硬件连接没有问题。
  • 如果 WiFi 连接失败,可能是由于密码错误、网络不可用或 ESP32 的 WiFi 模块出现问题等原因导致的。可以通过检查 WiFi 网络状态、确认密码正确性等方式进行排查。

1.3 ESP32 AP模式实战

#include <WiFi.h>

const char *ap_ssid = "MyESP32_AP";
const char *ap_password = "12345678";

void setup() {
  Serial.begin(115200);
  delay(5000);
  Serial.println();
  Serial.println("Starting AP mode...");

  // 设置ESP32为AP模式
  WiFi.softAP(ap_ssid, ap_password);

  // 获取AP的IP地址
  IPAddress ap_ip = WiFi.softAPIP();
  Serial.print("AP IP address: ");
  Serial.println(ap_ip);

  // 打印热点信息
  Serial.print("SSID: ");
  Serial.println(ap_ssid);
  Serial.print("Password: ");
  Serial.println(ap_password);
}

void loop() {
  // 主循环中可以添加其他功能
  delay(1000);
}

1.4 随堂练习:STA模式自动重连接功能

1.5 NTP网络校时

NTP(Network Time Protocol,网络时间协议)是一种用于在网络上同步计算机时钟的时间协议。它通过在计算机系统之间交换时间信息来确保网络中的设备具有统一的时间标准,从而使系统能够准确地协调和执行任务。

NTP客户端向服务器发送时间戳请求,服务器返回包含时间戳的响应。客户端根据请求和响应的时间戳计算出延迟和偏移量,从而调整本地时钟。NTP使用复杂的算法来过滤和选择时间服务器,以及调整本地时钟。它考虑了网络延迟、时钟漂移等因素,以确保时间的准确性。NTP能够在LAN上达到亚毫秒级的精度,在WAN上也能达到几十毫秒的精度。

以下为NTP网络校时代码,请在连接网络的前提下运行。

#define NTP1  "ntp1.aliyun.com"
#define NTP2  "ntp2.aliyun.com"
#define NTP3  "ntp3.aliyun.com"

void System_SetClockByNetwork(void)
{
    if (WiFi.isConnected())
    {
        configTime(8 * 3600, 0, NTP1, NTP2,NTP3);
        Serial.println("已基于NTP服务器校时");
    }    
}

u32_t times = 0;
void loop() {
    struct tm timeinfo;
    getLocalTime(&timeinfo);
        
    Serial.println(&timeinfo, "%F %T %A"); // 格式化输出:2021-10-24 23:00:44 Sunday
    Serial.print(asctime(&timeinfo));//默认打印格式:Mon Oct 25 11:13:29 2021
    times++;
    if (times == 30)
    {
       System_SetClockByNetwork();
    }
    

    // 主循环中可以添加其他功能
    delay(1000);
}

2 UDP网络通信

特性UDPTCP
连接类型无连接面向连接
可靠性不可靠可靠
速度通常更快速度相对较慢
数据单位数据报字节流
头部大小8字节20-60字节
流量控制有(滑动窗口)
拥塞控制
适用场景实时通信、DNS查询、流媒体等网页浏览、文件传输、电子邮件、远程登录等

UDP协议是一种无需建立连接就可以发送封装的IP数据出去的方式,相比较于TCP协议来说无需先建立连接之后再进行发送数据,UDP属于一种面向事务的简单不可靠信息传送服务,我们在使用ESP32进行UDP通信时相对于TCP连接来说由于协议简单数据传输会更为迅速,但是相对的数据也容易丢失,所以UDP传输信息相对来说不那么可靠,下面我们将使用ESP32发出热点之后用电脑与ESP32进行简单的UDP通信

我们下面将编写一个包含UDP通信数据接收和数据发送的程序以方便我们学习,我们在电脑上打开UDP通信软件向ESP32发送数据,ESP32将会把接收到的数据原样返回到电脑上

#include <Arduino.h>
#include <WiFi.h>
#include <WiFiUDP.h>

// WiFi配置
const char *ssid = "your_SSID";
const char *password = "your_PASSWORD";

// UDP配置
const uint16_t udpPort = 1234; // UDP服务器端口
WiFiUDP udp;

void setup() {
  Serial.begin(115200);
  Serial.println("Starting UDP server...");

  // 连接到WiFi网络
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println("");
  Serial.println("WiFi connected");

  // 启动UDP服务器
  udp.begin(udpPort);
  Serial.print("UDP Server started on port ");
  Serial.println(udpPort);
}

void loop() {
  // 检查是否有UDP数据包到达
  int packetSize = udp.parsePacket();
  if (packetSize) {
    Serial.print("Received packet of size ");
    Serial.println(packetSize);
    Serial.print("From ");
    Serial.print(udp.remoteIP());
    Serial.print(":");
    Serial.println(udp.remotePort());

    // 读取UDP数据包
    char incomingPacket[255];
    int len = udp.read(incomingPacket, 255);
    if (len > 0) {
      incomingPacket[len] = 0; // 确保字符串以null结尾
    }
    Serial.print("Contents: ");
    Serial.println(incomingPacket);

    // 回显接收到的数据
    udp.beginPacket(udp.remoteIP(), udp.remotePort());
    udp.write(incomingPacket);
    udp.endPacket();
  }
  delay(10);
}
函数名作用形参返回值注意事项
udp.begin(port)启动UDP服务,绑定指定端口port:绑定的端口号(uint16\_t)成功返回1,失败返回0端口号应为1024-65535之间的未被占用的端口
udp.beginPacket(ip, port)开始构造一个UDP数据包,指定目标IP和端口ip:目标IP地址(const char\*或IPAddress);port:目标端口号(uint16\_t)必须在调用writeprint函数前调用此函数
udp.write(buf, len)向UDP数据包中写入数据buf:要写入的数据缓冲区(const uint8\_t\*);len:数据长度(size\_t)实际写入的数据长度数据长度不能超过UDP最大数据包大小(通常为512字节)
udp.print(data)向UDP数据包中写入数据(字符串或数字)data:要写入的数据(各种类型)实际写入的数据长度数据会被转换为字符串格式写入
udp.endPacket()发送构造好的UDP数据包成功返回1,失败返回0必须在构造完数据包后调用此函数发送数据
udp.parsePacket()检查是否有UDP数据包到达到达的数据包大小(int),无数据包时返回0需要定期调用以检查新数据包
udp.read(buf, len)从UDP数据包中读取数据buf:读取数据的缓冲区(uint8\_t\*);len:要读取的数据长度(size\_t)实际读取的数据长度缓冲区大小应足够容纳读取的数据
udp.remoteIP()获取发送方的IP地址发送方的IP地址(IPAddress)需在parsePacket检测到数据包后调用
udp.remotePort()获取发送方的端口号发送方的端口号(uint16\_t)需在parsePacket检测到数据包后调用
Avatar photo

作者 skyate

发表回复