Random zero appears in display!

I have adapted a sketch for a stopwatch using a 32 x 8 LED Matrix. If the fractional part is less than 10, it puts a zero before the digit. This works fine until roughly 5 minutes and 27 seconds after which a zero appears before the two digit number and does not go away. I know the code is a bit untidy as I have been tweaking it as I go in order to get it to work. Can anyone see why after 5 minutes and 27 seconds it prefixes the fractional part with a zero even when the fractional part is greater than 10? I am tearing my hair out trying to find the error! Any help would be very much appreciated.

Thank you in advance

Steve

/*        
 *         Adapted Sketch October 2021
*/

// include the library code:

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

#include "SmallDigits.h"

#include <SafeString.h>

createSafeString(strOne, 20);
createSafeString(strTwo, 20);
createSafeString(strThree, 20);
createSafeString(strFour, 20);
createSafeString(strFive, 20);
createSafeString(strSix, 20);

// 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 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 ledPin = 8;                     // LED connected to digital pin 8
int buttonPin = 2;                  // button on pin 2
int value = LOW;                    // previous value of the LED
int buttonState;                    // variable to store button state
int lastButtonState;                // 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
long interval = (1000/frameRate);   // blink interval
long previousMillis = 0;            // variable to store last time LED was updated
long startTime;                     // start time for stop watch
long 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
int elapsedFrames;                  // elapsed frames for stop watch
int elapsedSeconds;                 // elapsed seconds for stop watch
int elapsedMinutes;                 // elapsed Minutes for stop watch

void setup()
{
  Serial.begin(57600);
  P.begin();                        // intialise the LED Matrix.
  P.setIntensity(5);                // Set LED Matrix Brightness
  P.print("Start!");                // print opening message
  pinMode(ledPin, OUTPUT);          // sets the digital pin as output
  pinMode(buttonPin, INPUT);        // not really necessary, pins default to INPUT anyway
  digitalWrite(buttonPin, HIGH);    // turn on pullup resistors. Wire button so that press shorts pin to ground.
  digitalWrite(ledPin, HIGH);       // LED Check
  delay(1000);
  P.setFont(SmallDigits);
}

void loop(){
  digitalWrite(ledPin, LOW);              // Initiate LED and Step Pin States

  buttonState = digitalRead(buttonPin);   // 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 (buttonState == LOW && lastButtonState == HIGH  &&  blinking == false){
      startTime = millis();                             // store the start time
      blinking = true;                                  // turn on blinking while timing
      delay(10);                                        // short delay to debounce switch
      lastButtonState = buttonState;                    // 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 (buttonState == LOW && lastButtonState == HIGH && blinking == true){
   blinking = false;                                    // turn off blinking, all done timing
   lastButtonState = buttonState;                       // 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
   
   strOne = fractionalSecs;
   strFour = 0;
   strSix = 0;
   
   if(fractionalSecs < 10){
      strOne = strFour += fractionalSecs; 
      }
   
   strFive = " ";
   strTwo = fractionalMins;
   strTwo = strFive += strTwo;
   strThree = fractional;
   
   if(fractional < 10){
      strThree = strSix += strThree; 
      }
      
   strTwo = strTwo += ":";
   strTwo = strTwo += strOne;
   strTwo = strTwo += ":";
   strTwo = strTwo += strThree;
   Serial.println(strTwo);
   P.print(strTwo);
   delay(10);

   }
 
 else{
      lastButtonState = buttonState;                  // 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

       digitalWrite(ledPin, HIGH);                     // Pulse the LED for Visual Feedback

         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

         strOne = fractionalSecs;
         strFour = 0;
         strSix = 0;
         
         if(fractionalSecs < 10){
            strOne = strFour += fractionalSecs; 
            }
            
         strFive = " ";
         strTwo = fractionalMins;
         strTwo = strFive += strTwo;
         strThree = fractional;
         
         if(fractional < 10){
            strThree = strSix += strThree; 
            }
            
         strTwo = strTwo += ":";
         strTwo = strTwo += strOne;
         strTwo = strTwo += ":";
         strTwo = strTwo += strThree;

         Serial.println(strTwo);
         P.print(strTwo);
         delay(10);
    }

    else{
          digitalWrite(ledPin, LOW);                   // turn off LED when not blinking 
          }      
 }

}

