4 Digit Seven Segment Display Countdown Timer - Minutes to

I'm guessing I can program the 328 on my current board (has a socket'ed 328) and just put in to your board for testing? OK, a bit of messing about but it still makes for a neat solution.

Sounds good to me.

Once you confirm I'll get a list of parts ordered ready for when it arrives (the shift registers should be here today or tomorrow). Admittedly, a populated board would be simpler but happy to have a proper board (as was going to use breadboard for final solution).

Thanks

Dave

Preprogramming is one way to go.
I find it a lot easier to plug one of these on, download, and pull it off again.
No chance to damage pins that way.

I made up a little pcb that I use for 7 seg displays, it is stand alone ( program the 328 on your arduino, then plug it in the board)
I have put some 10 pin idc headers on it, 4 for the first 4 digits ( as in a clock ), and one on the right of the board to carry on to more numbers if needed ( each digit has a TPIC6B595 high power latch driver that can drive massive digits )
The headers have 7 pins for segments a-g, and 3 pins for the LED supply.

There is another header on the left for gen purpose I/O to some of the pins of the micro. I use these for connecting to wireless / push buttons/ GPS / whatever.

There is also provision for a couple of power fets for switching other LEDs, horn , motor etc.

The board has its own 5v regulator, I generally supply 12v ( depending on the number of LEDs in series for each segment )

I can post the gerber files if anyone wants to make these, or I could perhaps supply the boards if anyone is interested.

I have a a simple sketch for the countdown bomb, with a couple of push buttons to preset the start time, and a run/pause button.

Sorry Crossroads,

I didnt see the pic of your one, its a lot more versatile than my simple one ! I will probably buy some boards from you next time ( I have just ordered another load of mine )

Looks good Boffin. I was going to have separate boards (arduino plus driver board) but it looks like you both have good single board options whihc is much neater.

I'd love a copy of the countdown sketch as that is exactly what I am looking to do (will save me many hours of programming\head-scratching XD ).

Dave

Heres a v22 sketch for the " Bollywood Bomb " that I have scratched up from another project of mine. I havn't tested this version, I dont have time to connect up the switches and display at the moment, but it should basically work, but if you try it we can debug any problems.

You could swap the pin numbers to work on Crossroads allsinging serial board.

For some reason it won't compile without the VirtualWire library , so I left it in.

Connect four pushbuttons to negative for the run pause plus and minus setting buttons.

It drives 4 x TPIC6B595 serial register/drivers using shiftout, I have made comments about which pins go to which LED segments.
the " spare" output of the TPIC for minutes units connects to the two dots between the mins and secs, which stays on while counting down, and flashes when paused ( you could swap it round if you ant the opposite effect.)
I have put in a " bombPin " output that you could drive an LED to light for 2 seconds ( or a sounderwhatever ) when countdown reaches zero.

// Boffin1's  countdown " bomb " timer with preset up down run and pause
//   connect dots  to spare TPIC output on minute units chip, on when counting down, flash when paused
#include <VirtualWire.h>
#define latchPin 19  // rck
#define clockPin 18  // sck
#define dataPin 16   // ser in

int tile [4];
//  bitmap for which leds segements ( TPIC pins ) are lit for numbers 0 - 9, first bit for dots
// this pattern will depend on how the segments are wired up
//       this pattern is for  sequence   =   dots,  e,  d,  c,  g,  b,  f,  a
const byte digitTable [10] = { 
  B01110111, B00010100, B01101101, B00111101, B00011110, B00111011, B01111011, B00010101, B01111111,B00111111 } 
;   

const byte blanked = B00000000; 
int dotState;
int pause = HIGH;
int blank = HIGH;
int mintens = 0;
int minunits = 0;
int sectens = 0;
int secunits = 0;
int timeleft = 0;              
unsigned long previousdotMillis = 0;  
unsigned long previousMillis = 0;     
int mintendisp; 
int minunitsdisp;
int sectensdisp;
int secunitsdisp;  
int plusPin = 4;
int minusPin = 5;
int runPin = 6;
int pausePin = 7;
int running;
int bombPin = 8;

//******************************************************        
void setup()
{
  Serial.begin(9600);	
  blank == HIGH;
  Serial.println("setup");
  pinMode ( latchPin, OUTPUT);
  pinMode ( clockPin, OUTPUT);
  pinMode ( dataPin, OUTPUT); 
  pinMode ( plusPin, INPUT); 
  digitalWrite(plusPin, HIGH); // set pullups
  pinMode ( minusPin, INPUT); 
  digitalWrite(minusPin, HIGH);
  pinMode ( runPin, INPUT); 
  digitalWrite (runPin, HIGH);
  pinMode ( pausePin, INPUT); 
  digitalWrite(pausePin, HIGH);

  digitalWrite(latchPin, LOW);      // blanking all displays
  for ( int k=0; k<=3; k++ ){
    shiftOut(dataPin, clockPin, LSBFIRST, blank); 
    delay (5);
  }
  digitalWrite(latchPin, HIGH);  
  pinMode ( bombPin, INPUT); 
  digitalWrite(bombPin, LOW);
}
//********************************************************************
void loop () 
{  

  checktime();

  if ( mintens + minunits + sectens + secunits  >= 1 ){ 
    timeleft = HIGH;
  }    // check again after countdown
  else 
  { 
    timeleft = LOW ; 
    pause = HIGH;   
  }     //  flashing dots
  unsigned long currentdotMillis = millis();
  if(currentdotMillis - previousdotMillis > 500) {
    previousdotMillis = currentdotMillis;    
    if (dotState == LOW)
    {
      dotState = HIGH;
    }    
    else     { 
      dotState = LOW;
    }  
    showtime ();
  }
  if ( pause == LOW ) { 
    dotState = HIGH ;
  }

  

  checkbutton ();    


}//  end of loop


void checktime () {
  if ( mintens + minunits + sectens + secunits  >= 1 )  //  if time still left on countdown
  { 
    timeleft = HIGH;
  } 
  else { 
    timeleft = LOW ; 
    pause = HIGH; 
  }

  if (timeleft == HIGH ) {
    if ( pause == LOW ) {  //  which means its counting down   i.e. not paused in countdown          
      static unsigned long previousMillis;
      if(millis() > previousMillis)
      { 
        previousMillis = millis() + 1000; 
        secunits -- ;             // countdown 1 sec 
        if ( secunits  < 0 ){ 
          secunits = 9;  
          sectens -- ;
        }
        if ( sectens   < 0 ){ 
          sectens = 5;  
          minunits -- ;
        }
        if ( minunits  < 0 ){ 
          minunits = 9;  
          mintens -- ;
        } 
        // mintens cant get to -1 or it would have to have been 0000 and paused
        Serial.print(mintens);  
        Serial.print(minunits);  
        Serial.print(" : "); 
        Serial.print(sectens);  
        Serial.println(secunits); 
        showtime ();

        if ( mintens + minunits + sectens + secunits  >= 1 ){ 
          timeleft = HIGH;
        } 
        else { 
          digitalWrite( bombPin, HIGH ) ; 
          timeleft = LOW ; 
          pause = HIGH ;
                // milliseconds bombPin goes high
          delay ( 2000 ); 
        } 
        digitalWrite( bombPin, LOW );

      } 
      dotState = HIGH; 
    } // keeps dots on while countdown 
    else 
      if ( blank == LOW ) {
      flashdots ();
    }

  }
} // END OF CHECKTIME

void checkbutton ()
{   
  int plus = digitalRead( plusPin );
  if ( plus == LOW ) {     
    secunits ++;  
  }  

  if ( secunits > 9 ){       
    secunits = 0 ;       
    sectens ++;    
  } 
  if ( sectens >5 ) {      
    sectens = 0;      
    minunits ++;    
  }
  if ( minunits > 9 ){      
    minunits=0;       
    mintens ++;    
  }
  if ( mintens >5 ){       
    mintens = 0 ;    
  }   

  int minus = digitalRead( minusPin );
  if ( minus == LOW ) {  
    secunits --; 
  }

  if ( secunits  < 0 ){       
    secunits = 9;        
    sectens -- ;    
  }
  if ( sectens   < 0 ){       
    sectens = 5;       
    minunits -- ;    
  }
  if ( minunits  < 0 ){       
    minunits = 9;        
    mintens -- ;    
  }  
  if ( mintens < 0 ){        
    mintens =5 ;     
  }

  //  CHANGE THIS DELAY TO SET COUNTSPEED WHEN + or - BUTTON HELD IN
  if ( plus || minus == LOW ){  
    delay ( 100 ) ;     
    showtime ();      
  }  


  // when run button pressed release pause
  int runbutton = digitalRead( runPin );
  if ( runbutton == LOW ) { 
    pause = LOW ; 
  } 


  // when pause button pressed  set pause
  int pausebutton  = digitalRead( pausePin );  
  if ( pausebutton == LOW ) { 
    pause = HIGH ; 
  } 



}  // end of checkbutton function 

