SolarMax Datenlogger

Allgemein über Datenlogger, Kommunikation, DFÜ

Moderator: Mod-Team

 

3.83 (6 Bewertungen) | Zum Bewerten bitte anmelden

Re: SolarMax Datenlogger

Beitragvon [Michi] » 13.10.2012, 17:10

ich habe mal die Werte: Logavginterval=3,Waitinterval=50 in der smw-logger.conf geändert.
Jetzt versuch er in die Datenbank zu schreiben. Im Terminalfenster kommt jedoch :

root@LINUX:~# /usr/local/bin/smw-logger /usr/local/etc/smw-logger.conf
You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near ')' at line 1: Success
root@LINUX:~#

:roll:
[Michi]
Öfters hier
Öfters hier
 
Beiträge: 14
Registriert: 06.07.2012, 22:22
Info: Interessent

Re: SolarMax Datenlogger

Beitragvon sollner11 » 13.10.2012, 17:34

von dem 5-Sekunden-Problem hat solarfan10 schon berichtet:

solarfan10 hat geschrieben:erstmal ein Danke an die Ersteller dieses Datenloggers, den ich mittlerweile auch auf meinem Windows-System rudimentär zum Laufen gebracht habe. Und zwar mit einem Solarmax 13MT.
Was mich dabei fast zur Verzweiflung gebracht hat, ist die Tatsache, dass das Logger-PHP-Skript und auch nacher das Logger-C-Programm ständig Kommunikationsfehler gemeldet haben. Bis ich dahinter gekommen bin, dass der Solarmax nach knapp 5 Sekunden die Verbindung von sich aus trennt. Just bevor der Logger mit den Standardeinstellungen wieder das nächste Kommando abschickt. Erst als ich dann die Intervallzeit auf 4 Sekunden gedrückt habe, klappte es. (Hattet Ihr mit euren Solarmaxen ähnliche Erfahrungen?)
Allerdings hat das den Nachteil, dass dann nur noch der Datenlogger an den Solarmax kommt. Es wird ja scheinbar nur eine Verbindung zu einer Zeit unterstützt. Das möchte ich aber eigentlich gar nicht, ich will auch noch mit der SolarMax-App auf dem iPhone connecten, bzw. mit anderen Datenloggern. Daher denke ich darüber nach, die Verbindung für jede Anfrage vorher aufzubauen und anschließend wieder abzubauen. Da ich den Thread hier noch nicht ganz durch habe, eine Frage an Euch: Hattet Ihr so etwas auch schon versucht? Gab es da Probleme?

und
solarfan10 hat geschrieben:ja, die Zeit ist auch bei mir knapp. Ich habe jetzt den logger soweit umgebaut, dass er die Verbindung jedesmal neu aufbaut und dann wieder kappt. Die Schleife habe ich drin gelassen - evtl. muss er ja die db-Verbindung neu aufbauen.

Das Telegramm aus einer meiner letzten Posts hat noch die falsche checksumme, ausserdem musste ich die regex noch anpassen, da die neuen Feldbezeichner teils vierstellig sind. Das läuft auch. Die Db habe ich um einige Felder erweitert. Werde dann noch eine zweite Abfrage für die restlichen Felder programmieren, wenn die Tests für den jetzigen Stand positiv sind.


und zagibu schrieb davon, dass er gar nicht über einen Intervall mithält

hast du die Datenbank in der install.sh auch angepasst? (gekürzt)
oder muss man das nicht?
sollner11
Fleißiges Mitglied
Fleißiges Mitglied
 
Beiträge: 454
Registriert: 05.05.2012, 12:15
PV-Anlage [kWp]: 19,11
Info: Betreiber

Re: SolarMax Datenlogger

Beitragvon zagibu » 14.10.2012, 11:46

Ach so, ja das könnte tatsächlich eines der Probleme sein. Die alten Solarmaxe machen das nämlich nicht, da bleibt die Verbindung offen. Vielleicht mal Solarfan anschreiben für seine modifizierte Version, welche die Verbindung für jede Anfrage neu aufbaut.

Wenn du im debug.log die Meldung "No match" drin hast, dann stimmt das Regex Pattern nicht mit der Antwort überein. Hast du mal geschaut was auf Zeile 23 ist, hast du da wirklich noch ein sprintf drin? Vielleicht fehlt dir auch eine Variablendeklaration.
zagibu
Vielschreiber
Vielschreiber
 
- Threadstarter -
 
Beiträge: 195
Registriert: 27.07.2010, 16:31
PV-Anlage [kWp]: 3
Info: Betreiber

Re: SolarMax Datenlogger

Beitragvon [Michi] » 14.10.2012, 19:29

Hallo zagibu,
Du hast Recht, der 5 sek Intervall ist das Problem.

Solarfan, kannst Du uns Deine modifizierte Version zur Verfügung stellen ?

Gruss
[Michi]
[Michi]
Öfters hier
Öfters hier
 
Beiträge: 14
Registriert: 06.07.2012, 22:22
Info: Interessent

Re: SolarMax Datenlogger

Beitragvon solarfan10 » 15.10.2012, 00:10

Hallo,

hab's schon sollner geschrieben - meine Version ist auf C# unter Windows portiert, die habe ich sollner11 auch schon gegeben. Ich selber kann mit dem orignal-code nur unter cygwin compilieren/debuggen und da der cygwin debugger ständig abschmiert, hat mich das so aufgeregt dass ich beschlossen hatte, nach C# zu portieren und dort weiter zu entwickeln. Tut mir leid, aber den Aufwand, zurück zu Linux/C zu gehen, tu ich mir nicht an. Den C#-Code kann ich aber gerne nochmal posten.

Prinzipiell ist die Umstellung aber ganz einfach. Michi, Du musst nur die Code-Stelle zum Öffnen der TCP-IP-Verbindung zum Solarmax verschieben, und zwar in die Schleife zum Daten abholen. Ich habe da einiges umgestellt, aber hier das Prinzip:
Code: Alles auswählen
                                    // Open connection
                                    if (!open_connection())
                                    {
                                        error_retry("ERROR opening connection, retry");
                                        failure_flag = 1;
                                        break;
                                    }

                                    // Send message 1
                                    if (!SendMessage(message1))
                                        break;

                                    // Read answer 1
                                    string bufferString1 = ReadMessage();
                                    if (bufferString1 == null)
                                        break;  // error occured during read

                                    string bufferString2 = null;
                                    if (isMTseries)
                                    {
                                        // Send message 2
                                        if (!SendMessage(message2))
                                            break;

                                        // Read answer 2
                                        bufferString2 = ReadMessage();
                                        if (bufferString2 == null)
                                            break;  // error occured during read
                                    }

                                    // and close connection to allow other clients to communicate
                                    try
                                    {
                                        close_connection();
                                    }
                                    catch (SocketException)
                                    {
                                        // ignore socket exceptions on close
                                    }



Die Methoden open_connection(), close_connection() und SendMessage() sehen so aus:
Code: Alles auswählen
        bool open_connection() {

#if !SIMULATION
            try {
               // Try to open socket for communication with solarmax
               sockfd = new Socket(AddressFamily.InterNetwork, SocketType.Stream, 0);

               // Try to resolve solarmax address/hostname
                IPAddress[] serverAddresses = Dns.GetHostAddresses(hostaddr);

               // Try to establish a connection with solarmax
                sockfd.Connect(serverAddresses[0], portno);

               // Make socket non-blocking
               set_nonblock(sockfd);
            }
            catch(SocketException e)
            {
                error_retry(e.Message);
              Thread.Sleep(60000);    // ms
              return false;
           }

           if (DEBUG) {
                StringBuilder sb = new StringBuilder();
                sb.AppendFormat("Connected to solarmax ({0}) on port {1}", hostaddr, portno.ToString());
              debug_entry(sb.ToString());
           }
#endif
           return true;
        }

        void close_connection()
        {
           if (sockfd != null)
            {
              sockfd.Shutdown(SocketShutdown.Both);
              sockfd.Close();
           }
        }

        private bool SendMessage(string message1)
        {
            if (DEBUG)
            {
                debug_entry("Sending message: " + message1);
            }

            // Send message
            try
            {
#if !SIMULATION
                byte[] sendBuffer = Encoding.ASCII.GetBytes(message1);
                int sentBytes = sockfd.Send(sendBuffer);
                if (sentBytes != message1.Length)
                {
                    close_connection();
                    error_retry("ERROR sending TCP packet: " + sentBytes.ToString());
                    failure_flag = 1;
                    return false;
                }
#endif
            }
            catch (SocketException ex)
            {
                error_retry(ex.Message);
                failure_flag = 1;
                return false;
            }
            return true;
        }



Ist halt alles .NET und C#, aber das Prinzip sollte klar sein. Ihr müsst ja den open/close code nicht auslagern in funktionen, aber halt zumindest vor und nach dem eigentlichen Telegramm-Senden einbauen. Damit die Verbindung nur ganz kurz offen ist.

Der code der mit dem define SIMULATION abhängig compiliert wird, ist dazu da, dass ich auch im Dunkeln noch testen kann ;). Gerade jetzt ja immer interessanter. bei gesetztem SIMULATION wird nicht kommuniziert und in der Methode ReadMessage dann stattdessen ein Dummy-String zurückgegeben, der ein gültiges Antworttelegramm enthält und den Regexp-Code schön beschäftigt.

Hoffe, das hilft euch weiter.

Viele Grüße
Michael
Jeder fängt mal klein an.
Benutzeravatar
solarfan10
Fleißiges Mitglied
Fleißiges Mitglied
 
Beiträge: 375
Registriert: 22.10.2011, 12:48
PV-Anlage [kWp]: 16,56
Info: Betreiber

Re: SolarMax Datenlogger

Beitragvon sollner11 » 16.10.2012, 09:55

danke solarfan10,

ich fasse das mal für mich zusammen

Erkenntnisse:
Die SolarMax MT-Reihe macht es beim Loggen erforderlich, eine Verbindung aufzubauen, die Werte abzufragen, in die DB einzutragen und die Verbindung wieder zu schließen.

Die Variablen-Namen sind durch solarfan eigentlich klar.

Die machbare Anzahl der Variablen pro Anfrage muss ermittelt werden.

Eventuell sind mehrere Anfragen abzusenden.

Ein Mitteln von Werten über eine gewisse Zeit ist damit außen vor (bzw. muss vom Viewer gemacht werden)

(die Probleme sollten für den Perl-Logger und die C-Varainate von Zagibu gleich gelten)


Fragen:
In welchem Intervall wird dann überhaupt sinnvoll abgefragt?
Ich hatte mal einen thread angefangen, um mir klarzumachen, was alles überhaupt geloggt werden sollte.
http://www.photovoltaikforum.com/datenlogger-f5/was-alles-loggen--t83503.html

Die Auswertung/Visualisierung ist sicher dann sicher auch noch ein Problem, erstmal muss aber der Logger arbeiten.

Lösungen:
solarfan10 = MT3 = aber mit C## und Windows
[Michi] = MT2 = sucht Lösung
sollner11 = MT2 = hofft, Michi findet Lösung :D

Gibt es sonst noch wen mit einem MT?

Gruss
sollner11
Fleißiges Mitglied
Fleißiges Mitglied
 
Beiträge: 454
Registriert: 05.05.2012, 12:15
PV-Anlage [kWp]: 19,11
Info: Betreiber

Re: SolarMax Datenlogger

Beitragvon zagibu » 16.10.2012, 13:08

Wenn ich etwas Zeit habe, werde ich versuchen, den Logger so umzubauen, dass er die Verbindung nach der Abfrage kappt. Ein sinnvolles Log-Intervall ist m.M.n. 1 Minute.
zagibu
Vielschreiber
Vielschreiber
 
- Threadstarter -
 
Beiträge: 195
Registriert: 27.07.2010, 16:31
PV-Anlage [kWp]: 3
Info: Betreiber

Re: SolarMax Datenlogger

Beitragvon solarfan10 » 16.10.2012, 13:40

Hallo sollner11,

die Mittelung ist kein Problem. Das Abfrageintervall ist auch kein Problem. Beides bleibt unverändert bestehen. Es ändert sich nur etwas daran, dass die TCP-Verbindung nicht während der ganzen Programmlaufzeit bestehen bleibt, sondern nur kurz, während der eigentlichen Kommunikation. Dazu muss man nur den Verbindungaufbau direkt vor das Telegrammsenden setzen und den Verbindungsabbau (close) halt direkt nach den Empfang.
Der Code im Anschluß, also die Mittelung, die Verarbeitung zu SQL, die Wartezeit bis zur nächsten Abfrage, usw., bleiben ja gleich. Eine Mittelung reduziert halt die Datenmenge. Wer genug Platz auf seiner Platte hat und einen Server, der die hohe Datenmenge im Viewer dann auch schnell genug visualisieren kann, kann sicher damit leben. Sinnvoll ist es aus meiner Sicht nicht, da der Viewer ja sowieso nur eine horizontale Auflösung von maximal 1 Minute hat (im Standardfall). Die Zwischenwerte fallen damit sowieso unter den Tisch. Womit aus meiner Sicht die sinnvolle Abfragezeit durchaus 5s mit Mittelung bei 1min sein kann.

Viele Grüße
Michael
Jeder fängt mal klein an.
Benutzeravatar
solarfan10
Fleißiges Mitglied
Fleißiges Mitglied
 
Beiträge: 375
Registriert: 22.10.2011, 12:48
PV-Anlage [kWp]: 16,56
Info: Betreiber

Re: SolarMax Datenlogger

Beitragvon zagibu » 17.10.2012, 14:50

Ich glaube, die Mittelung wurde vor allem eingebaut, weil man keine ausgefranste Kurve sehen wollte im Diagramm.
zagibu
Vielschreiber
Vielschreiber
 
- Threadstarter -
 
Beiträge: 195
Registriert: 27.07.2010, 16:31
PV-Anlage [kWp]: 3
Info: Betreiber

Re: SolarMax Datenlogger

Beitragvon sollner11 » 17.10.2012, 15:15

hallo,

inzwischen loggt er, ... mit anpassungen von mich
hier mal meine Dateien:

smw-logger.c
Code: Alles auswählen
    /*
       Simple solarmax logger c program written by zagibu@gmx.ch in July 2010
       This program was originally licensed under WTFPL 2 http://sam.zoy.org/wtfpl/
       It is now licensed under GPLv2 or later http://www.gnu.org/licenses/gpl2.html

       You need the mysql client library files installed to be able to compile it.

       Compile with: gcc -W -Wall -Wextra -Wshadow -Wlong-long -Wformat -Wpointer-arith -rdynamic -pedantic-errors -std=c99 -o smw-logger smw-logger.c -lmysqlclient

       Run with: ./smw-logger /path/to/config-file

       Structure of the config-file:

       Debug=0
       Loginterval=60
       Waitinterval=200
       DBhost=localhost
       DBname=solarmax
       DBtabprefix=log
       DBuser=solaruser
       DBpass=userpassword
       Hostname=192.168.178.35
       Hostport=12345
       NumberOfInverters=1

       You can set DEBUG to 1 to get detailed output in a separate logfile.

       It is recommended to schedule the smw-logger to be started between 5:00 - 6:00 in the
       morning and stopped between 22:00 and 23:00 in the evening (compare with sunshine
       duration). The smw-logger has no built-in facility for logging, so use CRON or similar.

       Example CRON entries:
       00 05 * * * /usr/local/bin/smw-logger /usr/local/etc/smw-logger.conf
       00 23 * * * killall smw-logger

       Sources:
      - http://www.linuxhowtos.org/C_C++/socket.htm
      - http://wwwuser.gwdg.de/~kboehm/ebook/21_kap15_w6.html#49329
      - http://man.cx/setbuf%283%29
      - http://allfaq.org/forums/t/169895.aspx
      - http://dev.mysql.com/tech-resources/articles/mysql-capi-tutorial.html
    */

    #define _GNU_SOURCE

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <strings.h>
    #include <sys/types.h>
    #include <sys/socket.h>
    #include <netinet/in.h>
    #include <netdb.h>
    #include <mysql/mysql.h>
    #include <regex.h>
    #include <time.h>
    #include <unistd.h>
    #include <fcntl.h>
    #include <errno.h>
    #include <pthread.h>

    FILE* error_file = NULL;
    char error_file_name[512];
    char* error_mode = "w";
    FILE* debug_file = NULL;
    char debug_file_name[512];
    char* debug_mode = "w";
    FILE* config_file = NULL;
    char* config_file_name;
    char* config_mode = "r";
    int sockfd, portno, n, log_interval, logavg_interval, result, counter, wait_interval, active_max, nr_of_maxes, DEBUG;
    // Renamed this flag because it is used for connection and regexp problems
    int failure_flag = 0;
    struct sockaddr_in serv_addr;
    struct hostent* server;
    char dbhost[512];
    char dbname[512];
    char dbtabprefix[512];
    char dbuser[512];
    char dbpass[512];
    char hostaddr[512];
    char line[512];
    char* message;
     char* expression = "...=([0-9A-F]*);...=([0-9A-F]*);...=([0-9A-F]*);...=([0-9A-F]*);...=([0-9A-F]*);...=([0-9A-F]*);...=([0-9A-F]*);....=([0-9A-F]*);....=([0-9A-F]*);....=([0-9A-F]*);....=([0-9A-F]*);...=([0-9A-F]*)";

   
    int kdy, kmt, kyr, kt0, tnf, tkk, pac, ud01, ud02, id01, id02, sys;
    char buffer[512], buffer2[512];
    char query[512];
    char* temp;
    regex_t rx;
    regmatch_t* matches;
    MYSQL* connection = NULL;
    // average stuff vars
    int i;

    void error_exit(const char* msg) {
       perror(msg);
       if (error_file != NULL)
          fclose(error_file);
       if (debug_file != NULL)
          fclose(debug_file);
       exit(0);
    }

    void debug_entry(char* msg) {
       time_t timestamp = time(NULL);
       char debug_msg[512];
       char *time_now = ctime(&timestamp);
       time_now[strlen(time_now)-1]=0;
       if (debug_file == NULL)
          error_exit("ERROR writing to debug.log file");
       sprintf(debug_msg, "%s %s", time_now, msg);
       fprintf(debug_file, "%s\n", debug_msg);
    }

    void error_retry(char* msg) {
       time_t timestamp = time(NULL);
       char error_msg[512];
       char *time_now = ctime(&timestamp);
       time_now[strlen(time_now)-1]=0;
       if (error_file == NULL)
          error_exit("ERROR writing to error.log file");
       sprintf(error_msg, "%s %s", time_now, msg);
       fprintf(error_file, "%s\n", error_msg);
    }

    void set_nonblock(int sock) {
       int flags;
       flags = fcntl(sock,F_GETFL,0);
       if (flags == -1)
          error_exit("ERROR no valid flags on socket");
       fcntl(sock, F_SETFL, flags | O_NONBLOCK);
    }

    int main(int argc, char *argv[]) {
       // Hold the time to wait between single requests.
       //int logavg_interval = 5;

       // Check commandline arguments
       if (argc < 2)
          error_exit("ERROR program needs config-file as parameter");

       //Read Config File
       config_file_name = argv[1];
       FILE *fp = fopen(config_file_name, config_mode);

       // Read variables
       if (fp) {
          while (fgets(line, sizeof(line), fp)) {
             sscanf(line, "Debug=%d[^\n]", &DEBUG);
             sscanf(line, "Errorfile=%[^\n]", error_file_name);
             sscanf(line, "Debugfile=%[^\n]", debug_file_name);
             sscanf(line, "Loginterval=%d[^\n]", &log_interval);
             sscanf(line, "Logavginterval=%d[^\n]", &logavg_interval);
             sscanf(line, "Waitinterval=%d[^\n]", &wait_interval);
             sscanf(line, "DBhost=%[^\n]", dbhost);
             sscanf(line, "DBname=%[^\n]", dbname);
             sscanf(line, "DBtabprefix=%[^\n]", dbtabprefix);
             sscanf(line, "DBuser=%[^\n]", dbuser);
             sscanf(line, "DBpass=%[^\n]", dbpass);
             sscanf(line, "Hostname=%[^\n]", hostaddr);
             sscanf(line, "Hostport=%d[^\n]", &portno);
             sscanf(line, "NumberOfInverters=%d[^\n]", &nr_of_maxes);
          }
       }
       fclose(fp);

       // Try to open error log file
       if ((error_file = fopen(error_file_name, error_mode)) == NULL)
          error_exit("ERROR opening error.log file");

       // Make file unbuffered
       setbuf(error_file, NULL);

       // create the arrays for the average calculation
       long tkdy[nr_of_maxes], tkmt[nr_of_maxes], tkyr[nr_of_maxes], tkt0[nr_of_maxes], ttnf[nr_of_maxes], ttkk[nr_of_maxes], tpac[nr_of_maxes], tud01[nr_of_maxes], tud02[nr_of_maxes], tid01[nr_of_maxes], tid02[nr_of_maxes], tsys[nr_of_maxes];
       // calculate the requests per log_interval
       // TODO -2 is a dirty fix to avoid desync
       int logavg_pertick = (int)((log_interval-2)/logavg_interval);

       // Try to open debug log file, if necessary
       if(DEBUG) {
          if((debug_file = fopen(debug_file_name, debug_mode)) == NULL)
             error_exit("ERROR opening debug.log file");

          // Make file unbuffered
          setbuf(debug_file, NULL);
       }

       // Try to compile regular expression
       result = regcomp(&rx, expression, REG_EXTENDED);
       if (result != 0) {
          regerror(result, &rx, expression, sizeof(expression));
          regfree(&rx);
          sprintf(buffer, "ERROR invalid regular expression: %s", expression);
          error_exit(buffer);
       }

       // Try to reserve memory for matches
       matches = (regmatch_t *) malloc((rx.re_nsub + 1) * sizeof(regmatch_t));
       if (!matches)
          error_exit("Out of memory");

       // Connect to database
       connection = mysql_init(NULL);
       if (!mysql_real_connect(connection, dbhost, dbuser, dbpass, dbname, 0, NULL, 0))
          error_exit(mysql_error(connection));

       if (DEBUG) {
          sprintf(buffer, "Connected to database %s on host %s", dbname, dbhost);
          debug_entry(buffer);
       }

       while (1) {

          // set variable to default value or it will keep trying to reconnect
          failure_flag = 0;

          // Check if connection to db-server must be re-established
          if (mysql_ping(connection)) {

             //TODO Maybe a reconnect (if needed) here ?
             // Connect to database
             if (!mysql_real_connect(connection, dbhost, dbuser, dbpass, dbname, 0, NULL, 0))
                error_exit(mysql_error(connection));

             if (DEBUG) {
                sprintf(buffer, "Connected to database %s on host %s", dbname, dbhost);
                debug_entry(buffer);
             }
          }

          // Try to open socket for communication with solarmax
          sockfd = socket(AF_INET, SOCK_STREAM, 0);
          if (sockfd < 0) {
             error_retry("Can't open any socket");
          sleep(60);
          continue;
          }

          // Try to resolve solarmax address/hostname
          server = gethostbyname(hostaddr);
          if (server == NULL) {
             sprintf(buffer, "Can't resolve \"%s\"", hostaddr);
             error_retry(buffer);
             sleep(60);
             continue;
          }

          // Try to establish a connection with solarmax
          //portno = atoi(argv[2]);
          bzero((char *) &serv_addr, sizeof(serv_addr));
          serv_addr.sin_family = AF_INET;
          bcopy((char *) server->h_addr, (char *) &serv_addr.sin_addr.s_addr, server->h_length);
          serv_addr.sin_port = htons(portno);
          if (connect(sockfd, (struct sockaddr*) &serv_addr, sizeof(serv_addr)) < 0) {
             sprintf(buffer, "%s: Can't connect to solarmax (%s) on port %d", strerror(errno), hostaddr, portno);
             error_retry(buffer);
             sleep(60);
             continue;
          }

          // Make socket non-blocking
          set_nonblock(sockfd);

          if (DEBUG) {
             sprintf(buffer, "Connected to solarmax (%s) on port %d", hostaddr, portno);
             debug_entry(buffer);
          }

          // Start sending the data requests and logging the answers
          while (1) {
             time_t start_time = time(NULL);
             for (i = 0; i < nr_of_maxes; ++i) {
                tkdy[i] = tkmt[i] = tkyr[i] = tkt0[i] = ttnf[i] = ttkk[i] = tpac[i] = tud01[i] = tud02[i] = tid01[i] = tid02[i] = tsys[i] = 0;
             }

             // Get the current time
             for (i = 0; i < logavg_pertick; ++i) {
                time_t single_start_time = time(NULL);
                // We have to get out of this while-loop to reestablish the connection to the inverter
                if (failure_flag == 1){
                    debug_entry("Looks like we lost our connection to solarmax, reconnecting...");
                    break;
                }


                for(active_max = 1; active_max <= nr_of_maxes; active_max++){

                   // Generate message according to device address of solarmax:

                   // Could be something like this:
                   // sprintf(message, "{FB;0%d;46|64:KDY;KMT;KYR;KT0;TNF;TKK;PAC;PRL;IL1;IDC;UL1;UDC;SYS|%s}", active_max, 16_bit_checksum
                   // For further information on the protocol refer to: http://blog.dest-unreach.be/2009/04/15/solarmax-maxtalk-protocol-reverse-engineered

                   // Until someone comes up with a nice solution to calculate the checksum, lets stick to a few precalculated message strings (tested only for 2 maxes!):
                   if (active_max == 1) {
                      message = "{FB;01;46|64:KDY;KMT;KYR;KT0;TNF;TKK;PAC;UD01;UD02;ID01;ID02;SYS|10fe}";
                   }
                   
                 

                   // Send message
                   n = write(sockfd,message,strlen(message));
         usleep(100000);
          debug_entry(buffer);
                   if (n < 0) {
                      close(sockfd);
                      error_retry("ERROR sending TCP packet");
                      failure_flag = 1;
                      break;
                   }

                   // Read answer
                  bzero(buffer, 256);
                   n = read(sockfd, buffer, 255);
                   for (counter = 0; counter < wait_interval && n < 0; counter++) {
                      if (DEBUG)
                         debug_entry("Socket contains no data, trying to read again later");
          debug_entry(buffer);
                      usleep(10000);
                      n = read(sockfd, buffer, 255);
                   }

                   if (n < 0) {
                      close(sockfd);
                      error_retry("ERROR receiving TCP packet");
                      failure_flag = 1;
                      break;
                   }

                   if (DEBUG) {
                      sprintf(buffer2, "Received answer: %s", buffer);
                      debug_entry(buffer2);
                   }

                   // Extract the data fields from answer
                   result = regexec(&rx, buffer, rx.re_nsub + 1, matches, 0);
                   if (result) {
                      regerror(result, &rx, buffer, sizeof(buffer));
                      debug_entry(buffer);
         
                      //error_exit("ERROR no regexp match");
                      error_retry("ERROR no regexp match!");
         
                      // TODO Create flag for this kind of failure or rename this one
                      failure_flag = 2;
                      break;
                   }

                   // Convert the extracted data fields to integer values
                   temp = strndup(buffer + matches[1].rm_so, matches[1].rm_eo - matches[1].rm_so);
                   kdy = strtol(temp, NULL, 16);
                   free(temp);
                   temp = strndup(buffer + matches[2].rm_so, matches[2].rm_eo - matches[2].rm_so);
                   kmt = strtol(temp, NULL, 16);
                   free(temp);
                   temp = strndup(buffer + matches[3].rm_so, matches[3].rm_eo - matches[3].rm_so);
                   kyr = strtol(temp, NULL, 16);
                   free(temp);
                   temp = strndup(buffer + matches[4].rm_so, matches[4].rm_eo - matches[4].rm_so);
                   kt0 = strtol(temp, NULL, 16);
                   free(temp);
                   temp = strndup(buffer + matches[5].rm_so, matches[5].rm_eo - matches[5].rm_so);
                   ttnf[active_max-1] += strtol(temp, NULL, 16);
                   free(temp);
                   temp = strndup(buffer + matches[6].rm_so, matches[6].rm_eo - matches[6].rm_so);
                   ttkk[active_max-1] += strtol(temp, NULL, 16);
                   free(temp);
                   temp = strndup(buffer + matches[7].rm_so, matches[7].rm_eo - matches[7].rm_so);
                   tpac[active_max-1] += strtol(temp, NULL, 16) / 2;
                   free(temp);
                   temp = strndup(buffer + matches[8].rm_so, matches[8].rm_eo - matches[8].rm_so);
                   tud01[active_max-1] += strtol(temp, NULL, 16);
                   free(temp);
                   temp = strndup(buffer + matches[9].rm_so, matches[9].rm_eo - matches[9].rm_so);
                   tud02[active_max-1] += strtol(temp, NULL, 16);
                   free(temp);
                   temp = strndup(buffer + matches[10].rm_so, matches[10].rm_eo - matches[10].rm_so);
                   tid01[active_max-1] += strtol(temp, NULL, 16);
                   free(temp);
                   temp = strndup(buffer + matches[11].rm_so, matches[11].rm_eo - matches[11].rm_so);
                   tid02[active_max-1] += strtol(temp, NULL, 16);
                   free(temp);
                   temp = strndup(buffer + matches[12].rm_so, matches[12].rm_eo - matches[12].rm_so);
                   tsys[active_max-1] += strtol(temp, NULL, 16);
               //sys = strtol(temp, NULL, 16);
                   free(temp);
               
                   
                }
                if (failure_flag > 0){
                    break;
                }
                //TODO check if the task need more time than logavg_interval
                sleep(logavg_interval - (time(NULL)-single_start_time));
             }
             // Calculate the average values and insert into db
             if (failure_flag == 0){
                for (i = 0; i < nr_of_maxes; ++i) {
         tkdy[i] = (int)((tkdy[i]*1.0)/(logavg_pertick*1.0));
               tkmt[i] = (int)((tkmt[i]*1.0)/(logavg_pertick*1.0));
               tkyr[i] = (int)((tkyr[i]*1.0)/(logavg_pertick*1.0));
               tkt0[i] = (int)((tkt0[i]*1.0)/(logavg_pertick*1.0));
                   ttnf[i] = (int)((ttnf[i]*1.0)/(logavg_pertick*1.0));
                   ttkk[i] = (int)((ttkk[i]*1.0)/(logavg_pertick*1.0));
                   tpac[i] = (int)((tpac[i]*1.0)/(logavg_pertick*1.0));
                   tud01[i] = (int)((tud01[i]*1.0)/(logavg_pertick*1.0));
                   tud02[i] = (int)((tud02[i]*1.0)/(logavg_pertick*1.0));
                   tid01[i] = (int)((tid01[i]*1.0)/(logavg_pertick*1.0));
                   tid02[i] = (int)((tid02[i]*1.0)/(logavg_pertick*1.0));
      tsys[i] = (int)((tsys[i]*1.0)/(logavg_pertick*1.0));

                   // Construct the query according to active solarmax
                   sprintf(query, "INSERT INTO %s%d (kdy, kmt, kyr, kt0, tnf, tkk, pac, ud01, ud02, id01, id02, sys) VALUES (%d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d);", dbtabprefix, (i+1), kdy, kmt, kyr, kt0, (int)ttnf[i], (int)ttkk[i], (int)tpac[i], (int)tud01[i], (int)tud02[i], (int)tid01[i], (int)tid02[i], sys);
                   if (DEBUG) {
                      sprintf(buffer, "Executing query: %s", query);
                      debug_entry(buffer);
                   }
                   // Execute the query to write the data into db
                   mysql_query(connection, query);
                   if (mysql_errno(connection))
                      error_exit(mysql_error(connection));
                }

             }

             // Wait for the specified number of seconds - calc duration - 1
             if (DEBUG)
                debug_entry("Waiting for about 1 minute ...");
             // Get the current time
             time_t stop_time = time(NULL);
             // TODO check if time needed is > log_interval
             int sleepTime = log_interval + start_time - stop_time - 1;
             if(sleepTime > 0)
                sleep(sleepTime);
             else{
                sprintf(buffer, "!!! sleepTime error. Assuming desync: %i seconds. !!!\n", sleepTime);
                debug_entry(buffer);
             }


             // Add a busy-loop for the last second to make sure we are perfectly accurate
             while (time(NULL) < start_time + log_interval) usleep(99999);

             // If the connection is lost -> retry
             if (failure_flag == 1){
                // just to be sure
                close(sockfd);
                break;
             }
          }
       }
       return 0;
    }


root@raspberrypi:/usr/local/etc# cat smw-logger.conf
Code: Alles auswählen
# Settings for the Solarmax Watcher
# defaults are shown in parentheses

## Global settings of the logger

# Debug? (0)
Debug=1

# location of error file
Errorfile=/var/log/solarmax-error.log

# location of logging file
Debugfile=/var/log/solarmax-debug.log

# Interval to read the values of the inverter (60)
Loginterval=60

# Interval between the queries for the averageable values during this
# interval (5). This means that the values will be averaged about
# Loginterval/Logavginterval (12) times during one Loginterval.
Logavginterval=3

# Interval to wait for inverters answer (200)
Waitinterval=50


## MySQL settings

# Hostname which is running the MySQL server (localhost)
DBhost=localhost

# Name of the MySQL DB for the Solarmax logger (solarmax)
DBname=solarmax

# Tablename prefix (log)
DBtabprefix=log

# DB user to write the values solarmax-DB (solaruser)
DBuser=solaruser

# Password for the DB user (userpassword)
DBpass=solarpi39


## Inverter settings

# IP-address or hostname of the inverter (192.168.178.35)
Hostname=192.168.178.90

# IP-Port of the inverter (12345)
Hostport=12345

# Number of Solarmax inverters in your array (1)
NumberOfInverters=1


der Zusammenhang von:
Loginterval=60
Logavginterval=3
Waitinterval=50

erschliesst sich mir noch nicht richtig
fakt ist, dass er anfangs alle 1,5 min in die DB einträgt und dann später irgendwann alle 3 min

gibt es da eine Logik?
offensichtlich nimmt er aber die 12 Variablen in einem Zug

Gruss
sollner11
Fleißiges Mitglied
Fleißiges Mitglied
 
Beiträge: 454
Registriert: 05.05.2012, 12:15
PV-Anlage [kWp]: 19,11
Info: Betreiber

VorherigeNächste

Zurück zu Datenlogger



Ähnliche Beiträge

Bedarf an weiterem Datenlogger? Sun-Watch    Forum: Sun-Watch    Antw.: 437
Datenlogger mit Vollausstattung    Forum: Datenlogger    Antw.: 16
Noch ein Datenlogger...    Forum: Datenlogger    Antw.: 1
Datenlogger für Steca Grid9000 mit GSM?    Forum: Datenlogger    Antw.: 14
Datenlogger    Forum: Inselanlagen    Antw.: 3

Wer ist online?

Mitglieder in diesem Forum: Google Feedfetcher und 1 Gast