a digital scan of a 35mm film image of a processing sketch running on an LCD
Skip to Content

Streetview Disaster

An unlikely glitch in a Google Streetview sequence captured on Route 30 in Rankin Pennsylvania.

The current theory is that the an orange bag snagged on the camera array. This would also explain the blurred, unusually large text overlaying the view. Maybe the StreetView-blocking British villagers could deploy a barrage of wind-born privacy prophylactics in their next campaign.

Further discussion is available in the Reddit comment thread.

Here's a screen capture for posterity, in case the glitch is fixed:

October 22 2009 at 3 AM

Interaction Observations

Pick a piece of interactive technology in public, used by multiple people. Write down your assumptions as to how it's used, and describe the context in which it's being used. Watch people use it, preferably without them knowing they're being observed. Take notes on how they use it, what they do differently, what appear to be the difficulties, what appear to be the easiest parts. Record what takes the longest, what takes the least amount of time, and how long the whole transaction takes. Consider how the readings from Norman and Crawford reflect on what you see.

[ I'll write up the commentary soon... ]

October 21 2009 at 8 AM

Lab: Serial Duplex

Another serial lab, this time about duplex: handling multiple inputs at once.

I don't have an accelerometer handy, and my ultrasonic sensor is giving weird values, so I went with two potentiometers as analog input. Uninspired, but workable.

The multi-format serial print program seems very handy. Shows a number of potential interpretations of the same serial data. Cornflake lets you toggle between different representations of byte data... but I couldn't find a way to get everything side by side at once... maybe I overestimate how useful this is.

I'm keeping the code here for reference:

  1. int analogPin = 0;
  2.  int analogValue = 0;                // integer to print
  3.  
  4.  void setup() {
  5.    // open serial communications at 9600 bps
  6.    Serial.begin(9600);
  7.  }
  8.  
  9.  void loop() {
  10.    // read the analog input, divide by 4:
  11.    analogValue = analogRead(analogPin) /4;
  12.  
  13.    // print in many formats:
  14.    Serial.print(analogValue, BYTE);     // Print the raw binary value analogValue
  15.    Serial.print('\t');                  // print a tab
  16.    Serial.print(analogValue, BIN);      // print the ASCII encoded binary analogValue
  17.    Serial.print('\t');                  // print a tab
  18.    Serial.print(analogValue, DEC);      // print the ASCII encoded decimal analogValue
  19.    Serial.print('\t');                  // print a tab
  20.    Serial.print(analogValue, HEX);      // print the ASCII encoded hexadecimal analogValue
  21.    Serial.print('\t');                  // print a tab
  22.    Serial.print(analogValue, OCT);      // print the ASCII encoded octal analogValue
  23.    Serial.println();                    // print a linefeed and carriage return
  24.  
  25.    delay(10);
  26.  }

Back to the lab... how can we distinguish between multiple sensors sending back serial?

There are several techniques to choose from:

Punctuation
Put a character between each value, and then send a newline to terminate the "packet" of info. We always know that the first item in the sequence is sensor 0, the second is sensor 1, and so on.

Demo code worked fine, just had to set bgcolor and change the sensor mapping to work well with the pots instead of the accelerometer for which the code was intended. Also made a few aesthetic tweaks: noStroke() and smooth() keeps the ellipse looking nice, and moving backgroung() out of the draw loop gives an etch-a-sketch effect well suited to the potentiometers.

(Worth noting: In Processing, setting myPort.bufferUntil('\n'); handily prevents serialEvent() from firing until a newline character is read.)

Call and Response (Handshaking)

Similar idea, but the Arduino waits for a message from processing before it sends data. More hassle than just sending the data perpetually, but maybe this makes sense when bandwidth is limited (wireless?) or your device has several different modes (e.g. send one byte from processing to request data from one sensor, then a different byte to get data from another sensor.).

Update: The lab explains it... Call-and-response works well when you need to push around binary data. With punctuation only, you risk a collision between your punctuation values and the binary data. Also, if we're reading data slower than it's coming in, the buffer of unread serial data can get too long, which means there will be lag before the most recent data is read. (Investigate this further to figure out why, exactly, the draw() loop is a rotten place to write serial handling code.)

October 20 2009 at 11 PM

Lab: Serial Output

Now for the serial output lab:

Worked great, no issues. I got lazy and skipped the breadboard since the pot can plug right in to power / ground / analog without any additional components. I tweaked the Processing code to print a longer (wider) history of the output, and to draw the signal in a nostalgic shade of oscilliscope-green.

This is going to be very handy... it already went to good use when Arturo and I wanted to see what the output from some FSRs would look like in the context of our media controller project.

October 20 2009 at 8 PM

Capirinha

Julio Terra and I put together a stop-motion video.

October 19 2009 at 8 PM

Sequential Story

Poram and I undertook the illustration of an October 8th exchange on the ITP student email list, involving the theft and retrieval of a student's mug.

Update: Get the backstory and ongoing drama surrounding Clay Shirky's mug thievery at Sarah Dahnke official blog on the topic.














Here's the PSD File (~7 MB).

Many of the images used in the even frames were under a Creative Commons license.

Clay Shirky's head: Pop!Tech
T-Shirt Body: Lee-Sean
Here Comes Everybody book cover: Serdal
Masking tape: Bixentro
Beer taps: Digiart2001
Arturo: TBA

October 12 2009 at 3 AM

Street Level

I'm intrigued by the possibilities for new interfaces things like Google Street View bring up. I was expecting full walkable VR setups and that sort of thing, but the projects that have actually been implemented are a bit more humble.


Here's Tokyo Jogging a clever (if low frame rate) pairing of Street View and the WiiMote.


Stringing together Street View frames to create animation:


Stweet pairs Tweets and Street View


Psuedo-augmented reality panning interface. (This one's pretty old — and while it makes a good demo, actually using it might be too much work compared to the iPhone's more practical implementation.


New York Then and Now


Finally, I wanted to explore retracing a GPS-logged route using Street View for my response to Red's M5 bus assignment. The results were a bit strange, but I think it brings up some of the contention between actually being somewhere and settling for Google's approximation.

If anyone's interested in exploring Street View in code, here's a bit of processing code I wrote which will attempt to download and composite the panoramic image for any given address to lat / lon pairing.

October 8 2009 at 11 AM

Street Scraping

Ever wanted to download Google Street View panoramas programmatically? If you said "yes!", then this is the code for you.

I reverse-engineered their URL query scheme using the Live HTTP Headers plug-in for Firefox. From there, I just parsed through the XML returned to download each jpeg tile and then stitch them together in order.

I used a variation of this code in combination with a few other programs to parse a GPS log, download the street view panorama for each position in the log, and then stitch each frame together into a video documenting my trip on the M5 bus.

  1. import processing.core.*;
  2. import processing.net.*;
  3. import processing.xml.*;
  4.  
  5. // get a hold of this for the xml library
  6. // better way to do this part?
  7. PApplet main = this;
  8.  
  9. void setup() {
  10.    // we usually end up with 3328 x 1664 pixel images
  11.    // let's undersample considerably for a more sane window size
  12.   size(832, 416);
  13.  
  14.   // your google maps api key goes here
  15.   // sign up: http://code.google.com/apis/maps/signup.html
  16.   String apiKey = "REPLACE ME WITH YOUR VERY OWN API KEY";
  17.  
  18.   // pick a location
  19.   // takes an address or a lat / lon
  20.   String location = "721 broadway, new york, ny";
  21.  
  22.   // generate the panorama
  23.   Panorama pano = new Panorama(location, apiKey);
  24.  
  25.   // show the image
  26.   image(pano.fullPano, 0, 0, width, height);
  27.  
  28.   // save the full-res image to the sketch folder
  29.   pano.fullPano.save(location + ".jpg");
  30.  
  31.   // skip the draw loop
  32.   noLoop();
  33. }
  34.  
  35.  
  36. class Panorama {
  37.   float lat;
  38.   float lon;
  39.   String panoId;  
  40.   int imageWidth;
  41.   int imageHeight;
  42.   int tileWidth;
  43.   int tileHeight;
  44.   int xTileCount;
  45.   int yTileCount;  
  46.   PImage[][] tiles;
  47.   PImage fullPano;
  48.   String apiKey;
  49.  
  50.   // constructor
  51.   Panorama(String address, String _apiKey) {
  52.     apiKey = _apiKey;
  53.     String[] location = geocode(address);
  54.     lon = float(location[0]);
  55.     lat = float(location[1]);
  56.  
  57.     fetchInfo(lat, lon);
  58.     buildImage();
  59.   }
  60.  
  61.  
  62.   void fetchInfo(float lat, float lon) {
  63.     String url = "http://maps.google.com/cbk?output=xml&ll=" + lat + "," + lon;
  64.  
  65.     XMLElement response = new XMLElement(main, url);
  66.  
  67.     XMLElement kid = response.getChild(0);
  68.     imageWidth = kid.getIntAttribute("image_width");
  69.     imageHeight = kid.getIntAttribute("image_height");
  70.     tileWidth = kid.getIntAttribute("tile_width");    
  71.     tileHeight = kid.getIntAttribute("tile_height");
  72.     panoId = kid.getStringAttribute("pano_id");
  73.  
  74.     xTileCount = imageWidth / tileWidth;
  75.     yTileCount = imageHeight / tileHeight;
  76.  
  77.     println("Panorama ID: " + panoId);
  78.     println("X Tile Count: " + xTileCount);
  79.     println("Y Tile Count: " + yTileCount);
  80.   }
  81.  
  82.   void buildImage() {
  83.     fullPano = createImage(imageWidth, imageHeight, RGB);
  84.     fullPano.loadPixels();
  85.  
  86.     for(int xPos = 0; xPos <= xTileCount; xPos++) {
  87.       for(int yPos = 0; yPos <= yTileCount; yPos++) {
  88.         // &.jpg fools processing into handling it
  89.         String imageUrl = "http://cbk0.google.com/cbk?output=tile&panoid=" + panoId + "&zoom=3&x=" + xPos + "&y=" + yPos + "&.jpg";
  90.         fullPano.set(xPos * tileWidth, yPos * tileHeight, loadImage(imageUrl));
  91.         println("Loaded tile " + xPos + "::" + yPos);      
  92.       }
  93.     }
  94.  
  95.     fullPano.updatePixels();        
  96.   }
  97.  
  98.   String[] geocode(String address) {
  99.     String cleanAddress =  address.replace(' ', '+');
  100.     String url = "http://maps.google.com/maps/geo?q=" + cleanAddress + "&output=xml&oe=utf8&sensor=false&key=" + apiKey;
  101.     println(url);
  102.     XMLElement location = new XMLElement(main, url);
  103.     XMLElement kid = location.getChild("Response/Placemark/Point/coordinates");
  104.     String rawCoordinates = kid.getContent();
  105.     String[] latlon = shorten(split(rawCoordinates, ',')); // ditch the 0
  106.  
  107.     println(latlon);
  108.  
  109.     return latlon;
  110.   }
  111. }

October 7 2009 at 4 AM

Lab: Servo Motor Control

Next came the servo lab.

I ended up using an LDR as an analog input to set the position of the servo.

Then I switched to a pot for finer control over the servo position. Worked great as a makeshift camera panning platform:


Update: The old long-form pulse method ended up being really useful, since my Digital Combination Lock needed to use a servo and the tone library at the same time. It turns out that both the servo library and the tone library use up the Arduino's available timers... so the only way to use both simultaneously was to fall back to the pulse method outlined in 4.1.

October 6 2009 at 10 PM

Lab: Tone Output

Time for the tone lab.

5. The photoresistors gave me an input range from 33 to 668.

I could only scrounge up a 16 ohm speaker. It was really quiet, so I substituted a 220ohm resistor in place of the 1Kohm resistor specified in the schematic. This gave the volume a bump.

7. Basic tone production using a photocell-based voltage divider

8. Playing series of tones

October 6 2009 at 9 PM