False Triggers on an Interrupt, from noise?

I've been troubleshooting this for a couple days now and I'm out of ideas. Long story short: I have an emergency switch that triggers an interrupt. The ISR stops all of my motors, puts everything in a safe place, and then hangs the code (I know, thats not great practice for an ISR, but there are enough capacitors in the power supply and motor drivers that a hardware power cutoff didn't seem fast enough.) Every once in a while the switch triggers and freezes the system when it shouldn't. Heck, the emergency switch should technically never trigger. Everything works perfectly the way I had planned, except for these occasional false triggers that then require me to reset the arduino. I'll describe the project in more detail below, but I've tried everything I know how to do.

My project has a cart rotating around a pole driven by 2 DC motors. The cart also includes a DC motor linear actuator, a motor driver running the 3 motors, an emergency switch, a big servo, and a small number of LEDs running from a WLED ESP32. The 2 motors driving the cart probably have 3' of wire to the driver and the linear actuator has probably 6-8'. In a separate location I'm running a knock-off nano controlling the whole thing. Between the cart and the electrical box I have 2 cables that run about 10', one being 12v power and the other a cat6 cable carrying the various signal wires as well as 5v and ground. Other parts of the project I don't believe to be involved in the issue include 4 optical fork sensors running from the nano around the circle that the cart drives and a cat6 cable running from the nano to a ESP32 controlling LEDs.
The program essentially moves the cart to one of the sensors, the linear actuator extends, waits, retracts and then a new sensor is selected to repeat the process.

As far as I can tell, the accidental triggers seem to always happen during the extending phase of the program, but not every time.

I included cable lengths because I was originally very suspicious that long motor wires are causing noise being picked up by my interrupt signal wire. I did everything I knew how to mitigate that, the longest wire (to the linear actuator) I replaced with shielded DMX wire, and I ran all my signals through cat6 hoping to protect them a bit. not every signal is paired with ground or 5v in the cat6, so does that eliminate the usefulness of using that cable? I tied the shielding in the DMX cable to the common ground. I tidied up all the wiring and moved the motor driver to the cart to make the motor wires shorter

I added an external pullup resistor to the interrupt pin, I tried 10k and 5k. (should I keep the pin in pullup mode when adding an external resistor?) I also added a capacitor to ground to try and filter the noise? I tried a few different sizes I had laying around from 100nF, 10uF, and 100uF. Adding these components definitely helped reduce how much it falsely triggers, but it's still happening. I know the capacitor can make a filter, but I don't know what size would be ideal for this situation.

The cart is steel, could that be causing extra noise? Should I try to incorporate some sort of "debouncing" in my emergency switch? I thought about making a check where it trys to detect if the emergency trigger was actually an emergency, but I kind of don't want to mess with that.

I'm not sure what else is useful information to help me troubleshoot this, but I can take more photos or add a diagram if needed. I would greatly appreciate any help

I forgot to add that I have a secondary button hooked to the interrupt pin so I have a button on my electrical box. That shouldn't affect it should it?

A complete diagram is a requirement not an ask, in such cases.

More photos would be helpful, I can not see the Arduino...

Hmmm. You didn't post any code. Since you're asking code questions, I'm kind of surprised you didn't post that. Please use code tags.

Did you read the forum introductory guide that you were asked to?



The yellow and grey cables are the ones going to the cart shown in the previous picture.

I didn't post code because this seemed like a hardware issue to me. For some reason the interrupt pin is going low when it shouldn't be. I can post code, I just thought it would be distracting from my issue.

Did you read the forum introductory guide that you were asked to?

However, looking at the project, I do suspect a wiring issue.

You should post the code because it is often uncertain whether it is a hardware or software issue. If you get that wrong at the start, you can be troubleshooting forever.

My code is not commented very well yet, I apologize.

#include <Servo.h>

#define NUM_STOPS 4
/*************PINS**************/
const int directionPin = 4;
const int speedPin = 5;
const int headPin = 3;
const int armdir = 7;
const int armspeed = 6;

const int pos0 = A2;
const int pos1 = A3;
const int pos2 = A4;
const int pos3 = A5;
const int estop = 2;  //has to be 2 or 3 on arduino nano and uno (interrupt)


// const int targetSelectorPin[NUM_STOPS] = { A0, 3, 4, 5 };
const int needfillPin[NUM_STOPS] = { 8, 9, 10, 11 };
const int refillingPin[NUM_STOPS] = { 12, 13, A0, A1 };
/**********VARIABLES FOR ADJUSTMENT************/

/////////////// MOTORS /////////////////
const unsigned long acceltime = 2700;
const int maxspeed = 255;
const int minspeed = 30;
int currentspeed = 0;
////////////////////////////////////////

/////////////// SERVOS /////////////////
const int headLeft = 1900;
const int headCenter = 1500;
const int headRight = 1100;
const unsigned long servoRefreshRate = 30;
////////////////////////////////////////



/******************** VARIABLES TO NOT MESS *****************/

int STATE = 1;
int LASTSTATE = 2;
int refillState = 0;
bool freshwait = true;
bool targetselected = false;
bool targetdirection;
bool newstate = true;
bool speedreporting;
bool targetreached = false;
bool needfill[NUM_STOPS] = { false, false, false, false };

///////////////TIME////////////////
unsigned long currentTime = 0;
unsigned long lastaccelTime = 0;
unsigned long lastservoTime = 0;
const unsigned long stepTime = acceltime / (maxspeed - minspeed);
unsigned long stoppedTime;
unsigned long movestartTime;
unsigned long waitTime, refillTime;
const unsigned long extendTime = 3800;


const unsigned long travelTime[NUM_STOPS][NUM_STOPS] = {
  //rows = from | column = to
  //  0   1   2   3
  { 0, 2800, 5500, 8100 },
  { 2800, 0, 2500, 5000 },
  { 5600, 2500, 0, 2500 },
  { 8100, 5100, 2500, 0 }
};
///////////////////////////////////

//////////// SERVO /////////////////
int headTarget = headCenter;
float servosmoothFactor = .96;  //The closer to 1 the smoother the acceleration
float servoSmoothed = headCenter;
float servoprevSmoothed = headCenter;
Servo headServo;
////////////////////////////////////

///////////// TARGETING ////////////
int target = 0;
int lasttarget = 0;
// int lastposition = 0;
////////////////////////////////////

const int sensorpin[NUM_STOPS] = { pos0, pos1, pos2, pos3 };


bool stop = true;
/*********************************************************************************/

void setup() {
  Serial.begin(9600);
  pinMode(estop, INPUT_PULLUP);
  attachInterrupt(digitalPinToInterrupt(estop), emergencyStop, LOW);
  delay(1000);


  headServo.attach(headPin);
  /************ I/O **************/
  pinMode(directionPin, OUTPUT);
  pinMode(speedPin, OUTPUT);
  pinMode(headPin, OUTPUT);
  pinMode(armdir, OUTPUT);
  pinMode(armspeed, OUTPUT);

  pinMode(pos0, INPUT);
  pinMode(pos1, INPUT);
  pinMode(pos2, INPUT);
  pinMode(pos3, INPUT);


  for (int i = 0; i < NUM_STOPS; i++) {
    // pinMode(targetSelectorPin[i], INPUT_PULLUP);
    pinMode(needfillPin[i], INPUT);
    pinMode(refillingPin[i], OUTPUT);
    digitalWrite(refillingPin[i], HIGH);
  }

  /************HOMING*************/
  Serial.println("...HOMING...");

  digitalWrite(directionPin, LOW);
  analogWrite(speedPin, minspeed);
  digitalWrite(armdir, LOW);
  analogWrite(armspeed, 255);

  while (!targetreached) {
    for (int i = 0; i < NUM_STOPS; i++) {
      if (digitalRead(sensorpin[i]) == HIGH) {
        analogWrite(speedPin, 0);
        lasttarget = i;
        target = i;
        targetreached = true;
        break;
      }
    }
  }
  targetreached = false;
  Serial.print("Homed Position: ");
  Serial.println(lasttarget);
  Serial.println("Complete");
  delay(5000);
}

void loop() {

  currentTime = millis();


  // lastpositioncheck();
  printstate();
  checkfilllevel();


  switch (STATE) {

    case 1:  //Targeting
      choosetarget();


      if (targetselected) {
        targetselected = false;

        newstate = true;
        STATE = 2;
        speedreporting = true;
        movestartTime = millis();
      }

      // delay(1000);

      break;

    case 2:  //Moving
      aimhead();
      move();

      break;

    case 3:  //Refilling
      switch (refillState) {

        case 0:  //Extending
          digitalWrite(armdir, HIGH);
          // analogWrite(armspeed, 255);
          if (currentTime - refillTime >= extendTime) {
            Serial.println("Filling");
            refillState = 1;
          }
          break;

        case 1:  //Filling
          // analogWrite(armspeed, 0);
          if (needfill[target]) {
            digitalWrite(refillingPin[target], LOW);
          } else {
            refillState = 2;
            refillTime = currentTime;
            Serial.println("Retracting");
          }
          break;

        case 2:  //Retracting

          digitalWrite(armdir, LOW);
          // analogWrite(armspeed, 255);

          if (currentTime - refillTime >= extendTime) {
            Serial.println("Refill Complete");

            digitalWrite(refillingPin[target], HIGH);

            //do i add a wait?
            // analogWrite(armspeed, 0);
            newstate = true;
            STATE = 1;
            refillState = 0;
          }
          break;
      }
  }
  servosmoothing();
  // Serial.println(lastposition);
}

void emergencyStop() {
  analogWrite(speedPin, 0);
  digitalWrite(armdir, LOW);

  while (1) {
    Serial.print("ERROR - Target was: ");
    Serial.println(target);
  }
}

// void lastpositioncheck() {
//   if (digitalRead(pos0) == LOW) {
//     lastposition = 0;
//   }
//   if (digitalRead(pos1) == HIGH) {
//     lastposition = 1;
//   }
//   if (digitalRead(pos2) == HIGH) {
//     lastposition = 2;
//   }
//   if (digitalRead(pos3) == LOW) {
//     lastposition = 3;
//   }
// }

void choosetarget() {
  lasttarget = target;
  if (targetdirection) {
    for (int i = 0; i < NUM_STOPS; i++) {
      if (needfill[i]) {
        target = i;

        Serial.print("Moving From: ");
        Serial.print(lasttarget);
        Serial.print(" Target: ");
        Serial.println(target);

        targetselected = true;
        targetreached = false;

        break;
      }
    }
    targetdirection = !targetdirection;
  } else {

    for (int i = NUM_STOPS - 1; i >= 0; i--) {
      if (needfill[i]) {
        target = i;

        Serial.print("Moving From: ");
        Serial.print(lasttarget);
        Serial.print(" Target: ");
        Serial.println(target);

        targetselected = true;
        targetreached = false;
        break;
      }
    }
    targetdirection = !targetdirection;
  }
}

void aimhead() {
  if (target < lasttarget) {
    digitalWrite(directionPin, LOW);
    headTarget = headLeft;
  } else if (target > lasttarget) {
    digitalWrite(directionPin, HIGH);
    headTarget = headRight;
  } else {
    headTarget = headCenter;
  }
  stop = false;
}

void move() {
  LASTSTATE = STATE;

  if (currentTime - movestartTime >= travelTime[lasttarget][target]) {  // {
    stop = true;                                                        //start decceleration

    headTarget = headCenter;
  }

  if (stop == true) {
    if (currentspeed == maxspeed) {
      Serial.println("Deccelerating");
    }
    if (currentTime - lastaccelTime >= stepTime) {
      if (currentspeed > minspeed) {
        currentspeed--;
        lastaccelTime = millis();
      }
    }
  }

  // if ((target == 0) || (target == NUM_STOPS - 1)) {
  //   if (digitalRead(sensorpin[target]) == LOW) {
  //     targetreached = true;
  //   }
  // } else {
  if (digitalRead(sensorpin[target]) == HIGH) {
    targetreached = true;
  }
  // }
  if (targetreached) {
    stop = true;
    Serial.println("Arrived");
    currentspeed = 0;
    newstate = true;
    STATE = 3;
    refillTime = currentTime;
  }

  if (stop == false) {
    if (currentspeed <= minspeed) {
      currentspeed = minspeed;
      Serial.println("Accelerating");
    }
    if (currentTime - lastaccelTime >= stepTime) {
      if (currentspeed < maxspeed) {
        currentspeed++;
        lastaccelTime = millis();
      }
    }
    if ((currentspeed == maxspeed) && (speedreporting)) {
      Serial.println("Full Speed Ahead");
      speedreporting = false;
    }
  }
  analogWrite(speedPin, currentspeed);

  // Serial.print("Target: ");
  // Serial.print(target);
  // Serial.print(" Stop: ");
  // Serial.print(stop);
  // Serial.print(" Current Speed: ");
  // Serial.println(currentspeed);
}

void checkfilllevel() {
  for (int i = 0; i < NUM_STOPS; i++) {
    if (digitalRead(needfillPin[i]) == HIGH) {
      needfill[i] = true;
    } else {
      needfill[i] = false;
    }
  }
}

void servosmoothing() {
  if (currentTime - lastservoTime >= servoRefreshRate) {
    lastservoTime = millis();

    servoSmoothed = headTarget * (1 - servosmoothFactor) + servoprevSmoothed * servosmoothFactor;
    servoprevSmoothed = servoSmoothed;
    // Serial.print(Target[i]);
    // Serial.print(" , ");
    // Serial.print(Smoothed[i]);
    // Serial.print(" , ");
    headServo.writeMicroseconds(servoSmoothed);
  }
}
void printstate() {
  if (newstate) {
    switch (STATE) {
      case 0:
        Serial.println("...WAITING... ");
        break;

      case 1:
        Serial.println("...TARGETING...");
        break;

      case 2:
        Serial.println("...MOVING...");
        break;

      case 3:
        Serial.println("...REFILLING...");
        Serial.println("Extending");
        break;
    }
  }
  newstate = false;
}

It's not cool to serial print inside an ISR. For the false triggering problem, we need to do a fine grained, detailed examination of all your layout, wiring, and noise suppression components.

Of course, it would take hours to even begin associating the wiring in the image with the diagram. So you need to direct us to the relevant wires/components.

BTW do the emerg switch wires share a wire bundle with any power or active motor drive wiring?

Yeah, I know an ISR is only supposed to do quick things like change a flag, but I figured I want everything to freeze if the interrupt activates anyway so I assumed it wouldn't matter much to throw an infinite loop in there. Is it not cool outside of being bad practice?

The cat6 cable has the following wires, in pairs:

E stop
Ground

+5V
PWM 1 (motor control)

Direction 1 (motor control)
Direction 2 (motor control)

Servo signal
PWM2 (motor control)

so it has wires associated with the motors but shouldn't be high current wires I think?

The 12V power gets to the cart via that gold colored speaker wire. The 5V wire in the cat6 powers the WLED ESP32 and Servo. All of the grounds on the cart are tied together in the block on the cart and through the power supplies and busses back in the electrical "box" (it will have one eventually). So the e switch is tied to the same ground as the motor driver on board the cart and the other wire runs to the messy prototype wiring in the top right of the box. I'll sketch a diagram of that connection. The rectangles represent the Wago connector things I'm currently using. The ground wire from the secondary switch goes directly to the Nano

I only just learned that a ground loop is a thing and still don't fully understand it, could that be something I'm dealing with?

How far are the switches from the Arduino?

If C ? F and R 5K Ω are close to the input pin, remove both wires to the switches and see if you get false triggers.

You could also reduce the 5k in order to pull the input pin up harder.

What is the uF C?

a7

There is roughly 10 ft of wire from arduino to switch. I'm suspicious of wire length as well. I will unplug the switches and see if it happens.
What resistor value would you recommend, 2.2k?
currently there is a 100 nF, but I have tried 10 uF and 100 uF as well and It's hard to say which works best. When I added the resistor it got better, and it got even better with the cap as well, but it still triggers periodically. I am running it now with no switches plugged in and will report

I said nothing about the infinite loop. There is nothing wrong with that, if it's what you want to do (freeze). It's not considered a bad practice, generally it's seen as a way to freeze. Sometimes freezing is a bad practice but that is a system/device issue, not a programming issue.

1 Like

Does it have it's own ground return, or is that shared with something else?

The 12V supply wire's ground goes straight to the power supply, The ground from the switch is shared with other grounds on the cart including the 12v wire ground. Then the ground in the cat6 (also plugged into that shared ground) runs back to the box where it meets up with the grounds from the fork sensors, lights, and the ground of the 5v converter

Bad. Give it a separate ground wire, back to the Arduino. Put a twist in the pair if there isn't already one.

Illustrating, we need a better diagram. Word salad never represents things well enough.

okay, I can run its ground separately. So I understand the why of what I'm doing, does twisting the pair together just kind of act like shielding for the signal? I also have a hard time understanding the difference if all of the grounds are connected. As I was retracing my wiring for the last reply I realized there were a couple of ways back to ground close to the arduino, is that the main issue? Because even if I run the ground wire back to the arduino separately won't it still just connect with other things there? This is probably a beginner question, but its just a bit confusing to me.

I will make more/better diagrams but I might not get to it until tomorrow

Grounding - rats nest grounding will always confound in any project where significant currents flow. Mixing signal and power return paths is a recipe for a nightmare. BTDT, the scars still itch.
Ideally, star ground your system such that all power return paths are independent of each other, and of signal grounds, but such that all grounds are common. That is, don't forget that control signals to power equipment usually need a common reference with that power equipment. That's a tall order, and you'll never achieve it perfectly, particularly when revising an already committed project, but it's something to strive for. As you modify your grounding, test as often as is reasonable, to verify that you've not yet found the problem, or alternatively, verify you've not made it worse. This could become a long and painful snark hunt.

As for your interrupt being false-triggered, I'd be adding a timing component that looks at the input after 5 or 10 ms to verify that it really is still low. That's far too fast for the user to have taken their finger off the button, but long enough for transients from any but the most egregious noise to have settled out. If your input is still low then, throw out the anchor.

Sort of. Voltages in the wires can be induced by external alternating magnetic fields. If the wire is twisted, the voltages become opposite and so cancel each other.

I also have a hard time understanding the difference if all of the grounds are connected.

It's because no ordinary room temperature substances are perfect conductors of electricity, they all have some resistance. Even a 10cm thick copper bus bar has some resistance. From Ohm's law, when current flows in a conductor, a voltage is developed, a very good conductor will just have a very small voltage drop. So these small voltages can become significant if the ground conductors are arranged in such a way that they can share a circuit path. Star grounding avoids such common paths, so avoid common voltage drops (where you don't want them).

Alright, that makes some sense. I’ll read up more on it. So if I’m understanding correctly, ideally you want every single component to have its own ground wire going back to a common ground near the controller and straight back to the power supply ground? Are there cases where it doesn’t matter and you can connect them however you want?

That got convoluted, then sent prematurely. Will try again later.

1 Like