diff --git a/README.md b/README.md index 38f17a2..44f5a3d 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,84 @@ # DreherTankController -Experimentelle Firmware für einen Tankregler, der die Temperatur und den Druck eines Tanks regeln kann, und über Modbus-RTU (RS485) von einem Master gesteuert / abgefragt werden kann. \ No newline at end of file +Experimentelle Firmware für einen Tankregler, der die Temperatur und den Druck eines Tanks regeln kann, und über Modbus-RTU (RS485) von einem Master gesteuert / abgefragt werden kann. + +Das Programm ist für den Atmega2560 (Arduino Mega 2560) geschrieben. + +# Modbus + +## Coils - FC 1, 5, 15 + +Betriebszustand Regler (durch die Modbus-Implementierung sind die Register ***0xB4...0xB7*** les-/schreibbar – diese Werte werden ignoriert): + +- ***0xB0***: Temperaturregelung aktiv +- ***0xB1***: Drucksteigerung aktiv +- ***0xB2***: Druckabfall aktiv +- ***0xB3***: Regler aktiv (unabhängig von den vorigen Werten kann hier der Regler komplett deaktiviert werden) + +--- +## Discrete Inputs - FC 2 + +Ausgangszustände Ventile (durch die Modbus-Implementierung sind die Register ***0xD4...0xD7*** lesbar und geben immer 0 zurück): + +- ***0xD0***: Ventil Temperatur 1 +- ***0xD1***: Ventil Temperatur 2 (noch nicht genutzt) +- ***0xD2***: Ventil Drucksteigerung +- ***0xD3***: Ventil Druckabfall + +--- +## Input Register - FC 4 + +Anzahl der gespeicherten Ereignisse abfragen: + +- ***0x00***: gibt an, ob und wieviele Ereignisse (Ventil-Schaltvorgänge) aufgetreten sind. Dieses Register wird mit 0 initialisiert. + +Verschiedenes: Ausgangszustände Ventile, Betriebsart Regler, Flags: + +- ***0x01***: + - Bit 0...3: siehe Coils (Bit 0: ***0xB0***) + - Bit 4...7: reserviert + - Bit 8...11: siehe Discrete Inputs (Bit 7: ***0x00***) + - Bit 12...13: reserviert + - Bit 14: ist gesetzt, falls der 12 Bit Timer übergelaufen ist + - Bit 15: ist gesetzt, bevor das Holding-Register ***0xC0*** (setzen der Referenzzeit) erstmalig beschrieben wurde + +Messwerte: + +- ***0x02***: Temperatur 1 (Durchschnittswert zwischen 2 Abfragen als INT16 in Hundertstel-°C – max ?? Minuten, einzeln vorkommende Sensorfehler werden ignoriert, bei häufigeren Fehlern wird *0xFFFF* zurückgegeben) +***### TODO – Durchschnittswerte ermitteln (aktuell nur letzter Wert – ganz schlecht beim Druck, da dieser extrem bei den Schaltvorgängen schwankt)*** +***### TODO – maximale Zeit ermitteln (auch für Druck)*** +- ***0x03***: Temperatur 2 (noch nicht genutzt – gibt *0xFFFF* zurück) +- ***0x04***: Druck (siehe Temperatur) + +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*** + - 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: + +- ***0xF0***: Firmware-Version Regler (4 MSBs: Major, 6 Bits Minor, 6 LSBs: Micro) +- ***0xF1***: Anzahl der Kühlzonen (aktuell nur 1 Zone implementiert) + +--- +## Holding Register - FC 3, 6, 16 + +Sollwerte: + +- ***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 + +Setzen der Referenzzeit: + +- ***0xC0***: Es muss ein vom vorigen Wert abweichender Wert (z.B. ein Zähler oder die 16 LSBs der UNIX-Zeit in Zehntelsekunden – beim ersten Mal jedenfalls NICHT *0xFFFF*, da das Register damit initialisiert ist und kein Unterschied festgestellt werden könnte) übergeben werden. Der Regler verwendet diesen Wert prinzipiell für nichts, außer dass der interne 12 Bit-Timer (1/10 Sekunden) und der Event-Counter auf 0 zurückgesetzt werden (siehe Input-Register ***0x00*** und ***0x06***), wenn sich dieser Wert ändert. Der Master kann entweder + + - den entsprechenden Zeitstempel im speichern, und den Zeitstempel der Antwort (siehe Input-Register ***0x06***) dazu addieren, um den tatsächlichen Zeitpunkt des Ereignisses zu ermitteln, oder + - den in ***0x05*** gespeicherten Referenzzeitpunkt (Zehntelsekunden seit Reglerstart bzw. letzter Referenzierung (siehe Holding-Register ***0xC0***)) mit dem Event-Zeitpunkt und dem aktuellen Zeitstempel des Masters gegenrechnen. diff --git a/src/display/display.h b/src/display/display.h index 449bd3d..b0850b0 100644 --- a/src/display/display.h +++ b/src/display/display.h @@ -170,7 +170,7 @@ public: void (*reset)() = 0; /* Callback-Funktionen */ - void(*setParams)(const parameters&); + void(*setParams)(parameters&); void(*setModbusParams)(const modbusParameters&); void(*setPSensor)(const PSensor&); }; diff --git a/src/modbus/README.md b/src/modbus/README.md index b903b61..1cb44a3 100644 --- a/src/modbus/README.md +++ b/src/modbus/README.md @@ -3,5 +3,7 @@ This is a modified version of ModbusRTU_Slave_RS485 version 1.0.2 by Łukasz Śl https://github.com/lucasso/ModbusRTUSlaveArduino +*Modified by brunotic.* + ## License See ./LICENSE file (GNU GPL v3.0 or later) diff --git a/tank_controller.ino b/tank_controller.ino index cd992b1..eab9d61 100644 --- a/tank_controller.ino +++ b/tank_controller.ino @@ -1,3 +1,7 @@ +#ifndef ARDUINO_AVR_MEGA2560 +#error This program was written for an Atmega2560 +#endif + #include #include "src/OneWire/OneWire.h" #include "src/DallasTemperature/DallasTemperature.h" @@ -22,7 +26,7 @@ u16 lastRefTime = 0xFFFF; 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]; +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 @@ -34,7 +38,7 @@ u16 &refTime = modbusRefTime[0]; ModbusRTUSlave mb(&Serial1, 2); -void checkParamINT16(int *source, int *target, const int &std, const int &min, const int &max) { +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; @@ -103,40 +107,40 @@ void checkModbusParams() { if (modbusSetpoints[0] != params.ts1) { if (paramsChangedByUI) { _print("ts1 - UI -> Modbus - "); - checkParamINT16(¶ms.ts1, &modbusSetpoints[0], _STD_TEMP_SETPOINT, _MIN_TEMP_SETPOINT, _MAX_TEMP_SETPOINT); + checkParamINT16(¶ms.ts1, (int16_t*)&modbusSetpoints[0], _STD_TEMP_SETPOINT, _MIN_TEMP_SETPOINT, _MAX_TEMP_SETPOINT); } else { _print("ts1 - Modbus -> UI - "); - checkParamINT16(&modbusSetpoints[0], ¶ms.ts1, _STD_TEMP_SETPOINT, _MIN_TEMP_SETPOINT, _MAX_TEMP_SETPOINT); + checkParamINT16((int16_t*)&modbusSetpoints[0], ¶ms.ts1, _STD_TEMP_SETPOINT, _MIN_TEMP_SETPOINT, _MAX_TEMP_SETPOINT); p.ts1 = params.ts1; } } if (modbusSetpoints[1] != params.th1) { if (paramsChangedByUI) { _print("th1 - UI -> Modbus - "); - checkParamINT16(¶ms.th1, &modbusSetpoints[1], _STD_TEMP_HYSTERESIS, _MIN_TEMP_HYSTERESIS, _MAX_TEMP_HYSTERESIS); + checkParamINT16(¶ms.th1, (int16_t*)&modbusSetpoints[1], _STD_TEMP_HYSTERESIS, _MIN_TEMP_HYSTERESIS, _MAX_TEMP_HYSTERESIS); } else { _print("th1 - Modbus -> UI - "); - checkParamINT16(&modbusSetpoints[1], ¶ms.th1, _STD_TEMP_HYSTERESIS, _MIN_TEMP_HYSTERESIS, _MAX_TEMP_HYSTERESIS); + checkParamINT16((int16_t*)&modbusSetpoints[1], ¶ms.th1, _STD_TEMP_HYSTERESIS, _MIN_TEMP_HYSTERESIS, _MAX_TEMP_HYSTERESIS); p.th1 = params.th1; } } if (modbusSetpoints[4] != params.ps) { if (paramsChangedByUI) { _print("ps - UI -> Modbus - "); - checkParamINT16(¶ms.ps, &modbusSetpoints[4], _STD_P_SETPOINT, _MIN_P_SETPOINT, _MAX_P_SETPOINT); + checkParamINT16(¶ms.ps, (int16_t*)&modbusSetpoints[4], _STD_P_SETPOINT, _MIN_P_SETPOINT, _MAX_P_SETPOINT); } else { _print("ps - Modbus -> UI - "); - checkParamINT16(&modbusSetpoints[4], ¶ms.ps, _STD_P_SETPOINT, _MIN_P_SETPOINT, _MAX_P_SETPOINT); + checkParamINT16((int16_t*)&modbusSetpoints[4], ¶ms.ps, _STD_P_SETPOINT, _MIN_P_SETPOINT, _MAX_P_SETPOINT); p.ps = params.ps; } } if (modbusSetpoints[5] != params.ph) { if (paramsChangedByUI) { _print("ph - UI -> Modbus - "); - checkParamINT16(¶ms.ph, &modbusSetpoints[5], _STD_P_HYSTERESIS, _MIN_P_HYSTERESIS, _MAX_P_HYSTERESIS); + checkParamINT16(¶ms.ph, (int16_t*)&modbusSetpoints[5], _STD_P_HYSTERESIS, _MIN_P_HYSTERESIS, _MAX_P_HYSTERESIS); } else { _print("ph - Modbus -> UI - "); - checkParamINT16(&modbusSetpoints[5], ¶ms.ph, _STD_P_HYSTERESIS, _MIN_P_HYSTERESIS, _MAX_P_HYSTERESIS); + checkParamINT16((int16_t*)&modbusSetpoints[5], ¶ms.ph, _STD_P_HYSTERESIS, _MIN_P_HYSTERESIS, _MAX_P_HYSTERESIS); p.ph = params.ph; } } @@ -366,10 +370,8 @@ void setPSensor(const PSensor &sensor) { void setup() { #if _DEBUG == 1 Serial.begin(19200); - // char test[60]; - // sprintf(test, "test: %u, %u, %u", 115200, 57600, 38400); - // Serial.println(test); #endif + pinMode(53, OUTPUT); // Mega CS-Pin (um Slave-Betrieb zu vermeiden) paramsChangedByUI = true;