MicroPython 'pyb' Library: Delays & Timing

Contents

Introduction

This article investigates methods from the Pyboard-specific MicroPython pyb library to:

  1. Code a defined length delay during program execution
  2. Precisely time a process.

The more generic MicroPython time library provides similar methods and are discussed in the article MicroPython 'time' Library: Delays & Timing.

Delays During Code Execution

There are many instances when program execution must be held for a short period of time. A classic example occurs when the microcontroller requests a reading from a sensor then must wait while the sensor takes the reading and processes it ready for the microcontroller to receive the value.

From the pyb library there are two delay methods:

  1. pyb.delay(ms) : Delay for the given number of milliseconds.
  2. pyb.udelay(us) : Delay for the given number of microseconds.

Example 1   is a made-up example where a sensor measures the number of widgets in a sample.


Example 1:
# Simulates a sensor that measures widgets.

# Algorithm:
#   (1) The microcontroller requests a measurement.
#   (2) The microcontroller must wait 500 ms for the sensor
#       to take the measurement and do any conversions.
#   (3) The microcontroller asks for the reading from the
#       sensor.

import pyb
import random

class WIDGETS():
    def Measurement(self):
        pass
    
    def Reading(self):
        return random.uniform(3.0, 5.0)
    
widgets = WIDGETS()
widgets.Measurement() # Request measurement
pyb.delay(500) # delay for 500 ms
print(widgets.Reading(), 'Widgets') # Get reading and report it

Sample Output:
4.652955 Widgets
          

This example uses the uniform() method from the random MicroPython standard library. In this case it generates a random float value between 3.0 and 5.0 inclusive.

Timing a Process

Introducing a delay into the execution of the MicroPython program is often very useful. The main problem is this delay is blocking i.e. execution of code (except for interrupt handling) comes to a standstill till the delay interval times out.

Sometimes it's necessary for a process to continue but not beyond a set time limit. Or it might be necessary to know how long a process takes to complete.

The pyb library has four methods that are useful for accurately timing a process:

  1. pyb.millis() : Returns the number of milliseconds since the board was last reset.
  2. pyb.micros() : Returns the number of microseconds since the board was last reset.
  3. pyb.elapsed_millis(start) : Returns the number of milliseconds which have elapsed since start.
  4. pyb.elapsed_micros(start) : Returns the number of microseconds which have elapsed since start.

The methods pyb.millis() and pyb.micros() count up to a maximum value then wrap back to zero. Direct arithmetic shouldn't be applied to the values from these methods.

This is why the methods pyb.elapsed_millis() and pyb.elapsed_micros() are provided. They take into account this wraparound behaviour.

Example 2  calculates the sequence usually referred to as 'the sum of the squares of the first n  natural numbers'.

  •   Sum = 12 + 22 + 32 + ... + n2  

Since this series is infinite in nature the calculation will only be permitted to run for 700 microseconds.


Example 2:
# Calculates the sum of the squares of the first 'n' integers.
# The calculation is allowed to proceed for 700 microseconds.

import pyb

start = pyb.micros()
sum = n = 0

# Calculate series for about 700 microseconds.
while pyb.elapsed_micros(start) < 700:
    n += 1
    sum += n*n
    
print('n:', n, '  Sum:', sum)

Output:
n: 30   Sum: 9455
          

Example 3  times a process that needs to be measured in microseconds. In this case the actual process of doing the timing must be measured and offset.


Example 3:
# Program that uses methods from the MicroPython 'pyb' library
# to time a coded 200 us delay.

import pyb

# Calculate offset.
# This is the time in microseconds that is taken to measure
# the 'measuring' process.
start = pyb.micros()
offset = pyb.elapsed_micros(start)
print('Offset(us):', offset)

# Time a delay of 200 microseconds
start = pyb.micros() 
pyb.udelay(200)
interval = pyb.elapsed_micros(start)
interval -= offset
print('Coded delay (us):', 200)
print('Measured delay (us):', interval)

Sample Output:
Offset(us): 15
Coded delay (us): 200
Measured delay (us): 206
          

Example 4  times a process that needs to be measured in milliseconds. Even for a microcontroller running an interpreted language (MicroPython) this is a simple task with a high degree of accuracy.


Example 4:
# Program that uses methods from the MicroPython 'pyb' library
# to time a coded 200 ms delay.

import pyb

# Time a delay of 200 milliseconds
start = pyb.millis() 
pyb.delay(200)
interval = pyb.elapsed_millis(start)
print('Coded delay (ms):', 200)
print('Measured delay (ms):', interval)

Sample Output:
Coded delay (ms): 200
Measured delay (ms): 200
          

Power Management

The Pyboard has several useful low power states especially handy if the board is being powered by a battery. A possible scenario could be where the Pyboard is being used as a data logger.

A temperature and humidity sensor might be attached with the Pyboard required to log temperature and relative humidity to a file once every five minutes. It makes sense to reduce power consumption to a minimum on the Pyboard between each five minute read.

The following example uses the pyb.RTC.wakeup() class timer to generate the wakeup trigger. This method is discussed here: The Pyboard's Real-Time Clock.


Syntax:
pyb.stop()
Puts the Pyboard into a sleeping state which reduces
power consumption to <500 µA.

To wake from this sleep state requires an external
interrupt or a real-time clock event. Upon waking
execution continues where it left off. A RTC
wakeup event is often used. See example below.

pyb.standby()
Puts the Pyboard into a deep-sleep state which reduces
power consumption to <50 µA.

A specific external interrupt or real-time clock event
is required to wake from this state. Upon waking the
system will hard reset.

Example:
# The green LED is flashed for 500 ms
# every 3 seconds.

import pyb

# Flashes green LED for 500 ms.
def flash():
    led.on()
    pyb.delay(500)
    led.off()
    
# Define green LED
led = pyb.LED(2)

# Define RTC and set wakeup every 3 seconds.
rtc = pyb.RTC()
rtc.wakeup(3000)

# Endless loop
while True:
    flash()
    pyb.stop()
          

If using Thonny it will probably throw an error and lose connection to the Pyboard shortly after this program begins to run. The program on the Pyboard will continue to run as evidenced by the green LED continuing to flash every three seconds.

Press the reset button on the Pyboard to re-enable the connection to Thonny.