Skip to content
Snippets Groups Projects
Commit c5067970 authored by fuchsi*'s avatar fuchsi* Committed by rahix
Browse files

settings menu for the ecg app

parent 27777266
No related branches found
No related tags found
No related merge requests found
...@@ -7,12 +7,13 @@ import max30001 ...@@ -7,12 +7,13 @@ import max30001
import math import math
import struct import struct
import itertools import itertools
from ecg.settings import *
config = ecg_settings()
WIDTH = 160 WIDTH = 160
HEIGHT = 80 HEIGHT = 80
OFFSET_Y = 49 OFFSET_Y = 49
ECG_RATE = 128 HISTORY_MAX = WIDTH * 4
HISTORY_MAX = ECG_RATE * 4
DRAW_AFTER_SAMPLES = 5 DRAW_AFTER_SAMPLES = 5
SCALE_FACTOR = 30 SCALE_FACTOR = 30
MODE_USB = "USB" MODE_USB = "USB"
...@@ -26,20 +27,9 @@ COLOR_MODE_USB = [0, 0, 255] ...@@ -26,20 +27,9 @@ COLOR_MODE_USB = [0, 0, 255]
COLOR_WRITE_FG = [255, 255, 255] COLOR_WRITE_FG = [255, 255, 255]
COLOR_WRITE_BG = [255, 0, 0] COLOR_WRITE_BG = [255, 0, 0]
current_mode = MODE_FINGER
modes = itertools.cycle(
[
({"bar", "pulse"}, {"text": "Top + Pulse", "posx": 0}),
({}, {"text": "off", "posx": 55}),
({"bar"}, {"text": "Top Only", "posx": 25}),
({"pulse"}, {"text": "Pulse Only", "posx": 5}),
]
)
led_mode = next(modes)[0]
history = [] history = []
filebuffer = bytearray() filebuffer = bytearray()
write = 0 write = 0
bias = True
update_screen = 0 update_screen = 0
pause_screen = 0 pause_screen = 0
pause_histogram = False pause_histogram = False
...@@ -64,7 +54,7 @@ def update_history(datasets): ...@@ -64,7 +54,7 @@ def update_history(datasets):
global history, moving_average, alpha, beta, last_sample_count global history, moving_average, alpha, beta, last_sample_count
last_sample_count = len(datasets) last_sample_count = len(datasets)
for val in datasets: for val in datasets:
if current_mode == MODE_FINGER: if "HP" in config.get_option("Filter"):
history.append(val - moving_average) history.append(val - moving_average)
moving_average += betadash * (val - moving_average) moving_average += betadash * (val - moving_average)
# identical to: moving_average = (alpha * moving_average + beta * val) / (alpha + beta) # identical to: moving_average = (alpha * moving_average + beta * val) / (alpha + beta)
...@@ -81,7 +71,7 @@ samples_since_last_pulse = 0 ...@@ -81,7 +71,7 @@ samples_since_last_pulse = 0
last_pulse_blink = 0 last_pulse_blink = 0
q_threshold = -1 q_threshold = -1
r_threshold = 1 r_threshold = 1
q_spike = -ECG_RATE q_spike = -500 #just needs to be long ago
def neighbours(n, lst): def neighbours(n, lst):
...@@ -103,6 +93,8 @@ def detect_pulse(num_new_samples): ...@@ -103,6 +93,8 @@ def detect_pulse(num_new_samples):
# consider ["CDE", "DEF"] # consider ["CDE", "DEF"]
# new samples: "GHI" => "ABCDEFGHI" # new samples: "GHI" => "ABCDEFGHI"
# consider ["EFG", "FGH", "GHI"] # consider ["EFG", "FGH", "GHI"]
ecg_rate = config.get_option("Rate")
for [prev, cur, next_] in neighbours(3, history[-(num_new_samples + 2) :]): for [prev, cur, next_] in neighbours(3, history[-(num_new_samples + 2) :]):
samples_since_last_pulse += 1 samples_since_last_pulse += 1
...@@ -113,17 +105,17 @@ def detect_pulse(num_new_samples): ...@@ -113,17 +105,17 @@ def detect_pulse(num_new_samples):
elif ( elif (
prev < cur > next_ prev < cur > next_
and cur > r_threshold and cur > r_threshold
and samples_since_last_pulse - q_spike < ECG_RATE // 10 and samples_since_last_pulse - q_spike < ecg_rate // 10
): ):
# the full QRS complex is < 0.1s long, so the q and r spike in particular cannot be more than ECG_RATE//10 samples apart # the full QRS complex is < 0.1s long, so the q and r spike in particular cannot be more than ecg_rate//10 samples apart
pulse = 60 * ECG_RATE // samples_since_last_pulse pulse = 60 * ecg_rate // samples_since_last_pulse
samples_since_last_pulse = 0 samples_since_last_pulse = 0
q_spike = -ECG_RATE q_spike = -ecg_rate
if pulse < 30 or pulse > 210: if pulse < 30 or pulse > 210:
pulse = -1 pulse = -1
# we expect the next r-spike to be at least 60% as high as this one # we expect the next r-spike to be at least 60% as high as this one
r_threshold = (cur * 3) // 5 r_threshold = (cur * 3) // 5
elif samples_since_last_pulse > 2 * ECG_RATE: elif samples_since_last_pulse > 2 * ecg_rate:
q_threshold = -1 q_threshold = -1
r_threshold = 1 r_threshold = 1
pulse = -1 pulse = -1
...@@ -183,9 +175,9 @@ def write_filebuffer(): ...@@ -183,9 +175,9 @@ def write_filebuffer():
def open_sensor(): def open_sensor():
global sensor global sensor
sensor = max30001.MAX30001( sensor = max30001.MAX30001(
usb=(current_mode == MODE_USB), usb=(config.get_option("Mode") == "USB"),
bias=bias, bias=config.get_option("Bias"),
sample_rate=ECG_RATE, sample_rate=config.get_option("Rate"),
callback=callback_ecg, callback=callback_ecg,
) )
...@@ -195,34 +187,6 @@ def close_sensor(): ...@@ -195,34 +187,6 @@ def close_sensor():
sensor.close() sensor.close()
def toggle_mode():
global current_mode, disp, pause_screen
if write > 0:
pause_screen = utime.time_ms() + 500
disp.clear(COLOR_BACKGROUND)
disp.print("Locked", posx=30, posy=30, fg=COLOR_TEXT)
disp.update()
return
close_sensor()
current_mode = MODE_USB if current_mode == MODE_FINGER else MODE_FINGER
open_sensor()
def toggle_bias():
global bias, disp, pause_screen
if write > 0:
pause_screen = utime.time_ms() + 500
disp.clear(COLOR_BACKGROUND)
disp.print("Locked", posx=30, posy=30, fg=COLOR_TEXT)
disp.update()
return
close_sensor()
bias = not bias
open_sensor()
def toggle_write(): def toggle_write():
global write, disp, pause_screen global write, disp, pause_screen
pause_screen = utime.time_ms() + 1000 pause_screen = utime.time_ms() + 1000
...@@ -252,23 +216,14 @@ def toggle_pause(): ...@@ -252,23 +216,14 @@ def toggle_pause():
leds.clear() leds.clear()
def toggle_leds():
global led_mode, disp, pause_screen, leds, modes
led_mode, display_args = next(modes)
pause_screen = utime.time_ms() + 250
disp.clear(COLOR_BACKGROUND)
disp.print("LEDs", posx=50, posy=20, fg=COLOR_TEXT)
disp.print(**display_args, posy=40, fg=COLOR_TEXT)
disp.update()
leds.clear()
def draw_leds(vmin, vmax): def draw_leds(vmin, vmax):
# vmin should be in [0, -1] # vmin should be in [0, -1]
# vmax should be in [0, 1] # vmax should be in [0, 1]
global pulse, samples_since_last_pulse, last_pulse_blink global pulse, samples_since_last_pulse, last_pulse_blink
led_mode = config.get_option("LEDs")
# stop blinking # stop blinking
if not bool(led_mode): if not bool(led_mode):
return return
...@@ -301,7 +256,7 @@ def draw_leds(vmin, vmax): ...@@ -301,7 +256,7 @@ def draw_leds(vmin, vmax):
def draw_histogram(): def draw_histogram():
global disp, history, current_mode, bias, write, pause_screen, update_screen global disp, history, write, pause_screen, update_screen
# skip rendering due to message beeing shown # skip rendering due to message beeing shown
if pause_screen == -1: if pause_screen == -1:
...@@ -316,10 +271,10 @@ def draw_histogram(): ...@@ -316,10 +271,10 @@ def draw_histogram():
disp.clear(COLOR_BACKGROUND) disp.clear(COLOR_BACKGROUND)
# offset in pause_histogram mode # offset in pause_histogram mode
timeWindow = config.get_option("Window")
window_end = int(len(history) - histogram_offset) window_end = int(len(history) - histogram_offset)
s_start = max(0, window_end - (ECG_RATE * 2))
s_end = max(0, window_end) s_end = max(0, window_end)
s_draw = max(0, s_end - WIDTH) s_start = max(0, s_end - WIDTH*timeWindow)
# get max value and calc scale # get max value and calc scale
value_max = max(abs(x) for x in history[s_start:s_end]) value_max = max(abs(x) for x in history[s_start:s_end])
...@@ -327,11 +282,11 @@ def draw_histogram(): ...@@ -327,11 +282,11 @@ def draw_histogram():
# draw histogram # draw histogram
# values need to be inverted so high values are drawn with low pixel coordinates (at the top of the screen) # values need to be inverted so high values are drawn with low pixel coordinates (at the top of the screen)
draw_points = (int(-x * scale + OFFSET_Y) for x in history[s_draw:s_end]) draw_points = (int(-x * scale + OFFSET_Y) for x in history[s_start:s_end])
prev = next(draw_points) prev = next(draw_points)
for x, value in enumerate(draw_points): for x, value in enumerate(draw_points):
disp.line(x, prev, x + 1, value, col=COLOR_LINE) disp.line(x//timeWindow, prev, (x + 1)//timeWindow, value, col=COLOR_LINE)
prev = value prev = value
# draw text: mode/bias/write # draw text: mode/bias/write
...@@ -339,7 +294,7 @@ def draw_histogram(): ...@@ -339,7 +294,7 @@ def draw_histogram():
disp.print( disp.print(
"Pause" "Pause"
+ ( + (
" -{:0.1f}s".format(histogram_offset / ECG_RATE) " -{:0.1f}s".format(histogram_offset / config.get_option("Rate"))
if histogram_offset > 0 if histogram_offset > 0
else "" else ""
), ),
...@@ -354,11 +309,11 @@ def draw_histogram(): ...@@ -354,11 +309,11 @@ def draw_histogram():
) )
if pulse < 0: if pulse < 0:
disp.print( disp.print(
current_mode + ("+Bias" if bias else ""), config.get_option("Mode") + ("+Bias" if config.get_option("Bias") else ""),
posx=0, posx=0,
posy=0, posy=0,
fg=( fg=(
COLOR_MODE_FINGER if current_mode == MODE_FINGER else COLOR_MODE_USB COLOR_MODE_FINGER if config.get_option("Mode") == MODE_FINGER else COLOR_MODE_USB
), ),
) )
else: else:
...@@ -367,7 +322,7 @@ def draw_histogram(): ...@@ -367,7 +322,7 @@ def draw_histogram():
posx=0, posx=0,
posy=0, posy=0,
fg=( fg=(
COLOR_MODE_FINGER if current_mode == MODE_FINGER else COLOR_MODE_USB COLOR_MODE_FINGER if config.get_option("Mode") == MODE_FINGER else COLOR_MODE_USB
), ),
) )
...@@ -382,7 +337,7 @@ def draw_histogram(): ...@@ -382,7 +337,7 @@ def draw_histogram():
def main(): def main():
global pause_histogram, histogram_offset global pause_histogram, histogram_offset, pause_screen
# show button layout # show button layout
disp.clear(COLOR_BACKGROUND) disp.clear(COLOR_BACKGROUND)
...@@ -392,10 +347,10 @@ def main(): ...@@ -392,10 +347,10 @@ def main():
" Pause >", posx=0, posy=28, fg=COLOR_MODE_FINGER, font=display.FONT16 " Pause >", posx=0, posy=28, fg=COLOR_MODE_FINGER, font=display.FONT16
) )
disp.print( disp.print(
" Mode/Bias >", posx=0, posy=44, fg=COLOR_MODE_USB, font=display.FONT16 " Settings >", posx=0, posy=44, fg=COLOR_MODE_USB, font=display.FONT16
) )
disp.print( disp.print(
"< LED/WriteLog", posx=0, posy=64, fg=COLOR_WRITE_BG, font=display.FONT16 "< WriteLog ", posx=0, posy=64, fg=COLOR_WRITE_BG, font=display.FONT16
) )
disp.update() disp.update()
utime.sleep(3) utime.sleep(3)
...@@ -410,90 +365,64 @@ def main(): ...@@ -410,90 +365,64 @@ def main():
) )
# TOP RIGHT # TOP RIGHT
#
# pause
# down # down
if button_pressed["TOP_RIGHT"] == 0 and v & buttons.TOP_RIGHT != 0: if button_pressed["TOP_RIGHT"] == 0 and v & buttons.TOP_RIGHT != 0:
button_pressed["TOP_RIGHT"] = utime.time_ms() button_pressed["TOP_RIGHT"] = 1
toggle_pause() toggle_pause()
# up # up
if button_pressed["TOP_RIGHT"] > 0 and v & buttons.TOP_RIGHT == 0: if button_pressed["TOP_RIGHT"] > 0 and v & buttons.TOP_RIGHT == 0:
duration = utime.time_ms() - button_pressed["TOP_RIGHT"]
button_pressed["TOP_RIGHT"] = 0 button_pressed["TOP_RIGHT"] = 0
# BOTTOM LEFT # BOTTOM LEFT
# #
# on pause = shift view left # on pause = shift view left
# long = toggle write # else = toggle write
# short = toggle leds
# down, and still pressed
if (
button_pressed["BOTTOM_LEFT"] > 0
and v & buttons.BOTTOM_LEFT != 0
and not pause_histogram
):
duration = utime.time_ms() - button_pressed["BOTTOM_LEFT"]
if duration > 1000:
button_pressed["BOTTOM_LEFT"] = -1
toggle_write()
# register down event
elif button_pressed["BOTTOM_LEFT"] == 0 and v & buttons.BOTTOM_LEFT != 0:
button_pressed["BOTTOM_LEFT"] = utime.time_ms()
# register up event but event already called # down
if button_pressed["BOTTOM_LEFT"] == -1 and v & buttons.BOTTOM_LEFT == 0: if button_pressed["BOTTOM_LEFT"] == 0 and v & buttons.BOTTOM_LEFT != 0:
button_pressed["BOTTOM_LEFT"] = 0 button_pressed["BOTTOM_LEFT"] = 1
if pause_histogram:
# register normal up event
elif button_pressed["BOTTOM_LEFT"] > 0 and v & buttons.BOTTOM_LEFT == 0:
duration = utime.time_ms() - button_pressed["BOTTOM_LEFT"]
button_pressed["BOTTOM_LEFT"] = 0
if not pause_histogram:
toggle_leds()
else:
l = len(history) l = len(history)
histogram_offset += ECG_RATE / 2 histogram_offset += config.get_option("Rate") / 2
if l - histogram_offset < WIDTH: if l - histogram_offset < WIDTH*config.get_option("Window"):
histogram_offset = l - WIDTH histogram_offset = l - WIDTH*config.get_option("Window")
else:
toggle_write()
# up
if button_pressed["BOTTOM_LEFT"] > 0 and v & buttons.BOTTOM_LEFT == 0:
button_pressed["BOTTOM_LEFT"] = 0
# BOTTOM RIGHT # BOTTOM RIGHT
# #
# on pause = shift view right # on pause = shift view right
# long = toggle bias # else = show settings
# short = toggle mode (finger/usb)
# down, and still pressed
if (
button_pressed["BOTTOM_RIGHT"] > 0
and v & buttons.BOTTOM_RIGHT != 0
and not pause_histogram
):
duration = utime.time_ms() - button_pressed["BOTTOM_RIGHT"]
if duration > 1000:
button_pressed["BOTTOM_RIGHT"] = -1
toggle_bias()
# register down event
elif button_pressed["BOTTOM_RIGHT"] == 0 and v & buttons.BOTTOM_RIGHT != 0:
button_pressed["BOTTOM_RIGHT"] = utime.time_ms()
# register up event but event already called
if button_pressed["BOTTOM_RIGHT"] == -1 and v & buttons.BOTTOM_RIGHT == 0:
button_pressed["BOTTOM_RIGHT"] = 0
# register normal up event # down
elif button_pressed["BOTTOM_RIGHT"] > 0 and v & buttons.BOTTOM_RIGHT == 0: if button_pressed["BOTTOM_RIGHT"] == 0 and v & buttons.BOTTOM_RIGHT != 0:
duration = utime.time_ms() - button_pressed["BOTTOM_RIGHT"] button_pressed["BOTTOM_RIGHT"] = 1
button_pressed["BOTTOM_RIGHT"] = 0
if pause_histogram: if pause_histogram:
histogram_offset -= ECG_RATE / 2 histogram_offset -= config.get_option("Rate") / 2
histogram_offset -= histogram_offset % (ECG_RATE / 2) histogram_offset -= histogram_offset % (config.get_option("Rate") / 2)
if histogram_offset < 0: if histogram_offset < 0:
histogram_offset = 0 histogram_offset = 0
else: else:
toggle_mode() pause_screen = -1 # hide graph
leds.clear() # disable all LEDs
config.run() # show config menu
close_sensor() # reset sensor in case mode or bias was changed TODO do not close sensor otherwise?
open_sensor()
pause_screen = 0 # start plotting graph again
button_pressed["TOP_RIGHT"] = 1 # returning from menu was by pressing the TOP_RIGHT button
# up
if button_pressed["BOTTOM_RIGHT"] > 0 and v & buttons.BOTTOM_RIGHT == 0:
button_pressed["BOTTOM_RIGHT"] = 0
if __name__ == "__main__": if __name__ == "__main__":
......
import color
import simple_menu
import itertools
class Settings(simple_menu.Menu):
color_1 = color.CAMPGREEN
color_2 = color.CAMPGREEN_DARK
options = {}
def __init__(self):
super().__init__([("return",False)])
def on_select(self, value, index):
if index == 0:
self.exit()
else:
self.options[value[0]] = next(value[1])
def entry2name(self, value):
print(value, value[0])
if value[0]=="return":
return value[0]
else:
return "{}: {}".format(value[0], self.options[value[0]][0])
def add_option(self, option):
self.entries.append(option)
self.options[option[0]] = next(option[1])
def get_option(self, name):
return self.options[name][1]
def ecg_settings():
config = Settings()
config.add_option(("LEDs",
itertools.cycle(
[
("off", {}),
("Pulse", {"pulse"}),
("Bar", {"bar"}),
("Full", {"pulse", "bar"}),
]
)
))
config.add_option(("Mode",
itertools.cycle(
[
("Finger", "Finger"),
("USB", "USB")
]
)
))
config.add_option(("Bias",
itertools.cycle(
[
("on", True),
("off", False)
]
)
))
config.add_option(("Filter",
itertools.cycle(
[
("HP", {"HP"}),
("off", {})
]
)
))
config.add_option(("Rate",
itertools.cycle(
[
("128Hz", 128),
("256Hz", 256)
]
)
))
config.add_option(("Window",
itertools.cycle(
[
("1x", 1),
("2x", 2),
("3x", 3)
]
)
))
return config
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment