leempi
Posts: 4
Joined: Sat Aug 26, 2017 12:23 am

Intelligent Pi cooling

Sun Aug 04, 2019 4:14 am

OK intelligent is a stretch but all my non-pi systems spin fans up/down based on cooling needs. I wanted that on my Pis too. When my current code didnt' work on my pi4 since it appears wiringpi and its gpio command don't support this chipset yet I found a different way to do it.

First expose the hardware PWM interface in /sys/class/pwm by appending a line in /boot/config.txt and reboot:

dtoverlay=pwm-2chan

Source an N-channel JFET and a PiFan. I am not affiliated with Amazon but here are example URLS:

https://www.amazon.com/gp/product/B074DJGZ2S - JEFTs i buy in bulk since I use them in low-current LED projects
https://www.amazon.com/gp/product/B0792BW2VH - PiFans

This is PERL code and that dates me and I'm assuming Raspian here. But you can poke /sys/class/pwm/* with the language of your choice.

I'm also assuming you are using physical rPi pin 12 and tweak code based on your cooling needs.

Here's one of mine: http://mayeses.com/temp/pi_cooling.jpg

Here's the daemon, must run as root since writing to /sys/class/pwm/*

Code: Select all

#!/usr/bin/perl
# Quick daemon to spin pifan up/down based on CPU temp
# 28 Jul 2019 - Lee Mayes <ntac@mayeses.com>
# NOTE: You MUST enable /sys/class/pwm interface in /boot/config.txt by adding 'dtoverlay=pwm-2chan'
# Wiring of nchannel JFET/Fan:
# PiFan+                  - directly to 5V rail on rpi (Pin 2 or 4)
# PiFan-                  - Drain on JFET
# rPi phys pin 12/BCOM 18 - Gate on JEFT
# rPi GND (pin 6,14,etc.) - Source on JFET
use strict;
my $debug = $ARGV[0];				# pass any arg to enable debug on STDOUT
use IPC::SysV qw(IPC_CREAT);
use IPC::Semaphore;
my $fan_pin = 18;				# rPi Physcial pin #12/BCOM 18/PWM0
my $pwmdir = "/sys/class/pwm/pwmchip0";		# head of PWM /sys/class filesystem struct
my $poll_int = 1;				# poll temp every Xs
my $temp_thres = 130;				# Temp(F) to spin fan up past idle 
my $idle_pwm = .6;				# Low speed/0 noise PWM duty_cycle pct
my $max_temp = 155;				# Temp(F) to run fan at full speed 
my $fast_pwm = 1;				# Fan on full speed PWM level 100%
my $period = 10000000;				# PWM period in ns (100Hz here)
my $base_cycle = int($idle_pwm * $period);	# Min speed duty cycle
my $max_cycle = int($fast_pwm * $period);	# Max speed duty cycle
my $delta_cycle = $max_cycle - $base_cycle;	# How much wiggle room based on temp
my $sem = &get_sem();				# Create semaphore for fanmon
print "base = $base_cycle + delta = $delta_cycle == max = $max_cycle\n" if ($debug);

# Initialize - enable pwm0
open(P,">$pwmdir/export") ||
  die "Cannot write to $pwmdir/export : $!\n";; 
print P "0\n"; 
close(P);
sleep 1;					# Allow hardware to set up
# Set PWM hardware clock rate in nanosecs
system "echo $period >$pwmdir/pwm0/period ";    # cannot write in perl, inappro IOCTL? so fork()
# Enable it
system "echo 1 >$pwmdir/pwm0/enable";

while ( 1 ) {					# Forever
  my ($c,$f) = &read_temp;			# Check temp
  print "$c C, $f F\n" if ($debug);
  my $speed = $base_cycle;
  my $offset = 0;
  if ($f >= $max_temp) { 			# overtemp, run @ max
    $speed = int($period * $fast_pwm);
    $offset = 1;
  } elsif ($f >= $temp_thres) {			# rPi has the vapors
    $offset = 1 - ($max_temp - $f) / ($max_temp - $temp_thres);
    $speed = $base_cycle + int($offset * $delta_cycle)
  }
  print " \$delta = $delta_cycle, \$offset = $offset, \$speed = $speed\n" if $debug;  
  open(P,">$pwmdir/pwm0/duty_cycle") ||
    die "Cannot write to $pwmdir/pwm0/duty_cycle : $!\n";; 
  print P "$speed" if ($debug);
  close(P);
  $speed = int(100*($speed/$period));
  $sem->setval(1,$speed);			# poke speed in sem 1
  sleep($poll_int);
}

