Saving the Emotiv EPOC affective suite data in a text file

// Based on the ProcessingEpocOsc1 example by Joshua Madara, available at
// hyperritual.com/blog/processing-epoc-osc/
// Modified by Dorothea Kalogianni and Zechao Alvin
//Saving the Emotiv EPOC affective suite data in a text file
import oscP5.*;
import netP5.*;
float excitement;
float boredom;
float engagement;
float frustration;
float meditation;
OscP5 oscP5;
PrintWriter output;

void setup() {
oscP5 = new OscP5(this, 7400);
// Create a new file in the sketch directory
output = createWriter(“testing.txt”);
}

void draw() {
int m = millis();
//output.print(m,”excitement=”++ “,”+);
output.println(“time=” + hour()+”:”+minute()+”:”+second() + ” ” + “,”);
output.println(“excitement=” + excitement + ” ” + “,”);
output.println(“engagement=” + engagement + ” ” + “,”);
output.println(“frustration=” + frustration + ” ” + “,”);
output.println(“meditation=” + meditation + ” ” + “,”);
output.println(“boredom=” + meditation + ” ” + “,”);
}

void oscEvent(OscMessage theOscMessage) {
// check if theOscMessage has an address pattern we are looking for
if(theOscMessage.checkAddrPattern(“/AFF/Excitement”) == true) {
// parse theOscMessage and extract the values from the OSC message arguments
//excitement = ceil(theOscMessage.get(0).floatValue()*255);
excitement = theOscMessage.get(0).floatValue();
} else if (theOscMessage.checkAddrPattern(“/AFF/Meditation”) == true) {
meditation =theOscMessage.get(0).floatValue();
}
if(theOscMessage.checkAddrPattern(“/AFF/Engaged/Bored”) == true) {
// parse theOscMessage and extract the values from the OSC message arguments

engagement = theOscMessage.get(0).floatValue();
boredom = 1-engagement; //to seperate boredom from engagement

} else if (theOscMessage.checkAddrPattern(“/AFF/Frustration”) == true) {
frustration = theOscMessage.get(0).floatValue();
}
}

void keyPressed() {
output.flush(); // Writes the remaining data to the file
output.close(); // Finishes the file
exit(); // Stops the program
}

Download file EMOTIV_data

Processing Code: ‘Hairy Ball’

import oscP5.*;
import netP5.*;
import ddf.minim.*;
NetAddress myRemoteLocation1;
NetAddress myRemoteLocation2;
NetAddress myRemoteLocation3;
int cuantos = 3000;//change the density of the hair
Pelo[] lista ;
float[] z = new float[cuantos];
float[] phi = new float[cuantos];
float[] largos = new float[cuantos];
float radio;
float rx = 0;
float ry =0;
boolean checkPlay=false;
Minim minim;
AudioPlayer player;

float excitement=0.8;
float engagement=0.6;
float frustration=0.4;
float meditation=0.2;
float boredom=0;
float lastExcitement;
float lastEngagement;
float lastFrustration;
float lastMeditation;
float lastBoredom;
int excount=0;
int encount=0;
int fcount=0;
int mcount=0;
int bcount=0;
int allcount=1500;//change the time for monitoring the value change of data

OscP5 oscP5;

int angle;

void setup() {
oscP5 = new OscP5(this, 7400);
minim = new Minim(this);
player = minim.loadFile(“WhiteNoiseSmall.aif”);

myRemoteLocation1 = new NetAddress(“172.20.187.146”,5001);
myRemoteLocation2 = new NetAddress(“172.20.187.146”,5001);
myRemoteLocation3 = new NetAddress(“172.20.187.146”,5001);

size(displayWidth, displayHeight, P3D);
radio = 150;//can change the size of the hariball
lista = new Pelo[cuantos];
for (int i=0; i<cuantos; i++) {//drawing the hair
lista[i] = new Pelo();
}
noiseDetail(3);//change the sharpness of the display
}

