Presse a button to call a function and keep it

Hi everyone, it has been a long while since I gave up my current project but as it is holidays, I have more time to do Arduno stuff. But my current problem is still not solved and after having spent few hours today trying to find the solution, I am saturated enough to give up and ask for some help here.

As you will read in the code, I am trying to call for a function in the void loop when I push the button.
My willing is to have the function “standby()” to loop, then I push the button and the function “transformation1()” is called UNTIL i push again the button to stop it and back in “standby()”.
I watched some tutorials and messages in this forum but nothing seems to answer my trouble.
It seems simple but I am truly unable to find…

Here my code :

// Program to exercise the MD_MAX72XX library
//
// Uses most of the functions in the library
#include <MD_MAX72xx.h>
//#include <SPI.h>

// Turn on debug statements to the serial output
#define  DEBUG  1

#if  DEBUG
#define PRINT(s, x) { Serial.print(F(s)); Serial.print(x); }
#define PRINTS(x) Serial.print(F(x))
#define PRINTD(x) Serial.println(x, DEC)

#else
#define PRINT(s, x)
#define PRINTS(x)
#define PRINTD(x)

#endif

// 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::ICSTATION_HW
#define MAX_DEVICES  11

#define CLK_PIN   13  // or SCK
#define DATA_PIN  11  // or MOSI
#define CS_PIN    10  // or SS

int BUTTON_PINL = 4;
//int BUTTON_PINR = 3;


// SPI hardware interface
MD_MAX72XX mx = MD_MAX72XX(HARDWARE_TYPE, CS_PIN, MAX_DEVICES);
// Arbitrary pins
// MD_MAX72XX mx = MD_MAX72XX(HARDWARE_TYPE, DATA_PIN, CLK_PIN, CS_PIN, MAX_DEVICES);

// We always wait a bit between updates of the display
#define  DELAYTIME  100  // in milliseconds

void setup()
{
  pinMode(BUTTON_PINL, INPUT_PULLUP);
  //pinMode(BUTTON_PINR, INPUT_PULLUP);

  mx.begin();

#if  DEBUG
  Serial.begin(57600);
#endif
  PRINTS("\n[MD_MAX72XX Test & Demo]");
}

//------------------------------------------------------------------------------------------------------------------------------------

void standby()
// Demonstrate the use of buffer based repeated patterns
// across all devices.
{
  PRINTS("\nBullseye");
  mx.clear();
  mx.control(MD_MAX72XX::UPDATE, MD_MAX72XX::OFF);

  for (uint8_t n=0; n<3; n++)
  {
    byte  b = 0xff;
    int   i = 0;

    while (b != 0x00)
    {
      for (uint8_t j=0; j<MAX_DEVICES+1; j++)
      {
        mx.setRow(j, i, b);
        mx.setColumn(j, i, b);
        mx.setRow(j, ROW_SIZE-1-i, b);
        mx.setColumn(j, COL_SIZE-1-i, b);
      }
      mx.update();
      delay(3*DELAYTIME);
      for (uint8_t j=0; j<MAX_DEVICES+1; j++)
      {
        mx.setRow(j, i, 0);
        mx.setColumn(j, i, 0);
        mx.setRow(j, ROW_SIZE-1-i, 0);
        mx.setColumn(j, COL_SIZE-1-i, 0);
      }

      bitClear(b, i);
      bitClear(b, 7-i);
      i++;
    }

    while (b != 0xff)
    {
      for (uint8_t j=0; j<MAX_DEVICES+1; j++)
      {
        mx.setRow(j, i, b);
        mx.setColumn(j, i, b);
        mx.setRow(j, ROW_SIZE-1-i, b);
        mx.setColumn(j, COL_SIZE-1-i, b);
      }
      mx.update();
      delay(3*DELAYTIME);
      for (uint8_t j=0; j<MAX_DEVICES+1; j++)
      {
        mx.setRow(j, i, 0);
        mx.setColumn(j, i, 0);
        mx.setRow(j, ROW_SIZE-1-i, 0);
        mx.setColumn(j, COL_SIZE-1-i, 0);
      }

      i--;
      bitSet(b, i);
      bitSet(b, 7-i);
    }
  }

  mx.control(MD_MAX72XX::UPDATE, MD_MAX72XX::ON);
}

//------------------------------------------------------------------------------------------------------------------------------------

void transformation1()
// Demonstrates the use of transform() to move bitmaps on the display
// In this case a user defined bitmap is created and animated.
{
  uint8_t arrow[COL_SIZE] =
  {
    0b00001000,
    0b00011100,
    0b00111110,
    0b01111111,
    0b00011100,
    0b00011100,
    0b00111110,
    0b00000000
  };

  MD_MAX72XX::transformType_t  t[] =
  {
    MD_MAX72XX::TSL, MD_MAX72XX::TSL, MD_MAX72XX::TSL, MD_MAX72XX::TSL,
    MD_MAX72XX::TSL, MD_MAX72XX::TSL, MD_MAX72XX::TSL, MD_MAX72XX::TSL,
    MD_MAX72XX::TSL, MD_MAX72XX::TSL, MD_MAX72XX::TSL, MD_MAX72XX::TSL,
    MD_MAX72XX::TSL, MD_MAX72XX::TSL, MD_MAX72XX::TSL, MD_MAX72XX::TSL,
    
  };

  PRINTS("\nTransformation1");
  mx.clear();

  // use the arrow bitmap
  mx.control(MD_MAX72XX::UPDATE, MD_MAX72XX::OFF);
  for (uint8_t j=0; j<mx.getDeviceCount(); j++)
    mx.setBuffer(((j+1)*COL_SIZE)-1, COL_SIZE, arrow);
  mx.control(MD_MAX72XX::UPDATE, MD_MAX72XX::ON);
  delay(DELAYTIME);

  // run through the transformations
  mx.control(MD_MAX72XX::WRAPAROUND, MD_MAX72XX::ON);
  for (uint8_t i=0; i<(sizeof(t)/sizeof(t[0])); i++)
  {
    mx.transform(t[i]);
    delay(DELAYTIME*4);
  }
  mx.control(MD_MAX72XX::WRAPAROUND, MD_MAX72XX::OFF);
}


//------------------------------------------------------------------------------------------------------------------------------------


void loop()
{ 
  standby();
 if (BUTTON_PINL == HIGH)
  {
   transformation1();
  }
  
}

I am looking for your answers !

int BUTTON_PINL = 4;

BUTTON_PINL is set to 4. It is the number of an Arduino input pin and its value never changes (why would it ?)

Later in the program its value is tested

  if (BUTTON_PINL == HIGH)

It will always be HIGH

I assume that you meant something like

  if (digitalRead(BUTTON_PINL) == HIGH)

UKHeliBob:

int BUTTON_PINL = 4;

BUTTON_PINL is set to 4. It is the number of an Arduino input pin and its value never changes (why would it ?)

Later in the program its value is tested

  if (BUTTON_PINL == HIGH)

It will always be HIGH

I assume that you meant something like

  if (digitalRead(BUTTON_PINL) == HIGH)

Indeed, sorry I forgot to undo the code before the last try I did.
if (digitalRead(BUTTON_PINL) == HIGH) doesn't work however.

Hi Mike,

If you turn on the debug what do you see in the serial monitor?
Post the debug-output as a codesection

And I would add some more debug-output

// Program to exercise the MD_MAX72XX library
//
// Uses most of the functions in the library
#include <MD_MAX72xx.h>
//#include <SPI.h>

// Turn on debug statements to the serial output
#define  DEBUG  1

#if  DEBUG
#define PRINT(s, x) { Serial.print(F(s)); Serial.print(x); }
#define PRINTS(x) Serial.print(F(x))
#define PRINTD(x) Serial.println(x, DEC)

#else
#define PRINT(s, x)
#define PRINTS(x)
#define PRINTD(x)

#endif

// 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::ICSTATION_HW
#define MAX_DEVICES  11

#define CLK_PIN   13  // or SCK
#define DATA_PIN  11  // or MOSI
#define CS_PIN    10  // or SS

int BUTTON_PINL = 4;
//int BUTTON_PINR = 3;


// SPI hardware interface
MD_MAX72XX mx = MD_MAX72XX(HARDWARE_TYPE, CS_PIN, MAX_DEVICES);
// Arbitrary pins
// MD_MAX72XX mx = MD_MAX72XX(HARDWARE_TYPE, DATA_PIN, CLK_PIN, CS_PIN, MAX_DEVICES);

// We always wait a bit between updates of the display
#define  DELAYTIME  100  // in milliseconds

void setup() {
  pinMode(BUTTON_PINL, INPUT_PULLUP);
  //pinMode(BUTTON_PINR, INPUT_PULLUP);

  mx.begin();

#if  DEBUG
  Serial.begin(57600);
#endif
  PRINTS("\n[MD_MAX72XX Test & Demo]");
}
//------------------------------------------------------------------------------------------------------------------------------------
void standby() {
// Demonstrate the use of buffer based repeated patterns
// across all devices.
  PRINTS("\nBullseye");
  mx.clear();
  mx.control(MD_MAX72XX::UPDATE, MD_MAX72XX::OFF);

  for (uint8_t n=0; n<3; n++) {
    byte  b = 0xff;
    int   i = 0;

    while (b != 0x00) {
      for (uint8_t j=0; j<MAX_DEVICES+1; j++) {
        mx.setRow(j, i, b);
        mx.setColumn(j, i, b);
        mx.setRow(j, ROW_SIZE-1-i, b);
        mx.setColumn(j, COL_SIZE-1-i, b);
      }
      mx.update();
      delay(3*DELAYTIME);
      for (uint8_t j=0; j<MAX_DEVICES+1; j++) {
        mx.setRow(j, i, 0);
        mx.setColumn(j, i, 0);
        mx.setRow(j, ROW_SIZE-1-i, 0);
        mx.setColumn(j, COL_SIZE-1-i, 0);
      }

      bitClear(b, i);
      bitClear(b, 7-i);
      i++;
    }

    while (b != 0xff) {
      for (uint8_t j=0; j<MAX_DEVICES+1; j++) {
        mx.setRow(j, i, b);
        mx.setColumn(j, i, b);
        mx.setRow(j, ROW_SIZE-1-i, b);
        mx.setColumn(j, COL_SIZE-1-i, b);
      }
      mx.update();
      delay(3*DELAYTIME);
      for (uint8_t j=0; j<MAX_DEVICES+1; j++) {
        mx.setRow(j, i, 0);
        mx.setColumn(j, i, 0);
        mx.setRow(j, ROW_SIZE-1-i, 0);
        mx.setColumn(j, COL_SIZE-1-i, 0);
      }

      i--;
      bitSet(b, i);
      bitSet(b, 7-i);
    }
  }

  mx.control(MD_MAX72XX::UPDATE, MD_MAX72XX::ON);
}
//------------------------------------------------------------------------------------------------------------------------------------

void transformation1() {
// Demonstrates the use of transform() to move bitmaps on the display
// In this case a user defined bitmap is created and animated.
  PRINTS("\n entering transformation1()");
  uint8_t arrow[COL_SIZE] =
  {
    0b00001000,
    0b00011100,
    0b00111110,
    0b01111111,
    0b00011100,
    0b00011100,
    0b00111110,
    0b00000000
  };

  MD_MAX72XX::transformType_t  t[] =
  {
    MD_MAX72XX::TSL, MD_MAX72XX::TSL, MD_MAX72XX::TSL, MD_MAX72XX::TSL,
    MD_MAX72XX::TSL, MD_MAX72XX::TSL, MD_MAX72XX::TSL, MD_MAX72XX::TSL,
    MD_MAX72XX::TSL, MD_MAX72XX::TSL, MD_MAX72XX::TSL, MD_MAX72XX::TSL,
    MD_MAX72XX::TSL, MD_MAX72XX::TSL, MD_MAX72XX::TSL, MD_MAX72XX::TSL,   
  };

  PRINTS("\nTransformation1");
  mx.clear();

  // use the arrow bitmap
  mx.control(MD_MAX72XX::UPDATE, MD_MAX72XX::OFF);
  for (uint8_t j=0; j<mx.getDeviceCount(); j++)
    mx.setBuffer(((j+1)*COL_SIZE)-1, COL_SIZE, arrow);
  mx.control(MD_MAX72XX::UPDATE, MD_MAX72XX::ON);
  delay(DELAYTIME);

  // run through the transformations
  mx.control(MD_MAX72XX::WRAPAROUND, MD_MAX72XX::ON);
  for (uint8_t i=0; i<(sizeof(t)/sizeof(t[0])); i++)   {
    mx.transform(t[i]);
    delay(DELAYTIME*4);
  }
  mx.control(MD_MAX72XX::WRAPAROUND, MD_MAX72XX::OFF);
  PRINTS("\n leaving transformation1()");
}

//------------------------------------------------------------------------------------------------------------------------------------
void loop() {
  standby();
 if (digitalRead(BUTTON_PINL) == HIGH)
  {
   PRINTS("\n button pressed right before entering transformation1()");
 
   transformation1();
  }
}

best regards Stefan

How long does the standby() function take to run ?

Save the value of millis() as the start time before you call it then print the difference between millis() and the start time when the code returns to loop()

In addition, you need to take action when the button becomes pressed rather than when it is pressed. Take a look at the StateChangeDetection example in the IDE

This is the monitor with the debug code gave to me :

18:39:10.638 -> 
18:39:10.638 -> [MD_MAX72XX Test & Demo]
18:39:10.638 -> Bullseye
18:39:17.928 -> Bullseye
18:39:25.223 -> Bullseye
18:39:32.495 -> Bullseye
18:39:39.774 -> Bullseye

So the program spends about 7 seconds in the standby() function which means that there is at least 7 seconds between each read of the button pin. At the best this means that the program is going to be very unresponsive to button presses

You need to change the structure of the program to read the button pin more frequently either by reading it in the standby() function itself, which is clumsy, or by preventing the standby() function taking so long to execute

Take a look at Using millis() for timing. A beginners guide, Several things at the same time and the BlinkWithoutDelay example in the IDE

In the Several things at the same time examples you will see that the program repeatedly calls a function to do part of a task then returns back to loop() where inputs can be tested frequently

oldest programmer-wisdom:
A program does always what is programmed. Though if it does something different than you expected you don’t (yet) understand what you have programed.

from the timestamps you can see that it takes aprox 7 seconds to perform the standby()-function
you have to keep your button pressed down until the function standby() has finished then your code will check for th ebutton beeing pressed

void loop() {
  standby(); // this takes 7 seconds
 if (digitalRead(BUTTON_PINL) == HIGH) // this check runs through in 0,001 seconds and then another standby() of 7 seconds
  {
   PRINTS("\n button pressed right before entering transformation1()");
 
   transformation1();
  }
}

You have answered my question. In analysing programs and get support from other people it is always a good idea to
describe a lot of details what you have done (except making coffee while program loads up …)

So please give a very detailed description of what you have tried with your code
best regards Stefan

Ok, thanks to both of you, I manipulated a bit the code in the both functions, adjusting the DELAYTIME.
Now by launching the loop, I have the standby() (Bullseye) on going in background (as I want), and when I push the button (at least one second and more), the transformation1() appeared.

I did not understand well how to use the millis() tutorial gave above, but I understood the idea behind.

Now, I have to check how to keep the transformation1() going until I push again the button. :o

19:35:23.827 -> [MD_MAX72XX Test & Demo]
19:35:23.827 -> Bullseye
19:35:25.349 ->  entering transformation1()
19:35:25.349 -> Transformation1
19:35:27.344 ->  leaving transformation1()
19:35:27.344 -> Bullseye
19:35:28.869 -> Bullseye
19:35:30.400 -> Bullseye
19:35:31.899 -> Bullseye
19:35:33.421 -> Bullseye
19:35:34.946 -> Bullseye
19:35:36.475 -> Bullseye
19:35:37.998 -> Bullseye

Now, I have to check how to keep the transformation1() going until I push again the button.

From reply #4

In addition, you need to take action when the button becomes pressed rather than when it is pressed. Take a look at the StateChangeDetection example in the IDE

UKHeliBob:
From reply #4

I already found a video tutoral about this, I tried with my current code but could not found how to apply with a MD_MAX72XX device. In their tutorial, they used a LED, which is easy to understand, but for my case, I am lost in the terms.

You need to detect the change of state of the button pin. That has nothing to do with the MD_MAX72XX

so in very simple words:

Open serial monitor
Start your program and hold the button continuosly pressed down for a minimum of 30 seconds
to see what your program does.

best regards Stefan

StefanL38:
so in very simple words:

Open serial monitor
Start your program and hold the button continuosly pressed down for a minimum of 30 seconds
to see what your program does.

best regards Stefan

20:38:45.990 -> Bullseye
20:38:47.509 -> Bullseye
20:38:49.034 -> Bullseye
20:38:50.556 -> Bullseye
20:38:52.080 ->  entering transformation1()
20:38:52.080 -> Transformation1
20:38:54.076 ->  leaving transformation1()
20:38:54.076 -> Bullseye
20:38:55.596 -> Bullseye
20:38:57.118 -> Bullseye
20:38:58.649 -> Bullseye
20:39:00.184 -> Bullseye
20:39:01.702 -> Bullseye
20:39:03.224 -> Bullseye
20:39:04.748 -> Bullseye
20:39:06.243 -> Bullseye
20:39:07.772 -> Bullseye
20:39:09.299 -> Bullseye
20:39:10.819 -> Bullseye
20:39:12.347 -> Bullseye
20:39:13.881 -> Bullseye
20:39:15.395 -> Bullseye
20:39:16.925 -> Bullseye
20:39:18.419 -> Bullseye
20:39:19.950 -> Bullseye
20:39:21.475 -> Bullseye
20:39:23.014 -> Bullseye
20:39:24.537 -> Bullseye
20:39:26.062 -> Bullseye
20:39:27.586 -> Bullseye
20:39:29.103 -> Bullseye
20:39:30.607 -> Bullseye
20:39:32.143 -> Bullseye
20:39:33.668 -> Bullseye
20:39:35.188 -> Bullseye
20:39:36.708 -> Bullseye
20:39:38.238 -> Bullseye
20:39:39.766 -> Bullseye
20:39:41.264 -> Bullseye

This is what I got when I pushed 1 minute.

UKHeliBob:
You need to detect the change of state of the button pin. That has nothing to do with the MD_MAX72XX

Here the code I tried, the button still not maintain the transformation1() when i pused it :

void loop()
  {
    standby();
    buttonState = digitalRead(BUTTON_PINL);
    if (buttonState != lastButtonState) {
     if (buttonState == HIGH) {
        transformation1();
      
    } 
    else {
    }
    delay(100);
  }
  lastButtonState = buttonState;
  }

And the monitor

20:49:58.245 -> Bullseye
20:49:59.766 -> Bullseye
20:50:01.358 -> Bullseye
20:50:02.911 ->  entering transformation1()
20:50:02.911 -> Transformation1
20:50:04.875 ->  leaving transformation1()
20:50:04.976 -> Bullseye
20:50:06.499 -> Bullseye
20:50:08.125 -> Bullseye
20:50:09.645 ->  entering transformation1()
20:50:09.645 -> Transformation1
20:50:11.642 ->  leaving transformation1()
20:50:11.744 -> Bullseye
20:50:13.275 -> Bullseye
20:50:14.806 -> Bullseye
20:50:16.305 -> Bullseye

As written the sketch will only call the transformation1() function when the button is first pressed

Instead of calling the transformation1() function as you do now, make calling it dependant on a counter being odd or even and place the test of the counter and call in loop(). Use the button press detection to add 1 to the counter thus triggering the call of transformation1() or not

UKHeliBob:
As written the sketch will only call the transformation1() function when the button is first pressed

Instead of calling the transformation1() function as you do now, make calling it dependant on a counter being odd or even and place the test of the counter and call in loop(). Use the button press detection to add 1 to the counter thus triggering the call of transformation1() or not

Thanks, it was clearer to understand, and I feel now I am nearer of what I want.
However the button seems not as reactive as it should, it seems not to follow the 4 pushs as the tutorial said :

void loop()
  {
    buttonState = digitalRead(BUTTON_PINL);
    if (buttonState != lastButtonState) {
     if (buttonState == HIGH) 
     {      
     buttonPushCounter++;
     } 
    else {
    }
    delay(50);
  }
  lastButtonState = buttonState;

  if (buttonPushCounter % 4 == 0) 
  {
    transformation1();
  } 
  else 
  {
    standby();
  }
  
  }

Please post your whole sketch

How long does transformation1() take to run ?

You don't want to count 4 button presses before calling transformation1(), only 2

of course checking for odd/even works.

How about just inverting the value of a bool-variable

bool activate_transf = false;

buttonState = digitalRead(BUTTON_PINL);
    if (buttonState != lastButtonState) {
     if (buttonState == HIGH) { // each time the button gets pressed 
       activate_transf = !activate_transf;  // invert value with the not-operator "!"
     }
     if (activate_transf) {
       transformation1();
     }

to have long delays in your function standby() combined with fast repsonsiveness of the button requires
non-blocking timing in combination with using a state-machine

best regards Stefan

UKHeliBob:
Please post your whole sketch

How long does transformation1() take to run ?

You don't want to count 4 button presses before calling transformation1(), only 2

I will do the sketch tomorrow, already middle of night here.
For transformation1(), the code was given in the first post of me. I used a code gave with the matrix, so I don't understand it well.

StefanL38:
of course checking for odd/even works.

How about just inverting the value of a bool-variable

bool activate_transf = false;

buttonState = digitalRead(BUTTON_PINL);
    if (buttonState != lastButtonState) {
    if (buttonState == HIGH) { // each time the button gets pressed
      activate_transf = !activate_transf;  // invert value with the not-operator "!"
    }
    if (activate_transf) {
      transformation1();
    }



to have long delays in your function standby() combined with fast repsonsiveness of the button requires 
non-blocking timing in combination with using a state-machine

best regards Stefan

This code is giving the same problem, I adjusted it to mine and when I pused (too long push btw) it call transformation1() for few seconds without waiting my own push.
I totally understood the principle, but I think the code has a problem somewhere.