Jump to content

DCC Controlled (PECO) Turntable Project using a Arduino Uno


Recommended Posts

Hi Ray,

 

This is a truly brilliant and informative thread, many thanks for taking the time to share your knowledge and experiences. On the strength of this thread, I've gone out and treated myself to an Adruino Uno, Adafruit V2 and the same stepper motor as yourself. The other bits I'll hoover up off the internet once I've finished writing this message!

 

I'm looking to power a sector plate using a very similar set up as you have used for the turntable. The total travel required is 120 degree and will not need to stop at intermediate points. Ideally, I would like to be able to control this from one turnout address on my Roco Multimaus (So if I throw the point on the Multimaus, it will travel 120 degees clockwise from position A to position B. When the close the same point on the Multimaus it will travel 120 degree anticlockwise from position B to position A). I'm presuming that the coding shown in Part 6 could be tweaked to accommodate this?

 

To keep things simple, if I presume that the reference point (the location of the Hall Effect Sensor) is mid way through the 120 degree of travel (So position A is 60 degrees counter clockwise of this point and position B is 60 degrees clockwise from it) my guess is I would have to alter the 400 and 2000 values (around mid way through the script) to something like 533 and 2667 if there is 3200 microsteps per rotation? Is there an element of the script that dictates whether the stepper motor travels clockwise or anticlockwise? Sorry if I've got this wrong, I've never even thought of programming before!  

 

One other concern, is that the swing of the sector plate will be physically limited to a travel of 120 degrees. In other words, if the stepper motor was to try and complete a full turn it'd smash through the walls of the sector plate well! I can remove the walls for the initial set up (as detailed in part 5) but in service is there any requirement for the stepper motor to be able to travel a full rotation?

 

Again, many thanks for writing this up and apologies if my questions are a little basic!

 

Thanks,

 

Steve

Hi Steve,

Currently the software as written finds the reference point by turning in one direction so if the table is just past it will complete a 360 degree turn to find it. That part of the code will need rewriting, I'll have to think about that one. Other than that the code in part 6 should be ok, you just need to work out the steps for the 120 degree turn.

 

Ray.

Link to post
Share on other sites

Robin-

 

Thanks for the help.  I have fixed the previous posting and also included the arduino *.ino type file as an attachment.

 

-Eric

Why not have a selector switch connected to a number of I/O inputs instead of a potentiometer?
Link to post
Share on other sites

Why not have a selector switch connected to a number of I/O inputs instead of a potentiometer?

No reason really.  But I do have 12 tracks (13 if you include the "0" track) and I had a 10k pot on hand.  I would need to multi-plex the inputs to fit them all into the arduino... so, I thought just an analog signal would work.  I was able to recognize the position of the Pot reliably, I just don't know how to write the code to make it do anything useful.

 

This bit of code recognizes the input and outputs an integer 0~12 based on position of the knob.

 //configure alalog pin 1 as an input
        int potDisp = 1;
        int reading = analogRead(potDisp);
        int analogout = reading / 79; //1023 / 13

The value is displayed when you make a DCC address selection (as part of that routine).  It is not ideal, but I wanted to see if I could read the value before I tried to make it perform work.

   Serial.print("Analog Location: ");
          Serial.println(analogout);

I am building two of these set-ups. One for home and one for the club.  At the club the dial and push button will be easier for members to work with.  The club layout has a roundhouse with 13 roads.  My plan is to get mine working perfectly at home, then tackle the retrofit of the club layout.  I purchased all these parts a few months ago on a whim, then found your post about halfway through the design.  I really like the code you have written.  It works!  I did use a bigger stepper motor because I had it from another project. 

 

-Eric

 

 

  

Link to post
Share on other sites

Hi Steve,

Currently the software as written finds the reference point by turning in one direction so if the table is just past it will complete a 360 degree turn to find it. That part of the code will need rewriting, I'll have to think about that one. Other than that the code in part 6 should be ok, you just need to work out the steps for the 120 degree turn.

 

Ray.

 

Hi Ray,

 

Thanks for the reply. Could one solution to be to place the sensor a couple of degrees beyond one of the normal, operating limits of travel so that upon start up the table has to move in the same direction to find it? In other words, if in normal operation the table travels 120 degrees clockwise from Point A to Point B and 120 degrees, counter-clockwise from Point B to Point A, the table will always be between these points at shut down. If the sensor way placed at (say) 5 degrees anticlockwise from Point A and the table travelled anti-clockwise as part of the start up routine, it would always find the sensor before reaching the physical limit of travel. I hope that makes sense, if not I'll try to sketch it out! 

 

Thanks again

 

Steve

Link to post
Share on other sites

Hi Ray,

 

Thanks for the reply. Could one solution to be to place the sensor a couple of degrees beyond one of the normal, operating limits of travel so that upon start up the table has to move in the same direction to find it? In other words, if in normal operation the table travels 120 degrees clockwise from Point A to Point B and 120 degrees, counter-clockwise from Point B to Point A, the table will always be between these points at shut down. If the sensor way placed at (say) 5 degrees anticlockwise from Point A and the table travelled anti-clockwise as part of the start up routine, it would always find the sensor before reaching the physical limit of travel. I hope that makes sense, if not I'll try to sketch it out!

 

Thanks again

 

Steve

Yep, that should work, I was assuming that you had a physical limit that stopped it going beyond points A and B but if you can go a few degrees past putting the sensor there will work fine.

Ray.

 

Edit, so if your reference point is zero, point A (+5 degrees) will be position 45 and point B will be position 1110 (+125 degrees) roughly.

Edited by tender
Link to post
Share on other sites

@estreetcar, once you can resolve the pot reading into position values (0-12) it should be simple to get the rest of the code to move to the appropriate location. I wouldn't try to use the pot to get the actual degrees - just the number of the position you want to get to. Then, elsewhere, you can have the actual degrees that correspond to each position. That way you can have two separate simple processes.

 

@pixie, if there is a possibility of the turntable going too far and causing damage I would put a limit switch (perhaps a microswitch) at each end of the travel - to be sure to be sure. On the other hand if the only problem is that the turntable will stall the motor and miss steps (it won't harm the motor) I would just have a routine that re-establishes position by moving to the "home" detector.

 

...R

Link to post
Share on other sites

Hi all,

 

The <> icon ain't there when I edit posts on my ipad, but it is on my laptop.

 

Andy has modified permissions so we can attach .ino files to posts - this does make downloading easy, but remember to put the file in a folder with the same name.

 

@ Pixie - stepper will surely work but might be overkill - a DC drive, perhaps by threaded rod, with limit switches, might be simpler to build and adjust, and may be cheaper too? EDIT. Perhaps a servo might work - not sure about accuracy & repeatability though.

 

@ gcodori - steppers with gears are great, but as noted by Robin, backlash is an issue which needs to be addressed. There are two options as far as I am aware - always approach from the same direction, either by only going one way (not good on traversers...) or by overshooting and coming back, or alternatively, build a suitable constant into the software so it compensates. Effectively these are the same if the amount of overshoot is actually zero. See t/t program attached to the Arduino thread in my signature below for examples.

 

Best

Simon

Edited by Simond
Link to post
Share on other sites

@estreetcar, once you can resolve the pot reading into position values (0-12) it should be simple to get the rest of the code to move to the appropriate location. I wouldn't try to use the pot to get the actual degrees - just the number of the position you want to get to. Then, elsewhere, you can have the actual degrees that correspond to each position. That way you can have two separate simple processes.

 

@pixie, if there is a possibility of the turntable going too far and causing damage I would put a limit switch (perhaps a microswitch) at each end of the travel - to be sure to be sure. On the other hand if the only problem is that the turntable will stall the motor and miss steps (it won't harm the motor) I would just have a routine that re-establishes position by moving to the "home" detector.

 

...R

Robin-

 

I agree completely.  That is what I was thinking.  I have resolved the pot reading into 0-12.  I just don't know how or where to get that output value into a working function so that it does something.  I searched for example code and haven't found anything. Would it be in another expression similar to the DCC code?  If so,what would that look like?  I don't think I should have the push buttons in the "loop" function, but I found that was where they worked (by trial and error).  I would think I should refer to something in the loop and then make a new function to call the pot value, look for push button and then execute the move of the stepper.  What would that syntax look like? 

 

It isn't code but the logic stream to me would be like this:

 

In the loop:

      Look at state of the DCC code (as is), add looking at the state of the two push buttons..

      If DCC code is within address range => run dcc function

      If Either push button goes LOW => run Push button function

 

The functions:

     DCC function- as is, {maybe make position a global variable defined at the beginning so the values can be referenced from either function (DCC or Button)}

     Push Button function- copy of DCC function with analog value in place of address.

Thanks

-Eric

Link to post
Share on other sites

Robin-

 

I agree completely.  That is what I was thinking.  I have resolved the pot reading into 0-12.  I just don't know how or where to get that output value into a working function so that it does something.  I searched for example code and haven't found anything. Would it be in another expression similar to the DCC code?  If so,what would that look like?  I don't think I should have the push buttons in the "loop" function, but I found that was where they worked (by trial and error).  I would think I should refer to something in the loop and then make a new function to call the pot value, look for push button and then execute the move of the stepper.  What would that syntax look like? 

 

It isn't code but the logic stream to me would be like this:

 

In the loop:

      Look at state of the DCC code (as is), add looking at the state of the two push buttons..

      If DCC code is within address range => run dcc function

      If Either push button goes LOW => run Push button function

 

The functions:

     DCC function- as is, {maybe make position a global variable defined at the beginning so the values can be referenced from either function (DCC or Button)}

     Push Button function- copy of DCC function with analog value in place of address.

Thanks

-Eric

 

If you've resolved the pot reading to an integer value between 0-12 you can probably use a routine similar to the 'switch/case' function as in the Basic accessory packet handler routine. 

Link to post
Share on other sites

If you've resolved the pot reading to an integer value between 0-12 you can probably use a routine similar to the 'switch/case' function as in the Basic accessory packet handler routine. 

 

I don't know enough about the code to give better advice than this. (There is a lot of code !)

 

...R

Link to post
Share on other sites

@estreetcar, it is more usual to put the

pinMode(5, INPUT_PULLUP);
statements into setup() so they are only called once rather than hundreds or thousands of times per second. (And see what putting the code into code tags - the <> icon - does).

 

@gcodori, stepper motors can easily work with gears. The problem is that any backlash in the gear train will make the alignment less precise. There are various ways to deal with that.

 

...R

I asked this question because I've seen stepper motors with the builtin reduction gears. They are 18 degree steps with 100:1 which would yield 2000 steps per revolution. 20x100.

 

This wouldn't be a home built reduction system but metal gears held in a case attached to the front of the motor. This would keep the slop in check.

Link to post
Share on other sites

I don't know enough about the code to give better advice than this. (There is a lot of code !)

 

...R

All-

 

Ok, I had some time today.  I got the analog inputs working.  I could really use help  on the "release" function. and the continuous rotation for Route 200.

 

Thanks again for a starting point for the code.

 

Below is the code as well as a download file for those that prefer it that way.  I am sure I will try and get it a little cleaner, but it works.... so I am happy.

Any suggestions for consolidation would be appreciated.

 

DCC_Turntable_Rev_3.ino

 

-Eric

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

////////////////////////////////////////////////////////////////////////////////
//
//   DCC Turntable Rev_3 Mar_21_2014
//        based on code from rmweb.co.uk forum
//          edited by E.Sitiko
//     This is a work in progress, I need some help with programming
//        Changes desired are:
//            12 road turntable  (works)
//            DCC addresses 200 to 212  (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)
//
//  (future)    Address 200= continuous slow rotation, based on "Head" or "Tail"
//  (future)    A "Release" function, universal to operation, that would release power
//              to the table after sitting idle for 2-3 min.  Right now, you just call up
//              the same function again and it will "release"
//
////////////////////////////////////////////////////////////////////////////////

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

#include <DCC_Decoder.h>
#include <AccelStepper.h>
#include <Wire.h>
#include <Adafruit_MotorShield.h>
#include "utility/Adafruit_PWMServoDriver.h"

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

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

DCCAccessoryAddress gAddresses[13];

                               //configure alalog pin 1 as an input
        int analogPin = 1;     // potentiometer wiper (middle terminal) connected to analog pin 1
                               // outside leads to ground and +5V

        int potDisp = 0;           // variable to store the value read
        int potOut = 0;
        int sensorOut = 0;
        
        int delayvalue = 500;      // makes default delay value
        boolean AnalogOut = false;
                
       int sensorHead = digitalRead(4);
       int sensorTail = digitalRead(5);
      
      //location variables
        int A = 150;          //TT Track 0-  Head
        int B = 45;           //TT Track 1-  Head
        int C = 205;          //TT Track 2-  Head
        int D = 490;          //TT Track 3-  Head
        int E = 588;          //TT Track 4-  Head
        int F = 765;          //TT Track 5-  Head
        int G = 1939;         //TT Track 6-  Head
        int H = 2072;         //TT Track 7-  Head
        int I = 2205;         //TT Track 8-  Head
        int J=  2499;         //TT Track 9-  Head
        int K = 2623;         //TT Track 10- Head
        int L = 2739;         //TT Track 11- Head
        int M = 2872;         //TT Track 12- Head
        int N = 1750;         //TT Track 0-  Tail
        int O = 1645;         //TT Track 1-  Tail
        int P = 1805;         //TT Track 2-  Tail
        int Q = 2090;         //TT Track 3-  Tail
        int R = 2188;         //TT Track 4-  Tail
        int S = 2365;         //TT Track 5-  Tail
        int T = 339;          //TT Track 6-  Tail
        int U = 472;          //TT Track 7-  Tail
        int V = 605;          //TT Track 8-  Tail
        int W = 899;          //TT Track 9-  Tail
        int X = 1023;         //TT Track 10- Tail
        int Y = 1139;         //TT Track 11- Tail
        int Z = 1272;         //TT Track 12- Tail
        
                      int TrackPos[13]={A,B,C,D,E,F,G,H,I,J,K,L,M};
                      int TrackNeg[13]={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(forwardstep2, backwardstep2);

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// Decoder Init
//
void ConfigureDecoder()
{
    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)
{
   
   int delayvalue = 500;
           
        // 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.print("Basic addr: ");
            Serial.print(address,DEC);
            Serial.print("   activate: ");
            Serial.println(enable,DEC);
                
            if( enable )
            {
                switch (i) {
                  case 0:
                    stepper2.moveTo(A);
                       delay(delayvalue);
                       //release2();
                  break;
                  case 1:
                    stepper2.moveTo(B);
                       delay(delayvalue);
                       release2();
                  break;
                  case 2:
                    stepper2.moveTo(C);
                       delay(delayvalue);
                       release2();                  
                  break;
                  case 3:
                    stepper2.moveTo(D);
                       delay(delayvalue);
                       release2();
                  break;
                  case 4:
                    stepper2.moveTo(E);
                       delay(delayvalue);
                       release2();
                  break;
                  case 5:
                    stepper2.moveTo(F);
                       delay(delayvalue);
                       release2();
                  break;
                  case 6:
                    stepper2.moveTo(G);
                       delay(delayvalue);
                       release2();
                  break;
                  case 7:
                    stepper2.moveTo(H);
                       delay(delayvalue);
                       release2();
                  break;
                  case 8:
                    stepper2.moveTo(I);
                       delay(delayvalue);
                       release2();
                  break;
                  case 9:
                    stepper2.moveTo(J);
                       delay(delayvalue);
                       release2();
                  break;
                  case 10:
                    stepper2.moveTo(K);
                       delay(delayvalue);
                       release2();
                  break;
                  case 11:
                    stepper2.moveTo(L);
                       delay(delayvalue);
                       release2();
                  break;
                  case 12:
                    stepper2.moveTo(M);
                       delay(delayvalue);
                       release2();
                  break;
                 }
            }else{
                switch (i) {
                  case 0:
                    stepper2.moveTo(N);
                       delay(delayvalue);
                       release2();
                  break;
                  case 1:
                    stepper2.moveTo(O);
                       delay(delayvalue);
                       release2();
                  break;
                  case 2:
                    stepper2.moveTo(P);
                       delay(delayvalue);
                       release2();
                  break;
                  case 3:
                    stepper2.moveTo(Q);
                       delay(delayvalue);
                       release2();
                  break;
                  case 4:
                    stepper2.moveTo(R);
                       delay(delayvalue);
                       release2();
                  break;
                  case 5:
                    stepper2.moveTo(S);
                       delay(delayvalue);
                       release2();
                  break;
                  case 6:
                    stepper2.moveTo(T);
                       delay(delayvalue);
                       release2();
                  break;
                  case 7:
                    stepper2.moveTo(U);
                       delay(delayvalue);
                       release2();
                  break;
                  case 8:
                    stepper2.moveTo(V);
                       delay(delayvalue);
                       release2();
                  break;
                  case 9:
                    stepper2.moveTo(W);
                       delay(delayvalue);
                       release2();
                  break;
                  case 10:
                    stepper2.moveTo(X);
                       delay(delayvalue);
                       release2();
                  break;
                  case 11:
                    stepper2.moveTo(Y);
                       delay(delayvalue);
                       release2();
                  break;
                  case 12:
                    stepper2.moveTo(Z);
                       delay(delayvalue);
                       release2();
                  break;
                }
            }
        }
      }
    }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
//
// Trial Analog In sub-routine
//
///////////////////////////////////////


void analog()
//(int potOut, boolean HeadOut, boolean TailOut)
{

 
       int sensorHead = digitalRead(4);
       int sensorTail = digitalRead(5);
       
   while (sensorHead == LOW) {
   sensorHead = digitalRead(4);
    delay(100);
         if (sensorHead == 0);
         {AnalogOut = 1;}}
   
   while (sensorTail == LOW) {
    sensorTail = digitalRead(5);
    delay(100);
      if (sensorTail == 0);
     {AnalogOut = 0;}}
   
       //configure alalog pin 1 as an input
       int potDisp = analogRead(1);
      
       int potOut = potDisp / 79; //1023 / 13
       
          Serial.print("Analog Location: ");
          Serial.println(potOut);
             Serial.print("Track #: ");
            Serial.print(potOut,DEC);
            Serial.print("   activate: ");
            Serial.println(AnalogOut,DEC);
                        
                
            if(AnalogOut == 1)
            {
                switch (potOut) {
                  case 0:
                    stepper2.moveTo(A);
                       delay(delayvalue);
                       //release2();
                  break;
                  case 1:
                    stepper2.moveTo(B);
                       delay(delayvalue);
                       release2();
                  break;
                  case 2:
                    stepper2.moveTo(C);
                       delay(delayvalue);
                       release2();                  
                  break;
                  case 3:
                    stepper2.moveTo(D);
                       delay(delayvalue);
                       release2();
                  break;
                  case 4:
                    stepper2.moveTo(E);
                       delay(delayvalue);
                       release2();
                  break;
                  case 5:
                    stepper2.moveTo(F);
                       delay(delayvalue);
                       release2();
                  break;
                  case 6:
                    stepper2.moveTo(G);
                       delay(delayvalue);
                       release2();
                  break;
                  case 7:
                    stepper2.moveTo(H);
                       delay(delayvalue);
                       release2();
                  break;
                  case 8:
                    stepper2.moveTo(I);
                       delay(delayvalue);
                       release2();
                  break;
                  case 9:
                    stepper2.moveTo(J);
                       delay(delayvalue);
                       release2();
                  break;
                  case 10:
                    stepper2.moveTo(K);
                       delay(delayvalue);
                       release2();
                  break;
                  case 11:
                    stepper2.moveTo(L);
                       delay(delayvalue);
                       release2();
                  break;
                  case 12:
                    stepper2.moveTo(M);
                       delay(delayvalue);
                       release2();
                  break;  
                 }
            
            }else
            {
                  switch (potOut) {
                  case 0:
                    stepper2.moveTo(N);
                       delay(delayvalue);
                       release2();
                  break;
                  case 1:
                    stepper2.moveTo(O);
                       delay(delayvalue);
                       release2();
                  break;
                  case 2:
                    stepper2.moveTo(P);
                       delay(delayvalue);
                       release2();
                  break;
                  case 3:
                    stepper2.moveTo(Q);
                       delay(delayvalue);
                       release2();
                  break;
                  case 4:
                    stepper2.moveTo(R);
                       delay(delayvalue);
                       release2();
                  break;
                  case 5:
                    stepper2.moveTo(S);
                       delay(delayvalue);
                       release2();
                  break;
                  case 6:
                    stepper2.moveTo(T);
                       delay(delayvalue);
                       release2();
                  break;
                  case 7:
                    stepper2.moveTo(U);
                       delay(delayvalue);
                       release2();
                  break;
                  case 8:
                    stepper2.moveTo(V);
                       delay(delayvalue);
                       release2();
                  break;
                  case 9:
                    stepper2.moveTo(W);
                       delay(delayvalue);
                       release2();
                  break;
                  case 10:
                    stepper2.moveTo(X);
                       delay(delayvalue);
                       release2();
                  break;
                  case 11:
                    stepper2.moveTo(Y);
                       delay(delayvalue);
                       release2();
                  break;
                  case 12:
                    stepper2.moveTo(Z);
                       delay(delayvalue);
                       release2();
                  break;
                }
              }
            }
            


//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
//
// Setup
//
void setup()
{
   Serial.begin(9600);
   
   AFMStop.begin(); // Start the shield

  //configure pin3 as an input and enable the internal pull-up resistor
  pinMode(3, INPUT_PULLUP);
    //configure pin4 as an input and enable the internal pull-up resistor
  pinMode(4, INPUT_PULLUP);
  //configure pin5 as an input and enable the internal pull-up resistor
  pinMode(5, INPUT_PULLUP);
 
  //read the sensoron Dig I/O #3 (open collector type) 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(50);
      Serial.println("Stepper Home");
 }
 
// step backrward to sensor index point
  while (sensorVal == HIGH) {
    sensorVal = digitalRead(3);
    backwardstep2();
      delay(50);
  }

 
   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;
    }
    
    //The analog input section of the loop text
    //read the sensor (open collector type) value into a variable
  int sensorHead = digitalRead(4);
  //read the sensor (open collector type) value into a variable
  int sensorTail = digitalRead(5);
 
 
  while (sensorHead == LOW) {
   sensorHead = digitalRead(4);
    delay(100);
    Serial.println("Head Pressed");  
      analog();}
   
   while (sensorTail == LOW) {
    sensorTail = digitalRead(5);
    delay(100);
    Serial.println("Tail Pressed");  
      analog();}
      
    // Run the Stepper Motor //
    
    stepper2.run();
    

}

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

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.

 

These comments are presented as they occurred to me

 

1. If all of the "int A = 150;" were changed to "const int A = 150;" they will work the same but the compiler won't waste memory on them since they never change.

 

2. The way the stuff is readied for Accelstepper seems strangely complex, but I don't know enough about the Adafruit_MotorShield yo know if it is unavoidable.

 

3. "int TrackPos[13]={A,B ..." etc doesn't seem to be used anywhere. Try commenting it out and if that causes no problem you could delete it.

 

4. The long suite of SWITCH CASE statements is duplicated. They (one copy of them) should be moved to a function that can be called for use by the DCC commands and the POT commands. Duplication on this scale is a recipe for more coffee when you change one and not the other.

 

5. This is probably my most significant comment. The DCC library is a black box. We have no idea what DCC.loop() does except that it has been told to call BasicAccDecoderPacket_Handler (presumably whenever it gets a new address sent to it). While the only input was from the DCC device putting all the Switch/Case stuff into the ..._Handler was a reasonable approach. Now, however, when you want to use the DCC info as just one source of input I would shorten the ...Handler routine so it also produces a number from 0-12 just the same as your POT. Then your code can take charge of which source you wish to respond to. In other words, things won't move inappropriately just because a DCC command arrives.

 

Just my 2 cents.

 

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

 

...R

Edited by Robin2
Link to post
Share on other sites

I asked this question because I've seen stepper motors with the builtin reduction gears. They are 18 degree steps with 100:1 which would yield 2000 steps per revolution. 20x100.

This wouldn't be a home built reduction system but metal gears held in a case attached to the front of the motor. This would keep the slop in check.

 

Hi gcodori

 

Please see my post 114 et seq in Jeff's thread - shows the stepper I bought, and discusses backlash

 

http://www.rmweb.co.uk/community/index.php?/topic/76732-stepper-motor-turntable-drive/page-5

 

Hope this is helpful

Best

Simon

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.

 

These comments are presented as they occurred to me

 

1. If all of the "int A = 150;" were changed to "const int A = 150;" they will work the same but the compiler won't waste memory on them since they never change.

 

2. The way the stuff is readied for Accelstepper seems strangely complex, but I don't know enough about the Adafruit_MotorShield yo know if it is unavoidable.

 

3. "int TrackPos[13]={A,B ..." etc doesn't seem to be used anywhere. Try commenting it out and if that causes no problem you could delete it.

 

4. The long suite of SWITCH CASE statements is duplicated. They (one copy of them) should be moved to a function that can be called for use by the DCC commands and the POT commands. Duplication on this scale is a recipe for more coffee when you change one and not the other.

 

