CircuitPython-powered Birch Books

This part April, after leaving one bubble and before joining the next, I found myself deciding to work on some basic electronics, to brush up my down-to-hardware skills and learn new stuff. And since I just had assembled the Lego Bookstore (Birch Books), I thought I would improve on that by adding LEDs and controlling them with a programmed microcontroller.

I’ve been back and forth on choosing which MCU to use, settling on an 8051 clone, which was not entirely straightforward to get to work, but eventually I thought I did. Until I got the actual boards to mount it on, and found out that I couldn’t get any new code on my chips, for some reasons I still haven’t figured out. Instead I decided to take a different approach and use a higher-level programming language and higher-level board, too.

Due to the new job having different set of contribution guidelines, I had to wait a bit before making the new boards and code available, but I have described most of the plans for it in the previous blog post. The code is now dropped in the repository. And I also spent some time to tidy up some of the other boards based on the various bits and pieces I learned from the past few months of trials and error.

The CircuitPython-based implementation relies on either the MCP23016 or the MCP23017 GPIO expanders. The trick for that is that the code is looking for the former at address 32 and the latter at address 33. The pull request to support the older, clunkier ’16 expander is approved but hasn’t been merged yet at the time of writing. I briefly considered making yet another alternative board based on the MCP23018 just to add support for it to the Adafruit library, but… I’ll leave that to someone else for now.

And while I used a Feather M4 to have it working at first, I ended up building the boards to the tinier Trinket M0, which turned out not just to be a good fit for the project, but also a good way to make sure the code would work on more boards. Turns out that on the Trinket, there’s no time.time() and I had to switch to monotonic() instead, and that lead to me finding a bug in CircuitPython.

In truth, you can hotwire anything to these boards as long as they have I²C and can talk to the MCP23016/7. And while the connector on them is designed with my actuator board in mind, what it has is pretty much the whole 16 I/O lines coming from the expander. Were you looking for an easy way to connect a Trinket M0 with 16 I/O lines to a breadboard? There you go — you might just have to connect the top header as a male pin header on the bottom of the board. Making an adapter to use this with a Feather would be relatively straightforward.

The (nearly) end result is fairly pleasing:

The version you see there is not the most recent design that you’ll find in the repository, but rather the first prototype of this version of the board that, surprisingly, managed to work the first time — nearly. The actuator board needed some work after I set up the Trinket: at first it kept blinking really fast, because it kept going in and out of test mode (which turns on all the LEDs), and in and out of “fast forward” mode (I’ll get to that in a moment). The reason turned out to be that the inputs needed an explicit pull-down. Which is why the new version of the actuator has three resistors to the side of the buttons. I also learnt that I really should pay attention to the orientation of components on the design, since at first I messed up and pulled up one of the inputs constantly.

Let’s talk a moment about the “fast forward” mode. When I originally came to design the inputs for the project, I decided I would want to have a way to run the whole cycle faster, to make videos out of it without needing to use a time-lapse or something like that. This turned out to be a good idea for testing, as well. But it’s implemented in fundamentally different ways between the 8052 firmware and its CircuitPython counterpart.

In the 8052 version, the time is kept as a number of ticks increased by a timer interrupt. When the fast-forward mode is engaged, instead of adding one tick per interrupt, it adds sixty four. Which is the speed up time of the fast forward. It also means that fast-forward works similar to a fast-forward on an old cassette tape, as it speeds up time while pressed, but it starts and end with normal time.

In the CircuitPython version, the “virtual hour” (as in, which scene we’re at) is wholly dependent on the monotonic time since start up, and the fast forward mode just changes the divisor between real time and virtual time. Which means that pressing fast-forward completely changes the virtual time. Oops. I couldn’t find a decent way around that that would have made the firmware overly complicated, so I pretty much gave up.

On the bright side, since I already have a repository for flame effect code for CircuitPython, it might be a good time for me to include the design for the fireplace, since it should be trivial to do so with the boards as they are right now!

Overall, I’m fairly happy with the result, although it feels less “self-contained” than I originally planned. I was honestly afraid at first that the Trinket M0 would be more power-hungry than the STC89, but it might be the opposite. Indeed I’m now having some issues with at least one one of the LEDs that I put in the bookstore being “burned down” — I fear my current limiting calculation was too tied to the STC89, as noticed when the ground floor LEDs turned out to be much brighter than with the breadboard. I need to make sure I got my numbers right — and if I need to, I have some new, fancier LEDs I can install, although those were rated for 12V, in which case I might need some rethinking of the whole setup.

Now to wrap this up a few words about this whole experience: I have worked on hardware before, but never down to this level. I have worked on firmware for very tiny systems that go into HVAC control planes, and for bigger systems built with COTS components but treated and sold as embedded solutions. But the closest to anything like this for me has been high school, which is over fifteen years ago!

This journey has been a learning experience, and I want to thank the folks over on the Adafruit Discord server. I also welcome any comment and critique on the code or the board designs — the latter particularly because I really have no idea what I was doing, and I had to improvise.

I also want to keep talking about mistakes as I make them, and even wonder out loud when I think I did something wrong, for two reasons: first of all, as I just said, I welcome more critiques of my thought process — if I’m going around something the wrong way, please let me know. The second reason is a bit more “squishy”: while making these mistakes is a sure way to learn more, they are not cheap mistakes. Ordering another set of five prototypes is around £5, but then you need to batch a few and spread over the shipping costs, plus the “bill of materials” tend to add up, even if the single components are just a few cents. I hope I’ll be saving someone else’s money when they look around to “how did someone else do this for this stuff?”

Investigating Chinese Acrylic Lamps

A couple of months ago I built an insulin reminder light, roughly hacking around what I would call an acrylic lamp. The name being a reference to the transparent acrylic (or is it polycarbonate?) shape that you fit on top, and that lights up with the LEDs it’s resting on top. They are totally not a new thing, and even Techmoan looked at them three years ago. The relatively simple board inside looked fairly easy to hack around, and I thought it would make a good hack project to look more into them.

They are also not particularly expensive. You can go on AliExpress and get them for a few bucks each with so many different shape designs. There’s different “bases” to choose from, too — the one I hacked the Pikachu on was a fairly simple design with a translucent base, and no remote control, although the board clearly showed space for a TSOP-style infrared decoder. So I ended up ordering four different variants — although all of them without remotes because that part I didn’t particularly care for: one translucent base, one black base with no special features, one with two-colour shapes and LEDs, one one self-changing LEDs with mains power only.

While waiting for those to turn up, I also found a decent deal on Amazon on four bases without the acrylic shapes on them for about £6 each. I took a punt and ordered them, which turned out to be a better deal than expected.

These bases appear to use the same board design, and the same remote control (although they shipped four remotes, too!), and you can see an image of it on the right. This is pretty much the same logic on the board as the one I hacked for my insulin reminder, although it has slightly different LEDs, which are not common anode in the package, but are still wired in a common-anode configuration.

For both the boards, the schema above is as good a reversing as I managed on my own. I did it on the white board, so there might be some differences in the older green one, particularly in the number of capacitors, but all of that is not important for what I’m getting to right now. I shortened the array to just four LEDs to show, but this goes on for all of the others too. The chip is definitely not a Microchip one, but it fits the pinout, so I kept that one, similarly to what I did for the fake candle. Although in this case there’s no crystal on the board, which suggests this is a different chip.

I kind of expected that all the remaining boards would be variation on the same idea, except for the multi-color one, but I was surprised to figure out that only two of them shared the same board design (but took different approaches as to how to connect the IR decoder — oh yeah, I didn’t select any of the remote-controlled lamps, but two of them came with IR decoderes anyway!)

The first difference is due to the base itself: there’s at least two types of board that relate to where the opening for the microUSB port is in relation to the LEDs: either D-shaped (connector inline with the LEDs) or T shaped (connector perpendicular to the LEDs). Another difference is in the placement of the IR decoder: on most of the bases, it’s at 90° from the plug, but in at least one of them it’s direct opposite.

Speaking of bases, the one that was the most different was the two-colours base: it’s quite smaller in size, and round with a smooth finish, and the board was proper D shaped and… different. While the LEDs were still common-anode and appeared wired together, each appears to have its own paired resistor (or two!), and the board itself is double-sided! That was a surprise! It also is changing the basic design quite a bit more than I expected, including only having one Zener, and powering up the microcontroller directly over 4.5V instead of using a 3V regulator.

It also lacks the transistor configuration that you’d find on the other models, which shouldn’t surprise, given how it needs to drive more than the usual three channels. Which actually had me wonder: how does it drive two sets of RGB LEDs with an 8-pin microcontroller? Theoretically, if you don’t have any inputs at all, you could do it: VDD and VSS take two pins, each set of LEDs take three pins for the three colour channels. But this board is designed to take an IR decoder for a remote control, which is an input, and it comes with a “button” (or rather, a piece of metal you can ground with your finger), which is another input. That means you only have four lines you can toggle!

At first I thought that the answer was to be found on the other six-pin chip on the lift, but turns out that’s not the case. That one is marked 8223LC and that appears to correspond to a “touch controller” Shouding SD8223L and is related to the metal circlet that all of these bases use as input.

Instead, the answer became apparent when using the multimeter in continuity mode: since it provides a tiny bit of current, you can turn on LEDs by pointing them between anode and cathode of the diode. Since the RGB cathode on the single LED package are already marked on the board, that’s also not difficult to do, and while doing that I found their trick: the Blue cathods are common to all 10 LEDs, they are not separate for outer and inner groups, and more interestingly the Green cathodes are shorted to the anodes for the inner four LEDs — that means that only the outer LEDs have the full spectrum of colours available, and the only colour combination that make the two groups independent is Green/Red.

So why am I this interested in these particular lamps? Well, they seem to be a pretty decent candidate to do some “labor of love” hack – as bigclive would call it – with making them “Internet of Things” enabled: there’s enough space to fit an ESP32 inside, and with the right stuff you should be able to create a lamp that is ESPHome compatible — or run MicroPython on it, either to reimplement the insulin reminder logic, or something else entirely!

A size test print of my custom designed PCB.

Indeed, after taking a few measurement, I decided to try my hand at designing a replacement board that fits the most bases I have: a D-shaped board, with the inline microUSB, has just enough space to put an ESP32 module on it, while keeping the components on the same side of the board like in the original models. And while the ESP32 would have enough output lines to control at least the two group of LEDs without cheating, it wouldn’t have enough to address normal RGB LEDs individually… but that doesn’t need to stop a labor of love hack (or an art project): Adafruit NeoPixel are pretty much the same shape and size, and while they are a bit more expensive than the regular RGB LEDs they can be individually addressed easily.

Once I have working designs and code, I’ll be sharing, particularly in the hopes that others can improve on them. I have zero designing skills when it comes to graphics or 3D designing, but if I could, I would probably get to design my own base as well as the board: with the exception of the translucent ones, the bases are otherwise some very bland black cylinders, and they waste most of the space to allow 3×AAA batteries (which I don’t think would last for any amount of time). Instead, a 3D printed base, with hooks to hold it onto a wall (or a door) and a microUSB-charged rechargeable battery, would be a lovely replacement for the original ones. And if we have open design for the board, there’s pretty much no need to order and hope for a compatible base to arrive.

Art Projects Setbacks: Birch Books’ New Brain

You may remember that two months ago I declared my art project completed, and posted pictures of it. I have asid back then that it wasn’t quite all done and dusted, because I have been waiting for PCBs to arrive from PCBway, an online PCB ordering service that has been heavily advertising on YouTube makers’ videos for the past year or so. I ordered those back in April and, now that it’s July, they still haven’t turned up! I decided instead to follow again the advice of bigclive, and tried JLCPCB instead, which turned out to be a very good idea: ordered on Sunday, the boards arrived on Friday!

So on Friday I set myself to spend some of the time listening to training talks, and solder on the boards, which turned out to be a bit less practical than I intended — I might try again to solder microUSB connectors, but then I’ll follow Richard’s advice and try without the shielding in the back. After some screwups, I managed to get working boards, programmed a new STC89, and went on to connect it to the LEGO set — and it all turned up at once, which it wasn’t meant to!

After trying a few combinations of different Darlington arrays, and flashing a new build of the firmware, I ended up figuring that all the micros I flasked anew were completely stuck, with all the I/O ports staying at 5V with an occasional momentary pull down. At first I thought I screwed up the R/C network for the reset line, but no, I managed to check it with the Saleae Logic Pro and the reset line behaves exactly as it should have. I also popped in the original STC I used, before the ones I ordered on AliExpress arrived from China. And it worked fine, if a bit misconfigured (the new firmware has a few fixes). I also tried it and one of the “pristine” ones on the programmer, and they all work fine, but anything I program anew fails the same way.

It’s not a firmware problem either: I tried going back in time and build even the first draft version; I tried demos written by someone else; I tried four different versions of SDCC — all to no avail. Nothing changed in stcgal so it doesn’t sound like it’s a problem there… I’m at a complete loss of what’s going on there. I honestly felt desperate about that, because it worked perfectly fine two months ago, and now it suddenly stopped.

So instead, I spent Saturday working on my plan B: using CircuitPython for the main scenes logic, with an Adafruit board — in particular I decided to start working with the Feather M0, but I’m targeting the Trinket M0, which is cheaper, smaller, and still plenty powerful for what I need. This was also fun because, since I designed the actuator board to be separate from the MCU and in particular to be possible to plug it into a breadboard, which means that half of the design was already known working and didn’t need redesign.

Unfortunately what I didn’t think of was to produce a test board that would just plug into the pins on the actuator board to test it, without connecting it to the bookstore… so I ended up having to manually wire a lot of LEDs on the breadboard to turn them on. I’m going to use this as a visible example of why you should always build a fake of your service if you’re writing a client, to run integration testing with! I am addressing this by ordering an explicit testing board to connect on top of the actuator, so that I can just use it. Also fun fact: it looks like the LEDs that I have used in this pictures are… more sensible than the other ones, and Sparky the Blue Smoke Monster came to visit me again when I gave it a straight 5V.

