A self-contained display + touch + relay driver for the ESP32-S3 4848S040 development board — the 4.0" board with a 480×480 ST7701 RGB LCD, a GT911 capacitive touch panel, and up to 3 onboard relays. It drives all of these so you don't have to think about any of them: the hardware is fixed and fully handled, leaving you to write an LVGL 9 app and nothing else.
The two bundled examples are the heart of the library. Each is a complete, working starting point — the display, touch, and LVGL are already wired up and running before your code gets control. You pick the one that matches how you want to build your UI, copy it, and start replacing its placeholder UI with your own.
This is for the ST7701 480×480 RGB-interface variant of the 4848S040 (ESP32-S3, OPI PSRAM). Other 4848-style boards with different panels need a different library.
This library is distributed by downloading the code as a ZIP from GitHub — there is no Arduino Library Manager entry. To install:
- On the GitHub project page, click Code ▸ Download ZIP.
- Unzip it, and move the resulting folder into your Arduino libraries
directory:
- macOS / Linux:
~/Documents/Arduino/libraries/ - Windows:
Documents\Arduino\libraries\
- macOS / Linux:
- Restart Arduino IDE so the examples will appear in the File menu.
(Alternatively, in the IDE: Sketch ▸ Include Library ▸ Add .ZIP Library… and select the downloaded ZIP.)
The examples need LVGL 9. Install it via the Arduino Library Manager
(search "lvgl"). Each example sketch folder ships its own lv_conf.h; LVGL reads
its configuration from that file, so keep it next to the .ino.
This library was developed against LVGL 9.3 (the current version of LVGL that is compatible with SquareLine) but is very likely compatible with later versions.
This board uses a generic ESP32-S3 board definition. In Tools, set:
- Board: ESP32S3 Dev Module
- PSRAM: OPI PSRAM (required — the framebuffers live in PSRAM)
- Flash Size: 16 MB (128 Mbit)
- USB CDC On Boot: Enabled (required to see serial output)
- Upload Speed: 460800
Serial note: USB serial on this board is
Serial0, notSerial. With "USB CDC On Boot" enabled,Serial0.begin(115200)prints to the USB port.
Open them from File ▸ Examples ▸ chipguy_4848S040_display. Both come up running on the hardware immediately, so you can confirm your board works before you change a line. They differ only in how you author the UI.
To turn either one into your own project, save it under a new name
(File ▸ Save As) so your work lives outside the library, then start editing.
Each example folder is self-contained — it carries its own lv_setup.hpp and
lv_conf.h — so a copied sketch is fully standalone.
Use this when you want to build the interface in code: directly, or by handing the sketch to an AI coding assistant such as Claude Code.
The UI lives in a tiny ui.h / ui.cpp pair in the sketch folder. All the
hardware/LVGL setup happens in the sketch before ui_init() is called, so
ui_init() is a blank canvas: it receives a live LVGL screen and you create
widgets on it. To start your app:
- Open
ui.cppand look atui_init(). The demo it ships with (a label that follows your finger, plus a color-cycling rectangle) is just there to prove the display and touch work — delete it. - In its place, build your own UI on
lv_screen_active()using normal LVGL calls (lv_label_create,lv_button_create, event callbacks, …). - As your app grows, add more screens and helper functions, and declare the
ones the sketch needs in
ui.h.
Because everything except the UI is already wired up, this scaffold is ideal to
hand to Claude Code: point it at the sketch folder and describe the app you
want — it can focus entirely on ui.cpp and the screens you add, without
touching display/touch/LVGL plumbing.
Reach for this one when you want full control, a tiny footprint, or AI-assisted/hand-written UI code.
Use this when you'd rather design the interface visually in SquareLine Studio (a drag-and-drop LVGL UI editor) and just run the result on the board.
The sketch's src/ folder holds a small placeholder UI in the exact file
shape a SquareLine export produces (ui.c, ui_Screen1.c, …). The sketch calls
the ui_init() that SquareLine generates. To start your app:
- In SquareLine Studio, create a project sized 480×480, 16-bit color, with no rotation or offset.
- Lay out your screens visually.
- Export ▸ UI files (the "Arduino TFT_eSPI profile" works, among others).
- Replace the whole
src/folder with your export. Build and upload —ui_init()now runs your design.
Treat src/ as disposable: you overwrite it wholesale each time you re-export
from SquareLine, so don't keep anything there. Your own application logic lives
in the sketch — after calling ui_init(), attach event handlers from the
.ino to the UI objects SquareLine exposes (e.g. ui_Button1). That way
re-exporting the visuals never touches your code.
Reach for this one when you want to iterate on layout visually and write little or no C for the UI itself.
Both examples wire the hardware to LVGL through one small header,
lv_setup.hpp (plus lv_conf.h for LVGL's build config). That header,
and the global objects it defines, is the entire surface you interact with — and
once begin() returns, you are just using plain LVGL.
#include "lv_conf.h"
#include "lvgl.h"
#include "lv_setup.hpp"
void setup() {
Serial0.begin(115200);
lv_setup.begin(); // initialize display + touch + LVGL
// ...build your UI on lv_screen_active() here (or call ui_init())...
}
void loop() {
lv_timer_handler(); // let LVGL run
delay(5);
}What the header provides:
lv_setup.begin()— call once insetup(). Brings up the ST7701 display, the GT911 touch, and LVGL; allocates the two PSRAM framebuffers, creates the LVGL display in direct render mode plus a pointer (touch) input device, and connects them. After this returns, the active LVGL screen exists and you build your UI with ordinary LVGL calls.display— a globalESP32_4848S040display object. Handy methods:display.width(),display.height()(480×480), anddisplay.setBacklight(0..100)to dim/brighten the backlight.
You don't render or flush anything yourself — LVGL renders straight into the
panel's framebuffers and the driver flips them at vsync (tear-free, double
buffered). Your job is to create LVGL widgets and call lv_timer_handler() in
loop().
Because every hardware detail lives in just two files — lv_setup.hpp and
lv_conf.h — your application code (ui.cpp, or the SquareLine src/
files) is hardware-independent. It only ever talks to LVGL.
So to move an app to a different display board, drop in the lv_setup.hpp
and lv_conf.h from that board's chipguy library, and the rest of your sketch
is unchanged. The same UI runs on different panels with no edits to your widget
code. (Different boards have different resolutions, so alignment-based layouts
travel best; fixed pixel coordinates may need adjusting.)
Beyond display and touch, the board has 3 relays, driven by
ESP32_4848S040_Relays:
#include <chipguy_4848S040_relays.h>
ESP32_4848S040_Relays relays;
void setup() {
relays.begin(); // pins as outputs
relays.setRelayEnergized(1); // relay 1 ON
relays.setRelayOff(1); // relay 1 OFF
relays.setRelay(2, HIGH); // explicit state, relays 1..3
}Relay states are held across a warm reset (watchdog timeout, software reset,
task panic). Each relay pin uses the ESP32 GPIO hold latch, which lives in
the always-on RTC power domain and is released only by a power-on reset or an
explicit change — so the pad keeps physically driving its level straight through
a watchdog reboot, with no drop even during the boot window before your code
runs. The last commanded state is also saved in RTC memory so relays.begin()
can re-establish the hold glitch-free on the way back up. A true power-on
intentionally starts with all relays OFF (the latch and RTC memory are both
cleared on power loss), so relays won't surprise-energize when power returns.
This library is mixed-license:
- The original code (display driver, relay driver, examples) is MIT — see LICENSE.
- The GT911 touch driver (
chipguy_4848S040_touch.h/.cpp) is a derivative of TAMC_GT911 (Copyright © TAMC) and is licensed under the Apache License, Version 2.0 — see LICENSE-Apache-2.0.txt. Those files carry the upstream attribution and note the modifications made for this board.
The ST7701 initialization/gamma values are standard panel register settings.
lv_conf.h is based on LVGL's template (LVGL is MIT-licensed).