Alpha power battles with toy helicopters

About this project

What is more exciting than a brain-controlled helicopter battle against two people? We only need two brains, one OpenBCI board, one Arduino board, two digital potentiometers, and two toy helicopters. Are you ready for this?

Components and Supplies

Get the hardware

We need to indispensable things to “hack” the helicopter tottle:

  • Two Digital Potentiometer (DigiPot) from Microchip Technology. You can check the Digital Potentiometer documentation file here. The digiPot ranges from 0 to 5K, so plan to use the only 1/10th of the digiPot wiper range.

pot.jpg

  • Two PCBs & Breadboard 0.65mm Pitch SOIC (20 pins) to DIP Adapter from SchmartBoard. You can download the documentation file here. We will use this adapter to connect the helicopter remote controller to the new potentiometer and the Arduino board.

2150117.png

  • Two helicopter toys from Amazon. It’s a kid toy, but it works perfectly for what we want to do. You can even choose the color that you prefer among blue, red, and yellow. The helicopter comes with a remote controller, USB charger, and manual instructions. We chose red and yellow to distinguish them. You can buy them here.
  • Arduino UNO. Arduino Uno board makes it very easy to use the ATmega328 (datasheet) processor by providing easy access to most of the pins via the headers. In addition, it has 14 digital input/output pins (of which 6 can be used as PWM outputs), 6 analog inputs, a 16 MHz quartz crystal, a USB connection, a power jack, an ICSP header and a reset button. It contains everything needed to support the microcontroller; simply connect it to a computer with a USB cable or power it with an AC to DC adapter or battery to get started. You can find more info and buy it here.

Arduino_Uno_-_R3.jpg

Get the software

  • Arduino Software (IDE). The open-source Arduino Integrated Development Environment – or Arduino Software (IDE) – contains a text editor for writing code, a message area, a text console, a toolbar with buttons for common functions and a series of menus. It connects to the Arduino hardware to upload programs and communicate with them.
  • OpenBCI GUI_Alpha_Power_Battle based in Processing (Java). The OpenBCI GUI The Helicopter code for now only runs on Processing version 2.2.1. Please, download the version here.

Set up the hardware

Remote Controller

We have done this before when we controlled a toy helicopter by using alpha waves in an older post. You can follow the instructions step by step of the process here.

Since we want to control the helicopter throttle with alpha waves, we need to remove the piece from the remote controller that says the helicopter either to go up or go down. This piece is known as a potentiometer. To do so, we need to open the remote controller by unscrewing the cover. Once it’s removed, we need to identify the potentiometer and desoldered from the remote controller board. Afterward, we need to attach two jumpers in its place. The idea is to attach this two jumpers to the digital potentiometer so as it can be controlled by the Arduino, and not with the remote controller per se.

IMG_20160719_111946839
Figure 1.  Remote control of the helicopter with no cover.

Digital potentiometers and Arduino board

Each player will have its digital potentiometer that will be controlled by the values sent by the OpenBCI_GUI. We need to connect them to the Arduino UNO board accordingly to the schematics shown in the figure below.

alpha_nocontroller
Figure 2. Schematics of the digital potentiometers attached to

Schematics

This is how the hardware looks like when you attach the jumpers from the remote controllers to the breadboards. The jumpers are attached in the following way:

  • The jumper above is connected to PIN 12 of the digipot.
  • The jumper below is connected to PIN 13 of the digipot.

 

alphapowerbattle3.png
Figure 3. Schematics of the hardware to set up the remote controller, the Arduino board, and the digital potentiometers.

 

OpenBCI 32bit Board

Since we are using only one OpenBCI board for two people, we must adapt how we attach the wires for safety reasons. One of the players is going to use the first four channels (1-4 channels), and the second one will use the second four channels (5-8 channels). Both players need a different reference point, but they can share the ground.

openbciboard.png
Figure 4. Schematics of the pins that need to be used in the OpenBCI 32bit board for the players.

Set Up the Software

STEP 1: Download Arduino code and OpenBCI GUI

The alpha power battle and the throttle control code are in the Processing sketch tabs ‘EEG_Processing‘ and ‘Helicopter‘. The GUI already has a peak detection, so we look to see if the peak is in the alpha range. If it is, and if it’s strong enough against the background noise, then a character is sent out to the Arduino that controls the helicopter. The strength of the alpha wave is encoded in the characters ‘A’ to ‘Z’. Then Processing sends the prefix ‘$’ followed by the character code. Arduino gets the prefix, and then apply the next value it gets to the potentiometer. It all took about half a day of hacking on the original OpenBCI_GUI, so I’m sure there’s room for improvement. You can download both the Arduino code and the OpenBCI GUI here.

Take the entire OpenBCI_GUI folder and put it in your Documents/Processing folder. If you already have an OpenBCI_GUI and don’t want to over-write it, create a subfolder in the Processing folder called ‘copter control’ for this one to live in.

Take the entire ‘CopterControl‘ folder and put it in your Arduino folder: Documents/Arduino

STEP 3: Upload Arduino code to Arduino UNO

For this setup, you need to plot the Arduino board with a USB to the computer. You must choose in Tools > Board the Arduino board that you are using. In our case, we are using “Arduino/Genuino UNO” connected to a digital potentiometer connected to the hacked throttle control of a toy helicopter. Also, do not forget to select the serial port of the Arduino. If you have Windows, you will see your port name under the name of “COM X“, whereas you will see “/dev/tty.usbmodem14131” if you own either a Mac or a Linux OS X.

arduino.png
Figure 5. Screenshot of the Arduino code.

Helicopter Control

The Helicopter Control code controls the toy helicopter by receiving the information sent by the Processing code via characters and creating a channel of communication with the digital potentiometer.

First of all, we need to include the libraries that we want to use, and create the variables that we will use to determine when the helicopter has to fly or not. Also, we need the define the variables from the digipot accordingly to the pins that we used to set up the hardware.

// include SPI library
#include <SPI.h>
// digitPot defines
#define CS 10
#define RST 9
#define READ_REG 0x0C
#define WRITE_REG 0x00
#define INCR 0x04
#define DECR 0x08
#define TCON0 0x40
#define TCON1 0xA0

// create variables
boolean testingPot = false; // used to test initial pot function
boolean expectingValue = false;
boolean throttling = false; 
unsigned long throttleTimer;
int throttleDuration = 2000; // 2 seconds of throttle duration
byte throttleSetting = 0x00;
byte lastThrottleSetting;
byte wiper[4] = {0x00, 0x10, 0x60, 0x70}; // address of each wiper register
byte wiperPos[4] = {0x01, 0x02, 0x03, 0x04};
int resistance;

After that, we setup the serial port with the baud rate from the Arduino board. Also, we configure the pins mode and the digital writes accordingly to Figure 4; we pre-set a delay, and we start with the throttle down.

void setup() {
Serial.begin(115200);
SPI.begin();
SPI.beginTransaction(SPISettings(10000000, MSBFIRST, SPI_MODE0));
Serial.println("OpenBCI Alpha Wave Helicopter Control");
pinMode(CS,OUTPUT); digitalWrite(CS,HIGH);
pinMode(RST,OUTPUT); digitalWrite(RST,LOW); // digipot reset pin active low
delay(100); digitalWrite(RST,HIGH);
throttleDown();
throttleSetting = lastThrottleSetting = 0x00;
Serial.print("trottle: ");Serial.println(throttleSetting);
}

We create a loop based on time to control the time the helicopter is up and down, and we set a resistance for the throttle.

void loop() {
 if((millis() - throttleTimer > throttleDuration) && throttling){
  throttleSetting = 0; 
  throttleDown();
  throttling = false;
 }
 if(throttleSetting != lastThrottleSetting){
  setResistance(throttleSetting);
  Serial.print("trottle: ");Serial.println(throttleSetting,DEC);
  lastThrottleSetting = throttleSetting;
 }
 eventSerial();
}

We configure the event serial to receive the characters sent from the OpenBCI GUI. The expecting value is ASCII encoded and goes from A to Z (26 letters), where A=0, B=1, C=2, and so on. These values are proportionally related the “strength” controlsof the alpha waves registered in the OpenBCI GUI.

void eventSerial(){
 while (Serial.available()) {
  char inChar = (char)Serial.read();
  // Serial.print("Received "); Serial.println(inChar);
   if(expectingValue){
    throttleSetting = byte(inChar - 'A'); 
    // variable is ascii encoded 0=A, 1=B, etc
    throttleSetting = map(throttleSetting, 0,26,0,255);
    // Serial.print("trottle: ");Serial.println(throttleSetting);
    throttleTimer = millis();
    throttling = true;
    expectingValue = false;
    return; // get outa here
  }
 Serial.print("Received "); Serial.println(inChar);
 switch (inChar) {
  // Processing must send the $ to tell Arduino the next value is special
  case '$': 
   if(expectingValue){
    return;
   }
   throttleUp();
   expectingValue = true;
   break;
   default:
   break;
   }
 }
}

The Digital Potentiometer

The DigiPot code controls the digital potentiometer connected to the Arduino. There are five void functioncontrol when the helicopter goes up, when goes down, how to set the resistance, when to read wipers, and when to check possible errors.

The throttleDown() sets the digpot wipers to full on for lowest current output. Digital writes are activated or disconnected depending on the inputs.

void throttleDown(){ // set the digipot wipers to full on for lowest current output
 byte command, error;
 digitalWrite(CS,LOW); // activate with W & B connected, A disconnected
 error = SPI.transfer(TCON0 | WRITE_REG);
 SPI.transfer(0xBF); // 1011 1111 // connect A for testing resistance
 checkError(error,TCON0);
 digitalWrite(CS,HIGH);
 delay(1);
 digitalWrite(CS,LOW);
 error = SPI.transfer(TCON1 | WRITE_REG);
 SPI.transfer(0xBB); // 1011 1011
 digitalWrite(CS,HIGH);
 checkError(error,TCON1);
 for(int i=0; i<4; i++){
  digitalWrite(CS,LOW);
  error = SPI.transfer(wiper[i] | WRITE_REG);
  SPI.transfer(0x00); // connect W to B only see W resistance
  digitalWrite(CS,HIGH);
  checkError(error,wiper[i]);
 }
 Serial.println("throttled down");
}

The throttleUp()  works the same way as the trottleDown() and it is activated depending on the CS input that comes from the Arduino.

void throttleUp(){
 byte error;
 digitalWrite(CS,LOW); // activate with only Pot 0 engaged [lower right]
 error = SPI.transfer(TCON0 | WRITE_REG);
 SPI.transfer(0x8F); // 1000 1111 // connect A for testing resistance
 checkError(error,TCON0);
 digitalWrite(CS,HIGH);
 delay(10);
 digitalWrite(CS,LOW);
 error = SPI.transfer(TCON1 | WRITE_REG);
 SPI.transfer(0x88); // 1000 1000
 digitalWrite(CS,HIGH);
 checkError(error,TCON1);
}

The following functions are related wit the resistance set up, the wipers reading, and checking possible errors through the Arduino code.

void setResistance(byte r){
 byte error;
 digitalWrite(CS,LOW); // activate with W & B connected, A disconnected
 error = SPI.transfer(wiper[0] | WRITE_REG);
 SPI.transfer(r); 
 digitalWrite(CS,HIGH); 
 checkError(error,wiper[0]);
}

void readWipers(){
 byte error;
 for(int i=0; i<4; i++){
  digitalWrite(CS,LOW);
  error = SPI.transfer(wiper[i] | READ_REG);
  wiperPos[i] = SPI.transfer(0x00); // read wiper values
  digitalWrite(CS,HIGH);
  checkError(error,wiper[i]);
 }
}

void checkError(byte e, byte r){
 if(e != 0xFF){
  Serial.print("\terror: on register "); Serial.println(r,HEX);
 }
}

STEP 4: Update the OpenBCI GUI

The OpenBCI_GUI_Helicopter_Throttle is a variant of OpenBCI GUI that uses the strength of alpha wave to control the helicopter throttle. So, if you have the original version of OpenBCI GUI you only have to add the helicopter class, replace the EEG Processing file with the new one, and add some lines of code to the OpenBCI GUI file.

processing.png
Figure 6. Screenshot of the OpenBCI GUI Helicopter Throttle.

This program looks for peaks in the alpha band, and it displays a real-time peak detector in the FFT plot (see Figure 9). When there is a dominant alpha wave present, the peak marker, a small circle, will darken (become bold). There is a short dashed line that references the background level the peak is measured against. Each channel has a peak detector and a background marker in its color. The most visceral way to understand how it works is to watch it. Use the Synthetic setting in the GUI, or to playback from an existing file of real EEG data.

“OpenBCI_GUI.pde” Tab

Remember to change the port name in the code above depending on the OS that you have on your computer. If you have Windows, you must write your port name as it follows “COM X“, whereas type “/dev/tty.usbmodem14131” if you own either a Mac or an OS X.

//define Helicopter
String helicopter_portName = "COM87"; //starts as N/A but is selected from control panel to match your OpenBCI USB Dongle's serial/COM
Serial helicopter_serial;
int helicopter_baud = 115200; //baud rate from the Arduino
Helicopter helicopter;
Every time the program window is drawn there is a function called processNewData(). When you processNewData(), you run a function called eegProcessing.process which runs the filters for the GUI [process() is in the EEG_Processing.pde tab].
 //apply additional processing for the time-domain montage plot (ie, filtering)
 eegProcessing.process(yLittleBuff_uV,dataBuffY_uV,dataBuffY_filtY_uV,fftBuff);
There is also an eegProcessing_user.process function call, which does the work to determine a peak frequency in the alpha band.
 //apply user processing
 // ...yLittleBuff_uV[Ichan] is the most recent raw data since the last call to this processing routine
 // ...dataBuffY_filtY_uV[Ichan] is the full set of filtered data as shown in the time-domain plot in the GUI
 // ...fftBuff[Ichan] is the FFT data structure holding the frequency spectrum as shown in the freq-domain plot in the GUI
 eegProcessing_user.process(yLittleBuff_uV,dataBuffY_uV,dataBuffY_filtY_uV,fftBuff);

“EEG_Processing.pde” Tab

The first step is to find the frequency with the highest value in a given channel’s FFT by sorting through the channel’s FFT bin values. The variable min_allowed_peak_freq_Hz [4.5] and max_allowed_peak_freq_Hz [15.0] create a window of frequencies to consider a peak candidate. In other words, the peak that is detected must be at least 4.5Hz and no more than 15Hz. That is a way to try and avoid noise. Frequencies outside the alpha band are not considered.

class EEG_Processing_User {
 private float fs_Hz; //sample rate
 private int nchan;

 // THE CRITICAL DETECTION PARAMETER!!!!
 final float detection_thresh_dB = 6.0f; 
 //how much bigger must the peak be relative to the background

 //add your own variables here
 final float min_allowed_peak_freq_Hz = 4.5f; //was 4.0f, input, for peak frequency detection
 final float max_allowed_peak_freq_Hz = 15.0f; //was 15.0f, input, for peak frequency detection
 final float[] processing_band_low_Hz = {
 4.0, 6.5, 9, 13.5
 }; //lower bound for each frequency band of interest (2D classifier only)
 final float[] processing_band_high_Hz = {
 6.5, 9, 12, 16.5
 }; //upper bound for each frequency band of interest
 DetectedPeak[] detectedPeak; //output per channel, from peak frequency detection
 DetectedPeak[] peakPerBand;
 Helicopter helicopter;
 boolean showDetectionOnGUI = true;
 public boolean useClassfier_2DTraining = false; //use the fancier classifier?

 //class constructor for user defined signal processing rules
 EEG_Processing_User() {
 } //empty
 EEG_Processing_User(int NCHAN, float sample_rate_Hz, Helicopter copter) {
 nchan = NCHAN;
 fs_Hz = sample_rate_Hz;
 helicopter = copter;
 detectedPeak = new DetectedPeak[nchan];
 for (int Ichan=0; Ichan<nchan; Ichan++) detectedPeak[Ichan]=new DetectedPeak();

 int nBands = processing_band_low_Hz.length;
 peakPerBand = new DetectedPeak[nBands];
 for (int Iband=0; Iband<nBands; Iband++) peakPerBand[Iband] = new DetectedPeak();
 }

 //here is the processing routine called by the OpenBCI main program...update this with whatever you'd like to do
 public void process(float[][] data_newest_uV, //holds raw EEG data that is new since the last call
 float[][] data_long_uV, //holds a longer piece of buffered EEG data, of same length as will be plotted on the screen
 float[][] data_forDisplay_uV, //this data has been filtered and is ready for plotting on the screen
 FFT[] fftData) {

 if (false) {
 // to target one channel, go here
 processSingleChannel(data_newest_uV, data_long_uV, data_forDisplay_uV, fftData);
 } else {
 // to target multiple channels, go here
 processMultiChannel(data_newest_uV, data_long_uV, data_forDisplay_uV, fftData);
 }
 } // end of process

The next step is to average the other FFT bin values around the peak bin to see if the peak ‘stands out’ against the background. The function called findPeakFreqeuncy in EEG_Processing.pde does all this work to find the largest peak in the signal. It also sets variables, like the level in dB of the peak and the level of dB of the background. It will measure the difference between the peak and the background, and test the difference against a threshold. The variable detection_thresh_dB [set to 6.0] is the threshold in dB above the background that the detectedPeak frequency needs to be to trigger an event. In this case, when the peak is 6.0dB bigger or more than the background, it will be considered a ‘detected peak’.

 void findPeakFrequency(FFT[] fftData, int Ichan) {

 float FFT_freq_Hz, FFT_value_uV;

 //clear the data structure that will hold the peak for this channel
 detectedPeak[Ichan].clear();

 //loop over each frequency bin in this channel to find the one with the strongest peak
 int nBins = fftData[Ichan].specSize();
 for (int Ibin=0; Ibin < nBins; Ibin++) {
 FFT_freq_Hz = fftData[Ichan].indexToFreq(Ibin); //here is the frequency of this bin of this channel

 //is this bin within the frequency band of interest?
 if ((FFT_freq_Hz >= min_allowed_peak_freq_Hz) && (FFT_freq_Hz <= max_allowed_peak_freq_Hz)) {
 //we are within the frequency band of interest

 //get the RMS voltage per bin (must div by nBins)
 FFT_value_uV = fftData[Ichan].getBand(Ibin) / ((float)nBins);
 //FFT_value_uV = fftData[Ichan].getBand(Ibin);

 //decide if this is bigger, compared to previous bins for this channel
 if (FFT_value_uV > detectedPeak[Ichan].rms_uV_perBin) {
 //this is bigger, so hold onto this value as the new "maximum"
 detectedPeak[Ichan].bin = Ibin;
 detectedPeak[Ichan].freq_Hz = FFT_freq_Hz;
 detectedPeak[Ichan].rms_uV_perBin = FFT_value_uV;
 }
 } //close if within frequency band
 } //close loop over bins to find peak

 // loop over the bins again (within the sense band) to get the average background power,
 // excluding the bins on either side of the peak
 float sum_pow=0.0;
 int count=0;
 for (int Ibin=0; Ibin < nBins; Ibin++) {
  FFT_freq_Hz = fftData[Ichan].indexToFreq(Ibin);
  if ((FFT_freq_Hz >= min_allowed_peak_freq_Hz) && (FFT_freq_Hz <= max_allowed_peak_freq_Hz)) {
   if ((Ibin < detectedPeak[Ichan].bin - 1) || (Ibin > detectedPeak[Ichan].bin + 1)) {
    FFT_value_uV = fftData[Ichan].getBand(Ibin) / ((float)nBins); //get the RMS per bin
    sum_pow+=pow(FFT_value_uV, 2.0f);
    count++;
    }
   }
  }
 //compute mean
 detectedPeak[Ichan].background_rms_uV_perBin = sqrt(sum_pow / count);

 //decide if peak is big enough to be detected
 detectedPeak[Ichan].SNR_dB
 = 20.0f*(float)java.lang.Math.log10(detectedPeak[Ichan].rms_uV_perBin
 / detectedPeak[Ichan].background_rms_uV_perBin);

 //kludge
 //if ((detectedPeak[Ichan].freq_Hz >= processing_band_low_Hz[0])
 // && (detectedPeak[Ichan].freq_Hz <= processing_band_high_Hz[0])) {
 // if (detectedPeak[Ichan].SNR_dB >= detection_thresh_dB-2.0) {
 // detectedPeak[Ichan].threshold_dB = detection_thresh_dB;
 // detectedPeak[Ichan].isDetected = true;
 // }
 //} else {
 // if (detectedPeak[Ichan].SNR_dB >= detection_thresh_dB) {
 // detectedPeak[Ichan].threshold_dB = detection_thresh_dB;
 // detectedPeak[Ichan].isDetected = true;
 // }
 //}

 //} // end loop over channels
 } //end method findPeakFrequency

The other thing I will add is that the method of measurement is specific. We measure the SNR, Signal to Noise Ratio. This is a measurement that is in RMS Voltage per bin. In this case, the FFT value of each bin (frequency) is delivered in RMS, but then it needs to be divided by the number of bins to get the RMS per bin [RMS/bin] this is a weird number, and very scientific. There is squaring and square-rooting going on all over the place, and the calculations can be found in the findPeakFrequency function in the EEG_Processing.pde tab if you want to dig into it.

OpenBCI-2016-08-04_15-20-17.jpg
Figure 7. Screenshot of the OpenBCI_GUI_Alpha_Power_Battle. If there is a peak detected in the alpha range, a wide circle will appear in the FFT, such as it is shown.

The function processMultiChannel() maps the values to the Helicopter. If there is a peak detection in the alpha range, and if it’s strong enough against the background noise, then a character is sent out to the Arduino that controls the helicopter. The strength of the alpha wave is encoded in the characters ‘A’ to ‘Z’.

public void processMultiChannel(float[][] data_newest_uV, //holds raw EEG data that is new since the last call
 // create variables
 float[][] data_long_uV, //holds a longer piece of buffered EEG data, of same length as will be plotted on the screen
 float[][] data_forDisplay_uV, //this data has been filtered and is ready for plotting on the screen
 FFT[] fftData) { //holds the FFT (frequency spectrum) of the latest data
 boolean isDetected = false;
 int lastPeak = 0;
 int peakChan = 0;
 String mappedValue = "";

 // passes a mappedValue to the Helicopter. mapping is done here
 for(int i=0; i<nchan; i++){
  findPeakFrequency(fftData, i); // look for the peak freq on this channel
  // first check that any detectedPeaks are in the desired freqeuency range
  if ((detectedPeak[i].freq_Hz >= processing_band_low_Hz[3-1]) && // <= 9Hz line 48
   (detectedPeak[i].freq_Hz < processing_band_high_Hz[3-1])) { // < 12Hz line 50
   // then measure the peak against the background
   if (detectedPeak[i].SNR_dB >= detection_thresh_dB) {// if it is pronounced
    detectedPeak[i].threshold_dB = detection_thresh_dB;
    // on the fly threshold adjust (?)
    println("detectedPeak channel " + (i+1) + " = " + detectedPeak[i].SNR_dB); 
    // verbose debug
    int peak = int(detectedPeak[i].SNR_dB) + 'A'; // map SNR.dB to ASCII Helicopter command
   if(peak > lastPeak) { lastPeak = peak; peakChan = i+1; 
   }
  detectedPeak[i].isDetected = true;// flag that we got a winner
  isDetected = true; // uncomment for verbose output
   if (isDetected) {
   //print some output
   println("EEG_Processing_User: alpha!, Chan " + (i+1) + ", peak = " + detectedPeak[i].rms_uV_perBin + " uV at "
   + detectedPeak[i].freq_Hz + " Hz with background at = " + detectedPeak[i].background_rms_uV_perBin
   + ", SNR (dB) = " + detectedPeak[i].SNR_dB + " " + mappedValue);
   // isDetected = false;
   }
  }
 }
 }
 if(isDetected){
  mappedValue += char(lastPeak);
  println("Detected Peak on chan " + peakChan + " Mapped Value: " + mappedValue);
  helicopter.throttle(mappedValue, peakChan); // send the ASCII command to the hacked helicopter
  }
} // end of processMultiChannel

“Helicopter.pde” Tab

The Helicopter class has an inner class called Command that controls the helicopters commands and receives the mapped values from EEG_Processing. Then, the same class sends the prefix ‘$’ followed by the character code from EEG_Processing. Arduino gets the prefix, and then apply the next value it gets to the potentiometer.

class Helicopter { 
 // create an inner class command
 class Command {
 private String command_str;
 private String variable_str;
 private String name;
 private int counter;
 private Serial serial_h;
 public boolean printReceivedCommand = true;
 // public int ID;

