we're getttng C to work

C deez nuts
This commit is contained in:
2026-05-02 19:22:29 +02:00
parent 5f7d4136cd
commit 96e14ed6ea
27 changed files with 1287 additions and 2187 deletions

View File

@@ -1,48 +0,0 @@
#ifndef LCDDISPLAY_HPP
#define LCDDISPLAY_HPP
#define LCD_LINES 4
#define LCD_COLS 40
#include <array>
#include <cstdint>
#include <string>
class LCDDisplay {
private:
int lcd = -1;
uint32_t baud = 0;
std::string dev;
std::array<char, LCD_LINES * LCD_COLS + 8> writebuf{}; // 4x40 chars + 2x4 setcursor commands
static constexpr bool dualDisp = (LCD_LINES * LCD_COLS > 80); // is it dual display device or not, evauluated from the size at compile time
int open(bool reopen = false);
void write(const uint8_t* cmdbuf, uint8_t len);
void close();
// we avoid using these, use buffer (even if these are faster for small writes, perhaps a rewrite is in order)
void writeStr(const std::string& str);
void writeStr(uint8_t row, uint8_t col, const std::string& str);
public:
explicit LCDDisplay(const std::string& dev, uint32_t baud = 0010002); // from termios-baud.h, B115200
~LCDDisplay();
int Init();
void clear();
void setContrast(uint8_t contrast);
void setBacklight(bool enabled);
void setBacklight(uint8_t brightness);
void setCursor(uint8_t row, uint8_t col);
void setCustomChar(uint8_t index, std::array<uint8_t, 8> cchar);
void writeBuf(uint8_t row, uint8_t col, const std::string& str);
void clearBuf();
void flushBuf();
char bar(uint8_t char_id, double value);
// returns custom char no. c, small enough to define in header
static constexpr char cc(const uint8_t c) { return static_cast<char>(c % 8); }
};
#endif

View File

@@ -1,71 +0,0 @@
#ifndef APP_HPP
#define APP_HPP
#include <atomic>
#include <cpu.hpp>
#include <gpu.hpp>
#include <mutex>
#include <semaphore>
#include <stop_token>
#include <sys.hpp>
#include <thread>
class Application {
public:
struct Config {
struct Lcd {
uint16_t refresh_interval_ms;
uint8_t contrast;
std::string port;
};
struct HomeAssistant {
std::string url;
std::string entity;
std::string token;
};
Lcd lcd;
HomeAssistant hass;
std::string cpu_pciaddr;
std::string gpu_pciaddr;
std::string sys_sensor;
std::string sys_ifname;
std::string home_dir;
};
explicit Application(Config config);
~Application();
int Run();
private:
struct Sensors {
sys::TimeDate td;
double loadavg[3] = {0.0f};
sys::Memory ram;
sys::Disk home;
sys::Network net;
sys::sysSensors sys;
cpu::cpuSensors cpu;
gpu::gpuSensors gpu;
double hass_state = 0.0f;
};
struct Control {
std::binary_semaphore display{0};
std::mutex sensors; // making sure the sensors struct isnt accessed by multiple threads at once
sigset_t sigset;
};
pid_t m_mainPid{};
int m_ret = 0;
Config m_config{}; // m_ is prefix for member variable
Control m_control{};
Sensors m_sensors{};
void hassThread(const std::stop_token& st);
void sensorsThread(const std::stop_token& st);
void displayThread(const std::stop_token& st);
void Crash(int ret);
};
#endif

View File

@@ -1,33 +0,0 @@
#ifndef CPU_HPP
#define CPU_HPP
#include <cstdint>
#include <string>
namespace cpu {
enum Module : uint8_t { unknown,
k10temp,
zenpower };
struct cpuSensors {
Module module = unknown;
uint8_t num_cores = 0;
int16_t tdie = 0;
int16_t tctl = 0;
int16_t tccd1 = 0;
// double pcore = 0.0;
// double psoc = 0.0;
float ptotal = 0.0;
// double vcore = 0.0;
// double vsoc = 0.0;
uint32_t avg_freq = 0;
std::string hwmon;
};
int detect(cpuSensors* target, const std::string& pci);
void update(cpuSensors* c);
} // namespace cpu
#endif

View File