5. This is probably my most significant comment. The DCC library is a black box. We have no idea what DCC.loop() does except that it has been told to call BasicAccDecoderPacket_Handler (presumably whenever it gets a new address sent to it). While the only input was from the DCC device putting all the Switch/Case stuff into the ..._Handler was a reasonable approach. Now, however, when you want to use the DCC info as just one source of input I would shorten the ...Handler routine so it also produces a number from 0-12 just the same as your POT. Then your code can take charge of which source you wish to respond to. In other words, things won't move inappropriately just because a DCC command arrives.

 

Just my 2 cents.

 

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

 

...R

Robin-

 

Thanks for the help.  Your comments are exactly what I was hoping for!  I know just enough about this to make "coffee" as you put it.  I was able to get it working, but it isn't pretty.

 

I was able to make the changes in #1 & #2.  I made the "int TrackPos[13]={A,B,..." to try and simplify the SWITCH CASE logic even further, but it wouldn't let me put the function into that function.  I just forgot to comment it out.

 

I tried to simplify the "SWITCH CASE" duplicated code, but I don't know now to make the DCC output variable into a 0-12 output and also have the " i " variable activate the IF ELSE phrasing.  If you have an idea, I am definitely up for trying.

 

Where in this code below is the output variable that sets the "CASE #" ?

