Can/how do I use interrupts to improve this code?

This is my first Arduino project ever, so this code is probably pretty inefficient. This is the code for a motorized trap door (for our cat). There is a momentary switch on either side of the door, and if he pushes the switch, the door opens and stays open for 10 seconds and closes automatically behind him. There is also an override switch on the inside to hold the door open for longer periods of time for training purposes.

Here is a video of the servo opening and closing the door:

Despite probably being inefficient, the code seems to work fine for this purpose except one problem. Our cat as already ruined the servo motor by forcing the door open rather than using the switch. I want to know if there is a way to modify the code so that it basically goes to sleep most of the time, and if either switch is actuated, then it does something. I am guessing interrupts are the best way to do this, but not sure. This would also mean that if he forces the door open, the motor will just open rather than trying to keep the door closed.

Thanks in advance for the advice!

#include <Servo.h>

#define servoPin 3
#define switchPin1 A0
#define switchPin2 A1
#define override_switchPin A2
#define openPosition 0
#define closedPosition 140

Servo myservo;  // create servo object to control a servo

int switchState1 = LOW;
int switchState2 = LOW;
int overrideState = LOW;
int lastOverrideState = LOW;
//boolean isOpen = false;
unsigned long lastDebounceTime = 0; //the last time a switch was toggled
unsigned long debounceDelay = 50; //the debounce time; increase/decrease as necessary

void setup() {
  myservo.attach(servoPin);  // attaches the servo on pin X to the servo object
  pinMode(switchPin1, INPUT);
  pinMode(switchPin2, INPUT);
  pinMode(override_switchPin, INPUT);
  myservo.write(closedPosition);
}

void loop() {
  //polling all three switches for a change
  switchState1 = digitalRead(switchPin1);
  switchState2 = digitalRead(switchPin2);
  overrideState = digitalRead(override_switchPin);

  if((millis() - lastDebounceTime)>debounceDelay && overrideState == HIGH){
    lastDebounceTime = millis();
    lastOverrideState = overrideState;
    myservo.write(openPosition);
    }
  
  if(overrideState != lastOverrideState && overrideState == LOW && (millis() - lastDebounceTime)>debounceDelay){
    lastDebounceTime = millis();
    lastOverrideState = overrideState;
    closeDoor(15);
    }
    
   if(overrideState == LOW && (switchState1 == HIGH || switchState2 == HIGH)){
      myservo.write(openPosition);
      delay(5000); //door to stay open for 5 seconds
      closeDoor(15);//15ms delay
    }
}


//this funciton is only called in two spots
 void closeDoor(int closeDelay){
    for (int pos=openPosition; pos <= closedPosition; pos +=1){
       myservo.write(pos);
       delay(closeDelay); //delay xx milliseconds each degree of rotation to slow the motor down
     }
 }

After thinking about this some more, I'm wondering maybe interrupts aren't the way to stop the servo gears from getting stripped. I need a way to switch off power to the servo when its not in use incase he tries to force his way in. A relay is the first thing that comes to mind, but wondering if there is a better way to do that?

A transistor, either bipolar or a logic-level FET, is a great way to switch power and there are many tutorials on how to do this with an Arduino.

I think interrupts can't work in here. If you want to turn only the Servo motor off when the switch is not pressed. You should use a heavy duty transistor to power the servo as Arduino 5v cant power the servo because when the servo will move, then the Arduino will go to a reset because of the power cut. You should use a MOSFET to power on the servo. And when the switch is pressed, tell the Arduino to first turn on the MOSFET, and then send signal to the servo to move.
You should use a small power supply to connect to the MOSFET. I think these small lines of code will work.

int mosfetpin 2;
int servopin 3;

if(switchpressed) {
//turn on MOSFET
//Tell servo to goto default position first
delay(1000)
//Tell the servo to open door
}
The delay 100 is very important or the servo will crash

Reply if it works. :slight_smile:

Try detaching the servo when its motion has finished.

Even if you completely de-power a servo repeatedly pushing against its gear train will lead to failure. Servos are not designed for that. This will be especially true if the gears are pushed quickly.

If you need the door to be capable of being opened by pushing then you need to find another mechanical arrangement. Please describe how you would like the system to work.

...R

I would suggest, seeming as you want to train your cat to press the button, your best option would be to prevent forced pushing of the flap from opening it at all?

If he forces it and it opens, then that just reinforces that very same unwanted behaviour for next time. Sorry, but I think like that you will get nowhere fast.

When closed and flap is not meant to be being opened, I would deploy a mechanical deadlock. In that case having the flap mechanically open is neat, but somewhat redundant. Cats are quire good at learning to open flaps... :wink:

However, if you have both functions it should prevent damage to your servo and assist with your training - the flap opening and allowing entrance/egress will act as it's own positive reinforcement for correct behaviour.

Edit: I'd probably start by modifying a 12VDC solenoid door lock. Cheap, easy to obtain and designed for purpose.

Robin2:
Try detaching the servo when its motion has finished.

Even if you completely de-power a servo repeatedly pushing against its gear train will lead to failure. Servos are not designed for that. This will be especially true if the gears are pushed quickly.

If you need the door to be capable of being opened by pushing then you need to find another mechanical arrangement. Please describe how you would like the system to work.

…R

That’s what I was thinking, I tested with a relay between the servo and its power supply to cut it off when not is use last night. It would be nice if the door could be pushed open but I can’t think of a way to do this mechanically

pcbbc:
I would suggest, seeming as you want to train your cat to press the button, your best option would be to prevent forced pushing of the flap from opening it at all?

If he forces it and it opens, then that just reinforces that very same unwanted behaviour for next time. Sorry, but I think like that you will get nowhere fast.

When closed and flap is not meant to be being opened, I would deploy a mechanical deadlock. In that case having the flap mechanically open is neat, but somewhat redundant. Cats are quire good at learning to open flaps... :wink:

However, if you have both functions it should prevent damage to your servo and assist with your training - the flap opening and allowing entrance/egress will act as it's own positive reinforcement for correct behaviour.

Edit: I'd probably start by modifying a 12VDC solenoid door lock. Cheap, easy to obtain and designed for purpose.

Thanks for the suggestion. I was thinking about doing this but it does add some mechanical complexity to the set up. Instead of using a solenoid lock, I saw this guys project and thought about added a second small servo just like he did (minus all the bluetooth)

EDIT: I actually decided to order the 12V solenoid lock as you had suggested

ArnavPawarAA:
I think interrupts can't work in here. If you want to turn only the Servo motor off when the switch is not pressed. You should use a heavy duty transistor to power the servo as Arduino 5v cant power the servo because when the servo will move, then the Arduino will go to a reset because of the power cut. You should use a MOSFET to power on the servo. And when the switch is pressed, tell the Arduino to first turn on the MOSFET, and then send signal to the servo to move.
You should use a small power supply to connect to the MOSFET. I think these small lines of code will work.

int mosfetpin 2;

int servopin 3;

if(switchpressed) {
//turn on MOSFET
//Tell servo to goto default position first
delay(1000)
//Tell the servo to open door
}
The delay 100 is very important or the servo will crash




Reply if it works. :)

I tried added a relay in to cut off the servo power when its not in use. Seems to work well. Not sure what the pros and cons are to using a transistor versus a relay. I just use a relay because its what I had lying around.

I did share the hardware schematic, but the servo is not power directly from the arduino, it is connected to buck converter which drops in 12V supply down to 6.8V. The power supply is rated for 2A.