esp-rs / esp-idf-svc

Type-Safe Rust Wrappers for various ESP-IDF services (WiFi, Network, Httpd, Logging, etc.)
https://docs.esp-rs.org/esp-idf-svc/
Apache License 2.0
309 stars 175 forks source link

Weird behaviour when both Eth and Wifi are started with static IP config #426

Closed thetek42 closed 4 months ago

thetek42 commented 5 months ago

In our application, we are starting both Ethernet and Wi-Fi. When a static IP is configured for both interfaces and an Ethernet cable is connected to the ESP32, connections to a server cannot be made under certain circumstances.

Notes:

These are the cases that I observed:

The behaviour for "connect to Wi-Fi but not to Ethernet" (by not having an Ethernet cable connected) has the exact reverse effect: starting Wi-Fi and then Ethernet causes the connection to fail, but the reverse causes the connection to succeed.

Thus, the interface that was started last "determines" which interface can work with static IP. Again, for DHCP, all of this does not matter since it works all the time, no matter in which order the interfaces were started.

Below is a piece of sample code that can be used to (hopefully) reproduce the issue. Feel free to play around a bit by moving stuff around (e.g. putting Wi-Fi after Ethernet or putting the eth.wait_*() block after the Wi-Fi code).

Example code ```rust #![allow(unused_imports)] use std::io::{Read, Write}; use std::net::{Ipv4Addr, TcpStream}; use esp_idf_svc::eth::{EspEth, EthDriver, RmiiEthChipset, RmiiClockConfig, BlockingEth, AsyncEth}; use esp_idf_svc::eventloop::EspSystemEventLoop; use esp_idf_svc::hal::gpio::{self, PinDriver}; use esp_idf_svc::hal::prelude::Peripherals; use esp_idf_svc::ipv4::{ClientConfiguration, Configuration, ClientSettings, Subnet, Mask, DHCPClientSettings, RouterConfiguration}; use esp_idf_svc::log::EspLogger; use esp_idf_svc::netif::{NetifConfiguration, EspNetif}; use esp_idf_svc::timer::EspTaskTimerService; use esp_idf_svc::wifi::{self, WifiDriver, EspWifi, BlockingWifi, AuthMethod, AsyncWifi}; fn main() -> anyhow::Result<()> { esp_idf_svc::sys::link_patches(); EspLogger::initialize_default(); let peripherals = Peripherals::take()?; let pins = peripherals.pins; let sys_loop = EspSystemEventLoop::take()?; let timer_service_wifi = EspTaskTimerService::new()?; let timer_service_eth = EspTaskTimerService::new()?; let mut eth_pwr = PinDriver::output(pins.gpio5)?; let mut clk_en = PinDriver::output(pins.gpio4)?; eth_pwr.set_low()?; clk_en.set_low()?; std::thread::sleep(std::time::Duration::from_millis(100)); eth_pwr.set_high()?; std::thread::sleep(std::time::Duration::from_millis(10)); clk_en.set_high()?; std::thread::sleep(std::time::Duration::from_millis(10)); log::info!("--- EthDriver"); let eth_driver = EthDriver::new( peripherals.mac, pins.gpio25, pins.gpio26, pins.gpio27, pins.gpio23, pins.gpio22, pins.gpio21, pins.gpio19, pins.gpio18, RmiiClockConfig::::Input(pins.gpio0), Some(pins.gpio33), RmiiEthChipset::LAN87XX, None, sys_loop.clone(), )?; log::info!("--- EspNetif"); let netif = EspNetif::new_with_conf(&NetifConfiguration { ip_configuration: Configuration::Client(ClientConfiguration::Fixed(ClientSettings { // using a different ip address than wi-fi does not make it work. // ip: "192.168.178.234".parse()?, ip: "192.168.178.235".parse()?, subnet: Subnet { gateway: "192.168.178.1".parse()?, mask: Mask(24), }, dns: Some("192.168.178.1".parse()?), secondary_dns: None, })), ..NetifConfiguration::eth_default_client() })?; log::info!("--- EspEth"); let eth = EspEth::wrap_all(eth_driver, netif)?; log::info!("--- AsyncEth"); let mut eth = AsyncEth::wrap(eth, sys_loop.clone(), timer_service_eth)?; esp_idf_svc::hal::task::block_on(async { log::info!("--- eth.start()"); eth.start().await?; log::info!("--- eth.wait_connected()"); eth.wait_connected().await?; log::info!("--- eth.wait_netif_up()"); eth.wait_netif_up().await })?; log::info!("--- WifiDriver"); let wifi_driver = WifiDriver::new(peripherals.modem, sys_loop.clone(), None)?; let netif_sta = EspNetif::new_with_conf(&NetifConfiguration { ip_configuration: Configuration::Client(ClientConfiguration::Fixed(ClientSettings { ip: "192.168.178.234".parse()?, subnet: Subnet { gateway: "192.168.178.1".parse()?, mask: Mask(24), }, dns: Some("192.168.178.1".parse()?), secondary_dns: None, })), ..NetifConfiguration::wifi_default_client() })?; let netif_ap = EspNetif::new_with_conf(&NetifConfiguration::wifi_default_router())?; log::info!("--- EspWifi"); let wifi = EspWifi::wrap_all(wifi_driver, netif_sta, netif_ap)?; log::info!("--- AsyncWifi"); let mut wifi = AsyncWifi::wrap(wifi, sys_loop.clone(), timer_service_wifi)?; log::info!("--- wifi.set_configuration()"); wifi.set_configuration(&wifi::Configuration::Client(wifi::ClientConfiguration { ssid: "".try_into().unwrap(), bssid: None, auth_method: AuthMethod::WPA2Personal, password: "".try_into().unwrap(), channel: None, }))?; log::info!("--- wifi.start()"); esp_idf_svc::hal::task::block_on(wifi.start())?; log::info!("--- TcpStream"); let mut stream = TcpStream::connect((Ipv4Addr::new(93, 184, 215, 14), 80))?; stream.write_all(b"GET / HTTP/1.1\r\nHost: example.com\r\n\r\n")?; log::info!("--- response"); let mut buf = [0; 512]; loop { let read = stream.read(&mut buf)?; print!("{}", core::str::from_utf8(&buf[..read])?); if read == 0 { break; } } log::info!("--- done"); Ok(()) } ```
ivmarkov commented 5 months ago

Do any of your problems happen, if you replace "example.com" with an IP address?

thetek42 commented 5 months ago

Yes. It does not matter if I use example.com or the corresponding IP address.

ivmarkov commented 5 months ago

You are sure 100% about that (as in you have used IpAddr and not a string that might still trigger DNS resolution somehow)?

ivmarkov commented 5 months ago

Your example is way too big and I have difficulties understanding what exactly you are trying to achieve...

This seems very odd though:

let netif_ap = EspNetif::new_with_conf(&NetifConfiguration {
        ip_configuration: Configuration::Client(ClientConfiguration::DHCP(DHCPClientSettings {
            hostname: None,
        })),
        ..NetifConfiguration::wifi_default_router()
    })?;

How is this supposed to even work?

thetek42 commented 5 months ago

How is this supposed to even work?

That must have snuck in there somehow. I had it set to the correct configuration previously, but it seems like I somehow managed to clone the config into the wrong place 🤷. It does not affect the result though.

ivmarkov commented 5 months ago

Even Fixed is wrong. You can't set a client config on an access point and expect not to have issues w.r.t. routing on IP level. Why are you configuring the access point in the first place?

Can you: a) try to simplify this example to the bare minimum. Remove everything not necessary, including dns resolutions and whatnot b) try to report one problem only at first and an example about this single one problem.

thetek42 commented 5 months ago

You are sure 100% about that (as in you have used IpAddr and not a string that might still trigger DNS resolution somehow)?

I specified it as (Ipv4Addr::new(93, 184, 215, 14), 80).

thetek42 commented 5 months ago

I updated the code in the original post.

thetek42 commented 5 months ago

Also, this issue has nothing to do with the Access Point. EspWifi::wrap_all simply requires a netif for the AP to be present.

ivmarkov commented 5 months ago

Maybe. But it has a default configuration and you are changing it. Maybe it does not matter, as the Wifi is not in Mixed mode, but maybe it does.

My point is the following: if you leave the example as-is, and with all the cases you have enumerated, it might take me days, if not weeks to get there.

