diff --git a/DreherTankController.ino b/DreherTankController.ino index d36dad1..4b05255 100644 --- a/DreherTankController.ino +++ b/DreherTankController.ino @@ -2,514 +2,163 @@ #error This program was written for an Arduino Mega (Atmega2560) #endif +#include "src/common.h" +#include "src/modbus/ModbusRTUSlave.h" #include -#include "src/OneWire/OneWire.h" -#include "src/DallasTemperature/DallasTemperature.h" -#include "src/display/display.h" -#include "src/controller/controller.h" const uint8_t BTN_PWR = 7; -modbusParameters modbusParams; -parameters params; // Prozessparameter -values vals; // aktuelle Messwerte +ModbusParameters modbusParams; +Parameters params; // Prozessparameter +Values vals; // aktuelle Messwerte PSensor pSensor; // verwendeter Drucksensor -valveStates vStates; // Zustände der Ausgänge -bool paramsChangedByUI; // true, außer ein Wert wurde über Modbus geändert +ValveStates vStates; // Zustände der Ausgänge + bool timeStampOverflow = false; // true, wenn mehr als 0xFFF Zehntelsekunden vergangen sind unsigned long timeStamp; u16 lastRefTime = 0xFFFF; - -void checkParamINT16(int16_t *source, int16_t *target, const int &std, const int &min, const int &max) { - _print("source: "); _print(*source); - if (*source == _SENSOR_FAULT) { - *source = *target = std; - } else if (*source > max) { - *source = *target = max; - } else if (*source < min) { - *source = *target = min; - } else if (*source == min || *source == max) { - *target = *source; - } else { - u8 mod = *source % 5; - _print(", modulo: "); _print(mod); - if (mod == 0) - *target = *source; - else { - int tempVal = *source - mod; - _print(", tempVal: "); _print(tempVal); - if (mod > 2) { // runde auf den nächsten 5er Schritt bzw. setze auf max - if ((tempVal + 5) > max) { - *source = *target = max; - _print(" ... auf max setzen"); - } else { - *source = *target = tempVal + 5; - _print(" ... aufrunden"); - } - } else { // runde auf den vorigen 5er Schritt bzw. setze auf min - if (tempVal < min) { - *source = *target = min; - _print(" ... auf min setzen"); - } else { - *source = *target = tempVal; - _print(" ... abrunden"); - } - } - } - } - _print(", target: "); _print(*target); _print(", source: "); _println(*source); -} - -void checkParamUINT8(u8 *source, u8 *target, const u8 &std) { - _print("checkParamUINT8 source vorher: "); _print(*source); - if (*source > 1) { - *source = *target = std; - } else { - *target = *source; - } - _print(", target: "); _print(*target); _print(", source: "); _println(*source); -} +u16 zones = 1; // Aktuell ist nur eine Kühlzone implementiert +u32 currMillis; #if _MODBUS == 1 -#include "src/modbus/ModbusRTUSlave.h" - -u8 modbusStates[1]; // Bit0: tEn, Bit1: pInc, Bit2: pDec, Bit3: cEn -u8 modbusValves[1]; // Bit0: Temp1, Bit1: Temp2, Bit2: Druck -// 0: Event-Counter, 1: high: modbusValves[0], low: modbusStates[0], 2...4: Temp 1, 2, Druck, ab5: gespeicherte Schaltvorgänge -u16 modbusData[_REGS_INFRONTOF_EVENTS + _MODBUS_MAX_EVENTS]; // Main Input Registers -u16 modbusMiscReadable[2]; // Version, Kühlzonen -u16 modbusSetpoints[6]; // Temp1, 2, Druck jeweils Sollwert + Hysterese -u16 modbusRefTime[1]; // setzt bei Änderung Event-Counter und -Timer zurück - -u8 &states = modbusStates[0]; -u8 &valves = modbusValves[0]; -u16 &eventCounter = modbusData[0]; -u16 &refTime = modbusRefTime[0]; - ModbusRTUSlave mb(&Serial1, 2); - -void checkParamBool(u8 &p, const u8 &bitNr, u8 *p2=nullptr) { - u8 *p_ = (p2) ? p2 : &p; - if (paramsChangedByUI) { - _print("UI -> Modbus - vorher: "); _print(bitRead(modbusStates[0], bitNr)); - bitWrite(modbusStates[0], bitNr, *p_); - _print(", nachher: "); _println(bitRead(modbusStates[0], bitNr)); - } else { - _print("Modbus -> UI - vorher: "); _print(*p_); - p = bitRead(modbusStates[0], bitNr); - _print(", nachher: "); _println(p); - } - bitWrite(modbusData[1], bitNr, bitRead(modbusStates[0], bitNr)); -} - -void checkModbusParams() { - parameters p; - if (modbusSetpoints[0] != params.ts1) { - if (paramsChangedByUI) { - _print("ts1 - UI -> Modbus - "); - checkParamINT16(¶ms.ts1, (int16_t*)&modbusSetpoints[0], _STD_TEMP_SETPOINT, _MIN_TEMP_SETPOINT, _MAX_TEMP_SETPOINT); - } else { - _print("ts1 - Modbus -> UI - "); - checkParamINT16((int16_t*)&modbusSetpoints[0], ¶ms.ts1, _STD_TEMP_SETPOINT, _MIN_TEMP_SETPOINT, _MAX_TEMP_SETPOINT); - p.ts1 = params.ts1; - } - } - if (modbusSetpoints[1] != params.th1) { - if (paramsChangedByUI) { - _print("th1 - UI -> Modbus - "); - checkParamINT16(¶ms.th1, (int16_t*)&modbusSetpoints[1], _STD_TEMP_HYSTERESIS, _MIN_TEMP_HYSTERESIS, _MAX_TEMP_HYSTERESIS); - } else { - _print("th1 - Modbus -> UI - "); - checkParamINT16((int16_t*)&modbusSetpoints[1], ¶ms.th1, _STD_TEMP_HYSTERESIS, _MIN_TEMP_HYSTERESIS, _MAX_TEMP_HYSTERESIS); - p.th1 = params.th1; - } - } - if (modbusSetpoints[4] != params.ps) { - if (paramsChangedByUI) { - _print("ps - UI -> Modbus - "); - checkParamINT16(¶ms.ps, (int16_t*)&modbusSetpoints[4], _STD_P_SETPOINT, _MIN_P_SETPOINT, _MAX_P_SETPOINT); - } else { - _print("ps - Modbus -> UI - "); - checkParamINT16((int16_t*)&modbusSetpoints[4], ¶ms.ps, _STD_P_SETPOINT, _MIN_P_SETPOINT, _MAX_P_SETPOINT); - p.ps = params.ps; - } - } - if (modbusSetpoints[5] != params.ph) { - if (paramsChangedByUI) { - _print("ph - UI -> Modbus - "); - checkParamINT16(¶ms.ph, (int16_t*)&modbusSetpoints[5], _STD_P_HYSTERESIS, _MIN_P_HYSTERESIS, _MAX_P_HYSTERESIS); - } else { - _print("ph - Modbus -> UI - "); - checkParamINT16((int16_t*)&modbusSetpoints[5], ¶ms.ph, _STD_P_HYSTERESIS, _MIN_P_HYSTERESIS, _MAX_P_HYSTERESIS); - p.ph = params.ph; - } - } - u8 bitNr = 0; - if (bitRead(modbusStates[0], bitNr) != params.tEn) { - _print("tEn - "); - checkParamBool(p.tEn, bitNr, ¶ms.tEn); - } - bitNr++; - if (bitRead(modbusStates[0], bitNr) != params.pInc) { - _print("pInc - "); - checkParamBool(p.pInc, bitNr, ¶ms.pInc); - } - bitNr++; - if (bitRead(modbusStates[0], bitNr) != params.pDec) { - _print("pDec - "); - checkParamBool(p.pDec, bitNr, ¶ms.pDec); - } - bitNr++; - if (bitRead(modbusStates[0], bitNr) != params.cEn) { - _print("cEn - "); - checkParamBool(p.cEn, bitNr, ¶ms.cEn); - } - if (!paramsChangedByUI) { - setParams(p); - } - paramsChangedByUI = false; -} -#endif // _MODBUS == - -// EEPROM Adressen: - -#define _EEPROM_OFFSET 0 // Falls sich die Register nicht mehr beschreiben lassen -#define _EEPROM_MODBUS_ADDRESS 0 + _EEPROM_OFFSET // 1 byte -#define _EEPROM_MODBUS_BAUDRATE 1 + _EEPROM_OFFSET // 4 bytes -#define _EEPROM_MODBUS_DELAY 5 + _EEPROM_OFFSET // 1 byte -#define _EEPROM_TEMP_SETPOINT 6 + _EEPROM_OFFSET // 4 bytes -#define _EEPROM_TEMP_HYSTERESIS 10 + _EEPROM_OFFSET // 4 bytes -#define _EEPROM_P_SETPOINT 14 + _EEPROM_OFFSET // 4 bytes -#define _EEPROM_P_HYSTERESIS 18 + _EEPROM_OFFSET // 4 bytes -#define _EEPROM_P_EN_INC 22 + _EEPROM_OFFSET // 1 byte -#define _EEPROM_P_EN_DEC 23 + _EEPROM_OFFSET // 1 byte -#define _EEPROM_P_SENSOR 24 + _EEPROM_OFFSET // 1 byte -#define _EEPROM_BG_LIGHT 25 + _EEPROM_OFFSET // 1 byte -#define _EEPROM_T_EN 26 + _EEPROM_OFFSET // 1 byte -#define _EEPROM_CONTROL_EN 27 + _EEPROM_OFFSET // 1 byte - -// cs, btnNext, btnPrev, btnSelect, btnCancel, bgLed, Parameter, Messwerte, Modbus, Drucksensor -Display d(10, 3, 4, 5, 6, 9, ¶ms, &vals, &modbusParams, &pSensor, &vStates); -// Analogeingang Druck, OneWire-Pin, Parameter, Messwerte, Display, Drucksensor, Ausgangszustände -// nach vStates: t1Pin, t2Pin, pRisePin, pFallPin -Controller c(A0, 8, ¶ms, &vals, &d, &pSensor, &vStates, 55, 56, 57, 58); - -#if _MODBUS == 1 -void beginModbus() { - mb.begin(modbusParams.address, modbusParams.baudrate); - mb.waitWithAnswerMicroS = modbusParams.delay * 100; // 1/10 ms -> us -} #endif -void getParams() { - parameters p; - EEPROM.get(_EEPROM_TEMP_SETPOINT, p.ts1); - EEPROM.get(_EEPROM_TEMP_HYSTERESIS, p.th1); - EEPROM.get(_EEPROM_P_SETPOINT, p.ps); - EEPROM.get(_EEPROM_P_HYSTERESIS, p.ph); - EEPROM.get(_EEPROM_P_EN_INC, p.pInc); - EEPROM.get(_EEPROM_P_EN_DEC, p.pDec); - EEPROM.get(_EEPROM_T_EN, p.tEn); - EEPROM.get(_EEPROM_CONTROL_EN, p.cEn); - _println("getParams() vor Validierung:"); - _print(" Temperatur Sollwert: "); _println(p.ts1); - _print(" Temperatur Hysterese: "); _println(p.th1); - _print(" Druck Sollwert: "); _println(p.ps); - _print(" Druck Hysterese: "); _println(p.ph); - _print(" Temp-Regelung aktiv: "); _println(p.tEn); - _print(" Drucksteigerung aktiv: "); _println(p.pInc); - _print(" Druckabfall aktiv: "); _println(p.pDec); - _print(" Regler aktiv: "); _println(p.cEn); - _print("ts1 - UI -> Modbus - "); - checkParamINT16(&p.ts1, ¶ms.ts1, _STD_TEMP_SETPOINT, _MIN_TEMP_SETPOINT, _MAX_TEMP_SETPOINT); - _print("th1 - UI -> Modbus - "); - checkParamINT16(&p.th1, ¶ms.th1, _STD_TEMP_HYSTERESIS, _MIN_TEMP_HYSTERESIS, _MAX_TEMP_HYSTERESIS); - _print("ps - UI -> Modbus - "); - checkParamINT16(&p.ps, ¶ms.ps, _STD_P_SETPOINT, _MIN_P_SETPOINT, _MAX_P_SETPOINT); - _print("ph - UI -> Modbus - "); - checkParamINT16(&p.ph, ¶ms.ph, _STD_P_HYSTERESIS, _MIN_P_HYSTERESIS, _MAX_P_HYSTERESIS); - _print("tEn - "); - checkParamUINT8(&p.tEn, ¶ms.tEn, _STD_T_EN); - _print("pInc - "); - checkParamUINT8(&p.pInc, ¶ms.pInc, _STD_P_EN_INC); - _print("pDec - "); - checkParamUINT8(&p.pDec, ¶ms.pDec, _STD_P_EN_DEC); - _print("cEn - "); - checkParamUINT8(&p.cEn, ¶ms.cEn, _STD_C_EN); - _println("getParams() nach Validierung:"); - _print(" Temperatur Sollwert: "); _println(params.ts1); - _print(" Temperatur Hysterese: "); _println(params.th1); - _print(" Druck Sollwert: "); _println(params.ps); - _print(" Druck Hysterese: "); _println(params.ph); - _print(" Temp-Regelung aktiv: "); _println(params.tEn); - _print(" Drucksteigerung aktiv: "); _println(params.pInc); - _print(" Druckabfall aktiv: "); _println(params.pDec); - _print(" Regler aktiv: "); _println(params.cEn); -} +// cs, btnNext, btnPrev, btnSelect, btnCancel, bgLed +Display display(10, 3, 4, 5, 6, 9); +// Analogeingang Druck, OneWire-Pin, t1Pin, t2Pin, pRisePin, pFallPin +Controller controller(A0, 8, 55, 56, 57, 58); -void setParams(parameters &p) { - if (p.ts1 != _SENSOR_FAULT) { - _print("ts1 alt: "); _print(params.ts1); _print(", neu: "); _println(p.ts1); - checkParamINT16(&p.ts1, ¶ms.ts1, _STD_TEMP_SETPOINT, _MIN_TEMP_SETPOINT, _MAX_TEMP_SETPOINT); - EEPROM.put(_EEPROM_TEMP_SETPOINT, params.ts1); -} - if (p.th1 != _SENSOR_FAULT) { - _print("th1 alt: "); _print(params.th1); _print(", neu: "); _println(p.th1); - checkParamINT16(&p.th1, ¶ms.th1, _STD_TEMP_HYSTERESIS, _MIN_TEMP_HYSTERESIS, _MAX_TEMP_HYSTERESIS); - EEPROM.put(_EEPROM_TEMP_HYSTERESIS, params.th1); - } - if (p.ps != _SENSOR_FAULT) { - _print("ps alt: "); _print(params.ps); _print(", neu: "); _println(p.ps); - checkParamINT16(&p.ps, ¶ms.ps, _STD_P_SETPOINT, _MIN_P_SETPOINT, _MAX_P_SETPOINT); - EEPROM.put(_EEPROM_P_SETPOINT, params.ps); - } - if (p.ph != _SENSOR_FAULT) { - _print("ph alt: "); _print(params.ph); _print(", neu: "); _println(p.ph); - checkParamINT16(&p.ph, ¶ms.ph, _STD_P_HYSTERESIS, _MIN_P_HYSTERESIS, _MAX_P_HYSTERESIS); - EEPROM.put(_EEPROM_P_HYSTERESIS, params.ph); - } - if (p.pInc < 2) { - _print("pInc alt: "); _print(params.pInc); _print(", neu: "); _println(p.pInc); - params.pInc = p.pInc; - EEPROM.put(_EEPROM_P_EN_INC, params.pInc); - } - if (p.pDec < 2) { - _print("pDec alt: "); _print(params.pDec); _print(", neu: "); _println(p.pDec); - params.pDec = p.pDec; - EEPROM.put(_EEPROM_P_EN_DEC, params.pDec); - } - if (p.tEn < 2) { - _print("tEn alt: "); _print(params.tEn); _print(", neu: "); _println(p.tEn); - params.tEn = p.tEn; - EEPROM.put(_EEPROM_T_EN, params.tEn); - } - if (p.cEn < 2) { - _print("cEn alt: "); _print(params.cEn); _print(", neu: "); _println(p.cEn); - params.cEn = p.cEn; - EEPROM.put(_EEPROM_CONTROL_EN, params.cEn); - } - paramsChangedByUI = true; -} - -void getModbusParams() { - uint8_t addr = EEPROM.read(_EEPROM_MODBUS_ADDRESS); - if (addr < _MODBUS_ADDR_MIN || addr > _MODBUS_ADDR_MAX) - modbusParams.address = _MODBUS_ADDR_MIN; - else - modbusParams.address = addr; - uint32_t baudrate; - EEPROM.get(_EEPROM_MODBUS_BAUDRATE, baudrate); - switch (baudrate) { - case 115200: break; - case 57600: break; - case 38400: break; - case 19200: break; - case 9600: break; - case 4800: break; - case 2400: break; - case 1200: break; - case 300: break; - default: baudrate = 9600; - } - modbusParams.baudrate = baudrate; - uint8_t delay_ = EEPROM.read(_EEPROM_MODBUS_DELAY); - if (delay_ < _MODBUS_DELAY_MIN) - modbusParams.delay = _MODBUS_DELAY_MIN; - else if (delay_ > _MODBUS_DELAY_MAX) - modbusParams.delay = _MODBUS_DELAY_MAX; - else - modbusParams.delay = delay_; -} - -void setModbusParams(const modbusParameters &p) { - bool changed = false; - if (p.address <= _MODBUS_ADDR_MAX) { - EEPROM.put(_EEPROM_MODBUS_ADDRESS, p.address); - modbusParams.address = p.address; - changed = true; - } - if (p.baudrate != _MODBUS_INVALID_BAUDRATE) { - EEPROM.put(_EEPROM_MODBUS_BAUDRATE, p.baudrate); - modbusParams.baudrate = p.baudrate; - changed = true; - } - if (p.delay <= _MODBUS_DELAY_MAX) { - EEPROM.put(_EEPROM_MODBUS_DELAY, p.delay); - modbusParams.delay = p.delay; - changed = true; - } - if (changed) { -#if _MODBUS == 1 - beginModbus(); -#endif - } -} - -PSensor getPSensor() { - uint8_t val; - EEPROM.get(_EEPROM_P_SENSOR, val); - switch (val) { - case SMC_1_5V_0_5BAR: - pSensor = SMC_1_5V_0_5BAR; - break; - case GEMS_0_5V_0_6BAR: - pSensor = GEMS_0_5V_0_6BAR; - break; - default: - pSensor = SMC_1_5V_0_5BAR; - EEPROM.put(_EEPROM_P_SENSOR, pSensor); - } - return pSensor; -} - -void setPSensor(const PSensor &sensor) { - EEPROM.put(_EEPROM_P_SENSOR, sensor); - pSensor = sensor; -} - -void setup() { +void setup() +{ #if _DEBUG == 1 - Serial.begin(115200); + Serial.begin(115200); #endif - pinMode(53, OUTPUT); // Mega CS-Pin (um Slave-Betrieb zu vermeiden) + pinMode(BTN_PWR, INPUT_PULLUP); + pinMode(53, OUTPUT); // Mega CS-Pin (um Slave-Betrieb zu vermeiden) - paramsChangedByUI = true; + display.init(); + display.bgLight(true); - d.init(); - d.bgLight(true); - d.setModbusParams = setModbusParams; - d.setPSensor = setPSensor; - d.setParams = setParams; + getModbusParams(); + _printf("ModbusAddress: "); + _println(modbusParams.address); + _printf("ModbusBaudrate: "); + _println(modbusParams.baudrate); + getPSensor(); + getParams(); + getFaultSettings(); - getModbusParams(); - _print("ModbusAddress: "); - _println(modbusParams.address); - _print("ModbusBaudrate: "); - _println(modbusParams.baudrate); - getPSensor(); - getParams(); - - pinMode(BTN_PWR, INPUT_PULLUP); + u16 powerOnCount; + EEPROM.get(_EEPROM_POWERON_COUNT, powerOnCount); + switch (powerOnCount) { + case 0: + case 0xFFFF: + powerOnCount = 1; + break; + case 0xFFFE: + break; + default: + powerOnCount++; + } + EEPROM.put(_EEPROM_POWERON_COUNT, powerOnCount); #if _MODBUS == 1 - bool modbusFail = false; - if (!mb.addDiscreteInputArea(0xD0, modbusValves, 1)) - modbusFail = true; - if (!modbusFail && !mb.addHoldingRegisterArea(0xA0, modbusSetpoints, 6)) - modbusFail = true; - if (!modbusFail && !mb.addCoilArea(0xB0, modbusStates, 1)) - modbusFail = true; - if (!modbusFail && !mb.addHoldingRegisterArea(0xC0, modbusRefTime, 1)) - modbusFail = true; - if (!modbusFail && !mb.addInputRegisterArea(0x00, modbusData, _REGS_INFRONTOF_EVENTS + _MODBUS_MAX_EVENTS)) - modbusFail = true; - if (!modbusFail && !mb.addInputRegisterArea(0xF0, modbusMiscReadable, 2)) - modbusFail = true; - if (modbusFail) { - d.modbusProblem(); - while(1); - } - modbusData[0] = 0; // EventCounter - modbusData[1] = 0x8000; // setze das MSB. Das Bit wird durch setzen von modbusRefTime[0] zurückgesetzt - modbusData[2] = _SENSOR_FAULT; // Temp1 in 100stel-°C - modbusData[3] = _SENSOR_FAULT; // Temp2 in 100stel-°C, noch nicht implementiert - modbusData[4] = _SENSOR_FAULT; // Druck in 100stel bar - modbusData[5] = 0; // Zehntelsekunden seit Reglerstart / letzter Referenzierung - modbusMiscReadable[0] = _VERSION_NUMBER; - modbusMiscReadable[1] = 1; // Aktuell ist nur eine Kühlzone implementiert - modbusRefTime[0] = 0xFFFF; - beginModbus(); + setupModbus(powerOnCount); + mb.callbackCoil = modbusCallbackCoil; + mb.callbackRegister = modbusCallbackRegister; #endif - d.greeting(); + display.greeting(); - delay(1000); - c.init(true); - if (params.cEn) - d.bgLight(true); - else - d.bgLight(100); - - timeStamp = millis(); -} - -bool readPwrBtn() { - const uint8_t debounceDelay = 20; - static unsigned long lastDebounceTime; - static bool lastBounceState; - static bool steadyState; - bool currentState = !digitalRead(BTN_PWR); - if (currentState != lastBounceState) { - lastDebounceTime = millis(); - lastBounceState = currentState; - } - if ((millis() - lastDebounceTime) > debounceDelay) - steadyState = currentState; - if (steadyState && (millis() - lastDebounceTime) > 3000) - d.reset(); - return steadyState; -} - -void loop() { - static bool pwrBtnPressed; - bool currentPwrBtnState = readPwrBtn(); - if (!pwrBtnPressed && currentPwrBtnState) { - _println("pwrButton pressed"); - pwrBtnPressed = true; - } else if(pwrBtnPressed && !currentPwrBtnState) { - _println("pwrButton released"); - pwrBtnPressed = false; - parameters p; - p.cEn = !params.cEn; - if (p.cEn) - d.bgLight(true); + delay(1000); + controller.init(true); + if (params.cEn) + display.bgLight(true); else - d.bgLight(100); - setParams(p); - } + display.bgLight(100); - c.process(); - d.process(); + _printf("Gespeicherte Alarme: "); _println(faults.count()); + + timeStamp = millis(); +} + +void loop() +{ + currMillis = millis(); + static bool pwrBtnPressed; + bool currentPwrBtnState = readPwrBtn(); + if (!pwrBtnPressed && currentPwrBtnState) { + _printlnf("pwrButton pressed"); + pwrBtnPressed = true; + } else if(pwrBtnPressed && !currentPwrBtnState) { + _printlnf("pwrButton released"); + pwrBtnPressed = false; + Parameters p; + p.cEn = !params.cEn; + setParams(p, true); + } + + controller.process(); + display.process(); #if _MODBUS == 1 - mb.process(); - checkModbusParams(); - if (refTime != lastRefTime) { - lastRefTime = refTime; - timeStamp = millis(); - modbusData[5] = 0; - eventCounter = 0; - timeStampOverflow = false; - // Setze das 'Timer-Überlauf'-Bit zurück (falls es gesetzt war) - 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) { -#if _DEBUG == 1 - u16 passed = (millis() - timeStamp) / 100; - _print("Der 12 Bit Timer ist übergelaufen. Vergangene Sekunden: "); - _print(passed / 10); _print("."); _println(passed % 10); -#endif // _DEBUG == - timeStampOverflow = true; - bitSet(modbusData[1], 14); - } - modbusData[5] = (millis() - timeStamp) / 100; -#endif // _MODBUS == + mb.process(); + //checkModbusParams(); + static bool firstLoop = true; + if (refTime != lastRefTime) { + // _printf("refTime: "); _print(refTime); _printf(", lastRefTime: "); + // _print(lastRefTime); _printf(", new Alarm: "); _println(bitRead(modbusData[1], 12)); + lastRefTime = refTime; + timeStamp = currMillis; + modbusData[5] = 0; + eventCounter = 0; + timeStampOverflow = false; + // Bit 12 gibt an, ob ein neuer Alarm aufgetreten ist - zurücksetzen + bitClear(modbusData[1], 12); + // Bit 13 gibt an, ob ein Alarm aktiv ist + if (faults.state() == activeFault) { + bitSet(modbusData[1], 13); + } else { + bitClear(modbusData[1], 13); + } + // Setze das 'Timer-Überlauf'-Bit zurück (falls es gesetzt war) + bitClear(modbusData[1], 14); + if (firstLoop) { + // Nachden die 'ReferenzZeit' erstmalig gesetzt wurde, wird dieses Bit gelöscht + bitClear(modbusData[1], 15); + firstLoop = false; + } + // Setze die Durchschnittswerte für die Messwerte zurück + controller.resetAverageCounters(); + _printblnf("Der 12 Bit Timer wurde zurückgesetzt"); + } + modbusData[5] = (currMillis - timeStamp) / 100; + if (modbusDelAlarms[0] != 0 && modbusDelAlarms[0] != 0xFFFF) { + modbusDelAlarms[0] = (faults.removeID(modbusDelAlarms[0])) ? 0 : 0xFFFF; + } else if (modbusDelAlarms[1] != 0 && modbusDelAlarms[1] != 0xFFFF) { + modbusDelAlarms[1] = (faults.removeAll()) ? 0 : 0xFFFF; + } + if (!timeStampOverflow && modbusData[5] > 0xFFF) { +#if _DEBUG_MODBUS == 1 + _printf("Der 12 Bit Timer ist übergelaufen. Vergangene Sekunden: "); + _print(modbusData[5] / 10); _printf("."); _println(modbusData[5] % 10); +#endif // _DEBUG_MODBUS == 1 + timeStampOverflow = true; + bitSet(modbusData[1], 14); + } +#endif // _MODBUS == 1 -#if 1 - static unsigned long counter = 0; - static unsigned long tl = millis(); - if (millis() - tl > 1000) { - tl = millis(); - _print("Loops / Sekunde: "); _println(counter); - counter = 0; - } else { - counter++; - } +#if 0 + static unsigned long counter = 0; + static unsigned long tl = currMillis; + if (currMillis - tl > 1000) { + tl = currMillis; + _printf("Loops / Sekunde: "); _println(counter); + counter = 0; + } else { + counter++; + } #endif } diff --git a/README.md b/README.md index ec429e4..36c7c95 100644 --- a/README.md +++ b/README.md @@ -8,27 +8,24 @@ Das Programm ist für den Atmega2560 (Arduino Mega 2560) geschrieben. Die aktuell einzige externe Abhängigkeit ist die [U8g2-Library von olikraus](https://github.com/olikraus/u8g2), da diese sehr groß ist (> 30 MB), und deshalb nicht in dieses Repository aufgenommen wird. Diese lässt sich einfach mit dem Arduino Library Manager installieren. -## Probleme, offene Fragen - -- Das Macro *_FAST_STEP_MS* (Millisekunden, nach denen eine gedrückte Menütaste ein MenuEvent auslöst) darf nicht auf unter 100 gesetzt werden, da die Displayroutinen sonst zu sehr blockieren und Modbus nicht mehr funktioniert. Dabei muss jedenfalls der Konstruktor ***U8G2_ST7920_128X64_F_HW_SPI*** für das Display verwendet werden, da die Displayroutinen sonst zu langsam sind (siehe Liste unten). Eine Erhöhung der SPI-Clockrate kommt leider nicht in Frage, da im Test das Display dabei nicht mehr richtig funktionierte. Folgend die ungefähr benötigten Zeiten (abhängig vom Konstruktor) in ms, um den Displayinhalt aufzubauen (Homescreen, Startmenü): - - *U8G2_ST7920_128X64_1_HW_SPI*: 192, 125 - - *U8G2_ST7920_128X64_2_HW_SPI*: 131, 92 - - *U8G2_ST7920_128X64_F_HW_SPI*: 87, 67 -- Der Durchschnittswert der Temperatur wird nicht mehr gebildet (außer das Macro *_MODBUS_AVERAGE_TEMPERATURES* ist 1 - dafür reicht der RAM aber eh nicht ;-) ). -***Frage:*** Ist die Mittelung der Temperatur wirklich nicht notwendig? # Modbus ## Coils - FC 1, 5, 15 -Betriebszustand Regler (durch die Modbus-Implementierung sind die Register ***0xB4...0xB7*** les-/schreibbar – diese Werte werden ignoriert): +Regler-Settings: -- ***0xB0***: (de)aktiviere Temperaturregelung -- ***0xB1***: (de)aktiviere Drucksteigerung -- ***0xB2***: (de)aktiviere Druckabfall -- ***0xB3***: (de)aktiviere Regler (unabhängig von den vorigen Werten kann hier der Regler komplett deaktiviert werden) +- ***0xB0***: aktiviere Temperaturregelung +- ***0xB1***: aktiviere Drucksteigerung +- ***0xB2***: aktiviere Druckabfall +- ***0xB3***: aktiviere Regler (unabhängig von den vorigen Werten kann hier der Regler komplett deaktiviert werden) +- ***0xB4***: aktiviere akustisches Warnsignal +- ***0xB5***: aktiviere optisches Warnsignal (pulsierende Displaybeleuchtung) +- ***0xB6***: aktiviere Temperaturregelung im Fehlerfall +- ***0xB7***: aktiviere Druckregelung im Fehlerfall --- + ## Discrete Inputs - FC 2 Ausgangszustände Ventile (durch die Modbus-Implementierung sind die Register ***0xD4...0xD7*** lesbar und geben immer 0 zurück): @@ -39,6 +36,7 @@ Ausgangszustände Ventile (durch die Modbus-Implementierung sind die Register ** - ***0xD3***: Ventil Druckabfall --- + ## Input Register - FC 4 Anzahl der gespeicherten Ereignisse abfragen: @@ -48,48 +46,105 @@ Anzahl der gespeicherten Ereignisse abfragen: Verschiedenes: Ausgangszustände Ventile, Betriebsart Regler, Flags: - ***0x01***: - - Bit 0...3: siehe Coils (Bit 0: ***0xB0***) - - Bit 4...7: reserviert + - Bit 0...7: siehe Coils (Bit 0: ***0xB0***) - Bit 8...11: siehe Discrete Inputs (Bit 8: ***0xD0***) - - Bit 12...13: reserviert - - Bit 14: ist gesetzt, falls der 12 Bit Timer übergelaufen ist + - Bit 12: ist gesetzt, falls ein neuer Alarm seit der letzten Referenzierung (siehe Holding-Register ***0xC0*** - setzen der Referenzzeit) aufgetreten ist, auch wenn er nicht mehr aktiv sein sollte. + - Bit 13: ist gesetzt, falls ein Alarm aktiv ist + - Bit 14: ist gesetzt, falls der 12 Bit Timer übergelaufen ist (wenn das Holding-Register ***0xC0*** (setzen der Referenzzeit) vor über 409,6 Sekunden zuletzt beschrieben wurde) - Bit 15: ist gesetzt, bevor das Holding-Register ***0xC0*** (setzen der Referenzzeit) erstmalig beschrieben wurde Messwerte: - ***0x02***: Temperatur 1 - aktuelle Temperatur als INT16 in Hundertstel-°C -- ***0x03***: Temperatur 2 - noch nicht genutzt – gibt *0xFFFF* zurück -- ***0x04***: Druck - Durchschnittswert zwischen 2 Abfragen (der letzten 409,6 Sekunden bzw. der letzten Referenzierung - siehe Holding-Register ***0xC0***) als INT16 in Hundertstel-Bar, einzeln vorkommende Sensorfehler werden ignoriert, bei häufigeren Fehlern wird *0xFFFF* zurückgegeben +- ***0x03***: Temperatur 2 - noch nicht genutzt – gibt *0x8000* zurück +- ***0x04***: Druck - Durchschnittswert zwischen 2 Abfragen (der letzten 409,6 Sekunden bzw. der letzten Referenzierung - siehe Holding-Register ***0xC0***) als INT16 in Hundertstel-Bar, einzeln vorkommende Sensorfehler werden ignoriert, bei häufigeren Fehlern wird *0x8000* zurückgegeben gespeicherte Ereignisse: - ***0x05***: Vergangene Zehntelsekunden seit letzter Referenzierung bzw Reglerstart. Ein Überlaufen der Variable wird vom Regler nicht geprüft, da der interne 12 Bit Timer viel früher überläuft, und dabei das ‚Überlauf-Bit‘ im Input-Register ***0x01*** gesetzt wird. -- ab ***0x06*** +- ab ***0x06*** (bis *0xFF*) - Bit 0...11 geben den Zeitpunkt relativ zur gespeicherten Referenz (alles in 1/10-Sekunden – siehe Holding-Register ***0xC0***) an. Sobald dieser Offset aber den Wert *0x0FFF* erreicht, wird er nicht mehr geändert, bis die Referenzzeit aktualisiert wird. - Bit 12...14 bezeichnen den geschalteten Ausgang (z.B.: x011-xxxx-xxxx-xxxx für ‚Druckabfall‘) - Bit 15 gibt an, ob der der Ausgang ein- / ausgeschaltet wurde. -sonstiges: +Alarme: -- ***0xF0***: Firmware-Version Regler (4 MSBs: Major, 6 Bits Minor, 6 LSBs: Micro) -- ***0xF1***: Anzahl der Kühlzonen (aktuell nur 1 Zone implementiert) +- ***0x0100***: Anzahl der gespeicherten Alarme (0 ... 30) +- ab ***0x0101*** (bis *0x0160*): Ein aufgetretener Alarm wird in drei Registern abgebildet (folglich wäre der z.B. 10te Alarm in den Registern *0x0131 ... 0x0133* auszulesen) + - **Register 1**: Die ID des Alarms. Wenn der Master die IDs speichert, kann er diese zuordnen, wenn der gleiche Alarm mehrmals ausgelesen wird (aktive Alarme bzw. nicht gelöschte deaktivierte Alarme). Unter der Annahme, dass der Regler während seiner gesamten Einsatzdauer nicht mehr als 65534 Alarme registriert (0 und 0xFFFF sind keine gültigen IDs), ist diese ID praktisch eindeutig. + - **Register 2**: + - Bit 0...11: Zeitpunkt als der Alarm ausgelöst wurde (siehe Input-Register ***0x06***) bzw. einen Zähler, der mit jedem Einschalten/Reset des Reglers inkrementiert wird (siehe Register 3 Bit 12). Der Wert ist auf 0xFFF gesetzt, falls Register 3 Bit 13 (Timer-Überlauf) gesetzt ist. + - Bit 12...15 geben den Fehlercode an: + - 0: Sollte nur aus Registern zu lesen sein, welche noch nicht mit einem Fehler belegt sind. + - 1: Temperatur 1 zu hoch + - 2: Temperatur 1 zu niedrig + - 3: Temperatur 1 Sensorfehler + - 4: Temperatur 2 zu hoch + - 5: Temperatur 2 zu niedrig + - 6: Temperatur 2 Sensorfehler + - 7: Druck zu hoch + - 8: Druck zu niedrig + - 9: Druck-Sensorfehler (Nur für 1-5 Volt Sensor verfügbar) + - **Register 3**: + - Bit 0...11: Zeitpunkt, als der Alarm durch Beheben des Fehlers inaktiv wurde bzw. einen Zähler, der mit jedem Einschalten/Reset des Reglers inkrementiert wird (siehe Bit 14). Der Wert ist auf 0xFFF gesetzt, falls Bit 15 (Timer-Überlauf) gesetzt ist. Der Wert 0 besagt, dass der Alarm noch aktiv ist. Falls der Alarm nie deaktiviert wurde, aber trotzdem inaktiv ist (weil z.B. ein 'Druck zu niedrig'-Alarm direkt von einem 'Druck zu hoch'-Alarm abgelöst wird) und Bit 14 NICHT gesetzt ist, ist der Wert 1. Gültige Zeitpunkte sind somit immer > 1. + - Bit 12: wenn gesetzt: Register 2 enthält die Anzahl der Resets seitdem der Alarm ausgelöst wurde + - Bit 13: ist gesetzt, falls der interne 12 Bit Timer für Register 2 (Alarm wurde aktiviert) übergelaufen sein sollte (siehe Input-Register *0x01* Bit 14) + - Bit 14: wenn gesetzt: Register 3 enthält die Anzahl der Resets seitdem der Alarm inaktiv wurde + - Bit 15: ist gesetzt, falls der interne 12 Bit Timer für Register 3 (Alarm wurde deaktiviert) übergelaufen sein sollte (siehe Input-Register *0x01* Bit 14) +Regler-Info: + +- ***0x0F00***: Firmware-Version (4 MSBs: Major, 6 Bits Minor, 6 LSBs: Micro) +- ***0x0F01***: Anzahl der Kühlzonen (aktuell nur 1 Zone implementiert - In Zukunft wird man (z.B. bei der Inbetriebnahme) zwischen 1- und 2-Zonen-Betrieb wählen können) +- ***0x0F02***: Wie oft der Regler bis jetzt eingeschaltet wurde (beginnend bei 1) +- ***0x0F03***: Werkseinstellung für Coils ***0xB0...0xB7*** -> Bit0(LSB)...Bit7 - Das MSByte ist zu ignorieren +- ***0x0F04***: Werkseinstellung Sollwert Temperatur/en (in Hundertstel-°C) +- ***0x0F05***: Werkseinstellung Hysterese Temperatur/en (in Hundertstel-°C) +- ***0x0F06***: Werkseinstellung Sollwert Druck (in Hundertstel-Bar) +- ***0x0F07***: Werkseinstellung Hysterese Druck (in Hundertstel-Bar) +- ***0x0F08***: Werkseinstellung Sollwert min. Temperatursteigerungsrate (in Hundertstel-°C - zur Alarmauslösung) +- ***0x0F09***: Werkseinstellung Sollwert min. Temperaturabfallrate (in Hundertstel-°C - zur Alarmauslösung) +- ***0x0F10***: Werkseinstellung Sollwert min. Druckänderungsrate (in Hundertstel-Bar - zur Alarmauslösung) +- ***0x0F11***: Mindestwert Sollwert Temperatur/en (in Hundertstel-°C) +- ***0x0F12***: Maximalwert Sollwert Temperatur/en (in Hundertstel-°C) +- ***0x0F13***: Mindestwert Hysterese Temperatur/en (in Hundertstel-°C) +- ***0x0F14***: Maximalwert Hysterese Temperatur/en (in Hundertstel-°C) +- ***0x0F15***: Mindestwert Sollwert Druck (in Hundertstel-Bar) +- ***0x0F16***: Maximalwert Sollwert Druck (in Hundertstel-Bar) +- ***0x0F17***: Mindestwert Hysterese Druck (in Hundertstel-Bar) +- ***0x0F18***: Maximalwert Hysterese Druck (in Hundertstel-Bar) +- ***0x0F19***: Mindestwert Änderungsrate für Temperatur/en und Druck (in Hundertstel - zur Alarmauslösung) +- ***0x0F20***: Maximalwert Änderungsrate für Temperatur/en und Druck (in Hundertstel - zur Alarmauslösung) --- + ## Holding Register - FC 3, 6, 16 -Sollwerte: +- ***0xA0***: Bit 0...7: siehe Coils (Bit 0: ***0xB0***) -- ***0xA0***: Temperatur 1 Sollwert -- ***0xA1***: Temperatur 1 Hysterese -- ***0xA2***: Temperatur 2 Sollwert (noch nicht implementiert – Wert wird ignoriert) -- ***0xA3***: Temperatur 2 Hysterese (noch nicht implementiert – Wert wird ignoriert) -- ***0xA4***: Druck Sollwert -- ***0xA5***: Druck Hysterese +Sollwerte in Hundertsel- [ °C | Bar ]: + +- ***0xA1***: Temperatur 1 Sollwert +- ***0xA2***: Temperatur 1 Hysterese +- ***0xA3***: Temperatur 2 Sollwert (noch nicht implementiert – Wert wird ignoriert) +- ***0xA4***: Temperatur 2 Hysterese (noch nicht implementiert – Wert wird ignoriert) +- ***0xA5***: Druck Sollwert +- ***0xA6***: Druck Hysterese + +Werte zur Alarmauslösung (Hundertsel- [°C / 1 Stunde | Bar / 10 Sekunden) - bei Nichterreichen dieser Werte wird ein entsprechender Alarm ausgelöst: + +- ***0xA7***: Temperatur-Steigerungsrate +- ***0xA8***: Temperatur-Abfallrate +- ***0xA9***: Druckrate (Steigerung und Abfall) Setzen der Referenzzeit: - ***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. + - den entsprechenden Zeitstempel 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) mit dem Event-Zeitpunkt und dem aktuellen Zeitstempel des Masters gegenrechnen. + +Löschen von Alarmen: + +- ***0x0200***: Wird in dieses Register eine gültige Alarm-ID (siehe Input-Register ***0x0101***) geschrieben, wird der Alarm aus dem Regler gelöscht, insofern er existiert und nicht aktiv ist. Wurde der Alarm erfolgreich gelöscht, enthält das Register danach den Wert 0, ansonsten 0xFFFF (Alarm nicht vorhanden oder noch aktiv). Dieses Register ist mit 0 initialisiert. +- ***0x0201***: Wird in dieses Register ein anderer Wert als 0 oder 0xFFFF geschrieben, werden alle nicht aktiven Alarme aus dem Regler gelöscht. War mindestens ein nicht aktiver Alarm zum Löschen vorhanden, enthält dieses Register danach den Wert 0, ansonsten 0xFFFF. Dieses Register ist mit 0 initialisiert. diff --git a/src/common.cpp b/src/common.cpp new file mode 100644 index 0000000..d7d2942 --- /dev/null +++ b/src/common.cpp @@ -0,0 +1,614 @@ +#include "common.h" +#include + +extern Display display; +extern u32 currMillis; +extern u16 zones; + +static void checkParamINT16(int16_t *source, int16_t *target, const int &std, const int &min, const int &max) +{ + _printf("source: "); _print(*source); + if (*source == _SENSOR_FAULT) { + *source = *target = std; + } else if (*source > max) { + *source = *target = max; + } else if (*source < min) { + *source = *target = min; + } else if (*source == min || *source == max) { + *target = *source; + } else { + u8 mod = *source % 5; + _printf(", modulo: "); _print(mod); + if (mod == 0) + *target = *source; + else { + int tempVal = *source - mod; + _printf(", tempVal: "); _print(tempVal); + if (mod > 2) { // runde auf den nächsten 5er Schritt bzw. setze auf max + if ((tempVal + 5) > max) { + *source = *target = max; + _printf(" ... auf max setzen"); + } else { + *source = *target = tempVal + 5; + _printf(" ... aufrunden"); + } + } else { // runde auf den vorigen 5er Schritt bzw. setze auf min + if (tempVal < min) { + *source = *target = min; + _printf(" ... auf min setzen"); + } else { + *source = *target = tempVal; + _printf(" ... abrunden"); + } + } + } + } + _printf(", target: "); _print(*target); _printf(", source: "); _println(*source); +} + +static void checkParamUINT8(u8 *source, u8 *target, const u8 &std) +{ + _printf("checkParamUINT8 source vorher: "); _print(*source); + if (*source > 1) { + *source = *target = std; + } else { + *target = *source; + } + _printf(", target: "); _print(*target); _printf(", source: "); _println(*source); +} + +#if _MODBUS == 1 +#include "modbus/ModbusRTUSlave.h" + +u8 modbusCoils[1]; // Bit0: tEn, 1: pInc, 2: pDec, 3: cEn, 4: akustisch, 5: optisch, 6: t-off-if-error, 7: p-off-if-error +u8 modbusValves[1]; // Bit0: Temp1, Bit1: Temp2, Bit2: Druck +// 0: Event-Counter, 1: high: modbusValves[0], low: modbusCoils[0], 2...4: Temp 1, 2, Druck, ab5: gespeicherte Schaltvorgänge +u16 modbusData[_REGS_INFRONTOF_EVENTS + _MODBUS_MAX_EVENTS]; +u16 modbusMiscReadable[21]; // Version, Kühlzonen, Anzahl PowerOns, Konstanten +// Anzahl der gespeicherten Alarme + gespeicherte Alarme +u16 modbusAlarms[1 + (_MAX_FAULTS * 3)]; +// Löschen von einzelnen/allen Alarmen +u16 modbusDelAlarms[2]; +// modbusCoils[0], Temp1, 2, Druck jeweils Sollwert + Hysterese, T+Error, T-Error, P+-Error +u16 modbusHolding[10]; +u16 modbusRefTime[1]; // setzt bei Änderung Event-Counter und -Timer zurück + +u16 &eventCounter = modbusData[0]; +u16 &refTime = modbusRefTime[0]; + +extern ModbusRTUSlave mb; + +static void setBitInRegisters(u8 bitNr, u8 val) +{ + _print("Setze Register "); _print(bitNr); _print(" auf "); _println(val); + bitWrite(modbusCoils[0], bitNr, val); + bitWrite(modbusData[1], bitNr, val); + bitWrite(modbusHolding[0], bitNr, val); +} + +static void setBitInRegisters(u8 bitNr, u8 val, Parameters &p, FaultSettings &fs) +{ + setBitInRegisters(bitNr, val); + switch(bitNr) { + case 0: // Temperaturregelung + p.tEn = val; + break; + case 1: // Drucksteigerung + p.pInc = val; + break; + case 2: // Druckabfall + p.pDec = val; + break; + case 3: // Regler aktiv + p.cEn = val; + break; + case 4: // akustisches Warnsignal + fs.acoustic = val; + break; + case 5: // optisches Warnsignal + fs.optical = val; + break; + case 6: // Temperaturregelung im Fehlerfall + fs.tOnIfError = val; + break; + case 7: // Druckregelung im Fehlerfall + fs.pOnIfError = val; + break; + } +} + +void beginModbus() +{ + mb.begin(modbusParams.address, modbusParams.baudrate); + mb.waitWithAnswerMicroS = modbusParams.delay * 100; // 1/10 ms -> us +} + +void setupModbus(const u16 &powerOnCount) +{ + bool modbusFail = false; + if (!mb.addDiscreteInputArea(0xD0, modbusValves, 1)) + modbusFail = true; + if (!modbusFail && !mb.addHoldingRegisterArea(0xA0, modbusHolding, 10)) + modbusFail = true; + if (!modbusFail && !mb.addCoilArea(0xB0, modbusCoils, 1)) + modbusFail = true; + if (!modbusFail && !mb.addHoldingRegisterArea(0xC0, modbusRefTime, 1)) + modbusFail = true; + if (!modbusFail && !mb.addInputRegisterArea(0x00, modbusData, _REGS_INFRONTOF_EVENTS + _MODBUS_MAX_EVENTS)) + modbusFail = true; + if (!modbusFail && !mb.addInputRegisterArea(0x0100, modbusAlarms, 1 + (_MAX_FAULTS * 3))) + modbusFail = true; + if (!modbusFail && !mb.addHoldingRegisterArea(0x0200, modbusDelAlarms, 2)) + modbusFail = true; + if (!modbusFail && !mb.addInputRegisterArea(0x0F00, modbusMiscReadable, 21)) + modbusFail = true; + if (modbusFail) { + display.modbusProblem(); + while(1); + } + modbusData[0] = 0; // EventCounter + modbusData[1] |= 0x8000; // setze das MSB. Das Bit wird durch setzen von modbusRefTime[0] zurückgesetzt + modbusData[2] = _SENSOR_FAULT; // Temp1 in 100stel-°C + modbusData[3] = _SENSOR_FAULT; // Temp2 in 100stel-°C, noch nicht implementiert + modbusData[4] = _SENSOR_FAULT; // Druck in 100stel bar + modbusData[5] = 0; // Zehntelsekunden seit Reglerstart / letzter Referenzierung + modbusAlarms[0] = 0; // Alarm-Counter + modbusDelAlarms[0] = 0; // Löschen eines Alarms anhand einer ID + modbusDelAlarms[1] = 0; // Alle inaktiven Alarme löschen + modbusMiscReadable[0] = _VERSION_NUMBER; + modbusMiscReadable[1] = zones; + modbusMiscReadable[2] = powerOnCount; + modbusMiscReadable[3] = _STD_T_EN; + bitWrite(modbusMiscReadable[3], 1, _STD_P_EN_INC); + bitWrite(modbusMiscReadable[3], 2, _STD_P_EN_DEC); + bitWrite(modbusMiscReadable[3], 3, _STD_C_EN); + bitWrite(modbusMiscReadable[3], 4, _STD_FAULTS_ACOUSTIC); + bitWrite(modbusMiscReadable[3], 5, _STD_FAULTS_OPTICAL); + bitWrite(modbusMiscReadable[3], 6, _STD_FAULTS_T_ON); + bitWrite(modbusMiscReadable[3], 7, _STD_FAULTS_P_ON); + modbusMiscReadable[4] = _STD_TEMP_SETPOINT; + modbusMiscReadable[5] = _STD_TEMP_HYSTERESIS; + modbusMiscReadable[6] = _STD_P_SETPOINT; + modbusMiscReadable[7] = _STD_P_HYSTERESIS; + modbusMiscReadable[8] = _STD_FAULTS_T_R_RATE; + modbusMiscReadable[9] = _STD_FAULTS_T_F_RATE; + modbusMiscReadable[10] = _STD_FAULTS_P_RATE; + modbusMiscReadable[11] = _MIN_TEMP_SETPOINT; + modbusMiscReadable[12] = _MAX_TEMP_SETPOINT; + modbusMiscReadable[13] = _MIN_TEMP_HYSTERESIS; + modbusMiscReadable[14] = _MAX_TEMP_HYSTERESIS; + modbusMiscReadable[15] = _MIN_P_SETPOINT; + modbusMiscReadable[16] = _MAX_P_SETPOINT; + modbusMiscReadable[17] = _MIN_P_HYSTERESIS; + modbusMiscReadable[18] = _MAX_P_HYSTERESIS; + modbusMiscReadable[19] = _MIN_FAULTS_RATE; + modbusMiscReadable[20] = _MAX_FAULTS_RATE; + modbusRefTime[0] = 0xFFFF; + beginModbus(); +} + +void modbusCallbackCoil(u16 reg, bool val) +{ + Parameters p; + FaultSettings fs; + if ((reg >= 0xB0) && (reg <= 0xB7)) { + setBitInRegisters(reg - 0xB0, val, p, fs); + } + setParams(p, false); + setFaultSettings(fs, false); +} + +u16 modbusCallbackRegister(u16 reg, u16 val) +{ + Parameters p; + FaultSettings fs; + switch(reg) { + case 0xA0: // Bit 0...7: siehe Coils (Bit 0: 0xB0) + for (u8 i=0; i<8; i++) { + u8 bitVal = bitRead(val, i); + if (bitVal != bitRead(modbusHolding[0], i)) { + setBitInRegisters(i, bitVal, p, fs); + } + } + break; + case 0xA1: // Temperatur 1 Sollwert + checkParamINT16((int16_t*)&val, &p.ts1, _STD_TEMP_SETPOINT, _MIN_TEMP_SETPOINT, _MAX_TEMP_SETPOINT); + val = p.ts1; + break; + case 0xA2: // Temperatur 1 Hysterese + checkParamINT16((int16_t*)&val, &p.th1, _STD_TEMP_HYSTERESIS, _MIN_TEMP_HYSTERESIS, _MAX_TEMP_HYSTERESIS); + val = p.th1; + break; + case 0xA3: // Temperatur 2 Sollwert (noch nicht implementiert – Wert wird ignoriert) + case 0xA4: // Temperatur 2 Hysterese (noch nicht implementiert – Wert wird ignoriert) + break; + case 0xA5: // Druck Sollwert + checkParamINT16((int16_t*)&val, &p.ps, _STD_P_SETPOINT, _MIN_P_SETPOINT, _MAX_P_SETPOINT); + val = p.ps; + break; + case 0xA6: // Druck Hysterese + checkParamINT16((int16_t*)&val, &p.ph, _STD_P_HYSTERESIS, _MIN_P_HYSTERESIS, _MAX_P_HYSTERESIS); + val = p.ph; + break; + case 0xA7: // Temperatur-Steigerungsrate (Hundertstel-°C / 1 Stunde) + checkParamINT16((int16_t*)&val, &fs.tMinRRate, _STD_FAULTS_T_R_RATE, _MIN_FAULTS_RATE, _MAX_FAULTS_RATE); + val = fs.tMinRRate; + break; + case 0xA8: // Temperatur-Abfallrate (Hundertstel-°C / 1 Stunde) + checkParamINT16((int16_t*)&val, &fs.tMinFRate, _STD_FAULTS_T_F_RATE, _MIN_FAULTS_RATE, _MAX_FAULTS_RATE); + val = fs.tMinFRate; + break; + case 0xA9: // Druckrate (Steigerung und Abfall - Hundertstel-Bar / 10 Sekunden) + checkParamINT16((int16_t*)&val, &fs.pMinRate, _STD_FAULTS_P_RATE, _MIN_FAULTS_RATE, _MAX_FAULTS_RATE); + val = fs.pMinRate; + break; + } + setParams(p, false); + setFaultSettings(fs, false); + return val; +} +#endif // _MODBUS == 1 + +void getParams() +{ + Parameters p; + EEPROM.get(_EEPROM_TEMP_SETPOINT, p.ts1); + EEPROM.get(_EEPROM_TEMP_HYSTERESIS, p.th1); + EEPROM.get(_EEPROM_P_SETPOINT, p.ps); + EEPROM.get(_EEPROM_P_HYSTERESIS, p.ph); + EEPROM.get(_EEPROM_P_EN_INC, p.pInc); + EEPROM.get(_EEPROM_P_EN_DEC, p.pDec); + EEPROM.get(_EEPROM_T_EN, p.tEn); + EEPROM.get(_EEPROM_CONTROL_EN, p.cEn); + _printlnf("getParams() vor Validierung:"); + _printf(" Temperatur Sollwert: "); _println(p.ts1); + _printf(" Temperatur Hysterese: "); _println(p.th1); + _printf(" Druck Sollwert: "); _println(p.ps); + _printf(" Druck Hysterese: "); _println(p.ph); + _printf(" Temp-Regelung aktiv: "); _println(p.tEn); + _printf(" Drucksteigerung aktiv: "); _println(p.pInc); + _printf(" Druckabfall aktiv: "); _println(p.pDec); + _printf(" Regler aktiv: "); _println(p.cEn); + _printf("ts1 - UI -> Modbus - "); + checkParamINT16(&p.ts1, ¶ms.ts1, _STD_TEMP_SETPOINT, _MIN_TEMP_SETPOINT, _MAX_TEMP_SETPOINT); + modbusHolding[1] = params.ts1; + _printf("th1 - UI -> Modbus - "); + checkParamINT16(&p.th1, ¶ms.th1, _STD_TEMP_HYSTERESIS, _MIN_TEMP_HYSTERESIS, _MAX_TEMP_HYSTERESIS); + modbusHolding[2] = params.th1; + _printf("ps - UI -> Modbus - "); + checkParamINT16(&p.ps, ¶ms.ps, _STD_P_SETPOINT, _MIN_P_SETPOINT, _MAX_P_SETPOINT); + modbusHolding[5] = params.ps; + _printf("ph - UI -> Modbus - "); + checkParamINT16(&p.ph, ¶ms.ph, _STD_P_HYSTERESIS, _MIN_P_HYSTERESIS, _MAX_P_HYSTERESIS); + modbusHolding[6] = params.ph; + _printf("tEn - "); + checkParamUINT8(&p.tEn, ¶ms.tEn, _STD_T_EN); + setBitInRegisters(0, params.tEn); + _printf("pInc - "); + checkParamUINT8(&p.pInc, ¶ms.pInc, _STD_P_EN_INC); + setBitInRegisters(1, params.pInc); + _printf("pDec - "); + checkParamUINT8(&p.pDec, ¶ms.pDec, _STD_P_EN_DEC); + setBitInRegisters(2, params.pDec); + _printf("cEn - "); + checkParamUINT8(&p.cEn, ¶ms.cEn, _STD_C_EN); + setBitInRegisters(3, params.cEn); + _printlnf("getParams() nach Validierung:"); + _printf(" Temperatur Sollwert: "); _println(params.ts1); + _printf(" Temperatur Hysterese: "); _println(params.th1); + _printf(" Druck Sollwert: "); _println(params.ps); + _printf(" Druck Hysterese: "); _println(params.ph); + _printf(" Temp-Regelung aktiv: "); _println(params.tEn); + _printf(" Drucksteigerung aktiv: "); _println(params.pInc); + _printf(" Druckabfall aktiv: "); _println(params.pDec); + _printf(" Regler aktiv: "); _println(params.cEn); +} + +void setParams(Parameters &p, bool writeModbusRegister) +{ + if (p.ts1 != _SENSOR_FAULT) { + _printf("ts1 alt: "); _print(params.ts1); _printf(", neu: "); _println(p.ts1); + checkParamINT16(&p.ts1, ¶ms.ts1, _STD_TEMP_SETPOINT, _MIN_TEMP_SETPOINT, _MAX_TEMP_SETPOINT); + EEPROM.put(_EEPROM_TEMP_SETPOINT, params.ts1); + if (writeModbusRegister) modbusHolding[1] = params.ts1; + } + if (p.th1 != _SENSOR_FAULT) { + _printf("th1 alt: "); _print(params.th1); _printf(", neu: "); _println(p.th1); + checkParamINT16(&p.th1, ¶ms.th1, _STD_TEMP_HYSTERESIS, _MIN_TEMP_HYSTERESIS, _MAX_TEMP_HYSTERESIS); + EEPROM.put(_EEPROM_TEMP_HYSTERESIS, params.th1); + if (writeModbusRegister) modbusHolding[2] = params.th1; + } + if (p.ps != _SENSOR_FAULT) { + _printf("ps alt: "); _print(params.ps); _printf(", neu: "); _println(p.ps); + checkParamINT16(&p.ps, ¶ms.ps, _STD_P_SETPOINT, _MIN_P_SETPOINT, _MAX_P_SETPOINT); + EEPROM.put(_EEPROM_P_SETPOINT, params.ps); + if (writeModbusRegister) modbusHolding[5] = params.ps; + } + if (p.ph != _SENSOR_FAULT) { + _printf("ph alt: "); _print(params.ph); _printf(", neu: "); _println(p.ph); + checkParamINT16(&p.ph, ¶ms.ph, _STD_P_HYSTERESIS, _MIN_P_HYSTERESIS, _MAX_P_HYSTERESIS); + EEPROM.put(_EEPROM_P_HYSTERESIS, params.ph); + if (writeModbusRegister) modbusHolding[6] = params.ph; + } + if (p.pInc < 2) { + _printf("pInc alt: "); _print(params.pInc); _printf(", neu: "); _println(p.pInc); + params.pInc = p.pInc; + EEPROM.put(_EEPROM_P_EN_INC, params.pInc); + if (writeModbusRegister) setBitInRegisters(1, params.pInc); + } + if (p.pDec < 2) { + _printf("pDec alt: "); _print(params.pDec); _printf(", neu: "); _println(p.pDec); + params.pDec = p.pDec; + EEPROM.put(_EEPROM_P_EN_DEC, params.pDec); + if (writeModbusRegister) setBitInRegisters(2, params.pDec); + } + if (p.tEn < 2) { + _printf("tEn alt: "); _print(params.tEn); _printf(", neu: "); _println(p.tEn); + params.tEn = p.tEn; + EEPROM.put(_EEPROM_T_EN, params.tEn); + if (writeModbusRegister) setBitInRegisters(0, params.tEn); + } + if (p.cEn < 2) { + _printf("cEn alt: "); _print(params.cEn); _printf(", neu: "); _println(p.cEn); + params.cEn = p.cEn; + EEPROM.put(_EEPROM_CONTROL_EN, params.cEn); + if (writeModbusRegister) setBitInRegisters(3, params.cEn); + } +} + +void getModbusParams() +{ + uint8_t addr = EEPROM.read(_EEPROM_MODBUS_ADDRESS); + if (addr < _MODBUS_ADDR_MIN || addr > _MODBUS_ADDR_MAX) + modbusParams.address = _MODBUS_ADDR_MIN; + else + modbusParams.address = addr; + uint32_t baudrate; + EEPROM.get(_EEPROM_MODBUS_BAUDRATE, baudrate); + switch (baudrate) { + case 115200: break; + case 57600: break; + case 38400: break; + case 19200: break; + case 9600: break; + case 4800: break; + case 2400: break; + case 1200: break; + case 300: break; + default: baudrate = 9600; + } + modbusParams.baudrate = baudrate; + uint8_t delay_ = EEPROM.read(_EEPROM_MODBUS_DELAY); + if (delay_ < _MODBUS_DELAY_MIN) + modbusParams.delay = _MODBUS_DELAY_MIN; + else if (delay_ > _MODBUS_DELAY_MAX) + modbusParams.delay = _MODBUS_DELAY_MAX; + else + modbusParams.delay = delay_; +} + +void setModbusParams(const ModbusParameters &p) +{ + bool changed = false; + if (p.address <= _MODBUS_ADDR_MAX) { + EEPROM.put(_EEPROM_MODBUS_ADDRESS, p.address); + modbusParams.address = p.address; + changed = true; + } + if (p.baudrate != _MODBUS_INVALID_BAUDRATE) { + EEPROM.put(_EEPROM_MODBUS_BAUDRATE, p.baudrate); + modbusParams.baudrate = p.baudrate; + changed = true; + } + if (p.delay <= _MODBUS_DELAY_MAX) { + EEPROM.put(_EEPROM_MODBUS_DELAY, p.delay); + modbusParams.delay = p.delay; + changed = true; + } + if (changed) { +#if _MODBUS == 1 + beginModbus(); +#endif + } +} + +void getPSensor() +{ + uint8_t val; + EEPROM.get(_EEPROM_P_SENSOR, val); + switch (val) { + case SMC_1_5V_0_5BAR: + pSensor = SMC_1_5V_0_5BAR; + break; + case GEMS_0_5V_0_6BAR: + pSensor = GEMS_0_5V_0_6BAR; + break; + default: + pSensor = SMC_1_5V_0_5BAR; + EEPROM.put(_EEPROM_P_SENSOR, pSensor); + } + return pSensor; +} + +void setPSensor(const PSensor &sensor) +{ + EEPROM.put(_EEPROM_P_SENSOR, sensor); + pSensor = sensor; +} + +void getFaultSettings() +{ + FaultSettings settings; + EEPROM.get(_EEPROM_FAULTS_ACOUSTIC, settings.acoustic); + EEPROM.get(_EEPROM_FAULTS_OPTICAL, settings.optical); + EEPROM.get(_EEPROM_FAULTS_T_ON, settings.tOnIfError); + EEPROM.get(_EEPROM_FAULTS_P_ON, settings.pOnIfError); + EEPROM.get(_EEPROM_FAULTS_T_R_RATE, settings.tMinRRate); + EEPROM.get(_EEPROM_FAULTS_T_F_RATE, settings.tMinFRate); + EEPROM.get(_EEPROM_FAULTS_P_RATE, settings.pMinRate); + _printlnf("getFaultSettings() vor Validierung:"); + _printf(" akustisches Signal: "); _println(settings.acoustic); + _printf(" optisches Signal : "); _println(settings.optical); + _printf(" t on if error : "); _println(settings.tOnIfError); + _printf(" p on if error : "); _println(settings.pOnIfError); + _printf(" t min rise rate : "); _println(settings.tMinRRate); + _printf(" t min fall rate : "); _println(settings.tMinFRate); + _printf(" p min r/f rate : "); _println(settings.pMinRate); + if (settings.acoustic < 2) { + faultSettings.acoustic = settings.acoustic; + } else { + faultSettings.acoustic = _STD_FAULTS_ACOUSTIC; + } + setBitInRegisters(4, faultSettings.acoustic); + if (settings.optical < 2) { + faultSettings.optical = settings.optical; + } else { + faultSettings.optical = _STD_FAULTS_OPTICAL; + } + setBitInRegisters(5, faultSettings.optical); + if (settings.tOnIfError < 2) { + faultSettings.tOnIfError = settings.tOnIfError; + } else { + faultSettings.tOnIfError = _STD_FAULTS_T_ON; + } + setBitInRegisters(6, faultSettings.tOnIfError); + if (settings.pOnIfError < 2) { + faultSettings.pOnIfError = settings.pOnIfError; + } else { + faultSettings.pOnIfError = _STD_FAULTS_P_ON; + } + setBitInRegisters(7, faultSettings.pOnIfError); + if (settings.tMinRRate == 0x8000) { + faultSettings.tMinRRate = _STD_FAULTS_T_R_RATE; + } else if (settings.tMinRRate < _MIN_FAULTS_RATE) { + faultSettings.tMinRRate = _MIN_FAULTS_RATE; + } else if (settings.tMinRRate > _MAX_FAULTS_RATE) { + faultSettings.tMinRRate = _MAX_FAULTS_RATE; + } else { + faultSettings.tMinRRate = settings.tMinRRate; + } + modbusHolding[7] = faultSettings.tMinRRate; + if (settings.tMinFRate == 0x8000) { + faultSettings.tMinFRate = _STD_FAULTS_T_F_RATE; + } else if (settings.tMinFRate < _MIN_FAULTS_RATE) { + faultSettings.tMinFRate = _MIN_FAULTS_RATE; + } else if (settings.tMinFRate > _MAX_FAULTS_RATE) { + faultSettings.tMinFRate = _MAX_FAULTS_RATE; + } else { + faultSettings.tMinFRate = settings.tMinFRate; + } + modbusHolding[8] = faultSettings.tMinFRate; + if (settings.pMinRate == 0x8000) { + faultSettings.pMinRate = _STD_FAULTS_P_RATE; + } else if (settings.pMinRate < _MIN_FAULTS_RATE) { + faultSettings.pMinRate = _MIN_FAULTS_RATE; + } else if (settings.pMinRate > _MAX_FAULTS_RATE) { + faultSettings.pMinRate = _MAX_FAULTS_RATE; + } else { + faultSettings.pMinRate = settings.pMinRate; + } + modbusHolding[9] = faultSettings.pMinRate; + _printlnf("getFaultSettings() nach Validierung:"); + _printf(" akustisches Signal: "); _println(faultSettings.acoustic); + _printf(" optisches Signal : "); _println(faultSettings.optical); + _printf(" t on if error : "); _println(faultSettings.tOnIfError); + _printf(" p on if error : "); _println(faultSettings.pOnIfError); + _printf(" t min rise rate : "); _println(faultSettings.tMinRRate); + _printf(" t min fall rate : "); _println(faultSettings.tMinFRate); + _printf(" p min r/f rate : "); _println(faultSettings.pMinRate); +} + +void setFaultSettings(const FaultSettings &settings, bool writeModbusRegister) +{ + if (settings.acoustic < 2 && settings.acoustic != faultSettings.acoustic) { + _printf("faultSettings.acoustic alt: "); _print(faultSettings.acoustic); + _printf(", neu: "); _println(settings.acoustic); + faultSettings.acoustic = settings.acoustic; + EEPROM.put(_EEPROM_FAULTS_ACOUSTIC, settings.acoustic); + if (writeModbusRegister) setBitInRegisters(4, settings.acoustic); + } + if (settings.optical < 2 && settings.optical != faultSettings.optical) { + _printf("faultSettings.optical alt: "); _print(faultSettings.optical); + _printf(", neu: "); _println(settings.optical); + faultSettings.optical = settings.optical; + EEPROM.put(_EEPROM_FAULTS_OPTICAL, settings.optical); + if (writeModbusRegister) setBitInRegisters(5, settings.optical); + } + if (settings.tOnIfError < 2 && settings.tOnIfError != faultSettings.tOnIfError) { + _printf("faultSettings.tOnIfError alt: "); _print(faultSettings.tOnIfError); + _printf(", neu: "); _println(settings.tOnIfError); + faultSettings.tOnIfError = settings.tOnIfError; + EEPROM.put(_EEPROM_FAULTS_T_ON, settings.tOnIfError); + if (writeModbusRegister) setBitInRegisters(6, settings.tOnIfError); + } + if (settings.pOnIfError < 2 && settings.pOnIfError != faultSettings.pOnIfError) { + _printf("faultSettings.pOnIfError alt: "); _print(faultSettings.pOnIfError); + _printf(", neu: "); _println(settings.pOnIfError); + faultSettings.pOnIfError = settings.pOnIfError; + EEPROM.put(_EEPROM_FAULTS_P_ON, settings.pOnIfError); + if (writeModbusRegister) setBitInRegisters(7, settings.pOnIfError); + } + if (settings.tMinRRate != 0x8000 && settings.tMinRRate != faultSettings.tMinRRate) { + if (settings.tMinRRate < _MIN_FAULTS_RATE) { + faultSettings.tMinRRate == _MIN_FAULTS_RATE; + EEPROM.put(_EEPROM_FAULTS_T_R_RATE, _MIN_FAULTS_RATE); + } else if (settings.tMinRRate > _MAX_FAULTS_RATE) { + faultSettings.tMinRRate == _MAX_FAULTS_RATE; + EEPROM.put(_EEPROM_FAULTS_T_R_RATE, _MAX_FAULTS_RATE); + } else { + faultSettings.tMinRRate == settings.tMinRRate; + EEPROM.put(_EEPROM_FAULTS_T_R_RATE, settings.tMinRRate); + } + if (writeModbusRegister) modbusHolding[7] = settings.tMinRRate; + _printf("faultSettings.tMinRRate alt: "); _print(faultSettings.tMinRRate); + _printf(", neu: "); _println(settings.tMinRRate); + } + if (settings.tMinFRate != 0x8000 && settings.tMinFRate != faultSettings.tMinFRate) { + if (settings.tMinFRate < _MIN_FAULTS_RATE) { + faultSettings.tMinFRate == _MIN_FAULTS_RATE; + EEPROM.put(_EEPROM_FAULTS_T_F_RATE, _MIN_FAULTS_RATE); + } else if (settings.tMinFRate > _MAX_FAULTS_RATE) { + faultSettings.tMinFRate == _MAX_FAULTS_RATE; + EEPROM.put(_EEPROM_FAULTS_T_F_RATE, _MAX_FAULTS_RATE); + } else { + faultSettings.tMinFRate == settings.tMinFRate; + EEPROM.put(_EEPROM_FAULTS_T_F_RATE, settings.tMinFRate); + } + if (writeModbusRegister) modbusHolding[8] = settings.tMinFRate; + _printf("faultSettings.tMinFRate alt: "); _print(faultSettings.tMinFRate); + _printf(", neu: "); _println(settings.tMinFRate); + } + if (settings.pMinRate != 0x8000 && settings.pMinRate != faultSettings.pMinRate) { + if (settings.pMinRate < _MIN_FAULTS_RATE) { + faultSettings.pMinRate == _MIN_FAULTS_RATE; + EEPROM.put(_EEPROM_FAULTS_P_RATE, _MIN_FAULTS_RATE); + } else if (settings.pMinRate > _MAX_FAULTS_RATE) { + faultSettings.pMinRate == _MAX_FAULTS_RATE; + EEPROM.put(_EEPROM_FAULTS_P_RATE, _MAX_FAULTS_RATE); + } else { + faultSettings.pMinRate == settings.pMinRate; + EEPROM.put(_EEPROM_FAULTS_P_RATE, settings.pMinRate); + } + if (writeModbusRegister) modbusHolding[9] = settings.pMinRate; + _printf("faultSettings.pMinRate alt: "); _print(faultSettings.pMinRate); + _printf(", neu: "); _println(settings.pMinRate); + } +} + +bool readPwrBtn() +{ + const uint8_t debounceDelay = 20; + static unsigned long lastDebounceTime; + static bool lastBounceState; + static bool steadyState; + bool currentState = !digitalRead(BTN_PWR); + if (currentState != lastBounceState) { + lastDebounceTime = currMillis; + lastBounceState = currentState; + } + if ((currMillis - lastDebounceTime) > debounceDelay) + steadyState = currentState; + if (steadyState && (currMillis - lastDebounceTime) > 3000) + display.reset(); + return steadyState; +} diff --git a/src/common.h b/src/common.h index e2ac9dc..6239d4d 100644 --- a/src/common.h +++ b/src/common.h @@ -1,158 +1,38 @@ #ifndef _MY_STUFF_H_INCLUDED #define _MY_STUFF_H_INCLUDED -#include "./DallasTemperature/DallasTemperature.h" +#include "DallasTemperature/DallasTemperature.h" +#include "OneWire/OneWire.h" +#include "display/display.h" +#include "controller/controller.h" +#include "faults/faults.h" +#include "types.h" +#include "macros.h" -#define _VERSION_MAJOR 0 -#define _VERSION_MINOR 0 -#define _VERSION_MICRO 1 -#define _VERSION_NUMBER _VERSION_MICRO | (_VERSION_MINOR << 6) | (_VERSION_MAJOR << 12) -#define _VERSION_STRING "v0.0.1" +extern const uint8_t BTN_PWR; -#define _DEBUG 1 -#define _DEBUG_SENSORS 0 -#define _DEBUG_DISPLAY 0 -#define _MODBUS 1 -// Das Ermitteln einer Durchschnittstemperatur braucht (zu) viel RAM und ist wahrscheinlich unnötig -#define _MODBUS_AVERAGE_TEMPERATURES 0 - -#if _DEBUG == 1 -#define _print(x) Serial.print(x) -#define _println(x) Serial.println(x) -#if _DEBUG_DISPLAY == 1 -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) -#else -#define _printtime(text) -#endif // _DEBUG_DISPLAY -#if _DEBUG_SENSORS == 1 -#define _prints(x) Serial.print(x) -#define _printsln(x) Serial.println(x) -#else -#define _prints(x) -#define _printsln(x) -#endif -#else -#define _print(x) -#define _println(x) -#define _prints(x) -#define _printsln(x) -#define _printtime() -#endif - -#define _REGS_INFRONTOF_EVENTS 6 -#define _MODBUS_MAX_EVENTS 234 // Ergibt sich aus 0xF0 - 0x06 +extern void getParams(); +extern void setParams(Parameters&, bool writeModbusRegister); +extern void getModbusParams(); +extern void setModbusParams(const ModbusParameters&); +extern void getPSensor(); +extern void setPSensor(const PSensor&); +extern void getFaultSettings(); +extern void setFaultSettings(const FaultSettings&, bool writeModbusRegister); +extern bool readPwrBtn(); #if _MODBUS == 1 -#include "modbus/ModbusRTUSlave.h" -extern bool timeStampOverflow; -extern unsigned long timeStamp; -extern u8 &valves; -extern u16 &eventCounter; extern u16 modbusData[]; -#define _setModbusValve(index, val)\ - if (bitRead(valves, index) != val) {\ - bitWrite(valves, index, val);\ - bitWrite(modbusData[1], index+8, val);\ - if (eventCounter < _MODBUS_MAX_EVENTS) {\ - /* Die ersten 12 Bits beinhalten den relativen Zeitstempel in Zehntelsekunden */\ - /* Dieser Stempel ist immer 0xFFF, falls schon zuviel Zeit seit der letzten Referenzierung vergangen ist */\ - u16 result = (timeStampOverflow) ? 0xFFF : ((millis() - timeStamp) / 100) & 0xFFF;\ - /* Die Bits 12...14 beinhalten die Nr des geschalteten Ausgangs */\ - result |= index << 12;\ - /* Das MSB gibt an, ob der Ausgang ein- oder ausgeschaltet wurde */\ - result |= val << 15;\ - modbusData[_REGS_INFRONTOF_EVENTS + eventCounter] = result;\ - eventCounter++;\ - }\ - } -//#define _setModbusValue(index, val) modbusData[index] = val -#else -#define _setModbusValve(index, val) -//#define _setModbusValue(index, val) -#endif // _MODBUS == +extern u16 modbusDelAlarms[]; -#define _MODBUS_ADDR_MIN 1 -#define _MODBUS_ADDR_MAX 247 // Laut QModMaster letzte Adresse -#define _MODBUS_DELAY_MIN 0 // 1/10 Millisekunden -#define _MODBUS_DELAY_MAX 250 // 1/10 Millisekunden -#define _MODBUS_INVALID_BAUDRATE 4294967295 +extern u16 &eventCounter; +extern u16 &refTime; -#define _MODBUS_VALVE_T1_INDEX 0 -#define _MODBUS_VALVE_T2_INDEX 1 -#define _MODBUS_VALVE_PR_INDEX 2 -#define _MODBUS_VALVE_PF_INDEX 3 -#define _MODBUS_T1_INDEX 2 -#define _MODBUS_T2_INDEX 3 -#define _MODBUS_P_INDEX 4 - -#define _SENSOR_FAULT 0xFFFF - -#define _STD_TEMP_SETPOINT 400 // Hundertstel -#define _STD_TEMP_HYSTERESIS 10 // Hundertstel -#define _STD_P_SETPOINT 100 // Hundertstel -#define _STD_P_HYSTERESIS 5 // Hundertstel -#define _STD_P_EN_INC 1 -#define _STD_P_EN_DEC 1 -#define _STD_T_EN 1 -#define _STD_C_EN 0 -#define _MIN_TEMP_SETPOINT 10 // Hundertstel -#define _MAX_TEMP_SETPOINT 3000 // Hundertstel -#define _MIN_TEMP_HYSTERESIS 1 // Hundertstel -#define _MAX_TEMP_HYSTERESIS 100 // Hundertstel -#define _MIN_P_SETPOINT 0 // Hundertstel -#define _MAX_P_SETPOINT 300 // Hundertstel -#define _MIN_P_HYSTERESIS 1 // Hundertstel -#define _MAX_P_HYSTERESIS 50 // Hundertstel - -#define _P_SENSOR_1_5V_0_5BAR 1 -#define _P_SENSOR_0_5V_0_6BAR 2 - -enum PSensor : uint8_t { - SMC_1_5V_0_5BAR=1, // Sensor mit Kabel - GEMS_0_5V_0_6BAR // Sensor mit Würfelstecker -}; - -struct parameters { // Prozesswerte - int16_t ts1 = _SENSOR_FAULT; // Soll-Temperatur 1 in 1/100 °C - int16_t th1 = _SENSOR_FAULT; // Hysterese für Temperatur 1 (1/100) - int16_t ts2 = _SENSOR_FAULT; // Soll-Temperatur 2 in 1/100 °C - int16_t th2 = _SENSOR_FAULT; // Hysterese für Temperatur 2 (1/100) - int16_t ps = _SENSOR_FAULT; // Druck in bar - int16_t ph = _SENSOR_FAULT; // Hysterese für Druck - uint8_t tEn = 255; // Kühlung (de)aktiviert - uint8_t pInc = 255; // Drucksteigerung (de)aktiviert - uint8_t pDec = 255; // Druckabfall (de)aktiviert - uint8_t cEn = 255; // Controller (de)aktiviert -}; - -struct values { // aktuelle Messwerte - int16_t t1 = _SENSOR_FAULT; // Temperatur in 1/100 °C - int16_t p = _SENSOR_FAULT; // Druck in 1/100 bar -}; - -struct modbusParameters { // Parameter für Modbus - uint32_t baudrate = _MODBUS_INVALID_BAUDRATE; // Modbus-Baudrate - uint8_t address = 255; // Modbus-Adresse - uint8_t delay = 255; // delay in 1/10 ms vor der Antwort -}; - -struct valveStates { - bool t1; - bool t2; - bool pInc; - bool pDec; -}; +extern void checkModbusParams(); +extern void setupModbus(const u16&); +extern void beginModbus(); +extern void modbusCallbackCoil(u16 reg, bool val); +extern u16 modbusCallbackRegister(u16 reg, u16 val); +#endif // _MODBUS #endif // _MY_STUFF_H_INCLUDED diff --git a/src/controller/controller.cpp b/src/controller/controller.cpp index 326c17e..42ddc5d 100644 --- a/src/controller/controller.cpp +++ b/src/controller/controller.cpp @@ -1,23 +1,16 @@ #include "controller.h" +#include "../faults/faults.h" + +extern u32 currMillis; Controller::Controller(const uint8_t pAnalaogPin, const uint8_t oneWirePin, - parameters *p, - values *v, - Display *d, - PSensor *s, - valveStates *vStates, const uint8_t t1Pin, const uint8_t t2Pin, const uint8_t pRisePin, const uint8_t pFallPin) : _aPin(pAnalaogPin) , _oneWire(oneWirePin) - , _params(p) - , _vals(v) - , _display(d) - , _pSensor(s) - , _vStates(vStates) , _prPin(pRisePin) , _pfPin(pFallPin) , _t1Pin(t1Pin) @@ -32,9 +25,9 @@ Controller::Controller(const uint8_t pAnalaogPin, digitalWrite(_pfPin, 0); digitalWrite(_t1Pin, 0); digitalWrite(_t2Pin, 0); - _vStates->pInc = false; - _vStates->pDec = false; - _vStates->t1 = false; + vStates.pInc = false; + vStates.pDec = false; + vStates.t1 = false; } void Controller::init(bool startup=false) @@ -44,60 +37,69 @@ void Controller::init(bool startup=false) const uint8_t maxDevices = 1; uint8_t deviceCount = _dallas.getDS18Count(); if (deviceCount > maxDevices) { - _display->tooMuchOneWireDevicesDetected(deviceCount, maxDevices); + display.tooMuchOneWireDevicesDetected(deviceCount, maxDevices); } else if (!deviceCount) { - _display->noOneWireDevicesDetected(); + display.noOneWireDevicesDetected(); } else { _initOk = true; _dallas.getAddress(_tempAddr1, 0); _dallas.setResolution(12); if (!startup) - _display->oneWireDevicesDetected(deviceCount); + display.oneWireDevicesDetected(deviceCount); } } void Controller::process() { - if (_initOk && millis() - _lastConversion > _TEMP_CONVERSION_DELAY) { - _lastConversion = millis(); + if (_initOk && currMillis - _lastConversion > _TEMP_CONVERSION_DELAY) { + _lastConversion = currMillis; _requestConversion = true; float temp = _dallas.getTempC(_tempAddr1); if (temp == DEVICE_DISCONNECTED_C) - _vals->t1 = _SENSOR_FAULT; + vals.t1 = _SENSOR_FAULT; else - _vals->t1 = temp * 100; - _prints("Temp: "); _prints(_vals->t1); - if (_vals->t1 != _SENSOR_FAULT && _params->tEn && _params->cEn) { + vals.t1 = temp * 100; + _printsf("Temp: "); _prints(vals.t1); + if (vals.t1 != _SENSOR_FAULT && params.tEn && params.cEn) { switch (_tState) { case stateIdle: - _printsln(", case stateIdle"); - if (_vals->t1 < _params->ts1 - _params->th1) { + faults.t1Idle(vals.t1); + _printslnf(", case stateIdle"); + if (vals.t1 < params.ts1 - params.th1) { _setT1Valve(0, stateRising); - _prints(" -> rising"); - } else if (_vals->t1 > _params->ts1 + _params->th1) { + _printsf(" -> rising"); + } else if (vals.t1 > params.ts1 + params.th1) { _setT1Valve(1, stateFalling); - _prints(" -> falling"); + _printsf(" -> falling"); } _printsln(); break; case stateRising: - _prints(", case stateRising"); - if (_vals->t1 > _params->ts1 + _params->th1) { + faults.check(t1TooLo); + _printsf(", case stateRising"); + if (vals.t1 > params.ts1 + params.th1) { _setT1Valve(1, stateIdle); - _prints(" -> idle"); + _printsf(" -> idle"); } _printsln(); break; case stateFalling: - _prints(", case stateFalling"); - if (_vals->t1 < _params->ts1 - _params->th1) { + faults.check(t1TooHi); + _printsf(", case stateFalling"); + if (vals.t1 < params.ts1 - params.th1) { _setT1Valve(0, stateIdle); - _prints(" -> idle"); + _printsf(" -> idle"); + } else { // um ev. abschalten (Fehler) zu können + _setT1Valve(1, stateFalling); } _printsln(); break; } + } else if (vals.t1 == _SENSOR_FAULT && params.tEn && params.cEn) { + faults.check(t1Invalid); + _setT1Valve(0, stateIdle); } else { + faults.t1Idle(); _setT1Valve(0, stateIdle); _printsln(); } @@ -105,51 +107,51 @@ void Controller::process() #if _MODBUS_AVERAGE_TEMPERATURES == 1 _averageTemperature1(); #else - modbusData[_MODBUS_T1_INDEX] = _vals->t1; + modbusData[_MODBUS_T1_INDEX] = vals.t1; #endif #endif } else if (_requestConversion) { _requestConversion = false; _dallas.requestTemperatures(); } - if (millis() - _lastAnalogRead > _ANALOG_READ_DELAY) { - _lastAnalogRead = millis(); + if (currMillis - _lastAnalogRead > _ANALOG_READ_DELAY) { + _lastAnalogRead = currMillis; _rawP = analogRead(_aPin); - _prints("_rawP: "); _prints(_rawP); - switch (*_pSensor) { + _printsf("_rawP: "); _prints(_rawP); + switch (pSensor) { case SMC_1_5V_0_5BAR: float p = (float) _rawP / 2.046f; // 1023 / 5 * 100 (0-5 1/100 bar) p = (p - 1) * 1.25f; // 1...5 V Sensor - _prints(" (Gems), p: "); _prints(p); + _printsf(" (Gems), p: "); _prints(p); if (p < -0.5f) // Sensor ist definitiv nicht angeschlossen - _vals->p = _SENSOR_FAULT; + vals.p = _SENSOR_FAULT; else if (p < 0) // Bei kleiner Toleranz ein bisschen schwindeln ;-) - _vals->p = 0; + vals.p = 0; else - _vals->p = p; + vals.p = p; break; case GEMS_0_5V_0_6BAR: - _vals->p = (float) _rawP / 1.705f; // 1023 / 6 * 100 (0-5 1/100 bar) - _prints(" (Gems), p: "); _prints(_vals->p); + vals.p = (float) _rawP / 1.705f; // 1023 / 6 * 100 (0-5 1/100 bar) + _printsf(" (Gems), p: "); _prints(vals.p); } -#if _MODBUS == 1 _averagePressure(); -#endif - if (_vals->p != _SENSOR_FAULT && _params->cEn) { + if (vals.p != _SENSOR_FAULT && params.cEn && (params.pInc || params.pDec)) { switch (_pState) { case stateIdle: - _prints(", case stateIdle"); - if (_vals->p < _params->ps - _params->ph) { - if (_params->pInc) { - _printsln(" -> stateRising"); + faults.pIdle(_buildAveragePForFaultDetection()); + _p_i_fault_start = _p_i_curr; + _printsf(", case stateIdle"); + if (vals.p < params.ps - params.ph) { + if (params.pInc) { + _printslnf(" -> stateRising"); _setPValves(stateRising); } else { _printsln(); _setPValves(stateIdle); } - } else if (_vals->p > _params->ps + _params->ph) { - if (_params->pDec) { - _printsln(" -> stateFalling"); + } else if (vals.p > params.ps + params.ph) { + if (params.pDec) { + _printslnf(" -> stateFalling"); _setPValves(stateFalling); } else { _printsln(); @@ -158,65 +160,85 @@ void Controller::process() } else _printsln(); break; case stateRising: - _prints(", case stateRising"); - if ((_params->pInc && _vals->p > _params->ps) || !_params->pInc) { - _prints(" -> stateIdle"); + if (params.pInc) + faults.check(pTooLo, _buildAveragePForFaultDetection()); + _printsf(", case stateRising"); + if ((params.pInc && vals.p > params.ps) || !params.pInc) { + _printsf(" -> stateIdle"); _setPValves(stateIdle); + } else { // um ev. abschalten (Fehler) zu können + _setPValves(stateRising); } _printsln(); break; case stateFalling: - _prints(", case stateFalling"); - if ((_params->pDec && _vals->p < _params->ps) || !_params->pDec) { - _prints(" -> stateIdle"); + if (params.pDec) + faults.check(pTooHi, _buildAveragePForFaultDetection()); + _printsf(", case stateFalling"); + if ((params.pDec && vals.p < params.ps) || !params.pDec) { + _printsf(" -> stateIdle"); _setPValves(stateIdle); + } else { // um ev. abschalten (Fehler) zu können + _setPValves(stateFalling); } _printsln(); break; } + } else if (vals.p == _SENSOR_FAULT && params.cEn && (params.pInc || params.pDec)) { + _printsln(); + faults.check(pInvalid, _buildAveragePForFaultDetection()); + _setPValves(stateIdle); } else { _printsln(); + faults.pIdle(); _setPValves(stateIdle); + // faults.checkP(_buildAveragePForFaultDetection()); + // _p_i_fault_start = _p_i_curr; } - _prints("_vStates->pInc: "); _prints(_vStates->pInc); - _prints(", _vStates->pDec: "); _printsln(_vStates->pDec); + _printsf("vStates.pInc: "); _prints(vStates.pInc); + _printsf(", vStates.pDec: "); _printsln(vStates.pDec); } + faults.process(); } void Controller::_setT1Valve(uint8_t val, States state) { _tState = state; + if (faults.tActive() && !faultSettings.tOnIfError) + val = 0; // Ventile ausschalten digitalWrite(_t1Pin, val); - _vStates->t1 = val; + vStates.t1 = val; _setModbusValve(_MODBUS_VALVE_T1_INDEX, val); } void Controller::_setPValves(States state) { _pState = state; + if (faults.pActive() && !faultSettings.pOnIfError) + state = stateIdle; // Ventile ausschalten switch (state) { case stateRising: digitalWrite(_prPin, 1); - _vStates->pInc = true; + vStates.pInc = true; _setModbusValve(_MODBUS_VALVE_PR_INDEX, 1); digitalWrite(_pfPin, 0); - _vStates->pDec = false; + vStates.pDec = false; _setModbusValve(_MODBUS_VALVE_PF_INDEX, 0); break; case stateFalling: digitalWrite(_pfPin, 1); - _vStates->pDec = true; + vStates.pDec = true; _setModbusValve(_MODBUS_VALVE_PF_INDEX, 1); digitalWrite(_prPin, 0); - _vStates->pInc = false; + vStates.pInc = false; _setModbusValve(_MODBUS_VALVE_PR_INDEX, 0); break; default: // idle valves digitalWrite(_pfPin, 0); - _vStates->pDec = false; + vStates.pDec = false; _setModbusValve(_MODBUS_VALVE_PF_INDEX, 0); digitalWrite(_prPin, 0); - _vStates->pInc = false; + vStates.pInc = false; _setModbusValve(_MODBUS_VALVE_PR_INDEX, 0); } } @@ -224,19 +246,19 @@ void Controller::_setPValves(States state) #if _MODBUS == 1 void Controller::resetAverageCounters() { - _print("Setze Zähler zurück ("); + _printbf("Setze Zähler zurück ("); #if _MODBUS_AVERAGE_TEMPERATURES == 1 - _print("_t1_c: "); _print(_t1_c); - _print(", _t2_c: "); _print(_t2_c); - _print(", "); + _printbf("_t1_c: "); _printb(_t1_c); + _printbf(", _t2_c: "); _printb(_t2_c); + _printbf(", "); _t1_c = 0; _t2_c = 0; _t1_of = false; _t2_of = false; #endif - _print("_p_c: "); _print(_p_c); _println(")"); - _p_c = 0; - _p_of = false; + _printbf("_p_i_curr: "); _printb(_p_i_curr); _printblnf(")"); + _p_i_mb_start = _p_i_curr; + _p_mb_full = false; } #if _MODBUS_AVERAGE_TEMPERATURES == 1 @@ -251,7 +273,7 @@ inline void Controller::_averageTemperature1() _t1_of = true; } // Falls das Array schon voll ist, wird es wieder vom Anfang befüllt - _t1_arr[_t1_c++] = _vals->t1; + _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 _P_ARR_LEN) { + _p_i_curr = 0; + _p_mb_full = true; + } + _p_arr[_p_i_curr] = vals.p; +#if _MODBUS == 1 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++; + u16 valid = 0; + u16 invalid = 0; + u16 i; + if (_p_i_curr > _p_i_mb_start && !_p_mb_full) { + for (i=_p_i_mb_start; i<_p_i_curr; i++) { + if (_p_arr[i] != _SENSOR_FAULT) { + total += _p_arr[i]; + valid++; + } else { + invalid++; + } + } + } else if (_p_i_curr < _p_i_mb_start && !_p_mb_full) { + for (i=0; i<_p_i_curr; i++) { + if (_p_arr[i] != _SENSOR_FAULT) { + total += _p_arr[i]; + valid++; + } else { + invalid++; + } + } + for (i=_p_i_mb_start; i<_P_ARR_LEN; i++) { + if (_p_arr[i] != _SENSOR_FAULT) { + total += _p_arr[i]; + valid++; + } else { + invalid++; + } + } + } else { + for (i=0; i<_P_ARR_LEN; 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 + _p_i_curr++; +} + +inline int16_t Controller::_buildAveragePForFaultDetection() +{ + int32_t total = 0; + int16_t result = _SENSOR_FAULT; + u16 valid = 0; + u16 invalid = 0; + u16 i; + if (_p_i_curr > _p_i_fault_start) { + for (i=_p_i_fault_start; i<_p_i_curr; i++) { + if (_p_arr[i] != _SENSOR_FAULT) { + total += _p_arr[i]; + valid++; + } else { + invalid++; + } + } + } else if (_p_i_curr < _p_i_fault_start) { + for (i=0; i<_p_i_curr; i++) { + if (_p_arr[i] != _SENSOR_FAULT) { + total += _p_arr[i]; + valid++; + } else { + invalid++; + } + } + for (i=_p_i_fault_start; i<_P_ARR_LEN; i++) { + if (_p_arr[i] != _SENSOR_FAULT) { + total += _p_arr[i]; + valid++; + } else { + invalid++; + } + } + } else { + for (i=0; i<_P_ARR_LEN; i++) { + if (_p_arr[i] != _SENSOR_FAULT) { + total += _p_arr[i]; + valid++; + } else { + invalid++; + } + } + } + if (valid >= invalid) { + result = total / valid; + } + // _p_i_fault_start = _p_i_curr; + // _printf("Durchschnittsdruck: "); _println(result); + return result; +} diff --git a/src/controller/controller.h b/src/controller/controller.h index a19db70..07a9249 100644 --- a/src/controller/controller.h +++ b/src/controller/controller.h @@ -4,26 +4,26 @@ #include "../OneWire/OneWire.h" #include "../DallasTemperature/DallasTemperature.h" #include "../display/display.h" -#include "../common.h" +#include "../faults/faults.h" +#include "../macros.h" +#include "../types.h" #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 _P_ARR_LEN (409600 / _ANALOG_READ_DELAY) + 1 -#if _MODBUS_AVERAGE_TEMPERATURES == 1 -#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) +#if _MODBUS_AVERAGE_TEMPERATURES == 1 +// 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 #endif +extern u16 modbusData[]; +extern Display display; + class Controller { private: const uint8_t _aPin; - parameters *_params; - values *_vals; - Display *_display; - PSensor *_pSensor; - valveStates *_vStates; uint8_t _prPin; uint8_t _pfPin; uint8_t _t1Pin; @@ -38,8 +38,10 @@ private: bool _t2_of = false; // Flag, die anzeigt, ob das _t2_arr-Array schon voll ist #endif 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 + u16 _p_i_curr = 0; // Aktueller Index des _p_arr-Arrays + u16 _p_i_mb_start = 0; + u16 _p_i_fault_start = 0; + bool _p_mb_full = false; // ob das _p_arr-Array für Modbus komplett gelesen werden soll OneWire _oneWire; DallasTemperature _dallas; @@ -53,31 +55,33 @@ private: bool _requestConversion; unsigned long _lastConversion; unsigned long _lastAnalogRead; - uint16_t _rawP; + u16 _rawP; + + // u32 _lastT1Idle; + // u32 _lastT2Idle; + // u32 _lastPIdle; + // u32 _lastFaultCheck; + // u32 _lastT2checked; + // u32 _lastPchecked; void _setT1Valve(uint8_t, States); void _setPValves(States); -#if _MODBUS == 1 +#if _MODBUS_AVERAGE_TEMPERATURES == 1 void _averageTemperature1(); void _averageTemperature2(); - void _averagePressure(); -#endif +#endif // _MODBUS_AVERAGE_TEMPERATURES == 1 + inline void _averagePressure(); + inline int16_t _buildAveragePForFaultDetection(); public: Controller(const uint8_t pAnalaogPin, const uint8_t oneWirePin, - parameters *p, - values *vals, - Display *d, - PSensor *s, - valveStates *vStates, const uint8_t pRisePin, const uint8_t t1Pin, const uint8_t t2Pin, const uint8_t pFallPin ); - void init(bool startup=false); void process(); #if _MODBUS == 1 diff --git a/src/display/display.cpp b/src/display/display.cpp index ca26a6c..0209245 100644 --- a/src/display/display.cpp +++ b/src/display/display.cpp @@ -1,6 +1,13 @@ #include -#include "../DallasTemperature/DallasTemperature.h" #include "display.h" +#include "../DallasTemperature/DallasTemperature.h" +#include "../mystrings/mystrings.h" + +extern u32 currMillis; +extern void setPSensor(const PSensor&); +extern void setModbusParams(const ModbusParameters&); +extern void setParams(Parameters&, bool writeModbusRegister); +extern void setFaultSettings(const FaultSettings&, bool writeModbusRegister); Display::Display( uint8_t cs, @@ -8,30 +15,56 @@ Display::Display( uint8_t btnPrev, uint8_t btnSelect, uint8_t btnCancel, - uint8_t bgLED, - parameters *params, - values *vals, - modbusParameters *modbus, - PSensor *sensor, - valveStates *vStates) + uint8_t bgLED) : U8G2_ST7920_128X64_F_HW_SPI(U8G2_R0, cs) , btnNext(btnNext) , btnPrev(btnPrev) , btnSelect(btnSelect) , btnCancel(btnCancel) , bgLed(bgLED) - , _params(params) - , _vals(vals) - , _modbusParams(modbus) - , _pSensor(sensor) - , _vStates(vStates) { // Bei 2 MHz funtionierte im Test das Display schon nicht mehr // setBusClock(1000000); + _lastCEn = params.cEn; } void Display::process() { + if (_lastCEn != params.cEn || _lastFaultState != faults.state() || + _lastOpticalState != faultSettings.optical) { + _lastCEn = params.cEn; + _lastFaultState = faults.state(); + _lastOpticalState = faultSettings.optical; + if (_lastCEn && _lastFaultState == activeFault && _lastOpticalState) { + _currBrightness = 255; + _lastBrightnessChange = currMillis; + bgLight(true); + } else { + _currBrightness = 0; + if (_lastCEn) { + bgLight(true); + } else { + bgLight(100); + } + } + } + if (_currBrightness) { + if (_currBrightness == 255) + _incBrightness = false; + else if (_currBrightness == 100) + _incBrightness = true; + if (currMillis - _lastBrightnessChange > 2) { + u8 diff = (currMillis - _lastBrightnessChange) / 2; + if (_incBrightness) { + _currBrightness = (_currBrightness + diff > 255) ? 255 : _currBrightness + diff; + bgLight(_currBrightness); + } else { + _currBrightness = (_currBrightness - diff < 100) ? 100 : _currBrightness - diff; + bgLight(_currBrightness); + } + _lastBrightnessChange = currMillis; + } + } switch (_event()) { case _BACK_TO_HOME_SCREEN: _selection = 0; @@ -47,178 +80,331 @@ void Display::process() break; case U8X8_MSG_GPIO_MENU_SELECT: if (_selection) { - parameters params; - modbusParameters modbus; + Parameters paramsLocal; + ModbusParameters mbParamsLocal; + FaultSettings faultSettingsLocal; switch (_selection) { case _MENU_MAIN: + _printmlnf("case _MENU_MAIN"); _saveCursorPosForLevel(MenuLevel_1); switch (_prevCursor) { case _POS_TEMPERATURE_MENU: + _printmlnf("main menu: temperature"); _select(_MENU_TEMPERATURE); break; case _POS_PRESSURE_MENU: + _printmlnf("main menu: pressure"); _select(_MENU_PRESSURE); break; + case _POS_FAULTS_MAIN: + _printmlnf("main menu: faults"); + _select(_MENU_FAULTS_MAIN); + break; case _POS_SYSTEM: + _printmlnf("main menu: system"); _restrictInputValue(0, 255); _select(_VERIFY_ADMIN); break; case _POS_RESET: + _printmlnf("main menu: reset"); _select(_SELECTION_RESET); break; } break; case _MENU_TEMPERATURE: + _printmlnf("case _MENU_TEMPERATURE"); _saveCursorPosForLevel(MenuLevel_2); switch (_prevCursor) { case _POS_TEMPERATURE_ENABLED: - _cursor = _params->tEn; + _cursor = params.tEn; + _printmf("menu temperature: control enabled: "); + _printmln(_cursor); _select(_SELECTION_TEMPERATURE_ENABLED); break; case _POS_TEMPERATURE_SETPOINT: - _val = _params->ts1; + _printmlnf("menu temperature: setpoint"); + _val = params.ts1; _restrictInputValue(_MIN_TEMP_SETPOINT, _MAX_TEMP_SETPOINT, Precision_2); _select(_SELECTION_TEMPERATURE_SETPOINT); break; case _POS_TEMPERATURE_HYSTERESIS: - _val = _params->th1; + _printmlnf("menu temperature: hysteresis"); + _val = params.th1; _restrictInputValue(_MIN_TEMP_HYSTERESIS, _MAX_TEMP_HYSTERESIS, Precision_2); _select(_SELECTION_TEMPERATURE_HYSTERESIS); break; } break; + case _SELECTION_TEMPERATURE_ENABLED: + _printmlnf("case _SELECTION_TEMPERATURE_ENABLED"); + paramsLocal.tEn = _cursor; + setParams(paramsLocal, true); + _select(_MENU_TEMPERATURE, true); + break; + case _SELECTION_TEMPERATURE_SETPOINT: + _printmlnf("case _SELECTION_TEMPERATURE_SETPOINT"); + paramsLocal.ts1 = _val; + setParams(paramsLocal, true); + _select(_MENU_TEMPERATURE, true); + break; + case _SELECTION_TEMPERATURE_HYSTERESIS: + _printmlnf("case _SELECTION_TEMPERATURE_HYSTERESIS"); + paramsLocal.th1 = _val; + setParams(paramsLocal, true); + _select(_MENU_TEMPERATURE, true); + break; case _MENU_PRESSURE: + _printmlnf("case _MENU_PRESSURE"); _saveCursorPosForLevel(MenuLevel_2); switch (_prevCursor) { case _POS_PRESSURE_ENABLE_INCREASE: - _cursor = _params->pInc; + _printmlnf("menu pressure: enable increase"); + _cursor = params.pInc; _select(_SELECTION_PRESSURE_ENABLE_INCREASE); break; case _POS_PRESSURE_ENABLE_DECREASE: - _cursor = _params->pDec; + _printmlnf("menu pressure: enable decrease"); + _cursor = params.pDec; _select(_SELECTION_PRESSURE_ENABLE_DECREASE); break; case _POS_PRESSURE_SETPOINT: - _val = _params->ps; + _printmlnf("menu pressure: setpoint"); + _val = params.ps; _restrictInputValue(_MIN_P_SETPOINT, _MAX_P_SETPOINT, Precision_2); _select(_SELECTION_PRESSURE_SETPOINT); break; case _POS_PRESSURE_HYSTERESIS: - _val = _params->ph; + _printmlnf("menu pressure: hysteresis"); + _val = params.ph; _restrictInputValue(_MIN_P_HYSTERESIS, _MAX_P_HYSTERESIS, Precision_2); _select(_SELECTION_PRESSURE_HYSTERESIS); break; } break; + case _SELECTION_PRESSURE_ENABLE_INCREASE: + _printmlnf("case _SELECTION_PRESSURE_ENABLE_INCREASE"); + paramsLocal.pInc = _cursor; + setParams(paramsLocal, true); + _select(_MENU_PRESSURE, true); + break; + case _SELECTION_PRESSURE_ENABLE_DECREASE: + _printmlnf("case _SELECTION_PRESSURE_ENABLE_DECREASE"); + paramsLocal.pDec = _cursor; + setParams(paramsLocal, true); + _select(_MENU_PRESSURE, true); + break; + case _SELECTION_PRESSURE_SETPOINT: + _printmlnf("case _SELECTION_PRESSURE_SETPOINT"); + paramsLocal.ps = _val; + setParams(paramsLocal, true); + _select(_MENU_PRESSURE, true); + break; + case _SELECTION_PRESSURE_HYSTERESIS: + _printmlnf("case _SELECTION_PRESSURE_HYSTERESIS"); + paramsLocal.ph = _val; + setParams(paramsLocal, true); + _select(_MENU_PRESSURE, true); + break; + case _MENU_FAULTS_MAIN: + _printmlnf("case _MENU_FAULTS_MAIN"); + _saveCursorPosForLevel(MenuLevel_2); + switch (_prevCursor) { + case _POS_FAULTS_OPEN: + _printmlnf("menu faults: open faults"); + _select(_MENU_FAULTS_OPEN); + break; + case _POS_FAULTS_CLEAR: + _printmlnf("menu faults: clear faults"); + _select(_SELECTION_CLEAR_FAULTS); + break; + case _POS_FAULTS_SETTINGS: + _printmlnf("menu faults: settings"); + _select(_MENU_FAULTS_SETTINGS); + break; + } + break; + case _MENU_FAULTS_OPEN: + _saveCursorPosForLevel(MenuLevel_3); + if (faults.count()) { + _select(_FAULT_INFO); + } else { + _select(_MENU_FAULTS_MAIN); + } + break; + case _FAULT_INFO: + if (_cursor && !faults.at(_prevCursor)->active) { + faults.removeOne(_prevCursor); + } + _select(_MENU_FAULTS_OPEN, true); + break; + case _SELECTION_CLEAR_FAULTS: + _printmlnf("case _SELECTION_CLEAR_FAULTS"); + if (_cursor) { + faults.removeAll(); + } + _select(_MENU_FAULTS_MAIN, true); + break; + case _MENU_FAULTS_SETTINGS: + _printmlnf("case _MENU_FAULTS_SETTINGS"); + _saveCursorPosForLevel(MenuLevel_3); + switch (_prevCursor) { + case _POS_FAULTS_ACOUSTIC: + _printmlnf("menu faults settings: acoustic warning"); + _cursor = faultSettings.acoustic; + _select(_SELECTION_FAULTS_ACOUSTIC); + break; + case _POS_FAULTS_OPTICAL: + _printmlnf("menu faults settings: optical warning"); + _cursor = faultSettings.optical; + _select(_SELECTION_FAULTS_OPTICAL); + break; + case _POS_FAULTS_T_OFF: + _printmlnf("menu faults settings: t-off if error"); + _cursor = faultSettings.tOnIfError; + _select(_SELECTION_FAULTS_T_OFF); + break; + case _POS_FAULTS_P_OFF: + _printmlnf("menu faults settings: p-off if error"); + _cursor = faultSettings.pOnIfError; + _select(_SELECTION_FAULTS_P_OFF); + break; + case _POS_FAULTS_T_R_RATE: + _printmlnf("menu faults settings: min t rise rate"); + _val = faultSettings.tMinRRate; + _restrictInputValue(_MIN_FAULTS_RATE, _MAX_FAULTS_RATE, Precision_2); + _select(_SELECTION_FAULTS_T_R_RATE); + break; + case _POS_FAULTS_T_F_RATE: + _printmlnf("menu faults settings: min t fall rate"); + _val = faultSettings.tMinFRate; + _restrictInputValue(_MIN_FAULTS_RATE, _MAX_FAULTS_RATE, Precision_2); + _select(_SELECTION_FAULTS_T_F_RATE); + break; + case _POS_FAULTS_P_RATE: + _printmlnf("menu faults settings: min p rate"); + _val = faultSettings.pMinRate; + _restrictInputValue(_MIN_FAULTS_RATE, _MAX_FAULTS_RATE, Precision_2); + _select(_SELECTION_FAULTS_P_RATE); + break; + } + break; + case _SELECTION_FAULTS_ACOUSTIC: + faultSettingsLocal.acoustic = _cursor; + setFaultSettings(faultSettingsLocal, true); + _select(_MENU_FAULTS_SETTINGS, true); + break; + case _SELECTION_FAULTS_OPTICAL: + faultSettingsLocal.optical = _cursor; + setFaultSettings(faultSettingsLocal, true); + _select(_MENU_FAULTS_SETTINGS, true); + break; + case _SELECTION_FAULTS_T_OFF: + faultSettingsLocal.tOnIfError = _cursor; + setFaultSettings(faultSettingsLocal, true); + _select(_MENU_FAULTS_SETTINGS, true); + break; + case _SELECTION_FAULTS_P_OFF: + faultSettingsLocal.pOnIfError = _cursor; + setFaultSettings(faultSettingsLocal, true); + _select(_MENU_FAULTS_SETTINGS, true); + break; + case _SELECTION_FAULTS_T_R_RATE: + faultSettingsLocal.tMinRRate = _val; + setFaultSettings(faultSettingsLocal, true); + _select(_MENU_FAULTS_SETTINGS, true); + break; + case _SELECTION_FAULTS_T_F_RATE: + faultSettingsLocal.tMinFRate = _val; + setFaultSettings(faultSettingsLocal, true); + _select(_MENU_FAULTS_SETTINGS, true); + break; + case _SELECTION_FAULTS_P_RATE: + faultSettingsLocal.pMinRate = _val; + setFaultSettings(faultSettingsLocal, true); + _select(_MENU_FAULTS_SETTINGS, true); + break; + case _VERIFY_ADMIN: + _printmlnf("case _VERIFY_ADMIN"); + if (_val == _ADMIN_CODE) { + _select(_MENU_SYSTEM); + } else { + _select(_MENU_MAIN, true); + } + _val = 0; + break; case _MENU_SYSTEM: + _printmlnf("case _MENU_SYSTEM"); _saveCursorPosForLevel(MenuLevel_2); switch (_prevCursor) { case _POS_MODBUS_MENU: + _printmlnf("menu system: modbus"); _select(_MENU_SETTINGS_MODBUS); break; case _POS_P_SENSOR_SELECTION: - _cursor = (*_pSensor) - 1; + _printmlnf("menu system: pressure sensor"); + _cursor = pSensor - 1; _select(_SELECTION_P_SENSOR); break; } break; - case _MENU_SETTINGS_MODBUS: - _saveCursorPosForLevel(MenuLevel_3); - switch (_prevCursor) { - case _POS_MODBUS_ADDRESS: - _val = _modbusParams->address; - _restrictInputValue(_MODBUS_ADDR_MIN, _MODBUS_ADDR_MAX); - _select(_SELECTION_MODBUS_ADDRESS); + case _MENU_SETTINGS_MODBUS: + _printmlnf("case _MENU_SETTINGS_MODBUS"); + _saveCursorPosForLevel(MenuLevel_3); + switch (_prevCursor) { + case _POS_MODBUS_ADDRESS: + _printmlnf("menu modbus settings: address"); + _val = modbusParams.address; + _restrictInputValue(_MODBUS_ADDR_MIN, _MODBUS_ADDR_MAX); + _select(_SELECTION_MODBUS_ADDRESS); + break; + case _POS_MODBUS_BAUDRATE: + _printmlnf("menu modbus settings: baudrate"); + _baudrateCursorPos(); + _select(_SELECTION_MODBUS_BAUDRATE); + break; + case _POS_MODBUS_DELAY: + _printmlnf("menu modbus settings: delay"); + _val = modbusParams.delay; + _restrictInputValue(_MODBUS_DELAY_MIN, _MODBUS_DELAY_MAX, Precision_1); + _select(_SELECTION_MODBUS_DELAY); + break; + } break; - case _POS_MODBUS_BAUDRATE: - _baudrateCursorPos(); - _select(_SELECTION_MODBUS_BAUDRATE); + case _SELECTION_MODBUS_BAUDRATE: + _printmlnf("case _SELECTION_MODBUS_BAUDRATE"); + _setModbusBaudrate(); + _select(_MENU_SETTINGS_MODBUS, true); + break; + case _SELECTION_MODBUS_ADDRESS: + _printmlnf("case _SELECTION_MODBUS_ADDRESS"); + mbParamsLocal.address = _val; + setModbusParams(mbParamsLocal); + _select(_MENU_SETTINGS_MODBUS, true); + break; + case _SELECTION_MODBUS_DELAY: + _printmlnf("case _SELECTION_MODBUS_DELAY"); + mbParamsLocal.delay = _val; + setModbusParams(mbParamsLocal); + _select(_MENU_SETTINGS_MODBUS, true); + break; + case _SELECTION_P_SENSOR: + _printmlnf("case _SELECTION_P_SENSOR"); + switch (_cursor) { + case _POS_P_SMC_1_5V_0_5BAR: + setPSensor(SMC_1_5V_0_5BAR); + break; + case _POS_P_GEMS_0_5V_0_6BAR: + setPSensor(GEMS_0_5V_0_6BAR); + break; + } + _select(_MENU_SYSTEM, true); break; - case _POS_MODBUS_DELAY: - _val = _modbusParams->delay; - _restrictInputValue(_MODBUS_DELAY_MIN, _MODBUS_DELAY_MAX, Precision_1); - _select(_SELECTION_MODBUS_DELAY); - break; - } - break; - case _SELECTION_TEMPERATURE_ENABLED: - params.tEn = _cursor; - setParams(params); - _select(_MENU_TEMPERATURE, true); - break; - case _SELECTION_TEMPERATURE_SETPOINT: - // Serial.print("\n_val : "); Serial.println((float) _val); - // Serial.print("_val / 100: "); Serial.println(((float) _val) / 100); - params.ts1 = _val; - setParams(params); - _select(_MENU_TEMPERATURE, true); - break; - case _SELECTION_TEMPERATURE_HYSTERESIS: - params.th1 = _val; - setParams(params); - _select(_MENU_TEMPERATURE, true); - break; - case _SELECTION_PRESSURE_ENABLE_INCREASE: - params.pInc = _cursor; - setParams(params); - _select(_MENU_PRESSURE, true); - break; - case _SELECTION_PRESSURE_ENABLE_DECREASE: - params.pDec = _cursor; - setParams(params); - _select(_MENU_PRESSURE, true); - break; - case _SELECTION_PRESSURE_SETPOINT: - params.ps = _val; - setParams(params); - _select(_MENU_PRESSURE, true); - break; - case _SELECTION_PRESSURE_HYSTERESIS: - params.ph = _val; - setParams(params); - _select(_MENU_PRESSURE, true); - break; - case _SELECTION_MODBUS_BAUDRATE: - _setModbusBaudrate(); - _select(_MENU_SETTINGS_MODBUS, true); - break; - case _SELECTION_MODBUS_ADDRESS: - modbus.address = _val; - setModbusParams(modbus); - _select(_MENU_SETTINGS_MODBUS, true); - break; - case _SELECTION_MODBUS_DELAY: - modbus.delay = _val; - setModbusParams(modbus); - _select(_MENU_SETTINGS_MODBUS, true); - break; - case _SELECTION_P_SENSOR: - switch (_cursor) { - case _POS_P_SMC_1_5V_0_5BAR: - setPSensor(SMC_1_5V_0_5BAR); - break; - case _POS_P_GEMS_0_5V_0_6BAR: - setPSensor(GEMS_0_5V_0_6BAR); - break; - } - _restoreCursor(); - _select(_MENU_SYSTEM); - break; - case _VERIFY_ADMIN: - if (_val == _ADMIN_CODE) { - _select(_MENU_SYSTEM); - } else { - _restoreCursor(); - _select(_MENU_MAIN); - } - _val = 0; - break; case _SELECTION_RESET: + _printmlnf("case _SELECTION_RESET"); if (_cursor == 1) { reset(); } else { - _restoreCursor(); - _select(_MENU_MAIN); + _select(_MENU_MAIN, true); } break; } @@ -233,37 +419,63 @@ void Display::process() } switch (_selection) { case _MENU_MAIN: + _printmlnf("back to: home screen"); _selection = 0; _home(); return; case _SELECTION_TEMPERATURE_ENABLED: case _SELECTION_TEMPERATURE_SETPOINT: case _SELECTION_TEMPERATURE_HYSTERESIS: + _printmlnf("back to: menu temperature"); _select(_MENU_TEMPERATURE); break; case _SELECTION_PRESSURE_ENABLE_INCREASE: case _SELECTION_PRESSURE_ENABLE_DECREASE: case _SELECTION_PRESSURE_SETPOINT: case _SELECTION_PRESSURE_HYSTERESIS: + _printmlnf("back to: menu pressure"); _select(_MENU_PRESSURE); break; + case _MENU_FAULTS_OPEN: + case _SELECTION_CLEAR_FAULTS: + case _MENU_FAULTS_SETTINGS: + _printmlnf("back to: menu faults"); + _select(_MENU_FAULTS_MAIN); + break; + case _FAULT_INFO: + _printmlnf("back to: menu faults open"); + _select(_MENU_FAULTS_OPEN); + break; + case _SELECTION_FAULTS_ACOUSTIC: + case _SELECTION_FAULTS_OPTICAL: + case _SELECTION_FAULTS_T_OFF: + case _SELECTION_FAULTS_P_OFF: + case _SELECTION_FAULTS_T_R_RATE: + case _SELECTION_FAULTS_T_F_RATE: + case _SELECTION_FAULTS_P_RATE: + _printmlnf("back to: menu faults settings"); + _select(_MENU_FAULTS_SETTINGS); + break; case _MENU_SETTINGS_MODBUS: case _SELECTION_P_SENSOR: + _printmlnf("back to: menu system"); _select(_MENU_SYSTEM); break; case _SELECTION_MODBUS_ADDRESS: case _SELECTION_MODBUS_BAUDRATE: case _SELECTION_MODBUS_DELAY: + _printmlnf("back to: menu modbus"); _select(_MENU_SETTINGS_MODBUS); break; default: + _printmlnf("back to: main menu"); _select(_MENU_MAIN); } } break; } - if (millis() - _prevT > 250) { - _prevT = millis(); + if (currMillis - _prevT > 250) { + _prevT = currMillis; if (!_selection) { _home(); } @@ -280,174 +492,335 @@ void Display::_select(const uint8_t selection, const bool restoreCursor=false) void Display::_select() { + _printmf("select value: "); _printm(_selection); _printmf(" - "); + _fastStepsDelay = (_selection == _MENU_FAULTS_OPEN) ? 150 : _FAST_STEP_MS; setFont(_SEL_FONT); setFontRefHeightAll(); _inputValueActive = false; _stepSize = 1; + // String str_; _printtime("select screen"); switch (_selection) { case _MENU_MAIN: + _printmf("_MENU_MAIN"); _menu_depth = 0; - sprintf(_text, "Hauptmenü (%s)", _VERSION_STRING); - _selCnt = my_UserInterfaceSelectionList(&u8g2, _text, _cursor, "Temperatur\nDruck\nSystem\nReset"); + sprintf(_text, getMyStr501(STR_MENU_MAIN_TITLE), _VERSION_STRING); + _selCnt = my_UserInterfaceSelectionList(&u8g2, _text, _cursor, getMyStr200(STR_MENU_MAIN)); break; case _MENU_TEMPERATURE: + _printmf("_MENU_TEMPERATURE"); _menu_depth = 1; - _selCnt = my_UserInterfaceSelectionList(&u8g2, "Temperatur", _cursor, "Kühlung de/aktivieren\nSollwert\nHysterese"); + _selCnt = my_UserInterfaceSelectionList(&u8g2, getMyStr501(STR_TEMPERATURE), _cursor, getMyStr200(STR_MENU_TEMPERATURE)); break; case _SELECTION_TEMPERATURE_ENABLED: + _printmf("_SELECTION_TEMPERATURE_ENABLED"); _menu_depth = 2; - _selCnt = my_UserInterfaceMessage(&u8g2, "Kühlung", nullptr, "aktivieren", " nein \n ja ", _cursor); + _selCnt = my_UserInterfaceMessage(&u8g2, getMyStr501(STR_COOLING), nullptr, getMyStr502(STR_ACTIVATE), getMyStr503(STR_NO_YES), _cursor); break; case _SELECTION_TEMPERATURE_SETPOINT: + _printmf("_SELECTION_TEMPERATURE_SETPOINT"); _menu_depth = 2; - _inputValue(5, "Temperatur\nSollwert", "__float__"); + _inputValue(5, getMyStr200(STR_T_SETP), getMyStr501(STR__FLOAT__)); break; case _SELECTION_TEMPERATURE_HYSTERESIS: + _printmf("_SELECTION_TEMPERATURE_HYSTERESIS"); _menu_depth = 2; - _inputValue(5, "Temperatur\nHysterese", "__float__"); + _inputValue(5, getMyStr200(STR_T_HYST), getMyStr501(STR__FLOAT__)); break; case _MENU_PRESSURE: + _printmf("_MENU_PRESSURE"); _menu_depth = 1; - _selCnt = my_UserInterfaceSelectionList(&u8g2, "Druck", _cursor, "Drucksteigerung\nDruckabfall\nSollwert\nHysterese"); + _selCnt = my_UserInterfaceSelectionList(&u8g2, getMyStr501(STR_P), _cursor, getMyStr200(STR_MENU_PRESSURE)); break; case _SELECTION_PRESSURE_ENABLE_INCREASE: + _printmf("_SELECTION_PRESSURE_ENABLE_INCREASE"); _menu_depth = 2; - _selCnt = my_UserInterfaceMessage(&u8g2, "Drucksteigerung", nullptr, "aktivieren", " nein \n ja ", _cursor); + _selCnt = my_UserInterfaceMessage(&u8g2, getMyStr501(STR_P_INC), nullptr, getMyStr502(STR_ACTIVATE), getMyStr503(STR_NO_YES), _cursor); break; case _SELECTION_PRESSURE_ENABLE_DECREASE: + _printmf("_SELECTION_PRESSURE_ENABLE_DECREASE"); _menu_depth = 2; - _selCnt = my_UserInterfaceMessage(&u8g2, "Druckabfall", nullptr, "aktivieren", " nein \n ja ", _cursor); + _selCnt = my_UserInterfaceMessage(&u8g2, getMyStr501(STR_P_DEC), nullptr, getMyStr502(STR_ACTIVATE), getMyStr503(STR_NO_YES), _cursor); break; case _SELECTION_PRESSURE_SETPOINT: + _printmf("_SELECTION_PRESSURE_SETPOINT"); _menu_depth = 2; - _inputValue(5, "Druck\nSollwert", "__float__"); + _inputValue(5, getMyStr200(STR_P_SETP), getMyStr501(STR__FLOAT__)); break; case _SELECTION_PRESSURE_HYSTERESIS: + _printmf("_SELECTION_PRESSURE_HYSTERESIS"); _menu_depth = 2; - _inputValue(5, "Druck\nHysterese", "__float__"); + _inputValue(5, getMyStr200(STR_P_HYST), getMyStr501(STR__FLOAT__)); + break; + case _MENU_FAULTS_MAIN: + _printmf("_MENU_FAULTS_MAIN"); + _menu_depth = 1; + sprintf(_text, getMyStr501(STR_F_ERR_TITLE), faults.count()); + _selCnt = my_UserInterfaceSelectionList(&u8g2, _text, _cursor, getMyStr200(STR_MENU_ERR_HANDLING)); + break; + case _MENU_FAULTS_OPEN: + _printmf("_MENU_FAULTS_OPEN"); + _menu_depth = 2; + if (faults.count()) { + sprintf(_text, "%s (%u/%u)", getMyStr501(STR_CURR_ERRS), _cursor + 1, faults.count()); + _selCnt = my_UserInterfaceSelectionList(&u8g2, _text, _cursor, faults.menu()); + } else { + _selCnt = my_UserInterfaceMessage(&u8g2, faults.menu(), nullptr, nullptr, getMyStr501(STR_BACK), _cursor); + } + break; + case _FAULT_INFO: + _printmf("_FAULT_INFO"); + _menu_depth = 3; + strcpy(charBuffer502, faults.at(_prevCursor)->title()); + strcpy(charBuffer503, faults.at(_prevCursor)->activatedText()); + sprintf(charBuffer501, "%s\n%s", charBuffer502, charBuffer503); + strcpy(charBuffer502, faults.at(_prevCursor)->deactivatedText()); + sprintf(charBuffer503, "ID: %u", faults.at(_prevCursor)->id); + _selCnt = my_UserInterfaceMessage(&u8g2, charBuffer501, charBuffer502, charBuffer503, getMyStr200((faults.at(_prevCursor)->active) ? STR_BACK : STR_BACK_DEL), _cursor); + break; + case _MENU_FAULTS_SETTINGS: + _menu_depth = 2; + _selCnt = my_UserInterfaceSelectionList(&u8g2, getMyStr501(STR_MENU_FAULT_SETTINGS_TITLE), _cursor, getMyStr200(STR_MENU_FAULT_SETTINGS)); + break; + case _SELECTION_FAULTS_ACOUSTIC: + _menu_depth = 3; + _selCnt = my_UserInterfaceMessage(&u8g2, getMyStr200(STR_FAULTS_ACOUSTIC), nullptr, nullptr, getMyStr502(STR_NO_YES), _cursor); + break; + case _SELECTION_FAULTS_OPTICAL: + _menu_depth = 3; + _selCnt = my_UserInterfaceMessage(&u8g2, getMyStr200(STR_FAULTS_OPTICAL), nullptr, nullptr, getMyStr502(STR_NO_YES), _cursor); + break; + case _SELECTION_FAULTS_T_OFF: + _menu_depth = 3; + _selCnt = my_UserInterfaceMessage(&u8g2, getMyStr200(STR_FAULTS_T_OFF), nullptr, nullptr, getMyStr502(STR_NO_YES), _cursor); + break; + case _SELECTION_FAULTS_P_OFF: + _menu_depth = 3; + _selCnt = my_UserInterfaceMessage(&u8g2, getMyStr200(STR_FAULTS_P_OFF), nullptr, nullptr, getMyStr502(STR_NO_YES), _cursor); + break; + case _SELECTION_FAULTS_T_R_RATE: + _menu_depth = 3; + _inputValue(5, getMyStr200(STR_FAULTS_T_R_RATE), getMyStr501(STR__FLOAT__)); + break; + case _SELECTION_FAULTS_T_F_RATE: + _menu_depth = 3; + _inputValue(5, getMyStr200(STR_FAULTS_T_F_RATE), getMyStr501(STR__FLOAT__)); + break; + case _SELECTION_FAULTS_P_RATE: + _menu_depth = 3; + _inputValue(5, getMyStr200(STR_FAULTS_P_RATE), getMyStr501(STR__FLOAT__)); + break; + case _SELECTION_CLEAR_FAULTS: + _printmf("_SELECTION_CLEAR_FAULTS"); + _menu_depth = 2; + _selCnt = my_UserInterfaceMessage(&u8g2, getMyStr200(STR_DEL_ALL_INACTIVE_FAULTS), nullptr, nullptr, getMyStr501(STR_NO_YES), _cursor); break; case _VERIFY_ADMIN: + _printmf("_VERIFY_ADMIN"); _menu_depth = 1; _inputValueActive = true; - sprintf(_text, "%d", _val); - my_UserInterfaceInputValueString(&u8g2, "Systemeinstellungen\nBerechtigung\nnachweisen:", nullptr, _text); + sprintf(_text, getMyStr501(STR_F_DEC_0), _val); + my_UserInterfaceInputValueString(&u8g2, getMyStr200(STR_ADMIN_AUTH), nullptr, _text); break; case _MENU_SYSTEM: + _printmf("_MENU_SYSTEM"); _menu_depth = 1; - _selCnt = my_UserInterfaceSelectionList(&u8g2, "System", _cursor, "Modbus\nDrucksensor"); + _selCnt = my_UserInterfaceSelectionList(&u8g2, getMyStr501(STR_SYSTEM), _cursor, getMyStr200(STR_MENU_SYSTEM)); break; case _MENU_SETTINGS_MODBUS: + _printmf("_MENU_SETTINGS_MODBUS"); _menu_depth = 2; - _selCnt = my_UserInterfaceSelectionList(&u8g2, "Modbus", _cursor, "Adresse\nBaudrate\nAntwort-Delay"); + _selCnt = my_UserInterfaceSelectionList(&u8g2, getMyStr501(STR_MODBUS), _cursor, getMyStr200(STR_MENU_MODBUS)); break; case _SELECTION_MODBUS_BAUDRATE: + _printmf("_SELECTION_MODBUS_BAUDRATE"); _menu_depth = 3; - _selCnt = my_UserInterfaceSelectionList(&u8g2, "Baudrate", _cursor, "300\n1200\n2400\n4800\n9600\n19200\n38400\n57600\n115200"); + _selCnt = my_UserInterfaceSelectionList(&u8g2, getMyStr501(STR_BAUDR), _cursor, getMyStr200(STR_MENU_BAUDR)); break; case _SELECTION_MODBUS_ADDRESS: + _printmf("_SELECTION_MODBUS_ADDRESS"); _menu_depth = 3; - sprintf(_text, "Dezimal: %d", _val); - _inputValue(1, "Modbus Adresse"); + sprintf(_text, getMyStr501(STR_SEL_MODB_ADDR_F), _val); + _inputValue(1, getMyStr200(STR_SEL_MODB_ADDR)); break; case _SELECTION_MODBUS_DELAY: + _printmf("_SELECTION_MODBUS_DELAY"); _menu_depth = 3; - sprintf(_text, "%d.%.d", _val / 10, _val % 10); - _inputValue(1, "Modbus\nAntwort-Verzögerung\nin Millisekunden"); + sprintf(_text, getMyStr501(STR_SEL_MODB_DELAY_F), _val / 10, _val % 10); + _inputValue(1, getMyStr200(STR_SEL_MODB_DELAY)); break; case _SELECTION_P_SENSOR: + _printmf("_SELECTION_P_SENSOR"); _menu_depth = 2; - _selCnt = my_UserInterfaceSelectionList(&u8g2, "Drucksensor", _cursor, "SMC: 1-5V,0-5 bar\nGems: 0-5V,0-6 bar"); + _selCnt = my_UserInterfaceSelectionList(&u8g2, getMyStr501(STR_P_SENSOR), _cursor, getMyStr200(STR_MENU_P_SENSOR)); break; case _SELECTION_RESET: + _printmf("_SELECTION_RESET"); _menu_depth = 1; - _selCnt = my_UserInterfaceMessage(&u8g2, "Controller neustarten", "--------------------", "Wirklich neustarten?", " nein \n ja ", _cursor); + _selCnt = my_UserInterfaceMessage(&u8g2, getMyStr501(STR_RESTART_CONTROLLER), getMyStr502(STR_20_HYPHENS), getMyStr503(STR_REALLY_RESTART), getMyStr200(STR_NO_YES), _cursor); break; default: + _printmf("no matched case"); _menu_depth = 0; } + _printmln(); } void Display::_home() { // Das Display hat eine Auflösung von 128 x 64 Pixel - // Mit der Schriftart u8g2_font_ncenB10_te stellt \xb0 das °-Zeichen dar - int ts1a = _params->ts1 / 100; - int ts1b = _params->ts1 % 100; - int th1a = _params->th1 / 100; - int th1b = _params->th1 % 100; - int psa = _params->ps / 100; - int psb = _params->ps % 100; - int pha = _params->ph / 100; - int phb = _params->ph % 100; - bool cEn = _params->cEn; - bool tEn = (!cEn) ? false : _params->tEn; - bool pInc = (!cEn) ? false : _params->pInc; - bool pDec = (!cEn) ? false : _params->pDec; + int ts1a = params.ts1 / 100; + int ts1b = params.ts1 % 100; + int th1a = params.th1 / 100; + int th1b = params.th1 % 100; + int psa = params.ps / 100; + int psb = params.ps % 100; + int pha = params.ph / 100; + int phb = params.ph % 100; + bool cEn = params.cEn; + bool tEn = (!cEn) ? false : params.tEn; + bool pInc = (!cEn) ? false : params.pInc; + bool pDec = (!cEn) ? false : params.pDec; bool pEn = pInc || pDec; - uint8_t pState = (_vStates->pInc) ? 1 : (_vStates->pDec) ? 2 : 0; - bool tState = _vStates->t1; + bool pFault = faults.pActive(); + bool tFault = faults.tActive(); + uint8_t pState = (vStates.pInc) ? 1 : (vStates.pDec) ? 2 : 0; + bool tState = vStates.t1; // %u in sprintf() geht nur bis uint16_t - String baudStr(_modbusParams->baudrate); + String baudStr(modbusParams.baudrate); + FaultState fState = faults.state(); + u8 x = 112; + u8 y; _printtime("homescreen"); firstPage(); do { setFont(u8g2_font_fub20_tf); - if (_vals->t1 == _SENSOR_FAULT) - sprintf(_text, "--.-- \xb0\C"); + if (vals.t1 == _SENSOR_FAULT) + sprintf(_text, getMyStr501(STR_HOME_1)); else - sprintf(_text, "%d.%.2d \xb0\C", _vals->t1 / 100, _vals->t1 % 100); + sprintf(_text, getMyStr501(STR_HOME_2), vals.t1 / 100, vals.t1 % 100); drawStr(0, 27, _text); - if (_vals->p == _SENSOR_FAULT) - sprintf(_text, "--.-- bar"); + if (vals.p == _SENSOR_FAULT) + sprintf(_text, getMyStr501(STR_HOME_3)); else - sprintf(_text, "%d.%.2d bar", _vals->p / 100, _vals->p % 100); + sprintf(_text, getMyStr501(STR_HOME_4), vals.p / 100, vals.p % 100); drawStr(0, 57, _text); - //drawHLine(0, 57, 128); setFont(u8g2_font_micro_tr); - //setFont(u8g2_font_5x7_tr); - sprintf(_text, "%d.%.2d+-%d.%.2d Regelung %saktiv", ts1a, ts1b, th1a, th1b, (tEn) ? "": "in"); + sprintf(_text, getMyStr501(STR_HOME_5), ts1a, ts1b, th1a, th1b, (tEn) ? "": getMyStr502(STR_IN)); drawStr(0, 5, _text); - sprintf(_text, "%d.%.2d+-%d.%.2d P+ %s P- %s", psa, psb, pha, phb, (pInc) ? "ein": "aus", (pDec) ? "ein": "aus"); + sprintf(_text, getMyStr501(STR_HOME_6), psa, psb, pha, phb, (pInc) ? getMyStr502(STR_ON) : getMyStr502(STR_OFF), (pDec) ? getMyStr503(STR_ON) : getMyStr503(STR_OFF)); drawStr(0, 35, _text); - // sprintf(_text, "Modbus Adr. %u, Baudr. %u", _modbusParams->address, _modbusParams->baudrate); - sprintf(_text, "Modbus Adr. %u, Baudr. %s", _modbusParams->address, baudStr.c_str()); + sprintf(_text, getMyStr501(STR_HOME_7), modbusParams.address, baudStr.c_str()); drawStr(0, 64, _text); if (cEn) { - if (tEn) { - if (tState) { - setFont(u8g2_font_open_iconic_arrow_2x_t); - drawStr(112, 25, "\x58"); // Kreislaufsymbol + _displayFaultOrPowerSymbol(fState, false); + y = 22; + if (!tEn) { // Temperaturregelung deaktiviert + if (tFault && _showFault) { + // Sollte nicht eintreten, da entsprechende Alarme + // mit der Temperaturregelung mit deaktiviert werden + setFont(u8g2_font_open_iconic_embedded_2x_t); + drawStr(x+1, y, "\x47"); // Warnung-Symbol + } else { + setFont(u8g2_font_open_iconic_check_2x_t); + drawStr(x, y, "\x42"); // X in schwarzem Kreis } - } else { - setFont(u8g2_font_open_iconic_check_2x_t); - drawStr(112, 25, "\x42"); // X in schwarzem Kreis + } else if (!faultSettings.tOnIfError && tFault) { + // Ventile werden nicht mehr in controller.cpp geschaltet + setFont(u8g2_font_open_iconic_embedded_2x_t); + drawStr(x+1, y, "\x47"); // Warnung-Symbol + } else if (tFault && _showFault) { + setFont(u8g2_font_open_iconic_embedded_2x_t); + drawStr(x+1, y, "\x47"); // Warnung-Symbol + } else if (tState) { + setFont(u8g2_font_open_iconic_arrow_2x_t); + drawStr(x, y, "\x58"); // Kreislaufsymbol } - if (!pEn) { - setFont(u8g2_font_open_iconic_check_2x_t); - drawStr(112, 55, "\x42"); // X in schwarzem Kreis + y = 58; + if (!pEn) { // Druckregelung deaktiviert + if (pFault && _showFault) { + // Sollte nicht eintreten, da entsprechende Alarme + // mit der Druckregelung mit deaktiviert werden + setFont(u8g2_font_open_iconic_embedded_2x_t); + drawStr(x+1, y, "\x47"); // Warnung-Symbol + } else { + setFont(u8g2_font_open_iconic_check_2x_t); + drawStr(x, y, "\x42"); // X in schwarzem Kreis + } + } else if (!faultSettings.pOnIfError && pFault) { + // Ventile werden nicht mehr in controller.cpp geschaltet + setFont(u8g2_font_open_iconic_embedded_2x_t); + drawStr(x+1, y, "\x47"); // Warnung-Symbol + } else if (pFault && _showFault) { + setFont(u8g2_font_open_iconic_embedded_2x_t); + drawStr(x+1, y, "\x47"); // Warnung-Symbol } else if (pState == 1) { setFont(u8g2_font_open_iconic_arrow_2x_t); - drawStr(112, 55, "\x4b"); // Pfeil rauf + drawStr(x, y, "\x4b"); // Pfeil rauf } else if (pState == 2) { setFont(u8g2_font_open_iconic_arrow_2x_t); - drawStr(112, 55, "\x48"); // Pfeil runter + drawStr(x, y, "\x48"); // Pfeil runter } } else { - setFont(u8g2_font_open_iconic_check_2x_t); - // drawStr(112, 25, "\x42"); // X in schwarzem Kreis - // drawStr(112, 55, "\x42"); // X in schwarzem Kreis - drawStr(112, 22, "\x42"); // X in schwarzem Kreis - drawStr(112, 58, "\x42"); // X in schwarzem Kreis - setFont(u8g2_font_open_iconic_embedded_2x_t); - drawStr(113, 40, "\x4e"); // Power-Symbol - // drawLine(0, 0, 128, 64); - // drawLine(128, 0, 0, 64); + _displayFaultOrPowerSymbol(fState, true); + y = 22; + if (tFault && _showFault) { + setFont(u8g2_font_open_iconic_embedded_2x_t); + drawStr(x+1, y, "\x47"); // Warnung-Symbol + } else { + setFont(u8g2_font_open_iconic_check_2x_t); + drawStr(x, y, "\x42"); // X in schwarzem Kreis + } + y = 58; + if (pFault && _showFault) { + setFont(u8g2_font_open_iconic_embedded_2x_t); + drawStr(x+1, y, "\x47"); // Warnung-Symbol + } else { + setFont(u8g2_font_open_iconic_check_2x_t); + drawStr(x, y, "\x42"); // X in schwarzem Kreis + } } } while (nextPage()); } +bool Display::_displayFaultOrPowerSymbol(const FaultState &state, bool power) +{ + bool fault = false; + switch (state) { + case activeFault: + case inactiveFault: + fault = true; + _showFaultC++; + if (_showFaultC % ((state == activeFault) ? 2 : 4) == 0) { + _showFault = !_showFault; + _showFaultC = 0; + } + break; + default: + if (power) { + setFont(u8g2_font_open_iconic_embedded_2x_t); + drawStr(113, 40, "\x4e"); // Power-Symbol + } + } + if (fault && _showFault) { + setFont(u8g2_font_open_iconic_embedded_2x_t); + drawStr(113, 40, "\x47"); // Warnung-Symbol + } else if (fault) { + char text[3]; + sprintf(text, "%u", faults.count()); + setFont(u8g2_font_fub11_tf); + u8 w = getUTF8Width(text); + u8 x; + if (119 + w > 128) + x = 128 - w; + else + x = 119 - (w / 2); + drawStr(x, 37, text); + } + return fault; +} + void Display::init() { pinMode(bgLed, OUTPUT); @@ -472,22 +845,22 @@ void Display::oneWireDevicesDetected(uint8_t count) setFont(_SEL_FONT); setFontRefHeightAll(); sprintf(_text, "%u", count); - userInterfaceMessage("OneWire-Sensoren", "Erfolgreich erkannt:", _text, " OK "); + userInterfaceMessage(getMyStr501(STR_1W_1), getMyStr502(STR_1W_2), _text, getMyStr503(STR_OK)); } void Display::tooMuchOneWireDevicesDetected(uint8_t actual, uint8_t max) { setFont(_SEL_FONT); setFontRefHeightAll(); - sprintf(_text, "%u (max: %u)", actual, max); - userInterfaceMessage("Fehler OneWire", "Zu viele Sensoren:", _text, " OK "); + sprintf(_text, getMyStr501(STR_1W_F1), actual, max); + userInterfaceMessage(getMyStr501(STR_1W_ERR), getMyStr502(STR_1W_3), _text, getMyStr503(STR_OK)); } void Display::noOneWireDevicesDetected() { setFont(_SEL_FONT); setFontRefHeightAll(); - userInterfaceMessage("Fehler OneWire", "Keine Sensoren", "erkannt", " OK "); + userInterfaceMessage(getMyStr501(STR_1W_ERR), getMyStr502(STR_1W_4), getMyStr503(STR_1W_5), getMyStr200(STR_OK)); } void Display::greeting() @@ -566,7 +939,7 @@ bool Display::bgLightState() void Display::_baudrateCursorPos() { - switch (_modbusParams->baudrate) { + switch (modbusParams.baudrate) { case 300: _cursor = _POS_BAUDRATE_300; break; case 1200: _cursor = _POS_BAUDRATE_1200; break; case 2400: _cursor = _POS_BAUDRATE_2400; break; @@ -581,19 +954,19 @@ void Display::_baudrateCursorPos() void Display::_setModbusBaudrate() { - modbusParameters params; + ModbusParameters mbParamsLocal; switch (_cursor) { - case _POS_BAUDRATE_300: params.baudrate = 300; break; - case _POS_BAUDRATE_1200: params.baudrate = 1200; break; - case _POS_BAUDRATE_2400: params.baudrate = 2400; break; - case _POS_BAUDRATE_4800: params.baudrate = 4800; break; - case _POS_BAUDRATE_19200: params.baudrate = 19200; break; - case _POS_BAUDRATE_38400: params.baudrate = 38400; break; - case _POS_BAUDRATE_57600: params.baudrate = 57600; break; - case _POS_BAUDRATE_115200: params.baudrate = 115200; break; - default: params.baudrate = 9600; + case _POS_BAUDRATE_300: mbParamsLocal.baudrate = 300; break; + case _POS_BAUDRATE_1200: mbParamsLocal.baudrate = 1200; break; + case _POS_BAUDRATE_2400: mbParamsLocal.baudrate = 2400; break; + case _POS_BAUDRATE_4800: mbParamsLocal.baudrate = 4800; break; + case _POS_BAUDRATE_19200: mbParamsLocal.baudrate = 19200; break; + case _POS_BAUDRATE_38400: mbParamsLocal.baudrate = 38400; break; + case _POS_BAUDRATE_57600: mbParamsLocal.baudrate = 57600; break; + case _POS_BAUDRATE_115200: mbParamsLocal.baudrate = 115200; break; + default: mbParamsLocal.baudrate = 9600; } - setModbusParams(params); + setModbusParams(mbParamsLocal); } uint8_t Display::_event() @@ -605,21 +978,21 @@ uint8_t Display::_event() bool prev = !digitalRead(btnPrev); bool cancel = !digitalRead(btnCancel); if (next || prev || cancel) { - if (millis() - _checkFastStepTime > _BEGIN_FAST_STEPS_DELAY) { - if (millis() - _lastFastStep > _FAST_STEP_MS) { - _lastFastStep = millis(); + if (currMillis - _checkFastStepTime > _BEGIN_FAST_STEPS_DELAY) { + if (currMillis - _lastFastStep > _fastStepsDelay) { + _lastFastStep = currMillis; if (next) event = U8X8_MSG_GPIO_MENU_NEXT; else if (prev) event = U8X8_MSG_GPIO_MENU_PREV; else if (cancel && _selection) event = _BACK_TO_HOME_SCREEN; - if (millis() - _checkFastStepTime > _BEGIN_BIG_STEPS_DELAY) + if (currMillis - _checkFastStepTime > _BEGIN_BIG_STEPS_DELAY) _enableBigSteps = true; } } } else { - _checkFastStepTime = millis(); + _checkFastStepTime = currMillis; _enableBigSteps = false; } return event; @@ -695,39 +1068,39 @@ void Display::_restrictInputValue(int min, int max, Precision p=Precision_0) switch (p) { case Precision_1: if (min % 10 == 0) - sprintf(s1, "%d", min / 10); + sprintf(s1, getMyStr501(STR_F_DEC_0), min / 10); else - sprintf(s1, "%d.%.1d", min / 10, min % 10); + sprintf(s1, getMyStr501(STR_F_DEC_1), min / 10, min % 10); if (max % 10 == 0) - sprintf(s2, "%d", max / 10); + sprintf(s2, getMyStr501(STR_F_DEC_0), max / 10); else - sprintf(s2, "%d.%.1d", max / 10, max % 10); + sprintf(s2, getMyStr501(STR_F_DEC_1), max / 10, max % 10); break; case Precision_2: if (min % 100 == 0) - sprintf(s1, "%d", min / 100); + sprintf(s1, getMyStr501(STR_F_DEC_0), min / 100); else - sprintf(s1, "%d.%.2d", min / 100, min % 100); + sprintf(s1, getMyStr501(STR_F_DEC_2), min / 100, min % 100); if (max % 100 == 0) - sprintf(s2, "%d", max / 100); + sprintf(s2, getMyStr501(STR_F_DEC_0), max / 100); else - sprintf(s2, "%d.%.2d", max / 100, max % 100); + sprintf(s2, getMyStr501(STR_F_DEC_2), max / 100, max % 100); break; default: - sprintf(s1, "%d", min); - sprintf(s2, "%d", max); + sprintf(s1, getMyStr501(STR_F_DEC_0), min); + sprintf(s2, getMyStr501(STR_F_DEC_0), max); } _valMin = min; _valMax = max; - sprintf(_text2, "min: %s, max: %s", s1, s2); + sprintf(_text2, getMyStr501(STR_F_S_MIN_MAX), s1, s2); } void Display::_inputValue(const uint8_t stepSize, const char *title, const char *val=nullptr, const char *text=nullptr) { if (!val) { val = _text; - } else if (val == "__float__") { - sprintf(_text, "%d.%.2d", _val / 100, _val % 100); + } else if (!strncmp(val, getMyStr503(STR__FLOAT__), 9)) { + sprintf(_text, getMyStr501(STR_F_DEC_2), _val / 100, _val % 100); val = _text; } if (!text) diff --git a/src/display/display.h b/src/display/display.h index bdff0e7..6c0b688 100644 --- a/src/display/display.h +++ b/src/display/display.h @@ -2,7 +2,9 @@ #define MY_DISPLAY_H_INCLUDED #include -#include "../common.h" +#include "../types.h" +#include "../macros.h" +#include "../faults/faults.h" //#define _STD_FONT u8g2_font_ncenB10_te //#define _FIRS_LINE_Y 11 @@ -13,11 +15,10 @@ #define _SEL_FONT u8g2_font_6x10_tf #define _FIRS_LINE_Y 12 #define _STD_LINEHEIGHT 13 -// Mit der Schriftart u8g2_font_ncenB10_te stellt \xb0 das °-Zeichen dar #define _BEGIN_FAST_STEPS_DELAY 500 #define _BEGIN_BIG_STEPS_DELAY 3000 -#define _FAST_STEP_MS 100 +#define _FAST_STEP_MS 110 #define _BIG_STEP 10 #define _BACK_TO_HOME_SCREEN 1 @@ -27,6 +28,10 @@ #define _MENU_SETTINGS_MODBUS 11 #define _MENU_TEMPERATURE 12 #define _MENU_PRESSURE 13 +#define _MENU_FAULTS_MAIN 20 +#define _MENU_FAULTS_OPEN 21 +#define _MENU_FAULTS_SETTINGS 22 + #define _SELECTION_TEMPERATURE_ENABLED 100 #define _SELECTION_TEMPERATURE_SETPOINT 101 #define _SELECTION_TEMPERATURE_HYSTERESIS 102 @@ -34,12 +39,22 @@ #define _SELECTION_PRESSURE_ENABLE_DECREASE 111 #define _SELECTION_PRESSURE_SETPOINT 115 #define _SELECTION_PRESSURE_HYSTERESIS 116 +#define _SELECTION_FAULTS_ACOUSTIC 125 +#define _SELECTION_FAULTS_OPTICAL 126 +#define _SELECTION_FAULTS_T_OFF 127 +#define _SELECTION_FAULTS_P_OFF 128 +#define _SELECTION_FAULTS_T_R_RATE 129 +#define _SELECTION_FAULTS_T_F_RATE 130 +#define _SELECTION_FAULTS_P_RATE 131 #define _SELECTION_MODBUS_BAUDRATE 150 #define _SELECTION_MODBUS_ADDRESS 151 #define _SELECTION_MODBUS_DELAY 152 #define _SELECTION_P_SENSOR 200 +#define _SELECTION_CLEAR_FAULTS 220 #define _SELECTION_RESET 255 +#define _FAULT_INFO 170 + #define _VERIFY_ADMIN 253 #define _ADMIN_CODE 42 @@ -52,7 +67,18 @@ #define _POS_PRESSURE_ENABLE_DECREASE 1 #define _POS_PRESSURE_SETPOINT 2 #define _POS_PRESSURE_HYSTERESIS 3 -#define _POS_SYSTEM 2 +#define _POS_FAULTS_MAIN 2 +#define _POS_FAULTS_OPEN 0 +#define _POS_FAULTS_CLEAR 1 +#define _POS_FAULTS_SETTINGS 2 +#define _POS_FAULTS_ACOUSTIC 0 +#define _POS_FAULTS_OPTICAL 1 +#define _POS_FAULTS_T_OFF 2 +#define _POS_FAULTS_P_OFF 3 +#define _POS_FAULTS_T_R_RATE 4 +#define _POS_FAULTS_T_F_RATE 5 +#define _POS_FAULTS_P_RATE 6 +#define _POS_SYSTEM 3 #define _POS_MODBUS_MENU 0 #define _POS_MODBUS_ADDRESS 0 #define _POS_MODBUS_BAUDRATE 1 @@ -60,7 +86,7 @@ #define _POS_P_SENSOR_SELECTION 1 #define _POS_P_SMC_1_5V_0_5BAR 0 #define _POS_P_GEMS_0_5V_0_6BAR 1 -#define _POS_RESET 3 +#define _POS_RESET 4 #define _POS_BAUDRATE_300 0 #define _POS_BAUDRATE_1200 1 @@ -110,12 +136,15 @@ private: unsigned long _checkFastStepTime; // letzte Zeit, zu der ein entsprechender Button NICHT gedrückt war unsigned long _lastFastStep; // letzte Zeit, zu der ein schneller Schritt ausgeführt wurde int _stepSize; // Um wieviel der entsprechende Wert auf einmal geändert werden soll - - parameters *_params; - values *_vals; - modbusParameters *_modbusParams; - PSensor *_pSensor; - valveStates *_vStates; + u8 _fastStepsDelay; + bool _showFault; + u8 _showFaultC = 0; + FaultState _lastFaultState = noFault; + u8 _lastCEn; + u8 _lastOpticalState; + u8 _currBrightness = 0; + u32 _lastBrightnessChange; + bool _incBrightness = false; void _saveCursorPosForLevel(MenuLevel); uint8_t _restoreCursor(); @@ -127,6 +156,7 @@ private: void _nextEvent(); void _prevEvent(); void _home(); + bool _displayFaultOrPowerSymbol(const FaultState&, bool); void _inputValue(const uint8_t stepSize, const char *title, const char *text=nullptr, const char *val=nullptr); void _restrictInputValue(int min, int max, Precision p=Precision_0); @@ -143,12 +173,7 @@ public: uint8_t btnPrev, uint8_t btnSelect, uint8_t btnCancel, - uint8_t bgLED, - parameters*, - values*, - modbusParameters*, - PSensor*, - valveStates*); + uint8_t bgLED); void init(); void process(); @@ -168,11 +193,6 @@ public: bool bgLightState(); void (*reset)() = 0; - - /* Callback-Funktionen */ - void(*setParams)(parameters&); - void(*setModbusParams)(const modbusParameters&); - void(*setPSensor)(const PSensor&); }; #endif // MY_DISPLAY_H_INCLUDED diff --git a/src/faults/faults.cpp b/src/faults/faults.cpp new file mode 100644 index 0000000..2f8041c --- /dev/null +++ b/src/faults/faults.cpp @@ -0,0 +1,683 @@ +#include "faults.h" +#include "../types.h" +#include "../mystrings/mystrings.h" +#include "../macros.h" +#include + +extern u32 currMillis; +extern u16 zones; +extern u16 modbusAlarms[]; + +const u16 EEPROM_FAULTS[] = { + _EEPROM_FAULT_1, _EEPROM_FAULT_2, _EEPROM_FAULT_3,_EEPROM_FAULT_4, _EEPROM_FAULT_5, + _EEPROM_FAULT_6, _EEPROM_FAULT_7, _EEPROM_FAULT_8, _EEPROM_FAULT_9, _EEPROM_FAULT_10, + _EEPROM_FAULT_11, _EEPROM_FAULT_12, _EEPROM_FAULT_13, _EEPROM_FAULT_14, _EEPROM_FAULT_15, + _EEPROM_FAULT_16, _EEPROM_FAULT_17, _EEPROM_FAULT_18, _EEPROM_FAULT_19, _EEPROM_FAULT_20, + _EEPROM_FAULT_21, _EEPROM_FAULT_22, _EEPROM_FAULT_23, _EEPROM_FAULT_24, _EEPROM_FAULT_25, + _EEPROM_FAULT_26, _EEPROM_FAULT_27, _EEPROM_FAULT_28, _EEPROM_FAULT_29, _EEPROM_FAULT_30 +}; + +Fault::Fault() + : code(invalidCode) + , active(false) +{ +} + +Fault::Fault(FaultCode fcode, const u16 id_) + : code(fcode) + , id(id_) + , counterActivated(0) + , counterDeactivated(0) +{ + timeActivated = currMillis; +} + +Fault::Fault(const Fault &other) +{ + id = other.id; + code = other.code; + timeActivated = other.timeActivated; + timeDeactivated = other.timeDeactivated; + timeActivatedOverflow = other.timeActivatedOverflow; + timeDeactivatedOverflow = other.timeDeactivatedOverflow; + active = other.active; + counterActivated = other.counterActivated; + counterDeactivated = other.counterDeactivated; + _of1 = other._of1; + _of2 = other._of2; +} + +Fault::Fault(const SavedFault &other) +{ + id = other.id; + code = other.code; + active = other.active; + counterActivated = other.counterActivated + 1; + if (other.deactivated) { + timeDeactivated = 0; + counterDeactivated = other.counterDeactivated + 1; + } else { + counterDeactivated = 0; + } +} + +static char __buff_1[30]; + +const char* Fault::title() +{ + switch(code) { + case t1TooHi: + (zones > 1) ? getMyStr200(STR_T1_TOO_HI) : getMyStr200(STR_T_TOO_HI); + break; + case t1TooLo: + (zones > 1) ? getMyStr200(STR_T1_TOO_LO) : getMyStr200(STR_T_TOO_LO); + break; + case t2TooHi: + getMyStr200(STR_T2_TOO_HI); + break; + case t2TooLo: + getMyStr200(STR_T2_TOO_LO); + break; + case pTooHi: + getMyStr200(STR_P_TOO_HI); + break; + case pTooLo: + getMyStr200(STR_P_TOO_LO); + break; + case t1Invalid: + case t2Invalid: + getMyStr200(STR_T_FAIL); + break; + case pInvalid: + getMyStr200(STR_P_FAIL); + break; + default: + getMyStr200(STR_UNKNOWN_ERROR); + } + if (active) { + strcpy(__buff_1, "*"); + strcat(__buff_1, charBuffer200); + return __buff_1; + } + return charBuffer200; +} + +const char* Fault::activatedText() +{ + const u32 diff = currMillis - timeActivated; + u16 result; + if (counterActivated) { + sprintf(__buff_1, getMyStr200(STR_UNKNOWN_ACTIVATED), counterActivated); + } else if (timeActivatedOverflow) { + return getMyStr200(STR_GT_7_WEEKS_1); + } else if (diff < 100000) { // < 100 Sekunden + result = diff / 1000; + sprintf(__buff_1, getMyStr200(STR_F_SECONDS_1), result); + } else if (diff < 6000000) { // < 100 Minuten + result = diff / 60000; + sprintf(__buff_1, getMyStr200(STR_F_MINUTES_1), result); + } else if (diff < 172800000) { // < 48 Stunden + result = diff / 3600000; + sprintf(__buff_1, getMyStr200(STR_F_HOURS_1), result); + } else { // Tage + result = diff / 86400000; + sprintf(__buff_1, getMyStr200(STR_F_DAYS_1), result); + } + return __buff_1; +} + +const char* Fault::deactivatedText() +{ + const u32 diff = currMillis - timeDeactivated; + u16 result; + if (timeDeactivatedOverflow) { + return getMyStr200(STR_GT_7_WEEKS_2); + } else if (counterDeactivated) { + sprintf(__buff_1, getMyStr200(STR_UNKNOWN_DEACTIVAED), counterDeactivated); + } else if (timeDeactivated == 0xFFFFFFFF) { // wurde nie deaktiviert + sprintf(__buff_1, (active) ? getMyStr200(STR_NEVER_DEACTIVATED_ACTIVE) : getMyStr200(STR_NEVER_DEACTIVATED_INACTIVE)); + } else if (diff < 100000) { // < 100 Sekunden + result = diff / 1000; + sprintf(__buff_1, getMyStr200(STR_F_SECONDS_2), result); + } else if (diff < 6000000) { // < 100 Minuten + result = diff / 60000; + sprintf(__buff_1, getMyStr200(STR_F_MINUTES_2), result); + } else if (diff < 172800000) { // < 48 Stunden + result = diff / 3600000; + sprintf(__buff_1, getMyStr200(STR_F_HOURS_2), result); + } else { // Tage + result = diff / 86400000; + sprintf(__buff_1, getMyStr200(STR_F_DAYS_2), result); + } + return __buff_1; +} + +void Fault::process() +{ + if (!counterActivated) { + if (!_of1 && currMillis < timeActivated) + _of1 = true; + else if (!timeActivatedOverflow && _of1 && currMillis > timeActivated) + timeActivatedOverflow = true; + if (!active && !_of2 && currMillis < timeDeactivated) + _of2 = true; + else if (!timeDeactivatedOverflow && _of2 && currMillis > timeDeactivated) + timeDeactivatedOverflow = true; + } +} + + +FaultSettings faultSettings; + +FaultList::FaultList() +{ + EEPROM.get(_EEPROM_FAULT_LAST_ID, _lastID); + if (_lastID == 0xFFFF) + _lastID = 0; + uint8_t count = EEPROM.read(_EEPROM_FAULT_COUNT); + _size = (count > _MAX_FAULTS) ? 0 : count; + for (u8 i=0; i<_size; i++) { + SavedFault savedFault; + u16 id, reg_1, reg_2; + EEPROM.get(EEPROM_FAULTS[i], id); + EEPROM.get(EEPROM_FAULTS[i] + 2, reg_1); + EEPROM.get(EEPROM_FAULTS[i] + 4, reg_2); + u8 rawCode = reg_1 >> 12; + if (reg_2 >> 14 || rawCode > 9 || id == 0 || id == 0xFFFF) { + // unter einer dieser Bedingungen wären die Daten nicht gültig + savedFault.id = 0; + savedFault.code = invalidCode; + savedFault.active = false; + savedFault.deactivated = true; + savedFault.counterActivated = 0; + savedFault.counterDeactivated = 0; + } else { + savedFault.id = id; + savedFault.counterActivated = reg_1 & 0xFFF; + savedFault.counterDeactivated = reg_2 & 0xFFF; + savedFault.active = bitRead(reg_2, 12); + savedFault.deactivated = bitRead(reg_2, 13); + switch (rawCode) { + case t1TooHi: + savedFault.code = t1TooHi; + if (savedFault.active) { + _lastActiveT1Fault = savedFault.code; + _t1Active = true; + } + break; + case t1TooLo: + savedFault.code = t1TooLo; + if (savedFault.active) { + _lastActiveT1Fault = savedFault.code; + _t1Active = true; + } + break; + case t1Invalid: + savedFault.code = t1Invalid; + if (savedFault.active) { + _lastActiveT1Fault = savedFault.code; + _t1Active = true; + } + break; + case t2TooHi: + savedFault.code = t2TooHi; + if (savedFault.active) { + _lastActiveT2Fault = savedFault.code; + _t2Active = true; + } + break; + case t2TooLo: + savedFault.code = t2TooLo; + if (savedFault.active) { + _lastActiveT2Fault = savedFault.code; + _t2Active = true; + } + break; + case t2Invalid: + savedFault.code = t2Invalid; + if (savedFault.active) { + _lastActiveT2Fault = savedFault.code; + _t2Active = true; + } + break; + case pTooHi: + savedFault.code = pTooHi; + if (savedFault.active) { + _lastActivePFault = savedFault.code; + _pActive = true; + } + break; + case pTooLo: + savedFault.code = pTooLo; + if (savedFault.active) { + _lastActivePFault = savedFault.code; + _pActive = true; + } + break; + case pInvalid: + savedFault.code = pInvalid; + if (savedFault.active) { + _lastActivePFault = savedFault.code; + _pActive = true; + } + break; + default: + savedFault.code = invalidCode; + } + } + _list[i] = savedFault; + _save(i); +#if _MODBUS == 1 + _setModbusAlarm(_list[i], i, true); +#endif + } +} + +void FaultList::process() +{ + FaultState s = noFault; + for (u8 i=0; i<_size; i++) { + Fault &f = _list[i]; + f.process(); + bool wasActive = f.active; + switch (f.code) { + case t1TooHi: + case t1TooLo: + case t1Invalid: + _test(f, _t1Active, _lastActiveT1Fault); + break; + case t2TooHi: + case t2TooLo: + case t2Invalid: + _test(f, _t2Active, _lastActiveT2Fault); + break; + case pTooHi: + case pTooLo: + case pInvalid: + _test(f, _pActive, _lastActivePFault); + } + switch (s) { + case inactiveFault: + if (f.active) + s = activeFault; + break; + case activeFault: + break; + default: // noFault + s = (f.active) ? activeFault : inactiveFault; + } + if (f.active != wasActive) { + // _printf("save in process(): "); + _save(i); + } +#if _MODBUS == 1 + _setModbusAlarm(f, i, false); +#endif + } + _state = s; +} + +bool FaultList::check(FaultCode code, int16_t val=0) +{ + if (_size >= _MAX_FAULTS) + return false; + switch (code) { + case t1TooHi: + if (code == _lastActiveT1Fault || currMillis - _lastT1Idle < _CHECK_T_FAULT_INTERVAL + || faultSettings.tMinFRate < _compT1 - vals.t1) + return false; + _t1Active = true; + _lastActiveT1Fault = code; + break; + case t1TooLo: + if (code == _lastActiveT1Fault || currMillis - _lastT1Idle < _CHECK_T_FAULT_INTERVAL + || faultSettings.tMinFRate < vals.t1 - _compT1) + return false; + _t1Active = true; + _lastActiveT1Fault = code; + break; + case t1Invalid: + if (code == _lastActiveT1Fault || currMillis - _lastT1Idle < _CHECK_T_FAULT_INTERVAL) + return false; + _t1Active = true; + _lastActiveT1Fault = code; + break; + case t2TooHi: + if (code == _lastActiveT2Fault || currMillis - _lastT2Idle < _CHECK_T_FAULT_INTERVAL + || faultSettings.tMinFRate < _compT2 - vals.t2) + return false; + _t2Active = true; + _lastActiveT2Fault = code; + break; + case t2TooLo: + if (code == _lastActiveT2Fault || currMillis - _lastT2Idle < _CHECK_T_FAULT_INTERVAL + || faultSettings.tMinFRate < vals.t2 - _compT2) + return false; + _t2Active = true; + _lastActiveT2Fault = code; + break; + case t2Invalid: + if (code == _lastActiveT2Fault || currMillis - _lastT2Idle < _CHECK_T_FAULT_INTERVAL) + return false; + _t2Active = true; + _lastActiveT2Fault = code; + break; + case pTooHi: + if (code == _lastActivePFault || currMillis - _lastPIdle < _CHECK_P_FAULT_INTERVAL + || faultSettings.pMinRate < _compP - val) + return false; + _pActive = true; + _lastActivePFault = code; + break; + case pTooLo: + if (code == _lastActivePFault || currMillis - _lastPIdle < _CHECK_P_FAULT_INTERVAL + || faultSettings.pMinRate < val - _compP) + return false; + _pActive = true; + _lastActivePFault = code; + break; + case pInvalid: + if (code == _lastActivePFault || currMillis - _lastPIdle < _CHECK_P_FAULT_INTERVAL) + return false; + _pActive = true; + _lastActivePFault = code; + break; + } + _lastID++; + if (_lastID == 0xFFFF || _lastID == 0) + _lastID = 1; + Fault f(code, _lastID); + // _printf("Nächste Alarm-ID: "); _println(_lastID); + _list[_size] = f; + // _printf("save in check(): "); + _save(_size); + _saveLastID(); + _size++; + _saveSize(); +#if _MODBUS == 1 + _setModbusAlarm(f, _size - 1, true); +#endif + return true; +} + +u8 FaultList::count() +{ + return _size; +} + +Fault* FaultList::first() +{ + if (_size) + return &_list[0]; + return nullptr; +} + +Fault* FaultList::last() +{ + if (_size) + return &_list[_size-1]; + return nullptr; +} + +Fault* FaultList::at(const u8 position) +{ + if (position < _size) + return &_list[position]; + return nullptr; +} + +bool FaultList::removeID(const u16 id) +{ + for (u8 i=0; i<_size; i++) { + if (_list[i].id == id) { + return removeOne(i); + } + } + return false; +} + +bool FaultList::removeOne(const u8 position) +{ + if (position < _size) { + if (_list[position].active) + return false; // aktiver Fehler soll nicht gelöscht werden! + u8 i; + for (i=position; i<_size; i++) { + if (i < _MAX_FAULTS - 1) + _list[i] = _list[i + 1]; + } + _size--; +#if _MODBUS == 1 + if (!_size) + // normalerweise wird der Alarm-Counter in _setModbusAlarm() gesetzt, + // aber wenn _size == 0, muss der Counter extra zurückgesetzt werden + modbusAlarms[0] = 0; +#endif + if (!_removeAllActive) { + for (i=0; i<_size; i++) + _save(i); + _saveSize(); + } + return true; + } + return false; +} + +bool FaultList::removeAll() +{ + bool atLeastOneRemoved = false; + if (!_size) { + return false; + } else if (_state != activeFault) { + _size = 0; + _saveSize(); + atLeastOneRemoved = true; +#if _MODBUS == 1 + // normalerweise wird der Alarm-Counter in _setModbusAlarm() gesetzt, + // aber wenn _size == 0, muss der Counter extra zurückgesetzt werden + modbusAlarms[0] = 0; +#endif + } else { + _removeAllActive = true; + bool finished = false; + u8 i; + while (!finished) { + for (i=0; i<_size; i++) { + if (!_list[i].active) { // aktiver Fehler soll nicht gelöscht werden! + removeOne(i); + atLeastOneRemoved = true; + break; + } + if (i >= _size - 1) + finished = true; + } + } + for (i=0; i<_size; i++) + _save(i); + _saveSize(); + _removeAllActive = false; + } + return atLeastOneRemoved; +} + +FaultState FaultList::state() +{ + return _state; +} + +void FaultList::t1Idle() { + static bool firstRun = true; + if (!_t1Active) { + return; + } + if (firstRun) { + // Um beim ersten Idle einen ev. gespeicherten Code nicht zurückzusetzen + firstRun = false; + return; + } + _t1Active = false; + _lastActiveT1Fault = invalidCode; +} + +void FaultList::t1Idle(const int16_t val) +{ + _compT1 = val; + _lastT1Idle = currMillis; + t1Idle(); +} + +void FaultList::t2Idle() { + static bool firstRun = true; + if (!_t2Active) { + return; + } + if (firstRun) { + // Um beim ersten Idle einen ev. gespeicherten Code nicht zurückzusetzen + firstRun = false; + return; + } + _t2Active = false; + _lastActiveT2Fault = invalidCode; +} + +void FaultList::t2Idle(const int16_t val) +{ + _compT2 = val; + _lastT2Idle = currMillis; + t2Idle(); +} + +void FaultList::pIdle() { + static bool firstRun = true; + if (!_pActive) { + return; + } + if (firstRun) { + // Um beim ersten Idle einen ev. gespeicherten Code nicht zurückzusetzen + firstRun = false; + return; + } + _pActive = false; + _lastActivePFault = invalidCode; +} + +void FaultList::pIdle(const int16_t val) +{ + _compP = val; + _lastPIdle = currMillis; + pIdle(); +} + +const char* FaultList::menu() +{ + if (_size) { + _str = ""; + for (u8 i=0; i<_size; i++) { + _str += _list[i].title(); + if (i < _size - 1) + _str += '\n'; + } + return _str.c_str(); + } else { + return getMyStr503(STR_NO_ERROR); + } +} + +bool FaultList::tActive() +{ + return _t1Active || _t2Active; +} + +bool FaultList::pActive() +{ + return _pActive; +} + +void FaultList::_save(const u8 index) +{ + // _printf("Speichere Index "); _print(index); + if (index <= _size) { + u16 reg_1 = _list[index].counterActivated; + u16 reg_2 = _list[index].counterDeactivated; + reg_1 |= _list[index].code << 12; // setze Bit 12...15 + if (_list[index].active) + reg_2 |= 0x1000; // Setze Bit 12 + if (_list[index].timeDeactivated != 0xFFFFFFFF) + reg_2 |= 0x2000; // Setze Bit 13 + EEPROM.put(EEPROM_FAULTS[index], _list[index].id); + EEPROM.put(EEPROM_FAULTS[index] + 2, reg_1); + EEPROM.put(EEPROM_FAULTS[index] + 4, reg_2); + // _printf(", code = "); _print(_list[index].code); + // _printf(", active = "); _print(_list[index].active); + } + // _println(); +} + +void FaultList::_saveSize() +{ + EEPROM.update(_EEPROM_FAULT_COUNT, _size); +} + +void FaultList::_saveLastID() +{ + EEPROM.put(_EEPROM_FAULT_LAST_ID, _lastID); +} + +void FaultList::_test(Fault &f, const bool &groupActive, const FaultCode &lastActiveFault) +{ + if (f.active && !groupActive) { + f.timeDeactivated = + (currMillis == 0xFFFFFFFF) ? 0xFFFFFFFE : + (currMillis == 0) ? 1 : currMillis; + f.active = false; + } else if (f.code != lastActiveFault) + f.active = false; +} + +#if _MODBUS == 1 +void FaultList::_setModbusAlarm(const Fault &fault, const u8 index, const bool new_) +{ + u16 reg_1 = 0; + u16 reg_2 = 0; + u16 timestamp = modbusData[5] & 0xFFF; + if (fault.counterActivated) { + reg_1 = fault.counterActivated; + reg_2 = 0x1000; // Bit 12 setzen + } else if (fault.timeActivatedOverflow) { + reg_1 = 0xFFF; + reg_2 = 0x2000; // Bit 13 setzen + } else { + reg_1 = timestamp; + } + reg_1 |= fault.code << 12; + if (fault.counterDeactivated) { + reg_2 |= fault.counterDeactivated; + reg_2 |= 0x4000; // Bit 14 setzen + } else if (fault.timeDeactivatedOverflow) { + reg_2 |= 0xFFF; + reg_2 |= 0x8000; // Bit 15 setzen + } else if (fault.timeDeactivated == 0xFFFFFFFF) { + // wurde nie deaktiviert + if (!fault.active) { + // Der Alarm wurde nicht deaktiviert, sondern von einem neuen Alarm abgelöst + reg_2 |= 1; + } // else: nichts tun, die 12 MSBs von reg_2 bleiben 0 + } else { + // Die Werte 0 und 1 sind reserviert, also muss der Wert hier min. 2 sein + reg_2 |= (timestamp > 1) ? timestamp : 2; + } + modbusAlarms[0] = _size; + modbusAlarms[index * 3 + 1] = fault.id; + modbusAlarms[index * 3 + 2] = reg_1; + modbusAlarms[index * 3 + 3] = reg_2; + if (new_) { + // Bit 12 gibt an, ob ein neuer Fehler aufgetreten ist - setzen + bitSet(modbusData[1], 12); + } + // _printf("alarm reg 1 (index "); _print(index*3+1); _printf("): "); _println(fault.id); + // _printf("alarm reg 2 (index "); _print(index*3+2); _printf("): "); _println(reg_1); + // _printf("alarm reg 3 (index "); _print(index*3+3); _printf("): "); _println(reg_2); +} +#endif + +FaultList faults; diff --git a/src/faults/faults.h b/src/faults/faults.h new file mode 100644 index 0000000..55d11ec --- /dev/null +++ b/src/faults/faults.h @@ -0,0 +1,124 @@ +#ifndef FAULTS_H_INCLUDED +#define FAULTS_H_INCLUDED + +#include +#include "../macros.h" + +enum FaultCode : u8 +{ + invalidCode = 0, + t1TooHi, t1TooLo, t1Invalid, + t2TooHi, t2TooLo, t2Invalid, + pTooHi, pTooLo, pInvalid +}; + +enum FaultState : u8 +{ + noFault, activeFault, inactiveFault +}; + +struct SavedFault +{ + FaultCode code; // Fehlercode + u16 id; // ID, mit der der Master den Alarm identifizieren kann + u16 counterActivated; // Wie oft der Regler seit dem Alarm gestartet/resetted wurde + u16 counterDeactivated; // Wie oft der Regler seit der Alarm-Deaktivierung gestartet/resetted wurde + bool active; // Ob der Fehler beim Ausschalten noch vorhanden war + bool deactivated; // Ob der Fehler vor dem Ausschalten noch behoben wurde +}; + +class Fault +{ +public: + Fault(); + Fault(FaultCode, const u16); + Fault(const Fault&); + Fault(const SavedFault&); + void process(); + const char* title(); + const char* activatedText(); + const char* deactivatedText(); + FaultCode code; // Fehlercode + u16 id; // ID, mit der der Master den Alarm identifizieren kann + u16 counterActivated; // Wie oft der Regler seit der Alarm-Aktivierung gestartet/resetted wurde + u16 counterDeactivated; // Wie oft der Regler seit der Alarm-Deaktivierung gestartet/resetted wurde + u32 timeActivated; // millis() zum Zeitpunkt des Fehlers + u32 timeDeactivated = 0xFFFFFFFF; // millis(), als die Fehlerursache nicht mehr vorhanden war + bool timeActivatedOverflow = false; // Ob schon mehr als 0xFFFFFFFF (u32) ms vergangen sind + bool timeDeactivatedOverflow = false; // Ob schon mehr als 0xFFFFFFFF (u32) ms vergangen sind + bool active = true; // Ob der Fehler noch vorhanden ist +private: + bool _of1 = false; + bool _of2 = false; +}; + +struct FaultSettings +{ + uint8_t acoustic = 0xFF; + uint8_t optical = 0xFF; + uint8_t tOnIfError = 0xFF; + uint8_t pOnIfError = 0xFF; + int16_t tMinRRate = 0x8000; // Hundertstel-°C / 5 Minuten + int16_t tMinFRate = 0x8000; // Hundertstel-°C / 5 Minuten + int16_t pMinRate = 0x8000; // Hundertstel-Bar / 5 Minuten +}; + +class FaultList +{ +public: + FaultList(); + void process(); + bool check(FaultCode, int16_t val=0); + u8 count(); + Fault* first(); + Fault* last(); + Fault* at(const u8); + bool removeID(const u16); + bool removeOne(const u8); + bool removeAll(); + FaultState state(); + void t1Idle(); + void t1Idle(const int16_t); + void t2Idle(); + void t2Idle(const int16_t); + void pIdle(); + void pIdle(const int16_t); + const char* menu(); + bool tActive(); + bool pActive(); + +private: + Fault _list[_MAX_FAULTS]; + u8 _size = 0; + u16 _lastID; + FaultState _state = noFault; + FaultCode _lastActiveT1Fault = invalidCode; + FaultCode _lastActiveT2Fault = invalidCode; + FaultCode _lastActivePFault = invalidCode; + bool _t1Active = false; + bool _t2Active = false; + bool _pActive = false; + u32 _lastT1Idle; + u32 _lastT2Idle; + u32 _lastPIdle; + int16_t _compT1; + int16_t _compT2; + int16_t _compP; + String _str; + bool _removeAllActive = false; + bool _tStateBefore; + bool _pIncBefore; + bool _pDecBefore; + + void _save(const u8); + void _saveSize(); + void _saveLastID(); + void _test(Fault&, const bool&, const FaultCode&); + void _setModbusAlarm(const Fault&, const u8, const bool); +}; + +extern FaultList faults; +extern FaultSettings faultSettings; +// extern const u8 EEPROM_FAULTS[]; + +#endif // FAULTS_H_INCLUDED diff --git a/src/macros.h b/src/macros.h new file mode 100644 index 0000000..bdaa188 --- /dev/null +++ b/src/macros.h @@ -0,0 +1,250 @@ +#ifndef _MY_MACROS_H_INCLUDED +#define _MY_MACROS_H_INCLUDED + +#define _DEBUG 1 +#define _DEBUG_SENSORS 0 +#define _DEBUG_DISPLAY 0 +#define _DEBUG_MENU 0 +#define _DEBUG_MODBUS 0 +#define _MODBUS 1 +// Das Ermitteln einer Durchschnittstemperatur braucht (zu) viel RAM und ist wahrscheinlich unnötig +#define _MODBUS_AVERAGE_TEMPERATURES 0 + +#define _REGS_INFRONTOF_EVENTS 6 +// Ergibt sich aus 0xFF - 0x06, ev. wird +#define _MODBUS_MAX_EVENTS 249 + +#define _VERSION_MAJOR 0 +#define _VERSION_MINOR 0 +#define _VERSION_MICRO 1 +#define _VERSION_NUMBER _VERSION_MICRO | (_VERSION_MINOR << 6) | (_VERSION_MAJOR << 12) +#define _VERSION_STRING "v0.0.1" + +#define _MODBUS_ADDR_MIN 1 +#define _MODBUS_ADDR_MAX 247 // Laut QModMaster letzte Adresse +#define _MODBUS_DELAY_MIN 0 // 1/10 Millisekunden +#define _MODBUS_DELAY_MAX 250 // 1/10 Millisekunden + +#define _MODBUS_VALVE_T1_INDEX 0 +#define _MODBUS_VALVE_T2_INDEX 1 +#define _MODBUS_VALVE_PR_INDEX 2 +#define _MODBUS_VALVE_PF_INDEX 3 +#define _MODBUS_T1_INDEX 2 +#define _MODBUS_T2_INDEX 3 +#define _MODBUS_P_INDEX 4 + +#define _STD_TEMP_SETPOINT 400 // Hundertstel +#define _STD_TEMP_HYSTERESIS 10 // Hundertstel +#define _STD_P_SETPOINT 100 // Hundertstel +#define _STD_P_HYSTERESIS 5 // Hundertstel +#define _STD_P_EN_INC 1 +#define _STD_P_EN_DEC 1 +#define _STD_T_EN 1 +#define _STD_C_EN 0 +#define _MIN_TEMP_SETPOINT 10 // Hundertstel +#define _MAX_TEMP_SETPOINT 3000 // Hundertstel +#define _MIN_TEMP_HYSTERESIS 1 // Hundertstel +#define _MAX_TEMP_HYSTERESIS 100 // Hundertstel +#define _MIN_P_SETPOINT 0 // Hundertstel +#define _MAX_P_SETPOINT 300 // Hundertstel +#define _MIN_P_HYSTERESIS 1 // Hundertstel +#define _MAX_P_HYSTERESIS 50 // Hundertstel + +#define _P_SENSOR_1_5V_0_5BAR 1 +#define _P_SENSOR_0_5V_0_6BAR 2 + +#define _STD_FAULTS_ACOUSTIC 1 +#define _STD_FAULTS_OPTICAL 1 +#define _STD_FAULTS_T_ON 1 +#define _STD_FAULTS_P_ON 0 +#define _STD_FAULTS_T_R_RATE 10 // Hundertstel +#define _STD_FAULTS_T_F_RATE 10 // Hundertstel +#define _STD_FAULTS_P_RATE 10 // Hundertstel +#define _MIN_FAULTS_RATE 0 // Hundertstel +#define _MAX_FAULTS_RATE 1000 // Hundertstel + +// Alarme + +#define _MAX_FAULTS 30 + +// #define _CHECK_FAULT_INTERVAL 300000 // 5 Minuten +// #define _CHECK_FAULT_INTERVAL 3000 // 3 Sekunden (für Testzwecke) +// #define _CHECK_T_FAULT_INTERVAL 3600000 // 1 Stunde +// #define _CHECK_P_FAULT_INTERVAL 10000 // 10 Sekunden +#define _CHECK_T_FAULT_INTERVAL 3000 // 3 Sekunden (für Testzwecke) +#define _CHECK_P_FAULT_INTERVAL 3000 // 3 Sekunden (für Testzwecke) + +// EEPROM Adressen: + +#define _EEPROM_OFFSET 0 // Falls sich die Register nicht mehr beschreiben lassen +#define _EEPROM_MODBUS_ADDRESS (0 + _EEPROM_OFFSET) // 1 byte +#define _EEPROM_MODBUS_BAUDRATE (1 + _EEPROM_OFFSET) // 4 bytes +#define _EEPROM_MODBUS_DELAY (5 + _EEPROM_OFFSET) // 1 byte +#define _EEPROM_TEMP_SETPOINT (6 + _EEPROM_OFFSET) // 4 bytes +#define _EEPROM_TEMP_HYSTERESIS (10 + _EEPROM_OFFSET) // 4 bytes +#define _EEPROM_P_SETPOINT (14 + _EEPROM_OFFSET) // 4 bytes +#define _EEPROM_P_HYSTERESIS (18 + _EEPROM_OFFSET) // 4 bytes +#define _EEPROM_P_EN_INC (22 + _EEPROM_OFFSET) // 1 byte +#define _EEPROM_P_EN_DEC (23 + _EEPROM_OFFSET) // 1 byte +#define _EEPROM_P_SENSOR (24 + _EEPROM_OFFSET) // 1 byte +#define _EEPROM_BG_LIGHT (25 + _EEPROM_OFFSET) // 1 byte +#define _EEPROM_T_EN (26 + _EEPROM_OFFSET) // 1 byte +#define _EEPROM_CONTROL_EN (27 + _EEPROM_OFFSET) // 1 byte +// bisschen Reserveplatz +#define _EEPROM_POWERON_COUNT (48 + _EEPROM_OFFSET) // 2 bytes +#define _EEPROM_FAULT_SIZE 6 // 6 bytes für 1 Fehler +#define _EEPROM_FAULT_SETTINGS (50 + _EEPROM_OFFSET) // 20 bytes (incl. Reserve) +#define _EEPROM_FAULT_LAST_ID (70 + _EEPROM_OFFSET) // 2 byte +#define _EEPROM_FAULT_COUNT (72 + _EEPROM_OFFSET) // 1 byte +/* + * Bytes 1, 2: Alarm ID + * Bytes 3, 4: Bit 0...11: counterActivated + * Bytes 3, 4: Bit 12...15: Alarm-Code + * Bytes 5, 6: Bit 0...11: counterDeactivated + * Bytes 5, 6: Bit 12...13: active, deactivated + * Bytes 5, 6: Bit 14...15: reserviert + */ +#define _EEPROM_FAULT_1 (73 + _EEPROM_OFFSET) +#define _EEPROM_FAULT_2 (_EEPROM_FAULT_1 + _EEPROM_OFFSET + _EEPROM_FAULT_SIZE) +#define _EEPROM_FAULT_3 (_EEPROM_FAULT_2 + _EEPROM_OFFSET + _EEPROM_FAULT_SIZE) +#define _EEPROM_FAULT_4 (_EEPROM_FAULT_3 + _EEPROM_OFFSET + _EEPROM_FAULT_SIZE) +#define _EEPROM_FAULT_5 (_EEPROM_FAULT_4 + _EEPROM_OFFSET + _EEPROM_FAULT_SIZE) +#define _EEPROM_FAULT_6 (_EEPROM_FAULT_5 + _EEPROM_OFFSET + _EEPROM_FAULT_SIZE) +#define _EEPROM_FAULT_7 (_EEPROM_FAULT_6 + _EEPROM_OFFSET + _EEPROM_FAULT_SIZE) +#define _EEPROM_FAULT_8 (_EEPROM_FAULT_7 + _EEPROM_OFFSET + _EEPROM_FAULT_SIZE) +#define _EEPROM_FAULT_9 (_EEPROM_FAULT_8 + _EEPROM_OFFSET + _EEPROM_FAULT_SIZE) +#define _EEPROM_FAULT_10 (_EEPROM_FAULT_9 + _EEPROM_OFFSET + _EEPROM_FAULT_SIZE) +#define _EEPROM_FAULT_11 (_EEPROM_FAULT_10 + _EEPROM_OFFSET + _EEPROM_FAULT_SIZE) +#define _EEPROM_FAULT_12 (_EEPROM_FAULT_11 + _EEPROM_OFFSET + _EEPROM_FAULT_SIZE) +#define _EEPROM_FAULT_13 (_EEPROM_FAULT_12 + _EEPROM_OFFSET + _EEPROM_FAULT_SIZE) +#define _EEPROM_FAULT_14 (_EEPROM_FAULT_13 + _EEPROM_OFFSET + _EEPROM_FAULT_SIZE) +#define _EEPROM_FAULT_15 (_EEPROM_FAULT_14 + _EEPROM_OFFSET + _EEPROM_FAULT_SIZE) +#define _EEPROM_FAULT_16 (_EEPROM_FAULT_15 + _EEPROM_OFFSET + _EEPROM_FAULT_SIZE) +#define _EEPROM_FAULT_17 (_EEPROM_FAULT_16 + _EEPROM_OFFSET + _EEPROM_FAULT_SIZE) +#define _EEPROM_FAULT_18 (_EEPROM_FAULT_17 + _EEPROM_OFFSET + _EEPROM_FAULT_SIZE) +#define _EEPROM_FAULT_19 (_EEPROM_FAULT_18 + _EEPROM_OFFSET + _EEPROM_FAULT_SIZE) +#define _EEPROM_FAULT_20 (_EEPROM_FAULT_19 + _EEPROM_OFFSET + _EEPROM_FAULT_SIZE) +#define _EEPROM_FAULT_21 (_EEPROM_FAULT_20 + _EEPROM_OFFSET + _EEPROM_FAULT_SIZE) +#define _EEPROM_FAULT_22 (_EEPROM_FAULT_21 + _EEPROM_OFFSET + _EEPROM_FAULT_SIZE) +#define _EEPROM_FAULT_23 (_EEPROM_FAULT_22 + _EEPROM_OFFSET + _EEPROM_FAULT_SIZE) +#define _EEPROM_FAULT_24 (_EEPROM_FAULT_23 + _EEPROM_OFFSET + _EEPROM_FAULT_SIZE) +#define _EEPROM_FAULT_25 (_EEPROM_FAULT_24 + _EEPROM_OFFSET + _EEPROM_FAULT_SIZE) +#define _EEPROM_FAULT_26 (_EEPROM_FAULT_25 + _EEPROM_OFFSET + _EEPROM_FAULT_SIZE) +#define _EEPROM_FAULT_27 (_EEPROM_FAULT_26 + _EEPROM_OFFSET + _EEPROM_FAULT_SIZE) +#define _EEPROM_FAULT_28 (_EEPROM_FAULT_27 + _EEPROM_OFFSET + _EEPROM_FAULT_SIZE) +#define _EEPROM_FAULT_29 (_EEPROM_FAULT_28 + _EEPROM_OFFSET + _EEPROM_FAULT_SIZE) +#define _EEPROM_FAULT_30 (_EEPROM_FAULT_29 + _EEPROM_OFFSET + _EEPROM_FAULT_SIZE) +// bisschen Reserveplatz +#define _EEPROM_FAULTS_ACOUSTIC 300 // 1 byte +#define _EEPROM_FAULTS_OPTICAL 301 // 1 byte +#define _EEPROM_FAULTS_T_ON 302 // 1 byte +#define _EEPROM_FAULTS_P_ON 303 // 1 byte +#define _EEPROM_FAULTS_T_R_RATE 304 // 2 bytes +#define _EEPROM_FAULTS_T_F_RATE 306 // 2 bytes +#define _EEPROM_FAULTS_P_RATE 308 // 2 bytes + +extern u32 currMillis; + +#if _DEBUG == 1 +#define _print(s) Serial.print(s) +#define _println(s) Serial.println(s) +#define _printf(s) Serial.print(F(s)) +#define _printlnf(s) Serial.println(F(s)) +#if _DEBUG_DISPLAY == 1 +struct __printTimeStruct { + __printTimeStruct(const char *text) : _text(text) { + _t0 = currMillis; + }; + ~__printTimeStruct() { + _printf("Vergangene Millisekunden ["); _print(_text); + _printf("]: "); _println(currMillis - _t0); + }; + private: + u32 _t0; + const char *_text; +}; +#define _printtime(text) __printTimeStruct __printt(text) +#else +#define _printtime(text) +#endif // _DEBUG_DISPLAY +#if _DEBUG_SENSORS == 1 +#define _prints(s) _print(s) +#define _printsln(s) _println(s) +#define _printsf(s) _printf(s) +#define _printslnf(s) _printlnf(s) +#else +#define _prints(s) +#define _printsln(s) +#define _printsf(s) +#define _printslnf(s) +#endif // _DEBUG_SENSORS +#if _DEBUG_MODBUS == 1 +#define _printb(s) _print(s) +#define _printbln(s) _println(s) +#define _printbf(s) _printf(s) +#define _printblnf(s) _printlnf(s) +#else +#define _printb(s) +#define _printbln(s) +#define _printbf(s) +#define _printblnf(s) +#endif // _DEBUG_MODBUS +#if _DEBUG_MENU == 1 +#define _printm(s) _print(s) +#define _printmln(s) _println(s) +#define _printmf(s) _printf(s) +#define _printmlnf(s) _printlnf(s) +#else +#define _printm(s) +#define _printmln(s) +#define _printmf(s) +#define _printmlnf(s) +#endif // _DEBUG_MENU +#else // _DEBUG != 1 +#define _print(s) +#define _println(s) +#define _printf(s) +#define _printlnf(s) +#define _prints(s) +#define _printsln(s) +#define _printsf(s) +#define _printslnf(s) +#define _printb(s) +#define _printbln(s) +#define _printbf(s) +#define _printblnf(s) +#define _printm(s) +#define _printmln(s) +#define _printmf(s) +#define _printmlnf(s) +#define _printtime(s) +#endif + +#if _MODBUS == 1 +extern bool timeStampOverflow; +extern unsigned long timeStamp; +extern u8 modbusValves[]; +extern u16 &eventCounter; +extern u16 modbusData[]; +#define _setModbusValve(index, val)\ + if (bitRead(modbusValves[0], index) != val) {\ + bitWrite(modbusValves[0], index, val);\ + bitWrite(modbusData[1], index+8, val);\ + if (eventCounter <= _MODBUS_MAX_EVENTS) {\ + /* Die ersten 12 Bits beinhalten den relativen Zeitstempel in Zehntelsekunden */\ + /* Dieser Stempel ist immer 0xFFF, falls schon zuviel Zeit seit der letzten Referenzierung vergangen ist */\ + /* u16 result = (timeStampOverflow) ? 0xFFF : ((currMillis - timeStamp) / 100) & 0xFFF;*/\ + u16 result = (timeStampOverflow) ? 0xFFF : (modbusData[5] & 0xFFF);\ + /* Die Bits 12...14 beinhalten die Nr des geschalteten Ausgangs */\ + result |= index << 12;\ + /* Das MSB gibt an, ob der Ausgang ein- oder ausgeschaltet wurde */\ + result |= val << 15;\ + modbusData[_REGS_INFRONTOF_EVENTS + eventCounter] = result;\ + eventCounter++;\ + }\ + } +#else +#define _setModbusValve(index, val) +#endif // _MODBUS == 1 + +#endif // _MY_MACROS_H_INCLUDED diff --git a/src/modbus/ModbusRTUSlave.cpp b/src/modbus/ModbusRTUSlave.cpp index 7928872..49252a1 100644 --- a/src/modbus/ModbusRTUSlave.cpp +++ b/src/modbus/ModbusRTUSlave.cpp @@ -74,6 +74,11 @@ bool ModbusRTUSlave::addInputRegisterArea(u16 address, u16* values, int cnt) return false; } +// bool ModbusRTUSlave::addValidator(u16 reg, u16 min, u16 max, u16 step=5) +// { +// return validator.add(reg, min, max, step); +// } + ModbusRTUSlaveWordAddress* ModbusRTUSlave::getWordAddress(wordsList *words, u16 Addr) { ModbusRTUSlaveWordAddress* ret=NULL; @@ -170,11 +175,13 @@ void ModbusRTUSlave::process() { u16 stidx = (requestedRegister - a->addr) / 8; - bitWrite(a->values[stidx], (requestedRegister - a->addr)%8, Data==0xFF00); + bool b = Data==0xFF00; + callbackCoil(requestedRegister, b); + bitWrite(a->values[stidx], (requestedRegister - a->addr)%8, b); byte ret[8]; - ret[0]=requestedSlave; - ret[1]=fnCode; + ret[0]=requestedSlave; + ret[1]=fnCode; ret[2]=((requestedRegister&0xFF00)>>8); ret[3]=((requestedRegister&0x00FF)); ret[4]=((Data&0xFF00)>>8); @@ -201,7 +208,7 @@ void ModbusRTUSlave::process() { u16 stidx = requestedRegister - a->addr; - a->values[stidx] = Data; + a->values[stidx] = callbackRegister(requestedRegister, Data); byte ret[8]; ret[0]=requestedSlave; @@ -235,6 +242,8 @@ void ModbusRTUSlave::process() ModbusRTUSlaveBitAddress *a = getBitAddress(rwBits, requestedRegister, Length); if (a != NULL) { + bool b; + u16 offset = 0; u16 stidx = (requestedRegister - a->addr) / 8; int ng=(requestedRegister - a->addr) % 8; int ns=stidx; @@ -244,8 +253,11 @@ void ModbusRTUSlave::process() byte val = lstResponse[i]; for(int j=0;j<8;j++) { - bitWrite(a->values[ns], ng++, bitRead(val,j)); + b = bitRead(val,j); + callbackCoil(requestedRegister + offset, b); + bitWrite(a->values[ns], ng++, b); if(ng==8){ns++;ng=0;} + offset++; } } @@ -285,7 +297,7 @@ void ModbusRTUSlave::process() { for(int i=7; i<7+ByteCount;i+=2) { - u16 data = lstResponse[i] << 8 | lstResponse[i+1]; + u16 data = callbackRegister(requestedRegister, lstResponse[i] << 8 | lstResponse[i+1]); ModbusRTUSlaveWordAddress *a = getWordAddress(rwWords, requestedRegister + ((i-7)/2)); if (a != NULL) { a->values[(requestedRegister + ((i-7)/2)) - a->addr] = data; } else { bvalid=false; break; } diff --git a/src/modbus/ModbusRTUSlave.h b/src/modbus/ModbusRTUSlave.h index d25a206..d79079b 100644 --- a/src/modbus/ModbusRTUSlave.h +++ b/src/modbus/ModbusRTUSlave.h @@ -4,22 +4,20 @@ #include "Arduino.h" #include "utility/LinkedList.h" -class ModbusRTUSlaveWordAddress +struct ModbusRTUSlaveWordAddress { - public : - u16 addr; - byte len; - u16 *values; - ModbusRTUSlaveWordAddress(u16 Address, u16* value, int cnt); + u16 addr; + byte len; + u16 *values; + ModbusRTUSlaveWordAddress(u16 Address, u16* value, int cnt); }; -class ModbusRTUSlaveBitAddress +struct ModbusRTUSlaveBitAddress { - public : - u16 addr; - byte len; - u8 *values; - ModbusRTUSlaveBitAddress(u16 Address, u8* value, int cnt); + u16 addr; + byte len; + u8 *values; + ModbusRTUSlaveBitAddress(u16 Address, u8* value, int cnt); }; using wordsList = LinkedList; @@ -38,6 +36,9 @@ class ModbusRTUSlave void process(); unsigned long waitWithAnswerMicroS = 0; + u16 (*callbackRegister)(u16 reg, u16 val) = [](u16, u16 val){return val;}; + void (*callbackCoil)(u16 reg, bool val); + private: byte slaveAddress; byte requestedSlave; @@ -48,8 +49,6 @@ class ModbusRTUSlave bool isReading; unsigned long receivedLastRequest; - // LinkedList *rWords; - // LinkedList *rBits; wordsList *rWords; bitsList *rBits; wordsList *rwWords; diff --git a/src/modbus/utility/HashMap.h b/src/modbus/utility/HashMap.h deleted file mode 100644 index 327be8a..0000000 --- a/src/modbus/utility/HashMap.h +++ /dev/null @@ -1,112 +0,0 @@ -/* -|| -|| @file HashMap.h -|| @version 1.0 Beta -|| @author Alexander Brevig -|| @contact alexanderbrevig@gmail.com -|| -|| @description -|| | This library provides a simple interface for storing data with an associate key -|| # -|| -|| @license -|| | This library is free software; you can redistribute it and/or -|| | modify it under the terms of the GNU Lesser General Public -|| | License as published by the Free Software Foundation; version -|| | 2.1 of the License. -|| | -|| | This library is distributed in the hope that it will be useful, -|| | but WITHOUT ANY WARRANTY; without even the implied warranty of -|| | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -|| | Lesser General Public License for more details. -|| | -|| | You should have received a copy of the GNU Lesser General Public -|| | License along with this library; if not, write to the Free Software -|| | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -|| # -|| -*/ - -#ifndef HASHMAP_H -#define HASHMAP_H - -#include "Arduino.h" - -/* Handle association */ -template -class HashType { -public: - HashType(){ reset(); } - - HashType(hash code,map value):hashCode(code),mappedValue(value){} - - void reset(){ hashCode = 0; mappedValue = 0; } - hash getHash(){ return hashCode; } - void setHash(hash code){ hashCode = code; } - map getValue(){ return mappedValue; } - void setValue(map value){ mappedValue = value; } - - HashType& operator()(hash code, map value){ - setHash( code ); - setValue( value ); - } -private: - hash hashCode; - map mappedValue; -}; - -/* -Handle indexing and searches -TODO - extend API -*/ -template -class HashMap { -public: - HashMap(HashType* newMap,byte newSize){ - hashMap = newMap; - size = newSize; - for (byte i=0; i& operator[](int x){ - //TODO - bounds - return hashMap[x]; - } - - byte getIndexOf( hash key ){ - for (byte i=0; i* hashMap; - byte size; -}; - -#endif - -/* -|| @changelog -|| | 1.0 2009-07-13 - Alexander Brevig : Initial Release -|| # -*/ \ No newline at end of file diff --git a/src/modbus/utility/HashMap.h.bak b/src/modbus/utility/HashMap.h.bak deleted file mode 100644 index c2fa631..0000000 --- a/src/modbus/utility/HashMap.h.bak +++ /dev/null @@ -1,113 +0,0 @@ -/* -|| -|| @file HashMap.h -|| @version 1.0 Beta -|| @author Alexander Brevig -|| @contact alexanderbrevig@gmail.com -|| -|| @description -|| | This library provides a simple interface for storing data with an associate key -|| # -|| -|| @license -|| | This library is free software; you can redistribute it and/or -|| | modify it under the terms of the GNU Lesser General Public -|| | License as published by the Free Software Foundation; version -|| | 2.1 of the License. -|| | -|| | This library is distributed in the hope that it will be useful, -|| | but WITHOUT ANY WARRANTY; without even the implied warranty of -|| | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -|| | Lesser General Public License for more details. -|| | -|| | You should have received a copy of the GNU Lesser General Public -|| | License along with this library; if not, write to the Free Software -|| | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -|| # -|| -*/ - -#ifndef HASHMAP_H -#define HASHMAP_H - -#include "Arduino.h" - -/* Handle association */ -template -class HashType { -public: - HashType(){ reset(); } - - HashType(hash code,map value):hashCode(code),mappedValue(value){} - - void reset(){ hashCode = 0; mappedValue = 0; } - hash getHash(){ return hashCode; } - void setHash(hash code){ hashCode = code; } - map getValue(){ return mappedValue; } - void setValue(map value){ mappedValue = value; } - - HashType& operator()(hash code, map value){ - setHash( code ); - setValue( value ); - } -private: - hash hashCode; - map mappedValue; -}; - -/* -Handle indexing and searches -TODO - extend API -*/ -template - -class HashMap { -public: - HashMap(HashType* newMap,byte newSize){ - hashMap = newMap; - size = newSize; - for (byte i=0; i& operator[](int x){ - //TODO - bounds - return hashMap[x]; - } - - byte getIndexOf( hash key ){ - for (byte i=0; i* hashMap; - byte size; -}; - -#endif - -/* -|| @changelog -|| | 1.0 2009-07-13 - Alexander Brevig : Initial Release -|| # -*/ \ No newline at end of file diff --git a/src/mystrings/mystrings.cpp b/src/mystrings/mystrings.cpp new file mode 100644 index 0000000..1bdcd37 --- /dev/null +++ b/src/mystrings/mystrings.cpp @@ -0,0 +1,31 @@ +#include "mystrings.h" + +char charBuffer200[200]; +char charBuffer501[50]; +char charBuffer502[50]; +char charBuffer503[50]; +// char charBuffer[50]; + +char* getMyStr200(const char* str) +{ + strcpy_P(charBuffer200, str); + return charBuffer200; +} + +char* getMyStr501(const char* str) +{ + strcpy_P(charBuffer501, str); + return charBuffer501; +} + +char* getMyStr502(const char* str) +{ + strcpy_P(charBuffer502, str); + return charBuffer502; +} + +char* getMyStr503(const char* str) +{ + strcpy_P(charBuffer503, str); + return charBuffer503; +} diff --git a/src/mystrings/mystrings.h b/src/mystrings/mystrings.h new file mode 100644 index 0000000..aa70a10 --- /dev/null +++ b/src/mystrings/mystrings.h @@ -0,0 +1,125 @@ +#ifndef _MY_STRINGS_H_INCLUDED +#define _MY_STRINGS_H_INCLUDED + +#include + +const char STR_MENU_MAIN[] PROGMEM = "Temperatur\nDruck\nFehlerbehandlung\nSystem\nReset"; +const char STR_MENU_MAIN_TITLE[] PROGMEM = "Hauptmenü (%s)"; + +const char STR_MENU_TEMPERATURE[] PROGMEM = "Kühlung de/aktivieren\nSollwert\nHysterese"; +const char STR_TEMPERATURE[] PROGMEM = "Temperatur"; +const char STR_COOLING[] PROGMEM = "Kühlung"; +const char STR_T_SETP[] PROGMEM = "Temperatur\nSollwert"; +const char STR_T_HYST[] PROGMEM = "Temperatur\nHysterese"; + +const char STR_MENU_PRESSURE[] PROGMEM = "Drucksteigerung\nDruckabfall\nSollwert\nHysterese"; +const char STR_P[] PROGMEM = "Druck"; +const char STR_P_INC[] PROGMEM = "Drucksteigerung"; +const char STR_P_DEC[] PROGMEM = "Druckabfall"; +const char STR_P_SETP[] PROGMEM = "Druck\nSollwert"; +const char STR_P_HYST[] PROGMEM = "Druck\nHysterese"; + +const char STR_F_ERR_TITLE[] PROGMEM = "Fehler (%u)"; +const char STR_MENU_ERR_HANDLING[] PROGMEM = "offene Fehler\nalle löschen\nEinstellungen"; +const char STR_CURR_ERRS[] PROGMEM = "aktuelle Fehler"; +const char STR_MENU_FAULT_SETTINGS_TITLE[] PROGMEM = "Fehler-Settings"; +const char STR_MENU_FAULT_SETTINGS[] PROGMEM = "akustisches Signal\noptisches Signal\nTempregelung\nDruckregelung\nTemp+ Rate\nTemp- Rate\nDruckrate"; +const char STR_FAULTS_ACOUSTIC[] PROGMEM = "Summer zur\nFehlersignalisierung\nverwenden?"; +const char STR_FAULTS_OPTICAL[] PROGMEM = "Displaybeleuchtung\nim Fehlerfall\npulsieren lassen?"; +const char STR_FAULTS_T_OFF[] PROGMEM = "Temperatur im\nFehlerfall weiter\nregeln?"; +const char STR_FAULTS_P_OFF[] PROGMEM = "Druck im\nFehlerfall weiter\nregeln?"; +const char STR_FAULTS_T_R_RATE[] PROGMEM = "Min. Steigerungsrate\nfür Temperatur"; +const char STR_FAULTS_T_F_RATE[] PROGMEM = "Min. Abfallrate\nfür Temperatur"; +const char STR_FAULTS_P_RATE[] PROGMEM = "Min. Änderungsrate\nfür Druck"; + +const char STR_ADMIN_AUTH[] PROGMEM = "Systemeinstellungen:\nBerechtigung\nnachweisen:"; +const char STR_SYSTEM[] PROGMEM = "System"; +const char STR_MENU_SYSTEM[] PROGMEM = "Modbus\nDrucksensor"; +const char STR_MODBUS[] PROGMEM = "Modbus"; +const char STR_MENU_MODBUS[] PROGMEM = "Adresse\nBaudrate\nAntwort-Delay"; +const char STR_BAUDR[] PROGMEM = "Baudrate"; +const char STR_MENU_BAUDR[] PROGMEM = "300\n1200\n2400\n4800\n9600\n19200\n38400\n57600\n115200"; +const char STR_SEL_MODB_ADDR[] PROGMEM = "Modbus Adresse"; +const char STR_SEL_MODB_ADDR_F[] PROGMEM = "Dezimal: %d"; +const char STR_SEL_MODB_DELAY[] PROGMEM = "Modbus\nAntwort-Verzögerung\nin Millisekunden"; +const char STR_SEL_MODB_DELAY_F[] PROGMEM = "%d.%.d"; +const char STR_P_SENSOR[] PROGMEM = "Drucksensor"; +const char STR_MENU_P_SENSOR[] PROGMEM = "SMC: 1-5V,0-5 bar\nGems: 0-5V,0-6 bar"; +const char STR_RESTART_CONTROLLER[] PROGMEM = "Controller neustarten"; +const char STR_20_HYPHENS[] PROGMEM = "--------------------"; +const char STR_REALLY_RESTART[] PROGMEM = "Wirklich neustarten?"; + +const char STR_NO_YES[] PROGMEM = " nein \n ja "; +//const char STR_YES_NO[] PROGMEM = " ja \n nein "; +const char STR_ACTIVATE[] PROGMEM = "aktivieren"; +const char STR__FLOAT__[] PROGMEM = "__float__"; +const char STR_OK[] PROGMEM = " OK "; + +const char STR_IN[] PROGMEM = "in"; +const char STR_ON[] PROGMEM = "ein"; +const char STR_OFF[] PROGMEM = "aus"; +const char STR_HOME_1[] PROGMEM = "--.-- \xb0\C"; +const char STR_HOME_2[] PROGMEM = "%d.%.2d \xb0\C"; +const char STR_HOME_3[] PROGMEM = "--.-- bar"; +const char STR_HOME_4[] PROGMEM = "%d.%.2d bar"; +const char STR_HOME_5[] PROGMEM = "%d.%.2d+-%d.%.2d Regelung %saktiv"; +const char STR_HOME_6[] PROGMEM = "%d.%.2d+-%d.%.2d P+ %s P- %s"; +const char STR_HOME_7[] PROGMEM = "Modbus Adr. %u, Baudr. %s"; + +const char STR_1W_ERR[] PROGMEM = "Fehler OneWire"; +const char STR_1W_F1[] PROGMEM = "%u (max: %u)"; +const char STR_1W_1[] PROGMEM = "OneWire-Sensoren"; +const char STR_1W_2[] PROGMEM = "Erfolgreich erkannt:"; +const char STR_1W_3[] PROGMEM = "Zu viele Sensoren:"; +const char STR_1W_4[] PROGMEM = "Keine Sensoren"; +const char STR_1W_5[] PROGMEM = "erkannt"; + +const char STR_F_DEC_0[] PROGMEM = "%d"; +const char STR_F_DEC_1[] PROGMEM = "%d.%.1d"; +const char STR_F_DEC_2[] PROGMEM = "%d.%.2d"; + +const char STR_F_S_MIN_MAX[] PROGMEM = "min: %s, max: %s"; + +const char STR_T_FAIL[] PROGMEM = "Fehler Tempsensor"; +const char STR_T_TOO_HI[] PROGMEM = "Temp. zu hoch"; +const char STR_T_TOO_LO[] PROGMEM = "Temp. zu niedrig"; +const char STR_T1_TOO_HI[] PROGMEM = "Temp. 1 zu hoch"; +const char STR_T1_TOO_LO[] PROGMEM = "Temp. 1 zu niedrig"; +const char STR_T2_TOO_HI[] PROGMEM = "Temp. 2 zu hoch"; +const char STR_T2_TOO_LO[] PROGMEM = "Temp. 2 zu niedrig"; +const char STR_P_FAIL[] PROGMEM = "Fehler Drucksensor"; +const char STR_P_TOO_HI[] PROGMEM = "Druck zu hoch"; +const char STR_P_TOO_LO[] PROGMEM = "Druck zu niedrig"; +const char STR_UNKNOWN_ERROR[] PROGMEM = "unbekannter Fehler"; +const char STR_NO_ERROR[] PROGMEM = "keine Fehler :-)"; + +const char STR_NEVER_DEACTIVATED_ACTIVE[] PROGMEM = "~ nie (aktiv)"; +const char STR_NEVER_DEACTIVATED_INACTIVE[] PROGMEM = "~ nie (inaktiv)"; +const char STR_GT_7_WEEKS_1[] PROGMEM = "* > 7 Wochen"; +const char STR_F_SECONDS_1[] PROGMEM = "* %u Sekunden"; +const char STR_F_MINUTES_1[] PROGMEM = "* %u Minuten"; +const char STR_F_HOURS_1[] PROGMEM = "* %u Stunden"; +const char STR_F_DAYS_1[] PROGMEM = "* %u Tage"; +const char STR_GT_7_WEEKS_2[] PROGMEM = "~ > 7 Wochen"; +const char STR_F_SECONDS_2[] PROGMEM = "~ %u Sekunden"; +const char STR_F_MINUTES_2[] PROGMEM = "~ %u Minuten"; +const char STR_F_HOURS_2[] PROGMEM = "~ %u Stunden"; +const char STR_F_DAYS_2[] PROGMEM = "~ %u Tage"; +const char STR_UNKNOWN_ACTIVATED[] PROGMEM = "* ? (vor %u Resets)"; +const char STR_UNKNOWN_DEACTIVAED[] PROGMEM = "~ ? (vor %u Resets)"; + +const char STR_DEL_ALL_INACTIVE_FAULTS[] PROGMEM = "Alle nicht\naktiven Fehler\nlöschen?"; +const char STR_BACK_DEL[] PROGMEM = " zurück \n löschen "; +const char STR_BACK[] PROGMEM = " zurück "; + +extern char charBuffer200[]; +extern char charBuffer501[]; +extern char charBuffer502[]; +extern char charBuffer503[]; +// extern char charBuffer[]; +extern char* getMyStr200(const char* str); +extern char* getMyStr501(const char* str); +extern char* getMyStr502(const char* str); +extern char* getMyStr503(const char* str); + +#endif // _MY_STRINGS_H_INCLUDED diff --git a/src/types.h b/src/types.h new file mode 100644 index 0000000..57218ad --- /dev/null +++ b/src/types.h @@ -0,0 +1,50 @@ +#ifndef _MYTYPES_H_INCLUDED +#define _MYTYPES_H_INCLUDED + +#define _SENSOR_FAULT 0x8000 +#define _MODBUS_INVALID_BAUDRATE 4294967295 + +enum PSensor : uint8_t { + SMC_1_5V_0_5BAR=1, // Sensor mit Kabel + GEMS_0_5V_0_6BAR // Sensor mit Würfelstecker +}; + +struct Parameters { // Prozesswerte + int16_t ts1 = _SENSOR_FAULT; // Soll-Temperatur 1 in 1/100 °C + int16_t th1 = _SENSOR_FAULT; // Hysterese für Temperatur 1 (1/100) + int16_t ts2 = _SENSOR_FAULT; // Soll-Temperatur 2 in 1/100 °C + int16_t th2 = _SENSOR_FAULT; // Hysterese für Temperatur 2 (1/100) + int16_t ps = _SENSOR_FAULT; // Druck in bar + int16_t ph = _SENSOR_FAULT; // Hysterese für Druck + uint8_t tEn = 255; // Kühlung (de)aktiviert + uint8_t pInc = 255; // Drucksteigerung (de)aktiviert + uint8_t pDec = 255; // Druckabfall (de)aktiviert + uint8_t cEn = 255; // Controller (de)aktiviert +}; + +struct Values { // aktuelle Messwerte + int16_t t1 = _SENSOR_FAULT; // Temperatur in 1/100 °C + int16_t t2 = _SENSOR_FAULT; // Temperatur in 1/100 °C + int16_t p = _SENSOR_FAULT; // Druck in 1/100 bar +}; + +struct ModbusParameters { // Parameter für Modbus + uint32_t baudrate = _MODBUS_INVALID_BAUDRATE; // Modbus-Baudrate + uint8_t address = 255; // Modbus-Adresse + uint8_t delay = 255; // delay in 1/10 ms vor der Antwort +}; + +struct ValveStates { + bool t1; + bool t2; + bool pInc; + bool pDec; +}; + +extern PSensor pSensor; +extern Parameters params; +extern ModbusParameters modbusParams; +extern Values vals; +extern ValveStates vStates; + +#endif // _MYTYPES_H_INCLUDED \ No newline at end of file