/**
* Arduino Nano + Ethernet + MQTT
* Version mit STATISCHER IP und konfliktfreier Pin-Belegung.
* PUMPE: Pin 3 | LED: Pin 2
*/
#include <SPI.h>
#include <Ethernet.h>
#include <PubSubClient.h>
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
// --- NETZWERK & MQTT KONFIGURATION ---
byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };
// Statische IP-Konfiguration
IPAddress ip(192, 168, 1, 100);
IPAddress dns(192, 168, 1, 1);
IPAddress gateway(192, 168, 1, 1);
IPAddress subnet(255, 255, 255, 0);
IPAddress mqtt_server(192, 168, 1, 50);
const char* mqtt_user = "dein_user";
const char* mqtt_pass = "dein_passwort";
const char* device_id = "pumpe_nano_01";
// --- LCD Konfiguration ---
LiquidCrystal_I2C lcd(0x27, 16, 2);
// --- Hardware Pins (KONFLIKTFREI) ---
const int triggerPin = 5;
const int echoPin = 6;
const int resetTasterPin = 4;
const int pumpenPin = 3; // NEU: Pin 3 (kein SPI-Konflikt mehr)
const int autoModusPin = 7;
const int handPumpenPin = 8;
const int sirenenPin = 9;
const int alarmLedPin = 2; // NEU: Pin 2 (kein SPI-Konflikt mehr)
// --- Zustands-Definition (FSM) ---
enum PumpenZustand { INITIALISIERUNG = 0, AUTO_RUHE = 1, AUTO_PUMPEN = 2, MANUELL = 3, FEHLER = 4 };
PumpenZustand aktuellerZustand = INITIALISIERUNG;
uint16_t startPegelOben = 20;
uint16_t stopPegelUnten = 100;
unsigned long startZeitPumpen = 0;
const unsigned long maximaleLaufzeit = 15000;
unsigned long letzteMessungZeit = 0;
const unsigned long messIntervall = 2000;
unsigned long lastReconnectAttempt = 0;
EthernetClient ethClient;
PubSubClient client(ethClient);
/**
* MQTT Discovery für Home Assistant
*/
void sendDiscovery() {
client.publish("homeassistant/sensor/pumpe1/dist/config", "{\"name\":\"Pegelstand\",\"stat_t\":\"ha/pumpe1/dist\",\"unit_of_meas\":\"cm\",\"dev\":{\"ids\":[\"p1\"],\"name\":\"Pumpensteuerung\"}}", true);
client.publish("homeassistant/sensor/pumpe1/stat/config", "{\"name\":\"Betriebszustand\",\"stat_t\":\"ha/pumpe1/stat\",\"dev\":{\"ids\":[\"p1\"]}}", true);
client.publish("homeassistant/binary_sensor/pumpe1/pump/config", "{\"name\":\"Pumpe Status\",\"stat_t\":\"ha/pumpe1/pump\",\"dev\":{\"ids\":[\"p1\"]}}", true);
client.subscribe("ha/pumpe1/setStart");
client.subscribe("ha/pumpe1/setStop");
}
void callback(char* topic, byte* payload, unsigned int length) {
char msg[10];
if (length > 9) length = 9;
memcpy(msg, payload, length);
msg[length] = '\0';
int val = atoi(msg);
if (strstr(topic, "setStart")) startPegelOben = val;
if (strstr(topic, "setStop")) stopPegelUnten = val;
}
boolean reconnect() {
if (client.connect(device_id, mqtt_user, mqtt_pass, "ha/pumpe1/avail", 1, true, "offline")) {
client.publish("ha/pumpe1/avail", "online", true);
sendDiscovery();
}
return client.connected();
}
long einzelMessung() {
digitalWrite(triggerPin, LOW);
delayMicroseconds(2);
digitalWrite(triggerPin, HIGH);
delayMicroseconds(10);
digitalWrite(triggerPin, LOW);
long dauer = pulseIn(echoPin, HIGH, 30000);
if (dauer == 0) return 999; // Korrigiert: hieß vorher duration
return (dauer * 0.0343) / 2;
}
long messeGefiltert() {
long werte[5];
for (int i = 0; i < 5; i++) { werte[i] = einzelMessung(); delay(10); }
for (int i = 0; i < 4; i++) {
for (int j = i + 1; j < 5; j++) {
if (werte[i] > werte[j]) { long temp = werte[i]; werte[i] = werte[j]; werte[j] = temp; }
}
}
return werte[2];
}
void updateLCD(long distanz, PumpenZustand zustand) {
static unsigned long lastLCDUpdate = 0;
if (millis() - lastLCDUpdate < 500) return;
lastLCDUpdate = millis();
lcd.setCursor(0, 0);
switch (zustand) {
case INITIALISIERUNG: lcd.print(F("Booting... ")); break;
case AUTO_RUHE: lcd.print(F("A: Bereit ")); break;
case AUTO_PUMPEN: lcd.print(F("A: PUMPEN! ")); break;
case MANUELL: lcd.print(F("H: Manuell ")); break;
case FEHLER: lcd.print(F("ALARM: Timeout ")); break;
}
lcd.setCursor(0, 1);
if (zustand == MANUELL) {
lcd.print(digitalRead(handPumpenPin) == LOW ? F("Pumpe: AN ") : F("Pumpe: AUS "));
} else {
lcd.print(F("Pegel: ")); lcd.print(distanz); lcd.print(F(" cm "));
}
}
void setup() {
Ethernet.begin(mac, ip, dns, gateway, subnet);
delay(1500);
lcd.init();
lcd.backlight();
pinMode(triggerPin, OUTPUT);
pinMode(echoPin, INPUT);
pinMode(resetTasterPin, INPUT_PULLUP);
pinMode(autoModusPin, INPUT_PULLUP);
pinMode(handPumpenPin, INPUT_PULLUP);
pinMode(pumpenPin, OUTPUT);
pinMode(sirenenPin, OUTPUT);
pinMode(alarmLedPin, OUTPUT);
client.setServer(mqtt_server, 1883);
client.setCallback(callback);
}
void loop() {
if (!client.connected()) {
unsigned long now = millis();
if (now - lastReconnectAttempt > 5000) {
lastReconnectAttempt = now;
if (reconnect()) lastReconnectAttempt = 0;
}
} else {
client.loop();
}
static long distanz = 100;
if (millis() - letzteMessungZeit >= messIntervall) {
distanz = messeGefiltert();
if (client.connected()) {
client.publish("ha/pumpe1/dist", String(distanz).c_str());
const char* statusText;
switch(aktuellerZustand) {
case AUTO_RUHE: statusText = "Bereit"; break;
case AUTO_PUMPEN: statusText = "Pumpen"; break;
case MANUELL: statusText = "Manuell"; break;
case FEHLER: statusText = "Fehler"; break;
default: statusText = "Init";
}
client.publish("ha/pumpe1/stat", statusText);
client.publish("ha/pumpe1/pump", digitalRead(pumpenPin) ? "ON" : "OFF");
}
letzteMessungZeit = millis();
}
bool resetGedrueckt = (digitalRead(resetTasterPin) == LOW);
bool schalterAufAuto = (digitalRead(autoModusPin) == LOW);
unsigned long aktuelleZeit = millis();
updateLCD(distanz, aktuellerZustand);
// --- Zustandsmaschine ---
switch (aktuellerZustand) {
case INITIALISIERUNG:
digitalWrite(pumpenPin, LOW);
digitalWrite(sirenenPin, LOW);
digitalWrite(alarmLedPin, LOW);
aktuellerZustand = schalterAufAuto ? AUTO_RUHE : MANUELL;
break;
case AUTO_RUHE:
digitalWrite(pumpenPin, LOW);
digitalWrite(sirenenPin, LOW);
digitalWrite(alarmLedPin, LOW);
if (!schalterAufAuto) aktuellerZustand = MANUELL;
else if (distanz > 0 && distanz < startPegelOben) {
aktuellerZustand = AUTO_PUMPEN;
startZeitPumpen = aktuelleZeit;
}
break;
case AUTO_PUMPEN:
digitalWrite(pumpenPin, HIGH);
digitalWrite(alarmLedPin, HIGH); // LED leuchtet bei Pumpenbetrieb
if (!schalterAufAuto) aktuellerZustand = MANUELL;
else if (distanz > stopPegelUnten) aktuellerZustand = AUTO_RUHE;
else if (aktuelleZeit - startZeitPumpen > maximaleLaufzeit) aktuellerZustand = FEHLER;
break;
case MANUELL:
digitalWrite(sirenenPin, LOW);
if (digitalRead(handPumpenPin) == LOW) {
digitalWrite(pumpenPin, HIGH);
digitalWrite(alarmLedPin, HIGH);
} else {
digitalWrite(pumpenPin, LOW);
digitalWrite(alarmLedPin, LOW);
}
if (schalterAufAuto) aktuellerZustand = AUTO_RUHE;
break;
case FEHLER:
digitalWrite(pumpenPin, LOW);
if ((millis() / 500) % 2 == 0) {
digitalWrite(sirenenPin, HIGH);
digitalWrite(alarmLedPin, HIGH);
} else {
digitalWrite(sirenenPin, LOW);
digitalWrite(alarmLedPin, LOW);
}
if (resetGedrueckt || !schalterAufAuto) aktuellerZustand = INITIALISIERUNG;
break;
}
}