Firmware for an RP2040-based lickometer with:
- MPR121 capacitive lick sensing (12 electrodes)
- SD card session logging (CSV)
- DS3231 RTC wall-time support
- SH1107 OLED status UI
- TTL pulse output on pad 0 touches
- Recovery behavior for SD and sensor faults
This project is designed for low-latency touch capture while remaining robust to removable media and peripheral faults.
- Source of truth for procurement: DigiKey_BOM.xlsx
- Readable mirror: HARDWARE_BOM.md
The sketch runs a small state machine:
IDLE: waiting to start a recording sessionRECORDING: logging touch edges and system eventsDEGRADED: MPR121 unavailable; auto-retry runs until recovery
Recording starts only when A is pressed. During recording:
- Touch edges (
START/STOP) are logged per electrode - Counts are shown on the OLED
- Buffered events flush to SD after inactivity
Pin definitions from the sketch:
SD_CS_PIN:23SD_DETECT_PIN:16(HIGHmeans inserted)BUTTON_A:9(start)BUTTON_B:6(toggle display during recording)BUTTON_C:5(stop)TTL_OUT_PIN:26(A0on RP2040 Adalogger), HIGH while pad 0 is touchedMPR121_I2C_ADDR:0x5A
A: start recording sessionB: toggle OLED on/off during recordingC: stop recording session
Each session writes one CSV file:
- Name format with RTC:
/log_YYYYMMDD_HHMMSS_mmm.csv - Collision fallback: adds
_NNNsuffix if needed - Fallback without RTC:
/log_ms<millis64>.csv(with_NNNcollision suffix)
CSV format:
- Row 1:
SchemaVersion,1 - Row 2:
Time(ms),WallTime,Electrode,Event - Data rows:
- Touch edges:
E0..E11, eventsSTART/STOP - System events:
E-, events likeSESSION_START,SESSION_STOP,MPR121_SELF_HEAL
- Touch edges:
Timing fields:
Time(ms): 64-bit monotonic milliseconds (time_us_64()/1000)WallTime: ISO-like RTC string for system events; touch edges leave wall-time empty for speed
SessionLogger uses a RAM buffer with a hard cap:
MAX_BUFFER_BYTES = 48 * 1024- If full, oldest buffered lines are dropped and a marker row is emitted after recovery:
BUFFER_OVERFLOW_DROPPED=<n>
When SD is removed during recording:
- Recording continues in RAM (
REC(NO SD)shown) - File handle is closed safely
- Writes are retried after reinsertion
When SD is reinserted:
- Buffered events are flushed automatically
- Deferred close/finalization is attempted when not actively recording
The firmware considers MPR121 faulted when:
ECR == 0x00(stopped), or- OOR registers
0x02/0x03are non-zero
During RECORDING, health checks run periodically. On fault:
- Reinitialize MPR121 (including autoconfig)
- If recovery succeeds, continue recording
- If recovery fails, stop session and enter
DEGRADED
Peripheral auto-retry attempts are also made from DEGRADED.
File timestamps are stamped from RTC via SessionLogger during create/flush.
Timestamp metadata is best-effort; data durability takes priority.
Idle screen shows:
- Current RTC date/time (or
RTC_MISSING) - Peripheral status (
SD,RTC,MPR121) - Button hints
Recording screen shows:
RECorREC(NO SD)with elapsed time- 3-column per-pad counts (
0..11)
Degraded screen shows:
- Peripheral status
- Auto-retry message
DEBUG_MODE is set in the sketch:
- If
DEBUG_MODE == true, boot blocks until serial is connected. - OLED shows a waiting message in this mode.
- This intentionally prevents running recordings in debug mode without a host.
For standalone operation, set DEBUG_MODE false and reflash.
Main libraries used:
SdFatAdafruit_MPR121RTClibAdafruit_SH110X- RP2040 SDK time API (
pico/time.h)
Firmware/RP2040_Lickometer.ino: main state machine, UI, hardware integrationFirmware/SessionLogger.h/.cpp: buffered CSV logger + SD recovery + timestampsFirmware/ButtonDebouncer.h: non-blocking button edge debounceFirmware/tools/validate_log_csv.py: CSV validatorDigiKey_BOM.xlsx: procurement/source-of-truth BOMHARDWARE_BOM.md: hardware bill of materials
Hardware smoke tests:
- Boot with USB serial connected in debug mode; verify startup reaches
IDLE. - Boot with SD inserted; verify card is detected on idle status screen.
- Boot without SD; press
A; verify no-start behavior and no crash. - Start (
A), touch multiple electrodes, stop (C); verify CSV created. - Toggle display with
Bduring recording; verify logging continues. - Remove SD during recording; verify recording continues with
REC(NO SD). - Reinsert SD; verify buffered events flush and session finalizes cleanly.
Peripheral recovery tests:
- Boot with MPR121 disconnected; verify
DEGRADED. - Reconnect MPR121; verify auto-recovery to
IDLE. - Boot with RTC disconnected; verify logging still works (
RTC_UNAVAILABLEwall-time fallback). - Reconnect RTC; verify retry recovery and proper wall-time on system events.
CSV checks:
python3 Firmware/tools/validate_log_csv.py /path/to/log_directoryExpected:
SchemaVersion,1first row- Header row exactly
Time(ms),WallTime,Electrode,Event - Monotonic non-negative
Time(ms) SESSION_STARTincludesWallTime- At most one
SESSION_STOPper file