İçeriğe geç

Build a Fun Countdown Timer Box for Free Meals at 08.08!

If you’ve ever wanted a portable, rechargeable countdown timer that rewards you with a special blink sequence when you stop right at 08.08 (to win a free meal in a restaurant!), you’re in the right place. In this project, we’ll build a neat little box that includes an Arduino with a 4-digit 7-segment display, all powered by a Li-ion battery and packaged in a 3D-printed enclosure.

Table of Contents

  1. Project Overview
  2. Bill of Materials (BOM)
  3. Circuit and Wiring
  4. Arduino Code Walkthrough
  5. 3D-Printed Enclosure
  6. Assembly Steps
  7. Operation and Tips
  8. Conclusion

Project Overview

The main features of our Countdown Timer Box are:

  • Rechargeable Power: A single 18650 Li-ion battery, charged via a TP4056 module, boosted to 10V (approximately) by an MT3608 step-up converter.
  • Arduino Core: An Arduino Uno that handles timing and user input.
  • Interactive Display: A 4-digit 7-segment LED for time readouts.
  • Push Buttons: One to start/stop, another to reset.
  • Special Easter Egg: Stopping the timer at 08.08 triggers a flashy blink sequence—perfect for marketing your restaurant’s free-meal challenge!

Important Note:
It is not recommended to use (i.e., power on and run) the device while the battery is being charged. The TP4056 expects a certain load environment, and having the timer running can cause unexpected battery stress or charging behavior.


Bill of Materials

You’ll need the following components to build this project:

  1. Arduino Uno
  2. 4-Digit 7-Segment LED Display (common anode or common cathode; code here is for a particular pin arrangement)
  3. 2× Push Button with Cap (12 mm diameter caps)
  4. 18650 Li-ion Battery
  5. 18650 Li-ion Battery Holder
  6. TP4056 Li-ion Charger Module (for safe charging via Micro-USB)
  7. MT3608 DC-DC Boost Converter (to step battery voltage up to ~5 V)
  8. On-Off Switch (13×20 mm, or adapt to whatever fits your enclosure)
  9. Enough Cable/Wires (for all connections)
  10. Soldering Equipment (soldering iron, solder, flux, etc.)
  11. 8× M3 Nuts
  12. 8× M3 × 12 mm Screws (for the enclosure)
  13. 3× M3 × 10 mm Screws (to mount the Arduino)
  14. Hot Glue (helps create a non-slip base and secure parts)
  15. 3D Printer
  16. PETG or PLA Filament (your choice, but PETG can be more durable)

Circuit and Wiring

Below is a quick outline of how to wire everything:

  1. Battery & TP4056
    • Place the 18650 battery in the battery holder.
    • Solder the holder leads to the TP4056 module’s BAT+ and BAT–.
    • The TP4056’s IN+ and IN– connect to a micro-USB jack, so you can charge the battery via USB.
  2. TP4056 & MT3608
    • The TP4056 has output pins (OUT+ and OUT–) that feed the MT3608 input.
    • Adjust the MT3608’s output to around 10 V using its onboard potentiometer.
  3. MT3608 → Arduino
    • IMPORTANT: The MT3608 output should go to Arduino’s VIN pin rather than the 5 V pin.
    • Connect the MT3608 + output to Arduino VIN, and – output to Arduino GND.
    • This setup goes through the Arduino’s onboard regulator circuitry at VIN.
  4. Arduino → 4-Digit Display
    • Each segment pin (A, B, C, D, E, F, G, DP) is connected to a dedicated Arduino pin, as shown in the code.
    • Each digit common pin is connected to pins 6, 9, 10, 11. (These pin assignments can vary if you adapt the code.)
  5. Push Buttons
    • Start/Stop Button to Arduino digital pin 13 with INPUT_PULLUP; other side to GND.
    • Reset Button to Arduino digital pin 12 with INPUT_PULLUP; other side to GND.
  6. On-Off Switch
    • Wire the switch in series between the MT3608 output and Arduino VIN, so you can flip the entire system on and off.

Because we’re drawing power from the MT3608 into VIN, it’s recommended to confirm your Arduino’s input voltage range. The VIN pin typically needs at least ~6 V for a stable 5 V regulator output. If your boost converter is only set for 5 V, the onboard regulator may drop some voltage, so you might want to boost it slightly above 6 V if stable. Alternatively, if you prefer a simpler approach, you can feed 5 V directly to the 5 V pin—but you specifically requested VIN, so be sure to adjust accordingly.


Arduino Code Walkthrough

Below is the full code. We’ll break it down line by line to show how it counts time, updates the display, and blinks when hitting 08.08.

#include <Arduino.h>  // Standard Arduino library

// Digit pins for the 4-digit display (left to right)
int digit_pin[] = {6, 9, 10, 11};

// Segment pins (A-G, plus decimal point DP)
int segA = 2;
int segB = 3;
int segC = 4;
int segD = 5;
int segE = A0;
int segF = 7;
int segG = 8;
int segDP = A1;  // Decimal point

// Display logic (depends on common-cathode or common-anode setup)
#define DIGIT_ON  LOW
#define DIGIT_OFF HIGH

#define SEGMENT_ON  HIGH
#define SEGMENT_OFF LOW

// Push button pins
int buttonStartStop = 13;
int buttonRestart   = 12;

// "timeCount" is how we measure time steps
// 0..1000 => 0.00..10.00 (i.e., up to 10 seconds)
int timeCount = 0;
bool isRunning = false;

// Variables to track elapsed time
unsigned long previousMillis = 0;  // last time we checked
unsigned long leftoverMs = 0;      // leftover milliseconds that haven't formed a 10ms chunk yet

// We'll increment every 10ms => 1000 increments = 10 seconds
#define TIMER_STEP_MS 10

void setup()
{
  // Set segment pins to OUTPUT
  pinMode(segA,  OUTPUT);
  pinMode(segB,  OUTPUT);
  pinMode(segC,  OUTPUT);
  pinMode(segD,  OUTPUT);
  pinMode(segE,  OUTPUT);
  pinMode(segF,  OUTPUT);
  pinMode(segG,  OUTPUT);
  pinMode(segDP, OUTPUT);

  // Set digit pins to OUTPUT
  for (int i = 0; i < 4; i++) {
    pinMode(digit_pin[i], OUTPUT);
  }

  // Configure buttons with pull-ups
  pinMode(buttonStartStop, INPUT_PULLUP);
  pinMode(buttonRestart,   INPUT_PULLUP);
}

void setSegmentsForNumber(int num, bool decimalOn)
{
  // First, ignore invalid digits
  if (num < 0 || num > 9) {
    return;
  }

  // Turn on the segments needed for this digit
  switch (num) {
    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);
      break;
    case 1:
      digitalWrite(segB, SEGMENT_ON);
      digitalWrite(segC, SEGMENT_ON);
      break;
    case 2:
      digitalWrite(segA, SEGMENT_ON);
      digitalWrite(segB, SEGMENT_ON);
      digitalWrite(segD, SEGMENT_ON);
      digitalWrite(segE, SEGMENT_ON);
      digitalWrite(segG, SEGMENT_ON);
      break;
    case 3:
      digitalWrite(segA, SEGMENT_ON);
      digitalWrite(segB, SEGMENT_ON);
      digitalWrite(segC, SEGMENT_ON);
      digitalWrite(segD, SEGMENT_ON);
      digitalWrite(segG, SEGMENT_ON);
      break;
    case 4:
      digitalWrite(segB, SEGMENT_ON);
      digitalWrite(segC, SEGMENT_ON);
      digitalWrite(segF, SEGMENT_ON);
      digitalWrite(segG, SEGMENT_ON);
      break;
    case 5:
      digitalWrite(segA, SEGMENT_ON);
      digitalWrite(segC, SEGMENT_ON);
      digitalWrite(segD, SEGMENT_ON);
      digitalWrite(segF, SEGMENT_ON);
      digitalWrite(segG, SEGMENT_ON);
      break;
    case 6:
      digitalWrite(segA, SEGMENT_ON);
      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);
      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(segF, SEGMENT_ON);
      digitalWrite(segG, SEGMENT_ON);
      break;
  }

  // If decimalOn is true, light up the decimal point
  if (decimalOn) {
    digitalWrite(segDP, SEGMENT_ON);
  }
}

