Skip to content
Snippets Groups Projects
Commit 4401598b authored by schneider's avatar schneider
Browse files

feat(max30001): Pycardium support

parent 051036fe
No related branches found
No related tags found
No related merge requests found
Pipeline #3193 failed
......@@ -23,6 +23,7 @@ Last but not least, if you want to start hacking the lower-level firmware, the
pycardium/overview
pycardium/stdlib
pycardium/bme680
pycardium/max30001
pycardium/buttons
pycardium/color
pycardium/display
......
......@@ -9,6 +9,7 @@ modsrc = files(
'modules/interrupt.c',
'modules/sys_leds.c',
'modules/light_sensor.c',
'modules/max30001-sys.c',
'modules/os.c',
'modules/personal_state.c',
'modules/power.c',
......
......@@ -91,6 +91,9 @@ static const mp_rom_map_elem_t interrupt_module_globals_table[] = {
MP_OBJ_NEW_SMALL_INT(EPIC_INT_BHI160_ORIENTATION) },
{ MP_ROM_QSTR(MP_QSTR_BHI160_GYROSCOPE),
MP_OBJ_NEW_SMALL_INT(EPIC_INT_BHI160_GYROSCOPE) },
{ MP_ROM_QSTR(MP_QSTR_MAX30001_ECG),
MP_OBJ_NEW_SMALL_INT(EPIC_INT_MAX30001_ECG) },
};
static MP_DEFINE_CONST_DICT(
interrupt_module_globals, interrupt_module_globals_table
......
#include "py/obj.h"
#include "py/runtime.h"
#include "py/builtin.h"
#include "epicardium.h"
#include "api/common.h"
#include "mphalport.h"
STATIC mp_obj_t mp_max30001_enable_sensor(size_t n_args, const mp_obj_t *args)
{
struct max30001_sensor_config cfg = { 0 };
cfg.usb = mp_obj_is_true(args[0]);
cfg.bias = mp_obj_is_true(args[1]);
cfg.sample_rate = mp_obj_get_int(args[2]);
cfg.sample_buffer_len = mp_obj_get_int(args[3]);
int stream_id = epic_max30001_enable_sensor(&cfg);
return MP_OBJ_NEW_SMALL_INT(stream_id);
}
STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(
mp_max30001_enable_sensor_obj, 4, 4, mp_max30001_enable_sensor
);
STATIC mp_obj_t mp_max30001_read_sensor(mp_obj_t stream_id_in)
{
int16_t buf[256];
int stream_id = mp_obj_get_int(stream_id_in);
int n = epic_stream_read(stream_id, buf, sizeof(buf));
mp_obj_list_t *list = mp_obj_new_list(0, NULL);
for (int i = 0; i < n; i++) {
mp_obj_list_append(list, mp_obj_new_int(buf[i]));
}
return MP_OBJ_FROM_PTR(list);
}
STATIC MP_DEFINE_CONST_FUN_OBJ_1(
mp_max30001_read_sensor_obj, mp_max30001_read_sensor
);
STATIC mp_obj_t mp_max30001_disable_sensor(void)
{
int ret = epic_max30001_disable_sensor();
return MP_OBJ_NEW_SMALL_INT(ret);
}
STATIC MP_DEFINE_CONST_FUN_OBJ_0(
mp_max30001_disable_sensor_obj, mp_max30001_disable_sensor
);
STATIC const mp_rom_map_elem_t max30001_module_globals_table[] = {
{ MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_sys_max30001) },
{ MP_ROM_QSTR(MP_QSTR_enable_sensor),
MP_ROM_PTR(&mp_max30001_enable_sensor_obj) },
{ MP_ROM_QSTR(MP_QSTR_read_sensor),
MP_ROM_PTR(&mp_max30001_read_sensor_obj) },
{ MP_ROM_QSTR(MP_QSTR_disable_sensor),
MP_ROM_PTR(&mp_max30001_disable_sensor_obj) },
};
STATIC MP_DEFINE_CONST_DICT(
max30001_module_globals, max30001_module_globals_table
);
// Define module object.
const mp_obj_module_t max30001_module = {
.base = { &mp_type_module },
.globals = (mp_obj_dict_t *)&max30001_module_globals,
};
/* clang-format off */
// Register the module to make it available in Python
MP_REGISTER_MODULE(MP_QSTR_sys_max30001, max30001_module, MODULE_MAX30001_ENABLED);
import sys_max30001
import interrupt
import ucollections
class MAX30001:
"""
The MAX30001 class provides a stram interface to the MAX30001
ECG and BIO-Z sensor.
.. code-block:: python
import max30001
m = max30001.MAX30001()
m.read()
m.close()
"""
def __init__(
self,
usb=False,
bias=True,
sample_rate=128,
callback=None,
sample_buffer_len=256,
):
"""
Initializes the MAX30001 (if it is not already running).
:param usb: True if all ECG leads should use the USB-C connector
:param bias: True if the internal bias of the ECG should be used. Mandatory if the card10 is not attached to the body and the USB-C connector is used.
:param sample_rate: Selected sample rate in Hz. Supported values: 128 and 256.
:param callback: If not None: A callback which is called with the data when ever new data is available
:param sample_buffer: Length of the internal buffer (in samples)
"""
self.sample_rate = sample_rate
self.callback = callback
self.sample_buffer_len = sample_buffer_len
self.interrupt_id = interrupt.MAX30001_ECG
self.usb = usb
self.bias = bias
self._callback = callback
self.enable_sensor()
def enable_sensor(self):
"""
Enables the sensor. Automatically called by __init__.
"""
interrupt.disable_callback(self.interrupt_id)
interrupt.set_callback(self.interrupt_id, self._interrupt)
self.stream_id = sys_max30001.enable_sensor(
self.usb, self.bias, self.sample_rate, self.sample_buffer_len
)
if self.stream_id < 0:
raise ValueError("Enable sensor returned %i", self.stream_id)
self.active = True
if self._callback:
interrupt.enable_callback(self.interrupt_id)
def __enter__(self):
return self
def __exit__(self, _et, _ev, _t):
self.close()
def close(self):
"""
Close the currently open connection to the sensor.
"""
if self.active:
self.active = False
ret = sys_max30001.disable_sensor(self.sensor_id)
if ret < 0:
raise ValueError("Disable sensor returned %i", ret)
interrupt.disable_callback(self.interrupt_id)
interrupt.set_callback(self.interrupt_id, None)
def read(self):
"""
Read as many samples (signed integer) as currently available.
"""
if self.active:
return sys_max30001.read_sensor(self.stream_id)
return []
def _interrupt(self, _):
if self.active:
data = self.read()
if self._callback:
self._callback(data)
......@@ -4,6 +4,7 @@ python_modules = files(
'htmlcolor.py',
'display.py',
'leds.py',
'max30001.py',
'pride.py',
'ledfx.py',
'simple_menu.py',
......
......@@ -163,3 +163,5 @@ Q(NO_CONTACT)
Q(CHAOS)
Q(COMMUNICATION)
Q(CAMP)
Q(MAX30001_ECG)
......@@ -53,6 +53,7 @@ int mp_hal_trng_read_int(void);
#define MODULE_INTERRUPT_ENABLED (1)
#define MODULE_LEDS_ENABLED (1)
#define MODULE_LIGHT_SENSOR_ENABLED (1)
#define MODULE_MAX30001_ENABLED (1)
#define MODULE_OS_ENABLED (1)
#define MODULE_PERSONAL_STATE_ENABLED (1)
#define MODULE_POWER_ENABLED (1)
......
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