An open-hardware Python microcontroller for LEGO®

Feb 25, 2020

Project update 7 of 11

Now Running CircuitPython!

by Keith P

CircuitPython for Snekboard completely fills the SoC on-chip flash, so we have to use an external flash part for Python source code and libraries. The latest Snekboard prototype uses a different flash part to save some space, but I couldn’t get the part from Mouser as they were out of stock. I received them yesterday and got one soldered onto a board this evening, so the current prototypes can now run CircuitPython!

Feel the Power

It’s clear that Adafruit has invested a ton of time and energy into CircuitPython — there are piles of supported boards, lots of example code, and a whole bunch of libraries that take full advantage of these little computers.

If you’ve read any of the articles about Snek on Snekboard, you’ll notice how simple it is to use. The same isn’t as true for CircuitPython. As with Arduino, you need to tell CircuitPython how you want to use each pin for before you can do anything.

Mu-editor has built-in support for CircuitPython boards, so that’s what I’m using to experiment with Snekboard. You can open up a serial window and type directly into the REPL.

Turn on a Motor

The first thing I wanted to do was make sure I could drive the motors. The native CircuitPython motor controller library expects to drive a controller that takes two PWM inputs; the motor controllers on Snekboard take one PWM (for power) and one digital pin (for direction). So, we get to roll our own here. As a trivial first test, let’s just turn a motor on full speed by treating the PWM output as a digital pin. I’ve got a motor connected to M2, which uses pins POWER2 and DIR2 in CircuitPython

import board
import digitalio

power = digitalio.DigitalInOut(board.POWER2)
power.direction = digitalio.Direction.OUTPUT
power.value = True

Now let’s try varying the speed. First we have to stop treating the power pin as digital:


Now we can use the pulseio library to control the power output:

power = pulseio.PWMOut(board.POWER2)
power.duty_cyle = 32767

Finally, we can play with the direction pin. That’s a digital output:

direct = digitalio.DigitalInOut(board.DIR2)
direct.direction = digitalio.Direction.OUTPUT
direct.value = True

Snekboard is Already Supported Upstream

One of the best things about CircuitPython is how willing Adafruit has been to take my patches and integrate support for Snekboard into their regular release cycle. I’m running 5.0.0-beta.5 on my boards, downloaded from the Snekboard page at

Sample Code

Here’s the code that was used to make the video above.

# Copyright © 2020 Keith Packard <>
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# General Public License for more details.

import board
import neopixel_write
import time
import digitalio
import pulseio
import analogio
import random

def set_power(motor, power):
    """Set the motor direction and power. power ranges from -1 to 1"""
    if power >= 0:
    motor[1].value = True
    motor[1].value = False
    power = -power
    motor[0].duty_cycle = min(max(power * 65535, 0), 65535)

def make_motor(pwm_pin, dir_pin):
    """Create a motor. pwm_pin is the board element which is the PWM pin.
    dir_pin is the board element which is the direction pin"""
    pwm = pulseio.PWMOut(pwm_pin)
    dir = digitalio.DigitalInOut(dir_pin)
    dir.direction = digitalio.Direction.OUTPUT
    return (pwm, dir)

def make_sensor(sensor_pin):
    """Set up an analog sensor input"""
    return analogio.AnalogIn(sensor_pin)

def read(sensor):
    return sensor.value / 65535

left_motor = make_motor(board.POWER3, board.DIR3)
right_motor = make_motor(board.POWER2, board.DIR2)
front = make_sensor(board.A1)
back = make_sensor(board.A7)

neo = digitalio.DigitalInOut(board.NEOPIXEL)
neo.direction = digitalio.Direction.OUTPUT

def set_colors(r, g, b):
    """Set both neopixels to the specified color.
    r, g, b range from 0 to 1"""
    global neo
    r = int(r * 255)
    g = int(g * 255)
    b = int(b * 255)
    pixels = bytearray([g, r, b, g, r, b])
    neopixel_write.neopixel_write(neo, pixels)

def spin_random(d):
    set_power(left_motor, d)
    set_power(right_motor, d)
    set_colors(0, 0, 0.25)
    time.sleep(1 + random.randrange(200) / 200)

def bumper():
    while True:
    while read(front) < 0.5:
    set_power(left_motor, -1)
    set_power(right_motor, 1)
    set_power(left_motor, -1)
    set_power(right_motor, 1)
    set_colors(0.25, 0, 0)
    while read(back) < 0.5:
    set_power(left_motor, 1)
    set_power(right_motor, -1)
    set_power(left_motor, 1)
    set_power(right_motor, -1)
    set_colors(0, 0.25, 0)


Sign up to receive future updates for Snekboard.

Subscribe to the Crowd Supply newsletter, highlighting the latest creators and projects