Remote to start coutdown using TM1637

Hi,
Bad english and first time writing in this forum. Hop you can understand:-)

I am using an Arduino Mega to control a briefcase bomb for an home made Escape Room.
When powering, the 4 digit on the TM1637 should show 60:00 and when triggered by a remote the countdown from 60 minutes should begin. When reaching 00:00 the numbers blink 10 times and stop.
I have a keypad and a joystick that can stop the timer. These are working fine, so I havent put them in my code here.

My problems are:

  1. When reaching about 35:25 the timer starts over from 60:00. This is my main concern. I think it is because I can not figure out how to get a constant value from Millis() when starting with my remote, but I am not sure

  2. I can not get a colon from the beginning. The display shows 6000 and not 60:00. I have tried using “display.showNumberDecEx(0,64);” I get my colon, but the 3 first digits and the colon is flickering.

  3. A blinking colon through out the 60 minutes would be nice, but how?

Here is my code:

// remote---------------------Remote setup--------------------------------------
#include <IRremote.h>

const int RECV_PIN = 16;
IRrecv irrecv(RECV_PIN);
decode_results results;



// millis setup----------------------------Millis setup----------------------------------------

unsigned long newMillis = 0;
unsigned long starttime = 0;
int start = 0;
int ending = 0;



//countdown------------------------------Countdown setup-------------------------------------

// DEFINES
// Macros to retrieve the fractional seconds and minute parts of a time
// supplied in ms
#define numberOfSeconds(_time_) ((_time_ / 1000) % 60)  
#define numberOfMinutes(_time_) (((_time_ / 1000) / 60) % 60) 

// INCLUDES
// https://github.com/avishorp/TM1637
#include <TM1637Display.h>

// CONSTANTS
const uint8_t OFF[] = {0, 0, 0, 0};
// In this library, the byte order is .GFEDCBA
const uint8_t SLUT[] = {B0111111, B0111111, B0111111, B0111111};
const uint8_t START[] = {B1111101, B0111111, B0111111, B0111111};

// GLOBALS
// Create a display object, specifying parameters (Clock pin, Data pin)
TM1637Display display(14, 15);


//set timer-----------------------set timer-----------------------------------------------

// 1000ms in 1sec, 60secs in 1min, 60mins in 1hr. So, 1000x60x60 = 3600000ms = 1hr
unsigned long timeLimit = 3600000;

// Calculate the time remaining 
  unsigned long timeRemaining = 3600000;
  


//--------------------------------------------SETUP--------------------------------------------


void setup(){
// remote
  irrecv.enableIRIn();
  
//countdown

  Serial.begin(9600);
  // Set brightness
  display.setBrightness(0x0c);
  // Clear the display
  display.setSegments(OFF);
}



void displayText() {
 display.setSegments(SLUT);
  }

void displayText2() {
 display.setSegments(START);
  }
  
void displayText3() {
 display.setSegments(OFF);
}


//------------------------------------------------LOOP------------------------------------------


void loop(){
   

if(start == 0)
{displayText2();
}

if(start == 1)
{starttime = millis();}

newMillis = millis() - starttime;

//remote


if(irrecv.decode(&results))
        irrecv.resume();
        switch(results.value)
        {
        
        case 0xFFC23D: //Keypad button "play/pause"
        start = start + 1;



if(timeRemaining > 100) {
    // To display the countdown in mm:ss format, separate the parts
    int seconds = numberOfSeconds(timeRemaining);
    int minutes = numberOfMinutes(timeRemaining); 
  
    // This displays the seconds in the last two places
    display.showNumberDecEx(seconds, 0, true, 2, 2);
    // Display the minutes in the first two places, with colon
    display.showNumberDecEx(minutes, 0x80>>3, true, 2, 0);

    // Update the time remaining
    
    timeRemaining = timeLimit - newMillis;
}

else{
  displayText();
  delay(300);
  displayText3();
  delay(300);
  ending = ending + 1;
}
  if(ending == 10)
  {
  exit(0);  
      }

        } }

