├── .DS_Store ├── assets ├── output.png ├── ads1292r_shield.jpg └── ads1292r_breakout.jpg ├── examples ├── .DS_Store ├── Example-3-ECG-Respiration-Arduino-Plotter │ └── Example-3-ECG-Respiration-Arduino-Plotter.ino ├── Example-2-Computation-only │ └── Example-2-Computation-only.ino └── Example-1-ECG-Respiration-Plot-Openview │ └── Example-1-ECG-Respiration-Plot-Openview.ino ├── .github └── workflows │ ├── main.yml │ └── compile-examples.yml ├── library.properties ├── src ├── ecgRespirationAlgo.h ├── protocentralAds1292r.h ├── protocentralAds1292r.cpp └── ecgRespirationAlgo.cpp ├── LICENSE.md └── README.md /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Protocentral/protocentral-ads1292r-arduino/HEAD/.DS_Store -------------------------------------------------------------------------------- /assets/output.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Protocentral/protocentral-ads1292r-arduino/HEAD/assets/output.png -------------------------------------------------------------------------------- /examples/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Protocentral/protocentral-ads1292r-arduino/HEAD/examples/.DS_Store -------------------------------------------------------------------------------- /assets/ads1292r_shield.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Protocentral/protocentral-ads1292r-arduino/HEAD/assets/ads1292r_shield.jpg -------------------------------------------------------------------------------- /assets/ads1292r_breakout.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Protocentral/protocentral-ads1292r-arduino/HEAD/assets/ads1292r_breakout.jpg -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | on: [push, pull_request] 2 | jobs: 3 | lint: 4 | runs-on: ubuntu-latest 5 | steps: 6 | - uses: actions/checkout@v2 7 | - uses: arduino/arduino-lint-action@v1 8 | with: 9 | library-manager: update 10 | -------------------------------------------------------------------------------- /library.properties: -------------------------------------------------------------------------------- 1 | name=ProtoCentral ADS1292R ECG and Respiration boards library 2 | version=1.1.0 3 | author=ProtoCentral Electronics 4 | maintainer=ProtoCentral Electronics 5 | sentence=Library for the ProtoCentral ADS1292R Shield/Breakout board 6 | paragraph= Measure ECG and respiration using the ADS1292R-based shield/breakout boards from ProtoCentral 7 | category=Sensors 8 | url=https://github.com/Protocentral/protocentral-ads1292r-arduino 9 | architectures=* 10 | -------------------------------------------------------------------------------- /src/ecgRespirationAlgo.h: -------------------------------------------------------------------------------- 1 | #ifndef ecgRespirationAlgo_h 2 | #define ecgRespirationAlgo_h 3 | 4 | #include "Arduino.h" 5 | 6 | #define TEMPERATURE 0 7 | #define FILTERORDER 161 8 | /* DC Removal Numerator Coeff*/ 9 | #define NRCOEFF (0.992) 10 | #define WAVE_SIZE 1 11 | 12 | //******* ecg filter ********* 13 | #define MAX_PEAK_TO_SEARCH 5 14 | #define MAXIMA_SEARCH_WINDOW 25 15 | #define MINIMUM_SKIP_WINDOW 30 16 | #define SAMPLING_RATE 125 17 | #define TWO_SEC_SAMPLES 2 * SAMPLING_RATE 18 | #define QRS_THRESHOLD_FRACTION 0.4 19 | #define TRUE 1 20 | #define FALSE 0 21 | 22 | class ecg_respiration_algorithm 23 | { 24 | public: 25 | void ECG_FilterProcess(int16_t * WorkingBuff, int16_t * CoeffBuf, int16_t* FilterOut); 26 | void ECG_ProcessCurrSample(int16_t *CurrAqsSample, int16_t *FilteredOut); 27 | void QRS_Algorithm_Interface(int16_t CurrSample,volatile uint8_t *Heart_rate); 28 | void Resp_FilterProcess(int16_t * RESP_WorkingBuff, int16_t * CoeffBuf, int16_t* FilterOut); 29 | int16_t Resp_ProcessCurrSample(int16_t CurrAqsSample); 30 | void RESP_Algorithm_Interface(int16_t CurrSample,volatile uint8_t *RespirationRate); 31 | 32 | private: 33 | void QRS_process_buffer(volatile uint8_t *Heart_rate); 34 | void QRS_check_sample_crossing_threshold( uint16_t scaled_result,volatile uint8_t *Heart_rate); 35 | void Respiration_Rate_Detection(int16_t Resp_wave,volatile uint8_t *RespirationRate); 36 | 37 | }; 38 | 39 | #endif 40 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | License Information 2 | =================== 3 | 4 | Hardware 5 | --------- 6 | 7 | **All hardware is released under [Creative Commons Share-alike 4.0 International](http://creativecommons.org/licenses/by-sa/4.0/).** 8 | 9 | You are free to: 10 | 11 | Share — copy and redistribute the material in any medium or format 12 | Adapt — remix, transform, and build upon the material 13 | for any purpose, even commercially. 14 | The licensor cannot revoke these freedoms as long as you follow the license terms. 15 | Under the following terms: 16 | 17 | Attribution — You must give appropriate credit, provide a link to the license, and indicate if changes were made. You may do so in any reasonable manner, but not in any way that suggests the licensor endorses you or your use. 18 | ShareAlike — If you remix, transform, or build upon the material, you must distribute your contributions under the same license as the original. 19 | No additional restrictions — You may not apply legal terms or technological measures that legally restrict others from doing anything the license permits. 20 | Notices: 21 | 22 | You do not have to comply with the license for elements of the material in the public domain or where your use is permitted by an applicable exception or limitation. 23 | No warranties are given. The license may not give you all of the permissions necessary for your intended use. For example, other rights such as publicity, privacy, or moral rights may limit how you use the material. 24 | 25 | 26 | Software 27 | -------- 28 | 29 | **All software is released under the MIT License(http://opensource.org/licenses/MIT).** 30 | 31 | The MIT License (MIT) 32 | 33 | Copyright (c) 2015 ProtoCentral 34 | 35 | Permission is hereby granted, free of charge, to any person obtaining a copy 36 | of this software and associated documentation files (the "Software"), to deal 37 | in the Software without restriction, including without limitation the rights 38 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 39 | copies of the Software, and to permit persons to whom the Software is 40 | furnished to do so, subject to the following conditions: 41 | 42 | The above copyright notice and this permission notice shall be included in all 43 | copies or substantial portions of the Software. 44 | 45 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 46 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 47 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 48 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 49 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 50 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 51 | SOFTWARE. 52 | -------------------------------------------------------------------------------- /src/protocentralAds1292r.h: -------------------------------------------------------------------------------- 1 | ////////////////////////////////////////////////////////////////////////////////////////// 2 | // 3 | // Arduino Library for ADS1292R Shield/Breakout 4 | // 5 | // Copyright (c) 2017 ProtoCentral 6 | // 7 | // This software is licensed under the MIT License(http://opensource.org/licenses/MIT). 8 | // 9 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT 10 | // NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 11 | // IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 12 | // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 13 | // SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 14 | // 15 | // Requires g4p_control graphing library for processing. Built on V4.1 16 | // Downloaded from Processing IDE Sketch->Import Library->Add Library->G4P Install 17 | // 18 | ///////////////////////////////////////////////////////////////////////////////////////// 19 | #ifndef ads1292r_h 20 | #define ads1292r_h 21 | 22 | #include "Arduino.h" 23 | 24 | #define CONFIG_SPI_MASTER_DUMMY 0xFF 25 | 26 | // Register Read Commands 27 | #define RREG 0x20; //Read n nnnn registers starting at address r rrrr 28 | //first byte 001r rrrr (2xh)(2) - second byte 000n nnnn(2) 29 | #define WREG 0x40; //Write n nnnn registers starting at address r rrrr 30 | //first byte 010r rrrr (2xh)(2) - second byte 000n nnnn(2) 31 | #define START 0x08 //Start/restart (synchronize) conversions 32 | #define STOP 0x0A //Stop conversion 33 | #define RDATAC 0x10 //Enable Read Data Continuous mode. 34 | 35 | //This mode is the default mode at power-up. 36 | #define SDATAC 0x11 //Stop Read Data Continuously mode 37 | #define RDATA 0x12 //Read data by command; supports multiple read back. 38 | 39 | //register address 40 | #define ADS1292_REG_ID 0x00 41 | #define ADS1292_REG_CONFIG1 0x01 42 | #define ADS1292_REG_CONFIG2 0x02 43 | #define ADS1292_REG_LOFF 0x03 44 | #define ADS1292_REG_CH1SET 0x04 45 | #define ADS1292_REG_CH2SET 0x05 46 | #define ADS1292_REG_RLDSENS 0x06 47 | #define ADS1292_REG_LOFFSENS 0x07 48 | #define ADS1292_REG_LOFFSTAT 0x08 49 | #define ADS1292_REG_RESP1 0x09 50 | #define ADS1292_REG_RESP2 0x0A 51 | 52 | //Packet format 53 | #define CES_CMDIF_PKT_START_1 0x0A 54 | #define CES_CMDIF_PKT_START_2 0xFA 55 | #define CES_CMDIF_TYPE_DATA 0x02 56 | #define CES_CMDIF_PKT_STOP_1 0x00 57 | #define CES_CMDIF_PKT_STOP_2 0x0B 58 | 59 | typedef struct Record{ 60 | volatile signed long sDaqVals[8]; 61 | boolean leadoffDetected = true; 62 | signed long sresultTempResp; 63 | }ads1292OutputValues; 64 | 65 | class ads1292r 66 | { 67 | public: 68 | boolean getAds1292EcgAndRespirationSamples(const int dataReady,const int chipSelect,ads1292OutputValues *ecgRespirationValues); 69 | static void ads1292Init(const int chipSelect,const int pwdnPin,const int startPin); 70 | static void ads1292Reset(const int pwdnPin); 71 | 72 | private: 73 | static void ads1292RegWrite (unsigned char READ_WRITE_ADDRESS, unsigned char DATA,const int chipSelect); 74 | static void ads1292SPICommandData(unsigned char dataIn,const int chipSelect); 75 | static void ads1292DisableStart(const int startPin); 76 | static void ads1292EnableStart(const int startPin); 77 | static void ads1292HardStop (const int startPin); 78 | static void ads1292StartDataConvCommand (const int chipSelect); 79 | static void ads1292SoftStop (const int chipSelect); 80 | static void ads1292StartReadDataContinuous (const int chipSelect); 81 | static void ads1292StopReadDataContinuous (const int chipSelect); 82 | static char* ads1292ReadData(const int chipSelect); 83 | }; 84 | 85 | #endif 86 | -------------------------------------------------------------------------------- /examples/Example-3-ECG-Respiration-Arduino-Plotter/Example-3-ECG-Respiration-Arduino-Plotter.ino: -------------------------------------------------------------------------------- 1 | ////////////////////////////////////////////////////////////////////////////////////////// 2 | // 3 | // Arduino Library for ADS1292R Shield/Breakout 4 | // 5 | // Copyright (c) 2017 ProtoCentral 6 | // Heartrate and respiration computation based on original code from Texas Instruments 7 | // 8 | // This is a simple example to plot ECG through arduino serial plotter. 9 | // 10 | // This software is licensed under the MIT License(http://opensource.org/licenses/MIT). 11 | // 12 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT 13 | // NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 14 | // IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 15 | // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 16 | // SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 17 | // 18 | // Requires g4p_control graphing library for processing. Built on V4.1 19 | // Downloaded from Processing IDE Sketch->Import Library->Add Library->G4P Install 20 | // If you have bought the breakout the connection with the Arduino board is as follows: 21 | // 22 | // |ads1292r pin label| Arduino Connection |Pin Function | 23 | // |----------------- |:--------------------:|-----------------:| 24 | // | VDD | +5V | Supply voltage | 25 | // | PWDN/RESET | D4 | Reset | 26 | // | START | D5 | Start Input | 27 | // | DRDY | D6 | Data Ready Outpt| 28 | // | CS | D7 | Chip Select | 29 | // | MOSI | D11 | Slave In | 30 | // | MISO | D12 | Slave Out | 31 | // | SCK | D13 | Serial Clock | 32 | // | GND | Gnd | Gnd | 33 | // 34 | ///////////////////////////////////////////////////////////////////////////////////////// 35 | 36 | 37 | #include "protocentralAds1292r.h" 38 | #include "ecgRespirationAlgo.h" 39 | #include 40 | 41 | volatile uint8_t globalHeartRate = 0; 42 | volatile uint8_t globalRespirationRate=0; 43 | 44 | const int ADS1292_DRDY_PIN = 6; 45 | const int ADS1292_CS_PIN = 7; 46 | const int ADS1292_START_PIN = 5; 47 | const int ADS1292_PWDN_PIN = 4; 48 | 49 | int16_t ecgWaveBuff, ecgFilterout; 50 | int16_t resWaveBuff,respFilterout; 51 | 52 | ads1292r ADS1292R; 53 | ecg_respiration_algorithm ECG_RESPIRATION_ALGORITHM; 54 | 55 | void setup() 56 | { 57 | delay(2000); 58 | 59 | SPI.begin(); 60 | SPI.beginTransaction(SPISettings(1000000, MSBFIRST, SPI_MODE1)); 61 | 62 | pinMode(ADS1292_DRDY_PIN, INPUT); 63 | pinMode(ADS1292_CS_PIN, OUTPUT); 64 | pinMode(ADS1292_START_PIN, OUTPUT); 65 | pinMode(ADS1292_PWDN_PIN, OUTPUT); 66 | 67 | Serial.begin(57600); 68 | ADS1292R.ads1292Init(ADS1292_CS_PIN,ADS1292_PWDN_PIN,ADS1292_START_PIN); 69 | Serial.println("Initiliziation is done"); 70 | } 71 | 72 | void loop() 73 | { 74 | ads1292OutputValues ecgRespirationValues; 75 | 76 | boolean ret = ADS1292R.getAds1292EcgAndRespirationSamples(ADS1292_DRDY_PIN,ADS1292_CS_PIN,&ecgRespirationValues); 77 | if (ret == true) 78 | { 79 | ecgWaveBuff = (int16_t)(ecgRespirationValues.sDaqVals[1] >> 8) ; // ignore the lower 8 bits out of 24bits 80 | resWaveBuff = (int16_t)(ecgRespirationValues.sresultTempResp>>8) ; 81 | 82 | if(ecgRespirationValues.leadoffDetected == false) 83 | { 84 | ECG_RESPIRATION_ALGORITHM.ECG_ProcessCurrSample(&ecgWaveBuff, &ecgFilterout); // filter out the line noise @40Hz cutoff 161 order 85 | ECG_RESPIRATION_ALGORITHM.QRS_Algorithm_Interface(ecgFilterout,&globalHeartRate); // calculate 86 | //respFilterout = ECG_RESPIRATION_ALGORITHM.Resp_ProcessCurrSample(resWaveBuff); 87 | //ECG_RESPIRATION_ALGORITHM.RESP_Algorithm_Interface(respFilterout,&globalRespirationRate); 88 | 89 | }else{ 90 | ecgFilterout = 0; 91 | respFilterout = 0; 92 | } 93 | 94 | Serial.println(ecgFilterout); 95 | //Serial.println(resWaveBuff); 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Protocentral ADS1292R ECG/respiration Arduino Library 2 | 3 | [![Compile Examples](https://github.com/Protocentral/protocentral-ads1292r-arduino/workflows/Compile%20Examples/badge.svg)](https://github.com/Protocentral/protocentral-ads1292r-arduino/actions?workflow=Compile+Examples) 4 | 5 | ## Don't have one? [Buy shield here](https://protocentral.com/product/ads1292r-ecg-respiration-shield-for-arduino-v2/) 6 | 7 |
8 | 9 | ![ADS1292R breakout](./assets/ads1292r_shield.jpg) 10 | 11 |
12 | 13 | 14 | ## Don't have one? [Buy breakout here](https://protocentral.com/product/ads1292r-ecg-respiration-breakout-kit/) 15 | 16 |
17 | 18 | ![ADS1292R shield](./assets/ads1292r_breakout.jpg) 19 | 20 |
21 | 22 | Easily monitor ECG and respiration using your Arduino with this plug-in shield. The version 2 of this product adds a new SPI pin header making it compatible with newer Arduino devices including the Arduino Yun and 3.5mm connector for the electrodes. We now include the electrodes and cable also with the shield 23 | 24 | Just plug it into an Arduino and you're ready to go. The 3.5 mm circular connector provides an easy way to connect electodes to the shield. The other end of this cable has snaps for standard ECG electrodes. We also include a pakc of 10 disposable EG electrodes. It accepts two ECG electrodes and one Driven Right Leg (DRL) electrode for common mode noise reduction. 25 | 26 | Another interesting feature of this shield is that you can also measure the respiratory activity using the same two electrodes connected to the shield. The ADS1292R uses a method known as impedance pneumography to measure respiration using the changes in chest impedance caused during respiration. 27 | 28 | 29 | ## Hardware Setup 30 | 31 | Connection with the Arduino board is as follows: 32 | 33 | 34 | |ADS1292R pin label| Arduino Connection |Pin Function | 35 | |----------------- |:--------------------:|-----------------:| 36 | | MISO | D12 | Slave out | 37 | | MOSI | D11 | Slave in | 38 | | SCK | D13 | Serial clock | 39 | | CS | D7 | chip select | 40 | | DRDY | D6 | Data Ready Output| 41 | | START | D5 | Start Input | 42 | | PWDN/RESET | D4 | Reset | 43 | | VDD | +5V | Power Supply | 44 | | GND | GND | GND | 45 | 46 | 47 | # Visualizing Output 48 | 49 | ![Streaming output](./assets/output.png) 50 | 51 | ## For further details, refer [the documentation on ADS1292R breakout board](https://docs.protocentral.com/getting-started-with-ADS1292R/) 52 | 53 | 54 | License Information 55 | =================== 56 | 57 | This product is open source! Both, our hardware and software are open source and licensed under the following licenses: 58 | 59 | Hardware 60 | --------- 61 | 62 | **All hardware is released under [Creative Commons Share-alike 4.0 International](http://creativecommons.org/licenses/by-sa/4.0/).** 63 | ![CC-BY-SA-4.0](https://i.creativecommons.org/l/by-sa/4.0/88x31.png) 64 | 65 | You are free to: 66 | 67 | * Share — copy and redistribute the material in any medium or format 68 | * Adapt — remix, transform, and build upon the material for any purpose, even commercially. 69 | The licensor cannot revoke these freedoms as long as you follow the license terms. 70 | 71 | Under the following terms: 72 | 73 | * Attribution — You must give appropriate credit, provide a link to the license, and indicate if changes were made. You may do so in any reasonable manner, but not in any way that suggests the licensor endorses you or your use. 74 | * ShareAlike — If you remix, transform, or build upon the material, you must distribute your contributions under the same license as the original. 75 | 76 | Software 77 | -------- 78 | 79 | **All software is released under the MIT License(http://opensource.org/licenses/MIT).** 80 | 81 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 82 | 83 | 84 | Please check [*LICENSE.md*](LICENSE.md) for detailed license descriptions. 85 | -------------------------------------------------------------------------------- /examples/Example-2-Computation-only/Example-2-Computation-only.ino: -------------------------------------------------------------------------------- 1 | ////////////////////////////////////////////////////////////////////////////////////////// 2 | // 3 | // Arduino Library for ADS1292R Shield/Breakout 4 | // 5 | // Copyright (c) 2017 ProtoCentral 6 | // Heartrate and respiration computation based on original code from Texas Instruments 7 | // Note: This example is not compatible with arduino uno as the algorithm requires higher RAM size for its computations. 8 | // 9 | // This software is licensed under the MIT License(http://opensource.org/licenses/MIT). 10 | // 11 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT 12 | // NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 13 | // IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 14 | // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 15 | // SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 16 | // 17 | // Requires g4p_control graphing library for processing. Built on V4.1 18 | // Downloaded from Processing IDE Sketch->Import Library->Add Library->G4P Install 19 | // If you have bought the breakout the connection with the Arduino board is as follows: 20 | // 21 | // |ads1292r pin label| Arduino Connection |Pin Function | 22 | // |----------------- |:--------------------:|-----------------:| 23 | // | VDD | +5V | Supply voltage | 24 | // | PWDN/RESET | D4 | Reset | 25 | // | START | D5 | Start Input | 26 | // | DRDY | D6 | Data Ready Outpt| 27 | // | CS | D7 | Chip Select | 28 | // | MOSI | D11 | Slave In | 29 | // | MISO | D12 | Slave Out | 30 | // | SCK | D13 | Serial Clock | 31 | // | GND | Gnd | Gnd | 32 | // 33 | // *Note: Computation will not support with Arduino uno and nano 34 | ///////////////////////////////////////////////////////////////////////////////////////// 35 | 36 | #include "protocentralAds1292r.h" 37 | #include "ecgRespirationAlgo.h" 38 | #include 39 | 40 | volatile uint8_t globalHeartRate; 41 | volatile uint8_t globalRespirationRate=0; 42 | 43 | const int ADS1292_DRDY_PIN = 26; 44 | const int ADS1292_CS_PIN = 13; 45 | const int ADS1292_START_PIN = 14; 46 | const int ADS1292_PWDN_PIN = 27; 47 | 48 | int16_t ecgWaveBuff, ecgFilterout; 49 | int16_t resWaveBuff,respFilterout; 50 | 51 | long timeElapsed=0; 52 | 53 | ads1292r ADS1292R; 54 | ecg_respiration_algorithm ECG_RESPIRATION_ALGORITHM; 55 | 56 | void setup() 57 | { 58 | delay(2000); 59 | 60 | SPI.begin(); 61 | SPI.beginTransaction(SPISettings(1000000, MSBFIRST, SPI_MODE1)); 62 | 63 | pinMode(ADS1292_DRDY_PIN, INPUT); 64 | pinMode(ADS1292_CS_PIN, OUTPUT); 65 | pinMode(ADS1292_START_PIN, OUTPUT); 66 | pinMode(ADS1292_PWDN_PIN, OUTPUT); 67 | Serial.begin(57600); 68 | 69 | ADS1292R.ads1292Init(ADS1292_CS_PIN,ADS1292_PWDN_PIN,ADS1292_START_PIN); //initalize ADS1292 slave 70 | Serial.println("Initiliziation is done"); 71 | } 72 | 73 | void loop() 74 | { 75 | ads1292OutputValues ecgRespirationValues; 76 | boolean ret = ADS1292R.getAds1292EcgAndRespirationSamples(ADS1292_DRDY_PIN,ADS1292_CS_PIN,&ecgRespirationValues); 77 | 78 | if (ret == true) 79 | { 80 | ecgWaveBuff = (int16_t)(ecgRespirationValues.sDaqVals[1] >> 8) ; // ignore the lower 8 bits out of 24bits 81 | resWaveBuff = (int16_t)(ecgRespirationValues.sresultTempResp>>8) ; 82 | 83 | if(ecgRespirationValues.leadoffDetected == false) 84 | { 85 | ECG_RESPIRATION_ALGORITHM.ECG_ProcessCurrSample(&ecgWaveBuff, &ecgFilterout); // filter out the line noise @40Hz cutoff 161 order 86 | ECG_RESPIRATION_ALGORITHM.QRS_Algorithm_Interface(ecgFilterout,&globalHeartRate);// calculate 87 | 88 | //disable below 2 lines if you want to run with arduino uno. (arduino uno does not have the memory to do all processing together) 89 | respFilterout = ECG_RESPIRATION_ALGORITHM.Resp_ProcessCurrSample(resWaveBuff); 90 | ECG_RESPIRATION_ALGORITHM.RESP_Algorithm_Interface(respFilterout,&globalRespirationRate); 91 | 92 | }else{ 93 | 94 | ecgFilterout = 0; 95 | respFilterout = 0; 96 | } 97 | 98 | if(millis() > timeElapsed) // update every one second 99 | { 100 | if(ecgRespirationValues.leadoffDetected == true) // lead in not connected 101 | { 102 | Serial.println(F("ECG lead error!!! ensure the leads are properly connected")); 103 | }else{ 104 | 105 | Serial.print(F("Heart rate: ")); 106 | Serial.print(globalHeartRate); 107 | Serial.println(F("BPM")); 108 | Serial.print(F("Respiration Rate :")); 109 | Serial.println(globalRespirationRate); 110 | } 111 | timeElapsed += 1000; 112 | } 113 | } 114 | } -------------------------------------------------------------------------------- /examples/Example-1-ECG-Respiration-Plot-Openview/Example-1-ECG-Respiration-Plot-Openview.ino: -------------------------------------------------------------------------------- 1 | ////////////////////////////////////////////////////////////////////////////////////////// 2 | // 3 | // Arduino Library for ADS1292R Shield/Breakout 4 | // 5 | // Copyright (c) 2017 ProtoCentral 6 | // Heartrate and respiration computation based on original code from Texas Instruments 7 | // 8 | // This example plots ECG and respiartion wave through protocentral_openview processing GUI. 9 | // GUI URL: https://github.com/Protocentral/protocentral_openview.git 10 | // 11 | // This software is licensed under the MIT License(http://opensource.org/licenses/MIT). 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT 14 | // NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 15 | // IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 16 | // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 17 | // SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 18 | // 19 | // Requires g4p_control graphing library for processing. Built on V4.1 20 | // Downloaded from Processing IDE Sketch->Import Library->Add Library->G4P Install 21 | // If you have bought the breakout the connection with the Arduino board is as follows: 22 | // 23 | // |ads1292r pin label| Arduino Connection |Pin Function | 24 | // |----------------- |:--------------------:|-----------------:| 25 | // | VDD | +5V | Supply voltage | 26 | // | PWDN/RESET | D4 | Reset | 27 | // | START | D5 | Start Input | 28 | // | DRDY | D6 | Data Ready Outpt| 29 | // | CS | D7 | Chip Select | 30 | // | MOSI | D11 | Slave In | 31 | // | MISO | D12 | Slave Out | 32 | // | SCK | D13 | Serial Clock | 33 | // | GND | Gnd | Gnd | 34 | // 35 | ///////////////////////////////////////////////////////////////////////////////////////// 36 | 37 | 38 | #include "protocentralAds1292r.h" 39 | #include "ecgRespirationAlgo.h" 40 | #include 41 | 42 | volatile uint8_t globalHeartRate = 0; 43 | volatile uint8_t globalRespirationRate=0; 44 | 45 | //Pin declartion the other you need are controlled by the SPI library 46 | const int ADS1292_DRDY_PIN = 6; 47 | const int ADS1292_CS_PIN = 7; 48 | const int ADS1292_START_PIN = 5; 49 | const int ADS1292_PWDN_PIN = 4; 50 | 51 | #define CES_CMDIF_PKT_START_1 0x0A 52 | #define CES_CMDIF_PKT_START_2 0xFA 53 | #define CES_CMDIF_TYPE_DATA 0x02 54 | #define CES_CMDIF_PKT_STOP 0x0B 55 | #define DATA_LEN 9 56 | #define ZERO 0 57 | 58 | volatile char DataPacket[DATA_LEN]; 59 | const char DataPacketFooter[2] = {ZERO, CES_CMDIF_PKT_STOP}; 60 | const char DataPacketHeader[5] = {CES_CMDIF_PKT_START_1, CES_CMDIF_PKT_START_2, DATA_LEN, ZERO, CES_CMDIF_TYPE_DATA}; 61 | 62 | int16_t ecgWaveBuff, ecgFilterout; 63 | int16_t resWaveBuff,respFilterout; 64 | 65 | ads1292r ADS1292R; 66 | ecg_respiration_algorithm ECG_RESPIRATION_ALGORITHM; 67 | 68 | void sendDataThroughUART(void){ 69 | 70 | DataPacket[0] = ecgFilterout; 71 | DataPacket[1] = ecgFilterout >> 8; 72 | DataPacket[2] = resWaveBuff; 73 | DataPacket[3] = resWaveBuff >> 8; 74 | 75 | DataPacket[4] = globalRespirationRate; 76 | DataPacket[5] = globalRespirationRate >> 8; 77 | DataPacket[6] = globalHeartRate; 78 | DataPacket[7] = globalHeartRate >> 8; 79 | DataPacket[8] = 0; 80 | 81 | //send packet header 82 | for(int i=0; i<5; i++){ 83 | 84 | Serial.write(DataPacketHeader[i]); 85 | } 86 | 87 | //send 30003 data 88 | for(int i=0; i> 8) ; // ignore the lower 8 bits out of 24bits 125 | resWaveBuff = (int16_t)(ecgRespirationValues.sresultTempResp>>8) ; 126 | 127 | if(ecgRespirationValues.leadoffDetected == false) 128 | { 129 | ECG_RESPIRATION_ALGORITHM.ECG_ProcessCurrSample(&ecgWaveBuff, &ecgFilterout); // filter out the line noise @40Hz cutoff 161 order 130 | ECG_RESPIRATION_ALGORITHM.QRS_Algorithm_Interface(ecgFilterout,&globalHeartRate); // calculate 131 | //respFilterout = ECG_RESPIRATION_ALGORITHM.Resp_ProcessCurrSample(resWaveBuff); 132 | //ECG_RESPIRATION_ALGORITHM.RESP_Algorithm_Interface(respFilterout,&globalRespirationRate); 133 | 134 | }else{ 135 | ecgFilterout = 0; 136 | respFilterout = 0; 137 | } 138 | 139 | sendDataThroughUART(); 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /.github/workflows/compile-examples.yml: -------------------------------------------------------------------------------- 1 | name: Compile Examples 2 | 3 | # See: https://docs.github.com/en/free-pro-team@latest/actions/reference/events-that-trigger-workflows 4 | on: 5 | push: 6 | paths: 7 | - ".github/workflows/compile-examples.yml" 8 | - "examples/**" 9 | - "src/**" 10 | pull_request: 11 | paths: 12 | - ".github/workflows/compile-examples.yml" 13 | - "examples/**" 14 | - "src/**" 15 | 16 | 17 | jobs: 18 | build: 19 | name: ${{ matrix.board.fqbn }} 20 | runs-on: ubuntu-latest 21 | 22 | env: 23 | SKETCHES_REPORTS_PATH: sketches-reports 24 | 25 | strategy: 26 | fail-fast: false 27 | 28 | matrix: 29 | board: 30 | # Arduino 31 | - fqbn: arduino:avr:mega 32 | platforms: | 33 | - name: arduino:avr 34 | - fqbn: arduino:avr:leonardo 35 | platforms: | 36 | - name: arduino:avr 37 | - fqbn: arduino:megaavr:nona4809 38 | platforms: | 39 | - name: arduino:megaavr 40 | - fqbn: arduino:sam:arduino_due_x_dbg 41 | platforms: | 42 | - name: arduino:sam 43 | - fqbn: arduino:samd:mkrzero 44 | platforms: | 45 | - name: arduino:samd 46 | - fqbn: arduino:samd:mkrwifi1010 47 | platforms: | 48 | - name: arduino:samd 49 | - fqbn: arduino:samd:mkrfox1200 50 | platforms: | 51 | - name: arduino:samd 52 | - fqbn: arduino:samd:mkrwan1300 53 | platforms: | 54 | - name: arduino:samd 55 | - fqbn: arduino:samd:mkrwan1310 56 | platforms: | 57 | - name: arduino:samd 58 | - fqbn: arduino:samd:mkrgsm1400 59 | platforms: | 60 | - name: arduino:samd 61 | - fqbn: arduino:samd:mkrnb1500 62 | platforms: | 63 | - name: arduino:samd 64 | - fqbn: arduino:samd:mkrvidor4000 65 | platforms: | 66 | - name: arduino:samd 67 | - fqbn: arduino:mbed_portenta:envie_m7:target_core=cm4 68 | platforms: | 69 | - name: arduino:mbed_portenta 70 | - fqbn: arduino:mbed_portenta:envie_m7 71 | platforms: | 72 | - name: arduino:mbed_portenta 73 | - fqbn: arduino:mbed_nano:nanorp2040connect 74 | platforms: | 75 | - name: arduino:mbed_nano 76 | - fqbn: arduino:mbed_edge:edge_control 77 | platforms: | 78 | - name: arduino:mbed_edge 79 | 80 | # Artemis / Apollo3 81 | # https://github.com/sparkfun/Arduino_Apollo3/blob/main/boards.txt 82 | - fqbn: SparkFun:apollo3:sfe_artemis_atp 83 | platforms: | 84 | - name: SparkFun:apollo3 85 | source-url: https://raw.githubusercontent.com/sparkfun/Arduino_Apollo3/master/package_sparkfun_apollo3_index.json 86 | 87 | # ESP32 88 | # https://github.com/espressif/arduino-esp32/blob/master/boards.txt 89 | - fqbn: esp32:esp32:esp32 90 | platforms: | 91 | - name: esp32:esp32 92 | source-url: https://raw.githubusercontent.com/espressif/arduino-esp32/gh-pages/package_esp32_index.json 93 | 94 | # ESP32-S2 95 | # https://github.com/espressif/arduino-esp32/blob/master/boards.txt 96 | - fqbn: esp32:esp32:esp32s2 97 | platforms: | 98 | - name: esp32:esp32 99 | source-url: https://raw.githubusercontent.com/espressif/arduino-esp32/gh-pages/package_esp32_index.json 100 | 101 | # ESP32-C3 102 | # https://github.com/espressif/arduino-esp32/blob/master/boards.txt 103 | - fqbn: esp32:esp32:esp32c3 104 | platforms: | 105 | - name: esp32:esp32 106 | source-url: https://raw.githubusercontent.com/espressif/arduino-esp32/gh-pages/package_esp32_index.json 107 | 108 | # ESP8266 109 | # https://github.com/esp8266/Arduino/blob/master/boards.txt 110 | - fqbn: esp8266:esp8266:thingdev 111 | platforms: | 112 | - name: esp8266:esp8266 113 | source-url: https://arduino.esp8266.com/stable/package_esp8266com_index.json 114 | 115 | # Nano BLE 33 / nRF52840 116 | # https://github.com/arduino/ArduinoCore-mbed/blob/master/boards.txt 117 | - fqbn: arduino:mbed:nano33ble 118 | platforms: | 119 | - name: arduino:mbed 120 | # source-url: https://downloads.arduino.cc/packages/package_index.json 121 | - fqbn: arduino:mbed_nano:nano33ble 122 | platforms: | 123 | - name: arduino:mbed_nano 124 | 125 | # RP2040 126 | # https://github.com/arduino/ArduinoCore-mbed/blob/master/boards.txt 127 | - fqbn: rp2040:rp2040:sparkfun_promicrorp2040 128 | platforms: | 129 | - name: rp2040:rp2040 130 | source-url: https://github.com/earlephilhower/arduino-pico/releases/download/global/package_rp2040_index.json 131 | - fqbn: rp2040:rp2040:rpipico 132 | platforms: | 133 | - name: rp2040:rp2040 134 | source-url: https://github.com/earlephilhower/arduino-pico/releases/download/global/package_rp2040_index.json 135 | 136 | # SAMD21 137 | # https://github.com/arduino/ArduinoCore-samd/blob/master/boards.txt 138 | - fqbn: arduino:samd:mkr1000 139 | platforms: | 140 | - name: arduino:samd 141 | # source-url: https://downloads.arduino.cc/packages/package_index.json 142 | 143 | # STM32 144 | # https://github.com/arduino/ArduinoCore-mbed/blob/master/boards.txt 145 | - fqbn: STMicroelectronics:stm32:GenF4 146 | platforms: | 147 | - name: STMicroelectronics:stm32 148 | source-url: https://github.com/stm32duino/BoardManagerFiles/raw/main/package_stmicroelectronics_index.json 149 | 150 | 151 | steps: 152 | - name: Checkout repository 153 | uses: actions/checkout@v3 154 | 155 | - name: Install ESP32 platform dependencies 156 | if: startsWith(matrix.board.fqbn, 'esp32:esp32') 157 | run: pip3 install pyserial 158 | 159 | - name: Compile examples 160 | uses: arduino/compile-sketches@v1 161 | with: 162 | github-token: ${{ secrets.GITHUB_TOKEN }} 163 | fqbn: ${{ matrix.board.fqbn }} 164 | platforms: ${{ matrix.board.platforms }} 165 | libraries: | 166 | # Install the library from the local path. 167 | - source-path: ./ 168 | # Additional library dependencies can be listed here. 169 | # See: https://github.com/arduino/compile-sketches#libraries 170 | sketch-paths: | 171 | - examples 172 | enable-deltas-report: true 173 | sketches-report-path: ${{ env.SKETCHES_REPORTS_PATH }} 174 | 175 | - name: Save sketches report as workflow artifact 176 | uses: actions/upload-artifact@v3 177 | with: 178 | if-no-files-found: error 179 | path: ${{ env.SKETCHES_REPORTS_PATH }} 180 | name: ${{ env.SKETCHES_REPORTS_PATH }} 181 | -------------------------------------------------------------------------------- /src/protocentralAds1292r.cpp: -------------------------------------------------------------------------------- 1 | ////////////////////////////////////////////////////////////////////////////////////////// 2 | // 3 | // Arduino Library for ADS1292R Shield/Breakout 4 | // 5 | // Copyright (c) 2017 ProtoCentral 6 | // 7 | // This software is licensed under the MIT License(http://opensource.org/licenses/MIT). 8 | // 9 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT 10 | // NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 11 | // IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 12 | // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 13 | // SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 14 | // 15 | // Requires g4p_control graphing library for processing. Built on V4.1 16 | // Downloaded from Processing IDE Sketch->Import Library->Add Library->G4P Install 17 | // 18 | ///////////////////////////////////////////////////////////////////////////////////////// 19 | #include "Arduino.h" 20 | #include "protocentralAds1292r.h" 21 | #include 22 | 23 | int j,i; 24 | 25 | volatile byte SPI_RX_Buff[15] ; 26 | volatile static int SPI_RX_Buff_Count = 0; 27 | volatile char *SPI_RX_Buff_Ptr; 28 | volatile bool ads1292dataReceived = false; 29 | 30 | unsigned long uecgtemp = 0; 31 | unsigned long resultTemp = 0; 32 | 33 | signed long secgtemp = 0; 34 | 35 | long statusByte=0; 36 | 37 | uint8_t LeadStatus=0; 38 | 39 | boolean ads1292r::getAds1292EcgAndRespirationSamples(const int dataReady,const int chipSelect,ads1292OutputValues *ecgRespirationValues) 40 | { 41 | 42 | if ((digitalRead(dataReady)) == LOW) // Sampling rate is set to 125SPS ,DRDY ticks for every 8ms 43 | { 44 | SPI_RX_Buff_Ptr = ads1292ReadData(chipSelect); // Read the data,point the data to a pointer 45 | 46 | for (int i = 0; i < 9; i++) 47 | { 48 | SPI_RX_Buff[SPI_RX_Buff_Count++] = *(SPI_RX_Buff_Ptr + i); // store the result data in array 49 | } 50 | 51 | ads1292dataReceived = true; 52 | //Serial.println(ads1292dataReceived); 53 | //delay(1000); 54 | j = 0; 55 | 56 | for (i = 3; i < 9; i += 3) // data outputs is (24 status bits + 24 bits Respiration data + 24 bits ECG data) 57 | { 58 | uecgtemp = (unsigned long) ( ((unsigned long)SPI_RX_Buff[i + 0] << 16) | ( (unsigned long) SPI_RX_Buff[i + 1] << 8) | (unsigned long) SPI_RX_Buff[i + 2]); 59 | uecgtemp = (unsigned long) (uecgtemp << 8); 60 | secgtemp = (signed long) (uecgtemp); 61 | secgtemp = (signed long) (secgtemp >> 8); 62 | 63 | (ecgRespirationValues->sDaqVals)[j++] = secgtemp; //s32DaqVals[0] is Resp data and s32DaqVals[1] is ECG data 64 | } 65 | 66 | statusByte = (long)((long)SPI_RX_Buff[2] | ((long) SPI_RX_Buff[1]) <<8 | ((long) SPI_RX_Buff[0])<<16); // First 3 bytes represents the status 67 | statusByte = (statusByte & 0x0f8000) >> 15; // bit15 gives the lead status 68 | LeadStatus = (unsigned char ) statusByte ; 69 | resultTemp = (uint32_t)((0 << 24) | (SPI_RX_Buff[3] << 16)| SPI_RX_Buff[4] << 8 | SPI_RX_Buff[5]);//6,7,8 70 | resultTemp = (uint32_t)(resultTemp << 8); 71 | ecgRespirationValues->sresultTempResp = (long)(resultTemp); 72 | 73 | 74 | if(!((LeadStatus & 0x1f) == 0 )) 75 | { 76 | ecgRespirationValues->leadoffDetected = true; 77 | } 78 | 79 | else 80 | { 81 | ecgRespirationValues->leadoffDetected = false; 82 | } 83 | 84 | ads1292dataReceived = false; 85 | SPI_RX_Buff_Count = 0; 86 | return true; 87 | } 88 | 89 | else 90 | return false; 91 | } 92 | 93 | char* ads1292r::ads1292ReadData(const int chipSelect) 94 | { 95 | static char SPI_Dummy_Buff[10]; 96 | digitalWrite(chipSelect, LOW); 97 | 98 | for (int i = 0; i < 9; ++i) 99 | { 100 | SPI_Dummy_Buff[i] = SPI.transfer(CONFIG_SPI_MASTER_DUMMY); 101 | } 102 | 103 | digitalWrite(chipSelect, HIGH); 104 | return SPI_Dummy_Buff; 105 | } 106 | 107 | void ads1292r::ads1292Init(const int chipSelect,const int pwdnPin,const int startPin) 108 | { 109 | // start the SPI library: 110 | ads1292Reset(pwdnPin); 111 | delay(100); 112 | ads1292DisableStart(startPin); 113 | ads1292EnableStart(startPin); 114 | ads1292HardStop(startPin); 115 | ads1292StartDataConvCommand(chipSelect); 116 | ads1292SoftStop(chipSelect); 117 | delay(50); 118 | ads1292StopReadDataContinuous(chipSelect); // SDATAC command 119 | delay(300); 120 | ads1292RegWrite(ADS1292_REG_CONFIG1, 0x00,chipSelect); //Set sampling rate to 125 SPS 121 | delay(10); 122 | ads1292RegWrite(ADS1292_REG_CONFIG2, 0b10100000,chipSelect); //Lead-off comp off, test signal disabled 123 | delay(10); 124 | ads1292RegWrite(ADS1292_REG_LOFF, 0b00010000,chipSelect); //Lead-off defaults 125 | delay(10); 126 | ads1292RegWrite(ADS1292_REG_CH1SET, 0b01000000,chipSelect); //Ch 1 enabled, gain 6, connected to electrode in 127 | delay(10); 128 | ads1292RegWrite(ADS1292_REG_CH2SET, 0b01100000,chipSelect); //Ch 2 enabled, gain 6, connected to electrode in 129 | delay(10); 130 | ads1292RegWrite(ADS1292_REG_RLDSENS, 0b00101100,chipSelect); //RLD settings: fmod/16, RLD enabled, RLD inputs from Ch2 only 131 | delay(10); 132 | ads1292RegWrite(ADS1292_REG_LOFFSENS, 0x00,chipSelect); //LOFF settings: all disabled 133 | delay(10); //Skip register 8, LOFF Settings default 134 | ads1292RegWrite(ADS1292_REG_RESP1, 0b11110010,chipSelect); //Respiration: MOD/DEMOD turned only, phase 0 135 | delay(10); 136 | ads1292RegWrite(ADS1292_REG_RESP2, 0b00000011,chipSelect); //Respiration: Calib OFF, respiration freq defaults 137 | delay(10); 138 | ads1292StartReadDataContinuous(chipSelect); 139 | delay(10); 140 | ads1292EnableStart(startPin); 141 | } 142 | 143 | void ads1292r::ads1292Reset(const int pwdnPin) 144 | { 145 | digitalWrite(pwdnPin, HIGH); 146 | delay(100); // Wait 100 mSec 147 | digitalWrite(pwdnPin, LOW); 148 | delay(100); 149 | digitalWrite(pwdnPin, HIGH); 150 | delay(100); 151 | } 152 | 153 | void ads1292r::ads1292DisableStart(const int startPin) 154 | { 155 | digitalWrite(startPin, LOW); 156 | delay(20); 157 | } 158 | 159 | void ads1292r::ads1292EnableStart(const int startPin) 160 | { 161 | digitalWrite(startPin, HIGH); 162 | delay(20); 163 | } 164 | 165 | void ads1292r::ads1292HardStop (const int startPin) 166 | { 167 | digitalWrite(startPin, LOW); 168 | delay(100); 169 | } 170 | 171 | void ads1292r::ads1292StartDataConvCommand (const int chipSelect) 172 | { 173 | ads1292SPICommandData(START,chipSelect); // Send 0x08 to the ADS1x9x 174 | } 175 | 176 | void ads1292r::ads1292SoftStop (const int chipSelect) 177 | { 178 | ads1292SPICommandData(STOP,chipSelect); // Send 0x0A to the ADS1x9x 179 | } 180 | 181 | void ads1292r::ads1292StartReadDataContinuous (const int chipSelect) 182 | { 183 | ads1292SPICommandData(RDATAC,chipSelect); // Send 0x10 to the ADS1x9x 184 | } 185 | 186 | void ads1292r::ads1292StopReadDataContinuous (const int chipSelect) 187 | { 188 | ads1292SPICommandData(SDATAC,chipSelect); // Send 0x11 to the ADS1x9x 189 | } 190 | 191 | void ads1292r::ads1292SPICommandData(unsigned char dataIn,const int chipSelect) 192 | { 193 | //byte data[1]; 194 | //data[0] = dataIn; 195 | digitalWrite(chipSelect, LOW); 196 | delay(2); 197 | digitalWrite(chipSelect, HIGH); 198 | delay(2); 199 | digitalWrite(chipSelect, LOW); 200 | delay(2); 201 | SPI.transfer(dataIn); 202 | delay(2); 203 | digitalWrite(chipSelect, HIGH); 204 | } 205 | 206 | //Sends a write command to SCP1000 207 | void ads1292r::ads1292RegWrite (unsigned char READ_WRITE_ADDRESS, unsigned char DATA,const int chipSelect) 208 | { 209 | 210 | switch (READ_WRITE_ADDRESS) 211 | { 212 | case 1: 213 | DATA = DATA & 0x87; 214 | break; 215 | case 2: 216 | DATA = DATA & 0xFB; 217 | DATA |= 0x80; 218 | break; 219 | case 3: 220 | DATA = DATA & 0xFD; 221 | DATA |= 0x10; 222 | break; 223 | case 7: 224 | DATA = DATA & 0x3F; 225 | break; 226 | case 8: 227 | DATA = DATA & 0x5F; 228 | break; 229 | case 9: 230 | DATA |= 0x02; 231 | break; 232 | case 10: 233 | DATA = DATA & 0x87; 234 | DATA |= 0x01; 235 | break; 236 | case 11: 237 | DATA = DATA & 0x0F; 238 | break; 239 | default: 240 | break; 241 | } 242 | // now combine the register address and the command into one byte: 243 | byte dataToSend = READ_WRITE_ADDRESS | WREG; 244 | digitalWrite(chipSelect, LOW); 245 | delay(2); 246 | digitalWrite(chipSelect, HIGH); 247 | delay(2); 248 | // take the chip select low to select the device: 249 | digitalWrite(chipSelect, LOW); 250 | delay(2); 251 | SPI.transfer(dataToSend); //Send register location 252 | SPI.transfer(0x00); //number of register to wr 253 | SPI.transfer(DATA); //Send value to record into register 254 | delay(2); 255 | // take the chip select high to de-select: 256 | digitalWrite(chipSelect, HIGH); 257 | } 258 | -------------------------------------------------------------------------------- /src/ecgRespirationAlgo.cpp: -------------------------------------------------------------------------------- 1 | #include "Arduino.h" 2 | #include "protocentralAds1292r.h" 3 | #include "ecgRespirationAlgo.h" 4 | #include 5 | 6 | unsigned char Start_Sample_Count_Flag = 0; 7 | unsigned char first_peak_detect = FALSE ; 8 | 9 | unsigned int sample_count = 0 ; /* Variable which will hold the calculated heart rate */ 10 | unsigned int sample_index[MAX_PEAK_TO_SEARCH + 2] ; 11 | 12 | int QRS_Second_Prev_Sample = 0 ; 13 | int QRS_Prev_Sample = 0 ; 14 | int QRS_Current_Sample = 0 ; 15 | int QRS_Next_Sample = 0 ; 16 | int QRS_Second_Next_Sample = 0 ; 17 | int RESP_Second_Prev_Sample = 0 ; 18 | int RESP_Prev_Sample = 0 ; 19 | int RESP_Current_Sample = 0 ; 20 | int RESP_Next_Sample = 0 ; 21 | int RESP_Second_Next_Sample = 0 ; 22 | 23 | uint8_t Respiration_Rate = 0 ; 24 | 25 | volatile uint16_t QRS_Heart_Rate = 0 ; 26 | static uint16_t QRS_B4_Buffer_ptr = 0 ; /* Variable which holds the threshold value to calculate the maxima */ 27 | int16_t RESP_WorkingBuff[2 * FILTERORDER]; 28 | int16_t Pvev_DC_Sample=0, Pvev_Sample=0; 29 | int16_t QRS_Threshold_Old = 0; 30 | int16_t QRS_Threshold_New = 0; 31 | int16_t ECG_WorkingBuff[2 * FILTERORDER]; 32 | int16_t CoeffBuf_40Hz_LowPass[FILTERORDER] = {-72, 122, -31, -99, 117, 0, -121, 105, 34, 33 | -137, 84, 70, -146, 55, 104, -147, 20, 135, 34 | -137, -21, 160, -117, -64, 177, -87, -108, 185, 35 | -48, -151, 181, 0, -188, 164, 54, -218, 134, 36 | 112, -238, 90, 171, -244, 33, 229, -235, -36, 37 | 280, -208, -115, 322, -161, -203, 350, -92, -296, 38 | 361, 0, -391, 348, 117, -486, 305, 264, -577, 39 | 225, 445, -660, 93, 676, -733, -119, 991, -793, 40 | -480, 1486, -837, -1226, 2561, -865, -4018, 9438, 20972, 41 | 9438, -4018, -865, 2561, -1226, -837, 1486, -480, -793, 42 | 991, -119, -733, 676, 93, -660, 445, 225, -577, 43 | 264, 305, -486, 117, 348, -391, 0, 361, -296, 44 | -92, 350, -203, -161, 322, -115, -208, 280, -36, 45 | -235, 229, 33, -244, 171, 90, -238, 112, 134, 46 | -218, 54, 164, -188, 0, 181, -151, -48, 185, 47 | -108, -87, 177, -64, -117, 160, -21, -137, 135, 48 | 20, -147, 104, 55, -146, 70, 84, -137, 34, 49 | 105, -121, 0, 117, -99, -31, 122, -72 }; 50 | 51 | int16_t RespCoeffBuf[FILTERORDER] = { 120, 124, 126, 127, 127, 125, 122, 118, 113, /* Coeff for lowpass Fc=2Hz @ 125 SPS*/ 52 | 106, 97, 88, 77, 65, 52, 38, 24, 8, 53 | -8, -25, -42, -59, -76, -93, -110, -126, -142, 54 | -156, -170, -183, -194, -203, -211, -217, -221, -223, 55 | -223, -220, -215, -208, -198, -185, -170, -152, -132, 56 | -108, -83, -55, -24, 8, 43, 80, 119, 159, 57 | 201, 244, 288, 333, 378, 424, 470, 516, 561, 58 | 606, 650, 693, 734, 773, 811, 847, 880, 911, 59 | 939, 964, 986, 1005, 1020, 1033, 1041, 1047, 1049, 60 | 1047, 1041, 1033, 1020, 1005, 986, 964, 939, 911, 61 | 880, 847, 811, 773, 734, 693, 650, 606, 561, 62 | 516, 470, 424, 378, 333, 288, 244, 201, 159, 63 | 119, 80, 43, 8, -24, -55, -83, -108, -132, 64 | -152, -170, -185, -198, -208, -215, -220, -223, -223, 65 | -221, -217, -211, -203, -194, -183, -170, -156, -142, 66 | -126, -110, -93, -76, -59, -42, -25, -8, 8, 67 | 24, 38, 52, 65, 77, 88, 97, 106, 113, 68 | 118, 122, 125, 127, 127, 126, 124, 120 }; 69 | 70 | 71 | void ecg_respiration_algorithm :: ECG_FilterProcess(int16_t * WorkingBuff, int16_t * CoeffBuf, int16_t* FilterOut) 72 | { 73 | int32_t acc = 0; // accumulator for MACs 74 | int k; 75 | // perform the multiply-accumulate 76 | 77 | for ( k = 0; k < 161; k++ ) 78 | { 79 | acc += (int32_t)(*CoeffBuf++) * (int32_t)(*WorkingBuff--); 80 | } 81 | // saturate the result 82 | 83 | if ( acc > 0x3fffffff ) 84 | { 85 | acc = 0x3fffffff; 86 | } 87 | 88 | else if ( acc < -0x40000000 ) 89 | { 90 | acc = -0x40000000; 91 | } 92 | // convert from Q30 to Q15 93 | *FilterOut = (int16_t)(acc >> 15); 94 | } 95 | 96 | void ecg_respiration_algorithm :: ECG_ProcessCurrSample(int16_t *CurrAqsSample, int16_t *FilteredOut) 97 | { 98 | static uint16_t ECG_bufStart = 0, ECG_bufCur = FILTERORDER - 1, ECGFirstFlag = 1; 99 | static int16_t ECG_Pvev_DC_Sample, ECG_Pvev_Sample;/* Working Buffer Used for Filtering*/ 100 | int16_t *CoeffBuf; 101 | int16_t temp1, temp2, ECGData; 102 | /* Count variable*/ 103 | uint16_t Cur_Chan; 104 | int16_t FiltOut = 0; 105 | CoeffBuf = CoeffBuf_40Hz_LowPass; // Default filter option is 40Hz LowPass 106 | 107 | if ( ECGFirstFlag ) // First Time initialize static variables. 108 | { 109 | for ( Cur_Chan = 0 ; Cur_Chan < FILTERORDER; Cur_Chan++) 110 | { 111 | ECG_WorkingBuff[Cur_Chan] = 0; 112 | } 113 | 114 | ECG_Pvev_DC_Sample = 0; 115 | ECG_Pvev_Sample = 0; 116 | ECGFirstFlag = 0; 117 | } 118 | 119 | temp1 = NRCOEFF * ECG_Pvev_DC_Sample; //First order IIR 120 | ECG_Pvev_DC_Sample = (CurrAqsSample[0] - ECG_Pvev_Sample) + temp1; 121 | ECG_Pvev_Sample = CurrAqsSample[0]; 122 | temp2 = ECG_Pvev_DC_Sample >> 2; 123 | ECGData = (int16_t) temp2; 124 | 125 | /* Store the DC removed value in Working buffer in millivolts range*/ 126 | ECG_WorkingBuff[ECG_bufCur] = ECGData; 127 | ECG_FilterProcess(&ECG_WorkingBuff[ECG_bufCur], CoeffBuf, (int16_t*)&FiltOut); 128 | /* Store the DC removed value in ECG_WorkingBuff buffer in millivolts range*/ 129 | ECG_WorkingBuff[ECG_bufStart] = ECGData; 130 | /* Store the filtered out sample to the LeadInfo buffer*/ 131 | FilteredOut[0] = FiltOut ;//(CurrOut); 132 | ECG_bufCur++; 133 | ECG_bufStart++; 134 | 135 | if ( ECG_bufStart == (FILTERORDER - 1)) 136 | { 137 | ECG_bufStart = 0; 138 | ECG_bufCur = FILTERORDER - 1; 139 | } 140 | 141 | return ; 142 | } 143 | 144 | void ecg_respiration_algorithm :: QRS_Algorithm_Interface(int16_t CurrSample,volatile uint8_t *Heart_rate) 145 | { 146 | static int16_t prev_data[32] = {0}; 147 | int16_t i; 148 | long Mac = 0; 149 | prev_data[0] = CurrSample; 150 | 151 | for ( i = 31; i > 0; i--) 152 | { 153 | Mac += prev_data[i]; 154 | prev_data[i] = prev_data[i - 1]; 155 | } 156 | 157 | Mac += CurrSample; 158 | Mac = Mac >> 2; 159 | CurrSample = (int16_t) Mac; 160 | QRS_Second_Prev_Sample = QRS_Prev_Sample ; 161 | QRS_Prev_Sample = QRS_Current_Sample ; 162 | QRS_Current_Sample = QRS_Next_Sample ; 163 | QRS_Next_Sample = QRS_Second_Next_Sample ; 164 | QRS_Second_Next_Sample = CurrSample ; 165 | QRS_process_buffer(Heart_rate); 166 | } 167 | 168 | void ecg_respiration_algorithm :: QRS_process_buffer(volatile uint8_t *Heart_rate) 169 | { 170 | int16_t first_derivative = 0 ; 171 | int16_t scaled_result = 0 ; 172 | static int16_t Max = 0 ; 173 | /* calculating first derivative*/ 174 | first_derivative = QRS_Next_Sample - QRS_Prev_Sample ; 175 | /*taking the absolute value*/ 176 | if (first_derivative < 0) 177 | { 178 | first_derivative = -(first_derivative); 179 | } 180 | 181 | scaled_result = first_derivative; 182 | 183 | if ( scaled_result > Max ) 184 | { 185 | Max = scaled_result ; 186 | } 187 | 188 | QRS_B4_Buffer_ptr++; 189 | 190 | if (QRS_B4_Buffer_ptr == TWO_SEC_SAMPLES) 191 | { 192 | QRS_Threshold_Old = ((Max * 7) / 10 ) ; 193 | QRS_Threshold_New = QRS_Threshold_Old ; 194 | first_peak_detect = TRUE ; 195 | Max = 0; 196 | QRS_B4_Buffer_ptr = 0; 197 | } 198 | 199 | if ( TRUE == first_peak_detect ) 200 | { 201 | QRS_check_sample_crossing_threshold(scaled_result,Heart_rate) ; 202 | } 203 | 204 | } 205 | 206 | 207 | void ecg_respiration_algorithm :: QRS_check_sample_crossing_threshold( uint16_t scaled_result,volatile uint8_t *Heart_rate) 208 | { 209 | /* array to hold the sample indexes S1,S2,S3 etc */ 210 | static uint16_t s_array_index = 0 ; 211 | static uint16_t m_array_index = 0 ; 212 | static unsigned char threshold_crossed = FALSE ; 213 | static uint16_t maxima_search = 0 ; 214 | static unsigned char peak_detected = FALSE ; 215 | static uint16_t skip_window = 0 ; 216 | static long maxima_sum = 0 ; 217 | static unsigned int peak = 0; 218 | static unsigned int sample_sum = 0; 219 | static unsigned int nopeak = 0; 220 | uint16_t Max = 0 ; 221 | uint16_t HRAvg; 222 | 223 | 224 | if ( TRUE == threshold_crossed ) 225 | { 226 | /* 227 | Once the sample value crosses the threshold check for the 228 | maxima value till MAXIMA_SEARCH_WINDOW samples are received 229 | */ 230 | sample_count ++ ; 231 | maxima_search ++ ; 232 | 233 | if ( scaled_result > peak ) 234 | { 235 | peak = scaled_result ; 236 | } 237 | 238 | if ( maxima_search >= MAXIMA_SEARCH_WINDOW ) 239 | { 240 | // Store the maxima values for each peak 241 | maxima_sum += peak ; 242 | maxima_search = 0 ; 243 | threshold_crossed = FALSE ; 244 | peak_detected = TRUE ; 245 | } 246 | 247 | }else if ( TRUE == peak_detected ){ 248 | /* 249 | Once the sample value goes below the threshold 250 | skip the samples untill the SKIP WINDOW criteria is meet 251 | */ 252 | sample_count ++ ; 253 | skip_window ++ ; 254 | 255 | if ( skip_window >= MINIMUM_SKIP_WINDOW ) 256 | { 257 | skip_window = 0 ; 258 | peak_detected = FALSE ; 259 | } 260 | 261 | if ( m_array_index == MAX_PEAK_TO_SEARCH ) 262 | { 263 | sample_sum = sample_sum / (MAX_PEAK_TO_SEARCH - 1); 264 | HRAvg = (uint16_t) sample_sum ; 265 | // Compute HR without checking LeadOffStatus 266 | QRS_Heart_Rate = (uint16_t) 60 * SAMPLING_RATE; 267 | QRS_Heart_Rate = QRS_Heart_Rate / HRAvg ; 268 | 269 | if (QRS_Heart_Rate > 250) 270 | { 271 | QRS_Heart_Rate = 250 ; 272 | } 273 | /* Setting the Current HR value in the ECG_Info structure*/ 274 | maxima_sum = maxima_sum / MAX_PEAK_TO_SEARCH; 275 | Max = (int16_t) maxima_sum ; 276 | /* calculating the new QRS_Threshold based on the maxima obtained in 4 peaks */ 277 | maxima_sum = Max * 7; 278 | maxima_sum = maxima_sum / 10; 279 | QRS_Threshold_New = (int16_t)maxima_sum; 280 | /* Limiting the QRS Threshold to be in the permissible range*/ 281 | 282 | if (QRS_Threshold_New > (4 * QRS_Threshold_Old)) 283 | { 284 | QRS_Threshold_New = QRS_Threshold_Old; 285 | } 286 | 287 | sample_count = 0 ; 288 | s_array_index = 0 ; 289 | m_array_index = 0 ; 290 | maxima_sum = 0 ; 291 | sample_index[0] = 0 ; 292 | sample_index[1] = 0 ; 293 | sample_index[2] = 0 ; 294 | sample_index[3] = 0 ; 295 | Start_Sample_Count_Flag = 0; 296 | sample_sum = 0; 297 | } 298 | 299 | }else if ( scaled_result > QRS_Threshold_New ){ 300 | /* 301 | If the sample value crosses the threshold then store the sample index 302 | */ 303 | Start_Sample_Count_Flag = 1; 304 | sample_count ++ ; 305 | m_array_index++; 306 | threshold_crossed = TRUE ; 307 | peak = scaled_result ; 308 | nopeak = 0; 309 | /* storing sample index*/ 310 | sample_index[ s_array_index ] = sample_count ; 311 | 312 | if ( s_array_index >= 1 ) 313 | { 314 | sample_sum += sample_index[ s_array_index ] - sample_index[ s_array_index - 1 ] ; 315 | } 316 | 317 | s_array_index ++ ; 318 | 319 | }else if (( scaled_result < QRS_Threshold_New ) && (Start_Sample_Count_Flag == 1)){ 320 | sample_count ++ ; 321 | nopeak++; 322 | 323 | if (nopeak > (3 * SAMPLING_RATE)) 324 | { 325 | sample_count = 0 ; 326 | s_array_index = 0 ; 327 | m_array_index = 0 ; 328 | maxima_sum = 0 ; 329 | sample_index[0] = 0 ; 330 | sample_index[1] = 0 ; 331 | sample_index[2] = 0 ; 332 | sample_index[3] = 0 ; 333 | Start_Sample_Count_Flag = 0; 334 | peak_detected = FALSE ; 335 | sample_sum = 0; 336 | first_peak_detect = FALSE; 337 | nopeak = 0; 338 | QRS_Heart_Rate = 0; 339 | } 340 | 341 | }else{ 342 | nopeak++; 343 | 344 | if (nopeak > (3 * SAMPLING_RATE)) 345 | { 346 | /* Reset heart rate computation sate variable in case of no peak found in 3 seconds */ 347 | sample_count = 0 ; 348 | s_array_index = 0 ; 349 | m_array_index = 0 ; 350 | maxima_sum = 0 ; 351 | sample_index[0] = 0 ; 352 | sample_index[1] = 0 ; 353 | sample_index[2] = 0 ; 354 | sample_index[3] = 0 ; 355 | Start_Sample_Count_Flag = 0; 356 | peak_detected = FALSE ; 357 | sample_sum = 0; 358 | first_peak_detect = FALSE; 359 | nopeak = 0; 360 | QRS_Heart_Rate = 0; 361 | } 362 | } 363 | 364 | *Heart_rate = (uint8_t)QRS_Heart_Rate; 365 | } 366 | 367 | void ecg_respiration_algorithm :: Resp_FilterProcess(int16_t * RESP_WorkingBuff, int16_t * CoeffBuf, int16_t* FilterOut) 368 | { 369 | int32_t acc=0; // accumulator for MACs 370 | int k; 371 | // perform the multiply-accumulate 372 | for ( k = 0; k < 161; k++ ) 373 | { 374 | acc += (int32_t)(*CoeffBuf++) * (int32_t)(*RESP_WorkingBuff--); 375 | } 376 | // saturate the result 377 | if ( acc > 0x3fffffff ) 378 | { 379 | acc = 0x3fffffff; 380 | } 381 | 382 | else if ( acc < -0x40000000 ) 383 | { 384 | acc = -0x40000000; 385 | } 386 | 387 | // convert from Q30 to Q15 388 | *FilterOut = (int16_t)(acc >> 15); 389 | } 390 | 391 | int16_t ecg_respiration_algorithm :: Resp_ProcessCurrSample(int16_t CurrAqsSample) 392 | { 393 | static uint16_t bufStart=0, bufCur = FILTERORDER-1; 394 | int16_t temp1, temp2;//, RESPData; 395 | int16_t RESPData; 396 | /* Count variable*/ 397 | 398 | int16_t FiltOut; 399 | temp1 = NRCOEFF * Pvev_DC_Sample; 400 | Pvev_DC_Sample = (CurrAqsSample - Pvev_Sample) + temp1; 401 | Pvev_Sample = CurrAqsSample; 402 | temp2 = Pvev_DC_Sample; 403 | RESPData = (int16_t) temp2; 404 | RESPData = CurrAqsSample; 405 | /* Store the DC removed value in RESP_WorkingBuff buffer in millivolts range*/ 406 | RESP_WorkingBuff[bufCur] = RESPData; 407 | Resp_FilterProcess(&RESP_WorkingBuff[bufCur],RespCoeffBuf,(int16_t*)&FiltOut); 408 | /* Store the DC removed value in Working buffer in millivolts range*/ 409 | RESP_WorkingBuff[bufStart] = RESPData; 410 | /* Store the filtered out sample to the LeadInfo buffer*/ 411 | bufCur++; 412 | bufStart++; 413 | 414 | if ( bufStart >= (FILTERORDER-1)) 415 | { 416 | bufStart=0; 417 | bufCur = FILTERORDER-1; 418 | } 419 | 420 | return FiltOut; 421 | } 422 | 423 | void ecg_respiration_algorithm :: RESP_Algorithm_Interface(int16_t CurrSample,volatile uint8_t *RespirationRate) 424 | { 425 | static int16_t prev_data[64] ={0}; 426 | char i; 427 | long Mac=0; 428 | prev_data[0] = CurrSample; 429 | 430 | for ( i=63; i > 0; i--) 431 | { 432 | Mac += prev_data[i]; 433 | prev_data[i] = prev_data[i-1]; 434 | 435 | } 436 | 437 | Mac += CurrSample; 438 | CurrSample = (int16_t) Mac >> 1; 439 | RESP_Second_Prev_Sample = RESP_Prev_Sample ; 440 | RESP_Prev_Sample = RESP_Current_Sample ; 441 | RESP_Current_Sample = RESP_Next_Sample ; 442 | RESP_Next_Sample = RESP_Second_Next_Sample ; 443 | RESP_Second_Next_Sample = CurrSample;// << 3 ; 444 | Respiration_Rate_Detection(RESP_Second_Next_Sample,RespirationRate); 445 | } 446 | 447 | void ecg_respiration_algorithm :: Respiration_Rate_Detection(int16_t Resp_wave,volatile uint8_t *RespirationRate) 448 | { 449 | static uint16_t skipCount = 0, SampleCount = 0,TimeCnt=0, SampleCountNtve=0, PtiveCnt =0,NtiveCnt=0 ; 450 | static int16_t MinThreshold = 0x7FFF, MaxThreshold = 0x8000, PrevSample = 0, PrevPrevSample = 0, PrevPrevPrevSample =0; 451 | static int16_t MinThresholdNew = 0x7FFF, MaxThresholdNew = 0x8000, AvgThreshold = 0; 452 | static unsigned char startCalc=0, PtiveEdgeDetected=0, NtiveEdgeDetected=0, peakCount = 0; 453 | static uint16_t PeakCount[8]; 454 | SampleCount++; 455 | SampleCountNtve++; 456 | TimeCnt++; 457 | 458 | if (Resp_wave < MinThresholdNew) 459 | { 460 | MinThresholdNew = Resp_wave; 461 | } 462 | 463 | if (Resp_wave > MaxThresholdNew) 464 | { 465 | MaxThresholdNew = Resp_wave; 466 | } 467 | 468 | if (SampleCount > 1000) 469 | { 470 | SampleCount =0; 471 | } 472 | if (SampleCountNtve > 1000) 473 | { 474 | SampleCountNtve =0; 475 | } 476 | 477 | if ( startCalc == 1) 478 | { 479 | 480 | if (TimeCnt >= 500) 481 | { 482 | TimeCnt =0; 483 | 484 | if ( (MaxThresholdNew - MinThresholdNew) > 400) 485 | { 486 | MaxThreshold = MaxThresholdNew; 487 | MinThreshold = MinThresholdNew; 488 | AvgThreshold = MaxThreshold + MinThreshold; 489 | AvgThreshold = AvgThreshold >> 1; 490 | }else{ 491 | 492 | startCalc = 0; 493 | Respiration_Rate = 0; 494 | } 495 | } 496 | PrevPrevPrevSample = PrevPrevSample; 497 | PrevPrevSample = PrevSample; 498 | PrevSample = Resp_wave; 499 | 500 | if ( skipCount == 0) 501 | { 502 | 503 | if (PrevPrevPrevSample < AvgThreshold && Resp_wave > AvgThreshold) 504 | { 505 | 506 | if ( SampleCount > 40 && SampleCount < 700) 507 | { 508 | PtiveEdgeDetected = 1; 509 | PtiveCnt = SampleCount; 510 | skipCount = 4; 511 | } 512 | 513 | SampleCount = 0; 514 | } 515 | 516 | if (PrevPrevPrevSample < AvgThreshold && Resp_wave > AvgThreshold) 517 | { 518 | 519 | if ( SampleCountNtve > 40 && SampleCountNtve < 700) 520 | { 521 | NtiveEdgeDetected = 1; 522 | NtiveCnt = SampleCountNtve; 523 | skipCount = 4; 524 | } 525 | 526 | SampleCountNtve = 0; 527 | } 528 | 529 | if (PtiveEdgeDetected ==1 && NtiveEdgeDetected ==1) 530 | { 531 | PtiveEdgeDetected = 0; 532 | NtiveEdgeDetected =0; 533 | 534 | if (abs(PtiveCnt - NtiveCnt) < 5) 535 | { 536 | PeakCount[peakCount++] = PtiveCnt; 537 | PeakCount[peakCount++] = NtiveCnt; 538 | 539 | if( peakCount == 8) 540 | { 541 | peakCount = 0; 542 | PtiveCnt = PeakCount[0] + PeakCount[1] + PeakCount[2] + PeakCount[3] + 543 | PeakCount[4] + PeakCount[5] + PeakCount[6] + PeakCount[7]; 544 | PtiveCnt = PtiveCnt >> 3; 545 | Respiration_Rate = 6000/PtiveCnt; // 60 * 125/SampleCount; 546 | } 547 | } 548 | } 549 | 550 | }else{ 551 | skipCount--; 552 | } 553 | }else{ 554 | TimeCnt++; 555 | 556 | if (TimeCnt >= 500) 557 | { 558 | TimeCnt = 0; 559 | 560 | if ( (MaxThresholdNew - MinThresholdNew) > 400) 561 | { 562 | startCalc = 1; 563 | MaxThreshold = MaxThresholdNew; 564 | MinThreshold = MinThresholdNew; 565 | AvgThreshold = MaxThreshold + MinThreshold; 566 | AvgThreshold = AvgThreshold >> 1; 567 | PrevPrevPrevSample = Resp_wave; 568 | PrevPrevSample = Resp_wave; 569 | PrevSample = Resp_wave; 570 | } 571 | } 572 | } 573 | 574 | *RespirationRate=(uint8_t)Respiration_Rate; 575 | } 576 | --------------------------------------------------------------------------------