M.V.P.
Published

Atomic Clock and Weather with ESP32 and Nextion

Using an ESP32 board and Nextion 2.4" (240x320) you can have accurate time and weather forecast from internet.

BeginnerFull instructions provided1 hour18,075
Atomic Clock and Weather with ESP32 and Nextion

Things used in this project

Hardware components

ESP32 - Geekworm Easy Kit ESP32-C1
×1
Nextion 2.8"
×1

Software apps and online services

Arduino IDE
Arduino IDE

Story

Read more

Schematics

Fritzing schematics

Code

NEXTION code

ActionScript
This is the Nextion file of the project. Upload it, compile and save to SD card.
No preview (download only).

Arduino code

Arduino
Put your own router and Wunderground passwords
/* 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
}

Credits

M.V.P.

M.V.P.

7 projects • 46 followers
Arduino: Amazing, useful and rewarding hobby for some!

Comments