chaser.py
1 # SPDX-FileCopyrightText: 2022 Phillip Burgess for Adafruit Industries 2 # 3 # SPDX-License-Identifier: MIT 4 5 """ 6 Chaser lights for Little Connection Machine. Random bit patterns shift 7 left or right in groups of four rows. Nothing functional, just looks cool. 8 Inspired by Jurassic Park's blinky CM-5 prop. As it's a self-contained demo 9 and not connected to any system services or optional installations, this is 10 the simplest of the Little Connection Machine examples, making it a good 11 starting point for your own projects...there's the least to rip out here! 12 """ 13 14 import random 15 import time 16 from cm1 import CM1 17 18 DENSITY = 30 # Percentage of bits to set (0-100) 19 FPS = 6 # Frames/second to update (roughly) 20 21 22 def randbit(bitpos): 23 """Return a random bit value based on the global DENSITY percentage, 24 shifted into position 'bitpos' (typically 0 or 8 for this code).""" 25 return (random.randint(1, 100) > (100 - DENSITY)) << bitpos 26 27 28 class Chaser(CM1): 29 """Purely decorative chaser lights for Little Connection Machine.""" 30 31 def __init__(self, *args, **kwargs): 32 super().__init__(*args, **kwargs) # CM1 base initialization 33 # Initialize all bits to 0. 32 rows, 4 columns of 9-bit patterns. 34 self.bits = [[0 for _ in range(4)] for _ in range(32)] 35 36 def run(self): 37 """Main loop for Little Connection Machine chaser lights.""" 38 39 last_redraw_time = time.monotonic() 40 interval = 1 / FPS # Frame-to-frame time 41 42 while True: 43 self.clear() # Clear PIL self.image, part of the CM1 base class 44 for row in range(self.image.height): # For each row... 45 for col in range(4): # For each of 4 columns... 46 # Rows operate in groups of 4. Some shift left, others 47 # shift right. Empty spots are filled w/random bits. 48 if row & 4: 49 self.bits[row][col] = (self.bits[row][col] >> 1) + randbit(8) 50 else: 51 self.bits[row][col] = (self.bits[row][col] << 1) + randbit(0) 52 # Draw new bit pattern into image... 53 xoffset = col * 9 54 for bit in range(9): 55 mask = 0x100 >> bit 56 if self.bits[row][col] & mask: 57 # self.draw is PIL draw object in CM1 base class 58 self.draw.point([xoffset + bit, row], fill=self.brightness) 59 60 # Dillydally to roughly frames/second refresh. Preferable to 61 # time.sleep() because bit-drawing above isn't deterministic. 62 while (time.monotonic() - last_redraw_time) < interval: 63 pass 64 last_redraw_time = time.monotonic() 65 self.redraw() 66 67 68 if __name__ == "__main__": 69 MY_APP = Chaser() # Instantiate class, calls __init__() above 70 MY_APP.process()