Emergency stopping with I2C

Hi,

I have questions regarding a couple scenarios I tried. I am currently using a touch screen shield from Adafruit that's mounted in my Arduino Uno (1). I connected it with I2C to another Arduino Uno (2). I want a "button" on the touch screen that will to stop all motors at any time. For example, when the button is touched, STOP==1.

  1. I tried using a while loop right under void loop(), but it runs through the whole void loop before checking the while condition again.
void loop(){
 while (!STOP==1){
 //run motors
 }
}
  1. Since the ISR acts like an interrupt, I added a function that executes when STOP==1. Currently, the function just prints "EMERGENCY STOPPING". Nothing printed when ran.
#include <NewPing.h>
#include <Wire.h>

#define D1 11   //for LA
#define Stop 7 //for LA
#define D3 12    //for LA

#define TRIGGER_PIN  4  // for trigger ultrasonic sensor.
#define ECHO_PIN     6  // for echo ultrasonic sensor..
#define MAX_DISTANCE 30 // for ultrasonic sensor.
NewPing sonar(TRIGGER_PIN, ECHO_PIN, MAX_DISTANCE);
volatile int uSonar = 0;
volatile int pipeSSelection = 5;
volatile int STOP = 0;
void setup() {
  pinMode(D1, OUTPUT);
  pinMode(Stop, OUTPUT);
  pinMode(D3, OUTPUT);
  pinMode(4, INPUT);
  pinMode(5, INPUT);
  pinMode(6, INPUT);
  pinMode(8, OUTPUT);
  pinMode(9, OUTPUT);
  pinMode(10, OUTPUT);

  digitalWrite(8, HIGH);//wheel stops

  digitalWrite(Stop, HIGH); //STOP on LA
  // put your setup code here, to run once:
  Wire.begin(4);                // join i2c bus with address #4
  Wire.onReceive(receiveEvent);

  Serial.begin(9600);
}

void loop() {
  exitLA(10);
  unsigned int uS = sonar.ping();
  uS = uS / US_ROUNDTRIP_CM;
  uSonar = 0;
  while (uS < 17) {
    uSonar = 1;
    break;
  }

  Serial.print("uSonar: ");
  Serial.print(uSonar);
  Serial.print("\n");
  Serial.print("pipeSelection:  ");
  Serial.print(pipeSSelection);
  Serial.print("\n");
  if ((pipeSSelection == 1) && (uSonar == 1)) { //reading ultrasonic sensor
    digitalWrite(8, LOW); //Spins the wheel
    delay(500);
    Serial.print("Wheels spun");
    Serial.print("\n");
    enterLA(13); //Linear actuator Enters
    delay(10000);
    digitalWrite(Stop, HIGH); //stops LA
    Serial.print("LA entered and stopped");
    Serial.print("\n");
    while (digitalRead(5) == LOW) { //reading the limit switch
      Serial.print("waiting for LS");
      Serial.print("\n");
    }
    if (digitalRead(5) == HIGH) { //Limit swith detects pipe
      Serial.print("LS detects pipe");
      Serial.print("\n");
      delay(2000); //turns the pipe 200 deg
      Serial.print("pipe spun 200 degrees");
      Serial.print("\n");
      digitalWrite(8, HIGH); //Stops wheel
      Serial.print("Wheel stopped");
      Serial.print("\n");
      digitalWrite(9, HIGH); //FANUC_1 DI=ON
      Serial.print("FANUC DI_1 is ON!!!");
      Serial.print("\n");
      delay(500);
      digitalWrite(9, LOW); //FANUC_1 DI=OFF
      Serial.print("FANUC DI_1 turned OFF");
      Serial.print("\n");
      digitalWrite(8, LOW); //starts wheel
      delay(2500); //turns pipe 180
      Serial.print("Wheels spun 180");
      Serial.print("\n");
      digitalWrite(8, HIGH); //stops wheel
      Serial.print("Wheels stopped");
      Serial.print("\n");
      digitalWrite(9, HIGH); //FANUC_1 DI=ON
      Serial.print("FANUC DI_1 is ON!!!");
      Serial.print("\n");
      delay(1000);
      digitalWrite(9, LOW); //FANUC_1 DI=OFF
      Serial.print("FANUC DI_1 turned OFF");
      Serial.print("\n");
      exitLA(15);
      exit;
    }
  }
  else if (pipeSSelection == 2) {
    if (uSonar == 1) {
      //ultrasonic sensor detects pipe
      digitalWrite(8, HIGH); //Spins the wheel

      //LA movement
      digitalWrite(Stop, LOW);
      digitalWrite(D1, LOW);
      digitalWrite(D3, LOW); //Linear actuator Enters
      delay(13000);
      digitalWrite(Stop, HIGH);
      //Linear actuator positions the pipe


      while (digitalRead(5) == LOW) { //reading the limit switch
        return;
      }
      if (digitalRead(5) == HIGH) { //Limit swith detects pipe
        delay(2000); //turns the pipe 200 deg
        digitalRead(5) == LOW; //Stops wheel
        digitalWrite(10, HIGH); //FANUC_2 DI=ON
        delay(50);
        digitalWrite(10, LOW); //FANUC_2 DI=OFF
        digitalWrite(8, HIGH); //starts wheel
        delay(2500); //turns pipe 180
        digitalWrite(8, LOW); //stops wheel
        digitalWrite(10, HIGH); //FANUC_2 DI=ON
        delay(50);
        digitalWrite(10, LOW); //FANUC_2 DI=OFF
        exit;
      }
    }
  }
}
void EmergencyStop() {
  Serial.print("EMERGENCY STOPPING");
  Serial.print("EMERGENCY STOPPING");
  Serial.print("EMERGENCY STOPPING");
  Serial.print("EMERGENCY STOPPING");
  Serial.print("EMERGENCY STOPPING");
  Serial.print("EMERGENCY STOPPING");
  Serial.print("EMERGENCY STOPPING");
  Serial.print("EMERGENCY STOPPING");
}
void receiveEvent(int howMany) {
  byte Slave[2];
  int index = 0;
  while (Wire.available() && index < 2) {
    Slave[index] = Wire.read();
    index++;
  }
  pipeSSelection = Slave[0];
  STOP = Slave[1];
  if (STOP == 8) {
    EmergencyStop();
  }
}
void enterLA (int enterTime) {
  int x = 0;
  digitalWrite(Stop, LOW);
  while (x < enterTime) {
    digitalWrite(D1, LOW);
    digitalWrite(D3, LOW);
    x++;
  }

  return;
}
void exitLA (int exitTime) {
  digitalWrite(Stop, LOW);
  for (int x = 0; x < exitTime; x++) {
    digitalWrite(D1, HIGH);
    digitalWrite(D3, HIGH);
  }
  return;
}
void stay (int stayTime) {
  for (int x = 0; x < stayTime; x++) {
    digitalWrite(Stop, HIGH);
    digitalWrite(D1, HIGH);
    digitalWrite(D3, LOW);
  }
  return;
}
  1. If I'm able to get have digitalOutput from the touch screen, currently asking the manufacturer, could I use an attachInterrupt? It wouldn't be a continuous wave.

Since the ISR acts like an interrupt

What do you mean "acts like" an interrupt? An ISR (Interrupt Service Routine) is the function that gets called when an interrupt happens.

  while (uS < 17) {
    uSonar = 1;
    break;
  }

WTF? How many times will this while loop iterate? Why would you use a while statement when you mean if?

      exit;

Another WTF. What do you think this is doing? You might as well have

   42;

there.

Even if you succeed in sending a stop sign to that code, it will ignore it, since all that you do in the interrupt service routine is overflow the outgoing serial buffer, causing the program to lock up. Do NOT do Serial.print() in an ISR, or in anything called from an ISR.

  1. I tried using a while loop right under void loop(), but it runs through the whole void loop before checking the while condition again.

Is that on the sender or on the receiver? What is

// run motors

supposed to accomplish?

If it's an emergency stop, you shouldn't be relying on software.

My first thought was the same as @AWOL's.

If it is not a safety-related stop and if all you want to do is have one button that stops everything rather than pressing 2 or 3 separate buttons you need your receiving program to listen continuously for new instructions.

There are two parts to the problem - your program must not be so preoccupied with other things that it fails to detect a new command and it must receive the new commands as they arrive without interfering with the other things.

The demo several things at a time and the examples in serial input basics allow that to happen.

...R

PaulS:
What do you mean "acts like" an interrupt? An ISR (Interrupt Service Routine) is the function that gets called when an interrupt happens.

I meant the I2C data transfer acts like an ISR. Master sends the slave new data anytime the touch screen senses anything. Therefore, I was asking if it made sense for me to have this function in the I2C command:

void EmergencyStop() {
  //stop motors
}
void receiveEvent(int howMany) {
  byte Slave[2];
  int index = 0;
  while (Wire.available() && index < 2) {
    Slave[index] = Wire.read();
    index++;
  }
  pipeSSelection = Slave[0];
  STOP = Slave[1];
  if (STOP == 8) {
    EmergencyStop();
  }
}

Is that on the sender or on the receiver? What is

// run motors

supposed to accomplish?

This is all code on the receiver. //run motors represents code I haven't created yet that will start the motors.

Robin2:
My first thought was the same as @AWOL's.

The demo several things at a time and the examples in serial input basics allow that to happen.

...R

It is not for safety stops, so it is okay running it on software for now.

I did look at your links. The difference is that the process I want to interrupt takes a total of 8 seconds. In contrast, your LED example functions were short and could move onto the next function quickly. What would you recommend for such a long process? The emergency stop button would have to be able to execute a function to stop all motors even if the program was still in the middle of running another function.

Mebble:
I did look at your links. The difference is that the process I want to interrupt takes a total of 8 seconds. In contrast, your LED example functions were short and could move onto the next function quickly. What would you recommend for such a long process? The emergency stop button would have to be able to execute a function to stop all motors even if the program was still in the middle of running another function.

You should not have (and almost certainly do not need to have) a function that takes more than a few millisecs for each iteration. That is the concept underlying the code in several things at a time.

Post you complete program.

...R

I posted my complete program in the original question. It's not the program that takes 8 seconds, it's the time I need that I need the motors to spin. For example, motor #1 needs to move for 2 seconds, then motor #2 takes 3 seconds, all while motor #3 has been spinning the entire time.

Cleaned up code:

#include <NewPing.h>
#include <Wire.h>

#define D1 11   //for LA
#define Stop 7 //for LA
#define D3 12    //for LA

#define TRIGGER_PIN  4  // for trigger ultrasonic sensor.
#define ECHO_PIN     6  // for echo ultrasonic sensor..
#define MAX_DISTANCE 30 // for ultrasonic sensor.
NewPing sonar(TRIGGER_PIN, ECHO_PIN, MAX_DISTANCE);
volatile int uSonar = 0;
volatile int pipeSSelection = 5;
volatile int STOP = 0;
void setup() {
  pinMode(D1, OUTPUT);
  pinMode(Stop, OUTPUT);
  pinMode(D3, OUTPUT);
  pinMode(4, INPUT);
  pinMode(5, INPUT);
  pinMode(6, INPUT);
  pinMode(8, OUTPUT);
  pinMode(9, OUTPUT);
  pinMode(10, OUTPUT);

  digitalWrite(8, HIGH);//wheel stops

  digitalWrite(Stop, HIGH); //STOP on LA
  // put your setup code here, to run once:
  Wire.begin(4);                // join i2c bus with address #4
  Wire.onReceive(receiveEvent);

  Serial.begin(9600);
}

void loop() {
  exitLA(10);
  unsigned int uS = sonar.ping();
  uS = uS / US_ROUNDTRIP_CM;
  uSonar = 0;
  if (uS < 17) {
    uSonar = 1;
  }
  if ((pipeSSelection == 1) && (uSonar == 1)) { //reading ultrasonic sensor
    digitalWrite(8, LOW); //Spins the wheel
    delay(500);
    enterLA(13); //Linear actuator Enters
    delay(10000);
    digitalWrite(Stop, HIGH); //stops LA
    while (digitalRead(5) == LOW) { //reading the limit switch
    }
    if (digitalRead(5) == HIGH) { //Limit swith detects pipe
      delay(2000); //turns the pipe 200 deg
      digitalWrite(8, HIGH); //Stops wheel
      digitalWrite(9, HIGH); //FANUC_1 DI=ON
      delay(500);
      digitalWrite(9, LOW); //FANUC_1 DI=OFF
      digitalWrite(8, LOW); //starts wheel
      delay(2500); //turns pipe 180
      digitalWrite(8, HIGH); //stops wheel
      digitalWrite(9, HIGH); //FANUC_1 DI=ON
      delay(1000);
      digitalWrite(9, LOW); //FANUC_1 DI=OFF
      exitLA(15);
    }
  }
  else if (pipeSSelection == 2) {
    if (uSonar == 1) {
      //ultrasonic sensor detects pipe
      digitalWrite(8, HIGH); //Spins the wheel

      //LA movement
      digitalWrite(Stop, LOW);
      digitalWrite(D1, LOW);
      digitalWrite(D3, LOW); //Linear actuator Enters
      delay(13000);
      digitalWrite(Stop, HIGH);
      //Linear actuator positions the pipe


      while (digitalRead(5) == LOW) { //reading the limit switch
        return;
      }
      if (digitalRead(5) == HIGH) { //Limit swith detects pipe
        delay(2000); //turns the pipe 200 deg
        digitalRead(5) == LOW; //Stops wheel
        digitalWrite(10, HIGH); //FANUC_2 DI=ON
        delay(50);
        digitalWrite(10, LOW); //FANUC_2 DI=OFF
        digitalWrite(8, HIGH); //starts wheel
        delay(2500); //turns pipe 180
        digitalWrite(8, LOW); //stops wheel
        digitalWrite(10, HIGH); //FANUC_2 DI=ON
        delay(50);
        digitalWrite(10, LOW); //FANUC_2 DI=OFF
        exit;
      }
    }
  }
}
void EmergencyStop() {
  //stop motors
}
void receiveEvent(int howMany) {
  byte Slave[2];
  int index = 0;
  while (Wire.available() && index < 2) {
    Slave[index] = Wire.read();
    index++;
  }
  pipeSSelection = Slave[0];
  STOP = Slave[1];
  if (STOP == 8) {
    EmergencyStop();
  }
}
void enterLA (int enterTime) {
  int x = 0;
  digitalWrite(Stop, LOW);
  while (x < enterTime) {
    digitalWrite(D1, LOW);
    digitalWrite(D3, LOW);
    x++;
  }

  return;
}
void exitLA (int exitTime) {
  digitalWrite(Stop, LOW);
  for (int x = 0; x < exitTime; x++) {
    digitalWrite(D1, HIGH);
    digitalWrite(D3, HIGH);
  }
  return;
}
void stay (int stayTime) {
  for (int x = 0; x < stayTime; x++) {
    digitalWrite(Stop, HIGH);
    digitalWrite(D1, HIGH);
    digitalWrite(D3, LOW);
  }
  return;
}

If you want anything to happen in an emergency ALL the delay()s need to go. Use millis() to manage timing as in several things at a time.

And don't use FOR loops for timing either as in your enterLA() function.

With these changes the motors can continue to spin while the Arduino does other things - such as receive a stop instruction.

...R

I am heeding your advice and am adjusting to millis(). Below is the part of my code that I changed. In your example you said:

if ((pipeSSelection == 1) && (uSonar == 1)) { //reading ultrasonic sensor
    currentTime = millis();
    if ((millis() - currentTime) == 500) {
      digitalWrite(8, LOW); //Spins the wheel
      enterLA(13); //Linear actuator Enters
      currentTime = millis();
    }
    if ((millis() - currentTime) == 10000) {
      digitalWrite(Stop, HIGH); //stops LA
    }
    while (digitalRead(5) == LOW) { //reading the limit switch
    }
    if (digitalRead(5) == HIGH) { //Limit swith detects pipe
      delay(2000); //turns the pipe 200 deg
      digitalWrite(8, HIGH); //Stops wheel
      digitalWrite(9, HIGH); //FANUC_1 DI=ON

I don't understand how the "if statement" is waiting for the interval to expire. I thought if statements only ran once, in contrast to while statements which run all the time. I tried to implement millis into my code below. There really is not else statements I can add. It's a linear process where all the motors have to start in order.

if ((pipeSSelection == 1) && (uSonar == 1)) { //reading ultrasonic sensor
    currentTime=millis();
    if ((millis()-currentTime)==500){
    digitalWrite(8, LOW); //Spins the wheel
    enterLA(13); //Linear actuator Enters
    currentTime=millis();
    }
    if ((millis()-currentTime)==10000){
    digitalWrite(Stop, HIGH); //stops LA
    }
    while (digitalRead(5) == LOW) { //reading the limit switch
    }
    if (digitalRead(5) == HIGH) { //Limit swith detects pipe
      delay(2000); //turns the pipe 200 deg
      digitalWrite(8, HIGH); //Stops wheel
      digitalWrite(9, HIGH); //FANUC_1 DI=ON
      delay(500);
      digitalWrite(9, LOW); //FANUC_1 DI=OFF

I'm trying to learn this and really appreciate your help.

If I put all the motor commands into one function, something like your program, how would I add the emergency stop command?

Your code:

 readButton();               // call the functions that do the work
  updateOnBoardLedState();
  updateLed_A_State();
  updateLed_B_State();
  switchLeds();
  servoSweep();

My idea:

MotorsRun();
EmergencyCheck(); //the emergency check would if STOP==1. If STOP==1, would this function's commands override commands in MotorsRun()? They would be sending contradicting commands to the pin via DigitalWrite.

I know that is an incomplete code, but would that idea work?

It's difficult to keep up when you make successive posts with very different stuff.

In answer to Reply #10 "I don't understand how the "if statement" is waiting for the interval to expire."
The function will be called hundreds or thousands of times per second. Most times the IF will fail but at the appropriate time the test will be true.

In relation to Reply#11
I don't understand the relation between your two code snippets. The parts in the second snippet should just go at the bottom of the first snippet.

If the MotorsRun() function is something like this it will respond immediately to the STOP command

void MotorsRun() {
   if (emergencyStop == true) {
        // code to stop motors
   }
   else {
      // other motor code
   }
}

This assumes that the function EmergencyCheck() will set the variable emergencyStop to true if appropriate.

Is the emergency message the only message that will arrive via Serial ?
If not I think the EmergencyCheck() function should be a more general receiveData() function of the sort in serial input basics.

...R