Assignment Four

The pomodoro technique

Password: MakingThingsInteractive

This was a bit of a tricky project for me. I had several idea’s that seemed to bypass either the Arduino or p5.js completely. So, after my board kicked the bucket at 3 in the morning, I went back to the drawing board. Lately, I’ve been struggling with task management in terms of mustering up the courage to even begin. I was researching memory and multitasking for another class, when I stumbled upon something called The Pomodoro Technique. This is a technique that uses a timer to break down tasks into intervals of 25 minutes. The idea is to focus on something for 25 minutes straight, take a short break, then tackle another 25 minutes.

Using an RFID reader and tags, I sought to physicalize the interaction of changing tasks to intentionally bring awareness to the switch. These tags are labeled with my current class schedule, plus a personal passion project. You can scan in the task you want to focus on and a full screen time will take over the browser window. When 25 minutes have passed, the user will be notified by LED light. In addition, a small accomplishment dot is added to the task. You can collect up to three right now, however if you switch tasks in the middle of another, you will not get a dot.

Arduino Code:
#include <SPI.h>
#include <MFRC522.h>

#define SS_PIN 10
#define RST_PIN 9

MFRC522 newReader(SS_PIN, RST_PIN); // Create MFRC522 instance.
String id = "";

//--------NEOPIXEL SETUP --------
#include <Adafruit_NeoPixel.h>
#ifdef __AVR__
#include <avr/power.h>
#endif
static const int PIN = 8;
static const int NUMPIXELS = 1;
Adafruit_NeoPixel pixels = Adafruit_NeoPixel(NUMPIXELS, PIN, NEO_GRBW + NEO_KHZ800);

//--------INCOMING CASE STATE--------
int caseState;

//--------OUTGOING CASE STATE--------
static const int neutralState = 0;
static const int thesisState = 1;
static const int rmeState = 2;
static const int mtiState = 3;
static const int cdfState = 4;
static const int personalState = 5;
int sendState = neutralState;

//--------TIME--------
unsigned long lastSampleTime = 0;
unsigned long sampleInterval = 300000; // in ms 5 minutes

void setup() {
Serial.begin(9600); // Initialize serial communications with the PC
SPI.begin(); // Init SPI bus
newReader.PCD_Init(); // Init MFRC522 card
Serial.println("Scan PICC to see UID and type...");
pixels.begin();
}

void loop() {
unsigned long now = millis();
pixelOff();

if (Serial.available() > 0) {
caseState = Serial.read();
}

getID();

if (lastSampleTime + sampleInterval < now) {
lastSampleTime = now;
if (caseState == 6) {
pixelBlink();
} else {
pixelOff();
}
}
}

void pixelBlink() {
pixels.setPixelColor(0, pixels.Color(138, 70, 215));
pixels.show(); // This sends the updated pixel color to the hardware.
delay(500);
pixels.setPixelColor(0, pixels.Color(0, 0, 0));
pixels.show(); // This sends the updated pixel color to the hardware.
delay(500);
}

void pixelOff() {
pixels.setPixelColor(0, pixels.Color(0, 0, 0));
pixels.show(); // This sends the updated pixel color to the hardware.
}

void getID() {
// Getting ready for Reading PICCs
if ( ! newReader.PICC_IsNewCardPresent()) { //If a new PICC placed to RFID reader continue
return 0;
}
if ( ! newReader.PICC_ReadCardSerial()) { //Since a PICC placed get Serial and continue
return 0;
}

// Serial.print("Card UID:");

for (byte i = 0; i < newReader.uid.size; i++) {
// Create a RFID Hexdecimal String
id += String(newReader.uid.uidByte[i], HEX);
// Serial.print(newReader.uid.uidByte[i] < 0x10 ? " 0" : " ");

}
// Convert to Uppercase
id.toUpperCase();

// If it is this card, do something
if (id == "B0A167A") {
sendState = thesisState;
// Serial.println("Card One: Thesis");
// Serial.print("State: ");
// Serial.println(sendState);
}
if (id == "B0DECE7A") {
sendState = rmeState;
// Serial.println("Card Two: Responsive Mobile Environments");
// Serial.print("State: ");
// Serial.println(sendState);
}
if (id == "702E89C") {
sendState = mtiState;
// Serial.println("Card Three: Making Things Interactive");
// Serial.print("State: ");
// Serial.println(sendState);
}
if (id == "3078E49B") {
sendState = cdfState;
// Serial.println("Card Four: Communication Design Fundamentals");
// Serial.print("State: ");
// Serial.println(sendState);
}
if (id == "50611A9C") {
sendState = personalState;
// Serial.println("Card Five: Personal Projects");
// Serial.print("State: ");
// Serial.println(sendState);
}
Serial.write (sendState);

// Reset Id
id = "";
// Serial.println();
newReader.PICC_HaltA();
newReader.PCD_StopCrypto1();
}
p5.js Code:
//---Serial---
var serial;
var portName = '/dev/cu.usbmodem1411';
//---Incoming Case State---
var caseState;
//---Outgoing Case State---
var accomplishState;

//---Color---
var c;
var thesisColor;
var rmeColor;
var mtiColor;
var cdfColor;
var personalColor;

//---Canvas---
var xLen = 1000;
var yLen = 700;

//---Squares & Grid---
let squares = [];
let bubbles0 = [];
let bubbles1 = [];
let bubbles2 = [];
let bubbles3 = [];
let bubbles4 = [];

//---Time---
var time = 1500;
var timer0 = time;
var timer1 = time;
var timer2 = time;
var timer3 = time;
var timer4 = time;

//---Counter---
var count0 = 0;
var count1 = 0;
var count2 = 0;
var count3 = 0;
var count4 = 0;

//---Converts Time to minutes and seconds---
function convertTime(s){
var minutes = floor(s / 60);
var seconds = s % 60;
return nf(minutes,2) + "m " + nf(seconds,2) + "s"; // formats to look like "00m 00s"
}

function setup() {
createCanvas(xLen, yLen);
serial = new p5.SerialPort();
// now set a number of callback functions for SerialPort
serial.on('list', printList);
serial.on('connected', serverConnected);
serial.on('open', portOpen);
serial.on('data', serialEvent);
serial.on('error', serialError);
serial.on('close', portClose);

serial.list();
serial.open(portName);

// setting task squares as well as accomplished "dots" through objects
for (let i=0; i<5; i++){
for(let j =0; j<3; j++){
let x = 0 + 200 * i;
let y = 550;
let w = 200;
let h = 150;
let gridX = 50;
let gridY = 625;
let offset = 50;
let gridR = 10;

squares[i] = new Square(x,y,w,h);

bubbles0[j] = new Bubble(gridX + offset*j, gridY, gridR);
bubbles1[j] = new Bubble(200 + gridX + offset*j, gridY, gridR);
bubbles2[j] = new Bubble(400 + gridX + offset*j, gridY, gridR);
bubbles3[j] = new Bubble(600 + gridX + offset*j, gridY, gridR);
bubbles4[j] = new Bubble(800 + gridX + offset*j, gridY, gridR);

}
}
}

function draw() {
// put drawing code here
// background(255);

checkIncoming(); // checks the incoming state and runs the proper timer
statusBar(); // keeps the tabs with what tasks, right now classes
bubbleKeeper(); // keeps the circles that tick when time has been accomplished

}

function bubbleKeeper(){

for(let i=0; i < bubbles0.length; i++){ // accomplish bubbles for thesis
noStroke();
// bubbles[i].show(0);
if (count0 == 1){
bubbles0[0].show(0);
}
if (count0 == 2){
bubbles0[0].show(0);
bubbles0[1].show(0);
}
if (count0 == 3){
bubbles0[0].show(0);
bubbles0[1].show(0);
bubbles0[2].show(0);
}
}

for(let i=0; i < bubbles1.length; i++){ // accomplish bubbles for RME
noStroke();
// bubbles[i].show(0);
if (count1 == 1){
bubbles1[0].show(0);
}
if (count1 == 2){
bubbles1[0].show(0);
bubbles1[1].show(0);
}
if (count1 == 3){
bubbles1[0].show(0);
bubbles1[1].show(0);
bubbles1[2].show(0);
}
}

for(let i=0; i < bubbles2.length; i++){ // accomplish bubbles for MTI
noStroke();
// bubbles[i].show(0);
if (count2 == 1){
bubbles2[0].show(0);
}
if (count2 == 2){
bubbles2[0].show(0);
bubbles2[1].show(0);
}
if (count2 == 3){
bubbles2[0].show(0);
bubbles2[1].show(0);
bubbles2[2].show(0);
}
}

for(let i=0; i < bubbles3.length; i++){ // accomplish bubbles for CDF
noStroke();
// bubbles[i].show(0);
if (count3 == 1){
bubbles3[0].show(0);
}
if (count3 == 2){
bubbles3[0].show(0);
bubbles3[1].show(0);
}
if (count3 == 3){
bubbles3[0].show(0);
bubbles3[1].show(0);
bubbles3[2].show(0);
}
}

for(let i=0; i < bubbles4.length; i++){ // accomplish bubbles for Passion Projects
noStroke();
// bubbles[i].show(0);
if (count4 == 1){
bubbles4[0].show(0);
}
if (count4 == 2){
bubbles4[0].show(0);
bubbles4[1].show(0);
}
if (count4 == 3){
bubbles4[0].show(0);
bubbles4[1].show(0);
bubbles4[2].show(0);
}
}

}

function statusBar(){
textSize(20);
textAlign(CENTER);
textStyle(BOLD);
var textY = 575;

//---Using these to change the color of the timer background based on the class
thesisColor = color(236,195,211);
rmeColor = color(247,161,171);
mtiColor = color(250,116,86);
cdfColor = color(252,195,135);
personalColor = color(242,240,181);

//---Setting up the squares based on color
for(let i =0; i<squares.length; i++){
noStroke();
squares[0].show(thesisColor);
squares[1].show(rmeColor);
squares[2].show(mtiColor);
squares[3].show(cdfColor);
squares[4].show(personalColor);
}

fill(0);
noStroke();
text("THESIS", 100, textY);
text("RME", 300, textY);
text("MTI", 500, textY);
text("CDF", 700, textY);
text("PASSION", 900, textY);

}

function checkIncoming(){
textSize(200);
textAlign(CENTER);

if (caseState == 1){ // THESIS
if (frameCount % 60 == 0 && timer0 > 0) { // if the frameCount is divisible by 60, then a second has passed. it will stop at 0
timer0 --;
}
if (timer0 == 0){ // if time reaches zero, task accomplished
timer0 = time; // timer resets to 25minutes (1500 seconds)
count0++; // counter adds one
accomplishState = 6; // sends notification to arudino to light up light
} else{
accomplishState = 5;
}
if (count0 > 3){
count0=0; // resets after three rounds
}

background(color(236,195,211));
fill(0);
noStroke();
text(convertTime(timer0), width/2, height*.5);

}

if (caseState == 2){ //RME
if (frameCount % 60 == 0 && timer1 > 0) { // if the frameCount is divisible by 60, then a second has passed. it will stop at 0
timer1 --;
}
if (timer1 == 0){
timer1 = time;
count1++;
accomplishState = 6;
} else{
accomplishState = 5;
}
if (count1 > 3){
count1=0;
}

background(color(247,161,171));
fill(0);
noStroke();
text(convertTime(timer1), width/2, height*.5);

}

if (caseState == 3){ // MTI
if (frameCount % 60 == 0 && timer2 > 0) { // if the frameCount is divisible by 60, then a second has passed. it will stop at 0
timer2 --;
}
if (timer2 == 0){
timer2 = time;
count2++;
accomplishState = 6;
} else{
accomplishState = 5;
}
if (count2 > 3){
count2=0;
}

background(color(250,116,86));
fill(0);
noStroke();
text(convertTime(timer2), width/2, height*.5);

}

if (caseState == 4){ // CDF
if (frameCount % 60 == 0 && timer3 > 0) { // if the frameCount is divisible by 60, then a second has passed. it will stop at 0
timer3 --;
}
if (timer3 == 0){
timer3 = time;
count3++;
accomplishState = 6;
} else{
accomplishState = 5;
}
if (count3 > 3){
count3=0;
}

background(color(252,195,135));
fill(0);
noStroke();
text(convertTime(timer3), width/2, height*.5);
}

if (caseState == 5){ // PERSONAL
if (frameCount % 60 == 0 && timer4 > 0) { // if the frameCount is divisible by 60, then a second has passed. it will stop at 0
timer4 --;
}
if (timer4 == 0){
timer4 = time;
count4++;
accomplishState = 6;
} else{
accomplishState = 5;
}
if (count4 > 3){
count4=0;
}
background(color(242,240,181));
fill(0);
noStroke();
text(convertTime(timer4), width/2, height*.5);
}

serial.write(accomplishState);

}

