#!/usr/bin/perl
#-----------------------------------------------------------------
#
# Title:
# sensors.rrd.pl
#
# Purpouse:
# Chart temp and fan stats
# Keep fan as slow as possible given safe Target CPU Temp.
#
# To find the correct settings, run: pwmconfig
# 9191-0290/fan1_input current speed: 17307 RPM
# 9191-0290/fan2_input current speed: 17307 RPM
# 9191-0290/fan3_input current speed: 15000 RPM
#
# Fan's controlled by:
# /sys/devices/platform/i2c-9191/9191-0290/pwm1
# /sys/devices/platform/i2c-9191/9191-0290/pwm2
#
#-----------------------------------------------------------------
my $debug= "1"; # debug 1=on, 0=off
my $graph= "1"; # graph 1=on, 0=off
my $quiet= "0"; # change fan speed 1=on, 0=off
my $do_rrd= "0"; # rrd create/update 1=on, 0=off
my $pause= "5"; # Time between tests
my $sensors= "/usr/bin/sensors"; # Binary for sensors
my $Sensor_string= "w83627hf-isa-0290"; # Header in sensors output
my $time=time( );
my $Speed1= "100" ; # Setting default fan speed.
my $TargetTmp_upper="44"; # Target temp for CPU
my $TargetTmp_lower="42"; # Target temp for CPU
my $ProcFile= "/sys/devices/platform/i2c-9191/9191-1410/pwm2";
my $rrd_file= "/root/rrd/fan_temp.rrd";
my $graph_file_2hr= "/root/rrd/fan_temp_2hr.png";
my $graph_file_12hr="/root/rrd/fan_temp_12hr.png";
my $graph_file_1wk="/root/rrd/fan_temp_1wk.png";
my %stats=( temp1 => "0",
temp2 => "0",
temp3 => "0",
rpm1 => "0",
rpm2 => "0",
rpm3 => "0"
);
#-----------------------------------------------------------------
# flush the buffer
$| = 1;
#-----------------------------------------------------------------
# daemonize the program
if ( $debug eq "0" ){ &daemonize; }
#-----------------------------------------------------------------
# Set initial fan speed to our base
#&update_fanspeed($Speed1);
#-----------------------------------------------------------------
# Main Loop,
# Runs forever
while ( 1 ){
if ( $debug eq "1" ){
print "Contents of hash before.\n";
for my $key ( sort ( keys ( %stats ) ) ){
print "\t$key:\t$stats{$key}\n";
}
}
#
# Get temp
#
&get_i2c( \%stats ) || die "can't check sensors:\t $!\n";
if ( $debug eq "1" ){
print "Contents of hash after\n";
for my $key ( sort ( keys ( %stats ) ) ){
print "\t$key:\t$stats{$key}\n";
}
}
if ( $quiet eq "1" ){
#
# Get speed
#
chomp ( $Speed1 = &get_file_fanspeed );
if ( $debug eq "1" ){ print "Speed: $Speed1\n"; }
#
# Calculate new speed
#
my $NewSpeed=&new_fanspeed($Temp,$Speed1);
if ( $debug eq "1" ){ print "NewSpeed: $NewSpeed\n"; }
#
# If Speed and NewSpeed are not equal, update fan speed file.
#
if ( $NewSpeed ne $Speed1 ){
&update_fanspeed($NewSpeed);
if ( $debug eq "1" ){ print "Loading new speed\n"; }
}
}
if ( $do_rrdeq eq "1" ){
#
# Create RRD file, if it does not exist
#
if ( ! -e "$rrd_file" ){
&create_graph($rrd_file);
};
#
# Record Fan speed
#
$Rpm=($Rpm/100);
if ( $debug eq "1" ){ print "$Temp,$Speed1,$Rpm,$rrd_file\n"; }
my $NewSpeed=&record_fanspeed($Temp,$Speed1,$Rpm,$rrd_file);
#
# Graph from rrd
#
&graph_fanspeed($rrd_file,$graph_file_2hr,$graph_file_12hr,$graph_file_1wk);
}
#
#
# Sleep
#
system ( sleep $pause );
}
#-----------------------------------------------------------------
sub daemonize {
chdir '/' or die "Can't chdir to /: $!";
open STDIN, '/dev/null' or die "Can't read /dev/null: $!";
open STDOUT, '>>/dev/null' or die "Can't write to /dev/null: $!";
open STDERR, '>>/dev/null' or die "Can't write to /dev/null: $!";
defined(my $pid = fork) or die "Can't fork: $!";
exit if $pid;
setsid or die "Can't start a new session: $!";
umask 0;
}
#-----------------------------------------------------------------
sub create_graph {
chomp ( my $rrd_file=shift );
# rrdtool create $rrd_directory/fan_temp.rrd \
# --step 1 \ # 1 sec steps for RRA's blow
# DS:Temp:GAUGE:10:0:U \ # DataSource: over max of 10sec, "0" minimum and unknown max value as valid data
# DS:Speed:GAUGE:10:0:U \ # DataSource: over max of 10sec, "0" minimum and unknown max value as valid data
# DS:Rpm:GAUGE:10:0:U \ # DataSource: over max of 10sec, "0" minimum and unknown max value as valid data
# RRA:AVERAGE:0.5:1:10 \ # Archive: Average over 1 measurement (10 sec), 10 values should be stroed
# RRA:AVERAGE:0.5:6:60 \ # Archive: Average over 6 measurements (1 min), 60 values should be stroed
# RRA:AVERAGE:0.5:24:240 \ # Archive: Average over 24 measurements (4 min), 240 values should be stroed
# RRA:AVERAGE:0.5:288:2880 \ # Archive: Average over 288 measurements (42 min),2880 values should be stroed
# RRA:MAX:0.5:1:10 \ # Archive: MAX over 1 measurements (10 sec), 10 values should be stroed
# RRA:MAX:0.5:6:60 \ # Archive: MAX over 6 measurements (1 min), 60 values should be stroed
# RRA:MAX:0.5:24:240 \ # Archive: MAX over 24 measurements (4 min), 240 values should be stroed
# RRA:MAX:0.5:288:2880 \ # Archive: MAX over 288 measurements (42 min),2880 values should be stroed
# RRA:LAST:0.5:1:10 \ # Archive: Last over 1 measurement (10 sec), 10 values should be stored
# RRA:LAST:0.5:6:60 \ # Archive: MAX over 6 measurements (1 min), 60 values should be stroed
# RRA:LAST:0.5:24:240 \ # Archive: MAX over 24 measurements (4 min), 240 values should be stroed
# RRA:LAST:0.5:288:2880 \ # Archive: MAX over 288 measurements (42 min),2880 values should be stroed
system( qq{rrdtool create $rrd_file \\
--step 1 \\
DS:Temp:GAUGE:10:0:U \\
DS:Speed:GAUGE:10:0:U \\
DS:Rpm:GAUGE:10:0:U \\
RRA:AVERAGE:0.5:1:10 \\
RRA:AVERAGE:0.5:6:60 \\
RRA:AVERAGE:0.5:24:240 \\
RRA:AVERAGE:0.5:288:2880 \\
RRA:MAX:0.5:1:10 \\
RRA:MAX:0.5:6:60 \\
RRA:MAX:0.5:24:240 \\
RRA:MAX:0.5:288:2880 \\
RRA:LAST:0.5:1:10 \\
RRA:LAST:0.5:6:60 \\
RRA:LAST:0.5:24:240 \\
RRA:LAST:0.5:288:2880} );
}
#-----------------------------------------------------------------
sub record_fanspeed{
my $temp=shift;
my $Speed1=shift;
my $rpm=shift;
my $rrd_file=shift ;
system ( "rrdtool update $rrd_file --template Temp:Speed:Rpm N:$temp:$Speed1:$rpm" );
}
#-----------------------------------------------------------------
sub graph_fanspeed {
my $rrd_file=shift;
my $graph_file_2hr=shift;
my $graph_file_12hr=shift;
my $graph_file_1wk=shift;
#####################################
# For the graph:
# Temp a=ave b=max c=last
# Speed d=ave e=max f=last
# Rpm g=ave h=max i=last
#####################################
#
# Draw 2 Hour png
#
system( "rrdtool", "graph", "$graph_file_2hr",
"--imgformat=PNG",
"--start=-2880",
"-c", "BACK#343435", "-c", "FONT#ffffff", "-c", "CANVAS#605f60",
"--title=2 Hour Fanspeed Tmp",
"--height=150", "--width=400",
"--vertical-label=Red=degreeC, Yellow=speed, Blue=rpm",
"--step", "10",
"DEF:a=$rrd_file:Temp:AVERAGE",
"DEF:b=$rrd_file:Temp:MAX",
"DEF:c=$rrd_file:Temp:LAST",
"DEF:d=$rrd_file:Speed:AVERAGE",
"DEF:e=$rrd_file:Speed:MAX",
"DEF:f=$rrd_file:Speed:LAST",
"DEF:g=$rrd_file:Rpm:AVERAGE",
"DEF:h=$rrd_file:Rpm:MAX",
"DEF:i=$rrd_file:Rpm:LAST",
"LINE1:c#ea0e2b:Temp\\r:",
"GPRINT:a:AVERAGE:Average\\t%6.0lf%s\\r",
"GPRINT:b:MAX:Max\\t%6.0lf%s\\r",
"GPRINT:c:LAST:Last\\t%6.0lf%s\\r",
"COMMENT:\\n",
"LINE1:f#EACC00:Speed\\r:",
"GPRINT:d:AVERAGE:Average\\t%6.0lf%s\\r",
"GPRINT:e:MAX:Max\\t%6.0lf%s\\r",
"GPRINT:f:LAST:Last\\t%6.0lf%s\\r",
"COMMENT:\\n",
"LINE1:g#d4dcf8:Rpm\\r:",
"GPRINT:g:AVERAGE:Average\\t%6.0lf%s\\r",
"GPRINT:h:MAX:Max\\t%6.0lf%s\\r",
"GPRINT:i:LAST:Last\\t%6.0lf%s\\r",
"COMMENT:\\n"
);
#
# Draw 12 Hour png
#
system( "rrdtool", "graph", "$graph_file_12hr",
"--imgformat=PNG",
"--start=-86400",
"-c", "BACK#343435", "-c", "FONT#ffffff", "-c", "CANVAS#605f60",
"--title=12 Hour Fanspeed Tmp",
"--height=150", "--width=400",
"--vertical-label=Red=degreeC, Yellow=speed, Blue=rpm",
"--step", "10",
"DEF:a=$rrd_file:Temp:AVERAGE",
"DEF:b=$rrd_file:Temp:MAX",
"DEF:c=$rrd_file:Temp:LAST",
"DEF:d=$rrd_file:Speed:AVERAGE",
"DEF:e=$rrd_file:Speed:MAX",
"DEF:f=$rrd_file:Speed:LAST",
"DEF:g=$rrd_file:Rpm:AVERAGE",
"DEF:h=$rrd_file:Rpm:MAX",
"DEF:i=$rrd_file:Rpm:LAST",
"LINE1:c#ea0e2b:Temp\\r:",
"GPRINT:a:AVERAGE:Average\\t%6.0lf%s\\r",
"GPRINT:b:MAX:Max\\t%6.0lf%s\\r",
"GPRINT:c:LAST:Last\\t%6.0lf%s\\r",
"COMMENT:\\n",
"LINE1:f#EACC00:Speed\\r:",
"GPRINT:d:AVERAGE:Average\\t%6.0lf%s\\r",
"GPRINT:e:MAX:Max\\t%6.0lf%s\\r",
"GPRINT:f:LAST:Last\\t%6.0lf%s\\r",
"COMMENT:\\n",
"LINE1:g#d4dcf8:Rpm\\r:",
"GPRINT:g:AVERAGE:Average\\t%6.0lf%s\\r",
"GPRINT:h:MAX:Max\\t%6.0lf%s\\r",
"GPRINT:i:LAST:Last\\t%6.0lf%s\\r",
"COMMENT:\\n"
);
#
# Draw 1week png
#
system( "rrdtool", "graph", "$graph_file_1wk",
"--imgformat=PNG",
"--start=-604800",
"-c", "BACK#343435", "-c", "FONT#ffffff", "-c", "CANVAS#605f60",
"--title=1 Week Fanspeed Tmp",
"--height=150", "--width=400",
"--vertical-label=Red=degreeC, Yellow=speed, Blue=rpm",
"--step", "10",
"DEF:a=$rrd_file:Temp:AVERAGE",
"DEF:b=$rrd_file:Temp:MAX",
"DEF:c=$rrd_file:Temp:LAST",
"DEF:d=$rrd_file:Speed:AVERAGE",
"DEF:e=$rrd_file:Speed:MAX",
"DEF:f=$rrd_file:Speed:LAST",
"DEF:g=$rrd_file:Rpm:AVERAGE",
"DEF:h=$rrd_file:Rpm:MAX",
"DEF:i=$rrd_file:Rpm:LAST",
"LINE1:c#ea0e2b:Temp\\r:",
"GPRINT:a:AVERAGE:Average\\t%6.0lf%s\\r",
"GPRINT:b:MAX:Max\\t%6.0lf%s\\r",
"GPRINT:c:LAST:Last\\t%6.0lf%s\\r",
"COMMENT:\\n",
"LINE1:f#EACC00:Speed\\r:",
"GPRINT:d:AVERAGE:Average\\t%6.0lf%s\\r",
"GPRINT:e:MAX:Max\\t%6.0lf%s\\r",
"GPRINT:f:LAST:Last\\t%6.0lf%s\\r",
"COMMENT:\\n",
"LINE1:g#d4dcf8:Rpm\\r:",
"GPRINT:g:AVERAGE:Average\\t%6.0lf%s\\r",
"GPRINT:h:MAX:Max\\t%6.0lf%s\\r",
"GPRINT:i:LAST:Last\\t%6.0lf%s\\r",
"COMMENT:\\n"
);
}
#-----------------------------------------------------------------
sub update_fanspeed {
my $NewSpeed=shift;
open ( FANSPEED, ">>$ProcFile") || die "Cannot read fanspeed file: $?\n";
print FANSPEED "$NewSpeed";
close ( FANSPEED ) || die "Cannot close fanspeed file: $?\n";
return;
}
#-----------------------------------------------------------------
sub new_fanspeed {
my $Temp=shift;
my $Speed1=shift;
#
# If temp is over 42, increment fan speed.
#
if ( "$Temp" > $TargetTmp_upper ){ unless( $Speed1 eq 255){ ++$Speed1 ; } }
#
# If temp is under 42, decrement fan speed.
#
if ( $Temp < $TargetTmp_lower ){ unless( $Speed1 eq 0 ){ --$Speed1 ; } }
#
# Return the new speed
#
return ( $Speed1 );
}
#-----------------------------------------------------------------
sub get_file_fanspeed {
open ( FANSPEED, "<$ProcFile") || die "Cannot read fanspeed file: $?\n";
chomp ( my $Speed1=<FANSPEED> );
close ( FANSPEED ) || die "Cannot close fanspeed file: $?\n";
return ( $Speed1 );
}
#-----------------------------------------------------------------
sub get_i2c {
my $stat_ref = $_;
my $temp1;
my $temp2;
my $temp3;
my $rpm1;
my $rpm2;
my $rpm3;
if ( $debug eq "1" ){ print "Parsing lm_sensors output\n"; }
# Process output of `sensors` one line at a time.
open ( SENSORS, "$sensors |") || die "Cannot run sensors\n";
while (<SENSORS>){
########################################
# Screen for relevent sensor data
# - If lm_sensors modules are loade incorrectly,
# double output of incorrect values result.
# - This allows one to specify which to collect
########################################
# Start collecting data when this line is reached
if ( $_ =~ m/$Sensor_string/ ){
$Start="1";
# Stop Collecting data when this line is reached
} elsif ( $_ =~ m/^$/ ) {
$Start="0";
done;
}
# If Start is 1, we are working with good data
if ( $Start eq "1" ){
#
# Grab the main
#
$bla=$_;
# print "$bla";
#
# Example string: temp1: +18°C (high = -1°C, hyst = -17°C) sensor = thermistor ALARM
#
if ( $bla =~ m{
temp1: # Starts with temp:
\W* # NonWord characters
\+
(\d+) # digits are the temp
.* # Anything up to
\n # new line
}xig # allow comments, case insensetive, global
) {
#
# Debug: Print the captured value
#
if ( $debug eq "1" ){ print "\tTemp1:\t$1\n"; }
$temp1="$1";
}
#
# Example string: temp2: +18°C (high = -1°C, hyst = -17°C) sensor = thermistor ALARM
#
if ( $bla =~ m{
temp2: # Starts with temp:
\W* # NonWord characters
\+
(\d+) # digits are the temp
.* # Anything up to
\n # new line
}xig # allow comments, case insensetive, global
) {
#
# Debug: Print the captured value
#
if ( $debug eq "1" ){ print "\tTemp2:\t$1\n"; }
$temp2="$1";
}
#
# Example string: temp3: +18°C (high = -1°C, hyst = -17°C) sensor = thermistor ALARM
#
if ( $bla =~ m{
temp3: # Starts with temp:
\W* # NonWord characters
\+
(\d+) # digits are the temp
.* # Anything up to
\n # new line
}xig # allow comments, case insensetive, global
) {
#
# Debug: Print the captured value
#
if ( $debug eq "1" ){ print "\tTemp3:\t$1\n"; }
$temp3="$1";
}
#
# Example string: fan1: 4528 RPM (min = 941 RPM, div = 2)
#
if ( $bla =~ m{
^fan1: # starts with fan
\W* # NonWord characters
(\d+) # rpm number
\W* # NonWord characters
RPM # text RPM
.* # Anything up to
\n # new line
}xig # allow comments, case insensetive, global
) {
#
# Debug: Print the captured value
#
if ( $debug eq "1" ){ print "\tFan1:\t$1\n"; }
$rpm1="$1";
}
#
# Example string: fan2: 4528 RPM (min = 941 RPM, div = 2)
#
if ( $bla =~ m{
^fan2: # starts with fan
\W* # NonWord characters
(\d+) # rpm number
\W* # NonWord characters
RPM # text RPM
.* # Anything up to
\n # new line
}xig # allow comments, case insensetive, global
) {
#
# Debug: Print the captured value
#
if ( $debug eq "1" ){ print "\tFan2:\t$1\n"; }
$rpm2="$1";
}
#
# Example string: fan3: 4528 RPM (min = 941 RPM, div = 2)
#
if ( $bla =~ m{
^fan3: # starts with fan
\W* # NonWord characters
(\d+) # rpm number
\W* # NonWord characters
RPM # text RPM
.* # Anything up to
\n # new line
}xig # allow comments, case insensetive, global
) {
#
# Debug: Print the captured value
#
if ( $debug eq "1" ){ print "\tFan3:\t$1\n"; }
$rpm3="$1";
}
}
}
# Should never get here.
close ( SENSORS );
if ( 1 ){
$stats_ref->{temp1} = "$temp1";
$stats_ref->{temp2} = "$temp2";
$stats_ref->{temp3} = "$temp3";
$stats_ref->{rpm1} = "$rpm1";
$stats_ref->{rpm2} = "$rpm2";
$stats_ref->{rpm3} = "$rpm3";
if ( $debug eq "1" ){
print "Contents of hash_reference\n";
for my $key ( sort ( keys ( %$stats_ref )) ){
print "\t$key:\t$stats_ref->{$key}\n";
}
}
return ( 1 ) ;
} else {
exit 1;
}
}
#-----------------------------------------------------------------