And if/when I get there, I'll start by anyway with eliminating every single line of code in the example which should not be there, which is additional unknown variable that only confuses and obfuscates the issue. Like the DNS issue. Or configuring the AP. Etc. And then try to tackle just one case of the ones you have enumerated and which should work, but isn't.

... or you could try to do it, and then I can help with thinking / brainstorming. I think it might be faster that way. :)

ivmarkov commented 5 months ago

I updated the code in the original post.

In your "simplified example"... why are you setting the eth and wifi-sta to the same IP, namely "192.168.178.234"?

ivmarkov commented 5 months ago

Also, for the netif_ap (I know it should not affect anything, but still) - can you please use the default network configuration, which would then be different from your 192.168.178.1/24, that you are setting everywhere.

ivmarkov commented 4 months ago

@thetek42 What happens once you assign different static IPs to Eth and Wifi?

thetek42 commented 4 months ago

Sorry for the delay, I was busy the last couple of days.

Using the default values for the AP netif does not change anything.

Using a different IP address for Wi-Fi and Ethernet does in fact change the behaviour, except that it makes it worse. When both have a different static IP address configured, no matter in which order Wi-Fi and Ethernet are initialized, the connection to the server is not possible.

Most of the time, the device does not even show up in my router's user interface if that happens. However, I was able to observe it show up in the list of connections once, and then never again. In that incident, it showed the "device name" as "PC-192-168-178-235", even though the IP address was listed as 192.168.178.234 -- while the static IP address configuration for Ethernet was set to 192.168.178.235 and the IP for Wi-Fi was set to 192.168.178.234. I am unsure if this was just a bug by the router or if this is the actual behaviour. The fact that the device does not show up in the list makes sense considering that running it through Wireshark yielded no transmitted packets, but the fact that it turned up in the list exactly once does not make sense to me.

The more I investigate this issue, the more confused I am.

Again, I will update the code in the original post in order to use of default values for AP config and two different static IP addresses.

ivmarkov commented 4 months ago

One little detail that is interesting: what list do you mean? The router does not keep any "list" of devices with static IP addresses (let alone knowing their host names) as it is just not aware of these devices.

More advanced routers might produce a "list" based on tx/rx packet statistics and potentially, based on nat statistics.

Btw: have you tried pinging the eth and wifi interfaces from another pc?

Another experiment would be to assign a static ip to the wifi interface which is from a completely different subnet (even if the wifi won't be reachable this way).

thetek42 commented 4 months ago

I was talking about the list that the router provides in the user interface of its website.

thetek42 commented 4 months ago

A different IP in a different subnet for the Wi-Fi interface does not have any effect.

Pinging the Ethernet interface does work.

thetek42 commented 4 months ago

Also, when setting the Ethernet IP address to .235 and the Wi-Fi IP address to .234, I can ping both, even though Wi-Fi is not connected.

ivmarkov commented 4 months ago

I was talking about the list that the router provides in the user interface of its website.

See above. :) I would not trust this list for static IPs at all. :)

ivmarkov commented 4 months ago

Also, when setting the Ethernet IP address to .235 and the Wi-Fi IP address to .234, I can ping both, even though Wi-Fi is not connected.

OK, hold on. So just to summarize. What you are saying:

Are you sure the Wifi is not connected? Otherwise, how pinging that IP would work?! Are you sure you don't have an IP conflict in your net, and you are not pinging something else, which is not the Esp?

thetek42 commented 4 months ago

I would not trust this list for static IPs at all. :)

I don't! :) I was just looking to see if the router recognized it at all. The weird results are to be expected.

thetek42 commented 4 months ago

I am 100% sure the Wi-Fi is not connected. The SSID and passphrase are both set to an empty string, and .connect() isn't even called. There is no NVS in which Wi-Fi credentials might be stored. The .234 and .235 IP addresses are not taken up by any other device.

Interestingly enough, this double IP phenomenon thingy only happens sometimes.

ivmarkov commented 4 months ago

1) Ok let's pretend that the second (wifi) IP was never pingable (or else I should start believing in miracles, OR there is a nasty bug/behavior in esp-idf somewhere, where the wifi netif is operational even though its phy layer is not - as in it gets ethernet packets from the eth phy layer which sounds very unlikely either).

2) let's also forget about the router list, shall we? If you have used that instead of pinging the static ips from the outside, this had been wrong all along.

Let's concentrate on the one remaining issue: from inside the esp, can you ping the gateway on the eth interface? And then - as a second step only - does opening the socket work?

thetek42 commented 4 months ago

Obviously, I used the ping command for pinging the esp32.

It seems that it only happens for approximately 15-20 secs after flashing a new firmware on the device with a different static IP than the previous firmware. After that timespan, it does not occur again. The only way I can explain that is that there is some nasty caching going on somewhere, not neccesarily on esp-idf's side.

Pinging the gateway from the esp32 provides the exact same behaviour as opening a socket to somewhere. For this, I used the EspPing. However, I was a bit unsure as to what to set the interface to, so I left it at 0. From what I gathered, you are supposed to put the netif index in there, but obtaining the netif index via .get_index() and plugging that into EspPing yielded to no ping succeeding, even when only Ethernet was enabled in the code. Is there something I missed or misunderstood?

ivmarkov commented 4 months ago

Pinging the gateway from the esp32 provides the exact same behaviour as opening a socket to somewhere. For this, I used the EspPing. However, I was a bit unsure as to what to set the interface to, so I left it at 0. From what I gathered, you are supposed to put the netif index in there, but obtaining the netif index via .get_index() and plugging that into EspPing yielded to no ping succeeding, even when only Ethernet was enabled in the code. Is there something I missed or misunderstood?

By the way, what are you trying to ping, and did you check that it is pingable in the first place?

thetek42 commented 4 months ago

I am pinging the gateway (192.168.178.1).

ivmarkov commented 4 months ago

Ok assuming it is pingable from inside (99.9% routers are, but you never know and you should check it from a pc), can you try with just the ethernet connection, without even creating the wifi driver? Until we get a reliable ping, we cannot progress any further. Try with get_index + 1, or with index = 1 and/or = 2 until it works.

ivmarkov commented 4 months ago

And just to confirm again: None of these issues happen if you don't create the wifi driver in the first place? But once you create it, even if you dont start it, issues start on the eth interface?

thetek42 commented 4 months ago
ivmarkov commented 4 months ago

@thetek42 Weird. Did you just delete your last comment from 5 minutes ago?

thetek42 commented 4 months ago

Yes, I still had to fix one small part of the code.

I investigated the issue a bit further by trying it in C. It works perfectly there.

