Programming reference

Start coding in MicroPython on systematica products using code snippets from this programming reference!
Both alpha and omega boards come with MicroPython preinstalled, ready to use.

See also

Check out the official MicroPython documentation for more in-depth information on MicroPython itself.
See also the official website MicroPython.org and the official MicroPython Github repository.

official MycroPython documentation

Boxes like this throughout the page provide direct links to specific relevant pages in the official MicroPython documentation.

Digital IOs

Digital Inputs

alpha:ONE provides 12 digital inputs, compatible with 11-36V input voltage.
The inputs are isolated, therefore at least one reference ground has to be connected to the input ground on one of the pins labeled GND between the inputs (by default they are all connected together).
The Pin objects are ready to be used, initialized as inputs, and can be found under inputs.Pin.

Each digital input has a green LED that lights up when voltage is applied to the input (meaning the input state is then logic high).

The state of the input can be read simply by calling the method value() from the corresponding Pin object:

inputs.Pin.input1.value()      # returns the input logic state, either 0 or 1

official MycroPython documentation

pyb.Pin class

Quadrature encoder Inputs

alpha:ONE does not support quadrature encoders on the digital inputs.

official MycroPython documentation

pyb.Timer class

Digital outputs

alpha:ONE provides six power outputs compatible with a supply voltage between 9 and 48V and delivering up to 32A.
At high current levels it is recommended to monitor the board temperature. Cooling may be required in some applications.
The outputs are high-side switches, meaning they switch the supply voltage. The low side of the load is wired to ground.
The outputs can be switched on and off by calling the methods on() and off().
Each output has a yellow LED that lights up to show when the output has been switched on.
This example switches on output 1 for one second:
outputs.output1.on()      # output_1 is switched on
pyb.delay(1000)
outputs.output1.off()     # output_1 is switched off
The outputs are short-circuit protected. A red LED will light up if there is a fault.
The method is_in_fault() returns True if the protection has tripped.
The proteciton resets automatically once the fault has been removed.
The method OC_threshold() returns the current threshold in Amps for the overcurrent protection.
The outputs can also be used in PWM mode to modulate the power output to the load.
mode('PWM') is used to enable PWM mode and pulse_width_percent() sets the percentage of ON time:
outputs.output1.mode('PWM')                  # output1 is configured to PWM mode
outputs.output1.pulse_width_percent(20)      # output1 set to 20% duty cycle
pyb.delay(1000)
outputs.output1.off()                        # output_1 is switched off
Note that on() and off() still work also in PWM mode, and set the duty cycle to 100% and 0% respectively.
pwm_freq() changes the PWM frequency:
outputs.output1.pwm_freq(500)                # PWM frequency set to 500 Hz (default is 1000 Hz)
A specific PWM frequency can also be set when enabling PWM mode using the keyword pwm_freq:
outputs.output1.mode('PWM', pwm_freq=2000)   # PWM mode with 2000 Hz PWM frequency
Outputs 1 to 4 use timer5, while outputs 5 and 6 use timer8. Changing the PWM frequency of one channel affects also the others using the same timer.
To go back to GPIO mode (the default mode) simply call mode('GPIO').
Each power output has two diagnostic functions, accessible only when the output is off:
The method load_connected() returns True if a low-impedance load is connected to the output.
The method Vin() returns the actual supply voltage measured from the power output.
These diagnostic functions require the installation of voltage divider resistors on the board. Contact us for further assistance.

Attention

Make sure inductive loads such as solenoids and relays have a free-wheeling diode installed anti-parallel to the load. This is critical to avoid voltage spikes when the load is turned off.
This is even more necessary if you use the outputs in PWM mode so, that the current can recirculate through the load during the off-phase.

official MycroPython documentation

pyb.Pin class

H-Bridges

alpha:ONE does not provide H-Bridge outputs.

See also

For more detailed information on the current decay modes, read Texas Instruments’ Application Report Current Recirculation and Decay Modes. as well as the H-Bridge IC, DRV8841 datasheet.

LEDs

MicroPython LEDs

alpha:ONE has one RGB LED, accessible through the module pyb, where each color is controlled by a different LED object

LED(1)

blue

LED(2)

green

LED(3)

red

The LEDs can be turned on and off simply using the on() and off() methods:

pyb.LED(1).on()          # blue LED is switched on
pyb.delay(1000)
pyb.LED(1).off()         # blue LED is switched off

For better code readability, you can also use a more self-explanatory variable:

LED_blue = pyb.LED(1)

LED_blue.on()          # blue LED is switched on
pyb.delay(1000)
LED_blue.off()         # blue LED is switched off

Finally, the toggle() method also exists:

LED_blue.toggle()          # blue LED is toggled

Note

MicroPython LEDs are also directly accessed by MicroPython:
LED(1) is used to signal when data is being written to the flash memory.

official MycroPython documentation

pyb.LED class

RGB Status LEDs

alpha:ONE provides three additional user-controllable RGB status LEDs, accessed using the object LEDs, automatically instantiated at boot.
To set one of the LEDs to a specific color you can use the method set_color(LED_nr, color, brightness)

keyword

valid values

LED_nr

1 to 3

color

‘red’, ‘green’, ‘blue’, ‘yellow’, ‘orange’, ‘turquoise’, ‘aqua’, ‘pink’, ‘white’, ‘off’

brightness

0 to 31

LEDs.set_color(LED_nr=1,color='red')                   # with default brightness = 1
LEDs.set_color(LED_nr=2,color='green', brightness=5)
LEDs.set_color(3, 'blue')                              # shorter, using positional arguments
The colors available can be listed from the dictionary object colors:
LEDs.colors.keys()   # returns all colors
The colors dictionary can also be modified to include special, user-defined colors, if needed.
You can also directly set the LEDs to any RGB color using the method set_rgb(LED_nr, red, green, blue, brightness)

keyword

valid values

LED_nr

1 to 3

red

0 to 255

green

0 to 255

blue

0 to 255

brightness

0 to 31

LEDs.set_rgb(LED_nr=1, red=167, green=23, blue=255, brightness=3)

To turn it off you can set color = 'off':

LEDs.set_color(LED_nr=1, color='off')
LEDs.set_color(2, 'off')
The method all_off() is also provided, to turn all LEDs off at once.

Timing and interrupts

Delay and timing

Insert a delay in the execution of a program, using the module pyb

pyb.delay(100)                      # wait for 100 milliseconds
pyb.udelay(10)                      # wait for 10 microsenconds

Measure the duration of a program in milliseconds

start_ms_count = pyb.millis()       # store the starting time

pyb.delay(100)                      # the program or code block you want to measure

delta_t = pyb.elapsed_millis(start_ms_count)
print("elapsed time:", delta_t, "ms")

Measure the duration of a program in microseconds

start_us_count = pyb.micros()       # store the starting time

pyb.delay(100)                      # the program or code block you want to measure

delta_t = pyb.elapsed_micros(start_us_count)
print("elapsed time:", delta_t, "us")

alternatively you can also use the time module

import time

start_ms_count = time.ticks_ms()    # store the start time in milliseconds

pyb.delay(100)                      # the program or code block you want to measure

delta_t = time.ticks_diff(time.ticks_ms(), start_ms_count)    # compute time difference
print("elapsed time:", delta_t, "ms")

official MycroPython documentation

External interrupts

External interrupts automatically invoke a user-provided callback function each time a digital input changes value.
They can be enabled on the individual digital inputs using the class pyb.ExtInt(pin, mode, pull, callback),
where mode can have following values:

ExtInt.IRQ_RISING

The interrupt will be triggered only by rising edges

ExtInt.IRQ_FALLING

The interrupt will be triggered only by falling edges

ExtInt.IRQ_RISING_FALLING

The interrupt will be triggered both by rising and falling edges

pull is defined like with any GPIO input pin.
The following example demonstrates toggling the LED each time the digital input goes from low to high (flip-flop logic):
led = pyb.LED(1)

# first define the callback function
def cb_func():
   led.toggle()

# this will call the callback function cb_func() each time the pin object input_1 goes high, toggling the LED
ext_input_1 = pyb.ExtInt(input_1, pyb.ExtInt.IRQ_RISING, pyb.Pin.PULL_NONE, cb_func)

Important

