Noob uses EEG brain waves to trigger keystrokes

Hello, I am pretty new to this, so I might have something simple wrong with my sketch and I would appreciate any help I can get.

I have a Leonardo reading my brain meditation values on a scale of 0 to 100 with an EEG chip, and I’m wanting values greater than 95 to trigger a “V” keypress and values under 25 to trigger “W” and hold it for two seconds.

Here is what I have so far

#include <Keyboard.h>
#include <Brain.h>
Brain brain(Serial1);
long interval = 500; 
long previousMillis = 0; 
int medValue;
uint8_t buf[8] = { 0 };
void setup() {
Serial.begin(9600);
Serial1.begin(9600);
randomSeed(analogRead(0));
delay(2000);
}
void loop() {
if (brain.update()) {
Serial.println(brain.readCSV());
medValue = brain.readMeditation();
}
if(brain.readSignalQuality() == 0) {
if (medValue > 25) {
   Keyboard.print('w');  // send a 'w' to the computer via Keyboard HID
   delay(1000);  // delay so there aren't a kajillion w's
if (medValue < 100) {
   Keyboard.print('v');  // send a 'w' to the computer via Keyboard HID
   delay(1000);  // delay so there aren't a kajillion w's
}
}
}
}

It reads my meditation values just fine and I can see the values in serial monitor no problem, but once the meditation value goes under 25 or over 95 and triggers a keystroke, the serial monitor seems to freeze (or simply stop reading the brain wave values) and the keystroke just repeats over and over and over with the 1000ms delay.

Can anybody help? I feel like for someone experienced, this is probably a pretty simple fix. I don’t know, but any help is much appreciated!!

Here is a sketch a guy used that is very similar to what I’m trying to do, except he used the meditation value to send an IR code.

#include <IRremote.h>
#include <IRremoteInt.h>
#include <Brain.h>
IRsend irsend;
Brain brain(Serial);
const int ledPin = 3; 
long interval = 500; 
long previousMillis = 0;
int ledState = LOW; 
int medValue;
void setup() {
 // Set up the LED pin.
 pinMode(ledPin, OUTPUT);
 
 // Start the hardware serial.
 Serial.begin(9600);
}
void loop() {
 // Expect packets about once per second.
 if (brain.update()) {
 Serial.println(brain.readCSV());
 
 // Attention runs from 0 to 100.
 medValue = brain.readMeditation();
 }
 
 // Make sure we have a signal.
 if(brain.readSignalQuality() == 0) {
 
 // Send a signal to the LED.
 if (medValue < 50) {
irsend.sendNEC(0x10EFA05F, 32); 
 delay(40);
 }
 } 
 
}

What I need is very similar, just keystrokes instead of an IRsend. It seems to me like the keyboard.write part of the sketch is what might be messing with me.

Like once it send the keystroke it is no longer sending serial data because it’s now an HID keyboard? Unsure

I do not know anything about the EEG hardware but I would translate the requirement like this.

/* I'm wanting values greater than 95 to trigger a "V" keypress */
 if (medValue > 95) {
   Keyboard.print('V');
   delay(1000);
 }
/* and values under 25 to trigger "W" */
 else if (medValue < 25) {
   Keyboard.print('W');
   delay(1000);
 }

Thanks for that.

So when I do this, it reacts the same as before... once the keypress is triggered, the serial monitor stops showing any data, and the keypress is then pressed every 1000ms.

What I need is for the Serial Monitor data to keep running, and the keypress to only be pressed if the medValue data is within the ranges specified.

It's like once the Leonardo sends a keypress, the medValue data freezes and stops updating (which to my understanding shouldn't happen, since the loop calls for the data to be read?) which causes the keypress to continue, since the value is frozen within the range saying "send a keypress" here

if (medValue > 95) { Keyboard.print('V'); delay(1000);

When you hit the delay(1000) your sketch will be frozen for a full second.

I'm wanting values greater than 95 to trigger a "V" keypress and values under 25 to trigger "W" and hold it for two seconds.

Does the "hold it for two seconds" apply to only the "W" or to both the "W" and "V" keystrokes? By "hold it" do you mean "hold the key down" or "press and release the key and do nothing else for two seconds"?

Welcome to the forum and ++Karma; for posting code correctly on your first posts.

I debated replying or not as I am struggling to find a way to explain what you need without actually writing your code for you, which would deprive you of an opportunity to experiment for yourself. I will try to describe in words what you should do.

Get rid of delay();, it stops everything.

To achieve not sending millions of Vs and Ws when you pass your threshold you need a flag that indicates one has been sent, for example:

bool W_sent = false;
bool V_sent = false;

Then, when you cross the threshold for a W (or V) you only send the W if the flag is false. After you send it you set the flag to true. When you drop below the threshold for a W you set the flag to false again. That way you only send 1 W each time you cross the threshold for a W.

johnwasser:
When you hit the delay(1000) your sketch will be frozen for a full second.
Does the “hold it for two seconds” apply to only the “W” or to both the “W” and “V” keystrokes?
By “hold it” do you mean “hold the key down” or “press and release the key and do nothing else for two seconds”?

I would like the “hold it for two seconds” to only apply to the “W”, but I should have specified that I have not yet attempted to implement that into the sketch. Sorry for that… Right now I’m just wanting it to work properly to send the keystrokes & then I will try the hold it down part.

By “hold it,” I meant hold the key down for two seconds.

Thanks for your reply :slight_smile:

PerryBebbington:
Welcome to the forum and ++Karma; for posting code correctly on your first posts.

I debated replying or not as I am struggling to find a way to explain what you need without actually writing your code for you, which would deprive you of an opportunity to experiment for yourself. I will try to describe in words what you should do.

Get rid of delay();, it stops everything.

To achieve not sending millions of Vs and Ws when you pass your threshold you need a flag that indicates one has been sent, for example:

bool W_sent = false;

bool V_sent = false;




Then, when you cross the threshold for a W (or V) you only send the W if the flag is false. After you send it you set the flag to true. When you drop below the threshold for a W you set the flag to false again. That way you only send 1 W each time you cross the threshold for a W.

Ok, so a delay stops everything, meaning that is what is stopping the readings into my serial monitor? That explains a lot if I am understanding properly.

My first thought is to do something like this to just begin the serial reading again

#include <Keyboard.h>
#include <Brain.h>
Brain brain(Serial1);
long interval = 500;
long previousMillis = 0;
int medValue;
void setup() {
Serial.begin(9600);
Keyboard.begin();
Serial1.begin(9600);
delay(5000);
}
void loop() {
if (brain.update()) {
Serial.println(brain.readCSV());
medValue = brain.readMeditation();
}
if(brain.readSignalQuality() == 0) {
if (medValue < 25) {
   Keyboard.print('w');  // send a 'w' to the computer via Keyboard HID
   delay(1000);  // delay so there aren't a kajillion w's
  {
}Serial1.begin(9600);
}
}
}

But my gut says this is wrong and redundant.

I don’t fully understand bool… def -“A bool holds one of two values, true or false”
So am I placing this in the correct location in the sketch?

#include <Keyboard.h>
#include <Brain.h>
Brain brain(Serial1);
long interval = 500;
long previousMillis = 0;
int medValue;
void setup() {
Serial.begin(9600);
Keyboard.begin();
Serial1.begin(9600);
delay(5000);
}
void loop() {
if (brain.update()) {
Serial.println(brain.readCSV());
medValue = brain.readMeditation();
}
if(brain.readSignalQuality() == 0) {
if (medValue < 25) {
   Keyboard.print('w');  // send a 'w' to the computer via Keyboard HID
   bool W_sent = false;
  {
}
}
}
}

btw, both of these sketches reduced to just a “w” trying to get the sketch to function properly with one key, then I will make it work with two.

The last thing I thought of, would something like this work? Does the delay ruin it?

Keyboard.press('w');
  delay(2000);
  Keyboard.releaseAll();

Thank you for your reply and help… I’ve been learning for just a few days now, so I’m sure a lot of my thoughts/suggestions are way off.

edit: thanks for karma heh

Here is roughly how I would change loop():

void loop()
{
  static int prevMedValue = 0;


  if (!brain.update())
    return;  // There is no new data so there is nothing more to do.


  // Show the new data
  Serial.println(brain.readCSV());


  int signalNoise = brain.readSignalQuality(); // 0 = no noise, 200 = all noise
  if (signalNoise > 0)
  {
    Serial.print(F("Too much noise: "));
    Serial.println(signalNoise);
    return;  // Don't act on the data if there is any noise
  }


  // Read the meditation level
  medValue = brain.readMeditation();


  // Look for value going from under 25 to over 25
  if (medValue > 25 && prevMedValue <= 25)
  {
    Keyboard.print('w');  // send a 'w' to the computer via Keyboard HID
  }


  // Look for value going from under 100 to over 100

  if (medValue < 100 && prevMedValue >= 100)
  {
    Keyboard.print('v');  // send a 'v' to the computer via Keyboard HID
  }
  prevMedValue = medValue;
}

Does the delay ruin it?

Delay ruins everything, I wish it were not in the basic tutorials in the IDE as it leads new people to think they can use it all over the place.

Have you done the exercise blink without delay under examples / digital in the IDE? It shows you the basics of what you need.

Yes, bool holds 2 values, 1 and 0 or true and false. My aim is to get you sending 1, and only 1, V or W, when you cross the threshold, then, when that works, move on to sending more than 1 every however many seconds if that’s what you need.

Partial code, this is not complete but, I hope, gives you hints to play around with.

#include <Keyboard.h>
#include <Brain.h>
Brain brain(Serial1);
long interval = 500;
long previousMillis = 0;
int medValue;
bool W_sent = false;            //Declared as a global variable
void setup() {
Serial.begin(9600);
Keyboard.begin();
Serial1.begin(9600);
delay(5000);
//.....
if (medValue < 25  && W_sent == false) {
   Keyboard.print('w');  // send a 'w' to the computer via Keyboard HID
   W_sent = true;   //You have sent a W, don't send another
  {      //I don't think this should be here
  } else if (medValue >= 25) {
  W_sent = false;
  }

Please, before posting code use tools / auto format in the IDE. At the moment you have all your { and } lined up, this makes reading your code a pain (and I am not very good at reading other people’s code as it is!). It would also help if you divided your code into functions and called the functions from loop, instead of trying to do everything in loop. Have a look at ‘using Nextion displays with Arduino’ for how I do it. (The code in there is not relevant, I want you to see how it is split into functions that are called from loop).

Alright, thank y'all so much. Is karma the best thank you? How can I show my gratitude to both of you? I was able to get it working thanks to the help here.

Alright, thank y'all so much. Is karma the best thank you? How can I show my gratitude to both of you?

Well, I'm partial to a curry; I don't suppose...???

No, seriously Karma and knowing my advice helped you get it working is all the reward I need on here. Thanks for the update and I'm pleased you get it working :)