I covered the basics of the Arduino Uno Q, but I very much wanted to experiment with the LED Matrix.
The LED Matrix is an 8 by 13 grid of LEDs that are blue. I won't get into how long it took me to get a single LED to light up, but it was definitely longer than it should have been. It took a while to get it streamlined and then make it something worth watching.
We are going to make rain on the Arduino Uno Q.
Basic Setup
The first thing you need is a system hooked up to the Arduino with the Arduino App Lab and the Arduino IDE. You can run natively on the board if you have the connections to do so. Now, the Arduino App Lab allows for writing apps in Python, but the Arduino IDE is C++. We will work with the App Lab since this code is in Python.
For anyone who needs help with Python, there are Python tutorials starting with 'Python Series Part 1: Installing and Configuring Python 3'. You can perform a search for the following articles, which are listed numerically by part.
For a basic setup of the Arduino Uno Q, look at the article 'Arduino Uno Q: A Debian Linux Board for Headless Projects'.
NOTE: On Ubuntu 26.04, I could not get Arduino IDE to start until I added 'libfuse2' with the command:
Code:
sudo apt install libfuse2
So, to make this work, you need to open the Arduino App Lab. By 'Apps', click on the plus sign (+) to add a new app. From the drop-down list, choose 'Create New App'.
The lab will prompt you for a name to give the new application you are creating. For mine, I gave it the name of 'Rain'. Click on the 'Create New' button.
In the lower-left of the window is a section that lists the files included in the app. Keep in mind that the app is not just made up of Python code, but you can also add C++ code. The C++ code is for the real-time microcontroller (MCU) layer that the app uses for time-sensitive options, managing pins, and controlling sensors. You will need to place the code under 'sketch/sketch.ino'.
The Python code is for the CPU that is also running Linux. The Python code goes into 'python/main.py'.
We now need to place our code in the proper places. Under 'sketch/sketch.ino', you should erase all the existing sample code and paste in:
Code:
// SPDX-FileCopyrightText: Copyright (C) Arduino s.r.l. and/or its affiliated companies
// SPDX-License-Identifier: MPL-2.0
#include <Arduino_RouterBridge.h>
#include <Arduino_LED_Matrix.h>
#include <vector>
Arduino_LED_Matrix matrix;
const uint8_t FRAME_ROWS = 8; // Rows
const uint8_t FRAME_COLS = 13; // Columns
const uint8_t FRAME_SIZE = FRAME_ROWS * FRAME_COLS; // Total number of pixels in frame
uint8_t frame[FRAME_SIZE] = {0}; // Global frame buffer initialized to zeros (empty frame)
void setup() {
matrix.begin();
matrix.setGrayscaleBits(3); // Set the number of bits for brightness levels to 3 (0-7 values)
matrix.clear();
Bridge.begin();
Bridge.provide("draw", draw);
}
void loop() {
matrix.draw(frame); // Draw the current frame on the display
delay(10);
}
void draw(std::vector<uint8_t> newFrame) {
size_t len = min(newFrame.size(), (size_t)FRAME_SIZE);
memcpy(frame, newFrame.data(), len);
}
This is all we need for the C++ code.
Now, click on 'python' in the same section, select 'main.py' from the drop-down list, and remove the code already there. In its place, paste in:
Code:
# Code from Linux.org
def showLED():
frame = Frame(frame_array)
frame_bytes = frame.to_board_bytes()
Bridge.call("draw", frame_bytes)
from arduino.app_utils import App, Bridge, Frame
import numpy as np
import time
import random
import sys
#Define a 2D numpy array of brightness values for the 8x13 LED matrix
frame_array = np.array(
[
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
],
dtype=np.uint8,
)
base = np.array([0, 0, 0, 0, 0, 0, 0, 0])
rng = np.random.default_rng()
showLED() # Clear all LEDs
b1 = 1
for i in range(8):
b = random.randint(1, 3)
b1 = b1 + b
base[i] = b1 * -1
rng.shuffle(base)
try:
while True:
for i in range(8):
v1 = (base[i])
v1 += 1
base[i] = v1
if (v1 >= 0) and (v1 <= 12):
frame_array[i][v1] = 7
if v1 >= 13:
v1 = base[i]
b = random.randint(1, 4)
base[i] = b * -1
showLED()
for i in range(8):
v1 = base[i]
if (v1 >= 0) and (v1 <= 12):
frame_array[i][v1] = 0
showLED()
except KeyboardInterrupt():
frame_array.fill(0)
showLED()
sys.exit(0)
Once this is done, you can run the code and you should see something similar to Video 1.
VIDEO 1
The code is creating dots in random places and spacing, so the video won't be exact.
Let's get into the code itself and see how this works.
The Code
To start, let's look at the function 'showLED'. The code is:
Code:
def showLED():
frame = Frame(frame_array)
frame_bytes = frame.to_board_bytes()
Bridge.call("draw", frame_bytes)
This function redraws the LED Matrix from the 'frame_array' that specifies the intensity of each LED. The array is 8 by 13 and corresponds to the LED Matrix with numeric values between 0 and 7. The value of 0 determines that the LED is off and 7 is the brightest setting.
The first line places the array through the 'Frame' option and passes it to the variable 'frame'. This process is to verify that the size of the array is appropriate and that it should comprise valid values. It prepares the array as a flat byte array to pass over the RPC Bridge to the microcontroller. Since the data is going to the microcontroller, the data goes to the C++ code that we placed into the 'sketch.ino'.
The second line will serialize the array into binary so it can go over the Bridge to the microcontroller. Once the microcontroller receives the stream, it can repackage the data and perform the commands.
For the third line, we issue the command to use the data on the board. The system sends the command over the bridge and takes the data in 'frame_bytes' and passes it to the C++ code for the 'draw' option. The data goes to the LED Matrix and changes the LEDs’ intensities as needed.
So, since this is a function, we need to call the function every time we need to update the LED Matrix. This should make things a little easier. All we need to do is manipulate the 'frame_array', call the function, and repeat.
The next few lines import specific libraries that we need to manage commands included in a specific library. For example, we load 'random' because we are generating random numbers.
Next, we create the array for 'frame_array' with:
Code:
frame_array = np.array(
[
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
],
dtype=np.uint8,
)
Here, we use a 'numpy' array (np) that shows the 8 by 13 array. We set the data type as 'uint8'. Which are unsigned integers. Since the values for the LED intensity are 0 to 7, we do not need negative numbers, which are signed. Unsigned numbers are always positive numbers. So, to work with the array, we designate a row and a column. The rows are 0 to 7 and the columns are 0 to 12, for a size of 8 by 13. If we wanted to change the intensity of the LED at row 5 and column 3 to a setting of '6', the code would be:
So, if I called 'showLED()' only one LED would light up at near full intensity. I can change as many as I want and then call the function. Be aware that all the code listed above is really a single command, but it is spread out over multiple lines to make it easier to visualize the array as two-dimensional.
The next line creates a one-dimensional array that will hold specific data for manipulating the individual columns of the LED Matrix. If we are going to show falling rain, or pixels of the LEDs, we do not want to have the lit pixels all on the same row. They need to be more random. Of course, there may be times when two raindrops will be in the same row, or over two can occur. The new array, named 'base', we create with:
Code:
base = np.array([0, 0, 0, 0, 0, 0, 0, 0])
Each one of these is a row and will contain a number that is a negative number. Notice that we did not give the 'base' array a data type. The default data type is 'float64', but if you pass only integers to the array, then the system will default it to 'int64' on a 64-bit system or 'int32' on a 32-bit system (depending on your Operating System).
The next line is an ability to handle random number generation for NumPy:
Code:
rng = np.random.default_rng()
We will get into this more when we get to the random number generation for NumPy.
Right now, we made the NumPy array for the 'frame_array' and all values are still '0'. This means that we will turn off all LEDs. If I call 'showLED', it will clear any LEDs that are not turned off. This is a good way to clear the LED Matrix.
For these random numbers, I need a place to start, so I set up a variable named 'b1' and set it to '1'.
Next, we go through a for loop:
Code:
for i in range(8):
b = random.randint(1, 3)
b1 = b1 + b
base[i] = b1 * -1
We are going through a loop eight times, which the values for 'i' will be 0 to 7. Remember that the For loop starts at 0, unless you specify otherwise, and goes through the iterations shown by the value you give, which is 8. This makes the value of 'i' start at 0 and end at 7. It then goes through 3 commands before incrementing 'i' and it executes the loop again.
First, we generate a random number between 1 and 3, inclusive, and places the value in the variable 'b'. This value is then added to the variable 'b1'. Then we take the value in 'b1' and multiply it by -1 to make it a negative number and place the result in the array at the element 'i'. This will then generate our initial values for each column; unfortunately, this will create values that are usually in a decreasing order from element '0' to element '7' in the array.
To fix the problem of the values decreasing steadily, we need to mix up the values by switching them around randomly. To do this, we run the command:
With this single command, it shuffles the values in the columns of the array with the name of 'base'. Now, the numbers are not in any kind of order and will look more random and not in order.
The way the numbers are going to work is that we will perform a continuous loop and increment the values by 1. When a value reaches between 0 and 12, it can go on the LED Matrix. So, when the value is 0, we can light up the LED and then flip it off, increment the numbers and light it up again. This will be repeated over and over. Once the value hits 12, which is the last LED in the column, we generate a random negative number and continue the process.
So, now that the basics are set up, we can look into the heart of the code that is continually looping:
Code:
try:
while True:
for i in range(8):
v1 = (base[i])
v1 += 1
base[i] = v1
if (v1 >= 0) and (v1 <= 12):
frame_array[i][v1] = 7
if v1 >= 13:
v1 = base[i]
b = random.randint(1, 4)
base[i] = b * -1
showLED()
for i in range(8):
v1 = base[i]
if (v1 >= 0) and (v1 <= 12):
frame_array[i][v1] = 0
showLED()
You may notice that the code is inside a 'try' block. Within the 'try' block, we can handle error detection and other things, which we will get to soon.
Within the 'try' block is a 'while' block that is only testing for 'true'. There is no actual testing being done, so unless we come across a 'break' command, this loop will go forever.
So, the next line starts a for loop setting up the value 'i' to go through each row, 0 to 7. Through the for loop, there are a few lines of code that it works through.
First, it finds the value in the specific element of 'base' and places it in 'v1'. The value is then incremented by 1, and that value goes back into the base array.
The third line in the for loop checks if the value of the base array element is greater than or equal to 0 and less than or equal to 12. If this is true, then the specific row and column are on the LED Matrix. So, the value at the '[row][column]' is set in the 'frame_array' to a value of 7, for the intensity.
Next, we check if the value of the base element is greater than or equal to 13. This means that the column has gone through the LED Matrix and reached the bottom of the Matrix. Here, the column is ready to be reset as it was in the beginning.
We then generate a random number between 1 and 4. Next, it is multiplied by -1 and put into the base array element.
This process continues until all elements have been changed as needed, and we then call the 'showLED' function to show the updated LED lights.
The next for loop goes through all columns and gets the element value, then checks if the values fall between 0 and 12. If they do, so that an LED is lit on the Matrix, we set the intensity to 0, which means off. Once all values are set in 'frame_array', we call 'showLED' to update the LED Matrix, which all the LEDs should be off.
This while loop will then increment the values, set them in the frame_array, and show the LEDs again. The process continues on and on.
The last part of the code is a condition that can help stop the while loop. If is an exception to the 'while True'. It is a 'KeyboardInterrupt', which occurs when pressing 'Ctrl+C'.
When the loop is broken, the 'frame_array' is filled with zeroes. The system passes control to 'showLED' to clear all lit LEDs to off. Then the system will exit with an exit value of '0'.
This is the entire program. You can change values to increase the numbers as you may need. Play with the program and learn it, because this is a simple basis for some dynamic LED lighting by the program.
Conclusion
This is a nice, brief sample of code to show how to implement the LED Matrix with Python. I'm hoping the code is understandable and you can easily learn it as well as use it as a basis to create some of your own apps.
Try it. I should have more coming to give more examples of using the Arduino Uno Q.