There is a problem with helping you saying this is for a game.

Other not so trust worthy people can use the same code for nefarious reasons.

is your friend.

Study and master the ‘Blink Without Delay’ BWD example that comes with the IDE.

Don’t use delay().

I have studied the 'Blink Without Delay´ a lot but I am sure not a Master yet;-)

I have tried to delete the delays, and still the same outcome. My timer stops at around 35:25.

Did you remove the two capacitors from your display PCB?

You really need to watch a few things.

Since ‘start’ kept incrementing, it eventually came back to 1 which reset your timing.

Always have a ‘break’ with your ‘case’ inside your ‘switch’.

Using macros can make your sketches difficult to read.

Always use to format your code.

// remote---------------------Remote setup--------------------------------------
#include <IRremote.h>

const int RECV_PIN = 16;
IRrecv irrecv(RECV_PIN);
decode_results results;

// millis setup----------------------------Millis setup----------------------------------------

unsigned long newMillis = 0;
unsigned long starttime = 0;

int start = 0;
int ending = 0;

//countdown------------------------------Countdown setup-------------------------------------

// DEFINES
// Macros to retrieve the fractional seconds and minute parts of a time
// supplied in ms
//#define numberOfSeconds(_time_) ((_time_ / 1000) % 60)
//#define numberOfMinutes(_time_) (((_time_ / 1000) / 60) % 60)

// INCLUDES
// https://github.com/avishorp/TM1637
#include <TM1637Display.h>

// CONSTANTS
const uint8_t OFF[] = {0, 0, 0, 0};
// In this library, the byte order is .GFEDCBA
const uint8_t SLUT[] = {B0111111, B0111111, B0111111, B0111111};
const uint8_t START[] = {B1111101, B0111111, B0111111, B0111111};

// GLOBALS
// Create a display object, specifying parameters (Clock pin, Data pin)
TM1637Display display(14, 15);


//set timer-----------------------set timer-----------------------------------------------

// 1000ms in 1sec, 60secs in 1min, 60mins in 1hr. So, 1000x60x60 = 3600000ms = 1hr
unsigned long timeLimit = 3600000ul;

// Calculate the time remaining
unsigned long timeRemaining = 3600000ul;


//--------------------------------------------SETUP--------------------------------------------
void setup()
{
  // remote
  irrecv.enableIRIn();

  //countdown

  Serial.begin(9600);
  // Set brightness
  display.setBrightness(0x0c);
  // Clear the display
  display.setSegments(OFF);

} //END of setup()

//***************************************************
void displayText()
{
  display.setSegments(SLUT);
}

void displayText2()
{
  display.setSegments(START);
}

//***************************************************
void displayText3()
{
  display.setSegments(OFF);
}


//------------------------------------------------LOOP------------------------------------------
void loop()
{
  if (start == 0)
  {
    displayText2();
  }

  if (start == 1)
  {
    starttime = millis();    
  }

  newMillis = millis() - starttime;


  //remote
  if (irrecv.decode(&results))
    irrecv.resume();
  
  switch (results.value)                
  {
    case 0xFFC23D: //Keypad button "play/pause"           
      start = start + 1;
      
      if(start > 2)
      {
        //do not go over 2
        start = 2;
      }

      if (timeRemaining > 100)
      {
        // To display the countdown in mm:ss format, separate the parts
        //int seconds = numberOfSeconds(timeRemaining);
        int seconds = (timeRemaining / 1000) % 60;
        
        //int minutes = numberOfMinutes(timeRemaining);
        int minutes = ((timeRemaining / 1000) / 60) % 60;

        // This displays the seconds in the last two places
        display.showNumberDecEx(seconds, 0, true, 2, 2);
        // Display the minutes in the first two places, with colon
        display.showNumberDecEx(minutes, 0x80 >> 3, true, 2, 0);

        // Update the time remaining

        timeRemaining = timeLimit - newMillis;
      }

      else
      {
        displayText();
        delay(300);
        displayText3();
        delay(300);
        ending = ending + 1;
      }
      if (ending == 10)
      {
        exit(0);
      }
      
  } //END of  switch (results.value)   
  
} //END of loop()

