Skip to content

Commit

Permalink
Hardware bring up
Browse files Browse the repository at this point in the history
  • Loading branch information
georgeharker committed Oct 24, 2024
1 parent 64e5536 commit 036fbf4
Show file tree
Hide file tree
Showing 2 changed files with 111 additions and 57 deletions.
108 changes: 75 additions & 33 deletions adafruit_neotrellis/multitrellis.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,70 +29,112 @@
__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
of the key measured from the top lefthand corner. Edge specifies what
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"""
for _n in range(self._rows):
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()
60 changes: 36 additions & 24 deletions adafruit_neotrellis/neotrellis.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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)
Expand All @@ -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))

0 comments on commit 036fbf4

Please sign in to comment.