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 <noel.eck@intel.com>
This commit is contained in:
Noel Eck 2016-12-08 15:24:36 -08:00
parent 6ea65a16a4
commit bd47b9ed45
19 changed files with 2133 additions and 0 deletions

BIN
docs/images/max30100.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 106 KiB

View File

@ -328,6 +328,7 @@ add_example (mb704x)
add_example (rf22-server)
add_example (rf22-client)
add_example (mcp2515)
add_example (max30100)
# These are special cases where you specify example binary, source file and module(s)
include_directories (${PROJECT_SOURCE_DIR}/src)

99
examples/c++/max30100.cxx Normal file
View File

@ -0,0 +1,99 @@
/*
* Author: Noel Eck <noel.eck@intel.com>
* 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 <unistd.h>
#include <iostream>
#include <signal.h>
#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;
}

View File

@ -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)

130
examples/c/max30100.c Normal file
View File

@ -0,0 +1,130 @@
/*
* Author: Noel Eck <noel.eck@intel.com>
* 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 <unistd.h>
#include <stdio.h>
#include <signal.h>
#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;
}

View File

@ -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)

View File

@ -0,0 +1,88 @@
/*
* Author: Noel Eck <noel.eck@intel.com>
* 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());
}
}

View File

@ -0,0 +1,57 @@
/*
* Author: Noel Eck <noel.eck@intel.com>
* 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);
});

71
examples/python/max30100.py Executable file
View File

@ -0,0 +1,71 @@
#!/usr/bin/python
# Author: Noel Eck <noel.eck@intel.com>
# 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()

View File

@ -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)

View File

@ -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);
}
}
%}

View File

@ -0,0 +1,9 @@
%module jsupm_max30100
%include "../upm.i"
%{
#include "max30100.hpp"
%}
%include "max30100_regs.h"
%include "max30100.hpp"

529
src/max30100/max30100.c Normal file
View File

@ -0,0 +1,529 @@
/*
* Author: Noel Eck <noel.eck@intel.com>
* 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 <assert.h>
#include <stdlib.h>
#include <syslog.h>
#include <math.h>
#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;
}

211
src/max30100/max30100.cxx Normal file
View File

@ -0,0 +1,211 @@
/*
* Author: Noel Eck <noel.eck@intel.com>
* 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 <stdexcept>
#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);
}

318
src/max30100/max30100.h Normal file
View File

@ -0,0 +1,318 @@
/*
* Author: Noel Eck <noel.eck@intel.com>
* 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 <errno.h>
#include <stdint.h>
#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

295
src/max30100/max30100.hpp Normal file
View File

@ -0,0 +1,295 @@
/*
* Author: Noel Eck <noel.eck@intel.com>
* 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 <iostream>
#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;
};
}

109
src/max30100/max30100_fti.c Normal file
View File

@ -0,0 +1,109 @@
/*
* Author: Noel Eck <noel.eck@intel.com>
* 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 <string.h>
#include <stdlib.h>
#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;
}

View File

@ -0,0 +1,170 @@
/*
* Author: Noel Eck <noel.eck@intel.com>
* 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 <stdint.h>
#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

View File

@ -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"