Jump to content

Contributions:SerialWidgetADC: Difference between revisions

From BCI2000 Wiki
Jhill (talk | contribs)
No edit summary
Jhill (talk | contribs)
No edit summary
Line 6: Line 6:
which allows widgets to be used alongside other source acquisition hardware. The difference is that the ADC allows
which allows widgets to be used alongside other source acquisition hardware. The difference is that the ADC allows
the primary signal to be acquired from the widget as well.
the primary signal to be acquired from the widget as well.
 
 
 
==Parameters==
==Parameters==


Line 27: Line 28:


This is a list of integer values, one per channel. The microcontroller interprets these numbers when deciding how to acquire signals (this depends entirely on the way the microcontroller 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 <code>analogRead()</code> or <code>digitalRead()</code> in the Arduino language).
This is a list of integer values, one per channel. The microcontroller interprets these numbers when deciding how to acquire signals (this depends entirely on the way the microcontroller 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 <code>analogRead()</code> or <code>digitalRead()</code> in the Arduino language).


==States==
==States==
Line 32: Line 34:
The ''SerialWidgetADC'' does not define any State Variables or Events of its own, but can create Events as directed by the widget, [[Contributions:SerialInterface#Defining_BCI2000_Parameters_and_Events_from_the_Widget|as described in the SerialInterface documentation]].
The ''SerialWidgetADC'' does not define any State Variables or Events of its own, but can create Events as directed by the widget, [[Contributions:SerialInterface#Defining_BCI2000_Parameters_and_Events_from_the_Widget|as described in the SerialInterface documentation]].


The requirements for widget programming are somewhat more stringent than for the SerialInterface Extension. Widget programs in the Arduino IDE are called "sketches" for some reason.
 
An example SerialWidetADC-compatible sketch is provided in [https://bci2000.org/svn/trunk/src/contrib/SignalSource/SerialWidget/ExampleSourceSketch/ the ExampleSourceSketch subdirectory]. It makes use of [https://www.arduino.cc/reference/en/libraries/keyhole 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 provided with the Extension, and it handles TTL input and output in the same way, but it also fulfills the following additional requirements for SerialWidgetADC compatibility:
==Widget Programming==
 
The requirements for widget programming are somewhat more stringent than for the SerialInterface Extension.
An example SerialWidetADC-compatible sketch is provided in [https://bci2000.org/svn/trunk/src/contrib/SignalSource/SerialWidget/ExampleSourceSketch/ the ExampleSourceSketch subdirectory] (programs in the Arduino IDE are called "sketches" for some reason). ''ExampleSourceSketch.ino'' makes use of [https://www.arduino.cc/reference/en/libraries/keyhole 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):
* 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):
Line 40: Line 45:
   sourcePins="26 27"\n
   sourcePins="26 27"\n


* When the microcontroller 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 <code>\x01\x00\r\n</code> or <code>\x00\x01\r\n</code>. Excluding the <code>\r\n</code> line-ending, this is actually the binary 16-bit representation of the number 1 (the former 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:
* When the microcontroller 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 <code>\x01\x00\r\n</code> or <code>\x00\x01\r\n</code>. If we disregard the <code>\r\n</code> line-ending, this is actually the binary 16-bit 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;
   const uint16_t endianMarker = 1;
   Serial.write( (uint8_t*)&endianMarker, sizeof(endianMarker) );
   Serial.write( (uint8_t*)&endianMarker, sizeof(endianMarker) );
   Serial.println(); // don't forget the line-ending
   Serial.println(); // don't forget the line-ending
    
    
* Immediately after the sample-block header, the microcontroller must send the raw sample data as packed 32-bit floats. The size of this payload is exactly (number of pins)*<code>samplesPerBlock</code>*4 bytes (because a <code>float</code> is 4 bytes).  All the different channels' values for the first sample should be sent in the order specified by <code>sourcePins</code>, then all the channels' values for the second sample, and so on.
* Immediately after the sample-block header, the microcontroller must send the raw sample data as packed 32-bit floats. The size of this payload is exactly (number of pins)*<code>samplesPerBlock</code>*4 bytes (because a <code>float</code> is 4 bytes).  All the different channels' values for the first sample should be sent in the order specified by <code>sourcePins</code>, then all the channels' values for the second sample, and so on.

Revision as of 22:18, 22 September 2023

Function

The SerialWidgetADC filter is the key component of the SerialWidget source module, and can acquire signals from a serial-port-equipped microcontroller such as an Arduino, Teensy or Pico (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. The difference is that the ADC allows the primary signal to be acquired from the widget as well.


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 microcontroller interprets these numbers when deciding how to acquire signals (this depends entirely on the way the microcontroller 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 for the SerialInterface Extension. An example SerialWidetADC-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
  • When the microcontroller 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\x00\r\n or \x00\x01\r\n. If we disregard the \r\n line-ending, this is actually the binary 16-bit 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 microcontroller must send the raw sample data as packed 32-bit floats. The size of this payload is exactly (number of pins)*samplesPerBlock*4 bytes (because a float is 4 bytes). All the different channels' values for the first sample should be sent in the order specified by sourcePins, then all the channels' values for the second sample, and so on.
  Serial.write( (uint8_t *)data, numberOfBytes );
  Serial.println(); // optional, but makes debugging easier in the Arduino IDE's Serial Monitor
  Serial.flush();   // important for good timing

See also

User Reference:DataIOFilter, Programming Reference:GenericADC Class, Contributions:SerialInterface