Model railroad turnout script

Seeking some help with a script that appears to be simple. I’m new to this technology and asking for some help. I’m a model railroader that would like to program my 24-channel Mini Maestro 24 for the turnouts using servos. I copied the example code and was able to switch one turnout. My question is really about programming the other switches. Do I need to add to the current script for each turnout or should they be incorporated to the original script. Below is my current script that was modified from the examples I have 10 more turnouts that need to be programed. I am also using momentary push buttons to activate the turnouts.

goto main_loop # Run the main loop when the script starts (see below).

# This subroutine returns 1 if the button is pressed, 0 otherwise.
# To convert the input value (0-1023) to a digital value (0 or 1) representing
# the state of the button, we make a comparison to an arbitrary threshold (500).
# This subroutine puts a logical value of 1 or a 0 on the stack, depending
# on whether the button is pressed or not.
sub button
18 get_position 500 less_than
return

# This subroutine uses the BUTTON subroutine above to wait for a button press,
# including a small delay to eliminate noise or bounces on the input.
sub wait_for_button_press
wait_for_button_open_10ms
wait_for_button_closed_10ms
return

# Wait for the button to be NOT pressed for at least 10 ms.
sub wait_for_button_open_10ms
get_ms # put the current time on the stack
begin
# reset the time on the stack if it is pressed
button
if
drop get_ms
else
get_ms over minus 10 greater_than
if drop return endif
endif
repeat

# Wait for the button to be pressed for at least 10 ms.
sub wait_for_button_closed_10ms
get_ms
begin
# reset the time on the stack if it is not pressed
button
if
get_ms over minus 10 greater_than
if drop return endif
else
drop get_ms
endif
repeat

# An example of how to use wait_for_button_press is shown below:

# Uses WAIT_FOR_BUTTON_PRESS to allow a user to step through
# a sequence of positions on servo 1.
main_loop:
begin
4000 frame

8000 frame
repeat

sub frame
wait_for_button_press
1 servo
return

Hello.

Thank you for posting the code you have so far. Could you describe your desired system with a little more detail? Are you trying to have a separate momentary pushbutton activate each turnout (e.g. you have 11 pushbuttons and you want each to activate a different servo)?

-Brandon

Brandon,
Thanks for the quick response!
I’m using the Mini Maestro 24. I’m using the controller board that handles momentary push buttons for 11 switches(servos). I have the push buttons on the few channels that won’t need the pull up resistors. I would like the momentary push button to control 1 turnout each. For example: Press button (input) on Channel 18 , that will send signal to the servo on channel 2. The servo channel will move the switch via linkage.
Answer to your question, YES, I’m trying to have a separate momentary pushbutton activate each turnout (e.g. you have 11 pushbuttons and you want each to activate a different servo)?

Writing scripts is something I never have tried, yet I think I can follow along using your examples and plenty of searches on Google.
I’m thinking that the script has to have the same as the subroutines given in the example.
Should I list all the buttons in a subroutine in a format like the example below:

sub button
18 get_position 500 less_than
return

Then do the same for the servos in a format as:

main_loop:
begin
4000 frame
8000 frame
repeat
sub frame
wait_for_button_press
1 servo
return

Once again,THANK YOU FOR ADVICE!!!
Russ

There is an example under the “Using multiple buttons or switches to control servos” heading of the “Example Scripts” section of the Maestro’s user’s guide that you might find very useful as a starting place. If you use a script like the one you posted above, the Maestro will be stuck waiting for a specific button to be pushed and not respond to the other buttons. Also, I recommend starting with getting two servos working, which can be much different than having just one servo; after you have two working, it should be much easier to add more. It seems like you might need the Maestro to react differently when the same button is pushed, based on the servo’s current position. You might try doing this by using the “GET_POSITION” command. Alternatively, you could have a state variable associated with each button. This method could be much more complicated. An example script for using a state variable with one button can be found in this post.

If you try making a script that works with two buttons and servos and have questions or problems, you can post the code here, and I would be happy to help.

-Brandon

Brandon,
The script that you suggested works well, yet after pushing the buttons the servos will move to the other side then returns back to the starting position…I need the servos to stay at the target versus returning back to the start.
Here is the script:

# When the script is not doing anything else,
# this loop will listen for button presses. When a button
# is pressed it runs the corresponding sequence.
begin
button_a if sequence_a endif
button_b if sequence_b endif
button_c if sequence_c endif
repeat

# These subroutines each return 1 if the corresponding
# button is pressed, and return 0 otherwise.
# Currently button_a is assigned to channel 1, 
# button_b is assigned to channel 2, and
# button_c is assigned to channel 3.
# These channels must be configured as Inputs in the
# Channel Settings tab.
sub button_a
18 get_position 500 less_than
return

sub button_b
19 get_position 500 less_than
return

sub button_c
20 get_position 500 less_than
return

# These subroutines each perform an arbitrary sequence
# of servo movements. You should change these to fit
# your application.
sub sequence_a
10 3 speed
4000 1 servo 1000 delay
 
8000 1 servo 500 delay

return

sub sequence_b
10 3 speed
4000 2 servo 900 delay
8000 2 servo 900 delay
return

sub sequence_c
10 3 speed
4000 3 servo 3000 delay
8000 3 servo 3000 delay
return

Almost there! thanks for your help,
Russ

It looks like you want the servo to go to one position the first time the button is pressed, and a different position when it is pressed again. To do something like this, you might try adding an “if” statement within your subroutine sequences. One way to do this is to have this if statement check whether the servo’s target position is above or below the neutral point, and change it to the other position accordingly. For example:

sub sequence_a
  
    10 1 speed

    1 get_position 6000 less_than
    if
        8000 1 servo 500 delay
    else
        4000 1 servo 1000 delay
    endif
    
return

By the way, I also noticed that you seem to be setting the speed setting for the servo on channel 3 in each of your sequences. It looks like this might be a typo and you want to set the speed for the specific servo being controlled in that particular sequence. Also, if you want the servo to always be at this speed, you can configure the speed setting in the “Channel Settings” tab of the Maestro Control Center so you do not need to include it in your script.

-Brandon

Brandon,
I have been away for awhile with the Model Railroad turnouts. “Due to hunny-doos!”
Your last reply, using the “get_position” works just the way I need it to, yet theres a glitch!
While running the script below, sub sequence a and b work great, yet to move the sub sequence c, I need to go back and press sub sequence button b prior to geting sub sequence servo to move. I’ve tried the pull-ups (18,19,20) and also the resister setup.
As stated, the script works just the way I need. Currently, using 1,2,3 Channels and the three pull-ups 18-19-20.
Thanks for the help in advanced,
Russell

# When the script is not doing anything else,
# this loop will listen for button presses. When a button
# is pressed it runs the corresponding sequence.
begin
button_a if sequence_a endif
button_b if sequence_b endif
button_c if sequence_c endif
repeat

# These subroutines each return 1 if the corresponding
# button is pressed, and return 0 otherwise.
# Currently button_a is assigned to channel 1, 
# button_b is assigned to channel 2, and
# button_c is assigned to channel 3.
# These channels must be configured as Inputs in the
# Channel Settings tab.
sub button_a
18 get_position 500 less_than
return

sub button_b
19 get_position 500 less_than
return

sub button_c
20 get_position 500 less_than
return

# These subroutines each perform an arbitrary sequence
# of servo movements. You should change these to fit
# your application.
sub sequence_a
10 1 speed
1 get_position 6000 less_than
if
8000 1 servo 100 delay
else
4000 1 servo 1000 delay
endif
return

sub sequence_b
10 1 speed
2 get_position 6000 less_than
if
8000 2 servo 100 delay
else
4000 2 servo 1000 delay
endif
return

sub sequence_c
10 1 speed
2 get_position 6000 less_than
if
8000 3 servo 100 delay
else
4000 3 servo 1000 delay
endif
return

July 7 maestro_settings.txt (5.94 KB)

I am not sure I completely understand the problem you are describing, but it looks like your “sequence_c” subroutine is using the position of the servo on channel 2 to set the position of the servo on channel 3. If this is the problem you are describing, you might try changing that line of code in your sequence_c subroutine to:

3 get_position 6000 less_than

If this does not correct the problem, could you try restating how you want it to work and what is actually happening?

-Brandon

Brandon,
Once again, you have come through! The added script addition now works all turnouts! Wahooo.
With that said, can you explain or show me an example or how to set a servo target. I need the inner rails, “called the points in railroad jargon” to move approx. 3/4 each way.
I attached a pic of the turnout that might help you.
I looked over examples, yet they are greek to me.
I have a case of Beersw or whatever you drink for all the help.
Thanks,
Russ


I am glad my suggestion helped you! I am not sure I understand what you are asking in your last post, but it sounds like you might be asking how to figure out what target positions to send to your servo to have it move where you want it to. If that is what you are asking, it will depend on several things like the particular servos you are using, the way they are mounted, and how they are connected to your system. I usually find it easiest to connect the servo to a Maestro and use the slider in the “Status” tab of the Maestro Control Center to find the target positions that correspond to the servo positions I am interested in. Please note that the value show in the “Target” field of the “Status” tab is in units of microseconds. To use these values in a script, you will need to convert it to quarter-microseconds by multiplying them by 4 (e.g. a target of 1500 microseconds in the “Status” tab corresponds to 6000 quarter-microseconds in a script).

-Brandon

Brandon, new here and to script and relay work, I hope this is not too old to reactivate, I have a Maestro 6 channel which I want to use to control 3 micro relays that will operate model rr switches from 3 momentary push buttons, 1 push moves rr switch from start to end positions, 2 push returns rr switch to start position.
I started by using the last script in this thread and immediately ran into trouble of my own making/knowledge lack.
The relays I am using have been set with the sliders to determine a minimum start point of 1312 ms and a max of 1696 ms, this is the total relay movement needed to operate the rr switches without binding them.