 Command(Serial _serial_h, String _str, String _name, int _ID) { // String _var,
 serial_h = _serial_h;
 command_str = _str;
 name =_name;
 counter = 0;
 }

 public void issue() {
 counter++;
  if (printReceivedCommand) {
   println("Helicopter: Command: " + name + " (" + counter + ")");
  }
  // println("A"); // verbose debug
  if (serial_h != null) serial_h.write(command_str);
  // println("B"); // verbose debug
  }
 } //close definition of class Command

 private Command command_throttle;//, // Helicopter command
 private String mappedValue; // variable to control helicopter throttle
 private int counter = 0;

 //Constructor, pass in an already-opened serial port
 Helicopter(Serial serialPort) { 
 // empty
 }

 public void throttle(String V, int chan) {
 counter++;
  if (chan < 5) { //Player 1 (Channels 1-4)
   println("Helicopter 1 - Command: Throttle $" + V + " (" + counter + ")");
  helicopter_serial.write("$" + V); // shift the incoming value to ASCII
  while (helicopter_serial.available () > 0) { // hang and wait
   print(char(helicopter_serial.read())); // for the handshake
  }
 }
  if (chan > 4) { // Player 2 (Channels 5-8)
   println("Helicopter 2 - Command: Throttle #" + V + " (" + counter + ")");
   helicopter_serial.write("#" + V); // shift the incoming value to ASCII
   while (helicopter_serial.available () > 0) { // hang and wait
    print(char(helicopter_serial.read())); // for the handshake
   }
  }
 }
}

Let’s hack it!

We’ve got both the software and the hardware ready to start the alpha power battle! Before the players start the battle, you must two last things.

ONE: Turn on the hardware

We better turn on the helicopters and the remote controllers. Make sure to add 6 batteries of 1.5V in each remote controller, and turn on the

hardware.png
Figure 8. Hardware set up. Each remote controller is in charge of one helicopter, and its new digital potentiometer is connected to the Arduino board.

 

TWO: Turn off the BIAS and the SRB2 from the OpenBCI GUI

Since we are using an unusual configuration of the OpenBCI 32bit board and we connected the wires in a particular way, we need to change the preset configuration of the OpenBCI GUI in order to visualize the EEG data from the two players.

heei
Figure 9. Screenshot of Channel settings of the OpenBCI GUI.

 

THREE: Check the OpenBCI GUI console

I would suggest you to have a look at the OpenBCI GUI console and make sure that there is a channel communication between Processing and Arduino. As you can see in the image below, there is a command that is shown every time that the Processing detects a peak.

 

mesasges.png
Figure 10. Screenshot of the console of Processing.

 

FOUR: Let’s the battle start!

It´s been a long way, but now your are ready to start the alpha power battle. The only thing that´s left is to find an opponent. Have fun and enjoy!

IMG_6202
Figure 11. Joel, Irene, and Niels performing the first alpha power battle at OpenBCI HQ.

Written by Joel Murphy (co-founder and President of OpenBCI) and Irene Vigue Guix (Biomedical Engineer and neuroscientist resident at OpenBCI).

 

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s