Frédéric Boulanger
CentraleSupélec LRI
Département informatique Équipe VALS
Bât Breguet, 3 rue Joliot-Curie Bât 650 Ada Lovelace, Université Paris Sud
91190 Gif-sur-Yvette, France Rue Noetzlin, 91190 Gif-sur-Yvette, France
Téléphone : +33 [0]1 69 85 14 84
Télécopie : +33[0]1 69 85 14 99

Contents MicroPython is a version of Python 3 which runs on microcontrollers.

I use it on a pyboard for hands-on lab sessions in a computer architecture course (in French). It is like an Arduino on steroids, with an embedded Python 3 interpreter, which makes it very suitable for teaching in French “grandes écoles” since all students in prep classes now use Python in their “computer science” course.

I now also use it on an ESP32 for personal and student projects. The big advantage of the ESP32 is that it has WiFi and Bluetooth and that it is really cheap!

Using the pyboard

When plugged into a USB port of a computer, the pyboard appears as a disk. You can edit the file on this disk, then eject it, and press the RST button on the pyboard to reboot it and execute

However, there is a more interactive way of playing with the pyboard which has a REPL (Read-Eval-Print Loop). For this, you need to connect to the /dev/ttyACM0 (Linux) or /dev/tty.usbmodemXXXX (MacOS) using a serial communication program. The Unix screen command works fine, I used minicom for a while, but it does not seem to work with some ESP32 cards with MicroPython, so I switched to picocom. There are specific instructions for using the pyboard with Windows.

Pyterm Pyterm

The following script, which I use to call pyterm, will look for a plugged pyboard or ESP32 board and connect to the first one it finds using picocom:

#!/usr/bin/env python3
import sys
import os
import fnmatch

Usage: pyterm [device]
Calls picocom on "device". If device is not given, looks for /dev/ttyACM* devices
(the name of the pyboard under Linux) or /dev/tty.usbmodem* devices (name of the
pyboard under MacOS).
def main():
  if len(sys.argv) > 1:
    ttymodems = [sys.argv[1]]
    # look for pyboard v1 under Linux
    ttymodems = fnmatch.filter(os.listdir('/dev'), 'ttyACM*')
    if len(ttymodems) == 0:
      # look for pyboard v1 under MacOS
      ttymodems = fnmatch.filter(os.listdir('/dev'), 'tty.usbmodem*')
    if len(ttymodems) == 0:
      # look for wipy under MacOS
      ttymodems = fnmatch.filter(os.listdir('/dev'), 'tty.usbserial-*')
    if len(ttymodems) == 0:
      # look for ESP32 board under MacOS
      ttymodems = fnmatch.filter(os.listdir('/dev'), 'tty.SLAB_USBtoUART')
    if len(ttymodems) == 0:
      print('Error: no pyboard found.')
    ttymodems[0] = '/dev/' + ttymodems[0]
# os.system('minicom -D '+ttymodems[0])
  os.system('picocom '+ttymodems[0]+' -b115200')

if __name__ == "__main__":

Once connected, you can type Python commands and discover many possibilities of the pyboard. For instance:

led = pyb.LED(1)

will turn on the red LED on the pyboard.

led = pyb.LED(1)
def blink(timer):
tim = pyb.Timer(4, freq=2, callback=blink)

will make the red LED blink at 1Hz (it is toggled on/off at 2Hz).

To close the connection and exit type:

  • Ctrl-A followed by X with minicom (it is ESC followed by X on MacOS)
  • Ctrl-A followed by Ctrl-X with picocom.
  • Ctrl-A followed by :quit with screen.

You may also use the command that comes with pySerial. The default exit character is Ctrl-], which you cannot type on a French keyboard, so use --exit-char 0 to be able to exit with Ctrl-@.

You will find more information about what you can do with the pyboard in the MicroPython tutorial.

Loading code into the pyboard Loading code

For writing more complex code, there are two possibilities:

  1. edit the files on the pyboard, and load them into the REPL using import
  2. edit the files on your computer, and paste them into the REPL

The first solution works fine and is the only way to work with code that is split into different modules.

The second solution is perhaps easier, but you cannot paste too much text in the REPL without getting errors. You have to switch to the raw REPL by hitting Ctrl-A, pasting your code, and hitting Ctrl-D to finish the transfer. Your code will be running on the pyboard. Hit Ctrl-C to kill it if it does not terminate by itself, then hit Ctrl-B to exit the raw REPL and switch back to the friendly REPL. You can perform a soft reset of the pyboard by hitting Ctrl-D.

Note than when using minicom or picocom, you have to hit Ctrl-A twice to send a Ctrl-A to the REPL because Ctrl-A is by default the escape key of minicom and picocom. You can use the paste file command in minicom to send a file to the REPL. For this:

  1. hit Ctrl-A twice to enter the raw REPL
  2. hit Ctrl-A Y to execute the paste file minicom command
  3. navigate to your file in the file selection screen that appears and select it
  4. hit Ctrl-D to finish the transfer

Your file is then loaded. If it contains just definitions, hit Ctrl-B to exit the rawREPL and use the definitions in the regular REPL.

Another way of executing code on the pyboard is to use the script available in the tools directory of the MicroPython git repository. Exit pyterm, then load your file onto the pyboard with You can connect to the REPL again by launching pyterm.

You can modify the script to find the pyboard device automatically by changing the beginning of the main function as follows:

def main():
    import argparse
    import os
    import fnmatch
    # look for pyboard v1 under Linux
    ttymodems = fnmatch.filter(os.listdir('/dev'), 'ttyACM*')
    if len(ttymodems) == 0:
        # look for pyboard v1 under MacOS
        ttymodems = fnmatch.filter(os.listdir('/dev'), 'tty.usbmodem*')
    if len(ttymodems) == 0:
        # look for wipy under MacOS
        ttymodems = fnmatch.filter(os.listdir('/dev'), 'tty.usbserial-*')
    if len(ttymodems) == 0:
      # look for ESP32 board under MacOS
      ttymodems = fnmatch.filter(os.listdir('/dev'), 'tty.SLAB_USBtoUART')
    if len(ttymodems) == 0:
        print('Error: no pyboard found.')
    cmd_parser = argparse.ArgumentParser(description='Run scripts on the pyboard.')
    cmd_parser.add_argument('--device', default='/dev/' + ttymodems[0], help='the serial device of the pyboard')

An already patched version (may not be up to date with respect to the git repository) is available.

Fritzing parts

I use Fritzing to draw my sketches. I have made a Fritzing part for the pyboard. You can also get the SVG pictures I made for the PCB, the schematics and the icon.

You can see how it looks on the example below:

I have also made a Fritzing part for an 8x8 red LED matrix with a MAX7219: RedLEDmatrixWithMAX.fzpz. The SVG pictures for the breadboard, PCD and icon, and for the schematic are also available: RedLEDmatrixWithMAX7219.svg and RedLEDmatrixWithMAX7219_schematic.svg.

Code samples

Here are some code samples that work on the pyboard. I give them here, hoping they can be useful to someone, but there is no guaranty about their correctness or accuracy. I welcome any constructive remark about this code since I am not an expert in Python nor in coding for microcontrollers.

Using the MMA7660 accelerometer of the pyboard Accelerometer

The pyboard has a builtin 3 axis accelerometer. You can find how it is interfaced with the pyboard on the schematic view. When powered on, it appears on I2C bus 1, and the documentation of the chip is also available. Although the MicroPython library contains an Accel class to access the data provided by the accelerometer, I have written an MMA7660 class which gives more in-depth control of the chip and allows you to configure it for generating interrupts.

As an example, here is how you can code a spirit level using this module and an 8x8 LED matrix (see below for the module used to drive the matrix):

# © Frédéric Boulanger <>
# 2015-06-16
# This code uses the built-in MMA7660 accelerometer of the pyboard and
# an attached 8x8 LED matrix driven by a MAX7219 to build a spirit level.
# Everything is handled using interrupts.
# This software is licensed under the Eclipse Public License 2.0

