Skip to content

Commit cd6e329

Browse files
committed
python: Refine sample API
Signed-off-by: Daniel Schaefer <dhs@frame.work>
1 parent 631ffa7 commit cd6e329

File tree

1 file changed

+187
-39
lines changed

1 file changed

+187
-39
lines changed

python/sample.py

Lines changed: 187 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,88 +1,206 @@
11
import time
22
from 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
615
from enum import IntEnum
16+
17+
718
class 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+
929
class 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

1357
class 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+
30104
class 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

Comments
 (0)