MAX7219 Message Selector

This may be a stupid question, but since I am quite new to programming anything outside of 1980's BASIC, I am having trouble locating what I need to know.

Basically, I am building a simple LED scrolling text display using an MAX7219 4-in-1 matrix and a Nano. That part is easy, and there is a ton of such projects out there to help you with the code needed for displaying a single message.

My problem is, I want to know how I would code the ability to select the message being displayed, with the messages being pulled from a preset bank, each time a momentary switch is pressed. So, it goes high, move to next message in the list. Reach the end of the list, go back to the first message. Repeat.

You can enter custom messages via serial, Bluetooth, etc. in many examples, but I do not want this, I want to simply have selectable messages contained within the code. No outside connections.

I know it's possible, but have no idea how, I really want to understand how to do this so I can learn as I go. Sadly, with my limited time, it's turning out to be a slower process than I had wished.

Thanks in advance for any help!

Do you have a library in mind ?
What about the libraries from: https://github.com/MajicDesigns ?
You can see it here in a Wokwi simulation: https://wokwi.com/arduino/projects/289186888566178317.

To check if a button is pressed is the State Change Detection example.

You could make an array of pointers to text and select one of them with the button.

Are you using the Parola library?

Please provide more information on your setup. What Arduino?

MAX7219 4-in-1 matrix.

What matrix modules (data sheet, link to where you got them)? What Bluetooth (classic, BLE)? Serial from where, a PC?

What have you done so far? Post your code (in code tags), describe what it does and what it should be doing.

I have code for a setup of 8 FC16 matrix module that can be controlled by classic Bluetooth and using the Parola libray that may be adapted to do what you want. I will look for it.

Sorry, yes, I am using the following libraries.

MD_Parola.h
MD_MAX72xx.h

Module: https://www.amazon.com/gp/product/B07FFV537V/

I am following a tutorial from: https://www.makerguides.com/max7219-led-dot-matrix-display-arduino-tutorial/

I have their code example up and running, reading through it so I understand whats going on. Messing with parameters. Testing.

// Include the required Arduino libraries:
#include <MD_Parola.h>
#include <MD_MAX72xx.h>
#include <SPI.h>

// Define hardware type, size, and output pins:
#define HARDWARE_TYPE MD_MAX72XX::FC16_HW
#define MAX_DEVICES 4
#define CS_PIN 3

// Create a new instance of the MD_Parola class with hardware SPI connection:
// MD_Parola myDisplay = MD_Parola(HARDWARE_TYPE, CS_PIN, MAX_DEVICES);

// Setup for software SPI:
#define DATA_PIN 4
#define CLK_PIN 5
MD_Parola myDisplay = MD_Parola(HARDWARE_TYPE, DATA_PIN, CLK_PIN, CS_PIN, MAX_DEVICES);

void setup() {
  // Intialize the object:
  myDisplay.begin();
  // Set the intensity (brightness) of the display (0-15):
  myDisplay.setIntensity(0);
  // Clear the display:
  myDisplay.displayClear();
  myDisplay.displayText("TEST MESSAGE - It works. !! @@ $$", PA_CENTER, 100, 0, PA_SCROLL_LEFT, PA_SCROLL_LEFT);
}

void loop() {
  if (myDisplay.displayAnimate()) {
    myDisplay.displayReset();
  }
}

I am testing on a Mega 2560, but will move to a Nano on the final version.

IMG_20210530_122626675

I am not using any serial or BT connection for entering the messages. I edited my main post to be more clear, my bad. I am not wanting to use any external connection, just selectable messaged contained in the code that you cycle through using a momentary switch.

Hope that helps.

Next step is the button. There are some basic examples that come with the IDE that will help read it.

Reading the button press I can do, using something like the following.

const int pinButton = 2;

void setup() {
  pinMode(pinButton, INPUT);
  Serial.begin(9600);
}

Output to the serial monitor to verify it's working using?

void loop() {
  int stateButton = digitalRead(pinButton);
  Serial.println(stateButton);
}

I just have no clue how to get that to change to the next text block to display instead.

I know what I want, just not how. Press button, and that tells the code to input the next value from a list into the "displayText" parameter.

I will get this down, ... eventually. :stuck_out_tongue:

I think I already gave the right links and some explanation.
The button bouncing is turned of this Wokwi project, to make it work. Perhaps you need to add a debounce library.

1 Like

When the button is pressed, increment a global variable. Then have a series of if statements that check it and send a message to the display. Or use an array.

Here is a quick example that shows, I think, the points that @ Koepel talked of in reply #2. Using pointer to a string in a array of pointers to select a message to display and the state change detection method for detecting and counting button presses.

// Program to demonstrate the MD_Parola library
// button select canned messages
// MD_MAX72XX library can be found at https://github.com/MajicDesigns/MD_MAX72XX
// by groundFungus AKA c. goulding

#include <MD_Parola.h>
#include <MD_MAX72xx.h>
#include <SPI.h>

const byte  buttonPin = 7;    // the pin that the pushbutton is attached to

// Define the number of devices we have in the chain and the hardware interface
// NOTE: These pin numbers will probably not work with your hardware and may
// need to be adapted
#define HARDWARE_TYPE MD_MAX72XX::FC16_HW
#define MAX_DEVICES 8

#define CLK_PIN   13
#define DATA_PIN  11
#define CS_PIN    10

MD_Parola P = MD_Parola(HARDWARE_TYPE, CS_PIN, MAX_DEVICES);

// individual messages in strings
const char msg_1[] = "it is time";
const char msg_2[] = "all good men";
const char msg_3[] = "to come to";
const char msg_4[] = "the aid of";
const char msg_5[] = "their";
const char msg_6[] = "country";

// an array of pointers to the strings
char *messages[] = {msg_1, msg_2, msg_3, msg_4, msg_5, msg_6};
byte messageNum = sizeof(messages) / sizeof(messages[0]);

int buttonPushCounter = 0;   // counter for the number of button presses
int buttonState = 0;         // current state of the button
int lastButtonState = 0;     // previous state of the button

void setup(void)
{
   Serial.begin(115200);
   Serial.println("\nParola pick a message program\n");
   P.begin();
   pinMode(buttonPin, INPUT_PULLUP);
}

void loop(void)
{
   if (P.displayAnimate())  // time to show next frame?
   {
      P.displayText(messages[buttonPushCounter], PA_CENTER, 200, 3000, PA_SCROLL_DOWN, PA_SCROLL_UP);
   }
   checkButton();
}

void checkButton()
{

   static unsigned long timer = 0;
   unsigned long interval = 50;
   if (millis() - timer >= interval)
   {
      timer = millis();
      buttonState = digitalRead(buttonPin);
      // compare the buttonState to its previous state
      if (buttonState != lastButtonState)
      {
         if (buttonState == LOW)
         {
            // if the current state is LOW then the button
            // went from off to on:
            buttonPushCounter++;  // add one to counter
            // if counter over number of messages, reset the counter to message 0
            if (buttonPushCounter >= messageNum)
            {
               buttonPushCounter = 0;
            }
            //Serial.println(buttonPushCounter);
         }
      }
      lastButtonState = buttonState;
   }
}

You may need to add button debounce. This is tested and works OK with my Uno and 8 FC16 8x8 matrix modules.

1 Like

That works perfectly! Thank you!

So the button state is telling it to move to the next line (pMessages) and display it.

My only questions are, what is (index) doing and is the (NUM_MESSAGES) so it knows when to start over?

1 Like

Very nice!

Two examples to work with!

I can't thank you enough. This is perfect since it allows me to see different ways of getting to the same end result. It also allows me to see what's easier for me. I am one of those people who prefer code that's "readable", easier to understand and follow what it's doing. Using remarks is fine, and they help out immensely, but being able to read the code is better in my opinion.

I will get there. :slight_smile:

@groundFungus uses byte messageNum = sizeof(messages) / sizeof(messages[0]); and I use #define NUM_MESSAGES 2.

Those are the same things. It is the total number of messages. The one that groundFungus uses asks the compiler how many elements there are in the array.

We both use the State Change Detection, that I linked to earlier.

There are a few minor differences. I have never used a MAX7219 display so I don't know what I'm doing, and it seems that groundFungus has it laying on his table.

Can you make a good sketch and show it to us ?

Messing around with this and doing other things. Multitasking! :stuck_out_tongue:

Right now, I am messing with your code...

#include <MD_Parola.h>
#include <MD_MAX72xx.h>
#include <SPI.h>

// Define hardware type, size, and output pins:
#define HARDWARE_TYPE MD_MAX72XX::FC16_HW   // For real project
//#define HARDWARE_TYPE MD_MAX72XX::PAROLA_HW   // For Wokwi

#define MAX_DEVICES 4
#define CS_PIN 3

// Create a new instance of the MD_Parola class with hardware SPI connection:
// MD_Parola myDisplay = MD_Parola(HARDWARE_TYPE, CS_PIN, MAX_DEVICES);

// Setup for software SPI:
#define DATA_PIN 4
#define CLK_PIN 5
MD_Parola myDisplay = MD_Parola(HARDWARE_TYPE, DATA_PIN, CLK_PIN, CS_PIN, MAX_DEVICES);

const int pinButton = 2;
int lastButtonState = HIGH;  // HIGH is button not pressed
int index = 0;
#define NUM_MESSAGES 3
char *pMessages[NUM_MESSAGES] =
{
  "Hello World!",
  "It Works!",
  "Thank you for the help!",
};

void setup() {
  Serial.begin( 9600);
  Serial.println( "The sketch has started");
  pinMode( pinButton, INPUT_PULLUP);

  // Intialize the object:
  myDisplay.begin();
  // Set the intensity (brightness) of the display (0-15):
  myDisplay.setIntensity(10);
  // Clear the display:
  myDisplay.displayClear();
  myDisplay.displayText("Press button to start cycleing messages.", PA_CENTER, 20, 0, PA_SCROLL_LEFT, PA_SCROLL_LEFT);
}

void loop() {
  
  int buttonState = digitalRead( pinButton);
  if( buttonState != lastButtonState)
  {
    if( buttonState == HIGH)
    {
      index++;
      if( index >= NUM_MESSAGES)
      {
        index = 0;
      }
      Serial.println( pMessages[index]);
      myDisplay.displayClear();
      myDisplay.displayText( pMessages[index], PA_CENTER, 50, 0, PA_SCROLL_LEFT, PA_SCROLL_LEFT);
    }
    lastButtonState = buttonState;
  }

  if (myDisplay.displayAnimate()) {
    myDisplay.displayReset();
  }
}

It yields this result.

I had to change yours a little to work with my setup, but it works great as well!

Would work better if I had more than one 4 unit display. I do plan on picking up a few more, but I wanted to make sure I have a grasp of what I am doing first. lol

// Program to demonstrate the MD_Parola library
// button select canned messages
// MD_MAX72XX library can be found at https://github.com/MajicDesigns/MD_MAX72XX
// by groundFungus AKA c. goulding

#include <MD_Parola.h>
#include <MD_MAX72xx.h>
#include <SPI.h>

const byte  buttonPin = 2;    // the pin that the pushbutton is attached to

// Define the number of devices we have in the chain and the hardware interface
// NOTE: These pin numbers will probably not work with your hardware and may
// need to be adapted
#define HARDWARE_TYPE MD_MAX72XX::FC16_HW
#define MAX_DEVICES 4

#define CLK_PIN   5
#define DATA_PIN  4
#define CS_PIN    3

MD_Parola P = MD_Parola(HARDWARE_TYPE, DATA_PIN, CLK_PIN, CS_PIN, MAX_DEVICES);

// individual messages in strings
const char msg_1[] = "it is time";
const char msg_2[] = "all good men";
const char msg_3[] = "to come to";
const char msg_4[] = "the aid of";
const char msg_5[] = "their";
const char msg_6[] = "country";

// an array of pointers to the strings
char *messages[] = {msg_1, msg_2, msg_3, msg_4, msg_5, msg_6};
byte messageNum = sizeof(messages) / sizeof(messages[0]);

int buttonPushCounter = 0;   // counter for the number of button presses
int buttonState = 0;         // current state of the button
int lastButtonState = 0;     // previous state of the button

void setup(void)
{
   Serial.begin(115200);
   Serial.println("\nParola pick a message program\n");
   P.begin();
   pinMode(buttonPin, INPUT_PULLUP);
}

void loop(void)
{
   if (P.displayAnimate())  // time to show next frame?
   {
      P.displayText(messages[buttonPushCounter], PA_CENTER, 30, 0, PA_SCROLL_LEFT, PA_SCROLL_LEFT);
   }
   checkButton();
}

void checkButton()
{

   static unsigned long timer = 0;
   unsigned long interval = 50;
   if (millis() - timer >= interval)
   {
      timer = millis();
      buttonState = digitalRead(buttonPin);
      // compare the buttonState to its previous state
      if (buttonState != lastButtonState)
      {
         if (buttonState == LOW)
         {
            // if the current state is LOW then the button
            // went from off to on:
            buttonPushCounter++;  // add one to counter
            // if counter over number of messages, reset the counter to message 0
            if (buttonPushCounter >= messageNum)
            {
               buttonPushCounter = 0;
            }
            //Serial.println(buttonPushCounter);
         }
      }
      lastButtonState = buttonState;
   }
}

It results in ...
arduinosketchhelp2

That Parola library is really fun. All of the effects and sprites. I have just scratched the surface.

Once you get beyond 4 modules you should think about using external power for the matrix modules. Uno will struggle with 8 fully lit.

Yeah, I am having a blast with it.

I had planned on, eventually, working on a larger similar array that I could use to display basic pixel art. That will of course, require it's own separate power. For now though, I want to understand the basics before I try and dive too far into coding something like that. There are some good examples out there to work with, I will likely follow one of those, but I want to know what I'm doing first.

Electronics, not a problem, I built more projects than I can remember, back when I had the time, about 20 years ago. I am picking it back up now as a hobby. I find it relaxing. I just wish we had Arduino 20 years ago. lol

Again, thank you so much for the assistance! My wife thinks I am crazy, but this is my idea of fun.

:slight_smile:

Been doing electronics and physical computing for decades as a hobby. And you are not alone in the crazy place.