#!/usr/bin/perl # pulsecounter.pl # # Daemon to for pulsecounter hardware # # (c) Daniel Vindevåg, daniel@vindevag.com # http://www.nattsjo.se/temp/power-hw/ ## Dependencies # # apt-get install libdevice-serialport-perl libconfig-simple-perl libdbi-perl libdbd-mysql-perl libsys-syslog-perl libwww-perl libio-socket-timeout-perl ## Device # # To use /dev/pulsecounter instead of /dev/ttyACM[n], # the following udev rule must be applied # #ACTION=="add", SUBSYSTEM=="tty", ATTRS{idVendor}=="0403", ATTRS{idProduct}=="9136", SYMLINK+="pulsecounter", MODE="0666" ## Make firmware update accessable to userland # # atmega32u2 DFU #ACTION=="add", SUBSYSTEMS=="usb", ATTRS{idVendor}=="03eb", ATTRS{idProduct}=="2ff0", MODE="0666" ## Install timzone support in MySQL # # mysql_tzinfo_to_sql /usr/share/zoneinfo | mysql -u root mysql -p ## Database layout # # CREATE TABLE IF NOT EXISTS power1 (id INTEGER UNSIGNED NOT NULL AUTO_INCREMENT, time TIMESTAMP NULL, counter INTEGER unsigned, power MEDIUMINT unsigned, PRIMARY KEY(id)); # INSERT INTO power1 values (null, now(), 1, 1); # DB sql strings my $sql_insert = "INSERT INTO %s VALUES (null, FROM_UNIXTIME(%d), %d, %d)"; my $sql_select = "SELECT time, UNIX_TIMESTAMP(time), counter, power FROM %s ORDER BY -time LIMIT 1;"; my $sql_create = "CREATE TABLE IF NOT EXISTS %s (id INTEGER UNSIGNED NOT NULL AUTO_INCREMENT, time TIMESTAMP NULL, counter INTEGER UNSIGNED, power MEDIUMINT UNSIGNED, PRIMARY KEY(id));"; use FindBin; # locate this script use lib "$FindBin::Bin"; # include script directory use Device::SerialPort; use Sys::Syslog qw(:standard :macros); use POSIX qw(setsid strftime setuid setgid floor); use Time::HiRes qw(usleep nanosleep); use DBI; use Config::Simple; use LWP::UserAgent; use HTTP::Request::Common qw(GET POST); use IO::Socket; use IO::Socket::Timeout; use strict; use File::Basename; use File::Spec; # global variables my $COUNTER; # Serial port object my $rtc_set; # Timestamp for last RTC sync my $rtc_drift; # Drift correction, subtract 1s every # my $rtc_check_hours = 3; # Adjust hardware clock every x hours my $sock; # TCP/IP Socket # Default values for global variables set in config file # my $counter_report = 100; # Hardware reports every x pulses my $counter_overflow = 10000; # Hardware overflows fter x pulses my $db_report_wh = 500; # Add to db every x Wh my $db_report_delta = 10; # Add to db if power incr/decr more than x % my $mcu = "atmega32u2"; # Hardware MCU my $hw_tz = 0; # Hardware timezone my $db_max_power_check = 30000; # Filter out illegal power values # Device port and seconds until program quits if device disappear my $port_name = "/dev/pulsecounter"; # Device filename my $port_ttl = 60*10; # TCP port number for status info and password to calibrate my $tcp_port = ""; my $daemon_password = ""; # setuid and run as daemon my $user = 0; my $group = 0; my $daemonize = 1; # Save pid to file, leave blank to ignore my @n = split("/",$0); my $n = pop @n; my ($program_name,$ext) = split('\.',$n); undef @n; undef $n; undef $ext; my $pidfile = "/run/$program_name.pid"; # Database config, my $DBHOST = ""; my $DBNAME = ""; my $DBUSER = ""; my $DBPASS = ""; my $DBTABLE = ""; # Use remote url for database my $webreport = ""; my $web_password = ""; # HD44780 character LCD, size, backligth intensity 0 - 255 my $lcd_lines = 0; my $lcd_columns = 0; my $lcd_bl = 50; # functions sub open_port; sub check_port; sub db_add; sub log_and_die; sub daemonize; sub main; sub read_config_file; sub rtc_get; $SIG{INT} = \&sigint; $SIG{TERM} = \&sigterm; $SIG{HUP} = \&sighup; ### begin read_config_file; # Connect to deamon and print status if ($ARGV[0] =~ "-status") { $sock = new IO::Socket::INET( PeerAddr => "localhost", PeerPort => $tcp_port, Proto => 'tcp', Blocking => 0, ); $sock or die "Can't connect to local daemon!\n"; sleep 1; print <$sock>; $sock->close(); exit; } if (-e $pidfile && !system("ps `cat $pidfile` >/dev/null")) { if ($ARGV[0] eq "-set") { $daemonize = 0; my $set_counter = int($ARGV[1]); $sock = new IO::Socket::INET( PeerAddr => "localhost", PeerPort => $tcp_port, Proto => 'tcp', Blocking => 0, ); $sock or die "Can't connect to local daemon!\r\n"; if ($daemon_password) { print $sock "CALIBRATE $set_counter $daemon_password"; #print "CALIBRATE $set_major$set_minor $daemon_password\n"; sleep 5; print <$sock>; } else { print "No password in config file!\n"; } $sock->close(); exit; } die "Another instance of $program_name is currently running, exiting!"; } # Command line parameters # for (my $p=0; $p<5; $p++) { if ($ARGV[$p]) { if ($ARGV[$p] =~ "-h") { print "Power Meter Pulse Counter\n\n"; print " -status Connect to deamon and show current values\n"; print " -set [Wh] Calibrate hardware to X Wh.\n"; print " -createdb Create database table.\n"; print " -flash FILE Update firmware with FILE.\n"; print " -halt Save hardware data and halt hardware.\n"; print " -start Resume hardware.\n\n"; exit; } if ($ARGV[$p] eq "-set") { $daemonize = 0; my $set_major = floor(int($ARGV[$p+1]) / 1000); my $set_minor = (int($ARGV[$p+1]) % 1000) * $counter_overflow / 1000; open_port(); my $result = $COUNTER->input; $COUNTER->write("ATCNTMNSET $set_minor\r\n"); sleep 1; $result = $COUNTER->input; $COUNTER->write("ATCNTMJSET $set_major\r\n"); sleep 1; $result = $COUNTER->input; my $tz = int(strftime("%z", localtime())/100); my $TZ = strftime("%Z", localtime()); $COUNTER->write("ATRTCTZSET $tz\r\n"); sleep 1; $result = $COUNTER->input; print "Timezone set to: $tz ($TZ)\n"; $COUNTER->write("ATCNTGET\r\n"); sleep 1; $result = $COUNTER->input; print "(major, minor, f, report, overflow, time): $result"; my ($major, $minor, $f, $report, $overflow, $time) = split(" ", $result); my $error = db_add(time(), ($major*1000)+(1000 * $minor/$counter_overflow), 0); undef $COUNTER; if ($error == -1) { die "Database error!\n"; } syslog(LOG_INFO, "Set Major = $major, Minor = $minor"); exit; } if ($ARGV[$p] eq "-createdb") { my $sql = sprintf($sql_create, $DBTABLE); printf "Creating table '%s' in database '%s' on host '%s'\n", $DBTABLE, $DBNAME, $DBHOST; syslog(LOG_INFO, $sql); my $db = DBI->connect("DBI:mysql:$DBNAME:$DBHOST", $DBUSER, $DBPASS, { PrintError => 0, RaiseError => 0 }); if (!$db) { undef $db; log_and_die("Database error, exiting"); } else { $DBI::result = $db->prepare($sql); $DBI::result->execute(); $DBI::result->finish(); $db->disconnect; undef $db; exit; } } if ( ($ARGV[$p] eq "-halt") || ($ARGV[$p] eq "-flash")) { if (! -e $ARGV[$p+1]) { print "Error, file not found (" . $ARGV[$p+1] . ")\n"; exit; } $daemonize = 0; open_port(); $COUNTER->write("ATV\r\n"); sleep 1; my $result = $COUNTER->input; $COUNTER->write("ATF\r\n"); if (-e $ARGV[$p+1]) { print "Firmware upgrade...\n"; sleep 2; system("dfu-programmer $mcu erase"); system("dfu-programmer $mcu flash $ARGV[$p+1]"); system("dfu-programmer $mcu start"); } exit; } if ($ARGV[$p] eq "-start") { system("dfu-programmer $mcu start"); exit; } } } # CLI parameters openlog($program_name, "ndelay,pid", "local0"); if ( (!$webreport) && (!$DBHOST || !$DBNAME || !$DBUSER || !$DBPASS || !$DBTABLE) ) { log_and_die "Database config missing, exiting!"; } if ($daemonize) { daemonize; } check_port(); if (not defined $COUNTER) { open_port(); } main; ### end # Signal handlers # sub sigint { undef $COUNTER; undef $sock; syslog(LOG_INFO, "SIGINT exiting!"); closelog(); exit; }; sub sigterm { undef $COUNTER; undef $sock; syslog(LOG_INFO, "SIGTERM exiting!"); closelog(); exit; }; sub sighup { syslog(LOG_INFO, "SIGHUP reloading!"); print "SIGHUP reloading!\n"; undef $COUNTER; undef $sock; sleep 1; read_config_file; open_port(); }; sub log_and_die { syslog(LOG_INFO, $_[0]); closelog(); print strftime("%Y%m%d %H:%M:%S", localtime()) . ": " . $_[0] . "\n"; die; } sub daemonize { chdir "/" or log_and_die("Can't chdir to /: $!"); defined(my $pid = fork) or log_and_die("Can't fork: $!"); if ($pid) { syslog(LOG_INFO, "$program_name daemonized as $pid"); if ($pidfile) { open PIDFILE, ">", $pidfile or log_and_die($!); print PIDFILE "$pid\n"; close PIDFILE; chown($user, $group, $pidfile); } closelog(); exit; } setsid() or log_and_die("Can't start a new session: $!"); umask 0; open STDOUT, ">>/dev/null" or log_and_die("Can't write to /dev/null: $!"); open STDERR, ">>/dev/null" or log_and_die("Can't write to /dev/null: $!"); open STDIN, ">>/dev/null" or log_and_die("Can't read /dev/null: $!"); if ($group) { setgid($group); } if ($user) { setuid($user); } syslog(LOG_INFO, "Running as daemon, UID ". $< . ", GID " . $( ); } # Main program # sub main { my $last_power = 0; # Last power from hw my $last_hw_timestamp = 0; # Last timestamp from hw my $last_count = 0; # Last counter from hw my $prev_db = 0; # Last count in db my $prev_db_timestamp = 0; # Last timestamp in db my $db_count = 0; # Current count in db my $db_power = 0; # Current power in db my $hw_connected_counter = 0; # Counter for checking if hw is connected my $rtc_check_counter = 0; # Counter for checking hw RTC # Get last value from db if ($DBHOST) { my $sql = sprintf($sql_select, $DBTABLE); my $db = DBI->connect("DBI:mysql:$DBNAME:$DBHOST", $DBUSER, $DBPASS, { PrintError => 0, RaiseError => 0 }); if (!$db) { undef $db; syslog(LOG_INFO, $sql); log_and_die("Database error, exiting"); } else { $DBI::result = $db->prepare($sql); $DBI::result->execute(); my ($db_old_date, $db_old_timestamp, $db_old_count, $db_old_power) = $DBI::result->fetchrow_array; if ($db_old_timestamp) { if (!$daemonize) { print "DB: $db_old_date: $db_old_timestamp, " . $db_old_count/1000 . " kWh, $db_old_power W\n"; } syslog(LOG_INFO, "DB: $db_old_date: " . $db_old_count/1000 . " KWh, $db_old_power W"); $prev_db = $db_old_count; $db_power = $db_old_power; $prev_db_timestamp = $db_old_timestamp; } $DBI::result->finish(); $db->disconnect; undef $db; } } else { if ($webreport) { my $ua = LWP::UserAgent->new; my $req = GET $webreport; my $res = $ua->request($req); if ($res->is_success) { my ($db_old_timestamp, $db_old_count, $db_old_power, $db_old_date, $db_old_time) = split(/ /, $res->content); $db_old_time =~ s/\R//g; $db_old_date .= " " . $db_old_time; if ($db_old_timestamp) { if (!$daemonize) { print "DB: $db_old_date: $db_old_timestamp, " . $db_old_count/1000 . " Wh, $db_old_power W\n"; } syslog(LOG_INFO, "DB: $db_old_date: " . $db_old_count/1000 . " KWh, $db_old_power W"); $prev_db = $db_old_count; $db_power = $db_old_power; $prev_db_timestamp = $db_old_timestamp; } } undef $ua; undef $req; undef $res; } # if webreport } # if dbhost # Main program loop # while (1) { sleep 1; if ($hw_connected_counter++ == 5) { # Check port every 5s check_port(); $hw_connected_counter = 0; } if ( $rtc_check_counter++ == ($rtc_check_hours*3600) ) { # Check and adjust RTC every x hour rtc_get(); $rtc_check_counter = 0; } # Read input my @lines; my $input = ""; if (defined($COUNTER)) { $input = $COUNTER->input; } # else { undef $COUNTER; } if ($input) { while (!($input =~ m/\n$/)) { usleep(100000); $input .= $COUNTER->input; } } if ($input) { @lines = split /\r\n/, $input; } # Process input foreach my $result (@lines) { if ($result =~ "Counter") { my ($c, $major, $minor, $hertz, $hw_timestamp) = split(" ", $result); last if ($hw_timestamp == $last_hw_timestamp); # Two reports the same second, ignore to avoid div 0 if (!$daemonize) { print strftime("%y-%m-%d %H:%M:%S", localtime(int($hw_timestamp))) . ": "; print "major: $major, minor: $minor, f: " . $hertz . "Hz"; } # KWh -> Ws my $power = ( (60*60*1000) / ( $counter_overflow/$counter_report) ) / ($hw_timestamp-$last_hw_timestamp); if ($last_hw_timestamp) { my $p_delta = int(($power-$last_power)*100/$power); my $p_db_delta = 0; if ($db_power) { $p_db_delta = int(($power-$db_power)*100/$db_power); } if (!$last_power) { $p_delta = 0; } if (!$daemonize) { print ", P: " . int($power) . "W, delta P: " . $p_delta . "% ($p_db_delta%)\n"; } $db_count = int( ($major+($minor/$counter_overflow))*1000 ); if ( (abs($p_db_delta) > $db_report_delta) || ( !($db_count % $db_report_wh) ) ) { if ( (abs($p_db_delta) > $db_report_delta) && $prev_db_timestamp) { if ($last_hw_timestamp != $prev_db_timestamp) { my $db_old_power = int ( ($last_count-$prev_db)*3600 / (($last_hw_timestamp-$prev_db_timestamp)) ); if ($db_old_power>$db_max_power_check) { $db_old_power = 0; } # Filter out illegal power values db_add($last_hw_timestamp, $last_count, $db_old_power); } $prev_db_timestamp = $last_hw_timestamp; $prev_db = $last_count; } $db_power = int ( ($db_count - $prev_db)*3600 / (($hw_timestamp - $prev_db_timestamp)) ); if (!($prev_db_timestamp)) { $db_power = 0; } # first db entry if ($db_power>$db_max_power_check) { $db_power = 0; } # Filter out illegal power values if (!db_add($hw_timestamp, $db_count, $db_power)) { # check for db error $prev_db = $db_count; $prev_db_timestamp = $hw_timestamp; } } } else { if (!$daemonize) { print "\n"; } } $last_count = int( ($major+($minor/$counter_overflow))*1000 ); $last_hw_timestamp = $hw_timestamp; $last_power = $power; } # if counter } # foreach # Print status over TCP/IP if ( defined $sock && (my $connection = $sock->accept)) { my $input = <$connection>; if ($input =~ m/CALIBRATE\s(\d+)\s(\w+)/g) { my $new_counter = $1; my $pw = $2; if ( ($new_counter >0) && ($pw eq $daemon_password) ) { my $new_major = int($new_counter / 1000); my $new_minor = $new_counter % 1000 * $counter_overflow / 1000; print $connection "Calibrate to: " . $new_counter/1000 . " kWh\n"; $COUNTER->write("ATCNTMNSET $new_minor\r\n"); sleep 1; my $result = $COUNTER->input; $COUNTER->write("ATCNTMJSET $new_major\r\n"); sleep 1; $COUNTER->write("ATRTCTZSET " . int(strftime("%z", localtime())/100) . "\r\n"); sleep 1; $result = $COUNTER->input; $COUNTER->write("ATCNTGET\r\n"); sleep 1; $result = $COUNTER->input; print "(major, minor, f, report, overflow, time): $result"; my ($major, $minor, $f, $report, $overflow, $time) = split(" ", $result); if ($time) { my $error = db_add($time, ($major*1000)+(1000 * $minor/$counter_overflow), 0); if ($error == -1) { syslog(LOG_INFO, "Database error"); } syslog(LOG_INFO, "Set Major = $major, Minor = $minor, (" . ($major*1000)+(1000 * $minor/$counter_overflow) . " kWh)" ); $last_hw_timestamp = $time; $last_count = $new_counter; $last_power = 0; $prev_db_timestamp = $time; $prev_db = $new_counter; $db_power = 0; } else { print $connection "Error no reply from hardware\n"; } } else { print $connection "Wrong password or zero counter value.\n"; } } else { if ($input) { print $connection "Error, input format: CALIBRATE [meter in Ws] [password]\n"; } } printf $connection "%d %d %.0f \r\n", $last_hw_timestamp, $last_count, $last_power; printf $connection "%d %d %.0f \r\n", $prev_db_timestamp, $prev_db, $db_power; print $connection "HW Timestamp: $last_hw_timestamp ("; print $connection strftime("%Y-%m-%d %H:%M:%S", localtime($last_hw_timestamp)); print $connection ") " . (time() - $last_hw_timestamp) . " seconds ago\r\n"; print $connection "HW Meter: " . ($last_count/1000) . " KWh\r\n"; printf $connection "HW Power: %.0f W\r\n", $last_power; printf $connection "HW Overflow: %d, Report: %d, (%d Wh)\r\n", $counter_overflow, $counter_report, $counter_report / ($counter_overflow / 1000 ); print $connection "DB Timestamp: $prev_db_timestamp ("; print $connection strftime("%Y-%m-%d %H:%M:%S", localtime($prev_db_timestamp)); print $connection ") " . (time() - $prev_db_timestamp) . " seconds ago\r\n"; print $connection "DB Meter: " . ($prev_db/1000) . " KWh\r\n"; printf $connection "DB Power: %.0f W\r\n", $db_power; printf $connection "DB Report: %d Wh, %d%\r\n", $db_report_wh, $db_report_delta; $connection->close(); } } # while } # main # Add entry to database. # sub db_add { my $time = shift(@_); my $count = shift(@_); my $power = shift(@_); if ($webreport) { my $ua = LWP::UserAgent->new; my $req = POST $webreport, ["pw" => "$web_password", "time" => $time, "count" => $count, "power" => $power]; $ua->request($req); undef $ua; undef $req; } if ($DBHOST) { my $sql = sprintf($sql_insert, $DBTABLE, $time, $count, $power); if (!$daemonize) { print $sql . "\n"; } my $db = DBI->connect("DBI:mysql:$DBNAME:$DBHOST", $DBUSER, $DBPASS, { PrintError => 0, RaiseError => 0 }); if (!$db) { undef $db; if (!$daemonize) { print "Database error\n"; } syslog(LOG_INFO, "Database error"); syslog(LOG_INFO, $sql); return -1; } $DBI::result = $db->prepare($sql); $DBI::result->execute(); $DBI::result->finish(); $db->disconnect; undef $db; } } # Check if serial port exists, open or wait until it reappears # sub check_port { if ( ! (-e $port_name) ) { syslog(LOG_INFO, "Port disappeared"); if (!$daemonize) { print strftime("%Y-%m-%d %H:%M:%S", localtime(time())) . ": Port disappeared\n"; } if (defined $COUNTER) { undef $COUNTER; } undef $sock; my $ttl_count = $port_ttl; while ($ttl_count && ! (-e $port_name) ) { sleep 1; if ( (-e $port_name) ) { syslog(LOG_INFO, "Port reappered"); if (!$daemonize) { print strftime("%Y-%m-%d %H:%M:%S", localtime(time())) . ": Port reappered\n"; } sleep 1; open_port(); } $ttl_count--; } } if ( ! (-e $port_name) ) { syslog(LOG_INFO, "Port gone to long exiting!"); if (!$daemonize) { print strftime("%Y-%m-%d %H:%M:%S", localtime(time())) . ": Port gone to long exiting!\n"; } exit (-1); } } # Open serial port # sub open_port { my $result; my $str; $COUNTER = new Device::SerialPort($port_name) || log_and_die("Can't open $port_name: $!\n"); $COUNTER->baudrate(115200); $COUNTER->parity("none"); $COUNTER->databits(8); $COUNTER->stopbits(1); $COUNTER->handshake("none"); my $full_portname = File::Spec->rel2abs(dirname($port_name)) . "/" . readlink($port_name); if (readlink($port_name)) { $str = "Port: " . $port_name . " ($full_portname) opened"; } else { $str = "Port: $port_name opened"; } if (!$daemonize) { print "$str\n"; } syslog(LOG_INFO, $str); sleep 1; $COUNTER->write("ATV\r\n"); sleep 1; $result = $COUNTER->input; $COUNTER->write("ATV\r\n"); sleep 1; $result = $COUNTER->input; my $ver = 0; ($ver, $mcu) = split(" ", $result); if (!int($ver)) { log_and_die("Unexpected hardware version $result\n"); } if ($result) { $str = "Version: " . int($result) . " (" . strftime("%Y%m%d %H:%M:%S", localtime(int($result))) . ")"; if (!$daemonize) { print "$str\n"; } syslog(LOG_INFO, $str); } $COUNTER->write("ATLCDSIZE\r\n"); sleep 1; $result = $COUNTER->input; ($lcd_lines, $lcd_columns) = split(" ", $result); if ($lcd_lines) { $str = "LCD: $lcd_lines x $lcd_columns, MCU: $mcu"; $COUNTER->write("ATLCDPWMSET $lcd_bl\r\n"); sleep 1; $result = $COUNTER->input; } else { $str = "LCD: no, MCU: $mcu"; } if (!$daemonize) { print "$str\n"; } syslog(LOG_INFO, $str); $COUNTER->write("ATRTCOGET\r\n"); sleep 1; $result = $COUNTER->input; $rtc_drift = int($result); $COUNTER->write("ATRTCGET\r\n"); $rtc_set = time(); sleep 1; $result = $COUNTER->input; my $rtc = int($result); if ($rtc) { my $diff = $rtc-$rtc_set; $str = "RTC: " . strftime("%Y-%m-%d %H:%M:%S", localtime($rtc)) . " ($diff s), drift = $rtc_drift"; if (!$daemonize) { print "$str\n"; } syslog(LOG_INFO, $str); if ($diff) { $rtc_set = time(); $COUNTER->write("ATRTCSET $rtc_set\r\n"); sleep 1; $result = $COUNTER->input; my $tz = int(strftime("%z", localtime())/100); my $TZ = strftime("%Z", localtime()); $COUNTER->write("ATRTCTZSET $tz\r\n"); sleep 1; $result = $COUNTER->input; $str = "Timezone set to: $tz ($TZ)"; if (!$daemonize) { print "$str\n"; } syslog(LOG_INFO, $str); $hw_tz = $tz; } } $COUNTER->write("ATCNTGET\r\n"); sleep 1; $result = $COUNTER->input; my ($major, $minor, $hertz, $report, $overflow, $hw_timestamp) = split(" ", $result); $str = "Hardware: " . ($major+($minor/$counter_overflow)) . " KWh, Overflow: $counter_overflow, Report: $counter_report (" . $counter_report / ($counter_overflow / 1000 ) . " Wh)"; if (!$daemonize) { print $str . "\n"; } syslog(LOG_INFO, $str); $COUNTER->write("ATCNTOVERFLOW " . $counter_overflow . "\r\n"); sleep 1; $result = $COUNTER->input; $COUNTER->write("ATCNTREPORT " .$counter_report . "\r\n"); sleep 1; $result = $COUNTER->input; $str = "DB Report: $db_report_wh Wh, $db_report_delta %"; if (!$daemonize) { print $str . "\n"; } syslog(LOG_INFO, $str); # TCP port if ($tcp_port) { $sock = new IO::Socket::INET ( LocalHost => '', LocalPort => $tcp_port, Proto => 'tcp', Listen => 1, # Reuse => 1, # Timeout => 1, # Blocking => 0, ); log_and_die "Could not create socket for port $tcp_port: \n" unless $sock; IO::Socket::Timeout->enable_timeouts_on($sock); $sock->read_timeout(0.1); } } # Check and adjust hardware clock # sub rtc_get() { my $result; my $line = ""; my $hour = strftime("%H", localtime()); # set timezone my $tz = int(strftime("%z", localtime())/100); my $TZ = strftime("%Z", localtime()); if ($hw_tz != $tz) { $COUNTER->write("ATRTCTZSET $tz\r\n"); sleep 1; $result = $COUNTER->input; syslog(LOG_INFO, "Timezone set to: $tz ($TZ)"); $hw_tz = $tz; } $COUNTER->write("ATRTCGET\r\n"); my $rtc_local = time(); sleep 1; $result = $COUNTER->input; my $rtc = int($result); if ($rtc) { my $diff = $rtc-$rtc_local; $line = "RTC: " . strftime("%H:%M:%S", localtime($rtc)) . " ($diff s)"; # Adjust drift if ($diff != 0) { my $drift = int ( ($rtc_local-$rtc_set)/$diff ); if ($rtc_drift) { $drift = int( abs (($rtc_drift*$drift) / ($rtc_drift+$drift)) ); } if ( (abs($diff) >= 10) || ($rtc_drift && abs($diff) >= 5)) { if (abs($diff) < 30) { $COUNTER->write("ATRTCOSET $drift\r\n"); sleep 1; $result = $COUNTER->input; $line .= ", set drift = $drift"; $rtc_drift = $drift; } else { # Only set clock $line .= ", set RTC"; } $rtc_set = time(); $COUNTER->write("ATRTCSET $rtc_set\r\n"); sleep 1; $result = $COUNTER->input; } else { $line .= ", drift: $drift"; } } syslog(LOG_INFO, $line); } } # Read config file # sub read_config_file { my $configfile; if (@ARGV && -e $ARGV[0]) { $configfile = $ARGV[0]; } else { if (-e "$FindBin::Bin/$program_name.conf") { $configfile = "$FindBin::Bin/$program_name.conf"; } else { if (-e "~/.$program_name") { $configfile = "~/.$program_name"; } else { if (-e "/etc/$program_name.conf") { $configfile = "/etc/$program_name.conf"; } } } } if (!$configfile) { die "Missing config file, supply config file as start parameter or use $program_name.conf in the same directory or ~/.$program_name or /etc/$program_name.conf!"; } my $cfg = new Config::Simple(); $cfg->read($configfile); if ($cfg->param('port_name') ne "") { $port_name = $cfg->param('port_name'); } if ($cfg->param('port_ttl') ne "") { $port_ttl = $cfg->param('port_ttl'); } if ($cfg->param('pidfile') ne "") { $pidfile = $cfg->param('pidfile'); } if ($cfg->param('daemonize') ne "") { $daemonize = $cfg->param('daemonize'); } if ($cfg->param('user') ne "") { $user = $cfg->param('user'); } if ($cfg->param('group') ne "") { $group = $cfg->param('group'); } if ($cfg->param('tcp_port') ne "") { $tcp_port = $cfg->param('tcp_port'); } if ($cfg->param('DBHOST')) { $DBHOST = $cfg->param('DBHOST'); } if ($cfg->param('DBNAME')) { $DBNAME = $cfg->param('DBNAME'); } if ($cfg->param('DBUSER')) { $DBUSER = $cfg->param('DBUSER'); } if ($cfg->param('DBPASS')) { $DBPASS = $cfg->param('DBPASS'); } if ($cfg->param('DBTABLE')) { $DBTABLE = $cfg->param('DBTABLE'); } if ($cfg->param('password')) { $web_password = $cfg->param('password'); } if ($cfg->param('webreport')) { $webreport = $cfg->param('webreport'); } if ($cfg->param('password')) { $daemon_password = $cfg->param('password'); } if ($cfg->param('counter_report') ne "") { $counter_report = int($cfg->param('counter_report')); } if ($cfg->param('counter_overflow') ne "") { $counter_overflow = int($cfg->param('counter_overflow')); } if ($cfg->param('db_report_wh') ne "") { $db_report_wh = int($cfg->param('db_report_wh')); } if ($cfg->param('db_report_delta') ne "") { $db_report_delta = int($cfg->param('db_report_delta')); } if ($cfg->param('db_max_power_check') ne "") { $db_max_power_check = int($cfg->param('db_max_power_check')); } if ($cfg->param('lcd_bl') ne "") { $lcd_bl = int($cfg->param('lcd_bl')); } undef $cfg; }