RPM Interrupt stopping Servo loop

You can't use delay() in an interrupt. You can use delayMicroseconds() but delays of more than a few hundred microseconds are likely to cause problems.

It also looks like your full range of servo travel is two degrees: 100 to 102. What's with that?

Your ISR should be short. Put the rest in loop().

Try something like this:

volatile byte rpmcount;
unsigned int rpm;
unsigned long timeold;
int ledPin1 = 13;
int ledPin2 = 12;
int ledPin3 = 11;
int ledPin4 = 10;

#include <Servo.h>

Servo myservo;
int delayTimeF = 17;  //delay between servo movements to control speed
int delayTimeM = 25;
int delayTimeS = 30;
int delayTimeVS = 40;


void setup()
{   
  Serial.begin(9600);
  attachInterrupt(0, rpm_fun, RISING);
  rpmcount = 0;
  rpm = 0;
  timeold = 0;
  pinMode(ledPin1, OUTPUT); 
  pinMode(ledPin2, OUTPUT); 
  pinMode(ledPin3, OUTPUT); 
  pinMode(ledPin4, OUTPUT); 
  
  digitalWrite(2, HIGH); //anemometer pin set high
  
  myservo.write(0);
  myservo.attach(9);  // attaches the servo on pin 9 to the servo object 
}


void rpm_fun()
{
  rpmcount++;
}


void loop()
{
  if (rpmcount >= 5) {
    //Update RPM every 5 counts,   
    rpm = 30*1000/(millis() - timeold) * rpmcount;
    timeold = millis();
    rpmcount = 0;
    Serial.println(rpm,DEC);
  }

  if((rpm >= 0) && (rpm < 79)) {              
    digitalWrite(ledPin1, LOW);
    digitalWrite(ledPin2, LOW);
    digitalWrite(ledPin3, LOW);
    digitalWrite(ledPin4, LOW);
    myservo.write(0);
  }

  if((rpm >= 80) && (rpm < 150)){ 
    digitalWrite(ledPin1, HIGH);
    digitalWrite(ledPin2, LOW);
    digitalWrite(ledPin3, LOW);
    digitalWrite(ledPin4, LOW);
    myservo.write(30);
  }

  if((rpm >= 151) && (rpm < 199)){     
    digitalWrite(ledPin1, LOW);
    digitalWrite(ledPin2, HIGH);
    digitalWrite(ledPin3, LOW);
    digitalWrite(ledPin4, LOW);
    myservo.write(60);
  }

  if((rpm >= 200) && (rpm < 399)){   
    digitalWrite(ledPin1, LOW);
    digitalWrite(ledPin2, LOW);
    digitalWrite(ledPin3, HIGH);
    digitalWrite(ledPin4, LOW);
    myservo.write(90);
  }

  if((rpm >= 400) && (rpm < 550)){   
    digitalWrite(ledPin1, LOW);
    digitalWrite(ledPin2, LOW);
    digitalWrite(ledPin3, LOW);
    digitalWrite(ledPin4, HIGH);
    myservo.write(120);
  }
}

Which Arduino are you using? If your not using the Due you have the wrong interrupt pins!.

Mark

myservo.write(100);
delay(delayTimeM);
myservo.write(101);  //full sequence removed for forum posting
delay(delayTimeM);
myservo.write(100);
delay(delayTimeM);

You will not be able to see a movement as small as 1 degree.

Mark

you may need to give the servo more time to finish its move. Try 100 delay or more depending on the distance the servo has to travel.

Thanks John, I just tried your code without delay's and it works.

Any ideas on how I can control the Servo speed? In the code I posted the servo is only moving 2 steps but it actually moves up to 80 in one loop as it can go from 100 to 140 to 100 with a delay between each step to control the speed; its a swing sign like one you get above a pub blowing in the wind. I did not post the full code as its about 100 lines longer of just servo positions and delays.

You say the "Your ISR should be short. Put the rest in loop()." I don't know what or how to do this; can you point me in the right direction?

Mark: I am using an UNO R3 but I have a Mega 2560 around if that makes any difference?

Can RPM be calculated without an interrupt as the interrupt is not compatible with delay()

An example of a full Servo Sequence to be used with a low RPM reading:

myservo.write(100);
delay(delayTimeVS);
myservo.write(101);
delay(delayTimeVS);
myservo.write(102);
delay(delayTimeS);
myservo.write(103);
delay(delayTimeS);
myservo.write(104);
delay(delayTimeS);
myservo.write(105);
delay(delayTimeS);
myservo.write(106);
delay(delayTimeS);
myservo.write(107);
delay(delayTimeS);
myservo.write(108);
delay(delayTimeS);
myservo.write(109);
delay(delayTimeS);
myservo.write(110);
delay(delayTimeS);
myservo.write(109);
delay(delayTimeS);
myservo.write(108);
delay(delayTimeS);
myservo.write(107);
delay(delayTimeVS);
myservo.write(106);
delay(delayTimeVS);
myservo.write(105);
delay(delayTimeVS);
myservo.write(104);
delay(delayTimeVS);
myservo.write(103);
delay(delayTimeVS);
myservo.write(102);
delay(delayTimeVS);
myservo.write(101);
delay(delayTimeVS);
myservo.write(100);
delay(delayTimeVS);

On the UNO the ext interrupt pins are 2 and 3 pins 0 and 1 are used by serial. Your serial output (prints) may be interfering with our interrupt!. Look in the ref section of this site.

Mark

I have some sketches on this page that do counting:

Basically reading an anemometer is a job of determining a frequency. You can use the hardware timers for that.

squeaky_wheel:
An example of a full Servo Sequence to be used with a low RPM reading:

myservo.write(100);

delay(delayTimeVS);
myservo.write(101);
delay(delayTimeVS);
myservo.write(102);
delay(delayTimeS);
myservo.write(103);
delay(delayTimeS);
myservo.write(104);
delay(delayTimeS);
myservo.write(105);
delay(delayTimeS);
myservo.write(106);
delay(delayTimeS);
myservo.write(107);
delay(delayTimeS);
myservo.write(108);
delay(delayTimeS);
myservo.write(109);
delay(delayTimeS);
myservo.write(110);
delay(delayTimeS);
myservo.write(109);
delay(delayTimeS);
myservo.write(108);
delay(delayTimeS);
myservo.write(107);
delay(delayTimeVS);
myservo.write(106);
delay(delayTimeVS);
myservo.write(105);
delay(delayTimeVS);
myservo.write(104);
delay(delayTimeVS);
myservo.write(103);
delay(delayTimeVS);
myservo.write(102);
delay(delayTimeVS);
myservo.write(101);
delay(delayTimeVS);
myservo.write(100);
delay(delayTimeVS);

OUCH !
Even if you were to do it that way that is crying out to be put in a couple of for loops

Rather than writing 160 lines of code you should write a function like this:

void swing(int low, int high) {
   for (i=low; i<=high; i++) {
       myservo.write(i);
       delay(delayTimeVS);
    }
   for (i=high; i>=low; i--) {
       myservo.write(i);
       delay(delayTimeVS);
    }
}

Then you can replace those hundreds of lines with:

    swing(100,180);

squeaky_wheel:
An example of a full Servo Sequence to be used with a low RPM reading:

myservo.write(100);

delay(delayTimeVS);
myservo.write(101);
delay(delayTimeVS);
myservo.write(102);
delay(delayTimeS);
myservo.write(103);
delay(delayTimeS);
myservo.write(104);
delay(delayTimeS);
myservo.write(105);
delay(delayTimeS);
myservo.write(106);
delay(delayTimeS);
myservo.write(107);
delay(delayTimeS);
myservo.write(108);
delay(delayTimeS);
myservo.write(109);
delay(delayTimeS);
myservo.write(110);
delay(delayTimeS);
myservo.write(109);
delay(delayTimeS);
myservo.write(108);
delay(delayTimeS);
myservo.write(107);
delay(delayTimeVS);
myservo.write(106);
delay(delayTimeVS);
myservo.write(105);
delay(delayTimeVS);
myservo.write(104);
delay(delayTimeVS);
myservo.write(103);
delay(delayTimeVS);
myservo.write(102);
delay(delayTimeVS);
myservo.write(101);
delay(delayTimeVS);
myservo.write(100);
delay(delayTimeVS);

Thanks johnwasser; I have tried your "for" code which works great in its own sketch. My RPM sketch still doesn't run with it (I assume this is still due to the delay) so I am going to look at Nick Gammons suggestion of running hardware timers; might take me an evening or two to figure that out and write a code for it. I will post once I have come up with something.
Thanks.

Thanks Nick Gammon for the link to the information on Hardware Timers, I have got it to read the RPM and work with the LED's. I took a look at the information on interrupts which was very useful and I have a much better understanding of the previous code I used.

I have inserted the swing code johnwasser recommended into the Timer code but I can't get that or my old servo loop to work with it, only the LED's.

With the original code it would work with "myservo.write(100);" command in the loop but the new code doesn't even move the servo which has led me to think I have put the servo commands in the wrong place?

The full code is below; any ideas where I have gone wrong are most welcome.

volatile unsigned long timerCounts;
volatile boolean counterReady;

// internal to counting routine
unsigned long overflowCount;
unsigned int timerTicks;
unsigned int timerPeriod;
 int ledPin1 = 13;
 int ledPin2 = 12;
 int ledPin3 = 11;
 int ledPin4 = 10;
 
 #include <Servo.h>

Servo myservo;
int delayTimeF = 17;
int delayTimeM = 25;
int delayTimeS = 30;
int delayTimeVS = 40;
int i = 0;    // variable to store the servo position 

void startCounting (unsigned int ms) 
  {

  counterReady = false;         // time not up yet
  timerPeriod = ms;             // how many 1 mS counts to do
  timerTicks = 0;               // reset interrupt counter
  overflowCount = 0;            // no overflows yet

  // reset Timer 1 and Timer 2
  TCCR1A = 0;             
  TCCR1B = 0;              
  TCCR2A = 0;
  TCCR2B = 0;

  // Timer 1 - counts events on pin D5
  TIMSK1 = _BV (TOIE1);   // interrupt on Timer 1 overflow

  // Timer 2 - gives us our 1 mS counting interval
  // 16 MHz clock (62.5 nS per tick) - prescaled by 128
  //  counter increments every 8 uS. 
  // So we count 125 of them, giving exactly 1000 uS (1 mS)
  TCCR2A = _BV (WGM21) ;   // CTC mode
  OCR2A  = 124;            // count up to 125  (zero relative!!!!)

  // Timer 2 - interrupt on match (ie. every 1 mS)
  TIMSK2 = _BV (OCIE2A);   // enable Timer2 Interrupt

  TCNT1 = 0;      // Both counters to zero
  TCNT2 = 0;     

  // Reset prescalers
  GTCCR = _BV (PSRASY);        // reset prescaler now
  // start Timer 2
  TCCR2B =  _BV (CS20) | _BV (CS22) ;  // prescaler of 128
  // start Timer 1
  // External clock source on T1 pin (D5). Clock on rising edge.
  TCCR1B =  _BV (CS10) | _BV (CS11) | _BV (CS12);

}  // end of startCounting

ISR (TIMER1_OVF_vect)
{
  ++overflowCount;               // count number of Counter1 overflows  
}  // end of TIMER1_OVF_vect


//******************************************************************
//  Timer2 Interrupt Service is invoked by hardware Timer 2 every 1ms = 1000 Hz
//  16Mhz / 128 / 125 = 1000 Hz

ISR (TIMER2_COMPA_vect) 
{
  // grab counter value before it changes any more
  unsigned int timer1CounterValue;
  timer1CounterValue = TCNT1;  // see datasheet, page 117 (accessing 16-bit registers)

  // see if we have reached timing period
  if (++timerTicks < timerPeriod) 
    return;  // not yet

  // if just missed an overflow
  if (TIFR1 & TOV1)
    overflowCount++;

  // end of gate time, measurement ready

  TCCR1A = 0;    // stop timer 1
  TCCR1B = 0;    

  TCCR2A = 0;    // stop timer 2
  TCCR2B = 0;    

  TIMSK1 = 0;    // disable Timer1 Interrupt
  TIMSK2 = 0;    // disable Timer2 Interrupt
    
  // calculate total count
  timerCounts = (overflowCount << 16) + timer1CounterValue;  // each overflow is 65536 more
  counterReady = true;              // set global flag for end count period
}  // end of TIMER2_COMPA_vect


void setup () {
  Serial.begin(115200);       
  Serial.println("Frequency Counter");
  pinMode(ledPin1, OUTPUT); 
   pinMode(ledPin2, OUTPUT); 
   pinMode(ledPin3, OUTPUT); 
   pinMode(ledPin4, OUTPUT); 
   myservo.attach(9);  // attaches the servo on pin 9 to the servo object 

} // end of setup

void swing(int low, int high) {
   for (i=low; i<=high; i++) {
       myservo.write(i);
       delay(delayTimeVS);
    }
   for (i=high; i>=low; i--) {
       myservo.write(i);
       delay(delayTimeVS);
    }
}

void swing1(int low, int high) {
   for (i=low; i<=high; i++) {
       myservo.write(i);
       delay(delayTimeF);
    }
   for (i=high; i>=low; i--) {
       myservo.write(i);
       delay(delayTimeF);
    }
}


void loop () {

  // stop Timer 0 interrupts from throwing the count out
  byte oldTCCR0A = TCCR0A;
  byte oldTCCR0B = TCCR0B;
  TCCR0A = 0;    // stop timer 0
  TCCR0B = 0;    

  
  startCounting (500);  // how many mS to count for

  while (!counterReady) 
     { }  // loop until count over

  // adjust counts by counting interval to give frequency in Hz
  float frq = (timerCounts *  1000.0) / timerPeriod;

  Serial.print ("Frequency: ");
  Serial.println ((unsigned long) frq);
  
  // restart timer 0
  TCCR0A = oldTCCR0A;
  TCCR0B = oldTCCR0B;
  
  // let serial stuff finish
  delay(200);
  
  if((frq >=0) && (frq < 4)){
 
         
         digitalWrite( ledPin1, LOW);
    digitalWrite(ledPin2, LOW);
digitalWrite(ledPin3, LOW);
digitalWrite(ledPin4, LOW);

  }
  
  if((frq >= 5) && (frq < 10)){

         
         digitalWrite( ledPin1, HIGH);
    digitalWrite(ledPin2, LOW);
digitalWrite(ledPin3, LOW);
digitalWrite(ledPin4, LOW);
swing(100,120);



}   // end of loop

if((frq >= 11) && (frq < 15)){

         
         digitalWrite( ledPin1, LOW);
    digitalWrite(ledPin2, HIGH);
digitalWrite(ledPin3, LOW);
digitalWrite(ledPin4, LOW);
swing(100,130);

}

if((frq >= 16) && (frq < 20)){
    
         
         digitalWrite( ledPin1, LOW);
    digitalWrite(ledPin2, LOW);
digitalWrite(ledPin3, HIGH);
digitalWrite(ledPin4, LOW);
swing1(100,140);


}
if((frq >= 21) && (frq < 80)){
       
         
         digitalWrite( ledPin1, LOW);
    digitalWrite(ledPin2, LOW);
digitalWrite(ledPin3, LOW);
digitalWrite(ledPin4, HIGH);
swing1(100,150);


}
}

The servo library uses timers. You are using two in your code and you have stopped the third.

Try something like this:

volatile byte rpmcount;
unsigned int rpm;
unsigned long timeold;
int ledPin1 = 13;
int ledPin2 = 12;
int ledPin3 = 11;
int ledPin4 = 10;

#include <Servo.h>

Servo myservo;
int delayTimeF = 17;  //delay between servo movements to control speed
int delayTimeM = 25;
int delayTimeS = 30;
int delayTimeVS = 40;


void setup()
{   
  Serial.begin(9600);
  attachInterrupt(0, rpm_fun, RISING);
  rpmcount = 0;
  rpm = 0;
  timeold = 0;
  pinMode(ledPin1, OUTPUT); 
  pinMode(ledPin2, OUTPUT); 
  pinMode(ledPin3, OUTPUT); 
  pinMode(ledPin4, OUTPUT); 
  
  digitalWrite(2, HIGH); //anemometer pin set high
  
  myservo.write(0);
  myservo.attach(9);  // attaches the servo on pin 9 to the servo object 
}


void rpm_fun()
{
  rpmcount++;
}


void loop()
{
  if (rpmcount >= 5) {
    //Update RPM every 5 counts,   
    rpm = 30*1000/(millis() - timeold) * rpmcount;
    timeold = millis();
    rpmcount = 0;
    Serial.println(rpm,DEC);
  }

  if((rpm >= 0) && (rpm < 79)) {              
    digitalWrite(ledPin1, LOW);
    digitalWrite(ledPin2, LOW);
    digitalWrite(ledPin3, LOW);
    digitalWrite(ledPin4, LOW);
    swing(100,110);
  }

  if((rpm >= 80) && (rpm < 150)){ 
    digitalWrite(ledPin1, HIGH);
    digitalWrite(ledPin2, LOW);
    digitalWrite(ledPin3, LOW);
    digitalWrite(ledPin4, LOW);
    swing(100,120);
  }

  if((rpm >= 151) && (rpm < 199)){     
    digitalWrite(ledPin1, LOW);
    digitalWrite(ledPin2, HIGH);
    digitalWrite(ledPin3, LOW);
    digitalWrite(ledPin4, LOW);
    swing(100,140);
  }

  if((rpm >= 200) && (rpm < 399)){   
    digitalWrite(ledPin1, LOW);
    digitalWrite(ledPin2, LOW);
    digitalWrite(ledPin3, HIGH);
    digitalWrite(ledPin4, LOW);
    swing(100,160);
  }

  if((rpm >= 400) && (rpm < 550)){   
    digitalWrite(ledPin1, LOW);
    digitalWrite(ledPin2, LOW);
    digitalWrite(ledPin3, LOW);
    digitalWrite(ledPin4, HIGH);
    swing(100,180);
  }
}

void swing(int low, int high) {
   for (i=low; i<=high; i++) {
       myservo.write(i);
       delay(delayTimeVS);
    }
   for (i=high; i>=low; i--) {
       myservo.write(i);
       delay(delayTimeVS);
    }
}

Nick from what I understand the Arduino Mega 2560 has 5 Timers and I have the board here. Timers 3, 4 and 5 are 16 bit. Do you know if the Servo Library automatically selects a 16 bit Timer that is available or does the Servo Library only use Timer 5 meaning I need to free up Timer 5 from the Pulse Counting sketch so that the Servo Library can use that?

I am also having trouble finding the Pins (which can be used to count) that go with the other Timers on the Mega 2560. Is it only Timer 5 that can count my pulse or can Timers 3 and 4 do it, if so what are the corresponding Pins?

I don't know offhand, and I must admit I didn't notice you were using a Mega. You should be able to find some combination of timers and servos to make it work.

As for the pins, not all timer inputs are broken out to actual pins on the Mega, I know that much.

Originally I was using the Uno R3 but when I was told I needed more Timers I decided to switch to the MEGA 2560 as it has more timers. I have had a look at the Servo Library and I would have though it would select the first available Timer but it doesn't seem to be the case.

Looking at the Atmel website the documentation on the MEGA 2560 says Counter4 has Pin 42 for Capture Event and Pin 32 for TIMER3/Counter3 Capture Event. (pg: 106 http://www.atmel.com/images/doc2549.pdf). I haven't tried switching counters yet but I will give it a go although I am not sure if it is necessary and if it will fix my problem.

I have tried to get the counters 3 and 4 to work (MEGA 2560) to capture the anemometer but they do not work with the pins listed 32 and 42. Counter5 works in a simple count sketch with pin 47.

I have adapted johnwassers servo swing code to work with a Counting pulses sketch which can be found in the Arduino Cookbook 18.7. When the Counter is set to Counter5 (MEGA 2560) the servo does not register. If I change the Counter to Counter4 then when the board is turned on the servo moves into 100 degrees and completes swing(100,180); as in the "If" statement if((count >= 21) && (count < 80)){. Once it has completed this nothing else happens. When I open the Serial Monitor I get a reading of 37 and the servo starts again moving from position 100 to 180. The reading of 37 must be interferance as Counter4 cannot read the anemometer on pin 42.

Because of all of this I think the sketch could work if somehow either Counter3 or Counter4 could read a capture pin. When the sketch is set to Counter5 the servo cannot run. Another alternative might be to adapt the servo library to call on Counters 3 or 4 instead of 5 as it seems to be doing.

What I am trying to achieve is to take an RPM reading of an anemometer (read switch) and use that to control a servo movement. What I have written above might be wrong as I am a bit out of my depth now. My alternatives might be to use two seperate arduino's, one for counting and one for the Servo sequence as eventually the Anemometer will be outside probably communicating by Xbee; this will free up the relevant timers.

Any advice is welcome.

I believe this code would work if counter4 could read pin 42.

const int hardwareCounterPin = 42;   // input pin fixed to internal Timer
const int ledPin             = 13; 

const int samplePeriod = 1000;   // the sample period in milliseconds
unsigned int count;
 int ledPin1 = 13;
 int ledPin2 = 12;
 int ledPin3 = 11;
 int ledPin4 = 10;
 #include <Servo.h> 
 Servo myservo;
int delayTimeF = 17;
int delayTimeM = 25;
int delayTimeS = 30;
int delayTimeVS = 40; 
int i = 0;    // variable to store the servo position 

void setup()
{
  Serial.begin(9600);
  pinMode(ledPin,OUTPUT);
  // hardware counter setup (see ATmega data sheet for details)
  TCCR4A=0;        // reset timer/counter control register A
  pinMode(ledPin1, OUTPUT); 
   pinMode(ledPin2, OUTPUT); 
   pinMode(ledPin3, OUTPUT); 
   pinMode(ledPin4, OUTPUT); 
   myservo.attach(9);  // attaches the servo on pin 9 to the servo object 
}  

void swing(int low, int high) {
   for (i=low; i<=high; i++) {
       myservo.write(i);
       delay(delayTimeVS);
    }
   for (i=high; i>=low; i--) {
       myservo.write(i);
       delay(delayTimeVS);
    }
}


void loop()
{
   
  // start the counting
  bitSet(TCCR4B ,CS12);  // Counter Clock source is external pin
  bitSet(TCCR4B ,CS11);  // Clock on rising edge  
  delay(samplePeriod);
  // stop the counting 
  TCCR4B = 0;           
  count = TCNT4;
  TCNT4 = 0;  // reset the hardware counter
  if(count > 0)
     Serial.println(count);
     
     
 digitalWrite( ledPin1, LOW);
    digitalWrite(ledPin2, LOW);
digitalWrite(ledPin3, LOW);
digitalWrite(ledPin4, LOW);

  
  
  if((count >= 5) && (count < 10)){ 
         digitalWrite( ledPin1, HIGH);
    digitalWrite(ledPin2, LOW);
digitalWrite(ledPin3, LOW);
digitalWrite(ledPin4, LOW);
swing(100,120);

}   // end of loop

if((count >= 11) && (count < 15)){
         digitalWrite( ledPin1, LOW);
    digitalWrite(ledPin2, HIGH);
digitalWrite(ledPin3, LOW);
digitalWrite(ledPin4, LOW);
swing(100,140);

}

if((count >= 16) && (count < 20)){
             digitalWrite( ledPin1, LOW);
    digitalWrite(ledPin2, LOW);
digitalWrite(ledPin3, HIGH);
digitalWrite(ledPin4, LOW);
swing(100,140);


}
if((count >= 21) && (count < 80)){
         
         digitalWrite( ledPin1, LOW);
    digitalWrite(ledPin2, LOW);
digitalWrite(ledPin3, LOW);
digitalWrite(ledPin4, HIGH);
swing(100,180);

}
}

I have got the last code posted to work with a few changes; I have put the hardwareCounterPin back on 47 and changed the Timer from 4 to Timer5 and edited the Servo library.

The previous Counter sketch would not work as both the counter and the servo were using Timer5. MEGA 2560 has three 16bit timers but only Timer5 has an event capture pin which the counter needs to pick up the anemometer pulses. The servo library automatically selects Timer5 so I have removed it from the Servo library and now the sketch works.

The original RPM code would not work as the loop() delays were affecting the interrupts; they are not compatible.

Next step is to put the Anemometer outside and connect to it with zigbee or ethernet; as the servo and counter sketch will be on different boards there will not be an issue with timers.

Thanks to Nick Gammon and johnwasser for their help.