Skip to main content

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.

This is the module build

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.

Safi wiring diagram
Work in stages

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

  1. Battery to the TP4056 B+/B- pads through the JST connector.
  2. TP4056 OUT+ through the slide switch to the ESP32 dev board 5V pin; OUT- to GND.
  3. 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.

The PN532 is 3.3 V only

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 toNotes
VCC3.3 VNot 5 V. The backlight runs off this pin, so 5 V is what leaves it too dim or dark
GNDGND
GNDGNDSecond ground; connect it too, or leave it, but at least one GND is required
NCleave empty
NCleave empty
NCleave empty
CLKGPIO 12SPI clock (same wire as SD_SCK)
SDAGPIO 13SPI data in (same wire as SD_MOSI)
RSGPIO 16Data/command select. This is the pin the firmware and pinout call DC
RSTGPIO 21Panel reset
CSGPIO 2Display chip select

The four SD_ pins on the other edge drive the on-board card slot:

Module pin (silk)Connect toNotes
SD_MISOGPIO 11The only line the card sends back on
SD_SCKGPIO 12Same wire as the display CLK
SD_MOSIGPIO 13Same wire as the display SDA
SD_CSGPIO 5With a 10 kΩ pull-up to 3.3 V
This board has no backlight pin

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 toNotes
GNDGND
VDD3.3 V
SCLGPIO 12SPI clock (the ST7735S CLK)
SDAGPIO 13SPI data in (the ST7735S SDA)
RESGPIO 21Panel reset (the ST7735S RST)
DCGPIO 16Data/command select (the ST7735S RS)
CSGPIO 2Display chip select
BLGPIO 18Backlight, 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).

Lit but all white? Check RS/DC first

A 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 toNotes
LRCGPIO 39Word select (left/right clock). Required; a missing LRC is silent
BCLKGPIO 38Bit clock
DINGPIO 40Audio data
GAINleave emptyThe default gain (9 dB) is fine; see below to change it
SDGPIO 41Enable. The firmware holds this high to switch the amp on
GNDGND
VIN5 VRuns 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 outputConnect 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.

Speaker connected but no sound?

The device confirms it is playing (the dashboard shows a track and the position moves) but nothing comes out. Check these, most common first:

  1. Both speaker wires go to the amp's + and - outputs. This output is bridge-tied, so neither speaker wire goes to GND. A speaker run from + to ground is the usual cause of silence.
  2. LRC (GPIO 39), BCLK (GPIO 38) and DIN (GPIO 40) are all connected. Missing the word-select line LRC is a frequent omission and produces no sound.
  3. SD is high. The firmware drives GPIO 41 high to enable the amp, so SD must be wired to it. On a board where SD is left floating the amp stays shut down; tying SD straight to VIN also enables it (at the fixed default gain).
  4. VIN has power (5 V, or at least 3.3 V) and GND is 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 pinGPIONotes
PN532 SDA + DS3231 SDA8One shared wire, 4.7 to 10 kΩ pull-up to 3.3 V
PN532 SCL + DS3231 SCL9One shared wire, same pull-up
PN532 IRQ, RSTOnot connectedThe firmware polls
DS3231 INT/SQW10Open 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

SignalGPIONotes
Encoder CLK (A)1Internal pull-up used
Encoder DT (B)7Internal pull-up used
Encoder SW15Push switch, also the deep-sleep wake pin
Touch pad4Native 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

  1. Continuity test every ground.
  2. Verify 3.3 V is not shorted to 5 V anywhere.
  3. Confirm the PN532 is on 3.3 V.
  4. 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.