DreherTankController/DreherTankController.ino

516 lines
17 KiB
C++

#ifndef ARDUINO_AVR_MEGA2560
#error This program was written for an Arduino Mega (Atmega2560)
#endif
#include <EEPROM.h>
#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(&params.ts1, (int16_t*)&modbusSetpoints[0], _STD_TEMP_SETPOINT, _MIN_TEMP_SETPOINT, _MAX_TEMP_SETPOINT);
} else {
_print("ts1 - Modbus -> UI - ");
checkParamINT16((int16_t*)&modbusSetpoints[0], &params.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(&params.th1, (int16_t*)&modbusSetpoints[1], _STD_TEMP_HYSTERESIS, _MIN_TEMP_HYSTERESIS, _MAX_TEMP_HYSTERESIS);
} else {
_print("th1 - Modbus -> UI - ");
checkParamINT16((int16_t*)&modbusSetpoints[1], &params.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(&params.ps, (int16_t*)&modbusSetpoints[4], _STD_P_SETPOINT, _MIN_P_SETPOINT, _MAX_P_SETPOINT);
} else {
_print("ps - Modbus -> UI - ");
checkParamINT16((int16_t*)&modbusSetpoints[4], &params.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(&params.ph, (int16_t*)&modbusSetpoints[5], _STD_P_HYSTERESIS, _MIN_P_HYSTERESIS, _MAX_P_HYSTERESIS);
} else {
_print("ph - Modbus -> UI - ");
checkParamINT16((int16_t*)&modbusSetpoints[5], &params.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, &params.tEn);
}
bitNr++;
if (bitRead(modbusStates[0], bitNr) != params.pInc) {
_print("pInc - ");
checkParamBool(p.pInc, bitNr, &params.pInc);
}
bitNr++;
if (bitRead(modbusStates[0], bitNr) != params.pDec) {
_print("pDec - ");
checkParamBool(p.pDec, bitNr, &params.pDec);
}
bitNr++;
if (bitRead(modbusStates[0], bitNr) != params.cEn) {
_print("cEn - ");
checkParamBool(p.cEn, bitNr, &params.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, &params, &vals, &modbusParams, &pSensor, &vStates);
// Analogeingang Druck, OneWire-Pin, Parameter, Messwerte, Display, Drucksensor, Ausgangszustände
// nach vStates: t1Pin, t2Pin, pRisePin, pFallPin
Controller c(A0, 8, &params, &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, &params.ts1, _STD_TEMP_SETPOINT, _MIN_TEMP_SETPOINT, _MAX_TEMP_SETPOINT);
_print("th1 - UI -> Modbus - ");
checkParamINT16(&p.th1, &params.th1, _STD_TEMP_HYSTERESIS, _MIN_TEMP_HYSTERESIS, _MAX_TEMP_HYSTERESIS);
_print("ps - UI -> Modbus - ");
checkParamINT16(&p.ps, &params.ps, _STD_P_SETPOINT, _MIN_P_SETPOINT, _MAX_P_SETPOINT);
_print("ph - UI -> Modbus - ");
checkParamINT16(&p.ph, &params.ph, _STD_P_HYSTERESIS, _MIN_P_HYSTERESIS, _MAX_P_HYSTERESIS);
_print("tEn - ");
checkParamUINT8(&p.tEn, &params.tEn, _STD_T_EN);
_print("pInc - ");
checkParamUINT8(&p.pInc, &params.pInc, _STD_P_EN_INC);
_print("pDec - ");
checkParamUINT8(&p.pDec, &params.pDec, _STD_P_EN_DEC);
_print("cEn - ");
checkParamUINT8(&p.cEn, &params.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, &params.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, &params.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, &params.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, &params.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
}