code.py
  1  # SPDX-FileCopyrightText: 2020 John Park for Adafruit Industries
  2  #
  3  # SPDX-License-Identifier: MIT
  4  
  5  """
  6  Heart Rate Trainer
  7  Read heart rate data from a heart rate peripheral using the standard BLE
  8  Heart Rate service.
  9  Displays BPM value to Seven Segment FeatherWing
 10  Displays percentage of max heart rate on another 7Seg FeatherWing
 11  """
 12  
 13  import time
 14  import board
 15  
 16  import adafruit_ble
 17  from adafruit_ble.advertising.standard import ProvideServicesAdvertisement
 18  from adafruit_ble.services.standard.device_info import DeviceInfoService
 19  from adafruit_ble_heart_rate import HeartRateService
 20  
 21  from adafruit_ht16k33.segments import Seg7x4
 22  
 23  from digitalio import DigitalInOut, Direction
 24  
 25  # Feather on-board status LEDs setup
 26  red_led = DigitalInOut(board.RED_LED)
 27  red_led.direction = Direction.OUTPUT
 28  red_led.value = True
 29  
 30  blue_led = DigitalInOut(board.BLUE_LED)
 31  blue_led.direction = Direction.OUTPUT
 32  blue_led.value = False
 33  
 34  # target heart rate for interval training
 35  # Change this number depending on your max heart rate, usually figured
 36  # as (220 - your age).
 37  max_rate = 180
 38  
 39  # Seven Segment FeatherWing setup
 40  i2c = board.I2C()
 41  display_A = Seg7x4(i2c, address=0x70)  # this will be the BPM display
 42  display_A.fill(0)  # Clear the display
 43  # Second display has A0 address jumpered
 44  display_B = Seg7x4(i2c, address=0x71)  # this will be the % target display
 45  display_B.fill(0)  # Clear the display
 46  
 47  # display_A "b.P.M."
 48  display_A.set_digit_raw(0, 0b11111100)
 49  display_A.set_digit_raw(1, 0b11110011)
 50  display_A.set_digit_raw(2, 0b00110011)
 51  display_A.set_digit_raw(3, 0b10100111)
 52  # display_B "Prct"
 53  display_B.set_digit_raw(0, 0b01110011)
 54  display_B.set_digit_raw(1, 0b01010000)
 55  display_B.set_digit_raw(2, 0b01011000)
 56  display_B.set_digit_raw(3, 0b01000110)
 57  time.sleep(3)
 58  
 59  display_A.fill(0)
 60  for h in range(4):
 61      display_A.set_digit_raw(h, 0b10000000)
 62  # display_B show maximum heart rate value
 63  display_B.fill(0)
 64  display_B.print(max_rate)
 65  time.sleep(2)
 66  
 67  # PyLint can't find BLERadio for some reason so special case it here.
 68  ble = adafruit_ble.BLERadio()    # pylint: disable=no-member
 69  
 70  hr_connection = None
 71  
 72  def display_SCAN():
 73      display_A.fill(0)
 74      display_A.set_digit_raw(0, 0b01101101)
 75      display_A.set_digit_raw(1, 0b00111001)
 76      display_A.set_digit_raw(2, 0b01110111)
 77      display_A.set_digit_raw(3, 0b00110111)
 78  
 79  
 80  def display_bLE():
 81      display_B.fill(0)
 82      display_B.set_digit_raw(0, 0b00000000)
 83      display_B.set_digit_raw(1, 0b01111100)
 84      display_B.set_digit_raw(2, 0b00111000)
 85      display_B.set_digit_raw(3, 0b01111001)
 86  
 87  def display_dots():  # "...."
 88      for j in range(4):
 89          display_A.set_digit_raw(j, 0b10000000)
 90          display_B.set_digit_raw(j, 0b10000000)
 91  
 92  def display_dashes():  # "----"
 93      for k in range(4):
 94          display_A.set_digit_raw(k, 0b01000000)
 95          display_B.set_digit_raw(k, 0b01000000)
 96  
 97  # Start with a fresh connection.
 98  if ble.connected:
 99      display_SCAN()
100      display_bLE()
101      time.sleep(1)
102  
103      for connection in ble.connections:
104          if HeartRateService in connection:
105              connection.disconnect()
106          break
107  
108  while True:
109      print("Scanning...")
110      red_led.value = True
111      blue_led.value = False
112      display_SCAN()
113      display_bLE()
114      time.sleep(1)
115  
116  
117      for adv in ble.start_scan(ProvideServicesAdvertisement, timeout=5):
118          if HeartRateService in adv.services:
119              print("found a HeartRateService advertisement")
120              hr_connection = ble.connect(adv)
121              display_dots()
122              time.sleep(2)
123              print("Connected")
124              blue_led.value = True
125              red_led.value = False
126              break
127  
128      # Stop scanning whether or not we are connected.
129      ble.stop_scan()
130      print("Stopped scan")
131      red_led.value = False
132      blue_led.value = True
133      time.sleep(0.5)
134  
135      if hr_connection and hr_connection.connected:
136          print("Fetch connection")
137          if DeviceInfoService in hr_connection:
138              dis = hr_connection[DeviceInfoService]
139              try:
140                  manufacturer = dis.manufacturer
141              except AttributeError:
142                  manufacturer = "(Manufacturer Not specified)"
143              try:
144                  model_number = dis.model_number
145              except AttributeError:
146                  model_number = "(Model number not specified)"
147              print("Device:", manufacturer, model_number)
148          else:
149              print("No device information")
150          hr_service = hr_connection[HeartRateService]
151          print("Location:", hr_service.location)
152  
153          while hr_connection.connected:
154              values = hr_service.measurement_values
155              print(values)  # returns the full heart_rate data set
156              if values:
157                  bpm = (values.heart_rate)
158                  if bpm is not 0:
159                      pct_target = (round(100*(bpm/max_rate)))
160                  display_A.fill(0)  # clear the display
161                  display_B.fill(0)
162                  if values.heart_rate is 0:
163                      display_dashes()
164                  else:
165                      display_A.fill(0)
166                      display_B.print(pct_target)
167                      time.sleep(0.1)
168                      display_A.print(bpm)
169  
170              time.sleep(0.9)
171              display_A.set_digit_raw(0, 0b00000000)