diff --git a/README.org b/README.org
index 540ddfab992559b05f05f55b92dd3f57a592bf1c..7d2c8ae7b8e0c3923b25e27855fe5f88b1a991d5 100644
--- a/README.org
+++ b/README.org
@@ -44,6 +44,15 @@ the TB6612FNG motor driver.
 
 - [[https://wiki.dfrobot.com/Micro_DC_Motor_with_Encoder-SJ01_SKU__FIT0450][Wiki]]
 
+* Led pattern
+
+The led pattern is in Morse code.
+
+| Code | Meaning                      | Pattern |
+|------+------------------------------+---------|
+| I    | Regular reset                | ..      |
+| WW   | Reset due to window watchdog | .-- .-- |
+
 * Command set
 
 Multibyte values are exchanged in little endian format.
diff --git a/controller/src/main.rs b/controller/src/main.rs
index 7fb02aef910be94144d12c735d911785731254fc..b3de259234d74c625735585a32d2bddddebffd26 100644
--- a/controller/src/main.rs
+++ b/controller/src/main.rs
@@ -7,7 +7,8 @@ use embassy_executor::Spawner;
 use embassy_stm32::{
     gpio::{Level, Output, Speed},
     interrupt::Priority,
-    peripherals::PB15,
+    pac,
+    peripherals::{PB15, RCC},
     rcc::{APBPrescaler, Hse, HseMode, Pll, PllMul, PllPreDiv, PllSource, Sysclk},
     time::{khz, mhz},
     Config,
@@ -19,6 +20,45 @@ mod encoders;
 mod logic;
 mod tb6612fng;
 
+#[derive(Clone, Copy, Debug)]
+enum ResetCause {
+    ExternalReset,
+    WindowWatchdogReset,
+    IndependentWatchdogReset,
+    SoftwareReset,
+    LowPowerManagementReset,
+    PowerReset,
+    Unknown,
+}
+
+fn reset_cause(_rcc: &mut RCC) -> ResetCause {
+    // Read reset cause, and reset it
+    let csr = pac::RCC.csr().read();
+    pac::RCC.csr().modify(|w| w.set_rmvf(true));
+    if csr.lpwrrstf() {
+        ResetCause::LowPowerManagementReset
+    } else if csr.wwdgrstf() {
+        ResetCause::WindowWatchdogReset
+    } else if csr.iwdgrstf() {
+        ResetCause::IndependentWatchdogReset
+    } else if csr.sftrstf() {
+        ResetCause::SoftwareReset
+    } else if csr.porrstf() {
+        ResetCause::PowerReset
+    } else if csr.pinrstf() {
+        ResetCause::ExternalReset
+    } else {
+        ResetCause::Unknown
+    }
+}
+
+fn blink_pattern(reset_cause: ResetCause) -> &'static str {
+    match reset_cause {
+        ResetCause::WindowWatchdogReset => ".-- .--", // WW
+        _ => "..",                                    // I
+    }
+}
+
 #[embassy_executor::main]
 async fn main(spawner: Spawner) {
     let mut config = Config::default();
@@ -36,16 +76,20 @@ async fn main(spawner: Spawner) {
     });
     // APB1 speed (PCLK1) is max 36MHz (SysClk/2), use that
     config.rcc.apb1_pre = APBPrescaler::DIV2;
-    let p = embassy_stm32::init(config);
+    let mut p = embassy_stm32::init(config);
+    let reset_cause = reset_cause(&mut p.RCC);
 
     defmt::info!(
-        "Firmware {}.{}.{} starting",
+        "Firmware {}.{}.{} starting – reset cause: {}",
         logic::PKG_VERSION[0],
         logic::PKG_VERSION[1],
-        logic::PKG_VERSION[2]
+        logic::PKG_VERSION[2],
+        defmt::Debug2Format(&reset_cause),
     );
 
-    spawner.spawn(blink(p.PB15)).unwrap();
+    spawner
+        .spawn(blink(p.PB15, blink_pattern(reset_cause)))
+        .unwrap();
 
     let motors = Tb6612fng::new(
         p.PA4,
@@ -69,12 +113,33 @@ async fn main(spawner: Spawner) {
 }
 
 #[embassy_executor::task]
-async fn blink(pin: PB15) {
+async fn blink(pin: PB15, pattern: &'static str) {
     let mut led = Output::new(pin, Level::High, Speed::Low);
     loop {
-        for _ in 0..6 {
-            led.toggle();
-            Timer::after_millis(200).await;
+        for b in pattern.chars() {
+            match b {
+                ' ' => {
+                    Timer::after_millis(200).await;
+                }
+                '.' => {
+                    led.toggle();
+                    Timer::after_millis(150).await;
+                    led.toggle();
+                    Timer::after_millis(200).await;
+                }
+                '-' => {
+                    led.toggle();
+                    Timer::after_millis(300).await;
+                    led.toggle();
+                    Timer::after_millis(200).await;
+                }
+                _ => {
+                    defmt::error!("unknown pattern letter {}", b);
+                    led.toggle();
+                    Timer::after_secs(5).await;
+                    led.toggle();
+                }
+            }
         }
         Timer::after_secs(1).await;
     }