Find an API or datasource. Using your microcontroller as a client, connect to the source and parse out at least one useful bit of information. Use this information to control a physical output of some sort (LED, motor, other actuator). How does the data relate to the movement - can you make it relevant and meaningful?

For this piece I am aiming to make something functional but also daily enjoyable.

Getting API weather data working

The biggest challenge to this is just finding a provider that has the exact data you are looking for, in JSON, for FREE, and reputable enough that the connection would not die in a few months.

I went with Open Weather Map since they had a student initiative where I could satisfy all those parameters.

Screenshot 2024-11-21 at 4.22.39 PM.png

Below is the Arduino code for ESP32.

#include <WiFi.h>
#include <HTTPClient.h>
#include <Arduino_JSON.h>

const char* ssid = "******";
const char* pass = "******";

String apiKey = "*************";
String lat = "40.7128"; // Latitude for New York
String lon = "-74.0060"; // Longitude for New York
String url = "<https://pro.openweathermap.org/data/2.5/forecast/climate?lat=>" + lat + "&lon=" + lon + "&cnt=30&appid=" + apiKey + "&units=metric";

HTTPClient client;

unsigned long previousMillis = 0;  
const long interval = 24 * 60 * 60 * 1000; // 24 hours in milliseconds

void setup() {
  Serial.begin(115200);
  while (!Serial) { ; };

  // Connect to WiFi network
  Serial.print("Connecting to ");
  Serial.println(ssid);

  // log on to network
  WiFi.begin(ssid, pass);

  // wait to get on
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println("");

  // print put local IP address and verify port
  Serial.println("WiFi connected");
  Serial.println("IP address: ");
  Serial.println(WiFi.localIP());
  
  // start the client with the URL we're going to call
  Serial.print(url);
  client.begin(url);  
  // make the request
  makeRequest();
}

void loop() {
  // only call it every minute
  unsigned long currentMillis = millis();
  if (currentMillis - previousMillis >= interval) {
    // save the time you made the req
    previousMillis = currentMillis;
    // make the request!
    makeRequest();
  }
}

void makeRequest() {
  Serial.println(" client GET...");
  // start connection and send HTTP header
  int httpCode = client.GET();

  // httpCode will be negative on error
  if (httpCode > 0) {
    // HTTP header has been sent and the server response header has been handled
    Serial.print("HTTP GET... code:");
    Serial.println(httpCode);

    // if the file is found at the server
    if (httpCode == HTTP_CODE_OK) {
      // parse the data
      String payload = client.getString();
      parseJSON(payload);
    }
  } else { // if we have a problem, report it
    Serial.print("HTTP GET... failed, error: ");
    Serial.println(client.errorToString(httpCode).c_str());
  }
  // close the connection
  client.end();
}

void parseJSON(String response) {
  // Parse the response into a JSONVar object
  JSONVar forecast = JSON.parse(response);

  // Check if the parsing was successful
  if (JSON.typeof(forecast) == "undefined") {
    Serial.println("Failed to parse JSON");
    return;
  }

  // Print the entire JSON response (for debugging)
  Serial.println(forecast);

  // Access the "list" array
  JSONVar list = forecast["list"];
  if (JSON.typeof(list) != "undefined") {
    for (int i = 0; i < list.length(); i++) {
      // Get each day's weather description
      JSONVar day = list[i];
      JSONVar weather = day["weather"][0];
      String description = weather["description"];
      Serial.println("Day " + String(i + 1) + ": " + description);
    }
  } else {
    Serial.println("Weather list not found.");
  }
}

LEDs and Data

It took a while to figure out how to even power on the LEDs. I had to go down to basics just to test the strand. This seems like a good practice going forward. The problem I had was no external 5V power source since my ESP only have 3.3V. I had to settle with an UNO and hard coded data. I took the array from the API and put it into this code.

Hard coded with API response to showcase data affected by

#include <Adafruit_NeoPixel.h>

// Define pin and LED count
#define LED_PIN 6          // Pin connected to the data line
#define NUM_LEDS 30        // Total number of LEDs (1 for each day)
#define BRIGHTNESS 50      // Brightness level (0-255)

// Create a NeoPixel object
Adafruit_NeoPixel strip(NUM_LEDS, LED_PIN, NEO_GRBW + NEO_KHZ800);

// Weather descriptions for each day
String weatherDescriptions[NUM_LEDS] = {
  "heavy intensity rain", "light rain", "light rain", "scattered clouds",
  "light rain", "light rain", "broken clouds", "scattered clouds",
  "scattered clouds", "few clouds", "light rain", "few clouds",
  "broken clouds", "few clouds", "scattered clouds", "few clouds",
  "few clouds", "few clouds", "light rain", "light rain",
  "moderate rain", "moderate rain", "sky is clear", "sky is clear",
  "few clouds", "light rain", "light rain", "few clouds",
  "sky is clear", "few clouds"
};

// Function to map weather descriptions to RGBW colors
uint32_t getWeatherColor(String description) {
  if (description == "heavy intensity rain") return strip.Color(75, 0, 130, 0);   // Blue-ish Purple
  if (description == "light rain") return strip.Color(64, 64, 255, 0);           // Light Blue
  if (description == "moderate rain") return strip.Color(0, 0, 150, 0);          // Dark Blue
  if (description == "scattered clouds") return strip.Color(200, 200, 200, 0);  // Light Gray
  if (description == "broken clouds") return strip.Color(150, 150, 150, 0);     // Gray
  if (description == "few clouds") return strip.Color(255, 255, 0, 50);         // Light Yellow with white added
  if (description == "sky is clear") return strip.Color(255, 255, 0, 0);        // Yellow
  return strip.Color(0, 0, 0, 0); // Default: Off
}

void setup() {
  // Initialize the LED strip
  strip.begin();
  strip.setBrightness(BRIGHTNESS);
  strip.show(); // Clear all LEDs initially

  // Light up the LEDs based on weather data
  for (int i = 0; i < NUM_LEDS; i++) {
    uint32_t color = getWeatherColor(weatherDescriptions[i]); // Get color for the day's weather
    strip.setPixelColor(i, color); // Set the LED color
  }

  strip.show(); // Update the LED strip
}

void loop() {
  // LEDs remain static for now
}