Creator: Kevin Daokai Liu
Instructor: Eric Parren
Rising sea levels invade the city. Source: Sea levels set to keep rising for centuries even if emissions targets met. Nov 2019.
Project Intro:
My Interaction Lab final project “Rising Seas” is created to exemplify climate change. The project uses Arduino input to simulate the utilization of fossil fuels and the generation of renewable clean energy, and uses Processing output to simulate the rise of sea levels as well as corresponding consequences. These consequences serve as audio and visual feedback instructing the user’s next operation in a loop.
Conception & Design:
My project’s concept is to simulate how human beings’ utilization of energy affects global warming, in which the users act as energy generators and are able to see the damage caused by their commands. My preparatory research on Ernest Edmonds’ “Art, Interaction and Engagement” inspired me to create a project that intuitively appeals to users’ attention and aims to communicate a social message. With that objective in mind, I did more related research and three projects stood out to me and provided great references: (1) “Climate Symphony“, (2) “Waterlicht“, and (3) “Carbon Visuals“. I zoomed into greater details in discussing how these projects further guided my project conception, as well as coming up with specific designs with my Arduino control command unit and my Processing interface. Since my project involves simulating wind power generation, I was expecting users to blow into the sensors to trigger the 3D-printed wind turbines. To achieve that, I tried to use humidity sensors as a replacement for wind sensors (because we don’t have that). Also, since another way of generating power is the traditional way of burning coals, I tried to fabricate a mini power station.
Figure 1. Draft Processing Sketch
However, many attempts failed in later tests. The humidity sensor was not able to function accurately and capture stable serial signals, while the power station idea could really confuse the users. At the user test, I demonstrated my project draft, and was recommended to simplify my Arduino command unit and put more effort to improve the Processing part. I took this feasible suggestion and improved my project by condensing the Arduino part and optimizing my Processing sketch.
Fabrication & Production:
1. Circuit and Appearance Design
I started executing the project by building the circuit. Since I wanted to simplify the Arduino side command unit, I used two slide potentiometers that users can intuitively interact with. I also installed two LEDs and correlate their brightness with the sliders, as well as one LED neo-pixels strip + a speaker giving sirens when the serial values exceed an upper/lower bound. Then, since the laser cutter was down for two days, I had to manually saw to make a box where I can put my Arduino and manage my wires. I re-designed the box and later cut it when the cutter is back on. These production designs provide a good mix of audio and visual feedback to users during the interaction. However, higher preparedness and better management of timelines would’ve helped me respond to the technical failure. The following is a video of my project appearance being laser cut:
2. Coding and Optimization
Then, according to my project design proposal, I coded respectively for the Arduino part and the Processing part. I invited my classmates working in the studio to user test and give me feedback on how to improve the immersive user experience. Building upon their suggestions, I incorporated various enhancements that significantly enhanced the overall user experience. One such improvement involved the addition of background music, carefully chosen to complement the theme and ambiance of the project. The melodic notes contributed to the immersive nature of the experience, captivating the users’ senses. Moreover, I implemented the visualization of serial values, allowing users to visually perceive and comprehend the data transmitted by the Arduino. This visual representation not only provided real-time feedback but also facilitated a deeper understanding of the system’s behavior. Furthermore, I extended the visual enhancements to incorporate the visualization of wind patterns. By displaying the wind movement, users were able to grasp the intricate relationship between their interactions and the simulated environmental changes, fostering a sense of connection and awareness. I also made connections between the volume + the sea level, and the wind speed + the wave speed, etc. The iterative process of incorporating feedback and optimizing the project not only improved its functionality but also refined its aesthetic appeal. The following video is the final version of my project (audio on):
To provide more details, here is a POV video of interacting with the project:
Conclusions:
The goal of my project is to highlight the effects that human energy behaviors have on global warming by simulating the rising sea levels. Based on the reactions and feedback from my user testers, the project has fairly achieved its goal and could be improved in the application of climate change education. For most users, it was an intuitive and smooth process to interact with my project and make sense of the social message behind it without extra instructions. The project aligns with my proposed definition of interaction by providing a constant interaction in which users can intuitively and naturally respond to and based on the feedback in a continuous loop. If I had more time, I would (1) redesign my Arduino command unit, using more intriguing sensors instead of plain slide pots to create more immersive experiences, and (2) refine my Processing sketch by adding more aesthetic details or instructions/orientations at the beginning of the display. From my accomplishments and setbacks, I have learned a few takeaways: (1) always start early and plan ahead to allow more time for developing the conception, executing the fabrication, debugging, responding to emergent situations, or refining the project; (2) keep testing the project, refining it on the way, and when necessary, be bold to use alternatives; (3) a good project should be user-friendly: these interactive works are not intended to confuse or trick users, but to encourage them to enjoy the interactive process (even it could be challenging to some extent).
In conclusion, this project offers a prompt response to climate change issues using the form of an interactive project. The project consolidates my learned knowledge and skills throughout the course Interaction Lab for using a combination of Arduino, Processing, prototyping, and digital fabrication that collectively delivers simulation of rising sea waters. The project, though not intricate and highly technical, provides an educational and reflective experience of concluding the class in practice.
Appendix 1 – Appearance Vector Image for Laser Cutting
Appendix 2 – Arduino Code
#include <Adafruit_NeoPixel.h>
#define LED_PIN 3
#define LED_COUNT 10
Adafruit_NeoPixel strip(LED_COUNT, LED_PIN, NEO_GRB + NEO_KHZ800);
int greenledPin = 5;
int redledPin = 6;
int speakerPin = 9;
int toneFreq = 1000;
int toneDuration = 50;
int pauseDuration = 100;
unsigned long previousMillis = 0;
unsigned long previousMillis2 = 0;
bool blink = false;
bool pause = false;
void setup() {
Serial.begin(9600);
pinMode(greenledPin, OUTPUT);
pinMode(redledPin, OUTPUT);
pinMode(speakerPin, OUTPUT);
strip.begin();
strip.show();
}
void loop() {
int sensor0 = analogRead(A0);
int sensor1 = analogRead(A1);
int greenBrightness = map(sensor0, 0, 1023, 0, 255);
int redBrightness = map(sensor1, 0, 1023, 0, 255);
analogWrite(greenledPin, greenBrightness);
analogWrite(redledPin, redBrightness);
if (sensor1 – sensor0 > 650) {
tone(speakerPin, toneFreq, toneDuration);
if (!pause) {
unsigned long currentMillis = millis();
if (currentMillis – previousMillis >= 200) {
if (!blink) {
for (int i = 0; i < strip.numPixels(); i++) {
strip.setPixelColor(i, strip.Color(255, 0, 0)); // set the color of the LED
}
strip.show(); // update the LED strip
blink = true;
} else {
strip.clear(); // turn off all LEDs
strip.show(); // update the LED strip
blink = false;
}
previousMillis = currentMillis;
}
}
if (millis() – previousMillis2 >= pauseDuration) {
pause = !pause;
previousMillis2 = millis();
}
} else if (sensor1 + sensor0 < 500) {
noTone(speakerPin);
pause = false;
for (int i = 0; i < strip.numPixels(); i++) {
strip.setPixelColor(i, strip.Color(128, 128, 128)); // set the color of the LED to grey
}
strip.show();
} else {
noTone(speakerPin);
pause = false;
for (int i = 0; i < strip.numPixels(); i++) {
strip.setPixelColor(i, strip.Color(0, 255, 0)); // set the color of the LED
}
strip.show();
}
Serial.print(sensor0);
Serial.print(“,”);
Serial.print(sensor1);
Serial.println();
delay(20);
}
Appendix 3 – Processing Code
import processing.serial.*;
import processing.sound.*;
Serial serialPort;
SoundFile sound;
int NUM_OF_VALUES_FROM_ARDUINO = 2;
int arduino_values[] = new int[NUM_OF_VALUES_FROM_ARDUINO];
float angle = 0;
float waveHeight = 9;
float waveSpeed = 0.03;
float waveFrequency = 0.04;
//float waveAltitude = 100; // note that this variable is negatively correlated with the actual sea level; 800-100 would be good range.
float curtainOpacity = 0;
float curtainSpeed = 5;
float volume = 0.5;
void setup() {
//size(1400, 800);
fullScreen();
printArray(Serial.list());
serialPort = new Serial(this, "/dev/cu.usbmodem1401", 9600);
sound = new SoundFile(this, "wave sound.mp3");
sound.loop();
}
void draw() {
background(255);
getSerialData();
float waveAltitude = map (arduino_values[0]-arduino_values[1], 1023, -1023, 700, 100);
sound.amp(volume);
volume = map (arduino_values[0]-arduino_values[1], 1023, -1023, 0.2, 1);
waveSpeed = map(arduino_values[0], 0, 1023, 0.05, 0.4);
// draw the wave line
//fill(0, 150, 255);
noFill();
stroke(#006994);
beginShape();
for (float x = 0; x <= width; x += 10) {
float y = waveAltitude + sin(angle + x * waveFrequency) * waveHeight;
vertex(x, y);
}
endShape();
// update the angle for the next frame
angle += waveSpeed;
// color the area below the wave line
fill(#006994);
noStroke();
beginShape();
vertex(0, height);
for (float x = 0; x <= width; x += 10) {
float y = waveAltitude + sin(angle + x * waveFrequency) * waveHeight;
vertex(x, y);
}
vertex(width, height);
endShape(CLOSE);
// the following is the text info
float tempValue = 13.9 + (arduino_values[1]-arduino_values[0])/500;
int enValue = arduino_values[0]+arduino_values[1];
String formattedenValue = nf(enValue, 0, 2);
textSize(40);
String energy = "Total Units of Energy Generated:";
String temp = "Global Current Average Temperature:";
fill(0);
text(energy, 40, 80);
text(formattedenValue, 620, 80);
text(temp, 40, 150);
text(tempValue, 685, 150);
float wind = map(arduino_values[0], 0, 1023, 0, 10);
float coal = map(arduino_values[1], 0, 1023, 0, 10);
// Dashboard 1 (Wind)
noFill();
stroke(0);
strokeWeight(3);
ellipse(width - 100, 100, 150, 150);
textSize(30);
//textAlign(CENTER);
fill(0);
text(wind, width - 165, 110);
text("/10", width - 90, 110);
// Dashboard 2 (Coal)
noFill();
stroke(0);
strokeWeight(3);
ellipse(width - 100, 300, 150, 150);
textSize(30);
//textAlign(CENTER);
fill(0);
text(coal, width - 165, 310);
text("/10", width - 90, 310);
float windsize = map(arduino_values[0], 0, 1023, 15, 60);
drawWindWave(1150, 100, windsize);
float firesize = map(arduino_values[1], 0, 1023, 0.5, 2);
drawFire(1170, 330, firesize);
if (arduino_values[1]-arduino_values[0]>650) {
curtainHigh();
}
if (arduino_values[1]+arduino_values[0]<500) {
curtainLow();
}
}
void drawWindWave(float x, float y, float size) {
noStroke();
fill(#0066CC);
beginShape();
for (int i = 0; i < 3; i++) {
vertex(x + size*(i-1), y + size*sin(frameCount*0.1 + i*PI/3));
}
vertex(x + size, y);
endShape(CLOSE);
}
void drawFire(float x, float y, float size) {
pushMatrix();
translate(x, y);
scale(size);
// flame base color
noStroke();
fill(255, 140, 0);
// draw flame
beginShape();
vertex(0, -50);
bezierVertex(-10, -30, -20, -20, 0, 0);
bezierVertex(20, -20, 10, -30, 0, -50);
endShape(CLOSE);
// draw flame highlight
fill(255, 255, 255, 200);
beginShape();
vertex(0, -50);
bezierVertex(-5, -30, -10, -20, 0, -10);
bezierVertex(10, -20, 5, -30, 0, -50);
endShape(CLOSE);
popMatrix();
}
void curtainHigh() {
curtainOpacity += curtainSpeed;
if (curtainOpacity >= 255 || curtainOpacity <= 0) {
curtainSpeed *= -1;
}
fill(255, 0, 0, curtainOpacity);
rect(0, 0, width, height);
if (curtainOpacity >= 200) {
textSize(130);
textAlign(CENTER, CENTER);
fill(0);
text("LAND EROSION", width/2, height/2);
textAlign(LEFT, BASELINE);
}
}
void curtainLow() {
curtainOpacity += curtainSpeed;
if (curtainOpacity >= 255 || curtainOpacity <= 0) {
curtainSpeed *= -1;
}
fill(200, curtainOpacity); // set fill color to grey with varying opacity
rect(0, 0, width, height);
if (curtainOpacity >= 200) {
textSize(130);
textAlign(CENTER, CENTER);
fill(0); // set fill color to black for the text
text("LOW ENERGY", width/2, height/2);
}
textAlign(LEFT, BASELINE);
}
void getSerialData() {
while (serialPort.available() > 0) {
String in = serialPort.readStringUntil( 10 ); // 10 = '\n' Linefeed in ASCII
if (in != null) {
print("From Arduino: " + in);
String[] serialInArray = split(trim(in), ",");
if (serialInArray.length == NUM_OF_VALUES_FROM_ARDUINO) {
for (int i=0; i<serialInArray.length; i++) {
arduino_values[i] = int(serialInArray[i]);
}
}
}
}
}