This is a cheatsheet for getting up and going with the Adafruit Circuit Playground Express using CircuitPython.
When you plug the device into a USB port it should mount a drive called CircuitPy
. In the root of this drive should reside a file called boot_out.txt
. This will contain version information for the device, e.g.
Adafruit CircuitPython 4.1.2 on 2019-12-18; Adafruit CircuitPlayground Express with samd21g18
Our devices should all be preconfigured with CircuitPython 4.1.
You can either use the Adafruit recommended editor, Mu Editor, or directly connect to the serial console via the terminal.
This allows you to see output from print
commands, and to restart the device (Control-D) or use the REPL (any other key).
- Download Mu Editor or install via brew:
brew cask install mu-editor
- Connect the device via USB. A drive called
CircuitPy
should mount. - Open Mu Editor.
- Click the
Serial
button. - A panel should appear at the bottom with the console output, which should look something like:
Auto-reload is on. Simply save files over USB to run them or enter REPL to disable.
- Connect the device via USB. A drive called
CircuitPy
should mount. - Find the device name. On the Mac,
ls /dev/tty.usbmodem*
should list possible devices; on Linux,ls /dev/ttyACM*
should do the same. - Connect to the device, e.g. using
screen
:screen /dev/tty.usbmodem14301 115200
, changing device name as appropriate. - A panel should appear at the bottom with the console output, which should look something like:
Auto-reload is on. Simply save files over USB to run them or enter REPL to disable.
- You can use
Control-A, D
to exitscreen
.
You can edit the code using any text editor. When you connect the device via USB a drive called CircuitPy
will mount. Edit main.py
in the root of this drive.
You can make other files available, e.g. audio files, by simply copying them to the CircuitPy
drive.
Should you need any libraries which are not pre-installed then you can just add them to the lib
directory in the root of the CircuitPy
drive.
The pre-packaged libraries are available on the CircuitPython website.
It's an embedded runtime based on Python 3. You can do a lot (but not all) of Python 3.
Stick an infinite loop in there, e.g.
while True:
print('Ka pai')
Have you considered sleeping for a bit?
import time
while True:
print('Langsamer, bitte')
time.sleep(0.1)
Did you put a loop in? The board will enter a clear state once the end of the script is reached. If you want to hold the last state then just enter a blank loop, e.g.
while True:
time.sleep(0.1)
This is the LED opposite the power LED, next to the USB socket. Here we turn it on.
import board
import digitalio
import time
led = digitalio.DigitalInOut(board.D13)
led.switch_to_output()
led.value = True
while True:
time.sleep(0.1)
You can use the NeoPixel library to control the ten coloured LEDs.
import neopixel
import board
import time
pixels = neopixel.NeoPixel(board.NEOPIXEL, 10, brightness=0.2, auto_write=False)
pixels.fill((0, 0, 0))
pixels.show()
offset = 0
while True:
colours = [(255, 0, 0), (0, 255, 0), (0, 0, 255)]
for i in range(0, 10, 1):
pixels[i] = colours[(offset + i) % len(colours)]
pixels.show()
offset = (offset + 1) % 10
time.sleep(0.5)
You can use the digitalio
library to access the two push-buttons.
import board
import digitalio
import time
import neopixel
pixels = neopixel.NeoPixel(board.NEOPIXEL, 10, brightness=0.2, auto_write=False)
pixels.fill((0, 0, 0))
pixels.show()
button_a = digitalio.DigitalInOut(board.BUTTON_A)
button_a.switch_to_input(pull=digitalio.Pull.DOWN)
button_b = digitalio.DigitalInOut(board.BUTTON_B)
button_b.switch_to_input(pull=digitalio.Pull.DOWN)
while True:
pixels.fill((0, 0, 0))
if button_a.value:
pixels[0] = (0, 255, 0)
if button_b.value:
pixels[9] = (0, 255, 0)
pixels.show()
time.sleep(0.01)
Like the push-buttons, this is exposed via digitalio
. The following will light up one side of the board, depending on which side the switch is on.
import board
import digitalio
import time
import neopixel
pixels = neopixel.NeoPixel(board.NEOPIXEL, 10, brightness=0.2, auto_write=False)
pixels.fill((0, 0, 0))
pixels.show()
switch = digitalio.DigitalInOut(board.SLIDE_SWITCH)
switch.direction = digitalio.Direction.INPUT
switch.pull = digitalio.Pull.UP
while True:
pixels.fill((0, 0, 0))
if switch.value:
led_range = range (0, 5, 1)
else:
led_range = range (5, 10, 1)
for i in led_range:
pixels[i] = (0, 255, 0)
pixels.show()
time.sleep(0.01)
The holes around the edge of the board can be used to wire up external devices to the board, but most of them also work as capacitive touch-sensors. If you can't get your fingers in there then try something metal, like scissors.
import time
import board
import touchio
touch_A1 = touchio.TouchIn(board.A1)
touch_A2 = touchio.TouchIn(board.A2)
touch_A3 = touchio.TouchIn(board.A3)
touch_A4 = touchio.TouchIn(board.A4)
touch_A5 = touchio.TouchIn(board.A5)
touch_A6 = touchio.TouchIn(board.A6)
touch_TX = touchio.TouchIn(board.TX)
while True:
if touch_A1.value:
print("A1 touched!")
if touch_A2.value:
print("A2 touched!")
if touch_A3.value:
print("A3 touched!")
if touch_A4.value:
print("A4 touched!")
if touch_A5.value:
print("A5 touched!")
if touch_A6.value:
print("A6 touched!")
if touch_TX.value:
print("TX touched!")
time.sleep(0.01)
The light sensor returns a 16-bit unsigned value (0 to 65535) representing the amount of light - higher is more. The sensor is marked with an eye
symbol on the board.
import time
import board
import analogio
light = analogio.AnalogIn(board.LIGHT)
while True:
print(light.value)
time.sleep(1.0)
We can use simpleio.map_range
to map a sensor range to the number of LEDs, done here with the light sensor.
import time
import board
import neopixel
import analogio
import simpleio
pixels = neopixel.NeoPixel(board.NEOPIXEL, 10, brightness=.05, auto_write=False)
pixels.fill((0, 0, 0))
pixels.show()
light = analogio.AnalogIn(board.LIGHT)
while True:
# transform light value to LED range - higher values are lighter
peak = simpleio.map_range(light.value, 2000, 62000, 0, 9)
for i in range(0, 9, 1):
if i <= peak:
pixels[i] = (0, 255, 0)
else:
pixels[i] = (0, 0, 0)
pixels.show()
time.sleep(0.1)
The temperature sensor returns a value in degrees centigrade.
import time
import adafruit_thermistor
import board
thermistor = adafruit_thermistor.Thermistor(board.TEMPERATURE, 10000, 10000, 25, 3950)
while True:
temp_c = thermistor.temperature
print("Temperature is: %fC" % temp_c)
time.sleep(1.0)
You can generate a waveform and play it.
import time
import array
import math
import board
import digitalio
from audioio import WaveFile, AudioOut, RawSample
FREQUENCY = 440 # 440 Hz middle 'A'
SAMPLERATE = 8000 # 8000 samples/second, recommended!
# Generate one period of sine wave
length = SAMPLERATE // FREQUENCY
sine_wave = array.array("H", [0] * length)
for i in range(length):
sine_wave[i] = int(math.sin(math.pi * 2 * i / 18) * (2 ** 15) + 2 ** 15)
speaker_enable = digitalio.DigitalInOut(board.SPEAKER_ENABLE)
speaker_enable.direction = digitalio.Direction.OUTPUT
speaker_enable.value = True
audio = AudioOut(board.SPEAKER)
sine_wave_sample = RawSample(sine_wave)
# Play sine wave for one second
audio.play(sine_wave_sample, loop=True)
time.sleep(1)
audio.stop()
You can play simple WAV files with the built-in libraries.
import time
import board
import digitalio
from audioio import WaveFile, AudioOut
speaker_enable = digitalio.DigitalInOut(board.SPEAKER_ENABLE)
speaker_enable.direction = digitalio.Direction.OUTPUT
speaker_enable.value = True
audio = AudioOut(board.SPEAKER)
while True:
file = open('sample.wav', 'rb')
wave_file = WaveFile(file)
audio.play(wave_file)
time.sleep(0.5)
You can sample data from the microphone, however we aware you don't have a lot of memory to play with!
import time
import array
import audiobusio
import board
import neopixel
NUM_SAMPLES = 160
mic = audiobusio.PDMIn(board.MICROPHONE_CLOCK, board.MICROPHONE_DATA,
sample_rate=16000, bit_depth=16)
samples = array.array('H', [0] * NUM_SAMPLES)
while True:
mic.record(samples, len(samples))
print(samples)
time.sleep(1.0)
The built-in libraries make this pretty trivial.
Warning: the adafruit_circuitplayground.express
library wraps a lot of the functionality of the board, and may conflict with code that doesn't use it. In particular, it takes control of the speaker, so code that tries to use board.SPEAKER
may not work. You can instead access the audio functions via the library.
To detect tapping:
from adafruit_circuitplayground.express import cpx
cpx.detect_taps = 2
while True:
if cpx.tapped:
print("Tapped!")
Or, for shaking:
import time
from adafruit_circuitplayground.express import cpx
while True:
if cpx.shake(shake_threshold=20):
print("Shake detected!")
cpx.pixels.fill((150, 0, 0))
time.sleep(5.0)
else:
cpx.pixels.fill((0, 0, 0))
Or for raw acceleration data:
import time
from adafruit_circuitplayground.express import cpx
while True:
x, y, z = cpx.acceleration
print((x, y, z))
time.sleep(0.5)
import time
import board
import usb_hid
from adafruit_hid.keyboard import Keyboard
from adafruit_hid.keyboard_layout_us import KeyboardLayoutUS
from adafruit_hid.keycode import Keycode
from digitalio import DigitalInOut, Direction, Pull
buttonpins = [board.BUTTON_A, board.BUTTON_B]
buttons = []
buttonkeys = [(Keycode.A, Keycode.SHIFT), ("Hello World!\n", None)]
kbd = Keyboard()
layout = KeyboardLayoutUS(kbd)
for pin in buttonpins:
button = DigitalInOut(pin)
button.direction = Direction.INPUT
button.pull = Pull.DOWN
buttons.append(button)
led = DigitalInOut(board.D13)
led.direction = Direction.OUTPUT
while True:
for button in buttons:
if button.value:
i = buttons.index(button)
print("Button #%d Pressed" % i)
led.value = True
while button.value:
pass
(key_code, control_key) = buttonkeys[i]
if isinstance(key_code, str):
layout.write(key_code)
else:
if control_key is not None:
kbd.press(key_code, control_key)
else:
kbd.press(key_code)
kbd.release_all()
led.value = False
time.sleep(0.1)
You'll need two devices for this one. The sensors are on the top of the device.
The receiver listens for volume up/down events.
import pulseio
import board
import adafruit_irremote
pulsein = pulseio.PulseIn(board.IR_RX, maxlen=120, idle_state=True)
decoder = adafruit_irremote.GenericDecode()
while True:
pulses = decoder.read_pulses(pulsein)
try:
received_code = decoder.decode_bits(pulses)
except adafruit_irremote.IRNECRepeatException:
continue
except adafruit_irremote.IRDecodeException as e:
continue
print("NEC Infrared code received: ", received_code)
if received_code == [255, 2, 255, 0]:
print("Received NEC Vol-")
elif received_code == [255, 2, 191, 64]:
print("Received NEC Vol+")
And the transmitter sends them when one of the push buttons is pressed.
import time
from adafruit_circuitplayground.express import cpx
import adafruit_irremote
import pulseio
import board
pwm = pulseio.PWMOut(board.IR_TX, frequency=38000, duty_cycle=2 ** 15)
pulseout = pulseio.PulseOut(pwm)
encoder = adafruit_irremote.GenericTransmit(header=[9500, 4500], one=[550, 550], zero=[550, 1700], trail=0)
while True:
if cpx.button_a:
print("Sending NEC Vol-\n")
cpx.red_led = True
encoder.transmit(pulseout, [255, 2, 255, 0])
cpx.red_led = False
# wait so the receiver can get the full message
time.sleep(0.2)
if cpx.button_b:
print("Sending NEC Vol+\n")
cpx.red_led = True
encoder.transmit(pulseout, [255, 2, 191, 64])
cpx.red_led = False
time.sleep(0.2)