delay is disturbing the interrupt...

Hello,

anybody got me an idea? The sketch is workling but: I receive data from the arduino with an processing sketch. To receive the data correctly i need the delay in the arduino sketch.The communication between processing and arduino is workling realy good........but the delay is disturbing my arduino program because im using an interrupt. I´m measuring speed with my arduino sketch. so with the delay sometimes the interrupt can´t be triggered and my speed value seems strange. Without the delay, the data comming from the arduino is to fast to work with. Do you got an idea for a workaroud? Sorry for my english.

here´s my arduino sketch:

unsigned const int BAUD = 9600;
unsigned long timeold, rpm, delta;
float KMH, DIST, RAD= 0.165;


volatile int ticks;
void countRPM(){
  ticks++;
  rpm++;
  }

void setup() {

  Serial.begin(BAUD);
  attachInterrupt(0,countRPM, FALLING);
  ticks = 0;
  timeold = 0;
}

void loop() {

  if (ticks>=2){
  delta = millis () - timeold;
  timeold = millis () ;
  KMH = ((ticks * RAD)/ delta)*3600.0;
  ticks = 0;
  } 
  DIST = rpm * RAD ;

  //Serial.print("KMH:");
  Serial.print(KMH,1);
  Serial.print(",");
  Serial.print(DIST,1);
  Serial.println(",");
  Serial.println(); // CR/LF senden
  //delay(50);

}

Thank you!

I don’t have any suggestions, other than figure out what’s really causing your problem. It is NOT delay(). delay() has absolutely no affect whatsoever on interrupts.

Regards,
Ray L.

I always like to pick some low hanging fruit just to clear the view... variables ticks and rpm are simultaneously incremented and intitialized. So they are always the same. So why are there two variables?

rpm should be volatile since it is used inside and outside the ISR.

There is a lot of wheel spinning going on in this program (heh heh). Variable "delta" is almost completely determined by the interval set by the delay(). No wonder changing the delay breaks it.

And ticks is also used in both, so it should be volatile.

Since they are integers (more than one byte) then they need to be protected inside loop each time you read or write them so that the interrupt can't occur during the read or write.

volatile int ticks;
...
void loop() {
  int tick_local;
  noInterrupts();
  tick_local = ticks;  //safely get a copy of ticks
  interrupts();

  Serial.println(ticks_local);

  ...

  noInterrupts();
  ticks = 0;  //safely set ticks
  interrupts();

 }

Tick counting is the worst way to implement a tachometer anyway. The only advantage is that it averages, but there are better ways to do that. You should be measuring the time period between ticks instead.

If you want to go ahead and use tick counting, you still have a lot of dross to remove. You have redundant variables and redundant time handling which does nothing at all.

Your program has no internal or external documentation to explain how it is supposed to work.

Hey Thanks a lot for your help!

@RayLivingston:

I think you´re right but i´m very new to coding at all so it´s really hard to figure out the problem. I thought i red something that i can´t use delay() with interrupt but maybe this is only meant inside the interrupt service routine?

@Delta_G:

Thank you! I corrected this.

@aarg:

Yes there is a lot of wheel spinning ;-). It´s a skateboard speedometer. I´m using the variables ticks and rpm because at the end of every ISR ticks is set to 0 again.

"Variable "delta" is almost completely determined by the interval set by the delay(). No wonder changing the delay breaks it."

I´m sorry i´m shure if i understand this right...but i think this is where my problem is comming from.

I´m very new to this and i took me a long to time to get to this point so it´s sad to hear that i´m on the wrong way. But measuring the time period between ticks instead sounds realy good. Can you please give me a hint how to do this?

about my sketch:

This sketch is supposed to measure the rpm of a skateboard wheel with a hall sensor. It calculate the distance(265m) and the speed(17,6kmh) and send it via serial connection(bluetooth). It shoud send it as one string. The values with "," between and at the end of the line a linefeed (kmh,Dist,\n). I´m receiving this string via bluetooth with an processing android app. The processing sketch is seperating the string and is working with the values. In future i would link to send more than only two values from my arduino.

I hope i didn´t forget some info.

Thank you..

@MorganS:

Thanks a lot for your ideas!

I changed it to volatile.

But if i use noInterrupts(); couldn´t i miss an interrupt? i should not miss any interrupt if possible…

Thank you

You will only miss an interrupt when two interrupts happen while interrupts are disabled, if there is only one, it will be honored after being enabled again. For the very short period that a variable copy takes, two interrupts should be very unlikely.

Okay Thanks. I tried it like this:

unsigned const int BAUD = 9600;
unsigned long timeold, delta;
float KMH, RAD= 0.165;
long DIST;

volatile unsigned long ticks, rpm;
void countRPM(){
  ticks++;
  rpm++;
  }

void setup() {    
  Serial.begin(BAUD);
  attachInterrupt(0,countRPM, FALLING);
  ticks = 0;
  timeold = 0;
}
void loop(){
  int tick_local;
  noInterrupts();
  tick_local = ticks;  //safely get a copy of ticks
  interrupts();
  if (tick_local>=2){
  delta = millis () - timeold;
  timeold = millis () ;
  KMH = ((ticks * RAD)/ delta)*3600.0;
  ticks = 0;
} 
DIST = rpm * RAD ;
  
Serial.print(KMH,1);
Serial.print(",");
Serial.print(DIST,DEC);
Serial.print(",");
Serial.print(rpm,DEC);
Serial.print(","); // Beachten Sie, dass ein Komma nach dem letzten Feld gesendet wird
Serial.println(); // CR/LF senden
delay(100);
}

Sadly this didn´t help. As long as i use the delay(100) my value is going strange.

I think aarg is right that problem is comming from delta which is affected from a delay(if i get it right). I should measuring the time period between ticks instead of counting ticks.... but i don´t know how to do.

Can anybody give me a hint?

I tried to change my sketch. I would like to measure the time period between every interrupt. But i stuck. Somebody got an idea for me?

unsigned const int BAUD = 9600;
unsigned long timeold, delta;
float KMH, RAD= 0.165;
long DIST;
volatile unsigned long rpmtime;


void setup() {    
  Serial.begin(BAUD);
  attachInterrupt(0,countRPM, FALLING);
  ticks = 0;
  timeold = 0;
}
void loop(){
  if (ticks>=2){
  delta = millis () - timeold;
  timeold = millis () ;
  KMH = ((ticks * RAD)/ delta)*3600.0;
  ticks = 0;
} 
DIST = rpm * RAD ;
  
/*Serial.print("H");
Serial.print(",");
Serial.print(KMH,1);
Serial.print(",");
Serial.print(DIST,DEC);
Serial.print(",");
Serial.print(rpm,DEC);
Serial.print(","); // Beachten Sie, dass ein Komma nach dem letzten Feld gesendet wird
*/
Serial.println(); // CR/LF senden
Serial.print(rpmtime);
//delay(100);
}
void countRPM(){
  rpmtime = millis();
  
  }

This version never changes ticks, so the loop will never see ticks>2.

One way to do it is to use the ISR to save away the previous time and then store the new time. Then the main loop only needs to subtract the two, after taking a safe copy of course.

That will be very unsteady. The time measured will pick up lots of tiny variations in the speed and magnify them. It is better to count the time to do 10 revolutions or more. That will average out the unsteadiness. Try it both ways and see what you get.

At this point, your best hope is to look at someone else's working sketch. You are making a lot of minor changes and when you do, it is clear that you don't see how they connect with the overall architecture. I suggested measuring the intervals between ticks. What you did was sketch in the ISR, and nothing else. Then you threw in the towel and came back pleading for answers.

Here is my last answer. The interval between ticks decreases as the RPM (frequency) increases. It is a really common physics formula. t = 1/f. You now know t. Calculate f.

Instead of using delay i made it like this:

if(lastsend + 220<millis()){
  
lastsend = millis();

Serial.print("H");
Serial.print(",");
Serial.print(KMH,1);
Serial.print(",");
Serial.print(DIST,DEC);
Serial.print(",");
Serial.print(rpm,DEC);
Serial.print(","); // Beachten Sie, dass ein Komma nach dem letzten Feld gesendet wird
Serial.println(); // CR/LF senden
KMH = 0;
}

It´s workling now. Thank you!

@MorganS:

Thank you, your last post was exactly the thought i couldn´t catch;-). But finally counting ticks is better for me because i got many revolutions going on. Thanks

Just a note for future projects involving millis. You can not reliably do this:

if(lastsend + 220<millis()){

because the value of millis rolls over to zero every 49 days. It doesn’t matter for a skateboard speedometer (I guess 49 days of skateboarding would put you in the Guinness book of records!), but for some applications it can create a very mysterious bug. At that time the calculation will fail.

If lastsend was a past time stamp, the correct way to calculate it is like this:

if(millis() - lastsend < 220){

I say this not mainly for you, but for others who might duplicate the idea in more critical code.