A Big I²C Project

A Big I²C Project

This project started in a small way when I purchased a PLASMA 2350 kit with skull bottle. The kit worked very well, was easy to program but needed some sort of input device to make the blinkies flash. For a start I added 3 pots to the ADC pins for basic colour control but needed an extra pot to control position. I connected an IO Expansion board so that I could connect the 4th potentiometer. I thought a display would be useful so added a SSD1306 128x64 mono I2C device to the same bus. I quickly got these talking to each other and the fun had started.

Normally, I would have swapped input devices to try out a new one. I remembered that in theory the I2C bus can support up to 127 devices in its addresses but only about 10 or 11 in practice because of capacitance problems on the bus. I looked around and collected all the I2C devices from my collection and started adding them one by one. Each one was added, tested for basic operation before adding another. The list grew longer and it still worked. I included I2C sensors and it all kept working.

I really like a big, bright and colourful display such as Display 2.0" and wanted to add it the mix, but the PLASM board did not have sufficient pins for an SPI device. I changed project focus and switched processor board to a Pico Lipo 2XL W. Loads of pins and plenty of memory. I dug out an old LCD2040 with an I2C backpack – slow and looking from a different era - but it worked. It needed 5V so I put that on the other I2C bus.

I exchanged the long RGBLED string in the skull for a shorter one (15 Neopixels and less power load) and added a 16 Neopixel ring to another pin. At this point Pimoroni brought out the Infra-Red Aye Arr remote with IR receiver stick. It only needs 1 pin and is ideal for controlling Neopixels so I connected it up.

In the end I finished up with the following circuit.

Scanning I2C bus. (GPIO Pins 4 & 5 = QW/ST connector)

10 devices found.

Dec addr: 10 , Hex addr: 0xa Track Ball Has RGBLED in ball

Dec addr: 15 , Hex addr: 0xf Rotary encoder Has RGBLED in knob

Dec addr: 24 , Hex addr: 0x18 IO Expander Analog and Digital I/O

Dec addr: 33 , Hex addr: 0x21 Qw/ST Pad 10 buttons and 4 white LEDs

Dec addr: 35 , Hex addr: 0x23 LTR-559ALS-01 Light sensor

Dec addr: 56 , Hex addr: 0x38 AHT20 Temperature & Humidity

Dec addr: 60 , Hex addr: 0x3c SSD1306 OLED Display 128x64

Dec addr: 100 , Hex addr: 0x64 DF2301QG Offline Voice Recognition Sensor

Dec addr: 106 , Hex addr: 0x6a LSM6DS3TR-C Accelerometer & gyroscope

Dec addr: 118 , Hex addr: 0x76 BME280 Temperature, pressure, humidity

GPIO 0 has 15 NeoPixel RGB string in the skull bottle

GPIO 38 has 16 NeoPixel RGBW ring

IR Receiver stick on Rx, GPIO 1. Aye Arr IR Remote sends the signals for input.

3 x 10K potentiometers on ADC pins (GPIO 26,27,28)

Hanging from the IO Expander: RGB LED, Single RED LED, button and 10K pot

0x27 is LCD2004 on the other I2C bus (GPIO Pins 2 and 3) – 4 rows of 20 characters

Display 2.0" SPI 320x240 pixel colour screen

All connected to Pico Lipo 2XL W, on a Pico Decker

Coding

This was a pretty big project. I wanted a single program, menu driven, which would use all the connected components and devices including the WiFi chip on the Pico board. I decided that the menu would be driven with the buttons on the QW/ST Pad and displayed on the little SSD1306 screen. The rather slow LCD2004 was used mainly for instructions for each menu item.

SSD1306 Menu screen

All run from the Menu

There would be distinct sections to the code – hints of COBOL! The first part would import all the necessary libraries and carry out the setup for each in turn, with a simple LED flash or screen message to prove they were working. A scan of the I2C bus would be useful to check all 10 devices had connected. The second section would allow the user to pick input devices from the menu and output changes to as many devices as possible. I thought I could use the WiFi connection to report on recent earthquakes as I already had code which could be easily repurposed.

Each menu item/input device was coded and the resulting visual output tested as a separate program. Then the active part converted to a single procedure, added and tested in the main program via the menu. Once this was finished, I collected many of the similar visual output sequences together into a couple of procedures [lights() - switch things on] and [tidy_up() – turn them off]) to make the whole thing neater.

Each time I was ready to add a new menu item to the growing program I saved it with a new version number. You do not want to damage the existing working code while adding a complicated new feature. Increasing versions numbers allow you to easily take a step back to a working version if you get lost/confused in an upgrade.

After thoughts

The simple SSD1306 display is very useful. The improved graphical instructions now available in MicroPython for the Pico are excellent and upgrade the display from just small text output. I’ve included a simple routine to allow double sized text characters in the code. Which others might find useful.

The Aye Arr remote library taught me a great deal. (I’d not encountered binding instructions before nor realised that you can use tuples without brackets!) It might take bit of time to understand how the library works but it is much more versatile and reliable than less sophisticated IR remote libraries that I’ve used in the past.

I was delighted with the Track Ball - very responsive and easy to code. I prefer it upside-down with the wires pointing away. It would make a good menu controller in place of the QW/ST Pad. I kept it and the other small components steady on the desktop with small blobs of BluTack.

I’ve been unhappy with the output from Pico ADC pins for ages. You do not get a reliable zero when using a 10K potentiometer. (Arduino UNOs gave much more reliable 0 to 1023 readings years ago. Who needs 16-bit values from a pot? Most of the time we only need a range of 0-255. [I know, consistent 16-bits with PWM]) The IO Expansion board is the answer. It has more ADC pins, no jitter on the zero, versatile with PWM and digital IO. It is really easy to code and you can use several together by changing the addr.)

I feel I must thank all the programmers who supply the libraries which make the lives of we ‘hobby coders’ so much easier by doing all the really difficult stuff and neatly package it for our use in MicroPython. Their efforts are built on the work and ideas of Ad P.M.M. Moelands and Herman Schutte, who developed I²C (Inter-Integrated Circuit) in 1980 at Philips’ laboratories in Eindhoven, Netherlands.

Code is below.

Comments welcome

Regards

Tony Goodhew

2 Likes

Very impressive!

Just some notes on the I2C limit: there are two main problems with many I2C devices. One is capacitance as you mentioned. And the other one are strong pullups (i.e. pullups with a low R). A lot of I2C breakouts bring their own pullups (typically 10K) and adding ten of those devices gives you 1K. This can pull SDA/SCL so strong to high, that you don’t get clean signals anymore. But as you demonstrated, it can still work.

One simple workaround is to use I2C multiplexers based on TCA/PCA954x. They are cheap and provide 4 or 8 additional busses. Pimoroni has them.

Another I2C device I can recommend: there are simple touch keypads based on the MPR121, which is also an I2C device. So you get a little keypad with 12 keys only taking up a single I2C address. Pimoroni has the raw MPR121, but I prefer those ready to use 4x3 keypads.

Thank you, I will look into those. At the moment I’ve out of things to add on to the circuit.

Code too long to post here. I will put it on pastebin.com

You can find the code here: 2XLW BIG-ONE v2 - I2C project - Pastebin.com