Skip to content

Instantly share code, notes, and snippets.

@minimum-necessary-change
Forked from andreacioni/fand.c
Created August 2, 2019 18:22
Show Gist options
  • Save minimum-necessary-change/e12b8553a9723dad3a37bf92488e373f to your computer and use it in GitHub Desktop.
Save minimum-necessary-change/e12b8553a9723dad3a37bf92488e373f to your computer and use it in GitHub Desktop.
Simple utility program for GNU/Linux systems to control a fan motor of your miniPC (UDOO,Raspberry Pi,Orange Pi,etc) through a PWM signal
/*
A Project of Andrea Cioni
Title: Fan Daemon (FanD)
Author: Andrea Cioni (andreacioni)
[email protected]
Copyright:
Copyright (c) 2017 Andrea Cioni <[email protected]>
https://github.com/dweeber/WiFi_Check
Purpose:
Simple utility program for GNU/Linux systems to control a fan motor of your miniPC (UDOO,Raspberry Pi,Orange Pi,etc) through a PWM signal.
Compile:
gcc -std=gnu99 fand.c -o fand
Launch:
fand <frequency of the PWM signal> <sleep seconds between temperature checks> <min temp in Celsius> <max temp in Celsius> <min dc percent> <max dc percent>
Example usage:
./fand 2 3 20 30 10 100
*/
#include<stdio.h>
#include<stdbool.h>
#include<stdlib.h>
#include<unistd.h>
#include<signal.h>
#define HELLO_BANNER "Fan Daemon"
#define VERSION "(0.2v)"
#define BASE_PATH "/sys/class/pwm/pwmchip0/"
#define EXPORT_PATH BASE_PATH "pwm0/"
static volatile bool go = true;
struct console_args {
unsigned long period;
unsigned long sleep_timeout;
unsigned long min_temp;
unsigned long max_temp;
unsigned short min_percent_dc;
unsigned short max_percent_dc;
};
bool export() {
return system("echo 0 > " BASE_PATH "export") == 0;
}
bool unexport() {
return system("echo 0 > " BASE_PATH "unexport") == 0;
}
bool enable(bool enable) {
if(enable)
return system("echo 1 > " EXPORT_PATH "enable") == 0;
else
return system("echo 0 > " EXPORT_PATH "enable") == 0;
}
bool setup(unsigned long period) {
char buf[1024];
return (sprintf(buf,"echo %lu > " EXPORT_PATH "period",period) != 0) && (system(buf) == 0);
}
bool set_dc(float percent,unsigned long period) {
char buf[1024];
percent = (percent>100.0f) ? 100.0f : percent;
unsigned long dc_ns = (percent/100)*period;
dc_ns = (dc_ns >= period) ? (period-1) : dc_ns;
printf("Percent: %0.1f %%, DC: %lu/%lu\n",percent,dc_ns,period);
return (sprintf(buf,"echo %lu > " EXPORT_PATH "duty_cycle",dc_ns) != 0) && (system(buf) == 0);
}
float read_cpu_temp() {
float ret = -1.0f;
FILE *fp = popen("/bin/cat /sys/devices/virtual/thermal/thermal_zone0/temp", "r");
char buf[1024] = {0};
if(fp != NULL) {
if(fgets(buf,1023,fp) != NULL) {
printf("Temperature read: %s",buf);
if((ret=strtoul(buf,NULL,10)) != 0) {
ret=ret/1000;
} else {
printf("Cannot convert output string\n");
}
} else {
printf("Cannot read CPU temperature from output\n");
}
pclose(fp);
} else {
printf("Cannot read CPU temperature\n");
}
return ret;
}
void kill_handler(int sig) {
printf("Terminating...\n");
go=false;
}
int run(struct console_args _args) {
int ret=0;
signal(SIGINT,kill_handler);
while(go) {
float cpu_temp = read_cpu_temp(),percentdc=0;
if(cpu_temp > _args.min_temp) {
float ratio = (cpu_temp-_args.min_temp)/(_args.max_temp - _args.min_temp);
percentdc = (ratio*(_args.max_percent_dc - _args.min_percent_dc))+_args.min_percent_dc;
}
if(!set_dc(percentdc,_args.period))
printf("There was an error setting the duty cicle");
sleep(_args.sleep_timeout);
printf("\n");
}
return ret;
}
bool parse_arguments(int argv, char **argc,struct console_args *_args) {
bool ret=true;
if(argv == 7) {
unsigned long ul = strtoul(argc[1],NULL,10);
if(ul != 0) {
_args->period = (1.0f/ul)*100000000L;
printf("Frequency: %lu Hz, Period: %lu s\n",ul,_args->period);
} else
ret &= false;
ul = strtoul(argc[2],NULL,10);
if(ul != 0) {
_args->sleep_timeout = ul;
printf("Sleep timeout: %lu s\n",_args->sleep_timeout);
} else
ret &= false;
ul = strtoul(argc[3],NULL,10);
if(ul != 0) {
_args->min_temp = ul;
printf("Min temperature: %lu *C\n",_args->min_temp);
} else
ret &= false;
ul = strtoul(argc[4],NULL,10);
if(ul != 0) {
_args->max_temp = ul;
printf("Max temperature: %lu *C\n",_args->max_temp);
} else
ret &= false;
ul = strtoul(argc[5],NULL,10);
if(ul != 0) {
_args->min_percent_dc = (unsigned short) ul;
printf("Min duty cycle: %i %%\n",_args->min_percent_dc);
} else
ret &= false;
ul = strtoul(argc[6],NULL,10);
if(ul != 0) {
_args->max_percent_dc = (unsigned short) ul;
printf("Max duty cycle: %i %%\n",_args->max_percent_dc);
} else
ret &= false;
} else
ret=false;
return ret;
}
int main(int argv, char **argc) {
int ret = 0;
struct console_args _args;
printf("%s %s\n",HELLO_BANNER,VERSION);
if(!parse_arguments(argv,argc,&_args)) {
printf("fand <frequency> <sleep seconds> <min temp> <max temp> <min dc percent> <max dc percent> \n");
ret = 100;
} else {
printf("Exporting...");
if(export()) {
printf("OK!\n");
printf("Setupping (f=%lu)...",_args.period);
if(setup(_args.period)) {
printf("OK!\n");
printf("Enabling...");
if(enable(true)) {
printf("OK!\n");
ret = run(_args);
printf("Disabling...");
if(enable(false)) {
printf("OK!\n");
} else {
printf("FAIL!\n");
ret = 4;
}
} else {
printf("FAIL!\n");
ret=3;
}
} else {
printf("FAIL!\n");
ret=2;
}
} else {
printf("FAIL!\n");
ret=1;
}
printf("Unexporting...");
if(unexport()) {
printf("OK!\n");
} else {
printf("FAIL!\n");
}
}
return ret;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment