diff --git a/preload/apps/ecg/__init__.py b/preload/apps/ecg/__init__.py index 6a0dd08886f37173306c49bbf40eee792d105f89..2299f13ec20695668583baed6cb7f7dcd8e44e14 100644 --- a/preload/apps/ecg/__init__.py +++ b/preload/apps/ecg/__init__.py @@ -1,3 +1,4 @@ +import button_gestures as bg import os import display import leds @@ -26,17 +27,23 @@ COLOR_WRITE_FG = [255, 255, 255] COLOR_WRITE_BG = [255, 0, 0] current_mode = MODE_FINGER +# FIFO from callback +new_datasets = [] history = [] filebuffer = bytearray() -write = 0 +recording_timestamp = 0 bias = True -update_screen = 0 +samples_since_draw = 0 pause_screen = 0 pause_histogram = False histogram_offset = 0 sensor = 0 disp = display.open() +mapper = bg.ActionMapper() +KEYMAP_PAUSED = {} +KEYMAP_NORMAL = {} + leds.dim_top(1) COLORS = [((23 + (15 * i)) % 360, 1.0, 1.0) for i in range(11)] @@ -47,9 +54,9 @@ alpha = 2 beta = 3 -def update_history(datasets): +def update_history(samples): global history, moving_average, alpha, beta - for val in datasets: + for val in samples: history.append(val - moving_average) moving_average = (alpha * moving_average + beta * val) / (alpha + beta) @@ -75,8 +82,8 @@ def neighbours(n, lst): yield lst[i : i + n] -def detect_pulse(num_new_samples): - global history, pulse, samples_since_last_pulse, q_threshold, r_threshold, q_spike +def detect_pulse(all_samples, num_new_samples): + global pulse, samples_since_last_pulse, q_threshold, r_threshold, q_spike # look at 3 consecutive samples, starting 2 samples before the samples that were just added, e.g.: # existing samples: "ABCD" @@ -84,7 +91,7 @@ def detect_pulse(num_new_samples): # consider ["CDE", "DEF"] # new samples: "GHI" => "ABCDEFGHI" # consider ["EFG", "FGH", "GHI"] - for [prev, cur, next_] in neighbours(3, history[-(num_new_samples + 2) :]): + for [prev, cur, next_] in neighbours(3, all_samples[-(num_new_samples + 2) :]): samples_since_last_pulse += 1 if prev > cur < next_ and cur < q_threshold: @@ -110,32 +117,36 @@ def detect_pulse(num_new_samples): pulse = -1 -def callback_ecg(datasets): - global update_screen, history, filebuffer, write - update_screen += len(datasets) +def callback_ecg(dataset): + new_datasets.append(dataset) + +def process_samples(samples): + global samples_since_draw, history, filebuffer, recording_timestamp + samples_since_draw += len(samples) # update histogram datalist if not pause_histogram: - update_history(datasets) - detect_pulse(len(datasets)) + update_history(samples) + detect_pulse(history, len(samples)) # buffer for writes - if write > 0: - for value in datasets: + if recording_timestamp > 0: + for value in samples: filebuffer.extend(struct.pack("h", value)) if len(filebuffer) >= FILEBUFFERBLOCK: write_filebuffer() # don't update on every callback - if update_screen >= DRAW_AFTER_SAMPLES: + if samples_since_draw >= DRAW_AFTER_SAMPLES: draw_histogram() + samples_since_draw = 0 def write_filebuffer(): - global write, filebuffer + global recording_timestamp, filebuffer # write to file chars = "" - lt = utime.localtime(write) + lt = utime.localtime(recording_timestamp) filename = "/ecg-{:04d}-{:02d}-{:02d}_{:02d}{:02d}{:02d}.log".format(*lt) # write stuff to disk @@ -145,7 +156,7 @@ def write_filebuffer(): f.close() except OSError as e: print("Please check the file or filesystem", e) - write = 0 + recording_timestamp = 0 pause_screen = -1 disp.clear(COLOR_BACKGROUND) disp.print("IO Error", posy=0, fg=COLOR_TEXT) @@ -156,7 +167,7 @@ def write_filebuffer(): close_sensor() except: print("Unexpected error, stop writeing logfile") - write = 0 + recording_timestamp = 0 filebuffer = bytearray() @@ -172,8 +183,8 @@ def open_sensor(): def close_sensor(): - global sensor sensor.close() + pass def toggle_mode(): @@ -191,32 +202,54 @@ def toggle_bias(): def toggle_write(): - global write, disp, pause_screen + global recording_timestamp, disp, pause_screen + pause_screen = utime.time_ms() + 1000 disp.clear(COLOR_BACKGROUND) - if write > 0: + if recording_timestamp > 0: write_filebuffer() - write = 0 + recording_timestamp = 0 disp.print("Stop", posx=50, posy=20, fg=COLOR_TEXT) disp.print("logging", posx=30, posy=40, fg=COLOR_TEXT) else: filebuffer = bytearray() - write = utime.time() + recording_timestamp = utime.time() disp.print("Start", posx=45, posy=20, fg=COLOR_TEXT) disp.print("logging", posx=30, posy=40, fg=COLOR_TEXT) disp.update() -def toggle_pause(): +def set_paused(paused): global pause_histogram, histogram_offset, history - if pause_histogram: - pause_histogram = False - history = [] - else: - pause_histogram = True + + pause_histogram = paused histogram_offset = 0 + if paused: + mapper.set_mapping(KEYMAP_PAUSED) + else: + mapper.set_mapping(KEYMAP_NORMAL) + history = [] + + +def scroll_left(): + global histogram_offset + + l = len(history) + histogram_offset += ECG_RATE / 2 + if l - histogram_offset < WIDTH: + histogram_offset = l - WIDTH + + +def scroll_right(): + global histogram_offset + + histogram_offset -= ECG_RATE / 2 + histogram_offset -= histogram_offset % (ECG_RATE / 2) + if histogram_offset < 0: + histogram_offset = 0 + def draw_leds(val): # val should be in [0, 11] @@ -226,7 +259,7 @@ def draw_leds(val): def draw_histogram(): - global disp, history, current_mode, bias, write, pause_screen, update_screen + global disp, history, current_mode, bias, recording_timestamp, pause_screen # skip rendering due to message beeing shown if pause_screen == -1: @@ -294,17 +327,29 @@ def draw_histogram(): ) # announce writing ecg log - if write > 0: + if recording_timestamp > 0: t = utime.time() - if write > 0 and t % 2 == 0: + if recording_timestamp > 0 and t % 2 == 0: disp.print("LOG", posx=0, posy=60, fg=COLOR_WRITE_FG, bg=COLOR_WRITE_BG) disp.update() - update_screen = 0 def main(): - global pause_histogram, histogram_offset + global new_datasets, KEYMAP_NORMAL, KEYMAP_PAUSED + + KEYMAP_NORMAL = { + (buttons.BOTTOM_LEFT, bg.SHORT_PRESS): toggle_write, + (buttons.BOTTOM_RIGHT, bg.SHORT_PRESS): toggle_bias, + (buttons.TOP_RIGHT, bg.SHORT_PRESS): toggle_mode, + (buttons.TOP_RIGHT, bg.LONG_PRESS): lambda: set_paused(True), + } + + KEYMAP_PAUSED = { + (buttons.BOTTOM_LEFT, bg.SHORT_PRESS): scroll_left, + (buttons.BOTTOM_RIGHT, bg.SHORT_PRESS): scroll_right, + (buttons.TOP_RIGHT, bg.SHORT_PRESS): lambda: set_paused(False), + } # show button layout disp.clear(COLOR_BACKGROUND) @@ -317,71 +362,17 @@ def main(): # start ecg open_sensor() + + mapper.set_mapping(KEYMAP_NORMAL) while True: - button_pressed = {"BOTTOM_LEFT": 0, "BOTTOM_RIGHT": 0, "TOP_RIGHT": 0} - while True: - v = buttons.read( - buttons.BOTTOM_LEFT | buttons.BOTTOM_RIGHT | buttons.TOP_RIGHT - ) + mapper.update() - # BOTTOM LEFT - - if button_pressed["BOTTOM_LEFT"] == 0 and v & buttons.BOTTOM_LEFT != 0: - button_pressed["BOTTOM_LEFT"] = utime.time_ms() - if not pause_histogram: - toggle_write() - else: - l = len(history) - histogram_offset += ECG_RATE / 2 - if l - histogram_offset < WIDTH: - histogram_offset = l - WIDTH - - if button_pressed["BOTTOM_LEFT"] > 0 and v & buttons.BOTTOM_LEFT == 0: - duration = utime.time_ms() - button_pressed["BOTTOM_LEFT"] - button_pressed["BOTTOM_LEFT"] = 0 - - # BOTTOM RIGHT - - if button_pressed["BOTTOM_RIGHT"] == 0 and v & buttons.BOTTOM_RIGHT != 0: - button_pressed["BOTTOM_RIGHT"] = utime.time_ms() - if not pause_histogram: - toggle_bias() - else: - histogram_offset -= ECG_RATE / 2 - histogram_offset -= histogram_offset % (ECG_RATE / 2) - if histogram_offset < 0: - histogram_offset = 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"] = 0 - - # TOP RIGHT - - # down, and still pressed - if button_pressed["TOP_RIGHT"] > 0 and v & buttons.TOP_RIGHT != 0: - duration = utime.time_ms() - button_pressed["TOP_RIGHT"] - if duration > 1000: - button_pressed["TOP_RIGHT"] = -1 - toggle_pause() - - # register down event - elif button_pressed["TOP_RIGHT"] == 0 and v & buttons.TOP_RIGHT != 0: - button_pressed["TOP_RIGHT"] = utime.time_ms() - - # register up event but event already called - if button_pressed["TOP_RIGHT"] == -1 and v & buttons.TOP_RIGHT == 0: - button_pressed["TOP_RIGHT"] = 0 - - # register normal up event - elif button_pressed["TOP_RIGHT"] > 0 and v & buttons.TOP_RIGHT == 0: - duration = utime.time_ms() - button_pressed["TOP_RIGHT"] - button_pressed["TOP_RIGHT"] = 0 - if pause_histogram: - toggle_pause() - else: - toggle_mode() + processed_datasets = 0 + for dataset in new_datasets: + process_samples(dataset) + processed_datasets += 1 + new_datasets = new_datasets[processed_datasets:] if __name__ == "__main__": main()