diff --git a/.vscode/settings.json b/.vscode/settings.json index 753944e..89b615e 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,5 +1,7 @@ { "cSpell.words": [ + "Huus", + "Lebennig", "RCLK", "SRCLK", "SROE", diff --git a/include/config.h b/include/config.h index 5a8e5c9..d34dd09 100644 --- a/include/config.h +++ b/include/config.h @@ -14,4 +14,5 @@ #define INT_HALL_PIN 2 #define VALVE_COUNT 12 -const uint8_t valvePins[VALVE_COUNT] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}; \ No newline at end of file +const uint8_t valvePins[VALVE_COUNT] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}; +const char mainTopic[] = "LebennigHuus/Garden/Irrigation"; \ No newline at end of file diff --git a/krams/pump.json b/krams/pump.json new file mode 100644 index 0000000..6f9716e --- /dev/null +++ b/krams/pump.json @@ -0,0 +1,15 @@ +{ + "pump": { + "status": 1, + "duration": 12, + "pressure": 12.5, + "flow": 120 + } +} + +{ + "pump": { + "mode": 1, + "duration": 25 + } +} \ No newline at end of file diff --git a/krams/valve.json b/krams/valve.json new file mode 100644 index 0000000..2825d98 --- /dev/null +++ b/krams/valve.json @@ -0,0 +1,6 @@ +{ + "valve": { + "duration": 2, + "number": 3 + } + } \ No newline at end of file diff --git a/lib/Pump/pump.cpp b/lib/Pump/pump.cpp index 0387553..4072f8e 100644 --- a/lib/Pump/pump.cpp +++ b/lib/Pump/pump.cpp @@ -1,74 +1,62 @@ #include "pump.h" -Pump::Pump(uint8_t pumpPin, uint8_t pressurePin, uint8_t flowPin, uint8_t errorLedPin) { +Pump::Pump(uint8_t pumpPin, uint8_t pressurePin, uint8_t flowPin, uint8_t ledPin) { this->pumpPin = pumpPin; this->pressurePin = pressurePin; this->flowPin = flowPin; - this->errorLedPin = errorLedPin; + this->ledPin = ledPin; pinMode(this->pumpPin, OUTPUT); - pinMode(this->errorLedPin, OUTPUT); + pinMode(this->ledPin, OUTPUT); pinMode(this->pressurePin, INPUT); // Hall flow sensor? digitalWrite(this->pumpPin, LOW); - digitalWrite(this->errorLedPin, LOW); + digitalWrite(this->ledPin, LOW); } void Pump::loop() { switch (this->state) { case State::Off : - break; case State::On : - + this->timerStandby = false; + this->checkSensors(); break; case State::Timer : - + this->stateTimer(); break; case State::Standby : - + this->stateStandby(); break; case State::Error : - if (millis() - this->startTime > Pump::blinkTime) { - this->startTime = millis(); - if (this->ledState) { - this->ledState = false; - digitalWrite(this->errorLedPin, LOW); - } else { - this->ledState = true; - digitalWrite(this->errorLedPin, HIGH); - } - } - break; - - default: + this->stateError(); break; } } void Pump::activate(uint32_t duration) { - this->startTime = millis(); if (duration) { - this->state = State::Timer; + this->changeState(State::Timer); this->duration = duration; } else { - this->state = State::On; + this->changeState(State::On); } } void Pump::stop() { - if (this->state != State::Error) - this->state = State::Off; + this->changeState(State::Off); } void Pump::clearError() { - if (this->state == State::Error) + if (this->state == State::Error) { this->state = State::Off; + this->changeState(State::Off); + } } uint16_t Pump::getPressure() { @@ -76,13 +64,151 @@ uint16_t Pump::getPressure() { } uint32_t Pump::getRunningTime() { - return millis() -this->startTime; + if (this->state == State::Off) + return 0; + + return (millis() - this->startTime) / 1000; } uint16_t Pump::getFlow() { return 0; } -void Pump::checkSensors() { +uint16_t Pump::getStatus() { + switch (this->state) { + case State::Off : + return 0; + break; + + case State::On : + return 1; + break; + + case State::Timer : + return 2; + break; + + case State::Standby : + return 3; + break; + + case State::Error : + return -1; + break; + + default: + return -2; + break; + } +} + +bool Pump::checkSensors() { + if (millis() - this->lastMillisCheckSensor < Pump::delayCheckSensor) + return; + this->lastMillisCheckSensor = millis(); -} \ No newline at end of file + //TODO: read pressure + //TODO: read flow + + if (this->flow) { + return true; + } + + if (!this->pressure) { + this->changeState(State::Error); + return false; + } + + if (pressure > Pump::maxPressure) { + if (this->maxPressureStart == 0) + this->maxPressureStart = millis(); + else if (millis() - this->maxPressureStart > Pump::maxMaxPressureTime) + this->changeState(State::Standby); + } + + return true; +} + +void Pump::handlePins(bool state) { + digitalWrite(this->pumpPin, state); + digitalWrite(this->ledPin, state); + if (state) { + this->timerStandby = false; + this->startTime = millis(); + this->maxPressureStart = 0; + } +} + +void Pump::changeState(State state) { + if (this->state == State::Error) + return; + + if (this->state == state) + return; + + switch (state) { + case State::Off : + this->handlePins(false); + this->pressure = 0; + this->flow = 0; + break; + + case State::On : + this->handlePins(true); + break; + + case State::Timer : + this->handlePins(true); + break; + + case State::Standby : + if (this->state == State::Timer) + this->timerStandby = true; + this->handlePins(false); + break; + + case State::Error : + this->handlePins(false); + break; + + default: + break; + } + this->state = state; +} + +void Pump::stateTimer() { + if (!this->checkSensors()) + return; + + if (millis() - this->lastMillisTimer < Pump::delayTimer) + return; + this->lastMillisTimer = millis(); + + uint8_t minutesOn = (millis() - this->startTime) / 1000 / 60; + if (minutesOn >= this->duration) + this->changeState(State::Off); +} + +void Pump::stateStandby() { + this->checkSensors(); + if (this->pressure < Pump::maxPressure / 2) { + if (this->timerStandby) + this->changeState(State::Timer); + else + this->changeState(State::On); + } +} + +void Pump::stateError() { + if (millis() - this->startTime > Pump::blinkTime) { + this->startTime = millis(); + if (this->ledState) { + this->ledState = false; + digitalWrite(this->ledPin, LOW); + } else { + this->ledState = true; + digitalWrite(this->ledPin, HIGH); + } + } +} diff --git a/lib/Pump/pump.h b/lib/Pump/pump.h index 68708bb..678f218 100644 --- a/lib/Pump/pump.h +++ b/lib/Pump/pump.h @@ -4,7 +4,6 @@ class Pump { public: - static const uint16_t blinkTime = 1000; enum State { Off, On, @@ -13,7 +12,7 @@ class Pump { Error }; - Pump(uint8_t pumpPin, uint8_t pressurePin, uint8_t flowPin, uint8_t errorLedPin); + Pump(uint8_t pumpPin, uint8_t pressurePin, uint8_t flowPin, uint8_t ledPin); void loop(); @@ -24,20 +23,40 @@ class Pump { uint16_t getPressure(); uint32_t getRunningTime(); uint16_t getFlow(); + uint16_t getStatus(); State getState() { return this->state; } private: - void checkSensors(); + static const uint8_t delayCheckSensor = 50; + static const uint16_t blinkTime = 1000; + static const uint16_t maxPressure = 1000; + static const uint16_t maxMaxPressureTime = 30000; + static const uint16_t delayTimer = 1000; + + bool checkSensors(); + void handlePins(bool state); + void changeState(State state); + void stateTimer(); + void stateStandby(); + void stateError(); State state = State::Off; bool ledState = false; + bool timerStandby = false; uint8_t pumpPin; uint8_t pressurePin; uint8_t flowPin; - uint8_t errorLedPin; + uint8_t ledPin; + + uint16_t delayTimer = 1000; + uint16_t flow; + uint16_t pressure; uint32_t startTime; uint32_t duration; + uint32_t lastMillisTimer = 0; + uint32_t lastMillisCheckSensor = 0; + uint32_t maxPressureStart = 0; }; \ No newline at end of file diff --git a/lib/Valve/valve.cpp b/lib/Valve/valve.cpp index 2a66cbe..3047038 100644 --- a/lib/Valve/valve.cpp +++ b/lib/Valve/valve.cpp @@ -10,7 +10,9 @@ void Valve::loop() { this->loopLastMillis = millis(); for (uint8_t i = 0; i < this->valveCount; i++) { - uint8_t minutesOn = (millis() - this->valves[i].startTime) / 1000; + if (this->valves[i].duration == 0) + continue; + uint8_t minutesOn = (millis() - this->valves[i].startTime) / 1000 / 60; if (minutesOn >= this->valves[i].duration) this->close(i); } @@ -38,6 +40,9 @@ void Valve::open(uint8_t valveNumber, uint8_t minutes) { this->valves[valveNumber].duration = minutes; this->valves[valveNumber].startTime = millis(); this->setValve(valveNumber, true); + + Serial.print(F("Opened valve: ")); + Serial.println(valveNumber); } void Valve::close(uint8_t valveNumber) { @@ -46,13 +51,13 @@ void Valve::close(uint8_t valveNumber) { this->valves[valveNumber].duration = 0; this->setValve(valveNumber, false); + Serial.print(F("Closed valve: ")); + Serial.println(valveNumber); } void Valve::closeAll() { - for (uint8_t i = 0; i < this->valveCount; i++) { - this->valves[i].duration = 0; - this->setValve(i, false); - } + for (uint8_t i = 0; i < this->valveCount; i++) + this->close(i); } uint8_t Valve::getNumOfOpen() { @@ -63,12 +68,15 @@ uint8_t Valve::getNumOfOpen() { return res; } -uint8_t Valve::getTimeLeft(uint8_t valveNumber) { +uint16_t Valve::getTimeLeft(uint8_t valveNumber) { if (valveNumber >= this->valveCount) return -1; + if (this->valves[valveNumber].duration == 0) + return 0; + uint32_t millisOn = millis() - this->valves[valveNumber].startTime; - return this->valves[valveNumber].duration - (millisOn / 1000); + return this->valves[valveNumber].duration * 60 - (millisOn / 1000); } void Valve::setValve(uint8_t valveNumber, bool state) { diff --git a/lib/Valve/valve.h b/lib/Valve/valve.h index d143b50..148ba76 100644 --- a/lib/Valve/valve.h +++ b/lib/Valve/valve.h @@ -25,8 +25,7 @@ class Valve { void closeAll(); uint8_t getNumOfOpen(); - uint8_t getTimeLeft(uint8_t valveNumber); - + uint16_t getTimeLeft(uint8_t valveNumber); private: void setValve(uint8_t valveNumber, bool state); diff --git a/platformio.ini b/platformio.ini index b981ea1..b3c84b4 100644 --- a/platformio.ini +++ b/platformio.ini @@ -17,3 +17,4 @@ lib_deps = arduino-libraries/Ethernet@^2.0.1 https://git.kleiax.de/PlatformIO-Libs/Shiftregister.git#v1.0 knolleary/PubSubClient@^2.8 + bblanchon/ArduinoJson@^6.21.2 diff --git a/src/main.cpp b/src/main.cpp index 118c689..4cbb91a 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -4,13 +4,12 @@ #include #include #include +#include #include "config.h" #include "pump.h" #include "valve.h" -#define COUNT_HTTP_METHOD 3 -#define METHOD_LENGTH 6 #define BUFFER_SIZE 128 byte mac[] = { 0xC3, 0xF9, 0x7C, 0xB1, 0x8C, 0xF3 }; @@ -34,10 +33,13 @@ PubSubClient client(ethClient); void reconnect(); void callback(char* topic, byte* payload, unsigned int length); +void sendPumpStatus(); +void sendValveStatus(uint8_t valveNumber); +void sendInfo(); void setup() { Serial.begin(115200); - Serial.println("Arduino Irrigation"); + Serial.println(F("Arduino Irrigation")); valves.addShiftRegister(®); for (uint8_t i = 0; i < VALVE_COUNT; i++) @@ -58,25 +60,70 @@ void loop() { reconnect(); client.loop(); pump.loop(); + valves.loop(); + + static uint32_t lastDataSend = 0; + if (millis() - lastDataSend > 2000) { + sendInfo(); + sendPumpStatus(); + for (uint8_t i = 0; i < VALVE_COUNT; i++) + sendValveStatus(i); + lastDataSend = millis(); + } } void callback(char* topic, byte* payload, unsigned int length) { - Serial.print("Message arrived ["); - Serial.print(topic); - Serial.print("] "); - for (unsigned int i=0;i 128) { + Serial.println(F("Payload to big.")); + return; } - Serial.println(); + + StaticJsonDocument<128> doc; + DeserializationError error = deserializeJson(doc, payload, length); + + if (error) { + Serial.print(F("deserializeJson() failed: ")); + Serial.println(error.f_str()); + return; + } + + if (doc.containsKey("valve")) { + uint8_t valveNumber = doc["valve"]["number"]; + uint8_t duration = doc["valve"]["duration"]; + if (duration) + valves.open(valveNumber, duration); + else + valves.close(valveNumber); + } else if (doc.containsKey("pump")) { + uint8_t mode = doc["pump"]["mode"]; + uint8_t duration = doc["pump"]["duration"]; + if (mode) + pump.activate(duration); + else + pump.stop(); + } else { + Serial.println(F("Key not found.")); + } + + // Serial.print("Message arrived ["); + // Serial.print(topic); + // Serial.print("] "); + // for (uint8_t i=0;i doc; + + JsonObject valve = doc.createNestedObject("valve"); + valve["timeLeft"] = valves.getTimeLeft(valveNumber); + + char buffer[256]; + char topic[64]; + sprintf(topic, "%s/valve_%d", mainTopic, valveNumber); + size_t n = serializeJson(doc, buffer); + client.publish(topic, buffer, n); +} + +void sendInfo() { + StaticJsonDocument<48> doc; + + JsonObject info = doc.createNestedObject("valve"); + info["uptime"] = millis() / 1000; + info["opened"] = valves.getNumOfOpen(); + + char buffer[256]; + char topic[64]; + sprintf(topic, "%s/info", mainTopic); + size_t n = serializeJson(doc, buffer); + client.publish(topic, buffer, n); +}