Skip to content

Commit 3f0b334

Browse files
committed
Add support for HW revision B/flagship, add RGB LED setting in theme for flagship revision
1 parent 85c2ede commit 3f0b334

File tree

9 files changed

+396
-159
lines changed

9 files changed

+396
-159
lines changed

config.yaml

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,16 +11,15 @@ config:
1111
THEME: 3.5inchTheme2_theme
1212

1313
display:
14-
# Display size (in portrait orientation)
14+
# Display resolution in portrait orientation
15+
# Do not use this setting to rotate display! Display orientation is managed by themes
1516
DISPLAY_WIDTH: 320
1617
DISPLAY_HEIGHT: 480
1718

1819
# Display Brightness
1920
# Set this as the desired %, 0 being completely dark and 100 being max brightness
2021
BRIGHTNESS: 20
2122

22-
# Display orientation is configured by themes
23-
2423
# Display revision: A or B (for "flagship" version, use B)
2524
# To identify your revision: https://github.com/mathoudebine/turing-smart-screen-python/wiki/Hardware-revisions
2625
REVISION: A

library/display.py

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
from library import config
2-
from library.lcd_comm_rev_a import LcdCommRevA, Orientation
2+
from library.lcd_comm import Orientation
3+
from library.lcd_comm_rev_a import LcdCommRevA
4+
from library.lcd_comm_rev_b import LcdCommRevB
35

46
THEME_DATA = config.THEME_DATA
57
CONFIG_DATA = config.CONFIG_DATA
@@ -18,18 +20,27 @@ def __init__(self):
1820
if CONFIG_DATA["display"]["REVISION"] == "A":
1921
self.lcd = LcdCommRevA()
2022
elif CONFIG_DATA["display"]["REVISION"] == "B":
21-
pass
23+
self.lcd = LcdCommRevB()
2224
else:
2325
print("Unknown display revision '", CONFIG_DATA["display"]["REVISION"], "'")
2426

2527
def initialize_display(self):
28+
# Send initialization commands
29+
self.lcd.InitializeComm()
30+
31+
# Reset screen in case it was in an unstable state
32+
self.lcd.Reset()
33+
2634
# Clear screen (blank)
2735
self.lcd.SetOrientation(Orientation.PORTRAIT) # Bug: orientation needs to be PORTRAIT before clearing
2836
self.lcd.Clear()
2937

3038
# Set brightness
3139
self.lcd.SetBrightness()
3240

41+
# Set backplate RGB LED color (for supported HW only)
42+
self.lcd.SetBackplateLedColor()
43+
3344
# Set orientation
3445
self.lcd.SetOrientation()
3546

@@ -53,7 +64,7 @@ def display_static_text(self):
5364
text=THEME_DATA['static_text'][text].get("TEXT"),
5465
x=THEME_DATA['static_text'][text].get("X", 0),
5566
y=THEME_DATA['static_text'][text].get("Y", 0),
56-
font=THEME_DATA['static_text'][text].get("FONT", "roboto/Roboto-Regular.ttf"),
67+
font=THEME_DATA['static_text'][text].get("FONT", "roboto-mono/RobotoMono-Regular.ttf"),
5768
font_size=THEME_DATA['static_text'][text].get("FONT_SIZE", 10),
5869
font_color=THEME_DATA['static_text'][text].get("FONT_COLOR", (0, 0, 0)),
5970
background_color=THEME_DATA['static_text'][text].get("BACKGROUND_COLOR", (255, 255, 255)),

library/lcd_comm.py

Lines changed: 115 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
from abc import ABC, abstractmethod
22
from enum import IntEnum
3+
from PIL import Image, ImageDraw, ImageFont
34

45
from library import config
56

@@ -43,6 +44,10 @@ def get_height() -> int:
4344

4445

4546
class LcdComm(ABC):
47+
@abstractmethod
48+
def InitializeComm(self):
49+
pass
50+
4651
@abstractmethod
4752
def Reset(self):
4853
pass
@@ -64,33 +69,123 @@ def SetBrightness(self, level: int):
6469
pass
6570

6671
@abstractmethod
67-
def SetOrientation(self, orientation: Orientation):
72+
def SetBackplateLedColor(self, led_color: tuple[int, int, int]):
6873
pass
6974

7075
@abstractmethod
71-
def DisplayBitmap(self, bitmap_path: str, x: int, y: int, width: int, height: int):
76+
def SetOrientation(self, orientation: Orientation):
7277
pass
7378

