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.
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]; }