Digital Decomposition of 10 Ruston Close Installation - Source Code
John Wild 2011
john.wild@network.rca.ac.uk
The installation consisted of 3 elements. A programe, writen using Processing, that displays and processes a photograph stored on a webserver. A PHP program that allows Processing to upload and overwrite the photograph on the server. A MaxSona sonic range finder connected to an Arduino that registered the presence of a viewer in the Gallery and sends a message via the serial port.
Bellow is the Source Code for all 3 programes required to make the installation work: -
1) house_installation_Final.pde - applet writen in processing. displays and processes the image.
2) Upload.php - php file stored on the webserver.
3) MaxSona_House.pde - arduino source code to sense the presence of a viewer.
/////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////
// house_installation_Final.pde Source Code.....
// Downloads an image from a webserver and displays it fullscreen.
// If Arduino sensors a viewer - Delete a pixel, recompress as jpg, send back to webserver.
/////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////
// The program uses the, Post To Web Library v0.6, created by seltar [http://libraries.seltar.org/postToWeb/]
//and the FullScreen Library [http://www.superduper.org/processing/fullscreen_api/]
//Plus the Serial library to read data from the Arduino.
import fullscreen.*;
import processing.serial.*;
import processing.pdf.*;
import org.seltar.Bytes2Web.*;
FullScreen fs;
PDFToWeb pdf;
PImage img;
Serial myPort; // Create object from Serial class
int val; // Data received from the serial port
String Iurl = "http://www.***.***/***/saved/image/House.jpg";
int sec=59999;
void setup() {
size(720,576);
background(0, 0, 0);
pdf = new PDFToWeb(this);
String portName = Serial.list()[3];
myPort = new Serial(this, portName, 9600);
frameRate(5);
fs = new FullScreen(this);
fs.enter(); // enter fullscreen mode
}
void draw()
{
img = loadImage(Iurl);// Load the image from the web server into the program.
image(img, 0, 0); // Displays the image
while (sec < 60000){
if ( myPort.available() > 0) // If serial data is available
{
val = myPort.read(); // read it and store it in val
if (val == 1) //If the Arduino detects someone
{
color white = color(255,255,255);
// Selects a random pixle
int rand_width = int (random(width));
int rand_height = int (random(height));
color c = img.get(rand_width, rand_height);
// check pixel is not already white.
if (c == white)
{
int i=0;
while (i < 1)
{
rand_width = int (random(width));
rand_height = int (random(height));
c = img.get(rand_width, rand_height);
if (c != white)
{
i=2;
}
}
}
img.set(rand_width, rand_height, white); // Destroys random pixel
image(img, 0, 0); // Displays the image
pdf.addPage();
save_to_web();
sec=60000;
}
}
sec++;
}
sec=0;
}
void save_to_web() // Sends the image back to the webserver and over rights the original
{
String url = "http://www.***.***/***/Upload.php";
ImageToWeb img = new ImageToWeb(this);
img.save("jpg",true); // Recomposed the image in jpeg file format. This will cause the image quality to deteriate.
img.post("image",url,"House",true,img.getBytes(g));
println("sending Image");
}
/////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////
// Upload.php Source Code.....
// To make house_instalation_Final.pde work you will also have to have the file Upload.php on your server. More info about this code at:- http://libraries.seltar.org/postToWeb/
/////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////
// Constants since these parameters are unlikely to change on each call...
// Maximum 100 chars for file name in input, after filtering
define('MAX_FILE_NAME_LENGTH', 100);
// Roughly 2MB
define('MAX_FILE_SIZE', 2000000);
// Where ever your save folder is located - from the place this file is places - no leading slash
$destDir = 'saved/';
// Folder from the root - for using the popup function
$rootDir = '/House/';
/**
* Handle an upload field/file.
* Reject files too big (constant MAX_FILE_SIZE), filter out uploaded file name
* (remove path, reduce charset, reject incorrect file types (actually, extensions),
* truncate given name), then prepend unique identifier (date/time, good for sorting)
* and move the uploaded file to the given directory.
*
* @param $fieldName the name of the input type="file" tag in HTML.
* There can be several such inputs in a form.
* @param $destinationDir where to put the uploaded file (absolute path).
* Might be always the same place or depend on other parameters like a login name.
* @param $allowedExtensions an array looking like array('.png', '.gif', '.jpg', '.jpeg')
* @return An array with two elements. If error, the first element is an empty string,
* the second one is the error message. Otherwise, the first element is the file name
* and the second one is the uploaded file path.
*/
function HandleUploadedFile($fieldName, $destinationDir, $allowedExtensions)
{
global $debugData; // To trace actions. Comment out the lines with this variable for real use
if (isset($_FILES[$fieldName]))
{
// Handle error code
$error = $_FILES[$fieldName]['error'];
switch ($error)
{
case UPLOAD_ERR_OK: // zero
break; // No error, continue process
case UPLOAD_ERR_INI_SIZE: // 1
case UPLOAD_ERR_FORM_SIZE: // 2
return array('', 'File too big!'); // The Web page should indicate upfront the maximum size...
case UPLOAD_ERR_PARTIAL: // 3
return array('', 'Incomplete upload, please retry.');
case UPLOAD_ERR_NO_FILE: // 4
return array('', 'No file! Give a file in the upload field...');
case UPLOAD_ERR_TMP_DIR: // 6 - No temp folder! :(
case UPLOAD_ERR_CANT_WRITE: // 7 - Can't write! chmod error?
return array('', 'Bad server config! Sorry...');
case UPLOAD_ERR_EXTENSION: // 8 - File upload stopped by extension
return array('', 'Bad file extension.');
default: // Future version of PHP?
return array('', "Error when uploading: $error");
}
// Check size of uploaded file
$tempLocation = $_FILES[$fieldName]['tmp_name'];
$debugData .= "Uploaded file is in: $tempLocation
\n";
$debugData .= "Other info given by browser (size, type): {$_FILES[$fieldName]['size']}, {$_FILES[$fieldName]['type']}
\n";
$fileSize = filesize($tempLocation);
$debugData .= "Real file size: $fileSize
\n";
// Strangely enough, if IE is given a path leading to nowhere, it just sends a 0 byte file!
if ($fileSize == 0) // Might test a minimum size (smallest header size for graphics...)
{
return array('', 'File is empty!');
}
if ($fileSize > MAX_FILE_SIZE)
{
return array('', 'File too big!');
}
// Get original file name
$file = $_FILES[$fieldName]['name'];
$debugData .= "Original file name: $file
\n"; // No HTML escape! :(
// Strip out the path (given by IE, perhaps other browsers -- Firefox and Opera just give the name)
// Most samples I saw use basename() but I found out that it fails to strip a Windows path on a Unix server
// I could have used a str_replace, but I like REs...
// (I gobble anything up to the last sequence of characters not having slash or anti-slash in it)
// Note: ensure magic quotes are disabled or neutralized
$file = preg_replace('!.*?([^\\/]+)$!', '$1', $file);
$debugData .= "Filter 1: $file
\n";
// Filter out all characters that are not alphanumerical, dot and dash
// as they can be troublesome in some OSes.
// A sequence of such chars is replaced by a unique underscore.
$file = preg_replace('/[^a-zA-Z0-9.-]+/', '_', $file);
$debugData .= "Filter 2: $file
\n";
// Split name and extension: gobble everything up to the last dot (file name), then dot and remainder (extension)
// Note that .htaccess has no extension and is a pure filename
if (preg_match('/^(.+)\.([^.]+)$/', $file, $m) == 0)
{
// No match => No dot or nothing before the dot
$extension = '';
}
else
{
$file = $m[1];
$extension = $m[2];
}
$debugData .= "Split: $file $extension
\n";
// Ensure name isn't too long: just truncate it
$file = substr($file, 0, MAX_FILE_NAME_LENGTH);
$debugData .= "Truncated: $file
\n";
// If extension not allowed (could be a CGI file...), discard it
if ($extension != '' && !in_array($extension, $allowedExtensions))
{
return array('', 'File format not allowed.');
}
// Add trailing slash to dest dir, supposed in Unix format
// (if not slash at end, replace last char by itself followed by a slash)
$destinationDir = preg_replace('!([^/])$!', '$1/', $destinationDir);
if(!file_exists($destinationDir)){
if(!mkdir_recursive($destinationDir, 0777)){
return array('', 'Cannot create project folder.' . " (dest.: $destinationDir)");
}
chmod($destinationDir, 0777);
}
// Create a time stamp used as prefix to make the file name unique.
// Might have a conflict if you have millions of users, ie. a probability of having two users
// uploading a file in the same second, supposing they go in the same dir.
// If so, you are able to improve this code! ;-)
// One might want to pass a prefix (or suffix) parameter to this function, eg. to tag the file name
// with the name of the author. Just do it!
//$prefix = date('Ymd-His-');
//$prefix = '';
$destinationFile = $file . ($extension != '' ? '.' . $extension : '');
$debugData .= "Destination file: $destinationFile
\n";
$destinationPath = $destinationDir . $destinationFile;
$debugData .= "Destination path: $destinationPath
\n";
// Move uploaded file from temporary folder to destination
// Overwrite a file of same name there, if any (unless, perhaps server is on Windows, might just fail to move).
// if (!file_exists($destinationFile))
// {
if (!move_uploaded_file($tempLocation, $destinationPath))
{
return array('', 'Failed to move upload file.' . " (dest.: $destinationPath)");
}
chmod($destinationDir.$destinationFile, 0777);
return array($destinationFile, $destinationPath);
// }
}
else
{
return array('', 'Field not found');
}
}
function mkdir_recursive($pathname, $mode)
{
is_dir(dirname($pathname)) || mkdir_recursive(dirname($pathname), $mode);
return is_dir($pathname) || @mkdir($pathname, $mode);
}
$debugData = '';
// Get the title
foreach($_FILES as $title=>$file){
break;
}
unset($file); // just to be sure
$destDir .= $title.'/';
/*
$maxFiles = 1000; // Max number of files accepted in the dest dir
// First, check if we don't have too much files. Not a bad test: since we fix the max size of files, the max total size of the files
// is under control.
$files = @scandir($destDir);
if(is_array($files)){
$files = array_slice($files, 2); // Shift out . and ..
while (count($files) > $maxFiles)
{
$file = array_shift($files);
echo $file . " to remove\n";
unlink($destDir . '/' . $file);
}
}
*/
$result = HandleUploadedFile($title, './'.$destDir, array('jpg', 'jpeg'));
/*
if ($debugData != '')
{
echo "
Debug data:
$debugData
uploader
\n"; if ($result[0] == '') { echo "Error: {$result[1]}
\n"; } else { $page="result.php"; $imageURL = 'http://' . $_SERVER['SERVER_NAME'] . $rootDir . $destDir . $result[0]; // echo $imageURL; } ?> ///////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////// // Arduino Source Code..... // MaxSona_House.pde // Reads a MaxSona range finder. If a person is 410cm or less 1byte is send on the serial bus. ///////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////// // Wire LV MaxSonar-EZ0 // GRD -> GRD // +5 -> 5v // pw -> Arduino pin 7 //Digital pin 7 for reading in the pulse width from the MaxSonar device. //This variable is a constant because the pin will not change throughout execution of this code. const int pwPin = 7; //variables needed to store values long pulse, inches, cm; void setup() { //This opens up a serial connection to shoot the results back to the PC console Serial.begin(9600); Serial.print("started"); } void loop() { pinMode(pwPin, INPUT); //Used to read in the pulse that is being sent by the MaxSonar device. //Pulse Width representation with a scale factor of 147 uS per Inch. pulse = pulseIn(pwPin, HIGH); //147uS per inch inches = pulse/147; //change inches to centimetres cm = inches * 2.54; if (cm < 410){ Serial.print(1, BYTE); // send 1 to Processing delay(2000); } else { // If the switch is not ON, Serial.print(0, BYTE); } }