Compare commits

...

6 Commits

Author SHA1 Message Date
NateSchoolfield 5849fc5b6d More README updates 2021-02-15 20:18:54 -08:00
NateSchoolfield bfb2e81ec1 Updated README 2021-02-15 20:15:27 -08:00
NateSchoolfield fc23f1f3c9 Fixed variable name to not conflict with base function names 2021-02-15 20:10:17 -08:00
NateSchoolfield 6e3ddc2045 Cleanup 2021-02-15 20:08:10 -08:00
NateSchoolfield e6732d62df Added heartrate alarm 2021-02-15 20:05:55 -08:00
NateSchoolfield c3bc050593 Added heartrate alarm 2021-02-15 20:04:48 -08:00
6 changed files with 82 additions and 17 deletions

8
.gitignore vendored
View File

@ -1,5 +1,6 @@
mac.txt
auth_key.txt
mac*.txt
auth_key*.txt
*.sh
*.pyc
/__pycache__
*.swp
@ -8,3 +9,6 @@ auth_key.txt
*.code-workspace
/.vscode
/data
datafilter.py
crymonitor.py

11
README
View File

@ -10,10 +10,8 @@ You will need to create two .txt files in the base directory:
CURRENT STATUS:
Right now the project does not yet fulfill its primary purpose (responding to biometrics).
If executed now, it will:
* Log raw and averaged biometric data to disk
* Vibrate in a random pattern every 30 minutes (adjustable)
The project now supports basic "heartrate alarming". Currently it's configured to send a 10-second random vibration pattern if the heartrate increase percentage goes above 17% of the lowest HR value for the last 10 readings, with a 20-minute delay between vibrations. This was tested on the Miband5, which has a better HR sensor, though I've not yet fully figured out the gyroscope data. Right now only HR and vibration are supported on the Miband5.
GOALS:
* Publish statistics to Google Sheets or other easy-to-use target. I'd like to make this usable by non-technical folks, so S3 buckets and CloudWatch are out of scope.
@ -24,7 +22,4 @@ GOALS:
DISCLAIMER:
None of the statements on this web site have been evaluated by the FDA.
Furthermore, none of the statements herein should be construed as dispensing medical advice, or making claims regarding the cure or treatment of diseases.
These statements have not been evaluated by the Food and Drug Administration.
These project is not intended to diagnose, treat, cure, or prevent any diseases.
None of the statements on this web site have been evaluated by the FDA. Furthermore, none of the statements herein should be construed as dispensing medical advice, or making claims regarding the cure or treatment of diseases. These statements have not been evaluated by the Food and Drug Administration. This project is not intended to diagnose, treat, cure, or prevent any diseases.

View File

@ -15,9 +15,10 @@ mac_filename = 'mac.txt'
maximize_graph = False
vibration_settings = {
'interval_minutes': 0.1,
'duration_seconds': 5,
'type': 'random'
'interval_minutes': 20,
'duration_seconds': 10,
'type': 'random',
'heartrate_alarm_pct': 17
}
band = None
@ -65,7 +66,7 @@ def average_data(tick_time):
sleepdata.average_raw_data(tick_time)
sleepdata.last_tick_time = time.time()
def sleep_monitor_callback(data):
tick_time = time.time()
@ -79,6 +80,9 @@ def sleep_monitor_callback(data):
average_data(tick_time)
vibration.heartrate_increase_pct = sleepdata.analyze_heartrate(10)
print("HR increase percent: {}".format(vibration.heartrate_increase_pct))
def connect():
global band
@ -113,7 +117,9 @@ def start_data_pull():
def start_vibration():
while True:
try:
vibration.timed_vibration(vibration_settings)
#vibration.timed_vibration(vibration_settings)
vibration.heartrate_alarm(vibration_settings)
except BTLEDisconnectError:
print("Vibration thread waiting for band reconnect...")
time.sleep(1)

View File

@ -231,7 +231,7 @@ class miband(Peripheral):
def _parse_heart_measure(self, bytes):
res = struct.unpack('bb', bytes)[1]
return_tuple = ["HR", res]
print("BPM: {}".format(res))
#print("BPM: {}".format(res))
return return_tuple

View File

@ -12,6 +12,7 @@ sleep_data = {
'periods': [2, 5, 10, 15],
'raw_data': [],
'averaged_data': [],
'last_hr': []
},
'movement':{
'value_name': 'movement',
@ -173,6 +174,7 @@ def process_gyro_data(gyro_data, tick_time):
def process_heartrate_data(heartrate_data, tick_time):
last_heartrate_count = 20
print("BPM: " + str(heartrate_data))
if heartrate_data > 0:
value_name = sleep_data['heartrate']['value_name']
@ -181,6 +183,21 @@ def process_heartrate_data(heartrate_data, tick_time):
value_name: heartrate_data
} )
if len(sleep_data['heartrate']['last_hr']) > last_heartrate_count:
sleep_data['heartrate']['last_hr'].pop(0)
sleep_data['heartrate']['last_hr'].append(heartrate_data)
def analyze_heartrate(hr_count):
# Finds the pct change between the lowest HR in the last $hr_count samples and the current HR
pct_heartrate_increase = 0
if len(sleep_data['heartrate']['last_hr']) >= hr_count:
last_heartrate_list = sleep_data['heartrate']['last_hr'][-hr_count:]
last_heartrate_min = min(last_heartrate_list)
current_heartrate = last_heartrate_list[-1]
pct_heartrate_increase = int((current_heartrate - last_heartrate_min)/last_heartrate_min*100)
return pct_heartrate_increase
def zero_to_nan(value):
if value == 0:

View File

@ -12,6 +12,8 @@ import logging
class Vibrate():
vibrate_band = None
vibration_log = None
heartrate_increase_pct = 0
def __init__(self, band):
self.vibrate_band = band
@ -22,12 +24,53 @@ class Vibrate():
self.vibration_log = logging.getLogger(__name__)
self.vibration_log.setLevel(vibration_log_level)
def heartrate_alarm(self, settings):
interval_minutes = settings['interval_minutes']
duration_seconds = settings['duration_seconds']
vibration_type = settings['type']
heartrate_alarm_pct = settings['heartrate_alarm_pct']
tick_time = time.time()
buzz_delay = interval_minutes * 60
buzz_timer = tick_time - buzz_delay
self.vibration_log.info("Starting heartrate alarm timer, alarming at {} percent for {} seconds with a {} minute interval".format(
heartrate_alarm_pct,
duration_seconds,
interval_minutes))
if vibration_type not in ['random', 'pattern', 'rolling', 'continuous']:
self.vibration_log.warn("Invalid or no vibration type specified: {}".format(type))
self.vibration_log.warn("Must be one of these: random, pattern, rolling, continuous")
return
while True:
elapsed_time = tick_time - buzz_timer
if elapsed_time >= buzz_delay and self.heartrate_increase_pct >= heartrate_alarm_pct:
self.vibration_log.info("Heartrate alarm triggered at {} percent, buzzing".format(self.heartrate_increase_pct))
if vibration_type == 'random':
self.vibrate_random(duration_seconds)
elif vibration_type == 'pattern':
self.vibrate_pattern(duration_seconds)
elif vibration_type == 'rolling':
self.vibrate_rolling(duration_seconds)
elif vibration_type == 'continuous':
self.vibrate_continuous(duration_seconds)
buzz_timer = tick_time
elif not elapsed_time >= buzz_delay and self.heartrate_increase_pct >= heartrate_alarm_pct:
self.vibration_log.info("Heartrate alarm threshold reached ({} percent) but timout not expired".format(self.heartrate_increase_pct))
else:
tick_time = time.time()
time.sleep(0.5)
def timed_vibration(self, settings):
interval_minutes = settings['interval_minutes']
duration_seconds = settings['duration_seconds']
type = settings['type']
buzz_timer = time.time()
buzz_timer = time.time()
tick_time = time.time()
buzz_delay = interval_minutes * 60