From 86139abc426bceb6def32278b3e8b9f5b88e965e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bruno=20H=C3=BCtter?= Date: Thu, 3 Dec 2020 19:04:27 +0100 Subject: [PATCH] Added support for average measurements --- ...controller.ino => DreherTankController.ino | 13 ++- README.md | 9 +- src/common.h | 21 ++++- src/controller/controller.cpp | 92 +++++++++++++++++-- src/controller/controller.h | 34 +++++-- src/display/display.cpp | 2 + 6 files changed, 147 insertions(+), 24 deletions(-) rename tank_controller.ino => DreherTankController.ino (98%) diff --git a/tank_controller.ino b/DreherTankController.ino similarity index 98% rename from tank_controller.ino rename to DreherTankController.ino index eab9d61..9b02d1d 100644 --- a/tank_controller.ino +++ b/DreherTankController.ino @@ -1,5 +1,5 @@ #ifndef ARDUINO_AVR_MEGA2560 -#error This program was written for an Atmega2560 +#error This program was written for an Arduino Mega (Atmega2560) #endif #include @@ -469,6 +469,9 @@ void loop() { setParams(p); } + c.process(); + d.process(); + #if _MODBUS == 1 mb.process(); checkModbusParams(); @@ -482,6 +485,8 @@ void loop() { bitClear(modbusData[1], 14); // Nachden die 'ReferenzZeit' erstmalig gesetzt wurde, wird dieses Bit gelöscht bitClear(modbusData[1], 15); + // Setze die Durchschnittswerte für die Messwerte zurück + c.resetAverageCounters(); _println("Der 12 Bit Timer wurde zurückgesetzt"); } if (!timeStampOverflow && (millis() - timeStamp) / 100 > 0xFFF) { @@ -495,15 +500,13 @@ void loop() { } modbusData[5] = (millis() - timeStamp) / 100; #endif // _MODBUS == - c.process(); - d.process(); -#if 0 +#if 1 static unsigned long counter = 0; static unsigned long tl = millis(); if (millis() - tl > 1000) { tl = millis(); - _print("Loops: "); _println(counter); + _print("Loops / Sekunde: "); _println(counter); counter = 0; } else { counter++; diff --git a/README.md b/README.md index 44f5a3d..d107ee9 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,13 @@ Experimentelle Firmware für einen Tankregler, der die Temperatur und den Druck Das Programm ist für den Atmega2560 (Arduino Mega 2560) geschrieben. +## Probleme + +- ***Großes Problem:*** Während des Gedrückthaltens einer Menütaste am Regler blockiert das Programm fast (nur noch ca. 10 Loops / Sekunde). In diesem Fall funktioiert Modbus nicht mehr. +***### TODO - Das liegt an der Zeit, die gebraucht wird, um das Display zu beschreiben (70 - 100+ ms). Wenn praktisch dauernd das Display neu beschrieben wird, bleibt einfach keine Zeit für was anderes -- SCHNELL LEINE LÖSUNG FINDEN*** +- Solange nur ein Kühlkreis implementiert ist, lässt sich das maximale Abfrageintervall (siehe Modbus-Holding-Register 0xC0; wenn _TEMP_CONVERSION_DELAY und _ANALOG_READ_DELAY nicht reduziert werden (800 bzw. 500 ms)) auf dem Wert des internen 12 Bit Timers halten (409,6 Sekunden), da der RAM wegen den Arrays zur Messwert-Mittelung schon jetzt gerade so ausreicht (Bei einem Test waren **nur noch 874 Bytes** für lokale Variablen über). +***### Temperatur-Mittelung wieder entfernen, da die Temperaturschwankungen BEI WEITEM nicht so stark sind wie die des Drucks?*** (Da der Drucksensor in einer Leitung außerhalb des Tanks sitzt, schwankt der Druck extrem beim Schalten der Druckventile) + # Modbus ## Coils - FC 1, 5, 15 @@ -78,7 +85,7 @@ Sollwerte: Setzen der Referenzzeit: -- ***0xC0***: Es muss ein vom vorigen Wert abweichender Wert (z.B. ein Zähler oder die 16 LSBs der UNIX-Zeit in Zehntelsekunden – beim ersten Mal jedenfalls NICHT *0xFFFF*, da das Register damit initialisiert ist und kein Unterschied festgestellt werden könnte) übergeben werden. Der Regler verwendet diesen Wert prinzipiell für nichts, außer dass der interne 12 Bit-Timer (1/10 Sekunden) und der Event-Counter auf 0 zurückgesetzt werden (siehe Input-Register ***0x00*** und ***0x06***), wenn sich dieser Wert ändert. Der Master kann entweder +- ***0xC0***: Es muss ein vom vorigen Wert abweichender Wert (z.B. ein Zähler – beim ersten Mal jedenfalls NICHT *0xFFFF*, da das Register damit initialisiert ist und kein Unterschied festgestellt werden könnte) übergeben werden. Der Regler verwendet diesen Wert prinzipiell für nichts, außer dass der interne 12 Bit-Timer (1/10 Sekunden) und der Event-Counter auf 0 zurückgesetzt werden (siehe Input-Register ***0x00*** und ***0x06***), wenn sich dieser Wert ändert. Der Master kann entweder - den entsprechenden Zeitstempel im speichern, und den Zeitstempel der Antwort (siehe Input-Register ***0x06***) dazu addieren, um den tatsächlichen Zeitpunkt des Ereignisses zu ermitteln, oder - den in ***0x05*** gespeicherten Referenzzeitpunkt (Zehntelsekunden seit Reglerstart bzw. letzter Referenzierung (siehe Holding-Register ***0xC0***)) mit dem Event-Zeitpunkt und dem aktuellen Zeitstempel des Masters gegenrechnen. diff --git a/src/common.h b/src/common.h index 2c48f31..b755bcf 100644 --- a/src/common.h +++ b/src/common.h @@ -2,7 +2,6 @@ #define _MY_STUFF_H_INCLUDED #include "./DallasTemperature/DallasTemperature.h" -// #include // NAN #define _VERSION_MAJOR 0 #define _VERSION_MINOR 0 @@ -10,13 +9,26 @@ #define _VERSION_NUMBER _VERSION_MICRO | (_VERSION_MINOR << 6) | (_VERSION_MAJOR << 12) #define _VERSION_STRING "v0.0.1" -#define _DEBUG 1 +#define _DEBUG 0 #define _DEBUG_SENSORS 0 #define _MODBUS 1 #if _DEBUG == 1 #define _print(x) Serial.print(x) #define _println(x) Serial.println(x) +struct _printTimeStruct { + _printTimeStruct(const char *text) : _text(text) { + _t0 = millis(); + }; + ~_printTimeStruct() { + _print("Vergangene Millisekunden ["); _print(_text); + _print("]: "); _println(millis() - _t0); + }; + private: + u32 _t0; + const char *_text; +}; +#define _printtime(text) _printTimeStruct __printt(text) #if _DEBUG_SENSORS == 1 #define _prints(x) Serial.print(x) #define _printsln(x) Serial.println(x) @@ -29,6 +41,7 @@ #define _println(x) #define _prints(x) #define _printsln(x) +#define _printtime() #endif #define _REGS_INFRONTOF_EVENTS 6 @@ -57,10 +70,10 @@ extern u16 modbusData[]; eventCounter++;\ }\ } -#define _setModbusValue(index, val) modbusData[index] = val +//#define _setModbusValue(index, val) modbusData[index] = val #else #define _setModbusValve(index, val) -#define _setModbusValue(index, val) +//#define _setModbusValue(index, val) #endif // _MODBUS == #define _MODBUS_ADDR_MIN 1 diff --git a/src/controller/controller.cpp b/src/controller/controller.cpp index ee92aa1..659e5ac 100644 --- a/src/controller/controller.cpp +++ b/src/controller/controller.cpp @@ -58,7 +58,7 @@ void Controller::init(bool startup=false) void Controller::process() { - if (_initOk && millis() - _lastConversion > _CONVERSION_DELAY) { + if (_initOk && millis() - _lastConversion > _TEMP_CONVERSION_DELAY) { _lastConversion = millis(); _requestConversion = true; float temp = _dallas.getTempC(_tempAddr1); @@ -101,7 +101,9 @@ void Controller::process() _setT1Valve(0, stateIdle); _printsln(); } - _setModbusValue(_MODBUS_T1_INDEX, _vals->t1); +#if _MODBUS == 1 + _averageTemperature1(); +#endif } else if (_requestConversion) { _requestConversion = false; _dallas.requestTemperatures(); @@ -126,7 +128,9 @@ void Controller::process() _vals->p = (float) _rawP / 1.705f; // 1023 / 6 * 100 (0-5 1/100 bar) _prints(" (Gems), p: "); _prints(_vals->p); } - _setModbusValue(_MODBUS_P_INDEX, _vals->p); +#if _MODBUS == 1 + _averagePressure(); +#endif if (_vals->p != _SENSOR_FAULT && _params->cEn) { switch (_pState) { case stateIdle: @@ -213,7 +217,81 @@ void Controller::_setPValves(States state) } } -// uint16_t Controller::_convertFloat(const float &x) -// { -// return (uint16_t) (x * 100); -// } +#if _MODBUS == 1 +void Controller::resetAverageCounters() +{ + _print("Setze Zähler zurück (_t1_c: "); + _print(_t1_c); _print(", _t2_c: "); + _print(_t2_c); _print(", _p_c: "); + _print(_p_c); _println(")"); + _t1_c = 0; + _t2_c = 0; + _p_c = 0; + _t1_of = false; + _t2_of = false; + _p_of = false; +} + +inline void Controller::_averageTemperature1() +{ + int32_t total = 0; + int16_t result = _SENSOR_FAULT; + u8 valid = 0; + u8 invalid = 0; + if (_t1_c >= _T_ARR_LEN) { + _t1_c = 0; + _t1_of = true; + } + // Falls das Array schon voll ist, wird es wieder vom Anfang befüllt + _t1_arr[_t1_c++] = _vals->t1; + // Dabei läuft die for-Schleife dann über alle Einträge, ansonsten nur bis zum aktuellen + u16 loopTill = (_t1_of) ? _T_ARR_LEN : _t1_c; + for (u16 i=0; i= invalid) { + result = total / valid; + } + modbusData[_MODBUS_T1_INDEX] = result; + // _setModbusValue(_MODBUS_T1_INDEX, result); +} + +inline void Controller::_averageTemperature2() +{ + // noch nicht implementiert +} + +inline void Controller::_averagePressure() +{ + int32_t total = 0; + int16_t result = _SENSOR_FAULT; + u8 valid = 0; + u8 invalid = 0; + if (_p_c >= _P_ARR_LEN) { + _p_c = 0; + _p_of = true; + } + // Falls das Array schon voll ist, wird es wieder vom Anfang befüllt + _p_arr[_p_c++] = _vals->p; + // Dabei läuft die for-Schleife dann über alle Einträge, ansonsten nur bis zum aktuellen + u16 loopTill = (_p_of) ? _P_ARR_LEN : _p_c; + for (u16 i=0; i<_p_c; i++) { + if (_p_arr[i] != _SENSOR_FAULT) { + total += _p_arr[i]; + valid++; + } else { + invalid++; + } + } + if (valid >= invalid) { + result = total / valid; + } + modbusData[_MODBUS_P_INDEX] = result; + // _setModbusValue(_MODBUS_P_INDEX, result); +} +#endif // _MODBUS == 1 diff --git a/src/controller/controller.h b/src/controller/controller.h index 947a5af..df3226b 100644 --- a/src/controller/controller.h +++ b/src/controller/controller.h @@ -6,8 +6,14 @@ #include "../display/display.h" #include "../common.h" -#define _CONVERSION_DELAY 800 +#define _TEMP_CONVERSION_DELAY 800 #define _ANALOG_READ_DELAY 500 +// Maximal nötige Anzahl an Temperatur-Einträgen, bevor der interne 12 Bit Timer überläuft +#define _T_ARR_LEN (409600 / _TEMP_CONVERSION_DELAY) + 1 +// Maximal nötige Anzahl an Druck-Einträgen, bevor der interne 12 Bit Timer überläuft +#define _P_ARR_LEN (409600 / _ANALOG_READ_DELAY) + 1 +// #define _T_ARR_LEN 255 +// #define _P_ARR_LEN 255 class Controller { @@ -23,13 +29,21 @@ private: uint8_t _t1Pin; uint8_t _t2Pin; + int16_t _t1_arr[_T_ARR_LEN]; // Array, aus dem der Durchschnittswert ermittelt wird + u16 _t1_c = 0; // Anzahl der Einträge, aus denen der Durchschnitt gebildet wird + bool _t1_of = false; // Flag, die anzeigt, ob das _t1_arr-Array schon voll ist + // int16_t _t2_arr[_T_ARR_LEN]; // Array, aus dem der Durchschnittswert ermittelt wird + u16 _t2_c = 0; // Anzahl der Einträge, aus denen der Durchschnitt gebildet wird + bool _t2_of = false; // Flag, die anzeigt, ob das _t2_arr-Array schon voll ist + int16_t _p_arr[_P_ARR_LEN]; // Array, aus dem der Durchschnittswert ermittelt wird + u16 _p_c = 0; // Anzahl der Einträge, aus denen der Durchschnitt gebildet wird + bool _p_of = false; // Flag, die anzeigt, ob das _p_arr-Array schon voll ist + OneWire _oneWire; DallasTemperature _dallas; DeviceAddress _tempAddr1; - enum States : uint8_t { - stateIdle, stateRising, stateFalling - }; + enum States : uint8_t { stateIdle, stateRising, stateFalling }; States _tState = stateIdle; States _pState = stateIdle; @@ -39,11 +53,14 @@ private: unsigned long _lastAnalogRead; uint16_t _rawP; - // void _setTState(States curr, States next); - // void _setPState(States curr, States next); void _setT1Valve(uint8_t, States); void _setPValves(States); - // uint16_t _convertFloat(const float &x); + +#if _MODBUS == 1 + void _averageTemperature1(); + void _averageTemperature2(); + void _averagePressure(); +#endif public: Controller(const uint8_t pAnalaogPin, @@ -61,6 +78,9 @@ private: void init(bool startup=false); void process(); +#if _MODBUS == 1 + void resetAverageCounters(); +#endif }; #endif // MY_CONTROLLER_H_INCLUDED diff --git a/src/display/display.cpp b/src/display/display.cpp index c912e92..e4ebbc2 100644 --- a/src/display/display.cpp +++ b/src/display/display.cpp @@ -278,6 +278,7 @@ void Display::_select(const uint8_t selection, const bool restoreCursor=false) void Display::_select() { + // _printtime("select screen"); setFont(_SEL_FONT); setFontRefHeightAll(); _inputValueActive = false; @@ -386,6 +387,7 @@ void Display::_home() bool tState = _vStates->t1; // %u in sprintf() geht nur bis uint16_t String baudStr(_modbusParams->baudrate); + // _printtime("homescreen"); firstPage(); do { setFont(u8g2_font_fub20_tf);