mirror of
https://github.com/eclipse/upm.git
synced 2025-03-15 04:57:30 +03:00
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:
parent
6ea65a16a4
commit
bd47b9ed45
BIN
docs/images/max30100.png
Normal file
BIN
docs/images/max30100.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 106 KiB |
@ -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
99
examples/c++/max30100.cxx
Normal 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;
|
||||
}
|
@ -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
130
examples/c/max30100.c
Normal 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;
|
||||
}
|
@ -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)
|
||||
|
88
examples/java/MAX30100_Example.java
Normal file
88
examples/java/MAX30100_Example.java
Normal 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());
|
||||
}
|
||||
}
|
||||
|
57
examples/javascript/max30100.js
Normal file
57
examples/javascript/max30100.js
Normal 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
71
examples/python/max30100.py
Executable 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()
|
9
src/max30100/CMakeLists.txt
Normal file
9
src/max30100/CMakeLists.txt
Normal 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)
|
21
src/max30100/javaupm_max30100.i
Normal file
21
src/max30100/javaupm_max30100.i
Normal 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);
|
||||
}
|
||||
}
|
||||
%}
|
9
src/max30100/jsupm_max30100.i
Normal file
9
src/max30100/jsupm_max30100.i
Normal 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
529
src/max30100/max30100.c
Normal 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
211
src/max30100/max30100.cxx
Normal 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
318
src/max30100/max30100.h
Normal 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
295
src/max30100/max30100.hpp
Normal 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
109
src/max30100/max30100_fti.c
Normal 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;
|
||||
}
|
170
src/max30100/max30100_regs.h
Normal file
170
src/max30100/max30100_regs.h
Normal 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
|
14
src/max30100/pyupm_max30100.i
Normal file
14
src/max30100/pyupm_max30100.i
Normal 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"
|
Loading…
x
Reference in New Issue
Block a user