// KWL mit easyControls über Modbus TCP steuern v5.5.1 // by jan.wachsmuth@web.de, Feedback gerne erwünscht. // //INPUTS //AI1 .... Betriebsart (noch nicht implementiert) //AI2 .... Lüfterstufe Normalbetrieb (schreiben, 0 .. 4) //AI3 .... Funktion Stoßlüften (schreiben, 0 oder 1): Lüfterstufe auf Maximum (=4) einstellen //AI4 .... Bypass - Raumtemperatur (Bypass wird bei geringeren Innentemperaturen nicht aktiviert) //AI5 .... Bypass - min. Aussentemperatur (Bypass wird bei geringeren Aussentempaturen abgeschaltet) //AI6 .... Abfragefrequenz der Statuswerte aus Helios in Sek - z.B. 10 => ca. alle 10 Sek werden Werte abgefragt //AI7 .... Funktion Intensivlüften (schreiben, 0 oder 1): Lüfterstufe um 1 erhöhen, wenn z.B. CO2 Sensor eine Verschlechterung // der Luftqualität feststellt oder über eine Schaltuhr wenn meist mehrere Personen anwesend sind // Fehlermeldungen im Log reduzieren, um Lebensdauer der SD-Karte nicht zu verkürzen // sinnvolle Werte sind z.B. 10 Fehler pro 60 Minuten. //AI8 .... Anzahl der Fehlermeldungen im Log pro Zeitspanne (Default: 10 Fehler) //AI9 .... Zeitintervall für Zähler der Fehlermeldungen in Minuten (Default: 60 min) // Es werden im Intervall nur die o.a. Anzahl der Fehler im Detail protokolliert Wenn mehr Fehler produziert // werden, dann gibt es am Ende des Intervalls für die zusätzlichen Fehler nur eine Sammelmeldung mit der Anzahl. // //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 //AQ10 ... Anzahl Infos //AQ11 ... Lüfterstufe Normalbetrieb (Radiotasten) //AQ12 ... aktive Lüfterstufe (aus KWL auslesen) //TQ1 .... Artikelbezeichnung //CONSTANTS // Disable/enable debug mode (writes log messages) #define DEBUG_MODE 0 // Disable/enable debug at start time to find the reason // why the program gets disabled (writes log messages) // #define DEBUG_START 1 int DEBUG_START = 1; if (DEBUG_START) printf("DEBUG: Helios KWL Modbus TCP - Watchpoint 1 - Start of program"); //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; float minAussenBypass = 0.0; int minAussenBypassTemp = 0.0; int minAussenBypassTempLast = 0.0; int anzahlFehler = 0; int anzahlWarnungen = 0; int anzahlInfo = 0; char artikelBez[32]; int betriebsart; int luefterStufe = 3; int luefterStufeLast; int stosslueften; int intensivlueften; int luefterStufeNormal; int pullFreq = 10; STREAM* stream; unsigned int lastLoopTime; unsigned int currentTime; unsigned int errorInterval; unsigned int errorCount; unsigned int errorMax; unsigned int errorTime; char errorMessage[255]; // Modbus TCP header char modbusTCP[30]; char tIDl,tIDh; char szBuffer[100]; int nLen; int err; int intValue; float floatValue; char stringValue[255]; printf("INFO: Helios KWL Modbus TCP start"); if (DEBUG_START) printf("DEBUG: Helios KWL Modbus TCP - Watchpoint 2 - variables defined"); // 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; if (DEBUG_START) printf("DEBUG: Helios KWL Modbus TCP - Watchpoint 3 - modbus variable initialized"); void printError(char* errorMessage) { if (errorCount == 0) { errorTime=getcurrenttime(); } if (errorCount < errorMax) printf(errorMessage); errorCount++; sleep(1000); } 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, 500 msec auf Antwort warten nLen = stream_read(stream, szBuffer, sizeof(szBuffer) - 1, 500); if (nLen == 0) { sprintf(errorMessage, "ERROR: Helios KWL modbusTCP select or write variable %s failed (0 bytes read) !", heliosVar); printError(errorMessage); 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, 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 == 0) { // leere Antwort - evtl. zu kurze Wartezeit ? // falls diese Fehlermeldung sehr häufig kommt, bitte ggf. die Wartezeit von 500ms erhöhen oder Netzwerk auf Fehler/Probleme prüfen sprintf(errorMessage, "ERROR: Helios KWL modbusTCP read variable %s - no answer !", heliosVar); printError(errorMessage); return NULL; } 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; sprintf(errorMessage, "ERROR: Helios KWL modbusTCP read variable %s is not %s !", heliosVar, &szBuffer[9]); printError(errorMessage); return NULL; } } else { if (DEBUG_MODE) { sprintf(errorMessage, "DEBUG: Helios KWL modbusTCP read variable %s - answer too short, only %d Bytes, <%s>", heliosVar, nLen, &szBuffer[9]); } else { sprintf(errorMessage, "ERROR: Helios KWL modbusTCP read variable %s - answer too short, only %d Bytes!", heliosVar, nLen); } printError(errorMessage); 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) { sprintf(errorMessage, "DEBUG: Helios KWL modbusTCP READ %s = %f", heliosVar, *floatValue); printError(errorMessage); } 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) { sprintf(errorMessage, "DEBUG: Helios KWL modbusTCP READ %s = %i", heliosVar, *intValue); printError(errorMessage); } 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) { sprintf(errorMessage, "DEBUG: Helios KWL modbusTCP READ %s = %s", heliosVar, stringValue); printError(errorMessage); } 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) { sprintf(errorMessage, "DEBUG: Helios KWL modbusTCP WRITE %s = %d", heliosVar, value); printError(errorMessage); } 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) { sprintf(errorMessage, "DEBUG: Helios KWL modbusTCP WRITE %s = %f", heliosVar, value); printError(errorMessage); } modbusTCPwriteVar(heliosString); } // BEGIN MAIN SCRIPT if (DEBUG_START) printf("DEBUG: Helios KWL Modbus TCP - Watchpoint 4 - begin of main"); // Vorsichtsmaßnahme, damit die gewünschten Werte an allen Eingängen anliegen sleeps(10); // 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; if (DEBUG_START) printf("DEBUG: Helios KWL Modbus TCP - Watchpoint 5 - INPUT betriebsart = %d ", betriebsart); // Fehlerzähler initialisieren errorCount = 0; // max. Anzahl der Fehler, die im Intervall geloggt werden sollen - wird nur am Anfang ausgelesen errorMax = (unsigned int)getinput(7); if (errorMax == 0) errorMax = 10; // Default: 10 Fehlermeldungen (pro Stunden, s.u.) if (DEBUG_START) printf("DEBUG: Helios KWL Modbus TCP - Watchpoint 6 - INPUT errorMax = %u ", errorMax); errorTime=getcurrenttime(); // Zeitintervall in Minuten, in dem der Zähler für die Fehlermeldungen zurückgesetzt wird errorInterval = (unsigned int)(getinput(8) * 60); // in Sekunden umrechnen if (errorInterval == 0) errorInterval = 3600; // Default: 1 Stunde, wenn kein Wert vorgegeben wird if (DEBUG_START) printf("DEBUG: Helios KWL Modbus TCP - Watchpoint 7 - INPUT errorInterval = %u ", errorInterval); // vorherige Werte auf ungültige Werte setzen, damit die aktuellen Werte von Loxone zu Helios geschrieben werden luefterStufeLast = -1; minAussenBypassTempLast = -1; bypassTempLast = -1; // Ende der Initialisierung if (DEBUG_START) printf("DEBUG: Helios KWL Modbus TCP - Watchpoint 8 - start of main loop"); // main loop while(TRUE) { // Lüfterstufe für Normalbetrieb lesen (Wertebereich 0...4) luefterStufeNormal = (int)getinput(1); if (luefterStufeNormal < 0) luefterStufeNormal = 0; if (luefterStufeNormal > 4) luefterStufeNormal = 4; if (DEBUG_START) printf("DEBUG: Helios KWL Modbus TCP - Watchpoint 9 - INPUT luefterStufeNormal = %d ", luefterStufeNormal); // Funktion Stoßlüften: maximale Stufe einstellen (=4) stosslueften = (int)getinput(2); if (DEBUG_START) printf("DEBUG: Helios KWL Modbus TCP - Watchpoint 10 - INPUT stosslueften = %d ", stosslueften); // Funktion Intensivlüften: Lüfterstufe um 1 erhöhen, wenn z.B. CO2 Sensor eine Verschlechterung der Luftqualität feststellt intensivlueften = (int)getinput(6); if (DEBUG_START) printf("DEBUG: Helios KWL Modbus TCP - Watchpoint 11 - INPUT intensivlueften = %d ", intensivlueften); // aktuelle Lüfterstufe einstellen if (stosslueften == 1) { luefterStufe = 4; } else { // Lüfterstufe im erlaubten Wertebereich einstellen if ((intensivlueften == 1) && (luefterStufeNormal < 4)) { luefterStufe = luefterStufeNormal+1; } else { luefterStufe = luefterStufeNormal; } } // Bypasstemperatur aus Loxone lesen (10...40) bypassTemp = (int)getinput(3); // Wertebereich ggf. anpassen if (bypassTemp > 40) bypassTemp = 40; if (bypassTemp < 10) bypassTemp = 10; if (DEBUG_START) printf("DEBUG: Helios KWL Modbus TCP - Watchpoint 12 - INPUT bypassTemp = %d ", bypassTemp); // Min. Aussentemperatur für Bypass aus Loxone lesen (0...40) minAussenBypassTemp = (int)getinput(4); // Wertebereich ggf. anpassen if (minAussenBypassTemp > 40) minAussenBypassTemp = 40; if (minAussenBypassTemp < 0) minAussenBypassTemp = 0; if (DEBUG_START) printf("DEBUG: Helios KWL Modbus TCP - Watchpoint 13 - INPUT minAussenBypassTemp = %d ", minAussenBypassTemp); // Abfragefrequenz in Sekunden für das Auslesen der Parameter aus Helios KWL pullFreq = (int)getinput(5); if (pullFreq == 0) pullFreq = 10; // Default sind 10 Sekunden if (DEBUG_START) printf("DEBUG: Helios KWL Modbus TCP - Watchpoint 14 - INPUT pullFreq = %d ", pullFreq); // aktuelle Zeit ermitteln currentTime = getcurrenttime(); // Meldung mit Summe der Fehler im Zeitintervall, wenn zu viele Fehler auftraten if (currentTime > errorTime + errorInterval) { if (errorCount > errorMax) printf("ERROR: Helios KWL modbusTCP - %u errors within %u minutes! Only first %u errors logged.", errorCount, (unsigned int)(errorInterval/60), errorMax); errorCount = 0; } if (DEBUG_START) printf("DEBUG: Helios KWL Modbus TCP - Watchpoint 15 - before test of write"); // Werte schreiben und/oder lesen, wenn // entweder das Abfrageintervall erreicht ist // oder sich mind. ein Wert geändert hat if (currentTime>=lastLoopTime+pullFreq || luefterStufe!=luefterStufeLast || bypassTemp!=bypassTempLast || minAussenBypassTemp!=minAussenBypassTempLast) { if (DEBUG_START) printf("DEBUG: Helios KWL Modbus TCP - Watchpoint 16 - before creating TCP stream"); // Stream zur IP-Adresse der Helios KWL (Modbus TCP Gegenstelle) stream = stream_create("/dev/tcp/192.168.128.11/502", 1, 0); if (DEBUG_START) printf("DEBUG: Helios KWL Modbus TCP - Watchpoint 17 - TCP stream created"); if (stream != NULL) { lastLoopTime = currentTime; // neue Lüfterstufe nur schreiben, wenn sich diese geändert hat if (luefterStufe != luefterStufeLast) { if (DEBUG_START) printf("DEBUG: Helios KWL Modbus TCP - Watchpoint 18 - WRITE luefterStufe = %d ", luefterStufe); heliosWriteInt("v00102", luefterStufe); luefterStufeLast = luefterStufe; if (DEBUG_START) printf("DEBUG: Helios KWL Modbus TCP - Watchpoint 19 - luefterStufe = %d written", luefterStufe); } if (DEBUG_START) printf("DEBUG: Helios KWL Modbus TCP - Watchpoint 20 - before setoutput"); setoutput(10, luefterStufeNormal); // Lüfterstufe für Normalbetrieb setoutput(11, luefterStufe); // akutelle Lüfterstufe sleep(50); if (DEBUG_START) printf("DEBUG: Helios KWL Modbus TCP - Watchpoint 21 - setoutput and sleep finished"); // wurde bypassTemp wurde in Loxone geändert? if (bypassTemp != bypassTempLast) { if (DEBUG_START) printf("DEBUG: Helios KWL Modbus TCP - Watchpoint 22 - WRITE bypassTemp = %d ", bypassTemp); heliosWriteInt("v01035", bypassTemp); bypassTempLast = bypassTemp; if (DEBUG_START) printf("DEBUG: Helios KWL Modbus TCP - Watchpoint 23 - bypassTemp = %d written", bypassTemp); } sleep(50); // wurde minAussenBypassTemp wurde in Loxone geändert? if (minAussenBypassTemp != minAussenBypassTempLast) { if (DEBUG_START) printf("DEBUG: Helios KWL Modbus TCP - Watchpoint 24 - WRITE minAussenBypassTemp = %d ", minAussenBypassTemp); heliosWriteInt("v01036", minAussenBypassTemp); minAussenBypassTempLast = minAussenBypassTemp; if (DEBUG_START) printf("DEBUG: Helios KWL Modbus TCP - Watchpoint 25 - minAussenBypassTemp = %d written", minAussenBypassTemp); } sleep(50); if (DEBUG_START) printf("DEBUG: Helios KWL Modbus TCP - Watchpoint 26 - READ tempAussenluft"); // Temperatur Aussenluft auslesen, char[7] if (heliosReadFloat(&floatValue, "v00104", 7)) { tempAussenluft = floatValue; setoutput(0, tempAussenluft); } sleep(50); if (DEBUG_START) printf("DEBUG: Helios KWL Modbus TCP - Watchpoint 27 - READ tempAussenluft"); // Temperatur Zuluft auslesen, char[7] if (heliosReadFloat(&floatValue, "v00105", 7)) { tempZuluft = floatValue; setoutput(1, tempZuluft); } sleep(50); if (DEBUG_START) printf("DEBUG: Helios KWL Modbus TCP - Watchpoint 28 - READ tempFortluft"); // Temperatur Fortluft auslesen, char[7] if (heliosReadFloat(&floatValue, "v00106", 7)) { tempFortluft = floatValue; setoutput(2, tempFortluft); } sleep(50); if (DEBUG_START) printf("DEBUG: Helios KWL Modbus TCP - Watchpoint 29 - READ tempAbluft"); // Temperatur Abluft auslesen, char[7] if (heliosReadFloat(&floatValue, "v00107", 7)) { tempAbluft = floatValue; setoutput(3, tempAbluft); } sleep(50); if (DEBUG_START) printf("DEBUG: Helios KWL Modbus TCP - Watchpoint 30 - READ tempAussenluft"); // Drehzahl Zuluft (rpm) auslesen, char[4] if (heliosReadFloat(&floatValue, "v00348", 4)) { rpmZuluft = floatValue; setoutput(4, rpmZuluft); } sleep(50); if (DEBUG_START) printf("DEBUG: Helios KWL Modbus TCP - Watchpoint 31 - READ rpmAbluft"); // Drehzahl Abluft (rpm) auslesen, char[4] if (heliosReadFloat(&floatValue, "v00349", 4)) { rpmAbluft = floatValue; setoutput(5, rpmAbluft); } sleep(50); if (DEBUG_START) printf("DEBUG: Helios KWL Modbus TCP - Watchpoint 32 - READ bypass"); // Status Bypass auslesen, char[1], undokumentiert! if (heliosReadFloat(&floatValue, "v02119", 1)) { bypass = floatValue; setoutput(6, bypass); } sleep(50); if (DEBUG_START) printf("DEBUG: Helios KWL Modbus TCP - Watchpoint 33 - READ anzahlFehler"); // AnzahlFehler auslesen, char[2] if (heliosReadInt(&intValue, "v01300", 2)) { anzahlFehler = intValue; setoutput(7, (float)anzahlFehler); } sleep(50); if (DEBUG_START) printf("DEBUG: Helios KWL Modbus TCP - Watchpoint 34 - READ anzahlWarnungen"); // AnzahlWarnungen auslesen, char[1] if (heliosReadInt(&intValue, "v01301", 1)) { anzahlWarnungen = intValue; setoutput(8, (float)anzahlWarnungen); } sleep(50); if (DEBUG_START) printf("DEBUG: Helios KWL Modbus TCP - Watchpoint 35 - READ anzahlInfo"); // AnzahlInfo auslesen, char[1] if (heliosReadInt(&intValue, "v01302", 1)) { anzahlInfo = intValue; setoutput(9, (float)anzahlInfo); } sleep(50); if (DEBUG_START) printf("DEBUG: Helios KWL Modbus TCP - Watchpoint 36 - READ real luefterstufe"); // Lüfterstufe auslesen, char[1] if (heliosReadInt(&intValue, "v00102", 1)) { setoutput(11, intValue); } sleep(50); if (DEBUG_START) printf("DEBUG: Helios KWL Modbus TCP - Watchpoint 37 - READ artikelBez"); // Artikelbezeichnung auslesen, char[31] if (heliosReadString(&artikelBez, "v00000", 31)) { setoutputtext(0, artikelBez); } sleep(50); if (DEBUG_START) printf("DEBUG: Helios KWL Modbus TCP - Watchpoint 38 - before close stream"); stream_close(stream); } else { sprintf(errorMessage, "ERROR: Helios KWL modbusTCP - unable to create TCP stream"); printError(errorMessage); sleep(500); } } if (DEBUG_START) { printf("DEBUG: Helios KWL Modbus TCP - Watchpoint 39 - end of main loop and startup debugging"); DEBUG_START = 0; } sleep(500); }