[Solved (hw fix)] What's causing "Reset Reason: Reset over SDIO"? (and some other watchdogs...)

I hope someone can help me looking in the right direction here.
This ESP32 node is starting to drive me crazy…

It crash/reboot when I do certain things but it’s not totally consequent.
I have tried to reduce code, do it different ways and so on but no luck.

It’s a quite big yaml so I think it can be better to try to track it down a little bit more first before posting all of it.
Maybe I should try to migrate just the suspect to another ESP that I can test more easily on the bench, but not right now. (It’s 01:20 now and up for work at 06:00…)

What I have to do a couple of times (~2-10) to make it crash is in a nutshell:

  • Write to a text sensor (serial in or template button)
  • A binary template sensor evaluate the text sensor and turn on an output
  • Clear the text sensor to be able to receive a new command
  • Simulate a flowmeter by sending pulses on a GPIO that are connected to GPIO for puls_meter input. (have tried both “bit bang” on-delay-off and ledc function, no different)
  • Turn off the output after 5 sec

The debug Reset Reason reports Reset over SDIO
What does that mean, and what can cause that?

Ya, not to be rude but, if your seeking help and posting a question, please dont determine what should be shared or not shared and especially when its regarding your Esphome config… Its kind of haed to determine the problem without seeing your config, seeing logs, pictures of wiring or a diagram, etc.

We will survive and i’d bet that there’s a few people that cope with its size because, thats not unusual and code exceeding 4-500 lines or more isnt oit of the norm at all…

If you dont know whether to share details that are relevant then you should always try providing more information than less information if you actually want help and not random guesses from people.

You can leave out specific stuff like wifi, api, and most of the stuff that comes as part of a new default config file. If you added things then post it.

Post the details for any devices connected to the esp32. It would be helpful to us and very likely you too if you explain what it is you’re trying to do with this project, like what is the big picture with it?

What are you trying to control or manipulate by simulating pulses from a gpio and it would be helpful to see how your attempting to simulate it through the code you used.

I totally agree that shearing as much information possible is usually good to solve problems and I have no problem shearing my code, however it’s not very well structured and lacks English comments.
I do not expect (but would definitely appreciate if) someone took the time to and dug down into hundreds lines of messy code.

That’s why I thought trying to understand the cause of Reset Reason Reset over SDIO could be a reasonable first step and as I initially wrote, I hope someone can help me looking in the right direction, to help me find more hints to try solving problems like this, rather expecting/hoping someone to solve a complex problem for me.

substitutions:
  device_name: tank-pumpstyrning
  friendly_name: Tank Pumpstyrning
  device_description: "Pumpa på bara!"

  # Antal pulser/liter från flödesgivaren #
  pulse_rate: '396'


esphome:
  name: '${device_name}'
  friendly_name: '${friendly_name}'
  comment: '${device_description}'
  on_boot: 
#    priority: -100
    then:
      - pulse_meter.set_total_pulses: 
          id: water_flow
          value: !lambda 'return id(gfr_water_total_total_save) * ${pulse_rate};'
      - lambda: |-
          id(gf_water_total_01) = id(gfr_water_total_01_save);
          id(gf_water_total_02) = id(gfr_water_total_02_save);
          id(gf_water_total_03) = id(gfr_water_total_03_save);
          id(gf_water_total_04) = id(gfr_water_total_04_save);
          id(gf_water_total_ext) = id(gfr_water_total_ext_save);


#  includes:
#    - uart_read_line_sensor.h

esp32:
  board: esp32dev
  framework:
    type: arduino

# Enable logging
logger:

# Enable Home Assistant API
api:
  encryption:
    key: "xxxxxxxxxxxxxxxxxxxxxxxxxxx"

ota:
  - platform: esphome
    password: "xxxxxxxxxxxxxxxxxxxxxxxxxx"

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

  # Enable fallback hotspot (captive portal) in case wifi connection fails
  ap:
    ssid: "Tank-Pumpstyrning"
    password: "xxxxxxxxxxxx"

captive_portal:

#---------------

debug:
  update_interval: 5s


globals:
  # Spara flödesmätarens totala mätarställning i flash
  - id: gfr_water_total_total_save
    type: float
    restore_value: True

  # Spara vattenmängd för respektive ventil i flash
  - id: gf_water_total_01
    type: float
  - id: gfr_water_total_01_save
    type: float
    restore_value: True

  - id: gf_water_total_02
    type: float
  - id: gfr_water_total_02_save
    type: float
    restore_value: True

  - id: gf_water_total_03
    type: float
  - id: gfr_water_total_03_save
    type: float
    restore_value: True

  - id: gf_water_total_04
    type: float
  - id: gfr_water_total_04_save
    type: float
    restore_value: True

  # Spara vattenmängd för förbrukning utöver ventiler (manuell vattning) i flash
  - id: gf_water_total_ext
    type: float
  - id: gfr_water_total_ext_save
    type: float
    restore_value: True

  # Startvärde flödesmätare vid öppnad ventil
  - id: gf_water_start_value
    type: float

  # Stoppvärde flödesmätare vid stängd ventil
  - id: gf_water_stop_value
    type: float

  # Flagga för indikering av "internt" flöde via ventiler
  - id: gb_water_int
    type: bool
  # Flagga för indikering av externt/manuellt flöde utan ventiler
  - id: gb_water_ext
    type: bool


