Marc Solc
Published

Caffeine Struggle Data Visualization

A project that visualizes the stock prices of Celsius, Starbucks, and Monster and how students struggle with caffeine and school work.

IntermediateFull instructions provided5 hours93
Caffeine Struggle Data Visualization

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
Need 3 strips, each with 3 LEDs
×1
Micro Servo 9g (S51)
×1
Continuous Servo FS90R
×1
External Power Cord
×1
Jumper wires (generic)
Jumper wires (generic)
×1
Solid Core Wire
×1

Software apps and online services

Particle Build Web IDE
Particle Build Web IDE

Hand tools and fabrication machines

Tape, Electrical
Tape, Electrical
Tape, Clear
Tape, Clear
Hot glue gun (generic)
Hot glue gun (generic)
Soldering iron (generic)
Soldering iron (generic)
Solder Flux, Soldering
Solder Flux, Soldering
Scissor, Electrician
Scissor, Electrician
Cardboard
Yarn
Rubber Band
Worbla (Clay)
Paint
Construction Paper
Popsicle Sticks

Story

Read more

Schematics

Big Picture Circuit

Signal pins are as follows
Micro servo: D1
Continuous motion servo: A2
Single LED strip: D2
2 LED strips in series: MOSI

All components need to be connected to ground and power. A ground and power rail were made. Connect the ground rail to GND on the photon and the negative terminal on the external power cord. Connect the power rail to VUSB on the photon and the positive terminal on the external power cord.

Code

Data Visualization Code

C/C++
The code pulls data from the mentioned API and triggers the components to run either when it is between the hours of 13:00 and 14:00, or by manually triggering an event called "blink".
// This #include statement was automatically added by the Particle IDE.
#include <ArduinoJson.h>

// This #include statement was automatically added by the Particle IDE.
#include <neopixel.h>

// This #include statement was automatically added by the Particle IDE.
#include <ArduinoJson.h>

#include "Particle.h"


SYSTEM_MODE(AUTOMATIC);


SerialLogHandler logHandler(LOG_LEVEL_INFO);

#if (PLATFORM_ID == 32)
// MOSI pin MO
#define PIXEL_PIN SPI1

// MOSI pin D2
// #define PIXEL_PIN SPI1
#else // #if (PLATFORM_ID == 32)
//#define PIXELB_PIN D3
#define PIXEL_PIN D2
#endif
//#define PIXEL_COUNT 7
//#define PIXEL_TYPE WS2812B

Adafruit_NeoPixel stripCelcius(3, PIXEL_PIN, WS2812B);

#if (PLATFORM_ID == 32)
#define PIXELB_PIN SPI
#else
#define PIXELB_PIN MOSI
#endif

Adafruit_NeoPixel stripMonster(6, PIXELB_PIN, WS2812B);

uint32_t Wheel(byte WheelPos);

String jsonText = "";
JsonDocument doc;
double celciusPrice;
double monsterPrice;
double starbucksPrice;
int counter = 0;

int state = 0; //0 = off, 1 = on

Servo noddingServo;
double k = 97.0;
bool isMoving = false;
unsigned long previousMillis = 0;
long interval = 100;
double factor;

Servo clockServo;

bool timeFlag = false;


void setup() {

    stripCelcius.begin();
    stripCelcius.show(); // Initialize all pixels to 'off'
    stripMonster.begin();
    stripMonster.show();
	
	noddingServo.attach(D1);
	clockServo.attach(A2);
	
	Particle.function("blink", run);
	pullData();
	
	Time.zone(-5);

}

int run(String param)
{
    
    Serial.println("time: ");
    Serial.println(Time.hour());
    
    if(state == 0)
    {
        pullData();
        
        factor = (( ((celciusPrice - 25) / 15) + ((monsterPrice - 55) / 5) + ((starbucksPrice - 99) / 16) ) / 3);
        interval = 100 * factor;

        lightLEDs();
        startNodding();
        spinClock();
        
        state = 1;
    }
    else
    {
        state = 0;
        for(int i = 0; i < 7; i++)
            {
                stripCelcius.setPixelColor(i, stripCelcius.Color(0, 0, 0));
                stripMonster.setPixelColor(i, stripMonster.Color(0, 0, 0));
                clockServo.write(93);
            }
            
    }
    stripCelcius.show();
	stripMonster.show();
	
	return 0;
}

void loop()
{
    noddingMotion(); // Call in loop to handle timing
    if(isMoving == false && state == 1)
    {
        startNodding();
    }
    
    if(Time.hour() == 13 && timeFlag == false)
    {
        run("run");
        timeFlag = true;
    }
    else if(Time.hour() != 13 && timeFlag == true)
    {
        timeFlag = false;
    }

}

void pullData()
{
    counter = 0;
    
    Particle.publish("celsiusStockPrice");
    Particle.subscribe("hook-response/celsiusStockPrice/0", handleEventResponse, MY_DEVICES);
}

void lightLEDs()
{
    Serial.println("lightLEDs");
    Serial.print("Celcius price: ");
    Serial.println(celciusPrice);
    Serial.print("Monster price: ");
    Serial.println(monsterPrice);
    Serial.print("Starbucks price: ");
    Serial.println(starbucksPrice);
    
    celciusPrice = mapDouble(celciusPrice, 25, 40, 0, 3);
    for(int i = 0; i < celciusPrice; i++)
    {
        stripCelcius.setPixelColor(i, 255, 255, 0); //yellow
    }
    
    monsterPrice = mapDouble(monsterPrice, 55, 60, 0, 3);
    for(int i = 0; i < monsterPrice; i++)
    {
        stripMonster.setPixelColor(i, stripMonster.Color(255, 0, 0)); //red
    }
    
    starbucksPrice = mapDouble(starbucksPrice, 80, 115, 3, 6);
    for(int i = 3; i < starbucksPrice; i++)
    {
        stripMonster.setPixelColor(i, 0, 255, 0); //green
    }
    
}

void noddingMotion() {
    unsigned long currentMillis = millis();

    if (isMoving) {
        if (currentMillis - previousMillis >= interval) {
            previousMillis = currentMillis;

            if (k <= 150) {
                noddingServo.write(k);
                k += 0.5;
            } else {
                noddingServo.write(97); // Reset to initial position
                k = 97;
                isMoving = false; // Stop motion
            }
        }
    }
}

void startNodding() {
    
    noddingServo.write(100);
    isMoving = true;
    
}

void spinClock()
{
    
    double speed = factor * 180;
    int realSpeed = map((int) speed, 0, 180, 100, 105);
    
    clockServo.write(realSpeed);
    
}

void handleEventResponse(const char *event, const char *data)
{
    jsonText = String(data);
    
    retrieveValues();
}


void retrieveValues()
{
    // The deserializeJSON method turns the JSON text object into a JsonDocument object
    // Note: You must retrieve the internal CONSTANT C representation of the string, c_str()
    DeserializationError error = deserializeJson(doc, jsonText.c_str());
    
    // ---- Error handling code from ArduinoJSON
    if (error)
    {
        Serial.print("deserializeJson() failed: ");
        Serial.println(error.c_str());
        return;
    }
    // ---- end error handling

    if (jsonText != nullptr)
    {
        //Serial.println(counter);
        if(counter == 0)
        {
            celciusPrice = jsonText.toFloat();
            Serial.print("Celsius stock price: ");
            Serial.println(celciusPrice);
            counter++;
            Particle.publish("monsterStockPrice");
            Particle.subscribe("hook-response/monsterStockPrice/0", handleEventResponse, MY_DEVICES);
        }
        else if(counter == 1)
        {
            monsterPrice = jsonText.toFloat();
            Serial.print("Monster stock price: ");
            Serial.println(monsterPrice);
            counter++;
            Particle.publish("starbucksStockPrice");
            Particle.subscribe("hook-response/starbucksStockPrice/0", handleEventResponse, MY_DEVICES);
        }
        else if(counter == 2)
        {
            starbucksPrice = jsonText.toFloat();
            Serial.print("Starbucks stock price: ");
            Serial.println(starbucksPrice);
            counter = 0;
        }
        
    }
    else
    {
        Serial.println("Failed to retrieve 'price' from JSON.");
    }
    
}

// Input a value 0 to 255 to get a color value.
// The colours are a transition r - g - b - back to r.
uint32_t Wheel(byte WheelPos) {
  if(WheelPos < 85) {
   return stripCelcius.Color(WheelPos * 3, 255 - WheelPos * 3, 0);
  } else if(WheelPos < 170) {
   WheelPos -= 85;
   return stripCelcius.Color(255 - WheelPos * 3, 0, WheelPos * 3);
  } else {
   WheelPos -= 170;
   return stripCelcius.Color(0, WheelPos * 3, 255 - WheelPos * 3);
  }
}

//map function for doubles
double mapDouble(double value, double fromStart, double fromEnd, double toStart, double toEnd)
{
    if (fromEnd == fromStart) {
        return value;
    }
    return (value - fromStart) * (toEnd - toStart) / (fromEnd - fromStart) + toStart;
}

Credits

Marc Solc
2 projects • 0 followers

Comments