trying to make pulse sensor amped restful using yun

Hello, I am trying to make this pulsesensor restful.
I just took the sample code from the website for my arduino yun.I modified it in the restful manner using bridge lib , yun server and yun client. The compiler returns no error messages. when i call the website e.g. localhost/arduino/heartbeat , nothing returns. What could be the problem? Timing Problem between server request and interrupt?I am very thankful for solutions ! Kris

My modified code:

#include <Bridge.h>
#include <YunServer.h>
#include <YunClient.h>

YunServer server;

// VARIABLES
int pulsePin = 0; // Pulse Sensor purple wire connected to analog pin 0
int blinkPin = 13; // pin to blink led at each beat
int fadePin = 5; // pin to do fancy classy fading blink at each beat
int fadeRate = 0; // used to fade LED on with PWM on fadePin

// these variables are volatile because they are used during the interrupt service routine!
volatile int BPM; // used to hold the pulse rate
volatile int Signal; // holds the incoming raw data
volatile int IBI = 600; // holds the time between beats, the Inter-Beat Interval
volatile boolean Pulse = false; // true when pulse wave is high, false when it's low
volatile boolean QS = false; // becomes true when Arduoino finds a beat.

void setup(){
pinMode(blinkPin,OUTPUT); // pin that will blink to your heartbeat!
pinMode(fadePin,OUTPUT); // pin that will fade to your heartbeat!
Serial.begin(115200); // we agree to talk fast!

Bridge.begin();

server.listenOnLocalhost();
server.begin();
interruptSetup();
}

void loop(){

// Get clients coming from server
YunClient client = server.accept();

// There is a new client?
if (client) {
// Process request
process(client);

// Close connection and free resources.
client.stop();

}
}

void process(YunClient client) {

String command = client.readStringUntil('/');

if (command == "heartbeat") {
heartbeatCommand(client);
}
}

void heartbeatCommand(YunClient client) {

sendDataToProcessing('S', Signal); // send Processing the raw Pulse Sensor data
if (QS == true){ // Quantified Self flag is true when arduino finds a heartbeat
fadeRate = 255; // Set 'fadeRate' Variable to 255 to fade LED with pulse
sendDataToProcessing('B',BPM);
client.print('B:'+BPM); // send heart rate with a 'B' prefix
sendDataToProcessing('Q',IBI); // send time between beats with a 'Q' prefix
client.print('Q:'+IBI);
QS = false; // reset the Quantified Self flag for next time
}
ledFadeToBeat();
delay(50); // Poll every 50ms

}

void ledFadeToBeat(){
fadeRate -= 15; // set LED fade value
fadeRate = constrain(fadeRate,0,255); // keep LED fade value from going into negative numbers!
analogWrite(fadePin,fadeRate); // fade LED
}

void sendDataToProcessing(char symbol, int data ){
Serial.print(symbol); // symbol prefix tells Processing what type of data is coming
Serial.println(data); // the data to send culminating in a carriage return
}

and the interrupt, where I didnt change anything:

volatile int rate[10];                    // used to hold last ten IBI values
volatile unsigned long sampleCounter = 0;          // used to determine pulse timing
volatile unsigned long lastBeatTime = 0;           // used to find the inter beat interval
volatile int P =512;                      // used to find peak in pulse wave
volatile int T = 512;                     // used to find trough in pulse wave
volatile int thresh = 512;                // used to find instant moment of heart beat
volatile int amp = 100;                   // used to hold amplitude of pulse waveform
volatile boolean firstBeat = true;        // used to seed rate array so we startup with reasonable BPM
volatile boolean secondBeat = true;       // used to seed rate array so we startup with reasonable BPM


void interruptSetup(){     
  
  TCCR1A = 0x00;
  TCCR1B = 0x0C;
  OCR1A = 0x7C;
  TIMSK1 = 0x02;



  sei();                
} 


// THIS IS THE TIMER 2 INTERRUPT SERVICE ROUTINE. 
// Timer 2 makes sure that we take a reading every 2 miliseconds
ISR(TIMER2_COMPA_vect){                         // triggered when Timer2 counts to 124
    cli();                                      // disable interrupts while we do this
    Signal = analogRead(pulsePin);              // read the Pulse Sensor 
    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
    digitalWrite(blinkPin,HIGH);                // turn on pin 13 LED
    IBI = sampleCounter - lastBeatTime;         // measure time between beats in mS
    lastBeatTime = sampleCounter;               // keep track of time for next pulse
         
         if(firstBeat){                         // if it's the first time we found a beat, if firstBeat == TRUE
             firstBeat = false;                 // clear firstBeat flag
             return;                            // IBI value is unreliable so discard it
            }   
         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;                      
                    }
            }
          
    // 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
      digitalWrite(blinkPin,LOW);            // turn off pin 13 LED
      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 = 512;                          // 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 = true;                     // when we get the heartbeat back
     }
  
  sei();                                     // enable interrupts when youre done!
}// end isr

