/* * main.c * * Beej's Guide to Network Programming * http://beej.us/guide/bgnet/output/html/multipage/index.html * http://beej.us/guide/bgnet/output/html/multipage/clientserver.html * * MySQL and C * http://zetcode.com/db/mysqlc/ * http://www.kitebird.com/mysql-book/ch06-3ed.pdf * * Omnik message info found here: * Written in PHP * https://github.com/micromys/Omnik * Written in Python * https://github.com/Woutrrr/Omnik-Data-Logger/blob/master/InverterMsg.py * Written in C * https://github.com/arjenv/omnikstats * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define OMNIKPORTAL_PULL "8899" #define PORT_LISTEN "8989" #define BACKLOG 10 FILE * fp; MYSQL *conn; MYSQL_RES *res; MYSQL_ROW row; unsigned char mysql_enabled = 0; char mysql_host[100] = ""; char mysql_user[32] = "omniklistener"; char mysql_pass[32] = ""; char mysql_db[100] = "energyportal"; unsigned int mysql_port = 3306; unsigned char csv_enabled = 0; char csvfile[250] = ""; unsigned char silent = 0; void doChildProcess(int fd_client); void printMessage(unsigned char *message, int length); void sigchld_handler(int s){ while(waitpid(-1, NULL,WNOHANG) > 0); } // get sockaddr, IPv4 or IPv6: void *get_in_addr(struct sockaddr *sa) { if (sa->sa_family == AF_INET) { return &(((struct sockaddr_in*)sa)->sin_addr); } return &(((struct sockaddr_in6*)sa)->sin6_addr); } int startswith(const char *prefix, const char *string){ size_t lenstring = strlen(string); size_t lenprefix = strlen(prefix); if(lenstring < lenprefix){ return 0; } else{ return (strncmp(string,prefix,lenprefix) == 0); } } int main(int argc, char *argv[]){ int fd_server; //Listening socket int fd_client; //Client connected socket struct addrinfo hints, *res, *p; struct sockaddr_storage their_addr; struct sigaction sa; int status; char ipstr[INET6_ADDRSTRLEN]; socklen_t addr_size; char s[INET6_ADDRSTRLEN]; pid_t pid; //Loop through command line arguments int i = 0; for(i = 0; i < argc; i++){ if(startswith("--mysql-host=",argv[i])){ strcpy(mysql_host,argv[i]+strlen("--mysql-host=")); } if(startswith("--mysql-user=",argv[i])){ strcpy(mysql_user,argv[i]+strlen("--mysql-user=")); } if(startswith("--mysql-pass=",argv[i])){ strcpy(mysql_pass,argv[i]+strlen("--mysql-pass=")); } if(startswith("--mysql-db=",argv[i])){ strcpy(mysql_db,argv[i]+strlen("--mysql-db=")); } if(startswith("--mysql-port=",argv[i])){ sscanf(argv[i]+strlen("--mysql-port="),"%d",&mysql_port); } if(startswith("--csv-file=",argv[i])){ strcpy(csvfile,argv[i]+strlen("--csv-file=")); } if(startswith("--silent",argv[i])){ silent = 1; } } //When --mysql-host is filled, then enable mysql export if(strlen(mysql_host) > 0){ mysql_enabled = 1; } //When --csv-file is filled, then enable csv export if(strlen(csvfile) > 0){ csv_enabled = 1; } //Print out active configuration printf("OmnikListenerTCP\n"); printf(" MySQL enabled : %d\n",mysql_enabled); if(mysql_enabled){ printf(" MySQL host : %s\n",mysql_host); printf(" MySQL port : %d\n",mysql_port); printf(" MySQL user : %s\n",mysql_user); //Maybe hide password? ;) //printf(" MySQL pass : %s\n",mysql_pass); printf(" MySQL db : %s\n",mysql_db); } printf(" CSV enabled : %d\n",csv_enabled); if(csv_enabled){ printf(" CSV file : %s\n",csvfile); } memset(&hints, 0, sizeof hints); hints.ai_family = AF_UNSPEC; hints.ai_socktype = SOCK_STREAM; hints.ai_flags = AI_PASSIVE; if((status = getaddrinfo(NULL, PORT_LISTEN, &hints, &res))!=0){ fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(status)); return 2; } for(p = res; p != NULL; p=p->ai_next){ void *addr; char *ipver; if(p->ai_family == AF_INET){ struct sockaddr_in *ipv4 = (struct sockaddr_in *)p->ai_addr; addr = &(ipv4->sin_addr); ipver = "IPv4"; } else{ struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)p->ai_addr; addr = &(ipv6->sin6_addr); ipver = "IPv6"; } inet_ntop(p->ai_family, addr, ipstr, sizeof ipstr); //printf(" %s: %s\n",ipver,ipstr); } fd_server = socket(res->ai_family, res->ai_socktype, res->ai_protocol); if(fd_server == -1){ perror("socket"); return 3; } else{ printf("Socket opened %d\n",fd_server); } if(bind(fd_server, res->ai_addr, res->ai_addrlen) == -1){ perror("bind"); return 4; } if(listen(fd_server,BACKLOG) == -1){ perror("listen"); return 5; } sa.sa_handler = sigchld_handler; sigemptyset(&sa.sa_mask); sa.sa_flags = SA_RESTART; if(sigaction(SIGCHLD, &sa, NULL) == -1){ perror("sigaction"); exit(1); } printf("server: waiting for connections...\n"); while(1){ addr_size = sizeof their_addr; fd_client = accept(fd_server, (struct sockaddr *)&their_addr, &addr_size); if(fd_client == -1){ perror("accept"); continue; } else{ printf("Socket accepted %d\n",fd_client); } inet_ntop(their_addr.ss_family, get_in_addr((struct sockaddr *)&their_addr), s, sizeof s); printf("server: get connection from %s\n",s); pid = fork(); if(pid == 0){ close(fd_server); //child doesn't need the listener doChildProcess(fd_client); } close(fd_client); } return 0; } void doChildProcess(int fd_client) { int numbytes; unsigned char recvbuf[512]; if((numbytes = recv(fd_client, recvbuf, 511, 0)) == -1){ perror("recv"); exit(1); } printf("client: received bytes %d \n",numbytes); printMessage(recvbuf,numbytes); close(fd_client); exit(0); } void printMessage(unsigned char *message, int length){ time_t timer; char timebuffer[26]; struct tm* tm_info; int i = 0; unsigned char byte; char p_id[17]; short p_temp; long p_etoday; long p_etotal; long p_htotal; short p_vpv1; short p_vpv2; short p_vpv3; short p_ipv1; short p_ipv2; short p_ipv3; short p_vac1; short p_vac2; short p_vac3; short p_iac1; short p_iac2; short p_iac3; short p_fac1; short p_fac2; short p_fac3; short p_pac1; short p_pac2; short p_pac3; time(&timer); tm_info = localtime(&timer); if(length < 143) { printf("Message to short? Do not process.\n"); return; } for(i = 0; i < length; i++){ byte = message[i]; printf("%02X ",byte); if((i + 1) % 8 == 0){ printf("\n"); } } printf("\n"); if(message[0] == 0x68 && message[1] == 0x81 && message[2] == 0x41 && message[3] == 0xB0){ //OK } else{ printf("Header does not match hex: 68 81 41 B0\n"); printf("Skip message"); return; } for(i = 0; i < length; i++){ byte = message[i]; //Byte 15, start of SerialNumber if(i >= 15 && i <= 30){ p_id[i-15]=byte; } //Byte 31-32 contain temperature if(i == 31){ p_temp = 0; p_temp = byte << 8; } if(i == 32){ p_temp = p_temp | byte; } //Byte 33-34 PV 1 input voltage if(i == 33){ p_vpv1 = 0; p_vpv1 = byte << 8; } if(i == 34){ p_vpv1 = p_vpv1 | byte; } //Byte 35-36 PV 2 input voltage if(i == 35){ p_vpv2 = 0; p_vpv2 = byte << 8; } if(i == 36){ p_vpv2 = p_vpv2 | byte; } //Byte 37-38 PV 3 input voltage if(i == 37){ p_vpv3 = 0; p_vpv3 = byte << 8; } if(i == 38){ p_vpv3 = p_vpv3 | byte; } //Byte 39-40 PV 1 input current if(i == 39){ p_ipv1 = 0; p_ipv1 = byte << 8; } if(i == 40){ p_ipv1 = p_ipv1 | byte; } //Byte 41-42 PV 2 input current if(i == 41){ p_ipv2 = 0; p_ipv2 = byte << 8; } if(i == 42){ p_ipv2 = p_ipv2 | byte; } //Byte 43-44 PV 3 input current if(i == 43){ p_ipv3 = 0; p_ipv3 = byte << 8; } if(i == 44){ p_ipv3 = p_ipv3 | byte; } //Byte 45-46 AC output current phase 1 if(i == 45){ p_iac1 = 0; p_iac1 = byte << 8; } if(i == 46){ p_iac1 = p_iac1 | byte; } //Byte 47-48 AC output current phase 2 if(i == 47){ p_iac2 = 0; p_iac2 = byte << 8; } if(i == 48){ p_iac2 = p_iac2 | byte; } //Byte 49-50 AC output current phase 3 if(i ==49){ p_iac3 = 0; p_iac3 = byte << 8; } if(i == 50){ p_iac3 = p_iac3 | byte; } //Byte 51-52 AC output voltage phase 1 if(i == 51){ p_vac1 = 0; p_vac1 = byte << 8; } if(i == 52){ p_vac1 = p_vac1 | byte; } //Byte 53-54 AC output voltage phase 2 if(i == 53){ p_vac2 = 0; p_vac2 = byte << 8; } if(i == 54){ p_vac2 = p_vac2 | byte; } //Byte 55-56 AC output voltage phase 3 if(i == 55){ p_vac3 = 0; p_vac3 = byte << 8; } if(i == 56){ p_vac3 = p_vac3 | byte; } //Byte 57-58 AC output ??what?? phase 1 if(i == 57){ p_fac1 = 0; p_fac1 = byte << 8; } if(i == 58){ p_fac1 = p_fac1 | byte; } //Byte 59-60 AC output power phase 1 if(i == 59){ p_pac1 = 0; p_pac1 = byte << 8; } if(i == 60){ p_pac1 = p_pac1 | byte; } //Byte 61-62 AC output ??what?? phase 2 if(i == 61){ p_fac2 = 0; p_fac2 = byte << 8; } if(i == 62){ p_fac2 = p_fac2 | byte; } //Byte 63-64 AC output power phase 2 if(i == 63){ p_pac2 = 0; p_pac2 = byte << 8; } if(i == 64){ p_pac2 = p_pac2 | byte; } //Byte 63-64 AC output ??what?? phase 3 if(i == 65){ p_fac3 = 0; p_fac3 = byte << 8; } if(i == 66){ p_fac3 = p_fac3 | byte; } //Byte 67-68 AC output power phase 3 if(i == 67){ p_pac3 = 0; p_pac3 = byte << 8; } if(i == 68){ p_pac3 = p_pac3 | byte; } //Byte 69-70 contain total kwh of today if(i == 69){ p_etoday = 0; p_etoday = byte << 8; } if(i == 70){ p_etoday = p_etoday | byte; } //Byte 71-74 contain total kwh if(i == 71){ p_etotal = 0; p_etotal = p_etotal | (byte << 24); } if(i == 72){ p_etotal = p_etotal | (byte << 16); } if(i == 73){ p_etotal = p_etotal | (byte << 8); } if(i == 74){ p_etotal = p_etotal | byte; } //Byte 75-78 contain hours online if(i == 75){ p_htotal = 0; p_htotal = p_htotal | (byte << 24); } if(i == 76){ p_htotal = p_htotal | (byte << 16); } if(i == 77){ p_htotal = p_htotal | (byte << 8); } if(i == 78){ p_htotal = p_htotal | byte; } } p_id[16] = 0; if(!silent){ strftime(timebuffer, sizeof(timebuffer), "%Y-%m-%d %H:%M:%S", tm_info); printf("Time : %s \n", timebuffer); printf("Serial : %s \n", p_id); printf("Temp : %f \n", ((double) p_temp / 10)); printf("E-Today : %f kWh \n", ((double) p_etoday / 100)); printf("E-Total : %f kWh \n", ((double) p_etotal / 10)); printf("H-Total : %ld H \n", p_htotal); printf("PV 1 U : %f V \n", ((double) p_vpv1 / 10)); printf("PV 1 I : %f A \n", ((double) p_ipv1 / 10)); printf("PV 2 U : %f V \n", ((double) p_vpv2 / 10)); printf("PV 2 I : %f A \n", ((double) p_ipv2 / 10)); printf("PV 3 U : %f V \n", ((double) p_vpv3 / 10)); printf("PV 3 I : %f A \n", ((double) p_ipv3 / 10)); printf("AC1 U : %f V \n", ((double) p_vac1 / 10)); printf("AC1 I : %f A \n", ((double) p_iac1 / 10)); printf("AC1 ? : %f ? \n", ((double) p_fac1 / 100)); printf("AC1 P : %d W \n", p_pac1); printf("AC2 U : %f V \n", ((double) p_vac2 / 10)); printf("AC2 I : %f A \n", ((double) p_iac2 / 10)); printf("AC2 ? : %f ? \n", ((double) p_fac2 / 100)); printf("AC2 P : %d W \n", p_pac2); printf("AC3 V : %f V \n", ((double) p_vac3 / 10)); printf("AC3 I : %f A \n", ((double) p_iac3 / 10)); printf("AC3 ? : %f ? \n", ((double) p_fac3 / 100)); printf("AC3 P : %d W \n", p_pac3); strftime(timebuffer, sizeof(timebuffer), "%Y-%m-%d %H:%M:%S", tm_info); printf("%s,%s,%f,%f,%f,%ld,%f,%f,%f,%f,%f,%f,%f,%f,%f,%d,%f,%f,%f,%d,%f,%f,%f,%d\n", timebuffer, p_id, ((double) p_temp / 10), ((double) p_etoday / 100), ((double) p_etotal / 10), p_htotal, ((double) p_vpv1 / 10), ((double) p_ipv1 / 10), ((double) p_vpv2 / 10), ((double) p_ipv2 / 10), ((double) p_vpv3 / 10), ((double) p_ipv3 / 10), ((double) p_vac1 / 10), ((double) p_iac1 / 10), ((double) p_fac1 / 100), p_pac1, ((double) p_vac2 / 10), ((double) p_iac2 / 10), ((double) p_fac2 / 100), p_pac2, ((double) p_vac3 / 10), ((double) p_iac3 / 10), ((double) p_fac3 / 100), p_pac3); } //END !silent if(csv_enabled){ fp = fopen(csvfile,"a"); if(fp == NULL){ perror("fopen"); } else{ printf("Opened file for appending\n"); strftime(timebuffer, sizeof(timebuffer), "%Y-%m-%d %H:%M:%S", tm_info); fprintf(fp, "%s,%s,%f,%f,%f,%ld,%f,%f,%f,%f,%f,%f,%f,%f,%f,%d,%f,%f,%f,%d,%f,%f,%f,%d\n", timebuffer, p_id, ((double) p_temp / 10), ((double) p_etoday / 100), ((double) p_etotal / 10), p_htotal, ((double) p_vpv1 / 10), ((double) p_ipv1 / 10), ((double) p_vpv2 / 10), ((double) p_ipv2 / 10), ((double) p_vpv3 / 10), ((double) p_ipv3 / 10), ((double) p_vac1 / 10), ((double) p_iac1 / 10), ((double) p_fac1 / 100), p_pac1, ((double) p_vac2 / 10), ((double) p_iac2 / 10), ((double) p_fac2 / 100), p_pac2, ((double) p_vac3 / 10), ((double) p_iac3 / 10), ((double) p_fac3 / 100), p_pac3); fflush(fp); fclose(fp); } } if(mysql_enabled){ //Try to write to mysql conn = mysql_init(NULL); if(conn == NULL){ perror("mysql_init"); } else{ if(!mysql_real_connect(conn,mysql_host,mysql_user,mysql_pass,mysql_db,mysql_port,NULL,0)){ fprintf(stderr, "%s\n",mysql_error(conn)); } else{ char stmt_buf[1024]; sprintf(stmt_buf,"" "INSERT INTO `omnikdata` (" "`inverterid`,`moment`,`temperature`,`p_today`,`p_total`,`h_total`," "`pv1_u`,`pv1_i`,`pv2_u`,`pv2_i`,`pv3_u`,`pv3_i`," "`ac1_u`,`ac1_i`,`ac1_f`,`ac1_p`," "`ac2_u`,`ac2_i`,`ac2_f`,`ac2_p`," "`ac3_u`,`ac3_i`,`ac3_f`,`ac3_p`) " " VALUES (" "'%s',NOW(),%f,%f,%f,%ld,%f,%f,%f,%f,%f,%f,%f,%f,%f,%d,%f,%f,%f,%d,%f,%f,%f,%d);", p_id, ((double) p_temp / 10), ((double) p_etoday / 100), ((double) p_etotal / 10), p_htotal, ((double) p_vpv1 / 10), ((double) p_ipv1 / 10), ((double) p_vpv2 / 10), ((double) p_ipv2 / 10), ((double) p_vpv3 / 10), ((double) p_ipv3 / 10), ((double) p_vac1 / 10), ((double) p_iac1 / 10), ((double) p_fac1 / 100), p_pac1, ((double) p_vac2 / 10), ((double) p_iac2 / 10), ((double) p_fac2 / 100), p_pac2, ((double) p_vac3 / 10), ((double) p_iac3 / 10), ((double) p_fac3 / 100), p_pac3); int nQueryResult = mysql_query(conn,stmt_buf); if (nQueryResult > 0){ fprintf(stdout,"%s\n",mysql_error(conn)); } else{ } //res = mysql_use_result(conn); //while((row = mysql_fetch_row(res)) != NULL){ // printf("Database: %s\n",row[0]); // //int dumpresult = system("") //} //mysql_free_result(res); mysql_close(conn); } //END mysql_real_connect } //END mysql_init } //END mysql_enabled }