Capacitive touch toggle switching ON/OFF during DC motor activity

Dear Arduino community!

Thank you for all the information and help you guys put out here for people to learn from. This is my first post but I've been reading a lot of helpful ideas on here.

I have to start saying that I'm a beginner hobbyist at this stuff, both SW/HW side, but admittedly I'm a slightly better coder. Everything I do, I've learned from reading online, so in case you see something crazy in my schematics or code, please know I'm doing my best here.

So, on to the project. I have a vertical drawer that opens and closes with 2 linear actuators and one NEMA17 stepper. One of the actuators is dumb, with limit switches, the other one has no limit switches but potentiometer position feedback. I use an Arduino UNO to control this project. A capacitive toggle switch is used to trigger everything.

The problem; the cap switch toggles ON/OFF by its own. At prototype stage (when I used a breadboard and Dupont cables) I managed to get this problem solved simply by adding a 10k pulldown on the pin used for the switch. Now however, I have moved the Arduino and the drivers to the box and the problem is back - with one difference. Before adding pulldown the switch could toggle at any time and toggle back and forth several times within a minute.

Now however, the random switching only happens when I have started the process of opening or closing the drawer - that is when one or more motors are running. So I'm guessing there is a lot of noise going on in the circuitry.

What am I missing here? There is nothing in the direct vicinity of the switch and it never triggers on its own if no motors are running. I even tried adding a 0,1µF close to the switch with no result.

Other info: Arduino is powered through USB with a 5V source that can handle max 1,5A. The drivers and motors are driven by 12V transformer @ max 3A. The current setting on the TMC2208 is done according to my NEMA17 specs.

Actuonix P16 Datasheet

TMC2208 Datasheet

MD13S Datasheet

AT42QT1012 Datasheet

#include <elapsedMillis.h>
elapsedMillis timeElapsed;

// Hall Sensors - all arduino pins default to inputs so these don't have to be declared with pinMode
#define sensor_bot A0
#define sensor_mid A1
#define sensor_top A2
#define sensor_back A3

// MD13S
#define P16_dir 7
#define P16_pwm 6  // pwm pin - to MD13S for speed
#define P16_feedback A4 // pwm pin - from P16 - to check position feedback
#define DS300_dir 4
#define DS300_pwm 5

// Beeper
#define beeper 3

// Cap Touch Button
 #define touchButtonPin 2

// TMC2208
#define NEMA17_dir 9
#define NEMA17_step 10
#define NEMA17_MS1 11
#define NEMA17_MS2 12
#define NEMA17_EN 13

// Variables
volatile int touch = 0; // indicator for touch - use to trigger start/pause
volatile int pause = 0; // for when button is pressed and no end point is active, break, then reset it
volatile int sensorX = 0; // variable to store X axle sensor value
volatile int sensorY = 0; // variable to store Y axle sensor value
volatile int comingFromPause = 0; // to indicate the shelf is stalled
volatile int pos = 1; // 1 for bot, 2 for top, set false just before calling open/close from end position? Not sure if needed to set false
volatile int firstStart = 1;
volatile int totalSteps = 0; // track steps for backtracking stepper after pause
volatile int P16_pos = 0;
volatile int P16_ignore = 0;

void setup() {
// Touch button pin mode
  //pinMode(touchButtonPin, INPUT_PULLUP); // just making sure interrupt pin has a pullup and not floating // used real pulldown with resistor instead
  attachInterrupt(digitalPinToInterrupt(touchButtonPin),touchButton,CHANGE); // interupt declaration, whenever there is a change on pin 2

// Beeper pin mode
  pinMode(beeper, OUTPUT);

// TMC2208 pin modes and settings
  pinMode(NEMA17_dir, OUTPUT);
  pinMode(NEMA17_step, OUTPUT);
  pinMode(NEMA17_MS1, OUTPUT);
  pinMode(NEMA17_MS2, OUTPUT);
  pinMode(NEMA17_EN, OUTPUT);
  digitalWrite(NEMA17_MS1, HIGH);
  digitalWrite(NEMA17_MS2, HIGH);
  digitalWrite(NEMA17_EN, HIGH);

// P16 pin modes and settings
  pinMode(P16_dir, OUTPUT);
  pinMode(P16_pwm, OUTPUT);
  pinMode(P16_feedback, INPUT);
  digitalWrite(P16_dir, LOW);
  digitalWrite(P16_pwm, LOW);
}

void loop() {
  if ( touch == 1 ){
    // touch = 0; // reset to listen for new touch event as soon as possible - unnecessary at start - done at end
    delay(1000);
    if ( pos == 1 and comingFromPause == 0 ) {
      fStart_Open();
    } else if ( pos == 2 and comingFromPause == 0 ) { // use only else if in this function, pause check excluded
      fStart_Close();
      P16_pos = 0; // reset for opening sequence - needs to be 0 if pause is going to work in fDS300
    } else if ( comingFromPause == 1 and pause == 0 ) {
      if ( pos == 1 ) { // if started at bottom, return to bottom
        fSafe_Close();
      } else { // else, started at top, return to top
        fSafe_Open();
      }
      comingFromPause = 0;
    }
    // last thing to do in void loop under 'if touch'
    pause = 0;
    touch = 0;
  }
}


///////////////////////////////////////////// TOUCH AND STATUS FUNCTIONS /////////////////////////////////////////////
void touchButton() { // try to not have anything else going on here since interrupt can have some unexpected result on things
      makeNormalBeep ();
      checkStatus();
      touch = 1;
}

void checkStatus() {
  //check for comingFromPause, endPoints etc - when comingFromPause is true this will practically be bypassed
  sensorY = analogRead(sensor_bot);
  sensorX = analogRead(sensor_back);
  if ( sensorY <= 20 and sensorX <= 100 ) {
    pos = 1; // pos can be checked during safe move to make sure reverse move is done in correct direction
    firstStart = 0; 
    return; // if any of these statements are true, we need to leave checkStatus
  }
  sensorY = analogRead(sensor_top);
  P16_pos = analogRead(P16_feedback);
  if ( sensorY <= 20 and P16_pos >= 800 ) {
    pos = 2;
    if ( firstStart == 1 ) {
      totalSteps = 350;
    }
    firstStart = 0;
    return;
  }
  if ( firstStart == 1 ) { // in case of power outage
    comingFromPause = 1; // reset at the end of safe move
    pause = 0;
    totalSteps = 350;
    firstStart = 0;
    sensorY = analogRead(sensor_top); // if at top but P16 half closed - set pos to 2
    if ( sensorY <= 20 ) {
      pos = 2;
      comingFromPause = 0; // practically, looking for a normal close in this case
    }
  } else if ( comingFromPause == 0 ) {
    comingFromPause = 1; // reset at the end of safe move
    pause = 1;
  }
}

///////////////////////////////////////////// BEEPER FUNCTIONS /////////////////////////////////////////////
void makeNormalBeep() {
  tone(beeper, 3000, 200);
  // delays don't work inside an interrupt, this gave me quite a headache until solved! XD
}

void makePauseBeep() {
  delay(1500);
  tone(beeper, 3000, 150);
  delay(200);
  tone(beeper, 3000, 150);
}

void makeCautionBeep() {
  tone(beeper, 3000, 500);
  delay(1000);
  tone(beeper, 3000, 500);
  delay(1000);
  tone(beeper, 3000, 500);
}

void makeErrorBeep() {
  tone(beeper, 3000, 500);
  delay(1000);
  tone(beeper, 3000, 500);
  delay(1000);
  tone(beeper, 3000, 500);
  delay(1000);
  tone(beeper, 3000, 500);
  delay(1000);
  tone(beeper, 3000, 500);
}

///////////////////////////////////////////// NORMAL OPENING FUNCTIONS /////////////////////////////////////////////
void fStart_Open(){ // for opening from starting point
  sensorX = analogRead(sensor_back);
  if ( sensorX <= 100 ) { // checking for shelf top position before start - making sure it is close to the wall
    fStepper_Open(); // void functions are called like this
    sensorX = analogRead(sensor_back); // recheck to make sure moved away from wall
    if ( pause == 0 and sensorX > 150 ) { // pause check is needed - in case pause has interrupted previous function
      fDS300_Open();
    } else if ( pause == 1 ) {
      //double beep for pause?
      return;
    } else if ( pause == 0 ) { // in case of failure to move top out
      makeErrorBeep();
      digitalWrite(NEMA17_EN, HIGH); // unlock NEMA17 so the top can be pulled out manually
      comingFromPause = 0;
    }
  }
}

void fStepper_Open(){
  // start by setting correct direction and enable the driver
  digitalWrite(NEMA17_EN, LOW);
  digitalWrite(NEMA17_dir, HIGH);
  totalSteps = 0; // reset step counter before opening
  // 4 for loops for movement, 3 last loops for decelaration
  for (int i = 1; i <= 275 ; i++) {
    if ( pause == 1 ) { // if button pressed during movement - pause becomes TRUE and function returns
      makePauseBeep(); // double beep for pause
      P16_ignore = 1;
      return;
    }
    else if ( pause == 0 ) {
      // these four lines result in 1 step:
      digitalWrite(NEMA17_step, HIGH);
      delayMicroseconds(1000); // speed, lower means faster
      digitalWrite(NEMA17_step, LOW);
      delayMicroseconds(1000);
      totalSteps = ++totalSteps;
    }
  }
  for (int i = 1; i <= 25 ; i++) { // deceleration
    if ( pause == 1 ) {
      makePauseBeep(); // double beep for pause
      P16_ignore = 1;
      return;
    }
    else if ( pause == 0 ) {
      // these four lines result in 1 step:
      digitalWrite(NEMA17_step, HIGH);
      delayMicroseconds(1500);
      digitalWrite(NEMA17_step, LOW);
      delayMicroseconds(1500);
      totalSteps = ++totalSteps;
    }
  }
  for (int i = 1; i <= 25 ; i++) { // deceleration
    if ( pause == 1 ) {
      makePauseBeep(); // double beep for pause
      P16_ignore = 1;
      return;
    }
    else if ( pause == 0 ) {
      // these four lines result in 1 step:
      digitalWrite(NEMA17_step, HIGH);
      delayMicroseconds(2000);
      digitalWrite(NEMA17_step, LOW);
      delayMicroseconds(2000);
      totalSteps = ++totalSteps;
    }
  }
  for (int i = 1; i <= 25 ; i++) { // deceleration
    if ( pause == 1 ) {
      makePauseBeep(); // double beep for pause  
      P16_ignore = 1;
      return;
    }
    else if ( pause == 0 ) {    
      // these four lines result in 1 step:
      digitalWrite(NEMA17_step, HIGH);
      delayMicroseconds(4000);
      digitalWrite(NEMA17_step, LOW);
      delayMicroseconds(4000);
      totalSteps = ++totalSteps;
    }
  }
  delay(1000); // small delay before moving on - putting this here will make sure pause beep is triggered if pause activated here
}

void fDS300_Open() {
  while ( pause == 0 ) {
    //extend DS300
    digitalWrite(DS300_dir, HIGH);
    digitalWrite(DS300_pwm, HIGH);

    //check mid sensor to start opening lid
    sensorY = analogRead(sensor_mid);
    if ( sensorY <= 20 ) {
      fP16_Open();
    }

    //check top sensor to exit function
    sensorY = analogRead(sensor_top);
    if ( sensorY <= 20 ) {
      delay(1000); //gives time for DS300 to fully open
      digitalWrite(DS300_dir, LOW);
      digitalWrite(DS300_pwm, LOW);      
      return;
    }
  }
  P16_pos = analogRead(P16_feedback);
  if ( pause == 1 and P16_pos <= 80 ) {
    //stop DS300 and double beep if paused before P16 started
    digitalWrite(DS300_dir, LOW);
    digitalWrite(DS300_pwm, LOW);
    P16_ignore = 1;
    //double beep for pause
    makePauseBeep();
  }
}

void fP16_Open() {
  int acc_counter = 5;
  analogRead(A0); // dummy read to discharge ADC;
  
  P16_pos = analogRead(P16_feedback);
  while ( P16_pos < 30 and pause == 0 ) { // Slow open until postion 30, breaks if pause set true
    digitalWrite(P16_dir, HIGH);
    analogWrite(P16_pwm, 100); // Speed set to 40 - don't go lower - P16 will stall
    P16_pos = analogRead(P16_feedback);
  }
  
  int prevReading = 0;
  do {
    while ( acc_counter <= 255 and pause == 0 ) { // Accelerating to top speed, but will break as intended if paused
      digitalWrite(P16_dir, HIGH);
      analogWrite(P16_pwm, 100+acc_counter);
      delay(10);
      acc_counter=acc_counter+5;
    }
    prevReading = P16_pos;
    timeElapsed = 0;
    while( timeElapsed < 300 ){ delay(1);}  // Keep moving until analog reading remains the same for 300ms
      P16_pos = analogRead(P16_feedback);
  } while ( prevReading != P16_pos and pause == 0 );
        
    digitalWrite(P16_dir, LOW); // turn off P16
    digitalWrite(P16_pwm, LOW); 
  if ( pause == 1 ) {
    digitalWrite(DS300_dir, LOW);
    digitalWrite(DS300_pwm, LOW); 
    // Double beep for pause
    makePauseBeep();
    return;
  }
}

///////////////////////////////////////////// NORMAL CLOSING FUNCTIONS /////////////////////////////////////////////
void fStart_Close(){ // for closing from top point
  fP16_Close();
  if ( pause == 1 ) {
    comingFromPause = 1;
    return;
  }
  fDS300_Close();
  if ( pause == 1 ) {
    comingFromPause = 1;
    return;
  }
  fStepper_Close();
  if ( pause == 1 ) {
    comingFromPause = 1;
    return;
  }
}


void fP16_Close () {
  P16_pos = analogRead(P16_feedback);
  digitalWrite(P16_dir, LOW);
  analogWrite(P16_pwm, 200); // speed set to 200
  int prevReading = 0;
  do {
    prevReading = P16_pos;
    timeElapsed = 0;
    while( timeElapsed < 200 ){ delay(1);}  // keep moving until analog reading remains the same for 200ms
      P16_pos = analogRead(P16_feedback);
  } while ( prevReading != P16_pos and pause == 0 );
  digitalWrite(P16_dir, LOW); // turn off P16
  digitalWrite(P16_pwm, LOW);
  if ( pause == 1) {
    makePauseBeep();
    return;
  } else if ( P16_pos > 100 and pause == 0 ) { // if lid not closed properly, and not because of pause, make error beep and pause
    makeErrorBeep();
    pause = 1;
    comingFromPause = 1;
  }
}

void fDS300_Close () {
  while ( pause == 0 ) {
    //detract DS300
    digitalWrite(DS300_dir, LOW);
    digitalWrite(DS300_pwm, HIGH);

    //check bottom sensor to exit function
    sensorY = analogRead(sensor_bot);
    if ( sensorY <= 20 ) {
      delay(1000); //gives time for DS300 to fully close
      digitalWrite(DS300_dir, LOW);
      digitalWrite(DS300_pwm, LOW);      
      return;
    }
  }
  if ( pause == 1 ) {
    //stop DS300 and double beep
    digitalWrite(DS300_dir, LOW);
    digitalWrite(DS300_pwm, LOW);
    //double beep for pause
    makePauseBeep();
  }
}

void fStepper_Close () {
  digitalWrite(NEMA17_EN, LOW);
  digitalWrite(NEMA17_dir, LOW);
  for (int i = 0; i <= 350 ; i++) {
    if ( pause == 1 ) { // if button pressed during movement - pause becomes TRUE and function returns
      makePauseBeep(); // double beep for pause
      return;
    }
      // these four lines result in 1 step:
      digitalWrite(NEMA17_step, HIGH);
      delayMicroseconds(2000); // speed, lower means faster
      digitalWrite(NEMA17_step, LOW);
      delayMicroseconds(2000);
      totalSteps = --totalSteps;
  }
}

