Created: 2026-05-22 14:07 EEST Last updated: 2026-05-22 18:28 EEST
This sketch depends on changes made outside the project folder. When porting to another machine, replicate the changes below after installing the required libraries via the Arduino Library Manager.
- Arduino IDE / arduino-cli
- ESP32 Arduino core 3.3.10 (board package
esp32:esp32) - Libraries (current versions known to work):
- TFT_eSPI 2.5.43
- PU2CLR SI4735 2.1.8
- PU2CLR RDA5807 1.1.9
- PU2CLR SI470X 1.0.5
- PU2CLR SI4844 1.2.2
- RotaryEncoder 1.6.0
- OneButton (mathertel)
- Battery18650Stats (danilopinotti)
- FastLED 3.10.5 (drives the APA102 ring around the encoder — see
build_opt.hin section 3, which this version makes necessary)
Library paths below assume the Arduino sketchbook is ~/Arduino. Adjust if
yours lives elsewhere.
File: ~/Arduino/libraries/TFT_eSPI/User_Setup_Select.h
Comment out the T-Dongle S3 line, uncomment the T-Embed S3 line:
-#include <User_Setups/Setup209_LilyGo_T_Dongle_S3.h> // For the LilyGo T-Dongle S3 ...
-//#include <User_Setups/Setup210_LilyGo_T_Embed_S3.h> // For the LilyGo T-Embed S3 ...
+//#include <User_Setups/Setup209_LilyGo_T_Dongle_S3.h> // For the LilyGo T-Dongle S3 ...
+#include <User_Setups/Setup210_LilyGo_T_Embed_S3.h> // For the LilyGo T-Embed S3 ...File: ~/Arduino/libraries/TFT_eSPI/User_Setups/Setup210_LilyGo_T_Embed_S3.h
Two edits:
a) Force HSPI port. arduino-esp32 v3.x changed FSPI from 1 to 0,
which makes REG_SPI_BASE(FSPI) return 0 on the S3. The default crashes
with Guru Meditation: StoreProhibited at EXCVADDR=0x00000010 inside
tft.begin(). Forcing HSPI gives a valid SPI peripheral base.
#define USER_SETUP_ID 210
+// arduino-esp32 v3.x changed FSPI=0 (invalid index for REG_SPI_BASE on S3),
+// so force HSPI (SPI3) explicitly to get a valid SPI peripheral base.
+#define USE_HSPI_PORT
+
#define ST7789_DRIVER // Configure all registersb) Set RGB colour order (the panel on this device wants RGB, not the default BGR):
-//#define TFT_RGB_ORDER TFT_RGB // Colour order Red-Green-Blue
+#define TFT_RGB_ORDER TFT_RGB // Colour order Red-Green-Blue
//#define TFT_RGB_ORDER TFT_BGR // Colour order Blue-Green-RedNothing else in Setup210 needs to change.
build_opt.his part of the build, not a stray file. It carries-DFASTLED_LOG_VERBOSITY=0. FastLED 3.10.x logs its channel-driver registration at runtime throughSerial, from insideFastLED.addLeds()— which is the USB CDC port the CAT transport owns, so without this every boot injects ten lines ofChannelManager: Added driver ...into the CAT command stream. It cannot be a#definein the.ino: Arduino compiles each library into its own translation units, which never see the sketch's defines, so the flag has to reach the whole build. Arduino IDE andarduino-cliboth pickbuild_opt.hup automatically from the sketch folder. Deleting it costs 76 KB of flash as well, because the same switch drops FL_WARN's string pool from.rodata.CatControl.cpp/.h,WiFiPortal.cpp/.h: the CAT remote-control transports and the WiFi credential store / captive portal. No library patches needed — they useWiFi,WebServer,DNSServer,ESPmDNSandPreferencesfrom the core as shipped.ESP32_S3_LILYGO_T_EMBED_SI4732.ino: migrated LEDC API from core 2.x (ledcSetup/ledcAttachPin/ledcWrite(channel,...)) to core 3.x (ledcAttach(pin,freq,res)/ledcWrite(pin,...)).ESP32_S3_LILYGO_T_EMBED_SI4732.ino: addedSerial.begin(115200)+ boot diagnostic prints insetup(). Harmless; useful when debugging.- UX: double-press of the encoder now enters the menu with selection defaulting
to Volume (was Band) — easier volume access. See the
cmdMenu = !cmdMenu;block in the click handler (menuIdx = VOLUME;set on entering menu mode). - UI colour tweaks: peak signal bars (>=10), the frequency-grid center marker /
vertical line / triangle, and the "FM Stereo" indicator are now green
(
TFT_GREEN) instead of red — green reads as a positive/locked indicator. - Encoder direction reversed: turning left (CCW) now increments frequency,
volume, etc. One-line change in
rotaryEncoder()ISR — flips the CW/CCW mapping at the source, so every consumer ofencoderCountfollows. - Screen timeout: backlight is dimmed to 0 after
SCREEN_TIMEOUT_MS(20 s) of no encoder/button activity. Any interaction wakes it back to full brightness. Implemented vianoteInteraction()+updateScreenTimeout(); the backlight uses the existing LEDC channel onPIN_LCD_BL, no extra hardware setup. - APA102 LED ring (DI = GPIO42, CLK = GPIO45, 7 LEDs, physical sort
{2,1,0,6,5,4,3}— taken from the LilyGO factory example). Turning the encoder right launches a green comet sweeping clockwise; left sweeps counter-clockwise. The animation free-runs from a single start time and is not reset on each tick, so fast turning produces a smooth continuous spin instead of a stuck single LED. After the last tick the comet finishes one full revolution and then turns off. Throttled to ~33 fps, returns immediately when idle, so the cost inloop()is negligible.FastLED.setBrightness(22)keeps the ring as a discreet indicator.
Tools → Board → ESP32S3 Dev Module, then:
| Setting | Value |
|---|---|
| USB CDC On Boot | Enable |
| CPU Frequency | 240 MHz (WiFi) |
| Core Debug Level | None |
| USB DFU On Boot | Disable |
| Erase All Flash Before Sketch Upload | Disable |
| Events Run On | Core1 |
| Flash Mode | QIO 80 MHz |
| Flash Size | 16MB (128Mb) |
| Arduino Runs On | Core1 |
| USB Firmware MSC On Boot | Disable |
| Partition Scheme | 16M Flash (3MB APP/9.9MB FATFS) |
| PSRAM | OPI PSRAM |
| Upload Mode | UART0/Hardware CDC |
| Upload Speed | 921600 |
| USB Mode | CDC and JTAG |
Equivalent arduino-cli FQBN:
esp32:esp32:esp32s3:PSRAM=opi,USBMode=hwcdc,CDCOnBoot=cdc,MSCOnBoot=default,DFUOnBoot=default,UploadMode=default,CPUFreq=240,FlashMode=qio,FlashSize=16M,PartitionScheme=app3M_fat9M_16MB,LoopCore=1,EventsCore=1,EraseFlash=none,UploadSpeed=921600,DebugLevel=none
- The rotary-encoder push button is on GPIO0 (the BOOT strapping pin). Pressing it during reset puts the chip into download mode.
- esptool's "Hard resetting via RTS pin" is unreliable on the S3 USB-Serial/JTAG port. After upload, a physical RST tap (without touching the encoder) is the most reliable way to start the firmware.
- To force download mode manually: hold BOOT (rotary encoder), tap RST, release both. Then upload. Then tap RST to run.