Contributions:SerialWidgetADC: Difference between revisions
No edit summary |
|||
| Line 80: | Line 80: | ||
* Between sample-blocks, the widget may send other messages provided they do not begin with <code>\x01\x00</code> or <code>\x00\x01</code>. This allows it to send: | * Between sample-blocks, the widget may send other messages provided they do not begin with <code>\x01\x00</code> or <code>\x00\x01</code>. This allows it to send: | ||
** BCI2000 [[Programming_Reference:Events#Descriptor_Syntax|Event descriptor]] lines, when an event occurs (BCI2000 will attempt to interpret any line that does not begin with <code>{</code> as an Event descriptor); | ** BCI2000 [[Programming_Reference:Events#Descriptor_Syntax|Event descriptor]] lines, when an event occurs (BCI2000 will attempt to interpret any line that does not begin with <code>{</code> as an Event descriptor); | ||
** error messages | ** error messages (BCI2000 will issue a fatal error in response to any line that begins with <code>{</code> and contains the substring <code>_ERROR_</code>, which is true of error messages sent by the Keyhole library); | ||
** any other JSON output ( | ** any other JSON output (BCI2000 will simply ignore any other line beginning with the <code>{</code> character). | ||
==See also== | ==See also== | ||
Revision as of 17:42, 23 September 2023
Synopsis
An ADC that allows signals to be acquired from programmable serial-port devices such as the Arduino, Teensy, Pico, etc.
Location
http://www.bci2000.org/svn/trunk/src/contrib/SignalSource/SerialWidget
Versioning
Authors
Jeremy Hill (hill@neurotechcenter.org)
Version History
- 2023-09-22: Initial public release
Source Code Revisions
- Initial development: r7613
- Known to compile under: r7613
- Broken since: --
Functional Description
The SerialWidgetADC filter is the key component of the SerialWidget source module, and can acquire signals from a serial-port-equipped embedded system such as an Arduino, Teensy or Pico microcontroller (hereafter referred to as a "widget").
This ADC includes all the functionality of the SerialInterface Extension (which allows widgets to be used alongside other source acquisition hardware, providing auxiliary input and output). The difference is that the ADC allows the primary signal to be acquired from the widget as well.
Developer note: the SerialInterface Extension and the SerialWidgetADC are aware of each other, and the Extension will automatically disable itself in the presence of the ADC, so there is no conflict if the Extension is turned on in CMake during the build.
Parameters
Parameters for handling widgets are described under Contributions:SerialInterface.
As in the SerialInterface Extension, the --SerialPort=... parameter must be supplied on the command-line and cannot be changed without relaunching BCI2000.
For example, your BCI2000 script might contain the line:
start executable SerialWidget --local --SerialPort=COM4:baud=9600,dtr=on
The same is true of the --PublishCommand=... parameter, if used.
The one difference relative to SerialInterface lies in the handling of custom StartCommand and StopCommand messages. The StartCommand message (if specified) is sent to the widget as soon as the user presses Set Config rather than waiting for Start. Similarly, the StopCommand message (if specified) is not sent when you press Suspend: rather, it is sent just prior to disconnecting and reconnecting on subsequent Set Config occurrences, and/or when BCI2000 quits.
If you specify a --PublishCommand on the command-line, and provided your widget sketch supports it, the widget can also define its own Parameters as described in the SerialInterface documentation.
Parameters common to all source modules are described under User Reference:DataIOFilter.
SerialWidgetADC adds only one parameter of its own:
SourceChPins
This is a list of integer values, one per channel. The widget interprets these numbers when deciding how to acquire signals (this depends entirely on the way the widget is programmed, but the simplest way would be to interpret them as indices of the pins from which signals should be read—for example, as arguments to analogRead() or digitalRead() in the Arduino language).
States
The SerialWidgetADC does not define any State Variables or Events of its own, but can create Events as directed by the widget, as described in the SerialInterface documentation.
Widget Programming
The requirements for widget programming are somewhat more stringent than they were for compatibility with the SerialInterface Extension. An example SerialWidgetADC-compatible sketch is provided in the ExampleSourceSketch subdirectory (programs in the Arduino IDE are called "sketches" for some reason). ExampleSourceSketch.ino makes use of the Keyhole library, which can be installed via the IDE's library manager and which makes it easy for sketches to respond to serial-port commands and to allow their variables to be read and written. This sketch is an expanded version of the TTLExampleSketch.ino provided with the SerialInterface Extension, and it handles TTL input and output in the same way; however, it also fulfills the following additional requirements for SerialWidgetADC compatibility:
- To enable signal acquisition, the sketch must support the following commands, sent over the serial port (the specific numbers are just examples, but the variable names and syntax must be supported as shown):
samplesPerSecond=1000\n samplesPerBlock=40\n sourcePins="26 27"\n
- The widget is responsible for sample timing. When the widget has acquired a complete sample-block (the specified number of signal samples from all the specified pins) it must send a sample-block header, which is either
\x01\x00or\x00\x01followed by a line-ending (\nor\r\n). Disregarding the line-ending, the header is actually the 16-bit binary representation of the number 1 (the former version is little-endian, the latter big-endian). Based on the header, the ADC will automatically determine whether the subsequent sample data need to be endian-swapped. The header can be sent easily using the following Arduino code:
const uint16_t endianMarker = 1; Serial.write( (uint8_t*)&endianMarker, sizeof(endianMarker) ); Serial.println(); // don't forget the line-ending
- Immediately after the sample-block header, the widget must send the raw sample data as packed 32-bit floats. The size of this payload is exactly (number of pins)*
samplesPerBlock*sizeof(float)bytes. All the different channels' values for the first sample should be sent in the order specified bysourcePins, then all the channels' values for the second sample, and so on.
Serial.write( (uint8_t *)data, numberOfBytes ); Serial.println(); // a line-ending at this point is optional, but makes debugging easier in the Arduino IDE's Serial Monitor Serial.flush(); // important for good timing
- Between sample-blocks, the widget may send other messages provided they do not begin with
\x01\x00or\x00\x01. This allows it to send:- BCI2000 Event descriptor lines, when an event occurs (BCI2000 will attempt to interpret any line that does not begin with
{as an Event descriptor); - error messages (BCI2000 will issue a fatal error in response to any line that begins with
{and contains the substring_ERROR_, which is true of error messages sent by the Keyhole library); - any other JSON output (BCI2000 will simply ignore any other line beginning with the
{character).
- BCI2000 Event descriptor lines, when an event occurs (BCI2000 will attempt to interpret any line that does not begin with
See also
User Reference:DataIOFilter, Programming Reference:GenericADC Class, Contributions:SerialInterface