Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
Dependencies: MAX30101 MAX32620FTHR
main.cpp@16:07842c9f55cc, 2020-10-31 (annotated)
- Committer:
- gregtoth
- Date:
- Sat Oct 31 15:45:51 2020 +0000
- Revision:
- 16:07842c9f55cc
- Parent:
- 15:d9f7d0d5fc4e
add MediumOne support
Who changed what in which revision?
User | Revision | Line number | New contents of line |
---|---|---|---|
coreyharris | 5:2f708191f1bd | 1 | /******************************************************************************* |
coreyharris | 5:2f708191f1bd | 2 | * Copyright (C) 2017 Maxim Integrated Products, Inc., All Rights Reserved. |
coreyharris | 5:2f708191f1bd | 3 | * |
coreyharris | 5:2f708191f1bd | 4 | * Permission is hereby granted, free of charge, to any person obtaining a |
coreyharris | 5:2f708191f1bd | 5 | * copy of this software and associated documentation files (the "Software"), |
coreyharris | 5:2f708191f1bd | 6 | * to deal in the Software without restriction, including without limitation |
coreyharris | 5:2f708191f1bd | 7 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, |
coreyharris | 5:2f708191f1bd | 8 | * and/or sell copies of the Software, and to permit persons to whom the |
coreyharris | 5:2f708191f1bd | 9 | * Software is furnished to do so, subject to the following conditions: |
coreyharris | 5:2f708191f1bd | 10 | * |
coreyharris | 5:2f708191f1bd | 11 | * The above copyright notice and this permission notice shall be included |
coreyharris | 5:2f708191f1bd | 12 | * in all copies or substantial portions of the Software. |
coreyharris | 5:2f708191f1bd | 13 | * |
coreyharris | 5:2f708191f1bd | 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS |
coreyharris | 5:2f708191f1bd | 15 | * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF |
coreyharris | 5:2f708191f1bd | 16 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. |
coreyharris | 5:2f708191f1bd | 17 | * IN NO EVENT SHALL MAXIM INTEGRATED BE LIABLE FOR ANY CLAIM, DAMAGES |
coreyharris | 5:2f708191f1bd | 18 | * OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, |
coreyharris | 5:2f708191f1bd | 19 | * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR |
coreyharris | 5:2f708191f1bd | 20 | * OTHER DEALINGS IN THE SOFTWARE. |
coreyharris | 5:2f708191f1bd | 21 | * |
coreyharris | 5:2f708191f1bd | 22 | * Except as contained in this notice, the name of Maxim Integrated |
coreyharris | 5:2f708191f1bd | 23 | * Products, Inc. shall not be used except as stated in the Maxim Integrated |
coreyharris | 5:2f708191f1bd | 24 | * Products, Inc. Branding Policy. |
coreyharris | 5:2f708191f1bd | 25 | * |
coreyharris | 5:2f708191f1bd | 26 | * The mere transfer of this software does not imply any licenses |
coreyharris | 5:2f708191f1bd | 27 | * of trade secrets, proprietary technology, copyrights, patents, |
coreyharris | 5:2f708191f1bd | 28 | * trademarks, maskwork rights, or any other form of intellectual |
coreyharris | 5:2f708191f1bd | 29 | * property whatsoever. Maxim Integrated Products, Inc. retains all |
coreyharris | 5:2f708191f1bd | 30 | * ownership rights. |
coreyharris | 5:2f708191f1bd | 31 | ******************************************************************************* |
coreyharris | 5:2f708191f1bd | 32 | */ |
johnGreeneMaxim | 11:976c80cc99d5 | 33 | |
coreyharris | 5:2f708191f1bd | 34 | |
coreyharris | 0:0bd4103885bf | 35 | #include "mbed.h" |
gregtoth | 14:b47461092164 | 36 | #include "MAX32620FTHR.h" |
coreyharris | 0:0bd4103885bf | 37 | #include "MAX30101.h" |
johnGreeneMaxim | 11:976c80cc99d5 | 38 | #include <stdio.h> |
johnGreeneMaxim | 11:976c80cc99d5 | 39 | #include <stdlib.h> |
johnGreeneMaxim | 11:976c80cc99d5 | 40 | #include <math.h> |
johnGreeneMaxim | 11:976c80cc99d5 | 41 | #include <string.h> |
johnGreeneMaxim | 9:affd4e6372a0 | 42 | |
gregtoth | 16:07842c9f55cc | 43 | #define WIFI_SSID "YOUR_INFO" |
gregtoth | 16:07842c9f55cc | 44 | #define WIFI_PASSWORD "YOUR_INFO" |
gregtoth | 16:07842c9f55cc | 45 | |
gregtoth | 16:07842c9f55cc | 46 | #define MQTT_BROKER "mqtt.mediumone.com" |
gregtoth | 16:07842c9f55cc | 47 | #define MQTT_PORT 61618 /* encrypted port */ |
gregtoth | 16:07842c9f55cc | 48 | #define MQTT_USERNAME "xxxxxxxxxxx/xxxxxxxxxxx" |
gregtoth | 16:07842c9f55cc | 49 | #define MQTT_PASSWORD "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx/xxxxxxxxxxxxxxxxx" |
gregtoth | 16:07842c9f55cc | 50 | #define MQTT_PUB_TOPIC "0/xxxxxxxxxxx/xxxxxxxxxxx/mydevice" |
gregtoth | 16:07842c9f55cc | 51 | |
gregtoth | 16:07842c9f55cc | 52 | #define PUB_INTERVAL_MS 3000 |
gregtoth | 16:07842c9f55cc | 53 | #define ENABLE_PUBLISH 1 |
gregtoth | 16:07842c9f55cc | 54 | |
gregtoth | 16:07842c9f55cc | 55 | typedef enum { |
gregtoth | 16:07842c9f55cc | 56 | APP_STATE_INIT = 0, |
gregtoth | 16:07842c9f55cc | 57 | APP_STATE_RESET_BRIDGE, |
gregtoth | 16:07842c9f55cc | 58 | APP_STATE_INIT_BRIDGE, |
gregtoth | 16:07842c9f55cc | 59 | APP_STATE_INIT_BRIDGE_CONFIRM, |
gregtoth | 16:07842c9f55cc | 60 | APP_STATE_CONNECT_WIFI, |
gregtoth | 16:07842c9f55cc | 61 | APP_STATE_CONNECT_WIFI_CONFIRM, |
gregtoth | 16:07842c9f55cc | 62 | APP_STATE_CONNECT_MQTT, |
gregtoth | 16:07842c9f55cc | 63 | APP_STATE_CONNECT_MQTT_CONFIRM, |
gregtoth | 16:07842c9f55cc | 64 | APP_STATE_PUBLISH_SENSOR_DATA, |
gregtoth | 16:07842c9f55cc | 65 | APP_STATE_PUBLISH_SENSOR_DATA_CONFIRM, |
gregtoth | 16:07842c9f55cc | 66 | APP_STATE_DISCONNECT_MQTT, |
gregtoth | 16:07842c9f55cc | 67 | APP_STATE_DISCONNECT_WIFI, |
gregtoth | 16:07842c9f55cc | 68 | APP_STATE_DISCONNECT_WIFI_CONFIRM, |
gregtoth | 16:07842c9f55cc | 69 | APP_STATE_TIMED_DELAY, |
gregtoth | 16:07842c9f55cc | 70 | APP_STATE_NONE |
gregtoth | 16:07842c9f55cc | 71 | } APP_STATE_T; |
gregtoth | 16:07842c9f55cc | 72 | |
gregtoth | 16:07842c9f55cc | 73 | APP_STATE_T app_state = APP_STATE_INIT; |
gregtoth | 16:07842c9f55cc | 74 | APP_STATE_T next_app_state = APP_STATE_NONE; |
gregtoth | 16:07842c9f55cc | 75 | uint32_t timed_delay_start = 0; |
gregtoth | 16:07842c9f55cc | 76 | uint32_t timed_delay_duration = 0; |
gregtoth | 16:07842c9f55cc | 77 | |
gregtoth | 16:07842c9f55cc | 78 | typedef struct { |
gregtoth | 16:07842c9f55cc | 79 | uint32_t iteration; // iteration counter |
gregtoth | 16:07842c9f55cc | 80 | uint32_t timestamp; // millisecond timestamp |
gregtoth | 16:07842c9f55cc | 81 | uint16_t hrbpm; // heart rate, beats per minute |
gregtoth | 16:07842c9f55cc | 82 | uint16_t spo2; // oxygen saturation, percent |
gregtoth | 16:07842c9f55cc | 83 | int valid; // 1=hrbpm and spo2 are valid, 0=not valid |
gregtoth | 16:07842c9f55cc | 84 | } SENSOR_DATA_T; |
gregtoth | 16:07842c9f55cc | 85 | |
gregtoth | 16:07842c9f55cc | 86 | SENSOR_DATA_T sensorData; |
gregtoth | 16:07842c9f55cc | 87 | |
johnGreeneMaxim | 11:976c80cc99d5 | 88 | //variable for the algorithm |
johnGreeneMaxim | 11:976c80cc99d5 | 89 | uint16_t sampleRate =100; |
johnGreeneMaxim | 11:976c80cc99d5 | 90 | uint16_t compSpO2=1; |
johnGreeneMaxim | 11:976c80cc99d5 | 91 | int16_t ir_ac_comp =0; |
johnGreeneMaxim | 11:976c80cc99d5 | 92 | int16_t red_ac_comp=0; |
johnGreeneMaxim | 11:976c80cc99d5 | 93 | int16_t green_ac_comp=0; |
johnGreeneMaxim | 11:976c80cc99d5 | 94 | int16_t ir_ac_mag=0; |
johnGreeneMaxim | 11:976c80cc99d5 | 95 | int16_t red_ac_mag=0; |
johnGreeneMaxim | 11:976c80cc99d5 | 96 | int16_t green_ac_mag=0; |
johnGreeneMaxim | 11:976c80cc99d5 | 97 | uint16_t HRbpm2=0; |
johnGreeneMaxim | 11:976c80cc99d5 | 98 | uint16_t SpO2B=0; |
johnGreeneMaxim | 11:976c80cc99d5 | 99 | uint16_t DRdy=0; |
johnGreeneMaxim | 10:fcfa9adc99a9 | 100 | |
johnGreeneMaxim | 11:976c80cc99d5 | 101 | //Heart rate and SpO2 algorithm |
johnGreeneMaxim | 11:976c80cc99d5 | 102 | void HRSpO2Func(uint32_t dinIR, uint32_t dinRed, uint32_t dinGreen, uint32_t ns,uint16_t SampRate,uint16_t compSpO2, |
johnGreeneMaxim | 11:976c80cc99d5 | 103 | int16_t *ir_ac_comp,int16_t *red_ac_comp, int16_t *green_ac_comp, int16_t *ir_ac_mag,int16_t *red_ac_mag, int16_t *green_ac_mag, uint16_t *HRbpm2,uint16_t *SpO2B,uint16_t *DRdy ); |
coreyharris | 0:0bd4103885bf | 104 | |
johnGreeneMaxim | 11:976c80cc99d5 | 105 | //helper functions for the heart rate and SpO2 function |
johnGreeneMaxim | 11:976c80cc99d5 | 106 | uint16_t avg_dc_est(int32_t *p, uint16_t x); |
johnGreeneMaxim | 11:976c80cc99d5 | 107 | |
johnGreeneMaxim | 11:976c80cc99d5 | 108 | void lp_dfir_flt(int16_t din0,int16_t din1,int16_t din2, int16_t *dout0,int16_t *dout1,int16_t *dout2) ; |
johnGreeneMaxim | 11:976c80cc99d5 | 109 | int32_t mul16(int16_t x, int16_t y); |
johnGreeneMaxim | 11:976c80cc99d5 | 110 | |
johnGreeneMaxim | 11:976c80cc99d5 | 111 | //set logic level to 3.3V |
gregtoth | 14:b47461092164 | 112 | MAX32620FTHR pegasus(MAX32620FTHR::VIO_3V3); |
coreyharris | 0:0bd4103885bf | 113 | |
johnGreeneMaxim | 11:976c80cc99d5 | 114 | //IC configuration functions |
coreyharris | 0:0bd4103885bf | 115 | bool op_sensor_config(MAX30101 &op_sensor); |
johnGreeneMaxim | 8:a1538e8a3fd9 | 116 | void pmic_config(I2C & i2c_bus, DigitalOut & pmic_en); |
coreyharris | 0:0bd4103885bf | 117 | |
coreyharris | 0:0bd4103885bf | 118 | /* Op Sensor FIFO nearly full callback */ |
johnGreeneMaxim | 11:976c80cc99d5 | 119 | volatile bool op_sensorIntFlag = 0; |
coreyharris | 0:0bd4103885bf | 120 | void op_sensor_callback() |
coreyharris | 0:0bd4103885bf | 121 | { |
coreyharris | 1:471e2b722d24 | 122 | op_sensorIntFlag = 1; |
coreyharris | 0:0bd4103885bf | 123 | } |
coreyharris | 0:0bd4103885bf | 124 | |
johnGreeneMaxim | 11:976c80cc99d5 | 125 | //declare large variables outside of main |
johnGreeneMaxim | 11:976c80cc99d5 | 126 | uint32_t redData[500];//set array to max fifo size |
johnGreeneMaxim | 11:976c80cc99d5 | 127 | uint32_t irData[500];//set array to max fifo size |
johnGreeneMaxim | 11:976c80cc99d5 | 128 | uint32_t greenData[500];//set array to max fifo size |
johnGreeneMaxim | 11:976c80cc99d5 | 129 | |
gregtoth | 16:07842c9f55cc | 130 | Serial *ppc = NULL; |
gregtoth | 16:07842c9f55cc | 131 | |
gregtoth | 16:07842c9f55cc | 132 | // UART2 serial |
gregtoth | 16:07842c9f55cc | 133 | RawSerial *puart2 = NULL; |
gregtoth | 16:07842c9f55cc | 134 | #define UART2_GETC() puart2->getc() |
gregtoth | 16:07842c9f55cc | 135 | #define UART2_PUTC(ch) puart2->putc(ch) |
gregtoth | 16:07842c9f55cc | 136 | #define UART2_PUTS(str) puart2->puts(str) |
gregtoth | 16:07842c9f55cc | 137 | #define UART2_READABLE() puart2->readable() |
gregtoth | 16:07842c9f55cc | 138 | |
gregtoth | 16:07842c9f55cc | 139 | #define SERIAL_BUF_SIZE 80 |
gregtoth | 16:07842c9f55cc | 140 | char read_buffer[SERIAL_BUF_SIZE] = {0}; |
gregtoth | 16:07842c9f55cc | 141 | char current_response[SERIAL_BUF_SIZE] = {0}; |
gregtoth | 16:07842c9f55cc | 142 | char last_response[SERIAL_BUF_SIZE] = {0}; |
gregtoth | 16:07842c9f55cc | 143 | int read_pos = 0; |
gregtoth | 16:07842c9f55cc | 144 | int write_pos = 0; |
gregtoth | 16:07842c9f55cc | 145 | int new_response_available = 0; |
gregtoth | 16:07842c9f55cc | 146 | uint32_t cmd_send_time; |
gregtoth | 16:07842c9f55cc | 147 | int fail_count = 0; |
gregtoth | 16:07842c9f55cc | 148 | char command[256]; |
gregtoth | 16:07842c9f55cc | 149 | |
gregtoth | 16:07842c9f55cc | 150 | Timer timer; |
gregtoth | 16:07842c9f55cc | 151 | #define GET_TIME_MS() (uint32_t)timer.read_ms() |
gregtoth | 16:07842c9f55cc | 152 | //#define GET_TIME_MS() 0 |
gregtoth | 16:07842c9f55cc | 153 | |
gregtoth | 16:07842c9f55cc | 154 | // LEDs on MAX32620FTHR |
gregtoth | 16:07842c9f55cc | 155 | DigitalOut redLed(LED1 /*RED*/, LED_OFF); |
gregtoth | 16:07842c9f55cc | 156 | DigitalOut grnLed(LED2 /*GREEN*/, LED_OFF); |
gregtoth | 16:07842c9f55cc | 157 | DigitalOut bluLed(LED3 /*BLUE*/, LED_OFF); |
gregtoth | 16:07842c9f55cc | 158 | |
gregtoth | 16:07842c9f55cc | 159 | // Prototypes |
gregtoth | 16:07842c9f55cc | 160 | void Clear_Rx(void); |
gregtoth | 16:07842c9f55cc | 161 | void Send_Command(const char *cmd); |
gregtoth | 16:07842c9f55cc | 162 | uint32_t Response_Elapsed_Time(void); |
gregtoth | 16:07842c9f55cc | 163 | bool isOKResponse(char *p); |
gregtoth | 16:07842c9f55cc | 164 | void Delay_Ms(uint32_t ms); |
gregtoth | 16:07842c9f55cc | 165 | void LED_On(void); |
gregtoth | 16:07842c9f55cc | 166 | void LED_Off(void); |
gregtoth | 16:07842c9f55cc | 167 | APP_STATE_T new_app_state(APP_STATE_T app_state); |
gregtoth | 16:07842c9f55cc | 168 | void set_timed_delay(uint32_t delay_ms, APP_STATE_T next_state); |
gregtoth | 16:07842c9f55cc | 169 | void reset_bridge(void); |
gregtoth | 16:07842c9f55cc | 170 | void State_Machine(void); |
gregtoth | 16:07842c9f55cc | 171 | |
gregtoth | 16:07842c9f55cc | 172 | void uart2_rx() |
gregtoth | 16:07842c9f55cc | 173 | { |
gregtoth | 16:07842c9f55cc | 174 | char ch = UART2_GETC(); |
gregtoth | 16:07842c9f55cc | 175 | if (ch == '\n') { // end of response |
gregtoth | 16:07842c9f55cc | 176 | current_response[write_pos] = 0; |
gregtoth | 16:07842c9f55cc | 177 | strcpy(last_response, current_response); |
gregtoth | 16:07842c9f55cc | 178 | read_pos = 0; |
gregtoth | 16:07842c9f55cc | 179 | write_pos = 0; |
gregtoth | 16:07842c9f55cc | 180 | current_response[write_pos] = 0; |
gregtoth | 16:07842c9f55cc | 181 | new_response_available = 1; |
gregtoth | 16:07842c9f55cc | 182 | } else if (ch != 0 && ch != '\r' && write_pos < sizeof(current_response) - 1) { // ignore NUL & CR, keep rest |
gregtoth | 16:07842c9f55cc | 183 | current_response[write_pos++] = ch; |
gregtoth | 16:07842c9f55cc | 184 | current_response[write_pos] = 0; |
gregtoth | 16:07842c9f55cc | 185 | } |
gregtoth | 16:07842c9f55cc | 186 | } |
gregtoth | 16:07842c9f55cc | 187 | |
gregtoth | 16:07842c9f55cc | 188 | void Clear_Rx(void) |
gregtoth | 16:07842c9f55cc | 189 | { |
gregtoth | 16:07842c9f55cc | 190 | while (UART2_READABLE()) { |
gregtoth | 16:07842c9f55cc | 191 | UART2_GETC(); |
gregtoth | 16:07842c9f55cc | 192 | } |
gregtoth | 16:07842c9f55cc | 193 | } |
gregtoth | 16:07842c9f55cc | 194 | |
gregtoth | 16:07842c9f55cc | 195 | void Send_Command(const char *cmd) |
gregtoth | 16:07842c9f55cc | 196 | { |
gregtoth | 16:07842c9f55cc | 197 | if (cmd != NULL) { |
gregtoth | 16:07842c9f55cc | 198 | read_pos = 0; |
gregtoth | 16:07842c9f55cc | 199 | write_pos = 0; |
gregtoth | 16:07842c9f55cc | 200 | current_response[write_pos] = 0; |
gregtoth | 16:07842c9f55cc | 201 | new_response_available = 0; |
gregtoth | 16:07842c9f55cc | 202 | Clear_Rx(); |
gregtoth | 16:07842c9f55cc | 203 | UART2_PUTS(cmd); |
gregtoth | 16:07842c9f55cc | 204 | cmd_send_time = GET_TIME_MS(); |
gregtoth | 16:07842c9f55cc | 205 | } |
gregtoth | 16:07842c9f55cc | 206 | } |
gregtoth | 16:07842c9f55cc | 207 | |
gregtoth | 16:07842c9f55cc | 208 | uint32_t Response_Elapsed_Time(void) |
gregtoth | 16:07842c9f55cc | 209 | { |
gregtoth | 16:07842c9f55cc | 210 | return GET_TIME_MS() - cmd_send_time; |
gregtoth | 16:07842c9f55cc | 211 | } |
gregtoth | 16:07842c9f55cc | 212 | |
gregtoth | 16:07842c9f55cc | 213 | bool isOKResponse(char *p) |
gregtoth | 16:07842c9f55cc | 214 | { |
gregtoth | 16:07842c9f55cc | 215 | if (strncmp(p, "OK", 2) == 0) { |
gregtoth | 16:07842c9f55cc | 216 | return true; |
gregtoth | 16:07842c9f55cc | 217 | } else { |
gregtoth | 16:07842c9f55cc | 218 | return false; |
gregtoth | 16:07842c9f55cc | 219 | } |
gregtoth | 16:07842c9f55cc | 220 | } |
gregtoth | 16:07842c9f55cc | 221 | |
gregtoth | 16:07842c9f55cc | 222 | void Delay_Ms(uint32_t ms) |
gregtoth | 16:07842c9f55cc | 223 | { |
gregtoth | 16:07842c9f55cc | 224 | uint32_t start = GET_TIME_MS(); |
gregtoth | 16:07842c9f55cc | 225 | while ((GET_TIME_MS() - start) < ms) { |
gregtoth | 16:07842c9f55cc | 226 | } |
gregtoth | 16:07842c9f55cc | 227 | } |
gregtoth | 16:07842c9f55cc | 228 | |
gregtoth | 16:07842c9f55cc | 229 | void LED_On(void) |
gregtoth | 16:07842c9f55cc | 230 | { |
gregtoth | 16:07842c9f55cc | 231 | grnLed = LED_ON; |
gregtoth | 16:07842c9f55cc | 232 | } |
gregtoth | 16:07842c9f55cc | 233 | |
gregtoth | 16:07842c9f55cc | 234 | void LED_Off(void) |
gregtoth | 16:07842c9f55cc | 235 | { |
gregtoth | 16:07842c9f55cc | 236 | grnLed = LED_OFF; |
gregtoth | 16:07842c9f55cc | 237 | } |
gregtoth | 16:07842c9f55cc | 238 | |
gregtoth | 16:07842c9f55cc | 239 | APP_STATE_T new_app_state(APP_STATE_T app_state) |
gregtoth | 16:07842c9f55cc | 240 | { |
gregtoth | 16:07842c9f55cc | 241 | // Return next_app_state if it's not APP_STATE_NONE, |
gregtoth | 16:07842c9f55cc | 242 | // otherwise return app_state |
gregtoth | 16:07842c9f55cc | 243 | APP_STATE_T state = app_state; |
gregtoth | 16:07842c9f55cc | 244 | if (next_app_state != APP_STATE_NONE) { |
gregtoth | 16:07842c9f55cc | 245 | state = next_app_state; |
gregtoth | 16:07842c9f55cc | 246 | next_app_state = APP_STATE_NONE; |
gregtoth | 16:07842c9f55cc | 247 | } |
gregtoth | 16:07842c9f55cc | 248 | return state; |
gregtoth | 16:07842c9f55cc | 249 | } |
gregtoth | 16:07842c9f55cc | 250 | |
gregtoth | 16:07842c9f55cc | 251 | void set_timed_delay(uint32_t delay_ms, APP_STATE_T next_state) |
gregtoth | 16:07842c9f55cc | 252 | { |
gregtoth | 16:07842c9f55cc | 253 | timed_delay_start = GET_TIME_MS(); |
gregtoth | 16:07842c9f55cc | 254 | timed_delay_duration = delay_ms; |
gregtoth | 16:07842c9f55cc | 255 | next_app_state = next_state; |
gregtoth | 16:07842c9f55cc | 256 | app_state = APP_STATE_TIMED_DELAY; |
gregtoth | 16:07842c9f55cc | 257 | } |
gregtoth | 16:07842c9f55cc | 258 | |
gregtoth | 16:07842c9f55cc | 259 | void reset_bridge(void) |
gregtoth | 16:07842c9f55cc | 260 | { |
gregtoth | 16:07842c9f55cc | 261 | // Not used due to MAX32620FTHR RST wiring |
gregtoth | 16:07842c9f55cc | 262 | } |
johnGreeneMaxim | 11:976c80cc99d5 | 263 | |
coreyharris | 0:0bd4103885bf | 264 | int main() |
coreyharris | 0:0bd4103885bf | 265 | { |
gregtoth | 16:07842c9f55cc | 266 | // Create serial objects after main() has started |
gregtoth | 16:07842c9f55cc | 267 | |
coreyharris | 1:471e2b722d24 | 268 | Serial pc(USBTX, USBRX); // Use USB debug probe for serial link |
gregtoth | 16:07842c9f55cc | 269 | ppc = &pc; |
coreyharris | 1:471e2b722d24 | 270 | pc.baud(115200); // Baud rate = 115200 |
gregtoth | 15:d9f7d0d5fc4e | 271 | |
gregtoth | 16:07842c9f55cc | 272 | pc.printf("Starting main program\r\n"); |
gregtoth | 16:07842c9f55cc | 273 | |
gregtoth | 15:d9f7d0d5fc4e | 274 | RawSerial uart2(P3_1, P3_0); |
gregtoth | 16:07842c9f55cc | 275 | puart2 = &uart2; |
gregtoth | 15:d9f7d0d5fc4e | 276 | uart2.baud(115200); |
gregtoth | 16:07842c9f55cc | 277 | uart2.attach(uart2_rx); |
gregtoth | 16:07842c9f55cc | 278 | |
gregtoth | 16:07842c9f55cc | 279 | timer.start(); |
gregtoth | 16:07842c9f55cc | 280 | |
gregtoth | 16:07842c9f55cc | 281 | //DigitalOut redLed(LED1, LED_OFF); // Debug LED |
johnGreeneMaxim | 11:976c80cc99d5 | 282 | |
coreyharris | 1:471e2b722d24 | 283 | InterruptIn op_sensor_int(P3_2); // Config P3_2 as int. in for |
coreyharris | 1:471e2b722d24 | 284 | op_sensor_int.fall(&op_sensor_callback); // FIFO ready interrupt |
johnGreeneMaxim | 11:976c80cc99d5 | 285 | |
johnGreeneMaxim | 11:976c80cc99d5 | 286 | I2C i2cBus(I2C1_SDA, I2C1_SCL); // I2C bus, P3_4 = SDA, P3_5 = SCL |
coreyharris | 0:0bd4103885bf | 287 | |
johnGreeneMaxim | 8:a1538e8a3fd9 | 288 | DigitalOut VLED_EN(P3_3,0); //Enable for VLEDs |
johnGreeneMaxim | 8:a1538e8a3fd9 | 289 | pmic_config(i2cBus, VLED_EN); |
johnGreeneMaxim | 11:976c80cc99d5 | 290 | |
coreyharris | 4:14d1b87cd3c4 | 291 | MAX30101 op_sensor(i2cBus); // Create new MAX30101 on i2cBus |
coreyharris | 4:14d1b87cd3c4 | 292 | int rc = op_sensor_config(op_sensor); // Config sensor, return 0 on success |
johnGreeneMaxim | 11:976c80cc99d5 | 293 | |
coreyharris | 5:2f708191f1bd | 294 | MAX30101::InterruptBitField_u ints; // Read interrupt status to clear |
johnGreeneMaxim | 11:976c80cc99d5 | 295 | rc = op_sensor.getInterruptStatus(ints); // power on interrupt |
johnGreeneMaxim | 11:976c80cc99d5 | 296 | |
coreyharris | 0:0bd4103885bf | 297 | uint8_t fifoData[MAX30101::MAX_FIFO_BYTES]; |
coreyharris | 0:0bd4103885bf | 298 | uint16_t idx, readBytes; |
coreyharris | 7:6075af57668e | 299 | int32_t opSample; |
coreyharris | 2:54182d6a168f | 300 | uint32_t sample; |
johnGreeneMaxim | 11:976c80cc99d5 | 301 | uint16_t HRTemp; |
johnGreeneMaxim | 11:976c80cc99d5 | 302 | uint16_t spo2Temp; |
johnGreeneMaxim | 10:fcfa9adc99a9 | 303 | |
johnGreeneMaxim | 11:976c80cc99d5 | 304 | int r=0; //counter for redData position |
johnGreeneMaxim | 11:976c80cc99d5 | 305 | int ir=0; //counter for irData position |
johnGreeneMaxim | 11:976c80cc99d5 | 306 | int g =0; //counter for greenData position |
johnGreeneMaxim | 11:976c80cc99d5 | 307 | int c=0; //counter to print values |
johnGreeneMaxim | 11:976c80cc99d5 | 308 | |
johnGreeneMaxim | 10:fcfa9adc99a9 | 309 | pc.printf("Starting Program...Please wait a few seconds while data is being collected.\r\n"); |
gregtoth | 16:07842c9f55cc | 310 | |
johnGreeneMaxim | 11:976c80cc99d5 | 311 | while(1) { |
gregtoth | 16:07842c9f55cc | 312 | |
gregtoth | 16:07842c9f55cc | 313 | State_Machine(); |
gregtoth | 16:07842c9f55cc | 314 | |
johnGreeneMaxim | 11:976c80cc99d5 | 315 | if( rc == 0 ) { |
johnGreeneMaxim | 11:976c80cc99d5 | 316 | |
coreyharris | 4:14d1b87cd3c4 | 317 | // Check if op_sensor interrupt asserted |
johnGreeneMaxim | 11:976c80cc99d5 | 318 | if(op_sensorIntFlag) { |
johnGreeneMaxim | 10:fcfa9adc99a9 | 319 | //pc.printf("Entered op_sensorIntFlag check\r\n"); |
coreyharris | 1:471e2b722d24 | 320 | op_sensorIntFlag = 0; // Lower interrupt flag |
johnGreeneMaxim | 11:976c80cc99d5 | 321 | rc = op_sensor.getInterruptStatus(ints); // Read interrupt status |
johnGreeneMaxim | 11:976c80cc99d5 | 322 | //to clear interrupt |
johnGreeneMaxim | 11:976c80cc99d5 | 323 | |
johnGreeneMaxim | 11:976c80cc99d5 | 324 | // Confirms proper read prior to executing |
gregtoth | 15:d9f7d0d5fc4e | 325 | if(rc == 0) { |
johnGreeneMaxim | 11:976c80cc99d5 | 326 | // Read FIFO |
johnGreeneMaxim | 11:976c80cc99d5 | 327 | rc = op_sensor.readFIFO(MAX30101::ThreeLedChannels, fifoData, readBytes); |
gregtoth | 15:d9f7d0d5fc4e | 328 | //pc.printf("op_sensor.readFIFO() returned readBytes=%u, rc=%d\r\n", readBytes, rc); |
johnGreeneMaxim | 11:976c80cc99d5 | 329 | |
johnGreeneMaxim | 11:976c80cc99d5 | 330 | if(rc == 0) { |
johnGreeneMaxim | 11:976c80cc99d5 | 331 | |
coreyharris | 4:14d1b87cd3c4 | 332 | // Convert read bytes into samples |
johnGreeneMaxim | 13:ef4a84158e4c | 333 | for (idx = 0; idx < readBytes; idx+=9) { |
johnGreeneMaxim | 13:ef4a84158e4c | 334 | if (r >= 500 || ir >= 500 || g >= 500) { |
gregtoth | 15:d9f7d0d5fc4e | 335 | pc.printf("Overflow! "); |
gregtoth | 15:d9f7d0d5fc4e | 336 | pc.printf("readBytes=%u, idx=%u, r=%d, ir=%d, g=%d\r\n", readBytes, idx, r, ir, g); |
johnGreeneMaxim | 13:ef4a84158e4c | 337 | } |
johnGreeneMaxim | 13:ef4a84158e4c | 338 | redData[r++] = ((fifoData[idx] << 16) | (fifoData[idx + 1] << 8) | (fifoData[idx + 2])) & 0x03FFFF; |
johnGreeneMaxim | 11:976c80cc99d5 | 339 | |
johnGreeneMaxim | 13:ef4a84158e4c | 340 | irData[ir++] = ((fifoData[idx + 3] << 16) | (fifoData[idx + 4] << 8) | (fifoData[idx + 5])) & 0x03FFFF; |
johnGreeneMaxim | 11:976c80cc99d5 | 341 | |
johnGreeneMaxim | 13:ef4a84158e4c | 342 | greenData[g++] = ((fifoData[idx + 6] << 16) | (fifoData[idx + 7] << 8) | (fifoData[idx + 8])) & 0x03FFFF; |
johnGreeneMaxim | 13:ef4a84158e4c | 343 | } |
johnGreeneMaxim | 11:976c80cc99d5 | 344 | |
johnGreeneMaxim | 11:976c80cc99d5 | 345 | |
johnGreeneMaxim | 11:976c80cc99d5 | 346 | |
gregtoth | 15:d9f7d0d5fc4e | 347 | if(r>=500 && ir>=500 && g>=500)//checks to make sure there are 500 |
johnGreeneMaxim | 11:976c80cc99d5 | 348 | //samples in data buffers |
johnGreeneMaxim | 11:976c80cc99d5 | 349 | { |
johnGreeneMaxim | 11:976c80cc99d5 | 350 | |
johnGreeneMaxim | 11:976c80cc99d5 | 351 | //runs the heart rate and SpO2 algorithm |
johnGreeneMaxim | 11:976c80cc99d5 | 352 | for(c=0, HRTemp = 0; c<r; c++) { |
johnGreeneMaxim | 11:976c80cc99d5 | 353 | |
johnGreeneMaxim | 11:976c80cc99d5 | 354 | HRSpO2Func(irData[c], redData[c],greenData[c], c,sampleRate, compSpO2, |
johnGreeneMaxim | 11:976c80cc99d5 | 355 | &ir_ac_comp,&red_ac_comp, &green_ac_comp, &ir_ac_mag,&red_ac_mag, |
johnGreeneMaxim | 11:976c80cc99d5 | 356 | &green_ac_mag, &HRbpm2,&SpO2B,&DRdy); |
johnGreeneMaxim | 11:976c80cc99d5 | 357 | if(DRdy) |
johnGreeneMaxim | 11:976c80cc99d5 | 358 | { |
johnGreeneMaxim | 11:976c80cc99d5 | 359 | HRTemp = HRbpm2; |
johnGreeneMaxim | 11:976c80cc99d5 | 360 | spo2Temp = SpO2B; |
johnGreeneMaxim | 11:976c80cc99d5 | 361 | } |
johnGreeneMaxim | 11:976c80cc99d5 | 362 | |
johnGreeneMaxim | 11:976c80cc99d5 | 363 | } |
johnGreeneMaxim | 11:976c80cc99d5 | 364 | |
johnGreeneMaxim | 11:976c80cc99d5 | 365 | //If the above algorithm returns a valid heart rate on the last sample, it is printed |
johnGreeneMaxim | 11:976c80cc99d5 | 366 | if(DRdy==1) { |
johnGreeneMaxim | 11:976c80cc99d5 | 367 | pc.printf("Heart Rate = %i\r\n",HRbpm2); |
johnGreeneMaxim | 11:976c80cc99d5 | 368 | pc.printf("SPO2 = %i\r\n",SpO2B); |
gregtoth | 16:07842c9f55cc | 369 | sensorData.hrbpm = HRbpm2; |
gregtoth | 16:07842c9f55cc | 370 | sensorData.spo2 = SpO2B; |
gregtoth | 16:07842c9f55cc | 371 | sensorData.valid = 1; |
gregtoth | 16:07842c9f55cc | 372 | sensorData.timestamp = GET_TIME_MS(); |
gregtoth | 16:07842c9f55cc | 373 | //uart2.printf("SPO2 = %i (SpO2B)\r\n", SpO2B); |
gregtoth | 16:07842c9f55cc | 374 | //UART2_PUTS("Got SPO2 reading (Sp02B)\r\n"); |
johnGreeneMaxim | 11:976c80cc99d5 | 375 | } |
johnGreeneMaxim | 11:976c80cc99d5 | 376 | else if (HRTemp!=0)//if a valid heart was calculated at all, it is printed |
johnGreeneMaxim | 9:affd4e6372a0 | 377 | { |
johnGreeneMaxim | 11:976c80cc99d5 | 378 | pc.printf("Heart Rate = %i\r\n",HRTemp); |
johnGreeneMaxim | 11:976c80cc99d5 | 379 | pc.printf("SPO2 = %i\r\n",spo2Temp); |
gregtoth | 16:07842c9f55cc | 380 | sensorData.hrbpm = HRTemp; |
gregtoth | 16:07842c9f55cc | 381 | sensorData.spo2 = spo2Temp; |
gregtoth | 16:07842c9f55cc | 382 | sensorData.valid = 1; |
gregtoth | 16:07842c9f55cc | 383 | sensorData.timestamp = GET_TIME_MS(); |
gregtoth | 16:07842c9f55cc | 384 | //uart2.printf("SPO2 = %i (spo2Temp)\r\n", spo2Temp); |
gregtoth | 16:07842c9f55cc | 385 | //UART2_PUTS("Got SPO2 reading (spo2Temp)\r\n"); |
johnGreeneMaxim | 9:affd4e6372a0 | 386 | } |
johnGreeneMaxim | 9:affd4e6372a0 | 387 | else |
johnGreeneMaxim | 9:affd4e6372a0 | 388 | { |
johnGreeneMaxim | 11:976c80cc99d5 | 389 | pc.printf("Calculation failed...waiting for more samples...\r\n"); |
gregtoth | 16:07842c9f55cc | 390 | pc.printf("Please keep your finger on the MAX30101 sensor with minimal movement.\r\n"); |
gregtoth | 16:07842c9f55cc | 391 | sensorData.hrbpm = 0; |
gregtoth | 16:07842c9f55cc | 392 | sensorData.spo2 = 0; |
gregtoth | 16:07842c9f55cc | 393 | sensorData.valid = 0; |
gregtoth | 16:07842c9f55cc | 394 | sensorData.timestamp = GET_TIME_MS(); |
johnGreeneMaxim | 9:affd4e6372a0 | 395 | } |
johnGreeneMaxim | 11:976c80cc99d5 | 396 | |
johnGreeneMaxim | 11:976c80cc99d5 | 397 | //dump the first hundred samples after caluclaiton |
johnGreeneMaxim | 11:976c80cc99d5 | 398 | for(c=100; c<500; c++) |
johnGreeneMaxim | 11:976c80cc99d5 | 399 | |
johnGreeneMaxim | 9:affd4e6372a0 | 400 | { |
johnGreeneMaxim | 9:affd4e6372a0 | 401 | redData[c-100]=redData[c]; |
johnGreeneMaxim | 9:affd4e6372a0 | 402 | irData[c-100]=irData[c]; |
johnGreeneMaxim | 11:976c80cc99d5 | 403 | greenData[c-100] = greenData[c]; |
johnGreeneMaxim | 9:affd4e6372a0 | 404 | |
johnGreeneMaxim | 9:affd4e6372a0 | 405 | } |
johnGreeneMaxim | 11:976c80cc99d5 | 406 | //reset counters |
johnGreeneMaxim | 11:976c80cc99d5 | 407 | r=400; |
johnGreeneMaxim | 11:976c80cc99d5 | 408 | ir=400; |
johnGreeneMaxim | 11:976c80cc99d5 | 409 | g=400; |
johnGreeneMaxim | 11:976c80cc99d5 | 410 | } |
coreyharris | 0:0bd4103885bf | 411 | } |
coreyharris | 0:0bd4103885bf | 412 | } |
coreyharris | 0:0bd4103885bf | 413 | } |
johnGreeneMaxim | 11:976c80cc99d5 | 414 | |
johnGreeneMaxim | 11:976c80cc99d5 | 415 | // If rc != 0, a communication error has occurred |
johnGreeneMaxim | 11:976c80cc99d5 | 416 | } else { |
johnGreeneMaxim | 11:976c80cc99d5 | 417 | |
coreyharris | 4:14d1b87cd3c4 | 418 | pc.printf("Something went wrong, " |
coreyharris | 4:14d1b87cd3c4 | 419 | "check the I2C bus or power connections... \r\n"); |
johnGreeneMaxim | 11:976c80cc99d5 | 420 | //bLed = LED_OFF; |
johnGreeneMaxim | 11:976c80cc99d5 | 421 | //gLed = LED_OFF; |
johnGreeneMaxim | 11:976c80cc99d5 | 422 | |
johnGreeneMaxim | 11:976c80cc99d5 | 423 | while(1) { |
gregtoth | 16:07842c9f55cc | 424 | redLed = !redLed; |
gregtoth | 16:07842c9f55cc | 425 | //wait(0.5); |
gregtoth | 16:07842c9f55cc | 426 | //wait_ms(500); |
gregtoth | 16:07842c9f55cc | 427 | wait_us(500000); |
gregtoth | 16:07842c9f55cc | 428 | //ThisThread::sleep_for(500); |
johnGreeneMaxim | 11:976c80cc99d5 | 429 | } |
johnGreeneMaxim | 11:976c80cc99d5 | 430 | } |
johnGreeneMaxim | 11:976c80cc99d5 | 431 | |
johnGreeneMaxim | 11:976c80cc99d5 | 432 | } |
coreyharris | 0:0bd4103885bf | 433 | } |
coreyharris | 0:0bd4103885bf | 434 | |
gregtoth | 16:07842c9f55cc | 435 | void State_Machine(void) |
gregtoth | 16:07842c9f55cc | 436 | { |
gregtoth | 16:07842c9f55cc | 437 | switch (app_state) { |
gregtoth | 16:07842c9f55cc | 438 | case APP_STATE_INIT: |
gregtoth | 16:07842c9f55cc | 439 | app_state = APP_STATE_RESET_BRIDGE; |
gregtoth | 16:07842c9f55cc | 440 | break; |
gregtoth | 16:07842c9f55cc | 441 | case APP_STATE_RESET_BRIDGE: |
gregtoth | 16:07842c9f55cc | 442 | ppc->printf("Resetting bridge hardware..."); |
gregtoth | 16:07842c9f55cc | 443 | reset_bridge(); |
gregtoth | 16:07842c9f55cc | 444 | ppc->printf("complete\r\n"); |
gregtoth | 16:07842c9f55cc | 445 | app_state = APP_STATE_INIT_BRIDGE; |
gregtoth | 16:07842c9f55cc | 446 | break; |
gregtoth | 16:07842c9f55cc | 447 | case APP_STATE_INIT_BRIDGE: |
gregtoth | 16:07842c9f55cc | 448 | ppc->printf("Initializing bridge\r\n"); |
gregtoth | 16:07842c9f55cc | 449 | Send_Command("AT\n"); |
gregtoth | 16:07842c9f55cc | 450 | app_state = APP_STATE_INIT_BRIDGE_CONFIRM; |
gregtoth | 16:07842c9f55cc | 451 | break; |
gregtoth | 16:07842c9f55cc | 452 | case APP_STATE_INIT_BRIDGE_CONFIRM: |
gregtoth | 16:07842c9f55cc | 453 | if (new_response_available && isOKResponse(last_response)) { |
gregtoth | 16:07842c9f55cc | 454 | ppc->printf("Bridge initialization complete\r\n"); |
gregtoth | 16:07842c9f55cc | 455 | app_state = APP_STATE_CONNECT_WIFI; |
gregtoth | 16:07842c9f55cc | 456 | } else if (Response_Elapsed_Time() > 1000) { |
gregtoth | 16:07842c9f55cc | 457 | ppc->printf("ERROR: Bridge not responding\r\n"); |
gregtoth | 16:07842c9f55cc | 458 | app_state = APP_STATE_RESET_BRIDGE; |
gregtoth | 16:07842c9f55cc | 459 | } |
gregtoth | 16:07842c9f55cc | 460 | break; |
gregtoth | 16:07842c9f55cc | 461 | case APP_STATE_CONNECT_WIFI: |
gregtoth | 16:07842c9f55cc | 462 | ppc->printf("Connecting to Wi-Fi\r\n"); |
gregtoth | 16:07842c9f55cc | 463 | sprintf(command, "AT+CWIFI={\"ssid\":\"%s\",\"password\":\"%s\"}\n", |
gregtoth | 16:07842c9f55cc | 464 | WIFI_SSID, WIFI_PASSWORD); |
gregtoth | 16:07842c9f55cc | 465 | Send_Command(command); |
gregtoth | 16:07842c9f55cc | 466 | app_state = APP_STATE_CONNECT_WIFI_CONFIRM; |
gregtoth | 16:07842c9f55cc | 467 | break; |
gregtoth | 16:07842c9f55cc | 468 | case APP_STATE_CONNECT_WIFI_CONFIRM: |
gregtoth | 16:07842c9f55cc | 469 | if (new_response_available) { |
gregtoth | 16:07842c9f55cc | 470 | if (isOKResponse(last_response)) { |
gregtoth | 16:07842c9f55cc | 471 | ppc->printf("Wi-Fi connect successful\r\n"); |
gregtoth | 16:07842c9f55cc | 472 | app_state = APP_STATE_CONNECT_MQTT; |
gregtoth | 16:07842c9f55cc | 473 | } else { |
gregtoth | 16:07842c9f55cc | 474 | ppc->printf("ERROR: Wi-Fi connect failed, will retry\r\n"); |
gregtoth | 16:07842c9f55cc | 475 | set_timed_delay(5000, APP_STATE_CONNECT_WIFI); |
gregtoth | 16:07842c9f55cc | 476 | } |
gregtoth | 16:07842c9f55cc | 477 | } else if (Response_Elapsed_Time() > 20000) { |
gregtoth | 16:07842c9f55cc | 478 | ppc->printf("ERROR: Wi-Fi connect timed out, will retry\r\n"); |
gregtoth | 16:07842c9f55cc | 479 | set_timed_delay(5000, APP_STATE_CONNECT_WIFI); |
gregtoth | 16:07842c9f55cc | 480 | } |
gregtoth | 16:07842c9f55cc | 481 | break; |
gregtoth | 16:07842c9f55cc | 482 | case APP_STATE_CONNECT_MQTT: |
gregtoth | 16:07842c9f55cc | 483 | ppc->printf("Connecting to MQTT broker\r\n"); |
gregtoth | 16:07842c9f55cc | 484 | sprintf(command, "AT+CMQTT={\"host\":\"%s\",\"port\":%d,\"username\":\"%s\",\"password\":\"%s\"}\n", |
gregtoth | 16:07842c9f55cc | 485 | MQTT_BROKER, MQTT_PORT, MQTT_USERNAME, MQTT_PASSWORD); |
gregtoth | 16:07842c9f55cc | 486 | Send_Command(command); |
gregtoth | 16:07842c9f55cc | 487 | app_state = APP_STATE_CONNECT_MQTT_CONFIRM; |
gregtoth | 16:07842c9f55cc | 488 | break; |
gregtoth | 16:07842c9f55cc | 489 | case APP_STATE_CONNECT_MQTT_CONFIRM: |
gregtoth | 16:07842c9f55cc | 490 | if (new_response_available) { |
gregtoth | 16:07842c9f55cc | 491 | if (isOKResponse(last_response)) { |
gregtoth | 16:07842c9f55cc | 492 | ppc->printf("MQTT connect successful\r\n"); |
gregtoth | 16:07842c9f55cc | 493 | app_state = APP_STATE_PUBLISH_SENSOR_DATA; |
gregtoth | 16:07842c9f55cc | 494 | } else { |
gregtoth | 16:07842c9f55cc | 495 | ppc->printf("ERROR: MQTT connect failed, will retry\r\n"); |
gregtoth | 16:07842c9f55cc | 496 | set_timed_delay(5000, APP_STATE_CONNECT_MQTT); |
gregtoth | 16:07842c9f55cc | 497 | } |
gregtoth | 16:07842c9f55cc | 498 | } else if (Response_Elapsed_Time() > 10000) { |
gregtoth | 16:07842c9f55cc | 499 | ppc->printf("ERROR: MQTT connect timed out, will retry\r\n"); |
gregtoth | 16:07842c9f55cc | 500 | set_timed_delay(5000, APP_STATE_CONNECT_MQTT); |
gregtoth | 16:07842c9f55cc | 501 | } |
gregtoth | 16:07842c9f55cc | 502 | break; |
gregtoth | 16:07842c9f55cc | 503 | case APP_STATE_PUBLISH_SENSOR_DATA: |
gregtoth | 16:07842c9f55cc | 504 | ppc->printf("Building publish message\r\n"); |
gregtoth | 16:07842c9f55cc | 505 | sprintf(command, |
gregtoth | 16:07842c9f55cc | 506 | "AT+PUBLISH={\"topic\":\"%s\",\"msg\":\"{\\\"event_data\\\":{\\\"iteration\\\":%u,\\\"timestamp\\\":%u,\\\"valid\\\":%d,\\\"hrbpm\\\":%u,\\\"spo2\\\":%u}}\"}\n", |
gregtoth | 16:07842c9f55cc | 507 | MQTT_PUB_TOPIC, |
gregtoth | 16:07842c9f55cc | 508 | (unsigned)sensorData.iteration, (unsigned)sensorData.timestamp, sensorData.valid, sensorData.hrbpm, sensorData.spo2 |
gregtoth | 16:07842c9f55cc | 509 | ); |
gregtoth | 16:07842c9f55cc | 510 | ppc->printf("%s\r", command); // command already has \n at end |
gregtoth | 16:07842c9f55cc | 511 | sensorData.iteration++; |
gregtoth | 16:07842c9f55cc | 512 | #if ENABLE_PUBLISH == 1 |
gregtoth | 16:07842c9f55cc | 513 | Send_Command(command); |
gregtoth | 16:07842c9f55cc | 514 | LED_On(); |
gregtoth | 16:07842c9f55cc | 515 | app_state = APP_STATE_PUBLISH_SENSOR_DATA_CONFIRM; |
gregtoth | 16:07842c9f55cc | 516 | #else |
gregtoth | 16:07842c9f55cc | 517 | set_timed_delay(PUB_INTERVAL_MS, APP_STATE_PUBLISH_SENSOR_DATA); |
gregtoth | 16:07842c9f55cc | 518 | #endif |
gregtoth | 16:07842c9f55cc | 519 | break; |
gregtoth | 16:07842c9f55cc | 520 | case APP_STATE_PUBLISH_SENSOR_DATA_CONFIRM: |
gregtoth | 16:07842c9f55cc | 521 | if (new_response_available) { |
gregtoth | 16:07842c9f55cc | 522 | LED_Off(); |
gregtoth | 16:07842c9f55cc | 523 | if (isOKResponse(last_response)) { |
gregtoth | 16:07842c9f55cc | 524 | ppc->printf("MQTT publish successful\r\n"); |
gregtoth | 16:07842c9f55cc | 525 | fail_count = 0; |
gregtoth | 16:07842c9f55cc | 526 | set_timed_delay(PUB_INTERVAL_MS, APP_STATE_PUBLISH_SENSOR_DATA); |
gregtoth | 16:07842c9f55cc | 527 | } else { |
gregtoth | 16:07842c9f55cc | 528 | ppc->printf("ERROR: MQTT publish failed\r\n"); |
gregtoth | 16:07842c9f55cc | 529 | if (++fail_count >= 3) { |
gregtoth | 16:07842c9f55cc | 530 | app_state = APP_STATE_DISCONNECT_WIFI; |
gregtoth | 16:07842c9f55cc | 531 | } else { |
gregtoth | 16:07842c9f55cc | 532 | set_timed_delay(PUB_INTERVAL_MS, APP_STATE_PUBLISH_SENSOR_DATA); |
gregtoth | 16:07842c9f55cc | 533 | } |
gregtoth | 16:07842c9f55cc | 534 | } |
gregtoth | 16:07842c9f55cc | 535 | } else if (Response_Elapsed_Time() > 10000) { |
gregtoth | 16:07842c9f55cc | 536 | LED_Off(); |
gregtoth | 16:07842c9f55cc | 537 | ppc->printf("ERROR: MQTT publish timed out\r\n"); |
gregtoth | 16:07842c9f55cc | 538 | if (++fail_count >= 3) { |
gregtoth | 16:07842c9f55cc | 539 | app_state = APP_STATE_DISCONNECT_WIFI; |
gregtoth | 16:07842c9f55cc | 540 | } else { |
gregtoth | 16:07842c9f55cc | 541 | // try again with no additional delay |
gregtoth | 16:07842c9f55cc | 542 | } |
gregtoth | 16:07842c9f55cc | 543 | } |
gregtoth | 16:07842c9f55cc | 544 | break; |
gregtoth | 16:07842c9f55cc | 545 | case APP_STATE_DISCONNECT_MQTT: |
gregtoth | 16:07842c9f55cc | 546 | /* not currently used */ |
gregtoth | 16:07842c9f55cc | 547 | break; |
gregtoth | 16:07842c9f55cc | 548 | case APP_STATE_DISCONNECT_WIFI: |
gregtoth | 16:07842c9f55cc | 549 | ppc->printf("Disconnecting Wi-Fi\r\n"); |
gregtoth | 16:07842c9f55cc | 550 | Send_Command("AT+DWIFI\n"); |
gregtoth | 16:07842c9f55cc | 551 | app_state = APP_STATE_DISCONNECT_WIFI_CONFIRM; |
gregtoth | 16:07842c9f55cc | 552 | break; |
gregtoth | 16:07842c9f55cc | 553 | case APP_STATE_DISCONNECT_WIFI_CONFIRM: |
gregtoth | 16:07842c9f55cc | 554 | if (new_response_available) { |
gregtoth | 16:07842c9f55cc | 555 | if (isOKResponse(last_response)) { |
gregtoth | 16:07842c9f55cc | 556 | ppc->printf("Wi-Fi disconnect successful\r\n"); |
gregtoth | 16:07842c9f55cc | 557 | app_state = new_app_state(APP_STATE_INIT); |
gregtoth | 16:07842c9f55cc | 558 | } else { |
gregtoth | 16:07842c9f55cc | 559 | ppc->printf("ERROR: Wi-Fi disconnect failed\r\n"); |
gregtoth | 16:07842c9f55cc | 560 | app_state = new_app_state(APP_STATE_INIT); |
gregtoth | 16:07842c9f55cc | 561 | } |
gregtoth | 16:07842c9f55cc | 562 | } else if (Response_Elapsed_Time() > 10000) { |
gregtoth | 16:07842c9f55cc | 563 | ppc->printf("ERROR: Wi-Fi disconnect timed out\r\n"); |
gregtoth | 16:07842c9f55cc | 564 | app_state = new_app_state(APP_STATE_INIT); |
gregtoth | 16:07842c9f55cc | 565 | } |
gregtoth | 16:07842c9f55cc | 566 | break; |
gregtoth | 16:07842c9f55cc | 567 | case APP_STATE_TIMED_DELAY: |
gregtoth | 16:07842c9f55cc | 568 | if ((GET_TIME_MS() - timed_delay_start) >= timed_delay_duration) { |
gregtoth | 16:07842c9f55cc | 569 | app_state = new_app_state(APP_STATE_INIT); |
gregtoth | 16:07842c9f55cc | 570 | } |
gregtoth | 16:07842c9f55cc | 571 | case APP_STATE_NONE: |
gregtoth | 16:07842c9f55cc | 572 | default: |
gregtoth | 16:07842c9f55cc | 573 | break; |
gregtoth | 16:07842c9f55cc | 574 | } |
gregtoth | 16:07842c9f55cc | 575 | } |
coreyharris | 0:0bd4103885bf | 576 | |
johnGreeneMaxim | 11:976c80cc99d5 | 577 | bool op_sensor_config(MAX30101 &op_sensor) |
johnGreeneMaxim | 11:976c80cc99d5 | 578 | { |
johnGreeneMaxim | 11:976c80cc99d5 | 579 | |
coreyharris | 0:0bd4103885bf | 580 | //Reset Device |
coreyharris | 0:0bd4103885bf | 581 | MAX30101::ModeConfiguration_u modeConfig; |
coreyharris | 0:0bd4103885bf | 582 | modeConfig.all = 0; |
coreyharris | 0:0bd4103885bf | 583 | modeConfig.bits.reset = 1; |
johnGreeneMaxim | 11:976c80cc99d5 | 584 | modeConfig.bits.mode = MAX30101::MultiLedMode; // Sets SPO2 Mode |
coreyharris | 0:0bd4103885bf | 585 | int32_t rc = op_sensor.setModeConfiguration(modeConfig); |
johnGreeneMaxim | 11:976c80cc99d5 | 586 | |
johnGreeneMaxim | 11:976c80cc99d5 | 587 | |
coreyharris | 0:0bd4103885bf | 588 | //enable MAX30101 interrupts |
coreyharris | 0:0bd4103885bf | 589 | MAX30101::InterruptBitField_u ints; |
johnGreeneMaxim | 11:976c80cc99d5 | 590 | if(rc == 0) { |
coreyharris | 0:0bd4103885bf | 591 | ints.all = 0; |
johnGreeneMaxim | 9:affd4e6372a0 | 592 | ints.bits.a_full = 1; // Enable FIFO almost full interrupt |
johnGreeneMaxim | 11:976c80cc99d5 | 593 | ints.bits.ppg_rdy =1; //Enables an interrupt when a new sample is ready |
coreyharris | 0:0bd4103885bf | 594 | rc = op_sensor.enableInterrupts(ints); |
coreyharris | 0:0bd4103885bf | 595 | } |
johnGreeneMaxim | 11:976c80cc99d5 | 596 | |
coreyharris | 0:0bd4103885bf | 597 | //configure FIFO |
coreyharris | 0:0bd4103885bf | 598 | MAX30101::FIFO_Configuration_u fifoConfig; |
johnGreeneMaxim | 11:976c80cc99d5 | 599 | if(rc == 0) { |
coreyharris | 0:0bd4103885bf | 600 | fifoConfig.all = 0; |
johnGreeneMaxim | 11:976c80cc99d5 | 601 | fifoConfig.bits.fifo_a_full = 10; // Max level of 17 samples |
johnGreeneMaxim | 11:976c80cc99d5 | 602 | fifoConfig.bits.sample_average = MAX30101::AveragedSamples_0;// Average 0 samples |
coreyharris | 0:0bd4103885bf | 603 | rc = op_sensor.setFIFOConfiguration(fifoConfig); |
coreyharris | 0:0bd4103885bf | 604 | } |
johnGreeneMaxim | 11:976c80cc99d5 | 605 | |
coreyharris | 0:0bd4103885bf | 606 | MAX30101::SpO2Configuration_u spo2Config; |
johnGreeneMaxim | 11:976c80cc99d5 | 607 | if(rc == 0) { |
johnGreeneMaxim | 11:976c80cc99d5 | 608 | spo2Config.all = 0; // clears register |
johnGreeneMaxim | 11:976c80cc99d5 | 609 | spo2Config.bits.spo2_adc_range = 1; //sets resolution to 4096 nAfs |
johnGreeneMaxim | 11:976c80cc99d5 | 610 | spo2Config.bits.spo2_sr = MAX30101::SR_100_Hz; // SpO2 SR = 100Hz |
johnGreeneMaxim | 11:976c80cc99d5 | 611 | spo2Config.bits.led_pw = MAX30101::PW_3; // 18-bit ADC resolution ~400us |
coreyharris | 0:0bd4103885bf | 612 | rc = op_sensor.setSpO2Configuration(spo2Config); |
coreyharris | 0:0bd4103885bf | 613 | } |
johnGreeneMaxim | 11:976c80cc99d5 | 614 | |
johnGreeneMaxim | 11:976c80cc99d5 | 615 | //Set time slots for LEDS |
johnGreeneMaxim | 11:976c80cc99d5 | 616 | MAX30101::ModeControlReg_u multiLED; |
johnGreeneMaxim | 11:976c80cc99d5 | 617 | if(rc==0) { |
johnGreeneMaxim | 11:976c80cc99d5 | 618 | //sets timing for control register 1 |
johnGreeneMaxim | 11:976c80cc99d5 | 619 | multiLED.bits.lo_slot=1; |
johnGreeneMaxim | 11:976c80cc99d5 | 620 | multiLED.bits.hi_slot=2; |
johnGreeneMaxim | 11:976c80cc99d5 | 621 | rc = op_sensor.setMultiLEDModeControl(MAX30101::ModeControlReg1, multiLED); |
johnGreeneMaxim | 11:976c80cc99d5 | 622 | if(rc==0) { |
johnGreeneMaxim | 11:976c80cc99d5 | 623 | multiLED.bits.lo_slot=3; |
johnGreeneMaxim | 11:976c80cc99d5 | 624 | multiLED.bits.hi_slot=0; |
johnGreeneMaxim | 11:976c80cc99d5 | 625 | rc = op_sensor.setMultiLEDModeControl(MAX30101::ModeControlReg2, multiLED); |
johnGreeneMaxim | 9:affd4e6372a0 | 626 | } |
coreyharris | 0:0bd4103885bf | 627 | } |
johnGreeneMaxim | 11:976c80cc99d5 | 628 | |
johnGreeneMaxim | 11:976c80cc99d5 | 629 | //Set LED drive currents |
johnGreeneMaxim | 11:976c80cc99d5 | 630 | if(rc == 0) { |
johnGreeneMaxim | 11:976c80cc99d5 | 631 | // Heart Rate only, 1 LED channel, Pulse amp. = ~7mA |
johnGreeneMaxim | 11:976c80cc99d5 | 632 | rc = op_sensor.setLEDPulseAmplitude(MAX30101::LED1_PA, 0x24); |
johnGreeneMaxim | 11:976c80cc99d5 | 633 | //To include SPO2, 2 LED channel, Pulse amp. ~7mA |
johnGreeneMaxim | 11:976c80cc99d5 | 634 | if(rc==0) { |
johnGreeneMaxim | 11:976c80cc99d5 | 635 | rc = op_sensor.setLEDPulseAmplitude(MAX30101::LED2_PA, 0x24); |
johnGreeneMaxim | 11:976c80cc99d5 | 636 | } |
johnGreeneMaxim | 11:976c80cc99d5 | 637 | if(rc==0) { |
johnGreeneMaxim | 11:976c80cc99d5 | 638 | rc = op_sensor.setLEDPulseAmplitude(MAX30101::LED3_PA, 0x24); |
johnGreeneMaxim | 11:976c80cc99d5 | 639 | } |
johnGreeneMaxim | 11:976c80cc99d5 | 640 | |
johnGreeneMaxim | 11:976c80cc99d5 | 641 | } |
johnGreeneMaxim | 11:976c80cc99d5 | 642 | |
coreyharris | 0:0bd4103885bf | 643 | //Set operating mode |
coreyharris | 0:0bd4103885bf | 644 | modeConfig.all = 0; |
johnGreeneMaxim | 11:976c80cc99d5 | 645 | if(rc == 0) { |
johnGreeneMaxim | 11:976c80cc99d5 | 646 | modeConfig.bits.mode = MAX30101::MultiLedMode; // Sets multiLED mode |
coreyharris | 0:0bd4103885bf | 647 | rc = op_sensor.setModeConfiguration(modeConfig); |
johnGreeneMaxim | 11:976c80cc99d5 | 648 | } |
johnGreeneMaxim | 11:976c80cc99d5 | 649 | |
johnGreeneMaxim | 11:976c80cc99d5 | 650 | |
coreyharris | 0:0bd4103885bf | 651 | return rc; |
coreyharris | 0:0bd4103885bf | 652 | } |
johnGreeneMaxim | 8:a1538e8a3fd9 | 653 | |
johnGreeneMaxim | 8:a1538e8a3fd9 | 654 | void pmic_config(I2C & i2c_bus, DigitalOut & pmic_en) |
johnGreeneMaxim | 8:a1538e8a3fd9 | 655 | { |
johnGreeneMaxim | 11:976c80cc99d5 | 656 | |
johnGreeneMaxim | 8:a1538e8a3fd9 | 657 | const uint8_t PMIC_ADRS = 0x54; |
johnGreeneMaxim | 8:a1538e8a3fd9 | 658 | const uint8_t BBB_EXTRA_ADRS = 0x1C; |
johnGreeneMaxim | 8:a1538e8a3fd9 | 659 | const uint8_t BOOST_VOLTAGE = 0x05; |
johnGreeneMaxim | 11:976c80cc99d5 | 660 | |
johnGreeneMaxim | 11:976c80cc99d5 | 661 | char data_buff[] = {BBB_EXTRA_ADRS, 0x40}; //BBBExtra register address |
johnGreeneMaxim | 11:976c80cc99d5 | 662 | //and data to enable passive |
johnGreeneMaxim | 11:976c80cc99d5 | 663 | //pull down. |
johnGreeneMaxim | 8:a1538e8a3fd9 | 664 | i2c_bus.write(PMIC_ADRS, data_buff,2); //write to BBBExtra register |
johnGreeneMaxim | 11:976c80cc99d5 | 665 | |
johnGreeneMaxim | 8:a1538e8a3fd9 | 666 | data_buff[0] = BOOST_VOLTAGE; |
johnGreeneMaxim | 11:976c80cc99d5 | 667 | data_buff[1] = 0x08; //Boost voltage configuration |
johnGreeneMaxim | 11:976c80cc99d5 | 668 | //register followed by data |
johnGreeneMaxim | 11:976c80cc99d5 | 669 | //to set voltage to 4.5V 1f |
johnGreeneMaxim | 11:976c80cc99d5 | 670 | pmic_en = 0; //disables VLED 08 |
johnGreeneMaxim | 8:a1538e8a3fd9 | 671 | i2c_bus.write(PMIC_ADRS, data_buff,2); //write to BBBExtra register |
johnGreeneMaxim | 11:976c80cc99d5 | 672 | pmic_en = 1; //enables VLED |
johnGreeneMaxim | 11:976c80cc99d5 | 673 | } |
johnGreeneMaxim | 11:976c80cc99d5 | 674 | |
johnGreeneMaxim | 11:976c80cc99d5 | 675 | |
johnGreeneMaxim | 11:976c80cc99d5 | 676 | |
johnGreeneMaxim | 11:976c80cc99d5 | 677 | // |
johnGreeneMaxim | 11:976c80cc99d5 | 678 | // Heart Rate/SpO2 Monitor function takes sample input 'dinIR' and dinRed. |
johnGreeneMaxim | 11:976c80cc99d5 | 679 | // Other inputs: |
johnGreeneMaxim | 11:976c80cc99d5 | 680 | // ns -> Sample Counter, increments with each sample input. |
johnGreeneMaxim | 11:976c80cc99d5 | 681 | // SampRate -> Input data real-time sample rate. |
johnGreeneMaxim | 11:976c80cc99d5 | 682 | // dinLShft -> Number of left shifts for data to be 16 bit wide. |
johnGreeneMaxim | 11:976c80cc99d5 | 683 | // compSpO2 -> If '1' compute SpO2 value,else compute HR only. |
johnGreeneMaxim | 11:976c80cc99d5 | 684 | |
johnGreeneMaxim | 11:976c80cc99d5 | 685 | // |
johnGreeneMaxim | 11:976c80cc99d5 | 686 | // Outputs: |
johnGreeneMaxim | 11:976c80cc99d5 | 687 | // ir_ac_comp -> AC component of the IR signal. |
johnGreeneMaxim | 11:976c80cc99d5 | 688 | // red_ac_comp -> AC component of the Red signal. |
johnGreeneMaxim | 11:976c80cc99d5 | 689 | // ir_ac_mag -> Peak to Peak magnitude of the IR signal. |
johnGreeneMaxim | 11:976c80cc99d5 | 690 | // red_ac_mag -> Peak to Peak magnitude of the Red signal. |
johnGreeneMaxim | 11:976c80cc99d5 | 691 | // HRbpm -> Heart Rate in beats per minute. |
johnGreeneMaxim | 11:976c80cc99d5 | 692 | // SpO2 -> SpO2 value as %saturation. |
johnGreeneMaxim | 11:976c80cc99d5 | 693 | // DRdy -> '1' when new data is available. |
johnGreeneMaxim | 11:976c80cc99d5 | 694 | // |
johnGreeneMaxim | 11:976c80cc99d5 | 695 | |
johnGreeneMaxim | 11:976c80cc99d5 | 696 | void HRSpO2Func(uint32_t dinIR, uint32_t dinRed, uint32_t dinGreen, uint32_t ns,uint16_t SampRate,uint16_t compSpO2, |
johnGreeneMaxim | 11:976c80cc99d5 | 697 | int16_t *ir_ac_comp,int16_t *red_ac_comp, int16_t *green_ac_comp, int16_t *ir_ac_mag,int16_t *red_ac_mag, |
johnGreeneMaxim | 11:976c80cc99d5 | 698 | int16_t *green_ac_mag, uint16_t *HRbpm2,uint16_t *SpO2B,uint16_t *DRdy ) |
johnGreeneMaxim | 11:976c80cc99d5 | 699 | { |
johnGreeneMaxim | 11:976c80cc99d5 | 700 | static int32_t ir_avg_reg=0; |
johnGreeneMaxim | 11:976c80cc99d5 | 701 | static int32_t red_avg_reg=0; |
johnGreeneMaxim | 11:976c80cc99d5 | 702 | static int32_t green_avg_reg=0; |
johnGreeneMaxim | 11:976c80cc99d5 | 703 | |
johnGreeneMaxim | 11:976c80cc99d5 | 704 | static int16_t ir_ac_sig_cur=0; |
johnGreeneMaxim | 11:976c80cc99d5 | 705 | static int16_t ir_ac_sig_pre; |
johnGreeneMaxim | 11:976c80cc99d5 | 706 | static int16_t ir_ac_sig_min=0; |
johnGreeneMaxim | 11:976c80cc99d5 | 707 | static int16_t ir_ac_sig_max=0; |
johnGreeneMaxim | 11:976c80cc99d5 | 708 | static int16_t ir_avg_est; |
johnGreeneMaxim | 11:976c80cc99d5 | 709 | |
johnGreeneMaxim | 11:976c80cc99d5 | 710 | static int16_t ir_pedge=0, ir_nedge=0; |
johnGreeneMaxim | 11:976c80cc99d5 | 711 | static int16_t ir_pzxic, ir_pzxip; |
johnGreeneMaxim | 11:976c80cc99d5 | 712 | static int16_t ir_nzxic; |
johnGreeneMaxim | 11:976c80cc99d5 | 713 | |
johnGreeneMaxim | 11:976c80cc99d5 | 714 | static int16_t red_ac_sig_cur=0; |
johnGreeneMaxim | 11:976c80cc99d5 | 715 | static int16_t red_ac_sig_min=0; |
johnGreeneMaxim | 11:976c80cc99d5 | 716 | static int16_t red_ac_sig_max=0; |
johnGreeneMaxim | 11:976c80cc99d5 | 717 | static int16_t red_avg_est; |
johnGreeneMaxim | 11:976c80cc99d5 | 718 | |
johnGreeneMaxim | 11:976c80cc99d5 | 719 | static int16_t green_avg_est; |
johnGreeneMaxim | 11:976c80cc99d5 | 720 | static int16_t green_ac_sig_cur=0; |
johnGreeneMaxim | 11:976c80cc99d5 | 721 | //static int16_t green_ac_sig_cur=0; |
johnGreeneMaxim | 11:976c80cc99d5 | 722 | static int16_t green_ac_sig_pre; |
johnGreeneMaxim | 11:976c80cc99d5 | 723 | static int16_t green_ac_sig_max ; |
johnGreeneMaxim | 11:976c80cc99d5 | 724 | static int16_t green_ac_sig_min; |
johnGreeneMaxim | 11:976c80cc99d5 | 725 | static int16_t green_mac_FIFO[5]; |
johnGreeneMaxim | 11:976c80cc99d5 | 726 | int16_t meanGreenMagFIFO; |
johnGreeneMaxim | 11:976c80cc99d5 | 727 | int16_t minAmpForHeartBeat ; |
johnGreeneMaxim | 11:976c80cc99d5 | 728 | |
johnGreeneMaxim | 11:976c80cc99d5 | 729 | uint32_t IRData,RedData, greenData , rnum,rden,rdens; |
johnGreeneMaxim | 11:976c80cc99d5 | 730 | uint16_t zeros_in_HrQue =0 , posCount=0; |
johnGreeneMaxim | 11:976c80cc99d5 | 731 | static uint32_t prevPeakLoc = 0 ; |
johnGreeneMaxim | 11:976c80cc99d5 | 732 | static int16_t IrFIFO[100]; |
johnGreeneMaxim | 11:976c80cc99d5 | 733 | static int16_t HrQue[10], lastKnownGoodHr[10]; |
johnGreeneMaxim | 11:976c80cc99d5 | 734 | static int16_t SPO2Que[5]; |
johnGreeneMaxim | 11:976c80cc99d5 | 735 | int16_t SPO2score[5]; |
johnGreeneMaxim | 11:976c80cc99d5 | 736 | static uint16_t HrQindex=0 ,lengthOfposCountExceeding =0 ; |
johnGreeneMaxim | 11:976c80cc99d5 | 737 | static uint16_t initHrQueCounter=0 , fingerOff =0; |
johnGreeneMaxim | 11:976c80cc99d5 | 738 | |
johnGreeneMaxim | 11:976c80cc99d5 | 739 | static int16_t HrQueSmoothing[3]; |
johnGreeneMaxim | 11:976c80cc99d5 | 740 | static int16_t SPO2QueSmoothing[3]; |
johnGreeneMaxim | 11:976c80cc99d5 | 741 | |
johnGreeneMaxim | 11:976c80cc99d5 | 742 | int16_t k, j; |
johnGreeneMaxim | 11:976c80cc99d5 | 743 | uint32_t peakLoc ; |
johnGreeneMaxim | 11:976c80cc99d5 | 744 | int16_t bufferIdx1, bufferIdx2; |
johnGreeneMaxim | 11:976c80cc99d5 | 745 | int16_t maxFIFO ,IdxMaxFIFO ; |
johnGreeneMaxim | 11:976c80cc99d5 | 746 | int16_t HRperiod2, HRComp2 ,deltaHR; |
johnGreeneMaxim | 11:976c80cc99d5 | 747 | int16_t cSpO2, SpO2; |
johnGreeneMaxim | 11:976c80cc99d5 | 748 | |
johnGreeneMaxim | 11:976c80cc99d5 | 749 | int16_t HrCount =0, HrSum =0 ,meanGreenMagFIFOcounter =0; |
johnGreeneMaxim | 11:976c80cc99d5 | 750 | int16_t SPO2D ,meanHrQ; |
johnGreeneMaxim | 11:976c80cc99d5 | 751 | int16_t dx[99] ,cumsumX[99]; |
johnGreeneMaxim | 11:976c80cc99d5 | 752 | static int16_t SPO2QueCounter =0 ;//, lastDisplayedHrValue; |
johnGreeneMaxim | 11:976c80cc99d5 | 753 | |
johnGreeneMaxim | 11:976c80cc99d5 | 754 | int16_t validSPO2Count =0; |
johnGreeneMaxim | 11:976c80cc99d5 | 755 | int16_t validSPO2Sum =0; |
johnGreeneMaxim | 11:976c80cc99d5 | 756 | int16_t SPO2scoreAverage= 0; |
johnGreeneMaxim | 11:976c80cc99d5 | 757 | int16_t SPO2scoreSum =0 ; |
johnGreeneMaxim | 11:976c80cc99d5 | 758 | // int16_t deltaMeanLastKnownGoodHr=0,meanLastKnownGoodHr =0 ; |
johnGreeneMaxim | 11:976c80cc99d5 | 759 | // int16_t counterMeanLastKnownGoodHr =0 ; |
johnGreeneMaxim | 11:976c80cc99d5 | 760 | |
johnGreeneMaxim | 11:976c80cc99d5 | 761 | |
johnGreeneMaxim | 11:976c80cc99d5 | 762 | // clear some vars if fresh new start |
johnGreeneMaxim | 11:976c80cc99d5 | 763 | if ((ns ==0) || (fingerOff >300)) { |
johnGreeneMaxim | 11:976c80cc99d5 | 764 | ir_avg_reg=0; |
johnGreeneMaxim | 11:976c80cc99d5 | 765 | red_avg_reg=0; |
johnGreeneMaxim | 11:976c80cc99d5 | 766 | green_avg_reg=0; |
johnGreeneMaxim | 11:976c80cc99d5 | 767 | |
johnGreeneMaxim | 11:976c80cc99d5 | 768 | ir_ac_sig_cur=0; |
johnGreeneMaxim | 11:976c80cc99d5 | 769 | ir_ac_sig_pre=0; |
johnGreeneMaxim | 11:976c80cc99d5 | 770 | ir_ac_sig_min=0; |
johnGreeneMaxim | 11:976c80cc99d5 | 771 | ir_ac_sig_max=0; |
johnGreeneMaxim | 11:976c80cc99d5 | 772 | |
johnGreeneMaxim | 11:976c80cc99d5 | 773 | ir_avg_est=0; |
johnGreeneMaxim | 11:976c80cc99d5 | 774 | green_avg_est =0; |
johnGreeneMaxim | 11:976c80cc99d5 | 775 | red_avg_est =0 ; |
johnGreeneMaxim | 11:976c80cc99d5 | 776 | |
johnGreeneMaxim | 11:976c80cc99d5 | 777 | ir_pedge=0; |
johnGreeneMaxim | 11:976c80cc99d5 | 778 | ir_nedge=0; |
johnGreeneMaxim | 11:976c80cc99d5 | 779 | ir_pzxic=0; |
johnGreeneMaxim | 11:976c80cc99d5 | 780 | ir_pzxip =0; |
johnGreeneMaxim | 11:976c80cc99d5 | 781 | ir_nzxic=0 ; |
johnGreeneMaxim | 11:976c80cc99d5 | 782 | //ir_nzxip =0; |
johnGreeneMaxim | 11:976c80cc99d5 | 783 | red_ac_sig_cur=0; |
johnGreeneMaxim | 11:976c80cc99d5 | 784 | red_ac_sig_min=0; |
johnGreeneMaxim | 11:976c80cc99d5 | 785 | red_ac_sig_max=0; |
johnGreeneMaxim | 11:976c80cc99d5 | 786 | |
johnGreeneMaxim | 11:976c80cc99d5 | 787 | prevPeakLoc = 0 ; |
johnGreeneMaxim | 11:976c80cc99d5 | 788 | bufferIdx1=0 ; |
johnGreeneMaxim | 11:976c80cc99d5 | 789 | bufferIdx2=0; |
johnGreeneMaxim | 11:976c80cc99d5 | 790 | HrQindex =0; |
johnGreeneMaxim | 11:976c80cc99d5 | 791 | initHrQueCounter=0; |
johnGreeneMaxim | 11:976c80cc99d5 | 792 | lengthOfposCountExceeding =0 ; |
johnGreeneMaxim | 11:976c80cc99d5 | 793 | fingerOff =0; |
johnGreeneMaxim | 11:976c80cc99d5 | 794 | HRComp2 =0; |
johnGreeneMaxim | 11:976c80cc99d5 | 795 | |
johnGreeneMaxim | 11:976c80cc99d5 | 796 | for (k=0 ; k<100 ; k++) { |
johnGreeneMaxim | 11:976c80cc99d5 | 797 | IrFIFO[k]= 0; |
johnGreeneMaxim | 11:976c80cc99d5 | 798 | } |
johnGreeneMaxim | 11:976c80cc99d5 | 799 | for (k=0 ; k<10 ; k++) { |
johnGreeneMaxim | 11:976c80cc99d5 | 800 | HrQue[k]= 0; |
johnGreeneMaxim | 11:976c80cc99d5 | 801 | lastKnownGoodHr[k]=0; |
johnGreeneMaxim | 11:976c80cc99d5 | 802 | } |
johnGreeneMaxim | 11:976c80cc99d5 | 803 | for (k=0 ; k<3 ; k++) { |
johnGreeneMaxim | 11:976c80cc99d5 | 804 | HrQueSmoothing[k]= 70; |
johnGreeneMaxim | 11:976c80cc99d5 | 805 | SPO2QueSmoothing[k]=97; |
johnGreeneMaxim | 11:976c80cc99d5 | 806 | } |
johnGreeneMaxim | 11:976c80cc99d5 | 807 | for (k=0 ; k<5 ; k++) { |
johnGreeneMaxim | 11:976c80cc99d5 | 808 | SPO2Que[k] =97; |
johnGreeneMaxim | 11:976c80cc99d5 | 809 | SPO2score[k] =0; |
johnGreeneMaxim | 11:976c80cc99d5 | 810 | green_mac_FIFO[k] =0; |
johnGreeneMaxim | 11:976c80cc99d5 | 811 | } |
johnGreeneMaxim | 11:976c80cc99d5 | 812 | SPO2QueCounter =0; |
johnGreeneMaxim | 11:976c80cc99d5 | 813 | *SpO2B =97; |
johnGreeneMaxim | 11:976c80cc99d5 | 814 | *HRbpm2 = 0; |
johnGreeneMaxim | 11:976c80cc99d5 | 815 | *DRdy =0 ; |
johnGreeneMaxim | 11:976c80cc99d5 | 816 | |
johnGreeneMaxim | 11:976c80cc99d5 | 817 | } |
johnGreeneMaxim | 11:976c80cc99d5 | 818 | |
johnGreeneMaxim | 11:976c80cc99d5 | 819 | |
johnGreeneMaxim | 11:976c80cc99d5 | 820 | // Save current state |
johnGreeneMaxim | 11:976c80cc99d5 | 821 | green_ac_sig_pre = green_ac_sig_cur; |
johnGreeneMaxim | 11:976c80cc99d5 | 822 | // |
johnGreeneMaxim | 11:976c80cc99d5 | 823 | // Process next data sample |
johnGreeneMaxim | 11:976c80cc99d5 | 824 | minAmpForHeartBeat =0; |
johnGreeneMaxim | 11:976c80cc99d5 | 825 | IRData = dinIR; |
johnGreeneMaxim | 11:976c80cc99d5 | 826 | RedData = dinRed; |
johnGreeneMaxim | 11:976c80cc99d5 | 827 | greenData = dinGreen ; |
johnGreeneMaxim | 11:976c80cc99d5 | 828 | |
johnGreeneMaxim | 11:976c80cc99d5 | 829 | ir_avg_est = avg_dc_est(&ir_avg_reg,IRData); |
johnGreeneMaxim | 11:976c80cc99d5 | 830 | red_avg_est = avg_dc_est(&red_avg_reg,RedData); |
johnGreeneMaxim | 11:976c80cc99d5 | 831 | green_avg_est = avg_dc_est(&green_avg_reg,greenData); |
johnGreeneMaxim | 11:976c80cc99d5 | 832 | |
johnGreeneMaxim | 11:976c80cc99d5 | 833 | lp_dfir_flt((uint16_t)(IRData - ir_avg_est),(uint16_t)(RedData - red_avg_est), |
johnGreeneMaxim | 11:976c80cc99d5 | 834 | (uint16_t)(greenData - green_avg_est), &ir_ac_sig_cur,&red_ac_sig_cur,&green_ac_sig_cur); |
johnGreeneMaxim | 11:976c80cc99d5 | 835 | |
johnGreeneMaxim | 11:976c80cc99d5 | 836 | |
johnGreeneMaxim | 11:976c80cc99d5 | 837 | *ir_ac_comp = ir_ac_sig_cur; |
johnGreeneMaxim | 11:976c80cc99d5 | 838 | *red_ac_comp = red_ac_sig_cur; |
johnGreeneMaxim | 11:976c80cc99d5 | 839 | *green_ac_comp = green_ac_sig_cur; |
johnGreeneMaxim | 11:976c80cc99d5 | 840 | |
johnGreeneMaxim | 11:976c80cc99d5 | 841 | //save to FIFO |
johnGreeneMaxim | 11:976c80cc99d5 | 842 | for (k=1 ; k<100 ; k++) { |
johnGreeneMaxim | 11:976c80cc99d5 | 843 | IrFIFO[100 -k]= IrFIFO[99-k]; |
johnGreeneMaxim | 11:976c80cc99d5 | 844 | } |
johnGreeneMaxim | 11:976c80cc99d5 | 845 | IrFIFO[0] = green_ac_sig_cur ; // invert |
johnGreeneMaxim | 11:976c80cc99d5 | 846 | for (k=0 ; k<97 ; k++) |
johnGreeneMaxim | 11:976c80cc99d5 | 847 | dx[k]= IrFIFO[k+2]-IrFIFO[k] ; |
johnGreeneMaxim | 11:976c80cc99d5 | 848 | dx[97]= dx[96]; |
johnGreeneMaxim | 11:976c80cc99d5 | 849 | dx[98]=dx[96]; |
johnGreeneMaxim | 11:976c80cc99d5 | 850 | |
johnGreeneMaxim | 11:976c80cc99d5 | 851 | for (k=0 ; k<99 ; k++) { |
johnGreeneMaxim | 11:976c80cc99d5 | 852 | if (dx[k] > 0 ) |
johnGreeneMaxim | 11:976c80cc99d5 | 853 | dx[k]=1; |
johnGreeneMaxim | 11:976c80cc99d5 | 854 | else |
johnGreeneMaxim | 11:976c80cc99d5 | 855 | dx[k] = 0; |
johnGreeneMaxim | 11:976c80cc99d5 | 856 | } |
johnGreeneMaxim | 11:976c80cc99d5 | 857 | |
johnGreeneMaxim | 11:976c80cc99d5 | 858 | cumsumX[0] =0; |
johnGreeneMaxim | 11:976c80cc99d5 | 859 | for (k=1; k<99 ; k++) { |
johnGreeneMaxim | 11:976c80cc99d5 | 860 | if (dx[k]>0 ) |
johnGreeneMaxim | 11:976c80cc99d5 | 861 | cumsumX[k]= cumsumX[k-1] + dx[k] ; |
johnGreeneMaxim | 11:976c80cc99d5 | 862 | else |
johnGreeneMaxim | 11:976c80cc99d5 | 863 | cumsumX[k]= 0; |
johnGreeneMaxim | 11:976c80cc99d5 | 864 | } |
johnGreeneMaxim | 11:976c80cc99d5 | 865 | // determine noise |
johnGreeneMaxim | 11:976c80cc99d5 | 866 | // ignore less than 3 conseuctive non-zeros's |
johnGreeneMaxim | 11:976c80cc99d5 | 867 | // detect # of sign change |
johnGreeneMaxim | 11:976c80cc99d5 | 868 | posCount=0; |
johnGreeneMaxim | 11:976c80cc99d5 | 869 | for (k=1; k<99 ; k++) { |
johnGreeneMaxim | 11:976c80cc99d5 | 870 | if (cumsumX[k]>0 ) { |
johnGreeneMaxim | 11:976c80cc99d5 | 871 | posCount ++ ; |
johnGreeneMaxim | 11:976c80cc99d5 | 872 | } else if (cumsumX[k]==0 ) { |
johnGreeneMaxim | 11:976c80cc99d5 | 873 | if (posCount<4 && k>=4) { |
johnGreeneMaxim | 11:976c80cc99d5 | 874 | for ( j= k-1; j> k-posCount-1; j--) |
johnGreeneMaxim | 11:976c80cc99d5 | 875 | cumsumX[j]=0 ; |
johnGreeneMaxim | 11:976c80cc99d5 | 876 | } |
johnGreeneMaxim | 11:976c80cc99d5 | 877 | posCount =0; |
johnGreeneMaxim | 11:976c80cc99d5 | 878 | } |
johnGreeneMaxim | 11:976c80cc99d5 | 879 | } |
johnGreeneMaxim | 11:976c80cc99d5 | 880 | // ignore less than 3 conseuctive zeros's |
johnGreeneMaxim | 11:976c80cc99d5 | 881 | |
johnGreeneMaxim | 11:976c80cc99d5 | 882 | posCount=0; |
johnGreeneMaxim | 11:976c80cc99d5 | 883 | for (k=1; k<99 ; k++) { |
johnGreeneMaxim | 11:976c80cc99d5 | 884 | if (cumsumX[k]==0 ) { |
johnGreeneMaxim | 11:976c80cc99d5 | 885 | posCount ++ ; |
johnGreeneMaxim | 11:976c80cc99d5 | 886 | } else if (cumsumX[k] > 0 ) { |
johnGreeneMaxim | 11:976c80cc99d5 | 887 | if (posCount<4 && k>=4) { |
johnGreeneMaxim | 11:976c80cc99d5 | 888 | for ( j= k-1; j> k-posCount-1; j--) |
johnGreeneMaxim | 11:976c80cc99d5 | 889 | cumsumX[j]=100 ; |
johnGreeneMaxim | 11:976c80cc99d5 | 890 | } |
johnGreeneMaxim | 11:976c80cc99d5 | 891 | posCount =0; |
johnGreeneMaxim | 11:976c80cc99d5 | 892 | } |
johnGreeneMaxim | 11:976c80cc99d5 | 893 | } |
johnGreeneMaxim | 11:976c80cc99d5 | 894 | |
johnGreeneMaxim | 11:976c80cc99d5 | 895 | //// detect # of sign change |
johnGreeneMaxim | 11:976c80cc99d5 | 896 | posCount=0; // sign change counter |
johnGreeneMaxim | 11:976c80cc99d5 | 897 | for (k=0; k<98 ; k++) { |
johnGreeneMaxim | 11:976c80cc99d5 | 898 | if (cumsumX[k]==0 && cumsumX[k+1] > 0 ) { |
johnGreeneMaxim | 11:976c80cc99d5 | 899 | posCount ++; |
johnGreeneMaxim | 11:976c80cc99d5 | 900 | } |
johnGreeneMaxim | 11:976c80cc99d5 | 901 | } |
johnGreeneMaxim | 11:976c80cc99d5 | 902 | if (posCount>=4) { |
johnGreeneMaxim | 11:976c80cc99d5 | 903 | lengthOfposCountExceeding ++ ; |
johnGreeneMaxim | 11:976c80cc99d5 | 904 | // printf("PosCount =%i \n", posCount ); |
johnGreeneMaxim | 11:976c80cc99d5 | 905 | } else |
johnGreeneMaxim | 11:976c80cc99d5 | 906 | lengthOfposCountExceeding = 0 ; |
johnGreeneMaxim | 11:976c80cc99d5 | 907 | // Detect IR channel positive zero crossing (rising edge) |
johnGreeneMaxim | 11:976c80cc99d5 | 908 | if ((green_ac_sig_pre < 0) && (green_ac_sig_cur >= 0) && fingerOff==0 ) { |
johnGreeneMaxim | 11:976c80cc99d5 | 909 | |
johnGreeneMaxim | 11:976c80cc99d5 | 910 | *ir_ac_mag = ir_ac_sig_max - ir_ac_sig_min; |
johnGreeneMaxim | 11:976c80cc99d5 | 911 | *red_ac_mag = red_ac_sig_max - red_ac_sig_min; |
johnGreeneMaxim | 11:976c80cc99d5 | 912 | *green_ac_mag = green_ac_sig_max - green_ac_sig_min; |
johnGreeneMaxim | 11:976c80cc99d5 | 913 | if ( *green_ac_mag>0 ) { |
johnGreeneMaxim | 11:976c80cc99d5 | 914 | for (k=0; k<4 ; k++) |
johnGreeneMaxim | 11:976c80cc99d5 | 915 | green_mac_FIFO[k]=green_mac_FIFO[k+1]; |
johnGreeneMaxim | 11:976c80cc99d5 | 916 | green_mac_FIFO[4] = *green_ac_mag ; |
johnGreeneMaxim | 11:976c80cc99d5 | 917 | if ( green_mac_FIFO[4] > 1000) |
johnGreeneMaxim | 11:976c80cc99d5 | 918 | green_mac_FIFO[4] =1000; |
johnGreeneMaxim | 11:976c80cc99d5 | 919 | } |
johnGreeneMaxim | 11:976c80cc99d5 | 920 | meanGreenMagFIFO=0; |
johnGreeneMaxim | 11:976c80cc99d5 | 921 | meanGreenMagFIFOcounter=0; |
johnGreeneMaxim | 11:976c80cc99d5 | 922 | for (k=0; k<5 ; k++) { |
johnGreeneMaxim | 11:976c80cc99d5 | 923 | if( green_mac_FIFO[k] >0) { |
johnGreeneMaxim | 11:976c80cc99d5 | 924 | meanGreenMagFIFO= meanGreenMagFIFO +green_mac_FIFO[k] ; |
johnGreeneMaxim | 11:976c80cc99d5 | 925 | meanGreenMagFIFOcounter++; |
johnGreeneMaxim | 11:976c80cc99d5 | 926 | } |
johnGreeneMaxim | 11:976c80cc99d5 | 927 | } |
johnGreeneMaxim | 11:976c80cc99d5 | 928 | if (meanGreenMagFIFOcounter>=2 ) { |
johnGreeneMaxim | 11:976c80cc99d5 | 929 | meanGreenMagFIFO =meanGreenMagFIFO/ meanGreenMagFIFOcounter ; |
johnGreeneMaxim | 11:976c80cc99d5 | 930 | minAmpForHeartBeat= meanGreenMagFIFO /4 ; //25% of mean of past heart beat |
johnGreeneMaxim | 11:976c80cc99d5 | 931 | } else |
johnGreeneMaxim | 11:976c80cc99d5 | 932 | minAmpForHeartBeat = 75; |
johnGreeneMaxim | 11:976c80cc99d5 | 933 | if (minAmpForHeartBeat <75) |
johnGreeneMaxim | 11:976c80cc99d5 | 934 | minAmpForHeartBeat =75; |
johnGreeneMaxim | 11:976c80cc99d5 | 935 | if (minAmpForHeartBeat >400) |
johnGreeneMaxim | 11:976c80cc99d5 | 936 | minAmpForHeartBeat =400; |
johnGreeneMaxim | 11:976c80cc99d5 | 937 | |
johnGreeneMaxim | 11:976c80cc99d5 | 938 | ir_pedge = 1; |
johnGreeneMaxim | 11:976c80cc99d5 | 939 | ir_nedge = 0; |
johnGreeneMaxim | 11:976c80cc99d5 | 940 | ir_ac_sig_max = 0; |
johnGreeneMaxim | 11:976c80cc99d5 | 941 | ir_pzxip = ir_pzxic; |
johnGreeneMaxim | 11:976c80cc99d5 | 942 | ir_pzxic = ns; |
johnGreeneMaxim | 11:976c80cc99d5 | 943 | bufferIdx1= ir_pzxic- ir_nzxic; |
johnGreeneMaxim | 11:976c80cc99d5 | 944 | bufferIdx2 = ir_pzxic -ir_pzxip; |
johnGreeneMaxim | 11:976c80cc99d5 | 945 | |
johnGreeneMaxim | 11:976c80cc99d5 | 946 | |
johnGreeneMaxim | 11:976c80cc99d5 | 947 | if ((*green_ac_mag)> minAmpForHeartBeat && (*green_ac_mag)< 20000 && bufferIdx1>=0 |
johnGreeneMaxim | 11:976c80cc99d5 | 948 | && bufferIdx1 <100 && bufferIdx2>=0 && bufferIdx2 <100 && bufferIdx1< bufferIdx2 ) { // was <5000 |
johnGreeneMaxim | 11:976c80cc99d5 | 949 | maxFIFO = -32766; |
johnGreeneMaxim | 11:976c80cc99d5 | 950 | |
johnGreeneMaxim | 11:976c80cc99d5 | 951 | IdxMaxFIFO = 0; |
johnGreeneMaxim | 11:976c80cc99d5 | 952 | for ( j=bufferIdx1; j<= bufferIdx2; j++) { // find max peak |
johnGreeneMaxim | 11:976c80cc99d5 | 953 | if (IrFIFO[j] > maxFIFO ) { |
johnGreeneMaxim | 11:976c80cc99d5 | 954 | maxFIFO =IrFIFO[j]; |
johnGreeneMaxim | 11:976c80cc99d5 | 955 | IdxMaxFIFO =j; |
johnGreeneMaxim | 11:976c80cc99d5 | 956 | } |
johnGreeneMaxim | 11:976c80cc99d5 | 957 | } |
johnGreeneMaxim | 11:976c80cc99d5 | 958 | peakLoc= ir_pzxic -IdxMaxFIFO+1 ; |
johnGreeneMaxim | 11:976c80cc99d5 | 959 | |
johnGreeneMaxim | 11:976c80cc99d5 | 960 | if (prevPeakLoc !=0) { |
johnGreeneMaxim | 11:976c80cc99d5 | 961 | HRperiod2 =( uint16_t) (peakLoc - prevPeakLoc); |
johnGreeneMaxim | 11:976c80cc99d5 | 962 | if (HRperiod2>33 && HRperiod2 < 134) { |
johnGreeneMaxim | 11:976c80cc99d5 | 963 | HRComp2= (6000/HRperiod2); |
johnGreeneMaxim | 11:976c80cc99d5 | 964 | fingerOff =0 ; |
johnGreeneMaxim | 11:976c80cc99d5 | 965 | } else |
johnGreeneMaxim | 11:976c80cc99d5 | 966 | HRComp2=0 ; |
johnGreeneMaxim | 11:976c80cc99d5 | 967 | } else |
johnGreeneMaxim | 11:976c80cc99d5 | 968 | HRComp2 = 0 ; |
johnGreeneMaxim | 11:976c80cc99d5 | 969 | |
johnGreeneMaxim | 11:976c80cc99d5 | 970 | if ( initHrQueCounter<10 && HRComp2 >0 ) { |
johnGreeneMaxim | 11:976c80cc99d5 | 971 | HrQue[HrQindex] =HRComp2; |
johnGreeneMaxim | 11:976c80cc99d5 | 972 | HrQindex++; |
johnGreeneMaxim | 11:976c80cc99d5 | 973 | initHrQueCounter ++; |
johnGreeneMaxim | 11:976c80cc99d5 | 974 | if (HrQindex== 10) |
johnGreeneMaxim | 11:976c80cc99d5 | 975 | HrQindex =0; |
johnGreeneMaxim | 11:976c80cc99d5 | 976 | } |
johnGreeneMaxim | 11:976c80cc99d5 | 977 | |
johnGreeneMaxim | 11:976c80cc99d5 | 978 | if (initHrQueCounter > 7 && lengthOfposCountExceeding<=3) { |
johnGreeneMaxim | 11:976c80cc99d5 | 979 | if ( HRComp2 > 0) { |
johnGreeneMaxim | 11:976c80cc99d5 | 980 | |
johnGreeneMaxim | 11:976c80cc99d5 | 981 | HrCount =0; |
johnGreeneMaxim | 11:976c80cc99d5 | 982 | HrSum =0; |
johnGreeneMaxim | 11:976c80cc99d5 | 983 | zeros_in_HrQue=0; |
johnGreeneMaxim | 11:976c80cc99d5 | 984 | for (k=1 ; k<initHrQueCounter ; k++) { |
johnGreeneMaxim | 11:976c80cc99d5 | 985 | if (HrQue[k] >0) { |
johnGreeneMaxim | 11:976c80cc99d5 | 986 | HrSum +=HrQue[k]; |
johnGreeneMaxim | 11:976c80cc99d5 | 987 | HrCount ++; |
johnGreeneMaxim | 11:976c80cc99d5 | 988 | } else |
johnGreeneMaxim | 11:976c80cc99d5 | 989 | zeros_in_HrQue ++; |
johnGreeneMaxim | 11:976c80cc99d5 | 990 | } |
johnGreeneMaxim | 11:976c80cc99d5 | 991 | meanHrQ = HrSum/HrCount ; |
johnGreeneMaxim | 11:976c80cc99d5 | 992 | deltaHR= lastKnownGoodHr[0]/10; |
johnGreeneMaxim | 11:976c80cc99d5 | 993 | |
johnGreeneMaxim | 11:976c80cc99d5 | 994 | if ( HRComp2 > lastKnownGoodHr[0] -deltaHR && HRComp2 <lastKnownGoodHr[0] +deltaHR ) { |
johnGreeneMaxim | 11:976c80cc99d5 | 995 | for (k=1 ; k<10 ; k++) { |
johnGreeneMaxim | 11:976c80cc99d5 | 996 | HrQue[10 -k]= HrQue[9-k]; |
johnGreeneMaxim | 11:976c80cc99d5 | 997 | } |
johnGreeneMaxim | 11:976c80cc99d5 | 998 | HrQue[0] =HRComp2; |
johnGreeneMaxim | 11:976c80cc99d5 | 999 | } // HR smmothing using FIFO queue - |
johnGreeneMaxim | 11:976c80cc99d5 | 1000 | |
johnGreeneMaxim | 11:976c80cc99d5 | 1001 | if (zeros_in_HrQue<=2) { |
johnGreeneMaxim | 11:976c80cc99d5 | 1002 | for (k=1 ; k<3 ; k++) { |
johnGreeneMaxim | 11:976c80cc99d5 | 1003 | HrQueSmoothing[3 -k]= HrQueSmoothing[2-k]; |
johnGreeneMaxim | 11:976c80cc99d5 | 1004 | } |
johnGreeneMaxim | 11:976c80cc99d5 | 1005 | HrQueSmoothing[0] = meanHrQ ; |
johnGreeneMaxim | 11:976c80cc99d5 | 1006 | HRComp2 = ( (HrQueSmoothing[0]<<2) + (HrQueSmoothing[1]<<1) + (HrQueSmoothing[2] <<1) ) >>3; |
johnGreeneMaxim | 11:976c80cc99d5 | 1007 | *HRbpm2 =HRComp2 ; |
johnGreeneMaxim | 11:976c80cc99d5 | 1008 | |
johnGreeneMaxim | 11:976c80cc99d5 | 1009 | for (k=1 ; k<10 ; k++) { |
johnGreeneMaxim | 11:976c80cc99d5 | 1010 | lastKnownGoodHr[10 -k]= lastKnownGoodHr[9-k]; |
johnGreeneMaxim | 11:976c80cc99d5 | 1011 | } |
johnGreeneMaxim | 11:976c80cc99d5 | 1012 | lastKnownGoodHr[0] =HRComp2; |
johnGreeneMaxim | 11:976c80cc99d5 | 1013 | } |
johnGreeneMaxim | 11:976c80cc99d5 | 1014 | } |
johnGreeneMaxim | 11:976c80cc99d5 | 1015 | |
johnGreeneMaxim | 11:976c80cc99d5 | 1016 | } |
johnGreeneMaxim | 11:976c80cc99d5 | 1017 | ///// if (initHrQueCounter > 7 && lengthOfposCountExceeding<5) |
johnGreeneMaxim | 11:976c80cc99d5 | 1018 | else if (initHrQueCounter < 7) { // before que is filled up, display whatever it got. |
johnGreeneMaxim | 11:976c80cc99d5 | 1019 | *HRbpm2 = HRComp2; |
johnGreeneMaxim | 11:976c80cc99d5 | 1020 | |
johnGreeneMaxim | 11:976c80cc99d5 | 1021 | } else { |
johnGreeneMaxim | 11:976c80cc99d5 | 1022 | // *HRbpm2 = 0 ; |
johnGreeneMaxim | 11:976c80cc99d5 | 1023 | HrCount =0; |
johnGreeneMaxim | 11:976c80cc99d5 | 1024 | HrSum =0; |
johnGreeneMaxim | 11:976c80cc99d5 | 1025 | for (k=0 ; k<10 ; k++) { |
johnGreeneMaxim | 11:976c80cc99d5 | 1026 | if (lastKnownGoodHr[k] >0) { |
johnGreeneMaxim | 11:976c80cc99d5 | 1027 | HrSum =HrSum + lastKnownGoodHr[k]; |
johnGreeneMaxim | 11:976c80cc99d5 | 1028 | HrCount++; |
johnGreeneMaxim | 11:976c80cc99d5 | 1029 | } |
johnGreeneMaxim | 11:976c80cc99d5 | 1030 | } |
johnGreeneMaxim | 11:976c80cc99d5 | 1031 | if (HrCount>0) |
johnGreeneMaxim | 11:976c80cc99d5 | 1032 | *HRbpm2 = HrSum/HrCount; |
johnGreeneMaxim | 11:976c80cc99d5 | 1033 | else |
johnGreeneMaxim | 11:976c80cc99d5 | 1034 | *HRbpm2 = 0; |
johnGreeneMaxim | 11:976c80cc99d5 | 1035 | |
johnGreeneMaxim | 11:976c80cc99d5 | 1036 | |
johnGreeneMaxim | 11:976c80cc99d5 | 1037 | |
johnGreeneMaxim | 11:976c80cc99d5 | 1038 | } |
johnGreeneMaxim | 11:976c80cc99d5 | 1039 | prevPeakLoc = peakLoc ; // save peakLoc into Static var |
johnGreeneMaxim | 11:976c80cc99d5 | 1040 | |
johnGreeneMaxim | 11:976c80cc99d5 | 1041 | |
johnGreeneMaxim | 11:976c80cc99d5 | 1042 | if (compSpO2) { |
johnGreeneMaxim | 11:976c80cc99d5 | 1043 | rnum = (ir_avg_reg >> 20)*(*red_ac_mag); |
johnGreeneMaxim | 11:976c80cc99d5 | 1044 | rden = (red_avg_reg >> 20)*(*ir_ac_mag); |
johnGreeneMaxim | 11:976c80cc99d5 | 1045 | rdens = (rden>>15); |
johnGreeneMaxim | 11:976c80cc99d5 | 1046 | if (rdens>0) cSpO2 = 110- (((25*rnum)/(rdens))>>15); |
johnGreeneMaxim | 11:976c80cc99d5 | 1047 | |
johnGreeneMaxim | 11:976c80cc99d5 | 1048 | if (cSpO2 >=100) SpO2 = 100; |
johnGreeneMaxim | 11:976c80cc99d5 | 1049 | else if (cSpO2 <= 70) SpO2 = 70; |
johnGreeneMaxim | 11:976c80cc99d5 | 1050 | else SpO2 = cSpO2; |
johnGreeneMaxim | 11:976c80cc99d5 | 1051 | |
johnGreeneMaxim | 11:976c80cc99d5 | 1052 | SPO2Que[SPO2QueCounter ] = SpO2; |
johnGreeneMaxim | 11:976c80cc99d5 | 1053 | |
johnGreeneMaxim | 11:976c80cc99d5 | 1054 | for (k=0 ; k<5 ; k++) { |
johnGreeneMaxim | 11:976c80cc99d5 | 1055 | SPO2score[k] =0; |
johnGreeneMaxim | 11:976c80cc99d5 | 1056 | for (j=0 ; j< 5 ; j++) |
johnGreeneMaxim | 11:976c80cc99d5 | 1057 | if( abs( SPO2Que[k] - SPO2Que[j] )>5) |
johnGreeneMaxim | 11:976c80cc99d5 | 1058 | SPO2score[k] ++; |
johnGreeneMaxim | 11:976c80cc99d5 | 1059 | } |
johnGreeneMaxim | 11:976c80cc99d5 | 1060 | |
johnGreeneMaxim | 11:976c80cc99d5 | 1061 | |
johnGreeneMaxim | 11:976c80cc99d5 | 1062 | SPO2scoreSum= 0; |
johnGreeneMaxim | 11:976c80cc99d5 | 1063 | for (k=0 ; k<5 ; k++) |
johnGreeneMaxim | 11:976c80cc99d5 | 1064 | SPO2scoreSum += SPO2score[k] ; |
johnGreeneMaxim | 11:976c80cc99d5 | 1065 | SPO2scoreAverage= SPO2scoreSum / 5; |
johnGreeneMaxim | 11:976c80cc99d5 | 1066 | for (k=1 ; k<5 ; k++) |
johnGreeneMaxim | 11:976c80cc99d5 | 1067 | SPO2score[k] = SPO2score[k] -SPO2scoreAverage; |
johnGreeneMaxim | 11:976c80cc99d5 | 1068 | |
johnGreeneMaxim | 11:976c80cc99d5 | 1069 | validSPO2Count =0; |
johnGreeneMaxim | 11:976c80cc99d5 | 1070 | validSPO2Sum =0; |
johnGreeneMaxim | 11:976c80cc99d5 | 1071 | for (k=1 ; k<5 ; k++) { |
johnGreeneMaxim | 11:976c80cc99d5 | 1072 | if (SPO2score[k]<=0 ) { // add for HR to report |
johnGreeneMaxim | 11:976c80cc99d5 | 1073 | validSPO2Sum +=SPO2Que[k]; |
johnGreeneMaxim | 11:976c80cc99d5 | 1074 | validSPO2Count ++; |
johnGreeneMaxim | 11:976c80cc99d5 | 1075 | } |
johnGreeneMaxim | 11:976c80cc99d5 | 1076 | } |
johnGreeneMaxim | 11:976c80cc99d5 | 1077 | if ( validSPO2Count>0) |
johnGreeneMaxim | 11:976c80cc99d5 | 1078 | SPO2D = (validSPO2Sum /validSPO2Count)-1; |
johnGreeneMaxim | 11:976c80cc99d5 | 1079 | if ( SPO2D >100) |
johnGreeneMaxim | 11:976c80cc99d5 | 1080 | SPO2D = 100; |
johnGreeneMaxim | 11:976c80cc99d5 | 1081 | |
johnGreeneMaxim | 11:976c80cc99d5 | 1082 | SPO2QueCounter ++; |
johnGreeneMaxim | 11:976c80cc99d5 | 1083 | if (SPO2QueCounter ==5) SPO2QueCounter = 0; |
johnGreeneMaxim | 11:976c80cc99d5 | 1084 | |
johnGreeneMaxim | 11:976c80cc99d5 | 1085 | for (k=1 ; k<3 ; k++) { |
johnGreeneMaxim | 11:976c80cc99d5 | 1086 | SPO2QueSmoothing[3 -k]= SPO2QueSmoothing[2-k]; |
johnGreeneMaxim | 11:976c80cc99d5 | 1087 | } |
johnGreeneMaxim | 11:976c80cc99d5 | 1088 | SPO2QueSmoothing[0] = SPO2D; |
johnGreeneMaxim | 11:976c80cc99d5 | 1089 | *SpO2B = ( (SPO2QueSmoothing[0]<<2) + (SPO2QueSmoothing[1]<<1) + (SPO2QueSmoothing[2] <<1) ) >>3; |
johnGreeneMaxim | 11:976c80cc99d5 | 1090 | |
johnGreeneMaxim | 11:976c80cc99d5 | 1091 | if (*SpO2B> 100) *SpO2B = 100 ; |
johnGreeneMaxim | 11:976c80cc99d5 | 1092 | |
johnGreeneMaxim | 11:976c80cc99d5 | 1093 | } else { |
johnGreeneMaxim | 11:976c80cc99d5 | 1094 | SpO2 = 0; |
johnGreeneMaxim | 11:976c80cc99d5 | 1095 | *SpO2B = 0; |
johnGreeneMaxim | 11:976c80cc99d5 | 1096 | } |
johnGreeneMaxim | 11:976c80cc99d5 | 1097 | *DRdy = 1; |
johnGreeneMaxim | 11:976c80cc99d5 | 1098 | |
johnGreeneMaxim | 11:976c80cc99d5 | 1099 | } |
johnGreeneMaxim | 11:976c80cc99d5 | 1100 | } |
johnGreeneMaxim | 11:976c80cc99d5 | 1101 | // Detect IR channel negative zero crossing (falling edge) |
johnGreeneMaxim | 11:976c80cc99d5 | 1102 | if ((green_ac_sig_pre > 0) && (green_ac_sig_cur <= 0)) { |
johnGreeneMaxim | 11:976c80cc99d5 | 1103 | ir_pedge = 0; |
johnGreeneMaxim | 11:976c80cc99d5 | 1104 | ir_nedge = 1; |
johnGreeneMaxim | 11:976c80cc99d5 | 1105 | ir_ac_sig_min = 0; |
johnGreeneMaxim | 11:976c80cc99d5 | 1106 | ir_nzxic = ns; |
johnGreeneMaxim | 11:976c80cc99d5 | 1107 | } |
johnGreeneMaxim | 11:976c80cc99d5 | 1108 | // Find Maximum IR & Red values in positive cycle |
johnGreeneMaxim | 11:976c80cc99d5 | 1109 | if (ir_pedge && (green_ac_sig_cur > green_ac_sig_pre)) { |
johnGreeneMaxim | 11:976c80cc99d5 | 1110 | ir_ac_sig_max = ir_ac_sig_cur; |
johnGreeneMaxim | 11:976c80cc99d5 | 1111 | red_ac_sig_max = red_ac_sig_cur; |
johnGreeneMaxim | 11:976c80cc99d5 | 1112 | green_ac_sig_max = green_ac_sig_cur; |
johnGreeneMaxim | 11:976c80cc99d5 | 1113 | } |
johnGreeneMaxim | 11:976c80cc99d5 | 1114 | |
johnGreeneMaxim | 11:976c80cc99d5 | 1115 | // Find minimum IR & Red values in negative cycle |
johnGreeneMaxim | 11:976c80cc99d5 | 1116 | if (ir_nedge && (green_ac_sig_cur < green_ac_sig_pre)) { |
johnGreeneMaxim | 11:976c80cc99d5 | 1117 | ir_ac_sig_min = ir_ac_sig_cur; |
johnGreeneMaxim | 11:976c80cc99d5 | 1118 | red_ac_sig_min = red_ac_sig_cur; |
johnGreeneMaxim | 11:976c80cc99d5 | 1119 | green_ac_sig_min = green_ac_sig_cur; |
johnGreeneMaxim | 11:976c80cc99d5 | 1120 | } |
johnGreeneMaxim | 11:976c80cc99d5 | 1121 | if (IRData <50000 ) |
johnGreeneMaxim | 11:976c80cc99d5 | 1122 | // finger-off |
johnGreeneMaxim | 11:976c80cc99d5 | 1123 | { |
johnGreeneMaxim | 11:976c80cc99d5 | 1124 | fingerOff++; |
johnGreeneMaxim | 11:976c80cc99d5 | 1125 | *DRdy = 0; |
johnGreeneMaxim | 11:976c80cc99d5 | 1126 | } else |
johnGreeneMaxim | 11:976c80cc99d5 | 1127 | fingerOff = 0 ; |
johnGreeneMaxim | 11:976c80cc99d5 | 1128 | /*if (IRData <50000 && fingerOff>10 ) |
johnGreeneMaxim | 11:976c80cc99d5 | 1129 | fingerOff = 0; */ |
johnGreeneMaxim | 11:976c80cc99d5 | 1130 | if ( *SpO2B == 0 || *HRbpm2 ==0) |
johnGreeneMaxim | 11:976c80cc99d5 | 1131 | *DRdy = 0; |
johnGreeneMaxim | 11:976c80cc99d5 | 1132 | |
johnGreeneMaxim | 11:976c80cc99d5 | 1133 | /*if (ns > 2000 ) |
johnGreeneMaxim | 11:976c80cc99d5 | 1134 | { |
johnGreeneMaxim | 11:976c80cc99d5 | 1135 | if (abs(lastDisplayedHrValue - *HRbpm2)>5) |
johnGreeneMaxim | 11:976c80cc99d5 | 1136 | *HRbpm2 =lastDisplayedHrValue ; |
johnGreeneMaxim | 11:976c80cc99d5 | 1137 | else |
johnGreeneMaxim | 11:976c80cc99d5 | 1138 | lastDisplayedHrValue = *HRbpm2; |
johnGreeneMaxim | 11:976c80cc99d5 | 1139 | }*/ |
johnGreeneMaxim | 11:976c80cc99d5 | 1140 | // *DRdy = minAmpForHeartBeat; |
johnGreeneMaxim | 11:976c80cc99d5 | 1141 | |
johnGreeneMaxim | 11:976c80cc99d5 | 1142 | } |
johnGreeneMaxim | 11:976c80cc99d5 | 1143 | |
johnGreeneMaxim | 11:976c80cc99d5 | 1144 | // Average DC Estimator |
johnGreeneMaxim | 11:976c80cc99d5 | 1145 | uint16_t avg_dc_est(int32_t *p, uint16_t x) |
johnGreeneMaxim | 11:976c80cc99d5 | 1146 | { |
johnGreeneMaxim | 11:976c80cc99d5 | 1147 | *p += ((((int32_t) x << 15) - *p)>>4); |
johnGreeneMaxim | 11:976c80cc99d5 | 1148 | return (*p >> 15); |
johnGreeneMaxim | 11:976c80cc99d5 | 1149 | } |
johnGreeneMaxim | 11:976c80cc99d5 | 1150 | |
johnGreeneMaxim | 11:976c80cc99d5 | 1151 | // Symmetric Dual Low Pass FIR Filter |
johnGreeneMaxim | 11:976c80cc99d5 | 1152 | void lp_dfir_flt(int16_t din0,int16_t din1,int16_t din2, int16_t *dout0,int16_t *dout1,int16_t *dout2) |
johnGreeneMaxim | 11:976c80cc99d5 | 1153 | { |
johnGreeneMaxim | 11:976c80cc99d5 | 1154 | static const uint16_t FIRCoeffs[12] = {688,1283,2316,3709,5439,7431, |
johnGreeneMaxim | 11:976c80cc99d5 | 1155 | 9561,11666,13563,15074,16047,16384 |
johnGreeneMaxim | 11:976c80cc99d5 | 1156 | }; |
johnGreeneMaxim | 11:976c80cc99d5 | 1157 | |
johnGreeneMaxim | 11:976c80cc99d5 | 1158 | static int16_t cbuf0[32],cbuf1[32] , cbuf2[32]; |
johnGreeneMaxim | 11:976c80cc99d5 | 1159 | static int16_t offset = 0; |
johnGreeneMaxim | 11:976c80cc99d5 | 1160 | int32_t y0,y1, y2; |
johnGreeneMaxim | 11:976c80cc99d5 | 1161 | int16_t i; |
johnGreeneMaxim | 11:976c80cc99d5 | 1162 | |
johnGreeneMaxim | 11:976c80cc99d5 | 1163 | cbuf0[offset] = din0; |
johnGreeneMaxim | 11:976c80cc99d5 | 1164 | cbuf1[offset] = din1; |
johnGreeneMaxim | 11:976c80cc99d5 | 1165 | cbuf2[offset] = din2; |
johnGreeneMaxim | 11:976c80cc99d5 | 1166 | |
johnGreeneMaxim | 11:976c80cc99d5 | 1167 | y0 = mul16(FIRCoeffs[11], cbuf0[(offset - 11)&0x1F]); |
johnGreeneMaxim | 11:976c80cc99d5 | 1168 | y1 = mul16(FIRCoeffs[11], cbuf1[(offset - 11)&0x1F]); |
johnGreeneMaxim | 11:976c80cc99d5 | 1169 | y2 = mul16(FIRCoeffs[11], cbuf2[(offset - 11)&0x1F]); |
johnGreeneMaxim | 11:976c80cc99d5 | 1170 | |
johnGreeneMaxim | 11:976c80cc99d5 | 1171 | |
johnGreeneMaxim | 11:976c80cc99d5 | 1172 | for (i=0; i<11; i++) { |
johnGreeneMaxim | 11:976c80cc99d5 | 1173 | y0 += mul16(FIRCoeffs[i], cbuf0[(offset-i)&0x1F] + cbuf0[(offset-22+i)&0x1F]); |
johnGreeneMaxim | 11:976c80cc99d5 | 1174 | y1 += mul16(FIRCoeffs[i], cbuf1[(offset-i)&0x1F] + cbuf1[(offset-22+i)&0x1F]); |
johnGreeneMaxim | 11:976c80cc99d5 | 1175 | y2 += mul16(FIRCoeffs[i], cbuf2[(offset-i)&0x1F] + cbuf2[(offset-22+i)&0x1F]); |
johnGreeneMaxim | 11:976c80cc99d5 | 1176 | } |
johnGreeneMaxim | 11:976c80cc99d5 | 1177 | offset = (offset + 1)&0x1F; |
johnGreeneMaxim | 11:976c80cc99d5 | 1178 | |
johnGreeneMaxim | 11:976c80cc99d5 | 1179 | *dout0 = (y0>>15); |
johnGreeneMaxim | 11:976c80cc99d5 | 1180 | *dout1 = (y1>>15); |
johnGreeneMaxim | 11:976c80cc99d5 | 1181 | *dout2 = (y2>>15); |
johnGreeneMaxim | 11:976c80cc99d5 | 1182 | } |
johnGreeneMaxim | 11:976c80cc99d5 | 1183 | |
johnGreeneMaxim | 11:976c80cc99d5 | 1184 | // Integer multiplier |
johnGreeneMaxim | 11:976c80cc99d5 | 1185 | int32_t mul16(int16_t x, int16_t y) |
johnGreeneMaxim | 11:976c80cc99d5 | 1186 | { |
johnGreeneMaxim | 11:976c80cc99d5 | 1187 | return (int32_t)(x* y); |
johnGreeneMaxim | 8:a1538e8a3fd9 | 1188 | } |