Comments (13)
@goldenhenry
First, please don't post your SSID and PASSWORD on this page.
Second, it looks like you are using some timer interrupts on the ESP32! Super cool! Can you point me to where you are working from to make that happen? I can help you work out the timer interrupts for Pulse Sensor so that we can improve the PulseSensor Playground for everyone.
It looks like you are using a different code base than the one that this repo holds, FYI.
You are using the onTimer to call getPulse() every 2mS, which is how we do it here at Pulse Sensor. However, when you go into the getPulse() you are NOT taking a sample of the Pulse Sensor. Instead, you are sampling the Pulse Sensor signal in the loop() function, and that seems to be happening once every 20mS. Far too slow for the getPulse routine to make any sense out of the signal. Try putting the analogRead(pulsePin) at the top of the getPulse() and see if that helps?
from pulsesensorplayground.
@biomurph Thanks for you reply.
I actually used a coniferconifer/ESP32_pulsesensor and modify it. This coding is working when it is just using pulse sensor and mpu6050 gyro+accelerometer. However, when I add in wifi related coding the esp keep rebooting. At last, I found out the interrupt function of esp32 cannot include "analogread()" which will cause this error:
Guru Meditation Error: Core 1 panic'ed (Cache disabled but cached memory region accessed)
Register dump:
PC : 0x400812d1 PS : 0x00060034 A0 : 0x800810af A1 : 0x3ffc0b70
A2 : 0x00000023 A3 : 0x000000c0 A4 : 0x8008244d A5 : 0x3ffd0960
A6 : 0x00000000 A7 : 0x00000001 A8 : 0x3f40226c A9 : 0x000000c0
A10 : 0xbad00bad A11 : 0x00000018 A12 : 0x80082a7e A13 : 0x3ffd0940
A14 : 0x00000001 A15 : 0x3ffc3afc SAR : 0x00000014 EXCCAUSE: 0x00000007
EXCVADDR: 0x00000000 LBEG : 0x4000c2e0 LEND : 0x4000c2f6 LCOUNT : 0x00000000
Backtrace: 0x400812d1:0x3ffc0b70 0x400810ac:0x3ffc0b90 0x40081225:0x3ffc0bb0 0x40080ec1:0x3ffc0bd0 0x4008163d:0x3ffc0bf0 0x40081b19:0x3ffc0c10 0x4000bfed:0x00000000
Rebooting...
ets Jun 8 2016 00:22:57
rst:0x3 (SW_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT)
configsip: 0, SPIWP:0xee
clk_drv:0x00,q_drv:0x00,d_drv:0x00,cs0_drv:0x00,hd_drv:0x00,wp_drv:0x00
mode:DIO, clock div:1
load:0x3fff0018,len:4
load:0x3fff001c,len:956
load:0x40078000,len:0
load:0x40078000,len:13076
entry 0x40078a58
Hence, no matter what I did, if I need to "analogread()" during the interrupt routine, this error will occur. My own stupid solution will be keep sampling the signal at the loop, but processing the signal at the interrupt service routine. So, this prevents the error.
I found out that the firebase.get is lagging me. I took it out, then I can get quite consistent pulserate.
As you say, I do this
Signal = analogRead(pulsePin) / 4;
in my loop. Then I will process it at interrupt service routine every 2ms, but using the signal get maybe last few ms. So I not sure, by getting old signal (get from few ms ago), will it affected my result badly?
from pulsesensorplayground.
@goldenhenry
Interesting
I will say again that you are using an old code base for Pulse Sensor. Go to the PulseSensor Playground repo under World Famous Electronics. There you will find library examples that don't use interrupts. They use a software timer instead which will make your work easier.
from pulsesensorplayground.
Oh nevermind about the code base comment 😂
Pulsesensor playground does have tools to check your timing, so you can get a sense of your sample jitter. The tools are in the PulseSensorTimingStatistics. That can help you get a baseline of how much jitter you get with your setup
from pulsesensorplayground.
@biomurph Software timer? Are you saying Free RTOS?
I just checked the PulseSensorTimingStatistics but I dont think I understand how it works. Is it I just call the function inside the .cpp?
from pulsesensorplayground.
Oh, no I don't mean RTOS. By software timer, I mean that when the _Alternative code us run, it doesn't use the hardware timer. Instead, it compares the current program time in micros with the previous sample time, and if it's time (or past time) to sample (once every 2mS) then a sample is taken. Simple software timing function. The fact that is uses measured time could cause some jitter in the actual sample timing, since it could be some microseconds later than the actual 2mS.
We don't really have a TimingStatistics example. That's something that I should work out...
@bneedhamia if you can, would you help out with some code that implements functions from the timing statistics portion of the library? I will be able to stab at it in a couple of days.
from pulsesensorplayground.
I see. I know what you're saying now. I did test that code. It is working when there is less task in the loop. My tasks consider too much. Hence, I got too far inaccurate reading like 200++bpm consistently.
from pulsesensorplayground.
#late#update
I was managed to get the BPM reading as accurate as using UNO timer interrupt. In ESP32, I have to call the whole interrupt function externally by using
void IRAM_ATTR getpulse()
This function name will able to read the analog every time the timer interrupt get triggered without error I mentioned above.
I have a new problem which I should not ask here, but I still want to try to get answer here. I searched through INTERNET but I cannot find any datasheet of pulsesensor, is there any keyword needed other than "pulsesensor datasheet"?
from pulsesensorplayground.
@goldenhenry that is awesome! Can you share your working sketch that targets the ESP32 using hardware interrupts? Would love to roll that into the library or set up example code (attributing to you, of course).
The datasheet for the Pulse Sensor can be found here
https://github.com/WorldFamousElectronics/PulseSensorAmped_Hardware/blob/master/Pulse%20Sensor%20Data%20Sheet.pdf
from pulsesensorplayground.
int pulsePin = 34; // Pulse Sensor purple wire connected to analog pin 34 , ADC6
volatile int BPM; // int that holds raw Analog in 0. updated every 2mS
volatile int Signal; // holds the incoming raw data
volatile int IBI = 600; // int that holds the time interval between beats! Must be seeded!
volatile boolean Pulse = false; // "True" when User's live heartbeat is detected. "False" when not a "live beat".
volatile boolean QS = false; // becomes true when Arduino finds a beat.
volatile int rate[10]; // array to hold last ten IBI values
volatile unsigned long sampleCounter = 0; // used to determine pulse timing
volatile unsigned long lastBeatTime = 0; // used to find IBI
volatile int P = 512; // used to find peak in pulse wave, seeded
volatile int T = 512; // used to find trough in pulse wave, seeded
volatile int thresh = 530; // used to find instant moment of heart beat, seeded
volatile int amp = 0; // used to hold amplitude of pulse waveform, seeded
volatile boolean firstBeat = true; // used to seed rate array so we startup with reasonable BPM
volatile boolean secondBeat = false; // used to seed rate array so we startup with reasonable BPM
hw_timer_t * timertocheckpulse = NULL; //declare timer global variable
portMUX_TYPE timertocheckpulseMux = portMUX_INITIALIZER_UNLOCKED;
void IRAM_ATTR getPulse() //must called using IRAM_ATTR in ESP32 since it need to be called in ISR
{
Signal = analogRead(pulsePin) / 4; // read the Pulse Sensor, bits of ESP32 ADC ch is 4 times larger
sampleCounter += 2; // keep track of the time in mS with this variable
int N = sampleCounter - lastBeatTime; // monitor the time since the last beat to avoid noise
// find the peak and trough of the pulse wave
if (Signal < thresh && N > (IBI / 5) * 3) { // avoid dichrotic noise by waiting 3/5 of last IBI
if (Signal < T) { // T is the trough
T = Signal; // keep track of lowest point in pulse wave
}
}
if (Signal > thresh && Signal > P) { // thresh condition helps avoid noise
P = Signal; // P is the peak
} // keep track of highest point in pulse wave
// NOW IT'S TIME TO LOOK FOR THE HEART BEAT
// signal surges up in value every time there is a pulse
if (N > 250) { // avoid high frequency noise
if ( (Signal > thresh) && (Pulse == false) && (N > (IBI / 5) * 3) ) {
Pulse = true; // set the Pulse flag when we think there is a pulse
IBI = sampleCounter - lastBeatTime; // measure time between beats in mS
lastBeatTime = sampleCounter; // keep track of time for next pulse
if (secondBeat) { // if this is the second beat, if secondBeat == TRUE
secondBeat = false; // clear secondBeat flag
for (int i = 0; i <= 9; i++) { // seed the running total to get a realisitic BPM at startup
rate[i] = IBI;
}
}
if (firstBeat) { // if it's the first time we found a beat, if firstBeat == TRUE
firstBeat = false; // clear firstBeat flag
secondBeat = true; // set the second beat flag
sei(); // enable interrupts again
return; // IBI value is unreliable so discard it
}
// keep a running total of the last 10 IBI values
word runningTotal = 0; // clear the runningTotal variable
for (int i = 0; i <= 8; i++) { // shift data in the rate array
rate[i] = rate[i + 1]; // and drop the oldest IBI value
runningTotal += rate[i]; // add up the 9 oldest IBI values
}
rate[9] = IBI; // add the latest IBI to the rate array
runningTotal += rate[9]; // add the latest IBI to runningTotal
runningTotal /= 10; // average the last 10 IBI values
BPM = 60000 / runningTotal; // how many beats can fit into a minute? that's BPM!
QS = true; // set Quantified Self flag
// QS FLAG IS NOT CLEARED INSIDE THIS ISR
}
}
if (Signal < thresh && Pulse == true) { // when the values are going down, the beat is over
Pulse = false; // reset the Pulse flag so we can do it again
amp = P - T; // get amplitude of the pulse wave
thresh = amp / 2 + T; // set thresh at 50% of the amplitude
P = thresh; // reset these for next time
T = thresh;
}
if (N > 2500) { // if 2.5 seconds go by without a beat
thresh = 530; // set thresh default
P = 512; // set P default
T = 512; // set T default
lastBeatTime = sampleCounter; // bring the lastBeatTime up to date
firstBeat = true; // set these to avoid noise
secondBeat = false; // when we get the heartbeat back
QS = false;
BPM = 0;
IBI = 600; // 600ms per beat = 100 Beats Per Minute (BPM)
Pulse = false;
amp = 100; // beat amplitude 1/10 of input range.
}
}
NOTES:
//this is the Interrupt Service Routine
//the interrupt handling routine should have the IRAM_ATTR attribute, in order for the compiler to place the code in IRAM.
//Also, interrupt handling routines should only call functions also placed in IRAM,
void IRAM_ATTR onTimertocheckpulse() {
portENTER_CRITICAL_ISR(&timertocheckpulseMux);
getPulse();
portEXIT_CRITICAL_ISR(&timertocheckpulseMux);
}
void interruptSetup() { // CHECK OUT THE Timer_Interrupt_Notes TAB FOR MORE ON INTERRUPTS (i referred https://techtutorialsx.com/2017/10/07/esp32-arduino-timer-interrupts/)
timertocheckpulse = timerBegin(0, 80, true);
//1st input: Use 1st timer of 4 (counted from zero).
//2nd input: Set 80 divider for prescaler (most of the ESP32 running 80MHz as base frequency, prescaler of 80 make it every tick with 1microseconds, easy to determine the time later)
timerAttachInterrupt(timertocheckpulse, &onTimertocheckpulse, true);
//1st input: timer global variable
//2nd input: Attach onTimer function to correspond timer.
//3rd input: interrupt to be generated at edge(true) or level(false)
timerAlarmWrite(timertocheckpulse, 2000, true);
//1st input: timer to be started
//2nd input: as every tick is 1 microsecond, use 2000 to get timer total tick of 2miliseconds which is needed to check the signal evey 2ms
//3rd input: true(repeat the alarm automatically);false will make this timer one-time timer
timerAlarmEnable(timertocheckpulse);// Start the timer
}
void setup() {
Serial.begin(115200); // we agree to talk fast!
}
void loop() {
Serial.print(BPM);
Serial.print(",");
Serial.print(IBI);
Serial.print(",");
Serial.println(Signal);
if (QS == true) { // A Heartbeat Was Found
// BPM and IBI have been Determined
// Quantified Self "QS" true when arduino finds a heartbeat
QS = false; // reset the Quantified Self flag for next time
}
delay(20); // take a break
}
@biomurph Thanks for the datasheet! It helps! I wonder how it hides from Google.
This is my pulse sensor code target for ESP32.
I actually found this ESP32 Arduino IDE version for pulsesensor Amped when I started to code using Arduino IDE. As I mentioned, it could be run before I added Wi-Fi related codes. Hence, I modified it so it can interact with more codes such as running Wi-Fi or other sensors by referencing to ESP32 Arduino: Timer interrupts. The difference is calling
getpulse();
out of the ISR, and the function called should be declared as
void IRAM_ATTR getpulse()
{}
In this project, I didnt display the blinking LED or visualizer but only using Serial Plotter. However, it could be done by just adding the related code into part where the Pulse sensor Playground inserted. Hope these help.
from pulsesensorplayground.
@goldenhenry
This is awesome.
I will roll it into our PusleSensor Playground Library.
from pulsesensorplayground.
@biomurph
I'm sorry. I have to tag you again. https://github.com/WorldFamousElectronics/PulseSensorAmped_Hardware/blob/master/PulseSensorAmpd_1.sch
The schematic above cannot be opened by using Eagle. It says "line 1, column 1: Start tag expected."
I wonder can I get the schematic from you?
from pulsesensorplayground.
@goldenhenry the schematics were designed in Design Spark, not Eagle.
There is a PDF of the schematic linked to from here
https://pulsesensor.com/pages/open-hardware
from pulsesensorplayground.
Related Issues (20)
- PulseSensor Playground downloaded by Arduino doesn't work properly for Arduino Nano 33 BLE HOT 5
- Generating Comparable Amplitudes with Two Pulse Sensors HOT 2
- Can't acquire .sawStartOfBeat() HOT 19
- Adafruit Feather M0 WiFi with uFL - ATSAMD21 + ATWINC1500 - fw 19.4.4 HOT 8
- Generating RMSSD and SDNN from IBI, BPM data HOT 2
- Adafruit Feather nRF52832 Compatibility? HOT 4
- Do we need to set THRESHOLD twice when we use 2 pulsesensors at once? HOT 2
- USE_ARDUINO_INTERRUPTS problem with NodeMCU 0.9 HOT 3
- GETTING STARTED WORKING BUT NOT GETTING BPM TO MONITOR HOT 5
- Quick question, my smart meter has a pulse HOT 1
- Getting_BPM_to_monitor sketch displaying random values at 11250 baud HOT 8
- Node MCU V1 - Plotter is working but no BPM in Monitor HOT 3
- I'm getting high BPM HOT 1
- Code error for sp32 pulse sensor data to thinspeak HOT 19
- Compatibility issue Nano BLE ABX00034 HOT 6
- PulseSensr_BPM_Alternative Compilation Errors HOT 7
- turning off/on the bright light when needed HOT 2
- Can't compile even when including "USE_ARDUINO_INTERRUPTS true" HOT 14
- GETTING STARTED WORKING BUT NOT GETTING BPM TO MONITOR , ALTERNATIVE HOT 1
- No BPM readings HOT 3
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from pulsesensorplayground.