kris20:
when i call the website e.g. localhost/arduino/heartbeat , nothing returns.

What do you mean by "nothing"? An empty response? An error response? No response (with the browser eventually timing out)?

What could be the problem?

I see something in your code that came up in a recent discussion: Why am I getting \u000d\u000a appended to only SOME of my String variables - Arduino Yún - Arduino Forum

You have this in your code:

void process(YunClient client) {
  
 String command = client.readStringUntil('/');
 
 if (command == "heartbeat") {
   heartbeatCommand(client);
 }
}

Given your URL that has no additional tokens after the "heartbeat" token, I suspect the same thing is happening to you that was happening to jverive in the link above: you are probably getting a CR/LF at the end of your command string. Therefore the string "heartbeat\r\n" that is probably in the command variable does not match "heartbeat" that is your comparison string. If that's the problem, adding a call to command.trim() should fix it.

Try this for your process() function:

void process(YunClient client) {
  
 String command = client.readStringUntil('/');

 // Remove any leading/trailing whitespace that might might confuse the comparison
 command.trim();
 
 if (command == "heartbeat") {
   heartbeatCommand(client);
 }
}

Thank you for your help ShapeShifter. I replaced my code with yours , it did not give me the desired output on the website. I am still getting a white blank webpage. Somehow the Server is serving the request, unless i guess there would occur an error message?

I just noticed that your URL starts with localhost. Is that what you are actually using, or are you actually using something like arduino.local? If you are actually using localhost then you are not accessing your arduino, you are sending the command to the local computer running the web browser.

Assuming the command is actually getting to your arduino, maybe you should add a little bit of debugging output. For example:

void process(YunClient client) {
 
 String command = client.readStringUntil('/');

 client.println("Inside process()");
 client.print("command = \"");
 client.print(command);
 client.println("\"");

 // Remove any leading/trailing whitespace that might might confuse the comparison
 command.trim();

 client.print("trimmed command = \"");
 client.print(command);
 client.println("\"");
 
 if (command == "heartbeat") {
  client.println("Got heartbeat command.");
   heartbeatCommand(client);
 }
}

I still suspect that your command is not being received properly, therefore heatbeatCommand() is not getting called, therefore nothing is being written to the client object, therefore you are getting a blank page.

With the added client.print() calls, there should always be some output sent back to the web browser, and you can see what is actually happening. If you get some output, please post it.

If it's still blank after this, then it's likely the YunServer is never returning a valid client connection? In that case, you will probably want to add some Serial.print() statements up there to see if you are ever getting a connection.

sorry , I used the wrong expression . Of course the url is the ip adress of yun and not localhost.
I tried your suggestions . I got the attached output but only when I commented out the interruptSetup(); function!!! The function is placed in the setup part. Do you think , i should put the funktion smewhere elso or might there be clashes between the timing of the server -client part and the interruptSetup() ??? Thanks

output.JPG

Another issue is my usb port. It cannot recognize my yun borad (cant load usb drivers). I already tried it on other computers , without any change or improvement. So i cant use the serial terminal for whatever reason.

kris20:
I got the attached output

So it looks like the client connection is being made, the command string is being received (with a trailing CR/LF as I suspected), and the trim() function is cleaning up the command string so it is as expected. However, it's very curious that the next if statement is being executed? The "Got heartbeat command" line isn't printed out, and the heartbeatCommand() function is not getting called. This explains your previously empty response. But why, when the command string is "heartbeat" does (command == "heartbeat") evaluate fails?

Very odd... Does anyone see what I'm missing?

but only when I commented out the interruptSetup(); function!!!

It sounds to me like once you enable interrupts, they are taking up so much of the processing time that there isn't time left to process the regular loop. I've had this happen when the ISR code takes longer to run than the time between interrupts, and also when the ISR is not hooked up to the proper vector: the device generates an interrupt, but because no ISR is connected the proper vector, no code gets executed, the interrupt never gets serviced, and the device is constantly interrupt the processor and not letting it do anything else.

I strongly suspect there is something wrong in the interrupt code.

kris20:
and the interrupt, where I didnt change anything:

