由于 ESP32 可以同时使用以太网和 Wi-Fi 接入网络,此篇博客用来分析 ESP32 双网卡(Wi-Fi & 以太网同时存在)时使用 TCP 等协议通信会使用哪个 IP。此处会以 websocket 例程为例进行测试分析。
注:对应的 IDF commit 为 c9646ff0b (HEAD, tag: v4.3) versions: Update version to 4.3.0。
1 环境配置
打开终端,按照 ESP-IDF 编程指南 搭建 IDF 环境,然后在工程对应的终端下输入 idf.py menuconfig
,在 menuconfig -> Example Connection Configuration
里同时使能 Wi-Fi 和 ETH,配置 Wi-Fi 的 SSID & Password 以及以太网相关配置,如下:
注:由于此处使用的硬件是 ESP32 以太网开发板 ESP32-Ethernet-Kit,故以太网相关 menuconfig 使用默认配置即可。
为了方便测试,在这里同时在 menuconfig -> Component config -> LWIP -> Enable LWIP Debug
里开启 LWIP IP debug 来查看实际通信时用的具体 IP,如下:
配置完成后,就可以烧录 websocket 例程进行测试了。
2 初步测试
按上述配置编译烧写例程后,相关 log 如下:
I (5626) esp_netif_handlers: example_connect: eth ip: 192.168.1.90, mask: 255.255.255.0, gw: 192.168.1.1
I (5626) example_connect: Got IPv4 event: Interface "example_connect: eth" address: 192.168.1.90
I (6626) example_connect: Got IPv6 event: Interface "example_connect: eth" address: fe80:0000:0000:0000:7e9e:bdff:fee1:744b, type: ESP_IP6_ADDR_IS_LINK_LOCAL
I (6626) example_connect: Connected to example_connect: eth
I (6636) example_connect: - IPv4 address: 192.168.1.90
I (6636) example_connect: - IPv6 address: fe80:0000:0000:0000:7e9e:bdff:fee1:744b, type: ESP_IP6_ADDR_IS_LINK_LOCAL
I (6656) example_connect: Connected to example_connect: sta
I (6656) example_connect: - IPv4 address: 192.168.1.87
I (6666) example_connect: - IPv6 address: fe80:0000:0000:0000:7e9e:bdff:fee1:7448, type: ESP_IP6_ADDR_IS_LINK_LOCAL
I (6676) WEBSOCKET: Connecting to ws://echo.websocket.org...
| 4 | 5 | 0x00 | 40 | (v, hl, tos, len)
+-------------------------------+
| 11691 |010| 0 | (id, flags, offset)
+-------------------------------+
| 43 | 6 | 0xd15a | (ttl, proto, chksum)
+-------------------------------+
| 174 | 129 | 224 | 73 | (src)
+-------------------------------+
| 192 | 168 | 1 | 87 | (dest)
+-------------------------------+
ip4_input: p->len 40 p->tot_len 40
ip4_output_if: st1
IP header:
+-------------------------------+
| 4 | 5 | 0x00 | 50 | (v, hl, tos, len)
+-------------------------------+
| 10 |000| 0 | (id, flags, offset)
+-------------------------------+
| 255 | 6 | 0x6af1 | (ttl, proto, chksum)
+-------------------------------+
| 192 | 168 | 1 | 87 | (src)
+-------------------------------+
| 174 | 129 | 224 | 73 | (dest)
+-------------------------------+
ip4_output_if: call netif->output()
I (12226) WEBSOCKET: Sending hello 0001
I (12726) WEBSOCKET: Sending hello 0002
可以看到此时的 Wi-Fi sta 的 ip 为 192.168.1.87
,ETH 的 ip 为 192.168.1.90
。实际进行 websocket 通信时使用的 ip 为 192.168.1.87
,测试了好几次发现都是优先使用 Wi-Fi ip 进行 websocket 通信。查阅资料后发现当 ESP32 双网卡时通信优先级是由 esp_netif_t
结构体里的 route_prio
决定的,哪个网卡的 route_prio
高就会优先被用于通信。
3 进阶分析
此时通过 esp_netif_get_route_prio()
查看 websocket 例程中 Wi-Fi 和 ETH 的默认 route_prio
,相关代码如下:
- 查看 Wi-Fi 部分的
route_prio
- 查看 ETH 部分的
route_prio
注:esp_netif 结构体是内部的,不能直接打印对应的结构体成员变量的值,可以调用
esp_netif_get_route_prio()
查看该值。
加入上述代码后,继续编译烧写示例,log 如下:
W (726) example_connect: ----------------wifi route_prio = 128
I (736) example_connect: Connecting to zztest...
I (736) phy_init: phy_version 4670,719f9f6,Feb 18 2021,17:07:07
I (846) wifi:mode : sta (7c:9e:bd:e1:74:48)
I (846) wifi:enable tsf
W (846) example_connect: ----------------ETH route_prio = 64
I (6626) example_connect: Connected to example_connect: eth
I (6636) example_connect: - IPv4 address: 192.168.1.90
I (6636) example_connect: - IPv6 address: fe80:0000:0000:0000:7e9e:bdff:fee1:744b, type: ESP_IP6_ADDR_IS_LINK_LOCAL
I (6646) example_connect: Connected to example_connect: sta
I (6656) example_connect: - IPv4 address: 192.168.1.87
I (6666) example_connect: - IPv6 address: fe80:0000:0000:0000:7e9e:bdff:fee1:7448, type: ESP_IP6_ADDR_IS_LINK_LOCAL
I (6676) WEBSOCKET: Connecting to ws://echo.websocket.org...
+-------------------------------+
| 4 | 5 | 0x00 | 46 | (v, hl, tos, len)
+-------------------------------+
| 9 |000| 0 | (id, flags, offset)
+-------------------------------+
| 255 | 6 | 0x6af6 | (ttl, proto, chksum)
+-------------------------------+
| 192 | 168 | 1 | 87 | (src)
+-------------------------------+
| 174 | 129 | 224 | 73 | (dest)
+-------------------------------+
ip4_output_if: call netif->output()
ip_input: iphdr->dest 0x5701a8c0 netif->ip_addr 0x5701a8c0 (0x1a8c0, 0x1a8c0, 0x57000000)
ip4_input: packet accepted on interface st
ip4_input:
IP header:
+-------------------------------+
| 4 | 5 | 0x00 | 40 | (v, hl, tos, len)
+-------------------------------+
| 12173 |010| 0 | (id, flags, offset)
+-------------------------------+
| 31 | 6 | 0xdb78 | (ttl, proto, chksum)
+-------------------------------+
| 174 | 129 | 224 | 73 | (src)
+-------------------------------+
| 192 | 168 | 1 | 87 | (dest)
+-------------------------------+
ip4_input: p->len 40 p->tot_len 40
I (8716) WEBSOCKET: Sending hello 0001
可以看到:
- Wi-Fi sta 的
route_prio
为 128,ETH 的route_prio
为 64,此时 Wi-Fi sta 的优先级更高 - Wi-Fi sta 的 ip 为
192.168.1.87
,ETH 的 ip 为192.168.1.90
。实际进行 websocket 通信时使用的 ip 为192.168.1.87
,为 Wi-Fi sta 的 ip,与上述优先级结果吻合
此时可以尝试修改 ETH esp_netif_inherent_config_t
里的 route_prio
参数来观察是否能让 ETH 被优先使用,将 ETH 的优先级设置为 200(Wi-Fi sta 的 route_prio
此时为 128),如下:
继续编译烧写例程,log 如下:
W (726) example_connect: ----------------wifi route_prio = 128
I (736) example_connect: Connecting to zztest...
I (736) phy_init: phy_version 4670,719f9f6,Feb 18 2021,17:07:07
I (836) wifi:mode : sta (7c:9e:bd:e1:74:48)
I (836) wifi:enable tsf
W (846) example_connect: ----------------ETH route_prio = 200
I (6626) example_connect: Connected to example_connect: eth
I (6636) example_connect: - IPv4 address: 192.168.1.90
I (6636) example_connect: - IPv6 address: fe80:0000:0000:0000:7e9e:bdff:fee1:744b, type: ESP_IP6_ADDR_IS_LINK_LOCAL
I (6656) example_connect: Connected to example_connect: sta
I (6656) example_connect: - IPv4 address: 192.168.1.87
I (6666) example_connect: - IPv6 address: fe80:0000:0000:0000:7e9e:bdff:fee1:7448, type: ESP_IP6_ADDR_IS_LINK_LOCAL
I (6676) WEBSOCKET: Connecting to ws://echo.websocket.org...
+-------------------------------+
| 4 | 5 | 0x00 | 241 | (v, hl, tos, len)
+-------------------------------+
| 17973 |010| 0 | (id, flags, offset)
+-------------------------------+
| 32 | 6 | 0xc304 | (ttl, proto, chksum)
+-------------------------------+
| 174 | 129 | 224 | 73 | (src)
+-------------------------------+
| 192 | 168 | 1 | 90 | (dest)
+-------------------------------+
ip4_input: p->len 241 p->tot_len 241
I (7586) WEBSOCKET: WEBSOCKET_EVENT_CONNECTED
I (7686) WEBSOCKET: Sending hello 0000
ip4_output_if: en2
IP header:
+-------------------------------+
| 4 | 5 | 0x00 | 46 | (v, hl, tos, len)
+-------------------------------+
| 8 |000| 0 | (id, flags, offset)
+-------------------------------+
| 255 | 6 | 0x6af4 | (ttl, proto, chksum)
+-------------------------------+
| 192 | 168 | 1 | 90 | (src)
+-------------------------------+
| 174 | 129 | 224 | 73 | (dest)
+-------------------------------+
可以看到:
- Wi-Fi sta 的
route_prio
为 128,ETH 的route_prio
为 200,此时 ETH 的优先级更高 - Wi-Fi sta 的 ip 为
192.168.1.87
,ETH 的 ip 为192.168.1.90
。实际进行 websocket 通信时使用的 ip 为192.168.1.90
,为 ETH 的 ip,与上述优先级结果吻合。
此时可以证明成功通过增大 ETH 优先级来让 ESP32 优先以以太网的形式通信。
4 附录
后续做了更多的相关测试,此处总结了双网卡时默认路由如何选择,以 ETH 和 STA 为例:
- 当 ETH 和 STA 在同一个局域网,设备访问局域网地址时,数据走最后 up 的 netif; 设备访问非局域网内地址时,数据走 route_prio 值大的 netif。
- 当 ETH 和 STA 不在一个局域网里面,ETH 属于
192.168.3.x
网段, sta 属于192.168.2.x
网段,设备访问 192.168.3.5 时,就会走 ETH netif; 设备访问 192.168.2.5 时,就会走 STA netif;设备访问 10.10.10.10 时,就会走默认路由(route_prio 值大的 netif)。
netif 起来后,会根据 route_prio 值大小设置默认路由,默认路由往往是 route_prio 值大的 netif。当设备访问的地址不在路由表里面,数据就会走默认路由。
注:上述第一点中设备访问局域网地址时判定最后 up 的 netif 的方法是查看
connect.c
示例中start()
函数里的wifi_start()
和eth_start()
的先后调用顺序,使用最后配置完成的 netif。