Mirza Mehmedagic
Published

Lane Tech HS - PCL - Data Visualization

Based on crimes or COVID-19 cases in a particular area today compared to yesterday, see a visualization on whether to go outside or not.

BeginnerProtip2.5 hours187
Lane Tech HS - PCL - Data Visualization

Things used in this project

Hardware components

Argon
Particle Argon
×1
Breadboard (generic)
Breadboard (generic)
×1
SG90 Micro-servo motor
SG90 Micro-servo motor
×1
Elegoo Button Switch Module
*From the 37 Sensor kit. A simple pushbutton on the breadboard could also suffice, but this is what I had available.
×1
Male/Male Jumper Wires
*For the micro-servo motor and breadboard.
×1
Male/Female Jumper Wires
Male/Female Jumper Wires
*For the button switch module.
×1

Software apps and online services

Particle Build Web IDE
Particle Build Web IDE
City of Chicago COVID-19 Case Data
City of Chicago Crime Data

Story

Read more

Schematics

Bird's Eye View with Scale

Mirco-servo motor and button switch module with Argon on breadboard.

For the micro-servo: Yellow servo wire is connected to D6 pin through black male-male jumper wire, red servo wire is connected to positive power rail through white male-male jumper wire, brown servo wire is connected to ground power rail through gray male-male jumper wire.

For the button switch: Black male-female jumper wire is connected to D5 pin, white male-female jumper wire is connected to positive power rail, gray male-female jumper wire is connected to ground power rail.

Positive power rail is connected to VUSB pin (5 Volt source) through blue male-male jumper wire. Ground power rail is connected to GND pin through orange male-male jumper wire.

Hand-drawn Schematic

Fritzing Breadboard

Replace the Photon with an Argon (and VBAT pin with VUSB pin) and the humidity sensor with a button switch module/sensor.

Fritzing Schematic

Replace the Photon with an Argon (and VBAT pin with VUSB pin) and the humidity sensor with a button switch module/sensor.

Code

Data-Visualization-Project

C/C++
To be used in Particle IDE. Serial monitor testing was commented out.
Servo servo;

const int servoPin = D6;
const int buttonPin = D5;

int reading; // What the button input state reads
int lastButtonState = HIGH; // Button input state to update for debounce
int buttonState = HIGH; // Button input state to update for debounce

unsigned long lastDebounceTime = 0;
unsigned long debounceDelay = 50;

// Lengths of these strings are used for finding the data we need
const String dateFormat = "YYYY-MM-DD";
const String timeFormat = "THH:MM:SS.XXX";
const String value = "\"updated_on\"";

// Dates to compare
String dateBefore;
String dateAfter;

// Crime counters, raw difference comparison, and angle comparison
int crimesBefore = 0;
int crimesAfter = 0;
int crimesDiff;
int crimesAngle;

// COVID-19 case counters, checks-if-done, raw difference comparison, and angle comparison
int casesBefore = 0;
int casesAfter = 0;
bool beforeDone = false;
bool afterDone = false;
int casesDiff;
int casesAngle;

// Toggler for button debounce
bool forCases = false;

void setup() {
    // Serial.begin(9600);
    
    Time.zone(-6); // Set time zone to Central Stadard Time (CST)
    
    // Get necessary dates for comparison
    dateBefore = getDate(-3); // 3 days before today, will act as "yesterday" for comparison
    dateAfter = getDate(-2); // 2 days before today, will act as "today" for comparison
    
    // Subscribe to the webhook response events
    Particle.subscribe("hook-response/CityOfChicagoCrimeData", crimeHandler, MY_DEVICES); // Returns a JSON object array (separated into multiple strings) that contains datd for crimes during or after a given date
    Particle.subscribe("hook-response/CityOfChicagoCaseData", caseHandler, MY_DEVICES); // Retruns a date, time, and case amout for a given date

    // Publish events to trigger webhooks
    Particle.publish("CityOfChicagoCrimeData", dateBefore, PRIVATE); 
    Particle.publish("CityOfChicagoCaseData", dateBefore, PRIVATE); // Gets COVID-19 case data only for given date
    Particle.publish("CityOfChicagoCaseData", dateAfter, PRIVATE); // Gets COVID-19 case data only for given date
    
    // Prepare inpuit and output for  hardware
    pinMode(buttonPin, INPUT_PULLUP);
    servo.attach(servoPin);
}

void loop() {
    reading = digitalRead(buttonPin); // Read button state

  	if (reading != lastButtonState) lastDebounceTime = millis(); // Check if button pressed
  	
  	// Debounce the botton so that one press is one toggle
   	if ((millis() - lastDebounceTime) > debounceDelay) {
      	if (reading != buttonState) { // Check if button input state changed
          	buttonState = reading; // Update button state
     		if (buttonState == LOW) {
     		    forCases = !forCases;
     		    
                if (forCases) {
                    servo.write(casesAngle);
                    // Serial.print("Cases Angle: " + String(casesAngle) + "; ");
                } else if (!forCases) {
                    servo.write(crimesAngle);
                    // Serial.print("Crimes Angle: " + String(crimesAngle) + "; ");
                }
                
     		}
     		
     		// Serial.println("Button: " + String(buttonState) + "; Boolean: " + String(forCases));
        }
   	}
  	
  	lastButtonState = reading; // Update button state
}

void crimeHandler(const char *event, const char *data) {
    String date = findDate(data); //
    
    
    if (date == dateBefore) crimesBefore++;
    else if (date == dateAfter) crimesAfter++;
    
    if (crimesDiff != crimesAfter - crimesBefore) {
        crimesDiff = crimesAfter - crimesBefore;
        crimesAngle = map(crimesDiff, -9, 9, 0, 180);  // Find an angle that we can write on the servo for the change in crimes 
        
        // Serial.println("\n\n");
        // Serial.println("crimesDiff: " + String(crimesDiff));
        // Serial.println("crimesAngle: " + String(crimesAngle));
        // Serial.println("\n\n");
    }
}

void caseHandler(const char *event, const char *data) {
    String strData = String(data); // Repsonse template for date (& time) followed by number of cases number
    
    String date = strData.substring(0, dateFormat.length()); // Isolate the date
    strData = strData.substring(dateFormat.length()); // Get rid of the date in the original string
    strData = strData.substring(timeFormat.length() + 1); // Get rid of the time (and the comma after time) in the original string
    int cases = atoi(strData); // Get the cases as an int from the remaining original string
    
    // Check which event was publcished as it goes out of order sometimes
    if (date == dateBefore) {
        casesBefore = cases;
        beforeDone = true;
    } else if (date == dateAfter) {
        casesAfter = cases;
        afterDone = true;
    }
    
    if (beforeDone && afterDone) { // If we have retrieved both data points
        casesDiff = casesAfter - casesBefore; 
        casesAngle = map(casesDiff, -900, 900, 0, 180); // Find an angle that we can write on the servo for the change in cases 
        
        // Serial.println("\n\n");
        // Serial.println("casesDiff: " + String(casesDiff));
        // Serial.println("casesAngle: " + String(casesAngle));
        // Serial.println("\n\n");
    }
}

// Find a date of the format YYYY-MM-DD after the JSON value we want in a string representation of a part of the data
String findDate(String data) {
    int index = data.indexOf(value); // The JSON value in question is always "updated_on"
    int startOfDate = index + value.length() + 2; // Date startes after the JSON value (and extra colon and quote mark)
    int endOfDate = startOfDate + dateFormat.length();
    String date = data.substring(startOfDate, endOfDate);
    return date;
}

// Retrieve a date of the format YYYY-MM-DD, given a shift in date from today
String getDate(int shift) {
    int daysInMonth[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
    
    int year = Time.year();
    int month = Time.month();
    int day = Time.day();
    
    while (shift < 0) { // This means we are finding a date before today
        day--;
        shift++;
        
        if (day == 0) { // If the day underflows, go to the previous month's last day
            month--;
            
            if (month == 0) { // If the month underflows, go to the previous year's last month
                month = 12;
                year--;
            } else if (month == 2) { // If the previous month is February, check for leap year
                if (year % 4 == 0) { // Leap year
                    daysInMonth[month - 1]++;
                    if (year % 100 == 0) { // Not a leap year
                        daysInMonth[month - 1]--;
                        if (year % 400 == 0) { // Leap year
                            daysInMonth[month - 1]++;
                        }
                    }
                }
            }
            
            day = daysInMonth[month - 1];
        }
    }
    
    while (shift > 0) { // This means we are finding a date after today
        day++;
        shift--;
        
        if (day == daysInMonth[month - 1] + 1) { // If the day overflows, go to the next month's first day
            month++;
            
            if (month == 13) { // If the month overflows, go to the next year's first month
                month = 1;
                year++;
            }
                
            day = daysInMonth[month - 1];
        }
    }

    if (day < 10 && month < 10) return String(year) + "-0" + String(month) + "-0" + String(day);
    else if (day < 10) return String(year) + "-"  + String(month) + "-0" + String(day);
    else if (month < 10) return String(year) + "-0" + String(month) + "-"  + String(day);
    else return String(year) + "-"  + String(month) + "-"  + String(day);
}

Credits

Mirza Mehmedagic

Mirza Mehmedagic

3 projects • 2 followers

Comments

Add projectSign up / Login