[Solved]Interrupt CHANGE does not behave as expected.

Hello World,
I am currently building a stepper driven camera dolly and the electronics work great so far. The logic is an Arduino Micro and the stepper driver is a Pololu A4988. However, I still have troubles with the programming especially interrupts.

There are two buttons triggering an interrupt. One on each side of the dolly so the Arduino knows when the sled has hit the end of the rail. Now what the code below is supposed to do is peddle free 3000 steps (that’s half a rotation for the geared stepper) if one button is HIGH on start. This works. Then attach the interrupts and go right until it hits a button again. Then go left in iterations of 1000 until it hits the other button.

Well the peddling free part works then a small delay and then the motor starts turning counter clockwise and never stops. When I press the interrupt buttons I can feel a small jolt in the stepper, so there is definitely something happening, but the motor won’t stop and change direction.

My best guess is that I have misunderstood the logic behind interrupt CHANGE or it is something really stupid and simple as so often when I try to program. :roll_eyes:

const int buttonStopLeft = 2;
const int buttonStopRight = 3;
const int stepperPin = 7;
const int directionPin = 8;
const boolean left = true; 
const boolean right = false;

int motorPhaseDelay = 400;

boolean contactLeft = false;
boolean contactRight = false;

void setup(){ 
  pinMode(buttonStopLeft, INPUT);
  pinMode(buttonStopRight, INPUT);       
  pinMode(directionPin, OUTPUT);
  pinMode(stepperPin, OUTPUT);
  

  if(digitalRead(buttonStopRight) == HIGH){
    photoStep(left,3000);
  }
  if(digitalRead(buttonStopLeft) == HIGH){
    photoStep(right,3000);
  }
  delay(3000);
  attachInterrupt(1, contactToggleLeft, CHANGE);
  attachInterrupt(0, contactToggleRight, CHANGE); 
  
  videoStep(right); //Go Right
  delay(3000);
  while(contactLeft == false){ //Go left and count steps
    photoStep(left,1000);
  }
  delay(3000);  
}

void contactToggleLeft(){
  static unsigned long lastInterruptTime = 0;//Debounce
  unsigned long interruptTime = millis();
  if (interruptTime - lastInterruptTime > 100){
    contactLeft != contactLeft;
  }
  lastInterruptTime = interruptTime;
}
void contactToggleRight(){
  static unsigned long lastInterruptTime = 0;//Debounce
  unsigned long interruptTime = millis();
  if (interruptTime - lastInterruptTime > 100){
    contactRight != contactRight;
  }
  lastInterruptTime = interruptTime;
}

void photoStep(boolean dir,int stepAmount){
  digitalWrite(directionPin,dir);
  int i = 0;
  if(dir == true){
    while(i<stepAmount && contactLeft == false){
      steps();
      i++;
    }
  }
  else{
    while(i < stepAmount && contactRight == false){
      steps();
      i++;
    }
  }
}

void videoStep(boolean dir){
  digitalWrite(directionPin,dir);
  if(dir == true){
    while(contactLeft == false){
      steps();
    }
  }
  else{
    while(contactRight == false){
      steps();
    }
  }  
}

void steps(){
  digitalWrite(stepperPin, HIGH);
  delayMicroseconds(motorPhaseDelay);
  digitalWrite(stepperPin, LOW);
  delayMicroseconds(motorPhaseDelay);
}

void loop(){
}

Thanks in advance
MooJuice

There are two buttons triggering an interrupt.

So, you have an interrupt handler register that a switch was pressed. Later, you check to see if the interrupt service routine was invoked, and react accordingly.

That is instead of reading the limit switch, which, by definition, is still pressed.

Please explain THAT.

Serial.print() in an ISR is REALLY not a good idea.

void steps(){
  digitalWrite(stepperPin, HIGH);
  delayMicroseconds(motorPhaseDelay);
  digitalWrite(stepperPin, LOW);
  delayMicroseconds(motorPhaseDelay);
}

How many steps are taken?

Where does it matter that the limit switches were hit?

Variables used by an ISR and other functions need to be marked volatile. None of your variables are.

Hello PaulS,
Thank you for the quick reply. I didn’t mean to shout CHANGE it is just written in all caps in the code.^^
Sorry I had edited out the Serial.print() before you sent your reply. It was a relic I had forgotten from when I was testing the switch debounce.

PaulS:
So, you have an interrupt handler register that a switch was pressed. Later, you check to see if the interrupt service routine was invoked, and react accordingly.

Exactly. My logic behind this is that, if for example the camera sled moves right and hits the switch, the switch goes high and triggers the change interrupt. The Interrupt sets contactRight to true thus ending the while loop in the videoStep function. If the motor goes left now the switch is released, triggers change again and contactRight is set to false. Now movements to the right are possible again.

void videoStep(boolean dir){
  digitalWrite(directionPin,dir); //dir == true == move to the left 
  if(dir == true){
    while(contactLeft == false){
      steps();
    }
  }
  else{
    while(contactRight == false){
      steps();
    }
  } 
}

Thank you for the hint with volatile declarations. I have set the booleans contactLeft and contactRight to volatile since these are the only variables used outside the interrupts. It didn't solve the problem but I have learned something new. :smiley:

These statements, which appear in contactToggleLeft() and contactToggleRight(), may not be what you want:

   contactLeft != contactLeft;
    contactRight != contactRight;

You may have intended to invert the logical sense of the contact variables. This code won't do that. As I see it, those lines return a boolean value describing whether contact isn't equal to itself - certain to be false - which it then silently discards. You probably wanted something else.

:o Thank you tmd3. That solved it... As I said it is probably something really simple and stupid. I of course meant to write "contactLeft = !contactLeft;" :cold_sweat:

The Interrupt sets contactRight to true thus ending the while loop in the videoStep function.

It is far simpler to simply poll the state of the limit switches in the while loop, and break when one of them changes state.

Yes it would be, but it comes in handy later when I have further developed the program. Also, since the switch is checked before every single step I thought that comparing a boolean might be faster than a digitalRead. Also I learned about interrupts this way.^^

Also I learned about interrupts this way.

What I hope you learned was to use them only as necessary. They are A tool. Like a hammer, it is not always the best tool in the box.