How to make arduino receive eeg data and control rc car - so close I think

I am attempting to use an Arduino to connect to a Mindflex headband that I attached the T pin to the RX pin on an arduino and then grounded them. I then have opened an RC car controller and soldered leads to the test points that correlate to the forward and backward buttons on the RC car controller. I am trying to get my code to read the attention or meditation values and based on the if statement move the RC car. I have two questions can you take a look at my code and see where I am going wrong these are the two things that I was wondering.

  1. How am I able to reference a specific value of the *char getCSV() just to get the attention or meditation values in the serial output? I want to write if-statements based upon these values. Is there a way to name the specific value in the array or reference it via a number in an arduino sketch to allow me to do if-statements that will tell the arduino to power the forward motor. Can you help?

  2. So I was wondering is one single arduino capable of receiving a signal from the EEG (RX pin and GRD) and then on the digital side Pin 2 - Forward, Pin 4 - Backward, 3.3v - To the power TP on the RC controller, and GRD to the Ground. So do I need to use multiple arduinos, or processing or anything or can I use one arduino and my sketch to make it work?

Below is my code I have tried to write I thought it would finally work but no luck, any help you all can provide?

#include <Brain.h>

#define FORWARD 2
#define BACKWARD 4

int MindFlex = 0;

Brain brain(Serial);

void setup() {
   Serial.begin(9600);
   pinMode(FORWARD, OUTPUT);
   pinMode(BACKWARD, OUTPUT);
   pinMode(MindFlex, INPUT);
}


void loop() {

 int attValue = brain.readAttention();

   if (brain.update()) {
     Serial.println(brain.readCSV());
      delay(2);
 }
   if (brain.readSignalQuality() == 0) {
      Serial.println("Good Connection");
      delay(5);
 }
   if (attValue < 50) {
       digitalWrite(FORWARD, LOW);
       delay(5);
 }
   else if (attValue > 50) {
       digitalWrite(BACKWARD, LOW);
       delay(5);
 }
}

If you could help me out I would really appreciate it, I have been working on this for over a month scouring the internet for resources and trying different methods and I think I am finally close but just struggling to finish it up. Thank you!

  1. How am I able to reference a specific value of the *char getCSV() just to get the attention or meditation values in the serial output?

I don't see any kind of function called getCSV() or any method called getCSV(), so it's hard to understand what you are asking.

The function would return a char *, a pointer to a string, if your question is correct. You can use strcmp() to compare that string to known strings.

Is there a way to name the specific value in the array or reference it via a number in an arduino sketch to allow me to do if-statements that will tell the arduino to power the forward motor.

I don't see the array that you are referring to, so no answers are possible.

  1. So I was wondering is one single arduino capable of receiving a signal from the EEG (RX pin and GRD) and then on the digital side Pin 2 - Forward, Pin 4 - Backward, 3.3v - To the power TP on the RC controller, and GRD to the Ground.

Yes.

So do I need to use multiple arduinos, or processing or anything or can I use one arduino and my sketch to make it work?

You need one Arduino, no PC or PC applications. You can make the sketch work.

Seeing the serial output would be the first step towards helping you.

This is an example of the serial output.

Also the getCSV(), I'm sorry I meant(brain.readCSV())

24001,5232,28374,14588,8769,6155,2686

0,40,94,2158540,612203,129669,58072,53379,24860,17169,8943

0,35,83,539645,34005,8947,14976,7326,5302,1689,1147

0,30,66,173791,145539,14610,7782,25125,23536,21001,18331

0,30,43,520130,263998,17963,8589,92736,40831,21627,10849

0,44,44,735608,39219,6620,12921,13447,6531,3570,5797

0,35,38,29141,77058,20896,2770,4403,3690,1782,946

0,20,44,1116069,114167,23296,7537,14569,4666,2677,1915

0,20,50,16987,31509,2457,2867,13629,4929,1050,419

0,4,37,2291927,842580,37290,34925,81330,24469,11039,9710

0,3,23,647256,613229,12266,36502,57075,21455,18017,18053

0,7,20,940493,470588,49443,54791,60543,25691,21935,12380

0,1,35,332324,78103,29726,16857,17505,4001,4255,1080

0,1,54,43427,19858,4641,9234,3382,1190,2175,696

0,1,75,111507,52244,26973,10449,6571,5501,1548,1174

0,8,87,1754380,127393,61628,38826,27425,22277,19257,7166

Is there a way to reference a specific one of these numbers to run the example if statement, ideally the second or third numbers in this array.

This is an example of the serial output.

I can't tell where the stream of data starts and ends.

Serial.print("Brain data as CSV file: [");
Serial.print(brain.readCSV());
Serial.println("]");

would make it clear.

Try this, too:

   char *stuff = brain.readCSV();
   Serial.print("stuff: [");
   Serial.print(stuff);
   Serial.println("]");

If that works, then you can parse the data as needed, and we can help with that.

It looks like brain.readCSV() returns a comma separated list of values. If so you can separate the values using the strtok() function but you will need to assign the return value to a variable first.

What does the code in your Original Post actually do and what should it do?

Is the variable attValue getting the values you want it to have? Print the value so you can see what is happening.

I get the impression you are trying to make the Arduino operate the R/C transmitter. If not, please explain what you do want it to do.

If so, make a pencil drawing showing how you have connected everything and post a photo of the drawing. Also (and not instead of) post a photo of the transmitter with the connections on it.

The parse example in Serial Input Basics illustrates the use of strtok()

...R

Robin2 Thank you for your help and thorough questions I have spent so so so so much time on this, I cant tell you how much I appreciate it, that being said here are the answers to everything you asked in order to hopefully give you all the info you need, also not an artist but really tried to draw a good sketch and take good pics.

  1. Right now my actual code doesnt do much but output what I posted as the serial output. It doesnt move the RC car at all right now.

  2. Know the attValue is not getting the values that I need. Should I try printing it using Serial.Println(attValue)? Trying to really get an understanding for this.

  3. You are right I am trying to write an if statement to control the RC car using the RC car transmitter. I am attempting to make the if statement dependent upon the values printed in the serial output. I really just need to use the first 3,(signal quality, meditation, and attention) as I only need it to go forward and backward.

  4. Bad drawer and tried to get good pics.. they are attached.

I have looked at the Serial Input Basics really scoured this site and online as well as forums to try and do this. I am really having a hard time with it though.

Thanks so much for all y'alls help.

cornwellington:
. they are attached.

Not yet :slight_smile:

...R

Oops sorry about that was trying to be super thorough and forgot haha, they are now attached.

@cornwellington, first up, you don't need to use the readCSV() method. Take a closer look at the 'Brain' library. In particular, the 'uint8_t readAttention()' and 'uint8_t readMeditation()' methods.

Just for the information of others, in the serial CSV output shown above, only the first three values are really relevant:- "0,40,94"
The '0' is the "Poor_Signal" value, (inverse of signal strength), with '0' being full-strength and '200' being "No signal - headset not on".
The '40' is the "Attention" value, ranging from 0-100, and the '94' is the "Meditation" value, also ranging from 0-100.

So I was wondering is one single arduino capable of receiving a signal from the EEG (RX pin and GRD) and then on the digital side Pin 2 - Forward, Pin 4 - Backward, 3.3v - To the power TP on the RC controller, and GRD to the Ground. So do I need to use multiple arduinos, or processing or anything or can I use one arduino and my sketch to make it work?

You just need one Arduino. Get the "Attention" and "Meditation" values, then have your code decide what to do.
Since you're obviously a beginner with Arduino, forget building a whole car for now, and concentrate on getting those values, powering a single motor on it's own for forward/reverse, then if you manage to get good control, go from there.
You'll probably have enough trouble just controlling those values with your mind, if my experience is anything to go by.
I'm doing a similar thing, but haven't touched my project for a few weeks. I'm just setting up now to get back into it.

OldSteve-

Thank you for your input, I agree with not needing the use readCSV(). I have been trying to get just the first 3 values to display in the serial output. I have looked at the Brain library documentation but this is just still a little over my head when I look at the code it makes sense I just dont know how to utilize the uint8_t readAttention() and uint8_t readMeditation() methods. Then when I can get just those 3 values printed to the serial monitor I want to assign them variables so they can be utilized in an IF statement. Would you mind elaborating a bit on your answer and possibly providing and example/or correction of my code to help this make sense to me?

Note- Ya I am a beginner been working on this for about 2 months, but I am not building the RC car I just bought one from Walmart and soldered leads to the test points that correspond to FORWARD and BACKWARD. Ya I thought about building the whole car myself but its a bit too advanced for me now.

Thank you for taking the time to answer my friend! :slight_smile:

cornwellington:
OldSteve-

Thank you for your input, I agree with not needing the use readCSV(). I have been trying to get just the first 3 values to display in the serial output. I have looked at the Brain library documentation but this is just still a little over my head when I look at the code it makes sense I just dont know how to utilize the uint8_t readAttention() and uint8_t readMeditation() methods. Then when I can get just those 3 values printed to the serial monitor I want to assign them variables so they can be utilized in an IF statement. Would you mind elaborating a bit on your answer and possibly providing and example/or correction of my code to help this make sense to me?

Note- Ya I am a beginner been working on this for about 2 months, but I am not building the RC car I just bought one from Walmart and soldered leads to the test points that correspond to FORWARD and BACKWARD. Ya I thought about building the whole car myself but its a bit too advanced for me now.

Thank you for taking the time to answer my friend! :slight_smile:

First up, you need to post your code between code tags, not inline. I'm so weary of looking at code posted inline that I don't even look at it any more. You'll find that many others share the same opinion. I only read the text of your questions.
This is all outlined in:- How to use this forum
Code tags look like this </> in the 'Post' window. (It's not too late to edit and do that. :slight_smile: )

So I just had a quick look at your code in lieu of you posting it correctly.
I now see that you already use 'readAttention()'.
At this point, before anything else, show us an accurate diagram of your connections. (And fix that initial post please.)

Edit: OK, so I see that you edited and added diagrams of a sort after I first looked at your post #6.
Have you written basic working code to run the car forwards and backwards without the 'Brain' stuff?
(I'm wondering if the "TP" connections are correct, and if they're really 'active-low'.)

And instead of "int attValue", you only need an 8-bit variable, so it's better to use "byte attValue".
(I don't know why your original post was all about 'readCSV()' since you already knew of 'readAttention()', and why you said "How am I able to reference a specific value of the *char getCSV() just to get the attention or meditation values in the serial output?", either.)

If connections are correct and you have previously tested those connections with a basic program to run the car forwards and backwards without the 'Brain' stuff, then your code should work fine.
You could rip out all of those delays, though. The updates only happen once per second anyway. ("BrainSerialTest" didn't need delays after everything.)

Edit2: I just spotted one thing - you should wrap everything in brackets, dependent on the brain.update().
This is how I'd write it:-

#include <Brain.h>

#define FORWARD 2
#define BACKWARD 4

const byte MindFlex = 0;

Brain brain(Serial);

void setup()
{
    Serial.begin(9600);
    pinMode(FORWARD, OUTPUT);
    pinMode(BACKWARD, OUTPUT);
    pinMode(MindFlex, INPUT);
}

void loop()
{
    if (brain.update())
    {
        Serial.println(brain.readCSV());

        if (brain.readSignalQuality() == 0)
            Serial.println("Good Connection");

        byte attValue = brain.readAttention();
        if (attValue < 50)
            digitalWrite(FORWARD, LOW);
        else if (attValue > 50)
            digitalWrite(BACKWARD, LOW);
    }
}

@cornwellington, responding to your PM ... it seems to me some of the others here know a lot more about your Brain library than I do.

...R

Great news!!!

So now I got it to moved based on the brain data provided by the eeg.

I am just new to C++ and think I am having some basic syntax issues that may be solved easily.

So what I want the code to do is...

Start up then check the signal quality of the headset.

Then perform actions based on the attention value...

-First: if the attValue == 0, then move the wheels back and forth to signify no connection
-Second: if attValue > 50, then move the RC car forward
-Third: if the attValue < 50, then move the RC car backward

I only want it to perform whatever action based on the attValue for maybe 1 to 2 seconds then recheck the attValue and perform an action based upon value.

I was advised to check out the BlinkWithoutDelay example to see how to recheck the code so I tried to implement that. In a separate sketch I was about to get the wheels to either jostle back and forth, go forward, or straight. I am just not 100% sure how to go about integrating that to work with serial data being received through the monitor. I have attached my code below.

A bit of extra info on the Brain library. The signal value will be between 0 and 200. 0 means a perfect signal quality while 200 means very poor eeg signal quality, well even none at all.

#include <Brain.h>
#define FORWARD 2
#define BACKWARD 4
#define RIGHT 7
#define LEFT 8

const byte MindFlex = 0;

unsigned long previousMillis = 0;
const long interval = 8000;
//This first action sent will be HIGH
int moveCar = LOW;
//This first action sent will be LOW
int moveIt = HIGH;
Brain brain(Serial);

void setup() {
    Serial.begin(9600);
    pinMode(FORWARD, OUTPUT);
    pinMode(BACKWARD, OUTPUT);
    pinMode(RIGHT, OUTPUT);
    pinMode(LEFT, OUTPUT);
    pinMode(MindFlex, INPUT);
    byte readSignalQuality();

 //Not getting much from this first part none if it is printing in the serial monitor
 //Look at modifying the if statements to else, else if possibly

  if (brain.update()) {
    Serial.println("You have ran 'brain.update' so can begin receiving data.");
    delay(80000);
  }
 
  if (brain.readSignalQuality() == 0) { 
      Serial.println("Good Connection");
      delay(2000);
     
     
  if (brain.readSignalQuality() > 0) 
      Serial.println("Need to adjust headset, you have a poor connection.");
      delay(2000);
  }

}


void loop() {
unsigned long currentMillis = millis();  
byte attValue = brain.readAttention(); 
byte sigValue = brain.readSignalQuality();
String yourAtt = "Your attention level is: ";
String yourSignal = "Your signal quality is: ";
String Signal = yourSignal + sigValue;
String Attention = yourAtt + attValue;
String moveForward = "Your Attention is > 50, and GO!";
String moveBackward = "Your Attention is < 50, back, back, BACK IT UP!";
String noAttention = "Error: Unable to receive Attention signal.";

 // brain.update(); 

//This while loop will execute continuously until the connection drops
  while (sigValue < 200) {
      Serial.println(Signal);
      Serial.println(Attention);
      delay(1000);
      //Serial.println(brain.readCSV());
  }

  if (currentMillis - previousMillis >= interval) {
       previousMillis = currentMillis;

          if (moveCar == LOW)
            moveCar = HIGH;
          else
            moveCar = LOW;

          if (moveIt == HIGH)
            moveIt = LOW;
          else
            moveIt = HIGH;
       
  if (attValue == 0) 
      Serial.println(noAttention);
      //Need to solder wires to left n right to have that happen if no connection
      digitalWrite(RIGHT, moveCar);
      digitalWrite(LEFT, moveIt);
      
  if (attValue < 50)
       Serial.println(moveForward);
       digitalWrite(FORWARD, LOW);
       digitalWrite(BACKWARD, HIGH);    
     
  if (attValue > 50) 
        Serial.println(moveBackward);
        digitalWrite(BACKWARD, LOW); 
        digitalWrite(FORWARD, HIGH);
    }
    
  }

You need brain.update() inside the loop. You've commented it out, and it's only in setup() now. It gets the fresh packets of data from the headset:-

boolean Brain::update() {
 latestError[0]='\0';          // <- *****My mod to clear error string at start of each update. (SC)
    if (brainStream->available()) {
        latestByte = brainStream->read();

        // Build a packet if we know we're and not just listening for sync bytes.
        if (inPacket) {
        
            // First byte after the sync bytes is the length of the upcoming packet.
            if (packetIndex == 0) {
                packetLength = latestByte;

                // Catch error if packet is too long
                if (packetLength > MAX_PACKET_LENGTH) {
                    // Packet exceeded max length
                    // Send an error
                    sprintf(latestError, "ERROR: Packet too long %i", packetLength);
                    inPacket = false;
                }
            }
            else if (packetIndex <= packetLength) {
                // Run of the mill data bytes.
                
                // Print them here

                // Store the byte in an array for parsing later.
                packetData[packetIndex - 1] = latestByte;

                // Keep building the checksum.
                checksumAccumulator += latestByte;
            }
            else if (packetIndex > packetLength) {
                // We're at the end of the data payload.
                
                // Check the checksum.
                checksum = latestByte;
                checksumAccumulator = 255 - checksumAccumulator;

                // Do they match?
                if (checksum == checksumAccumulator) {
                    boolean parseSuccess = parsePacket();
                    
                    if (parseSuccess) {
                        freshPacket = true;
                    }
                    else {
                        // Parsing failed, send an error.
                        sprintf(latestError, "ERROR: Could not parse");
                        // good place to print the packet if debugging
                    }
                }
                else {
                    // Checksum mismatch, send an error.
                    sprintf(latestError, "ERROR: Checksum");
                    // good place to print the packet if debugging
                }
                // End of packet
                
                // Reset, prep for next packet
                inPacket = false;
            }
            
            packetIndex++;
        }
        
        // Look for the start of the packet
        if ((latestByte == 170) && (lastByte == 170) && !inPacket) {
            // Start of packet
            inPacket = true;
            packetIndex = 0;
            checksumAccumulator = 0;
        }
        
        // Keep track of the last byte so we can find the sync byte pairs.
        lastByte = latestByte;
    }
    
    if (freshPacket) {
        freshPacket = false;
        return true;
    }
    else {
        return false;
    }
    
}

'parsePacket()' is called from within brain.update(), which loads the current values into the 'attention' and 'signal quality' variables that you're reading:-

boolean Brain::parsePacket() {
    // Loop through the packet, extracting data.
    // Based on mindset_communications_protocol.pdf from the Neurosky Mindset SDK.
    // Returns true if passing succeeds
    hasPower = false;
    boolean parseSuccess = true;
 int rawValue = 0;
 
    clearEegPower();    // clear the eeg power to make sure we're honest about missing values
    
    for (uint8_t i = 0; i < packetLength; i++) {
        switch (packetData[i]) {
            case 0x2:
                signalQuality = packetData[++i];
                break;
            case 0x4:
                attention = packetData[++i];
                break;
            case 0x5:
                meditation = packetData[++i];
                break;
            case 0x83:
                // ASIC_EEG_POWER: eight big-endian 3-uint8_t unsigned integer values representing delta, theta, low-alpha high-alpha, low-beta, high-beta, low-gamma, and mid-gamma EEG band power values           
                // The next uint8_t sets the length, usually 24 (Eight 24-bit numbers... big endian?)
                // We dont' use this value so let's skip it and just increment i
                i++;

                // Extract the values
                for (int j = 0; j < EEG_POWER_BANDS; j++) {
                    eegPower[j] = ((uint32_t)packetData[++i] << 16) | ((uint32_t)packetData[++i] << 8) | (uint32_t)packetData[++i];
                }

                hasPower = true;
                // This seems to happen once during start-up on the force trainer. Strange. Wise to wait a couple of packets before
                // you start reading.
                break;
            case 0x80:
                // We dont' use this value so let's skip it and just increment i
                // uint8_t packetLength = packetData[++i];
                i++;
                rawValue = ((int)packetData[++i] << 8) | packetData[++i];
                break;
            default:
                // Broken packet ?
                /*
                Serial.print(F("parsePacket UNMATCHED data 0x"));
                Serial.print(packetData[i], HEX);
                Serial.print(F(" in position "));
                Serial.print(i, DEC);
                printPacket();
                */
                parseSuccess = false;
                break;
        }
    }
    return parseSuccess;
}

Edit: As I suggested yesterday, you should really use 'brain.update()' inside 'loop()', with an 'if()' statement:-

if(brain.update())
{
    // Get your signal strength and attention here.
}

And a bit more info - the updates are only valid if signal strength is <26. If it's 26 or higher, the attention and meditation values repeat, and the same values are sent over and over. The Mindflex game cheats, to make it look like things are working when they're not.
And there are no signal strength values between 0 and 26. It either works at 0, or repeats.

Edit2:
Just looking further at your code, this also stops it from controlling the motors. It'll just loop in the 'while' loop, sending the same values to the serial monitor over and over:-

while (sigValue < 200) {
      Serial.println(Signal);
      Serial.println(Attention);
      delay(1000);
      //Serial.println(brain.readCSV());
  }

-First: if the attValue == 0, then move the wheels back and forth to signify no connection
-Second: if attValue > 50, then move the RC car forward
-Third: if the attValue < 50, then move the RC car backward

Isn't there a problem with the first and third requirements both being satisfied by a value of zero ?

I just took the time to load your code into my IDE and hit "Auto Format" for a better look.
When formatted properly, you can see how little sense this makes:-

if (brain.readSignalQuality() == 0)
{
    Serial.println("Good Connection");
    delay(2000);

    if (brain.readSignalQuality() > 0)
        Serial.println("Need to adjust headset, you have a poor connection.");
        delay(2000);
}

Forgot some brackets maybe?

And more forgotten brackets, (as well as the oversight that Bob pointed out in the last reply)? :-

if (attValue == 0)
    Serial.println(noAttention);
//Need to solder wires to left n right to have that happen if no connection
digitalWrite(RIGHT, moveCar);
digitalWrite(LEFT, moveIt);

if (attValue < 50)
    Serial.println(moveForward);
digitalWrite(FORWARD, LOW);
digitalWrite(BACKWARD, HIGH);

if (attValue > 50)
    Serial.println(moveBackward);
digitalWrite(BACKWARD, LOW);
digitalWrite(FORWARD, HIGH);

When writing your code, if you click on >Tools >Auto Format occasionally, it'll format your code correctly and errors like these will stand out.

Next point, in the following snippet, the last two lines do nothing and can be omitted:-

if (moveCar == LOW)
    moveCar = HIGH;
else
    moveCar = LOW;

If 'moveCar' is already "LOW', you don't need to make it low again.

And the same applies here, with 'moveIt':-

if (moveIt == HIGH)
    moveIt = LOW;
else
    moveIt = HIGH;

In summary, you should spend a little more time planning your code before writing it, and maybe some time practising with simpler stuff before taking on slightly more complex projects like this one.
A bit more time spent studying the 'Brain' library and some research on what values to expect wouldn't go astray, either.
Before diving in, you should have spent more time sitting down studying the output on the serial monitor, and trying to get your brain to actually control the values. If it's possible at all, (which I seriously doubt), a lot of 'brain training' will be necessary. After many hours analysing the data from two Mindflex headsets, I have seen absolutely no genuine correlation between the "Attention" and "Meditation" values, and the state of my brain waves. Perhaps it's just me, but I tend to doubt it.