Scrolling message waiting for button press

I developed and adapted this sketch with valuable help from forum members so that when switched on the message would read "Start!". I thought I would try a father adaptation to display a scrolling message while waiting for the start button to be pressed. I have tried "while" and the "do while" and the scrolling message appears but nothing happens when the start button is pressed, the message just keeps scrolling. Not sure if it is where I have placed the "do while" although I did try a couple of places, and I have tried it using startbuttonPin and start buttonState. I am obviously missing something can anyone spot my error(s) and point me in the right direction. As always, any help would be much appreciated.

// Scrolling Message Stopwatch

// include the library code:

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

#define HARDWARE_TYPE MD_MAX72XX::FC16_HW
#define MAX_DEVICES 4               // Number of 8 x 8 LED Matrix Modules used

#define DATA_PIN  11                // Pin for Arduino Nano 11
#define CS_PIN    3                 // Pin for Arduino Nano 3
#define CLK_PIN   13                // Pin for Arduino Nano 13

// Hardware SPI connection
MD_Parola P = MD_Parola(HARDWARE_TYPE, CS_PIN, MAX_DEVICES);
// Arbitrary output pins

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


int startbuttonPin = 2;                  // start button on pin 2
int stopbuttonPin = 4;                   // stop button on pin 4
int startbuttonState;                    // variable to store button state
int stopbuttonState;                     // variable to store button state
int laststartButtonState;                // variable to store last button state
int laststopButtonState;                 // variable to store last button state
int blinking;                            // condition for blinking - timer is timing
int frameRate = 100;                     // the frame rate (frames per second) at which the stopwatch runs - Change to suit
uint32_t interval = (1000/frameRate);    // blink interval
uint32_t previousMillis = 0;             // variable to store last time LED was updated
uint32_t startTime;                      // start time for stop watch
uint32_t elapsedTime;                    // elapsed time for stop watch
int fractional;                          // variable used to store fractional part of Frames
int fractionalSecs;                      // variable used to store fractional part of Seconds
int fractionalMins;                      // variable used to store fractional part of Minutes
uint32_t elapsedFrames;                  // elapsed frames for stop watch
uint32_t elapsedSeconds;                 // elapsed seconds for stop watch
uint32_t elapsedMinutes;                 // elapsed Minutes for stop watch
char buff[10];                           // a buffer that will contain the string to be displayed

uint8_t scrollSpeed = 25;    // default frame delay value
textEffect_t scrollEffect = PA_SCROLL_LEFT;
textPosition_t scrollAlign = PA_LEFT;
uint16_t scrollPause = 2000; // in milliseconds

#define  BUF_SIZE  100
char curMessage[BUF_SIZE] = { "" };
char newMessage[BUF_SIZE] = { "If you are ready to begin, press the start button!" };
bool newMessageAvailable = true;


void setup()
{
  Serial.begin(57600);
  P.begin();                             // intialise the LED Matrix.
  P.setIntensity(5);                     // Set LED Matrix Brightness
  //P.print("Start!");
  P.displayText(curMessage, scrollAlign, scrollSpeed, scrollPause, scrollEffect, scrollEffect);    // print opening message                 
  //P.setFont(SmallDigits);
  pinMode(startbuttonPin, INPUT);        // not really necessary, pins default to INPUT anyway
  pinMode(stopbuttonPin, INPUT);         // not really necessary, pins default to INPUT anyway
  digitalWrite(startbuttonPin, HIGH);    // turn on pullup resistors. Wire button so that press shorts pin to ground.
  digitalWrite(stopbuttonPin, HIGH);     // turn on pullup resistors. Wire button so that press shorts pin to ground.
  
}

void loop(){

do {
CallScrolling();
}
while (startbuttonState = HIGH);

  startbuttonState = digitalRead(startbuttonPin);                 // Check for button press, read the button state and store
  stopbuttonState = digitalRead(stopbuttonPin);                   // Check for button press, read the button state and store

// check for a high to low transition if true then found a new button press while clock is not running - start the clock   
 
   if (startbuttonState == LOW && laststartButtonState == HIGH  &&  blinking == false){

      startTime = millis();                             // store the start time
      blinking = true;                                  // turn on blinking while timing
      delay(10);                                        // short delay to debounce switch
      laststartButtonState = startbuttonState;          // store buttonState in lastButtonState, to compare next time 
   }

// check for a high to low transition if true then found a new button press while clock is running - stop the clock and report

   else if (stopbuttonState == LOW && laststopButtonState == HIGH && blinking == true){
   blinking = false;                                    // turn off blinking, all done timing
   laststopButtonState = stopbuttonState;               // store buttonState in lastButtonState, to compare next time

// Routine to report elapsed time  
          
   elapsedTime =   millis() - startTime;                // store elapsed time
   elapsedMinutes = (elapsedTime / 60000L);             // divide by 60000 to convert to minutes - then cast to an int to print
   elapsedSeconds = (elapsedTime / 1000L);              // divide by 1000 to convert to seconds - then cast to an int to print
   elapsedFrames = (elapsedTime / interval);            // divide by 100 to convert to 1/100 of a second - then cast to an int to print
   fractional = (int)(elapsedFrames % frameRate);       // use modulo operator to get fractional part of 100 Seconds
   fractionalSecs = (int)(elapsedSeconds % 60L);        // use modulo operator to get fractional part of 60 Seconds
   fractionalMins = (int)(elapsedMinutes % 60L);        // use modulo operator to get fractional part of 60 Minutes

    P.setFont(SmallDigits);
    sprintf(buff, "%2d:%02d.%02d", fractionalMins, fractionalSecs, fractional);
    P.print(buff);
    delay(10);

   }
 
 else{
      laststartButtonState = startbuttonState;                     // store buttonState in lastButtonState, to compare next time
      laststopButtonState = stopbuttonState;                       // store buttonState in lastButtonState, to compare next time
   } 

// run commands at the specified time interval
// blink routine - blink the LED while timing
// check to see if it's time to blink the LED; that is, the difference
// between the current time and last time we blinked the LED is larger than
// the interval at which we want to blink the LED.

 if ( (millis() - previousMillis > interval) ) {

    if (blinking == true){
       previousMillis = millis();                         // remember the last time we blinked the LED
 
         elapsedTime =   millis() - startTime;            // store elapsed time
         elapsedMinutes = (elapsedTime / 60000L);         // divide by 60000 to convert to minutes - then cast to an int to print
         elapsedSeconds = (elapsedTime / 1000L);          // divide by 1000 to convert to seconds - then cast to an int to print
         elapsedFrames = (elapsedTime / interval);        // divide by 40 to convert to 1/25 of a second - then cast to an int to print
         fractional = (int)(elapsedFrames % frameRate);   // use modulo operator to get fractional part of 25 Frames
         fractionalSecs = (int)(elapsedSeconds % 60L);    // use modulo operator to get fractional part of 60 Seconds
         fractionalMins = (int)(elapsedMinutes % 60L);    // use modulo operator to get fractional part of 60 Minutes

         P.setFont(SmallDigits);
         sprintf(buff, "%2d:%02d.%02d", fractionalMins, fractionalSecs, fractional);
         P.print(buff);
         delay(10);
    }
    
  }

}

void CallScrolling(){
  
  if (P.displayAnimate())
  {
    if (newMessageAvailable)
    {
      strcpy(curMessage, newMessage);
      newMessageAvailable = false;
    }
    P.displayReset();
  } 

}

while (startbuttonState = HIGH);

How do you compare values ?
Fix this first

You need to place the read of the button inside the loop, otherwise startbuttonState cannot change. And you need to learn to use == for comparison, not =

Thank you for taking the time to reply, I have put a digitalRead statement inside the loop and the timer starts fine. However when I press the stop button, the display goes blank rather than displaying the finish time and I cannot see why this is happening. I have taken on board the == instead of the =.

