From 6a5d69d9f8f0ab3326e082f265c4d68375d53e33 Mon Sep 17 00:00:00 2001 From: Matthieu Houdebine Date: Sat, 5 Apr 2025 12:28:32 +0200 Subject: [PATCH 1/6] Try to enable back the "orientation" command for rev. C --- library/lcd/lcd_comm_rev_c.py | 31 ++++++++++++++++--------------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/library/lcd/lcd_comm_rev_c.py b/library/lcd/lcd_comm_rev_c.py index 91aeb7f3..19f59827 100644 --- a/library/lcd/lcd_comm_rev_c.py +++ b/library/lcd/lcd_comm_rev_c.py @@ -146,7 +146,16 @@ def __del__(self): def auto_detect_com_port() -> Optional[str]: com_ports = comports() - # Try to find awake device through serial number or vid/pid + # First, 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() + if com_port.vid == 0x1a86 and com_port.pid == 0xca21: + LcdCommRevC._connect_to_reset_device_name(com_port) + return LcdCommRevC.auto_detect_com_port() + + # Then try to find awake device through serial number or vid/pid for com_port in com_ports: if com_port.serial_number == '20080411': return com_port.device @@ -155,14 +164,6 @@ def auto_detect_com_port() -> Optional[str]: 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() - if com_port.serial_number == '20080411': - return com_port.device - return None @staticmethod @@ -292,12 +293,12 @@ def SetOrientation(self, orientation: Orientation = Orientation.PORTRAIT): self.orientation = orientation # logger.info(f"Call SetOrientation to: {self.orientation.name}") - # if self.orientation == Orientation.REVERSE_LANDSCAPE or self.orientation == Orientation.REVERSE_PORTRAIT: - # b = Command.STARTMODE_DEFAULT.value + Padding.NULL.value + Command.FLIP_180.value + SleepInterval.OFF.value - # self._send_command(Command.OPTIONS, payload=b) - # else: - b = Command.STARTMODE_DEFAULT.value + Padding.NULL.value + Command.NO_FLIP.value + SleepInterval.OFF.value - self._send_command(Command.OPTIONS, payload=b) + if self.orientation == Orientation.REVERSE_LANDSCAPE or self.orientation == Orientation.REVERSE_PORTRAIT: + b = Command.STARTMODE_DEFAULT.value + Padding.NULL.value + Command.FLIP_180.value + SleepInterval.OFF.value + self._send_command(Command.OPTIONS, payload=b) + else: + b = Command.STARTMODE_DEFAULT.value + Padding.NULL.value + Command.NO_FLIP.value + SleepInterval.OFF.value + self._send_command(Command.OPTIONS, payload=b) def DisplayPILImage( self, From d720a80e1711bf2a15360f8f32313c688d9d8330 Mon Sep 17 00:00:00 2001 From: Matthieu Houdebine Date: Fri, 16 May 2025 10:15:48 +0200 Subject: [PATCH 2/6] Implement BGRA mode for Turing 8.8" with SW rev. 90 --- library/lcd/lcd_comm_rev_c.py | 69 ++++++++++++++++++----------------- 1 file changed, 36 insertions(+), 33 deletions(-) diff --git a/library/lcd/lcd_comm_rev_c.py b/library/lcd/lcd_comm_rev_c.py index 19f59827..a7c2834b 100644 --- a/library/lcd/lcd_comm_rev_c.py +++ b/library/lcd/lcd_comm_rev_c.py @@ -125,7 +125,8 @@ class SubRevision(Enum): UNKNOWN = "" REV_2INCH = "chs_21inch" REV_5INCH = "chs_5inch" - REV_8INCH = "chs_88inch" + REV_8INCH_V88 = "chs_88inch.dev1_rom1.88" + REV_8INCH_V90 = "chs_88inch.dev1_rom1.90" def __init__(self, command): self.command = command @@ -216,33 +217,24 @@ def _hello(self): self.serial_flush_input() logger.debug("HW sub-revision returned: %s" % ''.join(filter(lambda x: x in set(string.printable), response))) - # Note: sub-revisions returned by display are not reliable e.g. 2.1" displays return "chs_5inch" - # if response.startswith(SubRevision.REV_5INCH.value): - # self.sub_revision = SubRevision.REV_5INCH - # self.display_width = 480 - # self.display_height = 800 - # elif response.startswith(SubRevision.REV_2INCH.value): - # self.sub_revision = SubRevision.REV_2INCH - # self.display_width = 480 - # self.display_height = 480 - # elif response.startswith(SubRevision.REV_8INCH.value): - # self.sub_revision = SubRevision.REV_8INCH - # self.display_width = 480 - # self.display_height = 1920 - # else: - # logger.warning("Display returned unknown sub-revision on Hello answer (%s)" % str(response)) - # logger.debug("HW sub-revision detected: %s" % (str(self.sub_revision))) - - # Relay on width/height for sub-revision detection + # Note: sub-revisions returned by display are not reliable for some models e.g. 2.1" displays return "chs_5inch" + # Relay mainly on width/height for sub-revision detection, except for 8.8" where ROM version matters if self.display_width == 480 and self.display_height == 480: self.sub_revision = SubRevision.REV_2INCH elif self.display_width == 480 and self.display_height == 800: self.sub_revision = SubRevision.REV_5INCH elif self.display_width == 480 and self.display_height == 1920: - self.sub_revision = SubRevision.REV_8INCH + if response.startswith(SubRevision.REV_8INCH_V88.value): + self.sub_revision = SubRevision.REV_8INCH_V88 + elif response.startswith(SubRevision.REV_8INCH_V90.value): + self.sub_revision = SubRevision.REV_8INCH_V90 + else: + logger.warning("Display returned unknown sub-revision on Hello answer (%s)" % str(response)) else: logger.error(f"Unsupported resolution {self.display_width}x{self.display_height} for revision C") + logger.debug("HW sub-revision detected: %s" % (str(self.sub_revision))) + def InitializeComm(self): self._hello() @@ -293,12 +285,12 @@ def SetOrientation(self, orientation: Orientation = Orientation.PORTRAIT): self.orientation = orientation # logger.info(f"Call SetOrientation to: {self.orientation.name}") - if self.orientation == Orientation.REVERSE_LANDSCAPE or self.orientation == Orientation.REVERSE_PORTRAIT: - b = Command.STARTMODE_DEFAULT.value + Padding.NULL.value + Command.FLIP_180.value + SleepInterval.OFF.value - self._send_command(Command.OPTIONS, payload=b) - else: - b = Command.STARTMODE_DEFAULT.value + Padding.NULL.value + Command.NO_FLIP.value + SleepInterval.OFF.value - self._send_command(Command.OPTIONS, payload=b) + # if self.orientation == Orientation.REVERSE_LANDSCAPE or self.orientation == Orientation.REVERSE_PORTRAIT: + # b = Command.STARTMODE_DEFAULT.value + Padding.NULL.value + Command.FLIP_180.value + SleepInterval.OFF.value + # self._send_command(Command.OPTIONS, payload=b) + # else: + b = Command.STARTMODE_DEFAULT.value + Padding.NULL.value + Command.NO_FLIP.value + SleepInterval.OFF.value + self._send_command(Command.OPTIONS, payload=b) def DisplayPILImage( self, @@ -336,11 +328,12 @@ def DisplayPILImage( display_bmp_cmd = Command.DISPLAY_BITMAP_5INCH elif self.sub_revision == SubRevision.REV_2INCH: display_bmp_cmd = Command.DISPLAY_BITMAP_2INCH - elif self.sub_revision == SubRevision.REV_8INCH: + elif self.sub_revision == SubRevision.REV_8INCH_V88 or self.sub_revision == SubRevision.REV_8INCH_V90: display_bmp_cmd = Command.DISPLAY_BITMAP_8INCH self._send_command(display_bmp_cmd, - payload=bytearray(int(self.display_width * self.display_width / 64).to_bytes(2, "big"))) + payload=bytearray( + int(self.display_width * self.display_width / 64).to_bytes(2, "big"))) self._send_command(Command.SEND_PAYLOAD, payload=bytearray(self._generate_full_image(image)), readsize=1024) @@ -354,7 +347,7 @@ def DisplayPILImage( Count.Start += 1 def _generate_full_image(self, image: Image.Image) -> bytes: - if self.sub_revision == SubRevision.REV_8INCH: + if self.sub_revision == SubRevision.REV_8INCH_V88 or self.sub_revision == SubRevision.REV_8INCH_V90: if self.orientation == Orientation.LANDSCAPE: image = image.rotate(270, expand=True) elif self.orientation == Orientation.REVERSE_LANDSCAPE: @@ -379,7 +372,7 @@ def _generate_update_image( self, image: Image.Image, x: int, y: int, count: int, cmd: Optional[Command] = None ) -> Tuple[bytearray, bytearray]: x0, y0 = x, y - if self.sub_revision == SubRevision.REV_8INCH: + if self.sub_revision == SubRevision.REV_8INCH_V88 or self.sub_revision == SubRevision.REV_8INCH_V90: if self.orientation == Orientation.LANDSCAPE: image = image.rotate(270, expand=True) y0 = self.get_height() - y - image.width @@ -409,9 +402,19 @@ def _generate_update_image( y0 = x img_raw_data = bytearray() - bgr_data = image_to_BGR(image) - for h, line in enumerate(chunked(bgr_data, image.width * 3)): - if self.sub_revision == SubRevision.REV_8INCH: + + # Some screens require BGR for update image, some require BGRA + if self.sub_revision == SubRevision.REV_8INCH_V90: + # BGRA mode + img_data = image_to_BGRA(image) + pixel_size = 4 + else: + # BGR mode + img_data = image_to_BGR(image) + pixel_size = 3 + + for h, line in enumerate(chunked(img_data, image.width * pixel_size)): + if self.sub_revision == SubRevision.REV_8INCH_V88 or self.sub_revision == SubRevision.REV_8INCH_V90: img_raw_data += int(((x0 + h) * self.display_width) + y0).to_bytes(3, "big") else: img_raw_data += int(((x0 + h) * self.display_height) + y0).to_bytes(3, "big") From 964e42084998d46b7946436cbc02086b4aac68a5 Mon Sep 17 00:00:00 2001 From: Matthieu Houdebine Date: Fri, 16 May 2025 12:30:52 +0200 Subject: [PATCH 3/6] Improve Turing Rev.C ROM version detection --- library/lcd/lcd_comm_rev_c.py | 55 ++++++++++++++++------------------- 1 file changed, 25 insertions(+), 30 deletions(-) diff --git a/library/lcd/lcd_comm_rev_c.py b/library/lcd/lcd_comm_rev_c.py index a7c2834b..85917164 100644 --- a/library/lcd/lcd_comm_rev_c.py +++ b/library/lcd/lcd_comm_rev_c.py @@ -92,17 +92,11 @@ class Command(Enum): NO_FLIP = bytearray((0x00,)) SEND_PAYLOAD = bytearray((0xFF,)) - def __init__(self, command): - self.command = command - class Padding(Enum): NULL = bytearray([0x00]) START_DISPLAY_BITMAP = bytearray([0x2c]) - def __init__(self, command): - self.command = command - class SleepInterval(Enum): OFF = bytearray((0x00,)) @@ -117,19 +111,12 @@ class SleepInterval(Enum): NINE = bytearray((0x09,)) TEN = bytearray((0x0a,)) - def __init__(self, command): - self.command = command - class SubRevision(Enum): UNKNOWN = "" REV_2INCH = "chs_21inch" REV_5INCH = "chs_5inch" - REV_8INCH_V88 = "chs_88inch.dev1_rom1.88" - REV_8INCH_V90 = "chs_88inch.dev1_rom1.90" - - def __init__(self, command): - self.command = command + REV_8INCH = "chs_88inch" # This class is for Turing Smart Screen 2.1" / 5" / 8" screens @@ -213,27 +200,32 @@ def _hello(self): # This command reads LCD answer on serial link, so it bypasses the queue self.sub_revision = SubRevision.UNKNOWN self._send_command(Command.HELLO, bypass_queue=True) - response = str(self.serial_read(23).decode(errors="ignore")) + response = ''.join(filter(lambda x: x in set(string.printable), str(self.serial_read(23).decode(errors="ignore")))) self.serial_flush_input() - logger.debug("HW sub-revision returned: %s" % ''.join(filter(lambda x: x in set(string.printable), response))) + logger.debug("Display ID returned: %s" % response) - # Note: sub-revisions returned by display are not reliable for some models e.g. 2.1" displays return "chs_5inch" - # Relay mainly on width/height for sub-revision detection, except for 8.8" where ROM version matters + # Note: ID returned by display are not reliable for some models e.g. 2.1" displays return "chs_5inch" + # Rely on width/height for sub-revision detection if self.display_width == 480 and self.display_height == 480: self.sub_revision = SubRevision.REV_2INCH elif self.display_width == 480 and self.display_height == 800: self.sub_revision = SubRevision.REV_5INCH elif self.display_width == 480 and self.display_height == 1920: - if response.startswith(SubRevision.REV_8INCH_V88.value): - self.sub_revision = SubRevision.REV_8INCH_V88 - elif response.startswith(SubRevision.REV_8INCH_V90.value): - self.sub_revision = SubRevision.REV_8INCH_V90 - else: - logger.warning("Display returned unknown sub-revision on Hello answer (%s)" % str(response)) + self.sub_revision = SubRevision.REV_8INCH else: logger.error(f"Unsupported resolution {self.display_width}x{self.display_height} for revision C") - logger.debug("HW sub-revision detected: %s" % (str(self.sub_revision))) + # Detect ROM version + try: + self.rom_version = int(response.split(".")[2]) + if self.rom_version < 80 or self.rom_version > 100: + logger.warning("ROM version %d may be invalid, use default ROM version 87" % self.rom_version) + self.rom_version = 87 + except: + logger.warning("Display returned invalid or unsupported ID on Hello answer, use default ROM version 87") + self.rom_version = 87 + + logger.debug("HW sub-revision detected: %s, ROM version: %d" % ((str(self.sub_revision)), self.rom_version)) def InitializeComm(self): self._hello() @@ -328,7 +320,7 @@ def DisplayPILImage( display_bmp_cmd = Command.DISPLAY_BITMAP_5INCH elif self.sub_revision == SubRevision.REV_2INCH: display_bmp_cmd = Command.DISPLAY_BITMAP_2INCH - elif self.sub_revision == SubRevision.REV_8INCH_V88 or self.sub_revision == SubRevision.REV_8INCH_V90: + elif self.sub_revision == SubRevision.REV_8INCH: display_bmp_cmd = Command.DISPLAY_BITMAP_8INCH self._send_command(display_bmp_cmd, @@ -347,7 +339,8 @@ def DisplayPILImage( Count.Start += 1 def _generate_full_image(self, image: Image.Image) -> bytes: - if self.sub_revision == SubRevision.REV_8INCH_V88 or self.sub_revision == SubRevision.REV_8INCH_V90: + if self.sub_revision == SubRevision.REV_8INCH: + # Switch landscape/portrait mode for 8" if self.orientation == Orientation.LANDSCAPE: image = image.rotate(270, expand=True) elif self.orientation == Orientation.REVERSE_LANDSCAPE: @@ -372,7 +365,8 @@ def _generate_update_image( self, image: Image.Image, x: int, y: int, count: int, cmd: Optional[Command] = None ) -> Tuple[bytearray, bytearray]: x0, y0 = x, y - if self.sub_revision == SubRevision.REV_8INCH_V88 or self.sub_revision == SubRevision.REV_8INCH_V90: + if self.sub_revision == SubRevision.REV_8INCH: + # Switch landscape/portrait mode for 8" if self.orientation == Orientation.LANDSCAPE: image = image.rotate(270, expand=True) y0 = self.get_height() - y - image.width @@ -404,7 +398,7 @@ def _generate_update_image( img_raw_data = bytearray() # Some screens require BGR for update image, some require BGRA - if self.sub_revision == SubRevision.REV_8INCH_V90: + if self.rom_version > 88: # BGRA mode img_data = image_to_BGRA(image) pixel_size = 4 @@ -414,7 +408,8 @@ def _generate_update_image( pixel_size = 3 for h, line in enumerate(chunked(img_data, image.width * pixel_size)): - if self.sub_revision == SubRevision.REV_8INCH_V88 or self.sub_revision == SubRevision.REV_8INCH_V90: + if self.sub_revision == SubRevision.REV_8INCH: + # Switch landscape/portrait mode for 8" img_raw_data += int(((x0 + h) * self.display_width) + y0).to_bytes(3, "big") else: img_raw_data += int(((x0 + h) * self.display_height) + y0).to_bytes(3, "big") From d439a060347b656d7c20549bee14eed8460c51d7 Mon Sep 17 00:00:00 2001 From: Matthieu Houdebine Date: Fri, 16 May 2025 13:49:43 +0200 Subject: [PATCH 4/6] Improve Turing 2.1/5/8.8 by sending correct colors --- library/lcd/lcd_comm_rev_c.py | 23 +++++++++++------------ library/lcd/serialize.py | 25 ++++++++++++++++++++----- 2 files changed, 31 insertions(+), 17 deletions(-) diff --git a/library/lcd/lcd_comm_rev_c.py b/library/lcd/lcd_comm_rev_c.py index 85917164..b1407da5 100644 --- a/library/lcd/lcd_comm_rev_c.py +++ b/library/lcd/lcd_comm_rev_c.py @@ -30,7 +30,7 @@ from serial.tools.list_ports import comports from library.lcd.lcd_comm import Orientation, LcdComm -from library.lcd.serialize import image_to_BGRA, image_to_BGR, chunked +from library.lcd.serialize import image_to_BGRA, image_to_compressed_BGRA, chunked from library.log import logger @@ -200,7 +200,8 @@ def _hello(self): # This command reads LCD answer on serial link, so it bypasses the queue self.sub_revision = SubRevision.UNKNOWN self._send_command(Command.HELLO, bypass_queue=True) - response = ''.join(filter(lambda x: x in set(string.printable), str(self.serial_read(23).decode(errors="ignore")))) + response = ''.join( + filter(lambda x: x in set(string.printable), str(self.serial_read(23).decode(errors="ignore")))) self.serial_flush_input() logger.debug("Display ID returned: %s" % response) @@ -252,13 +253,13 @@ def Clear(self): self.SetOrientation(orientation=backup_orientation) def ScreenOff(self): - logger.info("Calling ScreenOff") + # logger.info("Calling ScreenOff") self._send_command(Command.STOP_VIDEO) self._send_command(Command.STOP_MEDIA, readsize=1024) self._send_command(Command.TURNOFF) def ScreenOn(self): - logger.info("Calling ScreenOn") + # logger.info("Calling ScreenOn") self._send_command(Command.STOP_VIDEO) self._send_command(Command.STOP_MEDIA, readsize=1024) # self._send_command(Command.SET_BRIGHTNESS, payload=bytearray([255])) @@ -357,7 +358,7 @@ def _generate_full_image(self, image: Image.Image) -> bytes: elif self.orientation == Orientation.REVERSE_LANDSCAPE: image = image.rotate(180) - bgra_data = image_to_BGRA(image) + bgra_data, pixel_size = image_to_BGRA(image) return b'\x00'.join(chunked(bgra_data, 249)) @@ -397,15 +398,13 @@ def _generate_update_image( img_raw_data = bytearray() - # Some screens require BGR for update image, some require BGRA + # Some screens require different RGBA encoding if self.rom_version > 88: - # BGRA mode - img_data = image_to_BGRA(image) - pixel_size = 4 + # BGRA mode on 4 bytes : [B, G, R, A] + img_data, pixel_size = image_to_BGRA(image) else: - # BGR mode - img_data = image_to_BGR(image) - pixel_size = 3 + # BGRA mode on 3 bytes: [6-bit B + 2-bit A, 6-bit G + 2-bit A, R] + img_data, pixel_size = image_to_compressed_BGRA(image) for h, line in enumerate(chunked(img_data, image.width * pixel_size)): if self.sub_revision == SubRevision.REV_8INCH: diff --git a/library/lcd/serialize.py b/library/lcd/serialize.py index 7f6f63d0..5909c185 100644 --- a/library/lcd/serialize.py +++ b/library/lcd/serialize.py @@ -6,7 +6,7 @@ def chunked(data: bytes, chunk_size: int) -> Iterator[bytes]: for i in range(0, len(data), chunk_size): - yield data[i : i + chunk_size] + yield data[i: i + chunk_size] def image_to_RGB565(image: Image.Image, endianness: Literal["big", "little"]) -> bytes: @@ -39,20 +39,35 @@ def image_to_RGB565(image: Image.Image, endianness: Literal["big", "little"]) -> return rgb565.astype(typ).tobytes() -def image_to_BGR(image: Image.Image) -> bytes: +def image_to_BGR(image: Image.Image) -> (bytes, int): if image.mode not in ["RGB", "RGBA"]: # we need the first 3 channels to be R, G and B image = image.convert("RGB") rgb = np.asarray(image) # same as rgb[:, :, [2, 1, 0]] but faster bgr = np.take(rgb, (2, 1, 0), axis=-1) - return bgr.tobytes() + return bgr.tobytes(), 3 -def image_to_BGRA(image: Image.Image) -> bytes: +def image_to_BGRA(image: Image.Image) -> (bytes, int): if image.mode != "RGBA": image = image.convert("RGBA") rgba = np.asarray(image) # same as rgba[:, :, [2, 1, 0, 3]] but faster bgra = np.take(rgba, (2, 1, 0, 3), axis=-1) - return bgra.tobytes() + return bgra.tobytes(), 4 + + +# FIXME: to optimize like other functions above +def image_to_compressed_BGRA(image: Image.Image) -> (bytes, int): + compressed_bgra = bytearray() + image_data = image.convert("RGBA").load() + for h in range(image.height): + for w in range(image.width): + # r = pixel[0], g = pixel[1], b = pixel[2], a = pixel[3] + pixel = image_data[w, h] + a = pixel[3] >> 4 + compressed_bgra.append(pixel[2] & 0xFC | a >> 2) + compressed_bgra.append(pixel[1] & 0xFC | a & 2) + compressed_bgra.append(pixel[0]) + return bytes(compressed_bgra), 3 From 1ba10c2e8d03a905d37af0eea45aa503ada7d768 Mon Sep 17 00:00:00 2001 From: Matthieu Houdebine Date: Fri, 16 May 2025 14:27:01 +0200 Subject: [PATCH 5/6] Improve Turing Rev. C connection/disconnection/reset timings --- library/lcd/lcd_comm_rev_c.py | 53 ++++++++++++++++++++++++++--------- 1 file changed, 40 insertions(+), 13 deletions(-) diff --git a/library/lcd/lcd_comm_rev_c.py b/library/lcd/lcd_comm_rev_c.py index b1407da5..fd44ab75 100644 --- a/library/lcd/lcd_comm_rev_c.py +++ b/library/lcd/lcd_comm_rev_c.py @@ -137,12 +137,16 @@ def auto_detect_com_port() -> Optional[str]: # First, 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) + LcdCommRevC._wake_up_device(com_port) return LcdCommRevC.auto_detect_com_port() if com_port.vid == 0x1a86 and com_port.pid == 0xca21: - LcdCommRevC._connect_to_reset_device_name(com_port) + LcdCommRevC._wake_up_device(com_port) return LcdCommRevC.auto_detect_com_port() + return LcdCommRevC._get_awake_com_port(com_ports) + + @staticmethod + def _get_awake_com_port(com_ports) -> Optional[str]: # Then try to find awake device through serial number or vid/pid for com_port in com_ports: if com_port.serial_number == '20080411': @@ -155,14 +159,21 @@ def auto_detect_com_port() -> Optional[str]: return None @staticmethod - def _connect_to_reset_device_name(com_port): + def _wake_up_device(com_port): # this device enumerates differently when off, we need to connect once to reset it to correct COM device - try: - logger.debug(f"Waiting for device {com_port} to be turned ON...") - serial.Serial(com_port.device, 115200, timeout=1, rtscts=True) - except serial.SerialException: - pass - time.sleep(10) + logger.debug(f"Waiting for device {com_port} to be turned ON...") + + for i in range(15): + try: + # Try to connect every second, since it takes sometimes multiple connect to wake up the device + serial.Serial(com_port.device, 115200, timeout=1, rtscts=True) + except serial.SerialException: + pass + + if LcdCommRevC._get_awake_com_port(comports()) is not None: + time.sleep(1) + return + time.sleep(1) def _send_command(self, cmd: Command, payload: Optional[bytearray] = None, padding: Optional[Padding] = None, bypass_queue: bool = False, readsize: Optional[int] = None): @@ -199,11 +210,20 @@ def _send_command(self, cmd: Command, payload: Optional[bytearray] = None, paddi def _hello(self): # This command reads LCD answer on serial link, so it bypasses the queue self.sub_revision = SubRevision.UNKNOWN + self.serial_flush_input() self._send_command(Command.HELLO, bypass_queue=True) response = ''.join( filter(lambda x: x in set(string.printable), str(self.serial_read(23).decode(errors="ignore")))) self.serial_flush_input() logger.debug("Display ID returned: %s" % response) + while not response.startswith("chs_"): + logger.warning("Display returned invalid or unsupported ID, try again in 1 second") + time.sleep(1) + self._send_command(Command.HELLO, bypass_queue=True) + response = ''.join( + filter(lambda x: x in set(string.printable), str(self.serial_read(23).decode(errors="ignore")))) + self.serial_flush_input() + logger.debug("Display ID returned: %s" % response) # Note: ID returned by display are not reliable for some models e.g. 2.1" displays return "chs_5inch" # Rely on width/height for sub-revision detection @@ -223,7 +243,7 @@ def _hello(self): logger.warning("ROM version %d may be invalid, use default ROM version 87" % self.rom_version) self.rom_version = 87 except: - logger.warning("Display returned invalid or unsupported ID on Hello answer, use default ROM version 87") + logger.warning("Display returned invalid or unsupported ID, use default ROM version 87") self.rom_version = 87 logger.debug("HW sub-revision detected: %s, ROM version: %d" % ((str(self.sub_revision)), self.rom_version)) @@ -236,8 +256,15 @@ def Reset(self): # Reset command bypasses queue because it is run when queue threads are not yet started self._send_command(Command.RESTART, bypass_queue=True) self.closeSerial() - # Wait for display reset then reconnect - time.sleep(15) + # Wait for disconnection (max. 15 seconds) + for i in range(15): + if LcdCommRevC._get_awake_com_port(comports()) is not None: + time.sleep(1) + # Wait for reconnection (max. 15 seconds) + for i in range(15): + if LcdCommRevC._get_awake_com_port(comports()) is None: + time.sleep(1) + # Reconnect to device self.openSerial() def Clear(self): @@ -403,7 +430,7 @@ def _generate_update_image( # BGRA mode on 4 bytes : [B, G, R, A] img_data, pixel_size = image_to_BGRA(image) else: - # BGRA mode on 3 bytes: [6-bit B + 2-bit A, 6-bit G + 2-bit A, R] + # BGRA mode on 3 bytes: [6-bit B + 2-bit A, 6-bit G + 2-bit A, 8-bit R] img_data, pixel_size = image_to_compressed_BGRA(image) for h, line in enumerate(chunked(img_data, image.width * pixel_size)): From 94ed336241e21b5c649f43e74bd428fe0cab80b7 Mon Sep 17 00:00:00 2001 From: Matthieu Houdebine Date: Wed, 21 May 2025 18:54:24 +0200 Subject: [PATCH 6/6] Restore simple BGR mode for Turing with ROM <= 88 --- library/lcd/lcd_comm_rev_c.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/library/lcd/lcd_comm_rev_c.py b/library/lcd/lcd_comm_rev_c.py index fd44ab75..980514b0 100644 --- a/library/lcd/lcd_comm_rev_c.py +++ b/library/lcd/lcd_comm_rev_c.py @@ -30,7 +30,7 @@ from serial.tools.list_ports import comports from library.lcd.lcd_comm import Orientation, LcdComm -from library.lcd.serialize import image_to_BGRA, image_to_compressed_BGRA, chunked +from library.lcd.serialize import image_to_BGRA, image_to_BGR, chunked from library.log import logger @@ -431,7 +431,9 @@ def _generate_update_image( img_data, pixel_size = image_to_BGRA(image) else: # BGRA mode on 3 bytes: [6-bit B + 2-bit A, 6-bit G + 2-bit A, 8-bit R] - img_data, pixel_size = image_to_compressed_BGRA(image) + #img_data, pixel_size = image_to_compressed_BGRA(image) + # For now use simple BGR that is more optimized, because this program does not support transparent background + img_data, pixel_size = image_to_BGR(image) for h, line in enumerate(chunked(img_data, image.width * pixel_size)): if self.sub_revision == SubRevision.REV_8INCH: