Ethernet 转 WiFi 用于实现以太网口的设备通过 WiFi 进行无线互联。将从设备 发出的 802.3 帧数据转化为 802.11 帧数据发送出去,接收到的 802.11 帧数据转换为 802.3 帧数据发送给设备,其跳过了 ESP32 的 lwip,直接在物理层进行了数据的转发,提高了数据转发的效率。
在本 Demo 中,Ethernet 转 WiFi 主要有两种使用方式:STATION 模式和 SOFTAP 模式,
实现 Ethernet 转 WiFi 需使用带有 phy 功能的 ESP32 开发板,本 Demo 使用的是 ESP32_Ethernet_v3。对于普通的 ESP32 开发板,想要实现本 Demo 需外接 PHY,目前 ESP32 Ethernt driver 支持的 phy 有 TLK110 和 LAN8720,而在 ESP32_Ethernet_v3 中集成的是 TLK110 。
用户可以在 make menuconfig
中对 PHY 进行配置。如果使用 ESP32_Ethernet_v3 开发板,只需要使用默认配置即可;对于外接 phy 的用户,可以参照 Ethernet Demo 中的要求对 phy 进行修改。
WiFi 的配置主要是为了提高数据的吞吐率,在本 Demo 中,增加了 WiFi RX 和 TX 的 buffer 数量,并调整了 ESP32 的 CPU 时钟,具体优化信息请参照默认配置项 sdkconfig.defaults。
Note: 用户可以根据自己需要通过make menuconfig
调整相关参数,但是 STATIC_RX_BUFFER_NUM和 TX/RX BA Window(the size of WiFi Block Ack TX window) 均不要超过 16 ,否则可能会因为内存问题引起 crash,其中这些参数位于 Component config -> Wi-Fi 下。
通过如下方式获取此 Demo
$ git clone https://github.com/espressif/esp-iot-solution
$ cd esp-iot-solution
$ git submodule update --init --recursive
本 Demo 位于 esp-iot-solution/examples/eth2wifi 下,Demo 提供了 ethernet 转 WiFi 的两种模式:ESP32 作为 STATION 模式和 ESP32 作为 SOFTAP 模式。
STATION 模式是设备将 Ethernet 帧数据转化为 WiFi station 帧数据,然后将 WiFi station 数据无线发送给 AP ,从而实现类似于无线网卡的功能。
a) 使用 USB 转串口线将开发板连接到 PC 上
b) 进入 esp-iot-solution/examples/eth2wifi 所在目录
c) 选择配置
$ make menuconfig
d) 编译并烧录程序
$ make flash
e) 运行
烧录成功后,打开串口工具,此时会打印 log,根据 SSID 连接 AP,然后 PC 会发起 DHCP 请求
查看是否拿到 IP,并尝试 ping 网关来测试是否正常
SOFTAP 模式是利用 ESP32 所属的设备作为一个 AP,从而可以组建一个小型的局域网,因为交互需要 IP 信息,所以需要设备具备 DHCP Server 的能力,在本 Demo 中,使用 PC 来作为 DHCP Server。
Ubuntu 配置 dhcp 服务器需要:计算机设置静态 ip ,(充当 dhcp 服务器的计算机的 ip 需要是静态 ip,不可以是通过动态获得,设置静态 ip 需要设置在对应的网卡:如 eth0 )设置子网掩码,默认网关,以及配置 dhcp。
a) PC 设置静态 ip/子网掩码/网关
静态 ip 的配置:
设置–>系统设置–>网络–>选项–> ipv4 –>方法:手动;添加–> address, subnet, netmask.
我设置的静态ip地址:192.168.5.0,子网掩码:255.255.255.0,网关:192.168.5.1。
$ sudo vim /etc/network/interfaces
设置成如下内容:
auto lo
iface lo inet loopback
auto eth0
iface eth0 inet static
address 192.168.5.0
gateway 192.168.5.1
netmask 255.255.255.0
Note: eth0 是测试 PC 上的网卡名称,不同的电脑网卡名称不同,可通过 ipconfig 查看,替换成自己的网卡名称即可
配置完成后重启网络服务:
sudo /etc/init.d/networking restart
b) 配置 DHCP 服务器
$ sudo apt-get install isc-dhcpd-server
$ sudo vim /etc/default/isc-dhcp-server
设置使用网卡(我的是 eth0 )作为 DHCP Server
$ INTERFACES = "eth0"
$ sudo vim /etc/dhcp/dhcpd.conf
在文件末尾添加如下几项
subnet 192.168.5.0 netmask 255.255.255.0 {
range 192.168.5.0 192.168.5.100;
# option domain-name-servers 210.30.100.2;
# option domain-name "internal.example.org";
# option routers 172.6.1.1;
option broadcast-address 192.168.5.255;
default-lease-time 600;
max-lease-time 7200;
}
$ sudo /etc/init.d/isc-dhcpd-server restart
Note: /etc/dhcp/dhcpd.conf中的 subnet 一定要包含 server 本机的 ip 在内,否则启动不了
最后可以通过 ifconfig eth0 查看是否静态 IP 设置成功
c) 烧写步骤
烧写 softap 的步骤与 STA 基本相同,只需要在第 4 步时将 Ethernet to wifi station forwarding data 设置为 n 即为 softap 模式。
d) 运行
ESP32 代码中将网络中的各种状态全部封装成了事件,所以 Ethernet 转 WiFi 的核心是网络事件( Event )的处理;在本次 Demo 中,主要做了如下事件的处理:
static esp_err_t event_handler(void* ctx, system_event_t* event)
{
switch (event->event_id) {
case SYSTEM_EVENT_STA_START:
printf("SYSTEM_EVENT_STA_START\r\n");
break;
case SYSTEM_EVENT_STA_CONNECTED:
printf("SYSTEM_EVENT_STA_CONNECTED\r\n");
wifi_is_connected = true;
esp_wifi_internal_reg_rxcb(ESP_IF_WIFI_STA, (wifi_rxcb_t)tcpip_adapter_sta_input_eth_output);
对于 SYSTEM_EVENT_STA_START 事件,我们并没有进行常见的 esp_wifi_connect() 操作,而是放在 Ethernet task 中进行,这么做主要是为了获取 PC 的 MAC 地址,然后将 sta 的 MAC 替换为 PC 的 MAC。
对于 SYSTEM_EVENT_STA_CONNECTED 事件,ESP32 连接上 AP 后,会进入此事件,在一般的网络交互中,此时开始进行 DHCP 请求操作,但因为此时 ESP32 只是作为一个管道,所以在这里我们并不是将数据抛给 lwip 而是将数据“窃取”并转发 给 Ethernet。
case SYSTEM_EVENT_STA_GOT_IP:
printf("SYSTEM_EVENT_STA_GOT_IP\r\n");
break;
case SYSTEM_EVENT_STA_DISCONNECTED:
printf("SlYSTEM_EVENT_STA_DISCONNECTED\r\n");
wifi_is_connected = false;
esp_wifi_internal_reg_rxcb(ESP_IF_WIFI_STA, NULL);
esp_wifi_connect();
break;
对于事件 SYSTEM_EVENT_STA_GOT_IP ,好吧,这个事件在这个 Demo 是不会被触发了,因为我们没使用 lwip 中的 DHCP client 功能。
当事件 SYSTEM_EVENT_STA_DISCONNECTED 被触发,我们要做的是让 Ethernet 收到的数据将不会通过 WiFi 进行转发,同时重连网络。
case SYSTEM_EVENT_AP_STACONNECTED:
printf("SYSTEM_EVENT_AP_STACONNECTED\r\n");
wifi_is_connected = true;
esp_wifi_internal_reg_rxcb(ESP_IF_WIFI_AP, (wifi_rxcb_t)tcpip_adapter_ap_input_eth_output);
break;
case SYSTEM_EVENT_AP_STADISCONNECTED:
printf("SYSTEM_EVENT_AP_STADISCONNECTED\r\n");
wifi_is_connected = false;
esp_wifi_internal_reg_rxcb(ESP_IF_WIFI_AP, NULL);
break;
作为 AP 时事件处理和 STA 相同,区别是这里设置为 AP 模式,而不是 STA 模式。
case SYSTEM_EVENT_ETH_CONNECTED:
printf("SYSTEM_EVENT_ETH_CONNECTED\r\n");
ethernet_is_connected = true;
break;
case SYSTEM_EVENT_ETH_DISCONNECTED:
printf("SYSTEM_EVENT_ETH_DISCONNECTED\r\n");
ethernet2wifi_mac_status_set(false);
ethernet_is_connected = false;
break;
Ethernet 事件的处理主要是探测以太网是否连接或者断开,同时在断开时,ethernet2wifi_mac_status_set 还要置为 false,以保证下次重新连接时重新替换 sta 的 mac 地址。
因为 Ethernet 转 WiFi 牵涉到 3 个 MAC(PC、Etherner 和 STA/AP ),而网络很多操作需要校验 MAC,为了确保在网络交互时 MAC 还能保持 PC 的 MAC,在本 Demo 中将 STA 的 MAC 设置为 PC 的 MAC。
if (!ethernet2wifi_mac_status_get()) {
memcpy(eth_mac, (uint8_t*)msg.buffer + 6, sizeof(eth_mac));
ESP_ERROR_CHECK(esp_wifi_start());
#ifdef CONFIG_ETH_TO_STATION_MODE
esp_wifi_set_mac(WIFI_IF_STA, eth_mac);
esp_wifi_connect();
#else
esp_wifi_set_mac(WIFI_IF_AP, eth_mac);
#endif
ethernet2wifi_mac_status_set(true);
}
在 SYSTEM_EVENT_ETH_CONNECTED 事件被触发之后,PC 开始发送包含 MAC 地址的网络数据,如下图所示:
我们截取之后将 STA 的 MAC 地址设置为 PC 的 MAC 地址,此时再连接 AP,并将状态置为 true,以避免下次 PC 发送数据时重复设置 MAC。
在连接到 AP 之后,PC 再进行 DHCP 请求时,sta 就会使用 PC的 MAC 地址和 AP 进行交互,以此保证正常通讯。
我们使用了 iperf 对吞吐率进行了简单的测试,在开放工作环境下,其 TCP 吞吐量稳定在 45Mbps 左右。但周围环境会对 WiFi 吞吐量造成较大影响,在环境较差时,WiFi 吞吐量可能会下降。