Miles Barbick
Published

Interactive Sculpture Powered by Real-Time Car Accident Data

A responsive sculpture that uses live data from alcohol-impaired driving accidents to dynamically change LED headlights and hood movement.

IntermediateShowcase (no instructions)45
Interactive Sculpture Powered by Real-Time Car Accident Data

Things used in this project

Hardware components

Photon 2
Particle Photon 2
×1
Adafruit NeoPixel Digital RGB LED Strip 144 LED, 1m White
Adafruit NeoPixel Digital RGB LED Strip 144 LED, 1m White
Only 2 singular leds are used, one for each headlight of the car.
×1
SG90 Micro-servo motor
SG90 Micro-servo motor
×1

Software apps and online services

Particle Build Web IDE
Particle Build Web IDE
Chicago Data Portal

Hand tools and fabrication machines

Soldering iron (generic)
Soldering iron (generic)
Hot glue gun (generic)
Hot glue gun (generic)

Story

Read more

Code

Particle Photon Code

C/C++
Controls the NeoPixel headlights and servo motor based on how recent an alcohol-impaired driving crash was, using live data from a webhook.
The code is currently in presentation mode (but does respond to data from the Chicago Data Portal!), where 'adjustCar' is called in the loop with fake webhooks that override real data to show off the different headlight colors and hood angles.
NeoPixel is connected to D2 and the servo to D1.
// This #include statement was automatically added by the Particle IDE.
#include <neopixel.h>
#include "Particle.h"

SYSTEM_MODE(AUTOMATIC);

// IMPORTANT: Set pixel COUNT, PIN and TYPE
#if (PLATFORM_ID == 32)
  #define PIXEL_PIN    SPI1
#else
  #define PIXEL_PIN    D2
#endif
#define PIXEL_COUNT  2
#define PIXEL_TYPE   WS2812B

// --- servo calibration ---
const int servoPin    = D1;    // D1 is pin 1 on Photon
int currentAngle = 79;

Adafruit_NeoPixel strip(PIXEL_COUNT, PIXEL_PIN, PIXEL_TYPE);
Servo myServo;

const unsigned long DAY = 86400UL * 1000UL;
unsigned long lastPublished = 0;

void setup() {
    Particle.syncTime();
    Time.zone(-5);

    strip.begin();
    strip.clear();
    strip.show();

    myServo.attach(servoPin);

    Serial.begin(9600);
    Particle.subscribe("hook-response/crash",   handleEventResponse, MY_DEVICES);
    Particle.subscribe("hook-response/<12",     lessThan12);
    Particle.subscribe("hook-response/<24",     lessThan24,         MY_DEVICES);
    Particle.subscribe("hook-response/<48",     lessThan48);
    Particle.subscribe("hook-response/>48",     moreThan48,         MY_DEVICES);

    delay(2000);

    smoothServoMove(80);

    delay(1000);
    
    // Particle.publish("crash");
    // Serial.println("crash published");
}

void loop() {
    // unsigned long now = millis();
    // if (now > lastPublished + DAY) {
    //     lastPublished = now;
    //     Particle.publish("crash");
    //     Serial.println("crash published");
    // }
    
    delay(6000);
    
    adjustCar(6);
    
    delay(6000);
    
    adjustCar(18);
    
    delay(6000);
    
    adjustCar(36);
    
    delay(6000);
    
    adjustCar(49);
}

void smoothServoMove(int targetAngle) {
    if (currentAngle == targetAngle) return;
    
    int step = (targetAngle > currentAngle) ? 1 : -1;
    for (int a = currentAngle; a != targetAngle; a += step) {
        myServo.write(a);
        delay(15);  // 10 ms per degree → ~100°/s
    }
    
    myServo.write(targetAngle);
    
    currentAngle = targetAngle;
}

void adjustCar(int hoursAgo) {
    if (hoursAgo > 48) hoursAgo = 48;

    if      (hoursAgo < 12) strip.setColor(0,255,0,0),   strip.setColor(1,255,0,0);   // red
    else if (hoursAgo < 24) strip.setColor(0,255,165,0), strip.setColor(1,255,165,0);  // orange
    else if (hoursAgo < 48) strip.setColor(0,160,32,240),strip.setColor(1,160,32,240);  // purple
    else                    strip.setColor(0,255,255,255),strip.setColor(1,255,255,255);  // white
    strip.show();
    
    int angle = map(hoursAgo, 0, 48, 10, 80);
    smoothServoMove(angle);
}

void handleEventResponse(const char *event, const char *data) {
    String s = String(data);
    int year   = s.substring(0,4).toInt();
    int month  = s.substring(5,7).toInt();
    int day    = s.substring(8,10).toInt();
    int hour   = s.substring(11,13).toInt();
    int minute = s.substring(14,16).toInt();
    int second = s.substring(17,19).toInt();

    struct tm tm;
    tm.tm_year  = year - 1900;
    tm.tm_mon   = month - 1;
    tm.tm_mday  = day;
    tm.tm_hour  = hour;
    tm.tm_min   = minute;
    tm.tm_sec   = second;
    tm.tm_isdst = -1;

    time_t crashTime   = mktime(&tm);
    time_t currentTime = Time.now();
    int hoursAgo = (currentTime - crashTime) / 3600;
    
    adjustCar(hoursAgo);
}

void lessThan12(const char *e,const char*d){ adjustCar(6);  }
void lessThan24(const char *e,const char*d){ adjustCar(18); }
void lessThan48(const char *e,const char*d){ adjustCar(36); }
void moreThan48(const char *e,const char*d){ adjustCar(49); }

Credits

Miles Barbick
2 projects • 0 followers

Comments