Skip to content

Commit 695dfb4

Browse files
authored
Merge pull request #30 from mathoudebine/feature/6-screen-orientation
Implement screen rotation for version A, harmonize with version B
2 parents 3462f7a + 57bdef1 commit 695dfb4

File tree

4 files changed

+149
-64
lines changed

4 files changed

+149
-64
lines changed

README.md

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,14 +34,12 @@ This Python script can do some simple operations on the Turing display like :
3434
- **Display custom picture**
3535
- **Display text**
3636
- **Display progress bar**
37+
- **Screen rotation**
3738
- Clear the screen (blank) - HW version A only
3839
- Turn the screen on/off - HW version A only
3940
- Display soft reset - HW version A only
4041
- Set brightness
4142

42-
Not yet implemented:
43-
- Screen rotation
44-
4543
Operating systems supported : macOS, Windows, Linux (incl. Raspberry Pi) and all OS that support Python3.7
4644

4745
## Getting started

mainVersionA.py

Lines changed: 82 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import signal
77
import struct
88
from datetime import datetime
9+
from enum import IntEnum
910
from time import sleep
1011

1112
import serial # Install pyserial : pip install pyserial
@@ -19,16 +20,43 @@
1920
DISPLAY_HEIGHT = 480
2021

2122

22-
class Command:
23+
class Orientation(IntEnum):
24+
PORTRAIT = 0
25+
LANDSCAPE = 2
26+
REVERSE_PORTRAIT = 1
27+
REVERSE_LANDSCAPE = 3
28+
29+
30+
CUR_ORIENTATION = Orientation.PORTRAIT
31+
32+
33+
def getWidth():
34+
global CUR_ORIENTATION
35+
if CUR_ORIENTATION == Orientation.PORTRAIT or CUR_ORIENTATION == Orientation.REVERSE_PORTRAIT:
36+
return DISPLAY_WIDTH
37+
else:
38+
return DISPLAY_HEIGHT
39+
40+
41+
def getHeight():
42+
global CUR_ORIENTATION
43+
if CUR_ORIENTATION == Orientation.PORTRAIT or CUR_ORIENTATION == Orientation.REVERSE_PORTRAIT:
44+
return DISPLAY_HEIGHT
45+
else:
46+
return DISPLAY_WIDTH
47+
48+
49+
class Command(IntEnum):
2350
RESET = 101
2451
CLEAR = 102
2552
SCREEN_OFF = 108
2653
SCREEN_ON = 109
2754
SET_BRIGHTNESS = 110
2855
DISPLAY_BITMAP = 197
56+
SET_ORIENTATION = 121
2957

3058

31-
def SendReg(ser: serial.Serial, cmd: int, x: int, y: int, ex: int, ey: int):
59+
def SendReg(ser: serial.Serial, cmd: Command, x: int, y: int, ex: int, ey: int):
3260
byteBuffer = bytearray(6)
3361
byteBuffer[0] = (x >> 2)
3462
byteBuffer[1] = (((x & 3) << 6) + (y >> 4))
@@ -61,10 +89,36 @@ def SetBrightness(ser: serial.Serial, level: int):
6189
SendReg(ser, Command.SET_BRIGHTNESS, level, 0, 0, 0)
6290

6391

92+
def SetOrientation(ser: serial.Serial, orientation: Orientation):
93+
global CUR_ORIENTATION
94+
CUR_ORIENTATION = orientation
95+
width = getWidth()
96+
height = getHeight()
97+
x = 0
98+
y = 0
99+
ex = 0
100+
ey = 0
101+
byteBuffer = bytearray(11)
102+
byteBuffer[0] = (x >> 2)
103+
byteBuffer[1] = (((x & 3) << 6) + (y >> 4))
104+
byteBuffer[2] = (((y & 15) << 4) + (ex >> 6))
105+
byteBuffer[3] = (((ex & 63) << 2) + (ey >> 8))
106+
byteBuffer[4] = (ey & 255)
107+
byteBuffer[5] = Command.SET_ORIENTATION
108+
byteBuffer[6] = (CUR_ORIENTATION + 100)
109+
byteBuffer[7] = (width >> 8)
110+
byteBuffer[8] = (width & 255)
111+
byteBuffer[9] = (height >> 8)
112+
byteBuffer[10] = (height & 255)
113+
ser.write(bytes(byteBuffer))
114+
115+
64116
def DisplayPILImage(ser: serial.Serial, image: Image, x: int, y: int):
65117
image_height = image.size[1]
66118
image_width = image.size[0]
67119

120+
assert x <= getWidth(), 'Image X coordinate must be <= display width'
121+
assert y <= getHeight(), 'Image Y coordinate must be <= display height'
68122
assert image_height > 0, 'Image width must be > 0'
69123
assert image_width > 0, 'Image height must be > 0'
70124

@@ -82,7 +136,7 @@ def DisplayPILImage(ser: serial.Serial, image: Image, x: int, y: int):
82136
line += struct.pack('H', rgb)
83137

84138
# Send image data by multiple of DISPLAY_WIDTH bytes
85-
if len(line) >= DISPLAY_WIDTH * 8:
139+
if len(line) >= getWidth() * 8:
86140
ser.write(line)
87141
line = bytes()
88142

@@ -107,12 +161,14 @@ def DisplayText(ser: serial.Serial, text: str, x=0, y=0,
107161
# Convert text to bitmap using PIL and display it
108162
# Provide the background image path to display text with transparent background
109163

164+
assert x <= getWidth(), 'Text X coordinate must be <= display width'
165+
assert y <= getHeight(), 'Text Y coordinate must be <= display height'
110166
assert len(text) > 0, 'Text must not be empty'
111167
assert font_size > 0, "Font size must be > 0"
112168

113169
if background_image is None:
114170
# A text bitmap is created with max width/height by default : text with solid background
115-
text_image = Image.new('RGB', (DISPLAY_WIDTH, DISPLAY_HEIGHT), background_color)
171+
text_image = Image.new('RGB', (getWidth(), getHeight()), background_color)
116172
else:
117173
# The text bitmap is created from provided background image : text with transparent background
118174
text_image = Image.open(background_image)
@@ -123,8 +179,8 @@ def DisplayText(ser: serial.Serial, text: str, x=0, y=0,
123179
d.text((x, y), text, font=font, fill=font_color)
124180

125181
# Crop text bitmap to keep only the text
126-
left, top, text_width, text_height = d.textbbox((0,0), text, font=font)
127-
text_image = text_image.crop(box=(x, y, min(x + text_width, DISPLAY_WIDTH), min(y + text_height, DISPLAY_HEIGHT)))
182+
left, top, text_width, text_height = d.textbbox((0, 0), text, font=font)
183+
text_image = text_image.crop(box=(x, y, min(x + text_width, getWidth()), min(y + text_height, getHeight())))
128184

129185
DisplayPILImage(ser, text_image, x, y)
130186

@@ -138,8 +194,10 @@ def DisplayProgressBar(ser: serial.Serial, x: int, y: int, width: int, height: i
138194
# Generate a progress bar and display it
139195
# Provide the background image path to display progress bar with transparent background
140196

141-
assert x + width <= DISPLAY_WIDTH, 'Progress bar width exceeds display width'
142-
assert y + height <= DISPLAY_HEIGHT, 'Progress bar height exceeds display height'
197+
assert x <= getWidth(), 'Progress bar X coordinate must be <= display width'
198+
assert y <= getHeight(), 'Progress bar Y coordinate must be <= display height'
199+
assert x + width <= getWidth(), 'Progress bar width exceeds display width'
200+
assert y + height <= getHeight(), 'Progress bar height exceeds display height'
143201
assert min_value <= value <= max_value, 'Progress bar value shall be between min and max'
144202

145203
if background_image is None:
@@ -155,11 +213,11 @@ def DisplayProgressBar(ser: serial.Serial, x: int, y: int, width: int, height: i
155213
# Draw progress bar
156214
bar_filled_width = value / (max_value - min_value) * width
157215
draw = ImageDraw.Draw(bar_image)
158-
draw.rectangle([0, 0, bar_filled_width-1, height-1], fill=bar_color, outline=bar_color)
216+
draw.rectangle([0, 0, bar_filled_width - 1, height - 1], fill=bar_color, outline=bar_color)
159217

160218
if bar_outline:
161219
# Draw outline
162-
draw.rectangle([0, 0, width-1, height-1], fill=None, outline=bar_color)
220+
draw.rectangle([0, 0, width - 1, height - 1], fill=None, outline=bar_color)
163221

164222
DisplayPILImage(ser, bar_image, x, y)
165223

@@ -184,13 +242,21 @@ def sighandler(signum, frame):
184242
lcd_comm = serial.Serial(COM_PORT, 115200, timeout=1, rtscts=1)
185243

186244
# Clear screen (blank)
245+
SetOrientation(lcd_comm, Orientation.PORTRAIT) # Bug: orientation needs to be PORTRAIT before clearing screen
187246
Clear(lcd_comm)
188247

189248
# Set brightness to max value
190249
SetBrightness(lcd_comm, 0)
191250

251+
# Set screen orientation
252+
# SetOrientation(lcd_comm, Orientation.LANDSCAPE)
253+
254+
# Define background picture
255+
background = "res/example.png"
256+
# background = "res/example_landscape.jpg"
257+
192258
# Display sample picture
193-
DisplayBitmap(lcd_comm, "res/example.png")
259+
DisplayBitmap(lcd_comm, background)
194260

195261
# Display sample text
196262
DisplayText(lcd_comm, "Basic text", 50, 100)
@@ -203,18 +269,11 @@ def sighandler(signum, frame):
203269
background_color=(255, 255, 0))
204270

205271
# Display custom text with transparent background
206-
DisplayText(lcd_comm, "Transparent bold text", 5, 300,
272+
DisplayText(lcd_comm, "Transparent bold text", 5, 250,
207273
font="geforce/GeForce-Bold.ttf",
208274
font_size=30,
209275
font_color=(255, 255, 255),
210-
background_image="res/example.png")
211-
212-
# Display text that overflows
213-
DisplayText(lcd_comm, "Text overflow!", 5, 430,
214-
font="roboto/Roboto-Bold.ttf",
215-
font_size=60,
216-
font_color=(255, 255, 255),
217-
background_image="res/example.png")
276+
background_image=background)
218277

219278
# Display the current time and some progress bars as fast as possible
220279
bar_value = 0
@@ -223,19 +282,19 @@ def sighandler(signum, frame):
223282
font="roboto/Roboto-Bold.ttf",
224283
font_size=20,
225284
font_color=(255, 0, 0),
226-
background_image="res/example.png")
285+
background_image=background)
227286

228287
DisplayProgressBar(lcd_comm, 10, 40,
229288
width=140, height=30,
230289
min_value=0, max_value=100, value=bar_value,
231290
bar_color=(255, 255, 0), bar_outline=True,
232-
background_image="res/example.png")
291+
background_image=background)
233292

234293
DisplayProgressBar(lcd_comm, 160, 40,
235294
width=140, height=30,
236295
min_value=0, max_value=19, value=bar_value % 20,
237296
bar_color=(0, 255, 0), bar_outline=False,
238-
background_image="res/example.png")
297+
background_image=background)
239298

240299
bar_value = (bar_value + 2) % 101
241300

0 commit comments

Comments
 (0)