class Square{ // object class for bottom squares
constructor(x,y,w,h){
this.x = x;
this.y = y;
this.w = w;
this.h = h;
}

show(c){
fill(c);
rect(this.x, this.y, this.w, this.h);
}
}

class Bubble { // onject class for dots
constructor(x, y, r) {
this.x = x;
this.y = y;
this.r = r;
}

show(c) {
// stroke(90);
// strokeWeight(1);
fill(c);
ellipse(this.x, this.y, this.r * 2);
}
}

//-----SERIAL FUNCTIONS-----

function printList(portList) {
for (var i = 0; i < portList.length; i++) {
print(i + " " + portList[i]);
}
}

function serverConnected() {
print('serverConnected');
}

function portOpen() {
print('portOpen');
}

function serialEvent() {

var incoming = serial.read();
caseState = incoming;

}

function serialError(err) {
print('serialError ' + err);
}

function portClose() {
print('portClose');
}

 

Assignment Four Files: Fritzing, p5.js, Arduino

Assignment 4

Room Light Controller 

Background: 

My idea for this project stemmed from my own room here in Pittsburgh. I currently turn on three different lights to get the ideal lighting in my room. There are two lamps and a string of starry lights. This past weekend, I was about to watch a movie and found myself wishing that I didn’t have to keep getting up and down to adjust the various lights according to my tv activity (movie, video games, tv, etc…). I’ve also been finding that the dark winter mornings have been making it difficult for my body to want to wake up. These problems were my inspiration for this light system project.

Description: 

My light system starts with a simple GUI with three big buttons. The modes are sleep, bathroom, and movie. The idea behind the sleep button is that you would click it and the lights would turn on however many hours later you wish to sleep. The idea behind the bathroom mode is that the lights would turn on for about 3-5 minutes to guide you to the bathroom at night when you need to go in the middle of the night and then turn off, eliminating your need to touch a switch. The movie lights will turn on for about two hours and then turn off for ideal movie lighting. My vision is that there would be one long strand of lights lining my room ceiling. My demo is a prototype that I hope to further develop.

Photo of Arduino set-up 

Closer view of the three bread boards–they are all wired the same way 

Screenshot of simple GUI 

Final physical output

Process:

I started by using Javascript to create these buttons. I drew out the dimensions and figured out what if statements I needed to write to catch each click in the three distinct circles. Next, I sent a distinct serial key for each button to the Arduino to turn on three corresponding lights. I found that drawing the GUI and having them each turn on a single light was a smooth and promising process for me. I tried to remain aware of color blindness as I chose colors for the buttons and LEDs. My next step would turn out to be my most time consuming: figuring out how to program three different timer settings.

Problems:

It turns out there isn’t a particularly easy way to run timed programs on the Arduino. After many hours of reading various Arduino community threads and forums, I realized that there wouldn’t be a library to do what I wanted. I tried a few timer libraries, but many of them used strange loop formats that would not work with my p5.js set-up. Setting up three LED bread boards also posed issues as some of LEDs were different shades and overall they were not especially bright. I knew this issue would most likely remain unresolved until the next prototype due to material and time constraints.

Solution: 

