The Adafruit NeoTrellis RGB Driver PCB for 4x4 Keypad is a board  with i2c connections and an  array of 4x4 button pads, each  with a NeoPixel (RGB). Adafruit also sells number of accessories and kits around the board. The   Silicone Elastomer 4x4 Button Keypadacrylic case, and Feather M4 can also be purchased as a bundled kit.  You can drive the board with any i2c device, such as arduino, raspberryPi, etc. There is a kit that pairs 4 of the keypad boards with a single Feather M4 for a large 8x8 keypad.  I chose to start with a single board, the Adafruit Feather M4 and CircuitPython, mostly to explore the chip and the Circuit Python system.

Adafruit has a nice Learn project for building the kit and basic programming (arduino or Circuit Python).

Note there is a similar but very different PCB kit the Adafruit NeoTrellis M4.  This board has a 4x8 button/neopixel array, but these are directly connected to the M4, not via i2c, which as we will see makes the NeoTrellis 4x4 a very different beast.

Basic Kit Build  and Initial Coding

It was pretty easy getting the board wired  up to the Feather.  The 4x4 board has a JST-PH 4pin socket, so we solder a matching plug with wires to the Feather. The 4x4 also provides solder holes for the i2c, so I could have gone with simple soldered connections on both ends. However, a DesignPattern I prefer is to use connectors between boards and peripherals. This way parts can be separated and re-arranged easily 

It was annoying to note that the connector is NOT the same as the 4 wire connection used by other i2c standards like Qwiic and Stemma QT.  Those use smaller JST SH connectors.  I had purchased a number  of those for previous projects,  So I had to buy a set of -PH connectors.  Standards Aren't.

The Case

Building the case was also pretty easy, following the Learn project.  I did have to do it at least twice, as one of the solder connections broke.  I found it wasnt too hard to pick up the outer acrylic in one piece if you squeeze a bit.  Once assembled it looks good.

Python Code

Programming the basic example from the Learn also went pretty well.  On a previous project, I found the MU Editor is very nice for these boards, but the  'autoreload' feature gets triggered every second or so by something on my PC. (someday i need to track that down).  There is a simple fix for this.  Insert the following  code snippet at the beginning of the code.py program:

import supervisor

supervisor.disable_autoreload()

modify the Adafruit Learn example

I pulled the code bundle from the Learn pages and THEN went back and read the bits before it.  The example code uses the common code for initializing the i2c connection:

import board
# create the i2c object for the trellis
i2c_bus = board.I2C()

However, this does NOT work on a Feather M4, because the I2C() function is not implemented in the board module for Feather M4 boards.  So you MUST use the alternative i2c setup:

from board import SCL, SDA
import busio

#create the i2c object for the trellis
i2c_bus = busio.I2C(SCL, SDA)

I had a similar issue with the raspberryPi Pico.  Not sure why but guess the rather impressive team at Adafruit/CircuitPython are a  bit busy. This may have changed by the time you read this, so check.

Note that while SCL and SDA are defined in board, these are only the primary i2c pins.  Many boards support alternative pins and you can easily use those.

Once those two hiccups were fixed, the example worked just fine.  It blinks the corresponding neoPixel when a button is pressed. Not super impressive but a decent starting example.

Code for project at this state is in github repository at examples/code_neotrellis_example_m4.py

Communications

simple blinking  its Boring. and doesnt communicate.

Communication is pretty easy as a MIDI device. Did that on last project, although with an rPi Pico, but still with CircuitPython its pretty high level setup.  There are examples for doing HID interfaces (look like a USB keyboard, joystick, mouse, etc).. Neat, but save that for later, because ...

Blinking LED Animations - THATS why we use NeoPixels

Better Blinking. Thats what we use NeoPixels for! So time to dig into neopixel coding with CircuitPython!

CircuitPython has a neat looking library called LED_Animation.  One really really good point in its favor is that is Non-Blocking.  Running an animation does not block  other code from running.  The arduino Neopixel library has many similar effects, but it uses a delay() function that stops all other activity until the specified time elapses.  This does NOT work for interactive projects.  I previously built an alternative non-blocking library. NeoEffects that works on top of either NeoPixel or the Octo libraries.  I used that library on many projects (sometime i might even document them).  So LED_Animation looks good. It even has Pixel mapping functions that let you define rows, columns and other groups of pixels.  Those would be very nice for making this matrix blink

So I pulled up the LED_Animation/examples/led_animation_all_animations.py, added the above modifications for the M4/MU and  it sorta worked. A couple of the animations crashed, giving odd errors.  Thanks to the Adafruit Discord, I was able to get some quick answers.  It seems the NeoTrellis uses the Adafruit Seesaw (ATSAMD09) processor and the CircuitPython Seesaw support library has its own special version of Neopixel.  This library apparently was created before the CircuitPython_NeoPixel library expected by LED_Animation. The CircuitPython NeoPixel derives from adafruit_pixelbuf. This handles the color order, low level set/get, and fancy array slicing that makes LED_Animation possible.  Unfortunately, this means the LED_Animation library cant be used.  Ugh.

(NOTE: the official seesaw library was updated in late July with changes per this project. no longer any need for the special branch)

I thought of a variety of options, but all would take a lot more programming time than I really wanted to put to this project.  Encouraged by the Discord folks, I put in a github Issue on the seesaw library and put project down for a day or two.  One of the Discord helpers, NeraDoc, came forward with a revised seesaw.neopixel that did derive from PixelBuf, and before I cycled back to the project, had already created a branch of seesaw library that included that fix.  Wow.  Fast help.

I  modified my setup to use the new branch - download a zip of the branch, extract  it and put the folder into the lb folder of my M4 processor. And the all_animations example worked!  Some of the effects needed minor tweaking to deal with the small number of LEDs (16), but they all worked! Yea for NeraDoc!

At this point the code works. it is in the github repository as examples/code_myAllAnimations.py

And for reference a (not forked) copy of NeraDoc's seesaw library is in the github lib folder.

I did note the led_animation.animation library has inconsistent support for the on_cycle_complete() callback that is supposed to happen when a sequence ends.  The AnimationSequence class lets you create a list of animations that it will cycle through.  The callback function should be called whenever an animation is complete and AnimationSequence is going to move to next animation.  I was going  to use it to print the animation name so i could keep track of them.  Otherwise it might be used to cleanup the animation memory(?) or let the client program do something special (move servo?) when the sequence changes.  No biggie.  Might go explore that later.

Incorporate key buttons to trigger animations and communications.

A bit of reworking brought in rough keypad handling, setting up 16 animations played back when the corresponding keys are pressed. see examples/code_animKey.py

I set up each key press to stop any animation, blank the leds, and set the single pressed key's led to the key's color, as defined by a 16 entry palette. The key release starts the animation defined for that key.  I will go over that code in more detail in a log.  The examples/createPalette.py and some other tools were used to create some different palettes.

A small bit more work implemented Row and Column PixelMaps and some demonstration animations. see examples/code_animKey_PixelMaps.py

The first few project Logs will examine how this all works, in the refactored, modularized code in top level of the github repo

Other potential explorations:

oh and find a use for this toy ;-)

ToC of Project Logs 

Log 1: Refactored code_NeotrellisExplorations and code.py

Log 2: Walkthru onboard_neopixel.py module

Log 3: Walkthru neotrellis_animations.py module

Log 4: Walkthru neotrellis_keypad module

Log 5: Adding Midi NoteOn/NoteOff

Log6: Official Seesaw Library Updated