Final Crit – James Kyle

Sims…but its you

 

Description:

I decided to take my previous visual crit further for this final crit by adding more components from a typical house into the control scheme/visualization method. The idea is to create a little virtual world where the user can visualize and interact with things going on in their home related to electronics. The user has the ability to turn lamps on and off and the states of each lamp will brighten the virtual room accordingly.

Lamps both off
One lamp on and one lamp off

The user can also see whether someone is at the door and let them inside if they so choose. I did not know how to connect a camera to the Arduino so the system is not able to distinguish who is at the door but that is a future addition to theoretical implementation of this concept. Assuming the user knows the person present at the door, they can click the lock next to the door and unlock the door for the user.

Process for unlocking door

The user also has visual indications of the temperature and thermostat setting through a heater/ac unit. The color of the unit dynamically changes based on the difference between the goal temperature and current temperature in the room. If the room is hotter than it should be, the unit appears more blue to indicate that the ac is running. The same is true for the opposite scenario as well where the room is colder than desired.

Temperature settings and their color representation
Circuit:

Demo Video:

 

Code:
p5.js
/*


- RGB to HSV Conversion Function from http://www.javascripter.net/faq/rgb2hsv.htm


*/
let serial;
let latestData = "waiting for data";
var outMessage;
var prevOutMessage;
var message;
var currentMessage;

let goalTemp = 0;
let currTemp = 0;

let newFont;
let lightsEnable = true;

let x = 100;
let y = 100;

let width = 1700;
let height = 900;

let color = 300;
let baseSaturation = 40;
let baseBrightness = 30;

let table1X = 970;
let table2X = 630;
let table1Y = 200;

//Lightsource vector [on/off, x, y, Lamp Radius, Shine Radius]
let lightSources = [];

let updateLightSource = true;

//States
let heaterState = 0;
let acState = 1;

//Colors
let doorStepColor = [50, 50, 50];
let lockColor = [255, 0, 0];
let heaterColor = [255, 150, 173];


//Positions
let heaterX = 375;
let heaterY = 250;

let livingRoomWidth = width - 600;
let livingRoomHeight = height - 100;

let doorStepHeight = 200;
let doorStepWidth = 200;

let lockX = width/2 + livingRoomWidth/2 + 10;
let lockY = height/2 + livingRoomHeight/2 - doorStepHeight/2 - 60;
let lockState = 0;


let minDistIndex = 0;


function setup() {
  createCanvas(width, height);

  //Lamps initiation
  lightSources[0] = [0, table1X, table1Y, 40, 200];
  lightSources[1] = [0, table2X, table1Y, 40, 200];
  
  
  serial = new p5.SerialPort();

  serial.list();
  serial.open('/dev/tty.usbmodem114697101');

  serial.on('connected', serverConnected);

  serial.on('list', gotList);

  serial.on('data', gotData);

  serial.on('error', gotError);

  serial.on('open', gotOpen);

  serial.on('close', gotClose);
}

function serverConnected() {
  print("Connected to Server");
 }
 
 function gotList(thelist) {
  print("List of Serial Ports:");
 
  for (let i = 0; i < thelist.length; i++) {
   print(i + " " + thelist[i]);
  }
 }
 
 function gotOpen() {
  print("Serial Port is Open");
 }
 
 function gotClose(){
  print("Serial Port is Closed");
  latestData = "Serial Port is Closed";
 }
 
 function gotError(theerror) {
  print(theerror);
 }
 
 function gotData() {
   let currentString = serial.readLine();
   trim(currentString);
   if (!currentString) return;
   console.log(currentString);
   latestData = currentString;

   if (latestData != currentMessage) {
    currentMessage = latestData;
    handleInputData(latestData);
   }
 }

 function handleInputData(latestData) {

//Current temperature
  if (latestData/10 >= 70) {
    currTemp = latestData - 700;
  }


//Goal temperature
  else if (latestData/10 >= 60) {
    goalTemp = latestData - 600;
  }

  //Presence at door
  else if (latestData/10 >= 4) {
    if (latestData == 41) {
      doorStepColor = [255, 255, 255];
    } else if (latestData == 40) {
      doorStepColor = [50, 50, 50];
    }
  }

  //Lock
  else if (latestData/10 >= 3) {
    if (latestData == 30){
      lockState = 0;
      toggleLockColor(lockState);
    } else if (latestData == 31) {
      lockState = 1;
      toggleLockColor(lockState);
    }
   }

   //Lamp 2
  else if (latestData/10 >= 2) {
    if (latestData == 20){
      lightSources[1][0] = 0;
    } else if (latestData == 21) {
      lightSources[1][0] = 1;
    }
   }

  //Lamp 1
  else if (latestData/10 >= 1) {
    if (latestData == 10){
      lightSources[0][0] = 0;
    } else if (latestData == 11) {
      lightSources[0][0] = 1;
    }
   }
}


 
function draw() {

  colorMode(HSB);
  background(128, 20, 70);


  //House top view
  //================================
  push();
  colorMode(RGB);
  rectMode(CENTER);
  stroke(0);
  strokeWeight(10);
  fill(50);
  rect(width/2, height/2,livingRoomWidth,livingRoomHeight);

  //Room
  fill(50);
  stroke(0);
  strokeWeight(0);
  rect(width/2 + livingRoomWidth/2, height/2 + livingRoomHeight/2 - doorStepHeight/2,100,75);

  //Door step
  fill(doorStepColor[0],doorStepColor[1],doorStepColor[2]);
  stroke(0);
  strokeWeight(0);
  rect(width/2 + livingRoomWidth/2 + doorStepWidth/2 + 5, height/2 + livingRoomHeight/2 - doorStepHeight/2,doorStepWidth,doorStepHeight);
  
  //Lock
  fill(lockColor[0], lockColor[1],lockColor[2]);
  rect(lockX, lockY,10,30);
  pop();
  //============================

  //Heater
  drawHeater(heaterX, heaterY, 200, 80, PI/2);

  //Couch
  drawCouch(800, 200, 80, 200, 0, 50, 40, 80);

  //Chairs
  drawCouch(980, 300, 80, 80, PI/2, 50, 40, 80);
  drawCouch(620, 300, 80, 80, -PI/2, 50, 40, 80);

  //Coffee table
  drawTable(800, 300, 80, 200, PI, 30, 40, 30);

  //Lamp tables
  drawTable(table1X, table1Y, 80, 80, 0, 30, 40, 30);
  drawTable(table2X, table1Y, 80, 80, 0, 30, 40, 30);

  for (k = 0; k < lightSources.length; k++) {
    fill(200, 40, 60);
    drawLamp(lightSources[k][1], lightSources[k][2], lightSources[k][3]);
  }



  for (n = 0; n < lightSources.length; n++) {
    if (lightSources[n][0] == 1) {
      turnLightOn(lightSources[n][1], lightSources[n][2], lightSources[n][3], lightSources[n][4]);
    }
  }

  push();
  fill(0);
  textAlign(CENTER);
  textSize(20);
  text("House Setting: " + goalTemp, 50, 100, 150, 100);
  text("Room Temperature: " + currTemp, 30, 180, 190, 100);
  pop();

 
}


function updateLightState(lightIndex) {
  lightIndex = Number(lightIndex);
  if (lightSources[lightIndex][0] == 1) {
      lightSources[lightIndex][0] = 0;
      if (lightIndex == 0) { 
        currentMessage = 10;
        sendMessage(currentMessage);
      } else if (lightIndex == 1){
        currentMessage = 20;
        sendMessage(currentMessage);
      }
  }

  else if (lightSources[lightIndex][0] == 0) {
      lightSources[lightIndex][0] = 1;
      if (lightIndex == 0) { 
        currentMessage = 11;
        sendMessage(currentMessage);
      } else if (lightIndex == 1){
        currentMessage = 21;
        sendMessage(currentMessage);
      }
  }
}


function mousePressed() {

  let currX = mouseX;
  let currY = mouseY;

  dist2Lamp1 = sqrt(pow((currX-lightSources[0][1]),2) + pow((currY-lightSources[0][2]),2));
  dist2Lamp2 = sqrt(pow((currX-lightSources[1][1]),2) + pow((currY-lightSources[1][2]),2));
  dist2Heater = sqrt(pow((currX-heaterX),2) + pow((currY-heaterY),2));
  dist2Lock = sqrt(pow((currX-lockX),2) + pow((currY-lockY),2));

  let minDistIndex = 0;
  let distVector = [dist2Lamp1,dist2Lamp2,dist2Heater,dist2Lock];

  for (i = 1; i < 4; i++) { 
    if (distVector[minDistIndex] > distVector[i]) {
      minDistIndex = i;
    }
  }

  if (minDistIndex == 0) {
    if (dist2Lamp1 < lightSources[0][3]*2) {
      updateLightState(0);
    }

  }

  if (minDistIndex == 1) {
    if (dist2Lamp2 < lightSources[1][3]*2) {
      updateLightState(1);
    }
  }

  if (minDistIndex == 3) {
    
    if (currX < lockX + 5 && currX > lockX - 5) {
      if (currY < lockY + 15 && currY > lockY - 15) {
        lockState = !lockState;
        toggleLockColor();
      }
    }
  }

}


function turnLightOn(x, y, lampRadius, shineRadius) {
  var centerX = x;
  var centerY = y;


  //Iterating through each point in square to brighten color if inside lamp shine radius
  for (i = 0; i < 2*shineRadius; i = i+5) {
    for (j = 0; j < 2*shineRadius; j = j+5) {
      var currX = centerX - shineRadius + i;
      var currY = centerY - shineRadius + j;

      var dist2Center = sqrt(pow((centerX - currX),2) + pow((centerY - currY),2));

      if (dist2Center <= shineRadius) {
        var currColor = get(currX, currY);
        hsvConversion = rgb2hsv(currColor[0], currColor[1], currColor[2]);

        colorMode(HSB);
        stroke(hsvConversion[0], hsvConversion[1]+40, hsvConversion[2]+20);
        strokeWeight(7);
        point(currX, currY);
      }

    }
  }
 
  strokeWeight(0);
}



function drawCouch(x, y, width, height, theta, couchH, couchS, couchB) {

  let couchHeight = width;
  let couchWidth = height;
  let armWidth = couchWidth*1/5;
  let armHeight = couchHeight*4/5;
  let backWidth = couchWidth;
  let backHeight = couchHeight*2/5;

  //Base Couch
  push();
  angleMode(RADIANS);
  rectMode(CENTER);
  translate(x, y);
  rotate(theta);
  // translate(transRadius - transRadius*cos(theta), -transRadius*sin(theta));

  strokeWeight(2);
  stroke(0);
  fill(couchH, couchS, couchB);
  rect(0, 0, couchWidth, couchHeight, 0, 0, 10, 10);

  //Arm rests
  fill(couchH, couchS, couchB - 20);
  rect(-couchWidth/2, armHeight - couchHeight, armWidth, armHeight, 10, 10, 10, 10);
  rect(couchWidth/2, armHeight - couchHeight, armWidth, armHeight, 10, 10, 10, 10);

  //Backing
  fill(couchH, couchS, couchB - 25);
  rect(0, (armHeight/2 - couchHeight/2) + (backHeight/2 - couchHeight/2), backWidth, backHeight, 0, 0, 30, 30);
  strokeWeight(0);


  pop();

}

function drawTable(x, y, width, height, theta, couchH, couchS, couchB) {

  let tableHeight = width;
  let tableWidth = height;

  //Base Couch
  push();
  angleMode(RADIANS);
  rectMode(CENTER);
  translate(x, y);
  rotate(theta);
  // translate(transRadius - transRadius*cos(theta), -transRadius*sin(theta));

  //Table top
  strokeWeight(2);
  stroke(0);
  fill(couchH, couchS, couchB);
  rect(0, 0, tableWidth, tableHeight);

  //Top pattern
  strokeWeight(2);
  stroke(0);
  fill(couchH, couchS, couchB + 40);
  rect(0, 0, tableWidth - 10, tableHeight - 10);

  pop();

}


function drawLamp(x, y, radius, couchH, couchS, couchB) {

  let tableHeight = width;
  let tableWidth = height;

  //Base Couch
  push();

  //Lamp
  fill(couchH, couchS, couchB);
  ellipse(x, y, radius);

  pop();

}

function drawHeater(x, y, width, height, theta) {

  let heaterWidth = width;
  let heaterHeight = height;
  let numOfFins = 20;
  let finWidth = heaterWidth/numOfFins - 4 - 8/numOfFins;
  let finHeight = height/2 - 10;

  let tempDiff = goalTemp - currTemp;

  heaterColor = [220+2*tempDiff, 180, 200-tempDiff];

  //Base Couch
  push();
  colorMode(RGB);
  angleMode(RADIANS);
  rectMode(CENTER);
  translate(x, y);
  rotate(theta);
  // translate(transRadius - transRadius*cos(theta), -transRadius*sin(theta));

  //Heater Base
  strokeWeight(0);
  fill(heaterColor[0], heaterColor[1], heaterColor[2]);
  rect(0, 0, heaterWidth, heaterHeight, 5, 5, 5, 5);

  //Top pattern
  strokeWeight(2);
  stroke(0);
  fill(117, 109, 94);
  for (i = 0; i < numOfFins; i++) {
    let currX = 0 - heaterWidth/2 + (finWidth+12)/2 + (finWidth+4)*(i);
    rect(currX, finHeight/2+5, finWidth, finHeight, finHeight/2, finHeight/2, finHeight/2);
    rect(currX, -finHeight/2-5, finWidth, finHeight, finHeight/2, finHeight/2, finHeight/2);
  }
  
  pop();

}


function toggleLockColor() {
  if (lockState) {
    lockColor = [0, 255, 0];
    currentMessage = 31;
    sendMessage(currentMessage);
  } else {
    lockColor = [255, 0, 0];
    currentMessage = 30;
    sendMessage(currentMessage);
  }
}


function rgb2hsv (r,g,b) {
 var computedH = 0;
 var computedS = 0;
 var computedV = 0;

 //remove spaces from input RGB values, convert to int
 var r = parseInt( (''+r).replace(/\s/g,''),10 );
 var g = parseInt( (''+g).replace(/\s/g,''),10 );
 var b = parseInt( (''+b).replace(/\s/g,''),10 );

 if ( r==null || g==null || b==null ||
     isNaN(r) || isNaN(g)|| isNaN(b) ) {
   alert ('Please enter numeric RGB values!');
   return;
 }
 if (r<0 || g<0 || b<0 || r>255 || g>255 || b>255) {
   alert ('RGB values must be in the range 0 to 255.');
   return;
 }
 r=r/255; g=g/255; b=b/255;
 var minRGB = Math.min(r,Math.min(g,b));
 var maxRGB = Math.max(r,Math.max(g,b));

 // Black-gray-white
 if (minRGB==maxRGB) {
  computedV = minRGB;
  return [0,0,computedV];
 }

 // Colors other than black-gray-white:
 var d = (r==minRGB) ? g-b : ((b==minRGB) ? r-g : b-r);
 var h = (r==minRGB) ? 3 : ((b==minRGB) ? 1 : 5);
 computedH = 60*(h - d/(maxRGB - minRGB));
 computedS = (maxRGB - minRGB)/maxRGB;
 computedV = maxRGB;
 return [computedH,computedS*100,computedV*100];
}

function sendMessage(message) {
  for (i = 0; i < 1; i++) {
    //Output message to serial
    serial.write(int(message));
  }
}
Arduino
/*

   References:
      - Temperature Reading Code: https://www.circuitbasics.com/arduino-thermistor-temperature-sensor-tutorial/




   Goal: bring control of home to digital interface on p5.js

   1. Lights
      - ON/OFF through p5.js (100%)
      - State control based on presence in room (0%)

   2. Temperature
      - Holding temperature (70%)
      - Indication of heater and ac states through p5.js (0%)

   3. Doorbell
      - Represent door presence through p5.js (30%)
      - Unlock door through p5.js (80%) --> motor (CHECK), lights (CHECK), and sound to indicate to user that door is unlocked

   4. TV
      - Control ON/OF based on presence on couch (0%)

   5. Notifications
      - Notify user of house happenings through p5.js (0%)
      - Take input through p5.js (0%)



   Communication Protocol:
   1 - lamp 1
   2 - lamp 2
   3 - lock state --> 1=unlocked; 0=locked
   4 - presence state
   5 - heater on? --> 1=yes; 0=no
   6 - current temp



*/

#include <Servo.h>


#define lamp1pin 17
#define lamp2pin 16
#define button1 14
#define button2 40

#define coldPin             10
#define hotPin              11
#define tempSensor1         20
#define sliderPin           21

#define pressurePlate       19
#define presenceIndicator   8
#define lockButton          28
#define lockLight           38
#define unlockLight         35


//================//
//==Temp Control==//
//================//
bool heaterON = true;
bool acON = true;

long double prevTempTime = 0;
int tempTimerDuration = 2; // [s]


//========//
//==Lock==//
//========//
unsigned long debounceTimerLock = 0;
int debounceTimeLock = 300;
bool DEBOUNCE_LOCK = false;
bool firstCountLock = true;
volatile int lockState = 0; // 0 => locked; 1 => unlocked

int unlockTimer = 3000;
long double prevUnlockTime = 0;

Servo lockServo;


//=========//
//==Lamps==//
//=========//
unsigned long debounceTimer1 = 0;
int debounceTime1 = 300;
bool DEBOUNCE1 = false;
bool firstCount1 = true;

unsigned long debounceTimer2 = 0;
int debounceTime2 = 300;
bool DEBOUNCE2 = false;
bool firstCount2 = true;


volatile int lamp1state = 0;
volatile int lamp2state = 0;


//=================//
//==Communication==//
//=================//
int currentMessage;
int prevMessage;
int prevInMessage;
int inMessage;
bool receivedMessage = false;


void setup() {
  // put your setup code here, to run once:

  pinMode(lamp1pin, OUTPUT);
  pinMode(lamp2pin, OUTPUT);
  pinMode(coldPin, OUTPUT);
  pinMode(hotPin, OUTPUT);
  pinMode(button1, INPUT_PULLUP);
  pinMode(button2, INPUT_PULLUP);
  attachInterrupt(digitalPinToInterrupt(button1), button1Pressed, CHANGE);
  attachInterrupt(digitalPinToInterrupt(button2), button2Pressed, CHANGE);

  pinMode(tempSensor1, INPUT);
  pinMode(sliderPin, INPUT);

  pinMode(pressurePlate, INPUT);
  pinMode(lockButton, INPUT_PULLUP);

  attachInterrupt(digitalPinToInterrupt(lockButton), handleLockButton, CHANGE);
  Serial.begin(9600);

  lockServo.attach(29);
  lockServo.write(0);
  delay(15);
}

void loop() {

  //if main computer sent a command
  if ( Serial.available() > 0) {
    int c = Serial.read();
    parseInput(c);
  }

  doPlatePresenceCheck();


  //Handling heater check
  if (millis() / 1000.0 - prevTempTime > tempTimerDuration) {
    prevTempTime = millis() / 1000.0;
    checkTemp();
  }



  //Debounce for lock
  if (DEBOUNCE_LOCK && firstCountLock) {
    debounceTimerLock = millis();
    firstCountLock = false;
    toggleLockState(lockState);
  }

  if (millis() - debounceTimerLock > debounceTimeLock) {
    DEBOUNCE_LOCK = false;
    firstCountLock = true;
  }

  if (millis() - prevUnlockTime > unlockTimer && lockState) {
    lockState = 0;
    toggleLockState(lockState);
  }



  //Debounce for lamp 1
  if (DEBOUNCE1 && firstCount1) {
    debounceTimer1 = millis();
    firstCount1 = false;
    currentMessage = 10 + lamp1state;
    sendMessage(currentMessage);
  }

  if (millis() - debounceTimer1 > debounceTime1) {
    DEBOUNCE1 = false;
    firstCount1 = true;
  }



  //Debounce for lamp 2
  if (DEBOUNCE2 && firstCount2) {
    debounceTimer2 = millis();
    firstCount2 = false;
    currentMessage = 20 + lamp2state;
    sendMessage(currentMessage);
  }
  if (millis() - debounceTimer2 > debounceTime2) {
    DEBOUNCE2 = false;
    firstCount2 = true;
  }

}

void checkTemp() {

  int Vo;
  float R1 = 10000;
  float logR2, R2, T;
  float c1 = 1.009249522e-03, c2 = 2.378405444e-04, c3 = 2.019202697e-07;

  float minTemp = 60;
  float maxTemp = 90;
  float goalTemp;

  Vo = analogRead(tempSensor1);
  R2 = R1 * (1023.0 / (float)Vo - 1.0);
  logR2 = log(R2);
  T = (1.0 / (c1 + c2 * logR2 + c3 * logR2 * logR2 * logR2));
  T = T - 273.15;
  T = (T * 9.0) / 5.0 + 32.0;

  goalTemp  = map(analogRead(sliderPin), 0, 1023, minTemp, maxTemp);
  currentMessage = 600 + goalTemp;
  sendMessage(currentMessage);

  currentMessage = 700 + T;
  sendMessage(currentMessage);


  if (goalTemp > T && acON) {
    digitalWrite(hotPin, HIGH);
    digitalWrite(coldPin, LOW);
    heaterON = true;
    acON = false;
    currentMessage = 51; //Tell p5.js heater is on / AC is off
    sendMessage(currentMessage);
  } else if (goalTemp < T && heaterON) {
    digitalWrite(hotPin, LOW);
    digitalWrite(coldPin, HIGH);
    heaterON = false;
    acON = true;
    currentMessage = 50; //Tell p5.js heater is off / AC is on
    sendMessage(currentMessage);
  }

}


void doPlatePresenceCheck() {

  int standingThreshold = 600;
  int standingTimeThreshold = 2000; // [ms]
  static bool timingStand = false;
  static bool someoneOnPressurePlate = false;
  static unsigned long standingTime = 0;


  int plateReading = analogRead(pressurePlate);

  if (plateReading > standingThreshold) {
    if (!timingStand) {
      timingStand = true;
      standingTime = millis();
    }
  } else if (plateReading < standingThreshold && someoneOnPressurePlate) {
    timingStand = false;
    someoneOnPressurePlate = false;
    digitalWrite(presenceIndicator, LOW);
    currentMessage = 40;
    sendMessage(currentMessage);
  }

  if (timingStand && millis() - standingTime > standingTimeThreshold && !someoneOnPressurePlate) {
    timingStand = false;
    someoneOnPressurePlate = true;
    digitalWrite(presenceIndicator, HIGH);
    currentMessage = 41;
    sendMessage(currentMessage);
  }

}


void handleLockButton() {
  if (!DEBOUNCE_LOCK) {
    DEBOUNCE_LOCK = true;
    lockState = !lockState;
    receivedMessage = false;
  }
}

void toggleLockState(int lockState) {
  int numOfBlinks = 5;

  if (lockState) {
    //Unlock door
    lockServo.write(179);
    delay(15);

    //Turn light on
    digitalWrite(lockLight, LOW);
    for (int i = 0; i < numOfBlinks; i++) {
      digitalWrite(unlockLight, HIGH);
      delay(100);
      digitalWrite(unlockLight, LOW);
      delay(50);
    }
    digitalWrite(unlockLight, HIGH);

    //Start timing unlock
    prevUnlockTime = millis();

    //Indicate locking state to p5
    currentMessage = 31;
    sendMessage(currentMessage);


  } else {
    //Lock door
    lockServo.write(0);
    delay(15);

    //Turn lock light on and unlock light off
    digitalWrite(unlockLight, LOW);
    for (int i = 0; i < numOfBlinks; i++) {
      digitalWrite(lockLight, HIGH);
      delay(100);
      digitalWrite(lockLight, LOW);
      delay(50);
    }
    digitalWrite(lockLight, HIGH);

    //Indicate locking state to p5
    currentMessage = 30;
    sendMessage(currentMessage);


  }
}


void button2Pressed() {
  if (!DEBOUNCE2) {
    DEBOUNCE2 = true;
    lamp2state = !lamp2state;
    digitalWrite(lamp2pin, lamp2state);
  }
}

void button1Pressed() {
  if (!DEBOUNCE1) {
    DEBOUNCE1 = true;
    lamp1state = !lamp1state;
    digitalWrite(lamp1pin, lamp1state);
  }
}

void parseInput(int inputData) {
  int y = inputData;

  if (y == 10 && prevInMessage != 10) { //1
    lamp1state = 0;
    digitalWrite(lamp1pin, LOW);
  } else if (y == 11 && prevInMessage != 11) { //1
    lamp1state = 1;
    digitalWrite(lamp1pin, HIGH);
  } if (y == 20 && prevInMessage != 20) { //1
    lamp2state = 0;
    digitalWrite(lamp2pin, LOW);
  } else if (y == 21 && prevInMessage != 21) { //1
    lamp2state = 1;
    digitalWrite(lamp2pin, HIGH);
  } else if (y == 30 && prevInMessage != 30) { //2
    lockState = 0;
    toggleLockState(lockState);
    receivedMessage = true;
  } else if (y == 31 && prevInMessage != 31) { //3
    lockState = 1;
    toggleLockState(lockState);
    receivedMessage = true;
  }

}




void sendMessage(int message) {
  for (int i = 0; i < 10; i++) {
    Serial.println(message);
    delay(40);
  }
}

 

Visual Crit – James Kyle

Description:

For this project, I aimed to create an interactive 8-bit type representation of a house that the user could interact with and change stuff about their environment with the interface. I decided to go with lights and represent their states in the virtual p5.js environment. The user can click the lamps in the virtual world and turn them on/off in the real world. The states of the lamps are reflected in the p5.js environment as well brightening up the surrounding environment when they are on. The user can also turn the lights on and off in the physical world and the light states would be again reflected in the virtual environment so the information goes both ways from p5.js to arduino and back.

No lights on
One lamp on
Both lamps on

 

Demo video:

https://youtu.be/KQ2YrnX2HL4http://

 

Circuit:

 

Arduino Code:

#include <string.h>

#define lamp1pin 10
#define lamp2pin 9
#define button1 3
#define button2 2


unsigned long debounceTimer1 = 0;
int debounceTime1 = 300;
bool DEBOUNCE1 = false;
bool firstCount1 = true;

unsigned long debounceTimer2 = 0;
int debounceTime2 = 300;
bool DEBOUNCE2 = false;
bool firstCount2 = true;


volatile int lamp1state = 0;
volatile int lamp2state = 0;


int inputData;

char buf[25];
String command;

int c;


void setup() {
  // put your setup code here, to run once:

  Serial.begin(9600);

  pinMode(lamp1pin, OUTPUT);
  pinMode(lamp2pin, OUTPUT);
  pinMode(button1, INPUT_PULLUP);
  pinMode(button2, INPUT_PULLUP);
  attachInterrupt(digitalPinToInterrupt(button1), button1Pressed, CHANGE);
  attachInterrupt(digitalPinToInterrupt(button2), button2Pressed, CHANGE);

}


void button2Pressed() {
  if (!DEBOUNCE2) {
    DEBOUNCE2 = true;
    lamp2state = !lamp2state;
    digitalWrite(lamp2pin, lamp2state);
  }
}

void button1Pressed() {
  if (!DEBOUNCE1) {
    DEBOUNCE1 = true;
    lamp1state = !lamp1state;
    digitalWrite(lamp1pin, lamp1state);
  }
}

void loop() {

  int message1 = 10;
  int message2 = 20;

  //if main computer sent a command
  if ( Serial.available() > 0) {

    c = Serial.read();
    
//    if (c == 1) {
//      
//      digitalWrite(lamp1pin, HIGH);
//    }
    Serial.println(c);
    parseInput(c);
  }

  if (DEBOUNCE1 && firstCount1) {
    debounceTimer1 = millis();
    firstCount1 = false;
    message1 = 1 + lamp1state;
//    Serial.println(message1);
  }
  
  if (millis() - debounceTimer1 > debounceTime1) {
    DEBOUNCE1 = false;
    firstCount1 = true;
  }

  if (DEBOUNCE2 && firstCount2) {
    debounceTimer2 = millis();
    firstCount2 = false;
    message2 = 3 + lamp2state;
//    Serial.println(message2);
  }
  if (millis() - debounceTimer2 > debounceTime2) {
    DEBOUNCE2 = false;
    firstCount2 = true;
  }
  
  
}


void parseInput(int inputData) {


  int y = inputData;

    if (y == 1) { //1
      lamp1state = 1;
      digitalWrite(lamp1pin, HIGH);
    } else if (y == 2) { //2
      lamp1state = 0;
      digitalWrite(lamp1pin, LOW);
    }
    else if (y == 3) { //3
      lamp2state = 1;
      digitalWrite(lamp2pin, HIGH);
    } else if (y == 4) { //4
      lamp2state = 0;
      digitalWrite(lamp2pin, LOW);
    }

  
}

 

p5.js Code:

/*


- RGB to HSV Conversion Function from http://www.javascripter.net/faq/rgb2hsv.htm


*/




let serial;
let latestData = "waiting for data";
var outData;
var message;

let newFont;
let lightsEnable = true;

let x = 100;
let y = 100;

let width = 1700;
let height = 900;

let color = 300;
let baseSaturation = 40;
let baseBrightness = 30;

let table1X = 970;
let table2X = 630;
let table1Y = 200;

//Lightsource vector [on/off, x, y, Lamp Radius, Shine Radius]
let lightSources = [];

let updateLightSource = true;


function setup() {
  createCanvas(width, height);

  //Lamps initiation
  lightSources[0] = [0, table1X, table1Y, 40, 200];
  lightSources[1] = [0, table2X, table1Y, 40, 200];
  lightSources[2] = [0, table1X, table1Y, 40, 200];
  
  
  serial = new p5.SerialPort();

  serial.list();
  serial.open('/dev/tty.usbmodem14301');

  serial.on('connected', serverConnected);

  serial.on('list', gotList);

  serial.on('data', gotData);

  serial.on('error', gotError);

  serial.on('open', gotOpen);

  serial.on('close', gotClose);
}

function serverConnected() {
  print("Connected to Server");
 }
 
 function gotList(thelist) {
  print("List of Serial Ports:");
 
  for (let i = 0; i < thelist.length; i++) {
   print(i + " " + thelist[i]);
  }
 }
 
 function gotOpen() {
  print("Serial Port is Open");
 }
 
 function gotClose(){
  print("Serial Port is Closed");
  latestData = "Serial Port is Closed";
 }
 
 function gotError(theerror) {
  print(theerror);
 }
 
 function gotData() {
   let currentString = serial.readLine();
   trim(currentString);
   if (!currentString) return;
   console.log(currentString);
   latestData = currentString;

   if (latestData/10 >= 2) {
     lightSources[1][0] = latestData - 20;
   } else {
    lightSources[0][0] = latestData - 10; 
   }
 }
 
function draw() {

  colorMode(HSB);
  background(128, 20, 70);

  push();
  rectMode(CENTER);
  fill(50);
  rect(width/2, height/2,width - 100, height - 100);
  pop();

  //Couch
  drawCouch(800, 200, 80, 200, 0, 50, 40, 80);

  //Chairs
  drawCouch(980, 300, 80, 80, PI/2, 50, 40, 80);
  drawCouch(620, 300, 80, 80, -PI/2, 50, 40, 80);

  //Coffee table
  drawTable(800, 300, 80, 200, PI, 30, 40, 30);

  //Lamp tables
  drawTable(table1X, table1Y, 80, 80, 0, 30, 40, 30);
  drawTable(table2X, table1Y, 80, 80, 0, 30, 40, 30);

  for (k = 0; k < lightSources.length; k++) {
    fill(200, 40, 60);
    drawLamp(lightSources[k][1], lightSources[k][2], lightSources[k][3]);
  }



  for (n = 0; n < lightSources.length; n++) {
    if (lightSources[n][0] == 1) {
      turnLightOn(lightSources[n][1], lightSources[n][2], lightSources[n][3], lightSources[n][4]);
    }
  }

  push();
  fill(0);
  textSize(14);
  text(latestData, 10, 20);
  pop();

  push();
  fill(0);
  textSize(14);
  text(message, 10, 40);
  pop();

  var d = 3;
  d = int(d);

  // serial.write(int(2));
 
}


function updateLightState(lightIndex) {
  lightIndex = Number(lightIndex);
  if (lightSources[lightIndex][0] == 1) {
      lightSources[lightIndex][0] = 0;
      if (lightIndex == 2) { 
        outData = 2;
      } else if (lightIndex == 1){
        outData = 4;
      }
      // print(message);
      serial.write(int(outData));
  }

  else if (lightSources[lightIndex][0] == 0) {
      lightSources[lightIndex][0] = 1;
      if (lightIndex == 2) { 
        outData = 1;
      } else if (lightIndex == 1){
        outData = 3;
      }
      // print(message);
      serial.write(int(outData));
  }
}

function checkIfLampClicked(x, y) {

  for (n = 0; n < lightSources.length; n++) {
    let currX = lightSources[n][1];
    let currY = lightSources[n][2];
    let currR = lightSources[n][3];

    let distance = sqrt(pow((currX-x),2) - pow((currY-y),2));

    if (distance <= currR) {
      //Toggle lamp state
      updateLightState(n);
      return;
    }
  }
}

function keyPressed() {

  updateLightState(key);

}



function turnLightOn(x, y, lampRadius, shineRadius) {
  var centerX = x;
  var centerY = y;


  //Iterating through each point in square to brighten color if inside lamp shine radius
  for (i = 0; i < 2*shineRadius; i = i+5) {
    for (j = 0; j < 2*shineRadius; j = j+5) {
      var currX = centerX - shineRadius + i;
      var currY = centerY - shineRadius + j;

      var dist2Center = sqrt(pow((centerX - currX),2) + pow((centerY - currY),2));

      if (dist2Center <= shineRadius) {
        var currColor = get(currX, currY);
        hsvConversion = rgb2hsv(currColor[0], currColor[1], currColor[2]);

        colorMode(HSB);
        stroke(hsvConversion[0], hsvConversion[1]+40, hsvConversion[2]+20);
        strokeWeight(7);
        point(currX, currY);
      }

    }
  }
 
  strokeWeight(0);
}



function drawCouch(x, y, width, height, theta, couchH, couchS, couchB) {

  let couchHeight = width;
  let couchWidth = height;
  let armWidth = couchWidth*1/5;
  let armHeight = couchHeight*4/5;
  let backWidth = couchWidth;
  let backHeight = couchHeight*2/5;

  //Base Couch
  push();
  angleMode(RADIANS);
  rectMode(CENTER);
  translate(x, y);
  rotate(theta);
  // translate(transRadius - transRadius*cos(theta), -transRadius*sin(theta));

  strokeWeight(2);
  stroke(0);
  fill(couchH, couchS, couchB);
  rect(0, 0, couchWidth, couchHeight, 0, 0, 10, 10);

  //Arm rests
  fill(couchH, couchS, couchB - 20);
  rect(-couchWidth/2, armHeight - couchHeight, armWidth, armHeight, 10, 10, 10, 10);
  rect(couchWidth/2, armHeight - couchHeight, armWidth, armHeight, 10, 10, 10, 10);

  //Backing
  fill(couchH, couchS, couchB - 25);
  rect(0, (armHeight/2 - couchHeight/2) + (backHeight/2 - couchHeight/2), backWidth, backHeight, 0, 0, 30, 30);
  strokeWeight(0);


  pop();

}

function drawTable(x, y, width, height, theta, couchH, couchS, couchB) {

  let tableHeight = width;
  let tableWidth = height;

  //Base Couch
  push();
  angleMode(RADIANS);
  rectMode(CENTER);
  translate(x, y);
  rotate(theta);
  // translate(transRadius - transRadius*cos(theta), -transRadius*sin(theta));

  //Table top
  strokeWeight(2);
  stroke(0);
  fill(couchH, couchS, couchB);
  rect(0, 0, tableWidth, tableHeight);

  //Top pattern
  strokeWeight(2);
  stroke(0);
  fill(couchH, couchS, couchB + 40);
  rect(0, 0, tableWidth - 10, tableHeight - 10);

  pop();

}


function drawLamp(x, y, radius, couchH, couchS, couchB) {

  let tableHeight = width;
  let tableWidth = height;

  //Base Couch
  push();

  //Lamp
  fill(couchH, couchS, couchB);
  ellipse(x, y, radius);

  pop();

}



function rgb2hsv (r,g,b) {
 var computedH = 0;
 var computedS = 0;
 var computedV = 0;

 //remove spaces from input RGB values, convert to int
 var r = parseInt( (''+r).replace(/\s/g,''),10 );
 var g = parseInt( (''+g).replace(/\s/g,''),10 );
 var b = parseInt( (''+b).replace(/\s/g,''),10 );

 if ( r==null || g==null || b==null ||
     isNaN(r) || isNaN(g)|| isNaN(b) ) {
   alert ('Please enter numeric RGB values!');
   return;
 }
 if (r<0 || g<0 || b<0 || r>255 || g>255 || b>255) {
   alert ('RGB values must be in the range 0 to 255.');
   return;
 }
 r=r/255; g=g/255; b=b/255;
 var minRGB = Math.min(r,Math.min(g,b));
 var maxRGB = Math.max(r,Math.max(g,b));

 // Black-gray-white
 if (minRGB==maxRGB) {
  computedV = minRGB;
  return [0,0,computedV];
 }

 // Colors other than black-gray-white:
 var d = (r==minRGB) ? g-b : ((b==minRGB) ? r-g : b-r);
 var h = (r==minRGB) ? 3 : ((b==minRGB) ? 1 : 5);
 computedH = 60*(h - d/(maxRGB - minRGB));
 computedS = (maxRGB - minRGB)/maxRGB;
 computedV = maxRGB;
 return [computedH,computedS*100,computedV*100];
}

 

Ideas for Visual Crit – James Kyle

In thinking about projects that I could do for the visual crit, I thought about two ways of representing information visually. The first idea I had was a different version of the ripe fruit detector from last crit. I was thinking it would be cool if you cold take color data from one spectrum and manipulate it/represent it in another spectrum to make colors “easier” to discern.

Another idea I had was somehow representing data about utility usage visually. This would probably require a combination of p5.js and maybe other sensing from utility meters around the house to give you a more continuous representation but I think it could be cool to take this data and map it to a tank of water or a thunderstorm for electricity (more electricity usage translating to a larger storm). I also think this could be used to represent usage compared to household averages or goals based on how green or efficient you would want your house to be (This idea may be outside the scope of a crit but would be really fun to think about).

Make it So Ch 3, 4, 5 – James Kyle

Thoughts:

I thought the conversation about virtual projections was especially interesting given the state of virtual reality in the modern tech world. I think it is interesting to think about real world applications of virtual projections and I think things like the Metaverse are the closest thing we will get in the near future. I think it’s also interesting to think about the application of virtual reality type equipment not only in virtual environments but also in exploring real world places and things. With haptic feedback and camera technology nowadays, I think it is realistic to think that the next step of virtual projection type technology is not only visual but also touch based.

 

The conversation about color was also interesting to me because giving meaning to colors as an engineer is not something I think about everyday but affects the way I interact with my projects. For example, the color of wires in electrical diagrams plays a big part in how easy they are to understand. Using red wires for everything connecting to power and black wires for everything connecting to ground is something that is second nature to me having worked with electrical components for a while but it seems like an arbitrary decision. There are lots of other color standards for indicating signal types flowing through specific wires but it’s kind of interesting to think about the origin of those color schemes similar to the reasons why blue displays signify futuristic tech.

 

 

Doorbell for the Blind

Concept:

The concept for this device was to create a doorbell that could be used navigated and used with little to no eyesight required. My goal was to eliminate the component of doorbell interactions where the user has to navigate on the door to find the button and ring the doorbell. I ended up settling on a doormat that senses when a person is present and then greets the user and prompts them to stomp a specified amount of times for desired interactions. The actions were made to resemble those of a traditional answering machine: stomp once to hear the address, stomp twice to ring the doorbell, stomp three times to hear the options again. Going further into the design, I think it would be helpful to add a message component so the user could leave a message to the resident if they want. I think it would also be helpful for security sake to incorporate a notification system to the resident is notified of any users on their doorstep and a picture is captured of whoever is there.

 

Electrical Schematic:

 

Demo Video:

 

Code:

/*
   Warning Machine for Something Approaching
*/

#include <Audio.h>
#include <Wire.h>
#include <SPI.h>
#include <SD.h>
#include <SerialFlash.h>

AudioPlaySdWav           playWav;

AudioOutputMQS           mqs1;           //xy=625,279
AudioConnection          patchCord1(playWav, 0, mqs1, 0);
AudioConnection          patchCord2(playWav, 1, mqs1, 1);


// Use these with the Teensy 3.5 & 3.6 SD card
#define SDCARD_CS_PIN     BUILTIN_SDCARD
#define SDCARD_MOSI_PIN   11  // not actually used
#define SDCARD_SCK_PIN    13  // not actually used

#define pressurePlate     14



String message = "";
int stompCounter = 0;


void setup() {
  // put your setup code here, to run once:

  Serial.begin(9600);

  // Audio connections require memory to work.  For more
  // detailed information, see the MemoryAndCpuUsage example
  AudioMemory(8);

  // Comment these out if not using the audio adaptor board.
  // This may wait forever if the SDA & SCL pins lack
  // pullup resistors

  SPI.setMOSI(SDCARD_MOSI_PIN);
  SPI.setSCK(SDCARD_SCK_PIN);
  if (!(SD.begin(SDCARD_CS_PIN))) {
    // stop here, but print a message repetitively
    while (1) {
      Serial.println("Unable to access the SD card");
      delay(500);
    }
  }


  pinMode(pressurePlate, INPUT);


}

void loop() {
  // put your main code here, to run repeatedly

  bool someoneOnPressurePlate = doPlatePresenceCheck();

  if (someoneOnPressurePlate) {
    doPressurePlateCheck();
  }

  
  Serial.print("Pressure Plate Reading: ");
  Serial.print(analogRead(pressurePlate));
  Serial.print("\t");
  Serial.print("Stomp Counter: ");
  Serial.print(stompCounter);
  Serial.print("\t");
  //  Serial.print("Pressure Plate Pressence: ");
  //  Serial.print(someoneOnPressurePlate);
  //  Serial.print("\t");

  Serial.print("Message: ");
  Serial.print(message);
  Serial.print("\t");
  Serial.println("");
  delay(5);


}

void playSound(const char *filename) {
  playWav.play(filename);
  delay(25);
  while (playWav.isPlaying()) {

  }
}

bool doPlatePresenceCheck() {

  int standingThreshold = 600;
  int standingTimeThreshold = 2000; // [ms]
  static bool timingStand = false;
  static bool someoneOnPressurePlate = false;
  static unsigned long standingTime = 0;


  int plateReading = analogRead(pressurePlate);

  if (plateReading > standingThreshold) {
    if (!timingStand) {
      timingStand = true;
      standingTime = millis();
    }
  } else if (plateReading < standingThreshold) {
    timingStand = false;
    someoneOnPressurePlate = false;
  }

  if (timingStand && millis() - standingTime > standingTimeThreshold) {
    if (!someoneOnPressurePlate) {
      playSound("Initial Greeting.WAV");
      playSound("Options Sound.WAV");
    }
    timingStand = false;
    someoneOnPressurePlate = true;
  }

  return someoneOnPressurePlate;

}


void doPressurePlateCheck() {

  int debounceTime = 300;
  int stompThreshold = 900;
  int countTimerThreshold = 3000; // [ms]
  static bool DEBOUNCE = false;
  static bool timeDebounce = false;
  static bool countStart = false;
  static unsigned long debounceTimer = 0;
  static unsigned long countTimer = 0;

  int pressureReading = analogRead(pressurePlate);


  if (!DEBOUNCE && pressureReading > stompThreshold) {
    if (!countStart) {
      countStart = true;
      countTimer = millis();
    }

    DEBOUNCE = true;
    stompCounter += 1;
  }

  if (millis() - countTimer > countTimerThreshold || stompCounter > 3) {
    actionBasedOnStompCount(stompCounter);
    countStart = false;
    stompCounter = 0;
  }


  if (DEBOUNCE && timeDebounce) {
    timeDebounce = false;
    debounceTimer = millis();
  }

  if (millis() - debounceTimer > debounceTime) {
    DEBOUNCE = false;
    timeDebounce = true;
  }

}


void actionBasedOnStompCount(int stompCount) {
  if (stompCount == 1) {
    //user wants to read address
    message = "Read Address";

    playSound("Address Sound.WAV");
    
  } else if (stompCount == 2) {
    //user wants to ring doorbell
    message = "Ring Doorbell";

    playSound("Doorbell Sound.WAV");
    
  } else if (stompCount == 3) {
    //user wants to leave message
    message = "Reading Options Again";

    playSound("Options Sound.WAV");

  }
}

 

Assignment 7: Sound with Meaning – James Kyle

Concept:

For my sound with meaning, I tried to recreate the jaws “duuuuuuuuuh…duh” tune on an interrupt as an indication that someone is approaching. I tried creating my own tune using the tone() function and playing F1 for 1 second and then and F1# for a quarter second but it wasn’t realistic enough for me. I ended up using an SD card loaded with some ambient noise and a clip from the Jaws theme song to indicate the states of the device. When the button is pressed, it triggers the Jaws snippet and then goes back to ambient noise once the tune is finished.

Demo Video:

Circuit Diagram:

Woes:

I faced a lot of issues getting the sound to play from the amplifier and here are some things I learned. First and foremost, the amplifier needs to be tuned to the correct volume so that you can clearly hear you sound. When it wasn’t tuned correctly, I would get a lot of static or muffled versions of the sound I was trying to play. I also ran into the problem of playing the tunes too fast which gave me a beeping machine error type f sound. From experimenting with the circuit and code a little, I have pinned this problem down to playing tunes too fast in succession (not waiting for the tunes to finish before playing it again).

 

Beats Headphones Noises – James Kyle

When pairing and unpairing my headphones, I noticed that it plays a little jingle that ends on a high note when paired and a low note when unpaired. It seems natural at this point that a higher note at the end would indicate a connection because I have been using them for so long but it makes me wonder if that would have always been the case. I don’t know much about music and chord progressions but I would think that a higher note ending would convey happiness consistent with how the manufacturer wants you to feel when you use their products, and a lower note ending would convey sadness or some type of negative motion for stopping use of their product.

 

On the topic of pairing noises, I noticed the sound another speaker makes when in paring mode resembles a radar blip which is a cool experience to give the user. I think the sound adds a layer to the experience of pairing a device by tying it to searching for non-visible objects.

Kinetic Crit: Stove-top Temperature Indicator – James Kyle

Problem:

Stoves are a common household product for anyone with a kitchen and extremely useful in giving a person some autonomy over their life. However, they are by nature extremely hazardous because they deal with high temperatures. Although many electric stovetops have indication lights for when the surface is hot, these indications do nothing to help a blind user know when the stovetop is hot and not safe to touch.

 

Solution:

To solve this problem, I have decided to make a stove top temperature indicator that relies on sound and proximity to the stovetop to indicate when a surface is safe to touch. A set of ultrasonic rangers determines when an object is near and how far away the object is from the stove top. If the stovetop is too hot, the device then uses vibration motors to bounce around and audibly indicate the ensuing danger. The user also has an override button to turn the vibration off because the device cannot discern the difference between intentional and unintentional object placement on the hot stove.

 

Proof of Concept:

For a proof of concept, I have built a smaller version with one ultrasonic for each x and y components of the stove top. There are only two vibration motors as well just to show the possibility. In a real version of the concept, the vibration motors would be placed in a resonating surface such as a bell to create more noise and alert the user better.

Top down view of entire setup

 

Demo:

 

Code:

//Temperature stuff
const int tempSensor = A0;
float stoveTemp;
float stoveTempThreshold = 40; // [*C]
float c1 = 1.009249522e-03, c2 = 2.378405444e-04, c3 = 2.019202697e-07;


//Vibration motor stuff
const int vibrationMotor1 = 6;
const int vibrationMotor2 = 7;
const int vibrationMotorCount = 2;
int vibrationMotors[vibrationMotorCount] = {vibrationMotor1, vibrationMotor2};


//Object detection stuff
const int trigPin1 = 12;
const int echoPin1 = 13;
const int trigPin2 = 8;
const int echoPin2 = 9;
float dangerDistance = 5; //Distance threshold


//E-stop stuff
const int stopButton = 3;


//Logical stuff
bool stoveIsHot;
bool objectOnStove;
bool userOverride = false;


//Debounce Stuff
unsigned long debounceTimer = 0;
unsigned long debounceDelay = 100;
bool prevButtonState = false;
bool currButtonState = false;
bool lookForPress = true;



void setup()
{

  pinMode(trigPin1, OUTPUT); // Sets the trigPin as an Output
  pinMode(echoPin1, INPUT); // Sets the echoPin as an Input
  pinMode(trigPin2, OUTPUT); // Sets the trigPin as an Output
  pinMode(echoPin2, INPUT); // Sets the echoPin as an Input
  pinMode(tempSensor, INPUT);
  pinMode(stopButton, INPUT_PULLUP);

  pinMode(vibrationMotor1, OUTPUT);
  pinMode(vibrationMotor2, OUTPUT);

  attachInterrupt(digitalPinToInterrupt(stopButton), stopNoise, LOW);

  Serial.begin(9600); // Starts the serial communication

}

void loop()
{

  if (prevButtonState != currButtonState) {
    debounceTimer = millis();
    prevButtonState = currButtonState;
  }

  if (millis() - debounceTimer > debounceDelay) {
    lookForPress = true;
  }


  if (!userOverride) {
    stoveIsHot = checkStoveTemp();

    if (stoveIsHot) {
      objectOnStove = checkStoveSpace();
    }
  } else {
    vibrateMotors(0);
  }
  


}



int getDistance(int trigPin, int echoPin) {

  long duration;
  int distance;

  // Clears the trigPin
  digitalWrite(trigPin, LOW);
  delayMicroseconds(2);
  // Sets the trigPin on HIGH state for 10 micro seconds
  digitalWrite(trigPin, HIGH);
  delayMicroseconds(10);
  digitalWrite(trigPin, LOW);
  // Reads the echoPin, returns the sound wave travel time in microseconds
  duration = pulseIn(echoPin, HIGH);
  // Calculating the distance
  distance = duration * 0.034 / 2;
  // Prints the distance on the Serial Monitor
  return distance;

}


bool checkStoveTemp() {

  const int stoveTempThreshold = 40;
  static float stoveTemp;
  static bool DANGER;

  stoveTemp = analogRead(tempSensor);
  stoveTemp = log(stoveTemp);
  stoveTemp = (1.0 / (c1 + c2 * stoveTemp + c3 * stoveTemp * stoveTemp * stoveTemp));
  stoveTemp = stoveTemp - 353.15;
  //Serial.println(stoveTemp);


  if (stoveTemp > stoveTempThreshold) {
    if (!DANGER) {
      DANGER = true;
      Serial.println("WARNING...DANGER!!");
    }
  } else {
    if (DANGER) {
      DANGER = false;
      Serial.println("Surface is safe to touch now");
    }
  }

  return DANGER;
}




bool checkStoveSpace() {

  static bool moveHand = false;
  static const int numberOfDistanceSensors = 2;
  static float distanceReadings[numberOfDistanceSensors];
  static int distanceSensorPins[2 * numberOfDistanceSensors] = {trigPin1, echoPin1, trigPin2, echoPin2};


  //Check ultrasonic rangers
  for (int i = 0; i < numberOfDistanceSensors; i++) {
    int currTrig = distanceSensorPins[2 * i];
    int currEcho = distanceSensorPins[2 * i + 1];
    distanceReadings[i] = getDistance(currTrig, currEcho);
  }


  //Indicate danger if something is close to stove
  for (int i = 0; i < numberOfDistanceSensors; i++) {
    if (distanceReadings[i] < dangerDistance) {

      Serial.println("MOVE YOU HAND!!");

      //Make warning noise
      vibrateMotors(1);

      return true;

    }
  }


  //Turn off warning noise
  vibrateMotors(0);


  return false;
}

void vibrateMotors(int vibrationSpeed) {
  for (int i = 0; i < vibrationMotorCount; i++) {
    digitalWrite(vibrationMotors[i], vibrationSpeed);
  }
}


void stopNoise() {

  if (lookForPress) {
    currButtonState = !currButtonState;
    lookForPress = false;

    if (userOverride) {
      userOverride = false;
    } else {
      userOverride = true;
    }

  }

}

 

 

 

 

 

Assignment 6 – James Kyle

Daylight Level Indicator:

For this assignment, I have made a theoretical daylight level sensor. The inspiration for this comes from my lab being in the basement of Ansys. We have no windows down there so I can easily get sucked into work and stay there for hours without realizing what time of day it is and/or whether it is time to pack up and call it a day. This daylight level indicator is meant to solve this problem by sensing the amount of daylight coming from outside and physically turning a dial to show the time of day. Theoretically, this dial would take the shape of a circle with night and day images on either side. The servo would turn to the appropriate position to indicate how close the sun is to setting or rising depending on the level of light outside and the time of day.

#include <Servo.h>


int hours = 0;

const int measurementNumber = 20;
int measurementCount = 0;
int lightMeasurements[measurementNumber];
const int lightLevel = A0; //Light level pin (photoresistor pin)

int lightBottomBound = 0;
int lightTopBound = 0;


Servo myServo;
int SERVOPIN = 9;



void setup() {

  pinMode(lightLevel, INPUT);

  myServo.attach(9);

}

void loop() {

  //Update measurement counter
  if (measurementCount < measurementNumber) {
    measurementCount += 1;
  } else {
    measurementCount = 0;
  }


  //Update light level average
  int lightLevelSum = 0;
  for (int i = 1; i < measurementNumber; i++) {
    lightLevelSum += lightMeasurements[i];
  }

  int lightLevelAverage = lightLevelSum / measurementNumber;



  //Update light level measurements
  int currentLightLevel = analogRead(lightLevel);
  lightMeasurements[measurementCount] = currentLightLevel;




  //Change bounds if light level is outside of bounds
  if (lightLevelAverage < lightBottomBound) {
    lightBottomBound = lightLevelAverage;
  }

  if (lightLevelAverage > lightTopBound) {
    lightTopBound = lightLevelAverage;
  }


  //Write to servo
  int servoAngle = map(lightLevelAverage, lightBottomBound, lightTopBound, 0, 180);
  myServo.write(servoAngle);


  delay(1000);

}

 

Assignment 5 – James Kyle

For this assignment, I tried taking in force readings from a force sensitive resistor (FSR) and displaying them in the Serial Monitor. I split the force readings into 3 groups so that it displays a visual indication of the amount of pressure with words. The data wasn’t very noisy so there was no need for smoothing it. I created a threshold evenly spaced between the highest and lowest values. I then displayed “High”, “Medium”, or “Low” depending on the regime the pressure was in.