Overview
Circuit Playground Bluefruit displays your notification icons so you know when there's fresh activity!
Using the Apple Notification Center Service (ANCS), this project displays app icons on your TFT Gizmo display whenever there's an important alert from one of your apps, such as Slack, Basecamp, Discord, SMS messages, calendar events and more!
You can even use the A and B buttons on the CPB to scroll between current notification icons.
Once you dismiss a notification on your phone, the associated icon will disappear from the Gizmo screen.
Page last edited March 08, 2024
Text editor powered by tinymce.
CircuitPython on Circuit Playground Bluefruit
Install or Update CircuitPython
Follow this quick step-by-step to install or update CircuitPython on your Circuit Playground Bluefruit.
Click the link above and download the latest UF2 file
Download and save it to your Desktop (or wherever is handy)
Plug your Circuit Playground Bluefruit into your computer using a known-good data-capable USB cable.
A lot of people end up using charge-only USB cables and it is very frustrating! So make sure you have a USB cable you know is good for data sync.
Double-click the small Reset button in the middle of the CPB (indicated by the red arrow in the image). The ten NeoPixel LEDs will all turn red, and then will all turn green. If they turn all red and stay red, check the USB cable, try another USB port, etc. The little red LED next to the USB connector will pulse red - this is ok!
If double-clicking doesn't work the first time, try again. Sometimes it can take a few tries to get the rhythm right!
(If double-clicking doesn't do it, try a single-click!)
You will see a new disk drive appear called CPLAYBTBOOT.
Drag the adafruit_circuitpython_etc.uf2 file to CPLAYBTBOOT.
Page last edited March 08, 2024
Text editor powered by tinymce.
Circuit Playground Bluefruit CircuitPython Libraries
The Circuit Playground Bluefruit is packed full of features like Bluetooth and NeoPixel LEDs. Now that you have CircuitPython installed on your Circuit Playground Bluefruit, you'll need to install a base set of CircuitPython libraries to use the features of the board with CircuitPython.
Follow these steps to get the necessary libraries installed.
Installing CircuitPython Libraries on Circuit Playground Bluefruit
If you do not already have a lib folder on your CIRCUITPY drive, create one now.
Then, download the CircuitPython library bundle that matches your version of CircuitPython from CircuitPython.org.
Once inside, you'll find a lengthy list of folders and .mpy files. To install a CircuitPython library, you drag the file or folder from the bundle lib folder to the lib folder on your CIRCUITPY drive.
Copy the following folders and files from the bundle lib folder to the lib folder on your CIRCUITPY drive:
- adafruit_ble
- adafruit_bluefruit_connect
- adafruit_bus_device
- adafruit_circuitplayground
- adafruit_gizmo
- adafruit_hid
- adafruit_lis3dh.mpy
- adafruit_thermistor.mpy
- neopixel.mpy
Your lib folder should look like the image on the left.
Now you're all set to use CircuitPython with the features of the Circuit Playground Bluefruit!
Page last edited March 08, 2024
Text editor powered by tinymce.
Code with CircuitPython
The Apple Notification Center Service (ANCS) allows iOS devices to act as a provider of notification alerts to Bluetooth Low Energy (BLE) accessories, such as the Apple Watch, or in our case, the Circuit Playground Bluefruit!
We've created a simple program that allows the CPB to pair with your iOS device over Bluetooth and then it will receive ANCS notifications to display on the TFT Gizmo.
In general, we recommend installing the libraries mentioned on the previous page for your Circuit Playground Bluefruit projects, however you can get away with a subset of them for this one. You can see them listed in the image here.
You'll also need to add the adafruit_ble_apple_notification_center.mpy file from the library bundle as seen in the image here.
Once you save the code.py, graphics, and .wav file to your CIRCUITPY drive as directed below, this image is what your drive should look like.
Click the "Download: Project Zip" link in the code block below to get all the files from the project's GitHub repo.
Then, uncompress the zip file and open the code.py file in Mu, then save it to your CPB's CIRCUITPY drive as code.py.
# SPDX-FileCopyrightText: 2019 Melissa LeBlanc-Williams for Adafruit Industries
#
# SPDX-License-Identifier: MIT
"""
This demo shows the latest icons from a connected Apple device on a TFT Gizmo screen.
The A and B buttons on the CircuitPlayground Bluefruit can be used to scroll through all active
notifications. The screen's backlight will turn off after a certain number of seconds to save power.
New notifications or pressing the buttons should turn it back on.
"""
import time
import board
import digitalio
import displayio
import adafruit_ble
from adafruit_ble.advertising.standard import SolicitServicesAdvertisement
from adafruit_ble_apple_notification_center import AppleNotificationCenterService
from adafruit_gizmo import tft_gizmo
from audiocore import WaveFile
from audiopwmio import PWMAudioOut as AudioOut
# Enable the speaker
speaker_enable = digitalio.DigitalInOut(board.SPEAKER_ENABLE)
speaker_enable.direction = digitalio.Direction.OUTPUT
speaker_enable.value = True
audio = AudioOut(board.SPEAKER)
# This is a whitelist of apps to show notifications from.
APP_ICONS = {
"com.tinyspeck.chatlyio": "/ancs_slack.bmp",
"com.basecamp.bc3-ios": "/ancs_basecamp.bmp",
"com.apple.MobileSMS": "/ancs_sms.bmp",
"com.hammerandchisel.discord": "/ancs_discord.bmp",
"com.apple.mobilecal": "/ancs_ical.bmp",
"com.apple.mobilephone": "/ancs_phone.bmp"
}
BLOCKLIST = []
DELAY_AFTER_PRESS = 15
DEBOUNCE = 0.1
DIM_TIMEOUT = 20 # Amount of timeout to turn off backlight
DIM_LEVEL = 0.05
a = digitalio.DigitalInOut(board.BUTTON_A)
a.switch_to_input(pull=digitalio.Pull.DOWN)
b = digitalio.DigitalInOut(board.BUTTON_B)
b.switch_to_input(pull=digitalio.Pull.DOWN)
file = open("/triode_rise.wav", "rb")
wave = WaveFile(file)
def play_sound():
audio.play(wave)
time.sleep(1)
def find_connection():
for connection in radio.connections:
if AppleNotificationCenterService not in connection:
continue
if not connection.paired:
connection.pair()
return connection, connection[AppleNotificationCenterService]
return None, None
class Dimmer:
def __init__(self):
self._update_time = time.monotonic()
self._level = DIM_LEVEL
self._timeout = DIM_TIMEOUT
def update(self):
self._update_time = time.monotonic()
def check_timeout(self):
if a.value or b.value:
self._update_time = time.monotonic()
if time.monotonic() - self._update_time > self._timeout:
if display.brightness > self._level:
display.brightness = self._level
else:
if display.brightness == self._level:
display.brightness = 1.0
dimmer = Dimmer()
# Start advertising before messing with the display so that we can connect immediately.
radio = adafruit_ble.BLERadio()
advertisement = SolicitServicesAdvertisement()
advertisement.complete_name = "CIRCUITPY"
advertisement.solicited_services.append(AppleNotificationCenterService)
def wrap_in_tilegrid(filename:str):
odb = displayio.OnDiskBitmap(filename)
return displayio.TileGrid(odb, pixel_shader=odb.pixel_shader)
display = tft_gizmo.TFT_Gizmo()
group = displayio.Group()
group.append(wrap_in_tilegrid("/ancs_connect.bmp"))
display.root_group = group
current_notification = None
current_notifications = {}
all_ids = []
last_press = time.monotonic()
active_connection, notification_service = find_connection()
cleared = False
while True:
if not active_connection:
radio.start_advertising(advertisement)
while not active_connection:
active_connection, notification_service = find_connection()
dimmer.check_timeout()
# Connected
dimmer.update()
play_sound()
no_notifications = "/ancs_none.bmp"
group.append(wrap_in_tilegrid(no_notifications))
while active_connection.connected:
all_ids.clear()
current_notifications = notification_service.active_notifications
for notif_id in current_notifications:
notification = current_notifications[notif_id]
if notification.app_id not in APP_ICONS or notification.app_id in BLOCKLIST:
continue
all_ids.append(notif_id)
# pylint: disable=protected-access
all_ids.sort(key=lambda x: current_notifications[x]._raw_date)
# pylint: enable=protected-access
if current_notification and current_notification.removed:
# Stop showing the latest and show that there are no new notifications.
current_notification = None
if not current_notification and not all_ids and not cleared:
cleared = True
dimmer.update()
group[1] = wrap_in_tilegrid(no_notifications)
elif all_ids:
cleared = False
now = time.monotonic()
if current_notification and current_notification.id in all_ids and \
now - last_press < DELAY_AFTER_PRESS:
index = all_ids.index(current_notification.id)
else:
index = len(all_ids) - 1
if now - last_press >= DEBOUNCE:
if b.value and index > 0:
last_press = now
index += -1
if a.value and index < len(all_ids) - 1:
last_press = now
index += 1
notif_id = all_ids[index]
if not current_notification or current_notification.id != notif_id:
dimmer.update()
current_notification = current_notifications[notif_id]
# pylint: disable=protected-access
print(current_notification._raw_date, current_notification)
# pylint: enable=protected-access
group[1] = wrap_in_tilegrid(APP_ICONS[current_notification.app_id])
dimmer.check_timeout()
# Bluetooth Disconnected
group.pop()
dimmer.update()
active_connection = None
notification_service = None
Code Explaination
Apple devices centralize the management of notifications into the "Notification Center", which is accessed on the lock screen or after swiping down from the top of the screen. Since this info is centralized, Apple provides access to the current notifications through a Bluetooth Low Energy Service on the device. Bluetooth Services are a collection of data referred to as Characteristics. In CircuitPython libraries we provide definitions for common services like the Apple Notification Center Service (or ANCS for short) so that they are easier to use.
The ANCS library is designed for two main uses. First, it allows one to list all currently active notifications. This is done by reading the active_notifications attribute of the service. Second, it allows you to wait for new notifications to come in and react to them. This is done by looping over wait_for_new_notifications().
Both of these uses provide a Notification object for each active notification. Reading the attributes of these objects will load the data from the peer device as needed. This can make printing slow but it conserves data which in turn saves battery. So, only read the attributes you need to know. Printing the object is useful for debugging but it loads many attributes and therefore, takes time.
The most useful attribute for this project is the app_id. The app_id identifies which app generated the notification. It is not the app name, but may be derived from it or the name of the folks who created the app. In the example code.py we've already added a few app_ids. For example, Twitter is com.atebits.Tweetie2, Slack is com.tinyspeck.chatlyio and GMail is com.google.Gmail. To add support for an app you use, connect to the serial terminal while running the example the Apple Notification Center library's simple test, which prints out all of the current notification's app_ids and title.
Page last edited March 08, 2024
Text editor powered by tinymce.
Notification Icons
Graphics
In keeping with the nature of a simple alert system, we decided to use graphic icons rather than text for these notifications. If you see a logo pop up, you can then go look at the actual notification on your iOS device. This is a great place to read the details of your message or alert, and this action will also clear the notification from the CPB!
By using the "Download: Project Zip" in the code window on the previous pages, you also got all the graphics files from the project GitHub repo. Copy these .bmp files to the root directory of your CIRCUITPY drive.
Connect to Bluetooth
This icon lets you know you need to pair your iOS device (from the Bluetooth settings page) with the CPB.
No Notifications
This bell icon lets you know that the CPB is connected over to an iOS device and awaiting new notifications.
Notification Icons
Here are some icons we made for popular notifications that are also included in the code.
CircuitPython is so fast and easy, pleasing clients with logo changes in minutes is possible, so here's the new logo, too! You can just change the icon name in code and use the one you want!
Customization
If you'd like to make new icons for other notifications, you'll need to create 16-bit .bmp files at 240x240 pixels.
You can use image software such as Photoshop, Affinity Photo, GIMP, or others to mask and layer an application icon on top of the black background and inside the white circle. Below you'll find a blank version of our design to use as a starting point.
Or, you can use any image you like, so long as you save or convert it first to the 240x240 .bmp file format first. You can use an online image converter for this.
Page last edited March 08, 2024
Text editor powered by tinymce.
ANCS Simple Demo
REPL Print Only Version
If you want to add more icon pairings, you'll need to know what sort of data ANCS is sending from the phone so you can match your icon to the app type.
This version of the code will give you just the emitted data! It's very informative to see what info comes along with your notifications. Just download the print_code.py file and paste it into your Mu window, then save it to your Circuit Playground Bluefruit as code.py.
Pair your iOS device with the CIRCUITPY BLE device and you'll see lots of great notification info!
# SPDX-FileCopyrightText: 2019 John Edgar Park for Adafruit Industries
#
# SPDX-License-Identifier: MIT
"""This demo shows the latest notification from a connected Apple device on the REPL and Gizmo"""
import time
import adafruit_ble
from adafruit_ble.advertising.standard import SolicitServicesAdvertisement
from adafruit_ble_apple_notification_center import AppleNotificationCenterService
from adafruit_gizmo import tft_gizmo
# This is a whitelist of apps to show notifications from.
#APPS = ["com.tinyspeck.chatlyio", "com.atebits.Tweetie2"]
APPS = []
DELAY_AFTER_PRESS = 15
DEBOUNCE = 0.1
def find_connection():
for connection in radio.connections:
if AppleNotificationCenterService not in connection:
continue
if not connection.paired:
connection.pair()
return connection, connection[AppleNotificationCenterService]
return None, None
# Start advertising before messing with the display so that we can connect immediately.
radio = adafruit_ble.BLERadio()
advertisement = SolicitServicesAdvertisement()
advertisement.complete_name = "CIRCUITPY"
advertisement.solicited_services.append(AppleNotificationCenterService)
display = tft_gizmo.TFT_Gizmo()
current_notification = None
new_ids = []
displayed_ids = []
active_connection, notification_service = find_connection()
while True:
if not active_connection:
radio.start_advertising(advertisement)
while not active_connection:
print("waiting for connection...")
active_connection, notification_service = find_connection()
time.sleep(0.1)
while active_connection.connected:
current_notifications = notification_service.active_notifications
for notification_id in current_notifications:
if notification_id in displayed_ids:
continue # already seen!
notification = current_notifications[notification_id]
print('-'*36)
category = str(notification).split(" ", 1)[0]
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)
displayed_ids.append(id)
active_connection = None
notification_service = None
Page last edited March 08, 2024
Text editor powered by tinymce.
Assembly
Assemble the Notifier
Now, you can use your iOS Notification Gizmo! I assembled the board with a battery between the TFT Gizmo and the CPB as shown here.
I decided to print one of the the 3D cases designed by the Ruiz Bros. in this guide so I can use it as a sort of pocket watch style alert device!
Use the Notifier
To use the notifier, the first step is to connect to Bluefruit.
First, make sure the Notifier is powered on. You'll see the connect to Bluetooth device screen.
On your iOS device, make sure Bluetooth is turned on and go to Settings > Bluetooth.
Pick the Circuit Playground Bluefruit device that shows up, and then press the "Pair" button.
Once connected, you'll see the default notifications bell screen. This indicates that there is a Bluetooth connection to your iOS device and no new notifications are in your notification center.
Notifications
Hey look! An SMS notification came in! Now I now to look at my phone and read the message.Oh, it's an Arrested Development GIF from my friend Tod featuring Tobias Fünke, awesome!
Now, I can clear the message from the iOS Notification screen and this will also clear it from the Gizmo screen.
Page last edited March 08, 2024
Text editor powered by tinymce.