yuge overhaul, threads, hass, rewrote display, hwmons, bunc of shit

This commit is contained in:
Drath
2025-09-11 17:56:40 +02:00
parent 3153b1dc1b
commit 7b4791395e
12 changed files with 411 additions and 255 deletions

View File

@@ -4,6 +4,8 @@
#include <termios.h>
#include <unistd.h>
#include <cstring>
#include <iostream>
#include <print>
#include <string>
@@ -27,6 +29,7 @@ LCDDisplay::LCDDisplay(std::string dev, uint32_t baud) {
tcflush(lcd, TCIFLUSH);
tcsetattr(lcd, TCSANOW, &term);
_lcd = lcd;
std::memset(linebuf, ' ', sizeof(linebuf)); // init linebuf to spaces
}
LCDDisplay::~LCDDisplay() {
@@ -46,7 +49,7 @@ void LCDDisplay::setContrast(uint8_t contrast) {
}
void LCDDisplay::setBacklight(bool enabled) {
std::println("[LCDDisplay] Set brightness to {}", enabled);
std::println("[LCDDisplay] Set backlight to {}", enabled);
uint8_t cmdbuf[] = {254, (uint8_t)(enabled ? 66 : 70), 255};
write(_lcd, &cmdbuf, enabled ? 3 : 2);
}
@@ -75,6 +78,29 @@ void LCDDisplay::writeLines(std::array<std::string, 4> lines) {
writeStr(lines[2] + lines[3]);
}
/* writes string to line buffer at given pos*/
void LCDDisplay::writeBuf(uint8_t row, uint8_t col, std::string str) {
// size_t max_copy = std::min(str.size(), sizeof(linebuf[row]) - col - 1);
std::memcpy(&linebuf[row][col], str.c_str(), str.size()); // will overflow
}
/* hyper optimized buffer flush command, writes whole disp with a single write */
void LCDDisplay::flushBuffer() {
char writebuf[168]; // 4x40 chars + 2x4 setcursor commands + 1 null terminator
uint8_t setDisp[] = {254, 71, 1}; // skeleton set cursor command
std::memcpy(&writebuf[0], setDisp, 3); // copy template set cursor command
writebuf[3] = 1; // actual set cursor param, set to 1st row
std::memcpy(&writebuf[4], linebuf[0], 40); // copy 1st line
std::memcpy(&writebuf[44], linebuf[1], 40); // copy 2nd line
std::memcpy(&writebuf[84], setDisp, 3); // copy template set cursor command
writebuf[87] = 3; // set cursor to 3rd line
std::memcpy(&writebuf[88], linebuf[2], 40); // copy 3rd line
std::memcpy(&writebuf[128], linebuf[3], 40); // copy 4th line
write(_lcd, writebuf, 168);
}
void LCDDisplay::writeSStream(std::stringstream& ss) {
std::string s = ss.str();
if (s.size() != 160)

45
src/cpu.cpp Normal file
View File

@@ -0,0 +1,45 @@
#include <cpu.hpp>
cpu_hwmon_t detectCpuHwmon(std::string pci) {
cpu_hwmon_t hw;
std::string path = "/sys/bus/pci/devices/" + pci + "/hwmon";
try {
for (const auto& entry : std::filesystem::directory_iterator(path))
hw.path = getFirstDir(path);
} catch (...) {
std::println("[CpuHwmon] NO CPU HWMON KERNEL MODULE DETECTED");
return hw;
}
hw.zenpower = readFile((hw.path + "/name"), 128) == "zenpower";
std::println("[CpuHwmon] Detected module \"{}\" in path \"{}\"", hw.zenpower ? "zenpower" : "k10temp", hw.path);
return hw;
}
cpu_t getCpu(cpu_hwmon_t hw) {
cpu_t cpu;
// commented out unused values so its faster
if (hw.path == "") return cpu; // break if no module
if (hw.zenpower) {
// cpu.tdie = std::stoi(readFile(hw.path + "/temp1_input", 2));
cpu.tctl = std::stoi(readFile(hw.path + "/temp2_input", 2));
cpu.tccd1 = std::stoi(readFile(hw.path + "/temp3_input", 2));
cpu.pcore = (double)(std::stoi(readFile(hw.path + "/power1_input", 3)) / 100.0);
cpu.psoc = (double)(std::stoi(readFile(hw.path + "/power2_input", 3)) / 100.0);
cpu.ptotal = cpu.pcore + cpu.psoc;
cpu.vcore = (double)(std::stoul(readFile(hw.path + "/in1_input", 4)) / 1000.0);
// cpu.vsoc = (double)(std::stoul(readFile(hw.path + "/in2_input")) / 1000.0);
} else {
cpu.tctl = std::stoi(readFile(hw.path + "/temp1_input", 2));
cpu.tccd1 = std::stoi(readFile(hw.path + "/temp3_input", 2));
}
// lots of reads but still faster than reading /proc/cpuinfo
char buf[5];
for (uint8_t i = 0; i < N_CPU; i++) {
readFile2("/sys/devices/system/cpu/cpu1/cpufreq/scaling_cur_freq", buf, sizeof(buf));
cpu.avg_freq += strtol(buf, NULL, 10);
}
cpu.avg_freq /= N_CPU;
return cpu;
}

View File

@@ -1,9 +1,4 @@
#include <cstdint>
#include <fstream>
#include <gpu.hpp>
#include <print>
#include <string>
#include <utils.hpp>
struct GpuMetrics_v1_1_t {
uint16_t temperature_edge;
@@ -84,6 +79,12 @@ struct GpuMetrics_v1_3_t {
uint8_t padding1;
};
std::string detectGpuHwmon(std::string pci) {
std::string path = getFirstDir("/sys/bus/pci/devices/" + pci + "/hwmon");
std::println("[GpuHwmon] Detected path \"{}\"", path);
return path;
}
GpuMetricsHeader_t getGpuMetricsHeader(std::string path) {
GpuMetricsHeader_t header;
std::ifstream f(path, std::ios::binary);
@@ -111,7 +112,7 @@ auto getGpuMetrics(GpuMetricsHeader_t h, std::string path) {
f.read(reinterpret_cast<char *>(&metrics), sizeof(metrics));
f.close();
}
} break;
} break;
}
return metrics;
@@ -123,10 +124,10 @@ gpu_t getGpu(GpuMetricsHeader_t header, std::string hw) {
uint8_t hwmon = 3;
// these are always read from fs
gpu.fan_speed = std::stol(readFile(hw + "/hwmon/hwmon" + std::to_string(hwmon)+ "/fan1_input"));
gpu.vcore = std::stod(readFile(hw + "/hwmon/hwmon" + std::to_string(hwmon)+ "/in0_input")) / 1000.0;
gpu.power_cap = std::stol(readFile(hw + "/hwmon/hwmon" + std::to_string(hwmon)+ "/power1_cap", 6));
gpu.power_max = std::stol(readFile(hw + "/hwmon/hwmon" + std::to_string(hwmon)+ "/power1_cap_max", 6));
gpu.fan_speed = std::stol(readFile(hw + "/hwmon/hwmon" + std::to_string(hwmon) + "/fan1_input"));
gpu.vcore = std::stol(readFile(hw + "/hwmon/hwmon" + std::to_string(hwmon) + "/in0_input")) / 1000.0;
gpu.power_cap = std::stol(readFile(hw + "/hwmon/hwmon" + std::to_string(hwmon) + "/power1_cap", 3));
gpu.power_max = std::stol(readFile(hw + "/hwmon/hwmon" + std::to_string(hwmon) + "/power1_cap_max", 3));
// gpu.fan_pwm = std::stoi(readFile(hw + "/hwmon/hwmon" + std::to_string(hwmon)+ "/pwm1")); // not super useful
gpu.vram_used = std::stoul(readFile(hw + "/mem_info_vram_used"));
gpu.vram_total = std::stoul(readFile(hw + "/mem_info_vram_total"));
@@ -148,16 +149,41 @@ gpu_t getGpu(GpuMetricsHeader_t header, std::string hw) {
gpu.mem_vrm_temp = metrics.temperature_vrmem;
gpu.power_avg = metrics.average_socket_power;
} else {
gpu.core_clock = std::stol(readFile(hw + "/hwmon/hwmon" + std::to_string(hwmon)+ "/freq1_input", 6));
gpu.mem_clock = std::stol(readFile(hw + "/hwmon/hwmon" + std::to_string(hwmon)+ "/freq2_input", 6));
gpu.vcore = std::stod(readFile(hw + "/hwmon/hwmon" + std::to_string(hwmon)+ "/in0_input")) / 1000.0;
gpu.power_avg = std::stol(readFile(hw + "/hwmon/hwmon" + std::to_string(hwmon)+ "/power1_input", 6));
gpu.edge_temp = std::stol(readFile(hw + "/hwmon/hwmon" + std::to_string(hwmon)+ "/temp1_input", 3));
gpu.core_clock = std::stoll(readFile(hw + "/hwmon/hwmon" + std::to_string(hwmon) + "/freq1_input", 16)) / 1000000;
gpu.mem_clock = std::stoll(readFile(hw + "/hwmon/hwmon" + std::to_string(hwmon) + "/freq2_input", 16)) / 1000000;
gpu.vcore = std::stod(readFile(hw + "/hwmon/hwmon" + std::to_string(hwmon) + "/in0_input", 8)) / 1000.0;
gpu.power_avg = std::stol(readFile(hw + "/hwmon/hwmon" + std::to_string(hwmon) + "/power1_input", 3));
gpu.edge_temp = std::stol(readFile(hw + "/hwmon/hwmon" + std::to_string(hwmon) + "/temp1_input", 2));
gpu.core_usage = std::stoi(readFile(hw + "/gpu_busy_percent"));
}
if(gpu.fan_speed > 9999)
if (gpu.fan_speed > 9999)
gpu.fan_speed = 9999;
return gpu;
}
gpu_t getGpu(std::string hwmon) {
gpu_t gpu;
// these are always read from fs
gpu.fan_speed = std::stol(readFile(hwmon + "/fan1_input", 4));
gpu.vcore = std::stol(readFile(hwmon + "/in0_input", 4)) / 1000.0;
gpu.power_cap = std::stol(readFile(hwmon + "/power1_cap", 16)) / 1000000;
// gpu.power_max = std::stol(readFile(hwmon + "/power1_cap_max", 16)) / 1000000;
// gpu.fan_pwm = std::stoi(readFile(hw + "/hwmon/hwmon" + std::to_string(hwmon)+ "/pwm1")); // not super useful
gpu.vram_used = std::stoul(readFile(hwmon + "/../../mem_info_vram_used", 16));
// gpu.vram_total = std::stoul(readFile(hwmon + "/../../mem_info_vram_total", 16));
// these also exist in gpu_metrics?
gpu.core_clock = std::stoll(readFile(hwmon + "/freq1_input", 16)) / 1000000;
gpu.mem_clock = std::stoll(readFile(hwmon + "/freq2_input", 16)) / 1000000;
gpu.power_avg = std::stol(readFile(hwmon + "/power1_input", 16)) / 1000000;
gpu.edge_temp = std::stol(readFile(hwmon + "/temp1_input", 2));
gpu.core_usage = std::stoi(readFile(hwmon + "/../../gpu_busy_percent"));
if (gpu.fan_speed > 9999)
gpu.fan_speed = 9999;
return gpu;
}

26
src/sys.cpp Normal file
View File

@@ -0,0 +1,26 @@
#include <sys.hpp>
std::string getAvgs() {
std::string ret;
double avgs[3];
getloadavg(avgs, 3);
for (uint8_t i = 0; i < 3; i++)
ret += pNum(avgs[i], avgs[i] > 10 ? 1 : 2) + " ";
ret.pop_back(); // remove trailing space
return ret;
}
std::array<std::string, 2> getTimeDate() {
std::array months = {"sty", "lut", "mar", "kwi", "maj", "cze", "lip", "sie", "wrz", "paz", "lis", "gru"};
std::array wdays = {"Nie", "Pon", "Wto", "Sro", "Czw", "Pia", "Sob"};
// std::array wdays = {"Ni", "Pn", "Wt", "Sr", "Cz", "Pi", "So"};
auto now = std::time(nullptr);
auto tm = *std::localtime(&now);
std::stringstream time, date;
time << std::setfill('0');
time << std::setw(2) << tm.tm_hour << ":";
time << std::setw(2) << tm.tm_min << ":";
time << std::setw(2) << tm.tm_sec;
date << wdays[tm.tm_wday] << ", " << tm.tm_mday << " " << months[tm.tm_mon];
return {time.str(), date.str()};
}

View File

@@ -1,27 +1,58 @@
#include <cstdint>
#include <fstream>
#include <print>
#include <sstream>
#include <string>
#include <utils.hpp>
/* SENSOR GETTERS + UTILS*/
std::string readFile(std::string path, uint8_t trim) {
std::string ret;
std::stringstream ss;
std::ifstream fs(path);
if (!fs.good()) {
std::println("[readFile] Error reading {}", path);
return "";
}
ss << fs.rdbuf();
fs.close();
ret = ss.str();
if (ret.size() < trim) return ret;
return ret.substr(0, ret.size() - trim - 1);
// converts number to string with given decimal places
std::string pNum(double num, uint8_t decs) {
std::stringstream ret;
ret << std::fixed << std::setprecision(decs) << num;
return ret.str();
}
int get_val(const std::string &target, const std::string &content) {
std::string pNum(float num, uint8_t decs) {
return pNum((double)num, decs);
}
// converts large number in (k)bytes to human readable numer with suffix and sane decimal
std::string pBytes(double bytes, bool isKbytes) {
std::array suffix_list = {'K', 'M', 'G', 'T', 'P', 'E', 'Z'};
for (auto& suffix : suffix_list) {
if (suffix == 'K' && isKbytes) continue; // skip first loop if already in Kbytes
bytes /= 1024.0;
if (bytes < 1024) {
uint8_t decs = 2;
if (bytes > 10) decs = 1;
if (bytes > 100) decs = 0;
return pNum(bytes, decs) + suffix;
}
}
return "++++";
}
/* SENSOR GETTERS + UTILS*/
ssize_t readFile2(const char* path, char* buffer, size_t maxlen) {
int fd = open(path, O_RDONLY | O_CLOEXEC);
if (fd < 0) {
std::println("[readFile] Error reading {}", path);
return 0;
}
ssize_t len = read(fd, buffer, maxlen - 1);
if (len >= 0) buffer[len] = '\0';
close(fd);
return len;
}
ssize_t readFile2(std::string path, char* buffer, size_t maxlen) {
return readFile2(path.c_str(), buffer, maxlen);
}
std::string readFile(std::string path, size_t trim) {
char buf[trim + 2];
ssize_t len = readFile2(path, buf, trim + 2);
std::string out(buf);
return out.substr(0, len - 1);
}
int get_val(const std::string& target, const std::string& content) {
int result = -1;
std::size_t start = content.find(target);
if (start != std::string::npos) {
@@ -33,14 +64,9 @@ int get_val(const std::string &target, const std::string &content) {
return result;
}
std::string findHwmonPath(std::string module) {
for (uint8_t id = 0; id < 10; id++) {
auto f = readFile("/sys/class/hwmon/hwmon" + std::to_string(id) + "/name");
if (f.size() == 0) break;
if (f == module) {
std::println("[Hwmon] Detected {} path is /sys/class/hwmon/hwmon{}", module, id);
return "/sys/class/hwmon/hwmon" + std::to_string(id);
}
}
std::string getFirstDir(std::string path) {
for (const auto& entry : std::filesystem::directory_iterator(path))
return entry.path();
return "";
}
}