Conflict between servo library and interrupts

Hi all from a spanner monkey who is just taking some steps into electronics and programming.

I have been attempting to create a very simple throttle governor for an internal combustion engine using an Arduino Uno, but have run into a snag with my code with which I hope some of you may be able to help me out!

The basic premise of the system I'm hoping to create is that I will be able to use a servo to control the engine throttle, directly onto the carb butterfly. This will be controlled by either one of two modes: "Throttle Mode", in which a potentiometer directly controls the throttle opening. And a "Governing Mode", in which a potentiometer is used to dial a set-speed, and the Arduino compares this set-speed to the actual speed of the engine and matches to two (hopefully using PID control in future iterations).

I have got the basics worked out, I can control servos, read pulses using an interrupt, and read potentiometers into the Arduino. However I am having trouble with what seems to be a conflict between my interrupt and the servo library.

The bug goes something like this:

  1. Start system in "throttle mode", everything works fine, servo follows setting on potentiometer.

  2. Switch to "governing mode", everything works fine, arduino counts pulses in and reads set-speed and moves servo accordingly.

  3. Switch back to "throttle mode", here it goes wrong... Although the serial monitor shows that everything is still working, the servo just does not move any more! As if it is not being given a command.

I hope I have described this adequately, I probably haven't used all the correct terms, this is all still new to me!

I should point out that I'm still prototyping in Tinkercad at the moment, but I don't feel like this is a simulator issue...

Other observations which may be of relevance are that if I comment out everything to do with the servo library and control the servo "manually" by using pinHigh, delay, pinLow, it all works as it should...

Thanks in advance for any help :smiley:

Here is my code:

// C++ code
//
#include <Servo.h>
int throttlepotpin = A1;
int throttlepotposition;
int govpotpin = A2;
int govpotposition;
int servopin = 3;
int modepin = 4;
int servopulse;
int throttlepotpercent;
int govpotpercent;
int mode;
int targetshaftpulses;
int shaftpulses;
int speedpin = 2;
int error;
Servo servo1;



void setup()
{
  pinMode(throttlepotpin, INPUT);
  pinMode(govpotpin, INPUT);
  pinMode(servopin, OUTPUT);
  pinMode(modepin, INPUT);
  servopulse = 1500;
  pinMode(speedpin, INPUT);
  attachInterrupt(digitalPinToInterrupt(2), speed, RISING);
  servo1.attach(servopin);
  Serial.begin(9600);

}

void loop()
{
delay(20);
mode=digitalRead(modepin);
  
if (mode==HIGH)
{
  govmode();
}
else
{
  throttlemode();
}

}

void throttlemode()
{
  throttlepotposition=analogRead(throttlepotpin);
  servopulse=map(throttlepotposition, 0, 1023, 1000, 2000);
  throttlepotpercent=map(throttlepotposition, 0, 1023, 0, 100);
  
  //digitalWrite(servopin, HIGH);
  //delayMicroseconds(servopulse);
  //digitalWrite(servopin, LOW);
  
  servo1.writeMicroseconds(servopulse);
  
  
  Serial.print("Throttle mode: ");
  Serial.print("Throttle percentage= ");
  Serial.print(throttlepotpercent);
  Serial.print(" ");
  Serial.print(servopulse);
  Serial.print('\n');

}

void govmode()
{
  //Setting desired speed
  govpotposition=analogRead(govpotpin);
  targetshaftpulses=map(govpotposition, 0, 1023, 8, 32);

  
  //Seeing what speed is happening
//resetting counters
 shaftpulses=0;
 //controlling interrupts
 interrupts();
//Enables interrupts on the Arduino
 delay (20);
//Wait 1/50th second
 noInterrupts();
//Disable the interrupts on the Arduino
  
  
  //Calculating error
  
  error=targetshaftpulses-shaftpulses;
  
  //Controlling servo
  
  servopulse=servopulse+(error*50);
  servopulse=constrain(servopulse, 1000, 2000);
  
  servo1.writeMicroseconds(servopulse);
  
  
  Serial.print("Governing mode: ");
  Serial.print("Setspeed=");
  Serial.print(targetshaftpulses*125);
  Serial.print(" Actual speed=");
  Serial.print(shaftpulses*125);
  Serial.print(" Error=");
  Serial.print(error*125);
  Serial.print(" Pulse=");
  Serial.print(servopulse);
  Serial.print('\n');
}

void speed()
{
 shaftpulses++; //The number of pulses in 1/50th of a second
}

You must not disable interrupts! At least not for a longer time - maybe only some µs. Interrupts are used by other functionality too. E.g. the servo lib, Serial.print(), delay() and the millis() function - they all use interrupts and will not work anymore if you disable interrupts.

You must know what you are doing if you use interrupts. If you are a beginner in programming interrupts produce much more problems than they solve.

Thank you for your reply. Hopefully you can see what I am trying to achieve, perhaps there is a way to achieve my aims without using interrupts? I have been doing some research and it seems that interrupts are the best way to count the pulses. But if there is a better/more simple method then I would be happy to implement it!

Depends how fast your pulses are. Because you are blocking your sketch while counting the pulses it should be possible to count them in a while loop and measuring the time with millis().
If you want to stay with the interrupts you could activate the counting with attachInterupt and detachInterrupt instead of interrupts() and noInterrupts().

Something like this:

 shaftpulses=0;
 //controlling interrupts
 attachInterrupt(digitalPinToInterrupt(speedpin), speed, RISING);
 delay (20);
//Wait 1/50th second
 detachInterrupt(digitalPinToInterrupt(speedpin));

I am expecting to receive a maximum of 32 pulses during my 20ms measuring window

You can safely count that in the code with a while loop.

Thank you very much, I shall experiment with both these options. A friend has just suggested a third option which may be to measure the period of the pulses and measure speed that way. But for some reason I feel like counting pulses should be more straightforward

You forgot to declare 'shaftpulses' as 'volatile' so the compile doesn't expect it to be changed in an ISR.

Since 'shaftpulses' is larger than one byte you should not read it with interrupts enabled. I recommend you make it a 'byte' variable instead of 'int'. Note: In your case (max count = 32) it doesn't matter since the second byte is never changed but it is bad practice. In cases where the value can go over 255 you might read the first byte, get an interrupt that changes the value from 0x00FF (255) to 0x0100 (256), and then read the second byte. You have now read the value as 0x0000 instead of 255 or 256. if the bytes are read in the other order, you might get 0x01FF (511) instead of 255 or 256.

Well, you chaps have in a few hours solved the issues I've been banging my head against a wall over all week!

Many thanks once again!

Code is now as follows and appears to be working perfectly (in simulator world at least):

// C++ code
//
#include <Servo.h>
int throttlepotpin = A1;
int throttlepotposition;
int govpotpin = A2;
int govpotposition;
int servopin = 3;
int modepin = 4;
int servopulse;
int throttlepotpercent;
int govpotpercent;
int mode;
int targetshaftpulses;
volatile byte shaftpulses;
int speedpin = 2;
int error;
Servo servo1;


void setup()
{
  pinMode(throttlepotpin, INPUT);
  pinMode(govpotpin, INPUT);
  pinMode(servopin, OUTPUT);
  pinMode(modepin, INPUT);
  servopulse = 90;
  pinMode(speedpin, INPUT);
  servo1.attach(servopin);
  Serial.begin(9600);

}

void loop()
{
delay(100);
mode=digitalRead(modepin);
  
if (mode==HIGH)
{
  govmode();
}
else
{
  throttlemode();
}

}

void throttlemode()
{
  throttlepotposition=analogRead(throttlepotpin);
  servopulse=map(throttlepotposition, 0, 1023, 45, 135);
  throttlepotpercent=map(throttlepotposition, 0, 1023, 0, 100);
  
  servo1.write(servopulse);
  
  
  Serial.print("Throttle mode: ");
  Serial.print("Throttle percentage= ");
  Serial.print(throttlepotpercent);
  Serial.print(" ");
  Serial.print(servopulse);
  Serial.print('\n');

}

void govmode()
{
  //Setting desired speed
  govpotposition=analogRead(govpotpin);
  targetshaftpulses=map(govpotposition, 0, 1023, 8, 32);

  
  //Seeing what speed is happening
//resetting counters
 shaftpulses=0;
 //controlling interrupts
 attachInterrupt(digitalPinToInterrupt(2), speed, RISING);
//Enables interrupts on the Arduino
 delay (20);
//Wait 1/50th second
 detachInterrupt(digitalPinToInterrupt(2));
//Disable the interrupts on the Arduino
  
 
  //Calculating error
  
  error=targetshaftpulses-shaftpulses;
  
  //Controlling servo
  
  servopulse=servopulse+(error*1);
  servopulse=constrain(servopulse, 45, 135);
  
  servo1.write(servopulse);
  
  
  Serial.print("Governing mode: ");
  Serial.print("Setspeed=");
  Serial.print(targetshaftpulses*125);
  Serial.print(" Actual speed=");
  Serial.print(shaftpulses*125);
  Serial.print(" Error=");
  Serial.print(error*125);
  Serial.print(" Pulse=");
  Serial.print(servopulse);
  Serial.print('\n');
}

void speed()
{
 shaftpulses++; //The number of pulses in 1/50th of a second
}

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.