For a number of years I’ve been working on a model of the EVA pod spacecraft from the film 2001: a Space Odyssey. I decided that the model should have a full interior, complete with working lights and video displays, and this sent me down a pretty deep rabbit hole.
It took some time to find a video display that matched my requirements, but finally in 2023 I found one: the T-Display S3 AMOLED. It’s made by microcontroller board manufacturer Lilygo (often stylized LILYGO; the trade name for Shenzhen Xin Yuan Electronic Technology Co., Ltd of Shenzhen, China).
While I focus here on my 2001 miniature, very small screens like this can be useful for all sorts of model-making applications – hobby and professional. You could make TV sets for a doll house, screens and displays for all kinds of diorama situations, small interactive readouts for simulated space helmets, and so on.
This page details what I’ve learned about this thing, and how to add animated GIF graphics to it. This is a writeup on how to get this device to work, since it’s all pretty fiddly.
This information deals with one specific microcontroller board, but it can in general terms be applied to other such boards which can use the “Arduino_GFX” libraries. I’ve used the technique on Lilygo T-Display S3, Lilygo T-QT, m5stack AtomS3, and and m5stack ESP32 Basic Core boards successfully.
Note that I have zero financial interest or stake with any company mentioned here, and I have not received anything from any of them either. I’m posting this stuff for the benefit of fellow hobbyists. Lilygo’s online documentation is so non-existent it’s rather difficult figuring all this out!
To work with my project, the digital screens had to match the following criteria:
Active area (the part of the display that actually shows pixels) of 19mm × 45mm or so
Minimal circuitry and framing around the display. I need illuminated buttons really close to the screens
Very high-rez image, so individual pixels can’t be discerned
Excellent off-axis viewing since two of the displays would be seen at an angle
Totally black when not showing pixels; no faintly glowing pale blue backlight
Bright colours
Smooth; non-patchy illumination
Capable of playing either animated GIF files or videos
Decent refresh rate, so no flicker to either the eye or a video camera
Instant loading of each frame of animation
Affordable
Over the years I’ve tried different things, but all have all been kind of dissatisfying. Small displays for microcontrollers tend to be pretty low resolution, and a lot of TFT displays lose a ton of contrast and look blue-grey and awful when viewed off-centre. I evaluated dozens of microcontroller boards and LCDs (see the list at the end of this page) Finally the T-Display S3 AMOLED turned up.
A little proof of concept with animated GIFs running on the Lilygo board. The pictures of the displays are offset on the screen intentionally. This won’t be my final animation, as I’m going to redo the images from scratch and also display them to match the film as closely as possible. But it’s impressive how legible the tiny text is. This test includes screenshots taken from the HAL Project.
In short, the board’s hardware is excellent. Its integrated AMOLED screen is fantastic; the best I’ve ever seen on a microcontroller. A big improvement over your typical pixellated and washed-out TFT LCD. The display is almost as high rez as a many a smartphone, and black areas are truly black. It’s an elongated rectangle; a bit of an unusual shape but very nearly the perfect size for my project. The display is protected by, and permanently fastened to, a machined acrylic frame.
The display uses AMOLED technology, so each white dot is actually made up of three microscopic LEDs. Unlike LCDs, which backlight the whole screen and then use liquid crystals to block light for black areas, AMOLED screens are simply off where a black pixel is required. This makes them really high-contrast and potentially lower power in operation. They also don’t require sheets of polarizers, making them better for off-axis viewing (ie: looking at the screen from an angle other than straight on).
The main drawbacks are that AMOLED displays tend to have shorter operational lifespans than LCDs - they can burn in and fade over time. And they can also colour-shift subtly when viewed off-axis.
The salient technical specs for the AMOLED display are:
Display: 1.91" AMOLED (active-matrix organic light-emitting diode)
Display make and model: DWO DO0200FS01.
Resolution: 240 × 536 pixels
Active area: 19.8 × 44.22mm
Display panel size: 22.4 × 51.32 mm
Board size: 60 × 25.5 × 10mm
Colours: 16.7 million (8 bit RGB)
Contrast: 2000:1
Viewing angle: 160° or 178° (unsure; both are published)
Dot pitch: 82.5 × 82.5 μm (0.0825mm square*)
Driver: Raydium RM67162
Interface: SPI or QSPI (the latter is default; the former selected by solder pads, which is awkward)
* Square dots 82.5 μm in size work out to 308 points per inch. This approximates the screen quality of the first “Retina” iPhones, which were 326ppi. Modern phones are usually around 450-500ppi, so this display isn’t as good as those. But it’s still pretty awesome for a microcontroller board!
The board is pretty speedy for a microcontroller, and uses a tiny ESP32-S3R8 dual-core LX7 microprocessor chip. This chip is designed by Chinese firm Espressif Systems, manufactured by Taiwanese semiconductor foundry TSMC, and using CPU designs from American software company Cadence/Tensilica. (that’s the modern global world of computing today!)
It has 16MB flash storage (though you may not be able to use all of it for annoying software reasons described later). It has a nice modern USB C port. It also has a ton of interesting features I don’t need, such as onboard 2.4 GHz Wi-Fi with 3D antenna & Bluetooth 5 LE, an antenna socket, two small programmable pushbuttons in addition to a hard reset button, loads of solder pads for additional hardware, a lithium cell socket (cell not included), a STEMMA QT compatible/4-pin JST-SH socket for easy I2C connectivity, and a green SMD LED.
The product isn’t perfect - it demonstrates some tearing at high framerates, and it does not have a microSD card slot - but it’s pretty great on the whole. And it’s really quite affordable – at time of writing about $26 US or £20 UK.
Basic specs:
Microcontroller chip: ESP32-S3R8 dual-core 32 bit LX7 microprocessor
Wireless: 2.4 GHz Wi-F, Bluetooth 5 (LE)
Antennas: Internal 3D and external socket
Dev environments: Arduino, PlatformIO-IDE, Micropython
Flash: 16MB
PSRAM: 8MB
Buttons: Boot (IO00), Other (IO14), Reset (IO21)
LED: one green, charge red
Power & Data: USB C
Battery: Optional 3.6V Li-Po battery; onboard 1.25mm JST GH connector
Bat Voltage Detection: IO04
Display: ST7789V driver chip; touch version uses CST816
Display protocol: QSPI by default; SPI optional via solder pads
Connector: JST-SH 1.0mm 4-pin, support for STEMMA QT / Qwiic
Size: ~25mm x ~60mm (V1.0 non-touch), ~2.5mm x ~55mm (v2.0 non-touch)
Documentation: (such as it is) https://github.com/Xinyuan-LilyGO/LilyGo-AMOLED-Series
Versions with Lilygo model numbers:
There are several variants of the board, the most important being version 1.0 and version 2.0. It appears that version 2.0 is a bit more compact lengthwise and has a different physical arrangement of buttons. I refer to version 1.0 no-touch in this article.
V1.0 Non-Soldered Pin [H619]
V1.0 Soldered Pin [H634] (includes 2x optional 14-pin headers)
V2.0 Non-Soldered Pin [H713]
V2.0 Soldered Pin [H712]
Touch Non-Soldered Pin [H681]
Touch Soldered pin [H705]
Touch With Black Shell [H717]
Two 1.0 boards, showing each side. So this is the whole thing. The larger of the two black squares is the actual ESP32 S3 CPU.
I bought and tested dozens of different displays looking for the perfect thing before finding the T-Display S3 AMOLED. One possible candidate was the earlier T-Display S3 which is cheaper and has a lower-resolution screen, but I disliked its screen resolution.
I didn’t like the touch version of the T-Display S3 AMOLED, because I have no need for touch capabilities. Touch adds thickness to the display, which gives an increased sense of distance to the images.
There’s also the T-Display AMOLED Lite, which has a 1.47" AMOLED touchscreen, like some smart watches. It’s not as long as the 1.91" boards, and I think it’s touchscreen only. Oddly it lacks S3 in the name, even though it has an ESP32-S3 chip.
From left to right: Lilygo T-Display S3 (the non-AMOLED version), Lilygo T-Display S3 AMOLED 1.91", Lilygo T-Display S3 AMOLED touch 1.91", Lilygo T-Display Lite AMOLED 1.47"
Unfortunately, Lilygo’s software is rudimentary at best. They’ve stuck a few driver libraries in a Github repo, and jotted some half-finished notes in a readme. That’s kind of it. They expect you to code everything from scratch using the Arduino IDE or whatever. No example code. You really have to know what you’re doing to use this thing.
Compare that to the way Chinese firm m5stack supports its boards - they have a bunch of documented code for the experts, upload apps for desktop/laptop machines, and even a nice drag and drop graphical UI environment for kids and casual beginners. Very cool, though they do cost a bit more accordingly. Unfortunately the m5stack boards that have integrated displays – I considered using the Atom S3 for a while – are all lower-resolution and TFT LCD, meaning they have mediocre off-axis viewing.
Another company that’s done a much better job is fellow Chinese maker Waveshare, who have produced an improved clone of the Lilygo T-Display S3 AMOLED. They don’t have docs as good as m5stack, but their material is still much better than Lilygo’s. I would probably have gone for the Waveshare board instead of the Lilygo had I not already bought a set of the latter’s products.
C’mon, Lilygo! Hire a writer or two and post some decent documentation and sample code. You’d probably sell a lot more product that way.
Anyway. All I want to do is upload an animated GIF file to the board and have it play in a loop when I turn it on. That’s it. Not exactly complicated. I mean, animated GIFs have existed since 19 freakin’ 87! Three and a half decades of dorky memes.
The main drawback with using animated GIFs is the limited colour palette: 8 bits, or 256 colours. I’m not showing photos here – the animations were of pretty flat line art graphics – but there’s still visible banding in my graphics in places, because of the restricted number of colours available to a standard GIF. So that is a problem.
Banding or posterizing, caused by the limited GIF palette, is evident here. I’ll probably rework the graphic to minimize this sort of thing.
Sadly, getting simple GIF animations to work is not as easy as I’d like it to be. The LVGL software that Lilygo uses can theoretically play them. But after a lot of messing about I couldn’t get it to play back anything at all.
Even if I could, it looks like I’d have to break each animated GIF into separate images stored in a local C array encoded in ASCII format for uploading, which would be a time-consuming mess every time I needed to alter an image.
Fark. I just want to stick a file in there and have it play!
I’d love to switch to 24 bit APNG if I could - the images would look so much better than the colour-limited GIF format. Unfortunately from what I can see neither LVGL nor Arduino_GFX support animated PNG. And stupidly, neither does Photoshop. Which is such a bummer. It’s 2023 and we’re still using crappy 8 bit GIFs, when we could be using PNGs...
As for actual video, the ESP32S3 is capable of handling certain types of video (ie: older codecs), but there are limitations: the CPU speed, the bottleneck of the connection to the display, and the amount of available storage. Though at least it is QSPI, which is faster than old-skool SPI. And LVGL supposedly has FFmpeg support. But the code is complicated and finicky, with only certain formats supported. (eg: MJPEG and RGB565) I have managed to get MPEG-1 video playing using coder Larry Bank’s fork of pl_mpeg, and I may experiment further with that. The main difficulties are the increase in filesize (the board has limited storage and partition mapping is a mess), and the fact that these obsolete formats are rather inconvenient to encode for these days.
Fortunately there’s no need for full-blown video with my project anyway, because I’m effectively modelling a very low framerate slideshow. Keep It Simple. GIFs have lousy colour, but are easy to use. And they also let you specify arbitrary frame time values, thus sort of simulating variable framerates.
Although I couldn’t get LVGL to cooperate, there is a collection of open source software built by Github user moononournation and other kind developers, called Arduino_GFX, that now handles the Lilygo device. This package incorporates a bunch of graphic tools into one library, and supports a large range of boards and displays.
In June 2023 he added support for the Lilygo AMOLED board. His code can play back animated GIFs stored in a filesystem on the board. Fantastic.
Big thanks to Chen Liang (I think his name is) of Hong Kong, the author/compiler of this thing.
Here’s how to use it. It took weeks to figure out this whole thing, so I’m posting it here in case anybody else is also struggling.
The easiest thing would be if Lilygo had put a microSD card socket on the board. But since they hadn’t, the next best bet would be to build a basic filesystem into the board’s firmware. You should then be able to upload files easily. You’d think this would be simple, right? The board has a damn USB port on it! Why can’t you just plug the thing into a computer, have it simulate a USB class storage device, appear on the desktop, and drag some files over? Easy. Microcontrollers aimed at kids: the BBC micro:bit and Adafruit’s Circuit Playground Express do that!
But nope. The Lilygo board has no filesystem at all. So you have to install one, such as SPIFFS or LittleFS, or even FAT. But then how do you transfer a file into your board’s filesystem from your computer?
One option is to encode the GIF as a bunch of ASCII-formatted code as an array to be incorporated into the program’s source code itself, the way LVGL does. This is a lousy option, since every time you make the smallest change to a graphic you have to mess around re-encoding it, recompiling the binary, and uploading the binary.
A much easier approach was to use a plugin that enabled this capability within the Arduino IDE. You’d install the software, a menubar item appears in the IDE, and you can upload your selected file or files from a “data” folder in your Arduino sketch’s folder. Great.
Except... Arduino IDE software version 2.0 broke these plugins. As a result I was stuck with Arduino 1.8.19, which is older abandoned software and no longer under development.
Fortunately, there's now a plugin that’s compatible with Arduino IDE 2.x. Hooray! Big thanks to Earle F. Philhower, III of the US, author of this plugin.
I’ve written up instructions on how to do this uploading business using either Arduino 1.8.x or Arduino IDE 2.3.x.
And speaking of the Arduino IDE, I really hate it. It’s free to use, sure. But the software and its ecosystem are a clunky, erratically designed, messy house of cards. Its UI is disorganized, with stuff scattered all over. Everything is brittle and you waste hours troubleshooting because its debugging capabilities are nil and error messages are cryptic.
And it forgets your settings! All the fucking time. I have to constantly go back and re-select the port, for example. Over and over and over.
I’d pay for something less flaky. Rant rant rant.
I’m not going to go into detail about how to make an animated GIF file. There are obviously a lot of tools out there that can be used for this. I personally use Adobe Photoshop, even though Adobe charge extortionate prices and are generally customer-hostile these days. Mainly because Photoshop lets you stack a bunch of layers consisting of all the different frames, then assign them different time values. Obviously your GIF should probably have the same pixel dimensions as the display that you’re using.
The ability to specify frame display times in Photoshop is really handy because the actual 2001 animations were displayed on-screen for varying lengths of time. And this is obviously a much more CPU and space efficient way of displaying these images than outputting an actual video file. You can also, through a lot of careful work, have independent left and right display timing.
As griped about earlier, GIF only supports 256 discrete colours. And, also as noted earlier, the screen does tear a bit on rapid frame changes. So flashing text set to 0.04 seconds per frame (ie: basically 24 fps) will exhibit the problem, sadly.
Finally - we’re getting to the useful part of this page!
Now I'm going to talk about how to get animated GIF playback installed onto the Lilygo device. Because of the aforementioned problems with uploading files to the LittleFS filesystem, I’ve split this part into two sections – one for legacy Arduino 1.8.x, and one for current Arduino IDE 2.3.x.
These instructions have been verified on a Mac running macOS 13.4 Ventura through to macOS 14.7 Sonoma.
I’m not writing up how to do this on a Windows PC or a Linux box because I haven’t got one of those. Much of this should still be applicable, but the specifics you’ll have to figure out yourself.
These are the steps to install an animated GIF using Arduino 1.8.x (the obsolete version), the ESP32 software, the Arduino_GFX libraries, and the file upload plugin.
Get Arduino 1.8.x
This is the IDE (integrated development environment, for varying values of the word “integrated”) written by Italian board maker Arduino, that lets you compile code for their line of small microcontrollers. Since it’s open source a lot of their competitors also use it.
Download the Legacy IDE (1.8.X) application.
Run the application.
It creates a new “Arduino” folder in your user account’s “Documents” folder if you haven’t got one already.
Get the ESP32 software
This is the software from Espressif, makers of the ESP32 CPU chipset, that allows the Arduino IDE software to compile code for those chips. The Lilygo board uses the S3 version of the ESP32 CPU.
Go to the Arduino Settings dialogue box and add an additional board manager URL. Paste in:
https://raw.githubusercontent.com/espressif/arduino-esp32/gh-pages/package_esp32_index.json
Go to “Tools > Board > Boards Manager” and search for “esp32”.
Click “Install” to get the latest drivers for the Espressif ESP32 board; 3.2.0 at time of writing.
Install GFX Library for Arduino
This is a set of open source libraries that allow boards like the Lilygo to display graphics, including animated GIFs.
Go to “Tools > Manage Libraries”
Search for “arduino_gfx”
Find the “GFX Library for Arduino” by “Moon On Our Nation”. Version 1.5.6 is current.
Click “Install”.
Install the filesystem uploader
This is a tool that allows you to upload animated GIF files to the Lilygo board. It supports the SPIFFS, LittleFS, and FAT filesystems. I use LittleFS since it’s the default for the Arduino_GFX library.
Download the latest release for macOS (this is a fork):
Uncompress the zip archive.
Make a set of new folders so you can have the following directory path:
~Documents/Arduino/tools/ESP32FS/tool/
(ie: you’ll probably need to make the “ESP32FS” and “tool” folders)
Copy esp32fs.jar into the tool folder.
Fix the missing esptool problem
One of the delights of working with open source software is that stuff is always breaking and acting weirdly, and it’s difficult to figure out what’s going on. In the case of the uploader plugin, it turns out it’s hardcoded to look for a specific Python tool for handling the communications with the chip. If it’s not in that one expected location, the plugin won’t work.
So if you get the error message “esptool not found!” when running the filesystem uploader, then try this fix suggested by the author of Arduino_GFX:
/usr/bin/pip3 install esptool
ln -s ~/Library/Python/3.9/bin/esptool.py ~/Library/Arduino15/packages/esp32/tools/esptool_py/4.5.1/esptool.py
Obviously you’ll need to update the symbolic link command to whatever version of the ESP32 software you’re using, and thus whatever directories are appropriate.
Configure the Arduino IDE
There is no preset board definition available for this AMOLED board. You have to use a generic ESP32 S3 board definition and change a bunch of settings.
Lilygo list most of the values that you need to set on the Arduino IDE through a whole series of pulldown menus. (note to Arduino: this is a poor choice of UI for selecting this stuff!) However Lilygo list them in the order in which they appear in Arduino IDE 2.x.
So here’s the settings list in the order in which they appear in Arduino 1.8.19, since it’s time-consuming to set these menu items when they’re in the 2.x order. I’ve marked the ones that need to change from the defaults in boldface.
Board: "ESP32S3 Dev Module" (that’s S3 there)
Upload Speed: 921600
USB Mode: Hardware CDC and JTAG
USB CDC On Boot: Enabled
USB Firmware MSC On Boot: Disabled
USB DFU On Boot: Enabled (Requires USB-OTG)
Upload Mode: UART0 / Hardware CDC
CPU Frequency: 240MHz (WiFi)
Flash Mode: QIO 80MHz
Flash Size: 16MB (128Mb)
Partition Scheme: Huge APP (3MB No OTA/1MB SPIFFS)
Core Debug Level: None
PSRAM: OPI PSRAM
Arduino Runs On: Core 1
Events Run On: Core 1
Erase All Flash Before Sketch Upload: Disabled/Enabled
JTAG Adapter: Integrated USB JTAG
Port: /dev/cu.usbmodem101 (ESP32S3 Dev Module)
You may need to change the partition scheme, depending on what you’re uploading. Unfortunately you don’t have a lot of choices in that regard. Even though the Lilygo board has 16 MB of RAM, there’s no partition option that lets you access more than 3MB of it, which is a drag since that’s not a filesystem limit. I’m sure those values are stored in an editable conf file somewhere, but I haven’t located it yet. Incidentally the menu command mentions SPIFFS only, but the setting seems to work with LittleFS also.
Note that because the Arduino IDE is so shitty you’ll probably have to wade through these menus time and again resetting some of these functions in the future, since the software keeps forgetting them. The active port seems the most commonly lost setting. I’ve had this problem with different versions of the Arduino IDE, on different versions of the OS, and with several different microcontroller boards.
This is the newer way of doing it.
Get the Arduino IDE
Download Arduino IDE for your operating system and CPU type. (2.3.4 at time of writing)
Run the application.
The IDE creates a new “Arduino” folder in your user’s “Documents” folder if you haven’t got one already.
Incidentally it also creates and relies upon “~.arduinoIDE”, “~/Library/Arduino15”, and “~/Library/Application Support/arduino-ide”.
Get the ESP32 software
This is the software from Espressif, makers of the ESP32. The Lilygo board uses the S3 version of the ESP32 CPU.
Go to Arduino IDE > Settings... and look for “Additional boards manager URLs”. Paste in:
https://raw.githubusercontent.com/espressif/arduino-esp32/gh-pages/package_esp32_index.json
Click OK.
Go to “Tools > Board > Boards Manager” and search for “esp32”.
Install the latest “esp32” drivers by Espressif Systems. (3.2.0 at time of writing)
Install GFX Library for Arduino
This is a set of open source libraries that allow boards like the Lilygo to display graphics, including animated GIFs.
Go to “Tools > Manage Libraries”
Search for “arduino_gfx”
Find “GFX Library for Arduino” by “Moon On Our Nation”. (1.5.6 is current at time of writing)
Click “Install”.
Install the LittleFS Uploader Plugin
This plugin requires Arduino IDE 2.2.1 or newer.
Go to the LittleFS Uploader page.
Download the latest binary. At time of writing it’s arduino-littlefs-upload-1.5.4.vsix.
Switch to the Finder and go to the folder “~/.arduinoIDE/”. (ie: go to Go > Go to Folder... and paste in that file path.
This reveals the invisible “~/.arduinoIDE” folder inside your home folder.
Create a new folder here called “plugins”
Find the “.vsix” file that you downloaded earlier – perhaps in the Downloads folder.
Move the file into the new “plugins” folder.
Launch the Arduino IDE (or quit it and relaunch if it’s open).
Type command-shift-p to bring up the command palette.
Scroll down and look for the item “Upload LittleFS to Pico/ESP8266/ESP32” in the list. This verifies that the new plugin is correctly installed.
Configure the Arduino IDE
There is no preset board definition available for the Lilygo T-Display S3 AMOLED. You have to use a generic ESP32 S3 board definition and change a bunch of settings.
Here’s the settings list in the order in which they appear in Arduino 2.3.x’s Tools menu.
Board: "ESP32S3 Dev Module" (note that’s S3 there; select Board > esp32 > ESP32S3 Dev Module if necessary)
Port: /dev/cu.usbmodem101 (ESP32S3 Family Device)
USB CDC On Boot: Enabled
CPU Frequency: 240MHz (WiFi)
Core Debug Level: None
USB DFU On Boot: Disabled
Erase All Flash Before Sketch Upload: Disabled
Events Run On: Core 1
Flash Mode: QIO 80MHz
Flash Size: 16MB (128Mb)
JTAG Adapter: Integrated USB JTAG
Arduino Runs On: Core 1
USB Firmware MSC On Boot: Disabled
Partition Scheme: Huge APP (3MB No OTA/1MB SPIFFS)
PSRAM: OPI PSRAM
Upload Mode: UART0 / Hardware CDC
Upload Speed: 921600
USB Mode: Hardware CDC and JTAG
Zigbee Mode: Disabled
You may need to change the partition scheme, depending on what you’re uploading. The one above seems to work for me, even though I’m actually uploading stuff using the LittleFS filesystem.
Note that because the Arduino IDE is so shitty you’ll probably find the software keeps forgetting these settings between launches.
This section applies to both versions of the Arduino IDE.
Navigate to:
/Documents/Arduino/libraries/GFX_Library_for_Arduino/examples/ImgViewer/ImgViewerAnimatedGIF
Make a duplicate of the “ImgViewerAnimatedGIF” folder and put it into the top level of the Arduino folder or wherever you want.
Critically, since you’re coding for an AMOLED board, delete or comment out this line:
// #define GFX_BL DF_GFX_BL
If you don’t deal with the line above, your graphics will appear green. AMOLED boards do not have backlights, so this LCD command for TFT LCD backlights breaks things.
Leave the backlight code in place if your screen is a TFT LCD, such as a Lilygo T-Display non-AMOLED or T-QT.
Urgh. Yes, this is how the AMOLED screen will look if you send the board a backlight command.
Next, we need the relevant AMOLED lines from the Arduino_GFX_dev_device.h file:
Find the Arduino_DataBus line and replace it with:
Arduino_DataBus *bus = new Arduino_ESP32QSPI(
6 /* cs */, 47 /* sck */, 18 /* d0 */, 7 /* d1 */, 48 /* d2 */, 5 /* d3 */);
And find the Arduino_GFX line and replace it with
Arduino_GFX *gfx = new Arduino_RM67162(bus, 17 /* RST */, 0 /* rotation */);
Incidentally, the same approach applies if you’re coding for a related product, such as the non-AMOLED Lilygo T-Display-S3, or the Lilygo T-QT. Just search for the correct DataBus and GFX lines in Arduino_GFX_dev_device.h and paste those in.
At this point it should be a simple matter of compiling and uploading the code, and uploading the animated GIF file that you prepared earlier.
Oh, yes. Should be.
This is how you compile your code and upload it using Arduino 1.8.x.
Click the small right arrow button in a circle (in the project’s window bar) to compile.
Make sure your GIF file is in a folder named “data” that’s in the same folder as your sketch.
Make sure the GIF file is named whatever name your code uses. The default for “ImgViewerAnimatedGIF.ino” is “ezgif.com-optimize.gif”.
Select “Tools > ESP32 Sketch Data Upload” to upload the GIF.
Choose the LittleFS filesystem.
It will probably take a few tries to get both tools to upload your code and data successfully. You might have to power-cycle the board, reset it, curse, etc. You might also need to experiment with different partition types.
But once it’s all done the board should play the animated GIF in a loop.
This is how you upload the GIF and code using Arduino 2.3.x and the new LittleFS uploader tool.
Note that you have to upload the GIF to the device before compiling the GIF player code. If you don’t, the GIF player will loop fruitlessly without a file to play. (“ERROR: File System Mount failed!”) The board will then reboot continuously, making it difficult to use the LittleFS uploader.
Upload the GIF
Make sure your GIF file is in a folder named “data” that’s in the same folder as your sketch.
Make sure the GIF file is named whatever name your code uses. The default for “ImgViewerAnimatedGIF.ino” is “ezgif.com-optimize.gif”.
Type command-shift-P and select “Upload LittleFS to Pico/ESP8266/ESP32” to upload the GIF.
Compile and upload the code
Click the small right arrow button in a circle (in the project’s window bar) to compile the code and upload it to the device.
Hopefully this will get your board playing your animated GIF. Sit back and watchen the blinkenlights.
If the board isn’t recognized by the Arduino IDE, you’ll get an error like this:
A fatal error occurred: Could not open /dev/cu.usbmodem1101, the port is busy or doesn't exist.
Since nobody likes fatalities you could try:
Putting the board into manual upload mode. To do this press the BOOT button (it’s the one directly behind the STEMMA QT port, not the one behind the red LED) and hold it. While holding it press the small button on the opposite side of the board marked RES for reset. Release RESET, and then release BOOT. The screen should stay blank and hopefully will talk to the Arduino IDE.
Another thing to try is to switch Tools > Port to something else, such as /dev/cu.wlan-debug, and then switch it back to /dev/cu.usbmodem101. That’s right – try turning it off and on again.
Finally, make sure the Serial Monitor pane in Arduino is not open.
If you get the Arduino IDE 2.3.x message “Board details not available. Compile the sketch once” when trying to upload a GIF, you may have an animated GIF player that’s stuck looping, sans file to play. If that happens you may need to do something like compile a generic program like HelloWorldAmoled.ino, upload the animated GIF from that window (copy the Data folder containing the GIF file to HelloWorld.Amoled’s home directory) and upload the GIF first. Then recompile the GIF player code.
I don’t know why the error “Unable to open upload terminal” comes up a lot with Arduino IDE 2.3.x and the uploader tool. Quitting the Arduino IDE app and relaunching it seems to fix it sometimes.
If the compilation fails with a message about XCode, you’ll need to install the XCode development environment from Apple. This is a free, but enormous, suite of software used for developing professional apps on Macs and other Apple products. You can get it from the Apple App Store. This lets you install XCode’s command line tools.
If you get an error about the partition, you’ve probably selected a partition type that isn’t compatible. Or maybe you’ve selected a partition scheme that’s larger than the physical memory on the board.
It is apparently possible to create your own partition definition. Which is good since the selection of partition types supplied in the esp32 folder is pretty crap. But you have to know some pretty specific stuff about the memory size and configuration of your device, and I haven’t been able to get the Arduino IDE to load any of my customized partition defs because you have to make changes to a file called boards.txt that define what is available. But if you want to try it the partition type files are stored as CSVs, and seem to be available in: ~/Library/Arduino15/packages/esp32/hardware/esp32/3.2.0/tools/partitions/
Not easily, no. But if you’re foolish enough, yes.
At least the non-touch variant. The touch version of the board is another story.
This is something I wondered about, because the width of the clear plastic frame around the display interferes with my project. (I need illuminated buttons right up next to the screen) However, because the glass display is so incredibly thin – a mere 0.89mm – Lilygo wisely mounted it within a recess in the protective frame. Incidentally, earlier models of the board had CNC-machined clear acrylic frames, but later versions seem to have injection-moulded frames.
The plastic frame is then attached to the PCB with clear gel tape at three locations – one long strip at the antenna end, and two squareish blobs at the pushbutton end. Ingeniously the rubbery resilience of the gel tape actually serves as a spring mechanism for the two pushbutton covers built into the frame.
The frame is raised a millimetre or so off the board surface because of the thickness of the gel tape. Now, you can carefully cut out the tape with a sharp knife and release the frame from the board itself, as shown below.
Detaching the screen and frame from the board gives you some flexibility in placement in some cases.
Don’t try this at home.
Here I’ve detached the acrylic frame (clear plastic) from the printed circuit board (black) and removed the gel tape. I also opened the ZIF connector and removed the flex cable.
However, separating the display and board is risky. The flat flexible cable (FFC) linking the two is fragile and easily torn, and it attaches to the board with a very easily broken ZIF (zero insertion force) connector. The flex cable also has SMD components soldered to it, and cannot be bent.
Separating the display from the plastic frame is a whole other story. The super-thin and delicate display is fastened to the frame’s recess with thin double-sided tape, and I don’t think it’s realistically possible to remove it without breaking the display. If it weren’t recessed you might have been able to slide a thin knife or razor blade between the display and acrylic, but that isn’t possible in this case. So you have the width of the acrylic frame limiting how close the screen can be positioned to things.
Or you can just buy a replacement bare screen and install it in place of the Lilygo one, which is what I’ve done. The DWO DO0200FS01 is the exact part that Lilygo used. You can even see the company name, website, and part number printed on the flex cable in the photo above. And at time of writing you can buy new displays right from the factory (luckily they have a MOQ or minimum order quantity of 1) or via various Chinese online resellers. They’re pretty cheap even sold individually – the main expense is actually shipping since they come in relatively large boxes to protect them.
But I would warn anyone who wants to give this a go — the flex cable is thin as paper (strong but can be creased or torn at the edges), the ZIF connector flip-clamp can be broken by sneezing on it, and the display itself is a piece of glass thinner than a microscope slide! The AMOLED display is way thinner than your typical miniature TFT LCD.
There's a reason that Lilygo chose to mount the display in a protective plastic frame, and it’s a good one.
An unprotected bare DWO DO0200FS01 screen plugged into the Lilygo T-Display-S3 AMOLED board.
This is how you can get the board running a display without the big plastic frame.
Incidentally, speaking of the screen, I’ve seen at least one review complaining about bad pixels — that their board had a ring of red dots on it. That’s actually a printed red circle on the protective plastic sheet that ships on the screen, and not on the screen at all! (you can see it in the photo above - it's the rough ring of dots to the left side of the screen)
Oopsie.
Most of Lilygo’s boards are available with and without pre-soldered headers. The header pins are useful if you want to plug the board into a breadboard or a DuPont cable.
The upper board has two rows of headers permanently soldered in place. The lower board has only unpopulated holes for each pin, and Lilygo usually supplies header pin blocks (below) you can add yourself.
If you plan on using the pins the presoldered version is definitely worth it – it’s super fiddly work, soldering the header blocks when the display is so close to them.
I’ve built a 3D model of the Lilygo T-Display S3 AMOLED board for my own planning purposes. (ie: figuring out how the real board would fit within a 3D model.)
If it’s useful to anyone else, here’s the STL of my model. Note that it is NOT 100% accurate, and it’s missing a lot of internal components of the real board - I mostly modelled the bits that stick out, and added some internal bits to make it more printable. I make no promises it will work for your application, but it should be reasonably close in size, which is the point.
T-Display-S3 AMOLED product page
T-Display-S3 AMOLED Github repo
Arduino_GFX Github discussions page
ESP32: Upload Files to LittleFS using Arduino (the older version)
Arduino IDE 2: Install EPS32 LittleFS Uploader
Arduino LittleFS Upload plugin
Volos Projects - a YouTube channel from a guy who specializes in Arduino projects
I’m probably not going to be able to help you if you can’t get your file to play. I’m not a developer, and I’m not versed on the detailed ins and outs of how this stuff works - let alone on what to do when it all breaks. I’ve put this material up here because I spent ages figuring this all out, these steps worked for me, and I hope they help someone else! That said, if you notice an error or have a suggestion for improving things, please drop me a line.
My recommendation is to post to the discussion areas associated with the software in question and see if someone there can give you a hand. Good luck!
Regrettably I'm not in a position to distribute the animations that I built for my project.
28 July 2023; revised 26 April 2025.