void turnAllDigitsOff()
{
  // Turn off all digits
  for (int i=0; i<4; i++) {
    digitalWrite(digit_pin[i], DIGIT_OFF);
  }
  // Turn off all segments
  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);
  digitalWrite(segDP, SEGMENT_OFF);
}

void displayOff(int durationMs)
{
  // Quick routine to blank the display for a set time
  unsigned long startTime = millis();
  while ((millis() - startTime) < (unsigned long)durationMs)
  {
    turnAllDigitsOff();
    delay(1);
  }
}

void loop()
{
  // --- Check Start/Stop button ---
  static bool lastStartStopState = HIGH;
  bool currentStartStop = digitalRead(buttonStartStop);

  if (lastStartStopState == HIGH && currentStartStop == LOW)
  {
    // The button was pressed => toggle the timer
    bool wasRunningBefore = isRunning;
    isRunning = !isRunning;

    // If we just started running, reset timing references
    if (!wasRunningBefore && isRunning) {
      previousMillis = millis();
      leftoverMs = 0;
    }

    // If we just stopped, check if timeCount == 808 => blink "08.08"
    if (wasRunningBefore && !isRunning && (timeCount == 808))
    {
      for(int i=0; i<10; i++)
      {
        // Show "08.08" for 200ms
        int dur = 200;
        unsigned long st = millis();
        while (millis() - st < (unsigned long)dur) {
          // Digit 0: '0'
          turnAllDigitsOff();
          setSegmentsForNumber(0, false);
          digitalWrite(digit_pin[0], DIGIT_ON);
          delay(2);

          // Digit 1: '8' + decimal
          turnAllDigitsOff();
          setSegmentsForNumber(8, true);
          digitalWrite(digit_pin[1], DIGIT_ON);
          delay(2);

          // Digit 2: '0'
          turnAllDigitsOff();
          setSegmentsForNumber(0, false);
          digitalWrite(digit_pin[2], DIGIT_ON);
          delay(2);

          // Digit 3: '8'
          turnAllDigitsOff();
          setSegmentsForNumber(8, false);
          digitalWrite(digit_pin[3], DIGIT_ON);
          delay(2);
        }
        // Turn off display for 200ms
        displayOff(200);
      }
    }
  }
  lastStartStopState = currentStartStop;

  // --- Check Reset button ---
  static bool lastRestartState = HIGH;
  bool currentRestart = digitalRead(buttonRestart);
  if (lastRestartState == HIGH && currentRestart == LOW)
  {
    // Reset the timer to 0
    timeCount = 0;
    leftoverMs = 0;
    previousMillis = millis();
  }
  lastRestartState = currentRestart;

  // --- Update timer if running ---
  if (isRunning && timeCount < 1000)
  {
    unsigned long now = millis();
    unsigned long elapsed = now - previousMillis;
    if (elapsed > 0)
    {
      previousMillis = now;
      leftoverMs += elapsed;

      // How many 10ms increments fit into leftoverMs?
      int increments = leftoverMs / TIMER_STEP_MS;
      if (increments > 0)
      {
        timeCount += increments;
        if (timeCount > 1000) timeCount = 1000; // cap at 10.00
        leftoverMs -= increments * TIMER_STEP_MS;
      }
    }
  }

  // --- Convert timeCount to digits (0.00..10.00) ---
  int integerPart  = (timeCount >= 1000) ? 10 : (timeCount / 100);
  int decimalPart  = (timeCount >= 1000) ? 0  : (timeCount % 100);

  int d0 = integerPart / 10;  // leftmost digit
  int d1 = integerPart % 10;
  int d2 = decimalPart / 10;
  int d3 = decimalPart % 10;

  // --- Display multiplexing ---
  // Digit 0
  turnAllDigitsOff();
  setSegmentsForNumber(d0, false);
  digitalWrite(digit_pin[0], DIGIT_ON);
  delay(2);

  // Digit 1 (with decimal)
  turnAllDigitsOff();
  setSegmentsForNumber(d1, true);
  digitalWrite(digit_pin[1], DIGIT_ON);
  delay(2);

  // Digit 2
  turnAllDigitsOff();
  setSegmentsForNumber(d2, false);
  digitalWrite(digit_pin[2], DIGIT_ON);
  delay(2);

  // Digit 3
  turnAllDigitsOff();
  setSegmentsForNumber(d3, false);
  digitalWrite(digit_pin[3], DIGIT_ON);
  delay(2);
}

