An Alternative to a Hypothetical Delay in Interrupt

I am currently working on a robot arm project for school. The arm will be autonomous and be able to sense objects and pick them up using SONAR and touch sensing. If at any time during the operating time that the arm fails I want to be able to manually control the arm at any time at the push of a button. I was thinking that an interrupt would work but I have noticed that they don't take delay functions. I would need a delay function to regulate the speed of the servo motors that I am using. What I need is a manual override button that when pressed, the arm will go into a loop where I can input values from buttons "1s and 0s" to control the different joints on the arm. I don't want to have to continuously check for a button press after every line in the code. What kind of alternative would there be to compensate for this?

The following code is what I tried to upload onto my Arduino UNO for the manual override of a single servo motor. At first it is just sweeping back and forth and then I push the override button and I have control.

#include <Arduino.h>
#include <Servo.h>

int interruptPin = 0; // attached to pin 2
volatile int servoPos = 180; // servo position angle
int rightButton = 13; // pin 13
int leftButton = 12; // pin 12
Servo servo; // 180 servo
void manual();

void setup(){
  attachInterrupt(interruptPin, direction, FALLING);
  servo.attach(6);
  pinMode(rightButton, INPUT);
  pinMode(leftButton, INPUT);
}

void loop(){
  for(servoPos = 180; servoPos > 1; servoPos--){
    servo.write(servoPos);
    delay(10);
  }
  for(servoPos = 1; servoPos < 180; servoPos++){
    servo.write(servoPos);
    delay(10);
  }
}

void manual(){
  if(digitalRead(rightButton) == 1){
    servoPos++;
    servo.write(servoPos);
    delay(10);
  }else{
    servo.write(servoPos);
  }
  if(digitalRead(leftButton) == 1){
    servoPos--;
    servo.write(servoPos);
    delay(10);
  }else{
    servo.write(servoPos);
  }
}

You'll just want to get rid of every delay in your code. You will probably have to replace it with something like the blink without delay example and a state machine:

enum states {SERVO_UP, SERVO_DOWN, MANUAL} state = SERVO_UP;
int servopos;

void loop() {
  static unsigned long prevTime;
  unsigned long curTime = millis();
  if (digitalRead(overridebtn)) state = MANUAL;
  if (curTime - prevTime >= 10) {
    prevTime = curTime;
    switch(state) {
    case SERVO_UP:
      servopos++;
      break;
    case SERVO_DOWN:
      servopos--;
      break;
    case MANUAL:
      if (digitalRead(upbtn)) servopos++;
      else if (digitalRead(downbtn)) servopos--;
      break;
    }
    servo.write(servopos);
  }
}

What you need to do in your ISR is set a flag - a boolean called manualOverrideInEffect, perhaps. Initialize that flag to false. Set it to true in the ISR. Then, in loop, check that flag. Do the automatic thing if false, and the manual thing if true.

I guess you're thinking to use the interrupt to detect the button press. That's not necessary or (IMO) sensible for this problem.

Just poll the button to detect button presses and update a state variable when the button press is detected. The state variable would indicate whether the sketch is in automatic or manual mode.

I will definitely look into state machines. I think this is what I need. Thanks!

I have written a code that is supposed to work with a 360 servo but I have ran into some issues. When I run the code the servo just jitters and my override button doesn't work either. I can't seem to find anything conceptually wrong but I may have made a silly syntax error. Could anybody pick it out?

#include <Arduino.h>
#include <Servo.h>

Servo servo;
int OVERRIDE = 2;
int rightButton = 12;
int leftButton = 13;
int servoPos;
static unsigned long prevTime = 0;
void manualControl();
bool manualTrigger = false;
bool autoLoopStart = true;
bool loopStart = false;
static unsigned long servoTime;
enum states{AUTO, MANUAL};

void setup(){
	servo.attach(5);
	pinMode(rightButton, INPUT);
	pinMode(leftButton, INPUT);
	pinMode(OVERRIDE, INPUT);
	servo.write(93);
}

void loop(){
	states state = AUTO;
	prevTime = millis();
	if(digitalRead(OVERRIDE) == HIGH){
		state = MANUAL;
	}
	switch(state){
	case AUTO:
		do{
			servoTime = millis();
			autoLoopStart = false;
		}while(autoLoopStart == true);
		if(millis() - servoTime <= 3000){
			servoPos = 93;
			servo.write(servoPos);
		}
	case MANUAL:
		manualControl();
		break;
	}
}

void manualControl(){
	while(1){
		if(digitalRead(leftButton == 1)){
			servoPos = 91;
			servo.write(servoPos);
		}else{
			servoPos = 90;
		}
		if(digitalRead(rightButton == 1)){
			servoPos = 89;
			servo.write(servoPos);
		}else{
			servoPos = 90;
			servo.write(servoPos);
		}
	}
}

note: I don't want to ever exit fom manual control which is why I have put in the while(1) loop.

(1) How many times will this loop execute ?

    do
    {
      servoTime = millis();
      autoLoopStart = false;
    }
    while(autoLoopStart == true);

(2) There is no break; at the end of the AUTO case

(3) When the routine gets to the manualControl() function it will move between angles of 90/89 or 89/90, hence the jitter.

note: I don't want to ever exit fom manual control which is why I have put in the while(1) loop.

I wouldn't do that; I would instead say that in the 'manual' case, it never reverts back to the 'auto' case. That way you can easily change it if you change your mind (eg press another button instead of "reset"). The switching from auto to manual should also take place inside the "AUTO" case since it only makes sense to "switch" to manual when you're not in it already.

However if you do that another problem will show its face -- your variable "state" (of type "states") is forgotten at the end of loop() and recreated at the beginning, and the whole point of the variable is to keep track of the state between loops. Using the "static" keyword means it will keep its value between calls of loop().

In the manualControl() function, you should only call servo.write() once. That way, you can be sure you don't write it multiple times in one loop.

Your digitalRead() syntax is wrong. You don't want to check if "rightButton" is 1, you want to check if digitalRead(rightButton) is 1. So just move the parentheses.

do{
			servoTime = millis();
			autoLoopStart = false;
		}while(autoLoopStart == true);
		if(millis() - servoTime <= 3000){

the if will never be true, since you had just set the value of servoTime. You probably want to have an if around that statement rather than a do...while(), which always executes the code at least once.