/ BLE_Buzzy_Box / code.py
code.py
  1  # SPDX-FileCopyrightText: 2020 Liz Clark for Adafruit Industries
  2  #
  3  # SPDX-License-Identifier: MIT
  4  
  5  import time
  6  import board
  7  import busio
  8  import neopixel
  9  import adafruit_drv2605
 10  import adafruit_ble
 11  from adafruit_ble.advertising.standard import SolicitServicesAdvertisement
 12  from adafruit_ble.services.standard import CurrentTimeService
 13  from adafruit_ble_apple_notification_center import AppleNotificationCenterService
 14  from digitalio import DigitalInOut, Direction
 15  
 16  #  setup for onboard NeoPixel
 17  pixel_pin = board.NEOPIXEL
 18  num_pixels = 1
 19  
 20  pixel = neopixel.NeoPixel(pixel_pin, num_pixels, brightness=0.3, auto_write=False)
 21  
 22  #  setup for haptic motor driver
 23  i2c = busio.I2C(board.SCL, board.SDA)
 24  drv = adafruit_drv2605.DRV2605(i2c)
 25  
 26  #  onboard blue LED
 27  blue_led = DigitalInOut(board.BLUE_LED)
 28  blue_led.direction = Direction.OUTPUT
 29  
 30  #  setup for BLE
 31  ble = adafruit_ble.BLERadio()
 32  if ble.connected:
 33      for c in ble.connections:
 34          c.disconnect()
 35  
 36  advertisement = SolicitServicesAdvertisement()
 37  
 38  #  adds ANCS and current time services for BLE to advertise
 39  advertisement.solicited_services.append(AppleNotificationCenterService)
 40  advertisement.solicited_services.append(CurrentTimeService)
 41  
 42  #  state machines
 43  current_notification = None #  tracks the current notification from ANCS
 44  current_notifications = {} #  array to hold all current notifications from ANCS
 45  cleared = False #  state to track if notifications have been cleared from ANCS
 46  notification_service = None #  holds the array of active notifications from ANCS
 47  all_ids = [] #  array to hold all of the ids from ANCS
 48  hour = 0 #  used to track when it is on the hour for the mindfulness reminder
 49  mindful = False #  state used to track if it is time for mindfulness
 50  vibration = 16 #  vibration effect being used for the haptic motor
 51  blue = (0, 0, 255) #  color blue for the NeoPixel
 52  purple = (255, 0, 255) #  color purple for the NeoPixel
 53  red = (255, 0, 0) #  color red for the NeoPixel
 54  clear = (0, 0, 0) #  allows for NeoPixel to be turned 'off'
 55  
 56  #  function for blinking NeoPixel
 57  #  blinks: # of blinks
 58  #  speed: how fast/slow blinks
 59  #  color1: first color
 60  #  color2: second color
 61  def blink_pixel(blinks, speed, color1, color2):
 62      for _ in range(0, blinks):
 63          pixel.fill(color1)
 64          pixel.show()
 65          time.sleep(speed)
 66          pixel.fill(color2)
 67          pixel.show()
 68          time.sleep(speed)
 69  
 70  #  function for haptic motor vibration
 71  #  num_zzz: # of times vibrates
 72  #  effect: type of vibration
 73  #  delay: time between vibrations
 74  def vibe(num_zzz, effect, delay):
 75      drv.sequence[0] = adafruit_drv2605.Effect(effect)
 76      for _ in range(0, num_zzz):
 77          drv.play()  # play the effect
 78          time.sleep(delay)  # for 0.5 seconds
 79          drv.stop()
 80  
 81  #  start BLE
 82  ble.start_advertising(advertisement)
 83  
 84  while True:
 85  
 86      blue_led.value = False
 87      print("Waiting for connection")
 88  
 89      #  NeoPixel is red when not connected to BLE
 90      while not ble.connected:
 91          blue_led.value = False
 92          pixel.fill(red)
 93          pixel.show()
 94  
 95      print("Connected")
 96  
 97      while ble.connected:
 98          blue_led.value = True #  blue LED is on when connected
 99          all_ids.clear()
100          for connection in ble.connections:
101              if not connection.paired:
102                  #  pairs to phone
103                  connection.pair()
104                  print("paired")
105              #  allows connection to CurrentTimeService
106              cts = connection[CurrentTimeService]
107              notification_service = connection[AppleNotificationCenterService]
108          #  grabs notifications from ANCS
109          current_notifications = notification_service.active_notifications
110  
111          for notif_id in current_notifications:
112              #  adds notifications into array
113              notification = current_notifications[notif_id]
114              all_ids.append(notif_id)
115  
116          if current_notification and current_notification.removed:
117              # Stop showing the latest and show that there are no new notifications.
118              current_notification = None
119              pixel.fill(clear)
120              pixel.show()
121  
122          if not current_notification and not all_ids and not cleared:
123              #  updates cleared state for notification
124              cleared = True
125              #  turns off NeoPixel when notifications are clear
126              pixel.fill(clear)
127              pixel.show()
128  
129          elif all_ids:
130              cleared = False
131              if current_notification and current_notification.id in all_ids:
132                  index = all_ids.index(current_notification.id)
133              else:
134                  index = len(all_ids) - 1
135                  notif_id = all_ids[index]
136              #  if there is a notification:
137              if not current_notification or current_notification.id != notif_id:
138                  current_notification = current_notifications[notif_id]
139                  #  parses notification info into a string
140                  category = str(notification).split(" ", 1)[0]
141                  #  haptic motor vibrates
142                  vibe(2, vibration, 0.5)
143                  #  all info for notification is printed to REPL
144                  print('-'*36)
145                  print("Msg #%d - Category %s" % (notification.id, category))
146                  print("From app:", notification.app_id)
147                  if notification.title:
148                      print("Title:", notification.title)
149                  if notification.subtitle:
150                      print("Subtitle:", notification.subtitle)
151                  if notification.message:
152                      print("Message:", notification.message)
153                  #  NeoPixel blinks and then stays on until cleared
154                  blink_pixel(2, 0.5, purple, clear)
155                  pixel.fill(purple)
156                  pixel.show()
157          #  if it's on the hour:
158          if cts.current_time[4] == hour and not mindful:
159              print(cts.current_time[4])
160              print("mindful time")
161              #  haptic motor vibrates
162              vibe(5, vibration, 1)
163              #  NeoPixel blinks and then stays on
164              blink_pixel(5, 1, blue, clear)
165              mindful = True
166              pixel.fill(blue)
167              pixel.show()
168              print("hour = ", hour)
169          #  if it's no longer on the hour:
170          if cts.current_time[4] == (hour + 1) and mindful:
171              #  NeoPixel turns off
172              mindful = False
173              pixel.fill(clear)
174              pixel.show()
175              print("mindful time over")
176  
177      #  if BLE becomes disconnected then blue LED turns off
178      #  and BLE begins advertising again to reconnect
179      print("Disconnected")
180      blue_led.value = False
181      print()
182      ble.start_advertising(advertisement)
183      notification_service = None