Skip to content

Commit 6a4a586

Browse files
committed
Implementation of the protocol in the different model of smart screen.
The different model has quite a different protocol which meant that the original code didn't work. But the principle seems the same. The main differences are... * Command packets are 10 bytes long, framed by the command byte at either end. * Command bytes are 0xCA-0xCE. * A 'HELLO' command elicits a response from the device of 'HELLO' followed by 0x0A, 0x12, 0x00. Useful to identify the device format. * An 'orientation' packet allows the orientation of the next bitmap block to be selected from landscape and portrait (column major or row major). * The graphics data is still 565, BUT... high 3 bits of green are in b0-b2, low 3 bits of green are in b13-15, red is in b3-7, blue is in b8-12. * A 'lighting' packet lets you select the colours of the backlight LEDs with 8bit RGB values. * Amusingly the 'lighting' isn't supported by the software that the seller included in their listing - although they mention it, there's no way to set the colours in their software. * There is no Clear, Screen off, or Screen on packet (as far as I can tell). The device has a serial number of '2017-2-25' - so whilst this appears to be newer, the year implies that it was created longer ago? These changes are just to make things work, with my own comments and debug to show what I found.
1 parent aba18d2 commit 6a4a586

File tree

1 file changed

+115
-16
lines changed

1 file changed

+115
-16
lines changed

main.py

Lines changed: 115 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -14,18 +14,39 @@
1414
# Set your COM port e.g. COM3 for Windows, /dev/ttyACM0 for Linux...
1515
COM_PORT = "/dev/ttyACM0"
1616
# COM_PORT = "COM5"
17+
# MacOS COM port:
18+
COM_PORT = '/dev/cu.usbmodem2017_2_251'
19+
20+
# The new device has a serial number of '2017-2-25'
1721

1822
DISPLAY_WIDTH = 320
1923
DISPLAY_HEIGHT = 480
2024

2125

26+
class TuringError(Exception):
27+
pass
28+
29+
2230
class Command:
23-
RESET = 101
24-
CLEAR = 102
25-
SCREEN_OFF = 108
26-
SCREEN_ON = 109
27-
SET_BRIGHTNESS = 110
28-
DISPLAY_BITMAP = 197
31+
# Old protocol (6 byte packets, command in final byte)
32+
#RESET = 101
33+
#CLEAR = 102
34+
#SCREEN_OFF = 108
35+
#SCREEN_ON = 109
36+
#SET_BRIGHTNESS = 110
37+
#DISPLAY_BITMAP = 197
38+
39+
# New protocol (10 byte packets, framed with the command, 8 data bytes inside)
40+
HELLO = 0xCA
41+
ORIENTATION = 0xCB
42+
ORIENTATION_PORTRAIT = 0
43+
ORIENTATION_LANDSCAPE = 1
44+
# The device seems to start in PORTRAIT, with the row ordering reversed from
45+
# the ORIENTATION_PORTRAIT setting. It is not clear how to restore the ordering
46+
# to the reset configuration.
47+
DISPLAY_BITMAP = 0xCC
48+
LIGHTING = 0xCD
49+
SET_BRIGHTNESS = 0xCE
2950

3051

3152
def SendReg(ser: serial.Serial, cmd: int, x: int, y: int, ex: int, ey: int):
@@ -39,26 +60,83 @@ def SendReg(ser: serial.Serial, cmd: int, x: int, y: int, ex: int, ey: int):
3960
ser.write(bytes(byteBuffer))
4061

4162

42-
def Reset(ser: serial.Serial):
43-
SendReg(ser, Command.RESET, 0, 0, 0, 0)
63+
def SendCommand(ser: serial.Serial, cmd: int, payload=None):
64+
if payload is None:
65+
payload = [0] * 8
66+
elif len(payload) < 8:
67+
payload = list(payload) + [0] * (8 - len(payload))
68+
69+
byteBuffer = bytearray(10)
70+
byteBuffer[0] = cmd
71+
byteBuffer[1] = payload[0]
72+
byteBuffer[2] = payload[1]
73+
byteBuffer[3] = payload[2]
74+
byteBuffer[4] = payload[3]
75+
byteBuffer[5] = payload[4]
76+
byteBuffer[6] = payload[5]
77+
byteBuffer[7] = payload[6]
78+
byteBuffer[8] = payload[7]
79+
byteBuffer[9] = cmd
80+
print("Sending %r" % (byteBuffer,))
81+
ser.write(bytes(byteBuffer))
82+
83+
84+
def Hello(ser: serial.Serial):
85+
hello = [ord('H'), ord('E'), ord('L'), ord('L'), ord('O')]
86+
SendCommand(ser, Command.HELLO, payload=hello)
87+
response = ser.read(10)
88+
if len(response) != 10:
89+
raise TuringError("Device not recognised (short response to HELLO)")
90+
if response[0] != Command.HELLO or response[-1] != Command.HELLO:
91+
raise TuringError("Device not recognised (bad framing)")
92+
if [x for x in response[1:6]] != hello:
93+
raise TuringError("Device not recognised (No HELLO; got %r)" % (response[1:6],))
94+
# The HELLO response here is followed by:
95+
# 0x0A, 0x12, 0x00
96+
# It is not clear what these might be.
97+
# It would be handy if these were a version number, or a set of capability
98+
# flags. The 0x0A=10 being version 10 or 0.10, and the 0x12 being the size or the
99+
# indication that a backlight is present, would be nice. But that's guessing
100+
# based on how I'd do it.
44101

