Memory Slot
From Accident Designs Wiki
Scripts developed for Annette Finnsdottirs exhibition Memory Slot, a part of Virtual Moves by Tagging Art.
Contents |
Concept
Scripts
A combination of scripts in LSL, PHP, Processing and Arduino keeps this project running. main.lsl located in a prim in Second Life transmits information to switch.php on a webserver. switch.php writes this information to a .txt file that is then retrieved by processing. Through arduino the information controls two light bulbs.
main.lsl
//By Sophie Zhu - accidentdesigns.com integer masterchannel = -1; //insert a relatively low negative integer to reduce lag string token = "token"; integer i; integer l1; integer ready = TRUE; key req_id; req(integer switch) { req_id = llHTTPRequest("http://example.com/path/to/switch.php", [HTTP_METHOD, "POST"], "y=" + "memslot" + "&x=" + (string)switch); } vector randVector(float min, float max) { list floats = []; for(i=0;i<3;i++) { float f = llFrand(max - min) + min; if ( llFrand(1.) < .5) { f = f*-1; } floats = floats + [f]; } return <llList2Float(floats, 0),llList2Float(floats, 1),llList2Float(floats, 2)>; } string path = "http://www.example.com/path/to/video"; string suffix = ".mov"; key client = ""; memory(key id) { ready = FALSE; client = id; llSetTimerEvent(0.); integer num = (integer)llFrand(9.0) + 1; string a = path + (string)num + suffix; llShout(masterchannel, "on"); llParcelMediaCommandList([PARCEL_MEDIA_COMMAND_URL,a,PARCEL_MEDIA_COMMAND_LOOP]); llSetTimerEvent(50.); llWhisper(0, llKey2Name(id) + " have unlocked a memory: " + a); req(1); integer tempchannel = (integer)((llFrand(2000) * 2) * -1); l1 = llListen(tempchannel, "token", "", ""); llRezObject(token, llGetPos() + randVector(1.5, 2), ZERO_VECTOR, ZERO_ROTATION, tempchannel); } default { state_entry() { } listen(integer channel, string name, key id, string message) { if(message == "ready") { llSay(channel, client); } llListenRemove(l1); ready = TRUE; } touch_start(integer num_detected) { if(ready == TRUE){ memory(llDetectedKey(0));} } timer() { req(0); llParcelMediaCommandList([PARCEL_MEDIA_COMMAND_UNLOAD]); llShout(masterchannel, "off"); llSetTimerEvent(0.); } http_response(key request_id, integer status, list metadata, string body) { //llOwnerSay(body); //Debug } }
token.lsl
//By Sophie Zhu //http://accidentdesigns.com integer l1; key id; string name; vector randColor() { if ( llFrand(1.) < .5) { return <0.0,0.0,0.0>; } return <1.0,1.0,1.0>; } init() { llSetStatus(STATUS_DIE_AT_EDGE, TRUE); if ( llFrand(1.) < .5) { } llSetPrimitiveParams( [ PRIM_POINT_LIGHT, TRUE, randColor(), 1.0, 4.0, 0.5 ] ) ; llSetColor(randColor(), ALL_SIDES); llParticleSystem( [0, 355, 1, randColor(), 2, 1.00, 3, randColor(), 4, 0.00, 5, <0.35,0.35,0.00>, 6, <0.03,0.40,0.00>, 7, 1.50, 8, <0.00,0.00,0.00>, 9, 2, 10, 0.50, 11, 0.00, 13, 0.10, 15, 2, 16, 0.00, 17, 0.20, 18, 0.40, 19, 0.00, 20, llGetKey(), 21, <0.00,0.00,0.00>]); llSetStatus(STATUS_PHYSICS, TRUE); float mass = llGetMass(); // mass of this object float gravity = 9.8; // gravity constant llSetForce(mass * <0,0,gravity>, FALSE); // in global orientation } vector randVector(float min, float max) { list floats = []; integer i; for(i=0;i<3;i++) { float f = llFrand(max - min) + min; if ( llFrand(1.) < .5) { f = f*-1; } floats = floats + [f]; } return <llList2Float(floats, 0),llList2Float(floats, 1),llList2Float(floats, 2)>; } default { state_entry() { init(); } touch_start(integer total_number) { llApplyImpulse(llGetMass()*randVector(0.1, 4),FALSE); } on_rez(integer start_param) { l1 = llListen(start_param, "", "", ""); llSay(start_param, "ready"); } listen(integer channel, string name, key id, string message) { id = (key)message; llListenRemove(l1); name = llKey2Name(id); llSetObjectName(name + " was here!"); llSetText(name + " was here!", <1.,1.,1.>, 0.5); init(); } }
effectscontroller.lsl
//By Sophie Zhu //http://accidentdesigns.com integer masterchannel = -2; //insert same channel as in main.lsl integer Side = ALL_SIDES; fade_out() { integer steps = 25; float base = 0.85; float delay = 1.0 / steps; integer i; // fade out for (i = 0; i < steps; i++) { llSetLinkAlpha(LINK_ALL_CHILDREN,llPow(base,i), Side); llSleep(delay); } llSetLinkAlpha(LINK_ALL_CHILDREN,0.0, Side); } fade_in() { integer steps = 25; float base = 0.85; float delay = 1.0 / steps; integer i; // fade in for (i = 0; i < steps; i++) { llSetLinkAlpha(LINK_ALL_CHILDREN, 1.0 - llPow(base,i), Side); llSleep(delay); } llSetLinkAlpha(LINK_ALL_CHILDREN, 1.0, Side); } default { state_entry() { llListen(masterchannel, "Frk. Memory Slot", "", ""); } listen(integer channel, string name, key id, string message) { if(message == "off") { llSleep(3.0); fade_in(); } else if(message == "on") { fade_out(); } } }
pyramidcontroller.lsl
This script is in the root prim of a linkset with all the 'pyramids'.
//By Sophie Zhu //http://accidentdesigns.com string mediatexture = "d864fe60-3743-9529-0238-b2170a55f29c"; string white = "34f2d0b0-6afd-c141-9562-b2d12d24ae4b"; integer masterchannel = -2; //insert same channel as in main.lsl integer Side = ALL_SIDES; fade_out() { integer steps = 25; float base = 0.85; float delay = 1.0 / steps; integer i; // fade out for (i = 0; i < steps; i++) { llSetLinkAlpha(LINK_ALL_CHILDREN,llPow(base,i), Side); llSleep(delay); } llSetLinkAlpha(LINK_ALL_CHILDREN,0.0, Side); } fade_in() { integer steps = 25; float base = 0.85; float delay = 1.0 / steps; integer i; // fade in for (i = 0; i < steps; i++) { llSetLinkAlpha(LINK_ALL_CHILDREN, 1.0 - llPow(base,i), Side); llSleep(delay); } llSetLinkAlpha(LINK_ALL_CHILDREN, 1.0, Side); } default { state_entry() { llSetLinkTexture(LINK_ALL_CHILDREN, white, ALL_SIDES); llListen(masterchannel, "Frk. Memory Slot", "", ""); } listen(integer channel, string name, key id, string message) { //llOwnerSay("message received"); if(message == "off") { fade_out(); llSetLinkTexture(LINK_ALL_CHILDREN, white, ALL_SIDES); fade_in(); } else if(message == "on") { fade_out(); llSetLinkTexture(LINK_ALL_CHILDREN, mediatexture, ALL_SIDES); fade_in(); } } }
switch.php
<?php //By Johan Bichel Lindegaard //http://accidentdesigns.com //Create an empty html file called 'switch.html' in the same directory as this script //Get POST variables and put into superglobal array $p_data = implode('', file('php://input')); $p_data = explode('&', $p_data); foreach ($p_data as $p_val) { $d_parts = explode('=', $p_val); $_POST[$d_parts[0]] = urldecode($d_parts[1]); } $switch = $_POST['x']; $id = $_POST['y']; if ($id == 'memslot') { $myFile = "switch.html"; $fh = fopen($myFile, 'w') or die("can't open file"); fwrite($fh, '<html><head></head><body>state:'.$switch.'</body></html>'); fclose($fh); echo 'state: '.$switch.' succesfully written to file'; } else { echo 'error wrong id'; } ?>
processing code
/*
=================================================
== memory SLot ==================================
== SL to arduino program by jacob sikker remin ==
=========================== 26/02-2008: ver1.2 ==
=================================================
*/
import processing.serial.*; //serial communication
import krister.Ess.*; //ESS sound package
AudioChannel[] mySound = new AudioChannel[1]; // One channel of audio playback
Serial port; // The serial port
PFont font; // Text display variables
String serialString = ""; // Serial variables
boolean output13 = false; //Output PIN variables
boolean change = false; //bool to check for change
//String URL = "http://sl.accidentdesigns.com/taggingart/memslot/switch.html";
// URL to read from
//String URL = "http://campingsex.org/tagging_art/switch.html"; // URL
to read from
String URL = "http://8bitklubben.dk/taggingart/switch.html"; // URL
to read from
//String[] data; //to store data from URL
char dummychar = '0';
boolean flipflop = false;
void setup() {
// GRAPHICS SETUP
size(300, 200);
font = loadFont("Ziggurat-HTF-Black-32.vlw");
textFont(font);
background(51);
text("status:"+output13, 15, 30);
// SERIAL / ARDUINO SETUP
// Print a list of the serial ports, for debugging purposes:
println("Serial ports");
println(Serial.list());
// the arduino is normally the third ([2]) port
print("Selected port: ");
println(Serial.list()[2]);
port = new Serial(this, Serial.list()[2], 9600);
Ess.start(this); // Start Ess
// Sounds must be located in the sketch's "data" folder
mySound[0] = new AudioChannel("Netfilm.mp3");
print("Started listening: "+hour()+":"+minute()+":"+second()+" ");
}
void draw() {
getstate(); // read from URL
setOutput13(); // set pin according to URL state
flipit(); // update progress cursor
delay(500); //wait 0.5 sec
}
void getstate() {
try {
if (loadStrings(URL) != null){
String[] data = loadStrings(URL); //read data from URL
dummychar = data[0].charAt(31);
}
if (dummychar == '1'){
output13 = true;
playsound ();
} else {
output13 = false;
mySound[0].stop();
}
if (change != output13) {
background(51); // clear screen w/ dark grey
stroke(255);
fill(255);
text("status:"+output13, 15, 30);
change = output13;
}
} catch(Exception e) {
print("caught one exception: "+hour()+":"+minute()+":"+second()+" ");
print(e);
delay(1000); // if error wait 1sec and try again
return;
}
}
void playsound () {
if (mySound[0].state == Ess.STOPPED) {
mySound[0].play();
}
}
void flipit () {
if (flipflop == true){
stroke(0,255,0);
fill(0,255,0);
rect(15,35,50,50);
} else {
stroke(51);
fill(51);
rect(15,35,50,50);
}
flipflop = !flipflop;
}
void setOutput13() {
try {
port.write("w d 13 "); // NOTE THE SPACE TRAILING THE 13
if(output13==true) {
port.write('1'); //NOTE THAT WE ARE SENDING A STRING AND NOT AN INTEGER
} else {
port.write('0'); //NOTE THAT WE ARE SENDING A STRING AND NOT AN INTEGER
}
port.write(13);
} catch (Exception e) {
print ("write exception!"+e);
}
}
arduino code
/*
SL to arduino via processing
by jacob sikker remin
based heavily on simple message system
---- SimpleMessageSystem Example 1 ----
Control Arduino board functions with the following messages:
r a -> read analog pins
r d -> read digital pins
w d [pin] [value] -> write digital pin
w a [pin] [value] -> write analog pin
Base: Thomas Ouellet Fredericks
Additions: Alexandre Quessy
*/
// Include de SimpleMessageSystem library
#include <SimpleMessageSystem.h>
void setup()
{
Serial.begin(9600); //Baud set at 9600
pinMode(12, OUTPUT);
pinMode(13, OUTPUT);
digitalWrite(13, HIGH);
digitalWrite(12, HIGH);
}
void loop()
{
if (messageBuild() > 0) { // Checks to see if the message is complete
and erases any previous messages
switch (messageGetChar()) { // Gets the first word as a character
case 'r': // Read pins (analog or digital)
readpins(); // Call the readpins function
break; // Break from the switch
case 'w': // Write pin
writepin(); // Call the writepin function
}
}
}
void readpins(){ // Read pins (analog or digital)
switch (messageGetChar()) { // Gets the next word as a character
case 'd': // READ digital pins
messageSendChar('d'); // Echo what is being read
for (char i=2;i<14;i++) {
messageSendInt(digitalRead(i)); // Read pins 2 to 13
}
messageEnd(); // Terminate the message being sent
break; // Break from the switch
case 'a': // READ analog pins
messageSendChar('a'); // Echo what is being read
for (char i=0;i<6;i++) {
messageSendInt(analogRead(i)); // Read pins 0 to 5
}
messageEnd(); // Terminate the message being sent
}
}
void writepin() { // Write pin
int pin;
int state;
switch (messageGetChar()) { // Gets the next word as a character
case 'a' : // WRITE an analog pin
pin = messageGetInt(); // Gets the next word as an integer
state = messageGetInt(); // Gets the next word as an integer
pinMode(pin, OUTPUT); //Sets the state of the pin to an output
analogWrite(pin, state); //Sets the PWM of the pin
break; // Break from the switch
// WRITE a digital pin
case 'd' :
pin = messageGetInt(); // Gets the next word as an integer
state = messageGetInt(); // Gets the next word as an integer
pinMode(pin, OUTPUT); //Sets the state of the pin to an output
digitalWrite(pin, state); //Sets the state of the pin HIGH (1) or
LOW (0)
// other lamp (pin 12) is opposite pin 13
if (state == 1) {
digitalWrite (12, 0);
} else {
digitalWrite (12, 1);
}
}
}
Facts about Memory SlotRDF feed
| Programming language | LSL +, PHP +, Processing +, and Arduino + |
| Project | Virtual Moves + |

