diff --git a/Documentation/bluetooth/ecg.rst b/Documentation/bluetooth/ecg.rst new file mode 100644 index 0000000000000000000000000000000000000000..3af7cf771a757baf4a9ab6f4ab0fccb1046761e1 --- /dev/null +++ b/Documentation/bluetooth/ecg.rst @@ -0,0 +1,27 @@ +Bluetooth ECG Service +======================== + +.. warning:: + The service is still work in progress and subject to change + +The ECG service provides access to the ECG sensor of the card10 + +BLE Service +----------- + +The current draft uses following service specification: + +- Service: + + UUID: ``42230300-2342-2342-2342-234223422342`` + +- ECG samples characteristic: + + UUID: ``42230301-2342-2342-2342-234223422342`` + notify + +ECG samples characteristic +-------------------------- + +List of 16 bit samples (big endian). Enable notifications to +receive a stream of samples while the ECG app is open. diff --git a/Documentation/index.rst b/Documentation/index.rst index d7acad2eea356d26215186f0da41f0a85950fb32..5c8f389f0c4d34ec38a288aade40e75cd60d8847 100644 --- a/Documentation/index.rst +++ b/Documentation/index.rst @@ -75,6 +75,7 @@ Last but not least, if you want to start hacking the lower-level firmware, the bluetooth/ess bluetooth/file-transfer bluetooth/card10 + bluetooth/ecg bluetooth/nimble Indices and tables diff --git a/preload/apps/ecg/__init__.py b/preload/apps/ecg/__init__.py index 978141d0e7c6722ad31e4ec1f6e976bd4e37195a..a19cebc80ae0a80f4bd365683ac1101d5a70058e 100644 --- a/preload/apps/ecg/__init__.py +++ b/preload/apps/ecg/__init__.py @@ -7,6 +7,7 @@ import max30001 import math import struct import itertools +import bluetooth from ecg.settings import * config = ecg_settings() @@ -43,6 +44,52 @@ sensor = 0 disp = display.open() last_sample_count = 1 + +_IRQ_CENTRAL_CONNECT = const(1) +_IRQ_CENTRAL_DISCONNECT = const(2) +_IRQ_GATTS_WRITE = const(3) + + +def ble_irq(event, data): + global ble_streaming + if event == _IRQ_CENTRAL_CONNECT: + print("BLE Connected") + elif event == _IRQ_CENTRAL_DISCONNECT: + print("BLE Disconnected") + elif event == _IRQ_GATTS_WRITE: + conn_handle, value_handle = data + if value_handle == ecg_cccd_handle: + value = b.gatts_read(value_handle) + print("New cccd value:", value) + # Value of 0 means notifcations off + if value == b"\x00\x00": + ble_streaming = False + if not config.get_option("BLE Disp"): + disp.backlight(20) + else: + ble_streaming = True + if not config.get_option("BLE Disp"): + disp.backlight(0) + + +b = bluetooth.BLE() +b.active(True) +b.irq(ble_irq) + +_ECG_UUID = bluetooth.UUID("42230300-2342-2342-2342-234223422342") +_ECG_DATA = ( + bluetooth.UUID("42230301-2342-2342-2342-234223422342"), + bluetooth.FLAG_READ | bluetooth.FLAG_NOTIFY, +) + +_ECG_SERVICE = (_ECG_UUID, (_ECG_DATA,)) +((ecg_data_handle,),) = b.gatts_register_services((_ECG_SERVICE,)) +ecg_cccd_handle = ecg_data_handle + 1 + +# Disable streaming by default +ble_streaming = False +b.gatts_write(ecg_cccd_handle, "\x00\x00") + leds.dim_top(1) COLORS = [((23 + (15 * i)) % 360, 1.0, 1.0) for i in range(11)] @@ -129,7 +176,20 @@ def detect_pulse(num_new_samples): def callback_ecg(datasets): - global update_screen, history, filebuffer, write, samples_since_start_of_write + global update_screen, history, filebuffer, write, samples_since_start_of_write, ble_streaming, ecg_data_handle + + if ble_streaming: + try: + b.gatts_notify( + 1, ecg_data_handle, struct.pack(">" + ("h" * len(datasets)), *datasets) + ) + except: + pass + + # Don't update the screen if it should be off during a connection + if not config.get_option("BLE Disp"): + return + update_screen += len(datasets) if write > 0: samples_since_start_of_write += len(datasets) @@ -441,7 +501,13 @@ def main(): else: pause_screen = -1 # hide graph leds.clear() # disable all LEDs + if ble_streaming and not config.get_option("BLE Disp"): + disp.backlight(20) + config.run() # show config menu + + if ble_streaming and not config.get_option("BLE Disp"): + disp.backlight(0) 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 diff --git a/preload/apps/ecg/settings.py b/preload/apps/ecg/settings.py index ca49bbd109a0b7d372b7320ea5b89c4444fd396b..2336a5c798e9c6e36652f69011669f1f5ceff7c0 100644 --- a/preload/apps/ecg/settings.py +++ b/preload/apps/ecg/settings.py @@ -95,6 +95,7 @@ def ecg_settings(): 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)]))) + config.add_option(("BLE Disp", itertools.cycle([("Off", False), ("On", True)]))) config.add_option( ( "Log",