# Test för debug
  - id: test_value
    type: int
  - id: test_value2
    type: int
#  - id: boot_delay_done
#    type: bool

uart:
  tx_pin: 17
  rx_pin: 16
  baud_rate: 9600
  id: uart_bus

#------------------------------
# Ny Extrenal Component 'uartx' för hantering av seriell kommunikation direkt i ESPHome
external_components:
  - source: github://eigger/espcomponents/@latest
    components: [ uartex ]
    refresh: 1 days

uartex:
  rx_footer: "\r" # Termination character

# Skapar en textsensor som fungerar på samma sätt som i gamla custom component.
# All mottagen data läggs i en stäng och publiceras i sensorn.
text_sensor:
  - platform: uartex
    id: "uart_readline"
    lambda: |-
      return std::string(reinterpret_cast<const char*>(data), len);
#      /*
#      std::string result;
#      for (int i = 0; i < len; ++i) {
#        result += to_string(data[i]);
#      }
#      return result;
#      */

#------------------------------
# Test med en "vanlig" textsensor. Felsökning krasch + reboot vid upprepade 
#  - platform: template
#    id: "uart_readline"

  - platform: debug
#    device:
#      name: "Device Info"
    reset_reason:
      name: "Reset Reason"

#------------------------------
# Återskapa funktion för Custom Component som togs bort i version 2025.2.0

#external_components:
#  - source:
#      type: git
#      url: https://github.com/robertklep/esphome-custom-component
#    components: [ custom, custom_component ]

#- platform: custom
#  lambda: |-
#    auto my_custom_sensor = new UartReadLineSensor(id(uart_bus));
#    App.register_component(my_custom_sensor);
#    return {my_custom_sensor};
#  text_sensors:
#    id: "uart_readline"
#------------------------------






output:
# Simulera ventilflöde (f.d. trig på ultraljudsgivare)
  - platform: ledc
    pin: GPIO18
    id: sim_flow_pwm

switch:
  - platform: gpio
    pin: GPIO27
    id: relay_0 # Matning Pump Shelly Plus 1PM
  - platform: gpio
    pin: GPIO26
    id: relay_1 # Ventil 1
  - platform: gpio
    pin: GPIO25
    id: relay_2 # Ventil 2
  - platform: gpio
    pin: GPIO33
    id: relay_3 # Ventil 3
  - platform: gpio
    pin: GPIO32
    id: relay_4 # Ventil 4
#  - platform: gpio
#    pin: GPIO18
#    id: output_sim_flow # Simulera ventilflöde (f.d. trig på ultraljudsgivare)
# Simulera flöde
  - platform: template
    name: "Simulera Flöde"
    id: switch_sim_flow
    optimistic: True



one_wire:
  - platform: gpio
    pin: GPIO23
    id: dallas_1
  - platform: gpio
    pin: GPIO22
    id: dallas_2



button:
  - platform: template
    name: "Trigger 1"
    id: button_trigger_1
    on_press:
      then:
        lambda: |-
          id(uart_readline).publish_state("OPEN_VALVE_1");

  - platform: template
    name: "Trigger 2"
    id: button_trigger_2
    on_press:
      then:
        lambda: |-
          id(uart_readline).publish_state("OPEN_VALVE_2");

  - platform: template
    name: "Trigger 3"
    id: button_trigger_3
    on_press:
      then:
        lambda: |-
          id(uart_readline).publish_state("OPEN_VALVE_3");

  - platform: template
    name: "Trigger 4"
    id: button_trigger_4
    on_press:
      then:
        lambda: |-
          id(uart_readline).publish_state("OPEN_VALVE_4");

### Reset mätarställningar ###
  - platform: template
    name: "Reset Vatten Total"
    id: reset_total_total
    on_press: 
      then:
        - pulse_meter.set_total_pulses: 
            id: water_flow
            value: 0
        - lambda: 'id(gfr_water_total_total_save) = 0;'

  - platform: template
    name: "Reset Vatten Ventil 1"
    id: reset_total_valve_1
    on_press: 
      then:
        - lambda: |-
            id(gf_water_total_01) = 0;
            id(gfr_water_total_01_save) = 0;
            id(water_total_valve_01).publish_state(id(gf_water_total_01));
  - platform: template
    name: "Reset Vatten Ventil 2"
    id: reset_total_valve_2
    on_press: 
      then:
        - lambda: |-
            id(gf_water_total_02) = 0;
            id(gfr_water_total_02_save) = 0;
            id(water_total_valve_02).publish_state(id(gf_water_total_02));
  - platform: template
    name: "Reset Vatten Ventil 3"
    id: reset_total_valve_3
    on_press: 
      then:
        - lambda: |-
            id(gf_water_total_03) = 0;
            id(gfr_water_total_03_save) = 0;
            id(water_total_valve_03).publish_state(id(gf_water_total_03));
  - platform: template
    name: "Reset Vatten Ventil 4"
    id: reset_total_valve_4
    on_press: 
      then:
        - lambda: |-
            id(gf_water_total_04) = 0;
            id(gfr_water_total_04_save) = 0;
            id(water_total_valve_04).publish_state(id(gf_water_total_04));
  - platform: template
    name: "Reset Vatten Ext"
    id: reset_total_valve_ext
    on_press: 
      then:
        - lambda: |-
            id(gf_water_total_ext) = 0;
            id(gfr_water_total_ext_save) = 0;
            id(water_total_ext).publish_state(id(gf_water_total_ext));


#        - uart.write: "uart_bus: "
#        - uart.write: !lambda
#            return {id(uart_bus)};
#        - uart.write: \n
#
#        - logger.log:
#            format: "UART_bus: %.0f"
#            args: [id(uart_bus)]
#        - logger.log:
#            format: "Readline: %.0f"
#            args: [id(uart_readline)]


sensor:
  - platform: debug
    loop_time:
      name: "Loop Time"

# *** Dallas Tempsensor ***
  - platform: dallas_temp
    one_wire_id: dallas_1
    address: 0x430000003097d128
    name: "Styrbox Temperatur"
  - platform: dallas_temp
    one_wire_id: dallas_2
    address: 0x323ce1e381c1c728
    name: "Tank Temperatur"

# *** Nivågmätning tryckgivare ***
# *** 0-1m / 4-20mA --> 50Ohm = ADC 0.2-1.0V ***
# *** 0,8856 mm/liter = 1,1292 l/mm = 100 cm = 1129 l = 0,7 mV/l = 1,41 l/mV ***
  - platform: adc
    pin: A6
    id: water_tank_level
    name: "Tank Nivå"
    device_class: "volume_storage"
    unit_of_measurement: "L"
    icon: "mdi:car-coolant-level"
    update_interval: 1s
    filters:
      - calibrate_linear:
        - 0.204 -> 0
        - 1.000 -> 1129
#      - median:
#          window_size: 50
#          send_every: 5
#          send_first_at: 5
      - sliding_window_moving_average:
          window_size: 30
          send_every: 10
          send_first_at: 10
    accuracy_decimals: 0


# *** Flödesmätare 396 pulser / liter (GPIO19) ***
# *** Total Förbrukning ***
  - platform: pulse_meter
    pin: 
      number: GPIO19
      inverted: false
      mode:
        input: true
#        pulldown: true
    internal_filter: 100us
    internal_filter_mode: PULSE
    id: water_flow
    name: "Pump Vattenflöde"
    unit_of_measurement: "L/min"
    device_class: volume_flow_rate
#    icon: "${ha_icon}"
    timeout: 1s
    accuracy_decimals: 2
    filters:
      - lambda: return (x / ${pulse_rate});
    total:
      id: water_total
      name: "Pump Vatten Total"
      unit_of_measurement: "L"
      device_class: water
      state_class: total_increasing
      accuracy_decimals: 2
#      icon: "${ha_icon}"
      filters:
        - lambda: return (x / ${pulse_rate});


## *** Nivåmätning ultraljud *** === ANVÄNDS INTE - I/O NYTTJAS TILL FLÖDESMÄTARE + SIMPULS ===
#  - platform: ultrasonic
#    trigger_pin:
#      number: GPIO18
#      inverted: true
#    echo_pin:
#      number: GPIO19
## Invertera Echo om ext transistor används för att kunna driva hårdare mot jord.
##      inverted: true
##    pulse_time: 1ms
#    timeout: 10m
#    update_interval: 5s
#    id: water_tank_level_ultrasonic
#    name: "Vattentank ultraljud"
#    device_class: "volume_storage"
#    unit_of_measurement: "L"
#    icon: "mdi:car-coolant-level"
#    filters:
#      - calibrate_linear:
#        - 0.93 -> 0
#        - 0.05 -> 1000
#      - median:
#          window_size: 60
#          send_every: 6
#          send_first_at: 6
#   accuracy_decimals: 0


# *** Total förbrukning Ventil ***
  - platform: template
    id: water_total_valve_01
    name: Vatten Total Ventil 1
    lambda: return id(gf_water_total_01);
    unit_of_measurement: "L"
    icon: "mdi:pulse"
    accuracy_decimals: 2

  - platform: template
    id: water_total_valve_02
    name: Vatten Total Ventil 2
    lambda: return id(gf_water_total_02);
    unit_of_measurement: "L"
    icon: "mdi:pulse"
    accuracy_decimals: 2

  - platform: template
    id: water_total_valve_03
    name: Vatten Total Ventil 3
    lambda: return id(gf_water_total_03);
    unit_of_measurement: "L"
    icon: "mdi:pulse"
    accuracy_decimals: 2

  - platform: template
    id: water_total_valve_04
    name: Vatten Total Ventil 4
    lambda: return id(gf_water_total_04);
    unit_of_measurement: "L"
    icon: "mdi:pulse"
    accuracy_decimals: 2

  - platform: template
    id: water_total_ext
    name: Vatten Total Ext
    lambda: return id(gf_water_total_ext);
    unit_of_measurement: "L"
    icon: "mdi:pulse"
    accuracy_decimals: 2



binary_sensor:
# *** Kapasitiv Nivåvakt "Låg-Låg" ***
  - platform: gpio
    device_class: problem
    pin:
      number: GPIO21
#      inverted: true
    publish_initial_state: true
    id: tank_low_low
    name: "Tank Låg-Låg Nivå"
    filters:
      - delayed_on_off: 3s
    on_press:
      then:
        # Vänta tills ventilerna hunnit stänga och pumpen bygga tryck innan strömmen bryts
        - delay: 30s
        - switch.turn_off: relay_0
    on_release:
      then:
        - switch.turn_on: relay_0

# *** Nivågivare Låg ***
  - platform: template
    device_class: problem
    id: tank_low
    name: "Tank Låg Nivå"
    lambda: |-
      return id(water_tank_level).state < 100;
    filters:
      - delayed_on_off: 10s

# *** Nivågivare Hög - Överfyllnadsskydd (dump) ***
  - platform: template
    device_class: problem
    id: tank_high
    name: "Tank Hög Nivå"
    lambda: |-
      return id(water_tank_level).state > 900;
    filters:
      - delayed_on_off: 10s
    on_press:
      then:
        - switch.turn_on: relay_4
    on_release:
      then:
        - switch.turn_off: relay_4


# *** Nivå vid start ***
# Intern sensor för att spara flödesgivarens mätarställning när ventilen öppnar
# Används för att räkna upp totalt flöde.
  - platform: template
    id: start_update
    internal: True
    lambda: |-
      return id(relay_1).state || id(relay_2).state || id(relay_3).state || id(relay_4).state;
    on_press: 
      then:
        - lambda: |-
            id(gf_water_start_value) = id(water_total).state;
            id(gb_water_int) = true;

#Extern förbrukning om det är flöde utan någon öppen ventil
  - platform: template
    id: start_update_ext
    internal: True
    lambda: |-
      if(id(water_flow).state > 0.0 && !id(gb_water_int) && !id(relay_1).state && !id(relay_2).state && !id(relay_3).state && !id(relay_4).state){
        return true;
      }else{
        return false;
      }
    on_press: 
      then:
        - lambda: |-
            id(gf_water_start_value) = id(water_total).state;
            id(gb_water_ext) = true;


# *** Nivå vid stopp ***
# Interna sensorer för att spara flödesgivarens mätarställning när ventilen stänger
  - platform: template
    id: stop_update_1
    internal: True
    lambda: |-
      return !id(relay_1).state;
    filters: 
      - delayed_on: 1s
    on_press: 
      then:
        - lambda: |-
            id(gf_water_stop_value) = id(water_total).state;
            if(id(gf_water_start_value) > -1) {
              id(gf_water_total_01) = id(gf_water_total_01) + (id(gf_water_stop_value) - id(gf_water_start_value));
              id(water_total_valve_01).publish_state(id(gf_water_total_01));
            }
            id(gf_water_start_value) = -1;
            id(gb_water_int) = false;
            id(gb_water_ext) = false;

  - platform: template
    id: stop_update_2
    internal: True
    lambda: |-
      return !id(relay_2).state;
    filters: 
      - delayed_on: 1s
    on_press: 
      then:
        - lambda: |-
            id(gf_water_stop_value) = id(water_total).state;
            if(id(gf_water_start_value) > -1) {
              id(gf_water_total_02) = id(gf_water_total_02) + (id(gf_water_stop_value) - id(gf_water_start_value));
              id(water_total_valve_02).publish_state(id(gf_water_total_02));
            }
            id(gf_water_start_value) = -1;
            id(gb_water_int) = false;
            id(gb_water_ext) = false;

  - platform: template
    id: stop_update_3
    internal: True
    lambda: |-
      return !id(relay_3).state;
    filters: 
      - delayed_on: 1s
    on_press: 
      then:
        - lambda: |-
            id(gf_water_stop_value) = id(water_total).state;
            if(id(gf_water_start_value) > -1) {
              id(gf_water_total_03) = id(gf_water_total_03) + (id(gf_water_stop_value) - id(gf_water_start_value));
              id(water_total_valve_03).publish_state(id(gf_water_total_03));
            }
            id(gf_water_start_value) = -1;
            id(gb_water_int) = false;
            id(gb_water_ext) = false;

  - platform: template
    id: stop_update_4
    internal: True
    lambda: |-
      return !id(relay_4).state;
    filters: 
      - delayed_on: 1s
    on_press: 
      then:
        - lambda: |-
            id(gf_water_stop_value) = id(water_total).state;
            if(id(gf_water_start_value) > -1) {
              id(gf_water_total_04) = id(gf_water_total_04) + (id(gf_water_stop_value) - id(gf_water_start_value));
              id(water_total_valve_04).publish_state(id(gf_water_total_04));
            }
            id(gf_water_start_value) = -1;
            id(gb_water_int) = false;
            id(gb_water_ext) = false;

  - platform: template
    id: stop_update_ext
    internal: True
    lambda: |-
      return id(gb_water_ext) = true && id(water_flow).state == 0.0;
    filters: 
      - delayed_on: 1s
    on_press: 
      then:
        - lambda: |-
            id(gf_water_stop_value) = id(water_total).state;
            if(id(gf_water_start_value) > -1) {
              id(gf_water_total_ext) = id(gf_water_total_ext) + (id(gf_water_stop_value) - id(gf_water_start_value));
              id(water_total_ext).publish_state(id(gf_water_total_ext));
            }
            id(gf_water_start_value) = -1;
            id(gb_water_int) = false;
            id(gb_water_ext) = false;


# *** Spara värden från flödesmätare till flash ***
  - platform: template
    id: save_total_water
    lambda: return id(water_flow).state == 0.0;
    filters:
      - delayed_on: 5s
    on_press:
      then:
        - lambda: |-
            id(gfr_water_total_total_save) = id(water_total).state;
            id(gfr_water_total_01_save) = id(gf_water_total_01);
            id(gfr_water_total_02_save) = id(gf_water_total_02);
            id(gfr_water_total_03_save) = id(gf_water_total_03);
            id(gfr_water_total_04_save) = id(gf_water_total_04);
            id(gfr_water_total_ext_save) = id(gf_water_total_ext);
        - logger.log: "*** Vattenflöden sparad i flash ***"
#            format: "Save Ext: %.0f"
#            args: [id(gf_water_total_ext)]
        





### Test-funktioner ###
# Simulera flöde på utgång för f.d. Trig Ultraljusdsgivare
  - platform: template
    id: test_simulate_flow
    lambda: |-
      return id(relay_1).state || id(relay_2).state || id(relay_3).state || id(relay_4).state || id(switch_sim_flow).state;
    on_press:
      - output.turn_on: sim_flow_pwm
      - output.ledc.set_frequency:
          id: sim_flow_pwm
          frequency: "20Hz"
      - output.set_level:
          id: sim_flow_pwm
          level: "50%"
#      - while:
#          condition:
#            - binary_sensor.is_on: test_simulate_flow
#          then:
#            - switch.turn_on: output_sim_flow
#            - delay: 1ms
#            - switch.turn_off: output_sim_flow
#            - delay: 1ms
    on_release: 
      then:
      - output.turn_off: sim_flow_pwm
#        - switch.turn_off: output_sim_flow




# *** VENTILSTYRNING ***
# - Utgång aktiveras av att rätt kommado skrivs i text sensor "uart_readline"
# - Kommando kan komma via UART eller annan funktion (t.ex. button)
# - Utgången går automatiskt från efter 5sek
# - Kommer ett nytt inom 5sek behålls utgången till
# - Kommando som kommer tätare än 0.5sek ignoreras.
# - Aktivering av utgång blockeras om lågnivå-vakt tank_low_low == true



  # *** VENTIL 1 ***
  - platform: template
    id: water_valve1
    name: "Water Valve 1"
    icon: "mdi:pipe-valve"
    lambda: |-
      if (id(uart_readline).state == "OPEN_VALVE_1") {
        return true;
      } else {
        return false;
      }
    filters:
      - delayed_off: 5s
    on_press:
      then:
        - switch.turn_on: relay_1
        - lambda: id(uart_readline).publish_state("");
        - uart.write: "Valve1 ON\n"
    on_release:
      then:
        - switch.turn_off: relay_1
        - uart.write: "Valve1 OFF\n"

  # *** VENTIL 2 ***
  - platform: template
    id: water_valve2
    name: "Water Valve 2"
    icon: "mdi:pipe-valve"
    lambda: |-
      static bool b_prohibit = false;
      static bool b_clock_running = false;
      static int i_delay_on = 0;
      static int now = 0;
      if (id(uart_readline).state == "OPEN_VALVE_2") {
        /*# Command recieved */
        if (b_clock_running == false && b_prohibit == false) {
          /*# Init on delay timer (ms) */
          i_delay_on = 0.5*1000;
          now = clock();
          b_clock_running = true;
          id(uart_readline).publish_state("");
          return false;
        } else {
          /*# New command recieved to early, abort on_delay timer */
          id(uart_readline).publish_state("");
          b_clock_running = false;
          return false;
        }
      } else {
        if (b_clock_running == true && b_prohibit == false) {
          if (clock() - now >i_delay_on) {
            /*# Time is up */
            b_clock_running = false;
            b_prohibit = true;
            if (id(tank_low_low).state == false) {
              return true;
            } else {
              return false;
            }
          } else {
            return false;
          }
        }
        b_prohibit = false;
        b_clock_running = false;
        return false;
      } 
    filters:
      - delayed_off: 5s
    on_press:
      then:
        - switch.turn_on: relay_2
        - uart.write: "Valve2 ON\n"
    on_release:
      then:
        - switch.turn_off: relay_2
        - uart.write: "Valve2 OFF\n"

  # *** VENTIL 3 ***
  - platform: template
    id: water_valve3
    name: "Water Valve 3"
    icon: "mdi:pipe-valve"
    lambda: |-
      static bool b_prohibit = false;
      static bool b_clock_running = false;
      static int i_delay_on = 0;
      static int now = 0;
      if (id(uart_readline).state == "OPEN_VALVE_3") {
        /*# Command recieved */
        if (b_clock_running == false && b_prohibit == false) {
          /*# Init on delay timer (ms) */
          i_delay_on = 0.5*1000;
          now = clock();
          b_clock_running = true;
          id(uart_readline).publish_state("");
          return false;
        } else {
          /*# New command recieved to early, abort on_delay timer */
          id(uart_readline).publish_state("");
          b_clock_running = false;
          return false;
        }
      } else {
        if (b_clock_running == true && b_prohibit == false) {
          if (clock() - now >i_delay_on) {
            /*# Time is up */
            b_clock_running = false;
            b_prohibit = true;
            if (id(tank_low_low).state == false) {
              return true;
            } else {
              return false;
            }
          } else {
            return false;
          }
        }
        b_prohibit = false;
        b_clock_running = false;
        return false;
      } 
    filters:
      - delayed_off: 5s
    on_press:
      then:
        - switch.turn_on: relay_3
        - uart.write: "Valve3 ON\n"
    on_release:
      then:
        - switch.turn_off: relay_3
        - uart.write: "Valve3 OFF\n"

  # *** VENTIL 4 ***
  - platform: template
    id: water_valve4
    name: "Water Valve 4"
    icon: "mdi:pipe-valve"
    lambda: |-
      static bool b_prohibit = false;
      static bool b_clock_running = false;
      static int i_delay_on = 0;
      static int now = 0;
      if (id(uart_readline).state == "OPEN_VALVE_4") {
        /*# Command recieved */
        if (b_clock_running == false && b_prohibit == false) {
          /*# Init on delay timer (ms) */
          i_delay_on = 0.5*1000;
          now = clock();
          b_clock_running = true;
          id(uart_readline).publish_state("");
          return false;
        } else {
          /*# New command recieved to early, abort on_delay timer */
          id(uart_readline).publish_state("");
          b_clock_running = false;
          return false;
        }
      } else {
        if (b_clock_running == true && b_prohibit == false) {
          if (clock() - now >i_delay_on) {
            /*# Time is up */
            b_clock_running = false;
            b_prohibit = true;
            if (id(tank_low_low).state == false) {
              return true;
            } else {
              return false;
            }
          } else {
            return false;
          }
        }
        b_prohibit = false;
        b_clock_running = false;
        return false;
      } 
    filters:
      - delayed_off: 5s
    on_press:
      then:
        - switch.turn_on: relay_4
        - uart.write: "Valve4 ON\n"
    on_release:
      then:
        - switch.turn_off: relay_4
        - uart.write: "Valve4 OFF\n"







#        - logger.log:
#            format: "test_value: %i"
#            args: [id(test_value)]
#        - logger.log:
#            format: "test_value2: %i"
#            args: [id(test_value2)]








 #       - lambda: id(stored_raw_value) = (float) id(soil_moisture_sensor).get_value();
 #       - logger.log:
 #           format: "Rådata: %.0f"
 #           args: [id(stored_raw_value)]


Googling only SDIO points me mostly to secure communication to SD-cards.
My thirst thought was that this must be wrong in this case, but when I think about it a little more the crashes always occurs when I sending a new command to the text sensor around the same time when I’m writing to some global variables that are saved in flash by using restore_value: True.

I think I have read somewhere that the internal flash is handled as a SD-card “under the hood”.
Maybe some kind of time racing can occur under special circumstances if writing to a variable happens to be at the same time as it’s saved to flash?

I tried som more, like removing the part saving to flash variables and adjusting some delays.
That does not change the behavior, but it gave some different Reset Reasons below.

Back to the originally code (I hope…) the behavior remains the same but the Reset Reason: Reset over SDIO has not been seen any more.

Instead I now got some other seemingly random:

  • Reset due to other watchdogs (most common)
  • Software reset due to exception/panic.
  • Reset due to task watchdog

Do you get any warnings when you compile?
Could you try to catch some hint from esphome log just before it misbehaves?

Also, why you have two one-wire buses?
What adc pin is A6??

Also don’t exclude possibility of power/wiring issue.

Not what I have noticed.

Not very much I’m afraid. I have noticed some warning a few times regarding something took a little more time than expected just after boot, but I don’t remember wat it was. I’ll try to keep an eye on it.

According to the debug: Loop Time sensor the loop time is sometimes quite high, jumping around 16-60ms, but I can’t see any direct patterns when the reboot occurs.

Looks like the error occurs short after the pulses into the pulse_sensor has stopped.
EDIT 1: But before the 1s timout for the pulse_meter has published 0.0 L/min

[12:40:23][D][sensor:094]: 'Pump Vattenflöde': Sending state 3.02897 L/min with 2 decimals of accuracy
[12:40:23][D][sensor:094]: 'Pump Vatten Total': Sending state 21.60859 L with 2 decimals of accuracy
[12:40:23][D][sensor:094]: 'Pump Vattenflöde': Sending state 3.02891 L/min with 2 decimals of accuracy
[12:40:24][D][sensor:094]: 'Pump Vatten Total': Sending state 21.61111 L with 2 decimals of accuracy
[12:40:24][D][sensor:094]: 'Pump Vattenflöde': Sending state 3.02897 L/min with 2 decimals of accuracy
[12:40:24][D][binary_sensor:036]: 'Water Valve 1': Sending state OFF
[12:40:24][D][switch:016]: 'relay_1' Turning OFF.
[12:40:24][D][switch:055]: 'relay_1': Sending state OFF
WARNING tank-pumpstyrning @ 192.168.1.245: Connection error occurred: [Errno 104] Connection reset by peer
INFO Processing unexpected disconnect from ESPHome API for tank-pumpstyrning @ 192.168.1.245
WARNING Disconnected from API
INFO Successfully connected to tank-pumpstyrning @ 192.168.1.245 in 0.011s
INFO Successful handshake with tank-pumpstyrning @ 192.168.1.245 in 0.090s
[12:41:03][D][sensor:094]: 'Loop Time': Sending state 63.00000 ms with 0 decimals of accuracy
[12:41:05][D][sensor:094]: 'Tank Nivå': Sending state -2.17480 L with 0 decimals of accuracy
[12:41:08][D][sensor:094]: 'Loop Time': Sending state 18.00000 ms with 0 decimals of accuracy
[12:41:13][D][sensor:094]: 'Vatten Total Ventil 1': Sending state 216.88132 L with 2 decimals of accuracy
[12:41:13][D][sensor:094]: 'Loop Time': Sending state 17.00000 ms with 0 decimals of accuracy
[12:41:15][D][sensor:094]: 'Vatten Total Ventil 3': Sending state 4.30050 L with 2 decimals of accuracy
[12:41:15][D][sensor:094]: 'Tank Nivå': Sending state -1.32379 L with 0 decimals of accuracy
[12:41:18][D][sensor:094]: 'Loop Time': Sending state 17.00000 ms with 0 decimals of accuracy
[12:41:21][D][sensor:094]: 'Vatten Total Ext': Sending state 7.02525 L with 2 decimals of accuracy
[12:41:22][D][dallas.temp.sensor:054]: 'Styrbox Temperatur': Got Temperature=30.4°C
[12:41:22][D][sensor:094]: 'Styrbox Temperatur': Sending state 30.43750 °C with 1 decimals of accuracy
[12:41:22][D][sensor:094]: 'Vatten Total Ventil 2': Sending state 40.17424 L with 2 decimals of accuracy
[12:41:23][D][sensor:094]: 'Loop Time': Sending state 33.00000 ms with 0 decimals of accuracy
[12:41:25][D][sensor:094]: 'Tank Nivå': Sending state -0.04728 L with 0 decimals of accuracy
[12:41:26][I][safe_mode:041]: Boot seems successful; resetting boot loop counter
[12:41:26][D][esp32.preferences:114]: Saving 1 preferences to flash...
[12:41:26][D][esp32.preferences:143]: Saving 1 preferences to flash: 0 cached, 1 written, 0 failed
[12:41:26][D][dallas.temp.sensor:054]: 'Tank Temperatur': Got Temperature=19.8°C
[12:41:26][D][sensor:094]: 'Tank Temperatur': Sending state 19.75000 °C with 1 decimals of accuracy
[12:41:28][D][sensor:094]: 'Loop Time': Sending state 47.00000 ms with 0 decimals of accuracy
[12:41:33][D][sensor:094]: 'Loop Time': Sending state 22.00000 ms with 0 decimals of accuracy
[12:41:35][D][sensor:094]: 'Tank Nivå': Sending state -0.99284 L with 0 decimals of accuracy4]: 'Tank Nivå': Sending state -0.04728 L with 0 decimals of accuracy

It was to many with three! :wink:
One is an internal temp sensor on the pcb. The other one is an external temp sensor I added later to a spare I/O outside a level shifter. Felt like the easiest way at the time.

CH6 (GPIO34)
I have no idea why I specified it that way, but it seems to compile and work… :thinking:
I added that last year, it’s for a water level pressure sensor.
I have changed it to GPIO34 now, but it’s still the same.

Absolutely possible, but in this case I don’t think that’s the most suspect as I have not had any issues earlier on this device for the almost two years that it has been used.
The problems came now right after I added the code and functions for the pulse_meter and handling that data.
Most of that code was copied from another project that has not showed any kind of problems like this.

16 is normal, 60 is not.

I don’t know your pcb, but one-wire is a bus. If you can, connect them to same one.

If your log is not giving you the answer, I don’t know what to suggest other than eliminate something one by one and retry.

Yes, I don’t know how much normal fluctuation is to be expected, but the high loop times is something I think maybe could be a clue.

I’m looking at the loop times for an hour (5s update) just sitting doing “nothing” and it shows around 15-20ms for most of the time. Exactly once every minute it peeks at 35-40ms, I assuming that is the standard sensor updates.
Little more randomly, 5-10 times it peeks at 45ms.

Looking outside that hour there are a few higher peeks at 50-60ms but they are never right before a reboot what I can see.

Yes I’m aware about how the one-wire bus works. The two buses was a conscious choice.

  1. I already had the temp senor on the pcb in place without any easy access to the I/O.
  2. I had a spare I/O already connected to a 3,3/5V level shifter accessible from outside the box. Its also always good to avoid direct physical access to the ESP32 I/O pins for field devices if possible.

I imagined that…

Most people dont even make the effort to create code comments so, no bug deal if there aren’t any but, its definitely a good habit to use comments and you wont get any discouraging comments from me regarding comments and i fully support people use them but, its not mandatory.

Over time you will learn to read through code and quickly track whats going on and what does what so, i understand it may be easier for some than others but again, being able to see the code with or without comments is really not something I would consider optional and should just be posted.

This goes back to the main theme ive my comments and posting code lol. I will help you and many others will too but, there’s gotta be code we can see inorder to clean it up.

Seeing the code is how we would diagnose a simple gpio conflict where you assigned a gpio connected to internal flash and is the cause of this issue and also keep in mind that there is very rarely a single problem that will have a single cause and a single solution. For many problems, there are potentially multiple things that can cause a single problem and thats again why posting more details is better than fewer details. It allows people to easily do a process of elimination and for example, i would first look at which gpio you are using and 100% know if it is an issue or not an issue and then i can move on to the next thing I want to check for potential issues.

Hopefully that makes sense for you.

Ya… You’ve definitely got a few lines of code there lol.

Is there an interval that its resetting or is it doing a boot loop that continuously reboots or is there a specific action that causes this?

Did you write all this code at once and do a single flash or did you make this over time by adding things incrementally? Whenever doing large or complex configs, its a good idea to add things and compile it in stages that way its easier to determine where the issue is coming from.

If you make a section, compile it and it all works fine then you move on to add more. After you add a new section and it causes issues or randomly reboots, then you can narrow the cause down to the last section you added to your configuration and it automatically eliminates a large %% of code that you dont need to scrutinize while searching for the root cause.

Does that make sense?

Can you post a copy of your log as it boots up?

Specific action. It reboot sometimes when I activate a command through a text sensor that controls one of four valves. Briefly explained in post 1.

That’s mostly how I proceed.

I have tried that a lot the last days, but the issues seems so random that I can’t find any reliable repeatable behavior after any changes.

Adding over time. The parts that seems to cause the issue are the first/oldest parts from two years ago that as far as I know have worked well until now…
I discovered the issues for the first time this weekend after adding code for the water flow sensor.

Logs from right before and after reboot is in post 7.

I think I may be on something here.
Trying to sort thins out a little more I realize that the crashes always seems to occur right after and less when a second after the output turns off and shuts the valve.

That together with the very random Reset Reasons made me start suspecting interferens caused by the valve coil opening, even though it’s 230VAC valves controlled by a relay board and the cables are reasonable separated from signal cables.

I disconnected one of the valves and could not make it crash on that output anymore. Reconnecting it makes it crash again.
I found some old RC-filters that I connected to the valve coils, and so far I have not be able to make it crash again, but time will tell…

This was quite surprising as those valves, cables, relays and so on has not been touched or changed in any way since I first installed it two years ago.
It’s possible that it have been on the edge all time and now tipping over for some reason.
It’s also possible that it have been there all the time and I just have not noticed that it rebooted sometimes as it probably just continued from there it was.

I will try to test and provoke it som more, but for now I hope this is the reason and solution.
Tracking down the root cause and find where the interference is picked up could be a really deep rabbit hole that I think I don’t will going in to if i absolutely don’t have to.

You should always use rc-snubber with valve coils controlled by regular relays.

Yes, I know… But AC-coils are usually not nearly as ugly as DC are.
However, newer bad to add some, and hopefully are the problems solved now.

For DC coils you can use flyback diode, for AC you need to content with snubber.

Yes,a diod is usually the first chose for DC-coils. The only downside is that the diod keeps the field in the coil a little longer causing a slower turn off time.
But that is usually not a big deal in most cases, and if it is, use a RC-filter instead .

Summary & Solution
The problem
ESP crashes/reboots seemingly randomly during valve cycles.

The reason
Caused by electromagnetic interference from valve coils when there are turned off, due to insufficient protections of the valves.
Interference was most likely picked up by external I/O- and/or power cables to the ESP.

The solution
RC-filters (snubbers) added to each valve coil.

The conclusion
A couple of hundreds of valve switching test cycles later, and not a single crash/reboot.
Case closed.

Nice that you resolved it! :+1:
This is a school example of a topic where time is wasted because of missing information about the setup.
And modifying the topic title is making it little bit confusing as well.

Yes, thanks for the engagement. :slightly_smiling_face:

I would rather say that it’s a school example of there the initial clues led down the wrong path until some more common patterns were found.
I don’t know what more I could have shared that was more relevant for what was known at the time.

It is very hard, almost impossible, to fully describe a setup in a proper and understandable way remotely without making some kind of a full documentation with schematics, drawings, pictures datasheets and so on, something we rarely see for hobby/DIY projects.

Remember that the original question only was what the Reset Reason “Reset over SDIO” actually meant, and that the setup has worked for two years without any problem noticed, until just some new code was added…
And from there we went quickly down the rabbit hole together. :upside_down_face:

I was only adding information as the discussions and conclusions went further away from the original title.
However, the title was always meant to be formatted as a question, but it looks like that went messed up. Corrected now.
As the actual reason and solution were quite far away from the initial question, I can agree that the title maybe looks a bit misleading, but as the journey went I think it’s ok. Both leaving it as original or totally change it are both worse alternatives in my opinion.


At the end of the day I think we can all agree that we will continue to learn from our experiences, mistakes, discussions and opinions.
Regardless of we are asking for help, trying to help or just reading later on.
And that’s the strength of the community. :muscle: