Simple, Reliable Sprinkler Controller with ESPHome

If it is any consolation, I just ordered a board and I am having the exact same issue as you are :frowning:

You need to hold the IO0 button which is normally labeled as BOOT on other devices and then press the EN button (normally labeled as RESET). This will then cause it to reboot into a mode that lets you flash it.

There is a bridge between IO0 and Ground.
I am holding the RESET button and releasing it after I click install.

That is (unfortunately) a consolation!

However… I wished I read this before I ordered a new board. It just arrived and I still have the same problem. If I read your message before, I would probably have gone with the ESP32 + Relayboard route…
This project has given me many grey hairs

Care to share your modified version?

I had the same challenges.

What resolved it for me was to check if the drivers of the FTDI were installed. I looked into the device manager.

Then I downloaded from the esphome web builder. I choose install and then manual download.

Lastly I flashed through web.esphome.io

I followed all the steps above in this thread to put it to flash.

Maybe it helps you too.

My FTDI adapter works with other boards. I can’t successfully upload a sketch to the board via Arduino IDE either. I contacted the seller and they sent through a manual in Chinese that translates to:
Use a jumper cap to connect IO0 and GND, and prepare a TTL serial module (such as FT232). Connect to the computer’s USB port.
2. Connect the serial port module and the development board as follows:
• GND → GND
• TX → RX
• RX → TX
• 5V → 5V
3. In Arduino IDE, select the board as ESPino (ESP-12 module).
4. Open the desired program, select the correct COM port, then click “Upload” — the program will compile and download to the board automatically.
5. After upload, disconnect IO0 from GND and either re-power the board or press the reset button to start the uploaded program.

I have tried this but it doesn’t work. Maybe a bad batch of boards were produced or some change?

I have had success migrating my Rainbird 24vac sprinkler system over to a ESP32 based board that I bought from Amazon.

For scheduling, I’m using Home Assistant to crawl my local agriculture extension’s weather data and then deciding when to run the sprinklers using Evapotranspiration (basically the soil is a water battery that discharges due to the sun and gets recharged by the sprinklers or rain)

I bought this AC power adapter for my Rainbird sprinklers. I just ordered a LM2596HV AC-DC buck converter to remove the extra 5v power charger.

1 Like

sounds like my experience with the lights cycling after the reset button was released. In my case one the gnd pin wasn’t connected to the board so the IO0 pin wasn’t actually grounded. I quickly used a jumper wire to connect to gnd on the power supply and it worked on my first attempt after that.

I actually only needed to modify 2 sections of the code - (1) sprinker and (2) switch, to only provide 4 and to match the GPIO with how I’d connected the relay. See below. Everything else is the same.

sprinkler:
  - id: ${id_prefix}_sprinklers
    main_switch: "Sprinkler Cycle Active"
    auto_advance_switch: "Auto Advance"
    valve_overlap: 5s
    valves:
      - valve_switch: "Zone 1 Active"
        enable_switch: "Zone 1 Auto"
        run_duration_number: 
          id: zone_1_run_duration
          name: "Zone 1 Run Duration"
          icon: "mdi:timer-outline"
          initial_value: 1
          unit_of_measurement: min
        valve_switch_id: ${id_prefix}_valve_sw1
      - valve_switch: "Zone 2 Active"
        enable_switch: "Zone 2 Auto"
        run_duration_number: 
          id: zone_2_run_duration
          name: "Zone 2 Run Duration"
          icon: "mdi:timer-outline"
          initial_value: 1
          unit_of_measurement: min
        valve_switch_id: ${id_prefix}_valve_sw2
      - valve_switch: "Zone 3 Active"
        enable_switch: "Zone 3 Auto"
        run_duration_number: 
          id: zone_3_run_duration
          name: "Zone 3 Run Duration"
          icon: "mdi:timer-outline"
          initial_value: 1
          unit_of_measurement: min
        valve_switch_id: ${id_prefix}_valve_sw3
      - valve_switch: "Zone 4 Active"
        enable_switch: "Zone 4 Auto"
        run_duration_number: 
          id: zone_4_run_duration
          name: "Zone 4 Run Duration"
          icon: "mdi:timer-outline"
          initial_value: 1
          unit_of_measurement: min
        valve_switch_id: ${id_prefix}_valve_sw4

switch:
  # relays
  - platform: gpio
    id: ${id_prefix}_valve_sw1
    pin: GPIO33
    restore_mode: ALWAYS_OFF
  - platform: gpio
    id: ${id_prefix}_valve_sw2
    pin: GPIO25
    restore_mode: ALWAYS_OFF
  - platform: gpio
    id: ${id_prefix}_valve_sw3
    pin: GPIO26
    restore_mode: ALWAYS_OFF
  - platform: gpio
    id: ${id_prefix}_valve_sw4
    pin: GPIO27
    restore_mode: ALWAYS_OFF
...

I’ve been playing about with the sprinkler for a while now, I’ve just added a 2.42” OLED screen

I added the same screen and an rotary encoder, it’s functional but not as pretty as yours.



Hope you share when ready so I can pilfer your work!

Pilfer away. Ill probably be making some more changes but this is how it is now

esphome:
  name: 1-relay-basket-irrigation

esp32:
  board: esp32dev
  framework:
    type: arduino

wifi:
  ssid: !secret wifi_ssid
  password: !secret wifi_password

logger:
  logs:
    switch: NONE
    binary_sensor: NONE

api:
  reboot_timeout: 0s

ota:
  - platform: esphome

# -------------------------
# I2C BUS for OLED
# -------------------------
i2c:
  sda: GPIO13
  scl: GPIO14
  scan: true
  id: bus_a

# -------------------------
# FONTS
# -------------------------
font:
  - file: "fonts/arial.ttf"
    id: font_small
    size: 10
  - file: "fonts/arial.ttf"
    id: font_large
    size: 20
  - file: "fonts/materialdesignicons-webfont.ttf"
    id: mdi_font
    size: 28
    glyphs: [
      "\U000F1068",  # mdi:valve-open
      "\U000F1067"   # mdi:valve (closed)
     # "\U000F05A9"  # Wi-Fi icon
    ]
  - file: "fonts/materialdesignicons-webfont.ttf"
    id: mdi_font_small
    size: 14
    glyphs: [
      "\U000F05A9"   # mdi:wifi
    ]

# -------------------------
# DISPLAY CONFIGURATION
# -------------------------
display:
  - platform: ssd1306_i2c
    model: "SSD1306 128x64"
    address: 0x3C
    id: oled_display
    rotation: 0
    lambda: |-
      // Draw Wi-Fi signal strength (bars)
      int wifi = (int)id(wifi_signal_db).state;
      int bars = 0;
      if (wifi > -50) bars = 4;
      else if (wifi > -60) bars = 3;
      else if (wifi > -70) bars = 2;
      else if (wifi > -80) bars = 1;
      else bars = 0;

      // Draw WiFi bars (top right)
      for (int i = 0; i < 4; i++) {
        if (i < bars)
          it.filled_rectangle(116 + (i * 3), 2 + (3 - i) * 2, 2, (i + 1) * 2);
        else
          it.rectangle(116 + (i * 3), 2 + (3 - i) * 2, 2, (i + 1) * 2);
      }

      // Label WiFi strength
      it.printf(0, 0, id(mdi_font_small), "\U000F05A9");  // Wi-Fi icon
      it.printf(20, 0, id(font_small), "%d%%", (int)min(max(2 * (wifi + 100.0), 0.0), 100.0));

      // Show which valve is ON
      int y = 24;
      bool any_on = false;

      if (id(valve_sw1).state) {
        it.printf(0, y, id(mdi_font), "\U000F1068"); // mdi:valve-open
        it.printf(34, y + 2, id(font_large), "Valve 1");
        any_on = true;
      } else if (id(valve_sw2).state) {
        it.printf(0, y, id(mdi_font), "\U000F1068");
        it.printf(34, y + 2, id(font_large), "Valve 2");
        any_on = true;
      } else if (id(valve_sw3).state) {
        it.printf(0, y, id(mdi_font), "\U000F1068");
        it.printf(34, y + 2, id(font_large), "Valve 3");
        any_on = true;
      } else if (id(valve_sw4).state) {
        it.printf(0, y, id(mdi_font), "\U000F1068");
        it.printf(34, y + 2, id(font_large), "Valve 4");
        any_on = true;
      } else if (id(valve_sw5).state) {
        it.printf(0, y, id(mdi_font), "\U000F1068");
        it.printf(34, y + 2, id(font_large), "Valve 5");
        any_on = true;
      }

      if (!any_on) {
        it.printf(0, y, id(mdi_font), "\U000F1067"); // mdi:valve (closed)
        it.printf(34, y + 2, id(font_large), "All Closed");
      }


# -------------------------
# SPRINKLER CONTROL
# -------------------------
sprinkler:
  - id: basket_irrigation
    main_switch: "Pump"
    auto_advance_switch: "Lawn Sprinklers Auto Advance"
    valve_overlap: 2s
    valves:
      - valve_switch: "Valve 1"
        enable_switch: "Enable Valve 1"
        pump_switch_id: pump_sw
        run_duration_number:
          id: valve_1_run_duration
          name: "Valve 1 Run Duration"
          icon: "mdi:timer-outline"
          initial_value: 1
          unit_of_measurement: min
          min_value: 1
          max_value: 60
        valve_switch_id: valve_sw1

      - valve_switch: "Valve 2"
        enable_switch: "Enable Valve 2"
        pump_switch_id: pump_sw
        run_duration_number:
          id: valve_2_run_duration
          name: "Valve 2 Run Duration"
          icon: "mdi:timer-outline"
          initial_value: 1
          unit_of_measurement: min
          min_value: 1
          max_value: 60
        valve_switch_id: valve_sw2

      - valve_switch: "Valve 3"
        enable_switch: "Enable Valve 3"
        pump_switch_id: pump_sw
        run_duration_number:
          id: valve_3_run_duration
          name: "Valve 3 Run Duration"
          icon: "mdi:timer-outline"
          initial_value: 1
          unit_of_measurement: min
          min_value: 1
          max_value: 60
        valve_switch_id: valve_sw3

      - valve_switch: "Valve 4"
        enable_switch: "Enable Valve 4"
        pump_switch_id: pump_sw
        run_duration_number:
          id: valve_4_run_duration
          name: "Valve 4 Run Duration"
          icon: "mdi:timer-outline"
          initial_value: 1
          unit_of_measurement: min
          min_value: 1
          max_value: 60
        valve_switch_id: valve_sw4

      - valve_switch: "Valve 5"
        enable_switch: "Enable Valve 5"
        pump_switch_id: pump_sw
        run_duration_number:
          id: valve_5_run_duration
          name: "Valve 5 Run Duration"
          icon: "mdi:timer-outline"
          initial_value: 1
          unit_of_measurement: min
          min_value: 1
          max_value: 60
        valve_switch_id: valve_sw5

# -------------------------
# SWITCHES
# -------------------------
switch:
  - platform: gpio
    id: pump_sw
    pin: GPIO16

  - platform: gpio
    id: pump_sw1
    pin: GPIO17

  - platform: gpio
    id: valve_sw1
    pin: GPIO25

  - platform: gpio
    id: valve_sw2
    pin: GPIO26

  - platform: gpio
    id: valve_sw3
    pin: GPIO32

  - platform: gpio
    id: valve_sw4
    pin: GPIO33

  - platform: gpio
    id: valve_sw5
    pin: GPIO27

# -------------------------
# SENSORS
# -------------------------
sensor:
  - platform: wifi_signal
    name: "WiFi Signal dB"
    id: wifi_signal_db
    update_interval: 60s
    entity_category: "diagnostic"

  - platform: copy
    source_id: wifi_signal_db
    name: "WiFi Signal Percent"
    filters:
      - lambda: return min(max(2 * (x + 100.0), 0.0), 100.0);
    unit_of_measurement: "%"
    entity_category: "diagnostic"

  - platform: uptime
    name: Uptime
    entity_category: "diagnostic"

# -------------------------
# STATUS BINARY SENSOR
# -------------------------
binary_sensor:
  - platform: status
    name: "Connection Status"

# GPIO16 Pump
# GPIO25 1
# GPIO26 2
# GPIO32 3
# GPIO33 4
# GPIO27 5
# OLED
# GPIO13 SDA
# GPIO14 SCL

What I wanted to add was a countdown timer but i don’t know how to do that. I did try but it didn’t work. The set time did display but didn’t count down, something like this would be great but anything would do at the moment

