Is there a way to have ESPHome renegotiate its WiFi connection without a restart?

I have two APs at my house and all devices are programmed with both (for backup). I have written in the preferred AP on each so that they connect to the closer (better connection) one – its based on the ESPHome WiFi strength data and it works GREAT!

However, when I update one of the APs or have to bring one down and back up, most of the devices shift to the “other” one. Some are close “enough” that they keep this mediocre connection until I reboot the ESPHome device.

I’d like some way to force a re-negotiation of the WiFi connection so I can restore these devices to the closer AP without having to reboot them. I know I can just reboot them but some have in-memory values that I don’t want to lose by forcing a reboot (like radon, water pressure, and AQI sensors).

wouldn’t a lambda with a wifi down and then up again work? define it as a service in your yaml and call it from an HA automation scheduled every X hours.
https://www.arduino.cc/reference/en/libraries/wifi/

# Enable Home Assistant API
api:
  services:
    - service: reset_wifi
      variables:
        ssid: string
        pass: string
      then:
        - lambda: |-
            WiFi.disconnect();
            WiFi.begin( ssid.c_str(), pass.c_str() );
2 Likes

@FredTheFrog, this is very interesting! Do you know if I can just use WiFi.begin() and have it use the stored prioritized SSID/credentials? I’d like it to just attempt reconnecting to the pre-programmed values that are already in the ESPHome wifi component. I don’t want to have to re-speicify the ssid and pass in the lambda.

I looked at your link and I’m not even sure how you made the leap from ESPHome to the Arduino.cc reference page. I wasn’t aware you could just use those functions in lambdas… I don’t have a lot of experience with custom ESPHome builds. :blush:

I’ve not tried it myself, yet. but I believe you can call Wifi.begin() with no arguments. i would hope ESPHome would use its own values. Good luck!!

I have created a switch to force my esp32 to connect to a certain wifi:

switch:
  - platform: template
    name: Connect to Wifi
    turn_on_action:
      then:
        - lambda: |-
            WiFi.disconnect();
            WiFi.begin("ssid", "pssw");

but it does not. Turning on the switch disconnects the esp32 from current wifi, but then it reconnects to the strongest ssid, not the one I am instructing in the Wifi.begin() call.

So, I have been using this for a while and it worked but recently updated ESPHome and I’m trying to update my devices and apparently it’s no longer supported.

YAML that used to work:

  - platform: template
    name: ${friendly_name} Reset WiFi
    on_press:
      then:
        - lambda: |-
            WiFi.disconnect();
            WiFi.begin();

Output log file now looks like this:

INFO Reading configuration /config/esphome/gym.yaml...
INFO Generating C++ source...
INFO Compiling app...
Processing gym (board: nodemcu-32s; framework: espidf; platform: platformio/espressif32 @ 5.2.0)
--------------------------------------------------------------------------------
HARDWARE: ESP32 240MHz, 320KB RAM, 4MB Flash
 - framework-espidf @ 3.40402.0 (4.4.2) 
 - tool-cmake @ 3.16.4 
 - tool-ninja @ 1.7.1 
 - toolchain-esp32ulp @ 2.35.0-20220830 
 - toolchain-xtensa-esp32 @ 8.4.0+2021r2-patch3
Reading CMake configuration...
LDF: Library Dependency Finder -> https://bit.ly/configure-pio-ldf
Dependency Graph
|-- noise-c @ 0.1.4
|   |-- libsodium @ 1.10018.1
Compiling /data/gym/.pioenvs/gym/src/main.o
Archiving /data/gym/.pioenvs/gym/esp-idf/asio/libasio.a
Compiling /data/gym/.pioenvs/gym/bt/common/btc/core/btc_manage.o
Compiling /data/gym/.pioenvs/gym/bt/common/btc/core/btc_task.o
Compiling /data/gym/.pioenvs/gym/bt/common/btc/profile/esp/blufi/blufi_prf.o
Compiling /data/gym/.pioenvs/gym/bt/common/btc/profile/esp/blufi/blufi_protocol.o
Compiling /data/gym/.pioenvs/gym/bt/common/osi/alarm.o
/config/esphome/common/common.button.yaml: In lambda function:
/config/esphome/common/common.button.yaml:9:7: error: 'WiFi' was not declared in this scope
             WiFi.disconnect();
       ^   
Compiling /data/gym/.pioenvs/gym/bt/common/osi/allocator.o
/config/esphome/common/common.text_sensor.yaml: In lambda function:
/config/esphome/common/common.text_sensor.yaml:28:19: error: 'String' was not declared in this scope
         return { (String(days) +"d " + String(hours) +"h " + String(minutes) +"m "+ String(seconds) +"s").c_str() };
                   ^~~~~~
/config/esphome/common/common.text_sensor.yaml:28:19: note: suggested alternative: 'trunc'
         return { (String(days) +"d " + String(hours) +"h " + String(minutes) +"m "+ String(seconds) +"s").c_str() };
                   ^~~~~~
                   trunc
/config/esphome/common/common.text_sensor.yaml:28:115: error: could not convert '{<expression error>}' from '<brace-enclosed initializer list>' to 'esphome::optional<std::__cxx11::basic_string<char> >'
         return { (String(days) +"d " + String(hours) +"h " + String(minutes) +"m "+ String(seconds) +"s").c_str() };
                                                                                                                   ^
/config/esphome/common/common.text_sensor.yaml:30:19: error: 'String' was not declared in this scope
         return { (String(hours) +"h " + String(minutes) +"m "+ String(seconds) +"s").c_str() };
                   ^~~~~~
/config/esphome/common/common.text_sensor.yaml:30:19: note: suggested alternative: 'trunc'
         return { (String(hours) +"h " + String(minutes) +"m "+ String(seconds) +"s").c_str() };
                   ^~~~~~
                   trunc
/config/esphome/common/common.text_sensor.yaml:30:94: error: could not convert '{<expression error>}' from '<brace-enclosed initializer list>' to 'esphome::optional<std::__cxx11::basic_string<char> >'
         return { (String(hours) +"h " + String(minutes) +"m "+ String(seconds) +"s").c_str() };
                                                                                              ^
/config/esphome/common/common.text_sensor.yaml:32:19: error: 'String' was not declared in this scope
         return { (String(minutes) +"m "+ String(seconds) +"s").c_str() };
                   ^~~~~~
/config/esphome/common/common.text_sensor.yaml:32:19: note: suggested alternative: 'trunc'
         return { (String(minutes) +"m "+ String(seconds) +"s").c_str() };
                   ^~~~~~
                   trunc
/config/esphome/common/common.text_sensor.yaml:32:72: error: could not convert '{<expression error>}' from '<brace-enclosed initializer list>' to 'esphome::optional<std::__cxx11::basic_string<char> >'
         return { (String(minutes) +"m "+ String(seconds) +"s").c_str() };
                                                                        ^
/config/esphome/common/common.text_sensor.yaml:34:19: error: 'String' was not declared in this scope
         return { (String(seconds) +"s").c_str() };
                   ^~~~~~
/config/esphome/common/common.text_sensor.yaml:34:19: note: suggested alternative: 'trunc'
         return { (String(seconds) +"s").c_str() };
                   ^~~~~~
                   trunc
/config/esphome/common/common.text_sensor.yaml:34:49: error: could not convert '{<expression error>}' from '<brace-enclosed initializer list>' to 'esphome::optional<std::__cxx11::basic_string<char> >'
         return { (String(seconds) +"s").c_str() };
                                                 ^
Compiling /data/gym/.pioenvs/gym/bt/common/osi/buffer.o
Compiling /data/gym/.pioenvs/gym/bt/common/osi/config.o
Compiling /data/gym/.pioenvs/gym/bt/common/osi/fixed_queue.o
Compiling /data/gym/.pioenvs/gym/bt/common/osi/future.o
Compiling /data/gym/.pioenvs/gym/bt/common/osi/hash_functions.o
Compiling /data/gym/.pioenvs/gym/bt/common/osi/hash_map.o
Compiling /data/gym/.pioenvs/gym/bt/common/osi/list.o
Compiling /data/gym/.pioenvs/gym/bt/common/osi/mutex.o
Compiling /data/gym/.pioenvs/gym/bt/common/osi/thread.o
Compiling /data/gym/.pioenvs/gym/bt/common/osi/osi.o
Compiling /data/gym/.pioenvs/gym/bt/common/osi/semaphore.o
Compiling /data/gym/.pioenvs/gym/bt/host/bluedroid/api/esp_a2dp_api.o
Compiling /data/gym/.pioenvs/gym/bt/host/bluedroid/api/esp_avrc_api.o
Compiling /data/gym/.pioenvs/gym/bt/host/bluedroid/api/esp_bt_device.o
Compiling /data/gym/.pioenvs/gym/bt/host/bluedroid/api/esp_bt_main.o
Compiling /data/gym/.pioenvs/gym/bt/host/bluedroid/api/esp_gap_ble_api.o
Compiling /data/gym/.pioenvs/gym/bt/host/bluedroid/api/esp_gap_bt_api.o
*** [/data/gym/.pioenvs/gym/src/main.o] Error 1
Compiling /data/gym/.pioenvs/gym/bt/host/bluedroid/api/esp_gatt_common_api.o
========================== [FAILED] Took 4.73 seconds ==========================

might need to add a framework: line to the esp8266 section to force the library being used?

Review the fourth line in your compile log output.

Processing gym (board: nodemcu-32s; framework: espidf; platform: platformio/espressif32 @ 5.2.0)

Ah, that was it. I was doing that specifically on this “gym” instance because I’m trying to use the esp-idf version:

esp32:
  board: nodemcu-32s
  framework:
    type: esp-idf

Apparently, they esp-idf devs don’t have (or don’t expose) a WiFi object. When I try to compile the other versions of nodemcu-32s devices which use the arduino framework, everything compiles fine. :expressionless:

They just use different names for things:
https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/network/esp_wifi.html

esp_wifi_connect(void)
esp_wifi_disconnect(void)

Hope this helps.

ooh thanks Fred! That looks really promising!! What is the instance name that I use to call those routines though? It doesn’t seem to like:

    on_press:
      then:
        - lambda: |-
            esp_wifi_disconnect();
            esp_wifi_connect();

Resulting log messages:

INFO Reading configuration /config/esphome/gym.yaml...
INFO Generating C++ source...
INFO Compiling app...
Processing gym (board: nodemcu-32s; framework: espidf; platform: platformio/espressif32 @ 5.2.0)
--------------------------------------------------------------------------------
HARDWARE: ESP32 240MHz, 320KB RAM, 4MB Flash
 - framework-espidf @ 3.40402.0 (4.4.2) 
 - tool-cmake @ 3.16.4 
 - tool-ninja @ 1.7.1 
 - toolchain-esp32ulp @ 2.35.0-20220830 
 - toolchain-xtensa-esp32 @ 8.4.0+2021r2-patch3
Reading CMake configuration...
LDF: Library Dependency Finder -> https://bit.ly/configure-pio-ldf
Dependency Graph
|-- noise-c @ 0.1.4
|   |-- libsodium @ 1.10018.1
Compiling /data/gym/.pioenvs/gym/src/main.o
/config/esphome/common/common.button.yaml: In lambda function:
/config/esphome/common/common.button.yaml:9:7: error: 'esp_wifi_disconnect' was not declared in this scope
             esp_wifi_disconnect();
       ^     ~~~~~~~~~~~~~
/config/esphome/common/common.button.yaml:9:7: note: suggested alternative: 'esp_ble_gap_disconnect'
             esp_wifi_disconnect();
       ^     ~~~~~~~~~~~~~
       esp_ble_gap_disconnect
/config/esphome/common/common.button.yaml:10:7: error: 'esp_wifi_connect' was not declared in this scope
             esp_wifi_connect();
       ^     ~~~~~~~~~~
/config/esphome/common/common.button.yaml:10:7: note: suggested alternative: 'lwip_connect'
             esp_wifi_connect();
       ^     ~~~~~~~~~~
       lwip_connect
Compiling /data/gym/.pioenvs/gym/bt/host/bluedroid/bta/hf_client/bta_hf_client_api.o
Compiling /data/gym/.pioenvs/gym/bt/host/bluedroid/bta/hf_client/bta_hf_client_at.o
Compiling /data/gym/.pioenvs/gym/bt/host/bluedroid/bta/hf_client/bta_hf_client_cmd.o
Compiling /data/gym/.pioenvs/gym/bt/host/bluedroid/bta/hf_client/bta_hf_client_main.o
Compiling /data/gym/.pioenvs/gym/bt/host/bluedroid/bta/hf_client/bta_hf_client_rfc.o
Compiling /data/gym/.pioenvs/gym/bt/host/bluedroid/bta/hf_client/bta_hf_client_sco.o
*** [/data/gym/.pioenvs/gym/src/main.o] Error 1
Compiling /data/gym/.pioenvs/gym/bt/host/bluedroid/bta/hf_client/bta_hf_client_sdp.o
========================== [FAILED] Took 4.65 seconds ==========================

I tried using wifi.esp_wifi_disconnect();, WiFi.esp_wifi_disconnect();, esp_wifi.esp_wifi_disconnect(); and they all throw “XXXX not declared in this scope” messages.

If I were trying to do this, I’d consult with @jesserockz or @ssieb on the ESPHome Discord.
I’m not deeply experienced in writing C code for the ESP32, whereas they are much more likely to know.