So a good friend of mine heard about the sad state of my ‘compute shelf’ in my garage and very kindly donated me his old 19“ mini racks and some of his old Unifi kit.
I didn’t have much kit to fill even this small rack with, so naturally my first step was to see what cool things you guys were doing with the extra “U“s in your home racks.
One of the fun things I found was u/aforsberg’s post about their WOPR LED panel creation, which I thought was a great idea.
After managing to re-create that WOPR look, I wondered if I could work out how to use the same panel to re-create those old ‘falling code’ screensavers of The Matrix in a lo-fi way.
I le…
So a good friend of mine heard about the sad state of my ‘compute shelf’ in my garage and very kindly donated me his old 19“ mini racks and some of his old Unifi kit.
I didn’t have much kit to fill even this small rack with, so naturally my first step was to see what cool things you guys were doing with the extra “U“s in your home racks.
One of the fun things I found was u/aforsberg’s post about their WOPR LED panel creation, which I thought was a great idea.
After managing to re-create that WOPR look, I wondered if I could work out how to use the same panel to re-create those old ‘falling code’ screensavers of The Matrix in a lo-fi way.
I learned heaps along the way, so I thought I’d share my remix here to express my thanks to u/aforsberg for their original idea, and also to give back to the community here that has helped with so much info.
My changes from the original WOPR version:
blue LED panels instead of red. There are also green LED panels if you want a more authentic Matrix vibe, but I chose to complement the Unifi blue. 1.
my first prototype used a 1U brush-plate to hold the LED panels instead of a 3D-printed frame. I don’t have easy access to a 3D printer so taking out the brush element (just a couple of tiny screws to remove) meant this approach is inexpensive and super-solid, though the down-side is it does hide 25% of the LEDs (the top and bottom rows). 1.
once I had my first prototype working well I found a local on-demand 3D print company and was able to get a 1U bracket printed. I found grajohnt had already remixed aforsberg’s design, so I started there and made some further small refinements (my design on Printables). 1.
and of course the new MicroPython code I wrote, which I’ll post below. I’ve included some in-line comments in case anyone wants to adjust/improve it. Basically each column gets a randomly-sized ‘code block’ of 1-4 pixels, each falling at a random speed.
If anyone adopts/adapts this further, I’d love to hear about it!
My code for The Matrix display (note the max7219.py driver is still needed, as per u/aforsberg’s design) is below:
from machine import Pin, SPI
import max7219
from utime import ticks_ms, ticks_diff, sleep
import random
# Configuration for MAX7219
NUM_MODULES = 12 # Specify how many modules you have
WIDTH = 8 * NUM_MODULES # Specify pixel width of each module
HEIGHT = 8 # Specify pixel height of each module
spi = SPI(0, sck=Pin(2),mosi=Pin(3))
cs = Pin(5, Pin.OUT)
display = max7219.Matrix8x8(spi, cs, NUM_MODULES)
display.brightness(0) # Set LED brightness (0=low 15=high)
def clear_display():
display.fill(0)
def falling_code():
# Initialise column properties: start position, group height, fall speed, last update time
columns = [{"start_row": -1, # Each group 'head' starts above row 0
"group_height": random.randint(0, 4), # Random group height (0-4 pixels)
"fall_speed": random.randint(5, 20) / 20, # Random fall speed (seconds per step)
"last_update": ticks_ms()} for _ in range(WIDTH)] # Timestamp of last movement
while True:
clear_display()
current_time = ticks_ms() # Get the current timestamp
for col in range(WIDTH):
column = columns[col]
start_row = column["start_row"]
group_height = column["group_height"]
fall_speed = column["fall_speed"]
last_update = column["last_update"]
# Calculate elapsed time since the last update
elapsed_time = ticks_diff(current_time, last_update) / 1000.0 # Convert to seconds
# Check if enough time has passed for this group to move
if elapsed_time >= fall_speed:
column["last_update"] = current_time # Update the last movement time
column["start_row"] += 1 # Move group down by 1 row
# Illuminate the current group's pixels
for i in range(group_height):
row = start_row - (group_height - 1) + i # Move group based on its height
if 0 <= row < HEIGHT: # Ensure rows stay within boundaries
display.pixel(col, row, 1)
# Reset the group if it has fallen out of bounds
if column["start_row"] >= (HEIGHT + group_height): # Check if 'tail' has exited
column["start_row"] = -1 # Reset to start above row 0
column["group_height"] = random.randint(0, 4) # New random group height
column["fall_speed"] = random.randint(5, 20) / 20 # New random fall speed
column["last_update"] = current_time # Reset the update timer
display.show()
sleep(0.05) # Small delay for smooth rendering
# Initialise display and run the effect
clear_display()
display.show()
falling_code()