lambda: |-
  // Draw Wi-Fi signal strength (bars)
  int wifi = (int)id(wifi_signal_db).state;
  int bars = 0;
  if (wifi > -50) bars = 4;
  else if (wifi > -60) bars = 3;
  else if (wifi > -70) bars = 2;
  else if (wifi > -80) bars = 1;
  else bars = 0;

  for (int i = 0; i < 4; i++) {
    if (i < bars)
      it.filled_rectangle(116 + (i * 3), 2 + (3 - i) * 2, 2, (i + 1) * 2);
    else
      it.rectangle(116 + (i * 3), 2 + (3 - i) * 2, 2, (i + 1) * 2);
  }

  it.printf(0, 0, id(mdi_font_small), "\U000F05A9");  // Wi-Fi icon
  it.printf(20, 0, id(font_small), "%d%%", (int)min(max(2 * (wifi + 100.0), 0.0), 100.0));

  // Show which valve is ON
  int y = 24;
  bool any_on = false;
  float remaining = 0.0;
  const char* valve_name = "";

  if (id(valve_sw1).state) {
    it.printf(0, y, id(mdi_font), "\U000F1068");
    it.printf(34, y + 2, id(font_large), "Valve 1");
    any_on = true;
    remaining = id(valve_1_run_duration).state;
    valve_name = "Valve 1";
  } else if (id(valve_sw2).state) {
    it.printf(0, y, id(mdi_font), "\U000F1068");
    it.printf(34, y + 2, id(font_large), "Valve 2");
    any_on = true;
    remaining = id(valve_2_run_duration).state;
    valve_name = "Valve 2";
  } else if (id(valve_sw3).state) {
    it.printf(0, y, id(mdi_font), "\U000F1068");
    it.printf(34, y + 2, id(font_large), "Valve 3");
    any_on = true;
    remaining = id(valve_3_run_duration).state;
    valve_name = "Valve 3";
  } else if (id(valve_sw4).state) {
    it.printf(0, y, id(mdi_font), "\U000F1068");
    it.printf(34, y + 2, id(font_large), "Valve 4");
    any_on = true;
    remaining = id(valve_4_run_duration).state;
    valve_name = "Valve 4";
  } else if (id(valve_sw5).state) {
    it.printf(0, y, id(mdi_font), "\U000F1068");
    it.printf(34, y + 2, id(font_large), "Valve 5");
    any_on = true;
    remaining = id(valve_5_run_duration).state;
    valve_name = "Valve 5";
  }

  if (!any_on) {
    it.printf(0, y, id(mdi_font), "\U000F1067");
    it.printf(34, y + 2, id(font_large), "All Closed");
  } else {
    // Show remaining run duration at the bottom
    it.printf(0, 52, id(font_small), "%s remaining: %.0f min", valve_name, remaining);
  }

With a lot of messing and stuff, The countdown timer is now working on the screen. There is probably unused code in there that Ive missed

esphome:
  name: 1-relay-basket-irrigation

esp32:
  board: esp32dev
  framework:
    type: arduino

wifi:
  ssid: !secret wifi_ssid
  password: !secret wifi_password
# -------------------------
# GLOBAL VARIABLES
# -------------------------
globals:
  - id: remaining_time
    type: float
    restore_value: no
    initial_value: '0'
logger:
  logs:
    switch: NONE
    binary_sensor: NONE

api:
  reboot_timeout: 0s

ota:
  - platform: esphome

# -------------------------
# I2C BUS for OLED
# -------------------------
i2c:
  sda: GPIO13
  scl: GPIO14
  scan: true
  id: bus_a

# -------------------------
# FONTS
# -------------------------
font:
  - file: "fonts/arial.ttf"
    id: font_small
    size: 10
  - file: "fonts/arial.ttf"
    id: font_large
    size: 20
  - file: "fonts/materialdesignicons-webfont.ttf"
    id: mdi_font
    size: 28
    glyphs: [
      "\U000F1068",  # mdi:valve-open
      "\U000F1067"   # mdi:valve (closed)
     # "\U000F05A9"  # Wi-Fi icon
    ]
  - file: "fonts/materialdesignicons-webfont.ttf"
    id: mdi_font_small
    size: 14
    glyphs: [
      "\U000F05A9"   # mdi:wifi
    ]

# -------------------------
# DISPLAY CONFIGURATION
# -------------------------
display:
  - platform: ssd1306_i2c
    model: "SSD1306 128x64"
    address: 0x3C
    id: oled_display
    rotation: 0
    lambda: |-
      // Draw Wi-Fi signal strength (bars)
      int wifi = (int)id(wifi_signal_db).state;
      int bars = 0;
      if (wifi > -50) bars = 4;
      else if (wifi > -60) bars = 3;
      else if (wifi > -70) bars = 2;
      else if (wifi > -80) bars = 1;
      else bars = 0;

      for (int i = 0; i < 4; i++) {
        if (i < bars)
          it.filled_rectangle(116 + (i * 3), 2 + (3 - i) * 2, 2, (i + 1) * 2);
        else
          it.rectangle(116 + (i * 3), 2 + (3 - i) * 2, 2, (i + 1) * 2);
      }

      it.printf(0, 0, id(mdi_font_small), "\U000F05A9");  // Wi-Fi icon
      it.printf(20, 0, id(font_small), "%d%%", (int)min(max(2 * (wifi + 100.0), 0.0), 100.0));

      // Show which valve is ON
      int y = 24;
      bool any_on = false;
      const char* valve_name = "";

      if (id(valve_sw1).state) {
        it.printf(0, y, id(mdi_font), "\U000F1068");
        it.printf(34, y + 2, id(font_large), "Valve 1");
        any_on = true;
        valve_name = "Valve 1";
      } else if (id(valve_sw2).state) {
        it.printf(0, y, id(mdi_font), "\U000F1068");
        it.printf(34, y + 2, id(font_large), "Valve 2");
        any_on = true;
        valve_name = "Valve 2";
      } else if (id(valve_sw3).state) {
        it.printf(0, y, id(mdi_font), "\U000F1068");
        it.printf(34, y + 2, id(font_large), "Valve 3");
        any_on = true;
        valve_name = "Valve 3";
      } else if (id(valve_sw4).state) {
        it.printf(0, y, id(mdi_font), "\U000F1068");
        it.printf(34, y + 2, id(font_large), "Valve 4");
        any_on = true;
        valve_name = "Valve 4";
      } else if (id(valve_sw5).state) {
        it.printf(0, y, id(mdi_font), "\U000F1068");
        it.printf(34, y + 2, id(font_large), "Valve 5");
        any_on = true;
        valve_name = "Valve 5";
      }

      if (!any_on) {
        it.printf(0, y, id(mdi_font), "\U000F1067");
        it.printf(34, y + 2, id(font_large), "All Closed");
      } else {
        // Show remaining formatted time from text sensor
        it.printf(0, 52, id(font_small), "%s remaining: %s", valve_name, id(time_remaining_text).state.c_str());
      }

# -------------------------
# SPRINKLER CONTROL
# -------------------------
sprinkler:
  - id: basket_irrigation
    main_switch: "Pump"
    auto_advance_switch: "Lawn Sprinklers Auto Advance"
    valve_overlap: 2s
    valves:
      - valve_switch: "Valve 1"
        enable_switch: "Enable Valve 1"
        pump_switch_id: pump_sw
        run_duration_number:
          id: valve_1_run_duration
          name: "Valve 1 Run Duration"
          icon: "mdi:timer-outline"
          initial_value: 1
          unit_of_measurement: min
          min_value: 1
          max_value: 60
        valve_switch_id: valve_sw1

      - valve_switch: "Valve 2"
        enable_switch: "Enable Valve 2"
        pump_switch_id: pump_sw
        run_duration_number:
          id: valve_2_run_duration
          name: "Valve 2 Run Duration"
          icon: "mdi:timer-outline"
          initial_value: 1
          unit_of_measurement: min
          min_value: 1
          max_value: 60
        valve_switch_id: valve_sw2

      - valve_switch: "Valve 3"
        enable_switch: "Enable Valve 3"
        pump_switch_id: pump_sw
        run_duration_number:
          id: valve_3_run_duration
          name: "Valve 3 Run Duration"
          icon: "mdi:timer-outline"
          initial_value: 1
          unit_of_measurement: min
          min_value: 1
          max_value: 60
        valve_switch_id: valve_sw3

      - valve_switch: "Valve 4"
        enable_switch: "Enable Valve 4"
        pump_switch_id: pump_sw
        run_duration_number:
          id: valve_4_run_duration
          name: "Valve 4 Run Duration"
          icon: "mdi:timer-outline"
          initial_value: 1
          unit_of_measurement: min
          min_value: 1
          max_value: 60
        valve_switch_id: valve_sw4

      - valve_switch: "Valve 5"
        enable_switch: "Enable Valve 5"
        pump_switch_id: pump_sw
        run_duration_number:
          id: valve_5_run_duration
          name: "Valve 5 Run Duration"
          icon: "mdi:timer-outline"
          initial_value: 1
          unit_of_measurement: min
          min_value: 1
          max_value: 60
        valve_switch_id: valve_sw5

# -------------------------
# SWITCHES
# -------------------------
switch:
  - platform: gpio
    id: pump_sw
    pin: GPIO16

  - platform: gpio
    id: pump_sw1
    pin: GPIO17

  - platform: gpio
    id: valve_sw1
    pin: GPIO25

  - platform: gpio
    id: valve_sw2
    pin: GPIO26

  - platform: gpio
    id: valve_sw3
    pin: GPIO32

  - platform: gpio
    id: valve_sw4
    pin: GPIO33

  - platform: gpio
    id: valve_sw5
    pin: GPIO27

# -------------------------
# SENSORS
# -------------------------
sensor:
  - platform: wifi_signal
    name: "WiFi Signal dB"
    id: wifi_signal_db
    update_interval: 60s
    entity_category: "diagnostic"

  - platform: copy
    source_id: wifi_signal_db
    name: "WiFi Signal Percent"
    filters:
      - lambda: return min(max(2 * (x + 100.0), 0.0), 100.0);
    unit_of_measurement: "%"
    entity_category: "diagnostic"

  - platform: uptime
    name: Uptime
    entity_category: "diagnostic"




text_sensor:
  # Readable formatted text version
  - platform: template
    id: time_remaining_text
    name: "Time Remaining"
    update_interval: 1s
    icon: "mdi:timer-sand"
    lambda: |-
      int seconds = round(id(basket_irrigation).time_remaining_active_valve().value_or(0));
      int days = seconds / (24 * 3600);
      seconds = seconds % (24 * 3600);
      int hours = seconds / 3600;
      seconds = seconds % 3600;
      int minutes = seconds / 60;
      seconds = seconds % 60;

      char buffer[32];
      if (days > 0)
        snprintf(buffer, sizeof(buffer), "%dd %dh %dm %ds", days, hours, minutes, seconds);
      else if (hours > 0)
        snprintf(buffer, sizeof(buffer), "%dh %dm %ds", hours, minutes, seconds);
      else if (minutes > 0)
        snprintf(buffer, sizeof(buffer), "%dm %ds", minutes, seconds);
      else
        snprintf(buffer, sizeof(buffer), "%ds", seconds);

      return std::string(buffer);



# -------------------------
# STATUS BINARY SENSOR
# -------------------------
binary_sensor:
  - platform: status
    name: "Connection Status"

# GPIO16 Pump
# GPIO25 1
# GPIO26 2
# GPIO32 3
# GPIO33 4
# GPIO27 5
# OLED
# GPIO13 SDA
# GPIO14 SCL
1 Like

I got my board to work - if it helps anyone else, I had to source the ground from the power input terminal and then hold a wire to the actual IO pin on the chip itself for it to enter programming mode as the headers didn’t work :stuck_out_tongue:

this is a very interesting project.
I bought a 4relay board in aliexpress yesterday. i will play with it when it arrives.

But i have a little question.
I am thinking on a 4 relay board.
relay 1 is MASTER VALVE.
relay 2 is the “zone 1”
relay 3 is the “zone 2”.

When the zone 1 active, the master must be active too.
when the zone 2 active, the master must be active too.

The master setup its interesting for safety. it the valve of the zone, broke, the master will cut the wather and everything its safety.

its posible to make this?

Yes, the ESPHome irrigation component supports the concept of a master valve.

1 Like

ok, thanks a lot.
im waiting to arrive the device and i will play with it.
This is very interesting!!!

i received the esp today (4relays). But i cant flash the esphome firmware. The device give me timeouts.

I dont understand what is the problem:

  • the jumper is ok, the pins is ok. the programer is in 3.3jumper option. i power the esp32 with a external 5v power and not work.
  • i power the esp32 with a external 12v and not work
  • i power the esp32 with the 230v power suply and not work
    the problem is allways the same:
  • push the reset button and keep the reset buton.
  • power on the external power supply
  • plug the programer to the usb port
  • the device apears in the web interface of esphome, select the com port and next
  • time out and process fails
    if i release the button reset, just after release the button, the process fails

any idea?