Skip to content

feat: implement basic support for turing 8.8 inch screen #585

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
16 changes: 10 additions & 6 deletions config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ config:

# Theme to use (located in res/themes)
# Use the name of the folder as value
# Choose a theme made for your screen size (see DISPLAY_SIZE inside theme.yaml)
THEME: 3.5inchTheme2

# Hardware sensors reading
Expand All @@ -35,13 +36,16 @@ config:

display:
# Display revision:
# - A for Turing 3.5" and UsbPCMonitor 3.5"/5"
# - B for Xuanfang 3.5" (inc. flagship)
# - C for Turing 5"
# - D for Kipye Qiye Smart Display 3.5"
# - SIMU for 3.5" simulated LCD (image written in screencap.png)
# - SIMU5 for 5" simulated LCD
# - A for Turing 3.5" and UsbPCMonitor 3.5"/5"
# - B for Xuanfang 3.5" (inc. flagship)
# - C for Turing 2.1"/5"/8.8"
# - D for Kipye Qiye Smart Display 3.5"
# To identify your smart screen: https://github.com/mathoudebine/turing-smart-screen-python/wiki/Hardware-revisions
# For simulated displays (image written in screencap.png):
# - SIMU2.1 for 2.1" simulated LCD
# - SIMU3.5 for 3.5" simulated LCD
# - SIMU5 for 5" simulated LCD
# - SIMU8.8 for 8.8" simulated LCD
REVISION: A

# Display Brightness
Expand Down
72 changes: 42 additions & 30 deletions configure.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,34 +73,44 @@
SIZE_8_8_INCH = "8.8\""
SIZE_2_1_INCH = "2.1\""

size_list = (SIZE_3_5_INCH, SIZE_5_INCH)
size_list = (SIZE_2_1_INCH, SIZE_3_5_INCH, SIZE_5_INCH, SIZE_8_8_INCH)

# Maps between config.yaml values and GUI description
revision_and_size_to_model_map = {
('A', SIZE_3_5_INCH): TURING_MODEL, # Can also be UsbPCMonitor 3.5, does not matter since protocol is the same
('A', SIZE_5_INCH): USBPCMONITOR_MODEL,
('B', SIZE_3_5_INCH): XUANFANG_MODEL,
('C', SIZE_2_1_INCH): TURING_MODEL,
('C', SIZE_5_INCH): TURING_MODEL,
('D', SIZE_3_5_INCH): KIPYE_MODEL,
('E', SIZE_8_8_INCH): TURING_MODEL,
('SIMU2.1', SIZE_2_1_INCH): SIMULATED_MODEL,
('SIMU', SIZE_3_5_INCH): SIMULATED_MODEL,
('SIMU3.5', SIZE_3_5_INCH): SIMULATED_MODEL,
('SIMU5', SIZE_5_INCH): SIMULATED_MODEL,
('SIMU8.8', SIZE_8_8_INCH): SIMULATED_MODEL,
}
model_and_size_to_revision_map = {
(TURING_MODEL, SIZE_3_5_INCH): 'A',
(USBPCMONITOR_MODEL, SIZE_3_5_INCH): 'A',
(USBPCMONITOR_MODEL, SIZE_5_INCH): 'A',
(XUANFANG_MODEL, SIZE_3_5_INCH): 'B',
(TURING_MODEL, SIZE_5_INCH): 'C',
(TURING_MODEL, SIZE_2_1_INCH): 'C',
(TURING_MODEL, SIZE_5_INCH): 'C',
(KIPYE_MODEL, SIZE_3_5_INCH): 'D',
(SIMULATED_MODEL, SIZE_3_5_INCH): 'SIMU',
(TURING_MODEL, SIZE_8_8_INCH): 'E',
(SIMULATED_MODEL, SIZE_2_1_INCH): 'SIMU2.1',
(SIMULATED_MODEL, SIZE_3_5_INCH): 'SIMU3.5',
(SIMULATED_MODEL, SIZE_5_INCH): 'SIMU5',
(SIMULATED_MODEL, SIZE_8_8_INCH): 'SIMU8.8',
}
hw_lib_map = {"AUTO": "Automatic", "LHM": "LibreHardwareMonitor (admin.)", "PYTHON": "Python libraries",
"STUB": "Fake random data", "STATIC": "Fake static data"}
reverse_map = {False: "classic", True: "reverse"}

themes_dir = 'res/themes'

circular_mask = Image.open("res/backgrounds/circular-mask.png")

def get_theme_data(name: str):
dir = os.path.join(themes_dir, name)
Expand Down Expand Up @@ -177,69 +187,69 @@ def __init__(self):
self.theme_author = ttk.Label(self.window)

sysmon_label = ttk.Label(self.window, text='Display configuration', font='bold')
sysmon_label.place(x=320, y=0)
sysmon_label.place(x=370, y=0)

self.model_label = ttk.Label(self.window, text='Smart screen model')
self.model_label.place(x=320, y=35)
self.model_label.place(x=370, y=35)
self.model_cb = ttk.Combobox(self.window, values=list(dict.fromkeys((revision_and_size_to_model_map.values()))),
state='readonly')
self.model_cb.bind('<<ComboboxSelected>>', self.on_model_change)
self.model_cb.place(x=500, y=30, width=250)
self.model_cb.place(x=550, y=30, width=250)

self.size_label = ttk.Label(self.window, text='Smart screen size')
self.size_label.place(x=320, y=75)
self.size_label.place(x=370, y=75)
self.size_cb = ttk.Combobox(self.window, values=size_list, state='readonly')
self.size_cb.bind('<<ComboboxSelected>>', self.on_size_change)
self.size_cb.place(x=500, y=70, width=250)
self.size_cb.place(x=550, y=70, width=250)

self.com_label = ttk.Label(self.window, text='COM port')
self.com_label.place(x=320, y=115)
self.com_label.place(x=370, y=115)
self.com_cb = ttk.Combobox(self.window, values=get_com_ports(), state='readonly')
self.com_cb.place(x=500, y=110, width=250)
self.com_cb.place(x=550, y=110, width=250)

self.orient_label = ttk.Label(self.window, text='Orientation')
self.orient_label.place(x=320, y=155)
self.orient_label.place(x=370, y=155)
self.orient_cb = ttk.Combobox(self.window, values=list(reverse_map.values()), state='readonly')
self.orient_cb.place(x=500, y=150, width=250)
self.orient_cb.place(x=550, y=150, width=250)

self.brightness_string = StringVar()
self.brightness_label = ttk.Label(self.window, text='Brightness')
self.brightness_label.place(x=320, y=195)
self.brightness_label.place(x=370, y=195)
self.brightness_slider = ttk.Scale(self.window, from_=0, to=100, orient=HORIZONTAL,
command=self.on_brightness_change)
self.brightness_slider.place(x=550, y=195, width=180)
self.brightness_slider.place(x=600, y=195, width=180)
self.brightness_val_label = ttk.Label(self.window, textvariable=self.brightness_string)
self.brightness_val_label.place(x=500, y=195)
self.brightness_val_label.place(x=550, y=195)
self.brightness_warning_label = ttk.Label(self.window,
text="⚠ Turing 3.5\" displays can get hot at high brightness!",
foreground='#ff8c00')

sysmon_label = ttk.Label(self.window, text='System Monitor Configuration', font='bold')
sysmon_label.place(x=320, y=260)
sysmon_label.place(x=370, y=260)

self.theme_label = ttk.Label(self.window, text='Theme')
self.theme_label.place(x=320, y=300)
self.theme_label.place(x=370, y=300)
self.theme_cb = ttk.Combobox(self.window, state='readonly')
self.theme_cb.place(x=500, y=295, width=250)
self.theme_cb.place(x=550, y=295, width=250)
self.theme_cb.bind('<<ComboboxSelected>>', self.on_theme_change)

self.hwlib_label = ttk.Label(self.window, text='Hardware monitoring')
self.hwlib_label.place(x=320, y=340)
self.hwlib_label.place(x=370, y=340)
if sys.platform != "win32":
del hw_lib_map["LHM"] # LHM is for Windows platforms only
self.hwlib_cb = ttk.Combobox(self.window, values=list(hw_lib_map.values()), state='readonly')
self.hwlib_cb.place(x=500, y=335, width=250)
self.hwlib_cb.place(x=550, y=335, width=250)
self.hwlib_cb.bind('<<ComboboxSelected>>', self.on_hwlib_change)

self.eth_label = ttk.Label(self.window, text='Ethernet interface')
self.eth_label.place(x=320, y=380)
self.eth_label.place(x=370, y=380)
self.eth_cb = ttk.Combobox(self.window, values=get_net_if(), state='readonly')
self.eth_cb.place(x=500, y=375, width=250)
self.eth_cb.place(x=550, y=375, width=250)

