Skip to content

Instantly share code, notes, and snippets.

@CharStiles
Last active December 16, 2019 23:21
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save CharStiles/d426d3775b1e02b7627b5eda70628193 to your computer and use it in GitHub Desktop.
Save CharStiles/d426d3775b1e02b7627b5eda70628193 to your computer and use it in GitHub Desktop.
/*
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