Commit 92ea35f5 authored by Tobias Rautenkranz's avatar Tobias Rautenkranz

initial import

parents
This diff is collapsed.
LDFLAGS := -lwiringPi
CXXFLAGS := -std=c++14 -Wall
fancontrol: fancontrol.cpp
$(CXX) $(LDFLAGS) $(CXXFLAGS) $^ -o $@
.PHONY: clean
clean:
rm -f -- fancontrol
.PHONY: install
install: fancontrol
install -o root -g root fancontrol /usr/local/sbin/
install -o root -g root fancontrol.service /etc/systemd/system/
@echo "run:\n systemctl deamon-reload\n systemctl enable fancontrol.service\nto execute on startup"
/*
* Copyright (C) 2018 Tobias Rautenkranz <mail@tobias.rautenkranz.ch>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <system_error>
#include <cassert>
#include <cstdlib>
#include <fstream>
#include <iostream>
#include <atomic>
#include <unistd.h>
#include <getopt.h>
#include <signal.h>
#include <wiringPi.h>
class cpu_temperature {
public:
cpu_temperature()
: file("/sys/class/thermal/thermal_zone0/temp")
{
file.exceptions(std::ifstream::failbit | std::ifstream::badbit);
}
double get()
{
file.sync();
file.seekg(0);
long t;
file >> t;
return celcius(t);
}
private:
static constexpr double celcius(long t)
{ return 1e-3 * t; }
std::ifstream file;
};
class signal_handler {
public:
signal_handler()
{
int ret;
// SIGHUP
struct sigaction action;
action.sa_handler = SIG_IGN; // No configuration file
ret = sigemptyset(&action.sa_mask);
if (-1 == ret) throw std::system_error(errno, std::generic_category(), "sigemptyset");
action.sa_flags = 0;
ret = sigaction(SIGHUP, &action, NULL);
if (-1 == ret) throw std::system_error(errno, std::generic_category(), "sigaction");
// SIGTERM
action.sa_handler = on_sigterm;
ret = sigaddset(&action.sa_mask, SIGTERM);
if (-1 == ret) throw std::system_error(errno, std::generic_category(), "sigaddset");
ret = sigaction(SIGTERM, &action, NULL);
if (-1 == ret) throw std::system_error(errno, std::generic_category(), "sigaction");
}
bool is_terminate() const
{ return terminate_requested; }
private:
static std::atomic<bool> terminate_requested;
static void on_sigterm (int nr) {
assert(nr == SIGTERM);
terminate_requested = true;
}
};
std::atomic<bool> signal_handler::terminate_requested(false);
class wiring_pi {
public:
static void init()
{
if (geteuid() != 0) {
throw std::runtime_error("not root");
}
init_impl();
}
private:
static void init_impl()
{
static auto ret = wiringPiSetupGpio();
assert(ret == 0); // always returns 0
}
};
class fan_control {
public:
fan_control(int pin)
: pin(pin)
{
assert(2 <= pin && pin <= 28);
wiring_pi::init();
pinMode(pin, OUTPUT);
}
~fan_control()
{
set_on(on_exit);
}
void set_temperature(double temp)
{
if (temp < temp_off) {
set_on(false);
} else if (temp > temp_on) {
set_on(true);
}
}
void set_on(bool on)
{
// inverted due to mosfet drive circuit
digitalWrite(pin, on ? LOW : HIGH);
}
bool is_on()
{
// inverted due to mosfet drive circuit
return !digitalRead(pin);
}
void set_temperature_treshold(double off, double on)
{
assert(off < on);
temp_off = off;
temp_on = on;
}
void set_on_exit(double on)
{
on_exit = on;
}
private:
double on_exit = true;
const int pin = 12;
double temp_off = 48;
double temp_on = 50;
};
/*
* Systemd handles damonization
* http://0pointer.de/public/systemd-man/daemon.html#New-Style%20Daemons
*/
static void print_help()
{
std::cout << "Turns the fan on a Raspberry Pi on/off base on the cpu temperature;\n";
std::cout << "using a custom control circuit\n\n";
std::cout << "usage: fan_contol [-vh]\n";
}
int
main (int argc, char** argv)
{
constexpr int sleep_delay = 1; // s
constexpr int gpio_pin = 21;
bool verbose = false;
while (1) {
int option_index = 0;
static struct option long_options[] = {
{"help", no_argument, 0, 'h' },
{"verbose", no_argument, 0, 'v' },
{0, 0, 0, 0 }
};
int c = getopt_long(argc, argv, "hv", long_options, &option_index);
if (c == -1)
break;
switch (c) {
case 'h':
print_help();
return EXIT_SUCCESS;
case 'v':
verbose = true;
break;
default:
case '?':
return EXIT_FAILURE;
}
if (optind < argc) {
printf("non-option ARGV-elements: ");
return EXIT_FAILURE;
}
}
if (geteuid() != 0) {
std::cerr << "error: must be run as root\n";
return EXIT_FAILURE;
}
signal_handler sa;
cpu_temperature temp;
assert(-80 <= temp.get() && temp.get() <= 100);
assert(sleep_delay > 0);
if (verbose) std::cout << "using gpio pin: " << gpio_pin << " for control\n";
fan_control fan(gpio_pin);
while (!sa.is_terminate()) {
auto t = temp.get();
fan.set_temperature(t);
if (verbose) {
std::cout << "CPU: " << t << "°C fan: " << (fan.is_on()?"on":"off") << '\n';
}
auto time_remaining = sleep(sleep_delay);
if (time_remaining > 0) {
sleep(time_remaining);
}
}
return EXIT_SUCCESS;
}
[Unit]
Description=Custom Fan Speed Control for the Raspberry Pi
Documentation=https://gitlab.com/tobiasrautenkranz/raspberry-pi-fancontrol
[Service]
ExecStart=/usr/local/sbin/fancontrol
[Install]
WantedBy=multi-user.target
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment