EXP Show Scripting¶
FAST Pinball expansion boards have the ability to run simple scripted shows directly on the EXP boards themselves. You can create your own shows and store them on a board, and then a show can be triggered via a simple command.
Here are some examples of things you could use this for:
- Fade an LED through several colors.
- Flash an LED on and off.
- Animate a series of LEDs.
- Coordinate a servo movement and light show for a topper.
- Run an automated attract mode show while your computer starts up.
- Etc.
Each show can target all of the resources such as LEDs and motors on a single breakout when you trigger it. They can also be passed arguments when triggered which means you can write a generic show (for example, to animate a circular motion across 4 LEDs) and then trigger it on different LEDs if you want to use the same show in multiple locations.
Shows can be very simple, or complicated. They support a powerful system of actions, variables, and branching execution which allow you to write very flexible shows.
Shows are written to an expansion board via the ES:
FAST Serial Protocol command and triggered with the TS:
command. Once you've developed a set of shows for your game they can be permanently stored in the board's flash memory using the EW:
command.
Do you need to use shows?¶
FAST Pinball EXP shows are a great way to add some simple animations to your game, especially if you're writing your own software framework. If you use an existing higher level game framework, such as the Mission Pinball Framework (which has its own much more powerful show system), then you probably don't need to use the EXP show scripting. However you may still find them useful for certain tasks such as reducing serial traffic for common displays.
These simple EXP shows are also nice if you have a few LEDs that you want to animate, but you don't want to have to write a full show in your game framework just for that. For example, maybe you have a ring of LEDs on a pop bumper that you always want to animate in a certain way. You could write a simple EXP show to do that, and then trigger it from your game framework targetting a specific bumper with a single command.
Another example is if you have a topper or some other mod with a FAST expansion board built-in. You could write an EXP show which automatically runs at power on, meaning the topper or toy could be animated and fun even if it's sitting on someone's shelf and not connected to a game at all.
Associated FAST Serial Commands¶
There are a few FAST Serial Protocol commands you can use to control the shows:
ER:
Configures an LED block (groups arbitrary LEDs which are then targeted by a show)ES:
Writes a show to the board which can be triggered laterEW:
Saves the shows on the board to flash memory so they'll remain after power offTS:
Triggers (starts/stops) a show
See it in action¶
Let's run through a very simple example demonstrating an EXP show in action. This will certainly not make sense yet, but we'll explain everything afterwards. Here's an example using a FAST Serial Protocol terminal session connected to the EXP port and the Neuron's built-in LED breakout:
ea:48 # Set active expansion board to the Neuron's built in one
id: # Confirm this EXP board is running at least firmware 0.10
ID:EXP FP-EXP-2000 0.10
es:0,([0007])_b=ffL1+W10-W10J1. # Create Show 0 which blinks some blue LEDs every half second
ES:P
ts:0,0 # Trigger Show 0 to run in Slot 0
TS:P
If you want to see this in action, plug a chain of LEDs into the Neuron's first LED port. You should see the first eight LEDs blink between blue and off about every half second.
Let's dig into how the show commands work.
Show structure¶
An EXP show consists of an arbitrary number of commands followed by a mandatory end of show marker (a period). Each individual show is limited to 128 characters in total but it is possible to combine multiple shows if more space is needed.
There are two types of commands:
- Single character command, which will have a constant number of arguments (and be a known length), or
- Variable-length commands, which have opening and closing characters to support the arbitrary number of arguments in between.
General show syntax¶
Here's a quick reference of the types of commands used in shows, as well as the valid syntax for each command. We'll go into more detail on each command below.
<color>
=<color_var>
or<number><number><number>
<number>
= 2 digit hex (00
-1F
) or<byte_var>
or<random_val>
<label>
= Single character digit1
through8
<comparison>
= Single character operator=
,>
,<
,!
, or&
<assignment>
= Single character operator=
,+
,-
,*
,/
,%
,&
,|
, or^
<byte_var>
= Underscore plus single digit 1-8 (_1
through_8
)<color_var>
= Underscore plus single letter_r
,_g
,_b
,_w
,_h
,_s
,_v
,_c
,_t
, or_i
<random>
=#
or#
plus<range>
<range>
=[
<number><number>
]
<group>
=(
<group_op>
one or more<number>
)
Show Commands¶
Basic flow control¶
.
End of ShowW
Wait (in ticks, each tick is 32ms):W<number>
L
Set a label (so you can jump back to this point later):L<label>
J
Jump to a previously set label location:J<label>
F
Show specific LED fade (in ticks, each tick is 32ms):F<number>
Advanced flow control¶
{...}
Block: encloses optional section for use with logical test?
Test: executes block if comparison is true?<byte_var><comparison><number> {...}
T
Trigger: continue running new show at modified indexT<operator><number>
Basic LED commands¶
+
- On: Turn on LED group-
- Off: Turn off LED group
Extended commands¶
M
- Motor Control: Actions for updating motorsM<number><motor_action>[arguments]
G
- GI Control:D
- Driver Control:S
- Store Group: Map LEDs in active group to aliasS<number>
E
- Send Event:
Motor actions¶
F
- Forward: Run motor forward for ticksM<index>F<number>
R
- Reverse: Run motor in reverse for ticksM<index>F<number>
H
- Home: Run motor to home position (default timeout)M<index>H
L
- Limit: Run motor to limit position (default timeout)M<index>L
P
- Position: Run motor to position for ticksM<index>P<number><number>
B
- Brake: Brake motor for ticksM<index>B<number>
C
- Coast: Coast motor (power off)M<index>C
Component variables¶
_h
Hue (wraps rather than clamped)_s
Saturation_v
Value_r
Red_g
Green_b
Blue_w
White_t
Color table entry_i
Index
Byte variables¶
_1
..._8
- locals
Color variables¶
_c
- color (also a “component” var)_9
,_0
- locals
Variable manipulation¶
<byte_var>
- Assign :<byte_var><assignment><number>
<color_var>
- Assign :<color_var><assignment><color>
(group)<component_var>
- Assign :(group)<component_var><assignment><number>
Active LED group¶
The indices of the LED group are all relative to the target block that was defined for the show (port, user block, etc.) and currently the show is limited to only the LEDs defined as that block.
(<number><number>...)
Set a list of LEDs as the active LED group(+<number><number>...)
Add a list of LEDs to the active LED group(-<number><number>...)
Remove a list of LEDs from the active LED group(?<component_var><comparison><number>)
Set the active LED group to the LEDs which match the test([<number><number>])
Set the active LED group to the LEDs in the range (all LEDs from the first to the second number, inclusive)
Using mapped LED groups within the show¶
The target LED block described above specifies which LEDs are part of the show. The active LED group is a subset of the LEDs in the target block which represents the LEDs being affected by the specific command in the specific step of the show.
For example, if you wanted a script which alternately flashed two groups of two LEDs (like a railroad crossing sign), you might use the ER:
command to specify a block of LEDs containing LEDs 0-3. Then you'd change which LED was active in each step of the show. So the show might look like this:
- Set LEDs 0-1 to be the active LED group, send a command to turn the active group red.
- Set LEDs 2-3 to be the active LED group, send a command to turn the active group off.
- Wait
- Send a command to turn the active group off (since LEDs 2-3 are still the active group from the previous step)
- Set LEDs 0-1 to be the active LED group, send a command to turn the active group off.
- Wait
- Repeat
In the logical example above, there are four LEDs in the target block, but only two LEDs in the active LED group at any given time. To use this show, you'd trigger it with the TS:
command and pass in the target block (whatever block number you used when you assigned the four LEDs to the block with the ER:
command), and then two of those LEDs would be the active ones for each step.
End of Show¶
The end of show marker (EOS) is a period .
character and valid any time a command identifier would be expected.
Command List¶
Command | Name | Arguments | Example | Explanation |
---|---|---|---|---|
. |
End of show | None | . |
Ends the show |
W |
Wait | <number> |
W40 |
Waits 64 ticks (40 hex) = 2 seconds |
L |
Label | <label> |
L1 |
Labels this point in the script as "1" |
J |
Jump | <label> |
J1 |
Jumps to the point in the script labeled "1" |
F |
Fade | <number> |
F40 |
Fades the active LED group over 64 ticks (40 hex) = 2 seconds |
{ |
Start a block | <LEDs> |
{?_1=1 |
Starts a block which will only run if byte variable 1 is equal to 1 |
} |
Close a block | <LEDs> |
{?_1=1} |
Close a block which will only run if byte variable 1 is equal to 1 |
? |
Test | <byte_var><comparison><number> |
?_1=1 |
Tests if byte variable 1 is equal to 1 |
T |
Trigger | <operator><number> |
T+01 |
Continue executing the next show in this slot |
+ |
On | <LEDs> |
+ |
Turns on the active LED group |
- |
Off | <LEDs> |
- |
Turns off the active LED group |
M |
Motor | M<number><motor_action>[arguments] |
M00F20 |
Run motor 0 forward for 32 ticks (20 hex) = 1 second |
S |
Store | <number> |
S80 |
Store the current active LED group into the first mapped index |
_ |
Variable | <variable><assignment><number> |
_r=ff |
Assigns component variable r to ff |
( |
Start a group definition | <LEDs> |
(000102) |
Defines a group of LEDs 0, 1, and 2 Numbers must be two digits. |
) |
Close a group definition | <LEDs> |
(000102) |
Defines a group of LEDs 0, 1, and 2 |
Whitespace characters ignored by parser¶
These characters still count towards the 128 character max show length, but they are ignored by the show parser.
Space
,
Comma\r
Carriage return\n
Line feed\t
Tab
Operators¶
=
Assign+
Add-
Subtract*
Multiply/
Divide%
Modulo&
Bitwise AND|
Bitwise OR^
Bitwise XOR
Comparators¶
=
Equal!
Not equal>
Greater than<
Less than&
Bitwise AND[
Range start]
Range end
Variables¶
Byte vars for show¶
1 = '1', 2 = '2', 3 = '3', 4 = '4', 5 = '5', 6 = '6', 7 = '7', 8 = '8',
Color vars for show¶
9 = '9', 0 = '0', C = 'c'
Component vars for LEDs¶
H = 'h', S = 's', V = 'v', R = 'r', G = 'g', B = 'b', W = 'w', I = 'i', T = 't'
Group¶
SET = '=',
INCLUDE = '+', EXCLUDE = '-', QUERY = '?', RANGE = '[...]'
Examples¶
Now that you've seen the syntax, let's look at some examples. First, let's look at the show we used at the beginning of this section:
es:0,([0007])_b=ffL1+W10-W10J1.
That first part of that line is the command es:
, and the 0,
just assigns this show script to be Show 0, so the ([0007])_b=ffL1+W10-W10J1.
is the actual show. Breaking it down:
([0007])# Set active LED group to the range from 0 to 7 (first 8 LEDs)
_b=ff # Set blue channel of the active LED group to `ff` hex = 255 decimal = full on
L1 # Set a label (1) here so we can jump back to this point later
+ # Turn on the active LED group
W10 # Wait 16 ticks (10 hex) = 0.5 seconds
- # Turn off the active LED group
W10 # Wait 16 ticks (10 hex) = 0.5 seconds
J1 # Jump back to the label 1 we set earlier
. # End of show (required even though it's never reached because of the jump)
Basics¶
set color and turn on a lamp
(00)_c=808080+.
Waiting¶
turn a lamp on and off
(00)_c=808080+W20-.
Fades¶
animate a lamp’s color
(00)_c=800000+F20_c=000080W20-.
Components¶
set color channels
(00)_c=808080+W20_r=00W20_g=00+W20.
Components¶
modify color channels
(00)_c=202020+W20_r+20W20_g-20W20_b*04.
Control¶
labels and jumps
(00)_c=808080L1+W20-W20J1.
Groups¶
add and remove lamps
(00)_c=808080+W20(+01)_r=00+W20(+02)_g=00+W20(-01)-.
Groups¶
setting a range
([0007])_c=008080+W20-.
Groups¶
save group to map
([0007])S80_c=008080+W20-W20(80)+.
Variables¶
using values from trigger
(00)_c=_1_2_3+W20-.
Variables¶
random value
([0007])_c=004040+L1W20_r=#J1.
Variables¶
random value in range
L1(#[0007])_c=008080+W20-J1.
Control¶
conditional execution
(00)_c=808080L1+W20-W20_1-01?_1>0{J1}.
N or > jump the next page, P or < for previous, search with S or ?