Chytrý zavlažovací systém ze senzorů a Arduina

Automatický zavlažovací systém řízený Arduinem dokáže ušetřit čas, vodu i vaše rostliny. V tomto projektu si postavíme kompletní IoT systém s mobilním ovládáním.
Přehled projektu
Co systém umí
- Měření vlhkosti půdy na více místech
- Automatické zavlažování podle nastaveného programu
- Dálkové ovládání přes WiFi
- Mobilní aplikaci pro monitoring
- Záložní ruční ovládání
- Ochranu proti přelití
Komponenty a cena
Arduino ESP32: 400 Kč
Čidla vlhkosti (5×): 250 Kč
Solenoidové ventily (4×): 800 Kč
Čerpadlo 12V: 300 Kč
Relé moduly (4×): 200 Kč
Napájení 12V/24V: 250 Kč
Hadice a konektory: 400 Kč
Krabička IP65: 150 Kč
Displej OLED: 100 Kč
Různé (rezistory atd.): 150 Kč
------------------------
Celkem: 3000 Kč
Schéma zapojení
Hlavní řídící jednotka (ESP32)
ESP32 piny:
GPIO 2 → LED status
GPIO 4 → Tlačítko START/STOP
GPIO 21 → SDA (I2C displej)
GPIO 22 → SCL (I2C displej)
GPIO 32 → Čidlo vlhkosti 1
GPIO 33 → Čidlo vlhkosti 2
GPIO 34 → Čidlo vlhkosti 3
GPIO 35 → Čidlo vlhkosti 4
GPIO 36 → Čidlo vlhkosti 5
GPIO 16 → Relé čerpadlo
GPIO 17 → Relé ventil 1
GPIO 5 → Relé ventil 2
GPIO 18 → Relé ventil 3
GPIO 19 → Relé ventil 4
Napájení
- ESP32: 5V z USB nebo external
- Relé moduly: 5V z napájecího zdroje
- Čerpadlo a ventily: 12V/24V podle typu
Kód pro Arduino IDE
Základní knihovny
#include <WiFi.h>
#include <WebServer.h>
#include <ArduinoJson.h>
#include <Wire.h>
#include <SSD1306Wire.h>
#include <Preferences.h>
Hlavní kód systému
// Definice pinů
#define PUMP_PIN 16
#define VALVE1_PIN 17
#define VALVE2_PIN 5
#define VALVE3_PIN 18
#define VALVE4_PIN 19
#define MOISTURE1_PIN 32
#define MOISTURE2_PIN 33
#define MOISTURE3_PIN 34
#define MOISTURE4_PIN 35
#define MOISTURE5_PIN 36
#define BUTTON_PIN 4
#define LED_PIN 2
// WiFi konfigurace
const char* ssid = "VasaWiFiSit";
const char* password = "VaseHeslo";
// Webserver na portu 80
WebServer server(80);
// OLED displej 128x64
SSD1306Wire display(0x3c, 21, 22);
// Preferences pro uložení nastavení
Preferences preferences;
// Globální proměnné
struct IrrigationZone {
int valvePin;
int moisturePin;
int moistureThreshold;
int wateringDuration;
bool isActive;
String name;
};
IrrigationZone zones[4] = {
{VALVE1_PIN, MOISTURE1_PIN, 30, 5000, false, "Rajčata"},
{VALVE2_PIN, MOISTURE2_PIN, 25, 3000, false, "Salát"},
{VALVE3_PIN, MOISTURE3_PIN, 35, 7000, false, "Květiny"},
{VALVE4_PIN, MOISTURE4_PIN, 40, 4000, false, "Bylinky"}
};
bool systemActive = false;
bool manualMode = false;
unsigned long lastCheck = 0;
const unsigned long CHECK_INTERVAL = 30000; // 30 sekund
void setup() {
Serial.begin(115200);
// Inicializace pinů
pinMode(PUMP_PIN, OUTPUT);
pinMode(LED_PIN, OUTPUT);
pinMode(BUTTON_PIN, INPUT_PULLUP);
for(int i = 0; i < 4; i++) {
pinMode(zones[i].valvePin, OUTPUT);
digitalWrite(zones[i].valvePin, LOW);
}
digitalWrite(PUMP_PIN, LOW);
// Inicializace displeje
display.init();
display.clear();
display.setFont(ArialMT_Plain_16);
display.drawString(0, 0, "Zavlažovač");
display.drawString(0, 20, "Spouštím...");
display.display();
// Připojení k WiFi
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(1000);
Serial.println("Připojuji k WiFi...");
}
Serial.println("WiFi připojeno!");
Serial.print("IP adresa: ");
Serial.println(WiFi.localIP());
// Inicializace webserveru
setupWebServer();
server.begin();
// Načtení uložených nastavení
loadSettings();
updateDisplay();
}
void loop() {
server.handleClient();
// Kontrola tlačítka
if (digitalRead(BUTTON_PIN) == LOW) {
delay(50); // debounce
if (digitalRead(BUTTON_PIN) == LOW) {
toggleSystem();
while(digitalRead(BUTTON_PIN) == LOW) delay(10);
}
}
// Automatická kontrola vlhkosti
if (systemActive && !manualMode &&
(millis() - lastCheck > CHECK_INTERVAL)) {
checkMoistureAndWater();
lastCheck = millis();
}
// Aktualizace displeje každých 5 sekund
static unsigned long lastDisplayUpdate = 0;
if (millis() - lastDisplayUpdate > 5000) {
updateDisplay();
lastDisplayUpdate = millis();
}
delay(100);
}
void checkMoistureAndWater() {
for (int i = 0; i < 4; i++) {
if (!zones[i].isActive) continue;
int moisture = readMoisture(zones[i].moisturePin);
Serial.printf("Zóna %d (%s): vlhkost %d%%\n",
i+1, zones[i].name.c_str(), moisture);
if (moisture < zones[i].moistureThreshold) {
waterZone(i);
}
}
}
int readMoisture(int pin) {
// Čtení analogové hodnoty a převod na procenta
int rawValue = analogRead(pin);
// Kalibrace: 4095 = suchá půda (0%), 1500 = mokrá půda (100%)
int moisture = map(rawValue, 4095, 1500, 0, 100);
return constrain(moisture, 0, 100);
}
void waterZone(int zoneIndex) {
Serial.printf("Zavlažuji zónu %d (%s)\n",
zoneIndex+1, zones[zoneIndex].name.c_str());
// Zapnout čerpadlo
digitalWrite(PUMP_PIN, HIGH);
delay(500); // Počkat na vytvoření tlaku
// Zapnout ventil
digitalWrite(zones[zoneIndex].valvePin, HIGH);
// Blikat LED během zavlažování
unsigned long startTime = millis();
while (millis() - startTime < zones[zoneIndex].wateringDuration) {
digitalWrite(LED_PIN, !digitalRead(LED_PIN));
delay(200);
}
// Vypnout ventil a čerpadlo
digitalWrite(zones[zoneIndex].valvePin, LOW);
delay(200);
digitalWrite(PUMP_PIN, LOW);
digitalWrite(LED_PIN, LOW);
Serial.printf("Zóna %d zavlažena\n", zoneIndex+1);
}
void setupWebServer() {
// Hlavní stránka
server.on("/", handleRoot);
// API endpointy
server.on("/api/status", handleStatus);
server.on("/api/water", handleManualWater);
server.on("/api/settings", handleSettings);
server.on("/api/toggle", handleToggle);
// CORS hlavičky
server.enableCORS(true);
}
void handleRoot() {
String html = getWebInterface();
server.send(200, "text/html", html);
}
void handleStatus() {
DynamicJsonDocument doc(1024);
doc["systemActive"] = systemActive;
doc["manualMode"] = manualMode;
doc["ip"] = WiFi.localIP().toString();
JsonArray zonesArray = doc.createNestedArray("zones");
for (int i = 0; i < 4; i++) {
JsonObject zone = zonesArray.createNestedObject();
zone["id"] = i;
zone["name"] = zones[i].name;
zone["moisture"] = readMoisture(zones[i].moisturePin);
zone["threshold"] = zones[i].moistureThreshold;
zone["active"] = zones[i].isActive;
zone["duration"] = zones[i].wateringDuration;
}
String response;
serializeJson(doc, response);
server.send(200, "application/json", response);
}
void updateDisplay() {
display.clear();
// Hlavní stav
display.setFont(ArialMT_Plain_16);
String status = systemActive ? "AKTIVNÍ" : "VYPNUTO";
display.drawString(0, 0, status);
// IP adresa
display.setFont(ArialMT_Plain_10);
display.drawString(0, 20, WiFi.localIP().toString());
// Vlhkosti
for (int i = 0; i < 4; i++) {
int moisture = readMoisture(zones[i].moisturePin);
String line = zones[i].name + ": " + String(moisture) + "%";
display.drawString(0, 32 + i*8, line);
}
display.display();
}
String getWebInterface() {
return R"(
<!DOCTYPE html>
<html>
<head>
<title>Chytrý zavlažovač</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<style>
body { font-family: Arial; margin: 20px; }
.zone { border: 1px solid #ccc; margin: 10px 0; padding: 15px; }
.button { background: #4CAF50; color: white; padding: 10px 20px;
border: none; cursor: pointer; margin: 5px; }
.button:hover { background: #45a049; }
.status { font-size: 18px; margin: 10px 0; }
.moisture { font-size: 16px; font-weight: bold; }
</style>
</head>
<body>
<h1>🌱 Chytrý zavlažovací systém</h1>
<div class="status" id="systemStatus">Načítám...</div>
<button class="button" onclick="toggleSystem()">Zapnout/Vypnout</button>
<h2>Zóny zavlažování</h2>
<div id="zones"></div>
<script>
function updateStatus() {
fetch('/api/status')
.then(response => response.json())
.then(data => {
document.getElementById('systemStatus').innerHTML =
'Systém: ' + (data.systemActive ? 'AKTIVNÍ' : 'VYPNUTÝ');
let zonesHTML = '';
data.zones.forEach(zone => {
zonesHTML += `
<div class="zone">
<h3>${zone.name}</h3>
<div class="moisture">Vlhkost: ${zone.moisture}%</div>
<div>Práh: ${zone.threshold}%</div>
<div>Doba: ${zone.duration/1000}s</div>
<button class="button" onclick="waterZone(${zone.id})">
Zavlažit nyní
</button>
</div>
`;
});
document.getElementById('zones').innerHTML = zonesHTML;
});
}
function toggleSystem() {
fetch('/api/toggle', {method: 'POST'})
.then(() => updateStatus());
}
function waterZone(zoneId) {
fetch('/api/water', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({zone: zoneId})
}).then(() => alert('Zavlažování spuštěno!'));
}
// Aktualizace každých 5 sekund
setInterval(updateStatus, 5000);
updateStatus();
</script>
</body>
</html>
)";
}
Instalace a nastavení
Příprava hardwaru
Sestavení řídící skříňky
- Umístění ESP32 do voděodolné krabičky
- Instalace displeje na víko
- Průchodky pro kabely
Pokládka senzorů
- Umístění čidel vlhkosti 10-15 cm pod povrch
- Ochrana před přímým kontaktem s vodou
- Kalibrace podle typu půdy
Instalace ventilů
- Umístění za hlavní uzávěr vody
- Kontrola průtoku a tlaku
- Test všech spojů
Software a konfigurace
// WiFi nastavení v kódu
const char* ssid = "VasaWiFiSit";
const char* password = "VaseHeslo";
// Kalibrace čidel vlhkosti
const int DRY_VALUE = 4095; // suchá půda
const int WET_VALUE = 1500; // mokrá půda
Mobilní aplikace (volitelně)
React Native aplikace
Pro pokročilé uživatele je možné vytvořit mobilní aplikaci:
// Základní API volání
const API_BASE = 'http://192.168.1.100';
const getStatus = async () => {
const response = await fetch(`${API_BASE}/api/status`);
return response.json();
};
const waterZone = async (zoneId) => {
await fetch(`${API_BASE}/api/water`, {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({zone: zoneId})
});
};
Rozšíření systému
Pokročilé funkce
- Meteorologická data: Integrace s weather API
- Kamerový monitoring: ESP32-CAM pro sledování rostlin
- Dešťový senzor: Vypnutí při dešti
- Flowmetr: Měření spotřeby vody
- Solární napájení: Nezávislé napájení systému
Integrace s chytrou domácností
// MQTT pro Home Assistant
#include <PubSubClient.h>
WiFiClient espClient;
PubSubClient client(espClient);
void publishToHA(String topic, String payload) {
client.publish(("homeassistant/sensor/irrigation/" + topic).c_str(),
payload.c_str());
}
Údržba a řešení problémů
Pravidelná údržba
- Týdně: Kontrola funkčnosti čidel
- Měsíčně: Čištění filtrů ventilů
- Sezónně: Kalibrace senzorů vlhkosti
- Ročně: Výměna těsnění
Časté problémy
❌ Falešné čtení vlhkosti ✅ Čištění elektrod senzorů
❌ Nefunkční ventily
✅ Kontrola napájení a průchodnosti
❌ WiFi výpadky ✅ Implementace offline režimu
Náklady vs. komerční řešení
Srovnání s komerčními systémy
DIY Arduino systém: 3 000 Kč
Komerční systém (basic): 15 000 Kč
Profesionální instalace: 35 000 Kč
Úspora: 80-90%
ROI (návratnost investice)
- Úspora vody: 30-50%
- Úspora času: 2-3 hodiny týdně
- Lepší růst rostlin: nezaměřitelné
- Návratnost: 1-2 sezóny
Chytrý zavlažovací systém na bázi Arduina je ideální projekt pro kutily, kteří chtějí kombinovat elektroniku s praktickou aplikací. Systém lze postupně rozšiřovat a přizpůsobovat konkrétním potřebám.
Tip pro začátečníky: Začněte s jednou zónou a postupně přidávejte další podle zkušeností a potřeb.