#include #include #include #include #include #include #include #include #include #include #include #include // #include #include #include #include #include #include #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 ' ' std::chrono::milliseconds interval(LCD_INTERVAL_MS); std::stop_source sigint; struct mem_usage_t { uint64_t free = 0; uint64_t used = 0; uint64_t total = 0; }; struct sensors_t { std::array td; std::string loadavg; mem_usage_t ram; mem_usage_t home; gpu_t gpu; cpu_t cpu; float hass_state = 0; }; /* DISPLAY FUNCS */ // pads given string to length std::string pad(std::string s, uint8_t len, bool ljust = false) { if (len > s.size()) s.insert(ljust ? s.end() : s.begin(), len - s.size(), FILL_CHAR); return s; } // fills n spaces std::string fill(uint8_t n) { return std::string(n, FILL_CHAR); } /* GETTERS */ mem_usage_t getRam() { mem_usage_t mem; std::ifstream proc_meminfo("/proc/meminfo"); if (proc_meminfo.good()) { std::string content((std::istreambuf_iterator(proc_meminfo)), std::istreambuf_iterator()); mem.free = get_val("MemAvailable:", content); mem.total = get_val("MemTotal:", content); mem.used = mem.total - mem.free; } proc_meminfo.close(); return mem; } mem_usage_t getDisk(std::string mount) { mem_usage_t disk; struct statvfs diskData; statvfs(mount.c_str(), &diskData); disk.free = diskData.f_bfree * diskData.f_frsize; disk.used = (diskData.f_blocks - diskData.f_bfree) * diskData.f_frsize; disk.total = diskData.f_blocks * diskData.f_frsize; return disk; } void sighandler(int s) { std::println("[Signal] got SIGINT, exiting..."); sigint.request_stop(); } 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::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 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); sensors_t sensors; std::jthread producer(sensors_thread, &sensors, sigint.get_token()); std::jthread hass(hass_thread, &sensors, sigint.get_token()); display_thread(&sensors, sigint.get_token()); return 0; }