Plug your Circuit Playground Bluefruit board into your computer with a known good USB A to USB micro B cable (good = had both data & power wires in it).
Your computer should recognize the Circuit Playground Bluefruit as a new flash drive named CIRCUITPY. If you see a new drive named CPLAYBTBOOT then press reset. If still no CIRCUITPY drive, go back to the CircuitPython on the Circuit Playground Bluefruit page to install CircuitPython again.
Using your computer's operating system, make a directory named lib in the main (root) directory of the CIRCUITPY drive if it is not already present. This will hold some code libraries for the project.
Libraries
Now we'll install the libraries that we need to run the rover code on the Circuit Playground Express with Crickit.
Click this link to got to the circuitpython.org Libraries page. Download the latest version of the Bundle library .zip file that matches the version of CircuitPython you're using on the board.
Uncompress the .zip file and then copy the following directories and .mpy files to the lib directory of the CIRCUITPY drive:
- adafruit_ble
- adafruit_bluefruit_connect
- adafruit_bus_device
- adafruit_motor
- adafruit_seesaw
- adafruit_crickit.mpy
- neopixel.mpy
Your lib directory on the CIRCUITPY drive should be similar to the picture above.
The Mu Editor
Adafruit recommends using the free program Mu to edit your CircuitPython programs and save them on your Circuit Playground Bluefruit. You can use any text editor, but Mu has some handy features.
See this page on the Circuit Playground Bluefruit guide on the steps used to install Mu.
Turtle Rover Code
The event we've all been waiting for! The actual CircuitPython code for our rover!
Copy this code and then paste it into a new document in Mu, then save it to your CIRCUITPY drive as code.py
# SPDX-FileCopyrightText: 2019 John Park for Adafruit Industries # # SPDX-License-Identifier: MIT # Circuit Playground Bluefruit Rover # Use with the Adafruit BlueFruit LE Connect app # Works with CircuitPython 5.0.0-beta.0 and later # running on an nRF52840 CPB board and Crickit import time import board import digitalio import neopixel from adafruit_crickit import crickit from adafruit_ble import BLERadio from adafruit_ble.advertising.standard import ProvideServicesAdvertisement from adafruit_ble.services.nordic import UARTService from adafruit_bluefruit_connect.packet import Packet # Only the packet classes that are imported will be known to Packet. from adafruit_bluefruit_connect.button_packet import ButtonPacket from adafruit_bluefruit_connect.color_packet import ColorPacket # Prep the status LED on the CPB red_led = digitalio.DigitalInOut(board.D13) red_led.direction = digitalio.Direction.OUTPUT ble = BLERadio() uart_service = UARTService() advertisement = ProvideServicesAdvertisement(uart_service) # motor setup motor_1 = crickit.dc_motor_1 motor_2 = crickit.dc_motor_2 FWD = 0.25 REV = -0.25 neopixels = neopixel.NeoPixel(board.NEOPIXEL, 10, brightness=0.1) RED = (200, 0, 0) GREEN = (0, 200, 0) BLUE = (0, 0, 200) PURPLE = (120, 0, 160) YELLOW = (100, 100, 0) AQUA = (0, 100, 100) BLACK = (0, 0, 0) color = PURPLE # current NeoPixel color neopixels.fill(color) print("BLE Turtle Rover") print("Use Adafruit Bluefruit app to connect") while True: neopixels[0] = BLACK neopixels.show() ble.start_advertising(advertisement) while not ble.connected: # Wait for a connection. pass # set a pixel blue when connected neopixels[0] = BLUE neopixels.show() while ble.connected: if uart_service.in_waiting: # Packet is arriving. red_led.value = False # turn off red LED packet = Packet.from_stream(uart_service) if isinstance(packet, ColorPacket): # Change the color. color = packet.color neopixels.fill(color) # do this when buttons are pressed if isinstance(packet, ButtonPacket) and packet.pressed: red_led.value = True # blink to show packet has been received if packet.button == ButtonPacket.UP: neopixels.fill(color) motor_1.throttle = FWD motor_2.throttle = FWD elif packet.button == ButtonPacket.DOWN: neopixels.fill(color) motor_1.throttle = REV motor_2.throttle = REV elif packet.button == ButtonPacket.RIGHT: color = YELLOW neopixels.fill(color) motor_2.throttle = 0 motor_1.throttle = FWD elif packet.button == ButtonPacket.LEFT: color = YELLOW neopixels.fill(color) motor_2.throttle = FWD motor_1.throttle = 0 elif packet.button == ButtonPacket.BUTTON_1: neopixels.fill(RED) motor_1.throttle = 0.0 motor_2.throttle = 0.0 time.sleep(0.5) neopixels.fill(color) elif packet.button == ButtonPacket.BUTTON_2: color = GREEN neopixels.fill(color) elif packet.button == ButtonPacket.BUTTON_3: color = BLUE neopixels.fill(color) elif packet.button == ButtonPacket.BUTTON_4: color = PURPLE neopixels.fill(color) # do this when some buttons are released elif isinstance(packet, ButtonPacket) and not packet.pressed: if packet.button == ButtonPacket.UP: neopixels.fill(RED) motor_1.throttle = 0 motor_2.throttle = 0 if packet.button == ButtonPacket.DOWN: neopixels.fill(RED) motor_1.throttle = 0 motor_2.throttle = 0 if packet.button == ButtonPacket.RIGHT: neopixels.fill(RED) motor_1.throttle = 0 motor_2.throttle = 0 if packet.button == ButtonPacket.LEFT: neopixels.fill(RED) motor_1.throttle = 0 motor_2.throttle = 0
How the Code Works
The first part of the program sets up a UARTServer
, which is a BLE service that wirelessly sends streams of characters between a BLE device and a client computer (which could be a phone or tablet). The program then starts advertising its UART service to anyone listening. The client computer receives the advertisement, and tells the user it's available. The user can then choose to connect as a client of the service.
One a connection is established, the program code waits for incoming command packets from the client, either a ColorPacket
or a ButtonPacket
. When one arrives, the program waits to see which button has been pressed or which color value is sent and then either runs the motors or changes the colors on the NeoPixels.
Motor Movement
We set up a couple of variables -- FWD
and REV
-- that tell the program which base values to send for forward and reverse. Depending on how you wired the motors, and which way you want to consider "forward" on the cart, you can adjust these values anywhere from -1.0
to 1.0
.
When you test the rover, if it goes backwards when you press the up button, you can switch the FWD
variable from 0.25
to -0.25
and flip the REV
value from -0.25
to 0.25
. The alternative is to swap which wire -- red or black -- is going into each port of the Motor 1 and Motor 2 terminals on the Crickit.
If you find the rover spinning in circle when you press up or down on the remote app, then one of the motors is wired "backwards". Simply swap the two wires of that motor into each others' ports on the Crickit!

Control the Rover with the Bluefruit LE Connect App
Download the App
To control the motors and NeoPixels from a client device (phone or tablet), you use the free Adafruit Bluefruit LE Connect App. Install it from the Apple App Store or Google Play App Store.
Connect to CPB
With the Crickit plugged into DC power and turned on with its on-board power switch, the CPB will also get power and automatically start running your code. Then start up the Bluefruit LE Connect app, and make sure it's in Central Mode (left button on the bottom). When you start the app, you should see a device named CIRCU or CIRCUITPY. If there's a long list of devices, you can shorten it by turning on the "Must have UART Service" switch.
To connect to the bot, touch the Connect button.
Drive and Light
Once connected, you can use the Controllers -> Control Pad to drive with the arrow buttons, stop with the 1 button, and change to some preset colors on 2, 3, & 4
The incoming packet for any arrow button press event will tell the motors to throttle to the preset speed variable, such as motor_1.throttle = FWD
. When you release the button, the button packet will tell the motor to stop. So, you can press and hold to go, and then release to stop, it's easy and fun!
Here's our turtle up on blocks to demonstrate the remote control:
And here it is with wheels (flippers!) down:
Page last edited January 21, 2025
Text editor powered by tinymce.