Hardware components | ||||||
| × | 1 | ||||
| × | 1 | ||||
Software apps and online services | ||||||
|
Accurate time and weather data are useful for everybody. You can buy a weather station for 25-30 Euros, but only some of them have the time synchronized to an Atomic Clock server. Not to speak about the weather: at best, some projects have just an outdoor sensor for temperature and humidity. From these and maybe an atmospheric pressure sensor, an estimate of the forecast weather? Why not get the accurate forecast, and update this every hour! You only have to read, by your home internet provider, but using you own made device, programmed under Arduino IDE (Integrated Development Environment).
This is not the first such project available on internet. We got inspired by:
We use an ESP32, which is far less documented.
WARNING: The JSON decoding library https://github.com/bblanchon/ArduinoJson was updated and so my code needs to be updated too, soon! The below given code is not compatible with the newest library.The Purpose
It uses only reliable time and weather data. Sensors around the house are never in standard conditions (shadow, height to the ground, accuracy, etc.) and the values can be misleading. The weather forecast from internet is professional and official. Time is adjusted every hour, thus can be expected a maximum error of 1s (not per month, not per year, but perpetual).
There is a scrolling text with the weather forecast. A touch slider if for Nextion's back-light. Next button changes the next window:
NEXTION code
ActionScriptNo preview (download only).
/* Upload this project to GeekWorm ESP32-C1 using :
* -selected board: >>SparkFun ESP32 Thing<< or >> Wemos WiFi& Bluetooth battery<<
* - set it to 40MHz (enough for this project)
* -upload speed: 115200 : IMPORTANT!
* -select available COM port
* Connect USB direct to computer (200 mA required +50 mA for LCD) or external power (5V-350mA min/500 mA better)
* If not enough current available: Brownout error!
* Pins: Geekworm ESP32 <--> Nextion
* IO16 - TX
* IO17 - RX
* GND - GND
* 3.3V - +5v (Nextion works also with 3.3V)
* Use Library: "nextion" with a modified file Nextion.h:
* #define nextion Serial2 // This line is for ESP32 with hardware Serial2
* //#define USE_SOFTWARE_SERIAL //Comment this line if you use HardwareSerial
* The Nextion file is genrated using Nextion Editor: digital_clock-weather.HMI
* Once compiled, the file digital_clock-weather.tft can be transfered from File/Open Build folder to the transfer SD card.
* With the SD card inserted, Nextion updates the screen. Remove the SD card and connect the pins to ESP32.
* Project size: 512910/1310720 bytes (321152 compressed) with variables 47956/294912 bytes
* Receives and displays the weather forecast from the Weather Underground and then displays data using a
* JSON decoder wx data to a NEXTION display.
* Weather data received via WiFi connection from Weather Underground Servers and using their 'Forecast' API and data
* is decoded using Copyright Benoit Blanchon's (c) 2014-2017 excellent JSON library.
*
* This MIT License (MIT) is copyright (c) 2017 by David Bird and permission is hereby granted, free of charge, to
* any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software
* without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, but not to sub-license and/or
* to sell copies of the Software or to permit persons to whom the Software is furnished to do so, subject to the following conditions:
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
* See more at http://dsbird.org.uk
*/
HardwareSerial Serial2(2); // Activate Serial communication 2 on ESP32 (RX=IO16 and TX=IO17)
#include <TimeLib.h> // Arduino Time library for 32 bit
#include <WiFi.h>
#include <WiFiClient.h>
#include <WiFiUdp.h>
#include <Nextion.h> // Was modified to accept Serial1 hardware on ESP32
#include <ArduinoJson.h>
Nextion myNextion(nextion, 9600); //create a Nextion object named myNextion using the nextion serial port
char ssid[] = "aaaaaaaaaaaaaa"; // your network SSID (name) DEFINED in callserver();
char pass[] = "bbbbbbbbbbbbbb"; // your network password
const int ledPin =0, timeZone = 2; // East European Time (winter time)
char daysOfTheWeek[7][12] = {"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"};
String RomanMonths[12] = {"I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX", "X", "XI", "XII"}; // January is month 0
String month_of_year[12] = {"Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"}; // January is month 0
int days,DST=0;
unsigned long t0t=0, tsec=3600, t0w=0, wsec=3600; // Update weather after wsec [s] (30 min...2h is ok) and time after tsec (2..4h) {unsigned long < 4.29e9}
//------ WEATHER NETWORK VARIABLES---------
// Use your own API key by signing up for a free developer account at http://www.wunderground.com/weather/api/
//Selected Plan: Stratus Developer: Calls Per Day= 500; Calls Per Minute : 10
String API_key = "xxxxxxxxxxxxxxxxx"; // See: http://www.wunderground.com/weather/api/d/docs (change here with your KEY)
String City = "yyyyyyyyyyyyy"; // Your home city : Weather Station ID: IBUCURET77
String pws = "zzzzzzzzzzzz"; // Dinicu Golescu 35 https://www.wunderground.com/personal-weather-station/dashboard?ID=IMUNICIP22&cm_ven=localwx_pwsdash
String Country = "CC"; // Your country
String Conditions = "conditions"; // See: http://www.wunderground.com/weather/api/d/docs?d=data/index&MR=1
char wxserver[] = "api.wunderground.com"; // Address for WeatherUnderGround
// unsigned long lastConnectionTime = 0; // Last time you connected to the server, in milliseconds
String SITE_WIDTH = "900";
String icon_set = "k"; //
String Units = "M"; // Default use either M for Metric, X for Mixed and I for Imperial
char* conds[]={"\"temp_c\":"};
// These are all (?) weather short definitions:
String rain="Rain, Hail, Rain Showers, Ice Pellet Showers, Hail Showers, Small Hail Showers, Freezing Rain, Small Hail, rain";
String thunde="Thunderstorm, Thunderstorms and Rain, Thunderstorms and Snow, Thunderstorms and Ice Pellets, Thunderstorms with Hail, Thunderstorms with Small Hail, chancetstorms, tstorms";
String sunny="Clear, Unknown Precipitation, Unknown, sunny, unknown, clear";
String cloudy="Overcast, cloudy";
String chanceflurries="chanceflurries, flurries";
String chancerain="Drizzle, Rain Mist, chancerain, chancesleet, sleet";
String chancesnow="Snow Grains, Ice Crystals, Ice Pellets, Low Drifting Snow, Snow Blowing Snow Mist, Freezing Drizzle, chancesnow";
String fog="Mist, Fog, FogPatches, Smoke, VolcanicAsh, WidespreadDust, Sand, Haze, Spray, DustWhirls, Sandstorm, LowDrifting WidespreadDust, LowDriftingSand, BlowingWidespreadDust, BlowingSand, FreezingFog, PatchesofFog, ShallowFog, fog, hazy";
String mostlycloudy="MostlyCloudy, Squalls, FunnelCloud, mostlycloudy, partlysunny";
String partlycloudy="PartlyCloudy, ScatteredClouds, mostlysunny, partlycloudy";
String snow="Snow, Blowing Snow, Snow Showers, snow";
float temp_c=0;
int temp_e, nr=0;
String wind_e, h_up, m_up, h_set, m_set; // Data strings
boolean timeok=false, foreok=false, condok=false, zzz=false; // Update flags
//-------------------------------------------------------------------------------------------
//################ PROGRAM VARIABLES and OBJECTS ################
// Conditions
String webpage, city, country, date_time, observationtime,
DWDay0, DMon0, DDateDa0, DDateMo0, DDateYr0, Dicon0, Dicon_url0, DHtemp0, DLtemp0, DHumi0, Dpop0, DRain0, DW_mph0, DW_dir0, DW_dir_deg0, DWeather0, Dconditions0, DcurrentTemp, Txw1,
DWDay1, DMon1, DDateDa1, DDateMo1, DDateYr1, Dicon1, Dicon_url1, DHtemp1, DLtemp1, DHumi1, DPop1, DRain1, DW_mph1, DW_dir1, DW_dir_deg1, DWeather1, Dconditions1,
DWDay2, DMon2, DDateDa2, DDateMo2, DDateYr2, Dicon2, Dicon_url2, DHtemp2, DLtemp2, DHumi2, DPop2, DRain2, DW_mph2, DW_dir2, DW_dir_deg2, DWeather2, Dconditions2,
DWDay3, DMon3, DDateDa3, DDateMo3, DDateYr3, Dicon3, Dicon_url3, DHtemp3, DLtemp3, DHumi3, DPop3, DRain3, DW_mph3, DW_dir3, DW_dir_deg3, DWeather3, Dconditions3,
DWDay4, DMon4, DDateDa4, DDateMo4, DDateYr4, Dicon4, Dicon_url4, DHtemp4, DLtemp4, DHumi4, DPop4, DRain4, DW_mph4, DW_dir4, DW_dir_deg4, DWeather4, Dconditions4;
char buffer[100] = {0};
char buffer_hour[10] = {0};
char Txtweather[200];
//-------------------------------------------------------------------------------------------
// NTP Servers:
//static const char ntpServerName[] = "ntps1-0.cs.tu-berlin.de"; // Chosen Atomic Time server
static const char ntpServerName[] = "0.pool.ntp.org"; // Recommended server
const int NTP_PACKET_SIZE = 48; // NTP time stamp is in the first 48 bytes of the message
byte packetBuffer[ NTP_PACKET_SIZE]; //buffer to hold incoming and outgoing packets
// A UDP instance to let us send and receive packets over UDP
WiFiUDP Udp; // Initialize User Datagram Protocol (UDP)
unsigned int localPort = 8888; // local port to listen for UDP time packets
//-------------------------------------------------------------------------------------------
// List of the functions defined below:
time_t getNtpTime(); // Convert received seconds into HH:MM:SS, YY-MO-DD
void sendNTPpacket(IPAddress &address); // Require site data
void digitalClockDisplay(); // See below function to print time on Nextion
void weatherDisplay(); // See below function to print weather on Nextion
void obtain_forecast (String forecast_type); // See below function to get weather data
#define min(a,b) ((a)<(b)?(a):(b)); // Define the min(a,b) function!
// =======================================================================
void callserver(){
// Start by connecting to a local WiFi router
// Since WiFi.disconnect(true)erases ssid&pass, define these here each time.
// =========================!!!!!===================================
char ssid[] = "RomTelecom-WEP-B086"; // your network SSID (name)
char pass[] = "AYPDG7J5TFKGF"; // your network password
// =========================!!!!!===================================
Serial.println(F("============================================"));
Serial.print(F("Connecting to ")); Serial.print(ssid); Serial.print(F(" & ")); Serial.println(pass);
delay(200);
WiFi.begin(ssid, pass);
Serial.println(F(">>>>>>>")); Serial.print("Connecting...");
myNextion.setComponentText("g0","Connecting..."); // Scroll weather description text !
delay(300);
int iter=0; // Try several times to connect to local router
while (WiFi.status() != WL_CONNECTED && iter<50) {
// LED blink as long as the connection is not establihed
delay(500);
digitalWrite(ledPin, LOW);
delay(500);
digitalWrite(ledPin, HIGH);
Serial.print(".");
iter++;
}
if (iter<50){
digitalWrite(ledPin, HIGH); // Led OFF
Serial.print("WiFi connected to local"); Serial.print("IP : ");
Serial.println(WiFi.localIP()); Serial.println("********************************");
myNextion.setComponentText("g0","Connected. Updating soon!"); // Scroll weather description text !
delay(300);
// At this stage the ESP32 is connected to the local network
}
else{
digitalWrite(ledPin, LOW); // Led ON
Serial.println(F("!! WiFi connection failed !!!"));
Serial.println(F("!!!!!!!!!!!!!!!!!!!!!!!!!!!!"));
WiFi.disconnect(true);
delay(2000);
}
}
void setup()
{
Serial.begin(115200); // USB communication with Serial Monitor
Serial2.begin(9600); // Initiate 2-nd UART towards Nextion
pinMode(ledPin, OUTPUT); // Set Led pin as output
myNextion.sendCommand("rest"); // reset Nextion
myNextion.sendCommand("dim=40"); // Dim to 40% the display
while(WiFi.status() != WL_CONNECTED){
callserver(); // Try to connect to local server
}
Serial.print("Synchronizing time: ");
Serial.println(ntpServerName);
setSyncProvider(getNtpTime); // Set the MCU time from Internet: the Atomic Clock Time
setSyncInterval(86400); // Requiring sync. after ... seconds
// but will fail if WiFi is turned off.
Serial.print("Next sync. in ");Serial.print(tsec);Serial.println(" s");
Udp.stop(); // STOP UDP
delay(500);
obtain_forecast("forecast"); // Get weather forecast
obtain_forecast("conditions"); // Get local conditions
digitalWrite(ledPin, HIGH); // LED OFF if OK
Serial.println("==============================================");
Serial.print("Weather for current time:");
Serial.print(hour());Serial.print(":");Serial.print(minute());Serial.print(":");Serial.println(second());
Serial.print("Time status:");Serial.println(timeok);
Serial.print("Forecast status:");Serial.println(foreok);
Serial.print("Conditions status:");Serial.println(condok);
Serial.println(DWDay1); Serial.println(Dicon1); Serial.println(DHtemp1);
Serial.println(DLtemp1); Serial.println(DHumi1); Serial.println(DW_mph1);
Serial.println(DWDay2); Serial.println(Dicon2); Serial.println(DHtemp2);
Serial.println(DLtemp2); Serial.println(DHumi2); Serial.println(DW_mph2);
Serial.println(DWDay3); Serial.println(Dicon3); Serial.println(DHtemp3);
Serial.println(DLtemp3); Serial.println(DHumi3); Serial.println(DW_mph3);
Serial.println("--Local weather, now: --");
Serial.println(Txtweather); Serial.println(temp_e); Serial.println(wind_e);
Serial.print(h_up);Serial.print(":"); Serial.println(m_up);
Serial.print(h_set);Serial.print(":");Serial.println(m_set);
vTaskDelay(100/portTICK_RATE_MS);
Serial.println("==============================================");
digitalClockDisplay(); // Send clock data to Nextion
vTaskDelay(100/portTICK_RATE_MS);
weatherDisplay(); // Send weather data to Nextion
nr=min(tsec, wsec);
Serial.print("WiFi off for "); Serial.print(nr); Serial.println(" s : after Setup");
WiFi.disconnect(true); // STOP WIFI!!
vTaskDelay(100/portTICK_RATE_MS);
nr=0;
}
time_t prevDisplay = 0; // moment when the clock was displayed
void loop()
{
// Every second: update time on Nextion
if (now() != prevDisplay) { // ..update the display only if time has changed [every sec.]
prevDisplay = now();
//----------------------------------------
// Compute DST hour if server is not a local one:
DST=0;
if (month()>3 && month()<10)
DST=1;
days=31-((5*year())/4+4)%7;
if (month()==3 && day()>=days)
DST=1;
days=31-((5*year())/4+1)%7;
if (month()==10 && day()<days)
DST=1;
//----------------------------------------
digitalClockDisplay(); // Send clock data to Nextion every second
}
if(millis() - t0t > tsec*1e3) { // after every time interval "tsec"
if (WiFi.status() != WL_DISCONNECTED)
WiFi.disconnect(true);
// if (timeStatus() == timeNeedsSync){
// Firstly: connect to local router (WiFi might be disconnected)
vTaskDelay(100/portTICK_RATE_MS);
while(WiFi.status() != WL_CONNECTED){
callserver(); // Try to connect to local server
if(WiFi.status() != WL_CONNECTED){
delay(5000);
Serial.println("RESET ESP32 in loop() for time update !!!!");
ESP.restart(); // Restart ESP32! So, the code is reinitialized!!
}
// Should be connected at this point, or MCU restarted ???
}
if(WiFi.status() == WL_CONNECTED){
Serial.print("Synchronizing time: "); Serial.println(ntpServerName);
// The "Time" library is supposed to connect to time server now
setSyncProvider(getNtpTime); // Set the MCU time from Internet: the Atomic Clock Time
setSyncInterval(86400); // Requiring sync. after ... seconds
// but will fail if WiFi is turned off.
Serial.print("Next sync. in ");Serial.print(tsec);Serial.println(" s");
Udp.stop(); // STOP UDP
delay(500);
obtain_forecast("forecast"); // Get weather forecast
obtain_forecast("conditions");
if (timeok==true){
t0t=millis();
myNextion.setComponentValue("bt3",0); // Button3 [:] has blue bkgd. if time is updated ...
myNextion.setComponentValue("bt4",0); // Button4 [:] has blue bkgd. if time is updated ...
WiFi.disconnect(true);
Serial.print(F("WiFi off for ")); Serial.print(tsec); Serial.println(F(" s : after Time & Weather sync.!"));
}
else{
myNextion.setComponentValue("bt3",1); // Button3 [:] has orange bkgd. if time is not updated ...
myNextion.setComponentValue("bt4",1); // Button4 [:] has orange bkgd. if time is not updated ...
delay(500);
WiFi.disconnect(true);
Serial.print(F("WiFi off for ")); Serial.print(tsec); Serial.println(F(" s : Failed sync.!"));
}
}
else{
t0t=tsec*1e3-60000; // Repeat updating in 1 minute
WiFi.disconnect(true);
Serial.println(F("!! WiFi off after failing to connect!"));
}
// Just for debugging, print results on Serial Monitor attached via USB
Serial.println(F("=============================================="));
Serial.print(F("Weather for current time:"));
Serial.print(hour());Serial.print(":");Serial.print(minute());Serial.print(":");Serial.println(second());
nr++;
Serial.print("Update no:");Serial.println(nr);
Serial.print("Time status:");Serial.println(timeok);
Serial.print("Forecast status:");Serial.println(foreok);
Serial.print("Conditions status:");Serial.println(condok);
Serial.println(F("----------------------------------------------"));
Serial.println(DWDay1); Serial.println(Dicon1); Serial.println(DHtemp1);
Serial.println(DLtemp1); Serial.println(DHumi1); Serial.println(DW_mph1);
Serial.println(DWDay2); Serial.println(Dicon2); Serial.println(DHtemp2);
Serial.println(DLtemp2); Serial.println(DHumi2); Serial.println(DW_mph2);
Serial.println(DWDay3); Serial.println(Dicon3); Serial.println(DHtemp3);
Serial.println(DLtemp3); Serial.println(DHumi3); Serial.println(DW_mph1);
Serial.println(Txtweather); Serial.println(temp_e); Serial.println(wind_e);
Serial.print(h_up);Serial.print(":"); Serial.println(m_up);
Serial.print(h_set);Serial.print(":");Serial.println(m_set);
// In any case write weather data to Nextion
weatherDisplay(); // Send weather data to Nextion
}
//==============================================================================
// During night hours 0-6, turn off display, but reactivate if touched
if (hour()<6 && zzz==false){
myNextion.sendCommand("dim=0"); // Dim to 0 the display
// myNextion.sendCommand("thup=1"); // Remain responsive to touch
delay(1000);
zzz=true; // Sleeping flag ON, to do this once/day.
Serial.println(F("Display sleeping!"));
// myNextion.sendCommand("sleep=1"); // Go to sleep Nextion!!
}
else if (hour()>=6 && zzz==true){
myNextion.sendCommand("dim=40"); // Dim to 40% the display
//myNextion.sendCommand("wup"); // wake-up Nextion
delay(1000);
zzz=false; // Not-sleelping flag
Serial.println(F("Display activated!"));
}
//==============================================================================
if (millis() < t0t){
t0t=millis(); // After millis reached the limit, reset counters
Serial.print("Time counter t0t reset!");
}
if (millis() < t0w){
t0w=millis(); // After millis reached the limit, reset counters
Serial.print("Weather counter t0w reset!");
}
}
void digitalClockDisplay()
{// Write Clock data on Nextion
// Display the time. Digital clock.
// Send a number to a number cell of Nextion
if (DST==1)
myNextion.setComponentValue("n0",hour()+1);
else
myNextion.setComponentValue("n0",hour());
myNextion.setComponentValue("n1",minute());
myNextion.setComponentValue("n2",second());
myNextion.setComponentText("t3",daysOfTheWeek[weekday()-1]);
// Send strings to string cells of Nextion
myNextion.setComponentText("t4", String(day()));
//myNextion.setComponentText("t5", String(month()));
myNextion.setComponentText("t5", month_of_year[month()-1]);
myNextion.setComponentText("t6", String(year()));
}
void weatherDisplay()
{// Write weather data on Nextion
myNextion.sendCommand("page 0");
myNextion.setComponentValue("n3",temp_e); // to numeric obj. n3 Temp. outside now
myNextion.setComponentText("t8",wind_e); // to text obj. 8 Wind speed outside now
myNextion.setComponentText("t10",DHtemp1); // to text obj. 10 Max Temp. of the day
myNextion.setComponentText("t11",DLtemp1); // to text obj. 11 Min Temp. of the day
myNextion.setComponentText("t12",DHumi1); // to text obj. 12 Humidity of the day
myNextion.setComponentText("g0",Txtweather); // Scroll weather description text + minutes after update!
myNextion.setComponentText("t33",h_up+":"+m_up); // to text obj. 33 Sun rise time
myNextion.setComponentText("t34",h_set+":"+m_set); // to text obj. 33 Sun set time
myNextion.sendCommand("page 1");
myNextion.setComponentText("t14",daysOfTheWeek[(weekday())%7]); // Swow tomorow
myNextion.setComponentText("t20",DHtemp2); //to text obj. 20 Estimated High Temp.
myNextion.setComponentText("t21",DLtemp2); // Estimated Low Temp.
myNextion.setComponentText("t22",DHumi2); // Estimated humidity
myNextion.setComponentText("t31",DW_mph2); // Estimated wind speed
myNextion.setComponentText("t15",daysOfTheWeek[(weekday()+1)%7]); // Swow after tomorow
myNextion.setComponentText("t26",DHtemp3); //to text obj. 26 Estimated High Temp.
myNextion.setComponentText("t27",DLtemp3); // Estimated Low Temp.
myNextion.setComponentText("t28",DHumi3); // Estimated humidity
myNextion.setComponentText("t32",DW_mph3); // Estimated wind speed
myNextion.sendCommand("page 0");
// Now change the pictures accordingly:
if(Dicon1=="clear")
myNextion.sendCommand("p0.pic=0");
if(Dicon1=="partlycloudy")
myNextion.sendCommand("p0.pic=1");
if(Dicon1=="cloudy"||Dicon1=="mostlycloudy")
myNextion.sendCommand("p0.pic=2");
if(Dicon1=="rain" || Dicon1=="chancerain")
myNextion.sendCommand("p0.pic=3");
if(Dicon1=="snow")
myNextion.sendCommand("p0.pic=4");
if(Dicon1=="fog" || Dicon1=="hazy" || Dicon1=="mist")
myNextion.sendCommand("p0.pic=5");
if(Dicon1=="thunderstorm" || Dicon1=="chancetstorms")
myNextion.sendCommand("p0.pic=6");
if(Dicon1=="sleet" || Dicon1=="chancetstorms")
myNextion.sendCommand("p0.pic=6");
myNextion.sendCommand("page 1"); // Go to page 1
if(Dicon2=="clear")
myNextion.sendCommand("p1.pic=0");
if(Dicon2=="partlycloudy")
myNextion.sendCommand("p1.pic=1");
if(Dicon2=="cloudy"||Dicon1=="mostlycloudy")
myNextion.sendCommand("p1.pic=2");
if(Dicon2=="rain" || Dicon2=="chancerain")
myNextion.sendCommand("p1.pic=3");
if(Dicon2=="snow")
myNextion.sendCommand("p1.pic=4");
if(Dicon2=="fog" || Dicon2=="hazy" || Dicon2=="mist")
myNextion.sendCommand("p1.pic=5");
if(Dicon2=="thunderstorm" || Dicon2=="chancetstorms")
myNextion.sendCommand("p1.pic=6");
if(Dicon3=="clear")
myNextion.sendCommand("p2.pic=0");
if(Dicon3=="partlycloudy")
myNextion.sendCommand("p2.pic=1");
if(Dicon3=="cloudy"||Dicon1=="mostlycloudy")
myNextion.sendCommand("p2.pic=2");
if(Dicon3=="rain" || Dicon3=="chancerain")
myNextion.sendCommand("p2.pic=3");
if(Dicon3=="snow")
myNextion.sendCommand("p2.pic=4");
if(Dicon3=="fog" || Dicon3=="hazy" || Dicon3=="mist")
myNextion.sendCommand("p2.pic=5");
if(Dicon3=="thunderstorm" || Dicon3=="chancetstorms")
myNextion.sendCommand("p2.pic=6");
myNextion.sendCommand("page 0"); // Come back to page 0
if (condok==true)
myNextion.setComponentValue("bt1",1); // Button1 is green if local conditions are updated ...
else
myNextion.setComponentValue("bt1",0); // else it is red.
if (foreok==true)
myNextion.setComponentValue("bt2",1); // Button2 is green if forecast is updated ...
else
myNextion.setComponentValue("bt2",0); // else it is red.
}
/*-------- NTP code ----------*/
time_t getNtpTime()
{ //PaulStoffregen/Time
while(WiFi.status() != WL_CONNECTED){
callserver(); // Try (50 times) to connect to the local server
if(WiFi.status() != WL_CONNECTED){
delay(3000);
ESP.restart(); // Restart ESP32! So, the code is reinitialized!!
}
// Should be connected at this point, or MCU restarted
}
Udp.begin(localPort);
IPAddress ntpServerIP; // NTP server's ip address
// while (Udp.parsePacket() > 0) ; // discard any previously received packets
Serial.print("Transmit NTP Request to:");
WiFi.hostByName(ntpServerName, ntpServerIP);
Serial.print(ntpServerName); Serial.print("= "); Serial.println(ntpServerIP);
uint32_t beginWait = millis();
sendNTPpacket(ntpServerIP);
while (millis() - beginWait < 5000) {
int size = Udp.parsePacket();
if (size >= NTP_PACKET_SIZE) {
Udp.read(packetBuffer, NTP_PACKET_SIZE); // read packet into the buffer
Serial.println(F("Received NTP Response. Successfully updated Time..."));
Serial.println(F("====================================="));
timeok=true;
unsigned long secsSince1900;
// convert four bytes starting at location 40 to a long integer
secsSince1900 = (unsigned long)packetBuffer[40] << 24;
secsSince1900 |= (unsigned long)packetBuffer[41] << 16;
secsSince1900 |= (unsigned long)packetBuffer[42] << 8;
secsSince1900 |= (unsigned long)packetBuffer[43];
return secsSince1900 - 2208988800UL + timeZone * SECS_PER_HOUR; // maybe add +1 for update delays?
// Exit function here.
}
else
timeok=false;
}
Udp.endPacket();
Serial.println("No NTP Response :-(");
Serial.println("Time is not synchronized");
Serial.println("RESET ESP32 in getNtpTime() !!!!");
ESP.restart(); // Restart ESP32! So, the code is reinitialized!!
return 0; // return 0 if unable to get the time
}
void sendNTPpacket(IPAddress& address)
// send an NTP request to the time server at the given address
{
Serial.println("sending NTP packet...");
// set all bytes in the buffer to 0
memset(packetBuffer, 0, NTP_PACKET_SIZE);
// Initialize values needed to form NTP request
// (see URL above for details on the packets)
packetBuffer[0] = 0b11100011; // LI, Version, Mode
packetBuffer[1] = 0; // Stratum, or type of clock
packetBuffer[2] = 6; // Polling Interval
packetBuffer[3] = 0xEC; // Peer Clock Precision
// 8 bytes of zero for Root Delay & Root Dispersion
packetBuffer[12] = 49;
packetBuffer[13] = 0x4E;
packetBuffer[14] = 49;
packetBuffer[15] = 52;
// all NTP fields have been given values, now
// you can send a packet requesting a timestamp:
Udp.beginPacket(address, 123); //NTP requests are to port 123
Udp.write(packetBuffer, NTP_PACKET_SIZE);
Udp.endPacket();
}
void obtain_forecast (String forecast_type) {
static char RxBuf[8704];
String request;
if (forecast_type=="forecast")
request = "GET /api/" + API_key + "/"+ forecast_type + "/q/" + Country + "/" + City + ".json HTTP/1.1\r\n"; // Request Country/City
else
request = "GET /api/" + API_key + "/"+ forecast_type + "/astronomy/q/geolookup/pws:" + pws + ".json HTTP/1.1\r\n"; // Request data by pws station
// http://api.wunderground.com/api/7a4e220afaaf6547/conditions/q/geolookup/pws:IBUCURET77.json
request += F("User-Agent: Weather Webserver v");
//request += version;
request += F("\r\n");
request += F("Accept: */*\r\n");
request += "Host: " + String(wxserver) + "\r\n";
request += F("Connection: close\r\n");
request += F("\r\n");
Serial.println(F("------------------------------------------"));
Serial.print(F("Connecting to ")); Serial.println(wxserver);
WiFiClient httpclient;
if (!httpclient.connect(wxserver, 80)) {
Serial.println(F("connection failed"));
httpclient.flush(); //Waits until all outgoing characters in buffer have been sent (forever if lost connection!)
httpclient.stop();
WiFi.disconnect(true);
vTaskDelay(1000/portTICK_RATE_MS);
ESP.restart(); // Restart ESP32! So, the code is reinitialized???
return;
}
vTaskDelay(100/portTICK_RATE_MS);
httpclient.print(request); //send the request to the server
httpclient.flush(); //Waits until all outgoing characters in buffer have been sent (forever if connection lost)
Serial.print(forecast_type); Serial.print(F(": "));
Serial.println("The request was sent to Weather server. Waiting for the response...");
vTaskDelay(100/portTICK_RATE_MS);
// Collect http response headers and content from Weather Underground, discarding HTTP headers,
// the content is JSON formatted and will be returned in RxBuf.
int respLen = 0;
bool skip_headers = true;
String rx_line;
unsigned long beginWait = millis();
while ((httpclient.connected() || httpclient.available()) && (millis() - beginWait < 10000)) {
//vTaskDelay(10/portTICK_RATE_MS);//works for keeping the watchdog fed when there is no other activity
if (skip_headers) {
rx_line = httpclient.readStringUntil('\n');
if (rx_line.length() <= 1) { // a blank line denotes end of headers
skip_headers = false;
}
}
else {
int bytesIn;
bytesIn = httpclient.read((uint8_t *)&RxBuf[respLen], sizeof(RxBuf) - respLen);
Serial.print(F("bytesIn: ")); Serial.println(bytesIn);
if (bytesIn > 0) {
respLen += bytesIn; // Gather bytes in RxBuf
if (respLen > sizeof(RxBuf)) respLen = sizeof(RxBuf);
//vTaskDelay(10/portTICK_RATE_MS); //works for keeping the watchdog fed when there is no other activity
}
else if (bytesIn < 0) {
vTaskDelay(200/portTICK_RATE_MS);
Serial.print("?"); // Appears very often, waiting for an answer!
vTaskDelay(200/portTICK_RATE_MS); //works for keeping the watchdog fed when there is no other activity
}
}
vTaskDelay(100/portTICK_RATE_MS); //works for keeping the watchdog fed when there is no other activity
}
httpclient.stop();
vTaskDelay(100/portTICK_RATE_MS); //works for keeping the watchdog fed when there is no other activity
RxBuf[respLen++] = '\0'; // Terminate the C string
// RxBuf contains now the server response, of length=respLen.
if (respLen<=1 && forecast_type == "forecast"){
foreok=false; // If no data available return without changing forecast
return;
}
if (respLen<=1 && forecast_type == "conditions"){
condok=false; // If no data available return without changing conditions
return;
}
if (forecast_type == "forecast"){
if (showWeather_forecast(RxBuf)){
foreok=true;
vTaskDelay(50/portTICK_RATE_MS); //works for keeping the watchdog fed when there is no other activity
}
}
if (forecast_type == "conditions"){
if (showWeather_conditions(RxBuf)){
condok=true;
vTaskDelay(50/portTICK_RATE_MS); //works for keeping the watchdog fed when there is no other activity
}
}
}
bool showWeather_forecast(char *json)
{// Convert forecast received data into Strings and values
DynamicJsonBuffer jsonBuffer(8704);
char *jsonstart = strchr(json, '{'); // Look for first "{"
// Serial.println(F("jsonstart ")); Serial.println(jsonstart); // This is input data
if (jsonstart == NULL) {
Serial.println(F("JSON data missing"));
return false;
}
json = jsonstart;
// Parse JSON
JsonObject& root = jsonBuffer.parseObject(json);
if (!root.success()) {
Serial.println(F("jsonBuffer.parseObject() failed"));
return false;
}
JsonObject& forecast = root["forecast"]["simpleforecast"];
String WDay1 = forecast["forecastday"][0]["date"]["weekday"]; DWDay1 = WDay1;
int DateDa1 = forecast["forecastday"][0]["date"]["day"]; DDateDa1 = DateDa1<10?"0"+String(DateDa1):String(DateDa1);
String Temp_mon = forecast["forecastday"][0]["date"]["monthname"];
String Mon1 = forecast["forecastday"][0]["date"]["monthname_short"]; DMon1 = Mon1;
int DateYr1 = forecast["forecastday"][0]["date"]["year"]; DDateYr1 = String(DateYr1).substring(2);
observationtime = "from " + String(DDateDa1) + " " + Temp_mon + ", " + DateYr1;
if (Units == "M" || Units == "X") {
String icon1 = forecast["forecastday"][0]["icon"]; Dicon1 = String(icon1);
String conditions1 = forecast["forecastday"][0]["conditions"]; Dconditions1 = String(conditions1);
int Htemp1 = forecast["forecastday"][0]["high"]["celsius"]; DHtemp1 = String(Htemp1);
int Ltemp1 = forecast["forecastday"][0]["low"]["celsius"]; DLtemp1 = String(Ltemp1);
int rain1 = forecast["forecastday"][0]["qpf_allday"]["mm"]; DRain1 = String(rain1)+"mm";
if (Units == "M") {int w_mph1 = forecast["forecastday"][0]["avewind"]["kph"]; DW_mph1 = String(w_mph1)+"km/h";}
else {int w_mph1 = forecast["forecastday"][0]["avewind"]["mph"]; DW_mph1 = String(w_mph1)+"mph";}
}
String icon_url1 = forecast["forecastday"][0]["icon_url"];
Dicon_url1 = icon_url1.substring(0,icon_url1.indexOf("/i/c/")+5) + icon_set + icon_url1.substring(icon_url1.indexOf("/i/c/")+6);
String pop1 = forecast["forecastday"][0]["pop"]; DPop1 = String(pop1);
String w_dir1 = forecast["forecastday"][0]["avewind"]["dir"]; DW_dir1 = String(w_dir1);
String w_dir_deg1 = forecast["forecastday"][0]["avewind"]["degrees"]; DW_dir_deg1 = String(w_dir_deg1);
int humi1 = forecast["forecastday"][0]["avehumidity"]; DHumi1 = String(humi1);
String WDay2 = forecast["forecastday"][1]["date"]["weekday"]; DWDay2 = WDay2;
int DateDa2 = forecast["forecastday"][1]["date"]["day"]; DDateDa2 = DateDa2<10?"0"+String(DateDa2):String(DateDa2);
String Mon2 = forecast["forecastday"][1]["date"]["monthname_short"]; DMon2 = Mon2;
int DateYr2 = forecast["forecastday"][1]["date"]["year"]; DDateYr2 = String(DateYr2).substring(2);
if (Units == "M" || Units == "X") {
String icon2 = forecast["forecastday"][1]["icon"]; Dicon2 = String(icon2);
String conditions2 = forecast["forecastday"][1]["conditions"]; Dconditions2 = String(conditions2);
int Htemp2 = forecast["forecastday"][1]["high"]["celsius"]; DHtemp2 = String(Htemp2);
int Ltemp2 = forecast["forecastday"][1]["low"]["celsius"]; DLtemp2 = String(Ltemp2);
int rain2 = forecast["forecastday"][1]["qpf_allday"]["mm"]; DRain2 = String(rain2)+"mm";
if (Units == "M"){int w_mph2 = forecast["forecastday"][1]["avewind"]["kph"]; DW_mph2 = String(w_mph2)+"km/h";}
else {int w_mph2 = forecast["forecastday"][1]["avewind"]["mph"]; DW_mph2 = String(w_mph2)+"mph";
}
}
String icon_url2 = forecast["forecastday"][1]["icon_url"];
Dicon_url2 = icon_url2.substring(0,icon_url2.indexOf("/i/c/")+5) + icon_set + icon_url2.substring(icon_url2.indexOf("/i/c/")+6);
String pop2 = forecast["forecastday"][1]["pop"]; DPop2 = String(pop2);
String w_dir2 = forecast["forecastday"][1]["avewind"]["dir"]; DW_dir2 = String(w_dir2);
String w_dir_deg2 = forecast["forecastday"][1]["avewind"]["degrees"]; DW_dir_deg2 = String(w_dir_deg2);
int humi2 = forecast["forecastday"][1]["avehumidity"]; DHumi2 = String(humi2);
String WDay3 = forecast["forecastday"][2]["date"]["weekday"]; DWDay3 = WDay3;
int DateDa3 = forecast["forecastday"][2]["date"]["day"]; DDateDa3 = DateDa3<10?"0"+String(DateDa3):String(DateDa3);
String Mon3 = forecast["forecastday"][2]["date"]["monthname_short"]; DMon3 = Mon3;
int DateYr3 = forecast["forecastday"][2]["date"]["year"]; DDateYr3 = String(DateYr3).substring(2);
if (Units == "M" || Units == "X") {
String icon3 = forecast["forecastday"][2]["icon"]; Dicon3 = String(icon3);
String conditions3 = forecast["forecastday"][2]["conditions"]; Dconditions3 = String(conditions3);
int Htemp3 = forecast["forecastday"][2]["high"]["celsius"]; DHtemp3 = String(Htemp3);
int Ltemp3 = forecast["forecastday"][2]["low"]["celsius"]; DLtemp3 = String(Ltemp3);
int rain3 = forecast["forecastday"][2]["qpf_allday"]["mm"]; DRain3 = String(rain3)+"mm";
if (Units == "M") {int w_mph3 = forecast["forecastday"][2]["avewind"]["kph"]; DW_mph3 = String(w_mph3)+"km/h"; }
else {int w_mph3 = forecast["forecastday"][2]["avewind"]["mph"]; DW_mph3 = String(w_mph3)+"mph"; }
}
String icon_url3 = forecast["forecastday"][2]["icon_url"];
Dicon_url3 = icon_url3.substring(0,icon_url3.indexOf("/i/c/")+5) + icon_set + icon_url3.substring(icon_url3.indexOf("/i/c/")+6);
String pop3 = forecast["forecastday"][2]["pop"]; DPop3 = String(pop3);
String w_dir3 = forecast["forecastday"][2]["avewind"]["dir"]; DW_dir3 = String(w_dir3);
String w_dir_deg3 = forecast["forecastday"][2]["avewind"]["degrees"]; DW_dir_deg3 = String(w_dir_deg3);
int humi3 = forecast["forecastday"][2]["avehumidity"]; DHumi3 = String(humi3);
String WDay4 = forecast["forecastday"][3]["date"]["weekday"]; DWDay4 = WDay4;
int DateDa4 = forecast["forecastday"][3]["date"]["day"]; DDateDa4 = DateDa4<10?"0"+String(DateDa4):String(DateDa4);
String Mon4 = forecast["forecastday"][3]["date"]["monthname_short"]; DMon4 = Mon4;
int DateYr4 = forecast["forecastday"][3]["date"]["year"]; DDateYr4 = String(DateYr4).substring(2);
if (Units == "M" || Units == "X") {
String icon4 = forecast["forecastday"][3]["icon"]; Dicon4 = String(icon4);
String conditions4 = forecast["forecastday"][3]["conditions"]; Dconditions4 = String(conditions4);
int Htemp4 = forecast["forecastday"][3]["high"]["celsius"]; DHtemp4 = String(Htemp4);
int Ltemp4 = forecast["forecastday"][3]["low"]["celsius"]; DLtemp4 = String(Ltemp4);
int rain4 = forecast["forecastday"][3]["qpf_allday"]["mm"]; DRain4 = String(rain4)+"mm";
if (Units == "M") {int w_mph4 = forecast["forecastday"][3]["avewind"]["kph"]; DW_mph4 = String(w_mph4)+"km/h";}
else {int w_mph4 = forecast["forecastday"][3]["avewind"]["mph"]; DW_mph4 = String(w_mph4)+"mph";}
}
String icon_url4 = forecast["forecastday"][3]["icon_url"];
Dicon_url4 = icon_url4.substring(0,icon_url4.indexOf("/i/c/")+5) + icon_set + icon_url4.substring(icon_url4.indexOf("/i/c/")+6);
String pop4 = forecast["forecastday"][3]["pop"]; DPop4 = String(pop4);
String w_dir4 = forecast["forecastday"][3]["avewind"]["dir"]; DW_dir4 = String(w_dir4);
String w_dir_deg4 = forecast["forecastday"][3]["avewind"]["degrees"]; DW_dir_deg4 = String(w_dir_deg4);
int humi4 = forecast["forecastday"][3]["avehumidity"]; DHumi4 = String(humi4);
JsonObject& current = root["forecast"]["txt_forecast"];
String Txw1 = current ["forecastday"][0]["fcttext_metric"]; // Read the text describing the forecast.
Txw1.toCharArray(Txtweather, 200); // Convert String to CharArray for Nextion
return true;
}
bool showWeather_conditions(char *json)
{// Convert forecast received data into Strings and values
DynamicJsonBuffer jsonBuffer(8704);
char *jsonstart = strchr(json, '{');
//Serial.println(F("jsonstart ")); Serial.println(jsonstart); // This is input data
if (jsonstart == NULL) {
Serial.println(F("JSON data missing"));
return false; }
json = jsonstart;
// Parse JSON
JsonObject& root = jsonBuffer.parseObject(json);
if (!root.success()) {
Serial.println(F("jsonBuffer.parseObject() failed"));
return false; }
JsonObject& current = root["current_observation"];
const float temp_c = current["temp_c"]; // Extract local temp. now
const float wind_c = current["wind_kph"]; // Extract local wind speed now
const char h_c = current["relative_humidity"]; // Extract local humidity now
temp_e=temp_c+0.5; // Round the decimal value
wind_e=String(wind_c,0)+"km/h"; // Convert wind speed to string
JsonObject& currentup = root["sun_phase"]["sunrise"];
String h_u = currentup["hour"]; // Extract local Sunrise hour
h_up=String(h_u);
String m_u = currentup["minute"]; // Extract local Sunrise minute
m_up=String(m_u);
JsonObject& currentset = root["sun_phase"]["sunset"];
String h_s = currentset["hour"]; // Extract local Sunrise hour
h_set=String(h_s);
String m_s = currentset["minute"]; // Extract local Sunrise minute
m_set=String(m_s);
return true; // All conversions worked, return True
}
Comments