├── .gitignore ├── Documents ├── MAX30101.pdf ├── MAX32664_REV4.pdf └── max32664-user-guide_REV3.pdf ├── README.md ├── examples ├── Example1_config_BPM_Mode1 │ └── Example1_config_BPM_Mode1.ino ├── Example2_config_BPM_Mode2 │ └── Example2_config_BPM_Mode2.ino ├── Example3_modify_AGC_Algo │ └── Example3_modify_AGC_Algo.ino ├── Example4_config_LEDs_BPM │ └── Example4_config_LEDs_BPM.ino └── Example5_define_pins_at_begin │ └── Example5_define_pins_at_begin.ino ├── keywords.txt ├── library.properties └── src ├── SparkFun_Bio_Sensor_Hub_Library.cpp └── SparkFun_Bio_Sensor_Hub_Library.h /.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | [._]*.un~ 3 | *.swp 4 | -------------------------------------------------------------------------------- /Documents/MAX30101.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sparkfun/SparkFun_Bio_Sensor_Hub_Library/91575258e5e2c47f0f4b8ec97b651381671f4637/Documents/MAX30101.pdf -------------------------------------------------------------------------------- /Documents/MAX32664_REV4.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sparkfun/SparkFun_Bio_Sensor_Hub_Library/91575258e5e2c47f0f4b8ec97b651381671f4637/Documents/MAX32664_REV4.pdf -------------------------------------------------------------------------------- /Documents/max32664-user-guide_REV3.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sparkfun/SparkFun_Bio_Sensor_Hub_Library/91575258e5e2c47f0f4b8ec97b651381671f4637/Documents/max32664-user-guide_REV3.pdf -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | SparkFun Pulse Oximeter and Heart Rate Sensor 2 | ======================================== 3 | ![SparkFun Pulse Oximeter and Heart Rate Monitor](https://cdn.sparkfun.com/assets/parts/1/3/6/6/4/15219-SparkFun_Pulse_Oximeter_and_Heart_Rate_Sensor_-_MAX30101__Qwiic_-01.jpg) 4 | 5 | [*SparkFun Pulse Oximeter and Heart Rate Monitor (SEN-15291)*](https://www.sparkfun.com/products/15219) 6 | 7 | This is an Arduino Library for the SparkFun Pulse Oximeter and Heart-Rate Sensor. The sensor is an 8 | I²C based [biometric](https://en.wikipedia.org/wiki/Biometrics) sensor, utilizing two chips from Maxim 9 | Integrated: the MAX32664 Bio Metric Sensor Hub and the MAX30101 Pulse Oximetry and Heart-Rate Module. 10 | 11 | Repository Contents 12 | ------------------- 13 | * **/examples** - Example code for the Arduino IDE 14 | * **/src** - Source files for the library (.cpp and .h files). 15 | * **/keywords.txt** - Keywords from the library that are highlighted in Arduino IDE. 16 | * **/library.properties** - General Library properties for the Arduino Package Manager. 17 | 18 | Documentation 19 | -------------- 20 | 21 | * **[Installing an Arduino Library Guide](https://learn.sparkfun.com/tutorials/installing-an-arduino-library)** - Basic information on how to install an Arduino library. 22 | * **[Hookup Guide](https://learn.sparkfun.com/tutorials/sparkfun-pulse-oximeter-and-heart-rate-monitor-hookup-guide)** - Basic hookup guide for the SparkFun Pulse Oximeter and Heart Rate Sensor. 23 | 24 | Products that use this library 25 | -------------- 26 | * [SEN-15291](https://www.sparkfun.com/products/15219)- SparkFun Version 1.0 27 | 28 | License Information 29 | ------------------- 30 | 31 | This product is _**open source**_! 32 | 33 | Various bits of the code have different licenses applied. Anything SparkFun wrote is beerware; if you see me (or any other SparkFun employee) at the local, and you've found our code helpful, please buy us a round! 34 | 35 | Please use, reuse, and modify these files as you see fit. Please maintain attribution to SparkFun Electronics and release anything derivative under the same license. 36 | 37 | Distributed as-is; no warranty is given. 38 | 39 | - Your friends at SparkFun. 40 | 41 | -------------------------------------------------------------------------------- /examples/Example1_config_BPM_Mode1/Example1_config_BPM_Mode1.ino: -------------------------------------------------------------------------------- 1 | /* 2 | This example sketch gives you exactly what the SparkFun Pulse Oximiter and 3 | Heart Rate Monitor is designed to do: read heart rate and blood oxygen levels. 4 | This board requires I-squared-C connections but also connections to the reset 5 | and mfio pins. When using the device keep LIGHT and CONSISTENT pressure on the 6 | sensor. Otherwise you may crush the capillaries in your finger which results 7 | in bad or no results. A summary of the hardware connections are as follows: 8 | SDA -> SDA 9 | SCL -> SCL 10 | RESET -> PIN 4 11 | MFIO -> PIN 5 12 | 13 | Author: Elias Santistevan 14 | Date: 8/2019 15 | SparkFun Electronics 16 | 17 | If you run into an error code check the following table to help diagnose your 18 | problem: 19 | 1 = Unavailable Command 20 | 2 = Unavailable Function 21 | 3 = Data Format Error 22 | 4 = Input Value Error 23 | 5 = Try Again 24 | 255 = Error Unknown 25 | */ 26 | 27 | #include 28 | #include 29 | 30 | // Reset pin, MFIO pin 31 | int resPin = 4; 32 | int mfioPin = 13; 33 | 34 | // Takes address, reset pin, and MFIO pin. 35 | SparkFun_Bio_Sensor_Hub bioHub(resPin, mfioPin); 36 | 37 | bioData body; 38 | // ^^^^^^^^^ 39 | // What's this!? This is a type (like int, byte, long) unique to the SparkFun 40 | // Pulse Oximeter and Heart Rate Monitor. Unlike those other types it holds 41 | // specific information on your heartrate and blood oxygen levels. BioData is 42 | // actually a specific kind of type, known as a "struct". 43 | // You can choose another variable name other than "body", like "blood", or 44 | // "readings", but I chose "body". Using this "body" varible in the 45 | // following way gives us access to the following data: 46 | // body.heartrate - Heartrate 47 | // body.confidence - Confidence in the heartrate value 48 | // body.oxygen - Blood oxygen level 49 | // body.status - Has a finger been sensed? 50 | 51 | 52 | void setup(){ 53 | 54 | Serial.begin(115200); 55 | 56 | Wire.begin(); 57 | int result = bioHub.begin(); 58 | if (result == 0) // Zero errors! 59 | Serial.println("Sensor started!"); 60 | else 61 | Serial.println("Could not communicate with the sensor!"); 62 | 63 | Serial.println("Configuring Sensor...."); 64 | int error = bioHub.configBpm(MODE_ONE); // Configuring just the BPM settings. 65 | if(error == 0){ // Zero errors! 66 | Serial.println("Sensor configured."); 67 | } 68 | else { 69 | Serial.println("Error configuring sensor."); 70 | Serial.print("Error: "); 71 | Serial.println(error); 72 | } 73 | 74 | // Data lags a bit behind the sensor, if you're finger is on the sensor when 75 | // it's being configured this delay will give some time for the data to catch 76 | // up. 77 | Serial.println("Loading up the buffer with data...."); 78 | delay(4000); 79 | 80 | } 81 | 82 | void loop(){ 83 | 84 | // Information from the readBpm function will be saved to our "body" 85 | // variable. 86 | body = bioHub.readBpm(); 87 | Serial.print("Heartrate: "); 88 | Serial.println(body.heartRate); 89 | Serial.print("Confidence: "); 90 | Serial.println(body.confidence); 91 | Serial.print("Oxygen: "); 92 | Serial.println(body.oxygen); 93 | Serial.print("Status: "); 94 | Serial.println(body.status); 95 | // Slow it down or your heart rate will go up trying to keep up 96 | // with the flow of numbers 97 | delay(250); 98 | } 99 | -------------------------------------------------------------------------------- /examples/Example2_config_BPM_Mode2/Example2_config_BPM_Mode2.ino: -------------------------------------------------------------------------------- 1 | /* 2 | This example sketch gives you exactly what the SparkFun Pulse Oximiter and 3 | Heart Rate Monitor is designed to do: read heart rate and blood oxygen levels. 4 | This board requires I-squared-C connections but also connections to the reset 5 | and mfio pins. When using the device keep LIGHT and CONSISTENT pressure on the 6 | sensor. Otherwise you may crush the capillaries in your finger which results 7 | in bad or no results. This differs from example one by giving an additional 8 | two data points: an extended finger status and the r value of the blood oxygen 9 | level. A summary of the hardware connections are as follows: 10 | SDA -> SDA 11 | SCL -> SCL 12 | RESET -> PIN 4 13 | MFIO -> PIN 5 14 | 15 | Author: Elias Santistevan 16 | Date: 8/2019 17 | SparkFun Electronics 18 | 19 | If you run into an error code check the following table to help diagnose your 20 | problem: 21 | 1 = Unavailable Command 22 | 2 = Unavailable Function 23 | 3 = Data Format Error 24 | 4 = Input Value Error 25 | 5 = Try Again 26 | 255 = Error Unknown 27 | */ 28 | 29 | #include 30 | #include 31 | 32 | // Reset pin, MFIO pin 33 | int resPin = 4; 34 | int mfioPin = 5; 35 | 36 | // Takes address, reset pin, and MFIO pin. 37 | SparkFun_Bio_Sensor_Hub bioHub(resPin, mfioPin); 38 | 39 | bioData body; 40 | // ^^^^^^^^^ 41 | // What's this!? This is a type (like int, byte, long) unique to the SparkFun 42 | // Pulse Oximeter and Heart Rate Monitor. Unlike those other types it holds 43 | // specific information on your heartrate and blood oxygen levels. BioData is 44 | // actually a specific kind of type, known as a "struct". 45 | // You can choose another variable name other than "body", like "blood", or 46 | // "readings", but I chose "body". Using this "body" varible in the 47 | // following way gives us access to the following data: 48 | // body.heartrate - Heartrate 49 | // body.confidence - Confidence in the heartrate value 50 | // body.oxygen - Blood oxygen level 51 | // body.status - Has a finger been sensed? 52 | // body.extStatus - What else is the finger up to? 53 | // body.rValue - Blood oxygen correlation coefficient. 54 | 55 | 56 | void setup(){ 57 | 58 | Serial.begin(115200); 59 | 60 | Wire.begin(); 61 | int result = bioHub.begin(); 62 | if (result == 0) //Zero errors! 63 | Serial.println("Sensor started!"); 64 | else 65 | Serial.println("Could not communicate with the sensor!"); 66 | 67 | Serial.println("Configuring Sensor...."); 68 | int error = bioHub.configBpm(MODE_TWO); // Configuring just the BPM settings. 69 | if(error == 0){ // Zero errors 70 | Serial.println("Sensor configured."); 71 | } 72 | else { 73 | Serial.println("Error configuring sensor."); 74 | Serial.print("Error: "); 75 | Serial.println(error); 76 | } 77 | 78 | // Data lags a bit behind the sensor, if you're finger is on the sensor when 79 | // it's being configured this delay will give some time for the data to catch 80 | // up. 81 | Serial.println("Loading up the buffer with data...."); 82 | delay(4000); 83 | 84 | } 85 | 86 | void loop(){ 87 | 88 | // Information from the readBpm function will be saved to our "body" 89 | // variable. 90 | body = bioHub.readBpm(); 91 | Serial.print("Heartrate: "); 92 | Serial.println(body.heartRate); 93 | Serial.print("Confidence: "); 94 | Serial.println(body.confidence); 95 | Serial.print("Oxygen: "); 96 | Serial.println(body.oxygen); 97 | Serial.print("Status: "); 98 | Serial.println(body.status); 99 | Serial.print("Extended Status: "); 100 | Serial.println(body.extStatus); 101 | Serial.print("Blood Oxygen R value: "); 102 | Serial.println(body.rValue); 103 | // Slow it down or your heart rate will go up trying to keep up 104 | // with the flow of numbers 105 | delay(250); 106 | } 107 | -------------------------------------------------------------------------------- /examples/Example3_modify_AGC_Algo/Example3_modify_AGC_Algo.ino: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | This example code is very similar to the first, however we're adjusting the 4 | Automatic Gain Control (AGC) Algorithm which automatically determines the 5 | pulse width and current consumption of the MAX30101's LEDs as it gathers data 6 | on light absorption. This particular setting is enabled when calling 7 | "configBpm" and so in this example we demonstrate how to modify the algorithm. 8 | SDA -> SDA 9 | SCL -> SCL 10 | RESET -> PIN 4 11 | MFIO -> PIN 5 12 | 13 | Author: Elias Santistevan 14 | Date: 8/2019 15 | SparkFun Electronics 16 | 17 | If you run into an error code check the following table to help diagnose your 18 | problem: 19 | 1 = Unavailable Command 20 | 2 = Unavailable Function 21 | 3 = Data Format Error 22 | 4 = Input Value Error 23 | 5 = Try Again 24 | 255 = Error Unknown 25 | */ 26 | 27 | #include 28 | #include 29 | 30 | // Reset pin, MFIO pin 31 | int resPin = 4; 32 | int mfioPin = 5; 33 | 34 | int algoRange = 80; // ADC Range (0-100%) 35 | int algoStepSize = 20; // Step Size (0-100%) 36 | int algoSens = 20; // Sensitivity (0-100%) 37 | int algoSamp = 10; // Number of samples to average (0-255) 38 | 39 | // Takes address, reset pin, and MFIO pin. 40 | SparkFun_Bio_Sensor_Hub bioHub(resPin, mfioPin); 41 | 42 | bioData body; 43 | // What's this!? This is a type (like int, byte, long) unique to the SparkFun 44 | // Pulse Oximeter and Heart Rate Monitor. Unlike those other types it holds 45 | // specific information on your heartrate and blood oxygen levels. BioData is 46 | // actually a specific kind of type, known as a "struct". 47 | // You can choose another variable name other than "body", like "blood", or 48 | // "readings", but I chose "body". Using this "body" varible in the 49 | // following way gives us access to the following data: 50 | // body.heartrate - Heartrate 51 | // body.confidence - Confidence in the heartrate value 52 | // body.oxygen - Blood oxygen level 53 | // body.status - Has a finger been sensed? 54 | 55 | void setup(){ 56 | 57 | Serial.begin(115200); 58 | 59 | Wire.begin(); 60 | int result = bioHub.begin(); 61 | if (result == 0) // Zero errors! 62 | Serial.println("Sensor started!"); 63 | 64 | // Adjusting the Automatic Gain Control (AGC) Algorithm 65 | int error = bioHub.setAlgoRange(algoRange); 66 | if (error > 0){ 67 | Serial.println("Could not set algorithm's Range."); 68 | } 69 | 70 | error = bioHub.setAlgoStepSize(algoStepSize); 71 | if (error > 0){ 72 | Serial.println("Could not set the step size."); 73 | } 74 | 75 | error = bioHub.setAlgoSensitivity(algoSens); 76 | if (error > 0){ 77 | Serial.println("Could not set the sensitivity."); 78 | } 79 | 80 | error = bioHub.setAlgoSamples(algoSamp); 81 | if (error > 0){ 82 | Serial.println("Could not set the sample size."); 83 | } 84 | 85 | // Let's read back what we set.... 86 | int algoVal = bioHub.readAlgoRange(); 87 | Serial.print("Algorithm set to: "); 88 | Serial.println(algoVal); 89 | 90 | int stepVal = bioHub.readAlgoStepSize(); 91 | Serial.print("Algorithm set to: "); 92 | Serial.println(stepVal); 93 | 94 | int senVal = bioHub.readAlgoSensitivity(); 95 | Serial.print("Algorithm set to: "); 96 | Serial.println(senVal); 97 | 98 | int sampVal = bioHub.readAlgoSamples(); 99 | Serial.print("Algorithm set to: "); 100 | Serial.println(sampVal); 101 | 102 | Serial.println("Configuring Sensor."); 103 | error = bioHub.configBpm(MODE_ONE); 104 | if (error > 0){ 105 | Serial.println("Could not configure the sensor."); 106 | } 107 | 108 | // Data lags a bit behind the sensor, if you're finger is on the sensor when 109 | // it's being configured this delay will give some time for the data to catch 110 | // up. 111 | Serial.println("Loading up the buffer with data...."); 112 | delay(4000); 113 | 114 | } 115 | 116 | void loop(){ 117 | 118 | // Information from the readBpm function will be saved to our "body" 119 | // variable. 120 | body = bioHub.readBpm(); 121 | Serial.print("Heartrate: "); 122 | Serial.println(body.heartRate); 123 | Serial.print("Confidence: "); 124 | Serial.println(body.confidence); 125 | Serial.print("Oxygen: "); 126 | Serial.println(body.oxygen); 127 | Serial.print("Status: "); 128 | Serial.println(body.status); 129 | // Slow it down or your heart rate will go up trying to keep up 130 | // with the flow of numbers 131 | delay(250); 132 | 133 | } 134 | -------------------------------------------------------------------------------- /examples/Example4_config_LEDs_BPM/Example4_config_LEDs_BPM.ino: -------------------------------------------------------------------------------- 1 | /* 2 | This example displays a more manual method of adjusting the way in which the 3 | MAX30101 gathers data. Specifically we'll look at how to modify the pulse 4 | length of the LEDs within the MAX30101 which impacts the number of samples 5 | that can be gathered, so we'll adjust this value as well. In addition we 6 | gather additional data from the bioData type: LED samples. This data gives 7 | the number of samples gathered by the MAX30101 for both the red and IR LEDs. 8 | As a side note you can also choose MODE_ONE and MODE_TWO for configSensorBpm 9 | as well. 10 | A summary of the hardware connections are as follows: 11 | SDA -> SDA 12 | SCL -> SCL 13 | RESET -> PIN 4 14 | MFIO -> PIN 5 15 | 16 | Author: Elias Santistevan 17 | Date: 8/2019 18 | SparkFun Electronics 19 | 20 | If you run into an error code check the following table to help diagnose your 21 | problem: 22 | 1 = Unavailable Command 23 | 2 = Unavailable Function 24 | 3 = Data Format Error 25 | 4 = Input Value Error 26 | 5 = Try Again 27 | 255 = Error Unknown 28 | */ 29 | 30 | #include 31 | #include 32 | 33 | // Reset pin, MFIO pin 34 | int resPin = 4; 35 | int mfioPin = 5; 36 | 37 | // Possible widths: 69, 118, 215, 411us 38 | int width = 411; 39 | // Possible samples: 50, 100, 200, 400, 800, 1000, 1600, 3200 samples/second 40 | // Not every sample amount is possible with every width; check out our hookup 41 | // guide for more information. 42 | int samples = 400; 43 | int pulseWidthVal; 44 | int sampleVal; 45 | 46 | // Takes address, reset pin, and MFIO pin. 47 | SparkFun_Bio_Sensor_Hub bioHub(resPin, mfioPin); 48 | 49 | bioData body; 50 | // ^^^^^^^^^ 51 | // What's this!? This is a type (like "int", "byte", "long") unique to the SparkFun 52 | // Pulse Oximeter and Heart Rate Monitor. Unlike those other types it holds 53 | // specific information on the LED count values of the sensor and ALSO the 54 | // biometric data: heart rate, oxygen levels, and confidence. "bioLedData" is 55 | // actually a specific kind of type, known as a "struct". I chose the name 56 | // "body" but you could use another variable name like "blood", "readings", 57 | // "ledBody" or whatever. Using the variable in the following way gives the 58 | // following data: 59 | // body.irLed - Infrared LED counts. 60 | // body.redLed - Red LED counts. 61 | // body.heartrate - Heartrate 62 | // body.confidence - Confidence in the heartrate value 63 | // body.oxygen - Blood oxygen level 64 | // body.status - Has a finger been sensed? 65 | 66 | void setup(){ 67 | 68 | Serial.begin(115200); 69 | 70 | Wire.begin(); 71 | int result = bioHub.begin(); 72 | if (result == 0) // Zero errors! 73 | Serial.println("Sensor started!"); 74 | 75 | Serial.println("Configuring Sensor...."); 76 | int error = bioHub.configSensorBpm(MODE_ONE); // Configure Sensor and BPM mode , MODE_TWO also available 77 | if (error == 0){// Zero errors. 78 | Serial.println("Sensor configured."); 79 | } 80 | else { 81 | Serial.println("Error configuring sensor."); 82 | Serial.print("Error: "); 83 | Serial.println(error); 84 | } 85 | 86 | // Set pulse width. 87 | error = bioHub.setPulseWidth(width); 88 | if (error == 0){// Zero errors. 89 | Serial.println("Pulse Width Set."); 90 | } 91 | else { 92 | Serial.println("Could not set Pulse Width."); 93 | Serial.print("Error: "); 94 | Serial.println(error); 95 | } 96 | 97 | // Check that the pulse width was set. 98 | pulseWidthVal = bioHub.readPulseWidth(); 99 | Serial.print("Pulse Width: "); 100 | Serial.println(pulseWidthVal); 101 | 102 | // Set sample rate per second. Remember that not every sample rate is 103 | // available with every pulse width. Check hookup guide for more information. 104 | error = bioHub.setSampleRate(samples); 105 | if (error == 0){// Zero errors. 106 | Serial.println("Sample Rate Set."); 107 | } 108 | else { 109 | Serial.println("Could not set Sample Rate!"); 110 | Serial.print("Error: "); 111 | Serial.println(error); 112 | } 113 | 114 | // Check sample rate. 115 | sampleVal = bioHub.readSampleRate(); 116 | Serial.print("Sample rate is set to: "); 117 | Serial.println(sampleVal); 118 | 119 | // Data lags a bit behind the sensor, if you're finger is on the sensor when 120 | // it's being configured this delay will give some time for the data to catch 121 | // up. 122 | Serial.println("Loading up the buffer with data...."); 123 | delay(4000); 124 | 125 | } 126 | 127 | void loop(){ 128 | 129 | // Information from the readSensor function will be saved to our "body" 130 | // variable. 131 | body = bioHub.readSensorBpm(); 132 | Serial.print("Infrared LED counts: "); 133 | Serial.println(body.irLed); 134 | Serial.print("Red LED counts: "); 135 | Serial.println(body.redLed); 136 | Serial.print("Heartrate: "); 137 | Serial.println(body.heartRate); 138 | Serial.print("Confidence: "); 139 | Serial.println(body.confidence); 140 | Serial.print("Blood Oxygen: "); 141 | Serial.println(body.oxygen); 142 | Serial.print("Status: "); 143 | Serial.println(body.status); 144 | // Slow it down or your heart rate will go up trying to keep up 145 | // with the flow of numbers 146 | delay(250); 147 | } 148 | -------------------------------------------------------------------------------- /examples/Example5_define_pins_at_begin/Example5_define_pins_at_begin.ino: -------------------------------------------------------------------------------- 1 | /* 2 | This example sketch gives you exactly what the SparkFun Pulse Oximiter and 3 | Heart Rate Monitor is designed to do: read heart rate and blood oxygen levels. 4 | 5 | Here the reset and mfio pins are defined at begin, instead of when the 6 | SparkFun_Bio_Sensor_Hub is instantiated. This makes it much easier to 7 | instantiate the class using a factory method. 8 | 9 | This board requires I-squared-C connections but also connections to the reset 10 | and mfio pins. When using the device keep LIGHT and CONSISTENT pressure on the 11 | sensor. Otherwise you may crush the capillaries in your finger which results 12 | in bad or no results. A summary of the hardware connections are as follows: 13 | SDA -> SDA 14 | SCL -> SCL 15 | RESET -> PIN 4 16 | MFIO -> PIN 5 17 | 18 | Author: Elias Santistevan 19 | Date: 8/2019 20 | SparkFun Electronics 21 | 22 | If you run into an error code check the following table to help diagnose your 23 | problem: 24 | 1 = Unavailable Command 25 | 2 = Unavailable Function 26 | 3 = Data Format Error 27 | 4 = Input Value Error 28 | 5 = Try Again 29 | 255 = Error Unknown 30 | */ 31 | 32 | #include 33 | #include 34 | 35 | // Reset pin, MFIO pin 36 | int resPin = 4; 37 | int mfioPin = 5; 38 | 39 | // The reset pin and MFIO pin will be defined at begin in this example. 40 | SparkFun_Bio_Sensor_Hub bioHub; 41 | 42 | bioData body; 43 | // ^^^^^^^^^ 44 | // What's this!? This is a type (like int, byte, long) unique to the SparkFun 45 | // Pulse Oximeter and Heart Rate Monitor. Unlike those other types it holds 46 | // specific information on your heartrate and blood oxygen levels. BioData is 47 | // actually a specific kind of type, known as a "struct". 48 | // You can choose another variable name other than "body", like "blood", or 49 | // "readings", but I chose "body". Using this "body" varible in the 50 | // following way gives us access to the following data: 51 | // body.heartrate - Heartrate 52 | // body.confidence - Confidence in the heartrate value 53 | // body.oxygen - Blood oxygen level 54 | // body.status - Has a finger been sensed? 55 | 56 | 57 | void setup(){ 58 | 59 | Serial.begin(115200); 60 | 61 | Wire.begin(); 62 | int result = bioHub.begin(Wire, resPin, mfioPin); // Define the pins here 63 | if (result == 0) // Zero errors! 64 | Serial.println("Sensor started!"); 65 | else 66 | Serial.println("Could not communicate with the sensor!"); 67 | 68 | Serial.println("Configuring Sensor...."); 69 | int error = bioHub.configBpm(MODE_ONE); // Configuring just the BPM settings. 70 | if(error == 0){ // Zero errors! 71 | Serial.println("Sensor configured."); 72 | } 73 | else { 74 | Serial.println("Error configuring sensor."); 75 | Serial.print("Error: "); 76 | Serial.println(error); 77 | } 78 | 79 | // Data lags a bit behind the sensor, if you're finger is on the sensor when 80 | // it's being configured this delay will give some time for the data to catch 81 | // up. 82 | Serial.println("Loading up the buffer with data...."); 83 | delay(4000); 84 | 85 | } 86 | 87 | void loop(){ 88 | 89 | // Information from the readBpm function will be saved to our "body" 90 | // variable. 91 | body = bioHub.readBpm(); 92 | Serial.print("Heartrate: "); 93 | Serial.println(body.heartRate); 94 | Serial.print("Confidence: "); 95 | Serial.println(body.confidence); 96 | Serial.print("Oxygen: "); 97 | Serial.println(body.oxygen); 98 | Serial.print("Status: "); 99 | Serial.println(body.status); 100 | // Slow it down or your heart rate will go up trying to keep up 101 | // with the flow of numbers 102 | delay(250); 103 | } 104 | -------------------------------------------------------------------------------- /keywords.txt: -------------------------------------------------------------------------------- 1 | ================================== 2 | CLASS 3 | ================================== 4 | SparkFun_Bio_Sensor_Hub KEYWORD1 5 | 6 | ================================== 7 | FUNCTIONS 8 | ================================== 9 | begin KEYWORD2 10 | beginBootloader KEYWORD2 11 | readSensorHubStatus KEYWORD2 12 | setOperatingMode KEYWORD2 13 | configBpm KEYWORD2 14 | configSensor KEYWORD2 15 | configSensorBpm KEYWORD2 16 | readBpm KEYWORD2 17 | readSensor KEYWORD2 18 | readSensorBpm KEYWORD2 19 | setPulseWidth KEYWORD2 20 | readPulseWidth KEYWORD2 21 | setSampleRate KEYWORD2 22 | readSampleRate KEYWORD2 23 | setAdcRange KEYWORD2 24 | readAdcRange KEYWORD2 25 | getMcuType KEYWORD2 26 | getBootloaderInf KEYWORD2 27 | max30101Control KEYWORD2 28 | readMAX30101State KEYWORD2 29 | accelControl KEYWORD2 30 | setOutputMode KEYWORD2 31 | setFifoThreshold KEYWORD2 32 | numSamplesOutFifo KEYWORD2 33 | getDataOutFifo KEYWORD2 34 | numSamplesExternalSensor KEYWORD2 35 | writeRegisterMAX30101 KEYWORD2 36 | writeRegisterAccel KEYWORD2 37 | readRegisterMAX30101 KEYWORD2 38 | readRegisterAccel KEYWORD2 39 | getAfeAttributesMAX30101 KEYWORD2 40 | getAfeAttributesAccelerometer KEYWORD2 41 | dumpRegisterMAX30101 KEYWORD2 42 | dumpRegisterAccelerometer KEYWORD2 43 | setAlgoRange KEYWORD2 44 | setAlgoStepSize KEYWORD2 45 | setAlgoSensitivity KEYWORD2 46 | setAlgoSamples KEYWORD2 47 | setMaximFastCoef KEYWORD2 48 | readAlgoRange KEYWORD2 49 | readAlgoStepSize KEYWORD2 50 | readAlgoSensitivity KEYWORD2 51 | readAlgoSamples KEYWORD2 52 | readMaximFastCoef KEYWORD2 53 | agcAlgoControl KEYWORD2 54 | maximFastAlgoControl KEYWORD2 55 | setNumPages KEYWORD2 56 | eraseFlash KEYWORD2 57 | readBootloaderVers KEYWORD2 58 | readSensorHubVersion KEYWORD2 59 | readAlgorithmVersion KEYWORD2 60 | 61 | ================================== 62 | CONSTANTS 63 | ================================== 64 | WRITE_FIFO_INPUT_BYTE LITERAL1 65 | DISABLE LITERAL1 66 | ENABLE LITERAL1 67 | MODE_ONE LITERAL1 68 | MODE_TWO LITERAL1 69 | APP_MODE LITERAL1 70 | BOOTLOADER_MODE LITERAL1 71 | NO_WRITE LITERAL1 72 | INCORR_PARAM LITERAL1 73 | CONFIGURATION_REGISTER LITERAL1 74 | PULSE_MASK LITERAL1 75 | READ_PULSE_MASK LITERAL1 76 | SAMP_MASK LITERAL1 77 | READ_SAMP_MASK LITERAL1 78 | ADC_MASK LITERAL1 79 | READ_ADC_MASK LITERAL1 80 | ENABLE_CMD_DELAY LITERAL1 81 | CMD_DELAY LITERAL1 82 | MAXFAST_ARRAY_SIZE LITERAL1 83 | MAXFAST_EXTENDED_DATA LITERAL1 84 | MAX30101_LED_ARRAY LITERAL1 85 | SET_FORMAT LITERAL1 86 | READ_FORMAT LITERAL1 87 | WRITE_SET_THRESHOLD LITERAL1 88 | WRITE_EXTERNAL_TO_FIFO LITERAL1 89 | SUCCESS LITERAL1 90 | ERR_UNAVAIL_CMD LITERAL1 91 | ERR_UNAVAIL_FUNC LITERAL1 92 | ERR_DATA_FORMAT LITERAL1 93 | ERR_INPUT_VALUE LITERAL1 94 | ERR_TRY_AGAIN LITERAL1 95 | ERR_BTLDR_GENERAL LITERAL1 96 | ERR_BTLDR_CHECKSUM LITERAL1 97 | ERR_BTLDR_AUTH LITERAL1 98 | ERR_BTLDR_INVALID_APP LITERAL1 99 | ERR_UNKNOWN LITERAL1 100 | HUB_STATUS LITERAL1 101 | SET_DEVICE_MODE LITERAL1 102 | READ_DEVICE_MODE LITERAL1 103 | OUTPUT_MODE LITERAL1 104 | READ_OUTPUT_MODE LITERAL1 105 | READ_DATA_OUTPUT LITERAL1 106 | READ_DATA_INPUT LITERAL1 107 | WRITE_INPUT LITERAL1 108 | WRITE_REGISTER LITERAL1 109 | READ_REGISTER LITERAL1 110 | READ_ATTRIBUTES_AFE LITERAL1 111 | DUMP_REGISTERS LITERAL1 112 | ENABLE_SENSOR LITERAL1 113 | READ_SENSOR_MODE LITERAL1 114 | CHANGE_ALGORITHM_CONFIG LITERAL1 115 | READ_ALGORITHM_CONFIG LITERAL1 116 | ENABLE_ALGORITHM LITERAL1 117 | BOOTLOADER_FLASH LITERAL1 118 | BOOTLOADER_INFO LITERAL1 119 | IDENTITY LITERAL1 120 | EXIT_BOOTLOADER LITERAL1 121 | RESET LITERAL1 122 | ENTER_BOOTLOADER LITERAL1 123 | PAUSE LITERAL1 124 | SENSOR_DATA LITERAL1 125 | ALGO_DATA LITERAL1 126 | SENSOR_AND_ALGORITHM LITERAL1 127 | PAUSE_TWO LITERAL1 128 | SENSOR_COUNTER_BYTE LITERAL1 129 | ALGO_COUNTER_BYTE LITERAL1 130 | SENSOR_ALGO_COUNTER LITERAL1 131 | NUM_SAMPLES LITERAL1 132 | READ_DATA LITERAL1 133 | SAMPLE_SIZE LITERAL1 134 | READ_INPUT_DATA LITERAL1 135 | READ_SENSOR_DATA LITERAL1 136 | READ_NUM_SAMPLES_INPUT LITERAL1 137 | READ_NUM_SAMPLES_SENSOR LITERAL1 138 | WRITE_MAX30101 LITERAL1 139 | WRITE_ACCELEROMETER LITERAL1 140 | READ_MAX30101 LITERAL1 141 | READ_ACCELEROMETER LITERAL1 142 | RETRIEVE_AFE_MAX30101 LITERAL1 143 | RETRIEVE_AFE_ACCELEROMETER LITERAL1 144 | DUMP_REGISTER_MAX30101 LITERAL1 145 | DUMP_REGISTER_ACCELEROMETER LITERAL1 146 | ENABLE_MAX30101 LITERAL1 147 | ENABLE_ACCELEROMETER LITERAL1 148 | READ_ENABLE_MAX30101 LITERAL1 149 | READ_ENABLE_ACCELEROMETER LITERAL1 150 | SET_TARG_PERC LITERAL1 151 | SET_STEP_SIZE LITERAL1 152 | SET_SENSITIVITY LITERAL1 153 | SET_AVG_SAMPLES LITERAL1 154 | SET_PULSE_OX_COEF LITERAL1 155 | AGC_GAIN_ID LITERAL1 156 | AGC_STEP_SIZE_ID LITERAL1 157 | AGC_SENSITIVITY_ID LITERAL1 158 | AGC_NUM_SAMP_ID LITERAL1 159 | MAXIMFAST_COEF_ID LITERAL1 160 | READ_AGC_PERCENTAGE LITERAL1 161 | READ_AGC_STEP_SIZE LITERAL1 162 | READ_AGC_SENSITIVITY LITERAL1 163 | READ_AGC_NUM_SAMPLES LITERAL1 164 | READ_MAX_FAST_COEF LITERAL1 165 | READ_AGC_PERC_ID LITERAL1 166 | READ_AGC_STEP_SIZE_ID LITERAL1 167 | READ_AGC_SENSITIVITY_ID LITERAL1 168 | READ_AGC_NUM_SAMPLES_ID LITERAL1 169 | READ_MAX_FAST_COEF_ID LITERAL1 170 | ENABLE_AGC_ALGO LITERAL1 171 | ENABLE_WHRM_ALGO LITERAL1 172 | SET_INIT_VECTOR_BYTES LITERAL1 173 | SET_AUTH_BYTES LITERAL1 174 | SET_NUM_PAGES LITERAL1 175 | ERASE_FLASH LITERAL1 176 | SEND_PAGE_VALUE LITERAL1 177 | BOOTLOADER_VERS LITERAL1 178 | PAGE_SIZE LITERAL1 179 | READ_MCU_TYPE LITERAL1 180 | READ_SENSOR_HUB_VERS LITERAL1 181 | READ_ALGO_VERS LITERAL1 182 | 183 | ================================== 184 | DATA TYPES 185 | ================================== 186 | READ_STATUS_BYTE_VALUE KEYWORD1 187 | FAMILY_REGISTER_BYTES KEYWORD1 188 | DEVICE_MODE_WRITE_BYTES KEYWORD1 189 | OUTPUT_MODE_WRITE_BYTE KEYWORD1 190 | FIFO_OUTPUT_INDEX_BYTE KEYWORD1 191 | FIFO_EXTERNAL_INDEX_BYTE KEYWORD1 192 | WRITE_REGISTER_INDEX_BYTE KEYWORD1 193 | READ_REGISTER_INDEX_BYTE KEYWORD1 194 | GET_AFE_INDEX_BYTE KEYWORD1 195 | DUMP_REGISTER_INDEX_BYTE KEYWORD1 196 | SENSOR_ENABLE_INDEX_BYTE KEYWORD1 197 | READ_SENSOR_ENABLE_INDEX_BYTE KEYWORD1 198 | ALGORITHM_CONFIG_INDEX_BYTE KEYWORD1 199 | ALGO_AGC_WRITE_BYTE KEYWORD1 200 | READ_ALGORITHM_INDEX_BYTE KEYWORD1 201 | READ_AGC_ALGO_WRITE_BYTE KEYWORD1 202 | ALGORITHM_MODE_ENABLE_INDEX_BYTE KEYWORD1 203 | BOOTLOADER_FLASH_INDEX_BYTE KEYWORD1 204 | BOOTLOADER_INFO_INDEX_BYTE KEYWORD1 205 | IDENTITY_INDEX_BYTES KEYWORD1 206 | -------------------------------------------------------------------------------- /library.properties: -------------------------------------------------------------------------------- 1 | name=SparkFun Bio Sensor Hub Library 2 | version=1.1 3 | author=Elias Santistevan 4 | maintainer=SparkFun Electronics 5 | sentence=Library for the MAX32664 Bio Metric Hub IC 6 | paragraph=The SparkFun Bio Sensor Hub Library is tailored to Maxim Integrated's MAX32664 Bio Sensor Hub interacting with the MAX30101 on [SparkFun's Pulse Oximeter and Heart Rate Monitor](https://www.sparkfun.com/products/15219). The Bio Sensor Hub is a practically microscopic cortex-m0 micro-controller that handles the algorithmic calculation of the light data gathered by the MAX30101 Pulse Oximeter and Heart Rate Monitor, to produce accurate and fast blood oxygen and heart rate readings. The library provides simple function calls to all available commands on the chip as well as example code demonstrating basic to advanced capabilities of the chip. 7 | category=Sensors 8 | url=https://github.com/sparkfun/SparkFun_Bio_Sensor_Hub_Library 9 | architecture=* 10 | -------------------------------------------------------------------------------- /src/SparkFun_Bio_Sensor_Hub_Library.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | This is an Arduino Library written for the MAXIM 32664 Biometric Sensor Hub 3 | The MAX32664 Biometric Sensor Hub is in actuality a small Cortex M4 microcontroller 4 | with pre-loaded firmware and algorithms used to interact with the a number of MAXIM 5 | sensors; specifically the MAX30101 Pulse Oximter and Heart Rate Monitor and 6 | the KX122 Accelerometer. With that in mind, this library is built to 7 | communicate with a middle-person and so has a unique method of communication 8 | (family, index, and write bytes) that is more simplistic than writing and reading to 9 | registers, but includes a larger set of definable values. 10 | 11 | SparkFun Electronics 12 | Date: June, 2019 13 | Author: Elias Santistevan 14 | kk 15 | License: This code is public domain but you buy me a beer if you use this and we meet someday (Beerware license). 16 | 17 | Feel like supporting our work? Buy a board from SparkFun! 18 | */ 19 | 20 | #include "SparkFun_Bio_Sensor_Hub_Library.h" 21 | 22 | SparkFun_Bio_Sensor_Hub::SparkFun_Bio_Sensor_Hub(int resetPin, int mfioPin, uint8_t address) 23 | { 24 | 25 | _resetPin = resetPin; 26 | if (resetPin >= 0) 27 | pinMode(_resetPin, OUTPUT); // Set the pin as output 28 | 29 | _mfioPin = mfioPin; 30 | if (mfioPin >= 0) 31 | pinMode(_mfioPin, OUTPUT); // Set the pin as output 32 | 33 | _address = address; 34 | } 35 | 36 | // Family Byte: READ_DEVICE_MODE (0x02) Index Byte: 0x00, Write Byte: 0x00 37 | // The following function initializes the sensor. To place the MAX32664 into 38 | // application mode, the MFIO pin must be pulled HIGH while the board is held 39 | // in reset for 10ms. After 50 addtional ms have elapsed the board should be 40 | // in application mode and will return two bytes, the first 0x00 is a 41 | // successful communcation byte, followed by 0x00 which is the byte indicating 42 | // which mode the IC is in. 43 | uint8_t SparkFun_Bio_Sensor_Hub::begin(TwoWire &wirePort, int resetPin, int mfioPin) 44 | { 45 | 46 | _i2cPort = &wirePort; 47 | // _i2cPort->begin(); A call to Wire.begin should occur in sketch 48 | // to avoid multiple begins with other sketches. 49 | 50 | if (resetPin >= 0) 51 | { 52 | _resetPin = resetPin; 53 | pinMode(_resetPin, OUTPUT); // Set the pin as output 54 | } 55 | 56 | if (mfioPin >= 0) 57 | { 58 | _mfioPin = mfioPin; 59 | pinMode(_mfioPin, OUTPUT); // Set the pin as output 60 | } 61 | 62 | if ((_resetPin < 0) || (_mfioPin < 0)) // Bail if the pins have still not been defined 63 | return 0xFF; // Return ERR_UNKNOWN 64 | 65 | digitalWrite(_mfioPin, HIGH); 66 | digitalWrite(_resetPin, LOW); 67 | delay(10); 68 | digitalWrite(_resetPin, HIGH); 69 | delay(1000); 70 | pinMode(_mfioPin, INPUT_PULLUP); // To be used as an interrupt later 71 | 72 | uint8_t responseByte = readByte(READ_DEVICE_MODE, 0x00); // 0x00 only possible Index Byte. 73 | return responseByte; 74 | } 75 | 76 | // Family Byte: READ_DEVICE_MODE (0x02) Index Byte: 0x00, Write Byte: 0x00 77 | // The following function puts the MAX32664 into bootloader mode. To place the MAX32664 into 78 | // bootloader mode, the MFIO pin must be pulled LOW while the board is held 79 | // in reset for 10ms. After 50 addtional ms have elapsed the board should be 80 | // in bootloader mode and will return two bytes, the first 0x00 is a 81 | // successful communcation byte, followed by 0x08 which is the byte indicating 82 | // that the board is in bootloader mode. 83 | uint8_t SparkFun_Bio_Sensor_Hub::beginBootloader(TwoWire &wirePort, int resetPin, int mfioPin) 84 | { 85 | 86 | _i2cPort = &wirePort; 87 | // _i2cPort->begin(); A call to Wire.begin should occur in sketch 88 | // to avoid multiple begins with other sketches. 89 | 90 | if (resetPin >= 0) 91 | { 92 | _resetPin = resetPin; 93 | pinMode(_resetPin, OUTPUT); // Set the pin as output 94 | } 95 | 96 | if (mfioPin >= 0) 97 | { 98 | _mfioPin = mfioPin; 99 | pinMode(_mfioPin, OUTPUT); // Set the pin as output 100 | } 101 | 102 | if ((_resetPin < 0) || (_mfioPin < 0)) // Bail if the pins have still not been defined 103 | return 0xFF; // Return ERR_UNKNOWN 104 | 105 | digitalWrite(_mfioPin, LOW); 106 | digitalWrite(_resetPin, LOW); 107 | delay(10); 108 | digitalWrite(_resetPin, HIGH); 109 | delay(50); // Bootloader mode is enabled when this ends. 110 | pinMode(_resetPin, OUTPUT); 111 | pinMode(_mfioPin, OUTPUT); 112 | 113 | // Let's check to see if the device made it into bootloader mode. 114 | uint8_t responseByte = readByte(READ_DEVICE_MODE, 0x00); // 0x00 only possible Index Byte 115 | return responseByte; 116 | } 117 | 118 | // Family Byte: HUB_STATUS (0x00), Index Byte: 0x00, No Write Byte. 119 | // The following function checks the status of the FIFO. 120 | uint8_t SparkFun_Bio_Sensor_Hub::readSensorHubStatus() 121 | { 122 | 123 | uint8_t status = readByte(0x00, 0x00); // Just family and index byte. 124 | return status; // Will return 0x00 125 | } 126 | 127 | // This function sets very basic settings to get sensor and biometric data. 128 | // The biometric data includes data about heartrate, the confidence 129 | // level, SpO2 levels, and whether the sensor has detected a finger or not. 130 | uint8_t SparkFun_Bio_Sensor_Hub::configBpm(uint8_t mode) 131 | { 132 | 133 | uint8_t statusChauf = 0; 134 | if (mode == MODE_ONE || mode == MODE_TWO) 135 | { 136 | } 137 | else 138 | return INCORR_PARAM; 139 | 140 | statusChauf = setOutputMode(ALGO_DATA); // Just the data 141 | if (statusChauf != SFE_BIO_SUCCESS) 142 | return statusChauf; 143 | 144 | statusChauf = setFifoThreshold(0x01); // One sample before interrupt is fired. 145 | if (statusChauf != SFE_BIO_SUCCESS) 146 | return statusChauf; 147 | 148 | statusChauf = agcAlgoControl(ENABLE); // One sample before interrupt is fired. 149 | if (statusChauf != SFE_BIO_SUCCESS) 150 | return statusChauf; 151 | 152 | statusChauf = max30101Control(ENABLE); 153 | if (statusChauf != SFE_BIO_SUCCESS) 154 | return statusChauf; 155 | 156 | statusChauf = maximFastAlgoControl(mode); 157 | if (statusChauf != SFE_BIO_SUCCESS) 158 | return statusChauf; 159 | 160 | _userSelectedMode = mode; 161 | _sampleRate = readAlgoSamples(); 162 | 163 | delay(1000); 164 | return SFE_BIO_SUCCESS; 165 | } 166 | 167 | // This function sets very basic settings to get LED count values from the MAX30101. 168 | // Sensor data includes 24 bit LED values for the three LED channels: Red, IR, 169 | // and Green. 170 | uint8_t SparkFun_Bio_Sensor_Hub::configSensor() 171 | { 172 | 173 | uint8_t statusChauf; // Our status chauffeur 174 | 175 | statusChauf = setOutputMode(SENSOR_DATA); // Just the sensor data (LED) 176 | if (statusChauf != SFE_BIO_SUCCESS) 177 | return statusChauf; 178 | 179 | statusChauf = setFifoThreshold(0x01); // One sample before interrupt is fired to the MAX32664 180 | if (statusChauf != SFE_BIO_SUCCESS) 181 | return statusChauf; 182 | 183 | statusChauf = max30101Control(ENABLE); // Enable Sensor. 184 | if (statusChauf != SFE_BIO_SUCCESS) 185 | return statusChauf; 186 | 187 | statusChauf = maximFastAlgoControl(MODE_ONE); // Enable algorithm 188 | if (statusChauf != SFE_BIO_SUCCESS) 189 | return statusChauf; 190 | 191 | delay(1000); 192 | return SFE_BIO_SUCCESS; 193 | } 194 | 195 | // This function sets very basic settings to get sensor and biometric data. 196 | // Sensor data includes 24 bit LED values for the two LED channels: Red and IR. 197 | // The biometric data includes data about heartrate, the confidence 198 | // level, SpO2 levels, and whether the sensor has detected a finger or not. 199 | // Of note, the number of samples is set to one. 200 | uint8_t SparkFun_Bio_Sensor_Hub::configSensorBpm(uint8_t mode) 201 | { 202 | 203 | uint8_t statusChauf; // Our status chauffeur 204 | if (mode == MODE_ONE || mode == MODE_TWO) 205 | { 206 | } 207 | else 208 | return INCORR_PARAM; 209 | 210 | statusChauf = setOutputMode(SENSOR_AND_ALGORITHM); // Data and sensor data 211 | if (statusChauf != SFE_BIO_SUCCESS) 212 | return statusChauf; 213 | 214 | statusChauf = setFifoThreshold(0x01); // One sample before interrupt is fired to the MAX32664 215 | if (statusChauf != SFE_BIO_SUCCESS) 216 | return statusChauf; 217 | 218 | statusChauf = max30101Control(ENABLE); // Enable Sensor. 219 | if (statusChauf != SFE_BIO_SUCCESS) 220 | return statusChauf; 221 | 222 | statusChauf = maximFastAlgoControl(mode); // Enable algorithm 223 | if (statusChauf != SFE_BIO_SUCCESS) 224 | return statusChauf; 225 | 226 | _userSelectedMode = mode; 227 | _sampleRate = readAlgoSamples(); 228 | 229 | delay(1000); 230 | return SFE_BIO_SUCCESS; 231 | } 232 | 233 | // This function takes the 8 bytes from the FIFO buffer related to the wrist 234 | // heart rate algortihm: heart rate (uint16_t), confidence (uint8_t) , SpO2 (uint16_t), 235 | // and the finger detected status (uint8_t). Note that the the algorithm is stated as 236 | // "wrist" though the sensor only works with the finger. The data is loaded 237 | // into the whrmFifo and returned. 238 | bioData SparkFun_Bio_Sensor_Hub::readBpm() 239 | { 240 | 241 | bioData libBpm; 242 | uint8_t statusChauf; // The status chauffeur captures return values. 243 | 244 | statusChauf = readSensorHubStatus(); 245 | 246 | if (statusChauf == 1) 247 | { // Communication Error 248 | libBpm.heartRate = 0; 249 | libBpm.confidence = 0; 250 | libBpm.oxygen = 0; 251 | return libBpm; 252 | } 253 | 254 | numSamplesOutFifo(); 255 | 256 | if (_userSelectedMode == MODE_ONE) 257 | { 258 | 259 | readFillArray(READ_DATA_OUTPUT, READ_DATA, MAXFAST_ARRAY_SIZE, bpmArr); 260 | 261 | // Heart Rate formatting 262 | libBpm.heartRate = (uint16_t(bpmArr[0]) << 8); 263 | libBpm.heartRate |= (bpmArr[1]); 264 | libBpm.heartRate /= 10; 265 | 266 | // Confidence formatting 267 | libBpm.confidence = bpmArr[2]; 268 | 269 | // Blood oxygen level formatting 270 | libBpm.oxygen = uint16_t(bpmArr[3]) << 8; 271 | libBpm.oxygen |= bpmArr[4]; 272 | libBpm.oxygen /= 10; 273 | 274 | //"Machine State" - has a finger been detected? 275 | libBpm.status = bpmArr[5]; 276 | 277 | return libBpm; 278 | } 279 | 280 | else if (_userSelectedMode == MODE_TWO) 281 | { 282 | readFillArray(READ_DATA_OUTPUT, READ_DATA, MAXFAST_ARRAY_SIZE + MAXFAST_EXTENDED_DATA, bpmArrTwo); 283 | 284 | // Heart Rate formatting 285 | libBpm.heartRate = (uint16_t(bpmArrTwo[0]) << 8); 286 | libBpm.heartRate |= (bpmArrTwo[1]); 287 | libBpm.heartRate /= 10; 288 | 289 | // Confidence formatting 290 | libBpm.confidence = bpmArrTwo[2]; 291 | 292 | // Blood oxygen level formatting 293 | libBpm.oxygen = uint16_t(bpmArrTwo[3]) << 8; 294 | libBpm.oxygen |= bpmArrTwo[4]; 295 | libBpm.oxygen /= 10.0; 296 | 297 | //"Machine State" - has a finger been detected? 298 | libBpm.status = bpmArrTwo[5]; 299 | 300 | // Sp02 r Value formatting 301 | uint16_t tempVal = uint16_t(bpmArrTwo[6]) << 8; 302 | tempVal |= bpmArrTwo[7]; 303 | libBpm.rValue = tempVal; 304 | libBpm.rValue /= 10.0; 305 | 306 | // Extended Machine State formatting 307 | libBpm.extStatus = bpmArrTwo[8]; 308 | 309 | // There are two additional bytes of data that were requested but that 310 | // have not been implemented in firmware 10.1 so will not be saved to 311 | // user's data. 312 | return libBpm; 313 | } 314 | 315 | else 316 | { 317 | libBpm.heartRate = 0; 318 | libBpm.confidence = 0; 319 | libBpm.oxygen = 0; 320 | return libBpm; 321 | } 322 | } 323 | 324 | // This function takes 9 bytes of LED values from the MAX30101 associated with 325 | // the RED, IR, and GREEN LEDs. In addition it gets the 8 bytes from the FIFO buffer 326 | // related to the wrist heart rate algortihm: heart rate (uint16_t), confidence (uint8_t), 327 | // SpO2 (uint16_t), and the finger detected status (uint8_t). Note that the the algorithm 328 | // is stated as "wrist" though the sensor only works with the finger. The data is loaded 329 | // into the whrmFifo and returned. 330 | bioData SparkFun_Bio_Sensor_Hub::readSensor() 331 | { 332 | 333 | bioData libLedFifo; 334 | readFillArray(READ_DATA_OUTPUT, READ_DATA, MAX30101_LED_ARRAY, senArr); 335 | 336 | // Value of LED one.... 337 | libLedFifo.irLed = uint32_t(senArr[0]) << 16; 338 | libLedFifo.irLed |= uint32_t(senArr[1]) << 8; 339 | libLedFifo.irLed |= senArr[2]; 340 | 341 | // Value of LED two... 342 | libLedFifo.redLed = uint32_t(senArr[3]) << 16; 343 | libLedFifo.redLed |= uint32_t(senArr[4]) << 8; 344 | libLedFifo.redLed |= senArr[5]; 345 | 346 | return libLedFifo; 347 | } 348 | 349 | // This function takes the information of both the LED value and the biometric 350 | // data from the MAX32664's FIFO. In essence it combines the two functions 351 | // above into a single function call. 352 | bioData SparkFun_Bio_Sensor_Hub::readSensorBpm() 353 | { 354 | 355 | bioData libLedBpm; 356 | 357 | if (_userSelectedMode == MODE_ONE) 358 | { 359 | 360 | readFillArray(READ_DATA_OUTPUT, READ_DATA, MAXFAST_ARRAY_SIZE + MAX30101_LED_ARRAY, bpmSenArr); 361 | 362 | // Value of LED one.... 363 | libLedBpm.irLed = uint32_t(bpmSenArr[0]) << 16; 364 | libLedBpm.irLed |= uint32_t(bpmSenArr[1]) << 8; 365 | libLedBpm.irLed |= bpmSenArr[2]; 366 | 367 | // Value of LED two... 368 | libLedBpm.redLed = uint32_t(bpmSenArr[3]) << 16; 369 | libLedBpm.redLed |= uint32_t(bpmSenArr[4]) << 8; 370 | libLedBpm.redLed |= bpmSenArr[5]; 371 | 372 | // -- What happened here? -- There are two uint32_t values that are given by 373 | // the sensor for LEDs that do not exists on the MAX30101. So we have to 374 | // request those empty values because they occupy the buffer: 375 | // bpmSenArr[6-11]. 376 | 377 | // Heart rate formatting 378 | libLedBpm.heartRate = (uint16_t(bpmSenArr[12]) << 8); 379 | libLedBpm.heartRate |= (bpmSenArr[13]); 380 | libLedBpm.heartRate /= 10; 381 | 382 | // Confidence formatting 383 | libLedBpm.confidence = bpmSenArr[14]; 384 | 385 | // Blood oxygen level formatting 386 | libLedBpm.oxygen = uint16_t(bpmSenArr[15]) << 8; 387 | libLedBpm.oxygen |= bpmSenArr[16]; 388 | libLedBpm.oxygen /= 10; 389 | 390 | //"Machine State" - has a finger been detected? 391 | libLedBpm.status = bpmSenArr[17]; 392 | return libLedBpm; 393 | } 394 | 395 | else if (_userSelectedMode == MODE_TWO) 396 | { 397 | 398 | readFillArray(READ_DATA_OUTPUT, READ_DATA, MAXFAST_ARRAY_SIZE + MAX30101_LED_ARRAY + MAXFAST_EXTENDED_DATA, 399 | bpmSenArrTwo); 400 | 401 | // Value of LED one.... 402 | libLedBpm.irLed = uint32_t(bpmSenArrTwo[0]) << 16; 403 | libLedBpm.irLed |= uint32_t(bpmSenArrTwo[1]) << 8; 404 | libLedBpm.irLed |= bpmSenArrTwo[2]; 405 | 406 | // Value of LED two... 407 | libLedBpm.redLed = uint32_t(bpmSenArrTwo[3]) << 16; 408 | libLedBpm.redLed |= uint32_t(bpmSenArrTwo[4]) << 8; 409 | libLedBpm.redLed |= bpmSenArrTwo[5]; 410 | 411 | // -- What happened here? -- There are two uint32_t values that are given by 412 | // the sensor for LEDs that do not exists on the MAX30101. So we have to 413 | // request those empty values because they occupy the buffer: 414 | // bpmSenArrTwo[6-11]. 415 | 416 | // Heart rate formatting 417 | libLedBpm.heartRate = (uint16_t(bpmSenArrTwo[12]) << 8); 418 | libLedBpm.heartRate |= (bpmSenArrTwo[13]); 419 | libLedBpm.heartRate /= 10; 420 | 421 | // Confidence formatting 422 | libLedBpm.confidence = bpmSenArrTwo[14]; 423 | 424 | // Blood oxygen level formatting 425 | libLedBpm.oxygen = uint16_t(bpmSenArrTwo[15]) << 8; 426 | libLedBpm.oxygen |= bpmSenArrTwo[16]; 427 | libLedBpm.oxygen /= 10; 428 | 429 | //"Machine State" - has a finger been detected? 430 | libLedBpm.status = bpmSenArrTwo[17]; 431 | 432 | // Sp02 r Value formatting 433 | uint16_t tempVal = uint16_t(bpmSenArrTwo[18]) << 8; 434 | tempVal |= bpmSenArrTwo[19]; 435 | libLedBpm.rValue = tempVal; 436 | libLedBpm.rValue /= 10.0; 437 | 438 | // Extended Machine State formatting 439 | libLedBpm.extStatus = bpmSenArrTwo[20]; 440 | 441 | // There are two additional bytes of data that were requested but that 442 | // have not been implemented in firmware 10.1 so will not be saved to 443 | // user's data. 444 | // 445 | return libLedBpm; 446 | } 447 | 448 | else 449 | { 450 | libLedBpm.irLed = 0; 451 | libLedBpm.redLed = 0; 452 | libLedBpm.heartRate = 0; 453 | libLedBpm.confidence = 0; 454 | libLedBpm.oxygen = 0; 455 | libLedBpm.status = 0; 456 | libLedBpm.rValue = 0; 457 | libLedBpm.extStatus = 0; 458 | return libLedBpm; 459 | } 460 | } 461 | // This function modifies the pulse width of the MAX30101 LEDs. All of the LEDs 462 | // are modified to the same width. This will affect the number of samples that 463 | // can be collected and will also affect the ADC resolution. 464 | // Default: 69us - 15 resolution - 50 samples per second. 465 | // Register: 0x0A, bits [1:0] 466 | // Width(us) - Resolution - Sample Rate 467 | // 69us - 15 - <= 3200 (fastest - least resolution) 468 | // 118us - 16 - <= 1600 469 | // 215us - 17 - <= 1600 470 | // 411us - 18 - <= 1000 (slowest - highest resolution) 471 | uint8_t SparkFun_Bio_Sensor_Hub::setPulseWidth(uint16_t width) 472 | { 473 | 474 | uint8_t bits; 475 | uint8_t regVal; 476 | 477 | // Make sure the correct pulse width is selected. 478 | if (width == 69) 479 | bits = 0; 480 | else if (width == 118) 481 | bits = 1; 482 | else if (width == 215) 483 | bits = 2; 484 | else if (width == 411) 485 | bits = 3; 486 | else 487 | return INCORR_PARAM; 488 | 489 | // Get current register value so that nothing is overwritten. 490 | regVal = readRegisterMAX30101(CONFIGURATION_REGISTER); 491 | regVal &= PULSE_MASK; // Mask bits to change. 492 | regVal |= bits; // Add bits 493 | writeRegisterMAX30101(CONFIGURATION_REGISTER, regVal); // Write Register 494 | 495 | return SFE_BIO_SUCCESS; 496 | } 497 | 498 | // This function reads the CONFIGURATION_REGISTER (0x0A), bits [1:0] from the 499 | // MAX30101 Sensor. It returns one of the four settings in microseconds. 500 | uint16_t SparkFun_Bio_Sensor_Hub::readPulseWidth() 501 | { 502 | 503 | uint8_t regVal; 504 | 505 | regVal = readRegisterMAX30101(CONFIGURATION_REGISTER); 506 | regVal &= READ_PULSE_MASK; 507 | 508 | if (regVal == 0) 509 | return 69; 510 | else if (regVal == 1) 511 | return 118; 512 | else if (regVal == 2) 513 | return 215; 514 | else if (regVal == 3) 515 | return 411; 516 | else 517 | return ERR_UNKNOWN; 518 | } 519 | 520 | // This function changes the sample rate of the MAX30101 sensor. The sample 521 | // rate is affected by the set pulse width of the MAX30101 LEDs. 522 | // Default: 69us - 15 resolution - 50 samples per second. 523 | // Register: 0x0A, bits [4:2] 524 | // Width(us) - Resolution - Sample Rate 525 | // 69us - 15 - <= 3200 (fastest - least resolution) 526 | // 118us - 16 - <= 1600 527 | // 215us - 17 - <= 1600 528 | // 411us - 18 - <= 1000 (slowest - highest resolution) 529 | uint8_t SparkFun_Bio_Sensor_Hub::setSampleRate(uint16_t sampRate) 530 | { 531 | 532 | uint8_t bits; 533 | uint8_t regVal; 534 | 535 | // Make sure the correct sample rate was picked 536 | if (sampRate == 50) 537 | bits = 0; 538 | else if (sampRate == 100) 539 | bits = 1; 540 | else if (sampRate == 200) 541 | bits = 2; 542 | else if (sampRate == 400) 543 | bits = 3; 544 | else if (sampRate == 800) 545 | bits = 4; 546 | else if (sampRate == 1000) 547 | bits = 5; 548 | else if (sampRate == 1600) 549 | bits = 6; 550 | else if (sampRate == 3200) 551 | bits = 7; 552 | else 553 | return INCORR_PARAM; 554 | 555 | // Get current register value so that nothing is overwritten. 556 | regVal = readRegisterMAX30101(CONFIGURATION_REGISTER); 557 | regVal &= SAMP_MASK; // Mask bits to change. 558 | regVal |= (bits << 2); // Add bits but shift them first to correct position. 559 | writeRegisterMAX30101(CONFIGURATION_REGISTER, regVal); // Write Register 560 | 561 | return SFE_BIO_SUCCESS; 562 | } 563 | 564 | // This function reads the CONFIGURATION_REGISTER (0x0A), bits [4:2] from the 565 | // MAX30101 Sensor. It returns one of the 8 possible sample rates. 566 | uint16_t SparkFun_Bio_Sensor_Hub::readSampleRate() 567 | { 568 | 569 | uint8_t regVal; 570 | 571 | regVal = readRegisterMAX30101(CONFIGURATION_REGISTER); 572 | regVal &= READ_SAMP_MASK; 573 | regVal = (regVal >> 2); 574 | 575 | if (regVal == 0) 576 | return 50; 577 | else if (regVal == 1) 578 | return 100; 579 | else if (regVal == 2) 580 | return 200; 581 | else if (regVal == 3) 582 | return 400; 583 | else if (regVal == 4) 584 | return 800; 585 | else if (regVal == 5) 586 | return 1000; 587 | else if (regVal == 6) 588 | return 1600; 589 | else if (regVal == 7) 590 | return 3200; 591 | else 592 | return ERR_UNKNOWN; 593 | } 594 | 595 | // MAX30101 Register: CONFIGURATION_REGISTER (0x0A), bits [6:5] 596 | // This functions sets the dynamic range of the MAX30101's ADC. The function 597 | // accepts the higher range as a parameter. 598 | // Default Range: 7.81pA - 2048nA 599 | // Possible Ranges: 600 | // 7.81pA - 2048nA 601 | // 15.63pA - 4096nA 602 | // 32.25pA - 8192nA 603 | // 62.5pA - 16384nA 604 | uint8_t SparkFun_Bio_Sensor_Hub::setAdcRange(uint16_t adcVal) 605 | { 606 | 607 | uint8_t regVal; 608 | uint8_t bits; 609 | 610 | if (adcVal <= 2048) 611 | bits = 0; 612 | else if (adcVal <= 4096) 613 | bits = 1; 614 | else if (adcVal <= 8192) 615 | bits = 2; 616 | else if (adcVal <= 16384) 617 | bits = 3; 618 | else 619 | return INCORR_PARAM; 620 | 621 | regVal = readRegisterMAX30101(CONFIGURATION_REGISTER); 622 | regVal &= ADC_MASK; 623 | regVal |= bits << 5; 624 | 625 | writeRegisterMAX30101(CONFIGURATION_REGISTER, regVal); 626 | 627 | return SFE_BIO_SUCCESS; 628 | } 629 | 630 | // MAX30101 Register: CONFIGURATION_REGISTER (0x0A), bits [6:5] 631 | // This function returns the set ADC range of the MAX30101 sensor. 632 | uint16_t SparkFun_Bio_Sensor_Hub::readAdcRange() 633 | { 634 | 635 | uint8_t regVal; 636 | regVal = readRegisterMAX30101(CONFIGURATION_REGISTER); 637 | regVal &= READ_ADC_MASK; 638 | regVal = (regVal >> 5); // Shift our bits to the front of the line. 639 | 640 | if (regVal == 0) 641 | return 2048; 642 | else if (regVal == 1) 643 | return 4096; 644 | else if (regVal == 2) 645 | return 8192; 646 | else if (regVal == 3) 647 | return 16384; 648 | else 649 | return ERR_UNKNOWN; 650 | } 651 | 652 | // Family Byte: SET_DEVICE_MODE (0x01), Index Byte: 0x01, Write Byte: 0x00 653 | // The following function is an alternate way to set the mode of the of 654 | // MAX32664. It can take three parameters: Enter and Exit Bootloader Mode, as 655 | // well as reset. 656 | // INCOMPLETE 657 | uint8_t SparkFun_Bio_Sensor_Hub::setOperatingMode(uint8_t selection) 658 | { 659 | 660 | // Must be one of the three.... 661 | if (selection == EXIT_BOOTLOADER || selection == SFE_BIO_RESET || selection == ENTER_BOOTLOADER) 662 | { 663 | } 664 | else 665 | return INCORR_PARAM; 666 | 667 | uint8_t statusByte = writeByte(SET_DEVICE_MODE, 0x00, selection); 668 | if (statusByte != SFE_BIO_SUCCESS) 669 | return statusByte; 670 | 671 | // Here we'll check if the board made it into Bootloader mode... 672 | uint8_t responseByte = readByte(READ_DEVICE_MODE, 0x00); // 0x00 only possible Index Byte 673 | return responseByte; // This is in fact the status byte, need second returned byte - bootloader mode 674 | } 675 | 676 | // Family Byte: IDENTITY (0x01), Index Byte: READ_MCU_TYPE, Write Byte: NONE 677 | // The following function returns a byte that signifies the microcontoller that 678 | // is in communcation with your host microcontroller. Returns 0x00 for the 679 | // MAX32625 and 0x01 for the MAX32660/MAX32664. 680 | uint8_t SparkFun_Bio_Sensor_Hub::getMcuType() 681 | { 682 | 683 | uint8_t returnByte = readByte(IDENTITY, READ_MCU_TYPE, NO_WRITE); 684 | if (returnByte != SFE_BIO_SUCCESS) 685 | return ERR_UNKNOWN; 686 | else 687 | return returnByte; 688 | } 689 | 690 | // Family Byte: BOOTLOADER_INFO (0x80), Index Byte: BOOTLOADER_VERS (0x00) 691 | // This function checks the version number of the bootloader on the chip and 692 | // returns a four bytes: Major version Byte, Minor version Byte, Space Byte, 693 | // and the Revision Byte. 694 | int32_t SparkFun_Bio_Sensor_Hub::getBootloaderInf() 695 | { 696 | 697 | int32_t bootVers = 0; 698 | const size_t sizeOfRev = 4; 699 | int32_t revNum[sizeOfRev] = {}; 700 | uint8_t status = readMultipleBytes(BOOTLOADER_INFO, BOOTLOADER_VERS, 0x00, 4, revNum); 701 | 702 | if (!status) 703 | return ERR_UNKNOWN; 704 | else 705 | { 706 | bootVers |= (int32_t(revNum[1]) << 16); 707 | bootVers |= (int32_t(revNum[2]) << 8); 708 | bootVers |= revNum[3]; 709 | return bootVers; 710 | } 711 | } 712 | 713 | // Family Byte: ENABLE_SENSOR (0x44), Index Byte: ENABLE_MAX30101 (0x03), Write 714 | // Byte: senSwitch (parameter - 0x00 or 0x01). 715 | // This function enables the MAX30101. 716 | uint8_t SparkFun_Bio_Sensor_Hub::max30101Control(uint8_t senSwitch) 717 | { 718 | 719 | if (senSwitch == 0 || senSwitch == 1) 720 | { 721 | } 722 | else 723 | return INCORR_PARAM; 724 | 725 | // Check that communication was successful, not that the sensor is enabled. 726 | uint8_t statusByte = enableWrite(ENABLE_SENSOR, ENABLE_MAX30101, senSwitch); 727 | if (statusByte != SFE_BIO_SUCCESS) 728 | return statusByte; 729 | else 730 | return SFE_BIO_SUCCESS; 731 | } 732 | 733 | // Family Byte: READ_SENSOR_MODE (0x45), Index Byte: READ_ENABLE_MAX30101 (0x03) 734 | // This function checks if the MAX30101 is enabled or not. 735 | uint8_t SparkFun_Bio_Sensor_Hub::readMAX30101State() 736 | { 737 | 738 | uint8_t state = readByte(READ_SENSOR_MODE, READ_ENABLE_MAX30101); 739 | return state; 740 | } 741 | 742 | // Family Byte: ENABLE_SENSOR (0x44), Index Byte: ENABLE_ACCELEROMETER (0x04), Write 743 | // Byte: accepts (parameter - 0x00 or 0x01). 744 | // This function enables the Accelerometer. 745 | uint8_t SparkFun_Bio_Sensor_Hub::accelControl(uint8_t accelSwitch) 746 | { 747 | 748 | if (accelSwitch != 0 || accelSwitch != 1) 749 | { 750 | } 751 | else 752 | return INCORR_PARAM; 753 | 754 | // Check that communication was successful, not that the sensor is enabled. 755 | uint8_t statusByte = enableWrite(ENABLE_SENSOR, ENABLE_ACCELEROMETER, accelSwitch); 756 | if (statusByte != SFE_BIO_SUCCESS) 757 | return statusByte; 758 | else 759 | return SFE_BIO_SUCCESS; 760 | } 761 | 762 | // Family Byte: OUTPUT_MODE (0x10), Index Byte: SET_FORMAT (0x00), 763 | // Write Byte : outputType (Parameter values in OUTPUT_MODE_WRITE_BYTE) 764 | uint8_t SparkFun_Bio_Sensor_Hub::setOutputMode(uint8_t outputType) 765 | { 766 | 767 | if (outputType > SENSOR_ALGO_COUNTER) // Bytes between 0x00 and 0x07 768 | return INCORR_PARAM; 769 | 770 | // Check that communication was successful, not that the IC is outputting 771 | // correct format. 772 | uint8_t statusByte = writeByte(OUTPUT_MODE, SET_FORMAT, outputType); 773 | if (statusByte != SFE_BIO_SUCCESS) 774 | return statusByte; 775 | else 776 | return SFE_BIO_SUCCESS; 777 | } 778 | 779 | // Family Byte: OUTPUT_MODE(0x10), Index Byte: WRITE_SET_THRESHOLD (0x01), Write byte: intThres 780 | // (parameter - value betwen 0 and 0xFF). 781 | // This function changes the threshold for the FIFO interrupt bit/pin. The 782 | // interrupt pin is the MFIO pin which is set to INPUT after IC initialization 783 | // (begin). 784 | uint8_t SparkFun_Bio_Sensor_Hub::setFifoThreshold(uint8_t intThresh) 785 | { 786 | 787 | // Checks that there was succesful communcation, not that the threshold was 788 | // set correctly. 789 | uint8_t statusByte = writeByte(OUTPUT_MODE, WRITE_SET_THRESHOLD, intThresh); 790 | if (statusByte != SFE_BIO_SUCCESS) 791 | return statusByte; 792 | else 793 | return SFE_BIO_SUCCESS; 794 | } 795 | 796 | // Family Byte: READ_DATA_OUTPUT (0x12), Index Byte: NUM_SAMPLES (0x00), Write 797 | // Byte: NONE 798 | // This function returns the number of samples available in the FIFO. 799 | uint8_t SparkFun_Bio_Sensor_Hub::numSamplesOutFifo() 800 | { 801 | 802 | uint8_t sampAvail = readByte(READ_DATA_OUTPUT, NUM_SAMPLES); 803 | return sampAvail; 804 | } 805 | 806 | // Family Byte: READ_DATA_OUTPUT (0x12), Index Byte: READ_DATA (0x01), Write 807 | // Byte: NONE 808 | // This function returns the data in the FIFO. 809 | uint8_t *SparkFun_Bio_Sensor_Hub::getDataOutFifo(uint8_t data[]) 810 | { 811 | 812 | uint8_t samples = numSamplesOutFifo(); 813 | readFillArray(READ_DATA_OUTPUT, READ_DATA, samples, data); 814 | return data; 815 | } 816 | 817 | // Family Byte: READ_DATA_INPUT (0x13), Index Byte: FIFO_EXTERNAL_INDEX_BYTE (0x00), Write 818 | // Byte: NONE 819 | // This function adds support for the acceleromter that is NOT included on 820 | // SparkFun's product, The Family Registery of 0x13 and 0x14 is skipped for now. 821 | uint8_t SparkFun_Bio_Sensor_Hub::numSamplesExternalSensor() 822 | { 823 | 824 | uint8_t sampAvail = readByte(READ_DATA_INPUT, SAMPLE_SIZE, WRITE_ACCELEROMETER); 825 | return sampAvail; 826 | } 827 | 828 | // Family Byte: WRITE_REGISTER (0x40), Index Byte: WRITE_MAX30101 (0x03), Write Bytes: 829 | // Register Address and Register Value 830 | // This function writes the given register value at the given register address 831 | // for the MAX30101 sensor and returns a boolean indicating a successful or 832 | // non-successful write. 833 | void SparkFun_Bio_Sensor_Hub::writeRegisterMAX30101(uint8_t regAddr, uint8_t regVal) 834 | { 835 | 836 | writeByte(WRITE_REGISTER, WRITE_MAX30101, regAddr, regVal); 837 | } 838 | 839 | // Family Byte: WRITE_REGISTER (0x40), Index Byte: WRITE_ACCELEROMETER (0x04), Write Bytes: 840 | // Register Address and Register Value 841 | // This function writes the given register value at the given register address 842 | // for the Accelerometer and returns a boolean indicating a successful or 843 | // non-successful write. 844 | void SparkFun_Bio_Sensor_Hub::writeRegisterAccel(uint8_t regAddr, uint8_t regVal) 845 | { 846 | 847 | writeByte(WRITE_REGISTER, WRITE_ACCELEROMETER, regAddr, regVal); 848 | } 849 | 850 | // Family Byte: READ_REGISTER (0x41), Index Byte: READ_MAX30101 (0x03), Write Byte: 851 | // Register Address 852 | // This function reads the given register address for the MAX30101 Sensor and 853 | // returns the values at that register. 854 | uint8_t SparkFun_Bio_Sensor_Hub::readRegisterMAX30101(uint8_t regAddr) 855 | { 856 | 857 | uint8_t regCont = readByte(READ_REGISTER, READ_MAX30101, regAddr); 858 | return regCont; 859 | } 860 | 861 | // Family Byte: READ_REGISTER (0x41), Index Byte: READ_ACCELEROMETER (0x04), Write Byte: 862 | // Register Address 863 | // This function reads the given register address for the MAX30101 Sensor and 864 | // returns the values at that register. 865 | uint8_t SparkFun_Bio_Sensor_Hub::readRegisterAccel(uint8_t regAddr) 866 | { 867 | 868 | uint8_t regCont = readByte(READ_REGISTER, READ_ACCELEROMETER, regAddr); 869 | return regCont; 870 | } 871 | 872 | // Family Byte: READ_ATTRIBUTES_AFE (0x42), Index Byte: RETRIEVE_AFE_MAX30101 (0x03) 873 | // This function retrieves the attributes of the AFE (Analog Front End) of the 874 | // MAX30101 sensor. It returns the number of bytes in a word for the sensor 875 | // and the number of registers available. 876 | sensorAttr SparkFun_Bio_Sensor_Hub::getAfeAttributesMAX30101() 877 | { 878 | 879 | sensorAttr maxAttr; 880 | uint8_t tempArray[2]{}; 881 | 882 | readFillArray(READ_ATTRIBUTES_AFE, RETRIEVE_AFE_MAX30101, 2, tempArray); 883 | 884 | maxAttr.byteWord = tempArray[0]; 885 | maxAttr.availRegisters = tempArray[1]; 886 | 887 | return maxAttr; 888 | } 889 | 890 | // Family Byte: READ_ATTRIBUTES_AFE (0x42), Index Byte: 891 | // RETRIEVE_AFE_ACCELEROMETER (0x04) 892 | // This function retrieves the attributes of the AFE (Analog Front End) of the 893 | // Accelerometer. It returns the number of bytes in a word for the sensor 894 | // and the number of registers available. 895 | sensorAttr SparkFun_Bio_Sensor_Hub::getAfeAttributesAccelerometer() 896 | { 897 | 898 | sensorAttr maxAttr; 899 | uint8_t tempArray[2]{}; 900 | 901 | readFillArray(READ_ATTRIBUTES_AFE, RETRIEVE_AFE_ACCELEROMETER, 2, tempArray); 902 | 903 | maxAttr.byteWord = tempArray[0]; 904 | maxAttr.availRegisters = tempArray[1]; 905 | 906 | return maxAttr; 907 | } 908 | 909 | // Family Byte: DUMP_REGISTERS (0x43), Index Byte: DUMP_REGISTER_MAX30101 (0x03) 910 | // This function returns all registers and register values sequentially of the 911 | // MAX30101 sensor: register zero and register value zero to register n and 912 | // register value n. There are 36 registers in this case. 913 | uint8_t SparkFun_Bio_Sensor_Hub::dumpRegisterMAX30101(uint8_t regArray[]) 914 | { 915 | 916 | uint8_t numOfBytes = 36; 917 | uint8_t status = readFillArray(DUMP_REGISTERS, DUMP_REGISTER_MAX30101, numOfBytes, regArray); 918 | return status; 919 | } 920 | 921 | // Family Byte: DUMP_REGISTERS (0x43), Index Byte: DUMP_REGISTER_ACCELEROMETER (0x04) 922 | // This function returns all registers and register values sequentially of the 923 | // Accelerometer: register zero and register value zero to register n and 924 | // register value n. 925 | uint8_t SparkFun_Bio_Sensor_Hub::dumpRegisterAccelerometer(uint8_t numReg, uint8_t regArray[]) 926 | { 927 | 928 | uint8_t status = readFillArray(DUMP_REGISTERS, DUMP_REGISTER_ACCELEROMETER, numReg, regArray); // Fake read amount 929 | return status; 930 | } 931 | 932 | // Family Byte: CHANGE_ALGORITHM_CONFIG (0x50), Index Byte: 933 | // SET_TARG_PERC (0x00), Write Byte: AGC_GAIN_ID (0x00) 934 | // This function sets the target percentage of the full-scale ADC range that 935 | // the automatic gain control algorithm uses. It takes a paramater of zero to 936 | // 100 percent. 937 | uint8_t SparkFun_Bio_Sensor_Hub::setAlgoRange(uint8_t perc) 938 | { 939 | 940 | if (perc > 100) 941 | return INCORR_PARAM; 942 | 943 | // Successful communication or no? 944 | uint8_t statusByte = writeByte(CHANGE_ALGORITHM_CONFIG, SET_TARG_PERC, AGC_GAIN_ID, perc); 945 | if (statusByte != SFE_BIO_SUCCESS) 946 | return statusByte; 947 | else 948 | return SFE_BIO_SUCCESS; 949 | } 950 | 951 | // Family Byte: CHANGE_ALGORITHM_CONFIG (0x50), Index Byte: 952 | // SET_STEP_SIZE (0x00), Write Byte: AGC_STEP_SIZE_ID (0x01) 953 | // This function changes the step size toward the target for the AGC algorithm. 954 | // It takes a paramater of zero to 100 percent. 955 | uint8_t SparkFun_Bio_Sensor_Hub::setAlgoStepSize(uint8_t step) 956 | { 957 | 958 | if (step > 100) 959 | return INCORR_PARAM; 960 | 961 | // Successful communication or no? 962 | uint8_t statusByte = writeByte(CHANGE_ALGORITHM_CONFIG, SET_STEP_SIZE, AGC_STEP_SIZE_ID, step); 963 | if (statusByte != SFE_BIO_SUCCESS) 964 | return statusByte; 965 | else 966 | return SFE_BIO_SUCCESS; 967 | } 968 | 969 | // Family Byte: CHANGE_ALGORITHM_CONFIG (0x50), Index Byte: 970 | // SET_SENSITIVITY (0x00), Write Byte: AGC_SENSITIVITY_ID (0x02) 971 | // This function changes the sensitivity of the AGC algorithm. 972 | uint8_t SparkFun_Bio_Sensor_Hub::setAlgoSensitivity(uint8_t sense) 973 | { 974 | 975 | if (sense > 100) 976 | return INCORR_PARAM; 977 | 978 | // Successful communication or no? 979 | uint8_t statusByte = writeByte(CHANGE_ALGORITHM_CONFIG, SET_SENSITIVITY, AGC_SENSITIVITY_ID, sense); 980 | if (statusByte != SFE_BIO_SUCCESS) 981 | return statusByte; 982 | else 983 | return SFE_BIO_SUCCESS; 984 | } 985 | 986 | // Family Byte: CHANGE_ALGORITHM_CONFIG (0x50), Index Byte: 987 | // SET_AVG_SAMPLES (0x00), Write Byte: AGC_NUM_SAMP_ID (0x03) 988 | // This function changes the number of samples that are averaged. 989 | // It takes a paramater of zero to 255. 990 | uint8_t SparkFun_Bio_Sensor_Hub::setAlgoSamples(uint8_t avg) 991 | { 992 | 993 | // Successful communication or no? 994 | uint8_t statusByte = writeByte(CHANGE_ALGORITHM_CONFIG, SET_AVG_SAMPLES, AGC_NUM_SAMP_ID, avg); 995 | if (statusByte != SFE_BIO_SUCCESS) 996 | return statusByte; 997 | else 998 | return SFE_BIO_SUCCESS; 999 | } 1000 | 1001 | // Family Byte: CHANGE_ALGORITHM_CONFIG (0x50), Index Byte: 1002 | // SET_PULSE_OX_COEF (0x02), Write Byte: MAXIMFAST_COEF_ID (0x0B) 1003 | // This function takes three values that are used as the Sp02 coefficients. 1004 | // These three values are multiplied by 100,000; 1005 | // default values are in order: 159584, -3465966, and 11268987. 1006 | uint8_t SparkFun_Bio_Sensor_Hub::setMaximFastCoef(int32_t coef1, int32_t coef2, int32_t coef3) 1007 | { 1008 | 1009 | const size_t numCoefVals = 3; 1010 | int32_t coefArr[numCoefVals] = {coef1, coef2, coef3}; 1011 | 1012 | uint8_t statusByte = 1013 | writeLongBytes(CHANGE_ALGORITHM_CONFIG, SET_PULSE_OX_COEF, MAXIMFAST_COEF_ID, coefArr, numCoefVals); 1014 | if (statusByte != SFE_BIO_SUCCESS) 1015 | return statusByte; 1016 | else 1017 | return SFE_BIO_SUCCESS; 1018 | } 1019 | 1020 | // Family Byte: READ_ALGORITHM_CONFIG (0x51), Index Byte: 1021 | // READ_AGC_PERCENTAGE (0x00), Write Byte: READ_AGC_PERC_ID (0x00) 1022 | // This function reads and returns the currently set target percentage 1023 | // of the full-scale ADC range that the Automatic Gain Control algorithm is using. 1024 | uint8_t SparkFun_Bio_Sensor_Hub::readAlgoRange() 1025 | { 1026 | 1027 | uint8_t range = readByte(READ_ALGORITHM_CONFIG, READ_AGC_PERCENTAGE, READ_AGC_PERC_ID); 1028 | return range; 1029 | } 1030 | 1031 | // Family Byte: READ_ALGORITHM_CONFIG (0x51), Index Byte: 1032 | // READ_AGC_STEP_SIZE (0x00), Write Byte: READ_AGC_STEP_SIZE_ID (0x01) 1033 | // This function returns the step size toward the target for the AGC algorithm. 1034 | // It returns a value between zero and 100 percent. 1035 | uint8_t SparkFun_Bio_Sensor_Hub::readAlgoStepSize() 1036 | { 1037 | 1038 | uint8_t stepSize = readByte(READ_ALGORITHM_CONFIG, READ_AGC_STEP_SIZE, READ_AGC_STEP_SIZE_ID); 1039 | return stepSize; 1040 | } 1041 | 1042 | // Family Byte: READ_ALGORITHM_CONFIG (0x51), Index Byte: 1043 | // READ_AGC_SENSITIVITY_ID (0x00), Write Byte: READ_AGC_SENSITIVITY_ID (0x02) 1044 | // This function returns the sensitivity (percentage) of the automatic gain control. 1045 | uint8_t SparkFun_Bio_Sensor_Hub::readAlgoSensitivity() 1046 | { 1047 | 1048 | uint8_t sensitivity = readByte(READ_ALGORITHM_CONFIG, READ_AGC_SENSITIVITY, READ_AGC_SENSITIVITY_ID); 1049 | return sensitivity; 1050 | } 1051 | 1052 | // Family Byte: READ_ALGORITHM_CONFIG (0x51), Index Byte: 1053 | // READ_AGC_NUM_SAMPLES (0x00), Write Byte: READ_AGC_NUM_SAMPLES_ID (0x03) 1054 | // This function changes the number of samples that are averaged. 1055 | // It takes a paramater of zero to 255. 1056 | uint8_t SparkFun_Bio_Sensor_Hub::readAlgoSamples() 1057 | { 1058 | 1059 | uint8_t samples = readByte(READ_ALGORITHM_CONFIG, READ_AGC_NUM_SAMPLES, READ_AGC_NUM_SAMPLES_ID); 1060 | return samples; 1061 | } 1062 | 1063 | // Family Byte: READ_ALGORITHM_CONFIG (0x51), Index Byte: 1064 | // READ_MAX_FAST_COEF (0x02), Write Byte: READ_MAX_FAST_COEF_ID (0x0B) 1065 | // This function reads the maximum age for the wrist heart rate monitor 1066 | // (WHRM) algorithm. It returns three uint32_t integers that are 1067 | // multiplied by 100,000. 1068 | uint8_t SparkFun_Bio_Sensor_Hub::readMaximFastCoef(int32_t coefArr[3]) 1069 | { 1070 | 1071 | const size_t numOfReads = 3; 1072 | uint8_t status = 1073 | readMultipleBytes(READ_ALGORITHM_CONFIG, READ_MAX_FAST_COEF, READ_MAX_FAST_COEF_ID, numOfReads, coefArr); 1074 | coefArr[0] = coefArr[0] * 100000; 1075 | coefArr[1] = coefArr[1] * 100000; 1076 | coefArr[2] = coefArr[2] * 100000; 1077 | return status; 1078 | } 1079 | 1080 | // Family Byte: ENABLE_ALGORITHM (0x52), Index Byte: 1081 | // ENABLE_AGC_ALGO (0x00) 1082 | // This function enables (one) or disables (zero) the automatic gain control algorithm. 1083 | uint8_t SparkFun_Bio_Sensor_Hub::agcAlgoControl(uint8_t enable) 1084 | { 1085 | 1086 | if (enable == 0 || enable == 1) 1087 | { 1088 | } 1089 | else 1090 | return INCORR_PARAM; 1091 | 1092 | uint8_t statusByte = enableWrite(ENABLE_ALGORITHM, ENABLE_AGC_ALGO, enable); 1093 | if (statusByte != SFE_BIO_SUCCESS) 1094 | return statusByte; 1095 | else 1096 | return SFE_BIO_SUCCESS; 1097 | } 1098 | 1099 | // Family Byte: ENABLE_ALGORITHM (0x52), Index Byte: 1100 | // ENABLE_WHRM_ALGO (0x02) 1101 | // This function enables (one) or disables (zero) the wrist heart rate monitor 1102 | // algorithm. 1103 | uint8_t SparkFun_Bio_Sensor_Hub::maximFastAlgoControl(uint8_t mode) 1104 | { 1105 | 1106 | if (mode == 0 || mode == 1 || mode == 2) 1107 | { 1108 | } 1109 | else 1110 | return INCORR_PARAM; 1111 | 1112 | uint8_t statusByte = enableWrite(ENABLE_ALGORITHM, ENABLE_WHRM_ALGO, mode); 1113 | if (statusByte != SFE_BIO_SUCCESS) 1114 | return statusByte; 1115 | else 1116 | return SFE_BIO_SUCCESS; 1117 | } 1118 | 1119 | // Family Byte: BOOTLOADER_FLASH (0x80), Index Byte: SET_INIT_VECTOR_BYTES (0x00) 1120 | // void SparkFun_Bio_Sensor_Hub::setInitBytes 1121 | 1122 | // Family Byte: BOOTLOADER_FLASH (0x80), Index Byte: SET_AUTH_BYTES (0x01) 1123 | 1124 | // Family Byte: BOOTLOADER_FLASH (0x80), Index Byte: SET_NUM_PAGES (0x02), 1125 | // Write Bytes: 0x00 - Number of pages at byte 0x44 from .msbl file. 1126 | bool SparkFun_Bio_Sensor_Hub::setNumPages(uint8_t totalPages) 1127 | { 1128 | 1129 | uint8_t statusByte = writeByte(BOOTLOADER_FLASH, SET_NUM_PAGES, 0x00, totalPages); 1130 | return statusByte; 1131 | } 1132 | 1133 | // Family Byte: BOOTLOADER_FLASH (0x80), Index Byte: ERASE_FLASH (0x03) 1134 | // Returns true on successful communication. 1135 | bool SparkFun_Bio_Sensor_Hub::eraseFlash() 1136 | { 1137 | 1138 | // This is a unique write in that it does not have a relevant write byte. 1139 | _i2cPort->beginTransmission(_address); 1140 | _i2cPort->write(BOOTLOADER_FLASH); 1141 | _i2cPort->write(ERASE_FLASH); 1142 | _i2cPort->endTransmission(); 1143 | delay(CMD_DELAY); 1144 | 1145 | _i2cPort->requestFrom(_address, static_cast(1)); 1146 | uint8_t statusByte = _i2cPort->read(); 1147 | if (!statusByte) 1148 | return true; 1149 | else 1150 | return false; 1151 | } 1152 | 1153 | // Family Byte: BOOTLOADER_INFO (0x81), Index Byte: BOOTLOADER_VERS (0x00) 1154 | version SparkFun_Bio_Sensor_Hub::readBootloaderVers() 1155 | { 1156 | 1157 | version booVers; // BOO! 1158 | _i2cPort->beginTransmission(_address); 1159 | _i2cPort->write(BOOTLOADER_INFO); 1160 | _i2cPort->write(BOOTLOADER_VERS); 1161 | _i2cPort->endTransmission(); 1162 | delay(CMD_DELAY); 1163 | 1164 | _i2cPort->requestFrom(_address, static_cast(4)); 1165 | uint8_t statusByte = _i2cPort->read(); 1166 | if (statusByte) 1167 | { // Pass through if SFE_BIO_SUCCESS (0x00). 1168 | booVers.major = 0; 1169 | booVers.minor = 0; 1170 | booVers.revision = 0; 1171 | return booVers; 1172 | } 1173 | 1174 | booVers.major = _i2cPort->read(); 1175 | booVers.minor = _i2cPort->read(); 1176 | booVers.revision = _i2cPort->read(); 1177 | 1178 | return booVers; 1179 | } 1180 | 1181 | // Family Byte: IDENTITY (0xFF), Index Byte: READ_SENSOR_HUB_VERS (0x03) 1182 | version SparkFun_Bio_Sensor_Hub::readSensorHubVersion() 1183 | { 1184 | 1185 | version bioHubVers; 1186 | _i2cPort->beginTransmission(_address); 1187 | _i2cPort->write(IDENTITY); 1188 | _i2cPort->write(READ_SENSOR_HUB_VERS); 1189 | _i2cPort->endTransmission(); 1190 | delay(CMD_DELAY); 1191 | 1192 | _i2cPort->requestFrom(_address, static_cast(4)); 1193 | uint8_t statusByte = _i2cPort->read(); 1194 | if (statusByte) 1195 | { // Pass through if SFE_BIO_SUCCESS (0x00). 1196 | bioHubVers.major = 0; 1197 | bioHubVers.minor = 0; 1198 | bioHubVers.revision = 0; 1199 | return bioHubVers; 1200 | } 1201 | 1202 | bioHubVers.major = _i2cPort->read(); 1203 | bioHubVers.minor = _i2cPort->read(); 1204 | bioHubVers.revision = _i2cPort->read(); 1205 | 1206 | return bioHubVers; 1207 | } 1208 | 1209 | // Family Byte: IDENTITY (0xFF), Index Byte: READ_ALGO_VERS (0x07) 1210 | version SparkFun_Bio_Sensor_Hub::readAlgorithmVersion() 1211 | { 1212 | 1213 | version libAlgoVers; 1214 | _i2cPort->beginTransmission(_address); 1215 | _i2cPort->write(IDENTITY); 1216 | _i2cPort->write(READ_ALGO_VERS); 1217 | _i2cPort->endTransmission(); 1218 | delay(CMD_DELAY); 1219 | 1220 | _i2cPort->requestFrom(_address, static_cast(4)); 1221 | uint8_t statusByte = _i2cPort->read(); 1222 | if (statusByte) 1223 | { // Pass through if SFE_BIO_SUCCESS (0x00). 1224 | libAlgoVers.major = 0; 1225 | libAlgoVers.minor = 0; 1226 | libAlgoVers.revision = 0; 1227 | return libAlgoVers; 1228 | } 1229 | 1230 | libAlgoVers.major = _i2cPort->read(); 1231 | libAlgoVers.minor = _i2cPort->read(); 1232 | libAlgoVers.revision = _i2cPort->read(); 1233 | 1234 | return libAlgoVers; 1235 | } 1236 | 1237 | // ------------------Function Below for MAX32664 Version D (Blood Pressure) ---- 1238 | 1239 | // Family Byte: CHANGE_ALGORITHM_CONFIG (0x50), Index Byte: BPT_CONFIG (0x04), 1240 | // Write Byte: BPT_MEDICATION (0x00) 1241 | uint8_t SparkFun_Bio_Sensor_Hub::isPatientBPMedication(uint8_t medication) 1242 | { 1243 | 1244 | if (medication != 0x01 || medication != 0x00) 1245 | return INCORR_PARAM; 1246 | 1247 | uint8_t status = writeByte(CHANGE_ALGORITHM_CONFIG, BPT_CONFIG, BPT_MEDICATION, medication); 1248 | return status; 1249 | } 1250 | 1251 | // Family Byte: CHANGE_ALGORITHM_CONFIG (0x50), Index Byte: BPT_CONFIG (0x04), 1252 | // Write Byte: BPT_MEDICATION (0x00) 1253 | uint8_t SparkFun_Bio_Sensor_Hub::isPatientBPMedication() 1254 | { 1255 | 1256 | uint8_t medication = readByte(CHANGE_ALGORITHM_CONFIG, BPT_CONFIG, BPT_MEDICATION); 1257 | return medication; 1258 | } 1259 | 1260 | // Family Byte: CHANGE_ALGORITHM_CONFIG (0x50), Index Byte: BPT_CONFIG (0x04), 1261 | // Write Byte: SYSTOLIC_VALUE (0x01) 1262 | uint8_t SparkFun_Bio_Sensor_Hub::writeSystolicVals(uint8_t sysVal1, uint8_t sysVal2, uint8_t sysVal3) 1263 | { 1264 | 1265 | const size_t numSysVals = 3; 1266 | uint8_t sysVals[numSysVals] = {sysVal1, sysVal2, sysVal3}; 1267 | uint8_t status = writeBytes(CHANGE_ALGORITHM_CONFIG, BPT_CONFIG, SYSTOLIC_VALUE, sysVals, numSysVals); 1268 | 1269 | return status; 1270 | } 1271 | 1272 | // Family Byte: CHANGE_ALGORITHM_CONFIG (0x50), Index Byte: BPT_CONFIG (0x04), 1273 | // Write Byte: SYSTOLIC_VALUE (0x01) 1274 | uint8_t SparkFun_Bio_Sensor_Hub::readSystolicVals(uint8_t userArray[]) 1275 | { 1276 | 1277 | const size_t numSysVals = 3; 1278 | uint8_t status = readMultipleBytes(CHANGE_ALGORITHM_CONFIG, BPT_CONFIG, SYSTOLIC_VALUE, numSysVals, userArray); 1279 | 1280 | return status; 1281 | } 1282 | 1283 | // Family Byte: CHANGE_ALGORITHM_CONFIG (0x50), Index Byte: BPT_CONFIG (0x04), 1284 | // Write Byte: DIASTOLIC_VALUE (0x02) 1285 | uint8_t SparkFun_Bio_Sensor_Hub::writeDiastolicVals(uint8_t diasVal1, uint8_t diasVal2, uint8_t diasVal3) 1286 | { 1287 | 1288 | const size_t numDiasVals = 3; 1289 | uint8_t diasVals[numDiasVals] = {diasVal1, diasVal2, diasVal3}; 1290 | uint8_t status = writeBytes(CHANGE_ALGORITHM_CONFIG, BPT_CONFIG, DIASTOLIC_VALUE, diasVals, numDiasVals); 1291 | 1292 | return status; 1293 | } 1294 | 1295 | // Family Byte: CHANGE_ALGORITHM_CONFIG (0x50), Index Byte: BPT_CONFIG (0x04), 1296 | // Write Byte: DIASTOLIC_VALUE (0x02) 1297 | uint8_t SparkFun_Bio_Sensor_Hub::readDiastolicVals(uint8_t userArray[]) 1298 | { 1299 | 1300 | const size_t numDiasVals = 3; 1301 | uint8_t status = readMultipleBytes(CHANGE_ALGORITHM_CONFIG, BPT_CONFIG, DIASTOLIC_VALUE, numDiasVals, userArray); 1302 | return status; 1303 | } 1304 | 1305 | // Family Byte: CHANGE_ALGORITHM_CONFIG (0x50), Index Byte: BPT_CONFIG (0x04), 1306 | // Write Byte: BPT_CALIB_DATA (0x03) 1307 | uint8_t SparkFun_Bio_Sensor_Hub::writeBPTAlgoData(uint8_t bptCalibData[]) 1308 | { 1309 | 1310 | const size_t numCalibVals = 824; 1311 | uint8_t status = writeBytes(CHANGE_ALGORITHM_CONFIG, BPT_CONFIG, BPT_CALIB_DATA, bptCalibData, numCalibVals); 1312 | return status; 1313 | } 1314 | 1315 | // Family Byte: CHANGE_ALGORITHM_CONFIG (0x50), Index Byte: BPT_CONFIG (0x04), 1316 | // Write Byte: BPT_CALIB_DATA (0x03) 1317 | uint8_t SparkFun_Bio_Sensor_Hub::readBPTAlgoData(uint8_t userArray[]) 1318 | { 1319 | 1320 | const size_t numCalibVals = 824; 1321 | uint8_t status = readMultipleBytes(CHANGE_ALGORITHM_CONFIG, BPT_CONFIG, BPT_CALIB_DATA, numCalibVals, userArray); 1322 | return status; 1323 | } 1324 | 1325 | // Family Byte: CHANGE_ALGORITHM_CONFIG (0x50), Index Byte: BPT_CONFIG (0x04), 1326 | // Write Byte: PATIENT_RESTING (0x05) 1327 | uint8_t SparkFun_Bio_Sensor_Hub::isPatientResting(uint8_t resting) 1328 | { // 1329 | 1330 | if (resting != 0x00 || resting != 0x01) 1331 | return INCORR_PARAM; 1332 | 1333 | uint8_t status = writeByte(CHANGE_ALGORITHM_CONFIG, BPT_CONFIG, PATIENT_RESTING, resting); 1334 | return status; 1335 | } 1336 | 1337 | // Family Byte: CHANGE_ALGORITHM_CONFIG (0x50), Index Byte: BPT_CONFIG (0x04), 1338 | // Write Byte: PATIENT_RESTING (0x05) 1339 | uint8_t SparkFun_Bio_Sensor_Hub::isPatientResting() 1340 | { 1341 | 1342 | uint8_t resting = writeByte(CHANGE_ALGORITHM_CONFIG, BPT_CONFIG, PATIENT_RESTING); 1343 | return resting; 1344 | } 1345 | 1346 | // Family Byte: CHANGE_ALGORITHM_CONFIG (0x50), Index Byte: BPT_CONFIG (0x04), 1347 | // Write Byte: AGC_SP02_COEFS (0x0B) 1348 | uint8_t SparkFun_Bio_Sensor_Hub::writeSP02AlgoCoef(int32_t intA, int32_t intB, int32_t intC) 1349 | { 1350 | 1351 | const size_t numCoefVals = 3; 1352 | int32_t coefVals[numCoefVals] = {intA, intB, intC}; 1353 | uint8_t status = writeLongBytes(CHANGE_ALGORITHM_CONFIG, BPT_CONFIG, AGC_SP02_COEFS, coefVals, numCoefVals); 1354 | return status; 1355 | } 1356 | 1357 | // Family Byte: CHANGE_ALGORITHM_CONFIG (0x50), Index Byte: BPT_CONFIG (0x04), 1358 | // Write Byte: AGC_SP02_COEFS (0x0B) 1359 | uint8_t SparkFun_Bio_Sensor_Hub::readSP02AlgoCoef(int32_t userArray[]) 1360 | { // Have the user provide their own array here and pass the pointer to it 1361 | 1362 | const size_t numOfReads = 3; 1363 | uint8_t status = readMultipleBytes(CHANGE_ALGORITHM_CONFIG, BPT_CONFIG, AGC_SP02_COEFS, numOfReads, userArray); 1364 | return status; 1365 | } 1366 | 1367 | //-------------------Private Functions----------------------- 1368 | 1369 | // This function uses the given family, index, and write byte to enable 1370 | // the given sensor. 1371 | uint8_t SparkFun_Bio_Sensor_Hub::enableWrite(uint8_t _familyByte, uint8_t _indexByte, uint8_t _enableByte) 1372 | { 1373 | 1374 | _i2cPort->beginTransmission(_address); 1375 | _i2cPort->write(_familyByte); 1376 | _i2cPort->write(_indexByte); 1377 | _i2cPort->write(_enableByte); 1378 | _i2cPort->endTransmission(); 1379 | 1380 | if (_familyByte == ENABLE_SENSOR && _indexByte == ENABLE_MAX30101) 1381 | delay(ENABLE_CMD_DELAY); 1382 | if (_familyByte == ENABLE_ALGORITHM && _indexByte == ENABLE_AGC_ALGO) 1383 | delay(ALGO_CMD_DELAY_SHORT); 1384 | if (_familyByte == ENABLE_ALGORITHM && _indexByte == ENABLE_WHRM_ALGO) 1385 | delay(ALGO_CMD_DELAY_LONG); 1386 | 1387 | // Status Byte, success or no? 0x00 is a successful transmit 1388 | _i2cPort->requestFrom(_address, static_cast(1)); 1389 | uint8_t statusByte = _i2cPort->read(); 1390 | return statusByte; 1391 | } 1392 | 1393 | // This function uses the given family, index, and write byte to communicate 1394 | // with the MAX32664 which in turn communicates with downward sensors. There 1395 | // are two steps demonstrated in this function. First a write to the MCU 1396 | // indicating what you want to do, a delay, and then a read to confirm positive 1397 | // transmission. 1398 | uint8_t SparkFun_Bio_Sensor_Hub::writeByte(uint8_t _familyByte, uint8_t _indexByte, uint8_t _writeByte) 1399 | { 1400 | 1401 | _i2cPort->beginTransmission(_address); 1402 | _i2cPort->write(_familyByte); 1403 | _i2cPort->write(_indexByte); 1404 | _i2cPort->write(_writeByte); 1405 | _i2cPort->endTransmission(); 1406 | delay(CMD_DELAY); 1407 | 1408 | // Status Byte, success or no? 0x00 is a successful transmit 1409 | _i2cPort->requestFrom(_address, static_cast(1)); 1410 | uint8_t statusByte = _i2cPort->read(); 1411 | return statusByte; 1412 | } 1413 | 1414 | // This function is the same as the function above and uses the given family, 1415 | // index, and write byte, but also takes a 16 bit integer as a paramter to communicate 1416 | // with the MAX32664 which in turn communicates with downward sensors. There 1417 | // are two steps demonstrated in this function. First a write to the MCU 1418 | // indicating what you want to do, a delay, and then a read to confirm positive 1419 | // transmission. 1420 | uint8_t SparkFun_Bio_Sensor_Hub::writeByte(uint8_t _familyByte, uint8_t _indexByte, uint8_t _writeByte, uint16_t _val) 1421 | { 1422 | 1423 | _i2cPort->beginTransmission(_address); 1424 | _i2cPort->write(_familyByte); 1425 | _i2cPort->write(_indexByte); 1426 | _i2cPort->write(_writeByte); 1427 | _i2cPort->write((_val >> 8)); // MSB 1428 | _i2cPort->write(_val); // LSB 1429 | _i2cPort->endTransmission(); 1430 | delay(CMD_DELAY); 1431 | 1432 | // Status Byte, success or no? 0x00 is a successful transmit 1433 | _i2cPort->requestFrom(_address, static_cast(1)); 1434 | uint8_t statusByte = _i2cPort->read(); 1435 | return statusByte; 1436 | } 1437 | 1438 | // This function sends information to the MAX32664 to specifically write values 1439 | // to the registers of downward sensors and so also requires a 1440 | // register address and register value as parameters. Again there is the write 1441 | // of the specific bytes followed by a read to confirm positive transmission. 1442 | uint8_t SparkFun_Bio_Sensor_Hub::writeByte(uint8_t _familyByte, uint8_t _indexByte, uint8_t _writeByte, 1443 | uint8_t _writeVal) 1444 | { 1445 | 1446 | _i2cPort->beginTransmission(_address); 1447 | _i2cPort->write(_familyByte); 1448 | _i2cPort->write(_indexByte); 1449 | _i2cPort->write(_writeByte); 1450 | _i2cPort->write(_writeVal); 1451 | _i2cPort->endTransmission(); 1452 | delay(CMD_DELAY); 1453 | 1454 | // Status Byte, 0x00 is a successful transmit. 1455 | _i2cPort->requestFrom(_address, static_cast(1)); 1456 | uint8_t statusByte = _i2cPort->read(); 1457 | return statusByte; 1458 | } 1459 | 1460 | // This function sends information to the MAX32664 to specifically write values 1461 | // to the registers of downward sensors and so also requires a 1462 | // register address and register value as parameters. Again there is the write 1463 | // of the specific bytes followed by a read to confirm positive transmission. 1464 | uint8_t SparkFun_Bio_Sensor_Hub::writeLongBytes(uint8_t _familyByte, uint8_t _indexByte, uint8_t _writeByte, 1465 | int32_t _writeVal[], const size_t _size) 1466 | { 1467 | 1468 | _i2cPort->beginTransmission(_address); 1469 | _i2cPort->write(_familyByte); 1470 | _i2cPort->write(_indexByte); 1471 | _i2cPort->write(_writeByte); 1472 | 1473 | for (size_t i = 0; i < _size; i++) 1474 | { 1475 | _i2cPort->write(_writeVal[i] >> 24); 1476 | _i2cPort->write(_writeVal[i] >> 16); 1477 | _i2cPort->write(_writeVal[i] >> 8); 1478 | _i2cPort->write(_writeVal[i]); 1479 | } 1480 | 1481 | _i2cPort->endTransmission(); 1482 | delay(CMD_DELAY); 1483 | 1484 | // Status Byte, 0x00 is a successful transmit. 1485 | _i2cPort->requestFrom(_address, static_cast(1)); 1486 | uint8_t statusByte = _i2cPort->read(); 1487 | return statusByte; 1488 | } 1489 | 1490 | // This function sends information to the MAX32664 to specifically write values 1491 | // to the registers of downward sensors and so also requires a 1492 | // register address and register value as parameters. Again there is the write 1493 | // of the specific bytes followed by a read to confirm positive transmission. 1494 | uint8_t SparkFun_Bio_Sensor_Hub::writeBytes(uint8_t _familyByte, uint8_t _indexByte, uint8_t _writeByte, 1495 | uint8_t _writeVal[], size_t _size) 1496 | { 1497 | 1498 | _i2cPort->beginTransmission(_address); 1499 | _i2cPort->write(_familyByte); 1500 | _i2cPort->write(_indexByte); 1501 | _i2cPort->write(_writeByte); 1502 | 1503 | for (size_t i = 0; i < _size; i++) 1504 | { 1505 | _i2cPort->write(_writeVal[i]); 1506 | } 1507 | 1508 | _i2cPort->endTransmission(); 1509 | delay(CMD_DELAY); 1510 | 1511 | // Status Byte, 0x00 is a successful transmit. 1512 | _i2cPort->requestFrom(_address, static_cast(1)); 1513 | uint8_t statusByte = _i2cPort->read(); 1514 | return statusByte; 1515 | } 1516 | // This function handles all read commands or stated another way, all information 1517 | // requests. It starts a request by writing the family byte an index byte, and 1518 | // then delays 60 microseconds, during which the MAX32664 retrieves the requested 1519 | // information. An I-squared-C request is then issued, and the information is read. 1520 | uint8_t SparkFun_Bio_Sensor_Hub::readByte(uint8_t _familyByte, uint8_t _indexByte) 1521 | { 1522 | 1523 | uint8_t returnByte; 1524 | uint8_t statusByte; 1525 | 1526 | _i2cPort->beginTransmission(_address); 1527 | _i2cPort->write(_familyByte); 1528 | _i2cPort->write(_indexByte); 1529 | _i2cPort->endTransmission(); 1530 | delay(CMD_DELAY); 1531 | 1532 | _i2cPort->requestFrom(_address, static_cast(sizeof(returnByte) + sizeof(statusByte))); 1533 | statusByte = _i2cPort->read(); 1534 | if (statusByte) // SFE_BIO_SUCCESS (0x00) - how do I know its 1535 | return statusByte; // Return the error, see: READ_STATUS_BYTE_VALUE 1536 | 1537 | returnByte = _i2cPort->read(); 1538 | return returnByte; // If good then return the actual byte. 1539 | } 1540 | 1541 | // This function is exactly as the one above except it accepts also receives a 1542 | // Write Byte as a parameter. It starts a request by writing the family byte, index byte, and 1543 | // write byte to the MAX32664 and then delays 60 microseconds, during which 1544 | // the MAX32664 retrieves the requested information. A I-squared-C request is 1545 | // then issued, and the information is read. 1546 | uint8_t SparkFun_Bio_Sensor_Hub::readByte(uint8_t _familyByte, uint8_t _indexByte, uint8_t _writeByte) 1547 | { 1548 | 1549 | uint8_t returnByte; 1550 | uint8_t statusByte; 1551 | 1552 | _i2cPort->beginTransmission(_address); 1553 | _i2cPort->write(_familyByte); 1554 | _i2cPort->write(_indexByte); 1555 | _i2cPort->write(_writeByte); 1556 | _i2cPort->endTransmission(); 1557 | delay(CMD_DELAY); 1558 | 1559 | _i2cPort->requestFrom(_address, static_cast(sizeof(returnByte) + sizeof(statusByte))); 1560 | statusByte = _i2cPort->read(); 1561 | if (statusByte) // SFE_BIO_SUCCESS (0x00) 1562 | return statusByte; // Return the error, see: READ_STATUS_BYTE_VALUE 1563 | 1564 | returnByte = _i2cPort->read(); 1565 | return returnByte; // If good then return the actual byte. 1566 | } 1567 | 1568 | uint8_t SparkFun_Bio_Sensor_Hub::readFillArray(uint8_t _familyByte, uint8_t _indexByte, uint8_t _numOfReads, 1569 | uint8_t array[]) 1570 | { 1571 | 1572 | uint8_t statusByte; 1573 | 1574 | _i2cPort->beginTransmission(_address); 1575 | _i2cPort->write(_familyByte); 1576 | _i2cPort->write(_indexByte); 1577 | _i2cPort->endTransmission(); 1578 | delay(CMD_DELAY); 1579 | 1580 | _i2cPort->requestFrom(_address, static_cast(_numOfReads + sizeof(statusByte))); 1581 | statusByte = _i2cPort->read(); 1582 | if (statusByte) 1583 | { // SFE_BIO_SUCCESS: 0x00 1584 | for (size_t i = 0; i < _numOfReads; i++) 1585 | { 1586 | array[i] = 0; 1587 | } 1588 | return statusByte; 1589 | } 1590 | 1591 | for (size_t i = 0; i < _numOfReads; i++) 1592 | { 1593 | array[i] = _i2cPort->read(); 1594 | } 1595 | return statusByte; 1596 | } 1597 | // This function handles all read commands or stated another way, all information 1598 | // requests. It starts a request by writing the family byte, an index byte, and 1599 | // a write byte and then then delays 60 microseconds, during which the MAX32664 1600 | // retrieves the requested information. An I-squared-C request is then issued, 1601 | // and the information is read. This differs from the above read commands in 1602 | // that it returns a 16 bit integer instead of a single byte. 1603 | uint16_t SparkFun_Bio_Sensor_Hub::readIntByte(uint8_t _familyByte, uint8_t _indexByte, uint8_t _writeByte) 1604 | { 1605 | 1606 | uint16_t returnByte; 1607 | uint8_t statusByte; 1608 | 1609 | _i2cPort->beginTransmission(_address); 1610 | _i2cPort->write(_familyByte); 1611 | _i2cPort->write(_indexByte); 1612 | _i2cPort->write(_writeByte); 1613 | _i2cPort->endTransmission(); 1614 | delay(CMD_DELAY); 1615 | 1616 | _i2cPort->requestFrom(_address, static_cast(sizeof(returnByte) + sizeof(statusByte))); 1617 | statusByte = _i2cPort->read(); 1618 | if (statusByte) // Pass through if SFE_BIO_SUCCESS (0x00). 1619 | return statusByte; // Return the error, see: READ_STATUS_BYTE_VALUE 1620 | 1621 | returnByte = (_i2cPort->read() << 8); 1622 | returnByte |= _i2cPort->read(); 1623 | 1624 | return returnByte; 1625 | } 1626 | 1627 | // This function handles all read commands or stated another way, all information 1628 | // requests. It starts a request by writing the family byte, an index byte, and 1629 | // a write byte and then then delays 60 microseconds, during which the MAX32664 1630 | // retrieves the requested information. An I-squared-C request is then issued, 1631 | // and the information is read. This function is very similar to the one above 1632 | // except it returns three uint32_t bytes instead of one. 1633 | uint8_t SparkFun_Bio_Sensor_Hub::readMultipleBytes(uint8_t _familyByte, uint8_t _indexByte, uint8_t _writeByte, 1634 | const size_t _numOfReads, int32_t userArray[]) 1635 | { 1636 | 1637 | uint8_t statusByte; 1638 | 1639 | _i2cPort->beginTransmission(_address); 1640 | _i2cPort->write(_familyByte); 1641 | _i2cPort->write(_indexByte); 1642 | _i2cPort->write(_writeByte); 1643 | _i2cPort->endTransmission(); 1644 | delay(CMD_DELAY); 1645 | 1646 | _i2cPort->requestFrom(_address, static_cast(sizeof(int32_t) * _numOfReads + sizeof(statusByte))); 1647 | statusByte = _i2cPort->read(); 1648 | if (statusByte) // Pass through if SFE_BIO_SUCCESS (0x00). 1649 | return statusByte; 1650 | else 1651 | { 1652 | for (size_t i = 0; i < (sizeof(int32_t) * _numOfReads); i++) 1653 | { 1654 | userArray[i] = _i2cPort->read() << 24; 1655 | userArray[i] |= _i2cPort->read() << 16; 1656 | userArray[i] |= _i2cPort->read() << 8; 1657 | userArray[i] |= _i2cPort->read(); 1658 | } 1659 | return statusByte; 1660 | } 1661 | } 1662 | 1663 | // This function handles all read commands or stated another way, all information 1664 | // requests. It starts a request by writing the family byte, an index byte, and 1665 | // a write byte and then then delays 60 microseconds, during which the MAX32664 1666 | // retrieves the requested information. An I-squared-C request is then issued, 1667 | // and the information is read. This function is very similar to the one above 1668 | // except it returns three uint32_t bytes instead of one. 1669 | uint8_t SparkFun_Bio_Sensor_Hub::readMultipleBytes(uint8_t _familyByte, uint8_t _indexByte, uint8_t _writeByte, 1670 | const size_t _numOfReads, uint8_t userArray[]) 1671 | { 1672 | 1673 | uint8_t statusByte; 1674 | 1675 | _i2cPort->beginTransmission(_address); 1676 | _i2cPort->write(_familyByte); 1677 | _i2cPort->write(_indexByte); 1678 | _i2cPort->write(_writeByte); 1679 | _i2cPort->endTransmission(); 1680 | delay(CMD_DELAY); 1681 | 1682 | _i2cPort->requestFrom(_address, static_cast(_numOfReads + sizeof(statusByte))); 1683 | statusByte = _i2cPort->read(); 1684 | if (statusByte) // Pass through if SFE_BIO_SUCCESS (0x00). 1685 | return statusByte; 1686 | else 1687 | { 1688 | for (size_t i = 0; i < _numOfReads; i++) 1689 | { 1690 | userArray[i] = _i2cPort->read(); 1691 | } 1692 | return statusByte; 1693 | } 1694 | } 1695 | -------------------------------------------------------------------------------- /src/SparkFun_Bio_Sensor_Hub_Library.h: -------------------------------------------------------------------------------- 1 | #ifndef _SPARKFUN_BIO_SENSOR_HUB_LIBRARY_H_ 2 | #define _SPARKFUN_BIO_SENSOR_HUB_LIBRARY_H_ 3 | 4 | #include 5 | #include 6 | 7 | #define WRITE_FIFO_INPUT_BYTE 0x04 8 | #define DISABLE 0x00 9 | #define ENABLE 0x01 10 | #define MODE_ONE 0x01 11 | #define MODE_TWO 0x02 12 | #define APP_MODE 0x00 13 | #define BOOTLOADER_MODE 0x08 14 | #define NO_WRITE 0x00 15 | #define INCORR_PARAM 0xEE 16 | 17 | #define CONFIGURATION_REGISTER 0x0A 18 | #define PULSE_MASK 0xFC 19 | #define READ_PULSE_MASK 0x03 20 | #define SAMP_MASK 0xE3 21 | #define READ_SAMP_MASK 0x1C 22 | #define ADC_MASK 0x9F 23 | #define READ_ADC_MASK 0x60 24 | 25 | #define ENABLE_CMD_DELAY 45 // Milliseconds 26 | #define ALGO_CMD_DELAY_SHORT 45 // Milliseconds 27 | #define ALGO_CMD_DELAY_LONG 45 // Milliseconds 28 | #define CMD_DELAY 2 // Milliseconds 29 | #define MAXFAST_ARRAY_SIZE 6 // Number of bytes.... 30 | #define MAXFAST_EXTENDED_DATA 5 31 | #define MAX30101_LED_ARRAY 12 // 4 values of 24 bit (3 byte) LED values 32 | 33 | #define SET_FORMAT 0x00 34 | #define READ_FORMAT 0x01 // Index Byte under Family Byte: READ_OUTPUT_MODE (0x11) 35 | #define WRITE_SET_THRESHOLD 0x01 // Index Byte for WRITE_INPUT(0x14) 36 | #define WRITE_EXTERNAL_TO_FIFO 0x00 37 | 38 | const uint8_t BIO_ADDRESS = 0x55; 39 | 40 | struct bioData 41 | { 42 | 43 | uint32_t irLed; 44 | uint32_t redLed; 45 | uint16_t heartRate; // LSB = 0.1bpm 46 | uint8_t confidence; // 0-100% LSB = 1% 47 | uint16_t oxygen; // 0-100% LSB = 1% 48 | uint8_t status; // 0: Success, 1: Not Ready, 2: Object Detectected, 3: Finger Detected 49 | float rValue; // -- Algorithm Mode 2 vv 50 | int8_t extStatus; // -- 51 | uint8_t reserveOne; // -- 52 | uint8_t resserveTwo; // -- Algorithm Mode 2 ^^ 53 | }; 54 | 55 | struct version 56 | { 57 | // 3 bytes total 58 | uint8_t major; 59 | uint8_t minor; 60 | uint8_t revision; 61 | }; 62 | 63 | struct sensorAttr 64 | { 65 | 66 | uint8_t byteWord; 67 | uint8_t availRegisters; 68 | }; 69 | 70 | // Status Bytes are communicated back after every I-squared-C transmission and 71 | // are indicators of success or failure of the previous transmission. 72 | enum READ_STATUS_BYTE_VALUE 73 | { 74 | 75 | SFE_BIO_SUCCESS = 0x00, 76 | ERR_UNAVAIL_CMD, 77 | ERR_UNAVAIL_FUNC, 78 | ERR_DATA_FORMAT, 79 | ERR_INPUT_VALUE, 80 | ERR_TRY_AGAIN, 81 | ERR_BTLDR_GENERAL = 0x80, 82 | ERR_BTLDR_CHECKSUM, 83 | ERR_BTLDR_AUTH, 84 | ERR_BTLDR_INVALID_APP, 85 | ERR_UNKNOWN = 0xFF 86 | }; 87 | 88 | // The family register bytes are the larger umbrella for all the Index and 89 | // Write Bytes listed below. You can not reference a nestled byte without first 90 | // referencing it's larger category: Family Register Byte. 91 | enum FAMILY_REGISTER_BYTES 92 | { 93 | 94 | HUB_STATUS = 0x00, 95 | SET_DEVICE_MODE, 96 | READ_DEVICE_MODE, 97 | OUTPUT_MODE = 0x10, 98 | READ_OUTPUT_MODE, 99 | READ_DATA_OUTPUT, 100 | READ_DATA_INPUT, 101 | WRITE_INPUT, 102 | WRITE_REGISTER = 0x40, 103 | READ_REGISTER, 104 | READ_ATTRIBUTES_AFE, 105 | DUMP_REGISTERS, 106 | ENABLE_SENSOR, 107 | READ_SENSOR_MODE, 108 | CHANGE_ALGORITHM_CONFIG = 0x50, 109 | READ_ALGORITHM_CONFIG, 110 | ENABLE_ALGORITHM, 111 | BOOTLOADER_FLASH = 0x80, 112 | BOOTLOADER_INFO, 113 | IDENTITY = 0xFF 114 | }; 115 | 116 | // All the defines below are: 1. Index Bytes nestled in the larger category of the 117 | // family registry bytes listed above and 2. The Write Bytes nestled even 118 | // farther under their Index Bytes. 119 | 120 | // Write Bytes under Family Byte: SET_DEVICE_MODE (0x01) and Index 121 | // Byte: 0x00. 122 | enum DEVICE_MODE_WRITE_BYTES 123 | { 124 | 125 | EXIT_BOOTLOADER = 0x00, 126 | SFE_BIO_RESET = 0x02, 127 | ENTER_BOOTLOADER = 0x08 128 | }; 129 | 130 | // Write Bytes under Family Byte: OUTPUT_MODE (0x10) and Index byte: SET_FORMAT 131 | // (0x00) 132 | enum OUTPUT_MODE_WRITE_BYTE 133 | { 134 | 135 | PAUSE = 0x00, 136 | SENSOR_DATA, 137 | ALGO_DATA, 138 | SENSOR_AND_ALGORITHM, 139 | PAUSE_TWO, 140 | SENSOR_COUNTER_BYTE, 141 | ALGO_COUNTER_BYTE, 142 | SENSOR_ALGO_COUNTER 143 | }; 144 | 145 | // Index Byte under the Family Byte: READ_DATA_OUTPUT (0x12) 146 | enum FIFO_OUTPUT_INDEX_BYTE 147 | { 148 | 149 | NUM_SAMPLES, 150 | READ_DATA 151 | }; 152 | 153 | // Index Byte under the Family Byte: READ_DATA_INPUT (0x13) 154 | enum FIFO_EXTERNAL_INDEX_BYTE 155 | { 156 | 157 | SAMPLE_SIZE, 158 | READ_INPUT_DATA, 159 | READ_SENSOR_DATA, // For external accelerometer 160 | READ_NUM_SAMPLES_INPUT, // For external accelerometer 161 | READ_NUM_SAMPLES_SENSOR 162 | }; 163 | 164 | // Index Byte under the Family Registry Byte: WRITE_REGISTER (0x40) 165 | enum WRITE_REGISTER_INDEX_BYTE 166 | { 167 | 168 | WRITE_MAX30101 = 0x03, 169 | WRITE_ACCELEROMETER 170 | }; 171 | 172 | // Index Byte under the Family Registry Byte: READ_REGISTER (0x41) 173 | enum READ_REGISTER_INDEX_BYTE 174 | { 175 | 176 | READ_MAX30101 = 0x03, 177 | READ_ACCELEROMETER 178 | }; 179 | 180 | // Index Byte under the Family Registry Byte: READ_ATTRIBUTES_AFE (0x42) 181 | enum GET_AFE_INDEX_BYTE 182 | { 183 | 184 | RETRIEVE_AFE_MAX30101 = 0x03, 185 | RETRIEVE_AFE_ACCELEROMETER 186 | }; 187 | 188 | // Index Byte under the Family Byte: DUMP_REGISTERS (0x43) 189 | enum DUMP_REGISTER_INDEX_BYTE 190 | { 191 | 192 | DUMP_REGISTER_MAX30101 = 0x03, 193 | DUMP_REGISTER_ACCELEROMETER 194 | }; 195 | 196 | // Index Byte under the Family Byte: ENABLE_SENSOR (0x44) 197 | enum SENSOR_ENABLE_INDEX_BYTE 198 | { 199 | 200 | ENABLE_MAX30101 = 0x03, 201 | ENABLE_ACCELEROMETER 202 | }; 203 | 204 | // Index Byte for the Family Byte: READ_SENSOR_MODE (0x45) 205 | enum READ_SENSOR_ENABLE_INDEX_BYTE 206 | { 207 | 208 | READ_ENABLE_MAX30101 = 0x03, 209 | READ_ENABLE_ACCELEROMETER 210 | }; 211 | 212 | // Index Byte under the Family Byte: CHANGE_ALGORITHM_CONFIG (0x50) 213 | enum ALGORITHM_CONFIG_INDEX_BYTE 214 | { 215 | 216 | SET_TARG_PERC = 0x00, 217 | SET_STEP_SIZE = 0x00, 218 | SET_SENSITIVITY = 0x00, 219 | SET_AVG_SAMPLES = 0x00, 220 | SET_PULSE_OX_COEF = 0x02, 221 | BPT_CONFIG = 0x04 222 | }; 223 | 224 | // Write Bytes under the Family Byte: CHANGE_ALGORITHM_CONFIG (0x50) and the 225 | // Index Byte: ALGORITHM_CONFIG_INDEX_BYTE - SET_TARG_PERC 226 | enum ALGO_AGC_WRITE_BYTE 227 | { 228 | 229 | AGC_GAIN_ID = 0x00, 230 | AGC_STEP_SIZE_ID, 231 | AGC_SENSITIVITY_ID, 232 | AGC_NUM_SAMP_ID, 233 | MAXIMFAST_COEF_ID = 0x0B 234 | }; 235 | 236 | enum ALGO_BPT_WRITE_BYTE 237 | { 238 | 239 | BPT_MEDICATION = 0x00, 240 | SYSTOLIC_VALUE, 241 | DIASTOLIC_VALUE, 242 | BPT_CALIB_DATA, // Index + 824 bytes of calibration data 243 | PATIENT_RESTING = 0x05, 244 | AGC_SP02_COEFS = 0x0B 245 | }; 246 | 247 | // Index Bytes under the Family Byte: READ_ALGORITHM_CONFIG (0x51) 248 | enum READ_ALGORITHM_INDEX_BYTE 249 | { 250 | 251 | READ_AGC_PERCENTAGE = 0x00, 252 | READ_AGC_STEP_SIZE = 0x00, 253 | READ_AGC_SENSITIVITY = 0x00, 254 | READ_AGC_NUM_SAMPLES = 0x00, 255 | READ_MAX_FAST_COEF = 0x02 256 | }; 257 | 258 | // Write Bytes under the Family Byte: READ_ALGORITHM_CONFIG (0x51) and Index Byte: 259 | // READ_ALGORITHM_INDEX_BYTE - AGC 260 | enum READ_AGC_ALGO_WRITE_BYTE 261 | { 262 | 263 | READ_AGC_PERC_ID = 0x00, 264 | READ_AGC_STEP_SIZE_ID, 265 | READ_AGC_SENSITIVITY_ID, 266 | READ_AGC_NUM_SAMPLES_ID, 267 | READ_MAX_FAST_COEF_ID = 0x0B 268 | }; 269 | 270 | // Index Byte under the Family Byte: ENABLE_ALGORITHM (0x52). 271 | enum ALGORITHM_MODE_ENABLE_INDEX_BYTE 272 | { 273 | 274 | ENABLE_AGC_ALGO = 0x00, 275 | ENABLE_WHRM_ALGO = 0x02 276 | }; 277 | 278 | // Index Byte under the Family Byte: BOOTLOADER_FLASH (0x80). 279 | enum BOOTLOADER_FLASH_INDEX_BYTE 280 | { 281 | 282 | SET_INIT_VECTOR_BYTES = 0x00, 283 | SET_AUTH_BYTES, 284 | SET_NUM_PAGES, 285 | ERASE_FLASH, 286 | SEND_PAGE_VALUE 287 | }; 288 | 289 | // Index Byte under the Family Byte: BOOTLOADER_INFO (0x81). 290 | enum BOOTLOADER_INFO_INDEX_BYTE 291 | { 292 | 293 | BOOTLOADER_VERS = 0x00, 294 | PAGE_SIZE 295 | }; 296 | 297 | // Index Byte under the Family Byte: IDENTITY (0xFF). 298 | enum IDENTITY_INDEX_BYTES 299 | { 300 | 301 | READ_MCU_TYPE = 0x00, 302 | READ_SENSOR_HUB_VERS = 0x03, 303 | READ_ALGO_VERS = 0x07 304 | }; 305 | 306 | class SparkFun_Bio_Sensor_Hub 307 | { 308 | public: 309 | // Variables ------------ 310 | uint8_t bpmArr[MAXFAST_ARRAY_SIZE]{}; 311 | uint8_t bpmArrTwo[MAXFAST_ARRAY_SIZE + MAXFAST_EXTENDED_DATA]{}; 312 | uint8_t senArr[MAX30101_LED_ARRAY]{}; 313 | uint8_t bpmSenArr[MAXFAST_ARRAY_SIZE + MAX30101_LED_ARRAY]{}; 314 | uint8_t bpmSenArrTwo[MAXFAST_ARRAY_SIZE + MAXFAST_EXTENDED_DATA + MAX30101_LED_ARRAY]{}; 315 | 316 | // Constructor ---------- 317 | SparkFun_Bio_Sensor_Hub(int resetPin = -1, int mfioPin = -1, uint8_t address = 0x55); 318 | 319 | // Functions ------------ 320 | 321 | // Family Byte: READ_DEVICE_MODE (0x02) Index Byte: 0x00, Write Byte: 0x00 322 | // The following function initializes the sensor. To place the MAX32664 into 323 | // application mode, the MFIO pin must be pulled HIGH while the board is held 324 | // in reset for 10ms. After 50 addtional ms have elapsed the board should be 325 | // in application mode and will return two bytes, the first 0x00 is a 326 | // successful communcation byte, followed by 0x00 which is the byte indicating 327 | // which mode the IC is in. 328 | uint8_t begin(TwoWire &wirePort = Wire, int resetPin = -1, int mfioPin = -1); 329 | 330 | // Family Byte: READ_DEVICE_MODE (0x02) Index Byte: 0x00, Write Byte: 0x00 331 | // The following function puts the MAX32664 into bootloader mode. To place the MAX32664 into 332 | // bootloader mode, the MFIO pin must be pulled LOW while the board is held 333 | // in reset for 10ms. After 50 addtional ms have elapsed the board should be 334 | // in bootloader mode and will return two bytes, the first 0x00 is a 335 | // successful communcation byte, followed by 0x08 which is the byte indicating 336 | // that the board is in bootloader mode. 337 | uint8_t beginBootloader(TwoWire &wirePort = Wire, int resetPin = -1, int mfioPin = -1); 338 | 339 | // Family Byte: HUB_STATUS (0x00), Index Byte: 0x00, No Write Byte. 340 | // The following function checks the status of the FIFO. 341 | uint8_t readSensorHubStatus(); 342 | 343 | // Family Byte: SET_DEVICE_MODE (0x01), Index Byte: 0x01, Write Byte: 0x00 344 | // The following function is an alternate way to set the mode of the of 345 | // MAX32664. It can take three parameters: Enter and Exit Bootloader Mode, as 346 | // well as reset. 347 | // INCOMPLETE 348 | uint8_t setOperatingMode(uint8_t); 349 | 350 | // This function sets very basic settings to get sensor and biometric data. 351 | // The biometric data includes data about heartrate, the confidence 352 | // level, SpO2 levels, and whether the sensor has detected a finger or not. 353 | uint8_t configBpm(uint8_t); 354 | 355 | // This function sets very basic settings to get LED count values from the MAX30101. 356 | // Sensor data includes 24 bit LED values for the three LED channels: Red, IR, 357 | // and Green. 358 | uint8_t configSensor(); 359 | 360 | // This function sets very basic settings to get sensor and biometric data. 361 | // Sensor data includes 24 bit LED values for the two LED channels: Red and IR. 362 | // The biometric data includes data about heartrate, the confidence 363 | // level, SpO2 levels, and whether the sensor has detected a finger or not. 364 | // Of note, the number of samples is set to one. 365 | uint8_t configSensorBpm(uint8_t); 366 | 367 | // This function takes the 8 bytes from the FIFO buffer related to the wrist 368 | // heart rate algortihm: heart rate (uint16_t), confidence (uint8_t) , SpO2 (uint16_t), 369 | // and the finger detected status (uint8_t). Note that the the algorithm is stated as 370 | // "wrist" though the sensor only works with the finger. The data is loaded 371 | // into the whrmFifo and returned. 372 | bioData readBpm(); 373 | 374 | // This function takes 9 bytes of LED values from the MAX30101 associated with 375 | // the RED, IR, and GREEN LEDs. In addition it gets the 8 bytes from the FIFO buffer 376 | // related to the wrist heart rate algortihm: heart rate (uint16_t), confidence (uint8_t), 377 | // SpO2 (uint16_t), and the finger detected status (uint8_t). Note that the the algorithm 378 | // is stated as "wrist" though the sensor only works with the finger. The data is loaded 379 | // into the whrmFifo and returned. 380 | bioData readSensor(); 381 | 382 | // This function takes the information of both the LED value and the biometric 383 | // data from the MAX32664's FIFO. In essence it combines the two functions 384 | // above into a single function call. 385 | bioData readSensorBpm(); 386 | 387 | // This function modifies the pulse width of the MAX30101 LEDs. All of the LEDs 388 | // are modified to the same width. This will affect the number of samples that 389 | // can be collected and will also affect the ADC resolution. 390 | // Width(us) - Resolution - Sample Rate 391 | // Default: 69us - 15 resolution - 50 samples per second. 392 | // 69us - 15 - <= 3200 (fastest - least resolution) 393 | // 118us - 16 - <= 1600 394 | // 215us - 17 - <= 1600 395 | // 411us - 18 - <= 1000 (slowest - highest resolution) 396 | uint8_t setPulseWidth(uint16_t); 397 | 398 | // This function reads the CONFIGURATION_REGISTER (0x0A), bits [1:0] from the 399 | // MAX30101 Sensor. It returns one of the four settings in microseconds. 400 | uint16_t readPulseWidth(); 401 | 402 | // This function changes the sample rate of the MAX30101 sensor. The sample 403 | // rate is affected by the set pulse width of the MAX30101 LEDs. 404 | // Default: 69us - 15 resolution - 50 samples per second. 405 | // Width(us) - Resolution - Sample Rate 406 | // 69us - 15 - <= 3200 (fastest - least resolution) 407 | // 118us - 16 - <= 1600 408 | // 215us - 17 - <= 1600 409 | // 411us - 18 - <= 1000 (slowest - highest resolution) 410 | // Samples Options: 411 | // 50, 100, 200, 400, 800, 1000, 1600, 3200 412 | uint8_t setSampleRate(uint16_t); 413 | 414 | // This function reads the CONFIGURATION_REGISTER (0x0A), bits [4:2] from the 415 | // MAX30101 Sensor. It returns one of the 8 possible sample rates. 416 | uint16_t readSampleRate(); 417 | 418 | // MAX30101 Register: CONFIGURATION_REGISTER (0x0A), bits [6:5] 419 | // This functions sets the dynamic range of the MAX30101's ADC. The function 420 | // accepts the higher range as a parameter. 421 | // Default Range: 7.81pA - 2048nA 422 | // Possible Ranges: 423 | // 7.81pA - 2048nA 424 | // 15.63pA - 4096nA 425 | // 32.25pA - 8192nA 426 | // 62.5pA - 16384nA 427 | uint8_t setAdcRange(uint16_t); 428 | 429 | // MAX30101 Register: CONFIGURATION_REGISTER (0x0A), bits [6:5] 430 | // This function returns the set ADC range of the MAX30101 sensor. 431 | uint16_t readAdcRange(); 432 | 433 | // Family Byte: IDENTITY (0x01), Index Byte: READ_MCU_TYPE, Write Byte: NONE 434 | // The following function returns a byte that signifies the microcontoller that 435 | // is in communcation with your host microcontroller. Returns 0x00 for the 436 | // MAX32625 and 0x01 for the MAX32660/MAX32664. 437 | uint8_t getMcuType(); 438 | 439 | // Family Byte: BOOTLOADER_INFO (0x80), Index Byte: BOOTLOADER_VERS (0x00) 440 | // This function checks the version number of the bootloader on the chip and 441 | // returns a four bytes: Major version Byte, Minor version Byte, Space Byte, 442 | // and the Revision Byte. 443 | int32_t getBootloaderInf(); 444 | 445 | // Family Byte: ENABLE_SENSOR (0x44), Index Byte: ENABLE_MAX30101 (0x03), Write 446 | // Byte: senSwitch (parameter - 0x00 or 0x01). 447 | // This function enables the MAX30101. 448 | uint8_t max30101Control(uint8_t); 449 | 450 | // Family Byte: READ_SENSOR_MODE (0x45), Index Byte: READ_ENABLE_MAX30101 (0x03) 451 | // This function checks if the MAX30101 is enabled or not. 452 | uint8_t readMAX30101State(); 453 | 454 | // Family Byte: ENABLE_SENSOR (0x44), Index Byte: ENABLE_ACCELEROMETER (0x04), Write 455 | // Byte: accelSwitch (parameter - 0x00 or 0x01). 456 | // This function enables the ACCELEROMETER. 457 | uint8_t accelControl(uint8_t); 458 | 459 | // Family Byte: OUTPUT_MODE (0x10), Index Byte: SET_FORMAT (0x00), 460 | // Write Byte : outputType (Parameter values in OUTPUT_MODE_WRITE_BYTE) 461 | uint8_t setOutputMode(uint8_t); 462 | 463 | // Family Byte: OUTPUT_MODE, Index Byte: WRITE_SET_THRESHOLD, Write byte: intThres 464 | // (parameter - value betwen 0 and 0xFF). 465 | // This function changes the threshold for the FIFO interrupt bit/pin. The 466 | // interrupt pin is the MFIO pin which is set to INPUT after IC initialization 467 | // (begin). 468 | uint8_t setFifoThreshold(uint8_t); 469 | 470 | // Family Byte: READ_DATA_OUTPUT (0x12), Index Byte: NUM_SAMPLES (0x00), Write 471 | // Byte: NONE 472 | // This function returns the number of samples available in the FIFO. 473 | uint8_t numSamplesOutFifo(); 474 | 475 | // Family Byte: READ_DATA_OUTPUT (0x12), Index Byte: READ_DATA (0x00), Write 476 | // Byte: NONE 477 | // This function returns the data in the FIFO. 478 | uint8_t *getDataOutFifo(uint8_t data[]); 479 | 480 | // Family Byte: READ_DATA_OUTPUT (0x12), Index Byte: READ_DATA (0x00), Write 481 | // Byte: NONE 482 | // This function adds support for the acceleromter that is NOT included on 483 | // SparkFun's product, The Family Registery of 0x13 and 0x14 is skipped for now. 484 | uint8_t numSamplesExternalSensor(); 485 | 486 | // Family Byte: WRITE_REGISTER (0x40), Index Byte: WRITE_MAX30101 (0x03), Write Bytes: 487 | // Register Address and Register Value 488 | // This function writes the given register value at the given register address 489 | // for the MAX30101 sensor and returns a boolean indicating a successful or 490 | // non-successful write. 491 | void writeRegisterMAX30101(uint8_t, uint8_t); 492 | 493 | // Family Byte: WRITE_REGISTER (0x40), Index Byte: WRITE_ACCELEROMETER (0x04), Write Bytes: 494 | // Register Address and Register Value 495 | // This function writes the given register value at the given register address 496 | // for the Accelerometer and returns a boolean indicating a successful or 497 | // non-successful write. 498 | void writeRegisterAccel(uint8_t, uint8_t); 499 | 500 | // Family Byte: READ_REGISTER (0x41), Index Byte: READ_MAX30101 (0x03), Write Byte: 501 | // Register Address 502 | // This function reads the given register address for the MAX30101 Sensor and 503 | // returns the values at that register. 504 | uint8_t readRegisterMAX30101(uint8_t); 505 | 506 | // Family Byte: READ_REGISTER (0x41), Index Byte: READ_MAX30101 (0x03), Write Byte: 507 | // Register Address 508 | // This function reads the given register address for the MAX30101 Sensor and 509 | // returns the values at that register. 510 | uint8_t readRegisterAccel(uint8_t); 511 | 512 | // Family Byte: READ_ATTRIBUTES_AFE (0x42), Index Byte: RETRIEVE_AFE_MAX30101/ (0x03) 513 | // This function retrieves the attributes of the AFE (Analog Front End) of the 514 | // MAX30101 sensor. It returns the number of bytes in a word for the sensor 515 | // and the number of registers available. 516 | sensorAttr getAfeAttributesMAX30101(); 517 | 518 | // Family Byte: READ_ATTRIBUTES_AFE (0x42), Index Byte: 519 | // RETRIEVE_AFE_ACCELEROMETER (0x04) 520 | // This function retrieves the attributes of the AFE (Analog Front End) of the 521 | // Accelerometer. It returns the number of bytes in a word for the sensor 522 | // and the number of registers available. 523 | sensorAttr getAfeAttributesAccelerometer(); 524 | 525 | // Family Byte: DUMP_REGISTERS (0x43), Index Byte: DUMP_REGISTER_MAX30101 (0x03) 526 | // This function returns all registers and register values sequentially of the 527 | // MAX30101 sensor: register zero and register value zero to register n and 528 | // register value n. There are 36 registers in this case. 529 | uint8_t dumpRegisterMAX30101(uint8_t regArray[]); 530 | 531 | // Family Byte: DUMP_REGISTERS (0x43), Index Byte: DUMP_REGISTER_ACCELEROMETER (0x04) 532 | // This function returns all registers and register values sequentially of the 533 | // Accelerometer: register zero and register value zero to register n and 534 | // register value n. 535 | uint8_t dumpRegisterAccelerometer(uint8_t, uint8_t regArray[]); 536 | 537 | // Family Byte: CHANGE_ALGORITHM_CONFIG (0x50), Index Byte: 538 | // SET_TARG_PERC (0x00), Write Byte: AGC_GAIN_ID (0x00) 539 | // This function sets the target percentage of the full-scale ADC range that 540 | // the automatic gain control algorithm uses. It takes a paramater of zero to 541 | // 100 percent. 542 | uint8_t setAlgoRange(uint8_t); 543 | 544 | // Family Byte: CHANGE_ALGORITHM_CONFIG (0x50), Index Byte: 545 | // SET_STEP_SIZE (0x00), Write Byte: AGC_STEP_SIZE_ID (0x01) 546 | // This function changes the step size toward the target for the AGC algorithm. 547 | // It takes a paramater of zero to 100 percent. 548 | uint8_t setAlgoStepSize(uint8_t); 549 | 550 | // Family Byte: CHANGE_ALGORITHM_CONFIG (0x50), Index Byte: 551 | // SET_SENSITIVITY (0x00), Write Byte: AGC_SENSITIVITY_ID (0x02) 552 | // This function changes the sensitivity of the AGC algorithm. 553 | uint8_t setAlgoSensitivity(uint8_t); 554 | 555 | // Family Byte: CHANGE_ALGORITHM_CONFIG (0x50), Index Byte: 556 | // SET_AVG_SAMPLES (0x00), Write Byte: AGC_NUM_SAMP_ID (0x03) 557 | // This function changes the number of samples that are averaged. 558 | // It takes a paramater of zero to 255. 559 | uint8_t setAlgoSamples(uint8_t); 560 | 561 | // Family Byte: CHANGE_ALGORITHM_CONFIG (0x50), Index Byte: 562 | // SET_PULSE_OX_COEF (0x02), Write Byte: MAXIMFAST_COEF_ID (0x0B) 563 | // This function takes three values that are used as the Sp02 coefficients. 564 | uint8_t setMaximFastCoef(int32_t, int32_t, int32_t); 565 | 566 | // Family Byte: READ_ALGORITHM_CONFIG (0x51), Index Byte: 567 | // READ_AGC_PERCENTAGE (0x00), Write Byte: READ_AGC_PERC_ID (0x00) 568 | // This function reads and returns the currently set target percentage 569 | // of the full-scale ADC range that the Automatic Gain Control algorithm is using. 570 | uint8_t readAlgoRange(); 571 | 572 | // Family Byte: READ_ALGORITHM_CONFIG (0x51), Index Byte: 573 | // READ_AGC_STEP_SIZE (0x00), Write Byte: READ_AGC_STEP_SIZE_ID (0x01) 574 | // This function returns the step size toward the target for the AGC algorithm. 575 | // It returns a value between zero and 100 percent. 576 | uint8_t readAlgoStepSize(); 577 | 578 | // Family Byte: READ_ALGORITHM_CONFIG (0x51), Index Byte: 579 | // READ_AGC_SENSITIVITY_ID (0x00), Write Byte: READ_AGC_SENSITIVITY_ID (0x02) 580 | // This function returns the sensitivity (percentage) of the automatic gain control. 581 | uint8_t readAlgoSensitivity(); 582 | 583 | // Family Byte: READ_ALGORITHM_CONFIG (0x51), Index Byte: 584 | // READ_AGC_NUM_SAMPLES (0x00), Write Byte: READ_AGC_NUM_SAMPlES_ID (0x03) 585 | // This function changes the number of samples that are averaged. 586 | // It takes a paramater of zero to 255. 587 | uint8_t readAlgoSamples(); 588 | 589 | // Family Byte: READ_ALGORITHM_CONFIG (0x51), Index Byte: 590 | // READ_MAX_FAST_COEF (0x02), Write Byte: READ_MAX_FAST_COEF_ID (0x0B) 591 | // This function reads the maximum age for the wrist heart rate monitor 592 | // (WHRM) algorithm. It returns three uint32_t integers that are 593 | // multiplied by 100,000. 594 | // INCOMPLETE 595 | uint8_t readMaximFastCoef(int32_t coefArr[3]); 596 | 597 | // Family Byte: ENABLE_ALGORITHM (0x52), Index Byte: 598 | // ENABLE_AGC_ALGO (0x00) 599 | // This function enables (one) or disables (zero) the automatic gain control algorithm. 600 | uint8_t agcAlgoControl(uint8_t); 601 | 602 | // Family Byte: ENABLE_ALGORITHM (0x52), Index Byte: 603 | // ENABLE_WHRM_ALGO (0x02) 604 | // This function enables (one) or disables (zero) the wrist heart rate monitor 605 | // algorithm. 606 | uint8_t maximFastAlgoControl(uint8_t); 607 | 608 | // Family Byte: BOOTLOADER_FLASH (0x80), Index Byte: SET_NUM_PAGES (0x02), 609 | // Write Bytes: 0x00 - Number of pages at byte 0x44 from .msbl file. 610 | bool setNumPages(uint8_t); 611 | 612 | // Family Byte: BOOTLOADER_FLASH (0x80), Index Byte: ERASE_FLASH (0x03) 613 | // Returns true on successful communication. 614 | bool eraseFlash(); 615 | 616 | // Family Byte: BOOTLOADER_INFO (0x81), Index Byte: BOOTLOADER_VERS (0x00) 617 | version readBootloaderVers(); 618 | 619 | // Family Byte: BOOTLOADER_INFO (0x81), Index Byte: PAGE_SIZE (0x01) 620 | // Family Byte: IDENTITY (0xFF), Index Byte: READ_SENSOR_HUB_VERS (0x03) 621 | version readSensorHubVersion(); 622 | 623 | // Family Byte: IDENTITY (0xFF), Index Byte: READ_ALGO_VERS (0x07) 624 | version readAlgorithmVersion(); 625 | 626 | // Family Byte: CHANGE_ALGORITHM_CONFIG (0x50), Index Byte: BPT_CONFIG (0x04), 627 | // Write Byte: BPT_MEDICATION (0x00) 628 | uint8_t isPatientBPMedication(uint8_t); 629 | 630 | // Family Byte: CHANGE_ALGORITHM_CONFIG (0x50), Index Byte: BPT_CONFIG (0x04), 631 | // Write Byte: BPT_MEDICATION (0x00) 632 | uint8_t isPatientBPMedication(); 633 | 634 | // Family Byte: CHANGE_ALGORITHM_CONFIG (0x50), Index Byte: BPT_CONFIG (0x04), 635 | // Write Byte: DIASTOLIC_VALUE (0x02) 636 | uint8_t writeDiastolicVals(uint8_t, uint8_t, uint8_t); 637 | 638 | // Family Byte: CHANGE_ALGORITHM_CONFIG (0x50), Index Byte: BPT_CONFIG (0x04), 639 | // Write Byte: DIASTOLIC_VALUE (0x02) 640 | uint8_t readDiastolicVals(uint8_t userArray[]); 641 | 642 | // Family Byte: CHANGE_ALGORITHM_CONFIG (0x50), Index Byte: BPT_CONFIG (0x04), 643 | // Write Byte: SYSTOLIC_VALUE (0x01) 644 | uint8_t writeSystolicVals(uint8_t, uint8_t, uint8_t); 645 | 646 | // Family Byte: CHANGE_ALGORITHM_CONFIG (0x50), Index Byte: BPT_CONFIG (0x04), 647 | // Write Byte: SYSTOLIC_VALUE (0x01) 648 | uint8_t readSystolicVals(uint8_t userArray[]); 649 | 650 | // Family Byte: CHANGE_ALGORITHM_CONFIG (0x50), Index Byte: BPT_CONFIG (0x04), 651 | // Write Byte: BPT_CALIB_DATA (0x03) 652 | uint8_t writeBPTAlgoData(uint8_t bptCalibData[]); 653 | 654 | // Family Byte: CHANGE_ALGORITHM_CONFIG (0x50), Index Byte: BPT_CONFIG (0x04), 655 | // Write Byte: BPT_CALIB_DATA (0x03) 656 | uint8_t readBPTAlgoData(uint8_t userArray[]); 657 | 658 | // Family Byte: CHANGE_ALGORITHM_CONFIG (0x50), Index Byte: BPT_CONFIG (0x04), 659 | // Write Byte: PATIENT_RESTING (0x05) 660 | uint8_t isPatientResting(uint8_t); 661 | 662 | // Family Byte: CHANGE_ALGORITHM_CONFIG (0x50), Index Byte: BPT_CONFIG (0x04), 663 | // Write Byte: PATIENT_RESTING (0x05) 664 | uint8_t isPatientResting(); 665 | 666 | // Family Byte: CHANGE_ALGORITHM_CONFIG (0x50), Index Byte: BPT_CONFIG (0x04), 667 | // Write Byte: AGC_SP02_COEFS (0x0B) 668 | uint8_t writeSP02AlgoCoef(int32_t, int32_t, int32_t); 669 | 670 | // Family Byte: CHANGE_ALGORITHM_CONFIG (0x50), Index Byte: BPT_CONFIG (0x04), 671 | // Write Byte: AGC_SP02_COEFS (0x0B) 672 | uint8_t readSP02AlgoCoef(int32_t userArray[]); 673 | 674 | private: 675 | // Variables ----------- 676 | int _resetPin; 677 | int _mfioPin; 678 | uint8_t _address; 679 | uint32_t _writeCoefArr[3]{}; 680 | uint8_t _userSelectedMode; 681 | uint8_t _sampleRate = 100; 682 | 683 | // I-squared-C Class---- 684 | TwoWire *_i2cPort; 685 | 686 | // Functions------------ 687 | 688 | // This function uses the given family, index, and write byte to enable 689 | // the given sensor. 690 | uint8_t enableWrite(uint8_t, uint8_t, uint8_t); 691 | 692 | // This function uses the given family, index, and write byte to communicate 693 | // with the MAX32664 which in turn communicates with downward sensors. There 694 | // are two steps demonstrated in this function. First a write to the MCU 695 | // indicating what you want to do, a delay, and then a read to confirm positive 696 | // transmission. 697 | uint8_t writeByte(uint8_t, uint8_t, uint8_t); 698 | 699 | // This function sends is simliar to the one above and sends info to the MAX32664 700 | // but takes an additional uint8_t as a paramter. Again there is the write 701 | // of the specific bytes followed by a read to confirm positive transmission. 702 | uint8_t writeByte(uint8_t, uint8_t, uint8_t, uint8_t); 703 | 704 | // This function is the same as the function above and uses the given family, 705 | // index, and write byte, but also takes a 16 bit integer as a paramter to communicate 706 | // with the MAX32664 which in turn communicates with downward sensors. There 707 | // are two steps demonstrated in this function. First a write to the MCU 708 | // indicating what you want to do, a delay, and then a read to confirm positive 709 | // transmission. 710 | uint8_t writeByte(uint8_t, uint8_t, uint8_t, uint16_t); 711 | 712 | // This function sends information to the MAX32664 to specifically write values 713 | // to the registers of downward sensors and so also requires a 714 | // register address and register value as parameters. Again there is the write 715 | // of the specific bytes followed by a read to confirm positive transmission. 716 | uint8_t writeLongBytes(uint8_t, uint8_t, uint8_t, int32_t _writeVal[], const size_t); 717 | 718 | // This function sends information to the MAX32664 to specifically write values 719 | // to the registers of downward sensors and so also requires a 720 | // register address and register value as parameters. Again there is the write 721 | // of the specific bytes followed by a read to confirm positive transmission. 722 | uint8_t writeBytes(uint8_t, uint8_t, uint8_t, uint8_t _writeVal[], const size_t); 723 | 724 | // This function handles all read commands or stated another way, all information 725 | // requests. It starts a request by writing the family byte, index byte, and 726 | // delays 60 microseconds, during which the MAX32664 retrieves the requested 727 | // information. An I-squared-C request is then issued, and the information is read and returned. 728 | uint8_t readByte(uint8_t, uint8_t); 729 | 730 | // This function is exactly as the one above except it accepts a Write Byte as 731 | // a paramter. It starts a request by writing the family byte, index byte, and 732 | // write byte to the MAX32664, delays 60 microseconds, during which 733 | // the MAX32664 retrieves the requested information. A I-squared-C request is 734 | // then issued, and the information is read and returned. 735 | uint8_t readByte(uint8_t, uint8_t, uint8_t); 736 | 737 | // This function handles all read commands or stated another way, all information 738 | // requests. It starts a request by writing the family byte, an index byte, and 739 | // a write byte and then then delays 60 microseconds, during which the MAX32664 740 | // retrieves the requested information. An I-squared-C request is then issued, 741 | // and the information is read. This differs from the above read commands in 742 | // that it returns a 16 bit integer instead of 8. 743 | uint16_t readIntByte(uint8_t, uint8_t, uint8_t); 744 | 745 | // This function handles all read commands or stated another way, all information 746 | // requests. It starts a request by writing the family byte, an index byte, and 747 | // a write byte and then then delays 60 microseconds, during which the MAX32664 748 | // retrieves the requested information. An I-squared-C request is then issued, 749 | // and the information is read. This function is very similar to the one above 750 | // except it returns three uint32_t bytes instead of one. 751 | uint8_t readMultipleBytes(uint8_t, uint8_t, uint8_t, const size_t, int32_t userArray[]); 752 | 753 | // This function handles all read commands or stated another way, all information 754 | // requests. It starts a request by writing the family byte, an index byte, and 755 | // a write byte and then then delays 60 microseconds, during which the MAX32664 756 | // retrieves the requested information. An I-squared-C request is then issued, 757 | // and the information is read. This function is very similar to the one above 758 | // except it returns multiple requested bytes. 759 | uint8_t readMultipleBytes(uint8_t, uint8_t, uint8_t, const size_t, uint8_t userArray[]); 760 | 761 | // Needs comment - INCOMPLETE 762 | uint8_t readFillArray(uint8_t, uint8_t, uint8_t, uint8_t array[]); 763 | }; 764 | #endif 765 | --------------------------------------------------------------------------------