void flashdots () { 
  unsigned long currentdotMillis = millis();
  if(currentdotMillis - previousdotMillis > 500) {
    previousdotMillis = currentdotMillis;    
    if (dotState == LOW)
    {
      dotState = HIGH;
    }
    else  { 
      dotState = LOW;
    }     
    showtime ();  
  }
  if ( pause == LOW ) { 
    dotState = HIGH ;
  }  
} //  end of flashdots routins 

void showtime ()   //  DISPLAY ROUTINE 
{   
  int mintendisp = (mintens) ? digitTable [ mintens ] : 0;  //  zero blanking tens minutes
  int minunitsdisp = digitTable [ minunits ];
  int sectensdisp = digitTable [ sectens ];
  int secunitsdisp = digitTable [ secunits ];
  if ( dotState == HIGH ){

    minunitsdisp = minunitsdisp|10000000 ; 
  }      //   adds msb to minunit to light dots when dotState high

  digitalWrite(latchPin, LOW); 

  shiftOut(dataPin, clockPin, LSBFIRST, mintendisp);
  shiftOut(dataPin, clockPin, LSBFIRST, minunitsdisp);
  shiftOut(dataPin, clockPin, LSBFIRST, sectensdisp); 
  shiftOut(dataPin, clockPin, LSBFIRST, secunitsdisp);

  digitalWrite(latchPin, HIGH);
}

I thought I would give an update on this project.

With lots of help from Boffin1 (Thanks John), and using his display board, I now have a working countdown timer. XD

Here is a link to a short video on Youtube:

sorry it's a bit dark but the video really didn't like the brightness of the displays.

and a couple of pictures:

The only issue I currently have is the accuracy. I have been playing but can't seem to get it very accurate. The best I have managed, so far, is losing 5 secs per 10 mins which is horrendous! I need to keep playing with the code to see what I can do.

So a big thanks to Crossraods and Boffin1 for getting me this far.

I will do another update, maybe for help on improving the accuracy, but definitely when I have it boxed and ready.

Dave

Using a timer isr, you can easily correct for oscillator errors. On top of that, with a temperature sensor (a diode for example), you can correct also for oscillator temperature drift.

dhenry:
Using a timer isr, you can easily correct for oscillator errors. On top of that, with a temperature sensor (a diode for example), you can correct also for oscillator temperature drift.

My code is predominantly based on what Boffin1 has posted above - would it take a massive shift in code to incorporate using a timer isr? I can't see a way of easily doing it without re-writing large chunks of the logic.

Dave

OK guys, I'm stuck.

I have tried a variety of thing but my timer runs slow. :0 I have tried changing the milli additionpart of the code but it doesn't appear to make much difference.

Here is the code I have right now:

//  countdown " bomb " timer with preset up down run and pause
//   connect dots  to spare TPIC output on minute units chip, on when counting down, flash when paused
#include <VirtualWire.h>
#define latchPin 11  // rck
#define clockPin 10  // sck
#define dataPin 13   // ser in

int tile [4];

const byte digitTable [10] = { 
  B01111110,B00010010,B01001111,B01010111,B00110011,B01110101,B01111101,
  B01010010,B01111111,B01110111  } 
;   

const byte blanked = B00000000; 
int dotState;
int pause = HIGH;
int blank = HIGH;
int mintens = 3;
int minunits = 0;
int sectens = 0;
int secunits = 0;
int timeleft = 0;              
unsigned long previousdotMillis = 0;  
unsigned long previousMillis = 0;     
int mintendisp; 
int minunitsdisp;
int sectensdisp;
int secunitsdisp;  
int plusPin = 19;
int minusPin = 18;
int runPin = 17;
int pausePin =15;
int running;
int bombPin = 6;
int phoneyground = 12;
int resetsunit = 0;
int resetstens = 0;
int resetmunit = 0;
int resetmtens = 3;


//******************************************************        
void setup()
{
  //  Serial.begin(9600);	// Debugging only
  blank == HIGH;
 // Serial.println("setup");
  pinMode ( latchPin, OUTPUT);
  pinMode ( clockPin, OUTPUT);
  pinMode ( dataPin, OUTPUT); 
  pinMode ( plusPin, INPUT); 
  digitalWrite(plusPin, HIGH); // set pullups
  pinMode ( minusPin, INPUT); 
  digitalWrite(minusPin, HIGH);
  pinMode ( runPin, INPUT); 
  digitalWrite (runPin, HIGH);
  pinMode ( pausePin, INPUT); 
  digitalWrite(pausePin, HIGH);
  pinMode (phoneyground, OUTPUT);
  digitalWrite(phoneyground, LOW);
  digitalWrite(latchPin, LOW);      // blanking all displays
  for ( int k=0; k<=3; k++ ){
    shiftOut(dataPin, clockPin, LSBFIRST, blank); 
    delay (5);
  }
  digitalWrite(latchPin, HIGH);  
  pinMode ( bombPin, INPUT); 
  digitalWrite(bombPin, LOW);
}
//********************************************************************
void loop () 
{  

  checktime();

  if ( mintens + minunits + sectens + secunits  >= 1 ){ 
    timeleft = HIGH;
  }    // check again after countdown
  else 
  { 
    timeleft = LOW ; 
    pause = HIGH;   
  }     //  flashing dots
  unsigned long currentdotMillis = millis();
  if(currentdotMillis - previousdotMillis > 500) {
    previousdotMillis = currentdotMillis;    
    if (dotState == LOW)
    {
      dotState = HIGH;
    }    
    else     { 
      dotState = LOW;
    } 
      showtime ();
  }
  if ( pause == LOW ) {   // change this to HIGH if you only want the dots flashing while counting
    dotState = HIGH ;
  }

  //     blank =0;  //  brings display out of standbye with any button pressed

  checkbutton ();    


}//  end of loop


void checktime () {
  if ( mintens + minunits + sectens + secunits  >= 1 )  //  if time still left on countdown
  { 
    timeleft = HIGH;
  } 
  else { 
    timeleft = LOW ; 
    pause = HIGH; 
  }

  if (timeleft == HIGH ) {
    if ( pause == LOW ) {  //  which means its counting down   i.e. not paused in countdown          
      static unsigned long previousMillis;
      if(millis() > previousMillis)
      { 
        previousMillis = millis() + 1010; 
        secunits -- ;             // countdown 1 sec 
        if ( secunits  < 0 ){ 
          secunits = 9;  
          sectens -- ;
        }
        if ( sectens   < 0 ){ 
          sectens = 5;  
          minunits -- ;
        }
        if ( minunits  < 0 ){ 
          minunits = 9;  
          mintens -- ;
        } 
        // mintens cant get to -1 or it would have to have been 0000 and paused
        //        Serial.print(mintens);  
        //        Serial.print(minunits);  
        //        Serial.print(" : "); 
        //        Serial.print(sectens);  
        //        Serial.println(secunits); 
        showtime ();

        if ( mintens + minunits + sectens + secunits  >= 1 ){ 
          timeleft = HIGH;
        } 
        else { 
          digitalWrite( bombPin, HIGH ) ; 
          timeleft = LOW ; 
          pause = HIGH ;
          delay ( 2000 ); 
        }// milliseconds bombPin goes high 
        digitalWrite( bombPin, LOW );

      } 
      dotState = HIGH; 
    } // keeps dots on while countdown 
    else 
      if ( blank == LOW ) {
      flashdots ();
    }

  }
} // END OF CHECKTIME

void checkbutton ()
{   
  int plus = digitalRead( plusPin );
  if ( plus == LOW && pause == HIGH) { 
    delay ( 200 );      
    if ( secunits !=0 ){      
      secunits=0;   
    }    
    if ( sectens !=0 ){      
      sectens=0;   
    }
    minunits ++;
    resetmtens = mintens; 
    resetmunit = minunits;
    resetstens = sectens;
    resetsunit = secunits;     
  }

  if ( minunits > 9 ){      
    minunits=0;       
    mintens ++;
    resetmtens = mintens; 
    resetmunit = minunits;
    resetstens = sectens;
    resetsunit = secunits;  

  }

  if ( mintens >5 ){       
    mintens = 0 ;    
  }

  int minus = digitalRead( minusPin );
  if ( minus == LOW && pause == HIGH) {  
    secunits --; 
    resetmtens = mintens; 
    resetmunit = minunits;
    resetstens = sectens;
    resetsunit = secunits;  

  }

  if ( secunits  < 0 ){       
    secunits = 9;        
    sectens -- ;    
    resetmtens = mintens; 
    resetmunit = minunits;
    resetstens = sectens;
    resetsunit = secunits;  

  }
  if ( sectens   < 0 ){       
    sectens = 5;       
    minunits -- ;    
    resetmtens = mintens; 
    resetmunit = minunits;
    resetstens = sectens;
    resetsunit = secunits;  

  }
  if ( minunits  < 0 ){       
    minunits = 9;        
    mintens -- ;    
    resetmtens = mintens; 
    resetmunit = minunits;
    resetstens = sectens;
    resetsunit = secunits;  

  }  
  if ( mintens < 0 ){        
    mintens =5 ;     
    resetmtens = mintens; 
    resetmunit = minunits;
    resetstens = sectens;
    resetsunit = secunits;  

  }


  //  CHANGE THIS DELAY TO SET COUNTSPEED WHEN + or - BUTTON HELD IN
  if ( plus || minus == LOW ){  
    delay ( 100 ) ;     
    showtime (); 
  }  

  // when run button pressed release pause
  int runbutton = digitalRead( runPin );
  if ( runbutton == LOW ) { 
    pause = LOW ; 
  } 

  // when pause button pressed  set pause
  int pausebutton = digitalRead( pausePin );  
  if ( pausebutton == LOW ) { 
    pause = HIGH ; 
  } 

  // when pause and run button pressed reset to start value
  if (runbutton == LOW && pausebutton == LOW) {
    mintens = resetmtens;
    minunits = resetmunit;
    sectens = resetstens;
    secunits = resetsunit;
  }

}  // end of checkbutton function 

void flashdots () { 
  unsigned long currentdotMillis = millis();
  if(currentdotMillis - previousdotMillis > 500) {
    previousdotMillis = currentdotMillis;    
    if (dotState == LOW)
    {
      dotState = HIGH;
    }
    else  { 
      dotState = LOW;
    }     
    showtime ();  
  }
  if ( pause == LOW ) { 
    dotState = HIGH ;
  }  
} //  end of flashdots routins 

void showtime ()   //  DISPLAY ROUTINE 
{   
  int mintendisp = (mintens) ? digitTable [ mintens ] : 0;  //  zero blanking tens minutes
  int minunitsdisp = digitTable [ minunits ];
  int sectensdisp = digitTable [ sectens ];
  int secunitsdisp = digitTable [ secunits ];
  if ( dotState == HIGH ){

    minunitsdisp = minunitsdisp|10000000 ; 
    mintendisp = mintendisp|10000000 ;
  }      //   adds msb to minunit to light dots when dotState high



  digitalWrite(latchPin, LOW); 

  shiftOut(dataPin, clockPin, LSBFIRST, mintendisp);
  shiftOut(dataPin, clockPin, LSBFIRST, minunitsdisp);
  shiftOut(dataPin, clockPin, LSBFIRST, sectensdisp); 
  shiftOut(dataPin, clockPin, LSBFIRST, secunitsdisp);

  digitalWrite(latchPin, HIGH);
}

It runs at approx 4-5 secs slow per 10 mins. Can anyone give me any hints as to how I can improve on that?

Thanks

Dave

