#ifndef ARDUINO_AVR_MEGA2560 #error This program was written for an Arduino Mega (Atmega2560) #endif #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 PSensor pSensor; // verwendeter Drucksensor valveStates vStates; // Zustände der Ausgänge bool paramsChangedByUI; // true, außer ein Wert wurde über Modbus geändert 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); } #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); } 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() { #if _DEBUG == 1 Serial.begin(115200); #endif pinMode(53, OUTPUT); // Mega CS-Pin (um Slave-Betrieb zu vermeiden) paramsChangedByUI = true; d.init(); d.bgLight(true); d.setModbusParams = setModbusParams; d.setPSensor = setPSensor; d.setParams = setParams; getModbusParams(); _print("ModbusAddress: "); _println(modbusParams.address); _print("ModbusBaudrate: "); _println(modbusParams.baudrate); getPSensor(); getParams(); pinMode(BTN_PWR, INPUT_PULLUP); #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(); #endif d.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); else d.bgLight(100); setParams(p); } c.process(); d.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 == #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++; } #endif }