/*
Thermostat System
This project monitors temperature using a DHT22 sensor. The system has an adjustable
threshold controlled by a potentiometer. If the temperature is below this threshold,
a green LED turns on, indicating the system begins heating up. The system can be
toggled ON and OFF via a button, with a red LED indicating the power state.
The circuit:
* DHT22 sensor connected to GPIO 32
* Potentiometer connected to GPIO 25
* Button connected to GPIO 23
* Power status LED connected to GPIO 21
* Heating status LED connected to GPIO 19
Created on: 01/11/2024
Author: Israel Mateos Aparicio Ruiz Santa Quiteria
*/
#include "DHTesp.h"
// Pin definitions
const int tempLedPin = 19; // Pin for heating LED
const int powerLedPin = 21; // Pin for power status LED
const int buttonPin = 23; // Pin for button input
const int dht22Pin = 32; // Pin for DHT22 sensor
const int potPin = 25; // Pin for potentiometer
// Timing constants
const unsigned long debounceDelay = 150; // Debounce time for button in ms
const unsigned long dhtReadInterval = 2000; // Interval for DHT22 reading in ms
// Temperature threshold range
const float tempThresholdMin = 0.0; // Minimum threshold for potentiometer
const float tempThresholdMax = 50.0; // Maximum threshold for potentiometer
// State machine variables
DHTesp dhtSensor;
enum State { SYSTEM_OFF, SYSTEM_ON_WAITING, HEATING, NOT_HEATING };
volatile State currentState = SYSTEM_OFF;
volatile State lastState = SYSTEM_OFF; // Tracks last state to avoid redundant prints
// Button and timer control
volatile bool buttonPressed = false; // Flag for button press
unsigned long lastDebounceTime = 0; // Time of last button press for debounce
volatile bool dhtReadFlag = false; // Flag to trigger DHT22 read every interval
hw_timer_t* timer = NULL; // Timer for DHT22 sampling
// Last temperature reading
float lastTemperature = -1.0; // Last valid temperature reading
// Function prototypes
void IRAM_ATTR handleButtonPressISR();
void IRAM_ATTR onTimer();
void handleButtonPress();
float readTemperature();
float readThreshold();
void displayTemperatureAndThreshold(const char* stateName, float temperature, float threshold);
void enterSystemOffState();
void enterSystemOnWaitingState();
void enterHeatingState();
void enterNotHeatingState();
void setup() {
Serial.begin(115200);
// 2-second startup delay to allow correct DHT22 initialization
delay(2000);
// Initialize pins
pinMode(tempLedPin, OUTPUT);
pinMode(powerLedPin, OUTPUT);
pinMode(buttonPin, INPUT);
// Initialize DHT sensor
dhtSensor.setup(dht22Pin, DHTesp::DHT22);
// Attach button interrupt for ON/OFF control
attachInterrupt(digitalPinToInterrupt(buttonPin), handleButtonPressISR, FALLING);
// Initialize timer for DHT22 sampling every 2 seconds
timer = timerBegin(1000000); // Initialize with 1 MHz frequency
timerAttachInterrupt(timer, &onTimer); // Attach ISR to timer
timerAlarm(timer, dhtReadInterval, true, 0); // Set 2-second alarm with auto-reload
// Start in off state
enterSystemOffState();
}
void loop() {
// Check for button press to toggle system ON/OFF
handleButtonPress();
// Update system state based on FSM
switch (currentState) {
case SYSTEM_OFF:
enterSystemOffState();
break;
case SYSTEM_ON_WAITING:
enterSystemOnWaitingState();
break;
case HEATING:
enterHeatingState();
break;
case NOT_HEATING:
enterNotHeatingState();
break;
}
}
/* ISR for button press - sets flag and debounce */
void IRAM_ATTR handleButtonPressISR() {
if (millis() - lastDebounceTime >= debounceDelay) {
buttonPressed = true;
lastDebounceTime = millis();
}
}
/* ISR for timer - sets flag to trigger DHT22 reading */
void IRAM_ATTR onTimer() {
dhtReadFlag = true;
}
/* Handles button press and toggles system state */
void handleButtonPress() {
if (buttonPressed) {
buttonPressed = false;
if (currentState == SYSTEM_OFF) {
currentState = SYSTEM_ON_WAITING;
} else {
currentState = SYSTEM_OFF;
}
}
}
/* Reads temperature, only if the timer interval has passed */
float readTemperature() {
if (dhtReadFlag) {
dhtReadFlag = false;
TempAndHumidity data = dhtSensor.getTempAndHumidity();
if (dhtSensor.getStatus() == DHTesp::ERROR_NONE) {
lastTemperature = data.temperature;
} else {
Serial.println("DHT22 read error");
}
}
return lastTemperature;
}
/* Reads threshold from potentiometer */
float readThreshold() {
int potValue = analogRead(potPin);
return map(potValue, 0, 4095, tempThresholdMin, tempThresholdMax);
}
/* Displays temperature and threshold information */
void displayTemperatureAndThreshold(const char* stateName, float temperature, float threshold) {
Serial.print(stateName);
Serial.print(" - Temperature: ");
Serial.print(temperature);
Serial.print(" C, Threshold: ");
Serial.print(threshold);
Serial.println(" C");
}
/* State functions for handling specific states */
/* Handles SYSTEM_OFF state */
void enterSystemOffState() {
if (lastState != SYSTEM_OFF) {
digitalWrite(powerLedPin, LOW);
digitalWrite(tempLedPin, LOW);
Serial.println("System OFF");
lastState = SYSTEM_OFF;
}
}
/* Handles SYSTEM_ON_WAITING state and transitions if temperature is read */
void enterSystemOnWaitingState() {
if (lastState != SYSTEM_ON_WAITING) {
digitalWrite(powerLedPin, HIGH);
Serial.println("System ON - Monitoring temperature");
lastState = SYSTEM_ON_WAITING;
}
float temperature = readTemperature();
if (dhtSensor.getStatus() == DHTesp::ERROR_NONE) { // Only continue if no error
float thresholdTemp = readThreshold();
displayTemperatureAndThreshold("SYSTEM_ON_WAITING", temperature, thresholdTemp);
if (temperature > thresholdTemp) {
currentState = NOT_HEATING;
} else {
currentState = HEATING;
}
}
}
/* Handles HEATING state - heating up the room */
void enterHeatingState() {
if (lastState != HEATING) {
digitalWrite(tempLedPin, HIGH);
Serial.println("Temperature below threshold - Heating ON");
lastState = HEATING;
}
float temperature = readTemperature();
float thresholdTemp = readThreshold();
displayTemperatureAndThreshold("HEATING", temperature, thresholdTemp);
if (temperature >= thresholdTemp) {
currentState = NOT_HEATING;
}
}
/* Handles NOT_HEATING state - not heating */
void enterNotHeatingState() {
if (lastState != NOT_HEATING) {
digitalWrite(tempLedPin, LOW);
Serial.println("Temperature above threshold - Heating OFF");
lastState = NOT_HEATING;
}
float temperature = readTemperature();
float thresholdTemp = readThreshold();
displayTemperatureAndThreshold("NOT_HEATING", temperature, thresholdTemp);
if (temperature < thresholdTemp) {
currentState = HEATING;
}
}