Maybe something to do with ((5*60) + 27) * 100 being very close to 32767 which is the max value of a signed 16 bits variable before it overflows.. :slight_smile:

It seems really complex. Can you explain what you want the display to show? Give us some examples of what that display should look like for different ranges of time.

Thanks for your reply, I just tried changing the Long variables to long unsigned int variables, the issue remains.

  • 5 minutes + 27 seconds is 327000 milliseconds

  • You divide this number by interval which is (1000/100) = 10 (not 100 as the comment says) so the result is close to the max of a signed 16 bits int

  • You store the result in elapsedFrame which is a signed 16 bits int.

So a few milliseconds later, elapsedFrame will be negative and the rest of your code will break

One solution is to use a type that can hold a much bigger value, like uint32_t (unsigned long)

Here is an example that should demonstrate the problem

int frameRate = 100; 
long interval = (1000/frameRate);
long elapsedTime;
int elapsedFrames;

void setup()
{
  Serial.begin(57600);
}

void loop()
{
  elapsedTime = micros();
  elapsedFrames = (elapsedTime / interval); // divide by 10, not 100
  Serial.print( "elapsedTime = " );
  Serial.print( elapsedTime );
  Serial.print( " elapsedFrames = " );
  Serial.println( elapsedFrames );
}

Then change int elapsedFrames; to uint32_t elapsedFrames; and see the difference

Here is a picture of the display.


It shows minutes:seconds: hundredths of seconds. It puts a zero before the hundredths if it is less than 10, so rather than 1:23:3 it displays 1:23:03. This works fine for 5 minutes and 27 seconds then is displays 1:34:022 ie there is a zero before the hundredths even if they are greater than 10. Only the edge of the final digit appears though.

Ok, I get what you are saying.

I should tell you, it is not normal to separate seconds from hundredths of seconds with a colon. Normally a decimal point is used for this. A colon is normally used to separate hours from minutes or minutes from seconds, in other words where the is a difference of 60x between a "1" on the left side of the colon and a "1" on the right colon. Where there is a difference off 100x, it's more normal to use a decimal point. But this is your project...

I would simplify your code by using the sprintf() function:

char buff[10];
...
sprintf(buff, "%02d:%02d.%02d", mins, secs, hundredths);
...
P.print(buff);

Thank you for your reply. I tried sprint to begin with as it seemed straightforward, but it did not work at all. I will revisit it if I can't get a solution using my current code.

I have tried changing the long variables to long unsigned int variables then to uint32_t variables, the result however is still the same! Really odd!

You are incorrect, it works perfectly. You did not use it correctly. This is the only thing I can assume, since you did not post your code where you attempted to use sprintf().

Sorry, what I meant to say was that I couldn't get it to work, it was a while ago and I did not keep the code, I am happy to try it again though. I take it then that I am using the fractionalMins, fractionalSecs and fractional where you have used mins, secs and hundredths? Where does the sprint() line go? I tried it just above the P.print line but the sketch no longer works when I press the start button, the display just sticks on Start!

Enough. Good luck with your project.

Apologies if I have upset you in any way, it was not my intention. I have stripped out all the SafeString() code and used sprint() code instead and I have managed to get it to work as before. However as before, when it reaches about 5 minutes and 27 seconds, it drops the padded zero, see photo at 6 minutes 2 seconds and 8 hundredths.


I know I have put in a colon between seconds and hundredths but the decimal point did not show up, I need to edit the smalldigits.h file to get that to work, which I will do. My new code is shown below and it would be much appreciated if you could have a look to see if there is any reason for the padded zero disappearing after 5 mins 27 secs.

EDIT: Decimal point sorted

/*        
 *         Adapted Sketch October 2021
*/

// include the library code:

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

#include "SmallDigits.h"

