Countdown timer

looking for a diagram and components for a countdown timer with start overrun and reset button.

i am a newbie so please bear with me.

thank in advance for any help.

g

Tried Google?

Clearly mention:
1. which Arduino are you using?
2. How digits are there on the display unit?
3. Are they common cathode or common anodoe?
4. What is te time format on te display: HH.MM.SS?
5. What is the intial time?
6. Do you ave a start button?
7. Do you have stop button?
8. Do you ave a pause buton?
9. What is overrun button?

using Arduino uno
4 digit seven segmant display
common anode
min sec
2 minutes
start button yes
stop button no
pause button no
overrun button no

what i am trying to achieve is as follows

countdown timer that will count down from 2 minutes then when time is up it will continue into minus count (overrun) until the reset is triggered back to the start command.

g

Try with the following schematic (Fig-1). This is multiplxed display unit.


Figure-1:

This is called underrun.

In terms of hardware, you have a 7-seg display and a few buttons. Can you not find a diagram that matches that description?

thanks will give it try.

1. Build the display unit.
2. Upload code to see 02.00 on the display unit.
3.
..... gradually add other hardware and software components.

Delayed start.

yes the start is delayed once reset possibly from a volt free relay.

Hi @graemed9 !

I'm not sure if this aligns with your intentions but feel free to have a look:

https://wokwi.com/projects/461542314286743553

Sketch:

/*
  Forum: https://forum.arduino.cc/t/countdown-timer/1440331
  Wokwi: https://wokwi.com/projects/461542314286743553

  Based on
  https://github.com/untr0py/SevSeg/tree/master/examples/SevSeg_Counter

  ec2021
  2026/04/17

  Starts to count down from 2 minutes (120 seconds) when green button pressed
  During countdown mode the red button can be pressed at any time to reset the counter
  The counter value is converted into minutes and seconds for display
  The decimal point is (mis-)used to indicate where minutes are displayed to the right and seconds to the left
  A minimum countdown value of - 10 minutes = -600 sec has been defined to take care of the displayable numbers
  If this minimum values has been reached or at any lower number the display will show "--.--"

*/

#include "SevSeg.h"

class BtnClass {
  private:
    byte pin;
    byte state;
    byte lastState;
    unsigned long lastChange;
  public:
    init(byte p) {
      pin = p;
      pinMode(pin, INPUT_PULLUP);
    };
    boolean pressed() {
      byte actState = digitalRead(pin);
      if (actState != lastState) {
        lastChange = millis();
        lastState = actState;
      };
      if (actState != state && millis() - lastChange > 30) {  // Fixed debounce time 30 ms
        state = actState;
        return !state;
      }
      return false;
    };
};

constexpr byte numOfDigits = 4;
constexpr byte digitPins[] = {2, 3, 4, 5};
constexpr byte segmentPins[] = {6, 7, 8, 9, 10, 11, 12, 13};
constexpr bool resistorsOnSegments = false; // 'false' means resistors are on digit pins
constexpr byte hardwareConfig = COMMON_ANODE; // See README.md at GitHub (link see above)for options
constexpr bool updateWithDelays = false; // Default 'false' is Recommended
constexpr bool leadingZeros = true; // Use 'true' if you'd like to keep the leading zeros
constexpr bool disableDecPoint = false; // Use 'true' if your decimal point doesn't exist or isn't connected

constexpr byte startPin {A1};
constexpr byte resetPin  {A0};
constexpr int startCount {120};  // 2 minutes in seconds = 120
constexpr int  minCount {- 600}; // in seconds = - (10 minutes ) = - 600
constexpr unsigned long countDownInterval {1000}; // in ms, can be changed for test purposes (e.g. to 500 ms or 200 ms ...)


enum class MODE {IDLE, COUNTDOWN, RESET};
MODE mode = MODE::IDLE;

SevSeg sevseg;

BtnClass startBtn;
BtnClass resetBtn;

unsigned long lastChange = 0;
int combinedTime = 200;
int counter;

void setup() {
  sevseg.begin(hardwareConfig, numOfDigits, digitPins, segmentPins, resistorsOnSegments,
               updateWithDelays, leadingZeros, disableDecPoint);
  sevseg.setBrightness(90);
  startBtn.init(startPin);
  resetBtn.init(resetPin);
  resetCounter();
}

void loop() {
  countDownMachine();
  sevseg.refreshDisplay(); // Must run repeatedly
}

void countDownMachine() {
  switch (mode) {
    case MODE::IDLE:
      if (startBtn.pressed()) {
        mode = MODE::COUNTDOWN;
        lastChange = millis(); // the first second starts with the
      }
      break;
    case MODE::COUNTDOWN:
      countDown();
      // The btnClass makes sure that you have to release a button before a next
      // pressed() event can become true
      // Therefore you can use the startBtn here also to reset the counter
      // To do this uncomment the following line
      //if (startBtn.pressed()) {
      // and comment this line:
      if (resetBtn.pressed()) {
        mode = MODE::RESET;
      }
      break;
    case MODE::RESET:
      resetCounter();
      mode = MODE::IDLE;
      break;
  }
}

void resetCounter() {
  counter = startCount;
  setTimer();
  mode = MODE::IDLE;
}

void countDown() {
  if (millis() - lastChange >= countDownInterval && counter > minCount) {
    lastChange = millis();
    counter--;
  }
  setTimer();
}

void setTimer() {
  int minutes = counter / 60;
  int seconds = counter - minutes * 60;
  int combinedTime = minutes * 100 + seconds;
  sevseg.setNumber(combinedTime, 2);
}

A main requirement when using this kind of displays (e.g. not one that's controlled via 4-wire interface like the TM1637 Display) is to apply non-blocking functions. This is just one of many possible realizations ...

I like to keep functions as small as reasonable so that it is easier to see what is done.

Good luck!
ec2021

A Multifunction shield that fixes into an UNO may be a good, ready-made prototyping idea for your project.

i have now built the timer and have code running, problem is that it is running at 1:1 slow that is 1 second is actually 2 seconds in real time. i believe this is a known issue with neopixel led strips. has anybody got knowledge to resolve this issue.

thanks Graeme

@graemed9 Why did you start a new topic to let us know that you had built the timer ?

Your second topic has been merged into this one

Post your full sketch and a schematic of your project as it is now

Which Arduino board are you using ?

using uno 3 and a dedicated 5vdc 10A psu with a 470uF capacitor across the +5v and -5v with inline 330 resister on the arduino pin.

#include <FastLED.h>

// Hardware Setup
#define LED_PIN     6
#define COLOR_ORDER GRB
#define CHIPSET     WS2812B
#define NUM_DIGITS   3
#define LEDS_PER_SEG 3
#define NUM_LEDS    (21)

// Control Pins
#define START_PIN   2
#define RESET_PIN   3

CRGB leds[NUM_LEDS];

// Timing Variables
long currentTime = 60; // Starting seconds
bool isRunning = false;
unsigned long lastTick = 0;

// 7-Segment Mapping (A, B, C, D, E, F, G)
const byte digitMap[11][7] = {
  {1, 1, 1, 1, 1, 1, 0}, // 0
  {0, 1, 1, 0, 0, 0, 0}, // 1
  {1, 1, 0, 1, 1, 0, 1}, // 2
  {1, 1, 1, 1, 0, 0, 1}, // 3
  {0, 1, 1, 0, 0, 1, 1}, // 4
  {1, 0, 1, 1, 0, 1, 1}, // 5
  {1, 0, 1, 1, 1, 1, 1}, // 6
  {1, 1, 1, 0, 0, 0, 0}, // 7
  {1, 1, 1, 1, 1, 1, 1}, // 8
  {1, 1, 1, 1, 0, 1, 1}, // 9
  {0, 0, 0, 0, 0, 0, 1}  // Negative sign (G segment only)
};

void setup() {
  FastLED.addLeds<CHIPSET, LED_PIN, COLOR_ORDER>(leds, NUM_LEDS).setCorrection(TypicalLEDStrip);
  pinMode(START_PIN, INPUT_PULLUP);
  pinMode(RESET_PIN, INPUT_PULLUP);
  FastLED.setBrightness(100);
}

void loop() {
  checkButtons();

  if (isRunning && (millis() - lastTick >= 1000)) {
    currentTime--; // Allow to go below zero
    lastTick = millis();
  }

  displayTime(currentTime);
  FastLED.show();
}

void checkButtons() {
  if (digitalRead(START_PIN) == LOW) {
    delay(50); // Debounce
    isRunning = !isRunning;
    while(digitalRead(START_PIN) == LOW); 
  }
  if (digitalRead(RESET_PIN) == LOW) {
    currentTime = 60; // Reset to your preferred start
    isRunning = false;
  }
}

void displayTime(long val) {
  FastLED.clear();
  bool isNegative = val < 0;
  long absVal = abs(val);

  int displayArray[4];
  displayArray[0] = (absVal / 1000) % 10;
  displayArray[1] = (absVal / 100) % 10;
  displayArray[2] = (absVal / 10) % 10;
  displayArray[3] = absVal % 10;

  for (int d = 0; d < 4; d++) {
    // If negative, replace first digit with a minus sign if space permits
    if (isNegative && d == 0) {
      drawDigit(0, 10, CRGB::Red); 
    } else {
      drawDigit(d, displayArray[d], isNegative ? CRGB::Red : CRGB::Green);
    }
  }
}

void drawDigit(int digitPos, int number, CRGB color) {
  int offset = digitPos * 7 * LEDS_PER_SEG;
  for (int seg = 0; seg < 7; seg++) {
    if (digitMap[number][seg] == 1) {
      for (int i = 0; i < LEDS_PER_SEG; i++) {
        leds[offset + (seg * LEDS_PER_SEG) + i] = color;
      }
    }
  }
}

I hope that you don't really have a -5V supply connected to your project. Do you mean GND ?

sorry yes i meant GND
age is a bad thing sometimes.

sorry must clarify that the arduino is powered by a seperate supply and the 5vdc psu is only for the GND and +5VDC to the neopixels.

Do the two power supplies have a common GND connection ?