Special care has to be given when writing callback functions (also called interrupt handlers) since they can be invoked at any time during the execution of the code and could therefore have unwanted consequences. For more information on this topic, see the detailed information in the official MicroPython documentation: Writing interrupt handlers.

Attention

Be careful with mecanical switches as they generally bounce when opening and closing, firing off multiple interrupts rather than just one. Hardware debouncing, such as a simple RC circuit, is recommended in this case.
See A Guide to Debouncing if you’re interested in a deep-dive into the topic.
Note that the isolation circuit present on the inputs will already provide some filtering of the fastest bounces.
As a minimum, at least software debouncing should be implemented, see the external interrupt datalogger example.
Software debouncing is not ideal, since multiple interrupts will still fire off, and need to be processed, but it should at least ensure that the software generally behaves as expected.

official MycroPython documentation

Timers

Timers are hardware peripherals that can be used for a variety of tasks, from frequency and pulse counting to generating PWM signals. See the official Micropython documentation for all the supported features.

Here is an example where it is used to repeatedly trigger a callback function at a precise frequency given in Hz:
# first define the callback function
def cb_func(t):
   pyb.LED(1).toggle()

# then create the timer and assign the callback function
tim = pyb.Timer(1, freq=1, callback=cb_func)    # Timer1 set to a frequency of 1 Hz

Alternatively, the callback function can also be defined or changed after creating the timer, here demonstrated using a lambda function

tim = pyb.Timer(1, freq=1)                      # Timer1 set to a frequency of 1 Hz
tim.callback(lambda t: pyb.LED(1).toggle())

The callback function can be disabled by setting it to None

tim.callback(None)

Using the machine module, a timer can also be configured to trigger a callback function just once after a delay specified in milliseconds:

import machine

def cb_function(t):
   print('test')

tim = machine.Timer()
tim.init(mode=machine.Timer.ONE_SHOT, period=2000, callback=cb_function)

official MycroPython documentation

pyb.Timer class
machine.Timer class

RTC

The RTC class provides access to the microcontroller’s Real Time Clock, which keeps track of time and date also when the board is not supplied with power, thanks to the CR2032 backup battery. The object rtc is automatically instantiated on boot.
The method datetime() is used both to set and to read the current datetime and uses a tuple with format
(year, month, day, weekday, hours, minutes, seconds, microseconds):
rtc.datetime((2013, 4, 29, 1, 0, 0, 0, 0))    # set a fairly specific date and time

pyb.delay(1000)                               # 1 s delay so you see a difference in the current time
rtc.datetime()                                # returns a tuple corresponding to the current datetime

If you need a datetime string, such as for a timestamp, you can use the method datetime_str():

rtc.datetime_str()
You can also call datetime_print() to print it out.
If a different format is needed, the format string _formatstring and/or the method datetime_str() can be overridden.
Alternatively, the time module provides access to different time formats, such as the Epoch (in seconds since Jan 1, 2000)
import time

print(time.mktime(time.localtime()))            # seconds since Jan 1, 2000

official MycroPython documentation

Communication interfaces

Ethernet

alpha:ONE provides an ethernet interface through a Wiznet W5500 ethernet controller.
The network interface object eth is automatically instantiated on boot.

The network interface then needs to be activated and configured, e.g. using DHCP:

eth.active(True)
eth.ifconfig('dhcp')

You can also configure a static IP by passing a tuple (ip_address, subnet_mask, gateway, dns_server)

eth.active(True)
eth.ifconfig(('192.168.1.88','255.255.255.0','192.168.1.1','192.168.1.1'))

This snippet prints out information on the state of the ethernet interface, so you can verify it is connected:

print('eth.status =', eth.status())
print('eth.active =', eth.active())
print('eth.isconnected =', eth.isconnected())
print('eth.ifconfig =', eth.ifconfig())

Look-up the IP address of a host using socket (it will automatically use the network interface available):

import usocket as socket
ip_addr = socket.getaddrinfo('www.systematica.ch',80)[0][-1][0]
print('www.systematica.ch has address', ip_addr)
To ping a host / ip address you can use the uping utility bundled with the firmware.
It can be imported and used as follows:
from uping import ping
ping('www.systematica.ch', count=4, interval=1000)    # ping www.systematica.ch four times with an interval of 1000 ms

