Troubleshooting
Symptoms, likely causes, and fixes for common problems.
Display
Display shows nothing / stays white
| Check | What to do |
|---|---|
| BUSY pin polarity | The SSD1683 holds BUSY HIGH while busy. Confirm you are waiting for LOW, not HIGH. |
| RST connected | Measure GPIO4 with a multimeter. It should go LOW briefly during init. If it stays HIGH, the wire is missing. |
| VCC on adapter | Measure VCC on the adapter board pin header. Should read 3.3 V. |
| CS floating | If CS is not connected, the display controller may not initialise. |
Display shows garbled pixels / ghosting
This is expected after many partial refreshes. Force a full refresh by setting
full_refresh_counter = 10 and calling render_screen().
Display flickers during audio playback
SPI and I2S share DMA resources. Pin the audio task to core 1 and the display task to core 0. See I2S concepts.
Audio
Speaker is silent
| Check | What to do |
|---|---|
| VIN is 5 V | Measure the MAX98357A VIN pin. Must be ~5 V, not 3.3 V. |
| DIN connected | Confirm DIN → GPIO17 (not DOUT — there is no DOUT). |
| SD pin state | Confirm SD pin is at GND or floating. If pulled HIGH without a resistor, the amp shuts down. |
| I2S sample rate | Confirm the WAV file's sample rate matches the I2S config. |
Hissing noise but no audio
The amp is powered but receiving no useful data. Check the I2S pin assignments (BCLK, LRCLK, DIN) in firmware against the wiring.
ESP32 resets / brownout during playback
The audio amp draws significant current at high volume. Use a USB-C charger rated for at least 2 A (10 W). A long or thin USB cable also causes voltage drop — try a shorter cable.
RTC / Timekeeping
RTC shows 00:00 on every boot
The RTC coin cell is not installed, is flat, or is inserted backwards. Check battery orientation (+ side facing up).
Time drifts by minutes per day
The DS3231 has very low drift (<2 ppm) — if you see large drift, the RTC is not being
updated from NTP. Check ntp_sync_callback calls rtc_set_time().
rtc_get_time() returns ESP_ERR_TIMEOUT
I2C communication is failing. Confirm pull-ups are present (measure SDA/SCL — they should idle at 3.3 V, not float). Run the I2C scanner to confirm 0x68 is visible.
LittleFS
Mount fails (Failed to mount LittleFS partition 'lfs')
| Check | What to do |
|---|---|
| Partition label | Confirm partition_label = "lfs" in the driver config matches the label in partitions.csv. |
| Image not flashed | Flash the LittleFS image with esptool.py write_flash <offset> lfs.bin. |
| Wrong flash offset | Run idf.py partition-table to confirm the exact offset; mismatch between image and partition causes mount failure. |
format_if_mount_failed | Do not use format_if_mount_failed = true in production — it silently erases audio data on any mount failure. |
fopen returns NULL for /lfs/alarms/alarm.wav
| Check | What to do |
|---|---|
| File in LittleFS image | Confirm mklittlefs was run against a directory containing alarms/alarm.wav. |
| Capitalisation | LittleFS is case-sensitive. alarm.wav ≠ Alarm.wav. |
| Partition mounted | Confirm lfs_init() succeeded before calling fopen. |
Partial WAV file after OTA update
Power was lost during the write before rename() completed. The original alarm.wav is
intact; the temp file (alarm_tmp.wav) may be partial. Delete the temp file and retry the upload.
Buttons
Button presses not registered
| Check | What to do |
|---|---|
| GND connected | Both button terminals must be wired: one to GPIO, one to GND rail. |
| GPIO configuration | Confirm GPIO_PULLUP_ENABLE is set in firmware. |
| GPIO0 accidentally used | GPIO0 held LOW at boot puts the chip in download mode. Switch to a different GPIO. |
Multiple events per press
Debouncing is not working. Increase the debounce delay from 50 ms to 100 ms in the
debounce_task. Or check that GPIO_INTR_ANYEDGE is configured (needed to detect
both press and release for long-press).
WiFi
Cannot connect to access point
- Check SSID and password in NVS (log them at startup, then delete the log before shipping)
- Confirm the 2.4 GHz network is reachable — the ESP32-S3 does not support 5 GHz
- If using WPA3 only, the ESP32-S3 supports it but some older firmwares have bugs — use WPA2
NTP sync never fires
- WiFi must be connected first — NTP only works when the IP stack is up
- Confirm
esp_sntp_init()is called after theIP_EVENT_STA_GOT_IPevent - Some firewalls block NTP (UDP port 123) — try a different NTP server:
time.cloudflare.com
Discrete Backup Circuit
Buzzer does not sound when alarm fires with USB disconnected
| Check | What to do |
|---|---|
| ATtiny VCC | Measure voltage at ATtiny VCC (pin 8) — should read 2.7–3.2 V from the AA batteries. |
| SQW idle level | SQW must idle HIGH (~3.3 V). If it reads LOW or floats, add a 10 kΩ pull-up from SQW to 3.3 V. |
| DS3231 Alarm 1 registers | Confirm the alarm time was written into DS3231 Alarm 1 before USB was disconnected. Log the register values at alarm-set time. |
| Active vs passive buzzer | Confirm the buzzer is active (built-in oscillator). Apply AA V+ directly to buzzer + terminal — it should beep without any signal. If it does not, the buzzer is passive and will not work with this circuit. |
| PN2222 orientation | The flat face of the TO-92 package determines pin order (E-B-C from left to right when flat face is toward you). Verify with a datasheet. |
Buzzer sounds immediately on ATtiny power-on (without an alarm)
| Check | What to do |
|---|---|
| SQW pull-up missing | If SQW floats LOW, ATtiny INT0 fires immediately. Add 10 kΩ pull-up from SQW to 3.3 V. |
| ATtiny firmware | The ATtiny must wait for INT0 low-level interrupt, not fire on VCC application. Reflash the ATtiny firmware. |
SNOOZE or DISMISS not working from ATtiny (backup circuit only)
| Check | What to do |
|---|---|
| External pull-ups missing | Without 10 kΩ pull-ups on SNOOZE (GPIO38) and DISMISS (GPIO39) lines, ATtiny inputs float when ESP32 is off. Both inputs will read random noise. Add the pull-ups. |
| Button wiring | Confirm button goes from the shared line to GND (not to 3.3 V). |
SNOOZE or DISMISS triggering spuriously (random dismissals)
Pull-up resistors are missing or undersized — ATtiny reads floating inputs as button presses. Add 10 kΩ pull-ups from each line to 3.3 V.
USB-present pin (GPIO41) always reads LOW or always reads HIGH
| Check | What to do |
|---|---|
| Divider input | R1 must connect from the USB-C 5 V VBUS rail — not from the ESP32's 5V pin (which is derived from VBUS via the dev board, but may behave differently). |
| Both resistors present | If either 100 kΩ resistor is missing, the divider output will not be correct. |
| GPIO configuration | Confirm GPIO41 is configured as input with no pull-up and no pull-down. |