Ali Mostafa
Published © GPL3+

Social Media Sentiment Art Sculpture

An engineering/art project that uses the Bluesky API to gauge general sentiments in social media for display using lights and servos.

IntermediateShowcase (no instructions)20 hours38
Social Media Sentiment Art Sculpture

Things used in this project

Hardware components

Photon 2
Particle Photon 2
×1
LED Strip, NeoPixel Digital RGB
LED Strip, NeoPixel Digital RGB
I used 3 NeoPixel LEDs wired in series
×1
SparkFun Snappable Protoboard
SparkFun Snappable Protoboard
I soldered all components to a protoboard
×1
DC POWER JACK 2.1MM BARREL-TYPE PCB MOUNT
TaydaElectronics DC POWER JACK 2.1MM BARREL-TYPE PCB MOUNT
I used this to power the board without being plugged into my computer
×1
Adafruit Continuous Rotation Micro Servo - FS90R
I used this for rotating the apple
×1
Circular Connector Cable Seal, Heat Shrinkable Sealing Boot
Circular Connector Cable Seal, Heat Shrinkable Sealing Boot
Applied these to the solders to keep them from breaking
×1
Female Header 8 Position 1 Row (0.1")
Female Header 8 Position 1 Row (0.1")
I used female headers to attach the micro controller to the protoboard
×1

Software apps and online services

Particle Build Web IDE
Particle Build Web IDE
Serial Terminal

Hand tools and fabrication machines

Soldering iron (generic)
Soldering iron (generic)
Solder Wire, Lead Free
Solder Wire, Lead Free
Hot glue gun (generic)
Hot glue gun (generic)
Mainly used hot glue for keeping the components in place and preventing the circuit from breaking

Story

Read more

Schematics

Circuit Diagram (somewhat)

This is a basic schematic I made in Fritzing. The LED strip isn't exact, but it should give you an idea.

Code

Photon 2 Firmware

C/C++
This is the firmware for the Photon 2, options are included at the top to customize the behavior.
#include "Particle.h"
#include <neopixel.h>

#define PIXEL_PIN SPI1 // D2
#define PIXEL_COUNT 3
#define PIXEL_TYPE WS2812B

SYSTEM_MODE(AUTOMATIC);
SerialLogHandler logHandler(LOG_LEVEL_INFO);

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

// Functional variables
int currentDay = 0;
unsigned long erraticSpinTargetTime = 0;
bool erraticSpinning = false;
unsigned long lastDebugModeUpdate = 0;
int debugModeState = 0;

// SETTINGS
int minErraticSpinDelay = 100;
int maxErraticSpinDelay = 1250;
int minErraticSpinServo = 40;
int maxErraticSpinServo = 140;

int servoSlowSpin = 95;
int servoStationary = 90;
float erraticSpinUpperRange = 0.25;
float slowSpinUpperRange = 0.5;

int warmColorR = 255, warmColorG = 213, warmColorB = 0;
int coldColorR = 200, coldColorG = 0, coldColorB = 255;

bool debugMode = false;
int debugStateInterval = 3000;
//////

void setStripColor(uint32_t color) {
    for(uint16_t i = 0; i < strip.numPixels(); i++) {
        strip.setPixelColor(i, color);
    }
    strip.show();
}

void setCurrentSentiment(String sentiment) {
    // Gives number between 0 - 1 representing sentiment
    float sentimentValue = sentiment.toFloat();

    // Sets whether to spin erratically or spin servo
    if (sentimentValue <= erraticSpinUpperRange) {
        erraticSpinning = true;
    } else {
        erraticSpinning = false;
        if (sentimentValue <= slowSpinUpperRange) {
            continuousServo.write(servoSlowSpin);
        } else {
            continuousServo.write(servoStationary);
        }
    }
    
    // Interpolates between cold and warm colors
    uint16_t newR = coldColorR + (warmColorR - coldColorR) * sentimentValue;
    uint16_t newG = coldColorG + (warmColorG - coldColorG) * sentimentValue;
    uint16_t newB = coldColorB + (warmColorB - coldColorB) * sentimentValue;
    
    setStripColor(strip.Color(newR, newG, newB));
}

void handleEventResponse(const char *event, const char *data) {
    setCurrentSentiment(String(data));
}

void setup() {
    // Connect to particle
    Serial.begin(9600);
    Particle.subscribe("hook-response/getSentiment/0", handleEventResponse);

    // Attach and setup servo and lights
    continuousServo.attach(D1);
    continuousServo.write(90);
    strip.begin();

    // Demonstrate working lights
    setStripColor(strip.Color(0, 255, 255));
    delay(1000);
    setStripColor(strip.Color(0, 0, 0));
    delay(1000);
}

void loop() {
    if (debugMode) {
        // Cycle between states
        unsigned long currentTime = millis();
        if (currentTime - lastDebugModeUpdate > debugStateInterval) {
            if (debugModeState == 0) {
                setCurrentSentiment(String(erraticSpinUpperRange));
                debugModeState = 1;
            } else if (debugModeState == 1) {
                setCurrentSentiment(String(slowSpinUpperRange));
                debugModeState = 2;
            } else {
                setCurrentSentiment("1");
                debugModeState = 0;
            }
            lastDebugModeUpdate = currentTime;
        }
    } else {
        // Get current day and check if it changes
        int newDay = Time.day();
        if (currentDay != newDay) {
            Particle.publish("getSentiment");
        }
        currentDay = newDay;
    }

    // If we're currently "erraticSpinning", randomly.
    if (erraticSpinning && millis() >= erraticSpinTargetTime) {
        continuousServo.write(random(minErraticSpinServo, maxErraticSpinServo));
        erraticSpinTargetTime = millis() + random(minErraticSpinDelay, maxErraticSpinDelay);
    }
}

Credits

Ali Mostafa
2 projects • 0 followers

Comments