Adafruit HUZZAH 8266 Arduino Libraries
Be sure to follow the guide below to install the board and libraries. When you've successfully install the ESP8266 Arduino libraries, come back here and continue the tutorial.
Arduino Sketch
The arduino sketch will connect your WiFI network and query the current weather conditions using the Yahoo Weather API. The Feather HUZZAH ESP8266 will then animate some NeoPixels to display the weather condition. In the sketch, you'll need to add your WiFi credentials, change the number of pixels you're using and your desired city.
The github repo contains this project. Below select Download: Project Zip to download both weatherPixels.ino and animation.cpp. Place both these files in an Arduino sketch folder named weatherPixels.
// SPDX-FileCopyrightText: 2019 Anne Barela for Adafruit Industries // // SPDX-License-Identifier: MIT #include <ESP8266WiFi.h> void animSetup(void); void animConfig(uint16_t, uint8_t, uint8_t, uint8_t, uint8_t, uint8_t); void waitForFrame(void); void renderFrame(void); const char* ssid = "adafruit"; const char* password = "ffffffff"; int8_t utc_offset = -5; // hours off of UTC, e.g. EST is -5 const char* location = "boston%2C%20ma"; const char* path_prefix = "/v1/public/yql?q=select%20item.condition.code%2C%20item.condition.text%20%20from%20weather.forecast%20where%20woeid%20in%20(select%20woeid%20from%20geo.places(1)%20where%20text%3D%22"; const char* path_postfix = "%22)&format=json&env=store%3A%2F%2Fdatatables.org%2Falltableswithkeys"; const char* host = "query.yahooapis.com"; const int httpPort = 80; int16_t weathercode = -1; int16_t createhour, createmin; void setup() { Serial.begin(115200); delay(10); // We start by connecting to a WiFi network Serial.println(); Serial.println(); Serial.print("Connecting to "); Serial.println(ssid); WiFi.begin(ssid, password); while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); } Serial.println(""); Serial.println("WiFi connected"); Serial.println("IP address: "); Serial.println(WiFi.localIP()); animConfig(0, 0, 0, 0, 0, 0); animSetup(); } uint32_t timekeep=0xFFFF; void loop() { uint32_t currTime = millis(); // every 30 seconds (or if there's a rollover/first time running, update the weather! if ((timekeep > currTime) || (currTime > (timekeep + 30000))) { timekeep = currTime; updateWeather(); } waitForFrame(); renderFrame(); } void updateWeather() { Serial.print("Connecting to "); Serial.println(host); // Use WiFiClient class to create TCP connections WiFiClient client; if (!client.connect(host, httpPort)) { Serial.println("Connection failed"); return; } // We now create a URI for the request String url = String(path_prefix) + String(location) + String(path_postfix); Serial.print("Requesting URL: "); Serial.println(url); // This will send the request to the server client.print(String("GET ") + url + " HTTP/1.1\r\n" + "Host: " + host + "\r\n" + "Connection: close\r\n\r\n"); delay(500); weathercode = -1; // Read all the lines of the reply from server and print them to Serial while(client.available()){ String line = client.readStringUntil('\r'); int i = line.indexOf(String("\"code\":")); if (i < 0) continue; Serial.println(line); weathercode = (line.substring(i+8)).toInt(); // extract hour and minute i = line.indexOf(String("\"created\":")); if (i < 0) continue; createhour = (line.substring(i+22)).toInt(); createmin = (line.substring(i+25)).toInt(); } Serial.println("Closing connection"); // convert from UTC to local createhour += 24; createhour += utc_offset; createhour %= 24; Serial.print("\nWeather code: "); Serial.print(weathercode); Serial.print(" @ "); Serial.print(createhour); Serial.print(":"); Serial.println(createmin); // Get the current time of day, between 0 and 65535 uint16_t timeofday = map((createhour * 60) + createmin, 0, 1440, 0, 65535); Serial.print("Time of day = "); Serial.print(timeofday); Serial.println("/65535"); /* void animConfig( uint16_t t, // Time of day in fixed-point 16-bit units, where 0=midnight, // 32768=noon, 65536=midnight. THIS DOES NOT CORRESPOND TO // ANY SORT OF REAL-WORLD UNITS LIKE SECONDS, nor does it // handle things like seasons or Daylight Saving Time, it's // just an "ish" approximation to give the sky animation some // vague context. The time of day should be polled from the // same source that's providing the weather data, DO NOT use // millis() or micros() to attempt to follow real time, as // the NeoPixel library is known to mangle these interrupt- // based functions. TIME OF DAY IS "ISH!" uint8_t c, // Cloud cover, as a percentage (0-100). uint8_t r, // Rainfall as a "strength" value (0-255) that doesn't really // correspond to anything except "none" to "max." uint8_t s, // Snowfall, similar "strength" value (0-255). uint8_t l, // Lightning, ditto. uint8_t w) { // Wind speed as a "strength" value (0-255) that also doesn't // correspond to anything real; this is the number of fixed- // point units that the clouds will move per frame. There are // 65536 units around the 'sky,' so a value of 255 will take // about 257 frames to make a full revolution of the LEDs, // which at 50 FPS would be a little over 5 seconds. **************************/ // weathercode = 46; // hardcode weather animation test switch (weathercode) { case 0: // tornado! Serial.println("tornado"); // lotsa cloud, no rain, and wind! animConfig(timeofday, 100, 0, 0, 0, 255); break; case 1: // tropical storm Serial.println("tropical storm"); // no cloud, a lot of rain, no snow, no thunder and lotsa wind! animConfig(timeofday, 0, 255, 0, 0, 255); break; case 2: // hurricane Serial.println("hurricane"); // some cloud, some rain, no snow, no thunder and lotsa wind! animConfig(timeofday, 50, 100, 0, 0, 255); break; case 3: // severe thunder Serial.println("severe thunder"); // some cloud, no rain, no snow, mega lightning, some wind! animConfig(timeofday, 100, 0, 0, 255, 20); break; case 4: // thunder Serial.println("thunder"); // some cloud, no rain, no snow, some lightning, some wind! animConfig(timeofday, 100, 0, 0, 100, 50); break; case 5: // mixed rain + snow case 6: // mixed rain and sleet case 7: // mixed snow and sleet case 18: // sleet case 35: // mixed rain/hail Serial.println("Rain/Snow/Sleet"); // some cloud, some rain, some snow, no lightning, no wind! animConfig(timeofday, 10, 100, 100, 0, 0); break; case 8: // freezing drizzle case 9: // drizzle Serial.println("Drizzle"); // some cloud, a little rain, no snow, no lightning, no wind! animConfig(timeofday, 30, 70, 0, 0, 0); break; case 10: // freezing rain case 11: // showers case 12: // showers Serial.println("Rain/Showers"); // some cloud, lotsa rain, no snow, no lightning, no wind! animConfig(timeofday, 30, 250, 0, 0, 0); break; case 13: // snow flurries case 14: // light snow showers Serial.println("flurries"); // some cloud, no rain, some snow, no lightning, no wind! animConfig(timeofday, 30, 0, 100, 0, 0); break; case 15: // blowing snow Serial.println("blowing snow"); // some cloud, no rain, snow, no lightning, lotsa wind! animConfig(timeofday, 30, 0, 150, 0, 200); break; case 16: // snow case 17: // hail case 42: // scattered snow showers Serial.println("snow"); // some cloud, no rain, snow, no lightning, no wind! animConfig(timeofday, 30, 0, 150, 0, 0); break; case 41: // heavy snow case 43: // heavy snow Serial.println("heavy snow"); // some cloud, no rain, lotsa snow, no lightning, no wind! animConfig(timeofday, 30, 0, 255, 0, 0); break; case 31: // clear (night) case 32: // sunny case 33: // fair (night) case 34: // fair (day) case 25: // hot case 36: // cold Serial.println("Clear/fair"); // no cloud, no rain, no snow, no lightning, no wind! animConfig(timeofday, 0, 0, 0, 0, 0); break; case 23: // blustery case 24: // windy Serial.println("Windy"); // no cloud, no rain, no snow, no lightning, lots wind animConfig(timeofday, 0, 0, 0, 0, 200); break; case 26: // cloudy case 19: // dust Serial.println("Cloudy"); // lotsa cloud, nothing else animConfig(timeofday, 255, 0, 0, 0, 0); break; case 27: // mostly cloudy case 28: // mostly cloudy case 20: // foggy case 22: // smoky Serial.println("mostly Cloudy"); // lotsa cloud, nothing else animConfig(timeofday, 150, 0, 0, 0, 0); break; case 29: // partly cloudy case 30: // partly cloudy case 44: // partly cloudy case 21: // haze Serial.println("Partly Cloudy"); // lotsa cloud, nothing else animConfig(timeofday, 150, 0, 0, 0, 0); break; case 37: // isolated thunderstorms case 47: // isolated thundershowers Serial.println("isolated thunderstorms"); // some cloud, some rain, no snow, some lite, no wind animConfig(timeofday, 30, 150, 0, 30, 0); break; case 38: // scattered thunderstorms case 39: // scattered thundershowers Serial.println("scattered thundershowers"); // some cloud, some rain, no snow, some lite, no wind animConfig(timeofday, 20, 150, 0, 60, 0); break; case 45: // thundershowers Serial.println("thundershowers"); // some cloud, rain, no snow, lite, no wind animConfig(timeofday, 20, 250, 0, 100, 0); break; case 40: // scattered showers Serial.println("scattered showers"); // some cloud, some rain, no snow, no lite, no wind animConfig(timeofday, 30, 50, 0, 0, 0); break; case 46: // snow showers Serial.println("snow showers"); // some cloud, some rain, some snow, no lite, no wind animConfig(timeofday, 30, 100, 100, 0, 0); break; default: break; } /* 25 cold 36 hot 3200 not available */ }
// SPDX-FileCopyrightText: 2019 Anne Barela for Adafruit Industries // // SPDX-License-Identifier: MIT // Weather animation is rendered procedurally based on a few parameters // (time of day, cloud cover, rainfall, etc.). Most of the inputs are NOT // real-world units...see comments for explanation of what's needed. // NeoPixel stuff -------------------------------------------------------- #include <Adafruit_NeoPixel.h> #define NEOPIXEL_PIN 14 // NeoPixels are connected to this pin #define NUM_LEDS 16 // Number of NeoPixels #define FPS 50 // Animation frame rate (frames per second) Adafruit_NeoPixel leds(NUM_LEDS, NEOPIXEL_PIN, NEO_GRB + NEO_KHZ800); // Animation control stuff ------------------------------------------------- uint8_t renderBuf[NUM_LEDS][3], // Each frame of animation is assembled here alphaBuf[NUM_LEDS], // Alpha mask for compositing each layer rainBuf[NUM_LEDS], // Extra mask just for raindrop brightness rainCounter = 1, // Drop-to-drop countdown, in frames rainInterval = 0, // Drop-to-drop interval, frames (0=no rain) windSpeed = 0, // Per-frame cloud motion (see comments) cloudCover = 0; // Percent cloud cover uint16_t sunCenter = 0, // Position of 'sun' in 16-bit sky sunRadius = 8192, // Size of sun (same units) cloudOffset = 0, // Position of cloud bitmap 'seam' timeOfDay = 32768; // Fixed-point day/night value (see notes) uint8_t lightningBrightness = 0; uint8_t lightningIntensity = 0; uint8_t snowIntensity = 0; uint32_t cloudBits = 0; // Bitmask of clouds #if NUM_LEDS < 32 #define NUM_CLOUD_BITS NUM_LEDS #else #define NUM_CLOUD_BITS 32 #endif #define N_STARS (3 + (NUM_LEDS / 7)) struct star { uint8_t pos; uint8_t brightness; } star[N_STARS]; // Flake will "move," then "stop" when it hits the "ground," then fade. // Kinda like raindrops, but moving first. #define MAX_FLAKES (3 + (NUM_LEDS / 7)) struct flake { uint16_t pos; int16_t speed; uint8_t brightness; uint8_t time; } flake[MAX_FLAKES]; uint8_t nFlakes = 0; void randomFlake(void) { flake[nFlakes].pos = random(65536); uint8_t w = windSpeed; if(w < 20) w = 20; do { flake[nFlakes].speed = random(w / -4, (w * 5) / 4); } while(!flake[nFlakes].speed); flake[nFlakes].brightness = random(128, 255); flake[nFlakes].time = random(FPS, FPS * 2); // # frames until snowflake "touches ground" nFlakes++; } uint16_t lightningCounter = 0; extern const uint8_t gamma8[]; // Big table at end of this code // One-time initialization - clears NeoPixels & sets up some variables ----- void animSetup(void) { leds.begin(); leds.setBrightness(200); leds.clear(); // All NeoPixels off ASAP leds.show(); randomSeed(analogRead(A0)); memset(rainBuf, 0, sizeof(rainBuf)); // Clear rain buffer for(uint8_t i=0; i<N_STARS; i++) { // Initialize star positions star[i].pos = random(NUM_LEDS); // TO DO: make stars not overlap star[i].brightness = random(15, 45); } memset(flake, 0, sizeof(flake)); // Clear snowflakes } // Utility functions ------------------------------------------------------- // Set up animation based on some weather attributes like cloud cover, etc. void animConfig( uint16_t t, // Time of day in fixed-point 16-bit units, where 0=midnight, // 32768=noon, 65536=midnight. THIS DOES NOT CORRESPOND TO // ANY SORT OF REAL-WORLD UNITS LIKE SECONDS, nor does it // handle things like seasons or Daylight Saving Time, it's // just an "ish" approximation to give the sky animation some // vague context. The time of day should be polled from the // same source that's providing the weather data, DO NOT use // millis() or micros() to attempt to follow real time, as // the NeoPixel library is known to mangle these interrupt- // based functions. TIME OF DAY IS "ISH!" uint8_t c, // Cloud cover, as a percentage (0-100). uint8_t r, // Rainfall as a "strength" value (0-255) that doesn't really // correspond to anything except "none" to "max." uint8_t s, // Snowfall, similar "strength" value (0-255). uint8_t l, // Lightning, ditto. uint8_t w) { // Wind speed as a "strength" value (0-255) that also doesn't // correspond to anything real; this is the number of fixed- // point units that the clouds will move per frame. There are // 65536 units around the 'sky,' so a value of 255 will take // about 257 frames to make a full revolution of the LEDs, // which at 50 FPS would be a little over 5 seconds. timeOfDay = t; cloudCover = (c > 100) ? 100 : c; rainInterval = r ? map(r, 1, 255, 64, 1) : 0; windSpeed = w; lightningIntensity = l; snowIntensity = s; // Randomize cloud bitmask based on cloud cover percentage: cloudBits = 0; for(uint8_t i=0; i<NUM_CLOUD_BITS; i++) { cloudBits <<= 1; if(cloudCover > random(150)) cloudBits |= 1; } nFlakes = 0; memset(flake, 0, sizeof(flake)); if(s) { uint8_t n = 3 + (snowIntensity * (MAX_FLAKES - 2)) / 256; while(nFlakes < n) { randomFlake(); } } } // Interpolate between two 'packed' (32-bit) RGB colors. // Second argument is weighting (0-255) of second color. uint32_t colorInterp(uint32_t color1, uint32_t color2, uint8_t w) { uint8_t r1 = (color1 >> 16) & 0xFF, g1 = (color1 >> 8) & 0xFF, b1 = color1 & 0xFF, r2 = (color2 >> 16) & 0xFF, g2 = (color2 >> 8) & 0xFF, b2 = color2 & 0xFF; uint16_t w2 = (uint16_t)w + 1, // 1-256 w1 = 257 - w2; // 1-256 r1 = (r1 * w1 + r2 * w2) >> 8; g1 = (g1 * w1 + g2 * w2) >> 8; b1 = (b1 * w1 + b2 * w2) >> 8; return (((uint32_t)r1 << 16) | ((uint32_t)g1 << 8) | b1); } // Using alphaBuf as a mask, fill an RGB color atop renderBuf void overlay(uint8_t r, uint8_t g, uint8_t b) { uint16_t i, a1, a2; for(i=0; i<NUM_LEDS; i++) { a1 = alphaBuf[i] + 1; // 1-256 a2 = 257 - a1; // 1-256 renderBuf[i][0] = (r * a1 + renderBuf[i][0] * a2) >> 8; renderBuf[i][1] = (g * a1 + renderBuf[i][1] * a2) >> 8; renderBuf[i][2] = (b * a1 + renderBuf[i][2] * a2) >> 8; } } // Same as above, for packed 32-bit RGB value void overlay(uint32_t color) { overlay((color >> 16) & 0xFF, (color >> 8) & 0xFF, color & 0xFF); } void waitForFrame(void) { static uint32_t timeOfLastFrame = 0L; uint32_t t; while(((t = millis()) - timeOfLastFrame) < (1000 / FPS)) yield(); timeOfLastFrame = t; } #define NIGHTSKYCLEAR 0x0a1923 #define DAYSKYCLEAR 0x28648c #define NIGHTSKYCLOUDBG 0x2c2425 #define DAYSKYCLOUDBG 0x5e6065 #define NIGHTSKYCLOUDFG 0x515159 #define DAYSKYCLOUDFG 0xc2c2c2 #define NIGHTSNOW 0xa6b1c0 #define DAYSNOW 0xffffff #define SUNCLEAR 0xffff60 #define SUNCLOUDY 0x7a7a61 void renderFrame(void) { // Display *prior* frame of data at start of function -- // this ensures uniform updates, as render time may vary. leds.show(); // Then begin processing next frame... int i; // tod: 0-64K, where 0 = midnight, 32K = noon, 64K = midnight // this is an artistic approximation and doesn't take seasons, // etc into consideration. if you need that, can fudge it into // tod rather than here. // Sunrise and sunset are two 90-minute periods centered around // 6am and 6pm (again, not factoring in seasons, daylight savings // time, etc.). Sky and other effects will interpolate between // day and night states for these two things. long y = timeOfDay; uint8_t dayWeight; if(y > 32767) y = 65536 - y; y = y * 256L / 4096 - 896; dayWeight = (y > 255) ? 255 : ((y < 0) ? 0 : y); // 0-255 night/day // Determine sky and cloud color based on % of cloud cover uint32_t clearSkyColor = colorInterp(NIGHTSKYCLEAR , DAYSKYCLEAR , dayWeight), cloudySkyColor = colorInterp(NIGHTSKYCLOUDBG, DAYSKYCLOUDBG, dayWeight), cloudColor = colorInterp(NIGHTSKYCLOUDFG, DAYSKYCLOUDFG, dayWeight), skyColor = colorInterp(clearSkyColor, cloudySkyColor, map(cloudCover, 30, 70, 0, 255)); for(i=0; i<NUM_LEDS; i++) { renderBuf[i][0] = skyColor >> 16; renderBuf[i][1] = skyColor >> 8; renderBuf[i][2] = skyColor; } // Stars if(dayWeight < 128) { // Dark? Or getting there? uint16_t nightWeight = 257 - dayWeight; memset(alphaBuf, 0, sizeof(alphaBuf)); for(i=0; i<N_STARS; i++) { alphaBuf[star[i].pos] = (nightWeight * random(star[i].brightness/2, star[i].brightness)) >> 8; } overlay(255, 255, 255); } else { sunRadius = map(dayWeight, 128, 255, 1, 8192); uint16_t x; int16_t px1, px2, sx1, sx2; // Clear alpha buffer, gonna render 'sun' there... memset(alphaBuf, 0, sizeof(alphaBuf)); uint32_t sunColor = colorInterp(SUNCLEAR, SUNCLOUDY, map(cloudCover, 30, 70, 0, 255)); // Figure overlap between sun and each pixel... // uint16_t left, right, dist1, dist2; for(i=0; i<NUM_LEDS; i++) { // Pixel coord in fixed-point space x = (i * 65536L) / NUM_LEDS; int16_t foo = sunCenter - x; // sun center in pixel space sx1 = foo - sunRadius; sx2 = foo + sunRadius; px1 = 0; px2 = 65536 / NUM_LEDS; if((sx1 >= px2) || (sx2 < 0)) continue; // No overlap else if((sx1 <= 0) && (sx2 >= px2)) alphaBuf[i] = 255; // Fully encompassed else { if(sx1 > 0) { if(sx2 < px2) { alphaBuf[i] = 255L * (sx2 - sx1) / (px2 - px1); } else { alphaBuf[i] = 255L * (px2 - sx1) / (px2 - px1); } } else { alphaBuf[i] = 255L * (sx2 - px1) / (px2 - px1); } } } overlay(sunColor); // Composite sun atop sky } if(cloudBits) { // Clear alpha buffer, gonna render clouds there... memset(alphaBuf, 0, sizeof(alphaBuf)); uint16_t x, minor; uint8_t major, l, r; for(i=0; i<NUM_LEDS; i++) { x = (i * 65536L) / NUM_LEDS - cloudOffset; // Pixel coord in fixed-point space (0-65535) relative to clouds x = (x * (NUM_CLOUD_BITS * 256UL)) / 65536; // Scale to cloud pixel space major = x >> 8; // Left bit number (0 to NUM_CLOUD_BITS-1) minor = x & 0xFF; // Weight (0-255) of next bit over l = (cloudBits & (1 << major)) ? 220 : 0; // Left bit opacity if(++major >= NUM_CLOUD_BITS) major = 0; // Next bit over r = (cloudBits & (1 << major)) ? 220 : 0; // Right bit opacity alphaBuf[i] = ((l * (257 - minor)) + (r * (minor + 1))) >> 8; // Blend } uint32_t c = colorInterp(NIGHTSKYCLOUDFG, DAYSKYCLOUDFG, dayWeight); overlay(c); // Composite clouds atop sky } if(rainInterval) { memset(alphaBuf, 0, sizeof(alphaBuf)); for(i=0; i<NUM_LEDS; i++) { rainBuf[i] = (rainBuf[i] * (uint16_t)245) >> 8; } // Periodically, randomly, add a drop to rainBuf[] if(!--rainCounter) { i = random(NUM_LEDS); // Which spot? int16_t foo = rainBuf[i] + 255; if(foo > 255) foo = 255; rainBuf[i] = foo; uint8_t r4 = rainInterval / 4; if(r4 < 1) r4 = 1; rainCounter = random(r4, rainInterval); } memcpy(alphaBuf, rainBuf, sizeof(rainBuf)); overlay(130, 130, 150); } if(nFlakes) { uint16_t x, minor; uint8_t major, l, r; memset(alphaBuf, 0, sizeof(alphaBuf)); for(i=0; i<nFlakes; i++) { // Render flake here x = (flake[i].pos * (NUM_LEDS * 256UL)) / 65536; major = x >> 8; // Left pixel number (0 to NUM_LEDS-1) minor = x & 0xFF; // Weight (0-255) of next pixel over alphaBuf[major] = (alphaBuf[major] * (1 + minor)) + (flake[i].brightness * (257 - minor)) >> 8; if(++major >= NUM_LEDS) major = 0; alphaBuf[major] = (alphaBuf[major] * (257 - minor)) + (flake[i].brightness * (1 + minor)) >> 8; flake[i].pos += flake[i].speed; if(flake[i].time) { flake[i].time--; } else { flake[i].brightness = (flake[i].brightness * 253) >> 8; if(!flake[i].brightness) { memcpy(&flake[i], &flake[nFlakes-1], sizeof(struct flake)); // Move last flake to this pos. i--; // Flake moved, so don't increment nFlakes--; // Decrement number of flakes randomFlake(); // And add a new one in last pos. } } } overlay(255, 255, 255); } if(lightningBrightness) { for(i=0; i<NUM_LEDS; i++) alphaBuf[i] = lightningBrightness; overlay(255, 255, 255); lightningBrightness = (lightningBrightness * 220) >> 8; } if(lightningIntensity) { if(!random(50 + (255 - lightningIntensity) * 3)) { i = random(128, 256); if(i > lightningBrightness) lightningBrightness = i; } } sunCenter += 65536 / 30 / FPS; // 30 sec for 1 revolution cloudOffset -= windSpeed; // timeOfDay += 65536/60/FPS; // 1 min for day/night cycle // Convert RGB renderbuf to gamma-corrected LED-native color order: for(uint16_t i=0; i<NUM_LEDS; i++) { leds.setPixelColor(i, pgm_read_byte(&gamma8[renderBuf[i][0]]), pgm_read_byte(&gamma8[renderBuf[i][1]]), pgm_read_byte(&gamma8[renderBuf[i][2]])); } // DON'T call leds.show() here! That's done at start of function. } // Gamma correction improves appearance of midrange colors const uint8_t gamma8[] PROGMEM = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 6, 6, 7, 7, 7, 7, 8, 8, 8, 9, 9, 9, 10, 10, 10, 11, 11, 11, 12, 12, 13, 13, 13, 14, 14, 15, 15, 16, 16, 17, 17, 18, 18, 19, 19, 20, 20, 21, 21, 22, 22, 23, 24, 24, 25, 25, 26, 27, 27, 28, 29, 29, 30, 31, 32, 32, 33, 34, 35, 35, 36, 37, 38, 39, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 50, 51, 52, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 66, 67, 68, 69, 70, 72, 73, 74, 75, 77, 78, 79, 81, 82, 83, 85, 86, 87, 89, 90, 92, 93, 95, 96, 98, 99,101,102,104,105,107,109,110,112,114, 115,117,119,120,122,124,126,127,129,131,133,135,137,138,140,142, 144,146,148,150,152,154,156,158,160,162,164,167,169,171,173,175, 177,180,182,184,186,189,191,193,196,198,200,203,205,208,210,213, 215,218,220,223,225,228,231,233,236,239,241,244,247,249,252,255 };
Enter WiFi Creds & City
In the weatherPixels.ino file, you'll need to input your WiFi credentials and your city. Reference the screenshot above and highlighted text to see which lines to modify.
Note that for the city, you'll need to keep the %2C and %20 text between the city and state/region. %2C encodes into a , an the %20 encodes into a space, when we do the API query
Page last edited January 21, 2025
Text editor powered by tinymce.