Hardware Details
This digital hour glass simulates falling sand on a 128x64 OLED screen. An ESP32 drives the system, communicating with both the IMU and OLED via I2C. This is excellent as it means hardware construction involves only 4 nets: VCC, GND, SDA, and SCL.
The IMU is crazy overkill -- an MPU-6050 which boasts 16-bit Gyroscope and Accelerometer. But, I didn't have one on hand and Amazon Prime search for "Accelerometer breakout" turned up a listing for $6 that was delivered in 24-hours. This screen came with the KiCon badge from last month, which was conveniently still on my desk and not soldered or bolted to the carrier board.
Unregulated power is supplied by a 2x AA battery holder. Nominally this is bad... 50% battery state will be 2.6V and shutoff for the ESP32 is something like 2.7. In practice it works okay but will only run for a few hours before batteries -- still far from consumed -- drop low enough to cause resets.
After prototyping and finalizing the firmware on a breadboard, I moved to assembly. Female-female jumper wires were connected to the pins of each module, clipped, stripped, and soldered together. Hot glue and popsicle sticks group everything together. The hope is that I can remove the glue after the event and reclaim the modules for future prototyping.
Software Details
The overall goal of this project was to build something cool in the least amount of time possible. I chose to use the Arduino IDE for this and it's debatable if that was a time saver or not. This provided ready-to-use libraries which saved time on getting the hardware to "hello world" with zero effort. But it cost a lot of time in not understanding exactly how the OLED library worked and lacking the most rudimentary IDE features (like refactoring... seriously, why isn't there refactoring support?!?!). Likely this still saved me time, but approximately 1/3 of the time I spend on the codes was fighting with the fastimage function of the library.
Cellular Automata
In my mind, this is the best (and most fun) part of the project. I'm am not tracking grains of sand individually. Instead I'm using a bit-packed array that is 128x64 bits. The hourglass is overlaid into this and at each frame refresh (10ms) the entire buffer is parsed and simple rules applied to each pixel:
- When a pixel exists it is compared to the hourglass shape to ensure we've found sand and not glass
- If a grain of sand has and empty space below it is moved down one pixel
- If the pixel below is occupied, the spaces to the left or right are check and it is moved there
- Gravity is applied in both north/south, and east/west orientations. This way, if you tilt the board slightly right, the sand will fall in that direction
- On/Off gravity is not very interesting, so 5 levels of gravity are applied based on how much you tilt the device. Lower gravity simply skips frame updates so the movement happens more slowly
- "Bathtub" is applied to the top half of the hourglass. This is special logic to make sure the draining sand comes from the center on the top. Using the default rules (2 & 3) I was getting all of the drain from one side resulting in a 45-degree angle of sand.
Closing Thoughts
I love it! I can't stop showing it to people and had such a delightful time working on this. In fact, what I really want to do is build silly projects like this all day, every day. I can't wait to get to the hacker-retirement-home ;-)
This is one major "won't fix" issue with this. If you tilt to the right and observer the left edge of the upper hour glass there is jitter. I believe this is caused because North/South gravity is calculated, then East/West gravity pulls the sand away form the glass. The sand needs to have North/South gravity applied again, but doing so would require E/W gravity to be calculated again. It's a vicious cycle and I suspect I've come up against a fundamental...
Read more »
Can you share the code?