// 4/30/2019 // In Memory of Hugh Magee, brother of John Magee author of poem HIGH FLIGHT // https://en.wikipedia.org/wiki/John_Gillespie_Magee_Jr. // REMARKS: Working !!! // Added time and Density Altitude 3/31 // Communication Error - POWER OFF for 30 Secs to RESET 4/2 // ADDED wifiMulti.addAP(ssid, password) 4/21 // ADDED extra Led Show 4/23 // UPDATED logical statements 4/26 // ADDED Start Time // REMOVED delay_time on first loop 4/29 // ADDED Twinkle for Weather 4/30 //#include #include #include #include #include WiFiMulti wifiMulti; #include "time.h" #define LED_BUILTIN 2 // ON Board LED GPIO 2 String FileName = "METAR_ESP32_4W"; //const char* ssid = "iPhone"; // your network SSID (name) //const char* password = "password"; // your network password const char* ssid = "NETGEAR46"; // your network SSID (name) const char* password = "password"; // your network password const char* ntpServer = "pool.ntp.org"; const long gmtOffset_sec = -7200; const int daylightOffset_sec = -7200; // Test link: https://www.aviationweather.gov/adds/dataserver_current/httpparam?datasource=metars&requestType=retrieve&format=xml&mostRecentForEachStation=constraint&hoursBeforeNow=1.25&stationString=KFLL String host = "https://aviationweather.gov"; String urlb = "/adds/dataserver_current/httpparam?datasource=metars&requestType=retrieve&format=xml&mostRecentForEachStation=constraint&hoursBeforeNow=1.25&stationString="; // Set Up LEDS #define NUM_AIRPORTS 52 // Also the number of LEDs 52/ok CRGB leds[NUM_AIRPORTS]; #define DATA_PIN 5 // Connect to pin D5/P5 #define LED_TYPE WS2812 #define COLOR_ORDER GRB // WD2811 are RGB or WS2812 are GRB #define BRIGHTNESS 20 // LED Brightness max 36 tested // Set Up STATIONS std::vector stations({ "NULL", // 0 order of LEDs; NULL for no airport "KCHA", // 1 CHATTANOOGA,TN "KRMG", // 2 ROME,GA "KVPC", // 3 CARTERSVILLE,GA "KATL", // 4 ATLANTA,GA "KCTJ", // 5 CARROLTON,GA "KLGC", // 6 LA GRANGE,GA "KCSG", // 7 COLUBUS,GA "KMCN", // 8 MACON,GA "KCKF", // 9 CORDELLE,GA "KABY", // 10 ALBANY,GA "KTLH", // 11 TALLAHASSEE,FL "KVLD", // 12 VALDOSTA,GA "KAYS", // 13 WAYCROSS,GA "KJAX", // 14 JACKSONVILLE,FL "KBQK", // 15 BRUNSWICK,GA "KSAV", // 16 SAVANNAH,GA "KTBR", // 17 STATESBORO,GA "KAGS", // 18 AUGUSTA,GA "KAHN", // 19 ATHENS,GA "KCEU", // 20 CLEMSON,GA "KJES", // 21 JESUP,GA (my home) "KBHC", // 22 BAXLEY,GA "KAZE", // 23 HAZLEHURST,GA "KRSW", // 24 FT MYERS,FL "KLGB", // 25 LONG BEACH,CA "KMKC", // 26 KANSAS CITY,MO "KMER", // 27 ATWATER,CA "KCVG", // 28 CINCINNATI,OH "KBWI", // 29 WASHINGTON,DC "KORD", // 30 CHICAGO,IL "KMEM", // 31 MEMPHIS,TN "KMSY", // 32 NEW ORLEANS,LA "KSDF", // 33 LOUISVILLE,KY "KBOS", // 34 BOSTON,MA "KCLT", // 35 CHARLOTTE,NC "KCHS", // 36 CHARLESTON,SC "KMYR", // 37 MYRTLE BEACH,SC "KJFK", // 38 KENNEDY,NY "KHOU", // 39 HOUSTON,TX "KPBI", // 40 WEST PALM BEACH,FL "KHWO", // 41 NTH PERRY,FL "KFXE", // 42 FT LAUDERDALE,FL "KFLL", // 43 FT LAUDERDALE,FL "KMIA", // 44 MIAMI,FL "KLAL", // 45 LAKELAND,FL "KORL", // 46 ORLANDO,FL "KEYW", // 47 KEY WEST,FL "KTPA", // 48 TAMPA,FL "KPIE", // 49 CLEARWATER,FL "KSPG", // 50 ST PETERSBERG,FL "EGLL", // 51 HEATHROW,UK "EGKK", // 52 GATWICK,UK }); String rem[NUM_AIRPORTS + 1]; // Remarks String wx[NUM_AIRPORTS + 1]; // Weather String temp[NUM_AIRPORTS + 1]; // temperature deg C String wind[NUM_AIRPORTS + 1]; // wind speed String visab[NUM_AIRPORTS + 1]; // visibility String sky[NUM_AIRPORTS + 1]; // sky_cover & cloud_base String category[NUM_AIRPORTS + 1]; //NULL VFR MVFR IFR LIFR //..................................Black Green Blue Red Magenta String metar; boolean first_time = true; String start_time; void setup() { pinMode(LED_BUILTIN, OUTPUT); // the onboard LED Serial.begin(115200); delay(1200); // Wait a little bit Serial.println("File Name ; " + FileName ); digitalWrite(LED_BUILTIN, HIGH); //ON Serial.print("WiFi Connecting to "); Serial.print(ssid); WiFi.begin(ssid, password); while (WiFi.status() != WL_CONNECTED) { delay(200); Serial.print("."); } Serial.print(WiFi.localIP()); Serial.println(" Connected"); digitalWrite(LED_BUILTIN, LOW); //OFF WiFi.mode(WIFI_STA); wifiMulti.addAP(ssid, password); //init and print the time configTime(gmtOffset_sec, daylightOffset_sec, ntpServer); PrintLocalTime(); } void loop() { if (first_time == true) Startleds(); GetMetar(); Dataprint(); Serial.println("Display All Weather [White]"); UpdateWeather_LEDS(6000); // for 6 Sec Serial.println("Display All Winds [Aqua]"); UpdateWind_LEDS(6000); // for 6 Sec Serial.println("Display All Temperatues [Yellow]"); UpdateTemp_LEDS(6000); // for 6 Sec Serial.println("Display All Categories"); UpdateCategory_LEDS(100); // with 100ms delay first_time = false; Serial.println("Updating again"); } // *********** GET & Print Local Time void PrintLocalTime() { struct tm timeinfo; if (!getLocalTime(&timeinfo)) { Serial.println("Failed to obtain time"); return; } Serial.println(&timeinfo, "%A, %B %d %Y %H:%M:%S"); if (first_time == true) { char output[10]; strftime(output, 10, "%H:%M:%S", &timeinfo); start_time = String(output); Serial.println("Start Time = " + start_time); } } // *********** Initialize LEDs at Start with rolling pattern void Startleds() { Serial.print("Initializing LEDs for NUM_AIRPORTS = "); Serial.println(NUM_AIRPORTS); FastLED.addLeds(leds, NUM_AIRPORTS).setCorrection(TypicalLEDStrip); // Set all leds to Black fill_solid(leds, NUM_AIRPORTS, CRGB::Black); FastLED.show(); delay(200); // Wait a little bit FastLED.setBrightness(BRIGHTNESS); // Set all leds to Orange fill_solid(leds, NUM_AIRPORTS, CRGB::Orange); FastLED.show(); delay(1000); // Wait a little bit for (int whiteLed = 0; whiteLed < NUM_AIRPORTS; whiteLed = whiteLed + 1) { leds[whiteLed] = CRGB::White; FastLED.show(); delay(200); // Wait a little bit leds[whiteLed] = CRGB::Black; } } // *********** GET Metar Routine void GetMetar() { for (int i = 1; i < NUM_AIRPORTS + 1; i++) { metar = ""; // Reset Metar Data String station = stations[i]; Serial.print("\nGetting METAR for " + station + " # "); Serial.println(i); UpdateLED (i, "Orange"); // updating this led String url = String (host) + String (urlb) + String (station); //Serial.println("url = " + url); // Check for WiFi connection if ((wifiMulti.run() == WL_CONNECTED)) { digitalWrite(LED_BUILTIN, HIGH); // ON HTTPClient http; http.begin(url); // Start connection and send HTTP header int httpCode = http.GET(); // httpCode will be negative on error if (httpCode > 0) { // HTTP header has been send and Server response header has been handled // file found at server if (httpCode == HTTP_CODE_OK) { metar = http.getString(); } http.end(); digitalWrite(LED_BUILTIN, LOW); //OFF } else { Serial.printf("[HTTP] GET... failed, error: %s\n", http.errorToString(httpCode).c_str()); Serial.print("HTTP reply code = "); Serial.println(httpCode); http.end(); UpdateLED (i, "Purple"); // updating this led Serial.println("Start Time = " + start_time); PrintLocalTime(); Serial.print("Error Wait min "); int errwait = 4; Serial.println(errwait); delay (60000 * errwait); // 4 Min delay digitalWrite(LED_BUILTIN, LOW); //OFF } // Serial.println("RAW DATA: " + metar); // Response from Server int data_start = metar.indexOf(station); // Looks for station int data_end = metar.indexOf(" 0 ) { metar = metar.substring(data_start, data_end); // Serial.println("METAR: " + metar); // Parsed Metar Data Decodedata(i, station); // Decode the Data Routine } else { // if ( data_start > 0 ) UpdateLED (i, "Orange"); // updating this led Serial.print("Station # " ); Serial.print(i); Serial.println(" " + stations[i] + " data not found, skipping this one"); } } else { // WiFi Connection failed Serial.print("WiFi Connection = "); UpdateLED (i, "Purple"); // updating this led Serial.print(WiFi.status()); if (WiFi.status() == 0 ) Serial.println(" : IDLE"); if (WiFi.status() == 3 ) Serial.println(" : CONNECTED"); if (WiFi.status() == 4 ) Serial.println(" : FAILED"); if (WiFi.status() == 5 ) Serial.println(" : LOST"); if (WiFi.status() == 6 ) Serial.println(" : DISCONNECTED"); } if ( first_time == false ) { Serial.println("Led Show / Time Delay"); UpdateWeather_LEDS(2000); // Display All Weather [White] for 2 Sec UpdateWind_LEDS(3000); // Display All Winds [Aqua] for 43 Sec UpdateTemp_LEDS(3000); // Display All Temperatues [Yellow] for 3 Sec UpdateCategory_LEDS(50); // Display All Categories with 50ms delay delay(2000); UpdateWeather_LEDS(2000); // Display All Weather [White] for 2 Sec UpdateWind_LEDS(3000); // Display All Winds [Aqua] for 3 Sec UpdateTemp_LEDS(3000); // Display All Temperatues [Yellow] for 3 Sec UpdateCategory_LEDS(50); // Display All Categories with 50ms delay delay(2000); UpdateWeather_LEDS(2000); // Display All Weather [White] for 2 Sec UpdateWind_LEDS(3000); // Display All Winds [Aqua] for 3 Sec UpdateTemp_LEDS(3000); // Display All Temperatues [Yellow] for 3 Sec UpdateCategory_LEDS(50); // Display All Categories with 50ms delay // 20+ Sec built in delay } } } // *********** DECODE the Data Routine void Decodedata(int i, String station) { // Reading Remarks int search0 = metar.indexOf(station) + 7; int search1 = metar.indexOf("/raw_text"); int search2 = metar.indexOf("RMK"); if (search2 > 0) search1 = search2; rem[i] = (metar.substring(search0, search1 - 1)); Serial.println(stations[i] + " Remarks = " + rem[i]); // Reading Significant Weather in array rem and making a readable wx wx[i] = ""; search0 = rem[i].indexOf("G"); if (search0 > 6 && search0 < 18) { search1 = rem[i].indexOf("KT"); wx[i] = "Gusts to " + (rem[i].substring(search1 - 2, search1 + 3)); } if (rem[i].indexOf(" +") > 0) wx[i] = wx[i] + "Heavy "; if (rem[i].indexOf(" -") > 0) wx[i] = wx[i] + "Light "; if (rem[i].indexOf("BC") > 0) wx[i] = wx[i] + "Patches "; if (rem[i].indexOf("BL") > 0) wx[i] = wx[i] + "Blowing "; if (rem[i].indexOf("DR") > 0) wx[i] = wx[i] + "Drifting "; if (rem[i].indexOf("FZ") > 0) wx[i] = wx[i] + "Freezing "; if (rem[i].indexOf("MI") > 0) wx[i] = wx[i] + "Shallow "; if (rem[i].indexOf("PR") > 0) wx[i] = wx[i] + "Partial "; if (rem[i].indexOf("SH") > 0) wx[i] = wx[i] + "Showers "; if (rem[i].indexOf("DZ") > 0) wx[i] = wx[i] + "Drizzle "; if (rem[i].indexOf("GR") > 0) wx[i] = wx[i] + "Hail Large "; if (rem[i].indexOf("GS") > 0) wx[i] = wx[i] + "Hail Small "; if (rem[i].indexOf("IC") > 0) wx[i] = wx[i] + "Ice "; if (rem[i].indexOf("RA") > 0) wx[i] = wx[i] + "Rain "; if (rem[i].indexOf("SG") > 0) wx[i] = wx[i] + "Snow Grains "; if (rem[i].indexOf("SN") > 0) wx[i] = wx[i] + "Snow "; if (rem[i].indexOf("BR") > 0) wx[i] = wx[i] + "Mist "; if (rem[i].indexOf("DU") > 0) wx[i] = wx[i] + "Dust "; if (rem[i].indexOf("FG") > 0) wx[i] = wx[i] + "Fog "; if (rem[i].indexOf("FU") > 0) wx[i] = wx[i] + "Smoke "; if (rem[i].indexOf("HZ") > 0) wx[i] = wx[i] + "Haze "; if (rem[i].indexOf("FY") > 0) wx[i] = wx[i] + "Spray "; if (rem[i].indexOf("SA") > 0) wx[i] = wx[i] + "Sand "; if (rem[i].indexOf("TS") > 0) wx[i] = wx[i] + "Thunderstorm "; if (rem[i].indexOf("VA") > 0) wx[i] = wx[i] + "Volcanic Ash "; if (rem[i].indexOf(" VC") > 0) wx[i] = wx[i] + "in Vicinity "; if (rem[i].indexOf("TCU") > 0) wx[i] = wx[i] + "Towering Cumulus "; if (rem[i].indexOf("CB ") > 0) wx[i] = wx[i] + "Cumulonimbus "; if (rem[i].indexOf("CBMAM") > 0) wx[i] = wx[i] + "CB Mammatus "; Serial.print(stations[i] + " Significant Weather = "); if (wx[i] == "") Serial.println("NONE"); else Serial.println(wx[i]); // Reading temp_c search0 = metar.indexOf(" 11) sky[i] = (metar.substring(search0, search0 + 3)); search0 = metar.indexOf("_ft_agl=") + 9; search1 = metar.indexOf(" />") - 1; if (sky[i] == "OVX") sky[i] = "OBSECURED"; if (sky[i] == "CAV") sky[i] = "CLEAR "; if (sky[i] == "CLR") sky[i] = "CLEAR "; if (sky[i] == "SKC") sky[i] = "CLEAR "; if (sky[i] == "BKN") sky[i] = sky[i] + " at " + (metar.substring(search0, search1)); if (sky[i] == "FEW") sky[i] = sky[i] + " at " + (metar.substring(search0, search1)); if (sky[i] == "SCT") sky[i] = sky[i] + " at " + (metar.substring(search0, search1)); if (sky[i] == "OVC") sky[i] = sky[i] + " at " + (metar.substring(search0, search1)); Serial.println(stations[i] + " Sky_cover = " + sky[i]); // Reading flight_category search0 = metar.indexOf("") + 13; search1 = metar.indexOf(""); if (search0 > 13) altim = (metar.substring(search0, search1)); float Pressure = altim.toFloat(); //Serial.println(stations[i] + " Altim_in_hg = " + altim); // Reading elevation_m String elevation = "NA"; search0 = metar.indexOf(" 13) elevation = (metar.substring(search0, search1)); float Elevation = elevation.toFloat(); Serial.print(stations[i] + " Elevation_m = " + elevation + " : Ft = "); Serial.println(Elevation * 3.28); // Calculating Pressure Altitude float PressAlt = (Elevation * 3.28) + (1000 * (29.92 - Pressure)) ; //Serial.print(stations[i] + " Pressure Altitude Ft = "); //Serial.println(PressAlt); // Calculating Density Altitude float DensityAlt = PressAlt + (120 * (Temp - (15 - (2 * Elevation * 3.28 / 1000)))); Serial.print(stations[i] + " Estimated Density Altitude Ft = "); Serial.println(DensityAlt); metar = ""; // Reset Metar Data UpdateLED (i, "Update"); // update this led } // *********** Display one Station LED void UpdateLED(int i, String col) { int n = i - 1; // LED Number if (col == "Update") { if (wx[i].length() > 1 ) { leds[n] = CRGB(100, 100, 100); // White WEATHER Here FastLED.show(); delay(2000); } int Wind = wind[i].toInt(); leds[n] = CRGB(0, Wind * 5, Wind * 5); // Shades of Aqua for Wind FastLED.show(); delay(2000); int Temp = temp[i].toInt(); leds[n] = CRGB(Temp * 3, Temp * 3, 0); // Shades of Yellow for Temp FastLED.show(); delay(2000); if (category[i] == "VFR" ) leds[n] = CRGB::Green; if (category[i] == "MVFR") leds[n] = CRGB::Blue; if (category[i] == "IFR" ) leds[n] = CRGB::Red; if (category[i] == "LIFR") leds[n] = CRGB::Magenta; if (category[i] == "NA ") leds[n] = CRGB::Black; } if (col == "Black") leds[n] = (0x000000); // Not Available if (col == "White") leds[n] = (0xA0A0A0); // Weather if (col == "Yellow") leds[n] = (0xFFFF00); // Temperature if (col == "Orange") leds[n] = (0xFF8000); // Reading Data/Not found if (col == "Purple") leds[n] = (0x500050); // Communication Error if (col == "Aqua") leds[n] = (0x00FFFF); // Wind Serial.println("Led color " + col ); FastLED.show(); delay(2000); } // *********** Display All Stations for Weather [White] void UpdateWeather_LEDS(int wait) { fill_solid(leds, NUM_AIRPORTS, CRGB::Black); for (int i = 1; i < (NUM_AIRPORTS + 1); i++) { int n = i - 1; // LED Number if (wx[i].length() > 1 ) leds[n] = CRGB(100, 100, 100); } FastLED.show(); delay(wait); for (int i = 1; i < (NUM_AIRPORTS + 1); i++) { int n = i - 1; // LED Number // Call Twinkle for Weather (red,green,blue, pulses, on time, led no); if (wx[i].indexOf("KT") > 0) Twinkle(0,0xff, 0xff, 2, 60, n);//Winds Aqua if (rem[i].indexOf("FG")> 0) Twinkle(0x22,0x88,0x22, 3, 50, n);//Fog L Green if (rem[i].indexOf("BR")> 0) Twinkle(0x22,0x88,0x22, 3, 50, n);//Mist L Green if (rem[i].indexOf("RA")> 0) Twinkle(0, 0xff, 0, 4, 40, n);//Rain Green if (rem[i].indexOf("TS")> 0) Twinkle(0xff,0xff,0xff, 5, 10, n);//Thunder White if (rem[i].indexOf("CB")> 0) Twinkle(0xff,0xff,0xff, 6, 10, n);//Thunder White } delay(2000); } // *********** Display All Stations for Winds [Aqua] void UpdateWind_LEDS(int wait) { for (int i = 1; i < (NUM_AIRPORTS + 1); i++) { int n = i - 1; // LED Number int Wind = wind[i].toInt(); leds[n] = CRGB(0, Wind * 5, Wind * 5); } FastLED.show(); delay(wait); } // *********** Display All Stations for Temperatures [Yellow] void UpdateTemp_LEDS(int wait) { for (int i = 1; i < (NUM_AIRPORTS + 1); i++) { int n = i - 1; // LED Number int Temp = temp[i].toInt(); leds[n] = CRGB(Temp * 3, Temp * 3, 0); } FastLED.show(); delay(wait); } // *********** Display rolling stations for all Categories void UpdateCategory_LEDS(int wait) { for (int i = 1; i < (NUM_AIRPORTS + 1); i++) { int n = i - 1; // LED Number if (category[i] == "VFR") leds[n] = CRGB::Green; if (category[i] == "MVFR") leds[n] = CRGB::Blue; if (category[i] == "IFR") leds[n] = CRGB::Red; if (category[i] == "LIFR") leds[n] = CRGB::Magenta; if (category[i] == "NA ") leds[n] = CRGB::Black; delay(wait); FastLED.show(); } } // *********** Twinkle for Wind, Rain & Thumderstorms // To Call Twinkle(red,green,blue, pulses, delay, led no); void Twinkle(byte red, byte green, byte blue, int Count, int SpeedDelay, int Pixel) { for (int i = 0; i < Count; i++) { leds[Pixel].r = 0x00; // Red Off leds[Pixel].g = 0x00; // Green Off leds[Pixel].b = 0x00; // Blue Off FastLED.show(); delay(20); leds[Pixel].r = red; // Red On leds[Pixel].g = green; // Green On leds[Pixel].b = blue; // Blue On FastLED.show(); delay(SpeedDelay); } } // *********** Print the Station Parameters void Dataprint() { // Print header Serial.println("\nCurrent Weather Conditions Note: Zulu Time = + Four Hours"); Serial.println("NUM\tID \tTEMP\tWIND\tVIS\tCAT \tSKY\t\tREMARKS"); for (int i = 1; i < (NUM_AIRPORTS + 1); i++) { Serial.print(i); Serial.print("\t" + stations[i]); Serial.print("\t" + temp[i]); Serial.print("\t" + wind[i]); Serial.print("\t" + visab[i]); Serial.print("\t" + category[i]); if (sky[i] == "CLR") Serial.print("\t" + sky[i] + "\t"); else Serial.print("\t" + sky[i]); Serial.println("\t" + rem[i]); } Serial.println(" "); }