An Arduino Microcontroller Graphics Library For LED Dot Matrix Displays
Depends on: MD_MAX_72XX
The MD_MAX_72XX library provides a system for addressing the "pixels" on the matrix individually.
The library implements functions that allow the MAX72xx to be used for LED matrices (64 individual LEDs), allowing the programmer to use the LED matrix as a pixel device, displaying graphics elements much like any other pixel addressable display.
- MAX72xx LED Matrix Display Library README
Note: The MD_MAX_72XX
library treats additional 8x8 matrices as adding to the Y axis. This library prefers treating them as the X axis. So the axes are inverted relative to the MD_MAX_72XX
library.
- Frame Based Animation
- Sprite Drawing
- Line Drawing
- Path Drawing
- Character Drawing (Numbers work but letters not implemented yet)
Line drawing is preformed by a custom digital differential analyzer (DDA) implementation.
This project creates an old retro-looking photo-frame with a Conway's Game of Life simulation running in it. It's designed to hang on the wall, and to work continuously without stabilising. To prevent the simulation stabilising, the frankenstein switch can be thrown to spark new life into the display - zzzap!
A WIFI Clock + Temperature/Humidity System for ESP32 Dev Boards.
Displays on a 32x8 "pixel" LED Matrix.
MatrixGL(int CLK_PIN, int DATA_PIN, int CS_PIN, int MAX_DEVICES=1, int MDX=NULL, int MDY=NULL);
Example:
#include <MatrixGL.h>
#define CS_PIN 10 // The CS Pin for your device, may need changing
#define CLK_PIN 18 // The SCK Pin for your device, may need changing
#define DATA_PIN 23 // The MOSI Pin for your device, may need changing
#define MAX_DEVICES 4 // How many matrix Blocks are connected?
MatrixGL matrix(CLK_PIN,DATA_PIN,CS_PIN,MAX_DEVICES);
void loop(){
// Draw some line segments
matrix.delayF();// Delay based on the desired frame rate
matrix.clear();// Clear the display
matrix.drawLine(0,0,4,4);// Draw a line from 0,0 to 4,4
matrix.delayF();// Delay based on the desired frame rate
matrix.clear();// Clear the display
matrix.drawLine(4,4,8,8);// Draw a line from 4,4 to 8,8
// Draw a path
int points[] = {0,0, 4,4, 7,6, 8,0};// Defines the points on the path
matrix.lock();// Lock the display (not required)
matrix.clear();// Clear the display
matrix.drawPath(points, 8);// Draw a path from the given points
matrix.unlock();// Unlock the display (not required)
}
By default, this library assumes that any additional matrices that you include will be added to the X axis.
If desired, you can add optional arguments (MDX & MDY) to the library instantiation that allow you to define the amount of displays on your X and Y axes.
Wiring Note: You will need to wire the matrices in order from bottom left (being the first) to top right (being the last).
Once you have it setup like that you can go ahead and start setting points that have a Y value over 7 and the library will automatically handle the addressing abstraction.
So if you had a 2x2 display setup you could draw 2 points like:
matrix.drawPoint(0,0,true);
matrix.drawPoint(15,15,true);
This will draw a point in the bottom left corner and another in the top right. The first point is drawn on the first display and the last point is drawn on last display in order of how they are wired.
NOTE: At this time, support for custom display configurations (additional matrices on the Y axis) is an experimental feature and may not play well with other features like sprite/character drawing. Please feel free to post an issue if you encounter any.
See the 4x2 example for more info.
- This library uses zero based numbering to describe coordinates.
- Unlike many graphics libraries, this one does not start counting the Y axis from the top. So point 0,0 is the bottom left corner (as you would expect on a graphing calculator).
- (Almost) Nothing will stop you from trying to draw outside the bounds of your matrix. Sometimes this results in overflow and sometimes wrap-around. It's up to you to avoid this.
void drawPoint(int x, int y, bool on=true);
Turn on an LED at point x,y.
Setting on
to false will turn off the LED at that point.
void drawLine(int x1, int y1, int x2, int y2, bool on=true);
Draw a line from the point x1,y1
to the point x2,y2
.
Setting on
to false will turn off the LEDs along the line.
Example:
matrix.drawLine(0, 0, 6, 8);
void drawPath(int points[], int pointsLen, bool on=true);
Draw a path based on the given array of point coordinates.
Setting on
to false will turn off the LEDs along the path.
Example:
int points[] = {0,0, 4,4, 7,6, 0,0};
matrix.drawPath(points, 8);
Note: The length of the points
array should always be an even number. This is not currently enforced.
void drawSprite(bool sprite[],int w, int h, int x, int y, inverted=false);
Draw a sprite with width w
and heigh h
at the point x,y
.
- sprite - contains an array of boolean pixel values
- w - The width (x length) of the sprite
- h - The width (y length) of the sprite
- x - The x drawing offset
- y - The y drawing offset
- inverted - Invert the "pixel values"
*Note: The x and y offsets position the bottom "left" pixel of the sprite.
Example:
matrix.drawSprite(sprite1, 6,6, 0,0);
Defining a sprite works like this:
const bool sprite[] PROGMEM = {
0,0,1,1,0,0,
0,0,1,1,0,0,
1,1,1,1,1,1,
0,1,0,0,1,0,
0,1,1,1,1,0,
0,1,0,0,1,0
};
You can more or less think of the sprite data as being a direct representation of the pixel matrix (assuming you lay it out in the same manner, which you don't have to). As you might have assumed, a 1 represents an active "pixel" (LED ON) and and a 0 represents an inactive "pixel" (LED OFF).
Drawing a series of sprites (an animation) might look like:
matrix.clear();
matrix.drawSprite(sprite1, 6,6, 1,1);
matrix.delayF();
matrix.clear();
matrix.drawSprite(sprite2, 6,6, 1,1);
matrix.delayF();
matrix.clear();
matrix.drawSprite(sprite3, 6,6, 1,1);
matrix.delayF();
Also notice that the array is being stored in program memory by the use of PROGMEM
More Info. This is because sprite/frame data is much too large to store in RAM. While it would be technically possible to get away with storing a few frames/sprites of data in RAM, the drawSprite()
method expects an array stored in program memory and will not work with an array stored in dynamic memory. Due to the fact that arrays MUST be stored in program memory, they are inherently immutable.
void drawFrame(bool frame[], bool clearFirst, bool inverted=false)
Drawing a frame works in much the same way as drawing a sprite but we can assume that the frame will take up the entire display. Because of this we do not need to provide an x or y value and we also don't need to describe the width or height.
- frame - contains an array of boolean pixel values with a length equal to the area of your display (in "pixels")
- clearFirst - should the previous display buffer be cleared before drawing?
- inverted - Invert the "pixel values"
Defining a frame works like this:
const bool frameX[256] PROGMEM = {
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,1,
0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,
0,0,1,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,1,0,0,0,0,0,0,1,0,0,1,1,1,
0,1,1,1,1,0,0,0,0,1,1,1,1,0,0,0,0,0,1,1,1,0,1,0,0,1,1,1,1,1,1,1,
1,1,1,1,1,1,0,0,1,1,1,1,1,1,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1
};
Drawing the frame might look like this:
matrix.drawFrame(frameX,true);
Drawing a series of frames (an animation) might look like:
matrix.drawFrame(frame1,true);
matrix.delayF();
matrix.drawFrame(frame2,true);
matrix.delayF();
matrix.drawFrame(frame3,true);
matrix.delayF();
Once again, you can think of the frame data as being a direct representation of the pixel matrix (assuming you lay it out in the same manner).
The first value represents the "pixel" 0,7 (This library uses zero-based counting). The second value represents pixel 1,7 and so on. The last value represents 32,0.
While you do not have to write out your array to match the columns and rows of your screen, the array length must be equal to the area of your display.
So, if your display is 32x8 your array MUST have a length of 256. If your display is 8x8 your array MUST have length of 16.
void drawChar(char c, int x, int y, inverted=false);
- c - Character to draw (A->Z)
- x - X position to draw the number at
- y - y position to draw the number at
- inverted - Invert the "pixel values"
You can draw any capital (uppercase) letter between A and Z. You can also draw a colon.
Example:
drawChar('A', 1, 1);
Characters are 4 "pixels" wide so if you want to space them out appropriately you should but a horizontal space of at least 5 "pixels" between each character.
Example:
drawChar('A', 1, 1);
drawChar('B', 6, 1);
drawChar('C', 11, 1);
void drawNum(int n, int x, int y, inverted=false);
- n - Number to draw (0->9)
- x - X position to draw the number at
- y - y position to draw the number at
- inverted - Invert the "pixel values"
This works much like drawing a sprite. Just provide a number between 0 and 9 and tell it where to draw at.
Note: At this time all built-in number sprites are 4 "pixels" wide and 6 "pixels" high.
void drawString(String str, int length=6);
Draw a string on the display. The length can be greater than your actual string length.
Note: At this time the string will be left justified. You can pad with spaces at the beginning if needed.
void setIntensity(int intensity);
The value of intensity
should be 0 through 15.
Note: The default value is 0 (zero).
void setFrameRate(int fr);
The argument given for fr
is the frame rate in 'frames per second'.
void delayF();
Used to delay from some time between drawing of frames or other items. The time delayed is based on the frame rate set with setFrameRate()
.
void delayN(int n);
Used to delay for n
frames.
void clear();
Removes everything from the display.
void lock();
When preforming a long series of draw operations you might wish to lock the display so that the updates are written to the display buffer but not displayed on the device.
To use this method effectively you need to lock the display, then clear it, write to the display, then unlock it.
Consider this example:
void randomNoise(){
int X = matrix.lenX();
int Y = matrix.lenY();
matrix.lock();
matrix.clear();
for(int i = 0; i<(X*Y); i++){
matrix.mx->setPoint(random(Y), random(X), true);
}
matrix.unlock();
matrix.delayF();
}
Without using the lock()
method, you will probably have issues with the display flickering.
void unlock();
Unlock the display after locking it.
int lenX();
Returns the X length of the display
int lenY();
Returns the Y length of the display
If you wish to access the underlying MD_MAX72xx
library directly, you can use the pointer mx
.
Example:
#include <MatrixGL.h>
#define CS_PIN 10
#define MAX_DEVICES 4
#define LENGTHX 32
#define LENGTHY 8
#define INTENSITY 0
MatrixGL matrix(CS_PIN,MAX_DEVICES, LENGTHX, LENGTHY, INTENSITY);
void loop(){
matrix.mx->setPoint(1, 1, true);
}