Installing CircuitPython on an ESP32 Reverse Feather TFT
(On Linux, natch.)
I've been wanting to play around with CircuitPython for ages. I like Python, I like microcontrollers, what's not to like? Quite a while back, I even ordered a Feather M0 for that — but I didn't do my research, ordered the wi-fi version and it turned out that's the one Feather M0 that can't run CircuitPython.
This time I checked more carefully before ordering, and got a processor that for sure claimed to run CircuitPython.
I had a specific project in mind: a CO2 meter that I can take to meetings and public spaces as a proxy for a COVID virus detector. After a lot of research on the Adafruit website, I decided the ESP32-S3 Reverse TFT Feather looked perfect. It's small, has a built-in display, definitely claims to run CircuitPython, and has built-in buttons and what Adafruit calls a Stemma QT connector (technically a 4 Pin JST SH) so I could connect to the CO2 sensor without even needing solder or test clips. (I'll write about the CO2 sensor project separately.) It also has a battery connector and built-in logic to charge a li-ion battery and report on voltage.
So far, it's a terrific board. But I had some teething pains getting it to run CircuitPython, and the documentation is very incomplete, so that's what this article is for.
Before I ordered, I read all the way through the installation instructions for CircuitPython on the Reverse Feather TFT. Everything looked very straightforward.
Once the Feather arrived, following the instructions, I plugged it in to USB. It showed up as a serial device, ready to be used from the Arduino toolset. Just to make sure, I fired up the Arduino IDE (normally I use the arduino-mk command-line tools for development, but the IDE is an easy way to check that things are working) and verified everything was fine by loading a blink sketch.
Enough of that; onward to CircuitPython. I followed the slightly confusing instructions (are you supposed to double-click once first, and then click twice again? The first double-click didn't seem to make any difference, but I did it just to make sure). After a tap on the reset button (LED turned purple) then a second tap (red, then green), the FTHRS3BOOT drive appeared as it was supposed to. I mounted the drive, then copyied the CP install to it:
ls -l /dev/disk/by-label sudo mount LABEL=FTHRS3BOOT /mnt sudo cp adafruit-circuitpython-adafruit_feather_esp32s3_reverse_tft-en_US-10.0.3.uf2 /mnt sync
Then I waited, periodically re-running that ls -l /dev/disk/by-label
to see when the FTHRS3BOOT drive disappeared and the CIRCUITPY
drive appeared.
Except that it never did. I waited fifteen minutes, running sync
a few more times just in case, and verified with diff that the UF2 file
on the FTHRS3BOOT was the same as the one I copied. But no CIRCUITPY label.
I tried unplugging and starting over several times, with the same result each time.
Trying Open Installer
I searched the Adafruit forums, and found several other people who'd had problems getting CP onto their Reverse TFT Feathers. One person recommended going to the CircuitPython download page for the ESP32-S3 Reverse TFT Feather and using the OPEN INSTALLER button — from chrome because Firefox doesn't support serial ports.
So I tried that, though I used chromium, not chrome. Open Installer gives you several choices; I told it to erase everything, overwrite the bootloader, then install CP. It looked like the re-flash of the bootloader went okay. Then it got to the CP install step and reported
Flashing ...There was a progress bar but it was empty and never filled in. I waited ten minutes, but nothing happened and nothing changed on the mounted FTHRS3BOOT.
IMPORTANT: "the BOOT/DFU button"
Instructions on installing the bootloader tell you:
- Press and hold the BOOT/DFU button down. Don't let go of it yet!
- Press and release the Reset button. You should still have the BOOT/DFU button pressed while you do this.
- Now you can release the BOOT/DFU button.
What they don't tell you anywhere, even on pages specific to the TFT Reverse Feather, is that "the BOOT/DFU button" is the one labeled D0.
So keep that in mind when installing the bootloader.
And you do need to install the bootloader, because the Reverse TFT Feather only has 4Mb of flash, and apparently anything with less than 8Mb ships with a bootloader that can't handle CircuitPython 10. I don't know why they don't just ship with a working bootloader, but they don't.
What finally worked
I tried the Open Installer in Chromium again; but this time, I told it to only flash the bootloader. I held down D0, pressed and released Reset, released D0, then hit Connect on the Open Installer page, and now there was a ttyACM0 option, which I chose. I let it erase the old bootloader, and it installed a new one.
Once that was done, I unplugged the Feather, plugged it
back in, and ran the manual install again, mounting FTHRS3BOOT
and copying the UF2 file to it, then running sync right away.
(OpenInstaller can't do that part unless you run chromium as root or
mount the virtual disk in a way that lets your user write to it;
I mounted it with mount /dev/sda /mnt and then copied the file
with cp adafruit-circuitpython-adafruit_feather_esp32s3_reverse_tft-en_US-10.1.4.uf2 /mnt/.))
And wonder of wonders, a light on the Feather started flashing (which it hadn't done before) and in just a few seconds, the CIRCUITPY drive appeared!
So, in summary, the instructions aren't clear, but the Feather Rev TFT definitely needs a new bootloader, related to the note on that circuitpython.org download page:
Note: CircuitPython 10 and later, on Espressif boards with 4MB flash, requires TinyUF2 0.33.0 or later. The flash partition layout has changed.
Is the ESP32 Reverse Feather TFT an "Espressif board"? That word isn't mentioned on its page, but apparently Espressif is a Chinese company that makes ESP32 chips, so, I guess.
Or maybe there's just a timing issue, and trying it over and over eventually accidentally got it right.
Anyway, if you have trouble installing CircuitPython on your Feather, try installing a new bootloader first, and be aware that you will need that button D0 = BOOT/DFU. And if that doesn't work, I guess, keep trying it over and over.
Once that pain was over, the board worked beautifully. I plugged in the
CO2 sensor and got it working in just a few minutes, and the rest of the
work was user interface and building a case. I love the display and the
other features. I'll probably buy a few more of these boards and I'm
looking forward to working more with them.
But I wish it wasn't so hard to get CircuitPython installed.
[ 13:58 Dec 04, 2025 More hardware | permalink to this entry | ]