3D-Printed Enclosure

  1. Download and Print: Grab the provided CAD/3D files (in STL format).
  2. Recommended Materials: PETG or PLA. PETG might be sturdier under heat, but PLA often prints more easily.
  3. Design Features:
    • Mounting standoffs sized for 3× M3×10 mm screws to secure the Arduino.
    • A top cutout for the 4-digit 7-segment display.
    • Slots for push buttons and an on-off switch.
    • A battery compartment to hold the 18650 cell securely.
    • Potential openings for the micro-USB port (TP4056) if you want external access for charging.

Measure your exact modules and adapt any hole placements in the CAD if needed.


Assembly Steps

  1. Print the Enclosure
    • Verify that the cutouts match your push buttons, on-off switch, and display dimensions.
  2. Mount the Battery Holder
    • Secure it inside using hot glue or fast glue.
  3. Mount the Arduino
    • Use 3× M3×10 mm screws to attach the Arduino Uno to the built-in standoffs.
    • Ensure your Arduino’s terminals remain accessible.
  4. Solder and Wire
    • Solder wires from the display to the Arduino’s corresponding pins (2, 3, 4, 5, A0, A1, 7, 8 for segments, 6, 9, 10, 11 for digit pins).
    • Solder wires from the buttons to pins 12 and 13. The other sides of the buttons go to GND.
    • Connect the MT3608 output to Arduino VIN and GND.
    • Double-check all polarities before connecting your battery.
  5. On-Off Switch
    • Place it in series on the line from MT3608 output to Arduino VIN so you can cut power completely.
  6. Insert Battery & TP4056
    • Slide the 18650 battery into its holder.
    • Position or secure the TP4056 so that the micro-USB input is accessible for charging.
  7. Close the Case
    • Use M3×12 mm screws for the enclosure lid.
    • Optionally add hot glue or rubber pads at the bottom to prevent slipping.

Operation and Tips

  1. Power On: Flip the on-off switch to supply power from the MT3608 to Arduino’s VIN.
  2. Start/Stop: Press the left button (or whichever you designated) to toggle the timer.
  3. Reset: Press the right button to reset the timer to 0.
  4. Target “08.08”: Try to stop exactly at 8.08 seconds. If you do, the display will blink “08.08” repeatedly—congratulations, you’ve won!
  5. Charging:
    • Plug a micro-USB cable into the TP4056 for recharging.
    • It is not recommended to use the device while it’s charging, as the charging circuit and boost converter can conflict under load. Let it charge, then unplug to operate.

Note: If you find the Arduino is not getting sufficient voltage through VIN (because the MT3608 is only at 5 V and there’s a drop across the onboard regulator), you may wish to boost the MT3608 output to around 6–7 V or feed the Arduino’s 5V pin directly (if you confirm the wiring). Just be mindful of your specific hardware requirements.


Conclusion

With this DIY Countdown Timer Box, you’ve:

  • Learned the basics of multiplexing a 4-digit 7-segment display with Arduino.
  • Explored Li-ion battery power using the TP4056 (charger) and MT3608 (boost converter), feeding the Arduino VIN pin.
  • Built a compact 3D-printed enclosure that houses everything neatly.
  • Created a fun, interactive timer game: Stop at 08.08 for a special surprise!

From here, you can expand the project—use a buzzer, extend the timer’s range, or incorporate Wi-Fi for a real-time scoreboard. Remember to charge safely and avoid running the system during charging. Happy making, and good luck hitting 08.08 for that free meal!


“Time you enjoy wasting was not wasted.” – John Lennon

Build Your Own Countdown Timer Box with an Arduino and Li-Ion Battery!

  • Hobby