The client code requires the following libraries from the CircuitPython Bundle:

  • adafruit_ble
  • adafruit_bluefruit_connect
  • adafruit_debouncer
  • adafruit_fancyled
  • neopixel
The code for this project needs extensive revision due to changes in CircuitPython. Sorry!

The latest version can be obtained from the Adafruit CircuitPython BLE repo

Plug in one Feather nRF52840 to your computer via a known good USB data + power cable. The board should show up as a disk drive named CIRCUITPY. Use this guide to update CircuitPython - you will want the latest version 5.0 or above.

Copy the 5 libraries to the lib folder on the Feather.

Here's the client code for the BLE peripheral. Click the download link and save to your computer. Then copy the client.py file to code.py on the Feather.

# SPDX-FileCopyrightText: 2019 Anne Barela for Adafruit Industries
#
# SPDX-License-Identifier: MIT

from binascii import unhexlify
from time import sleep

from micropython import const
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
from adafruit_bluefruit_connect.button_packet import ButtonPacket
from adafruit_bluefruit_connect.color_packet import ColorPacket
from neopixel import NeoPixel
from board import NEOPIXEL, SWITCH
from adafruit_debouncer import Debouncer
from digitalio import DigitalInOut, Direction, Pull
import adafruit_fancyled.adafruit_fancyled as fancy

pin = DigitalInOut(SWITCH)  # Set up built-in pushbutton switch
pin.direction = Direction.INPUT
pin.pull = Pull.UP
switch = Debouncer(pin)

pixels = NeoPixel(NEOPIXEL, 1)  # Set up built-in NeoPixel

AQUA = const(0x00FFFF)  # (0, 255, 255)
GREEN = const(0x00FF00)  # (0, 255, 0)
ORANGE = const(0xFF8000)  # (255, 128, 0)
RED = const(0xFF0000)  # (255, 0, 0)
BLUE = const(0x0000FF)  # (0, 0, 255)

gradients = {"Off": [(0.0, RED), (0.75, ORANGE)], "On": [(0.0, GREEN), (1.0, AQUA)]}
palette = fancy.expand_gradient(gradients["Off"], 30)

gamma_levels = (0.25, 0.3, 0.15)
color_index = 1
fade_direction = 1

TARGET = "f0:74:72:60:87:d2"  # CHANGE TO YOUR BLE ADDRESS
target_address = TARGET.split(":")  # Convert address string to list of bytes
target_address.reverse()  # Reverse bytes to match Address class little-endian
target_address = unhexlify("".join(target_address))  # Convert list to bytes

button_packet = ButtonPacket("1", True)  # Transmits pressed button 1

ble = BLERadio()
uart_client = None

while True:
    uart_addresses = []
    pixels[0] = BLUE  # Blue LED indicates disconnected status
    pixels.show()

    if not uart_client:
        print("Trying to connect to BLE server...")
        # Keep trying to find target UART peripheral
        for adv in ble.start_scan(ProvideServicesAdvertisement):
            print(adv.address.address_bytes)  # Print detected addresses
            if adv.address.address_bytes == target_address:
                uart_client = ble.connect(adv)
                print("Connected")
                break
        ble.stop_scan()

    if uart_client and uart_client.connected:
        uart_service = uart_client[UARTService]
        while uart_client and uart_client.connected:  # Connected
            switch.update()
            if switch.fell:  # Check for button press
                try:
                    # Transmit press
                    uart_service.write(button_packet.to_bytes())
                except OSError:
                    pass
            # Check for LED status receipt
            if uart_service.in_waiting:
                packet = Packet.from_stream(uart_service)
                if isinstance(packet, ColorPacket):
                    # Color match
                    if fancy.CRGB(*packet.color).pack() == GREEN:
                        # Green indicates on state
                        palette = fancy.expand_gradient(gradients["On"], 30)
                    else:
                        # Otherwise red indicates off
                        palette = fancy.expand_gradient(gradients["Off"], 30)

            # NeoPixel color fading routing
            color = fancy.palette_lookup(palette, color_index / 29)
            color = fancy.gamma_adjust(color, brightness=gamma_levels)
            c = color.pack()
            pixels[0] = c
            pixels.show()
            if color_index in (0, 28):
                fade_direction *= -1  # Change direction
            color_index += fade_direction

            sleep(0.02)

The program scans for BLE peripherals and connects once the target is located.

The user switch is debounced. Generally speaking, when a mechanical switch is pressed, it doesn’t just change from open to closed. The metal contacts oscillate for a few milliseconds. This can trick a microcontroller into reading multiple presses. The debouncer suppresses these erroneous readings. When the switch is pressed a button packet is sent to the server to activate the solenoid.

Next, the code monitors the UART buffer for color packets and uses FancyLED to generate smooth LED color fading for the corresponding color palette. The remote control fades green/aqua if the server is on. Otherwise, it fades red/orange.

This guide was first published on Oct 16, 2019. It was last updated on Jul 24, 2024.

This page (Client Code) was last updated on Jul 23, 2024.

Text editor powered by tinymce.