Where does the code produce the " i " variable for the IF/ELSE phrase?

    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.print("Basic addr: ");
            Serial.print(address,DEC);
            Serial.print("   activate: ");
            Serial.println(enable,DEC);
                
            if( enable )
            {
                switch (i) {
                  case 0:
                    stepper2.moveTo(A);
                       delay(delayvalue);
                       //release2();
                  break;

Once I figure that part out, I agree that it would very nice to have that call a "location" function with either the DCC or the Analog input.

 

Thanks for the help.

-Eric

Link to post
Share on other sites

Where in this code below is the output variable that sets the "CASE #" ?

 

The CASE# is decoded from the array of gAddresses with the 'if' statement

if( address == gAddresses.address )

 

Ie if the decoded address=205 then i=5

 

Where does the code produce the " i " variable for the IF/ELSE phrase?

 

In the line:

for(int i=0; i<(int)(sizeof(gAddresses)/sizeof(gAddresses[0])); i++)

 

Read

for i=0 to 12

..

..

..

Next i

 

Ray.

Edited by tender
Link to post
Share on other sites

The CASE# is decoded from the array of gAddresses with the 'if' statement

if( address == gAddresses.address )

 

Ie if the decoded address=205 then i=5

 

In the line:

for(int i=0; i<(int)(sizeof(gAddresses)/sizeof(gAddresses[0])); i++)

 

Read

for i=0 to 12

..

..

..

Next i

 

Ray.

Ray-

 

Thanks.  Ok, I think I can make that work.  What makes the table move to the IF or ELSE ?  If i= 0 to 12, what tells it which way to go?  I think the variable is "ENABLE",  but I am not sure what creates that variable.  I think it is either a 0 or a 1 as that is what I made in  my analog section.  With those two variables, I should be able to have either the DCC or the POT run the position function.

 

-Eric

Link to post
Share on other sites

Ray-

 

Thanks.  Ok, I think I can make that work.  What makes the table move to the IF or ELSE ?  If i= 0 to 12, what tells it which way to go?  I think the variable is "ENABLE",  but I am not sure what creates that variable.  I think it is either a 0 or a 1 as that is what I made in  my analog section.  With those two variables, I should be able to have either the DCC or the POT run the position function.

 

-Eric

Hi Eric,

That's correct,

The variable ENABLE is 0 or 1 depending on whether a 'straight' or 'thrown' command (data) is sent with the accessory decoder address.

 

boolean enable = (data & 0x01) ? 1 : 0;

 

Ray.

Link to post
Share on other sites

Hi Eric,

That's correct,

The variable ENABLE is 0 or 1 depending on whether a 'straight' or 'thrown' command (data) is sent with the accessory decoder address.

 

boolean enable = (data & 0x01) ? 1 : 0;

 

Ray.

 

 

Ray & Robin-

 

Thanks for the help this week.

I have completed my version 5 !  I now have a way to be on top of the layout looking closely at the tracks and adjust them with two push buttons.  The values that I changed from the recorded values are shown in the serial monitor.  Maybe someone could run with this and make it so that it doesn't move the "home" position... but for now there is a work around (in the code).  Here are the two bits of code...

 

 

The Program Sub-Routine:

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
//
// Programming Assit Tool-  
//          programming & track aligning tool-  lets you park the table at a position using DCC or Analog-
//          then press one of the programming buttons to adjust position- see sub-routine "programming"

//      PROBLEMS:  It works, mostly... but it adjusts the base value.  Basically when you push sensor 6 ot 7
//                 the table moves and then reads back how many the table moved.  +,-.  but the "home" position
//                 is moved by that same amount.  A bit of a pain.  Not sure how to just pull the stepper value
//                 from the motor shield library.  it says "private" when you try.  This way works, you just need
//                 to remember to reset the table after each time you adjust using the programming buttons.
//
//      Procedure: The good part is that you can be up top close to the tracks and with the push buttons to adjust the
//                 track to get a perfect fit.  Then just write down the number in the Serial Monitor and what track
//                 the adjustment was made from.  Then reset the table and do another track.  Remember to do both ends.
//
void programming()
{
       
       int sensorF_Step = digitalRead(6);
       int sensorR_Step = digitalRead(7);
              //atstep = stepper2.currentPosition(); //doesn't work... well,does work, but doesn't change to show the new location.
              
   while (sensorF_Step == LOW) {
   sensorF_Step = digitalRead(6);
    delay(500);
    atstep = ++atstep;
       forwardstep2();
   }
   
    while (sensorR_Step == LOW) {
    sensorR_Step = digitalRead(7);
    delay(500);
    atstep = --atstep;
        backwardstep2();}
   
       //configure alalog pin 1 as an input
       int potDisp = analogRead(1);
      
       int potOut = potDisp / 79; //1023 / 13
       
          Serial.print("Analog Location: ");
          Serial.println(potOut);
            
          Serial.print("Current Steps Added or Subtracted: ");
          Serial.println(atstep,DEC);
          Serial.println("");
             
}

The bit I added in "LOOP" to make it work.

//programming & track aligning tool-  lets you park the table at a position using DCC or Analog-
 //then press one of the programming buttons to adjust position- see sub-routine "programming"
 
 int sensorF_Step = digitalRead(6);   // Push button (N.O.)- for programming Forward one Micro-Step
 int sensorR_Step = digitalRead(7);   // Push button (N.O.)- for programming Reverse one Micro-Step
 
 while (sensorF_Step == LOW) {
   sensorF_Step = digitalRead(6);
    delay(200);
    Serial.println("Step Forward Pressed");  
       programming();}
   
   while (sensorR_Step == LOW) {
    sensorR_Step = digitalRead(7);
    delay(200);
    Serial.println("Step Backward Pressed");  
       programming();}

Again not the pretiest code in the world, but it works...

 

Here is the full code REV 5 for everyone.  Enjoy.  Thanks for the help.

 

DCC_Turntable_Rev_5.ino

 

-Eric

Link to post
Share on other sites

Hi Eric,

 

Great work you have done.

But if i wnt to compile all the sketches I get errors.

It seems that my system cant find the library in the utils directory.

Reading all the posts it looks that i am the only one having problems.

These are the first errors:

Arduino: 1.5.6-r2 (Windows 8 ), Board: "Arduino Uno"
Build options changed, rebuilding all
Using library DCC_Decoder in folder: D:\Gebruikers\harends\Arduino\libraries\DCC_Decoder (legacy)
Using library AccelStepper in folder: D:\Gebruikers\harends\Arduino\libraries\AccelStepper (legacy)
Using library Wire in folder: C:\Program Files (x86)\Arduino\hardware\arduino\avr\libraries\Wire (legacy)
Using library Adafruit_Motor_Shield_V2_Library in folder: D:\Gebruikers\harends\Arduino\libraries\Adafruit_Motor_Shield_V2_Library (legacy)

 

C:\Program Files (x86)\Arduino\hardware\tools\avr\bin\avr-g++ -c -g -Os -w -fno-exceptions -ffunction-sections -fdata-sections -MMD -mmcu=atmega328p -DF_CPU=16000000L -DARDUINO=156 -DARDUINO_AVR_UNO -DARDUINO_ARCH_AVR -IC:\Program Files (x86)\Arduino\hardware\arduino\avr\cores\arduino -IC:\Program Files (x86)\Arduino\hardware\arduino\avr\variants\standard -ID:\Gebruikers\harends\Arduino\libraries\DCC_Decoder -ID:\Gebruikers\harends\Arduino\libraries\AccelStepper -IC:\Program Files (x86)\Arduino\hardware\arduino\avr\libraries\Wire -ID:\Gebruikers\harends\Arduino\libraries\Adafruit_Motor_Shield_V2_Library C:\Users\HFA73~1.ARE\AppData\Local\Temp\build5269820185335347354.tmp\DCC_Turntable_Rev_5.cpp -o C:\Users\HFA73~1.ARE\AppData\Local\Temp\build5269820185335347354.tmp\DCC_Turntable_Rev_5.cpp.o

DCC_Turntable_Rev_5.ino:35:45: error: utility/Adafruit_PWMServoDriver.h: No such file or directory
DCC_Turntable_Rev_5.ino:113: error: 'Adafruit_MotorShield' does not name a type
DCC_Turntable_Rev_5.ino:118: error: expected constructor, destructor, or type conversion before '*' token

As other sketches i have written are compiling correct I asume that the error lies somewhere in between :-)

 

Harry

Link to post
Share on other sites

Ray & Robin-

 

Thanks for the help this week.

 

...

 

Again not the pretiest code in the world, but it works...

 

Here is the full code REV 5 for everyone.  Enjoy.  Thanks for the help.

 

attachicon.gifDCC_Turntable_Rev_5.ino

 

-Eric

 

All-

 

I did forget to mention- The Rev 5 code did clean up all that Ray and Robin helped me with in terms of getting the DCC and Analog inputs to both reference the same position table with all the Switch/ Case clauses.  Now there is just one table.  I tried a bunch of scenarios yesterday and they all worked.  Functionally the code is working very well, however it is still a little messy.

 

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.)

