This page is dedicated to documenting my binary clock that I built. I call it the BinarIKEA, as I took inspiration from IKEA's timers they had a while back.
The Inspiration
They were square boxes with a display on the front. Having a temperature sensor, calendar, clock, and timer all in one fun (and fun-sized) package made it a really appealing design and I wanted to recreate that. However, I was also inspired by the Fitbit binary clock face (shout out blueiii, he helped me want to learn binary) and decided to fuse the two into a sleek, unique yet functional decoration.
⠀⠀
Materials
If you want to follow along at home, then here's the materials you need:
- ESP32 NodeMCU
- MPU6050 (make sure it's working 😅)
- 16 WS2812 LEDs on a strip (NeoPixels are another name for them)
- Tiny USB Power Bank
- Small protoboard (big enough to fit the ESP32 and screw terminals)
- 6 screw terminals (I recommend 3x 2pin terminals, but you could just solder the wires to the protoboard if you prefer)
- Solderless 3pin LED to wire connectors if you don't feel comfortable doing light SMD soldering
- Soldering iron
- Solder
- Access to a 3D printer
You'll also need the sketch and STLs from the Github repo. It's possible to make the case without one (eg laser-cut wood), but it's a lot harder and library or college makerspaces typically have some available to the public. There are also online sites like PCPWay.
⠀⠀
The Trickiness of Supports
To start I, rather rashly, started modeling an extremely simple case in Tinkercad. I sized a cube to fit the dimensions of my LEDs (with padding, of course). Then I duplicated that cube sized it down approximately 2 mm and used it as a hole to cut the other, leaving some padding for the LEDs to diffuse in (This is going to be really important later). Then I made a caddy to hold the LEDs, cutting shallow grooves (around 3mm) into a square. I also cut holes for the solderless connectors to go through at the end, leaving room for the LEDs to wrap around (subtle foreshadowing). As far as a lid goes, all I did was cut an inverted pyramid into the top and call it a day. I put them on a thumb drive and sent it to get printed, orienting the tray so the grooves were facing toward the print bed. Now, this may not seem like a problem, but anyone who knows about 3D printing can tell you this was going to end very, very bad. When I got them back, there was a minor problem. The grooves were now full of support material. Unfortunately, I had asked this be printed in a durable black, and as such now had a hard sponge of material in the grooves, fused to the groove walls. I tried prying, sanding, and picking away as much as I could, but it didn't budge. Eventually, I scrapped that prototype, opting to correct the orientation and send it off again rather than salvage the marred mess of plastic I currently had. Lo and behold, it came back perfect. One task tackled. On to the next! Or so I thought. Once I actually tried using the new tray, the LEDs didn't line up with the holes in the front. Well, I thought, I'll have to reprint the tray yet again. However, by some stroke of luck, I had designed the case so that when I rotated the tray 180 degrees, the LEDs actually lined up with the holes.
⠀⠀
When The Project is Forgotten About
While I could have been working on the software while I was sorting out the case, I didn't. I told myself I'd get it sorted out once I got the case back. Once it was perfect. When that day came, however, the project was simply pushed to the back burner. Months passed. The cube, which I was so motivated to finish, sat neglected on the corner of my projects shelf. Then, one day, I looked at it and thought, "Well, how hard could it possibly be? How hard could it be to finish the software for a simple binary clock?" The answer, as will soon become apparent, was quite hard. Quite hard indeed. It would take many hours of staring at the same bug and wondering what needed to change to resolve it. Excruciating minutes spent waiting for a fix to compile, only for the fix to not work.
⠀⠀
A Few Tribulations of an Embedded Engineer
When you're working with embedded devices, it's hard to rerun the program. Needing to recompile takes minutes, and, to rectify a simple bug, minutes are a long time to wait.
⠀⠀
I eagerly started programming. Copy pasting from the examples led me to a prototype very quickly. ChatGPT also assisted in writing functions to deal with array rotation, decimal to binary conversion, and more. I had a basic grid in my control within 3 hours, but then the hard part began. I then proceeded to struggle with converting decimal numbers to binary. Oh, it's not that hard, all you do is ------ , I can hear you saying. While I would be typically open to easier ways of doing things, I want to make the translation algorithms functions. That is a whole lot harder than it sounds in C++. You have to deal with passing pointers around, naming the correct array pointer type, etc. For an experienced C++ chad, this wouldn't be hard. But for me, a stubborn C++ noob, it took forever. A good portion of development time was spent sorting out whether to pass an std::array via & or not, what type to name std::array
⠀⠀
The Definition of Insanity Is Trying the Same Thing Repeatedly Expecting Different Results
Rotation. Rotation was missing. So, I hooked up my MPU6050 to my ESP32 and tried running the example. It failed to detect the sensor, even though all the pins were correct and the code was unmodified. WTH??? I tried reseting the board, tried a different example, tried all of the above on a completely different board. No dice. Not once did I get the gyro sensor readings. I finally found (after much Googling and forum-browsing) a hecking-long sketch that finally detected my sensor. I was relieved. However, when I went to integrate it into my code, it simply didn't compile. Turns out it was written for the Arduino Uno, not ESP32. So I did what any sensible person would do: bought 3 more MPU6050s from Amazon. After soldering them, they worked immediately with the demo code. Integrating the demo code, I soon came to a realization. The sensor only gave me the acceleration data along each axis. Along with that came another realization: Yaw (side to side when the sensor is right side up) is relative to starting position. Of course, I learned this in the most normal place: standing in the checkout line at Costco. The fix was simple; rotate the sensor on its side, but converting the acceleration to absolute degrees was difficult to understand. I just copy-pasted someone else's code (it worked, btw), but I wanted to understand just how it converted 3 distance measures into an angle measure. Enter trigonometry.
⠀⠀
Brief Trigonometric Intermission
SOH CAH TOA
⠀⠀
Now, the code worked, so I didn't have to understand it. But I'm curious, so I needed to understand it. The formula for getting the pitch (in radians) is arctan2(zAccel, yAccel). What is arctan? Well, I'm glad you asked, because it was the subject of my bedtime reading that night.