Skip to content

Instantly share code, notes, and snippets.

@felipemanga
Last active February 2, 2024 03:36
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save felipemanga/f86683a8db11ee5d5f07549de2293c45 to your computer and use it in GitHub Desktop.
Save felipemanga/f86683a8db11ee5d5f07549de2293c45 to your computer and use it in GitHub Desktop.
Improved devterm automatic gearbox
// Compile like this: g++ fullauto.cpp --std=c++17 -o fullauto
// Run like this: sudo fullauto &
#include <thread>
#include <chrono>
#include <iostream>
#include <fstream>
#include <sstream>
#include <vector>
using namespace std;
using namespace chrono;
using namespace chrono_literals;
ifstream stat{"/proc/stat"};
ifstream ac{"/sys/class/power_supply/axp22x-ac/present"};
ifstream cpuTemp{"/sys/class/thermal/thermal_zone0/temp"};
milliseconds batteryPollRate = 500ms, acPollRate = 500ms;
const string& read(ifstream& stream) {
static stringstream buf;
static string out;
buf.str("");
stream.seekg(0);
buf << stream.rdbuf();
out = buf.str();
return out;
}
bool hasACPower() {
return read(ac)[0] == '1';
}
float getCPUTemp() {
return atof(read(cpuTemp).c_str()) * 0.001f;
}
class CPU {
static inline int powerUpFreq = 0;
static inline const char* gears[] = {
"⦻",
"◌",
"○",
"◎",
"◉",
"●",
"◙"
};
int id;
vector<int> frequencies;
int online = 0;
int currentFrequency = 0;
int busy = 0, idle = 0;
int percent = -1;
int needs = 0;
fstream onlineStream;
string freqFile;
public:
CPU(int id) : id{id} {
auto prefix = "/sys/devices/system/cpu/cpu" + std::to_string(id);
freqFile = prefix + "/cpufreq/scaling_max_freq";
if (id > 0)
frequencies.push_back(0);
{
int freq;
ifstream stream {prefix + "/cpufreq/scaling_available_frequencies"};
stream >> freq; // ignore first entry
while (stream >> freq)
frequencies.push_back(freq);
if (id == 0)
powerUpFreq = freq;
}
onlineStream.open(prefix + "/online", ios::in | ios::out);
onlineStream >> online;
if (online) {
ifstream{freqFile} >> currentFrequency;
}
cout << "cpu " << id << " freq:" << currentFrequency << endl;
}
const char* gear() {
if (!online)
return gears[0];
int g = id == 0;
for (int i = 0; i < frequencies.size(); ++i) {
if (frequencies[i] >= currentFrequency) {
g += i;
break;
}
}
return gears[std::min<int>(g, sizeof(gears)/sizeof(gears[0]) - 1)];
}
int getLoad() const {
return percent;
}
void update(int busy, int idle) {
if (this->busy) {
auto busyDelta = busy - this->busy;
auto idleDelta = idle - this->idle;
percent = (busyDelta * 100) / (busyDelta + idleDelta);
} else {
percent = -1;
}
this->busy = busy;
this->idle = idle;
if (percent == -1) {
needs = 0;
} else if (percent < 20) {
needs = -1;
} else if (percent > 90) {
needs = 1;
} else {
needs = 0;
}
}
static int update(CPU* cpus, int cpuCount) {
stat.seekg(0);
string word;
for (int i = 0; i < cpuCount; ++i) {
cpus[i].percent = -1;
cpus[i].needs = 0;
}
while (stat >> word) {
if (string_view{word}.substr(0, 3) != "cpu") {
if (word[0] >= '0' && word[0] <= '9')
continue;
break;
}
if (word.size() <= 3)
continue;
auto cpuid = atoi(word.c_str() + 3);
if (cpuid >= cpuCount)
continue;
int user, nice, system, idle;
stat >> user >> nice >> system >> idle;
cpus[cpuid].update(user + nice + system, idle);
}
int needs = 0;
for (int i = 0; i < cpuCount; ++i) {
needs += cpus[i].needs;
// cout << cpus[i].needs << "x" << cpus[i].percent << " ";
}
// cout << endl;
return needs;
}
void setFrequency(int freq) {
if (freq == currentFrequency)
return;
// cout << "cpu " << id << " ";
currentFrequency = freq;
bool online = freq > 0;
if (online != this->online) {
this->online = online;
// cout << (online ? " power on" : " shutdown");
onlineStream.seekp(0);
onlineStream << this->online;
onlineStream.flush();
if (online)
currentFrequency = powerUpFreq;
}
if (freq) {
// cout << " freq:" << freq;
ofstream{freqFile} << currentFrequency;
}
// cout << endl;
}
void gearUp(int& c, int profile) {
if (id >= 2 + ((profile & 1) + (profile >> 1)) * 2) {
needs = -1;
}
if (needs < 0) {
c = -1;
return;
}
int cf = currentFrequency;
for (auto freq : frequencies) {
if (freq > currentFrequency) {
cf = freq;
c--;
if (!c)
break;
}
}
if (cf != currentFrequency) {
setFrequency(cf);
c = 0;
}
}
void gearDown(int& c, int profile) {
if (needs > 0 || (id == 0 && profile == 3)) {
c = 0;
return;
}
int cf = currentFrequency;
for (int i = frequencies.size() - 1; i >= 0 && c < -needs; --i) {
auto freq = frequencies[i];
if (freq < cf) {
cf = freq;
++c;
if (!c)
break;
}
}
setFrequency(cf);
}
} cpu[] = {
{0},
{1},
{2},
{3},
{4},
{5},
};
int getCPUContention() {
return CPU::update(cpu, sizeof(cpu)/sizeof(cpu[0]));
}
void run() {
fstream{"/tmp/gears", ios::out};
fstream gearFile{"/tmp/gears", ios::in | ios::out};
int currentProfile = -1;
bool busy = false;
bool hot = false;
int skip = 1;
while (1) {
auto isAC = hasACPower();
auto rate = isAC ? acPollRate : batteryPollRate;
this_thread::sleep_for(rate);
auto cont = getCPUContention();
this_thread::sleep_for(rate);
cont = getCPUContention();
bool isHot = getCPUTemp() > (hot ? 70 : 80);
hot = isHot;
int profile = (int(!isHot) << 1) + int(isAC);
for (int i = 0; i < 6 && cont > 0; ++i)
cpu[i].gearUp(cont, profile);
for (int i = 5; i >= 0 && cont < 0; --i)
cpu[i].gearDown(cont, profile);
if (gearFile && !--skip) {
skip = 4;
gearFile.seekg(0);
gearFile
<< cpu[0].gear()
<< cpu[1].gear()
<< cpu[2].gear()
<< cpu[3].gear()
<< cpu[4].gear()
<< cpu[5].gear()
<< 2 + ((profile & 1) + (profile >> 1)) * 2;
gearFile.flush();
}
}
}
int main() {
if (!stat) {
cout << "Not OK" << endl;
return 1;
}
run();
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment