// KWL mit easyControls über Modbus TCP steuern v4 // by jan.wachsmuth@web.de, Feedback gerne erwünscht. // //INPUTS //AI1 .... Betriebsart (noch nicht implementiert) //AI2 .... Lüfterstufe Normalbetrieb (schreiben, 0 .. 4) //AI3 .... Stoßlüften (schreiben, 0 oder 1) // //OUTPUTS //AQ1 .... Temperatur Aussenluft //AQ2 .... Temperatur Zuluft //AQ3 .... Temperatur Fortluft //AQ4 .... Temperatur Abluft //AQ5 .... Ventilator Zuluft (rpm) //AQ6 .... Ventilator Abluft (rpm) //AQ7 .... Bypass aktiv //AQ8 .... Anzahl Fehler //AQ9 .... Anzahl Warnungen //A10 .... Anzahl Infos //A11 .... Lüfterstufe Normalbetrieb (Radiotasten) //A12 .... aktive Lüfterstufe (aus KWL auslesen) //TQ1 .... Artikelbezeichnung //CONSTANTS // Disable/enable debug mode (writes log messages) #define DEBUG_MODE 0 //VARIABLES char* answer; float tempAussenluft = 0.0; float tempZuluft = 0.0; float tempFortluft = 0.0; float tempAbluft = 0.0; float rpmZuluft = 0.0; float rpmAbluft = 0.0; float bypass = 0.0; float anzahlFehler = 0.0; float anzahlWarnungen = 0.0; float anzahlInfo = 0.0; char artikelBez[32]; int betriebsart; int luefterStufe; int luefterStufeLast; int stosslueften; int initScript; // Modbus TCP header char modbusTCP[30]; char tIDl,tIDh; char szBuffer[100]; int nLen; int err; printf("Helios KWL Modbus TCP start"); // Betriebsart wählen 0 = Automat. 1 = Handbetrieb betriebsart=(int)getinput(0); // Parameter auf Gültigkeit prüfen if(betriebsart<0) betriebsart=0; if(betriebsart>1) betriebsart=1; // modbusTCP Header vorbereiten und konstante Werte eintragen tIDl=0; tIDh=0; // Protocol Identifier - 2 Byte Zahl, immer 0 modbusTCP[2]=0; modbusTCP[3]=0; // Length - 2 Byte Zahl - Länge des nachfolgenden Datenpaketes insgesamt modbusTCP[4]=0; modbusTCP[5]=0; // Unit Identifier - immer 180 für Helios KWL modbusTCP[6]=180; // reference number - Zweck unklar modbusTCP[8]=0; modbusTCP[9]=1; // Word count - Anzahl der zu schreibenden Worte (16 Bit) modbusTCP[10]=0; int modbusTCPwriteVar(char* heliosVar) { int wordLen; int nLen; // Anfrage mit modbusTCP Header erstellen (variabler Anteil) // Transaction Identifier - 2 Byte Zahl, die mit jeder Anfrage hochzählt modbusTCP[0] = tIDh; modbusTCP[1] = tIDl; tIDl++; if (tIDl == 256) { tIDl = 0; tIDh++; if (tIDh == 256) tIDh = 0; } // Function Code modbusTCP[7] = 16; // Write Multiple Registers (16) // Word count - Anzahl der zu schreibenden Worte (16 Bit) // inkl. 0-Byte am Ende (+1), aufrunden (+1) nLen = strlen(heliosVar); wordLen = (nLen + 2) >> 1; nLen = wordLen << 1; // Length - 2 Byte Zahl - Länge des nachfolgenden Datenpaketes insgesamt modbusTCP[5] = 7 + nLen; // Anzahl der zu schreibenden Register (a 16 Bit) modbusTCP[11] = wordLen; // Byte count - Anzahl der nachfolgenden Bytes modbusTCP[12] = nLen; strncpy(&modbusTCP[13], heliosVar, nLen); // Header und Daten an Helios senden stream_write(stream, modbusTCP, 13 + nLen); stream_flush(stream); // TCP Datenpaket empfangen, max. 500 msec auf Antwort warten nLen = stream_read(stream, szBuffer, sizeof(szBuffer) - 1, 500); if (nLen == 0) { printf("modbusTCP request for %s failed (0 bytes read) !", heliosVar); return 1; } else { // weitere Überprüfung der Antwort fehlt - Werte ähnlich wie in der Anfrage // es wird angenommen, dass die Antwort passt return 0; } } char* modbusTCPreadVar(char* heliosVar, int len) { // "countWords" Worte (a 16-Bit) aus Holding Register auslesen // TCP Anfrage mit modbusTCP Header erstellen // Transaction Identifier - 2 Byte Zahl, die mit jeder Anfrage hochzählt modbusTCP[0] = tIDh; modbusTCP[1] = tIDl; tIDl++; if (tIDl == 256) { tIDl = 0; tIDh++; if (tIDh == 256) tIDh = 0; } // Length - 2 Byte Zahl - Länge des nachfolgenden Datenpaketes insgesamt modbusTCP[5] = 6; // Function Code modbusTCP[7] = 3; // Read Holding Registers (3) // Word count - Anzahl der zu lesenden Worte (16 Bit), muss < 256 sein! // Variable=6 Byte, "="=1 Byte, Wert=len Byte, 0-Byte am Ende (+1), aufrunden (+1) modbusTCP[11] = (9 + len) >> 1; // TCP Datenpaket mit modbus Header senden stream_write(stream, modbusTCP, 12); stream_flush(stream); // TCP Datenpaket empfangen, max. 500 msec auf Antwort warten nLen = stream_read(stream, szBuffer, sizeof(szBuffer) - 1, 500); // Länge der Antwort prüfen (mind v0XXXX=y\0) if (nLen>16) { // Variable auslesen und überprüfen, immer 6 Bytes lang "v0XXXX" if (strncmp(heliosVar, &szBuffer[9], 6) == 0) { return &szBuffer[16]; } else { szBuffer[15]=0; printf("modbusTCP variable %s is not %s !", heliosVar, &szBuffer[9]); return NULL; } } else { printf("modbusTCP answer too short, only %i Bytes, <%s>", nLen, &szBuffer[9]); return NULL; } } float heliosReadFloat(char* heliosVar, int len) { int err; char* answer; float floatValue; // 1. Teil: String für Variable schreiben err = modbusTCPwriteVar(heliosVar); if (err) return -999; // 2. Teil: Antwort aus Holding Register auslesen answer = modbusTCPreadVar(heliosVar, len); if (answer != NULL) { floatValue = atof(answer); if (DEBUG_MODE) printf("modbusTCP READ %s = %f", heliosVar, floatValue); return floatValue; } return -999; } int heliosReadInt(char* heliosVar, int len) { int err; char* answer; int intValue; // 1. Teil: String für Variable schreiben err = modbusTCPwriteVar(heliosVar); if (err) return -999; // 2. Teil: Antwort aus Holding Register auslesen answer = modbusTCPreadVar(heliosVar, len); if (answer != NULL) { intValue = atoi(answer); if (DEBUG_MODE) printf("modbusTCP READ %s = %i", heliosVar, intValue); return intValue; } return -999; } void heliosReadString(char* heliosVar, char* heliosString, int len) { int err; char* answer; // 1. Teil: String für Variable schreiben err = modbusTCPwriteVar(heliosVar); if (err) { strcpy(heliosString, "Error!"); return; } // 2. Teil: Antwort aus Holding Register auslesen answer = modbusTCPreadVar(heliosVar, len); if (answer != NULL) { strcpy(heliosString, answer); if (DEBUG_MODE) printf("modbusTCP READ %s = %s", heliosVar, heliosString); } else { strcpy(heliosString, "Error!"); } } void heliosWriteInt(char* heliosVar, int value) { char heliosString[20]; sprintf(heliosString,"%s=%d", heliosVar, value); if (DEBUG_MODE) printf("modbusTCP WRITE %s = %d", heliosVar, value); modbusTCPwriteVar(heliosString); } // BEGIN MAIN SCRIPT initScript = 1; while(TRUE) { // Stream zur IP-Adresse der Helios KWL (Modbus TCP Gegenstelle) STREAM* stream = stream_create("/dev/tcp/192.168.128.11/502", 1, 0); if (stream != NULL) { // einmalig zu Beginn des Skriptes die aktuelle Lüfterstufe auslesen if (initScript == 1) { // Lüfterstufe auslesen, char[1] as int luefterStufe = heliosReadInt("v00102", 1); luefterStufeLast = luefterStufe; setoutput(10, luefterStufe); // Lüfterstufe für Normalbetrieb (Radiotasten) setoutput(11, luefterStufe); // aktuelle Lüfterstufe initScript = 0; } else { // Temperatur Aussenluft auslesen, char[7] tempAussenluft = heliosReadFloat("v00104", 7); setoutput(0, tempAussenluft); sleep(50); // Temperatur Zuluft auslesen, char[7] tempZuluft = heliosReadFloat("v00105", 7); setoutput(1, tempZuluft); sleep(50); // Temperatur Fortluft auslesen, char[7] tempFortluft = heliosReadFloat("v00106", 7); setoutput(2, tempFortluft); sleep(50); // Temperatur Abluft auslesen, char[7] tempAbluft = heliosReadFloat("v00107", 7); setoutput(3, tempAbluft); sleep(50); // Drehzahl Zuluft (rpm) auslesen, char[4] rpmZuluft = heliosReadFloat("v00348", 4); setoutput(4, rpmZuluft); sleep(50); // Drehzahl Abluft (rpm) auslesen, char[4] rpmAbluft = heliosReadFloat("v00349", 4); setoutput(5, rpmAbluft); sleep(50); // Bypass auslesen, char[1], undokumentiert! bypass = heliosReadFloat("v02119", 1); setoutput(6, bypass); sleep(50); // AnzahlFehler auslesen, char[2] anzahlFehler = heliosReadFloat("v01300", 2); setoutput(7, anzahlFehler); sleep(50); // AnzahlWarnungen auslesen, char[1] anzahlWarnungen = heliosReadFloat("v01301", 1); setoutput(8, anzahlWarnungen); sleep(50); // AnzahlInfo auslesen, char[1] anzahlInfo = heliosReadFloat("v01302", 1); setoutput(9, anzahlInfo); sleep(50); // Artikelbezeichnung auslesen, char[31] heliosReadString("v00000", artikelBez, 31); setoutputtext(0, artikelBez); sleep(50); // Lüfterstufe für Normalbetrieb lesen (Wertebereich 0..4) luefterStufe = (int)getinput(1); stosslueften = (int)getinput(2); // wenn Stoßlüften = 1, dann Lüfterstufe 4 einstellen // Änderungen durch andere Quellen werden während des Stoßlüftens ignoriert if (stosslueften == 1) { heliosWriteInt("v00102", 4); // sicherstellen, dass am Ende des Stoßlüftens wieder der vorherige Wert eingestellt wird luefterStufeLast = 5; setoutput(10, luefterStufe); // in Loxone eingestellte Lüfterstufe für Normalbetrieb 1:1 wieder zurückgeben setoutput(11, 4); // akutelle Lüfterstufe } else { // Normalbetrieb (kein Stoßlüften) - Lüfterstufe setzen bzw. lesen (Wertebereich 0..4) // wenn Lüfterstufe in Loxone geändert wurde bzw. Stoßlüften beendet wird, dann schreiben if (luefterStufe != luefterStufeLast) { heliosWriteInt("v00102", luefterStufe); } else { // sonst auslesen, möglicherweise von easycontrols geändert worden luefterStufe = heliosReadInt("v00102", 1); } luefterStufeLast = luefterStufe; setoutput(10, luefterStufe); setoutput(11, luefterStufe); } sleep(50); } } else { printf("Unable to create TCP stream"); sleeps(10); } stream_close(stream); sleeps(1); }