2: "show mode" for DCC input 200. (continuous slow rotation).

3. Finish wrapping up the programming tool to see about making it even better.  It is ok now. (made it super easy to line up the tracks)

 

Any help would greatly be appreciated.

 

See below Pictures of my turntable module in progress:  I still need to finish the buildings and finish the detail of the TT bridge.  (Scale is On30).

 

post-22354-0-07589200-1395675816.jpg

post-22354-0-85014600-1395675816.jpg

post-22354-0-45622300-1395675817.jpg

 

The turntable is a CMR (Custom Model railroads- HO scale 105' turntable) with a new deck to make it O scale. 

http://www.custommodelrailroads.com/turntables-3.aspx

 

Showing in progress- handlaid track, modifications to the turntable are easier to see without paint.

post-22354-0-96430800-1395676823.jpg

 

Painted track, but you can see the modified Diamond Scale certer piece.

post-22354-0-69221800-1395676824.jpg

 

Underside- showing DCC Circuit breaker, and Auto-Reverser.  The motor (now removed) and the main drive shaft.

post-22354-0-51993000-1395676825.jpg

 

The mechanism was a diamond scale turntable mechanism with two bearings and a slow motion motor driving a worm shaft to a main gear.  The backlash was horrible.  The new stepper just drives straight to the main shaft.

I will post some youtube videos shortly.

 

 

Thanks for the great start.

-Eric

Link to post
Share on other sites

Hi Eric.

Thanks for sharing your additions the the turntable project. I've not been able to test any of these out as yet as my PC has had an OS upgrade and i need to reinstall all the Arduino stuff. Just need to find the time to do it.

 

Now that you can manually align the position of the deck the next step would be to store the resulting position(s) in non volatile memory that could be recalled at start-up for your 'location variables'.

This would make the system 'programmable' and more akin to a commercial turntable controller.

 

This has been on my to-do list but I don't know when I'm going to get the chance to do it.

 

Ray.

Link to post
Share on other sites

Hi Eric,

 

Great work you have done.

But if i wnt to compile all the sketches I get errors.

It seems that my system cant find the library in the utils directory.

Reading all the posts it looks that i am the only one having problems.

These are the first errors:

As other sketches i have written are compiling correct I asume that the error lies somewhere in between :-)

 

