Parts list
| Part | Qty | Approx. | Buy |
|---|---|---|---|
| Arduino Uno R3 (CH340 clone) | 1 | ₹450 | Buy Arduino Uno R3 (CH340 clone) |
| TM1637 4-digit display with colon (1.2" big version for a hall) | 1 | ₹180 | Buy TM1637 4-digit display with colon (1.2" big version for a hall) |
| WS2812B 8-LED stick | 1 | ₹120 | Buy WS2812B 8-LED stick |
| Tactile push buttons (or arcade buttons on the chair’s table) | 3 | ₹30 | Buy Tactile push buttons (or arcade buttons on the chair’s table) |
| Active buzzer module | 1 | ₹25 | Buy Active buzzer module |
| 330 Ω resistor + 1000 µF / 10 V capacitor | 1 | ₹10 | Buy 330 Ω resistor + 1000 µF / 10 V capacitor |
| 5 V 2 A adapter or power bank, 3 m USB cable | 1 | ₹250 | Buy 5 V 2 A adapter or power bank, 3 m USB cable |
| Box or a shoebox with cut-outs for the display and lights | 1 | ₹100 | Buy Box or a shoebox with cut-outs for the display and lights |
| Total | ₹1,165 |
Affiliate disclosure: “Buy” links may earn FlashReady a small commission at no extra cost to you. Prices are indicative.
Wiring
| Arduino Uno R3 | Part · pin | Wire | Notes |
|---|---|---|---|
| D2 | TM1637 4-digit 7-segment display · CLK | yellow | |
| D3 | TM1637 4-digit 7-segment display · DIO | blue | |
| 5V | TM1637 4-digit 7-segment display · VCC | red | |
| GND | TM1637 4-digit 7-segment display · GND | black | |
| D6 | WS2812B addressable RGB LED strip (NeoPixel) · DIN | green | Via 330 Ω — 470–1000 µF across 5V/GND at the stick |
| 5V | WS2812B addressable RGB LED strip (NeoPixel) · 5V | red | |
| GND | WS2812B addressable RGB LED strip (NeoPixel) · GND | black | |
| D4 | Push button (6 mm tactile switch) · 1 | orange | START / PAUSE — internal pull-up |
| GND | Push button (6 mm tactile switch) · 2 | black | |
| D5 | Push button (6 mm tactile switch) #2 · 1 | orange | RESET |
| GND | Push button (6 mm tactile switch) #2 · 2 | black | |
| D7 | Push button (6 mm tactile switch) #3 · 1 | orange | +1 MIN |
| GND | Push button (6 mm tactile switch) #3 · 2 | black | |
| D8 | Active buzzer module (high-level trigger) · I/O | purple | |
| 5V | Active buzzer module (high-level trigger) · VCC | red | |
| GND | Active buzzer module (high-level trigger) · GND | black |
Board quirks: The 1.2-inch TM1637 module is wired exactly like the small 0.56-inch one and is readable from the last row of a classroom. Both use the colon between the 2nd and 3rd digits — modules with decimal points instead of a colon will show the time without it. +1 MIN does two jobs: before the speech (clock reset, not running) it sets the speaking time from 1 to 15 minutes; once a speech has started it gives the speaker an extra minute. The bells are short on purpose: one at 1 minute left, two at time up, three every 30 s of overtime. A continuous alarm would drown the speaker out. Face the light stick towards the speaker and the display towards the judges (or put a second display on the same CLK/DIO lines — two TM1637s on the same pins show the same thing).
Code
// Debate / elocution competition timer — Arduino Uno + TM1637 + WS2812B stick + 3 buttons + buzzer.
// Counts down the speaking time as mm:ss. The light stick is green, turns yellow with one bell at
// 1 minute left, and red with a double bell at time up; then the display counts overtime as -m:ss
// with a triple bell every 30 s. The colon blinks while the clock runs.
// Buttons (to GND): START/PAUSE D4, RESET D5, +1 MIN D7. TM1637 CLK D2 / DIO D3.
// WS2812B DIN → D6 through 330 Ω. Active buzzer module I/O → D8.
#include <TM1637Display.h>
#include <Adafruit_NeoPixel.h>
constexpr uint8_t CLK = 2, DIO = 3, BTN_START = 4, BTN_RESET = 5, LED_PIN = 6, BTN_PLUS = 7, BUZZER_PIN = 8;
constexpr uint8_t NUM_LEDS = 8;
constexpr int DEFAULT_SEC = 180; // 3 minutes per speaker
constexpr int WARN_SEC = 60; // yellow + one bell
constexpr int MAX_SET_SEC = 900; // +1 MIN cycles 1 … 15 minutes
constexpr int MAX_OVER_SEC = 599; // overtime display limit (-9:59)
TM1637Display display(CLK, DIO);
Adafruit_NeoPixel lights(NUM_LEDS, LED_PIN, NEO_GRB + NEO_KHZ800);
int setSec = DEFAULT_SEC;
int remaining = DEFAULT_SEC; // goes negative in overtime
bool running = false;
unsigned long tickAt = 0;
// Non-blocking bell: `beepsLeft` beeps of `beepMs`, separated by 200 ms.
uint8_t beepsLeft = 0;
unsigned int beepMs = 0;
unsigned long beepAt = 0;
bool beepOn = false;
void bell(uint8_t count, unsigned int ms) {
beepsLeft = count;
beepMs = ms;
beepOn = false;
beepAt = millis();
}
void serviceBell() {
if (!beepsLeft || millis() < beepAt) return;
beepOn = !beepOn;
digitalWrite(BUZZER_PIN, beepOn);
if (beepOn) beepAt = millis() + beepMs;
else {
beepsLeft--;
beepAt = millis() + 200;
}
}
void showTime(bool colon) {
int t = abs(remaining);
uint8_t seg[4];
if (remaining < 0) {
seg[0] = SEG_G; // minus sign
seg[1] = display.encodeDigit(min(t / 60, 9));
} else {
seg[0] = t >= 600 ? display.encodeDigit(t / 600) : 0;
seg[1] = display.encodeDigit((t / 60) % 10);
}
if (colon) seg[1] |= 0x80; // colon between digits 2 and 3
seg[2] = display.encodeDigit((t % 60) / 10);
seg[3] = display.encodeDigit(t % 10);
display.setSegments(seg);
}
void showLights() {
uint32_t c;
if (remaining > WARN_SEC) c = 0x00FF00;
else if (remaining > 0) c = 0xFFA000;
else c = (running && (millis() / 400) % 2) ? 0 : 0xFF0000; // red, flashing while overtime runs
lights.fill(c);
lights.show();
}
bool pressed(uint8_t pin, bool& was) {
bool down = digitalRead(pin) == LOW;
bool edge = down && !was;
was = down;
if (edge) delay(25); // debounce
return edge;
}
void setup() {
pinMode(BTN_START, INPUT_PULLUP);
pinMode(BTN_RESET, INPUT_PULLUP);
pinMode(BTN_PLUS, INPUT_PULLUP);
pinMode(BUZZER_PIN, OUTPUT);
Serial.begin(115200);
Serial.println(F("Debate timer ready: START/PAUSE D4, RESET D5, +1 MIN D7"));
display.setBrightness(7);
lights.begin();
lights.setBrightness(70);
showTime(true);
showLights();
}
void loop() {
static bool wasStart = false, wasReset = false, wasPlus = false;
static int lastShown = 32767;
static bool lastColon = false;
if (pressed(BTN_START, wasStart)) {
running = !running;
tickAt = millis() + 1000;
Serial.println(running ? F("Running") : F("Paused"));
}
if (pressed(BTN_RESET, wasReset)) {
running = false;
remaining = setSec;
beepsLeft = 0;
digitalWrite(BUZZER_PIN, LOW);
Serial.print(F("Reset to "));
Serial.print(setSec / 60);
Serial.println(F(" min"));
}
if (pressed(BTN_PLUS, wasPlus)) {
if (!running && remaining == setSec) {
setSec = setSec >= MAX_SET_SEC ? 60 : setSec + 60; // set the speaking time
remaining = setSec;
} else if (remaining < 32000 - 60) {
remaining += 60; // chair grants an extra minute
}
Serial.print(F("Time now "));
Serial.print(remaining / 60);
Serial.println(F(" min"));
}
if (running && millis() >= tickAt) {
tickAt += 1000;
if (remaining > -MAX_OVER_SEC) remaining--;
if (remaining == WARN_SEC) bell(1, 300);
else if (remaining == 0) bell(2, 600);
else if (remaining < 0 && -remaining % 30 == 0) bell(3, 150);
Serial.print(remaining < 0 ? F("overtime -") : F("left "));
Serial.print(abs(remaining) / 60);
Serial.print(':');
if (abs(remaining) % 60 < 10) Serial.print('0');
Serial.println(abs(remaining) % 60);
}
bool colon = !running || (millis() % 1000) < 500;
if (remaining != lastShown || colon != lastColon) {
lastShown = remaining;
lastColon = colon;
showTime(colon);
}
static unsigned long lastLights = 0;
if (millis() - lastLights >= 100) {
lastLights = millis();
showLights();
}
serviceBell();
}// Debate / elocution competition timer — Arduino Uno + TM1637 + WS2812B stick + 3 buttons + buzzer.
// Counts down the speaking time as mm:ss. The light stick is green, turns yellow with one bell at
// 1 minute left, and red with a double bell at time up; then the display counts overtime as -m:ss
// with a triple bell every 30 s. The colon blinks while the clock runs.
// Buttons (to GND): START/PAUSE D4, RESET D5, +1 MIN D7. TM1637 CLK D2 / DIO D3.
// WS2812B DIN → D6 through 330 Ω. Active buzzer module I/O → D8.
#include <TM1637Display.h>
#include <Adafruit_NeoPixel.h>
constexpr uint8_t CLK = 2, DIO = 3, BTN_START = 4, BTN_RESET = 5, LED_PIN = 6, BTN_PLUS = 7, BUZZER_PIN = 8;
constexpr uint8_t NUM_LEDS = 8;
constexpr int DEFAULT_SEC = 180; // 3 minutes per speaker
constexpr int WARN_SEC = 60; // yellow + one bell
constexpr int MAX_SET_SEC = 900; // +1 MIN cycles 1 … 15 minutes
constexpr int MAX_OVER_SEC = 599; // overtime display limit (-9:59)
TM1637Display display(CLK, DIO);
Adafruit_NeoPixel lights(NUM_LEDS, LED_PIN, NEO_GRB + NEO_KHZ800);
int setSec = DEFAULT_SEC;
int remaining = DEFAULT_SEC; // goes negative in overtime
bool running = false;
unsigned long tickAt = 0;
// Non-blocking bell: `beepsLeft` beeps of `beepMs`, separated by 200 ms.
uint8_t beepsLeft = 0;
unsigned int beepMs = 0;
unsigned long beepAt = 0;
bool beepOn = false;
void bell(uint8_t count, unsigned int ms) {
beepsLeft = count;
beepMs = ms;
beepOn = false;
beepAt = millis();
}
void serviceBell() {
if (!beepsLeft || millis() < beepAt) return;
beepOn = !beepOn;
digitalWrite(BUZZER_PIN, beepOn);
if (beepOn) beepAt = millis() + beepMs;
else {
beepsLeft--;
beepAt = millis() + 200;
}
}
void showTime(bool colon) {
int t = abs(remaining);
uint8_t seg[4];
if (remaining < 0) {
seg[0] = SEG_G; // minus sign
seg[1] = display.encodeDigit(min(t / 60, 9));
} else {
seg[0] = t >= 600 ? display.encodeDigit(t / 600) : 0;
seg[1] = display.encodeDigit((t / 60) % 10);
}
if (colon) seg[1] |= 0x80; // colon between digits 2 and 3
seg[2] = display.encodeDigit((t % 60) / 10);
seg[3] = display.encodeDigit(t % 10);
display.setSegments(seg);
}
void showLights() {
uint32_t c;
if (remaining > WARN_SEC) c = 0x00FF00;
else if (remaining > 0) c = 0xFFA000;
else c = (running && (millis() / 400) % 2) ? 0 : 0xFF0000; // red, flashing while overtime runs
lights.fill(c);
lights.show();
}
bool pressed(uint8_t pin, bool& was) {
bool down = digitalRead(pin) == LOW;
bool edge = down && !was;
was = down;
if (edge) delay(25); // debounce
return edge;
}
void setup() {
pinMode(BTN_START, INPUT_PULLUP);
pinMode(BTN_RESET, INPUT_PULLUP);
pinMode(BTN_PLUS, INPUT_PULLUP);
pinMode(BUZZER_PIN, OUTPUT);
Serial.begin(115200);
Serial.println(F("Debate timer ready: START/PAUSE D4, RESET D5, +1 MIN D7"));
display.setBrightness(7);
lights.begin();
lights.setBrightness(70);
showTime(true);
showLights();
}
void loop() {
static bool wasStart = false, wasReset = false, wasPlus = false;
static int lastShown = 32767;
static bool lastColon = false;
if (pressed(BTN_START, wasStart)) {
running = !running;
tickAt = millis() + 1000;
Serial.println(running ? F("Running") : F("Paused"));
}
if (pressed(BTN_RESET, wasReset)) {
running = false;
remaining = setSec;
beepsLeft = 0;
digitalWrite(BUZZER_PIN, LOW);
Serial.print(F("Reset to "));
Serial.print(setSec / 60);
Serial.println(F(" min"));
}
if (pressed(BTN_PLUS, wasPlus)) {
if (!running && remaining == setSec) {
setSec = setSec >= MAX_SET_SEC ? 60 : setSec + 60; // set the speaking time
remaining = setSec;
} else if (remaining < 32000 - 60) {
remaining += 60; // chair grants an extra minute
}
Serial.print(F("Time now "));
Serial.print(remaining / 60);
Serial.println(F(" min"));
}
if (running && millis() >= tickAt) {
tickAt += 1000;
if (remaining > -MAX_OVER_SEC) remaining--;
if (remaining == WARN_SEC) bell(1, 300);
else if (remaining == 0) bell(2, 600);
else if (remaining < 0 && -remaining % 30 == 0) bell(3, 150);
Serial.print(remaining < 0 ? F("overtime -") : F("left "));
Serial.print(abs(remaining) / 60);
Serial.print(':');
if (abs(remaining) % 60 < 10) Serial.print('0');
Serial.println(abs(remaining) % 60);
}
bool colon = !running || (millis() % 1000) < 500;
if (remaining != lastShown || colon != lastColon) {
lastShown = remaining;
lastColon = colon;
showTime(colon);
}
static unsigned long lastLights = 0;
if (millis() - lastLights >= 100) {
lastLights = millis();
showLights();
}
serviceBell();
}Compiled for uno on avr 1.8.8 with TM1637 1.2.0 · 3 Nov 2026
How it works
At every school and college debate, elocution or paper-presentation round, someone holds up “1 minute” and “time up” cards. This timer replaces them with lights the speaker can see from the podium and bells the audience can hear.
How it works
The Uno keeps the remaining time in seconds and counts down once per second using millis(), so the buttons and lights keep working between ticks.
| Time left | Light stick | Bell |
|---|---|---|
| More than 1:00 | Green | — |
| 1:00 to 0:01 | Yellow | One ring at 1:00 |
| 0:00 and overtime | Red, flashing | Two rings at 0:00, three every 30 s after |
The TM1637 shows mm:ss with the colon blinking while the clock runs (steady when paused). After time up it counts the overtime as -m:ss, which judges often need for penalties.
Three buttons (all to GND with the internal pull-ups):
- START/PAUSE (D4) — starts or pauses the clock.
- RESET (D5) — back to the set time, stopped.
- +1 MIN (D7) — sets the speaking time (1–15 min) before a speech, or grants an extra minute during one.
The bells are generated without delay(): a small routine turns the buzzer on and off at the right moments, so the countdown never stutters while a bell rings.
What to show at the exhibition
Run a 2-minute “mini debate” with a volunteer and let the audience watch the light change colour and hear the bells. Then show the overtime count. If you have a big 1.2-inch display, put it on a stand facing the audience like a real competition.
Questions the examiner may ask
- How does the timer stay accurate? Each tick is scheduled 1000 ms after the previous one (
tickAt += 1000), not 1000 ms after the code finished, so small delays don’t add up. The Uno’s ceramic resonator is accurate to about 0.1–0.5 %, a second or two in 10 minutes. - How do you show a minus sign on a 7-segment display? Light only segment G (the middle bar).
- Why
INPUT_PULLUP? It saves three resistors: the pin reads HIGH until the button connects it to GND. - What does
F()do? It keeps the text in flash memory instead of copying it into the Uno’s 2 KB of RAM.
Cost in India
About ₹1,150 with a big 1.2-inch display and an adapter; about ₹900 with the small 0.56-inch display. Schools that already have an Uno spend under ₹500.
Troubleshooting
The time runs but the colon never shows
Your module has decimal points instead of a colon. It still works; the minutes and seconds just aren’t separated.
Pressing START does nothing
Check the button is between D4 and GND (on a 4-leg tactile button, use two legs on opposite corners). The Serial Monitor prints “Running” or “Paused” on every press.
The lights are blue or the wrong colour
Your stick is RGB order: change NEO_GRB to NEO_RGB.
The Uno resets when the lights turn red
Weak supply — use a 2 A adapter, not a laptop port, and fit the 1000 µF capacitor at the stick.
Upgrades
- Add a wireless remote (a cheap 433 MHz RF remote with an RX480 receiver) so the chairperson can control it from the jury table.
- Show the speaker number and keep a log of every speaker’s time on the Serial Monitor for the results sheet.
- Add a two-minute “cross-examination” mode with a different colour sequence.