Use a socket to receive data from a server:

import usocket as socket

addr_info = socket.getaddrinfo("towel.blinkenlights.nl", 23)
addr = addr_info[0][-1]

s = socket.socket()
s.connect(addr)

while True:
  data = s.recv(500)
  print(str(data, 'utf8'), end='')

You can interrupt the program execution with Ctrl + C as usual

official MycroPython documentation

network module
network.WIZNET5K class
network.LAN class
socket module
uping module by Github user Shawwwn (not part of the official MicroPython project)

CAN bus

alpha:ONE has a single CAN Bus interface named can
It supports baudrates up to 1 Mbit/s and tolerates bus fault voltages up to +/- 70V.
The 120 Ohm bus terminator is permanently connected.

CAN Bus test in loopback mode (to be able to test without actual bus nodes connected)

can.init(mode=can.LOOPBACK, baudrate=250_000)
can.setfilter(0, can.LIST16, 0, (123, 124, 125, 126))       # set a filter to receive messages with id=123, 124, 125, 126
can.send('message!', 123)                                   # send a message with id 123
can.recv(0)                                                 # receive message on FIFO 0

CAN Bus test in normal mode (at least one more node has to be present on the bus so, that the messages are acknowledged, otherwise the message will keep retransmitting indefinitely)

can.init(mode=can.NORMAL, baudrate=250_000)
can.setfilter(0, can.LIST16, 0, (123, 124, 125, 126))       # set a filter to receive messages with id=123, 124, 125, 126
can.send('message!', 123)                                   # send a message with id 123
can.recv(0)                                                 # receive message on FIFO 0

official MycroPython documentation

pyb.CAN class

RS422/485

alpha:ONE provides two RS422/485 Full-duplex interfaces, using objects rs485_1 and rs485_2.
They support baudrates up to 20 Mbit/s and tolerate line fault voltages up to +/- 60V.
The 120 Ohm terminators are permanently connected.
A low-EMI mode up to 250 kbit/s is software selectable using the method slew_rate('limited') (default).
To use the interface in 4-wire, full-duplex mode, connect the AB (rx pair) and YZ (tx pair) and use the following code:
rs485_1.mode('4-Wire')                                # default mode
rs485_1.init(baudrate=9600)
rs485_1.write('Test RS485 full-duplex 9.6 kbaud')
In this mode the receiver is always enabled, while the transmitter is only enabled while sending.
The methods write(), writechar() and sendbreak() automatically toggle the driver enable pin on and off when transmitting.
To use the interface in half-duplex mode (2-wire), jumper A-Y and B-Z, connect AB and use the following code:
rs485_1.mode('2-Wire')
rs485_1.init(baudrate=9600)
rs485_1.write('Test RS485 half-duplex 9.6 kbaud')
In this mode the receiver is automatically disabled when the transmitter is enabled to suppress local echo.
If you wish to receive an echo of the transmitted data, this can be enabled using echo('on'):
rs485_1.echo('on')
This can also be configured toghether with the mode by calling
rs485_1.mode('2-Wire echo')

|mode() returns the current mode:

rs485_1.mode()

For baud rates above 250 kbit/s the driver must be configured in fast mode using slew_rate('max'):

rs485_1.init(baudrate=500000)
rs485_1.slew_rate('max')
rs485_1.write('Test RS485 500 kbaud')

official MycroPython documentation

pyb.UART class

RS232/485

alpha:ONE provides a serial interface switchable between RS232 and RS485 half-duplex.
The interface object is named rs232_485 and can be used as follows:
For RS232 mode, connect Tx, Rx and GND to the device and use following code:
# set up interface in RS232 mode
rs232_485.init(baudrate=9600)
rs232_485.mode('RS232')
rs232_485.slew_rate('slow')            # enables low-EMI mode for <= 250 kBit/s (dafault)

# send a test message
rs232_485.write('Test RS232')
For baudrates higher than 250 kbit/s select slew_rate('max').
termination() does not apply to RS232 mode.
For RS485 half-duplex mode, connect A, B and GND to the device and use following code:
# set up interface in RS485 half-duplex mode
rs232_485.init(baudrate=9600)
rs232_485.mode('RS485')
rs232_485.termination('on')            # enable 120 Ohm termination

