Jump to content

DCC Controlled (PECO) Turntable Project using a Arduino Uno


Recommended Posts

I'd be interested in whether a tactile switch or maybe a optical switch would work instead of a hall switch?  Perhaps a small silver strip is painted on the end of the bridge to reflect the optical sensor...

 

Does the switch need to be normally open or closed?

 

 

Link to post
Share on other sites

  • RMweb Gold

^

Gluing tin foil (flattened) would work better.

Even better than that would be a light shining through slit. (Or reflected off the tin foil.)

 

Many IR sensors come with IR LEDS built in.

 

 

Kev.

Link to post
Share on other sites

If I understand things the hall effect sensor, or whatever alternative is chosen is just to provide a reference point for the software. If so it doesn't need to be precisely positioned but it does need to trigger at the same position every time. And it may be sufficient if it gives different reference numbers depending on which direction the turntable is moving as long as clockwise always gives X and anti-clockwise always gives Y. The software can then filter out the difference.

 

While a tactile switch, such as regular microswitch would work I suspect a non-contact sensor would be better for such a small device.

 

It should be easy to adjust the software to take account of normally on or normally off switches.

 

...R

Link to post
Share on other sites

I'd be interested in whether a tactile switch or maybe a optical switch would work instead of a hall switch?  Perhaps a small silver strip is painted on the end of the bridge to reflect the optical sensor...

 

Does the switch need to be normally open or closed?

 

 

^

Gluing tin foil (flattened) would work better.

Even better than that would be a light shining through slit. (Or reflected off the tin foil.)

 

Many IR sensors come with IR LEDS built in.

 

 

Kev.

 

 

If I understand things the hall effect sensor, or whatever alternative is chosen is just to provide a reference point for the software. If so it doesn't need to be precisely positioned but it does need to trigger at the same position every time. And it may be sufficient if it gives different reference numbers depending on which direction the turntable is moving as long as clockwise always gives X and anti-clockwise always gives Y. The software can then filter out the difference.

 

While a tactile switch, such as regular microswitch would work I suspect a non-contact sensor would be better for such a small device.

 

It should be easy to adjust the software to take account of normally on or normally off switches.

 

...R

 

Hi. 

Sorry for lack of updates, had an exhibition at the weekend to prepare for so times been precious.

 

I think Robins post just about sums this up, any sensor that will pull down the input on the Arduino board to near 0v should work. Like Robin says the important thing is that it does it at exactly the same place each time the unit is powered up to set the reference point.

I currently have a reflective and a slotted opto sensor to try out when i get a few minutes spare.

 

Ray.

Link to post
Share on other sites

Hi. 

Sorry for lack of updates, had an exhibition at the weekend to prepare for so times been precious.

 

I think Robins post just about sums this up, any sensor that will pull down the input on the Arduino board to near 0v should work. Like Robin says the important thing is that it does it at exactly the same place each time the unit is powered up to set the reference point.

I currently have a reflective and a slotted opto sensor to try out when i get a few minutes spare.

 

Ray.

The reason I asked about the tactile or other switch is because this project looked similar to the rocrail controller for turntables using a stepper.  Since it used a series of reductions on the drive, the stepper may turn hundreds of times before making a complete turn of the turntable.  The switch was in place to identify the start/end point.

 

More info here - http://www.wiki.rocrail.net/doku.php?id=gca145-en

 

They also designed a controller board that feeds position numbers to the turntable so it can go to a preset spot by keying in the position and hitting a button.

 

Older link with videos of the controller in action - http://www.wiki.rocrail.net/doku.php?id=mgv145-en

Edited by gcodori
Link to post
Share on other sites

Hello Ray, how are you?

Very good your project. Thanks for sharing!
I was working on a turntable but it would not, at first, controlled by dcc. Only keyboard and LCD. I'm using an external drive microstep and Arduino Leonardo to generate the STEP and DIR signals. When I saw your project, I decided to test to see if I could incorporate the controler by DCC on my project . But I'm having a problem here ... Leonardo apparently does not recognize the DCC commands. Even the example that comes with the library is not working. The code is not highlighted with the commands.

Do you have any idea what might be happening? 
Could be an incompatibility with the Arduino Leonardo?
 
Could you help me?

Edited by adgariglio
Link to post
Share on other sites

  • 2 weeks later...

Hi adgariglio, sorry I don't have any experience with the Leonardo but will try and have a look to see if there are any incompatibility problems. I'm currently separated for my turntable project but will update this thread when I return after the Christmas break.

 

Ray.

Link to post
Share on other sites

  • 3 weeks later...
  • 2 weeks later...

Hello Mr. Tender.
I have recently discovered this forum and I'm curious about your project.

I have to make a small turntable (for DCC) in HOm scale reproduction of a small Italian station, which no longer exists in reality.
Your project would be just perfect for me !.
I have already procured the Adafruit Motor Shield v.2
When you manage to complete the 7 part of your good project?.
Big thumbs up from me for your idea.
Greetings.
(sorry for my english ...)

 

Link to post
Share on other sites

Hi all followers.

Sorry for the lack of recent updates and the missing part 7. Will try and get it posted in the coming week.

Ray.

 

Ray 

 

Thanks for the update, will call back in a week or so, we all have other lives, cant always get things done as we might wish, I cant any way.

Link to post
Share on other sites

Hi All,

 

 

I have been following this project with keen interest and have been working on automating a traverser for my OO layout using an Arduino, stepper motors and DCC accessory control (using a Hornby Elite).

 

 

Thanks to Ray's pointers and inspiration on his turntable project I have made a reasonable amount of progress (at least on the electronics side), including incorporating an optical sensor and a seven segment LED display to indicate the selected lane.

 

 

Next steps are to work on the drive mechanism (the traverser is mounted on ball bearing drawer slides and the plan is to mount the stepper motors to the traverser bed with a gear on each shaft driving along a rack fixed to the drawer slide supports.  I will wire the stepper motors in parallel to the same output on the Arduino Motor shield which should keep them synchronized...

 

 

I know Ray is planning an update shortly and I certainly don’t want to “step on his toes” but I thought I would take the liberty of posting this in case it is of any use to anyone in the meantime?

 

 

Although I have adapted Ray’s code etc for my traverser, the principles could readily be used for a turntable.

 

 

 

How it works:

 

Once the Arduino is powered up, the stepper motors move the traverser bed slowly towards the sensor until that is tripped by a mechanical fixture breaking the IR beam and they then stop.  The “zero” position is then set ready for that session.  It is possible to save the last position on the Arduino while it is powered down but I figured it wouldn’t hurt to keep this as a calibration procedure for each session to ensure a known start point to calculate steps from without any need to calculate movement and direction relative to the current position.

 

 

The desired track address can then be selected on the DCC controller and once enabled, the LED displays the desired track number and the motors will move to the designated position and stop when it is reached.  The decimal point then lights up to indicate the traverser is in position.  There is also acceleration built in to give a soft start and end to the movement to reduce any “jolt” to the rolling stock.

 

 

By having set the zero position at the start, the Moveto command is used to drive the motors to move to a predetermined target position (number of steps) based on the track selected on the controller.

 

I currently have six parallel tracks on 67mm centres so I have currently set it up to move a fixed number of steps multiplied by the track number to determine the step count required.

 

However, I am planning to load the step counts for each of the track positions into an array at the start of the code so I can readily accommodate any variances between the physical track centres into the step count for each track.

 

 

I also plan to add a “kill” switch for emergency stops (I probably should have done that first but as it is all on breadboard at this stage, I haven’t made the effort!)

 

 

I am new to the forum so will upload the schematic for the sensor circuit and the code when I work out how to do that. :read:

The sensor I used is a ZD1901 photo interrupter from Jaycar here in NZ, but I am sure there would be suitable alternatives readily available elsewhere.

 

Apologies too for such a long first post!

 

Cheers

 

Paul

  • Like 2
Link to post
Share on other sites

Hi again,
 
Here is the sensor schematic: (Apologies for the quality - I am also still learning how to use the fantastic Fritzing software too!post-21790-0-90819300-1390989234_thumb.jpg
 
The sensor itself is fairly robust and includes mounting holes so I reckon makes a good solution with a plastic post or similar attached to the turntable/moving part to trigger it.  As I mentioned I got mine from Jaycar (Aus and NZ co) but I would expect Maplin or someone similar would have an equivalent.  Here is the package image and datasheet:
post-21790-0-53463300-1390990354_thumb.gif
post-21790-0-18581400-1390989508.jpg
 
The resistor values used give an output voltage of around 0.12v when not blocked and just under 4v when obstructed which appears to be enough difference for the Arduino to detect the change of state.  I am powering it directly from the 5v and gnd pins on the Arduino along with Ray's Isolator DC circuit and the seven segment LED.
 
For reference here is the hardware list I have used:

 

Arduino Duemilianove w/ ATmega328

Adafruit Motor Shield V2

 

DCC circuit as per Ray's posting

 

Sensor circuit:

ZD1901 x 1

220 ohm resistor x 1

4K7 ohm resistor x 1

 

Motors:

Mercury Motor SM-42BYG011-25 (200 steps per revolution)

"Transmission"

Acetal MOD 1 gear racks 4 x 250mm  and 12 tooth Spur gears by HUCO via Farnell (element 14)

 

LED display RS987-894 (I think these are now obsolete (80's vintage from my components box) but I am sure there is a modern equivalent.

 

 

And the code: (Hopefully it posts OK)

 

Please note: I am a novice programmer so it isn't that elegant and although this works on my hardware, it is still a work in progress, so please use with caution (and at your own risk).  Apart from that, please feel free to use it as you wish - any suggestions for improvements are welcome.

Many Thanks also to Ray for the initial code posting which was a great help in getting me started.

 

 

 
 

////////////////////////////////////////////////////////////////////////////////
//
// DCC Turntable Control Test Routines (Accessory Address 200)

#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[1];

////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
//
// 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(FORWARD, MICROSTEP);
}
void backwardstep2() {  
  myStepper2->onestep(BACKWARD, MICROSTEP);
}
//Variables

long lngMotorPos;
long lngTargetPosition;
const long lngOneRev = 5690.021;
int intPos = 0;//set initial LED value
//set up for LED Display
// bits representing segments A through G (and decimal point) for numerals 0-9
const byte numeral[10] = {
  //ABCDEFG /dp
  B11111100,  // 0
  B01100000,  // 1
  B11011010,  // 2
  B11110010,  // 3
  B01100110,  // 4
  B10110110,  // 5
  B00111110,  // 6
  B11100000,  // 7
  B11111110,  // 8
  B11100110,  // 9
};

// pins for decimal point and each segment
//                          dp,G,F,E,D,C,B,A
const int segmentPins[8] = {7,11,10,9,8,6,5,4};//defines Arduino pins for each segment
const int segmentSpin[6] = {4,5,6,8,9,10};// used to "rotate" the segments while motor is moving to sensor position


// 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;

}

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// 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;


  //  Check for DCC imput & determine entered address
  switch (address) { //Set target position based on address selected
  case 200:
    lngTargetPosition = lngOneRev*1;
    intPos=1;
    break;
  case 201:
    lngTargetPosition = lngOneRev*2;
    intPos=2;
    break;
  case 202:
    lngTargetPosition = lngOneRev*3;
    intPos=3;
    break;
  case 203:
    lngTargetPosition = lngOneRev*4;
    intPos=4;
    break;
  case 204:
    lngTargetPosition = lngOneRev*5;
    intPos=5;
    break;
  case 205:
    lngTargetPosition = lngOneRev*6;
    intPos=6;
  default:
    //nothing selected

    lngMotorPos=stepper2.currentPosition();
    Serial.println(lngMotorPos,DEC);

  }
//  Serial.print("Basic addr: ");
  //    Serial.print(address,DEC);
  //    Serial.print("   activate: ");
  //    Serial.println(enable,DEC);

  if ( enable )
  {
      Serial.print("Motor at: ");
    lngMotorPos=stepper2.currentPosition();
    Serial.println(lngMotorPos,DEC);
    Serial.print("Moving to: ");
    Serial.println(lngTargetPosition);
    showDigit(intPos);
    stepper2.moveTo(lngTargetPosition);
   while (stepper2.currentPosition() != lngTargetPosition) // move to target position
    stepper2.run(); 
    
    digitalWrite(segmentPins[0],0);
    
    
  } 
  
  
  else
  {
    //      stepper2.moveTo(2000);
    lngMotorPos=stepper2.currentPosition();
    Serial.println(lngMotorPos,DEC);
    delay(1000);
//    showDigit(intPos);

  }
  
}



//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// Setup
//
void setup() //runs once to determine start position when sensor reached.
{ 
  Serial.begin(9600);

  AFMStop.begin(); // Start the shield

  //configure pin3 as an input and enable the internal pull-up resistor
  pinMode(3, INPUT_PULLUP);
  //read the sensor (open collector type) value into a variable
  int sensorVal = digitalRead(3);

  //set stepper motor speed and acceleration 
  stepper2.setMaxSpeed(10000.0);
  stepper2.setAcceleration(100.0);
//  stepper2.setSpeed(100);
  //  stepper2.moveTo(800);
  
  for(int i=0; i < 8; i++)//assign pins for led display
  {
    pinMode(segmentPins[i], OUTPUT); // set segment and DP pins to output
  }
  showDigit(10);
  Serial.println("At switch off segments");

delay(1000);
  // if near reference point move away
  Serial.println("Check for reference point");
//  delay(2000);
  sensorVal = digitalRead(3);
  while (sensorVal == HIGH) {
    Serial.println("At Sensor");
    Serial.print("Motor at: ");
    lngMotorPos=stepper2.currentPosition();
    Serial.println(lngMotorPos,DEC);
   intPos=0;
   showDigit(intPos);
    sensorVal = digitalRead(3);
//    forwardstep2();
    delay(50);
  }

  // step forward to sensor index point
  while (sensorVal == LOW) {
    sensorVal = digitalRead(3);
    Serial.println("Moving to sensor");
    backwardstep2();
//stepper2.run();
digitalWrite(segmentPins[0],0);//DP off
//digitalWrite(segmentPins[1],1);
for(int i=0; i < 7; i++)//Switch onsegments
{
digitalWrite(segmentSpin[i],1);
delay(50);
digitalWrite(segmentSpin[i],0);
digitalWrite(segmentPins[0],1);//DP on
//digitalWrite(segmentPins[1],1);

}

//    delay(50);
  }

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

void showDigit( int number) //display number on seven segment display
{
  boolean isBitSet;

  for(int segment = 0; segment < 8; segment++) //was 1
  {
    if( number < 0 || number > 9){
      isBitSet = 0;   // turn off all segments
     digitalWrite( segmentPins[segment], isBitSet);
    }
    else{
      // isBitSet will be true if given bit is 1
      isBitSet = bitRead(numeral[number], segment);
    }
    isBitSet = ! isBitSet; // remove this line if common cathode display
    digitalWrite( segmentPins[segment], isBitSet);
  }
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// Main loop
//
void loop()
{
  static int addr = 0;


  ////////////////////////////////////////////////////////////////
  // Loop DCC library
  DCC.loop();

  ////////////////////////////////////////////////////////////////
  // Loop Stepper

  stepper2.run();


}
Edited by Pandasum
  • Like 3
Link to post
Share on other sites

Many thanks for posting this.

 

Having been inspired by both this thread and the "Automated Motorised Traverser" thread started by Deev, I have just bought an Arduino Uno and have started to study.........

 

I intend to use the Arduino to drive a Herkat N gauge traverser under DCC as there isn't (as far as I can find) a suitable off the shelf decoder.   Then I will attempt full automatic control with Railroad & Co.   :no:

 

Contibutions like yours, Deev's and Tender's all help, and the timing is perfect.   Just waiting for the postman to deliver some diodes and an optoisolator for the DCC circuit and we'll be off.....

 

Thanks to you all.

Link to post
Share on other sites

I have the same stepper motors as @Pandasum. They work very well with the Pololu A4988 stepper motor driver board which can be powered up to 35v to enable the motors to retain torque at higher speeds.

 

...R

Link to post
Share on other sites

I have updated the Code posting so it should be able to be copied now. (Pasting as Javascript and leaving the line number box blank is the answer in case you were wondering)

 

Thanks for the positive feedback and additional info.

 

Apologies too to Deev - I also followed his post with interest (hence the stepper motors and gear racks) but neglected to acknowledge that as a source.

 

Robin2 - Thanks for the tip.  I also intend to use a separate power supply for the motors once installed but the Arduino power has been enough so far so I haven't done that yet.

 

Cornerman - hope the Postman arrives soon and good luck!

 

Cheers

 

Paul

Link to post
Share on other sites

Paul

 

Thanks for your diligent efforts in posting the details of hard and software - makes it much easier to follow what you gave done and what you have achieved.

 

I wish you well, good luck in your efforts and keep posting!

 

SD

Link to post
Share on other sites

 but the Arduino power has been enough so far so I haven't done that yet.

 

It looks like you have been lucky so far. At worst, powering motors from the Arduino 5v or Vin pins runs the risk of damaging the Arduino. But it can also cause brown-outs which will appear like software problems and can be very hard to debug.

 

...R

Link to post
Share on other sites

Hi Robin2,

 

Thanks for the tip.

 

The green LED on the Motor shield was lit so I figured all was OK, but I will now revert to using a 12V supply via the DC jack on the Arduino with the jumper or the direct shield connection without the jumper, rather than USB alone.

 

 

P

Edited by Pandasum
Link to post
Share on other sites

Apolgies for waking up this topic but I am about to have a go at a stepper motor drive for a Peco On30 TT (which looks like its just a version of the OO job but with a wider deck (?)). This topic looks like its going to be incredibly useful in getting started. Indeed as we are now on the other side of Christmas (by some distance!) might I put in a plea for the story to continue? I particularly like the costed parts list and the detailed account of what you have done is very clear - most helpful. More please!

 

Chaz

Link to post
Share on other sites

Part 7 - The Seven Road Turntable

 

Ok, its been a long time coming, but other things have been taking up my time so apologies for that.

 

In Part 6 I described adding DCC control using a turnout command to rotate the turntable through 180 degrees depending on whether a straight or turnout command was sent.

 

The seven road turntable is just an extension of this and the only difference in the software is in the sections headed:

 

'Decoder Init'

 

and

 

'Basic accessory packet handler'

 

The Decoder Init section sets up the recognisable DCC decoder addresses. in Part 6 we only had 1 at address 200

 

gAddresses[0].address = 200;

 

So firstly we need to add a few more addresses for the other roads.

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// 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;
}

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

This gives us addresses in the range 200-207 (but we only use 201-207)

 

The section 'Basic accessory packet handler' now needs to decode which address was sent and if in the range 201-207 whether it was a 'straight' or 'turnout' command.

 

We do this using the 'Switch/Case' function

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// 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.print("Basic addr: ");
            Serial.print(address,DEC);
            Serial.print("   activate: ");
            Serial.println(enable,DEC);
            
            if( enable )
            {
                switch (i) {
                  case 1:
                    stepper2.moveTo(200);
                  break;
                  case 2:
                    stepper2.moveTo(400);
                  break;
                  case 3:
                    stepper2.moveTo(600);
                  break;
                  case 4:
                    stepper2.moveTo(800);
                  break;
                  case 5:
                    stepper2.moveTo(1000);
                  break;
                  case 6:
                    stepper2.moveTo(1200);
                  break;
                  case 7:
                    stepper2.moveTo(1400);
                  break;
                }
            }else{
                switch (i) {
                  case 1:
                    stepper2.moveTo(1800);
                  break;
                  case 2:
                    stepper2.moveTo(2000);
                  break;
                  case 3:
                    stepper2.moveTo(2200);
                  break;
                  case 4:
                    stepper2.moveTo(2400);
                  break;
                  case 5:
                    stepper2.moveTo(2600);
                  break;
                  case 6:
                    stepper2.moveTo(2800);
                  break;
                  case 7:
                    stepper2.moveTo(3000);
                  break;
                }
            }
        }
    }
}

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

This can be extended for more roads if required, just add more recognisable address in the 'Decoder Init' section and add another 'Case' statement for each additional address.

 

The 'moveTo' commands (stepper2.moveTo(200); etc) relate to absolute positions of the turntable deck from the 'reference' point.

 

As there are 3200 steps for a complete rotation the 'moveTo' positions after the 'else' statement are 1600 steps bigger than those before, giving a 180 degree rotation for each road position.

When (if) you get this far you will probably find that the tracks don't quite line up exactly when the deck is rotated through 180 degrees, maybe up to a rail width out. This is probably due to the accuracy of the step in the stepper motor, but don't worry its easily fixed as the error is very stable and repeatable.

 

All you have to do its to tweak the values of the relevant 'moveTo' command.

 

So for Road 1:

case 1:
                    stepper2.moveTo(200);
                  break;

the corresponding 180 degree turn after the 'else' statement is

case 1:
                    stepper2.moveTo(1800);
                  break;

Just adjust (by trial and error) the value 1800 to get the rails to lines up exactly. An increase (or decrease) in value of about 4 relates to about a rail width.

 

Do this for each road, and there you have it, a Seven (or more) Road, DCC Controlled Turntable.

 

Just for completeness, here's the complete code.

////////////////////////////////////////////////////////////////////////////////
//
// DCC Turntable Control Test 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[8];

////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
//
// 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(FORWARD, MICROSTEP);
}
void backwardstep2() {  
  myStepper2->onestep(BACKWARD, MICROSTEP);
}

// 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;
}

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// 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.print("Basic addr: ");
            Serial.print(address,DEC);
            Serial.print("   activate: ");
            Serial.println(enable,DEC);
            
            if( enable )
            {
                switch (i) {
                  case 1:
                    stepper2.moveTo(200);
                  break;
                  case 2:
                    stepper2.moveTo(400);
                  break;
                  case 3:
                    stepper2.moveTo(600);
                  break;
                  case 4:
                    stepper2.moveTo(800);
                  break;
                  case 5:
                    stepper2.moveTo(1000);
                  break;
                  case 6:
                    stepper2.moveTo(1200);
                  break;
                  case 7:
                    stepper2.moveTo(1400);
                  break;
                }
            }else{
                switch (i) {
                  case 1:
                    stepper2.moveTo(1800);
                  break;
                  case 2:
                    stepper2.moveTo(2000);
                  break;
                  case 3:
                    stepper2.moveTo(2200);
                  break;
                  case 4:
                    stepper2.moveTo(2400);
                  break;
                  case 5:
                    stepper2.moveTo(2600);
                  break;
                  case 6:
                    stepper2.moveTo(2800);
                  break;
                  case 7:
                    stepper2.moveTo(3000);
                  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);
  //read the sensor (open collector type) value into a variable
  int sensorVal = digitalRead(3);
   
  //set stepper motor speed and acceleration 
  stepper2.setMaxSpeed(30.0);
  stepper2.setAcceleration(20.0);
//  stepper2.moveTo(800);

// if near reference point move away
  sensorVal = digitalRead(3);
  while (sensorVal == LOW) {
    sensorVal = digitalRead(3);
    forwardstep2();
      delay(50);
  }
  
// step forward to sensor index point
  while (sensorVal == HIGH) {
    sensorVal = digitalRead(3);
    forwardstep2();
      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;
    }
    
    stepper2.run();
}

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

I said at the beginning that I'm no expert when it comes to programming so if you see any improvements or develop this further please add them to this thread.

 

Ray.

 

 

P.S. you will note that 'Pandasum' has used a similar method for decoding the addresses for his traverser in his posts above.

  • Like 4
Link to post
Share on other sites

Good giornagiorno. First of all, thanks to everyone for the effort put in the projects presented in this tread! :no: 
In particular, Ray, I tried to implement your project, but loading the sketch in ArduinoUNO me the following error is reported:

 

  sketch_feb07b.cpp.o: In function `__static_initialization_and_destruction_0':
C:\Programmi\Arduino_1.0.5/sketch_feb07b.ino:50: undefined reference to `AccelStepper::AccelStepper(void (*)(), void (*)())'
sketch_feb07b.cpp.o: In function `loop':
C:\Programmi\Arduino_1.0.5/sketch_feb07b.ino:208: undefined reference to `AccelStepper::run()'
sketch_feb07b.cpp.o: In function `BasicAccDecoderPacket_Handler(int, unsigned char, unsigned char)':
C:\Programmi\Arduino_1.0.5/sketch_feb07b.ino:139: undefined reference to `AccelStepper::moveTo(long)'
sketch_feb07b.cpp.o: In function `setup':
C:\Programmi\Arduino_1.0.5/sketch_feb07b.ino:164: undefined reference to `AccelStepper::setMaxSpeed(float)'
C:\Programmi\Arduino_1.0.5/sketch_feb07b.ino:165: undefined reference to `AccelStepper::setAcceleration(float)'

 

Someone is able to suggest a solution? :O 

Thank you very much & greetings.  Rovilazz

 

 

Link to post
Share on other sites

Ray

 

You put your sensor on the outside of the pit, and your magnet on the table - logical! - but I wonder how far away you could have sensor and still get a reliable switch point. The Metalsmiths t/t is mounted on an MDF pit, and I don't really fancy digging up the reasonably-well scenic-ed bit to install it. It's about 6 mm thick. Will the sensor work reliably at that distance, plus the one or two mm of clearance? I can hide it under the centre of the table reasonably easily, but then it's angular discrimination will be much less than if it were mounted at the edge. I wonder if I could just stick it down in the pit somewhere and paint it to match the gravel, and hope nobody notices.....?

 

I'm planning to use something like this http://www.farnell.com/datasheets/608704.pdf

 

Sound reasonable?

 

SD

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