// Define the platform (uncomment the appropriate line)
#define ESP32
//#define ESP8266
#if defined(ESP32)
#include <WiFi.h>
#include <HTTPClient.h>
#elif defined(ESP8266)
#include <ESP8266WiFi.h>
#include <ESP8266HTTPClient.h>
#else
#error "This code is intended to run on ESP8266 or ESP32 platform."
#endif
#include <ArduinoJson.h>
#define DEBUG 1 // 1 ==> Enables Serial prints
#if DEBUG == 1
#define BAUD 9600 //9600 bit/sec
#define debug(x) Serial.print(x)
#define debugln(x) Serial.println(x)
#define debugF(x, ...) Serial.printf(x, __VA_ARGS__)
#else //Replace with nothing
#define debug(x)
#define debugln(x)
#define debugF(x, ...)
#endif
char WIFI_SSID[] = "Wokwi-GUEST"; // WiFi SSID
char WIFI_PASSWORD[] = ""; // WiFi Password
const String TIME_API_URL = "https://worldtimeapi.org/api/timezone/"; // Base URL for the API
/*TIMEZONE WILL BE OBTAINED FROM WEATHER API WHEN SELECTING USER LOCATION*/
String TIMEZONE = "Africa/Tunis"; // Path for the specific timezone
/* ******** OLD API **********
// User defined location details (Latitude and Longitude can be obtained from Google Maps)
String USER_LATITUDE = "33.8815"; //Gabes LATITUDE
String USER_LONGITUDE = "10.0982"; //Gabes LONGITUDE
String WEATHER_API_URL = "https://api.open-meteo.com/v1/forecast?latitude=" + USER_LATITUDE + "&longitude=" + USER_LONGITUDE + "&hourly=temperature_2m,relative_humidity_2m,precipitation_probability,rain,wind_speed_10m&timezone=Europe%2FLondon&forecast_days=1"; // URL for the API
*/
// User defined location details
String USER_LOCATION = "Gabes";
String WEATHER_API_URL = "https://weather.visualcrossing.com/VisualCrossingWebServices/rest/services/timeline/" + USER_LOCATION + "?unitGroup=metric&elements=datetime%2Cname%2Clatitude%2Clongitude%2Ctempmax%2Ctempmin%2Ctemp%2Chumidity%2Cprecip%2Cprecipprob%2Cwindspeed&include=days%2Cfcst&key=GBJT2APDPGR428GZTEPCS4AU9&options=stnslevel1&contentType=json"; // URL for the API
// These variables need to be global to use them in the sendData() | !TODO : sendData()
int year = 0;
int month = 0;
int day = 0;
int hour = 0;
int minute = 0;
// Extract Date : "2024-06-12"
char date_str[11]; // 10 characters for the date + null terminator
const char* timezone_weatherAPI = "Africa/Tunis";
const char* day_datetime = "2024-06-12";
int day_tempmax = 0;
int day_tempmin = 0;
int day_temp = 0;
int day_humidity = 0;
int day_precip = 0;
int day_precipprob = 0;
int day_windspeed = 0;
unsigned long startMillis; //some global variables available anywhere in the program
unsigned long currentMillis;
unsigned long sec = 90; //90 sec
unsigned long period = sec * 1000; // update time&date every 90 sec
int prevHourValue = 0; // Initialize previous value to 0
// Function prototypes
void connectToWiFi();
bool fetchDateTime();
void handleFailedWiFiConnection();
void setup() {
//Initialize serial and wait for port to open:
#if DEBUG == 1
// Record the start time
unsigned long startTime = millis();
Serial.begin(BAUD); // Start serial communication
while (!Serial && (millis() - startTime < 10 * 1000)) { // 10 sec to avoid infinite loop
// Wait for serial port to connect.
}
if (!(millis() - startTime < 10 * 1000)) {
// You can blink an LED when failed to open serial communication.
}
#endif
debugln(" .: ESP : TIME & WEATHER :. "); // Print once
connectToWiFi(); // Connect to the WiFi network
if (fetchWeather()) { // Fetch and print weather
debugln("TIMEZONE fetched successfully from Weather API");
} else {
debugln("Failed to fetch TIMEZONE from Weather API");
}
startMillis = millis(); //initial start time
period = 10; //10 mili_Sec (first time getting time&date)
}
void loop() {
currentMillis = millis(); //get the current "time" (actually the number of milliseconds since the program started)
if (currentMillis - startMillis >= period) //test whether the period has elapsed
{
period = sec * 1000;
if (fetchDateTime()) { // Fetch and print the date and time
debugln("DateTime fetched successfully");
} else {
debugln("Failed to fetch DateTime");
}
startMillis = currentMillis; //IMPORTANT to save the start time.
}
// Check if the current hour is different from the previous hour
if (hour != prevHourValue) {
fetchWeather(); // Call the fetchWeather() function
prevHourValue = hour; // Update the previous value
}
}
void connectToWiFi() {
// Record the start time
unsigned long startTime = millis();
WiFi.begin(WIFI_SSID, WIFI_PASSWORD); // Start WiFi connection
debug("Connecting");
while (WiFi.status() != WL_CONNECTED && (millis() - startTime < 60 * 1000)) { // Wait for the connection & 60 sec timeout to avoid infinite loop
delay(500);
debug(".");
}
if (!(millis() - startTime < 60 * 1000)) {
// You can blink an LED when failed to connect to wifi
handleFailedWiFiConnection();
return;
}
debugln("");
debug("Connected to WiFi network with IP Address: ");
debugln(WiFi.localIP()); // Print local IP address
}
void handleFailedWiFiConnection() {
debugln("Failed to connect to WiFi after multiple attempts. Restarting...");
ESP.restart();
}
bool fetchDateTime() {
HTTPClient http;
#if defined(ESP8266)
WiFiClient client; //ESP8266
http.begin(client, TIME_API_URL + TIMEZONE); //ESP8266
#endif
#if defined(ESP32)
http.begin(TIME_API_URL + TIMEZONE); //ESP32
#endif
int httpResponseCode = http.GET(); // Send GET request
if (httpResponseCode > 0) { // Check if the request was successful
if (httpResponseCode == HTTP_CODE_OK) { // Check if the response code is 200 (OK)
String responseData = http.getString(); // Get the response data as a string
StaticJsonDocument<64> doc;
DeserializationError error = deserializeJson(doc, responseData); // Parse JSON data
if (error) { // Check if parsing was successful
debug("deserializeJson() failed: ");
debugln(error.c_str());
http.end(); // Close HTTP connection
return false;
}
const char* datetime = doc["datetime"]; // Extract "datetime" field from JSON
strncpy(date_str, datetime, 10);
date_str[10] = '\0'; // Ensure null termination
debug("Extracted date: ");
debugln(date_str);
year = atoi(datetime); // Extract year
month = atoi(datetime + 5); // Extract month
day = atoi(datetime + 8); // Extract day
hour = atoi(datetime + 11); // Extract hour
minute = atoi(datetime + 14); // Extract minute
debug("Year: ");
debugln(year);
debug("Month: ");
debugln(month);
debug("Day: ");
debugln(day);
debug("H: ");
debugln(hour);
debug("M: ");
debugln(minute);
http.end(); // Close HTTP connection
return true; // Return true if successful
} else {
debugF("[HTTP] GET... code: %d\n", httpResponseCode); // Print the HTTP response code if not 200
}
} else {
debugF("[HTTP] GET... failed, error: %s\n", http.errorToString(httpResponseCode).c_str()); // Print the error message if request failed
}
http.end(); // Close HTTP connection
return false;
}
bool fetchWeather() {
HTTPClient http;
#if defined(ESP8266)
WiFiClient client; //ESP8266
http.begin(client, WEATHER_API_URL); //ESP8266
#endif
#if defined(ESP32)
http.begin(WEATHER_API_URL); //ESP32
#endif
int httpResponseCode = http.GET(); // Send GET request
if (httpResponseCode > 0) { // Check if the request was successful
if (httpResponseCode == HTTP_CODE_OK) { // Check if the response code is 200 (OK)
String responseData = http.getString(); // Get the response data as a string
DynamicJsonDocument doc(3072);
DeserializationError error = deserializeJson(doc, responseData);
if (error) {
Serial.print("deserializeJson() failed: ");
Serial.println(error.c_str());
http.en// Define the platform (uncomment the appropriate line)
#define ESP32
//#define ESP8266
#if defined(ESP32)
#include <WiFi.h>
#include <HTTPClient.h>
#elif defined(ESP8266)
#include <ESP8266WiFi.h>
#include <ESP8266HTTPClient.h>
#else
#error "This code is intended to run on ESP8266 or ESP32 platform."
#endif
#include <ArduinoJson.h>
#define DEBUG 1 // 1 ==> Enables Serial prints
#if DEBUG == 1
#define BAUD 9600 //9600 bit/sec
#define debug(x) Serial.print(x)
#define debugln(x) Serial.println(x)
#define debugF(x, ...) Serial.printf(x, __VA_ARGS__)
#else //Replace with nothing
#define debug(x)
#define debugln(x)
#define debugF(x, ...)
#endif
char WIFI_SSID[] = "Wokwi-GUEST"; // WiFi SSID
char WIFI_PASSWORD[] = ""; // WiFi Password
const String TIME_API_URL = "https://worldtimeapi.org/api/timezone/"; // Base URL for the API
/*TIMEZONE WILL BE OBTAINED FROM WEATHER API WHEN SELECTING USER LOCATION*/
String TIMEZONE = "Africa/Tunis"; // Path for the specific timezone
/* ******** OLD API **********
// User defined location details (Latitude and Longitude can be obtained from Google Maps)
String USER_LATITUDE = "33.8815"; //Gabes LATITUDE
String USER_LONGITUDE = "10.0982"; //Gabes LONGITUDE
String WEATHER_API_URL = "https://api.open-meteo.com/v1/forecast?latitude=" + USER_LATITUDE + "&longitude=" + USER_LONGITUDE + "&hourly=temperature_2m,relative_humidity_2m,precipitation_probability,rain,wind_speed_10m&timezone=Europe%2FLondon&forecast_days=1"; // URL for the API
*/
// User defined location details
String USER_LOCATION = "Gabes";
String WEATHER_API_URL = "https://weather.visualcrossing.com/VisualCrossingWebServices/rest/services/timeline/" + USER_LOCATION + "?unitGroup=metric&elements=datetime%2Cname%2Clatitude%2Clongitude%2Ctempmax%2Ctempmin%2Ctemp%2Chumidity%2Cprecip%2Cprecipprob%2Cwindspeed&include=days%2Cfcst&key=GBJT2APDPGR428GZTEPCS4AU9&options=stnslevel1&contentType=json"; // URL for the API
// These variables need to be global to use them in the sendData() | !TODO : sendData()
int year = 0;
int month = 0;
int day = 0;
int hour = 0;
int minute = 0;
// Extract Date : "2024-06-12"
char date_str[11]; // 10 characters for the date + null terminator
const char* timezone_weatherAPI = "Africa/Tunis";
const char* day_datetime = "2024-06-12";
int day_tempmax = 0;
int day_tempmin = 0;
int day_temp = 0;
int day_humidity = 0;
int day_precip = 0;
int day_precipprob = 0;
int day_windspeed = 0;
unsigned long startMillis; //some global variables available anywhere in the program
unsigned long currentMillis;
unsigned long sec = 90; //90 sec
unsigned long period = sec * 1000; // update time&date every 90 sec
int prevHourValue = 0; // Initialize previous value to 0
// Function prototypes
void connectToWiFi();
bool fetchDateTime();
void handleFailedWiFiConnection();
void setup() {
//Initialize serial and wait for port to open:
#if DEBUG == 1
// Record the start time
unsigned long startTime = millis();
Serial.begin(BAUD); // Start serial communication
while (!Serial && (millis() - startTime < 10 * 1000)) { // 10 sec to avoid infinite loop
// Wait for serial port to connect.
}
if (!(millis() - startTime < 10 * 1000)) {
// You can blink an LED when failed to open serial communication.
}
#endif
debugln(" .: ESP : TIME & WEATHER :. "); // Print once
connectToWiFi(); // Connect to the WiFi network
if (fetchWeather()) { // Fetch and print weather
debugln("TIMEZONE fetched successfully from Weather API");
} else {
debugln("Failed to fetch TIMEZONE from Weather API");
}
startMillis = millis(); //initial start time
period = 10; //10 mili_Sec (first time getting time&date)
}
void loop() {
currentMillis = millis(); //get the current "time" (actually the number of milliseconds since the program started)
if (currentMillis - startMillis >= period) //test whether the period has elapsed
{
period = sec * 1000;
if (fetchDateTime()) { // Fetch and print the date and time
debugln("DateTime fetched successfully");
} else {
debugln("Failed to fetch DateTime");
}
startMillis = currentMillis; //IMPORTANT to save the start time.
}
// Check if the current hour is different from the previous hour
if (hour != prevHourValue) {
fetchWeather(); // Call the fetchWeather() function
prevHourValue = hour; // Update the previous value
}
}
void connectToWiFi() {
// Record the start time
unsigned long startTime = millis();
WiFi.begin(WIFI_SSID, WIFI_PASSWORD); // Start WiFi connection
debug("Connecting");
while (WiFi.status() != WL_CONNECTED && (millis() - startTime < 60 * 1000)) { // Wait for the connection & 60 sec timeout to avoid infinite loop
delay(500);
debug(".");
}
if (!(millis() - startTime < 60 * 1000)) {
// You can blink an LED when failed to connect to wifi
handleFailedWiFiConnection();
//return;
}
debugln("");
debug("Connected to WiFi network with IP Address: ");
debugln(WiFi.localIP()); // Print local IP address
}
void handleFailedWiFiConnection() {
debugln("Failed to connect to WiFi after multiple attempts. Restarting...");
ESP.restart();
}
bool fetchDateTime() {
HTTPClient http;
#if defined(ESP8266)
WiFiClient client; //ESP8266
http.begin(client, TIME_API_URL + TIMEZONE); //ESP8266
#endif
#if defined(ESP32)
http.begin(TIME_API_URL + TIMEZONE); //ESP32
#endif
int httpResponseCode = http.GET(); // Send GET request
if (httpResponseCode > 0) { // Check if the request was successful
if (httpResponseCode == HTTP_CODE_OK) { // Check if the response code is 200 (OK)
String responseData = http.getString(); // Get the response data as a string
StaticJsonDocument<64> doc;
DeserializationError error = deserializeJson(doc, responseData); // Parse JSON data
if (error) { // Check if parsing was successful
debug("deserializeJson() failed: ");
debugln(error.c_str());
http.end(); // Close HTTP connection
return false;
}
const char* datetime = doc["datetime"]; // Extract "datetime" field from JSON
strncpy(date_str, datetime, 10);
date_str[10] = '\0'; // Ensure null termination
debug("Extracted date: ");
debugln(date_str);
year = atoi(datetime); // Extract year
month = atoi(datetime + 5); // Extract month
day = atoi(datetime + 8); // Extract day
hour = atoi(datetime + 11); // Extract hour
minute = atoi(datetime + 14); // Extract minute
debug("Year: ");
debugln(year);
debug("Month: ");
debugln(month);
debug("Day: ");
debugln(day);
debug("H: ");
debugln(hour);
debug("M: ");
debugln(minute);
http.end(); // Close HTTP connection
return true; // Return true if successful
} else {
debugF("[HTTP] GET... code: %d\n", httpResponseCode); // Print the HTTP response code if not 200
}
} else {
debugF("[HTTP] GET... failed, error: %s\n", http.errorToString(httpResponseCode).c_str()); // Print the error message if request failed
}
http.end(); // Close HTTP connection
return false;
}
bool fetchWeather() {
HTTPClient http;
#if defined(ESP8266)
WiFiClient client; //ESP8266
http.begin(client, WEATHER_API_URL); //ESP8266
#endif
#if defined(ESP32)
http.begin(WEATHER_API_URL); //ESP32
#endif
int httpResponseCode = http.GET(); // Send GET request
if (httpResponseCode > 0) { // Check if the request was successful
if (httpResponseCode == HTTP_CODE_OK) { // Check if the response code is 200 (OK)
String responseData = http.getString(); // Get the response data as a string
DynamicJsonDocument doc(3072);
DeserializationError error = deserializeJson(doc, responseData);
if (error) {
Serial.print("deserializeJson() failed: ");
Serial.println(error.c_str());
http.end(); // Close HTTP connection
return false;
}
int queryCost = doc["queryCost"]; // 1
float latitude = doc["latitude"]; // 33.8813
float longitude = doc["longitude"]; // 10.0981
const char* resolvedAddress = doc["resolvedAddress"]; // "قابس, تونس"
const char* address = doc["address"]; // "Gabes"
timezone_weatherAPI = doc["timezone"]; // "Africa/Tunis"
int tzoffset = doc["tzoffset"]; // 1
for (JsonObject day : doc["days"].as<JsonArray>()) {
day_datetime = day["datetime"]; // "2024-06-12", "2024-06-13", "2024-06-14", "2024-06-15", ...
day_tempmax = day["tempmax"]; // 27.6, 27.9, 27.1, 33.7, 30.5, 30.8, 31.7, 31.3, 32.7, 28.7, 28.8, ...
day_tempmin = day["tempmin"]; // 23.8, 23.3, 22.8, 21.5, 24.6, 23.6, 24.6, 25.2, 25.3, 24.3, 23.5, ...
day_temp = day["temp"]; // 25.6, 25.1, 24.8, 27.6, 27.5, 27.1, 28.4, 28.2, 29.4, 27.1, 26, 27, ...
day_humidity = day["humidity"]; // 72.4, 62.4, 57.9, 47.7, 53.2, 55.5, 52.2, 55.7, 48.5, 60.4, ...
day_precip = day["precip"]; // 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
day_precipprob = day["precipprob"]; // 16.1, 16.1, 3.2, 0, 3.2, 0, 0, 3.2, 3.2, 0, 0, 0, 3.2, 3.2, ...
day_windspeed = day["windspeed"]; // 22, 28.4, 23.4, 19.1, 22.3, 23.8, 20.9, 23.4, 30.6, 30.2, ...
/* Print all week weather
debugln("-----------------------------");
debugln("DateTime: " + String(day_datetime));
debugF("Temperature Max: %d°C\n", day_tempmax);
debugF("Temperature Min: %d°C\n", day_tempmin);
debugF("Temperature: %d°C\n", day_temp);
debugF("Humidity: %d%%\n", day_humidity);
debugF("Precipitation: %d mm\n", day_precip);
debugF("Precipitation Probability: %d%%\n", day_precipprob);
debugF("Wind Speed: %d m/s\n", day_windspeed);
debugln("-----------------------------");
*/
if (String(day_datetime) == String(date_str)) { // Print todays weather only
debugln("-----------------------------");
debugln("DateTime: " + String(day_datetime));
debugF("Temperature Max: %d°C\n", day_tempmax);
debugF("Temperature Min: %d°C\n", day_tempmin);
debugF("Temperature: %d°C\n", day_temp);
debugF("Humidity: %d%%\n", day_humidity);
debugF("Precipitation: %d mm\n", day_precip);
debugF("Precipitation Probability: %d%%\n", day_precipprob);
debugF("Wind Speed: %d m/s\n", day_windspeed);
debugln("-----------------------------");
} else {
debug("TimeZone : ");
debugln(timezone_weatherAPI);
debugln("-----------------------------");
TIMEZONE = timezone_weatherAPI;
http.end(); // Close HTTP connection
return true; //prevent printing multiple times
}
}
} else {
debugF("[HTTP] GET... code: %d\n", httpResponseCode); // Print the HTTP response code if not 200
}
} else {
debugF("[HTTP] GET... failed, error: %s\n", http.errorToString(httpResponseCode).c_str()); // Print the error message if request failed
}
http.end(); // Close HTTP connection
return false;
}
d(); // Close HTTP connection
return false;
}
int queryCost = doc["queryCost"]; // 1
float latitude = doc["latitude"]; // 33.8813
float longitude = doc["longitude"]; // 10.0981
const char* resolvedAddress = doc["resolvedAddress"]; // "قابس, تونس"
const char* address = doc["address"]; // "Gabes"
timezone_weatherAPI = doc["timezone"]; // "Africa/Tunis"
int tzoffset = doc["tzoffset"]; // 1
for (JsonObject day : doc["days"].as<JsonArray>()) {
day_datetime = day["datetime"]; // "2024-06-12", "2024-06-13", "2024-06-14", "2024-06-15", ...
day_tempmax = day["tempmax"]; // 27.6, 27.9, 27.1, 33.7, 30.5, 30.8, 31.7, 31.3, 32.7, 28.7, 28.8, ...
day_tempmin = day["tempmin"]; // 23.8, 23.3, 22.8, 21.5, 24.6, 23.6, 24.6, 25.2, 25.3, 24.3, 23.5, ...
day_temp = day["temp"]; // 25.6, 25.1, 24.8, 27.6, 27.5, 27.1, 28.4, 28.2, 29.4, 27.1, 26, 27, ...
day_humidity = day["humidity"]; // 72.4, 62.4, 57.9, 47.7, 53.2, 55.5, 52.2, 55.7, 48.5, 60.4, ...
day_precip = day["precip"]; // 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
day_precipprob = day["precipprob"]; // 16.1, 16.1, 3.2, 0, 3.2, 0, 0, 3.2, 3.2, 0, 0, 0, 3.2, 3.2, ...
day_windspeed = day["windspeed"]; // 22, 28.4, 23.4, 19.1, 22.3, 23.8, 20.9, 23.4, 30.6, 30.2, ...
/* Print all week weather
debugln("-----------------------------");
debugln("DateTime: " + String(day_datetime));
debugF("Temperature Max: %d°C\n", day_tempmax);
debugF("Temperature Min: %d°C\n", day_tempmin);
debugF("Temperature: %d°C\n", day_temp);
debugF("Humidity: %d%%\n", day_humidity);
debugF("Precipitation: %d mm\n", day_precip);
debugF("Precipitation Probability: %d%%\n", day_precipprob);
debugF("Wind Speed: %d m/s\n", day_windspeed);
debugln("-----------------------------");
*/
if (String(day_datetime) == String(date_str)) { // Print todays weather only
debugln("-----------------------------");
debugln("DateTime: " + String(day_datetime));
debugF("Temperature Max: %d°C\n", day_tempmax);
debugF("Temperature Min: %d°C\n", day_tempmin);
debugF("Temperature: %d°C\n", day_temp);
debugF("Humidity: %d%%\n", day_humidity);
debugF("Precipitation: %d mm\n", day_precip);
debugF("Precipitation Probability: %d%%\n", day_precipprob);
debugF("Wind Speed: %d m/s\n", day_windspeed);
debugln("-----------------------------");
} else {
debug("TimeZone : ");
debugln(timezone_weatherAPI);
debugln("-----------------------------");
TIMEZONE = timezone_weatherAPI;
http.end(); // Close HTTP connection
return true; //prevent printing multiple times
}
}
} else {
debugF("[HTTP] GET... code: %d\n", httpResponseCode); // Print the HTTP response code if not 200
}
} else {
debugF("[HTTP] GET... failed, error: %s\n", http.errorToString(httpResponseCode).c_str()); // Print the error message if request failed
}
http.end(); // Close HTTP connection
return false;
}