Skip to main content

Troubleshooting

Symptoms, likely causes, and fixes for common problems.


Display

Display shows nothing / stays white

CheckWhat to do
BUSY pin polarityThe SSD1683 holds BUSY HIGH while busy. Confirm you are waiting for LOW, not HIGH.
RST connectedMeasure GPIO4 with a multimeter. It should go LOW briefly during init. If it stays HIGH, the wire is missing.
VCC on adapterMeasure VCC on the adapter board pin header. Should read 3.3 V.
CS floatingIf 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

CheckWhat to do
VIN is 5 VMeasure the MAX98357A VIN pin. Must be ~5 V, not 3.3 V.
DIN connectedConfirm DIN → GPIO17 (not DOUT — there is no DOUT).
SD pin stateConfirm SD pin is at GND or floating. If pulled HIGH without a resistor, the amp shuts down.
I2S sample rateConfirm 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')

CheckWhat to do
Partition labelConfirm partition_label = "lfs" in the driver config matches the label in partitions.csv.
Image not flashedFlash the LittleFS image with esptool.py write_flash <offset> lfs.bin.
Wrong flash offsetRun idf.py partition-table to confirm the exact offset; mismatch between image and partition causes mount failure.
format_if_mount_failedDo 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

CheckWhat to do
File in LittleFS imageConfirm mklittlefs was run against a directory containing alarms/alarm.wav.
CapitalisationLittleFS is case-sensitive. alarm.wavAlarm.wav.
Partition mountedConfirm 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

CheckWhat to do
GND connectedBoth button terminals must be wired: one to GPIO, one to GND rail.
GPIO configurationConfirm GPIO_PULLUP_ENABLE is set in firmware.
GPIO0 accidentally usedGPIO0 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 the IP_EVENT_STA_GOT_IP event
  • 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

CheckWhat to do
ATtiny VCCMeasure voltage at ATtiny VCC (pin 8) — should read 2.7–3.2 V from the AA batteries.
SQW idle levelSQW 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 registersConfirm the alarm time was written into DS3231 Alarm 1 before USB was disconnected. Log the register values at alarm-set time.
Active vs passive buzzerConfirm 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 orientationThe 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)

CheckWhat to do
SQW pull-up missingIf SQW floats LOW, ATtiny INT0 fires immediately. Add 10 kΩ pull-up from SQW to 3.3 V.
ATtiny firmwareThe 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)

CheckWhat to do
External pull-ups missingWithout 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 wiringConfirm 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

CheckWhat to do
Divider inputR1 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 presentIf either 100 kΩ resistor is missing, the divider output will not be correct.
GPIO configurationConfirm GPIO41 is configured as input with no pull-up and no pull-down.