Blink

For many of us, the first program we see or run on a new platform is some variation of "Blink" to turn an LED on and off at some rate. It has become like the "Hello World" of microcontroller programming.

Here is a CircuitPython implementation:

import time
import digitalio
import board

# Setup the LED pin.
led = digitalio.DigitalInOut(board.D13)
led.direction = digitalio.Direction.OUTPUT

while True:
    led.value = True
    time.sleep(0.5) # Wait until we want to turn LED on.
    led.value = False
    time.sleep(0.5) # Wait until we want to turn LED off.

This will turn the LED on and then wait, or in other words, time.sleep() for some time and then turn the LED back off. We can even use different values for the on and off times by changing the time.sleep() parameters.

So what's the problem?

time.sleep() blocks the program while it is running. Nothing else can occur. In the basic blink example, this doesn't matter so much because we aren't trying to do anything else. But let's say we wanted to add a second LED and blink it at a different rate while still continuing to blink the first. Using time.sleep() will cause trouble because each LED will have no choice but to wait for the sleep calls that are blinking the other. Their blink rates won't ever be truly independent.

How can we work around this problem?

Is it time yet?

So we know that sleeping until it's time to act is causing trouble, but what we can we do to fix it? One possible solution is to change from sleeping to asking "is it time yet" over and over, just like an annoying sibling on a long car ride. 

Our program already has a "main loop" that will execute over and over very fast, or at least it would if we allow it to by not taking up time with long-running blocking things like time.sleep()

We can make use of time.monotonic() to check what time it is, then compare that against the last time we changed the LED to decide "is it time yet" to change the LED again.

# SPDX-FileCopyrightText: 2020 FoamyGuy for Adafruit Industries
#
# SPDX-License-Identifier: MIT

"""
Using time.monotonic() to blink the built-in LED.

Instead of "wait until" think "Is it time yet?"
"""
import time
import digitalio
import board

# How long we want the LED to stay on
BLINK_ON_DURATION = 0.5

# How long we want the LED to stay off
BLINK_OFF_DURATION = 0.25

# When we last changed the LED state
LAST_BLINK_TIME = -1

# Setup the LED pin.
led = digitalio.DigitalInOut(board.D13)
led.direction = digitalio.Direction.OUTPUT

while True:
    # Store the current time to refer to later.
    now = time.monotonic()
    if not led.value:
        # Is it time to turn on?
        if now >= LAST_BLINK_TIME + BLINK_OFF_DURATION:
            led.value = True
            LAST_BLINK_TIME = now
    if led.value:
        # Is it time to turn off?
        if now >= LAST_BLINK_TIME + BLINK_ON_DURATION:
            led.value = False
            LAST_BLINK_TIME = now

By avoiding the use of time.sleep(), we've made it so that our main loop can complete each iteration very quickly. Now we check each time through the loop whether it's time to change the LED state or not. To determine this, we check the current time against a variable storing the last time we acted, along with a duration variable. 

Once it is time to act, we change the LED state and update the variable, storing the last time we acted. 

By using multiple duration variables, we can achieve different lengths of time for the various states. In this case we have the LED staying on for twice as long as it is off 0.5 seconds ON and 0.25 seconds OFF.

This guide was first published on Oct 08, 2020. It was last updated on Mar 29, 2024.

This page (No Sleeping) was last updated on Mar 28, 2024.

Text editor powered by tinymce.