7479
@abstractmethod
75-
def DisplayText(
80+
def DisplayPILImage(
7681
self,
77-
text: str,
78-
x: int,
79-
y: int,
80-
font: str,
81-
font_size: int,
82-
font_color: tuple[int, int, int],
83-
background_color: tuple[int, int, int],
84-
background_image: str
82+
image: Image,
83+
x: int = 0, y: int = 0,
84+
image_width: int = 0,
85+
image_height: int = 0
8586
):
8687
pass
8788

88-
@abstractmethod
89-
def DisplayProgressBar(self, x: int, y: int, width: int, height: int, min_value: int,
90-
max_value: int,
91-
value: int,
92-
bar_color: tuple[int, int, int],
93-
bar_outline: bool,
94-
background_color: tuple[int, int, int],
95-
background_image: str):
96-
pass
89+
def DisplayBitmap(self, bitmap_path: str, x: int = 0, y: int = 0, width: int = 0, height: int = 0):
90+
image = Image.open(bitmap_path)
91+
self.DisplayPILImage(image, x, y, width, height)
92+
93+
def DisplayText(
94+
self,
95+
text: str,
96+
x: int = 0,
97+
y: int = 0,
98+
font: str = "roboto-mono/RobotoMono-Regular.ttf",
99+
font_size: int = 20,
100+
font_color: tuple[int, int, int] = (0, 0, 0),
101+
background_color: tuple[int, int, int] = (255, 255, 255),
102+
background_image: str = None
103+
):
104+
# Convert text to bitmap using PIL and display it
105+
# Provide the background image path to display text with transparent background
106+
107+
if isinstance(font_color, str):
108+
font_color = tuple(map(int, font_color.split(', ')))
109+
110+
if isinstance(background_color, str):
111+
background_color = tuple(map(int, background_color.split(', ')))
112+
113+
assert x <= get_width(), 'Text X coordinate must be <= display width'
114+
assert y <= get_height(), 'Text Y coordinate must be <= display height'
115+
assert len(text) > 0, 'Text must not be empty'
116+
assert font_size > 0, "Font size must be > 0"
117+
118+
if background_image is None:
119+
# A text bitmap is created with max width/height by default : text with solid background
120+
text_image = Image.new(
121+
'RGB',
122+
(get_width(), get_height()),
123+
background_color
124+
)
125+
else:
126+
# The text bitmap is created from provided background image : text with transparent background
127+
text_image = Image.open(background_image)
128+
129+
# Draw text with specified color & font
130+
font = ImageFont.truetype("./res/fonts/" + font, font_size)
131+
d = ImageDraw.Draw(text_image)
132+
d.text((x, y), text, font=font, fill=font_color)
133+
134+
# Crop text bitmap to keep only the text (also crop if text overflows display)
135+
left, top, text_width, text_height = d.textbbox((0, 0), text, font=font)
136+
text_image = text_image.crop(box=(
137+
x, y,
138+
min(x + text_width, get_width()),
139+
min(y + text_height, get_height())
140+
))
141+
142+
self.DisplayPILImage(text_image, x, y)
143+
144+
def DisplayProgressBar(self, x: int, y: int, width: int, height: int, min_value: int = 0, max_value: int = 100,
145+
value: int = 50,
146+
bar_color: tuple[int, int, int] = (0, 0, 0),
147+
bar_outline: bool = True,
148+
background_color: tuple[int, int, int] = (255, 255, 255),
149+
background_image: str = None):
150+
# Generate a progress bar and display it
151+
# Provide the background image path to display progress bar with transparent background
152+
153+
if isinstance(bar_color, str):
154+
bar_color = tuple(map(int, bar_color.split(', ')))
155+
156+
if isinstance(background_color, str):
157+
background_color = tuple(map(int, background_color.split(', ')))
158+
159+
assert x <= get_width(), 'Progress bar X coordinate must be <= display width'
160+
assert y <= get_height(), 'Progress bar Y coordinate must be <= display height'
161+
assert x + width <= get_width(), 'Progress bar width exceeds display width'
162+
assert y + height <= get_height(), 'Progress bar height exceeds display height'
163+
164+
# Don't let the set value exceed our min or max value, this is bad :)
165+
if value < min_value:
166+
value = min_value
167+
elif max_value < value:
168+
value = max_value
169+
170+
assert min_value <= value <= max_value, 'Progress bar value shall be between min and max'
171+
172+
if background_image is None:
173+
# A bitmap is created with solid background
174+
bar_image = Image.new('RGB', (width, height), background_color)
175+
else:
176+
# A bitmap is created from provided background image
177+
bar_image = Image.open(background_image)
178+
179+
# Crop bitmap to keep only the progress bar background
180+
bar_image = bar_image.crop(box=(x, y, x + width, y + height))
181+
182+
# Draw progress bar
183+
bar_filled_width = value / (max_value - min_value) * width
184+
draw = ImageDraw.Draw(bar_image)
185+
draw.rectangle([0, 0, bar_filled_width - 1, height - 1], fill=bar_color, outline=bar_color)
186+
187+
if bar_outline:
188+
# Draw outline
189+
draw.rectangle([0, 0, width - 1, height - 1], fill=None, outline=bar_color)
190+
191+
self.DisplayPILImage(bar_image, x, y)

0 commit comments

Comments
 (0)