From 6b9a5b16107761218123301c4ecd4e8db96f547b Mon Sep 17 00:00:00 2001 From: Bert Outtier Date: Sun, 15 Mar 2026 22:18:23 +0100 Subject: [PATCH] add a driver for the 2024 and 2026 communicator UARt is untested because of REPL being activated on the UART --- .../lib/drivers/fri3d/communicator.py | 122 ++++++++++++++++++ 1 file changed, 122 insertions(+) create mode 100644 internal_filesystem/lib/drivers/fri3d/communicator.py diff --git a/internal_filesystem/lib/drivers/fri3d/communicator.py b/internal_filesystem/lib/drivers/fri3d/communicator.py new file mode 100644 index 00000000..e384edca --- /dev/null +++ b/internal_filesystem/lib/drivers/fri3d/communicator.py @@ -0,0 +1,122 @@ +import struct + +from micropython import const +from machine import I2C, UART + +from .device import Device + +# registers +_COMM_REG_KEY_REPORT = const(0x03) +_COMM_REG_CONFIG = const(0x0B) +_COMM_REG_BACKLIGHT = const(0x0C) +_COMM2024_REG_RGB_LED = const(0x0E) +_COMM2024_REG_RED_LED = const(0x11) + +_COMM2024_I2CADDR_DEFAULT = const(0x38) +_COMM2026_I2CADDR_DEFAULT = const(0x39) + + +class Communicator2026(Device): + """Fri3d Badge 2026 expander MCU.""" + + def __init__( + self, + i2c_bus: I2C, + uart_bus: UART = None, + address: int = _COMM2026_I2CADDR_DEFAULT, + ): + """Read from a 2026 communicator""" + Device.__init__(self, i2c_bus, address) + self.use_uart = False + self.write_idx = 0 + self.data_ready = False + if uart_bus: + self.use_uart = True + self.uart = uart_bus.init(115200, bits=8, parity=None, stop=1) + self._rx_buf = bytearray(8) + self._rx_mv = memoryview(self._rx_buf) + self.uart.irq(handler=self.uart_handler, trigger=UART.IRQ_RX) + + def uart_handler(self, uart): + """Interrupt handler for incoming UART data""" + while uart.any() and not self.data_ready: + # Calculate how much space is left + space_left = 8 - self.write_idx + + # Read directly into the slice of the memoryview + # readinto returns the number of bytes actually read + num_read = uart.readinto(self._rx_mv[self.write_idx :], space_left) + + if num_read: + self.write_idx += num_read + + if self.write_idx >= 8: + self.data_ready = True + + @property + def key_report(self) -> tuple[int, int, int, int, int, int, int, int, int]: + """return the key report read using I2C or UART""" + ret = None + if self.use_uart and self.data_ready: + # Process the data (raw_buffer now contains the 8 bytes) + ret = tuple(self._rx_buf) + self.write_idx = 0 + self.data_ready = False + else: + ret = self._read("BBBBBBBB", _COMM_REG_KEY_REPORT, 8) + return ret + + @property + def configuration(self) -> int: + """get the configuration byte""" + return self._read("B", _COMM_REG_CONFIG, 1)[0] + + @configuration.setter + def configuration(self, value: int): + """Set the configuration byte""" + self._write(_COMM_REG_CONFIG, struct.pack("B", value)) + + @property + def backlight(self) -> int: + """Get the backlight value (0-100)""" + return self._read("= 0 and value <= 100: + self.i2c.writeto_mem( + self.address, _COMM_REG_BACKLIGHT, struct.pack(" tuple[int, int, int]: + """Get the LANA module RGB LED""" + return self._read("BBB", _COMM2024_REG_RGB_LED, 3) + + @rgb_led.setter + def rgb_led(self, value: tuple[int, int, int]): + """Set the LANA module RGB LED""" + self._write(_COMM2024_REG_RGB_LED, struct.pack("BBB", *value)) + + @property + def red_led(self) -> int: + """Get the CAPS LED""" + return self._read("B", _COMM2024_REG_RED_LED, 1)[0] + + @red_led.setter + def red_led(self, value: int): + """Set the CAPS LED""" + if value >= 0 and value <= 0xFF: + self._write(_COMM2024_REG_RED_LED, struct.pack("B", value))