Commands and the stack
A program in the Maestro script language consists of a sequence of commands which act on a stack of values. Values in the stack are integers from -32768 to +32767. On the Micro Maestro 6-channel servo controller, there is room for up to 32 values on the stack, while on the Mini Maestro servo controllers there is room for up to 126 values on the stack. Commands always act on the topmost values of the stack and leave their results on the top of the stack. The simplest kind of commands are literals, numerical values that are placed directly onto the stack. For example, the program “-10 20 35 0” puts the values -10, 20, 35, and 0 sequentially onto the stack, so that it looks like this:
A more complicated command is the PLUS command, which adds the top two numbers, leaving the result on the top of the stack. Suppose the numbers 1, 2, 4, and 7 are sequentially placed on the stack, and the PLUS command is run. The following table shows the result:
Note that the PLUS command always decreases the size of the stack by one. It is up to you to make sure that you have enough values on the stack to complete the commands you want to run!
Consider a more complicated example: suppose we want to compute the value of (1 – 3) × 4, using the MINUS and MULTIPLY commands. The way to write this computation as a script is “1 3 MINUS 4 TIMES”.
Comments, case, whitespace, and line breaks
All parts of the Maestro script language are case-insensitive, and you may use any kind of whitespace (spaces, tabs, and newlines) to separate commands. Comments are indicated by the pound sign “#” – everything from the # to the end of the line will be ignored by the compiler. For example, the computation above may be written as:
1 3 miNUS 4 # this is a comment! times
with absolutely no effect on the compiled program. We generally use lower-case for commands and two or four spaces of indentation to indicate control structures and subroutines, but feel free to arrange your code to suit your personal style.
The Maestro script language has several control structures, which allow arbitrarily complicated programs to be written. Unlike subroutines, there is no limit to the level of nesting of control structures, since they are all ultimately based on GOTO commands (discussed below) and simple branching. By far the most useful control structure is the BEGIN…REPEAT infinite loop, an example of which is given below:
# move servo 1 back and forth with a period of 1 second begin 8000 1 servo 500 delay 4000 1 servo 500 delay repeat
This infinite loop will continue forever. If you want a loop that is bounded in some way, the WHILE keyword is likely to be useful. WHILE consumes the top number on the stack and jumps to the end of the loop if and only if it is a zero. For example, suppose we want to repeat this loop exactly 10 times:
10 # start with a 10 on the stack begin dup # copy the number on the stack - the copy will be consumed by WHILE while # jump to the end if the count reaches 0 8000 1 servo 500 delay 4000 1 servo 500 delay 1 minus # subtract 1 from the number of times remaining repeat
Note that BEGIN…WHILE…REPEAT loops are similar to the while loops of many other languages. But, just like everything else in the Maestro script language, the WHILE comes after its argument.
For conditional actions, an IF…ELSE…ENDIF structure is useful. Suppose we are building a system with a sensor on channel 3, and we want to set servo 5 to 6000 (1.5 ms) if the input value is less than 512 (about 2.5 V). Otherwise, we will set the servo to 7000 (1.75 ms). The following code accomplishes this relatively complicated task:
3 get_position # get the value of input 3 as a number from 0 to 1023 512 less_than # test whether it is less than 512 -> 1 if true, 0 if false if 6000 5 servo # this part is run when input3 < 512 else 7000 5 servo # this part is run when input3 >= 512 endif
As in most languages, the ELSE section is optional. Note again that this seems at first to be backwards relative to other languages, since the IF comes after the test.
The WHILE and IF structures are enough to create just about any kind of script. However, there are times when it is just not convenient to think about what you are trying to do in terms of a loop or a branch. If you just want to jump directly from one part of code to another, you may use a GOTO. It works like this:
goto mylabel # ...any code here is skipped... 4000 1 servo mylabel: # the program continues here 4000 2 servo
In this example, only servo 2 will get set to 4000, while servo 1 will be unchanged.
It can be useful to use the same sequence of commands many times throughout your program. Subroutines are used to make this easier and more space-efficient. For example, suppose you often need to set servo 1 and servo 2 back to their neutral positions of 6000 (1.5 ms) using the sequence “6000 1 servo 6000 2 servo”. You can encapsulate this in a subroutine as follows:
sub neutral 6000 1 servo 6000 2 servo return
Then, whenever you want send these two servos back to neutral, you can just use “neutral” as a command. More advanced subroutines take values off of the stack. For example, the subroutine
sub set_servos 2 servo 1 servo return
will set channel 2 to the value on the top of the stack and channel 1 to the next value. So, if you write “4000 6000 set_servos”, your script will set channel 1 to 4000 and channel 2 to 6000.
Subroutines can call other subroutines, up to a limit of 10 levels of recursion. For example, the subroutine “neutral” above can be implemented by calling set_servos:
sub neutral 6000 6000 set_servos return