Harry

 

Harry-

 

It looks like the sketch is just in the wrong folder.

 

Make sure that you have the DCC_Turntable_Rev_5 folder in your folder: (my folder is listed below)

 

...  \Arduino\DCC_Turntable_Rev_5\DCC_Turntable_Rev_5.ino

 

In my "Arduino" folder in Documents I have the following folders for this project:

"DCC_Accessory _packet_handler"

"DCC_Turntable_Rev_5"

"libraries"

 

In libraries I have the following folders for this project:

"AccelStepper"

"Adafruit_Motor_Shield_V2_Library_master"

"DCC_Decoder"

 

With all that matching I don't see why it wouldn't work.

Some of your folders have "(legacy)" after them.  Mine do not.  Don't know if that has anything to do with it.

 

 

Ray posted a great tutorial as post #14 on 18 November 2013 - 09:14

 

Take a look there and see where you might have files named incorrectly.

 

Hope that helps.

-Eric

Link to post
Share on other sites

Hi Eric.

Thanks for sharing your additions the the turntable project. I've not been able to test any of these out as yet as my PC has had an OS upgrade and i need to reinstall all the Arduino stuff. Just need to find the time to do it.

 

Now that you can manually align the position of the deck the next step would be to store the resulting position(s) in non volatile memory that could be recalled at start-up for your 'location variables'.

This would make the system 'programmable' and more akin to a commercial turntable controller.

 

This has been on my to-do list but I don't know when I'm going to get the chance to do it.

 

Ray.

 

All-

 

I forgot to post a picture of the break-out board I am using to manage all the inputs.  It is based on Ray's design, with some additions for the analog stuff.

 

Top of Board

post-22354-0-30956200-1395685440.jpg

 

Bottom of Board.

post-22354-0-74636800-1395685439.jpg

 

I am using screw terminals for the connections to the Adruino, to the Hall sensor and DCC In.

For the analog button connections I am just using header pins.  This allows a quick disconnect for things like the programmer.

 

Thanks

-Eric

  • Like 1
Link to post
Share on other sites

Now that you can manually align the position of the deck the next step would be to store the resulting position(s) in non volatile memory that could be recalled at start-up for your 'location variables'..

A very simple way to do this would be to show the positions on the Serial monitor as you adjust the settings. Make a note of them and put them into the program code.

 

...R

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...