|
| 1 | +""" |
| 2 | +SPDX-FileCopyrightText: 2022 Isaac Wellish for Adafruit Industries |
| 3 | +SPDX-License-Identifier: MIT |
| 4 | +Circuit Playground Bluefruit BLE Heart Rate Display |
| 5 | +Read heart rate data from a heart rate peripheral using |
| 6 | +the standard BLE heart rate service. |
| 7 | +The heart rate monitor connects to the CPB via BLE. |
| 8 | +LEDs on CPB blink to the heart rate of the user. |
| 9 | +""" |
| 10 | + |
| 11 | +import time |
| 12 | +import board |
| 13 | +import neopixel |
| 14 | +import adafruit_ble |
| 15 | +from adafruit_ble.advertising.standard import ProvideServicesAdvertisement |
| 16 | +from adafruit_ble.services.standard.device_info import DeviceInfoService |
| 17 | +from adafruit_ble_heart_rate import HeartRateService |
| 18 | +from digitalio import DigitalInOut, Direction |
| 19 | + |
| 20 | +#on-board status LED setup |
| 21 | +red_led = DigitalInOut(board.D13) |
| 22 | +red_led.direction = Direction.OUTPUT |
| 23 | +red_led.value = True |
| 24 | + |
| 25 | +#NeoPixel code |
| 26 | +pixels = neopixel.NeoPixel(board.NEOPIXEL, 10, brightness=0.2, auto_write=False) |
| 27 | +RED = (255, 0, 0) |
| 28 | +LIGHTRED = (20, 0, 0) |
| 29 | + |
| 30 | +def color_chase(color, wait): |
| 31 | + for i in range(10): |
| 32 | + pixels[i] = color |
| 33 | + time.sleep(wait) |
| 34 | + pixels.show() |
| 35 | + time.sleep(0.5) |
| 36 | + |
| 37 | +# animation to show initialization of program |
| 38 | +color_chase(RED, 0.1) # Increase the number to slow down the color chase |
| 39 | + |
| 40 | +#starting bpm value |
| 41 | +bpm = 60 |
| 42 | + |
| 43 | +# PyLint can't find BLERadio for some reason so special case it here. |
| 44 | +ble = adafruit_ble.BLERadio() # pylint: disable=no-member |
| 45 | + |
| 46 | +hr_connection = None |
| 47 | + |
| 48 | +# Start with a fresh connection. |
| 49 | +if ble.connected: |
| 50 | + time.sleep(1) |
| 51 | + |
| 52 | + for connection in ble.connections: |
| 53 | + if HeartRateService in connection: |
| 54 | + connection.disconnect() |
| 55 | + break |
| 56 | + |
| 57 | +while True: |
| 58 | + print("Scanning...") |
| 59 | + red_led.value = True |
| 60 | + time.sleep(1) |
| 61 | + |
| 62 | + for adv in ble.start_scan(ProvideServicesAdvertisement, timeout=5): |
| 63 | + if HeartRateService in adv.services: |
| 64 | + print("found a HeartRateService advertisement") |
| 65 | + hr_connection = ble.connect(adv) |
| 66 | + time.sleep(2) |
| 67 | + print("Connected") |
| 68 | + red_led.value = False |
| 69 | + break |
| 70 | + |
| 71 | + # Stop scanning whether or not we are connected. |
| 72 | + ble.stop_scan() |
| 73 | + print("Stopped scan") |
| 74 | + red_led.value = False |
| 75 | + time.sleep(0.5) |
| 76 | + |
| 77 | + if hr_connection and hr_connection.connected: |
| 78 | + print("Fetch connection") |
| 79 | + if DeviceInfoService in hr_connection: |
| 80 | + dis = hr_connection[DeviceInfoService] |
| 81 | + try: |
| 82 | + manufacturer = dis.manufacturer |
| 83 | + except AttributeError: |
| 84 | + manufacturer = "(Manufacturer Not specified)" |
| 85 | + try: |
| 86 | + model_number = dis.model_number |
| 87 | + except AttributeError: |
| 88 | + model_number = "(Model number not specified)" |
| 89 | + print("Device:", manufacturer, model_number) |
| 90 | + else: |
| 91 | + print("No device information") |
| 92 | + hr_service = hr_connection[HeartRateService] |
| 93 | + print("Location:", hr_service.location) |
| 94 | + |
| 95 | + while hr_connection.connected: |
| 96 | + values = hr_service.measurement_values |
| 97 | + print(values) # returns the full heart_rate data set |
| 98 | + if values: |
| 99 | + bpm = (values.heart_rate) |
| 100 | + if values.heart_rate == 0: |
| 101 | + print("-") |
| 102 | + else: |
| 103 | + time.sleep(0.1) |
| 104 | + print(bpm) |
| 105 | + if bpm != 0: # prevent from divide by zero |
| 106 | + #find interval time between beats |
| 107 | + bps = bpm / 60 |
| 108 | + period = 1 / bps |
| 109 | + time_on = 0.375 * period |
| 110 | + time_off = period - time_on |
| 111 | + |
| 112 | + # Blink leds at the given BPM |
| 113 | + pixels.fill(RED) |
| 114 | + pixels.show() |
| 115 | + time.sleep(time_on) |
| 116 | + pixels.fill(LIGHTRED) |
| 117 | + pixels.show() |
| 118 | + time.sleep(time_off) |
0 commit comments