## ThinkGear Serial Stream Guide ##
====== Introduction ======
ThinkGear(tm) is the technology inside every NeuroSky product or partner product that enables a device to interface with the wearers’ brainwaves. It includes the sensor that touches the forehead, the contact and reference points located on the ear pad, and the onboard chip that processes all of the data and provides this data to software and applications in digital form. Both the raw brainwaves and the eSense Meters (Attention and Meditation) are calculated on the ThinkGear chip.
The ThinkGear Serial Stream SDK document (formerly referred to as the ThinkGear Communications Protocol) defines, in detail, how to communicate with the ThinkGear modules.
In particular, it describes:
* How to **parse** the serial data stream of bytes to reconstruct the various types of brainwave data sent by the ThinkGear
* How to **interpret and use** the various types of brainwave data that are sent from the ThinkGear (including Attention, Meditation, and signal quality data) in a BCI application
* How to **send** reconfiguration Command Bytes to the ThinkGear, for on-the-fly customization of the module's behavior and output
The [[#ThinkGear Data Values]] chapter defines the types of Data Values that can be reported by ThinkGear. It is highly recommended that you read this section to familiarize yourself with which kinds of Data Values are (and aren't) available from ThinkGear before continuing to later chapters.
The [[#ThinkGear Packets]] chapter describes the ThinkGear Packet format used to deliver the ThinkGear Data Values.
The [[#ThinkGear Command Bytes]] chapter is for advanced users and covers how to send Command Bytes to the ThinkGear in order to customize its configuration (change baud rate, enable/disable certain Data Value outputs, etc).
====== ThinkGear Data Values ======
===== POOR_SIGNAL Quality =====
This unsigned one-byte integer value describes how poor the signal measured by
the ThinkGear is. It ranges in value from 0 to 255. Any non-zero
value indicates that some sort of noise contamination is detected. The
higher the number, the more noise is detected. A value of 200 has a
special meaning, specifically that the ThinkGear electrodes aren't
contacting a person's skin.
This value is typically output every second, and indicates the poorness
of the most recent measurements.
Poor signal may be caused by a number of different things. In order of
severity, they are:
* Sensor, ground, or reference electrodes not being on a person's head (i.e. when nobody is wearing the ThinkGear).
* Poor contact of the sensor, ground, or reference electrodes to a person's skin (i.e. hair in the way, or headset which does not properly fit a person's head, or headset not properly placed on the head).
* Excessive motion of the wearer (i.e. moving head or body excessively, jostling the headset).
* Excessive environmental electrostatic noise (some environments have strong electric signals or static electricity buildup in the person wearing the sensor).
* Excessive non-EEG biometric noise (i.e. EMG, EKG/ECG, EOG, etc)
A certain amount of noise is unavoidable in normal usage of ThinkGear,
and both NeuroSky's filtering technology and eSense(tm) algorithm have
been designed to detect, correct, compensate for, account for, and tolerate
many types of non-EEG noise. Most typical users who are only interested
in using the eSense values, such as Attention and Meditation, do not need
to worry too much about the ''POOR_SIGNAL'' Quality value, except to note that
the Attention and Meditation values will not be updated while ''POOR_SIGNAL''
is detected. The ''POOR_SIGNAL'' Quality value is more useful to some
applications which need to be more sensitive to noise (such as some medical
or research applications), or applications which need to know right away
when there is even minor noise detected.
By default, output of this Data Value is enabled. It is typically output
once a second.
===== eSense(tm) Meters =====
For all the different types of eSenses (i.e. Attention, Meditation), the
meter value is reported on a relative eSense scale of 1 to 100. On this
scale, a value between 40 to 60 at any given moment in time is considered
"neutral", and is similar in notion to "baselines" that are established in
conventional EEG measurement techniques (though the method for determining a
ThinkGear baseline is proprietary and may differ from conventional EEG). A
value from 60 to 80 is considered "slightly elevated", and may be interpreted
as levels being possibly higher than normal (levels of Attention or Meditation
that may be higher than normal for a given person). Values from 80 to 100 are
considered "elevated", meaning they are strongly indicative of heightened
levels of that eSense.
Similarly, on the other end of the scale, a value between 20 to 40 indicates
"reduced" levels of the eSense, while a value between 1 to 20 indicates
"strongly lowered" levels of the eSense. These levels may indicate states
of distraction, agitation, or abnormality, according to the opposite of
each eSense.
An eSense meter value of 0 is a special value indicating the ThinkGear
is unable to calculate an eSense level with a reasonable amount of
reliability. This may be (and usually is) due to excessive noise as
described in the ''POOR_SIGNAL'' Quality section above.
The reason for the somewhat wide ranges for each interpretation is that
some parts of the eSense algorithm are dynamically learning, and at times
employ some "slow-adaptive" algorithms to adjust to natural fluctuations
and trends of each user, accounting for and compensating for the fact
that EEG in the human brain is subject to normal ranges of variance and
fluctuation. This is part of the reason why ThinkGear sensors are able
to operate on a wide range of individuals under an extremely wide range
of personal and environmental conditions while still giving good accuracy
and reliability. Developers are encouraged to further interpret and
adapt these guideline ranges to be fine-tuned for their application (as one example, an application could disregard values below 60 and only react to values between 60-100, interpreting them as the onset of heightened attention levels).
==== ATTENTION eSense ====
This unsigned one-byte value reports the current eSense Attention meter of
the user, which indicates the intensity of a user's level of mental "focus"
or "attention", such as that which occurs during intense concentration and
directed (but stable) mental activity. Its value ranges from 0 to 100.
Distractions, wandering thoughts, lack of focus, or anxiety may lower the
Attention meter levels. See [[#eSense(tm) Meters]] above for details about
interpreting eSense levels in general.
By default, output of this Data Value is enabled. It is typically output
once a second.
==== MEDITATION eSense ====
This unsigned one-byte value reports the current eSense Meditation meter of
the user, which indicates the level of a user's mental "calmness" or
"relaxation". Its value ranges from 0 to 100. Note that Meditation is a
measure of a person's **mental** levels, not **physical** levels, so simply
relaxing all the muscles of the body may not immediately result in a
heightened Meditation level. However, for most people in most normal
circumstances, relaxing the body often helps the mind to relax as well.
Meditation is related to reduced activity by the active mental processes in
the brain, and it has long been an observed effect that closing one's eyes
turns off the mental activities which process images from the eyes, so
closing the eyes is often an effective method for increasing the Meditation
meter level. Distractions, wandering thoughts, anxiety, agitation, and
sensory stimuli may lower the Meditation meter levels. See "eSense Meters"
above for details about interpreting eSense levels in general.
By default, output of this Data Value is enabled. It is typically output
once a second.
===== 8BIT_RAW Wave Value =====
This unsigned one-byte value is equivalent to the signed [[#RAW Wave Value (16-bit)]] described below, except that it is scaled to be unsigned, and only the most significant 8 bits are output (where "most significant" is defined based on the specific ThinkGear hardware).
This makes it possible to output raw wave values given the bandwidth
restrictions of serial communications at a 9600 baud rate, at
the cost of not outputting the lowest couple bits of precision. For many
applications (such as realtime display of the graph of the raw wave),
showing 8 bits of precision is sufficient, since the human eye
typically cannot rapidly discern pixels which may correspond to the
lower bits of precision anyways. If more precision is required,
consider using the normal signed [[#RAW Wave Value (16-bit)]] (described below) output at a higher baud rate.
Although only the most significant 8 bits are output when ''8BIT_RAW'' output
is enabled, all calculations are still performed within the ThinkGear
based on the maximum precision of raw wave information available to the ThinkGear hardware, so no information is discarded internally. Only the outputted raw
value is reduced to 8 bits, to save serial bandwidth.
By default, output of this Data Value is disabled. Like the regular
signed 16-bit RAW Wave Value, the ''8BIT_RAW'' Wave Value is typically output
128 times a second, or approximately once every 7.8 ms.
This Data Value is only available in ThinkGear Embedded modules (TGEM). It is not
available from ThinkGear ASIC (TGAT/TGAM1), which is used in MindWave and MindWave Mobile.
===== RAW_MARKER Section Start =====
This is not really a Data Value, and is primarily only useful for debugging
very precise timing and synchronization of the raw wave, or research purposes.
Currently, the value will always be ''0x00''.
By default, output of this Data Value is disabled. It is typically output
once a second.
This Data Value is only available in ThinkGear modules. It is not available from ThinkGear ASIC (such as MindWave and MindWave Mobile).
===== RAW Wave Value (16-bit) =====
This Data Value consists of two bytes, and represents a single raw wave
sample. Its value is a signed 16-bit integer that ranges from -32768 to
32767. The first byte of the Value represents the high-order bits of the
twos-compliment value, while the second byte represents the low-order
bits. To reconstruct the full raw wave value, simply shift the first
byte left by 8 bits, and bitwise-or with the second byte:
short raw = (Value[0]<<8) | Value[1];
where ''Value[0]'' is the high-order byte, and ''Value[1]'' is the low-order byte.
In systems or languages where bit operations are inconvenient, the
following arithmetic operations may be substituted instead:
raw = Value[0]*256 + Value[1];
if( raw >= 32768 ) raw = raw - 65536;
where ''raw'' is of any signed number type in the language that can represent
all the numbers from -32768 to 32767.
However, in Java you use the following code instead in order to avoid problems when implementing the parsing of CODE 0x80 (Raw Wave Data). This is due to the fact that Java has no unsigned data types, which has to be handled carefully when combining the two bytes.
private int[] highlow = new int[2];
highlow[0] = (int)(payload[index] & 0xFF);
highlow[1] = (int)(payload[index+1] & 0xFF);
raw = (highlow[0] * 256) + highlow[1];
if( raw > 32768 ) raw -= 65536;
Each ThinkGear model reports its raw wave information in only certain
areas of the full -32768 to 32767 range. For example, ThinkGear ASIC
may only report raw waves that fall between approximately -2048 to
2047, while ThinkGear modules may only report raw waves that fall
between approximately 0 to 1023. Please consult the documentation
for your particular ThinkGear hardware for more information.
For TGEM the output of this Data Value is disabled by default. When enabled, the
RAW Wave Value is typically outputted 128 times a
second, or approximately once every 7.8ms. The ThinkGear ASIC (TGAT/TGAM1), however, outputs this value 512 times a second, or approximately once every 2ms. And it is usually turned on by default. The default can be adjusted at the module level, so please consult the spec sheet for more details.
Because of the high rate at which this value is output, and the
number of bytes of data involved, it is only possible to output the
''16-bit RAW Wave Value'' on the serial communication stream at 57,600 baud and
above. If raw wave information at 9600 baud is desired,
consider the [[#8BIT_RAW Wave Value]] output instead (described above).
===== EEG_POWER =====
This Data Value represents the current magnitude of 8 commonly-recognized
types of EEG frequency bands (brainwaves). It consists of eight 4-byte
floating point numbers in the following order: delta (0.5 - 2.75Hz),
theta (3.5 - 6.75Hz), low-alpha (7.5 - 9.25Hz), high-alpha (10 - 11.75Hz),
low-beta (13 - 16.75Hz), high-beta (18 - 29.75Hz), low-gamma (31 - 39.75Hz),
and mid-gamma (41 - 49.75Hz). These values have no units and therefore are
only meaningful when compared to each other and to themselves, for
considering relative quantity and temporal fluctuations. The floating
point format is standard big-endian IEEE 754, so the 32 bytes of the
Values can therefore be directly cast as a ''float*'' in C (on big-endian
environments) to be used as an array of floats.
By default, output of this Data Value is disabled. When enabled, it is
typically output once a second.
This version of ''EEG_POWER'', using floating point numbers, is only
available in ThinkGear modules, and not in ASIC. For the ASIC
equivalent, see ''[[#ASIC_EEG_POWER_INT]]''. As of ThinkGear
Firmware v1.7.8, the ASIC version is the standard and preferred
format for reading EEG band powers, and this floating point format
is deprecated to backwards-compatibility purposes only.
===== ASIC_EEG_POWER_INT =====
This Data Value represents the current magnitude of 8 commonly-recognized types of EEG (brainwaves). It is the ASIC equivalent of ''[[#EEG_POWER]]'', with the main difference being that this Data Value is output as a series of eight 3-byte unsigned integers instead of 4-byte floating point numbers. These 3-byte unsigned integers are in big-endian format.
The eight EEG powers are output in the following order: delta (0.5 - 2.75Hz),
theta (3.5 - 6.75Hz), low-alpha (7.5 - 9.25Hz), high-alpha (10 - 11.75Hz),
low-beta (13 - 16.75Hz), high-beta (18 - 29.75Hz), low-gamma (31 - 39.75Hz),
and mid-gamma (41 - 49.75Hz). These values have no units and therefore
are only meaningful compared to each other and to themselves, to
consider relative quantity and temporal fluctuations.
By default, output of this Data Value is enabled, and is
typically output once a second.
As of ThinkGear Firmware v1.7.8, this form of ''EEG_POWER'' is the
standard output format for EEG band powers, while the one
described in ''[[#EEG_POWER]]'' is only kept for backwards
compatibility and only accessible through command switches.
Prior to v1.7.8, the ''[[#EEG_POWER]]'' was the standard.
===== Eye Blink Strength =====
This data value is currently unavailable via the ThinkGear Serial Stream APIs. It is not directly available as output from any current ThinkGear hardware. For TGCD, see the ''TG_DATA_BLINK_STRENGTH'' data type for use with the ''TG_GetValueStatus()'' and ''TG_GetValue()'' functions.
===== Mind-wandering Level =====
This unsigned one byte value reports the intensity of the user's Mind-wandering Level. Its value ranges from 0 to 10. A value of 0 means the Level is N/A. A value from 1-10 indicates the Level (with higher values indicating higher levels of Mind-wandering.
====== ThinkGear Packets ======
ThinkGear components deliver their digital data as an asynchronous serial stream of bytes. The serial stream must be parsed and interpreted as ThinkGear Packets in order to properly extract and interpret the [[#ThinkGear Data Values]] described in the chapter above.
A ThinkGear Packet is a packet format consisting of 3 parts:
- Packet Header
- Packet Payload
- Payload Checksum
ThinkGear Packets are used to deliver Data Values (described in
the previous chapter) from a ThinkGear module to an arbitrary receiver
(a PC, another microprocessor, or any other device that can receive a
serial stream of bytes). Since serial I/O programming APIs are different
on every platform, operating system, and language, it is outside the
scope of this document (see your platform's documentation for serial
I/O programming). This chapter will only cover how to interpret the
serial stream of bytes into ThinkGear Packets, Payloads, and finally
into the meaningful Data Values described in the previous chapter.
The Packet format is designed primarily to be robust and
flexible: Combined, the Header and Checksum provide
data stream synchronization and data integrity checks, while
the format of the Data Payload ensures that new data fields
can be added to (or existing data fields removed from) the
Packet in the future without breaking any Packet parsers in
any existing applications/devices. This means that any
application that implements a ThinkGear Packet parser properly
will be able to use newer models of ThinkGear modules most
likely without having to change their parsers or application
at all, even if the newer ThinkGear includes new data fields.
===== Packet Structure =====
Packets are sent as an asynchronous serial stream of bytes.
The transport medium may be UART, serial COM, USB, bluetooth,
file, or any other mechanism which can stream bytes.
Each Packet begins with its Header, followed by its Data
Payload, and ends with the Payload's Checksum Byte, as
follows:
[SYNC] [SYNC] [PLENGTH] [PAYLOAD...] [CHKSUM]
_______________________ _____________ ____________
^^^^^^^^(Header)^^^^^^^ ^^(Payload)^^ ^(Checksum)^
The ''[PAYLOAD...]'' section is allowed to be up to 169 bytes long,
while each of ''[SYNC]'', ''[PLENGTH]'', and ''[CHKSUM]'' are a single byte
each. This means that a complete, valid Packet is a minimum of
4 bytes long (possible if the Data Payload is zero bytes long,
i.e. empty) and a maximum of 173 bytes long (possible if the
Data Payload is the maximum 169 bytes long).
A procedure for properly parsing ThinkGear Packets is given below in [[#Step-By-Step Guide to Parsing a Packet]].
==== Packet Header ====
The Header of a Packet consists of 3 bytes: two synchronization
''[SYNC]'' bytes (''0xAA'' ''0xAA''), followed by a ''[PLENGTH]''
(Payload length) byte:
[SYNC] [SYNC] [PLENGTH]
_______________________
^^^^^^^^(Header)^^^^^^^
The two ''[SYNC]'' bytes are used to signal the beginning of a
new arriving Packet and are bytes with the value ''0xAA''
(decimal 170). Synchronization is two bytes long, instead of
only one, to reduce the chance that ''[SYNC]'' (''0xAA'') bytes
occurring within the Packet could be mistaken for the beginning
of a Packet. Although it is still possible for two consecutive
''[SYNC]'' bytes to appear //within// a Packet (leading to a
parser attempting to begin parsing the middle of a Packet as
the beginning of a Packet) the ''[PLENGTH]'' and ''[CHKSUM]''
combined ensure that such a "mis-sync'd Packet" will never be
accidentally interpreted as a valid packet (see [[#Payload Checksum]] below
for more details).
The ''[PLENGTH]'' byte indicates the length, in bytes, of the
Packet's [[#Data Payload]] ''[PAYLOAD...]'' section, and may be
any value from 0 up to 169. Any higher value indicates an
error (''PLENGTH TOO LARGE''). Be sure to note that
''[PLENGTH]'' is the length of the Packet's **Data Payload**,
NOT of the entire Packet. The Packet's complete length
will always be ''[PLENGTH] + 4''.
==== Data Payload ====
The Data Payload of a Packet is simply a series of bytes.
The number of Data Payload bytes in the Packet is given
by the ''[PLENGTH]'' byte from the Packet Header. The interpretation
of the Data Payload bytes into the [[#ThinkGear Data Values]] described
in Chapter 1 is defined in detail in the [[#Data Payload Structure]]
section below. Note that parsing of the Data Payload typically should **not**
even be attempted until **after** the [[#Payload Checksum Byte]] ''[CHKSUM]''
is verified as described in the following section.
==== Payload Checksum ====
The ''[CHKSUM]'' Byte must be used to verify the integrity of
the Packet's [[#Data Payload]]. The Payload's Checksum is defined as:
- summing all the bytes of the Packet's [[#Data Payload]]
- taking the lowest 8 bits of the sum
- performing the bit inverse (one's compliment inverse) on those lowest 8 bits
A receiver receiving a Packet must use those 3 steps to calculate
the checksum for the [[#Data Payload]] they received, and then compare
it to the ''[CHKSUM]'' Checksum Byte received with the Packet. If
the calculated payload checksum and received ''[CHKSUM]'' values do
not match, the entire Packet should be discarded as invalid. If
they do match, then the receiver may procede to parse the [[#Data
Payload]] as described in the "Data Payload Structure" section below.
===== Data Payload Structure =====
Once the Checksum of a Packet has been verified, the bytes
of the Data Payload can be parsed. The [[#Data Payload]] itself
consists of a continuous series of Data Values, each contained
in a series of bytes called a [[#DataRow Format|DataRow]]. Each DataRow contains
information about what the Data Value represents, the length
of the Data Value, and the bytes of the Data Value itself.
Therefore, to parse a Data Payload, one must parse each DataRow
from it until all bytes of the [[#Data Payload]] have been parsed.
==== DataRow Format ====
A DataRow consists of bytes in the following format:
([EXCODE]...) [CODE] ([VLENGTH]) [VALUE...]
____________________ ____________ ___________
^^^^(Value Type)^^^^ ^^(length)^^ ^^(value)^^
Bytes in parentheses are conditional, meaning that they
only appear in some DataRows, and not in others. See the following description for details.
The DataRow may begin with zero or more ''[EXCODE]'' (**Ex**tended **Code**)
bytes, which are bytes with the value ''0x55''. The number of
''[EXCODE]'' bytes indicates the Extended Code Level. The Extended
Code Level, in turn, is used in conjuction with the ''[CODE]''
byte to determine what type of Data Value this DataRow contains.
Parsers should therefore always begin parsing a DataRow by counting
the number of ''[EXCODE]'' (''0x55'') bytes that appear to determine
the Extended Code Level of the DataRow's ''[CODE]''.
The ''[CODE]'' byte, in conjunction with the Extended Code Level,
indicates the type of Data Value encoded in the DataRow. For
example, at Extended Code Level 0, a ''[CODE]'' of ''0x04'' indicates
that the DataRow contains an eSense Attention value. For a list
of defined ''[CODE]'' meanings, see the [[#CODE Definitions Table]]
below. Note that the ''[EXCODE]'' byte of ''0x55'' will never be used
as a ''[CODE]'' (incidentally, the ''[SYNC]'' byte of ''0xAA'' will never
be used as a ''[CODE]'' either).
If the ''[CODE]'' byte is between ''0x00'' and ''0x7F'', then the
the ''[VALUE...]'' is implied to be 1 byte long (referred to as
a [[#Single-Byte CODEs|Single-Byte Value]]). In this case, there is no ''[VLENGTH]''
byte, so the single ''[VALUE]'' byte will appear immediately after the
''[CODE]'' byte.
If, however, the ''[CODE]'' is greater than ''0x7F'', then a
''[VLENGTH]'' ("Value Length") byte immediately follows the
''[CODE]'' byte, and this is the number of bytes in ''[VALUE...]''
(referred to as a [[#Multi-Byte CODEs|Multi-Byte Value]]). These higher CODEs are
useful for transmitting arrays of values, or values that cannot
be fit into a single byte.
The DataRow format is defined in this way so that any properly
implemented parser will not break in the future if new ''CODE''s
representing arbitrarily long ''DATA...'' values are added (they
simply ignore unrecognized ''CODE''s, but do not break in parsing),
the order of ''CODE''s is rearranged in the Packet, or if some
''CODE''s are not always transmitted in every Packet.
A procedure for properly parsing Packets and DataRows is given below in [[#Step-By-Step Guide to Parsing a Packet]] and [[#Step-By-Step Guide to Parsing DataRows in a Packet Payload]], respectively.
==== CODE Definitions Table ====
=== Single-Byte CODEs ===
Extended (Byte)
Code Level [CODE] [LENGTH] Data Value Meaning
---------- ------ -------- ------------------
0 0x02 - POOR_SIGNAL Quality (0-255)
0 0x03 - HEART_RATE (0-255)
Once/s on EGO.
0 0x04 - ATTENTION eSense (0 to 100)
0 0x05 - MEDITATION eSense (0 to 100)
0 0x06 - 8BIT_RAW Wave Value (0-255)
0 0x07 - RAW_MARKER Section Start (0)
=== Multi-Byte CODEs ===
Extended (Byte)
Code Level [CODE] [LENGTH] Data Value Meaning
---------- ------ -------- ------------------
0 0x80 2 RAW Wave Value: a single big-endian
16-bit two's-compliment signed value
(high-order byte followed by
low-order byte) (-32768 to 32767)
0 0x81 32 EEG_POWER: eight big-endian 4-byte
IEEE 754 floating point values
representing delta, theta, low-alpha
high-alpha, low-beta, high-beta,
low-gamma, and mid-gamma EEG band
power values
0 0x83 24 ASIC_EEG_POWER: eight big-endian
3-byte unsigned integer values
representing delta, theta, low-alpha
high-alpha, low-beta, high-beta,
low-gamma, and mid-gamma EEG band
power values
0 0x86 2 RRINTERVAL: two byte big-endian unsigned
integer representing the milliseconds
between two R-peaks
Any 0x55 - NEVER USED (reserved for [EXCODE])
Any 0xAA - NEVER USED (reserved for [SYNC])
(any Extended Code Level/''CODE'' combinations not listed in the
table above have not yet been defined, but may be added at any
time in the future)
For detailed explanations of the meanings of each type of Data
Value, please refer to the chapter on [[#ThinkGear Data Values]].
Whenever a ThinkGear module is powered on, it will always
start in a standard configuration in which only some of the
Data Values listed above will be output by default. To enable or disable the types of Data Values output by the
ThinkGear, refer to the advanced chapter [[#ThinkGear Command Bytes]].
===== Example Packet =====
The following is a typical Packet. Aside from the ''[SYNC]'', ''[PLENGTH]'', and ''[CHKSUM]'' bytes, all the other bytes (bytes ''[ 3]'' to ''[10]'') are part of the Packet's [[#Data Payload]]. Note that the [[#DataRow Format|DataRows]] within the Payload are **not** guaranteed to appear in every Packet, nor are any DataRows that do appear guaranteed by the Packet specification to appear in any particular order.
byte: value // Explanation
[ 0]: 0xAA // [SYNC]
[ 1]: 0xAA // [SYNC]
[ 2]: 0x08 // [PLENGTH] (payload length) of 8 bytes
[ 3]: 0x02 // [CODE] POOR_SIGNAL Quality
[ 4]: 0x20 // Some poor signal detected (32/255)
[ 5]: 0x01 // [CODE] BATTERY Level
[ 6]: 0x7E // Almost full 3V of battery (126/127)
[ 7]: 0x04 // [CODE] ATTENTION eSense
[ 8]: 0x12 // eSense Attention level of 18%
[ 9]: 0x05 // [CODE] MEDITATION eSense
[10]: 0x60 // eSense Meditation level of 96%
[11]: 0xE3 // [CHKSUM] (1's comp inverse of 8-bit Payload sum of 0x1C)
===== Step-By-Step Guide to Parsing a Packet =====
- Keep reading bytes from the stream until a ''[SYNC]'' byte (''0xAA'') is encountered.
- Read the next byte and ensure it is also a ''[SYNC]'' byte
* If not a ''[SYNC]'' byte, return to step 1.
* Otherwise, continue to step 3.
- Read the next byte from the stream as the ''[PLENGTH]''.
* If ''[PLENGTH]'' is 170 (''[SYNC]''), then repeat step 3.
* If ''[PLENGTH]'' is greater than 170, then return to step 1 (''PLENGTH TOO LARGE'').
* Otherwise, continue to step 4.
- Read the next ''[PLENGTH]'' bytes of the ''[PAYLOAD...]'' from the stream, saving them into a storage area (such as an ''unsigned char payload[256]'' array). Sum up each byte as it is read by incrementing a checksum accumulator (''checksum += byte'').
- Take the lowest 8 bits of the checksum accumulator and invert them. Here is the C code:
checksum &= 0xFF;
checksum = ~checksum & 0xFF;
- Read the next byte from the stream as the''[CHKSUM]'' byte.
* If the ''[CHKSUM]'' does not match your calculated chksum (''CHKSUM FAILED'').
* Otherwise, you may now parse the contents of the Payload into DataRows to obtain the Data Values, as described below.
* In either case, return to step 1.
==== Step-By-Step Guide to Parsing DataRows in a Packet Payload ====
Repeat the following steps for parsing a DataRow until all bytes in the payload[] array (''[PLENGTH]'' bytes) have been considered and parsed:
- Parse and count the number of ''[EXCODE]'' (''0x55'') bytes that may be at the beginning of the current DataRow.
- Parse the ''[CODE]'' byte for the current DataRow.
- If ''[CODE] >= 0x80'', parse the next byte as the ''[VLENGTH]'' byte for the current DataRow.
- Parse and handle the ''[VALUE...]'' byte(s) of the current DataRow, based on the DataRow's ''[EXCODE]'' level, ''[CODE]'', and ''[VLENGTH]''.
- If not all bytes have been parsed from the ''payload[]'' array, return to step 1. to continue parsing the next DataRow.
==== Sample C Code for Parsing a Packet ====
The following is an example of a program, implemented in C,
which reads from a stream and (correctly) parses Packets
continuously. Search for the word TODO for the two sections
which would need to be modified to be appropriate for your
application.
**//Note//**: For simplicity, error checking and handling
for standard library function calls have been omitted. A
real application should probably detect and handle all errors
gracefully.
#include
#define SYNC 0xAA
#define EXCODE 0x55
int parsePayload( unsigned char *payload, unsigned char pLength ) {
unsigned char bytesParsed = 0;
unsigned char code;
unsigned char length;
unsigned char extendedCodeLevel;
int i;
/* Loop until all bytes are parsed from the payload[] array... */
while( bytesParsed < pLength ) {
/* Parse the extendedCodeLevel, code, and length */
extendedCodeLevel = 0;
while( payload[bytesParsed] == EXCODE ) {
extendedCodeLevel++;
bytesParsed++;
}
code = payload[bytesParsed++];
if( code & 0x80 ) length = payload[bytesParsed++];
else length = 1;
/* TODO: Based on the extendedCodeLevel, code, length,
* and the [CODE] Definitions Table, handle the next
* "length" bytes of data from the payload as
* appropriate for your application.
*/
printf( "EXCODE level: %d CODE: 0x%02X length: %d\n",
extendedCodeLevel, code, length );
printf( "Data value(s):" );
for( i=0; i 169 ) continue;
/* Collect [PAYLOAD...] bytes */
fread( payload, 1, pLength, stream );
/* Compute [PAYLOAD...] chksum */
checksum = 0;
for( i=0; i
==== ThinkGearStreamParser C API ====
The ThinkGearStreamParser API is a library which implements
the parsing procedure described above and abstracts it into
two simple functions, so that the programmer does not need to
worry about parsing Packets and DataRows at all. All that is left
is for the programmer to get the bytes from the data stream,
stuff them into the parser, and then define what their program
does with the ''Value[]'' bytes from each [[#DataRow format|DataRow]] that is received and parsed.
The source code for the ThinkGearStreamParser API is provided,
and consists of a ''.h'' header file and a ''.c'' source file. It is
implemented in pure ANSI C for maximum portability to all
platforms (including microprocessors).
Using the API consists of 3 steps:
- Define a data handler (callback) function which handles (acts upon) Data Values as they're received and parsed.
- Initialize a ''ThinkGearStreamParser'' struct by calling the ''THINKGEAR_initParser()'' function.
- As each byte is received from the data stream, the program passes it to the ''THINKGEAR_parseByte()'' function. This function will automatically call the data handler function defined in 1) whenever a Data Value is parsed.
The following subsections are excerpts from the ''ThinkGearStreamParser.h'' header file, which serves as the API documentation.
=== Constants ===
/* Parser types */
#define PARSER_TYPE_NULL 0x00
#define PARSER_TYPE_PACKETS 0x01 /* Stream bytes as ThinkGear Packets */
#define PARSER_TYPE_2BYTERAW 0x02 /* Stream bytes as 2-byte raw data */
/* Data CODE definitions */
#define PARSER_BATTERY_CODE 0x01
#define PARSER_POOR_SIGNAL_CODE 0x02
#define PARSER_ATTENTION_CODE 0x04
#define PARSER_MEDITATION_CODE 0x05
#define PARSER_RAW_CODE 0x80
=== THINKGEAR_initParser() ===
/**
* @param parser Pointer to a ThinkGearStreamParser object.
* @param parserType One of the PARSER_TYPE_* constants defined
* above: PARSER_TYPE_PACKETS or
* PARSER_TYPE_2BYTERAW.
* @param handleDataValueFunc A user-defined callback function that will
* be called whenever a data value is parsed
* from a Packet.
* @param customData A pointer to any arbitrary data that will
* also be passed to the handleDataValueFunc
* whenever a data value is parsed from a
* Packet.
*
* @return -1 if @c parser is NULL.
* @return -2 if @c parserType is invalid.
* @return 0 on success.
*/
int
THINKGEAR_initParser( ThinkGearStreamParser *parser, unsigned char parserType,
void (*handleDataValueFunc)(
unsigned char extendedCodeLevel,
unsigned char code, unsigned char numBytes,
const unsigned char *value, void *customData),
void *customData );
=== THINKGEAR_parseByte() ===
/**
* @param parser Pointer to an initialized ThinkGearDataParser object.
* @param byte The next byte of the data stream.
*
* @return -1 if @c parser is NULL.
* @return -2 if a complete Packet was received, but the checksum failed.
* @return 0 if the @c byte did not yet complete a Packet.
* @return 1 if a Packet was received and parsed successfully.
*
*/
int
THINKGEAR_parseByte( ThinkGearStreamParser *parser, unsigned char byte );
=== Example ===
Here is an example program using the ThinkGearStreamParser API. It is
very similar to the example program described above, simply printing
received Data Values to stdout:
#include
#include "ThinkGearStreamParser.h"
/**
* 1) Function which acts on the value[] bytes of each ThinkGear DataRow as it is received.
*/
void
handleDataValueFunc( unsigned char extendedCodeLevel,
unsigned char code,
unsigned char valueLength,
const unsigned char *value,
void *customData ) {
if( extendedCodeLevel == 0 ) {
switch( code ) {
/* [CODE]: ATTENTION eSense */
case( 0x04 ):
printf( "Attention Level: %d\n", value[0] & 0xFF );
break;
/* [CODE]: MEDITATION eSense */
case( 0x05 ):
printf( "Meditation Level: %d\n", value[0] & 0xFF );
break;
/* Other [CODE]s */
default:
printf( "EXCODE level: %d CODE: 0x%02X vLength: %d\n",
extendedCodeLevel, code, valueLength );
printf( "Data value(s):" );
for( i=0; i
A few things to note:
* The ''handleDataValueFunc()'' callback should be implemented to execute quickly, so as not to block the thread which is reading from the data stream. A more robust (and useful) program would probably spin off the thread which reads from the data stream and calls ''handleDataValueFunc()'', and define ''handleDataValueFunc()'' to simply save the Data Values it receives, while the main thread actually uses the saved values for displaying to screen, controlling a game, etc. Threading is outside the scope of this manual.
* The code for opening a serial communication port data stream for reading varies by operating system and platform. Typically, it is very similar to opening a normal file for reading. Serial communication is outside the scope of this manual, so please consult the documentation for "Serial I/O" for your platform for details. As an alternative, you may use the ThinkGear Communications Driver (TGCD) API, which can take care of opening and reading from serial I/O streams on some platforms for you. Use of that interface is described in the [[:developer_tools_2.2_development_guide]] and TGCD API documentation.
* Most error handling has been omitted from the above code for clarity. A properly written program should check all error codes returned by functions. Please consult the ''ThinkGearStreamParser.h'' header file for details about function parameters and return values.
====== ThinkGear Command Bytes ======
Upon power-on, ThinkGear modules/AISCs always start in their factory-programmed default state. For example, ThinkGear modules with FWv1.7.13 and earlier always start at 9600 baud, and outputting only battery, poor signal, ASIC_EEG, Attention, and Meditation values. This configuration is intended to be sufficient for most toy, game, and demo applications. After power-on however, the ThinkGear module can be sent Command Bytes in order to change its configuration, such as switching to 57.6k baud, or enabling raw wave values output. ThinkGear Command Bytes are intended as an advanced feature for performing some customization of the behavior ThinkGear hardware.
ThinkGear Command Bytes are sent to the ThinkGear hardware through the same UART interface used to receive Packet bytes. A Command Byte is a single byte (8-bit) value with certain bits set. This section describes which bits to set in order to change the configuration of a ThinkGear module.
Note that after being power cycled, the ThinkGear module returns to its default settings described above.
Also note that, before an application sends any command bytes to the ThinkGear, it should first make sure it has read at least one complete, valid Packet from the ThinkGear, to ensure it sends Command Bytes at the proper baudrate. Sending Command Bytes at the wrong baudrate may result in the ThinkGear being rendered inoperable until it is power-cycled (reset back to default configuration).
===== Command Byte Syntax =====
A Command Byte is formed by 8 bits, each of which are either set or unset. The lowest (least significant) four bits are used to control modes, such as 9600 vs. 57.6k baud mode, attention output enabled or disabled mode, etc. The upper (most significant) four bits, known as the "Command Page", define the meaning of the lower four bits.
For example, a Command Byte of ''0x0E'' has the bit pattern of ''0000 1110''. The upper (most significant) four bits are ''0000'', which refers to Command Page zero. Looking on the table below for Command Page zero (for 1.6 firmware), we see that the lower four bits of 1110 are used to control the settings for baudrate, raw wave output, meditation output, and attention output, respectively. Because the baudrate bit is 1, the baudrate of the module will be set to 57.6k mode. Because the raw wave output and meditation output bits are each 1, each of those types of output will be enabled. Because the attention output bit is 0, attention output becomes disabled. Hence, sending a byte of ''0x0E'' to the ThinkGear module instructs it to operate at 57.6k baud mode with raw and meditation values output in packets, but no attention values.
Please note that the ordering of the Command Pages significantly changed between versions 1.6 and 1.7 of the module firmware. Also note that ThinkGear ASIC (such as MindWave and MindWave Mobile) only recognize the 4 Command Bytes on Page 0 for 1.7 firmware; all other Command Bytes may put the ThinkGear ASIC into an inoperable state until it is power-cycled. Please refer to the documentation that came with your ThinkGear hardware, along with the appropriate table below to determine which Command Bytes are valid for your hardware.
===== Firmware 1.6 Command Byte Table =====
Page 0 (0000____) (0x0_): **
bit[0] (____0001): Set/unset to enable/disable attention output
bit[1] (____0010): Set/unset to enable/disable meditation output
bit[2] (____0100): Set/unset to enable/disable raw wave output
bit[3] (____1000): Set/unset to use 57.6k/9600 baud rate
Page 1 (0001____) (0x1_):
bit[0] (____0001): Set/unset to enable/disable EEG powers output
bit[1] (____0010): Set/unset to use 10-bit/8-bit raw wave output
Page 15 (1111____) (0xF_):
bit[0] (____0001): Set/unset to enable/disable Testmode
**: After sending this Page byte, the application itself must change itself to begin communicating at the new requested baud rate, and then wait at that new requested baud rate for a complete, valid Packet to be received from the ThinkGear before attempting to send any other command bytes to the ThinkGear. Sending another command byte before performing this check may put the ThinkGear module into an indeterminate (and inoperable) state until it is power-cycled.
===== Firmware 1.7 Command Byte Table =====
ThinkGear ASIC only recognizes Command Bytes from Page 0 below. Any other Command Bytes may put it into an inoperable state until it is power cycled.
Page 0 (0000____) (0x0_): STANDARD/ASIC CONFIG COMMANDS* **
00000000 (0x00): 9600 baud, normal output mode
00000001 (0x01): 1200 baud, normal output mode
00000010 (0x02): 57.6k baud, normal+raw output mode
00000011 (0x03): 57.6k baud, FFT output mode
Page 1 (0001____) (0x1_): RAW WAVE OUTPUT
bit[0] (____0001): Set/unset to enable/disable raw wave output
bit[1] (____0010): Set/unset to use 10-bit/8-bit raw wave output
bit[2] (____0100): Set/unset to enable/disable raw marker output
bit[3] (____1000): Ignored
Page 2 (0010____) (0x2_): MEASUREMENTS OUTPUTS
bit[0] (____0001): Set/unset to enable/disable poor quality output
bit[1] (____0010): Set/unset to enable/disable EEG powers (int) output
bit[2] (____0100): Set/unset to enable/disable EEG powers (legacy/floats) output
bit[3] (____1000): Set/unset to enable/disable battery output***
Page 3 (0011____) (0x3_): ESENSE OUTPUTS
bit[0] (____0001): Set/unset to enable/disable attention output
bit[1] (____0010): Set/unset to enable/disable meditation output
bit[2] (____0100): Ignored
bit[3] (____1000): Ignored
Page 6 (0110____) (0x6_): BAUD RATE SELECTION* **
01100000 (0x60): No change
01100001 (0x61): 1200 baud
01100010 (0x62): 9600 baud
01100011 (0x63): 57.6k baud
*: Note that pages 0 and 6 are a little different from most command pages. While most pages use each of the 4 bits as an enable/disable switch commands for separate setting, pages 0 and 6 use the entire command value as a single command.
**: After sending this Page byte, the application itself must change itself to begin communicating at the new requested baud rate, and then wait at that new requested baud rate for a complete, valid Packet to be received from the ThinkGear before attempting to send any other command bytes to the ThinkGear. Sending another command byte before performing this check may put the ThinkGear module into an indeterminate (and inoperable) state until it is power-cycled.
***: Battery level sampling not available on some ThinkGear models.