///////////////////////////////////////////// SAFE MODE OPENING FUNCTION /////////////////////////////////////////////
void fSafe_Open() { //safe mode cannot be interrupted - done by choice
  makeCautionBeep();
  delay(2000);

  //safe open stepper
  digitalWrite(NEMA17_EN, LOW);
  digitalWrite(NEMA17_dir, HIGH);
  totalSteps = 350 - totalSteps; // how many steps to open? if fully open this will result in 0 and skip stepper
  for (int i = 0; i <= totalSteps ; i++) {
      // these four lines result in 1 step:
      digitalWrite(NEMA17_step, HIGH);
      delayMicroseconds(2000); // speed, lower means faster
      digitalWrite(NEMA17_step, LOW);
      delayMicroseconds(2000);
  }

  //safe open DS300 - delay before continue
  sensorY = analogRead(sensor_top);
  while ( sensorY >= 20 ) {  
    digitalWrite(DS300_dir, HIGH);
    digitalWrite(DS300_pwm, HIGH);
    sensorY = analogRead(sensor_top);
  }
  delay(1000); //gives time for DS300 to open fully
  digitalWrite(DS300_dir, LOW);
  digitalWrite(DS300_pwm, LOW);

  //safe open lid and stop P16 - delay before continue
  analogRead(A0); // dummy read to discharge ADC;
  P16_pos = analogRead(P16_feedback);  
  while ( P16_pos < 30 ) { // Slow open until postion 30, breaks if pause set true
    digitalWrite(P16_dir, HIGH);
    analogWrite(P16_pwm, 40); // Speed set to 40 - don't go lower - P16 will stall
    P16_pos = analogRead(P16_feedback);
  }
  int prevReading = 0;
  do {
    digitalWrite(P16_dir, HIGH);
    analogWrite(P16_pwm, 200);
    delay(10);
    prevReading = P16_pos;
    timeElapsed = 0;
    while( timeElapsed < 300 ){ delay(1);}  // Keep moving until analog reading remains the same for 300ms
      P16_pos = analogRead(P16_feedback);
  } while ( prevReading != P16_pos );
  digitalWrite(P16_dir, LOW); // turn off P16
  digitalWrite(P16_pwm, LOW); 

  comingFromPause = 0;
  totalSteps = 350;
}

///////////////////////////////////////////// SAFE MODE CLOSING FUNCTION /////////////////////////////////////////////
void fSafe_Close() { //safe mode cannot be interrupted - done by choice
  makeCautionBeep();
  delay(2000);
  
  //safe close lid and stop P16 - delay before continue
  if ( P16_ignore == 0 ) {
    P16_pos = analogRead(P16_feedback);
    digitalWrite(P16_dir, LOW);
    analogWrite(P16_pwm, 100); // Speed set to 100
    int prevReading = 0;
    do {
      prevReading = P16_pos;
      timeElapsed = 0;
      while( timeElapsed < 200 ){ delay(1);}  // Keep moving until analog reading remains the same for 200ms
        P16_pos = analogRead(P16_feedback);
    } while ( prevReading != P16_pos );
    digitalWrite(P16_dir, LOW); // turn off P16
    digitalWrite(P16_pwm, LOW);
    delay(1000);
  }

  //safe close DS300 - delay before continue
  sensorY = analogRead(sensor_bot);
  while ( sensorY >= 20 ) {  
    digitalWrite(DS300_dir, LOW);
    digitalWrite(DS300_pwm, HIGH);
    sensorY = analogRead(sensor_bot);
  }
  delay(1000); //gives time for DS300 to close properly
  digitalWrite(DS300_dir, LOW);
  digitalWrite(DS300_pwm, LOW);

  //safe close stepper
  digitalWrite(NEMA17_EN, LOW);
  digitalWrite(NEMA17_dir, LOW);
  for (int i = 0; i <= totalSteps ; i++) {
      // these four lines result in 1 step:
      digitalWrite(NEMA17_step, HIGH);
      delayMicroseconds(2000); // speed, lower means faster
      digitalWrite(NEMA17_step, LOW);
      delayMicroseconds(2000);
  }
  comingFromPause = 0;
  P16_ignore = 0;
}

