ESP32 freezing every night

Try this code

# ─────────────── Default values ───────────────
substitutions:
  friendly_name: "UNSET_FRIENDLY_NAME"
  device_id:    "UNSET_DEVICE_ID"
  description:  "UNSET DESCRIPTION"
  area:         "UNSET AREA"
  is_esp_32:    "true"
  logger:       "INFO"

  inside_temp_sensor_id:  sensor.climate_temperature_2
  inside_temp_name:       "OFFICE"
  outside_temp_sensor_id: sensor.climate_temperature
  outside_temp_name:      "OUTSIDE"

# ─────────────── Wi‑Fi: keep the link alive & reboot if lost ───────────────
wifi:                        # <<<  NEW
  ssid:      !secret wifi_ssid
  password:  !secret wifi_pass
  power_save_mode: none      # stay awake → quicker reconnect
  reboot_timeout: 5min       # full restart only if Wi‑Fi gone 5 min
  # optional – comment if you don’t care
  # manual_ip:               # avoids DHCP dance right when router reboots
  #   static_ip:  192.168.1.120
  #   gateway:    192.168.1.1
  #   subnet:     255.255.255.0
  on_disconnect:             # blink RGB while offline  <<<  NEW
    then:
      - light.turn_on:
          id: rgb_led
          red: 100%
  on_connect:
    then:
      - light.turn_off: rgb_led

# ─────────────── Native API: don’t reboot just because HA is down ──────────
api:                         # <<<  NEW
  reboot_timeout: 0s         # Wi‑Fi watchdog already covers us
  encryption:
    key: !secret api_key

ota:
  password: !secret ota_pass

# ─────────────── Logging ───────────────
logger:
  level: INFO

# ─────────────── Pins, buses, framework (unchanged) ───────────────
i2c:
  - id: bus_a
    sda: GPIO1
    scl: GPIO2
    scan: false

spi:
  clk_pin:  GPIO7
  miso_pin: GPIO5
  mosi_pin: GPIO6

one_wire:
  - pin: GPIO10
    platform: gpio

esp32:
  board: esp32-c6-devkitc-1
  variant: esp32c6
  flash_size: 4MB
  framework:
    type: esp-idf
    version: 5.3.1
    platform_version: 6.9.0
    sdkconfig_options:
      CONFIG_ESPTOOLPY_FLASHSIZE_DETECT: y
      CONFIG_ESPTOOLPY_FLASHSIZE_4MB:  y
      CONFIG_OPENTHREAD_ENABLED:       n
      CONFIG_USE_MINIMAL_MDNS:         y
      # Memory optimisation
      COMPILER_OPTIMIZATION_SIZE:              y
      COMPILER_OPTIMIZATION_ASSERTIONS_DISABLE:y
      CONFIG_HEAP_POISONING_DISABLED:          y

esphome:
  friendly_name: $friendly_name
  name:          $device_id
  name_add_mac_suffix: false
  comment: "${description}"
  area:   $area
  includes:
    - ./common/yaml/functions.h
  project:
    name: "Waveshare.ESP32-C6-LCD-1-47in"
    version: "v"
  platformio_options:
    board_build.flash_mode: dio
    build_flags:
      - "-DI2C_NUM_1=I2C_NUM_0"
      - "-Wl,-Map,output.map"
    board_build.arduino.memory_type: qio_opi
    platform_packages:
      - platformio/toolchain-xtensa-esp-elf @ 14.2.0+20241119

# (lights, sensors, fonts, colours … unchanged – trimmed for brevity)

# ─────────────── Display with watchdog feed ───────────────
display:
  - platform: st7789v
    cs_pin: GPIO14
    dc_pin:
      number: GPIO15
      ignore_strapping_warning: true
    reset_pin: GPIO21
    model: Waveshare 1.47in 172X320
    id: tft_ha
    rotation: 0
    pages:
      - id: page1
        lambda: |-
          App.feed_wdt();            // <<<  NEW – yield to RTOS first
          // id(tft_ha).update();    // (uncomment if you ever cache tiles)

          /*  … your original drawing code here – untouched …  */

          // inside the heavy for‑loop that draws the gradient,
          // add an extra feed every ~32 pixels so a huge bar
          // can’t starve networking for >100 ms
          for (int i = rect_start_x; i <= rect_end_x; i++) {
            DRAW_TEMP_COL(i);
            if ((i & 31) == 0) App.feed_wdt();   // <<< NEW
          }

          /*  … rest of the lambda unchanged …  */

# ─────────────────────────────────────────────────────────────

yields often enough to keep the watchdog happy and gives the Wi‑Fi stack clear rules for surviving your 02 : 29 router reboot.

Tweak Purpose
wifi.power_save_mode: none Keeps the radio awake so reconnect is immediate.
wifi.reboot_timeout: 5min Hard‑reset if the stack wedges after a long outage.
api.reboot_timeout: 0s Don’t reset just because Home Assistant is offline.
App.feed_wdt() at top of lambda Gives control back to the RTOS before the heavy drawing work.
Extra feed_wdt every 32 pixels Stops the gradient loop from blocking the TCP/IP task during outages.
LED feedback on on_disconnect / on_connect Visual clue that the node is offline while the router reboots.

Thank you. I will try that and report back.

1 Like

Okay sorry it took so long, but i made a new discovery reproducing the issue.
Restarting zigbee2mqtt makes my ESP crash. I guess it’s because the temperature sensors become unavailable temporarily.

I reduced the drawing lambda to a very simple time display only, but I can still freeze the device by restarting z2m.

This looks very interesting.
Can you share the functions: PixelToTemperature, TemperatureToColor, TemperatureToPixel ?

Yes, sorry for the late reply.

int TemperatureToPixel(float temp_current, float temp_min = -10.0f, float temp_max = 40.0f, int pixel_min = 316, int pixel_max = 24) {
    // Handle edge case where min and max might be equal
    if (temp_min == temp_max) {return (pixel_min + pixel_max) / 2;}
    
    // Inverse lerp and clamp
    float normalized = (temp_current - temp_min) / (temp_max - temp_min);
    if (normalized < 0.0f) normalized = 0.0f;
    if (normalized > 1.0f) normalized = 1.0f;
    
    // Map to y range
    return pixel_min + static_cast<int>(normalized * (pixel_max - pixel_min));
}

float PixelToTemperature(int y, float temp_min = -10.0f, float temp_max = 40.0f, int pixel_min = 316, int pixel_max = 24) {
  // Handle edge case where y range is zero
  if (pixel_min == pixel_max) { return (temp_min + temp_max) / 2.0f; }
  
  // Normalize the y position (inverse of the pixel mapping)
  float normalized = static_cast<float>(y - pixel_min) / (pixel_max - pixel_min);
  
  // Clamp the normalized value between 0 and 1
  if (normalized < 0.0f) normalized = 0.0f;
  if (normalized > 1.0f) normalized = 1.0f;
  
  // Map to temperature range
  return temp_min + normalized * (temp_max - temp_min);
}

Color TemperatureToColor(float celsius) {
  // Define temperature-color mapping points (temperature in Celsius)
  static const std::map<float, Color> tempColorMap = {
    { -10.0f, Color(100, 100, 255) },   // Very cold blue
    { 0.0f,   Color(150, 200, 255) },   // Ice blue
    { 10.0f,  Color(100, 220, 200) },   // Cool green
    { 20.0f,  Color(150, 255, 150) },   // Light green
    { 30.0f,  Color(255, 200, 100) },   // Warm yellow
    { 40.0f,  Color(255, 100, 50) },    // Hot orange
    { 50.0f,  Color(255, 50, 0) }       // Very hot red
  };

  // Handle temperatures below the lowest defined point
  if (celsius <= tempColorMap.begin()->first) {
    return tempColorMap.begin()->second;
  }

  // Handle temperatures above the highest defined point
  if (celsius >= tempColorMap.rbegin()->first) {
    return tempColorMap.rbegin()->second;
  }

  // Find the two nearest temperature points
  auto upper = tempColorMap.upper_bound(celsius);
  auto lower = upper;
  --lower;

  // Calculate interpolation factor
  float tempRange = upper->first - lower->first;
  float factor = (celsius - lower->first) / tempRange;

  // Interpolate between the two colors
  const Color& lowerColor = lower->second;
  const Color& upperColor = upper->second;

  return Color(
    static_cast<uint8_t>(lowerColor.red + factor * (upperColor.red - lowerColor.red)),
    static_cast<uint8_t>(lowerColor.green + factor * (upperColor.green - lowerColor.green)),
    static_cast<uint8_t>(lowerColor.blue + factor * (upperColor.blue - lowerColor.blue))
  );
}

Suffered similar issues as the topic of this thread says.

And I think I nailed it down being sort of a memory leak or such like with the web_server component and esp-idf ?

Setup:
2 ESP32 devices both with 2 reed sensors attached.
Same code, only differy by devincename and IP.

What I found out in the past weeks.

framework: arduino + web_server component ==> works for days,weeks, …
framework: esp-idf + web_server component ==> fails with 24hrs
framework: arduino no web_server component ==> works for days, weeks, …
framework: esp-idf no web_server component ==> wirks for days, weeks, …

Anyone with similar experiences?

btw, when the esp with esp-idf + web_server seemed dead/frozen I recently found out that I was still able to ping the device. but a portscanner did show that port 80 wasn’t there while web_server should have been active. So I’m pretty sure about the issue might have to do with the web_server.

But I just realised there’s version 2025.9.1 … perhaps it’s solved in that release.