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()