Created
April 25, 2023 03:43
-
-
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
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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