Last active
December 16, 2019 23:21
-
-
Save CharStiles/d426d3775b1e02b7627b5eda70628193 to your computer and use it in GitHub Desktop.
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
/* | |
mix of the code provided by pulse to visulize BPM, and random code on the internet and some code that i wrote | |
THIS CODE PROVIDED AS IS, WITH NO CLAIMS OF FUNCTIONALITY OR EVEN IF IT WILL WORK | |
*/ | |
import java.lang.management.ManagementFactory; | |
import java.lang.management.OperatingSystemMXBean; | |
import java.lang.reflect.Method; | |
import java.lang.reflect.Modifier; | |
import java.awt.Robot; | |
import processing.serial.*; // serial library lets us talk to Arduino | |
PFont font; | |
PFont portsFont; | |
Scrollbar scaleBar; | |
Serial port; | |
int Sensor; // HOLDS PULSE SENSOR DATA FROM ARDUINO | |
int IBI; // HOLDS TIME BETWEN HEARTBEATS FROM ARDUINO | |
int BPM; // HOLDS HEART RATE VALUE FROM ARDUINO | |
int[] RawY; // HOLDS HEARTBEAT WAVEFORM DATA BEFORE SCALING | |
int[] ScaledY; // USED TO POSITION SCALED HEARTBEAT WAVEFORM | |
int[] rate; // USED TO POSITION BPM DATA WAVEFORM | |
float zoom; // USED WHEN SCALING PULSE WAVEFORM TO PULSE WINDOW | |
float offset; // USED WHEN SCALING PULSE WAVEFORM TO PULSE WINDOW | |
color eggshell = color(255, 253, 248); | |
int heart = 0; // This variable times the heart image 'pulse' on screen | |
// THESE VARIABLES DETERMINE THE SIZE OF THE DATA WINDOWS | |
int PulseWindowWidth = 490; | |
int PulseWindowHeight = 512; | |
int BPMWindowWidth = 180; | |
int BPMWindowHeight = 340; | |
boolean beat = false; // set when a heart beat is detected, then cleared when the BPM graph is advanced | |
// SERIAL PORT STUFF TO HELP YOU FIND THE CORRECT SERIAL PORT | |
String serialPort; | |
String[] serialPorts = new String[Serial.list().length]; | |
boolean serialPortFound = false; | |
Radio[] button = new Radio[Serial.list().length*2]; | |
int numPorts = serialPorts.length; | |
boolean refreshPorts = false; | |
float computerHeartbeat; | |
double[] compHeartbeats; | |
int avg = 5; // for averaging across heatbeats | |
int place = 0; | |
Robot rbt; | |
private void printUsage() { | |
OperatingSystemMXBean operatingSystemMXBean = ManagementFactory.getOperatingSystemMXBean(); | |
for (Method method : operatingSystemMXBean.getClass().getDeclaredMethods()) { | |
method.setAccessible(true); | |
if (method.getName().startsWith("get") | |
&& Modifier.isPublic(method.getModifiers())) { | |
Object value; | |
try { | |
value = method.invoke(operatingSystemMXBean); | |
} catch (Exception e) { | |
value = e; | |
} | |
if (method.getName() == "getSystemCpuLoad"){ | |
if((java.lang.Double)value >= 0.0){ | |
compHeartbeats[place % avg] = (java.lang.Double)value; | |
place++; | |
//println(computerHeartbeat); | |
} | |
} | |
} | |
} | |
} | |
void setup() { | |
size(700, 600); // Stage size | |
frameRate(100); | |
try { | |
rbt = new Robot(); | |
} catch(Exception e) { | |
e.printStackTrace(); | |
} | |
font = loadFont("GlacialIndifference-Bold-48.vlw"); | |
textFont(font,16); | |
textAlign(CENTER); | |
rectMode(CENTER); | |
ellipseMode(CENTER); | |
// Scrollbar constructor inputs: x,y,width,height,minVal,maxVal | |
scaleBar = new Scrollbar (400, 575, 180, 12, 0.5, 1.0); // set parameters for the scale bar | |
RawY = new int[PulseWindowWidth]; // initialize raw pulse waveform array | |
ScaledY = new int[PulseWindowWidth]; // initialize scaled pulse waveform array | |
rate = new int [BPMWindowWidth]; // initialize BPM waveform array | |
compHeartbeats = new double[avg]; | |
zoom = 0.75; // initialize scale of heartbeat window | |
// set the visualizer lines to 0 | |
resetDataTraces(); | |
background(0); | |
// DRAW OUT THE PULSE WINDOW AND BPM WINDOW RECTANGLES | |
drawDataWindows(); | |
// GO FIND THE ARDUINO | |
text("Select Your Serial Port",245,30); | |
listAvailablePorts(); | |
for (int i = 0; i < avg; i++){ | |
compHeartbeats[i] = 0; | |
} | |
} | |
void draw() { | |
printUsage(); | |
if(serialPortFound){ | |
// ONLY RUN THE VISUALIZER AFTER THE PORT IS CONNECTED | |
background(0); | |
noStroke(); | |
drawDataWindows(); | |
drawPulseWaveform(); | |
drawBPMwaveform(); | |
// PRINT THE DATA AND VARIABLE VALUES | |
fill(0,255,0); | |
// get ready to print text | |
textFont(font,16); | |
text("match your computer heartbeat with your human heartbeat",245,30); // tell them what you are | |
//text("IBI " + IBI + "mS",600,585); // print the time between heartbeats in mS | |
textFont(font,32); | |
text(BPM + " BPM",600,200); // print the Beats Per Minute | |
// show the current scale of Pulse Window | |
// DO THE SCROLLBAR THINGS | |
scaleBar.update (mouseX, mouseY); | |
scaleBar.display(); | |
for (int i =0; i < avg; i++){ | |
computerHeartbeat += compHeartbeats[i]; | |
} | |
computerHeartbeat/=avg; | |
float humanHeartbeat = (((float)BPM) - 20)/100.0; | |
println("human heartbeat"+humanHeartbeat); | |
println("computer heartbeat"+computerHeartbeat); | |
if (abs(humanHeartbeat - computerHeartbeat) < 0.25){ | |
println("YES"); | |
} | |
else if(frameCount > 100){ | |
rbt.mouseMove(0, 0); | |
} | |
textFont(font,32); | |
text(computerHeartbeat + nf(zoom,1,2), 150, 585); | |
} else { // SCAN BUTTONS TO FIND THE SERIAL PORT | |
autoScanPorts(); | |
if(refreshPorts){ | |
refreshPorts = false; | |
drawDataWindows(); | |
listAvailablePorts(); | |
} | |
for(int i=0; i<numPorts+1; i++){ | |
button[i].overRadio(mouseX,mouseY); | |
button[i].displayRadio(); | |
} | |
} | |
} //end of draw loop | |
void drawDataWindows(){ | |
// DRAW OUT THE PULSE WINDOW AND BPM WINDOW RECTANGLES | |
noStroke(); | |
fill(0); // color for the window background | |
rect(255,height/2,PulseWindowWidth,PulseWindowHeight); | |
rect(600,385,BPMWindowWidth,BPMWindowHeight); | |
} | |
void drawPulseWaveform(){ | |
// DRAW THE PULSE WAVEFORM | |
// prepare pulse data points | |
RawY[RawY.length-1] = (1023 - Sensor) - 212; // place the new raw datapoint at the end of the array | |
zoom = scaleBar.getPos(); // get current waveform scale value | |
offset = map(zoom,0.5,1,150,0); // calculate the offset needed at this scale | |
for (int i = 0; i < RawY.length-1; i++) { // move the pulse waveform by | |
RawY[i] = RawY[i+1]; // shifting all raw datapoints one pixel left | |
float dummy = RawY[i] * zoom + offset; // adjust the raw data to the selected scale | |
ScaledY[i] = constrain(int(dummy),44,556); // transfer the raw data array to the scaled array | |
} | |
stroke(0,255,0); // red is a good color for the pulse waveform | |
noFill(); | |
beginShape(); // using beginShape() renders fast | |
for (int x = 1; x < ScaledY.length-1; x++) { | |
vertex(x+10, ScaledY[x]); //draw a line connecting the data points | |
} | |
endShape(); | |
} | |
void drawBPMwaveform(){ | |
// DRAW THE BPM WAVE FORM | |
// first, shift the BPM waveform over to fit then next data point only when a beat is found | |
if (beat == true){ // move the heart rate line over one pixel every time the heart beats | |
beat = false; // clear beat flag (beat flag waset in serialEvent tab) | |
for (int i=0; i<rate.length-1; i++){ | |
rate[i] = rate[i+1]; // shift the bpm Y coordinates over one pixel to the left | |
} | |
// then limit and scale the BPM value | |
BPM = min(BPM,200); // limit the highest BPM value to 200 | |
float dummy = map(BPM,0,200,555,215); // map it to the heart rate window Y | |
rate[rate.length-1] = int(dummy); // set the rightmost pixel to the new data point value | |
} | |
// GRAPH THE HEART RATE WAVEFORM | |
stroke(0,250,0); // color of heart rate graph | |
strokeWeight(2); // thicker line is easier to read | |
noFill(); | |
beginShape(); | |
for (int i=0; i < rate.length-1; i++){ // variable 'i' will take the place of pixel x position | |
vertex(i+510, rate[i]); // display history of heart rate datapoints | |
} | |
endShape(); | |
} | |
void listAvailablePorts(){ | |
println(Serial.list()); // print a list of available serial ports to the console | |
serialPorts = Serial.list(); | |
fill(0,255,0); | |
textFont(font,16); | |
textAlign(LEFT); | |
// set a counter to list the ports backwards | |
int yPos = 0; | |
int xPos = 35; | |
for(int i=serialPorts.length-1; i>=0; i--){ | |
button[i] = new Radio(xPos, 95+(yPos*20),12,color(180),color(80),color(255),i,button); | |
text(serialPorts[i],xPos+15, 100+(yPos*20)); | |
yPos++; | |
if(yPos > height-30){ | |
yPos = 0; xPos+=200; | |
} | |
} | |
int p = numPorts; | |
fill(233,0,0); | |
button[p] = new Radio(35, 95+(yPos*20),12,color(180),color(80),color(255),p,button); | |
text("Refresh Serial Ports List",50, 100+(yPos*20)); | |
textFont(font); | |
textAlign(CENTER); | |
} | |
void autoScanPorts(){ | |
if(Serial.list().length != numPorts){ | |
if(Serial.list().length > numPorts){ | |
println("New Ports Opened!"); | |
int diff = Serial.list().length - numPorts; // was serialPorts.length | |
serialPorts = expand(serialPorts,diff); | |
numPorts = Serial.list().length; | |
}else if(Serial.list().length < numPorts){ | |
println("Some Ports Closed!"); | |
numPorts = Serial.list().length; | |
} | |
refreshPorts = true; | |
return; | |
} | |
} | |
void resetDataTraces(){ | |
for (int i=0; i<rate.length; i++){ | |
rate[i] = 555; // Place BPM graph line at bottom of BPM Window | |
} | |
for (int i=0; i<RawY.length; i++){ | |
RawY[i] = height/2; // initialize the pulse window data line to V/2 | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment