codebase heavily refactored

This commit is contained in:
Bruno Hütter 2021-01-27 15:19:56 +01:00
parent 636ec4df53
commit cbb96f3015
18 changed files with 3007 additions and 1261 deletions

View File

@ -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 <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
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(&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);
}
// 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, &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() {
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
}

119
README.md
View File

@ -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.

614
src/common.cpp Normal file
View File

@ -0,0 +1,614 @@
#include "common.h"
#include <EEPROM.h>
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, &params.ts1, _STD_TEMP_SETPOINT, _MIN_TEMP_SETPOINT, _MAX_TEMP_SETPOINT);
modbusHolding[1] = params.ts1;
_printf("th1 - UI -> Modbus - ");
checkParamINT16(&p.th1, &params.th1, _STD_TEMP_HYSTERESIS, _MIN_TEMP_HYSTERESIS, _MAX_TEMP_HYSTERESIS);
modbusHolding[2] = params.th1;
_printf("ps - UI -> Modbus - ");
checkParamINT16(&p.ps, &params.ps, _STD_P_SETPOINT, _MIN_P_SETPOINT, _MAX_P_SETPOINT);
modbusHolding[5] = params.ps;
_printf("ph - UI -> Modbus - ");
checkParamINT16(&p.ph, &params.ph, _STD_P_HYSTERESIS, _MIN_P_HYSTERESIS, _MAX_P_HYSTERESIS);
modbusHolding[6] = params.ph;
_printf("tEn - ");
checkParamUINT8(&p.tEn, &params.tEn, _STD_T_EN);
setBitInRegisters(0, params.tEn);
_printf("pInc - ");
checkParamUINT8(&p.pInc, &params.pInc, _STD_P_EN_INC);
setBitInRegisters(1, params.pInc);
_printf("pDec - ");
checkParamUINT8(&p.pDec, &params.pDec, _STD_P_EN_DEC);
setBitInRegisters(2, params.pDec);
_printf("cEn - ");
checkParamUINT8(&p.cEn, &params.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, &params.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, &params.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, &params.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, &params.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;
}

View File

@ -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

View File

@ -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<loopTill; i++) {
@ -274,33 +296,113 @@ inline void Controller::_averageTemperature2()
// noch nicht implementiert
}
#endif // _MODBUS_AVERAGE_TEMPERATURES == 1
#endif // _MODBUS == 1
inline void Controller::_averagePressure()
{
// Falls das Ende des Arrays erreicht ist, wird es wieder vom Anfang befüllt
if (_p_i_curr > _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;
}

View File

@ -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

File diff suppressed because it is too large Load Diff

View File

@ -2,7 +2,9 @@
#define MY_DISPLAY_H_INCLUDED
#include <U8g2lib.h>
#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

683
src/faults/faults.cpp Normal file
View File

@ -0,0 +1,683 @@
#include "faults.h"
#include "../types.h"
#include "../mystrings/mystrings.h"
#include "../macros.h"
#include <EEPROM.h>
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;

124
src/faults/faults.h Normal file
View File

@ -0,0 +1,124 @@
#ifndef FAULTS_H_INCLUDED
#define FAULTS_H_INCLUDED
#include <Arduino.h>
#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

250
src/macros.h Normal file
View File

@ -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

View File

@ -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; }

View File

@ -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<ModbusRTUSlaveWordAddress*>;
@ -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<ModbusRTUSlaveWordAddress*> *rWords;
// LinkedList<ModbusRTUSlaveBitAddress*> *rBits;
wordsList *rWords;
bitsList *rBits;
wordsList *rwWords;

View File

@ -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<typename hash,typename map>
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<typename hash, typename map>
class HashMap {
public:
HashMap(HashType<hash,map>* newMap,byte newSize){
hashMap = newMap;
size = newSize;
for (byte i=0; i<size; i++){
hashMap[i].reset();
}
}
HashType<hash,map>& operator[](int x){
//TODO - bounds
return hashMap[x];
}
byte getIndexOf( hash key ){
for (byte i=0; i<size; i++){
if (hashMap[i].getHash()==key){
return i;
}
}
}
map getValueOf( hash key ){
for (byte i=0; i<size; i++){
if (hashMap[i].getHash()==key){
return hashMap[i].getValue();
}
}
}
void debug(){
for (byte i=0; i<size; i++){
Serial.print(hashMap[i].getHash());
Serial.print(" - ");
Serial.println(hashMap[i].getValue());
}
}
private:
HashType<hash,map>* hashMap;
byte size;
};
#endif
/*
|| @changelog
|| | 1.0 2009-07-13 - Alexander Brevig : Initial Release
|| #
*/

View File

@ -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<typename hash,typename map>
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<typename hash, typename map>
class HashMap {
public:
HashMap(HashType<hash,map>* newMap,byte newSize){
hashMap = newMap;
size = newSize;
for (byte i=0; i<size; i++){
hashMap[i].reset();
}
}
HashType<hash,map>& operator[](int x){
//TODO - bounds
return hashMap[x];
}
byte getIndexOf( hash key ){
for (byte i=0; i<size; i++){
if (hashMap[i].getHash()==key){
return i;
}
}
}
map getValueOf( hash key ){
for (byte i=0; i<size; i++){
if (hashMap[i].getHash()==key){
return hashMap[i].getValue();
}
}
}
void debug(){
for (byte i=0; i<size; i++){
Serial.print(hashMap[i].getHash());
Serial.print(" - ");
Serial.println(hashMap[i].getValue());
}
}
private:
HashType<hash,map>* hashMap;
byte size;
};
#endif
/*
|| @changelog
|| | 1.0 2009-07-13 - Alexander Brevig : Initial Release
|| #
*/

View File

@ -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;
}

125
src/mystrings/mystrings.h Normal file
View File

@ -0,0 +1,125 @@
#ifndef _MY_STRINGS_H_INCLUDED
#define _MY_STRINGS_H_INCLUDED
#include <avr/pgmspace.h>
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

50
src/types.h Normal file
View File

@ -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