Once you've finished setting up your Feather nRF52840 Sense with CircuitPython, you can access the code and necessary libraries by downloading the Project Bundle.

To do this, click on the Download Project Bundle button in the window below. It will download as a zipped folder.

# SPDX-FileCopyrightText: 2021 Liz Clark for Adafruit Industries
#
# SPDX-License-Identifier: MIT

import time
import board
import digitalio
import simpleio
import adafruit_lsm6ds.lsm6ds33
import adafruit_apds9960.apds9960
from adafruit_hid.mouse import Mouse

import adafruit_ble
from adafruit_ble.advertising import Advertisement
from adafruit_ble.advertising.standard import ProvideServicesAdvertisement
from adafruit_ble.services.standard.hid import HIDService
from adafruit_ble.services.standard.device_info import DeviceInfoService

#  setup I2C
i2c = board.I2C()  # uses board.SCL and board.SDA
# i2c = board.STEMMA_I2C()  # For using the built-in STEMMA QT connector on a microcontroller

#  setup accelerometer
lsm6ds33 = adafruit_lsm6ds.lsm6ds33.LSM6DS33(i2c)
#  setup proximity sensor
apds9960 = adafruit_apds9960.apds9960.APDS9960(i2c)

#  enable proximity sensor
apds9960.enable_proximity = True

#  setup for onboard button
click = digitalio.DigitalInOut(board.SWITCH)
click.direction = digitalio.Direction.INPUT
click.pull = digitalio.Pull.UP

#  rounding algorhythm used for mouse movement
#  as used in the HID mouse CircuitPython example
mouse_min = -9
mouse_max = 9
step = (mouse_max - mouse_min) / 20.0

def steps(axis):
    return round((axis - mouse_min) / step)

#  time.monotonic() variable
clock = 0

#  variable for distance for proximity scrolling
distance = 245

#  setup for HID and BLE
hid = HIDService()

device_info = DeviceInfoService(software_revision=adafruit_ble.__version__,
                                manufacturer="Adafruit Industries")
advertisement = ProvideServicesAdvertisement(hid)
advertisement.appearance = 961
scan_response = Advertisement()
scan_response.complete_name = "CircuitPython HID"

ble = adafruit_ble.BLERadio()

if not ble.connected:
    print("advertising")
    ble.start_advertising(advertisement, scan_response)
else:
    print("already connected")
    print(ble.connections)

#  setup for mouse
mouse = Mouse(hid.devices)

while True:
    while not ble.connected:
        pass
    while ble.connected:
        #  sets x and y values for accelerometer x and y values
        #  x and y are swapped for orientation of feather
        y, x, z = lsm6ds33.acceleration

        #  map range of horizontal movement to mouse x movement
        horizontal_mov = simpleio.map_range(steps(x), 1.0, 20.0, -15.0, 15.0)
        #  map range of vertical movement to mouse y movement
        vertical_mov = simpleio.map_range(steps(y), 20.0, 1.0, -15.0, 15.0)
        #  map range of mouse y movement to scrolling
        scroll_dir = simpleio.map_range(vertical_mov, -15.0, 15.0, 3.0, -3.0)

        #  if onboard button is pressed, sends left mouse click
        if not click.value:
            mouse.click(Mouse.LEFT_BUTTON)
            time.sleep(0.2)
        #  if the proximity sensor is covered
        #  scroll the mouse
        if apds9960.proximity > distance:
            mouse.move(wheel=int(scroll_dir))
        #  otherwise move mouse cursor in x and y directions
        else:
            mouse.move(x=int(horizontal_mov))
            mouse.move(y=int(vertical_mov))

        #  debugging print for x and y values
        #  time.monotonic() is used so that the
        #  code is not delayed with time.sleep
        if (clock + 2) < time.monotonic():
            print("x", steps(x))
            print("y", steps(y))
            clock = time.monotonic()

    ble.start_advertising(advertisement)

Upload the Code and Libraries to the Feather nRF52840 Sense

After downloading the Project Bundle, plug your Feather nRF52840 Sense into the computer's USB port. You should see a new flash drive appear in the computer's File Explorer or Finder (depending on your operating system) called CIRCUITPY. Unzip the folder and copy the following items to the Feather nRF52840 Sense's CIRCUITPY drive. 

  • lib folder
  • code.py

Your Feather nRF52840 Sense CIRCUITPY drive should look like this after copying the lib folder and the code.py file.

How the CircuitPython Code Works

The Feather nRF52840 Sense is able to use the onboard accelerometer's x and y position data to move your computer's mouse around the screen. The lsm6ds33 is setup at the beginning of the code.

#  setup accelerometer
lsm6ds33 = adafruit_lsm6ds.lsm6ds33.LSM6DS33(i2c)

In the loop, x and y are setup to hold the value of the lsm6ds33's coordinates.

The map_range() function from the simpleio library is used to convert the accelerometer data to usable mouse movement data for both vertical and horizontal movement. The degree of motion from the accelerometer also affects the speed of the mouse's cursor movements.

Additionally, map_range() is used for scrolling, converting the mapped vertical movement of the mouse to scrolling direction and speed.

while ble.connected:
        #  sets x and y values for accelerometer x and y values
        #  x and y are swapped for orientation of feather
        y, x, z = lsm6ds33.acceleration

        #  map range of horizontal movement to mouse x movement
        horizontal_mov = simpleio.map_range(steps(x), 1.0, 20.0, -15.0, 15.0)
        #  map range of vertical movement to mouse y movement
        vertical_mov = simpleio.map_range(steps(y), 20.0, 1.0, -15.0, 15.0)
        #  map range of mouse y movement to scrolling
        scroll_dir = simpleio.map_range(vertical_mov, -15.0, 15.0, 3.0, -3.0)

Left-clicks are sent with the Feather nRF52840 Sense's onboard button.

#  if onboard button is pressed, sends left mouse click
        if not click.value:
            mouse.click(Mouse.LEFT_BUTTON)
            time.sleep(0.2)

Scrolling is done by covering the APDS9960 proximity sensor and tilting the Feather Sense up or down. If the proximity sensor is covered, then mouse.move(wheel=int(scroll_dir)) is sent. Otherwise, the mouse's cursor will move freely around the screen.

#  if the proximity sensor is covered
        #  scroll the mouse
        if apds9960.proximity > distance:
            mouse.move(wheel=int(scroll_dir))
        #  otherwise move mouse cursor in x and y directions
        else:
            mouse.move(x=int(horizontal_mov))
            mouse.move(y=int(vertical_mov))

The value that triggers scrolling can be adjusted with the distance variable if you need to increase or decrease sensitivity.

#  variable for distance for proximity scrolling
distance = 245

This guide was first published on Sep 08, 2021. It was last updated on Sep 08, 2021.

This page (Code the BLE Gesture Mouse) was last updated on Mar 31, 2023.

Text editor powered by tinymce.