#include #include #include #include "config.h" #include "display_ui.h" // Non-static so displayRender() can reference it via extern. Adafruit_ST7735 tft = Adafruit_ST7735(TFT_CS, TFT_DC, TFT_RST); void displayBegin() { pinMode(TFT_BLK, OUTPUT); digitalWrite(TFT_BLK, HIGH); // backlight on tft.initR(INITR_MINI160x80); // 0.96" ST7735S tft.invertDisplay(false); // this panel: no inversion tft.setRotation(3); // landscape, 160 wide x 80 tall tft.fillScreen(ST77XX_BLACK); } void displaySplash(const char* line1, const char* line2) { tft.fillScreen(ST77XX_BLACK); tft.setTextColor(ST77XX_WHITE); tft.setTextSize(2); tft.setCursor(4, 20); tft.print(line1); tft.setTextSize(1); tft.setCursor(4, 50); tft.print(line2); } // Simple 34x34 weather icon at (x, y). static void drawIcon(WxCategory c, int x, int y) { uint16_t sun = ST77XX_YELLOW; uint16_t cloud = ST77XX_WHITE; uint16_t rain = ST77XX_CYAN; switch (c) { case WX_FINE: tft.fillCircle(x + 17, y + 17, 11, sun); break; case WX_FAIR: tft.fillCircle(x + 12, y + 12, 8, sun); tft.fillRoundRect(x + 8, y + 18, 24, 12, 6, cloud); break; case WX_CHANGEABLE: tft.fillRoundRect(x + 4, y + 12, 26, 14, 7, cloud); break; case WX_RAIN: tft.fillRoundRect(x + 4, y + 8, 26, 14, 7, cloud); for (int i = 0; i < 3; i++) tft.drawFastVLine(x + 9 + i * 8, y + 24, 8, rain); break; case WX_STORM: tft.fillRoundRect(x + 4, y + 8, 26, 14, 7, cloud); tft.fillTriangle(x + 16, y + 22, x + 12, y + 32, x + 20, y + 30, ST77XX_YELLOW); break; default: tft.drawRect(x + 4, y + 10, 26, 18, cloud); // unknown / collecting break; } } // Layout for landscape 160 x 80. void displayRender(const AppState& s) { tft.fillScreen(ST77XX_BLACK); tft.setTextColor(ST77XX_WHITE); char buf[24]; // Time (large), top-left snprintf(buf, sizeof(buf), "%02u:%02u", s.now.hour, s.now.minute); tft.setTextSize(2); tft.setCursor(4, 4); tft.print(buf); // Icon, top-right drawIcon(s.haveTrend ? s.forecast.category : WX_UNKNOWN, 120, 4); // Date snprintf(buf, sizeof(buf), "%04u-%02u-%02u", s.now.year, s.now.month, s.now.day); tft.setTextSize(1); tft.setCursor(4, 26); tft.print(buf); // Pressure + trend arrow snprintf(buf, sizeof(buf), "%.0f hPa", s.mslHpa); tft.setCursor(4, 40); tft.print(buf); const char* arrow = (s.trend == TREND_RISING) ? "^" : (s.trend == TREND_FALLING ? "v" : "="); tft.setCursor(96, 40); tft.setTextColor(ST77XX_CYAN); tft.print(arrow); tft.setTextColor(ST77XX_WHITE); // Temperature snprintf(buf, sizeof(buf), "%.1f C", s.tempC); tft.setCursor(4, 52); tft.print(buf); // Forecast short label (full width, bottom row) tft.setTextColor(ST77XX_YELLOW); tft.setCursor(4, 66); tft.print(s.haveTrend ? categoryShort(s.forecast.category) : "Collecting..."); tft.setTextColor(ST77XX_WHITE); }