diff --git a/adafruit_neotrellis/multitrellis.py b/adafruit_neotrellis/multitrellis.py index 8898adf..7e8161d 100644 --- a/adafruit_neotrellis/multitrellis.py +++ b/adafruit_neotrellis/multitrellis.py @@ -29,28 +29,86 @@ __version__ = "0.0.0-auto.0" __repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_neotrellis.git" +from typing import List, Dict, Tuple, Sequence from time import sleep from micropython import const +from adafruit_neotrellis.neotrellis import NeoTrellis from adafruit_seesaw.keypad import KeyEvent -_NEO_TRELLIS_NUM_KEYS = const(16) - - -def _key(xval): - return int(int(xval / 4) * 8 + (xval % 4)) - - -def _seesaw_key(xval): - return int(int(xval / 8) * 4 + (xval % 8)) - class MultiTrellis: """Driver for multiple connected Adafruit NeoTrellis boards.""" + _trelli: List[List[NeoTrellis]] + _rows: int + _cols: int + _key_pads: List[List[NeoTrellis]] + def __init__(self, neotrellis_array): self._trelli = neotrellis_array self._rows = len(neotrellis_array) self._cols = len(neotrellis_array[0]) + col_size_sum = [0 for _ in range(self._cols)] + row_size_sum = [0 for _ in range(self._rows)] + for py in range(self._rows): + for px in range(self._cols): + assert len(self._trelli[py]) == self._cols + + tr0 = self._trelli[0][px] + tc0 = self._trelli[py][0] + t = self._trelli[py][px] + + # All columns must have similar shape + assert t.height == tc0.height + # All rows must have similar shape + assert t.width == tr0.width + + y_base = row_size_sum[py - 1] if py > 0 else 0 + x_base = col_size_sum[px - 1] if px > 0 else 0 + row_size_sum[py] = t.height + y_base + col_size_sum[px] = t.width + x_base + + t.x_base = x_base + t.y_base = y_base + + self._width = col_size_sum[self._cols - 1] + self._height = row_size_sum[self._rows - 1] + self._key_pads : List[List[NeoTrellis]] = [[None for _ in range(self._width)] + for _ in range(self._height)] + + for py in range(self._rows): + for px in range(self._cols): + t = self._trelli[py][px] + for ky in range(t.height): + for kx in range(t.width): + x = t.x_base + kx + y = t.y_base + ky + self._key_pads[y][x] = t + + @property + def width(self): + return self._width + + @property + def height(self): + return self._height + + @property + def rows(self): + return self._rowa + + @property + def cols(self): + return self._cols + + def __len__(self) -> int: + return self._rows + + def __getitem__(self, subscript: int) -> Sequence[NeoTrellis]: + return self._trelli[subscript] + + def get_key_pad(self, x: int, y: int) -> NeoTrellis: + return self._key_pads[y][x] def activate_key(self, x, y, edge, enable=True): """Activate or deactivate a key on the trellis. x and y are the index @@ -58,23 +116,20 @@ def activate_key(self, x, y, edge, enable=True): edge to register an event on and can be NeoTrellis.EDGE_FALLING or NeoTrellis.EDGE_RISING. enable should be set to True if the event is to be enabled, or False if the event is to be disabled.""" - xkey = x % 4 - ykey = int(int(y % 4) * 4 / 4) - self._trelli[int(y / 4)][int(x / 4)].activate_key(ykey * 4 + xkey, edge, enable) + pad = self._key_pads[y][x] + pad.activate_key(pad.key_index(x, y), enable) def set_callback(self, x, y, function): """Set a callback function for when an event for the key at index x, y (measured from the top lefthand corner) is detected.""" - xkey = x % 4 - ykey = int(int(y % 4) * 4 / 4) - self._trelli[int(y / 4)][int(x / 4)].callbacks[ykey * 4 + xkey] = function + pad = self._key_pads[y][x] + pad.callbacks[pad.key_index(x, y)] = function def color(self, x, y, color): """Set the color of the pixel at index x, y measured from the top lefthand corner of the matrix""" - xkey = x % 4 - ykey = int(int(y % 4) * 4 / 4) - self._trelli[int(y / 4)][int(x / 4)].pixels[ykey * 4 + xkey] = color + pad = self._key_pads[y][x] + pad.pixels[pad.key_index(x, y)] = color def sync(self): """Read all trellis boards in the matrix and call any callbacks""" @@ -82,17 +137,4 @@ def sync(self): for _m in range(self._cols): _t = self._trelli[_n][_m] - available = _t.count - sleep(0.0005) - if available > 0: - available = available + 2 - buf = _t.read_keypad(available) - for raw in buf: - evt = KeyEvent(_seesaw_key((raw >> 2) & 0x3F), raw & 0x3) - if ( - evt.number < _NEO_TRELLIS_NUM_KEYS - and _t.callbacks[evt.number] is not None - ): - y = int(evt.number / 4) + _n * 4 - x = int(evt.number % 4) + _m * 4 - _t.callbacks[evt.number](x, y, evt.edge) + _t.sync() diff --git a/adafruit_neotrellis/neotrellis.py b/adafruit_neotrellis/neotrellis.py index f119382..35a9679 100755 --- a/adafruit_neotrellis/neotrellis.py +++ b/adafruit_neotrellis/neotrellis.py @@ -47,8 +47,9 @@ __repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_neotrellis.git" from time import sleep +from typing import List, Callable from micropython import const -from adafruit_seesaw.keypad import Keypad, KeyEvent +from adafruit_seesaw.keypad import Keypad, KeyEvent, SeesawKeyResponse, ResponseType from adafruit_seesaw.neopixel import NeoPixel _NEO_TRELLIS_ADDR = const(0x2E) @@ -59,21 +60,27 @@ _NEO_TRELLIS_NUM_COLS = const(8) _NEO_TRELLIS_NUM_KEYS = const(64) -_NEO_TRELLIS_MAX_CALLBACKS = const(64) - - -def _key(xval): - return int(int(xval / 8) * 8 + (xval % 8)) - - -def _seesaw_key(xval): - return int(int(xval / 8) * 8 + (xval % 8)) - class NeoTrellis(Keypad): """Driver for the Adafruit NeoTrellis.""" - def __init__(self, i2c_bus, interrupt=False, addr=_NEO_TRELLIS_ADDR, drdy=None): + width: int + height: int + x_base: int + y_base: int + interrupt_enabled: bool + callbacks: List[Callable[[KeyEvent], None]] + pixels: List[NeoPixel] + + def __init__(self, i2c_bus, interrupt=False, + addr=_NEO_TRELLIS_ADDR, drdy=None, + width=_NEO_TRELLIS_NUM_COLS, + height=_NEO_TRELLIS_NUM_ROWS, + x_base = 0, y_base = 0): super().__init__(i2c_bus, addr, drdy) + self.width = width + self.height = height + self.x_base = x_base + self.y_base = y_base self.interrupt_enabled = interrupt self.callbacks = [None] * _NEO_TRELLIS_NUM_KEYS self.pixels = NeoPixel(self, _NEO_TRELLIS_NEOPIX_PIN, _NEO_TRELLIS_NUM_KEYS) @@ -84,23 +91,28 @@ def activate_key(self, key, edge, enable=True): NeoTrellis.EDGE_FALLING or NeoTrellis.EDGE_RISING. enable should be set to True if the event is to be enabled, or False if the event is to be disabled.""" - self.set_event(_key(key), edge, enable) + self.set_event(key, edge, enable) + + def clear(self): + self.pixels.fill((0, 0, 0)) def sync(self): """read any events from the Trellis hardware and call associated callbacks""" available = self.count - sleep(0.0005) + sleep(0.0005) # FIXME: resolve if available > 0: - # FIXME: used to read - # available = available + 2 - # why?? - buf = self.read_keypad(available) - for response in buf: - raw = response.data - evt = KeyEvent(_seesaw_key((raw >> 2) & 0x3F), raw & 0x3) - if (evt.number < _NEO_TRELLIS_NUM_KEYS and - self.callbacks[evt.number] is not None): - self.callbacks[evt.number](evt) + for r in buf: + if r.response_type == ResponseType.TYPE_KEY: + (e, n) = r.data_edge_num() + evt = KeyEvent(n, e) + if ( + evt.number < _NEO_TRELLIS_NUM_KEYS + and self.callbacks[evt.number] is not None + ): + self.callbacks[evt.number](evt) + + def key_index(self, x, y): + return int((y - self.y_base) * self.width + (x - self.x_base))