LED Matrix display with a switch button to turn on/off

Hi,
I am making a matrix display that can be turned on/off by a button switch, but I have been having a problem figuring out the code and schematics.
I think that it can be very simple - combining the basic LED switch code and my matrix display code into one, but it has been quite tricky since I am a beginner of electronics/Arduino.

I use 7219 chips and LedControll library. My code(without a switch button part) is below:

#include "LedControl.h"

LedControl lc=LedControl(12,11,10,2);  // Pins: DIN,CLK,CS, # of Display connected

unsigned long delayTime=200;  // Delay between Frames

// Put values in arrays

byte a[] =
{B00111100,B01000010,B01000010,B01000010,B01111110,B01000010,B01000010,B01000010};

byte invader1a[] =
{
  B00011000,  // First frame of invader #1
  B00111100,
  B01111110,
  B11011011,
  B11111111,
  B00100100,
  B01011010,
  B10100101
};

byte invader1b[] =
{
 B00011000, // Second frame of invader #1
 B00111100,
 B01111110,
 B11011011,
 B11111111,
 B00100100,
 B01011010,
 B01000010
};

byte b[] =   {B01111100,B01000010,B01000010,B01111100,B01000010,B01000010,B01000010,B01111100};

byte invader2a[] =
{
 B00100100, // First frame of invader #2
 B00100100,
 B01111110,
 B11011011,
 B11111111,
 B11111111,
 B10100101,
 B00100100
};

byte invader2b[] =
{
 B00100100, // Second frame of invader #2
 B10100101,
 B11111111,
 B11011011,
 B11111111,
 B01111110,
 B00100100,
 B01000010
};

void setup()
{
 lc.shutdown(0,false);  // Wake up displays
 lc.shutdown(1,false);
 lc.setIntensity(0,5);  // Set intensity levels
 lc.setIntensity(1,5);
 lc.clearDisplay(0);  // Clear Displays
 lc.clearDisplay(1);
}


//  Take values in Arrays and Display them

void sa()
{
 for (int i = 0; i < 8; i++)  
 {
   lc.setRow(0,i,a[i]);
 }
}

void sinvader1a()
{
 for (int i = 0; i < 8; i++)  
 {
   lc.setRow(0,i,invader1a[i]);
 }
}

void sinvader1b()
{
 for (int i = 0; i < 8; i++)
 {
   lc.setRow(0,i,invader1b[i]);
 }
}

void sb()
{
 for (int i = 0; i < 8; i++)
 {
   lc.setRow(1,i,b[i]);
 }
}

void sinvader2a()
{
 for (int i = 0; i < 8; i++)
 {
   lc.setRow(1,i,invader2a[i]);
 }
}
void sinvader2b()
{
for (int i = 0; i < 8; i++)
 {
   lc.setRow(1,i,invader2b[i]);
 }
}

void loop()
{
// Put #1 frame on both Display
   sa();
   sb();
   delay(delayTime);
   sinvader1a();
   sinvader2a();
   delay(delayTime);
   sinvader1b();
   sinvader2b();
   delay(delayTime);
}

If you know how to just add a touch button switch to turn on and off the display, I would very much appreciate your advice.

FBG6G4XHJ1XP58W.MEDIUM.jpg

Did you made it italic? Don't think so. Please edit your post to use code tags, see How to use the forum.

Sorry, I didn't read how to use the Forum... I just edited the code tag and also attached the picture of the circuit diagram.

Great job!

I see delays(), they will mess up any interaction from your side.

Instead of blocking the loop let it run freely and implement the delay like Blink without delay. And to select the different scenes use a state machine. (Aka, make a variable which holds which picture to draw and use that to look it up when it’s time to go to the next frame.)

Now you have to to check the switch in the loop as well.

And a small tip, use better names. Function names like sa() and sinvader2a() don’t tell what they do…

Also, all you functions look 99% the same… Then it’s time to think of a smarter way of doing it. Like:

void draw(byte &in[], byte address)){
  for (byte i = 0; i < 8; i++){
    lc.setRow(address, i, in[i]);
  }
}

//and call it like
draw(invader1a, 0);

And about the frames. You’re never going to change them so make them const. And the moment you start to add postfixes to variable names, put them in an array.

const byte invaders[2][2][8] =
{
  {
    {
      0b00011000,  // First frame of invader #1
      0b00111100,
      0b01111110,
      0b11011011,
      0b11111111,
      0b00100100,
      0b01011010,
      0b10100101
    },
    {
      0b00011000, // Second frame of invader #1
      0b00111100,
      0b01111110,
      0b11011011,
      0b11111111,
      0b00100100,
      0b01011010,
      0b01000010
    }
  },
  {
    {
      0b00100100, // First frame of invader #2
      0b00100100,
      0b01111110,
      0b11011011,
      0b11111111,
      0b11111111,
      0b10100101,
      0b00100100
    },
    {
      0b00100100, // Second frame of invader #2
      0b10100101,
      0b11111111,
      0b11011011,
      0b11111111,
      0b01111110,
      0b00100100,
      0b01000010
    }
  }
};

Now you can change draw to do both at once :smiley:

void draw(byte &in[])){
  for(byte j = 0; j < 2; j++){
    for (byte i = 0; i < 8; i++){
      lc.setRow(j, i, in[j][i]);
    }
  }
}

//call like
draw(invaders[0]);

Thank you so much for your quick reply and detailed advice. The code for the matrix display is very helpful as I was thinking if I could make the code shorter and more effective. I will try making a matrix with your effective code later :)

However, my question here is how to add a touch button to the code - this means an actual touch button to press for turning on/off the matrix, not the function as a switch only in the code. Sorry about my confusing explanation.

What I'm trying to make is a combination of a push button like this https://www.arduino.cc/en/Tutorial/Button) and the matrix code.

There are many examples of a pushbutton and some LEDs but could not find any example of a pushbutton and a LED matrix. If you know how to insert/add a touch button to the code, it will be very helpful.

Do you want to use a touch module / IC or use Capacitive Sensing Library?

To make it easy for now, let's drop the touch part and just focus on a normal button. We can extend it later on.

If you rewrite the code to have no delay() you have the time to check for a button press. I would recommend Bounce2 to do all the heavy lifting (debounce, check if it got pressed etc). And then it's simply checking the button and do what you want to do to turn it off / on.

But for now, start by removing the delay(), then I will help you further ;) Now the program simply has no time to check a button.

lc.shutdown(0,false); // Wake up displays

Hmm. Seems like a button that can magically make that false into a true might shut something down.

I just need to use a simple touch button like thishttp://www.jameco.com/z/R13-502A-05-BR-Switch-Push-Button-Single-Pole-Single-Throw-Off-Momentary-On-Red-3-Amp-R13-502A-05-BR_315660.html.

Thank you for suggesting me to eliminate “delay” and use the bounce2 library, which I didn’t know.
So, I rewrote the code based on your suggestion, and it became like this. This code only contains the LedControl. I was wondering if this code is correct:

#include "LedControl.h"

LedControl lc=LedControl(12,11,10,2);  // Pins: DIN,CLK,CS, # of Display connected

const byte invaders[2][2][8] =
{
  {
    {
      0b00011000,  // First frame of invader #1
      0b00111100,
      0b01111110,
      0b11011011,
      0b11111111,
      0b00100100,
      0b01011010,
      0b10100101
    },
    {
      0b00011000, // Second frame of invader #1
      0b00111100,
      0b01111110,
      0b11011011,
      0b11111111,
      0b00100100,
      0b01011010,
      0b01000010
    }
  },
  {
    {
      0b00100100, // First frame of invader #2
      0b00100100,
      0b01111110,
      0b11011011,
      0b11111111,
      0b11111111,
      0b10100101,
      0b00100100
    },
    {
      0b00100100, // Second frame of invader #2
      0b10100101,
      0b11111111,
      0b11011011,
      0b11111111,
      0b01111110,
      0b00100100,
      0b01000010
    }
  }
};

void draw(byte &in[], byte address)){
  for (byte i = 0; i < 8; i++){
    lc.setRow(address, i, in[i]);
  }
}


void setup()
{
 lc.shutdown(0,false);  // Wake up displays
 lc.shutdown(1,false);
 lc.setIntensity(0,5);  // Set intensity levels
 lc.setIntensity(1,5);
 lc.clearDisplay(0);  // Clear Displays
 lc.clearDisplay(1);
}

void loop()
{
draw(invaders[0]);
}

…and this is the bounce2 library example code I found. I paste the example code here:

/*
DESCRIPTION
====================
Simple example of the Bounce library that switches the debug LED when
either of 2 buttons are pressed.
*/

// Include the Bounce2 library found here :
// https://github.com/thomasfredericks/Bounce2
#include <Bounce2.h>

#define BUTTON_PIN_1 2
#define BUTTON_PIN_2 3



// Instantiate a Bounce object
Bounce debouncer1 = Bounce();

// Instantiate another Bounce object
Bounce debouncer2 = Bounce();
int COUNT1 = 0;
int COUNT2 = 0;
int LED_PIN1 = 6;
int LED_PIN2 = 7;
int LED_PIN3 = 8;

void setup() {

 // Setup the first button with an internal pull-up :
 pinMode(BUTTON_PIN_1,INPUT);
 // After setting up the button, setup the Bounce instance :
 debouncer1.attach(BUTTON_PIN_1);
 debouncer1.interval(50); // interval in ms
 
  // Setup the second button with an internal pull-up :
 pinMode(BUTTON_PIN_2,INPUT);
 // After setting up the button, setup the Bounce instance :
 debouncer2.attach(BUTTON_PIN_2);
 debouncer2.interval(50); // interval in ms


 //Setup the LED :
 pinMode(LED_PIN1,OUTPUT);
 pinMode(LED_PIN2,OUTPUT);
 pinMode(LED_PIN3,OUTPUT);

}

void loop() {
 // Update the Bounce instances :
 debouncer1.update();
 delay(50);
 debouncer2.update();
 delay(50);

 // Get the updated value :
 int value1 = debouncer1.read();
 int value2 = debouncer2.read();

 // Turn on the LED if either button is pressed :
 if ( value1 == HIGH) {
   COUNT1++;
 }
  if ( value2 == HIGH) {
   COUNT2++;
 }
  if (COUNT1 == 4) {
   COUNT1 = 0;
   digitalWrite(LED_PIN2, LOW);
   digitalWrite(LED_PIN1, LOW);
}
  if (COUNT1 == 1) {
   digitalWrite(LED_PIN1, HIGH);
 }
 if (COUNT1 == 2) {
   digitalWrite(LED_PIN2, HIGH);
 digitalWrite(LED_PIN1, LOW);
 }
if (COUNT1 == 3) {
   digitalWrite(LED_PIN2, HIGH);
   digitalWrite(LED_PIN1, HIGH);
   }
 if (COUNT2 == 2) {
   COUNT2 = 0;
   digitalWrite(LED_PIN3, LOW);
}
  if (COUNT2 == 1) {
   digitalWrite(LED_PIN3, HIGH);
 }
else
   digitalWrite(LED_PIN3, LOW);

}

What I am not sure here is that in the bounce2 code, it needs to set the PinMode() and uses the digitalWrite() command, but in the LedControl(matrix) code, it uses DIN, CLK and CS/LOAD. I cannot figure out how to declare the command for a chip instead of single LEDs each.
I thought about lc.shutdown using if command too, but the problem is the same - a chip cannot be commanded by digitalWrite().

In addition, if I remove the delay(), how do I set some interval between each frame?

I appreciated your patience on my very beginner question, and it is really helpful.

Shotran: I thought about lc.shutdown using if command too, but the problem is the same - a chip cannot be commanded by digitalWrite().

You're using a library. A library is essentially a predefined list of functions with its own names. Learning the words the library knows is kind of important.

http://playground.arduino.cc/Main/LedControl

The code is nice but only thing it does is draw the invader. You removed all the different scenes cycling… That’s the whole portion with the delay()… And that’s what will give you the trouble :wink:

Shotran:
In addition, if I remove the delay(), how do I set some interval between each frame?

Gave you a big pointer for that, Google “blink without delay Arduino” :wink:

Shotran:
I cannot figure out how to declare the command for a chip instead of single LEDs each.

That’s up to you :wink: Bounce2 gives you all the info of the button. And once you see the button action you want you act on it by sending the right stuff to the chip. I don’t know the LedControl library so I just go by the info from here but you get something like:

#include "LedControl.h"
#include <Bounce2.h>

const byte ButtonPin = 4;

LedControl display = LedControl(12,11,10,2);  // Pins: DIN,CLK,CS, # of Display connected
Bounce button;

bool displayOn = true; ///< Variable to hold if the display is on (true) or off (false)

const byte invaders[2][2][8] =
{
  {
    {
      0b00011000,  // First frame of invader #1
      0b00111100,
      0b01111110,
      0b11011011,
      0b11111111,
      0b00100100,
      0b01011010,
      0b10100101
    },
    {
      0b00011000, // Second frame of invader #1
      0b00111100,
      0b01111110,
      0b11011011,
      0b11111111,
      0b00100100,
      0b01011010,
      0b01000010
    }
  },
  {
    {
      0b00100100, // First frame of invader #2
      0b00100100,
      0b01111110,
      0b11011011,
      0b11111111,
      0b11111111,
      0b10100101,
      0b00100100
    },
    {
      0b00100100, // Second frame of invader #2
      0b10100101,
      0b11111111,
      0b11011011,
      0b11111111,
      0b01111110,
      0b00100100,
      0b01000010
    }
  }
};

void draw(byte &in[], byte address)){
  for (byte i = 0; i < 8; i++){
    display.setRow(address, i, in[i]);
  }
}


void setup()
{
  display.shutdown(0,false);  // Wake up displays
  display.shutdown(1,false);
  display.setIntensity(0,5);  // Set intensity levels
  display.setIntensity(1,5);
  display.clearDisplay(0);  // Clear Displays
  display.clearDisplay(1);
  
  //setup the button via Bounce2
  button.attach(ButtonPin, INPUT_PULLUP); 
  //now all you need is a button between ButtonPin and GND
}

void loop()
{
  //we need to update the button every time we want to recheck it
  button.update();
  
  //and if it BECAME pressed (is not the same as IS pressed ;) )
  if(button.fell){
    //change the display state on <-> off
    displayOn = !displayOn;
    
    //if it's now off, shutdown the display
    if(!displayOn){
      display.shutdown(0,false);
      display.shutdown(1,false);
    }
  }
  
  //We only draw if display is on
  if(displayOn){
    draw(invaders[0]);
  }
  
}

Also gave it a better name. And remember, spaces are free (and makes things more readable).

Thank you so much! It is really helpful and I really appreciated your tips and advices. I will try the code sooner and let you know how it worked :)

Hello again,

I tried your code given, but when compiling, this part of the code does not work:

void draw(byte &in[], byte address){
  for (byte i = 0; i < 8; i++){
    display.setRow(address, i, invaders[i]);

and “if(button.fell)” part was not also recognized when compiling. I changed “button” to “debouncer” but it still was not recognized.

Error Message:
switch_button_bouce2:5: error: declaration of ‘in’ as array of references
switch_button_bouce2:5: error: expected ‘)’ before ‘,’ token
switch_button_bouce2:5: error: expected initializer before ‘address’
switch_button_bouce2:58: error: declaration of ‘in’ as array of references
switch_button_bouce2:58: error: expected ‘)’ before ‘,’ token
switch_button_bouce2:58: error: expected initializer before ‘address’
declaration of ‘in’ as array of references

I also checked the bounce2 code, which I pasted in my previous post, with a circuit on breadboard but the code worked a bit wired(LEDs blinked a bit randomly), so the bounce2 code may have some problem(or my circuit).

However, the very first matrix code I posted works fine with my soldered circuits - I have made some works with the original matrix code already, so it does not seem to be my circuit(schematic) problem.

So myself tried to change some parts of your code for my study, but clearly my version of this code also does not work.
If you could check the error parts and have some advice, I would very much appreciate!

#include <LedControl.h>
#include <Bounce2.h>

const byte ButtonPin = 4;

LedControl display = LedControl(12,11,10,2);  // Pins: DIN,CLK,CS, # of Display connected

Bounce debouncer = Bounce(); 

bool displayOn = true; ///< Variable to hold if the display is on (true) or off (false)

const byte invaders[2][2][8] =
{
  {
    {
      0b00011000,  // First frame of invader #1
      0b00111100,
      0b01111110,
      0b11011011,
      0b11111111,
      0b00100100,
      0b01011010,
      0b10100101
    },
    {
      0b00011000, // Second frame of invader #1
      0b00111100,
      0b01111110,
      0b11011011,
      0b11111111,
      0b00100100,
      0b01011010,
      0b01000010
    }
  },
  {
    {
      0b00100100, // First frame of invader #2
      0b00100100,
      0b01111110,
      0b11011011,
      0b11111111,
      0b11111111,
      0b10100101,
      0b00100100
    },
    {
      0b00100100, // Second frame of invader #2
      0b10100101,
      0b11111111,
      0b11011011,
      0b11111111,
      0b01111110,
      0b00100100,
      0b01000010
    }
  }
};

void draw(byte &in[], byte address){
  for (byte i = 0; i < 8; i++){
    display.setRow(address, i, invaders[i]);
  }
}


void setup()
{
  display.shutdown(0,false);  // Wake up displays
  display.shutdown(1,false);
  display.setIntensity(0,5);  // Set intensity levels
  display.setIntensity(1,5);
  display.clearDisplay(0);  // Clear Displays
  display.clearDisplay(1);
 
  //setup the button via Bounce2
  debouncer.attach(ButtonPin, INPUT_PULLUP);
  debouncer.interval(5);
  //now all you need is a button between ButtonPin and GND
}

