-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
big refactoring, moved keyboard logic to app.js
organized code in app.js added navdata callback to log the battery level if under 30%
- Loading branch information
Showing
4 changed files
with
302 additions
and
166 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,91 +1,208 @@ | ||
//IMPORTS | ||
const express = require("express"); | ||
const path = require('path'); | ||
const readline = require('readline'); | ||
const eegDevice = require('./libs/eeglib'); | ||
const drone = require('./libs/dronelib'); | ||
const sse = require('connect-sse')(); | ||
const PubSub = require('pubsub-js'); | ||
var resp; | ||
var EEG_ENABLED = false | ||
const eegClient = eegDevice.eegClient(); | ||
|
||
|
||
//CONSTANTS | ||
const BLINK_STRENGTH_THRESHOLD = 50; //strength treshold for a EEG blinking event | ||
const MEDITATION_STRENGTH_THRESHOLD = 95; //strength treshold for a EEG "high level meditation" event | ||
const ATTENTION_STRENGTH_THRESHOLD = 95; //strength treshold for a EEG "high level attention" event | ||
const MIN_INTERVAL_EVENT = 2000; //minimum interval to trigger an event (if an event occur after < MIN_INTERVAL_EVENT the event wont be propagated) | ||
const LOGGING_ENABLED = false; | ||
|
||
|
||
//APP VARIABLES | ||
var recently_takeOffOrLand = false; // to avoid double commands due to EEG crazyness | ||
var recently_waved = false; // to avoid double commands due to EEG crazyness | ||
var resp; //global var for SSE response => find a better way to handle this | ||
var previousData; | ||
|
||
if(EEG_ENABLED){ | ||
eegClient.connect(); | ||
} | ||
startExpress(); | ||
|
||
//FUNCTIONS DECLARATION | ||
|
||
/** | ||
* Start the Express app on port EXPRESS_PORT with and endpoint SSE on /liveEEGData | ||
*/ | ||
function startExpress() { | ||
const EXPRESS_PORT = 8080; | ||
const app = express(); | ||
|
||
//expose public folder | ||
app.use(express.static('public')) | ||
|
||
//expose Server Side Event endpoint for live EEG data stream | ||
app.get('/liveEEGData', sse, function (req, res) { | ||
resp = res; // expose response to global scope variable TODO: find a better way | ||
}); | ||
|
||
var mySubscriber = function (msg, data) { | ||
console.log("RAW DATA RECEIVED:"); | ||
console.log(msg, data); | ||
console.log("\n"); | ||
app.listen(EXPRESS_PORT, function () { | ||
console.log("Application running at Port " + EXPRESS_PORT); | ||
}); | ||
} | ||
|
||
function subscriber(msg, data) { | ||
if(LOGGING_ENABLED){ | ||
console.log("RAW DATA RECEIVED:"); | ||
console.log(msg, data); | ||
console.log("\n"); | ||
} | ||
const dataToSend = prepareDataToSend(data); | ||
|
||
console.log("SENDING data to client:"); | ||
console.log(JSON.stringify(dataToSend) + "\n"); | ||
|
||
if(LOGGING_ENABLED){ | ||
console.log("SENDING data to client:"); | ||
console.log(JSON.stringify(dataToSend) + "\n"); | ||
} | ||
if(resp) { | ||
resp.json(dataToSend); | ||
console.log("Message sent correctly to subscriber. \n"); | ||
if(LOGGING_ENABLED){ | ||
console.log("Message sent correctly to subscriber. \n"); | ||
} | ||
}else{ | ||
console.log("There are no subscribers at the moment, skipping.\n"); | ||
if(LOGGING_ENABLED){ | ||
console.log("There are no subscribers at the moment, skipping.\n"); | ||
} | ||
} | ||
}; | ||
|
||
PubSub.subscribe('eeg', mySubscriber); | ||
//*************EVENTS HERE **********************/ | ||
|
||
if(data.blinkStrength && data.blinkStrength >= BLINK_STRENGTH_THRESHOLD && !recently_takeOffOrLand) { | ||
console.log("************TAKING OFF OR LANDING VIA MIND*************"); | ||
setTimeout(function(){recently_takeOffOrLand = false}, MIN_INTERVAL_EVENT); | ||
recently_takeOffOrLand = true; | ||
drone.takeOffOrLand(); | ||
} | ||
if(data.eSense && data.eSense.meditation && data.eSense.meditation >= MEDITATION_STRENGTH_THRESHOLD && !recently_waved){ | ||
console.log("************CONGRATULATIONS HIGH LEVEL MEDITATION ACQUIRED, WAVING*************"); | ||
setTimeout(function(){recently_waved = false}, MIN_INTERVAL_EVENT); | ||
recently_waved = true; | ||
drone.wave(); | ||
} | ||
if(data.eSense && data.eSense.attention && data.eSense.attention >= ATTENTION_STRENGTH_THRESHOLD){ | ||
console.log("************CONGRATULATIONS HIGH LEVEL ATTENTION ACQUIRED, FIRING LEDS*************"); | ||
drone.fireLeds(); | ||
} | ||
} | ||
|
||
/** | ||
* Parse the raw data from the EEG device and map it to a format compatible with the | ||
* Rickshaw.Graph library (D3.js) | ||
* @param {data received from the EEG device} data | ||
* | ||
*/ | ||
function prepareDataToSend(data) { | ||
const timeNow = Math.floor(new Date().getTime() / 1000); | ||
const dataToSend = [ | ||
var dataToSend = [ | ||
{x : timeNow, y:0}, {x : timeNow, y:0}, {x : timeNow, y:0}, {x : timeNow, y:0}, {x : timeNow, y:0}, {x : timeNow, y:0}, | ||
{x : timeNow, y:0}, {x : timeNow, y:0}, {x : timeNow, y:0}, {x : timeNow, y:0}, {x : timeNow, y:0}, {x : timeNow, y:0} | ||
]; | ||
if (data.eegPower) { | ||
dataToSend[0] = { x: timeNow, y: data.eegPower.delta / 10000 }; | ||
dataToSend[1] = { x: timeNow, y: data.eegPower.theta / 10000 }; | ||
dataToSend[2] = { x: timeNow, y: data.eegPower.lowAlpha / 10000 }; | ||
dataToSend[3] = { x: timeNow, y: data.eegPower.highAlpha / 10000 }; | ||
dataToSend[4] = { x: timeNow, y: data.eegPower.lowBeta / 10000 }; | ||
dataToSend[5] = { x: timeNow, y: data.eegPower.highBeta / 10000 }; | ||
dataToSend[6] = { x: timeNow, y: data.eegPower.lowGamma / 10000 }; | ||
dataToSend[7] = { x: timeNow, y: data.eegPower.highGamma / 10000 }; | ||
dataToSend[0] = { x: timeNow, y: data.eegPower.delta }; | ||
dataToSend[1] = { x: timeNow, y: data.eegPower.theta }; | ||
dataToSend[2] = { x: timeNow, y: data.eegPower.lowAlpha }; | ||
dataToSend[3] = { x: timeNow, y: data.eegPower.highAlpha }; | ||
dataToSend[4] = { x: timeNow, y: data.eegPower.lowBeta }; | ||
dataToSend[5] = { x: timeNow, y: data.eegPower.highBeta }; | ||
dataToSend[6] = { x: timeNow, y: data.eegPower.lowGamma }; | ||
dataToSend[7] = { x: timeNow, y: data.eegPower.highGamma }; | ||
} | ||
if (data.poorSignalLevel) { | ||
dataToSend[8] = { x: timeNow, y: data.poorSignalLevel * 10 }; | ||
dataToSend[8] = { x: timeNow, y: data.poorSignalLevel }; | ||
} | ||
if (data.blinkStrength) { | ||
dataToSend[9] = { x: timeNow, y: data.blinkStrength * 10 }; | ||
if (data.blinkStrength) { //since blinking is an "async event" (does not happen regularly every second) I copy the previous data to not mess up the graph | ||
dataToSend = previousData; | ||
dataToSend[9] = { x: timeNow, y: data.blinkStrength }; | ||
} | ||
if (data.eSense) { | ||
dataToSend[10] = { x: timeNow, y: data.eSense.attention * 10 }; | ||
dataToSend[11] = { x: timeNow, y: data.eSense.meditation * 10 }; | ||
dataToSend[10] = { x: timeNow, y: data.eSense.attention }; | ||
dataToSend[11] = { x: timeNow, y: data.eSense.meditation }; | ||
} | ||
previousData = dataToSend; | ||
return dataToSend; | ||
} | ||
|
||
/** | ||
* Quit the app, safely (callback to event CTRL+C) | ||
*/ | ||
function quit() { | ||
console.log("****************** EXITING ******************\n"); | ||
process.stdin.pause(); | ||
drone.land(); | ||
process.exit(1); | ||
} | ||
|
||
//*********EXPRESS INIT **********/ | ||
/** | ||
* Init the keyboard and bind the keys to drone commands | ||
*/ | ||
function keyboardInitAndBinding(){ | ||
readline.emitKeypressEvents(process.stdin); //keyboard init | ||
if (process.stdin.isTTY) { //keybord init | ||
process.stdin.setRawMode(true); | ||
} | ||
process.stdin.on('keypress', (str, key) => { //keyboard binding | ||
if (key && key.sequence === '-') { | ||
drone.onOffToggle(); | ||
} | ||
if (key && key.sequence === '+') { | ||
eegDevice.onOffToggle(); | ||
} | ||
|
||
if (key && key.ctrl && key.name == 'c') { | ||
quit(); | ||
} | ||
if (key && key.name === 'space') { | ||
drone.takeOffOrLand(); | ||
} | ||
if (key && key.name === 'w') { | ||
drone.wave(); | ||
} | ||
if (key && key.name === 'up') { | ||
drone.front(); | ||
} | ||
if (key && key.name === 'down') { | ||
drone.back(); | ||
} | ||
if (key && key.name === 'left') { | ||
drone.left(); | ||
} | ||
if (key && key.name === 'right') { | ||
drone.right(); | ||
} | ||
if (key && key.shift && key.name == 'up') { | ||
drone.up(); | ||
} | ||
if (key && key.shift && key.name == 'down') { | ||
drone.down(); | ||
} | ||
if (key && key.name === 'i') { | ||
drone.yaw_left(); | ||
} | ||
if (key && key.name === 'o') { | ||
drone.yaw_right(); | ||
} | ||
if (key && key.name === 'x') { | ||
drone.calibrate(); | ||
} | ||
if (key && key.name === 'z') { | ||
drone.fireLeds(); | ||
} | ||
}); | ||
} | ||
|
||
function startExpress() { | ||
const EXPRESS_PORT = 8080; | ||
const app = express(); | ||
// app.use(express.static(path.join(__dirname,'public'))); | ||
|
||
//expose public folder | ||
app.use(express.static('public')) | ||
|
||
//expose S.S.E. endpoint for live EEG data stream | ||
app.get('/liveEEGData', sse, function (req, res) { | ||
resp = res; // expose response to global scope | ||
}); | ||
|
||
app.listen(EXPRESS_PORT, function () { | ||
console.log("Application running at Port " + EXPRESS_PORT); | ||
}); | ||
} | ||
//****************************** MAIN ******************************/ | ||
|
||
|
||
if(eegDevice.isEnabled()){ //nb. drone does not require an explict connect() method | ||
eegClient.connect(); | ||
} | ||
PubSub.subscribe('eeg', subscriber); //subscribe "subscriber" to eeg topic | ||
keyboardInitAndBinding(); //init keyboard and bind drone commands to it | ||
startExpress(); //start Express app |
Oops, something went wrong.