Main > Project Announcements
Stepper motor mechanism for animated cab topper
<< < (5/8) > >>
PL1:
My pet unicorn just observed that it's unrealistic for me to expect lightning-fast bulletproof results when doing a complicated first build requiring a newly-learned skillset with a somewhat steep learning curve and few clearly relevant examples to follow.  [/snark]   :lol

Crawl.  Walk.  Run.

This is the initial test frame. (crawl)

The mechanism is too complicated to go from rough idea to working prototype in one step without engineering and CAD skills that far exceed mine.

This is where I learn what works and if certain design assumptions are valid or not.

- Is one 608 bearing enough to keep the platform moving straight or does it require two more bearings on a T-shaped platform to keep it from twisting on the z-axis or does it require an optical guide rod and two linear bearings on a T-shaped platform?

- Does the lifting arm bearing need to be in a captive track or will it work with an open edge?

- How steep of an up-ramp is possible?

- Are there any unforseen complications?  * Shakes Magic 8-ball.  "Very likely." *

It will be fairly easy to transplant the electronics and drive train to a prototype when the time comes to walk.   ;D


Scott
PL1:
Big progress on the Arduino code.   ;D

Baritonomarchetto suggested using "INPUT_PULLUP" instead of "INPUT".   :cheers:

Added extensive comments to the code so even Arduino noobs can understand and modify it.

Arduino inputs and outputs confirmed working as expected using some spare pushbuttons and LEDs (with current limiting resistors  ;) ) on pins 2 (step) and 3. (direction)
- You need to verify that your driver and motor combination will initially drive the mechanism toward the correct switch BEFORE connecting the motor to the mechanism.  Failure to do this can damage the mechanism.
- Had to add a one second troubleshooting delay to slow down the program loop long enough to see the "step" LED on pin 2 blink.  The delay is commented out in the code below. "// delay(1000);"

Added code for optional cooling pauses during direction changes.  The related nine lines are currently commented out.

Remove the "//" at the beginning of a line of code to "uncomment" it.
- Second line of code commented out.

--- Code: ---const int dirPin = 3; // Arduino pin that connects to A4988 driver board DIR pin.
// const int enPin = 5; // Arduino pin that connects to A4988 driver board ENA(not) pin. (ENA with line over it)

--- End code ---
- Second line uncommented.

--- Code: ---const int dirPin = 3; // Arduino pin that connects to A4988 driver board DIR pin.
const int enPin = 5; // Arduino pin that connects to A4988 driver board ENA(not) pin. (ENA with line over it)

--- End code ---

This code should be about 98% done.

Need to dial in the step pulse duration with a complete Arduino/driver/motor setup.


--- Code: ---// Stepper motor mechanism controlled by an Arduino Pro Micro, an A4988 driver board, and two directional switches.

// Stepper motor goes one direction until it hits that directional switch then reverses until it hits the other switch then goes the original direction again.

// Adapted from http://www.arduinotutorialonline.com/2017/12/control-stepper-forward-and-reverse_17.html

// **********************
// *** IMPORTANT NOTE ***
// **********************
// Verify that your driver and motor combination will initially drive the mechanism toward the correct switch BEFORE connecting the motor to the mechanism.
// Failure to do this can damage the mechanism.


// Define Arduino digital pin numbers.
const int stepPin = 2; // Arduino pin that connects to A4988 driver board STEP pin.
const int dirPin = 3; // Arduino pin that connects to A4988 driver board DIR pin.
// const int enPin = 5; // Arduino pin that connects to A4988 driver board ENA(not) pin. (ENA with line over it)  Not needed unless the pause code for cooling during direction switch is uncommented below.
const int DirectionalSwitch_LEFT_Pin  = 8; // Arduino pin that connects to the left directional switch.
const int DirectionalSwitch_RIGHT_Pin = 9; // Arduino pin that connects to the right directional switch.
// End of defining Arduino digital pin numbers.