Using the control panel on a PC I have set the following basic parameters:
Positions 0 to 2 are inputs 1, 2, 3,
Positions 3 to 5 are Servos 1, 2, 3, with the following parameters, Rate at 63 htz, Min at 1312, Max at 1696, Smart Error at OFF, Speed at 10, serial is UART, fixed baud rate, all other parameters are default.

I will include the script I have modified for my use (note I have disabled some lines in order to get the first servo working) before going on.

I am now at the point where servo 1 will move from start position to end position but does not return on the 2nd push. I can operate the servo properly with the slider. I assume I have a serious issue with the way I converted the ms in the setup to script which means the script never reads the changed servo position. I have tried changing the script parameters but this either did nothing or made matters worse by having the servo go into a loop from start to end and back continuously until I stop the script.

I hope I am using the proper understandable terms and you are able to provide some guidance, here is my script.

# When the script is not doing anything else,
# this loop will listen for button presses. When a button
# is pressed it runs the corresponding sequence.
begin
      button_a if sequence_a endif
#button_b if sequence_b endif
#button_c if sequence_c endif
repeat
# These subroutines each return 1 if the corresponding
# button is pressed, and return 0 otherwise.
# Currently button_a is assigned to channel 0,
# button_b is assigned to channel 2, and
# button_c is assigned to channel 3.
# These channels must be configured as Inputs in the
# Channel Settings tab.
sub button_a
      0 get_position 500 less_than
return
#sub button_b
#1 get_position 500 less_than
#return
#sub button_c
#2 get_position 500 less_than
#return
#These subroutines each perform an arbitrary sequence
#of servo movements. You should change these to fit
# your application.
sub sequence_a
      0 get_position 6000 less_than
if
      8000 3 servo 500 delay
else
      4000 3 servo 1000 delay
endif
return
#sub sequence_b
#1 get_position 6000 less_than
#if
#8000 4 servo 100 delay
#else
#4000 4 servo 1000 delay
#endif
#return
#sub sequence_c
#2 get_position 6000 less_than
#if
#8000 5 servo 100 delay
#else
#4000 5 servo 1000 delay
#endif
#return

Thank you in advance for your help
Norm P.

Brandon, not sure what I did to create the bold caps font but it was not intentional.
Norm

Hello.

I am sorry you are having trouble getting your railroad switches to work. It looks like the subroutine sequence_a is commanding get_position from channel 0 (which it sounds like button_a is connected to), and it should be looking for the current position of the servo connected to channel 3, instead. So, updating your subroutine to the following should work:

sub sequence_a
      3 get_position 6000 less_than  # 0 --> 3
if
      8000 3 servo 500 delay 
else
      4000 3 servo 1000 delay
endif
return

Also, I am not sure if you noticed, but like Brandon said:

This means that sequence_a is commanding 8000/4 = 2000 microseconds as the Target at one end of your servo’s rotation, and 4000/4 = 1000 microseconds at the other. Both 2000 microseconds and 1000 microseconds are above and below your Max and Min servo channel settings of 1696 microseconds and 1312 microseconds, which would correspond to 6784 quarter-microseconds and 5248 quarter-microseconds. I expect the code I wrote above to still work for you since the Maestro will limit the Target values according to how the channel is configured. However, you might want to modify those values for yourself, since they are specific to your system, and could make any further troubleshooting easier.

By the way, you can surround your script with three backticks (```) to format it in a way that makes code easier to read. Like this:

# a single line of code comment

You can click the pencil icon at the bottom right of a post to see the characters used for specific formatting.

-Jon

Jon your explanation and repair fixed everything, all 3 now do exactly what I want them to do.

I understand my limits are set in the setup but am not clear what about which limits in the code to change and do not have time to try right now but will do so and I am sure I can figure it out.

Thank you for clear, concise help, especially on the Good Friday, Easter long weekend.
Cheers
Norm

Jon, I have played with the script settings and think I have them figured out.

Can the Maestro be used stand alone or does it need to be connected to the control panel via USB?
If it can, how do I set it up as when I disconnect the USB it still works but if I then turn off power and then turn it on the program does not work.

Norm

I am glad that explanation helped!

It sounds like you might need to check the “Run script on startup” checkbox at the top left of the Script tab of the Maestro Control Center.

It also sounds like you might have already tried disconnecting your Maestro from USB and powering it separately, but I’ll answer your question anyway: the Maestro does not need to be connected to a computer in order to control servos. However, since the Maestro’s processor will no longer be powered from the computer, you will need to find a way to supply it with power. You can see our instructions for doing that under the “Powering the Maestro” section of the Maestro’s user’s guide, and you might particularly be interested in the “One power supply” method.

-Jon

Thanks Jon that fixed it, I already had done the modification for one power supply.
I appreciate your help.
Norm