Jump to content
 

DCC Controlled (PECO) Turntable Project using a Arduino Uno


Recommended Posts

Really enjoying this series of articles on turntable control. I will be having ago myself in the near further

but cannot seem to be able to source the stepper motor. Where can I get hold of the stepper motor that cambriancoaster

and Tender are using.

 

Regards

 

Alan

 

Hello Alan:

 

I obtained my stepper motor from Proto-pic for £13.32 (including VAT).   The following link should take you directly to the stepper motor at their website:

 

http://proto-pic.co.uk/stepper-motor-with-cable/         

 

(Note you might have to copy and post the above web address into your Browser).

 

I also got my Arduino Uno and Adafruit Motor Shield from them,

 

Best wishes

 

CC

Link to post
Share on other sites

Hello JB:

 

I am afraid it will only work with V2 of the motor shield.

 

I suggest you deal directly with the manufacturers Adafruit Industries (USA).

 

Their website is at:

 

www.adafruit.com

 

The specific link to the motor shield (v2) is:

 

www.adafruit.com/product/1438

 

 

However they do have an agent in Argentina:

 

www.openhacks.com

 

I hope this helps you.

 

Regards

 

CC

Link to post
Share on other sites

@estreetcar, may I be permitted to make a few observations on your code. Please feel free to ignore them.

...

Just my 2 cents.

 

Organizing it like this should make it easier to figure out how to do the remaining pieces

 

...R

Robin and Ray-

 

I have been traveling this last month, but I have also been working on code with a friend.  I have taken all of the groups suggestions and think I will have a really nice result to share.  I have also been working on a laser cut motor mount that I plan to cut out tomorrow.  Hopefully I will have some time before I head out again to try everything.  My goal is to have the turntable fully operational, with scenery and buildings by the time our modular clubs gets together this July.

 

What we have been working on with the code is really simplifying the entire thing so that multiple processes can occur without seeing "stutters" in the drive.  Every input puts in a request and if one is active, it will decelerate the table to zero, then move to the new target position.  The programming feature is fabulous! We are unable to write back to the code, but it shows the value on the screen and that is good enough for me.  I have the analog input panel working very well, the DCC working and a JMRI screen (attached) also working. 

 

post-22354-0-71545600-1398273178_thumb.jpg

 

For a long time I have wanted a turntable that worked correctly, was reliable and was also adjustable.  I don't have the hobby budget for the NYRS unit, but I really want the features.  I think I am really close to duplicating all of its features with a lot less cost.  I am sure it won't be perfect for everyone, but I hope that the code I post can be used by other model railroaders with the same goals. 

 

In response to the other folks trying to do this but not using the auto align feature on start-up, I think you are missing out on one of the huge features of this control system.  Just power on the railway and it will click around to the home position.  No intervention needed.  My tests on the bench show the same as Ray, it is very reliable and consistent.  I have the turntable "release" the motor after inactivity of 5 min.  This is really to reduce power consumption and heat on the stepper.  I don't have a brake, but have made provisions for one in the future.  I will probably go that route and then the unit will be pretty much a stand in for the NYRS unit.

 

I have toyed with the NYRS feature of always approaching the track from the same direction- ie- if you are spinning CCW, going past the track and then backing up CW, so that the lash & slop of the bridge is always consistent (always approached track from CW direction) and any slop can be ignored.  On my turntable I don't see the need as it is new, but I am putting it in the code anyway to account for wear/slop over time.  The table only moves a tiny bit past the target, but it does eliminate any slop in the system.  

 

I really appreciate all the help on this. 

 

Thanks again everyone.  I hope to post the code and more pictures in a week or so.

-Eric

  • Like 2
Link to post
Share on other sites

  I hope to post the code and more pictures in a week or so.

-Eric

Will look forward to that Eric, sounds like you've been busy. I've had a new computer so need to reinstall all the Arduino stuff so I can continue development with my system.

Ray.

Link to post
Share on other sites

Hi all,

 

A quick update and belated thanks to those who helped out with the operation of my sector plate - I've got my test rig up and working beautifully thanks to this thread. I'm trying to fine tune a few bits before bolting it all together permanently.

 

Open Items for me are:

1: release after 2-3 min of inactivity  (then I can remove the "release()" command from the Switch/Case causes.)

 

Eric, Did you mange to get anywhere with the release function? I've noticed that my stepper does get quite warm after a period of use so I'm quite keen to try and include such a function if possible.

 

Speaking more generally about using the Arduino/Adafruit combination to control stepper motors via DCC, has given me a few ideas of possible applications. Is it possible to alter the coding given in Part 6 so that when the point is thrown on the DCC controller the stepper motors moves from it's start position (Let's say 12 o'clock) to 3 o'clock, pause for a couple of seconds and then return to 12 o'clock? When the switch is closed is stays at 12 o'clock. I had a little play about with the DCC segment of the coding, thinking it my be something along the lines of the below, but it didn't seem to work.

 

boolean enable = (data & 0x01) ? 1 : 0;
   
        if( address == 196 )
        {
            Serial.print("Basic addr: ");
            Serial.print(address,DEC);
            Serial.print("   activate: ");
            Serial.println(enable,DEC);
           
            if( enable )
              {
                stepper2.moveTo(800);
                delay(2000);
                stepper2.moveTo(0);        

              }

           else
              {
                stepper2.moveTo(0);
                     
              }
       }
}

 

Sorry if I'm barking up the wrong tree.... or even in the wrong forest!

 

Cheers,

 

Steve

Edited by Pixie
Link to post
Share on other sites

Hi all,

 

Eric, Did you mange to get anywhere with the release function? I've noticed that my stepper does get quite warm after a period of use so I'm quite keen to try and include such a function if possible.

Cheers,

 

Steve

Steve-

 

I have release working as it should, but I want to add a brake so that when not moving the table will be locked into position.  The cost of a new electrically released spring brake is beyond my hobby budget, but I found a used/surplus one to use that is within my budget.  FYI new the brake costs between $165 and $200 USD, used you can find them for $40-$50.  The other option is to just call up another track to reactivate the stepper before you drive anything onto the unlocked table.  My table is very smooth rotating and can easily get bumped.  The lock will keep it in position in the no-power state.  When you wake up the stepper/adruino with either a DCC or Analog command the brake will release and the stepper move the table to the position.  Then after a short time out, the brake will set and the release will activate.  In the stand-by state only the arduino is running  and the brake and stepper are not powered.

 

More later

-Eric

Link to post
Share on other sites

Steve-

 

I have release working as it should, but I want to add a brake so that when not moving the table will be locked into position.  The cost of a new electrically released spring brake is beyond my hobby budget, but I found a used/surplus one to use that is within my budget.  FYI new the brake costs between $165 and $200 USD, used you can find them for $40-$50.  The other option is to just call up another track to reactivate the stepper before you drive anything onto the unlocked table.  My table is very smooth rotating and can easily get bumped.  The lock will keep it in position in the no-power state.  When you wake up the stepper/adruino with either a DCC or Analog command the brake will release and the stepper move the table to the position.  Then after a short time out, the brake will set and the release will activate.  In the stand-by state only the arduino is running  and the brake and stepper are not powered.

 

More later

-Eric

 

 Hi Eric,

 

Many thanks for the above - the brake arrangement sounds very interesting. I hope I am not being cheeky, but I'd be really keen to see your coding you have used to get the automatic release() function working. I must admit that I don't really understand why the Arduino requires a second throw of the point of the DCC controller to activate the release() function on your initial coding - is there a reason why it essentially stops after the stepper2.moveto(20) on the first throw of the point? 

 

Thanks again,

 

Steve

Link to post
Share on other sites

I suspect any small servo could be used as a brake if you put a piece of friction material (rubber?) on the end of the servo arm. Cost about £5 or less. Programming would be trivial.

 

...R

Robin-

 

Yes, I had a little time today and made the servo brake as you suggested.  Very cool idea.  Here is the drawing I am taking to my friends laser cutter tonight.  I think you are on to something.  I still left the mounting holes for the electric brake if it doesn't work, but I remain optimistic.  And it was very easy to do with the V2 motor shield.  I just plugged in the servo and it worked!  I will share the code once I fully debug it. 

 

This is probably the most fun I have had in a long time... 

I will post some pictures when I get home with the laser cut parts.

-Eric

Stepper Motor Turntable Mount- EMS 4-24-14.pdf

Link to post
Share on other sites

  • RMweb Gold

 

This is probably the most fun I have had in a long time... 

I will post some pictures when I get home with the laser cut parts.

-Eric

 

Hi Eric,

 

I like your construction method for the Stepper Motor mounting - very neat, accurate and sturdy.

 

An open question...

Is the 'Brake' required because you are micro-stepping the motor, (for a quieter smoother rotation), and when you power the motor drive board down there could be a small movement of the steppers' output shaft as it finds the nearest whole step?

Or is it for something else?

 

 

Kev.

Link to post
Share on other sites

Hi Eric,

 

I like your construction method for the Stepper Motor mounting - very neat, accurate and sturdy.

 

An open question...

Is the 'Brake' required because you are micro-stepping the motor, (for a quieter smoother rotation), and when you power the motor drive board down there could be a small movement of the steppers' output shaft as it finds the nearest whole step?

Or is it for something else?

 

 

Kev.

Kev-

 

The brake is only there so that I can release the power to the stepper and have confidence that the table will still be in the same position when I move it again.  The stepper does not move at all when released.  If there is any mechanical binding in the turntable, it might move, but mine does not.  I just want to ensure that if a locomotive drives onto the table or someone bumps the table it does not move.  As the only reference for positioning is at start-up, any slight unintended movement would cause error until the table was reset.

 

I hope that helps.I should have the mount built this weekend. I will post some pictures and video to explain the set-up.

-Eric

Link to post
Share on other sites

I have been admiring the diagram in Reply #130 and it occurred to me that it would be nice if you could select the roads by touching the diagram.

 

Quite by coincidence I discovered the existence of these Nintendo DS touch screens as spare parts that can work with an Arduino. By another of those strange coincidences I realized I already had a similar device (though larger, 7inch diagonal) that I bought a few years ago for another abortive project. (Never throw anything away !)

 

These are just passive touch-sensitive glass panels. All that is necessary is to put them on top of a suitable drawing or picture (such as a picture of a keypad, or the turntable diagram) and program the Arduino to associate different regions with different actions. I roughly drew an 8x8 grid on a piece of paper representing  64 "buttons" and it looks like it would easily distinguish between them.

 

And the really nice thing is that it just requires connection to 4 of the Arduino analog pins. It then returns the X and Y positions as values between 0 and 1023.

 

The Nintendo screens are probably too small for the turntable diagram, but they should certainly be OK for a 5x4 button array. And larger screens seem to available on Ebay.

 

You could place the screen over a panel with LEDs if you wanted to have visual feedback - for example for point or signal controls.

 

...R

Link to post
Share on other sites

All-

 

I had a very productive weekend, but a busy week at work so far.  Below are pictures of the progress of the stepper motor mount I laser cut at my friends house.

post-22354-0-52013700-1398875107.jpg

post-22354-0-50472200-1398875108.jpg

post-22354-0-54692300-1398875109.jpg

 

The unit went together right out of the laser (as should be expected) and I need to glue it up and get everything finalized.  The small size is due to it needing to fit under a module and not protrude too far to make transport easier.  It is very scale-able.  I am building a second unit for the club layout (permanent install) and that will be a much more open design, and not so compact.  I did not need a second bearing to keep the turntable shaft running true for the module, as my old drive system included that.  The one for the club will have two shaft bearings and use that electric brake I picked up instead of the servo.  I am quite curious to see the servo brake work in real life.

 

Hope that was a little inspiring....  I can share the CAD files with anyone that wants, or just post them here.  The PDF of the drive unit as built is attached below.

Stepper Motor Turntable Mount- EMS 4-26-14.pdf

 

 

Thanks

-Eric

 

  • Like 1
Link to post
Share on other sites

All-

 

I had a very productive weekend, but a busy week at work so far.  Below are pictures of the progress of the stepper motor mount I laser cut at my friends house.

Thanks

-Eric

All-

 

To continue this post I think I have de-bugged the code enough to share it with everyone.  There are still some rough patches, but I have most of the important stuff working.  I need to re-cut one part with the Laser this weekend, and then I should be ready to assemble permanently.

 

The Important things are working:  Analog, DCC, Programming assistant and Release/Servo Brake. 

 

Still need to get working are: approaching the track from the same way each time and having the unit take the shortest route to the track instead of all the way around.  If anyone knows how to do either, please help.

 

Here is the code:

(note it looks a lot different than my last posting- as that was Rev. 3.  You should be able to follow.  Questions- just ask. See post #99 for my break-out boards)

 

**EDITS on 5-4-14  Revised Code**

////////////////////////////////////////////////////////////////////////////////
//
//   May 4 2014  - Completed Majority of Open Items- First Release Version !!!
//
////////////////////////////////////////////////////////////////////////////////
//
//   DCC Turntable Rev_15e May_4_2014
//        based on code from rmweb.co.uk forum
//          edited by E.Sitiko with programming help from D.Whetten
//     This is a work in progress, I need some help with programming
//        Program functions desired are:
//            12 road turntable  (works)
//            DCC addresses 200 to 212 (works!)
//            clean up code to make changes to Position easy (works)
//            "Real World" interface
//                2- N.O. Push buttons to enable move to "Head" or "Tail" (works)
//                1- 10k Continuous turn POT to select track. (works)
//                1- N.O. Reset button on fascia  (works)
//               Use digital inputs on 6 & 7 to stepforward and backward and
//               read out in the serial monitor so that you can program easier. (works)
//            "Release & Servo Brake" function works with brake and release function. (works!!)
//            Route positioning not taking shortest route to next location- (i.e. Tk.1 to Tk. 10 would be through 0)  (works)
//            Always approaches a track from the same direction to account for gear/mechanical lash/slop. (works)
//                      if the table was going CW to pos. 1 it approaches value B and stops
//                      if the table was going CCW to Pos 1 it should go past B a (value B-X) and then go CW to B.
//
//            Added a (commented out)set up tool in the "loop" section of the code to set up the POT on the fascia panel.
//                this let's you align the POT to the markings on the panel.  Uncomment to use, then re-comment to hide.  (works)
//            Added a (commented out)set up tool in the "loop" section of the code to set up the Brake Servo using the POT on the fascia panel.
//                this let's you find the optimal Release and Brake position for the servo brake.  Uncomment to use, then re-comment to hide.  (works)
//
//
//   (FUTURE)  Address 200 (or Analog 0)= continuous slow rotation, based on "Head" or "Tail" (does not work now)
//
////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////
//
// DCC Turntable Control Routines

#include <DCC_Decoder.h>
#include <AccelStepper.h>
#include <Wire.h>
#include <Adafruit_MotorShield.h>
#include "utility/Adafruit_PWMServoDriver.h"
#include <Servo.h>            //servo library reference

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// Defines and structures
//
#define kDCC_INTERRUPT            0

typedef struct
{
    int               address;                // Address to respond to
   
} DCCAccessoryAddress;

DCCAccessoryAddress gAddresses[13];          // Allows 13 dcc addresses: [XX] = number of addresses you need (including 0).

const unsigned long releaseTimeout_ms = 2000;      //reduced to 2 seconds for now    
const long MOTOR_STEP_COUNT = 200 * 16;                //number of steps for a full rotation
       
//new stuff
boolean tableTargetHead = false;        //the new variable for Head/Tail Position either from DCC or Analog
int tableTargetPosition = 0;            //the new variable for Table Postition either from DCC or Analog
boolean newTargetLocation = false;
boolean buttonPushedHead = false;
boolean buttonPushedTail = false;
boolean inMotionToNewTarget = false;
boolean isReleased = false;        // isReleased tries to make sure the motor is not continuously released
unsigned long stepperLastMoveTime = 0;
const long MOTOR_OVERSHOOT = 10;        // the amount of overshoot/ lash correction when approaching from CCW
int overshootDestination = -1;

//Servo Stuff
Servo brakeservo;  // create servo object to control a servo
const int servoBrake = 9;  //value for brake position
const int servoRelease = 2;  //value for release position

//Programming button variables
boolean programmingMode = false;
boolean programmingModeMoveForward = true;    //If in programming mode, are we moving forward?  
unsigned long programmingLastMoveMillis = 0;    //When was the last programming move done?
const int programRotateDelay = 100;            //Delay between steps in ms while holding button

//Do display rotation
const int displayRotateDelay = 5;    //This is the minimum delay in ms between steps of the stepper motor
boolean displayRotating = false;        //Is the "Display Rotate" function enabled?
boolean displayRotatingCW = true;        //In Display Rotate mode, are we rotating CW or CCW?  
unsigned long displayRotatingLastMoveMillis = 0;

//location variables
const int A = 150;          //TT Track 0-  Head
const int B = 45;           //TT Track 1-  Head
const int C = 205;          //TT Track 2-  Head
const int D = 490;          //TT Track 3-  Head
const int E = 588;          //TT Track 4-  Head
const int F = 765;          //TT Track 5-  Head
const int G = 1939;         //TT Track 6-  Head
const int H = 2072;         //TT Track 7-  Head
const int I = 2205;         //TT Track 8-  Head
const int J=  2499;         //TT Track 9-  Head
const int K = 2623;         //TT Track 10- Head
const int L = 2739;         //TT Track 11- Head
const int M = 2872;         //TT Track 12- Head
const int N = 1750;         //TT Track 0-  Tail
const int O = 1645;         //TT Track 1-  Tail
const int P = 1805;         //TT Track 2-  Tail
const int Q = 2090;         //TT Track 3-  Tail
const int R = 2188;         //TT Track 4-  Tail
const int S = 2365;         //TT Track 5-  Tail
const int T = 339;          //TT Track 6-  Tail
const int U = 472;          //TT Track 7-  Tail
const int V = 605;          //TT Track 8-  Tail
const int W = 899;          //TT Track 9-  Tail
const int X = 1023;         //TT Track 10- Tail
const int Y = 1139;         //TT Track 11- Tail
const int Z = 1272;         //TT Track 12- Tail

const int PositionTrackHead[] = { A, B, C, D, E, F, G, H, I, J, K, L, M };
const int PositionTrackTail[] = { N, O, P, Q, R, S, T, U, V, W, X, Y, Z };

////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
//
// Adafruit Setup

Adafruit_MotorShield AFMStop(0x60); // Default address, no jumpers

// Connect stepper with 200 steps per revolution (1.8 degree)
// to the M3, M4 terminals (blue,yellow,green,red)

Adafruit_StepperMotor *myStepper2 = AFMStop.getStepper(200, 2);

// you can change these to SINGLE, DOUBLE, INTERLEAVE or MICROSTEP!

// wrapper for the motor! (3200 Microsteps/revolution)
void forwardstep2() {  
  myStepper2->onestep(BACKWARD, MICROSTEP);
}
void backwardstep2() {  
  myStepper2->onestep(FORWARD, MICROSTEP);
}
void release2()   {
  myStepper2->release();
}


// Now we'll wrap the stepper in an AccelStepper object  

AccelStepper stepper2 = AccelStepper(forwardstep2, backwardstep2);


//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// Decoder Initiation
//
void ConfigureDecoder()
{                                    //Put all the decoder #'s you need here.  Remember to change
                                     // DCCAccessoryAddress gAddresses[XX];(above) where XX = number of addresses you need.
    gAddresses[0].address = 200;      
    gAddresses[1].address = 201;      
    gAddresses[2].address = 202;
    gAddresses[3].address = 203;
    gAddresses[4].address = 204;
    gAddresses[5].address = 205;
    gAddresses[6].address = 206;
    gAddresses[7].address = 207;
    gAddresses[8].address = 208;
    gAddresses[9].address = 209;
    gAddresses[10].address = 210;
    gAddresses[11].address = 211;
    gAddresses[12].address = 212;
}

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// Basic accessory packet handler
//

void BasicAccDecoderPacket_Handler(int address, boolean activate, byte data)
{
        // Convert NMRA packet address format to human address
    address -= 1;
    address *= 4;
    address += 1;
    address += (data & 0x06) >> 1;
    
    boolean enable = (data & 0x01) ? 1 : 0;
    
    for(int i=0; i<(int)(sizeof(gAddresses)/sizeof(gAddresses[0])); i++)
    {
        if( address == gAddresses[i].address )
        {
          
            Serial.println("");  
            Serial.print("DCC addr: ");
            Serial.print(address,DEC);
            Serial.print("   Head/Tail = (1/0) : ");
            Serial.println(enable,DEC);
                                  
            //new stuff
                        tableTargetHead = enable;
                        tableTargetPosition = i;

            //New packet and we have a new target location, set the flag
            newTargetLocation = true;
                        doStepperMove();   
            
              // old location for if/else statements
        }
    }
}
      
/////////////////////////////////////////////////////////////////////////////
//
//  readAnalogLocation() Reads the pot input on analog pin 1, displays some
//  information out the serial, and sets the target position variable
//
int readAnalogLocation()
{
    int potDisp = analogRead(1);
    int potOut = potDisp / 79; //1023 / 13

    Serial.println("");
        Serial.print("Analog Location: ");
    Serial.print("Track # ");
    Serial.print(potOut, DEC);
    Serial.print("   Head/Tail = (1/0) : ");
    Serial.println(tableTargetHead, DEC);
    

    //tableTargetPosition = potOut;
    return potOut;
}


//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// Subroutine: doStepperMove()
//     Moves the stepper based on location inputs, calls up "SetStepperTargetLocation()"
//
///////////////////////////////////////

void doStepperMove()
{

        // Run the Stepper Motor //
        stepper2.run();

    boolean isInMotion = (abs(stepper2.distanceToGo()) > 0);
    boolean newTargetSet = false;

    // If there is a new target location, set the target
    if (newTargetLocation)
    {
        Serial.println("Moving to New Target Location...");
        SetStepperTargetLocation();
        newTargetSet = true;
    }

    if (inMotionToNewTarget)
    {
        if ((!isInMotion) && (!newTargetSet))
        {
            Serial.print("Not Moving!  DtG: ");
            Serial.print(stepper2.distanceToGo());
            Serial.print(" TP: ");
            Serial.print(stepper2.targetPosition());
            Serial.print(" CP: ");
            Serial.print(stepper2.currentPosition());
            Serial.print(" S: ");
            Serial.print(stepper2.speed());
            Serial.println();
        }
                //release the brake
                                brakeservo.write(servoRelease);
                                delay (5);  
        inMotionToNewTarget = isInMotion;
    }
    else
    {
        if (programmingMode)
        {
            //If we are programming, do that move
                //release the brake
                                brakeservo.write(servoRelease);
                                delay (5);  
        
                        if ((millis() - programmingLastMoveMillis) >= programRotateDelay)
            {
                programmingLastMoveMillis = millis();
                stepper2.move(programmingModeMoveForward ? 1 : -1);
                                
                                Serial.println("");
                Serial.print("Programming mode, Current location: ");
                Serial.println(stepper2.currentPosition());
                        int potDisp = analogRead(1);
                        int potOut = potDisp / 79; //1023 / 13
                        Serial.print("Analog Location: ");
                                Serial.print("Track # ");
                        Serial.println(potOut, DEC);
                        delay(45);
    
            }
        }
        //Only do display rotate if we aren't doing the programming move
        else if (displayRotating)
        {
            //Are we in "DisplayRotate" mode?  if so, do that..
                //release the brake
                                brakeservo.write(servoRelease);
                                delay (5);  
                        if ((millis() - displayRotatingLastMoveMillis) >= displayRotateDelay)
            {
                displayRotatingLastMoveMillis = millis();
                stepper2.move(displayRotatingCW ? 1 : -1);
                Serial.print("Display rotate mode: ");
                Serial.println(stepper2.currentPosition());
            }
        }

        if ((stepper2.currentPosition() % MOTOR_STEP_COUNT) == 0)
        {
                    //setCurrentPosition seems to always reset the position to 0, ignoring the parameter
            Serial.print("Current location: ");
            Serial.print(stepper2.currentPosition());
            Serial.println(" % STEPCOUNT.  Why here?");
        }
    }

                      //stepper timer subroutine came from here.
}

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// Subroutine: SetStepperTargetLocation()
//     Takes the global variables: tableTargetHeadOrTail, and tableTargetPosition, and sets the stepper2
//   object moveTo() target position in steps-  inserts values back into "doStepperMove()"
//
///////////////////////////////////////
void SetStepperTargetLocation()
{
    int newTargetLoc = -1;
    if (tableTargetHead)
    {    //use head location variable
        if (tableTargetPosition == 0)
        {
            //Special case: Display Rotate
            displayRotating = true;
            displayRotatingCW = true;
           
            Serial.println("Entering display rotate mode, CW.");
        }
        else
        {
            displayRotating = false;
            newTargetLoc = PositionTrackHead[tableTargetPosition];
            inMotionToNewTarget = true;
        }
    }
    else
    {    //use tail location variable
        if (tableTargetPosition == 0)
        {
            //Special case: Display Rotate
            displayRotating = true;
            displayRotatingCW = false;

            Serial.println("Entering display rotate mode, CCW.");
        }
        else
        {
            displayRotating = false;
            newTargetLoc = PositionTrackTail[tableTargetPosition];
            inMotionToNewTarget = true;
        }
    }

    if (newTargetLoc > 0)
    {
        int currentLoc = stepper2.currentPosition();
        int mainDiff = newTargetLoc - currentLoc;
        if (mainDiff > (MOTOR_STEP_COUNT / 2))
        {
            mainDiff = mainDiff - MOTOR_STEP_COUNT;
        }
        else if (mainDiff < (-MOTOR_STEP_COUNT / 2))
        {
            mainDiff = mainDiff + MOTOR_STEP_COUNT;
        }

        if (mainDiff < 0)
        {
            mainDiff -= MOTOR_OVERSHOOT;
            overshootDestination = MOTOR_OVERSHOOT;
        }
        stepper2.move(mainDiff);
    }

    programmingMode = false;
    newTargetLocation = false;

  }
 
//////////////////////////////////////////////////////////////////////////////////////////////////
//
//  Stepper Timer sub routine this runs from the main loop. It also supports the release function.
//
//////////////////////////////////////////////////////////////////////////////////////////////////

void stepperTimer()
{
    // Run the Stepper Motor //
    stepper2.run();
       
    boolean isInMotion = (abs(stepper2.distanceToGo()) > 0);
    //Check if we have any distance to move for release() timeout.  Can check the
    // buffered var isInMotion because we also check the other variables.
    if (isInMotion || programmingMode || displayRotating)  // || newTargetSet)
    {
        //We still have some distance to move, so reset the release timeout
        stepperLastMoveTime = millis();
        isReleased = false;
    }
    else
    {
        if (!isReleased)
        {
            if (overshootDestination > 0)
            {
                stepper2.move(overshootDestination);
                overshootDestination = -1;
            }

            if (((millis() - stepperLastMoveTime) >= releaseTimeout_ms))
            
            {
                //If isReleased, don't release again.
                isReleased = true;
                Serial.print ("Relative Current Position: ");
                Serial.print(stepper2.currentPosition());    //shows position the table thinks it is at (how it got here)

                int currentLoc = stepper2.currentPosition();    // Resets the positon to the actual positive number it should be
                currentLoc = currentLoc % MOTOR_STEP_COUNT;
                if (currentLoc < 0)
                {
                    currentLoc += MOTOR_STEP_COUNT;
                }
                stepper2.setCurrentPosition(currentLoc);
                stepper2.moveTo(currentLoc);
                
                Serial.print ("    Actual Current Position: ");
                Serial.println(stepper2.currentPosition());    // shows the position value corrected.

                //Set the servo brake
                brakeservo.write(servoBrake);
                delay(750);

                //release the motor
                release2();
                Serial.println("    Brake Set & Motor Released ");
            }
        }
    }
}

///////////////////////////////////////////////////////
//
//  Check Programming Buttons-  look for either of the programming buttons being pushed
//
void checkProgrammingButtons()
{
    programmingMode = false;
    bool buttonPushedProgUp = (digitalRead(6) == LOW);
    bool buttonPushedProgDown = (digitalRead(7) == LOW);

    //If one button is pushed, but not both
    if ((buttonPushedProgDown || buttonPushedProgUp) && !(buttonPushedProgDown && buttonPushedProgUp))
    {
        programmingMode = true;
        programmingModeMoveForward = buttonPushedProgUp;
        doStepperMove();  
        }

}

///////////////////////////////////////////////////////////////
// Manual move to using the pushbuttons and POT
//
void checkManualButtons()
{
    //Read the Head button input
    if (digitalRead(4) == LOW)
    {
        buttonPushedHead = true;
    }
    else if (buttonPushedHead)
    {
        //Button was pushed, but is not being pushed now
        //Clear pushed variable
        buttonPushedHead = false;
        //Set the target head variable
        tableTargetHead = true;
        //Read the analog location
        tableTargetPosition = readAnalogLocation();

        //Then set the new target flag
        newTargetLocation = true;
          doStepperMove();
    }

    //Read the Tail button input
    if (digitalRead(5) == LOW)
    {
        buttonPushedTail = true;
    }
    else if (buttonPushedTail)
    {
        //Button was pushed, but is not being pushed now
        //Clear pushed variable
        buttonPushedTail = false;
        //Set the target head variable
        tableTargetHead = false;
        //Read the analog location
        tableTargetPosition = readAnalogLocation();

        //Then set the new target flag
        newTargetLocation = true;
          doStepperMove();  
    }

}

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////


//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
//
// Setup
//
void setup()
{
    Serial.begin(9600);

    AFMStop.begin(); // Start the shield

        //servo brake release before stepp moves
        brakeservo.attach(10);  // attaches the servo on pin 10 to the servo object
        brakeservo.write(servoRelease);
        delay (30);

    //configure pin3 as an input and enable the internal pull-up resistor
    pinMode(3, INPUT_PULLUP);        //Hall Effect sensor: to reset position on startup
    //configure pin4 as an input and enable the internal pull-up resistor
    pinMode(4, INPUT_PULLUP);        //Head button
    //configure pin5 as an input and enable the internal pull-up resistor
    pinMode(5, INPUT_PULLUP);        //Tail button
    //configure pin6 as an input and enable the internal pull-up resistor
    pinMode(6, INPUT_PULLUP);        //Programming: Move forward single step
    //configure pin7 as an input and enable the internal pull-up resistor
    pinMode(7, INPUT_PULLUP);        //Programming: Move reverse single step

    //read the sensoron Dig I/O #3 (open collector type- Hall Effect sensor) value into a variable
    int sensorVal = digitalRead(3);

    //set stepper motor speed and acceleration
    stepper2.setMaxSpeed(80.0);
    stepper2.setAcceleration(10.0);

    // if near reference point move away
    while (sensorVal == LOW) {
        sensorVal = digitalRead(3);
        forwardstep2();
        delay(25);
        Serial.println("Stepper Home");
    }

    // step backward to sensor index point
    while (sensorVal == HIGH) {
        sensorVal = digitalRead(3);
        backwardstep2();
        delay(50);
    }

        //when home- sets brake
        delay (500);
        brakeservo.write(servoBrake);
    

   DCC.SetBasicAccessoryDecoderPacketHandler(BasicAccDecoderPacket_Handler, true);
   ConfigureDecoder();
   DCC.SetupDecoder( 0x00, 0x00, kDCC_INTERRUPT );
}

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// Main loop
//
void loop()
{
   static int addr = 0;
    
        ////////////////////////////////////////////////////////////////
        // Loop DCC library
    DCC.loop();
    
        ////////////////////////////////////////////////////////////////
        // Bump to next address to test
    if( ++addr >= (int)(sizeof(gAddresses)/sizeof(gAddresses[0])) )
    {
        addr = 0;
    }

    //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    //  To configure the POT (uncomment this text)
    //configure alalog pin 1 as an input
    
        //  int potDisp = analogRead(1);
    //
    //  int potOut = potDisp / 79; //1023 / 13
    //     delay(1000);  
    //     Serial.print("Analog Location: ");
    //     Serial.println(potOut);
    
        // end of POT configuration- re-comment this section to hide during normal use
    //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~


    //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    //  To configure the Servo Brake (uncomment this text)
    //configure alalog pin 1 as an input
    
        //    int potDisp = analogRead(1);            // reads the value of the potentiometer (value between 0 and 1023)
        //    int potOut = map(potDisp, 0, 1023, 0, 50);     // scale it to use it with the servo (value between 0 and 180)
        //    brakeservo.write(potOut);                  // sets the servo position according to the scaled value
        //    delay(1000);
    //    Serial.print("Servo Position Value: ");
    //    Serial.println(potOut);
        
    // end of POT configuration- re-comment this section to hide during normal use
    //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    //Tests to see if the manual move buttons are pressed, if so set the needed flags
          checkManualButtons();

    //Checks the programming buttons, if depressed then set the right flags
          checkProgrammingButtons();

    //StepperAccel.Run() in this function
          stepperTimer();  
}

And the File:

**EDITED 5-4-14  REVISED FILE**

DCC_Turntable_Rev_15e.ino

 

I hope to have rev 14 out soon with the changes.  Thank for the support, this has been a fun project.

 

-Eric

Edited by Estreetcar
  • Like 1
Link to post
Share on other sites

 

having the unit take the shortest route to the track instead of all the way around.

I can't remember, but I guess you have code to work out the number of steps to get from (say) C to F. All that would seem necessay would be calculate the number from C to F in both directions and use the smaller number and its direction.

 

It may even be as simple as deciding if there are fewer track positions (counting clockwise) between C and F or between F and C.

 

...R

Link to post
Share on other sites

I can't remember, but I guess you have code to work out the number of steps to get from (say) C to F. All that would seem necessay would be calculate the number from C to F in both directions and use the smaller number and its direction.

 

It may even be as simple as deciding if there are fewer track positions (counting clockwise) between C and F or between F and C.

 

...R

 

All-

 

I worked with my friend this weekend on the code and now I have something to really post.  I would be comfortable calling this the "first released" version.  The only thing left to work out is the "display mode" constant slow rotation, but that isn't needed for the functionality, so its DONE!

 

I hope that someone else can try the code with their stepper set-up and let me know of any other problems.  I should have this mounted up and begin some live testing this week.  I will take some video to post as well.

 

Thanks for all the help and suggestions along the way.  I have amended the post #141 to include the newest software and code.

 

Enjoy,

-Eric

Link to post
Share on other sites

All-

 

I see that one person has downloaded the program.  Has anyone else tried the code to see if I missed anything?  The glue is drying on the motor mount.  I should be able to try it over the weekend.  As a side project I am working on a Raspberry-Pi running JMRI headless to act as the web-server for my layout.  It will have the Panel from the post above auto loaded.  I will be able to use my tablet or my phone (running Engine Driver and Wi-Throttle (respectively).  It works great running off my laptop.  I just wanted a more hands off option running in the background.  I wonder if anyone else has had success with that?

 

Thanks.

-Eric

Link to post
Share on other sites

Eric

 

I have not yet had a chance to try your code.

 

I am interested by your pi-server approach and JMRI but have a) no experience of it, b) no time and c) no need at the moment!

 

