Living Rock

Worldbuilding and interactive design through touch and light.

Dates

October 2024

Role

Individual

Skills

Fabrication: 3D Printing, Electronics, Coding, Paper Mache, Painting

Design: Onshape, Arduino, Worldbuilding

Interaction

Touch sensors and fiber optics are embedded in each moss section. When the moss is touched, that section flickers and dances.

Prototyping & Development

Features

  • Chicken wire structure
  • Paper mache form
  • Paint
  • Moss
  • 6x Piezoelectric touch sensors
  • 1x Adafruit Neopixel LED ring
  • 1x Adafruit Metro Mini
#include <Adafruit_NeoPixel.h>

#define PixelPIN 3    // Pin where NeoPixels are connected
#define NUMPIXELS  12 // Total number of NeoPixels to be used
#define SIXTHPIXELS (NUMPIXELS / 6) // One-sixth of the total pixels (2 LEDs per sensor)

Adafruit_NeoPixel pixels(NUMPIXELS, PixelPIN, NEO_RGBW + NEO_KHZ800);

// Sensor pins
const int sensor1 = A0;
const int sensor2 = A1;
const int sensor3 = A2;
const int sensor4 = A4;
const int sensor5 = A3;
const int sensor6 = A5;

// Individual threshold values for each sensor
const int thresholds[6] = {20, 30, 15, 20, 40, 15};

// Flicker timing variables
unsigned long flickerDuration = 2000; // Duration to flicker
unsigned long flickerStartTime[6];    // Start time of flickering for each sensor
bool isFlickering[6] = {false, false, false, false, false, false}; // Flickering state for sensors

void setup() {
  Serial.begin(9600);       
  pixels.begin();           // Initialize the NeoPixel strip
  pixels.clear();
  pixels.show();
}

void loop() {
  // Reading sensors
  int sensorReadings[6] = {
    analogRead(sensor1), 
    analogRead(sensor2), 
    analogRead(sensor3), 
    analogRead(sensor4), 
    analogRead(sensor5),
    analogRead(sensor6)
  };

  // Check if any sensor is triggered and start flickering
  for (int i = 0; i < 6; i++) {
    if (sensorReadings[i] >= thresholds[i] && !isFlickering[i]) {
      isFlickering[i] = true;
      flickerStartTime[i] = millis(); // Record the start time for the sensor
    }
  }

  // Handle flickering for each sensor section
  for (int i = 0; i < 6; i++) {
    if (isFlickering[i]) {
      flickerLights(i);  // Call flicker function for the section
      if (millis() - flickerStartTime[i] >= flickerDuration) {
        pixels.clear();   // Clear the pixels after flickering
        pixels.show();
        isFlickering[i] = false;  // Stop flickering for that section
      }
    }
  }

  // Print sensor readings to serial monitor
  for (int i = 0; i < 6; i++) {
    Serial.print("Sensor "); Serial.print(i + 1); Serial.print(": "); 
    Serial.print(sensorReadings[i]); 
    if (i < 5) Serial.print(" | ");
  }
  Serial.println();
}

// Non-blocking flicker function
void flickerLights(int sensorNumber) {
  static unsigned long lastFlickerTime[6] = {0, 0, 0, 0, 0, 0};  // Last time a flicker occurred for each section
  const int flickerInterval = random(20, 100); // Random interval for flicker

  // Check if it's time to flicker again for this section
  if (millis() - lastFlickerTime[sensorNumber] >= flickerInterval) {
    lastFlickerTime[sensorNumber] = millis();  // Update the last flicker time

    // Determine which sixth of the pixels to flicker
    int startPixel = sensorNumber * SIXTHPIXELS;
    int endPixel = startPixel + SIXTHPIXELS;

    // Flicker the pixels in the specified range
    for (int i = startPixel; i < endPixel; i++) {
      // Randomly decide to turn the pixel on or off
      if (random(2) == 0) {
        pixels.setPixelColor(i, pixels.Color(0, 0, 0, 100));  // Turn on white
      } else {
        pixels.setPixelColor(i, pixels.Color(0, 0, 0, 0));    // Turn off
      }
    }

    pixels.show();  // Update the pixels after flickering
  }
}