6.c. Example Scripts

<h4>Getting started: blinking an LED</h4> <p>The following script will cause the red LED on the Maestro to blink once per second:</p> <pre name="code" class="maestro">&#x000A;# Blinks the red LED once per second.&#x000A;begin&#x000A; led_on&#x000A; 100 delay&#x000A; led_off&#x000A; 900 delay&#x000A;repeat</pre> <p>It is a good idea to try stepping through this script before doing anything further with scripts on the Maestro. In particular, pay attention to how the command &#8220;100&#8221; puts the number 100 on the stack, and the DELAY command consumes that number. In the Maestro scripting language, arguments to commands always need to be placed on the stack <em>before</em> the commands that use them, which makes the language seem backwards compared to other languages. It also means that you can arrange your code in a variety of different ways. For example, this program is equivalent to the one above:</p> <pre name="code" class="maestro">&#x000A;# Blinks the red LED once per second.&#x000A;begin&#x000A; 900 100&#x000A; led_on delay&#x000A; led_off delay&#x000A;repeat</pre> <p>The numbers are placed on the stack at the beginning of the loop, then consumed later on in execution. Pay attention to the order of the numbers used here: the 900 goes on the stack <em>first</em>, and it is used <em>last</em>.</p> <h4>A simple servo sequence</h4> <p>The following script shows how to direct servo 0 to five different positions in a loop.</p> <pre name="code" class="maestro">&#x000A;# Move servo 0 to five different positions, in a loop.&#x000A;begin&#x000A; 4000 0 servo # set servo 0 to 1.00 ms&#x000A; 500 delay&#x000A; 5000 0 servo # 1.25 ms&#x000A; 500 delay&#x000A; 6000 0 servo # 1.50 ms&#x000A; 500 delay&#x000A; 7000 0 servo # 1.75 ms&#x000A; 500 delay&#x000A; 8000 0 servo # 2.00 ms&#x000A; 500 delay&#x000A;repeat</pre> <p class="note">The serial mode must <strong>not</strong> be set to detect baud rate for this script to work. In detect baud rate mode, the Maestro does not enable any of the servo outputs until the start byte has been received.</p> <p>Note that the servo positions are specified in units of 0.25&nbsp;&mu;s, so a value of 4000 corresponds to 1&nbsp;ms. The text after the # is a <em>comment</em>; it does not get programmed on to the device, but it can be useful for making notes about how the program works. Good comments are essential for complicated programs. It is important to remember the DELAY commands; without these, the script will not wait at all between servo commands, running the loop hundreds of times per second.</p> <h4>Compressing the sequence</h4> <p>The program above takes 58 bytes of program space: 11 bytes for each servo position and 3 for the loop. At this rate, we could store up to 92 servo positions in the 1024-byte memory of the Micro Maestro, or 744 servo positions in the 8192-byte memory of the Mini Maestros. To get the most out of the limited memory, there are a variety of ways to compress the program. Most important is to make use of <em>subroutines</em>. For example, since we repeat the instructions &#8220;0 servo 500 delay&#8221; several times, we can move them into a subroutine to save space. At the same time, this simplifies the code and makes it easier to make future modifications, such as changing the speed of the entire sequence.</p> <pre name="code" class="maestro">&#x000A;# Move servo 0 to five different positions, in a loop.&#x000A;begin&#x000A; 4000&#x000A; frame&#x000A; 5000&#x000A; frame&#x000A; 6000&#x000A; frame&#x000A; 7000&#x000A; frame&#x000A; 8000&#x000A; frame&#x000A;repeat&#x000A;&#x000A;sub frame&#x000A; 0 servo&#x000A; 500 delay&#x000A; return</pre> <p>Using the subroutine brings the script down to 31 bytes: 4 per position and 11 bytes of overhead for the loop and to define FRAME. We can go further: inspecting the compiled code shows that putting each number on the stack requires 3 bytes: one byte as a command, and two for the two-byte number. Numbers from 0 to 255 can be loaded onto the stack with just two bytes. Suppose in our application we do not need the full 0.25&nbsp;&mu;s resolution of the device, since all of our settings are multiples of 100. Then we can use smaller numbers to save another byte:</p> <pre name="code" class="maestro">&#x000A;# Move servo 0 to five different positions, in a loop.&#x000A;begin&#x000A; 40 frame&#x000A; 50 frame&#x000A; 60 frame&#x000A; 70 frame&#x000A; 80 frame&#x000A;repeat&#x000A;&#x000A;# loads a frame specified in 25 us units&#x000A;sub frame&#x000A; 100 times&#x000A; 0 servo&#x000A; 500 delay&#x000A; return</pre> <p>This program is 29 bytes long, with 3 bytes used per position and 14 bytes of overhead. Note that we could get the same efficiency if we used the SERVO_8BIT command, which takes a one-byte argument from 0 to 254. We can go even smaller by putting all of the numbers together:</p> <pre name="code" class="maestro">&#x000A;# Move servo 0 to five different positions, in a loop.&#x000A;begin&#x000A; 80 70 60 50 40&#x000A; frame frame frame frame frame&#x000A;repeat&#x000A;&#x000A;# loads a frame specified in 25 us units&#x000A;sub frame&#x000A; 100 times&#x000A; 0 servo&#x000A; 500 delay&#x000A; return</pre> <p>If you step through this version program, you will also notice that all five numbers are placed on the stack in a single step: this is because the compiler can use a single command to put multiple numbers on the stack. Using a single command for multiple numbers saves space: we are now down to just 26 bytes. Only 12 bytes are used for the 5 frames, for an average of 2.4 bytes per frame. This is probably compact enough &#8211; by duplicating this structure we could fit 420 different positions into the 1024-byte program memory of the Micro Maestro. However, the code can get even smaller. Consider this script, which uses 31 frames to make a smooth back-and-forth motion:</p> <pre name="code" class="maestro">&#x000A;# Moves servo in a sine wave between 1 and 2 ms.&#x000A;begin &#x000A; 60 64 68 71 74 77 79 80 80 79 78 76 73 70 66 62&#x000A; 58 54 50 47 44 42 41 40 40 41 43 46 49 52 56 &#x000A; all_frames&#x000A;repeat&#x000A;&#x000A;sub all_frames&#x000A; begin&#x000A; depth&#x000A; while&#x000A; 100 times&#x000A; 0 servo&#x000A; 100 delay&#x000A; repeat&#x000A; return</pre> <p>In this version of the code, we have rewritten the FRAME subroutine, using the DEPTH command to automatically load frames from the stack until there are none left. This program uses 34 bytes to store 31 frames, for an average of just 1.1 bytes per frame. We could store a sequence containing 900 different positions in the memory of the Micro Maestro using this kind of script.</p> <h4>Making smooth sequences with GET_MOVING_STATE</h4> <p>Speed and acceleration settings can be used to make smooth motion sequences with the Maestro. However, a common problem is that you do not know how much you need to delay between frames to allow the servo to reach its final position. Here is an example of how to use the built-in function GET_MOVING_STATE to make a smooth sequence, instead of DELAY:</p> <pre name="code" class="maestro">&#x000A;# This example uses speed and acceleration to make a smooth&#x000A;# motion back and forth between 1 and 2 ms.&#x000A;3 0 acceleration&#x000A;30 0 speed&#x000A;&#x000A;begin&#x000A; 4000 0 servo # set servo 0 to 1.00 ms&#x000A; moving_wait&#x000A; 8000 0 servo # 2.00 ms&#x000A; moving_wait&#x000A;repeat&#x000A;&#x000A;sub moving_wait&#x000A; begin&#x000A; get_moving_state&#x000A; while&#x000A; # wait until it is no longer moving&#x000A; repeat&#x000A; return</pre> <p>GET_MOVING_STATE returns a 1 as long as there is at least one servo that is limited by a speed or acceleration setting still moving, so you can use it whenever you want to wait for all motion to stop before proceeding to the next step of a script.</p> <h4>Using an analog input to control servos</h4> <p>An important feature of the Maestro is that it can be used to read inputs from sensors, switches, or other devices. As a simple example, suppose we want to use a potentiometer to control the position of a servo. For this example, connect the potentiometer to form a voltage divider between 5V and 0, with the center tap connected to channel 1. Configure channel 1 to be an input, and examine the signal on the Status tab of the Maestro Control Center. You should see the position indicator vary from 0 to 255&nbsp;&mu;s as you turn the potentiometer from one side to the other. In your script, this range corresponds to numbers from 0 to 1023. We can scale this number up to approximately the full range of a servo, then set the servo position to this number, all in a loop:</p> <pre name="code" class="maestro">&#x000A;# Sets servo 0 to a position based on an analog input.&#x000A;begin&#x000A; 1 get_position # get the value of the pot, 0-1023&#x000A; 4 times 4000 plus # scale it to 4000-8092, approximately 1-2 ms&#x000A; 0 servo # set servo 0 based to the value&#x000A;repeat</pre> <p>Alternatively, you might want the servo to go to discrete positions depending on the input value:</p> <pre name="code" class="maestro">&#x000A;# Set the servo to 4000, 6000, or 8000 depending on an analog input.&#x000A;begin&#x000A; 1 get_position # get the value of the pot, 0-1023&#x000A; dup 300 less_than&#x000A; if&#x000A; 4000 # go to 4000 for values 0-299&#x000A; else&#x000A; dup 600 less_than&#x000A; if&#x000A; 6000 # go to 6000 for values 300-599&#x000A; else&#x000A; 8000 # go to 8000 for values 600-1023&#x000A; endif&#x000A; endif&#x000A; 0 servo&#x000A; drop # remove the original copy of the pot value&#x000A;repeat</pre> <p>The example above works, but when the potentiometer is close to 300 or 600, noise on the analog-to-digital conversion can cause the servo to jump randomly back and forth. A better way to do it is with hysteresis:</p> <pre name="code" class="maestro">&#x000A;# Set the servo to 4000, 6000, or 8000 depending on an analog input, with hysteresis.&#x000A;begin&#x000A; 4000 0 300 servo_range&#x000A; 6000 300 600 servo_range&#x000A; 8000 600 1023 servo_range&#x000A;repeat&#x000A;&#x000A;# usage: &lt;pos&gt; &lt;low&gt; &lt;high&gt; servo_range&#x000A;# If the pot is in the range specified by low and high,&#x000A;# keeps servo 0 at pos until the pot moves out of this&#x000A;# range, with hysteresis.&#x000A;sub servo_range&#x000A; pot 2 pick less_than logical_not # &gt;= low&#x000A; pot 2 pick greater_than logical_not # &lt;= high&#x000A; logical_and&#x000A; if&#x000A; begin&#x000A; pot 2 pick 10 minus less_than logical_not # &gt;= low - 10&#x000A; pot 2 pick 10 plus greater_than logical_not # &lt;= high + 10&#x000A; logical_and&#x000A; while&#x000A; 2 pick 0 servo&#x000A; repeat&#x000A; endif&#x000A; drop drop drop&#x000A; return&#x000A;&#x000A;sub pot&#x000A; 1 get_position&#x000A; return</pre> <p>This example uses one range for deciding where to go when making a transition, then it waits for the servo to leave a slightly larger range before making another transition. As long as the difference (10 in this example) is larger than the amount of noise, this will prevent the random jumping.</p> <p>Note that this example will only work if you connect your potentiometer to one of the analog input capable channels (channels 0&ndash;11). The inputs on the other channels are digital.</p> <h4>Using a button or switch to control servos</h4> <p>It is possible to connect a button or switch to a Maestro and detect the state of the button in your script. The script below moves a servo through a predefined sequence of movements, advancing to the next step each time the button is pushed. It uses channel 0 for the button and channel 1 for the servo.</p> <p>The button channel must be configured as an input and wired correctly. See <a href="/docs/0J40/7.b">Section 7.b</a> for instructions on how to wire a button to your Maestro using a pull-up resistor, so that the input is normally high, and when the button is pressed it goes low.</p> <pre name="code" class="maestro">&#x000A;goto main_loop # Run the main loop when the script starts (see below).&#x000A;&#x000A;# This subroutine returns 1 if the button is pressed, 0 otherwise.&#x000A;# To convert the input value (0-1023) to a digital value (0 or 1) representing&#x000A;# the state of the button, we make a comparison to an arbitrary threshold (500).&#x000A;# This subroutine puts a logical value of 1 or a 0 on the stack, depending&#x000A;# on whether the button is pressed or not.&#x000A;sub button&#x000A; 0 get_position 500 less_than&#x000A; return&#x000A;&#x000A;# This subroutine uses the BUTTON subroutine above to wait for a button press,&#x000A;# including a small delay to eliminate noise or bounces on the input.&#x000A;sub wait_for_button_press&#x000A; wait_for_button_open_10ms&#x000A; wait_for_button_closed_10ms&#x000A; return&#x000A;&#x000A;# Wait for the button to be NOT pressed for at least 10 ms.&#x000A;sub wait_for_button_open_10ms&#x000A; get_ms # put the current time on the stack&#x000A; begin&#x000A; # reset the time on the stack if it is pressed&#x000A; button&#x000A; if&#x000A; drop get_ms&#x000A; else&#x000A; get_ms over minus 10 greater_than&#x000A; if drop return endif&#x000A; endif&#x000A; repeat&#x000A;&#x000A;# Wait for the button to be pressed for at least 10 ms.&#x000A;sub wait_for_button_closed_10ms&#x000A; get_ms&#x000A; begin&#x000A; # reset the time on the stack if it is not pressed&#x000A; button&#x000A; if&#x000A; get_ms over minus 10 greater_than&#x000A; if drop return endif&#x000A; else&#x000A; drop get_ms&#x000A; endif&#x000A; repeat&#x000A;&#x000A;# An example of how to use wait_for_button_press is shown below:&#x000A;&#x000A;# Uses WAIT_FOR_BUTTON_PRESS to allow a user to step through&#x000A;# a sequence of positions on servo 1.&#x000A;main_loop:&#x000A;begin&#x000A; 4000 frame&#x000A; 5000 frame&#x000A; 6000 frame&#x000A; 7000 frame&#x000A; 8000 frame&#x000A;repeat&#x000A;&#x000A;sub frame&#x000A; wait_for_button_press&#x000A; 1 servo&#x000A; return</pre> <p>Just like the sequencing examples above, the script steps through a sequence of frames, but instead of a timed delay between frames, this example waits for a button press. The WAIT_FOR_BUTTON_PRESS subroutine can be used in a variety of different scripts, whenever you want to wait for a button press. You could also expand this example to allow multiple buttons, continuous motion, or a variety of other types of button control.</p> <h4>Using multiple buttons or switches to control servos</h4> <p>This script demonstrates how to connect your Maestro to multiple buttons. When a button is pressed, it runs the corresponding sequence.</p> <pre name="code" class="maestro">&#x000A;# When the script is not doing anything else,&#x000A;# this loop will listen for button presses. When a button&#x000A;# is pressed it runs the corresponding sequence.&#x000A;begin&#x000A; button_a if sequence_a endif&#x000A; button_b if sequence_b endif&#x000A; button_c if sequence_c endif&#x000A;repeat&#x000A;&#x000A;# These subroutines each return 1 if the corresponding&#x000A;# button is pressed, and return 0 otherwise.&#x000A;# Currently button_a is assigned to channel 0, &#x000A;# button_b is assigned to channel 1, and&#x000A;# button_c is assigned to channel 2.&#x000A;# These channels must be configured as Inputs in the&#x000A;# Channel Settings tab.&#x000A;sub button_a&#x000A; 0 get_position 500 less_than&#x000A; return&#x000A;&#x000A;sub button_b&#x000A; 1 get_position 500 less_than&#x000A; return&#x000A;&#x000A;sub button_c&#x000A; 2 get_position 500 less_than&#x000A; return&#x000A;&#x000A;# These subroutines each perform an arbitrary sequence&#x000A;# of servo movements. You should change these to fit&#x000A;# your application.&#x000A;sub sequence_a&#x000A; 4000 3 servo 1000 delay&#x000A; 6000 3 servo 500 delay&#x000A; return&#x000A; &#x000A;sub sequence_b&#x000A; 8000 4 servo 900 delay&#x000A; 7000 4 servo 900 delay&#x000A; 6000 4 servo 900 delay&#x000A; return&#x000A;&#x000A;sub sequence_c&#x000A; 10 4 speed&#x000A; 7000 4 servo 3000 delay&#x000A; 6000 4 servo 3000 delay&#x000A; return</pre> <p>Please note that this script does not do multi-tasking. If a sequence is running, the script will not detect other button presses until the sequence is done. It is possible to make the buttons operate independently, but the script would need to be much more complicated. Depending on how skilled you are at writing scripts, you might prefer to use multiple Maestros instead.</p> <h4>Long delays</h4> <p>The longest delay possible with the DELAY command is approximately 32 seconds. In some cases, you will want to make delays much longer than that. Here is an example that shows how delays of many seconds or minutes can be accomplished:</p> <pre name="code" class="maestro">&#x000A;# Moves servo 0 back and forth, with a delay of 10 minutes between motions.&#x000A;begin&#x000A; 4000 0 servo&#x000A; 10 delay_minutes&#x000A; 8000 0 servo&#x000A; 10 delay_minutes&#x000A;repeat&#x000A;&#x000A;# delay by a specified number of seconds, up to 65535 s&#x000A;sub delay_seconds&#x000A; begin dup while # check if the count has reached zero&#x000A; 1 minus 1000 delay # subtract one and delay 1s&#x000A; repeat&#x000A; drop return # remove the 0 from the stack and return&#x000A;&#x000A;# delay by a specified number of minutes, up to 65535 min&#x000A;sub delay_minutes&#x000A; begin dup while&#x000A; 1 minus 60 delay_seconds # subtract one and delay 1min&#x000A; repeat&#x000A; drop return # remove the 0 from the stack and return</pre> <p>It is easy to write subroutines for delays of hours, days, weeks, or whatever you want. Keep in mind, however, that the timer on the Micro Maestro is not as accurate as a stopwatch &#8211; these delays could easily be off by 1%.</p> <h4>Digital output</h4> <table class="picture_with_caption right"><tr><td style="max-width: 300px"><a href="https://a.pololu-files.com/picture/0J2051.1200.jpg?5d7468acc52f6dfe49e5b136b6357620" class="noscript-fallback"><img alt="" class="zoomable" data-gallery-pictures="[{&quot;id&quot;:&quot;0J2051&quot;,&quot;caption&quot;:&quot;\u003cp\u003eConnecting the Micro Maestro to a chain of ShiftBars. A single 12V supply powers all of the devices.\u003c/p\u003e&quot;,&quot;url_tiny&quot;:&quot;https://a.pololu-files.com/picture/0J2051.98x98.jpg?5d7468acc52f6dfe49e5b136b6357620&quot;,&quot;url_medium&quot;:&quot;https://a.pololu-files.com/picture/0J2051.600x480.jpg?5d7468acc52f6dfe49e5b136b6357620&quot;,&quot;url_full&quot;:&quot;https://a.pololu-files.com/picture/0J2051.1200.jpg?5d7468acc52f6dfe49e5b136b6357620&quot;,&quot;longest_side&quot;:800}]" data-picture-id="0J2051" data-picture-longest_side="800" src="https://a.pololu-files.com/picture/0J2051.300.jpg?5d7468acc52f6dfe49e5b136b6357620" /></a></td><p></tr><tr><th style="max-width: 300px"><p>Connecting the Micro Maestro to a chain of ShiftBars. A single 12V supply powers all of the devices.</p></th></tr></table></p> <p>The digital output feature of the Maestro is capable of controlling anything from simple circuits to intelligent devices such as the <a href="/product/1222">ShiftBrite LED Modules</a> and <a href="/product/1242">ShiftBar LED Controllers</a>, which use a simple synchronous serial protocol. In this example, the clock, latch, and data pins of a ShiftBrite or ShiftBar are connected to servo channels 0, 1, and 2, respectively, and these channels are all configured as outputs. The subroutine RGB defined here takes 10-bit red, green, and blue values from the stack, then sends a 32-byte color packet and toggles the latch pin to update the ShiftBrite with the new color value. The subroutine could be modified to control a larger chain of ShiftBrites if desired.</p> <pre name="code" class="maestro">&#x000A;begin&#x000A; 1023 0 0 rgb 500 delay # red&#x000A; 0 1023 0 rgb 500 delay # green &#x000A; 0 0 1023 rgb 500 delay # blue&#x000A;repeat&#x000A;&#x000A;# Subroutine for setting the RGB value of a ShiftBrite/ShiftBar.&#x000A;# example usage: 1023 511 255 rgb&#x000A;sub rgb&#x000A; 0 send_bit # this bit does not matter&#x000A; 0 send_bit # the "address" bit - 0 means a color command&#x000A; swap rot rot&#x000A; send_10_bit_value &#x000A; send_10_bit_value &#x000A; send_10_bit_value&#x000A; 0 1 8000 1 servo servo # toggle the latch pin&#x000A; return&#x000A;&#x000A;# sends a numerical value as a sequence of 10 bits&#x000A;sub send_10_bit_value&#x000A; 512&#x000A; begin&#x000A; dup&#x000A; while&#x000A; over over bitwise_and send_bit&#x000A; 1 shift_right&#x000A; repeat&#x000A; drop drop&#x000A; return&#x000A;&#x000A;# sends a single bit&#x000A;sub send_bit&#x000A; if 8000 else 0 endif&#x000A; 2 servo # set DATA to 0 or 1&#x000A; 0 0 8000 0 servo servo # toggle CLOCK&#x000A; return</pre> <p>Note that we use 0 to set the output low and 8000 to set the output high. These are reasonable choices, but any value from 0 to 5999 could be used for low, and anything from 6000 to 32767 could be used for high, if desired.</p> <h4>Serial output (Mini Maestro 12, 18, and 24 only)</h4> <p>On the Mini Maestro 12, 18, and 24, a script can be used to send serial data out on the TTL-level serial port (TX). This means that the Maestro can control additional Maestros, allowing for large numbers of channels without a separate microcontroller. Here is a simple program that shows how a serial command can be used to control another Maestro. To use this code, configure both Maestros in UART mode at the same baud rate, and connect TX on the master to RX on the slave.</p> <pre name="code" class="maestro">&#x000A;100 delay # initial delay to make sure that the other maestro has time to initialize&#x000A;&#x000A;begin&#x000A; 127 0 mini_ssc # set servo 0 to position 127, using the mini-SSC command&#x000A; 254 0 mini_ssc # set servo 0 to position 254&#x000A;repeat&#x000A;&#x000A;sub mini_ssc&#x000A; 0xFF serial_send_byte serial_send_byte serial_send_byte&#x000A; return</pre>

Related Products

Micro Maestro 6-Channel USB Servo Controller (Partial Kit)
Micro Maestro 6-Channel USB Servo Controller (Assembled)
Mini Maestro 12-Channel USB Servo Controller (Assembled)
Mini Maestro 12-Channel USB Servo Controller (Partial Kit)
Mini Maestro 18-Channel USB Servo Controller (Assembled)
Mini Maestro 18-Channel USB Servo Controller (Partial Kit)
Mini Maestro 24-Channel USB Servo Controller (Assembled)
Mini Maestro 24-Channel USB Servo Controller (Partial Kit)
Log In
Pololu Robotics & Electronics
Shopping cart
(702) 262-6648
Same-day shipping, worldwide
Menu
Shop Blog Forum Support
My account Comments or questions? About Pololu Contact Ordering information Distributors