Processing code:
//Processing
import processing.serial.*;
PFont font;
Serial port;
int numSensors = 2;
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[][] RawPPG; // HOLDS HEARTBEAT WAVEFORM DATA BEFORE SCALING
int[][] ScaledPPG; // USED TO POSITION SCALED HEARTBEAT WAVEFORM
int[][] ScaledBPM; // USED TO POSITION BPM DATA WAVEFORM
float offset; // USED WHEN SCALING PULSE WAVEFORM TO PULSE WINDOW
color eggshell = color(255, 253, 248);
int heart[]; // USED TO TIME THE HEART 'PULSE'
float PTT;
int PulseWindowWidth; // = 490;
int PulseWindowHeight; // = 512;
int PulseWindowX;
int PulseWindowY[];
int BPMWindowWidth; // = 180;
int BPMWindowHeight; // = 340;
int BPMWindowX;
int BPMWindowY[];
int spacer = 10;
boolean beat[]; // 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;
void setup() {
size(900, 725); // Stage size
frameRate(100);
font = loadFont("Arial-BoldMT-24.vlw");
textFont(font);
textAlign(CENTER);
rectMode(CORNER);
ellipseMode(CENTER);
// Display Window Setup
PulseWindowWidth = 490;
PulseWindowHeight = 640/numSensors;
PulseWindowX = 10;
PulseWindowY = new int [numSensors];
for(int i=0; i<numSensors; i++){
PulseWindowY[i] = 43 + (PulseWindowHeight * i);
if(i > 0) PulseWindowY[i]+=spacer*i;
}
BPMWindowWidth = 180;
BPMWindowHeight = PulseWindowHeight;
BPMWindowX = PulseWindowX + PulseWindowWidth + 10;
BPMWindowY = new int [numSensors];
for(int i=0; i<numSensors; i++){
BPMWindowY[i] = 43 + (BPMWindowHeight * i);
if(i > 0) BPMWindowY[i]+=spacer*i;
}
heart = new int[numSensors];
beat = new boolean[numSensors];
// Data Variables Setup
Sensor = new int[numSensors]; // HOLDS PULSE SENSOR DATA FROM ARDUINO
IBI = new int[numSensors]; // HOLDS TIME BETWEN HEARTBEATS FROM ARDUINO
BPM = new int[numSensors]; // HOLDS HEART RATE VALUE FROM ARDUINO
RawPPG = new int[numSensors][PulseWindowWidth];
ScaledPPG = new int[numSensors][PulseWindowWidth];
ScaledBPM = new int [numSensors][BPMWindowWidth];
// set the visualizer lines to 0
resetDataTraces();
background(0);
noStroke();
drawDataWindows();
drawHeart();
fill(eggshell);
text("Select Your Serial Port",245,30);
listAvailablePorts();
}
void draw() {
if(serialPortFound){
background(0);
drawDataWindows();
drawPulseWaveform();
drawBPMwaveform();
drawHeart();
printDataToScreen();
} else {
autoScanPorts();
if(refreshPorts){
refreshPorts = false;
drawDataWindows();
drawHeart();
listAvailablePorts();
}
for(int i=0; i<numPorts+1; i++){
button[i].overRadio(mouseX,mouseY);
button[i].displayRadio();
}
}
}
void drawDataWindows(){
noStroke();
fill(eggshell);
for(int i=0; i<numSensors; i++){
rect(PulseWindowX, PulseWindowY[i], PulseWindowWidth, PulseWindowHeight);
rect(BPMWindowX, BPMWindowY[i], BPMWindowWidth, BPMWindowHeight);
}
}
void drawPulseWaveform(){
for (int i=0; i<numSensors; i++) {
RawPPG[i][PulseWindowWidth-1] = (1023 - Sensor[i]);
for (int j = 0; j < PulseWindowWidth-1; j++) {
RawPPG[i][j] = RawPPG[i][j+1];
float dummy = RawPPG[i][j] * 0.625/numSensors;
offset = float(PulseWindowY[i]);
ScaledPPG[i][j] = int(dummy) + int(offset);
}
stroke(250, 0, 0);
noFill();
beginShape();
for (int x = 1; x < PulseWindowWidth-1; x++) {
vertex(x+10, ScaledPPG[i][x]);
}
endShape();
}
}
void drawBPMwaveform(){
for (int i=0; i<numSensors; i++) {
if (beat[i] == true) {
beat[i] = false;
for (int j=0; j<BPMWindowWidth-1; j++) {
ScaledBPM[i][j] = ScaledBPM[i][j+1];
}
// then limit and scale the BPM value
BPM[i] = constrain(BPM[i], 0, 200);
float dummy = map(BPM[i], 0, 200, BPMWindowY[i]+BPMWindowHeight, BPMWindowY[i]);
ScaledBPM[i][BPMWindowWidth-1] = int(dummy);
}
}
// GRAPH THE HEART RATE WAVEFORM
stroke(250, 0, 0);
strokeWeight(2);
noFill();
for (int i=0; i<numSensors; i++) {
beginShape();
for (int j=0; j < BPMWindowWidth; j++) {
vertex(j+BPMWindowX, ScaledBPM[i][j]);
}
endShape();
}
}
void drawHeart(){
// DRAW THE HEART AND MAYBE MAKE IT BEAT
fill(250,0,0);
stroke(250,0,0);
int bezierZero = 0;
for(int i=0; i<numSensors; i++){
heart[i]--;
heart[i] = max(heart[i], 0);
if (heart[i] > 0) {
strokeWeight(8);
}
smooth();
bezier(width-100, bezierZero+70, width-20, bezierZero, width, bezierZero+160, width-100, bezierZero+170);
bezier(width-100, bezierZero+70, width-190, bezierZero, width-200, bezierZero+160, width-100, bezierZero+170);
strokeWeight(1);
bezierZero += BPMWindowHeight+spacer;
}
}
void listAvailablePorts(){
println(Serial.list());
serialPorts = Serial.list();
fill(0);
textFont(font,16);
textAlign(LEFT);
// set a counter to list the ports backwards
int yPos = 0;
for(int i=numPorts-1; i>=0; i--){
button[i] = new Radio(35, 95+(yPos*20),12,color(180),color(80),color(255),i,button);
text(serialPorts[i],50, 100+(yPos*20));
yPos++;
}
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;
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<numSensors; i++) {
BPM[i] = 0;
for(int j=0; j<BPMWindowWidth; j++){
ScaledBPM[i][j] = BPMWindowY[i] + BPMWindowHeight;
}
}
for (int i=0; i<numSensors; i++) {
Sensor[i] = 512;
for (int j=0; j<PulseWindowWidth; j++) {
RawPPG[i][j] = 1024 - Sensor[i];
}
}
}
void printDataToScreen(){
fill(eggshell);
text("Blood Pressure Monitor", 300, 30);
for (int i=0; i<numSensors; i++) {
text("Sensor # " + (i+1), 800, BPMWindowY[i] + 220);
text(BPM[i] + " BPM", 800, BPMWindowY[i] +185);// 215
text("IBI " + IBI[i] + "ms", 800, BPMWindowY[i] + 160);// 245
}
text("PTT: " + PTT + "ms", 800, BPMWindowY[1] - 50);
}