# Import the MMA7660 driver
from MMA7660 import *
# Import the LED matrix driver
from LEDMatrixWithMAX import *

mma = MMA7660()             # our accelerometer
led = LEDMatrixWithMAX(2)   # the LED matrix, connected on SPI bus 2

accel = bytearray(3)        # buffer for reading data samples from the accelerometer

# Buffer for the window avarage
wsize = 16          # width of the window
xbuf = [0] * wsize  # buffer for x acceleration
ybuf = [0] * wsize  # buffer for y acceleration
idx = 0             # current index for writing into the buffer

Compute the integer mean of a buffer
def mean(buf):
	m = 0
	for i in range(len(buf)):  # cannot use "for x in buf" in an interrupt handler
		m += buf[i]
	return m // len(buf)

Read the accelerometer data, update the average window,
and move the bubble on the LED matrix accordingly.
def moveBubble(line):
	global idx

	mma.getSample(accel)      # get a sample of data from the accelerometer
	x = accel[0]              # acceleration along the x axis
	if x > 31:                # interpret as 2's complement
		x -= 64
	xbuf[idx] = x             # update window average buffer
	y = accel[1]              # same processing for y axis acceleration
	if y > 31:
		y -= 64
	ybuf[idx] = y
	idx = (idx + 1) % wsize   # advance buffer index
	x = mean(xbuf) + 1        # get the mean x axis average over the window
	y = mean(ybuf) + 1        # get the mean y axis average over the window
	x = (6 * x) // 32         # scale the value from -32/31 to -6/+5
	x += 3                    # center of the matrix is (3,3)
	if x < 0: x = 0           # avoid indexing out of range
	if x > 6: x = 6
	x = 6 - x                 # reverse x axis display
	y = (6 * y) // 32         # same processing for the y axis
	y += 3
	if y < 0: y = 0
	if y > 6 : y = 6
	led.clearBitmap()         # clear the LED matrix
	led.setPixel(x, y, True)  # switch on 4 LEDs (the bubble) on the matrix
	led.setPixel(x+1, y, True)
	led.setPixel(x, y+1, True)
	led.setPixel(x+1, y+1, True)
	led.updateDisplay()       # update the display

# Get 16 samples per second
# Install the moveBubble function as interrupt handler
# Enable interrupts when the accelerometer data is refreshed
# Switch the accelerometer to active mode
# Switch the LED matrix on

Using a MAX7219 to drive LED displays MAX7219

The MAX7219 provides a serial interface for driving LED displays with up to 8 7-segment digits, or 8x8 LED matrices. Here is a MAX7219 module to communicate with a MAX7219 and display digits or control individual segments.

Built on this module, a LEDMatrixWithMAX module is also available for controlling an 8x8 LED matrix, addressing individual pixels and even displaying pictures.

Using a HT16K33 to drive LED displays HT16K33

The 7-segment display with backpack by Adafruit uses an HT16K33 to control the display through an I2C interface. Here is a module to use it with the pyboard.

Using AdaFruit Motor shield V2 to drive motors Motor shield

The Arduino motor shield V2 by Adafruit can be used with the pyboard to control DC motors, stepper motors and servo motors. I have written two modules to interface with the PCA9685 chip on the shield and drive different kinds of motors. I use the four PWM pins that are not used by the M1, M2, m3 and M4 ports (PWM outputs 0, 1, 14 and 15) to drive servo motors. See the details and get the code on my github repository.

Firmware update

In order to update the firmware of the pyboard, you must place it into DFU (Device Firmware Update) mode. For this, disconnect everything from the pyboard, connect the DFU pin to the 3V3 pin (they are next to the X16 and X17 pins), and connect your pyboard to your computer with a USB cable.

The latest firmware for your pyboard can be found at

To upload it to the pyboard, you need a DFU utility. You can use dfu-util:

sudo dfu-util --alt 0 -D <firmware.dfu>

or the pydfu Python script:

python -u <firmware.dfu>

where <firmware.dfu> is the exact name of the firmware you want to put on the pyboard. relies on libusb and the PyUSB module.

Detailed instructions can be found on