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!
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.
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
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 CircuitPython.org
Here’s the code that was used to make the video above.
# # Copyright © 2020 Keith Packard <firstname.lastname@example.org> # # 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 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # 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.value = True else: motor.value = False power = -power motor.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: pass set_power(left_motor, -1) set_power(right_motor, 1) time.sleep(1) spin_random(1) set_power(left_motor, -1) set_power(right_motor, 1) set_colors(0.25, 0, 0) while read(back) < 0.5: pass set_power(left_motor, 1) set_power(right_motor, -1) time.sleep(1) spin_random(-1) set_power(left_motor, 1) set_power(right_motor, -1) set_colors(0, 0.25, 0) bumper()