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.
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.
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.");
}
}
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
}