Start/stop a turntable with toggle pushbutton

Thanks mate, I will test the code tomorrow and give feedback. Enjoy the wekend.
Cheers

This code looks marvellous, will check operation tomorrow.

i imagine separate toggles for each turntable position or simply 2 buttons to advance to the next CW or CCW position

why DT?

sounds like you want to recognize a button press and continuously move the motor until another press?

it's not clear what you're attempting to do with the above code

  • button switches are typically wired between the pin and ground with the pin configure to have it's internal pullup resistor enabled, INPUT_PULLUP. the button pulls the pin LOW when pressed
  • looks like the code will recognize the the release of the button (going HIGH)
  • the internal condition will invoke doOneStep() if there's been a delay since the last press which is likely
  • while that condition sets moveButStateHIGH, it is immediately set LOW outside the condition
  • why isn't the delay (timeOfLastStep) simply in doOneStep()

looks like each button release invoked doOneStep(), but in an overly complicated way

consider, which toggles the run variable

const byte moveButPin = A1;

byte moveButStateOld;
byte run;

// -----------------------------------------------------------------------------
void
loop ()
{
    byte but = digitalRead(moveButPin);

    if (moveButStateOld != but)  {
        moveButStateOld = but;

        if (LOW == but)
            run = !run;
    }

    if (run)
        doOneStep ();
}

// -----------------------------------------------------------------------------
void
doOneStep ()
{
    Serial.println (__func__);
    delay (100);
}

// -----------------------------------------------------------------------------
void
stepperIdle ()
{
    Serial.println (__func__);
}

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

    pinMode (moveButPin, INPUT_PULLUP);
    moveButStateOld = digitalRead (moveButPin);
}

Hi John,

I want to reassure that my imagination of what you mean with turntable is correct
Do you mean something that is similar to this

There is a rotating plate with rails on it.
The control-unit (arduino) shall rotate the turntable from a well defined position to the next position.

The way you want to achieve this is:

  • press a button turning starts
  • press button again turning stops

or do you want it to work this way:
like in the picture shown the turntable is on position 8 to park the locomotive in shelter 8
After the green locomotive on the turntable has been driven into shelter 8.

  • press a button turning starts and automatically stops at position 9
  • press a button turning starts and automatically stops at position 10
  • locomotive in shelter 10 drives onto turntable
  • press button again turning starts and automatically stops at (pos 11)
  • press button again turning starts and automatically stops at (pos 12)
    .....
    press button turning starts and automatically stops at position 17
    drive locomotive off the turntable

Is this a correct principle of your turntable?
Which kind of controlling do you want to have?

Steppermotors are able to stop at the very exact positions
so the tracks of the turntable line up with the tracks towards the shelters or towards to rest of the tracks.

best regards Stefan

transforming a momentary push-button which works this way:

only as long as button is pressed down contact is closed
if button is released contact opens.

Needs two things:

  1. state-change-detection
    When does the change from
    .
    unpressed_contact_opened
    .
    to
    .
    pressed_contact_closed

happen.
.
2. In the moment this happens
change the state of a "keep-the-state-until-next-buttonpress"-variable from
.
OFF to ON
or from
.
ON to OFF
.

This means the mechanical push-and-rest-button:

push and release button: button stays in mode contact closed
push and release button: button stays in mode contact opened
is replaced by software

Here is a pretty well commented demo-code that demonstrates this in principle
IMHO the advantage of this code is that there is an easy to find place in the code where you have to insert the action that shall be activated or DE_activated.

If somebody who is new to this kind of writing code wants to dive into it
the comments will help to explain how the code works.

best regards Stefan

Wow, lots of examples here. I thought I'd mention that the Toggle Library is the only library that makes all of this (state-change detection, industrial strength debouncing both on press and release, and keep-the-state-until-next-buttonpress transparent to the end user so they can focus on their code and not technicalities with making a pushbutton work.

In the example I've provided, It's not apparent that any of this was was taken care of, because the user code looks the same and has the same flow to it. However, guaranteed, the momentary push button will act as a true toggle switch, no matter how fast and how many iterations the loop does.

Transitions are recorded and can be accessed multiple times until the next transition occurs. The library requires less memory than most and will have the best response (onPress and onRelease) while not having issue with up to 2 anomalous (spurious) glitches.

micros() is used for setting the sample period (default 5000μs) and can be set lower if working with higher data rates (like flow meters, rpm pulses or debouncing 8-bit data).

The debounce algorithm was inspired by how its done with hardware ICs and VHDL and FPGAs, except the Toggle Library debounces symmetrically on both falling and rising state changes. If the button is wired for pulldown mode, the debounce algorithm remains and works the same.

[quote="dlloyd, post:6, topic:998544"]

#include <Toggle.h>

....[/quote]

indeed very easy to use. Hides away all the details from the user.

For a real newcomer the sourcecode must have a lot of basic comments to explain what is important.
If a library becomes popular or not depends a lot from how easy is it to modify. And does the source-code teach important basics.

So here is a version of your democode with additional commands and selfexplaining names

// you can install the toogle.h-library with the library-manager 
// inside the Arduino-IDE which is the most comfortable way
// or through downloading the ZIP-file from here https://github.com/Dlloydev/Toggle
// and installing as a ZIP-library
#include <Toggle.h> // this line adds the code inside the file Toggle.h that makes it work

const byte myTestButton  =  4;
const byte OnBoardLedPin = 13; // take Arduino-Uno / Mega Onboard LED 

byte LED_State;

Toggle myButton(myTestButton); // create object that uses IO-pin myTestButton 

void setup() {
  myButton.begin(myTestButton); // start the button-object 
  pinMode(OnBoardLedPin, OUTPUT);
}

void loop() {
  // the function poll() must be called very often 
  // to read in and update the actual state of the button
  myButton.poll(); 

  // the function toggle() returns the state of a SOFWTARE EMULATED latched button
  // after a first  press of the button the function returns a "1" like I'm in ON-mode
  // after the next press of the button the function returns a "0" like I'm in OFF-mode
  // after the next press of the button the function returns a "1" like I'm in ON-mode
  // etc. etc. etc. etc. etc. ....
  // this means that only AFTER releasing the button and then
  // pressing DOWN the button NEW 
  // the value that function toggle() returns is CHANGED 
  LED_State = myButton.toggle();
  digitalWrite(OnBoardLedPin, LED_State);
}

best regards Stefan

1 Like

Hi Stefan
Thanks for taking an interest. I note that your picture reflects an old Marklin turntable and locomotives - probably 1950s/60s.
Yes, I do anticipate operating a turntable similar to this one, although with less tracks, but rather than move from track 1 to track 2 to track 3 etc, I want to store the exact position of each track (in practice the tracks are not evenly spaced) and even a 0.5mm misalignment can cause derailment. Then I want to select the track to move to, and the table should take the shortest route to get there. Obviously a reference point is required, maybe using a Hall effect transistor, and then the table must rotate to the values stored in an EPROM.
Any thoughts?

turntable precision requires a calibration point that is less wide than the thickness of a track. i believe walthers uses an optical sensor in the turntable wall.

the walthers turntable uses a DC motor that has a drive gear that rides on a geared ledge in the turntable and an encoder to track position of the DC motor shaft

a calibration mode resets the zero position whenever the bridges crosses the calibration point and can be triggered thru the control panel whenever the bridge mislocated.

a programming mode allows manual movement of the bridge to align it with each track and store that position. a pair of 7-segment displays or some other device can be used with just a few buttons to drive the display

an operation mode allows the desired position to be selected as well as reversing the position 180 deg

instead of maintaining position with an encoder, your mechanism would use the # of steps. both would be relative to the calibration point

Hi Doug
I note that you changed the stepnr+1 to stepnr+2. Since I want half steps to get better resolution (4096 steps per revolution, I use +1 and remainder %8).
When I first tried your sketch the led lit and motor turned on power up and would not turn off. I changed movebutton.toggle() to toggle(1) and then nothing happened until the button was pressed, and then it worked! But still would not turn off.
I changed the 'while' function to 'if..else' and then it worked perfectly.

[code]
void loop() {
  moveButton.poll();
  if (moveButton.toggle(1) == LOW) {
    digitalWrite(redLedPin, HIGH);
    if ((millis() - timeOfLastStep) > timeOfEachStep) { //micros is the number of milliseconds since the program started
      timeOfLastStep = millis();
      doOneStep();
    }
  }
  else
    stepperIdle();
}
[/code]

Thanks so much for the help.
I understand that your expertise is switches, but have you ever seen a sketch that converts a potentiometer into a centre-OFF, speed CCW turning left of centre and speed CW turning right?
It would be like dividing a pot into 3 sections - 1 to 5 rpm CCW, 6 and 7 OFF, 8 to 13rpm CW when mapping an analog INPUT. A stepper has to be mapped 0 to 1024, to 1 to 13.

Oh, I grabbed the code from post#2 thinking it was unchanged from the first post, but formatted as code.

I was wondering about the while loop (blocking) ... a small change could fix that, but your non-blocking 'if..else' is definitely much better for the performance of your code.

A sketch (code) like that should be quite simple to achieve. Just wondering if you need 13 definitive steps (like a 13-position rotary switch) for 1 rpm resolution, or if you need the continuously variable characteristic of the potentiometer, or maybe using a rotary encoder as a control?

Another Idea might be just to use a single momentary push button and the pressCode() function where (for example):

  • 1-5 Short presses for CCW returns code 1 to 5
  • 1-6 Fast presses for CW returns code F1 to F6
  • 1 long press goes to "centre-OFF" returns code 10

Its not difficult to reliably return a specific code (try the example for practice) For a push button, I just use a jumper wire and touch the grounded USB shell on an UNO!

Tip: For short presses just make the first within 0.4 to 1 sec, then rapidly do the remaining presses to save time (they'll all be recognized as short presses).

Your existing push button could do this (225 codes available), just need to add the cases to the switch-case.

Glad you have it working!
Regards,
David

If you have reached a certain knowledge-level about writing code and electronics any functionality is just a matter of taking the time sometimes beeing 30 minutes, sometime 300 minutes or in rare cases 3000 minutes to make it work.

At the beginning you had a specific question which is very good. It is much easier to answer specific questions than generalised questions.

It is always a good idea to write an overview about the project. And to write a description of the wanted functionality. This description shall be without code-words. Not using code-words assures that misconceptions about how certain functions work are not included in the description.
What I have understood so far is you want to have a "teach-in-mode.
Press a button to make the turntable rotate, press the button again to stop.
choose rotatingdirection clockwise (CW) / counterclockwise (CCW)
repeat this process until the alignment between tracks on the turntable and outside matches very good.
If it matches very good save this position as a relative coordinate to the reference-point to the EEPROM.

For detecting the referencepoint there are a lot of possabilities:

  • fork-light-barrier
  • reed-relay
  • halleffect-sensor
  • microswitch
  • inductive proximity sensor
  • capacitive proximity sensor

The only thing that is important is, that the reference-point-sensor switches always at the same rotation-position within a precision of at least 0,1 mm.

Here it would be good if you post a picture of your turntable. Topview and bottomview.

There are a lot of options how to realise operation-mode: rotate turntable to position A,B,C etc.

Staying very close to the originators code would require reproducing the originators setup down to every dammed detail. Sometimes this requires even using the exact same type of microcontroller.
Can you please post a link to this youtube-video?

The more detailed information you provide the better the support becomes.

best regards Stefan

Since your ultimate goal involves a lot of building on simpler versions of turning a turntable,
would it be easier at this stage to keep the MCU/code totally independent of the momentary switch, ie: just wire the switch across the +5v wire to the stepper wherever it suits you?

The model train world prefers pushbutton to SPDT switches for turntable rotation, so I will keep going with the push/toggle concept.
Thanks for the input.

Hi Stefan
I really appreciate your involvement so I respond as follows.
I want to be able to align the turntable very closely to 4 or more destination positions, rotating CW and CCW, controlling the speed to ensure accuracy, and, when aligned, then storing the destination positions for future use - push a button for destination 4 and it turns to 4 the quickest way.

The tutorials I am following start here:

Fun with Arduino 31 Stepper Motor with 4 Input Driver, using a Function() – Fun with Arduino (wordpress.com)

and go on to Tutorial 40 or so.

Below are pictures of the 'demo' turntable I have built, and then a picture of what the final model will look like when I have the control sorted. Working with the final model at this stage is not advisable since the parts are very delicate. Yes, I know that in the days of this turntable the rotation was manual, but on a model train layout it is more than likely the turntable is out of arms reach, hence remote control.



My apologies for being unclear.
The stepper I am using (28BYJ48) includes a 64:1 gearbox so at full step there are 2048 steps per revolution. At half step, double this.
The actual motor has a maximum of 13 to 15rpm (say 13) so what I want to map is the 1024 bits to 1 to 13 rpm. 1 motor rpm at 4096 steps will give sufficient accuracy.
What I had in mind was to remove the 'move' button and a subsequent 'direction' button and use a pot to do all three - move, direction, speed.
In the mapping, if the pot is at the central position (when rpm would be 7) we make it 'idle'. From 6 to 0 rpm position the direction is CW and the speed increases from idle to 13. From 7 to 13rpm positions, rotation is CCW and speed increases from 0 to 13.
Doing it this way would save a lot of hardware and manual manipulation.

Hello johnturntable
Did you have integrate a slip ring to energize the table ?

Have a nice day and enjoy coding in C++.
Дайте миру шанс!

Hi David (sorry I assumed Doug),
Below is a new version of what I am trying to do - momentary push for movement and toggle push for direction. It works well but direction led only lights dimly.
What am I doing wrong?

[code]
//Turntable stepper (ex Dutch) with push to rotate, toggle for direction

#include <Toggle.h>

//define stepper pins
const int stepperPin1 = A1;
const int stepperPin2 = A2;
const int stepperPin3 = A3;
const int stepperPin4 = A4;

//define move button and led pins
const int stepperMovePin = 9; //when pushed, pullup resistor inverts logic (=LOW, 0, ON), stepper turns
const int redLedPin = 5; //led is HIGH (1, ON) when stepper is running

//define direction button and led pins
const int dirButPin = 8;
const int dirLedPin = 4;

//create toggle object
Toggle dirBut(dirButPin);

//define stepper constants
const float stepsPerMotorRevolution = 32;  //for the 28BYJ stepper this is 32
const float gearboxReduction = 64;  //the 28BYJ is fitted with a 64:1 reduction gearbox
const float stepsPerStepperRevolution = stepsPerMotorRevolution * gearboxReduction;  //Stepper and gearbox specific, = 2048 for 28BYJ-48

//define stepper variables
//if stepper turns at x Rpm, then time for one revolution is 60/x seconds, or 60 000 000/x micros (us)
//if each revolution is y stepsPerStepperRevolution, then each step takes 60 000 000/x/y us
int stepperRpm = 4; // max speed of stepper is 12 rpm, else pulses get lost
unsigned long stepInterval;  //time from one step to the next - step interval
unsigned long timeOfLastStep;
byte stepnr;  //stepper phase counter

void doOneStep() {
  digitalWrite(redLedPin, HIGH);
  if (dirBut.toggle() == LOW) {
    stepnr++;
    digitalWrite(dirLedPin, HIGH);
  }
  else stepnr--;
  digitalWrite(dirLedPin, LOW);
  stepnr = stepnr % 8;  // counts and remembers in which of the 8 phases the stepper is
  switch (stepnr) {
    case 0:
      digitalWrite(stepperPin1, HIGH);
      digitalWrite(stepperPin2, LOW);
      digitalWrite(stepperPin3, LOW);
      digitalWrite(stepperPin4, LOW);
      break;
    case 1:
      digitalWrite(stepperPin1, HIGH);
      digitalWrite(stepperPin2, HIGH);
      digitalWrite(stepperPin3, LOW);
      digitalWrite(stepperPin4, LOW);
      break;
    case 2:
      digitalWrite(stepperPin1, LOW);
      digitalWrite(stepperPin2, HIGH);
      digitalWrite(stepperPin3, LOW);
      digitalWrite(stepperPin4, LOW);
      break;
    case 3:
      digitalWrite(stepperPin1, LOW);
      digitalWrite(stepperPin2, HIGH);
      digitalWrite(stepperPin3, HIGH);
      digitalWrite(stepperPin4, LOW);
      break;
    case 4:
      digitalWrite(stepperPin1, LOW);
      digitalWrite(stepperPin2, LOW);
      digitalWrite(stepperPin3, HIGH);
      digitalWrite(stepperPin4, LOW);
      break;
    case 5:
      digitalWrite(stepperPin1, LOW);
      digitalWrite(stepperPin2, LOW);
      digitalWrite(stepperPin3, HIGH);
      digitalWrite(stepperPin4, HIGH);
      break;
    case 6:
      digitalWrite(stepperPin1, LOW);
      digitalWrite(stepperPin2, LOW);
      digitalWrite(stepperPin3, LOW);
      digitalWrite(stepperPin4, HIGH);
      break;
    case 7:
      digitalWrite(stepperPin1, HIGH);
      digitalWrite(stepperPin2, LOW);
      digitalWrite(stepperPin3, LOW);
      digitalWrite(stepperPin4, HIGH);
      break;
  }
}

void stepperIdle() {
  digitalWrite(stepperPin1, LOW);
  digitalWrite(stepperPin2, LOW);
  digitalWrite(stepperPin3, LOW);
  digitalWrite(stepperPin4, LOW);
  digitalWrite(redLedPin, LOW);
}

void setup() {
  dirBut.begin(dirButPin);
  pinMode(stepperMovePin, INPUT_PULLUP);
  pinMode(stepperPin1, OUTPUT);
  pinMode(stepperPin2, OUTPUT);
  pinMode(stepperPin3, OUTPUT);
  pinMode(stepperPin4, OUTPUT);
  pinMode(redLedPin, OUTPUT);
  digitalWrite(redLedPin, LOW);
  pinMode(dirButPin, INPUT_PULLUP);
  pinMode(dirLedPin, OUTPUT);
  stepInterval = (60000000UL / stepperRpm) / stepsPerStepperRevolution; //approx 3000us
}

void loop() {
  dirBut.poll();
  if (digitalRead(stepperMovePin) == LOW) {  //if button is pushed (LOW)
    if ((micros() - timeOfLastStep) > stepInterval) {  //micros is the number of us since the program started
      timeOfLastStep = micros();
      doOneStep();
    }
  }
  else
    stepperIdle();
}
[/code]

Hi @johnturntable,
a very interesting project. I did something very similar, but it's not finished yet. I think you should consider some additional points:

  • This small stepper isn't really strong.
  • The resolution of one step without an additional gear is not sufficient in my opinion. The resolution should be higher than the accuracy you need
  • To get a smooth movement at an acceptable speed you should use acceleration and deceleration when moving from point to point.
  • Because of the backlash of the gear, the exact positions - counted in steps from the reference point - are different when moving CW or CCW. The same is true when setting the reference point - you must take into account wether its moving CW or CCW when passing the reference point.
  • If you switch off the motor completely when not moving, you may get an additional inaccuracy, because it may lose at least one step when switching off and on again. You should position your reference point in such a way that it is passed fairly often.

Here are some photos of my test setup:
Upper
Lower
Gear

Even if you actually want to stick to your first attempt, consider using a library to move the stepper. This makes things much more easy, especially when using acceleration and deceleration. My MobaTools library was original developed with such things in mind. (Moba=Modellbahn=model railroad :wink: ).
The library also contains a class to manage push buttons and a class to make timing tasks more easy.