Pico RGB Keypad Base and Neopixel Coding

I’ve had a good bash at accessing the keypad keys and LEDs with no success. Would it be possible for Sandy to port his Keybow 2040 library across to the RGB Keypad Base?

I know that the Keypad is the ‘low rent’ version, but it is very useful for accessing unused pins and functions for projects.

In the mean time, two things might make things a little better. Could some Pico SDK code examples be published showing how to access the keys and LEDs using C/Cpp?

In the alternative, you have already stated that the keys are accessed on the default I2C0 pins and on address 20 (although I have failed to detect anything on the I2C bus). Could you say how the LEDs are accessed (SPI?) and a little more information than is given in the APA 102 Data Sheet on the format of data messages?

According to the pinout on the shop page it uses SPI.
MOSI, SCLK and CS are SPI.
The C examples are here,
pimoroni-pico/examples at main · pimoroni/pimoroni-pico · GitHub
and the Micro Python here, if that helps.
pimoroni-pico/micropython/examples at main · pimoroni/pimoroni-pico · GitHub

I think what you need to sort out is how the TCA9555 IO expander (I2C address: 0x20) works?

The reason to go CircuitPython is to get HID and MIDI modules.

The Pimoroni MicroPython UF2 example for the RGB Keypad Base works very well and it is easy to detect keystrokes and light-up LEDs from the command line. However, the lack of HID and MIDI modules reduces the potential of the Base.

