Interrupts for a servo

Hi All,

This is my first time posting on the forums. I am comfortable with the programming but the I am unsure if my current approach will work well.

I want to control a servo that will move to a few different degrees at different speeds but if it were to receive an interrupt on pin 2/3 I would want it to stop and go back to the original position (0 deg). I wanted to find out if this is possible at all.

i.e. If I tell a servo to move 90 deg but at 45 deg it receives and interrupt would the servo stop moving so that the interrupt function can say move back to 0 deg?

If you look at the servo sweep example that comes with the Arduino IDE you will see how a servo can be made to move step by step. You could arrange for your code to be able to change the "destination" between any of the steps.

The word "interrupt" has a very specific meaning for computers and I don't know is that's how you mean it - or whether you just use the word in a general sense to mean "get it to do something else".

If you are unfamiliar with them I suggest you don't bother using Arduino interrupts unless you have no choice. Using interrupts can give rise to all sorts of complicated problems that are hard to debug. In most cases the Arduino repeats things fast enough that (for example) a switch can be tested and acted upon without the need for interrupts.

...R

Hi Robin,

Thank you for your reply and I apologize for not replying sooner. I do understand the difference between the two interrupts in a very general sense.

I am in the process of a building a device that will function with animals as part of my labwork.

The idea is to have the servo tilt a platform at some designated degrees +/- ~20 from the stationary position (does this already). If I receive the correct response from the test animal even mid tilt then it should stop and return to the starting position.

Right now I have 3 digital pins coming from a DAQ/Labview which will let me know what speed/direction I should move but if I try to use the interrupt function on Pin 2 the program stops running.

#include <VarSpeedServo.h>


const int SERVO_PIN = 9;

const int COMM_RATE = 9600;

//speeds at which the servo will be told to move 
const int SLOW = 1;
const int MED = 20;
const int FAST = 50;

//3 pins used to determine the angle/speed from the DAQ
const int P1 = 10;
const int P2 = 11;
const int P3 = 12;

//agle determined to be the center
const int CENTER = 71;
//feedback
const int FB = 8;
//Right Angle & Left Angle. It's just 20 degrees from the center in either direction
const int RA = CENTER - 20;
const int LA = CENTER + 20;

VarSpeedServo s1; //name for the servo


void setup()
{
  //serial communications for errors -> debugging
  Serial.begin(COMM_RATE);
  
  //intiate the pin for the servo
  s1.attach(SERVO_PIN);
  
  //3 digital pins used to determine the value of the turn from the arduino
  pinMode(P1,INPUT);
  pinMode(P2,INPUT);
  pinMode(P3,INPUT);
  
  pinMode(FB,OUTPUT); // send feedback to the DAQ
  
  //make sure it the platform centers itself
  s1.write(CENTER,SLOW);
  
  //attach an interrupt to stop any of the motions
  attachInterrupt(0, stopTurn, CHANGE);
  
} // end setup function


void loop()
{
  int val,lastSpeed;
  //Read the values of the pins. You will get 3 high/low 1/0 inputs which add up to 1 decimal number
  val = digitalRead(P1) * 4 + digitalRead(P2) * 2 + digitalRead(P3);
  
  Serial.println(val);
  
  switch(val)
  {
    //center
    case 0:
    {
      s1.write(CENTER,SLOW,true);
      break;
    }
    //right fast
    case 1: 
    {
      s1.write(RA,FAST,true);
      break;
    }
    //right mid
    case 2:
    {
      s1.write(RA,MED,true);
      break;
    }
    case 3: 
    {
      s1.write(RA,SLOW,true);
      break;
    }
    case 4:
    {
      s1.write(LA,FAST,true);
      break;
    }
    case 5:
    {
      s1.write(LA,MED,true);
      break;
    }
    case 6:
    {
      s1.write(LA,SLOW,true);
      break;
    }
    default:
    {
      s1.write(CENTER,SLOW,true);
      break;
    }
  }
  
  delay(800);
  if (val)
  s1.write(CENTER,SLOW,true);  
}

void stopTurn()
{
  Serial.println("HELLO");
  //s1.write(CENTER,FAST,true); 
}

I looked over the sweeper example and I actually think that I can do this task without using interrupts at all just modifying the sweeper example (a lot).

Thank you for your help

garcijon:
I looked over the sweeper example and I actually think that I can do this task without using interrupts at all just modifying the sweeper example (a lot).

Animals (including humans) do things very slowly by Arduino standards so it should be easy to detect behaviour just by polling some sensor between servo moves. Code for loop() that calls functions something like this might be suitable

void loop() {
   readSensor();
   updateServoPosition(); // a little at a time
   moveServoToNewPosition(); 
}

...R

This is that I have so far. It works by moving left/right 20 degrees at 3 different speeds. It is pretty jerky between steps. Is there some way to have the servo move smoothly between steps? Specially when it moves SLOW (1 step, delay(25) ) it is easy to tell that it's moving like a stepper motor.
Thanks again.

#include <Servo.h>


const int SERVO_PIN = 9;

const int COMM_RATE = 9600;

//speeds at which the servo will be told to move. This is determine by how many degrees you move in between iterations and the delay.
const int SLOW [] = {1,25};
const int MED  [] = {2,20};
const int FAST [] = {3,15};

//3 pins used to determine the angle/speed from the DAQ
const int P1 = 10;
const int P2 = 11;
const int P3 = 12;

//agle determined to be the center
const int CENTER = 90;
//feedback
const int FB = 8;
//Right Angle & Left Angle. It's just 20 degrees from the center in either direction
const int RA = CENTER - 20;
const int LA = CENTER + 20;

const int INTERRUPT = 2;

Servo s1; //name for the servo

int val,lastSpeed,oldVal,lastInt = 0;

void setup()
{
  //serial communications for errors -> debugging
  Serial.begin(COMM_RATE);
  
  //intiate the pin for the servo
  s1.attach(SERVO_PIN);
  
  //3 digital pins used to determine the value of the turn from the arduino
  pinMode(P1,INPUT);
  pinMode(P2,INPUT);
  pinMode(P3,INPUT);
  
  pinMode(FB,OUTPUT); // send feedback to the DAQ
  
  //make sure it the platform centers itself
  move(CENTER,SLOW);
  
} // end setup function


void loop()
{
  //Read the values of the pins. You will get 3 high/low 1/0 inputs which add up to 1 decimal number
  val = digitalRead(P1) * 4 + digitalRead(P2) * 2 + digitalRead(P3);

  if (val != oldVal)
  {
  Serial.println(val);
  Serial.println(oldVal);
  switch(val)
  {
    //center
    case 0:
    {
      move(CENTER,SLOW);
      break;
    }
    //right fast
    case 1: 
    {
      move(RA,FAST);
      break;
    }
    //right mid
    case 2:
    {
      move(RA,MED);
      break;
    }
    case 3: 
    {
      move(RA,SLOW);
      break;
    }
    case 4:
    {
      move(LA,FAST);
      break;
    }
    case 5:
    {
      move(LA,MED);
      break;
    }
    case 6:
    {
      move(LA,SLOW);
      break;
    }
    default:
    {
      move(CENTER,SLOW);
      break;
    }
  }
  
  delay(800);
  if (val)
  move(CENTER,SLOW);  
  }
  oldVal = val;
}


void move(int pos, const int *speed)
{
  if (pos == RA)
  {
  for(int dest = CENTER; dest > pos; dest -= speed[0])  // goes from 0 degrees to 180 degrees 
  {                                  // in steps of 1 degree 
    if(lastInt != digitalRead(INTERRUPT))
    {
      lastInt = digitalRead(INTERRUPT);
      stopMove();
      return;
    }
    s1.write(dest);              // tell servo to go to position in variable 'pos' 
    delay(speed[1]);                       // waits 15ms for the servo to reach the position 
  }
  } else {
    if(pos == LA)
    {
      for(int dest = CENTER; dest < pos; dest += speed[0])  // goes from 0 degrees to 180 degrees 
      {                                  // in steps of 1 degree 

      if(lastInt != digitalRead(INTERRUPT))
      {
        lastInt = digitalRead(INTERRUPT);
        stopMove();
        return;
      }
      s1.write(dest);              // tell servo to go to position in variable 'pos' 
      delay(speed[1]);                       // waits 15ms for the servo to reach the position 
    }
  } else {
    if(pos == CENTER)
   {
     pos = s1.read();
     if (pos > CENTER)
     {
       for(int dest = pos; dest > CENTER ; dest -= speed[0])  // goes from 0 degrees to 180 degrees 
       {                                  // in steps of 1 degree 
       s1.write(dest);              // tell servo to go to position in variable 'pos' 
       delay(speed[1]);                       // waits 15ms for the servo to reach the position 
     }
   } else {
            for(int dest = pos; dest < CENTER; dest += speed[0])  // goes from 0 degrees to 180 degrees 
       {                                  // in steps of 1 degree 
       s1.write(dest);              // tell servo to go to position in variable 'pos' 
       delay(speed[1]);                       // waits 15ms for the servo to reach the position 
     }
   }
 }
 }
 }
 }

void stopMove()
{
  Serial.println("Interrupted");
  s1.write(CENTER);
  delay(800);
}

Instead of sweep take a look at the blink without delay example and then change that to work with the servo. Avoid using loops with in your program (at least in the way that sweep does) Remember that you are already in a never ending loop.

Mark

I have never used the VarSpeedServo library - I just use the standard servo library.

Calling a pin number "INTERRUPT" and then using digitalRead(INTERRUPT); is very confusing. The concept of an interrupt is that it works autonomously in the background.

Your code seems very long for what seems to be a simple requirement.

Look at the code in this demo several things at a time. It should provide a simple basis for evolving into what you need.

Your code to move the servo should not have a loop. It should just move the servo to whatever position is saved in a variable

void moveServoToNewPosition() {
   myServo.writeMicroseconds(servoPos);
}

The position to move to should be updated elsewhere. If it does not change the servo won't move.

The interval between moves can be managed using the BWoD technique as follow. Assume that in each iteration of loop() the variable currentMillis is updated.

void moveServoToNewPosition() {
   if (currentMills - previousMillis >= stepIntervalMillis) {
       myServo.writeMicroseconds(servoPos);
      previousMillis += stepIntervalMillis;
   }
}

Then the speed of movement can be adjusted either by changing the size of the step movement or the interval between steps.

With the structure I suggested earlier the function readSensor() can also check for a signal on Pin 2 and then the functionn updateServoPosition() use that to set the value of servoPos to whatever you want. Should all be very straightforward.

...R