Offline
Newbie
Karma: 0
Posts: 10
|
 |
« on: February 12, 2013, 05:25:18 pm » |
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. 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
Edison Member
Karma: 48
Posts: 1408
May all of your blinks be without delay
|
 |
« Reply #1 on: February 12, 2013, 05:38:41 pm » |
I spy a delay(), or several, in that interrupt routine, which is not a good idea.
|
|
|
|
|
Logged
|
|
|
|
|
Massachusetts, USA
Offline
Tesla Member
Karma: 96
Posts: 6372
|
 |
« Reply #2 on: February 12, 2013, 05:44:27 pm » |
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); } }
|
|
|
|
|
Logged
|
|
|
|
|
Poole, Dorset, UK
Offline
God Member
Karma: 8
Posts: 671
|
 |
« Reply #3 on: February 12, 2013, 05:44:56 pm » |
Which Arduino are you using? If your not using the Due you have the wrong interrupt pins!.
Mark
|
|
|
|
|
Logged
|
|
|
|
|
Poole, Dorset, UK
Offline
God Member
Karma: 8
Posts: 671
|
 |
« Reply #4 on: February 12, 2013, 05:47:58 pm » |
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
Newbie
Karma: 1
Posts: 47
I'm glad I bought an Arduino Leonardo
|
 |
« Reply #5 on: February 12, 2013, 05:48:26 pm » |
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
Newbie
Karma: 0
Posts: 10
|
 |
« Reply #6 on: February 12, 2013, 05:55:01 pm » |
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
Newbie
Karma: 0
Posts: 10
|
 |
« Reply #7 on: February 12, 2013, 05:58:13 pm » |
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);
|
|
|
|
|
Logged
|
|
|
|
|
Poole, Dorset, UK
Offline
God Member
Karma: 8
Posts: 671
|
 |
« Reply #8 on: February 12, 2013, 06:03:05 pm » |
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
Melbourne, Australia
Offline
Shannon Member
Karma: 218
Posts: 13897
Lua rocks!
|
 |
« Reply #9 on: February 12, 2013, 07:09:07 pm » |
I have some sketches on this page that do counting: http://www.gammon.com.au/forum/?id=11504Basically reading an anemometer is a job of determining a frequency. You can use the hardware timers for that.
|
|
|
|
|
Logged
|
|
|
|
|
East Anglia (UK)
Offline
Edison Member
Karma: 48
Posts: 1408
May all of your blinks be without delay
|
 |
« Reply #10 on: February 13, 2013, 03:21:06 am » |
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
|
|
|
|
|
Logged
|
|
|
|
|
Massachusetts, USA
Offline
Tesla Member
Karma: 96
Posts: 6372
|
 |
« Reply #11 on: February 13, 2013, 09:00:16 am » |
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);
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);
|
|
|
|
|
Logged
|
|
|
|
|
Offline
Newbie
Karma: 0
Posts: 10
|
 |
« Reply #12 on: February 13, 2013, 01:16:55 pm » |
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
Newbie
Karma: 0
Posts: 10
|
 |
« Reply #13 on: February 13, 2013, 05:23:48 pm » |
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);
} }
|
|
|
|
|
Logged
|
|
|
|
|
Global Moderator
Melbourne, Australia
Offline
Shannon Member
Karma: 218
Posts: 13897
Lua rocks!
|
 |
« Reply #14 on: February 13, 2013, 06:33:09 pm » |
The servo library uses timers. You are using two in your code and you have stopped the third.
|
|
|
|
|
Logged
|
|
|
|
|
|