Wiring and Assembly
This page wires every module to the ESP32-S3, bus by bus. The full pin table lives in the pinout reference; this page adds the order, the pull-ups, and the mistakes to avoid.
Everything below wires discrete breakout boards to an ESP32-S3 dev board. The same circuit also exists as an integrated single board (one PCB with USB-C, on-board charging, and amplifier); if you are building that instead, the main board chapter is the reference and the pin map is identical.
Wire and test one subsystem at a time in this order: power, display, SD, audio, then the rest. The firmware boots and reports missing peripherals on the splash screen instead of failing, so a partial build is always testable.
Power first
- Battery to the TP4056
B+/B-pads through the JST connector. - TP4056
OUT+through the slide switch to the ESP32 dev board5Vpin;OUT-toGND. - Every module shares one common ground. Star-ground at the dev board if you can.
The dev board's own regulator supplies 3.3 V to everything else. The MAX98357A can take 5 V directly for a little more headroom; everything else runs at 3.3 V.
Powering the PN532 from 5 V will damage it. Every signal on this build is 3.3 V logic.
The display and SD card (shared SPI bus)
The display and the SD card share one SPI peripheral (SPI2). They sit on the same clock and data wires and are told apart by their own chip-select lines, which saves three pins. Wire your module straight from the tables below, pin for pin, in the order the pins are printed on the board. The label in the first column is the exact silk text on the module.
Safi supports two display modules (display options). Wire the one you have.
1.8 inch ST7735S (red board, with its own microSD slot)
This board has eleven pins down one edge and four SD_ pins on the opposite edge. Every pin, top to bottom on the main edge:
| Module pin (silk) | Connect to | Notes |
|---|---|---|
VCC | 3.3 V | Not 5 V. The backlight runs off this pin, so 5 V is what leaves it too dim or dark |
GND | GND | |
GND | GND | Second ground; connect it too, or leave it, but at least one GND is required |
NC | leave empty | |
NC | leave empty | |
NC | leave empty | |
CLK | GPIO 12 | SPI clock (same wire as SD_SCK) |
SDA | GPIO 13 | SPI data in (same wire as SD_MOSI) |
RS | GPIO 16 | Data/command select. This is the pin the firmware and pinout call DC |
RST | GPIO 21 | Panel reset |
CS | GPIO 2 | Display chip select |
The four SD_ pins on the other edge drive the on-board card slot:
| Module pin (silk) | Connect to | Notes |
|---|---|---|
SD_MISO | GPIO 11 | The only line the card sends back on |
SD_SCK | GPIO 12 | Same wire as the display CLK |
SD_MOSI | GPIO 13 | Same wire as the display SDA |
SD_CS | GPIO 5 | With a 10 kΩ pull-up to 3.3 V |
The backlight is wired to VCC on the board, so it is on whenever the module has power. There is no BL or LED pin to connect, and GPIO 18 stays unused. The dashboard brightness slider therefore has no effect on this module.
1.47 inch ST7789 (blue GMT147SPI board, no SD slot)
Eight pins along the bottom edge, left to right:
| Module pin (silk) | Connect to | Notes |
|---|---|---|
GND | GND | |
VDD | 3.3 V | |
SCL | GPIO 12 | SPI clock (the ST7735S CLK) |
SDA | GPIO 13 | SPI data in (the ST7735S SDA) |
RES | GPIO 21 | Panel reset (the ST7735S RST) |
DC | GPIO 16 | Data/command select (the ST7735S RS) |
CS | GPIO 2 | Display chip select |
BL | GPIO 18 | Backlight, PWM dimmed by the firmware; tie to 3.3 V instead for always-on |
This module has no card slot, so for the SD library wire a separate microSD breakout to GPIO 11 (MISO), 12 (SCK), 13 (MOSI) and 5 (CS, with the 10 kΩ pull-up).
RS/DC firstA backlight that comes on but shows a blank white screen means the panel has power but never received its startup commands. The usual cause is the data/command line left unconnected or on the wrong pin. On the red board that pin is silkscreened RS, not DC, and it goes to GPIO 16. After that, confirm RST (GPIO 21) and CS (GPIO 2), then CLK and SDA. The SPI bus is write-only, so the firmware cannot detect a missing display and will report no error.
Why the SD CS pull-up matters: during boot, before the firmware configures GPIO5, the pin floats. A floating CS can let the card wake up mid-bus-traffic and corrupt the first transactions. The pull-up keeps the card deselected until the firmware explicitly talks to it.
Why GPIO11-13 and not the classic VSPI pins: on the ESP32-S3, GPIO19 and GPIO20 carry native USB, which Safi uses for flashing and the serial console. The developer notes on bus sharing explain how the firmware arbitrates the two devices safely.
Audio (I2S)
The MAX98357A takes a three-wire I2S signal, an enable line, power, and drives the speaker directly. Wire it pin for pin:
| MAX98357A pin (silk) | Connect to | Notes |
|---|---|---|
LRC | GPIO 39 | Word select (left/right clock). Required; a missing LRC is silent |
BCLK | GPIO 38 | Bit clock |
DIN | GPIO 40 | Audio data |
GAIN | leave empty | The default gain (9 dB) is fine; see below to change it |
SD | GPIO 41 | Enable. The firmware holds this high to switch the amp on |
GND | GND | |
VIN | 5 V | Runs from 3.3 V too, but 5 V is louder into an 8 Ω speaker |
The two output pads, silkscreened + and - (or a speaker symbol), drive the speaker:
| Amp output | Connect to |
|---|---|
Speaker + | One speaker terminal |
Speaker - | The other speaker terminal |
The MAX98357A is a mono amplifier; the firmware downmixes stereo sources. Keep the speaker wires short and away from the antenna end of the ESP32 module. An 8 Ω 3 W speaker such as the YD66-2 is well within its range.
The device confirms it is playing (the dashboard shows a track and the position moves) but nothing comes out. Check these, most common first:
- Both speaker wires go to the amp's
+and-outputs. This output is bridge-tied, so neither speaker wire goes toGND. A speaker run from+to ground is the usual cause of silence. LRC(GPIO 39),BCLK(GPIO 38) andDIN(GPIO 40) are all connected. Missing the word-select lineLRCis a frequent omission and produces no sound.SDis high. The firmware drives GPIO 41 high to enable the amp, soSDmust be wired to it. On a board whereSDis left floating the amp stays shut down; tyingSDstraight toVINalso enables it (at the fixed default gain).VINhas power (5 V, or at least 3.3 V) andGNDis shared with the ESP32.
To set a non-default gain, connect GAIN per the MAX98357A datasheet (to GND for 12 dB, to VIN for 6 dB, through 100 kΩ for 3 or 15 dB). Leaving it open gives 9 dB.
NFC and RTC (shared I2C)
The PN532 and the DS3231 sit on the same two-wire bus:
| Module pin | GPIO | Notes |
|---|---|---|
| PN532 SDA + DS3231 SDA | 8 | One shared wire, 4.7 to 10 kΩ pull-up to 3.3 V |
| PN532 SCL + DS3231 SCL | 9 | One shared wire, same pull-up |
| PN532 IRQ, RSTO | not connected | The firmware polls |
| DS3231 INT/SQW | 10 | Open drain, active low; wakes the device for scheduled playback |
Set the PN532's interface switches to I2C: switch 1 ON, switch 2 OFF on the common red boards. Both modules usually carry their own pull-ups; stacked pull-ups are fine at this bus speed. Different I2C addresses (0x24 and 0x68) keep them out of each other's way. Before fitting the RTC's coin cell, read the charging circuit warning: common modules cook non-rechargeable CR2032 cells unless one resistor is removed.
Encoder and touch
| Signal | GPIO | Notes |
|---|---|---|
| Encoder CLK (A) | 1 | Internal pull-up used |
| Encoder DT (B) | 7 | Internal pull-up used |
| Encoder SW | 15 | Push switch, also the deep-sleep wake pin |
| Touch pad | 4 | Native touch channel TOUCH4; any conductive pad works |
The encoder switch doubles as a wake source: when the device deep-sleeps (battery emergency or the dashboard's Sleep button), pressing the knob wakes it. With a DS3231 fitted, the RTC alarm on GPIO 10 is the second wake source, so a sleeping device still wakes for its next schedule. Note for bench setups: if the encoder is not wired at all, GPIO 15 floats and can wake the chip from sleep immediately; that is the loose wire, not the firmware.
Battery sense and charge status
Scale the battery voltage into ADC range with the divider:
Battery+ ──[100 kΩ]──┬──[27 kΩ]── GND
│
GPIO 6 (ADC1 channel 5)
A full battery at 4.2 V presents 0.89 V to the ADC. The firmware multiplies readings back up by the ratio (127/27), averages 16 samples, and rejects implausible values, so a bench setup without a battery does not trigger false low-battery shutdowns.
The TP4056 CHRG pin (open drain, active low) goes to GPIO 17 with a 10 kΩ pull-up to 3.3 V. Low means charging.
Final checks before power-on
- Continuity test every ground.
- Verify 3.3 V is not shorted to 5 V anywhere.
- Confirm the PN532 is on 3.3 V.
- With the battery disconnected, power from USB-C first and watch the serial console: the boot log lists each peripheral as it initializes, and the splash screen shows a boot report with anything that failed.
If a peripheral shows as failed, see troubleshooting for the failure-by-failure checklist.