After class on Tuesday, I wrote the timer into the javascript which turned out to be a much easier fix. I created functions for each mode, as well as functions to turn off each mode. I utilized a javascript function called setTimeout to turn off the LEDs after an amount of time. I used the same simple LEDs because this was our first prototype.

Result: 

My project is a prototype of system for a much bigger room. Each button controls a light square. Sleep is green, bathroom is white, and movie is yellow. Sleep turns on after 8000ms, bathroom turns on for 2000ms and movie turns on for 5000ms. I scaled back the time because if the timing works for seconds, it will work if I multiply it out and set it for hours. This also made it able for me to debug and run tests quickly. I laser cut three boxes to try and enhance the weak LED lighting. I chose frosted acrylic because I had used it in a previous project and it created a glowing effect.

Videos: 

The left button is ‘Sleep’, the middle button is ‘Bathroom’, and the right button is ‘Movie’. The LEDs are very dim and hard to see when the room is not dark. The buttons can be clicked at the same time and they will run their respective timers. The bathroom light is on for the shortest amount of time, the movie light is on for about double the bathroom light time, and the sleep light process is the longest. It is turned off for “8 hours” (in my simulation, it is 8000 ms) and then turns on indefinitely. I decided to leave it on because it is meant to wake the user up, so the timing will vary depending on the user and their morning schedule.

Light system with GUI.

Closer view of the light boxes

Future Work: 

I hope to scale this project up a lot. My dream would be to connect it to a long bright LED strand and fix up the GUI control system. Each mode should be able to enter custom times and there should be a reset/all off button. I would want to add a few more sensors that would provide other assistance, as well.

 

Code:

assignment4

Assignment 2

It’s running smoothly, so here it is.

I wanted to make a ghost chat which would allow the user to interact with “ghosts” that I programmed to be “detected” by a photosensor. By pushing the button, the user can ask questions and get an answer. I also initially wanted to program a theme song for each ghost that could run in the background or when someone asks for the ghosts’ favorite song, but that took a backseat when I realized how confused I actually was by interrupts. If I were to really make this perfect, I’d probably find a different sensor, and figure out a way to have the ghost answer questions that the user defines, as opposed to pre-programmed answers to pre-programmed questions.

ghost_chat_a2

Assignment 3

It’s still not working, but this is what I have.

I decided to integrate mouse clicking into my assignment 2 ghost chat, and bring the push-button interrupt into the p5js part of the equation, as opposed to leaving it with the arduino. This means that the user should be able to click around on the screen to ask questions. My issue could be that my code is wonky, because I haven’t yet been able to make p5js actually take information from the arduino and print it successfully using my own sketch, or it could be technical:

I thought the problem had solved itself, but it seems that the Beats have spawned more beats, and p5.serialcontrol likes to open that port instead of the usbmodem even after I’ve closed the weird beats port and opened the other one instead. Sometimes it stays with the usb modem for a few minutes, and I can run a few sketches, and sometimes it doesn’t feel the need to do anything I ask, and I wrestle with it for an hour or two.

So here are my sketches:

ghost_chat_p5js 2

and the fritzing:

Virtural “Etch a Sketch”

For this assignment, I used two potentiometers to simulate the “Etch A Sketch” (Wikipedia_”Etch A Sketch”). Using two potentiometer, one controls left and right movement, and the other controls the up and down movement, the browser window is going to serve as a canvas for the “Etch A Sketch” board.

 

  • Parts list:
    • Potentiometer (10K)  x2
    • Adafruit M0 Metro Express
    • Bunch of jumper wires

 

 

Fritzing Sketch:

 

Code (Arduino Part):

const int leftRightPot = A4; //with green LED
const int upDownPot = A5; // with blue LED

const int tol = 100;

int leftRightVal = 0;
int upDownVal = 0;

int valHi = 1024;
int valLo = 0;


//******************************
//Setup for Arduino to run
void setup() {
 // put your setup code here, to run once:
 pinMode(leftRightPot , INPUT);
 pinMode(upDownPot, OUTPUT);

Serial.begin(9600);
}


//******************************
//The operating loop
void loop() {
 // put your main code here, to run repeatedly:
 leftRightVal = reMapped(analogRead(leftRightPot));
 upDownVal = reMapped(analogRead(upDownPot));

// Serial.print("leftRightVal = ");
 // Serial.println(leftRightVal);

//Modified from class Note 5
 Serial.print("leftRightVal,");
 Serial.print(leftRightVal);
 Serial.print(",upDownVal,");
 Serial.println(upDownVal);
}


//***********************************
//function used to remap input value into one
int reMapped(int input){
 int output = map(input,0,1023,0,255);
 return output;
}

 

Code (js part):

var cnv;
var serial;
var portName = 'COM4';
var dataRead;
 //@TODO : find out what port name it is

//***************
//Here are some global variables:
//@TODO: Figure out how to make them function in OOP
var curPosX = 0;
var curPosY = 0;
var leftRightVal = 255/2;
var upDownVal = 255/2;

const rightUpThresh = 150;
const leftDownThresh = 100;

var xPoint=[];
var yPoint=[];

// ****************
//Basic setup for serial port and canvas
//******************
function setup(){
 
 serial = new p5.SerialPort();
 // now set a number of callback functions for SerialPort
 serial.on('list', printList);
 serial.on('connected', serverConnected);
 serial.on('open', portOpen);
 serial.on('data', serialEvent);
 serial.on('error', serialError);
 serial.on('close', portClose);

serial.list();
 serial.open(portName);

//Here is canvas setup
 createCanvas(windowWidth, windowHeight);

var x = (windowWidth - width)/2;
 var y = (windowHeight - height)/2 ;
 cnv.position(x,y);


 background ("white");
}



//**********************
//Put drawing functions in here
function draw() {
 // put drawing code here
 mouseSetPt();
 drawWithPot();
 drawPts();
 
}


//**************************
//click mouse and reset the drawing point
function mouseSetPt(){
 //get the point where the mouse clicks
 var mouseClickX = mouseX;
 var mouseClickY = mouseY;

//update the point in the array
 xPoint = xPoint + [mouseClickX];
 yPoint = yPoint + [mouseClickY];

//update the current/new position to start
 curPosX = xPoint[xPoint.length-1];
 curPosY = yPoint[yPoint.length-1];

}


//****************************
//use potentiometer to draw (only moving left/right and up/down)
function drawWithPot(){
//@TODO: find a better way to store tese values, like tuples?

//check right and left
 if (leftRightVal >= rightUpThresh){
 curPosX = curPosX + 1;
 }
 else if (leftRightVal <= leftDownThresh){
 curPosX = curPosX - 1;
 }

//check up and down
 if (upDownVal >= rightUpThresh){
 curPosY = curPosY + 1;
 }
 else if (upDownVal <= leftDownThresh){
 curPosY = curPosY - 1;
 }

//update the position list
 var prePosX = xPoint[xPoint.length-1];
 var prePosY = yPoint[yPoint.length-1];

if (curPosX != prePosX || curPosY != prePosY){
 xPoint = xPoint + [curPosX]
 yPoint = yPoint + [curPosY]
 }

}

//************************
//Draw all the points that is saved in the array
function drawPts(){
 for ( i = 0; i < xPoint.length ; i++){
 var xPointPos = xPoint[i];
 var yPointPos = yPoint[i];
 point(xPointPos, yPointPos);
 }
}

//*****************************
//All server related
function printList(portList) {
 // for (var i = 0; i < portList.length; i++) {
 // print(i + " " + portList[i]);
 // }
}

function serverConnected()
{
 print('serverConnected');
}

function portOpen()
{
 print('portOpen');
}

function serialEvent(){

var strRead = serial.readStringUnil('\r\n');

if (strRead.length > 0 ) {
 var potStr = split(strRead, ',');

if (potStr.length == 4) { // ignore any tiny strings
 leftRightVal = potStr[1];
 upDownVal = potStr[3];
 }
 }

}


function serialError(err)
{
 print('serialError ' + err);
}

function portClose()
{
 print('portClose');
}

 

Zip -Etch A Sketch