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:
VIDEO
VIDEO
Code:
p5.js
- RGB to HSV Conversion Function from http://www.javascripter.net/faq/rgb2hsv.htm
let latestData = "waiting for data" ;
//Lightsource vector [on/off, x, y, Lamp Radius, Shine Radius]
let updateLightSource = true ;
let doorStepColor = [ 50 , 50 , 50 ] ;
let lockColor = [ 255 , 0 , 0 ] ;
let heaterColor = [ 255 , 150 , 173 ] ;
let livingRoomWidth = width - 600 ;
let livingRoomHeight = height - 100 ;
let doorStepHeight = 200 ;
let lockX = width/ 2 + livingRoomWidth/ 2 + 10 ;
let lockY = height/ 2 + livingRoomHeight/ 2 - doorStepHeight/ 2 - 60 ;
createCanvas ( width, height ) ;
lightSources [ 0 ] = [ 0 , table1X, table1Y, 40 , 200 ] ;
lightSources [ 1 ] = [ 0 , table2X, table1Y, 40 , 200 ] ;
serial = new p5. SerialPort ( ) ;
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 ] ) ;
print ( "Serial Port is Open" ) ;
print ( "Serial Port is Closed" ) ;
latestData = "Serial Port is Closed" ;
function gotError ( theerror ) {
let currentString = serial. readLine ( ) ;
if ( !currentString ) return ;
console . log ( currentString ) ;
latestData = currentString;
if ( latestData != currentMessage ) {
currentMessage = latestData;
handleInputData ( latestData ) ;
function handleInputData ( latestData ) {
if ( latestData/ 10 > = 70 ) {
currTemp = latestData - 700 ;
else if ( latestData/ 10 > = 60 ) {
goalTemp = latestData - 600 ;
else if ( latestData/ 10 > = 4 ) {
doorStepColor = [ 255 , 255 , 255 ] ;
} else if ( latestData == 40 ) {
doorStepColor = [ 50 , 50 , 50 ] ;
else if ( latestData/ 10 > = 3 ) {
toggleLockColor ( lockState ) ;
} else if ( latestData == 31 ) {
toggleLockColor ( lockState ) ;
else if ( latestData/ 10 > = 2 ) {
} else if ( latestData == 21 ) {
else if ( latestData/ 10 > = 1 ) {
} else if ( latestData == 11 ) {
//================================
rect ( width/ 2 , height/ 2 ,livingRoomWidth,livingRoomHeight ) ;
rect ( width/ 2 + livingRoomWidth/ 2 , height/ 2 + livingRoomHeight/ 2 - doorStepHeight/ 2 , 100 , 75 ) ;
fill ( doorStepColor [ 0 ] ,doorStepColor [ 1 ] ,doorStepColor [ 2 ] ) ;
rect ( width/ 2 + livingRoomWidth/ 2 + doorStepWidth/ 2 + 5 , height/ 2 + livingRoomHeight/ 2 - doorStepHeight/ 2 ,doorStepWidth,doorStepHeight ) ;
fill ( lockColor [ 0 ] , lockColor [ 1 ] ,lockColor [ 2 ] ) ;
rect ( lockX, lockY, 10 , 30 ) ;
//============================
drawHeater ( heaterX, heaterY, 200 , 80 , PI/ 2 ) ;
drawCouch ( 800 , 200 , 80 , 200 , 0 , 50 , 40 , 80 ) ;
drawCouch ( 980 , 300 , 80 , 80 , PI/ 2 , 50 , 40 , 80 ) ;
drawCouch ( 620 , 300 , 80 , 80 , -PI/ 2 , 50 , 40 , 80 ) ;
drawTable ( 800 , 300 , 80 , 200 , PI, 30 , 40 , 30 ) ;
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++ ) {
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 ] ) ;
text ( "House Setting: " + goalTemp, 50 , 100 , 150 , 100 ) ;
text ( "Room Temperature: " + currTemp, 30 , 180 , 190 , 100 ) ;
function updateLightState ( lightIndex ) {
lightIndex = Number ( lightIndex ) ;
if ( lightSources [ lightIndex ] [ 0 ] == 1 ) {
lightSources [ lightIndex ] [ 0 ] = 0 ;
sendMessage ( currentMessage ) ;
} else if ( lightIndex == 1 ) {
sendMessage ( currentMessage ) ;
else if ( lightSources [ lightIndex ] [ 0 ] == 0 ) {
lightSources [ lightIndex ] [ 0 ] = 1 ;
sendMessage ( currentMessage ) ;
} else if ( lightIndex == 1 ) {
sendMessage ( currentMessage ) ;
function mousePressed ( ) {
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 distVector = [ dist2Lamp1,dist2Lamp2,dist2Heater,dist2Lock ] ;
for ( i = 1 ; i < 4 ; i++ ) {
if ( distVector [ minDistIndex ] > distVector [ i ] ) {
if ( dist2Lamp1 < lightSources [ 0 ] [ 3 ] * 2 ) {
if ( dist2Lamp2 < lightSources [ 1 ] [ 3 ] * 2 ) {
if ( currX < lockX + 5 && currX > lockX - 5 ) {
if ( currY < lockY + 15 && currY > lockY - 15 ) {
function turnLightOn ( x, y, lampRadius, shineRadius ) {
//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 ] ) ;
stroke ( hsvConversion [ 0 ] , hsvConversion [ 1 ] + 40 , hsvConversion [ 2 ] + 20 ) ;
function drawCouch ( x, y, width, height, theta, couchH, couchS, couchB ) {
let armWidth = couchWidth* 1 / 5 ;
let armHeight = couchHeight* 4 / 5 ;
let backWidth = couchWidth;
let backHeight = couchHeight* 2 / 5 ;
// translate(transRadius - transRadius*cos(theta), -transRadius*sin(theta));
fill ( couchH, couchS, couchB ) ;
rect ( 0 , 0 , couchWidth, couchHeight, 0 , 0 , 10 , 10 ) ;
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 ) ;
fill ( couchH, couchS, couchB - 25 ) ;
rect ( 0 , ( armHeight/ 2 - couchHeight/ 2 ) + ( backHeight/ 2 - couchHeight/ 2 ) , backWidth, backHeight, 0 , 0 , 30 , 30 ) ;
function drawTable ( x, y, width, height, theta, couchH, couchS, couchB ) {
// translate(transRadius - transRadius*cos(theta), -transRadius*sin(theta));
fill ( couchH, couchS, couchB ) ;
rect ( 0 , 0 , tableWidth, tableHeight ) ;
fill ( couchH, couchS, couchB + 40 ) ;
rect ( 0 , 0 , tableWidth - 10 , tableHeight - 10 ) ;
function drawLamp ( x, y, radius, couchH, couchS, couchB ) {
fill ( couchH, couchS, couchB ) ;
function drawHeater ( x, y, width, height, theta ) {
let heaterHeight = height;
let finWidth = heaterWidth/numOfFins - 4 - 8 /numOfFins;
let finHeight = height/ 2 - 10 ;
let tempDiff = goalTemp - currTemp;
heaterColor = [ 220 + 2 *tempDiff, 180 , 200 -tempDiff ] ;
// translate(transRadius - transRadius*cos(theta), -transRadius*sin(theta));
fill ( heaterColor [ 0 ] , heaterColor [ 1 ] , heaterColor [ 2 ] ) ;
rect ( 0 , 0 , heaterWidth, heaterHeight, 5 , 5 , 5 , 5 ) ;
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 ) ;
function toggleLockColor ( ) {
sendMessage ( currentMessage ) ;
sendMessage ( currentMessage ) ;
function rgb2hsv ( r,g,b ) {
//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!' ) ;
if ( r < 0 || g < 0 || b < 0 || r > 255 || g > 255 || b > 255 ) {
alert ( 'RGB values must be in the range 0 to 255.' ) ;
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 ) ) ;
// 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;
return [computedH,computedS*100,computedV*100];
function sendMessage(message) {
for (i = 0; i < 1; i++) {
/ /Output message to serial
serial. write ( int ( message ) ) ;
/*
- 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));
}
}
/*
- 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
- Temperature Reading Code: https://www.circuitbasics.com/arduino-thermistor-temperature-sensor-tutorial/
Goal: bring control of home to digital interface on p5.js
- ON/OFF through p5.js (100%)
- State control based on presence in room (0%)
- Holding temperature (70%)
- Indication of heater and ac states through p5.js (0%)
- 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
- Control ON/OF based on presence on couch (0%)
- Notify user of house happenings through p5.js (0%)
- Take input through p5.js (0%)
3 - lock state --> 1=unlocked; 0=locked
5 - heater on? --> 1=yes; 0=no
#define presenceIndicator 8
long double prevTempTime = 0;
int tempTimerDuration = 2; // [s]
unsigned long debounceTimerLock = 0;
int debounceTimeLock = 300;
bool DEBOUNCE_LOCK = false ;
bool firstCountLock = true ;
volatile int lockState = 0; // 0 => locked; 1 => unlocked
long double prevUnlockTime = 0;
unsigned long debounceTimer1 = 0;
unsigned long debounceTimer2 = 0;
volatile int lamp1state = 0;
volatile int lamp2state = 0;
bool receivedMessage = false ;
// put your setup code here, to run once:
pinMode ( lamp1pin, OUTPUT ) ;
pinMode ( lamp2pin, OUTPUT ) ;
pinMode ( coldPin, 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 ) ;
//if main computer sent a command
if ( Serial. available () > 0 ) {
if ( millis () / 1000.0 - prevTempTime > tempTimerDuration ) {
prevTempTime = millis () / 1000.0 ;
if ( DEBOUNCE_LOCK && firstCountLock ) {
debounceTimerLock = millis () ;
toggleLockState ( lockState ) ;
if ( millis () - debounceTimerLock > debounceTimeLock ) {
if ( millis () - prevUnlockTime > unlockTimer && lockState ) {
toggleLockState ( lockState ) ;
if ( DEBOUNCE1 && firstCount1 ) {
debounceTimer1 = millis () ;
currentMessage = 10 + lamp1state;
sendMessage ( currentMessage ) ;
if ( millis () - debounceTimer1 > debounceTime1 ) {
if ( DEBOUNCE2 && firstCount2 ) {
debounceTimer2 = millis () ;
currentMessage = 20 + lamp2state;
sendMessage ( currentMessage ) ;
if ( millis () - debounceTimer2 > debounceTime2 ) {
float c1 = 1.009249522e-03 , c2 = 2.378405444e-04 , c3 = 2.019202697e-07 ;
Vo = analogRead ( tempSensor1 ) ;
R2 = R1 * ( 1023.0 / ( float ) Vo - 1.0 ) ;
T = ( 1.0 / ( c1 + c2 * logR2 + c3 * logR2 * logR2 * logR2 )) ;
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 ) ;
currentMessage = 51; //Tell p5.js heater is on / AC is off
sendMessage ( currentMessage ) ;
} else if ( goalTemp < T && heaterON ) {
digitalWrite ( hotPin, LOW ) ;
digitalWrite ( coldPin, HIGH ) ;
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 ) {
} else if ( plateReading < standingThreshold && someoneOnPressurePlate ) {
someoneOnPressurePlate = false ;
digitalWrite ( presenceIndicator, LOW ) ;
sendMessage ( currentMessage ) ;
if ( timingStand && millis () - standingTime > standingTimeThreshold && !someoneOnPressurePlate ) {
someoneOnPressurePlate = true ;
digitalWrite ( presenceIndicator, HIGH ) ;
sendMessage ( currentMessage ) ;
void handleLockButton () {
void toggleLockState ( int lockState ) {
digitalWrite ( lockLight, LOW ) ;
for ( int i = 0; i < numOfBlinks; i++ ) {
digitalWrite ( unlockLight, HIGH ) ;
digitalWrite ( unlockLight, LOW ) ;
digitalWrite ( unlockLight, HIGH ) ;
prevUnlockTime = millis () ;
//Indicate locking state to p5
sendMessage ( currentMessage ) ;
//Turn lock light on and unlock light off
digitalWrite ( unlockLight, LOW ) ;
for ( int i = 0; i < numOfBlinks; i++ ) {
digitalWrite ( lockLight, HIGH ) ;
digitalWrite ( lockLight, LOW ) ;
digitalWrite ( lockLight, HIGH ) ;
//Indicate locking state to p5
sendMessage ( currentMessage ) ;
lamp2state = !lamp2state;
digitalWrite ( lamp2pin, lamp2state ) ;
lamp1state = !lamp1state;
digitalWrite ( lamp1pin, lamp1state ) ;
void parseInput ( int inputData ) {
if ( y == 10 && prevInMessage != 10 ) { //1
digitalWrite ( lamp1pin, LOW ) ;
} else if ( y == 11 && prevInMessage != 11 ) { //1
digitalWrite ( lamp1pin, HIGH ) ;
} if ( y == 20 && prevInMessage != 20 ) { //1
digitalWrite ( lamp2pin, LOW ) ;
} else if ( y == 21 && prevInMessage != 21 ) { //1
digitalWrite ( lamp2pin, HIGH ) ;
} else if ( y == 30 && prevInMessage != 30 ) { //2
toggleLockState ( lockState ) ;
} else if ( y == 31 && prevInMessage != 31 ) { //3
toggleLockState ( lockState ) ;
void sendMessage ( int message ) {
for ( int i = 0; i < 10; i++ ) {
/*
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);
}
}
/*
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);
}
}