-
Notifications
You must be signed in to change notification settings - Fork 258
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #985 from OpenBCI/development
GUI 5.0.6 - July 2021
- Loading branch information
Showing
15 changed files
with
361 additions
and
35 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
149 changes: 149 additions & 0 deletions
149
Networking-Test-Kit/Serial/Arduino_FocusFan_Serial/Arduino_FocusFan_Serial.ino
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,149 @@ | ||
///////////////////////////////////////////////////////////////////////////////////////// | ||
// OpenBCI_GUI to Arduino via Serial: Focus Fan! // | ||
// // | ||
// - The Arduino Built-In LED blinks when the user is Focused // | ||
// - A button on pin 7 toggles motor speed: Full, Medium, Low, and Off // | ||
// // | ||
// Tested 7/13/2021 using iMac, Arduino Pro Mini, OpenBCI_GUI 5.0.5 // | ||
// Uses https://learn.adafruit.com/adafruit-arduino-lesson-15-dc-motor-reversing/ // | ||
// and https://docs.openbci.com/Tutorials/17-Arduino_Focus_Example // | ||
///////////////////////////////////////////////////////////////////////////////////////// | ||
|
||
const byte numChars = 32; | ||
char receivedChars[numChars]; // an array to store the received data | ||
String previousData = ""; | ||
boolean newData = false; | ||
boolean isFocused = false; | ||
boolean lastFocusState = false; | ||
|
||
int enablePin = 11; | ||
int in1Pin = 10; | ||
int in2Pin = 9; | ||
int buttonPin = 7; | ||
int ledPin = 13; | ||
|
||
int buttonPushCounter = 0; // counter for the number of button presses | ||
int buttonState = 0; // current state of the button | ||
int lastButtonState = 0; // previous state of the button | ||
|
||
void setup() { | ||
pinMode(in1Pin, OUTPUT); | ||
pinMode(in2Pin, OUTPUT); | ||
pinMode(enablePin, OUTPUT); | ||
pinMode(buttonPin, INPUT_PULLUP); | ||
|
||
Serial.begin(57600); | ||
pinMode(LED_BUILTIN, OUTPUT); | ||
Serial.println("<Arduino is ready>"); | ||
} | ||
|
||
void loop() { | ||
recvWithEndMarker(); | ||
showNewData(); | ||
|
||
handleButtonState(); | ||
//check for state change | ||
if (isFocused) { | ||
setMotor(getMotorPower(), true); | ||
} else { | ||
setMotor(0, true); | ||
} | ||
lastFocusState = isFocused; | ||
} | ||
|
||
//Recieve data and look for the endMarker '\n' (new line) | ||
void recvWithEndMarker() { | ||
static byte ndx = 0; | ||
char endMarker = '\n'; | ||
char rc; | ||
|
||
while (Serial.available() > 0 && newData == false) { | ||
rc = Serial.read(); | ||
|
||
if (rc != endMarker) { | ||
receivedChars[ndx] = rc; | ||
ndx++; | ||
if (ndx >= numChars) { | ||
ndx = numChars - 1; | ||
} | ||
} | ||
else { | ||
receivedChars[ndx] = '\0'; // terminate the string | ||
ndx = 0; | ||
newData = true; | ||
} | ||
} | ||
} | ||
|
||
void showNewData() { | ||
if (newData == true) { | ||
//Convert char array into string | ||
String s = receivedChars; | ||
//Only perform an action when the incoming data changes | ||
if (!s.equals(previousData)) { | ||
//Check if the string is "true" or "false" | ||
if (s.equals("0")) { | ||
Serial.println("Input: FALSE"); | ||
isFocused = false; | ||
digitalWrite(LED_BUILTIN, LOW); | ||
} else if (s.equals("1")) { | ||
Serial.println("Input: TRUE"); | ||
digitalWrite(LED_BUILTIN, HIGH); | ||
isFocused = true; | ||
} else { | ||
//Otherwise print the incoming with no action | ||
Serial.println("This just in ... " + s); | ||
} | ||
} | ||
newData = false; | ||
previousData = s; | ||
} | ||
} | ||
|
||
void setMotor(int speed, boolean reverse) { | ||
analogWrite(enablePin, speed); | ||
digitalWrite(in1Pin, !reverse); | ||
digitalWrite(in2Pin, reverse); | ||
} | ||
|
||
void handleButtonState () { | ||
buttonState = digitalRead(buttonPin); | ||
|
||
// compare the buttonState to its previous state | ||
if (buttonState != lastButtonState) { | ||
// if the state has changed, increment the counter | ||
if (buttonState == HIGH) { | ||
// if the current state is HIGH then the button went from off to on: | ||
buttonPushCounter++; | ||
//Serial.println("on"); | ||
} else { | ||
// if the current state is LOW then the button went from on to off: | ||
//Serial.println("off"); | ||
} | ||
// Delay a little bit to avoid bouncing | ||
delay(50); | ||
} | ||
// save the current state as the last state, for next time through the loop | ||
lastButtonState = buttonState; | ||
} | ||
|
||
int getMotorPower() { | ||
//Toggle the fan power between four settings, shown below | ||
//Default: Full Power | ||
int power; | ||
switch (buttonPushCounter % 4) { | ||
case 0: //Every 4 clicks reverts to full power | ||
power = 255; //Full power | ||
break; | ||
case 1: | ||
power = 180; //Medium power | ||
break; | ||
case 2: | ||
power = 90; //Low power | ||
break; | ||
case 3: | ||
power = 0; //Motor off | ||
break; | ||
} | ||
return power; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,130 @@ | ||
//Used in the Focus Widget to provide auditory neurofeedback | ||
//Adjust amplitude of calming audio samples using normalized band power data or predicted metric | ||
|
||
Minim minim; | ||
FilePlayer[] auditoryNfbFilePlayers; | ||
ddf.minim.ugens.Gain[] auditoryNfbGains; | ||
AudioOutput audioOutput; | ||
|
||
//Pre-load audio files into memory in delayedSetup for best app performance and no waiting | ||
void asyncLoadAudioFiles() { | ||
final int _numSoundFiles = 5; | ||
minim = new Minim(this); | ||
auditoryNfbFilePlayers = new FilePlayer[_numSoundFiles]; | ||
auditoryNfbGains = new ddf.minim.ugens.Gain[_numSoundFiles]; | ||
audioOutput = minim.getLineOut(); | ||
println("OpenBCI_GUI: AuditoryFeedback: Loading Audio..."); | ||
for (int i = 0; i < _numSoundFiles; i++) { | ||
//Use large buffer size and cache files in memory | ||
auditoryNfbFilePlayers[i] = new FilePlayer( minim.loadFileStream("bp" + (i+1) + ".mp3", 2048, true) ); | ||
auditoryNfbGains[i] = new ddf.minim.ugens.Gain(-15.0f); | ||
auditoryNfbFilePlayers[i].patch(auditoryNfbGains[i]).patch(audioOutput); | ||
} | ||
println("OpenBCI_GUI: AuditoryFeedback: Done Loading Audio!"); | ||
} | ||
|
||
class AuditoryNeurofeedback { | ||
|
||
private int x, y, w, h; | ||
private ControlP5 localCP5; | ||
public Button startStopButton; | ||
public Button modeButton; | ||
private boolean usingBandPowers = false; | ||
//There will always be 5 band powers, and 5 possible concurrent audio files for playback | ||
private final int NUM_SOUND_FILES = auditoryNfbFilePlayers.length; | ||
private final float MIN_GAIN = -42.0; | ||
private final float MAX_GAIN = -7.0; | ||
private final int MAX_BUTTON_W = 120; | ||
private int buttonW = 120; | ||
private int buttonH; | ||
|
||
AuditoryNeurofeedback(int _x, int _y, int _w, int _h) { | ||
localCP5 = new ControlP5(ourApplet); | ||
localCP5.setGraphics(ourApplet, 0,0); | ||
localCP5.setAutoDraw(false); | ||
buttonH = _h; | ||
createStartStopButton(_x, _y, buttonW, buttonH); | ||
createModeButton(_x, _y, buttonW, buttonH); | ||
} | ||
|
||
//Use band powers or prediction value to control volume of each sound file | ||
public void update(double[] bandPowers, float predictionVal) { | ||
if (usingBandPowers) { | ||
for (int i = 0; i < NUM_SOUND_FILES; i++) { | ||
float gain = map((float)bandPowers[i], 0.1, .7, MIN_GAIN + 20f, MAX_GAIN); | ||
auditoryNfbGains[i].setValue(gain); | ||
} | ||
} else { | ||
float gain = map(predictionVal, 0.0, 1.0, MIN_GAIN, MAX_GAIN); | ||
for (int i = 0; i < NUM_SOUND_FILES; i++) { | ||
auditoryNfbGains[i].setValue(gain); | ||
} | ||
} | ||
} | ||
|
||
public void draw() { | ||
localCP5.draw(); | ||
} | ||
|
||
public void screenResized(int _x, int _y, int _w, int _h) { | ||
localCP5.setGraphics(ourApplet, 0, 0); | ||
buttonW = (_w - 6) / 2; | ||
buttonW = buttonW > MAX_BUTTON_W ? MAX_BUTTON_W : buttonW; | ||
startStopButton.setPosition(_x - buttonW - 3, _y); | ||
startStopButton.setSize(buttonW, _h); | ||
modeButton.setPosition(_x + 3, _y); | ||
modeButton.setSize(buttonW, _h); | ||
} | ||
|
||
public void killAudio() { | ||
for (int i = 0; i < NUM_SOUND_FILES; i++) { | ||
auditoryNfbFilePlayers[i].pause(); | ||
auditoryNfbFilePlayers[i].rewind(); | ||
} | ||
} | ||
|
||
private void createStartStopButton(int _x, int _y, int _w, int _h) { | ||
//This is a generalized createButton method that allows us to save code by using a few patterns and method overloading | ||
startStopButton = createButton(localCP5, "startStopButton", "Turn Audio On", _x, _y, _w, _h, p5, 12, colorNotPressed, OPENBCI_DARKBLUE); | ||
//Set the border color explicitely | ||
startStopButton.setBorderColor(OBJECT_BORDER_GREY); | ||
//For this button, only call the callback listener on mouse release | ||
startStopButton.onRelease(new CallbackListener() { | ||
public void controlEvent(CallbackEvent theEvent) { | ||
//If using a TopNav object, ignore interaction with widget object (ex. widgetTemplateButton) | ||
if (!topNav.configSelector.isVisible && !topNav.layoutSelector.isVisible) { | ||
if (auditoryNfbFilePlayers[0].isPlaying()) { | ||
killAudio(); | ||
startStopButton.getCaptionLabel().setText("Turn Audio On"); | ||
} else { | ||
for (int i = 0; i < NUM_SOUND_FILES; i++) { | ||
auditoryNfbFilePlayers[i].loop(); | ||
} | ||
startStopButton.getCaptionLabel().setText("Turn Audio Off"); | ||
} | ||
} | ||
} | ||
}); | ||
startStopButton.setDescription("Start and Stop Auditory Feedback."); | ||
} | ||
|
||
private void createModeButton(int _x, int _y, int _w, int _h) { | ||
//This is a generalized createButton method that allows us to save code by using a few patterns and method overloading | ||
modeButton = createButton(localCP5, "modeButton", "Use Band Powers", _x, _y, _w, _h, p5, 12, colorNotPressed, OPENBCI_DARKBLUE); | ||
//Set the border color explicitely | ||
modeButton.setBorderColor(OBJECT_BORDER_GREY); | ||
//For this button, only call the callback listener on mouse release | ||
modeButton.onRelease(new CallbackListener() { | ||
public void controlEvent(CallbackEvent theEvent) { | ||
//If using a TopNav object, ignore interaction with widget object (ex. widgetTemplateButton) | ||
if (!topNav.configSelector.isVisible && !topNav.layoutSelector.isVisible) { | ||
String s = !usingBandPowers ? "Use Metric" : "Use Band Powers"; | ||
modeButton.getCaptionLabel().setText(s); | ||
usingBandPowers = !usingBandPowers; | ||
} | ||
} | ||
}); | ||
modeButton.setDescription("Change Auditory Feedback mode. Use the Metric to control all notes at once, or use Band Powers to control certain notes of the chord."); | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.