C Code ```c #include #include #include #include #include #include #include #include #include #include #include #include #include #define WIFI_SSID "" #define WIFI_PASS "" #define ETH_PHY_ADDR -1 #define ETH_PIN_RESET 33 #define ETH_PIN_MDC 23 #define ETH_PIN_MDIO 18 #define ETH_PIN_PWR 5 #define ETH_PIN_CLK 4 #define GATEWAY "192.168.178.1" #define SUBNET "255.255.255.0" #define ETH_IP "192.168.178.234" #define WIFI_IP "192.168.178.234" #define DNS_1 "192.168.178.1" #define DNS_2 "192.168.178.1" #define HTTP_HOST "example.com" #define HTTP_PORT 80 #define WIFI_MAX_RETRY 3 #define WIFI_CONNECTED_BIT BIT0 #define WIFI_FAIL_BIT BIT1 static void init_netif (void); static void nvs_init (void); static void set_static_ip (esp_netif_t *netif, const char *ip); static void wifi_init (void); static void wifi_event_handler (void *arg, esp_event_base_t event_base, int32_t event_id, void *event_data); static void eth_init (void); static void eth_event_handler (void *arg, esp_event_base_t event_base, int32_t event_id, void *event_data); static void resolve_hostname (void); static void send_http_request (void); static esp_err_t http_event_handler (esp_http_client_event_t *ev); static size_t s_wifi_retry_num = 0; static EventGroupHandle_t s_wifi_event_group = NULL; const char *TAG = "main"; void app_main (void) { nvs_init (); init_netif (); eth_init (); wifi_init (); resolve_hostname (); send_http_request (); } static void nvs_init (void) { esp_err_t ret; ret = nvs_flash_init (); if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) { ESP_ERROR_CHECK (nvs_flash_erase ()); ret = nvs_flash_init (); } ESP_ERROR_CHECK (ret); } static void init_netif (void) { ESP_ERROR_CHECK (esp_netif_init ()); ESP_ERROR_CHECK (esp_event_loop_create_default ()); } static void set_static_ip (esp_netif_t *netif, const char *ip) { esp_netif_ip_info_t ip_info; esp_netif_dns_info_t dns_info; ESP_ERROR_CHECK (esp_netif_dhcpc_stop (netif)); memset (&ip_info, 0, sizeof (esp_netif_ip_info_t)); ip_info.ip.addr = ipaddr_addr (ip); ip_info.gw.addr = ipaddr_addr (GATEWAY); ip_info.netmask.addr = ipaddr_addr (SUBNET); ESP_ERROR_CHECK (esp_netif_set_ip_info (netif, &ip_info)); dns_info.ip.u_addr.ip4.addr = ipaddr_addr (DNS_1); dns_info.ip.type = IPADDR_TYPE_V4; ESP_ERROR_CHECK (esp_netif_set_dns_info (netif, ESP_NETIF_DNS_MAIN, &dns_info)); dns_info.ip.u_addr.ip4.addr = ipaddr_addr (DNS_2); ESP_ERROR_CHECK (esp_netif_set_dns_info (netif, ESP_NETIF_DNS_BACKUP, &dns_info)); } /* wifi *******************************************************************************************/ static void wifi_init (void) { esp_event_handler_instance_t instance_any_id, instance_got_ip; wifi_init_config_t init_cfg; wifi_config_t cfg; esp_netif_t *netif; EventBits_t bits; s_wifi_event_group = xEventGroupCreate(); netif = esp_netif_create_default_wifi_sta (); set_static_ip (netif, WIFI_IP); init_cfg = (wifi_init_config_t) WIFI_INIT_CONFIG_DEFAULT (); ESP_ERROR_CHECK (esp_wifi_init (&init_cfg)); ESP_LOGI (TAG, "-- wifi inititalized"); ESP_ERROR_CHECK (esp_event_handler_instance_register (WIFI_EVENT, ESP_EVENT_ANY_ID, &wifi_event_handler, netif, &instance_any_id)); ESP_ERROR_CHECK (esp_event_handler_instance_register (IP_EVENT, IP_EVENT_STA_GOT_IP, &wifi_event_handler, netif, &instance_got_ip)); cfg = (wifi_config_t) { .sta = { .ssid = WIFI_SSID, .password = WIFI_PASS, .threshold.authmode = WIFI_AUTH_WPA2_PSK, }, }; ESP_ERROR_CHECK (esp_wifi_set_mode (WIFI_MODE_STA)); ESP_ERROR_CHECK (esp_wifi_set_config (WIFI_IF_STA, &cfg)); ESP_ERROR_CHECK (esp_wifi_start ()); ESP_LOGI (TAG, "-- wifi started"); // bits = xEventGroupWaitBits (s_wifi_event_group, WIFI_CONNECTED_BIT | WIFI_FAIL_BIT, pdFALSE, pdFALSE, portMAX_DELAY); // if (bits & WIFI_CONNECTED_BIT) { // ESP_LOGI (TAG, "-- wifi connected"); // } else if (bits & WIFI_FAIL_BIT) { // ESP_LOGE (TAG, "-- wifi connection failed"); // } else { // ESP_LOGE (TAG, "-- wifi unexpected event"); // } } static void wifi_event_handler (void *arg, esp_event_base_t event_base, int32_t event_id, void *event_data) { if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START) { esp_wifi_connect (); } else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED) { if (s_wifi_retry_num < WIFI_MAX_RETRY) { esp_wifi_connect (); s_wifi_retry_num += 1; ESP_LOGW (TAG, "-- wifi retry connect to ap"); } else { xEventGroupSetBits (s_wifi_event_group, WIFI_FAIL_BIT); } ESP_LOGE (TAG, "-- wifi connect to ap fail"); } else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) { ip_event_got_ip_t *event = (ip_event_got_ip_t *) event_data; ESP_LOGI (TAG, "-- wifi got ip " IPSTR, IP2STR (&event->ip_info.ip)); s_wifi_retry_num = 0; xEventGroupSetBits (s_wifi_event_group, WIFI_CONNECTED_BIT); } } /* ethernet ***************************************************************************************/ static void eth_init (void) { eth_esp32_emac_config_t esp32_emac_config; esp_eth_netif_glue_handle_t glue; esp_netif_config_t netif_cfg; eth_mac_config_t mac_config; eth_phy_config_t phy_config; esp_eth_config_t config; esp_eth_handle_t handle; esp_eth_mac_t *mac; esp_eth_phy_t *phy; esp_netif_t *netif; ESP_ERROR_CHECK (gpio_set_direction (ETH_PIN_PWR, GPIO_MODE_OUTPUT)); ESP_ERROR_CHECK (gpio_set_direction (ETH_PIN_CLK, GPIO_MODE_OUTPUT)); ESP_ERROR_CHECK (gpio_set_level (ETH_PIN_PWR, 0)); ESP_ERROR_CHECK (gpio_set_level (ETH_PIN_CLK, 0)); vTaskDelay (100 / portTICK_PERIOD_MS); ESP_ERROR_CHECK (gpio_set_level (ETH_PIN_PWR, 1)); vTaskDelay (10 / portTICK_PERIOD_MS); ESP_ERROR_CHECK (gpio_set_level (ETH_PIN_CLK, 1)); vTaskDelay (10 / portTICK_PERIOD_MS); ESP_LOGI (TAG, "-- eth gpio initialized"); mac_config = (eth_mac_config_t) ETH_MAC_DEFAULT_CONFIG (); phy_config = (eth_phy_config_t) ETH_PHY_DEFAULT_CONFIG (); phy_config.phy_addr = ETH_PHY_ADDR; phy_config.reset_gpio_num = ETH_PIN_RESET; esp32_emac_config = (eth_esp32_emac_config_t) ETH_ESP32_EMAC_DEFAULT_CONFIG (); esp32_emac_config.smi_mdc_gpio_num = ETH_PIN_MDC; esp32_emac_config.smi_mdio_gpio_num = ETH_PIN_MDIO; mac = esp_eth_mac_new_esp32 (&esp32_emac_config, &mac_config); phy = esp_eth_phy_new_lan87xx (&phy_config); config = (esp_eth_config_t) ETH_DEFAULT_CONFIG (mac, phy); ESP_ERROR_CHECK (esp_eth_driver_install (&config, &handle)); ESP_LOGI (TAG, "-- eth initialized"); netif_cfg = (esp_netif_config_t) ESP_NETIF_DEFAULT_ETH (); netif = esp_netif_new (&netif_cfg); set_static_ip (netif, ETH_IP); glue = esp_eth_new_netif_glue (handle); ESP_ERROR_CHECK (esp_netif_attach (netif, glue)); ESP_LOGI (TAG, "-- eth netif initialized"); ESP_ERROR_CHECK (esp_event_handler_register (ETH_EVENT, ESP_EVENT_ANY_ID, ð_event_handler, netif)); ESP_ERROR_CHECK (esp_event_handler_register (IP_EVENT, IP_EVENT_ETH_GOT_IP, ð_event_handler, netif)); ESP_LOGI (TAG, "-- eth starting"); ESP_ERROR_CHECK (esp_eth_start (handle)); } static void eth_event_handler (void *arg, esp_event_base_t event_base, int32_t event_id, void *event_data) { ip_event_got_ip_t *event; if (event_base == ETH_EVENT && event_id == ETHERNET_EVENT_CONNECTED) { ESP_LOGI (TAG, "-- eth connected"); } else if (event_base == ETH_EVENT && event_id == ETHERNET_EVENT_DISCONNECTED) { ESP_LOGW (TAG, "-- eth disconnected"); } else if (event_base == ETH_EVENT && event_id == ETHERNET_EVENT_START) { ESP_LOGI(TAG, "-- eth started"); } else if (event_base == IP_EVENT && event_id == IP_EVENT_ETH_GOT_IP) { event = (ip_event_got_ip_t *) event_data; ESP_LOGI(TAG, "-- eth got ip " IPSTR, IP2STR (&event->ip_info.ip)); } } /* http and dns ***********************************************************************************/ static void resolve_hostname (void) { struct sockaddr_in *sockaddr; struct addrinfo *addr_info; struct addrinfo hints; int res; memset (&hints, 0, sizeof (hints)); hints.ai_family = AF_UNSPEC, hints.ai_socktype = SOCK_STREAM, res = getaddrinfo (HTTP_HOST, NULL, &hints, &addr_info); if (res != 0 || addr_info == NULL) { ESP_LOGE (TAG, "-- failed to resolve dns"); } else { if (addr_info->ai_family == AF_INET) { sockaddr = (struct sockaddr_in *) addr_info->ai_addr; ESP_LOGI (TAG, "-- dns resolved ip %s", ipaddr_ntoa ((const ip_addr_t *) &sockaddr->sin_addr.s_addr)); } else { ESP_LOGI (TAG, "-- dns resolved, but not ipv4"); } } } static void send_http_request (void) { esp_http_client_config_t cfg; esp_http_client_handle_t cli; cfg = (esp_http_client_config_t) { .host = HTTP_HOST, .port = HTTP_PORT, .method = HTTP_METHOD_GET, .path = "/", .event_handler = http_event_handler, }; cli = esp_http_client_init (&cfg); ESP_ERROR_CHECK (esp_http_client_perform (cli)); ESP_LOGI (TAG, "-- http ok, status %d, len %" PRId64, esp_http_client_get_status_code (cli), esp_http_client_get_content_length (cli)); esp_http_client_cleanup (cli); } static esp_err_t http_event_handler (esp_http_client_event_t *ev) { return ESP_OK; } ```
sdkconfig.defaults ``` ETH_USE_ESP32_EMAC=y ```
ivmarkov commented 4 months ago

OK, that's great news. The question is now, what is different in the Rust code, if you compare it with C...?

thetek42 commented 4 months ago

I don't see any differences. It starts Ethernet, then it starts Wi-Fi (without credentails and without waiting for it connect). Both Ethernet and Wi-Fi have a static IP assigned to them (the same static IP, in fact). Then, I try to resolve a hostname via DNS and try to send a HTTP request to it. Both of those work.

ivmarkov commented 4 months ago

No I mean the rust code in esp-idf-svc which is driving the netif stack, the wifi and the ethernet drivers. There must be a difference. And sorry for persisting that you look at it instead of me doing a deep dive. :( I simply don't have the physical time to do it these days, as I'm chasing other problems.

thetek42 commented 4 months ago

Ok. I figured out what the issue is. If you initialize the netif like it is currently done in esp-idf-svc (with different flags and ip_info set), it does not work. However, if you do it just like the official C example here (that is, initializing the netif with dhcp enabled, and then disabling it and setting the static IP config afterwards), it works perfectly fine. Below is a git diff of what I had to get it to work. I guess that the AP/dhcps code in there is not entirely correct, but I didn't bother with that since I was just trying to get it to work in the first place. I also don't know how much of this exactly is necessary.

diff --git a/src/netif.rs b/src/netif.rs
index 0bc1b784d..72a0daf1e 100644
--- a/src/netif.rs
+++ b/src/netif.rs
@@ -235,38 +235,13 @@ impl EspNetif {
         {
             ipv4::Configuration::Client(ref ip_conf) => (
                 esp_netif_inherent_config_t {
-                    flags: match ip_conf {
-                        ipv4::ClientConfiguration::DHCP(_) => {
-                            esp_netif_flags_ESP_NETIF_DHCP_CLIENT
-                                | esp_netif_flags_ESP_NETIF_FLAG_GARP
-                                | esp_netif_flags_ESP_NETIF_FLAG_EVENT_IP_MODIFIED
-                        }
-                        ipv4::ClientConfiguration::Fixed(_) => {
-                            esp_netif_flags_ESP_NETIF_FLAG_AUTOUP
-                        }
-                    },
+                    flags: esp_netif_flags_ESP_NETIF_DHCP_CLIENT
+                        | esp_netif_flags_ESP_NETIF_FLAG_GARP
+                        | esp_netif_flags_ESP_NETIF_FLAG_EVENT_IP_MODIFIED,
                     mac: initial_mac,
                     ip_info: ptr::null(),
-                    get_ip_event: match ip_conf {
-                        ipv4::ClientConfiguration::DHCP(_) => {
-                            if conf.stack == NetifStack::Sta {
-                                ip_event_t_IP_EVENT_STA_GOT_IP
-                            } else {
-                                0
-                            }
-                        }
-                        ipv4::ClientConfiguration::Fixed(_) => 0,
-                    },
-                    lost_ip_event: match ip_conf {
-                        ipv4::ClientConfiguration::DHCP(_) => {
-                            if conf.stack == NetifStack::Sta {
-                                ip_event_t_IP_EVENT_STA_LOST_IP
-                            } else {
-                                0
-                            }
-                        }
-                        ipv4::ClientConfiguration::Fixed(_) => 0,
-                    },
+                    get_ip_event: ip_event_t_IP_EVENT_STA_GOT_IP,
+                    lost_ip_event: ip_event_t_IP_EVENT_STA_LOST_IP,
                     if_key: c_if_key.as_c_str().as_ptr() as _,
                     if_desc: c_if_description.as_c_str().as_ptr() as _,
                     route_prio: conf.route_priority as _,
@@ -297,11 +272,8 @@ impl EspNetif {
             ),
             ipv4::Configuration::Router(ref ip_conf) => (
                 esp_netif_inherent_config_t {
-                    flags: (if ip_conf.dhcp_enabled {
-                        esp_netif_flags_ESP_NETIF_DHCP_SERVER
-                    } else {
-                        0
-                    }) | esp_netif_flags_ESP_NETIF_FLAG_AUTOUP,
+                    flags: esp_netif_flags_ESP_NETIF_DHCP_SERVER
+                        | esp_netif_flags_ESP_NETIF_FLAG_AUTOUP,
                     mac: initial_mac,
                     ip_info: ptr::null(),
                     get_ip_event: 0,
@@ -324,10 +296,6 @@ impl EspNetif {
             ),
         };

-        if let Some(ip_info) = ip_info.as_ref() {
-            esp_inherent_config.ip_info = ip_info;
-        }
-
         let cfg = esp_netif_config_t {
             base: &esp_inherent_config,
             driver: ptr::null(),
@@ -339,6 +307,12 @@ impl EspNetif {
                 .ok_or(EspError::from_infallible::<ESP_ERR_INVALID_ARG>())?,
         );

+        if let Some(ip_info) = ip_info.as_ref() {
+            esp!(unsafe { esp_netif_dhcpc_stop(handle.0) })?;
+            esp!(unsafe { esp_netif_dhcps_stop(handle.0) })?;
+            esp!(unsafe { esp_netif_set_ip_info(handle.0, &*ip_info) })?;
+        }
+
         if let Some(dns) = dns {
             handle.set_dns(dns);
ivmarkov commented 4 months ago

This is great news!... ... but a bit weird that we have to do it in such a strange way. What if there is simply no DHCP server on the network and we are just sitting there waiting for a DHCP address? Also the change where we run the AP DHCP server and then stop it is especially annoying. For a short while we would be running a DHCP server, and that might be unexpected for the user. Also, that last trick is not part of the C example anyway (the example is for STA only of course, but still)

Would you do another variation of your changes:

thetek42 commented 4 months ago

So, I did some more digging around. Apparently the only thing you actually need to do in order to make it work is to remove the esp_netif_flags_ESP_NETIF_FLAG_AUTOUP! Simply replacing it with 0 makes it behave just as expected. (As already mentioned, the diff above was simply me messing around in an attempt to get it to work)

ivmarkov commented 4 months ago

So, I did some more digging around. Apparently the only thing you actually need to do in order to make it work is to remove the esp_netif_flags_ESP_NETIF_FLAG_AUTOUP! Simply replacing it with 0 makes it behave just as expected. (As already mentioned, the diff above was simply me messing around in an attempt to get it to work)

You mean from the client (STA) configuration only? I guess it should stay in the server (AP) configuration?

ivmarkov commented 4 months ago

Can you try with latest master? I just removed the AUTOUP flag from the client conf, and also set the GARP and IP_MODFIFIED flags even for fixed client configuration.

thetek42 commented 4 months ago

You mean from the client (STA) configuration only? I guess it should stay in the server (AP) configuration?

I only tried STA. I don't know about AP, but I'll check that as well when I have the time to do so. But I think that it should be fine with AP since I never had any issues with it.

Can you try with latest master? I just removed the AUTOUP flag from the client conf, and also set the GARP and IP_MODFIFIED flags even for fixed client configuration.

Seems to be working now. Thanks!!

ivmarkov commented 4 months ago

Thank you for persisting through this journey and figuring out the root cause!