I am trying to make a simple stepper rig but going around in circles (no pun intended) with limit switches...
It now works like this:
i press "left" button switch and the TMC2209 driver is enabled and stepper turns left, as soon as i release the button the driver should disable and stepper stops turning. same goes with "right" push button
now, there are also 2 limit switches, one for each side. Whatever i do i cant make them to work properly.
Limit switches should work like this:
once the stepper triggers the left limit switch, stepper speed should be set to zero or go to HOLD, at this point pressing the "left" turn button should do nothing as we have the limit switch triggered, we can now only go right by pressing the "right" button.
Current loop code looks like this (i am using ezButton for ease of use)
void loop() {
limitSwitch_1.loop(); // MUST call the loop() function first
limitSwitch_2.loop(); // MUST call the loop() function first
left.loop(); // MUST call the loop() function first
right.loop(); // MUST call the loop() function first
if (left.isPressed() && limitSwitch_1.isReleased() ) {
digitalWrite(EN_PIN, LOW);
driver.VACTUAL(200); // Set motor speed
driver.shaft(dir); // SET DIRECTION
}
if (right.isPressed() && limitSwitch_2.isReleased()) {
digitalWrite(EN_PIN, LOW);
driver.VACTUAL(200); // Set motor speed
driver.shaft(!dir); // SET DIRECTION
}
if (right.isReleased() || left.isReleased()) {
digitalWrite(EN_PIN, HIGH);
}
}
My programming skills are modest, i need a bit of a push with this to continue.
So what happens is, stepper is turning and when it hits the limit switch it just start clicking, like its stopping and starting over and over. If i activate the servo and press and hold the limit switch before stepper gets to it, stepper stops as it should... but if i then release and press again the direction button while still holding the limit switch, stepper continues to the same direction of the pressed limit switch, although it should not be allowed to move in that direction as long as the limit switch is pressed....
Please write some simple sketches and/or review the operation of ezButton buttons.
I looked at it briefly, so I don't recall exactly how it works.
I believe that isPressed and isReleased would better be called gotPressed and wasReleased.
They report events on the buttons or switches.
Your code look like it would rather be consulting the state of the buttons or switches.
The code (and simulation) say to me ezButton is badly bad. Or that I can't figure out how it is meant to work. You will see that pressing a button reports a few "pressed" events.
// https://wokwi.com/projects/363250216927001601
// https://forum.arduino.cc/t/stepper-limit-switch-confusion/1120713
# include <ezButton.h>
ezButton button(7); // create ezButton object that attach to pin 7;
void setup() {
Serial.begin(9600);
}
void loop() {
button.loop(); // MUST call the loop() function first
if(button.isPressed())
Serial.println("The button is pressed");
if(button.isReleased())
Serial.println("The button is released");
static unsigned long last;
if (millis() - last > 777) {
last = millis();
Serial.print("the button is ");
if (button.getState())
Serial.println("UP");
else
Serial.println("DOWN");
}
}
Perhaps the demo will help you figure out the way ezButton needs you to work with it.
My recommendation is to find a better button library, and learn its corners and doors or...
Just gear up and step back and learn how to do your own button handling. It isn't that hard. You should know how to do it as it will involve learning programming techniques that show up all over the place. Not just for buttons.
Once you have walked through the fire of learning about buttons and doing your own job of it, you may wish to return to the ease and convenience of using a library.
I did a lot of arduino stuff few years ago, including reading button states without libraries but i feel a bit rusted.
ezButton seemed like ok solution as a proof of concept but somehow it seems that "isPressed" is not actually keeping the state of the button/switch...
If you run the demo at the link I provided, it is clear.
isPressed does not keep the state. It also doesn't even debounce. So basically it is worthless.
It can't be used for state, and it can't be used for event or edge detection.
I can't name a good button library. I have looked at them all, most seem to work, but all seem to do one thing or another in a manner I do not prefer.
So I just write the code, or steal it from my younger self. Sometimes elaborate meant-to-be-reusable code, other times it just flies off my fingers again in an adhoc manner.
You will also see ezButton's getState method, which is at least debounced and therefore a bit of improvement over just digitalReading the buttons.
In a loop like yours, a delay(25) at the bottom would not interfere very much, and would totally debounce all switches so you could just use digitslRead() and, if necessary, state change detection as seen in the IDE examples 02.Digital I think.
I remind you that there is available the serial monitor. I don't think I've ever made a sketch and gotten it working, working well and doing exactly what I thought I wrote without some kind of feedback or reporting. Usually the serial monitor. Sometimes a logic analyser. Even just using an LED.
You should never wonder why (or if) something is happening.
Now, still have a small problem, my code now tells the stepper the move only while the direction button is pressed, just as i wanted it... but as soon as i ramp up the speed the stepper starts shaking while turning.
i suspect there is a conflict of some kind telling it to go/stop all the time.
if (leftbtn == false && leftLimit == true) {
digitalWrite(EN_PIN, LOW);
driver.VACTUAL(1000); // Set motor speed
driver.shaft(dir); // SET DIRECTION
} else if (leftbtn == true && leftLimit == false) {
driver.VACTUAL(0);
}
if (rightbtn == false && rightLimit == true) {
digitalWrite(EN_PIN, LOW);
driver.VACTUAL(1000); // Set motor speed
driver.shaft(!dir); // SET DIRECTION
} else if (rightbtn == true && rightLimit == false) {
driver.VACTUAL(0);
}
Above code spins the stepper all the way to the limit switch if i just touch the direction button, without any noises or issues, silent and smooth.
but if i want to spin it only while holding the button then i have the code like this:
else if (rightbtn == true || rightLimit == false)
so no loner AND but OR...
any ideas of what the conflict would be or how to fix it?
Consider using the COM and NC (Normally Closed) terminals on your limit switch and having an external pullup resistor on this circuit. That way, if anything in the limit switch circuit fails (disconnect, etc), the motor will be disabled.
If you use the NO (Normally Open) terminal as you would a regular pushbutton switch, then if anything fails, the motor would be enabled.
I've sorted it out, my sketch was over defined, made it a bit simpler and everything works great now.
Many thanks.
Current/simplified code:
if ((leftbtn == false) && (leftLimit == true)) {
digitalWrite(EN_PIN, LOW);
driver.VACTUAL(1000); // Set motor speed
driver.shaft(dir); // SET DIRECTION
}
else if ((rightbtn == false) && (rightLimit == true)) {
digitalWrite(EN_PIN, LOW);
driver.VACTUAL(1000); // Set motor speed
driver.shaft(!dir); // SET DIRECTION
} else {
driver.VACTUAL(0);
}
ok, need a little more guidance. i would like to add millis() to disable the driver after a certain number of seconds ONLY if the motor is not active/turning. I think i have managed to define when the motor is running and when its not, so millis will disable the driver accordingly.
The problem:
It now adds the time each time i stop the motor (each time the motorstatus is "false"), meaning: i run the motor for some time then stop for 1 second, then run some time and then stop for 1 second... millis loop will keep adding the time while stopped and then will disable the driver when i stop it 5 times 1sec each (const long interval = 5000;)
What i need:
Reset the counter each time i start the motor after some inactivity time, ie. i start the motor and run it for some time, then i stop the motor and the "disable driver" countdown starts... i start the motor again at 4th second, just before the time is up... millis counter resets and now when i stop the motor i again get new 5 seconds before disabling the driver...
hope i have explained it clearly.
my current code:
#include <SpeedyStepper.h> //Simple & good stepper library, get it.
#include <TMCStepper.h>
#define DIR_PIN 14 // Direction
#define STEP_PIN 12 // Step
#define SERIAL_PORT Serial2 // HardwareSerial port pins 16 & 17
#define DRIVER_ADDRESS 0b00 // TMC2209 Driver address according to MS1 and MS2
#define R_SENSE 0.11f // Match to your driver
// SilentStepStick series use 0.11
#define x_pin 34 // Pin 34 connected to joystick x axis pin
#define EN_PIN 32
#define left 25
#define right 26
#define limitSwitch_1 27
#define limitSwitch_2 33
#define MAX_SPEED 40 // In timer value
#define MIN_SPEED 1000
bool shaft = false; // ONLY NEEDED FOR CHANGING DIRECTION VIA UART, NO NEED FOR DIR PIN FOR THIS
bool dir = false;
int analog_read;
bool leftbtn;
bool rightbtn;
bool leftLimit;
bool rightLimit;
int buttonStateLeft = 0;
int buttonStateRight = 0;
int buttonStateLeftLimit = 0;
int buttonStateRightLimit = 0;
int motorState = 0; // Stepper motor state - TRUE = Running, FALSE = holding position
int potPin = 35;
int potValue = 0;
unsigned long previousMillis = 0; // will store last time stepper driver is disabled
// constants won't change:
const long interval = 5000; // interval at which to disable the driver
TMC2209Stepper driver(&SERIAL_PORT, R_SENSE, DRIVER_ADDRESS);
SpeedyStepper stepper;
//----------------------------------------------------------------------------------------------------
// SETUP
//----------------------------------------------------------------------------------------------------
void setup() {
stepper.connectToPins(STEP_PIN, DIR_PIN); // INITIALIZE SpeedyStepper
SERIAL_PORT.begin(115200); // INITIALIZE UART TMC2209
Serial.begin(115200);
delay(500);
Serial.println(F("Serial Initialized"));
pinMode(x_pin, INPUT);
pinMode(EN_PIN, OUTPUT);
digitalWrite(EN_PIN, HIGH); // Disable TMC2209 board on boot
pinMode(DIR_PIN, OUTPUT);
pinMode(STEP_PIN, OUTPUT);
pinMode(left, INPUT_PULLUP);
pinMode(right, INPUT_PULLUP);
pinMode(limitSwitch_1, INPUT_PULLUP);
pinMode(limitSwitch_2, INPUT_PULLUP);
pinMode(potPin, INPUT);
driver.begin(); // Initialize driver
driver.rms_current(4800); // Set motor RMS current
driver.microsteps(2); // Set microsteps to 1/2
driver.pwm_autoscale(true); // Needed for stealthChop
driver.en_spreadCycle(false); // false = StealthChop / true = SpreadCycle
stepper.setCurrentPositionInSteps(0); // Set zero position
stepper.setSpeedInStepsPerSecond(6400); //Set Speed
stepper.setAccelerationInStepsPerSecondPerSecond(100); //Set acceleration, smaller value for super smooth direction changing
}
//----------------------------------------------------------------------------------------------------
// LOOP
//----------------------------------------------------------------------------------------------------
void loop() {
potValue = analogRead(potPin);
buttonStateLeft = digitalRead(left);
if (buttonStateLeft == HIGH) {
leftbtn = true;
} else {
leftbtn = false;
}
buttonStateRight = digitalRead(right);
if (buttonStateRight == HIGH) {
rightbtn = true;
} else {
rightbtn = false;
}
buttonStateLeftLimit = digitalRead(limitSwitch_1);
if (buttonStateLeftLimit == HIGH) {
leftLimit = true;
} else {
leftLimit = false;
}
buttonStateRightLimit = digitalRead(limitSwitch_2);
if (buttonStateRightLimit == HIGH) {
rightLimit = true;
} else {
rightLimit = false;
}
if ((leftbtn == false) && (leftLimit == true)) {
digitalWrite(EN_PIN, LOW);
driver.VACTUAL(potValue / 2); // Set motor speed
driver.shaft(dir); // SET DIRECTION
motorState = true;
}
else if ((rightbtn == false) && (rightLimit == true)) {
digitalWrite(EN_PIN, LOW);
driver.VACTUAL(potValue / 2); // Set motor speed
driver.shaft(!dir); // SET DIRECTION
motorState = true;
} else {
driver.VACTUAL(0);
motorState = false;
}
unsigned long currentMillis = millis();
if (currentMillis - previousMillis >= interval) {
// save the last time
previousMillis = currentMillis;
//
if (motorState == false) {
digitalWrite(EN_PIN, HIGH);
}
}
}