Skip to content

State Machine Serial Interface

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