Model Railway Turntable - in a spin so much Im dizzy



2 Likes

In respect of using belts etc I am extremely constrained by the size of the model. This is what I am working with…


1 Like
  • Thanks for the connection diagram.

  • Motors powered by the Arduino 5v pin is ill advised.
    Your Arduino board 5 volt supply is not adequate to power your project.
    If you do use it as such, your Arduino may be damaged.
    Also, never connect an inductive load to the Arduino 5v pin.

  • Here is some homework for you.

  • FYI, If you will be making your own belts, the glue (available on Amazon) is great !
    Search for Oil Glue.

Then use the stepper to drive the outside of the turn table and get a similar reduction in movement.

Thank you LarryD. I have saved that glue to my Amazon basket just in case. I have also watched that video, which was very helpful indeed. I will be spending some time perusing his website for some more guidance. I especially like how he explains his sketches.

  • Please confirm your rotary encoder has a push switch, i.e. pushing on the shaft closes an internal switch.

  • Test time, do you know what a:
    -pull-up/pull-down resistor is ?
    -non-blocking TIMER is ? (based on millis( ) ).
    -State Machine is ?
    -bouncing switch is ?

  • How do you envision the IR slot sensor will be used ?

  • Show us images of previous projects.

  • Have you considered using an OLED display ?
    Example: SSH1106 (1.3”)

Yes my rotary encoder has a push switch.

I will answer as best I can from my understanding. Apologies for any incorrect terminology I’ve quickly learnt that this is very very complex.

The pull-up/down refers to either being connected directly to vcc (up) or ground (down). They are used to control the state of an input pin. I.e. high or low. The theory is that this ensures the Arduino (in this case) always has either a high or low state on the pin. - I think that’s all correct anyway.
I wouldn’t know how to work out the correct value to use though as I haven’t personally used them before.

The blocking timer, as my understanding goes - but feel free to correct me, is to do with whether a time delay affects the entire Arduino. For instance whilst completing the time of, say, 10 seconds it means all other processes also get blocked from occurring for those 10 seconds.

A state machine is the process of determining whether a pin is high or low from an input. It’s the process of taking, for instance, a push to make switch, being recognised and turning a low state to high etc.

A bouncing switch refers to the noise created when a physical switch is thrown. Due to the mechanics of the parts moving this can cause multiple high/low user inputs to be registered as the pins physically switch and bounce or chatter between contacts.

I envisaged using the IR sensor to determine zero when the beam is broken. Obviously I am extremely limited due to the size of the model so I was intending to attach something to a gear/disc mounted underneath but on the same motor shaft. Thereby it would directly represent the movement of the turntable above it. However. I am open to ideas that would be better. I was intending to test different options once I knew the code worked so that I could test in the real world. I am much more hands on and prefer to physically see things to help me overcome problems.

I have not considered using an oled screen but would be happy to do so.
My thinking was I am trying to build the layout for my autistic son and so I wanted it to be very easy to use for him.
I was going to hide the rotary encoder and delete/set location switch in the layout so only I could access it.
That way he could only press cw or ccw to determine which direction he wanted to move in and/or to complete a half turn. I also determined that the zero routine needed to be accessible from the main controller in the event of power loss etc prior to writing the known location to the EEPROM.

For what purpose would you use the screen for? The one you show is quite small in size so I could easily integrate it into a controller if needed.

My previous projects are all related to applications such as the MERG DCC system, making moving scenery on layouts/dioramas - for instance I have created a moving canal boat system for this layout from a bike chain and magnets, creating voltage regulators and controllers for dc layouts etc. I also have spent time restoring and renovating old game consoles - gameboys, Lynx, sega’s etc. I have not done any programming like this before.
My previous projects for the Arduino are entirely related to getting this working - testing sensors, inputs, etc. It is all for the purpose of creating this turntable for my son.

I forgot to mention. I was considering the use of the ir sensor over a hall effect sensor for two reasons. (1) at this scale it would prove extremely inaccurate as the magnet would never travel very far from the sensor. (2) the train wheels are magnetic to keep them on the rails. A magnet could therefore theoretically affect this.

  • If you are willing to hang in there, we can help you through your project.

  • This will require you to ask questions when you do not understand something.

  • Unless somebody else wants to take over, I can start this process.

  • Do you want to commit the time into this ?

Without a doubt.

  • Okay then let's go.

  • Below is a schematic for the following Arduino UNO sketch.

  • I start projects off by using a skeleton sketch as it saves a lot of typing and time.
    See the below sketch.

  • This sketch toggles a Hearbeat LED every 500ms.
    We use a Heartbeat LED to see if the code is executing and can tell if it is in never never land if the LED stops flashing or stutters.

  • When the switch is closed a testLED turns on for a period of time then goes out.

  • When the switch is closed we save the current time and when we let go of the switch, we print the time to the serial monitor that the switch was closed/pressed.


  • See if you can work your way through the sketch, ask questions . . . .
//================================================^================================================
//
//  URL
//
//  Name
//
//  Version    YY/MM/DD    Comments
//  =======    ========    ========================================================================
//  1.00       24/05/25    Started writing this sketch
//
//
//

//#include <Wire.h>



//================================================
#define LEDon              HIGH   //PIN---[220R]---A[LED]K---GND
#define LEDoff             LOW

#define PRESSED            LOW    //+5V---[Internal 50k]---PIN---[Switch]---GND
#define RELEASED           HIGH

#define CLOSED             LOW    //+5V---[Internal 50k]---PIN---[Switch]---GND
#define OPENED             HIGH


//================================================^================================================
//                          millis() / micros()   B a s e d   T I M E R S
//================================================^================================================
//To keep the sketch tidy, you can put this structure in a different tab in the IDE
//
//These TIMER objects are non-blocking
struct makeTIMER
{
#define MILLIS             1
#define MICROS             1000  //we can use this value to divide into a variable to get milliseconds

#define ENABLED            true
#define DISABLED           false

#define YES                true
#define NO                 false

#define STILLtiming        0
#define EXPIRED            1
#define TIMERdisabled      2

  //these are the bare minimum "members" needed when defining a TIMER
  int                      TimerType;      //what kind of TIMER is this? MILLIS/MICROS
  unsigned long            Time;           //when the TIMER started
  unsigned long            Interval;       //delay time which we are looking for
  bool                     TimerFlag;      //is the TIMER enabled ? ENABLED/DISABLED
  bool                     Restart;        //do we restart this TIMER   ? YES/NO

  //================================================
  //condition returned: STILLtiming (0), EXPIRED (1) or TIMERdisabled (2)
  //function to check the state of our TIMER  ex: if(myTimer.checkTIMER() == EXPIRED);
  byte checkTIMER()
  {
    //========================
    //is this TIMER enabled ?
    if (TimerFlag == ENABLED)
    {
      //========================
      //has this TIMER expired ?
      if (getTime() - Time >= Interval)
      {
        //========================
        //should this TIMER restart again?
        if (Restart == YES)
        {
          //restart this TIMER
          Time = getTime();
        }

        //this TIMER has expired
        return EXPIRED;
      }

      //========================
      else
      {
        //this TIMER has not expired
        return STILLtiming;
      }

    } //END of   if (TimerFlag == ENABLED)

    //========================
    else
    {
      //this TIMER is disabled
      return TIMERdisabled;
    }

  } //END of   checkTime()

  //================================================
  //function to enable and restart this TIMER  ex: myTimer.enableRestartTIMER();
  void enableRestartTIMER()
  {
    TimerFlag = ENABLED;

    //restart this TIMER
    Time = getTime();

  } //END of   enableRestartTIMER()

  //================================================
  //function to disable this TIMER  ex: myTimer.disableTIMER();
  void disableTIMER()
  {
    TimerFlag = DISABLED;

  } //END of    disableTIMER()

  //================================================
  //function to restart this TIMER  ex: myTimer.restartTIMER();
  void restartTIMER()
  {
    Time = getTime();

  } //END of    restartTIMER()

  //================================================
  //function to force this TIMER to expire ex: myTimer.expireTimer();
  void expireTimer()
  {
    //force this TIMER to expire
    Time = getTime() - Interval;

  } //END of   expireTimer()

  //================================================
  //function to set the Interval for this TIMER ex: myTimer.setInterval(100);
  void setInterval(unsigned long value)
  {
    //set the Interval
    Interval = value;

  } //END of   setInterval()

  //================================================
  //function to return the current time
  unsigned long getTime()
  {
    //return the time             i.e. millis() or micros()
    //========================
    if (TimerType == MILLIS)
    {
      return millis();
    }

    //========================
    else
    {
      return micros();
    }

  } //END of   getTime()

}; //END of   struct makeTIMER


//                             D e f i n e   a l l   a r e   T I M E R S
//================================================^================================================
/*example
  //========================
  makeTIMER toggleLED =
  {
     MILLIS/MICROS, 0, 500ul, ENABLED/DISABLED, YES/NO  //.TimerType, .Time, .Interval, .TimerFlag, .Restart
  };

  Function examples:
  toggleLED.checkTIMER();
  toggleLED.enableRestartTIMER();
  toggleLED.disableTIMER();
  toggleLED.expireTimer();
  toggleLED.setInterval(100ul);
*/

//========================
makeTIMER heartbeatTIMER =
{
  MILLIS, 0, 500ul, ENABLED, YES       //.TimerType, .Time, .Interval, .TimerFlag, .Restart
};

//========================
makeTIMER switchesTIMER =
{
  MILLIS, 0, 5ul, ENABLED, YES        //.TimerType, .Time, .Interval, .TimerFlag, .Restart
};

//========================
makeTIMER machineTIMER =
{
  MICROS, 0, 1000ul, ENABLED, YES      //.TimerType, .Time, .Interval, .TimerFlag, .Restart
};

//========================
makeTIMER commonTIMER =
{
  MILLIS, 0, 1000ul, DISABLED, NO      //.TimerType, .Time, .Interval, .TimerFlag, .Restart
};


//                                     S t a t e   M a c h i n e
//================================================^================================================
//the States in our machine (use better names that mean something to you)
enum STATES : byte
{
  STARTUP, STATE1, STATE2, STATE3, STATE4, FINISHED
};
STATES mState = STARTUP;


//                                            G P I O s
//================================================^================================================
//
//structure to define input objects
struct makeInput
{
  const byte pin;                //the digital input pin number
  unsigned long switchTime;      //the time the switch was closed
  byte lastState;                //the state the input was last in
  byte counter;                  //a counter used to valididate a switch change in state
}; //END of   struct makeInput

//Digital Inputs
//===================================
//define this input which is connected to a PB switch
makeInput mySwitch =
{
  2, 0, OPENED, 0                        //pin, switchTime, lastState, counter
};

byte filter                      = 10;
//TIMER "switches" runs every 5ms.
//5ms * 10 = 50ms is needed to validate a switch change in state.
//A switch change in state is valid "only after" 10 identical changes is detected.
//used to filter out EMI noise in the system

//OUTPUTS
//===================================
const byte testLED               = 12;
const byte heartbeatLED          = 13;


//VARIABLES
//===================================



//                                           s e t u p ( )
//================================================^================================================
void setup()
{
  Serial.begin(115200);
  //Serial.begin(9600);

  //use INPUT_PULLUP so the pin dose not float which can cause faulty readings
  pinMode(mySwitch.pin, INPUT_PULLUP);  

  pinMode(heartbeatLED, OUTPUT);

  digitalWrite(testLED, LEDoff);
  pinMode(testLED, OUTPUT);

} //END of   setup()


//                                            l o o p ( )
//================================================^================================================
void loop()
{
  //========================================================================  T I M E R  heartbeatLED
  //is it time to toggle the heartbeat LED ?
  if (heartbeatTIMER.checkTIMER() == EXPIRED)
  {
    //toggle the heartbeat LED
    digitalWrite(heartbeatLED, digitalRead(heartbeatLED) == HIGH ? LOW : HIGH);
  }

  //========================================================================  T I M E R  switches
  //is it time to check our switches ?
  if (switchesTIMER.checkTIMER() == EXPIRED)
  {
    checkSwitches();
  }

  //========================================================================  T I M E R  machine
  //is it time to service our State Machine ?
  if (machineTIMER.checkTIMER() == EXPIRED)
  {
    checkMachine();
  }

  //================================================
  //other non blocking code goes here
  //================================================

} //END of   loop()


//                                    c h e c k M a c h i n e ( )
//================================================^================================================
void checkMachine()
{
  //================================================
  //service the current "state"
  switch (mState)
  {
    //========================
    case STARTUP:
      {
        //do startup stuff
      }
      break;

    //========================
    case STATE1:
      {
        //condition returned: STILLtiming, EXPIRED or TIMERdisabled
        if (commonTIMER.checkTIMER() == EXPIRED)
        {
          digitalWrite(testLED, LEDoff);

          //we are finished with this TIMER
          commonTIMER.disableTIMER();

          //next state
          mState = STATE2;
        }
      }
      break;

    //========================
    case STATE2:
      {
        //Do something
      }
      break;

    //========================
    case STATE3:
      {
        //Do something
      }
      break;

    //========================
    case STATE4:
      {
        //Do something
      }
      break;

    //========================
    case FINISHED:
      {
        //Do something
      }
      break;

  } //END of  switch/case

} //END of   checkMachine()


//                                   c h e c k S w i t c h e s ( )
//================================================^================================================
void checkSwitches()
{
  byte pinState;

  //========================================================================  mySwitch.pin
  pinState = digitalRead(mySwitch.pin);

  //===================================
  //has this switch changed state ?
  if (mySwitch.lastState != pinState)
  {
    mySwitch.counter++;

    //is this change in state stable ?
    if (mySwitch.counter >= filter)
    {
      //get ready for the next sequence
      mySwitch.counter = 0;

      //update to this new state
      mySwitch.lastState = pinState;

      //========================
      //did this switch go closed ?
      if (pinState == CLOSED)
      {
        //the time this switch closed
        mySwitch.switchTime = millis();

        digitalWrite(testLED, LEDon);

        //set interval to 2 seconds
        commonTIMER.setInterval(2000ul);
        commonTIMER.enableRestartTIMER();

        //next state
        mState = STATE1;
      }

      //========================
      //did this switch go opened ?
      else if (pinState == OPENED)
      {
        Serial.print("The time the switch was closed = ");
        Serial.println(millis() - mySwitch.switchTime);
      }
    }
  }

  //===================================
  //a valid switch change has not been confirmed
  else
  {
    mySwitch.counter = 0;
  }

  //END of mySwitch.pin

  //========================================================================  Next Switch

} //END of   checkSwitches()


//================================================^================================================

OK i have read through and implemented the code onto my uno.
The circuit works successfully with the heartbeat led flashing in time with the yellow L led on the uno board itself. The momentary switch triggers the test led. even if i hold the switch down it still times the set amount and switches off. it will not come back on until i release the switch and press it again. the heartbeat led flash remains constant throughout.

Q1 - the internal pull up resistor and pin on your schematic.
is this prebuilt into the 5v pin? it states 20-50k, does the user need to determine this value, or does it change automatically based on if the pin is high or low state? is there a physical pin so i can test its state?

Q2 - can you please clarify for me what bool is? i can see it is used to refer to whether the timer is enabled or not, and then whether the restart should occur or not. i presume therefore that it relates to the decision making process.

Q3 - where is the getTime defined? is this the time = getTime(); of the restartTIMER section?

I believe the 'if' or 'else' refers to the state machine. decision making process.
The 'return' is the final piece of code that is fed back into the machine. that way it avoids the constant feed of misinformation. by feeding back just the last piece of information it allows for a more stable feedback.

Q4 - I am not getting a time stamp on the serial monitor just some square shapes. i cannot locate what baud i should be in from the code. is this defined somewhere? i thought this needed to be done.

the 5ms * 10 ensures that a constant reading from the user input must be received for 10 consequetive sequences. this is to irradicate/allow for switch bounce. being that this is only 50ms the user is unlikely to even notice it occurs.

Q5 - what is the serial.begin(115200) and (9600) ? Does this relate to my previous question on the serial monitor baud.

is my understanding correct that when i haven't released the push button the reason that the light doesn't remain lit, or light again is due to the else if pinstate==opened code? whereby if it is closed it should then print the time closed to the serial monitor, and end the checkswitches routine.

is that correct?

Thank you for this. i am very appreciative.

ok i have answered my own questions in that my turning my serial monitor to 115200 baud i am now getting time stamps correctly

1 Like

Which I shall bookmark and use as an example. For ppl we ask to draw schematic, and they're all what software should I get…

pencil and paper.

a7

1 Like

Hi a7. i think thats a complement - im going to take it as one anyway. im feeling extremely silly on here at the moment. just treading water to stay afloat i think. I am already aware that the drawing is flawed. in that i know i need a separate power supply for the stepper motor driver, and, that i likely need pull-up/down resistors on my input pins. but its still a work in progress. As far as im concerned its pencil and paper over software every day of the week lol.

1 Like

Q1 - the internal pull up resistor and pin on your schematic.
is this prebuilt into the 5v pin? it states 20-50k, does the user need to determine this value, or does it change automatically based on if the pin is high or low state? is there a physical pin so i can test its state?

  • There is an internal pull-up resistor on all GPIO pins, can be 20k to 50k.
    You do not need to worry what the value is, just that you can enable it.
    No pin to test it.

Q2 - can you please clarify for me what bool is? i can see it is used to refer to whether the timer is enabled or not, and then whether the restart should occur or not. i presume therefore that it relates to the decision making process.

  • bool stands for Boolean.
    When we create a variable we must assign the varriable a number type.

Number 'type's.

  • boolean (8 bit) - simple logical true/false, Arduino does not use single bits for bool
  • byte (8 bit) - unsigned number from 0 to 255
  • char (8 bit) - signed number from -128 to 127. The compiler will attempt to interpret this data type as a character in some circumstances, which may yield unexpected results
  • unsigned char (8 bit) - same as 'byte'; if this is what you're after, you should use 'byte' instead, for reasons of clarity
  • word (16 bit) - unsigned number from 0 to 65535
  • unsigned int (16 bit)- the same as 'word'. Use 'word' instead for clarity and brevity
  • int (16 bit) - signed number from -32768 to 32767. This is most commonly what you see used for general purpose variables in Arduino example code provided with the IDE
  • unsigned long (32 bit) - unsigned number from 0 to 4,294,967,295. The most common usage of this is to store the result of the millis() function, which returns the number of milliseconds the current code has been running
  • long (32 bit) - signed number from -2,147,483,648 to 2,147,483,647
  • float (32 bit) - signed number from -3.4028235E38 to 3.4028235E38. Floating point on the Arduino is not native; the compiler has to jump through hoops to make it work. If you can avoid it, you should. We'll touch on this later. Sparkfun.
  • You select the 'type' best suited for your variables.

Examples:

  • your variable does not change and it defines a pin on the Arduino. const byte limitSwitchPin = 34;
  • since an analog variable can be 0 to 1023, a byte will not do, you can select 'int'. int temperature;
  • if your variable needs to be within -64 to +64 a 'char' will do nicely. char joystick;
  • if your variable is used for ASCII then you need type 'char', char myText[ ] = "Raspberry Pie Smells";
  • if your variable enables some code then boolean can be used. boolean enableFlag = false;
  • millis() returns the time in ms since rebooting, unsigned long currentTime = millis();
    etc.

Q3 - where is the getTime defined? is this the time = getTime(); of the restartTIMER section?

  • There is a function we can access called millis().
    This function returns the time in millis seconds since the Arduino was powered up (turned on).

I believe the 'if' or 'else' refers to the state machine. decision making process.
The 'return' is the final piece of code that is fed back into the machine. that way it avoids the constant feed of misinformation. by feeding back just the last piece of information it allows for a more stable feedback.

  • Not sure what you are saying.
    if( . . .) and else if(. . .) are decision statements we can use to determine when something has met a certain criteria; when it has we can do something.
    These statements can be used in a State Machine but elsewhere also.

Q4 - I am not getting a time stamp on the serial monitor just some square shapes. i cannot locate what baud i should be in from the code. is this defined somewhere? i thought this needed to be done.

  • In the setup( ) function you will find this Serial.begin(115200);
    It sets the speed to 115200 baud; your Serial Monitor must also be set to this value before you can see the correct text.
    Look in the bottom right corner of the Serial Monitor window.

the 5ms * 10 ensures that a constant reading from the user input must be received for 10 consequetive sequences. this is to irradicate/allow for switch bounce. being that this is only 50ms the user is unlikely to even notice it occurs.

  • This is just one method we can use to make sure switch bounce OR electrical noise has not cause a change in the switch state.
    In this case, the time is set to 5 * 10ms = 50ms before we validate a change in switch state.

Q5 - what is the serial.begin(115200) and (9600) ? Does this relate to my previous question on the serial monitor baud.

  • Yes, if you look at the baud rates in the bottom of the Serial Monitor window, you can see all the baud rates you an use.

is my understanding correct that when i haven't released the push button the reason that the light doesn't remain lit, or light again is due to the else if pinstate==opened code? whereby if it is closed it should then print the time closed to the serial monitor, and end the checkswitches routine.

  • When you close the switch on GPIO pin 2 the code starts a TIMER and turns on the LED on pin 12 (for 2 seconds), when this TIMER expires, the code stops this TIMER and turns off the LED.
    When it comes to switches it is highly recommended we look for switch changes in state (as this code does).
    The LED is totally managed by the TIMER for pin 12, not when the switch is held or opened.

Here is a schematic of your proposed projects.
Please check it over to see if it meets you design.
EDIT, updated schematic

Thank you for clearing up that 'types' definitions. i have saved that for future reference.

Wow! Yes, that schematic would work perfectly for my needs. It takes into account the separate motor power supply.
Ive also noted from your comments to my questions that the internal pull up resistors are built in for the relevant pins. hence why they are not shown/needed.

what does the input Z = 100Meg relate too?

I also note that the heartbeat sensor is integrated also to show the user the system is active.

How would i power the Arduino using this method? can it be powered from the same external power supply as the motor or would i need a separate one.
The reason i ask as i dont want to have it connected to my pc at all times.

The scale of trains i am modeling run on 5v so i intended on intercepting the power supply prior to it hitting the PWM track controllers. I presume i could still do this?

Ive also noted from your comments to my questions that the internal pull up resistors are built in for the relevant pins. hence why they are not shown/needed.

  • Some times we need to use an EXTERNAL pull-up OR pull-down resistor on digital pin in our projects.
    The maker of the 328 controller has give us the ability to use a built in pull_up resistor which we can enable, the 20-50k might be too large in some applications.
    We some times do not even need a pull_up.

what does the input Z = 100Meg relate too?

  • The GPIO pins have an impedance of over 100meg ohms to the outside world (devices connected to the pins).
    If we were to measure this with an ohm meter (don't do this) we would see 100M ohms or more on our meter.

I also note that the heartbeat sensor is integrated also to show the user the system is active.

  • I always us a Heartbeat LED on projects as it helps show if things are working.
    In the end, this LED can be removed/not used.

How would i power the Arduino using this method? can it be powered from the same external power supply as the motor or would i need a separate one.

  • You can power the Arduino for the external 5v power supply, often we make up a power only USB cable and plug the cable into the external supply.

The reason i ask as i dont want to have it connected to my pc at all times.

The scale of trains i am modeling run on 5v so i intended on intercepting the power supply prior to it hitting the PWM track controllers. I presume i could still do this?

  • Yes you can, however, be aware if there is electrical noise on that power supply due to the train motor it might interfere with the Arduino.

Test time:

  • How would you make the Heartbeat LED toggle every 1 second ?
  • How would you make it so the LED on pin 12 is ON all the time and go OFF for 2 seconds when the switch on pin 2 is closed ?

Yes. And I hadn't looked closely, one more thing that helps is to include all sources of power and how it is delivered to the components that need it, LEDs to motors.

But srsly, totally a compliment.

a7

Right what i first thought was correct i have just checked and it didn't work as intended. im just checking it over again now.