Last active
May 5, 2019 16:38
-
-
Save jhschwartz/f24f50bdf576f82f39fabe7df4328959 to your computer and use it in GitHub Desktop.
Example Socket for Emotiv EPOC+ Wireless EEG (emotiv.com/epoc)
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
// Written by Jacob Schwartz, jaschwa@umich.edu, September 2018, edited & published May 2019 | |
// In September 2018, I worked with the EPOC+ as part of a project for the MedHacks Hackathon. | |
// It was extremely counterintuitive to work with, and if anyone else comes across issues with | |
// it I figure this code might be helpful. It was awhile ago but I'd be willing to try to to help | |
// if anyone experiences issues with the device - just open an issue on my project's repo | |
// (github.com/jhschwartz/medhacks-zzg) and I'll see if I can help. | |
// EMOTIV docs: https://emotiv.github.io/cortex-docs/ | |
// see https://github.com/jhschwartz/medhacks-zzg for my failed project that used the EPOC+ | |
var token = 'YOUR-TOKEN-HERE' | |
var headset_id = 'YOUR-HEADSET-ID' | |
// Get references to elements on the page. | |
// Might not be useful to anyone else. | |
var form = document.getElementById('message-form') | |
var messageField = document.getElementById('message') | |
var messagesList = document.getElementById('messages') | |
var socketStatus = document.getElementById('status') | |
var closeBtn = document.getElementById('close') | |
// These IDs are how you jump into the websocket | |
const ID_GET_USER_LOGIN = 1 | |
const ID_AUTHORIZE = 2 | |
const ID_LICENSE_ACCEPT = 3 | |
const ID_QUERY_HEADSETS = 4 | |
const ID_CREATE_SESSION = 5 | |
const ID_UPDATE_SESSION = 6 | |
const ID_QUERY_SESSIONS = 7 | |
const ID_SUBSCRIBE = 8 | |
const ID_OTHER = 999 | |
// socket opens on load of the page | |
var run_socket = (socket, streams) => { | |
// Thanks SO user user3215378: https://stackoverflow.com/a/21394730/4176019 | |
function waitForSocketConnection(socket, callback) { | |
setTimeout( | |
function () { | |
if (socket.readyState === 1) { | |
console.log("Connection is made") | |
if(callback != null){ | |
callback() | |
} | |
return | |
} else { | |
console.log("wait for connection...") | |
waitForSocketConnection(socket, callback) | |
} | |
}, 5) // wait 5 milisecond for the connection... | |
} | |
// connect socket | |
var socket = new WebSocket('wss://emotivcortex.com:54321') | |
// send user login on load | |
waitForSocketConnection(socket, function() { | |
socket.send(JSON.stringify({ | |
'jsonrpc': '2.0', | |
'method': 'getUserLogin', | |
'id': ID_GET_USER_LOGIN | |
})) | |
}) | |
// every time the socket receives data from the headset it includes an id | |
// that corresponds to certain events defined in the consts (lines 24-32) | |
socket.onmessage = function(event) { | |
var data = JSON.parse(event.data) | |
// the switch statement lets you go from a generic reception of a headset "message" | |
// to specific methods defined in this file. | |
switch (data['id']) { | |
case ID_GET_USER_LOGIN: | |
console.log('received user login') | |
console.log(data) | |
authorize() | |
break | |
case ID_AUTHORIZE: | |
console.log('received authorize') | |
console.log(data) | |
token = data['result']['_auth'] | |
license(token) | |
break | |
case ID_LICENSE_ACCEPT: | |
console.log('received license') | |
console.log(data) | |
query_headsets() | |
break | |
case ID_QUERY_HEADSETS: | |
console.log('received query headsets') | |
console.log(data) | |
headset_id = data['result'][0]['id'] | |
console.log(headset_id) | |
create_session(headset_id) | |
break | |
case ID_CREATE_SESSION: | |
console.log('received create session') | |
console.log(data) | |
update_session() | |
break | |
case ID_UPDATE_SESSION: | |
console.log('received update session') | |
console.log(data) | |
query_sessions() | |
// break | |
case ID_QUERY_SESSIONS: | |
console.log('received query session') | |
console.log(data) | |
subscribe() | |
break | |
case ID_SUBSCRIBE: | |
console.log('received subscribe') | |
console.log(data) | |
handle_message(data) | |
break | |
case ID_OTHER: | |
console.log('received other') | |
handle_message(data) | |
break | |
default: | |
// default no id so we are collecting data -- this is specific to your project/where | |
// you get to do what you actually get to do something with the headset | |
process_data(data) | |
} | |
} | |
// specific parts of authorization, license, etc. are outlined by the EPOC+ docs. | |
function authorize() { | |
socket.send(JSON.stringify({ | |
'jsonrpc': '2.0', | |
'method': 'authorize', | |
'id': ID_AUTHORIZE, | |
'params': { | |
'username': username, | |
'password': password, | |
'client_id': client_id, | |
'client_secret': client_secret | |
} | |
})) | |
} | |
function license(token) { | |
console.log(token) | |
socket.send(JSON.stringify({ | |
'jsonrpc': '2.0', | |
'method': 'acceptLicense', | |
'id': ID_LICENSE_ACCEPT, | |
'params': { | |
'_auth': token | |
} | |
})) | |
} | |
function query_headsets() { | |
socket.send(JSON.stringify({ | |
'jsonrpc': '2.0', | |
'method': 'queryHeadsets', | |
'id': ID_QUERY_HEADSETS, | |
'params': { | |
'wilcard': 'EPOCPLUS-*' | |
} | |
})) | |
} | |
function create_session(headset_id) { | |
socket.send(JSON.stringify({ | |
'jsonrpc': '2.0', | |
'method': 'createSession', | |
'id': ID_CREATE_SESSION, | |
'params': { | |
'_auth': token, | |
'headset': headset_id, | |
'status': 'open' | |
} | |
})) | |
} | |
function update_session() { | |
socket.send(JSON.stringify({ | |
"jsonrpc": "2.0", | |
"method": "updateSession", | |
"params": { | |
"_auth": token, | |
"status": 'active' | |
}, | |
"id": ID_UPDATE_SESSION | |
})) | |
} | |
function query_sessions() { | |
socket.send(JSON.stringify({ | |
'jsonrpc': '2.0', | |
'method': 'querySessions', | |
'id': ID_QUERY_SESSIONS, | |
'params': { | |
'_auth': token | |
} | |
})) | |
} | |
function subscribe() { | |
socket.send(JSON.stringify({ | |
'jsonrpc': '2.0', | |
'method': 'subscribe', | |
'id': ID_SUBSCRIBE, | |
'params': { | |
'_auth': token, | |
'streams': streams | |
} | |
})) | |
} | |
var handle_message = data => { | |
console.log(data) | |
} | |
// --- BELOW THIS LINE IS WHERE YOU ACTUALLY GET TO DO STUFF WITH THE HEADSET BEYOND JUST CONNECTING --- // | |
// the stuff in data, like 'pow', 'time', 'eeg' are provided by the headset and were used by my project. | |
var process_data = data => { | |
// pow: band data | |
// using this to interpret sleep | |
// needs to be stored in sets for analysis | |
if (data['pow'] != undefined) { | |
var date = new Date() | |
data['time'] = date.getTime() | |
pow_data.push(data) // method was specific to my project | |
update_pow(data) // method was specific to my project | |
} | |
// met: health metrics (stress, excitement, etc) | |
// using this to associate with sleep | |
// needs to be stored in sets for analysis | |
if (data['met'] != undefined && data != undefined) { | |
met_data.push(data) // method was specific to my project | |
update_met(data) // method was specific to my project | |
} | |
// eeg: health metrics (stress, excitement, etc) | |
// using this to associate with sleep | |
// needs to be stored in sets for analysis | |
if (data['eeg'] != undefined && data != undefined) { | |
eeg_data.push(data) // method was specific to my project | |
update_eeg(data) // method was specific to my project | |
} | |
// mot: motion data | |
// using this to look pretty/stand in for our lack of real-time eeg | |
// needs to trigger an update event all the time | |
if (data['pow'] != undefined) { | |
update_mot(data) // method was specific to my project | |
} | |
} | |
socket.onclose = function(event) { | |
console.log('socket closed!') | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment