Chicago AQI Computational Sculpture
by hponce2 in Circuits > Arduino
72 Views, 0 Favorites, 0 Comments
Chicago AQI Computational Sculpture
My computational sculpture portrays the theme of poison through air pollution in Chicago. Air pollution in Chicago is represented using Chicago's AQI of the current day to set the sculpture to one of 6 stages:
- Good(AQI is 0-50) all LED's are on with no smoke coming of out the smokestack
- Moderate(51-100) 17 LED's on, smoke is 1/5th of the way out
- Unhealthy for sensitive groups(101-150) 13 LED's on, smoke is 2/5th of the way out
- Unhealthy(151-200) 9 LED's on, smoke is 3/5th of the way out
- Very unhealthy(201-300) 5 LED's on, smoke is 4/5th of the way out
- Hazardous(301+) 1 LED on, smoke is all the way out
Here is a link to the project cycling through the 6 stages.
Supplies
- 0-180 degree servo
- 21 NeoPixel LED's
- Protoboard
- Argon
- Cardboard circle for servo pulley
(Doesn't include materials used for the art aspect of the project)
API Webhook
The API for my sculpture comes from Open-Meteo, it provides hourly data of the AQI in Chicago for the current day.
Above is the response template used to get only the list of 24 numbers, an example of the response shown beside it.
This is the link the webhook uses to access the API:
Code
Below is my code, commented segments explain what each part does.
// This #include statement was automatically added by the Particle IDE.
#include <neopixel.h>
#define PIN D2
#define Pixels 21
#define PIXEL_TYPE WS2812B
Adafruit_NeoPixel strip = Adafruit_NeoPixel(Pixels, PIN, PIXEL_TYPE);
Servo myServo;
double avgAQI[24];
int aqiScale;
int count = 0;
int val = 0;
bool lightOOO[21]; //Array of 21 LED's
float state[Pixels];
float fadeRate = 0.45;
void setup()
{
strip.begin();
strip.show();
myServo.attach(3);
for(uint16_t l = 0; l < Pixels; l++) { //sets all lights to on for the blinking code
state[l] = 0;
}
for(int t = 0; t < 21; t++) { //sets all lights to on
lightOOO[t] = false;
}
Particle.publish("AQI");
Particle.subscribe("hook-response/AQI", myHandler, MY_DEVICES);
}
void loop()
{
if (random(200) == 1) { //This part of the code randomly blinks the LED's
uint16_t i = random(Pixels); //in order to make them look like stars
if (state[i] < 1) {
state[i] = random(256);
}
}
for(uint16_t l = 0; l < Pixels; l++) {
if (state[l] > 1) {
strip.setPixelColor(l, 0);
if (state[l] > 1) {
state[l] = state[l] * fadeRate;
}
else {
state[l] = 0;
}
}
else {
strip.setPixelColor(l, 255, 255, 255);
}
}
for(int j = 0; j < 21; j++){ //This for loop checks for which LED's are set to off
if(lightOOO[j]) { //and sets them off, overriding the part of the code
strip.setPixelColor(j, 0); //that blinks the LED's so LED's that are meant to be
} //off aren't turned back on by the blinking code
}
strip.show();
delay(10);
}
void myHandler(const char *event, const char *data)
{ //API response is made into a string, then to a
String dataStr = String(data); //char array, then the first AQI value is made
char strBuffer[100] = ""; //into a double
dataStr.toCharArray(strBuffer, 100);
double firstToken = strtod(strtok(strBuffer, ","), NULL);
avgAQI[0] = firstToken;
double tempStr;
double avgAQINum;
double tempNum = 0;
for(int q = 1; q < 24; q += 1){ //Makes the other 23 AQI values into
tempStr = strtod(strtok(NULL, ","), NULL); //doubles andputs them in the AQI array
avgAQI[q] = tempStr;
}
for(int o = 0; o < 24; o += 1){ //Averages the 24 AQI values
tempNum += avgAQI[o];
}
avgAQINum = tempNum / 24;
if(avgAQINum >= 0 && avgAQINum <= 50){ //This if else statement scales the average AQI
aqiScale = 0; //number to the number of LED's that should be
} //turned off
else if(avgAQINum > 50 && avgAQINum <= 100){
aqiScale = 4;
}
else if(avgAQINum > 100 && avgAQINum <= 150){
aqiScale = 8;
}
else if(avgAQINum > 150 && avgAQINum <= 200){
aqiScale = 12;
}
else if(avgAQINum > 200 && avgAQINum <= 300){
aqiScale = 16;
}
else if(avgAQINum>300){
aqiScale = 20;
}
while((count) != (aqiScale)){ //This while loop takes the number of LED's that should
lightOOO[random(21)] = true; //be off and sets that amount of random LED's to off
for(int v = 0; v<21;v++) //without accidentally setting one LED to off twice
{
if(lightOOO[v])
count += 1;
}
if((count) != (aqiScale)){
count = 0;
}
}
val = aqiScale; //Scales the number of LED's off to where the servo should be
val = map(val, 0, 20, 80, 180); //then sets the servo to that degree
myServo.write(val);
delay(10);
}