I've now got the fiddle yard traverser moving automagically. I found the 28BYJ-48 stepper just didn't have enough torque to move the table reliably without missing steps. Tried changing from the rack and pinion drive to a 2mm pitch timing belt, but had the same problem. There was nothing for it but to go to a bigger stepper motor. This is a NEMA 17 with 1.8 degrees per full step. The ULN2003 driver board wasn't powerful enough for this, so I tried the H-bridge board again. This wasn't successful. The motor was missing steps and reversing itself randomly. After a lot more reading up I bought an A4988 Stepstick board to drive the motor and this works well with the new stepper motor.
Another problem was the timing belt. Each step of the NEMA 17 motor would move the table about 0.15mm using the belt and pulleys. I wasn't happy with this large a movement considering the precision required for 2mm track and wheels, so a leadscrew and follower nut were purchased. This is 8mm pitch and gives 0.04mm of table movement per full step. Some new brackets were fabricated and the whole thing assembled. The motor is mounted in a bracket at one side of the traverser. The lead screw is attached via a flexible coupling and runs through the follower nut, which is attached by another bracket to the table. I am still waiting on a pillow block bearing to support the far end of the leadscrew. For the moment, this is in a plain aluminium hole.
There are five push buttons for the five possible positions of the four tracks on the table to access the double track of the layout. A single press of the button will send the table to the appropriate position. To prevent any inaccuracy from backlash in the system the table does a little joggle at the end so it always comes to a stop from the same direction. There are a pair of microswitches to stop the table over running and getting damaged. One of these is used as a home zero position for the stepper motor. I still need to check the accuracy and repeatability of the table, but so far it looks OK. There are options of sub-stepping the motor with the driver board, but for the moment I am using full steps.
The idea of automating this has lead me in to a bit of a diversion from actually producing the model. I now know a bit more about stepper motors and their control than I did a month or two ago. Trouble is I am now thinking "Home made CNC milling machine. Shouldn't be too hard!". Only two and a half years to finish this layout. Don't get side tracked.
Below is a video of the traverser in operation. From power on it finds its home position by operating, then releasing one of the microswitches. From there each button press sends it to a new position, with a little joggle at the end so it always stops after approaching from the same direction. Nice smooth acceleration and deceleration, which should keep 2mm trains on the track.
I've also included the sketch as Nick requested. This is extensively commented, so I've got a chance to work out what I did later.
Mim
Edited by Mim, who has just discovered the "code" button in the editor. Edited again 4/1/18 with the most recent version.
/*Traverser stepper motor control for Middleton Top fiddle yard.
Written by Madam Mim November 2017.
Modified December 2017 to make the code more compact
and add a single uncoupling magnet servo operation.
Feel free to copy, plagiarise, steal, modify, or ignore.
Especially, feel free to improve my kludgy coding.
Works with an Arduino Uno, an A4988 Stepstick steppper driver and a NEMA 17 stepper motor.
Can be modified for other Arduino PLC's, stepper drivers and motors.
Uses the AccelStepper library to give smooth acceleration and decelleration, which must be installed.
See http://www.airspayce.com/mikem/arduino/AccelStepper/
The uncoupling magnet servo uses the standard IDE servo library to drive an SG90 servo,
powered from an independent 5V supply.
No sub-stepping is used here, but it could be enabled if required.
A single press of any of the five buttons will send the traverser to the appropriate track position.
Pressing another button while in transit will change the destination.
There are limit microswitch inputs each end to prevent damage.
Hitting a limit switch will stop the motor.
One of the limit switches is also used as a home sensor.
The destination buttons, uncoupler switch and limit switches are grounded to operate.
Arduino internal pull-up resistors are used to prevent spurious triggering from noise.
Home sensing and all positions are finally approached from the positive steps direction.
This is an anti-backlash measure to improve accuracy and repeatability of positioning.
The sketch includes the operation of a single uncoupling magnet servo.
This can be removed if not required.
*/
#include <AccelStepper.h>
#include <Servo.h>
//Set motor interface type to Driver (1) to suit the A4988 Stepstick which uses two pins, step & direction.
byte stepStick = 1;
// pin outs to the stepper driver.
byte stepPin = 2; // The pin to send the step signal to the A4988 stepstick
byte directionPin = 3; // The pin to send the direction signal to the stepstick
//Output pin for uncoupler magnet servo.
byte magPin = 4;
// Define stepper1.
AccelStepper stepper1(stepStick, stepPin, directionPin);
//Define uncoupler magnet servo
Servo magServo;
//constants to hold input pin assignments.
byte limitforward = 7; //forward and reverse limit microswitches.
byte limitbackward = 6;
byte button[] = {8, 9, 10, 11, 12}; // Traverser go to position buttons.
byte magSwitch = 15; //Analogue pin A1 used as a digital input for uncoupler switch.
/*constants to hold steps for each position.
Adjust to tune traverser.
1mm = 25 steps (200 steps per revolution.
8mm pitch leadscrew and NEMA 17 1.8 deg/step motor).*/
const int backlash = 50; //steps to take during anti-backlash manouver.
const int pitch = 563; // Pitch in steps between tracks.
const int homed = 273; // distance from home to first position.
//define positions to go to for each track. adjust +/-0 for fine tuning.
const int pos[] = {homed + 4 * pitch + 0, homed + 3 * pitch + 0, homed + 2 * pitch + 0, homed + pitch + 0, homed};
//Constants to hold uncoupler magnet raised and lowered positions
byte magLowered = 50;
byte magRaised = 150;
//Constant to hold uncoupler magnet servo speed delay.
byte magSpeed = 15; // ms/deg.
//variables to store current table and servo position and remember previous position.
//Used for anti-backlash and change detection.
int targetpos = 0; //initially zero for after the traverser is homed in setup.
int oldpos = 0;
byte magPos;
void setup() {
// put your setup code here, to run once:
//Stepper1 speeds, accelerations. Adjust to prevent stock being rattled around.
stepper1.setMaxSpeed(1000.0);
stepper1.setAcceleration(100.0);
stepper1.setSpeed(100);
//define buttons as inputs with internal pullup resistors used.
for (byte n = 0; n < 5; n++) {
pinMode(button[n], INPUT_PULLUP);
}
//define microwswitches and uncoupler switch as inputs with internal pullup resistors used.
pinMode(limitforward, INPUT_PULLUP);
pinMode(limitbackward, INPUT_PULLUP);
pinMode(magSwitch, INPUT_PULLUP);
//home uncoupler magnet.
magServo.attach(magPin);
delay(100); //Delay for servo to sort itself out after attachment.
magServo.write(magLowered);
magPos = magLowered; //magPos set so changes can be acted on.
//home motor
//Travel backwards till you trigger the home microswitch.
//Works by setting stepPin high, then low with delays. A bit noisy when running!
while (digitalRead(limitbackward)) {
digitalWrite(directionPin, LOW);
digitalWrite(stepPin, HIGH);
delay(5);
digitalWrite(stepPin, LOW);
delay(5);
}
//Back away from the microswitch slowly till it goes off.
//As above, but longer delays to slow it down.
while (!digitalRead(limitbackward)) {
digitalWrite(directionPin, HIGH);
digitalWrite(stepPin, HIGH);
delay(10);
digitalWrite(stepPin, LOW);
delay(10);
}
//makes the home position zero steps. All subsequent movements are referenced from here.
stepper1.setCurrentPosition(0);
}
void loop() {
// put your main code here, to run repeatedly:
//overtravel protection. Stops while either limit switch is activated.
while (digitalRead(limitforward) && digitalRead(limitbackward)) {
//Check each button in turn.
//If they have been pressed, then move to the new position.
for (byte n = 0; n < 5; n++) {
if (digitalRead(button[n]) == LOW) {
stepper1.moveTo(pos[n]);
targetpos = pos[n]; //used to decide when to trigger anti-backlash moves.
}
}
stepper1.run(); //move the stepper to the position. Non blocking. Program is looping round.
/*Anti backlash protection.
//After moving to the new position, step back, then forward.
Final position always approached from the same direction.*/
if ((stepper1.distanceToGo() == 0) && (targetpos != oldpos)) {
stepper1.move(-backlash); //relative move backwards.
stepper1.runToPosition(); //runToPosition blocks everything else until complete.
stepper1.move(backlash); //relative move forwards.
stepper1.runToPosition();
oldpos = targetpos; //once complete, oldpos is set to targetpos to break out of loop.
}
//Check the uncoupler magnet switch. If it has changed, then move the magnet servo.
if ((digitalRead(magSwitch) == LOW) && (magPos == magLowered)) {
for (int m = magLowered; m <= magRaised; m += 1) {
magServo.write(m);
delay(magSpeed);
}
magPos = magRaised;
}
if ((digitalRead(magSwitch) == HIGH) && (magPos == magRaised)) {
for (int m = magRaised; m >= magLowered; m -= 1) {
magServo.write(m);
delay(magSpeed);
}
magPos = magLowered;
}
}
}