Description
Create a physically interactive system of your choice that relies on a multimedia computer for some sort of processing or data analysis. The Final should use BOTH Processing AND Arduino. Your focus should be on careful and timely sensing of the relevant actions of the person or people that you’re designing this for, and on clear, prompt, and effective responses. Any interactive system is going to involve systems of listening, thinking, and speaking from both parties. Whether it involves one cycle or many, the exchange should be engaging. You may work alone or in pairs.
Project
I started this project by working with a pulse sensor without thinking about what I would end up with. I progressed by displaying a heartbeat on the screen by using the data I received. I also wanted to work with LEDs, I attached the LEDs with the breadboard. I used two LEDs that turn on and off according to the pulse rate. I also checked the timings between heartbeats by subtracting the last-current, the variable I have used is TBH(time between heartbeats). I also made the heart pump by increasing the stroke weight. You can also use “S” or “s” to save the heartbeat in the folder where the code is saved and later you can compare the heartbeat. To make the pulse rate monitor more interesting, I added some questions to the screen to see how the heartbeat changed with respect to the question, which can be used as a lie detection test. If someone wanted to reset their heartbeats, they can press ‘r’ to reset it, and the monitor displays no heartbeat until some other person uses the sensor. For this project, I used two sensors, out of which one had technical problems. So, I couldn’t use that. But, the idea was that I would compare two people’s heartbeats by asking them questions.
I took some shots during the exhibition of people testing out the monitor and playing the yes/no question game.
The LEDs were covered with a white box that looked like an artificial heart because it would beat every time your pulse was beating. The circuit looked like this:
Certain challenges:-
- It took me some time to figure out how the sensor worked because I have never worked with a pulse sensor before.
- it was hard to display the pulse wave first, then I found some resources to make me understand the concept.
- it was hard to add further things to the project to make it more useful.
The code is following:-
Arduino
#define USE_ARDUINO_INTERRUPTS true #include <PulseSensorPlayground.h> const int OUTPUT_TYPE = PROCESSING_VISUALIZER; const int PULSE_SENSOR_COUNT = 2; const int PULSE_INPUT0 = A0; const int PULSE_BLINK0 = 13; //led const int PULSE_FADE0 = 5; const int PULSE_INPUT1 = A1; const int PULSE_BLINK1 = 12; const int PULSE_FADE1 = 11; const int THRESHOLD = 550; // to avoid noise when idle PulseSensorPlayground pulseSensor(PULSE_SENSOR_COUNT); void setup() { Serial.begin(250000); pulseSensor.analogInput(PULSE_INPUT0, 0); pulseSensor.blinkOnPulse(PULSE_BLINK0, 0); pulseSensor.fadeOnPulse(PULSE_FADE0, 0); pulseSensor.analogInput(PULSE_INPUT1, 1); pulseSensor.blinkOnPulse(PULSE_BLINK1, 1); pulseSensor.fadeOnPulse(PULSE_FADE1, 1); pulseSensor.setSerial(Serial); pulseSensor.setOutputType(OUTPUT_TYPE); pulseSensor.setThreshold(THRESHOLD); if (!pulseSensor.begin()) { for (;;) { digitalWrite(PULSE_BLINK0, LOW); delay(50); digitalWrite(PULSE_BLINK0, HIGH); delay(50); } } } void loop() { delay(20); pulseSensor.outputSample(); for (int i = 0; i < PULSE_SENSOR_COUNT; ++i) { if (pulseSensor.sawStartOfBeat(i)) { pulseSensor.outputBeat(i); } } }
Processing
import processing.sound.*; SoundFile file; import processing.serial.*; PFont font; Serial port; int numSensors = 2; //variable that holds number of sensors //the varible given below holds certain information int[] Sensor; int[] TBH; int[] BPM; int[][] RawPPG; int[][] ScaledPPG; int[][] ScaledBPM; float offset; color bgcolor = color(171,219,227); int heart[]; //when timing the pulse //determine the size int pulseWidth; int pulseHeight; int pulseX; int pulseY[]; int bpmWidth; int bpmHeight; int bpmX; int bpmY[]; int spacer = 10; boolean beat[]; //boolean to check if heart beat detected //find 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; int duration = 1000; int pressTime; void setup() { fullScreen(); frameRate(100); font = loadFont("Arial-BoldMT-24.vlw"); file = new SoundFile(this, "heart.mp3"); textFont(font); textAlign(CENTER); rectMode(CORNER); ellipseMode(CENTER); pulseWidth = width-520; pulseHeight = 1080/numSensors; pulseX = 10; pulseY = new int [numSensors]; for(int i=0; i<numSensors; i++){ pulseY[i] = 43 + (pulseHeight * i); if(i > 0) pulseY[i]+=spacer*i; } bpmWidth = 300; bpmHeight = pulseHeight; bpmX = pulseX + pulseWidth + 10; bpmY = new int [numSensors]; for(int i=0; i<numSensors; i++){ bpmY[i] = 43 + (bpmHeight * i); if(i > 0) bpmY[i]+=spacer*i; } heart = new int[numSensors]; beat = new boolean[numSensors]; // Data Variables Setup Sensor = new int[numSensors]; TBH = new int[numSensors]; BPM = new int[numSensors]; RawPPG = new int[numSensors][pulseWidth]; ScaledPPG = new int[numSensors][pulseWidth]; ScaledBPM = new int [numSensors][bpmWidth]; //setting lines to 0 resetDataTraces(); background(0); noStroke(); drawDataWindows(); drawHeart(); fill(bgcolor); text("Select Your Serial Port",245,30); listAvailablePorts(); } void draw() { if(serialPortFound){ // run only when port connected background(0); drawDataWindows(); drawPulseWaveform(); drawBpmWave(); drawHeart(); printDataToScreen(); if(spacePressed){ text("The following game involves Yes/No questions.\n Press 1 to view the first question.", 670, 500); } if(onePressed){ text("Are you happy with your life?", 700, 500); } if(twoPressed){ text("Have you ever truly been in love?", 700, 500); } if(threePressed){ text("Do you think you're successful in life?", 700, 500); } if(fourPressed){ text("Have you ever done something unforgivable?", 750, 500); } if(fivePressed){ text("Would you save your mother over 17 other random people?", 700, 500); } if(sixPressed){ text("Are you a good person?", 700, 500); } if(sevenPressed){ text("If you died tomorrow, would you have regrets?", 750, 500); } } else { autoScanPorts(); //scan to find port 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(bgcolor); for(int i=0; i<numSensors; i++){ rect(pulseX, pulseY[i], pulseWidth, pulseHeight); rect(bpmX, bpmY[i], bpmWidth, bpmHeight); } } void drawPulseWaveform(){ for (int i=0; i<numSensors; i++) { RawPPG[i][pulseWidth-1] = (1023 - Sensor[i]); for (int j = 0; j < pulseWidth-1; j++) { RawPPG[i][j] = RawPPG[i][j+1]; float dummy = RawPPG[i][j] * 0.625/numSensors; offset = float(pulseY[i]); ScaledPPG[i][j] = int(dummy) + int(offset); } stroke(250, 0, 0); noFill(); beginShape(); for (int x = 1; x < pulseWidth-1; x++) { vertex(x+10, ScaledPPG[i][x]); } endShape(); } } void drawBpmWave(){ //draw bpm wave for (int i=0; i<numSensors; i++) { if (beat[i] == true) { file.play(); beat[i] = false; for (int j=0; j<bpmWidth-1; j++) { ScaledBPM[i][j] = ScaledBPM[i][j+1]; } BPM[i] = constrain(BPM[i], 0, 200); // limit the highest BPM value to 200 float dummy = map(BPM[i], 0, 200, bpmY[i]+bpmHeight, bpmY[i]); ScaledBPM[i][bpmWidth-1] = int(dummy); } } // graph heart rate in small window stroke(250, 0, 0); // color of heart rate graph strokeWeight(2); // thicker line is easier to read noFill(); for (int i=0; i<numSensors; i++) { beginShape(); for (int j=0; j < bpmWidth; j++) { vertex(j+bpmX, ScaledBPM[i][j]); } endShape(); } } void drawHeart(){ //make the heart and 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(); //draw hearts 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); // reset the strokeWeight for next time bezierZero += bpmHeight+spacer; } } void listAvailablePorts(){ //println(Serial.list()); serialPorts = Serial.list(); fill(0); textFont(font,16); textAlign(LEFT); 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<bpmWidth; j++){ ScaledBPM[i][j] = bpmY[i] + bpmHeight; } } for (int i=0; i<numSensors; i++) { Sensor[i] = 512; for (int j=0; j<pulseWidth; j++) { RawPPG[i][j] = 1024 - Sensor[i]; } } } void printDataToScreen(){ fill(255); text("Pulse Sensor to compare heart beats", 245, 30); for (int i=0; i<numSensors; i++) { text("Sensor " + (i+1), 1700, bpmY[i] + 220); text(BPM[i] + " BPM", 1700, bpmY[i] +185); text("TBH " + TBH[i] + "mS", 1700, bpmY[i] + 160); } }
class Radio { int _x,_y; int size, dotSize; color baseColor, overColor, pressedColor; boolean over, pressed; int me; Radio[] radios; Radio(int xp, int yp, int s, color b, color o, color p, int m, Radio[] r) { _x = xp; _y = yp; size = s; dotSize = size - size/3; baseColor = b; overColor = o; pressedColor = p; radios = r; me = m; } boolean pressRadio(float mx, float my){ if (dist(_x, _y, mx, my) < size/2){ pressed = true; for(int i=0; i<numPorts+1; i++){ if(i != me){ radios[i].pressed = false; } } return true; } else { return false; } } boolean overRadio(float mx, float my){ if (dist(_x, _y, mx, my) < size/2){ over = true; for(int i=0; i<numPorts+1; i++){ if(i != me){ radios[i].over = false; } } return true; } else { over = false; return false; } } void displayRadio(){ noStroke(); fill(baseColor); ellipse(_x,_y,size,size); if(over){ fill(overColor); ellipse(_x,_y,dotSize,dotSize); } if(pressed){ fill(pressedColor); ellipse(_x,_y,dotSize,dotSize); } } } //understood some of these concepts throught additional information on web
boolean spacePressed; boolean onePressed; boolean twoPressed; boolean threePressed; boolean fourPressed; boolean fivePressed; boolean sixPressed; boolean sevenPressed; void mousePressed(){ if(!serialPortFound){ for(int i=0; i<=numPorts; i++){ if(button[i].pressRadio(mouseX,mouseY)){ if(i == numPorts){ if(Serial.list().length > numPorts){ println("New Ports Opened!"); int diff = Serial.list().length - numPorts; serialPorts = expand(serialPorts,diff); //button = (Radio[]) expand(button,diff); numPorts = Serial.list().length; }else if(Serial.list().length < numPorts){ println("Some Ports Closed!"); numPorts = Serial.list().length; }else if(Serial.list().length == numPorts){ return; } refreshPorts = true; return; }else try{ port = new Serial(this, Serial.list()[i], 250000); delay(1000); println(port.read()); port.clear(); port.bufferUntil('\n'); serialPortFound = true; } catch(Exception e){ println("Couldn't open port " + Serial.list()[i]); fill(255,0,0); textFont(font,16); textAlign(LEFT); text("Couldn't open port " + Serial.list()[i],60,70); textFont(font); textAlign(CENTER); } } } } } void mouseReleased(){ } void keyPressed(){ if(key == ' '){ spacePressed = true; } if(key == '1'){ onePressed = true; } if(key == '2'){ twoPressed = true; } if(key == '3'){ threePressed = true; } if(key == '4'){ fourPressed = true; } if(key == '5'){ fivePressed = true; } if(key == '6'){ sixPressed = true; } if(key == '7'){ sevenPressed = true; } switch(key){ case 's': // pressing 's' or 'S' will take a jpg of the processing window case 'S': saveFrame("heartLight-####.jpg"); break; case 'r': case 'R': resetDataTraces(); break; case ' ': default: break; } } void keyReleased(){ spacePressed = false; onePressed = false; twoPressed = false; threePressed = false; fourPressed = false; fivePressed = false; sixPressed = false; sevenPressed = false; }
void serialEvent(Serial port){ try{ String inData = port.readStringUntil('\n'); inData = trim(inData); for(int i=0; i<numSensors;i++){ if (inData.charAt(0) == 'a'+i){ // leading 'a' for sensor data inData = inData.substring(1); Sensor[i] = int(inData); } if (inData.charAt(0) == 'A'+i){ // leading 'A' for BPM data inData = inData.substring(1); BPM[i] = int(inData); beat[i] = true; heart[i] = 20; } if (inData.charAt(0) == 'M'+i){ // leading 'M' means TBH data inData = inData.substring(1); TBH[i] = int(inData); } } } catch(Exception e) { } }