Countdown Timer – Practical Python Tutorial Series

Welcome to the fourth instalment of our practical Python tutorial series. In the previous tutorials, you learned how to use Python with the Elecrow All-In-One Starter Kit for Arduino to:

  • respond to button events using the Robo-Tx API
  • read an analogue input and convert its value into a useful range
  • measure distance with the ultrasonic sonar module
  • display information on the LCD
  • control the All-in-One Starter Kit’s beeper

This tutorial brings many of these techniques together to create a more substantial application: a 60-minute countdown timer.

The timer uses the slider on the Elecrow All-in-One Starter Kit to select a duration, the button to control the timer, the LCD to display the remaining time and the beeper to provide an alarm when the countdown finishes.

More importantly, this project introduces two Python classes from the datetime module:

  • datetime
  • timedelta

These provide a much better way of working with elapsed time than repeatedly incrementing or decrementing a counter.

Before You Begin

If you haven’t already prepared your hardware and Python environment, first follow the setup instructions in the GitHub repository. The Python example discussed in this tutorial (3-countdown-timer.py) can be found at the same repository.

Don’t have an All-in-One Starter Kit? The Python Programming: Robotics Foundation Course provides supervised tuition together with remote access to the same All-in-One Starter kit, allowing you to learn challenging Robo-Tx API and Python programming concepts from anywhere.

The Countdown Timer Overview

The finished program behaves like a simple kitchen or laboratory timer. When the countdown is not running:

  • moving the slider selects the duration
  • the selected duration is displayed on the LCD
  • a short button press starts the countdown
  • a long button press resets the timer to the current slider position

When the countdown is running:

  • the LCD shows the remaining time
  • a short button press pauses the countdown
  • another short press resumes it

When the countdown reaches zero:

  • the countdown stops
  • an alarm pattern is played through the beeper
  • pressing the button cancels the alarm

The maximum duration is 60 minutes, and the slider selects the duration in increments of ten seconds.

The Program Structure and Design

The beginning of the program will look familiar.

import threading
import time
from datetime import datetime, timedelta
from app_config import *

The threading and time modules were introduced in earlier tutorials. The new import is:

from datetime import datetime, timedelta

These two classes provide the facilities needed to calculate when the countdown has elapsed.

Why Use datetime?

One way of implementing a countdown would be to subtract one second from a variable every second. However, that approach can become inaccurate because the computer is also performing other operations. Instead, this program records the actual target time at which the countdown should finish.

For example:

target_time = datetime.now() + timedelta(seconds=60)

This means: Take the current time and add 60 seconds. The resulting target_time represents the precise point at which the countdown should reach zero. The program can then repeatedly ask: How much time remains between now and the target time? This is a useful programming technique whenever an application needs to work with elapsed time.

Converting the Slider Value into a Duration

The sliding potentiometer all the All-in-One Starter Kit produces a raw analogue value between 0 and 1023. As you learned in the tutorial for reading analog inputs, this value can be converted before your application uses it.

This program defines a function for such a conversion:

def convert_to_duration(value):
    '''Convert raw analog value to duration as seconds, in tens of seconds.'''
    return int((360 * value) / 1023) * 10

The important part is the calculation:

int((360 * value) / 1023) * 10

The raw value is first mapped onto an integer range of 0 to 360, then multiplied by 10. The resulting range is therefore 0 … 3600 seconds. Since 3600 seconds is one hour, the slider can select a maximum duration of 60 minutes. The multiplication by 10 means that the duration changes in increments of ten seconds.

For example:

Analog value based on slider positionApproximate duration
00 seconds
17110 minutes
34120 minutes
51230 minutes
102360 minutes

The exact intermediate values depend on the analogue reading.

Registering the Converter Function

The converter function is registered in exactly the same way as in the Reading Analog Inputs tutorial:

slider = all_in_one_kit.Analog.A0
all_in_one_kit.Analog.UseConverter(AnalogConverter(convert_to_duration), slider)

This is an important feature of the Robo-Tx API. Once the converter has been registered, the program can simply use:

slider.Value

and receive a duration in seconds rather than the original 0–1023 analogue reading. The conversion logic is therefore kept separate from the code that uses the duration.

Formatting the Countdown for Display

The second function defined in the program is:

def to_time_format(seconds):
    '''Convert seconds to MM:SS format for display.'''
    return f"{seconds // 60:02}:{str(seconds % 60).zfill(2)}"

This function converts a number of seconds into a format suitable for the LCD. For example:

SecondsFormatted for Display
000:00
1000:10
6001:00
60010:00
360060:00

The display uses minutes and seconds, rather than hours and minutes. Even though the maximum duration is one hour, 60:00 is therefore displayed at the beginning of a maximum-length countdown.

