mindaffectBCI : Message Specification¶
Purpose¶
This document describes in the messages which are passed between the different components of the mindaffect BCI system, and the low-level byte-struture used for the messages. The message specification is transport agnostic in that the messages themselves may be sent over different ‘wire-protocols’, such as BTLE, UDP, TCP, etc.
Note: This specification is intended for developing new low-level components to interface to the mindaffectBCI. Most developers / users can directly use one of the provided higher-level APIs, such as python, java, c#/unity.
Objectives¶
- Stateless: as much as possible messages are self-contained information updates such that the interpretation of a message depends minimally on the reception of other messages. In this way, we are both robust to loss of a single message and failure/rebooting of an individual component, and also simplify the later loading/replay of saved data as we can basically start playback at any point. The disadvantage of this is however the messages tend to be longer than needed due to the additional redundancy.
- Simple + Readable: as much as possible the message spec will be human readable, such that for example message types are encoded in plain strings. However, the spec will also be simple to read manipulate the messages, thus integers will be sent as integers, and arrays as packed binary arrays.
- Efficient: as much as possible (without violating the stateless and readable objectives) the message spec will be efficient in space usage in transmission.
- Compact : some of our transport layers have very small payload sizes (e.g. BLE has 20 bytes) thus the messages should be compact such that a useful stateless message can be sent in this payload size.
- Latency tolerant : we cannot guarantee timely transmission of messages between components. Thus, the spec will (where appropriate) include additional time-stamp information to allow a ‘true’ message time-line to be reconstructed.
Structure¶
The message specification is structured as follows:
- Message Name: a human readable informative name for the message
- Message UID : ascii character used to uniquely identify this message. This is required to be the first character of any message.
- Sender: the component which produces the message
- Receiver: the component which receives the message
- Purpose: the reason for sending this message between these two components
- Format: the detailed structure of the message in terms of the basic types (i.e. char, int8, int16, uint8, single, double etc.) it is made up from. Each slot of the format has:
- Name:
- Type Information:
- Comment: human readable description of the purpose of this slot
Message Specifications¶
Endianness¶
To simplify things we require that all numbers be encoded in LITTLE ENDIAN.
Message Header¶
All messages start with a standard header consisting of:
Slot | Type (= value) | Comment |
UID | 1 of char | Message UID |
version | 1 of uint8 (0) | Message version number (0) |
length | [1] of uint16 (short) | Total length of the remaining message in bytes. This can be used to allow *forward compatibility* as clients can skip gracefully skip messages where they do not understand the payload, e.g. because of an unknown messageUID+version combination. |
payload | [ length ] of byte | The message payload, i.e. the rest of the message |
General Utility Messages¶
Name:
HEARTBEAT |
UID: “H” | |||||||||||||||
Sender:
ANY component REQUIRED: STIMULUS REQUIRED: RECOGNISER |
Receiver:
ANY component REQUIRED: RECOGNISER REQUIRED: UI |
|||||||||||||||
Purpose:
These heartbeat messages have two main purposes:
|
||||||||||||||||
Format:
|
Name:
SUBSCRIBE |
UID: “B” | ||||||||||||||||||
Sender:
ANY component |
Receiver:
RECOGNISER |
||||||||||||||||||
Purpose:
These subscribe messages main purpose is to reduce the network and computation load on both the Utopia-Hub message server and the client by allowing the client to inform the hub about which messages the client is interested in. Notes:
|
|||||||||||||||||||
Format:
|
Name:
LOG |
UID: “L” | ||||||||||||||||||
Sender:
Any |
Receiver:
Recogniser |
||||||||||||||||||
Purpose:
General logging of the state of any individual component. Currently this is used in the Recogniser to log things like it’s code version, where the data is being saved, and where the internal log-files are being saved. Note:
|
|||||||||||||||||||
Format:
|
Presentation -> Recogniser¶
Name:
STIMULUSEVENT |
UID: “E” | ||||||||||||||||||||||||||||||||||||||||
Sender:
Stimulus |
Receiver:
Recogniser |
||||||||||||||||||||||||||||||||||||||||
Purpose:
Provide the decoder with updated information about the current stimulus state. Note this message format is designed to allow for *stateless* updates with very small message sizes. Thus, each message is a self-contained report of (part of) the stimulus state at a given time, allowing to cut simulus-state updates over multiple messages without having to worry about message sequence numbers etc. Note: If we are **really** pressed for message sizes then we can define a less general but more compact STIMULUSEVENT message based on compressing the STIMULUSSTATE structure into a single ‘byte’ with 7-bits for objectUID and 1-bit for state. However, I think the implementation simplicity of the current message format makes this prefered even with the message size overhead. |
|||||||||||||||||||||||||||||||||||||||||
Format:
**STIMULUSDICT **structure
NOTES:
|
Recogniser -> Selection or Output¶
Name:
PREDICTEDTARGETPROB |
UID: “P” | |||||||||||||||||||||
Sender:
Recogniser |
Receiver:
Output |
|||||||||||||||||||||
Purpose:
Inform the output component of the current predicted target and it’s probability of being correct so the output decide: a) if the prediction is confident enough, b) if so to generate the appropriate output. |
||||||||||||||||||||||
Format:
|
Name:
PREDICTEDTARGETDIST |
UID: “F” | |||||||||||||||||||||||||||
Sender:
Recogniser |
Receiver:
Output |
|||||||||||||||||||||||||||
Purpose:
Inform the output component of the current distribution over all possible predicted targets --to allow more fine grained decision making w.r.t. when and what output to generate. |
||||||||||||||||||||||||||||
Format:
TARGETERRORDIST
|
Controller or Output -> All¶
Name:
MODECHANGE |
UID: “M” | ||||||||||||||||||
Sender:
Controller |
Receiver:
Recogniser |
||||||||||||||||||
Purpose:
Tell the decoder to switch to a new operating mode, e.g. switch from calibration to testing. Currently, we have the following operating modes for the decoder:
|
|||||||||||||||||||
Format:
|
Name:
NEWTARGET |
UID: “N” | |||||||||||||||
Sender:
Controller |
Receiver:
Recogniser |
|||||||||||||||
Purpose:
Tell the decoder that the user has switched to attempt selection of a new target. For example, in the speller because the OUTPUT has made a character selection and the user is moving on to the next letter. The RECOGNISER is expected to use this message to clear it’s prediction history and start fresh on a new output. |
||||||||||||||||
Format:
|
Name:
SELECTION |
UID: “S” | ||||||||||||||||||
Sender:
Controller |
Receiver:
Recogniser |
||||||||||||||||||
Purpose:
Tell other clients that a selection has been made (and possibly trigger output to be generated). This further implies that the user has switched to attempt selection of a new target (thus an explicit newtarget message is not required). For example, in the speller because the OUTPUT/SELECTION has made a character selection and the user is moving on to the next letter. The RECOGNISER is expected to use this message to clear it’s prediction history and start fresh on a new output. An OUTPUT module may use this message to decide if it should perform it’s tasked output if the selection matches it’s trigger criteria, e.g. changing the TV channel in a TV remote. |
|||||||||||||||||||
Format:
|
Name:
RESET |
UID: “R” | |||||||||||||||
Sender:
Controller |
Receiver:
Recogniser |
|||||||||||||||
Purpose:
Reset the RECOGNISER to a ‘fresh-start’ state, i.e. as if it has just been started with no saved information about training data or predictions. |
||||||||||||||||
Format:
|
Recogniser -> User Interface¶
Name:
SIGNALQUALITY |
UID: “Q” | ||||||||||||||||||
Sender:
Recogniser |
Receiver:
UI |
||||||||||||||||||
Purpose:
Inform user about the quality of the electrode fit, so they can adjust the electrode positioning or contact to improve the connection to the scalp as much as possible. |
|||||||||||||||||||
Format:
|
Acquisition -> Recogniser¶
Name:
DATAPACKET |
UID: “D” | |||||||||||||||||||||
Sender:
Acquisation Device (e.g. EEG) |
Receiver:
Recogniser |
|||||||||||||||||||||
Purpose:
Send raw data as measured by the acquisition device to the decoder. |
||||||||||||||||||||||
Format:
Basically this is not something we can specify as it depends on the exact hardware device. Minimum spec for us:
Notes: 32bit timestamps @1ms accuracy means the timestamps will wrap-around in 4294967296/1000/60/60/24 = 50 days.. Which is way more than we really need…. With 24 bits this would be 4hr.. For implementation simplicity standard 32bit ints are prefered. |
Name:
DATAHEADER |
UID: A | |||||||||||||||||||||
Sender:
Acquisation Device (e.g. EEG) |
Receiver:
Recogniser |
|||||||||||||||||||||
Purpose:
Provide meta-information about the data provided by the acquisation device. As a minimum this should be the number of channels sent and their sample rate. Optionally should include channel location information. |
||||||||||||||||||||||
Format:
Basically this is not something we can specify as it depends on the exact hardware device. But suggestion is:
|
Extension Messages¶
In this section are listed messages which may be useful in future, but will not be implemented in the V1.0 version of the system.
Config -> Recogniser¶
Name:
CONFIGURECOGNISER |
UID: “C” | ||||||||||||||||||
Sender:
Config |
Receiver:
Recogniser |
||||||||||||||||||
Purpose:
Update the general configuration of the recognizer, e.g. the response length. Note: The side-effects of changing the configuration on the RECOGNISER is UNDEFINED. In the worst case this may result in a complete restart of the RECOGNISER clearing all previous state, i.e. removing the trained classifier etc. |
|||||||||||||||||||
Format:
This message payload is a dictionary of name-value pairs encoded in JSON format of the configuration parameters that the RECOGNISER needs. Note: This message could potentially be *very large*, and need to extend over multiple packets. It is assumed the underlying transport will deal with this effectively.
|
Other¶
Name:
TICKTOCK |
UID: “T” | ||||||||||||||||||
Sender:
ANY component |
Receiver:
ANY component |
||||||||||||||||||
Purpose:
This message tells the receiver what the current time-stamp from the sender is. It also optionally represents a *query* to the receiver to send back it’s time-stamp as rapidly as possible. These messages have two main purposes:
|
|||||||||||||||||||
Format:
|
Name:
CURRENTMODEL |
UID: TBD | |||||||||||||||||||||||||||
Sender:
Recogniser |
Receiver:
UI |
|||||||||||||||||||||||||||
Purpose:
Inform user about the parameters of the current model. |
||||||||||||||||||||||||||||
Format:
|
Bluetooth Low Energy¶
When using BLE to communicate we need to define Services and Charactersitics. Services represent groups of functionality which is exposed to other devices, with Characteristics the values to be communicated. Within this message specification a unique characteristic maps directly onto a message type, with groups of messages together making a logically connected service. Specification. Based on this reasoning we have the following BLE specification:
Service: Presentation¶
UUID: **d3560000-b9ff-11ea-b3de-0242ac130004
Description:
Provides the generic interface for devices which can take on the presentation role, that is which show stimuli to the user for which the brain response will be used to make selections later.
Characteristic: Stimulus State:
UUID: **d3560001-b9ff-11ea-b3de-0242ac130004
Description:
Provides the characteristic to communicate the current device stimulus state to the decoder by formatting STIMULUSEVENT messages.
Properties:
Variable length
Read, Write, Notify.
Payload Format:
Read, Notify: encode the current stimulus state in a STIMULUSEVENT message – Note: this is a full message with type, version, length encoding (Later we may remove this technically unnecessary header information), thus it may take more than 1 message to communicate a full stimulus state when we have > 8 objectIDs on a single device. Note however, that as stimulus state’s are ‘latched’ only changed objects need to have their state communicated which may be used to reduce the communication load in resource constrained situations.
(Note: be sure to use the onCharacteristicWrite callback to stream these multi-packet writes in a good way!)
TODO: V2 StimulusEvent spec with binary stimulus state encoding.
Write: encode control messages for the presentation device. Basically this includes:
NEWTARGET messages
SELECTION messages.
Again, we encode the full message spec in the BLE characteristic write (including header, version, length).
Service: Decoder¶
UUID: **d3560100-b9ff-11ea-b3de-0242ac130004
Description:
Provides the general interface for the EEG decoder. Thus, it consumes STIMULUSEVENT messages and generates Prediction messages.
Characteristic: PredictedTargetProb:
UUID: **d3560101-b9ff-11ea-b3de-0242ac130004
Description:
Provides the characteristic to communicate the predicted target probability by formatting PREDICTEDTARGETPROB messages.
Properties:
Read, Notify.
Payload Format:
Read, Notify: encode the current predicted target prob as a PREDICTEDTARGETPROB message. Note this is the full message including headers.
Characteristic: PredictedTargetDist:
UUID: **d3560102-b9ff-11ea-b3de-0242ac130004
23c
Description:
Provides the characteristic to communicate the predicted target probability by formatting PREDICTEDTARGETDIST messages.
Properties:
Read, Notify.
Payload Format:
Read, Notify: encode the current predicted target prob as a PREDICTEDTARGETDIST message. Note: this is a full message with type, version, length encoding (Later we may remove this technically unnecessary header information), thus it may take more than 1 message to communicate a full prediction state when we have > 8 objectIDs on a single device. As all messages are both time-stamped and stateless, this will be achieved by simply cutting the messages into smaller pieces to be transmitted one after each other.
Service: Selection¶
UUID: **d3560200-b9ff-11ea-b3de-0242ac130004
Description:
Provide the ability to receive and act on selections by the BCI.
Characteristic: Selection:
UUID: **d3560201-b9ff-11ea-b3de-0242ac130004
Description:
Provides the characteristic to communicate selections to the output device.
Properties:
Write, Notify
Service: ScoreOutput¶
UUID: **d3560300-b9ff-11ea-b3de-0242ac130004
Description:
Provides an output-scoring service, which consumes fitted model parameters and generates a stream of stimulus scores.
Characteristic: OutputScore
UUID: **d3560301-b9ff-11ea-b3de-0242ac130004
Description:
Provides the characteristic to communicate the stimulus output score by sending: OUTPUTSCORE messages.
Properties:
Read, Notify.
Payload Format:
Read, Notify: encode the current output score as an OUTPUTSCORE message.
Name:
OUTPUTSCORE |
UID: “O” | |||||||||||||||||||||
Sender:
OutputScorer |
Receiver:
ANY component |
|||||||||||||||||||||
Purpose:
This message sends the current stimulus score information from an acquisition device with local signal processing to BCI server. |
||||||||||||||||||||||
Format:
|
Characteristic: CurrentModel:
UUID: **d3560302-b9ff-11ea-b3de-0242ac130004
Description:
Provides a characteristic to communicate the current BCI model to the score-output module.
Properties:
Write
Payload Format:
Write: encode the current model as a CURRENTMODEL message. Note: this is a full message with type, version, length encoding (Later we may remove this technically unnecessary header information), thus it will take more than 1 message to communicate a full stimulus state when we have > 8 objectIDs on a single device.
Characteristic: CurrentSOSFILTER:
UUID: **d3560303-b9ff-11ea-b3de-0242ac130004
Description:
Provides a characteristic to communicate the current BCI filter to use.
Properties:
Write
PayloadFormat:
Write: Encode the IIR as a Second-Order-Sections filter, which is a matrix of 6 x n-Sections. Thus, we send it as such a matrix.
Name:
SOSIIR |
UID: “I” | ||||||||||||||||||
Sender:
Decoder |
Receiver:
ANY component |
||||||||||||||||||
Purpose:
Sends the IIR to use as an EEG pre-filter |
|||||||||||||||||||
Format:
|