void loop(){

do {
CallScrolling();
digitalRead(startbuttonPin);  // **This was the only change I made to the code**
}
while (startbuttonPin == HIGH);

  startbuttonState = digitalRead(startbuttonPin);                 // Check for button press, read the button state and store
  stopbuttonState = digitalRead(stopbuttonPin);                   // Check for button press, read the button state and store

You are testing the value of the pin number not its state returned by digitalRead()

Thank you for your replies and please excuse my inexperience. I am not quite sure what you mean, the pin is set to HIGH and is waiting for a button press, making it LOW, is that not what I should be doing? The start button part is now working though, will this affect the stop button part of the code.

startbuttonPin is a number, the number of the pin, absolutely nothing to do with the state of the pin itself, it just names the pin. HIGH is an alias for the number 1, so your loop is waiting for the pin number to magically change to 1, which it won't as the variable isn't being changing in the (empty) loop body.

Perhaps you meant:

  while (digitalRead(startbuttonPin) == HIGH)
  {}

This reads the pin each time round the loop. Unless you call digitalRead your program isn't looking at the pin itself at all.

BTW this is an example of blocking code (the processor is jammed waiting for the pin to change and can't do other stuff). Often this leads to problems.

I have changed to a "while" loop and used your suggestion. If I contain all of the void loop code within the while loop, the scrolling message displays then when the start button is pressed, the timer begins displaying the increasing time. When I press the stop button the display goes blank rather than displaying the time when the stop button was pressed. Something is stopping the stop time being displayed, I must still be blocking but can't see where. I did try excluding the last part of the code which displays the stop time from the while loop but that made it worse.

// Scrolling Message Stopwatch

// include the library code:

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

#define HARDWARE_TYPE MD_MAX72XX::FC16_HW
#define MAX_DEVICES 4               // Number of 8 x 8 LED Matrix Modules used

#define DATA_PIN  11                // Pin for Arduino Nano 11
#define CS_PIN    3                 // Pin for Arduino Nano 3
#define CLK_PIN   13                // Pin for Arduino Nano 13

// Hardware SPI connection
MD_Parola P = MD_Parola(HARDWARE_TYPE, CS_PIN, MAX_DEVICES);
// Arbitrary output pins

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


int startbuttonPin = 2;                  // start button on pin 2
int stopbuttonPin = 4;                   // stop button on pin 4
int startbuttonState;                    // variable to store button state
int stopbuttonState;                     // variable to store button state
int laststartButtonState;                // variable to store last button state
int laststopButtonState;                 // variable to store last button state
int blinking;                            // condition for blinking - timer is timing
int frameRate = 100;                     // the frame rate (frames per second) at which the stopwatch runs - Change to suit
uint32_t interval = (1000/frameRate);    // blink interval
uint32_t previousMillis = 0;             // variable to store last time LED was updated
uint32_t startTime;                      // start time for stop watch
uint32_t elapsedTime;                    // elapsed time for stop watch
int fractional;                          // variable used to store fractional part of Frames
int fractionalSecs;                      // variable used to store fractional part of Seconds
int fractionalMins;                      // variable used to store fractional part of Minutes
uint32_t elapsedFrames;                  // elapsed frames for stop watch
uint32_t elapsedSeconds;                 // elapsed seconds for stop watch
uint32_t elapsedMinutes;                 // elapsed Minutes for stop watch
char buff[10];                           // a buffer that will contain the string to be displayed

uint8_t scrollSpeed = 25;    // default frame delay value
textEffect_t scrollEffect = PA_SCROLL_LEFT;
textPosition_t scrollAlign = PA_LEFT;
uint16_t scrollPause = 2000; // in milliseconds

#define  BUF_SIZE  100
char curMessage[BUF_SIZE] = { "" };
char newMessage[BUF_SIZE] = { "If you are ready to begin, press the start button!" };
bool newMessageAvailable = true;


void setup()
{
  Serial.begin(57600);
  P.begin();                             // intialise the LED Matrix.
  P.setIntensity(5);                     // Set LED Matrix Brightness
  //P.print("Start!");
  P.displayText(curMessage, scrollAlign, scrollSpeed, scrollPause, scrollEffect, scrollEffect);    // print opening message                 
  //P.setFont(SmallDigits);
  pinMode(startbuttonPin, INPUT);        // not really necessary, pins default to INPUT anyway
  pinMode(stopbuttonPin, INPUT);         // not really necessary, pins default to INPUT anyway
  digitalWrite(startbuttonPin, HIGH);    // turn on pullup resistors. Wire button so that press shorts pin to ground.
  digitalWrite(stopbuttonPin, HIGH);     // turn on pullup resistors. Wire button so that press shorts pin to ground.
  
}

void loop(){

/*do {
CallScrolling();
digitalRead(startbuttonPin);
startbuttonState = digitalRead(startbuttonPin); 
//digitalRead(stopbuttonPin);
}
while (startbuttonState == HIGH);
*/
while(digitalRead(startbuttonPin) == HIGH)
{
  CallScrolling();

  startbuttonState = digitalRead(startbuttonPin);                 // Check for button press, read the button state and store
  stopbuttonState = digitalRead(stopbuttonPin);                   // Check for button press, read the button state and store

// check for a high to low transition if true then found a new button press while clock is not running - start the clock   
 
   if (startbuttonState == LOW && laststartButtonState == HIGH  &&  blinking == false){

      startTime = millis();                             // store the start time
      blinking = true;                                  // turn on blinking while timing
      delay(10);                                        // short delay to debounce switch
      laststartButtonState = startbuttonState;          // store buttonState in lastButtonState, to compare next time 
   }

// check for a high to low transition if true then found a new button press while clock is running - stop the clock and report

   else if (stopbuttonState == LOW && laststopButtonState == HIGH && blinking == true){
   blinking = false;                                    // turn off blinking, all done timing
   laststopButtonState = stopbuttonState;               // store buttonState in lastButtonState, to compare next time

// Routine to report elapsed time  
          
   elapsedTime =   millis() - startTime;                // store elapsed time
   elapsedMinutes = (elapsedTime / 60000L);             // divide by 60000 to convert to minutes - then cast to an int to print
   elapsedSeconds = (elapsedTime / 1000L);              // divide by 1000 to convert to seconds - then cast to an int to print
   elapsedFrames = (elapsedTime / interval);            // divide by 100 to convert to 1/100 of a second - then cast to an int to print
   fractional = (int)(elapsedFrames % frameRate);       // use modulo operator to get fractional part of 100 Seconds
   fractionalSecs = (int)(elapsedSeconds % 60L);        // use modulo operator to get fractional part of 60 Seconds
   fractionalMins = (int)(elapsedMinutes % 60L);        // use modulo operator to get fractional part of 60 Minutes

    P.setFont(SmallDigits);
    sprintf(buff, "%2d:%02d.%02d", fractionalMins, fractionalSecs, fractional);
    P.print(buff);
    delay(10);

   }
 
 else{
      laststartButtonState = startbuttonState;                     // store buttonState in lastButtonState, to compare next time
      laststopButtonState = stopbuttonState;                       // store buttonState in lastButtonState, to compare next time
   } 

// run commands at the specified time interval
// blink routine - blink the LED while timing
// check to see if it's time to blink the LED; that is, the difference
// between the current time and last time we blinked the LED is larger than
// the interval at which we want to blink the LED.

 if ( (millis() - previousMillis > interval) ) {

    if (blinking == true){
       previousMillis = millis();                         // remember the last time we blinked the LED
 
         elapsedTime =   millis() - startTime;            // store elapsed time
         elapsedMinutes = (elapsedTime / 60000L);         // divide by 60000 to convert to minutes - then cast to an int to print
         elapsedSeconds = (elapsedTime / 1000L);          // divide by 1000 to convert to seconds - then cast to an int to print
         elapsedFrames = (elapsedTime / interval);        // divide by 40 to convert to 1/25 of a second - then cast to an int to print
         fractional = (int)(elapsedFrames % frameRate);   // use modulo operator to get fractional part of 25 Frames
         fractionalSecs = (int)(elapsedSeconds % 60L);    // use modulo operator to get fractional part of 60 Seconds
         fractionalMins = (int)(elapsedMinutes % 60L);    // use modulo operator to get fractional part of 60 Minutes

         P.setFont(SmallDigits);
         sprintf(buff, "%2d:%02d.%02d", fractionalMins, fractionalSecs, fractional);
         P.print(buff);
         delay(10);
    }
    
  }
}
}

void CallScrolling(){
  
  if (P.displayAnimate())
  {
    if (newMessageAvailable)
    {
      strcpy(curMessage, newMessage);
      newMessageAvailable = false;
    }
    P.displayReset();
  } 

}

A gratuitious use of do-while. Should be:

while (digitalRead(startbuttonPin) == HIGH) {
CallScrolling();
}

...assuming that either one works

Actually since

int startbuttonPin = 2;                  // start button on pin 2

you are saying

do {
CallScrolling();
digitalRead(startbuttonPin);  // **This was the only change I made to the code**
}
while (2 == HIGH);

Not only is the test faulty, but the result of the digitalRead() is thrown in the garbage can.

Thank you for your reply, both work to the same extent, the problem is that when the stop button is pressed the display goes blank rather than displaying the stop time!

No. See reply #9

I was under the impression that I was giving pin 2 the label startbuttonPin and that while (startbuttonPin == HIGH) was was talking about the state of the pin, not the number!

You are wrong. You need to read the state of the pin using digitalRead()

I have used the code:

while(digitalRead(startbuttonPin) == HIGH)

So when the button is pressed it will read as LOW.
This allows the timer to start and this displays on the LED matrix as it should. When the stop button is pressed, it should stop the timer and display the finish time but the display goes blank. I works fine without the scrolling message element in the code. It looks like the stop button does stop the timer, it just does not display the time!

Prior to adding the scrolling message part, when the stop button was pressed, the time would be displayed until the start button was pressed again or the program was reset. Now having looked at the results on the serial monitor, I can see that the stop time is there but only displays for 10ms hence the blank display. By increasing the delay I can see the time on the LED Matrix, so the while loop is causing this issue, just need to figure out a way around this.

Sketch now working with scrolling message, start, stop and reset functions. Thanks for the input guys, much appreciated.

Please post the working version to help anyone with the same problem in the future