void draw() {

noCursor();

print(“excitement=”+excitement+”\n”);
print(“engagement=”+engagement+”\n”);
print(“frustration=”+frustration+”\n”);
print(“meditation=”+meditation+”\n”);
print(“boredom=”+boredom+”\n”);
print(“excount=”+excount+”\n”);
print(“encount=”+encount+”\n”);
print(“fcount=”+fcount+”\n”);
print(“mcount=”+mcount+”\n”);
print(“bcount=”+bcount+”\n”);
excitement=excitement+0.0001;
engagement=engagement+0.0005;
frustration=frustration+0.001;
meditation=meditation+0.0015;
//boredom=boredom+0.002;

if(mousePressed==false){ // create an osc message
OscMessage myMessage = new OscMessage(“/test”);

println(meditation+”-“+frustration+”-“+engagement+”-“+excitement+”-“+boredom);
myMessage.add(meditation);
myMessage.add(frustration);
myMessage.add(engagement);
myMessage.add(excitement);
myMessage.add(boredom);
// send the message
oscP5.send(myMessage, myRemoteLocation1);
oscP5.send(myMessage, myRemoteLocation2);
oscP5.send(myMessage, myRemoteLocation3);
}

background(0);

//Text field for monitoring the data values
textSize(20);
fill(255);

text(“EXCITEMENT “+excitement, width-250, height-100);
text(“ENGAGEMENT “+engagement, width-250, height-80);
text(“FRUSTRATION “+frustration, width-250, height-60);
text(“MEDITATION “+meditation, width-250, height-40);
text(“BOREDOM “+boredom, width-250, height-20);
translate(width/2, height/2);//P3D set up
//here is to count the time that any parameter does not change indicating the headset is not working
if (excitement==lastExcitement) {
excount++;
if (excount>allcount) {
excount=allcount+1;
}
}
if (engagement==lastEngagement) {
encount++;
if (encount>allcount) {
encount=allcount+1;
}
}
if (frustration==lastFrustration) {
fcount++;
if (fcount>allcount) {
fcount=allcount+1;
}
}
if (meditation==lastMeditation) {
mcount++;
if (mcount>allcount) {
mcount=allcount+1;
}
}
if (boredom==lastBoredom) {
bcount++;
if (bcount>allcount) {
bcount=allcount+1;
}
}

if (excitement!=lastExcitement) {
excount=0;
}
if (engagement!=lastEngagement) {
encount=0;
}
if (frustration!=lastFrustration) {
fcount=0;
}
if (meditation!=lastMeditation) {
mcount=0;
}
if (boredom!=lastBoredom) {
bcount=0;
}

lastExcitement=excitement;
lastEngagement=engagement;
lastFrustration=frustration;
lastMeditation=meditation;
lastBoredom=boredom;

if (excount>allcount||encount>allcount||fcount>allcount||mcount>allcount||bcount>allcount) {

//here is the effect of TV noise when the headset is not working properly
//reference: www.openprocessing.org/sketch/24107
translate(-width/2, -height/2);
for (int i=width; i>=0; i-=4) {
for (int j=width; j>=0; j-=4) {
fill(random(255));
rect(i, j, 4, 4);
noStroke();
}
}
if(checkPlay==false){
minim = new Minim(this);
player = minim.loadFile(“WhiteNoiseSmall.aif”);
player.play();
checkPlay=true;
}
}
else {
checkPlay=false;
player.close();
minim.stop();
if (boredom>engagement && boredom>frustration && boredom> meditation&& boredom> excitement) {
//ADD YOUR effect FOR BOREDOM HERE
rotateY(0);
rotateX(0);
fill(0);
noStroke();
radio = height/4*boredom;
sphere(radio);

for (int i = 0;i < cuantos; i++) {
lista[i].dibujar();
}
}

if (excitement>engagement && excitement>frustration && excitement> meditation&& excitement>boredom) {

rotateY(radians(0));
rotateX(radians(0));
fill(0);
radio = height/4*excitement; //no more scaling *excitement
noStroke();
sphere(radio);

for (int i = 0;i < cuantos; i++) {
lista[i].dibujar();
}
}

if (engagement>excitement && engagement>frustration && engagement>meditation&&engagement>boredom) {

angle++;//angle needs to be set 360=0
if(angle==360){angle=0;}
rotateY(radians(angle));//ball can spin and rotate
rotateX(radians(angle));
radio = height/4*engagement; //no more scaling *engagement
fill(0);
noStroke();
sphere(radio);

for (int i = 0;i < cuantos; i++) {
lista[i].dibujar();
}
}

if (frustration>excitement && frustration>engagement && frustration>meditation && frustration>boredom) {

float rxp = ((random(150, 450)-(width/2))*0.005);//ball can fluctuate
float ryp = ((random(150, 450)-(height/2))*0.005);
rx = (rx*0.9)+(rxp*0.1);
ry = (ry*0.9)+(ryp*0.1);
rotateY(rx);
rotateX(ry);
radio = height/4*frustration; //no more scaling *frustration
fill(0);
noStroke();
sphere(radio);

for (int i = 0;i < cuantos; i++) {
lista[i].dibujar();
}
}

if (meditation>excitement && meditation>engagement && meditation>frustration&& meditation>boredom) {

rotateY(radians(0));
rotateX(radians(0));
fill(0);
noStroke();
radio = height/4*meditation; //no more scaling *meditation
sphere(radio);

for (int i = 0;i < cuantos; i++) {
lista[i].dibujar();
}
}
}
}
class Pelo {

float z = random(-radio, radio);
float phi = random(TWO_PI);
float largo = random(1.15, 1.2);//control the length of the hair
float theta = asin(z/radio);

void dibujar() {//grow hair

if (meditation>excitement && meditation>engagement && meditation>frustration &&meditation>boredom&& excount<allcount && encount<allcount && fcount<allcount && mcount<allcount&&bcount<allcount) {
float largo = random(1.15, 1.2);//control the length of the hair
float off = (random(millis()* 0.0005, sin(phi))-0.5) * 0.6;//enable the hair looks vivid can change noise to random to acheive falling effect
float offb = (random(millis() * 0.0007, sin(z) * 0.01)-0.5) * 0.6;//enable the hair looks vivid can change noise to random to acheive falling effect
float thetaff = theta+off;
float phff = phi+offb;
float x = radio * cos(theta) * cos(phi);
float y = radio * cos(theta) * sin(phi);
float z = radio * sin(theta);
float xo = radio * cos(thetaff) * cos(phff);
float yo = radio * cos(thetaff) * sin(phff);
float zo = radio * sin(thetaff);
float xb = xo * largo*1;//by mutiple >1 can change the length of each hair
float yb = yo * largo*1;//by mutiple >1 can change the length of each hair
float zb = zo * largo*1;//by mutiple >1 can change the length of each hair
beginShape(LINES);//drawing lines
//strokeWeight(2);
stroke(0);
vertex(x, y, z);
stroke(230,200);
vertex(xb, yb, zb);
endShape();
}

else if (excitement>engagement && excitement>frustration && excitement> meditation &&excitement>boredom&& excount<allcount && encount<allcount && fcount<allcount && mcount<allcount&&bcount<allcount) {
float largo = random(1.2, 1.35);//control the length of the hair
float off = (noise(millis()* 0.0005, sin(phi))-0.5) * 0.3;//enable the hair looks vivid can change noise to random to acheive falling effect
float offb = (noise(millis() * 0.0007, sin(z) * 0.01)-0.5) * 0.3;//enable the hair looks vivid can change noise to random to acheive falling effect
float thetaff = theta+off;
float phff = phi+offb;
float x = radio * cos(theta) * cos(phi);
float y = radio * cos(theta) * sin(phi);
float z = radio * sin(theta);
float xo = radio * cos(thetaff) * cos(phff);
float yo = radio * cos(thetaff) * sin(phff);
float zo = radio * sin(thetaff);
float xb = xo * largo*1;//by mutiple >1 can change the length of each hair
float yb = yo * largo*1;//by mutiple >1 can change the length of each hair
float zb = zo * largo*1;//by mutiple >1 can change the length of each hair
beginShape(LINES);//drawing lines
//strokeWeight(4);
stroke(255);
vertex(x, y, z);
stroke(150,100);
vertex(xb, yb, zb);
endShape();
}

else {
float off = (noise(millis()* 0.0005, sin(phi))-0.5) * 0.3;//enable the hair looks vivid can change noise to random to acheive falling effect
float offb = (noise(millis() * 0.0007, sin(z) * 0.01)-0.5) * 0.3;//enable the hair looks vivid can change noise to random to acheive falling effect
float thetaff = theta+off;
float phff = phi+offb;
float x = radio * cos(theta) * cos(phi);
float y = radio * cos(theta) * sin(phi);
float z = radio * sin(theta);
float xo = radio * cos(thetaff) * cos(phff);
float yo = radio * cos(thetaff) * sin(phff);
float zo = radio * sin(thetaff);
float xb = xo * largo*1;//by mutiple >1 can change the length of each hair
float yb = yo * largo*1;//by mutiple >1 can change the length of each hair
float zb = zo * largo*1;//by mutiple >1 can change the length of each hair
beginShape(LINES);//drawing lines
// strokeWeight(2);
stroke(255);
vertex(x, y, z);
stroke(150,100);
vertex(xb, yb, zb);
endShape();
}
}
}

 

void oscEvent(OscMessage theOscMessage) {
// check if theOscMessage has an address pattern we are looking for
if(theOscMessage.checkAddrPattern(“/AFF/Excitement”) == true) {
// parse theOscMessage and extract the values from the OSC message arguments
//excitement = ceil(theOscMessage.get(0).floatValue()*255);
excitement = theOscMessage.get(0).floatValue();
} else if (theOscMessage.checkAddrPattern(“/AFF/Meditation”) == true) {
meditation =theOscMessage.get(0).floatValue();
}
if(theOscMessage.checkAddrPattern(“/AFF/Engaged/Bored”) == true) {
// parse theOscMessage and extract the values from the OSC message arguments

engagement = theOscMessage.get(0).floatValue();
boredom = 1-engagement; //to seperate boredom from engagement
//println(“ENTERED”);
} else if (theOscMessage.checkAddrPattern(“/AFF/Frustration”) == true) {
frustration = theOscMessage.get(0).floatValue();
}
}

OSC communication between multi laptops

As there is only one headset, we need to send OSC messages to multi laptops to show 3 different types of visualizations.

Communication method

  1. Processing code in Laptop A receives the OSC message from a head set.
  2. Laptop A sends OSC message to laptop B and C.

oscp

To send and receive message from a laptop to other laptop, processing codes of each laptop have to includes port definition in void setup().

void setup() {

//start oscP5, listening for incoming messages on port 7400
//make sure this matches the port in Mind Your OSCs
oscP5 = new OscP5(this, 5001);
size(displayWidth, displayHeight, OPENGL);
}

“oscP5 = new OscP5(this, 7400); ” means that this code starts oscP5, listening for incoming messages at port 7400.  Port number is arbitrary but laptop A , which receives OSC message from a headset directly, must set port number 7400 because the number matches the port in Mind Your OSCs.

Code for sending message

Furthermore, laptop A includes the code as below.

void setup() {

//start oscP5, listening for incoming messages on port 7400
//make sure this matches the port in Mind Your OSCs
oscP5 = new OscP5(this, 7400);
size(displayWidth, displayHeight, OPENGL);


myRemoteLocation = new NetAddress(“127.0.0.1”,5001);
// laptop B
myRemoteLocation = new NetAddress(“127.0.0.2”,5001); // laptop C

}

The line “myRemoteLocation” defines the IP address and port number of laptop B and C.  This processing send messages to these remote locations.  Using this method, Processing code can send and receive not only value but also texts and numbers.

Reference

button controls servo_windChime

// Sweep
// by BARRAGAN <barraganstudio.com&gt;
// This example code is in the public domain.
#include <Servo.h>

const int buttonPin = 2;
const int servoPin = 9;

int buttonState = 0;
int lastButtonState = 0;
int count =0;
boolean state=0;

Servo myservo; // create servo object to control a servo
// a maximum of eight servo objects can be created

int pos = 0; // variable to store the servo position

void setup()
{
myservo.attach(9); // attaches the servo on pin 9 to the servo object
pinMode(buttonPin, INPUT);
pinMode(servoPin, OUTPUT);
Serial.begin(9600);

}

void loop()
{

buttonState = digitalRead(buttonPin);
if(buttonState!=lastButtonState){count++;}
if(count%2==1){state=1;}
else if(count%2==0){state=0;}
if(state==1){
for(pos = 0; pos < 180; pos += 7) // goes from 0 degrees to 180 degrees
{ // in steps of 1 degree
myservo.write(pos); // tell servo to go to position in variable ‘pos’
delay(15); // waits 15ms for the servo to reach the position
}
for(pos = 180; pos>=1; pos-=1) // goes from 180 degrees to 0 degrees
{
myservo.write(pos); // tell servo to go to position in variable ‘pos’
delay(15); // waits 15ms for the servo to reach the position
}
}
else if(state==0){myservo.write(0);}
lastButtonState=buttonState;
Serial.print(0,0);
}

code for button control toy motor_woodenFish

#include <Button.h>

const int motorPin = 9; // define the pin the motor is connected to
// (if you use pin 9,10,11 or 3you can also control speed)
const int buttonPin = 2;

//Button button = Button(2,PULLUP);
int buttonState = 0;
int lastButtonState = 0;
int count = 0;
boolean state = 0;
/*
* setup() – this function runs once when you turn your Arduino on
* We set the motors pin to be an output (turning the pin high (+5v) or low (ground) (-))
* rather than an input (checking whether a pin is high or low)
*/
void setup()
{
pinMode(motorPin, OUTPUT);
pinMode(buttonPin, INPUT);
}
/*
* loop() – this function will start after setup finishes and then repeat
* we call a function called motorOnThenOff()
*/

void loop() // run over and over again
{
motorOnThenOff();

void motorOnThenOff(){
int onTime = 2500; //the number of milliseconds for the motor to turn on for
int offTime = 1000; //the number of milliseconds for the motor to turn off for

buttonState = digitalRead(buttonPin);

if(button.isPressed()){
digitalWrite(buttonPin, HIGH);
}else{
digitalWrite(buttonPin, LOW);
}

if(buttonState == HIGH){
// state = 1;
digitalWrite(motorPin, LOW); // turns the motor On
//delay(onTime); // waits for onTime milliseconds
//digitalWrite(motorPin, LOW); // turns the motor Off
//delay(offTime); // waits for offTime milliseconds
}
else{
//state = 0;
digitalWrite(motorPin, HIGH); // turns the motor On
}

}
}
}

code for light sensor controlled by button

/*
Pitch follower

Plays a pitch that changes based on a changing analog input

circuit:
* 8-ohm speaker on digital pin 9
* photoresistor on analog 0 to 5V
* 4.7K resistor on analog 0 to ground

created 21 Jan 2010
modified 31 May 2012
by Tom Igoe, with suggestion from Michael Flynn

This example code is in the public domain.

arduino.cc/en/Tutorial/Tone2

*/
int inPin = 2;
int outPin = 9;
int state = HIGH;
int reading;
int previous = LOW;
long time = 0; // the last time the output pin was toggled
long debounce = 200;
void setup() {
// initialize serial communications (for debugging only):
Serial.begin(9600);
pinMode(inPin, INPUT);
pinMode(outPin, OUTPUT);
// read the sensor:
}

void loop() {
reading = digitalRead(inPin);
if(reading == HIGH && previous == LOW && millis() – time > debounce){
if (state == HIGH)
state = LOW;
else
state = HIGH;

time = millis();

}

digitalWrite(outPin, state);
previous = reading;

if(state == LOW){

int sensorReading = analogRead(A0);
Serial.println(sensorReading);
// map the analog input range (in this case, 400 – 1000 from the photoresistor)
// to the output pitch range (120 – 1500Hz)
// change the minimum and maximum input numbers below
// depending on the range your sensor’s giving:
int thisPitch = map(sensorReading, 10, 300, 90, 2500);

// play the pitch:
tone(9, thisPitch, 3.9);
delay(4); // delay in between reads for stability
}

if(state == HIGH){
//noTone(9);
int sensorReading = analogRead(A0);
Serial.println(sensorReading);
// map the analog input range (in this case, 400 – 1000 from the photoresistor)
// to the output pitch range (120 – 1500Hz)
// change the minimum and maximum input numbers below
// depending on the range your sensor’s giving:
int thisPitch = map(sensorReading, 10, 300, 0, 0);

}

}

code for pot on arduino working with the piano string

// Controlling a servo position using a potentiometer (variable resistor)
// by Michal Rinott <people.interaction-ivrea.it/m.rinott&gt;

#include <Servo.h>

Servo myservo; // create servo object to control a servo

int potpin = 2; // analog pin used to connect the potentiometer
int val; // variable to read the value from the analog pin

void setup()
{
//Serial.begin(9600);
myservo.attach(9); // attaches the servo on pin 9 to the servo object
}

void loop()
{
val = analogRead(potpin); // reads the value of the potentiometer (value between 0 and 1023)
val = map(val, 0, 1023, 0, 179); // scale it to use it with the servo (value between 0 and 180)
//if (val>10){
myservo.write(val); // sets the servo position according to the scaled value

// }
delay(100);
Serial.println(val);
//Serial.print(0,0);
// waits for the servo to get there
}