Saturday, June 12, 2010

Light Sharing

Another Saturday at uni and we were aiming to finish up our coding. It was decided that we needed a way to 'opt out' or 'log out' of the system in such a way that it continued running with three people. So the button came back into our system.

It seemed fairly simple, implemented on a local level inside the Arduino sketch. The Arduino reads the button state (digital input 1 or 0) and the button state determines what it send to the serial monitor.

if (offSwitch==1){
Serial.println(timer);
} else {
Serial.println(0);
}

If your button is pushed (button state 1), you're in the system and it sends your penalty / timer / LDR value to serial as usual.

If your button is not pushed (button state 0), you've opted out of the system so it sends back a zero. Hence, your value will always be lower than everyone else's, effectively removing you from the system.

By the time Sunday evening rolled around for the final system, we were using a final hybrid code of both methods we'd worked with, individualized for each user.

import processing.serial.*;
import eeml.*;
import cc.arduino.*;
import pachuino.*;

Pachuino p;
DataOut dOut;

float timer, scaledLDR;
float on_off;
float lastUpdate;
float jacquesLDR;
float tapaniLDR;
float seamusLDR;
float juditLDR;

PImage img;


void setup()
{
p = new Pachuino(this, Arduino.list()[0], 57600);
p.manualUpdate("http://www.pachube.com/api/7584.xml");
p.setKey("API");

dOut = new DataOut(this, "http://www.pachube.com/api/feeds/7584.xml",
"API");
dOut.addData(2,"CombinedLDR");

p.addRemoteSensor("http://www.pachube.com/api/7339.xml", 2); // tapani ldr
p.addRemoteSensor("http://www.pachube.com/api/7472.xml", 2); // seamus ldr
p.addRemoteSensor("http://www.pachube.com/api/7344.xml", 2); // jacques ldr

p.addLocalSensor("analog", 1, "ldr");
p.addLocalSensor("digital", 2, "on_off");

size(600,581);
background(255);
}


void draw()
{
juditLDR = p.localSensor[0].value;
on_off = p.localSensor[1].value;
tapaniLDR = p.remoteSensor[0].value;
seamusLDR = p.remoteSensor[1].value;
jacquesLDR = p.remoteSensor[2].value;

dOut.update(0, timer);
int response = dOut.updatePachube();

if ((millis() - lastUpdate) > 5000)
{
lastUpdate = millis();
if (on_off > 10)
{
timer = timer + 1;
scaledLDR = juditLDR/100;
timer = timer + scaledLDR;
delay(1000);
if (timer > seamusLDR && timer > tapaniLDR && timer > jacquesLDR)
{
img = loadImage("judit.jpg");
image (img,0,0);
println("judit = " + timer);
println("seamus = " + seamusLDR);
println("tapani = " + tapaniLDR);
println("jacques = " + jacquesLDR);
p.analogWrite(9, 1);
println("off");
delay(500);
p.analogWrite(9, 0);
}
else if (seamusLDR > timer && seamusLDR > tapaniLDR && seamusLDR > jacquesLDR)
{
img = loadImage("mus.jpg");
image (img,0,0);
println("judit = " + timer);
println("seamus = " + seamusLDR);
println("tapani = " + tapaniLDR);
println("jacques = " + jacquesLDR);
p.analogWrite(9, 250);
println("on");
delay(500);
p.analogWrite(9, 0);
}
else if (jacquesLDR > timer && juditLDR > tapaniLDR && juditLDR > seamusLDR)
{
img = loadImage("jaak.jpg");
image (img,0,0);
println("judit = " + timer);
println("seamus = " + seamusLDR);
println("tapani = " + tapaniLDR);
println("jacques = " + jacquesLDR);
p.analogWrite(9, 250);
println("on");
delay(500);
p.analogWrite(9, 0);
}
else if (tapaniLDR > timer && tapaniLDR > jacquesLDR && tapaniLDR > seamusLDR)
{
img = loadImage("tap.jpg");
image (img,0,0);
println("judit = " + timer);
println("seamus = " + seamusLDR);
println("tapani = " + tapaniLDR);
println("jacques = " + jacquesLDR);
p.analogWrite(9, 250);
println("on");
delay(500);
p.analogWrite(9, 0);
}
else
{
println("judit = " + timer);
println("seamus = " + seamusLDR);
println("tapani = " + tapaniLDR);
println("jacques = " + jacquesLDR);
p.analogWrite(9, 250);
println("on");
delay(500);
p.analogWrite(9, 0);
}
}
else
{
println("judit = " + 0);
println("seamus = " + seamusLDR);
println("tapani = " + tapaniLDR);
println("jacques = " + jacquesLDR);
p.analogWrite(9, 1);
println("off");
delay(500);
p.analogWrite(9, 0);
}
}
}


void onReceiveEEML(DataIn d)
{
p.updateRemoteSensors(d);
}

Essantially, each user uploads to a seperate Pachube feed their 'penalty' value which is accrued based on a calculation of how long they've had their light on and how bright the light. The other users' penalty values are read from the feeds and compared locally on each user's computer. If ones local value is greater than all the external values, the local servo physically switches the light switch off.

Time came to run everything offsite. I successfully managed to attach my device to my light switch so that the servo was able to flip the switch.

Once we were all online, we communicated via Skype conference and let the program run.

We ran a few tries and overall, I would say that it worked successfull. The first time my light got switched off, it was both a shock and a thrill. It was an incredible feeling that what we'd been planning and envisaging was actually physically occuring. I was actually sitting in the dark with no control over what had just happened. It was amazing.

Inevitably we did encounter problems. From the very first trial, it was evident that Jacques penalty value was racing ahead. After ruling out the more obvious potential causes such as minor multiplication errors in the coding or faulty wiring, we took it as a malfunctioning LDR and he replaced it with another, smaller, LDR.

The biggest issue we encountered was still with the delay in update times. Even though with the confidence of being whitelisted for higher data usage allowance on Pachube, it wasn't until the third trial we set the data update time from 5 seconds down to 1 second. The issue here was that where our local data was updating every second, often the data being updated from Pachube wasn't perfectly up to date so sometimes we would have multiple people with their lights off. This was caused by a leapfrogging effect when values were very close together, at times my light would be switched on an off several times in the space of a few seconds.

This was most evident when we began using webcams to commmunicate and could actually see who had their light on or off. However, I argue that if this system were applied over a longer period of time and we weren't actually in direct communication with each other, all we would be aware of is our own light. In fact, the system actually worked more successfully with a slightly longer update time, such as 2.5 seconds.

One important part of the coding which had gotten left out somehow was that you weren't supposed to accrue value when your light was off. This also contributed to the leapfrogging effect as users were still accruing penalty points when their light was off. We tried to fix this on the fly by setting a threshold for minimum amount of light required to accrue value.

We ran the code several times by between about 8pm-9:30 and it was amazing that it had all actually come together and, on the whole, worked as we had intended.

No comments:

Post a Comment