45102

46103
def Clear(ser: serial.Serial):
47-
SendReg(ser, Command.CLEAR, 0, 0, 0, 0)
104+
# Unknown what command this is
105+
print("Clear unknown")
106+
# Cannot find a 'clear' command
107+
#SendReg(ser, Command.CLEAR, 0, 0, 0, 0)
108+
109+
110+
def Orientation(ser: serial.Serial, state: int):
111+
print("Orientation: %r" % (state,))
112+
SendCommand(ser, Command.ORIENTATION, payload=[state])
113+
114+
115+
def SetLighting(ser: serial.Serial, red: int, green: int, blue: int):
116+
print("Lighting: %i, %i, %i" % (red, green, blue))
117+
assert red < 256, 'Red lighting must be < 256'
118+
assert green < 256, 'Green lighting must be < 256'
119+
assert blue < 256, 'Blue lighting must be < 256'
120+
SendCommand(ser, Command.LIGHTING, payload=[red, green, blue])
48121

49122

50123
def ScreenOff(ser: serial.Serial):
51-
SendReg(ser, Command.SCREEN_OFF, 0, 0, 0, 0)
124+
print("Screen off unknown")
125+
# Cannot find a 'screen off' command
126+
#SendReg(ser, Command.SCREEN_OFF, 0, 0, 0, 0)
52127

53128

54129
def ScreenOn(ser: serial.Serial):
55-
SendReg(ser, Command.SCREEN_ON, 0, 0, 0, 0)
130+
print("Screen on unknown")
131+
# Cannot find a 'screen on' command
132+
#SendReg(ser, Command.SCREEN_ON, 0, 0, 0, 0)
56133

57134

58135
def SetBrightness(ser: serial.Serial, level: int):
59136
# Level : 0 (brightest) - 255 (darkest)
60137
assert 255 >= level >= 0, 'Brightness level must be [0-255]'
61-
SendReg(ser, Command.SET_BRIGHTNESS, level, 0, 0, 0)
138+
# New protocol has 255 as the brightest, and 0 as off.
139+
SendCommand(ser, Command.SET_BRIGHTNESS, payload=[255-level])
62140

63141

64142
def DisplayPILImage(ser: serial.Serial, image: Image, x: int, y: int):
@@ -68,7 +146,14 @@ def DisplayPILImage(ser: serial.Serial, image: Image, x: int, y: int):
68146
assert image_height > 0, 'Image width must be > 0'
69147
assert image_width > 0, 'Image height must be > 0'
70148

71-
SendReg(ser, Command.DISPLAY_BITMAP, x, y, x + image_width - 1, y + image_height - 1)
149+
(x0, y0) = (x, y)
150+
(x1, y1) = (x + image_width - 1, y + image_height - 1)
151+
152+
SendCommand(ser, Command.DISPLAY_BITMAP,
153+
payload=[(x0>>8) & 255, x0 & 255,
154+
(y0>>8) & 255, y0 & 255,
155+
(x1>>8) & 255, x1 & 255,
156+
(y1>>8) & 255, y1 & 255])
72157

73158
pix = image.load()
74159
line = bytes()
@@ -78,7 +163,15 @@ def DisplayPILImage(ser: serial.Serial, image: Image, x: int, y: int):
78163
G = pix[w, h][1] >> 2
79164
B = pix[w, h][2] >> 3
80165

81-
rgb = (R << 11) | (G << 5) | B
166+
# Original: 0bRRRRRGGGGGGBBBBB
167+
# fedcba9876543210
168+
# New: 0bgggBBBBBRRRRRGGG
169+
# That is...
170+
# High 3 bits of green in b0-b2
171+
# Low 3 bits of green in b13-b15
172+
# Red 5 bits in b3-b7
173+
# Blue 5 bits in b8-b12
174+
rgb = (B << 8) | (G>>3) | ((G&7)<<13) | (R<<3)
82175
line += struct.pack('H', rgb)
83176

84177
# Send image data by multiple of DISPLAY_WIDTH bytes
@@ -183,12 +276,18 @@ def sighandler(signum, frame):
183276
# Do not change COM port settings unless you know what you are doing
184277
lcd_comm = serial.Serial(COM_PORT, 115200, timeout=1, rtscts=1)
185278

186-
# Clear screen (blank)
187-
Clear(lcd_comm)
279+
# Hello! to check this is the right device
280+
Hello(lcd_comm)
281+
282+
# Data orientation
283+
Orientation(lcd_comm, Command.ORIENTATION_PORTRAIT)
188284

189285
# Set brightness to max value
190286
SetBrightness(lcd_comm, 0)
191287

288+
# Lighting (a purple)
289+
SetLighting(lcd_comm, 128, 50, 112)
290+
192291
# Display sample picture
193292
DisplayBitmap(lcd_comm, "res/example.png")
194293

0 commit comments

Comments
 (0)