Well, then, that's the problem. Did you read the read the ReadMe for the code repository?

(Advanced) Timer Interrupt Notes / PWM on Pin 3 & 11

There is a tab in the Arduino code called Timer_Interrupt_Notes. This page describes how to set up the Timer interrupt depending on which version of Arduino you are using, and what other things you may want to do with your sketch. Please read it carefully!

PWM on pins 3 and 11 will not work when using this code, because we are using Timer 2!

The page and the video shows the code being used on an Uno. The Yun has a different processor chip than the Uno, so changes must be made to make it work. Did you read the Timer_Interrupt_Notes? The Yun has the same processor as the Lenoardo. At a quick glance it looks like this line of code:

ISR(TIMER2_COMPA_vect){                         // triggered when Timer2 counts to 124

Needs to be:

ISR(TIMER1_COMPA_vect){                         // triggered when Timer1 counts to 124

There may be other changes that are also needed, you'll have to read all of the documentation carefully. When you update that line of code, you probably should update the associated comments to note the change from timer 2 to time 1.

u were right . I missed to change timer2 to timer1 :slight_smile: .

Now the server accepts the client, without commenting out interruptSetup();

I mean the interruptSetup(); is now on the former place running and i am getting the attached output!

But the heartbeat data is still missing.

output.JPG

There is still the problem that "heartbeat" != "heartbeat"

I don't know why that IF statement is not true. How can that be?

The If statement must be true ,isnt it?, otherwise could not access the url : " http://192.168.2.101/arduino/heartbeat " ??

kris20:
The If statement must be true ,isnt it?, otherwise could not access the url : " http://192.168.2.101/arduino/heartbeat " ??

No.

The initial /arduino/ token means that the Linux side web server should route the request to the sketch. As long as the sketch has a YunServer that accepts the connection, the URL will be accepted. The YunServer object creates a YunClient object, which is passed everything in the URL after the /arduino/ token. At that point, the URL request is successful: it's up to the sketch to read from the YunClient object to get the rest of the URL, and anything that is written to the YunClient object will be sent back as the response to the request when the YunClient is closed by calling client.stop().

So, as long as the YunClient is accepted and finally closed, the request for the URL will be successful, at least from the requester's point of view.

What is happening is that you are reading the "heartbeat" portion of the URL, but because that IF statement is saying it is not "heartbeat" (even though printing out the value says that it is) that section of the code is not being executed. Therefore, no heartbeat information is returned as part of the request.

You were initially getting an empty response, because nothing at all was getting written to the client due to the failed IF statement. We added some additional debug output to the client, and that is being sent properly, but the IF statement is still not true, so you get no heartbeat data.

First of all, thank you so much for your help ShapeShifter!!!

I just saw ,
while I tried to find the error, I commented out that function :slight_smile:
---> heartbeatCommand(client);

Now I am getting the attached output, but i dont know ,how to interpret the number!
When I refresh the website the number is changing too.

output2.JPG

ok with "println" i am getting the attached output for IBI and Bpm.

I will paly arround with it :slight_smile:

Unbenannt.JPG

kris20:
while I tried to find the error, I commented out that function :slight_smile:

Well, that would explain it!

Yes, you're going to need println (or manually print some other delimiter.)

I notice this in your code:

      sendDataToProcessing('B',BPM);
    client.print('B:'+BPM);             // send heart rate with a 'B' prefix
       sendDataToProcessing('Q',IBI);   // send time between beats with a 'Q' prefix
      client.print('Q:'+IBI);

You are using single quotes for the tag in sendDataToProcessing(), which is correct since the function is expecting a single character. But you should use double quotes for "B:" and "Q:" when you print to the client object.

The single quotes are character literals, you want to use double quotes to make it a string. As a character literal, it's taking those two characters, making an int out of their ASCII values, and then adding that integer value to your data value. It is the combined sum that is being output, hence your strange large numbers with no prefix.

By using double quotes, you are making it a string (actually a character array.) A character array does not support concatenation with the "+" operator. You could convert it into a string object, and then use concatenation after converting the number to a string, but the easiest solution is probably:

    sendDataToProcessing('B',BPM);    // send heart rate with a 'B' prefix
    client.print("B:");
    client.println(BPM);

    sendDataToProcessing('Q',IBI);   // send time between beats with a 'Q' prefix
    client.print("Q:");
    client.println(IBI);

you are right !!! Now,its working fine!!! Thanks a lot ShapeShifter

Great news! 8)

Please edit your first post in this thread, and add [Solved] to the subject line.