change these kind of things to

  if(currentdotMillis - previousdotMillis >= 500) {
    previousdotMillis = previousdotMillis+500;

thus previousdotMilils is always at the 1/2 second interval.
If prior interval ran a little long (say 520ms)
then the next will just occur a little earler, say after 480mS.

Sorry Dave

I havn't been checking this thread, I must admit I didn't check the timing when I threw that code together :slight_smile:

I didnt realise you needed accuracy, the bomb timers on James Bond always take 3 minutes to count the last 5 seconds ! :slight_smile:

I will have a look over the weekend,

John

The bomb bit just seemed to creep in (maybe because of the original thread). It is actually a timer for my FIL's train club to do efficiency comps (30 mins + set amount of coal kind of thing).

In one respect it has been a good thing as it has made me investigate the code in more detail, understanding what each piece does and what affects what, so it is a good learning experience and improving my code skills immeasurably. Far better than just chucking the code in and letting it run. XD

Dave

if(currentdotMillis - previousdotMillis >= 500) {
previousdotMillis = previousdotMillis+500;
thus previousdotMilils is always at the 1/2 second interval.
If prior interval ran a little long (say 520ms)
then the next will just occur a little earler, say after 480mS.

Thats a good idea Crossroads, I will remember to do that in future.

I will see if I can have a look at the timing Dave, just got a few fires to stamp out first.

Any luck Dave, ?

I still havnt had a chance to plug in an Arduino yet ...

Not yet John.

I have tried quite a few things now and can't seem to get it any better. I've even tried different crystals and AT chip (just in case!).

I think it might be an issue with the time it takes to do a digitalwrite so have been looking at adding a condition into the code so that it only attempts a write on digit change to see if that makes a difference (although I would lose the flashing colons - not too big a deal). So far it doesn't seem to make too much difference but I will see where I can go with it.

Dave

If I get some time in the night I will run through it and see whats wrong

Try this simple sketch - I have found it to track the official US time quite well running on a Duemilanove
http://www.time.gov/timezone.cgi?Eastern/d/-5/java
Time shows up on the serial monitor, set to 57600
Once you are convinced it handles time okay, is straightforward to incorporate into your countdown sketch.

unsigned long currentmillis = 0;

unsigned long previousmillis = 0;

unsigned long interval = 10000;



byte ones_seconds = 0;
byte prior_seconds = 0;

byte tens_seconds = 0;

byte ones_minutes = 0;

byte tens_minutes = 0;

byte tenths = 0;

byte hundredths= 0;



void setup()

{



Serial.begin(57600);

}

void loop()

{

  currentmillis = micros(); // read the time.

  while (currentmillis - previousmillis >= interval) // 10 milliseconds have gone by

  {

    hundredths = hundredths +1;

    if (hundredths == 10){

      hundredths = 0;

      tenths = tenths +1;

    }

    if (tenths == 10){

      tenths = 0;

      ones_seconds = ones_seconds +1;

    }

    if (ones_seconds == 10){

      ones_seconds = 0;

      tens_seconds = tens_seconds +1;

    }

    if (tens_seconds == 6){

      tens_seconds = 0;

      ones_minutes = ones_minutes +1;

    }

    if (ones_minutes == 10){

      ones_minutes = 0;

      tens_minutes = tens_minutes +1;

    }

    if (tens_minutes == 6){

      tens_minutes = 0;

    }

    previousmillis = previousmillis + interval; // save the time for the next comparison

  }

  // counters are all updated now,

if (prior_seconds != ones_seconds){

  Serial.print (tens_minutes, DEC);

  Serial.print (" ");

  Serial.print (ones_minutes, DEC);

  Serial.print (" : ");

  Serial.print (tens_seconds, DEC);

  Serial.print (" ");

  Serial.println (ones_seconds, DEC);
prior_seconds = ones_seconds;
}

} // end void loop

Thats working fine Crossroads, is the idea of checking every hundredth of a second more accurate than milllis?

Partially, and partially from these

(currentmillis - previousmillis >= interval)

previousmillis = previousmillis + interval; // save the time for the next comparison

So the time test interval is always a fixed amount away, and not drifting around based on when micros() got captured.
If the stuff to do happens to take too long, then the next time around it gets a chance to complete it sooner and get back on track.

I think CodingBadly turned me onto that back in 2010 when I started coding for time intervals.