From 0f1023dff8258834fba45a5a48ed94243024bf7d Mon Sep 17 00:00:00 2001 From: arofarn Date: Wed, 15 Apr 2020 01:28:41 +0200 Subject: [PATCH] Add neotrellisM4 lib sources Lot of cleanup --- code.py | 50 +++----- lib/conway.mpy | Bin 983 -> 1513 bytes src/conway.py | 36 +++++- src/neotrellism4.py | 298 ++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 348 insertions(+), 36 deletions(-) create mode 100644 src/neotrellism4.py diff --git a/code.py b/code.py index 72c741d..b7df65e 100644 --- a/code.py +++ b/code.py @@ -1,63 +1,47 @@ import time - import board import busio from adafruit_neotrellis.neotrellis import NeoTrellis from adafruit_neotrellis.multitrellis import MultiTrellis from neotrellism4 import NeoTrellisM4 -import conway import adafruit_adxl34x -ACCEL_THRESHOLD = 100 +import conway # Some functions are in an .mpy module for performance + +# Starting point: +universe = conway.RANDOM_UNIVERSE(30) -#create the i2c object for the trellis +# Create the i2c object for the trellis I2C = busio.I2C(board.SCL, board.SDA) -"""create the trellis. This is for a 2x2 array of TrellisM4 (first row) with -2 Neotrellis (second row). - - [ NeoM4_left | NeoM4_right ] - neotrellis0 | neotrellis1 -""" +# Create the trellis. This is for a 2x2 array of TrellisM4 (first row) with +# 2 Neotrellis (second row). +# +# [ NeoM4_left | NeoM4_right ] +# neotrellis0 | neotrellis1 trellim4_left = NeoTrellisM4() trellim4_right = NeoTrellisM4(left_part=trellim4_left) -trelli = [ - [trellim4_left, trellim4_right], - [NeoTrellis(I2C, False, addr=0x2F), NeoTrellis(I2C, False, addr=0x2E)] - ] +trelli = [[trellim4_left, trellim4_right], + [NeoTrellis(I2C, False, addr=0x2F), NeoTrellis(I2C, False, addr=0x2E)]] trellis = MultiTrellis(trelli) -#some color definitions -OFF = (0, 0, 0) -ON = (100, 100, 80) -# RED = (127, 0, 0) -# YELLOW = (127, 75, 0) -# GREEN = (0, 127, 0) -# CYAN = (0, 127, 127) -# BLUE = (0, 0, 127) -# PURPLE = (90, 0, 127) - I2C_ACCEL = busio.I2C(board.ACCELEROMETER_SCL, board.ACCELEROMETER_SDA) accelerometer = adafruit_adxl34x.ADXL343(I2C_ACCEL) - -# Init universe -univers = [[False for x in range(8)] for y in range(8)] - -universe = conway.GLIDER +accelerometer.enable_motion_detection(threshold=conway.ACCEL_THRESHOLD) #this will be called when button events are received def genesis_callback(xcoord, ycoord, edge): #turn the LED on when a rising edge is detected if edge == NeoTrellis.EDGE_RISING: universe[xcoord][ycoord] = not(universe[xcoord][ycoord]) - if univers[xcoord][ycoord]: - trellis.color(xcoord, ycoord, ON) + if universe[xcoord][ycoord]: + trellis.color(xcoord, ycoord, conway.ON) else: - trellis.color(xcoord, ycoord, OFF) + trellis.color(xcoord, ycoord, conway.OFF) conway.draw_universe(trellis, universe) @@ -75,7 +59,7 @@ for x in range(8): trellis.sync() conway.draw_universe(trellis, universe) -accelerometer.enable_motion_detection(threshold=ACCEL_THRESHOLD) + # Genesis while not(accelerometer.events["motion"]): diff --git a/lib/conway.mpy b/lib/conway.mpy index efd104d5ccb208cefeddb8f9bb29d030d4783393..e69f0ac9d889a7ab65af6269e6f47a6b58e75a7b 100644 GIT binary patch delta 621 zcmcc4{*pVA&|Xe82*q9V)K(9jUTtRlxK(hvYbMnH}{<3ulwiE$zm<4n%a zD^IM{E2!iwO3X_E;`CHD5Gy|yA%I}yH4D1R?i~G!oa}Dz`&`@ zD8<;M4h*&b8J?P7x%+s!xCSx% zySXv>`%Qk&Xy(Wq&dtEU#K6ES$0)_v$gAe2;SaQ^!PKn5;8269dV|5?jhhrVE1L5+ zY>-oC+_YInmNAXl$dm`hm6`0p9M7f#(kwoCE%RnZiOGp9Rcu-y0sYMnSvVL0cU3n0 diff --git a/src/conway.py b/src/conway.py index f00aa1f..a0fcfbb 100644 --- a/src/conway.py +++ b/src/conway.py @@ -1,3 +1,22 @@ +from random import randrange +# Acceleration threshold to end the genesis and start +# evolving +ACCEL_THRESHOLD = 100 + +# Colors: +OFF = (0, 0, 0) +ON = (80, 80, 50) + +# Some presets : +VOID = [[False, False, False, False, False, False, False, False], + [False, False, False, False, False, False, False, False], + [False, False, False, False, False, False, False, False], + [False, False, False, False, False, False, False, False], + [False, False, False, False, False, False, False, False], + [False, False, False, False, False, False, False, False], + [False, False, False, False, False, False, False, False], + [False, False, False, False, False, False, False, False]] + GLIDER = [[False, False, False, False, False, False, False, False], [False, False, False, False, True, False, False, False], [False, False, True, False, True, False, False, False], @@ -7,8 +26,19 @@ GLIDER = [[False, False, False, False, False, False, False, False], [False, False, False, False, False, False, False, False], [False, False, False, False, False, False, False, False]] -OFF = (0, 0, 0) -ON = (100, 100, 80) +BEATING_HEART = [[False, False, False, False, False, False, False, False], + [False, False, False, False, False, False, False, False], + [False, False, True, True, True, False, False, False], + [False, False, False, False, False, False, False, False], + [False, False, False, False, False, False, False, False], + [False, False, True, True, True, False, False, False], + [False, False, False, False, False, False, False, False], + [False, False, False, False, False, False, False, False]] + + +def RANDOM_UNIVERSE(rate): + return [[randrange(0,100) < rate for i in range(8) ] for j in range(8)] + def draw_universe(trellis, universe): for x in range(8): @@ -56,6 +86,6 @@ def living_neighbour_count(xcoord, ycoord, ext_universe): living_neighbours.append(ext_universe[xcoord+2][ycoord]) living_neighbours.append(ext_universe[xcoord+2][ycoord+1]) living_neighbours.append(ext_universe[xcoord+2][ycoord+2]) - # print(living_neighbours.count(True), living_neighbours) + return living_neighbours.count(True) diff --git a/src/neotrellism4.py b/src/neotrellism4.py new file mode 100644 index 0000000..a44ffff --- /dev/null +++ b/src/neotrellism4.py @@ -0,0 +1,298 @@ +# The MIT License (MIT) +# +# Copyright (c) 2019 arofarn +# +# 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. +""" +`neotrellism4` +================================================================================ + +Use Adafruit TrellisM4 Express board as 2 Neotrellis seesaw boards with CircuitPython. +You can you use this to extend TrellisM4 with Neotrellis (seesaw) boards. + + +* Author(s): arofarn + +Implementation Notes +-------------------- + +**Hardware:** + +* Adafruit NeoTrellis M4 Express: https://www.adafruit.com/product/3938 + +**Software and Dependencies:** + +* Adafruit CircuitPython firmware for the supported boards: + https://github.com/adafruit/circuitpython/releases + +""" + +# imports + +__version__ = "0.0.0-auto.0" +__repo__ = "https://github.com/arofarn/NeoTrellisM4.git" + + +import board +import digitalio +from micropython import const +from adafruit_seesaw.keypad import KeyEvent +from adafruit_matrixkeypad import Matrix_Keypad +from neopixel import NeoPixel + + +_NEO_TRELLIS_NUM_ROWS = const(4) +_NEO_TRELLIS_NUM_COLS = const(4) +_NEO_TRELLIS_NUM_KEYS = const(16) + +# _NEO_TRELLIS_MAX_CALLBACKS = const(32) + +_TRELLISM4_LEFT_PART = const(0) +_TRELLISM4_RIGHT_PART = const(4) + + +def _key(xval): + return int(int(xval/4)*8 + (xval%4)) + +def _seesaw_key(xval): + return int(int(xval/8)*4 + (xval%8)) + +def _to_seesaw_key(xval): + return int(xval + (xval // 4) * 4) + + +class _TrellisNeoPixel: + """Neopixel driver + """ + # Lots of stuff come from Adafruit_CircuitPython_seesaw/neopixel.py + + def __init__(self, auto_write=True, brightness=1.0, + part=_TRELLISM4_LEFT_PART, left_part=None): + if part == _TRELLISM4_LEFT_PART: + self.pix = NeoPixel(board.NEOPIXEL, 32, auto_write=False, brightness=brightness) + elif part == _TRELLISM4_RIGHT_PART: + self.pix = left_part.pix + self.auto_write = auto_write + self._offset = part + + def __setitem__(self, key, color): + self.pix[_key(key) + self._offset] = color + if self.auto_write: + self.show() + + def __getitem__(self, key): + return self.pix[_key(key) + self._offset] + + def fill(self, color): + """Fill method wrapper + """ + # Suppress auto_write while filling. + current_auto_write = self.auto_write + self.auto_write = False + for i in range(16): + self[i] = color + if current_auto_write: + self.show() + self.auto_write = current_auto_write + + def show(self): + """Fill method wrapper + """ + self.pix.show() + + +class _TrellisKeypad: + """Simple Keypad object for Trellis M4 + No pixel, no rotation + Key numbers : 0 - 15""" + + def __init__(self, part=_TRELLISM4_LEFT_PART, row_pins=None): + self._offset = part + col_pins = [] + for x in range(self._offset, self._offset + _NEO_TRELLIS_NUM_COLS): + col_pin = digitalio.DigitalInOut(getattr(board, "COL{}".format(x))) + col_pins.append(col_pin) + + if part == _TRELLISM4_LEFT_PART: + self.row_pins = [] + for y in range(_NEO_TRELLIS_NUM_ROWS): + row_pin = digitalio.DigitalInOut(getattr(board, "ROW{}".format(y))) + self.row_pins.append(row_pin) + elif part == _TRELLISM4_RIGHT_PART: + if row_pins is None: + raise ValueError("Missing row_pins list for the right part") + else: + self.row_pins = row_pins + else: + raise ValueError("part arg should be 0 (left part) or 4 (right part)") + + key_names = [] + for y in range(4): + row = [] + for x in range(4): + row.append(4 * x + y) + key_names.append(row) # Keys of each halves is numbered from 0-15 + + self._matrix = Matrix_Keypad(col_pins, self.row_pins, key_names) + + @property + def pressed_keys(self): + """A list of tuples of currently pressed button coordinates. + + .. code-block:: python + + import time + import adafruit_trellism4 + + trellis = adafruit_trellism4.TrellisM4Express() + + current_press = set() + while True: + pressed = set(trellis.pressed_keys) + for press in pressed - current_press: + print("Pressed:", press) + for release in current_press - pressed: + print("Released:", release) + time.sleep(0.08) + current_press = pressed + """ + return self._matrix.pressed_keys + + +class NeoTrellisM4: + """Driver for the Adafruit NeoTrellis.""" + + EDGE_HIGH = const(0) + EDGE_LOW = const(1) + EDGE_FALLING = const(2) + EDGE_RISING = const(3) + + def __init__(self, left_part=None): + if left_part is None: + self._offset = _TRELLISM4_LEFT_PART + self.pixels = _TrellisNeoPixel() + self.keypad = _TrellisKeypad() + else: + self._offset = _TRELLISM4_RIGHT_PART + self.pixels = _TrellisNeoPixel(32, + part=_TRELLISM4_RIGHT_PART, + left_part=left_part.pixels) + self.keypad = _TrellisKeypad(part=_TRELLISM4_RIGHT_PART, + row_pins=left_part.keypad.row_pins) + + self._events = [0] * _NEO_TRELLIS_NUM_KEYS + self._current_press = set() + self._key_edges = [self.EDGE_HIGH] * _NEO_TRELLIS_NUM_KEYS # Keys edges + self._current_events = bytearray() + self.callbacks = [None] * 16 + + @property + def interrupt_enabled(self): + """Only for compatibility with neotrellis module + interrupts are disable on trellis M4 keypad""" + return False + + # pylint: disable=unused-argument, no-self-use + @interrupt_enabled.setter + def interrupt_enabled(self, value): + """Only for compatibility with neotrellis module + interrupts are disable on trellis M4 keypad + """ + print("Warning: no interrupt with Trellis M4 keypad (method does nothing)") + # pylint: enable=unused-argument, no-self-use + + @property + def count(self): + """Return the pressed keys count + """ + self._read_keypad() + return len(self._current_events) + + # pylint: disable=unused-argument, no-self-use + @count.setter + def count(self, value): + """Only for compatibility with neotrellis module + """ + raise AttributeError("count is read only") + # pylint: enable=unused-argument, no-self-use + + def set_event(self, key, edge, enable): + """Set event on a key + """ + if enable not in (True, False): + raise ValueError("event enable must be True or False") + if edge > 3 or edge < 0: + raise ValueError("invalid edge") + + # Pas besoin de l'écriture sur I2C mais de l'enregistrer dans self._events + if enable: + self._events[key] = self._events[key] | (1 << edge) + else: + self._events[key] = self._events[key] & (0xF ^ (1 << edge)) + + def read_keypad(self, num): + """Give the n events in the keypad buffer + """ + while num > len(self._current_events): + self._current_events.append(0xFF) + return self._current_events[:num] + + def _read_keypad(self): + """Read keypad and update _key_edges and _current_events""" + pressed = set(self.keypad.pressed_keys) + #default : not pressed => EDGE_HIGH + self._key_edges = [self.EDGE_HIGH] * _NEO_TRELLIS_NUM_KEYS + for k in pressed: + self._key_edges[k] = self.EDGE_LOW + for k in pressed - self._current_press: + self._key_edges[k] = self.EDGE_RISING + for k in self._current_press - pressed: + self._key_edges[k] = self.EDGE_FALLING + + self._current_press = pressed + self._current_events = bytearray() + + for k in range(_NEO_TRELLIS_NUM_KEYS): + if (self._events[k] >> self._key_edges[k]) & 0x1: + raw_evt = (_to_seesaw_key(k) << 2) | self._key_edges[k] + self._current_events.append(raw_evt) + + + def activate_key(self, key, edge, enable=True): + """Activate or deactivate a key on the trellis. Key is the key number from + 0 to 15. 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. + """ + self.set_event(key, edge, enable) + + + def sync(self): + """Read any events from the Trellis hardware and call associated + callbacks + """ + available = self.count + if available > 0: + available = available + 2 + buf = self.read_keypad(available) + for raw in buf: + 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)