This is a simple project. I am using a stepper motor attached to a slider mechanism, with two switches (to detect end of scale), a potentiometer to control movement and the steppter motor driver A4988.
At the beginning I had no micro switches. Instead I used 2 push buttons on the bread board. Pressing them would cause an event called "Hit Left Limit" and "Hit Right Limit" to calibrate the slider.
Now after I glued microswitches to the slider and ran 3 wires to it (red oval), I can see the 2 Limit events being triggered randomly, without the microswitches being activated. I tried to use pull up 10kohm resistors (white oval) but did not improve.
I am suspecting the 4 motor wires (green oval) are causing some sort of electricity being induced in the sensor wires. How can I avoid this?
Limit switches should be normally closed, and open when activated.
The switch should be between pin and ground, with pull up enabled in pinMode.
Switch/ground wire should be twisted pair, to reduce motor interference pickup.
Did you use potentially problematic interrupts (pin 2,3), or just polling.
Leo..
Try 1k pullup, not 10k, so its much more robust to interference, and yes, definitely use
twisted pair (and possibly shield) to protect those switch signals.
Loose separate wires act as a loop antenna picking up all manner of EMI. Its easy to
make up twisted pair using a cordless screwdriver/drill on slow speed, or with a hand
drill.
If you use a shielded cable the shield should only be connected to ground at one end
and not be part of the sensor circuit.
Wawa:
Limit switches should be normally closed, and open when activated.
The switch should be between pin and ground, with pull up enabled in pinMode.
Switch/ground wire should be twisted pair, to reduce motor interference pickup.
Did you use potentially problematic interrupts (pin 2,3), or just polling.
Leo..
Why normally closed? Does that have to do with interference? I have them normally open (but I could reconfigure), with pinMode configured as pull up. And then I assigned interrupt handlers on event FALLING, with FALLING occurring when switch closes the circuit to the ground. Is this wrong?
And I used int for switch but polling for the potentiometer.
I only have 1 common ground for both switches. Is that bad too?
MarkT:
Try 1k pullup, not 10k, so its much more robust to interference, and yes, definitely use
twisted pair (and possibly shield) to protect those switch signals.
Loose separate wires act as a loop antenna picking up all manner of EMI. Its easy to
make up twisted pair using a cordless screwdriver/drill on slow speed, or with a hand
drill.
If you use a shielded cable the shield should only be connected to ground at one end
and not be part of the sensor circuit.
I switched to 1k and now it works if I hold the wires separated by hand. Next step is to twist. Since it is 3 of them (1 gnd and 2 switches) can they be all together or must be 2 separate pairs?
Breadboards are for low power logic circuitry and the contacts tend to burn if subjected to motor currents. I strongly advise soldering motor and power connections, or use secure screw terminals if available.
Incidentally, if a motor connection becomes loose or is manually disconnected while the system is powered up, the driver will be instantly destroyed. Burned breadboard tracks lead to this sort of disaster.
On the first attempt at twisting wires with the Dremel 3000, the tool started too fast, pulled all the wires from the board and started whipping me like crazy.
I took notice and I started the process again slower and this time I made this wonderful twisted pair and a half (3 wires) that works flawlessly!
Thanks
(it is still with 1k pull up and switches normally open. Is that 1k gonna be ok long run or could cause some difficulties due to current draw?)
An opening contact is more reliable than a (old/dirty) closing contact.
And a signal wire of a twisted pair that is normally grounded is less likely to pick up interference.
Interrupts is for fast re-occurring things, like rotary encoders.
Not needed for a slow end stop, and usually the last thing you should think off.
Polling each loop (between each step of the motor) is just fine, and likely less problematic.
Leo..
bavareze:
On the first attempt at twisting wires with the Dremel 3000, the tool started too fast, pulled all the wires from the board and started whipping me like crazy.
Good to see that you're ok. Just got to be careful when it comes to 'power' tools. Eg. gloves, eye-protection etc. It's crazy unforeseen things like that which can make what should have been a good normal day turn into a bad day - unnecessarily.
Hi,
If you keep getting interference on your limit switch wires, it may be worth trying to wire the limit switches as change over switches, the NC and NO terminals would then be connected directly to gnd and 5V respsectively.
Also a 0.1uF capacitor between the signal wire and gnd is worth a try.
Keep your motor wires well away form the other wiring.
How are you powering the motor?
How are you powering the UNO?
A schematic of your project will help, a circuit, in CAD or a picture of a hand drawn circuit in jpg, png?
Hand drawn can be the easiest to do, please include your power supplies and your motor.
Wawa:
An opening contact is more reliable than a (old/dirty) closing contact.
And a signal wire of a twisted pair that is normally grounded is less likely to pick up interference.
Interrupts is for fast re-occurring things, like rotary encoders.
Not needed for a slow end stop, and usually the last thing you should think off.
Polling each loop (between each step of the motor) is just fine, and likely less problematic.
Leo..
I figured checking a boolean value is less time consuming that a digitalRead() call. I could switch to polling inbetween motor steps though later on.
//#define _VERBOSE_P
#include "stepper.h"
#include "pot.h"
const int calibrateReq =5;
const int MODE_STANDBY=0;
const int MODE_CALIBRATE=1;
const int MODE_RUN=2;
int mode=MODE_STANDBY;
void setup() {
Serial.begin(9600);
Serial.println("Starting Asnyc");
pinMode(stepPin,OUTPUT);
pinMode(dirPin,OUTPUT);
pinMode(LlimitPin,INPUT_PULLUP);
pinMode(RlimitPin,INPUT_PULLUP);
pinMode(calibrateReq,INPUT_PULLUP);
attachInterrupt(digitalPinToInterrupt(LlimitPin), hitLeftLimit, FALLING);
attachInterrupt(digitalPinToInterrupt(RlimitPin), hitRightLimit, FALLING);
}
void loop() {
if(digitalRead(calibrateReq)==LOW)
{
mode=MODE_CALIBRATE;
Serial.println("calibrate req");
}
switch (mode) {
case MODE_STANDBY: // your hand is on the sensor
//Serial.println("dark");
break;
case MODE_CALIBRATE: // your hand is close to the sensor
Serial.println("calibrating");
calibrate();
delay(500);
mode=MODE_RUN;
break;
case MODE_RUN: // your hand is a few inches from the sensor
#ifdef _VERBOSE_1
Serial.println("mode run");
#endif
if(readPot())
{
long desiredStep = (sensorValue*totalSteps)/1024;
#ifdef _VERBOSE_1
String a="total Steps = "; a+=totalSteps; a+=" sensorValue="; a+=sensorValue; a+=" going to step "; a+=desiredStep; a+=" now at step "; a+=currentStep; Serial.println(a); //stepp();
#endif
goToStep(desiredStep);
}
stepp(); // let the stepper routine do it's cycle
break;
}
delayMicroseconds(50);//was 50
}
/*void clearedLeftLimit() {
leftLimit=LOW;
}
void clearedRightLimit() {
rightLimit=LOW;
}*/
Stepper.h
const int LlimitPin = 2;
const int RlimitPin = 3;
const int stepPin = 6; // define pin for step
const int dirPin = 7; // define pin for direction
const int minDelay=120; //120 for single scre
const int maxDelay=700; //700 for single scre
short acceleration=4; //3 for single screw
volatile bool hitLeft=false;
volatile bool hitRight=false;
bool waitingMotorSync=false;
bool moving=false;
bool currentDirection=LOW;
long waitingSince;
int stepsToWait=0;
int stepsDone=0;
int currentStep;
int stepsToDo;
int totalSteps=0;
int stepDelay=maxDelay;
void stopp()
{
if(moving)
{
stepsToDo=0;
/*String a="I did so many steps ";
a+=stepsDone;
Serial.println(a);
stepsDone=0;*/
waitingMotorSync=false;
moving=false;
stepDelay=maxDelay;
}
}
void setDirection(bool newDirection)
{
//stepDelay=maxDelay; // should not be needed
if(newDirection!=currentDirection) //direction change
{
currentDirection=newDirection;
stepDelay=maxDelay;
if(moving)
{
stopp();
// stepDelay*=1.4;// was 1.2 for single screw; take it easier if sudden change of direction
stepsToWait=40;
Serial.println("sudden change of direction");
}
}
digitalWrite(dirPin,currentDirection);
}
void hitLeftLimit() { //interrupt handler
#ifdef _VERBOSE_5
Serial.println("hit L");
#endif
if(moving)
currentStep=0;
stopp();
hitLeft=true;
}
void hitRightLimit() { //interrupt handler
#ifdef _VERBOSE_5
Serial.println("hit R");
#endif
if(moving)
currentStep=totalSteps;
stopp();
hitRight=true;
}
void stepp()
{
/* if(stepsDone>7000)
return;*/
if(!moving)
return;
if(stepsToDo<=0)
{
stopp();
return;
}
if(waitingMotorSync) //stepPin is in LOW, we need to give it at least stepDelay time to execute and sync
{
if(micros()>waitingSince+stepDelay*1.5)
waitingMotorSync=false;
}
else
{
if(stepsToWait>0)
{
stepsToWait--;
// Serial.println("waiting");
waitingMotorSync=true;
waitingSince=micros();
}
else
{
digitalWrite(stepPin,HIGH);
delayMicroseconds(stepDelay*0.1);
digitalWrite(stepPin,LOW);
waitingMotorSync=true;
waitingSince=micros();
stepsToDo--;
stepsDone++;
if(currentDirection)
currentStep++;
else
currentStep--;
if(stepDelay>minDelay)
stepDelay-=acceleration;
}
}
}
void stepLsync()
{
//Serial.println("stepL called");
digitalWrite(dirPin, LOW);
digitalWrite(stepPin,HIGH);
delayMicroseconds(stepDelay*0.1); //was 500
digitalWrite(stepPin,LOW);
delayMicroseconds(stepDelay*1.6); //was 500
/* if(digitalRead(RlimitPin)==HIGH)
rightLimit=LOW;*/
}
void stepRsync()
{
digitalWrite(dirPin, HIGH);
digitalWrite(stepPin,HIGH);
delayMicroseconds(stepDelay*0.1); //was 500
digitalWrite(stepPin,LOW);
delayMicroseconds(stepDelay*1.6); //was 500
/*if(digitalRead(LlimitPin)==HIGH)
leftLimit=LOW;*/
}
void calibrate() //TODO: calibrate has a bug and can not start if the high limit is already hit
{
hitLeft=false;
hitRight=false;
totalSteps=0;
#ifdef _VERBOSE_5
Serial.println("CAL ");
#endif
while(!hitRight)
stepRsync();
#ifdef _VERBOSE_5
Serial.println("CAL 2 ");
#endif
delay(500);
while(!hitLeft)
{
stepLsync();
totalSteps++;
}
#ifdef _VERBOSE_5
String a="total steps= ";
a+=totalSteps;
Serial.println(a);
#endif
/* for(int i=0; i<totalSteps/2; i++)
stepL();*/
currentStep=0;
}
void goLeft(int numSteps)
{
#ifdef _VERBOSE_1
String a="Go left ";
a+=numSteps;
Serial.println(a);
#endif
setDirection(false);
stepsToDo=numSteps;
moving=true;
}
void goRight(int numSteps)
{
#ifdef _VERBOSE_1
String a="Go right ";
a+=numSteps;
Serial.println(a);
#endif
setDirection(true);
stepsToDo=numSteps;
moving=true;
}
void goToStep(int destination)
{
if(abs(destination-currentStep)<100)//ignore less than half turn requests
{
#ifdef _VERBOSE_1
String a="Waiting at step "; a+=currentStep; Serial.println(a);
#endif
}
else
{
if(destination<currentStep)
{
#ifdef _VERBOSE_3
Serial.println("go left");
#endif
setDirection(false);
}
else
{
#ifdef _VERBOSE_3
Serial.println("go Right");
#endif
setDirection(true);
}
stepsToDo=abs(currentStep-destination);
moving=true;
#ifdef _VERBOSE_1
String a="I am at step "; a+=currentStep; a+=" need to go to "; a+=destination; Serial.println(a);
#endif
}
}
Another problem with NO limit switches is if the wiring becomes open circuit then the limit switches will never be detected and the motor can run to the end and try to run further. If the switch is NC then if the wiring becomes open circuit the system will detect a limit switch open and stop the motor.
Paul_KD7HB:
IF the switch wires have to go in the same bundle, twist them in the opposite sense.
Paul
Whoops, no that's actually the slightly worse option (think about the geometry), all helices the same sense
is fine, but more important is that the number of twists per unit length is different for the two cables,
as this will provide much better cancellation of induced signals.
You'll see in CAT5/6 cables that all the pairs are twisted the same way, but with different number
of twists per metre (its in the specs of the cable in fact). This reduces crosstalk to a minimum.
But best of all use shielded twisted pair for every signal cable. Shielding the motor wires too isn't
a bad idea either.
And running the cables spaced apart is very sensible if possible.