Sungo SXL MQTT Anbindung

Aus TippvomTibb
Zur Navigation springen Zur Suche springen

Allgemeines

Nach einer kleinen Pause (5 Monate) versuche ich die SunGO SXL Anbindung aus dem Versuchstadium zu befreien und mal alles ordentlich zu machen. Die Hardware kommt in eine Gehäuse und die Software soll ihren ersten Stresztest bestehen.

Ausgangssituation

SunGO Raspi Interface roh.jpg

Abgesehen von der SunGo SXL Steuerung sind aktuelle noch ein RasPi 3B, ein CP2102 USBSER und Arduino NANO und 4 DS18B20 im Einsatz. Das Netzteil ist nur eine Übergangslösung. Am liebsten wuerde ich eine MiniUSV mit ins Gehaeuse packen. Mal schauen was es da von der Stange gibt. Auf dem RasPi laeuft derzeit nur eine Software die die Aufgabe hat die Daten des SunGo-Interfaces und die Temperaturen der 4 DS18B20 (Vor-/Ruecklauf-Heizkreis1 und Vor-/Ruecklauf-Fuszbodenheizung) einzulesen und per MQTT an den FHEM-Broker (MQTT-Server) zu liefern.


Software

Sungo_MQTT

Sungo_MQTT.c
  1 #include <string.h>
  2 #include <stdlib.h>
  3 #include <stdio.h>
  4 #include <unistd.h>
  5 #include <stdbool.h>
  6 #include <time.h>
  7 #include <sys/types.h>
  8 #include <sys/stat.h>
  9 #include <fcntl.h>
 10 #include <termios.h> // termios is the API that is in general recommended for serial I/O in Unix.
 11 #include "MQTTClient.h"
 12 
 13 // MQTT defines
 14 #define ADDRESS     "tcp://192.168.178.12:1883"
 15 //#define ADDRESS     "tcp://localhost:1883"
 16 #define CLIENTID    "SunGo"
 17 //#define TOPIC       "MQTTExamples"
 18 //#define PAYLOAD     "Hello World!"
 19 #define QOS         0
 20 #define TIMEOUT     10000L
 21 
 22 // SERCOM defines
 23 #define BAUDRATE B115200
 24 #define MODEMDEVICE "/dev/ttyS1"
 25 #define _POSIX_SOURCE 1 /* POSIX compliant source */
 26 #define FALSE 0
 27 #define TRUE 1
 28 
 29 
 30 // SunGo defines
 31 #define DUMMY       0
 32 #define TEMPERATUR  1   // °C
 33 #define STRAHLUNG   2   // W/m²     
 34 #define SPEICHER1    7   // kwh
 35 #define DATUM      8
 36 #define UHRZEIT     9
 37 #define PROZENT     10  // %
 38 #define FUNKTION1   11
 39 #define FUNKTION2   12
 40 #define SPEICHER2    15 // h
 41 #define VOLUMENSTROM 19 // l/min
 42 
 43 
 44 volatile int STOP=FALSE; // braucht man eigentlich nicht
 45 
 46 
 47 // globale Variablen 
 48 char datum[11];
 49 char uhrzeit[7];
 50 char temperatur[25];
 51 char prozent[7];
 52 char strahlung[8];
 53 char fehler[17];
 54 char stunden[8];
 55 char dummy[7];
 56 
 57 // hexadezimale Darstellung in dezimale umstellen
 58 unsigned int hextodec(char hex[10]){
 59   unsigned int decimal=0;
 60   unsigned int length=0;
 61   unsigned int base = 1;
 62   int i=0;
 63   
 64   while(hex[i]!='\0'){
 65     i++;
 66   }
 67   length=i;
 68 
 69     //printf("hextodec %s\n\r",hex);
 70     //printf("length %d\n\r",length);
 71   
 72   for(i = length-1; i >= 0; i--){
 73     //printf("i: %d\n\r",i); 
 74     if (i<0)i=0;
 75       if(hex[i] >= '0' && hex[i] <= '9'){
 76         decimal += ((unsigned char)hex[i] - 48) * base;
 77         base *= 16;
 78       }
 79       else if(hex[i] >= 'A' && hex[i] <= 'F'){
 80         decimal += ((unsigned char)hex[i] - 55) * base;
 81         base *= 16;
 82       }
 83       else if(hex[i] >= 'a' && hex[i] <= 'f'){
 84         decimal += ((unsigned char)hex[i] - 87) * base;
 85         base *= 16;
 86       }
 87   }
 88   //printf("decimal %d\n\r",decimal);
 89   return decimal;
 90 }
 91 
 92 void delay(int number_of_seconds) // wird doch nicht gebraucht
 93 {
 94     // Converting time into milli_seconds
 95     int milli_seconds = 1000 * number_of_seconds;
 96   
 97     // Storing start time
 98     clock_t start_time = clock();
 99   
100     // looping till required time is not achieved
101     while (clock() < start_time + milli_seconds)
102         ;
103 }
104 
105 // allgemeine Funktion; die verschiedenen Typen werden ueber switch case angesprochen  
106 void formatieren(unsigned char typ,unsigned char lowbyte,unsigned char highbyte){
107     char data[10]="";   // dient der Aufnahme des zu wandelnden Wertes gebildet aus highbyte und lowbyte
108     char tmp[3]="";     // hilfvariable
109     char formatted[25]="";
110     char dataStr[5]="";
111     unsigned int dataInteger;
112     float dataFloat;
113     unsigned int dataStunden, dataMinuten;
114 
115     //memset(formatted,'\0',sizeof(formatted));
116     //memset(data,'\0',sizeof(data)); 
117     //memset(tmp,'\0',sizeof(tmp)); 
118     
119     // aus den beiden Einzelwerten wird der "Gesamtwert in der "richtigen" Reihenfolge gebildet
120     sprintf(tmp,"%02X",highbyte);
121     strcpy(data,tmp);
122     sprintf(tmp,"%02X",lowbyte);
123     strcat(data,tmp);
124  
125     //printf("Data: %s\n",data);
126     
127     dataInteger=hextodec(data);
128  
129    switch(typ) {
130         case DUMMY:         if(TRUE){
131                             strcpy(dummy,data);
132                         }
133                         else{
134                             strcpy(dummy,"Error!");
135                         } 
136                         break;
137     case DATUM:         sprintf(dataStr,"%d",dataInteger);
138                         strcpy(formatted,"00.00.2021");
139                         if(strlen(dataStr)==3){
140                             formatted[0]=dataStr[1];  
141                             formatted[1]=dataStr[2]; 
142                             formatted[4]=dataStr[0]; 
143                             strcpy(datum,formatted);
144                         }
145                         else if(strlen(dataStr)==4){
146                             formatted[0]=dataStr[2];  
147                             formatted[1]=dataStr[3]; 
148                             formatted[3]=dataStr[0]; 
149                             formatted[4]=dataStr[1]; 
150                             strcpy(datum,formatted);
151                         }   
152                         else{
153                             strcpy(datum,"Error!");
154                         }
155                         break;
156         case UHRZEIT:       dataStunden=(unsigned int)dataInteger/60;
157                         dataMinuten=dataInteger-(dataStunden*60);
158                         if(TRUE){ // hier koennte man die gueltig der stunden und minuten noch testen 
159                             sprintf(formatted,"%02d:%02d",dataStunden,dataMinuten);
160                             strcpy(uhrzeit,formatted);
161                         }
162                         else{
163                             strcpy(uhrzeit,"Error!");
164                         }
165                         break;
166     case TEMPERATUR:   dataFloat=(float)dataInteger/10.0;
167                         if(TRUE){
168                             //sprintf(formatted,"%5.1f °C (%d)     ",dataFloat,dataInteger);
169                             sprintf(formatted,"%5.1f °C",dataFloat);
170                             strcpy(temperatur,formatted);
171                         }
172                         else{
173                             strcpy(temperatur,"Error!\0");
174                         }
175                         break;
176     case SPEICHER2:     if(TRUE){
177                             sprintf(formatted,"%dh",dataInteger);
178                             strcpy(stunden,formatted);
179                         }
180                         else{
181                             strcpy(stunden,"Error!");
182                         }
183                         break;
184     case PROZENT:       if(TRUE){
185                             sprintf(formatted,"%3d%%",dataInteger);
186                             strcpy(prozent,formatted);
187                         }
188                         else{
189                             strcpy(prozent,"Error!");
190                         }
191                         break;     
192     case STRAHLUNG:     if(TRUE){
193                             sprintf(formatted,"%d W/m²    ",dataInteger);
194                             strcpy(strahlung,formatted);
195                         }
196                         else{
197                             strcpy(strahlung,"Error!\0");
198                         }
199                         break; 
200         default:            printf("Type Error (unknown)\n"); break;
201     } 
202     
203      return;
204 }
205 
206 int main(int argc, char **argv)
207 {   int i; // Schleifenzaehler allgemein
208     unsigned int state=0, syncstate=0;  // state 10,20,30 oder 31 entsprechend 0100, 0200, 0300, 0301
209     unsigned int dsIndex, dfIndex;  // Zeiger auf Datensatz und Datenfeld
210     unsigned long zeitzaehler=0;    // Ausgabe verlangsamen 500000 etwa 1 Minute
211    
212     unsigned char buf[1];
213     unsigned char merker[6];
214     unsigned char datensatz[100];
215     unsigned char datenfeld[100];
216     unsigned char datenfelder[50][16];
217     bool printflag;
218 
219     // MQTT Vorbereitung
220     MQTTClient client;
221     MQTTClient_connectOptions conn_opts = MQTTClient_connectOptions_initializer;
222     MQTTClient_message pubmsg = MQTTClient_message_initializer;
223     MQTTClient_deliveryToken token;
224     int mqttrc;
225     char topic[255]; // zur Aufnahme Werte unterhalb des basetopic
226     char *basetopic = "Haus/Heizung/Solaranlage/SunGo/";
227     
228     // SerCOM Vorbereitung
229     struct termios oldtio,newtio;
230     int fd; // FileDescriptor SerCOM
231     int res; // SerCOM Einlesekontrolle
232     
233     // MQTT Cleint Struktur anlegen
234     if ((mqttrc = MQTTClient_create(&client, ADDRESS, CLIENTID, MQTTCLIENT_PERSISTENCE_NONE, NULL)) != MQTTCLIENT_SUCCESS){
235          printf("Failed to create mqttclient, return code %d\n", mqttrc);
236          exit(EXIT_FAILURE);
237     }
238 
239     // wenn in Kommandozeile die Schnittstelle mitgegeben wird, dann nimm diese sonst versuche default
240     if (argc > 1)
241         fd = open(argv[1], O_RDWR | O_NOCTTY );
242     else
243         fd = open(MODEMDEVICE, O_RDWR | O_NOCTTY );
244     if (fd <0) {
245         perror(MODEMDEVICE); 
246         exit(-1); 
247     }
248 
249     // sichere die aktuellen Einstellungen
250     tcgetattr(fd,&oldtio); 
251     
252     // baue die neuen Einstellungen zusammen
253     bzero(&newtio, sizeof(newtio));
254     newtio.c_cflag = BAUDRATE |  CS8 | CLOCAL | CREAD;
255     newtio.c_iflag = IGNPAR | IXON | IXOFF;
256     newtio.c_oflag = 0;
257     newtio.c_lflag = 0; 
258     newtio.c_cc[VTIME]    = 0;   /* inter-character timer unused */
259     newtio.c_cc[VMIN]     = 1;   /* blocking read until 1 chars received */
260 
261     tcflush(fd, TCIFLUSH);
262     tcsetattr(fd,TCSANOW,&newtio);
263    
264     setvbuf(stdout, (char *)NULL, _IONBF, 0);
265     
266      while (STOP==FALSE) {   // Endlosschleife 
267  
268          res = read(fd,buf,1);   
269   
270         //sobald ein AA kommt beginne die Beobachtung
271         if ((buf[0]==170) && (syncstate==5)){
272             syncstate=0;
273         }
274         if ((buf[0]==170) && (syncstate==0)){
275             syncstate=1;
276             merker[0]=buf[0];
277         }
278         if ((buf[0]==85) && (syncstate==1)){
279             syncstate=2;
280             merker[1]=buf[0];
281         }
282         if ((buf[0]==85) && (syncstate==2)){
283             syncstate=3;
284             merker[2]=buf[0];
285         }
286         if ((buf[0]==170) && (syncstate==3)){
287             syncstate=4;
288             merker[3]=buf[0];
289             state=0;
290         }
291         if (((buf[0]==1)||(buf[0]==2)||(buf[0]==3)) && (merker[0]==170) && (merker[1]==85) && (merker[2]==85) && (merker[3]==170) && (syncstate==4)){
292             merker[4]=buf[0];
293             syncstate=5;
294         }   
295  
296  
297         if ((buf[0]==0) && (merker[4]==1) && (merker[0]==170) && (merker[1]==85) && (merker[2]==85) && (merker[3]==170) && (syncstate==5)){
298             state=10; // state 10 bereit fuer 0100
299             printflag=FALSE;
300             for(i=0;i<5;i++)merker[i]=0;
301  
302         }
303         if ((buf[0]==0) && (merker[4]==2) && (merker[0]==170) && (merker[1]==85) && (merker[2]==85) && (merker[3]==170) && (syncstate==5)){
304             state=20; // state 10 bereit fuer 0100
305             printflag=FALSE;
306             for(i=0;i<5;i++)merker[i]=0;
307      
308         }
309         if ((buf[0]==0) && (merker[4]==3) && (merker[0]==170) && (merker[1]==85) && (merker[2]==85) && (merker[3]==170) && (syncstate==5)){
310             state=30; // state 10 bereit fuer 0100
311             printflag=FALSE;
312             for(i=0;i<5;i++)merker[i]=0;
313             dsIndex=0;
314  
315         }
316         if ((buf[0]==1) && (merker[4]==3) && (merker[0]==170) && (merker[1]==85) && (merker[2]==85) && (merker[3]==170) && (syncstate==5)){
317             state=31; // state 10 bereit fuer 0100
318             printflag=FALSE;
319             for(i=0;i<5;i++)merker[i]=0;
320             dfIndex=0;
321 
322         }
323         
324  
325                 
326         if ((state==10) && printflag) {
327 
328         }
329         else if ((state==20) && printflag) {
330  
331         }
332         else if ((state==30)  && printflag){
333  
334             if(dsIndex<100)datensatz[dsIndex++]=buf[0];
335         }
336         else if ((state==31) && printflag) {
337  
338             if(dfIndex<100)datenfeld[dfIndex++]=buf[0];
339         }
340         else{
341          //printf("syncstate: %d, state: %d, %X\n\r",syncstate,state,buf[0]);   
342         }
343 
344         printflag=TRUE;
345  
346         if (zeitzaehler==10000){
347             conn_opts.keepAliveInterval = 20;
348             conn_opts.cleansession = 1;
349             // Baue MQTT Verbindung auf
350             if ((mqttrc = MQTTClient_connect(client, &conn_opts)) != MQTTCLIENT_SUCCESS){
351                 printf("Failed to connect, return code %d\n", mqttrc);
352                 exit(EXIT_FAILURE);
353             }
354             //Datum
355             formatieren(DATUM,datensatz[2],datensatz[3]);
356             printf("Datum: %s\n",datum); 
357             strcpy(topic,basetopic);
358             strcat(topic,"Datum");
359             pubmsg.payload = datum;
360             pubmsg.payloadlen = (int)strlen(datum);
361             pubmsg.qos = QOS;
362             pubmsg.retained = 0;
363             if ((mqttrc = MQTTClient_publishMessage(client, topic, &pubmsg, &token)) != MQTTCLIENT_SUCCESS){
364                 printf("Failed to publish message, return code %d\n", mqttrc);
365                 exit(EXIT_FAILURE);
366             }else{
367                 printf("Message published, return code %d\n", mqttrc); 
368             }  
369             //Uhrzeit
370             formatieren(UHRZEIT,datensatz[4],datensatz[5]);
371             printf("Uhrzeit: %s\n",uhrzeit);
372             strcpy(topic,basetopic);            
373             strcat(topic,"Uhrzeit");            
374             pubmsg.payload = uhrzeit;
375             pubmsg.payloadlen = (int)strlen(uhrzeit);
376             pubmsg.qos = QOS;
377             pubmsg.retained = 0;
378             if ((mqttrc = MQTTClient_publishMessage(client, topic, &pubmsg, &token)) != MQTTCLIENT_SUCCESS){
379                 printf("Failed to publish message, return code %d\n", mqttrc);
380                 exit(EXIT_FAILURE);
381             }else{
382                 printf("Message published, return code %d\n", mqttrc); 
383             } 
384             //Kollektor
385             formatieren(TEMPERATUR,datensatz[6],datensatz[7]);
386             printf("Kollektor: %s\n",temperatur);
387             strcpy(topic,basetopic);            
388             strcat(topic,"Kollektor/Temperatur");
389             pubmsg.payload = temperatur;
390             pubmsg.payloadlen = (int)strlen(temperatur);
391             pubmsg.qos = QOS;
392             pubmsg.retained = 0;
393             if ((mqttrc = MQTTClient_publishMessage(client, topic, &pubmsg, &token)) != MQTTCLIENT_SUCCESS){
394                 printf("Failed to publish message, return code %d\n", mqttrc);
395                 exit(EXIT_FAILURE);
396             }else{
397                 printf("Message published, return code %d\n", mqttrc); 
398             }
399              //Speicher unten
400             formatieren(TEMPERATUR,datensatz[8],datensatz[9]);
401             printf("Speicher unten: %s\n",temperatur);
402             strcpy(topic,basetopic);            
403             strcat(topic,"Speicher/unten/Temperatur");
404             pubmsg.payload = temperatur;
405             pubmsg.payloadlen = (int)strlen(temperatur);
406             pubmsg.qos = QOS;
407             pubmsg.retained = 0;
408             if ((mqttrc = MQTTClient_publishMessage(client, topic, &pubmsg, &token)) != MQTTCLIENT_SUCCESS){
409                 printf("Failed to publish message, return code %d\n", mqttrc);
410                 exit(EXIT_FAILURE);
411             }else{
412                 printf("Message published, return code %d\n", mqttrc); 
413             }
414             //Speicher oben
415             formatieren(TEMPERATUR,datensatz[10],datensatz[11]);
416             printf("Speicher oben: %s\n",temperatur);
417             strcpy(topic,basetopic);            
418             strcat(topic,"Speicher/oben/Temperatur");
419             pubmsg.payload = temperatur;
420             pubmsg.payloadlen = (int)strlen(temperatur);
421             pubmsg.qos = QOS;
422             pubmsg.retained = 0;
423             if ((mqttrc = MQTTClient_publishMessage(client, topic, &pubmsg, &token)) != MQTTCLIENT_SUCCESS){
424                 printf("Failed to publish message, return code %d\n", mqttrc);
425                 exit(EXIT_FAILURE);
426             }else{
427                 printf("Message published, return code %d\n", mqttrc); 
428             } 
429             //Rücklaufanhebung T7
430             formatieren(TEMPERATUR,datensatz[18],datensatz[19]);
431             printf("Rücklaufanhebung T7: %s\n",temperatur);
432             strcpy(topic,basetopic);            
433             strcat(topic,"Rücklaufanhebung/T7/Temperatur");
434             pubmsg.payload = temperatur;
435             pubmsg.payloadlen = (int)strlen(temperatur);
436             pubmsg.qos = QOS;
437             pubmsg.retained = 0;
438             if ((mqttrc = MQTTClient_publishMessage(client, topic, &pubmsg, &token)) != MQTTCLIENT_SUCCESS){
439                 printf("Failed to publish message, return code %d\n", mqttrc);
440                 exit(EXIT_FAILURE);
441             }else{
442                 printf("Message published, return code %d\n", mqttrc); 
443             }              
444             //Rücklaufanhebung T8
445             formatieren(TEMPERATUR,datensatz[20],datensatz[21]);
446             printf("Rücklaufanhebung T8: %s\n",temperatur);
447             strcpy(topic,basetopic);            
448             strcat(topic,"Rücklaufanhebung/T8/Temperatur");
449             pubmsg.payload = temperatur;
450             pubmsg.payloadlen = (int)strlen(temperatur);
451             pubmsg.qos = QOS;
452             pubmsg.retained = 0;
453             if ((mqttrc = MQTTClient_publishMessage(client, topic, &pubmsg, &token)) != MQTTCLIENT_SUCCESS){
454                 printf("Failed to publish message, return code %d\n", mqttrc);
455                 exit(EXIT_FAILURE);
456             }else{
457                 printf("Message published, return code %d\n", mqttrc); 
458             } 
459            //Ausgang 1
460             formatieren(PROZENT,datensatz[32],0);
461             printf("Ausgang 1: %s\n",prozent);
462             strcpy(topic,basetopic);            
463             strcat(topic,"Ausgang/1/Prozent");
464             pubmsg.payload = prozent;
465             pubmsg.payloadlen = (int)strlen(prozent);
466             pubmsg.qos = QOS;
467             pubmsg.retained = 0;
468             if ((mqttrc = MQTTClient_publishMessage(client, topic, &pubmsg, &token)) != MQTTCLIENT_SUCCESS){
469                 printf("Failed to publish message, return code %d\n", mqttrc);
470                 exit(EXIT_FAILURE);
471             }else{
472                 printf("Message published, return code %d\n", mqttrc); 
473             }  
474            //Ausgang 6
475             formatieren(PROZENT,datensatz[37],0);
476             printf("Ausgang 6: %s\n",prozent);
477             strcpy(topic,basetopic);            
478             strcat(topic,"Ausgang/6/Prozent");
479             pubmsg.payload = prozent;
480             pubmsg.payloadlen = (int)strlen(prozent);
481             pubmsg.qos = QOS;
482             pubmsg.retained = 0;
483             if ((mqttrc = MQTTClient_publishMessage(client, topic, &pubmsg, &token)) != MQTTCLIENT_SUCCESS){
484                 printf("Failed to publish message, return code %d\n", mqttrc);
485                 exit(EXIT_FAILURE);
486             }else{
487                 printf("Message published, return code %d\n", mqttrc); 
488             }  
489             // Speicherstunden
490             formatieren(SPEICHER2,datensatz[50],datensatz[51]);
491             printf("Speicher 2: %s\n",stunden);
492             strcpy(topic,basetopic);            
493             strcat(topic,"Speicher/Stunden");
494             pubmsg.payload = stunden;
495             pubmsg.payloadlen = (int)strlen(stunden);
496             pubmsg.qos = QOS;
497             pubmsg.retained = 0;
498             if ((mqttrc = MQTTClient_publishMessage(client, topic, &pubmsg, &token)) != MQTTCLIENT_SUCCESS){
499                 printf("Failed to publish message, return code %d\n", mqttrc);
500                 exit(EXIT_FAILURE);
501             }else{
502                 printf("Message published, return code %d\n", mqttrc); 
503             }  
504     /*    printf("Waiting for up to %d seconds for publication of %s\n"
505                 "on topic %s for client with ClientID: %s\n",
506                 (int)(TIMEOUT/1000), PAYLOAD, TOPIC, CLIENTID);
507         rc = MQTTClient_waitForCompletion(client, token, TIMEOUT);
508         printf("Message with delivery token %d delivered\n", token);
509     */
510             if ((mqttrc = MQTTClient_disconnect(client, 10000)) != MQTTCLIENT_SUCCESS){
511                 printf("Failed to disconnect, return code %d\n", mqttrc);
512             }
513             zeitzaehler=0;
514         }
515         zeitzaehler++;
516         if (zeitzaehler%100000==0)printf("%d\n",zeitzaehler); // Lebenszeichen
517       
518     }
519     
520     close(fd);
521     
522     tcsetattr(fd,TCSANOW,&oldtio);
523    
524     MQTTClient_destroy(&client);
525     
526     return EXIT_SUCCESS;
527 }