I need help with my countdown timer project!

Hello community,

I received the Sparkfun Arduino Inventor’s kit for christmas and I have been having great fun with it so far.

I wanted to make some more advanced things so I decided to order some components.

I ordered:

4 way 7 segment Display

A rotary encoder

Resistors

I already have an Arduino Uno, breadboard and a Piezo Elements speaker.

What I want to do is make a countdown timer. I want to use the rotary encoder to select the amount of time to countdown and then use the 7 segment display as the counter. I then want the Piezo to sound when it reaches 0.

Problem is I have no clue where to start.

Any help is greatly appreciated! Codes, breadboard layouts. The lot!

Thanks alot!

Matt :slight_smile:

where you start is to break the project into more manageable pieces. Work on each separately. Figure out how to drive a 7 segment display (hint - look up multiplexing 7 segment displays). Figure out how to get input from the rotary encoder. Figure out how to make a timer (just output on the serial console). Then start putting them together. As you do each piece, think about how you can reuse the code you write.

Agh thanks. I found a place to start with the 7 Segment display. I think the Rotary encoder will be the bit that gets me.

nah, quadrature encoders are pretty simple. you need to get direction and count clicks. can be done with a table and a few lines of code.

Conway6288:
Agh thanks. I found a place to start with the 7 Segment display. I think the Rotary encoder will be the bit that gets me.

Which encoder ?

http://www.sparkfun.com/products/9117

or

http://www.sparkfun.com/products/10596

or

???

I got this Rotary encoder -

http://www.sparkfun.com/products/9117

Although I wish I got the other one now! LED’s make everything better.

I had a quick read through this - http://www.pavius.net/2010/02/rotary-en … ing-timer/ - which was on the rotary encoder page.

Update!

I have got the 7 segment display counting down from 30 seconds. It then flashes and ‘beeps’ simultaneously until you reset it.

I have the Rotary encoder on my breadboard by I have absolutely no clue how to hook it up or program it so that it can be used to set the time.

Here is what I have - http://www.sparkfun.com/products/9117

If somebody could tell me what pins need to go where etc that would be a great start!

If you look at the comments and links on the product page you’ve linked to, I think you’ll find all the info you need. My quick look finds this link particularly applicable.

http://www.pavius.net/2010/02/rotary-en … ing-timer/

It has a clear picture of which pins do what on the encoder and how to use them.

Conway6288:
I had a quick read through this - http://www.pavius.net/2010/02/rotary-en … ing-timer/ - which was on the rotary encoder page.

sheesh, that guy made the problem so much harder. You should debounce the encoder switches first and then any transition tells you the direction.

There is a decent treatment of how to use interrupts for the encoder. It’s a much better approach because it doesn’t burden the main loop. If you go off and do something in the loop, you might miss movement of the shaft. http://www.arduino.cc/playground/Main/RotaryEncoder

However, the author ignored debouncing the switches. It may turn out ok for the most part but you could see some odd results. A small (100nF) cap between a pulled up switch and gnd might keep the bouncing to a minimum. A lot depends on the switches in the encoder. There are also sw approaches to debouncing.

I have copied the code from a book that I have (Which is where this project originated)

I just changed the pins on it to suit where mine have gone. Which is probably where the problem lies. I tried it before changing the pins and the display flashed but nothing else. When I changed the pins to what they are on my board there is nothing on the display. I think the code is pretty old and I probably have the encoder hooked up wrong.

Here’s the code -

#include <EEPROM.h>

int address = 0;

byte selectedTimeIndex;

int aPin = A5;

int bPin = 13;

int buttonPin = 2;

int segmentPins = {A1,3,4,5,A0,7,8};

int displayPins = {11,10,9,6};

int times = {5,10,15,20,30,45,100,130,200,230,300,400,500,600,700,800,900,1000,1500,2000,3000};

int numTimes = 19;

int timerMinute;

int timerSecond;

int buzzerPin = 12;

boolean stopped = true;

byte digits [10][8] = {

// a b c d e f g .

(1,1,1,1,1,1,0,0), //0

(0,1,1,0,0,0,0,0), //1

(1,1,0,1,1,0,1,0), //2

(1,1,1,1,0,0,1,0), //3

(0,1,1,0,0,1,1,0), //4

(1,0,1,1,0,1,1,0), //5

(1,0,1,1,1,1,1,0), //6

(1,1,1,0,0,0,0,0), //7

(1,1,1,1,1,1,1,0), //8

(1,1,1,1,0,1,1,0) //9

};

void setup()

{

for (int i=0; i < 8; i++)

{

pinMode(segmentPins*, OUTPUT);*
}
for (int i=0; i < 4; i++)
{
pinMode(displayPins, OUTPUT);
}
pinMode(buzzerPin, OUTPUT);
pinMode(buttonPin, INPUT);
pinMode(aPin, INPUT);
pinMode(bPin, INPUT);
selectedTimeIndex = EEPROM.read(address);
timerMinute = times[selectedTimeIndex] / 100;
timerSecond = times[selectedTimeIndex] % 100;
}

void loop()
{
selectedTimeIndex = EEPROM.read(address);

if (digitalRead(buttonPin))
{
stopped = ! stopped;
digitalWrite(buzzerPin, LOW);
while (digitalRead(buttonPin)) {};
EEPROM.write(address, selectedTimeIndex);
}
updateDisplay();
}

void updateDisplay() //mmss
{
int minsecs = timerMinute * 100 + timerSecond;
int v = minsecs;
for (int i = 0; i < 4; i++)
{
int digit = v % 10;
setDigit(i);
setSegments(digit);
v = v/10;
process();
}
setDigit(5); // all digits off to prevent uneven illumination
}

void process()
{
for (int i = 0; i <100; i++) //tweak this number between flicker and blur
{
int change = getEncoderTurn();
if(stopped)
{
changeSetTime(change);
}
else
{
updateCountingTime();
}
}
if (timerMinute == 0 && timerSecond == 0)
{
digitalWrite(buzzerPin, HIGH);
}
}

void changeSetTime(int change)
{
selectedTimeIndex += change;
if(selectedTimeIndex < 0)
{
selectedTimeIndex = numTimes;
}
else if (selectedTimeIndex > numTimes)
{
selectedTimeIndex =0;
}
timerMinute = times[selectedTimeIndex] /100;
timerSecond = times[selectedTimeIndex] %100;
}
void updateCountingTime()
{
static unsigned long lastMillis;
unsigned long m= millis();
if (m> (lastMillis + 1000) && (timerSecond > 0 || timerMinute >0))
{
digitalWrite(buzzerPin, HIGH);
delay(10);
digitalWrite(buzzerPin, LOW);

if (timerSecond == 0)
{
timerSecond = 59;
timerMinute = 59;
}
else
{
timerSecond = 59;
}
lastMillis = m;
}
}
void setDigit (int digit)
{
for (int i = 0; i < 4; i++)
{
digitalWrite(displayPins, (digit != i));
}
}
void setSegments (int n)
{
for (int i = 0; i < 8; i++)
{
digitalWrite(segmentPins, ! digits [n] );
}
}
int getEncoderTurn()
{
//return -1, 0 or +1
static int oldA = LOW;
static int oldB = LOW;
int result = 0;
int newA = digitalRead(aPin);
int newB = digitalRead(bPin);
if (newA != oldA || newB != oldB)
{
//something has changed
if (oldA == LOW && newA == HIGH)
{
result = -(oldB * 2 - 1);
}
}
oldA = newA;
oldB = newB;
return result;
}

It is possible you have a Common Anode display, not a Common Cathode display. If this is the case, i believe all you need to do is change the following:

in: void setDigit (int digit){

change: digitalWrite(displayPins*, (digit != i));*
to digitalWrite(displayPins, (digit == i));
in: void setSegments (int n){
change: digitalWrite(segmentPins, ! digits [n] );
to: digitalWrite(segmentPins, digits [n] );

Philba:
There is a decent treatment of how to use interrupts for the encoder. It’s a much better approach because it doesn’t burden the main loop. If you go off and do something in the loop, you might miss movement of the shaft. http://www.arduino.cc/playground/Main/RotaryEncoder

I think you lost an “s” when posting the above URL.

http://www.arduino.cc/playground/Main/RotaryEncoders

Conway6288:
I have copied the code from a book that I have (Which is where this project originated)

I just changed the pins on it to suit where mine have gone. Which is probably where the problem lies. I tried it before changing the pins and the display flashed but nothing else. When I changed the pins to what they are on my board there is nothing on the display. I think the code is pretty old and I probably have the encoder hooked up wrong.

Just a hint to help you with code questions … when you post a port of code, highlight it and then click on the Code box. It’ll put your code in a box, like a quote box, making it much easier to read.

ie - from your post above …

Here’s the code -

#include <EEPROM.h>

int address = 0;
byte selectedTimeIndex;

int aPin = A5;
int bPin = 13;
int buttonPin = 2;

int segmentPins[] = {A1,3,4,5,A0,7,8};
int displayPins[] = {11,10,9,6};

int times [] = {5,10,15,20,30,45,100,130,200,230,300,400,500,600,700,800,900,1000,1500,2000,3000};
int numTimes = 19;
int timerMinute;
int timerSecond;

int buzzerPin = 12;

boolean stopped = true;

byte digits [10][8] = {
  // a b c d e f g .
  (1,1,1,1,1,1,0,0), //0
  (0,1,1,0,0,0,0,0), //1
  (1,1,0,1,1,0,1,0), //2
  (1,1,1,1,0,0,1,0), //3
  (0,1,1,0,0,1,1,0), //4
  (1,0,1,1,0,1,1,0), //5
  (1,0,1,1,1,1,1,0), //6
  (1,1,1,0,0,0,0,0), //7
  (1,1,1,1,1,1,1,0), //8
  (1,1,1,1,0,1,1,0) //9
 };
 
 void setup()
 {
   for (int i=0; i < 8; i++)
   {
     pinMode(segmentPins[i], OUTPUT);
   }
   for (int i=0; i < 4; i++)
   {
     pinMode(displayPins[i], OUTPUT);
   }
   pinMode(buzzerPin, OUTPUT);
   pinMode(buttonPin, INPUT);
   pinMode(aPin, INPUT);
   pinMode(bPin, INPUT);
   selectedTimeIndex = EEPROM.read(address);
   timerMinute = times[selectedTimeIndex] / 100;
   timerSecond = times[selectedTimeIndex] % 100;
 }
 
 void loop()
 {
   selectedTimeIndex = EEPROM.read(address);
   
   if (digitalRead(buttonPin))
   {
     stopped = ! stopped;
     digitalWrite(buzzerPin, LOW);
     while (digitalRead(buttonPin)) {};
     EEPROM.write(address, selectedTimeIndex);
   }
   updateDisplay();
 }
 
 void updateDisplay() //mmss
 {
   int minsecs = timerMinute * 100 + timerSecond;
   int v = minsecs;
   for (int i = 0; i < 4; i++)
   {
     int digit = v % 10;
     setDigit(i);
     setSegments(digit);
     v = v/10;
     process();
   }
   setDigit(5); // all digits off to prevent uneven illumination
 }
 
 void process()
 {
   for (int i = 0; i <100; i++) //tweak this number between flicker and blur
   {
     int change = getEncoderTurn();
     if(stopped)
     {
       changeSetTime(change);
     }
     else
     {
       updateCountingTime();
     }
   }
   if (timerMinute == 0 && timerSecond == 0)
   {
     digitalWrite(buzzerPin, HIGH);
   }
 }
 
 void changeSetTime(int change)
 {
 selectedTimeIndex += change;
 if(selectedTimeIndex < 0)
 {
   selectedTimeIndex = numTimes;
 }
 else if (selectedTimeIndex > numTimes)
 {
   selectedTimeIndex =0;
 }
 timerMinute = times[selectedTimeIndex] /100;
 timerSecond = times[selectedTimeIndex] %100;
}

void updateCountingTime()
{
  static unsigned long lastMillis;
  unsigned long m= millis();
  if (m> (lastMillis + 1000) && (timerSecond > 0 || timerMinute >0))
  {
    digitalWrite(buzzerPin, HIGH);
    delay(10);
    digitalWrite(buzzerPin, LOW);
    
    if (timerSecond == 0)
    {
      timerSecond = 59;
      timerMinute = 59;
    }
    else
    {
      timerSecond = 59;
    }
    lastMillis = m;
  }
}

void setDigit (int digit)
{
  for (int i = 0; i < 4; i++)
  {
    digitalWrite(displayPins[i], (digit != i));
  }
}

void setSegments (int n)
{
  for (int i = 0; i < 8; i++)
  {
    digitalWrite(segmentPins[i], ! digits [n] [i]);
  }
}

int getEncoderTurn()
{
  //return -1, 0 or +1
  static int oldA = LOW;
  static int oldB = LOW;
  int result = 0;
  int newA = digitalRead(aPin);
  int newB = digitalRead(bPin);
  if (newA != oldA || newB != oldB)
  {
    //something has changed
    if (oldA == LOW && newA == HIGH)
    {
      result = -(oldB * 2 - 1);
    }
  }
  oldA = newA;
  oldB = newB;
  return result;
}

Notice how it’s all nicely indented now.

@ TC World - That has made the display come on. It doesn’t display any numbers though, all the segments just stay on permanently. :confused:

@Mee_n_Mac - Thank you for that tip! I will use it in the future :slight_smile:

BTW I note that now the display doesn’t work and a post or so ago it was counting down. Did you change the portion of the code that outputs to the display ? How did you add the encoder software to the previously working code ? Also what’s your concept as to how the encoder is supposed to work (big picture idea). When does the software “know” when to read the encoder and update the display and when to stop reading and start the countdown ?

Ok, that can be undone as it means you have the correct display.

There is another thing I have noticed, at the top, this is declared:

int segmentPins = {A1,3,4,5,A0,7,8};

but from what I can see from the code, there should be 8 pins

int segmentPins = {a,b,c,d,e,f,g,dot};

But you only have seven. If you are not using the decimal point, you still need to declare a pin for it, otherwise there is a digital write to an unknown pin.

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

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

int speakerPin = 12;

#define c 261
#define d 294
#define e 329
#define f 349
#define g 391
#define gS 415
#define a 440
#define aS 455
#define b 466
#define cH 523
#define cSH 554
#define dH 587
#define dSH 622
#define eH 659
#define fH 698
#define fSH 740
#define gH 784
#define gSH 830
#define aH 880

int start_num=30;  // Number to countdown from
unsigned long time;

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);
  
  pinMode(speakerPin, OUTPUT);
}

void beep (unsigned char speakerPin, int frequencyInHertz, long timeInMilliseconds)

{

   

   

    int x;      

    long delayAmount = (long)(1000000/frequencyInHertz);

    long loopTime = (long)((timeInMilliseconds*1000)/(delayAmount*2));

    for (x=0;x<loopTime;x++)    

    {    

        digitalWrite(speakerPin,HIGH);

        delayMicroseconds(delayAmount);

        digitalWrite(speakerPin,LOW);

        delayMicroseconds(delayAmount);

    }    
}

void loop() {
  
  //long startTime = millis();
  if((millis()/1000) < start_num){
    displayNumber(start_num -(millis()/1000));
  }
  else {
    // reached zero, flash the display
    time=millis();
    while(millis() < time+200) {
      displayNumber(0);  // display 0 for 0.2 second
      beep(speakerPin, a, 500);
    }
    time=millis();    
    while(millis() < time+200) {
      lightNumber(10);  // Turn display off for 0.2 second
    }
  }  
  //while( (millis() - startTime) < 2000) {
  //displayNumber(1217);
  //}
  //delay(1000);  
}

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;
    }

 
    lightNumber(toDisplay % 10);
    toDisplay /= 10;

    delayMicroseconds(DISPLAY_BRIGHTNESS); 

    //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) ; 
  
}

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;
  }
}

That was the original code. Just counts down to 30 then beeps repeatedly.

For the encoder I just want it to cycle between different times and then use the button function of it to select when it should begin counting down (after selecting the time by turning it)

I also added in the missing segment pin (Thank you for noticing) but it has made no difference :confused:

Could I use the code from my previous post, the 30 second one. And just add in more times. Then add in the section about reading the encoder? That one has the display working.

I had a much closer look, and have noticed a few problems.

Firstly, your digits array is not properly initialised. You used “( )” not “{ }”. This is what it should be:

byte digits [10][8] = {
  // a b c d e f g .
  {1,1,1,1,1,1,0,0}, //0
  {0,1,1,0,0,0,0,0}, //1
  {1,1,0,1,1,0,1,0}, //2
  {1,1,1,1,0,0,1,0}, //3
  {0,1,1,0,0,1,1,0}, //4
  {1,0,1,1,0,1,1,0}, //5
  {1,0,1,1,1,1,1,0}, //6
  {1,1,1,0,0,0,0,0}, //7
  {1,1,1,1,1,1,1,0}, //8
  {1,1,1,1,0,1,1,0}  //9
};

Secondly, It wasn’t counting. The way you had written the program, seconds was always set to 59, and minutes never changed. Here is a changed version:

void updateCountingTime()
{
  static unsigned long lastMillis = 0; //Initial time is 0
  
  unsigned long m= millis(); //get the current time
  if (m> (lastMillis + 1000) && (timerSecond > 0 || timerMinute >0))
  {
    //if a second has passed, and there is time left on the clock
    digitalWrite(buzzerPin, HIGH);
    delay(10);
    digitalWrite(buzzerPin, LOW);
    if (timerSecond == 0)
    {
      //reached 0 seconds
      if(timerMinute > 0)
      {
        //if there are still minutes on the clock, reduce minutes by one and reset seconds to 59
        timerSecond = 59;
        timerMinute--;
      }
    } else {
      timerSecond--;
    }
    lastMillis = m; //update last clock time
  }
}

I haven’t got an display to test it with, but I output the value being sent to the setDigit() function over the serial port, and it appears to count down now as it should.

That has had an effect on the display, it now looks like - 3 - -

The 3 is backwards and it just flickers.

I am pretty useless at this.