/*
* ============================================================
* PALOKAAPIN OHJAUS – Arduino-ohjelma
* ============================================================
* Perustuu LOGO!-logiikkakaavioon (palokaapin ohjaus).
*
* ┌──────────┬───────┬──────────────────────────────────────┐
* │ Signaali │ Pinni │ Kuvaus │
* ├──────────┼───────┼──────────────────────────────────────┤
* │ I1 │ D2 │ Tulo: Käyntilupa (A/0-kytkin) │
* │ Q1 │ D8 │ Lähtö: Irtikytkentä │
* │ Q2 │ D9 │ Lähtö: ASD Pysäytys │
* │ Q3 │ D10 │ Lähtö: Putkiston erotus │
* │ Q4 │ D11 │ Lähtö: Puhdistuspulssi │
* └──────────┴───────┴──────────────────────────────────────┘
*
* ============================================================
* LOHKOKAAVIO TEKSTINÄ
* ============================================================
*
* B001 (Viikko-ohjelma Ma–Pe 11:00–11:02)
* │
* ├──► B003 (Trig 20s)
* │ │
* │ └──► B004 (Trig 8s) ──► Q3 (Putkiston erotus)
* │ │
* │ └──► B006 [AND] ──► B002 (Trig 8s) ──► B005 (1s/1s) ──► Q4
* │ ▲
* │ I1 (Käyntilupa)
* │
* └──► B007 (Trig 2min, takaisinkytketty)
* ├──► Q1 (Irtikytkentä)
* └──► Q2 (ASD Pysäytys)
*
* ============================================================
* LOHKOKUVAUKSET
* ============================================================
*
* B001 – Viikko-ohjelma (aikakello)
* Ohjelmoitu: Ma–Pe (MTWTF--), 11:00–11:02h
* Pulse=N: lähtö HIGH koko aikajakson 11:00–11:02 ajan.
* Koodissa käytetään nousevaa reunaa (klo 11:00) jotta
* ajastimet käynnistetään täsmälleen kerran per päivä.
*
* B003 – Päällä-viiveajastin (Trig), 20s
* Tulo Trg: B001:n nouseva reuna (klo 11:00)
* Toiminta: 20s kuluttua laukaisusta lähtö → HIGH (T_DONE).
* Trig-ajastin: jatkuu loppuun asti vaikka B001 sammuisi.
* Lähtö → B004:n Trg-tuloon.
*
* B004 – Päällä-viiveajastin (Trig), 8s
* Tulo Trg: B003:n lähtö (nouseva reuna B003:n valmistuessa)
* Toiminta: 8s kuluttua lähtö → HIGH (T_DONE).
* Trig-ajastin: jatkuu loppuun asti vaikka B003 sammuisi.
* Lähtö haarautuu:
* 1) → Q3 suoraan (Putkiston erotus päälle)
* 2) → B006 AND-portin tuloon
*
* B006 – AND-portti (4 tuloa)
* Tulo 1: B003:n lähtö (b003_out)
* Tulo 2: B004:n lähtö (b004_out)
* Tulo 3: I1 – Käyntilupa
* Tulo 4: I1 – Käyntilupa (sama signaali kuin tulo 3)
* LOGO!:ssa käyttämätön tulo täytetään toistamalla
* olemassa oleva signaali. I1 AND I1 = I1, joten
* kaksinkertainen tulo ei muuta logiikkaa.
* Täysi logiikka: b003_out AND b004_out AND i1 AND i1
* Yksinkertaistettu: b006_out = b004_out AND i1
* (koska b004_out edellyttää jo b003_out:ia)
* Lähtö → B002:n Trg-tuloon.
*
* B002 – Päällä-viiveajastin (Trig), 8s
* Tulo Trg: B006:n nouseva reuna
* Toiminta: 8s kuluttua lähtö → HIGH (T_DONE).
* Trig-ajastin: jatkuu loppuun asti vaikka B006 sammuisi
* kesken (esim. käyntilupa poistetaan).
* Lähtö → käynnistää B005:n (puhdistuspulssin).
*
* B005 – Toistopulssi (pulssilohko)
* Tulo En: käynnistyy kun B002 valmistuu (T_DONE)
* Parametrit: 01:00s+ = 1s ON / 01:00s = 1s OFF
* Toiminta: toistaa 1s päällä / 1s pois itsenäisesti
* kunnes B007 nollaa syklin.
* Lähtö → Q4 (Puhdistuspulssi)
*
* B007 – Päällä-viiveajastin (Trig) takaisinkytkennällä, 2min
* Tulo Trg: B001:n nouseva reuna (klo 11:00)
* Tulo R: ei kytketty
* Takaisinkytketty: B007:n lähtö on kytketty takaisin
* omaan Trg-tuloonsa. LOGO!:ssa Trig käynnistyy nousevasta
* reunasta – koska lähtö pysyy HIGH koko viiveen ajan,
* uutta nousevaa reunaa ei synny eikä ajastin käynnisty
* uudelleen. Vaikutus on identtinen tavallisen Trig-
* ajastimen kanssa: lähtö HIGH 2min ajan, sitten LOW.
* Lähtö → Q1 (Irtikytkentä) ja Q2 (ASD Pysäytys)
* Syklin nollaus: kun 2min täyttyy, kaikki ajastimet
* nollataan → Q3 ja Q4 sammuvat samalla.
*
* ============================================================
* AIKAJANA (normaali toimintaketju, I1 päällä koko ajan)
* ============================================================
*
* t = 0s B001 nouseva reuna (klo 11:00)
* → Q1 PÄÄLLÄ (Irtikytkentä) B007 käynnistyy
* → Q2 PÄÄLLÄ (ASD Pysäytys) B007 käynnistyy
* → B003 käynnistyy (20s laskenta alkaa)
*
* t = 20s B003 valmis → B004 käynnistyy (8s laskenta alkaa)
*
* t = 28s B004 valmis
* → Q3 PÄÄLLÄ (Putkiston erotus)
* → B006 = HIGH (B004=1 AND I1=1)
* → B002 käynnistyy (8s laskenta alkaa)
*
* t = 36s B002 valmis → B005 käynnistyy
* → Q4 1s PÄÄLLÄ, 1s POIS, 1s PÄÄLLÄ ... (toistuu)
*
* t = 120s B007 viive täyttyy → koko sykli nollataan
* → Q1 POIS, Q2 POIS, Q3 POIS, Q4 POIS
* → Kaikki ajastimet T_IDLE
* → Valmis seuraavaan laukaisuun (seuraava
* arkipäivä klo 11:00)
*
* ============================================================
* RTC-MODUULI (DS3231)
* ============================================================
* USE_RTC 1: oikea kellonaika DS3231-moduulilta.
* Kytkentä: SDA → A4, SCL → A5, VCC → 3.3V/5V, GND → GND
* Kirjasto: "RTClib by Adafruit" (Arduino IDE Library Manager)
*
* USE_RTC 0: testausmoodi ilman RTC-moduulia.
* Laukaisee SIM_TRIGGER_MS ms kuluttua käynnistyksestä ja
* simuloi 2 min aktiivista aikajaksoa (vastaa 11:00–11:02).
* ============================================================
*/
// ============================================================
// KONFIGUROINTI
// ============================================================
#define USE_RTC 0 // 0 = simulaatio, 1 = DS3231 RTC
// Arduino-pinnit
#define PIN_I1 2 // Tulo: Käyntilupa (INPUT_PULLUP, LOW = päällä)
#define PIN_Q1 8 // Lähtö: Irtikytkentä
#define PIN_Q2 9 // Lähtö: ASD Pysäytys
#define PIN_Q3 10 // Lähtö: Putkiston erotus
#define PIN_Q4 11 // Lähtö: Puhdistuspulssi
// Ajastimet millisekunteina – vastaa kaavion arvoja
// LOGO!-formaatti SS:hhs = sekunnit:sadasosat (s-yksikkö)
// Esim. 20:00s = 20 sekuntia tasatasan = 20 000 ms
// B007 käyttää m-yksikköä: 02:00m = 2 minuuttia = 120 000 ms
#define B003_DELAY 20000UL // B003: 20:00s+ = 20 sekuntia
#define B004_DELAY 8000UL // B004: 08:00s+ = 8 sekuntia
#define B002_DELAY 8000UL // B002: 08:00s+ = 8 sekuntia
#define B005_ON_MS 1000UL // B005: 01:00s+ = 1 sekunti (ON-aika)
#define B005_OFF_MS 1000UL // B005: 01:00s = 1 sekunti (OFF-aika)
#define B007_DELAY 120000UL // B007: 02:00m+ = 2 minuuttia (m-yksikkö)
// Testausmoodi: viive käynnistyksestä ensimmäiseen laukaisuun
#define SIM_TRIGGER_MS 5000UL // 5 sekuntia
// ============================================================
// RTC-KIRJASTO (käännetään mukaan vain kun USE_RTC = 1)
// ============================================================
#if USE_RTC
#include <Wire.h>
#include <RTClib.h>
RTC_DS3231 rtc;
#endif
// ============================================================
// TYYPIT
// ============================================================
// Trig-ajastimen tilakone.
// Jokainen ajastin (B003, B004, B002, B007) käyttää tätä.
//
// T_IDLE = ei käynnissä, odottaa laukaisusignaalia
// T_RUNNING = ajastin käy, viive ei ole vielä täyttynyt
// T_DONE = viive täyttynyt, lähtö pysyvästi HIGH
// Nollautuu vain kun B007 nollaa koko syklin.
enum TimerState { T_IDLE, T_RUNNING, T_DONE };
// ============================================================
// TILAMUUTTUJAT
// ============================================================
// B001: edellinen tila nousevan reunan tunnistusta varten
bool b001_prev = false;
// B003: Trig-ajastin 20s
TimerState b003_state = T_IDLE;
unsigned long b003_start = 0;
// B004: Trig-ajastin 8s
TimerState b004_state = T_IDLE;
unsigned long b004_start = 0;
// B006: edellinen tila B002:n nousevan reunan tunnistusta varten
bool b006_prev = false;
// B002: Trig-ajastin 8s
TimerState b002_state = T_IDLE;
unsigned long b002_start = 0;
// B005: toistopulssi
bool b005_running = false; // true kun B002 on lauennut
bool b005_output = false; // pulssijännitteen nykyinen tila
unsigned long b005_last = 0; // edellisen tilamuutoksen aikaleima
// B007: Trig-ajastin 2min
TimerState b007_state = T_IDLE;
unsigned long b007_start = 0;
// Lähtöjen edelliset tilat – setOutput() käyttää muutoksen tunnistukseen
bool q1_prev = false;
bool q2_prev = false;
bool q3_prev = false;
bool q4_prev = false;
// Simulaatiotila: estetään toistuva laukaisu
#if !USE_RTC
bool sim_fired = false;
#endif
// ============================================================
// APUFUNKTIO: weekProgramActive()
// Palauttaa true kun viikko-ohjelma on aktiivinen.
//
// USE_RTC 1: tarkistaa DS3231:ltä onko Ma–Pe klo 11:00–11:02.
// USE_RTC 0: palauttaa true SIM_TRIGGER_MS:stä alkaen 2 min ajan.
// ============================================================
bool weekProgramActive() {
#if USE_RTC
DateTime now = rtc.now();
// dayOfTheWeek(): 0 = Su, 1 = Ma, 2 = Ti, 3 = Ke, 4 = To, 5 = Pe, 6 = La
uint8_t dow = now.dayOfTheWeek();
uint16_t minOfDay = now.hour() * 60 + now.minute();
if (dow < 1 || dow > 5) return false; // Viikonloppu → ei aktiivinen
return (minOfDay >= 660 && minOfDay < 662); // 11:00 = 660 min, 11:02 = 662 min
#else
unsigned long t = millis();
if (!sim_fired && t >= SIM_TRIGGER_MS) sim_fired = true;
if (sim_fired && t < SIM_TRIGGER_MS + 120000UL) return true;
return false;
#endif
}
// ============================================================
// APUFUNKTIO: setOutput()
// Kirjoittaa digitaalilähtöön vain kun tila muuttuu.
// Tulostaa muutoksen Serial Monitoriin (9600 baud) aikaleiman kanssa.
// ============================================================
void setOutput(uint8_t pin, bool newState, const char* label, bool &prevState) {
if (newState == prevState) return;
prevState = newState;
digitalWrite(pin, newState ? HIGH : LOW);
Serial.print(F("["));
Serial.print(millis());
Serial.print(F(" ms] "));
Serial.print(label);
Serial.println(newState ? F(" -> PAALLE") : F(" -> POIS"));
}
// ============================================================
// SETUP
// ============================================================
void setup() {
Serial.begin(9600);
Serial.println(F("=== Palokaapin ohjaus kaynnistetty ==="));
// I1: INPUT_PULLUP – kytkin GND:ään suljettu = LOW = käyntilupa päällä
pinMode(PIN_I1, INPUT_PULLUP);
// Kaikki lähdöt pois päältä aluksi
pinMode(PIN_Q1, OUTPUT); digitalWrite(PIN_Q1, LOW);
pinMode(PIN_Q2, OUTPUT); digitalWrite(PIN_Q2, LOW);
pinMode(PIN_Q3, OUTPUT); digitalWrite(PIN_Q3, LOW);
pinMode(PIN_Q4, OUTPUT); digitalWrite(PIN_Q4, LOW);
#if USE_RTC
Wire.begin();
if (!rtc.begin()) {
Serial.println(F("VIRHE: RTC-moduulia ei loydy! Tarkista SDA=A4, SCL=A5."));
while (true) delay(1000);
}
if (rtc.lostPower()) {
// RTC on menettänyt varmuuspariston tehon – aika on nollautunut.
// Aseta oikea aika ennen tuotantokäyttöä, esim:
// rtc.adjust(DateTime(2025, 6, 2, 10, 59, 50));
rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
Serial.println(F("HUOM: RTC-aika asetettu kaannoshetkeen – tarkista aika!"));
}
Serial.println(F("RTC OK."));
#else
Serial.print(F("Simulaatiomoodi: laukaisee "));
Serial.print(SIM_TRIGGER_MS / 1000);
Serial.println(F("s kuluttua kaynnistyksesta."));
#endif
}
// ============================================================
// PÄÄSILMUKKA
// ============================================================
void loop() {
unsigned long now = millis();
// ──────────────────────────────────────────────────────────
// TULOT
// ──────────────────────────────────────────────────────────
// I1: Käyntilupa. INPUT_PULLUP → kytkin GND:ään = LOW = päällä.
bool i1 = (digitalRead(PIN_I1) == LOW);
// DEBUG: tulosta I1:n tila sekunnin välein
static unsigned long last_debug = 0;
if (now - last_debug >= 1000) {
last_debug = now;
Serial.print(F("["));
Serial.print(now);
Serial.print(F(" ms] DEBUG: I1="));
Serial.print(i1 ? F("PAALLA") : F("POIS"));
Serial.print(F(" B003="));
Serial.print(b003_state == T_IDLE ? F("IDLE") : b003_state == T_RUNNING ? F("RUN") : F("DONE"));
Serial.print(F(" B004="));
Serial.print(b004_state == T_IDLE ? F("IDLE") : b004_state == T_RUNNING ? F("RUN") : F("DONE"));
Serial.print(F(" B002="));
Serial.print(b002_state == T_IDLE ? F("IDLE") : b002_state == T_RUNNING ? F("RUN") : F("DONE"));
Serial.print(F(" B005="));
Serial.print(b005_running ? (b005_output ? F("ON") : F("OFF")) : F("POIS"));
Serial.print(F(" Q4="));
Serial.println(digitalRead(PIN_Q4) ? F("HIGH") : F("LOW"));
bool i1 = (digitalRead(PIN_I1) == LOW);
// B001: Viikko-ohjelma. HIGH kun Ma–Pe klo 11:00–11:02.
bool b001 = weekProgramActive();
// ──────────────────────────────────────────────────────────
// B001: nousevan reunan tunnistus
//
// B001 pysyy HIGH koko 11:00–11:02 ajan (Pulse=N).
// Ajastimet käynnistetään vain kerran nousevasta reunasta
// (klo 11:00), ei joka silmukassa aikajakson aikana.
// ──────────────────────────────────────────────────────────
bool b001_rising = (b001 && !b001_prev);
b001_prev = b001;
if (b001_rising) {
Serial.println(F("B001: nouseva reuna (klo 11:00) -> kaynnistetaan ketju"));
// B003 käynnistyy B001:n nousevasta reunasta
if (b003_state == T_IDLE) {
b003_state = T_RUNNING;
b003_start = now;
Serial.println(F(" B003 kaynnistetty (20s)"));
}
// B007 käynnistyy B001:n nousevasta reunasta
if (b007_state == T_IDLE) {
b007_state = T_RUNNING;
b007_start = now;
Serial.println(F(" B007 kaynnistetty (2min)"));
}
}
// ──────────────────────────────────────────────────────────
// B007: Trig-ajastin 2min → Q1 (Irtikytkentä), Q2 (ASD Pysäytys)
//
// Käynnistyy B001:n nousevasta reunasta.
// Kaaviossa B007:n lähtö on takaisinkytketty omaan Trg-tuloonsa.
// Koska Trg reagoi vain nousevaan reunaan, ja lähtö pysyy HIGH
// koko viiveen ajan (ei uutta nousevaa reunaa), takaisinkytketty
// rakenne toimii kuten tavallinen Trig-ajastin: lähtö HIGH 2min,
// sitten LOW. Erillistä takaisinkytkentälogiikkaa ei tarvita.
//
// Kun 2min täyttyy: nollataan kaikki ajastimet → Q3 ja Q4
// sammuvat automaattisesti (b004_out ja b005_running → false).
// ──────────────────────────────────────────────────────────
bool b007_out = false;
if (b007_state == T_RUNNING) {
if (now - b007_start < B007_DELAY) {
b007_out = true; // Viive käynnissä → lähtö HIGH
} else {
// Viive täyttyi – nollaa koko sykli
b007_state = T_IDLE;
b003_state = T_IDLE;
b004_state = T_IDLE;
b002_state = T_IDLE;
b005_running = false;
b005_output = false;
b006_prev = false;
// b007_out jää falseksi → Q1, Q2, Q3, Q4 kaikki sammuvat
Serial.println(F("B007: 2min tayttyi -> sykli nollattu, kaikki lahdot POIS"));
}
}
// ──────────────────────────────────────────────────────────
// B003: Trig-ajastin 20s
//
// Käynnistetty: B001:n nousevasta reunasta (yllä).
// Lähtö HIGH (T_DONE) kun 20s on kulunut.
// Lähtö → B004:n Trg-tuloon.
// ──────────────────────────────────────────────────────────
bool b003_out = false;
if (b003_state == T_RUNNING) {
if (now - b003_start >= B003_DELAY) {
b003_state = T_DONE;
Serial.println(F("B003: 20s valmis -> B004 kaynnistyy"));
// Käynnistä B004 heti B003:n valmistuessa
if (b004_state == T_IDLE) {
b004_state = T_RUNNING;
b004_start = now;
Serial.println(F(" B004 kaynnistetty (8s)"));
}
}
}
if (b003_state == T_DONE) b003_out = true;
(void)b003_out; // Käytetään vain B004:n käynnistykseen (yllä)
// ──────────────────────────────────────────────────────────
// B004: Trig-ajastin 8s
//
// Käynnistetty: B003:n valmistuessa (yllä).
// Lähtö HIGH (T_DONE) kun 8s on kulunut.
// Lähtö haarautuu:
// 1) → Q3 suoraan (Putkiston erotus päälle)
// 2) → B006 AND-portin tuloon
// ──────────────────────────────────────────────────────────
bool b004_out = false;
if (b004_state == T_RUNNING) {
if (now - b004_start >= B004_DELAY) {
b004_state = T_DONE;
Serial.println(F("B004: 8s valmis -> Q3 paalle, B006 tarkistetaan"));
}
}
if (b004_state == T_DONE) b004_out = true;
// ──────────────────────────────────────────────────────────
// B006: AND-portti (4 tuloa kaavion mukaan)
//
// Tulo 1: B003:n lähtö (b003_out)
// Tulo 2: B004:n lähtö (b004_out)
// Tulo 3: I1 – Käyntilupa
// Tulo 4: I1 – Käyntilupa (sama signaali uudelleen)
//
// LOGO!:ssa 4-tuloisessa AND-lohkossa käyttämätön tulo
// täytetään toistamalla jokin käytössä oleva signaali.
// Tässä I1 on kytketty tuloihin 3 ja 4. Koska I1 AND I1 = I1,
// kaksinkertainen tulo ei muuta logiikkaa.
//
// Täysi logiikka: b003_out AND b004_out AND i1 AND i1
// Yksinkertaistettu (b004_out edellyttää jo b003_out:ia,
// ja I1 AND I1 = I1):
// b006_out = b004_out AND i1
// ──────────────────────────────────────────────────────────
bool b006_out = (b004_out && i1);
// ──────────────────────────────────────────────────────────
// B002: Trig-ajastin 8s
//
// Käynnistyy B006:n NOUSEVASTA REUNASTA.
// Trig-ajastin: jatkuu loppuun asti vaikka B006 laskisi
// (esim. käyntilupa poistetaan kesken ajon).
// Lähtö HIGH (T_DONE) kun 8s on kulunut → käynnistää B005:n.
// ──────────────────────────────────────────────────────────
bool b006_rising = (b006_out && !b006_prev);
b006_prev = b006_out;
if (b006_rising && b002_state == T_IDLE) {
b002_state = T_RUNNING;
b002_start = now;
Serial.println(F("B002: kaynnistetty (8s)"));
}
if (b002_state == T_RUNNING) {
if (now - b002_start >= B002_DELAY) {
b002_state = T_DONE;
b005_running = true;
b005_output = true; // Aloitetaan ON-vaiheesta
b005_last = now;
Serial.println(F("B002: 8s valmis -> B005 kaynnistyy"));
}
}
// ──────────────────────────────────────────────────────────
// B005: Toistopulssi 1s ON / 1s OFF → Q4 (Puhdistuspulssi)
//
// Käynnistyy kun B002 valmistuu (b005_running = true).
// Toistaa 1s päällä / 1s pois itsenäisesti ajastimien
// avulla – ei tarvitse jatkuvaa Enable-signaalia.
// Pysähtyy kun B007 nollaa syklin (b005_running = false).
// ──────────────────────────────────────────────────────────
if (b005_running) {
unsigned long elapsed = now - b005_last;
if (b005_output && elapsed >= B005_ON_MS) {
b005_output = false; // ON-vaihe päättyy → siirry OFF:iin
b005_last = now;
Serial.print(F("["));
Serial.print(now);
Serial.println(F(" ms] PIN11 (Q4) -> POIS"));
} else if (!b005_output && elapsed >= B005_OFF_MS) {
b005_output = true; // OFF-vaihe päättyy → siirry ON:iin
b005_last = now;
Serial.print(F("["));
Serial.print(now);
Serial.println(F(" ms] PIN11 (Q4) -> PAALLE"));
}
}
// ──────────────────────────────────────────────────────────
// LÄHDÖT
// setOutput() kirjoittaa pinnille vain kun tila muuttuu ja
// tulostaa muutoksen Serial Monitoriin aikaleiman kanssa.
// ──────────────────────────────────────────────────────────
// Q1: Irtikytkentä
// HIGH B007:n 2min viiveen ajan, LOW syklin nollauksen jälkeen.
setOutput(PIN_Q1, b007_out, "Q1 Irtikytkenta", q1_prev);
// Q2: ASD Pysäytys
// Sama signaali kuin Q1 – molemmat ohjataan B007:n lähdöltä.
setOutput(PIN_Q2, b007_out, "Q2 ASD Pysaytys", q2_prev);
// Q3: Putkiston erotus
// HIGH kun B004 on valmis (T_DONE).
// LOW kun B007 nollaa syklin (b004_state → T_IDLE → b004_out = false).
setOutput(PIN_Q3, b004_out, "Q3 Putkiston erotus", q3_prev);
// Q4: Puhdistuspulssi
// 1s/1s toistopulssi B005:ltä, käynnistyy 36s syklin alusta.
// LOW kun B007 nollaa syklin (b005_running = false).
setOutput(PIN_Q4, (b005_running && b005_output), "Q4 Puhdistuspulssi", q4_prev);
// 10ms viive – ei vaikuta ajastintarkkuuteen koska kaikki
// ajat lasketaan millis():n avulla, ei delay():n varaan.
delay(10);
}
/*
* ============================================================
* KYTKENTÄOHJE
* ============================================================
*
* TULO:
* D2 I1 – Käyntilupa (A/0-kytkin)
* Kytke kytkin D2:n ja GND:n välille.
* Kytkin auki = HIGH (INPUT_PULLUP) = käyntilupa POIS
* Kytkin kiinni = LOW = käyntilupa PÄÄLLÄ
*
* LÄHDÖT (HIGH = aktiivinen):
* D8 Q1 – Irtikytkentä → relekortti tai optoerotus
* D9 Q2 – ASD Pysäytys → relekortti tai optoerotus
* D10 Q3 – Putkiston erotus → relekortti tai optoerotus
* D11 Q4 – Puhdistuspulssi → relekortti tai optoerotus
*
* RTC DS3231 (USE_RTC = 1):
* SDA → A4, SCL → A5, VCC → 3.3V tai 5V, GND → GND
* Kirjasto: "RTClib by Adafruit" (Arduino Library Manager)
*
* ============================================================
*/
Rele2 ASD Pysäytys
Rele1 irtikytkentä
Rele3 Putkiston erotus
Rele4 Puhdistuspulssi