# send a test message
rs232_485.write('Test RS485 half-duplex')
The bus termination should only be enabled with termination('on') if the bus is otherwise unterminated.
Only one termination at each end of the bus is required for correct operation.
slew_rate() does not apply to RS485 mode.

official MycroPython documentation

pyb.UART class

Multifunction pins

alpha:ONE provides 21 multifunction pins, which can be used to expand the functionality of the board and connect to expansion boards. These pins can be used freely as they are dedicated exclusively to this function and are found under gpios.Pin, named gpio1 to gpio21.
Refer to the pinout documentation to see what hardware capabilities are available on each pin.
alpha:ONE also provides on the gpio terminal a 5V output (always on), protected by a 350 mA resettable fuse, as well as a 3.3V from a dedicated LDO regulator, which can be controlled using AUX_3V3() as follows:
gpios.AUX_3V3('on')        # turns on the 3V3 regulator
Alternatively, the gpios.Pin.EN_3V3 regulator enable pin can also be manipulated directly.

Attention

These pins are 3.3 V level and may need additional circuitry to interface to third party hardware with other voltage levels.
Never connect voltages higher than 3.3 V or reverse voltages, as they may cause permanent damage to the board!

GPIO output

These pins can be used as 3.3 V digital outputs, providing up to a few mA.
Current-limiting resistors may be needed, for example if you connect an LED.
gpios.Pin.gpio1.init(pyb.Pin.OUT)

# turn on the output for 1s
gpios.Pin.gpio1.high()
pyb.delay(1000)
gpios.Pin.gpio1.low()

official MycroPython documentation

pyb.Pin class

GPIO input

GPIOs can also be used as digital inputs with a maximum voltage of 3.3 V.
You also have to specify if / what kind of pull you want to enable:

Pin.PULL_UP

enables a pull up of about 40 kOhm to 3.3 V

Pin.PULL_DOWN

enables a pull down of about 40 kOhm to ground

Pin.PULL_NONE

leaves it floating. Use this only if the circuit you connect already has a pull resistor installed.

gpios.Pin.gpio1.init(pyb.Pin.IN, pyb.Pin.PULL_UP)
gpios.Pin.gpio1.value()

official MycroPython documentation

pyb.Pin class

PWM

Pulse-width modulation is also supported on selected pins, using timer hardware.
Verify which timer and channel correspond to which pin first.
This example uses Timer2, Channel1 on GPIO9
# instantiate the timer with a frequency of 1 kHz
tim = pyb.Timer(2, freq=1000)    # the frequency will be the PWM frequency

# then create the pwm channel on the pin
ch = tim.channel(1, pyb.Timer.PWM, gpios.Pin.gpio9)

# you can then set or change the pulse width in percent
ch.pulse_width_percent(50)

official MycroPython documentation

pyb.Pin class
pyb.Timer class

ADC

The Analog to Digital Converter ADC allows you to convert an analog voltage to a digital value. (available on selected pins)
The ADC has a resolution of 12 bit, meaning the measured value will be between 0 and 4095 for a voltage between 0 and 3.3 V.
adc = pyb.ADC(gpios.Pin.gpio8)           # GPIO8 or cpu pin A4
adc.read()                               # read value, 0 to 4095 corresponding to a voltage of 0 to 3.3V

official MycroPython documentation

pyb.Pin class
pyb.ADC class

DAC

The Digital to Analog Converter DAC generates an analog voltage proportional to a digital value. (available on selected pins)
The DAC default resolution is 8 bit, meaning the value will have to be between 0 and 255 for an output of 0 to 3.3V.
A higher resolution of 12 bits can be selected by passing the argument bits=12
It is recommended to enable output buffering using buffering=True, unless your load has a very high impedance (MOhm range).
dac = pyb.DAC(gpios.Pin.gpio8, buffering=True)     # GPIO8 or cpu pin A4
dac.write(123)                                     # output between 0 and 255

official MycroPython documentation

pyb.Pin class
pyb.DAC class

UART

Universal Asynchronous Receiver-Transmitter, a.k.a. serial ports
An UART uses two pins, TX and RX, and is full-duplex capable:

