In the last issue, I wrote about nostalgia for a time we’ve never known in a place that never existed. In this issue I’d like to explore a world that could’ve never been, through the medium of hardware hacking. This issue is a little different to previous ones, focusing on one subject. I hope you enjoy it.
In this issue I want to show you how this hacker (me) approaches hacking, not blind you with the detail. If you want detail, Github links are provided. If you’re reading this online you can get Tales From The Dork Web in your inbox every fortnight. Click the button below to sign up.
I wrote a guide on how to get the most from The Dork Web in case it gets a bit overwhelming. It might help. That said, it's hacking time and we need hacking music.
What, you were expecting repetitive techno? It’s not the 90s. Just be thankful it’s not Balkan Harmonica Dance. Sebastian Teller’s awesome. Give him a play and read on.
Meet the TTGO T-Watch
I saw the TTGO T-Watch while I was looking into meshtastic. The T-Watch is a watch framework based on the ESP32. It’s not a finished smart watch, just a bunch of kit to build the software around. There are several daughterboards available, including a LoRaWan board. Initially I wanted to try and port meshtastic to the hardware.
As expected, the T-Watch arrived and promptly started gathering dust. It’s not exactly fancy, but I can bend it to my will.
My partner wanted to be more aware of how long she was outside to help manage her own COVID exposure risk. During lockdown our phones stay inside the house. They’re the filthiest objects that get closest to our face. My country has more daily deaths than the rest of Europe combined. We implemented our own COVID risk management controls from the start, so if it seems a little OTT you know why.
Some of you will know what it’s like when a partner asks you to build something hacky for them. Making something for the one I love that also solves a problem she has is the purest hacker expression of love I know.
I decided to make something based on the Commodore Amiga, asking myself what if Commodore hadn’t gone bust in the 90s. What might an 80s style Amiga Workbench look like on a modern smart watch? Nostalgia Nerd’s Amiga history documentary tells the Amiga story as it was, while this episode of the BBC’s Micro Live from 1985 shows you what the Amiga was like to use when it came out.
The TTGO T-Watch consists of two circuit boards in a plastic case. The top board controls the screen and built-in sensors, while the bottom board is for optional add-ons. Different boards have different features. I went for the LoRaWAN board as that was similar to what the Meshnet project used. This was great for meshnet, but not for my partner’s alarm as it has no audio speaker.
The T-Watch library uses the Arduino IDE (Integrated Development Environment), which is basic but usable. I installed the T-Watch library, and there were examples for playing sounds. Unfortunately, LoRaWan daughterboard I chose didn’t have a speaker, which is really needed for alarms.
TTGO are bringing out a better looking watch soon. It looks nicer but lacks the daughterboards. I’ve pre-ordered one anyway. I also ordered daughterboards for the existing watch featuring speakers, that arrived part-way through.
When I look at a new hardware platform for building I typically go through the following process:
Try to learn the basics from examples and libraries
Read the datasheets for specific chips
Swear a lot, sleep very little
Build a rough Proof-Of-Concept that approximates my goals in an ugly way
Learn the underlying technologies
Learn and iterate with completely new implementations till it gets vaguely good
First I tried the basics. The device uses libraries - reusable bits of code - to drive the different on-board features like the screen, step meter etc. Some examples used Bodmer’s eTFT_SPI screen library I’m already familiar with. Others used LVGL, something I’ve never used before.
Normally on a very small embedded system images are converted from standard formats into data stored with the code. This can be painful. Instead I borrowed a routine to load and display a Windows BMP file from internal flash storage. It took a while to get images into the right format for my BMP loader but after a short while an Amiga Kickstart image appeared.
Bodmer’s library has text support, but I didn’t like the built-in fonts. I used a processing sketch to convert Rewtnull’s reimplementation of the classic Amiga Topaz font. I then wrote a quick Arduino sketch based on Bodmer’s font examples write to the screen. Now I had bitmaps and text, I moved on to drawing rectangles and animating them to create a blinking Amiga-style Guru Meditation error message when things went wrong. Things often went wrong so it got a lot of use.
I ported a 3D starfield effect to learn how to manipulate individual pixels, then a palette shifted plasma effect. Finally I ported Tobozo’s raytracer to pull everything together with an animation. It’s quite a thing to see a smart watch being used as a rendering tool.
At this point Marizel asked how it was going and I showed her. Although she found it visually impressive she asked how this helps her tell the time outdoors. I realised that I could probably speed things up to arrange spheres in the scene to tell the time, but getting this to render in a second, let alone a single frame would be difficult, if not impossible. I could pre-render watch elements and animate them on the screen, but short of a juggler animation, this wouldn’t be very Amiga-like.
That night I had other ideas. What if I tried to made the watch play ZX Spectrum games? Aside from it’s ULA chip, the humble 80s ZX Spectrum home computer isn’t hard to emulate. I found an emulator online for another system and with some hacking, managed to get Manic Miner up and some other software just about running.
This changed everything. This watch could have access to the hundreds and thousands of ZX Spectrum apps and games available. Sure, there was no sound output, no controller input and any hopes of getting a usable keyboard interface on a watch-based Spectrum emulator were slim at best, but the potential was there. I had to chop a bit off the sides of the ZX Spectrum’s 256 pixel wide screen to fit the watch’s 240 pixels, but this didn’t matter. I rushed into the bedroom to show and tell Marizel. Groggily, the first thing she said was,
“What time is it?”
I knew it was getting light, the sun was coming up. But the watch was not yet able to tell the time.
“Come to bed Steve. I’ll order one online tomorrow. Don’t worry, love.”
Disheartened and dejected, I went to bed and woke up the following afternoon. Marizel hadn’t yet ordered a watch. There was still time. I convinced her to hold off ordering till Wednesday. As my dear friend Tomasz tells me:
“If a problem’s worth solving it’s worth over-engineering”.
I went back to the drawing board and jotted down minimal watch requirements:
Tell the time
Have a 15 minute countdown
Alert on countdown finish
After that, the secondary elements are going to be:
Some way for the user to cancel the alert
I decided that a visual alert would do until the daughterboards arrived. I looked at the TTGO library code samples and spotted lvlgl, a GUI graphics library. I’d never used before. It looked complicated but I knew I’d need a GUI at some point.
Lvgl uses it’s own font and image processing tools, so I ported earlier fonts and images and mocked up a display background. If I stuck with Bodmer’s library I knew I’d hit a roadblock when I started making GUI elements. I plugged away, and got this:
It looked interesting but was almost entirely static and the code made no sense. I rewrote my code from scratch several times. I built prototypes for specific functionality and integrated them into the watch code. My sleep went down as coding time went up. My back was sore from sitting at the dining table for hours. I was questioning myself and making sloppy mistakes. I went to bed on Monday late. The next morning, I checked in.
The watch face had a clock, day and timer along with a menu bar with status for steps and battery power. Tapping the top-right icons brought up a tile menu where you can slide through different options. There were 3 possible timer options but I hit a snag. The timer only worked while the screen was on.
My daughterboards arrived. I tried the GSM board. It was an incredibly tight fit into the watch and I was worried about the impact on parts inside the case. I put it back together and heard nothing. I took the case apart and immediately saw why. Speakers only work while connected. Tension inside the case pulled them off the board.
I switched to a different daughterboard and got horrific, nonsensical screeching noises. But I had sound! A quick software change and it worked fine. I had the time. I had a timer that worked when the screen was on. I had a full blown GUI framework and multitasking. To test the sound properly, I added an Amiga Modtunes player.
That buzzing noise is the mod player task stalling when tiles are moved. The roaring noise is my room fan. I’ll try to resolve the stalling if I rewrite this. Things were working well, but I had a few snags. On-board timer and power management chips weren’t behaving as expected. I didn’t fully understand how power management worked and it showed. I realised a large part of my problems were down to two issues:
Aggressive power management switching too much off
Doing too much in the GUI side of the watch
The former wasn’t helped by me not really understanding ESP32 power management as I’ve never had to use it before. The latter is an architectural issue worth exploring.
Most programs I write are very small single purpose tools. Everything is normally thrown into a single task, like sending a bunch of data to another program to make it do things it was never meant to do. Because this is a whole Operating System, it needs to facilitate other tasks. When the GUI did something non-GUI, it stopped doing normal GUI things. If the watch screen was off, it stopped doing GUI things. The timer code lived inside the GUI, that’s why it stopped when the screen was turned off.
I started moving logic away from the GUI into the core. This took a huge amount of effort due to the corners I’d cut along the way. Eventually, I started to see progress.
The process of code -> swear -> code -> disbelief -> code -> swear -> code -> joy -> repeat started to bear fruit. I successfully moved the timer logic out of the GUI. I added a new power-management mode so that sound wouldn’t cut off when the screen did. I created a new button field layout to replace the toggles. I added options to the menu bar. I even added speech for the timer countdown. Wanna see my watch?
I didn’t know my video recording settings were so low till afterwards. Still, you can hear what’s going on. As a last minute option I added a Say command, which those who remember Workbench 1.3 might find interesting. It’s just a clunky thing that reuses the Wifi keyboard class. It doesn’t hide the launch button, but the keyboard is so clunky to use, you probably wouldn’t use it more than once anyway.
In total I spent 5 man days over 2 calendar weeks on the watch. I learned a lot about OS design, GUI libraries and all kinds of things that will help for other projects. I’m glad I did it, but now it does what my partner asked for, I’m going to take a break from it before reimplementing it from scratch again. I’m not sure she’ll ever ask me to do anything again. Even so, it was worth every poorly calculated second.
If you want to get your own TTGO T-Watch, I’ll do an updated release when I get my hands on the new 2020 model. I can’t promise to fix all the bugs, nor can I promise not to introduce new ones. I’ll see what time permits once it arrives.
Things You Might’ve Missed
I’ve had to leave out this week’s links to fit everything in about the watch. I’ll do a special links edition next Thursday along with an AMA on Substack.
I want to leave you with the incredible Healah Dancing by Keaton Henson, featuring Ren Ford. It’s a beautiful piece that touches the very depths of the soul.
Out in the real world I know things are pretty scary. Please stay safe and know that this moment too will pass. I’d like to end with a quote from Martin Luther King:
“We must accept finite disappointment, but never lose infinite hope.”
I really hope you liked this issue, I know it’s a bit different to the others. If you know someone who might like Tales From The Dork Web, please share it with them. I’ll be back next week with more links to things you’ve missed and an AMA. If you’re not already subscribed, you can sign up below to get Tales From The Dork Web in your inbox once a fortnight.