Pages: [1] 2   Go Down
Author Topic: RPM Interrupt stopping Servo loop  (Read 1096 times)
0 Members and 1 Guest are viewing this topic.
Offline Offline
Newbie
*
Karma: 0
Posts: 16
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

I have an anemometer which uses a reed switch.  Depending on the RPM of the anemometer  a servo motor should move at a certain speed and amount to correspond with the wind turning the anemometer (the servo motor moves a swinging sign depending on the windspeed outside).

I started the code with an RPM calculation.  In the loop there are "If" statements; I have set them to turn an LED high as well as running the servo sequence depending on the RPM.  The LED's are working with the corresponding RPM but I cannot get the servo to move.

I am not sure but I assume the interrupt is stopping the servo loop from running?

I have tried "detachInterrupt(0);" but I could not get this to work, I added it after each "If" statement followed by an "attachInterrupt(0, rpm_fun, RISING);" you can see it in the code.

A precise RPM is not essential but a general wind speed of High, Medium or Low is needed.
I am not sure if I have overcomplicated this and any ideas are welcome.

Code:
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.attach(9);  // attaches the servo on pin 9 to the servo object
 }
 void loop()
 {
   if (rpmcount >= 5) {     
     
     //Update RPM every 5 counts,   
     rpm = 30*1000/(millis() - timeold)*rpmcount;
     timeold = millis();
     rpmcount = 0;
     Serial.println(rpm,DEC);
     //delay(1000);    use as an alternative to rpmcount >5     
   }
 }
 void rpm_fun()
 {
   rpmcount++;
   //Each rotation, this interrupt function is run twice
     
   
       if((rpm >= 0) && (rpm < 79)){         
         //detachInterrupt(0);         
         digitalWrite( ledPin1, LOW);
    digitalWrite(ledPin2, LOW);
digitalWrite(ledPin3, LOW);
digitalWrite(ledPin4, LOW);
//attachInterrupt(0, rpm_fun, RISING);
 }
 
     if((rpm >= 80) && (rpm < 150)){
//detachInterrupt(0);
       digitalWrite( ledPin1, HIGH);
    digitalWrite(ledPin2, LOW);
digitalWrite(ledPin3, LOW);
digitalWrite(ledPin4, LOW);
myservo.write(100);
delay(delayTimeVS);
myservo.write(101);  //full sequence removed for forum posting
delay(delayTimeVS);
myservo.write(100);
delay(delayTimeVS);
//attachInterrupt(0, rpm_fun, RISING);
 }
 
 if((rpm >= 151) && (rpm < 199)){   
   //detachInterrupt(0);   
    digitalWrite( ledPin1, LOW);
    digitalWrite(ledPin2, HIGH);
digitalWrite(ledPin3, LOW);
digitalWrite(ledPin4, LOW);
myservo.write(100);
delay(delayTimeS);
myservo.write(101);
delay(delayTimeS);
myservo.write(102);
delay(delayTimeS);  //full sequence removed for forum posting
myservo.write(101);
delay(delayTimeS);
myservo.write(100);
delay(delayTimeS);
//attachInterrupt(0, rpm_fun, RISING);
 }
 
 

 if((rpm >= 200) && (rpm < 399)){   
   //detachInterrupt(0);   
       digitalWrite( ledPin1, LOW);
    digitalWrite(ledPin2, LOW);
digitalWrite(ledPin3, HIGH);
digitalWrite(ledPin4, LOW);
myservo.write(100);
delay(delayTimeM);
myservo.write(101);  //full sequence removed for forum posting
delay(delayTimeM);
myservo.write(100);
delay(delayTimeM);
//attachInterrupt(0, rpm_fun, RISING);
 }
 
 if((rpm >= 400) && (rpm < 550)){   
   //detachInterrupt(0);   
       digitalWrite( ledPin1, LOW);
    digitalWrite(ledPin2, LOW);
digitalWrite(ledPin3, LOW);
digitalWrite(ledPin4, HIGH);
myservo.write(100);
delay(delayTimeF);
myservo.write(101);
delay(delayTimeF);
myservo.write(102);  //full sequence removed for forum posting
delay(delayTimeF);
myservo.write(101);
delay(delayTimeF);
myservo.write(100);
delay(delayTimeF);
//attachInterrupt(0, rpm_fun, RISING);
 }
  }
Logged

East Anglia (UK)
Offline Offline
Faraday Member
**
Karma: 89
Posts: 3453
May all of your blinks be without delay()
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

I spy a delay(), or several, in that interrupt routine, which is not a good idea.
Logged

Please do not send me PMs asking for help.  Post in the forum then everyone will benefit from seeing the questions and answers.

Massachusetts, USA
Offline Offline
Tesla Member
***
Karma: 178
Posts: 8064
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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:
Code:

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);
  }
}
Logged

Send Bitcoin tips to: 1L3CTDoTgrXNA5WyF77uWqt4gUdye9mezN
Send Litecoin tips to : LVtpaq6JgJAZwvnVq3ftVeHafWkcpmuR1e

Poole, Dorset, UK
Offline Offline
Edison Member
*
Karma: 25
Posts: 1872
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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

Mark
Logged

Poole, Dorset, UK
Offline Offline
Edison Member
*
Karma: 25
Posts: 1872
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Code:
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
Logged

Offline Offline
Newbie
*
Karma: 1
Posts: 47
I'm glad I bought an Arduino Leonardo
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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

Offline Offline
Newbie
*
Karma: 0
Posts: 16
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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()
Logged

Offline Offline
Newbie
*
Karma: 0
Posts: 16
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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

Code:
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);
Logged

Poole, Dorset, UK
Offline Offline
Edison Member
*
Karma: 25
Posts: 1872
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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
Logged

Global Moderator
Offline Offline
Brattain Member
*****
Karma: 452
Posts: 18694
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

I have some sketches on this page that do counting:

http://www.gammon.com.au/forum/?id=11504

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

East Anglia (UK)
Offline Offline
Faraday Member
**
Karma: 89
Posts: 3453
May all of your blinks be without delay()
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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

Code:
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
Logged

Please do not send me PMs asking for help.  Post in the forum then everyone will benefit from seeing the questions and answers.

Massachusetts, USA
Offline Offline
Tesla Member
***
Karma: 178
Posts: 8064
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Rather than writing 160 lines of code you should write a function like this:
Code:
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:
Code:
    swing(100,180);



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

Code:
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);
Logged

Send Bitcoin tips to: 1L3CTDoTgrXNA5WyF77uWqt4gUdye9mezN
Send Litecoin tips to : LVtpaq6JgJAZwvnVq3ftVeHafWkcpmuR1e

Offline Offline
Newbie
*
Karma: 0
Posts: 16
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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

Offline Offline
Newbie
*
Karma: 0
Posts: 16
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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.

Code:
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);


}
}
Logged

Global Moderator
Offline Offline
Brattain Member
*****
Karma: 452
Posts: 18694
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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

Pages: [1] 2   Go Up
Jump to: