Code the Rover with CircuitPython

Feather and Crickit Prep

We'll be using CircuitPython for this project. Are you new to using CircuitPython? No worries, there is a full getting started guide here.

For more info on the Feather nRF52840 Express, check out this guide.

Adafruit suggests using the Mu editor to edit your code and have an interactive REPL in CircuitPython. You can learn about Mu and its installation in this tutorial.

Follow this guide for instructions on installing the latest release version of CircuitPython for the Feather nRF52840 Express:

Libraries

You'll also need to add the following libraries for this project. Follow this guide on adding libraries.

Plug your nRF Feather board into your computer via a USB cable. Please be sure the cable is a good power+data cable so the computer can talk to the board.

A new disk should appear in your computer's file explorer/finder called CIRCUITPY. This is the place we'll copy the code and code library. If you can only get a drive named FTHR840BOOT, load CircuitPython per the guide above.

Make a folder on the CIRCUITPY drive named lib.

Now, download the latest CircuitPython libraries to your computer using the green button below. Match the library you get to the version of CircuitPython you are using. Save to your computer's hard drive where you can find it.

Note: This project uses the Bluetooth functionality available in CircuitPython 4.0.0-beta.1 and later, on nRF52840 boards such as the Feather nRF52840 Express. Make sure you are using that version or later.

With your file explorer/finder, browse to the bundle and open it up. Copy the following folders and files from the library bundle to your CIRCUITPY lib directory you made earlier:

The ones you'll need are:

  • adafruit_ble (folder)
  • adafruit_bluefruit_connect (folder)
  • adafruit_bus_device (folder)
  • adafruit_crickit.mpy (file)
  • adafruit_motor (folder)
  • adafruit_seesaw (folder)

All of the other necessary code is baked into CircuitPython!

CircuitPython Code

Copy the program below and paste it into a new document in Mu. Then, save it from Mu onto your CIRCUITPY flash drive as code.py. As soon as CircuitPython restarts, the NeoPixel ring will light up purple-ish and await your Bluetooth connection!

# CircuitPython BLE Rover
# Use with the Adafruit BlueFruit LE Connect app
# Works with CircuitPython 4.0.0-beta.1 and later
# running on an nRF52840 Feather board and Crickit FeatherWing

import time

import board
import digitalio

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 LEDs on the Feather
blue_led = digitalio.DigitalInOut(board.BLUE_LED)
red_led = digitalio.DigitalInOut(board.RED_LED)
blue_led.direction = digitalio.Direction.OUTPUT
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 = -1.0
REV = 0.7

crickit.init_neopixel(24, brightness = 0.2)  # create Crickit neopixel object
RED = (200, 0, 0)
GREEN = (0, 200, 0)
BLUE = (0, 0, 200)
PURPLE = (120, 0, 160)
YELLOW = (100, 100, 0)
AQUA = (0, 100, 100)
color = PURPLE  # current NeoPixel color
prior_color = PURPLE  # to store state of previous color when changing them
crickit.neopixel.fill(color)

print("BLE Rover")
print("Use Adafruit Bluefruit app to connect")
while True:
    blue_led.value = False
    ble.start_advertising(advertisement)
    while not ble.connected:
        # Wait for a connection.
        pass
    blue_led.value = True  # turn on blue LED when connected
    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
                crickit.neopixel.fill(color)

            # do this when buttons are pressed
            if isinstance(packet, ButtonPacket) and packet.pressed:
                red_led.value = True  # blink to show a packet has been received
                if packet.button == ButtonPacket.UP:  # UP button pressed
                    crickit.neopixel.fill(color)
                    motor_1.throttle = FWD
                    motor_2.throttle = FWD
                elif packet.button == ButtonPacket.DOWN:  # DOWN button
                    crickit.neopixel.fill(color)
                    motor_1.throttle = REV
                    motor_2.throttle = REV
                elif packet.button == ButtonPacket.RIGHT:
                    prior_color = color
                    color = YELLOW
                    crickit.neopixel.fill(color)
                    motor_1.throttle = FWD
                    motor_2.throttle = FWD * 0.5
                elif packet.button == ButtonPacket.LEFT:
                    prior_color = color
                    color = YELLOW
                    crickit.neopixel.fill(color)
                    motor_1.throttle = FWD * 0.5
                    motor_2.throttle = FWD
                elif packet.button == ButtonPacket.BUTTON_1:
                    crickit.neopixel.fill(RED)
                    motor_1.throttle = 0.0
                    motor_2.throttle = 0.0
                    time.sleep(0.5)
                    crickit.neopixel.fill(color)
                elif packet.button == ButtonPacket.BUTTON_2:
                    color = GREEN
                    crickit.neopixel.fill(color)
                elif packet.button == ButtonPacket.BUTTON_3:
                    color = BLUE
                    crickit.neopixel.fill(color)
                elif packet.button == ButtonPacket.BUTTON_4:
                    color = PURPLE
                    crickit.neopixel.fill(color)
            # do this when some buttons are released
            elif isinstance(packet, ButtonPacket) and not packet.pressed:
                if packet.button == ButtonPacket.RIGHT:
                    print("released right")
                    color = prior_color
                    crickit.neopixel.fill(color)
                    motor_1.throttle = FWD
                    motor_2.throttle = FWD
                if packet.button == ButtonPacket.LEFT:
                    print("released left")
                    color = prior_color
                    crickit.neopixel.fill(color)
                    motor_1.throttle = FWD
                    motor_2.throttle = FWD

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 -1.0 to 1.0 and flip the REV value from 0.7 to -0.7.

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 in from a client device (phone or tablet), you use the Adafruit Bluefruit LE Connect App. Install it from the Apple App Store or Google Play App Store.

Connect to Feather

With the Crickit plugged into DC power and turned on with its on-board power switch, the Feather 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 Light Switch, touch the Connect button. You should see Connecting and then Discovering Services.

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.

Troubleshooting

If you don't see CIRCU or CIRCUITPY right away, try pulling down to refresh. If that doesn't work, try turning Bluetooth off and back on on your phone or tablet and restarting the app.

Device Menu

After you connect, you'll see a menu of how you can interact with the device. Choose Controller.

Controller Menu

After you choose Controller, you'll see another screen with more choices. Control Pad is used for this project.

Change Color

To change the color of the NeoPixels, use the Color Picker screen. Choose a color and press Send Selected Color.

This guide was first published on Feb 16, 2019. It was last updated on Feb 16, 2019. This page (Code the Rover with CircuitPython) was last updated on Dec 10, 2019.