60 minute countdown with 7 segment display and relay output

Hi all. I am new to all this and to be honest, it seems a little above my skills but i would like to learn it.

I am wanting to have a 60 minute countdown timer with a 4 digit segment display (TM1637) and a relay to be triggered on at the beginning of the countdown and triggered off at the end of the countdown.

The countdown would start when the arduino receives power.

I have had a look at various timer sketches but cannot make head nor tail of them. I can get the idea to work without a display by using delays. The main struggle is getting it on the display.

I hope you more knowledgable people can advise and help me learn.

Thank you

/*
 6-13-2011
 Spark Fun Electronics 2011
 Nathan Seidle
 
 This code is public domain but you buy me a beer if you use this and we meet 
 someday (Beerware license).
 
 4 digit 7 segment display:
 http://www.sparkfun.com/products/9483
 Datasheet: 
 http://www.sparkfun.com/datasheets/Components/LED/7-Segment/YSD-439AR6B-35.pdf

 This is an example of how to drive a 7 segment LED display from an ATmega
 without the use of current limiting resistors. This technique is very common 
 but requires some knowledge of electronics - you do run the risk of dumping 
 too much current through the segments and burning out parts of the display. 
 If you use the stock code you should be ok, but be careful editing the 
 brightness values.
 
 This code should work with all colors (red, blue, yellow, green) but the 
 brightness will vary from one color to the next because the forward voltage 
 drop of each color is different. This code was written and calibrated for the 
 red color.

 This code will work with most Arduinos but you may want to re-route some of 
 the pins.

 7 segments
 4 digits
 1 colon
 =
 12 pins required for full control 
 
 */

int digit1 = 11; //PWM Display pin 1
int digit2 = 10; //PWM Display pin 2
int digit3 = 9; //PWM Display pin 6
int digit4 = 6; //PWM Display pin 8

//Pin mapping from Arduino to the ATmega DIP28 if you need it
//http://www.arduino.cc/en/Hacking/PinMapping
int segA = A1; //Display pin 14
int segB = 3; //Display pin 16
int segC = 4; //Display pin 13
int segD = 5; //Display pin 3
int segE = A0; //Display pin 5
int segF = 7; //Display pin 11
int segG = 8; //Display pin 15

void setup() {                
  pinMode(segA, OUTPUT);
  pinMode(segB, OUTPUT);
  pinMode(segC, OUTPUT);
  pinMode(segD, OUTPUT);
  pinMode(segE, OUTPUT);
  pinMode(segF, OUTPUT);
  pinMode(segG, OUTPUT);

  pinMode(digit1, OUTPUT);
  pinMode(digit2, OUTPUT);
  pinMode(digit3, OUTPUT);
  pinMode(digit4, OUTPUT);
  
  pinMode(13, OUTPUT);
}

void loop() {
  
  //long startTime = millis();

  displayNumber(millis()/1000);

  //while( (millis() - startTime) < 2000) {
  //displayNumber(1217);
  //}
  //delay(1000);  
}

//Given a number, we display 10:22
//After running through the 4 numbers, the display is left turned off

//Display brightness
//Each digit is on for a certain amount of microseconds
//Then it is off until we have reached a total of 20ms for the function call
//Let's assume each digit is on for 1000us
//Each digit is on for 1ms, there are 4 digits, so the display is off for 16ms.
//That's a ratio of 1ms to 16ms or 6.25% on time (PWM).
//Let's define a variable called brightness that varies from:
//5000 blindingly bright (15.7mA current draw per digit)
//2000 shockingly bright (11.4mA current draw per digit)
//1000 pretty bright (5.9mA)
//500 normal (3mA)
//200 dim but readable (1.4mA)
//50 dim but readable (0.56mA)
//5 dim but readable (0.31mA)
//1 dim but readable in dark (0.28mA)

void displayNumber(int toDisplay) {
#define DISPLAY_BRIGHTNESS  500

#define DIGIT_ON  HIGH
#define DIGIT_OFF  LOW

  long beginTime = millis();

  for(int digit = 4 ; digit > 0 ; digit--) {

    //Turn on a digit for a short amount of time
    switch(digit) {
    case 1:
      digitalWrite(digit1, DIGIT_ON);
      break;
    case 2:
      digitalWrite(digit2, DIGIT_ON);
      break;
    case 3:
      digitalWrite(digit3, DIGIT_ON);
      break;
    case 4:
      digitalWrite(digit4, DIGIT_ON);
      break;
    }

    //Turn on the right segments for this digit
    lightNumber(toDisplay % 10);
    toDisplay /= 10;

    delayMicroseconds(DISPLAY_BRIGHTNESS); 
    //Display digit for fraction of a second (1us to 5000us, 500 is pretty good)

    //Turn off all segments
    lightNumber(10); 

    //Turn off all digits
    digitalWrite(digit1, DIGIT_OFF);
    digitalWrite(digit2, DIGIT_OFF);
    digitalWrite(digit3, DIGIT_OFF);
    digitalWrite(digit4, DIGIT_OFF);
  }

  while( (millis() - beginTime) < 10) ; 
  //Wait for 20ms to pass before we paint the display again
}

//Given a number, turns on those segments
//If number == 10, then turn off number
void lightNumber(int numberToDisplay) {

#define SEGMENT_ON  LOW
#define SEGMENT_OFF HIGH

  switch (numberToDisplay){

  case 0:
    digitalWrite(segA, SEGMENT_ON);
    digitalWrite(segB, SEGMENT_ON);
    digitalWrite(segC, SEGMENT_ON);
    digitalWrite(segD, SEGMENT_ON);
    digitalWrite(segE, SEGMENT_ON);
    digitalWrite(segF, SEGMENT_ON);
    digitalWrite(segG, SEGMENT_OFF);
    break;

  case 1:
    digitalWrite(segA, SEGMENT_OFF);
    digitalWrite(segB, SEGMENT_ON);
    digitalWrite(segC, SEGMENT_ON);
    digitalWrite(segD, SEGMENT_OFF);
    digitalWrite(segE, SEGMENT_OFF);
    digitalWrite(segF, SEGMENT_OFF);
    digitalWrite(segG, SEGMENT_OFF);
    break;

  case 2:
    digitalWrite(segA, SEGMENT_ON);
    digitalWrite(segB, SEGMENT_ON);
    digitalWrite(segC, SEGMENT_OFF);
    digitalWrite(segD, SEGMENT_ON);
    digitalWrite(segE, SEGMENT_ON);
    digitalWrite(segF, SEGMENT_OFF);
    digitalWrite(segG, SEGMENT_ON);
    break;

  case 3:
    digitalWrite(segA, SEGMENT_ON);
    digitalWrite(segB, SEGMENT_ON);
    digitalWrite(segC, SEGMENT_ON);
    digitalWrite(segD, SEGMENT_ON);
    digitalWrite(segE, SEGMENT_OFF);
    digitalWrite(segF, SEGMENT_OFF);
    digitalWrite(segG, SEGMENT_ON);
    break;

  case 4:
    digitalWrite(segA, SEGMENT_OFF);
    digitalWrite(segB, SEGMENT_ON);
    digitalWrite(segC, SEGMENT_ON);
    digitalWrite(segD, SEGMENT_OFF);
    digitalWrite(segE, SEGMENT_OFF);
    digitalWrite(segF, SEGMENT_ON);
    digitalWrite(segG, SEGMENT_ON);
    break;

  case 5:
    digitalWrite(segA, SEGMENT_ON);
    digitalWrite(segB, SEGMENT_OFF);
    digitalWrite(segC, SEGMENT_ON);
    digitalWrite(segD, SEGMENT_ON);
    digitalWrite(segE, SEGMENT_OFF);
    digitalWrite(segF, SEGMENT_ON);
    digitalWrite(segG, SEGMENT_ON);
    break;

  case 6:
    digitalWrite(segA, SEGMENT_ON);
    digitalWrite(segB, SEGMENT_OFF);
    digitalWrite(segC, SEGMENT_ON);
    digitalWrite(segD, SEGMENT_ON);
    digitalWrite(segE, SEGMENT_ON);
    digitalWrite(segF, SEGMENT_ON);
    digitalWrite(segG, SEGMENT_ON);
    break;

  case 7:
    digitalWrite(segA, SEGMENT_ON);
    digitalWrite(segB, SEGMENT_ON);
    digitalWrite(segC, SEGMENT_ON);
    digitalWrite(segD, SEGMENT_OFF);
    digitalWrite(segE, SEGMENT_OFF);
    digitalWrite(segF, SEGMENT_OFF);
    digitalWrite(segG, SEGMENT_OFF);
    break;

  case 8:
    digitalWrite(segA, SEGMENT_ON);
    digitalWrite(segB, SEGMENT_ON);
    digitalWrite(segC, SEGMENT_ON);
    digitalWrite(segD, SEGMENT_ON);
    digitalWrite(segE, SEGMENT_ON);
    digitalWrite(segF, SEGMENT_ON);
    digitalWrite(segG, SEGMENT_ON);
    break;

  case 9:
    digitalWrite(segA, SEGMENT_ON);
    digitalWrite(segB, SEGMENT_ON);
    digitalWrite(segC, SEGMENT_ON);
    digitalWrite(segD, SEGMENT_ON);
    digitalWrite(segE, SEGMENT_OFF);
    digitalWrite(segF, SEGMENT_ON);
    digitalWrite(segG, SEGMENT_ON);
    break;

  case 10:
    digitalWrite(segA, SEGMENT_OFF);
    digitalWrite(segB, SEGMENT_OFF);
    digitalWrite(segC, SEGMENT_OFF);
    digitalWrite(segD, SEGMENT_OFF);
    digitalWrite(segE, SEGMENT_OFF);
    digitalWrite(segF, SEGMENT_OFF);
    digitalWrite(segG, SEGMENT_OFF);
    break;
  }
}

Sorry for the delays in posting. I have to wait 10 minutes between each post.

The above sketch has a lot of wires for the display. Mine has 4 wires. I understand that the above code has been programmed to tell the arduino which segments make up which digit. Do i need to do this with my display?

Learn how the modulo operator (%) works because it and the division operator are what you need to use.

I have had a look at this modulo thing and it is way above my head. Thanks anyway

Delta_G:
It gives you the remainder from a division. If that is "way over your head" then you need to put the Arduino down and find another hobby. Seriously. It's one of the simplest concepts you'll encounter here. It only gets harder. And you cannot possibly accomplish this project without it.

Sorry to differ Delta, but there are many other ways to accomplish the mission.
Of the first few I thought of, none of them used modulo.

One algorithm:
Turn the relay on;
update the display as you loop. You didn't say what you want displayed .
Determine the timeout (60 minute) based on millis(). How many millis in 60 minutes?
When the timeout occurs (by checking the millis(), turn off the relay.

Delta_G:
It gives you the remainder from a division. If that is "way over your head" then you need to put the Arduino down and find another hobby. Seriously. It's one of the simplest concepts you'll encounter here. It only gets harder. And you cannot possibly accomplish this project without it.

I understand division. Maybe some pointers towards some good sources of education for this instead of negativity would be a better approach

[/quote]

Hi, thanks for your input. I want the display to show the countdown to zero. I can do the simple timings with millis(1UL60UL60UL*1000UL) but cannot work out how to deliver the figures to this display so i can see what the remaining time is

if you have it figured out how to display based on,using millis(), then to go backwards try something like

backMili = maxMilli-millis();

I have to as. What is this projects purpose? It is not to take to school is it? After the last few weeks I had to ask!

Lol no i am way past school. It is for activating a 12v water pump

Delta_G:
Look at the top of this page. See the "Learning". Hover on it and click Reference.

I'll save you the trouble this time: Here's the link to modulo.
https://www.arduino.cc/en/Reference/Modulo

Apologies if i sound thick but i need to work this out now. I think i understand this part:

x = 7 % 5; // x now contains 2 5 goes into 7 once so the remainder is 2
x = 9 % 5; // x now contains 4 5 goes into 9 once so the remainder is 4
x = 5 % 5; // x now contains 0 5 goes into 5 once so the remainder is 0
x = 4 % 5; // x now contains 4 5 does not go into 4 so the remainder is still 4

This i do not understand.

/* update one value in an array each time through a loop */

int values[10];
int i = 0;

void setup() {}

void loop()
{
  values[i] = analogRead(0);
  i = (i + 1) % 10;   // modulo operator rolls over variable  
}

@rob, Don't feel along, I don't understand that either.

I found some pseudo code, but I have not tested it, :
10799999ms = 2h 59m 59s 999ms

The following pseudo-code is the only thing I could come up with:

The division operator below returns the result as a rounded down integer

function to_tuple(x):
h = x / (60601000)
x = x - h*(60601000)
m = x / (601000)
x = x - m
(601000)
s = x / 1000
x = x - s
1000
return (h,m,s,x)

Here's the 10:00 countdown timer part of my fencing scoring time. Change start time to 60:00 vs 10:00 for an hour.
Instead of the modulo stuff, I track the 4 digits to be displayed and update them as needed, rollover the value as things count down.

 void loop()
{
  // check if time needs updating
  if ((time_running == 1))  // time is counting down
  {
    unsigned long currentMillis = millis();  // see how long its been

    if (currentMillis - previousMillis >= interval) // more than our quarter second interval?
    {
      // save the last time we okayed time updates 
      previousMillis = currentMillis; 
      tenths = tenths+1;

      if (tenth_interval == 10) // we hit the one second update time
      {
        update_time = 1;   //  enable time display to be updated
        tenth_interval = 0;
        // update the time digits
        // cases: 
        // 0:01, final second - stop time, disable touch lights, sound buzzer
        // Tens of seconds rollover: time = x:50, x:40, x:30, x:20, x:10: decrement tens of seconds, rollover seconds to 9
        // Minutes rollover: time = 9:00, 8:00, etc. 2:00, 1:00: decrement ones of minutes, rollover tens of 
        // seconds to 5, ones of seconds to 9
          // 10:00: Roll all the digits over
        // otherwise: just roll over the seconds

        // Case: Final Second
        if ((minutes_ones == 0) && (seconds_tens == 0) && (seconds_ones == 1)) // don't need minutes_tens, can't have 10:01
        {
          time_running = 0;  // stop time running
          seconds_ones = 0;  // clear the last second
          updated = 1;  // fake a Case complete flag
          buzzer = 1;                     // add a buzzer for this: end of time buzzer sounds       

        }  // end of  if final second

        // Case: x:50, x:40, x:30, x:20, x:10
        if ((seconds_tens >0) && (seconds_ones == 0))  // case for the last tens of seconds 
        {
          seconds_tens = seconds_tens - 1;  // decrement the tens
          seconds_ones = 9;  // rollover the ones
          updated = 1;
        }  // end of if 10 of seconds rollover

        // Case: 9:00, 8:00, etc 2:00, 1:00
        if ((minutes_ones > 0) && (seconds_tens == 0) && (seconds_ones == 0)) // case for the last ones of minutes
        { 
          minutes_tens = 0x00;  //
          minutes_ones = minutes_ones - 1;  // decrement the minutes
          seconds_tens = 5;  // rollover the tens of seconds;
          seconds_ones = 9;  // rollover the ones of seconds;
          updated = 1;
        } // end of if minutes rollover

        // Case: starting from 10:00
        if (minutes_tens == 0x01)  // roll over all digits
        {
          minutes_tens = 0x00;  // rollover the tens of minutes
          minutes_ones = 9;  // rollover the ones of mints;
          seconds_tens = 5;  // rollover the tens of seconds;
          seconds_ones = 9;  // rollover the ones of seconds; 
          updated = 1;
        } // end of if 10:00 rollover

        // General Case: just decrement the seconds
        if (updated == 0)  // nothing else updated - but don't decrement if = 0.
        {
          seconds_ones = seconds_ones - 1;
        }
        updated = 0;  // reset for next pass thru
      } // end of if quarter_interval
    } // end of reaching our interval
  } // end of if time_running

Link: http://cpp.sh/6lhl

Your welcome.

@HazardsMind,

I can't see how that code will work. Have you tried it on an arduino? How did that work out for you?

I didn't want to give him a code that would work on the Arduino (directly), I wanted to show him the concept of extracting the numbers from a variable into 4 segments. These 4 segments represent a clock.

If you ran the code, you would have seen that.

Delta_G:
OK, if I just put i = i + 1 there then eventually i would get greater than 9 and I would be off the end of my values array.

If I divide something by 10, what is the largest remainder that is possible? IF that's hard to see, work out all these math problems and see if you see the pattern emerging.

Find the remainder for each:

1 % 10 =
2 % 10 =
3 % 10 =
4 % 10 =
5 % 10 =
6 % 10 =
7 % 10 =
8 % 10 =
9 % 10 =
10 % 10 =

... go all the way up to 39 % 10 if you're still not seeing it.

The largest remainder would be 9. I appreciate the help.

@ Delta_G ,
Yes, that code does indeed work, running on a Promini. I use it to drive the timer on my fencing scoring machine, sending updates to a MAX7219, while also watching for touches being made and receiving commands via VirtualWire from a remote control.
I'm sure it could done differently, when I wrote it in late 2010 I was just getting back into programming and the few comparisons seemed quicker in my mind than doing a bunch of divisions on a slow 8-bit processor.
I use the DP of the individual digits for other purposes as well, so there is code to set & clear them before sending the data to the MAX7219 for display.