Skip to content

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 later
  • EW: Saves the shows on the board to flash memory so they'll remain after power off
  • TS: 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 digit 1 through 8
  • <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 Show
  • W 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 index T<operator><number>

Basic LED commands

  • + - On: Turn on LED group
  • - - Off: Turn off LED group

Extended commands

  • M - Motor Control: Actions for updating motors M<number><motor_action>[arguments]
  • G - GI Control:
  • D - Driver Control:
  • S - Store Group: Map LEDs in active group to alias S<number>
  • E - Send Event:

Motor actions

  • F - Forward: Run motor forward for ticks M<index>F<number>
  • R - Reverse: Run motor in reverse for ticks M<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 ticks M<index>P<number><number>
  • B - Brake: Brake motor for ticks M<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:

  1. Set LEDs 0-1 to be the active LED group, send a command to turn the active group red.
  2. Set LEDs 2-3 to be the active LED group, send a command to turn the active group off.
  3. Wait
  4. Send a command to turn the active group off (since LEDs 2-3 are still the active group from the previous step)
  5. Set LEDs 0-1 to be the active LED group, send a command to turn the active group off.
  6. Wait
  7. 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 ?