Coding the Buzzy Box

Adding Libraries

Once you've finished setting up your Feather Sense with CircuitPython, you can add these additional libraries to the lib folder:

  • adafruit_ble
  • adafruit_bluefruit_connect
  • adafruit_ble_apple_notification_center.mpy
  • adafruit_ble_broadcastnet.mpy
  • adafruit_drv2605.mpy

Then, you can click on the Download: Project Zip link above the code to download the code file.

import time
import board
import busio
import neopixel
import adafruit_drv2605
import adafruit_ble
from adafruit_ble.advertising.standard import SolicitServicesAdvertisement
from adafruit_ble.services.standard import CurrentTimeService
from adafruit_ble_apple_notification_center import AppleNotificationCenterService
from digitalio import DigitalInOut, Direction

#  setup for onboard NeoPixel
pixel_pin = board.NEOPIXEL
num_pixels = 1

pixel = neopixel.NeoPixel(pixel_pin, num_pixels, brightness=0.3, auto_write=False)

#  setup for haptic motor driver
i2c = busio.I2C(board.SCL, board.SDA)
drv = adafruit_drv2605.DRV2605(i2c)

#  onboard blue LED
blue_led = DigitalInOut(board.BLUE_LED)
blue_led.direction = Direction.OUTPUT

#  setup for BLE
ble = adafruit_ble.BLERadio()
if ble.connected:
    for c in ble.connections:
        c.disconnect()

advertisement = SolicitServicesAdvertisement()

#  adds ANCS and current time services for BLE to advertise
advertisement.solicited_services.append(AppleNotificationCenterService)
advertisement.solicited_services.append(CurrentTimeService)

#  state machines
current_notification = None #  tracks the current notification from ANCS
current_notifications = {} #  array to hold all current notifications from ANCS
cleared = False #  state to track if notifications have been cleared from ANCS
notification_service = None #  holds the array of active notifications from ANCS
all_ids = [] #  array to hold all of the ids from ANCS
hour = 0 #  used to track when it is on the hour for the mindfulness reminder
mindful = False #  state used to track if it is time for mindfulness
vibration = 16 #  vibration effect being used for the haptic motor
blue = (0, 0, 255) #  color blue for the NeoPixel
purple = (255, 0, 255) #  color purple for the NeoPixel
red = (255, 0, 0) #  color red for the NeoPixel
clear = (0, 0, 0) #  allows for NeoPixel to be turned 'off'

#  function for blinking NeoPixel
#  blinks: # of blinks
#  speed: how fast/slow blinks
#  color1: first color
#  color2: second color
def blink_pixel(blinks, speed, color1, color2):
    for _ in range(0, blinks):
        pixel.fill(color1)
        pixel.show()
        time.sleep(speed)
        pixel.fill(color2)
        pixel.show()
        time.sleep(speed)

#  function for haptic motor vibration
#  num_zzz: # of times vibrates
#  effect: type of vibration
#  delay: time between vibrations
def vibe(num_zzz, effect, delay):
    drv.sequence[0] = adafruit_drv2605.Effect(effect)
    for _ in range(0, num_zzz):
        drv.play()  # play the effect
        time.sleep(delay)  # for 0.5 seconds
        drv.stop()

#  start BLE
ble.start_advertising(advertisement)

while True:

    blue_led.value = False
    print("Waiting for connection")

    #  NeoPixel is red when not connected to BLE
    while not ble.connected:
        blue_led.value = False
        pixel.fill(red)
        pixel.show()

    print("Connected")

    while ble.connected:
        blue_led.value = True #  blue LED is on when connected
        all_ids.clear()
        for connection in ble.connections:
            if not connection.paired:
                #  pairs to phone
                connection.pair()
                print("paired")
            #  allows connection to CurrentTimeService
            cts = connection[CurrentTimeService]
            notification_service = connection[AppleNotificationCenterService]
        #  grabs notifications from ANCS
        current_notifications = notification_service.active_notifications

        for notif_id in current_notifications:
            #  adds notifications into array
            notification = current_notifications[notif_id]
            all_ids.append(notif_id)

        if current_notification and current_notification.removed:
            # Stop showing the latest and show that there are no new notifications.
            current_notification = None
            pixel.fill(clear)
            pixel.show()

        if not current_notification and not all_ids and not cleared:
            #  updates cleared state for notification
            cleared = True
            #  turns off NeoPixel when notifications are clear
            pixel.fill(clear)
            pixel.show()

        elif all_ids:
            cleared = False
            if current_notification and current_notification.id in all_ids:
                index = all_ids.index(current_notification.id)
            else:
                index = len(all_ids) - 1
                notif_id = all_ids[index]
            #  if there is a notification:
            if not current_notification or current_notification.id != notif_id:
                current_notification = current_notifications[notif_id]
                #  parses notification info into a string
                category = str(notification).split(" ", 1)[0]
                #  haptic motor vibrates
                vibe(2, vibration, 0.5)
                #  all info for notification is printed to REPL
                print('-'*36)
                print("Msg #%d - Category %s" % (notification.id, category))
                print("From app:", notification.app_id)
                if notification.title:
                    print("Title:", notification.title)
                if notification.subtitle:
                    print("Subtitle:", notification.subtitle)
                if notification.message:
                    print("Message:", notification.message)
                #  NeoPixel blinks and then stays on until cleared
                blink_pixel(2, 0.5, purple, clear)
                pixel.fill(purple)
                pixel.show()
        #  if it's on the hour:
        if cts.current_time[4] == hour and not mindful:
            print(cts.current_time[4])
            print("mindful time")
            #  haptic motor vibrates
            vibe(5, vibration, 1)
            #  NeoPixel blinks and then stays on
            blink_pixel(5, 1, blue, clear)
            mindful = True
            pixel.fill(blue)
            pixel.show()
            print("hour = ", hour)
        #  if it's no longer on the hour:
        if cts.current_time[4] == (hour + 1) and mindful:
            #  NeoPixel turns off
            mindful = False
            pixel.fill(clear)
            pixel.show()
            print("mindful time over")

    #  if BLE becomes disconnected then blue LED turns off
    #  and BLE begins advertising again to reconnect
    print("Disconnected")
    blue_led.value = False
    print()
    ble.start_advertising(advertisement)
    notification_service = None

Your Feather Sense CIRCUITPY drive should look like this after you load the libraries and code.py file:

This guide was first published on May 13, 2020. It was last updated on May 13, 2020.
This page (Coding the Buzzy Box) was last updated on Jun 29, 2020.