// KWL mit easyControls über Modbus TCP steuern v5.0 // 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) //AI4 .... Bypass Raumtemperatur // //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) //A13 .... Bypass Raumtemperatur //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; int bypassTemp = 0.0; int bypassTempLast = 0.0; int anzahlFehler = 0; int anzahlWarnungen = 0; int anzahlInfo = 0; char artikelBez[32]; int betriebsart; int luefterStufe = 3; int luefterStufeLast; int stosslueften; int initScript; // Modbus TCP header char modbusTCP[30]; char tIDl,tIDh; char szBuffer[100]; int nLen; int err; int intValue; float floatValue; char stringValue[255]; 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; } } // liest eine Helios Variable vom Typ Float aus und schreibt diese in floatValue // Rückgabewert: True, wenn eine Zahl gelesen wurde int heliosReadFloat(float* floatValue, char* heliosVar, int len) { int err; char* answer; // 1. Teil: String für Variable schreiben err = modbusTCPwriteVar(heliosVar); if (err) return false; // 2. Teil: Antwort aus Holding Register auslesen answer = modbusTCPreadVar(heliosVar, len); if (answer == NULL) return false; // Überprüfung der Antwort auf korrekte Gleitkommazahl fehlt noch *floatValue = atof(answer); if (DEBUG_MODE) printf("modbusTCP READ %s = %f", heliosVar, *floatValue); return true; } // liest eine Helios Variable vom Typ Int aus und schreibt diese in intValue // Rückgabewert: True, wenn eine Zahl gelesen wurde int heliosReadInt(int * intValue, char* heliosVar, int len) { int err; char* answer; // 1. Teil: String für Variable schreiben err = modbusTCPwriteVar(heliosVar); if (err) return false; // 2. Teil: Antwort aus Holding Register auslesen answer = modbusTCPreadVar(heliosVar, len); if (answer == NULL) return false; *intValue = atoi(answer); if (DEBUG_MODE) printf("modbusTCP READ %s = %i", heliosVar, *intValue); return true; } // liest eine Helios Variable vom Typ String aus und schreibt diese in stringValue // Rückgabewert: True, wenn eine Zahl gelesen wurde int heliosReadString(char* stringValue, char* heliosVar, int len) { int err; char* answer; // 1. Teil: String für Variable schreiben err = modbusTCPwriteVar(heliosVar); if (err) return false; // 2. Teil: Antwort aus Holding Register auslesen answer = modbusTCPreadVar(heliosVar, len); if (answer == NULL) return false; strcpy(stringValue, answer); if (DEBUG_MODE) printf("modbusTCP READ %s = %s", heliosVar, stringValue); return true; } // schreibt einen Wert vom Typ Int in eine Helios Variable 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); } // schreibt einen Wert vom Typ Float in eine Helios Variable void heliosWriteFloat(char* heliosVar, float value) { char heliosString[20]; sprintf(heliosString,"%s=%f", heliosVar, value); if (DEBUG_MODE) printf("modbusTCP WRITE %s = %f", 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 if (heliosReadInt(&intValue, "v00102", 1)) { luefterStufe = intValue; luefterStufeLast = luefterStufe; setoutput(10, luefterStufe); // Lüfterstufe für Normalbetrieb (Radiotasten) setoutput(11, luefterStufe); // aktuelle Lüfterstufe } // Bypasstemp. auslesen, char[2] as int if (heliosReadInt(&intValue, "v01035", 2)) { bypassTemp = intValue; bypassTempLast = bypassTemp; setoutput(12, bypassTemp); // Bypasstemperatur } // Ende der Initialisierung initScript = 0; } else { // Temperatur Aussenluft auslesen, char[7] if (heliosReadFloat(&floatValue, "v00104", 7)) { tempAussenluft = floatValue; setoutput(0, tempAussenluft); } sleep(50); // Temperatur Zuluft auslesen, char[7] if (heliosReadFloat(&floatValue, "v00105", 7)) { tempZuluft = floatValue; setoutput(1, tempZuluft); } sleep(50); // Temperatur Fortluft auslesen, char[7] if (heliosReadFloat(&floatValue, "v00106", 7)) { tempFortluft = floatValue; setoutput(2, tempFortluft); } sleep(50); // Temperatur Abluft auslesen, char[7] if (heliosReadFloat(&floatValue, "v00107", 7)) { tempAbluft = floatValue; setoutput(3, tempAbluft); } sleep(50); // Drehzahl Zuluft (rpm) auslesen, char[4] if (heliosReadFloat(&floatValue, "v00348", 4)) { rpmZuluft = floatValue; setoutput(4, rpmZuluft); } sleep(50); // Drehzahl Abluft (rpm) auslesen, char[4] if (heliosReadFloat(&floatValue, "v00349", 4)) { rpmAbluft = floatValue; setoutput(5, rpmAbluft); } sleep(50); // Status Bypass auslesen, char[1], undokumentiert! if (heliosReadFloat(&floatValue, "v02119", 1)) { bypass = floatValue; setoutput(6, bypass); } sleep(50); // AnzahlFehler auslesen, char[2] if (heliosReadInt(&intValue, "v01300", 2)) { anzahlFehler = intValue; setoutput(7, (float)anzahlFehler); } sleep(50); // AnzahlWarnungen auslesen, char[1] if (heliosReadInt(&intValue, "v01301", 1)) { anzahlWarnungen = intValue; setoutput(8, (float)anzahlWarnungen); } sleep(50); // AnzahlInfo auslesen, char[1] if (heliosReadInt(&intValue, "v01302", 1)) { anzahlInfo = intValue; setoutput(9, (float)anzahlInfo); } sleep(50); // Artikelbezeichnung auslesen, char[31] if (heliosReadString(&artikelBez, "v00000", 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; // stellt sicher, dass luefterStufe != luefterStufeLast nach Ende vom Stoßlüften 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 if (heliosReadInt(&intValue, "v00102", 1)) luefterStufe = intValue; } luefterStufeLast = luefterStufe; setoutput(10, luefterStufe); setoutput(11, luefterStufe); } sleep(50); // Bypasstemperatur lesen (10...40) bypassTemp = (int)getinput(3); // wurde bypassTemp wurde in Loxone geändert? if (bypassTemp != bypassTempLast) { // Wertebereich ggf. anpassen if (bypassTemp > 40) bypassTemp = 40; if (bypassTemp < 10) bypassTemp = 10; heliosWriteInt("v01035", bypassTemp); } else { // Bypasstemp. auslesen, char[2] as int if (heliosReadInt(&intValue, "v01035", 2)) bypassTemp = intValue; } bypassTempLast = bypassTemp; setoutput(12, bypassTemp); // Bypasstemperatur sleep(50); } } else { printf("Unable to create TCP stream"); sleeps(10); } stream_close(stream); sleeps(1); }