回到首页 返回首页
回到顶部 回到顶部
返回上一页 返回上一页

【Arduino 动手做】ESP32 和 Nextion 的原子钟和天气 简单

头像 驴友花雕 2025.06.27 8 0

使用 ESP32 板和 Nextion 2.4 英寸 (240x320),您可以从互联网获得准确的时间和天气预报。

准确的时间和天气数据对每个人都有用。您可以花 25-30 欧元购买一个气象站,但只有其中一些将时间同步到 Atomic Clock 服务器。更不用说天气了:充其量,有些项目只有一个用于温度和湿度的室外传感器。从这些,也许还有一个大气压力传感器,可以估计出天气预报?为什么不获得准确的预报,并每小时更新一次呢!您只需通过您的家庭互联网提供商进行阅读,但使用您自己制作的设备,在 Arduino IDE(集成开发环境)下编程。

这不是互联网上第一个可用的此类项目。我们的灵感来自:
http://www.plastibots.com/index.php/2017/01/03/wiot-2-weather-station-nextion-tft-with-esp8266/
我们使用 ESP32,它的文档要少得多。
警告:JSON 解码库 https://github.com/bblanchon/ArduinoJson 已更新,因此我的代码也需要尽快更新!下面给出的代码与最新的库不兼容。

目的
它仅使用可靠的时间和天气数据。房屋周围的传感器从来都不是处于标准条件(阴影、离地高度、精度等),并且这些值可能会产生误导。来自互联网的天气预报是专业和官方的。时间每小时调整一次,因此可以预期最大误差为 1 秒(不是每月,不是每年,而是永久)。

01.jpg
02.jpg
time_weather_bb_LULnVQTI30.jpg

项目代码

代码
/* 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
}

【Arduino 动手做】ESP32 和 Nextion 的原子钟和天气
项目链接:https://www.hackster.io/M-V-P/atomic-clock-and-weather-with-esp32-and-nextion-8eaea7
项目作者:MVP
项目参考:http://www.plastibots.com/index.php/2017/01/03/wiot-2-weather-station-nextion-tft-with-esp8266/
项目代码:https://www.hackster.io/code_files/185702/download
https://hacksterio.s3.amazonaws.com/uploads/attachments/401051/digital_clock-weather.HMI

00.jpg

评论

user-avatar
icon 他的勋章
    展开更多