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

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"