self.wl_label = ttk.Label(self.window, text='Wi-Fi interface')
self.wl_label.place(x=320, y=420)
self.wl_label.place(x=370, y=420)
self.wl_cb = ttk.Combobox(self.window, values=get_net_if(), state='readonly')
self.wl_cb.place(x=500, y=415, width=250)
self.wl_cb.place(x=550, y=415, width=250)

# For Windows platform only
self.lhm_admin_warning = ttk.Label(self.window,
Expand Down Expand Up @@ -272,19 +282,21 @@ def run(self):
self.window.mainloop()

def load_theme_preview(self):
theme_data = get_theme_data(self.theme_cb.get())

try:
theme_preview = Image.open("res/themes/" + self.theme_cb.get() + "/preview.png")

if theme_data['display'].get("DISPLAY_SIZE", '3.5"') == '2.1"':
# This is a circular screen: apply a circle mask over the preview
theme_preview.paste(circular_mask, mask=circular_mask)
except:
theme_preview = Image.open("res/docs/no-preview.png")
finally:
if theme_preview.width > theme_preview.height:
theme_preview = theme_preview.resize((300, 200), Image.Resampling.LANCZOS)
else:
theme_preview = theme_preview.resize((280, 420), Image.Resampling.LANCZOS)
theme_preview.thumbnail((320, 480), Image.Resampling.LANCZOS)
self.theme_preview_img = ImageTk.PhotoImage(theme_preview)
self.theme_preview.config(image=self.theme_preview_img)

theme_data = get_theme_data(self.theme_cb.get())
author_name = theme_data.get('author', 'unknown')
self.theme_author.config(text="Author: " + author_name)
if author_name.startswith("@"):
Expand Down Expand Up @@ -478,7 +490,7 @@ def on_hwlib_change(self, e=None):
def show_hide_brightness_warning(self, e=None):
if int(self.brightness_slider.get()) > 50 and self.model_cb.get() == TURING_MODEL and self.size_cb.get() == SIZE_3_5_INCH:
# Show warning for Turing Smart screen 3.5 with high brightness
self.brightness_warning_label.place(x=320, y=225)
self.brightness_warning_label.place(x=370, y=225)
else:
self.brightness_warning_label.place_forget()

Expand Down
13 changes: 12 additions & 1 deletion library/display.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
from library.lcd.lcd_comm_rev_b import LcdCommRevB
from library.lcd.lcd_comm_rev_c import LcdCommRevC
from library.lcd.lcd_comm_rev_d import LcdCommRevD
from library.lcd.lcd_comm_rev_e import LcdCommRevE
from library.lcd.lcd_simulated import LcdSimulated
from library.log import logger

Expand Down Expand Up @@ -67,12 +68,22 @@ def __init__(self):
elif config.CONFIG_DATA["display"]["REVISION"] == "D":
self.lcd = LcdCommRevD(com_port=config.CONFIG_DATA['config']['COM_PORT'],
update_queue=config.update_queue)
elif config.CONFIG_DATA["display"]["REVISION"] == "SIMU":
elif config.CONFIG_DATA["display"]["REVISION"] == "E":
self.lcd = LcdCommRevE(com_port=config.CONFIG_DATA['config']['COM_PORT'],
update_queue=config.update_queue)
elif (config.CONFIG_DATA["display"]["REVISION"] == "SIMU"
or config.CONFIG_DATA["display"]["REVISION"] == "SIMU3.5"):
self.lcd = LcdSimulated(display_width=320,
display_height=480)
elif config.CONFIG_DATA["display"]["REVISION"] == "SIMU5":
self.lcd = LcdSimulated(display_width=480,
display_height=800)
elif config.CONFIG_DATA["display"]["REVISION"] == "SIMU2.1":
self.lcd = LcdSimulated(display_width=480,
display_height=480)
elif config.CONFIG_DATA["display"]["REVISION"] == "SIMU8.8":
self.lcd = LcdSimulated(display_width=1920,
display_height=480)
else:
logger.error("Unknown display revision '", config.CONFIG_DATA["display"]["REVISION"], "'")

Expand Down
8 changes: 4 additions & 4 deletions library/lcd/lcd_comm_rev_a.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,14 +64,14 @@ def __del__(self):
@staticmethod
def auto_detect_com_port():
com_ports = comports()
auto_com_port = None

for com_port in com_ports:
if com_port.serial_number == "USB35INCHIPSV2":
auto_com_port = com_port.device
break
return com_port.device
if com_port.vid == 0x1a86 and com_port.pid == 0x5722:
return com_port.device

return auto_com_port
return None

def SendCommand(self, cmd: Command, x: int, y: int, ex: int, ey: int, bypass_queue: bool = False):
byteBuffer = bytearray(6)
Expand Down
8 changes: 4 additions & 4 deletions library/lcd/lcd_comm_rev_b.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,14 +69,14 @@ def is_brightness_range(self):
@staticmethod
def auto_detect_com_port():
com_ports = comports()
auto_com_port = None

for com_port in com_ports:
if com_port.serial_number == "2017-2-25":
auto_com_port = com_port.device
break
return com_port.device
if com_port.vid == 0x1a86 and com_port.pid == 0x5722:
return com_port.device

return auto_com_port
return None

def SendCommand(self, cmd: Command, payload=None, bypass_queue: bool = False):
# New protocol (10 byte packets, framed with the command, 8 data bytes inside)
Expand Down
33 changes: 27 additions & 6 deletions library/lcd/lcd_comm_rev_c.py
Original file line number Diff line number Diff line change
Expand Up @@ -119,8 +119,10 @@ def __init__(self, command):


class SubRevision(Enum):
UNKNOWN = ""
FIVEINCH = "chs_5inch"
UNKNOWN = None
FIVEINCH = bytearray(b'chs_5inch.dev1_rom1.87\x00')
TWOINCH = bytearray(b'chs_5inch.dev1_rom1.88\x00')
EIGHTINCH = bytearray(b'chs_88inch.dev1_rom1.88')

def __init__(self, command):
self.command = command
Expand All @@ -141,12 +143,20 @@ def __del__(self):
def auto_detect_com_port():
com_ports = comports()

# Try to find awake device through serial number or vid/pid
for com_port in com_ports:
if com_port.serial_number == 'USB7INCH':
LcdCommRevC._connect_to_reset_device_name(com_port)
return LcdCommRevC.auto_detect_com_port()
if com_port.serial_number == '20080411':
return com_port.device
if com_port.vid == 0x0525 and com_port.pid == 0xa4a7:
return com_port.device
if com_port.vid == 0x1d6b and (com_port.pid == 0x0121 or com_port.pid == 0x0106):
return com_port.device

# Try to find sleeping device and wake it up
for com_port in com_ports:
if com_port.serial_number == 'USB7INCH' or com_port.serial_number == 'CT21INCH':
LcdCommRevC._connect_to_reset_device_name(com_port)
return LcdCommRevC.auto_detect_com_port()

return None

Expand Down Expand Up @@ -200,6 +210,16 @@ def _hello(self):
self.lcd_serial.flushInput()
if response.startswith(SubRevision.FIVEINCH.value):
self.sub_revision = SubRevision.FIVEINCH
self.display_width = 480
self.display_height = 800
elif response == SubRevision.TWOINCH.value:
self.sub_revision = SubRevision.TWOINCH
self.display_width = 480
self.display_height = 480
elif response == SubRevision.EIGHTINCH.value:
self.sub_revision = SubRevision.EIGHTINCH
self.display_width = 480
self.display_height = 1920
else:
logger.warning("Display returned unknown sub-revision on Hello answer (%s)" % str(response))

Expand Down Expand Up @@ -290,7 +310,8 @@ def DisplayPILImage(
with self.update_queue_mutex:
self._send_command(Command.PRE_UPDATE_BITMAP)
self._send_command(Command.START_DISPLAY_BITMAP, padding=Padding.START_DISPLAY_BITMAP)
self._send_command(Command.DISPLAY_BITMAP)
self._send_command(Command.DISPLAY_BITMAP,
payload=bytearray(int(self.display_width * self.display_width / 64).to_bytes(2)))
self._send_command(Command.SEND_PAYLOAD,
payload=bytearray(self._generate_full_image(image, self.orientation)),
readsize=1024)
Expand Down
6 changes: 2 additions & 4 deletions library/lcd/lcd_comm_rev_d.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,14 +52,12 @@ def __del__(self):
@staticmethod
def auto_detect_com_port():
com_ports = comports()
auto_com_port = None

for com_port in com_ports:
if com_port.vid == 0x454d and com_port.pid == 0x4e41:
auto_com_port = com_port.device
break
return com_port.device

return auto_com_port
return None

def WriteData(self, byteBuffer: bytearray):
LcdComm.WriteData(self, byteBuffer)
Expand Down
Loading