// Initial settings.
void setup() {

  // Open serial port, set data rate to 9600 bps.
  Serial.begin(9600);

  // Set pins as inputs.
  pinMode(DirectionalSwitch_LEFT_Pin,INPUT_PULLUP);
  pinMode(DirectionalSwitch_RIGHT_Pin,INPUT_PULLUP);

  // Set pins as outputs.
  pinMode(stepPin,OUTPUT);
  pinMode(dirPin,OUTPUT);

  // Set pin as output for A4988 driver board ENA(not) control and enable current to the motor.
  // These two lines are not used unless the pause code for cooling during direction switch is uncommented below.
  // pinMode(enPin,OUTPUT);
  // digitalWrite(enPin,LOW); // LOW enables current to the motor.  HIGH disables current to the motor.

  // Set starting direction of rotation.
  digitalWrite(dirPin,HIGH); // HIGH ==> physical left switch first.  LOW ==> physical right switch first.  See IMPORTANT NOTE at top.

}
// End of the initial settings.


// Define the program loop.
void loop() {

    // Read the switch states.
    int leftSw  = digitalRead( DirectionalSwitch_LEFT_Pin);
    int rightSw = digitalRead( DirectionalSwitch_RIGHT_Pin);
   
    // If neither switch is pressed, send motor step pulse.
    if( (leftSw  == HIGH && (digitalRead(dirPin) == HIGH)) ||
        (rightSw == HIGH && (digitalRead(dirPin) == LOW)) ){

        // delay(1000); // Delay to slow the loop enough to see an LED on stepPin blink on and off every second.  Troubleshooting use only.
        motorStep(1); // Calls motorStep function. (defined below)

    }

    // If left switch is pressed and direction pin is HIGH, change direction pin to LOW.
    else if( leftSw == LOW && (digitalRead(dirPin) == HIGH) ){
          digitalWrite(dirPin,LOW);
          // digitalWrite(enPin,HIGH); // Disable motor for motor or driver cooling.
          // delay(5000); // Miliseconds to pause for cooling.
          // digitalWrite(enPin,LOW); // Enable motor after cooling.
          delay(500); // Miliseconds to pause after switching direction.
          motorStep(100); // Take steps in the new direction to un-press the switch.
    }
   
    // If right switch is pressed and direction pin is LOW, change direction pin to HIGH.
    else if( rightSw == LOW && (digitalRead(dirPin) == LOW ) ){
          digitalWrite(dirPin,HIGH);
          // digitalWrite(enPin,HIGH); // Disable motor for motor or driver cooling.
          // delay(5000); // Miliseconds to pause for cooling.
          // digitalWrite(enPin,LOW); // Enable motor after cooling.
          delay(500); // Miliseconds to pause after switching direction.
          motorStep(100); // Take steps in the new direction to un-press the switch.
    }
 
}
// End of the program loop.


// Define the motorStep function.
void motorStep( int MAX){

   for(int x = 0; x < MAX; x++) {
        digitalWrite(stepPin,HIGH);
        delayMicroseconds(500); // Controls step pulse high duration. Default = 500?
        digitalWrite(stepPin,LOW);
        delayMicroseconds(500); // Controls step pulse low duration.
        // Total duration of step pulse (high + low) determines speed of motor.
      }
     
}
// End of the motor Step function.

--- End code ---

LMK if you find any errors in the code or comments.   ;D


Scott
EDIT: Almost forgot about hysteresis in microswitches.    :banghead:
Added code to take 100 steps (about 1/2 of a revolution on many stepper motors) after changing direction.
That should be far enough to release the switch that was pressed and keep the program loop from getting stuck.
mrclean:

--- Quote from: PL1 on August 24, 2019, 08:25:10 pm ---Big progress on the Arduino code.   ;D

--- End quote ---

Nice!!!

"We have top men working on it right now", "who"...... "top men". - Indiana Jones

I absolutely can't wait to see all this collectively come together!!!  :applaud:



PL1:
What am I missing here?   :dunno

The motorStep function is only called three places in the code.
- 1 step if neither switch is pressed. (short LED blink)
- 100 steps after changing dirPin HIGH to LOW. (long LED blink)
- 100 steps after changing dirPin LOW to HIGH. (long LED blink)

The comment for the switch not pressed section, "// If neither switch is pressed, send motor step pulse." is what the logic appears to say, but isn't what the program loop is doing.

What the program loop is expected to do:

--- Code: ---    if( (leftSw  == HIGH && (digitalRead(dirPin) == HIGH)) ||
        (rightSw == HIGH && (digitalRead(dirPin) == LOW)) ){

        motorStep(1); // Calls motorStep function. (defined below)

    }

--- End code ---

Code translated into a logic statement:
If (leftSwitch isn't pressed AND dirPin=high) OR (rightSwitch isn't pressed AND dirPin=low) THEN motorStep(1).

If leftSwitch is held closed and dirPin is high, it shouldn't send motorStep(1) because neither of the two IF options are true.
- First option (red) is false because leftSwitch is pressed.
- Second option (green) is false because dirPin=high.
- With both IF options false, the program loop should go to the next item (check for direction change), not execute the THEN, right?

What the program loop is actually doing:
Holding one button pressed does not stop the program loop from sending "short LED blink" single step commands.
- Changes direction when the other switch is pressed then changes back on the next program loop cycle.
- "Short LED blink" step commands sent whether dirPin is high or low.

Holding both buttons pressed does not stop the program loop from sending "short LED blink" single step commands.
- Changes direction with each program loop cycle.
- "Short LED blink" step commands sent whether dirPin is high or low.

What am I missing?


Scott
PL1:
Made an OpenSCAD 3d printable mount for the lead screw bearings shown in the first pic below.
- Fully parametric.  Change any of the 12 variables to alter the part as desired.
- Part renders in the correct orientation for the strongest print.


--- Code: ---// Bearing mount bracket (vertical face)

// Width, height, and depth variables are relative to the part being upright as installed, not on it's side as generated.

// For best part strength, print on it's side, NOT upright or face down.

/////////////////////////////
// Define variables
/////////////////////////////
FPWidth = 48;     // Faceplate width
FPHeight = 48;    // Faceplate height
FPDepth = 4;      // Faceplate depth
BoreHeight = 31;  // Center height for bearing rotation axis
BScrewDist = 37;  // Bearing screw distance (center-to-center)
BScrewDia = 5;    // Bearing screw diameter
BHoleDia = 26;    // Bearing center hole diameter

BPWidth = 48;     // Baseplate width
BPHeight = 4;     // Baseplate height
BPDepth = 20;     // Baseplate depth
BPScrewDia = 4;   // Baseplate screw diameter
BPScrewDist = 30; // Mount screw distance (center-to-center)

// Number of fragments (polygon sides) used to render a full circle.
    $fn = 180; // Default = 180  Typical range = 6 - 360
    // 6 will render a circular hole as a hexagon, 8 will render a circular hole as an octagon.
    // Lower the number for faster rendering, raise the number for smoother rendering.

/////////////////////////////
//  Make the part
/////////////////////////////

difference(){ // Faceplate minus screw and center holes
   
    translate([FPHeight/2, FPDepth/2, FPWidth/2])
    cube([FPHeight, FPDepth, FPWidth], center=true);
    // Faceplate

    rotate([90, 0, 0])
    translate([BoreHeight, (FPWidth/2)+(BScrewDist/2), -FPDepth/2])
    cylinder(FPDepth+1, d=BScrewDia, center=true);
    // Screw hole 1

    rotate([90, 0, 0])
    translate([BoreHeight, (FPWidth/2)-(BScrewDist/2), -FPDepth/2])
    cylinder(FPDepth+1, d=BScrewDia, center=true);
    // Screw hole 2

    rotate([90, 0, 0])
    translate([BoreHeight, FPWidth/2, -FPDepth/2])
    cylinder(FPDepth+1, d=BHoleDia, center=true);
    // Center hole

    } // End faceplate minus screw and center holes
//
difference(){ // Baseplate minus screw holes
   
    translate([BPHeight/2, BPDepth/2, BPWidth/2])
    cube([BPHeight, BPDepth, BPWidth], center=true);
    // Baseplate

    rotate([0, 270, 0])
    translate([(BPWidth/2)+(BPScrewDist/2), (BPDepth/2)+FPDepth/2, -BPHeight/2])
    cylinder(BPHeight+1, d=BPScrewDia, center=true);
    // Screw hole 1

    rotate([0, 270, 0])
    translate([(BPWidth/2)-(BPScrewDist/2), (BPDepth/2)+FPDepth/2, -BPHeight/2])
    cylinder(BPHeight+1, d=BPScrewDia, center=true);
    // Screw hole 2

    } // End baseplate minus screw holes
//

--- End code ---

You may want to avoid this type of bearing.



This type is the better choice.
- Less hardware to mount. (2 screws per bearing vs. 4)
- Stronger mount.  (A flat vertical spacer is less likely to break than the L-shaped bracket posted above.)




Scott
Navigation
Message Index
Next page
Previous page

Go to full version