commit 131878133d305a16c413b4309869ff1b295bd8e9 Author: eworc778 Date: Sun Aug 10 17:49:52 2025 +0100 Initial commit; Added previously untracked files. diff --git a/code.py b/code.py new file mode 100644 index 0000000..70b09b4 --- /dev/null +++ b/code.py @@ -0,0 +1,430 @@ +import busio +import board +from board import * +from rainbowio import colorwheel +import waveshare_LCD1602 +from digitalio import DigitalInOut, Direction, Pull +import time +import board +import usb_hid +from adafruit_hid.keyboard import Keyboard +from keyboard_layout_win_uk import KeyboardLayout +from keycode_win_uk import Keycode +"""Default I2C ports on boards that have one""" +i2c = busio.I2C(GP19, GP18) + +buttonOne = DigitalInOut(board.GP0) +buttonOne.pull = Pull.UP + +buttonTwo = DigitalInOut(board.GP1) +buttonTwo.pull = Pull.UP + +buttonThree = DigitalInOut(board.GP2) +buttonThree.pull = Pull.UP + +kbd = Keyboard(usb_hid.devices) +layout = KeyboardLayout(kbd) + + +lcd = waveshare_LCD1602.LCD1602(i2c, 16, 2) +lcd.setCursor(0,0) + + +def macro_parser(macroFileName=(str)): + + file = open("macros/" + macroFileName, 'r') + + clearScr = 1 + lineToShow = "" + holdingKeys = 0 + sendCombo = 0 + keysToPress = [] + for line in file: + print(line) + + + line = line.replace("\n", "") + workingLine = line + + sleepingTime = 0 + + if line == "HOLD": + + holdingKeys = 1 + + if line == "RELEASE": + + if holdingKeys == 1: + + sendCombo = 1 + + if sendCombo == 1: + + if len(keysToPress) == 1: + + kbd.send(keysToPress[0]) + + + + kbd.release_all() + + elif len(keysToPress) == 2: + + kbd.send(keysToPress[0], keysToPress[1]) + + + + kbd.release_all() + + elif len(keysToPress) == 3: + + kbd.send(keysToPress[0], keysToPress[1], keysToPress[2]) + + + + kbd.release_all() + + elif len(keysToPress) == 4: + + kbd.send(keysToPress[0], keysToPress[1], keysToPress[2], keysToPress[3]) + + + + kbd.release_all() + + elif len(keysToPress) == 5: + + kbd.send(keysToPress[0], keysToPress[1], keysToPress[2], keysToPress[3], keysToPress[4]) + + kbd.release_all() + + elif len(keysToPress) == 6: + + kbd.send(keysToPress[0], keysToPress[1], keysToPress[2], keysToPress[3], keysToPress[4], keysToPress[5]) + + + + kbd.release_all() + + sendCombo = 0 + + holdingKeys = 0 + + keysToPress = [] + + if line == "super": + + if holdingKeys == 1: + + keysToPress.append(Keycode.WINDOWS) + + else: + + time.sleep(0.1) + + kbd.send(Keycode.WINDOWS) + + if line == "escape": + + if holdingKeys == 1: + + keysToPress.append(Keycode.ESCAPE) + + else: + + time.sleep(0.1) + + kbd.send(Keycode.ESCAPE) + + if line == "tab": + + if holdingKeys == 1: + + keysToPress.append(Keycode.TAB) + + else: + + time.sleep(0.1) + + kbd.send(Keycode.TAB) + + + + + if line == "windows": + + if holdingKeys == 1: + + keysToPress.append(Keycode.WINDOWS) + + else: + + time.sleep(0.1) + + kbd.send(Keycode.WINDOWS) + + if line == "ctrl": + + if holdingKeys == 1: + + keysToPress.append(Keycode.LEFT_CONTROL) + else: + time.sleep(0.1) + + kbd.send(Keycode.LEFT_CONTROL) + + + if line == "shift": + + if holdingKeys == 1: + + keysToPress.append(Keycode.LEFT_SHIFT) + else: + time.sleep(0.1) + + kbd.send(Keycode.LEFT_SHIFT) + + if line.startswith("LCD_SHOW \""): + + line = line.replace("LCD_SHOW \"", "\"") + + line = line.replace('"', "") + + lineToShow = line + + clearScr = 0 + + if line.startswith("#"): + pass + + if line == "": + pass + + if line.startswith("WAIT"): + + line = line.replace("WAIT", "") + line = line.replace(" ", "") + + sleepingTime = line + + + + clearScr = 1 + + if line.startswith('"') and line.endswith('"'): + + lineLength = len(line) + + tempLine = "" + + currentLine = line + + for i in range(0, lineLength): + + if i == 0: + pass + + elif i != (lineLength - 1): + + tempLine = tempLine + currentLine[i] + + line = tempLine + + if holdingKeys == 0: + + layout.write(line) + + line = currentLine + + if len(tempLine) == 1: + + if holdingKeys == 1: + + keysToPress.append(layout.keycodes(tempLine)[0]) + + + if line == "downarrow": + if holdingKeys == 1: + + keysToPress.append(Keycode.DOWN_ARROW) + + else: + + time.sleep(0.1) + + kbd.send(Keycode.DOWN_ARROW) + + + elif line == "uparrow": + + if holdingKeys == 1: + + keysToPress.append(Keycode.UP_ARROW) + + else: + + time.sleep(0.1) + + kbd.send(Keycode.UP_ARROW) + + + elif line == "leftarrow": + if holdingKeys == 1: + + keysToPress.append(Keycode.LEFT_ARROW) + + else: + + time.sleep(0.1) + + kbd.send(Keycode.LEFT_ARROW) + elif line == "space": + if holdingKeys == 1: + + keysToPress.append(Keycode.SPACE) + + else: + + time.sleep(0.1) + + kbd.send(Keycode.SPACE) + + elif line == "capslock": + if holdingKeys == 1: + + keysToPress.append(Keycode.CAPS_LOCK) + + else: + + time.sleep(0.1) + + kbd.send(Keycode.CAPS_LOCK) + + + elif line == "rightarrow": + if holdingKeys == 1: + + keysToPress.append(Keycode.RIGHT_ARROW) + + else: + + time.sleep(0.1) + + kbd.send(Keycode.RIGHT_ARROW) + + elif line == "forwardslash": + if holdingKeys == 1: + + keysToPress.append(Keycode.FORWARD_SLASH) + + else: + + time.sleep(0.1) + + kbd.send(Keycode.FORWARD_SLASH) + + + elif line == "backslash": + if holdingKeys == 1: + + keysToPress.append(Keycode.BACKSLASH) + + else: + + time.sleep(0.1) + + kbd.send(Keycode.BACKSLASH) + + elif line == "backspace": + if holdingKeys == 1: + + keysToPress.append(Keycode.BACKSPACE) + + else: + + time.sleep(0.1) + + kbd.send(Keycode.BACKSPACE) + + + elif line == "enter": + if holdingKeys == 1: + + keysToPress.append(Keycode.ENTER) + + else: + + time.sleep(0.1) + + kbd.send(Keycode.ENTER) + + + print("KEYS_PRESSED: " + str(len(keysToPress))) + + if clearScr == 1 and workingLine.startswith("WAIT "): + lcd.clear() + + lcd.setCursor(0,1) + lcd.printout("> "+workingLine) + lcd.setCursor(0,0) + lcd.printout(lineToShow) + print(lineToShow) + + if lineToShow != "": + + lineToShow = "" + else: + lcd.clear() + + lcd.setCursor(0,1) + lcd.printout("> "+workingLine) + + + time.sleep(float(sleepingTime)) + time.sleep(0.5) + +while True: + + if not buttonOne.value: + lcd.clear() + lcd.setCursor(0,0) + lcd.printout("Install Arch") + time.sleep(1) + + lcd.clear() + + macro_parser("archinstall.mcr") + + lcd.clear() + + elif not buttonTwo.value: + lcd.clear() + lcd.setCursor(0,0) + lcd.printout("Macro Syntax") + time.sleep(1) + lcd.clear() + + macro_parser("syntax.mcr") + + lcd.clear() + + elif not buttonThree.value: + lcd.clear() + lcd.setCursor(0,0) + lcd.printout("Test Macro") + time.sleep(1) + lcd.clear() + + macro_parser("test.mcr") + + + lcd.clear() + + lcd.setCursor(0,0) + + lcd.printout("Kico v4") + + lcd.setCursor(0,1) + + lcd.printout("Hit a key!") + diff --git a/lib/adafruit_ahtx0.mpy b/lib/adafruit_ahtx0.mpy new file mode 100644 index 0000000..6edfd11 Binary files /dev/null and b/lib/adafruit_ahtx0.mpy differ diff --git a/lib/adafruit_bus_device/__init__.py b/lib/adafruit_bus_device/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/lib/adafruit_bus_device/i2c_device.mpy b/lib/adafruit_bus_device/i2c_device.mpy new file mode 100644 index 0000000..0bf7c9f Binary files /dev/null and b/lib/adafruit_bus_device/i2c_device.mpy differ diff --git a/lib/adafruit_bus_device/spi_device.mpy b/lib/adafruit_bus_device/spi_device.mpy new file mode 100644 index 0000000..1d0812f Binary files /dev/null and b/lib/adafruit_bus_device/spi_device.mpy differ diff --git a/lib/adafruit_hid/__init__.mpy b/lib/adafruit_hid/__init__.mpy new file mode 100644 index 0000000..fc3a7b7 Binary files /dev/null and b/lib/adafruit_hid/__init__.mpy differ diff --git a/lib/adafruit_hid/consumer_control.mpy b/lib/adafruit_hid/consumer_control.mpy new file mode 100644 index 0000000..6fecb4d Binary files /dev/null and b/lib/adafruit_hid/consumer_control.mpy differ diff --git a/lib/adafruit_hid/consumer_control_code.mpy b/lib/adafruit_hid/consumer_control_code.mpy new file mode 100644 index 0000000..618edca Binary files /dev/null and b/lib/adafruit_hid/consumer_control_code.mpy differ diff --git a/lib/adafruit_hid/keyboard.mpy b/lib/adafruit_hid/keyboard.mpy new file mode 100644 index 0000000..c1a036a Binary files /dev/null and b/lib/adafruit_hid/keyboard.mpy differ diff --git a/lib/adafruit_hid/keyboard_layout_base.mpy b/lib/adafruit_hid/keyboard_layout_base.mpy new file mode 100644 index 0000000..7ffdc06 Binary files /dev/null and b/lib/adafruit_hid/keyboard_layout_base.mpy differ diff --git a/lib/adafruit_hid/keyboard_layout_us.mpy b/lib/adafruit_hid/keyboard_layout_us.mpy new file mode 100644 index 0000000..7b3398c Binary files /dev/null and b/lib/adafruit_hid/keyboard_layout_us.mpy differ diff --git a/lib/adafruit_hid/keycode.mpy b/lib/adafruit_hid/keycode.mpy new file mode 100644 index 0000000..8a5c631 Binary files /dev/null and b/lib/adafruit_hid/keycode.mpy differ diff --git a/lib/adafruit_hid/mouse.mpy b/lib/adafruit_hid/mouse.mpy new file mode 100644 index 0000000..4eb4532 Binary files /dev/null and b/lib/adafruit_hid/mouse.mpy differ diff --git a/lib/adafruit_register/__init__.py b/lib/adafruit_register/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/lib/adafruit_register/i2c_bcd_alarm.mpy b/lib/adafruit_register/i2c_bcd_alarm.mpy new file mode 100644 index 0000000..833e909 Binary files /dev/null and b/lib/adafruit_register/i2c_bcd_alarm.mpy differ diff --git a/lib/adafruit_register/i2c_bcd_datetime.mpy b/lib/adafruit_register/i2c_bcd_datetime.mpy new file mode 100644 index 0000000..161a81b Binary files /dev/null and b/lib/adafruit_register/i2c_bcd_datetime.mpy differ diff --git a/lib/adafruit_register/i2c_bit.mpy b/lib/adafruit_register/i2c_bit.mpy new file mode 100644 index 0000000..d097614 Binary files /dev/null and b/lib/adafruit_register/i2c_bit.mpy differ diff --git a/lib/adafruit_register/i2c_bits.mpy b/lib/adafruit_register/i2c_bits.mpy new file mode 100644 index 0000000..1d865a8 Binary files /dev/null and b/lib/adafruit_register/i2c_bits.mpy differ diff --git a/lib/adafruit_register/i2c_struct.mpy b/lib/adafruit_register/i2c_struct.mpy new file mode 100644 index 0000000..c9bc877 Binary files /dev/null and b/lib/adafruit_register/i2c_struct.mpy differ diff --git a/lib/adafruit_register/i2c_struct_array.mpy b/lib/adafruit_register/i2c_struct_array.mpy new file mode 100644 index 0000000..0df98b3 Binary files /dev/null and b/lib/adafruit_register/i2c_struct_array.mpy differ diff --git a/lib/keyboard_layout_win_uk.mpy b/lib/keyboard_layout_win_uk.mpy new file mode 100644 index 0000000..3889e63 Binary files /dev/null and b/lib/keyboard_layout_win_uk.mpy differ diff --git a/lib/keycode_win_uk.mpy b/lib/keycode_win_uk.mpy new file mode 100644 index 0000000..6d139bb Binary files /dev/null and b/lib/keycode_win_uk.mpy differ diff --git a/lib/lcd/__init__.py b/lib/lcd/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/lib/lcd/i2c_pcf8574_interface.py b/lib/lcd/i2c_pcf8574_interface.py new file mode 100644 index 0000000..08e3a22 --- /dev/null +++ b/lib/lcd/i2c_pcf8574_interface.py @@ -0,0 +1,95 @@ +# Copyright (C) 2017 Dan Halbert +# Adapted from https://github.com/dbrgn/RPLCD, Copyright (C) 2013-2016 Danilo Bargen + +# Permission is hereby granted, free of charge, to any person obtaining a copy of +# this software and associated documentation files (the "Software"), to deal in +# the Software without restriction, including without limitation the rights to +# use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +# the Software, and to permit persons to whom the Software is furnished to do so, +# subject to the following conditions: + +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. + +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +# FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +# COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +# IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +"""Low-level interface to PCF8574.""" + +import busio +import board +import microcontroller +from adafruit_bus_device.i2c_device import I2CDevice + +from .lcd import LCD_4BITMODE, LCD_BACKLIGHT, LCD_NOBACKLIGHT, PIN_ENABLE + + +class I2CPCF8574Interface: + + # Bit values to turn backlight on/off. Indexed by a boolean. + _BACKLIGHT_VALUES = (LCD_NOBACKLIGHT, LCD_BACKLIGHT) + + def __init__(self, i2c, address): + """ + CharLCD via PCF8574 I2C port expander. + + Pin mapping:: + + 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 + D7 | D6 | D5 | D4 | BL | EN | RW | RS + + :param address: The I2C address of your LCD. + """ + self.address = address + + self._backlight_pin_state = LCD_BACKLIGHT + + self.i2c = i2c + self.i2c_device = I2CDevice(self.i2c, self.address) + self.data_buffer = bytearray(1) + + def deinit(self): + self.i2c.deinit() + + @property + def data_bus_mode(self): + return LCD_4BITMODE + + @property + def backlight(self): + return self._backlight_pin_state == LCD_BACKLIGHT + + @backlight.setter + def backlight(self, value): + self._backlight_pin_state = self._BACKLIGHT_VALUES[value] + with self.i2c_device: + self._i2c_write(self._backlight_pin_state) + + # Low level commands + + def send(self, value, rs_mode): + """Send the specified value to the display in 4-bit nibbles. + The rs_mode is either ``_RS_DATA`` or ``_RS_INSTRUCTION``.""" + self._write4bits(rs_mode | (value & 0xF0) | self._backlight_pin_state) + self._write4bits(rs_mode | ((value << 4) & 0xF0) | self._backlight_pin_state) + + def _write4bits(self, value): + """Pulse the `enable` flag to process value.""" + with self.i2c_device: + self._i2c_write(value & ~PIN_ENABLE) + # This 1us delay is probably unnecessary, given the time needed + # to execute the statements. + microcontroller.delay_us(1) + self._i2c_write(value | PIN_ENABLE) + microcontroller.delay_us(1) + self._i2c_write(value & ~PIN_ENABLE) + # Wait for command to complete. + microcontroller.delay_us(100) + + def _i2c_write(self, value): + self.data_buffer[0] = value + self.i2c_device.write(self.data_buffer) diff --git a/lib/lcd/lcd.py b/lib/lcd/lcd.py new file mode 100644 index 0000000..4788841 --- /dev/null +++ b/lib/lcd/lcd.py @@ -0,0 +1,289 @@ +# Copyright (C) 2017 Dan Halbert +# Adapted from https://github.com/dbrgn/RPLCD, Copyright (C) 2013-2016 Danilo Bargen + +# Permission is hereby granted, free of charge, to any person obtaining a copy of +# this software and associated documentation files (the "Software"), to deal in +# the Software without restriction, including without limitation the rights to +# use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +# the Software, and to permit persons to whom the Software is furnished to do so, +# subject to the following conditions: + +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. + +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +# FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +# COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +# IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +import time +from micropython import const + +# Commands +_LCD_CLEARDISPLAY = const(0x01) +_LCD_RETURNHOME = const(0x02) +_LCD_ENTRYMODESET = const(0x04) +_LCD_DISPLAYCONTROL = const(0x08) +_LCD_CURSORSHIFT = const(0x10) +_LCD_FUNCTIONSET = const(0x20) +_LCD_SETCGRAMADDR = const(0x40) +_LCD_SETDDRAMADDR = const(0x80) + +# Flags for display entry mode +_LCD_ENTRYRIGHT = const(0x00) +_LCD_ENTRYLEFT = const(0x02) +_LCD_ENTRYSHIFTINCREMENT = const(0x01) +_LCD_ENTRYSHIFTDECREMENT = const(0x00) + +# Flags for display on/off control +_LCD_DISPLAYON = const(0x04) +_LCD_DISPLAYOFF = const(0x00) +_LCD_CURSORON = const(0x02) +_LCD_CURSOROFF = const(0x00) +_LCD_BLINKON = const(0x01) +_LCD_BLINKOFF = const(0x00) + +# Flags for display/cursor shift +_LCD_DISPLAYMOVE = const(0x08) +_LCD_CURSORMOVE = const(0x00) +_LCD_MOVERIGHT = const(0x04) +_LCD_MOVELEFT = const(0x00) + +# Flags for function set +_LCD_8BITMODE = const(0x10) +LCD_4BITMODE = const(0x00) +_LCD_2LINE = const(0x08) +_LCD_1LINE = const(0x00) +_LCD_5x10DOTS = const(0x04) +_LCD_5x8DOTS = const(0x00) + +# Flags for backlight control +LCD_BACKLIGHT = const(0x08) +LCD_NOBACKLIGHT = const(0x00) + +# Flags for RS pin modes +_RS_INSTRUCTION = const(0x00) +_RS_DATA = const(0x01) + +# Pin bitmasks +PIN_ENABLE = const(0x4) +PIN_READ_WRITE = const(0x2) +PIN_REGISTER_SELECT = const(0x1) + +class CursorMode: + HIDE = const(_LCD_CURSOROFF | _LCD_BLINKOFF) + LINE = const(_LCD_CURSORON | _LCD_BLINKOFF) + BLINK = const(_LCD_CURSOROFF | _LCD_BLINKON) + +MICROSECOND = 1e-6 +MILLISECOND = 1e-3 + +class LCD(object): + + def __init__(self, interface, num_cols=20, num_rows=4, char_height=8): + """ + Character LCD controller. + + :param interface: Communication interface, such as I2CInterface + :param num_rows: Number of display rows (usually 1, 2 or 4). Default: 4. + :param num_cols: Number of columns per row (usually 16 or 20). Default 20. + :param char_height: Some 1 line displays allow a font height of 10px. + Allowed: 8 or 10. Default: 8. + """ + self.interface = interface + + if char_height not in (8, 10): + raise ValueError('The ``char_height`` argument should be either 8 or 10.') + self.char_height = char_height + + self.num_rows = num_rows + self.num_cols = num_cols + + # get row addresses (varies based on display size) + self._row_offsets = (0x00, 0x40, self.num_cols, 0x40 + self.num_cols) + + # Setup initial display configuration + displayfunction = self.interface.data_bus_mode | _LCD_5x8DOTS + if self.num_rows == 1: + displayfunction |= _LCD_1LINE + elif self.num_rows in (2, 4): + # LCD only uses two lines on 4 row displays + displayfunction |= _LCD_2LINE + if self.char_height == 10: + # For some 1 line displays you can select a 10px font. + displayfunction |= _LCD_5x10DOTS + + # Choose 4 or 8 bit mode + self.command(0x03) + time.sleep(4.5*MILLISECOND) + self.command(0x03) + time.sleep(4.5*MILLISECOND) + self.command(0x03) + if self.interface.data_bus_mode == LCD_4BITMODE: + # Hitachi manual page 46 + time.sleep(100*MICROSECOND) + self.command(0x02) + elif self.interface.data_bus_mode == _LCD_8BITMODE: + # Hitachi manual page 45 + self.command(0x30) + time.sleep(4.5*MILLISECOND) + self.command(0x30) + time.sleep(100*MICROSECOND) + self.command(0x30) + else: + raise ValueError('Invalid data bus mode: {}'.format(self.interface.data_bus_mode)) + + # Write configuration to display + self.command(_LCD_FUNCTIONSET | displayfunction) + time.sleep(50*MICROSECOND) + + # Configure entry mode. Define internal fields. + self.command(_LCD_ENTRYMODESET | _LCD_ENTRYLEFT) + time.sleep(50*MICROSECOND) + + # Configure display mode. Define internal fields. + self._display_mode = _LCD_DISPLAYON + self._cursor_mode = CursorMode.HIDE + self.command(_LCD_DISPLAYCONTROL | self._display_mode | self._cursor_mode) + time.sleep(50*MICROSECOND) + + self.clear() + + def close(self): + self.interface.deinit() + + def set_backlight(self, value): + self.interface.backlight = value + + def set_display_enabled(self, value): + self._display_mode = _LCD_DISPLAYON if value else _LCD_DISPLAYOFF + self.command(_LCD_DISPLAYCONTROL | self._display_mode | self._cursor_mode) + time.sleep(50*MICROSECOND) + + def set_cursor_mode(self, value): + self._cursor_mode = value + self.command(_LCD_DISPLAYCONTROL | self._display_mode | self._cursor_mode) + time.sleep(50*MICROSECOND) + + def cursor_pos(self): + """The cursor position as a 2-tuple (row, col).""" + return (self._row, self._col) + + def set_cursor_pos(self, row, col): + if not (0 <= row < self.num_rows): + raise ValueError('row should be in range 0-{}'.format(self.num_rows - 1)) + if not (0 <= col < self.num_cols): + raise ValueError('col should be in range 0-{}'.format(self.num_cols - 1)) + self._row = row + self._col = col + self.command(_LCD_SETDDRAMADDR | self._row_offsets[row] + col) + time.sleep(50*MICROSECOND) + + def print(self, string): + """ + Write the specified unicode string to the display. + A newline ('\n') will advance to the left side of the next row. + Lines that are too long automatically continue on next line. + + Only characters with an ``ord()`` value between 0 and 255 are currently + supported. + + """ + for char in string: + if char == '\n': + # Advance to next row, at left side. Wrap around to top row if at bottom. + self.set_cursor_pos((self._row + 1) % self.num_rows, 0) + else: + self.write(ord(char)) + + + def clear(self): + """Overwrite display with blank characters and reset cursor position.""" + self.command(_LCD_CLEARDISPLAY) + time.sleep(2*MILLISECOND) + self.home() + + def home(self): + """Set cursor to initial position and reset any shifting.""" + self.command(_LCD_RETURNHOME) + self._row = 0 + self._col = 0 + time.sleep(2*MILLISECOND) + + def shift_display(self, amount): + """Shift the display. Use negative amounts to shift left and positive + amounts to shift right.""" + if amount == 0: + return + direction = _LCD_MOVERIGHT if amount > 0 else _LCD_MOVELEFT + for i in range(abs(amount)): + self.command(_LCD_CURSORSHIFT | _LCD_DISPLAYMOVE | direction) + time.sleep(50*MICROSECOND) + + def create_char(self, location, bitmap): + """Create a new character. + + The HD44780 supports up to 8 custom characters (location 0-7). + + :param location: The place in memory where the character is stored. + Values need to be integers between 0 and 7. + :type location: int + :param bitmap: The bitmap containing the character. This should be a + bytearray of 8 numbers, each representing a 5 pixel row. + :type bitmap: bytearray + :raises AssertionError: Raised when an invalid location is passed in or + when bitmap has an incorrect size. + + Example: + + .. sourcecode:: python + + >>> smiley = bytearray( + ... 0b00000, + ... 0b01010, + ... 0b01010, + ... 0b00000, + ... 0b10001, + ... 0b10001, + ... 0b01110, + ... 0b00000, + ... ) + >>> lcd.create_char(0, smiley) + + """ + if not (0 <= location <= 7): + raise ValueError('Only locations 0-7 are valid.') + if len(bitmap) != 8: + raise ValueError('Bitmap should have exactly 8 rows.') + + # Store previous position + save_row = self._row + save_col = self._col + + # Write character to CGRAM + self.command(_LCD_SETCGRAMADDR | location << 3) + for row in bitmap: + self.interface.send(row, _RS_DATA) + + # Restore cursor pos + self.set_cursor_pos(save_row, save_col) + + def command(self, value): + """Send a raw command to the LCD.""" + self.interface.send(value, _RS_INSTRUCTION) + + def write(self, value): + """Write a raw character byte to the LCD.""" + self.interface.send(value, _RS_DATA) + if self._col < self.num_cols - 1: + # Char was placed on current line. No need to reposition cursor. + self._col += 1 + else: + # At end of line: go to left side next row. Wrap around to first row if on last row. + self._row = (self._row + 1) % self.num_rows + self._col = 0 + + self.set_cursor_pos(self._row, self._col) + diff --git a/lib/rgb1602/__init__.py b/lib/rgb1602/__init__.py new file mode 100644 index 0000000..04f7efa --- /dev/null +++ b/lib/rgb1602/__init__.py @@ -0,0 +1 @@ +from rgb1602.display import Screen diff --git a/lib/rgb1602/colours.py b/lib/rgb1602/colours.py new file mode 100644 index 0000000..498181c --- /dev/null +++ b/lib/rgb1602/colours.py @@ -0,0 +1,164 @@ +CSS_COLOURS: dict[str, tuple[int, int, int]] = { + "aliceblue": (240, 248, 255), + "antiquewhite": (250, 235, 215), + "aqua": (0, 255, 255), + "aquamarine": (127, 255, 212), + "azure": (240, 255, 255), + "beige": (245, 245, 220), + "bisque": (255, 228, 196), + "black": (0, 0, 0), + "blanchedalmond": (255, 235, 205), + "blue": (0, 0, 255), + "blueviolet": (138, 43, 226), + "brown": (165, 42, 42), + "burlywood": (222, 184, 135), + "cadetblue": (95, 158, 160), + "chartreuse": (127, 255, 0), + "chocolate": (210, 105, 30), + "coral": (255, 127, 80), + "cornflowerblue": (100, 149, 237), + "cornsilk": (255, 248, 220), + "crimson": (220, 20, 60), + "cyan": (0, 255, 255), + "darkblue": (0, 0, 139), + "darkcyan": (0, 139, 139), + "darkgoldenrod": (184, 134, 11), + "darkgray": (169, 169, 169), + "darkgreen": (0, 100, 0), + "darkgrey": (169, 169, 169), + "darkkhaki": (189, 183, 107), + "darkmagenta": (139, 0, 139), + "darkolivegreen": (85, 107, 47), + "darkorange": (255, 140, 0), + "darkorchid": (153, 50, 204), + "darkred": (139, 0, 0), + "darksalmon": (233, 150, 122), + "darkseagreen": (143, 188, 143), + "darkslateblue": (72, 61, 139), + "darkslategray": (47, 79, 79), + "darkslategrey": (47, 79, 79), + "darkturquoise": (0, 206, 209), + "darkviolet": (148, 0, 211), + "deeppink": (255, 20, 147), + "deepskyblue": (0, 191, 255), + "dimgray": (105, 105, 105), + "dimgrey": (105, 105, 105), + "dodgerblue": (30, 144, 255), + "firebrick": (178, 34, 34), + "floralwhite": (255, 250, 240), + "forestgreen": (34, 139, 34), + "fuchsia": (255, 0, 255), + "gainsboro": (220, 220, 220), + "ghostwhite": (248, 248, 255), + "gold": (255, 215, 0), + "goldenrod": (218, 165, 32), + "gray": (128, 128, 128), + "green": (0, 128, 0), + "greenyellow": (173, 255, 47), + "grey": (128, 128, 128), + "honeydew": (240, 255, 240), + "hotpink": (255, 105, 180), + "indianred": (205, 92, 92), + "indigo": (75, 0, 130), + "ivory": (255, 255, 240), + "khaki": (240, 230, 140), + "lavender": (230, 230, 250), + "lavenderblush": (255, 240, 245), + "lawngreen": (124, 252, 0), + "lemonchiffon": (255, 250, 205), + "lightblue": (173, 216, 230), + "lightcoral": (240, 128, 128), + "lightcyan": (224, 255, 255), + "lightgoldenrodyellow": (250, 250, 210), + "lightgray": (211, 211, 211), + "lightgreen": (144, 238, 144), + "lightgrey": (211, 211, 211), + "lightpink": (255, 182, 193), + "lightsalmon": (255, 160, 122), + "lightseagreen": (32, 178, 170), + "lightskyblue": (135, 206, 250), + "lightslategray": (119, 136, 153), + "lightslategrey": (119, 136, 153), + "lightsteelblue": (176, 196, 222), + "lightyellow": (255, 255, 224), + "lime": (0, 255, 0), + "limegreen": (50, 205, 50), + "linen": (250, 240, 230), + "magenta": (255, 0, 255), + "maroon": (128, 0, 0), + "mediumaquamarine": (102, 205, 170), + "mediumblue": (0, 0, 205), + "mediumorchid": (186, 85, 211), + "mediumpurple": (147, 112, 219), + "mediumseagreen": (60, 179, 113), + "mediumslateblue": (123, 104, 238), + "mediumspringgreen": (0, 250, 154), + "mediumturquoise": (72, 209, 204), + "mediumvioletred": (199, 21, 133), + "midnightblue": (25, 25, 112), + "mintcream": (245, 255, 250), + "mistyrose": (255, 228, 225), + "moccasin": (255, 228, 181), + "navajowhite": (255, 222, 173), + "navy": (0, 0, 128), + "oldlace": (253, 245, 230), + "olive": (128, 128, 0), + "olivedrab": (107, 142, 35), + "orange": (255, 165, 0), + "orangered": (255, 69, 0), + "orchid": (218, 112, 214), + "palegoldenrod": (238, 232, 170), + "palegreen": (152, 251, 152), + "paleturquoise": (175, 238, 238), + "palevioletred": (219, 112, 147), + "papayawhip": (255, 239, 213), + "peachpuff": (255, 218, 185), + "peru": (205, 133, 63), + "pink": (255, 192, 203), + "plum": (221, 160, 221), + "powderblue": (176, 224, 230), + "purple": (128, 0, 128), + "rebeccapurple": (102, 51, 153), + "red": (255, 0, 0), + "rosybrown": (188, 143, 143), + "royalblue": (65, 105, 225), + "saddlebrown": (139, 69, 19), + "salmon": (250, 128, 114), + "sandybrown": (244, 164, 96), + "seagreen": (46, 139, 87), + "seashell": (255, 245, 238), + "sienna": (160, 82, 45), + "silver": (192, 192, 192), + "skyblue": (135, 206, 235), + "slateblue": (106, 90, 205), + "slategray": (112, 128, 144), + "slategrey": (112, 128, 144), + "snow": (255, 250, 250), + "springgreen": (0, 255, 127), + "steelblue": (70, 130, 180), + "tan": (210, 180, 140), + "teal": (0, 128, 128), + "thistle": (216, 191, 216), + "tomato": (255, 99, 71), + "turquoise": (64, 224, 208), + "violet": (238, 130, 238), + "wheat": (245, 222, 179), + "white": (255, 255, 255), + "whitesmoke": (245, 245, 245), + "yellow": (255, 255, 0), + "yellowgreen": (154, 205, 50), +} +CSS_COLORS = CSS_COLOURS + +WAVESHARE_COLOURS = { + "Deep violet": (148, 0, 110), + "Purple": (255, 0, 255), + "Blue and white": (144, 249, 15), + "Light blue": (0, 128, 60), + "Yellow": (255, 209, 0), + "Ghost white": (248, 248, 60), + "Dark blue": (80, 80, 145), + "Red": (255, 0, 0), + "Cyan": (0, 255, 0), +} +WAVESHARE_COLORS = WAVESHARE_COLOURS diff --git a/lib/rgb1602/display.py b/lib/rgb1602/display.py new file mode 100644 index 0000000..78985cc --- /dev/null +++ b/lib/rgb1602/display.py @@ -0,0 +1,307 @@ +# This file converted from MicroPython to CircuitPython +# by robjwells. Original copyright Waveshare. + +from time import sleep + +from adafruit_bus_device.i2c_device import I2CDevice +from adafruit_register.i2c_struct import UnaryStruct +from busio import I2C +from microcontroller import Pin + +from rgb1602.colours import CSS_COLOURS + + +class Constants: + # Device I2C Addresses + LCD_ADDRESS = 0x7C >> 1 + RGB_ADDRESS = 0x3e >> 1 + + # fmt: off + # CGRAM and DDRAM commands but also their memory addresses + LCD_COMMAND_REG = 0x80 + LCD_SETDDRAMADDR = 0x80 # 0b1_______ Display Data RAM + LCD_SETCGRAMADDR = 0x40 # 0b_1______ Character Generator RAM + LCD_DATA_REG = 0x40 + + # RGB registers + REG_RED = 0x04 # 0b_1__ pwm2 + REG_GREEN = 0x03 # 0b__11 pwm1 + REG_BLUE = 0x02 # 0b__1_ pwm0 + + REG_MODE1 = 0x00 # 0b____ + REG_MODE2 = 0x01 # 0b___1 + REG_OUTPUT = 0x08 # 0b1___ + + # Commands -- bits of an 8-bit word + LCD_CLEARDISPLAY = 0x01 # 0b_______1 + LCD_RETURNHOME = 0x02 # 0b______1_ + + LCD_ENTRYMODESET = 0x04 # 0b_____1__ + # Used with bits I/D SH: + # I/D: 0x02 for entry left, 0x00 for entry right + # SH: 0x01 for shift increment, 0x00 for decrement + # See "flags for display entry mode" + + LCD_DISPLAYCONTROL = 0x08 # 0b____1___ + # Used with bits DCB: + # D: display on/off, + # C: cursor on/off, + # B: blink cursor on/off + # These are listed below as "flags for display on/off control" + + LCD_CURSORSHIFT = 0x10 # 0b___1____ + # Cursor or Display Shift + # Uses bits S/C R/L - -: + # S/C: 0x08 for screen or 0x00 for cursor + # R/L: 0x04 for right or 0x00 for left + + LCD_FUNCTIONSET = 0x20 # 0b__1_____ + # Sets number of display lines, and display font type. + # The documentation doesn't mention 8/4 bit mode. + + # flags for display entry mode + LCD_ENTRYRIGHT = 0x00 # 0b00 + LCD_ENTRYLEFT = 0x02 # 0b10 + LCD_ENTRYSHIFTINCREMENT = 0x01 # 0b01 + LCD_ENTRYSHIFTDECREMENT = 0x00 # 0b00 + + # flags for display on/off control + LCD_DISPLAYON = 0x04 # 0b1__ + LCD_CURSORON = 0x02 # 0b_1_ + LCD_BLINKON = 0x01 # 0b__1 + + LCD_DISPLAYOFF = 0x00 # 0b000 + LCD_CURSOROFF = 0x00 # 0b000 + LCD_BLINKOFF = 0x00 # 0b000 + + # flags for display/cursor shift + LCD_DISPLAYMOVE = 0x08 # 0b1000 + LCD_CURSORMOVE = 0x00 # 0b0000 + LCD_MOVERIGHT = 0x04 # 0b0100 + LCD_MOVELEFT = 0x00 # 0b0000 + + # flags for function set + + # I think the modes relate the to the number of bits + # sent down the wire at a time, _not_ the colour + # depth (as I thought originally). + LCD_8BITMODE = 0x10 # 0b10000 + LCD_4BITMODE = 0x00 # 0b00000 + + # Number of lines on the display + LCD_2LINE = 0x08 # 0b01000 + LCD_1LINE = 0x00 # 0b00000 + LCD_5x8DOTS = 0x00 # 0b00000 + + # fmt: on + + +class LCDControl: + def __init__(self, i2c: I2CDevice): + self.i2c_device = i2c # Required by UnaryStruct + + command_register = UnaryStruct(Constants.LCD_COMMAND_REG, " None: + self.i2c_device = i2c + + REG_RED = UnaryStruct(Constants.REG_RED, " 0x20 (DMBLNK to 1, ie blinky mode) + self._set_rgb_register("REG_MODE2", 0x20) + self.set_white() + + def _command(self, cmd: int): + assert 0 <= cmd <= 255, f"Command {cmd} out of range." + self._lcd.command_register = cmd + + def _write_byte(self, data: int) -> None: + assert 0 <= data <= 255, f"Command {data} out of range." + self._lcd.data_register = data + + def _set_rgb_register(self, reg: str, data: int) -> None: + assert reg in ( + "REG_RED", + "REG_BLUE", + "REG_GREEN", + "REG_MODE1", + "REG_MODE2", + "REG_OUTPUT", + ), f"Register {reg} is not a known register." + assert 0 <= data <= 255, f"Data {data} is out of range." + setattr(self._rgb, reg, data) + + def _set_rgb_mode(self, mode: int, value: int) -> None: + assert 0 <= value <= 0xFF, "Value not in range." + if mode == 1: + self._set_rgb_register("REG_MODE1", value) + elif mode == 2: + self._set_rgb_register("REG_MODE2", value) + else: + raise ValueError(f"Unknown mode: {repr(mode)}") + + def set_rgb(self, r: int, g: int, b: int): + assert 0 <= r <= 255, f"Red value {r} out of range." + assert 0 <= g <= 255, f"Green value {g} out of range." + assert 0 <= b <= 255, f"Blue value {b} out of range." + + self._set_rgb_register("REG_RED", r) + self._set_rgb_register("REG_GREEN", g) + self._set_rgb_register("REG_BLUE", b) + self.current_colour = (r, g, b) + + def position_cursor(self, *, col: int, row: int): + assert ( + 0 <= col < self.COLS + ), f"Column {col} is out of bounds (max {self.COLS - 1})." + assert ( + 0 <= row < self.ROWS + ), f"Row {row} is out of bounds (max {self.ROWS - 1})." + + if row == 0: + col |= 0x80 + else: + col |= 0xC0 + + assert self._i2c.try_lock(), "Could not lock" + self._i2c.writeto( + Constants.LCD_ADDRESS, bytearray([Constants.LCD_SETDDRAMADDR, col]) + ) + self._i2c.unlock() + + def clear(self): + self._command(Constants.LCD_CLEARDISPLAY) + sleep(0.002) + + def write_bytes(self, arg: bytes): + for byte in arg: + self._write_byte(byte) + + def write_at_position(self, text: str | bytes, *, col: int, row: int) -> None: + self.position_cursor(col=col, row=row) + self.write_bytes(self._ensure_bytes(text)) + + @staticmethod + def _ensure_bytes(s: str | bytes) -> bytes: + if isinstance(s, bytes): + return s + # Not JIS X 0213 but close enough if you’re careful. + return bytes(s, "jisx0213") + + def update( + self, first_line: str | bytes, second_line: str | bytes | None = None + ) -> None: + first = self._ensure_bytes(first_line) + self.clear() + self.write_bytes(first[: self.COLS]) + if second_line is not None: + self.position_cursor(col=0, row=1) + second = self._ensure_bytes(second_line) + self.write_bytes(second[: self.COLS]) + + def set_white(self) -> None: + self.set_css_colour("white") + + def set_css_colour(self, colour_name: str) -> None: + self.set_rgb(*CSS_COLOURS[colour_name]) + + def set_css_color(self, color_name: str) -> None: + self.set_css_colour(color_name) + + def set_backlight_power(self, on: bool) -> None: + data = 0xFF if on else 0x00 + self._set_rgb_register("REG_OUTPUT", data) + + @staticmethod + def special_char(c: str) -> bytes: + if c == "\\": + raise ValueError("\\ (backslash) is not in the character set.") + elif ord(c) < ord("}"): + # Everything matches ASCII up to }, except for \ -> ¥. + return c.encode("ascii") + + chars = { + "→": b"\x7E", + "←": b"\x7F", + "•": b"\xA5", + "☐": b"\xDB", + "°": b"\xDF", + "alpha": b"\xE0", + "beta": b"\xE2", + "epsilon": b"\xE3", + "mu": b"\xE4", + "sigma": b"\xE5", + "rho": b"\xE6", + "√": b"\xE8", + "theta": b"\xF2", + "omega": b"\xF4", + "SIGMA": b"\xF6", + "pi": b"\xF7", + "÷": b"\xFD", + "block": b"\xFF", + } + + try: + return chars[c] + except KeyError: + raise ValueError(f"Character {repr(c)} is not a registered special character.") diff --git a/lib/rgb1602/examples.py b/lib/rgb1602/examples.py new file mode 100644 index 0000000..c281df1 --- /dev/null +++ b/lib/rgb1602/examples.py @@ -0,0 +1,45 @@ +from time import sleep + +from rgb1602.colours import CSS_COLOURS, WAVESHARE_COLOURS +from rgb1602.display import Screen + + +def _show_colours( + screen: Screen, + delay: int, + colours: dict[str, tuple[int, int, int]], + colour_set_name: str, +) -> None: + original_rgb = screen.current_colour + for colour_name, rgb in sorted(colours.items()): + screen.set_rgb(*rgb) + screen.update(colour_set_name, colour_name) + sleep(delay) + screen.set_rgb(*original_rgb) + + +def show_css_colours(screen: Screen, delay: int = 2) -> None: + _show_colours(screen, delay, CSS_COLOURS, "CSS named colour") + + +def show_waveshare_colours(screen: Screen, delay: int = 2) -> None: + _show_colours(screen, delay, WAVESHARE_COLOURS, "Waveshare") + + +def show_discoloration_sample(screen: Screen) -> None: + from math import sin + + screen.update(f"Waveshare", "Hello, world!") + t = 0 + while True: + r = int((abs(sin(3.14 * t / 180))) * 255) + g = int((abs(sin(3.14 * (t + 60) / 180))) * 255) + b = int((abs(sin(3.14 * (t + 120) / 180))) * 255) + t = (t + 3) % 360 + + screen.set_rgb(r, g, b) + screen.write_at_position( + str(t).encode() + screen.special_char("°") + b" ", col=10, row=0 + ) + + sleep(0.3) diff --git a/lib/waveshare_lcd1602.py b/lib/waveshare_lcd1602.py new file mode 100644 index 0000000..6b97667 --- /dev/null +++ b/lib/waveshare_lcd1602.py @@ -0,0 +1,234 @@ +# SPDX-FileCopyrightText: 2017 Scott Shawcroft, written for Adafruit Industries +# SPDX-FileCopyrightText: Copyright (c) Waveshare +# SPDX-FileCopyrightText: Copyright (c) 2022 Neradoc +# +# SPDX-License-Identifier: MIT +# +# In order to keep basic compatibility with the orifinal library, ignore "bad" names. +# pylint: disable=invalid-name +""" +`waveshare_lcd1602` +================================================================================ + +Drive for Waveshare's I2C character display LCD1602. +A port of Waveshare's RGB1602 Micropython library. + +* Author(s): Neradoc + +Implementation Notes +-------------------- + +**Hardware:** + +* `Waveshare LCD1602 RGB Module `_ + +**Software and Dependencies:** + +* Adafruit CircuitPython firmware for the supported boards: + https://circuitpython.org/downloads +* Adafruit's Bus Device library: https://github.com/adafruit/Adafruit_CircuitPython_BusDevice +""" + +import time +from adafruit_bus_device.i2c_device import I2CDevice + +try: + from typing import Union + import busio +except ImportError: + pass + +__version__ = "0.0.0-auto.0" +__repo__ = "https://github.com/Neradoc/Circuitpython_Waveshare_LCD1602.git" + +# Device I2C Arress +LCD_ADDRESS = 0x7C >> 1 +RGB_ADDRESS = 0xC0 >> 1 + +# color define + +REG_RED = 0x04 +REG_GREEN = 0x03 +REG_BLUE = 0x02 +REG_MODE1 = 0x00 +REG_MODE2 = 0x01 +REG_OUTPUT = 0x08 +LCD_CLEARDISPLAY = 0x01 +LCD_RETURNHOME = 0x02 +LCD_ENTRYMODESET = 0x04 +LCD_DISPLAYCONTROL = 0x08 +LCD_CURSORSHIFT = 0x10 +LCD_FUNCTIONSET = 0x20 +LCD_SETCGRAMADDR = 0x40 +LCD_SETDDRAMADDR = 0x80 + +# flags for display entry mode +LCD_ENTRYRIGHT = 0x00 +LCD_ENTRYLEFT = 0x02 +LCD_ENTRYSHIFTINCREMENT = 0x01 +LCD_ENTRYSHIFTDECREMENT = 0x00 + +# flags for display on/off control +LCD_DISPLAYON = 0x04 +LCD_DISPLAYOFF = 0x00 +LCD_CURSORON = 0x02 +LCD_CURSOROFF = 0x00 +LCD_BLINKON = 0x01 +LCD_BLINKOFF = 0x00 + +# flags for display/cursor shift +LCD_DISPLAYMOVE = 0x08 +LCD_CURSORMOVE = 0x00 +LCD_MOVERIGHT = 0x04 +LCD_MOVELEFT = 0x00 + +# flags for function set +LCD_8BITMODE = 0x10 +LCD_4BITMODE = 0x00 +LCD_2LINE = 0x08 +LCD_1LINE = 0x00 +LCD_5X8DOTS = 0x00 + + +class LCD1602: + """ + Setup a new RGB LCD1602 display + + :param busio.I2C i2c: I2C bus object to use. + :param int columns: The number of columns on the display. + :param int rows: The number of rows on the display. + """ + + def __init__(self, i2c: busio.I2C, columns: int, rows: int): + self.lcd_device = I2CDevice(i2c, LCD_ADDRESS) + #self.rgb_device = I2CDevice(i2c, RGB_ADDRESS) + self._row = rows + self._col = columns + + self._showfunction = LCD_4BITMODE | LCD_1LINE | LCD_5X8DOTS + self.begin(self._row, self._col) + + def command(self, cmd: int): + """ + Send a command. + + :param int cmd: The command code. + """ + with self.lcd_device: + self.lcd_device.write(bytes([0x80]) + chr(cmd)) + + def write(self, data: int): + """ + Write a data byte. + + :param int data: The data byte to write. + """ + with self.lcd_device: + self.lcd_device.write(bytes([0x40]) + chr(data)) + + def setReg(self, reg, data): + """ + Set the value of a register. + + :param int reg: The register to write to. + :param int data: The data byte to write. + """ + #with self.rgb_device: + # self.rgb_device.write(bytes([reg]) + chr(data)) + + def setRGB(self, r, g, b): + """ + Set the color of the RGB backlight. + + :param int r: Red byte. + :param int g: Green byte. + :param int b: Blue byte. + """ + self.setReg(REG_RED, r) + self.setReg(REG_GREEN, g) + self.setReg(REG_BLUE, b) + + def setCursor(self, col, row): + """ + Place the cursor on the display. + + :param int col: The column. + :param int row: The row. + """ + if row == 0: + col |= 0x80 + else: + col |= 0xC0 + with self.lcd_device: + self.lcd_device.write(bytearray([0x80, col])) + + def clear(self): + """Clear the display.""" + self.command(LCD_CLEARDISPLAY) + time.sleep(0.002) + + def printout(self, arg: Union[str, int]): + """ + Print a string to the display. Ints are converted to strings. + + :param str|int arg: The string or int to printout. + """ + if isinstance(arg, int): + arg = str(arg) + + for x in bytearray(arg, "utf-8"): + self.write(x) + + def display(self): + """Turn on the display.""" + self._showcontrol |= LCD_DISPLAYON + self.command(LCD_DISPLAYCONTROL | self._showcontrol) + + def begin(self, columns: int, rows: int): # pylint: disable=unused-argument + """ + Initial configuration, called from init. + + :param int columns: The number of columns on the display. + :param int rows: The number of rows on the display. + """ + if rows > 1: + self._showfunction |= LCD_2LINE + + self._numlines = rows + self._currline = 0 + + time.sleep(0.05) + + # Send function set command sequence + self.command(LCD_FUNCTIONSET | self._showfunction) + # delayMicroseconds(4500); # wait more than 4.1ms + time.sleep(0.005) + # second try + self.command(LCD_FUNCTIONSET | self._showfunction) + # delayMicroseconds(150); + time.sleep(0.005) + # third go + self.command(LCD_FUNCTIONSET | self._showfunction) + # finally, set # lines, font size, etc. + self.command(LCD_FUNCTIONSET | self._showfunction) + # turn the display on with no cursor or blinking default + self._showcontrol = LCD_DISPLAYON | LCD_CURSOROFF | LCD_BLINKOFF + self.display() + # clear it off + self.clear() + # Initialize to default text direction (for romance languages) + self._showmode = LCD_ENTRYLEFT | LCD_ENTRYSHIFTDECREMENT + # set the entry mode + self.command(LCD_ENTRYMODESET | self._showmode) + # backlight init + self.setReg(REG_MODE1, 0) + # set LEDs controllable by both PWM and GRPPWM registers + self.setReg(REG_OUTPUT, 0xFF) + # set MODE2 values + # 0010 0000 -> 0x20 (DMBLNK to 1, ie blinky mode) + self.setReg(REG_MODE2, 0x20) + self.setColorWhite() + + def setColorWhite(self): + """Set the color of the RGB backlight to white.""" + self.setRGB(255, 255, 255) diff --git a/macros/archinstall.mcr b/macros/archinstall.mcr new file mode 100644 index 0000000..be5bdc5 --- /dev/null +++ b/macros/archinstall.mcr @@ -0,0 +1,194 @@ +# First Boot + +"loadkeys uk" +WAIT 1 +enter +WAIT 1 +"archinstall" +WAIT 1 +enter +WAIT 10 + + +# Locales + +downarrow +enter +WAIT 1 +enter +forwardslash +"uk" +enter +WAIT 1 +downarrow +enter +forwardslash +"en_gb." +downarrow +enter +WAIT 1 +downarrow +downarrow +enter + +# Mirrors + +downarrow +enter +enter +forwardslash +"kin" +enter +WAIT 5 +downarrow +downarrow +downarrow +downarrow +enter +WAIT 1 + +# Partitions + +# EXT4 Main Drive + +downarrow +enter +enter +enter +space +enter +downarrow +enter + +# Encryption + +downarrow +downarrow +enter +enter +enter +downarrow +enter +LCD_SHOW "Password? (5s)" +WAIT 5 +enter +LCD_SHOW "Password? (5s)" +WAIT 5 +enter +downarrow +enter +space +enter +downarrow +downarrow +enter +downarrow +enter + +# Bootloader + +downarrow +downarrow +enter +WAIT 1 + +# Pick GRUB + +forwardslash +"grub" +enter + + +# Hostname +downarrow +enter +WAIT 1 + +# Clear out pre-filled hostname + +backspace +backspace +backspace +backspace +backspace +backspace +backspace +backspace +backspace +backspace + +# Entering new hostname + +LCD_SHOW "Hostname? (5s)" +WAIT 5 +enter + +# Users + +downarrow +downarrow +enter +enter + +# Prompt for username + password + +LCD_SHOW "Username? (5s)" +WAIT 5 +enter +LCD_SHOW "Password? (5s)" +WAIT 5 +enter +LCD_SHOW "Password? (5s)" +WAIT 5 +enter +enter +downarrow +downarrow +enter + +# Profile + +downarrow +enter +enter +forwardslash +"Min" +enter +WAIT 1 +downarrow +enter + +# Audio servers + +downarrow +enter +downarrow +downarrow +enter + +# Networking + +downarrow +downarrow +enter +enter + +# Time and Date + +downarrow +downarrow +enter +forwardslash +"london" +enter +WAIT 1 +downarrow +enter +enter + +# Install + +downarrow +downarrow +enter +enter \ No newline at end of file diff --git a/macros/syntax.mcr b/macros/syntax.mcr new file mode 100644 index 0000000..e69de29 diff --git a/macros/test.mcr b/macros/test.mcr new file mode 100644 index 0000000..b6d7cca --- /dev/null +++ b/macros/test.mcr @@ -0,0 +1,59 @@ +LCD_SHOW "Hi" + +# test comment + +WAIT 3 + +HOLD + +super + +"d" + +RELEASE + +"text editor" + +enter + +HOLD + +ctrl + +"n" + +RELEASE + +tab + +forwardslash + +space + +backslash + +HOLD + +shift + +"a" + +RELEASE + +enter + +capslock + +"caps" + +capslock + +enter + +"lowercase" + +backspace + +enter + +"sentence longer than 16 chars..." diff --git a/settings.toml b/settings.toml new file mode 100644 index 0000000..e69de29