Preview build — page text is a draft awaiting human review. Code badges come from real compiles. The flasher is real but not yet hardware-tested; the Playground is still a preview.
Watchdog reset · ESP32

Fix ESP32 “rst:0x8 (TG1WDT_SYS_RESET)” reset loop

A hardware timer watchdog reset the ESP32 — interrupts were blocked too long. Shorten ISRs, avoid Serial/delay in interrupts, and check bit-banged LED libraries.

ESP32ESP32-S3ESP32-CAMUpdated 3 Nov 2026

Community draft — report a mistake

What you see

OutputOutput
ets Jul 29 2019 12:21:46

rst:0x8 (TG1WDT_SYS_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT)
configsip: 0, SPIWP:0xee
clk_drv:0x00,q_drv:0x00,d_drv:0x00,cs0_drv:0x00,hd_drv:0x00,wp_drv:0x00
mode:DIO, clock div:1
load:0x3fff0030,len:4888
load:0x40078000,len:16516
load:0x40080400,len:4
load:0x40080404,len:3476
entry 0x400805b4
Started
ets Jul 29 2019 12:21:46

rst:0x8 (TG1WDT_SYS_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT)

The ESP32 has hardware watchdogs in two timer groups. Timer Group 1 backs up the interrupt watchdog: if interrupts stay blocked so long that even the panic handler can’t run, it resets the whole system, and the next boot prints rst:0x8 TG1WDT_SYS_RESET.

That almost always points at your interrupt code or a library that turns interrupts off for too long. See also Interrupt wdt timeout on CPU1.

Quick fix — try in order

  1. 1

    Keep interrupt handlers tiny

    In an attachInterrupt() handler only set a volatile flag or counter. Move Serial.print, delay(), I2C, Wi-Fi and display code to loop().

  2. 2

    Mark ISRs IRAM_ATTR

    void IRAM_ATTR onPulse() { ... }. Without it, an interrupt arriving while flash is busy can stall and trip the watchdog.

  3. 3

    Look for noInterrupts() and long critical sections

    Code that disables interrupts and then loops (bit-banged NeoPixels with hundreds of LEDs, old DHT or OneWire versions, portENTER_CRITICAL around slow code) starves the watchdog. Update the library or use RMT-based drivers.

  4. 4

    Check for a noisy interrupt pin

    A floating input or a bouncing switch on a CHANGE interrupt can fire thousands of times a second. Add INPUT_PULLUP and debounce in software.

  5. 5

    Upload the reset-reason sketch to confirm

    It prints the last reset reason. INT_WDT confirms an interrupt-watchdog problem in your code.

Diagnostic sketch

Verified — core esp32 3.3.12, 3 Nov 2026Compile-only
esp32-reset-reason.inoArduino C++Open in PlaygroundOpen in PlaygroundFlash
// FlashReady diagnostic: why did my ESP32 reset?
// Prints the reset reason, chip, flash and PSRAM details once at boot, then a heartbeat
// every 5 s. If the uptime keeps going back to 0, the board is rebooting - and the
// "Reset reason" line tells you which kind of reset it was.
// Works on ESP32, ESP32-C3 and ESP32-S3 (on C3/S3 native USB set Tools -> USB CDC On Boot -> Enabled).
#include <esp_system.h>
#include <esp_flash.h>

const char *reasonText(esp_reset_reason_t r) {
  switch (r) {
    case ESP_RST_POWERON:   return "POWERON - power applied or EN/RST pressed";
    case ESP_RST_EXT:       return "EXT - external reset pin";
    case ESP_RST_SW:        return "SW - ESP.restart() or a library called esp_restart()";
    case ESP_RST_PANIC:     return "PANIC - crash (Guru Meditation); scroll up for the backtrace";
    case ESP_RST_INT_WDT:   return "INT_WDT - interrupt watchdog (an ISR or critical section ran too long)";
    case ESP_RST_TASK_WDT:  return "TASK_WDT - task watchdog (loop() blocked too long)";
    case ESP_RST_WDT:       return "WDT - other watchdog (RTC or timer group)";
    case ESP_RST_DEEPSLEEP: return "DEEPSLEEP - woke from deep sleep";
    case ESP_RST_BROWNOUT:  return "BROWNOUT - supply dipped below ~2.4 V; fix the power";
    case ESP_RST_SDIO:      return "SDIO - reset over SDIO";
    default:                return "other / unknown";
  }
}

void setup() {
  Serial.begin(115200);
  delay(1500);  // give native-USB boards time to enumerate before printing
  esp_reset_reason_t r = esp_reset_reason();
  Serial.println();
  Serial.println("=== FlashReady reset-reason report ===");
  Serial.printf("Reset reason : %d (%s)\n", (int)r, reasonText(r));
  Serial.printf("Chip         : %s rev %d, %d core(s), %lu MHz\n", ESP.getChipModel(), ESP.getChipRevision(),
                ESP.getChipCores(), (unsigned long)ESP.getCpuFreqMHz());

  uint32_t physical = 0;
  esp_flash_get_physical_size(esp_flash_default_chip, &physical);
  uint32_t header = ESP.getFlashChipSize();
  Serial.printf("Flash (chip) : %lu KB\n", (unsigned long)(physical / 1024));
  Serial.printf("Flash (IDE)  : %lu KB at %lu MHz\n", (unsigned long)(header / 1024),
                (unsigned long)(ESP.getFlashChipSpeed() / 1000000));
  if (physical && header > physical) {
    Serial.println("WARNING      : Tools -> Flash Size is larger than the real chip. Lower it.");
  }
  Serial.printf("PSRAM        : %lu KB%s\n", (unsigned long)(ESP.getPsramSize() / 1024),
                ESP.getPsramSize() ? "" : " (none found or PSRAM disabled in Tools)");
  Serial.printf("Sketch       : %lu KB used, %lu KB free in app partition\n",
                (unsigned long)(ESP.getSketchSize() / 1024), (unsigned long)(ESP.getFreeSketchSpace() / 1024));
  Serial.printf("Free heap    : %lu bytes\n", (unsigned long)ESP.getFreeHeap());
  Serial.println("======================================");
}

void loop() {
  static uint32_t last = 0;
  if (millis() - last >= 5000) {
    last = millis();
    Serial.printf("alive, uptime %lu s, free heap %lu\n", (unsigned long)(millis() / 1000),
                  (unsigned long)ESP.getFreeHeap());
  }
}

Why it happens

TG1 and TG0 — what's the difference?

Timer Group 1 runs the interrupt watchdog; Timer Group 0 runs the task watchdog and the bootloader's watchdog. rst:0x8 points at interrupts being blocked; rst:0x7 (TG0WDT_SYS_RESET) during boot points at flash or bootloader trouble instead.

Why no Guru Meditation message?

Usually the interrupt watchdog prints “Interrupt wdt timeout on CPU1” first. If the CPU is so stuck it can't even print, the hardware watchdog resets it directly and you only see rst:0x8.

Board-specific notes

ESP32 with WS2812B: Use a library that drives LEDs through the RMT peripheral (FastLED and Adafruit NeoPixel both do on current cores) instead of bit-banging with interrupts off.

↑↓ to move · Enter to open