Tinytron 📺

🔗 📋 Overview

Tinytron is an ESP32 powered video player with a 1.69 inch display, designed specifically around the ESP32-S3-LCD-1.69 from Waveshare.

It can play MJPEG files from a microSD card, or stream video content from a computer over WiFi. It runs on battery, features a web interface with Wifi access point mode for configuration, and can be controlled with a single physical button.

🔗 Project goals

There are plenty of great ESP32 TV projects around (credits name a few). Tinytron is my attempt at standing on their shoulders to make one that's very easy to build and use:

Note: with these goals in mind, I chose not to support audio playback in this project.

🔗 Additional features

🔗 ⚙️ Hardware

I sourced parts from Aliexpress and Amazon, and designed the case around them.

🔗 Bill of Materials

One of Tinytron's goals is to keep the BOM as short as possible. Only 3 parts, and 6 easy solder joints are needed. There is no audio amp nor speaker in this project.

Part Notes Link (non affiliated)
Waveshare ESP32-S3-LCD-1.69 This is the non touch version. The touch version has thicker glass and a different footprint. Amazon.fr, Aliexpress
Micro SD card reader breakout board The case was modeled after this exact board.

Dimensions: 18x18x20mm
Amazon.fr
400 mAH 3.7v LiPo battery Important: the default connector is PH2.0 which isn't compatible with the Waveshare dev board. I spliced a connector but you can custom order the correct one (JST1.25mm) from the battery seller.

Technically optional it is possible to power the device via USB C connector.

Dimensions: 6x25x30mm
Aliexpress
Micro SD card FAT32 formatted. Also technically optional since the project can also stream video over local network.

🔗 3D printed case

The case design is inspired by late 90's TV/VCR combos like the Sony KV-14V6D. It was made with OnShape and has 4 parts: front, back, bezel and button. Glue is required to stick the bezel to the front part, the rest of the assembly snap fits (with pretty tight tolerances). Printing the parts separately allows for interesting color combinations.

🔗 Printing instructions

Download the STL files

The case was only tested with Prusa MK4S and PLA. It prints with 0.28mm layers in about 30 minutes and 16 grams of material. I tried my best to limit the need for supports but paint-on supports (pictured in green in the screenshots below) are recommended in select spots for best results.

Part Face on print bed Paint-on support
Front Front face Inside of USB C slot
Back Vent side Around the curved edge on the print bed
Bezel Flat side None
Button Top side None

🔗 🪛 Assembly instructions

Assembly only takes a few minutes. You'll need a drop of plastic glue or cyanoacrylate glue, a bit of electrical tape, a soldering iron and flush cutter pliers.

🔗 1. Solder the header

Solder the 6 pin header to the SD breakout board. Trim the pins underneath.

🔗 2. Prepare the SPI cable

The ESP32 dev board comes with a cable that breaks out some IO. We need to cut some of the wires to save room, and use the rest to connect the SD card reader.

Separate the lines that we'll keep from the ones we'll cut according to the table below, triple check everything and cut the unused wires flush against the connector. You can change the suggested wiring if you update the platformio.ini file accordingly, but these are the default.

Keep Description Cut
 Black  GPIO18
 Red  GPIO17
GPIO16  Green 
GPIO3  Yellow 
GPIO2  Black 
 Red  SDA (GPIO11)
 Brown  SCL (GPIO10)
U0TXD  Orange 
U0RXD  Green 
 Blue  3v3
 Purple  GND
5v  Gray 

Connect the female pin sockets from the uncut wires to the micro SD board like so:

Cable color Label on SD board ESP32 pin
 Blue  3v3 3v3
 Brown  CS SCL
 Red  (the one next to black on the cable) MOSI GPIO17
 Red  (the one next to brown on the cable) CLK SDA
 Black  MISO GPIO18
 Purple  GND GND

Then wrap a bit of electrical tape around the male pins in order to create a makeshift connector that you can easily unplug and plug back. Detach it from the SD board for now.

⚠️ Warning: Be very careful never to plug your home made connector the wrong way: you'd swap the Ground and 3v3 lines and and most likely damage the hardware.

🔗 3. Glue the bezel

The first step is to glue the bezel on the front piece. If you aren't confident that you can one shot the alignment, use a piece of painter tape as a hinge to align the two pieces, then open it, add some cyanoacrylate or plastic glue, and snap it back in place.

🔗 4. Assemble the device

