yuge overhaul, threads, hass, rewrote display, hwmons, bunc of shit
This commit is contained in:
11
Makefile
11
Makefile
@@ -2,7 +2,7 @@ MAKEFLAGS := --jobs=16
|
||||
# MAKEFLAGS += --output-sync=target
|
||||
|
||||
# compiling with C++26
|
||||
CXXFLAGS := -std=c++2c -Ofast -fdiagnostics-color=always -Iinclude -lwebsockets
|
||||
CXXFLAGS := -std=c++2c -Ofast -fdiagnostics-color=always -Iinclude -lwebsockets -D_GLIBCXX_USE_CXX11_ABI=0
|
||||
CXXFLAGS += -MMD -MP
|
||||
|
||||
SOURCES := main.cpp $(wildcard src/*.cpp)
|
||||
@@ -36,12 +36,3 @@ install_service:
|
||||
systemctl --user daemon-reload
|
||||
systemctl --user enable linuxSmartie
|
||||
systemctl --user start linuxSmartie
|
||||
|
||||
|
||||
# build:
|
||||
# g++ -c $(BUILD_OPTS) -o LCDDisplay.o -Iinclude include/LCDDisplay.cpp
|
||||
# g++ -c $(BUILD_OPTS) -o gpu.o -Iinclude include/gpu.cpp
|
||||
# g++ -c $(BUILD_OPTS) -o utils.o -Iinclude include/utils.cpp
|
||||
# g++ -c $(BUILD_OPTS) -o main.o main.cpp
|
||||
|
||||
# g++ $(BUILD_OPTS) -o linuxSmartie *.o # linking
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
#ifndef LCDDISPLAY_HPP
|
||||
#define LCDDISPLAY_HPP
|
||||
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
#include <sstream>
|
||||
@@ -6,10 +9,12 @@
|
||||
class LCDDisplay {
|
||||
private:
|
||||
uint8_t _lcd;
|
||||
void writeStr(std::string str);
|
||||
char linebuf[4][41]{};
|
||||
void writeStr(std::string str); // do not use
|
||||
void writeLines(std::array<std::string, 4> lines); // do not use
|
||||
|
||||
public:
|
||||
LCDDisplay(std::string dev, uint32_t baud = 0010002); // from termios-baud.h, B115200
|
||||
LCDDisplay(std::string dev, uint32_t baud = 0010002); // from termios-baud.h, B115200
|
||||
~LCDDisplay();
|
||||
|
||||
// custom char array
|
||||
@@ -19,8 +24,11 @@ class LCDDisplay {
|
||||
void setContrast(uint8_t contrast);
|
||||
void setBacklight(bool enabled);
|
||||
void setCursor(uint8_t row, uint8_t col);
|
||||
void writeLines(std::array<std::string, 4> lines);
|
||||
void writeBuf(uint8_t row, uint8_t col, std::string str);
|
||||
void flushBuffer();
|
||||
void writeSStream(std::stringstream& ss);
|
||||
void setCustomChar(uint8_t index, std::array<uint8_t, 8> cchar);
|
||||
char bar(uint8_t char_id, double value);
|
||||
};
|
||||
|
||||
#endif
|
||||
33
include/cpu.hpp
Normal file
33
include/cpu.hpp
Normal file
@@ -0,0 +1,33 @@
|
||||
#ifndef CPU_HPP
|
||||
#define CPU_HPP
|
||||
|
||||
#include <cstdint>
|
||||
#include <filesystem>
|
||||
#include <print>
|
||||
#include <string>
|
||||
|
||||
#include <utils.hpp>
|
||||
|
||||
#define N_CPU 16
|
||||
|
||||
struct cpu_hwmon_t {
|
||||
std::string path = "";
|
||||
bool zenpower = false;
|
||||
};
|
||||
|
||||
struct cpu_t {
|
||||
int tdie = 0;
|
||||
int tctl = 0;
|
||||
int tccd1 = 0;
|
||||
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_t getCpu(cpu_hwmon_t hw);
|
||||
cpu_hwmon_t detectCpuHwmon(std::string pci);
|
||||
|
||||
#endif
|
||||
@@ -1,25 +1,32 @@
|
||||
#ifndef GPU_HPP
|
||||
#define GPU_HPP
|
||||
|
||||
#include <cstdint>
|
||||
#include <fstream>
|
||||
#include <print>
|
||||
#include <string>
|
||||
#include <utils.hpp>
|
||||
|
||||
struct gpu_t {
|
||||
uint16_t fan_speed;
|
||||
uint16_t core_clock;
|
||||
uint16_t mem_clock;
|
||||
double vcore;
|
||||
uint16_t power_avg;
|
||||
uint16_t power_cap;
|
||||
uint16_t power_max;
|
||||
uint8_t fan_pwm;
|
||||
uint8_t edge_temp;
|
||||
uint8_t hotspot_temp;
|
||||
uint8_t mem_temp;
|
||||
uint8_t gfx_vrm_temp;
|
||||
uint8_t soc_vrm_temp;
|
||||
uint8_t mem_vrm_temp;
|
||||
uint64_t vram_used;
|
||||
uint64_t vram_total;
|
||||
uint8_t core_usage;
|
||||
uint8_t media_usage;
|
||||
uint8_t umc_usage;
|
||||
uint16_t fan_speed = 0;
|
||||
uint16_t core_clock = 0;
|
||||
uint16_t mem_clock = 0;
|
||||
double vcore = 0;
|
||||
uint16_t power_avg = 0;
|
||||
uint16_t power_cap = 0;
|
||||
uint16_t power_max = 0;
|
||||
uint8_t fan_pwm = 0;
|
||||
uint8_t edge_temp = 0;
|
||||
uint8_t hotspot_temp = 0;
|
||||
uint8_t mem_temp = 0;
|
||||
uint8_t gfx_vrm_temp = 0;
|
||||
uint8_t soc_vrm_temp = 0;
|
||||
uint8_t mem_vrm_temp = 0;
|
||||
uint64_t vram_used = 0;
|
||||
uint64_t vram_total = 0;
|
||||
uint8_t core_usage = 0;
|
||||
uint8_t media_usage = 0;
|
||||
uint8_t umc_usage = 0;
|
||||
};
|
||||
|
||||
struct GpuMetricsHeader_t {
|
||||
@@ -28,6 +35,9 @@ struct GpuMetricsHeader_t {
|
||||
uint8_t content_revision = 0;
|
||||
};
|
||||
|
||||
std::string detectGpuHwmon(std::string pci);
|
||||
gpu_t getGpu(GpuMetricsHeader_t header, std::string hw);
|
||||
gpu_t getGpu(std::string hw);
|
||||
GpuMetricsHeader_t getGpuMetricsHeader(std::string path);
|
||||
GpuMetricsHeader_t getGpuMetricsHeader(std::string path);
|
||||
|
||||
#endif
|
||||
12
include/sys.hpp
Normal file
12
include/sys.hpp
Normal file
@@ -0,0 +1,12 @@
|
||||
#ifndef SYS_HPP
|
||||
#define SYS_HPP
|
||||
|
||||
#include <ctime>
|
||||
#include <array>
|
||||
#include <string>
|
||||
#include <utils.hpp>
|
||||
|
||||
std::string getAvgs();
|
||||
std::array<std::string, 2> getTimeDate();
|
||||
|
||||
#endif
|
||||
@@ -1,6 +1,27 @@
|
||||
#ifndef UTILS_HPP
|
||||
#define UTILS_HPP
|
||||
|
||||
#include <dirent.h>
|
||||
#include <fcntl.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstdlib>
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include <iomanip>
|
||||
#include <print>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
|
||||
std::string readFile(std::string path, uint8_t trim = 0);
|
||||
std::string pNum(double num, uint8_t decs = 0);
|
||||
std::string pNum(float num, uint8_t decs = 0);
|
||||
std::string pBytes(double bytes, bool isKbytes = false);
|
||||
std::string readFile(std::string path, size_t trim = 4096);
|
||||
ssize_t readFile2(std::string path, char *buffer, size_t maxlen);
|
||||
ssize_t readFile2(const char *path, char *buffer, size_t maxlen);
|
||||
int get_val(const std::string &target, const std::string &content);
|
||||
std::string findHwmonPath(std::string module);
|
||||
std::string getFirstDir(std::string path);
|
||||
|
||||
#endif
|
||||
278
main.cpp
278
main.cpp
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
45
src/cpu.cpp
Normal 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;
|
||||
}
|
||||
58
src/gpu.cpp
58
src/gpu.cpp
@@ -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
26
src/sys.cpp
Normal 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()};
|
||||
}
|
||||
@@ -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 "";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user