StateMachineSerialInterface
Firmware version 17
Description
Allows software (i.e. Matlab, Python) to communicate with a Bpod state machine via its USB serial port.
This document describes the format of byte strings to send to the state machine's USB serial port, and what bytes to expect in return.
Command Menu
The first byte sent to the Bpod state machine accesses a command menu, where different bytes specify different functions.
The command bytes are:
'6' (ASCII 54): Hand shake (used to confirm a valid connection to the state machine).
The state machine returns a byte: '5' (ASCII 53) to confirm the connection.
'F' (ASCII 70): Return firmware version and machine type
The state machine replies with the following bytes:
FirmwareVersion(2 bytes; 16-bit int)
MachineType (2 bytes; 16-bit int). MachineType is: 1 (State Machine hardware v0.5-0.9) or 2 (pocket state machine)
'H' (ASCII 72): Return the state machine's on-board hardware configuration (excluding modules).
The state machine replies with the following bytes:
MaxStates(2 bytes; 16-bit int); maximum number of supported states in a single state machine description
TimerPeriod(2 bytes; 16-bit int); the period (in microseconds) of the state machine's refresh cycle during a trial
maxSerialEvents(1 byte); the maximum number of behavior events that can be allocated among connected modules
nGlobalTimers (1 byte); the number of global timers supported
nGlobalCounters (1 byte); the number of global counters supported
nConditions (1 byte); the number of condition-events supported
nInputs (1 byte); the number of channels in the state machine's input channel description array
inputDescriptionArray (1 byte x nInputs); an array indicating the state machine's onboard input channel types
nOutputs (1 byte); the number of channels in the state machine's output channel description array
outputDescriptionArray (1 byte x nOutputs); a byte array indicating the state machine's onboard output channel types
'M' (ASCII 77): Return information describing the state machine's connected modules
The state machine replies with the following bytes:
for each module (character 'U') in outputDescriptionArray (see 'H' above)
moduleConnected (1 byte); 1 if a module was found, 0 if not.
if moduleConnected == 1
moduleFirmwareVersion (4 bytes; 32-bit int) - firmware version reported by the module
moduleNameLength (1 byte); length of module name, in characters
moduleName (1 x moduleNameLength bytes); a character array with the module name
moreInfoFollows (1 byte); 0 if module description is complete, 1 if more data follows
while moreInfoFollows == 1
infoType (1 byte); Type of info returned
if infoType == '#' (ASCII 35); code to request for a specific number of serial events
nEvents(1 byte); number of serial events requested
elseif infoType == 'E' (ASCII 69) - code to assign names to event bytes returned from this module to the state machine
nEventNames (1 byte); number of event names to transmit
for 1 to nEventNames
eventNameLength (1 byte); length of this event name
eventName (1 x eventNameLength bytes); a character array with the event name
end
end
moreInfoFollows (1 byte); 0 if module description is complete, 1 if more data follows
end
end
end
'%' (ASCII 37): Set number of behavior events allocated to each module. '#' (byte 0) is followed by:
Bytes 1-nModules: a byte for each module, indicating the number of behavior events it can generate.
NOTE: nModules is the sum of (character 'U') in outputDescriptionArray returned by the state machine (see 'H' above)
The state machine returns a byte (1) to confirm that it has finished setting each module's event allocation.
NOTE: By default, the maximum number of events (see 'H' -> maxSerialEvents above) is distributed equally among modules. If a module requests a specific number of events (see 'M' -> '#' above), the software (MATLAB / Python) must calculate the reallocation using the set of requests, and then use this command ('%') to update the state machine.
'E' (ASCII 69): Set the state of each input channel (enabled/disabled). 'E' (byte 0) is followed by:
Bytes 1-nInputs: a byte for each input channel, indicating whether it is enabled (1) or disabled (0).
nInputs must exist, having been returned from the 'H' command (above)
Generally, low-impedance inputs that are not connected to a signal source or tied to a pull-down resistor should be disabled.
On the Bpod state machine, only the port input channels (i.e. photogates) are low impedance inputs.
The state machine returns a byte (1) to confirm that it has finished setting each channel's enabled property.
'J' (ASCII 74): Enable/Disable relay of incoming bytes from one module to the USB port. 'J' (byte 0) is followed by:
Byte 1: the module number to enable or disable (indexed by 0)
Byte 2: the state of the module (0 = relay off, 1 = relay on)
'K' (ASCII 87): Set a state synchronization channel. 'K' (byte 0) is followed by:
Byte 1: the digital output channel to use for synchronization. This channel is an index of outputDescriptionArray (see 'H' above).
Byte 2: the synchronization mode. Valid modes are:
0: Channel set high on trial start and low on trial end
1: Channel switches logic states with each state transition
The state machine returns a byte (1) to confirm that it has finished setting the sync channel configuration.
'O' (ASCII 79): Override digital output line state. 'O' (byte 0) is followed by:
Byte 1: the digital output channel to override. This channel is an index of outputDescriptionArray (see 'H' above).
Byte 2: the new state of the channel
If outputDescriptionArray[index] is a digital line (D,B,W), Byte 2 should be (1 = high, 0 = low)
If outputDescriptionArray[index] is a PWM line (P), Byte 2 should be the new PWM duty cycle (0-255)
If outputDescriptionArray[index] is a valve bank (S), Byte 2 should be a byte whose bits set the state of the 8 valves.
'I' (ASCII 73): Read the state of a digital input channel. 'I' (byte 0) is followed by:
Byte 1: the digital input channel to read. This channel is an index of inputDescriptionArray (see 'H' above).
The state machine will return:
0 if the channel's logic level is low
1 if the channel's logic level is high.
'T' (ASCII 84): Transmit a string of bytes to a connected module. 'T' (byte 0) is followed by:
Byte 1: the index of the targeted module.
The module index is in range 0 -> max number of modules (instances of 'U' in outputDescriptionArray; see 'H' above)
Byte 2: nBytes (number of bytes in the message)
Bytes 3 --> (2+nBytes): The message to transmit
'L' (ASCII 76): Store a list of 1-3 byte serial messages, which can later be sent to modules by message index. 'L' (byte 0) is followed by:
Byte 1: the index of the targeted module. (Each module has its own message library)
The module index is in range 0 -> max number of modules (instances of 'U' in outputDescriptionArray; see 'H' above)
Byte 2: nMessages (the number of messages to store)
for each message between 1 and nMessages
MessageIndex (1 byte; 1-255)
MessageLength (1 byte; 1-3)
for 1 to MessageLength
1 Byte (The next byte of the current message)
The state machine returns a byte (1) to confirm that the specified channel's message library has been updated.
'>' (ASCII 62): Clear the serial message libraries. No data follows.
The state machine restores each message to default - a message of length 1, whose value is equal to its index.
The state machine returns a byte (1) to confirm that the message libraries have been cleared.
'U' (ASCII 85): Transmit a stored serial message (by index) to a connected module. 'U' (byte 0) is followed by:
Byte 1: the index of the targeted module.
The module index is in range 0 -> max number of modules (instances of 'U' in outputDescriptionArray; see 'H' above)
Byte 2: the index of the message to send.
'V' (ASCII 86): Manually override an input channel, creating a virtual event. 'V' (byte 0) is followed by:
Byte 1: the input channel to override. This channel is an index of inputDescriptionArray (see 'H' above).
Byte 2: the new value of the channel (0 = low, 1 = high).
NOTE: If a channel is overridden, it will remain in the overridden state regardless of what signals arrive. The channel must be reset with a second call to 'V' in order to return control to the hardware.
'C' (ASCII 67): Transmit a state machine description to the state machine device. 'C' (byte 0) is followed by:
Bytes 1-2: nBytes (the number of bytes in the state machine description, NOT including the first 3 bytes; 'C' and nBytes)
Byte 3: nStates (the number of states in the state machine description)
for each state s between 1 and nStates
TimerMatrix[s] (1 byte) - The state to go to if the state timer elapses
for each state s between 1 and nStates
nOverrides (1 byte) - The number of events handled in this state (overriding default of not-handled)
if nOverrides > 0
for each event e between 1 and nOverrides
thisEvent (1 byte) - (the numeric code of the event handled in state s)
thisState (1 byte) - (the state to go to if thisEvent occurs in state s)
for each state s between 1 and nStates
nOverrides (1 byte): The number of outputs controlled in this state (overriding default of no output)
if nOverrides > 0
for each output channel between 1 and nOverrides
thisOutputChannel (1 byte) - (the index of the target output channel outputDescriptionArray)
thisOutputValue (1 byte) - (the value of the output channel)
for each state between 1 and nStates
nOverrides (1 byte): The number of global timer start events handled in this state (overriding default of none)
if nOverrides > 0
for each timer t between 1 and nOverrides
thisTimer (1 byte) - (the index of the global timer whose start-events are handled)
thisState (1 byte) - (the state to go to if thisTimer's start event occurs)
for each state s between 1 and nStates
nOverrides (1 byte): The number of global timer end events handled in this state (overriding default of none)
if nOverrides > 0
for each timer t between 1 and nOverrides
thisTimer (1 byte) - (the index of the global timer whose end-events are handled)
thisState (1 byte) - (the state to go to if thisTimer's end event occurs)
for each state s between 1 and nStates
nOverrides (1 byte): The number of global counter threshold events handled in this state (overriding default of none)
if nOverrides > 0
for each counter c between 1 and nOverrides
thisCounter (1 byte) - (the index of the global counter whose threshold events are handled)
thisState (1 byte) - (the state to go to if thisCounter's threshold event occurs)
for each state s between 1 and nStates
nOverrides (1 byte): The number of condition events handled in this state (overriding default of none)
if nOverrides > 0
for each condition c between 1 and nOverrides
thisCondition (1 byte) - (the index of the condition whose events are handled)
thisState (1 byte) - (the state to go to if a thisCondition event occurs)
for each global timer between 1 and nGlobalTimers (nGlobalTimers must be retrieved earlier, using command 'H' above)
linkedOutputChannel (1-byte) - index of an output channel in outputDescriptionArray. 255 = no channel linked).
for each global timer between 1 and nGlobalTimers
onMessage (1-byte). This is the index of a message set previously with command 'L' above, to send when the timer starts.
Note: The target module for onMessage is defined by linkedOutputChannel (above), if linkedOutputChannel is a module port.
for each global timer between 1 and nGlobalTimers
offMessage (1-byte). This is the index of a message set previously with command 'L' above, to send when the timer starts.
Note: The target module for offMessage is defined by linkedOutputChannel (above), if linkedOutputChannel is a module port.
for each global timer between 1 and nGlobalTimers
loopMode (1-byte). Set to 0 (default) if a one-shot timer, 1 to use the timer in loop mode
for each global timer between 1 and nGlobalTimers
sendGlobalTimerEvents (1-byte). Set to 1 (default) to generate global timer on and off events. Set to 0 to disable them.
for each global counter between 1 and nGlobalCounters (nGlobalCounters must be retrieved earlier, using command 'H' above)
attachedEvent(1-byte). This is the index of a behavior event to count.
for each condition between 1 and nConditions (nConditions must be retrieved earlier, using command 'H' above)
conditionChannel(1-byte). This is an input channel index in inputDescriptionArray.
for each condition between 1 and nConditions (nConditions must be retrieved earlier, using command 'H' above)
conditionValue(1-byte). 0 = low, 1 = high. Defines the state of the channel when the condition is true.
for each state s between 1 and nStates
stateTimer[s] (4 bytes; 32-bit int). This state's internal timer (units = state machine cycles, 1 cycle = timerPeriod returned from 'H' above).
for each global timer t between 1 and nGlobalTimers
globalTimer[t] (4 bytes; 32-bit int). Duration of global timer t (units = state machine cycles).
for each global timer t between 1 and nGlobalTimers
globalTimerOnsetDelay[t] (4 bytes; 32-bit int). Onset delay of global timer t (units = state machine cycles).
for each global timer t between 1 and nGlobalTimers
globalTimerLoopInterval[t] (4 bytes; 32-bit int). Delay between timer loop iterations (units = state machine cycles).
for each global counter between 1 and nGlobalCounters
globalCounterThreshold[t] (4 bytes; 32-bit int). Threshold (units = instances of the event being counted).
NOTE: The state machine will return a confirmation byte to indicate successful transmission of the state machine description, on the next call to RunStateMachine. This saves 1 read/write cycle, and reduces dead-time.
'R' (ASCII 82): Run state machine.
If a new state machine was sent prior to calling 'R', the state machine returns a byte (1) to confirm.
While the trial is running, event codes and soft-codes are returned via the serial report as follows:
Op-code (byte 1) = 1 if the message is an event, and 2 if the message is a soft code
If op-code 1,
nEvents (byte 2) - number of events returned
for each event between 1 and nEvents
Event code (1 byte)
If op-code 2,
Soft code (byte 2) = the soft code to pass to the soft code handler function.
Exit state: If event code 255 was returned, the state machine has reached an exit state. It then sends the trial's timing data as follows:
Trial start timestamp in milliseconds (4 bytes; 32-bit integer)
nTimestamps (2 bytes; 16-bit integer) - the number of timestamps to be transmitted
for each timestamp between 1 and nTimestamps:
Timestamp (4 bytes; 32-bit integer) - for each event recorded during the trial, a 32-bit timestamp. Units = 100us state machine cycles following trial start.
'X' (ASCII 88): Force-exit the currently running state machine, and return the partial trial's data.
No data follows.
The state machine then returns the data in same format as for 'R' command above.
'Z' (ASCII 90): Disconnect state machine from the USB serial port.
No data follows.
This resets several program variables to prepare the state machine for its next connection