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

278
main.cpp
View File

@@ -5,56 +5,45 @@
#include <csignal>
#include <cstdint>
#include <cstdlib>
#include <ctime>
#include <fstream>
#include <iomanip>
// #include <iostream>
#include <iostream>
#include <print>
#include <sstream>
#include <stop_token>
#include <string>
#include <thread>
#include "include/LCDDisplay.hpp"
#include "include/gpu.hpp"
#include "include/utils.hpp"
//
#include <LCDDisplay.hpp>
#include <cpu.hpp>
#include <gpu.hpp>
#include <hass.hpp>
#include <sys.hpp>
#include <utils.hpp>
#define LCD_PORT "/dev/serial/by-id/usb-Raspberry_Pi_Pico_E6616407E37AA12F-if00" // tis a symlink, can also be just /dev/ttyACM0
#define LCD_INTERVAL_MS 1000
#define LCD_CONTRAST 215
#define FILL_CHAR ' '
#define N_CPU 16
std::chrono::milliseconds interval(LCD_INTERVAL_MS);
std::stop_source sigint;
struct mem_usage_t {
uint64_t free;
uint64_t used;
uint64_t total;
uint64_t free = 0;
uint64_t used = 0;
uint64_t total = 0;
};
bool daemon_mode = false;
struct sensors_t {
std::array<std::string, 2> td;
std::string loadavg;
mem_usage_t ram;
mem_usage_t home;
gpu_t gpu;
cpu_t cpu;
float hass_state = 0;
};
/* DISPLAY FUNCS */
// converts number to string with given decimal places
std::string pNum(auto num, uint8_t decs = 0) {
std::stringstream ret;
ret << std::fixed << std::setprecision(decs) << num;
return ret.str();
}
// converts large number in (k)bytes to human readable numer with suffix and sane decimal
std::string pBytes(double bytes, bool isKbytes = false) {
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 "++++";
}
// pads given string to length
std::string pad(std::string s, uint8_t len, bool ljust = false) {
@@ -69,30 +58,6 @@ std::string fill(uint8_t n) {
}
/* GETTERS */
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()};
}
mem_usage_t getRam() {
mem_usage_t mem;
@@ -118,127 +83,94 @@ mem_usage_t getDisk(std::string mount) {
return disk;
}
auto getCpuTemp(std::string hw) {
struct {
double tdie;
double tctl;
double tccd1;
double pcore = 0.0;
double psoc = 0.0;
double ptotal = 0.0;
double vcore = 0.0;
double vsoc = 0.0;
uint32_t avg_freq = 0;
} cpu;
// commented out unused values so its faster
if (readFile(hw + "/name") == "zenpower") {
// cpu.tdie = (double)(std::stoul(readFile(hw + "/temp1_input")) / 1000.0);
cpu.tctl = (double)(std::stoul(readFile(hw + "/temp2_input")) / 1000.0);
cpu.tccd1 = (double)(std::stoul(readFile(hw + "/temp3_input")) / 1000.0);
cpu.pcore = (double)(std::stoul(readFile(hw + "/power1_input")) / 1000000.0);
cpu.psoc = (double)(std::stoul(readFile(hw + "/power2_input")) / 1000000.0);
cpu.ptotal = cpu.pcore + cpu.psoc;
cpu.vcore = (double)(std::stoul(readFile(hw + "/in1_input")) / 1000.0);
// cpu.vsoc = (double)(std::stoul(readFile(hw + "/in2_input")) / 1000.0);
} else {
cpu.tctl = (double)(std::stoul(readFile(hw + "/temp1_input")) / 1000.0);
cpu.tccd1 = (double)(std::stoul(readFile(hw + "/temp3_input")) / 1000.0);
}
// lots of reads but still faster than reading /proc/cpuinfo
for (uint8_t i = 0; i < N_CPU; i++)
cpu.avg_freq += (long)(std::stol(readFile("/sys/devices/system/cpu/cpu" + std::to_string(i) + "/cpufreq/cpuinfo_avg_freq", 3)));
cpu.avg_freq /= N_CPU;
return cpu;
void sighandler(int s) {
std::println("[Signal] got SIGINT, exiting...");
sigint.request_stop();
}
void sighandler(int s) {
daemon_mode = false;
std::println("[Signal] deamon_mode = false, exiting on next loop");
void hass_thread(sensors_t *s, std::stop_token st) {
std::println("[Thread] Starting hass_thread, pid {}", gettid());
hass_connect();
while (!st.stop_requested()) {
hass_poll();
s->hass_state = hass_get();
std::this_thread::sleep_for(std::chrono::milliseconds(100));
}
hass_disconnect();
}
void sensors_thread(sensors_t *s, std::stop_token st) {
std::println("[Thread] Starting sensors_thread, pid {}", gettid());
/* find hwmon paths from pci addresses */
cpu_hwmon_t cpu_hwmon = detectCpuHwmon("0000:00:18.3");
std::string gpu_hwmon = detectGpuHwmon("0000:08:00.0");
while (!st.stop_requested()) {
auto start = std::chrono::steady_clock::now();
s->td = getTimeDate();
s->loadavg = getAvgs();
s->ram = getRam();
s->home = getDisk("/home/drath");
s->cpu = getCpu(cpu_hwmon);
s->gpu = getGpu(gpu_hwmon);
// std::print("[Sensors] took {} microseconds \r", std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::steady_clock::now() - start).count());
// std::cout << std::flush;
std::this_thread::sleep_until(start + interval);
}
}
void display_thread(sensors_t *s, std::stop_token st) {
std::println("[Thread] Starting display_thread, pid {}", gettid());
LCDDisplay lcd(LCD_PORT);
lcd.setCustomChar(0, {16, 6, 9, 8, 8, 9, 6, 0}); // celcius
std::println("[Chrono] Running in daemon mode, {} ms interval", LCD_INTERVAL_MS);
lcd.setContrast(LCD_CONTRAST);
lcd.setBacklight(true);
lcd.clear();
while (!st.stop_requested()) {
auto start = std::chrono::steady_clock::now();
std::array<std::string, 4> lines;
auto gpu_temps = (s->gpu.hotspot_temp) ? "/" + std::to_string(s->gpu.hotspot_temp) : "";
gpu_temps += (s->gpu.mem_temp) ? "/" + std::to_string(s->gpu.mem_temp) : "";
lcd.writeBuf(0, 0, pad(s->td[1], 11, true) + " " + s->td[0] + lcd.bar(1, s->gpu.core_usage / 100.0) + pad(s->loadavg, 17));
lcd.writeBuf(1, 0, pad(std::to_string(s->cpu.tctl) + "/" + std::to_string(s->cpu.tccd1) + lcd.cc[0], 8, true));
lcd.writeBuf(1, 11, pad(pBytes(s->ram.used, true) + "B", 7, true));
lcd.writeBuf(1, 22, pad(pBytes(s->gpu.vram_used) + "B", 7));
lcd.writeBuf(1, 29, pad(std::to_string(s->gpu.edge_temp) + gpu_temps + lcd.cc[0], 11));
lcd.writeBuf(2, 0, pad(pNum(s->cpu.ptotal, 1) + "W", 8, true));
lcd.writeBuf(2, 10, pad(pNum(s->hass_state) + "W", 4));
lcd.writeBuf(2, 15, pad("h:" + pBytes(s->home.free), 8));
lcd.writeBuf(2, 25, pad(s->gpu.fan_speed + "R", 5));
std::println("{}", s->gpu.fan_speed);
lcd.writeBuf(2, 31, pad(std::to_string(s->gpu.power_avg) + "/" + std::to_string(s->gpu.power_cap) + "W", 9));
lcd.writeBuf(3, 0, pad(pNum(s->cpu.vcore, 3) + "V", 7, true));
lcd.writeBuf(3, 7, std::to_string(s->cpu.avg_freq) + "MHz");
lcd.writeBuf(3, 21, pad(std::to_string(s->gpu.core_clock) + "/" + std::to_string(s->gpu.mem_clock) + "MHz ", 13));
lcd.writeBuf(3, 34, pad(pNum(s->gpu.vcore, 3) + "V", 5));
lcd.flushBuffer();
std::this_thread::sleep_until(start + interval);
}
}
int main(int argc, char *argv[]) {
signal(SIGINT, sighandler);
daemon_mode = (argc > 1 && std::string(argv[1]) == "-d");
/* init lcd and chars, set contrast and backlight */
LCDDisplay lcd(LCD_PORT);
std::array<std::string, 4> lines;
lcd.setCustomChar(0, {16, 6, 9, 8, 8, 9, 6, 0}); // celcius
sensors_t sensors;
/* find correct cpu module */
auto cpu_hwmon = findHwmonPath("k10temp");
if (cpu_hwmon.size() == 0) {
std::println("[Hwmon] k10temp not found, trying zenpower");
cpu_hwmon = findHwmonPath("zenpower");
}
std::jthread producer(sensors_thread, &sensors, sigint.get_token());
std::jthread hass(hass_thread, &sensors, sigint.get_token());
/* figure out gpu metrics path */
std::string gpu_hwmon;
GpuMetricsHeader_t gpu_mtr_header;
for (uint8_t c = 0; c < 10; c++) {
gpu_hwmon = "/sys/class/drm/card" + std::to_string(c) + "/device";
gpu_mtr_header = getGpuMetricsHeader(gpu_hwmon + "/gpu_metrics");
if (readFile(gpu_hwmon + "/board_info").length() != 0)
break;
}
/* loop setup */
std::chrono::milliseconds interval(LCD_INTERVAL_MS);
auto start = std::chrono::steady_clock::now();
if (daemon_mode) {
std::println("[Chrono] Running in daemon mode, {} ms interval", LCD_INTERVAL_MS);
lcd.setContrast(LCD_CONTRAST);
lcd.setBacklight(true);
}
/* main loop */
while (true) {
auto td = getTimeDate();
auto ram = getRam();
// auto anime = getDisk("/mnt/anime");
auto home = getDisk("/home/drath");
// auto gpu = (gpu_mtr_header.structure_size != 0) ? getGpu(gpu_mtr_header, gpu_hwmon) : getGpu("/sys/class/drm/card1/device");
auto gpu = getGpu(gpu_mtr_header, gpu_hwmon);
auto cpu = getCpuTemp(cpu_hwmon);
lines[0] = pad(td[1], 11, true) + " " + td[0] + lcd.bar(1, gpu.core_usage / 100.0) + pad(getAvgs(), 17);
lines[1] = pad(pNum(cpu.tctl) + "/" + pNum(cpu.tccd1) + lcd.cc[0], 8, true);
lines[1] += pad(pBytes(ram.used, true) + "B", 7, true);
lines[1] += fill(7);
lines[1] += pad(pBytes(gpu.vram_used) + "B", 7);
auto gpu_temps = (gpu.hotspot_temp) ? "/" + std::to_string(gpu.hotspot_temp) : "";
gpu_temps += (gpu.mem_temp) ? "/" + std::to_string(gpu.mem_temp) : "";
lines[1] += pad(std::to_string(gpu.edge_temp) + gpu_temps + lcd.cc[0], 11);
lines[2] = pad(pNum(cpu.ptotal, 1) + "W", 8, true);
lines[2] += pad("home: " + pBytes(home.free), 12);
lines[2] += fill(6);
lines[2] += pad(pNum(gpu.fan_speed) + "R", 5);
lines[2] += pad(std::to_string(gpu.power_avg) + "/" + std::to_string(gpu.power_cap) + "W", 9);
lines[3] = pad(pNum(cpu.vcore, 3) + "V", 7, true);
lines[3] += std::to_string(cpu.avg_freq) + "MHz";
lines[3] += fill(7);
lines[3] += pad(pNum(gpu.core_clock) + "/" + pNum(gpu.mem_clock) + "MHz ", 13);
lines[3] += pad(pNum(gpu.vcore, 3) + "V", 5);
lcd.writeLines(lines);
if (daemon_mode) {
std::this_thread::sleep_until(start + interval);
start += interval;
} else {
std::println("[Chrono] took {}ms", std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::steady_clock::now() - start).count() / 1000.0);
return 0;
}
}
/* should exit in loop, if it doesnt something went wrong */
return 1;
display_thread(&sensors, sigint.get_token());
return 0;
}