Stand-Alone Functions

Contents

Introduction

The microbit module provides seven stand-alone functions that (mostly) have no direct relationship to pieces of hardware on the micro:bit.

The only function described here that is related to hardware is one that reads the temperature sensor that is part of the microcontroller package. The reason it's covered off here is because it is only a simple and single function; not a class, object or module.

This website series is all about interfacing external components to the micro:bit, so bearing that in mind, four of these functions are potentially useful with the remaining three functions probably not so useful.

The 'useful' Functions

(1) microbit.sleep(ms)

This function causes the program to sleep for the given number of milliseconds. Sleep functions are regularly used to wait a given time before taking another reading or status from e.g. a sensor.

(2) microbit.running_time()

This function returns the number of milliseconds elapsed since the micro:bit was switched on or reset.

The following example demonstrates the functions from both modules.


Example 1
# Demonstrates usage of microbit and utime modules,
# specifically the following functions:
# microbit.sleep(), microbit.running_time()
# utime.sleep_ms(), utime.ticks_ms, utime.ticks_diff()

import microbit as m
import utime as t
# Using functions from microbit module
# to time a process.
init = m.running_time()
m.sleep(10)
final = m.running_time()
print('Using functions from microbit module')
print('Timing:', final - init, 'milliseconds\n')

# Using functions from utime module
# to time a process.
init = t.ticks_ms()
t.sleep_ms(10)
final = t.ticks_ms()
print('Using functions from utime module')
print('Timing:', t.ticks_diff(final, init), 'milliseconds')

Output:
Using functions from microbit module
Timing: 10 milliseconds

Using functions from utime module
Timing: 10 milliseconds
            

(3) microbit.scale(value, from_=(min, max), to=(min, max))

This is a very useful function that's often used to convert an analog pin voltage (e.g. from a sensor output) to a digital value in units a human understands.


Example 2:
# Demonstrate the function microbit.scale()

import microbit as m

# This example converts a value in
# centigrade to fahrenheit.

# Water freezes at 0C and 32F
Cmin = 0.0
Fmin = 32.0

# Water boils at 100C and 212F
Cmax = 100.0
Fmax = 212.0

# Convert 37C to F.
C = 37
F = m.scale(C, (Cmin, Cmax), (Fmin, Fmax))
print(C, 'centigrade = ', F, 'fahrenheit')

Output:
37 centigrade =  98.6 fahrenheit
            

The function scale() is also used in Example 3 below.

(4) microbit.run_every(callback, days, h, min, s, ms)

This a potentially useful function that might be called to schedule the microcontroller to request regular reads from a sensor. The function schedules the running of another function (callback) at intervals as specified by the time arguments.

The default value of all the time arguments is None. The idea is to change the None value on one or more of the arguments to obtain the desired time interval.

This function does not block the main program. Once it is called it will run as a background task with the main program continuing to execute.

For example, it could be used to periodically read and log data as a background task while the main program progresses with other tasks.


Example 3:
# Demonstrates the use
# of microbit.run_every()

# The microbit.run_every() function is
# invoked to call another function every
# 10 seconds to read a sensor.

# The sensor in this example program is
# simulated to return a random measurement
# in the range of 0V to 3.3V.

# The sensor has a 10-bit digital to
# analog converter (DAC) which means that
# the measurement then needs to be scaled
# back to a value between 0 and 1023
# i.e. (2**10 - 1) by the microcontroller.

import microbit as m
import urandom as r

def measure():
    # Returns a random value
    # between 0V and 3.3V
    return r.uniform(0, 3.3)
    
def reading():
    # Obtains a sensor measurement
    # and scales it to 10-bits.
    a = measure()
    d = m.scale(a, (0, 3.3), (0, 1023))
    print('analog:', a, end=', ')
    print('digital:', d)
    
    
# Continually takes a sensor
# reading every 10 seconds.
m.run_every(reading, s=10)
# Keep the program running...
while True:
    pass
 
Sample output:
analog: 2.788669, digital: 864
analog: 1.593082, digital: 494
analog: 2.219141, digital: 688
analog: 1.221218, digital: 379
analog: 0.7910952, digital: 245
           

Note the use of the uniform(a, b) function from the incredibly useful urandom module. This is a standard MicroPython module that provides functions to generate random numbers in a variety of different ways. Great for building simulations to test code!

The 'not so useful' Functions

The remaining three functions are not particularly useful when the task at hand is to interface external sensors, display devices and so on with the micro:bit. They are only mentioned here for completeness.

(5) microbit.reset()

This function causes the micro:bit to reset and re-run the loaded program.

(6) microbit.panic(error_code)

This function stops the program with a sad face image and the error code alternatively shown on the LED display continuously till the device's reset button is pressed or the unit is powered down.

(6) microbit.temperature()

The microcontroller package contains an embedded temperature sensor. This function returns the temperature in degrees centigrade. Since this sensor is contained within the microcontroller package it will often read slightly higher than ambient air temperature, especially if the micro:bit has been powered up for some time.


Example 4:
# Program to demonstrate temperature() function.

# This program will take three temperature
# readings 30 seconds apart and write
# the results to the REPL.

from microbit import temperature, sleep
print('Temperature in degrees centigrade:')
for i in range(3):
    sleep(30000)
    print(temperature())

Output:
Temperature in degrees centigrade:
22
22
22
            

The ambient temperature was measured with a calibrated mercury thermometer and found to be 19°C. The micro:bit had been powered up for about two hours.

The machine.time_pulse_us() Function

Some sensors, e.g. the HC-SR04 ultrasonic distance sensor, output their measurement as a pulse on a pin with a duration that is proportional to the value of the measurement. The duration of this pulse can be very short, even in the order of a few hundred microseconds.

This can be problematic when such a sensor is interfaced with the micro:bit. The micro:bit, programmed with MicroPython, is quite slow even though it possesses a 32-bit processor clocked at 64 MHz. MicroPython is an interpreted language that has considerable overheads when compared to a compiled language such as C++. The micro:bit's implementation of MicroPython appears to be particularly pedestrian.

The utime module has functions which 'allow' timing in microseconds such as utime.ticks_us but aren't particularly useful or accurate since the functions themselves take many microseconds to execute[1].

Additionally, the Micro:bits flavour of MicroPython does not permit the setting of external interrupts on pins that are actioned by interrupt service routines (ISR). This means that the micro:bit program must rely entirely on detecting a change on a GPIO pin by a continuous polling loop, something that is in itself very inefficient.

The machine module provides the incredibly useful function time_pulse_us() which will accurately time a pulse in microseconds on a given pin.


Syntax
machine.time_pulse_us(pin, pulse_level, timeout_us)

Where:
  pin : The given pin to monitor e.g. micropython.pin1

  pulse_level : 0 = time a LOW pulse
                1 = time a HIGH pulse

  timeout_us : Timeout value in microseconds.
               Default = 1000000 uSec (1 Sec)

  If the pin is already at pulse_level then timing
  starts straight away. If the pin doesn't change state
  before the timeout is reached then -1 is returned.

  If the pin isn't at pulse_level then the function
  waits till the pin state changes to pulse_level.
  If the pin doesn't change state before the timeout is
  reached then -2 is returned. If, during the measurement
  the pin doesn't change state before the timeout is reached
  then -1 is returned.

  In both cases, if there was no timeout, then the function
  returns the pulse interval in microseconds.

Example:

import machine
import microbit
machine.time_pulse_us(microbit.pin1, 0, 500000)

In this example the function will return the time
interval pin1 was LOW before it changed
to HIGH. If the pin does not change state within the
timeout of 500000 uSec (500 mSec) then -1 or -2 will
be returned.