There’s more jankiness going around with this board, though. When I was looking at expanding the I/O capabilities of the Feather, I ended up buying a few MCP23016 expanders. These are I²C chips that provide access to 16 input or output lines while only requiring two wires on the MCU. I can’t for the life of me remember or figure out why I went with this particular model, that currently sports a «Not Recommended for new designs.» warning on the top of the Microchip product page. I might as well have mistyped the MCP23017, which is the modern version of the same chip.

Beside not being (at all) pin compatible, the documentation on Adafruit’s website is designed for the later (’17) variant, and indeed the older (’16) version is not currently supported by CircuitPython (although I’m addressing this). It doesn’t stop there: 16 requires an additional R/C network, which turned out to be very unreliable: it ended up working better with a single resistor on the CLK line, and I’m not at all sure on why. So in general, the difference between MCP23016 and MCP23017 is that the latter is much nicer to use. Since I do have a few 16, and the components needed for the RC network, I’ve started writing the CircuitPython code based on that first, but also designed a separate board to fit the ’17, using a non-default address, so that I can distinguish between the two at startup.

Part of the reason for that is also that the ’16 is very finnicky, and when it receives a command it doesn’t entirely like, it decides to simply crash, and requires a full power cycle (not just a reset), which isn’t very good.

There’s another jankiness that you can possibly spot on the right side of the board: why is there a transistor, in this fairly minimalistic design? I mean okay, the pull-up resistors for I²C lines, and the RC network already ruined the idea of having the daughterboard and the expander only. But a transistor? Well, it turns out when I designed the actuator board I made a silly mistake: I didn’t consider that reset lines (RST) are not always active high (as they are on the 8051), but are sometimes actually ¬RST — and as you can imagine now, both the Feathers and the Trinket M0 are indeed active low.

And since pushing the Reset button is bridging the RST line of the actuator board to VIO (hey, at least I did realise that if I went to a different platform I would need a different “logic high”!), it wouldn’t work at all. Instead, it now activates the transistor, so that it bridges the RST line to ground. Basically, a NOT gate implemented with a single transistor — which I might have remembered thanks to Richard Feyman Lectures on Computation, which is a book I actually would recommend for those interested.

And if you’re wondering why the I/O lines are all orange, I checked which equipment wire roll I had the most of, turned out to be orange.

Also, a word of advise that I found out by complete random chance as well: the default for “quote dimensions” in Eagle is to put them in as traces at the top of the board! When I sent the generated gerber files to JLCPCB this time, they noted some dead shorts and asked me to confirm if that was intentional — so kudos to them for noticing. They allowed me to upload new, fixed gerber files after that.

The Importance of Project Boxes

Given my background, you may think I’m referring to servers — after all I ran the tinderbox for a long while. But no, in this case I’m referring to actual, physical boxes. And a few virtual ones as well.

See, when I listed my electronics projects, it probably sounded like my house is now top-to-bottom filled with random electronics stuff. This might have been the case if I was doing that in Dublin, but nowadays I’m not alone, and while my wife accepts the need for my random projects, making a total mess of the apartment is probably not a great idea. Indeed the “study” (that is, the second bedroom turned into a home office) is not quite sorted out, but it’s also not a total mess right now (you can see it in the test Live whiteboarding).

The way I’m trying to keep all of this together, is through a significant number of boxes. I already bought a number of LEGO boxes back in Dublin — the reason for the choice was that those boxes are particularly designed to be used by kids, and that made them particularly sturdy. Unfortunately they are also not particularly big. That made them not particularly useful for storing more bulky items, such as my multimeter, the dremel or most of my screwdrivers’ set. I did bring a toolbox with me from Italy – which includes not one but two hammers, more screwdrivers, one older soldering iron, and other similar tools – but this ended up being the kind of toolbox where tools end up to die, because it’s just so uncomfortable to use.

Instead I now bought bigger, bulkier boxes — using one to store the tools, and another one to store my “electronics” stuff, most of which is stored in tiny boxes inside — so that I have a box of transistors, two of resistors, a couple of Dupont connectors, and so on. This makes it easier to sort through what I need at any one time. I considered using bigger boxes with more compartments, but it turns out that most of the time I only need three or four smaller boxes, and sorting between smaller boxes is easier. I have unfortunately not found a very good box to hold the soldering kit – TS-100 and so on – but I might end up looking for something with foam inserts if I ever figure out how to properly design those, after all.

The LEGO boxes are now (mostly) project boxes: one of them has all of the glucometers that I’ve either already reversed or plan to reversing soon; another one has the R205 boards that I never made much progress on, and so on. The fact that I have so many half-complete projects is indeed disheartening when I think of it. Unfortunately there’s reason for that: either lack of skills on my part, the lack of time for myself, or the lack of tooling at the right time. I can for instance tell you that I definitely would have a much easier time nowadays trying to solder those those pins, not just because I improved my soldering skills, but also because I now have tools that I didn’t even know existed back then, including the Dupont crimper.

And this is where the project boxes come into play. Being able to put everything for one particular project into a box and not worry about it for a while is a liberating experience. I had to literally do that when I started my new job — it’s not that difficult passing from one Big Tech company to another, but it is time consuming, and after spending nearly eight straight hours in front of a monitor watching videos and/or reading tutorials, the last thing I wanted was to spend some more time in front of the computer… having all the half-projects in front of me was tough, and I preferred shoving everything into a project box, and going back to it a week or so later — I have indeed gone back to one of my projects already and will likely have a blog post about it later.

But as I said, there’s also “metaphorical” project boxes — that work for me both at work and at home: there are times when working on the same project for months at a time is not feasible. It may be that what was once a priority isn’t anymore, or a higher priority comes out. Or maybe you’re just too burnt out to work on one thing, and decided that it’s time to work on something else for a while. Having the ability to set aside a project, and come back to it later, having state and notes about what something was about is something important. For most of my opensource projects, I leave my own notes in the blog, which is why there’s so many posts of old projects that have not gone anywhere. For work projects, I try to leave design docs or notes, and for other stuff, I started having proper journals dedicated to the ideas. That’s how Birch Books started as well.

The main advantage I found for doing this at work is that it allows collecting rakes without losing track of the main objective of your job. Many of the examples I gave in the previous blog post on the topic have been months-long efforts. Some of those took two years to close the reported issues! And in at least one case, the work had to literally be halted, and wait for two quarters, because one of the blockers could only be solved by deleting the whole source of a service that was being replaced.

So what I’m advocating about is the ability to keep things around, but out of sight. It’s the reason why I can’t stand certain policies in projects (at work and FLOSS alike) that pretty much disallow “long-standing bugs”: sometimes you want to keep all the information you have on a project together, and a bug/issue/task that has been opened for a number of years might just be the right way — closing it for the sake of an OKR target is unlikely to do anyone a good service.

And also allowing ourselves (all of us), to box up projects — whether it is for a while or forever. Being reminded of the stuff you have not completed is not going to make you feel any better, in my experience. Being able to say “Yeah it’s in the box, one day I might or might not get to it” is a way to breathe in, and give yourself space.

Fake candles, and flame algorithms

The Birch Books LEGO set that I have been modifying has an interesting “fireplace” at the first floor of the townhouse. I have been wanting to wire that up to light up for the evening scenes in my smart lighting board, but I want it to look at least a bit realistic. But how do you do that?

As I previously noted, there’s flame effect LED lamps out there, which were looked at by both bigclive and Adam Savage, this very year. But those are way too big for the scale we’re talking about here. Simplifying this too much, you can think of those lamps as round, monochrome LED panels showing a flame animation, like an old DOS demo. Instead what I have to work with is going to be at most two LEDs — or at least two independent channels for the LEDs.

Thankfully, I didn’t have to look far to find something to learn from. A few months ago a friend of my wife gave us as a present a very cute candle holder, but since we’re renting, that’s not really a good idea. Instead I turned on Amazon (but AliExpress would have done the trick just as well) for some fake candles (LED Candles) that would do the trick. These are interesting because they are not just shaped like a candle, but they have a flickering light like one as well. Three of them in the holder fit fairly nicely and did the trick to give a bit of an atmosphere to our bedroom.

I was full ready to sacrifice one of the candles to reverse engineer it, but the base comes off non-destructively, and that the board inside is very easy to follow. Indeed, you can see the schematic of the board here on the right (I omitted the on/off switch for clarity), even though the board itself has space for more components. The whole of the candle is controlled by a microcontroller, with a PIC12F-compatible pinout (but as Hector pointed out, much more likely to be some random chinese microcontroller instead).

It’s interesting to note that the LED starts in “candle” mode once turning the switch to the “on” position, without using the remote control. My guess is that if you buy one of the versions that does not come with a remote control, you can add that functionality by just soldering a TSOP381x decoder. It also shows why the battery on these things don’t really last as long as you may want it to, despite using the bigger, rarer and more expensive CR2450 batteries. The microcontroller is powered up all the time, waiting to decode some signal from the remote control, even if the LED is off. I wanted to record the current flowing through in standby, but it’s fairly hard to get the battery in series with the multimeter — maybe I should invest on a bench supply for this kind of work.

So how does this all work? The LED turns out to be a perfectly normal warm white LED, with a domed form factor that fits nicely in the carved space in the fake candle itself, and that helps it diffuse it. To produce the flame effect, the microcontroller uses PWM (pulse-width modulation) — which is a pretty common way to modulate intensity of LEDs, and the way most RGB LEDs work to produce combined colours, just like on my insulin reminder. Varying the duty cycle (the ratio between “high” and “low” of the digital line) allows changing the intensity of the light (or of the specific colour for RGB ones). If you keep varying the duty cycle, you get a varying intensity that simulates a flame.

The screenshot you can see is from Saleae Logic software. It shows the variable duty cycle in span of a few seconds, and it’s pretty much unreadable. It’s possible that I can write code for a decoder in the Saleae logic, and export the actual waveform it uses to simulate the flickering of a flame — but honestly that sounds a lot of unjustified work: there’s not really “one” true flame algorithm, as long as the flickering looks the part, it’s going to be fine.

Example of a generated Perlin noise waveform for the LED flickering

So, how do you generate the right waveform? Well, I had a very vague idea of how when I started, but thanks to the awesome people in the Adafruit Discord (shout out to IoTPanic and OrangeworksDesign!) I found quite a bit of information to go by — while there are more “proper” way to simulate a fire, Perlin noise is a very good starting point for it. And what do you know? There’s a Python package for it which happens to be maintained by a friend of mine!

Now there’s a bit of an issue on how to properly output the waveform in PWM — in particular its frequency and resolution. I pretty much just thrown something at the wall, it works, and I’ll refine it later if needed, but the result is acceptable enough for what I have in mind, at least when it comes to just the waveform simulation.

The code I thrown at the wall for this is only going to be able to do the flickering. It doesn’t allow for much control and pretty much expects full control of the execution — almost the same as in the microcontroller of the original board, that literally turns off the moment the IR decoder receives (or thinks it’s receiving) a signal.

I was originally planning to implement this on the Adafruit M4 Express with PWMAudioOut — it’s not audio per-se, but it’s pretty much the same thing. But unfortunately it looks like the module needed for this is not actually built into the specific version of CircuitPython for that Feather. But now we’re in the business of productionizing code, rather than figuring out how to implement it.

Upcoming electronics projects (and posts)

Because of a strange alignment between my decision to leave Google to find a new challenge, and the pandemic causing a lockdown of most countries (including the UK, where I live), you might have noticed more activity on this blog. Indeed for the past two months I maintained an almost perfect record of three posts a week, up from the occasional post I have written in the past few years. In part this was achieved by sticking to a “programme schedule” — I started posted on Mondays about my art project – which then expanded into the insulin reminder – then on Thursday I had a rotating tech post, finishing the week up with sARTSurdays.

This week it’s a bit disruptive because while I do have topics to fill in the Monday schedule, they start being a bit more scatterbrained, so I want to give a bit of a regroup, and gauge what’s the interest around them in the first place. As a starting point, the topic for Mondays is likely going to stay electronics — to follow up from the 8051 usage on the Birch Books, and the Feather notification light.

As I have previously suggested on Twitter, I plan on controlling my Kodi HTPC with a vintage, late ’80s Sony SVHS remote control. Just for the craic, because I picked it up out of nostalgia, when I went to Weird Stuff a few years ago — I’m sad it’s closed now, but thankful to Mike for having brought me there the first time. The original intention was to figure out how the complicated VCR recording timer configuration worked ­— but not unexpectedly the LCD panel is not working right and that might not be feasible. I might have to do a bit more work and open it up, and that probably will be a blog post by itself.

Speaking of Sony, remotes and electronics — I’m also trying to get something else to work. I have a Sony TV connected to an HDMI switcher, and sometimes it get stuck with the ARC not initializing properly. Fixing it is relatively straightforward (just disable and re-enable the ARC) but it takes a few remote control button presses… so I’m actually trying to use an Adafruit Feather to transmit the right sequence of infrared commands as a macro to fix that. Which is why I started working on pysirc. There’s a bit more than that to be quite honest, as I would like to have a single-click selection of inputs with multiple switchers, but again that’s going to be a post by itself.

Then there’s some trimming work for the Birch Books art project. The PCBs are not here yet, so I have no idea if I have to respin them yet. If so, expects a mistakes-and-lessons post about it. I also will likely spend some more time figuring out how to make the board design more “proper” if possible. I also still want to sit down and see how I can get the same actuator board to work with the Feather M0 — because I’ll be honest and say that CircuitPython is much more enjoyable to work with than nearly-C as received by SDCC.

Also, while the actuator board supports it, I have currently left off turning on the fireplace lights for Birch Books. I’m of two minds about this — I know there are some flame effect single-LEDs out there, but they don’t appear to be easy to procure. Both bigclive and Adam Savage have shown flame-effect LED bulbs but they don’t really work in the small scale.

There are cheap fake-candle LED lamps out there – I saw them the first time in Italy at the one local pub that I enjoy going to (they serve so many varieties of tea!), and I actually have a few of them at home – but how they work is by using PWM on a normal LED (usually a warm light one). So what I’m planning on doing is diving into how those candles do that, and see if I can replicate the same feat on either the 8051 or the Feather.

I don’t know when the ESP32 boards I ordered will arrive, but probably will spend some time playing with those and talking about it then. It would be nice to have an easy way to “swap out the brains” of my various projects, and compare how to do things between them.

And I’m sure that, given the direction this is going, I’ll have enough stuff to keep myself entertained outside of work for the remaining of the lockdown.

Oh, before I forget — turns out that I’m now hanging out on Discord. Adafruit has a server, which seems to be a very easygoing and welcoming way to interact with the CircuitPython development team, as well as discussing options and showing off. If you happen to know of welcoming and interesting Discord servers I might be interested in, feel free to let me know.

I have not forgotten about the various glucometers I acquired in the past few months and that I still have not reversed. There will be more posts about glucometers, but for those I’m using the Thursday slot, as I have not once gone down to physically tapping into them yet. So unless my other electronics projects starve out that’s going to continue that way.

Insulin, routine, lockdown, and electronics

As you may know if you read this blog, I have insulin-dependent diabetes. In particular, I use both fast and long acting insulin, which basically means I need to take a shot of insulin every morning (at around the same time, but there’s at least a bit of leeway around it).

This is not usually a problem: the routine of waking up, getting ready to leave, making a coffee and either drinking it or taking it with me makes it very hard to miss the step “taking the insulin”. Unfortunately, like for many others, this routine is gone out of the window due to the current lockdown. Maybe a bit worse for me since I’m currently still between jobs, which means I don’t even have the routine of logging in to work form home, and of meetings.

What this meant, is that days blurred together, and I started wondering if I remembered to take my insulin in the morning. A few too many times that answer was “I don’t know”, and I think at least twice in the past couple of weeks I did indeed forget. I needed something to make it easier to remember and not to forget.

Because insulin injections tend to be one of those things that I do “in autopilot”, I needed something hard to forget to do. Theoretically, the Libre App allows annotating that you took long-acting insulin (and how much) but that requires me to remember to scan my sensor right after and write down that I did. It’s too easy to forget. I also wanted something that would be a lot more explicit about telling me (and my wife) that I forgot to tell my insulin. And hopefully something that I wouldn’t risk telling I took my insulin too soon in the interaction, and then not actually doing the right thing (as sometimes I reach out for my insulin pen, realise there’s not enough insulin there, and need to pick up a new one from the fridge).

The Trigger: Yes, I Took My Insulin

The first thing I decided to do was to use the spare Flic Button. I bought Flics last year upon suggestion of Luke, and they actually helped immensely — we have one in the study and one in the bedroom, to be able to turn on the smart lights quietly (in the night) and without bothering with the phone apps. We kept a third one “spare”, not quite sure what to use it for until now. The button fits on the back of the cabinet where I keep my “in use” insulin pen. And indeed, it’s extremely easy and obvious to reach while putting the pen down — as an aside, most European insulin pens fit perfectly on a Muji 3-tier slanted display, which is what I’ve been using to keep mine in.

This is not exactly the perfect trigger. The perfect trigger wouldn’t require an action outside of the measured action — so in a perfect world, I would be building something that triggers when I throw an used needle into the needle container. But since that’s a complex project for which I have no obvious solution, I’ll ignore that. I have a trigger, it doesn’t risk getting too much in my way. It should be fine.

But what should the trigger do? The first idea I had was to use IFTTT to create a Google Calendar event when I pressed the button. It wouldn’t be great for notifying if I forgot the insulin, but it would at least allow me to keep track of it. But then I had another idea. I had a spare Adafruit Feather M4 Express, including an AirLift FeatherWing coprocessor for WiFi. I originally bought it to fix an issue with my TV (which I still have not fixed), and considered using it on my art project, but it also has a big bright RGB LED on it (an AdaFruit NeoPixel), which I thought I would use for notifications.

A quick Flask app later, and I had something working — the Flic button would hit one endpoint on the web app, which would record me having taking my insulin, while the Feather would be requesting another endpoint to know how to reconfigure the LED. The webapp would have the logic to turn the LED either red or quiescent depending on whether I got my insulin for the day. There’s a bit of logic in there to define “the day”, as I don’t need the notification at 1am if I have not gone to bed yet (I did say that my routine is messed up didn’t I?) but that’s all minor stuff.

The code for the webapp (Python, Flask) and the Feather (CircuitPython) I pushed to GitHub immediately (because I can), but there’s no documentation for it yet. It also doesn’t use the NeoPixel anymore, which I’ll explain in a moment, so you may not really be able to use it out of the box as it is.

For placement, I involved my wife — I want her to be able to tell whether I didn’t take my insulin, so if I’m having a “spaced out day”, she can remind me. We settled for putting it in the kitchen, close to the kettle, so that it’s clearly visible when making coffee — something we do regularly early in the morning. It worked out well, since we already had an USB power supply in the kitchen, for the electric cheese grater I converted.

Limitations of the Feather Platform.

The Feather platform by itself turned out to be a crummy notification platform. Don’t get me wrong, the ease of using it is extremely nice. I wrote the CircuitPython logic in less than an hour, and that made it very nice. But if you need a clear LED to tell you whether something was done or not, you can’t just rely on the Feather. Or at least not on the Feather M4 Express. Yes it comes with a NeoPixel, but it also comes with two bright, surface-mount LEDs by the sides of the USB connector.

One of the two LEDs is yellow, and connected to the optional LiPo battery charging circuitry, and according to the documentation it’s expected to “flicker at times” — as it turns out, it seems to be continuously flickering for me, to the point at first I thought it was actually the RX/TX notification on the serial port. There’s also a red LED which I thought was just the “power” LED — but that is actually controlled by a GPIO on the board, except it’s pulled high (so turned on) when using the AirLift Wing.

The battery charging LED does appear to behave as documented (only at times flickering) on the M0 Express I ended up getting in addition to the M4. But since that is not compatible with the AirLift (at least using CircuitPython), it might just be that this is also a side-effect of using the AirLift.

Why am I bringing up these LEDs? Well, if you want a notification light that’s either red-or-off, having a bright always-on red LED is a bad idea. Indeed, a day after setting it up this way, my wife asked me if I took my insulin, because she saw the red light in the corner. Yeah it’s too bright — and easy to confuse for the one that you want to check out for.

My first reaction was to desolder the two LEDs — but I have hardly ever desoldered SMD components, and I seem to have fallen for the rookie mistake of trying to desolder them with a solder iron rather than a hot air gun, and actually destroyed the microUSB connector. Oops. A quick order from Mouser meant I had to wait a few days to go back playing with the Feather.

A Better, Funnier Light

This turned out to be a blessing in disguise as it forced me to take a few steps back and figure out how to make it less likely to confuse the LEDs, beside trying to glue them down with some opaque glue. So instead, I figured out that there’s plenty of RGB LED lamps out there — so why not using those? I ordered a cheap Pikachu from Amazon, which delivered about at the same time as Mouser. I knew it was probably coming from AliExpress (and, spoilers, it pretty much did — only the cardboard looked like printed for the UK market by a domestic company), but ordering it at the source would have taken too long.

The board inside turned out to be fairly well organised, and it was actually much easier to tap into it than expected — except for me forgetting how transistors work, and bridging to the wrong side of the resistor to try turning on the LEDs manually, thus shorting and burning them. I ended up having to pretty much reimplement the same transistor setup outside of the board, but if you do it carefully, you only need three GPIO lines to control the lamp.

The PCB from the RGB LED light I bought. Very well organised. If you want to pair it to a MCU to control it, remove the crossed out IC, then tap into the blue-marked connections. Pay attention to the side of the resistor you connect this on!

The LED colour can be varied by PWM, which is fairly easy to do with CircuitPython. You only need to be careful with which GPIO lines you use for it. I used “random” lines when testing on the breadboard, but then wanted to tidy it up using lines 10, 11, and 12 on the finalized board — turns out that line 10 does not appear to have timer capabilities, so you can’t actually use it for PWM. And also, lines 11 and 12 are needed for the AirLift — which means the only lines I could use for this were 5, 6 and 9.

At this point, I had to change the webapp so that instead of turning the LED off to signify I took my insulin, it would instead turn the LEDs yellow, to have a bright and happy Pikachu on the kitchen counter. And an angry red one in the morning until I take my insulin.

Of course to be able to put the lamp in the kitchen next to the kettle, I had to make sure it wouldn’t be just a bunch of boards with cables going back and forth. So first of all, I ended up wiring together a Feather Doubler — which allows a feather (and a wing) to sit side-by-side. The doubler has prototype areas in-between the connectors, which were enough to solder in the three transistors and three resistors — you won’t need those if you don’t burn out the original transistors either!

Unfortunately, because I had stacking headers on my AirLift, even with the cover off, the lamp wouldn’t sit straight. But my wife got the idea of turning the cover inside out, using the space provided by the battery compartment, which worked actually fairly good (it requires some fiddling to make it stable, and I was out of Sugru glue to build a base for it, but for now it’ll work).

Following Up: Alternative Designs and Trimmings

So now I have a working notification light. It works, it turns red in the morning, and turns back yellow after I click the button to signal I took my insulin. It needs a webapp running – and I have been running this on my desktop for now – but that’s good enough for me.

Any improvement from here is pretty much trimming, and trying something new. I might end up getting myself another AirLift and solder the simpler headers on it, to make the profile of the sandwiched board smaller. Or if I am to remake this from an original lamp with working transistors, I could just avoid the problem of the doubler — I would only need GPIO wirings, and could use the prototyping space next to the M4 Express to provide the connection.

I did order a few more lamps in different styles from AliExpress — they will take their due time to show up, and that’s okay. I’ll probably play with them a bit more. I ordered one that (probably) does not have RGB LEDs — it might be interesting to design a “gut replacement” board, that just brings in new LEDs altogether. I ordered one that has “two-colour” images, which likely just means it has two sets of LEDs. I’ll be curious to see how those look like.

I also ordered some ESP32-based “devkits” from AliExpress — this is the same CPU used in the AirLift wing as a WiFi co-processor only, but it’s generally powerful enough that it would be able to run the whole thing off a single processor. This might not be as easy as it sounds to prototype, particularly given that I’m not sure I can just provide the 5V coming from the lamp’s connector to the ESP board (ESP32 uses 3.3V), and I burnt the 3.3V regulator on the lamp’s original board. Also since I need the transistor assembly, I would have to at least get a prototype board to solder everything on and — well, it might just not work out. Still nice to have them in a drawer though.

While I don’t have a 3D printer, and I’m not personally interested in having one at home, and I’m also not going into an office, which may or may not have one (old dayjob did, new dayjob I’m not sure), I might also give a try to software to design a “replacement base” that can fit the Feather, and screw into the rest of the lamp that is already there. It might also be a starting point for designing a version that works with the ESP32 — for that one, you would need the microUSB port from the USB module rather than the one present in the lamp, to go through the on-board regulator. This one is just for the craic, as my Irish friends would say, as I don’t expect that to be needed any time soon.

All in all, I’m happy with what I ended up with. The lamp is cute, and doesn’t feel out of place. It does not need to broadcast to anyone but me and my wife what the situation is. And it turns out to be almost entirely based on Python code that I just released under MIT.

My new friend who reminds me to look after myself!

Art Project Completion: Lighted Up Birch Books

The first Birch Books scene

I started talking about my quarantine (or, well, lockdown) art project at the end of March, with a plan. I did manage to (mostly) complete it by the end of April, while the lockdown (and my sabbatical) are still in full swing.

I have shared some work in progress pictures over on Twitter, including a 2-minutes video (shot on my phone by hand, so not very high-quality) showing the full set of scenes (before the latest “firmware revision”). As you can see from all those pictures, the cables are still pretty much in the way and very visible. That’s not something I have addressed yet, but I’m waiting for the PCBs to arrive before doing that.

At the end, the result is that throughout the building there are 12 bright white LEDs, connected via Dupont connectors to a harness that then goes into the breadboard — that’s so that the harness can disappear the moment I have the PCBs.

For the PCBs, I think I made a rookie mistake. I ordered them from PCBWay, as I heard good about them on the tubes, and they seemed to have a good price point even for assembly of the SMD versions. That was not the mistake. The mistake was to send a single order with both the SMT and the through-hole PCBs, to save on shipping. I’m now having to wait until the end of this month to receive the boards, and hope I didn’t make any more mistakes on them. Edit 2020-07-01: nearly three months later, the PCBs have not turned up. I ended up ordering a new batch from JLCPCB (not an affiliate link) that arrived within a week. Beware of YouTubers being paid for it.

I’m also sure that there will be changes I’ll need to make. These are literally the first printed circuit boards that I design, and beside obvious stylistic mistakes (such as not marking the value of components), I’m sure there’s more mistakes than that. So I expect I’ll have to update the designs later on. Hopefully it won’t get a mess of bodge wires when I try to use it properly.

I think it might be dinner time.

I have to figure out how to fit all of this into a box, eventually. Probably a Lego box of some kind. And I’ll probably have to make sure that the USB plug is fit properly. Soldering a microUSB connector on a PCB is… not easy and not something I’m looking forward to. That’s why I added a 5-pin header on the design, so I can use one of the cheap breakout boards you can find all over on AliExpress or eBay.

The 8051 based firmware is available on the same GitHub repository, and it includes a full 16-scenes (although not unique scene) schedule that now runs in about one minute.

I also changed the “boot test” mode in the second “firmware revision”: instead of trying to turn on each LED one by one, it turns on each room one by one. The reason why I did that, is that I have hooked up multiple Darlington gates through a joint to provide more current for a couple of rooms that have three LEDs on the same room. This might have been a bit of a mistake and I should probably rewire those rooms to just use multiple LEDs, but that’s not going to happen any day now.

Also, with a bit of a hindsight, I should probably have just left the current-limiting resistors off the actuator board altogether. While the resistor networks are great to simplify the design, I could have just as easily soldered the resistors inline on the cable and wrapped them with heatshrink. I know because I did exactly that on another Lego set, when I realised that powering it directly from a 5V supply was significantly overheating one of the two LEDs.

This is, by the way, another “trick” learnt while watching bigclive on YouTube. Probably not something that would be worth a complete video on, and maybe something that felt fairly natural and not required to explain to him but… something I’m very happy to have learnt. Which is why I love rambling on about small choices in my thought process, as it’s very possible that something that is natural and obvious to me, is not for someone else reading me. And why I do welcome comments asking for clarification on points I may be glossing over.

Also, both my wife and my mother have been pushing me to figure out what the next Lego model I will be adding lights to is going to be. I don’t honestly know, right now. But I guess the lessons learnt with this model should allow me to step up to something a bit more complicated next time. For sure, it would be a cheaper and easier job, since I procured a lot of tools for it anyway.

I think that the things I will consider changing for next time are not just the place the current-limiting resistors are connected (which I can probably retro-fit on the current PCBs), but also the type of wiring I use for the LEDs. I used equipment wire for this build, and it turns out to be fairly stiff and unwieldy. If I were to re-do this, or do it to another model, I would probably use the equipment wire only within the rooms themselves (that way I can push it out of the way), and then solder it just outside the model with a stranded, softer wire.

I think this will probably be the end of the regularly scheduled updates on my art project, at least until the PCBs arrive. I did intend to draw some documentation with a tool like Fritzing, which is used by Adafruit for their documentation, but I’m having quite a few headaches even figuring out how to set it up. None of the components I need are part of their parts libraries, and the documentation is out of date regarding the parts editor, so don’t hold your breath.

Birch Books: 8051 and Yak Shaving

I have previously discussed my choice of splitting the actuator board, pointing out I’ll probably try designing an alternative controller board using something like the Adafruit Feather M4 and writing the firmware with CircuitPython. Part of the reason for that is that it’s just easier, but part of it is because 8051 is an annoying platform to work with.

There are a few different compilers for this platform, but as far as I know, the only open-source and maintained one is SDCC the Small Device C Compiler. I hadn’t used this in forever, but I was very happy to see a new release this year, including C2X work in progress, and C11 (mostly) supported, so I was in high spirits when I started working on this.

A Worrying Demonstration

I started from a demo that was supposed to be written explicitly for the STC89. The first thing I noted was that the code does not actually match the documentation in the same page, it references a _sdcc_external_startup() function that is not actually defined. On the other hand it does not seem to be required. There’s other issues with the code, and for something that is designed to work with the STC89, it seems to be overly complicated. Let me try to dissect the problems.

First of all, the source code is manually declaring the “Special Feature Registers” (SFR) for the device. In this case I don’t really understand the point, since all of the declared registers are part of the base 8051 architecture, and would be already declared by any of the model-specific header files that SDCC provides. While the STC89 does have a number of registers that are not found otherwise, none of those are used here. In my code I ended up importing at89x52.h, which is meant for the Atmel (now Microchip) AT89 series, which is the closest header I found for the STC89. I have since filed a patch with a header written based on other headers and the datasheet.

Side note: the datasheet is impressive in the matter of detail. It includes everything you may want to know, including the full ISA description, and a number of example cases.

Once you have the proper definition of headers, you can also avoid a lot of binary flag logic — the most important registers on the 8051 chips are bit-addressable, and so you don’t need to remember how many bits you need to shift around for you to set the correct flag to enable interrupts. And while you may be worrying that using the bit-addressed register would be slower: no, as long as you’re changing fewer than three bits on a register at a time, setting them with the bit-addressed variant is the same or faster. In the case of this demo, the original code uses two orl instructions, each taking 2 cycles, to set three bits total — using the setb instruction, it’s only going to take 3 cycles.

Once you use the correct header (either my contributed stc89c51rc.h, the at89x52.h, or even the very generic 8052.h), you have access to other older-than-thirty-years features that weren’t part of the original 8051, but were part of the subsequent 8052, which both the STC89 and AT89 series derive off. One of these features, as even Wikipedia knows, is a third 16-bit timer. This is important to the demo, since it’s effectively just an example of setting up a timer to “[set] up and using an accurate timer”.

Indeed, the code is fairly complicated, as it configures the timer both in main() and in the interrupt handler clockinc(). The reason for that is that Timer 0 is configured in “Mode 0”: the timer register is configured as 13-bit (with the word TH0, TL0), its rollover causes an interrupt, but you need to reload the timer afterwards. The reason for that is that you need more than 8 bit to set the timer to fire at 1kHz (once every millisecond), and while Timer 0 supports “automatic reload”, it only supports 8-bit reload values — since it’s using TH0 for the reload value.

8052 derivative support a third timer (Timer 2), which is 16-bit, rather than 8- or 13-bit. And it supports auto-reload at 16-bit through RCAP2H, RCAP2L. The only other complication is that unlike Timer 0 and Timer 1, you need to manually “disarm” the interrupt flag (TF2), but that’s still a lot less code.

I found the right way to solve this problem on Google Books, on a book that does not appear to have an ebook edition, and that does not seem to be in print at all. The end result is the following, modified demo.

// Source code under CC0 1.0
#include <stdbool.h>
#include <mcs51/8052.h>

volatile unsigned long int clocktime;
volatile bool clockupdate;

void clockinc(void) __interrupt(5)
{
        TF2 = 0;  // disarm interrupt flag.
	clocktime++;
	clockupdate = true;
}

unsigned long int clock(void)
{
	unsigned long int ctmp;

	do
	{
		clockupdate = false;
		ctmp = clocktime;
	} while (clockupdate);
	
	return(ctmp);
}

void main(void)
{
	// Configure timer for 11.0592 Mhz default SYSCLK
	// 1000 ticks per second
	TH2 = (65536 - 922) >> 8;
	TL2 = (65536 - 922) & 0xFF;
        RCAP2H = (65536 - 922) >> 8;
        RCAP2L = (65536 - 922) & 0xFF;
	
        TF2 = 0;
        ET2 = 1;
        EA = 1;
        TR2 = 1; // Start timer

	for(;;)
		P3 = ~(clock() / 1000) & 0x03;
}

I can only expect that this demo was just written long enough ago that the author forgot to update it, because… the author is an SDCC developer, and refers to his own papers working on it at the bottom of the demo.

A Very Conservative Compiler

Speaking of the compiler itself, I had no idea of what a mess I would get myself into by using it. Turns out that despite the fact that this is de-facto the only opensource embedded compiler people can use for the 8051, it is not a very good compiler.

I don’t say that to drag down the development team, who are probably trying to wrestle a very complex problem space (the 8051’s age make its quirk understandable, but irritating — and the fact that there’s more derivatives than there’s people working on them, is not making it any better), but rather because it is missing so much.

As Philipp describes it, SDCC “has a relative conservative architecture” — I would say that it’s a very conservative architecture, given that even some optimisations that, as far as I can tell, are completely safe are being skipped. For example, doing var % 2 (which I was using to alternate between two test patterns on my LEDs) was generating code calling into a function implementing integer modulo, despite being equivalent to var & 1, which is implemented in the basic instructions.

Similarly, the compiler does not optimise division by powers-of-two ­— which means that for anything that is not a build-time constant you’re better off using bitwise operations rather than divisions — it’s another thing that I addressed in the demo above, even though there it does not matter, as the value is constant at build time.

Speaking of build-time constants — turns out that SDCC does not do constant propagation at all. Even when you define something static const, and never take its address, it’s emitted in the data section of the output program, rather than being replaced at build time where it’s used. Together with the lack of optimisation noted above, it meant I gave up on my idea of structuring the firmware in easily-swappable components — those would rely on the ability of the compiler to do optimisation passes such as constant propagation and inlining, but we’re talking about the lack of much lower level optimisation now.

Originally, this blog post also wanted to touch on the fact that the one library of 8051 interfaces I found hasn’t been touched in six years, has still a few failed merge markers, and not even parsing with modern SDCC — but then again, now that I know SDCC does not optimise even the most basic of operations, I don’t think using a library like that is a good idea — the IO module there is extremely complicated, considering that most ports’ I/O lines can be accessed with bit-addressed registers.

Now, as Andrea (Insomniac) pointed out, Philipp also has a document on using LLVM with SDCC — but the source code this is referencing is more than five years old, and relies on the LLVM C backend, which means it’s generating C code for SDCC to continue compiling. I do wonder if it would make sense to have instead a proper LLVM target for 8051 code — it’s beyond the amount of work I want to put on this project, but last year they merged AVR support into LLVM, which allows to use (or at least try) Rust on 8-bit controllers already. It would be interesting to see if 8051 cores could be used with something different than C (or manually written assembly).

You could wonder why am I caring this much for a side project MCU that is quite older than me. The thing is I don’t, really. I just seem to keep bumping around 8051/2 in various places. I nearly wrote a disassembler for it to hack at my laptop’s keyboard layout a few years ago. I still feel bad I didn’t complete that project. 8051 is still an extremely common micro in low-power applications, and the STC89 in particular is possibly the cheapest micro you can set up prototypes at home: you can get 20 of them for less than 60p each from AliExpress, if you have the time to wait — I know, I just ordered a lot, just to have them around if I decide to do more with them now that I sort-of understand them. the manufacturer appears to make many multiple variants of them still, and I would be extremely surprised if you didn’t have a bunch of these throughout your home, in computers, dishwashers, washing machines, mice, and other devices that just need some cheap and cheerful logic controller without breaking the bank. Heck, I expect them to be used in glucometers, too!

With all these devices tied to closed-source, proprietary compilers, I would feel more comfortable if there was some active work on supporting a modern compiler platform in the open source world as well. From my point of view, this sounds like the needs of the industrial users, and those of the hobbyist community, diverged very much on this topic.

Sum It All Up

So for my art project I decided that even SDCC is good enough, but I wanted to make sure I would not end up with broken code (which appears to happen fairly often), so I ended up reading the generated assembly code to make sure it made sense. Despite not being particularly familiar with 8051 ISA, thanks to the Wikipedia article and the detailed datasheet from STC, it wasn’t too hard to read through it.

While I was going through it, I also figured out how to rewrite parts of the C code to force SDCC to emit some decent code. For instance, instead of a branch that either adds 1 or 32 to a counter, I was better off making a temporary variable hold 1, or change it to 32, add that variable. The fact that SDCC couldn’t optimise that made me sad, but again it’s understandable given the priorities.

Hopefully I have kept the source code still fairly readable. You can check the history to see all the various things I kept changing to make it more readable in assembly as well. Part of the changes meant changing some of my plans. In my first notes I wanted to run through 20 “hours” configurations in 60 minutes — but to optimise the code I decided that it’ll run 16 “hours” in just over 68 minutes. That way I could use a lot of power-of-twos and do away with annoying calculations.

Birch Books MCU Selection

A couple of people have asked me why I started the art project down the path of using an 8051 MCU, which is a fairly old microcontroller (heck, I found out I looked at those chips back in 2006!), rather than using one of the more modern hacker/maker solutions such as Arduino. The answer I already gave in that post: I had it already here.

I bought a devkit for it hoping to be able to hack on the LED heart I bought as a surprise for my wife on Valentine’s day, which was centered around the same micro. Now, with hindsight, that was silly: the board was explicitly marked with an AT89S52 name, which is a much more common chip, and probably one for which I could have found a devkit/programmer in much shorter time, but it turned out to be a nice exercise nonetheless.

Indeed, I ended up having to learn a lot more about this chip, its programming, and refreshing my (terrible) electronics understanding. And while this has been breaking my brain at times, it also stretched it to learn something new. I guess I now know how my wife is feeling while learning Python coming from a humanities background.

I had another micro at home. Some time ago I wanted to figure out how to send a certain sequence of infrared commands to my TV via Google Assistant (it’s a long story, sometimes my TV doesn’t initialize the audio return channel correctly), and I ended up buying (but never using) an Adafruit Feather M4 and an AirLift FeatherWing. I soldered the terminals and made sure they worked, but only played with it briefly.

The Feather comes with CircuitPython, a MicroPython implementation firmware, which actually is fairly nice to write simple logic for the microcontroller, and is very easy to deploy: you just need to copy the Python files in the virtual USB flash drive that appears when you connect the board to the computer. It also includes a very nice interactive Python shell you can use to experiment without needing to commit to code (yet). And with the AirLift you also get support for controlling remotely via WiFi, and setting up all kind of request handling.

On the other hand, the 8051 is a fairly complicated tool. The ISA has not had any refresh since 1980 for what I can tell, and that’s on purpose: binary and pin compatibility appears to be the main advantage on using 8051 derivatives chips (or cores on FPGA). You’d think that with that having stayed the same we would have very advanced toolchains for it, but you’d be wrong. As far as I can tell, the only maintained open-source compiler for this is SDCC, and even that barely just. You might have seen my rants about this on Twitter, and if not, fear not: I’ll write a post about it next week.

So why did I go for the 8051, which is significantly older, harder to write code for, harder to program (you either need a devboard, or make sure you provide the right ISP headers on the board), and with quite a few question marks on its availability?

Well, the Feather only has a few really general purpose I/O lines. While both the M4 and the ESP32 supposedly should have enough GPIO lines, the Feather is a specific configuration, that commits a lot of lines to specific usage, such as an I²C/SPI bus to communicate with different Feathers. The usual answer to this is to include something like the MCP23017, which is an I/O expander that you drive via the I²C bus. But as it turns out, not only I don’t have one of those at home, but even Adafruit appears to only sell it on an Expander Bonnet for the Raspberry Pi. I’m not sure why there’s no FeatherWing with it, despite the fact that they document how to use one with CircuitPython, and while I’m sure I could design one, or look for an unofficial one, it’s something I don’t want to get to right now.

On the other hand, 8051 and its clones come with a lot more GPIO lines, and most of those are uncommitted if you start from nothing. The DIP-40 packages have 32 lines, and if you don’t need to use external memory, you have at the very least 16 uncommitted lines. Of the other 16 lines, some are shared with other functions, including external hardware interrupts, serial port, and most in-system programming interfaces.

Now, theoretically it seems like the ESP32 chip also have quite a few GPIO lines, although I only counted 14 uncommitted lines on their QFN packages. I guess you can scavenge a few more lines by not using some of the features, but that might end up conflicting with the MicroPython interfaces anyway.

So yeah I will probably eventually move to a different design that includes the MCP23017. Maybe I’ll end up designing a Feather Base (if not a proper FeatherWing) for it after all, to prototype with the already designed (and sent to fab) actuator board. But that’s a story for another time.