A re-imagining of your code using a state machine to do the countdown and blinking.

It compiles and runs on a 2560 but I didn’t have a chance to test the OR or display driver. The output to the serial monitor looks promising.

The colon is intended to toggle each half-second during the count. I don’t know if your original code showed the colon properly. If not, we can try working on that.

#include <IRremote.h>
#include <TM1637Display.h>      // https://github.com/avishorp/TM1637

const int RECV_PIN = 16;
IRrecv irrecv(RECV_PIN);
decode_results results;
TM1637Display display(14, 15);

// CONSTANTS
const uint8_t OFF[] = {0, 0, 0, 0};
// In this library, the byte order is .GFEDCBA
//const uint8_t SLUT[] = {B0111111, B0111111, B0111111, B0111111};
//const uint8_t START[] = {B1111101, B0111111, B0111111, B0111111};
// 1000ms in 1sec, 60secs in 1min, 60mins in 1hr. So, 1000x60x60 = 3600000ms = 1hr
const unsigned long timeLimit = 3600000ul;
//const unsigned long timeLimit = 300000ul;     //5-min for debug


void setup()
{
    Serial.begin(9600);  
    // Set brightness
    display.setBrightness(0x0c);
    // Clear the display
    display.setSegments(OFF);
    
}//setup


void loop()
{
    countdownStateMachine();
     
}//loop

//state machine states
#define INIT        0
#define WAIT_START  1
#define COUNT_DOWN  2
#define BLINK       3
#define HOLD        4

void countdownStateMachine( void )
{
    static unsigned int
        minutes,
        seconds,
        tenths,
        last_minutes = -1,
        last_seconds = -1,
        last_tenths = -1;
    static bool
        bBlinkState = true;
    static byte
        ending,
        stateCD = INIT;
    static unsigned long
        timeCD;
    unsigned long
        timeElapsed,
        timeLeft,
        timeNow;

    switch( stateCD )
    {
        case    INIT:
            //This displays the initial 60:00, while waiting for the remote command to
            //start the countdown
            display.showNumberDecEx( 0, 0, true, 2, 2);
            // Display the minutes in the first two places, with colon
            display.showNumberDecEx( 60, 0x80>>3, true, 2, 0);
            DebugPrint( 60, 0, 0 );
            stateCD = WAIT_START;
            
        break;

        case    WAIT_START:
            //stateCD = COUNT_DOWN;         //debug
            if(irrecv.decode(&results))
            {
                irrecv.resume();
                if( results.value == 0xFFC23D )
                {
                    stateCD = COUNT_DOWN;
                    timeCD = millis();
                    
                }//if
                
            }//if
             
        break;
        
        case    COUNT_DOWN:
            timeElapsed = millis() - timeCD;
            timeLeft = timeLimit - timeElapsed;

            minutes = timeLeft / 60000;
            timeLeft = timeLeft - (minutes * 60000);
            seconds = timeLeft / 1000;
            timeLeft = timeLeft - (seconds * 1000);
            tenths = timeLeft / 100;
            
            if( tenths != last_tenths )
            {
                DebugPrint( minutes, seconds, tenths );
                display.showNumberDecEx( seconds, 0, true, 2, 2);
                //toggle the colon every half-second
                if( tenths >= 5 )
                    display.showNumberDecEx( minutes, 0x80>>3, true, 2, 0);
                else
                    display.showNumberDecEx( minutes, 0, true, 2, 0);
                last_minutes = minutes;
                last_seconds = seconds;
                last_tenths = tenths;                
                
            }//if

            //done countdown?
            if( minutes == 0 && seconds == 0 && tenths == 0 )
            {
                //yes; get timer to start blinking and set next state to blink
                timeCD = millis();
                stateCD = BLINK;
                ending = 10;
                
            }//if
            
        break;

        case    BLINK:
            //check if it's time to toggle the blink state
            timeNow = millis();
            if( (timeNow - timeCD) <= 300 )
                return;
            timeCD = timeNow;

            //toggle the blink flag (true=on false=off)
            bBlinkState ^= true;
            if( !bBlinkState )
            {
                //turn segs off for blink
                Serial.println();           //in debug, simulate 'off' with blank line
                display.setSegments(OFF);
                ending--;
                if( ending == 0 )
                    stateCD = HOLD;
                    
            }//if
            else
            {
                //turn digits back on for blink
                DebugPrint( minutes, seconds, tenths );
                display.showNumberDecEx( seconds, 0, true, 2, 2);
                display.showNumberDecEx( minutes, 0x80>>3, true, 2, 0);
                
            }//else
                
        break;

        case    HOLD:
            //do nothing after last blink
            
        break;
        
    }//switch
    
}//countdownStateMachine

void DebugPrint( int minutes, int seconds, int tenths )
{
    if( minutes < 10 )
        Serial.print( "0" );
    Serial.print( minutes );

    if( tenths >= 5 )
        Serial.print( ":" );
    else
        Serial.print( " " );
    
    if( seconds < 10 )
        Serial.print( "0" );
    Serial.print( seconds );
    Serial.print( "." );
    Serial.println( tenths );

}//DebugPrint

Thanks larryd and Blackfin I really appreciate your help.

My code runs as it should after writing (if(start>2)) as you suggested larryd.

To your code Blackfin. I can not start it with my remote. It will only show 60:00.

As where I am in my coding process (beginner) I do not quite understand all of your code Blackfin. I will try to look in to it, and learn to understand, but for now I will go with my own code. Is there an easy way to get the 60:00 instead of 6000 when powering the arduino?

To get the IR to work here I had to move to a different pin (2 in my case) and add a line to setup.

Try this to see if the colon works too:

#include <IRremote.h>
#include <TM1637Display.h>      // https://github.com/avishorp/TM1637

const int RECV_PIN = 2;
IRrecv irrecv(RECV_PIN);
decode_results results;
TM1637Display display(14, 15);

#define COLON       (0x80>>1)

// CONSTANTS
const uint8_t OFF[] = {0, 0, 0, 0};
// In this library, the byte order is .GFEDCBA
//const uint8_t SLUT[] = {B0111111, B0111111, B0111111, B0111111};
//const uint8_t START[] = {B1111101, B0111111, B0111111, B0111111};
// 1000ms in 1sec, 60secs in 1min, 60mins in 1hr. So, 1000x60x60 = 3600000ms = 1hr
const unsigned long timeLimit = 3600000ul;
//const unsigned long timeLimit = 300000ul;     //5-min for debug


void setup()
{
    Serial.begin(9600);  
    // Set brightness
    display.setBrightness(0x0c);
    irrecv.enableIRIn();
    irrecv.blink13(true);
    // Clear the display
    display.setSegments(OFF);

    int value = (1234 / 100)%10;
    Serial.println( value );
    
    
}//setup


void loop()
{
    countdownStateMachine();
    
}//loop

//state machine states
#define INIT        0
#define WAIT_START  1
#define COUNT_DOWN  2
#define BLINK       3
#define HOLD        4

void countdownStateMachine( void )
{
    static unsigned int
        minutes,
        seconds,
        tenths,
        last_minutes = -1,
        last_seconds = -1,
        last_tenths = -1;
    static bool
        bBlinkState = true;
    static byte
        ending,
        stateCD = INIT;
    static unsigned long
        timeCD;
    unsigned long
        timeElapsed,
        timeLeft,
        timeNow;

    switch( stateCD )
    {
        case    INIT:
            //This displays the initial 60:00, while waiting for the remote command to
            //start the countdown
            display.showNumberDecEx( 0, 0, true, 2, 2);
            // Display the minutes in the first two places, with colon
            display.showNumberDecEx( 60, COLON, true, 2, 0);
            DebugPrint( 60, 0, 0 );
            stateCD = WAIT_START;
            
        break;

        case    WAIT_START:
            //stateCD = COUNT_DOWN;         //debug
            if(irrecv.decode(&results))
            {
                irrecv.resume();
                Serial.print("Got " ); Serial.println( results.value,HEX );
                if( results.value == 0xFFC23D )
                {
                    stateCD = COUNT_DOWN;
                    timeCD = millis();
                    
                }//if
                
            }//if
            
            
                         
        break;
        
        case    COUNT_DOWN:
            timeElapsed = millis() - timeCD;
            timeLeft = timeLimit - timeElapsed;

            minutes = timeLeft / 60000;
            timeLeft = timeLeft - (minutes * 60000);
            seconds = timeLeft / 1000;
            timeLeft = timeLeft - (seconds * 1000);
            tenths = timeLeft / 100;
            
            if( tenths != last_tenths )
            {
                DebugPrint( minutes, seconds, tenths );
                display.showNumberDecEx( seconds, 0, true, 2, 2);
                //toggle the colon every half-second
                if( tenths >= 5 )
                    display.showNumberDecEx( minutes, COLON, true, 2, 0);
                else
                    display.showNumberDecEx( minutes, 0, true, 2, 0);
                last_minutes = minutes;
                last_seconds = seconds;
                last_tenths = tenths;                
                
            }//if

            //done countdown?
            if( minutes == 0 && seconds == 0 && tenths == 0 )
            {
                //yes; get timer to start blinking and set next state to blink
                timeCD = millis();
                stateCD = BLINK;
                ending = 10;
                
            }//if
            
        break;

        case    BLINK:
            //check if it's time to toggle the blink state
            timeNow = millis();
            if( (timeNow - timeCD) <= 300 )
                return;
            timeCD = timeNow;

            //toggle the blink flag (true=on false=off)
            bBlinkState ^= true;
            if( !bBlinkState )
            {
                //turn segs off for blink
                Serial.println();           //in debug, simulate 'off' with blank line
                display.setSegments(OFF);
                ending--;
                if( ending == 0 )
                    stateCD = HOLD;
                    
            }//if
            else
            {
                //turn digits back on for blink
                DebugPrint( minutes, seconds, tenths );
                display.showNumberDecEx( seconds, 0, true, 2, 2);
                display.showNumberDecEx( minutes, COLON, true, 2, 0);
                
            }//else
                
        break;

        case    HOLD:
            //do nothing after last blink
            
        break;
        
    }//switch
    
}//countdownStateMachine

void DebugPrint( int minutes, int seconds, int tenths )
{
    if( minutes < 10 )
        Serial.print( "0" );
    Serial.print( minutes );

    if( tenths >= 5 )
        Serial.print( ":" );
    else
        Serial.print( " " );
    
    if( seconds < 10 )
        Serial.print( "0" );
    Serial.print( seconds );
    Serial.print( "." );
    Serial.println( tenths );

}//DebugPrint

Hope you understand what the problem was.

Since your original sketch kept adding 1 to ‘start’, after ~35 minutes, ‘start’ went from 1 thru all the values an integer can hold, then it overflowed and again back to 1.

When ‘start’ got back back to 1, your code again set 60:00 into your timing variable.

Thank you for the explanation larryd. I now understand, what was wrong.

And Blackfin. Your code is now running on my arduino, but no colon.

I took a part from your code:

if
  (starttid == 0)
  { display.showNumberDecEx(0, 0, true, 2, 2);           
    display.showNumberDecEx(60, 0x80 >> 3, true, 2, 0);
  }

and put in the loop, and I now got the colon from the start. No blinking colon, but else it is all perfect, and I understand most of it :wink:

How do I close this topic and give you both some credit?

In my code, can you try changing:

#define COLON      (0x80>>1)

to

#define COLON       (0x80>>3)

and see if that gets the colon flashing?

Perfect - colon is now flashing:-)

Thanks