Happy for any help! Let me know if I've missed any more info you would need.

/Z

Are you sure it's the touch module that causes the problems.
Do you see the LED on the touch module changing state.
Did you try disconnecting D2.

If it's the touch module, then try powering it from the (cleaner) 3.3volt pin of the Uno R3.

The KY-003 has AFAIK digital outputs.
If so, then analogReading the sensors, and using thresholds doesn't make sense.
Leo..

Just a hint, many of us do not bother with fritzing, it leaves to many details out and sometimes is in a weird format. Your "Schematic" is how to wire it drawing not an electronics schematic (language of electronics). You can get a real well supported CAD (Computer Aided Design) program for free. It is called KiCad and it will take you from initial schematic capture through the needed GERBER files to make a PWB (Printed Wire Board).

1 Like

Thanks for taking time!

You are correct, the problem is not the cap button itself. The LED does not change. I have a "debug" version of my code with lots of prints to console so I can follow the procedure. What happens is that the interrupt triggers on D2, randomly and inappropriately, causing misbehavior. There is def some internal noise causing this. I can't disconnect D2 mid-procedure because that alone triggers a change in state. Connecting to 3,3V was one the things I tried before posting here. Nothing changed, which of course also points to the problem being internal noise and not the cap button (by my logic anyway).

You are absolutely right about the KY-003. However, I'm getting pretty precise and reliable results with the analogue readings so I just kept it that way. I could (and perhaps should) change the code rather easily to digital reads if you think that's the culprit here. I don't think it will have any negative effects on the expected procedure anyway.

Again, I appreciate your time!

Hey, thanks a lot for the tip! I just used Fritzing in the absence of any other software, according to my knowledge. I know some CAD so I'll definitely try KiCad, thanks! :))

1 Like

Nothing wrong with using analogRead() to read a digital signal, then using some constants and an if statement to derive the HIGH (true) or LOW (false) that just using digitslWrite() directly would have given you.

It's just always something ppl will notice and wonder about, and then maybe ask you questions.

I wondered, for example, if the KY-003 has an additional analog output you were exploiting. It does not. A similar module does have analog output as well as digital. Perhaps some code you found was using that type.

Little things can slow down the reading and finding of the true problem(s). The more unconventional your style, or habits, the less readable your code.

A trifling matter, to be sure. I don't argue with success, and I don't like fixing things that aren't broken. Here, however, I might one day switch to using digitalRead() for digital inputs, just to get ppl like me off my back. :wink:

a7

1 Like

I have seen that several times on this site.
Golden rule is to never use interrupts for things operated by slooow humans.

Interrupts are reacting very fast. The bit of wire between sensor and Arduino is likely the problem, acting like an aerial, picking up noise from the environment. I bet that a 10n cap between D2 and Arduino ground, close to the Arduino, will solve your problem. But it's far better to change the interrupt to common polling.

AnalogRead is much slower than digitalRead. Do try a version of the sketch with digitalRead.
Leo..

This will keep your skills up to date. Thanks for letting us know.

Came back to give you an update! Thank you all for the replies.

So I tried a 0,1µF cap on D2 and ground. The frequency of the problem decreased, however the system remained unreliable and kept breaking the process spontaneously. Having tried everything I could think of, and almost out of desperation, I noted the NEMA17 being a bit buzzy on idle. So I checked the current setting on the TMC2208, and while well within spec, I decided to lower it just enough for the stepper to handle the job.. And what do you know, all problems gone. I haven't had one single false trigger since. Not sure if it's the cheap NEMA17 from Amazon that's the problem, or the TMC2208 (from BigTreeTech) but with the current setting closer to 25-30% of max spec of the stepper did the job.

Thanks again guys.

/Z

2 Likes

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.