Ok @anon57585045 , I hope the attached photo meets any necessary requirements with posting a schematic of where I'm currently at. Sorry again.
That doesn't show how the power, ground and signal ground is connected to the driver or Uno.
Also do you have some code matching your schematic?
const int upLimitSwitchPin = A0;
const int downLimitSwitchPin = A4;
const int goUpButtonPin = A1;
const int goDownButtonPin = A2;
const int stopButtonPin = A3;
const int stepperDirPin = ??;
const int stepperStepPin = ??;
...
void setup(void){
pinMode(upLimitPin,INPUT_PULLUP);
...
}
...
With those pin sorts of definitions, you should be able to use any of those components individually with the matching Arduino examples and prove that your circuit & wiring works.
Yeah, the upper right and left pins of that driver board are +v and gnd; however, I didn't show them because that wasn't my actual driver.
I've been trying to take code snippets and add them to my code to make it progressively work. So far, I've only gotten to get the motor to turn by pushing a button. I've got some additional components coming today that should allow me to expand the code and testing.
Pins 6 and 7 coming off the arduino are the dir and step pins. Thank you for bearing with me Dave. I'm a bit over my head, but getting a better understanding as we go. Thanks again.
Post your code (even if you don't actually own all the components to match your simulated schematic) You can define and setup the pins even if there is nothing connected to them or not yet code written to actually use them.
I'm at work, but remote logged in to my home computer. I thought this was the code I was using, but plugging it into the simulation doesn't work. I may have to wait until I am home where I can verify everythign. But here is a version I had saved that I was tweaking on.
#include <AccelStepper.h>
#include <Bounce2.h>
//AccelStepper myStepper; // Defaults to AccelStepper::FULL4WIRE (4 pins) on 2, 3, 4, 5
AccelStepper stepper(1, 7, 6);
#define NUM_BUTTONS 1
const int buttonPin[NUM_BUTTONS] = {A0};
Bounce2::Button button[NUM_BUTTONS] = {Bounce2::Button()};
void setup()
{
Serial.begin(9600);
for (byte i = 0; i < NUM_BUTTONS; i++)
{
button[i].attach(buttonPin[i], INPUT_PULLUP);
button[i].setPressedState(LOW);
button[i].interval(5);
}
}
stepper.setMaxSpeed(1000);
stepper.setAcceleration(50);
stepper.setSpeed(100);
{
void loop()
{
int debouncedState[NUM_BUTTONS];
for (byte i = 0; i < NUM_BUTTONS; i++)
{
button[i].update();
debouncedState[i] = button[i].read();
if ((debouncedState[0] == LOW) && (button[0].currentDuration() > 200))
{
myStepper.move(-10000);
myStepper.run();
delay(5);
}
else if ((debouncedState[1] == LOW) && (button[1].currentDuration() > 200))
{
stepper.move(10000);
stepper.run();
delay(5);
}
}
}
I'm out due to lack of alignment with forum guidelines. Good luck.
Man, give a guy a break. I actually went through and re-read that whole post again for fear of upsetting you based on your previous reactions. I'm totally new here and just getting my feet wet. Just telling me to "go read" opposed to telling me what I'm doing wrong isn't productive. I've stated many times that I'm happy to adjust as is necessary. It stated in the post not to just copy and paste code, and to put it in a code block. That's what I did. Everyone else here has been extremely helpful, thank you guys for that. If I've formatted my post incorrectly, if someone could please advise as to what I needed to do differently, I will happily oblige.
Okay, adding, welcome to my permanent ignore list.
@DaveX I was wrong. The code posted above wasn't the code I had working. I'm almost afraid to post because argh went nuts on me, but until someone corrects me...I will continue on this path. I found some code on this site that was for two limit switches. Upon starting the code, the motor would spin and clicking either limit switch would cause the motor to reverse. This wasn't what I wanted, but it did actually work. I also had a version where a button would start the motor, but I couldn't combine them to make those work together. My plan was to continue to tweak (and seek help) to try to get it working as I wanted. Below is the code with the two limit switches:
#include <ezButton.h>
#include <AccelStepper.h>
#define DIRECTION_CCW -1
#define DIRECTION_CW 1
#define STATE_CHANGE_DIR 1
#define STATE_MOVE 2
#define STATE_MOVING 3
#define MAX_POSITION 0x7FFFFFFF // maximum of position we can set (long type)
ezButton limitSwitch_1(A0); // create ezButton object that attach to pin A0;
ezButton limitSwitch_2(A1); // create ezButton object that attach to pin A1;
AccelStepper stepper(AccelStepper::FULL4WIRE, 7, 6, 5, 4);
int stepperState = STATE_MOVE;
int direction = DIRECTION_CW;
long targetPos = 0;
void setup() {
Serial.begin(9600);
limitSwitch_1.setDebounceTime(50); // set debounce time to 50 milliseconds
limitSwitch_2.setDebounceTime(50); // set debounce time to 50 milliseconds
stepper.setMaxSpeed(500.0); // set the maximum speed
stepper.setAcceleration(50.0); // set acceleration
stepper.setSpeed(100); // set initial speed
stepper.setCurrentPosition(0); // set position
}
void loop() {
limitSwitch_1.loop(); // MUST call the loop() function first
limitSwitch_2.loop(); // MUST call the loop() function first
if (limitSwitch_1.isPressed()) {
stepperState = STATE_CHANGE_DIR;
Serial.println(F("The limit switch 1: TOUCHED"));
}
if (limitSwitch_2.isPressed()) {
stepperState = STATE_CHANGE_DIR;
Serial.println(F("The limit switch 2: TOUCHED"));
}
switch (stepperState) {
case STATE_CHANGE_DIR:
direction *= -1; // change direction
Serial.print(F("The direction -> "));
if (direction == DIRECTION_CW)
Serial.println(F("CLOCKWISE"));
else
Serial.println(F("ANTI-CLOCKWISE"));
stepperState = STATE_MOVE; // after changing direction, go to the next state to move the motor
break;
case STATE_MOVE:
targetPos = direction * MAX_POSITION;
stepper.setCurrentPosition(0); // set position
stepper.moveTo(targetPos);
stepperState = STATE_MOVING; // after moving, go to the next state to keep the motor moving infinity
break;
case STATE_MOVING: // without this state, the move will stop after reaching maximum position
if (stepper.distanceToGo() == 0) { // if motor moved to the maximum position
stepper.setCurrentPosition(0); // reset position to 0
stepper.moveTo(targetPos); // move the motor to maximum position again
}
break;
}
stepper.run(); // MUST be called in loop() function
}
Looks reasonable. It uses two combined state variables--stepperState and direction. I’d map out its states and the transitions and see how to adapt them to your needs. This looks like:
I think it would probably be cleaner for your project to have one state variable with distinct up and down moving states.
My suggestion was more like:
Thank you @DaveX . I stepped away for a bit to do a bit more research. I realized I was in over my head. I took a 15 hour class on arduino programming. I have a bit better understanding of the basics. I now have a working code (with some help from another forum). The code was created without any additional libraries, and I was considering trying to move the code over to accelstepper as I think it would make adjusting the acceleration/ramps a tad easier. It might not be necessary but wanted to get your opinion. A copy of the code is below.
// Define pins for stepper motor control
#define DIR_PIN 8
#define STEP_PIN 9
// Define pins for buttons and limit switches
#define UP_BUTTON 2
#define DOWN_BUTTON 3
#define STOP_BUTTON 4
#define LIMIT_SWITCH_1 5
#define LIMIT_SWITCH_2 6
enum MotorStates
{
STOPPED=0,
MOVING
};
#define BTN_PRESSED LOW
#define LIMSW_PRESSED LOW
const uint32_t kFadeTime = 3000000ul; //3-second ramp time
const uint32_t kInitSpeed = 1000ul; //2x == uS/step
const uint32_t kFinalSpeed = 250ul; //2x == uS/step
const uint32_t kAccelDelay = (uint32_t)(kFadeTime / (kInitSpeed - kFinalSpeed) );
uint32_t
ulStepDelay,
tNow,
tAccel = 0ul;
bool
bAccel;
void setup()
{
// Set pins for motor control as output
pinMode(DIR_PIN, OUTPUT);
pinMode(STEP_PIN, OUTPUT);
// Set pins for buttons and limit switches as input
pinMode(UP_BUTTON, INPUT_PULLUP);
pinMode(DOWN_BUTTON, INPUT_PULLUP);
pinMode(STOP_BUTTON, INPUT_PULLUP);
pinMode(LIMIT_SWITCH_1, INPUT_PULLUP);
pinMode(LIMIT_SWITCH_2, INPUT_PULLUP);
// Set initial state of DIR_PIN to LOW
digitalWrite(DIR_PIN, LOW);
bAccel = false;
}//setup
void loop()
{
static uint8_t
motorState = STOPPED,
pinLimit;
if( digitalRead( STOP_BUTTON ) == BTN_PRESSED )
{
motorState = STOPPED;
}
switch( motorState )
{
case STOPPED:
if( digitalRead( UP_BUTTON ) == BTN_PRESSED )
{
digitalWrite(DIR_PIN, LOW);
pinLimit = LIMIT_SWITCH_1;
ulStepDelay = kInitSpeed;
tAccel = micros();
bAccel = true;
motorState = MOVING;
}//if
else if( digitalRead( DOWN_BUTTON ) == BTN_PRESSED )
{
digitalWrite(DIR_PIN, HIGH);
pinLimit = LIMIT_SWITCH_2;
ulStepDelay = kInitSpeed;
tAccel = micros();
bAccel = true;
motorState = MOVING;
}//if
break;
case MOVING:
tNow = micros();
if( bAccel )
{
if( (tNow - tAccel) >= kAccelDelay )
{
tAccel = tNow;
ulStepDelay--;
if( ulStepDelay == kFinalSpeed )
bAccel = false;
}//if
}//if
if( digitalRead( pinLimit ) != LIMSW_PRESSED )
{
digitalWrite(STEP_PIN, HIGH);
delayMicroseconds(ulStepDelay);
digitalWrite(STEP_PIN, LOW);
delayMicroseconds(ulStepDelay);
}//if
else
{
motorState = STOPPED;
}//if
break;
}//switch
}//loop
Nice. I just have time to wonder if it makes sense to have a few more states.
Now days later (abandoned draft!)… I pick it up again:
@crankycowboy it took me awhile, looking through the tiny window, to see how this works
pinLimit = LIMIT_SWITCH_1;
You are clever, you change the pin that will get read.
But in the abstract, this is an enhancement to the state MOVING, essentially giving you a MOVING_UP state and a MOVING_DOEN state.
MOVING_UP = moving and pinLimit is switch A
MOVING_DONW = moving and pinLimit is switch B
which is fine, and does allow for reusing some code lines.
It comes, in my opinion, at a cost that outweighs the savings of not duplicating any code.
Explicit states for the two directions makes reading the code easier.
That's why some time ago I was going to ask @DaveX about not only MOVING_UP and MOVING_DOWN, but actually adding another STOP state, so there would be a state for stopped when I was moving up and one for stopped when I was moving down.
I can't think of good short names for the two states where the thing is stopped, one where it was going up and, well, you get it.
When I am in the lab I can take a closer look, it may be that my idea is bad or would make things more comlicate; wouldn't be the first time.
a7
I don't have your hardware, and that code uses a stepper, so it isn't immediately compatible with your Wokwi setup of #13
If you start off with
// Define pins for stepper motor control
#define DIR_PIN 6
#define STEP_PIN 7
// Define pins for buttons and limit switches
#define UP_BUTTON A1
#define DOWN_BUTTON A3
#define STOP_BUTTON A2
#define LIMIT_SWITCH_1 A0
#define LIMIT_SWITCH_2 A4
and flip all the switches to OPEN, it compiles and works (switches L-R are limitDown, down,stop, up, limit up) Good job!
Quibbles:
@FernandoGarcia's switches are all default NC+LOW with internal pullups, which is good for limit switches bc they are fail-safe -- if anything breaks the circuit, the limit switch triggers.
I avoid commas in the declarations because things like this are a bit ambiguous. (for instance bool i=true,j=false;
-- what is i?) It does compile and work.
Logic-wise, I think it can a bit easier to keep track of what should be happening with minimal state variables, rather than a larger set of interacting variables. As @alto777 found, you are tracking motorState and pinLimit as the the state variables that combine to give you my {movingUp, movingDown, Stopped} states. The bAccel and uStepDelay are other state variables you are using for controlling acceleration, so all together {motorState, pinLimit,bAccel, uStepDelay} is the state vector you are using to remember what needs to be done from loop() to loop(). Moving to accelstepper could manage the functionality of bAccel and uStepDelay.
Visually it looks pretty fast in the sim. I wasn't sure how fast it was getting, and looking at your variables, I'd probably not use XxxxSpeed for the period between steps and let the compiler calculate a kInitUs and kFinalUs from speeds. Rather than 50% duty cycle, your driver probably needs only a step pulse of a few us, it is likely the time of digitalWrite() is enough.
Code like this would be non-blocking:
if( digitalRead( pinLimit ) != LIMSW_PRESSED )
{
static uint32_t last = 0;
if(micros() - last >= ulStepDelay){
digitalWrite(STEP_PIN, HIGH);
last = micros();
digitalWrite(STEP_PIN, LOW);
}
}//if
const uint32_t kInitUs = 1000000UL*60/200/150; // 150RPM
const uint32_t kFinalUs = 1000000UL*60/200/600; // 600RPM
const uint32_t kAccelDelay = (uint32_t)(kFadeTime / (kInitUs - kFinalUs) );
All together, this works in your post #13 simulator:
// Define pins for stepper motor control
#define DIR_PIN 6
#define STEP_PIN 7
// Define pins for buttons and limit switches
#define UP_BUTTON A1
#define DOWN_BUTTON A3
#define STOP_BUTTON A2
#define LIMIT_SWITCH_1 A0
#define LIMIT_SWITCH_2 A4
enum MotorStates
{
STOPPED=0,
MOVING
};
#define BTN_PRESSED LOW
#define LIMSW_PRESSED LOW
const uint32_t kFadeTime = 3000000ul; //3-second ramp time
const uint32_t kInitUs = 1000000UL*60/200/10; //10 RPM
const uint32_t kFinalUs = 1000000UL*60/200/600; //600RPM
const uint32_t kAccelDelay = (uint32_t)(kFadeTime / (kInitUs - kFinalUs) );
uint32_t
ulStepDelay,
tNow,
tAccel = 0ul;
bool
bAccel;
void setup()
{
// Set pins for motor control as output
pinMode(DIR_PIN, OUTPUT);
pinMode(STEP_PIN, OUTPUT);
// Set pins for buttons and limit switches as input
pinMode(UP_BUTTON, INPUT_PULLUP);
pinMode(DOWN_BUTTON, INPUT_PULLUP);
pinMode(STOP_BUTTON, INPUT_PULLUP);
pinMode(LIMIT_SWITCH_1, INPUT_PULLUP);
pinMode(LIMIT_SWITCH_2, INPUT_PULLUP);
// Set initial state of DIR_PIN to LOW
digitalWrite(DIR_PIN, LOW);
bAccel = false;
}//setup
void loop()
{
static uint8_t
motorState = STOPPED,
pinLimit;
if( digitalRead( STOP_BUTTON ) == BTN_PRESSED )
{
motorState = STOPPED;
}
switch( motorState )
{
case STOPPED:
if( digitalRead( UP_BUTTON ) == BTN_PRESSED )
{
digitalWrite(DIR_PIN, LOW);
pinLimit = LIMIT_SWITCH_1;
ulStepDelay = kInitUs;
tAccel = micros();
bAccel = true;
motorState = MOVING;
}//if
else if( digitalRead( DOWN_BUTTON ) == BTN_PRESSED )
{
digitalWrite(DIR_PIN, HIGH);
pinLimit = LIMIT_SWITCH_2;
ulStepDelay = kInitUs;
tAccel = micros();
bAccel = true;
motorState = MOVING;
}//if
break;
case MOVING:
tNow = micros();
if( bAccel )
{
if( (tNow - tAccel) >= kAccelDelay )
{
tAccel = tNow;
ulStepDelay--;
if( ulStepDelay == kFinalUs )
bAccel = false;
}//if
}//if
if( digitalRead( pinLimit ) != LIMSW_PRESSED )
{
static uint32_t last = 0;
if(micros() - last >= ulStepDelay){
digitalWrite(STEP_PIN, HIGH);
last = micros();
//delayMicroseconds(10);
digitalWrite(STEP_PIN, LOW);
}
}//if
else
{
motorState = STOPPED;
}//if
break;
}//switch
}//loop
Another advantage of the AccelStepper is that it could take care of a softer stop, which could be important, if not skipping steps and losing position was important.
Thanks again. I moved quite a bit around from my last post of hardware layout. I wasn’t sure if it was that, that got me in trouble about post “formatting” previously. I will update the layout for clarification shortly.
You can add other states as needed. If you want different behavior when stopped up or stopped down, you might think of them as STOWED, or DEPLOYED vs STOPPED_IN_BETWEEN.
Another useful sort of state is a one-shot that does something and immediately transitions to the next. For example you might put the code that sets the variables for going up in START_GOING_UP with something like ths fragment:
case STOPPED:
if( digitalRead( UP_BUTTON ) == BTN_PRESSED ){
motorState = START_GOING_UP;
}
if( digitalRead( DN_BUTTON ) == BTN_PRESSED ){
motorState = START_GOING_DN;
}
break;
case START_GOING_UP:
digitalWrite(DIR_PIN, LOW);
pinLimit = LIMIT_SWITCH_1;
ulStepDelay = kInitSpeed;
tAccel = micros();
bAccel = true;
motorState = MOVING;
break;
...
edit I hadn't saved my simulated setup (I updated it from work) so it showed an old project I was working. I have updated it now to show correctly exactly as my setup is configured.
@DaveX I know it's generally frowned upon to post links, but it seems like the best way to thoroughly explain my setup. In this link, it's exactly my code and how I have it setup. The only difference is that there wasn't a "limit switch" component so I used slide switches instead, but it functions the same. I hope this clarifies everything a little better. Also, I wanted to thank you for continuing to stick with me through this. Your patience and effort is greatly appreciated.
THX.
Just now it doesn't compile! I'd fix a minor error, but this
if (button_Up.isPressed()) {
looks like you meant to be using a button library of some kind. button_Up is just a number, knows nothing about buttons. You need a button object as seen in the examples from the library you are using, which is not possible for me to guess.
Also, if you dare, you can edit the *.json hardware description file and add an attribute to the stepper:
{
"type": "wokwi-stepper-motor",
"id": "stepper1",
"top": -323.64,
"left": -295.8,
"attrs": { "size": "17", "arrow":"white" }
},
which puts a nice arrow on there. Mind the pesky syntax, watch the commas.
HTH and looking forward to playing with it.
a7
The discussion is fruitful, but limits and stepper morris only work well together if the program knows where the motor ‘is’.
Consider at some point there needs to be a ‘awareness’ of the ‘current’ position - so you know how far to step with any future command.
This could be using the limit switches to update the current position when they are passed, or an extra sensor somewhere along the span of travel that just keeps everything in sync.
I removed the limit switches and added a display rack that also directly feeds the inputs that were the limit switches.
A nice thing about non-blocking code is that additional tasks can be added to run in parallel with the main functions. All the new code is below @crankycowboy's code; the only small additions in the original are a call to set up the display/limit code, and a call in the loop() function to rackService() to give it the processor briefly to maintain the display and limit switch proxy outputs.
I had also changed the sense of the limit switches to use normally closed switches.
This passes for fun in my empty life. <- Try it out.
// https://wokwi.com/projects/357333073299191809
// https://forum.arduino.cc/t/stepper-motors-limit-switches-and-buttons/1089118
// Define pins for stepper motor control
#define DIR_PIN 8
#define STEP_PIN 9
// Define pins for buttons and limit switches
#define UP_BUTTON 2
#define DOWN_BUTTON 3
#define STOP_BUTTON 4
#define LIMIT_SWITCH_1 5
#define LIMIT_SWITCH_2 6
enum MotorStates
{
STOPPED=0,
MOVING
};
#define BTN_PRESSED LOW
#define LIMSW_PRESSED HIGH
const uint32_t kFadeTime = 3000000ul; //3-second ramp time
const uint32_t kInitSpeed = 1000ul; //2x == uS/step
const uint32_t kFinalSpeed = 250ul; //2x == uS/step
const uint32_t kAccelDelay = (uint32_t)(kFadeTime / (kInitSpeed - kFinalSpeed) );
uint32_t
ulStepDelay,
tNow,
tAccel = 0ul;
bool
bAccel;
void setup()
{
// Set pins for motor control as output
pinMode(DIR_PIN, OUTPUT);
pinMode(STEP_PIN, OUTPUT);
// Set pins for buttons and limit switches as input
pinMode(UP_BUTTON, INPUT_PULLUP);
pinMode(DOWN_BUTTON, INPUT_PULLUP);
pinMode(STOP_BUTTON, INPUT_PULLUP);
pinMode(LIMIT_SWITCH_1, INPUT_PULLUP);
pinMode(LIMIT_SWITCH_2, INPUT_PULLUP);
// Set initial state of DIR_PIN to LOW
digitalWrite(DIR_PIN, LOW);
bAccel = false;
setupDisplay();
}//setup
void loop()
{
static uint8_t
motorState = STOPPED,
pinLimit;
rackService(motorState, pinLimit);
if( digitalRead( STOP_BUTTON ) == BTN_PRESSED )
{
motorState = STOPPED;
}
switch( motorState )
{
case STOPPED:
if( digitalRead( UP_BUTTON ) == BTN_PRESSED )
{
digitalWrite(DIR_PIN, LOW);
pinLimit = LIMIT_SWITCH_1;
ulStepDelay = kInitSpeed;
tAccel = micros();
bAccel = true;
motorState = MOVING;
}//if
else if( digitalRead( DOWN_BUTTON ) == BTN_PRESSED )
{
digitalWrite(DIR_PIN, HIGH);
pinLimit = LIMIT_SWITCH_2;
ulStepDelay = kInitSpeed;
tAccel = micros();
bAccel = true;
motorState = MOVING;
}//if
break;
case MOVING:
tNow = micros();
if( bAccel )
{
if( (tNow - tAccel) >= kAccelDelay )
{
tAccel = tNow;
ulStepDelay--;
if( ulStepDelay == kFinalSpeed )
bAccel = false;
}//if
}//if
if( digitalRead( pinLimit ) != LIMSW_PRESSED )
{
digitalWrite(STEP_PIN, HIGH);
delayMicroseconds(ulStepDelay);
digitalWrite(STEP_PIN, LOW);
delayMicroseconds(ulStepDelay);
}//if
else
{
motorState = STOPPED;
}//if
break;
}//switch
}//loop
//
# include <Adafruit_NeoPixel.h>
# define LED_PIN A2
# define LED_COUNT 25
# define upperFakeLimit 10
# define lowerFakeLimit 11
# define SPEED 277 // ms per step
Adafruit_NeoPixel rack(LED_COUNT, LED_PIN, NEO_GRB + NEO_KHZ800);
void setupDisplay()
{
pinMode(lowerFakeLimit, OUTPUT);
pinMode(upperFakeLimit, OUTPUT);
digitalWrite(lowerFakeLimit, LOW);
digitalWrite(upperFakeLimit, LOW);
rack.begin();
rack.setPixelColor(LED_COUNT / 2, 0x00ff00);
rack.show();
}
void rackService(uint8_t state, uint8_t direction)
{
static byte position = LED_COUNT / 2;
static unsigned long lastDisplay;
unsigned long now = millis();
if (state != MOVING) return;
if (now - lastDisplay < SPEED) return;
lastDisplay = now;
rack.setPixelColor(position, 0x101010);
if (direction == LIMIT_SWITCH_1)
position++;
else
position--;
if (position >= LED_COUNT) {
position = LED_COUNT - 1;
}
if (position < 0) position = 0;
digitalWrite(lowerFakeLimit, position <= 0 ? HIGH : LOW);
digitalWrite(upperFakeLimit, position >= (LED_COUNT - 1) ? HIGH : LOW);
rack.setPixelColor(position, 0x0000ff);
rack.show();
lastDisplay = now;
}
Time to make the donuts.
a7
Hey @alto777 I replied below, but went to edit it and accidentally deleted my own post
In any case, your ideas of "fun" seem a lot like mine. This code stuff (at least c++, I dabble in some other languages) is all new to me...but I always have to have some sort of crazy project going. I'm glad my project inspired you to play along! In any case, I'm close to the finish line here and everyone has been great at helping. I especially appreciate the input from @DaveX