The rest of the assembly requires no glue nor any tool and takes about 3 minutes, but please DO READ THE ADVICE BELOW VERY CAREFULLY BEFORE PROCEEDING.

⚠️ Warning: The tolerances are very tight, be gentle when manipulating the display or you might break it (trust me, I know). Watch the video and respect the insertion order of the display.

Assembly is quick, but there are 4 points to be pay attention to:

  1. First, hold the case vertically and make sure the push button piece is at its lowest position when inserting the display. Do not force if the button blocks the display!
  2. Then, be very gentle when snapping the display in its final position (use the flex of the case rather than the flex of the display, as it turns out it doesn't have any). Make sure the USB-C port is properly aligned.
  3. Pay attention to plug the SPI cable to the SD card reader with the blue cable on the left or you'll fry the board.
  4. Perhaps most importantly, when closing the back, also pay attention to which side to insert first and be careful not to pinch the battery. If you did, it could catch on fire.

✨ Congratulations, your Tinytron is ready! ✨

🔗 Reopening the case

In case you need to reopen the case, look for the small notch (left side when facing the display), insert a flathead screwdriver and gently twist it. The case should snap back open. In my experience - and it depends on the filament used - it's rather difficult to open it with bare hands.

🔗 ⚡ Flashing the firmware

🔗 Web flasher

You can flash the latest build directly from this page: simply connect your ESP32 to this computer over USB C, click the Connect button, and follow the instructions.

Note: this is a "factory firmware", flashing it will erase the memory and any previous settings (WiFi etc.) that might be stored on your device. To update the firmware to a new version without losing your data, prefer the other methods below.

🔗 Building locally

This project is built with PlatformIO. You can use the PlatformIO extension for VSCode or the command line interface.

To build the project locally and flash it to the device, connect the ESP32-S3 board via USB and run:

platformio run --target upload

🔗 Over-the-air updates

Once the initial firmware is flashed, you can perform subsequent updates over the air. Connect to the device over WiFi, go to the Firmware tab, select your ota firmware file and click "Upload Firmware".

You can find a build of the latest OTA firmware here, or you can build it yourself as explained below.

🔗 Build the binary

platformio run

Then, navigate to the device's web UI, go to the "Firmware" tab, and upload the firmware.bin file located in the .pio/build/esp32-s3-devkitc-1/ directory of the project.

🔗 📼 Preparing video files

You'll need a FAT32 formatted SD Card, and properly encoded video files (AVI MJPEG). Keep the file names short, and place the files at the root of the SD Card. They will play in alphabetical order.

🔗 Transcoding

You can use this web page to convert video files in the expected format (max. output size 2Gb). It relies on ffmpeg.wasm for purely local, browser based conversion.

For much faster conversion, the ffmpeg command line tool is recommended. This is what the web version does:

ffmpeg -y -i input.mp4 -an -c:v mjpeg -q:v 10 \
-vf "scale=-1:240:flags=lanczos,crop=288:240:(in_w-288)/2:0,fps=25" \
out.avi

In case you arent ffmpeg fluent, here's a breakdown of what this does:

Option/Parameter Purpose
-y Overwrite output without prompt.
-i input.mp4 Input file (use your own file name here).
-an No Audio. Discard the audio stream, we don't need it.
-c:v mjpeg Video codec: Motion JPEG (MJPEG).
-q:v 10 High Quality (Quantizer 10), ranges [1-31], lower value is higher quality.
-vf "..." Video Filter Chain:
scale=-1:240:flags=lanczos Scale to 240px height, auto-width, using Lanczos algorithm.
crop=288:240:(in_w-288)/2:0 Crop to 288x240, horizontally centered. It's 8 pixels wider than the display, but the JPEG decoding library performs faster with multiples of 16.
fps=25 Cap FPS to 25.
out.avi Output file name and container.

🔗 📖 Usage

🔗 Powering

Long press the button (1 second) then release it to turn the Tinytron on and off.

🔗 SD Card Mode

If a FAT32 formatted SD card containing .avi files is detected at boot, the device will automatically start playing them in alphabetical order. You can use the button to control the playback:

In SD Card mode, WiFi is disabled to save battery.

🔗 WiFi Mode

If no SD card is detected, the device enters WiFi mode. It will attempt to connect to the previously configured WiFi network.

🔗 Access Point (AP) Mode

If the device fails to connect to a previously configured WiFi network (or if no network is configured), it will start in Access Point (AP) mode.

🔗 Web Interface

The web interface allows you to:

🔗 Battery & charging

🔗 🫰 Credits and references