TX

transmit data

RX

receive data

UARTS on multifunctioni pins are compatible with 3.3V level devices. An external transceiver is needed for other levels.
Hardware UARTS are available on selected pins using pyb.UART()
This example uses UART4 on GPIO19 (TX) and GPIO20 (RX)
Note that UART4 is assigned to these pins in the Firmware, so Alternate Function configuration is not necessary.
uart = pyb.UART(4, 9600)
uart.write('UART test')
uart.read()                # read what has been received in the UART buffer

official MycroPython documentation

pyb.UART class

SPI bus

Serial Peripheral Interface Bus
It uses 4 pins and is full-duplex capable:

SCK

clock signal

MISO

Master-in, Slave-out data

MOSI

Master-out, Slave-in data

CS

Chip Select, must be pulled low to enable communication with a slave device

Hardware SPI buses are available on selected pins using pyb.SPI()
This example uses SPI4 on pins E5 (MISO), E6 (MOSI), E12 (SCK) and E4 (CS)
# configure SPI4 as a master/controller
spi = pyb.SPI(4, mode=pyb.SPI.MASTER, baudrate=1000000, polarity=1, phase=0)
cs = pyb.Pin(pyb.Pin.cpu.E4, pyb.Pin.OUT, value=1)

cs.low()                              # enable chip select
spi.send(b'\x01\x02\x03\x04')         # send bytes on the bus
spi.recv(5)                           # receive 5 bytes on the bus
spi.send_recv(b'\x01\x02\x03\x04')    # send message and simultaneously receive 4 bytes
cs.high()                             # disable chip select
Whenever possible use hardware SPI peripherals, as they offer the best performance. However, if this is not possible, a software SPI interface can also be created on any pin using machine.SoftSPI():
This example uses a SoftSPI on GPIO17 (MISO), GPIO18 (MOSI), GPIO19 (SCK) and GPIO20 (CS)
from machine import SoftSPI

# create Software SPI bus as a master/controller (pinout for omega)
spi = SoftSPI(baudrate=500000, polarity=1, phase=0, bits=8, firstbit=SoftSPI.MSB, sck=gpios.Pin.gpio19, mosi=gpios.Pin.gpio18, miso=gpios.Pin.gpio17)
cs = pyb.Pin(gpios.Pin.gpio20, pyb.Pin.OUT, value=1)

cs.low()                              # enable chip select
spi.write(b'\x01\x02\x03\x04')        # send bytes on the bus
spi.read(4)                           # receive 4 bytes on the bus
cs.high()                             # disable chip select

official MycroPython documentation

pyb.SPI class
machine.SoftSPI class

I2C bus

Inter-Integrated Circuit Bus
It uses two pins, SDA and SCL, and is only half-duplex capable:

SCL

clock signal

SDA

transmit and receive data

Hardware I2C buses are available on selected pins using pyb.I2C()
This example uses I2C2 on GPIO14 (SDA) and GPIO13 (SCL)
# create hardware I2C1 object
i2c = pyb.I2C(2, pyb.I2C.CONTROLLER, baudrate=400000)

addresses = i2c.scan()                                        # returns list of slave addresses
i2c.send('123', addr=0x42)                                    # send 3 bytes to slave with address 0x42
data = i2c.recv(2, addr=0x42)                                 # read 2 bytes from bus

i2c.mem_write(b'\x01\x02', addr=0x42, memaddr=0x10)           # write 2 bytes to slave 0x42,  memory address 0x10
data = i2c.mem_read(2, addr=0x42, memaddr=0x10)               # read 2 bytes from slave 0x42, starting from memory address 0x10
Whenever possible use hardware I2C peripherals, as they offer the best performance. However, if this is not possible, a software I2C interface can also be created on any avilable pin using machine.SoftI2C():
This example uses SoftI2C on GPIO19 (SDA) and GPIO20 (SCL)
from machine import SoftI2C

# create Software I2C bus as a controller
i2c = SoftI2C(scl=gpios.Pin.gpio20, sda=gpios.Pin.gpio19, freq=400000)

