Gate / Door opener RCSwitch interrupts problem

Hi all,
I'm trying to make a gate opener using RCSwitch library (GitHub - sui77/rc-switch: Arduino lib to operate 433/315Mhz devices like power outlet sockets.)
I'm working with a RF receiver 315Mhz(RF Link Receiver - 4800bps (315MHz) - WRL-10533 - SparkFun Electronics)
Everything is working ok, I can read the signals from the different buttons of keyfob (Keyfob 4-Button RF Remote Control - 315MHz : ID 1095 : $6.95 : Adafruit Industries, Unique & fun DIY electronics and kits)
But, the problem is that I want to be able to send new commands while something is processing.
I'm not a pro programmer so my knowledge about interrupts are limited.
Is there any way to use the attachInterrupt(interrupt, ISR, mode) (attachInterrupt() - Arduino Reference) with the RCSwitch library?

EXAMPLE 1:
Press button1 --> Open Gate (relay1 ON)
While Gate is opening if I press button1 again --> Stop Gate (relay1 OFF)

EXAMPLE 2:
Press button1 --> Open Gate (relay1 ON)
While Gate is opening if I press button2 --> Open a (relay2 ON)

RCSwitch mySwitch = RCSwitch();

void setup() {
  Serial.begin(9600);
  mySwitch.enableReceive(0);  // Receiver on inerrupt 0 => that is pin #2
}

void loop() {

  if (mySwitch.available()) {
    button_time = millis();

      Serial.print("Value");
      int value = mySwitch.getReceivedValue();
      Serial.println(value);

      if (value == 0) {
        Serial.print("Unknown encoding");
      } else {
        Serial.println("Value: ");
        Serial.println(value);
      }

      if (value == 21763) {
        Serial.println("OPEN");
        openGate(); //PROBLEM!! WHEN ENTER HERE, STOPS TO RECEIVE NEW COMMANDS UNTIL END
      }

      if (value == 21772) {
        Serial.println("CLOSE");
        closeGate(); //PROBLEM!! WHEN ENTER HERE, STOPS TO RECEIVE NEW COMMANDS UNTIL END
      }
      ....

    }
    mySwitch.resetAvailable();
  }
}

Thanks for any help

        openGate(); //PROBLEM!! WHEN ENTER HERE, STOPS TO RECEIVE NEW COMMANDS UNTIL END

The problems are that:

  1. You are screaming. We may be a bunch of grumpy old men, but we aren't deaf.
  2. You haven't posted the code for openGate().

So, whatever you are doing in openGate() that doesn't read switch states, stop doing that.

PaulS:

  1. You are screaming. We may be a bunch of grumpy old men, but we aren't deaf.

Its not to hear better... it's to see better :wink:

PaulS:
2) You haven't posted the code for openGate().

Ok, the code is here. But the point is: when it enters on another function (ex. openGate) I cant send any commands until it finishes.

void openGate() {

  digitalWrite(relayOpen, HIGH);
  Serial.println("Portao a abrir");
  flag = true;

  long now = millis(); Serial.println("NOW: "); Serial.println(now);
  long start = millis(); Serial.println("START: "); Serial.println(start);

  while (flag == true) {

    now = millis();
    if (now >= (start + timeOpening)) {
      digitalWrite(relayOpen, LOW);
      Serial.println("OPENGATE Opened and waiting some time so the car can get in");
      paused = true;
    }
    if ((now >= start + timeOpening + timeOpen) && (paused == true)) {
      Serial.println("OPENGATE I will close");
      flag = false;
      paused = false;
    }
    Serial.println(now);
  }

  closeGate();
}

But the point is: when it enters on another function (ex. openGate) I cant send any commands until it finishes.

Not true. What is true is that you pay no attention to new commands until you have completely finished dealing with the previous one. openGate() should do nothing more than start the gate opening. closeGate() should do nothing more than start the gate closing. Testing limit switches, etc. should be done in loop(), where you also read the remote control switches.

Opening or closing a gate by powering a motor for a fixed period of time is not a good idea.

What I'm trying to achieve is something like a gate opener that I've bought.
It works like this:
While infrared sensors not interrupted:
EX 1: Press button --> gate start to open x time --> gate is opened --> delay y time --> start to close x time --> gate closed
EX 2: Press button --> gate start to open x time --> press button again --> gate stops --> press button again --> start to close --> press button again --> gate stops --> press button again --> start to open . . . . . . gate closed

I think it can be done with the attachInterrupt() but the mySwitch.enableReceive(0) Receive on inerrupt 0 => that is pin #2 :roll_eyes:

PaulS:

Opening or closing a gate by powering a motor for a fixed period of time is not a good idea.

I plan to use other things like switches to stop it, but I have to make small steps.
I'm trying to make this project with a old gate motor that my fried gave to me. It has 3 wires for running the motor plus 3 wires for other options like give the information about the force/pressure that is doing. Its one more thing to explore...
That can be another way to stop it.

I'm with @PaulS on this.

You should write down all the separate steps that are needed to get your system to work - for example

listenForRCinput
startOpeningGate
checkForGateOpen

and then you will almost certainly find that each of them makes a nice basis for a short function in your code

and you can shuffle these little pieces around to make room for other stuff until you have the full configuration you need.

Don't worry too much about how to implement each piece until you have the overall logic clear in your head.

The other nice thing about using small functions is that you can try them out separately in a short test sketch.

...R

The other nice thing about using small functions is that you can try them out separately in a short test sketch.

You can also create a stub, and call the function. The function doesn't really need to do anything.

void openGate()
{
   Serial.println(F("I need to actually make the gate start opening...));
}

Then, when the order of operations is correct - you see something like:

I need to actually make the gate start opening...
I need to test that the gate is moving and hasn't hit something like a limit switch
I need to stop the gate moving - it hit something
-or-
I need to stop the gate moving - it is all the way open

you can then pick one function and write that code.

Then, pick another, and actually write that code. Then another. Pretty soon, the code will be complete.

If, at any point, you think of something else that needs to be done, like listening to the remote control, it is much easier to incorporate that into 30 tiny functions than it is to incorporate it into one huge function that you don't understand.

Ok, I will write all the small functions I need and try to separate everything, then, I think I will need your help again :slight_smile:
I will take some time because I'm working on this project just at the weekends.
Thanks for your advises

Ok, I've made some changes and tried to have everything separated but the problem is the same:
When I press the button the program starts and, when it's doing something (ex: opening/closing) it will not receive another signal from the remote control.
So, my question is: How do I change this interrupt thing to make it respond every time I press the button.
Thanks again

/*
  Simple example for receiving
  http://code.google.com/p/rc-switch/
*/

#include <RCSwitch.h>

const unsigned long timeOpening = 5000; // open & close time
const unsigned long timeOpen = 2000; // time to be opened
unsigned long button_time = 0;
unsigned long last_button_time = 0;

boolean flag = false;
boolean paused = false;

const int relayOpen = 11; // pin 11
const int relayClose = 12; // pin 12
int switchOpen = 9; // pin 9
int switchClose = 10; // pin 10

RCSwitch mySwitch = RCSwitch();

/*-------------------------------------------------------------------------------*/


void setup() {
  Serial.begin(9600);
  mySwitch.enableReceive(0);  // Receiver on inerrupt 0 => that is pin #2
  pinMode(relayOpen, OUTPUT);
  pinMode(relayClose, OUTPUT);
  pinMode(switchOpen, INPUT_PULLUP);
  pinMode(switchClose, INPUT_PULLUP);

}

/*-------------------------------------------------------------------------------*/
void loop() {

  if (mySwitch.available()) {unsigned long lastRequest = 0;      // when you last made a request

    button_time = millis(); //function to ignore signal if press button < 2 seconds
    if (button_time - last_button_time > 2000)
    {
      last_button_time = button_time;

      Serial.print("Value");
      int value = mySwitch.getReceivedValue();
      Serial.println(value);

      if (value == 0) {
        Serial.print("Unknown encoding");
      } else {
        Serial.println("Value: ");
        Serial.println(value);
      }

      if (value == 21763) { //Value received fron button 1
        Serial.println("Lets Start");
        openGate();
        openGateTimer();
        closeGate();
        closeGateTimer();
      }
    }
    mySwitch.resetAvailable();
  }
}
/*-------------------------------------------------------------------------------*/

void openGate() {
  if (digitalRead(switchOpen) == HIGH) {// switchOpen is pressed - its HIGH just for testing without the switch
    digitalWrite(relayOpen, HIGH);
    Serial.println("The gate is opening");
  }
}
/*-------------------------------------------------------------------------------*/

void closeGate() {
  if (digitalRead(switchClose) == HIGH) {// switchClose is pressed - its HIGH just for testing without the switch
    digitalWrite(relayClose, HIGH);
    Serial.println("The gate is closing");
  }
}
/*-------------------------------------------------------------------------------*/

void openGateTimer() {

  flag = true;

  long now = millis(); //Serial.println("NOW: "); Serial.println(now);
  long start = millis(); //Serial.println("START: "); Serial.println(start);

  while (flag == true) {

    now = millis();
    if ((now >= (start + timeOpening)) && (digitalRead(switchClose) == HIGH)) {//until switchClose is pressed
      digitalWrite(relayOpen, LOW);
      Serial.println("Making some time...");
      paused = true;
    }
    if ((now >= start + timeOpening + timeOpen) && (paused == true)) {
      Serial.println("I have to close now");
      flag = false;
      paused = false;
    }
    //Serial.println(now);
  }
}
/*-------------------------------------------------------------------------------*/

void closeGateTimer() {

  flag = true;

  long now = millis(); //Serial.println("NOW: "); Serial.println(now);
  long start = millis(); //Serial.println("START: "); Serial.println(start);

  while (flag == true) {

    now = millis();
    if ((now >= (start + timeOpening)) && (digitalRead(switchOpen) == HIGH)) {//until switchOpen is pressed
      digitalWrite(relayClose, LOW);
      Serial.println("Gate is closed");
      flag = false;
    }
    Serial.println(now);
  }
}

The concept in my mind is something like this

void loop() {
readAndSaveSwitches();
openGate();
closeGate();
stopGate();
}

The code in (for example) openGate() would check the saved switch value to see if it should start working. The switches would NOT be read within the function.

The code in stopGate() would check if the gate has gone far enough - probably also based on switch states saved in the first function.

Because the code loops around very quickly it will pick up the next switch press quickly.

...R

Yes, but that doest solve my problem...
The thing I'm trying to do is to be able to read the radio signal of remote control every time.
In you example when the program enter on function openGate(), or nay other, it will stop to receive the RF signal until is exit from function.
Thanks for you help Robin

anjoze:
The thing I'm trying to do is to be able to read the radio signal of remote control every time.
In you example when the program enter on function openGate(), or nay other, it will stop to receive the RF signal until is exit from function.

In the concept in my mind the code will only be in each function for a number of microseconds so it will get back to reading the remote plenty fast enough. My outline should, obviously, have had a function to read the remote as in

void loop() {
   readRemote();
   readAndSaveSwitches();
   openGate();
   closeGate();
   stopGate();
}

This demo several things at a time illustrates the idea. And Chapter 8 of this new demo illustrates reading Serial data while doing other things.

...R

  while (flag == true) {

Writing your own delay() function does not alter the fact that you spend long periods of time NOT reading the switch.

What you MUST do is understand that NO function should block. On any given pass through loop, you want to read the switch and you might want to start opening the gate, start closing the gate, stop opening the gate, or stop closing the gate.

Contrary to what Robin says, I think you need to make the decisions in loop(), and only call the functions as needed. Keep the logic in one place. Keep the mechanics of how to do what needs to be done in the functions.

Hi,

After lots of Googling I found this thread. I thought I am the only one in the universe to have this challenge :sob:

The problem described here by anjoze is identical to the problem I have, except I'm using a simple stepper motor, but also using the RCSwitch library.

Case:
When I push the Remote Control button (say 'Right), the motor start to run clockwise. (AccelStepper library; non-blocking method 'runSpeed()' is being used in the loop().) Idem for 'Left'.

Problem:
During this action, The RC commands do not work until the motor has stopped (using endswitches).

Deepest wish:
I want to pause the motor by pressing the same RC switch a second time and I also want to be able to stop the motor by pressing the RC button of the opposite direction.

My code is well-designed using a Finite State Machine based on a nice state diagram.

I hope someone can enlighten me since I'm getting pretty frustrated...

UPDATE: There's another one seeming to have the same problem: Controlar motor Brushless com Arduino via RF. - Laboratorio de Garagem (arduino, eletrônica, robotica, hacking)
(note: is in Spanish - use Google Translate or something)
This makes three of us. The RCSwitch lib now seemes even more questionnable... Unfortunately I don't have the knowledge to dive into it...

My "Deepest wish" is that you post the code you are using. And use the code button

so it looks like this and is easy to follow.

...R

Your wish is my command... :wink:

I deliberately made a set of similar functions (the 'void do_*' series). This is just to make things clear for the moment. I'll optimize it as soon as the code works as expected... Cheers.

#include <RCSwitch.h>
#include <AccelStepper.h>

// Button RC code definitions
#define D3_ON  5246997
#define D3_OFF 5246996

// Interrupts definitions
#define ReceiverInt  0  // Pin 2

// Pin definitions
#define EndSwitchPin  3
#define MotorPin1     8
#define MotorPin2     10 // Correction on motordriver wiring bug
#define MotorPin3     9  // Correction on motordriver wiring bug
#define MotorPin4     11
#define LedPin        13

// Stepper data
#define SPEED  500
#define HALF_STEP 4
#define FULL_STEP 8

// States
#define STOP        0
#define STOP_LEFT  -1
#define STOP_RIGHT  1
#define TURN_LEFT  -2
#define TURN_RIGHT  2
#define PAUSE_LEFT -3
#define PAUSE_RIGHT 3


// Classses  --------------------------------------------------------
AccelStepper stepper(HALF_STEP, MotorPin1, MotorPin2, MotorPin3, MotorPin4);
RCSwitch rfSwitch = RCSwitch(); // 434 receiver


// Functions --------------------------------------------------------
void flash_led() {
  digitalWrite(LedPin, true); 
  digitalWrite(LedPin, false);
}

void do_stop() {
  Serial.println("STOP");
  stepper.setSpeed(0);
  stepper.disableOutputs();
}

void do_turn_right() {
  Serial.println("TURN_RIGHT");
  stepper.setSpeed(SPEED);
}

void do_turn_left() {
  Serial.println("TURN_LEFT");
  stepper.setSpeed(-SPEED);
}

void do_pause_left() {
  Serial.println("PAUSE_LEFT");
  stepper.setSpeed(0);
  stepper.disableOutputs();
}

void do_pause_right() {
  Serial.println("PAUSE_RIGHT");
  stepper.setSpeed(0);
  stepper.disableOutputs();
}

void do_stop_left() {
  Serial.println("STOP_LEFT");
  stepper.setSpeed(0);
  stepper.disableOutputs();
}

void do_stop_right() {
  Serial.println("STOP_RIGHT");
  stepper.setSpeed(0);
  stepper.disableOutputs();
}



void setup() {
  stepper.setMaxSpeed(SPEED);
  stepper.setSpeed(0);

  digitalWrite(EndSwitchPin, INPUT_PULLUP);

  rfSwitch.enableReceive(ReceiverInt);

  Serial.begin(115200);

  if (digitalRead(EndSwitchPin) == LOW) {
    Serial.println("Endswitch activated. Please initialise manually."); 
  }
  while (digitalRead(EndSwitchPin) == LOW) {
    digitalWrite(LedPin, true); 
    delay(200);
    digitalWrite(LedPin, false);
    delay(200);
  }
  Serial.println("System Ok.");   

  Serial.println("[+] Listening");
  flash_led();

}


// Endswitch state handling
int endSwitchState = HIGH;
int lastEndSwitchState = HIGH;

// Remote control command handling
long previousRCMillis = 0;
long RCinterval = 700;

// State machine
int state = STOP;

void loop() {

  int command;

  unsigned long currentMillis = millis();
  if (currentMillis - previousRCMillis > RCinterval) { 
    if (rfSwitch.available()) {
      switch (rfSwitch.getReceivedValue()) {
      case D3_ON:	
        command = TURN_LEFT;
        break;
      case D3_OFF:     
        command = TURN_RIGHT;
        break;
      default:
        command = 0;      
      }
      rfSwitch.resetAvailable();
    }    
    previousRCMillis = currentMillis;
  }

  // Falling edge detection for endswitches
  endSwitchState = digitalRead(EndSwitchPin);
  if (endSwitchState != lastEndSwitchState) {
    if (endSwitchState == LOW) {
      if (state == TURN_LEFT) {
        command = STOP_LEFT;
      }
      if (state == TURN_RIGHT) {
        command = STOP_RIGHT;
      }
    }
    lastEndSwitchState = endSwitchState;
  }

  if (command != 0) {
    switch (state) {
    case STOP:
      switch (command) {
      case TURN_LEFT:
        state = TURN_LEFT;
        do_turn_left();
        break;
      case TURN_RIGHT:
        state = TURN_RIGHT;
        do_turn_right();
        break;
      }
      break;

    case TURN_LEFT:
      switch (command) {
      case TURN_LEFT:
        state = PAUSE_LEFT;
        do_pause_left();
        break;
      case STOP_LEFT:
        state = STOP_LEFT;
        do_stop_left();
        break;
      case TURN_RIGHT:
        state = STOP;
        do_stop();
        break;
      }
      break;

    case TURN_RIGHT:
      switch (command) {
      case TURN_RIGHT:
        state = PAUSE_RIGHT;
        do_pause_right();
        break;
      case STOP_RIGHT:
        state = STOP_RIGHT;
        do_stop_right();
        break;
      case TURN_LEFT:
        state = STOP;
        do_stop();
        break;

      }    
      break;

    case STOP_LEFT:
      switch (command) {
      case TURN_RIGHT:
        state = TURN_RIGHT;
        do_turn_right();
        break;
      }
      break;

    case STOP_RIGHT:
      switch (command) {
      case TURN_LEFT:
        state = TURN_LEFT;
        do_turn_left();
        break;
      }
      break;

    case PAUSE_LEFT:
      switch (command) {
      case TURN_LEFT:
        state = TURN_LEFT;
        do_turn_left();
        break;
      case TURN_RIGHT:
        state = STOP;
        do_stop();
        break;

      }
      break;

    case PAUSE_RIGHT:
      switch (command) {
      case TURN_RIGHT:
        state = TURN_RIGHT;
        do_turn_right();
        break;
      case TURN_LEFT:
        state = STOP;
        do_stop();
        break;

      }
      break;
    }
  }

  stepper.runSpeed();

}

Update:

I monitored the SREG, EIMSK and EICRA registers when I send a command, while the motor runs and after the motor has stopped. (One of my thoughts was that the interrupt setting temporarily toggled for some reason)

They do not change, however.

Update:
When I replace 'stepper.runSpeed()' with 'delay(1)', 'delay(10)', 'delay(100)' or even 'delay(1000)', the statemachine reacts to the RC commands. I know stepper.runSpeed() returns very fast. Since a delay of 100ms already superceeds the time setSpeed() needs to return, this is weird.

Fresh start...
Forget arduino for the moment.
Setup the gate so that

  1. While Relay1 is on the gate opens, Relay2 is disabled and then stops when fully open. It is very unreliable to use time as it can easily be mucked up by a power failure.

while opening, Relay1 OFF will stop the gate opening.

  1. Relay2 is ON gate closes, Relay1 is disabled and the stops when fully closed.

While closing, Relay2 OFF will stop the gate closing.

Sensor Options:

  • A microswitch opens Relays to stop opening or closing
  • or a sensor of some sort is used to determine when the gate is fully open or closed
  • or the drive motor has a clutch and is designed to slip when fully closed or open. In this case a timer could be used instead of switches or sensors.

Once we fully understand the gate electrical, then we can dive into code.

Thanks for posting your code, but I can't figure it out. It seems far more complicated than I imagine to be necessary.

My mental picture is that there is a function readRemote() which saves a value picked up from the remote control and a function that reads all the current switch values. Then the function to open, close or stop the gate will act on that data.

I really do have in mind that the only code in loop() will be the code in Reply #11 - or maybe 2 or 3 lines more.

...R

Hi,

I stripped the code to a working minimum.
There still is a state machine as follows:
STOP -> TURN_LEFT <-> TURN_RIGHT
Also, I separated the RC code in a readRemote() function, to match your mental picture.
That's all there is.

The loop is now:

  1. Listen for a matching RC command
  2. If there is a matching RC command, execute the corresponding state (which is: Serial.println("name of the state"))
  3. Step motor

There ar three states defined to be able to demonstrate the state changes (which currently do not work)

/*
#include <RCSwitch.h>
#include <AccelStepper.h>

#define D3_ON        5246997
#define D3_OFF       5246996
#define STOP         0
#define TURN_LEFT   -2
#define TURN_RIGHT   2

int state = STOP; // Start state

AccelStepper stepper(8, 8, 10, 9, 11);
RCSwitch rfSwitch = RCSwitch(); // 434 receiver

void setup(void) {
  stepper.setMaxSpeed(1000);
  stepper.setSpeed(0);

  rfSwitch.enableReceive(0);

  Serial.begin(115200);
}


int readRemote() {
  int command;

  if (rfSwitch.available()) {
    switch (rfSwitch.getReceivedValue()) {
    case D3_ON:	
      Serial.println("command = TURN_LEFT");
      command = TURN_LEFT;
      break;
    case D3_OFF:     
      Serial.println("command = TURN_RIGHT");
      command = TURN_RIGHT;
      break;
    default:
      command = 0;      
    }
    rfSwitch.resetAvailable();
  }    

  return command;
}

void loop(void) {

  int command = readRemote();

  if (command != 0) {
    switch (state) {
    case STOP:
      switch (command) {
      case TURN_LEFT:
        state = TURN_LEFT;
        Serial.println("state = TURN_LEFT");
        break;
      case TURN_RIGHT:
        state = TURN_RIGHT;
        Serial.println("state = TURN_RIGHT");
        break;
      }
      break;

    case TURN_LEFT:
      switch (command) {
      case TURN_RIGHT:
        Serial.println("state = TURN_RIGHT");
        break;
      }
      break;

    case TURN_RIGHT:
      switch (command) {
      case TURN_LEFT:
        state = STOP;
        Serial.println("state = TURN_LEFT");
        break;
      }    
      break;
    }
  }
  stepper.runSpeed();
}