RockPi4_FanControl/pwmfan.cpp

177 lines
4.0 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

/* standard headers */
#include <iostream>
#include <signal.h>
#include <stdlib.h>
#include <unistd.h>
#include <fstream>
#include <string>
#include <cstring>
#include <fstream>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <getopt.h>
/* mraa headers */
#include "mraa/common.hpp"
#include "mraa/pwm.hpp"
/* для HDD*/
#include <sys/ioctl.h>
#include <linux/hdreg.h>
#include <cstdio>
#include <memory>
#include <stdexcept>
#include <array>
#define PWM_PORT 13
#define CPU_TEMPERATURE_FILE "/sys/class/thermal/thermal_zone0/temp"
#define T_MIN 30
#define T_MAX 50
#define INVERT_CONTROL_CURVE 1
const std::array<std::string, 4> HDD_NAMES = {"/dev/sda", "/dev/sdb", "/dev/sdc", "/dev/sdd"};
volatile sig_atomic_t flag = 1;
mraa::Pwm pwm(PWM_PORT);
// power^
// 100%| *---------
// | /
// | /
// | /
// | /
// | /
// | /
// | /
// | /
// | /
// ---====*--------*--------------->
// 0 | T_MIN T_MAX max[TEMP], celsius
float calcPower(int temperature) {
float value = 0.0f;
if (temperature < T_MIN) {
value = 0.0f;
}
if (temperature >= T_MIN && temperature <= T_MAX) {
value = (float(temperature)-float(T_MIN))/(float(T_MAX)-float(T_MIN));
if (value < 0.25) {
value = 0.0f;
}
}
if (temperature > T_MAX) {
value = 1.0f;
}
std::cout << "T:" << temperature << "С power:"<< value * 100 << "%\n";
if (INVERT_CONTROL_CURVE) {
value = 1.0f-value;
}
return value;
}
void checkControlCurve() {
std::cout << "PWM power 0%" << std::endl;
pwm.write(0.0f);
usleep(2000000);
std::cout << "PWM power 25%" << std::endl;
pwm.write(0.25f);
usleep(2000000);
std::cout << "PWM power 50%" << std::endl;
pwm.write(0.5f);
usleep(2000000);
std::cout << "PWM power 75%" << std::endl;
pwm.write(0.75f);
usleep(2000000);
std::cout << "PWM power 100%" << std::endl;
pwm.write(1.0f);
usleep(2000000);
}
void initPWM() {
std::cout << "Cycling PWM on pin " << PWM_PORT << std::endl;
pwm.enable(true);
}
int readTempCPU() {
std::fstream ftemp;
std::string raw;
ftemp.open(CPU_TEMPERATURE_FILE,std::ios::in);
if (ftemp.is_open()){
getline(ftemp, raw);
ftemp.close();
return std::stoi(raw)/1000;
} else {
std::cerr << "Read temperature error. Cannot open file.\n";
}
return -1;
}
std::string exec(const char* cmd) {
std::array<char, 128> buffer;
std::string result;
std::unique_ptr<FILE, decltype(&pclose)> pipe(popen(cmd, "r"), pclose);
if (!pipe) {
throw std::runtime_error("popen() failed!");
}
while (fgets(buffer.data(), buffer.size(), pipe.get()) != nullptr) {
result += buffer.data();
}
return result;
}
int readTempHDD(const std::string& drive) {
std::string cmd = "smartctl -A " + drive + " | grep Temperature_Celsius | awk '{print $10}'";
std::string output = exec(cmd.c_str());
return std::stoi(output);
}
int readTemp() {
int T_CPU = readTempCPU();
int max = T_CPU;
std::array<int, HDD_NAMES.size()> T_HDD;
for (int i = 0; i < HDD_NAMES.size(); ++i) {
T_HDD[i] = readTempHDD(HDD_NAMES[i]);
if (T_HDD[i] > max) {
max = T_HDD[i];
}
}
return max;
}
void sig_handler(int signum)
{
if (signum == SIGINT) {
std::cout << "Exiting..." << std::endl;
flag = 0;
}
}
int main(void)
{
signal(SIGINT, sig_handler);
std::cout << "!!!Control fan connected to RockPi4 pin " << PWM_PORT << " according to CPU&HDD temperature!!!\n";
initPWM();
checkControlCurve();
float power = 0.0f;
int temperature = 0;
while (flag) {
temperature = readTemp();
power = calcPower(temperature);
pwm.write(power);
usleep(2000000);
}
return EXIT_SUCCESS;
}