Also, Pimoroni have not documented the modules included in their Custom MicroPython UF2, which is why I want to create my own CircuitPython alternative so that when the next release of CircuitPython appears, I won’t have to wait for Pimoroni to update their custom version. (It would be helpful if MicroPython allowed the import of new library modules in the same way that CircuitPython does. Then Pimoroni could have given us a module file.

An additional problem is that the MicroPython example scan.py, which should scan I2C ports fails to run.

Traceback (most recent call last):
  File "<stdin>", line 5, in <module>
OSError: [Errno 5] EIO

That is the first line of the program that tries to write to the I2C port. I have not found that method documented, but I assume the 76 is the address.

i2c.writeto(76, b'123')

Another problem is that the pico-example bus-scan.c checks all the addresses and reports no I2C devices.

Adafruit have working CircuitPython modules that run Dotstar LEDs (e.g. APA 102) on both the SPI port and by bitbanging other pins. I think the RGB Keypad Base connects the ADA102 LEDs to the SPI and CS pins, but it would be nice to know that from Pimoroni.

I’m sure that their MicroPython I2C modules work well because of their RGB Keypad Base example and because of the success that Sandy had with CircuitPython and the KeyBow RP2040. Finally, Sandy has also shown that there are working HID and MIDI modules on Circuit Python.

The TCA9555 IO expander returns a 16 bit data word in which each bit represents the state of one key. Thats why the returned values are 1,2,4,8,16… 32768. However, unless I can get this and other devices working on the I2C port, there will be no chance to get a small OLED screen connected.

Pimoroni have resolved the I2C problems with MicroPython and the support of Dotstar LEDs. It would be helpful if they would show us how they did it so that we can get the most value from their RGB Keypad Base.

Going by the pinout.

Pin 25 is SPI0 TX, and labeled MOSI in the Pinout
Pin 24 is SPI0 SCK and labeled SCLK in the Pinout
Pin 22 is SPI0 CSn and labeled CS in the Pinout.
If I was a betting man I’d say the LED’s are on SPI0. IMHO it makes the most sense.

Pin 6 is i2c1 SDA and labeled SDA in the Pinout
Pin 7 is i2c1 SCL and labeled SCL in the Pinout
I’d say i2c1 is what’s used for the keys. And figure out what to do with the Interrupt pin?

I’ve just released a CircuitPython library for the TCA9555 expander so that I could use it with CircuitPython.

One of the examples is specifially for the Pico RGB Base and shows how to use the CircuitPython DotStar library to control the leds.

It is still beta and feedback would be appreciated.

Yes, I agree. I am doing loads of research ATM and I can see why the I2C scan programs don’t work. I2C, SPI and UART need setting to the correct PICO pinset. In CircuitPython, that is achieved with module busio. I have not researched MicroPython, yet.

There is an additional question for SPI0 of how (why?) CSn (Cable Select Not) is used on the RGB Keypad Base. I assume it needs to be set to ‘0’ to enable the LEDs.

That is terrific news. I will give it a try ASAP! Thanks for all your efforts.

Hi lesamouraipourpre,

I’m learning a vast amount about CircuitPython, the TCA9555 and MicroPython from the files in your link.

In MicroPython, I have specifically set up the I2C port pins and scanned for the addresses. I have received 32 from the TCA9555 which tells me that Pimoroni gave the Hex value and not the decimal. That will resolve a lot of heartache! Your library files and examples have given me a much broader understanding of how the TCA9555 works.

I was confused to not find a .mpy file in the download. Having found out a great deal about the CircuitPython libraries and their creation, I see that the library can be a .py file. I loaded up the pico with the following libs,

adafruit_bus_device
adafruit_dotstar.mpy
adafruit_register
community_tca9555.py
2 x underscore init 2 x underscore.py

the dotstar and init files being just in case!

with the example file tca9555_simpletest.py I got the result;

Traceback (most recent call last):
  File "<stdin>", line 13, in <module>
ImportError: can't import name TCA9555

Line 13: from community_tca9555 import TCA9555

I’m hosting this on a Kubuntu machine rather than a Raspberry Pi (my other computer) because I am addicted to eight threads and a lightening-fast NVRAM. For this reason I decided not to push all the compile buttons until a .mpy file or folder popped out in case I, inadvertantly, added it to the Adafruit or Community bundle.

Any advice on the next step without destroying the Internet would be much appreciated.

The PyPI upload just contains the .py code and examples. Using the .py will work fine. The .mpy is only preferable if the RAM and/or Flash is limited.
Copying community_tca9555.py to <CIRCUITPY>/lib/ should work fine.

There are .mpy downloads available from github:

You probably want the 6.x zip. If you have switched to the alpha release of CircuitPython use the 7.x zip.

Running ls -R on my mounted PICO gives:

/media/pi/CIRCUITPY/:
boot_out.txt  code.py  lib

/media/pi/CIRCUITPY/lib:
adafruit_bus_device     adafruit_dotstar.mpy     adafruit_register
adafruit_debouncer.mpy  adafruit_pypixelbuf.mpy  community_tca9555.py

/media/pi/CIRCUITPY/lib/adafruit_bus_device:
i2c_device.mpy  __init__.py  spi_device.mpy

/media/pi/CIRCUITPY/lib/adafruit_register:
i2c_bcd_alarm.mpy  i2c_bcd_datetime.mpy  i2c_bit.mpy  i2c_bits.mpy  i2c_struct_array.mpy  i2c_struct.mpy  __init__.py

The only __init__.py files are related to adafruit_bus_device and adafruit_register. There isn’t one needed for community_tca9555.py as it is a module, not a package.

Hopefully that should get you going.

I found that code.py was missing. I downloaded V6 again and reloaded the Pico with CircuitPython. However, code.py is still missing.

I noticed that files were beginning to disappear from CircuitPython, even with a reload.

I also notice that an old main.py file was still running on loading the pico UF2. Long story, short, I used Thonny to delete the main.py file and reloaded CircuitPython. I now get,

Traceback (most recent call last):
  File "<stdin>", line 17, in <module>
AttributeError: 'module' object has no attribute 'I2C'

I think that hints that the regular swapping between Pythons has left a whole range of invisible files in the board that are stopping it working correctly.

I’m going to search for a way of properly clearing the decks before loading CircuitPython.

As far as I remember, there is no code.py to begin with, it’s up to you to provide one.

CircuitPython will look for code.py first and if it is missing it will look for main.py. So you can end up with CircuitPython running code that is intended for MicroPython.

With regards to the I2C, the only code in the simpletest which looks for that is
expander = TCA9555(board.I2C())
To the best of my knowledge board is a CircuitPython module and not available in MicroPython. It essentially returns busio.I2C(scl=board.SCL,sda=board.SDA) if the pins board.SCL and board.SDA are defined.

And I’ve just realised that the Pico RP2040 doesn’t have board.I2C or board.SCL or board.SDA defined because they can run on almost any pin.
All my testing has been done with the Pimoroni Pico Lipo which does have these pins defined. I’ll get a beta.3 uploaded in the next few hours with Pico RP2040 details included in the examples.

At a guess try:
expander = TCA9555(busio.I2C(scl=board.GP5, sda=board.GP4))

I found ‘nuke’ in the pico examples. I now have all the CircuitPython files. However, I should have realised what the problem was in line 17

expander = TCA9555(board.I2C())

The Pico board has no I2C, SPI or UART attributes. The solution in MicroPython is,

i2c = I2C(0, scl=Pin(5), sda=Pin(4), freq=100000)
print(i2c.scan())

I have not got that working for SoftI2C and bit-banging because it complains of too many attributes (or objects or…). I think the documentation is wrong.

In CircuitPython it is something like,

i2c = busio.I2C(board.GP5, board.GP4)

I have yet to find out how to set I2C0 and I2C1, but I assume that I2C defaults to I2C0. I think busio is just for pin setting and there must be another module for setting the frequency.

I need to follow this up, further to resolve I2C for the Pico. I wonder if you are working with an earlier version of Pico board that does not cater for two I2C ports and the possibility of bit-banging on other pins…

A new version has been uploaded to github and PyPI with better examples for the Pico.

Yes, yes, yes! I’ll interpret what he’s coding.

It works perfectly. Thank you.

But, I have checked over the schematics of the RPi Pico and the ability to route the hardware IO devices to different pins appears to be in the RP2040 with a device called the AHB-Lite Crossbar. Pimoroni say that “Pimoroni Pico boards are firmware agnostic! You can program them with C/C++ or MicroPython in the same way as you would a Raspberry Pi Pico.” and “One very exciting feature of RP2040 is the programmable IOs”.

I don’t know what is programmed into the Lipo board but I would expect all of the GP pins to be attributed. This suggests that the Lipo should be just as flexible as the RPi Pico and should work with your;
i2c = busio.I2C(scl=board.GP5, sda=board.GP4)

Having gone down several Escher inspired rabbit holes, I have uncovered a few new links that have only recently appeared.

More information and links, here:

Blinka brings CircuitPython APIs and, therefore, CircuitPython libraries to single board computers (SBCs).

I haven’t worked out how it is used in the Pico, but it might add HID, MIDI and lesamouraipourpre’s TCA9555 Expander code and Dotstar to MicroPython for use with the RGB Keypad Base libraries.

The Pimironi RGB Keypad Base library could be used with HID or MIDI (or other IO) or lesamouraipourpre’s code could be used with additional I2C devices on the bus. I am still in too much of a daze to add any clarification, but Pimoroni (and Adafruit) are adding libraries and code all the time.

There is a Learn guide related to this at Adafruit:

The TCA9555 library has been added to the CircuitPython Community Bundle:

Libraries can be added to MicroPython in the same way as with CircuitPython. Congrats on getting your library added to the CircuitPython bundle.

Unfortunately, busio is not compatible with MicroPython and it is called by TCA 9555expander.

However, I have been able to read the bytes from the TCA9555 in MicroPython and convert them to an int. My problem is that the bytes are the wrong endian which is spoiling my fun. I don’t think I am far from a solution which will make it possible to have several devices on the same I2C port, but using different addresses.