i2c.writeto(0x42, 'hello')            # write 5 bytes to slave with address 0x42
i2c.readfrom(0x42, 5)                 # read 5 bytes from slave

official MycroPython documentation

pyb.I2C class
machine.SoftI2C class

Storage

flash memory

All boards provide onboard, non-removable flash storage of 8 Mb, which is automatically mounted in the file system under /flash and can be used to store small files in addition to boot.py and main.py.

basic file system operations

You can list the /flash directory content using:
import os

print(os.listdir('/flash'))

To create a file and/or write data to it use the open() function with mode='w':

FILENAME = '/flash/test.txt'              # any file extension can be used

# create file
file = open(FILENAME, mode='w')
file.write('First line\n')                # write the first line to the file
file.close()                              # always close the file when it is not used anymore

To append data to a file use mode='a':

# append data to file
with open(FILENAME, mode='a') as file:    # 'w' would over write the data in the file
    file.write('Second line\n')           # '\n' is the escape sequence for the new line character

To print the content of a file use mode='r' to open it with read-only access, so you don’t accedentally modify it:

# read data from file
with open(FILENAME, mode='r') as f:       # using 'with' the file will be automatically closed afterwards
    for line in f.readlines():
      print(line)
Note: the statement with open() as f: makes sure the file is automatically closed after the operation.

To delete a file use os.remove()

import os

os.remove('/flash/test.txt')

To create and remove directories you can use os.mkdir(path) and os.rmdir(path), see the documentation below.

For examples on how to test for the presence or absence of files, see the code in the advanced examples

official MycroPython documentation

microSD Card

The microSD card slot adds the possibility to store larger amounts of data, such as for long-term logging.

Important

Make sure the microSD card is formatted to EXFAT, which supports card sizes >32 Gb

To use the microSD card, insert it into the slot and mount it using:

import os

os.mount(pyb.SDCard(), '/sd')

# list the SD card content
print(os.listdir('/sd'))

Attention

If an SD card is detected when booting, MicroPython will ignore the files under /flash , automatically mount the SD card and boot using the files under /sd.
If no files are found, nothing will be executed, i.e. it will not fall back to booting from /flash!
While this can be useful in some cases, it is often source of unexpected behaviour and generally it seems preferrable to always boot from /flash.
To force booting from /flash, create a folder or an empty file named SKIPSD under /flash:
import os

os.mkdir('/flash/SKIPSD')

print(os.listdir('/flash'))    # list the current directory content to verify the SKIPSD folder has been created

official MycroPython documentation

Accessing storage over USB

By default, the memory currently in use by MicroPython (flash memory or microSD card) is exposed to the Host PC as Mass Storage Device, so you can read and write files to them. This is controlled by the command found in boot.py:
pyb.usb_mode( 'VCP+MSC', msc=(pyb.Flash(),) )    # default setting

Meaning that both the Virtual Com Port for the REPL as well as the Mass Storage Device are enabled on the USB port.

Attention

Always safely remove the USB drive(s) by clicking Eject before unplugging / power cycling / hard resetting the board!
Failure to do so could cause memory corruption. You may lose the files on the device as a consequence.
→ frequently perform backup copies of important code while you develop it on the board!
To simultaneously access both the microSD card and the board’s internal flash memory from your computer as separate mass storage devices use the following command:
pyb.usb_mode( 'VCP+MSC', msc=(pyb.Flash(), pyb.SDCard()) )
To make this setting permanent you can add this line to boot.py.

official MycroPython documentation

pyb.usb_mode() function

General board control

power-saving sleep mode

For power saving purposes in battery applications you can put the CPU in a low-power state. An external interrupt or an interrupt from the RTC is then needed to wake it up.

pyb.stop()    # stop CPU, waiting for external or RTC interrupt

See rtc.wakeup() to configure a real-time-clock wakeup event, respectively pyb.ExtInt for external interrupts.

official MycroPython documentation

pyb.RTC.wakeup() method
pyb.ExtInt class

CPU frequency

You can read the CPU operating frequency, as well as the frequency of the different peripheral buses:

pyb.freq()            # get CPU and bus frequencies
In special cases, you can also change the CPU operating frequency, for instance to reduce the power consumption.
For the majority of the applications, it is however recommended to leave the default settings.
pyb.freq(120000000)    # set CPU freq to 120MHz

