Using a Raspberry Pi Pico, CircuitPython (+ javascript) and a standard LED Matrix create something people can have in their living room and use for whatever they can think of. This project contains all the code for loading onto the Raspberry Pi Pico.
Previously (10 years ago!) I had written LED Matrix. Use your old Model A Raspberry Pi and wanted to update the technology so I could give it to friends. This original proejct was very complex with a very wide range of technical expertise required to implement. So I set out to build something that friends could plug in and start using with no real technical expertise. I haven't quite achived that, but it is much closer.
I really had the following ambitions with this project
- Simple to build - extremly low entry to achive maximum results
- First and foremost - provide the ability to see the next three Trams coming at the end of my street (totally Melboure/Australia based). This deeply effects my family in the morning and its usefullness is the only reason I am allowed a LED matrix in the living/dining area's in the house.
- The trams run on roads and have real time updates and one can be coming in 15 minutes, then 3 minutes later it is due in 5 minutes - it is traffic dependant.
- Make it easy to integrate with phone widgets - quick and easy to turn it on/off
- Make it easy to add new functionality
I have zero affiliation with the company I am going to use for reference, but my dealing with them have always been really good.
- Raspberry Pi Pico 2WH (Wireless WiFi, with Headers)
- 5V DC 4A Fixed 2.1mm Tip Appliance Plugpack
- DC Barrel Jack Adapter - Female
- RGB full-color LED matrix panel (2.5mm Pitch, 64x32 pixels) - this includes
- 4a IDC to XH2.54 - from LED matrix to Pico GPIO Pins
- 4b Power Supply Cable
- Micro USB cable and power (phone charger more than enough)
- Wire Strippers
Tip
Check and recheck that the micro USB cable does data AND power. Some only do power. My first couple of weeks playing was spent telling Core-Electronics that they had sent me faulty devices. Turns out all 3 cables I tested with did not do data! They were very understanding.
We basically need to do the following
- Get the Pico set up and ready to code on
- Install CircuitPython and dependencies
- Build our LED matrix display
Google getting started raspberry pi pico. Follow the bouncing ball on a tutorial like Getting started with Raspberry Pi Pico which
- Install Thonny (Adafruit also has Mu Editor, I thought Thonny was simpler)
- Use Thonny to code on the device and understand the principles. I just got the led on the Pico flashing before I moved on.
You are going to delete all you have done on the Pico up until now and install CricuitPython
- I basically followed Installing CircuitPython
- Use the package manager to install other libraries that this code requires (see below)
Note
It did not always work exactly as described in the tutorials, but was obvious with a bit of tinkering
CircuitPython Libraries to install (Tools->Manage Packages in Thonny menus)
- adafruit_httpserver (searching package manager in Thony, search for -> adafruit-circuitpython-httpserver)
- adafruit_hashlib (required on Adafruit Matrix Portal M4, not Pico -> adafruit-circuitpython-hashlib)
- adafruit_datetime (-> adafruit-circuitpython-datetime)
- adafruit_requests (-> adafruit-circuitpython-requests)
- adafruit_ntp (-> adafruit-circuitpython-ntp)
- adafruit_display_text (-> adafruit-circuitpython-display-text)
- adafruit_display_shapes (-> adafruit-circuitpython-display-shapes)
From this Repository
- Copy across Code.py and cfg
- Update cfg with your local wirelss name and password
- Register (https://timezonedb.com/), then Update cfg with your timezonedb_api_key. Micopython and Circuitpython are missing ALOT of python libraries and this helps us manage accurate time.
- Copy across directories -> animation, data, img, saved, static, utils
You should now be able to run code.py, and be able to go to the 'website', the address will be in the Thonny IDE. It won't do much until we hook up our LED Matrix
Note
If you change the line in code.py to debug=True you will be able to see opening the website and any requests you make in the Thonny IDE.
server = Server(pool, "/static", debug=False)
Have a look at the picture above, it gives you a pretty good idea of where we are going.
- Start with connecting the IDC ribbons pins to the Pico. Using the Pico diagram and the table below. (Disconnect it from the Micro USB to do this)
Wire (if you are not colour blind) to Pico Pin is the easiest way to go.
| Function | Wire | GPIO Pin | Pico Pin* |
|-------------------|--------|----------|-----------|
| High R data | Blue | GP0 | 1 |
| High G data | Green | GP1 | 2 (4) |
| High B data | Yellow | GP2 | 4 (2) |
| GND | Orange | GND | 3 |
| Low R data | Red | GP3 | 5 |
| Low G data | Brown | GP4 | 6 (7) |
| Low B data | Black | GP5 | 7 (6) |
| GND | White | GND | 8 |
| A line selection | Grey | GP6 | 9 |
| B line selection | Purple | GP7 | 10 |
| C line selection | Blue | GP8 | 11 |
| D line selection | Green | GP9 | 12 |
| E line selection | | | |
| CLOCK | Yellow | GP10 | 14 |
| LATCH | Orange | GP11 | 15 |
| Output Enable | Red | GP12 | 16 |
| GND | Brown | GND | 18 |
Note
The above pin allocation is for the Matrix in the list above. I also bought a RGB full-color LED matrix panel (2.5mm Pitch, 64x32 pixels) and its pin allocation varies slightly - the brackets () in the above table
-
Connect the other end of the IDC to the Matrix (make sure its to the right end) Don't turn it on yet!
-
Affix the DC Barrel jack adaptor to the Power Supply Cable I cut off the connectors and used the wire strippers - red to +ve, black to -ve
-
(Re)connect the Micro USB from your Pico to the Computer You will have unplugged it to connect the cables. Stop Thonny IDE and restart code.py
-
Turn on the Power to your Matix and cross your fingers. Hopefully it all works and you can play with the Website driver
You will notice the website always produces a URL every time you ask it to do something. The intention is NOT to use the website to drive the Matrix, but use those URL's in widgets on the iPhone and tablet applications. I have an iPhone and the application Shortcuts has a Get Contents of URL shortcut. If you paste the contents of the URL to Copy as the url - then the iPhone shortcut (which you can add as widgets on your homepage etc.) to drive the Matrix. For example I have also used a Text Input in Shortcuts so I can nominate the time for any countdown from my widget.
I am fairly confident other operating systems, especially Andriod, has almost identical apps/features.
A fair bit of effort went into making this project easily extendable. Micropython and CircuitPythons limited python libraries prevented this being a project where you could just drop new files and they would be dynamically loaded. However, it is still fairly simple to add new functionality.
When looking at adding new features, follow along with CountDown as an example - hard to go wrong. Most things will work of you follow conventions. There are 2 main parts, a web front end to choose functionality, and driving the matrix using Circuitpython based on the request from the web front end.
Basically three area's need updating in the static directory:
- Create a new Javascript file (copy count_down.js and rename). Rename x_process and x_load to your new feature name (identical to step 3)
- delete pause_countdown unless you need similar funtionality which inegrates with a running feature
- Add js file in index.html header
- Include new feature as an option in the dropdown for id rgb-function-to-run
- the buttons use dynamic javascript (window context) to run fucntions based on the value selected in the dropdown
and then start coding (by example) in your new javascript file. Its basically doing domain object injection. See what I have done, and copy. Its not supposed to be a beautiful interface, but functional so you can generate the url's to put in widgets.
I found coding javascript locally (file system) while running Thonny meant that the json gets loaded, but I didn't have to restart Thonny all the time. To do this I temporarily hacked the fetch in index.js to not be relative. DON"T FORGET to turn it back.
This is very similar to the front end. Again use CountDown:
- Copy CountDown.py (in the utils directory). Rename it and rename the Class in the file
- Update code.py.
- import the new Class - near the top
- The route for loadjson has an if / else if that needs to be added to.
and then start coding. Circuitpython doco is pretty good and it does some amazing things. When you start getting fancy you are likely to need to add more packages for functionality. Thonny does this easily (see section above when setting up Thonny).
I found coding directly through Thonny onto the device the best way as you got imnmediate feedback. I also removed the try's in the while / true loop as it made it much easier to find issues.
At some point, we plug it and use it, and stop playing with it. When that point comes, it is worth driving the Pico off the 5V power source instead of a separate power source. The above diagram shows
- the connection from the +ve and -ve wires to
- pin 39 (+ve) and pin 38 (-ve/GRND)
- My 3D printed cover for the matrix.
Caution
NEVER have your Pico plugged into a micro USB AND running off a separate power source. This can break your Pico.
There is also a led-matrix.stl file, which I used to create a simple box for the LED-Matrix. Unfortuantely my 3d printer was only 20cm by 20cm, so I had to do half at a time, thus the dodgy fit.
MOST important because I can have it in the living room due to this functionality. You can also optionally add the route number to the display. With 64 pixels, sometimes it won;t show all of the thrid tram duw.

You can put more than one panel into series - I had 3 working at one point with almost no effort. Circuitpython framework just needs to know the width. I added in a config item which is horizontal_screens . Default is 1, but if you add more screens, change the config to 2 and pretty much all the code still works. The code I wrote always works to wdith, so just changing the initial value meanys ot works. The small panel I bought (RGB full-color LED matrix panel (2.5mm Pitch, 64x32 pixels)) acually had a power splitter which let me easily power 2 panels from the same 5V connector. With all panels I have bought they include the connector to serialise multiple panels.
- Weather is available, but I found it made the system unstable. I never figured out of it was the poor network connection in the living room, the API I was using (needed ssl connection) or any other reason. Try it out, but you will have to load your city into cities.json in the data directory. I found co-ordinates using google maps.
- Text Display. Next 3 Trams uses something called ThreeLines (in python) and Text Display in the front end. It allows you to display up to three lines on the matrix and is exceptionally flexible with what you can do with it - Just play with it, and you can add Weather back as an option. I only removed it from the front end. (update javascript function
text_display_line_loadintext_display.js) - Scrolling Text - part of Display Text / ThreeLines
- Clock - part of Display Text / ThreeLines
- Images and animations need to be pre-loaded onto the Pico, but just loading the files using underscores in the filename will have them appear in the options. For best results, make them 64 x 32 bit images. Lots of websites out there to help you.
-
- Added a feature to roate all the iopages through


