Unable to reduce LED matrix brightness

Trying to get some code together that will allow me to periodically call a Linux program to get some status text and then show that text on the LED matrix. Is there an API page for the Arduino_LED_Matrix somewhere? All I seem to be able to find are a few random examples.

I have a function that seems to be the start of what I need and will scroll some text, but no matter what I try I cannot reduce the brightness, and the display is very bright, much brighter than necessary in normal indoor lighting.

Code I have:

#include <ArduinoGraphics.h>
#include <Arduino_LED_Matrix.h>

ArduinoLEDMatrix matrix;

void setup() {
  matrix.begin();
  matrix.setGrayscaleBits(3); // Supposed to enable grayscale 
                              // but apparently doesn't work
  matrix.textFont(Font_5x7);
  matrix.textScrollSpeed(100);
  matrix.clear();
  //Serial.begin(115200);
  count = 100;
}

void loop() {
  // should display a dim color but the 'RGB' values apparently do nothing
  matrix.beginText(0, 1, 0, 0, 1); // X, Y, then R, G, B
  matrix.print(" count=" + String(count) + "  ");
  matrix.endText(SCROLL_LEFT);
  if (count > 0) count--; 
}

Would much appreciate any help on how to make the LEDs not be super bright. Once that is figured out my next question will be how to make a system call on the linux side to get some status text and then pass that to the MCU side. And eventually I would like to be able to set all this up via SSH only and not have to rely on the App Lab program, is there any documentation on how to do that?

The Arduino matrix doc is here.

If I recall, to change brightness, you change the 1s in the matrix table to a number between 1 and 7.

BTW, the doc is for the UNO R4, but the UNO Q Matrix I think behaves similarly, just that it has one extra column. The example found in the IDE from File -> Examples -> Arduino_LED_Matrix -> Basic under the Examples for Arduino UNO Q heading demonstrates this for graphics at least.

To add to the above: in the Arduino_LED_Matrix header (ArduinoCore-zephyr 1.0.0) the ArduinoGraphics hook is set(x, y, r, g, b), and it stores a pixel as 1 if (r | g | b) > 0, else 0. So the values you pass to beginText() are only on or off, they cannot make the text dimmer, which matches what you saw. The header comment for setGrayscaleBits() says the biggest value in the framebuffer should be 2^bits - 1, so with 3 bits the levels are 0 to 7. Grayscale is used by draw(buf), which takes a buffer of brightness values. My guess, not tested on a board, is that you render your text into a 8x13 byte array yourself with low values like 1 or 2 and pass it to draw(). I found no separate brightness function in that header.

Hi, thanks for the quick reply, yes looks like the ArduinoGraphics text functions are useless for controlling brightness, which is maybe fine as I may be doing some custom graphics anyway and thus need to create my own fonts, though I wonder if the fonts used by .textFont might be available in a .h file somewhere? Or might there be a way to modify the ArduinoGraphics / Arduino_LED_Matrix source code to dim the output of the text functions? Is this source code available somewhere? Thanks

Both are open source. ArduinoGraphics is a public repository under arduino-libraries on GitHub (MPL-2.0 licence). Its src folder has Font_4x6.c and Font_5x7.c, which are the fonts behind textFont(), as C data files, plus ArduinoGraphics.h and ArduinoGraphics.cpp. In that header set(x, y, r, g, b) is a pure virtual function, and the matrix class implements it, so that is where text pixels turn into on/off. Arduino_LED_Matrix lives in libraries/Arduino_LED_Matrix of the arduino/ArduinoCore-zephyr repository (Apache-2.0). A modified copy of the library in your sketch folder is one way to add a dim hook, but that part is a guess: I have not checked whether the render step (loadPixels) uses values other than 0 and 1, and I have not built it.

I opened an issue in the ArduinoGraphics repository to let them know that the stroke / grayscale functions appear to not work for text functions. (text functions do not appear to support dimming · Issue #71 · arduino-libraries/ArduinoGraphics · GitHub) I did also try just the plain text() function without any scrolling and same result.

Will see if I can copy the library source into the IDE, hopefully that is not hard to do.

Piece of semi-transparent (grey) plastic film (used on car windows).
That makes it also easier to read.

Yes a piece of dimming film is fine for prototyping but for production I will have a configuration parameter to set the display brightness and need the display to support the full range of brightness so my products will be usable in everything from low light to full sun ambient lighting conditions.

After several debug iterations Brave AI tracked the issue down to the ArduinoCore-zephyr repo ArduinoLEDMatrix set() function - the set function there discards the RGB values and was outputting only binary values to the output matrix buffer. I opened an issue there ArduinoLEDMatrix set() function discards brightness parameters · Issue #690 · arduino/ArduinoCore-zephyr · GitHub and let them know the fix, which applies to both the text() and [begin/end]Text() methods and thereby will support anti-aliased fonts as well as dimming.

I fixed this on my Uno Q by doing the following:

sudo nano ~/.arduino15/packages/arduino/hardware/zephyr/1.0.0/libraries/Arduino_LED_Matrix/src/Arduino_LED_Matrix.h

See change after // DDG: comment:

        virtual void set(int x, int y, uint8_t r, uint8_t g, uint8_t b) {
                if (y >= canvasHeight || x >= canvasWidth || y < 0 || x < 0) {
                        return;
                }
                // the r parameter is (mis)used to set the character to draw with
                //_canvasBuffer[y][x] = (r | g | b) > 0 ? 1 : 0;
                // DDG: Set grayscale value instead of binary
                _canvasBuffer[y][x] = (r + g + b) / 3;
        }

Then the 2nd issue was, as Brave AI summarized,

the function that actually pushes pixels to hardware is bit-packing your values, treating each byte as a single bit.

Where the data gets destroyed

Look at the call chain from endDraw() / endText():

endDraw()
  → renderBitmap(_canvasBuffer, 8, 13)
    → loadPixels(&buf[0][0], 104)
      → loadPixelsToBuffer(arr, 104, _frameHolder)   ← KILLER
        → loadFrame(_frameHolder)
          → matrixWrite(frame)                        // 4×uint32_t, 1 bit per LED

The culprit is loadPixelsToBuffer:

static void loadPixelsToBuffer(uint8_t *arr, size_t size, uint32_t *dst) {
    uint32_t partialBuffer = 0;
    while (pixelIndex / 32 < size / 32) {
        partialBuffer |= *frameP++;          // ORs the FULL byte into the buffer
        partialBuffer = partialBuffer << 1;  // shifts left by 1
        pixelIndex++;
    }
    ...
}

This is designed for binary values (0 or 1). Each byte is expected to be 0 or 1, and the OR + shift packs them one bit at a time into a uint32_t. When you write 128 (0x80), it ORs bit 7 into the accumulator, then the shift smears it across adjacent bits — hence your incorrect pixels.

The fix: bypass the bit-packing path

The library already has a grayscale path that skips all of this:

void draw(uint8_t *buf) {
    matrixGrayscaleWrite(buf);   // sends full 8-bit values directly to hardware
}

You need to patch endDraw() and endText() to use matrixGrayscaleWrite instead of renderBitmap:

void endText(int scrollDirection = NO_SCROLL) {
    ArduinoGraphics::endText(scrollDirection);
    // renderBitmap(_canvasBuffer, canvasHeight, canvasWidth);  // ← REMOVE
    matrixGrayscaleWrite(&_canvasBuffer[0][0]);                  // ← USE THIS
}

void endDraw() {
    ArduinoGraphics::endDraw();
    if (!captureAnimation) {
        // renderBitmap(_canvasBuffer, canvasHeight, canvasWidth);  // ← REMOVE
        matrixGrayscaleWrite(&_canvasBuffer[0][0]);                  // ← USE THIS
    } else {
        // ... animation capture path unchanged ...
    }
}

--
These changes are in the same file as the 1st issue so I made these 2 changes and now all is working perfectly and the scrolling text is now as bright or as dim as I would like. Example sketch code:

#include <Arduino_RouterBridge.h>
#include <ArduinoGraphics.h>
#include <Arduino_LED_Matrix.h>

ArduinoLEDMatrix matrix;
int i = 0;

void setup() {
  pinMode(LED_BUILTIN, OUTPUT);
  matrix.begin();
  matrix.setGrayscaleBits(8);
  matrix.stroke(63, 63, 63); // 25% brightness - much nicer!
  matrix.textFont(Font_4x6);
  matrix.textScrollSpeed(100);
  matrix.clear();
  //Serial.begin(115200);
}
void loop() {
  digitalWrite(LED_BUILTIN, (i++ & 1) ? HIGH : LOW);
  if(i > 1998)
    i = 0;
  matrix.beginDraw();
  matrix.clear();
  matrix.text(String(i/2), 0, 1);
  matrix.endDraw();
  delay(500);
}

What about an RPC call to exchange data between MPU and MCU?

Yes I have some example code that does that, just need to make the system call to get the text and RPC the display function, my background is in PHP & C but probably in ~1 hr of work I should have it working in python, might take a bit of fine tuning on the thread timing if scrolling is involved. But if there was already an example like this it might be even easier.

Hi @davidgsd.

Unfortunately, no. As mentioned in a somewhat roundabout manner by @rei_studio, the best reference will be the source code:

Note that the Arduino_LED_Matrix library has support for inheriting from the ArduinoGraphics library to give it more capabilities. There is an API reference for that library:

https://github.com/arduino-libraries/ArduinoGraphics/blob/1.1.5/docs/api.md

Here you go:

matrixprint.zip (2.8 KB)

The ZIP file can be imported to Arduino App Lab.

I'll also share the source code to make it conveniently visible to readers of this forum topic:

python/main.py:

# Offered for use under the CC0 1.0 Universal license:
# https://creativecommons.org/publicdomain/zero/1.0/

# See: https://docs.python.org/3/library/time.html
import time

from arduino.app_utils import App, Bridge


def loop():
    # See: https://docs.arduino.cc/software/app-lab/bridge/bridge-api/#notifystring-args
    Bridge.notify("matrix_print", "Hello, world!")

    # See: https://docs.python.org/3/library/time.html#time.sleep
    time.sleep(10)


# See: https://docs.arduino.cc/software/app-lab/apps/develop-apps/#apprun
App.run(user_loop=loop)

sketch/sketch.ino:

/*
  Offered for use under the CC0 1.0 Universal license: 
  https://creativecommons.org/publicdomain/zero/1.0/
*/

// In order to use the functions of the ArduinoGraphics library with the matrix, its #include directive must be placed
// before that of the Arduino_LED_Matrix library
#include <ArduinoGraphics.h>
#include <Arduino_LED_Matrix.h>

Arduino_LED_Matrix matrix;

void setup() {
  // See: https://docs.arduino.cc/software/app-lab/bridge/bridge-api/#begin
  Bridge.begin();
  // See: https://docs.arduino.cc/software/app-lab/bridge/bridge-api/#provide_safestring-function
  Bridge.provide_safe("matrix_print", matrixPrint);

  // See: https://github.com/arduino/ArduinoCore-zephyr/blob/1.0.0/libraries/Arduino_LED_Matrix/src/Arduino_LED_Matrix.h#L66
  matrix.begin();
  // See: https://github.com/arduino-libraries/ArduinoGraphics/blob/1.1.5/docs/api.md#textfont
  matrix.textFont(Font_4x6);
  // See: https://github.com/arduino-libraries/ArduinoGraphics/blob/1.1.5/docs/api.md#textscrollspeed
  matrix.textScrollSpeed(150);
}

void loop() {}

void matrixPrint(String text) {
  // See: https://github.com/arduino-libraries/ArduinoGraphics/blob/1.1.5/docs/api.md#begindraw
  matrix.beginDraw();
  // See: https://github.com/arduino-libraries/ArduinoGraphics/blob/1.1.5/docs/api.md#begintext
  // `beginText` parameters are `x, y, r, g, b`. With a monochrome matrix, the concept of RGB is not applicable, thus
  // all 0 values for `r, g, b` means "pixel off" and any non-zero values of these parameters means "pixel on".
  matrix.beginText(0, 0, 1, 0, 0);
  // See: https://github.com/arduino/ArduinoCore-API/blob/1.5.2/api/Print.cpp#L59
  matrix.print(text);
  // See: https://github.com/arduino-libraries/ArduinoGraphics/blob/1.1.5/docs/api.md#endtext
  matrix.endText(SCROLL_LEFT);
  // See: https://github.com/arduino-libraries/ArduinoGraphics/blob/1.1.5/docs/api.md#enddraw
  matrix.endDraw();
}

I downloaded and imported the ZIP file correctly into App Lab; it is running alright!