// #include <Adafruit_GFX.h> // Core graphics library
// #include <SPI.h>
// #include <Wire.h>
// #include <Adafruit_ILI9341.h> // Hardware-specific library
// #include <Adafruit_FT6206.h>
// #include "DHT.h" // DHT sensor library
// #include <cmath> // For round() and isnan()
// #include <Fonts/FreeSans24pt7b.h> // Custom Font for sensor values
// // #include <Fonts/FreeSans18pt7b.h> // Consider a slightly smaller font for target temp if 24pt is too large
// // Pin definitions
// #define TFT_CS 15
// #define TFT_DC 2
// #define TFT_RST -1
// #define TOUCH_SCL 8
// #define TOUCH_SDA 10
// #define DHTPIN 17
// #define DHTTYPE DHT22
// // Objects
// Adafruit_ILI9341 tft = Adafruit_ILI9341(TFT_CS, TFT_DC, TFT_RST);
// Adafruit_FT6206 touch = Adafruit_FT6206();
// DHT dht(DHTPIN, DHTTYPE);
// // --- Bitmaps ---
// static const unsigned char PROGMEM image_debug_on_bits[] = {
// 0x03,0xc0,0x12,0x48,0x2c,0x34,0x40,0x02,0x23,0xc4,0x24,0x24,0xc8,0x13,0x88,0x11,
// 0x88,0x11,0xc8,0x13,0x24,0x24,0x23,0xc4,0x40,0x02,0x2c,0x34,0x12,0x48,0x03,0xc0
// };
// #define DEBUG_ICON_W 16
// #define DEBUG_ICON_H 16
// static const unsigned char PROGMEM image_humidity_detail_bits[] = {
// 0x04,0x00,0x00,0x04,0x00,0x00,0x0c,0x00,0x00,0x0a,0x00,0x00,0x12,0x00,0x00,0x11,0x00,0x00,
// 0x20,0x80,0x00,0x20,0x80,0x00,0x41,0x40,0x00,0x40,0xc0,0x00,0x80,0xa0,0x00,0x80,0x20,0x00,
// 0x40,0x40,0x00,0x40,0x40,0x00,0x30,0x80,0x00,0x0f,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
// 0x1c,0x08,0x00,0x76,0x10,0x00,0x63,0x10,0x00,0x63,0x20,0x00,0x63,0x20,0x00,0x36,0x40,0x00,
// 0x1c,0x80,0x00,0x00,0x9e,0x00,0x01,0x3b,0x80,0x01,0x31,0x80,0x02,0x31,0x80,0x02,0x3b,0x80,
// 0x04,0x1e,0x00
// };
// #define HUMIDITY_ICON_W 17
// #define HUMIDITY_ICON_H 31
// // --- ADDED: Temp Up/Down Bitmaps ---
// static const unsigned char PROGMEM image_temp_down_bits[] = {
// 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
// 0xff,0xff,0xff,0xff,0xff,0xfe,0x00,0x7f,0xfe,0x00,0x7f,0xff,0xff,0xff,0x7f,0xff,
// 0xfe,0x3f,0xff,0xfc,0x1f,0xff,0xf8,0x0f,0xff,0xf0,0x07,0xff,0xe0,0x03,0xff,0xc0,
// 0x01,0xff,0x80,0x00,0xff,0x00,0x00,0x7e,0x00,0x00,0x3c,0x00,0x00,0x18,0x00
// };
// static const unsigned char PROGMEM image_temp_up_bits[] = {
// 0x00,0x00,0x00,0x00,0x18,0x00,0x00,0x3c,0x00,0x00,0x7e,0x00,0x00,0xff,0x00,0x01,
// 0xff,0x80,0x03,0xff,0xc0,0x07,0xe7,0xe0,0x0f,0xe7,0xf0,0x1f,0xe7,0xf8,0x3f,0xe7,
// 0xfc,0x7e,0x00,0x7e,0xfe,0x00,0x7f,0xff,0xe7,0xff,0xff,0xe7,0xff,0xff,0xe7,0xff,
// 0xff,0xe7,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x7f,0xff,0xfe
// };
// #define TEMP_ICON_W 24
// #define TEMP_ICON_H 21
// // ---
// // --- Layout Definitions ---
// // Debug Button Area
// #define DEBUG_BUTTON_RECT_X 5
// #define DEBUG_BUTTON_RECT_Y 201
// #define DEBUG_BUTTON_RECT_W 33
// #define DEBUG_BUTTON_RECT_H 33
// #define DEBUG_ICON_X (DEBUG_BUTTON_RECT_X + (DEBUG_BUTTON_RECT_W - DEBUG_ICON_W) / 2)
// #define DEBUG_ICON_Y (DEBUG_BUTTON_RECT_Y + (DEBUG_BUTTON_RECT_H - DEBUG_ICON_H) / 2)
// // Temperature Display Container
// #define CONTAINER_TEMP_X 43
// #define CONTAINER_TEMP_Y 11
// #define CONTAINER_TEMP_W 80
// #define CONTAINER_TEMP_H 36
// // Humidity Display Container
// #define CONTAINER_HUMID_X 43
// #define CONTAINER_HUMID_Y 194
// #define CONTAINER_HUMID_W 80
// #define CONTAINER_HUMID_H 36
// // Target Temperature Display Area (within inner circle)
// #define TARGET_CIRCLE_CENTER_X 178
// #define TARGET_CIRCLE_CENTER_Y 119
// #define TARGET_CIRCLE_INNER_R 84
// #define TARGET_TEXT_Y_POS 82 // As per user example drawString call
// // Target Temperature Control Buttons
// #define TEMP_UP_X 167 // Top-left X of UP bitmap
// #define TEMP_UP_Y 45 // Top-left Y of UP bitmap
// #define TEMP_DOWN_X 166 // Top-left X of DOWN bitmap
// #define TEMP_DOWN_Y 173 // Top-left Y of DOWN bitmap
// // Define Touch Areas slightly larger than icons for easier interaction
// #define TOUCH_AREA_PADDING 5
// #define TEMP_UP_RECT_X (TEMP_UP_X - TOUCH_AREA_PADDING)
// #define TEMP_UP_RECT_Y (TEMP_UP_Y - TOUCH_AREA_PADDING)
// #define TEMP_UP_RECT_W (TEMP_ICON_W + 2 * TOUCH_AREA_PADDING)
// #define TEMP_UP_RECT_H (TEMP_ICON_H + 2 * TOUCH_AREA_PADDING)
// #define TEMP_DOWN_RECT_X (TEMP_DOWN_X - TOUCH_AREA_PADDING)
// #define TEMP_DOWN_RECT_Y (TEMP_DOWN_Y - TOUCH_AREA_PADDING)
// #define TEMP_DOWN_RECT_W (TEMP_ICON_W + 2 * TOUCH_AREA_PADDING)
// #define TEMP_DOWN_RECT_H (TEMP_ICON_H + 2 * TOUCH_AREA_PADDING)
// // ---
// // --- Canvas Objects ---
// GFXcanvas1 tempCanvas(CONTAINER_TEMP_W, CONTAINER_TEMP_H);
// GFXcanvas1 humidCanvas(CONTAINER_HUMID_W, CONTAINER_HUMID_H);
// // --- Colors ---
// #define COLOR_DEBUG_ICON_ON 0x73AE
// #define COLOR_DEBUG_ICON_OFF ILI9341_WHITE
// #define COLOR_BUTTON_RECT ILI9341_WHITE
// #define COLOR_BORDER ILI9341_WHITE
// #define COLOR_TEXT_SENSOR ILI9341_WHITE // Color for current temp/humid text
// #define COLOR_TARGET_OUTER ILI9341_WHITE
// #define COLOR_TARGET_INNER ILI9341_BLACK
// #define COLOR_TEXT_TARGET ILI9341_WHITE // Color for target temp text
// #define COLOR_TEMP_BUTTONS ILI9341_WHITE // Color for up/down arrow icons
// #define COLOR_HUMIDITY_ICON ILI9341_WHITE
// #define COLOR_BACKGROUND ILI9341_BLACK
// #define COLOR_ERROR_TEXT ILI9341_RED
// // --- State Variables ---
// bool thermostatActive = false; // Placeholder, functionality not fully implemented
// bool isThermostatInitialized = false;
// bool debugModeActive = false;
// // --- ADDED: Target Temperature Variables ---
// float targetTempC = 21.0f; // Default target temp
// const float MIN_TARGET_TEMP = 13.0f;
// const float MAX_TARGET_TEMP = 32.0f;
// const float TARGET_TEMP_INCREMENT = 1.0f;
// // ---
// // Timing variables
// unsigned long previousSensorMillis = 0;
// const long readInterval = 2000;
// // Debouncing Variables
// unsigned long lastDebounceTime = 0;
// unsigned long debounceDelay = 50;
// // --- Variables to store last displayed sensor values ---
// float lastDisplayedTempC = -999.0;
// float lastDisplayedHumidity = -999.0;
// // Text Buffers for Formatting
// char tempBuffer[10];
// char humidBuffer[8];
// char targetTempBuffer[8]; // ADDED buffer for target temp
// // Function Prototypes
// void drawStaticElements();
// void updateDebugButtonDisplay();
// void updateTemperatureDisplay(float tempC);
// void updateHumidityDisplay(float humidity);
// void updateTargetTempDisplay(); // ADDED prototype
// void readAndProcessSensors();
// // *** Function Implementations ***
// // Draws elements that don't change after initial setup
// void drawStaticElements() {
// tft.fillScreen(COLOR_BACKGROUND);
// // button_debug outline
// tft.drawRoundRect(DEBUG_BUTTON_RECT_X, DEBUG_BUTTON_RECT_Y, DEBUG_BUTTON_RECT_W, DEBUG_BUTTON_RECT_H, 5, COLOR_BUTTON_RECT);
// // target_container_outer
// tft.fillCircle(TARGET_CIRCLE_CENTER_X, TARGET_CIRCLE_CENTER_Y, TARGET_CIRCLE_INNER_R + 5, COLOR_TARGET_OUTER); // Outer circle radius = inner + 5
// // target_container_inner
// tft.fillCircle(TARGET_CIRCLE_CENTER_X, TARGET_CIRCLE_CENTER_Y, TARGET_CIRCLE_INNER_R, COLOR_TARGET_INNER);
// // --- ADDED: Temp Up/Down Icons ---
// tft.drawBitmap(TEMP_UP_X, TEMP_UP_Y, image_temp_up_bits, TEMP_ICON_W, TEMP_ICON_H, COLOR_TEMP_BUTTONS);
// tft.drawBitmap(TEMP_DOWN_X, TEMP_DOWN_Y, image_temp_down_bits, TEMP_ICON_W, TEMP_ICON_H, COLOR_TEMP_BUTTONS);
// // ---
// // humidity_detail icon (static position)
// tft.drawBitmap(124, 198, image_humidity_detail_bits, HUMIDITY_ICON_W, HUMIDITY_ICON_H, COLOR_HUMIDITY_ICON);
// // Other static buttons
// tft.drawRoundRect(5, 5, 33, 33, 5, COLOR_BUTTON_RECT); // button_mode
// tft.drawRoundRect(5, 86, 33, 33, 5, COLOR_BUTTON_RECT); // button_acfan
// tft.drawRoundRect(5, 46, 33, 33, 5, COLOR_BUTTON_RECT); // button_cf
// tft.drawRoundRect(5, 163, 33, 33, 5, COLOR_BUTTON_RECT); // button_UNUSED
// tft.drawRoundRect(5, 125, 33, 33, 5, COLOR_BUTTON_RECT); // button_UNUSED copy
// // border
// tft.drawRect(1, 1, tft.width() - 2, tft.height() - 2, COLOR_BORDER);
// }
// // Updates only the debug icon based on the current state
// void updateDebugButtonDisplay() {
// uint16_t iconColor = debugModeActive ? COLOR_DEBUG_ICON_ON : COLOR_DEBUG_ICON_OFF;
// tft.fillRect(DEBUG_ICON_X, DEBUG_ICON_Y, DEBUG_ICON_W, DEBUG_ICON_H, COLOR_BACKGROUND);
// tft.drawBitmap(DEBUG_ICON_X, DEBUG_ICON_Y, image_debug_on_bits, DEBUG_ICON_W, DEBUG_ICON_H, iconColor);
// }
// // Updates the temperature display area using canvas
// void updateTemperatureDisplay(float tempC) {
// int16_t x1, y1;
// uint16_t w, h;
// if (isnan(tempC)) { strcpy(tempBuffer, "---"); }
// else { sprintf(tempBuffer, "%d", (int)round(tempC)); } // Format as integer
// tempCanvas.setFont(&FreeSans24pt7b);
// tempCanvas.setTextSize(1);
// tempCanvas.setTextWrap(false);
// tempCanvas.getTextBounds(tempBuffer, 0, 0, &x1, &y1, &w, &h);
// int16_t startX_c = (CONTAINER_TEMP_W - 3) - w; // Right align in canvas w/ padding
// int16_t startY_c = (CONTAINER_TEMP_H - h) / 2 - y1; // Vert center in canvas
// tempCanvas.fillScreen(0); // Clear canvas (bg bit 0)
// tempCanvas.setCursor(startX_c, startY_c);
// tempCanvas.setTextColor(1); // Text bit 1
// tempCanvas.print(tempBuffer);
// tft.drawBitmap(CONTAINER_TEMP_X, CONTAINER_TEMP_Y, tempCanvas.getBuffer(),
// CONTAINER_TEMP_W, CONTAINER_TEMP_H, COLOR_TEXT_SENSOR, COLOR_BACKGROUND);
// lastDisplayedTempC = tempC;
// // Optional: Simplified debug output
// // if (debugModeActive) Serial.printf("Updated Temp Canvas: %s C\n", tempBuffer);
// }
// // Updates the humidity display area using canvas
// void updateHumidityDisplay(float humidity) {
// int16_t x1, y1;
// uint16_t w, h;
// if (isnan(humidity)) { strcpy(humidBuffer, "--"); }
// else { sprintf(humidBuffer, "%.0f", humidity); } // Format as integer
// humidCanvas.setFont(&FreeSans24pt7b);
// humidCanvas.setTextSize(1);
// humidCanvas.setTextWrap(false);
// humidCanvas.getTextBounds(humidBuffer, 0, 0, &x1, &y1, &w, &h);
// int16_t startX_c = (CONTAINER_HUMID_W - 3) - w; // Right align in canvas w/ padding
// int16_t startY_c = (CONTAINER_HUMID_H - h) / 2 - y1; // Vert center in canvas
// humidCanvas.fillScreen(0); // Clear canvas (bg bit 0)
// humidCanvas.setCursor(startX_c, startY_c);
// humidCanvas.setTextColor(1); // Text bit 1
// humidCanvas.print(humidBuffer);
// tft.drawBitmap(CONTAINER_HUMID_X, CONTAINER_HUMID_Y, humidCanvas.getBuffer(),
// CONTAINER_HUMID_W, CONTAINER_HUMID_H, COLOR_TEXT_SENSOR, COLOR_BACKGROUND);
// lastDisplayedHumidity = humidity;
// // Optional: Simplified debug output
// // if (debugModeActive) Serial.printf("Updated Humid Canvas: %s %%\n", humidBuffer);
// }
// // // --- ADDED: Update Target Temperature Display ---
// // void updateTargetTempDisplay() {
// // int16_t x1, y1;
// // uint16_t w, h;
// // // Format the target temp as a whole number
// // int targetTempInt = round(targetTempC);
// // sprintf(targetTempBuffer, "%d", targetTempInt);
// // // Set font for target temp (using same font for consistency, adjust if needed)
// // tft.setFont(&FreeSans24pt7b);
// // tft.setTextSize(1); // Use size 1 with pt fonts
// // // Get bounds to calculate horizontal centering
// // tft.getTextBounds(targetTempBuffer, 0, 0, &x1, &y1, &w, &h);
// // // Calculate X for horizontal centering within the inner circle area
// // int16_t startX = TARGET_CIRCLE_CENTER_X - (w / 2);
// // // Use the specified Y position
// // int16_t startY = TARGET_TEXT_Y_POS;
// // // Set text color with background color matching the inner circle
// // // This overwrites previous text without needing explicit clearing
// // tft.setTextColor(COLOR_TEXT_TARGET, COLOR_TARGET_INNER);
// // // Draw the text
// // tft.setCursor(startX, startY);
// // tft.print(targetTempBuffer);
// // if (debugModeActive) Serial.printf("Updated Target Temp Display: %s C (Cursor: x=%d, y=%d)\n", targetTempBuffer, startX, startY);
// // }
// // // ---
// // --- Updates Target Temperature Display (Centered Horizontally & Vertically) ---
// void updateTargetTempDisplay() {
// int16_t x1, y1; // Vars for text bounding box relative offset
// uint16_t w, h; // Vars for text bounding box width and height
// // Format the target temp as a whole number
// int targetTempInt = round(targetTempC);
// sprintf(targetTempBuffer, "%d", targetTempInt);
// // Set font (ensure this is the font you want for the target temp)
// tft.setFont(&FreeSans24pt7b);
// tft.setTextSize(1); // Use size 1 with pt fonts
// // Get bounds to calculate centering
// tft.getTextBounds(targetTempBuffer, 0, 0, &x1, &y1, &w, &h);
// // Calculate X for horizontal centering within the inner circle area
// // Center of text's bounding box should align with circle center X
// int16_t startX = TARGET_CIRCLE_CENTER_X - (w / 2);
// // --- MODIFIED: Calculate Y for vertical centering ---
// // Calculate starting Y (baseline) for vertical centering within the inner circle
// // We want the center of the text's bounding box (h/2 from top) to align with the circle's center Y
// // Baseline Y = CircleCenterY - (distance from baseline to box center)
// // Baseline Y = CircleCenterY - (y1 + h/2)
// // Simplified: Baseline Y = CircleCenterY - y1 - h/2
// int16_t startY = TARGET_CIRCLE_CENTER_Y - y1 - (h / 2);
// // ---
// // Set text color with background color matching the inner circle
// // This overwrites previous text without needing explicit clearing of the circle area
// tft.setTextColor(COLOR_TEXT_TARGET, COLOR_TARGET_INNER);
// // Draw the text
// tft.setCursor(startX, startY); // Set cursor to calculated baseline position
// tft.print(targetTempBuffer);
// if (debugModeActive) Serial.printf("Updated Target Temp Display: %s C (Bounds: x1=%d, y1=%d, w=%d, h=%d | Cursor: x=%d, y=%d)\n",
// targetTempBuffer, x1, y1, w, h, startX, startY);
// }
// // Reads sensors and updates respective displays (via canvas or directly)
// void readAndProcessSensors() {
// float humidity = dht.readHumidity();
// float temperatureCelsius = dht.readTemperature();
// bool readError = isnan(humidity) || isnan(temperatureCelsius);
// if (readError) {
// if (debugModeActive) Serial.println("Failed to read from DHT sensor!");
// updateTemperatureDisplay(NAN); // Update display with error via canvas
// updateHumidityDisplay(NAN); // Update display with error via canvas
// } else {
// if (debugModeActive) {
// Serial.printf("Read: Temp=%.1fC, Hum=%.1f%% | Updating Sensor Displays\n",
// temperatureCelsius, humidity);
// }
// updateTemperatureDisplay(temperatureCelsius); // Update display via canvas
// updateHumidityDisplay(humidity); // Update display via canvas
// }
// }
// // *** SETUP ***
// void setup() {
// Serial.begin(115200);
// delay(500);
// Serial.println("\n---------------------------------");
// Serial.println("System Starting...");
// tft.begin();
// tft.setRotation(3);
// Serial.printf("TFT Initialized. Rotation: %d, Width: %d, Height: %d\n", tft.getRotation(), tft.width(), tft.height());
// // Initialize Canvas Settings
// tempCanvas.setFont(&FreeSans24pt7b);
// tempCanvas.setTextWrap(false);
// humidCanvas.setFont(&FreeSans24pt7b);
// humidCanvas.setTextWrap(false);
// Serial.println("Canvases initialized.");
// // Initial screen drawing (static elements)
// drawStaticElements();
// Serial.println("Drew static elements.");
// // Draw initial state of dynamic elements
// updateDebugButtonDisplay(); // Initial debug icon state
// updateTemperatureDisplay(NAN); // Initial sensor temp via canvas
// updateHumidityDisplay(NAN); // Initial sensor humid via canvas
// updateTargetTempDisplay(); // ADDED: Initial target temp display
// Serial.println("Drew initial dynamic elements.");
// Serial.println("Initializing DHT sensor...");
// dht.begin();
// isThermostatInitialized = true;
// Serial.println("Initializing Touchscreen...");
// Wire.begin(TOUCH_SDA, TOUCH_SCL);
// if (!touch.begin(40)) {
// Serial.println("!!!!!! Touchscreen NOT FOUND! Check Wiring & I2C Address !!!!!!");
// tft.setFont(); tft.setTextSize(2); tft.setTextColor(COLOR_ERROR_TEXT);
// tft.setCursor(50, 100); tft.print("Touch Error!");
// while (1) delay(100); // Halt
// } else {
// Serial.println("Touchscreen Initialized OK.");
// }
// Serial.println("Setup complete. Entering loop...");
// Serial.println("---------------------------------");
// previousSensorMillis = millis() - readInterval; // Force first sensor read soon
// }
// // *** LOOP ***
// void loop() {
// unsigned long currentMillis = millis();
// // --- Touch Handling ---
// if (touch.touched()) {
// if ((currentMillis - lastDebounceTime) > debounceDelay) {
// TS_Point p = touch.getPoint();
// int16_t touchX = tft.width() - 1 - p.y; // Map coords based on rotation
// int16_t touchY = p.x;
// bool touchHandled = false; // Flag to see if touch was processed
// // Check Debug Button
// if (!touchHandled &&
// (touchX >= DEBUG_BUTTON_RECT_X) && (touchX < (DEBUG_BUTTON_RECT_X + DEBUG_BUTTON_RECT_W)) &&
// (touchY >= DEBUG_BUTTON_RECT_Y) && (touchY < (DEBUG_BUTTON_RECT_Y + DEBUG_BUTTON_RECT_H)))
// {
// Serial.printf("Touch Debug Btn (X=%d, Y=%d)\n", touchX, touchY);
// lastDebounceTime = currentMillis;
// debugModeActive = !debugModeActive;
// updateDebugButtonDisplay();
// touchHandled = true;
// if (debugModeActive) Serial.println(" Debug Mode ENABLED.");
// else Serial.println(" Debug Mode DISABLED.");
// }
// // Check Temp UP Button --- ADDED ---
// if (!touchHandled &&
// (touchX >= TEMP_UP_RECT_X) && (touchX < (TEMP_UP_RECT_X + TEMP_UP_RECT_W)) &&
// (touchY >= TEMP_UP_RECT_Y) && (touchY < (TEMP_UP_RECT_Y + TEMP_UP_RECT_H)))
// {
// Serial.printf("Touch Temp UP (X=%d, Y=%d)\n", touchX, touchY);
// lastDebounceTime = currentMillis;
// if (targetTempC < MAX_TARGET_TEMP) {
// targetTempC += TARGET_TEMP_INCREMENT;
// // Optional: Clamp to max after incrementing just in case of float issues
// if (targetTempC > MAX_TARGET_TEMP) targetTempC = MAX_TARGET_TEMP;
// updateTargetTempDisplay(); // Update screen
// Serial.printf(" Target Temp increased to: %.1f C\n", targetTempC);
// } else {
// Serial.println(" Target Temp already at MAX.");
// }
// touchHandled = true;
// }
// // Check Temp DOWN Button --- ADDED ---
// if (!touchHandled &&
// (touchX >= TEMP_DOWN_RECT_X) && (touchX < (TEMP_DOWN_RECT_X + TEMP_DOWN_RECT_W)) &&
// (touchY >= TEMP_DOWN_RECT_Y) && (touchY < (TEMP_DOWN_RECT_Y + TEMP_DOWN_RECT_H)))
// {
// Serial.printf("Touch Temp DOWN (X=%d, Y=%d)\n", touchX, touchY);
// lastDebounceTime = currentMillis;
// if (targetTempC > MIN_TARGET_TEMP) {
// targetTempC -= TARGET_TEMP_INCREMENT;
// // Optional: Clamp to min after decrementing
// if (targetTempC < MIN_TARGET_TEMP) targetTempC = MIN_TARGET_TEMP;
// updateTargetTempDisplay(); // Update screen
// Serial.printf(" Target Temp decreased to: %.1f C\n", targetTempC);
// } else {
// Serial.println(" Target Temp already at MIN.");
// }
// touchHandled = true;
// }
// // --- End Temp Button Checks ---
// // Log if touch didn't hit any known active area
// if (!touchHandled && debugModeActive) {
// Serial.printf("Touch Elsewhere (X=%d, Y=%d)\n", touchX, touchY);
// }
// } // End debounce check
// } // End touch detected check
// // --- Sensor Reading Logic ---
// if (isThermostatInitialized && (currentMillis - previousSensorMillis >= readInterval)) {
// previousSensorMillis = currentMillis;
// readAndProcessSensors(); // Reads sensors and updates display via canvas functions
// }
// // --- End Sensor Reading Logic ---
// // delay(10); // Optional small delay
// }
#include <Adafruit_GFX.h> // Core graphics library
#include <SPI.h>
#include <Wire.h>
#include <Adafruit_ILI9341.h> // Hardware-specific library
#include <Adafruit_FT6206.h>
#include "DHT.h" // DHT sensor library
#include <cmath> // For round() and isnan()
#include <Fonts/FreeSans24pt7b.h> // Custom Font for sensor values
// Pin definitions
#define TFT_CS 15
#define TFT_DC 2
#define TFT_RST -1
#define TOUCH_SCL 8
#define TOUCH_SDA 10
#define DHTPIN 17
#define DHTTYPE DHT22
// Objects
Adafruit_ILI9341 tft = Adafruit_ILI9341(TFT_CS, TFT_DC, TFT_RST);
Adafruit_FT6206 touch = Adafruit_FT6206();
DHT dht(DHTPIN, DHTTYPE);
// --- Bitmaps ---
static const unsigned char PROGMEM image_debug_on_bits[] = { /* ... bitmap data ... */
0x03,0xc0,0x12,0x48,0x2c,0x34,0x40,0x02,0x23,0xc4,0x24,0x24,0xc8,0x13,0x88,0x11,
0x88,0x11,0xc8,0x13,0x24,0x24,0x23,0xc4,0x40,0x02,0x2c,0x34,0x12,0x48,0x03,0xc0 };
#define DEBUG_ICON_W 16
#define DEBUG_ICON_H 16
static const unsigned char PROGMEM image_humidity_detail_bits[] = { /* ... bitmap data ... */
0x04,0x00,0x00,0x04,0x00,0x00,0x0c,0x00,0x00,0x0a,0x00,0x00,0x12,0x00,0x00,0x11,0x00,0x00,
0x20,0x80,0x00,0x20,0x80,0x00,0x41,0x40,0x00,0x40,0xc0,0x00,0x80,0xa0,0x00,0x80,0x20,0x00,
0x40,0x40,0x00,0x40,0x40,0x00,0x30,0x80,0x00,0x0f,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x1c,0x08,0x00,0x76,0x10,0x00,0x63,0x10,0x00,0x63,0x20,0x00,0x63,0x20,0x00,0x36,0x40,0x00,
0x1c,0x80,0x00,0x00,0x9e,0x00,0x01,0x3b,0x80,0x01,0x31,0x80,0x02,0x31,0x80,0x02,0x3b,0x80,
0x04,0x1e,0x00 };
#define HUMIDITY_ICON_W 17
#define HUMIDITY_ICON_H 31
static const unsigned char PROGMEM image_temp_down_bits[] = { /* ... bitmap data ... */
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0xff,0xff,0xfe,0x00,0x7f,0xfe,0x00,0x7f,0xff,0xff,0xff,0x7f,0xff,
0xfe,0x3f,0xff,0xfc,0x1f,0xff,0xf8,0x0f,0xff,0xf0,0x07,0xff,0xe0,0x03,0xff,0xc0,
0x01,0xff,0x80,0x00,0xff,0x00,0x00,0x7e,0x00,0x00,0x3c,0x00,0x00,0x18,0x00 };
static const unsigned char PROGMEM image_temp_up_bits[] = { /* ... bitmap data ... */
0x00,0x00,0x00,0x00,0x18,0x00,0x00,0x3c,0x00,0x00,0x7e,0x00,0x00,0xff,0x00,0x01,
0xff,0x80,0x03,0xff,0xc0,0x07,0xe7,0xe0,0x0f,0xe7,0xf0,0x1f,0xe7,0xf8,0x3f,0xe7,
0xfc,0x7e,0x00,0x7e,0xfe,0x00,0x7f,0xff,0xe7,0xff,0xff,0xe7,0xff,0xff,0xe7,0xff,
0xff,0xe7,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x7f,0xff,0xfe };
#define TEMP_ICON_W 24
#define TEMP_ICON_H 21
// --- ADDED: C/F Icon Bitmaps ---
static const unsigned char PROGMEM image_c_ico_bits[] = {
0x70,0x00,0x00,0x88,0x00,0x00,0x88,0x00,0x00,0x88,0x00,0x00,0x70,0x00,0x00,0x00,
0x7e,0x00,0x00,0xc3,0x00,0x01,0x81,0x80,0x01,0x01,0x80,0x03,0x00,0x00,0x03,0x00,
0x00,0x03,0x00,0x00,0x03,0x00,0x00,0x03,0x01,0x80,0x01,0x01,0x80,0x01,0x83,0x00,
0x00,0xc3,0x00,0x00,0x7c,0x00
};
#define C_ICON_W 17
#define C_ICON_H 18
static const unsigned char PROGMEM image_f_ico_bits[] = {
0x70,0x00,0x88,0x00,0x88,0x00,0x88,0x00,0x70,0x00,0x01,0xfe,0x01,0x80,0x01,0x80,
0x01,0x80,0x01,0x80,0x01,0x80,0x01,0xfc,0x01,0x80,0x01,0x80,0x01,0x80,0x01,0x80,
0x01,0x80,0x01,0x80
};
#define F_ICON_W 15 // Note: F icon is narrower
#define F_ICON_H 18
// ---
// --- Layout Definitions ---
// Debug Button Area
#define DEBUG_BUTTON_RECT_X 5
#define DEBUG_BUTTON_RECT_Y 201
#define DEBUG_BUTTON_RECT_W 33
#define DEBUG_BUTTON_RECT_H 33
#define DEBUG_ICON_X (DEBUG_BUTTON_RECT_X + (DEBUG_BUTTON_RECT_W - DEBUG_ICON_W) / 2)
#define DEBUG_ICON_Y (DEBUG_BUTTON_RECT_Y + (DEBUG_BUTTON_RECT_H - DEBUG_ICON_H) / 2)
// C/F Toggle Button Area --- ADDED ---
#define BUTTON_CF_RECT_X 5
#define BUTTON_CF_RECT_Y 46
#define BUTTON_CF_RECT_W 33
#define BUTTON_CF_RECT_H 33
// Define a single position for the C/F icon, center it within the button area
#define UNIT_ICON_AREA_X BUTTON_CF_RECT_X
#define UNIT_ICON_AREA_Y BUTTON_CF_RECT_Y
#define UNIT_ICON_AREA_W BUTTON_CF_RECT_W
#define UNIT_ICON_AREA_H BUTTON_CF_RECT_H
// We will calculate the exact X,Y inside the update function based on icon width
// Temperature Display Container
#define CONTAINER_TEMP_X 43
#define CONTAINER_TEMP_Y 11
#define CONTAINER_TEMP_W 80
#define CONTAINER_TEMP_H 36
// Humidity Display Container
#define CONTAINER_HUMID_X 43
#define CONTAINER_HUMID_Y 194
#define CONTAINER_HUMID_W 80
#define CONTAINER_HUMID_H 36
// Target Temperature Display Area
#define TARGET_CIRCLE_CENTER_X 178
#define TARGET_CIRCLE_CENTER_Y 119
#define TARGET_CIRCLE_INNER_R 84
// Target Temperature Control Buttons
#define TEMP_UP_X 167
#define TEMP_UP_Y 45
#define TEMP_DOWN_X 166
#define TEMP_DOWN_Y 173
#define TOUCH_AREA_PADDING 5 // Make touch areas larger than icons
#define TEMP_UP_RECT_X (TEMP_UP_X - TOUCH_AREA_PADDING)
#define TEMP_UP_RECT_Y (TEMP_UP_Y - TOUCH_AREA_PADDING)
#define TEMP_UP_RECT_W (TEMP_ICON_W + 2 * TOUCH_AREA_PADDING)
#define TEMP_UP_RECT_H (TEMP_ICON_H + 2 * TOUCH_AREA_PADDING)
#define TEMP_DOWN_RECT_X (TEMP_DOWN_X - TOUCH_AREA_PADDING)
#define TEMP_DOWN_RECT_Y (TEMP_DOWN_Y - TOUCH_AREA_PADDING)
#define TEMP_DOWN_RECT_W (TEMP_ICON_W + 2 * TOUCH_AREA_PADDING)
#define TEMP_DOWN_RECT_H (TEMP_ICON_H + 2 * TOUCH_AREA_PADDING)
// --- Canvas Objects ---
GFXcanvas1 tempCanvas(CONTAINER_TEMP_W, CONTAINER_TEMP_H);
GFXcanvas1 humidCanvas(CONTAINER_HUMID_W, CONTAINER_HUMID_H);
// --- Colors ---
#define COLOR_DEBUG_ICON_ON 0x73AE
#define COLOR_DEBUG_ICON_OFF ILI9341_WHITE
#define COLOR_BUTTON_RECT ILI9341_WHITE
#define COLOR_BORDER ILI9341_WHITE
#define COLOR_TEXT_SENSOR ILI9341_WHITE
#define COLOR_TARGET_OUTER ILI9341_WHITE
#define COLOR_TARGET_INNER ILI9341_BLACK
#define COLOR_TEXT_TARGET ILI9341_WHITE
#define COLOR_TEMP_BUTTONS ILI9341_WHITE
#define COLOR_UNIT_ICON ILI9341_WHITE // Color for C/F icon
#define COLOR_HUMIDITY_ICON ILI9341_WHITE
#define COLOR_BACKGROUND ILI9341_BLACK
#define COLOR_ERROR_TEXT ILI9341_RED
// --- State Variables ---
bool thermostatActive = false;
bool isThermostatInitialized = false;
bool debugModeActive = false;
bool isCelsius = true; // ADDED: Temperature unit state (true=C, false=F)
// Target Temperature Variables
float targetTempC = 21.0f; // Always store target internally as Celsius
const float MIN_TARGET_TEMP = 12.8f;
const float MAX_TARGET_TEMP = 32.2f;
const float TARGET_TEMP_INCREMENT = 1.0f;
// Timing variables
unsigned long previousSensorMillis = 0;
const long readInterval = 2000;
// Debouncing Variables
unsigned long lastDebounceTime = 0;
unsigned long debounceDelay = 50;
// Variables to store last displayed sensor values
float lastDisplayedTempC = NAN; // Initialize to NAN to force first update
float lastDisplayedHumidity = NAN;
// Text Buffers for Formatting
char tempBuffer[10];
char humidBuffer[8];
char targetTempBuffer[8];
// --- Function Prototypes ---
float convertToFahrenheit(float celsius); // ADDED
void drawStaticElements();
void updateDebugButtonDisplay();
void updateUnitIconDisplay(); // ADDED
void updateTemperatureDisplay(float tempC);
void updateHumidityDisplay(float humidity);
void updateTargetTempDisplay();
void readAndProcessSensors();
// *** Function Implementations ***
// --- ADDED: Celsius to Fahrenheit Conversion ---
float convertToFahrenheit(float celsius) {
return (celsius * 1.8f) + 32.0f;
}
// Draws elements that don't change after initial setup
void drawStaticElements() {
tft.fillScreen(COLOR_BACKGROUND);
// button_debug outline
tft.drawRoundRect(DEBUG_BUTTON_RECT_X, DEBUG_BUTTON_RECT_Y, DEBUG_BUTTON_RECT_W, DEBUG_BUTTON_RECT_H, 5, COLOR_BUTTON_RECT);
// button_cf outline --- ADDED ---
tft.drawRoundRect(BUTTON_CF_RECT_X, BUTTON_CF_RECT_Y, BUTTON_CF_RECT_W, BUTTON_CF_RECT_H, 5, COLOR_BUTTON_RECT);
// target_container_outer
tft.fillCircle(TARGET_CIRCLE_CENTER_X, TARGET_CIRCLE_CENTER_Y, TARGET_CIRCLE_INNER_R + 5, COLOR_TARGET_OUTER);
// target_container_inner
tft.fillCircle(TARGET_CIRCLE_CENTER_X, TARGET_CIRCLE_CENTER_Y, TARGET_CIRCLE_INNER_R, COLOR_TARGET_INNER);
// Temp Up/Down Icons
tft.drawBitmap(TEMP_UP_X, TEMP_UP_Y, image_temp_up_bits, TEMP_ICON_W, TEMP_ICON_H, COLOR_TEMP_BUTTONS);
tft.drawBitmap(TEMP_DOWN_X, TEMP_DOWN_Y, image_temp_down_bits, TEMP_ICON_W, TEMP_ICON_H, COLOR_TEMP_BUTTONS);
// humidity_detail icon
tft.drawBitmap(124, 198, image_humidity_detail_bits, HUMIDITY_ICON_W, HUMIDITY_ICON_H, COLOR_HUMIDITY_ICON);
// Other static buttons (Ensure no overlap with C/F button)
tft.drawRoundRect(5, 5, 33, 33, 5, COLOR_BUTTON_RECT); // button_mode
tft.drawRoundRect(5, 86, 33, 33, 5, COLOR_BUTTON_RECT); // button_acfan
// tft.drawRoundRect(5, 46, 33, 33, 5, COLOR_BUTTON_RECT); // button_cf is now drawn above
tft.drawRoundRect(5, 163, 33, 33, 5, COLOR_BUTTON_RECT); // button_UNUSED
tft.drawRoundRect(5, 125, 33, 33, 5, COLOR_BUTTON_RECT); // button_UNUSED copy
// border
tft.drawRect(1, 1, tft.width() - 2, tft.height() - 2, COLOR_BORDER);
}
// Updates only the debug icon based on the current state
void updateDebugButtonDisplay() {
uint16_t iconColor = debugModeActive ? COLOR_DEBUG_ICON_ON : COLOR_DEBUG_ICON_OFF;
tft.fillRect(DEBUG_ICON_X, DEBUG_ICON_Y, DEBUG_ICON_W, DEBUG_ICON_H, COLOR_BACKGROUND);
tft.drawBitmap(DEBUG_ICON_X, DEBUG_ICON_Y, image_debug_on_bits, DEBUG_ICON_W, DEBUG_ICON_H, iconColor);
}
// --- ADDED: Updates the C/F Unit Icon ---
void updateUnitIconDisplay() {
const unsigned char* iconBitmap;
uint16_t iconW, iconH;
if (isCelsius) {
iconBitmap = image_c_ico_bits;
iconW = C_ICON_W;
iconH = C_ICON_H;
} else {
iconBitmap = image_f_ico_bits;
iconW = F_ICON_W;
iconH = F_ICON_H;
}
// Calculate position to center the icon within the button area
uint16_t iconX = UNIT_ICON_AREA_X + (UNIT_ICON_AREA_W - iconW) / 2;
uint16_t iconY = UNIT_ICON_AREA_Y + (UNIT_ICON_AREA_H - iconH) / 2;
// Clear the button area behind the icon
tft.fillRect(UNIT_ICON_AREA_X + 1, UNIT_ICON_AREA_Y + 1, UNIT_ICON_AREA_W - 2, UNIT_ICON_AREA_H - 2, COLOR_BACKGROUND);
// Draw the selected icon
tft.drawBitmap(iconX, iconY, iconBitmap, iconW, iconH, COLOR_UNIT_ICON);
if (debugModeActive) Serial.printf("Drew Unit Icon: %s\n", isCelsius ? "Celsius" : "Fahrenheit");
}
// Updates the CURRENT temperature display area using canvas, respecting unit
void updateTemperatureDisplay(float tempC) {
int16_t x1, y1;
uint16_t w, h;
int displayTempInt; // Integer value to display (either C or F)
// Check if input is valid before proceeding
if (isnan(tempC)) {
strcpy(tempBuffer, "---");
} else {
// Convert to F if needed, then round for display
if (isCelsius) {
displayTempInt = round(tempC);
} else {
displayTempInt = round(convertToFahrenheit(tempC));
}
sprintf(tempBuffer, "%d", displayTempInt); // Format the integer
}
// --- Canvas Drawing ---
tempCanvas.setFont(&FreeSans24pt7b);
tempCanvas.setTextSize(1);
tempCanvas.setTextWrap(false);
tempCanvas.getTextBounds(tempBuffer, 0, 0, &x1, &y1, &w, &h);
int16_t startX_c = (CONTAINER_TEMP_W - 3) - w; // Right align in canvas w/ padding
int16_t startY_c = (CONTAINER_TEMP_H - h) / 2 - y1; // Vert center in canvas
tempCanvas.fillScreen(0); // Clear canvas
tempCanvas.setCursor(startX_c, startY_c);
tempCanvas.setTextColor(1); // Text bit 1
tempCanvas.print(tempBuffer);
// --- Transfer Canvas to Screen ---
tft.drawBitmap(CONTAINER_TEMP_X, CONTAINER_TEMP_Y, tempCanvas.getBuffer(),
CONTAINER_TEMP_W, CONTAINER_TEMP_H, COLOR_TEXT_SENSOR, COLOR_BACKGROUND);
lastDisplayedTempC = tempC; // Store the last *valid Celsius* reading
// Optional: More detailed debug output
// if (debugModeActive) Serial.printf("Updated Current Temp Display: %s %s\n", tempBuffer, isCelsius ? "C" : "F");
}
// Updates the humidity display area using canvas (unaffected by C/F toggle)
void updateHumidityDisplay(float humidity) {
int16_t x1, y1;
uint16_t w, h;
if (isnan(humidity)) { strcpy(humidBuffer, "--"); }
else { sprintf(humidBuffer, "%.0f", humidity); }
humidCanvas.setFont(&FreeSans24pt7b);
humidCanvas.setTextSize(1);
humidCanvas.setTextWrap(false);
humidCanvas.getTextBounds(humidBuffer, 0, 0, &x1, &y1, &w, &h);
int16_t startX_c = (CONTAINER_HUMID_W - 3) - w;
int16_t startY_c = (CONTAINER_HUMID_H - h) / 2 - y1;
humidCanvas.fillScreen(0);
humidCanvas.setCursor(startX_c, startY_c);
humidCanvas.setTextColor(1);
humidCanvas.print(humidBuffer);
tft.drawBitmap(CONTAINER_HUMID_X, CONTAINER_HUMID_Y, humidCanvas.getBuffer(),
CONTAINER_HUMID_W, CONTAINER_HUMID_H, COLOR_TEXT_SENSOR, COLOR_BACKGROUND);
lastDisplayedHumidity = humidity;
// if (debugModeActive) Serial.printf("Updated Humid Canvas: %s %%\n", humidBuffer);
}
// Updates Target Temperature Display (Centered, respects unit C/F)
void updateTargetTempDisplay() {
int16_t x1, y1;
uint16_t w, h;
int displayTargetTempInt; // Integer value to display (C or F)
// Convert the internally stored Celsius target temp if needed for display
if (isCelsius) {
displayTargetTempInt = round(targetTempC);
} else {
displayTargetTempInt = round(convertToFahrenheit(targetTempC));
}
sprintf(targetTempBuffer, "%d", displayTargetTempInt); // Format the integer
// --- Drawing directly on TFT (no canvas needed for single static-ish element) ---
tft.setFont(&FreeSans24pt7b);
tft.setTextSize(2);
tft.getTextBounds(targetTempBuffer, 0, 0, &x1, &y1, &w, &h);
int16_t startX = TARGET_CIRCLE_CENTER_X - (w / 2); // Horiz center
int16_t startY = TARGET_CIRCLE_CENTER_Y - y1 - (h / 2); // Vert center baseline
// Set color with background to overwrite previous value
tft.setTextColor(COLOR_TEXT_TARGET, COLOR_TARGET_INNER);
tft.setCursor(startX, startY);
tft.print(targetTempBuffer);
if (debugModeActive) Serial.printf("Updated Target Temp Display: %s %s (Internal: %.1f C)\n",
targetTempBuffer, isCelsius ? "C" : "F", targetTempC);
}
// Reads sensors and updates respective displays
void readAndProcessSensors() {
// Read sensors (consider adding error handling / retry logic here if needed)
float humidity = dht.readHumidity();
float currentTemperatureC = dht.readTemperature(); // Get current temp in Celsius
// Update humidity display (only depends on humidity reading)
updateHumidityDisplay(humidity); // Handles isnan internally
// Update current temperature display (depends on temp reading and isCelsius flag)
updateTemperatureDisplay(currentTemperatureC); // Handles isnan internally
// We stored the valid Celsius reading in lastDisplayedTempC inside updateTemperatureDisplay
if (debugModeActive && !isnan(currentTemperatureC) && !isnan(humidity)) {
Serial.printf("Sensor Read OK: Temp=%.1fC, Hum=%.1f%%\n", currentTemperatureC, humidity);
} else if (debugModeActive && (isnan(currentTemperatureC) || isnan(humidity))) {
Serial.println("Sensor Read Error!");
}
}
// *** SETUP ***
void setup() {
Serial.begin(115200);
delay(500);
Serial.println("\n---------------------------------");
Serial.println("System Starting...");
tft.begin();
tft.setRotation(3);
Serial.printf("TFT Initialized. Rotation: %d, Width: %d, Height: %d\n", tft.getRotation(), tft.width(), tft.height());
// Initialize Canvas Settings
tempCanvas.setFont(&FreeSans24pt7b); tempCanvas.setTextWrap(false);
humidCanvas.setFont(&FreeSans24pt7b); humidCanvas.setTextWrap(false);
Serial.println("Canvases initialized.");
// Initial screen drawing (static elements)
drawStaticElements();
Serial.println("Drew static elements.");
// Draw initial state of dynamic elements
isCelsius = true; // Start in Celsius
updateDebugButtonDisplay();
updateUnitIconDisplay(); // ADDED: Draw initial unit icon (C)
updateTemperatureDisplay(NAN); // Show "---" initially
updateHumidityDisplay(NAN); // Show "--" initially
updateTargetTempDisplay(); // Show initial target temp (in C)
Serial.println("Drew initial dynamic elements.");
Serial.println("Initializing DHT sensor...");
dht.begin();
isThermostatInitialized = true;
Serial.println("Initializing Touchscreen...");
Wire.begin(TOUCH_SDA, TOUCH_SCL);
if (!touch.begin(40)) {
Serial.println("!!!!!! Touchscreen NOT FOUND!");
tft.setFont(); tft.setTextSize(2); tft.setTextColor(COLOR_ERROR_TEXT);
tft.setCursor(50, 100); tft.print("Touch Error!");
while (1) delay(100); // Halt
} else {
Serial.println("Touchscreen Initialized OK.");
}
Serial.println("Setup complete. Entering loop...");
Serial.println("---------------------------------");
previousSensorMillis = millis() - readInterval; // Force first sensor read soon
}
// *** LOOP ***
void loop() {
unsigned long currentMillis = millis();
// --- Touch Handling ---
if (touch.touched()) {
if ((currentMillis - lastDebounceTime) > debounceDelay) {
TS_Point p = touch.getPoint();
int16_t touchX = tft.width() - 1 - p.y; // Map coords
int16_t touchY = p.x;
bool touchHandled = false;
// Check Debug Button
if (!touchHandled &&
(touchX >= DEBUG_BUTTON_RECT_X) && (touchX < (DEBUG_BUTTON_RECT_X + DEBUG_BUTTON_RECT_W)) &&
(touchY >= DEBUG_BUTTON_RECT_Y) && (touchY < (DEBUG_BUTTON_RECT_Y + DEBUG_BUTTON_RECT_H)))
{
// ... (debug button logic as before) ...
Serial.printf("Touch Debug Btn (X=%d, Y=%d)\n", touchX, touchY);
lastDebounceTime = currentMillis;
debugModeActive = !debugModeActive;
updateDebugButtonDisplay();
touchHandled = true;
if (debugModeActive) Serial.println(" Debug Mode ENABLED.");
else Serial.println(" Debug Mode DISABLED.");
}
// Check C/F Toggle Button --- ADDED ---
if (!touchHandled &&
(touchX >= BUTTON_CF_RECT_X) && (touchX < (BUTTON_CF_RECT_X + BUTTON_CF_RECT_W)) &&
(touchY >= BUTTON_CF_RECT_Y) && (touchY < (BUTTON_CF_RECT_Y + BUTTON_CF_RECT_H)))
{
Serial.printf("Touch C/F Toggle (X=%d, Y=%d)\n", touchX, touchY);
lastDebounceTime = currentMillis;
isCelsius = !isCelsius; // Toggle the state
updateUnitIconDisplay(); // Update the icon display
// Force redraw of temperature displays with the new unit
updateTemperatureDisplay(lastDisplayedTempC); // Use last valid C reading
updateTargetTempDisplay(); // Update target display
touchHandled = true;
Serial.printf(" Temperature Unit changed to: %s\n", isCelsius ? "Celsius" : "Fahrenheit");
}
// Check Temp UP Button
if (!touchHandled &&
(touchX >= TEMP_UP_RECT_X) && (touchX < (TEMP_UP_RECT_X + TEMP_UP_RECT_W)) &&
(touchY >= TEMP_UP_RECT_Y) && (touchY < (TEMP_UP_RECT_Y + TEMP_UP_RECT_H)))
{
Serial.printf("Touch Temp UP (X=%d, Y=%d)\n", touchX, touchY);
lastDebounceTime = currentMillis;
// Logic always uses internal Celsius value
if (targetTempC < MAX_TARGET_TEMP) {
targetTempC += TARGET_TEMP_INCREMENT;
if (targetTempC > MAX_TARGET_TEMP) targetTempC = MAX_TARGET_TEMP; // Clamp
updateTargetTempDisplay(); // Update screen (will display C or F based on isCelsius)
Serial.printf(" Target Temp increased to: %.1f C\n", targetTempC);
} else {
Serial.println(" Target Temp already at MAX.");
}
touchHandled = true;
}
// Check Temp DOWN Button
if (!touchHandled &&
(touchX >= TEMP_DOWN_RECT_X) && (touchX < (TEMP_DOWN_RECT_X + TEMP_DOWN_RECT_W)) &&
(touchY >= TEMP_DOWN_RECT_Y) && (touchY < (TEMP_DOWN_RECT_Y + TEMP_DOWN_RECT_H)))
{
Serial.printf("Touch Temp DOWN (X=%d, Y=%d)\n", touchX, touchY);
lastDebounceTime = currentMillis;
// Logic always uses internal Celsius value
if (targetTempC > MIN_TARGET_TEMP) {
targetTempC -= TARGET_TEMP_INCREMENT;
if (targetTempC < MIN_TARGET_TEMP) targetTempC = MIN_TARGET_TEMP; // Clamp
updateTargetTempDisplay(); // Update screen (will display C or F based on isCelsius)
Serial.printf(" Target Temp decreased to: %.1f C\n", targetTempC);
} else {
Serial.println(" Target Temp already at MIN.");
}
touchHandled = true;
}
// Log unhandled touches
if (!touchHandled && debugModeActive) {
Serial.printf("Touch Elsewhere (X=%d, Y=%d)\n", touchX, touchY);
}
} // End debounce check
} // End touch detected check
// --- Sensor Reading Logic ---
if (isThermostatInitialized && (currentMillis - previousSensorMillis >= readInterval)) {
previousSensorMillis = currentMillis;
readAndProcessSensors(); // Reads sensors and updates displays
}
// --- End Sensor Reading Logic ---
// delay(10); // Optional small delay
}
Loading
esp32-s3-devkitc-1
esp32-s3-devkitc-1
Loading
ili9341-cap-touch
ili9341-cap-touch