CCCPaste

Smart Automation rev_01

/********* Pleasedontcode.com **********

	Pleasedontcode thanks you for automatic code generation! Enjoy your code!

	- Terms and Conditions:
	You have a non-exclusive, revocable, worldwide, royalty-free license
	for personal and commercial use. Attribution is optional; modifications
	are allowed, but you're responsible for code maintenance. We're not
	liable for any loss or damage. For full terms,
	please visit pleasedontcode.com/termsandconditions.

	- Project: Smart Automation
	- Source Code NOT compiled for: ESP32 DevKit V1
	- Source Code created on: 2025-11-19 21:19:09

********* Pleasedontcode.com **********/

/****** SYSTEM REQUIREMENTS *****/
/****** SYSTEM REQUIREMENT 1 *****/
	/* relé umidificatore On se rh da dht11 <82 e off se */
	/* rh da dht11 >85. relé ventole (AC e DC) on 6 volte */
	/* in 24 ore per 74 secondi ogni volta. relé luci 12 */
	/* ore on e 12 ore off */
/****** END SYSTEM REQUIREMENTS *****/


/* START CODE */

/*******************************************************
 * ESP32 + Blynk IoT + DHT11 + Fototransistor + 4 Relè + Buzzer
 * - Umidificatore: isteresi RHmin/RHmax (editabili da app)
 * - Ventole: 6 cicli/24h, ON 74 s ciascuno (2 relè: DC e AC)
 * - Luci: 12 h ON / 12 h OFF (ciclo relativo all'avvio)
 * - Fototransistor: verifica accensione luci (soglia editabile)
 * - Allarmi (buzzer attivo):
 *    A) RH < RHmin per 30 min: 1 min beep 1 s ON / 1 s OFF
 *    B) Luci dovrebbero essere ON ma luce < soglia per 30 min:
 *       1 min beep 2 s ON / 2 s OFF
 *   Dopo 1 min l'allarme si ferma; la finestra 30 min riparte.
 *******************************************************/

#define BLYNK_TEMPLATE_ID "TMPL4X0xNGkPD"
#define BLYNK_TEMPLATE_NAME "Camera Fruttificazione"
#define BLYNK_AUTH_TOKEN "yu5gwTTfR31Gf1GBrGyEn8i2rxM7Ie9y"

#define BLYNK_PRINT Serial

#include 
#include   // Blynk IoT per ESP32  (Virtual Pins, BlynkTimer)
#include                // Adafruit DHT library

// ---- Credenziali Wi-Fi ----
char ssid[] = "WiFi";
char pass[] = "azz";
// ====== Pinout ======
#define PIN_DHT          4
#define PIN_PHOTO        34   // ADC1 (necessario con WiFi attivo)
#define PIN_RELAY_HUM    26
#define PIN_RELAY_FAN_DC 27
#define PIN_RELAY_FAN_AC 25
#define PIN_RELAY_LIGHT  33
#define PIN_BUZZER       32

// Moduli relè spesso "active LOW"
#define RELAY_ACTIVE_ON   LOW
#define RELAY_ACTIVE_OFF  HIGH

// ====== DHT ======
#define DHTTYPE DHT11
DHT dht(PIN_DHT, DHTTYPE);

// ====== Blynk ======
BlynkTimer timer;

// Virtual Pins
#define VP_HUM       V0
#define VP_TMP       V1
#define VP_HUM_RLY   V2
#define VP_FAN_DC    V3
#define VP_FAN_AC    V4
#define VP_LIGHT_RLY V5
#define VP_PHOTO     V6
#define VP_RH_MIN    V7
#define VP_RH_MAX    V8
#define VP_LUX_THR   V9
#define VP_EMERG     V10
#define VP_ALARM     V11

// ====== Parametri editabili da app (default) ======
volatile float rhMin = 82.0;   // % RH
volatile float rhMax = 85.0;   // % RH
volatile int   lightThreshold = 200; // ADC 0..4095

// ====== Stati ======
bool emergency = false;
bool humidifierOn = false;
bool fanDCOn = false;
bool fanACOn = false;
bool lightsOn = false;

// Scheduli
const unsigned long FAN_PERIOD     = 4UL * 60UL * 60UL * 1000UL; // ogni 4 h (6 volte/24h)
const unsigned long FAN_ON_TIME    = 74UL * 1000UL;              // 74 s
unsigned long nextFanMillis = 0;

const unsigned long LIGHT_HALF_DAY = 12UL * 60UL * 60UL * 1000UL; // 12 h
unsigned long nextLightToggle = 0;

// Allarmi (finestre 30 min)
const unsigned long WINDOW_30M = 30UL * 60UL * 1000UL;
unsigned long lowRHStart = 0;
unsigned long darkStart = 0;

// ====== Buzzer / allarme ======
enum AlarmType { ALARM_NONE, ALARM_HUM_LOW, ALARM_LIGHT_FAIL };
AlarmType currentAlarm = ALARM_NONE;

bool alarmActive = false;
bool buzzerState = false;         // stato ON/OFF corrente del buzzer
uint16_t buzzerPeriodMs = 1000;   // 1000 o 2000
int buzzerToggleTimerId = -1;
int alarmStopTimerId = -1;

// ====== Prototipi funzioni ======
void setRelay(int pin, bool on);
void publishStates();
void startBuzzer(AlarmType t, uint16_t periodMs, const char* logEventCode, const char* msg);

void readAndPublishSensors();
void controlHumidifier();
void fanScheduler();
void lightScheduler();
void checkAlarms();

// Callback timer (no lambda)
void buzzerToggleTask();
void alarmStopTask();
void fanOffTask();

// ====== Utility ======
void setRelay(int pin, bool on) {
  digitalWrite(pin, on ? RELAY_ACTIVE_ON : RELAY_ACTIVE_OFF);
}

void publishStates() {
  Blynk.virtualWrite(VP_HUM_RLY, humidifierOn);
  Blynk.virtualWrite(VP_FAN_DC,  fanDCOn);
  Blynk.virtualWrite(VP_FAN_AC,  fanACOn);
  Blynk.virtualWrite(VP_LIGHT_RLY, lightsOn);
}

// ====== Blynk ======
BLYNK_CONNECTED() {
  Blynk.syncVirtual(VP_RH_MIN, VP_RH_MAX, VP_LUX_THR, VP_EMERG);
}

BLYNK_WRITE(VP_RH_MIN) { rhMin = param.asFloat(); }
BLYNK_WRITE(VP_RH_MAX) { rhMax = param.asFloat(); }
BLYNK_WRITE(VP_LUX_THR){ lightThreshold = param.asInt(); }

BLYNK_WRITE(VP_EMERG)  {
  emergency = param.asInt();
  if (emergency) {
    // spegni tutto
    humidifierOn = fanDCOn = fanACOn = lightsOn = false;
    setRelay(PIN_RELAY_HUM,   false);
    setRelay(PIN_RELAY_FAN_DC,false);
    setRelay(PIN_RELAY_FAN_AC,false);
    setRelay(PIN_RELAY_LIGHT, false);
    publishStates();
  } else {
    // riavvia scheduler da ora
    nextFanMillis   = millis() + FAN_PERIOD;
    nextLightToggle = millis() + LIGHT_HALF_DAY;
  }
}

// ====== Sensori e logiche ======
void readAndPublishSensors() {
  float h = dht.readHumidity();
  float t = dht.readTemperature();

  if (!isnan(h) && !isnan(t)) {
    Blynk.virtualWrite(VP_HUM, h);
    Blynk.virtualWrite(VP_TMP, t);
  }

  int raw = analogRead(PIN_PHOTO); // 0..4095
  Blynk.virtualWrite(VP_PHOTO, raw);

  // Gestione finestre 30 min
  unsigned long now = millis();

  if (!isnan(h)) {
    if (h < rhMin) {
      if (lowRHStart == 0) lowRHStart = now;
    } else {
      lowRHStart = 0;
    }
  }

  if (lightsOn) {
    if (raw < lightThreshold) {
      if (darkStart == 0) darkStart = now;
    } else {
      darkStart = 0;
    }
  } else {
    darkStart = 0;
  }
}

void controlHumidifier() {
  if (emergency || alarmActive) return;

  float h = dht.readHumidity();
  if (isnan(h)) return;

  if (!humidifierOn && h < rhMin) {
    humidifierOn = true;
  } else if (humidifierOn && h > rhMax) {
    humidifierOn = false;
  }
  setRelay(PIN_RELAY_HUM, humidifierOn);
  Blynk.virtualWrite(VP_HUM_RLY, humidifierOn);
}

void fanScheduler() {
  if (emergency || alarmActive) return;

  unsigned long now = millis();
  if (now >= nextFanMillis) {
    // Avvia ON di 74 s per entrambe le ventole
    fanDCOn = fanACOn = true;
    setRelay(PIN_RELAY_FAN_DC, true);
    setRelay(PIN_RELAY_FAN_AC, true);
    publishStates();

    // Spegni dopo 74 s (callback senza lambda)
    timer.setTimeout(FAN_ON_TIME, fanOffTask);

    nextFanMillis = now + FAN_PERIOD;
  }
}

void lightScheduler() {
  if (emergency || alarmActive) return;

  unsigned long now = millis();
  if (now >= nextLightToggle) {
    lightsOn = !lightsOn; // 12 h ON / 12 h OFF
    setRelay(PIN_RELAY_LIGHT, lightsOn);
    Blynk.virtualWrite(VP_LIGHT_RLY, lightsOn);
    nextLightToggle = now + LIGHT_HALF_DAY;
  }
}

void checkAlarms() {
  if (alarmActive || emergency) return;

  unsigned long now = millis();

  // A) Umidità bassa persistente 30 min
  if (lowRHStart != 0 && (now - lowRHStart >= WINDOW_30M)) {
    lowRHStart = 0;
    startBuzzer(ALARM_HUM_LOW, 1000, "humidity_low_30m", "Umidità sotto soglia per 30 minuti");
    return;
  }

  // B) Luci ON attese ma non rilevate per 30 min
  if (darkStart != 0 && (now - darkStart >= WINDOW_30M)) {
    darkStart = 0;
    startBuzzer(ALARM_LIGHT_FAIL, 2000, "light_fail_30m", "Luci attese ON ma non rilevate per 30 minuti");
    return;
  }
}

// ====== Buzzer ======
void startBuzzer(AlarmType t, uint16_t periodMs, const char* logEventCode, const char* msg) {
  if (alarmActive) return;

  alarmActive = true;
  currentAlarm = t;
  buzzerPeriodMs = periodMs;

  if (logEventCode) {
    Blynk.logEvent(logEventCode, msg); // Richiede eventi creati nel Template
  }

  Blynk.virtualWrite(
    VP_ALARM,
    (t == ALARM_HUM_LOW)    ? "ALLARME: Umidità bassa" :
    (t == ALARM_LIGHT_FAIL) ? "ALLARME: Luci non rilevate" : ""
  );

  pinMode(PIN_BUZZER, OUTPUT);
  buzzerState = true;
  digitalWrite(PIN_BUZZER, HIGH);

  // Toggle periodico (1 s o 2 s)
  buzzerToggleTimerId = timer.setInterval(buzzerPeriodMs, buzzerToggleTask);

  // Stop dopo 60 s
  alarmStopTimerId = timer.setTimeout(60UL * 1000UL, alarmStopTask);
}

void buzzerToggleTask() {
  buzzerState = !buzzerState;
  digitalWrite(PIN_BUZZER, buzzerState ? HIGH : LOW);
}

void alarmStopTask() {
  if (buzzerToggleTimerId != -1) {
    timer.disable(buzzerToggleTimerId);
    timer.deleteTimer(buzzerToggleTimerId);
    buzzerToggleTimerId = -1;
  }
  digitalWrite(PIN_BUZZER, LOW);
  alarmActive = false;
  currentAlarm = ALARM_NONE;
  Blynk.virtualWrite(VP_ALARM, "");
}

void fanOffTask() {
  fanDCOn = fanACOn = false;
  setRelay(PIN_RELAY_FAN_DC, false);
  setRelay(PIN_RELAY_FAN_AC, false);
  publishStates();
}

// ====== Setup/Loop ======
void setup() {
  Serial.begin(115200);

  // Relè
  pinMode(PIN_RELAY_HUM,    OUTPUT);
  pinMode(PIN_RELAY_FAN_DC, OUTPUT);
  pinMode(PIN_RELAY_FAN_AC, OUTPUT);
  pinMode(PIN_RELAY_LIGHT,  OUTPUT);
  // Tutto OFF
  setRelay(PIN_RELAY_HUM,   false);
  setRelay(PIN_RELAY_FAN_DC,false);
  setRelay(PIN_RELAY_FAN_AC,false);
  setRelay(PIN_RELAY_LIGHT, false);
  publishStates();

  // ADC: 12 bit e attenuazione 11 dB (~0..3.1 V)
  analogReadResolution(12);
  analogSetPinAttenuation(PIN_PHOTO, ADC_11db);

  dht.begin();

  Blynk.begin(BLYNK_AUTH_TOKEN, ssid, pass);

  // Timers (funzioni senza lambda)
  timer.setInterval(2000, readAndPublishSensors); // ogni 2 s
  timer.setInterval(1000, controlHumidifier);     // ogni 1 s
  timer.setInterval(1000, fanScheduler);          // ogni 1 s
  timer.setInterval(1000, lightScheduler);        // ogni 1 s
  timer.setInterval(5000, publishStates);         // ogni 5 s
  timer.setInterval(10000, checkAlarms);          // ogni 10 s

  // Inizializza scheduler da "adesso"
  unsigned long now = millis();
  nextFanMillis   = now;                  // prima accensione ventole subito
  lightsOn        = true;                 // avvia con 12 h ON
  setRelay(PIN_RELAY_LIGHT, lightsOn);
  Blynk.virtualWrite(VP_LIGHT_RLY, lightsOn);
  nextLightToggle = now + LIGHT_HALF_DAY;
}

void loop() {
  Blynk.run();
  timer.run();
}

/* END CODE */