From bd47b9ed45c6e298d1a5b166405f8890b61ad9a0 Mon Sep 17 00:00:00 2001 From: Noel Eck Date: Thu, 8 Dec 2016 15:24:36 -0800 Subject: [PATCH] max30100: Initial commit - MAX30100 pulse oximeter * c/c++ source * java/js/python/c/c++ examples * Doc image (png) * Tested on Intel Edison * TODO: Tuning for SpO2 reading Signed-off-by: Noel Eck --- docs/images/max30100.png | Bin 0 -> 108119 bytes examples/c++/CMakeLists.txt | 1 + examples/c++/max30100.cxx | 99 ++++++ examples/c/CMakeLists.txt | 1 + examples/c/max30100.c | 130 +++++++ examples/java/CMakeLists.txt | 1 + examples/java/MAX30100_Example.java | 88 +++++ examples/javascript/max30100.js | 57 +++ examples/python/max30100.py | 71 ++++ src/max30100/CMakeLists.txt | 9 + src/max30100/javaupm_max30100.i | 21 ++ src/max30100/jsupm_max30100.i | 9 + src/max30100/max30100.c | 529 ++++++++++++++++++++++++++++ src/max30100/max30100.cxx | 211 +++++++++++ src/max30100/max30100.h | 318 +++++++++++++++++ src/max30100/max30100.hpp | 295 ++++++++++++++++ src/max30100/max30100_fti.c | 109 ++++++ src/max30100/max30100_regs.h | 170 +++++++++ src/max30100/pyupm_max30100.i | 14 + 19 files changed, 2133 insertions(+) create mode 100644 docs/images/max30100.png create mode 100644 examples/c++/max30100.cxx create mode 100644 examples/c/max30100.c create mode 100644 examples/java/MAX30100_Example.java create mode 100644 examples/javascript/max30100.js create mode 100755 examples/python/max30100.py create mode 100644 src/max30100/CMakeLists.txt create mode 100644 src/max30100/javaupm_max30100.i create mode 100644 src/max30100/jsupm_max30100.i create mode 100644 src/max30100/max30100.c create mode 100644 src/max30100/max30100.cxx create mode 100644 src/max30100/max30100.h create mode 100644 src/max30100/max30100.hpp create mode 100644 src/max30100/max30100_fti.c create mode 100644 src/max30100/max30100_regs.h create mode 100644 src/max30100/pyupm_max30100.i diff --git a/docs/images/max30100.png b/docs/images/max30100.png new file mode 100644 index 0000000000000000000000000000000000000000..9604fe694b1b5d61a405c54a338491db1df1ddb1 GIT binary patch literal 108119 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 @MYC1mo)fd2IKIUB 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¬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 + * Copyright (c) 2016 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include +#include +#include + +#include "max30100.hpp" + +using namespace upm; + +int shouldRun = true; + +void sig_handler(int signo) +{ + if (signo == SIGINT) + shouldRun = false; +} + +// Example Callback handler +class mycallback : public Callback +{ + public: + virtual void run(max30100_value samp) + { + std::cout << "My callback sample IR: " + << samp.IR << " R: " << samp.R << std::endl; + } +}; + +int main(int argc, char **argv) +{ + signal(SIGINT, sig_handler); + //! [Interesting] + + // Instantiate a MAX30100 instance using i2c bus 0 + upm::MAX30100 sensor(0); + + // Create an instance of the Callback class + mycallback cb; + + // Read the temperature and version + std::cout << "Temperature: " << sensor.temperature() << " C" << std::endl; + std::cout << "Version: " << sensor.version() << std::endl; + + // Set high-res (50 Hz, 16-bit) + sensor.high_res_enable(true); + + // Set to sample SpO2 + sensor.mode(MAX30100_MODE_SPO2_EN); + + // Read continuously, stepping up the LED current every second, + // us GPIO 0 as the interrupt pin + sensor.sample_continuous(0, false, &cb); + for (int i = MAX30100_LED_CURRENT_0_0_MA; + i <= MAX30100_LED_CURRENT_50_0_MA && shouldRun; i++) + { + // Toggle the LED current + std::cout << "Setting LED current = " << i << std::endl; + + sensor.current((MAX30100_LED_CURRENT)i, (MAX30100_LED_CURRENT)i); + + upm_delay(1); + } + + // Read individual samples + for (int i = 0; i < 10; i++) + { + max30100_value val = sensor.sample(); + std::cout << "Single value IR: " << val.IR << " R: " << val.R << std::endl; + } + + //! [Interesting] + + std::cout << "Exiting..." << std::endl; + + return 0; +} diff --git a/examples/c/CMakeLists.txt b/examples/c/CMakeLists.txt index 8eb8e86b..2f8bfed5 100644 --- a/examples/c/CMakeLists.txt +++ b/examples/c/CMakeLists.txt @@ -142,6 +142,7 @@ add_example (ims) add_example (ecezo) add_example (mb704x) add_example (mcp2515) +add_example (max30100) # Custom examples add_custom_example (nmea_gps_i2c-example-c nmea_gps_i2c.c nmea_gps) diff --git a/examples/c/max30100.c b/examples/c/max30100.c new file mode 100644 index 00000000..d4898941 --- /dev/null +++ b/examples/c/max30100.c @@ -0,0 +1,130 @@ +/* + * Author: Noel Eck + * Copyright (c) 2016 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include +#include +#include + +#include "max30100.h" +#include "upm_utilities.h" + +bool shouldRun = true; + +void sig_handler(int signo) +{ + if (signo == SIGINT) + shouldRun = false; +} + +void my_sample_handler(max30100_value sample, void* arg) +{ + printf("My callback sample IR: %d R: %d\n", + sample.IR, sample.R); +} + +int main() +{ + signal(SIGINT, sig_handler); + + //! [Interesting] + max30100_context* sensor = max30100_init(0); + + if (!sensor) + { + printf("max30100_init() failed\n"); + return -1; + } + + /* Get the temperature */ + float temp; + if (max30100_get_temperature(sensor, &temp) != UPM_SUCCESS) + { + printf("max30100_get_temperature failed\n"); + goto max30100_exit; + } + + /* Get the version */ + uint16_t version; + if (max30100_get_version(sensor, &version) != UPM_SUCCESS) + { + printf("max30100_get_version failed\n"); + goto max30100_exit; + } + + printf("Temperature: %f C\n", temp); + printf("Version: 0x%04x\n", version); + + /* Set high-res (50 Hz, 16-bit) */ + if (max30100_set_high_res(sensor, true) != UPM_SUCCESS) + { + printf("max30100_set_high_res failed\n"); + goto max30100_exit; + } + + /* Set to sample SpO2 */ + if (max30100_set_mode(sensor, MAX30100_MODE_SPO2_EN) != UPM_SUCCESS) + { + printf("max30100_set_mode failed\n"); + goto max30100_exit; + } + + /* Read continuously, stepping up the LED current every second, + * us GPIO 0 as the interrupt pin */ + max30100_sample_continuous(sensor, 0, false, &my_sample_handler, sensor); + for (int i = MAX30100_LED_CURRENT_0_0_MA; + i <= MAX30100_LED_CURRENT_50_0_MA && shouldRun; i++) + { + /* Toggle the LED current */ + printf("Setting LED current = %d\n", i); + + if ( max30100_set_current(sensor, (MAX30100_LED_CURRENT)i, + (MAX30100_LED_CURRENT)i) != UPM_SUCCESS ) + { + printf("max30100_set_current failed\n"); + goto max30100_exit; + } + + upm_delay(1); + } + + /* Read individual samples */ + for (int i = 0; i < 10; i++) + { + max30100_value samp; + if (max30100_sample(sensor, &samp) != UPM_SUCCESS) + { + printf("max30100_sample failed\n"); + goto max30100_exit; + } + + printf("Single value IR: %d R: %d\n", samp.IR, samp.R); + } + +max30100_exit: + //! [Interesting] + printf("Exiting\n"); + max30100_close(sensor); + + return 0; +} diff --git a/examples/java/CMakeLists.txt b/examples/java/CMakeLists.txt index 50d53ad4..dbceccf8 100644 --- a/examples/java/CMakeLists.txt +++ b/examples/java/CMakeLists.txt @@ -162,6 +162,7 @@ add_example(IMS_Example ims) add_example(MB704X_Example mb704x) add_example(MCP2515_Example mcp2515) add_example(Ads1015Sample ads1x15) +add_example(MAX30100_Example max30100) add_example_with_path(Jhd1313m1_lcdSample lcd i2clcd) add_example_with_path(Jhd1313m1Sample lcd i2clcd) diff --git a/examples/java/MAX30100_Example.java b/examples/java/MAX30100_Example.java new file mode 100644 index 00000000..5d5319e8 --- /dev/null +++ b/examples/java/MAX30100_Example.java @@ -0,0 +1,88 @@ +/* + * Author: Noel Eck + * Copyright (c) 2016 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +import upm_max30100.*; + +public class MAX30100_Example +{ + public static void main(String[] args) throws InterruptedException + { + // ! [Interesting] + + // Instantiate a MAX30100 instance using bus 0 + MAX30100 sensor = new MAX30100((short)0); + + System.out.println("Oximeter sensor example..."); + + // Read the temperature and version + System.out.format("Temperature: %f C\n", sensor.temperature()); + System.out.format("Version: 0x%04x\n", sensor.version()); + + // Set high-res (50 Hz, 16-bit) + sensor.high_res_enable(true); + + // Set to sample SpO2 + sensor.mode(MAX30100_MODE.MAX30100_MODE_SPO2_EN); + + Callback cb = new JavaCallback(); + + // Read continuously, stepping up the LED current every second, + // us GPIO 0 as the interrupt pin + sensor.sample_continuous(0, false, cb); + for (int i = 0; i <= 15; i++) + { + // Toggle the LED current + System.out.format("Setting LED current = %d\n", i); + + sensor.current(MAX30100_LED_CURRENT.swigToEnum(i), + MAX30100_LED_CURRENT.swigToEnum(i)); + + Thread.sleep(1000); + } + + sensor.sample_stop(); + + // Read individual samples + for (int i = 0; i < 10; i++) { + max30100_value val = sensor.sample(); + System.out.format("Single value IR: %d R: %d\n", val.getIR(), val.getR()); + } + + // ! [Interesting] + } +} + +class JavaCallback extends Callback +{ + public JavaCallback() + { + super(); + } + + public void run(max30100_value val) + { + System.out.format("My callback sample IR: %d R: %d\n", val.getIR(), val.getR()); + } +} + diff --git a/examples/javascript/max30100.js b/examples/javascript/max30100.js new file mode 100644 index 00000000..3eac912a --- /dev/null +++ b/examples/javascript/max30100.js @@ -0,0 +1,57 @@ +/* + * Author: Noel Eck + * Copyright (c) 2016 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +var sensorObj = require('jsupm_max30100'); + +// Instantiate a MAX30100 instance using bus 0 +var sensor = new sensorObj.MAX30100(0); + +console.log('Oximeter sensor example...'); + +// Read the temperature and version +console.log ('Temperature: %d C', sensor.temperature()); +console.log ('Version: 0x%s', sensor.version().toString(16)); + +// Set high-res (50 Hz, 16-bit) +sensor.high_res_enable(true); + +// Set to sample SpO2 +sensor.mode(sensorObj.MAX30100_MODE_SPO2_EN); + +// Read individual samples +for (var i = 0; i < 10; i++) +{ + var val = sensor.sample(); + console.log('Single value IR: %d R: %d ', val.IR, val.R); +} + +// exit on ^C +process.on('SIGINT', function() +{ + sensor = null; + sensorObj.cleanUp(); + sensorObj = null; + console.log('Exiting.'); + process.exit(0); +}); diff --git a/examples/python/max30100.py b/examples/python/max30100.py new file mode 100755 index 00000000..2a681999 --- /dev/null +++ b/examples/python/max30100.py @@ -0,0 +1,71 @@ +#!/usr/bin/python +# Author: Noel Eck +# Copyright (c) 2016 Intel Corporation. +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +from __future__ import print_function +import time, sys, signal, atexit +from upm import pyupm_max30100 + +# Callback class derived from CXX Callback +class mycallback(pyupm_max30100.Callback): + def __init__(self): + self.count = 0 + pyupm_max30100.Callback.__init__(self) + + def run(self, samp): + print("My callback sample IR: %d R: %d" % (samp.IR, samp.R)) + +def main(): + # Create an instance of the oximiter + # I2C bus 0 + x = pyupm_max30100.MAX30100(0) + + print ('Oximeter sensor example...') + + # Create an instance of the mycallback class + cb = mycallback().__disown__() + + # Read the temperature and version + print ("Temperature: %d C" % x.temperature()) + print ("Version: 0x%04x" % x.version()) + + # Set high-res (50 Hz, 16-bit) + x.high_res_enable(True) + + # Set to sample SpO2 + x.mode(pyupm_max30100.MAX30100_MODE_SPO2_EN); + + # Read continuously, stepping up the LED current every second, + # us GPIO 0 as the interrupt pin + x.sample_continuous(0, False, cb) + for i in range(16): + print("Setting LED current = %d" % i) + x.current(i, i) + time.sleep(1) + + # Read individual samples + for i in range(10): + val = x.sample(); + print("Single value IR: %d R: %d " % (val.IR, val.R)) + +if __name__ == '__main__': + main() diff --git a/src/max30100/CMakeLists.txt b/src/max30100/CMakeLists.txt new file mode 100644 index 00000000..7eb76482 --- /dev/null +++ b/src/max30100/CMakeLists.txt @@ -0,0 +1,9 @@ +upm_mixed_module_init (NAME max30100 + DESCRIPTION "Pulse oximeter and heart-rate sensor" + C_HDR max30100.h + C_SRC max30100.c + CPP_HDR max30100.hpp + CPP_SRC max30100.cxx + FTI_SRC max30100_fti.c + CPP_WRAPS_C + REQUIRES mraa) diff --git a/src/max30100/javaupm_max30100.i b/src/max30100/javaupm_max30100.i new file mode 100644 index 00000000..c370b689 --- /dev/null +++ b/src/max30100/javaupm_max30100.i @@ -0,0 +1,21 @@ +%module(directors="1", threads="1") javaupm_max30100 +%include "../upm.i" + +%{ + #include "max30100.hpp" +%} + +%feature("director") upm::Callback; +%include "max30100_regs.h" +%include "max30100.hpp" + +%pragma(java) jniclasscode=%{ + static { + try { + System.loadLibrary("javaupm_max30100"); + } catch (UnsatisfiedLinkError e) { + System.err.println("Native code library failed to load. \n" + e); + System.exit(1); + } + } +%} diff --git a/src/max30100/jsupm_max30100.i b/src/max30100/jsupm_max30100.i new file mode 100644 index 00000000..edb35335 --- /dev/null +++ b/src/max30100/jsupm_max30100.i @@ -0,0 +1,9 @@ +%module jsupm_max30100 +%include "../upm.i" + +%{ + #include "max30100.hpp" +%} + +%include "max30100_regs.h" +%include "max30100.hpp" diff --git a/src/max30100/max30100.c b/src/max30100/max30100.c new file mode 100644 index 00000000..c4d13ff7 --- /dev/null +++ b/src/max30100/max30100.c @@ -0,0 +1,529 @@ +/* + * Author: Noel Eck + * Copyright (c) 2015 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include +#include +#include +#include + +#include "max30100.h" + +max30100_context* max30100_init(int16_t i2c_bus) +{ + /* Allocate space for the sensor structure */ + max30100_context* dev = (max30100_context*) malloc(sizeof(max30100_context)); + if(dev == NULL) + { + syslog(LOG_CRIT, "%s: malloc() failed\n", __FUNCTION__); + goto max30100_init_fail; + } + + /* Initilize mraa */ + mraa_result_t result = mraa_init(); + if (result != MRAA_SUCCESS) + { + syslog(LOG_ERR, "%s: mraa_init() failed (%d)\n", __FUNCTION__, result); + goto max30100_init_fail; + } + + /* Initialize I2C */ + dev->_i2c_context = mraa_i2c_init(i2c_bus); + if(dev->_i2c_context == NULL) + { + syslog(LOG_ERR, "%s: mraa_i2c_init() failed\n", __FUNCTION__); + goto max30100_init_fail; + } + + /* Set the I2C slave address for this device */ + if (mraa_i2c_address(dev->_i2c_context, MAX30100_I2C_ADDRESS) != MRAA_SUCCESS) + { + syslog(LOG_ERR, "%s: mraa_i2c_address() failed\n", __FUNCTION__); + goto max30100_init_fail; + } + + /* Attempt to run the device at 100kHz */ + if (mraa_i2c_frequency(dev->_i2c_context, MRAA_I2C_STD)) + syslog(LOG_ERR, "%s: mraa_i2c_frequency() failed, device may not function correctly\n", __FUNCTION__); + + /* Start without GPIO */ + dev->_gpio_context = NULL; + + return dev; + + /* Handle all failing cases here */ +max30100_init_fail: + /* Free structure memory if allocated */ + if (dev != NULL) + free(dev); + + return NULL; +} + +void max30100_close(max30100_context* dev) +{ + assert(dev != NULL && "max30100_close: Context cannot be NULL"); + + /* Cleanup the I2C context */ + mraa_i2c_stop(dev->_i2c_context); + free(dev); +} + +static void internal_uninstall_isr(max30100_context* dev) +{ + assert(dev != NULL && "internal_uninstall_isr: Context cannot be NULL"); + + /* If no GPIO context exists, return */ + if (dev->_gpio_context == NULL) return; + + mraa_gpio_isr_exit(dev->_gpio_context); + mraa_gpio_close(dev->_gpio_context); + dev->_gpio_context = NULL; +} + +static upm_result_t _internal_install_isr(max30100_context* dev, int gpio_pin, + void (*isr)(void *), void *arg) +{ + /* Only allow one ISR */ + internal_uninstall_isr(dev); + + if (!(dev->_gpio_context = mraa_gpio_init(gpio_pin))) + return UPM_ERROR_OPERATION_FAILED; + + /* Set the GPIO to input */ + if (mraa_gpio_dir(dev->_gpio_context, MRAA_GPIO_IN) != MRAA_SUCCESS) + return UPM_ERROR_OPERATION_FAILED; + + /* MAX30100 interrupts are active low, pull GPIO high */ + if (mraa_gpio_mode(dev->_gpio_context, MRAA_GPIO_PULLUP) != MRAA_SUCCESS) + return UPM_ERROR_OPERATION_FAILED; + + /* Install the interrupt handler */ + if (mraa_gpio_isr(dev->_gpio_context, MRAA_GPIO_EDGE_FALLING, isr, arg) != MRAA_SUCCESS) + return UPM_ERROR_OPERATION_FAILED; + + return UPM_SUCCESS; +} + +upm_result_t max30100_read(const max30100_context* dev, MAX30100_REG reg, uint8_t* rd_data) +{ + assert(dev != NULL && "max30100_read: Context cannot be NULL"); + /* Read the register */ + int tmp_val = mraa_i2c_read_byte_data(dev->_i2c_context, reg); + if (tmp_val < 0) return UPM_ERROR_OPERATION_FAILED; + + *rd_data = (uint8_t)tmp_val; + + return UPM_SUCCESS; +} + +upm_result_t max30100_write(const max30100_context* dev, MAX30100_REG reg, uint8_t wr_data) +{ + assert(dev != NULL && "max30100_write: Context cannot be NULL"); + + if (mraa_i2c_write_byte_data(dev->_i2c_context, (uint8_t)wr_data, reg) != MRAA_SUCCESS) + return UPM_ERROR_OPERATION_FAILED; + + return UPM_SUCCESS; +} + +upm_result_t max30100_rd_mod_wr(const max30100_context* dev, + MAX30100_REG reg, uint8_t value, uint8_t mask) +{ + uint8_t tmp_val = 0; + + /* Read the register */ + upm_result_t result = max30100_read(dev, reg, &tmp_val); + + if (result != UPM_SUCCESS) return result; + + /* Modify the value, firt clear the bits from mask */ + tmp_val &= (~mask); + /* Make sure the new value doesn't have anything set outside the mask */ + value &= mask; + /* OR in the new value */ + tmp_val |= value; + + /* Write the value back */ + return max30100_write(dev, reg, tmp_val); +} + +upm_result_t max30100_get_version(const max30100_context* dev, uint16_t* version) +{ + assert(dev != NULL && "max30100_get_version: Context cannot be NULL"); + + /* Read the revision ID */ + uint8_t tmp_val = 0; + upm_result_t result = max30100_read(dev, MAX30100_REG_REV_ID, &tmp_val); + if (result != UPM_SUCCESS) return result; + + *version = (uint8_t)tmp_val; + + result = max30100_read(dev, MAX30100_REG_PART_ID, &tmp_val); + if (result != UPM_SUCCESS) return result; + + /* Move the PART ID to upper byte */ + *version += ((uint16_t)tmp_val << 8); + + return UPM_SUCCESS; +} + +upm_result_t max30100_get_temperature(const max30100_context* dev, float* temperature) +{ + assert(dev != NULL && "max30100_get_temperature: Context cannot be NULL"); + int8_t tmp_val = 0; + + /* First, set TEMP_EN to initiate a temperature read */ + upm_result_t result = max30100_rd_mod_wr(dev, MAX30100_REG_MODE_CONFIG, + MAX30100_TEMP_EN, MAX30100_TEMP_EN); + if (result != UPM_SUCCESS) return result; + + /* Note, the docs for reading a temperature value states: + * This is a self-clearing bit which, when set, initiates a single + * temperature reading from the temperature sensor. This bit is + * cleared automatically back to zero at the conclusion of the + * temperature reading when the bit is set to one in heart rate + * or SpO2 mode. + * + * However, the next read of the MODE CONFIG register *always* seems + * to have TEMP_EN cleared w/o values in TINT/TFRAC until a short + * while later. To account for this, a delay has been added - sorry */ + + upm_delay_ms(100); + + /* Read the integer portion of the temperature */ + result = max30100_read(dev, MAX30100_REG_TEMP_INTEGER, (uint8_t*)&tmp_val); + if (result != UPM_SUCCESS) return result; + + /* cast the signed integer portion to float */ + *temperature = (float)tmp_val; + + /* This register stores the fractional temperature data in increments of + * 0.0625C (1/16th of a degree C). If this fractional temperature is + * paired with a negative integer, it still adds as a positive fractional + * value (e.g., -128°C + 0.5°C = -127.5°C). */ + result = max30100_read(dev, MAX30100_REG_TEMP_FRACTION, (uint8_t*)&tmp_val); + if (result != UPM_SUCCESS) return result; + + /* Add the fraction */ + *temperature += ((float)tmp_val)/16.0; + + return UPM_SUCCESS; +} + +upm_result_t max30100_set_mode(const max30100_context* dev, MAX30100_MODE mode) +{ + assert(dev != NULL && "max30100_set_mode: Context cannot be NULL"); + return max30100_rd_mod_wr(dev, MAX30100_REG_MODE_CONFIG, (uint8_t)mode, 0x03); +} + +upm_result_t max30100_get_mode(const max30100_context* dev, MAX30100_MODE* mode) +{ + assert(dev != NULL && "max30100_get_mode: Context cannot be NULL"); + + /* Read the mode configuration register */ + uint8_t data = 0; + upm_result_t result = max30100_read(dev, MAX30100_REG_MODE_CONFIG, &data); + if (result != UPM_SUCCESS) return result; + + *mode = (MAX30100_MODE) data & 0x7; + + return UPM_SUCCESS; +} + +upm_result_t max30100_set_high_res(const max30100_context* dev, bool high_res) +{ + assert(dev != NULL && "MAX30100_set_high_res: Context cannot be NULL"); + uint8_t wr_val = high_res ? MAX30100_SPO2_HI_RES_EN : ~MAX30100_SPO2_HI_RES_EN; + return max30100_rd_mod_wr(dev, MAX30100_REG_SPO2_CONFIG, wr_val, 0x40); +} + +upm_result_t max30100_get_high_res(const max30100_context* dev, bool* high_res) +{ + assert(dev != NULL && "MAX30100_get_high_res: Context cannot be NULL"); + + /* Read the SpO2 configuration register */ + uint8_t data = 0; + upm_result_t result = max30100_read(dev, MAX30100_REG_SPO2_CONFIG, &data); + if (result != UPM_SUCCESS) return result; + + *high_res = data & 0x40; + + return UPM_SUCCESS; +} + +upm_result_t max30100_set_sample_rate(const max30100_context* dev, MAX30100_SR sample_rate) +{ + assert(dev != NULL && "MAX30100_set_sample_rate: Context cannot be NULL"); + return max30100_rd_mod_wr(dev, MAX30100_REG_SPO2_CONFIG, (uint8_t)sample_rate << 2, 0x1c); +} + +upm_result_t max30100_get_sample_rate(const max30100_context* dev, MAX30100_SR* sample_rate) +{ + assert(dev != NULL && "MAX30100_get_high_res: Context cannot be NULL"); + + /* Read the SpO2 configuration register */ + uint8_t data = 0; + upm_result_t result = max30100_read(dev, MAX30100_REG_SPO2_CONFIG, &data); + if (result != UPM_SUCCESS) return result; + + *sample_rate = (MAX30100_SR)((data >> 2) & 0x7); + + return UPM_SUCCESS; +} + +upm_result_t max30100_set_pulse_width(const max30100_context* dev, MAX30100_LED_PW pulse_width) +{ + assert(dev != NULL && "MAX30100_set_pulse_width: Context cannot be NULL"); + return max30100_rd_mod_wr(dev, MAX30100_REG_SPO2_CONFIG, (uint8_t)pulse_width, 0x03); +} + +upm_result_t max30100_get_pulse_width(const max30100_context* dev, MAX30100_LED_PW* pulse_width) +{ + assert(dev != NULL && "MAX30100_get_high_res: Context cannot be NULL"); + + /* Read the SpO2 configuration register */ + uint8_t data = 0; + upm_result_t result = max30100_read(dev, MAX30100_REG_SPO2_CONFIG, &data); + if (result != UPM_SUCCESS) return result; + + *pulse_width = (MAX30100_LED_PW)(data & 0x3); + + return UPM_SUCCESS; +} + +upm_result_t max30100_set_current(const max30100_context* dev, + MAX30100_LED_CURRENT ir, MAX30100_LED_CURRENT r) +{ + assert(dev != NULL && "max30100_set_current: Context cannot be NULL"); + + return max30100_write(dev, MAX30100_REG_LED_CONFIG, + (uint8_t)((r << 4) | r)); +} + +upm_result_t max30100_get_current(const max30100_context* dev, + MAX30100_LED_CURRENT* ir, MAX30100_LED_CURRENT* r) +{ + assert(dev != NULL && "max30100_get_current: Context cannot be NULL"); + + /* Read the LED configuration register */ + uint8_t data = 0; + upm_result_t result = max30100_read(dev, MAX30100_REG_LED_CONFIG, &data); + if (result != UPM_SUCCESS) return result; + + *ir = (MAX30100_LED_CURRENT)(data & 0x0f); + *r = (MAX30100_LED_CURRENT)((data >> 4) & 0x0f); + + return UPM_SUCCESS; +} + +upm_result_t max30100_reset(const max30100_context* dev) +{ + assert(dev != NULL && "max30100_reset: Context cannot be NULL"); + + /* Set the RESET bit, don't worry about read/mod/write */ + return max30100_write(dev, MAX30100_REG_MODE_CONFIG, (uint8_t)0x40); +} + +upm_result_t max30100_sleep(const max30100_context* dev, bool sleep) +{ + assert(dev != NULL && "max30100_sleep: Context cannot be NULL"); + + /* Read/mod/write to set the SHDN bit */ + uint8_t wr_val = sleep ? MAX30100_SHDN : (uint8_t)~MAX30100_SHDN; + return max30100_rd_mod_wr(dev, MAX30100_REG_MODE_CONFIG, wr_val, MAX30100_SHDN); +} + +static upm_result_t _read_single_sample(const max30100_context* dev, max30100_value *samp) +{ + uint8_t data[4]; + if (mraa_i2c_read_bytes_data(dev->_i2c_context, MAX30100_REG_FIFO_DATA, data, 4) != 4) + return UPM_ERROR_OPERATION_FAILED; + + samp->IR = ((uint16_t)data[0] << 8) | data[1]; + samp->R = ((uint16_t)data[2] << 8) | data[3]; + + return UPM_SUCCESS; +} + +static void _internal_sample_rdy(void *arg) +{ + max30100_context* dev = arg; + + if (dev->sample_state == MAX30100_SAMPLE_STATE_IDLE) return; + + int i = 15; + max30100_value samp = {0, 0}; + /* If state is BUFFERED, read 16 samples, else read 1 sample */ + do + { + if (_read_single_sample(dev, &samp) != UPM_SUCCESS) + goto max30100_sample_rdy_fail; + + // Call handler + dev->func_sample_ready(samp, dev->arg); + + } while ((i-- > 0) && (dev->sample_state == MAX30100_SAMPLE_STATE_CONTINUOUS_BUFFERED)); + + /* If a FIFO full interrupt generated this, clear it by reading sts */ + uint8_t tmp; + if (dev->sample_state == MAX30100_SAMPLE_STATE_CONTINUOUS_BUFFERED) + if(max30100_read(dev, MAX30100_REG_INTERRUPT_STATUS, &tmp) != UPM_SUCCESS) + goto max30100_sample_rdy_fail; + + return; + + /* If a failure occurs in this method (which running on a seperate thread, + * log an error in syslog and attempt to stop sampling + * Handle all failing cases here */ + max30100_sample_rdy_fail: + syslog(LOG_CRIT, "%s: _internal_sample_rdy() failed, attempting to stop sampling...\n", + __FUNCTION__); + max30100_sample_stop(dev); + return; +} + +upm_result_t max30100_sample(max30100_context* dev, max30100_value *samp) +{ + assert(dev != NULL && "max30100_sample: Context cannot be NULL"); + + upm_result_t result = UPM_SUCCESS; + + // Disable interrupts + result = max30100_write(dev, MAX30100_REG_INTERRUPT_ENABLE, 0x00); + if (result != UPM_SUCCESS) return result; + + /* Set the state to one-shot */ + dev->sample_state = MAX30100_SAMPLE_STATE_ONE_SHOT; + + /* Clear wr/rd pointers */ + result = max30100_write(dev, MAX30100_REG_FIFO_WR_PTR, 0x00); + if (result != UPM_SUCCESS) return result; + result = max30100_write(dev, MAX30100_REG_FIFO_RD_PTR, 0x00); + if (result != UPM_SUCCESS) return result; + + /* Wait for a sample */ + uint8_t wr_ptr = 0; + int retry = 50; + while ((wr_ptr == 0) && (--retry > 0)) + { + result = max30100_read(dev, MAX30100_REG_FIFO_WR_PTR, &wr_ptr); + if (result != UPM_SUCCESS) return result; + } + + /* Return timeout if retry count is zero */ + if (retry == 0) return UPM_ERROR_TIMED_OUT; + + /* Set the rd ptr to wr ptr to ensure reading the most current sample */ + result = max30100_write(dev, MAX30100_REG_FIFO_RD_PTR, wr_ptr - 1); + if (result != UPM_SUCCESS) return result; + + /* Read the sample */ + if (_read_single_sample(dev, samp) != UPM_SUCCESS) + return UPM_ERROR_OPERATION_FAILED; + + return UPM_SUCCESS; +} + +upm_result_t max30100_sample_continuous(max30100_context* dev, int gpio_pin, + bool buffered, func_sample_ready_handler isr, void* arg) +{ + assert(dev != NULL && "max30100_sample_continuous: Context cannot be NULL"); + uint8_t tmp; + + upm_result_t result = UPM_SUCCESS; + + // Set state to IDLE + dev->sample_state = MAX30100_SAMPLE_STATE_IDLE; + + // Disable interrupts + result = max30100_write(dev, MAX30100_REG_INTERRUPT_ENABLE, 0x00); + if (result != UPM_SUCCESS) return result; + + /* Setup the external callback info */ + dev->func_sample_ready = isr; + dev->arg = arg; + // Register internal callback handler + result = _internal_install_isr(dev, gpio_pin, _internal_sample_rdy, dev); + if (result != UPM_SUCCESS) return result; + + uint8_t tmp_int_en = 0; + if (buffered) + { + dev->sample_state = MAX30100_SAMPLE_STATE_CONTINUOUS_BUFFERED; + + // Set value of interrupt for FIFO_FULL + tmp_int_en = MAX30100_EN_A_FULL; + } + else + { + dev->sample_state = MAX30100_SAMPLE_STATE_CONTINUOUS; + + // Read the mode field from the mode configuration register, + // decide which interrupt to set + result = max30100_read(dev, MAX30100_REG_MODE_CONFIG, &tmp); + if (result != UPM_SUCCESS) return result; + MAX30100_MODE mode = (MAX30100_MODE)(tmp & 0x3); + + // Set value of interrupt for HR or SpO2 + tmp_int_en = mode == MAX30100_MODE_HR_EN ? MAX30100_EN_HR_RDY : + (mode == MAX30100_MODE_SPO2_EN ? MAX30100_EN_SPO2_RDY : 0x00); + } + + /* Clear wr/rd pointers */ + result = max30100_write(dev, MAX30100_REG_FIFO_WR_PTR, 0x00); + if (result != UPM_SUCCESS) return result; + result = max30100_write(dev, MAX30100_REG_FIFO_RD_PTR, 0x00); + if (result != UPM_SUCCESS) return result; + + /* Enable interrupt, either FIFO full, HR only, or SpO2 */ + result = max30100_write(dev, MAX30100_REG_INTERRUPT_ENABLE, tmp_int_en); + if (result != UPM_SUCCESS) return result; + + /* Read the STATUS register to get things moving */ + result = max30100_read(dev, MAX30100_REG_INTERRUPT_STATUS, &tmp); + if (result != UPM_SUCCESS) return result; + + return UPM_SUCCESS; +} + +upm_result_t max30100_sample_stop(max30100_context* dev) +{ + assert(dev != NULL && "max30100_sample_stop: Context cannot be NULL"); + + dev->sample_state = MAX30100_SAMPLE_STATE_IDLE; + + /* Uninstall sampling ISR */ + internal_uninstall_isr(dev); + +// // Disable sampling +// upm_result_t result = max30100_write(dev, MAX30100_REG_MODE_CONFIG, MAX30100_MODE_DISABLED); +// if (result != UPM_SUCCESS) return result; + + // Disable interrupts + upm_result_t result = max30100_write(dev, MAX30100_REG_INTERRUPT_ENABLE, 0); + if (result != UPM_SUCCESS) return result; + + return UPM_SUCCESS; +} + diff --git a/src/max30100/max30100.cxx b/src/max30100/max30100.cxx new file mode 100644 index 00000000..c0507d0a --- /dev/null +++ b/src/max30100/max30100.cxx @@ -0,0 +1,211 @@ +/* + * Author: Noel Eck + * Copyright (c) 2015 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include +#include "max30100.hpp" + +using namespace upm; + +void max30100_throw(std::string func, std::string cmd, upm_result_t result) +{ + throw std::runtime_error(func + ": " + cmd + " failed, " + + "upm_result_t: " + std::to_string(result)); +} + +MAX30100::MAX30100(int16_t i2c_bus) : _dev(max30100_init(i2c_bus)) +{ + if (_dev == NULL) + throw std::runtime_error(std::string(__FUNCTION__) + + ": failed to initialize sensor, check syslog"); +} + +void _read_sample_proxy(max30100_value sample, void* _max30100) +{ + if ((_max30100 != NULL) && ((MAX30100*)_max30100)->_callback != NULL) + ((MAX30100*)_max30100)->_callback->run(sample); +} + +max30100_value MAX30100::sample() +{ + max30100_value retval; + upm_result_t result = max30100_sample(_dev, &retval); + if (result != UPM_SUCCESS) + max30100_throw(__FUNCTION__, "max30100_sample", result); + return retval; +} + + +void MAX30100::sample_continuous(int gpio_pin, bool buffered, Callback *cb) +{ + // Use a default callback if one is NOT provided + if (cb == NULL) + _callback = (Callback *)&_default_callback; + else + _callback = cb; + max30100_sample_continuous(_dev, gpio_pin, buffered, &_read_sample_proxy, this); +} + +void MAX30100::sample_stop() +{ + upm_result_t result = max30100_sample_stop(_dev); + if (result != UPM_SUCCESS) + max30100_throw(__FUNCTION__, "max30100_sample_stop" , result); +} + +uint16_t MAX30100::version() +{ + uint16_t retval; + upm_result_t result = max30100_get_version(_dev, &retval); + if (result != UPM_SUCCESS) + max30100_throw(__FUNCTION__, "max30100_get_version" , result); + return retval; +} + +float MAX30100::temperature() +{ + float retval; + upm_result_t result = max30100_get_temperature(_dev, &retval); + if (result != UPM_SUCCESS) + max30100_throw(__FUNCTION__, "max30100_get_temperature", result); + return retval; +} + +void MAX30100::mode(MAX30100_MODE mode) +{ + upm_result_t result = max30100_set_mode(_dev, mode); + if (result != UPM_SUCCESS) + max30100_throw(__FUNCTION__, "max30100_set_mode", result); +} + +MAX30100_MODE MAX30100::mode() +{ + MAX30100_MODE mode; + upm_result_t result = max30100_get_mode(_dev, &mode); + if (result != UPM_SUCCESS) + max30100_throw(__FUNCTION__, "max30100_get_mode", result); + return mode; +} + +void MAX30100::high_res_enable(bool enable) +{ + upm_result_t result = max30100_set_high_res(_dev, enable); + if (result != UPM_SUCCESS) + max30100_throw(__FUNCTION__, "max30100_set_high_res", result); +} + +bool MAX30100::high_res_enable() +{ + bool enabled; + upm_result_t result = max30100_get_high_res(_dev, &enabled); + if (result != UPM_SUCCESS) + max30100_throw(__FUNCTION__, "max30100_get_high_res", result); + return enabled; +} + +void MAX30100::sample_rate(MAX30100_SR sample_rate) +{ + upm_result_t result = max30100_set_sample_rate(_dev, sample_rate); + if (result != UPM_SUCCESS) + max30100_throw(__FUNCTION__, "max30100_set_sample_rate", result); +} + +MAX30100_SR MAX30100::sample_rate() +{ + MAX30100_SR sample_rate; + upm_result_t result = max30100_get_sample_rate(_dev, &sample_rate); + if (result != UPM_SUCCESS) + max30100_throw(__FUNCTION__, "max30100_get_sample_rate", result); + return sample_rate; +} + +void MAX30100::pulse_width(MAX30100_LED_PW pulse_width) +{ + upm_result_t result = max30100_set_pulse_width(_dev, pulse_width); + if (result != UPM_SUCCESS) + max30100_throw(__FUNCTION__, "max30100_set_pulse_width", result); +} + +MAX30100_LED_PW MAX30100::pulse_width() +{ + MAX30100_LED_PW pulse_width; + upm_result_t result = max30100_get_pulse_width(_dev, &pulse_width); + if (result != UPM_SUCCESS) + max30100_throw(__FUNCTION__, "max30100_get_pulse_width", result); + return pulse_width; +} + +void MAX30100::current(MAX30100_LED_CURRENT ir, MAX30100_LED_CURRENT r) +{ + upm_result_t result = max30100_set_current(_dev, ir, r); + if (result != UPM_SUCCESS) + max30100_throw(__FUNCTION__, "set_current", result); +} + +MAX30100_LED_CURRENT MAX30100::current_ir() +{ + MAX30100_LED_CURRENT ir, r; + upm_result_t result = max30100_get_current(_dev, &ir, &r); + if (result != UPM_SUCCESS) + max30100_throw(__FUNCTION__, "get_current_ir", result); + return ir; +} + +MAX30100_LED_CURRENT MAX30100::current_r() +{ + MAX30100_LED_CURRENT ir, r; + upm_result_t result = max30100_get_current(_dev, &ir, &r); + if (result != UPM_SUCCESS) + max30100_throw(__FUNCTION__, "get_current_r", result); + return r; +} + +void MAX30100::reset() +{ + upm_result_t result = max30100_reset(_dev); + if (result != UPM_SUCCESS) + max30100_throw(__FUNCTION__, "max30100_reset", result); +} + +uint8_t MAX30100::read(MAX30100_REG reg) +{ + uint8_t retval; + upm_result_t result = max30100_read(_dev, reg, &retval); + if (result != UPM_SUCCESS) + max30100_throw(__FUNCTION__, "max30100_read", result); + return retval; +} + +void MAX30100::write(MAX30100_REG reg, uint8_t value) +{ + upm_result_t result = max30100_write(_dev, reg, value); + if (result != UPM_SUCCESS) + max30100_throw(__FUNCTION__, "max30100_write", result); +} + +void MAX30100::sleep(bool sleep) +{ + upm_result_t result = max30100_sleep(_dev, sleep); + if (result != UPM_SUCCESS) + max30100_throw(__FUNCTION__, "max30100_sleep", result); +} diff --git a/src/max30100/max30100.h b/src/max30100/max30100.h new file mode 100644 index 00000000..8f5fad29 --- /dev/null +++ b/src/max30100/max30100.h @@ -0,0 +1,318 @@ +/* + * Author: Noel Eck + * Copyright (c) 2015 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#pragma once + +#include +#include + +#include "max30100_regs.h" + +#include "mraa/gpio.h" +#include "mraa/i2c.h" +#include "upm.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @file max30100.h + * @library max30100 + * @brief C API for the Pulse oximeter and heart-rate sensor. + * + * @include max30100.c + */ + +/** + * device context + */ +typedef struct { + /* mraa I2C context */ + mraa_i2c_context _i2c_context; + + /* mraa gpio context (for interrupt) */ + mraa_gpio_context _gpio_context; + + /* Sensor acquire mode */ + MAX30100_SAMPLE_STATE sample_state; + + /* Continuous sampling function ptr */ + func_sample_ready_handler func_sample_ready; + + /* Optional void ptr arg returned from callback */ + void* arg; +} max30100_context; + +/** + * Initialize sensor. Note, the MAX30100 I2C address is set to 0x57. + * + * @param i2c_bus Target I2C bus + * @return sensor context pointer + */ +max30100_context* max30100_init(int16_t i2c_bus); + +/** + * Sensor close method. + * + * Cleans up any memory held by this device + * @param dev Sensor context pointer + */ +void max30100_close(max30100_context* dev); + +/** + * Sample a single set of infrared/red values + * + * Note, all setup (sample rate, LED current, and pulse width must be done + * prior to calling this sample method. + * + * @param dev Sensor context pointer + * @param samp IR/R values are returned in this structure + * @return Function result code + */ +upm_result_t max30100_sample(max30100_context* dev, max30100_value *samp); + +/** + * Continuously sample Infrared/Red values. + * + * This method requires a GPIO pin which is used to signal + * sample/samples ready. The INT * pin is open-drain and requires a + * pullup resistor. The interrupt pin is not designed to sink large + * currents, so the pull-up resistor value should be large, such as + * 4.7k ohm. The RCWL-0530 PCB which this library was designed with + * had the I2C lines and INT pin pulled up to 1.8v. + * + * Note, all setup (sample rate, mode, LED current, and pulse width + * must be done prior to calling this sample method. + * + * @param dev Sensor context pointer + * @param gpio_pin GPIO pin used for interrupt (input from sensor INT pin) + * @param buffered Enable buffered sampling. In buffered sampling mode, the + * device reads 16 samples at a time. This can help with I2C read timing. + * buffered == true, enable buffered sampling + * buffered == false, single-sample mode + * @param isr Function pointer which handles 1 IR/R sample and a void ptr arg + * @param arg Void * passed back with ISR call + * @return Function result code + */ +upm_result_t max30100_sample_continuous(max30100_context* dev, + int gpio_pin, + bool buffered, + func_sample_ready_handler isr, + void* arg); + +/** + * Stop continuous sampling. Disable interrupts. + * + * @param dev Sensor context pointer + * @return Function result code + */ +upm_result_t max30100_sample_stop(max30100_context* dev); + +/** + * Read Oximeter and heart-rate sensor register + * + * @param dev Sensor context pointer + * @param reg Target register + * @param rd_data Data from sensor + * @return Function result code + */ +upm_result_t max30100_read(const max30100_context* dev, MAX30100_REG reg, uint8_t* rd_data); + +/** + * Write Oximeter and heart-rate sensor register + * + * @param dev Sensor context pointer + * @param reg Target register to write + * @param wr_data Target data to write + * @return Function result code + */ +upm_result_t max30100_write(const max30100_context* dev, MAX30100_REG reg, uint8_t wr_data); + +/** + * Read modify write Oximeter and heart-rate sensor register + * + * @param dev Sensor context pointer + * @param reg Target register + * @param value Target bits to set/clear + * @param mask Specify the bits to set/clear + * If mask = 0xf0, read full byte, modify only the upper 4 bits + * If mask = 0xaa, read full byte, modify every other bit + * @return Function result code + */ +upm_result_t max30100_rd_mod_wr(const max30100_context* dev, + MAX30100_REG reg, uint8_t value, uint8_t mask); + +/** + * Get sensor version + * Sensor version is a 2 byte value: + * upper byte = PART ID + * lower byte = REVISION ID + * + * example: + * version() return 0x1105 + * 0x11 = PART ID + * 0x05 = REVISION + * + * @param dev Sensor context pointer + * @param rd_data Sensor version + * @return Function result code + */ +upm_result_t max30100_get_version(const max30100_context* dev, uint16_t* version); + +/** + * Get temperature reading from device + * @param dev Sensor context pointer + * @param rd_data Temperature in degrees Celsius + * @return Function result code + */ +upm_result_t max30100_get_temperature(const max30100_context* dev, float* temperature); + +/** + * Set the sampling mode (none vs red only vs SpO2) + * + * @param dev Sensor context pointer + * @param mode Sensor mode value to write into the mode configuration register + * @return Function result code + */ +upm_result_t max30100_set_mode(const max30100_context* dev, MAX30100_MODE mode); + +/** + * Get the mode field from the mode configuration register + * + * @param dev Sensor context pointer + * @param mode Sensor mode value read from mode configuration register + * @return Function result code + */ +upm_result_t max30100_get_mode(const max30100_context* dev, MAX30100_MODE* mode); + +/** + * Set the high-res field in the SpO2 configuration register + * + * @param dev Sensor context pointer + * @param high_res Sensor high-res value to write into the SpO2 configuration register + * @return Function result code + */ +upm_result_t max30100_set_high_res(const max30100_context* dev, bool high_res); + +/** + * Get the high-res field from the SpO2 configuration register + * + * @param dev Sensor context pointer + * @param high_res Sensor high_res value read from the SpO2 configuration register + * @return Function result code + */ +upm_result_t max30100_get_high_res(const max30100_context* dev, bool* high_res); + +/** + * Set the sample rate field in the SpO2 configuration register + * + * @param dev Sensor context pointer + * @param sample_rate Sensor sample rate value to write into the SpO2 configuration register + * @return Function result code + */ +upm_result_t max30100_set_sample_rate(const max30100_context* dev, MAX30100_SR sample_rate); + +/** + * Get the sample rate field from the SpO2 configuration register + * + * @param dev Sensor context pointer + * @param sample_rate Sensor sample rate value read from the SpO2 configuration register + * @return Function result code + */ +upm_result_t max30100_get_sample_rate(const max30100_context* dev, MAX30100_SR* sample_rate); + +/** + * Set the pulse width field in the SpO2 configuration register + * + * @param dev Sensor context pointer + * @param pulse_width Sensor pulse width value to write into the SpO2 configuration register + * @return Function result code + */ +upm_result_t max30100_set_pulse_width(const max30100_context* dev, MAX30100_LED_PW pulse_width); + +/** + * Get the pulse width field from the SpO2 configuration register + * + * @param dev Sensor context pointer + * @param pulse_width Sensor pulse width value read from the SpO2 configuration register + * @return Function result code + */ +upm_result_t max30100_get_pulse_width(const max30100_context* dev, MAX30100_LED_PW* pulse_width); + +/** + * Set the LED current + * + * @param dev Sensor context pointer + * @param ir Infrared LED current enum + * @param r Red LED current enum + * @return Function result code + */ +upm_result_t max30100_set_current(const max30100_context* dev, + MAX30100_LED_CURRENT ir, + MAX30100_LED_CURRENT r); + +/** + * Get the LED current + * + * @param dev Sensor context pointer + * @param ir Infrared LED current read from the LED configuration register + * @param r Red LED current read from the LED configuration register + * @return Function result code + */ +upm_result_t max30100_get_current(const max30100_context* dev, + MAX30100_LED_CURRENT* ir, + MAX30100_LED_CURRENT* r); + +/** + * Reset sensor + * + * When the RESET bit is set to one, all configuration, threshold, + * and data registers are reset to their power-on-state. The only + * exception is writing both RESET and TEMP_EN bits to one at the + * same time since temperature data registers 0x16 and 0x17 are not + * cleared. The RESET bit is cleared automatically back to zero after + * the reset sequence is completed. + * + * @param dev Sensor context pointer + * @return Function result code + */ +upm_result_t max30100_reset(const max30100_context* dev); + +/** + * Put device into power-save mode. While in power-save mode, all + * registers retain their values, and write/read operations function + * as normal. All interrupts are cleared to zero in this mode. + * + * @param dev Sensor context pointer + * @param sleep Enter/exit power-save mode + * true = Enter power-save mode + * false = Exit power-save mode + * @return Function result code + */ +upm_result_t max30100_sleep(const max30100_context* dev, bool sleep); + +#ifdef __cplusplus +} +#endif diff --git a/src/max30100/max30100.hpp b/src/max30100/max30100.hpp new file mode 100644 index 00000000..806f67d6 --- /dev/null +++ b/src/max30100/max30100.hpp @@ -0,0 +1,295 @@ +/* + * Author: Noel Eck + * Copyright (c) 2015 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#pragma once + +#include + +#include "mraa/i2c.h" +#include "max30100.h" + +namespace upm { + +/* Callback class for continuously reading samples */ +class Callback { + public: + virtual ~Callback() { } + /* Default run method, called for each new sample in continous + * sampling mode. + * Override this method */ + virtual void run(max30100_value samp) + { std::cout << "Base sample IR: " << samp.IR << " R: " << samp.R << std::endl; } +}; + +/** + * @brief Pulse oximeter and heart-rate sensor + * @defgroup max30100 libupm-max30100 + * @ingroup maxim i2c medical + */ + +/** + * @library max30100 + * @sensor max30100 + * @comname Pulse Oximeter and Heart-rate Sensor + * @type medical + * @man maxim + * @web https://www.maximintegrated.com/en/products/analog/sensors-and-sensor-interface/MAX30100.html + * @con i2c gpio + * + * @brief API for the Pulse oximeter and heart-rate sensor + * + * The MAX30100 is an integrated pulse oximetry and heartrate monitor sensor + * solution. It combines two LEDs, a photodetector, optimized optics, and + * low-noise analog signal processing to detect pulse oximetry and heart-rate + * signals. + * + * I2C sensor which can be used to read: + * Heart-rate + * Peripheral capillary oxygen saturation + * temperature + * + * @image html max30100.png + * @snippet max30100.cxx Interesting + */ + +class MAX30100 { + public: + /** + * Oximeter and heart-rate sensor constructor + * + * Initialize Oximeter and heart-rate sensor. Note, the I2C address + * is 0x57. + * @param i2c_bus Target I2C bus + * @return sensor context pointer + * @throws std::runtime_error if sensor initializate fails + */ + MAX30100(int16_t i2c_bus); + + /** + * MAX30100 destructor + */ + virtual ~MAX30100() {}; + + /** + * Sample a single set of infrared/red values + * + * Note, all setup (sample rate, LED current, and pulse width must be done + * prior to calling this sample method. + * + * @return One IR/R sample + * @throws std::runtime_error on I2C command failure + */ + max30100_value sample(); + + /** + * Continuously sample Infrared/Red values. + * + * This method requires a GPIO pin which is used to signal + * sample/samples ready. The INT * pin is open-drain and requires a + * pullup resistor. The interrupt pin is not designed to sink large + * currents, so the pull-up resistor value should be large, such as + * 4.7k ohm. The RCWL-0530 PCB which this library was designed with + * had the I2C lines and INT pin pulled up to 1.8v. + * + * Note, all setup (sample rate, mode, LED current, and pulse width + * must be done prior to calling this sample method. + * + * @param gpio_pin GPIO pin for interrupt (input from sensor INT pin) + * @param buffered Enable buffered sampling. In buffered sampling mode, + * the device reads 16 samples at a time. This can help with I2C read + * timing. + * buffered == true, enable buffered sampling + * buffered == false, single-sample mode + * @param cb Pointer to instance of Callback class. If parameter is left + * NULL, a default instance of the Callback class will be used which + * prints out the IR/R values. + * @throws std::runtime_error on I2C command failure + */ + void sample_continuous(int gpio_pin, bool buffered, Callback *cb = NULL); + + /** + * Stop continuous sampling. Disable interrupts. + */ + void sample_stop(); + + /** + * Read Oximeter and heart-rate sensor registers + * @param reg Target register to read + * @return Data returned from sensor + * @throws std::runtime_error if I2C read command fails + */ + uint8_t read(MAX30100_REG reg); + + /** + * Write Oximeter and heart-rate sensor registers + * @param reg Target register to write + * @param wr_data Target data to write + * @throws std::runtime_error if I2C write command fails + */ + void write(MAX30100_REG reg, uint8_t wr_data); + + /** + * Get sensor version + * Sensor version is a 2 byte value: + * upper byte = PART ID + * lower byte = REVISION ID + * + * example: + * version() return 0x1105 + * 0x11 = PART ID + * 0x05 = REVISION + * @return Sensor version + * @throws std::runtime_error on I2C command failure + */ + uint16_t version(); + + /** + * Get temperature reading from device + * @return rd_data Temperature in degrees Celsius + * @throws std::runtime_error on I2C command failure + */ + float temperature(); + + /** + * Set the sampling mode (none vs red only vs SpO2) + * + * @param mode Target sampling mode + * @throws std::runtime_error on I2C command failure + */ + void mode(MAX30100_MODE mode); + + /** + * Get the sampling mode + * + * @return Current sampling mode + * @throws std::runtime_error on I2C command failure + */ + MAX30100_MODE mode(); + + /** + * Enable or disable high-resolution mode + * + * @param enable High-resolution enable + * true == SpO2 ADC resolution of 16 bit with 1.6ms LED pw + * @throws std::runtime_error on I2C command failure + */ + void high_res_enable(bool enable); + + /** + * Get the high-resolution enable bit + * + * @return Current high-resolution bit value + * @throws std::runtime_error on I2C command failure + */ + bool high_res_enable(); + + /** + * Set the sample rate + * + * @param sample_rate Target sample rate + * @throws std::runtime_error on I2C command failure + */ + void sample_rate(MAX30100_SR sample_rate); + + /** + * Get the sample rate + * + * @return Current sample rate + * @throws std::runtime_error on I2C command failure + */ + MAX30100_SR sample_rate(); + + /** + * Set the LED pulse width + * + * @param pulse_width Target LED pulse width + * @throws std::runtime_error on I2C command failure + */ + void pulse_width(MAX30100_LED_PW pulse_width); + + /** + * Get the LED pulse width + * + * @return Current LED pulse width + * @throws std::runtime_error on I2C command failure + */ + MAX30100_LED_PW pulse_width(); + + /** + * Set the current for the infrared and red LEDs + * + * @param ir LED current enum + * @param r LED current enum + * @throws std::runtime_error on I2C command failure + */ + void current(MAX30100_LED_CURRENT ir, MAX30100_LED_CURRENT r); + + /** + * Get the infrared LED current + * + * @throws std::runtime_error on I2C command failure + */ + MAX30100_LED_CURRENT current_ir(); + + /** + * Get the red LED current + * + * @throws std::runtime_error on I2C command failure + */ + MAX30100_LED_CURRENT current_r(); + + /** + * Reset sensor + * + * When the RESET bit is set to one, all configuration, threshold, + * and data registers are reset to their power-on-state. The only + * exception is writing both RESET and TEMP_EN bits to one at the + * same time since temperature data registers 0x16 and 0x17 are not + * cleared. The RESET bit is cleared automatically back to zero after + * the reset sequence is completed. + * + * @throws std::runtime_error on I2C command failure + */ + void reset(); + + /** + * Put device into power-save mode. While in power-save mode, all + * registers retain their values, and write/read operations function + * as normal. All interrupts are cleared to zero in this mode. + * + * @param sleep Enter/exit power-save mode + * @throws std::runtime_error on I2C command failure + */ + void sleep(bool sleep); + + /* Callback pointer available for a user-specified callback */ + Callback *_callback; + private: + /* base Callback instance to use if none provided */ + Callback _default_callback; + + /* device context struct */ + max30100_context* _dev; +}; +} diff --git a/src/max30100/max30100_fti.c b/src/max30100/max30100_fti.c new file mode 100644 index 00000000..4bdf4edd --- /dev/null +++ b/src/max30100/max30100_fti.c @@ -0,0 +1,109 @@ +/* + * Author: Noel Eck + * Copyright (c) 2015 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include +#include + +#include "max30100.h" +#include "upm_fti.h" +#include "fti/upm_sensor.h" + +/** + * This file implements the Function Table Interface (FTI) for this sensor + */ + +const char upm_max30100_name[] = "MAX30100"; +const char upm_max30100_description[] = "Pulse oximeter and heart-rate sensor"; +const upm_protocol_t upm_max30100_protocol[] = {UPM_I2C}; +/* TODO: Add/implement heart rate and SpO2 categories */ +const upm_sensor_t upm_max30100_category[] = {UPM_TEMPERATURE}; + +// forward declarations +const void* upm_max30100_get_ft(upm_sensor_t sensor_type); +void* upm_max30100_init_str(const char* protocol, const char* params); +void upm_max30100_close(void* dev); +const upm_sensor_descriptor_t upm_max30100_get_descriptor(); +upm_result_t upm_max30100_get_temperature(void* dev, float *value, upm_temperature_u unit); + +/* This sensor implementes 2 function tables */ +/* 1. Generic base function table */ +static const upm_sensor_ft ft_gen = +{ + .upm_sensor_init_name = &upm_max30100_init_str, + .upm_sensor_close = &upm_max30100_close, + .upm_sensor_get_descriptor = &upm_max30100_get_descriptor +}; + +/* 2. Temperatur function table */ +static const upm_temperature_ft ft_temperature = +{ + .upm_temperature_set_offset = NULL, + .upm_temperature_set_scale = NULL, + .upm_temperature_get_value = &upm_max30100_get_temperature +}; + +const void* upm_max30100_get_ft(upm_sensor_t sensor_type) +{ + switch(sensor_type) + { + case UPM_SENSOR: + return &ft_gen; + case UPM_TEMPERATURE: + return &ft_temperature; + default: + return NULL; + } +} + +void* upm_max30100_init_str(const char* protocol, const char* params) +{ + fprintf(stderr, + "String initialization - not implemented, using i2c bus 0: %s\n", __FILENAME__); + return max30100_init(0); +} + +void upm_max30100_close(void* dev) +{ + max30100_close(dev); +} + +const upm_sensor_descriptor_t upm_max30100_get_descriptor() +{ + /* Fill in the descriptor */ + upm_sensor_descriptor_t usd; + usd.name = upm_max30100_name; + usd.description = upm_max30100_description; + usd.protocol_size = 1; + usd.protocol = upm_max30100_protocol; + usd.category_size = 2; + usd.category = upm_max30100_category; + + return usd; +} + +upm_result_t upm_max30100_get_temperature(void* dev, float *value, upm_temperature_u unit) +{ + upm_result_t result = max30100_get_temperature((max30100_context*)dev, value); + return result; +} diff --git a/src/max30100/max30100_regs.h b/src/max30100/max30100_regs.h new file mode 100644 index 00000000..6bf35f23 --- /dev/null +++ b/src/max30100/max30100_regs.h @@ -0,0 +1,170 @@ +/* + * Author: Noel Eck + * Copyright (c) 2015 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#pragma once + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define MAX30100_I2C_ADDRESS 0x57 + +/* Single IR/R sample */ +typedef struct { + /* Raw IR (pulse) read value */ + uint16_t IR; + /* Raw R (O2) read value */ + uint16_t R; +} max30100_value; + +/* Function pointer for returning 1 IR/R sample */ +typedef void (*func_sample_ready_handler)(max30100_value sample, void* arg); + +/* Sample state */ +typedef enum { + /* NOT sampling */ + MAX30100_SAMPLE_STATE_IDLE, + /* Take one sample/currently taking one sample */ + MAX30100_SAMPLE_STATE_ONE_SHOT, + /* Sample continuously/currently sampling continuously */ + MAX30100_SAMPLE_STATE_CONTINUOUS, + /* Sample continuously using buffer/currently sampling continuously using buffer*/ + MAX30100_SAMPLE_STATE_CONTINUOUS_BUFFERED +} MAX30100_SAMPLE_STATE; + +/* Pulse oximeter and heart-rate sensor I2C registers */ +typedef enum { + /* Interrupt status (RO) */ + MAX30100_REG_INTERRUPT_STATUS = 0x00, + /* Interrupt enable */ + MAX30100_REG_INTERRUPT_ENABLE = 0x01, + /* FIFO write pointer */ + MAX30100_REG_FIFO_WR_PTR = 0x02, + /* FIFO overflow counter */ + MAX30100_REG_FIFO_OVF_COUNTER = 0x03, + /* FIFO read pointer */ + MAX30100_REG_FIFO_RD_PTR = 0x04, + /* FIFO data */ + MAX30100_REG_FIFO_DATA = 0x05, + /* Mode configuration */ + MAX30100_REG_MODE_CONFIG = 0x06, + /* SPO2 configuration */ + MAX30100_REG_SPO2_CONFIG = 0x07, + /* LED configuration */ + MAX30100_REG_LED_CONFIG = 0x09, + /* Temperature integer (2's compliment) */ + MAX30100_REG_TEMP_INTEGER = 0x16, + /* Temperature fraction) */ + MAX30100_REG_TEMP_FRACTION = 0x17, + /* Revision ID (RO)*/ + MAX30100_REG_REV_ID = 0xFE, + /* Part ID */ + MAX30100_REG_PART_ID = 0xFF +} MAX30100_REG; + +/* MAX30100_REG_INTERRUPT_STATUS register fields */ +/* FIFO almost full, set to 1 when WR_PTR == RD_PTR - 1 */ +#define MAX30100_A_FULL (1 << 7) +/* Temperature date ready flag */ +#define MAX30100_TEMP_RDY (1 << 6) +/* Heartrate data ready flag */ +#define MAX30100_HR_RDY (1 << 5) +/* HR and O2 data ready flag */ +#define MAX30100_SPO2_RDY (1 << 4) +/* Power ready after brownout flag */ +#define MAX30100_PWR_RDY (1 << 0) + +/* MAX30100_REG_INTERRUPT_ENABLE register fields */ +/* Enable interrupt on FIFO almost full */ +#define MAX30100_EN_A_FULL (1 << 7) +/* Enable interrupt on temperature date ready */ +#define MAX30100_EN_TEMP_RDY (1 << 6) +/* Enable interrupt on HR data ready */ +#define MAX30100_EN_HR_RDY (1 << 5) +/* Enable interrupt on HR and O2 data ready */ +#define MAX30100_EN_SPO2_RDY (1 << 4) + +/* MAX30100_REG_MODE_CONFIG register fields */ +/* Enable power-save mode */ +#define MAX30100_SHDN (1 << 7) +/* Reset device */ +#define MAX30100_RESET (1 << 6) +/* Initiate temperature reading */ +#define MAX30100_TEMP_EN (1 << 3) +/* Device sample mode (HR, vs SpO2) */ +typedef enum _MAX30100_MODE { +/* Turn off sampling */ + MAX30100_MODE_DISABLED = 0x00, +/* Enable heartrate ONLY sampling */ + MAX30100_MODE_HR_EN = 0x02, +/* Enable SpO2 sampling */ + MAX30100_MODE_SPO2_EN = 0x03 +} MAX30100_MODE; + +/* MAX30100_REG_SPO2_CONFIG register fields */ +#define MAX30100_SPO2_HI_RES_EN (1 << 6) +typedef enum _MAX30100_SR { + MAX30100_SR_50_HZ = 0x00, + MAX30100_SR_100_HZ = 0x01, + MAX30100_SR_167_HZ = 0x02, + MAX30100_SR_200_HZ = 0x03, + MAX30100_SR_400_HZ = 0x04, + MAX30100_SR_600_HZ = 0x05, + MAX30100_SR_900_HZ = 0x06, + MAX30100_SR_1000_HZ = 0x07 +} MAX30100_SR; +/* LED pulse width (microseconds) */ +typedef enum _MAX30100_LED_PW { + MAX30100_LED_PW_200_US_13_BITS = 0x00, + MAX30100_LED_PW_400_US_14_BITS = 0x01, + MAX30100_LED_PW_800_US_15_BITS = 0x02, + MAX30100_LED_PW_1600_US_16_BITS = 0x03 +} MAX30100_LED_PW; + +/* MAX30100_REG_LED_CONFIG register fields */ +/* LED (IR and R) current (milliamps) */ +typedef enum _MAX30100_LED_CURRENT { + MAX30100_LED_CURRENT_0_0_MA = 0x00, + MAX30100_LED_CURRENT_4_4_MA = 0x01, + MAX30100_LED_CURRENT_7_6_MA = 0x02, + MAX30100_LED_CURRENT_11_0_MA = 0x03, + MAX30100_LED_CURRENT_14_2_MA = 0x04, + MAX30100_LED_CURRENT_17_4_MA = 0x05, + MAX30100_LED_CURRENT_20_8_MA = 0x06, + MAX30100_LED_CURRENT_24_0_MA = 0x07, + MAX30100_LED_CURRENT_27_1_MA = 0x08, + MAX30100_LED_CURRENT_30_6_MA = 0x09, + MAX30100_LED_CURRENT_33_8_MA = 0x0a, + MAX30100_LED_CURRENT_37_0_MA = 0x0b, + MAX30100_LED_CURRENT_40_2_MA = 0x0c, + MAX30100_LED_CURRENT_43_6_MA = 0x0d, + MAX30100_LED_CURRENT_46_8_MA = 0x0e, + MAX30100_LED_CURRENT_50_0_MA = 0x0f +} MAX30100_LED_CURRENT; + +#ifdef __cplusplus +} +#endif diff --git a/src/max30100/pyupm_max30100.i b/src/max30100/pyupm_max30100.i new file mode 100644 index 00000000..8d5bef55 --- /dev/null +++ b/src/max30100/pyupm_max30100.i @@ -0,0 +1,14 @@ +// Include doxygen-generated documentation +%module(directors="1", threads="1") pyupm_max30100 +%include "pyupm_doxy2swig.i" +%include "../upm.i" + +%feature("autodoc", "3"); + +%{ + #include "max30100.hpp" +%} + +%feature("director") upm::Callback; +%include "max30100_regs.h" +%include "max30100.hpp"