/* 04/25/2020 METAR Reporting with LEDs and Local Web Server In Memory of F. Hugh Magee, brother of John Magee author of poem HIGH FLIGHT https://en.wikipedia.org/wiki/John_Gillespie_Magee_Jr. https://youtu.be/Yg61_kyG2zE HomebuiltHELP the video that started me on this project https://youtu.be/xPlN_Tk3VLQ geting started with ESP32 video from DroneBot Workshop. https://www.aviationweather.gov/docs/metar/stations.txt List of all Stations codes. REMARKS: Created by John Pipe with help from David Bird. Made things a little better and squished some bugs. No of Stations = 60 tested 5/24/19 (More possible with extenal power). When Error, successfuly recovers. If not, press "RESET" button on ESP32. The code is stored on the ESP32, so once downloaded, it will automatically restart, even after a power down. LEDS show station category and flashes for gusts(cyan), precipitation(green/white), ice(blue), other(yellow) and info(orange). Also displays for wind(blue), temperature(yellow) and visability(green/white). Station Display shows ALL detailed information and more. Bug Fixes, improvements, repairs to time-space continuum, etc, etc. Modified Significant Weather to include Cloud Cover, RVR & Weather 12/31 Changed to a TIMED 6 Minute METAR read and update 01/07 Added for ice and hail (Blue) 01/30 Added Capability to select any Airport 02/02 Added Summary to HTML 03/04 Cleaned up Get_Time & loop 03/08 Added User 03/10 Added Cloud_base Change Arrows 03/13 Added Alt Pressure Change Arrows 03/13 Added Yellow Misc Weather 03/24 Added Observation Time 04/01 Added Orange Info Changes 04/05 Cleaned up Parse Metar 04/14 Added Wind Change 04/25 */ #include #include #include #include #include WiFiMulti wifiMulti; #include WiFiServer server(80); // Set web server port number to 80 //const char* ssid = "iPhone"; // your network SSID (name) //const char* password = "********"; // your network password const char* ssid = "NETGEAR46"; // your network SSID (name) const char* password = "*********"; // your network password // Setup for Time Server const char* ntpServer = "pool.ntp.org"; const long gmtOffset_sec = 0; // UTC Time const int daylightOffset_sec = 0; // UTC Time struct tm timeinfo; // Time String "%A, %B %d %Y %H:%M:%S" // To get Station Name and Information // Test link: https://aviationweather.gov/adds/dataserver_current/httpparam?dataSource=stations&requestType=retrieve&format=xml&stationString=KFLL //String host = "https://aviationweather.gov"; String urls = "/adds/dataserver_current/httpparam?dataSource=stations&requestType=retrieve&format=xml&stationString="; // To get Station METAR Data // 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 81 // Also the number of LEDs 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 /* Define STATIONS & Global Variables First FIVE Characters are IMPORTANT !! Over type your stations and station names or Cut and Paste your sequence of stations and include the "quotes" and the "commas" Note: SKYVECTOR.COM is good for locating METARs Order of LEDs; NULL for no airport Don't forget to SET network SSID (name) and your network password above */ std::vector stations ({ // << Set Up - Do NOT delete this line "NULL, STATION NAME ", // 0 << Reserved - Do NOT delete this line "KABY, ALBANY ,GA", // 1 << START modifing from here "KACJ, AMERICUS ,GA", "KAMG, ALMA ,GA", "KAHN, ATHENS ,GA", "KATL, ATLANTA ,GA", "KFTY, ATLANTA/FULTON ,GA", "KPUJ, ATLANTA/PAULDING ,GA", "K6A2, GRIFFIN-SPALDING ,GA", "KDNL, AUGUSTA/DANIEL ,GA", "KAGS, AUGUSTA/BUSH ,GA", // 10 "KBGE, BAINBRIDGE ,GA", "KBHC, BAXLEY MUNI ,GA", "KDZJ, BLAIRSVILLE ,GA", "KBIJ, BLAKELY EARLY C ,GA", "KSSI, BRUNSWICK ,GA", "KBQK, BRUNSWICK/GLYNCO ,GA", "K6A1, BUTLER ,GA", "KCZL, CALHOUN ,GA", "KCXU, CAMILLA ,GA", "K18A, CANON ,GA", // 20 "KCNI, CANTON/CHEROKEE ,GA", "KCTJ, CARROLLTON/GRAY ,GA", "KVPC, CARTERSVILLE ,GA", "KCWV, CLAXTON/EVANS CO ,GA", "K48A, COCHRAN ,GA", "KCSG, COLUMBUS ,GA", "KCKF, CORDELE ,GA", "KAJR, CORNELIA ,GA", "KCVC, COVINGTON MUNI ,GA", "KDNN, DALTON ,GA", // 30 "KMGE, DOBBINS AFB/MARI ,GA", "K17J, DONALDSON ,GA", "KDQH, DOUGLAS MUNI ,GA", "KDBN, DUBLIN ,GA", "KEZM, EASTMAN ,GA", "KFZG, FITZGERALD ,GA", "KLSF, FT BENNING/COLUM ,GA", "KLHW, FT STEWART/WRIGH ,GA", "KGVL, GAINESVILLE ,GA", "K49A, GILMORE COUNTY ,GA", // 40 "K3J7, GREENSBORO ,GA", "K4A7, HAMPTON/CLAY CNT ,GA", "KHMP, HAMPTON/HENRY ,GA", "KAZE, HAZELHURST ,GA", "KHOE, HOMERVILLE ,GA", "KJZP, JASPER ,GA", "KJCA, JEFFERSON ,GA", "KJES, JESUP/WAYNE CTY ,GA", "K9A5, LAFAYETTE ,GA", "KLGC, LA GRANGE ,GA", "KLZU, LAWRENCEVILLE ,GA", // 50 "K2J3, LOUISVILLE ,GA", "KMCN, MACON ,GA", "KRYY, MARIETTA MCCOLUM ,GA", "KHQU, MCDUFFIE/THOMSON ,GA", "KMHP, METTER ,GA", "KMLJ, MILLEDGEVILLE ,GA", "K2J5, MILLEN ,GA", "KD73, MONROE ,GA", "KVAD, MOODY AFB/VALDOS ,GA", "KMGR, MOULTRIE MUNI ,GA", // 60 "KCCO, NEWNAN ,GA", "KPDK, PEACHTREE/DEKALB ,GA", "KFFC, PEACHTREE CITY ,GA", "KPXE, PERRY HOUSTON CN ,GA", "KPIM, PINE MOUNTAIN ,GA", "KRMG, ROME ,GA", "KOKZ, SANDERSVILLE ,GA", "KSAV, SAVANNAH ,GA", "KSVN, SAVANNAH/HUNTER ,GA", "KTBR, STATESBORO ,GA", // 70 "KSBO, SWAINSBORO ,GA", "KJYL, SYLVANIA ,GA", "KTMA, TIFTON ,GA", "KOPN, THOMASTON UPSON ,GA", "KTVI, THOMASVILLE ,GA", "KTOC, TOCCOA ,GA", "KVLD, VALDOSTA REGIONA ,GA", "KVDI, VIDALIA MUNI ,GA", "KIIY, WASHINGTON ,GA", "KAYS, WAYCROSS/WARE CO ,GA", // 80 "KWDR, WINDER/BARROW ,GA", }); // << Do NOT delete this line String updatetime[NUM_AIRPORTS + 1]; // Keeps previous time String rem[NUM_AIRPORTS + 1]; // Remarks String sig_wx[NUM_AIRPORTS + 1]; // Significant Weather String temp[NUM_AIRPORTS + 1]; // temperature deg C String dewpt[NUM_AIRPORTS + 1]; // dew point deg C String wind[NUM_AIRPORTS + 1]; // wind speed String wdir[NUM_AIRPORTS + 1]; // wind direction String old_wdir[NUM_AIRPORTS + 1]; // old wind direction String visab[NUM_AIRPORTS + 1]; // visibility String sky[NUM_AIRPORTS + 1]; // sky_cover & cloud_base int cloud_base[NUM_AIRPORTS + 1]; // cloud_base int old_cloud_base[NUM_AIRPORTS + 1]; // old cloud_base String seapres[NUM_AIRPORTS + 1]; // Sea Level Pressure String altim[NUM_AIRPORTS + 1]; // altimeter setting float old_altim[NUM_AIRPORTS + 1]; // old altimeter setting String elevation[NUM_AIRPORTS + 1]; // elevation setting String category[NUM_AIRPORTS + 1]; // NULL VFR MVFR IFR LIFR //.....................................Black Green Blue Red Magenta #define LED_BUILTIN 2 // ON Board LED GPIO 2 String metar; // Raw METAR data String Time; // Time "HH:MM" String Old_Time; // Last Update Time "HH:MM" int Hour; // Lastest Hour int Minute; // Lastest Minute int Old_Minute; // Last Update Minute int diff_in_clouds = 2750; // Significant CHANGE IN CLOUD BASE float diff_in_press = 0.045; // Significant CHANGE IN PRESSURE int station_num = 1; // Current Station for Server String header; // Header for Server int httpCode; // Error Code IPAddress ip; // WiFi local IP Address const char* FileName = "METAR_ESP32_04_25"; void setup() { pinMode(LED_BUILTIN, OUTPUT); // the onboard LED Serial.begin(115200); delay(4000); // time to press "Clear output" in Serial Monitor digitalWrite(LED_BUILTIN, HIGH); //ON Serial.print("WiFi Connecting to "); Serial.print(ssid); // Initializes the WiFi library's network settings. WiFi.begin(ssid, password); // CONNECT to Wifi network: while (WiFi.status() != WL_CONNECTED) { delay(200); Serial.print("."); } Serial.println(" Connected."); ip = WiFi.localIP(); Serial.println("*******************************************"); Serial.println("To View a decoded Station METAR \non a Computer or Cell Phone connected to the Local Network."); Serial.print("Enter this Url Address : http://"); Serial.print(ip); Serial.println("/"); WiFi.mode(WIFI_STA); wifiMulti.addAP(ssid, password); WiFi.setHostname(FileName); Serial.print("File/Host Name : "); Serial.println(FileName); configTime(gmtOffset_sec, daylightOffset_sec, ntpServer); // Get Time from Server Get_Time(); // *********** RTC Current Time, Hour & Minute Serial.println(&timeinfo, "%A, %B %d %Y %H:%M - Zulu"); server.begin(); // Start the webserver Serial.println("Webserver started..."); Serial.println("*******************************************"); digitalWrite(LED_BUILTIN, LOW); //OFF Startleds(); // *********** Initialize LEDs } // *********** Main Loop void loop() { Old_Time = Time; Old_Minute = Minute; int Trip = Old_Minute + 6; // Cycle time is 6 Minutes GetAllMetars(); // *********** GetAllMetars and Display First Loop DataPrint(); // *********** Print the Station Parameters (if set) Get_Time(); // *********** GET Current Time while (Trip > Minute) { if ((Trip - Minute) > 6 ) Trip = -1; else DisplayMetar(); // ******** Display Station Metar/Show Loops 30s } } // *********** RTC Current Time, Hour & Minute -Zulu void Get_Time() { if (!getLocalTime(&timeinfo)) { Serial.println("*********** FAILED to Obtain Time"); return; } char TimeChar[9]; strftime(TimeChar, 9, "%H:%M:%S", &timeinfo); Time = String(TimeChar); Hour = (Time.substring(0, 2).toInt()); Minute = (Time.substring(3, 5).toInt()); Time = Time.substring(0, 5); } // *********** Initialize LEDs void Startleds() { Serial.print("Initializing LEDs for NUM_AIRPORTS = "); Serial.println(NUM_AIRPORTS); // Set up the strip configuration FastLED.addLeds(leds, NUM_AIRPORTS).setCorrection(TypicalLEDStrip); // Set master brightness control FastLED.setBrightness(BRIGHTNESS); // Set all leds to Black fill_solid(leds, NUM_AIRPORTS, CRGB::Black); FastLED.show(); delay(200); // Wait a little bit } // *********** GET All Metars in Chunks void GetAllMetars() { int Step = 20; // 20 Stations at a time for (int j = 0; j < NUM_AIRPORTS; j = j + 1 + Step) { int start = j; int finish = start + Step; if (finish > NUM_AIRPORTS) finish = NUM_AIRPORTS; String url = ""; for (int i = start; i <= finish; i++) { String station = stations[i].substring(0, 5); url = url + String (station); } int len = url.length(); url = url.substring(0, len - 1); // remove last "comma" GetData(url); // *********** GET Some Metar Data 20 Stations at a time for (int i = start; i <= finish; i++) { ParseMetar(i); // *********** Parse Metar Data one Station at a time } } } // *********** GET Some Metar Data/Name void GetData(String url) { metar = ""; // Reset Metar Data if (url == "NAME") url = host + urls + stations[0]; else url = host + urlb + url; //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 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 { http.end(); digitalWrite(LED_BUILTIN, LOW); //OFF if (httpCode < 0) Serial.print(Time + " Communication Error - "); Serial.printf("[HTTP] GET... failed, error: %s\n", http.errorToString(httpCode).c_str()); } } else { Serial.print("WiFi Connection : "); // WiFi Connection Failed 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"); } } // *********** Parse Metar Data void ParseMetar(int i) { String parsedmetar = ""; String station = stations[i].substring(0, 4); if (station == "NULL") return; int data_start = metar.indexOf(station); // Search for station id int data_end = metar.indexOf("", data_start + 1); // Search for "data end" if (data_start > 0 && data_end > 0) { parsedmetar = metar.substring(data_start, data_end); // Parse Metar Data // Remove found data from metar metar = metar.substring(0, data_start) + metar.substring(data_end, metar.length()); Decodedata(i, station, parsedmetar); // *********** DECODE the Station DATA Routine } else { category[i] = "NF"; // Not Found sky[i] = "NA"; // Not Found rem[i] = "Station Not Found"; // Not Found sig_wx[i] = "None"; // Not Found visab[i] = "NA"; // Not Found wdir[i] = "NA"; // Not Found wind[i] = "NA"; // Not Found temp[i] = "NA"; // Not Found altim[i] = "NA"; // Not Found old_altim[i] = 0; // Not Found updatetime[i] = "NF"; // Not Found if (i != 0) UpdateLED (i, 30); // *********** Update One Station LED Serial.print(Time + " Station " + station + " No:"); Serial.print(i); Serial.println(" - Data not found, skipping this one - in ParseMetar"); } } // *********** DECODE the Station DATA Routine void Decodedata(int i, String station, String parsedmetar) { int pflag = 0; // Print Flag 0=NO Print 1=Print ALL 2= ALL + RAW DATA // Reading Remarks int search0 = parsedmetar.indexOf(station) + 7; // Start of Remark int search1 = parsedmetar.indexOf(" 0) search2 = parsedmetar.indexOf(" Q") + 6; // nonUS Airport if (search2 < 6) search2 = search1; rem[i] = parsedmetar.substring(search0, search2); int obsh = rem[i].substring(0, 2).toInt(); // Observation time int obsm = rem[i].substring(2, 4).toInt(); int ago = ((Hour - obsh) * 60) + Minute - obsm; if (ago < 0) ago = ((24 - obsh) * 60) + Minute - obsm; rem[i] = rem[i] + " (" + ago + "m ago)"; // Add Observation dtime if (updatetime[i].substring(0, 4) != rem[i].substring(0, 4)) { // UPDATE Station updatetime[i] = rem[i].substring(0, 4); rem[i] = "new " + rem[i]; if (pflag != 0) { if (pflag == 2) Serial.println("\nParsed Data = " + parsedmetar + "\n"); // Search Failed Serial.print("\nStation " + stations[i] + "\tNo: "); Serial.println(i); Serial.println("\tRemarks \t = " + rem[i]); } // Reading flight_category category[i] = "NA"; search0 = parsedmetar.indexOf(" 0 && search0 < search1) { sig_wx[i] = "Gusts to " + rem[i].substring(search1 - 2, search1 + 2) + ": "; } // Variable Wind Dir or Runway Vis search0 = rem[i].indexOf("0V"); if (search0 > 0) { String Variable = rem[i].substring(search0 - 3, search0 + 5); if (Variable.substring(0, 1) == " ") sig_wx[i] = sig_wx[i] + " Wind Dir Varies:" + Variable + "; "; search0 = rem[i].indexOf("SM"); search1 = rem[i].indexOf("FT"); if (search0 > 0 && search1 > 0) { Variable = rem[i].substring(search0 + 3, search1 + 2); sig_wx[i] = sig_wx[i] + " Runway Vis:" + Variable + "; "; } } // Significant Weather in Remarks and making readable Weather if (rem[i].indexOf(" +") > 0) sig_wx[i] = sig_wx[i] + " Heavy"; if (rem[i].indexOf(" -") > 0) sig_wx[i] = sig_wx[i] + " Light"; if (rem[i].indexOf("BL") > 0) sig_wx[i] = sig_wx[i] + " Blowing"; if (rem[i].indexOf("DR") > 0) sig_wx[i] = sig_wx[i] + " Drifting"; if (rem[i].indexOf("FZ") > 0) sig_wx[i] = sig_wx[i] + " Freezing"; if (rem[i].indexOf("RA") > 0) sig_wx[i] = sig_wx[i] + " Rain "; if (rem[i].indexOf("SN") > 0) sig_wx[i] = sig_wx[i] + " Snow "; if (rem[i].indexOf("DZ") > 0) sig_wx[i] = sig_wx[i] + " Drizzle "; if (rem[i].indexOf("SH") > 0) sig_wx[i] = sig_wx[i] + " Showers "; if (rem[i].indexOf("BR") > 0) sig_wx[i] = sig_wx[i] + " Mist "; if (rem[i].indexOf("MI") > 0) sig_wx[i] = sig_wx[i] + " Shallow"; if (rem[i].indexOf("PR") > 0) sig_wx[i] = sig_wx[i] + " Partial"; if (rem[i].indexOf("BC") > 0) sig_wx[i] = sig_wx[i] + " Patches of"; if (rem[i].indexOf("FG") > 0) sig_wx[i] = sig_wx[i] + " Fog "; if (rem[i].indexOf("GS") > 0) sig_wx[i] = sig_wx[i] + " Small Hail "; if (rem[i].indexOf("GR") > 0) sig_wx[i] = sig_wx[i] + " Large Hail "; if (rem[i].indexOf("IC") > 0) sig_wx[i] = sig_wx[i] + " Ice Crystals "; if (rem[i].indexOf("PL") > 0) sig_wx[i] = sig_wx[i] + " Ice Pellets "; if (rem[i].indexOf("SG") > 0) sig_wx[i] = sig_wx[i] + " Snow Grains "; if (rem[i].indexOf("DU") > 0) sig_wx[i] = sig_wx[i] + " Dust "; if (rem[i].indexOf("FU") > 0) sig_wx[i] = sig_wx[i] + " Smoke "; if (rem[i].indexOf("HZ") > 0) sig_wx[i] = sig_wx[i] + " Haze "; if (rem[i].indexOf("FY") > 0) sig_wx[i] = sig_wx[i] + " Spray "; if (rem[i].indexOf("SA") > 0) sig_wx[i] = sig_wx[i] + " Sand "; if (rem[i].indexOf("PO") > 0) sig_wx[i] = sig_wx[i] + " Dust/Sand "; if (rem[i].indexOf("SQ") > 0) sig_wx[i] = sig_wx[i] + " Squalls "; if (rem[i].indexOf("TS") > 0) sig_wx[i] = sig_wx[i] + " Thunderstorm "; if (rem[i].indexOf("VA") > 0) sig_wx[i] = sig_wx[i] + " Volcanic Ash "; if (rem[i].indexOf(" VC") > 0) sig_wx[i] = sig_wx[i] + "in Vicinity; "; if (rem[i].indexOf("-VC") > 0) sig_wx[i] = sig_wx[i] + "in Vicinity; "; if (rem[i].indexOf("+VC") > 0) sig_wx[i] = sig_wx[i] + "in Vicinity; "; if (rem[i].indexOf("TCU") > 0) sig_wx[i] = sig_wx[i] + " Towering Cumulus Clouds "; if (rem[i].indexOf("CB ") > 0) sig_wx[i] = sig_wx[i] + " Cumulonimbus Clouds "; if (rem[i].indexOf("CBMAM") > 0) sig_wx[i] = sig_wx[i] + " CB Mammatus Clouds "; if (rem[i].indexOf("UP ") > 0) sig_wx[i] = sig_wx[i] + " Unknown Precipitation "; if (sig_wx[i] == "") sig_wx[i] = "None"; if (pflag != 0) Serial.println("\tSignificant Weather = " + sig_wx[i]); // Reading temp_c search0 = parsedmetar.indexOf(" 0) wdir[i] = "VRB"; // Reading wind_speed_kt search0 = parsedmetar.indexOf("wind_speed_kt") + 14; search1 = parsedmetar.indexOf(" 11) sky[i] = parsedmetar.substring(search1, search1 + 3); { search1 = parsedmetar.indexOf("_ft_agl=", search0) + 9; search2 = parsedmetar.indexOf(" />", search0) - 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 " + parsedmetar.substring(search1, search2) + " Ft"; if (sky[i] == "FEW") sky[i] = sky[i] + " at " + parsedmetar.substring(search1, search2) + " Ft"; if (sky[i] == "SCT") sky[i] = sky[i] + " at " + parsedmetar.substring(search1, search2) + " Ft"; if (sky[i] == "OVC") sky[i] = sky[i] + " at " + parsedmetar.substring(search1, search2) + " Ft"; cloud_base[i] = parsedmetar.substring(search1, search2).toInt(); if (sky[i] == "OBSECURED") cloud_base[i] = 0; if (sky[i] == "CLEAR") cloud_base[i] = 99999; if (sky[i] == "NA") cloud_base[i] = 0; } if (pflag != 0) Serial.println("\tSky Cover \t = " + sky[i] + "\n\tCloud Base \t = " + cloud_base[i]); // Vertical Visibility search0 = parsedmetar.indexOf("") + 13; if (search0 > 13) { sig_wx[i] = sig_wx[i] + " Vertical Visibility = " + parsedmetar.substring(search0, search0 + 3) + " Ft; "; cloud_base[i] = parsedmetar.substring(search0, search0 + 3).toInt(); } // Reading ") + 13; if (search0 > 13) altim[i] = parsedmetar.substring(search0, search0 + 8); float Pressure = altim[i].toFloat(); // in_hg if (pflag != 0) { Serial.print("\tAltimeter \t = "); Serial.print(Pressure, 2); Serial.println(" in hg"); } // Reading ") + 23; if (search0 > 23) seapres[i] = parsedmetar.substring(search0, search0 + 6); if (pflag != 0) Serial.println("\tSea Level Press\t = " + seapres[i] + " in mb"); // Reading elevation_m elevation[i] = "NA"; search0 = parsedmetar.indexOf(" 13) elevation[i] = parsedmetar.substring(search0, search1); float Elevation = elevation[i].toFloat() * 3.28; // feet if (pflag != 0) { Serial.print("\tElevation m = " + elevation[i] + " : Ft = "); Serial.println(Elevation, 1); } // Calculating Pressure Altitude float PressAlt = Elevation + (1000 * (29.92 - Pressure)); // feet // Calculating Density Altitude float DensityAlt = PressAlt + (120 * (TempC - (15 - abs(2 * Elevation / 1000)))); // feet if (pflag != 0) { Serial.print("\tEstimated Density Altitude Ft = "); Serial.println(DensityAlt, 0); } } if (i != 0) UpdateLED (i, 20); // *********** Update One Station LED } // *********** Update One Station LED void UpdateLED(int index, int wait) { int lednum = index - 1; // LED Number leds[lednum] = (0xFF8000); // Orange Decoding Data FastLED.show(); delay(wait); Set_Category_LED(index, lednum); FastLED.show(); } // *********** DECODE the Station NAME void DecodeStation(int i) { String Station_name = stations[i].substring(0, 5); String Code = stations[i].substring(0, 4); int search0 = metar.indexOf(Code) + 1; // Start Search from Here int search1 = metar.indexOf(" 0 && search2 > 0) Station_name = Station_name + " " + metar.substring(search1 + 6, search2) + ","; search1 = metar.indexOf(" 0) Station_name = Station_name + " " + metar.substring(search1 + 7, search2); } if (search1 > 0 && search2 > 0) Station_name = Station_name + " " + metar.substring(search1 + 9, search2); stations[i] = Station_name; } // *********** Display Station Metar/Show Loops void DisplayMetar() { int wait_Time = 5000; // Delay for wait_Time 5 (Seconds x1000) UpdateWeather_LEDS(wait_Time); // Display All Weather, Delay for wait_Time UpdateWind_LEDS(wait_Time); // Display All Winds [Shades of Aqua], Delay for wait_Time UpdateTemp_LEDS(wait_Time); // Display All Temperatues [Shades of Yellow] Delay for wait_Time //CalcTemp_LEDS(wait_Time); // Calculate Temperatures [Blue Green Red] Delay for wait_Time UpdateVis_LEDS(wait_Time); // Display All Visabilities [Shades of Light Green], Delay for wait_Time //UpdateAlt_LEDS(wait_Time); // Display All Stations for Alitmeter Pressure [Blue], Delay for wait_Time Get_Time(); // *********** GET Current Time } // *********** All Stations for Weather void UpdateWeather_LEDS(int wait) { UpdateCategory_LEDS(); // Display All Categories for (int i = 1; i < (NUM_AIRPORTS + 1); i++) { if (sig_wx[i].substring(0, 4) != "None") { // IF NOT "None" in Sig Wx, Display Weather int n = i - 1; // LED Number // Call Twinkle for Weather (index, red,green,blue, pulses, on, off times, led no) if (sig_wx[i].indexOf("KT") > 0) Twinkle(i, 0x00, 0xff, 0xff, 2, 500, 500, n); //Gusts Aqua if (rem[i].indexOf("FZ") > 0) Twinkle(i, 0x00, 0x00, 0x70, 2, 300, 400, n); //Freezing Blue if (rem[i].indexOf("FG") > 0) Twinkle(i, 0x30, 0x40, 0x26, 2, 400, 500, n); //Fog L Yellow if (rem[i].indexOf("BR") > 0) Twinkle(i, 0x22, 0x70, 0x22, 2, 500, 500, n); //Mist L Green if (rem[i].indexOf("DZ") > 0) Twinkle(i, 0x20, 0x50, 0x00, 2, 300, 300, n); //Drizzle L Green if (rem[i].indexOf("RA") > 0) Twinkle(i, 0x00, 0xff, 0x00, 4, 300, 300, n); //Rain Green if (rem[i].indexOf("HZ") > 0) Twinkle(i, 0x20, 0x20, 0x20, 2, 400, 400, n); //Haze White/purple if (rem[i].indexOf("SN") > 0) Twinkle(i, 0xaf, 0xaf, 0xaf, 4, 300, 600, n); //Snow White if (rem[i].indexOf("SG") > 0) Twinkle(i, 0xaf, 0xaf, 0xaf, 4, 300, 600, n); //Snow White if (rem[i].indexOf("TS") > 0) Twinkle(i, 0xff, 0xff, 0xff, 4, 10, 900, n); //Thunder White if (rem[i].indexOf("GS") > 0) Twinkle(i, 0x88, 0x88, 0x00, 3, 100, 800, n); //S Hail Yellow if (rem[i].indexOf("GR") > 0) Twinkle(i, 0x88, 0x88, 0x00, 4, 100, 800, n); //L Hail Yellow if (rem[i].indexOf("IC") > 0) Twinkle(i, 0x00, 0x00, 0x40, 3, 300, 400, n); //Ice C Blue if (rem[i].indexOf("PL") > 0) Twinkle(i, 0x00, 0x00, 0x50, 3, 300, 400, n); //Ice P Blue if (rem[i].indexOf("VCSH") > 0) Twinkle(i, 0x00, 0xff, 0x00, 2, 300, 300, n); //Showers Green if (rem[i].indexOf("TCU") > 0) Twinkle(i, 0x40, 0x40, 0x40, 2, 300, 300, n); //CU Grey if (rem[i].indexOf("CB") > 0) Twinkle(i, 0x40, 0x40, 0x40, 2, 300, 300, n); //CB Grey // Rest of Weather = YELLOW if (rem[i].indexOf("DU") > 0) Twinkle(i, 0x88, 0x88, 0x00, 3, 500, 500, n); //Dust Yellow if (rem[i].indexOf("FU") > 0) Twinkle(i, 0x88, 0x88, 0x00, 3, 500, 500, n); //Smoke Yellow if (rem[i].indexOf("FY") > 0) Twinkle(i, 0x88, 0x88, 0x00, 3, 500, 500, n); //Spray Yellow if (rem[i].indexOf("SA") > 0) Twinkle(i, 0x88, 0x88, 0x00, 3, 500, 500, n); //Sand Yellow if (rem[i].indexOf("PO") > 0) Twinkle(i, 0x88, 0x88, 0x00, 3, 500, 500, n); //Dust&Sand Yellow if (rem[i].indexOf("SQ") > 0) Twinkle(i, 0x88, 0x88, 0x00, 3, 500, 500, n); //Squalls Yellow if (rem[i].indexOf("VA") > 0) Twinkle(i, 0x88, 0x88, 0x00, 3, 500, 500, n); //Volcanic Ash Yellow if (rem[i].indexOf("UP") > 0) Twinkle(i, 0x88, 0x88, 0x00, 3, 500, 500, n); //Unknown Yellow // Notification of changes = ORANGE if (sig_wx[i].indexOf("Info") >= 0) Twinkle(i, 0xaf, 0x58, 0x00, 2, 200, 800, n); //Flash Orange } FastLED.show(); delay(10); } delay(wait); } // *********** Display All Stations for Winds [Aqua] void UpdateWind_LEDS(int wait) { for (int index = 1; index < (NUM_AIRPORTS + 1); index++) { int lednum = index - 1; // LED Number int Wind = wind[index].toInt(); if (category[index] == "NF" || wind[index] == "NA") { leds[lednum] = CRGB(0, 0, 0); } else { leds[lednum] = CRGB(0, Wind * 6, Wind * 6); } } FastLED.show(); Handle_Server (1); //********* Handle_Server HTML Routine *********** delay(wait); } // *********** Display All Stations for Temperatures [Red/Greem=Orange or Blue] void UpdateTemp_LEDS(int wait) { for (int index = 1; index < (NUM_AIRPORTS + 1); index++) { int lednum = index - 1; // LED Number float Temp = temp[index].toFloat(); // deg C if (category[index] == "NF" || temp[index] == "NA") { leds[lednum] = CRGB(0, 0, 0); } else { leds[lednum] = CRGB(Temp * 4 + 6 , Temp * 4 + 10 , 0); if (Temp > 36) leds[lednum] = CRGB(Temp * 6 + 12 , Temp * 4, 0); // Hot-Red if (Temp < 0) leds[lednum] = CRGB(0 , 0, Temp * -30); // Cold-Blue } } FastLED.show(); Handle_Server (1); //********* Handle_Server HTML Routine *********** delay(wait); } // *********** Calculate Temperatures [Blue Green Red] void CalcTemp_LEDS(int wait) { int maxtemp = 40; for (int index = 1; index < (NUM_AIRPORTS + 1); index++) { int lednum = index - 1; // LED Number float TempC = temp[index].toFloat() / 10; // deg C double scale_blue = sin(TempC * 1.3 + 1.8); double scale_green = sin(TempC * 0.8); double scale_yellow = sin(TempC * 1.3); double scale_red = sin(TempC * 0.8 + 4.6); int Temp = temp[index].toInt(); int b = (maxtemp - Temp) * scale_blue * 2; if (b < 0) b = 0; int g = Temp * scale_yellow; if (g < 0) g = 0; int r = Temp * scale_red + scale_yellow; if (r < 0) r = 0; if (category[index] == "NF" || temp[index] == "NA") leds[lednum] = CRGB(0, 0, 0); else leds[lednum] = CRGB(r, g, b); } FastLED.show(); Handle_Server (1); //********* Handle_Server HTML Routine *********** delay(wait); } // *********** Display All Stations for Visability [White] void UpdateVis_LEDS(int wait) { for (int index = 1; index < (NUM_AIRPORTS + 1); index++) { int lednum = index - 1; // LED Number int Vis = visab[index].toInt(); Vis = Vis * 3; if (category[index] == "NF" || visab[index] == "NA") { leds[lednum] = CRGB(0, 0, 0); } else { leds[lednum] = CRGB(Vis + 6, Vis * 2 + 6, Vis + 6); } } FastLED.show(); Handle_Server (1); //********* Handle_Server HTML Routine *********** delay(wait); } // *********** Display All Stations for Alitmeter Pressure [Red/Blue=Magenta] void UpdateAlt_LEDS(int wait) { for (int index = 1; index < (NUM_AIRPORTS + 1); index++) { int lednum = index - 1; // LED Number float Pressure = altim[index].toFloat(); float Mult = 200.0; // Span=Mult Pressure = Pressure - 29.92; // Mean Pressure int Alt = 128 + (Pressure * + Mult); // Mean color change=128 int Alt1 = 128 + (Pressure * - Mult); if (category[index] == "NF" || visab[index] == "NA") { leds[lednum] = CRGB(0, 0, 0); } else { leds[lednum] = CRGB( Alt1, 0, Alt); } } FastLED.show(); Handle_Server (1); //********* Handle_Server HTML Routine *********** delay(wait); } // *********** Display all Categories void UpdateCategory_LEDS() { for (int index = 1; index < (NUM_AIRPORTS + 1); index++) { int lednum = index - 1; // LED Number Set_Category_LED(index, lednum); } FastLED.show(); delay(500); } // *********** Weather for Wind Gusts and Precipitation Only // Call for Weather (index,red,green,blue, pulses, on, off times, led no) void Twinkle(int index, byte red, byte green, byte blue, int Count, int on_Time, int off_Time, int lednum) { leds[lednum].r = 0x00; // Red Off leds[lednum].g = 0x00; // Green Off leds[lednum].b = 0x00; // Blue Off FastLED.show(); delay(100); for (int i = 0; i < Count; i++) { leds[lednum].r = red; // Red On leds[lednum].g = green; // Green On leds[lednum].b = blue; // Blue On FastLED.show(); delay(on_Time); leds[lednum].r = 0x00; // Red Off leds[lednum].g = 0x00; // Green Off leds[lednum].b = 0x00; // Blue Off FastLED.show(); delay(off_Time); } delay(100); Set_Category_LED(index, lednum); } // *********** Set Category for one Station LED void Set_Category_LED (int index, int lednum) { if (category[index] == "VFR" ) leds[lednum] = CRGB::Green; if (category[index] == "MVFR") leds[lednum] = CRGB::Blue; if (category[index] == "IFR" ) leds[lednum] = CRGB::Red; if (category[index] == "LIFR") leds[lednum] = CRGB::Magenta; if (category[index] == "NA") leds[lednum] = (0x102000); // Not Available : Yellowish if (category[index] == "NF") leds[lednum] = (0x102000); // Not Found : Yellowish Handle_Server (index); //********* Handle_Server HTML Routine *********** } // *********** Print the Station Parameters void DataPrint() { int pflag = 0; // Print Flag: 0=NO Print: 1=Print ALL: 2=Only UPDATES: if (pflag != 0) { if (pflag == 1) { Serial.println("\nSummary of Weather Conditions; Time = " + Time + " - Zulu"); Serial.println("No\tID \tCAT \tSKY COVER\tVIS\tDIR\tWIND\tTEMP\tREMARKS"); } for (int i = 1; i < (NUM_AIRPORTS + 1); i++) { if (rem[i].substring(0, 3) == "new" || pflag == 1) { Serial.print(i); Serial.print("\t" + stations[i].substring(0, 4)); Serial.print("\t" + category[i]); Serial.print("\t" + sky[i]); if (sky[i].length() < 11) Serial.print("\t"); Serial.print("\t" + visab[i]); Serial.print("\t" + wdir[i]); Serial.print("\t" + wind[i]); Serial.print("\t" + temp[i]); Serial.println("\t" + rem[i].substring(0, rem[i].length() - 13)); } } } } // *********** Handle_Server HTML *********** void Handle_Server (int station_n) { // From Set_Category station_n=index, else =1 WiFiClient client = server.available(); // Listen for incoMinuteg clients if (client) { // If a new client connects, String currentLine = ""; // make a String to hold incoMinuteg data from the client while (client.connected()) { // loop while the client's connected if (client.available()) { // if there's bytes to read from the client, char c = client.read(); // read a byte, then //Serial.write(c); // print it out the serial monitor header += c; if (c == '\n') { // if the byte is a newline character // if the current line is blank, you got two newline characters in a row. // that's the end of the client HTTP request, so send a response: if (currentLine.length() == 0) { // HTTP headers always start with a response code (e.g. HTTP/1.1 200 OK) // and a content-type so the client knows what's coming, then a blank line: client.println("HTTP/1.1 200 OK"); client.println("Content-type:text/html"); client.println("Connection: close"); client.println(); // Serial.println(header); // VARIABLES int Cloud_flag; int wx_flag; int station_flag = 1; int summary_flag = 0; String User; String Browser; // Checking for favicon with CHROME int search = header.indexOf("GET /favicon.ico"); if (search >= 0) { // Do nothing station_flag = 0; summary_flag = 0; } else { search = header.indexOf("User-Agent: Mozilla/5.0 (iPad;"); if (search >= 0) { User = "iPad User"; Browser = "Safari"; } else { search = header.indexOf("User-Agent: Mozilla/5.0 (iPhone"); if (search >= 0) { User = "iPhone User"; Browser = "Safari"; } else { search = header.indexOf("User-Agent: Mozilla/5.0 (X11; Linux armv7l)"); if (search >= 0) { User = "Linux User"; Browser = "Chromiun"; } else { search = header.indexOf("User-Agent: Mozilla/5.0 (Windows NT 10.0"); if (search >= 0) User = "Windows 10 User"; search = header.indexOf("User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0;"); if (search >= 0) Browser = "Microsoft Explorer"; search = header.indexOf("Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"); if (search >= 0) Browser = "Microsoft Edge"; search = header.indexOf("Accept-Language: en-US,en;q=0.9"); if (search >= 0) Browser = "Google Chrome"; } } } } // Checking if AIRPORT CODE was Entered search = header.indexOf("GET /get?Airport_Code="); if (search >= 0) { String Airport_Code = header.substring(search + 22, search + 26); Airport_Code.toUpperCase(); // changes all letters to UPPER CASE station_num = 0; for (int i = 1; i < (NUM_AIRPORTS + 1); i++) { // check if Airport_Code is in data base if (Airport_Code == stations[i].substring(0, 4)) station_num = i; } if (station_num == 0) { GetData(Airport_Code); // *********** GET Some Metar /DATA Airport_Code = Airport_Code + ","; stations[0] = Airport_Code.c_str(); updatetime[0] = ""; ParseMetar(0); // *********** Parse Metar DATA old_cloud_base[0] = 0; old_altim[0] = 0; old_wdir[0] = ""; GetData("NAME"); // *********** GET Some Metar /NAME DecodeStation(0); // *********** Decode Station NAME } station_flag = 1; } // Checking which BUTTON was Pressed search = header.indexOf("GET /back HTTP/1.1"); if (search >= 0) { station_num = station_num - 1; if (station_num < 0) station_num = NUM_AIRPORTS; if (station_num == 0 && stations[0].substring(0, 4) == "NULL") station_num = NUM_AIRPORTS; station_flag = 1; } search = header.indexOf("GET /next HTTP/1.1"); if (search >= 0) { station_num = station_num + 1; if (station_num > NUM_AIRPORTS) station_num = 0; if (station_num == 0 && stations[0].substring(0, 4) == "NULL") station_num = 1; station_flag = 1; } search = header.indexOf("GET /flash HTTP/1.1"); if (search >= 0) { station_num = station_n; station_flag = 1; } search = header.indexOf("GET /summary HTTP/1.1"); if (search >= 0) { summary_flag = 1 ; station_flag = 0; } if (summary_flag == 1) { // *********** DISPLAY SUMMARY *********** client.print(""); // Display the HTML web page // Responsive in any web browser, Header client.print(""); client.print("METAR"); // TITLE client.print(""); // Closes Style & Header // Web Page Body client.print("

METAR Summary

"); client.print("Summary of Weather Conditions - Last Update :" + Old_Time + " - Zulu    Next Update in Six Minutes
"); // Display Table SUMMARY *********** client.print(""); client.print(""); for (int i = 0; i < (NUM_AIRPORTS + 1); i++) { String color = ""); client.print(color + stations[i].substring(0, 4) + ""); client.print(color + category[i] + ""); if (sky[i].length() < 11) { client.print(color + sky[i]); } else { Cloud_flag = 0; if (old_cloud_base[i] > 0) { if (cloud_base[i] >= old_cloud_base[i] + diff_in_clouds) Cloud_flag = 1; // Significant INCREASE in Cloud Base if (cloud_base[i] <= old_cloud_base[i] - diff_in_clouds) Cloud_flag = 1; // Significant DECREASE in Cloud Base if (Cloud_flag == 1 && sig_wx[i].substring(0, 4) == "None") sig_wx[i] = "Info:"; } if (Cloud_flag == 1) client.print(""); client.print(color + visab[i] + ""); client.print(color + wdir[i] + ""); client.print(color + wind[i] + ""); client.print(color + temp[i] + ""); float Pressure = altim[i].toFloat(); //in_Hg wx_flag = 0; if (old_altim[i] > 0) { if (Pressure >= old_altim[i] + diff_in_press) wx_flag = 1; // Significant INCREASE in Pressure if (Pressure <= old_altim[i] - diff_in_press) wx_flag = 1; // Significant DECREASE in Pressure if (wx_flag == 1) client.print(""); if (rem[i].substring(0, 3) == "new") client.print(""); } } client.print("
No:IDCATSKY
COVER
VIS
Miles
DIR
from
WINDTEMP
Deg C
ALT
in Hg
REMARKS
"; if (category[i] == "MVFR") color = color + "'Blue'>"; if (category[i] == "IFR" ) color = color + "'Red'>"; if (category[i] == "LIFR") color = color + "'Magenta'>"; if (category[i] == "ERR") color = color + "'Black'>"; if (category[i] == "NA") color = color + "'Black'>"; if (category[i] == "NF") color = color + "'Orange'>"; if (stations[i].substring(0, 4) != "NULL") { // Display Station if (i == station_num ) client.print("
"); else client.print("
"); client.print(i); client.print(""); else client.print(color); client.print(sky[i].substring(0, 3) + " at
" + cloud_base[i] + " Ft "); if (old_cloud_base[i] > 0) { if (cloud_base[i] > old_cloud_base[i]) client.print("⇑"); //up arrow if (cloud_base[i] < old_cloud_base[i]) client.print("⇓"); //down arrow if (cloud_base[i] == old_cloud_base[i]) client.print("⇒"); //right arrow } } client.print("
"); else client.print(color); client.print(Pressure, 2); if (Pressure > old_altim[i]) client.print("
⇑"); //up arrow if (Pressure < old_altim[i]) client.print("
⇓"); //down arrow if (Pressure == old_altim[i]) client.print("
⇒"); //right arrow } else { client.print(color); client.print(Pressure, 2); } client.print("
"); else client.print(color); client.print(rem[i]); client.print("
"); } if (station_flag == 1) { // *********** DISPLAY STATION *********** client.print(""); // Display the HTML web page // Responsive in any web browser. client.print(""); client.print("METAR"); // TITLE client.print(""); // Closes Style & Header // Web Page Body client.print("

METAR Station

"); } if (station_flag == 1 || summary_flag == 1) { client.print("

"); client.print("For : " + stations[station_num] + "  #  "); client.print(station_num); client.print("
"); // Display BUTTONS: the ESP32 receives a request in the header ("GET /back HTTP/1.1") client.print(""); client.print(""); client.print(""); client.print("    "); client.print("
Press BUTTON, when LED is Flashing"); // Display TABLE/FORM: the ESP32 receives a request in the header ("GET /get?Airport_Code=") client.print(""); client.print("
ENTER AIRPORT CODE:"); client.print("
"); client.print(""); client.print("
"); client.print("Current Zulu Time = " + Time + " - Zulu    Next Update in "); if (Old_Minute + 6 > 60) client.print(60 - Minute); else client.print(Old_Minute + 6 - Minute); client.print(" Minutes
"); // Display Table DISPLAY STATION *********** String Bcol = "BORDERCOLOR="; if (category[station_num] == "VFR" ) Bcol = Bcol + "'Green'"; if (category[station_num] == "MVFR") Bcol = Bcol + "'Blue'"; if (category[station_num] == "IFR" ) Bcol = Bcol + "'Red'"; if (category[station_num] == "LIFR") Bcol = Bcol + "'Magenta'"; if (category[station_num] == "ERR") Bcol = Bcol + "'Black'"; if (category[station_num] == "NA") Bcol = Bcol + "'Black'"; if (category[station_num] == "NF") Bcol = Bcol + "'Orange'"; String color = ""; client.print(""); client.print("" + color + "" + category[station_num] + " for " + stations[station_num] + ""); client.print(""); if (rem[station_num].substring(0, 3) == "new" ) client.print(""); client.print("" + color + sig_wx[station_num]) + ""; float Pressure = altim[station_num].toFloat(); //in_Hg // Comments for Weather and Cloud Base if (sky[station_num].substring(0, 3) == "OVC") client.print("
Overcast Cloud Layer"); if (cloud_base[station_num] > 0 && cloud_base[station_num] <= 1200) client.print("
Low Cloud Base"); if (old_cloud_base[station_num] > 0) { if (cloud_base[station_num] >= old_cloud_base[station_num] + diff_in_clouds) // INCREASE client.print("
Significant Increase in Cloud Base"); if (cloud_base[station_num] <= old_cloud_base[station_num] - diff_in_clouds) // DECREASE client.print("
Significant Decrease in Cloud Base"); if (cloud_base[station_num] > old_cloud_base[station_num] && Pressure > old_altim[station_num]) client.print("
Weather is Getting Better"); if (cloud_base[station_num] < old_cloud_base[station_num] && Pressure < old_altim[station_num]) client.print("
Weather is Getting Worse"); } client.print("" + color); if (sky[station_num].length() > 11) { client.print(sky[station_num].substring(0, 3) + " Clouds " + sky[station_num].substring(4, sky[station_num].length()) + "   "); if (old_cloud_base[station_num] > 0) { if (cloud_base[station_num] > old_cloud_base[station_num]) { client.print("⇑ from "); //up arrow client.print(old_cloud_base[station_num]); } if (cloud_base[station_num] < old_cloud_base[station_num]) { client.print("⇓ from "); //down arrow if (old_cloud_base[station_num] > 99990) client.print("CLEAR"); else client.print(old_cloud_base[station_num]); } if (cloud_base[station_num] == old_cloud_base[station_num]) { client.print("⇒ No change"); //right arrow } } } else client.print(sky[station_num]); client.print(""); client.print("" + color + visab[station_num] + " Statute miles"); client.print(""); float TempC = temp[station_num].toFloat(); // deg C float TempF = temp[station_num].toFloat() * 1.8 + 32; // deg F client.print(""); client.print(""); float Elevation = elevation[station_num].toFloat() * 3.28; //feet client.print(""); float PressAlt = Elevation + (1000 * (29.92 - Pressure)); //feet float DensityAlt = PressAlt + (120 * (TempC - (15 - abs(2 * Elevation / 1000)))); // feet client.print("
Flight Category
Remarks" + rem[station_num] + ""); else client.print("" + rem[station_num]); client.print("
Significant Weather
Sky Cover
Visibility
Wind from"); if (wind[station_num] == "CALM") client.print(wind[station_num]); else client.print(wdir[station_num] + " Deg at " + wind[station_num]); int newdir = wdir[station_num].toInt(); int olddir = old_wdir[station_num].toInt(); if (newdir != 0 && olddir != 0) if (newdir != 360 && olddir != 360) if (newdir > olddir + 30 || newdir < olddir - 30) { client.print(" : Significant Change from " + old_wdir[station_num] + " Deg"); } else if (newdir != olddir) client.print(" previously " + old_wdir[station_num] + " Deg"); client.print("
Temperature"); if (TempC <= 0) client.print(""); else client.print(""); client.print(temp[station_num] + " Deg C   :   "); client.print(TempF, 1); client.print(" Deg F"); if (TempF > 100.0) client.print(" and Too HOT"); client.print("
Dewpoint" + dewpt[station_num] + " Deg C
Altimeter"); client.print(Pressure, 2); client.print(" in Hg  "); if (old_altim[station_num] > 0) { if (Pressure > old_altim[station_num]) { if (Pressure > old_altim[station_num] + diff_in_press) client.print("Significant "); client.print("⇑ from "); //up arrow client.print(old_altim[station_num], 2); } if (Pressure < old_altim[station_num]) { if (Pressure < old_altim[station_num] - diff_in_press) client.print("Significant "); client.print("⇓ from "); //down arrow client.print(old_altim[station_num], 2); } if (Pressure == old_altim[station_num]) client.print("⇒ Steady"); //right arrow } client.print("
Elevation" + elevation[station_num] + " m   :   "); client.print(Elevation, 1); client.print(" Ft
Estimated Density Altitude"); if (TempC == 0) client.print("NA"); else client.print(DensityAlt, 1); // feet client.print(" Ft

"); client.print("Welcome " + User + " with " + Browser + "
"); if (Browser.substring(0, 9) == "Microsoft") client.print("Works best with Google Chrome

"); else client.print("Works best with Microsoft Explorer

"); client.print("File/Host Name: " + String(FileName) + "
"); client.print("Url Address : http://"); client.print(ip); client.print("/"); client.print("
Dedicated to: F. Hugh Magee"); client.print("
"); } client.println(); // The HTTP response ends with another blank line break; // Break out of the while loop } else { // if you got a newline, then clear currentLine currentLine = ""; } } else if (c != '\r') { // if you got anything else but a carriage return character, currentLine += c; // add it to the end of the currentLine } } } header = ""; // Clear the header variable client.stop(); // Close the connection } }