@@ -1,85 +0,0 @@
#ifndef GPU_HPP
#define GPU_HPP
#include <cstdint>
#include <string>
/* clean abstraction for gpu_metrics or metrics in filesystem in sane units */
namespace gpu {
struct gpuSensors {
uint64_t vram_used = 0;
// uint64_t vram_total = 0;
float vcore = 0.0f;
uint16_t clock_core = 0;
uint16_t clock_mem = 0;
// uint8_t fan_pwm = 0;
uint16_t fan_speed = 0;
uint16_t power_avg = 0;
uint16_t power_cap = 0;
// uint16_t power_cap_max = 0;
uint8_t temp_edge = 0;
uint8_t temp_hotspot = 0;
uint8_t temp_mem = 0;
uint8_t temp_vrm_gfx = 0;
uint8_t temp_vrm_soc = 0;
uint8_t temp_vrm_mem = 0;
bool hasGpuMetrics = false;
uint8_t usage_core = 0;
uint8_t usage_media = 0;
uint8_t usage_umc = 0;
std::string hwmon;
};
int detect(gpuSensors* target, const std::string& pci);
void update(gpuSensors* g);
} // namespace gpu
// class ThrottleStatus(IntFlag):
// # linux/drivers/gpu/drm/amd/pm/inc/amdgpu_smu.h
// PPT0 = 1 << 0
// PPT1 = 1 << 1
// PPT2 = 1 << 2
// PPT3 = 1 << 3
// SPL = 1 << 4
// FPPT = 1 << 5
// SPPT = 1 << 6
// SPPT_APU = 1 << 7
// TDC_GFX = 1 << 16
// TDC_SOC = 1 << 17
// TDC_MEM = 1 << 18
// TDC_VDD = 1 << 19
// TDC_CVIP = 1 << 20
// EDC_CPU = 1 << 21
// EDC_GFX = 1 << 22
// APCC = 1 << 23
// TEMP_GPU = 1 << 32
// TEMP_CORE = 1 << 33
// TEMP_MEM = 1 << 34
// TEMP_EDGE = 1 << 35
// TEMP_HOTSPOT = 1 << 36
// TEMP_SOC = 1 << 37
// TEMP_VR_GFX = 1 << 38
// TEMP_VR_SOC = 1 << 39
// TEMP_VR_MEM0 = 1 << 40
// TEMP_VR_MEM1 = 1 << 41
// TEMP_LIQUID0 = 1 << 42
// TEMP_LIQUID1 = 1 << 43
// VRHOT0 = 1 << 44
// VRHOT1 = 1 << 45
// PROCHOT_CPU = 1 << 46
// PROCHOT_GFX = 1 << 47
// PPM = 1 << 56
// FIT = 1 << 57
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -1,37 +0,0 @@
#ifndef HASS_HPP
#define HASS_HPP
#ifdef ENABLE_HOME_ASSISTANT
#include <libwebsockets.h>
#include <cstdint>
#include <string>
class HomeAssistant {
public:
struct Config {
std::string address;
std::string entity;
std::string token;
};
explicit HomeAssistant(const Config& config);
~HomeAssistant();
float state = 0.0f;
int connect();
void disconnect() const;
void service(int timeout_ms = 0) const;
private:
uint16_t id = 2;
std::string tx;
lws_context* context = nullptr;
lws* websock = nullptr;
void event_handler(lws* wsi, lws_callback_reasons reason, char* in);
static int callback(lws* wsi, lws_callback_reasons reason, void* user, void* in, size_t len);
Config m_config;
};
#endif
#endif

35
include/lcd.h Normal file
View File

@@ -0,0 +1,35 @@
#ifndef LCDDISPLAY_HPP
#define LCDDISPLAY_HPP
#define LCD_LINES 4
#define LCD_COLS 40
#include <stdint.h>
struct lcd_t {
int fd;
uint32_t baud;
char writebuf[LCD_LINES * LCD_COLS + 8];
char port[128];
};
typedef uint8_t lcd_glyph_t[8];
void lcd_close(struct lcd_t* lcd);
void lcd_clear(struct lcd_t* lcd);
void lcd_set_contrast(struct lcd_t* lcd, uint8_t contrast);
void lcd_set_backlight(struct lcd_t* lcd, bool enabled);
void lcd_set_cursor(struct lcd_t* lcd, uint8_t row, uint8_t col);
void lcd_set_custom_char(struct lcd_t* lcd, uint8_t index, const lcd_glyph_t* glyph);
void lcd_write_str(struct lcd_t* lcd, const char* str, uint8_t len);
void lcd_write_str_pos(struct lcd_t* lcd, const char* str, uint8_t len, uint8_t posX, uint8_t posY);
void lcd_write_buf(struct lcd_t* lcd, uint8_t row, uint8_t col, const char* str);
void lcd_clear_buf(struct lcd_t* lcd);
void lcd_flush_buf(struct lcd_t* lcd);
char lcd_bar(struct lcd_t* lcd, uint8_t char_id, double value);
int lcd_init(struct lcd_t* lcd);
inline char cc(const uint8_t c) { return (char)(c % 8); }
#endif

41
include/log.h Normal file
View File

@@ -0,0 +1,41 @@
#ifndef LOG_H
#define LOG_H
/* ── kill switch ────────────────────────────────────────────────────────── */
/* comment out to disable all logging */
#define APPLOG_ENABLED
/* comment out to disable file logging */
/* #define LOG_FILE_LOCATION "linuxSmartie.log.gz" */
/* ─────────────────────────────────────────────────────────────────────── */
#ifdef APPLOG_ENABLED
#include <stdarg.h>
typedef enum {
LOG_INF,
LOG_WRN,
LOG_ERR,
LOG_DBG,
LOG_START,
} LogLevel;
/* __PRETTY_FUNCTION__ is a GCC/Clang extension that gives the full
s ignature: "voi*d foo(int x)" — same thing source_location gave you.
Falls back to plain __func__ (C99) if somehow unavailable. */
#define applog(level, fmt, ...) \
log_write(level, __PRETTY_FUNCTION__, fmt, ##__VA_ARGS__)
/* format attribute gives compile-time printf format checking —
better than wha*t std::format gave you at runtime */
void log_write(LogLevel level, const char *func, const char *fmt, ...)
__attribute__((format(printf, 3, 4)));
#else /* APPLOG_ENABLED not defined */
#define applog(...) ((void)0)
#endif /* APPLOG_ENABLED */
#endif /* LOG_H */

View File

@@ -1,96 +0,0 @@
#ifndef LOG_HPP
#define LOG_HPP
// comment out to disable logging in general
#define applog(level, fmt, ...) utils::log(level, std::source_location::current(), fmt, ##__VA_ARGS__)
// comment out to disable logging to file
// #define LOG_FILE_LOCATION "linuxSmartie.log.gz"
#ifdef applog
#include <chrono>
#include <format>
#include <iostream>
#include <mutex>
#include <source_location>
#include <string_view>
#ifdef LOG_FILE_LOCATION
#include <zlib.h>
inline std::mutex fmtx; // file logging mutex
#endif
enum class LOG { INF, WRN, ERR, DBG, START };
namespace utils {
inline std::mutex cmtx; // console logging mutex
constexpr std::string pFname(std::string_view full) {
if (const auto pos = full.find(' '); pos != std::string_view::npos)
full.remove_prefix(pos + 1);
if (const auto pos = full.rfind('('); pos != std::string_view::npos)
full.remove_suffix(full.size() - pos);
return std::string(full);
}
template <typename... Args>
void log(const LOG level, const std::source_location& loc, std::format_string<Args...> str, Args&&... args) {
constexpr std::string_view reset = "\033[0m";
constexpr std::string_view pattern = "[{}{}{}] ";
std::string_view color;
switch (level) {
case LOG::INF:
color = "\033[1;32m";
break; // bold; green
case LOG::WRN:
color = "\033[1;33m"; // bold; yellow
break;
case LOG::ERR:
color = "\033[1;31m"; // bold; red
break;
case LOG::DBG:
break;
case LOG::START:
color = "\033[1;3;36m"; // bold; blinking; cyan
break;
}
/* log to console */
{
std::scoped_lock lock(cmtx);
std::cout << std::format(pattern, color, pFname(loc.function_name()), reset);
std::cout << std::format(str, std::forward<Args>(args)...) << std::endl;
}
#ifdef LOG_FILE_LOCATION
/* log to file */
{
auto now = std::chrono::system_clock::now();
auto local = std::chrono::current_zone()->to_local(now);
std::string msg = std::format("{:%x %H:%M:%S }", floor<std::chrono::milliseconds>(local)); // timestamp
std::format_to(std::back_inserter(msg), pattern, color, pFname(loc.function_name()), reset); // tag
std::format_to(std::back_inserter(msg), str, std::forward<Args>(args)...); // actual log message
std::scoped_lock lock(fmtx);
gzFile file = gzopen(LOG_FILE_LOCATION, "ab");
if (file) {
gzprintf(file, "%s\n", msg.c_str());
gzclose(file);
}
}
#endif
}
} // namespace utils
#else
#define applog(...)
#endif
#endif

View File

@@ -1,49 +0,0 @@
#ifndef SYS_HPP
#define SYS_HPP
#include <cstdint>
#include <string>
namespace sys {
constexpr char months[][4] = {"sty", "lut", "mar", "kwi", "maj", "cze", "lip", "sie", "wrz", "paz", "lis", "gru"};
constexpr char wdays[][4] = {"Nie", "Pon", "Wto", "Sro", "Czw", "Pia", "Sob"};
struct sysSensors {
std::string hwmon;
uint16_t fan_speed[2] = {0}; // left and right cpu fans
int16_t t_sensor = 0; // ntc temp sensor
std::byte _pad[2];
};
struct Memory {
uint64_t free = 0;
uint64_t used = 0;
uint64_t total = 0;
};
struct Disk {
std::string mountpoint;
Memory usage;
};
struct TimeDate {
std::string time;
std::string date;
};
struct Network {
uint32_t download = 0;
uint32_t upload = 0;
};
void getTimeDate(TimeDate* td);
void getDisk(Disk* disk);
void getRam(Memory* mem);
void getNetwork(Network* net, const std::string& device);
int detect(sysSensors* s, const std::string& platform);
void updateSensors(sysSensors* s);
} // namespace sys
#endif

View File

@@ -1,80 +0,0 @@
#ifndef UTILS_HPP
#define UTILS_HPP
#include <fcntl.h>
#include <unistd.h>
#include <array>
#include <charconv>
#include <cstdint>
#include <filesystem>
#include <log.hpp>
#include <string>
namespace utils {
constexpr char fill_char = ' ';
enum just : uint8_t { right, left };
std::string pad(std::string s, uint8_t len, just j = right);
std::string center(std::string str, uint8_t len);
std::string getFirstDir(const std::string_view& path);
inline std::string pNum(const auto num, const uint8_t decs = 0) {
static std::array<char, 20> buf;
auto [ptr, ec] = std::to_chars(buf.data(), buf.data() + buf.size(), static_cast<double>(num), std::chars_format::fixed, decs);
return {buf.data(), ptr};
}
/* converts large number in (k)bytes to human-readable numer with suffix and sane decimal */
std::string pBytes(const auto bytes, const bool isKbytes = false) {
double bytes_d = bytes;
for (constexpr std::array suffix_list = {'K', 'M', 'G', 'T', 'P', 'E', 'Z'}; const auto& suffix : suffix_list) {
if (suffix == 'K' && isKbytes) continue; // skip first loop if already in Kbytes
bytes_d /= 1024.0;
if (bytes_d < 1024) {
if (bytes_d > 100) return pNum(bytes_d) + suffix;
if (bytes_d > 10) return pNum(bytes_d, 1) + suffix;
return pNum(bytes_d, 2) + suffix;
}
}
return "++++";
}
/* reads file into a std::array buffer */
template <size_t N>
inline ssize_t readFile(const std::string& path, std::array<char, N>& buffer) {
const int fd = open(path.c_str(), O_RDONLY | O_CLOEXEC);
if (fd < 0) {
applog(LOG::ERR, "Error reading {}", path);
return 0;
}
const ssize_t len = read(fd, buffer.data(), buffer.size());
if (len >= 0) buffer[len] = '\0';
close(fd);
return len;
}
/* reads a number fast and divides it if needed */
template <uint32_t Divider = 1, typename T, size_t N>
inline void fastReadNum(const std::string& path, std::array<char, N>& buf, T& result) {
readFile(path, buf);
if constexpr (Divider == 1) {
std::from_chars(buf.data(), buf.data() + N, result);
return;
}
uint64_t temp{};
std::from_chars(buf.data(), buf.data() + N, temp);
if constexpr (std::is_floating_point_v<T>)
result = temp / static_cast<T>(Divider);
else
result = static_cast<T>(temp / Divider);
}
inline bool fileExists(const std::string_view& path) {
return std::filesystem::exists(path);
}
} // namespace utils
#endif