Skip to content

WiFi.setHostname() doesn't always work #6700

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
1 task done
mrengineer7777 opened this issue May 5, 2022 · 18 comments
Closed
1 task done

WiFi.setHostname() doesn't always work #6700

mrengineer7777 opened this issue May 5, 2022 · 18 comments
Labels
Area: BT&Wifi BT & Wifi related issues Status: Awaiting triage Issue is waiting for triage

Comments

@mrengineer7777
Copy link
Collaborator

mrengineer7777 commented May 5, 2022

Board

ESP32-WROOM-32UE

Device Description

Custom hardware, but this is not a hardware issue.
FYI we need an option for "Version" below: arduino-esp32 v2.0.3.

Hardware Configuration

Interfaces with 2 UARTs.

Version

latest master (checkout manually)

IDE Name

VSCode with PIO

Operating System

Win10

Flash frequency

?

PSRAM enabled

no

Upload speed

460800

Description

WiFi.setHostname() doesn't always set the hostname. After looking at #6086, #6278, and studying WiFiSTA, WiFiGeneric, I can see the issue.

setHostname() just sets a string (cstr) in memory. It is not applied until WiFiGenericClass::mode() is called, which happens when WiFi.config(), WiFi.begin(), WiFi.enableSTA(), etc are called.

The issue is that if the WiFi.mode() is already STA, then the hostname will not be set even if WiFi is not connected. I would expect hostname to be set in WiFi.begin(), or WiFi.mode() to handle setting it when STA is enabled but not connected.

In our case we scan WiFi networks on startup, which apparently enables STA mode, so setHostname() is never applied.

Impact: It would be nice if it worked.

Workaround: If I stop wifi [WiFi.mode(WIFI_MODE_NULL);] before setting the hostname, then it works.

Sketch

Extracted pieces of my code:

void setup(void) {
    WDT_Start();    //Reboot if our main loop stops responding
    Serial.begin(115200);
    Serial.println("Booting...");  // USB won't be connected yet, so we will only see this on a reboot
    delay(2000);  // Delay for debugging. Allows time for USB to connect to computer.

    WiFiCtrl.StartEventHandler();
    WiFiCtrl.StartPingSvc();  // Make sure SoftAP or WiFi are up
    StartScanAsync();         // Start scan before starting WiFi as scan will fail if WiFi isn't connected. Completion of WiFi scan starts WiFi.
}

void loop {
    WDT_KeepAlive();
    delay(100);             // Allow other tasks to run
    DispCtrl_ReadEvents();  // Process any events from display (currently only factory reset msg)
}

//NOTE: WiFiCtrl class is a wrapper I wrote to extend the WiFi class. It basically handles events.
//Our order of operation is that we run a WiFi Scan on boot, which we save for later use.  After the scan is complete, we connect to WiFi or setup a SoftAP (not shown).

void WiFiCtrlClass::StartEventHandler(void) {
  if(eventhandler_id) return;
  eventhandler_id = WiFi.onEvent(WiFiEvent);
}

void WiFiEvent(WiFiEvent_t event, WiFiEventInfo_t info) {
  ....
  //WiFi
  else if(event==ARDUINO_EVENT_WIFI_SCAN_DONE) {
    Serial.printf("%d APs found\r\n", info.wifi_scan_done.number);
    //Serial.println("--Start WiFi--");                                             //Start WiFi after scan completes
    WiFiCtrl.ConfigWiFi();
  }
}

void WiFiCtrlClass::ConfigWiFi(void)
{
  WiFi.persistent(false);                                                           //Base library should NOT save SSID/pwd to NVS. This also prevents wifi autostart.
  WiFi.setAutoReconnect(true);

  //WiFi.mode(WIFI_MODE_NULL);                                                        //Bugfix: hostname won't be set if STA mode is already active, so shut down WiFi.
  //WiFi.config(INADDR_NONE, INADDR_NONE, INADDR_NONE, INADDR_NONE);                //Clearing IP config could be necessary to get WiFi hostname to work

  log_d("Setting WiFi hostname to '%s'", MF.DeviceName);
  WiFi.setHostname(MF.DeviceName);                                                  //Hostname isn't set until WiFi.mode() is called, and only if STA mode wasn't active.

  MacAddress oBssid = MacAddress(netconf.wlan.bssidval);

  log_v("Start STA BSSID='%s' SSID='%s', PASS='%s'",
     oBssid.Value() ? oBssid.toString().c_str() : "", 
     netconf.wlan.ssid,
     netconf.wlan.password);//TODO test this

  if(!oBssid.Value()) {
    WiFi.begin(netconf.wlan.ssid, netconf.wlan.password);
  }
  else {
    uint8_t bssid[6];
    oBssid.toBytes(bssid);
    WiFi.begin(netconf.wlan.ssid, netconf.wlan.password, 0, bssid, true);//TODO test this
  }
}

Debug Message

Discovering device name using Fing (Android App) or Win10 Command prompt "nslookup 192.168.0.xxx" shows the wrong hostname.

Other Steps to Reproduce

No response

I have checked existing issues, online documentation and the Troubleshooting Guide

  • I confirm I have checked existing issues, online documentation and Troubleshooting guide.

Edit: removed comment in code example about DNS and Netbios, as that is confusing the issue. Use MDNS for Netbios lookups.

@mrengineer7777 mrengineer7777 added the Status: Awaiting triage Issue is waiting for triage label May 5, 2022
@VojtechBartoska VojtechBartoska added the Area: BT&Wifi BT & Wifi related issues label May 6, 2022
@VojtechBartoska
Copy link
Contributor

Hi @mrengineer7777, thanks for reporting an issue and precise description. We will take a look on this.

@VojtechBartoska VojtechBartoska added Status: Needs investigation We need to do some research before taking next steps on this issue Status: Awaiting triage Issue is waiting for triage and removed Status: Awaiting triage Issue is waiting for triage labels May 6, 2022
@me-no-dev
Copy link
Member

You are thinking of another "hostname". The one that you need is provided by the MDNS library (and NBNS for Windows). This hostname here is for communicating with the DHCP server.

@VojtechBartoska VojtechBartoska removed the Status: Needs investigation We need to do some research before taking next steps on this issue label May 9, 2022
@mrengineer7777
Copy link
Collaborator Author

@me-no-dev I'm not talking about MDNS (netbios), though discovery via name might be nice to add to our product. I'm referring to the name registered with the DHCP (and DNS?) server, as set with WiFi.setHostname(). As I noted above the behavior is inconsistent. This is not is a high priority issue, but it would be nice if it worked. I even provided a workaround which should give a clue as to why it doesn't always work.

@me-no-dev
Copy link
Member

And what name does it show?

For nslookup to work, your DHCP need to have control over reverse resolution config in your DNS server. That has nothing to do with the ESP. Also, it is very much possible that you have a long TTL set for reverse resolution (if you even have it set) and that would cache the record for at least half that long on the client.

Maybe tcpdump the DHCP traffic to confirm

@mrengineer7777
Copy link
Collaborator Author

@me-no-dev I'm not here to argue with you. I'm just trying to make arduino-esp32 better. Sniffing WiFi traffic is difficult as it requires me to build a custom network. I've done it before. What I can do fairly easily though is setup a simple project to replicate the behavior I'm seeing. I'll get back with you.

@me-no-dev
Copy link
Member

It's not arguing, it's more like guessing from my end. You gave rather limited info on your network setup. What DHCP/DNS server are you using, how is it setup, what do you see when the ESP connects and what do you actually expect. Did you try DNS cache clear?

I can tell you that I have tested this feature and it works as expected on my DHCP server. I do need to clear cache if I change the hostname often, but other than that it's all good
Screenshot 2022-05-10 at 2 04 47

@mrengineer7777
Copy link
Collaborator Author

@me-no-dev Here is a sample project replicating the issue: https://github.com/mrengineer7777/test_sethostname

You can set your WiFi name and password in mEprom.cpp strcpy(netconf.wlan.ssid, "SSID") strcpy(netconf.wlan.password, "password") or hardcode them. The specific functionality we are interested in is WiFi.setHostname("BORG8472"); in WiFiCtrl.cpp.

Unfortunately I don't know the details of our DHCP and DNS servers on the corporate side.

When I run the project initially, I get:
image
image

If I uncomment WiFi.mode(WIFI_MODE_NULL);:
image
image

@mrengineer7777
Copy link
Collaborator Author

The code that actually sets the hostname is below. It only fires if WiFi mode changes, which is a problem for our sample because the WiFi scan sets the mode, but doesn't actually connect to WiFi.

bool WiFiGenericClass::mode(wifi_mode_t m)
{
    wifi_mode_t cm = getMode();
    if(cm == m) {
        return true;
    }
    if(!cm && m){
        if(!wifiLowLevelInit(_persistent)){
            return false;
        }
    } else if(cm && !m){
        return espWiFiStop();
    }

    esp_err_t err;
    if(m & WIFI_MODE_STA){
    	err = set_esp_interface_hostname(ESP_IF_WIFI_STA, get_esp_netif_hostname());
        if(err){
            log_e("Could not set hostname! %d", err);
            return false;
        }
    }
    err = esp_wifi_set_mode(m);
    if(err){
        log_e("Could not set mode! %d", err);
        return false;
    }
    if(_long_range){
        if(m & WIFI_MODE_STA){
            err = esp_wifi_set_protocol(WIFI_IF_STA, WIFI_PROTOCOL_LR);
            if(err != ESP_OK){
                log_e("Could not enable long range on STA! %d", err);
                return false;
            }
        }
        if(m & WIFI_MODE_AP){
            err = esp_wifi_set_protocol(WIFI_IF_AP, WIFI_PROTOCOL_LR);
            if(err != ESP_OK){
                log_e("Could not enable long range on AP! %d", err);
                return false;
            }
        }
    }
    if(!espWiFiStart()){
        return false;
    }

    #ifdef BOARD_HAS_DUAL_ANTENNA
        if(!setDualAntennaConfig(ANT1, ANT2, WIFI_RX_ANT_AUTO, WIFI_TX_ANT_AUTO)){
            log_e("Dual Antenna Config failed!");
            return false;
        }
    #endif

    return true;
}

@me-no-dev
Copy link
Member

you really need to provide minimal example of what you are doing. That project is way too much of a code. Then as I tried to explain to you.... name resolution and reverse resolutions are handled by a DNS server, which has nothing to do with your ESP devices. I also showed you how the hostname I set to my chip her (your github username) did show right away in my DHCP server (where it is supposed to show). Figure out your infrastructure and look for the problem there ;)

@mrengineer7777
Copy link
Collaborator Author

Fine. I cut it down to one file. It still demonstrates the issue. https://github.com/mrengineer7777/test_sethostname

Uncomment this line to see the difference:
image

If you really need to see the DHCP server entry I can get a screenshot later. But I can tell you now the issue is real.

@me-no-dev
Copy link
Member

Try calling setHostname BEFORE scanNetworks. scanNetworks already initialized the interface and set it's hostname.

so... see why I asked for short example?

@mrengineer7777
Copy link
Collaborator Author

Try calling setHostname BEFORE scanNetworks. scanNetworks already initialized the interface and set it's hostname.

Yes, that's exactly my point. The scan initializes WiFi, which causes setHostname() to not set. The behavior is very confusing. It would be helpful to log a debug message to the console, or even better have WiFi.mode() set hostname consistently.

Digging into the IDF:
esp_netif.h
esp_err_t esp_netif_set_hostname(esp_netif_t *esp_netif, const char *hostname);

esp_netif_lwip.c
esp_err_t esp_netif_set_hostname(esp_netif_t *esp_netif, const char *hostname) _RUN_IN_LWIP_TASK_IF_SUPPORTED(esp_netif_set_hostname_api, esp_netif, hostname)
sethostname redirects to sethostnameapi

static esp_err_t esp_netif_set_hostname_api(esp_netif_api_msg_t *msg)
{
...
p_netif->hostname = esp_netif->hostname;
...
}

Sets pointer to (const char *) hostname. Thus it is safe to call esp_netif_set_hostname() more than once.

A solution is to set hostname even if the mode is the same. This moves if(cm == m) after the hostname.
WiFiGeneric.cpp:

bool WiFiGenericClass::mode(wifi_mode_t m)
{
    wifi_mode_t cm = getMode();
    
    if(!cm && m){
        if(!wifiLowLevelInit(_persistent)){
            return false;
        }
    } else if(cm && !m){
        return espWiFiStop();
    }

    esp_err_t err;
    if(m & WIFI_MODE_STA){
    	err = set_esp_interface_hostname(ESP_IF_WIFI_STA, get_esp_netif_hostname());
        if(err){
            log_e("Could not set hostname! %d", err);
            return false;
        }
    }

    if(cm == m) {
        return true;
    }
   ....
}

If that is an acceptable solution I can create a PR for it.

@mrengineer7777
Copy link
Collaborator Author

Testing: code change above does not fix issue. I give up.

@mrengineer7777
Copy link
Collaborator Author

@me-no-dev FYI setting the hostname before the scan does work.

@me-no-dev
Copy link
Member

do you have debug enabled and also Serial.setDebugOutput(true);? I would have thought that you will get a message... if not, we can add such. Maybe I missed it when implementing

@rpannekoek
Copy link

It's remarkable how fiddly something as trivial as setting hostname is on ESP32! The misery in this area already started in 2019 (see #2537) and there seems to be regression in ESP32 Arduino 2.0.x now.

I can confirm that with ESP32 Arduino 2.0.3 WiFi.setHostName now only works if

  • You call it before calling WiFi.mode(WIFI_STA)
  • You ensure that the mode is not WIFI_STA already before calling WiFi.setHostName (by first calling WiFi.mode(WIFI_MODE_NULL))

In my case, I am not explicitly doing anything that initializes WIFI, but still it doesn't work unless I call WiFi.mode(WIFI_MODE_NULL) before WiFi.setHostName.

Note that this changed between ESP32 Arduino 1.0.x and 2.0.x and it is also no longer consistent with ESP8266 Arduino, where setting host name can be done after WiFi.mode (as long as you do it before WiFi.begin) and also works without an explicit WiFi.mode(WIFI_MODE_NULL).

@rpannekoek
Copy link

rpannekoek commented May 29, 2022

FYI: I have a library which can be used on both ESP32 and ESP8266. The code to initialize STA now looks as follows (note it now supports ESP8266, ESP32 SDK 1.0x and ESP32 SDK 2.0.x)

void WiFiStateMachine::initializeSTA()
{
    TRACE(F("Connecting to WiFi network '%s' ...\n"), _ssid.c_str());
    WiFi.persistent(false);
    if (!WiFi.setAutoReconnect(_reconnectInterval == 0))
        TRACE(F("Unable to set auto reconnect\n"));
#ifdef ESP8266
    if (!WiFi.mode(WIFI_STA))
        TRACE(F("Unable to set WiFi mode\n"));
    if (!WiFi.disconnect())
        TRACE(F("WiFi disconnect failed\n"));
    if (!WiFi.hostname(_hostName))
        TRACE(F("Unable to set host name\n"));
#elif defined(ESP32V1)
    if (!WiFi.mode(WIFI_STA))
        TRACE(F("Unable to set WiFi mode\n"));
    if (!WiFi.disconnect())
        TRACE(F("WiFi disconnect failed\n"));
    // ESP32 doesn't reliably set the status to WL_DISCONNECTED despite disconnect() call.
    WiFiSTAClass::_setStatus(WL_DISCONNECTED);
    // See https://github.com/espressif/arduino-esp32/issues/2537
    IPAddress none = IPAddress(0,0,0,0);
    if (!WiFi.config(none, none, none))
        TRACE(F("WiFi.config failed\n"));
    if (!WiFi.setHostname(_hostName.c_str()))
        TRACE(F("Unable to set host name ('%s')\n"), _hostName.c_str());
#else
    if (!WiFi.mode(WIFI_MODE_NULL))
        TRACE(F("Unable to set WiFi mode\n"));
    if (!WiFi.setHostname(_hostName.c_str()))
        TRACE(F("Unable to set host name ('%s')\n"), _hostName.c_str());
    if (!WiFi.mode(WIFI_STA))
        TRACE(F("Unable to set WiFi mode\n"));
    if (!WiFi.disconnect())
        TRACE(F("WiFi disconnect failed\n"));
    // ESP32 doesn't reliably set the status to WL_DISCONNECTED despite disconnect() call.
    WiFiSTAClass::_setStatus(WL_DISCONNECTED);
#endif
    ArduinoOTA.setHostname(_hostName.c_str());
    WiFi.begin(_ssid.c_str(), _password.c_str());
}

@tablatronix
Copy link
Contributor

tablatronix commented Aug 17, 2022

Sounds like this maybe?

(#6099 (comment))

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Area: BT&Wifi BT & Wifi related issues Status: Awaiting triage Issue is waiting for triage
Projects
None yet
Development

No branches or pull requests

5 participants