Understanding the Formatting Code

There are two useful bits of Python in this function.

First:

seconds // 60

uses integer division to calculate the number of complete minutes. The remaining seconds are obtained using the modulo operator:

seconds % 60

For 90 seconds:

90 % 60 = 30

The two values can therefore be combined to produce the remaining time in the format MM:SS. This is a good example of how relatively simple Python operators can be combined to solve a practical problem.

Tracking the Countdown Timer State

The program uses three Boolean variables to keep track of what the timer is doing:

countdown_running = False
timer_reset = True
alarm_sound_active = False

These variables represent three different aspects of the application’s state.

  • countdown_running Indicates whether the countdown is currently active.
  • timer_reset Indicates that the timer duration needs to be obtained from the slider.
  • alarm_sound_active Indicates whether the timer has reached zero and the alarm is currently sounding.

Using Boolean variables to represent the state of an application is a common programming technique and is particularly useful when building interactive systems.

Starting or Resuming the Countdown

When the timer isn’t running, the program checks for a button release with an if statement. A short press and release therefore starts the countdown.

The important line is:

target_time = datetime.now() + timedelta(seconds=timer_value_seconds)

The current time is obtained and the selected duration is added to it. The result is the exact time at which the countdown should finish. If there is no remaining time left for the countdown, the countdown running state is set to false.

Pausing the Countdown

While the countdown is running, another short button press stops it:

if input_event == Input.BUTTON_1_RELEASED:
    countdown_running = False

The countdown isn’t reset. The remaining time is retained in:

timer_value_seconds

This means that pressing the button again can resume the countdown.

Using a Long Button Press to Reset

The program also makes use of the sustained button event introduced in the first tutorial. When the countdown isn’t running, holding the button resets the timer to a duration determined by the current slider position.

This provides a useful distinction between a short press and a long press:

Button actionTimer action
Short pressStart/resume
Short press while runningPause
Long press while stoppedReset using current slider position
Button press during alarmCancel alarm

This is a good example of how a small number of input events can provide several different controls without requiring additional buttons.

Updating the Countdown

While the countdown is running, the program calculates the remaining time:

timer_value_seconds = int((target_time - datetime.now()).total_seconds())

This is an important piece of code that obtains the difference between the target time and the current time, represented in seconds. The result is then converted to an integer and displayed on column 0 and row 0 of the LCD using:

display.PrintAt(0, 0, to_time_format(timer_value_seconds))

As the target time approaches, the displayed value decreases.

Detecting the End of the Countdown

The program checks:

if timer_value_seconds <= 0:

Once the countdown reaches zero, several things happen:

countdown_running = False
alarm_sound_active = True
beeper.RunPattern(50, 50, 4, 3, 500)

The timer is stopped and the alarm active state is set to true. The beeper is then instructed to play a repeating pattern. In this example the pattern produces:

  • a group of four short beeps
  • repeated three times
  • with a 500 ms interval between groups

Conceptually, the resulting alarm sounds like: beep beep beep beep    beep beep beep beep    beep beep beep beep. The exact timing of the individual beeps is specified by the arguments passed to RunPattern(). This demonstrates the Robo-Tx API allowing the Python programmer to describe what they want the hardware to do without having to implement the low-level timing themselves.

Programming Concepts Covered

This practical Python example introduces several new Python concepts while reinforcing those from earlier tutorials.

  • datetime
  • timedelta
  • calculating elapsed time
  • formatting numerical values
  • state-based program logic
  • reusable functions for data conversion
  • complex conditional expressions

The increasing complexity is intentional. Rather than introducing completely unrelated examples, each tutorial combines techniques from previous exercises and adds another layer of programming.


Challenge Exercises

Now try modifying the countdown timer program yourself with these new requirements:

  • Modify convert_to_duration() so that the maximum countdown is 30 minutes instead of 60 minutes.
  • Make the beeper produce warning beeps when the countdown reaches 10 seconds remaining. Use beeper.Repeat(500, 500) for the warning beeps.
  • Use the LCD to create a simple progress indicator, e.g. [##########——]. The number of characters in the progress bar should represent the proportion of the countdown that has elapsed. The maximum number of characters the LCD can display on one row is 16.

Looking Ahead

The next practical Python tutorials will continue this progression by introducing more of the sensors and actuators available on the Elecrow All-in-One Starter Kit for Arduino. The ultimate goal isn’t simply to learn how to operate the All-in-One Starter Kit. It is to use the hardware as a practical environment in which you can develop real Python programming and robotics software development skills.

Leave a Reply

Your email address will not be published. Required fields are marked *