My tendency would be to have signalling separate from driving, but there is clearly an option to have an automated fiddle yard - I think there was a Peter Denny article in RM, years ago entitled "An automated Crispin" in which some bells and whistles and some clever woodwork replaced his son in the operation of his FY, so the concept is not new, but certainly interesting.

 

Do, please keep going, and keep the posts coming.

Link to post
Share on other sites

  • 4 months later...

All-

 

I see that one person has downloaded the program.  Has anyone else tried the code to see if I missed anything?  The glue is drying on the motor mount.  I should be able to try it over the weekend.  As a side project I am working on a Raspberry-Pi running JMRI headless to act as the web-server for my layout.  It will have the Panel from the post above auto loaded.  I will be able to use my tablet or my phone (running Engine Driver and Wi-Throttle (respectively).  It works great running off my laptop.  I just wanted a more hands off option running in the background.  I wonder if anyone else has had success with that?

 

Thanks.

-Eric

I have not yet downloaded the firmware.  I plan on building this module in the near future.  I hope you are able to continue to post changes here and I know we all appreciate the work you put in.

Link to post
Share on other sites

I have not yet downloaded the firmware.  I plan on building this module in the near future.  I hope you are able to continue to post changes here and I know we all appreciate the work you put in.

 

All-

 

This summer has taken me away from my modeling interests.  Now that the busy summer season is over, I plan on getting back into my hobbies for winter.  I did make some major changes to the turntable drive this summer.  I was unhappy with the performance of the drive when turning large heavy locomotives (+8 pounds) using the direct drive.  I looked at either putting in a larger stepper motor or adding in a gear reduction.  I decided on the gear reduction.  I revised the drive to include a 50:1 gear reduction. I remade the laser cut drive box and should get everything re-assembled within a few weeks.  I also added two more tracks to the turntable, so now it is a 14 road turntable.  The software has evolved a little to account for the larger (50x more) steps per revolutions.  See attached for the new gear box design and a photo of the new gears. 

 

I hope to post soon with a revision to the code and pictures of the progress.  When finished this should be suitable for O-scale turntables up to 32.5" diameter (O-scale 130ft).

 

TT- Attachment- actual size 9-3-14.pdf

 

post-22354-0-13063100-1412187902_thumb.jpg

 

-Eric

Link to post
Share on other sites

  • 4 weeks later...

All-

 

This summer has taken me away from my modeling interests.  Now that the busy summer season is over, I plan on getting back into my hobbies for winter.  I did make some major changes to the turntable drive this summer.  I was unhappy with the performance of the drive when turning large heavy locomotives (+8 pounds) using the direct drive.  I looked at either putting in a larger stepper motor or adding in a gear reduction.  I decided on the gear reduction.  I revised the drive to include a 50:1 gear reduction. I remade the laser cut drive box and should get everything re-assembled within a few weeks.  I also added two more tracks to the turntable, so now it is a 14 road turntable.  The software has evolved a little to account for the larger (50x more) steps per revolutions.  See attached for the new gear box design and a photo of the new gears. 

 

I hope to post soon with a revision to the code and pictures of the progress.  When finished this should be suitable for O-scale turntables up to 32.5" diameter (O-scale 130ft).

-Eric

 

All-

 

That took a lot longer than I thought to get to this point.  Amazing how life and work always get in the way of hobbies.  My first post was on March 19th and for only 7 months of very off and on work on this I think I have made it quite far.  I still want to tweak the code a little, but I have something that is very workable.  I am building a second version for the HO scale train club that I belong to.  I will update those pictures and code as I progress. 

 

Below is the success I has last night- It works very well!  Able to smoothly turn a large On30 locomotive (about 8 pounds) and line up perfectly each time. 

post-22354-0-62383600-1414355882.jpg

 

I was able to attach the 50:1 gear box that I made out of plywood using a friends laser cutter.  The gears came from Diamond Scale(I just called them directly).  The gearbox has adjustments for gear lash and to gear mesh to really get a smooth running machine.  I am very happy with the results. 

post-22354-0-62166200-1414355914.jpg

post-22354-0-21543600-1414355923.jpg

post-22354-0-67399000-1414355933.jpg

post-22354-0-52669500-1414355943.jpg

post-22354-0-88014600-1414355952.jpg

 

I have attached the final version of the CAD files as well as a PDF of them. 

TT- Attachment- actual size- final 9-3-14.dwg

TT- Attachment- actual size 9-3-14.pdf

 

I will take some video of the table once I put it back in the layout (it is designed to be semi-removable). 

 

Any questions, just ask.

-Eric

Portland, OR. USA

  • Like 2
Link to post
Share on other sites

All-

 

I see that one person has downloaded the program.  Has anyone else tried the code to see if I missed anything?  The glue is drying on the motor mount.  I should be able to try it over the weekend.  As a side project I am working on a Raspberry-Pi running JMRI headless to act as the web-server for my layout.  It will have the Panel from the post above auto loaded.  I will be able to use my tablet or my phone (running Engine Driver and Wi-Throttle (respectively).  It works great running off my laptop.  I just wanted a more hands off option running in the background.  I wonder if anyone else has had success with that?

 

Thanks.

-Eric

 

Hi Eric,

 

I've not had time until this week to catch up on RMWeb, however I'm interested in your approach as I am also planning on using an RPi (actually a Banana-Pi which has more RAM and a fast processor than the Raspberry) to run my layout eventually.

 

I wrote a script some time back called JMR-Pi which may help you with configuring your setup.

 

The code is freely available at https://github.com/proffalken/jmr-pi and I'd welcome any improvements :)

 

Cheers,

 

Matt

Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
 Share

×
×
  • Create New...