-
Notifications
You must be signed in to change notification settings - Fork 0
/
fffserial.py
181 lines (144 loc) · 5.42 KB
/
fffserial.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
'''
Module for communicating with the display using a serial interface. The display
is connected to an arduino. This packages helps during the
communication with the device over a serial interface.
'''
import serial # pip install pyserial
import displayprovider
import configuration
DEVICE = configuration.flipdotdisplay["serialdevice"]
BAUD = configuration.flipdotdisplay["serialbaudrate"]
# TODO handle serial errors
class SerialDisplay(displayprovider.DisplayBase):
"""
Serial Display sending commands to an arduino connected to the display.
Each command starts with a byte with a command identifier. The following
bytes are the command parameters.
"""
DIMENSION = 0b10010000
"The following two bytes are the width and height of the display."
PICTURE = 0b10000001
"The following bytes are the picture data (row by row)."
PXSET = 0b10000011
"The following two Bytes X, Y with information about the pixel to set."
PXRESET = 0b10000010
"Removing a pixel. The following two Bytes X, Y with information about the pixel to reset."
ECHO = 0b11110000
"The following byte is returned."
LED_BRIGTHNESS = 0b10000100
"Set the brightness of the LED. The following byte is the brightness."
def __init__(self, width=4, height=3, serial_device="/dev/ttyUSB0", baud=9600, buffered=True):
'''
Create serial display with given dimension. If buffered is True, all
calls to px() will write into an internal buffer until a call to
show() will send the data.
'''
# coordinate information must fit into 7 Bit!
assert width < 128 and height < 128, "Serial display dimension is too big!"
super().__init__(width, height)
# TODO add support for auto configuring dimensions
print('open serial device', serial_device)
self.ser = serial.serial_for_url(serial_device, baudrate=baud, timeout=1)
self.buffered = buffered
self.buffer = [False] * (width * height)
if not self.display_available():
print("WARNING: display not available")
def led(self, on_off):
'Turn LED of the display on or off'
# TODO add support for brightness
if on_off:
bs = [SerialDisplay.LED_BRIGTHNESS, 1]
else:
bs = [SerialDisplay.LED_BRIGTHNESS, 0]
self.ser.write(bs)
def px(self, x, y, val):
assert 0 <= x < self.width
assert 0 <= y < self.height
if self.buffered:
self.buffer[y * self.width + x] = val
else:
bs = [SerialDisplay.PXSET if val else SerialDisplay.PXRESET, x, y]
self.ser.write(bytes(bs))
def show(self):
'Send the content of the buffer to the display using serial interface.'
if not self.buffered:
# ignoring invocation.
return
byte_sequence = [SerialDisplay.PICTURE]
byte = '0' # Databytes start with 0
for bit in self.buffer:
byte += '1' if bit else '0'
if len(byte) == 8:
byte_sequence.append(int(byte, base=2))
byte = '0'
if len(byte) > 1:
byte += '0' * (8 - len(byte))
byte_sequence.append(int(byte, base=2))
self.ser.write(bytes(byte_sequence))
def display_available(self):
test_byte = 42
self.ser.write(bytes([SerialDisplay.ECHO, test_byte]))
bs = self.ser.read(2)
# TODO firmware should not return a string
try:
return len(bs) == 2 and str(bs, encoding="UTF8") == str(test_byte)
except UnicodeDecodeError:
# no decoding possible if display is not present.
# mainly during testing
return False
def close(self):
'Close the serial device'
self.ser.close()
def demo_simple():
ffd = SerialDisplay(width=28, height=13, serial_device=DEVICE, baud=BAUD, buffered=True)
print("sending pixel")
ffd.px(10, 10, True)
ffd.show()
#ffd.close()
def demo_all_onoff():
import time
fdd = SerialDisplay(width=28, height=13,
serial_device=DEVICE, baud=BAUD)
for _ in range(10):
print("all on")
for i in range(len(fdd.buffer)):
fdd.buffer[i] = True
fdd.show()
fdd.led(True)
time.sleep(1)
print("all off")
for i in range(len(fdd.buffer)):
fdd.buffer[i] = False
fdd.show()
fdd.led(False)
time.sleep(1)
def test_serial():
fdd = SerialDisplay(width=28, height=13,
# using a serial dummy device for debugging
# https://pythonhosted.org/pyserial/url_handlers.html#loop
serial_device='loop://?logging=debug',
buffered=False)
fdd.px(10, 10, True)
assert fdd.width == 28
assert fdd.height == 13
# turning buffering on
fdd.buffered = True
fdd.px(10, 10, True)
assert fdd.buffer[10 * fdd.width + 10] == True
for i in [-1, +1]:
assert fdd.buffer[10 * fdd.width + 10 + i] == False
fdd.show()
fdd.close()
def demo():
import demos
ffd = SerialDisplay(width=configuration.WIDTH, height=configuration.HEIGHT,
serial_device=DEVICE, baud=BAUD, buffered=True)
demo = demos.RotatingPlasmaDemo(ffd)
try:
demo.run()
except KeyboardInterrupt:
ffd.close()
if __name__ == '__main__':
#demo()
demo_all_onoff()
#demo_simple()