diff --git a/.gitignore b/.gitignore index 176b4ea..51f91ff 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ *.cfg *.json *.svd +*.zip +*.DS_Store diff --git a/LICENSE b/LICENSE index 310b44e..aaf78ba 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ The MIT License (MIT) -Copyright (c) 2015-2017 World Famous Electroncs LLC, Brooklyn, New York. +Copyright (c) 2015-2024 World Famous Electroncs LLC, Brooklyn, New York. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README-archive-3-2018.md b/README-archive-3-2018.md deleted file mode 100644 index 003d39d..0000000 --- a/README-archive-3-2018.md +++ /dev/null @@ -1,159 +0,0 @@ - -![logo](https://avatars0.githubusercontent.com/u/7002937?v=3&s=200) -# [PulseSensor.com](https://pulsesensor.com) Playground -``` -This Playground is a collection of code for the most popular uses of PulseSensor and Arduino. - -- The playgroud includes a number of projects, with the code already written-out and commented! 🤘  -- Use this code to get started quickly, or do advanced stuff. 👍 -- Switch between projects right in the Arduino IDE (software). 💻 -- Contritube your projects code back to the GitHub hive-mind. 🐝 -``` - ---- -## Install the PulseSensor Playground Library ! -
How To Install: 🤓 - -An Arduino Library is a collection of code and examples on a specific topic or device. For example, our PulseSensor Playground Library is a collection of code and projects made just for your PulseSensor and Arduino. - -(**NOTE** If you do not have Arduino, you can download it [here](https://www.arduino.cc/en/Main/Software)) - -To install the PulseSensor Playground Library, in Arduino, to go -`Sketch > Include Library > Manage Library...` - - - - -In the Library Manager: Search for and Select -`"PulseSensor.com` - - - - -Install or update to the lastest version.👍 - - - - -Hurray! Once this library is installed you will see our examples in Arduino's dropdown! -To select an example project, go to: -`File > Examples > PulseSensor Playground > GettingStartedProject` - - - - - - -More Info On Libraries in General 👉 [https://www.arduino.cc/en/Guide/Libraries](https://www.arduino.cc/en/Guide/Libraries). - - - -
- ---- -## Playground Project Descriptions: - - -### Getting Started Project:   - Plug your sensor in for the first time! Blink an LED with your pulse, live. - -- [**Project Page**](https://pulsesensor.com/pages/code-and-guide) - - ---- - -### Calculate BPM: - Focus-in on the code that calculates a user's HeartRate Beats Per Minute, "BPM". - See the best practises to get the best signal. - -- [**Project Page**](https://pulsesensor.com/pages/getting-advanced) - - ---- - -### Make A Sound to a live Heartbeat: - Transform the heartbeat into a live "beep" with a speaker. - -- [**Project Page**](https://pulsesensor.com/pages/pulse-sensor-speaker-tutorial) - - ---- - -### Move a Motor to a live Heartbeat: - Make a servo motor pulse to your live heartbeat. - -- [**Project Page**](https://pulsesensor.com/pages/pulse-sensor-servo-tutorial) - - - --- - -### Connect Two (or more) Pulse Sensors: - Use 2 or more Pulse Sensors on one Arduino. - -- [**Project Page**](https://pulsesensor.com/pages/two-or-more-pulse-sensors) - - - --- - -### Processing Visualizer: - - Get detailed visualization of the heart's pulse and behavior. Send the PulseSensor data into Processing! - -- [**Project Page**](https://pulsesensor.com/pages/getting-advanced) - - - ---- - -### Pulse Transit Time: - - Use two Pulse Sensors on different parts of your body to measure Pulse Transit Time! - -- [**Project Page**](https://pulsesensor.com/pages/pulse-transit-time) - - - ---- - -## Connecting the Harware 😎 -1. Prepare the sensor, with the Kit parts. - - - -2. See the recommended wiring for your specific project - - - - - ---- - -## The Functions Guide - -We put together a [HANDY GUIDE](https://github.com/WorldFamousElectronics/PulseSensorPlayground/blob/master/resources/PulseSensor%20Playground%20Tools.md) to the function-ality of our library. Check it out if you want to dive into the inner workings! - ---- - -## Troubleshooting Your Signal: - -
Ugh, Where's the Beat ? 😵 - If you're having trouble seeing a heartbeat, make sure that you are using 'Goldilocks' pressure on the Pulse Sensor: Not too hard, not too soft. Squeezing the Pulse Sensor too hard against your skin will make the heartbeat go away, and not enough pressure will cause too much noise to creep in! - -If you are seeing way too many Beats Per Minute, or you are getting lots of noise, try adjusting the Threshold setting. The Threshold variable tells Arduino when to find a pulse that is legit. Adjust this number (noted below with arrows) up for less sensitivity and down for more sensitivity. In the [**StarterProject**](https://pulsesensor.com/pages/code-and-guide) you can find the Threshold variable as shown in the pic below: - - ![StarterThreshold](https://github.com/WorldFamousElectronics/PulseSensorPlayground/blob/master/Images/screenshot-threshold-arrows.png) - -In the other examples, the `THRESHOLD` is defined at the top of the code. - - -
- ---- - -## Give and Get Feedback -The [Issues Tab](https://github.com/WorldFamousElectronics/PulseSensorStarterProject/issues) will get you the quickest answers to common techinal questions. - - ---- - -#### Legal: PulseSensor.com® World Famous Electronics llc. in Brooklyn, NY. USA diff --git a/Version 2.0 Beta Read Me.md b/Version 2.0 Beta Read Me.md new file mode 100644 index 0000000..652320e --- /dev/null +++ b/Version 2.0 Beta Read Me.md @@ -0,0 +1,30 @@ +# This branch is for development and testing of PulseSensor Playground Library v2.0 + +High-level goals for v2.0: + +- Remove user requirement for defining USE ARDUINO INTERRUPTS + +Files effected: + +- All example sketches will need to accomodate changes +- Interrupts.h will be superceded +- PulseSensorPlayground.h and PulseSensorPlayground.cpp will need to accomodate changes +- All comments in all files will be reviewed and editied to reflect paradigm shift + + +Notes 12/6/23 +It works! +The file SelectTimer.h contains the most up to date hardware architecture that +has hardware timer interrupt support. + +## TO DO: + +- Make adjustments to example sketches to accomodate library decisions +- Make a program PulseSensorFullSystem.ino to make hardware testing easy +- Test ATtiny85 using Digispark board + + + + + + diff --git a/examples/Getting_BPM_to_Monitor/Getting_BPM_to_Monitor.ino b/examples/Getting_BPM_to_Monitor/Getting_BPM_to_Monitor.ino index ea6a457..49eecfa 100644 --- a/examples/Getting_BPM_to_Monitor/Getting_BPM_to_Monitor.ino +++ b/examples/Getting_BPM_to_Monitor/Getting_BPM_to_Monitor.ino @@ -9,7 +9,6 @@ 4) Blinks the builtin LED with user's Heartbeat. --------------------------------------------------------------------*/ -#define USE_ARDUINO_INTERRUPTS true // Set-up low-level interrupts for most acurate BPM math. #include // Includes the PulseSensorPlayground Library. // Variables diff --git a/examples/PulseSensorLIbrary_V2_System_Test/PulseSensorLIbrary_V2_System_Test.ino b/examples/PulseSensorLIbrary_V2_System_Test/PulseSensorLIbrary_V2_System_Test.ino new file mode 100644 index 0000000..009a597 --- /dev/null +++ b/examples/PulseSensorLIbrary_V2_System_Test/PulseSensorLIbrary_V2_System_Test.ino @@ -0,0 +1,155 @@ +/* + This example will test functionality of PulseSensor user functions. + Program any hardware with this sketch and open a serial port + to view the test instructions, operation and results. + Functionality that is explicity tested by the program: + pause() + resume() + getInterBeatIntervalMs() + getBeatsPerMinute() + getLatestSample() + getPulseAmplitude() + getLastBeatTime() + All other functionality is implicitly tested by successfully running the program. + + + + Check out the PulseSensor Playground Tools for explaination + of all user functions and directives. + https://github.com/WorldFamousElectronics/PulseSensorPlayground/blob/master/resources/PulseSensor%20Playground%20Tools.md + + Copyright World Famous Electronics LLC - see LICENSE + Contributors: + Joel Murphy, https://pulsesensor.com + Yury Gitman, https://pulsesensor.com + Bradford Needham, @bneedhamia, https://bluepapertech.com + + Licensed under the MIT License, a copy of which + should have been included with this software. + + This software is not intended for medical use. +*/ + +/* + Test notes here +*/ + +#include + +// Test variables +#define TEST_DURATION 10000 // run time in milliseconds +unsigned long thisTime; +bool testing = false; +bool normal = false; +uint8_t errorCode = 0x00; // maybe used for anything automatic? +int testBPM, testIBI, testAmp, testLastBeatTime; // test variables +int beatCounter; +int firstBeatTime, lastBeatTime, firstToLastBeatTime; + +// Standard PulseSensor Stuff +const int OUTPUT_TYPE = SERIAL_PLOTTER; +const int PULSE_INPUT = A0; +const int PULSE_BLINK = LED_BUILTIN; +const int PULSE_FADE = 5; +const int THRESHOLD = 550; // Adjust this number to avoid noise when idle + +PulseSensorPlayground pulseSensor; + +void setup() { + + Serial.begin(115200); + pulseSensor.analogInput(PULSE_INPUT); + pulseSensor.blinkOnPulse(PULSE_BLINK); + pulseSensor.fadeOnPulse(PULSE_FADE); + + pulseSensor.setSerial(Serial); + pulseSensor.setOutputType(OUTPUT_TYPE); + pulseSensor.setThreshold(THRESHOLD); + + // Now that everything is ready, start reading the PulseSensor signal. + if (!pulseSensor.begin()) { + /* + PulseSensor initialization failed, + likely because our particular Arduino platform interrupts + aren't supported yet. + + If your Sketch hangs here, try PulseSensor_BPM_Alternative.ino, + which doesn't use interrupts. + */ + for(;;) { + // Flash the led to show things didn't work. + digitalWrite(PULSE_BLINK, LOW); + delay(50); Serial.println('!'); + digitalWrite(PULSE_BLINK, HIGH); + delay(50); + } + } + pulseSensor.pause(); + delay(100); + printInstructions(); + +} + +void loop() { + if(testing){ + runTest(millis()); + } + if(normal){ + runNormal(); + } + checkSerial(); +} // loop + + +/* + Receives millis() and runs a test for TEST_DURATION +*/ +void runTest(unsigned long startTime){ + beatCounter = 0; // reset the beat counter + testIBI = 0; + testBPM = 0; + testAmp = 0; + firstBeatTime = -1; + lastBeatTime = -1; + Serial.println("\n\tSTART TEST"); + pulseSensor.resume(); // start the sensing! + while((millis() - startTime) < TEST_DURATION){ + Serial.println(pulseSensor.getLatestSample()); // print raw data for plotter or monitor review + if(pulseSensor.sawStartOfBeat()){ + beatCounter++; + if(firstBeatTime < 0){ firstBeatTime = pulseSensor.getLastBeatTime(); } + testBPM += pulseSensor.getBeatsPerMinute(); + testIBI += pulseSensor.getInterBeatIntervalMs(); + testAmp += pulseSensor.getPulseAmplitude(); + } + delay(20); + } + lastBeatTime = pulseSensor.getLastBeatTime(); + pulseSensor.pause(); + testBPM /= beatCounter; + testIBI /= beatCounter; + testAmp /= beatCounter; + firstToLastBeatTime = lastBeatTime - firstBeatTime; + + printResults(); + printInstructions(); + testing = false; +} + + +void runNormal(){ + if(pulseSensor.UsingHardwareTimer){ + delay(20); + pulseSensor.outputSample(); + } else { + if (pulseSensor.sawNewSample()) { + if (--pulseSensor.samplesUntilReport == (byte) 0) { + pulseSensor.samplesUntilReport = SAMPLES_PER_SERIAL_SAMPLE; + pulseSensor.outputSample(); + } + } + } + if (pulseSensor.sawStartOfBeat()) { + pulseSensor.outputBeat(); + } +} \ No newline at end of file diff --git a/examples/PulseSensorLIbrary_V2_System_Test/serialStuff.ino b/examples/PulseSensorLIbrary_V2_System_Test/serialStuff.ino new file mode 100644 index 0000000..1abf714 --- /dev/null +++ b/examples/PulseSensorLIbrary_V2_System_Test/serialStuff.ino @@ -0,0 +1,62 @@ +/* + +*/ + +void checkSerial(){ + if(Serial.available()){ + char inChar = Serial.read(); + switch(inChar){ + case 't': + testing = true; + break; + case 'r': + normal = true; + pulseSensor.resume(); + break; + case 'p': + normal = false; + pulseSensor.pause(); + printInstructions(); + break; + default: + Serial.print("i got "); Serial.println(inChar); + } + } // Serial.available +} // checkSerial + + +void printResults(){ + float durationOfBeats = float(firstToLastBeatTime/1000.0); + Serial.println("\tTEST COMPLETE"); + Serial.print("\tAverage BPM: "); Serial.println(testBPM); + Serial.print("\tAverage IBI: "); Serial.println(testIBI); + Serial.print("\tAverage Pulse Amplitude: "); Serial.println(testAmp); + Serial.print("\tFirst to last heartbeat time: "); Serial.print(durationOfBeats,3); Serial.println(" Seconds"); + Serial.print("\tPlayground Library is using a "); + if(pulseSensor.UsingHardwareTimer){ + Serial.println("hardware timer"); + } else { + Serial.println("software timer"); + } +} + +void printInstructions(){ + Serial.print("\nPulseSensor Playground "); Serial.println(PULSESENSOR_PLAYGROUND_VERSION_STRING); + Serial.println("Full System Test Instructions:"); + Serial.println("\n\t1) Connect PulseSensor wires to the board under test"); + Serial.println("\t2) Use a known good signal source to connect PulseSensor to"); + Serial.println("\t3) Send 't' to begin the software funcional test"); + Serial.println("\t4) The test will run 10 seconds, be still and wait for results"); + Serial.println("\nDuring the test, the board will output PulseSensor raw data"); + Serial.println("for review in monitor or plotter."); + Serial.println("\nSend 'r' to run the pulseSensor with normal output"); + Serial.println("Send 'p' to pause normal output, and print this message"); + Serial.print("PulseSensor is currently "); + if(pulseSensor.isPaused()){ + Serial.println("paused"); + } else { + Serial.println("running!"); + } + // Serial.println(""); + // Serial.println(""); +} \ No newline at end of file diff --git a/examples/PulseSensor_ATtiny85_Serial/PulseSensor_ATtiny85_Serial.ino b/examples/PulseSensor_ATtiny85_Serial/PulseSensor_ATtiny85_Serial.ino index 54a4d9f..b104da4 100644 --- a/examples/PulseSensor_ATtiny85_Serial/PulseSensor_ATtiny85_Serial.ino +++ b/examples/PulseSensor_ATtiny85_Serial/PulseSensor_ATtiny85_Serial.ino @@ -1,9 +1,9 @@ /* - Code to detect pulses from the PulseSensor, - using an interrupt service routine. + Code to detect heartbeat pulses from the PulseSensor - Here is a link to the tutorial\ - https://pulsesensor.com/pages/ATtiny + Check out the PulseSensor Playground Tools for explaination + of all user functions and directives. + https://github.com/WorldFamousElectronics/PulseSensorPlayground/blob/master/resources/PulseSensor%20Playground%20Tools.md Copyright World Famous Electronics LLC - see LICENSE Contributors: @@ -18,14 +18,14 @@ */ /* - Every Sketch that uses the PulseSensor Playground must - define USE_ARDUINO_INTERRUPTS before including PulseSensorPlayground.h. - Here, #define USE_ARDUINO_INTERRUPTS true tells the library to use - interrupts to automatically read and process PulseSensor data. - - See PulseSensor_BPM_Alternative.ino for an example of not using interrupts. + Include the PulseSensor Playground library to get all the good stuff! + The PulseSensor Playground library will decide whether to use + a hardware timer to get accurate sample readings by checking + what target hardware is being used and adjust accordingly. + You may see a "warning" come up in red during compilation + if a hardware timer is not being used. */ -#define USE_ARDUINO_INTERRUPTS true + #include #include /* @@ -116,21 +116,55 @@ void setup() { } void loop() { + /* + See if a sample is ready from the PulseSensor. + + If USE_HARDWARE_TIMER is true, the PulseSensor Playground + will automatically read and process samples from + the PulseSensor. + + If USE_HARDWARE_TIMER is false, the call to sawNewSample() + will check to see how much time has passed, then read + and process a sample (analog voltage) from the PulseSensor. + Call this function often to maintain 500Hz sample rate, + that is every 2 milliseconds. Best not to have any delay() + functions in the loop when using a software timer. + + Check the compatibility of your hardware at this link + + and delete the unused code portions in your saved copy, if you like. + */ +if(pulseSensor.UsingHardwareTimer){ /* Wait a bit. We don't output every sample, because our baud rate won't support that much I/O. */ - delay(20); - + delay(20); // write the latest sample to Serial. - pulseSensor.outputSample(); - + pulseSensor.outputSample(); +} else { +/* + When using a software timer, we have to check to see if it is time + to acquire another sample. A call to sawNewSample will do that. +*/ + if (pulseSensor.sawNewSample()) { + /* + Every so often, send the latest Sample. + We don't print every sample, because our baud rate + won't support that much I/O. + */ + if (--pulseSensor.samplesUntilReport == (byte) 0) { + pulseSensor.samplesUntilReport = SAMPLES_PER_SERIAL_SAMPLE; + pulseSensor.outputSample(); + } + } +} /* If a beat has happened since we last checked, write the per-beat information to Serial. */ - if (pulseSensor.sawStartOfBeat()) { - pulseSensor.outputBeat(); - } + if (pulseSensor.sawStartOfBeat()) { + pulseSensor.outputBeat(); + } } diff --git a/examples/PulseSensor_ATtiny85_noSerial/PulseSensor_ATtiny85_noSerial.ino b/examples/PulseSensor_ATtiny85_noSerial/PulseSensor_ATtiny85_noSerial.ino index 5edd069..f798507 100644 --- a/examples/PulseSensor_ATtiny85_noSerial/PulseSensor_ATtiny85_noSerial.ino +++ b/examples/PulseSensor_ATtiny85_noSerial/PulseSensor_ATtiny85_noSerial.ino @@ -1,9 +1,9 @@ /* - Code to detect pulses from the PulseSensor, - using an interrupt service routine. + Code to detect heartbeat pulses from the PulseSensor - Here is a link to the tutorial\ - https://pulsesensor.com/pages/ATtiny + Check out the PulseSensor Playground Tools for explaination + of all user functions and directives. + https://github.com/WorldFamousElectronics/PulseSensorPlayground/blob/master/resources/PulseSensor%20Playground%20Tools.md Copyright World Famous Electronics LLC - see LICENSE Contributors: @@ -18,14 +18,14 @@ */ /* - Every Sketch that uses the PulseSensor Playground must - define USE_ARDUINO_INTERRUPTS before including PulseSensorPlayground.h. - Here, #define USE_ARDUINO_INTERRUPTS true tells the library to use - interrupts to automatically read and process PulseSensor data. - - See PulseSensor_BPM_Alternative.ino for an example of not using interrupts. + Include the PulseSensor Playground library to get all the good stuff! + The PulseSensor Playground library will decide whether to use + a hardware timer to get accurate sample readings by checking + what target hardware is being used and adjust accordingly. + You may see a "warning" come up in red during compilation + if a hardware timer is not being used. */ -#define USE_ARDUINO_INTERRUPTS true + #include /* diff --git a/examples/PulseSensor_BPM/PulseSensor_BPM.ino b/examples/PulseSensor_BPM/PulseSensor_BPM.ino index 96264f1..8aace06 100644 --- a/examples/PulseSensor_BPM/PulseSensor_BPM.ino +++ b/examples/PulseSensor_BPM/PulseSensor_BPM.ino @@ -1,9 +1,9 @@ /* - Code to detect pulses from the PulseSensor, - using an interrupt service routine. + Code to detect heartbeat pulses from the PulseSensor - Here is a link to the tutorial\ - https://pulsesensor.com/pages/getting-advanced + Check out the PulseSensor Playground Tools for explaination + of all user functions and directives. + https://github.com/WorldFamousElectronics/PulseSensorPlayground/blob/master/resources/PulseSensor%20Playground%20Tools.md Copyright World Famous Electronics LLC - see LICENSE Contributors: @@ -18,14 +18,14 @@ */ /* - Every Sketch that uses the PulseSensor Playground must - define USE_ARDUINO_INTERRUPTS before including PulseSensorPlayground.h. - Here, #define USE_ARDUINO_INTERRUPTS true tells the library to use - interrupts to automatically read and process PulseSensor data. - - See PulseSensor_BPM_Alternative.ino for an example of not using interrupts. + Include the PulseSensor Playground library to get all the good stuff! + The PulseSensor Playground library will decide whether to use + a hardware timer to get accurate sample readings by checking + what target hardware is being used and adjust accordingly. + You may see a "warning" come up in red during compilation + if a hardware timer is not being used. */ -#define USE_ARDUINO_INTERRUPTS true + #include /* @@ -113,21 +113,55 @@ void setup() { } void loop() { + /* + See if a sample is ready from the PulseSensor. + + If USE_HARDWARE_TIMER is true, the PulseSensor Playground + will automatically read and process samples from + the PulseSensor. + + If USE_HARDWARE_TIMER is false, the call to sawNewSample() + will check to see how much time has passed, then read + and process a sample (analog voltage) from the PulseSensor. + Call this function often to maintain 500Hz sample rate, + that is every 2 milliseconds. Best not to have any delay() + functions in the loop when using a software timer. + + Check the compatibility of your hardware at this link + + and delete the unused code portions in your saved copy, if you like. + */ +if(pulseSensor.UsingHardwareTimer){ /* Wait a bit. We don't output every sample, because our baud rate won't support that much I/O. */ - delay(20); - + delay(20); // write the latest sample to Serial. - pulseSensor.outputSample(); - + pulseSensor.outputSample(); +} else { +/* + When using a software timer, we have to check to see if it is time + to acquire another sample. A call to sawNewSample will do that. +*/ + if (pulseSensor.sawNewSample()) { + /* + Every so often, send the latest Sample. + We don't print every sample, because our baud rate + won't support that much I/O. + */ + if (--pulseSensor.samplesUntilReport == (byte) 0) { + pulseSensor.samplesUntilReport = SAMPLES_PER_SERIAL_SAMPLE; + pulseSensor.outputSample(); + } + } +} /* If a beat has happened since we last checked, write the per-beat information to Serial. */ - if (pulseSensor.sawStartOfBeat()) { - pulseSensor.outputBeat(); - } + if (pulseSensor.sawStartOfBeat()) { + pulseSensor.outputBeat(); + } } diff --git a/examples/PulseSensor_BPM_Alternative/PulseSensor_BPM_Alternative.ino b/examples/PulseSensor_BPM_Alternative/PulseSensor_BPM_Alternative.ino deleted file mode 100644 index c36f8b2..0000000 --- a/examples/PulseSensor_BPM_Alternative/PulseSensor_BPM_Alternative.ino +++ /dev/null @@ -1,182 +0,0 @@ -/* - Sketch to handle each sample read from a PulseSensor. - Typically used when you don't want to use interrupts - to read PulseSensor voltages. - - Here is a link to the tutorial that discusses this code - https://pulsesensor.com/pages/getting-advanced - - Copyright World Famous Electronics LLC - see LICENSE - Contributors: - Joel Murphy, https://pulsesensor.com - Yury Gitman, https://pulsesensor.com - Bradford Needham, @bneedhamia, https://bluepapertech.com - - Licensed under the MIT License, a copy of which - should have been included with this software. - - This software is not intended for medical use. -*/ - -/* - Every Sketch that uses the PulseSensor Playground must - define USE_ARDUINO_INTERRUPTS before including PulseSensorPlayground.h. - Here, #define USE_ARDUINO_INTERRUPTS false tells the library to - not use interrupts to read data from the PulseSensor. - - If you want to use interrupts, simply change the line below - to read: - #define USE_ARDUINO_INTERRUPTS true - - Set US_PS_INTERRUPTS to false if either - 1) Your Arduino platform's interrupts aren't yet supported - by PulseSensor Playground, or - 2) You don't wish to use interrupts because of the side effects. - - NOTE: if US_PS_INTERRUPTS is false, your Sketch must - call pulse.sawNewSample() at least once every 2 milliseconds - to accurately read the PulseSensor signal. -*/ -#define USE_ARDUINO_INTERRUPTS false -#include - -/* - The format of our output. - - Set this to PROCESSING_VISUALIZER if you're going to run - the Processing Visualizer Sketch. - See https://github.com/WorldFamousElectronics/PulseSensor_Amped_Processing_Visualizer - - Set this to SERIAL_PLOTTER if you're going to run - the Arduino IDE's Serial Plotter. -*/ -const int OUTPUT_TYPE = SERIAL_PLOTTER; - -/* - Pinout: - PULSE_INPUT = Analog Input. Connected to the pulse sensor - purple (signal) wire. - PULSE_BLINK = digital Output. Connected to an LED (and 1K series resistor) - that will flash on each detected pulse. - PULSE_FADE = digital Output. PWM pin onnected to an LED (and 1K series resistor) - that will smoothly fade with each pulse. - NOTE: PULSE_FADE must be a pin that supports PWM. Do not use - pin 9 or 10, because those pins' PWM interferes with the sample timer. - THRESHOLD should be set higher than the PulseSensor signal idles - at when there is nothing touching it. The expected idle value - should be 512, which is 1/2 of the ADC range. To check the idle value - open a serial monitor and make note of the PulseSensor signal values - with nothing touching the sensor. THRESHOLD should be a value higher - than the range of idle noise by 25 to 50 or so. When the library - is finding heartbeats, the value is adjusted based on the pulse signal - waveform. THRESHOLD sets the default when there is no pulse present. - Adjust as neccesary. -*/ -const int PULSE_INPUT = A0; -const int PULSE_BLINK = LED_BUILTIN; -const int PULSE_FADE = 5; -const int THRESHOLD = 550; // Adjust this number to avoid noise when idle - -/* - samplesUntilReport = the number of samples remaining to read - until we want to report a sample over the serial connection. - - We want to report a sample value over the serial port - only once every 20 milliseconds (10 samples) to avoid - doing Serial output faster than the Arduino can send. -*/ -byte samplesUntilReport; -const byte SAMPLES_PER_SERIAL_SAMPLE = 10; - -/* - All the PulseSensor Playground functions. -*/ -PulseSensorPlayground pulseSensor; - -void setup() { - /* - Use 115200 baud because that's what the Processing Sketch expects to read, - and because that speed provides about 11 bytes per millisecond. - - If we used a slower baud rate, we'd likely write bytes faster than - they can be transmitted, which would mess up the timing - of readSensor() calls, which would make the pulse measurement - not work properly. - */ - Serial.begin(115200); - - // Configure the PulseSensor manager. - pulseSensor.analogInput(PULSE_INPUT); - pulseSensor.blinkOnPulse(PULSE_BLINK); - pulseSensor.fadeOnPulse(PULSE_FADE); - - pulseSensor.setSerial(Serial); - pulseSensor.setOutputType(OUTPUT_TYPE); - pulseSensor.setThreshold(THRESHOLD); - - // Skip the first SAMPLES_PER_SERIAL_SAMPLE in the loop(). - samplesUntilReport = SAMPLES_PER_SERIAL_SAMPLE; - - // Now that everything is ready, start reading the PulseSensor signal. - if (!pulseSensor.begin()) { - /* - PulseSensor initialization failed, - likely because our Arduino platform interrupts - aren't supported yet. - - If your Sketch hangs here, try changing USE_PS_INTERRUPT to false. - */ - for(;;) { - // Flash the led to show things didn't work. - digitalWrite(PULSE_BLINK, LOW); - delay(50); Serial.println('!'); - digitalWrite(PULSE_BLINK, HIGH); - delay(50); - } - } -} - -void loop() { - - /* - See if a sample is ready from the PulseSensor. - - If USE_INTERRUPTS is true, the PulseSensor Playground - will automatically read and process samples from - the PulseSensor. - - If USE_INTERRUPTS is false, this call to sawNewSample() - will, if enough time has passed, read and process a - sample (analog voltage) from the PulseSensor. - */ - if (pulseSensor.sawNewSample()) { - /* - Every so often, send the latest Sample. - We don't print every sample, because our baud rate - won't support that much I/O. - */ - if (--samplesUntilReport == (byte) 0) { - samplesUntilReport = SAMPLES_PER_SERIAL_SAMPLE; - - pulseSensor.outputSample(); - - /* - At about the beginning of every heartbeat, - report the heart rate and inter-beat-interval. - */ - if (pulseSensor.sawStartOfBeat()) { - pulseSensor.outputBeat(); - } - } - - /******* - Here is a good place to add code that could take up - to a millisecond or so to run. - *******/ - } - - /****** - Don't add code here, because it could slow the sampling - from the PulseSensor. - ******/ -} diff --git a/examples/PulseSensor_BPM_UNO_R4/PulseSensor_BPM_UNO_R4.ino b/examples/PulseSensor_BPM_UNO_R4/PulseSensor_BPM_UNO_R4.ino deleted file mode 100644 index 5c27bb3..0000000 --- a/examples/PulseSensor_BPM_UNO_R4/PulseSensor_BPM_UNO_R4.ino +++ /dev/null @@ -1,178 +0,0 @@ -/* - Code to detect pulses from the PulseSensor, - using an interrupt service routine. - ->>>> This example targest the Arduino UNO R4. ->>>> It has been tested on the Minima and the WiFi board variants. - - Here is a link to the tutorial - https://pulsesensor.com/pages/getting-advanced - - Copyright World Famous Electronics LLC - see LICENSE - Contributors: - Joel Murphy, https://pulsesensor.com - Yury Gitman, https://pulsesensor.com - Bradford Needham, @bneedhamia, https://bluepapertech.com - - Licensed under the MIT License, a copy of which - should have been included with this software. - - This software is not intended for medical use. -*/ - -/* - We use the FspTimer to setup a timer interrupt for sample acquisition - FspTimer is part of the hardware core files for the UNO R4 -*/ -#include "FspTimer.h" -FspTimer sampleTimer; - - -/* - Every Sketch that uses the PulseSensor Playground must - define USE_ARDUINO_INTERRUPTS before including PulseSensorPlayground.h. - Here, #define USE_ARDUINO_INTERRUPTS true tells the library to use - interrupts to automatically read and process PulseSensor data. - - See PulseSensorBPM_Alternative.ino for an example of not using interrupts. -*/ -#define USE_ARDUINO_INTERRUPTS true -#include - -/* - This is the timer interrupt service routine where we acquire and process samples -*/ -void sampleTimerISR(timer_callback_args_t __attribute((unused)) *p_args){ - PulseSensorPlayground::OurThis->onSampleTime(); -} - -/* - The format of our output. - - Set this to PROCESSING_VISUALIZER if you're going to run - the Processing Visualizer Sketch. - See https://github.com/WorldFamousElectronics/PulseSensor_Amped_Processing_Visualizer - - Set this to SERIAL_PLOTTER if you're going to run - the Arduino IDE's Serial Plotter. -*/ -const int OUTPUT_TYPE = SERIAL_PLOTTER; - -/* - Pinout: - PULSE_INPUT = Analog Input. Connected to the pulse sensor - purple (signal) wire. - PULSE_BLINK = digital Output. Connected to an LED (and 1K series resistor) - that will flash on each detected pulse. - PULSE_FADE = digital Output. PWM pin onnected to an LED (and 1K series resistor) - that will smoothly fade with each pulse. - NOTE: PULSE_FADE must be a pin that supports PWM. Do not use - pin 9 or 10, because those pins' PWM interferes with the sample timer. - THRESHOLD should be set higher than the PulseSensor signal idles - at when there is nothing touching it. The expected idle value - should be 512, which is 1/2 of the ADC range. To check the idle value - open a serial monitor and make note of the PulseSensor signal values - with nothing touching the sensor. THRESHOLD should be a value higher - than the range of idle noise by 25 to 50 or so. When the library - is finding heartbeats, the value is adjusted based on the pulse signal - waveform. THRESHOLD sets the default when there is no pulse present. - Adjust as neccesary. -*/ -const int PULSE_INPUT = A0; -const int PULSE_BLINK = LED_BUILTIN; -const int PULSE_FADE = 5; -const int THRESHOLD = 550; // Adjust this number to avoid noise when idle - -/* - All the PulseSensor Playground functions. -*/ -PulseSensorPlayground pulseSensor; - -void setup() { - /* - Use 115200 baud because that's what the Processing Sketch expects to read, - and because that speed provides about 11 bytes per millisecond. - - If we used a slower baud rate, we'd likely write bytes faster than - they can be transmitted, which would mess up the sample reading - calls, which would make the pulse measurement not work properly. - */ - Serial.begin(115200); - - // Configure the PulseSensor manager. - - pulseSensor.analogInput(PULSE_INPUT); - pulseSensor.blinkOnPulse(PULSE_BLINK); - pulseSensor.fadeOnPulse(PULSE_FADE); - - pulseSensor.setSerial(Serial); - pulseSensor.setOutputType(OUTPUT_TYPE); - pulseSensor.setThreshold(THRESHOLD); - - // Now that everything is ready, start reading the PulseSensor signal. - if (!pulseSensor.begin()) { - /* - PulseSensor initialization failed, - likely because our particular Arduino platform interrupts - aren't supported yet. - - If your Sketch hangs here, try PulseSensor_BPM_Alternative.ino, - which doesn't use interrupts. - */ - for(;;) { - // Flash the led to show things didn't work. - digitalWrite(PULSE_BLINK, LOW); - delay(50); Serial.println('!'); - digitalWrite(PULSE_BLINK, HIGH); - delay(50); - } - } - -/* - We have to get control of a timer on the UNO R4. First, we try and see if there are any free timers available. - If there are no free timers available, we will just take control of one from some other purpose. - We shouldn't have to force things, but if you use alot of timers, beware of this force use code! - You can check to see if you are forcing by un-commenting the "forcing timer get" print line. - You can check to see what timer you have under your control by un-commenting the "got timer " print line. -*/ - uint8_t timer_type = GPT_TIMER; - int8_t tindex = FspTimer::get_available_timer(timer_type); - if(tindex == 0){ - // Serial.println("forcing timer get;") - FspTimer::force_use_of_pwm_reserved_timer(); - tindex = FspTimer::get_available_timer(timer_type); - } - // Serial.print("got timer "); Serial.println(tindex); - -/* - sampleTimer.begin sets up the timer that we just got control of as a periodic timer with 500Hz frequency. - It also passes the interrupt service routine that we made above. - SAMPLE_RATE_500HZ is defined in the PulseSensorPlayground.h file. -*/ - sampleTimer.begin(TIMER_MODE_PERIODIC, timer_type, tindex, SAMPLE_RATE_500HZ, 0.0f, sampleTimerISR); - sampleTimer.setup_overflow_irq(); - sampleTimer.open(); - sampleTimer.start(); -} - -void loop() { - /* - Wait a bit. - We don't output every sample, because our baud rate - won't support that much I/O. - */ - delay(20); - - // write the latest sample to Serial. - pulseSensor.outputSample(); - - /* - If a beat has happened since we last checked, - write the per-beat information to Serial. - */ - if (pulseSensor.sawStartOfBeat()) { - pulseSensor.outputBeat(); - } - -} - diff --git a/examples/PulseSensor_ESP32/PulseSensor_ESP32.ino b/examples/PulseSensor_ESP32/PulseSensor_ESP32.ino index 116721e..8ec9e2f 100644 --- a/examples/PulseSensor_ESP32/PulseSensor_ESP32.ino +++ b/examples/PulseSensor_ESP32/PulseSensor_ESP32.ino @@ -3,14 +3,14 @@ The code below will serve a web page on the local network and will refresh the BPM with every heartbeat. On startup, the ESP32 will send it's network address - over the serial port. Use that url in any browser - on the same network to connect and veiw the webpage. - - Code to detect pulses from the PulseSensor, - using an interrupt service routine. + over the serial port (for example 192.168.1.172). + Use that url in any browser on the same local network + to connect and veiw the webpage. This code will not + make the page available outside your local network. - Here is a link to the tutorial\ - https://pulsesensor.com/pages/getting-advanced + Check out the PulseSensor Playground Tools for explaination + of all user functions and directives. + https://github.com/WorldFamousElectronics/PulseSensorPlayground/blob/master/resources/PulseSensor%20Playground%20Tools.md Copyright World Famous Electronics LLC - see LICENSE Contributors: @@ -36,19 +36,9 @@ #include /* - The following hardware timer setup supports ESP32 -*/ -hw_timer_t * sampleTimer = NULL; -portMUX_TYPE sampleTimerMux = portMUX_INITIALIZER_UNLOCKED; + Include PulseSensor Playground library for all the good stuff! -/* - Every Sketch that uses the PulseSensor Playground must - define USE_ARDUINO_INTERRUPTS before including PulseSensorPlayground.h. - Here, #define USE_ARDUINO_INTERRUPTS true tells the library to use - interrupts to automatically read and process PulseSensor data. */ -#define USE_ARDUINO_INTERRUPTS true -//#define NO_PULSE_SENSOR_SERIAL true #include /* @@ -198,19 +188,6 @@ void beginWiFi() { Serial.println("\nConnected"); } - -/* - This is the interrupt service routine. - We need to declare it after the PulseSensor Playground - library is compiled, so that the onSampleTime - function is known. -*/ -void IRAM_ATTR onSampleTime() { - portENTER_CRITICAL_ISR(&sampleTimerMux); - PulseSensorPlayground::OurThis->onSampleTime(); - portEXIT_CRITICAL_ISR(&sampleTimerMux); -} - /* When sendPulseSignal is true, PulseSensor Signal data is sent to the serial port for user monitoring. @@ -296,15 +273,6 @@ void setup() { /* Print the control information to the serial monitor */ printControlInfo(); -/* - This will set up and start the timer interrupt on ESP32. - The interrupt will occur every 2000uS or 500Hz. -*/ - sampleTimer = timerBegin(0, 80, true); - timerAttachInterrupt(sampleTimer, &onSampleTime, true); - timerAlarmWrite(sampleTimer, 2000, true); - timerAlarmEnable(sampleTimer); - } void loop() { diff --git a/examples/PulseSensor_Nucleo/PulseSensor_Nucleo.ino b/examples/PulseSensor_Nucleo/PulseSensor_Nucleo.ino deleted file mode 100644 index 0e43fd3..0000000 --- a/examples/PulseSensor_Nucleo/PulseSensor_Nucleo.ino +++ /dev/null @@ -1,206 +0,0 @@ -/* - Sketch to handle each sample read from a PulseSensor. - Typically used when you don't want to use interrupts - to read PulseSensor voltages. - - Here is a link to the tutorial that discusses this code - >> Comming Soon! << - - Use this code as a basic example when targeting STM32 boards. - Open your Arduino > Preferences window, and add this to your Additional Boards Manager URLs: - -https://raw.githubusercontent.com/stm32duino/BoardManagerFiles/master/STM32/package_stm_index.json - - Then, go to Tools > Board > Boards Manager to install the STM32 core. - - This example targets the Nucleo 64 F401RE - If you are using a different Nucleo board, you may have to change some of the - board parameters. Here are the drop-down selections that I used. - - Board: Nucleo-64 - Board Part Number: Nucleo F401RE - Serial Interface: Enable first third (USART to 3 if available) - USB Interace: None - Upload Method: STlink - Optimize: Smallest (-Os defalut) - - This sketch uses Serial1 to communicate with your computer. - Attach an FTDI or other USB > Serial breakout to the pins - - tx: D8 (Arduino Pin) - rx: D2 (Arduino Pin) - - Copyright World Famous Electronics LLC - see LICENSE - Contributors: - Joel Murphy, https://pulsesensor.com - Yury Gitman, https://pulsesensor.com - Bradford Needham, @bneedhamia, https://bluepapertech.com - - Licensed under the MIT License, a copy of which - should have been included with this software. - - This software is not intended for medical use. -*/ - -/* - Every Sketch that uses the PulseSensor Playground must - define USE_ARDUINO_INTERRUPTS before including PulseSensorPlayground.h. - Here, #define USE_ARDUINO_INTERRUPTS false tells the library to - not use interrupts to read data from the PulseSensor. - - If you want to use interrupts, simply change the line below - to read: - #define USE_ARDUINO_INTERRUPTS true - - Set US_PS_INTERRUPTS to false if either - 1) Your Arduino platform's interrupts aren't yet supported - by PulseSensor Playground, or - 2) You don't wish to use interrupts because of the side effects. - - NOTE: if US_PS_INTERRUPTS is false, your Sketch must - call pulse.sawNewSample() at least once every 2 milliseconds - to accurately read the PulseSensor signal. -*/ -#define USE_ARDUINO_INTERRUPTS false -#include - -/* - The format of our output. - - Set this to PROCESSING_VISUALIZER if you're going to run - the Processing Visualizer Sketch. - See https://github.com/WorldFamousElectronics/PulseSensor_Amped_Processing_Visualizer - - Set this to SERIAL_PLOTTER if you're going to run - the Arduino IDE's Serial Plotter. -*/ -const int OUTPUT_TYPE = SERIAL_PLOTTER; - -/* - Pinout: - PULSE_INPUT = Analog Input. Connected to the pulse sensor - purple (signal) wire. - PULSE_BLINK = digital Output. Connected to an LED (and 1K series resistor) - that will flash on each detected pulse. - PULSE_FADE = digital Output. PWM pin onnected to an LED (and 1K series resistor) - that will smoothly fade with each pulse. - NOTE: PULSE_FADE must be a pin that supports PWM. Do not use - pin 9 or 10, because those pins' PWM interferes with the sample timer. - THRESHOLD should be set higher than the PulseSensor signal idles - at when there is nothing touching it. The expected idle value - should be 512, which is 1/2 of the ADC range. To check the idle value - open a serial monitor and make note of the PulseSensor signal values - with nothing touching the sensor. THRESHOLD should be a value higher - than the range of idle noise by 25 to 50 or so. When the library - is finding heartbeats, the value is adjusted based on the pulse signal - waveform. THRESHOLD sets the default when there is no pulse present. - Adjust as neccesary. -*/ -const int PULSE_INPUT = A0; -const int PULSE_BLINK = LED_BUILTIN; -const int PULSE_FADE = 5; -const int THRESHOLD = 550; // Adjust this number to avoid noise when idle - -/* - samplesUntilReport = the number of samples remaining to read - until we want to report a sample over the serial connection. - - We want to report a sample value over the serial port - only once every 20 milliseconds (10 samples) to avoid - doing Serial output faster than the Arduino can send. -*/ -byte samplesUntilReport; -const byte SAMPLES_PER_SERIAL_SAMPLE = 10; - -/* - All the PulseSensor Playground functions. -*/ -PulseSensorPlayground pulseSensor; - -void setup() { - /* - Use 115200 baud because that's what the Processing Sketch expects to read, - and because that speed provides about 11 bytes per millisecond. - - If we used a slower baud rate, we'd likely write bytes faster than - they can be transmitted, which would mess up the timing - of readSensor() calls, which would make the pulse measurement - not work properly. - */ - Serial1.begin(115200); - - // Configure the PulseSensor manager. - pulseSensor.analogInput(PULSE_INPUT); - pulseSensor.blinkOnPulse(PULSE_BLINK); - pulseSensor.fadeOnPulse(PULSE_FADE); - - pulseSensor.setSerial(Serial1); - pulseSensor.setOutputType(OUTPUT_TYPE); - pulseSensor.setThreshold(THRESHOLD); - - // Skip the first SAMPLES_PER_SERIAL_SAMPLE in the loop(). - samplesUntilReport = SAMPLES_PER_SERIAL_SAMPLE; - - // Now that everything is ready, start reading the PulseSensor signal. - if (!pulseSensor.begin()) { - /* - PulseSensor initialization failed, - likely because our Arduino platform interrupts - aren't supported yet. - - If your Sketch hangs here, try changing USE_PS_INTERRUPT to false. - */ - for(;;) { - // Flash the led to show things didn't work. - digitalWrite(PULSE_BLINK, LOW); - delay(50); - digitalWrite(PULSE_BLINK, HIGH); - delay(50); - } - } -} - -void loop() { - - /* - See if a sample is ready from the PulseSensor. - - If USE_INTERRUPTS is true, the PulseSensor Playground - will automatically read and process samples from - the PulseSensor. - - If USE_INTERRUPTS is false, this call to sawNewSample() - will, if enough time has passed, read and process a - sample (analog voltage) from the PulseSensor. - */ - if (pulseSensor.sawNewSample()) { - /* - Every so often, send the latest Sample. - We don't print every sample, because our baud rate - won't support that much I/O. - */ - if (--samplesUntilReport == (byte) 0) { - samplesUntilReport = SAMPLES_PER_SERIAL_SAMPLE; - - pulseSensor.outputSample(); - - /* - At about the beginning of every heartbeat, - report the heart rate and inter-beat-interval. - */ - if (pulseSensor.sawStartOfBeat()) { - pulseSensor.outputBeat(); - } - } - - /******* - Here is a good place to add code that could take up - to a millisecond or so to run. - *******/ - } - - /****** - Don't add code here, because it could slow the sampling - from the PulseSensor. - ******/ -} diff --git a/examples/PulseSensor_PTT/PulseSensor_PTT.ino b/examples/PulseSensor_Pulse_Transit_Time/PulseSensor_Pulse_Transit_Time.ino similarity index 73% rename from examples/PulseSensor_PTT/PulseSensor_PTT.ino rename to examples/PulseSensor_Pulse_Transit_Time/PulseSensor_Pulse_Transit_Time.ino index f2e585d..491dc8b 100644 --- a/examples/PulseSensor_PTT/PulseSensor_PTT.ino +++ b/examples/PulseSensor_Pulse_Transit_Time/PulseSensor_Pulse_Transit_Time.ino @@ -3,11 +3,15 @@ and measures the time between! This can be used to derive Pulse Transit Time (PTT) - Here is a link to the PTT tutorial + Here is a link to the Pulse Transit Time tutorial https://pulsesensor.com/pages/pulse-transit-time Using two PulseSensors, connect the one on A0 closer to your heart. Connect the one on A1 at a point further from your heart. + Check out the PulseSensor Playground Tools for explaination + of all user functions and directives. + https://github.com/WorldFamousElectronics/PulseSensorPlayground/blob/master/resources/PulseSensor%20Playground%20Tools.md + Copyright World Famous Electronics LLC - see LICENSE Contributors: Joel Murphy, https://pulsesensor.com @@ -21,25 +25,13 @@ */ /* - Every Sketch that uses the PulseSensor Playground must - define USE_ARDUINO_INTERRUPTS before including PulseSensorPlayground.h. - Here, #define USE_ARDUINO_INTERRUPTS false tells the library to - not use interrupts to read data from the PulseSensor. - - If you want to use interrupts, simply change the line below - to read: - #define USE_ARDUINO_INTERRUPTS true - - Set US_PS_INTERRUPTS to false if either - 1) Your Arduino platform's interrupts aren't yet supported - by PulseSensor Playground, or - 2) You don't wish to use interrupts because of the side effects. - - NOTE: if US_PS_INTERRUPTS is false, your Sketch must - call pulse.sawNewSample() at least once every 2 milliseconds - to accurately read the PulseSensor signal. + Include the PulseSensor Playground library to get all the good stuff! + The PulseSensor Playground library will decide whether to use + a hardware timer to get accurate sample readings by checking + what target hardware is being used and adjust accordingly. + You may see a "warning" come up in red during compilation + if a hardware timer is not being used. */ -#define USE_ARDUINO_INTERRUPTS true #include @@ -156,17 +148,50 @@ void setup() { } void loop() { - + /* + See if a sample is ready from the PulseSensor. + + If USE_HARDWARE_TIMER is true, the PulseSensor Playground + will automatically read and process samples from + the PulseSensor. + + If USE_HARDWARE_TIMER is false, the call to sawNewSample() + will check to see how much time has passed, then read + and process a sample (analog voltage) from the PulseSensor. + Call this function often to maintain 500Hz sample rate, + that is every 2 milliseconds. Best not to have any delay() + functions in the loop when using a software timer. + + Check the compatibility of your hardware at this link + + and delete the unused code portions in your saved copy, if you like. + */ +if(pulseSensor.UsingHardwareTimer){ /* Wait a bit. We don't output every sample, because our baud rate won't support that much I/O. */ - delay(20); - + delay(20); // write the latest sample to Serial. pulseSensor.outputSample(); - +} else { +/* + When using a software timer, we have to check to see if it is time + to acquire another sample. A call to sawNewSample will do that. +*/ + if (pulseSensor.sawNewSample()) { + /* + Every so often, send the latest Sample. + We don't print every sample, because our baud rate + won't support that much I/O. + */ + if (--pulseSensor.samplesUntilReport == (byte) 0) { + pulseSensor.samplesUntilReport = SAMPLES_PER_SERIAL_SAMPLE; + pulseSensor.outputSample(); + } + } +} /* If a beat has happened on a given PulseSensor since we last checked, write the per-beat information diff --git a/examples/PulseSensor_RP2040/PulseSensor_RP2040.ino b/examples/PulseSensor_RP2040/PulseSensor_RP2040.ino deleted file mode 100644 index d621a38..0000000 --- a/examples/PulseSensor_RP2040/PulseSensor_RP2040.ino +++ /dev/null @@ -1,173 +0,0 @@ -/* - Code to detect pulses from the PulseSensor, - using an interrupt service routine. - - This code is designed to target an RP2040 microcontroller - You may need to ajust the number in the RPI_PICO_Timer parameter, maybe? - If you have trouble, please post an issue on github - - Mostly works. Tested on: - Adafruit Feather RP2040 - Raspberry Pi Pico H - - - Copyright World Famous Electronics LLC - see LICENSE - Contributors: - Joel Murphy, https://pulsesensor.com - Yury Gitman, https://pulsesensor.com - Bradford Needham, @bneedhamia, https://bluepapertech.com - - Licensed under the MIT License, a copy of which - should have been included with this software. - - This software is not intended for medical use. -*/ - - -/* - * The TimerInterrupt library - * https://github.com/khoih-prog/RPI_PICO_TimerInterrupt - * Set the sample rate to 500Hz - * Keep the name sampleTimer! Don't change it, cause it's used in the library! - */ - -#include "RPi_Pico_TimerInterrupt.h" -#define SAMPLE_INTERVAL_US 2000L -RPI_PICO_Timer sampleTimer(0); // the paramater may need to change, depending? - -/* - Every Sketch that uses the PulseSensor Playground must - define USE_ARDUINO_INTERRUPTS before including PulseSensorPlayground.h. - Here, #define USE_ARDUINO_INTERRUPTS true tells the library to use - interrupts to automatically read and process PulseSensor data. - - See PulseSensor_BPM_Alternative.ino for an example of not using interrupts. -*/ -#define USE_ARDUINO_INTERRUPTS true -#include - -/* - * Declare the interrupt service routine - * This will be used in setup as the interrupt callback - */ -boolean sampleTimer_ISR(struct repeating_timer *t){ - (void) t; - PulseSensorPlayground::OurThis->onSampleTime(); - return true; -} - -/* - The format of our output. - - Set this to PROCESSING_VISUALIZER if you're going to run - the Processing Visualizer Sketch. - See https://github.com/WorldFamousElectronics/PulseSensor_Amped_Processing_Visualizer - - Set this to SERIAL_PLOTTER if you're going to run - the Arduino IDE's Serial Plotter. -*/ -const int OUTPUT_TYPE = SERIAL_PLOTTER; - -/* - Pinout: - PULSE_INPUT = Analog Input. Connected to the pulse sensor - purple (signal) wire. - PULSE_BLINK = digital Output. Connected to an LED (and 1K series resistor) - that will flash on each detected pulse. - PULSE_FADE = digital Output. PWM pin onnected to an LED (and 1K series resistor) - that will smoothly fade with each pulse. - NOTE: PULSE_FADE must be a pin that supports PWM. Do not use - pin 9 or 10, because those pins' PWM interferes with the sample timer. - THRESHOLD should be set higher than the PulseSensor signal idles - at when there is nothing touching it. The expected idle value - should be 512, which is 1/2 of the ADC range. To check the idle value - open a serial monitor and make note of the PulseSensor signal values - with nothing touching the sensor. THRESHOLD should be a value higher - than the range of idle noise by 25 to 50 or so. When the library - is finding heartbeats, the value is adjusted based on the pulse signal - waveform. THRESHOLD sets the default when there is no pulse present. - Adjust as neccesary. -*/ -const int PULSE_INPUT = A0; -const int PULSE_BLINK = LED_BUILTIN; -const int PULSE_FADE = 7; -const int THRESHOLD = 550; // Adjust this number to avoid noise when idle - -/* - All the PulseSensor Playground functions. -*/ -PulseSensorPlayground pulseSensor; - -void setup() { - /* - Use 115200 baud because that's what the Processing Sketch expects to read, - and because that speed provides about 11 bytes per millisecond. - - If we used a slower baud rate, we'd likely write bytes faster than - they can be transmitted, which would mess up the timing - of readSensor() calls, which would make the pulse measurement - not work properly. - */ - Serial.begin(115200); -// Stream &myPort = (Stream &)Serial; - // Configure the PulseSensor manager. - - pulseSensor.analogInput(PULSE_INPUT); - pulseSensor.blinkOnPulse(PULSE_BLINK); - pulseSensor.fadeOnPulse(PULSE_FADE); - - pulseSensor.setSerial(Serial); - pulseSensor.setOutputType(OUTPUT_TYPE); - pulseSensor.setThreshold(THRESHOLD); - - analogReadResolution(10); - - // Now that everything is ready, start reading the PulseSensor signal. - if (!pulseSensor.begin()) { - /* - PulseSensor initialization failed, - likely because our particular Arduino platform interrupts - aren't supported yet. - - If your Sketch hangs here, try PulseSensor_BPM_Alternative.ino, - which doesn't use interrupts. - */ - for(;;) { - // Flash the led to show things didn't work. - digitalWrite(PULSE_BLINK, LOW); - delay(50); - Serial.println('!'); - digitalWrite(PULSE_BLINK, HIGH); - delay(50); - } - } - /* This starts the sample timer interrupt - * Use pause() and resume() to start and stop sampling on the fly - * Check Resources folder in the library for more tools - */ - if (!sampleTimer.attachInterruptInterval(SAMPLE_INTERVAL_US, sampleTimer_ISR)){ - Serial.println("Can't set ISR! Select another timer?"); - } - -} - - -void loop() { - /* - Wait a bit. - We don't output every sample, because our baud rate - won't support that much I/O. - */ - delay(20); - - // write the latest sample to Serial. - pulseSensor.outputSample(); - - /* - If a beat has happened since we last checked, - write the per-beat information to Serial. - */ - if (pulseSensor.sawStartOfBeat()) { - pulseSensor.outputBeat(); - } -} diff --git a/examples/PulseSensor_Servo/PulseSensor_Servo.ino b/examples/PulseSensor_Servo_Motor/PulseSensor_Servo_Motor.ino similarity index 62% rename from examples/PulseSensor_Servo/PulseSensor_Servo.ino rename to examples/PulseSensor_Servo_Motor/PulseSensor_Servo_Motor.ino index de61271..71d68fe 100644 --- a/examples/PulseSensor_Servo/PulseSensor_Servo.ino +++ b/examples/PulseSensor_Servo_Motor/PulseSensor_Servo_Motor.ino @@ -6,6 +6,10 @@ Here is a link to the tutorial https://pulsesensor.com/pages/pulse-sensor-servo-tutorial + Check out the PulseSensor Playground Tools for explaination + of all user functions and directives. + https://github.com/WorldFamousElectronics/PulseSensorPlayground/blob/master/resources/PulseSensor%20Playground%20Tools.md + Copyright World Famous Electronics LLC - see LICENSE Contributors: Joel Murphy, https://pulsesensor.com @@ -20,18 +24,18 @@ /* Include Servo.h BEFORE you include PusleSensorPlayground.h + PulseSensor Playground needs to know if you want to use a Servo. */ #include /* - Every Sketch that uses the PulseSensor Playground must - define USE_ARDUINO_INTERRUPTS before including PulseSensorPlayground.h. - Here, #define USE_ARDUINO_INTERRUPTS true tells the library to use - interrupts to automatically read and process PulseSensor data. - - See PulseSensor_BPM_Alternative.ino for an example of not using interrupts. + Include the PulseSensor Playground library to get all the good stuff! + The PulseSensor Playground library will decide whether to use + a hardware timer to get accurate sample readings by checking + what target hardware is being used and adjust accordingly. + You may see a "warning" come up in red during compilation + if a hardware timer is not being used. */ -#define USE_ARDUINO_INTERRUPTS true #include /* @@ -77,7 +81,8 @@ const int THRESHOLD = 550; // Adjust this number to avoid noise when idle PulseSensorPlayground pulseSensor; /* - Make a heart servo, the pin to control it with, and a servo position variable + Make a heart servo, the pin to control it with, + and a servo position variable */ Servo heart; const int SERVO_PIN = 6; @@ -130,27 +135,58 @@ void setup() { void loop() { /* - Wait a bit. - We don't output every sample, because our baud rate - won't support that much I/O. + See if a sample is ready from the PulseSensor. + + If USE_HARDWARE_TIMER is true, the PulseSensor Playground + will automatically read and process samples from + the PulseSensor. + + If USE_HARDWARE_TIMER is false, the call to sawNewSample() + will check to see how much time has passed, then read + and process a sample (analog voltage) from the PulseSensor. + Call this function often to maintain 500Hz sample rate, + that is every 2 milliseconds. Best not to have any delay() + functions in the loop when using a software timer. + + Check the compatibility of your hardware at this link + + and delete the unused code portions in your saved copy, if you like. */ - delay(20); - - // write the latest sample to Serial. - pulseSensor.outputSample(); - - // write the latest analog value to the heart servo - moveServo(pulseSensor.getLatestSample()); - + if(pulseSensor.UsingHardwareTimer){ + /* + Wait a bit. + We don't output every sample, because our baud rate + won't support that much I/O. + */ + delay(20); + // write the latest sample to Serial. + pulseSensor.outputSample(); + // write the latest analog value to the heart servo + moveServo(pulseSensor.getLatestSample()); + } else { /* - If a beat has happened since we last checked, - write the per-beat information to Serial. - */ - if (pulseSensor.sawStartOfBeat()) { - pulseSensor.outputBeat(); + When using a software timer, we have to check to see if it is time + to acquire another sample. A call to sawNewSample will do that. + */ + if (pulseSensor.sawNewSample()) { + /* + Every so often, send the latest Sample. + We don't print every sample, because our baud rate + won't support that much I/O. + */ + if (--pulseSensor.samplesUntilReport == (byte) 0) { + pulseSensor.samplesUntilReport = SAMPLES_PER_SERIAL_SAMPLE; + pulseSensor.outputSample(); + // write the latest analog value to the heart servo + moveServo(pulseSensor.getLatestSample()); + (signal); + } + } } + } + /* Map the Pulse Sensor Signal to the Servo range Pulse Sensor = 0 <> 1023 diff --git a/examples/PulseSensor_Speaker/PulseSensor_Speaker.ino b/examples/PulseSensor_Speaker/PulseSensor_Speaker.ino index f2a7c99..59ecf0f 100644 --- a/examples/PulseSensor_Speaker/PulseSensor_Speaker.ino +++ b/examples/PulseSensor_Speaker/PulseSensor_Speaker.ino @@ -1,6 +1,5 @@ /* - Code to detect pulses from the PulseSensor, - using an interrupt service routine. + Code to detect pulses from the PulseSensor >>>> THIS EXAMPLE OUTPUTS USES TONE COMMAND <<<< >>>> TO MAKE A SPEAKER BEEP WITH HEARTBEAT! <<<< @@ -8,6 +7,10 @@ Here is a link to the tutorial https://pulsesensor.com/pages/pulse-sensor-speaker-tutorial + Check out the PulseSensor Playground Tools for explaination + of all user functions and directives. + https://github.com/WorldFamousElectronics/PulseSensorPlayground/blob/master/resources/PulseSensor%20Playground%20Tools.md + Copyright World Famous Electronics LLC - see LICENSE Contributors: Joel Murphy, https://pulsesensor.com @@ -21,14 +24,13 @@ */ /* - Every Sketch that uses the PulseSensor Playground must - define USE_ARDUINO_INTERRUPTS before including PulseSensorPlayground.h. - Here, #define USE_ARDUINO_INTERRUPTS true tells the library to use - interrupts to automatically read and process PulseSensor data. - - See PulseSensor_BPM_Alternative.ino for an example of not using interrupts. + Include the PulseSensor Playground library to get all the good stuff! + The PulseSensor Playground library will decide whether to use + a hardware timer to get accurate sample readings by checking + what target hardware is being used and adjust accordingly. + You may see a "warning" come up in red during compilation + if a hardware timer is not being used. */ -#define USE_ARDUINO_INTERRUPTS true #include /* @@ -80,7 +82,7 @@ PulseSensorPlayground pulseSensor; Then connect - side of electrolytic capacitor to GND. Capacitor value should be 1uF or higher! Follow this tutorial: - [link] + https://pulsesensor.com/pages/pulse-sensor-speaker-tutorial */ const int PIN_SPEAKER = 2; // speaker on pin2 makes a beep with heartbeat @@ -129,14 +131,49 @@ void setup() { void loop() { /* - Wait a bit. - We don't output every sample, because our baud rate - won't support that much I/O. + See if a sample is ready from the PulseSensor. + + If USE_HARDWARE_TIMER is true, the PulseSensor Playground + will automatically read and process samples from + the PulseSensor. + + If USE_HARDWARE_TIMER is false, the call to sawNewSample() + will check to see how much time has passed, then read + and process a sample (analog voltage) from the PulseSensor. + Call this function often to maintain 500Hz sample rate, + that is every 2 milliseconds. Best not to have any delay() + functions in the loop when using a software timer. + + Check the compatibility of your hardware at this link + + and delete the unused code portions in your saved copy, if you like. */ - delay(20); - - // write the latest sample to Serial. - pulseSensor.outputSample(); + if(pulseSensor.UsingHardwareTimer){ + /* + Wait a bit. + We don't output every sample, because our baud rate + won't support that much I/O. + */ + delay(20); + // write the latest sample to Serial. + pulseSensor.outputSample(); + } else { + /* + When using a software timer, we have to check to see if it is time + to acquire another sample. A call to sawNewSample will do that. + */ + if (pulseSensor.sawNewSample()) { + /* + Every so often, send the latest Sample. + We don't print every sample, because our baud rate + won't support that much I/O. + */ + if (--pulseSensor.samplesUntilReport == (byte) 0) { + pulseSensor.samplesUntilReport = SAMPLES_PER_SERIAL_SAMPLE; + pulseSensor.outputSample(); + } + } + } /* If a beat has happened since we last checked, diff --git a/examples/PulseSensor_BPM_UNO_R4_LEDmatrix_Heartbeat/PulseSensor_BPM_UNO_R4_LEDmatrix_Heartbeat.ino b/examples/PulseSensor_UNO_R4_WiFi_LEDmatrix_Heartbeat/PulseSensor_UNO_R4_WiFi_LEDmatrix_Heartbeat.ino similarity index 68% rename from examples/PulseSensor_BPM_UNO_R4_LEDmatrix_Heartbeat/PulseSensor_BPM_UNO_R4_LEDmatrix_Heartbeat.ino rename to examples/PulseSensor_UNO_R4_WiFi_LEDmatrix_Heartbeat/PulseSensor_UNO_R4_WiFi_LEDmatrix_Heartbeat.ino index bb16d91..ccc7ade 100644 --- a/examples/PulseSensor_BPM_UNO_R4_LEDmatrix_Heartbeat/PulseSensor_BPM_UNO_R4_LEDmatrix_Heartbeat.ino +++ b/examples/PulseSensor_UNO_R4_WiFi_LEDmatrix_Heartbeat/PulseSensor_UNO_R4_WiFi_LEDmatrix_Heartbeat.ino @@ -1,9 +1,10 @@ /* - Code to detect pulses from the PulseSensor, - using an interrupt service routine. + Code to target the UNO R4 WiFi board. + This example will beat a heart shape on the LED matrix! ->>>> This example targest the Arduino UNO R4 WiFi. ->>>> It will pulse the Arduino heart animation on the LED matrix to the time of your heartbeat! + Check out the PulseSensor Playground Tools for explaination + of all user functions and directives. + https://github.com/WorldFamousElectronics/PulseSensorPlayground/blob/master/resources/PulseSensor%20Playground%20Tools.md Copyright World Famous Electronics LLC - see LICENSE Contributors: @@ -18,31 +19,15 @@ */ /* - We use the FspTimer to setup a timer interrupt for sample acquisition - FspTimer is part of the hardware core files for the UNO R4 + Include the PulseSensor Playground library to get all the good stuff! + The PulseSensor Playground library will decide whether to use + a hardware timer to get accurate sample readings by checking + what target hardware is being used and adjust accordingly. + You will see a warning during compilation that notes if + a hardware timer is being used or not. */ -#include "FspTimer.h" -FspTimer sampleTimer; - - -/* - Every Sketch that uses the PulseSensor Playground must - define USE_ARDUINO_INTERRUPTS before including PulseSensorPlayground.h. - Here, #define USE_ARDUINO_INTERRUPTS true tells the library to use - interrupts to automatically read and process PulseSensor data. - - See PulseSensorBPM_Alternative.ino for an example of not using interrupts. -*/ -#define USE_ARDUINO_INTERRUPTS true #include -/* - This is the timer interrupt service routine where we acquire and process samples -*/ -void sampleTimerISR(timer_callback_args_t __attribute((unused)) *p_args){ - PulseSensorPlayground::OurThis->onSampleTime(); -} - /* The format of our output. @@ -152,32 +137,6 @@ void setup() { } } -/* - We have to get control of a timer on the UNO R4. First, we try and see if there are any free timers available. - If there are no free timers available, we will just take control of one from some other purpose. - We shouldn't have to force things, but if you use alot of timers, beware of this force use code! - You can check to see if you are forcing by un-commenting the "forcing timer get" print line. - You can check to see what timer you have under your control by un-commenting the "got timer " print line. -*/ - uint8_t timer_type = GPT_TIMER; - int8_t tindex = FspTimer::get_available_timer(timer_type); - if(tindex == 0){ - // Serial.println("forcing timer get;") - FspTimer::force_use_of_pwm_reserved_timer(); - tindex = FspTimer::get_available_timer(timer_type); - } - // Serial.print("got timer "); Serial.println(tindex); - -/* - sampleTimer.begin sets up the timer that we just got control of as a periodic timer with 500Hz frequency. - It also passes the interrupt service routine that we made above. - SAMPLE_RATE_500HZ is defined in the PulseSensorPlayground.h file. -*/ - sampleTimer.begin(TIMER_MODE_PERIODIC, timer_type, tindex, SAMPLE_RATE_500HZ, 0.0f, sampleTimerISR); - sampleTimer.setup_overflow_irq(); - sampleTimer.open(); - sampleTimer.start(); - // start up the LED matrix so we can control it. beatingHeart.begin(); } diff --git a/examples/PulseSensor_BPM_UNO_R4_LEDmatrix_Plotter/PulseSensor_BPM_UNO_R4_LEDmatrix_Plotter.ino b/examples/PulseSensor_UNO_R4_WiFi_LEDmatrix_Plotter/PulseSensor_UNO_R4_WiFi_LEDmatrix_Plotter.ino similarity index 68% rename from examples/PulseSensor_BPM_UNO_R4_LEDmatrix_Plotter/PulseSensor_BPM_UNO_R4_LEDmatrix_Plotter.ino rename to examples/PulseSensor_UNO_R4_WiFi_LEDmatrix_Plotter/PulseSensor_UNO_R4_WiFi_LEDmatrix_Plotter.ino index 22222cf..8ab1583 100644 --- a/examples/PulseSensor_BPM_UNO_R4_LEDmatrix_Plotter/PulseSensor_BPM_UNO_R4_LEDmatrix_Plotter.ino +++ b/examples/PulseSensor_UNO_R4_WiFi_LEDmatrix_Plotter/PulseSensor_UNO_R4_WiFi_LEDmatrix_Plotter.ino @@ -1,13 +1,11 @@ /* - Code to detect pulses from the PulseSensor, - using an interrupt service routine. + Code to target the UNO R4 WiFi board. + This example will plot the PulseSensor signal + scaled to fit on the LED Matrix! ->>>> This example targest the Arduino UNO R4 WiFi. ->>>> It will plot the PulseSensor signal on the LED matrix of the UNO R4. ->>>> Use the delay() inside the loop to change the matrix frame rate. - - Here is a link to the tutorial - https://pulsesensor.com/pages/getting-advanced + Check out the PulseSensor Playground Tools for explaination + of all user functions and directives. + https://github.com/WorldFamousElectronics/PulseSensorPlayground/blob/master/resources/PulseSensor%20Playground%20Tools.md Copyright World Famous Electronics LLC - see LICENSE Contributors: @@ -22,31 +20,15 @@ */ /* - We use the FspTimer to setup a timer interrupt for sample acquisition - FspTimer is part of the hardware core files for the UNO R4 + Include the PulseSensor Playground library to get all the good stuff! + The PulseSensor Playground library will decide whether to use + a hardware timer to get accurate sample readings by checking + what target hardware is being used and adjust accordingly. + You will see a warning during compilation that notes if + a hardware timer is being used or not. */ -#include "FspTimer.h" -FspTimer sampleTimer; - - -/* - Every Sketch that uses the PulseSensor Playground must - define USE_ARDUINO_INTERRUPTS before including PulseSensorPlayground.h. - Here, #define USE_ARDUINO_INTERRUPTS true tells the library to use - interrupts to automatically read and process PulseSensor data. - - See PulseSensorBPM_Alternative.ino for an example of not using interrupts. -*/ -#define USE_ARDUINO_INTERRUPTS true #include -/* - This is the timer interrupt service routine where we acquire and process samples -*/ -void sampleTimerISR(timer_callback_args_t __attribute((unused)) *p_args){ - PulseSensorPlayground::OurThis->onSampleTime(); -} - /* The format of our output. @@ -148,33 +130,7 @@ void setup() { delay(50); } } - -/* - We have to get control of a timer on the UNO R4. First, we try and see if there are any free timers available. - If there are no free timers available, we will just take control of one from some other purpose. - We shouldn't have to force things, but if you use alot of timers, beware of this force use code! - You can check to see if you are forcing by un-commenting the "forcing timer get" print line. - You can check to see what timer you have under your control by un-commenting the "got timer " print line. -*/ - uint8_t timer_type = GPT_TIMER; - int8_t tindex = FspTimer::get_available_timer(timer_type); - if(tindex == 0){ - // Serial.println("forcing timer get;") - FspTimer::force_use_of_pwm_reserved_timer(); - tindex = FspTimer::get_available_timer(timer_type); - } - // Serial.print("got timer "); Serial.println(tindex); - -/* - sampleTimer.begin sets up the timer that we just got control of as a periodic timer with 500Hz frequency. - It also passes the interrupt service routine that we made above. - SAMPLE_RATE_500HZ is defined in the PulseSensorPlayground.h file. -*/ - sampleTimer.begin(TIMER_MODE_PERIODIC, timer_type, tindex, SAMPLE_RATE_500HZ, 0.0f, sampleTimerISR); - sampleTimer.setup_overflow_irq(); - sampleTimer.open(); - sampleTimer.start(); - + // start up the LED matrix so we can control it. plotter.begin(); } diff --git a/examples/PulseSensor_nRF52/PulseSensor_nRF52.ino b/examples/PulseSensor_nRF52/PulseSensor_nRF52.ino deleted file mode 100644 index 1f167ac..0000000 --- a/examples/PulseSensor_nRF52/PulseSensor_nRF52.ino +++ /dev/null @@ -1,174 +0,0 @@ -/* - Code to detect pulses from the PulseSensor, - using an interrupt service routine. - - This example is made to target boards in the nRF52 family. - Install the dependent library. Go to Sketch > Include Library > Mange Libraries. - When the Library Manager loads, search for NRF52_TimerInterrupt, if you are using - an Adafruit or Seeed nRF52 platform. - If you are using MBED, like the Nano 33 BLE, search for NRF52_MBED_TimerInterrupt. - - Install the latest version. - - This is a prototype version of PulseSensor_nRF52.ino use at your own risk. - - Copyright World Famous Electronics LLC - see LICENSE - Contributors: - Joel Murphy, https://pulsesensor.com - Yury Gitman, https://pulsesensor.com - Bradford Needham, @bneedhamia, https://bluepapertech.com - - Licensed under the MIT License, a copy of which - should have been included with this software. - - This software is not intended for medical use. -*/ - -/* - If you are using an Adafruit or Seeed nRF52 platform, - uncomment the next line to use the NRF52TimerInterrupt library -*/ -#include "NRF52TimerInterrupt.h" - -/* - If you are using an MBED nRF52 platform, like Nano 33 BLE, - uncomment the next lines to use the NRF52_MBED_TimerInterrupt library -*/ -// #include -// #include -// #include -// #include - -#define TIMER3_INTERVAL_US 2000 // critical fine tuning here! - -/* - If you are using an Adafruit or Seeed nRF52 platform, - uncomment the next line to use the NRF52TimerInterrupt library -*/ -NRF52Timer sampleTimer(NRF_TIMER_3); - -/* - If you are using an MBED nRF52 platform, like Nano 33 BLE, - uncomment the next lines to use the NRF52_MBED_TimerInterrupt library -*/ -// NRF52_MBED_Timer sampleTimer(NRF_TIMER_3); - -/* - Every Sketch that uses the PulseSensor Playground must - define USE_ARDUINO_INTERRUPTS before including PulseSensorPlayground.h. - Here, #define USE_ARDUINO_INTERRUPTS true tells the library to use - interrupts to automatically read and process PulseSensor data. - - See PulseSensor_BPM_Alternative.ino for an example of not using interrupts. -*/ -#define USE_ARDUINO_INTERRUPTS true -#include - -void Timer3_ISR(){ - PulseSensorPlayground::OurThis->onSampleTime(); -} -/* - The format of our output. - - Set this to PROCESSING_VISUALIZER if you're going to run - the Processing Visualizer Sketch. - See https://github.com/WorldFamousElectronics/PulseSensor_Amped_Processing_Visualizer - - Set this to SERIAL_PLOTTER if you're going to run - the Arduino IDE's Serial Plotter. -*/ -const int OUTPUT_TYPE = SERIAL_PLOTTER; - -/* - Pinout: - PULSE_INPUT = Analog Input. Connected to the pulse sensor - purple (signal) wire. - PULSE_BLINK = digital Output. Connected to an LED (and 1K series resistor) - that will flash on each detected pulse. - PULSE_FADE = digital Output. PWM pin onnected to an LED (and 1K series resistor) - that will smoothly fade with each pulse. - NOTE: PULSE_FADE must be a pin that supports PWM. Do not use - pin 9 or 10, because those pins' PWM interferes with the sample timer. - THRESHOLD should be set higher than the PulseSensor signal idles - at when there is nothing touching it. The expected idle value - should be 512, which is 1/2 of the ADC range. To check the idle value - open a serial monitor and make note of the PulseSensor signal values - with nothing touching the sensor. THRESHOLD should be a value higher - than the range of idle noise by 25 to 50 or so. When the library - is finding heartbeats, the value is adjusted based on the pulse signal - waveform. THRESHOLD sets the default when there is no pulse present. - Adjust as neccesary. -*/ -const int PULSE_INPUT = A0; -const int PULSE_BLINK = 13; -const int PULSE_FADE = 12; -const int THRESHOLD = 550; // Adjust this number to avoid noise when idle - -/* - All the PulseSensor Playground functions. -*/ -PulseSensorPlayground pulseSensor; - -void setup() { - /* - 115200 provides about 11 bytes per millisecond. - If we used a slower baud rate, we'd likely write bytes faster than - they can be transmitted,. - */ - Serial.begin(115200); - while (!Serial && millis() < 5000); - - // Configure the PulseSensor manager. - - pulseSensor.analogInput(PULSE_INPUT); - pulseSensor.blinkOnPulse(PULSE_BLINK); - pulseSensor.fadeOnPulse(PULSE_FADE); - pulseSensor.setThreshold(THRESHOLD); - - pulseSensor.setSerial(Serial); - pulseSensor.setOutputType(OUTPUT_TYPE); - - // Now that everything is ready, start reading the PulseSensor signal. - if (!pulseSensor.begin()) { - /* - PulseSensor initialization failed, - likely because our particular Arduino platform interrupts - aren't supported yet. - - If your Sketch hangs here, try PulseSensor_BPM_Alternative.ino, - which doesn't use interrupts. - */ - for(;;) { - // Flash the led to show things didn't work. - digitalWrite(PULSE_BLINK, LOW); - delay(50); - digitalWrite(PULSE_BLINK, HIGH); - delay(50); - } - } - if (sampleTimer.attachInterruptInterval(TIMER3_INTERVAL_US, Timer3_ISR)){ - Serial.println(F("Starting Timer 3")); - } else { - Serial.println(F("Timer 3 Startup failed!")); - } -} - -void loop() { - /* - Wait a bit. - We don't output every sample, because our baud rate - won't support that much I/O. - */ - delay(20); - - // write the latest sample to Serial. - pulseSensor.outputSample(); - - /* - If a beat has happened since we last checked, - write the per-beat information to Serial. - */ - if (pulseSensor.sawStartOfBeat()) { - pulseSensor.outputBeat(); - } -} \ No newline at end of file diff --git a/examples/PulseSensor_nRF52840_Feather_Express/PulseSensor_nRF52840_Feather_Express.ino b/examples/PulseSensor_nRF52_Heartrate_Monitor_Service/PulseSensor_nRF52_Heartrate_Monitor_Service.ino similarity index 80% rename from examples/PulseSensor_nRF52840_Feather_Express/PulseSensor_nRF52840_Feather_Express.ino rename to examples/PulseSensor_nRF52_Heartrate_Monitor_Service/PulseSensor_nRF52_Heartrate_Monitor_Service.ino index 4c614b2..2f80727 100644 --- a/examples/PulseSensor_nRF52840_Feather_Express/PulseSensor_nRF52840_Feather_Express.ino +++ b/examples/PulseSensor_nRF52_Heartrate_Monitor_Service/PulseSensor_nRF52_Heartrate_Monitor_Service.ino @@ -1,44 +1,13 @@ /* - Code to detect pulses from the PulseSensor, - using an interrupt service routine. + This code is written to target the following hardrware: + Adafruit nRF52 boards + Seeed Studio nRF52 boards (NO EMBED) + Embed board architecture will be supported in the future. - This example is designed to target the Adafruit nRF52840 Feather Express. + Check out the PulseSensor Playground Tools for explaination + of all user functions and directives. + https://github.com/WorldFamousElectronics/PulseSensorPlayground/blob/master/resources/PulseSensor%20Playground%20Tools.md - Install the dependent library. Go to Sketch > Include Library > Mange Libraries. - When the Library Manager loads, search for NRF52_TimerInterrupt. - Install the latest version. - - Download the nRF Toobox App from Nordic Semiconductor to your phone or tablet. - Open the app and select Heart Rate Service, then connect to PulseSensor to view BPM graph. - - Copyright World Famous Electronics LLC - see LICENSE - Contributors: - Joel Murphy, https://pulsesensor.com - Yury Gitman, https://pulsesensor.com - Bradford Needham, @bneedhamia, https://bluepapertech.com - - Licensed under the MIT License, a copy of which - should have been included with this software. - - This software is not intended for medical use. -*/ - -/* - The NRF52TimerInterrupt library will set up a hardware timer to trigger - sampling of the PulseSensor Playground library. - Define the sample interval in microseconds. -*/ -#include "NRF52TimerInterrupt.h" -#define SAMPLE_INTERVAL_US 2000 - -/* - Create an instance of the timer class called Sample_Timer. - NRF_TIMER_0 and NRF_TIMER_1 can be used by the core - and the bluetool library, so avoid them. -*/ -NRF52Timer Sample_Timer(NRF_TIMER_3); - -/* Include the Adafruit bluefruit library The target hardware will play the role of Server The phone or tablet will play the role of Client @@ -73,19 +42,15 @@ uint8_t bpm = 0; BLEDis bledis; /* - Every Sketch that uses the PulseSensor Playground must - define USE_ARDUINO_INTERRUPTS before including PulseSensorPlayground.h. - Here, #define USE_ARDUINO_INTERRUPTS true tells the library to use - interrupts to automatically read and process PulseSensor data. - - See PulseSensor_BPM_Alternative.ino for an example of not using interrupts. + Include the PulseSensor Playground library to get all the good stuff! + The PulseSensor Playground library will decide whether to use + a hardware timer to get accurate sample readings by checking + what target hardware is being used and adjust accordingly. + You may see a "warning" come up in red during compilation + if a hardware timer is not being used. */ -#define USE_ARDUINO_INTERRUPTS true #include -void Timer3_ISR(){ - PulseSensorPlayground::OurThis->onSampleTime(); -} /* The format of our output. @@ -184,16 +149,6 @@ void setup() { startAdvertising(); Serial.println("Advertising\nConnect via Bluetooth to PulseSensor to view BPM"); -/* - Start the interrupt timer at the end of setup - so it does not interfere with other setup stuff -*/ - if (Sample_Timer.attachInterruptInterval(SAMPLE_INTERVAL_US, Timer3_ISR)){ - Serial.println(F("Starting Timer 3")); - } else { - Serial.println(F("Timer 3 Startup failed!")); - } - } // end of setup() void loop() { diff --git a/examples/SoftwareSerialDemo/SoftwareSerialDemo.ino b/examples/SoftwareSerialDemo/SoftwareSerialDemo.ino index 5fb1c86..a587af6 100644 --- a/examples/SoftwareSerialDemo/SoftwareSerialDemo.ino +++ b/examples/SoftwareSerialDemo/SoftwareSerialDemo.ino @@ -1,10 +1,10 @@ /* Code to detect pulses from the PulseSensor, - using an interrupt service routine and using SoftwareSerial for Serial output. - Here is a link to the tutorial that discusses this coe - https://pulsesensor.com/pages/getting-advanced + Check out the PulseSensor Playground Tools for explaination + of all user functions and directives. + https://github.com/WorldFamousElectronics/PulseSensorPlayground/blob/master/resources/PulseSensor%20Playground%20Tools.md Copyright World Famous Electronics LLC - see LICENSE Contributors: @@ -17,8 +17,14 @@ This software is not intended for medical use. */ - -#define USE_ARDUINO_INTERRUPTS true // we want the Playground to use interrupts +/* + Include the PulseSensor Playground library to get all the good stuff! + The PulseSensor Playground library will decide whether to use + a hardware timer to get accurate sample readings by checking + what target hardware is being used and adjust accordingly. + You may see a "warning" come up in red during compilation + if a hardware timer is not being used. +*/ #include #include @@ -26,11 +32,11 @@ The format of our output. Set this to PROCESSING_VISUALIZER if you're going to run - the Processing Visualizer Sketch. - See https://github.com/WorldFamousElectronics/PulseSensor_Amped_Processing_Visualizer + the Processing Visualizer Sketch. + See https://github.com/WorldFamousElectronics/PulseSensor_Amped_Processing_Visualizer Set this to SERIAL_PLOTTER if you're going to run - the Arduino IDE's Serial Plotter. + the Arduino IDE's Serial Plotter. */ const int OUTPUT_TYPE = SERIAL_PLOTTER; @@ -111,11 +117,53 @@ void setup() { } void loop() { - delay(20); - - pulseSensor.outputSample(); + /* + See if a sample is ready from the PulseSensor. + + If USE_HARDWARE_TIMER is true, the PulseSensor Playground + will automatically read and process samples from + the PulseSensor. + + If USE_HARDWARE_TIMER is false, the call to sawNewSample() + will check to see how much time has passed, then read + and process a sample (analog voltage) from the PulseSensor. + Call this function often to maintain 500Hz sample rate, + that is every 2 milliseconds. Best not to have any delay() + functions in the loop when using a software timer. + + Check the compatibility of your hardware at this link + + and delete the unused code portions in your saved copy, if you like. + */ + if(pulseSensor.UsingHardwareTimer){ + /* + Wait a bit. + We don't output every sample, because our baud rate + won't support that much I/O. + */ + delay(20); + // write the latest sample to Serial. + pulseSensor.outputSample(); + } else { + /* + When using a software timer, we have to check to see if it is time + to acquire another sample. A call to sawNewSample will do that. + */ + if (pulseSensor.sawNewSample()) { + /* + Every so often, send the latest Sample. + We don't print every sample, because our baud rate + won't support that much I/O. + */ + if (--pulseSensor.samplesUntilReport == (byte) 0) { + pulseSensor.samplesUntilReport = SAMPLES_PER_SERIAL_SAMPLE; + pulseSensor.outputSample(); + } + } + } if (pulseSensor.sawStartOfBeat()) { pulseSensor.outputBeat(); } + } diff --git a/examples/TwoPulseSensors_On_OneArduino/TwoPulseSensors_On_OneArduino.ino b/examples/TwoPulseSensors_On_OneArduino/TwoPulseSensors_On_OneArduino.ino index 37737c2..c7ab453 100644 --- a/examples/TwoPulseSensors_On_OneArduino/TwoPulseSensors_On_OneArduino.ino +++ b/examples/TwoPulseSensors_On_OneArduino/TwoPulseSensors_On_OneArduino.ino @@ -1,9 +1,13 @@ /* Arduino Sketch to detect pulses from two PulseSensors. - + This sketch will communicate to a Processing sketch on your computor. Here is a link to the tutorial https://pulsesensor.com/pages/two-or-more-pulse-sensors + Check out the PulseSensor Playground Tools for explaination + of all user functions and directives. + https://github.com/WorldFamousElectronics/PulseSensorPlayground/blob/master/resources/PulseSensor%20Playground%20Tools.md + Copyright World Famous Electronics LLC - see LICENSE Contributors: Joel Murphy, https://pulsesensor.com @@ -17,25 +21,13 @@ */ /* - Every Sketch that uses the PulseSensor Playground must - define USE_ARDUINO_INTERRUPTS before including PulseSensorPlayground.h. - Here, #define USE_ARDUINO_INTERRUPTS false tells the library to - not use interrupts to read data from the PulseSensor. - - If you want to use interrupts, simply change the line below - to read: - #define USE_ARDUINO_INTERRUPTS true - - Set US_PS_INTERRUPTS to false if either - 1) Your Arduino platform's interrupts aren't yet supported - by PulseSensor Playground, or - 2) You don't wish to use interrupts because of the side effects. - - NOTE: if US_PS_INTERRUPTS is false, your Sketch must - call pulse.sawNewSample() at least once every 2 milliseconds - to accurately read the PulseSensor signal. + Include the PulseSensor Playground library to get all the good stuff! + The PulseSensor Playground library will decide whether to use + a hardware timer to get accurate sample readings by checking + what target hardware is being used and adjust accordingly. + You may see a "warning" come up in red during compilation + if a hardware timer is not being used. */ -#define USE_ARDUINO_INTERRUPTS true #include @@ -146,17 +138,50 @@ void setup() { } void loop() { - /* - Wait a bit. - We don't output every sample, because our baud rate - won't support that much I/O. + See if a sample is ready from the PulseSensor. + + If USE_HARDWARE_TIMER is true, the PulseSensor Playground + will automatically read and process samples from + the PulseSensor. + + If USE_HARDWARE_TIMER is false, the call to sawNewSample() + will check to see how much time has passed, then read + and process a sample (analog voltage) from the PulseSensor. + Call this function often to maintain 500Hz sample rate, + that is every 2 milliseconds. Best not to have any delay() + functions in the loop when using a software timer. + + Check the compatibility of your hardware at this link + + and delete the unused code portions in your saved copy, if you like. */ - delay(20); - - // write the latest sample to Serial. - pulseSensor.outputSample(); - + if(pulseSensor.UsingHardwareTimer){ + /* + Wait a bit. + We don't output every sample, because our baud rate + won't support that much I/O. + */ + delay(20); + // write the latest sample to Serial. + pulseSensor.outputSample(); + } else { + /* + When using a software timer, we have to check to see if it is time + to acquire another sample. A call to sawNewSample will do that. + */ + if (pulseSensor.sawNewSample()) { + /* + Every so often, send the latest Sample. + We don't print every sample, because our baud rate + won't support that much I/O. + */ + if (--pulseSensor.samplesUntilReport == (byte) 0) { + pulseSensor.samplesUntilReport = SAMPLES_PER_SERIAL_SAMPLE; + pulseSensor.outputSample(); + } + } + } /* If a beat has happened on a given PulseSensor since we last checked, write the per-beat information diff --git a/examples/TwoPulseSensors_On_OneArduino_Alternative/TwoPulseSensors_On_OneArduino_Alternative.ino b/examples/TwoPulseSensors_On_OneArduino_Alternative/TwoPulseSensors_On_OneArduino_Alternative.ino deleted file mode 100644 index f22f33e..0000000 --- a/examples/TwoPulseSensors_On_OneArduino_Alternative/TwoPulseSensors_On_OneArduino_Alternative.ino +++ /dev/null @@ -1,193 +0,0 @@ -/* - Arduino Sketch to detect pulses from two PulseSensors. - - Here is a link to the tutorial - https://pulsesensor.com/pages/two-or-more-pulse-sensors - - Copyright World Famous Electronics LLC - see LICENSE - Contributors: - Joel Murphy, https://pulsesensor.com - Yury Gitman, https://pulsesensor.com - Bradford Needham, @bneedhamia, https://bluepapertech.com - - Licensed under the MIT License, a copy of which - should have been included with this software. - - This software is not intended for medical use. -*/ - -/* - Every Sketch that uses the PulseSensor Playground must - define USE_ARDUINO_INTERRUPTS before including PulseSensorPlayground.h. - Here, #define USE_ARDUINO_INTERRUPTS false tells the library to - not use interrupts to read data from the PulseSensor. - - If you want to use interrupts, simply change the line below - to read: - #define USE_ARDUINO_INTERRUPTS true - - Set US_PS_INTERRUPTS to false if either - 1) Your Arduino platform's interrupts aren't yet supported - by PulseSensor Playground, or - 2) You don't wish to use interrupts because of the side effects. - - NOTE: if US_PS_INTERRUPTS is false, your Sketch must - call pulse.sawNewSample() at least once every 2 milliseconds - to accurately read the PulseSensor signal. -*/ -#define USE_ARDUINO_INTERRUPTS false -#include - - -/* - The format of our output. - - Set this to PROCESSING_VISUALIZER if you're going to run - the multi-sensor Processing Visualizer Sketch. - See https://github.com/WorldFamousElectronics/PulseSensorAmped_2_Sensors - - Set this to SERIAL_PLOTTER if you're going to run - the Arduino IDE's Serial Plotter. -*/ -const int OUTPUT_TYPE = SERIAL_PLOTTER; - -/* - Number of PulseSensor devices we're reading from. -*/ -const int PULSE_SENSOR_COUNT = 2; - -/* - Pinout: - PULSE_INPUT = Analog Input. Connected to the pulse sensor - purple (signal) wire. Ends with index number. - PULSE_BLINK = digital Output. Connected to an LED (and 1K series resistor) - that will flash on each detected pulse. Ends with index number. - PULSE_FADE = digital Output. PWM pin onnected to an LED (and 1K series resistor) - that will smoothly fade with each pulse. Ends with index number. - NOTE: PULSE_FADE must be a pin that supports PWM. Do not use - pin 9 or 10, because those pins' PWM interferes with the sample timer. Ends with index number. - THRESHOLD should be set higher than the PulseSensor signal idles - at when there is nothing touching it. The expected idle value - should be 512, which is 1/2 of the ADC range. To check the idle value - open a serial monitor and make note of the PulseSensor signal values - with nothing touching the sensor. THRESHOLD should be a value higher - than the range of idle noise by 25 to 50 or so. When the library - is finding heartbeats, the value is adjusted based on the pulse signal - waveform. THRESHOLD sets the default when there is no pulse present. - Adjust as neccesary. Ends with index number. -*/ -const int PULSE_INPUT0 = A0; -const int PULSE_BLINK0 = LED_BUILTIN; -const int PULSE_FADE0 = 5; - -const int PULSE_INPUT1 = A1; -const int PULSE_BLINK1 = 12; -const int PULSE_FADE1 = 11; - -const int THRESHOLD0 = 550; // Adjust this number to avoid noise when idle -const int THRESHOLD1 = 550; - -/* - samplesUntilReport = the number of samples remaining to read - until we want to report a sample over the serial connection. - - We want to report a sample value over the serial port - only once every 20 milliseconds (10 samples) to avoid - doing Serial output faster than the Arduino can send. -*/ -byte samplesUntilReport; -const byte SAMPLES_PER_SERIAL_SAMPLE = 10; - -/* - All the PulseSensor Playground functions. - We tell it how many PulseSensors we're using. -*/ -PulseSensorPlayground pulseSensor(PULSE_SENSOR_COUNT); - -void setup() { - /* - Use 250000 baud because that's what the Processing Sketch expects to read, - and because that speed provides about 25 bytes per millisecond, - or 50 characters per PulseSensor sample period of 2 milliseconds. - - If we used a slower baud rate, we'd likely write bytes faster than - they can be transmitted, which would mess up the timing - of readSensor() calls, which would make the pulse measurement - not work properly. - */ - Serial.begin(250000); - - /* - Configure the PulseSensor manager, - telling it which PulseSensor (0 or 1) - we're configuring. - */ - - pulseSensor.analogInput(PULSE_INPUT0, 0); - pulseSensor.blinkOnPulse(PULSE_BLINK0, 0); - pulseSensor.fadeOnPulse(PULSE_FADE0, 0); - pulseSensor.setThreshold(THRESHOLD0, 0); - - pulseSensor.analogInput(PULSE_INPUT1, 1); - pulseSensor.blinkOnPulse(PULSE_BLINK1, 1); - pulseSensor.fadeOnPulse(PULSE_FADE1, 1); - pulseSensor.setThreshold(THRESHOLD1, 1); - - pulseSensor.setSerial(Serial); - pulseSensor.setOutputType(OUTPUT_TYPE); - - // Skip the first SAMPLES_PER_SERIAL_SAMPLE in the loop(). - samplesUntilReport = SAMPLES_PER_SERIAL_SAMPLE; - - // Now that everything is ready, start reading the PulseSensor signal. - if (!pulseSensor.begin()) { - /* - PulseSensor initialization failed, - likely because our Arduino platform interrupts - aren't supported yet. - - If your Sketch hangs here, try changing USE_ARDUINO_INTERRUPTS to false. - */ - for (;;) { - // Flash the led to show things didn't work. - digitalWrite(PULSE_BLINK0, LOW); - delay(50); - digitalWrite(PULSE_BLINK0, HIGH); - delay(50); - } - } -} - -void loop() { - - if (pulseSensor.sawNewSample()) { - - if (--samplesUntilReport == (byte) 0) { - samplesUntilReport = SAMPLES_PER_SERIAL_SAMPLE; - - pulseSensor.outputSample(); - - /* - If a beat has happened on a given PulseSensor - since we last checked, write the per-beat information - about that PulseSensor to Serial. - */ - - for (int i = 0; i < PULSE_SENSOR_COUNT; ++i) { - if (pulseSensor.sawStartOfBeat(i)) { - pulseSensor.outputBeat(i); - } - } - } - - /******* - Here is a good place to add code that could take up - to a millisecond or so to run. - *******/ - } - - /****** - Don't add code here, because it could slow the sampling - from the PulseSensor. - ******/ -} diff --git a/keywords.txt b/keywords.txt index 1a935ce..4801996 100644 --- a/keywords.txt +++ b/keywords.txt @@ -33,6 +33,7 @@ getPulseAmplitude KEYWORD2 pause KEYWORD2 resume KEYWORD2 isPaused KEYWORD2 +UsingHardwareTimer KEYWORD2 ####################################### # Instances (KEYWORD2) diff --git a/library.properties b/library.properties index 981c458..5bbec75 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=PulseSensor Playground -version=1.9.1 +version=2.0.0 author=Joel Murphy, Yury Gitman, Brad Needham maintainer=Joel Murphy, Yury Gitman sentence=Support at PulseSensor.com diff --git a/resources/PulseSensor Playground Tools.md b/resources/PulseSensor Playground Tools.md index 6c075e4..e1b246a 100644 --- a/resources/PulseSensor Playground Tools.md +++ b/resources/PulseSensor Playground Tools.md @@ -14,29 +14,30 @@ You have to create an instance that can access all the PulseSensor Playground fu --- ### begin() -Start reading and processing data from the Pulse Sensor! +Start reading and processing data from the PulseSensor! Returns `true` when successfull and `false` if there is a problem. In our examples, if this function returns false, the program will hang, blink the LED and send '!' over the serial port. --- ### pause() -Turn off timer and stop reading and processing Pulse Sensor data. +Turn off timer and stop reading and processing PulseSensor data. Useful if you need to do other time sensitive things. Returns `true` when successful. --- ### resume() -Turn on timer and start reading and processing Pulse Sensor data. -Use this after calling pause() to resume Pulse Sensor. Returns `true` when successful. +Turn on timer and start reading and processing PUlseSensor data. +Use this after calling pause() to resume sampling PulseSensor. Returns `true` when successful. --- ### isPaused() -Retruns `true` while Pulse Sensor is paused, `false` while Pulse Sensor is running. +Retruns `true` while PulseSensor algorithgm is paused, `false` while it is running. --- ### sawNewSample() -Will return `true` if a new sample has been read. Type = boolean. +Will return `true` if a new sample has been read. This function is used to ensure software sample time +when not using a hardware timer. If a hardware timer is not being used, this function needs to be called often enough to ensure 500Hz sample rate (every 2mS). --- ### analogInput(int) -Set the pin your Pulse Sensor is connected to. +Set the pin your PulseSensor is connected to. --- ### blinkOnPulse(int) @@ -48,19 +49,19 @@ Set the pin to fade with you're heartbeat. Make sure the pin can do PWM! --- ### setSerial(Serial) -The Playground doesn't output serial data automatically. If you want to output serial pulse data, include this. If not, don't. +The Playground doesn't output serial data automatically. If you want the library to output serial pulse data, include this. If not, don't, and see the explication of the pre-processor directive below. --- ### setOutputType(int) -By default, Playground will output Serial data in `SERIAL_PLOTTER` format. You can change this to `PROCESSING_VISUALIZER` if you like. +By default, Playground will output Serial data in `SERIAL_PLOTTER` format. Change this to `PROCESSING_VISUALIZER` if you are going to use one of our Processing Visualizer Sketch examples. --- ### setThreshold(int) -Set a value that the Pulse Sensor signal has to cross when going up. Adjusting this can be useful to combat noise. +Set a value that the PulseSensor signal has to cross when going up. Adjusting this can be useful to combat noise. We set the default value at 550 --- ### getLatestSample() -Returns the most recently read analog value from the Pulse Sensor. Type = int. +Returns the most recently read analog value from the PulseSensor. Type = int. --- ### getBeatsPerMinute() @@ -72,49 +73,63 @@ Returns the latest inter-beat interval (IBI) in milliseconds. Type = int. --- ### getPulseAmplitude() -Returns the amplitude of the latest pulse wave. Value is computed from latest pulse wave peak minus latest pulse wave trough. Type = int. +Returns the amplitude of the latest pulse wave. Value is computed from analog value of latest pulse wave peak minus latest pulse wave trough. Type = int. --- ### getLastBeatTime() -Returns the sample number when the latest beat was found. The sample number is based on millis() and has a 2mS resolution. Type = unsigned long. +Returns the sample number when the latest beat was found. The sample number has a 2mS resolution. Type = unsigned long. --- ### sawStartOfBeat() -Returns `true` if a new heartbeat pulse has been detected. Type = boolean. +Returns `true` if a new heartbeat pulse has been detected. Type = bool. --- ### isInsideBeat() -Returns `true` for the time when a measured heartbeat wave is above the value set by the setThreshold() function, and `false` when it's not. Type = boolean. +Returns `true` for the time when a measured heartbeat wave is above the value set by the setThreshold() function, and `false` when it's not. --- ### outputSample() -Output the latest sample over the Serial port. If your Sketch wants to plot samples, it should call this function every so often. +Output the latest sample over the Serial port. The samples will either be formatted for the Arduino Serial Plotter, or one for our Processing Visualizer Sketches depending on the parameter set in setOutputType() above. In the case of `SERIAL_PLOTTER`, the library will print BPM, IBI, and PulseSensor raw signal. In the case of `PROCESSING_VISUALIZER`, the library will print just the raw PulseSensor value formatted for our Processing Visualizer Sketches. --- ### outputBeat() -Output the latest BPM and IBI values over the Serial port. +Output the latest BPM and IBI values over the Serial port. This function is only run when outputType is set to `PROCESSING_VISUALIZER`, and the output is formatted for our Processing Visualizer Sketches. --- ### outputToSerial(char, int) -Output Data with a character prefix. Used exclusively with the Pulse Sensor Processing Visualizer. Processing Visualizer needs to know what the prefix means in order to parse data from the serial stream. +Output Data with a character prefix. Used exclusively with the PulseSensor Processing Visualizer. Processing Visualizer needs to know what the prefix means in order to parse data from the serial stream. The characters we use are`S` for raw PulseSensor data, `B` for beats per minute, and `Q` for interbeat interval. --- -## Using Pulse Sensor Interrupts +## Notes On Sample Timing -We want to use interrupts if we can in our Pulse Sensor code in order to get super accurate hear rate data: Beats Per Minute (BPM), and Inter-beat Interval (IBI). In the example sketch [PulseSensor_BPM.ino](https://github.com/WorldFamousElectronics/PulseSensorPlayground/tree/master/examples/PulseSensor_BPM) there are a couple of lines of code at the very top of the sketch that set this up: - - #define USE_ARDUINO_INTERRUPTS true - #include - -While using interrupts is super cool and useful, it won't work with all of the myriad Arduino boards out there. If your board does not support interrupts yet, not to worry! We have code that works just as well without employing interrupts. It's called [PulseSensor_BPM_Alternative.ino](https://github.com/WorldFamousElectronics/PulseSensorPlayground/tree/master/examples/PulseSensor_BPM_Alternative). +Version 2.0.0 and up of the PulseSensor Playground library uses preprocessor directives to determine support for using a hardware timer or a software timer. We wan to use hardware timer if we can, but software timers work OK when you need to. If a software timer is used, the compiler will print a warning to the output terminal in Arduino IDE. In this case, the function `sawNewSample()` needs to be called often to ensure accurate sample timing. For a full list of supported and unsupported Arduino and Arduino compatible boards, please review this link --- ## Selecting Your Serial Output The sketch examples in PulseSensor Playground will output different kinds of serial info for visualizing your pulse waveform and Beats Per Minute data. Here's how you can choose which serial to use. -* Our basic example sketch called [GettingStartedProject.ino](https://github.com/WorldFamousElectronics/PulseSensorPlayground/tree/master/examples/GettingStartedProject) will output a serial stream of Pulse Sensor signal data only. The signal is read in the loop function about every 10mS. This works perfectly with the Arduino Serial Plotter. Follow this [Tutorial](https://pulsesensor.com/pages/code-and-guide) to give it a go. +* Our basic example sketch called [GettingStartedProject.ino](https://github.com/WorldFamousElectronics/PulseSensorPlayground/tree/master/examples/GettingStartedProject) will output a serial stream of PulseSensor signal data only. The signal is read in the loop function about every 10mS. This works perfectly with the Arduino Serial Plotter. Follow this [Tutorial](https://pulsesensor.com/pages/code-and-guide) to give it a go. + +* All the other example sketches that we have give you the option to output to the Arduino Serial Plotter, or to our [PulseSensor Visualizer](https://github.com/WorldFamousElectronics/PulseSensor_Amped_Processing_Visualizer) program. To select which one you want to output to, you will need to tell Arduino by setting the value of a variable called `OUTPUT_TYPE` at the top of the sketch. Here's an example that shows how to set the output to work with our Visualizer software. + +--- +## Preprocessor Directive Use + +The PulseSensor Playground library has a lot of functionality. It can handle serial messages, for example. But sometimes you might not want the library to manage the Serial port. If you don't want us to manage the Serial port, then you can tell the library that by changing one line in the PulseSensorPlayground.h file. When the library uses the Serial port, this line is commented out: + +`#define USE_SERIAL true` + +If you don't want us to use the Serial port, simply comment out the line above and uncomment the line: + +`#define USE_SERIAL false` + +You also will need to discard the `setSerial()` and `setOutputType()` functions. You can access the raw PulseSensor, BPM and IBI data, etc, by using calls to functions that return those values above, and print them on your own as you like. + +Other preprocessor directives that we have for users to adjust are -* All the other example sketches that we have give you the option to output to the Arduino Serial Plotter, or to our [Pulse Sensor Visualizer](https://github.com/WorldFamousElectronics/PulseSensor_Amped_Processing_Visualizer) program. To select which one you want to output to, you will need to tell Arduino by setting the value of a variable called `OUTPUT_TYPE` at the top of the sketch. Here's an example that shows how to set the output to work with our Visualizer software. +`PULSE_SENSOR_TIMING_ANALYSIS` +and +`PULSE_SENSOR_MEMORY_USAGE` -![outputType](https://github.com/WorldFamousElectronics/PulseSensorPlayground/blob/master/Images/outputType.png) +Please read all about them in the PulseSensorPlayground.h file! diff --git a/examples/PulseSensor_DUE/PulseSensor_DUE.ino b/resources/PulseSensor_V2_BPM_Prototype/PulseSensor_V2_BPM_Prototype.ino similarity index 58% rename from examples/PulseSensor_DUE/PulseSensor_DUE.ino rename to resources/PulseSensor_V2_BPM_Prototype/PulseSensor_V2_BPM_Prototype.ino index 4fc45cf..bb1852b 100644 --- a/examples/PulseSensor_DUE/PulseSensor_DUE.ino +++ b/resources/PulseSensor_V2_BPM_Prototype/PulseSensor_V2_BPM_Prototype.ino @@ -1,9 +1,13 @@ /* - Code to detect pulses from the PulseSensor, - using an interrupt service routine. - Here is a link to the tutorial\ - https://pulsesensor.com/pages/getting-advanced + This code is a prototype for v2 using a conditional check + on the UsingHardwareTimer variable in the library. + + Code to detect heartbeat pulses from the PulseSensor + + Check out the PulseSensor Playground Tools for explaination + of all user functions and directives. + https://github.com/WorldFamousElectronics/PulseSensorPlayground/blob/master/resources/PulseSensor%20Playground%20Tools.md Copyright World Famous Electronics LLC - see LICENSE Contributors: @@ -18,33 +22,15 @@ */ /* - Include the DueTimer library. If you don't have it, use library manager to get it. - You can also find it at https://github.com/ivanseidel/DueTimer - If you use the Servo library, probably want to include that before this inlude. Just sayin'... - This will grab the next available timer and call it sampleTimer for use throughout the code -*/ -#include -DueTimer sampleTimer = Timer.getAvailable(); - -/* - Every Sketch that uses the PulseSensor Playground must - define USE_ARDUINO_INTERRUPTS before including PulseSensorPlayground.h. - Here, #define USE_ARDUINO_INTERRUPTS true tells the library to use - interrupts to automatically read and process PulseSensor data. - - See PulseSensor_BPM_Alternative.ino for an example of not using interrupts. + Include the PulseSensor Playground library to get all the good stuff! + The PulseSensor Playground library will decide whether to use + a hardware timer to get accurate sample readings by checking + what target hardware is being used and adjust accordingly. + You may see a "warning" come up in red during compilation + if a hardware timer is not being used. */ -#define USE_ARDUINO_INTERRUPTS true #include -/* - * Declare the interrupt service routine - * This will be used in setup as the interrupt callback in attachInterrupt - */ -void sampleTimer_ISR(){ - PulseSensorPlayground::OurThis->onSampleTime(); -} - /* The format of our output. @@ -119,42 +105,68 @@ void setup() { If your Sketch hangs here, try PulseSensor_BPM_Alternative.ino, which doesn't use interrupts. */ + Serial.println("PulseSensor.begin() returned false!"); for(;;) { // Flash the led to show things didn't work. digitalWrite(PULSE_BLINK, LOW); - delay(50); Serial.println('!'); + delay(100); Serial.println('!'); digitalWrite(PULSE_BLINK, HIGH); - delay(50); + delay(100); } } - - /* This starts the sample timer interrupt. Do this last in the setup() routine. - * We are using Timer6 to avoid potential conflict with the servo library - * This timer selection could be better... - * Use pause() and resume() to start and stop sampling on the fly - * Check Resources folder in the library for more tools - */ - sampleTimer.attachInterrupt(sampleTimer_ISR); - sampleTimer.start(2000); // Calls every period microseconds - } void loop() { /* - Wait a bit. - We don't output every sample, because our baud rate - won't support that much I/O. + See if a sample is ready from the PulseSensor. + + If USE_HARDWARE_TIMER is true, the PulseSensor Playground + will automatically read and process samples from + the PulseSensor. + + If USE_HARDWARE_TIMER is false, the call to sawNewSample() + will check to see how much time has passed, then read + and process a sample (analog voltage) from the PulseSensor. + Call this function often to maintain 500Hz sample rate, + that is every 2 milliseconds. Best not to have any delay() + functions in the loop when using a software timer. + + Check the compatibility of your hardware at this link + + and delete the unused code portions in your saved copy, if you like. */ - delay(20); - - // write the latest sample to Serial. - pulseSensor.outputSample(); - + if(pulseSensor.UsingHardwareTimer){ + /* + Wait a bit. + We don't output every sample, because our baud rate + won't support that much I/O. + */ + delay(20); + // write the latest sample to Serial. + pulseSensor.outputSample(); + } else { + /* + When using a software timer, we have to check to see if it is time + to acquire another sample. A call to sawNewSample will do that. + */ + if (pulseSensor.sawNewSample()) { + /* + Every so often, send the latest Sample. + We don't print every sample, because our baud rate + won't support that much I/O. + */ + if (--pulseSensor.samplesUntilReport == (byte) 0) { + pulseSensor.samplesUntilReport = SAMPLES_PER_SERIAL_SAMPLE; + pulseSensor.outputSample(); + } + } + } /* If a beat has happened since we last checked, write the per-beat information to Serial. */ - if (pulseSensor.sawStartOfBeat()) { - pulseSensor.outputBeat(); - } + if (pulseSensor.sawStartOfBeat()) { + pulseSensor.outputBeat(); + } + } diff --git a/src/PulseSensorPlayground.cpp b/src/PulseSensorPlayground.cpp index 85d3e0d..b4df535 100644 --- a/src/PulseSensorPlayground.cpp +++ b/src/PulseSensorPlayground.cpp @@ -13,27 +13,40 @@ This software is not intended for medical use. */ -#include -// Define the "this" pointer for the ISR -PulseSensorPlayground *PulseSensorPlayground::OurThis; +#include +/* + Define the "this" pointer for the Inerrupt Service Routine, if used. + TimerHandler.h will define the Interrupt Service Rooutine + if hardware timer interrutps are used. + It is placed here so that happens only once. +*/ +#if USE_HARDWARE_TIMER + PulseSensorPlayground *PulseSensorPlayground::OurThis; +#include "utility/TimerHandler.h" +#endif PulseSensorPlayground::PulseSensorPlayground(int numberOfSensors) { // Save a static pointer to our playground so the ISR can read it. +#if USE_HARDWARE_TIMER OurThis = this; +#endif // Dynamically create the array to minimize ram usage. SensorCount = (byte) numberOfSensors; Sensors = new PulseSensor[SensorCount]; +// set our internal variable to reflect hardware timer use + UsingHardwareTimer = USE_HARDWARE_TIMER; + #if PULSE_SENSOR_TIMING_ANALYSIS // We want sample timing analysis, so we construct it. pTiming = new PulseSensorTimingStatistics(MICROS_PER_READ, 500 * 30L); #endif // PULSE_SENSOR_TIMING_ANALYSIS } -boolean PulseSensorPlayground::PulseSensorPlayground::begin() { +bool PulseSensorPlayground::PulseSensorPlayground::begin() { for (int i = 0; i < SensorCount; ++i) { Sensors[i].initializeLEDs(); @@ -46,24 +59,25 @@ boolean PulseSensorPlayground::PulseSensorPlayground::begin() { Paused = false; #if PULSE_SENSOR_MEMORY_USAGE - // Report the RAM usage and hang. + // Report the RAM usage printMemoryUsage(); - for (;;); + // for (;;); // optional hang. #endif // PULSE_SENSOR_MEMORY_USAGE -// #if defined (ARDUINO_ARCH_SAM) -// void sampleTimer(){ -// onSampleTime(); // PulseSensorPlayground::OurThis->onSampleTime(); -// } -// #endif - // Lastly, set up and turn on the interrupts. - if (UsingInterrupts) { - if (!PulseSensorPlaygroundSetupInterrupt()) { + if (UsingHardwareTimer) { + if (!setupInterrupt()) { Paused = true; return false; } } +/* + Uncomment the next line, and the other two references to timingPin + in this file as well as PulseSensorPlayground.h. + Connect the pin to an osilloscope to measure the run time + of the PulseSensor heartbeat finding algorithm. + This is useful to know the time it takes every sample. +*/ // pinMode(timingPin,OUTPUT); return true; } @@ -89,29 +103,31 @@ void PulseSensorPlayground::fadeOnPulse(int fadePin, int sensorIndex) { Sensors[sensorIndex].fadeOnPulse(fadePin); } -boolean PulseSensorPlayground::sawNewSample() { +bool PulseSensorPlayground::sawNewSample() { /* - If using interrupts, this function reads and clears the + If using hardware timer, this function reads and clears the 'saw a sample' flag that is set by the ISR. - When not using interrupts, this function sees whether it's time + When not using hardware timer, this function sees whether it's time to sample and, if so, reads the sample and processes it. First, check to see if the sketch has paused the Pulse Sensor sampling */ + bool result = false; if(!Paused){ - if (UsingInterrupts) { + if (UsingHardwareTimer) { // Disable interrupts to avoid a race with the ISR. // DISABLE_PULSE_SENSOR_INTERRUPTS; - boolean sawOne = SawNewSample; + bool sawOne = SawNewSample; SawNewSample = false; // ENABLE_PULSE_SENSOR_INTERRUPTS; - return sawOne; - } else { // Time the sample as close as you can when not using interrupts + result = sawOne; + } else { +// Sample PulseSensor as close as you can to 500Hz when not using hardware timer unsigned long nowMicros = micros(); if ((long) (NextSampleMicros - nowMicros) > 0L) { - return false; // not time yet. + result = false; // not time yet. } NextSampleMicros = nowMicros + MICROS_PER_READ; @@ -124,12 +140,10 @@ boolean PulseSensorPlayground::sawNewSample() { // time to call the sample processor onSampleTime(); - return true; + result = true; } } - - return false; - + return result; } void PulseSensorPlayground::onSampleTime() { @@ -151,8 +165,6 @@ void PulseSensorPlayground::onSampleTime() { } // Set the flag that says we've read a sample since the Sketch checked. - SawNewSample = true; - // digitalWrite(timingPin,LOW); // optionally connect timingPin to oscilloscope to time algorithm run time } @@ -177,14 +189,14 @@ int PulseSensorPlayground::getInterBeatIntervalMs(int sensorIndex) { return Sensors[sensorIndex].getInterBeatIntervalMs(); } -boolean PulseSensorPlayground::sawStartOfBeat(int sensorIndex) { +bool PulseSensorPlayground::sawStartOfBeat(int sensorIndex) { if (sensorIndex != constrain(sensorIndex, 0, SensorCount)) { return false; // out of range. } return Sensors[sensorIndex].sawStartOfBeat(); } -boolean PulseSensorPlayground::isInsideBeat(int sensorIndex) { +bool PulseSensorPlayground::isInsideBeat(int sensorIndex) { if (sensorIndex != constrain(sensorIndex, 0, SensorCount)) { return false; // out of range. } @@ -236,14 +248,14 @@ unsigned long PulseSensorPlayground::getLastBeatTime(int sensorIndex) { return Sensors[sensorIndex].getLastBeatTime(); } -boolean PulseSensorPlayground::isPaused() { +bool PulseSensorPlayground::isPaused() { return Paused; } -boolean PulseSensorPlayground::pause() { - boolean result = true; - if (UsingInterrupts) { - if (!PulseSensorPlaygroundDisableInterrupt()) { +bool PulseSensorPlayground::pause() { + bool result = true; + if (UsingHardwareTimer) { + if (!disableInterrupt()) { Paused = false; result = false; }else{ @@ -263,10 +275,10 @@ boolean PulseSensorPlayground::pause() { return result; } -boolean PulseSensorPlayground::resume() { - boolean result = true; - if (UsingInterrupts) { - if (!PulseSensorPlaygroundEnableInterrupt()) { +bool PulseSensorPlayground::resume() { + bool result = true; + if (UsingHardwareTimer) { + if (!enableInterrupt()) { Paused = true; result = false; }else{ @@ -313,4 +325,357 @@ boolean PulseSensorPlayground::resume() { } } #endif // PULSE_SENSOR_MEMORY_USAGE +#endif // USE_SERIAL + + +bool PulseSensorPlayground::setupInterrupt(){ + bool result = false; + + +#if !USE_HARDWARE_TIMER + /* + The Sketch doesn't want interrupts, + so we won't waste Flash space and create complexity + by adding interrupt-setup code. + */ + return result; + +#else + // This code sets up the sample timer interrupt + // based on the type of Arduino platform. + + #if defined(__AVR_ATmega328P__) || defined(__AVR_ATmega168__) || defined(__AVR_ATmega32U4__) || defined(__AVR_ATmega16U4__) + + // check to see if the Servo library is in use + #if defined Servo_h + // Initializes Timer2 to throw an interrupt every 2mS + // Interferes with PWM on pins 3 and 11 + #if defined(__AVR_ATmega328P__) || defined(__AVR_ATmega168__) + TCCR2A = 0x02; // Disable PWM and go into CTC mode + TCCR2B = 0x05; // don't force compare, 128 prescaler + #if F_CPU == 16000000L // if using 16MHz crystal + OCR2A = 0XF9; // set count to 249 for 2mS interrupt + #elif F_CPU == 8000000L // if using 8MHz crystal + OCR2A = 0X7C; // set count to 124 for 2mS interrupt + #endif + TIMSK2 = 0x02; // Enable OCR2A match interrupt DISABLE BY SETTING TO 0x00 + ENABLE_PULSE_SENSOR_INTERRUPTS; + // #define _useTimer2 + result = true; + #elif defined(__AVR_ATmega32U4__) || defined(__AVR_ATmega16U4__) + TCCR3A = 0x02; // Disable PWM and go into CTC mode + TCCR3B = 0x05; // don't force compare, 128 prescaler + #if F_CPU == 16000000L // if using 16MHz crystal + OCR3A = 0XF9; // set count to 249 for 2mS interrupt + #elif F_CPU == 8000000L // if using 8MHz crystal + OCR3A = 0X7C; // set count to 124 for 2mS interrupt + #endif + TIMSK3 = 0x02; // Enable OCR2A match interrupt DISABLE BY SETTING TO 0x00 + ENABLE_PULSE_SENSOR_INTERRUPTS; + // #define _useTimer2 + result = true; + #endif + #else + // Initializes Timer1 to throw an interrupt every 2mS. + // Interferes with PWM on pins 9 and 10 + TCCR1A = 0x00; // Disable PWM and go into CTC mode + TCCR1C = 0x00; // don't force compare + #if F_CPU == 16000000L // if using 16MHz crystal + TCCR1B = 0x0C; // prescaler 256 + OCR1A = 0x007C; // count to 124 for 2mS interrupt + #elif F_CPU == 8000000L // if using 8MHz crystal + TCCR1B = 0x0B; // prescaler = 64 + OCR1A = 0x00F9; // count to 249 for 2mS interrupt + #endif + TIMSK1 = 0x02; // Enable OCR1A match interrupt DISABLE BY SETTING TO 0x00 + ENABLE_PULSE_SENSOR_INTERRUPTS; + result = true; + #endif + #endif + + #if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) + + // check to see if the Servo library is in use + #if defined Servo_h + // Initializes Timer1 to throw an interrupt every 2mS. + // Interferes with PWM on pins 9 and 10 + TCCR1A = 0x00; // Disable PWM and go into CTC mode + TCCR1C = 0x00; // don't force compare + #if F_CPU == 16000000L // if using 16MHz crystal + TCCR1B = 0x0C; // prescaler 256 + OCR1A = 0x007C; // count to 124 for 2mS interrupt + #elif F_CPU == 8000000L // if using 8MHz crystal + TCCR1B = 0x0B; // prescaler = 64 + OCR1A = 0x00F9; // count to 249 for 2mS interrupt + #endif + TIMSK1 = 0x02; // Enable OCR1A match interrupt + ENABLE_PULSE_SENSOR_INTERRUPTS; + result = true; + + #else + // Initializes Timer2 to throw an interrupt every 2mS + // Interferes with PWM on pins 3 and 11 + TCCR2A = 0x02; // Disable PWM and go into CTC mode + TCCR2B = 0x05; // don't force compare, 128 prescaler + #if F_CPU == 16000000L // if using 16MHz crystal + OCR2A = 0XF9; // set count to 249 for 2mS interrupt + #elif F_CPU == 8000000L // if using 8MHz crystal + OCR2A = 0X7C; // set count to 124 for 2mS interrupt + #endif + TIMSK2 = 0x02; // Enable OCR2A match interrupt + ENABLE_PULSE_SENSOR_INTERRUPTS; + // #define _useTimer2 + result = true; + + #endif + #endif + + #if defined(__AVR_ATtiny85__) + GTCCR = 0x00; // Disable PWM, don't connect pins to events + OCR1A = 0x7D; // Set top of count to 125. Timer match throws the interrupt + OCR1C = 0x7D; // Set top of the count to 125. Timer match resets the counter + #if F_CPU == 16000000L + TCCR1 = 0x89; // Clear Timer on Compare, Set Prescaler to 256 + #elif F_CPU == 8000000L + TCCR1 = 0x88; // Clear Timer on Compare, Set Prescaler to 128 + #elif F_CPU == 1000000L + TCCR1 = 0x85 // Clear Timer on Compare, Set Prescaler to 16 + #endif + bitSet(TIMSK,6); // Enable interrupt on match between TCNT1 and OCR1A + ENABLE_PULSE_SENSOR_INTERRUPTS; + result = true; + #endif + + #if defined(ARDUINO_ARCH_RENESAS) + uint8_t timer_type = GPT_TIMER; + int8_t tindex = FspTimer::get_available_timer(timer_type); + if(tindex == 0){ + FspTimer::force_use_of_pwm_reserved_timer(); + tindex = FspTimer::get_available_timer(timer_type); + } + sampleTimer.begin(TIMER_MODE_PERIODIC, timer_type, tindex, SAMPLE_RATE_500HZ, 0.0f, sampleTimerISR); + sampleTimer.setup_overflow_irq(); + sampleTimer.open(); + sampleTimer.start(); + result = true; + #endif + + #if defined(ARDUINO_SAM_DUE) + sampleTimer.attachInterrupt(sampleTimer_ISR); + sampleTimer.start(2000); // Calls every period microseconds + result = true; + #endif + + #if defined(ARDUINO_ARCH_RP2040) + /* This starts the sample timer interrupt + * Use pause() and resume() to start and stop sampling on the fly + * Check Resources folder in the library for more tools + */ + sampleTimer.attachInterruptInterval(SAMPLE_INTERVAL_US, sampleTimer_ISR); + result = true; + #endif + + #if defined(ARDUINO_NRF52_ADAFRUIT) + /* This starts the sample timer interrupt + * Use pause() and resume() to start and stop sampling on the fly + * Check Resources folder in the library for more tools + */ + sampleTimer.attachInterruptInterval(TIMER3_INTERVAL_US, Timer3_ISR); + result = true; + #endif + + #if defined(ARDUINO_ARCH_ESP32) + /* + This will set up and start the timer interrupt on ESP32. + The interrupt will occur every 2000uS or 500Hz. + */ + sampleTimer = timerBegin(0, 80, true); + timerAttachInterrupt(sampleTimer, &onInterrupt, true); + timerAlarmWrite(sampleTimer, 2000, true); + timerAlarmEnable(sampleTimer); + result = true; + #endif + + #if defined(ARDUINO_ARCH_ESP8266) + ESP8266Timer sampleTimer; + sampleTimer.setFrequency(500,onInterrupt); + sampleTimer.restartTimer(); + #endif + + #if defined(ARDUINO_SAMD_ZERO) || defined(ARDUINO_ARCH_SAMD) + sampleTimer.attachInterrupt(SAMPLE_RATE_500HZ, onInterrupt); + result = true; + #endif + +#endif // USE_HARDWARE_TIMER + + return result; +} // setup interrupt + + + +bool PulseSensorPlayground::enableInterrupt(){ + bool result = false; +#if USE_HARDWARE_TIMER + #if defined(__AVR_ATmega328P__) || defined(__AVR_ATmega168__) || defined(__AVR_ATmega32U4__) || defined(__AVR_ATmega16U4__) + // check to see if the Servo library is in use + #if defined Servo_h + #if defined(__AVR_ATmega328P__) || defined(__AVR_ATmega168__) + DISABLE_PULSE_SENSOR_INTERRUPTS; + TIMSK2 = 0x02; // Enable OCR2A match interrupt + ENABLE_PULSE_SENSOR_INTERRUPTS; + result = true; + #elif defined(__AVR_ATmega32U4__) || defined(__AVR_ATmega16U4__) + DISABLE_PULSE_SENSOR_INTERRUPTS; + TIMSK3 = 0x02; // Enable OCR2A match interrupt + ENABLE_PULSE_SENSOR_INTERRUPTS; + result = true; + #endif + #else + DISABLE_PULSE_SENSOR_INTERRUPTS; + TIMSK1 = 0x02; // Enable OCR1A match interrupt + ENABLE_PULSE_SENSOR_INTERRUPTS; + result = true; + #endif + #endif + + #if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) + // check to see if the Servo library is in use + #if defined Servo_h + DISABLE_PULSE_SENSOR_INTERRUPTS; + TIMSK1 = 0x02; // Enable OCR1A match interrupt + ENABLE_PULSE_SENSOR_INTERRUPTS; + result = true; + #else + DISABLE_PULSE_SENSOR_INTERRUPTS; + TIMSK2 = 0x02; // Enable OCR2A match interrupt + ENABLE_PULSE_SENSOR_INTERRUPTS; + result = true; + #endif + #endif + + #if defined(__AVR_ATtiny85__) + DISABLE_PULSE_SENSOR_INTERRUPTS; + bitSet(TIMSK,6); // Enable interrupt on match between TCNT1 and OCR1A + ENABLE_PULSE_SENSOR_INTERRUPTS; + result = true; + #endif + + #if defined(ARDUINO_ARCH_ESP32) + timerAlarmEnable(sampleTimer); + result = true; + #endif + + #if defined(ARDUINO_ARCH_NRF52840) + sampleTimer.restartTimer(); + result = true; + #endif + + #if defined(ARDUINO_ARCH_RP2040) + sampleTimer.restartTimer(); + result = true; + #endif + + #if defined(ARDUINO_ARCH_RENESAS) + sampleTimer.start(); + result = true; + #endif + + #if defined(ARDUINO_ARCH_ESP8266) + sampleTimer.restartTimer(); + result = true; + #endif + + #if defined(ARDUINO_ARCH_SAM) + sampleTimer.start(2000); + result = true; + #endif + #endif + +return result; +} + + +bool PulseSensorPlayground::disableInterrupt(){ + bool result = false; +#if USE_HARDWARE_TIMER + #if defined(__AVR_ATmega328P__) || defined(__AVR_ATmega168__) || defined(__AVR_ATmega32U4__) || defined(__AVR_ATmega16U4__) + // check to see if the Servo library is in use + #if defined Servo_h + #if defined(__AVR_ATmega328P__) || defined(__AVR_ATmega168__) + DISABLE_PULSE_SENSOR_INTERRUPTS; + TIMSK2 = 0x00; // Disable OCR2A match interrupt + ENABLE_PULSE_SENSOR_INTERRUPTS; + result = true; + #elif defined(__AVR_ATmega32U4__) || defined(__AVR_ATmega16U4__) + DISABLE_PULSE_SENSOR_INTERRUPTS; + TIMSK3 = 0x00; // Disable OCR2A match interrupt + ENABLE_PULSE_SENSOR_INTERRUPTS; + result = true; + #endif + #else + DISABLE_PULSE_SENSOR_INTERRUPTS; + TIMSK1 = 0x00; // Disable OCR1A match interrupt + ENABLE_PULSE_SENSOR_INTERRUPTS; + result = true; + #endif + #endif + + #if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) + // check to see if the Servo library is in use + #if defined Servo_h + DISABLE_PULSE_SENSOR_INTERRUPTS; + TIMSK1 = 0x00; // Disable OCR1A match interrupt + ENABLE_PULSE_SENSOR_INTERRUPTS; + result = true; + #else + DISABLE_PULSE_SENSOR_INTERRUPTS; + TIMSK3 = 0x00; // Disable OCR2A match interrupt + ENABLE_PULSE_SENSOR_INTERRUPTS; + result = true; + #endif + #endif + + #if defined(__AVR_ATtiny85__) + DISABLE_PULSE_SENSOR_INTERRUPTS; + bitClear(TIMSK,6); // Disable interrupt on match between TCNT1 and OCR1A + ENABLE_PULSE_SENSOR_INTERRUPTS; + result = true; + #endif + + #if defined(ARDUINO_ARCH_ESP32) + timerAlarmDisable(sampleTimer); + #endif + + #if defined(ARDUINO_ARCH_NRF52840) + sampleTimer.stopTimer(); + result = true; + #endif + + #if defined(ARDUINO_ARCH_RP2040) + sampleTimer.stopTimer(); + result = true; + #endif + + #if defined(ARDUINO_ARCH_RENESAS) + sampleTimer.stop(); + result = true; + #endif + + #if defined(ARDUINO_ARCH_ESP8266) + sampleTimer.stopTimer(); + result = true; + #endif + + #if defined(ARDUINO_ARCH_SAM) + sampleTimer.stop(); + result = true; + #endif + +#endif + + + +return result; +} // DisableInterrupt diff --git a/src/PulseSensorPlayground.h b/src/PulseSensorPlayground.h index 2d032fc..b4f04e2 100644 --- a/src/PulseSensorPlayground.h +++ b/src/PulseSensorPlayground.h @@ -2,7 +2,7 @@ A central Playground object to manage a set of PulseSensors. See https://www.pulsesensor.com to get started. - Copyright World Famous Electronics LLC - see LICENSE + Copyright World Famous Electronics LLC - see LICENSE -JM why not list the copyright here? Contributors: Joel Murphy, https://pulsesensor.com Yury Gitman, https://pulsesensor.com @@ -14,40 +14,26 @@ This software is not intended for medical use. */ -/* - NOTE: Every Sketch that uses the PulseSensor Playground - must define the variable USE_ARDUINO_INTERRUPTS *before* including - PulseSensorPlayground.h. If you don't, you will get a compiler error - about "undefined reference to `PulseSensorPlayground::UsingInterrupts". - - In particular, if your Sketch wants the Playground to use interrupts - to read and process PulseSensor data, your Sketch must contain the - following two lines, in order: - #define USE_ARDUINO_INTERRUPTS true - #include - - If, instead, your Sketch does not use interrupts to read PulseSensor - data, your Sketch must instead contain the - following two lines, in order: - #define USE_ARDUINO_INTERRUPTS false - #include - - See utility/interrupts.h for details. - - Internal, developer note: in the Playground code, don't use - USE_ARDUINO_INTERRUPTS as a variable; instead, refer to - PulseSensorPlayground::UsingInterrupts, which is a static variable - that reflects what the Sketch defined USE_ARDUINO_INTERRUPTS to. - Because USE_ARDUINO_INTERRUPTS is defined *only* in the user's Sketch, - it doesn't exist when the various Playground modules are compiled. - - See further notes in interrupts.h -*/ - #ifndef PULSE_SENSOR_PLAYGROUND_H #define PULSE_SENSOR_PLAYGROUND_H +/* + SelectTimer.h will determine if the library supports + hardware timer interrupts or not. If so, it will set + up a hardware timer to sample at 500Hz. If not, + it will set up a software timer that the main program + will have to check often called + +*/ +#include "utility/SelectTimer.h" + +/* + Library version number +*/ +#define PULSESENSOR_PLAYGROUND_VERSION_STRING "v2.0.0" +#define PULSESENSOR_PLAYGROUND_VERSION_NUMBER 2 + /* If you wish to perform timing statistics on your non-interrupt Sketch: @@ -118,11 +104,12 @@ /* Tell the compiler not to include Serial related code. - If you are coming up against issues with the Serial class - removing the related code here can help. + If you are coming up against issues with the Serial class, + or have no need for the library's Serial output, + you can remove the related code here. - When true, the library can mangage Serial output, - and messages from libraries will be printed. + When true, the library will mangage Serial output, + and messages from the library will be sent to the Serial specified in the .ino file. When false, the library will not have any access to the Serial class. All the other functionality remains. @@ -130,7 +117,8 @@ #define USE_SERIAL true // #define USE_SERIAL false -#if defined(ARDUINO_ARCH_NRF52) + +#if defined(ARDUINO_NRF52_ADAFRUIT) #include "Adafruit_TinyUSB.h" #endif #include @@ -141,6 +129,7 @@ #include "utility/PulseSensorTimingStatistics.h" #define SAMPLE_RATE_500HZ 500 +#define SAMPLES_PER_SERIAL_SAMPLE 10 class PulseSensorPlayground { public: @@ -168,48 +157,29 @@ class PulseSensorPlayground { PulseSensorPlayground(int numberOfSensors = 1); /* - Start reading and processing data from the PulseSensors. - + Start reading and processing data from the PulseSensor(s). Your Sketch should make all necessary PulseSensor configuration calls before calling begin(). - - If the Sketch defined USE_ARDUINO_INTERRUPTS as true, this function - sets up and turns on interrupts for the PulseSensor. - - If instead the Sketch defined USE_ARDUINO_INTERRUPTS as false, - it initializes what's necessary for the Sketch to process - PulsSensor signals. See sawNewSample(), below. - - Returns true if successful, false if unsuccessful. - Returns false if PulseSensorPlayground doesn't yet support - interrupts on this Arduino platform and the user's Sketch - did a #define USE_ARDUINO_INTERRUPTS true. - - If begin() returns false, you can either use a different - type of Arduino platform, or you can change your Sketch's - definition of USE_ARDUINO_INTERRUPTS to false: - #define USE_ARDUINO_INTERRUPTS false */ - boolean begin(); + bool begin(); /* + +vvvvvvvv THIS NEEDS MODIFICATION FOR V2 vvvvvvvv Returns true if a new sample has been read from each PulseSensor. You'll likely want to add this call to your Sketch's loop() only if you either 1) want to do something with each sample of the - PulseSensor signals, or 2) your Sketch doesn't use interrupts + PulseSensor signals, or 2) your Sketch doesn't use a hardware timer to read from the PulseSensors. - NOTE: If your Sketch defined USE_ARDUINO_INTERRUPTS as false, - you must call pulse.sawNewSample() frequently (at least + NOTE: If your Sketch uses a software timer, + you must call sawNewSample() frequently (at least once every 2 milliseconds) to assure that PulseSensor signals are read accurately. - A typical loop() that doesn't use interrupts will contain: - if (pulse.sawNewSample()) { - int latest = pulse.getLatestSample(); - ...do whatever you want with the sample read from the PulseSensor. - } + A typical loop() that uses a software timer should not have + any delay() statements in it. */ - boolean sawNewSample(); + bool sawNewSample(); //---------- Per-PulseSensor functions @@ -219,8 +189,7 @@ class PulseSensorPlayground { call pulse.analogInput(pin) or pulse.analogInput(pin, sensorIndex). inputPin = the analog input this PulseSensor is connected to. - sensorIndex = optional, index (0..numberOfSensors - 1) - of the PulseSensor to configure. + sensorIndex = optional, index (0..numberOfSensors - 1). */ void analogInput(int inputPin, int sensorIndex = 0); @@ -233,33 +202,32 @@ class PulseSensorPlayground { pulse.blinkOnPulse(blinkPin, sensorIndex). blinkPin = the pin to blink on each pulse, which you've connected - to an LED and 220 ohm resistor, or the built in LED pin + to an LED and 1K ohm resistor, or the built in LED pin on your Arduino (for example, pin 13 on Arduino Uno). - sensorIndex = optional, index (0..numberOfSensors - 1) - of the PulseSensor to configure. + sensorIndex = optional, index (0..numberOfSensors - 1). */ void blinkOnPulse(int blinkPin, int sensorIndex = 0); /* - By default, the Playground doesn't blink LEDs automatically. + By default, the Playground doesn't fade LEDs automatically. - If you wish the Playground to automatically blink a fading LED - during each detected pulse, - call fadeOnPulse(fadePin) or fadeOnPulse(fadePin, sensorIndex). + If you wish the Playground to automatically fade an LED + during each detected pulse, call fadeOnPulse(fadePin) + or fadeOnPulse(fadePin, sensorIndex). - NOTE: the fade pin must be a PWM (Pulse-Width Modulation) pin. + NOTE: the fade pin must be able to output PWM (Pulse-Width Modulation). fadePin = the PWM pin to blink and fade on each pulse, which is connected to an LED and a current-limit resistor. - sensorIndex = optional, index (0..numberOfSensors - 1) - of the PulseSensor to configure. + sensorIndex = optional, index (0..numberOfSensors - 1). */ void fadeOnPulse(int fadePin, int sensorIndex = 0); /* - (Internal to library - do not call from a Sketch) Perform all the processing necessary when it's time to read from all the PulseSensors and process their signals. + This function is not called by the user, but in some cases + the sketch needs to associate it with other code above the setup. */ void onSampleTime(); @@ -267,8 +235,7 @@ class PulseSensorPlayground { Returns the most recently read analog value from the given PulseSensor (range: 0..1023). - sensorIndex = optional, index (0..numberOfSensors - 1) - of the PulseSensor of interest. + sensorIndex = optional, index (0..numberOfSensors - 1). */ int getLatestSample(int sensorIndex = 0); @@ -278,8 +245,7 @@ class PulseSensorPlayground { The internal beats-per-minute measure is updated per-PulseSensor, when a beat is detected from that PulseSensor. - sensorIndex = optional, index (0..numberOfSensors - 1) - of the PulseSensor of interest. + sensorIndex = optional, index (0..numberOfSensors - 1). */ int getBeatsPerMinute(int sensorIndex = 0); @@ -290,30 +256,28 @@ class PulseSensorPlayground { The internal IBI measure is updated per-PulseSensor, when a beat is detected from that PulseSensor. - sensorIndex = optional, index (0..numberOfSensors - 1) - of the PulseSensor of interest. + sensorIndex = optional, index (0..numberOfSensors - 1). */ int getInterBeatIntervalMs(int sensorIndex = 0); /* - Returns true if a new heartbeat (pulse) has been detected - from the given PulseSensor since the last call to sawStartOfBeat() - on this PulseSensor. + Returns true if the moment of a new heartbeat (pulse) has been detected + since the last time sawStartOfBeat() was called on this PulseSensor. Typical use in loop(): if (pulse.sawStartOfBeat()) { - ...do what you want to do per-heartbeat. + ...do what you want to do when there is a heartbeat. } - sensorIndex = optional, index (0..numberOfSensors - 1) - of the PulseSensor of interest. + sensorIndex = optional, index (0..numberOfSensors - 1). */ - boolean sawStartOfBeat(int sensorIndex = 0); + bool sawStartOfBeat(int sensorIndex = 0); /* Returns true if the given PulseSensor signal is currently - inside a heartbeat. That is, returns true if the signal is above - the automatically-set threshold of a beat, false otherwise. + inside a heartbeat. Inside a heartbeat means, the signal is above + the dynamically-set threshold of a beat. When the signal goes below + the threshold, isInsiceBeat will return false. Typical use in loop(): if (pulse.isInsideBeat()) { @@ -322,15 +286,16 @@ class PulseSensorPlayground { ...do what you want while between beats. } - sensorIndex = optional, index (0..numberOfSensors - 1) - of the PulseSensor of interest. + sensorIndex = optional, index (0..numberOfSensors - 1). */ - boolean isInsideBeat(int sensorIndex = 0); + bool isInsideBeat(int sensorIndex = 0); /* - By default, the threshold value is 530. - threshold is used to find the heartbeat - adjust this value up in the setup function to avoid noise. + By default, the threshold value is 530. JM- is this true? + threshold value is used to find the heartbeat. + PulseSensor signal idles at V/2 (512 analog value on a 10 bit ADC) + It is recommended to set this value above the idle threshold. + adjust this value in the setup function to avoid noise. */ void setThreshold(int threshold, int sensorIndex = 0); @@ -350,6 +315,9 @@ class PulseSensorPlayground { /* By default, Playground output is in SERIAL_PLOTTER format. + In SERIAL_PLOTTER format, data will be sent to the Serial port + in CSV format. Data include the PulseSensor raw signal, + Interbeat Interval, and BPM will print with every heartbeat. If you want output in a different format, call this function once sometime before calling pulse.begin(). @@ -357,16 +325,16 @@ class PulseSensorPlayground { Remember to call pulse.setSerial() if you want serial output. outputType = SERIAL_PLOTTER to output to the Arduino Serial Plotter, - PROCESSSING_VISUALIZER to output to the Processing Sketch - that draws the PulseSensor output. + PROCESSSING_VISUALIZER to output formatted to our data visualization + software written in Processing. See www.pulsesensor.com for tutorials. */ void setOutputType(byte outputType); /* - Output the current signal information for each PulseSensor, + Output the current raw signal data for each PulseSensor, in the previously-set outputType. - If your Sketch wants to plot samples, it should call this function + If your Sketch wants to plot raw sample data, it should call this function every so often. */ void outputSample(); @@ -390,55 +358,69 @@ class PulseSensorPlayground { Used exclusively with the Pulse Sensor Processing sketch. */ void outputToSerial(char symbol, int data); +#else + #warning "PulseSensor Playground internal Serial commands not used" #endif /* Returns the current amplitude of the pulse waveform. + Amplitude is calculated after a heartbeat is detected. + the value is the ADC counts from the previous trough + to the following peak in the raw sensor data. */ int getPulseAmplitude(int sensorIndex = 0); /* Returns the sample number when the last beat was found. 2mS resolution. + The sample number will count up continually. + As an unsigned long variable, it will roll-over in approx 100 days free running. */ unsigned long getLastBeatTime(int sensorIndex = 0); - // check to see if the library is sampling (on interrupt OR in software) - boolean isPaused(); + /* + Returns true if PulseSensor sampling is paused, false if it is sampling - // option to pause Pulse Sensor sampling in order to do other stuff - // this function will only tell the timer to stop interrupting - // does not return PWM or other fuctionality to effected pins - boolean pause(); + */ + bool isPaused(); - // restart sampling the Pulse Sensor after a pause - boolean resume(); + /* + Pause PulseSensor sampling in order to do other stuff. + This will effect all PulseSensors if you are using more than one. + + */ + bool pause(); + + /* + Resume sampling the PulseSensor after a call to pause(). + This will effect all PulseSensors if you are using more than one. + */ + bool resume(); // (internal to the library) "this" pointer for the ISR. +#if USE_HARDWARE_TIMER static PulseSensorPlayground *OurThis; +#endif + + byte samplesUntilReport = SAMPLES_PER_SERIAL_SAMPLE; + bool UsingHardwareTimer; private: /* - Optionally use this (or a different) pin to toggle high while the algorithm is running. - Connect it to an oscilloscope to determine algorithm run time. + Optionally use this (or a different) pin to toggle high + while the beat finding algorithm is running. + Uncomment this line and the other 3 timingPin lines + in PulseSensorPlayground.cpp, then connect the pin + to an oscilloscope to measure algorithm run time. */ // int timingPin = 10; -/* - Configure and enable interrupts to read samples. - Call only if PulseSensorPlayground::UsingInterrupts is true. - - This function is defined (vs. declared here) in interrupts.h -*/ - // void setupInterrupt(); - // boolean disableInterrupt(); - // boolean enableInterrupt(); /* - Don't let the library try to print stuff - if the serial is not passed to us + Print the memory usage of the compiled program. + Only do this if the Serial class is enabled. */ #if USE_SERIAL #if PULSE_SENSOR_MEMORY_USAGE @@ -450,35 +432,29 @@ class PulseSensorPlayground { #endif /* - If true, the Sketch wants to use interrupts to read the PulseSensor(s). + Sets up the sample timer interrupt for this Arduino Platform + and the pause and resume routines. +*/ +bool setupInterrupt(); +bool disableInterrupt(); +bool enableInterrupt(); - This variable is defined (vs. declared here) in interrupts.h +/* + Varialbles */ - static boolean UsingInterrupts; - boolean Paused; + bool Paused; // keeps track of whether the algorithm is running byte SensorCount; // number of PulseSensors in Sensors[]. PulseSensor *Sensors; // use Sensors[idx] to access a sensor. volatile unsigned long NextSampleMicros; // Desired time to sample next. - volatile boolean SawNewSample; // "A sample has arrived from the ISR" + volatile bool SawNewSample; // "A sample has arrived from the ISR" #if USE_SERIAL PulseSensorSerialOutput SerialOutput; // Serial Output manager. -#endif +#endif // USE_SERIAL #if PULSE_SENSOR_TIMING_ANALYSIS // Don't use ram and flash we don't need. PulseSensorTimingStatistics *pTiming; #endif // PULSE_SENSOR_TIMING_ANALYSIS -#if defined(ARDUINO_ARCH_RENESAS) - uint8_t timer_type; - int8_t tindex; -#endif - }; -/* - We include interrupts.h here instead of above - because it depends on variables and functions we declare (vs. define) - in PulseSensorPlayground.h. -*/ -#include "utility/Interrupts.h" #endif // PULSE_SENSOR_PLAYGROUND_H diff --git a/src/utility/Interrupts.h b/src/utility/Interrupts.h deleted file mode 100644 index 2bdb702..0000000 --- a/src/utility/Interrupts.h +++ /dev/null @@ -1,492 +0,0 @@ -/* - Interrupt handling helper functions for PulseSensors. - See https://www.pulsesensor.com to get started. - - Copyright World Famous Electronics LLC - see LICENSE - Contributors: - Joel Murphy, https://pulsesensor.com - Yury Gitman, https://pulsesensor.com - Bradford Needham, @bneedhamia, https://bluepapertech.com - - Licensed under the MIT License, a copy of which - should have been included with this software. - - This software is not intended for medical use. -*/ - -/* - Any Sketch using the Playground must do one of two things: - 1) #define USE_ARDUINO_INTERRUPTS true - if using interrupts; - 2) #define USE_ARDUINO_INTERRUPTS false - if not using interrupts. - - Only the Sketch must define USE_ARDUINO_INTERRUPTS. - If the Sketch doesn't define USE_ARDUINO_INTERRUPTS, or if some other file - defines it as well, a link error will result. - - See notes in PulseSensorPlayground.h - - The code below is rather convoluted, with nested #if's. - This structure is used to achieve two goals: - 1) Minimize the complexity the user has to deal with to use or - not use interrupts to sample the PulseSensor data; - 2) Create an ISR() only if the Sketch uses interrupts. Defining an - ISR(), even if not used, may interfere with other libraries' use - of interrupts. - - The nesting goes something like this: - if the Sketch is being compiled... #if defined(USE_ARDUINO_INTERRUPTS) - if the user wants to use interrupts... #if USE_ARDUINO_INTERRUPTS - #if's for the various Arduino platforms... #if defined(__AVR_ATmega328P__)... - - RULES of the constant USE_ARDUINO_INTERRUPTS: - 1) This file, interrupts.h, should be the only file that uses USE_ARDUINO_INTERRUPTS - (although PulseSensorPlayground's comments talk about it to the user). - If other code in the library wants to know whether interrupts are being used, - that code should use PulseSensorPlayground::UsingInterrupts, which is true - if the Sketch wants to use interrupts. - 1) Always use #if USE_ARDUINO_INTERRUPTS inside an #if defined(USE_ARDUINO_INTERRUPTS). - If you don't first test the #if defined(...), a compile error will occur - when compiling the library modules. - 2) USE_ARDUINO_INTERRUPTS is defined only when this file is being included - by the user's Sketch; not when the rest of the library is compiled. - 3) USE_ARDUINO_INTERRUPTS is true if the user wants to use interrupts; - it's false if they don't. -*/ - -#ifndef PULSE_SENSOR_INTERRUPTS_H -#define PULSE_SENSOR_INTERRUPTS_H - - -//TODO: if noInterrupts() and interrupts() are defined for Arduino 101, -// Use them throughout and eliminate these DISABLE/ENAGLE macros. -// -// Macros to link to interrupt disable/enable only if they exist -// The name is long to avoid collisions with Sketch and Library symbols. -#if defined(__arc__)||(ARDUINO_SAMD_MKR1000)||(ARDUINO_SAMD_MKRZERO)||(ARDUINO_SAMD_ZERO)\ -||(ARDUINO_ARCH_SAMD)||(ARDUINO_ARCH_STM32)||(ARDUINO_STM32_STAR_OTTO)||(ARDUINO_ARCH_NRF52)\ -||(ARDUINO_NANO33BLE)||(ARDUINO_ARCH_RP2040)||(ARDUINO_ARCH_ESP32)||(ARDUINO_ARCH_MBED_NANO)\ -||(ARDUINO_ARCH_NRF52840)||(ARDUINO_ARCH_SAM)||(ARDUINO_ARCH_RENESAS) - -#define DISABLE_PULSE_SENSOR_INTERRUPTS -#define ENABLE_PULSE_SENSOR_INTERRUPTS -#else -#define DISABLE_PULSE_SENSOR_INTERRUPTS cli() -#define ENABLE_PULSE_SENSOR_INTERRUPTS sei() -#endif - -// SAVED FOR FUTURE SUPPORT OF TEENSY INTERRUPTS -#if defined(__MK66FX1M0__)||(__MK64FX512__)||(__MK20DX256__)||(__MK20DX128__) -// #include -#endif - - -/* - (internal to the library) - Sets up the sample timer interrupt for this Arduino Platform. - - Returns true if successful, false if we don't yet support - the timer interrupt on this Arduino. - - NOTE: This is the declaration (vs. definition) of this function. - See the definition (vs. declaration) of this function, below. -*/ -boolean PulseSensorPlaygroundSetupInterrupt(); -boolean PulseSensorPlaygroundDisableInterrupt(); -boolean PulseSensorPlaygroundEnableInterrupt(); - -#if defined(USE_ARDUINO_INTERRUPTS) // that is, if the Sketch is including us... - -/* - (internal to the library) True if the Sketch uses interrupts to - sample - We need to define USE_PS_INTERRUPTS once per Sketch, whether or not - the Sketch uses interrupts. - Not doing this or doing it for every file that includes interrupts.h - would cause a link error. - - To refer to this variable, use "PulseSensorPlayground::UsingInterrupts". - - See PulseSensorPlayground.h -*/ -boolean PulseSensorPlayground::UsingInterrupts = USE_ARDUINO_INTERRUPTS; - -boolean PulseSensorPlaygroundSetupInterrupt(){ - boolean result = false; - -#if !USE_ARDUINO_INTERRUPTS - /* - The Sketch doesn't want interrupts, - so we won't waste Flash space and create complexity - by adding interrupt-setup code. - */ - return result; - -#else - // This code sets up the sample timer interrupt - // based on the type of Arduino platform. - - /* - NOTE: when you change the #if's in this function, - be sure to add similar #if's (if necessary) to the ISR() defined - below. - */ - - #if defined(__AVR_ATmega328P__) || defined(__AVR_ATmega168__) || defined(__AVR_ATmega32U4__) || defined(__AVR_ATmega16U4__) - - // check to see if the Servo library is in use - #if defined Servo_h - // Initializes Timer2 to throw an interrupt every 2mS - // Interferes with PWM on pins 3 and 11 - #if defined(__AVR_ATmega328P__) || defined(__AVR_ATmega168__) - TCCR2A = 0x02; // Disable PWM and go into CTC mode - TCCR2B = 0x05; // don't force compare, 128 prescaler - #if F_CPU == 16000000L // if using 16MHz crystal - OCR2A = 0XF9; // set count to 249 for 2mS interrupt - #elif F_CPU == 8000000L // if using 8MHz crystal - OCR2A = 0X7C; // set count to 124 for 2mS interrupt - #endif - TIMSK2 = 0x02; // Enable OCR2A match interrupt DISABLE BY SETTING TO 0x00 - ENABLE_PULSE_SENSOR_INTERRUPTS; - // #define _useTimer2 - result = true; - #elif defined(__AVR_ATmega32U4__) || defined(__AVR_ATmega16U4__) - TCCR3A = 0x02; // Disable PWM and go into CTC mode - TCCR3B = 0x05; // don't force compare, 128 prescaler - #if F_CPU == 16000000L // if using 16MHz crystal - OCR3A = 0XF9; // set count to 249 for 2mS interrupt - #elif F_CPU == 8000000L // if using 8MHz crystal - OCR3A = 0X7C; // set count to 124 for 2mS interrupt - #endif - TIMSK3 = 0x02; // Enable OCR2A match interrupt DISABLE BY SETTING TO 0x00 - ENABLE_PULSE_SENSOR_INTERRUPTS; - // #define _useTimer2 - result = true; - #endif - #else - // Initializes Timer1 to throw an interrupt every 2mS. - // Interferes with PWM on pins 9 and 10 - TCCR1A = 0x00; // Disable PWM and go into CTC mode - TCCR1C = 0x00; // don't force compare - #if F_CPU == 16000000L // if using 16MHz crystal - TCCR1B = 0x0C; // prescaler 256 - OCR1A = 0x007C; // count to 124 for 2mS interrupt - #elif F_CPU == 8000000L // if using 8MHz crystal - TCCR1B = 0x0B; // prescaler = 64 - OCR1A = 0x00F9; // count to 249 for 2mS interrupt - #endif - TIMSK1 = 0x02; // Enable OCR1A match interrupt DISABLE BY SETTING TO 0x00 - ENABLE_PULSE_SENSOR_INTERRUPTS; - result = true; - #endif - #endif - - #if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) - - // check to see if the Servo library is in use - #if defined Servo_h - // Initializes Timer1 to throw an interrupt every 2mS. - // Interferes with PWM on pins 9 and 10 - TCCR1A = 0x00; // Disable PWM and go into CTC mode - TCCR1C = 0x00; // don't force compare - #if F_CPU == 16000000L // if using 16MHz crystal - TCCR1B = 0x0C; // prescaler 256 - OCR1A = 0x007C; // count to 124 for 2mS interrupt - #elif F_CPU == 8000000L // if using 8MHz crystal - TCCR1B = 0x0B; // prescaler = 64 - OCR1A = 0x00F9; // count to 249 for 2mS interrupt - #endif - TIMSK1 = 0x02; // Enable OCR1A match interrupt - ENABLE_PULSE_SENSOR_INTERRUPTS; - result = true; - - #else - // Initializes Timer2 to throw an interrupt every 2mS - // Interferes with PWM on pins 3 and 11 - TCCR2A = 0x02; // Disable PWM and go into CTC mode - TCCR2B = 0x05; // don't force compare, 128 prescaler - #if F_CPU == 16000000L // if using 16MHz crystal - OCR2A = 0XF9; // set count to 249 for 2mS interrupt - #elif F_CPU == 8000000L // if using 8MHz crystal - OCR2A = 0X7C; // set count to 124 for 2mS interrupt - #endif - TIMSK2 = 0x02; // Enable OCR2A match interrupt - ENABLE_PULSE_SENSOR_INTERRUPTS; - // #define _useTimer2 - result = true; - - #endif - #endif - - #if defined(__AVR_ATtiny85__) - GTCCR = 0x00; // Disable PWM, don't connect pins to events - OCR1A = 0x7D; // Set top of count to 125. Timer match throws the interrupt - OCR1C = 0x7D; // Set top of the count to 125. Timer match resets the counter - #if F_CPU == 16000000L - TCCR1 = 0x89; // Clear Timer on Compare, Set Prescaler to 256 - #elif F_CPU == 8000000L - TCCR1 = 0x88; // Clear Timer on Compare, Set Prescaler to 128 - #elif F_CPU == 1000000L - TCCR1 = 0x85 // Clear Timer on Compare, Set Prescaler to 16 - #endif - bitSet(TIMSK,6); // Enable interrupt on match between TCNT1 and OCR1A - ENABLE_PULSE_SENSOR_INTERRUPTS; - result = true; - #endif - - #if defined(__arc__)||(ARDUINO_SAMD_MKR1000)||(ARDUINO_SAMD_MKRZERO)||(ARDUINO_SAMD_ZERO)\ - ||(ARDUINO_ARCH_SAMD)||(ARDUINO_ARCH_STM32)||(ARDUINO_STM32_STAR_OTTO)||(ARDUINO_NANO33BLE) - - - #error "Unsupported Board Selected! Try Using the example: PulseSensor_BPM_Alternative.ino" - result = false; // unknown or unsupported platform. - #endif - - #if defined(__arc__)||(ARDUINO_SAMD_MKR1000)||(ARDUINO_SAMD_MKRZERO)||(ARDUINO_SAMD_ZERO)\ -||(ARDUINO_ARCH_SAMD)||(ARDUINO_ARCH_STM32)||(ARDUINO_STM32_STAR_OTTO)||(ARDUINO_ARCH_NRF52)\ -||(ARDUINO_NANO33BLE)||(ARDUINO_ARCH_RP2040)||(ARDUINO_ARCH_ESP32)||(ARDUINO_ARCH_MBED_NANO)\ -||(ARDUINO_ARCH_NRF52840)||(ARDUINO_ARCH_RENESAS)||(ARDUINO_ARCH_SAM) - - result = true; - #endif - -#endif // USE_ARDUINO_INTERRUPTS - - return result; -} - -boolean PulseSensorPlaygroundDisableInterrupt(){ - boolean result = false; -#if USE_ARDUINO_INTERRUPTS - #if defined(__AVR_ATmega328P__) || defined(__AVR_ATmega168__) || defined(__AVR_ATmega32U4__) || defined(__AVR_ATmega16U4__) - // check to see if the Servo library is in use - #if defined Servo_h - #if defined(__AVR_ATmega328P__) || defined(__AVR_ATmega168__) - DISABLE_PULSE_SENSOR_INTERRUPTS; - TIMSK2 = 0x00; // Disable OCR2A match interrupt - ENABLE_PULSE_SENSOR_INTERRUPTS; - result = true; - #elif defined(__AVR_ATmega32U4__) || defined(__AVR_ATmega16U4__) - DISABLE_PULSE_SENSOR_INTERRUPTS; - TIMSK3 = 0x00; // Disable OCR2A match interrupt - ENABLE_PULSE_SENSOR_INTERRUPTS; - result = true; - #endif - #else - DISABLE_PULSE_SENSOR_INTERRUPTS; - TIMSK1 = 0x00; // Disable OCR1A match interrupt - ENABLE_PULSE_SENSOR_INTERRUPTS; - result = true; - #endif - #endif - - #if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) - // check to see if the Servo library is in use - #if defined Servo_h - DISABLE_PULSE_SENSOR_INTERRUPTS; - TIMSK1 = 0x00; // Disable OCR1A match interrupt - ENABLE_PULSE_SENSOR_INTERRUPTS; - result = true; - #else - DISABLE_PULSE_SENSOR_INTERRUPTS; - TIMSK3 = 0x00; // Disable OCR2A match interrupt - ENABLE_PULSE_SENSOR_INTERRUPTS; - result = true; - #endif - #endif - - #if defined(__AVR_ATtiny85__) - DISABLE_PULSE_SENSOR_INTERRUPTS; - bitClear(TIMSK,6); // Disable interrupt on match between TCNT1 and OCR1A - ENABLE_PULSE_SENSOR_INTERRUPTS; - result = true; - #endif - - #if defined(ARDUINO_ARCH_ESP32) - timerAlarmDisable(sampleTimer); - #endif - - #if defined(ARDUINO_ARCH_NRF52840) - sampleTimer.stopTimer(); - result = true; - #endif - - #if defined(ARDUINO_ARCH_RP2040) - sampleTimer.stopTimer(); - result = true; - #endif - - #if defined(ARDUINO_ARCH_RENESAS) - sampleTimer.stop(); - result = true; - #endif - - #if defined(ARDUINO_ARCH_SAM) - sampleTimer.stop(); - result = true; - #endif - -#endif - - - -return result; // unknown or unsupported platform. -} // PulseSensorPlaygroundDisableInterrupt - - -boolean PulseSensorPlaygroundEnableInterrupt(){ - boolean result = false; -#if USE_ARDUINO_INTERRUPTS - #if defined(__AVR_ATmega328P__) || defined(__AVR_ATmega168__) || defined(__AVR_ATmega32U4__) || defined(__AVR_ATmega16U4__) // || defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) - // check to see if the Servo library is in use - #if defined Servo_h - #if defined(__AVR_ATmega328P__) || defined(__AVR_ATmega168__) - DISABLE_PULSE_SENSOR_INTERRUPTS; - TIMSK2 = 0x02; // Enable OCR2A match interrupt - ENABLE_PULSE_SENSOR_INTERRUPTS; - result = true; - #elif defined(__AVR_ATmega32U4__) || defined(__AVR_ATmega16U4__) - DISABLE_PULSE_SENSOR_INTERRUPTS; - TIMSK3 = 0x02; // Enable OCR2A match interrupt - ENABLE_PULSE_SENSOR_INTERRUPTS; - result = true; - #endif - #else - DISABLE_PULSE_SENSOR_INTERRUPTS; - TIMSK1 = 0x02; // Enable OCR1A match interrupt - ENABLE_PULSE_SENSOR_INTERRUPTS; - result = true; - #endif - #endif - - #if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) - // check to see if the Servo library is in use - #if defined Servo_h - DISABLE_PULSE_SENSOR_INTERRUPTS; - TIMSK1 = 0x02; // Enable OCR1A match interrupt - ENABLE_PULSE_SENSOR_INTERRUPTS; - result = true; - #else - DISABLE_PULSE_SENSOR_INTERRUPTS; - TIMSK2 = 0x02; // Enable OCR2A match interrupt - ENABLE_PULSE_SENSOR_INTERRUPTS; - result = true; - #endif - #endif - - #if defined(__AVR_ATtiny85__) - DISABLE_PULSE_SENSOR_INTERRUPTS; - bitSet(TIMSK,6); // Enable interrupt on match between TCNT1 and OCR1A - ENABLE_PULSE_SENSOR_INTERRUPTS; - result = true; - #endif - - #if defined(ARDUINO_ARCH_ESP32) - timerAlarmEnable(sampleTimer); - result = true; - #endif - - #if defined(ARDUINO_ARCH_NRF52840) - sampleTimer.restartTimer(); - result = true; - #endif - - #if defined(ARDUINO_ARCH_RP2040) - sampleTimer.restartTimer(); - result = true; - #endif - - #if defined(ARDUINO_ARCH_RENESAS) - sampleTimer.start(); - result = true; - #endif - - #if defined(ARDUINO_ARCH_SAM) - sampleTimer.start(2000); - result = true; - #endif - -#endif - -return result; // unknown or unsupported platform. -} - -#if USE_ARDUINO_INTERRUPTS -/* - We create the Interrupt Service Routine only if the Sketch is - using interrupts. If we defined it when we didn't use it, - the ISR() will inappropriately intercept timer interrupts that - we don't use when not using interrupts. - - We define the ISR that handles the timer that - PulseSensorPlaygroundSetupInterrupt() set up. - NOTE: Make sure that this ISR uses the appropriate timer for - the platform detected by PulseSensorPlaygroundSetupInterrupt(), above. -*/ - #if defined(__AVR_ATmega328P__) || defined(__AVR_ATmega168__) || defined(__AVR_ATmega32U4__) || defined(__AVR_ATmega16U4__) || defined(__AVR_ATtiny85__) - #if defined Servo_h - #if defined(__AVR_ATmega328P__) || defined(__AVR_ATmega168__) - ISR(TIMER2_COMPA_vect) - { - DISABLE_PULSE_SENSOR_INTERRUPTS; // disable interrupts while we do this - - PulseSensorPlayground::OurThis->onSampleTime(); - - ENABLE_PULSE_SENSOR_INTERRUPTS; // enable interrupts when you're done - } - #elif defined(__AVR_ATmega32U4__) || defined(__AVR_ATmega16U4__) - ISR(TIMER3_COMPA_vect) - { - DISABLE_PULSE_SENSOR_INTERRUPTS; // disable interrupts while we do this - - PulseSensorPlayground::OurThis->onSampleTime(); - - ENABLE_PULSE_SENSOR_INTERRUPTS; // enable interrupts when you're done - } - #endif - #else - ISR(TIMER1_COMPA_vect) - { - DISABLE_PULSE_SENSOR_INTERRUPTS; // disable interrupts while we do this - - PulseSensorPlayground::OurThis->onSampleTime(); - - ENABLE_PULSE_SENSOR_INTERRUPTS; // enable interrupts when you're done - } - #endif - #endif - - #if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) - #if defined Servo_h - ISR(TIMER1_COMPA_vect) - { - DISABLE_PULSE_SENSOR_INTERRUPTS; // disable interrupts while we do this - - PulseSensorPlayground::OurThis->onSampleTime(); - - ENABLE_PULSE_SENSOR_INTERRUPTS; // enable interrupts when you're done - } - #else - ISR(TIMER2_COMPA_vect) - { - DISABLE_PULSE_SENSOR_INTERRUPTS; // disable interrupts while we do this - - PulseSensorPlayground::OurThis->onSampleTime(); - - ENABLE_PULSE_SENSOR_INTERRUPTS; // enable interrupts when you're done - } - #endif - #endif - - - - #if defined(__MK66FX1M0__)||(__MK64FX512__)||(__MK20DX256__)||(__MK20DX128__) - // Interrupts not supported yet for Teensy - #endif - - -#endif // USE_ARDUINO_INTERRUPTS - -#endif // defined(USE_ARDUINO_INTERRUPTS) - -#endif // PULSE_SENSOR_INTERRUPTS_H -// #endif diff --git a/src/utility/PulseSensor.cpp b/src/utility/PulseSensor.cpp index 2e7f55e..14520fa 100644 --- a/src/utility/PulseSensor.cpp +++ b/src/utility/PulseSensor.cpp @@ -13,6 +13,9 @@ This software is not intended for medical use. */ +// #ifndef PULSE_SENSOR_PLAYGROUND_H +// #define PULSE_SENSOR_PLAYGROUND_H +// #endif #include /* @@ -102,17 +105,17 @@ unsigned long PulseSensor::getLastBeatTime() { return lastBeatTime; } -boolean PulseSensor::sawStartOfBeat() { +bool PulseSensor::sawStartOfBeat() { // Disable interrupts to avoid a race with the ISR. DISABLE_PULSE_SENSOR_INTERRUPTS; - boolean started = QS; + bool started = QS; QS = false; ENABLE_PULSE_SENSOR_INTERRUPTS; return started; } -boolean PulseSensor::isInsideBeat() { +bool PulseSensor::isInsideBeat() { return Pulse; } @@ -122,12 +125,8 @@ void PulseSensor::readNextSample() { } void PulseSensor::processLatestSample() { - // Serial.println(threshSetting); - // Serial.print('\t'); - // Serial.println(thresh); sampleCounter += sampleIntervalMs; // keep track of the time in mS with this variable N = sampleCounter - lastBeatTime; // monitor the time since the last beat to avoid noise - // Fade the Fading LED FadeLevel = FadeLevel - FADE_LEVEL_PER_SAMPLE; FadeLevel = constrain(FadeLevel, 0, MAX_FADE_LEVEL); diff --git a/src/utility/PulseSensor.h b/src/utility/PulseSensor.h index 0c68114..4e23f47 100644 --- a/src/utility/PulseSensor.h +++ b/src/utility/PulseSensor.h @@ -45,10 +45,10 @@ class PulseSensor { int getInterBeatIntervalMs(); // Reads and clears the 'saw start of beat' flag, "QS". - boolean sawStartOfBeat(); + bool sawStartOfBeat(); // Returns true if this PulseSensor signal is inside a beat vs. outside. - boolean isInsideBeat(); + bool isInsideBeat(); // Returns the latest amp value. int getPulseAmplitude(); @@ -84,8 +84,8 @@ class PulseSensor { volatile int BPM; // int that holds raw Analog in 0. updated every call to readSensor() volatile int Signal; // holds the latest incoming raw data (0..1023) volatile int IBI; // int that holds the time interval (ms) between beats! Must be seeded! - volatile boolean Pulse; // "True" when User's live heartbeat is detected. "False" when not a "live beat". - volatile boolean QS; // The start of beat has been detected and not read by the Sketch. + volatile bool Pulse; // "True" when User's live heartbeat is detected. "False" when not a "live beat". + volatile bool QS; // The start of beat has been detected and not read by the Sketch. volatile int FadeLevel; // brightness of the FadePin, in scaled PWM units. See FADE_SCALE volatile int threshSetting; // used to seed and reset the thresh variable volatile int amp; // used to hold amplitude of pulse waveform, seeded (sample value) @@ -100,7 +100,7 @@ class PulseSensor { int P; // used to find peak in pulse wave, seeded (sample value) int T; // used to find trough in pulse wave, seeded (sample value) int thresh; // used to find instant moment of heart beat, seeded (sample value) - boolean firstBeat; // used to seed rate array so we startup with reasonable BPM - boolean secondBeat; // used to seed rate array so we startup with reasonable BPM + bool firstBeat; // used to seed rate array so we startup with reasonable BPM + bool secondBeat; // used to seed rate array so we startup with reasonable BPM }; #endif // PULSE_SENSOR_H diff --git a/src/utility/PulseSensorSerialOutput.cpp b/src/utility/PulseSensorSerialOutput.cpp index 625e0d0..6195544 100644 --- a/src/utility/PulseSensorSerialOutput.cpp +++ b/src/utility/PulseSensorSerialOutput.cpp @@ -42,20 +42,20 @@ void PulseSensorSerialOutput::outputSample(PulseSensor sensors[], int numSensors case SERIAL_PLOTTER: if (numSensors == 1) { pOutput->print(sensors[0].getBeatsPerMinute()); - pOutput->print(F(",")); + pOutput->print(','); pOutput->print(sensors[0].getInterBeatIntervalMs()); - pOutput->print(F(",")); - pOutput->println(sensors[0].getLatestSample()); + pOutput->print(','); + pOutput->print(sensors[0].getLatestSample()); } else { for (int i = 0; i < numSensors; ++i) { if (i != 0) { - pOutput->print(F(",")); + pOutput->print(','); } pOutput->print(sensors[i].getLatestSample()); // Could output BPM and IBI here. } - pOutput->println(); } + pOutput->println(); break; case PROCESSING_VISUALIZER: diff --git a/src/utility/PulseSensorTimingStatistics.cpp b/src/utility/PulseSensorTimingStatistics.cpp index a70b5b3..6e3462a 100644 --- a/src/utility/PulseSensorTimingStatistics.cpp +++ b/src/utility/PulseSensorTimingStatistics.cpp @@ -17,6 +17,9 @@ This software is not intended for medical use. */ +// #ifndef PULSE_SENSOR_PLAYGROUND_H +// #define PULSE_SENSOR_PLAYGROUND_H +// #endif #include PulseSensorTimingStatistics::PulseSensorTimingStatistics( diff --git a/src/utility/SelectTimer.h b/src/utility/SelectTimer.h new file mode 100644 index 0000000..d51797d --- /dev/null +++ b/src/utility/SelectTimer.h @@ -0,0 +1,85 @@ +/* + We will use pre-processor directives to determine the target hardware architecture + and based on that we will define USE_HARDWARE_TIMER true or false accordingly. + + In the event that hardware timer interrupts are supported, + USE_HARDWARE_TIMER will be defined true, and the compilation process will proceed without warnings. + + In the event that hardware timer interrupts are not supported, + USE_HARDWARE_TIMER will be defined false, and a warning saying this will be printed during compiliation. + + These are the different architectures, and the boards that they cover. + + ARDUINO_ARCH_AVR + ATmega328P + Arduino UNO, Arduino Duemilinova, Arduino Pro Mini, Adafruit Metro, + ATmega32u4, ATmega16u4 + Leonardo, Lily Pad, Sparkfun Pro Micro, Adafruit Feather 32u4, etc. + ATmega168 + Arduino NG (Vintage Arduino) + ATmega2560, ATmega1280 + Arduino Mega + ATtiny85 + Digispark, Adafruit Trinket (old version), etc + + ARDUINO_ARCH_RENESAS + RA4M1 Core-M4 + Arduino UNO R4 Minima + Arduino UNO R4 Wifi + + ARDUINO_SAM_DUE + AT91SAM3X8E Core-M3 + Ardino DUE + + ARDUINO_ARCH_RP2040 + RP2040 + Raspi Pico + Adafruit Feather RP2040 + Any board with RP2040 + + ARDUINO_NRF52_ADAFRUIT + nRF52840 Bluetooth Family + Adafruit and Seeed nRF52 boards (non EMBED boards yet!) + + ARDUINO_ARCH_ESP32 + ESP32 Family + Adafruit Feather ESP32-S2 + Needs testing on other family members + + ARDUINO_ARCH_SAMD + ATSAMD21 Core-M0 + Adafruit Feather M0 + ATSAMD51 Core-M4 + Adafruit Meather M4 + + ARDUINO_ARCH_ESP8266 + ESP8266 Family + Adafruit Feather Huzzah + + +*/ + +#ifndef SELECT_TIMER_H +#define SELECT_TIMER_H + + +#if defined(ARDUINO_ARCH_AVR) || defined(ARDUINO_ARCH_RENESAS) || defined(ARDUINO_ARCH_RP2040)\ +|| defined(ARDUINO_SAM_DUE) || defined(ARDUINO_NRF52_ADAFRUIT) || defined(ARDUINO_ARCH_ESP32)\ +|| defined(ARDUINO_SAMD_ZERO) || defined(ARDUINO_ARCH_SAMD) || defined(ARDUINO_ARCH_ESP8266) +#define USE_HARDWARE_TIMER true + // #warning "Using hardware timer to sample PulseSensor." +#else +#define USE_HARDWARE_TIMER false + #warning "Using software timer to sample PulseSensor." +#endif + +// Macros to link to interrupt disable/enable only if they exist +#if defined ARDUINO_ARCH_AVR +#define DISABLE_PULSE_SENSOR_INTERRUPTS cli() +#define ENABLE_PULSE_SENSOR_INTERRUPTS sei() +#else +#define DISABLE_PULSE_SENSOR_INTERRUPTS +#define ENABLE_PULSE_SENSOR_INTERRUPTS +#endif + +#endif // SELECT_TIMER include guard \ No newline at end of file diff --git a/src/utility/TimerHandler.h b/src/utility/TimerHandler.h new file mode 100644 index 0000000..9a8e6e9 --- /dev/null +++ b/src/utility/TimerHandler.h @@ -0,0 +1,233 @@ + +#ifndef TIMERHANDLER_H +#define TIMERHANDLER_H + +/* + Interrupt handling functions for PulseSensors. + See https://www.pulsesensor.com to get started. + + Copyright World Famous Electronics LLC - see LICENSE + Contributors: + Joel Murphy, https://pulsesensor.com + Yury Gitman, https://pulsesensor.com + Bradford Needham, @bneedhamia, https://bluepapertech.com + + Licensed under the MIT License, a copy of which + should have been included with this software. + + This software is not intended for medical use. +*/ + +/* + This library will determine the value of USE_HARDWARE_TIMER: + 1) #define USE_HARDWARE_TIMER true - if using interrupts; + 2) #define USE_HARDWARE_TIMER false - if not using interrupts. + + Preprocessor directives will determine if the library supports + hardware interrupts for the target hardware platform. + +*/ + + + +// SAVED FOR FUTURE SUPPORT OF TEENSY INTERRUPTS +#if defined(__MK66FX1M0__)||(__MK64FX512__)||(__MK20DX256__)||(__MK20DX128__) +// #include +#endif + + +/* + We create the Interrupt Service Routine only if the library knows + how to engage them. + + We define the ISR that handles the timer that + PulseSensorPlaygroundSetupInterrupt() set up. + NOTE: Make sure that this ISR uses the appropriate timer for + the platform detected by PulseSensorPlaygroundSetupInterrupt(). +*/ + #if defined(__AVR_ATmega328P__) || defined(__AVR_ATmega168__) || defined(__AVR_ATmega32U4__) || defined(__AVR_ATmega16U4__) || defined(__AVR_ATtiny85__) + #if defined Servo_h + #if defined(__AVR_ATmega328P__) || defined(__AVR_ATmega168__) + #ifndef TIMER_VECTOR + #define TIMER_VECTOR + ISR(TIMER2_COMPA_vect) + { + DISABLE_PULSE_SENSOR_INTERRUPTS; // disable interrupts while we do this + + PulseSensorPlayground::OurThis->onSampleTime(); + + ENABLE_PULSE_SENSOR_INTERRUPTS; // enable interrupts when you're done + } + #endif + #elif defined(__AVR_ATmega32U4__) || defined(__AVR_ATmega16U4__) + #ifndef TIMER_VECTOR + #define TIMER_VECTOR + ISR(TIMER3_COMPA_vect) + { + DISABLE_PULSE_SENSOR_INTERRUPTS; // disable interrupts while we do this + + PulseSensorPlayground::OurThis->onSampleTime(); + + ENABLE_PULSE_SENSOR_INTERRUPTS; // enable interrupts when you're done + } + #endif + #endif + #else + #ifndef TIMER_VECTOR + #define TIMER_VECTOR + ISR(TIMER1_COMPA_vect) + { + DISABLE_PULSE_SENSOR_INTERRUPTS; // disable interrupts while we do this + + PulseSensorPlayground::OurThis->onSampleTime(); + + ENABLE_PULSE_SENSOR_INTERRUPTS; // enable interrupts when you're done + } + #endif + #endif + #endif + + #if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) + #if defined Servo_h + #ifndef TIMER_VECTOR + #define TIMER_VECTOR + ISR(TIMER1_COMPA_vect) + { + DISABLE_PULSE_SENSOR_INTERRUPTS; // disable interrupts while we do this + + PulseSensorPlayground::OurThis->onSampleTime(); + + ENABLE_PULSE_SENSOR_INTERRUPTS; // enable interrupts when you're done + } + #endif + #else + #ifndef TIMER_VECTOR + #define TIMER_VECTOR + ISR(TIMER2_COMPA_vect) + { + DISABLE_PULSE_SENSOR_INTERRUPTS; // disable interrupts while we do this + + PulseSensorPlayground::OurThis->onSampleTime(); + + ENABLE_PULSE_SENSOR_INTERRUPTS; // enable interrupts when you're done + } + #endif + #endif + #endif + + #if defined(ARDUINO_ARCH_RENESAS) + #include "FspTimer.h" + FspTimer sampleTimer; + void sampleTimerISR(timer_callback_args_t __attribute((unused)) *p_args){ + PulseSensorPlayground::OurThis->onSampleTime(); + } + #endif + + + #if defined(ARDUINO_SAM_DUE) + /* + Include the DueTimer library. If you don't have it, use library manager to get it. + You can also find it at https://github.com/ivanseidel/DueTimer + If you use the Servo library, probably want to include that before this inlude. Just sayin'... + This will grab the next available timer and call it sampleTimer for use throughout the code + */ + #include + DueTimer sampleTimer = Timer.getAvailable(); + void sampleTimer_ISR(){ + PulseSensorPlayground::OurThis->onSampleTime(); + } + #endif + + #if defined(ARDUINO_ARCH_RP2040) + /* + Include the TimerInterrupt library + https://github.com/khoih-prog/RPI_PICO_TimerInterrupt + Set the sample rate to 500Hz + */ + #include "RPi_Pico_TimerInterrupt.h" + #define SAMPLE_INTERVAL_US 2000L + RPI_PICO_Timer sampleTimer(0); // the paramater may need to change, depending? + bool sampleTimer_ISR(struct repeating_timer *t){ + (void) t; + PulseSensorPlayground::OurThis->onSampleTime(); + return true; + } + #endif + + #if defined(ARDUINO_NRF52_ADAFRUIT) + /* + If you are using an Adafruit or Seeed nRF52 platform, + use this library and ISR + */ + #include "NRF52TimerInterrupt.h" + #define TIMER3_INTERVAL_US 2000 // critical fine tuning here! + NRF52Timer sampleTimer(NRF_TIMER_3); + void Timer3_ISR(){ + PulseSensorPlayground::OurThis->onSampleTime(); + } + #endif + + #if defined(ARDUINO_ARCH_ESP32) + /* + The hw_timer_t variable type and all the timer tools + come in the board download so we don't need an external library + */ + hw_timer_t *sampleTimer = NULL; + portMUX_TYPE sampleTimerMux = portMUX_INITIALIZER_UNLOCKED; + void IRAM_ATTR onInterrupt() { + portENTER_CRITICAL_ISR(&sampleTimerMux); + PulseSensorPlayground::OurThis->onSampleTime(); + portEXIT_CRITICAL_ISR(&sampleTimerMux); + } + #endif + + #if defined(ARDUINO_ARCH_ESP8266) + /* + If you are using ESP8266, include the TimerInterrupt library + https://github.com/khoih-prog/ESP8266TimerInterrupt + */ + #define USING_TIM_DIV16 true + #include "ESP8266TimerInterrupt.h" + + void IRAM_ATTR onInterrupt(){ + PulseSensorPlayground::OurThis->onSampleTime(); + } + ESP8266Timer sampleTimer; + + #endif + + #if defined(ARDUINO_SAMD_ZERO) || defined(ARDUINO_ARCH_SAMD) + // These define's must be placed at the beginning before #include "SAMDTimerInterrupt.h" + // _TIMERINTERRUPT_LOGLEVEL_ from 0 to 4 + // Don't define _TIMERINTERRUPT_LOGLEVEL_ > 0. Only for special ISR debugging only. Can hang the system. + // Don't define TIMER_INTERRUPT_DEBUG > 2. Only for special ISR debugging only. Can hang the system. + #define TIMER_INTERRUPT_DEBUG 0 + #define _TIMERINTERRUPT_LOGLEVEL_ 1 + // Select only one to be true for SAMD21. Must must be placed at the beginning before #include "SAMDTimerInterrupt.h" + #define USING_TIMER_TC3 true // Only TC3 can be used for SAMD51 + #define USING_TIMER_TC4 false // Not to use with Servo library + #define USING_TIMER_TC5 false + #define USING_TIMER_TCC false + #define USING_TIMER_TCC1 false + #define USING_TIMER_TCC2 false // Don't use this, can crash on some boards + + #include "SAMDTimerInterrupt.h" + + #if USING_TIMER_TC3 + #define SELECTED_TIMER TIMER_TC3 + #endif + // Define selected SAMD timer and set up ISR + SAMDTimer sampleTimer(SELECTED_TIMER); + void onInterrupt(){ + PulseSensorPlayground::OurThis->onSampleTime(); + } + #endif + + + #if defined(__MK66FX1M0__)||(__MK64FX512__)||(__MK20DX256__)||(__MK20DX128__) + // Interrupts here for Teensy in future + #endif + + + +#endif // SANDBOX_H \ No newline at end of file