rhandycan1

ESP32 + ST7796S WEATHER DASHBOARD

Oct 4th, 2025
294
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 7.76 KB | None | 0 0
  1. //PROGRAM BY: RANDY N. CAÑADA
  2. /*SUBSCRIBE: FB PAGES:
  3. Playlist.ph:
  4. https://www.facebook.com/profile.php?id=61573177858722
  5.  
  6. Arduino:
  7. https://www.facebook.com/rhacan2?__tn__=R
  8.  
  9. YOUTUBE:
  10. PlaylistPH
  11. https://www.youtube.com/@PlaylistPh-h2v
  12.  
  13. ElectroCrafters:
  14. https://www.youtube.com/channel/UCu1aqzUdTqSnTN5BHNlbVgA */
  15.  
  16.  
  17. #include <WiFi.h>
  18. #include <HTTPClient.h>
  19. #include <Adafruit_GFX.h>
  20. #include <Adafruit_ST7796S_kbv.h>
  21. #include <SPI.h>
  22. #include <ArduinoJson.h>
  23. #include <time.h>
  24. #include <math.h>
  25.  
  26. // ------------------- TFT Pins -------------------
  27. #define TFT_CS   5
  28. #define TFT_DC   2
  29. #define TFT_RST  4
  30. #define TFT_BL   15
  31. #define REFRESH_BUTTON 13
  32.  
  33. Adafruit_ST7796S_kbv tft = Adafruit_ST7796S_kbv(TFT_CS, TFT_DC, TFT_RST);
  34.  
  35. // ------------------- Colors -------------------
  36. #define COLOR_BG        0x0000
  37. #define COLOR_CLOCK     0x7D7C
  38. #define COLOR_LOCATION  0xA89F
  39. #define COLOR_SUNDAY    0xF800
  40. #define COLOR_WEEKDAY   0x001F
  41. #define COLOR_TEMP      0xFD20
  42. #define COLOR_CONDITION 0xFFE0
  43. #define COLOR_SUNRISE   0xFFE0
  44. #define COLOR_DATE      0xFFFF
  45. #define COLOR_ERROR     0xF800
  46.  
  47. // ------------------- WiFi & API -------------------
  48. const char* ssid = "YOUR WIFI";
  49. const char* password = "YOUR PASSWORD";
  50. const char* apiKey = "YOUR API KEY";
  51. const char* apiURL = "https://api.openweathermap.org/data/2.5/forecast?lat=-39.6386&lon=176.8497&units=metric&appid=";
  52.  
  53. String locationName = "Pukahu, NZ";
  54. const char* dayNames[] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
  55.  
  56. // ------------------- Weather Icons -------------------
  57. void drawSunAnimated(int x, int y, int frame) {
  58.   int radius = 10 + frame % 3;
  59.   tft.fillCircle(x, y, radius, COLOR_SUNDAY);
  60.   for (int i = 0; i < 360; i += 45) {
  61.     float rad = i * M_PI / 180.0;
  62.     int x1 = x + cos(rad) * (14 + frame % 2);
  63.     int y1 = y + sin(rad) * (14 + frame % 2);
  64.     int x2 = x + cos(rad) * (18 + frame % 2);
  65.     int y2 = y + sin(rad) * (18 + frame % 2);
  66.     tft.drawLine(x1, y1, x2, y2, COLOR_SUNDAY);
  67.   }
  68. }
  69.  
  70. void drawCloudAnimated(int x, int y, int frame) {
  71.   int offset = frame % 3;
  72.   tft.fillRoundRect(x + offset, y, 30, 20, 10, COLOR_WEEKDAY);
  73.   tft.fillCircle(x + 10 + offset, y, 10, COLOR_WEEKDAY);
  74.   tft.fillCircle(x + 20 + offset, y, 10, COLOR_WEEKDAY);
  75. }
  76.  
  77. void drawRainAnimated(int x, int y, int frame) {
  78.   drawCloudAnimated(x, y, frame);
  79.   for (int i = 0; i < 3; i++) {
  80.     int dropY = y + 20 + (frame % 4);
  81.     tft.drawLine(x + 10 + i * 8, dropY, x + 10 + i * 8, dropY + 10, COLOR_WEEKDAY);
  82.   }
  83. }
  84.  
  85. // ------------------- Setup -------------------
  86. void setup() {
  87.   Serial.begin(115200);
  88.   SPI.begin(18, -1, 23, TFT_CS);
  89.   pinMode(TFT_BL, OUTPUT);
  90.   pinMode(REFRESH_BUTTON, INPUT_PULLUP);
  91.   digitalWrite(TFT_BL, HIGH);
  92.   tft.begin();
  93.   tft.setRotation(1);
  94.   tft.fillScreen(COLOR_BG);
  95.  
  96.   WiFi.begin(ssid, password);
  97.   while (WiFi.status() != WL_CONNECTED) {
  98.     delay(500);
  99.     Serial.print(".");
  100.   }
  101.   Serial.println("WiFi Connected!");
  102.  
  103.   configTime(0, 0, "pool.ntp.org");
  104.   setenv("TZ", "NZST-12NZDT,M9.5.0,M4.1.0/3", 1);
  105.   tzset();
  106.  
  107.   fetchForecast();
  108. }
  109.  
  110. // ------------------- Forecast Fetch -------------------
  111. void fetchForecast() {
  112.   HTTPClient http;
  113.   String fullURL = String(apiURL) + apiKey;
  114.   http.begin(fullURL);
  115.   int httpCode = http.GET();
  116.  
  117.   if (httpCode == 200) {
  118.     String payload = http.getString();
  119.     DynamicJsonDocument doc(16384);
  120.     DeserializationError error = deserializeJson(doc, payload);
  121.     if (error) {
  122.       tft.setCursor(20, 20);
  123.       tft.setTextColor(COLOR_ERROR);
  124.       tft.setTextSize(2);
  125.       tft.println("JSON parse error.");
  126.       return;
  127.     }
  128.  
  129.     // ---- Current Date & Time ----
  130.     time_t now;
  131.     time(&now);
  132.     struct tm timeinfo;
  133.     localtime_r(&now, &timeinfo);
  134.  
  135.     char dateStr[32];
  136.     strftime(dateStr, sizeof(dateStr), "%A %d %b %Y", &timeinfo);
  137.  
  138.     tft.fillScreen(COLOR_BG);
  139.     tft.setTextColor(COLOR_DATE);
  140.     tft.setTextSize(2);
  141.     tft.setCursor(20, 20);
  142.     tft.print(dateStr);
  143.     tft.setTextColor(COLOR_LOCATION);
  144.     tft.print(", ");
  145.     tft.println(locationName);
  146.  
  147.     char timeStr[16];
  148.     strftime(timeStr, sizeof(timeStr), "%I:%M:%S %p", &timeinfo);
  149.     tft.setTextColor(COLOR_CLOCK);
  150.     tft.setTextSize(4);
  151.     tft.setCursor(20, 50);
  152.     tft.println(timeStr);
  153.  
  154.     // ---- Current Temp & Condition ----
  155.     float currentTemp = doc["list"][0]["main"]["temp"];
  156.     String currentCond = doc["list"][0]["weather"][0]["description"].as<String>();
  157.  
  158.     tft.setTextSize(2);
  159.     tft.setTextColor(COLOR_DATE);
  160.     tft.setCursor(20, 100);
  161.     tft.printf("Now: %.1fC", currentTemp);
  162.  
  163.     tft.setTextColor(COLOR_CONDITION);
  164.     tft.setCursor(160, 100);
  165.     tft.println(currentCond);
  166.  
  167.     // ---- Sunrise & Sunset ----
  168.     time_t sunriseUTC = doc["city"]["sunrise"].as<long>();
  169.     time_t sunsetUTC  = doc["city"]["sunset"].as<long>();
  170.  
  171.     struct tm suninfo, setinfo;
  172.     localtime_r(&sunriseUTC, &suninfo);
  173.     localtime_r(&sunsetUTC, &setinfo);
  174.  
  175.     char sunStr[16], setStr[16];
  176.     strftime(sunStr, sizeof(sunStr), "%I:%M %p", &suninfo);
  177.     strftime(setStr, sizeof(setStr), "%I:%M %p", &setinfo);
  178.  
  179.     // ---- Debug ----
  180.     Serial.printf("Sunrise Local: %s\n", sunStr);
  181.     Serial.printf("Sunset  Local: %s\n", setStr);
  182.  
  183.     // ---- Print Sunrise & Sunset ----
  184.     tft.setTextColor(COLOR_DATE);
  185.     tft.setCursor(20, 125);
  186.     tft.print("Sunrise: ");
  187.     tft.setTextColor(COLOR_SUNRISE);
  188.     tft.println(sunStr);
  189.  
  190.     tft.setTextColor(COLOR_DATE);
  191.     tft.setCursor(220, 125);
  192.     tft.print("  Sunset: "); // two spaces
  193.     tft.setTextColor(COLOR_SUNRISE);
  194.     tft.println(setStr);
  195.  
  196.     // ---- Forecast Midday ----
  197.     tft.drawLine(20, 145, 460, 145, COLOR_DATE);
  198.     int found = 0;
  199.     int frame = millis() / 500;
  200.     for (JsonObject entry : doc["list"].as<JsonArray>()) {
  201.       const char* dt_txt = entry["dt_txt"];
  202.       if (strstr(dt_txt, "12:00:00")) {
  203.         time_t dt = entry["dt"];
  204.         struct tm dayinfo;
  205.         localtime_r(&dt, &dayinfo);
  206.  
  207.         int x = 20 + found * 90;
  208.         if (dayNames[dayinfo.tm_wday] == String("Sun")) {
  209.           tft.setTextColor(COLOR_SUNDAY);
  210.         } else {
  211.           tft.setTextColor(COLOR_WEEKDAY);
  212.         }
  213.  
  214.         tft.setTextSize(2);
  215.         tft.setCursor(x, 160);
  216.         tft.println(dayNames[dayinfo.tm_wday]);
  217.  
  218.         float temp = entry["main"]["temp"];
  219.         tft.setTextColor(COLOR_TEMP);
  220.         tft.setCursor(x, 185);
  221.         tft.printf("%.0fC", temp);
  222.  
  223.         String condition = entry["weather"][0]["main"].as<String>();
  224.         if (condition == "Clear") drawSunAnimated(x + 30, 220, frame);
  225.         else if (condition == "Rain") drawRainAnimated(x + 20, 220, frame);
  226.         else drawCloudAnimated(x + 20, 220, frame);
  227.  
  228.         found++;
  229.         if (found >= 5) break;
  230.       }
  231.     }
  232.  
  233.     if (found == 0) {
  234.       tft.setCursor(20, 160);
  235.       tft.setTextColor(COLOR_ERROR);
  236.       tft.setTextSize(2);
  237.       tft.println("No midday forecasts found.");
  238.     }
  239.  
  240.   } else {
  241.     tft.setCursor(20, 20);
  242.     tft.setTextColor(COLOR_ERROR);
  243.     tft.setTextSize(2);
  244.     tft.println("Failed to fetch forecast.");
  245.   }
  246.  
  247.   http.end();
  248. }
  249.  
  250. // ------------------- Loop -------------------
  251. // ------------------- Loop -------------------
  252. void loop() {
  253.   static bool buttonPressed = false;
  254.   static unsigned long lastDebounceTime = 0;
  255.   const unsigned long debounceDelay = 50; // 50ms debounce
  256.  
  257.   int reading = digitalRead(REFRESH_BUTTON);
  258.  
  259.   // If button state changes
  260.   if (reading == LOW && !buttonPressed && (millis() - lastDebounceTime > debounceDelay)) {
  261.     buttonPressed = true;
  262.     lastDebounceTime = millis();
  263.  
  264.     Serial.println("Manual refresh triggered!");
  265.     fetchForecast();
  266.   }
  267.  
  268.   // Reset latch when released
  269.   if (reading == HIGH) {
  270.     buttonPressed = false;
  271.   }
  272. }
  273.  
Tags: ESP32 ST7796S
Advertisement
Add Comment
Please, Sign In to add comment