Add neotrellisM4 lib sources
Lot of cleanup
This commit is contained in:
parent
5a008b3052
commit
0f1023dff8
50
code.py
50
code.py
@ -1,63 +1,47 @@
|
|||||||
import time
|
import time
|
||||||
|
|
||||||
import board
|
import board
|
||||||
import busio
|
import busio
|
||||||
from adafruit_neotrellis.neotrellis import NeoTrellis
|
from adafruit_neotrellis.neotrellis import NeoTrellis
|
||||||
from adafruit_neotrellis.multitrellis import MultiTrellis
|
from adafruit_neotrellis.multitrellis import MultiTrellis
|
||||||
from neotrellism4 import NeoTrellisM4
|
from neotrellism4 import NeoTrellisM4
|
||||||
import conway
|
|
||||||
import adafruit_adxl34x
|
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)
|
I2C = busio.I2C(board.SCL, board.SDA)
|
||||||
|
|
||||||
"""create the trellis. This is for a 2x2 array of TrellisM4 (first row) with
|
# Create the trellis. This is for a 2x2 array of TrellisM4 (first row) with
|
||||||
2 Neotrellis (second row).
|
# 2 Neotrellis (second row).
|
||||||
|
#
|
||||||
[ NeoM4_left | NeoM4_right ]
|
# [ NeoM4_left | NeoM4_right ]
|
||||||
neotrellis0 | neotrellis1
|
# neotrellis0 | neotrellis1
|
||||||
"""
|
|
||||||
|
|
||||||
trellim4_left = NeoTrellisM4()
|
trellim4_left = NeoTrellisM4()
|
||||||
trellim4_right = NeoTrellisM4(left_part=trellim4_left)
|
trellim4_right = NeoTrellisM4(left_part=trellim4_left)
|
||||||
trelli = [
|
trelli = [[trellim4_left, trellim4_right],
|
||||||
[trellim4_left, trellim4_right],
|
[NeoTrellis(I2C, False, addr=0x2F), NeoTrellis(I2C, False, addr=0x2E)]]
|
||||||
[NeoTrellis(I2C, False, addr=0x2F), NeoTrellis(I2C, False, addr=0x2E)]
|
|
||||||
]
|
|
||||||
|
|
||||||
trellis = MultiTrellis(trelli)
|
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)
|
I2C_ACCEL = busio.I2C(board.ACCELEROMETER_SCL, board.ACCELEROMETER_SDA)
|
||||||
accelerometer = adafruit_adxl34x.ADXL343(I2C_ACCEL)
|
accelerometer = adafruit_adxl34x.ADXL343(I2C_ACCEL)
|
||||||
|
accelerometer.enable_motion_detection(threshold=conway.ACCEL_THRESHOLD)
|
||||||
# Init universe
|
|
||||||
univers = [[False for x in range(8)] for y in range(8)]
|
|
||||||
|
|
||||||
universe = conway.GLIDER
|
|
||||||
|
|
||||||
#this will be called when button events are received
|
#this will be called when button events are received
|
||||||
def genesis_callback(xcoord, ycoord, edge):
|
def genesis_callback(xcoord, ycoord, edge):
|
||||||
#turn the LED on when a rising edge is detected
|
#turn the LED on when a rising edge is detected
|
||||||
if edge == NeoTrellis.EDGE_RISING:
|
if edge == NeoTrellis.EDGE_RISING:
|
||||||
universe[xcoord][ycoord] = not(universe[xcoord][ycoord])
|
universe[xcoord][ycoord] = not(universe[xcoord][ycoord])
|
||||||
if univers[xcoord][ycoord]:
|
if universe[xcoord][ycoord]:
|
||||||
trellis.color(xcoord, ycoord, ON)
|
trellis.color(xcoord, ycoord, conway.ON)
|
||||||
else:
|
else:
|
||||||
trellis.color(xcoord, ycoord, OFF)
|
trellis.color(xcoord, ycoord, conway.OFF)
|
||||||
conway.draw_universe(trellis, universe)
|
conway.draw_universe(trellis, universe)
|
||||||
|
|
||||||
|
|
||||||
@ -75,7 +59,7 @@ for x in range(8):
|
|||||||
trellis.sync()
|
trellis.sync()
|
||||||
conway.draw_universe(trellis, universe)
|
conway.draw_universe(trellis, universe)
|
||||||
|
|
||||||
accelerometer.enable_motion_detection(threshold=ACCEL_THRESHOLD)
|
|
||||||
|
|
||||||
# Genesis
|
# Genesis
|
||||||
while not(accelerometer.events["motion"]):
|
while not(accelerometer.events["motion"]):
|
||||||
|
BIN
lib/conway.mpy
BIN
lib/conway.mpy
Binary file not shown.
@ -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],
|
GLIDER = [[False, False, False, False, False, False, False, False],
|
||||||
[False, False, False, False, True, False, False, False],
|
[False, False, False, False, True, False, False, False],
|
||||||
[False, False, True, 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],
|
||||||
[False, False, False, False, False, False, False, False]]
|
[False, False, False, False, False, False, False, False]]
|
||||||
|
|
||||||
OFF = (0, 0, 0)
|
BEATING_HEART = [[False, False, False, False, False, False, False, False],
|
||||||
ON = (100, 100, 80)
|
[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):
|
def draw_universe(trellis, universe):
|
||||||
for x in range(8):
|
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])
|
||||||
living_neighbours.append(ext_universe[xcoord+2][ycoord+1])
|
living_neighbours.append(ext_universe[xcoord+2][ycoord+1])
|
||||||
living_neighbours.append(ext_universe[xcoord+2][ycoord+2])
|
living_neighbours.append(ext_universe[xcoord+2][ycoord+2])
|
||||||
# print(living_neighbours.count(True), living_neighbours)
|
|
||||||
return living_neighbours.count(True)
|
return living_neighbours.count(True)
|
||||||
|
|
||||||
|
298
src/neotrellism4.py
Normal file
298
src/neotrellism4.py
Normal file
@ -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)
|
Loading…
Reference in New Issue
Block a user