void loop()
{
  //we need to update the button every time we want to recheck it
  debouncer.update();
 
  //and if it BECAME pressed (is not the same as IS pressed ;) )
  if(debouncer.fell){
    //change the display state on <-> off
    displayOn = !displayOn;
   
    //if it's now off, shutdown the display
    if(!displayOn){
      display.shutdown(0,false);
      display.shutdown(1,false);
    }
  }
 
  //We only draw if display is on
  if(displayOn){
    draw(invaders[0]);
  }
 
}

Shotran:
I tried your code given, but when compiling, this part of the code does not work:

Did you change anything? Line numbers 5 and 58 don’t match my code…

But it’s an error of mine, I coded it dry / without compiling (A) Change to:

void draw(const byte in[2][8]){
  for(byte j = 0; j < 2; j++){
    for (byte i = 0; i < 8; i++){
      display.setRow(j, i, in[j][i]);
    }
  }
}

Shotran:
and “if(button.fell)” part was not also recognized when compiling. I changed “button” to “debouncer” but it still was not recognized.

Error of my side, “fell” is a method (function) of a Bounce object, here button. But then you have to call it like a methode which means including brackets :smiley: Try

if(button.fell()){

So total becomes:

#include "LedControl.h"
#include <Bounce2.h>

const byte ButtonPin = 4;

LedControl display = LedControl(12,11,10,2);  // Pins: DIN,CLK,CS, # of Display connected
Bounce button;

bool displayOn = true; ///< Variable to hold if the display is on (true) or off (false)

const byte invaders[2][2][8] =
{
  {
    {
      0b00011000,  // First frame of invader #1
      0b00111100,
      0b01111110,
      0b11011011,
      0b11111111,
      0b00100100,
      0b01011010,
      0b10100101
    },
    {
      0b00011000, // Second frame of invader #1
      0b00111100,
      0b01111110,
      0b11011011,
      0b11111111,
      0b00100100,
      0b01011010,
      0b01000010
    }
  },
  {
    {
      0b00100100, // First frame of invader #2
      0b00100100,
      0b01111110,
      0b11011011,
      0b11111111,
      0b11111111,
      0b10100101,
      0b00100100
    },
    {
      0b00100100, // Second frame of invader #2
      0b10100101,
      0b11111111,
      0b11011011,
      0b11111111,
      0b01111110,
      0b00100100,
      0b01000010
    }
  }
};

void draw(const byte in[2][8]){
  for(byte j = 0; j < 2; j++){
    for (byte i = 0; i < 8; i++){
      display.setRow(j, i, in[j][i]);
    }
  }
}


void setup()
{
  display.shutdown(0,false);  // Wake up displays
  display.shutdown(1,false);
  display.setIntensity(0,5);  // Set intensity levels
  display.setIntensity(1,5);
  display.clearDisplay(0);  // Clear Displays
  display.clearDisplay(1);
 
  //setup the button via Bounce2
  button.attach(ButtonPin, INPUT_PULLUP);
  //now all you need is a button between ButtonPin and GND
}

void loop()
{
  //we need to update the button every time we want to recheck it
  button.update();
 
  //and if it BECAME pressed (is not the same as IS pressed ;) )
  if(button.fell()){
    //change the display state on <-> off
    displayOn = !displayOn;
   
    //if it's now off, shutdown the display
    if(!displayOn){
      display.shutdown(0,false);
      display.shutdown(1,false);
    }
  }
 
  //We only draw if display is on
  if(displayOn){
    draw(invaders[0]);
  }
 
}

This will compile but is untested. I don’t have a hardware setup to test it. But something along this line should work.[/code]

Thank you for your advice and help on the code. I tried the code on my circuit, and the matrix screen only showed the first frame as a still image but does not move/animate all the frame.

The button part also does not work: when I plug in my Arduino, the first frame appears on the matrix screen without pressing the button. No matter how many times I press the button, it does not give any effect on the matrix.

I use this schematic for wiring the button: https://www.arduino.cc/en/Tutorial/Button

I really appreciate your tolerance on helping with my code issue so far… a button for a matrix seems quite a tricky combination :frowning:

P.S.
I attached a picture of my matrix - just to show how it works with the code.

Shotran: Thank you for your advice and help on the code. I tried the code on my circuit, and the matrix screen only showed the first frame as a still image but does not move/animate all the frame.

That's indeed what's suppose to happen. Have a look at the code. That's because you still didn't made a delay()-less version of the animation. I'm not going to do all the work fun parts. Have a look at Blink without delay ;)

Code for the animation needs to go in

  //We only draw if display is on
  if(displayOn){
    //instead of just drawing the first scene
    //draw(invaders[0]);

    //do the animation stuf
  }

Shotran: The button part also does not work: when I plug in my Arduino, the first frame appears on the matrix screen without pressing the button. No matter how many times I press the button, it does not give any effect on the matrix.

I use this schematic for wiring the button: https://www.arduino.cc/en/Tutorial/Button

That page is rubish and I don't know why it's still part of Arduino.cc :/

Just connect it between GND and pin 4 (you can change the pin number in code of course).

Shotran: a button for a matrix seems quite a tricky combination :(

Not really. But you have to start coding / read and understand the code I post ;) Not just assume it does what you want.

I added the delay-without-blink code in the code. My matrix turns on when I pressed the button twice - I’m not sure why twice… but then the button becomes inactive and the screen keeps the first frame unless I press the reset button of Arduino.

However, I understand about the theory of “delay-without-blink” instead of using delay(). Thank you :slight_smile:

Also, two matrix screens show the same image(both show the same invader, the second matrix does not show another invader set in the code).

I was wondering which part I should change:

void loop()
{
  //we need to update the button every time we want to recheck it
  button.update();

  unsigned long currentMillis = millis();
 
  //and if it BECAME pressed (is not the same as IS pressed ;) )
  if(button.fell()){
    //change the display state on <-> off
    displayOn = !displayOn;
    
    //if it's now off, shutdown the display
    if(!displayOn){
      display.shutdown(0,false);
      display.shutdown(1,false);
    }
  
  //We only draw if display is on
  if(displayOn){
    if (currentMillis - previousMillis > interval){
        draw(invaders[0]);
        previousMillis = currentMillis;
  }
  }
 }
}

I also set up these in the code too:

unsigned long previousMillis = 0;

const long interval = 1000;

Sorry for the delay answering back. I’ve been quite busy and yeah, coming here’s still a hobby thing…

Next time, please post the whole code when you made changes. It’s now pretty hard to find the error.

First thing, the double press issue. That’s indeed in the code. Why? I set the displayOn variable to true when initialized. So after one press it becomes false. After the second press it’s true again. And because I only draw the display when a press is detected it only draws it when pressed two times, because then it’s pressed and displayOn is true. To get around this we can put the draw() part outside the button.fell() check. Only thing that can give you trouble then is that you redraw everything every loop even when nothing changed…

Off to the second part, not being able to “turn it off”. I think it’s because I don’t shut the display down ::slight_smile:
I wrote:

    if (!displayOn) {
      display.shutdown(0, false);
      display.shutdown(1, false);
    }

But I think that needs to be true of course. ;D But now we also need to turn it back on when we want the display to be on.

About the invaders, we still only draw one frame… You only call draw() as draw(invaders[0]); which will draw the first invader for both screens. It you want to draw the second invader after some time you need to implement that as well. You are on the right track with millis() but now it times nothing :wink: This I will leave for you do do :wink: Hint, state machine.

So again, dry coded. But I fixed the errors. I only didn’t implement the animation, that’s up to you :wink:

#include "LedControl.h"
#include <Bounce2.h>

const byte ButtonPin = 4;

LedControl display = LedControl(12,11,10,2);  // Pins: DIN,CLK,CS, # of Display connected
Bounce button;

bool displayOn = true; ///< Variable to hold if the display is on (true) or off (false)

unsigned long previousMillis = -1; //-1 so to trigger the interval right away

const int Interval = 1000; 

const byte invaders[2][2][8] =
{
  {
    {
      0b00011000,  // First frame of invader #1
      0b00111100,
      0b01111110,
      0b11011011,
      0b11111111,
      0b00100100,
      0b01011010,
      0b10100101
    },
    {
      0b00011000, // Second frame of invader #1
      0b00111100,
      0b01111110,
      0b11011011,
      0b11111111,
      0b00100100,
      0b01011010,
      0b01000010
    }
  },
  {
    {
      0b00100100, // First frame of invader #2
      0b00100100,
      0b01111110,
      0b11011011,
      0b11111111,
      0b11111111,
      0b10100101,
      0b00100100
    },
    {
      0b00100100, // Second frame of invader #2
      0b10100101,
      0b11111111,
      0b11011011,
      0b11111111,
      0b01111110,
      0b00100100,
      0b01000010
    }
  }
};

void draw(const byte in[2][8]){
  for(byte j = 0; j < 2; j++){
    for (byte i = 0; i < 8; i++){
      display.setRow(j, i, in[j][i]);
    }
  }
}


void setup()
{
  display.shutdown(0, false);  // Wake up displays
  display.shutdown(1, false);
  display.setIntensity(0, 5);  // Set intensity levels
  display.setIntensity(1, 5);
  display.clearDisplay(0);  // Clear Displays
  display.clearDisplay(1);
 
  //setup the button via Bounce2
  button.attach(ButtonPin, INPUT_PULLUP);
  //now all you need is a button between ButtonPin and GND
}

void loop()
{
  //we need to update the button every time we want to recheck it
  button.update();

  //and if it BECAME pressed (is not the same as IS pressed ;) )
  if (button.fell()) {
    //change the display state on <-> off
    displayOn = !displayOn;

    //Now set the state of the displays the same:
    display.shutdown(0, !displayOn);
    display.shutdown(1, !displayOn);
    
    //to force a draw() when the display is turned on
    //we set a fake previousMillis
    if(displayOn){
      //we set previousMillis to millis minus interval so it will trigger right away
      previousMillis = millis() - Interval;
    }
  } // done dealing with the button part
  
  //on with drawing invaders!
  //We only draw if display is on
  if (displayOn) {
    if (millis() - previousMillis >= Interval) {
      draw(invaders[0]);
      previousMillis = currentMillis;
    }
  }
}

Thank you for your kind help on the button & animation code issue. This time, your code perfectly worked and the button worked as I press the button. No double pressing issue! Very impressive and thank you so much :slight_smile:
The smallest thing is that when I turn on Arduino with the code, the image appears as a custom image without pressing button. My ideal idea was that when someone press the button, the image appears like a surprising box. It doesn’t matter which comes first though as the important thing is to swich on/off.

But to experiment, I changed here from if(displayOn) to if (!displayOn) in the code.

void loop()
{
  //we need to update the button every time we want to recheck it
  button.update();

  //and if it BECAME pressed (is not the same as IS pressed ;) )
  if (button.fell()) {
    //change the display state on <-> off
    displayOn = !displayOn;

    //Now set the state of the displays the same:
    display.shutdown(0, !displayOn);
    display.shutdown(1, !displayOn);
   
    //to force a draw() when the display is turned on
    //we set a fake previousMillis
    if(!displayOn){
      //we set previousMillis to millis minus interval so it will trigger right away
      previousMillis = millis() - Interval;
    }
  } // done dealing with the button part
 
  //on with drawing invaders!
  //We only draw if display is on
  if (!displayOn) {
    if (millis() - previousMillis >= Interval) {
      draw(invaders[0]);
      previousMillis = millis();
    }
  }
}

When I changed the code to this, the button normally works well, but the double pressing issue came back. The issue can be seen only the first pressing after turned on Arduino, so it is not a huge issue but just wondered why.

I could not figure out how to command animation in this style of code, so I tried using my original code with the button code:

#include <avr/pgmspace.h>
#include <LedControl.h>
#include <Bounce2.h>

LedControl lc =LedControl(12,11,10,2);  // Pins: DIN,CLK,CS, # of Display connected

Bounce button;

const byte ButtonPin = 4;

bool displayOn = true; ///< Variable to hold if the display is on (true) or off (false)

const int numDevices = 2;      //Number of MAX7219 LED Driver Chips (1-8)

unsigned long previousMillis = -1; //-1 so to trigger the interval right away

const int Interval = 200;

// Put values in arrays

byte invader1a[] =
{
  B00011000,  // First frame of invader #1
  B00111100,
  B01111110,
  B11011011,
  B11111111,
  B00100100,
  B01011010,
  B10100101
};

byte invader1b[] =
{
 B00011000, // Second frame of invader #1
 B00111100,
 B01111110,
 B11011011,
 B11111111,
 B00100100,
 B01011010,
 B01000010
};

byte invader2a[] =
{
 B00100100, // First frame of invader #2
 B00100100,
 B01111110,
 B11011011,
 B11111111,
 B11111111,
 B10100101,
 B00100100
};

byte invader2b[] =
{
 B00100100, // Second frame of invader #2
 B10100101,
 B11111111,
 B11011011,
 B11111111,
 B01111110,
 B00100100,
 B01000010
};

void setup()
{
     Serial.begin(300);
    for (int x=0; x<numDevices; x++){
        lc.shutdown(x,false);       //The MAX72XX is in power-saving mode on startup
        lc.setIntensity(x,5);       // Set the brightness to default value
        lc.clearDisplay(x);         // and clear the display
    }
    
  lc.shutdown(0,false);  // Wake up displays
  lc.shutdown(1,false);
  lc.setIntensity(0,5);  // Set intensity levels
  lc.setIntensity(1,5);
  lc.clearDisplay(0);  // Clear Displays
  lc.clearDisplay(1);

    button.attach(ButtonPin, INPUT_PULLUP);
  
}

//  Take values in Arrays and Display them

void sinvader1a() //frame1 image1
{
 for (int i = 0; i < 8; i++)  
 {
   lc.setRow(0,i,invader1a[i]);
 }
}

void sinvader1b() //frame1 image2
{
 for (int i = 0; i < 8; i++)
 {
   lc.setRow(0,i,invader1b[i]);
 }
}


void sinvader2a() //frame2 image1
{
 for (int i = 0; i < 8; i++)
 {
   lc.setRow(1,i,invader2a[i]);
 }
}

void sinvader2b() //frame2 image2
{
for (int i = 0; i < 8; i++)
 {
   lc.setRow(1,i,invader2b[i]);
 }
}

void loop()
{

   Serial.println("Time: ");
   //we need to update the button every time we want to recheck it
  button.update();

  //and if it BECAME pressed (is not the same as IS pressed ;) )
  if (button.fell()) {
    //change the display state on <-> off
    displayOn = !displayOn;

    //Now set the state of the displays the same:
    lc.shutdown(0, !displayOn);
    lc.shutdown(1, !displayOn);
   
    //to force a draw() when the display is turned on
    //we set a fake previousMillis
    if(displayOn){
      //we set previousMillis to millis minus interval so it will trigger right away
      previousMillis = millis() - Interval;
    }
  } // done dealing with the button part
 
  //We only draw if display is on
  if(displayOn){
    if (millis() - previousMillis >= Interval) {
    sinvader1a();
    sinvader2a();
    Serial.print(previousMillis);
    sinvader1b();
    sinvader2b();
    Serial.print(previousMillis);
    previousMillis = millis();
    
  }
  }
}

This code worked with button and animation on both matrix displays. However, the animation was moving very quick that barely can see the animated images. In this code, I used Serial.begin() and millis(). I wasn’t sure how to slow down between each images with millis(), so I reduced the Serial speed from 9600 to 300. Then the animation speed became normal which can be seen by eyes, but the button reaction speed of course became very slow so I have to keep pressing the button about for 2 seconds to turn on/off the display and wait about 2 seconds for pressing the button again. I wonder if there is a way without reducing the Serial rate.

Thinking about where I started, it has been very much progressed! Button works now and I learned new ways. Thank you so so much :slight_smile:

Alright, point for trying to fix it yourself :) But uhm, really, changing it to if(!displayOn)... That would mean the meaning of the variable is flipped making it very very very confusing. And there is even an easier solution... The solution is right under your nose, to be more, precise, line 10 ;) (or 9 if you removed my stupid blank line on line 1....)

Only thing is, now the display is blank on startup but not in shutdown. You probably don need the shutdown but it's a hardware state to save power. Probably overkill but just add

  display.shutdown(0, !displayOn);
  display.shutdown(1, !displayOn);

to setup to fix it (no matter what you initialize displayOn to)

Off to the second problem. That's the fact you still don't get the use of millis(). Compare it to a timer. How would you do it if you had to time two things using a timer?

Now, you wait for the timer to end and then do, right after each other, animation 1, print something, animation 2, print something and go back to waiting... That's why I talked about a state machine ;) Isn't it more logical to, when the time is up, display one animation, reset the timer and display the second animation when the time is up? (And repeat after that.)

I could edit the code for that but let's see if you understand the use of millis() now ;)

Sorry for my late reply. It has been very busy over the past few weeks. Thank you for your hint on the display - I swapped "true" to "false" on line 10, then it worked in the way I intended the display and button:) Thank you!!

However, I could not figure out to show all the frames as animation with millis(). Also, since the code is for two matrixes, I wonder how to set up more than two displays/matrixes with each different frames - such as 3 to 10 displays. Meanwhile, I will keep trying to figure out how to set animation/frame with more numbers of displays, your advise would be very much appreciated. I'm in a busy schedule so my reply would be quite slow for the next few months :slightly_frowning_face: but I'm making and checking!