From a6e5e19060da99c24ebef118ed827c4030c88eca Mon Sep 17 00:00:00 2001 From: Denis Denisov Date: Thu, 27 Jul 2023 22:23:09 +0300 Subject: [PATCH] Updated working modes. Settings saved between runs. Added NODELAY to decrease latency --- readme.md | 15 ++- well_control.ino | 285 +++++++++++++++++++++++++++++++++----------- well_control.items | 9 +- well_control.things | 15 ++- 4 files changed, 244 insertions(+), 80 deletions(-) diff --git a/readme.md b/readme.md index e35a8b2..337ba9a 100644 --- a/readme.md +++ b/readme.md @@ -10,6 +10,17 @@ 1 - влажность (аналогично) 2 - давление (также, умноженное на 100) 3 - текущее состояние насоса (0 - выкл, 1 - вкл) -4 - режим управления (0 - авто, 1 - ручное) +4 - режим работы + 0 - авто-вкл (поддерживает температуру, если она в норме, то закрывает слив и поддерживает давление) + 1 - авто-выкл (открывает слив, давление не поддерживает, температуру не трогает) + 2 - ручное (ничего само не делает) 5 - давление включения (умноженное на 100, по умолчанию 150) -6 - давление выключения (умноженное на 100, по умолчанию 250) \ No newline at end of file +6 - давление выключения (умноженное на 100, по умолчанию 250) +7 - температура включения (умноженная на 100) +8 - температура выключения (умноженная на 100) +10 - состояние нагревателя (0 - выкл, 1 - вкл) +11 - ошибка насоса +12 - текущее состояние слива (0 - закрыт, 1 - открыт) +13 - принудительно включить насос (0 - нет, 1 - да) +14 - принудительно включить нагрев (0 - нет, 1 - да) +15 - принудительно открыть слив (0 - нет, 1 - да) \ No newline at end of file diff --git a/well_control.ino b/well_control.ino index 32caa6e..0525988 100644 --- a/well_control.ino +++ b/well_control.ino @@ -1,13 +1,19 @@ -#include "DHT.h" #include #include + +#include "DHT.h" #include "config.h" #include "rgb_lcd.h" +#include "LittleFS.h" #define TCP_PORT 15000 +// After how much time without connection we will +// reboot (in millis) +#define NON_CONNECTED_REBOOT_TIMEOUT 60000 #define SENSOR_UPDATE_INTERVAL 500 #define SCREEN_UPDATE_INTERVAL 200 +#define STATE_SAVE_INTERVAL 5000 // Interval between allowed switch-states of the pump #define PUMP_SWITCH_INTERVAL 5000 // Interval considering "rather long" to work for pump without pressure increase @@ -17,30 +23,47 @@ // In fact, pump needs to be able to increase pressure by PRESSURE_PRODUCTIVE_INCREASE // in PUMP_UNPRODUCTIVE_TIMEOUT time of working +// Interval between allowed switch-states of the flush valve +#define FLUSH_SWITCH_INTERVAL 1000 -#define MODE_AUTO 0 -#define MODE_MANUAL 1 +#define MODE_AUTO_ON 0 +#define MODE_AUTO_OFF 1 +#define MODE_MANUAL 2 #define REGISTER_CURRENT_TEMP 0 #define REGISTER_CURRENT_HUM 1 #define REGISTER_CURRENT_PRESS 2 #define REGISTER_PUMP_STATE 3 -#define REGISTER_PUMP_MODE 4 +#define REGISTER_WORK_MODE 4 #define REGISTER_P_ON 5 #define REGISTER_P_OFF 6 #define REGISTER_T_ON 7 #define REGISTER_T_OFF 8 -#define REGISTER_HEAT_MODE 9 #define REGISTER_HEAT_STATE 10 #define REGISTER_PUMP_ERROR 11 +#define REGISTER_FLUSH_STATE 12 +#define REGISTER_FORCE_PUMP 13 +#define REGISTER_FORCE_HEAT 14 +#define REGISTER_FORCE_FLUSH 15 #define DHTPIN D3 #define DHTTYPE AM2301 #define PUMP_PIN D4 #define HEATER_PIN D0 +#define FLUSH_PIN D6 -ModbusIP mb; +class ModbusIPNoDelay : public ModbusIP +{ + public: + void serverNoDelay(uint16_t port) + { + server(port); + tcpserver->setNoDelay(true); + } +}; + +ModbusIPNoDelay mb; DHT dht(DHTPIN, DHTTYPE); rgb_lcd lcd; @@ -50,22 +73,62 @@ unsigned long last_update; int p_on = 150; int p_off = 250; -int cur_pump_mode = MODE_AUTO; +int cur_work_mode = MODE_AUTO_ON; int cur_pump_state = 0; int cur_p = 0; int cur_t = 0; int cur_hum = 0; int t_on = 200; // 2 degrees int t_off = 1500; // 15 degrees -int cur_heat_mode = MODE_AUTO; int cur_heat_state = 0; +int cur_flush_state = 0; +int force_pump = 0; +int force_heat = 0; +int force_flush = 0; bool error_pump = false; bool error_heat = false; +int ReadInt(File &f) +{ + String s = f.readStringUntil('\n'); + return atoi(s.c_str()); +} + +void LoadState() +{ + File f = LittleFS.open("/state.txt", "r"); + if (!f) return; + t_on = ReadInt(f); + t_off = ReadInt(f); + p_on = ReadInt(f); + p_off = ReadInt(f); + cur_work_mode = ReadInt(f); + f.close(); +} + +void SaveState() +{ + File f = LittleFS.open("/state.txt", "w"); + if (!f) return; + f.println(t_on); + f.println(t_off); + f.println(p_on); + f.println(p_off); + f.println(cur_work_mode); + f.close(); +} + void setup() { - pinMode(PUMP_PIN, OUTPUT); + if (!LittleFS.begin()) + { + Serial.println("Unable to mount the filesystem"); + ESP.restart(); + } + LoadState(); + pinMode(FLUSH_PIN, OUTPUT); + pinMode(PUMP_PIN, OUTPUT); pinMode(HEATER_PIN, OUTPUT); pinMode(DHTPIN, INPUT); pinMode(A0, INPUT); @@ -94,19 +157,22 @@ void setup() // DHT sensor init dht.begin(); // Modbus init - mb.server(15000); + mb.serverNoDelay(15000); mb.addHreg(REGISTER_CURRENT_TEMP, cur_t); mb.addHreg(REGISTER_CURRENT_HUM, cur_hum); mb.addHreg(REGISTER_CURRENT_PRESS, cur_p); mb.addHreg(REGISTER_PUMP_STATE, cur_pump_state); - mb.addHreg(REGISTER_PUMP_MODE, cur_pump_mode); + mb.addHreg(REGISTER_WORK_MODE, cur_work_mode); mb.addHreg(REGISTER_P_ON, p_on); mb.addHreg(REGISTER_P_OFF, p_off); mb.addHreg(REGISTER_T_ON, t_on); mb.addHreg(REGISTER_T_OFF, t_off); mb.addHreg(REGISTER_HEAT_STATE, cur_heat_state); - mb.addHreg(REGISTER_HEAT_MODE, cur_heat_mode); mb.addHreg(REGISTER_PUMP_ERROR, error_pump); + mb.addHreg(REGISTER_FLUSH_STATE, cur_flush_state); + mb.addHreg(REGISTER_FORCE_PUMP, force_pump); + mb.addHreg(REGISTER_FORCE_HEAT, force_heat); + mb.addHreg(REGISTER_FORCE_FLUSH, force_flush); // Serial.print("Heater pin "); Serial.println(HEATER_PIN); @@ -127,24 +193,26 @@ void UpdateDHTData() // Workings with pressure sensor /////////////////////////////////////////////////////////////////////////////////////////////////// -const int MEASURES_AMOUNT = 1000; +const int MEASURES_AMOUNT = 10; int measures[MEASURES_AMOUNT]; int measure_pos = 0; -// добавка к измеренному значению (поправка на особенности датчика) -float PRESSURE_CALIBRATION = -2; void UpdatePressure() { int p_int = analogRead(A0); - // we have 10-bit ADC, so get value between 0 and 1 and then multiply by max voltage + // we have 10-bit ADC, so get value between 0 and 1 and then multiply by max voltage // (it is 3.3) - float p_volt = (p_int / 1024.0f) * 3.3f; - // divide by maximal input voltage from sensor (it is 2.5) - // and then multiply by sensor max value (10 bar) - we will get pressure in bar - // also add one atmosphere (1.01325 bar), because sensor reads excess pressure (not absolute) + // we divide sensor output by two (in order not to burn our ADC), se here we multiply it back + float p_volt = (p_int / 1024.0f) * 3.3f * 2.f; + // sensor outputs 0.5V --> 0bar and 4.5V --> 10.2bar (150psi) + // so to get value in bar, we subtract 0.5V, then divide by 4.5 - 0.5 = 4V + // and then multiply by sensor max value (10.2 bar) // finally multiply by 100 as we have int registers - measures[measure_pos] = (int)(((p_volt / 2.5f) * 10 + 1.01325) * 100); - measure_pos++; + if (p_volt >= 0.5f && p_volt <= 4.5f) + { + measures[measure_pos] = (int)((((p_volt - 0.5f) / 4.f) * 10.2) * 100); + measure_pos++; + } // We will use average of MEASURES_AMOUNT last sensor readings // excluding minimal and maximal value (to get rid of peaks) if (measure_pos >= MEASURES_AMOUNT) measure_pos = 0; @@ -161,7 +229,7 @@ void UpdatePressure() if (i == min_pos || i == max_pos) continue; sum += measures[i]; } - cur_p = sum / (MEASURES_AMOUNT - 2) + PRESSURE_CALIBRATION * 100; + cur_p = sum / (MEASURES_AMOUNT - 2); } /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -218,41 +286,48 @@ void UpdatePump() { unsigned long current = millis(); int new_state = cur_pump_state; - if (cur_pump_mode == MODE_AUTO) + if (cur_work_mode == MODE_AUTO_ON) { - if (cur_p < p_on) new_state = 1; - else if (cur_p > p_off) new_state = 0; - if ((current < last_pump_switch || current - last_pump_switch > PUMP_SWITCH_INTERVAL) - && new_state != cur_pump_state - && cur_t > t_on) // turning on pump only if temperature is high enough - { - Serial.print("CHANGE STATE "); - Serial.print(cur_p); - Serial.print(" "); - Serial.print(p_on); - Serial.print(" "); - Serial.print(p_off); - cur_pump_state = new_state; - mb.Hreg(REGISTER_PUMP_STATE, cur_pump_state); - last_pump_switch = current; - if (new_state == 1) - { - last_pump_on = current; - last_pump_p = cur_p; - } - } + // turning on pump only if temperature is high enough + if (cur_t > t_on) + { + if (cur_p < p_on) new_state = 1; + else if (cur_p > p_off) new_state = 0; + } + if (error_pump) + { + new_state = 0; + } } - else + else if (cur_work_mode == MODE_AUTO_OFF) { - cur_pump_state = mb.Hreg(REGISTER_PUMP_STATE); - } - if (error_pump) - { - cur_pump_state = 0; + new_state = 0; } + else if (cur_work_mode == MODE_MANUAL) + { + new_state = force_pump; + } + if ((cur_work_mode == MODE_MANUAL || (current < last_pump_switch || current - last_pump_switch > PUMP_SWITCH_INTERVAL)) + && new_state != cur_pump_state ) + { + Serial.print("CHANGE PUMP STATE "); + Serial.print(cur_p); + Serial.print(" "); + Serial.print(p_on); + Serial.print(" "); + Serial.print(p_off); + cur_pump_state = new_state; + last_pump_switch = current; + if (new_state == 1) + { + last_pump_on = current; + last_pump_p = cur_p; + } + } digitalWrite(PUMP_PIN, cur_pump_state); - // check for unproductive - if (cur_pump_state == 1) + mb.Hreg(REGISTER_PUMP_STATE, cur_pump_state); + // check for unproductive (in auto modes) + if (cur_work_mode != MODE_MANUAL && cur_pump_state == 1) { if (current < last_pump_on || current - last_pump_on >= PUMP_UNPRODUCTIVE_TIMEOUT) { @@ -274,23 +349,68 @@ void UpdatePump() void UpdateHeater() { int new_state = cur_heat_state; - if (cur_heat_mode == MODE_AUTO) + if (cur_work_mode == MODE_AUTO_ON) { if (cur_t < t_on) new_state = 1; else if (cur_t > t_off) new_state = 0; - if (new_state != cur_heat_state) - { - cur_heat_state = new_state; - mb.Hreg(REGISTER_HEAT_STATE, cur_heat_state); - } - } - else - { - cur_heat_state = mb.Hreg(REGISTER_HEAT_STATE); } + else if (cur_work_mode == MODE_AUTO_OFF) + { + new_state = 0; + } + else if (cur_work_mode == MODE_MANUAL) + { + new_state = force_heat; + } + if (new_state != cur_heat_state) + { + cur_heat_state = new_state; + } digitalWrite(HEATER_PIN, cur_heat_state); + mb.Hreg(REGISTER_HEAT_STATE, cur_heat_state); } +unsigned long last_flush_switch = 0; + +void UpdateFlush() +{ + unsigned long current = millis(); + int new_state = cur_flush_state; + if (cur_work_mode == MODE_AUTO_ON) + { + // closing flush valve only when temperature is high enough + if (cur_t > t_on) + { + new_state = 0; // 0 means closed + } + else + { + new_state = 1; + } + } + else if (cur_work_mode == MODE_AUTO_OFF) + { + new_state = 1; + } + else if (cur_work_mode == MODE_MANUAL) + { + new_state = force_flush; + } + if ((cur_work_mode == MODE_MANUAL || current < last_flush_switch || current - last_flush_switch > FLUSH_SWITCH_INTERVAL) + && new_state != cur_flush_state ) + { + Serial.print("CHANGE FLUSH STATE "); + Serial.print(new_state); + cur_flush_state = new_state; + last_flush_switch = current; + } + digitalWrite(FLUSH_PIN, cur_flush_state); + mb.Hreg(REGISTER_FLUSH_STATE, cur_flush_state); +} + +unsigned long last_connected = 0; +unsigned long last_state_save = 0; + void loop() { unsigned long current = millis(); @@ -305,16 +425,20 @@ void loop() mb.task(); p_on = mb.Hreg(REGISTER_P_ON); p_off = mb.Hreg(REGISTER_P_OFF); - cur_pump_mode = mb.Hreg(REGISTER_PUMP_MODE); + cur_work_mode = mb.Hreg(REGISTER_WORK_MODE); t_on = mb.Hreg(REGISTER_T_ON); t_off = mb.Hreg(REGISTER_T_OFF); - cur_heat_mode = mb.Hreg(REGISTER_HEAT_MODE); error_pump= mb.Hreg(REGISTER_PUMP_ERROR); + force_pump = mb.Hreg(REGISTER_FORCE_PUMP); + force_heat = mb.Hreg(REGISTER_FORCE_HEAT); + force_flush = mb.Hreg(REGISTER_FORCE_FLUSH); UpdatePump(); UpdateHeater(); + UpdateFlush(); if (u) { - Serial.print("Current pressure: "); + Serial.print(current); + Serial.print(" current pressure: "); Serial.print(cur_p); Serial.print(" t "); Serial.print(cur_t); @@ -323,20 +447,43 @@ void loop() Serial.print(" pump "); Serial.print(cur_pump_state); Serial.print(" heat "); - Serial.println(cur_heat_state); + Serial.print(cur_heat_state); + Serial.print(" flush "); + Serial.print(cur_flush_state); + Serial.print(" mode "); + Serial.println(cur_work_mode); } mb.Hreg(REGISTER_P_ON, p_on); mb.Hreg(REGISTER_P_OFF, p_off); - mb.Hreg(REGISTER_PUMP_MODE, cur_pump_mode); + mb.Hreg(REGISTER_WORK_MODE, cur_work_mode); mb.Hreg(REGISTER_T_ON, t_on); mb.Hreg(REGISTER_T_OFF, t_off); - mb.Hreg(REGISTER_HEAT_MODE, cur_heat_mode); mb.Hreg(REGISTER_CURRENT_TEMP, cur_t); mb.Hreg(REGISTER_CURRENT_HUM, cur_hum); mb.Hreg(REGISTER_CURRENT_PRESS, cur_p); mb.Hreg(REGISTER_PUMP_ERROR, error_pump); + mb.Hreg(REGISTER_FORCE_PUMP, force_pump); + mb.Hreg(REGISTER_FORCE_HEAT, force_heat); + mb.Hreg(REGISTER_FORCE_FLUSH, force_flush); UpdateDisplay(); if (WiFi.status() != WL_CONNECTED) { + Serial.println("RECONNECTING"); + Serial.println(WiFi.status()); + WiFi.reconnect(); + } + else + { + last_connected = current; + } + if (current < last_state_save || current - last_state_save > STATE_SAVE_INTERVAL) + { + SaveState(); + last_state_save = current; + } + if (current < last_connected || current - last_connected > NON_CONNECTED_REBOOT_TIMEOUT) + { + SaveState(); + ESP.restart(); } } diff --git a/well_control.items b/well_control.items index 0a051e1..b5f5d98 100644 --- a/well_control.items +++ b/well_control.items @@ -3,9 +3,12 @@ Number Well_P_off "Well pump turn off pressure [%.2f]" {channel="modbus:data:Wel Number Well_P "Well current pressure [%.2f]" {channel="modbus:data:Well:W:Well_P:number"} Number Well_Temp "Well temperature [%.1f °C]" {channel="modbus:data:Well:W:Well_Temp:number"} Number Well_Hum "Well humidity [%.1f%%]" {channel="modbus:data:Well:W:Well_Hum:number"} -Number Well_Pump_Mode "Well pump mode" {channel="modbus:data:Well:W:Well_Pump_Mode:number"} +Number Well_Mode "Well working mode" {channel="modbus:data:Well:W:Well_Mode:number"} Switch Well_Pump_State "Well pump state" {channel="modbus:data:Well:W:Well_Pump_State:switch"} Number Well_T_on "Well turn on heater [%.2f]" {channel="modbus:data:Well:W:Well_T_on:number"} Number Well_T_off "Well turn off temperature [%.2f]" {channel="modbus:data:Well:W:Well_T_off:number"} -Number Well_Heat_Mode "Well heater mode" {channel="modbus:data:Well:W:Well_Heat_Mode:number"} -Switch Well_Heat_State "Well heater state" {channel="modbus:data:Well:W:Well_Heat_State:switch"} \ No newline at end of file +Switch Well_Heat_State "Well heater state" {channel="modbus:data:Well:W:Well_Heat_State:switch"} +Switch Well_Flush_State "Well flush valve state" {channel="modbus:data:Well:W:Well_Flush_State:switch"} +Switch Well_Force_Pump "Well force pump" {channel="modbus:data:Well:W:Well_Force_Pump:switch"} +Switch Well_Force_Heat "Well force heat" {channel="modbus:data:Well:W:Well_Force_Heat:switch"} +Switch Well_Force_Flush "Well force flush" {channel="modbus:data:Well:W:Well_Force_Flush:switch"} diff --git a/well_control.things b/well_control.things index 0ac7792..6e98787 100644 --- a/well_control.things +++ b/well_control.things @@ -1,15 +1,18 @@ -Bridge modbus:tcp:Well [ host="192.168.0.24", port=15000, id=1 ] { - Bridge poller W[start = 0, length = 11, type = "holding", refresh = 500] { +Bridge modbus:tcp:Well [ host="192.168.0.24", port=15000, id=1, reconnectAfterMillis=120000 ] { + Bridge poller W[start = 0, length = 16, type = "holding", refresh = 1000] { Thing data Well_Temp[readStart = "0", readValueType = "int16", readTransform="JS(divide100.js)"] Thing data Well_Hum[readStart = "1", readValueType = "int16", readTransform="JS(divide100.js)"] Thing data Well_P[readStart = "2", readValueType = "int16", readTransform="JS(divide100.js)"] - Thing data Well_Pump_State[readStart = "3", readValueType = "int16", writeStart="3", writeValueType="int16", writeType="holding"] - Thing data Well_Pump_Mode[readStart = "4", readValueType = "int16", writeStart="4", writeValueType="int16", writeType="holding"] + Thing data Well_Pump_State[readStart = "3", readValueType = "int16"] + Thing data Well_Mode[readStart = "4", readValueType = "int16", writeStart="4", writeValueType="int16", writeType="holding"] Thing data Well_P_on[readStart = "5", readValueType = "int16", readTransform="JS(divide100.js)", writeStart="5", writeValueType="int16", writeTransform="JS(multiply100.js)", writeType="holding"] Thing data Well_P_off[readStart = "6", readValueType = "int16", readTransform="JS(divide100.js)", writeStart="6", writeValueType="int16", writeTransform="JS(multiply100.js)", writeType="holding"] Thing data Well_T_on[readStart = "7", readValueType = "int16", readTransform="JS(divide100.js)", writeStart="7", writeValueType="int16", writeTransform="JS(multiply100.js)", writeType="holding"] Thing data Well_T_off[readStart = "8", readValueType = "int16", readTransform="JS(divide100.js)", writeStart="8", writeValueType="int16", writeTransform="JS(multiply100.js)", writeType="holding"] - Thing data Well_Heat_Mode[readStart = "9", readValueType = "int16", writeStart="9", writeValueType="int16", writeType="holding"] - Thing data Well_Heat_State[readStart = "10", readValueType = "int16", writeStart="10", writeValueType="int16", writeType="holding"] + Thing data Well_Heat_State[readStart = "10", readValueType = "int16"] + Thing data Well_Flush_State[readStart = "12", readValueType = "int16"] + Thing data Well_Force_Pump[readStart = "13", readValueType = "int16", writeStart="13", writeValueType="int16", writeType="holding"] + Thing data Well_Force_Heat[readStart = "14", readValueType = "int16", writeStart="14", writeValueType="int16", writeType="holding"] + Thing data Well_Force_Flush[readStart = "15", readValueType = "int16", writeStart="15", writeValueType="int16", writeType="holding"] } }