#include <WiFi.h>
#include <HTTPClient.h>
#include <Arduino_JSON.h>
#include "time.h"
#include "sntp.h"
#include <LiquidCrystal_I2C.h>
//0x3F or 0x27
LiquidCrystal_I2C lcd(0x27, 16, 2); //LCD Object
// Set your own API here
constexpr char OPENWEATHERMAPAPI[] = "###";
// Set your country code and city name here
constexpr char CITY[] = "Sibiu";
constexpr char COUNTRYCODE[] = "RO";
String serverPath = String("http://api.openweathermap.org/data/2.5/weather?q=") + CITY + "," + COUNTRYCODE + "&units=metric&APPID=" + OPENWEATHERMAPAPI;
const char* ssid = "Wokwi-GUEST";
const char* password = "";
const char* ntpServer1 = "pool.ntp.org";
const char* ntpServer2 = "time.nist.gov";
const long gmtOffset_sec = 7200;
const int daylightOffset_sec = 3600;
// Timer set to 1 hr in ms -> That should read 60 min x 60 sec x 1000 = 3600000
constexpr unsigned long TIMERDELAY = 900000; // This is actually 15 minutes, 15x60=900s=900000ms
char *daynames[7] = {
"Duminica",
" Luni",
" Marti",
"Miercuri",
" Joi",
" Vineri",
" Sambata"
}; // put them in this order as tm struct 0 = Sunday
//*****************************************************************************************************
byte degree[8] = {
0B00111,
0B00101,
0B00111,
0B00000,
0B00000,
0B00000,
0B00000,
0B00000
}; // create degree sign
struct weatherData {
String temp;
String pressure;
String humidity;
String windspeed;
} weather;
enum State {
MOVE_CHAR_IN,
MOVE_CHAR_OUT,
WAIT_BETWEEN_CHARS,
HOLD_DISPLAY,
WAIT_BETWEEN_CHARS_OUT,
HOLD_DISPLAY_OUT
};
State currentState = MOVE_CHAR_IN;
int i, j;
unsigned long previousMillis = 0;
unsigned long timeMillis = 0;
const long interval = 50;
const long holdInterval = 5000;
struct tm timeinfo;
//*****************************************************************************************************
void setup()
{
Serial.begin(115200);
lcd.init();
lcd.backlight();
lcd.createChar(0, degree);
Serial.println(serverPath);
// sntp_set_time_sync_notification_cb( onTimeAvailable );
sntp_servermode_dhcp(1); // (optional)
configTime(gmtOffset_sec, daylightOffset_sec, ntpServer1, ntpServer2);
Serial.printf("Connecting to %s ", ssid);
lcd.clear();
lcd.print("Connecting to ");
lcd.setCursor(0, 1);
lcd.print(ssid);
delay(1000);
//WiFi.begin(ssid, password, 6); // Added channel 6 to decrease connection time on WOKWI
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.print("IP address: ");
Serial.println(WiFi.localIP());
Serial.println(" CONNECTED");
lcd.clear();
lcd.print("CONNECTED IP =");
lcd.setCursor(0, 1);
lcd.print(WiFi.localIP());
delay(2000);
lcd.clear();
struct tm timeinfo;
if (getLocalTime(&timeinfo)) {
i = strlen(daynames[timeinfo.tm_wday]) - 1;
j = 0;
}
} // setup
void loop()
{
displayDataOnLCD();
readWeatherData();
unsigned long Millis = millis();
if (Millis - timeMillis >= 100) {
timeMillis = Millis;
displayDay(timeinfo.tm_wday);
}
} // loop
void readWeatherData() {
static boolean firstTime = true;
static unsigned long lastTime = 0;
String jsonBuffer;
JSONVar myObject = JSONVar();
if (((millis() - lastTime) > TIMERDELAY) || firstTime)
{
// Check WiFi connection status
if (WiFi.status() == WL_CONNECTED)
{
jsonBuffer = httpGETRequest(serverPath.c_str());
Serial.println(jsonBuffer);
myObject = JSON.parse(jsonBuffer);
// JSON.typeof(jsonVar) can be used to get the type of the var
if (JSON.typeof(myObject) == "undefined")
{
Serial.println("Parsing input failed!");
return;
}
weather.temp = JSON.stringify(myObject["main"]["temp"]);
weather.pressure = JSON.stringify(myObject["main"]["pressure"]);
weather.humidity = JSON.stringify(myObject["main"]["humidity"]);
weather.windspeed = JSON.stringify(myObject["wind"]["speed"]);
// Dump to serial
Serial.print("JSON object = ");
Serial.println(myObject);
Serial.print("Temperature: ");
Serial.println(weather.temp);
Serial.print("Pressure: ");
Serial.println(weather.pressure);
Serial.print("Humidity: ");
Serial.println(weather.humidity);
Serial.print("Wind Speed: ");
Serial.println(weather.windspeed);
}
else
{
Serial.println("WiFi Disconnected");
}
lastTime = millis();
firstTime = false;
}
}
void displayDay(int dayIndex) {
unsigned long currentMillis = millis();
switch (currentState) {
case MOVE_CHAR_IN:
if (currentMillis - previousMillis >= interval) {
previousMillis = currentMillis;
lcd.setCursor(j, 1);
lcd.print(daynames[dayIndex][i]);
Serial.print(j);
Serial.print("--");
Serial.print(i);
Serial.print("--");
Serial.println(daynames[dayIndex][i]);
Serial.println("******************");
if (j > 0) {
lcd.setCursor(j - 1, 1);
lcd.print(' ');
}
j++;
if (j == i + 2) {
currentState = WAIT_BETWEEN_CHARS;
}
}
break;
case WAIT_BETWEEN_CHARS:
if (currentMillis - previousMillis >= interval) {
i--;
j = 0;
previousMillis = currentMillis;
if (i < 0) {
currentState = HOLD_DISPLAY;
} else {
currentState = MOVE_CHAR_IN;
}
}
break;
case WAIT_BETWEEN_CHARS_OUT:
if (currentMillis - previousMillis >= interval) {
previousMillis = currentMillis;
i--;
j = i + 1; // offset on screen
if (i < 0) {
currentState = HOLD_DISPLAY_OUT;
} else {
currentState = MOVE_CHAR_OUT;
}
}
break;
case HOLD_DISPLAY:
if (currentMillis - previousMillis >= holdInterval) {
i = strlen(daynames[dayIndex]) - 1; // Setup for chars out
j = i + 1; // offset on screen
previousMillis = currentMillis;
// lcd.clear();
currentState = MOVE_CHAR_OUT;
}
break;
case HOLD_DISPLAY_OUT:
if (currentMillis - previousMillis >= 2000) {
i = strlen(daynames[dayIndex]) - 1; // Setup for chars out
j = 0; // start from first LCD position
previousMillis = currentMillis;
currentState = MOVE_CHAR_IN;
}
break;
case MOVE_CHAR_OUT:
if (currentMillis - previousMillis >= interval) {
previousMillis = currentMillis;
lcd.setCursor(j, 1);
lcd.print(' ');
lcd.setCursor(j + 1, 1);
lcd.print(daynames[dayIndex][i]);
j++;
if (j >= 16)
currentState = WAIT_BETWEEN_CHARS_OUT;
}
break;
}
} // displayDay()
void printLocalTime()
{
static boolean timeAvailable = true;
//struct tm timeinfo;
if (!getLocalTime(&timeinfo)) {
if (timeAvailable) {
lcd.clear();
Serial.println("No time available (yet)");
lcd.print("Time not");
lcd.setCursor(0, 1);
lcd.print("available (yet)");
}
timeAvailable = false;
return;
}
if (!timeAvailable) {
lcd.clear(); // Clear LCD when time becomes available (again)
}
timeAvailable = true;
lcd.setCursor(0, 0);
lcd.print(&timeinfo, "%H:%M:%S"); // 10:30:05
Serial.println(&timeinfo, "%H:%M:%S");
// Display Day
Serial.print("Day : ");
Serial.println(daynames[timeinfo.tm_wday]);
lcd.setCursor(0, 1);
//lcd.print(daynames[timeinfo.tm_wday]);
//displayDay(timeinfo.tm_wday);
} // printLocalTime
void displayDataOnLCD() {
static uint16_t counter = 0;
static unsigned long startTime = 0;
byte mode;
unsigned long currentTime = millis();
if (currentTime - startTime >= 1000)
{
counter++;
// display local time for 50s
if (counter >= 60) {
counter = 0; // Reset counter when weather was displayed for 10 secs
lcd.clear(); // Clear LDC for local time display
}
if (counter < 50 ) {
mode = 0; // Display local time for 50 secs (0..49)
}
if (counter == 50) {
mode = 1; // Clear LCD and start to display weather
}
if (counter > 50) {
mode = 2; // Display weather for 10 secs (50 ... 59)
}
switch (mode) {
case 0 : // Display local time
printLocalTime();
break;
case 1: // Clear LCD first
lcd.clear();
// and directly apply case 2 -> no break; !!!!
case 2 : // Display local weather for 10s
lcd.setCursor(0, 0);
lcd.print(weather.temp);
lcd.write((byte)0);
lcd.print("C");
lcd.setCursor(0, 1);
lcd.print("in ");
lcd.print(CITY);
break;
}
startTime = currentTime;
}
}
String httpGETRequest(const char *serverName)
{
WiFiClient client;
HTTPClient http;
// Your Domain name with URL path or IP address with path
http.begin(client, serverName);
// Send HTTP POST request
int httpResponseCode = http.GET();
String payload = "{}";
// Weather API seems to return 200 if data are valid ...
if (httpResponseCode >= 200 && httpResponseCode <= 299 )
{
Serial.print("HTTP Response code: ");
Serial.println(httpResponseCode);
payload = http.getString();
}
else
{
Serial.print("Error code: ");
Serial.println(httpResponseCode);
}
// Free resources
http.end();
return payload;
}