official MycroPython documentation

pyb.freq() method

miscellaneous functions

You can duplicate the REPL on an UART using pyb.repl_uart()

pyb.repl_uart(pyb.UART(1, 9600))    # duplicate REPL on UART(1)

info() prints lots of information about the board

pyb.info()    # print out lots of information about the board

official MycroPython documentation

pyb miscellaneous functions

unique ID

Each microcontroller has a globally-unique ID you can read out to identify a specific board:

pyb.unique_id()    # returns a 96 bit microcontroller unique identifier

official MycroPython documentation

pyb.unique_id method

Advanced examples

external interrupt datalogger

This example logs the current timestamp to a csv file located on the microSD card each time the input pin sees a rising edge (goes from low to high).
FOLDER = '/sd/'                  # where the log file will be saved
FILENAME = 'datalog_INT.csv'     # log filename, any other file extension such as .log or .txt can be used
INTERRUPT_PIN = input_1          # the input pin triggering the interrupt
DEBOUNCING_THRESHOLD = 20        # debouncing window in ms (determines minimum valid repetition rate)

import os, micropython

micropython.alloc_emergency_exception_buf(100)

last_event = pyb.millis()

# mount sd card if it isn't already
if 'sd' not in os.listdir('/'):
   os.mount(pyb.SDCard(), '/sd')

# create a new file to log to if it doesn't already exist
if FILENAME not in os.listdir(FOLDER):
   with open(FOLDER+FILENAME, 'w') as f:
      f.write('Timestamp;\n')     # write column header

# define the callback function
def schedule_log(line):
   micropython.schedule(log_event,line)

#define the logger function
def log_event(line):
   global last_event
   if pyb.elapsed_millis(last_event) > DEBOUNCING_THRESHOLD:
      last_event = pyb.millis()
      with open(FOLDER+FILENAME, 'a') as f:     # 'a' appends data to the file
         f.write(rtc.datetime_str())            # appends the timestamp

# attach the external interrupt to input_1
extint_pin = pyb.ExtInt(INTERRUPT_PIN, pyb.ExtInt.IRQ_RISING, pyb.Pin.PULL_NONE, schedule_log)

# define function to print the log file content
def print_log():
   with open(FOLDER+FILENAME, 'r') as f:
      for line in f.readlines():
         print(line, end='')

timed ADC datalogger

This example logs the timestamp and the value of an ADC pin to a csv file located on the microSD card at fixed intervals, controlled by a timer.
The voltage to be measured has to be between 0 and 3.3V, corrisponding to ADC values between 0 and 4095.
FOLDER = '/sd/'                  # where the log file will be saved
FILENAME = 'datalog_ADC.csv'     # log filename, any other file extension such as .log or .txt can be used
ADC_PIN = 'A4'                   # the ADC pin to measure
LOGGING_FREQUENCY = 10           # logging frequency in Hz

import os, micropython

micropython.alloc_emergency_exception_buf(100)

# create the ADC object on the specified pin
adc = pyb.ADC(eval('pyb.Pin.cpu.' + ADC_PIN))

# mount sd card if it isn't already
if 'sd' not in os.listdir('/'):
   os.mount(pyb.SDCard(), '/sd')

# create a new file to log to if it doesn't already exist
if FILENAME not in os.listdir(FOLDER):
   with open(FOLDER+FILENAME, 'w') as f:
      f.write('Timestamp;ADC_value;\n')     # write column header

# define the callback function
def schedule_log(tim):
   micropython.schedule(log_ADC,tim)

#define the logger function
def log_ADC(tim):
   value = str(adc.read())
   with open(FOLDER+FILENAME, 'a') as f:    # 'a' appends data to the file
      f.write(rtc.datetime_str() + ';' + value + ';\n')    #

# create timer with interrupt
timer_log = pyb.Timer(1, freq=LOGGING_FREQUENCY, callback=schedule_log)

# define function to print the log file content
def print_log():
   with open(FOLDER+FILENAME, 'r') as f:
      for line in f.readlines():
         print(line, end='')

printing log files

You can print the log file content by calling the function print_log() defined in both datalogger examples.