Display, menus, state machines
With the LED controller in place, I just needed to refine how I was putting pixels out and construct a menu system.
Turns out it was trickier than I thought - the pixels are indexed in memory very strangely! I'm not sure whether the quirks I observed were due to the way the Kello folks wired up the LED matrixes or just how the HT1632C works, but I didn't feel like doing too much investigation this time. Instead, I resorted to old-fashioned trial and error.
Pixels are written to the display by indexing them into a stream of bits (at least, they are for me - since I'm using "bit-banging" instead of real SPI). I just have to figure out what order the bits are in, relative to X-Y coordinates that I want to work in.
To start, I just began writing an increasing number:
So that was a bit less sensible than expected. Although the refresh rate flickering makes it a bit hard to tell, the lights are turning on in reverse order across the first panel, but they seem to be... split across the middle as well?
I was a little burnt out on thinking at this point, so I just began iterating on algorithms. Eventually, it worked. The code can't be summarized too well in English, but here it is.
The next step was to beef up the LED driver code with a framebuffer system, so I could write multiple things on the screen virtually before flushing it to the device - the device write has a noticeable delay, so consecutive writes are best avoided.
But writing the time, while great, is just the basics. To display a system of menus which I can utilize, I need both hardware controls and logic to determine what's on screen.
For that, I turned to XState. I've never used it before, but I've seen a lot about it! State charts are known to be less prone to logic errors, and the last thing I want is a logic error in a device I will only be using when I immediately wake up or when I'm already in bed - neither a great time to fix a bug.
Creating a state chart was fun! I enjoyed the experience of declaring the structure of the state transitions before I ever had to write implementation code. I used activities to implement drawing to the screen, which might not be the explicit intention, but did end up working just fine. Activities remind me of React's useEffect, in that they allow the returning of a "cancel" callback. This models the loop required to draw most screens effectively pretty well - I subscribe to events to update the screen, and can unsubscribe when the state transitions.
With the code in place, I connected a rotary encoder to the Pi for control. When the encoder is turned or pressed, events are sent to the state machine.
That's enough for now - next, it's time for playing an alarm. Of course, it was only now that I realized the Pi Zero W doesn't have an audio out... that's a tomorrow problem.