Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
186 changes: 186 additions & 0 deletions examples/MarqueeTextProgmem/MarqueeTextProgmem.ino
Original file line number Diff line number Diff line change
@@ -0,0 +1,186 @@
#include <avr/pgmspace.h>
#include <LEDMatrixDriver.hpp>

// This sketch will draw marquee text on your LED matrix using the hardware SPI driver Library by Bartosz Bielawski.
// Example written 16.06.2017 by Marko Oette, www.oette.info
// Example PROGMEM variant written 09.2025 by Bryn Mosher.

// This particular version was altered to support putting font data in PROGMEM to save space in our "dynamic memory"
// (SRAM) for other variables. Large arrays are ideal for PROGMEM storage. For more details, see
// https://arduinogetstarted.com/reference/arduino-progmem and
// https://docs.arduino.cc/language-reference/en/variables/utilities/PROGMEM/
//
// As of this writing, the memory used for an Arduino Micro to run the normal version of this example
// reports after compile:
//
// Sketch uses 6370 bytes (22%) of program storage space. Maximum is 28672 bytes.
// Global variables use 1017 bytes (39%) of dynamic memory, leaving 1543 bytes for local variables. Maximum is 2560 bytes.
//
// After the PROGMEM alteration, this sketch's compilation reports:
//
// Sketch uses 6428 bytes (22%) of program storage space. Maximum is 28672 bytes.
// Global variables use 257 bytes (10%) of dynamic memory, leaving 2303 bytes for local variables. Maximum is 2560 bytes.
//
// As you can see the program memory usage went up some, but the scarce dynamic memory use dropped by over
// two thirds! When there's only 2560 bytes total for variables, freeing up 758 bytes makes a big difference!

// Define the ChipSelect pin for the led matrix (Dont use the SS or MISO pin of your Arduino!)
// Other pins are Arduino specific SPI pins (MOSI=DIN, SCK=CLK of the LEDMatrix) see https://www.arduino.cc/en/Reference/SPI
const uint8_t LEDMATRIX_CS_PIN = 9;

// Number of 8x8 segments you are connecting
const int LEDMATRIX_SEGMENTS = 4;
const int LEDMATRIX_WIDTH = LEDMATRIX_SEGMENTS * 8;

// The LEDMatrixDriver class instance
LEDMatrixDriver lmd(LEDMATRIX_SEGMENTS, LEDMATRIX_CS_PIN);

// Marquee text
char text[] = "** LED MATRIX DEMO! ** (1234567890) ++ \"ABCDEFGHIJKLMNOPQRSTUVWXYZ\" ++ <$%/=?'.@,> --";

// Marquee speed (lower nubmers = faster)
const int ANIM_DELAY = 30;

// Only un-comment one of the lines below
#define L2R //Left to right scroll
//#define R2L //Right to left scroll

void setup()
{
// init the display
lmd.setEnabled(true);
lmd.setIntensity(2); // 0 = low, 10 = high
}

int x=0, y=0; // start top left

// To store our font array in PROGMEM, we need to declare it as a constant.
const byte fontMem[95][8] PROGMEM = {
// This is the font definition. You can use http://gurgleapps.com/tools/matrix to create your own font or sprites.
// If you like the font feel free to use it. I created it myself and donate it to the public domain.
{0,0,0,0,0,0,0,0}, // SPACE
{0x10,0x18,0x18,0x18,0x18,0x00,0x18,0x18}, // EXCL
{0x28,0x28,0x08,0x00,0x00,0x00,0x00,0x00}, // QUOT
{0x00,0x0a,0x7f,0x14,0x28,0xfe,0x50,0x00}, // #
{0x10,0x38,0x54,0x70,0x1c,0x54,0x38,0x10}, // $
{0x00,0x60,0x66,0x08,0x10,0x66,0x06,0x00}, // %
{0,0,0,0,0,0,0,0}, // &
{0x00,0x10,0x18,0x18,0x08,0x00,0x00,0x00}, // '
{0x02,0x04,0x08,0x08,0x08,0x08,0x08,0x04}, // (
{0x40,0x20,0x10,0x10,0x10,0x10,0x10,0x20}, // )
{0x00,0x10,0x54,0x38,0x10,0x38,0x54,0x10}, // *
{0x00,0x08,0x08,0x08,0x7f,0x08,0x08,0x08}, // +
{0x00,0x00,0x00,0x00,0x00,0x18,0x18,0x08}, // COMMA
{0x00,0x00,0x00,0x00,0x7e,0x00,0x00,0x00}, // -
{0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x06}, // DOT
{0x00,0x04,0x04,0x08,0x10,0x20,0x40,0x40}, // /
{0x00,0x38,0x44,0x4c,0x54,0x64,0x44,0x38}, // 0
{0x04,0x0c,0x14,0x24,0x04,0x04,0x04,0x04}, // 1
{0x00,0x30,0x48,0x04,0x04,0x38,0x40,0x7c}, // 2
{0x00,0x38,0x04,0x04,0x18,0x04,0x44,0x38}, // 3
{0x00,0x04,0x0c,0x14,0x24,0x7e,0x04,0x04}, // 4
{0x00,0x7c,0x40,0x40,0x78,0x04,0x04,0x38}, // 5
{0x00,0x38,0x40,0x40,0x78,0x44,0x44,0x38}, // 6
{0x00,0x7c,0x04,0x04,0x08,0x08,0x10,0x10}, // 7
{0x00,0x3c,0x44,0x44,0x38,0x44,0x44,0x78}, // 8
{0x00,0x38,0x44,0x44,0x3c,0x04,0x04,0x78}, // 9
{0x00,0x18,0x18,0x00,0x00,0x18,0x18,0x00}, // :
{0x00,0x18,0x18,0x00,0x00,0x18,0x18,0x08}, // ;
{0x00,0x10,0x20,0x40,0x80,0x40,0x20,0x10}, // <
{0x00,0x00,0x7e,0x00,0x00,0xfc,0x00,0x00}, // =
{0x00,0x08,0x04,0x02,0x01,0x02,0x04,0x08}, // >
{0x00,0x38,0x44,0x04,0x08,0x10,0x00,0x10}, // ?
{0x00,0x30,0x48,0xba,0xba,0x84,0x78,0x00}, // @
{0x00,0x1c,0x22,0x42,0x42,0x7e,0x42,0x42}, // A
{0x00,0x78,0x44,0x44,0x78,0x44,0x44,0x7c}, // B
{0x00,0x3c,0x44,0x40,0x40,0x40,0x44,0x7c}, // C
{0x00,0x7c,0x42,0x42,0x42,0x42,0x44,0x78}, // D
{0x00,0x78,0x40,0x40,0x70,0x40,0x40,0x7c}, // E
{0x00,0x7c,0x40,0x40,0x78,0x40,0x40,0x40}, // F
{0x00,0x3c,0x40,0x40,0x5c,0x44,0x44,0x78}, // G
{0x00,0x42,0x42,0x42,0x7e,0x42,0x42,0x42}, // H
{0x00,0x7c,0x10,0x10,0x10,0x10,0x10,0x7e}, // I
{0x00,0x7e,0x02,0x02,0x02,0x02,0x04,0x38}, // J
{0x00,0x44,0x48,0x50,0x60,0x50,0x48,0x44}, // K
{0x00,0x40,0x40,0x40,0x40,0x40,0x40,0x7c}, // L
{0x00,0x82,0xc6,0xaa,0x92,0x82,0x82,0x82}, // M
{0x00,0x42,0x42,0x62,0x52,0x4a,0x46,0x42}, // N
{0x00,0x3c,0x42,0x42,0x42,0x42,0x44,0x38}, // O
{0x00,0x78,0x44,0x44,0x48,0x70,0x40,0x40}, // P
{0x00,0x3c,0x42,0x42,0x52,0x4a,0x44,0x3a}, // Q
{0x00,0x78,0x44,0x44,0x78,0x50,0x48,0x44}, // R
{0x00,0x38,0x40,0x40,0x38,0x04,0x04,0x78}, // S
{0x00,0x7e,0x90,0x10,0x10,0x10,0x10,0x10}, // T
{0x00,0x42,0x42,0x42,0x42,0x42,0x42,0x3e}, // U
{0x00,0x42,0x42,0x42,0x42,0x44,0x28,0x10}, // V
{0x80,0x82,0x82,0x92,0x92,0x92,0x94,0x78}, // W
{0x00,0x42,0x42,0x24,0x18,0x24,0x42,0x42}, // X
{0x00,0x44,0x44,0x28,0x10,0x10,0x10,0x10}, // Y
{0x00,0x7c,0x04,0x08,0x7c,0x20,0x40,0xfe}, // Z
// (the font does not contain any lower case letters. you can add your own.)
};

void loop()
{
// Draw the text to the current position
int len = strlen(text);
drawPMem(text, len, x, 0);
// In case you wonder why we don't have to call lmd.clear() in every loop: The font has a opaque (black) background...
// Toggle display of the new framebuffer
lmd.display();
// Wait to let the human read the display
delay(ANIM_DELAY);

#ifdef L2R
//For left to right scroll
if( ++x > LEDMATRIX_WIDTH ){
x = -len*8;
}
#endif
#ifdef R2L
//For right to left scroll
if( --x < len * -8 ) {
x = LEDMATRIX_WIDTH;
}
#endif
}

void drawPMem(const char* text, int len, int x, int y )
{
// The mask is used to get the column bit from the sprite row later on.
byte mask = B10000000;

for( int idx = 0; idx < len; idx ++ )
{
int c = text[idx] - 32;

// stop if char is outside visible area
if( x + idx * 8 > LEDMATRIX_WIDTH )
return;

// only draw if char is visible
if( 8 + x + idx * 8 > 0 )
{
for( int iy = 0; iy < 8; iy++ )
{
for( int ix = 0; ix < 8; ix++ )
{
// Add the width of our letter so far to our position.
int xdx = x + (idx * 8);
// We don't use fontMem directly. Instead, we use an Arduino helper to pull our desired
// value from PROGMEM. In our case, we want a pgm_read_byte_near(). The type of
// pgm_read_*() function is based on the type we set our array to. In our case, a
// single byte. This post heped understnd the "near" and "far" variations:
// https://forum.arduino.cc/t/using-multidimension-arrays-with-progmem/459824/7
lmd.setPixel(xdx + ix, y + iy, (bool)(pgm_read_byte_near(&fontMem[c][iy]) & mask ));

// shift the mask by one pixel to the right
mask = mask >> 1;
}

// reset column mask
mask = B10000000;
}
}
}
}