From 2dc05583581b156740afe4b18279d9909e49708c Mon Sep 17 00:00:00 2001 From: Joel Murphy Date: Mon, 30 Oct 2023 17:49:57 -0400 Subject: [PATCH 1/2] Adding support for Arduino Due --- examples/PulseSensor_DUE/PulseSensor_DUE.ino | 160 +++++++++++++++++++ src/PulseSensorPlayground.cpp | 6 + src/PulseSensorPlayground.h | 6 +- src/utility/Interrupts.h | 38 +++-- 4 files changed, 193 insertions(+), 17 deletions(-) create mode 100644 examples/PulseSensor_DUE/PulseSensor_DUE.ino diff --git a/examples/PulseSensor_DUE/PulseSensor_DUE.ino b/examples/PulseSensor_DUE/PulseSensor_DUE.ino new file mode 100644 index 0000000..4fc45cf --- /dev/null +++ b/examples/PulseSensor_DUE/PulseSensor_DUE.ino @@ -0,0 +1,160 @@ +/* + 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 + + 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. +*/ + +/* + 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. +*/ +#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. + + 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 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); + + // 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. 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. + */ + 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/src/PulseSensorPlayground.cpp b/src/PulseSensorPlayground.cpp index 0b8130d..85d3e0d 100644 --- a/src/PulseSensorPlayground.cpp +++ b/src/PulseSensorPlayground.cpp @@ -51,6 +51,12 @@ boolean PulseSensorPlayground::PulseSensorPlayground::begin() { for (;;); #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()) { diff --git a/src/PulseSensorPlayground.h b/src/PulseSensorPlayground.h index edf1a52..08d04b9 100644 --- a/src/PulseSensorPlayground.h +++ b/src/PulseSensorPlayground.h @@ -133,9 +133,9 @@ #if defined(ARDUINO_ARCH_NRF52) #include "Adafruit_TinyUSB.h" #endif -// #if defined(ARDUINO_ARCH_RENESAS) -// #include "FspTimer.h" -// FspTimer sampleTimer +// #if defined(ARDUINO_ARCH_SAM) +// #include +// DueTimer sampleTimer = Timer.getAvailable(); // #endif #include #include "utility/PulseSensor.h" diff --git a/src/utility/Interrupts.h b/src/utility/Interrupts.h index b05ae4d..351af3c 100644 --- a/src/utility/Interrupts.h +++ b/src/utility/Interrupts.h @@ -115,7 +115,7 @@ boolean PulseSensorPlaygroundEnableInterrupt(); */ boolean PulseSensorPlayground::UsingInterrupts = USE_ARDUINO_INTERRUPTS; -boolean PulseSensorPlaygroundSetupInterrupt() { +boolean PulseSensorPlaygroundSetupInterrupt(){ boolean result = false; #if !USE_ARDUINO_INTERRUPTS @@ -239,8 +239,8 @@ boolean PulseSensorPlaygroundSetupInterrupt() { #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)\ - ||(ARDUINO_ARCH_SAM) + ||(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. @@ -249,14 +249,18 @@ boolean PulseSensorPlaygroundSetupInterrupt() { #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_NRF52840)||(ARDUINO_ARCH_RENESAS)||(ARDUINO_ARCH_SAM) result = true; #endif - #if defined(ARDUINO_ARCH_RENESAS) - result = true; - #endif + // #if defined(ARDUINO_ARCH_RENESAS) + // result = true; + // #endif + + // #if defined(ARDUINO_ARCH_SAM) + // result = true; + // #endif #endif // USE_ARDUINO_INTERRUPTS @@ -326,6 +330,12 @@ boolean PulseSensorPlaygroundDisableInterrupt(){ #if defined(ARDUINO_ARCH_RENESAS) sampleTimer.stop(); + result = true; + #endif + + #if defined(ARDUINO_ARCH_SAM) + sampleTimer.stop(); + result = true; #endif #endif @@ -400,6 +410,12 @@ boolean PulseSensorPlaygroundEnableInterrupt(){ #if defined(ARDUINO_ARCH_RENESAS) sampleTimer.start(); + result = true; + #endif + + #if defined(ARDUINO_ARCH_SAM) + sampleTimer.start(2000); + result = true; #endif #endif @@ -474,13 +490,7 @@ return result; // unknown or unsupported platform. #endif #endif - // #if defined (ARDUINO_ARCH_NRF52840) - // void Timer3_ISR(){ - // // thisSample = micros(); - // // isrCounter++; - // PulseSensorPlayground::OurThis->onSampleTime(); - // } - // #endif + #if defined(__MK66FX1M0__)||(__MK64FX512__)||(__MK20DX256__)||(__MK20DX128__) // Interrupts not supported yet for Teensy From 28307b3e79443b6bd9b2d8e739b7059766e84f46 Mon Sep 17 00:00:00 2001 From: Joel Murphy Date: Mon, 30 Oct 2023 17:53:56 -0400 Subject: [PATCH 2/2] dusting lint --- src/PulseSensorPlayground.h | 4 ---- src/utility/Interrupts.h | 22 +--------------------- 2 files changed, 1 insertion(+), 25 deletions(-) diff --git a/src/PulseSensorPlayground.h b/src/PulseSensorPlayground.h index 08d04b9..2d032fc 100644 --- a/src/PulseSensorPlayground.h +++ b/src/PulseSensorPlayground.h @@ -133,10 +133,6 @@ #if defined(ARDUINO_ARCH_NRF52) #include "Adafruit_TinyUSB.h" #endif -// #if defined(ARDUINO_ARCH_SAM) -// #include -// DueTimer sampleTimer = Timer.getAvailable(); -// #endif #include #include "utility/PulseSensor.h" #if USE_SERIAL diff --git a/src/utility/Interrupts.h b/src/utility/Interrupts.h index 351af3c..2bdb702 100644 --- a/src/utility/Interrupts.h +++ b/src/utility/Interrupts.h @@ -73,12 +73,7 @@ #define DISABLE_PULSE_SENSOR_INTERRUPTS cli() #define ENABLE_PULSE_SENSOR_INTERRUPTS sei() #endif - -// #if defined (ARDUINO_ARCH_NRF52840) -// #define TIMER1_INTERVAL_US 2000 // critical fine tuning here! -// NRF52Timer nRF52_Timer(NRF_TIMER_3); -// #endif - + // SAVED FOR FUTURE SUPPORT OF TEENSY INTERRUPTS #if defined(__MK66FX1M0__)||(__MK64FX512__)||(__MK20DX256__)||(__MK20DX128__) // #include @@ -254,14 +249,6 @@ boolean PulseSensorPlaygroundSetupInterrupt(){ result = true; #endif - // #if defined(ARDUINO_ARCH_RENESAS) - // result = true; - // #endif - - // #if defined(ARDUINO_ARCH_SAM) - // result = true; - // #endif - #endif // USE_ARDUINO_INTERRUPTS return result; @@ -496,13 +483,6 @@ return result; // unknown or unsupported platform. // Interrupts not supported yet for Teensy #endif - // #if defined(ARDUINO_ARCH_RENESAS) - // void sampleTimerISR(timer_callback_args_t __attribute((unused)) *p_args) - // { - // PulseSensorPlayground::OurThis->onSampleTime(); - // } - // #endif - #endif // USE_ARDUINO_INTERRUPTS