// 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 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 ledPin = 8;                     // LED connected to digital pin 8
int buttonPin = 2;                  // button on pin 2
int value = LOW;                    // previous value of the LED
int buttonState;                    // variable to store button state
int lastButtonState;                // 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
int elapsedFrames;                  // elapsed frames for stop watch
int elapsedSeconds;                 // elapsed seconds for stop watch
int elapsedMinutes;                 // elapsed Minutes for stop watch

char buff[10];                      // I take it this line is OK here?

void setup()
{
  Serial.begin(57600);
  P.begin();                        // intialise the LED Matrix.
  P.setIntensity(5);                // Set LED Matrix Brightness
  P.print("Start!");                // print opening message
  pinMode(ledPin, OUTPUT);          // sets the digital pin as output
  pinMode(buttonPin, INPUT);        // not really necessary, pins default to INPUT anyway
  digitalWrite(buttonPin, HIGH);    // turn on pullup resistors. Wire button so that press shorts pin to ground.
  digitalWrite(ledPin, HIGH);       // LED Check
  delay(1000);
  P.setFont(SmallDigits);
}

void loop(){
  digitalWrite(ledPin, LOW);              // Initiate LED and Step Pin States

  buttonState = digitalRead(buttonPin);   // 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 (buttonState == LOW && lastButtonState == HIGH  &&  blinking == false){
      startTime = millis();                             // store the start time
      blinking = true;                                  // turn on blinking while timing
      delay(10);                                        // short delay to debounce switch
      lastButtonState = buttonState;                    // 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 (buttonState == LOW && lastButtonState == HIGH && blinking == true){
   blinking = false;                                    // turn off blinking, all done timing
   lastButtonState = buttonState;                       // 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

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

   }
 
 else{
      lastButtonState = buttonState;                  // 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

       digitalWrite(ledPin, HIGH);                     // Pulse the LED for Visual Feedback

         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

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

    else{
          digitalWrite(ledPin, LOW);                   // turn off LED when not blinking 
          }      
 }

}


I really appreciate you taking the time to reply in the first place and I again apologise for upsetting you.

Best regards

Steve

I've explained your problem, and the solution, in reply #5 ... Read it again !

Here is the output of your original code, when printing in the Serial monitor instead of your display

...
5:25:77
5:27:52
5:29:0-10
5:31:0-1
5:33:0-9
...

Here is the output of your modified code using sprintf, when printing in the Serial monitor instead of your display

...
5:25:33
5:27:07
5:28:-55
5:30:-64
5:32:-72
...

Why you don't see the minus '-' character on your display, you may ask ? Well this is the font that you are using MD_Parola_Fonts/SmallDigits.h at master · maxbanton/MD_Parola_Fonts · GitHub and as you can see, this character is not defined, that's why it's not displayed.

Once again, thank you for your reply, I read your previous reply #5 and obviously missed something. I have now changed elapsedFrames to uint32_t and it works fine now. Your help is very much appreciated.

Best regards

Steve

Good :slight_smile:

And can you explain how you did to display the decimal point ?

The decimal point was not defined in smalldigits.h so checking the ASCII table, I then defined character 46 as 1, 64 that is 1 column wide with 64 for the 7th LED to be illuminated.

Well done. And apologies for losing my patience with you.

I find it infuriating when a change is suggested and the reply comes back "it doesn't work" without posting the updated code showing how the suggestion was implemented (usually incorrectly) and often with no real explanation of exactly what went wrong: what happened vs. what was expected.

Frequently, we get the "your suggestion did not work" and "I'm too dumb to even attempt your suggestion" responses from posters who are lazy and want the forum members to do all the coding for them, want the solution handed to them on a silver plate, which I despise. I'm very much in the "teach a man to fish" camp.

1 Like

In future, I will be sure to show my code when I am looking for help and also when I change things. I now have a version of the sketch working using sprintf() and a version using SafeString(), the sprint() version is much less complicated. I don't know what I was doing when I tried it earlier but I just could not get it to work. Thanks again for your assistance.