11import time
22from datetime import datetime
3- #from inputmodule.inputmodule.ledmatrix import LedMatrix, Pattern
3+ # from inputmodule.inputmodule.ledmatrix import LedMatrix, Pattern
4+
5+ FWK_MAGIC = [0x32 , 0xAC ]
6+ FWK_VID = 0x32AC
7+ LED_MATRIX_PID = 0x20
8+ QTPY_PID = 0x001F
9+ INPUTMODULE_PIDS = [LED_MATRIX_PID , QTPY_PID ]
10+ RESPONSE_SIZE = 32
11+
12+ import serial
413
514# TODO: Import
615from enum import IntEnum
16+
17+
718class PatternVals (IntEnum ):
19+ Percentage = 0x00
20+ Gradient = 0x01
21+ DoubleGradient = 0x02
22+ DisplayLotus = 0x03
23+ ZigZag = 0x04
824 FullBrightness = 0x05
25+ DisplayPanic = 0x06
26+ DisplayLotus2 = 0x07
27+
28+
929class CommandVals (IntEnum ):
1030 Brightness = 0x00
1131 Pattern = 0x01
32+ BootloaderReset = 0x02
33+ Sleep = 0x03
34+ Animate = 0x04
35+ Panic = 0x05
36+ Draw = 0x06
37+ StageGreyCol = 0x07
38+ DrawGreyColBuffer = 0x08
39+ SetText = 0x09
40+ StartGame = 0x10
41+ GameControl = 0x11
42+ GameStatus = 0x12
43+ SetColor = 0x13
44+ DisplayOn = 0x14
45+ InvertScreen = 0x15
46+ SetPixelColumn = 0x16
47+ FlushFramebuffer = 0x17
48+ ClearRam = 0x18
49+ ScreenSaver = 0x19
50+ SetFps = 0x1A
51+ SetPowerMode = 0x1B
52+ PwmFreq = 0x1E
53+ DebugMode = 0x1F
54+ Version = 0x20
55+
1256
1357class Pattern :
1458 width = 9
1559 height = 34
16- vals = []
60+
61+ def __init__ (self ):
62+ """Empty pattern with all LEDs off"""
63+ self ._vals = [[0 for _ in range (self .height )] for _ in range (self .width )]
1764
1865 def percentage (p ):
19- Pattern ()
66+ """A percentage of LEDs on, increasing vertically from the bottom"""
67+ pattern = Pattern ()
68+ pattern ._vals = [
69+ [(0xFF if (y * 100 / 34 > p ) else 0 ) for y in range (pattern .height )]
70+ for _ in range (pattern .width )
71+ ]
72+ return pattern
2073
2174 def from_string (s ):
22- Pattern ()
75+ # TODO
76+ return Pattern ()
2377
2478 def set (self , x , y , val ):
25- pass
79+ """Set a specific LED to a brightness value"""
80+ assert val >= 0 and val <= 0xFF
81+ assert x >= 0 and x <= self .width
82+ assert y >= 0 and y <= self .height
83+ self ._vals [x ][y ] = val
84+
85+ def to_bw_vals (self ):
86+ """To list of 39 byte values [Int]"""
87+ vals = [0x00 for _ in range (39 )]
88+ for x , col in enumerate (self ._vals ):
89+ for y , val in enumerate (col ):
90+ if val == 0xFF :
91+ i = x + self .width * y
92+ vals [int (i / 8 )] |= 1 << i % 8
93+ return vals
94+
95+ def to_gray_vals (self ):
96+ """To [[]]"""
97+ return self ._vals
98+
2699
27- class InputModule :
100+ class ModuleNotFoundException ( Exception ) :
28101 pass
29102
103+
30104class LedMatrix (object ):
31- def __init__ (self , name ):
32- self .name = name
105+ def __init__ (self , dev_path = None ):
106+ self .dev_path = dev_path
107+
108+ if dev_path is None :
109+ pass
110+
33111 self .fw_version = "0.1.9"
34112 self .sleeping = True
35- self .brightness = 100
113+ # self.brightness = 100
114+ self .dev_path = "/dev/tty0"
115+ # self.dev_path = None
116+ self .dev = None
117+ # TODO: Check if it's there
118+ # raise ModuleNotFoundException(f"Module {port} not found")
119+ if False :
120+ raise ModuleNotFoundException ("No Module found" )
36121
37- def find_all ():
38- return [1 , 2 ]
122+ def from_port (port ):
123+ """Connect to an LED matrix by specifying the serial port name/path"""
124+ return LedMatrix (port )
125+
126+ def left ():
127+ """Connect to the left LED matrix"""
128+ # TODO
129+ raise ModuleNotFoundException ("Left Module not found" )
130+
131+ def right ():
132+ """Connect to the right LED matrix"""
133+ # TODO
134+ raise ModuleNotFoundException ("Right Module not found" )
135+
136+ def list_ports ():
137+ """List all serial ports with LED matrices"""
138+ return ["/dev/ttyACM0" ]
39139
40140 def __enter__ (self ):
41- #print('entering' )
141+ self . dev = serial . Serial ( self . dev_path , 115200 )
42142 return self
43143
44144 def __exit__ (self , exc_type , exc_val , exc_tb ):
45- pass
46- #print('leaving')
145+ self .dev .close ()
47146
48147 def enter_bootloader (self ):
49- pass
148+ """Put the module in bootloader mode to update the firmware"""
149+ self .raw_command (CommandVals .BootloaderReset , [0x00 ])
50150
51- def raw_command (self , command , vals ):
52- pass
151+ def raw_command (self , command , parameters = [], with_response = False ):
152+ """Send a raw command with command ID and payload bytes"""
153+ vals = FWK_MAGIC + [command ] + parameters
154+ self .dev .write (command )
155+ if with_response :
156+ res = self .dev .read (RESPONSE_SIZE )
157+ return res
53158
54159 def set_bw (self , pattern ):
55- pass
160+ """Draw a black and white pattern on the LED matrix"""
161+ vals = pattern .to_bw_vals ()
162+ self .raw_command (CommandVals .Draw , vals )
56163
57164 def set_grayscale (self , pattern ):
58- pass
165+ """Draw a greyscale pattern on the LED matrix"""
166+ for x in range (0 , pattern .width ):
167+ vals = pattern .to_gray_vals ()[x ]
168+ self ._send_col (x , vals )
169+ self ._commit_cols ()
170+
171+ def _send_col (self , x , vals ):
172+ """Stage greyscale values for a single column. Must be committed with commit_cols()"""
173+ command = FWK_MAGIC + [CommandVals .StageGreyCol , x ] + vals
174+ self .dev .write (command )
175+
176+ def _commit_cols (self ):
177+ """Commit the changes from sending individual cols with send_col(), displaying the matrix.
178+ This makes sure that the matrix isn't partially updated."""
179+ command = FWK_MAGIC + [CommandVals .DrawGreyColBuffer , 0x00 ]
180+ self .dev .write (command )
59181
60182 # TODO: Properties for things like sleeping, brightness, ...
61183 @property
62- def radius (self ):
63- """The radius property."""
64- print ("Get radius" )
65- return self ._radius
66-
67- @radius .setter
68- def radius (self , value ):
69- print ("Set radius" )
70- self ._radius = value
184+ def brightness (self ):
185+ """Get current module brightness"""
186+ res = self .raw_command (CommandVals .Brightness , with_response = True )
187+ return int (res [0 ])
71188
189+ @brightness .setter
190+ def brightness (self , value ):
191+ """Change brightness"""
192+ self .raw_command (CommandVals .Brightness , [value ])
72193
73- #
74- matrices = LedMatrix .find_all ()
75- print (f"{ len (matrices )} LED Matrices connected to the system" )
76194
77- with LedMatrix ( "COM1" ) as matrix :
195+ def demo_interaction ( matrix ) :
78196 print (f"Firmware version: { matrix .fw_version } " )
79197
80198 print (f"Sleep status: { matrix .sleeping } " )
81199 print (f"Going to sleep and back" )
82200 matrix .sleeping = True
83201 matrix .sleeping = False
84202
85- print (f"Current brightness: { matrix .brightness } " )
203+ # print(f"Current brightness: {matrix.brightness}")
86204 print ("Setting 100% brightness" )
87205 matrix .brightness = 100
88206 print ("Setting 50% brightness" )
@@ -101,12 +219,12 @@ def radius(self, value):
101219 print ("Iterating through a couple of built-in patterns, 1s each" )
102220 pattern_commands = [
103221 PatternVals .FullBrightness ,
104- # PatternVals.Gradient,
105- # PatternVals.DoubleGradient,
106- # PatternVals.DisplayLotus,
107- # PatternVals.ZigZag,
108- # PatternVals.DisplayPanic,
109- # PatternVals.DisplayLotus2
222+ PatternVals .Gradient ,
223+ PatternVals .DoubleGradient ,
224+ PatternVals .DisplayLotus ,
225+ PatternVals .ZigZag ,
226+ PatternVals .DisplayPanic ,
227+ PatternVals .DisplayLotus2 ,
110228 ]
111229 for pattern in pattern_commands :
112230 matrix .raw_command (CommandVals .Pattern , [pattern ])
@@ -136,3 +254,33 @@ def radius(self, value):
136254 current_time = datetime .now ().strftime ("%H:%M" )
137255 print ("Current Time =" , current_time )
138256 matrix .set_bw (Pattern .from_string (current_time ))
257+
258+
259+ def demo ():
260+ matrices = LedMatrix .list_ports ()
261+ print (f"{ len (matrices )} LED Matrices connected to the system" )
262+
263+ try :
264+ # Open specific
265+ with LedMatrix .from_port ("COM1" ) as matrix :
266+ pass
267+ except ModuleNotFoundException as e :
268+ print (e )
269+
270+ # Left and right matrix, fails if only one is connected
271+ try :
272+ with LedMatrix .left () as left_matrix , LedMatrix .right () as right_matrix :
273+ print (f"Left: { left_matrix } , Right: { right_matrix } " )
274+ except ModuleNotFoundException as e :
275+ print (e )
276+
277+ # Choose first available matrix (best if just one is connected)
278+ try :
279+ with LedMatrix () as matrix :
280+ demo_interaction (matrix )
281+ except ModuleNotFoundException as e :
282+ print (e )
283+
284+
285+ if __name__ == "__main__" :
286+ demo ()
0 commit comments