Giter Site home page Giter Site logo

Comments (13)

biomurph avatar biomurph commented on May 22, 2024

@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.

goldenhenry avatar goldenhenry commented on May 22, 2024

@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.

biomurph avatar biomurph commented on May 22, 2024

@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.

biomurph avatar biomurph commented on May 22, 2024

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.

goldenhenry avatar goldenhenry commented on May 22, 2024

@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.

biomurph avatar biomurph commented on May 22, 2024

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.

goldenhenry avatar goldenhenry commented on May 22, 2024

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.

goldenhenry avatar goldenhenry commented on May 22, 2024

#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.

biomurph avatar biomurph commented on May 22, 2024

@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.

goldenhenry avatar goldenhenry commented on May 22, 2024
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.

biomurph avatar biomurph commented on May 22, 2024

@goldenhenry
This is awesome.
I will roll it into our PusleSensor Playground Library.

from pulsesensorplayground.

goldenhenry avatar goldenhenry commented on May 22, 2024

@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.

biomurph avatar biomurph commented on May 22, 2024

@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)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo 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.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.