Wednesday, April 27, 2016

Final Project

All information/phases will be edited into this post.

Idea and planning

We want to build a biometric door lock. The lock will be unlocked by RFID and log who has access to the room and who has accessed the room. The lock will also broadcast (with the user's permission) when the user enters that way non-authorized people can enter as well. The lock will be used on a door to a shared club room that many people use, but few people have keys to. So if people want access to the room, they need to either have a key or someone already there. With this mechanism, we can extend how many people have access to the room without needing a key.

Target audience: members of our club
Solve need: yes, giving members of the club access to the room
Enhance need: yes, unlocking doors
Parts: RFID reader, strong motor to turn the lock, something for wireless communication
Libraries/Frameworks: Arduino for reading the lock/sending controls to the lock, Processing for taking lock information and logging/broadcasting
Team members:
Chaitanya Garg: senior, CS/Math major, good at software development
Giorgio Pizzorni: senior, CS/Math major, good at <whatever's on his blog post>
Division of labor: collaborate on everything

Execution and implementation

Stuff done till end of day May 4th:

RFID reader parts arrived, soldered together materials, reads an RFID card and outputs to Arduino's Serial Monitor on the computer.
Next task is to attach the servo to a spool. Spool has been created already, but hot gluing the servo and the spool failed, and so did soldering. Next attempts are crazy glue and double sided sticky tape.

Design decisions for the prototype:
Values will be hardcoded (for the time being due to the time constrant).
A demo for the registration and login page will be created (probably in Python given we both know Python).

Our demonstration will have one of our friends use the RFID lock.

Next update to be posted when we have made progress.

Stuff done till end of day May 6th:

The RFID lock actually works now! Here are a bunch of videos documenting our incremental progress throughout the day. Our code uses the Adafruit PN532 library and pretty much rips off their example code (some servo controlling is tacked on). Our next tasks are to put the RFID reader outside the door (however we don't want to do this until the presentation itself due to security/liability concerns) and to create the prototype for the registration and logging interface.


Take 1

Take 2

Take 3

Take 4

Take 5

Take 6 does not exist because numbers are hard.

Take 7

To be edited once more progress has been made.

Stuff done till end of day May 9th:

Mockup of registration and logger application has started.

Stuff done till end of day May 10th:

Created presentation. Put contraption in a box so that circuitry isn't visible. Had friend try out our reader. Finished mockup for registration and logger application.

Internals

Step by step guide

Other notes

Link to presentation (be sure to sign in with your NYU credentials):

Most of the content of the presentation should answer all questions, but here are some extra details.
  • The first RFID reader we bought just didn't work, either due to lack of a compatible library, or lack of our knowledge of how to wire it. The one that did work took us a while to figure out how to use it.
  • We ended up using Adafruit PN532 library located at https://github.com/adafruit/Adafruit-PN532 and used the sample code in the file iso14443a_uid (and built our servo controlling code on top of it).
  • Lesson learned: even if you buy a part that is advertised as no soldering required, you will need to solder something related to it sooner or later.
  • Challenge faced: on our first attempt with the contraption, the servo (the entire servo, not just the arm) moved rather than the doorknob rotating. We needed a way to keep the servo in place.
  • Lesson learned: crazy glue is really good at sticking things together.
  • Challenge faced: we had no idea how to use the RFID reader at the start.
  • Lesson learned: hardware documentation is minimal at best, non-existant at worst.
  • Lesson learned: order parts really early, delivery estimates are wrong.
  • Lesson learned: look for hardware documentation/sample code/sample usage before purchasing.
  • Change of plans: originally we wanted to have multiple RFID cards, but they didn't work. 
  • We ended up only getting 1 RFID card to actually function, the other 5 we had simply weren't being read.
  • Final thought on the project: I hope the department doesn't notice that we did this.
  • Final thought on physical computing: the idea behind the field is nice, there have been great products created, but it doesn't seem like the field for me. While I enjoyed the projects in class, I prefer programming in the more virtual sense.

Monday, April 18, 2016

Data Labs

For this assignment, I decided to use as few libraries as possible just to get used to Processing without having my hand held while using it. While the data may not look visually appealing, the message is there and the visualization is still present.

Single series data

Data source: http://www.u-s-history.com/pages/h980.html
Data description: population over time
Data visualization: scatter plot
x axis is time (increase right)
y axis is population (increase down)

code (moved to IntelliJ):
package single_series;

import processing.core.*;

public class single_series extends PApplet
{
    private int[] year;
    private int[] population;

    public void settings()
    {
        size(600, 400);
    }

    public void setup()
    {
        surface.setResizable(true);
        String[] lines = loadStrings("single_series/data.txt");
        int len = lines.length;
        year = new int[len];
        population = new int[len];
        for (int i = 0; i < len; i++)
        {
            String[] split_line = lines[i].split(";");
            year[i] = Integer.parseInt(split_line[0]);
            population[i] = Integer.parseInt(split_line[1]);
            print(year[i] + " " + population[i] + "\n");
        }
    }

    public void draw()
    {
        fill(255);
        strokeWeight(4);
        textSize(14);
        text("time", 300, 40);
        text("population", 80, 200);

        for (int i = 0; i < year.length; i++)
        {
            int y = year[i] - 1600;
            int p = (population[i] / 1000000) + 50;
            line(y, p, y + 5, p);
        }
    }
}

Multiple Series data

Data sources:
https://en.wikipedia.org/wiki/Gareth_Bale#Club (red)
https://en.wikipedia.org/wiki/Lionel_Messi#Club (green)
https://en.wikipedia.org/wiki/Cristiano_Ronaldo#Club (blue)
Data description: goals (for the club) over time
Data visualization: line chart
x axis is time (increase right)
y axis is goals (increase down)

code:
package multiple_series;

import processing.core.*;

import java.util.*;

class SoccerPlayer
{
    String name;
    Map<Integer, Integer> goals;

    public SoccerPlayer(String name)
    {
        this.name = name;
        goals = new TreeMap<Integer, Integer>();
    }

    public void addGoal(int year, int num_goals)
    {
        goals.put(year, num_goals);
    }
}

/**
 * Assume we only have 3 soccer players
 */
public class multiple_series extends PApplet
{
    private List<SoccerPlayer> players;

    public void settings()
    {
        size(600, 400);
    }

    public void setup()
    {
        surface.setResizable(true);
        players = new ArrayList<SoccerPlayer>(3);
        String[] lines = loadStrings("multiple_series/data.txt");
        int i = -1;
        for (String line : lines)
        {
            if (line.indexOf("#") == 0)
            {
                // implies the line is the name of the player

                String player_name = line.substring(2, line.length());
                SoccerPlayer player = new SoccerPlayer(player_name);
                players.add(player);
                i++;
            }
            else
            {
                SoccerPlayer curr_player = players.get(i);
                if (line.length() == 0)
                    continue;
                String[] split_line = line.split(",");
                int year = Integer.parseInt(split_line[0]);
                int num_goals = Integer.parseInt(split_line[1]);
                curr_player.addGoal(year, num_goals);
            }
        }
        System.out.println();
    }

    public void draw()
    {
        fill(255);
        textSize(14);
        text("time", 200, 70);
        text("goals", 50, 150);

        for (int i = 0; i < players.size(); i++)
        {
            SoccerPlayer player = players.get(i);
            int prevx = -1;
            int prevy = -1;
            setRGB(i, 0);
            for (Map.Entry<Integer, Integer> e : player.goals.entrySet())
            {
                int year = e.getKey();
                int num_goals = e.getValue();
                int y = (year - 2000) * 10 + 100;
                int p = num_goals + 100;
                // draw line from previous point
                if (prevx != -1 && prevy != -1)
                {
                    strokeWeight(1);
                    line(prevx + 1, prevy, y, p);
                }
                strokeWeight(6);
                line(y, p, y + 1, p);
                prevx = y;
                prevy = p;
            }
        }
    }

    private void setRGB(int i, int j)
    {
        // terrible code I know
        switch (i)
        {
            case 0:
                stroke(255 - j, 0, 0);
                break;
            case 1:
                stroke(0, 255 - j, 0);
                break;
            case 2:
                stroke(0, 0, 255 - j);
                break;
            default:
                break;
        }
    }
}


Real time data

Data source: https://www.wunderground.com/US/NY/New_York.html
Data description: temperature over time
Data visualization: scatter plot

x axis is time (increase right)
y axis is temperature, degrees F, (increases up)

Details:
the first picture is about 15 seconds after program start
the second picture is about 45 seconds after program start
every second, the program makes a web request to the web server, and parses out the temperature and last updated offset areas of the page, and graphs them
time is determined by current time minus last updated offset minus program start time
the reason for the weird gaps that don't seem uniform is just how long it takes the website to display a new reading

a library called jsoup was used to make requesting and parsing HTML simple
originally I planned on using the HTTP library on Processing's website, but that did not have parsing capabilities (at least not at first glance)
I decided against using a graphical library as the statement at the start of the post, but used an HTTP/HTML library as that's not Processing-only (or at least under the domain of Processing)

code:
package real_time;

import java.io.*;

import org.jsoup.*;
import org.jsoup.nodes.*;
import org.jsoup.select.*;
import processing.core.*;

class WeatherInfo
{
    private long seconds_displacement;
    private double temperature;

    WeatherInfo(long time_displacement, double temperature)
    {
        this.seconds_displacement = time_displacement;
        this.temperature = temperature;
    }

    long get_seconds_displacement()
    {
        return seconds_displacement;
    }

    double get_temperature()
    {
        return temperature;
    }
}

public class real_time extends PApplet
{
    // considered the "0" time
    private long start_time;

    public void settings()
    {
        size(600, 400);
    }

    private WeatherInfo connectToSite()
    {
        Document weather_doc = null;
        try
        {
            weather_doc = Jsoup.connect("https://www.wunderground.com/US/NY/New_York.html").get();
        }
        catch (IOException e)
        {
            e.printStackTrace();
            System.err.println("Unable to connect to weather website, exiting");
            System.exit(1);
        }
        Elements weather_info = weather_doc.select("#curTemp span");
        String weather_str = weather_info.get(1).text();
        Elements updated_info = weather_doc.select("#update-time");
        String updated_str = updated_info.get(0).text();
        double temperature = Double.parseDouble(weather_str);
        if (!updated_str.contains("second"))
        {
            System.out.println(updated_str);
            System.err.println("Unable to parse time displacement");
            return null;
        }
        long curr_time = System.currentTimeMillis();
        long weather_time = Long.parseLong(updated_str.substring(0, updated_str.indexOf(" ")));
        curr_time -= 1000 * weather_time;
        return new WeatherInfo((curr_time - start_time) / 1000, temperature);
    }

    public void setup()
    {
        surface.setResizable(true);
        // give ourselves 1 minute headroom in case weather info is old
        start_time = System.currentTimeMillis() - 60000;
    }

    public void draw()
    {
        fill(255);
        textSize(14);
        text("time", 200, 30);
        text("temperature", 30, 70);
        WeatherInfo w = connectToSite();
        if (w == null)
        {
            return;
        }
        try
        {
            Thread.sleep(1000);
        }
        catch (InterruptedException e)
        {
            e.printStackTrace();
        }
        strokeWeight(4);
        long time = w.get_seconds_displacement();
        float temp = (float)w.get_temperature();
        line(time + 100, 200 - temp, time + 101, 200 - temp);
        System.out.println("Drawing: " + time + ", " + temp);
    }
}

Saturday, April 9, 2016

Motor Labs

Control servo via Processing

(no questions)

Control DC motor via potentiometer

Here's the code used:
const int pot_pin = A0;
const int motor_pin = 9;

void setup()
{
  // put your setup code here, to run once:
  pinMode(pot_pin, INPUT);
  pinMode(motor_pin, OUTPUT);
  Serial.begin(9600);
}

void loop()
{
  // put your main code here, to run repeatedly:
  int motor_speed = map(pot_read(), 0, 1023, 0, 255);
  int time = 500;
  analogWrite(motor_pin, motor_speed);
  delay(time);
}

int pot_read()
{
  int pot_val = analogRead(pot_pin);
  Serial.println("Potentiometer read: " + (String)pot_val);
  return pot_val;
}

Control DC motor via Processing

Here's the code used:
const int motor_pin = 9;

void setup()
{
  // put your setup code here, to run once:
  pinMode(motor_pin, OUTPUT);
  Serial.begin(9600);
}

void loop(){
  // put your main code here, to run repeatedly:
  if (Serial.available() > 0)
  {
    int read_in = Serial.read();
    int motor_speed = map(read_in, 0, 255, 0, 255);
    int time = 100;
    analogWrite(motor_pin, motor_speed);
  }
  delay(10);
}

and

import processing.serial.*;

Serial arduinoPort;

void setup(){
  size(320, 240);
  background(0,255,255);
  println(Serial.list());
  
  String portName = Serial.list()[0];
  arduinoPort = new Serial(this, portName, 9600);
}

void draw() {
  int servoVal = (int) 255 * mouseX / width;
  arduinoPort.write(servoVal);
  println(servoVal);
  
  background(255,255,0);
  fill(255,0,0);
  ellipse(mouseX, mouseY, 50, 50);
}

Saturday, March 26, 2016

Midterm Project

The information related to the in-class presentation was/will be given in class itself. The in-class presentation is available at https://docs.google.com/presentation/d/1f2q-bnjgmTGTGDaIORXxPnVoGM0FWm_QAYhCOtABfzI/ for reference purposes (you will need to sign in with your NYU credentials).

The product is called "eLSAR" which stands for "thE Light Sensitive AlaRm." It is a light sensitive alarm. The interaction we are mediating is between human and light, and waking up. The device senses the amount of ambient light and wakes up the human depending on the device's configuration. Input is the ambient light in the room, the on-off switch on the device, and the configuration knob on the device. Output is either no sound, or sound.

I worked with Giorgio Pizzorni on this project.

Plan for where the pieces should go (outside the casing)

Plain Arduino in casing
Potentiometer (10k ohm) soldered with wires
Speaker (8 ohm 0.25W) soldered with wires
Photoresistor soldered with wires
Switch soldered with wires
Completed upper casing with parts put in their places
Complete circuitry
Completed product (back left open to cause less headaches)
Testing the potentiometer

Testing the speaker

Testing the photoresistor

Testing the switch

Demo of the product:

Giorgio was, in fact, not very happy


Timeline of our project:
Wednesday (March 23): Finalized product idea
Thursday: Went to Leslie eLab for 3D printer/laser cutter training; our idea for 3D printing the casing was shot down (too large, would take too long), idea instead to laser cut wood/other material
Friday: Came up with list of components/materials to buy. Attempted to book laser cutter, system did not let us book over the weekend, earliest was Monday 10 AM, laser cutter idea scrapped.
Saturday: Purchased components/materials. Built casing (foam board rather than wood) and circuitry.
Sunday: Tested product. Wrote documentation.

Regarding our process and the various phases:
Concept/parts/planning: decided to build an alarm clock, thought of various parts to buy with Amos's help, and purchased them at Tinkersphere.
User observation/implement concept revisions/technical development iteration until complete: our various iterations are documented above where we test out each individual component and our final product. No obvious bugs/glitches/errors were found on first revision, so we stuck with it. One functionality we decided to change was rather than having a snooze/setup switch, instead have an on/off switch.
Demonstration of functioning project: documented above.

Presentation related information:
Located in the presentation above (link at top of post).

Sunday, March 20, 2016

Interactive Toy









Where's Waldo? created by Chaitanya Garg and Giorgio Pizzorni



Find all 5 of the characters, press the paper where they are, a tone will play for each one found and a victory tone will play when all 5 are found (and the game resets).

We tried to 3D print a box to hold our circuitry, however the resources at the Leslie eLab were closed to us over break. Instead, we ended up soldering wires and the force sensitive resistor.

Intended user: basically anyone. A child can play it and so can an adult (it's not the easiest game if you don't cheat!).
Limitations and capabilities: assumes no limitations and capabilities of a human being
How design of toy takes above into account: requires sight and ability to press; an infant can play this game, but their strategy would probably just be random guessing
Affordances the toy offers: Press the game board for input, keep the entire game on a table, listen to sounds for output

Here is the code we used:
/**
 * Interactive toy: Where's Waldo?
 * Created by Chaitanya Garg and Giorgio Pizzorni
 */

#include "pitches.h"
#define THRESHOLD 100
#define TONE_TIME 500

const int fsr[5] = { A0, A1, A2, A3, A4 };
const int tones[5] = { NOTE_C5, NOTE_D5, NOTE_E5, NOTE_F5, NOTE_G5 };
const int speaker = 11;
bool activated[5] = { false, false, false, false, false };

void reset()
{
  for (int i = 0; i < 5; i++)
  {
    activated[i] = false;
  }
}

void setup()
{
  Serial.begin(9600);
  
  // initialize input/output pins
  pinMode(speaker, OUTPUT);
  for (int i = 0; i < 5; i++)
  {
    pinMode(fsr[i], INPUT);
  }
}

void play(int note, int duration)
{
  tone(speaker, note, duration);
}

void loop()
{
  int readings[5] = { 0, 0, 0, 0, 0 };
  bool pressed[5] = { false, false, false, false, false };
  for (int i = 0; i < 5; i++)
  {
    readings[i] = analogRead(fsr[i]);
    if (readings[i] > THRESHOLD)
    {
      pressed[i] = true;
      activated[i] = true;
    }
  }
  
  // if two are pressed at once, picks the "first" one based on array index
  int pressed_fsr = index_of(pressed, 5, true);
  if (pressed_fsr == -1)
  {
    // implies that no fsr was pressed
    return;
  }
  
  // play tone corresponding to which fsr was pressed
  play(tones[pressed_fsr], TONE_TIME);
  
  // now check if all 5 are pressed
  if (all_pressed())
  {
    victory();
  }
}

void victory()
{
  // play all 5 tones in order
  
  for (int i = 0; i < 5; i++)
  {
    play(tones[i], TONE_TIME);
    delay(TONE_TIME);
  }
  
  reset();
}

int index_of(bool *arr, int size, bool val)
{
  for (int i = 0; i < size; i++)
  {
    if (arr[i] == val)
    {
      return i;
    }
  }
  return -1;
}

bool all_pressed()
{
  for (int i = 0; i < 5; i++)
  {
    if (activated[i] == false)
    {
      return false;
    }
  }
  return true;
}

Wednesday, March 16, 2016

Processing Labs

Oscillating circle


1. Java
2. Technically speaking, the only distinction is scope (where they live and die). Global variable has a scope of the file (or more depending on your linker), and a local variable has a scope of whatever block you're in.
3. void which means no return type
4. When the ellipse reaches the top or bottom of the screen, reverse its direction of movement

Potentiometer controlled ellipse


(no questions)

Etch-a-Sketch



(no questions)
Here is my code:

Processing:
import processing.serial.*;

Serial arduino;
int x;
int y;
int x_prev;
int y_prev;

void setup()
{
  x = 0;
  y = 0;
  x_prev = 0;
  y_prev = 0;
  
  size(320, 240);
  background(0,255,255);
    
  String port = Serial.list()[0];
  arduino = new Serial(this, port, 9600);
}

void draw()
{
  fill(0, 0, 0);
  if (!(x_prev == 0 && y_prev == 0))
  {
    line(x_prev, y_prev, x, y);
  }
  x_prev = x;
  y_prev = y;
}

void serialEvent(Serial port)
{
  try
  {
    String input = port.readStringUntil('*');
    if (input != null)
    {
      int[] vals = int(splitTokens(input, ",*"));
      x = vals[0];
      y = vals[1];
      println("x: " + x + ", y: " + y);
    }
  }
  catch (Exception e)
  {
    
  }
}

Arduino:

const int pot_left = A0;
const int pot_right = A1;

void setup()
{
  Serial.begin(9600);
}

void loop()
{
  int pot_left_val = analogRead(pot_left);
  int pot_right_val = analogRead(pot_right);
  int left = map(pot_left_val, 0, 1023, 127, 0);
  int right = map(pot_right_val, 0, 1023, 127, 0);
  // pack the two values together into one
  Serial.write(44);
  Serial.print(left, DEC);
  Serial.write(44); // comma
  Serial.print(right, DEC);
  Serial.write(42); // asterisk
}