import processing.serial.*; import controlP5.*; import javax.sound.midi.*; ControlP5 controlP5; ControlFont font; Serial serial; int lf = 10; // ASCII linefeed Channel[] channels = new Channel[11]; Monitor[] monitors = new Monitor[10]; Graph graph; ConnectionLight connectionLight; int packetCount = 0; int globalMax; String scaleMode; String buffer=""; byte signalQuality, attention, meditation, eegPowerLength,latestByte; long[] eegPower= new long[8]; boolean hasPower; boolean inPacket; byte lastByte; byte packetIndex; byte packetLength; byte checksum; byte checksumAccumulator; char[] latestError = new char[23]; byte[] packetData = new byte[32]; boolean freshPacket; void setup() { size(1024, 768); smooth(); // Set up the knobs and dials controlP5 = new ControlP5(this); controlP5.setColorLabel(color(0)); // controlP5.setColorValue(color(0)); controlP5.setColorBackground(color(0)); // controlP5.setColorForeground(color(130)); // controlP5.setColorActive(color(0)); font = new ControlFont(createFont("DIN-MediumAlternate", 12), 12); // Create each channel println(Serial.list()); byte testing; for (int i=0; i<255; i++) { testing=(byte)i; print(testing+" "); } serial = new Serial(this, Serial.list()[3], 9600); //serial.bufferUntil(-86); // Creat the channel objects // yellow to purple and then the space in between, grays for the alphas channels[0] = new Channel("Signal Quality", color(0), ""); channels[1] = new Channel("Attention", color(100), ""); channels[2] = new Channel("Meditation", color(50), ""); channels[3] = new Channel("Delta", color(219, 211, 42), "Dreamless Sleep"); channels[4] = new Channel("Theta", color(245, 80, 71), "Drowsy"); channels[5] = new Channel("Low Alpha", color(237, 0, 119), "Relaxed"); channels[6] = new Channel("High Alpha", color(212, 0, 149), "Relaxed"); channels[7] = new Channel("Low Beta", color(158, 18, 188), "Alert"); channels[8] = new Channel("High Beta", color(116, 23, 190), "Alert"); channels[9] = new Channel("Low Gamma", color(39, 25, 159), "???"); channels[10] = new Channel("High Gamma", color(23, 26, 153), "???"); // Manual override for a couple of limits. channels[0].minValue = 0; channels[0].maxValue = 200; channels[1].minValue = 0; channels[1].maxValue = 100; channels[2].minValue = 0; channels[2].maxValue = 100; channels[0].allowGlobal = false; channels[1].allowGlobal = false; channels[2].allowGlobal = false; // Set up the monitors, skip the signal quality for (int i = 0; i < monitors.length; i++) { monitors[i] = new Monitor(channels[i + 1], i * (width / 10), height / 2, width / 10, height / 2, this); } monitors[monitors.length - 1].w += width % monitors.length; // Set up the graph graph = new Graph(0, 0, width, height / 2); connectionLight = new ConnectionLight(width - 98, 10, 20, this); globalMax = 0; } void draw() { // find the global max if(scaleMode == "Global") { if(channels.length > 3) { for(int i = 3; i < channels.length; i++) { if (channels[i].maxValue > globalMax) globalMax = channels[i].maxValue; } } } background(255); graph.update(); graph.draw(); connectionLight.update(); connectionLight.draw(); for (int i = 0; i < monitors.length; i++) { monitors[i].update(); monitors[i].draw(); } } int convert(byte yay) { if ((int)yay>127) return (128-(int)yay)+128; else return (int) yay; } void clearPacket() { for (byte i = 0; i < 32; i++) { packetData[i] = 0; } } void clearEegPower() { // Zero the power bands. for(byte i = 0; i < 8; i++) { eegPower[i] = 0; } } boolean parsePacket() { hasPower = false; clearEegPower(); // clear the eeg power to make sure we're honest about missing values... null would be better than 0. for (byte i = 0; i < packetLength; i++) { switch (packetData[i]) { case 2: signalQuality = packetData[++i]; break; case 4: attention = packetData[++i]; break; case 5: meditation = packetData[++i]; break; case -125: eegPowerLength = packetData[++i]; for(int j = 0; j < 8; j++) { try {eegPower[j] = (convert(packetData[++i]) << 16) | (convert(packetData[++i]) << 8) | convert(packetData[++i]);} catch (Exception e) { print (e);} } hasPower = true; break; default: return false; } } return true; } boolean update(Serial p) { if (p.available()>0) { latestByte = (byte)serial.read(); // Build a packet if we know we're and not just listening for sync bytes. if (inPacket) { // First byte after the sync bytes is the length of the upcoming packet. if (packetIndex == 0) { packetLength = latestByte; // Catch error if packet is too long if (packetLength > 32) { // Packet exceeded max length // Send an error print(latestError + "ERROR: Packet too long"); inPacket = false; } } else if (packetIndex <= packetLength) { // Run of the mill data bytes. // Print them here // Store the byte in an array for parsing later. try {packetData[packetIndex - 1] = latestByte;} catch (Exception e){ print("broked"); } // Keep building the checksum. checksumAccumulator += latestByte; } else if (packetIndex > packetLength) { // We're at the end of the data payload. // Check the checksum. checksum = latestByte; checksumAccumulator =(byte) (255 - checksumAccumulator); // Do they match? if (checksum == checksumAccumulator) { // Parse the data. parsePacker() returns true if parsing succeeds. if (parsePacket()) { freshPacket = true; } else { // Parsing failed, send an error. print(latestError + "ERROR: Could not parse"); // good place to print the packet if debugging } } else { // Checksum mismatch, send an error. print(latestError + "ERROR: Checksum"); // good place to print the packet if debugging } // End of packet // Reset, prep for next packet inPacket = false; } packetIndex++; } // Look for the start of the packet if ((latestByte == -86) && (lastByte == -86) && !inPacket) { // Start of packet inPacket = true; packetIndex = 0; packetLength = 0; // Technically not necessarry. checksum = 0; // Technically not necessary. checksumAccumulator = 0; //clearPacket(); // Zeros the packet array, technically not necessarry. //clearEegPower(); // Zeros the EEG power. Necessary if hasPower turns false... better off on the gettter end? } // Keep track of the last byte so we can find the sync byte pairs. lastByte = latestByte; } if(freshPacket) { freshPacket = false; return true; } else { return false; } } String readCSV() { // spit out a big string? // find out how big this really needs to be // should be popped off the stack once it goes out of scope? // make the character array as small as possible String yay; if(hasPower) { yay=Integer.toString(signalQuality)+","+attention+","+meditation+","; for (int i=0; i<7; i++) yay=yay+eegPower[i]+","; yay=yay+eegPower[7]; return yay; } else { yay=Integer.toString(signalQuality)+","+attention+","+meditation; return yay; } } void serialEvent(Serial p) { update(p); String[] incomingValues = split(readCSV(), ','); println(incomingValues); // Add the data to the logs if (incomingValues.length > 1) { packetCount++; // Wait till the third packet or so to start recording to avoid initialization garbage. if(packetCount > 3) { for (int i = 0; i < incomingValues.length; i++) { int newValue = Integer.parseInt(incomingValues[i].trim()); // Zero the EEG power values if we don't have a signal. // Can be useful to leave them in for development. //if((Integer.parseInt(incomingValues[0]) == 200) && (i > 2)) newValue = 0; channels[i].addDataPoint(newValue); } } } } // Extend core's Map function to the Long datatype. long mapLong(long x, long in_min, long in_max, long out_min, long out_max) { return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min; } long constrainLong(long value, long min_value, long max_value) { if(value > max_value) return max_value; if(value < min_value) return min_value; return value; }