// // include library, include base class, make path known // #include // // select the display class to use, only one // #include // 2.13" b/w // #include // #include // #if defined(ESP32) // GxIO_Class io(SPI, /*CS=5*/ SS, /*DC=*/ 17, /*RST=*/ 16); // arbitrary selection of 17, 16 // GxEPD_Class display(io, /*RST=*/ 16, /*BUSY=*/ 4); // arbitrary selection of (16), 4 // #endif #include #include #include GxEPD2_BW display(GxEPD2_213_B73(/*CS=5*/ SS, /*DC=*/17, /*RST=*/16, /*BUSY=*/4)); // GDEH0213B73 // #include "fonts/alarm_clock32pt7b.h" // #include "fonts/alarm_clock30pt7b.h" // #include "fonts/alarm_clock16pt7b.h" #include "fonts/Pixeltype8pt7b.h" #include "fonts/Pixeltype16pt7b.h" #include "fonts/retro_computer_personal_use8pt7b.h" #include "fonts/retro_computer_personal_use16pt7b.h" #include "fonts/retro_computer_personal_use24pt7b.h" #include "images/clear_d.h" #include "images/clear_n.h" #include "images/cloud_d.h" #include "images/cloud_n.h" #include "images/cloud.h" #include "images/clouds.h" #include "images/rain_h.h" #include "images/rain_l_d.h" #include "images/rain_l_n.h" #include "images/thunder.h" #include "images/snow_d.h" #include "images/snow_n.h" #include "images/mist_d.h" #include "images/mist_n.h" #include "time.h" #include #include #include #include #include #include #include WiFiMulti WiFiMulti; struct tm timeinfo; WiFiClient client; // Allocate the JSON document // Use arduinojson.org/v6/assistant to compute the capacity. const size_t capacity = JSON_ARRAY_SIZE(1) + JSON_OBJECT_SIZE(1) + 2 * JSON_OBJECT_SIZE(2) + JSON_OBJECT_SIZE(4) + JSON_OBJECT_SIZE(5) + JSON_OBJECT_SIZE(6) + JSON_OBJECT_SIZE(13) + 280; DynamicJsonDocument weather(capacity); unsigned long t = 0; int dt = 0; int interval = 0; int refresh = 0; int update = 0; void drawTime() { getLocalTime(&timeinfo); display.setTextColor(GxEPD_BLACK); display.setFont(&retro_computer_personal_use24pt7b); display.setCursor(78, 65); display.println(&timeinfo, "%H:%M"); display.setFont(&retro_computer_personal_use16pt7b); display.setCursor(78, 95); display.println(&timeinfo, "%d"); display.setFont(&retro_computer_personal_use8pt7b); display.setCursor(132, 83); display.println(&timeinfo, "%A"); display.setCursor(132, 95); display.println(&timeinfo, "%B"); } void drawWeather() { display.fillRect(0, 0, 75, 150, GxEPD_BLACK); display.setTextColor(GxEPD_WHITE); display.setFont(&retro_computer_personal_use16pt7b); int16_t tbx, tby; uint16_t tbw, tbh; display.getTextBounds(String(weather["main"]["temp"].as()), 0, 100, &tbx, &tby, &tbw, &tbh); uint16_t x = ((75 - 10 - tbw) / 2) - tbx; display.setCursor(x, 100); display.print(weather["main"]["temp"].as()); display.setFont(&retro_computer_personal_use8pt7b); display.setCursor(60, 100); display.println("C"); display.getTextBounds(String(weather["weather"][0]["main"].as()), 0, 110, &tbx, &tby, &tbw, &tbh); x = ((75 - tbw) / 2) - tbx; display.setCursor(x, 120); display.print(weather["weather"][0]["main"].as()); const char *code = weather["weather"][0]["icon"]; if (strcmp(code, "01d") == 0) { display.drawBitmap(5, 10, clear_d, 64, 64, GxEPD_WHITE); } else if (strcmp(code, "01n") == 0) { display.drawBitmap(5, 10, clear_n, 64, 64, GxEPD_WHITE); } else if (strcmp(code, "02d") == 0) { display.drawBitmap(5, 10, cloud_d, 64, 64, GxEPD_WHITE); } else if (strcmp(code, "02n") == 0) { display.drawBitmap(5, 10, cloud_n, 64, 64, GxEPD_WHITE); } else if (strcmp(code, "03d") == 0 || strcmp(code, "03n") == 0) { display.drawBitmap(5, 10, cloud, 64, 64, GxEPD_WHITE); } else if (strcmp(code, "04d") == 0 || strcmp(code, "04n") == 0) { display.drawBitmap(5, 10, clouds, 64, 64, GxEPD_WHITE); } else if (strcmp(code, "09d") == 0 || strcmp(code, "09n") == 0) { display.drawBitmap(5, 10, rain_h, 64, 64, GxEPD_WHITE); } else if (strcmp(code, "10d") == 0) { display.drawBitmap(5, 10, rain_l_d, 64, 64, GxEPD_WHITE); } else if (strcmp(code, "10n") == 0) { display.drawBitmap(5, 10, rain_l_n, 64, 64, GxEPD_WHITE); } else if (strcmp(code, "11d") == 0 || strcmp(code, "11n") == 0) { display.drawBitmap(5, 10, thunder, 64, 64, GxEPD_WHITE); } else if (strcmp(code, "13d") == 0) { display.drawBitmap(5, 10, snow_d, 64, 64, GxEPD_WHITE); } else if (strcmp(code, "13n") == 0) { display.drawBitmap(5, 10, snow_n, 64, 64, GxEPD_WHITE); } else if (strcmp(code, "50d") == 0) { display.drawBitmap(5, 10, mist_d, 64, 64, GxEPD_WHITE); } else if (strcmp(code, "50n") == 0) { display.drawBitmap(5, 10, mist_n, 64, 64, GxEPD_WHITE); } } void updateWeather() { client.setTimeout(10000); if (!client.connect("api.openweathermap.org", 80)) { display.println(F("Connection failed")); return; } Serial.println(F("Connected!")); // Send HTTP request client.println(F("GET /data/2.5/weather?id=2745909&units=metric&APPID=eb45aa1fa0246863cfa095c79e361e3f HTTP/1.0")); client.println(F("Connection: close")); if (client.println() == 0) { display.println(F("Failed to send request")); return; } // Check HTTP status char status[32] = {0}; client.readBytesUntil('\r', status, sizeof(status)); // It should be "HTTP/1.0 200 OK" or "HTTP/1.1 200 OK" if (strcmp(status + 9, "200 OK") != 0) { display.print(F("Unexpected response: ")); display.println(status); return; } // Skip HTTP headers char endOfHeaders[] = "\r\n\r\n"; if (!client.find(endOfHeaders)) { display.println(F("Invalid response")); return; } // Parse JSON object DeserializationError error = deserializeJson(weather, client); if (error) { display.print(F("deserializeJson() failed: ")); display.println(error.c_str()); return; } } void setup() { Serial.begin(115200); display.init(115200); // enable diagnostic output on Serial delay(10); Serial.println("T5 Dashboard "__DATE__" "__TIME__); display.fillScreen(GxEPD_WHITE); display.setTextColor(GxEPD_BLACK); display.setFont(&Pixeltype8pt7b); display.setRotation(1); display.setPartialWindow(0, 0, display.width(), display.height()); display.firstPage(); display.setCursor(0, 12); display.println("TTGO T5 V2.3 ePaper"); display.println("T5 Dashboard, "__DATE__" "__TIME__); display.nextPage(); WiFiMulti.addAP("SSID", "Password"); if (int stat = WiFiMulti.run() != WL_CONNECTED) { Serial.println(WiFi.status()); display.print("WiFi connection failed"); display.println(stat); display.nextPage(); esp_deep_sleep_start(); } display.printf("WiFi connected to "); display.println(WiFi.SSID()); display.print("IP address: "); display.println(WiFi.localIP()); display.nextPage(); ArduinoOTA .onStart([]() { String type; if (ArduinoOTA.getCommand() == U_FLASH) type = "sketch"; else // U_SPIFFS type = "filesystem"; // NOTE: if updating SPIFFS this would be the place to unmount SPIFFS using SPIFFS.end() Serial.println("Start updating " + type); }) .onEnd([]() { Serial.println("\nEnd"); }) .onProgress([](unsigned int progress, unsigned int total) { Serial.printf("Progress: %u%%\r", (progress / (total / 100))); }) .onError([](ota_error_t error) { Serial.printf("Error[%u]: ", error); if (error == OTA_AUTH_ERROR) Serial.println("Auth Failed"); else if (error == OTA_BEGIN_ERROR) Serial.println("Begin Failed"); else if (error == OTA_CONNECT_ERROR) Serial.println("Connect Failed"); else if (error == OTA_RECEIVE_ERROR) Serial.println("Receive Failed"); else if (error == OTA_END_ERROR) Serial.println("End Failed"); }); ArduinoOTA.begin(); display.println("ArduinoOTA started"); display.nextPage(); interval = timeinfo.tm_sec * 1000; t = millis(); updateWeather(); delay(1000); configTime(3600, 3600, "pool.ntp.org"); display.fillScreen(GxEPD_WHITE); drawTime(); drawWeather(); display.nextPage(); } void loop() { ArduinoOTA.handle(); dt = millis() - t; t = millis(); if (interval >= 60 * 1000) { display.fillScreen(GxEPD_WHITE); if (refresh >= 5) { refresh = 0; display.setFullWindow(); display.firstPage(); display.nextPage(); display.setPartialWindow(0, 0, display.width(), display.height()); } else { refresh++; } if (update >= 15) { update = 0; updateWeather(); } else { update++; } drawTime(); drawWeather(); display.nextPage(); interval = timeinfo.tm_sec * 1000; } else { interval += dt; } }