#include <WiFi.h>
#include <HTTPClient.h>
#include <JsonListener.h>
#include "SSD1306.h"
#include "OLEDDisplayUi.h"
#include "Wire.h"
#include "OpenWeatherMapForecast.h"
#include "OpenWeatherMapCurrent.h"
#include "WeatherStationFonts.h"
#include "WeatherStationImages.h"
#include "simbol.h"
#include "DHT.h"
#include <NTPClient.h>
#include <WiFiUdp.h>
#define DHTPIN 2 // Digital pin connected to the DHT sensor
#define DHTTYPE DHT22 // DHT 22 (AM2302)
DHT dht(DHTPIN, DHTTYPE);
//DynamicJsonDocument doc(1024);
const char* ssid = "Wokwi-GUEST";
const char* password = "";
String url = "http://api.openweathermap.org/data/2.5/weather?id=1650357&units=metric&appid=4609400bf25782a2cfa87e43d25c8bcf";
const char* apiKey = "4609400bf25782a2cfa87e43d25c8bcf";
const char* cityId = "1650357";
const char* units = "metric";
// Prototipe fungsi-fungsi
void drawHeaderOverlay(OLEDDisplay* display, OLEDDisplayUiState* state);
void drawTempHum(OLEDDisplay* display, OLEDDisplayUiState* state, int16_t x, int16_t y);
//void drawHum(OLEDDisplay* display, OLEDDisplayUiState* state, int16_t x, int16_t y);
void displayTimeDate(OLEDDisplay* display, OLEDDisplayUiState* state, int16_t x, int16_t y);
void getWeatherData(OLEDDisplay* display, OLEDDisplayUiState* state, int16_t x, int16_t y);
void getData();
void drawForecast(OLEDDisplay *display, OLEDDisplayUiState* state, int16_t x, int16_t y);
void drawForecastDetails(OLEDDisplay *display, int x, int y, int dayIndex);
const int I2C_DISPLAY_ADDRESS = 0x3c;
#define SDA_PIN 18 // Ganti dengan pin GPIO yang Anda inginkan
#define SCL_PIN 19 // Ganti dengan pin GPIO yang Anda inginkan
SSD1306Wire display(I2C_DISPLAY_ADDRESS, SDA_PIN, SCL_PIN);
OLEDDisplayUi ui(&display);
const long utcOffsetInSeconds = 25200; //UTC offset in seconds
WiFiUDP ntpUDP;
NTPClient timeClient(ntpUDP, "pool.ntp.org", utcOffsetInSeconds);
char days[7][12] = {"SUN", "MON", "TUE", "WED", "THU", "FRI", "SAT"};
char month[12][4] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
const boolean IS_METRIC = true;
const uint8_t MAX_FORECASTS = 4;
OpenWeatherMapCurrentData currentWeather;
OpenWeatherMapCurrent currentWeatherClient;
OpenWeatherMapForecastData forecasts[MAX_FORECASTS];
OpenWeatherMapForecast forecastClient;
bool readyForWeatherUpdate = false;
time_t now;
void drawHeaderOverlay(OLEDDisplay* display, OLEDDisplayUiState* state) {
timeClient.update();
//display->setColor(WHITE);
display->setFont(ArialMT_Plain_10);
display->setTextAlignment(TEXT_ALIGN_LEFT);
int hours = timeClient.getHours();
int minutes = timeClient.getMinutes();
String jam = hours < 10 ? "0" + String(hours) : String(hours);
String menit = minutes < 10 ? "0" + String(minutes) : String(minutes);
String feels = String (currentWeather.feelsLike, 1) + ("°C");
display->drawString(1, 53, String (jam+ ":" +menit ));
display->setFont(ArialMT_Plain_10);
display->setTextAlignment(TEXT_ALIGN_RIGHT);
display->drawString(127, 53, feels);
display->drawRect(0, 0, 128, 55);
display->drawRect(0, 0, 128, 64);
}
OverlayCallback overlays[] = { drawHeaderOverlay };
int numberOfOverlays = 1;
FrameCallback frames[] = { displayTimeDate, drawTempHum, getWeatherData, drawForecast };
int numberOfFrames = 4;
const int buzzer = 14; //buzzer to arduino pin 9
void setup() {
Serial.begin(115200);
Wire.begin(SDA_PIN, SCL_PIN); // Konfigurasi pin SDA dan SCL
dht.begin();
display.init();
pinMode(buzzer, OUTPUT); // Set buzzer - pin 9 as an output
WiFi.begin(ssid, password);
int counter = 0;
while (WiFi.status() != WL_CONNECTED) {
display.setFont(ArialMT_Plain_10);
display.setTextAlignment(TEXT_ALIGN_CENTER);
delay(500);
Serial.print(".");
display.clear();
display.drawString(64, 10, "Connecting to WiFi");
display.drawXbm(46, 30, 8, 8, counter % 3 == 0 ? activeSymbole : inactiveSymbole);
display.drawXbm(60, 30, 8, 8, counter % 3 == 1 ? activeSymbole : inactiveSymbole);
display.drawXbm(74, 30, 8, 8, counter % 3 == 2 ? activeSymbole : inactiveSymbole);
display.display(); // Ganti display->display(); menjadi display.display();
counter++;
}
Serial.println("Connected to the WiFi network");
ui.setTargetFPS(30);
ui.setActiveSymbol(activeSymbole);
ui.setInactiveSymbol(inactiveSymbole);
ui.setIndicatorPosition(BOTTOM);
ui.setIndicatorDirection(LEFT_RIGHT);
ui.setFrameAnimation(SLIDE_LEFT);
ui.setFrames(frames, numberOfFrames);
ui.setOverlays(overlays, numberOfOverlays);
ui.init();
display.display();
timeClient.begin();
timeClient.update();
getData();
}
unsigned long previousMillis = 0;
const unsigned long interval = 10 * 60 * 1000; // 10 menit dalam milisekon
void loop() {
unsigned long currentMillis = millis();
if (currentMillis - previousMillis >= interval) {
readyForWeatherUpdate = true;
previousMillis = currentMillis;
}
if (readyForWeatherUpdate && ui.getUiState()->frameState == FIXED) {
getData();
Serial.println("ambil data dari getData()");
}
int remainingTimeBudget = ui.update();
if (remainingTimeBudget > 0) {
delay(remainingTimeBudget);
}
}
void drawTempHum(OLEDDisplay* display, OLEDDisplayUiState* state, int16_t x, int16_t y) {
float t = dht.readTemperature();
float h = dht.readHumidity();
display->setTextAlignment(TEXT_ALIGN_CENTER);
display->drawXbm(23 + x, 10 + y, 16, 16, temperature_icon);
display->drawXbm(23 + x, 36 + y, 16, 16, humidity_icon);
display->setTextAlignment(TEXT_ALIGN_LEFT);
display->setFont(ArialMT_Plain_10);
display->drawString(40 + x, 0 + y, "Temperature");
display->drawString(40 + x, 26 + y, "Humidity");
display->setTextAlignment(TEXT_ALIGN_LEFT);
display->setFont(ArialMT_Plain_16);
display->drawString(40 + x, 10 + y, String(t) + ("°C"));
display->drawString(40 + x, 36 + y, String(h) + ("%"));
}
void displayTimeDate(OLEDDisplay* display, OLEDDisplayUiState* state, int16_t x, int16_t y)
{
timeClient.update();
unsigned long epochTime = timeClient.getEpochTime();
struct tm *ptm = gmtime((time_t *)&epochTime);
int date = ptm->tm_mday;
int monthNum = ptm->tm_mon + 1;
int year = ptm->tm_year + 1900;
//String day = days[date]; // Anda perlu memiliki array 'days' yang sesuai untuk nama hari.
String day = days[ptm->tm_wday];
int hour = (ptm->tm_hour % 12 == 0) ? 12 : ptm->tm_hour % 12;
String meridiem = (ptm->tm_hour >= 12) ? "PM" : "AM";
int minute = ptm->tm_min;
int second = ptm->tm_sec;
String time = String(hour) + ":" + (minute < 10 ? "0" : "") + String(minute) + ":" + String(second) +" " + meridiem;
String dateStr = String(day) + "," + String(date) + String(month[monthNum-1]) + String(year);
display->setTextAlignment(TEXT_ALIGN_LEFT);
// display->setTextAlignment(TEXT_ALIGN_CENTER);
display->setFont(ArialMT_Plain_16);
display->drawString(16 + x, 9 + y, String(day));
display->setTextAlignment(TEXT_ALIGN_CENTER);
display->setFont(ArialMT_Plain_24);
display->drawString(75 + x, 5 + y, String((date < 10 ? "0" : "") + String(date)));
display->setTextAlignment(TEXT_ALIGN_LEFT);
display->setFont(ArialMT_Plain_10);
display->drawString(95 + x, 7 + y, String(month[monthNum-1]));
display->drawString(95 + x, 17 + y, String(year));
display->setTextAlignment(TEXT_ALIGN_LEFT);
display->setFont(ArialMT_Plain_24);
display->drawString(16 + x, 25 + y, String((hour < 10 ? "0" : "") + String(hour) + " : " + (minute < 10 ? "0" : "") + String(minute)));
display->setFont(ArialMT_Plain_10);
display->setTextAlignment(TEXT_ALIGN_LEFT);
display->drawString(95 + x, 27 + y, String((second < 10 ? "0" : "") + String(second)));
display->drawString(95 + x, 37 + y, String(meridiem));
}
void getWeatherData(OLEDDisplay* display, OLEDDisplayUiState* state, int16_t x, int16_t y) {
String temp = String(currentWeather.temp,1) + (IS_METRIC ? "°C" : "°F");
String humidity = String(currentWeather.humidity) + ("%");
String feels = String (currentWeather.feelsLike, 1) + ("°C");
display->setTextAlignment(TEXT_ALIGN_LEFT);
display->setFont(ArialMT_Plain_10);
display->drawString(47 + x, 0 + y, currentWeather.description);
display->drawString(47 + x, 10 + y, currentWeather.main +(" ")+ currentWeather.clouds +("%"));
display->drawString(47 + x, 40 + y, humidity + " I " + feels ) ;
display->setFont(ArialMT_Plain_24);
display->drawString(47 + x, 19 + y,temp);
display->setFont(Meteocons_Plain_36);
display->setTextAlignment(TEXT_ALIGN_LEFT);
display->drawString(5 + x, 0 + y, currentWeather.iconMeteoCon);
}
void getData(){
tone(buzzer, 1000); // Send 1KHz sound signal...
delay(10); // ...for 1 sec
noTone(buzzer); // Stop sound...
currentWeatherClient.setMetric(IS_METRIC);
currentWeatherClient.updateCurrentById(¤tWeather, apiKey, cityId);
forecastClient.setMetric(IS_METRIC);
uint8_t allowedHours[] = {12};
forecastClient.setAllowedHours(allowedHours, sizeof(allowedHours));
forecastClient.updateForecastsById(forecasts, apiKey, cityId, MAX_FORECASTS);
readyForWeatherUpdate = false;
Serial.println(timeClient.getFormattedTime());
Serial.println("update data");
}
void drawForecast(OLEDDisplay *display, OLEDDisplayUiState* state, int16_t x, int16_t y)
{
drawForecastDetails(display, x, y, 0);
drawForecastDetails(display, x + 44, y, 1);
drawForecastDetails(display, x + 88, y, 2);
}
void drawForecastDetails(OLEDDisplay *display, int x, int y, int dayIndex)
{
time_t observationTimestamp = forecasts[dayIndex].observationTime;
struct tm* timeInfo;
timeInfo = localtime(&observationTimestamp);
display->setTextAlignment(TEXT_ALIGN_CENTER);
display->setFont(ArialMT_Plain_10);
display->drawString(x + 20, y, days[timeInfo->tm_wday]);
display->setFont(Meteocons_Plain_21);
display->drawString(x + 20, y + 12, forecasts[dayIndex].iconMeteoCon);
String temp = String(forecasts[dayIndex].temp, 0) + (IS_METRIC ? "°C" : "°F");
display->setFont(ArialMT_Plain_10);
display->drawString(x + 20, y + 34, temp);
display->setTextAlignment(TEXT_ALIGN_LEFT);
}