Skip to content

Instantly share code, notes, and snippets.

@ril3y
Created April 25, 2023 03:43
Show Gist options
  • Save ril3y/608c026e88fafdb7510d0d438c0343b6 to your computer and use it in GitHub Desktop.
Save ril3y/608c026e88fafdb7510d0d438c0343b6 to your computer and use it in GitHub Desktop.
Python script (work in progress) to communicate with the wisecoco lcd rotation module
import struct
import sys
import serial
import argparse
from enum import Enum
import time
DISPLAY_ON = 1
DISPLAY_OFF = 0
# COLORS
COLOR_RED = (0xF8, 0x00)
COLOR_GREEN = (0x07, 0xE0)
COLOR_BLUE = (0x00, 0x1F)
COLOR_YELLOW = (0xFF, 0xE0)
COLOR_MAGENTA = (0xF8, 0x1F)
COLOR_CYAN = (0x07, 0xFF)
COLOR_WHITE = (0xFF, 0xFF)
COLOR_BLACK = (0x00, 0x00)
COLOR_PURPLE = (0xB000, 0x1F)
COLOR_ORANGE = (0xFD80, 0x00)
COLOR_PINK = (0xF81F, 0x3F)
COLOR_DARK_GRAY = (0x39E7, 0x0000)
COLOR_LIGHT_GRAY = (0x7BEF, 0x0000)
COLOR_TURQUOISE = (0x40E0, 0x0000)
COLOR_BROWN = (0x8200, 0x0000)
COLOR_LAVENDER = (0xE73D, 0x3F)
COLOR_BEIGE = (0xF7BB, 0x8410)
COLOR_SKY_BLUE = (0x867D, 0x0000)
class ProtocolType(Enum):
ACK = 0xFE
NACK_CHECKSUM_ERROR = 0xFD
NACK_NOT_SUPPORTED = 0xFC
NACK_BUSY = 0xFB
NACK_STARTING_OR_NOT_CONNECTED = 0xFA
class Response(Enum):
BASIC_INFO_LCD_VERSION = 0xF1
OPERATIONAL_BUTTON_KNOB_VALUES = 0xF2
FILL_BACKGROUND_COMPLETE = 0xF3
PROTOCOL_COMMAND = 0xFF
class Command(Enum):
DISCONNECT = 0x00
CONNECT = 0x01
FILL_BACKGROUND = 0x02
FILL_AREA = 0x03
SCREEN_DISPLAY = 0x04
PREPARE_BIN = 0x05
GET_SCREEN_VERSION = 0x06
class DisplayController:
def __init__(self, port):
self.ser = serial.Serial(port, 115200, timeout=0.05)
self.display_on = True # set initial display state to on
self.on_connect()
def __del__(self):
self.disconnect()
def on_connect(self):
connect_packet = self.build_packet(Command.CONNECT, bytearray([Command.CONNECT.value]))
self.send_packet(connect_packet)
packet = self.build_packet(Command.GET_SCREEN_VERSION, bytearray())
self.send_packet(packet)
def disconnect(self):
disconnect_packet = self.build_packet(Command.DISCONNECT, bytearray([Command.DISCONNECT.value]))
self.send_packet(disconnect_packet)
self.ser.close()
def send_packet(self, packet):
self.ser.write(packet)
def read_header_response(self):
header = self.ser.read(3)
if len(header) == 0:
return None, None, None
head_code = header[0]
command = header[1]
data_len = header[2]
if head_code != 0xAA:
print("Invalid packer header")
return None, None, None
else:
return head_code, command, data_len
def read_response(self):
head_code, command, data_len = self.read_header_response()
if head_code is None:
return
if data_len == 0:
return None
else:
data = self.ser.read(data_len)
received_checksum = self.ser.read(1)
calculated_checksum = self.compute_checksum(command, data_len, data)
if int.from_bytes(received_checksum, 'big') != calculated_checksum:
print("Checksum mismatch")
return None
return bytearray(
head_code.to_bytes(1, 'big') +
command.to_bytes(1, 'big') +
data_len.to_bytes(1, 'big') +
data +
received_checksum)
def compute_checksum(self, command, length, data):
data = sum(data)
checksum = (command + length + data) ^ 0xff
return checksum & 0xff
def send_ack_packet(self):
ack_packet = self.build_packet(Response.PROTOCOL_COMMAND, bytearray(b'\xfe'))
self.send_packet(ack_packet)
def build_packet(self, command: Command, data: bytearray):
header = 0xAA
packet = bytearray()
packet.append(header)
packet.append(command.value)
length = len(data)
if length == 0:
packet.append(0x01)
length = 1
else:
packet.append(length)
if len(data) == 0:
packet.append(0x00)
else:
for byte in data:
packet.append(byte)
checksum = self.compute_checksum(command.value, length, data)
packet.append(checksum)
return packet
def parse_protocol_command(self, response):
match response[3]:
case ProtocolType.ACK.value:
return print("ACK")
case ProtocolType.NACK_BUSY.value:
return print("BUSY")
case ProtocolType.NACK_NOT_SUPPORTED.value:
return print("Not Supported")
case ProtocolType.NACK_STARTING_OR_NOT_CONNECTED.value:
return print("starting or disconnected")
case ProtocolType.NACK_CHECKSUM_ERROR.value:
print("Checksum Error")
def parse_version_info(self, response):
data_length = response[2]
version_offset = 0x03
version_string = response[3:(data_length + version_offset)]
return version_string
def parse_response(self, response) -> Response:
response_code = response[1]
try:
response_type = Response(response_code)
except ValueError:
print(f"Unknown response code: {response_code}")
print(f"Full packet: {response}")
return None
if response_type == Response.PROTOCOL_COMMAND:
self.parse_protocol_command(response)
return Response.PROTOCOL_COMMAND
elif response_type == Response.BASIC_INFO_LCD_VERSION:
print(f"Basic information: LCD version: {self.parse_version_info(response).decode('utf-8')}")
return Response.BASIC_INFO_LCD_VERSION
elif response_type == Response.OPERATIONAL_BUTTON_KNOB_VALUES:
action = None
knob_value = response[3]
if knob_value == 1:
action = "Button Short Press"
elif knob_value == 2:
action = "Button Long Press"
self.toggle_screen()
elif knob_value == 3:
action = "Rotated Counter Clockwise"
self.test(2)
elif knob_value == 4:
action = "Rotated Clockwise"
self.test(1)
if action is not None:
print(f"Operational button/knob values: Action: {action}")
return Response.OPERATIONAL_BUTTON_KNOB_VALUES
elif response_type == Response.FILL_BACKGROUND_COMPLETE:
print("Fill background complete")
self.turn_on_display()
return Response.FILL_BACKGROUND_COMPLETE
def run(self):
connect_packet = self.build_packet(Command.CONNECT, bytearray([Command.CONNECT.value]))
self.send_packet(connect_packet)
while True:
response = None
while response is None:
response = self.read_response()
response_code = self.parse_response(response)
# packet = self.build_packet(Command.GET_SCREEN_VERSION, bytearray())
# self.send_packet(packet)
# The button_knob responses are generated on the device
# There is no need to ACK them so we just state its true
# For all other response types we need ACK the response.
if response_code.OPERATIONAL_BUTTON_KNOB_VALUES:
ack_received = True
else:
# All other response types
ack_received = False
while not ack_received:
ack_response = self.read_response()
if ack_response is not None and Response(ack_response[1]) == Response.PROTOCOL_COMMAND and ack_response[
3] == ProtocolType.ACK.value:
ack_received = True
self.send_ack_packet()
def turn_off_display(self):
packet = self.build_packet(Command.SCREEN_DISPLAY, data=bytearray([DISPLAY_OFF]))
self.send_packet(packet)
def turn_on_display(self):
packet = self.build_packet(Command.SCREEN_DISPLAY, data=bytearray([DISPLAY_ON]))
self.send_packet(packet)
@staticmethod
def int_to_16bit_be(value):
"""Converts an integer value to a 16-bit big-endian binary representation."""
return struct.pack(">H", value)
# def set_background_color(self, color):
# color = COLOR_RED
# x_coordinate = self.int_to_16bit_be(240)
# y_coordinate = self.int_to_16bit_be(240)
# packet = self.build_packet(Command.FILL_AREA.value, data)
def test(self, pic_number):
data = bytearray([0x00,pic_number,0x00,0x00,0x00,0x00])
x_coordinate = self.int_to_16bit_be(240)
y_coordinate = self.int_to_16bit_be(240)
packet = self.build_packet(Command.FILL_BACKGROUND, data)
self.send_packet(packet)
def toggle_screen(self):
if self.display_on:
self.turn_off_display()
self.display_on = False
else:
self.turn_on_display()
self.display_on = True
if __name__ == "__main__":
controller = DisplayController("COM4")
controller.run()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment