Skip to content

What your software needs to do on machine start

This guide explains the general approach and order of operations for how your pinball software interacts with FAST Pinball hardware. It's meant to be used alongside the FAST Pinball Serial Protocol reference which covers the full details for how to connect and use each command.

The basics

FAST Pinball controllers use multiple virtual serial ports over the single USB connection. Different ports are used for different purposes, and your game software will need to simultaneously connect to multiple ports to send commands and receive messages. You'll need to use best practices to ensure those don't block, via tasks / threads / coroutines / etc. to manage the reading and writing for each port.

MPF can be an example

If you can read Python, you can look at the Mission Pinball Framework as a reference framework implementation. We'll refer to MPF as an example throughout this guide. You don't have to do things like MPF does, but MPF has been around and using FAST hardware for almost 10 years, so it's battle-tested and uses FAST best practices. (Note that full Neuron & Expansion board support is in the FAST MPF branch. This will be merged into the mainline MPF branch when its done.)

Machine start up operations

Let's first look at what happens when your framework starts up. This would be the initial process for your software, but it does not necessarily mean that the FAST hardware has been freshly rebooted too. In a production machine, powering the machine on will start your software and boot up the FAST hardware in a fresh state, but we feel it's important to also support the case where your software is started up while the FAST hardware is already running. This is especially important for development and testing, but also nice since it means your software will be more robust in general.

Command sending strategy

There are essentially three different "types" of FAST Serial Protocol commands:

  • Commands that can be sent without caring about a response.
  • Commands which need awaited and verified with a simple "pass" response.
  • Commands which need awaited and because their response has real data that must be processed.

In MPF, there are three different sending methods: send_blind(), send_and_confirm(), and send_query(). The first one just sends without waiting. The second two will block while waiting for a response.

All three sending methods just drop the message into a shared queue along with some metadata, and the serial sending coroutine pulls them off the queue and send them. For messages that need a response, the sending coroutine will wait for the response to come back before sending the next message.

MPF uses a receiving coroutine to read the serial data and process it. It has access to a list of messages to ignore, as well as a list of messages that require processing (and therefore have associated methods to call when they receive that messages).

Ignored messages include things like WD:P and TL:P, whereas messages with headers like ID: and NN: are processed by specific methods which do things with their data.

MPF also maintains a value for the command it's waiting for when it's looking for a simple pass command, so the receiving routine knows it can unblock the sending queue.

MPF has separate instances of serial communicators for each port type, but if you look at the base class you can see all of this in action: mpf.platforms.fast.communicators.base.FastSerialCommunicator

Connecting to the ports

This guide uses a Neuron Controller with an FP-EXP-0071 Expansion board as an example. We'll walk through the NET and EXP connections separately here, but your framework will most likely connect to both at the same time and run these operations in parallel.

N or > jump the next page, P or < for previous, search with S or ?