Bring-Up Sequence
Work through these 10 steps in order. Each step has an expected serial output — do not proceed until you see it.
Step 1 — ESP32 Blink
What: Flash the blink example. Confirm the board is alive and the serial port works.
Expected output:
I (xxx) main: Turning on LED
I (xxx) main: Turning off LED
Step 2 — I2C Scanner
What: Wire the DS3231. Run the I2C scanner from the I2C concepts doc.
Expected output:
Scanning I2C bus...
Found device at 0x68
Stop if: Nothing appears at 0x68. Check VCC (3.3 V), SDA→GPIO21, SCL→GPIO18, and that pull-ups are present on the breakout.
Step 3 — RTC Read
What: Call rtc_init() and rtc_get_time(). Print the result.
Expected output:
I (xxx) rtc: Time from RTC: 00:00:00 (or last set time if battery was inserted)
I (xxx) rtc: Temperature: 23.0°C
If the time reads 00:00:00 on Jan 1 2000, the RTC lost power (no battery or first use). This is fine — it will be set by NTP on first WiFi connect.
Step 4 — Button Test
What: Wire all 6 buttons. Run the polling test from the buttons wiring doc.
Expected output (press each button):
BTN_UP pressed
BTN_DOWN pressed
BTN_SELECT pressed
BTN_BACK pressed
BTN_SNOOZE pressed
BTN_DISMISS pressed
Step 5 — LittleFS Mount
What: Flash the LittleFS partition image with esptool.py write_flash (see the
Audio Storage page).
Call lfs_init() and lfs_list_alarms().
Expected output:
I (xxx) lfs: LittleFS mounted at /lfs
I (xxx) lfs: alarm.wav found, size 1234567 bytes
Stop if: "Failed to mount LittleFS partition 'lfs'". Confirm the lfs partition label
in partitions.csv matches the driver config, and that the LittleFS image was flashed to
the correct offset.
Step 6 — Display Init
What: Wire the display adapter. Call display_init().
Expected output:
I (xxx) display: BUSY LOW — display ready
I (xxx) display: Full refresh complete
The screen should show a blank white frame.
Stop if: "BUSY stuck HIGH after 5s". Check RST→GPIO4, BUSY→GPIO3, VCC on adapter.
Step 7 — Audio Playback
What: Wire the MAX98357A. Play alarm.wav at startup.
Expected output:
I (xxx) audio: Playing /lfs/alarms/alarm.wav
I (xxx) audio: Sample rate: 44100 Hz, channels: 1
Expected behaviour: Audio plays from the speaker.
Stop if: Silent. Check DIN→GPIO17, VIN→5V, SD pin state.
Step 8 — NTP Sync + RTC Write-Back
What: Connect to WiFi, sync NTP, write back to RTC.
Expected output:
I (xxx) wifi: Connected to MyNetwork
I (xxx) ntp: Synced: Wed Apr 16 14:32:00 2025
I (xxx) rtc: RTC updated from NTP
After this, call rtc_get_time() — it should return today's actual date and time.
Step 9 — Full Firmware
What: Flash the complete application (all tasks, state machine, alarm manager).
Expected output:
I (xxx) app: NVS init OK
I (xxx) lfs: LittleFS mounted at /lfs
I (xxx) rtc: System clock set: Wed Apr 16 14:32:00 2025
I (xxx) display: Clock screen rendered
I (xxx) wifi: Connecting...
I (xxx) wifi: Connected
I (xxx) ntp: Synced
Expected behaviour:
- Clock displays current time
- Buttons navigate menus
- Setting an alarm 2 minutes out causes it to fire
- Audio plays on alarm fire
Proceed to Step 10 to verify the discrete backup circuit, then to the smoke test to validate all behaviour.
Step 10 — Discrete Backup Circuit
What: Wire the ATtiny85 backup circuit (see the battery backup wiring guide). Program the DS3231 Alarm 1 to fire in 2 minutes, then disconnect USB.
Expected behaviour:
- ATtiny wakes on DS3231 SQW interrupt
- Buzzer sounds within 1 second of the alarm time
- Pressing SNOOZE stops the buzzer (refires after ~9 minutes)
- Pressing DISMISS stops the buzzer permanently
Stop if: Buzzer does not sound. Check AA supply voltage at ATtiny VCC (should be 2.7–3.2 V), SQW idle level (should be HIGH, ~3.3 V), and that the DS3231 alarm registers were written before USB was disconnected.