sub read_temp {					# Grab ARM chip temp

  open(C,"/sys/class/thermal/thermal_zone0/temp");
  my $c = <C>;
  close(C);
  chomp($c);
  $c /= 1000;					# Temp in C
  my $f = ($c * 1.8) + 32;			# Temp in F
  $sem->setval(0,int($f));			# poke temp in sem 0
  return ($c,$f);

}

sub get_sem {

  my $keyfile = "/var/tmp/.keyfile";
  my $key;
  if (-f $keyfile) {
    open(KEY,"$keyfile");
    $key = <KEY>;
    chomp($key);
    close(KEY);
  } else {
    print "Creating $keyfile\n" if ($debug);
    open(KEY,">$keyfile") ||
      die "Cannot write to $keyfile : $!\n";
    $key = IPC::SysV::ftok($keyfile,2);
    print KEY "$key\n";
    close(KEY);
  }
  my $flags = 0644;
  my $sem = new IPC::Semaphore($key,2,$flags);                  # Open semaphore
  unless ($sem) {
    $flags = $flags | IPC_CREAT;
    $sem = new IPC::Semaphore($key,2,$flags);                   # Create if it does not exist
    print "Create semaphore with key $key\n" if ($debug);
  }
  return($sem);

}
A simple monitor that can run as a mortal user:

Code: Select all

#!/usr/bin/perl
# monitor sem set by fan_daemon
use strict;
use IPC::SysV qw(IPC_RMID IPC_STAT IPC_CREAT);  # SysV IPC
use IPC::Semaphore;
my $sem = &get_sem();				# Create semaphore
while ( 1 ) {
  my ($temp,$speed) = $sem->getall();
  print "Temp = $temp F, Speed = $speed %\n";
  sleep(1);
}

sub get_sem {

  my $node = shift;                                             # Which sensor's sem?
  my $keyfile = "/var/tmp/.keyfile";
  unless (-r $keyfile) { die "cannot read $keyfile : $!\n" }
  my $key = IPC::SysV::ftok($keyfile,1);
  my $flags = 0444;
  my $sem = new IPC::Semaphore($key,1,$flags);                  # Open semaphore
  unless ($sem) { die "failed to open sem w/key $key : $!\n" }
  return($sem);

}

Cheers!


leem@bench:~/bin $ gpio -v
gpio version: 2.50
Copyright (c) 2012-2018 Gordon Henderson
This is free software with ABSOLUTELY NO WARRANTY.
For details type: gpio -warranty

Raspberry Pi Details:
Type: Unknown17, Revision: 01, Memory: 0MB, Maker: Sony
* Device tree is enabled.
*--> Raspberry Pi 4 Model B Rev 1.1
* This Raspberry Pi supports user-level GPIO access.
leem@bench:~/bin $ sudo gpio readall
Oops - unable to determine board type... model: 17
leem@bench:~/bin $

Ernst
Posts: 1246
Joined: Sat Feb 04, 2017 9:39 am
Location: Germany

Re: Intelligent Pi cooling

Sun Aug 04, 2019 7:00 am

leempi wrote:
Sun Aug 04, 2019 4:14 am
OK intelligent is a stretch but all my non-pi systems spin fans up/down based on cooling needs. I wanted that on my Pis too. When my current code didnt' work on my pi4 since it appears wiringpi and its gpio command don't support this chipset yet I found a different way to do it.

.....

leem@bench:~/bin $ gpio -v
gpio version: 2.50
Copyright (c) 2012-2018 Gordon Henderson
This is free software with ABSOLUTELY NO WARRANTY.
For details type: gpio -warranty

Raspberry Pi Details:
Type: Unknown17, Revision: 01, Memory: 0MB, Maker: Sony
* Device tree is enabled.
*--> Raspberry Pi 4 Model B Rev 1.1
* This Raspberry Pi supports user-level GPIO access.
leem@bench:~/bin $ sudo gpio readall
Oops - unable to determine board type... model: 17
leem@bench:~/bin $
http://wiringpi.com/wiringpi-updated-to ... rry-pi-4b/
The road to insanity is paved with static ip addresses

Return to “General discussion”