#include #include #include #include #include #include #include #include #include "aio.h" #include "cpu.h" #include "gpu.h" #include "lcd.h" #include "log.h" #include "sys.h" #include "utils.h" static const uint8_t lcd_contrast = 220; static const uint16_t refresh_interval_ms = 500; static const char netif[] = "enp4s0f0"; static const char cpu_pcidev[] = "0000:00:18.3"; static const char gpu_pcidev[] = "0000:0b:00.0"; struct control_t { atomic_bool g_running; pthread_mutex_t sensors_mtx; pthread_cond_t sensors_ready; } control; struct sensors_t { // NOLINT yes it is struct time_date_t td; struct memory_t ram; struct disk_t home; struct disk_t gry; struct network_t net; struct syssensors_t sys; struct cpu_sensors_t cpu; struct gpu_sensors_t gpu; struct aio_t aio; double loadavg[3]; }; static void *sensors_thread(void *arg) { applog(LOG_START, "sensors thread started, refresh interval is %dms", refresh_interval_ms); struct sensors_t *sensors = arg; struct timespec next; clock_gettime(CLOCK_REALTIME, &next); /* set up paths for the sensors */ strcpy(sensors->home.mountpoint, "/home/drath"); strcpy(sensors->gry.mountpoint, "/var/mnt/gry"); strcpy(sensors->net.device, netif); cpu_detect(&sensors->cpu, cpu_pcidev); gpu_detect(&sensors->gpu, gpu_pcidev); sys_detect(&sensors->sys, "nct6775.656"); aio_detect(&sensors->aio); /* main sensor loop */ applog(LOG_START, "reached sensors main loop"); while (control.g_running) { // NOLINT pthread_mutex_lock(&control.sensors_mtx); { sys_get_time(&sensors->td); sys_get_load_avg(sensors->loadavg); sys_get_ram(&sensors->ram); sys_get_disk(&sensors->home); sys_get_disk(&sensors->gry); sys_get_network(&sensors->net); sys_update_sensors(&sensors->sys); cpu_update(&sensors->cpu); gpu_update(&sensors->gpu); aio_update(&sensors->aio); } pthread_cond_signal(&control.sensors_ready); pthread_mutex_unlock(&control.sensors_mtx); timespec_add_ms(&next, refresh_interval_ms); clock_nanosleep(CLOCK_REALTIME, TIMER_ABSTIME, &next, NULL); } aio_close(&sensors->aio); applog(LOG_WRN, "sensors thread stopped"); return NULL; } static void *display_thread(void *arg) { applog(LOG_START, "display thread started"); struct sensors_t *sensors = arg; /* lcd setup */ struct lcd_t lcd = { .port = "/dev/serial/by-id/usb-Raspberry_Pi_Pico_E6616407E37AA12F-if00", .baud = 0010002, // from termios-baud.h, B115200 }; lcd_init(&lcd); lcd_set_backlight(&lcd, true); lcd_set_contrast(&lcd, lcd_contrast); lcd_write_buf(&lcd, 1, 9, "Waiting for sensors..."); // need to center this still lcd_flush_buf(&lcd); lcd_clear_buf(&lcd); /* setup custom chars */ lcd_set_custom_char(&lcd, 0, &(lcd_glyph_t){16, 6, 9, 8, 8, 9, 6, 0}); // celcius lcd_set_custom_char(&lcd, 1, &(lcd_glyph_t){0, 0, 4, 10, 10, 17, 31, 0}); // delta lcd_set_custom_char(&lcd, 2, &(lcd_glyph_t){0, 0, 8, 21, 21, 2, 0, 0}); // home (tilde) lcd_set_custom_char(&lcd, 3, &(lcd_glyph_t){4, 14, 21, 4, 4, 4, 0}); // up arrow lcd_set_custom_char(&lcd, 4, &(lcd_glyph_t){4, 4, 4, 21, 14, 4, 0}); // down arrow lcd_set_custom_char(&lcd, 5, &(lcd_glyph_t){0, 0, 15, 21, 21, 17, 15, 0}); // home (tilde) /* main display loop */ applog(LOG_START, "reached display main loop"); while (control.g_running) { // NOLINT its updated globally bud struct timespec ts; clock_gettime(CLOCK_REALTIME, &ts); timespec_add_ms(&ts, (uint16_t)(refresh_interval_ms * 1.25)); pthread_mutex_lock(&control.sensors_mtx); // lock sensor struct pthread_cond_timedwait(&control.sensors_ready, &control.sensors_mtx, &ts); // wait until data ready { // prepare some tricky sensors const char modes[] = "QBP"; const uint16_t avg_fan_speed = (sensors->aio.fan_speed[0] + sensors->aio.fan_speed[1]) / 2; char gpudelta[5]; const uint8_t delta = sensors->gpu.temp_hotspot - sensors->gpu.temp_edge; snprintf(gpudelta, sizeof(gpudelta), "%c%d", 1, delta); const uint8_t gpu_pos = delta > 10 ? 3 : 2; char gpuclock[16]; snprintf(gpuclock, sizeof(gpuclock), "%d/%dMHz", sensors->gpu.clock_core, sensors->gpu.clock_mem); lcd_writef(&lcd, 0, 0, "%c%s", 2, P_BYTES(sensors->home.usage.free, 1)); lcd_writef(&lcd, 7, 0, "%c%s", 5, P_BYTES(sensors->gry.usage.free, 1)); lcd_writef(&lcd, 15, 0, "%s%c", sensors->td.time, lcd_bar(&lcd, 5, sensors->gpu.usage_core / 100.0)); lcd_writef(&lcd, 26, 0, "%s %s %s", P_NUM(sensors->loadavg[0]), P_NUM(sensors->loadavg[1]), P_NUM(sensors->loadavg[2])); lcd_writef(&lcd, 0, 1, "%d/%d%c", sensors->cpu.tctl, sensors->cpu.tccd1, 0); lcd_writef(&lcd, 7, 1, "%s", P_BYTES(sensors->ram.used, 0)); lcd_writef(&lcd, 13, 1, "%.1f%c", sensors->aio.water_temp, 0); lcd_writef(&lcd, 18, 1, "%3d%c", sensors->sys.t_sensor, 0); lcd_writef(&lcd, 28, 1, "%4s%c", gpudelta, 0); lcd_writef(&lcd, 25 - gpu_pos, 1, "%6s", P_BYTES(sensors->gpu.vram_used, 1)); lcd_writef(&lcd, 34, 1, "%d/%d%c", sensors->gpu.temp_edge, sensors->gpu.temp_hotspot, 0); lcd_writef(&lcd, 0, 2, "%.0fW", sensors->cpu.ptotal); lcd_writef(&lcd, 5, 2, "%dR", avg_fan_speed); lcd_writef(&lcd, 12, 2, "%c%s %c%s", 3, P_BYTES(sensors->net.upload, 1), 4, P_BYTES(sensors->net.download, 1)); lcd_writef(&lcd, 26, 2, "%4dR", sensors->gpu.fan_speed); lcd_writef(&lcd, 32, 2, "%3d/%3dW", sensors->gpu.power_avg, sensors->gpu.power_cap); lcd_writef(&lcd, 0, 3, "%dMHz", sensors->cpu.avg_freq); lcd_writef(&lcd, 8, 3, "%4dR-%c", sensors->aio.pump_speed, modes[sensors->aio.pump_mode]); lcd_writef(&lcd, 21, 3, "%12s", gpuclock); lcd_writef(&lcd, 34, 3, "%.3fV", sensors->gpu.vcore); } pthread_mutex_unlock(&control.sensors_mtx); // unlock sensor struct lcd_flush_buf(&lcd); lcd_clear_buf(&lcd); } lcd_write_buf(&lcd, 2, 16, "Goodbye"); // center lcd_flush_buf(&lcd); lcd_close(&lcd); applog(LOG_WRN, "display thread stopped"); return NULL; } int main(void) { applog(LOG_START, "linuxSmartie starting"); applog(LOG_DBG, "Compiled on %s %s with %s", __DATE__, __TIME__, C_STD); /* see if another instance is up, quit if it is */ if (get_instance_lock() < 0) return 0; /* malloc memory for the struct */ struct sensors_t *sensors = malloc(sizeof(*sensors)); // no actual point in mallocing and it is ever so sligtly slower to access heap vs stack, but its cool if (sensors == NULL) { applog(LOG_ERR, "can't allocate mem for the sensor struct. You don't got %lu bytes bro?", sizeof(struct sensors_t)); free(sensors); return 1; } applog(LOG_INF, "sensor struct allocated, size is %lu bytes", sizeof(struct sensors_t)); /* signal setup */ sigset_t sigset; sigemptyset(&sigset); sigaddset(&sigset, SIGINT); sigaddset(&sigset, SIGTERM); sigaddset(&sigset, SIGQUIT); pthread_sigmask(SIG_BLOCK, &sigset, NULL); struct signalfd_siginfo info; const int sfd = signalfd(-1, &sigset, SFD_CLOEXEC); /* control vars setup */ pthread_mutex_init(&control.sensors_mtx, NULL); pthread_cond_init(&control.sensors_ready, NULL); control.g_running = true; /* thread setup */ pthread_t sensors_pthread; pthread_t display_pthread; /* actually start the threads */ pthread_create(&sensors_pthread, NULL, sensors_thread, sensors); pthread_create(&display_pthread, NULL, display_thread, sensors); /* halts here until signal to stop */ const ssize_t ret = read(sfd, &info, sizeof(info)); (void)ret; /* cleanup on exit */ applog(LOG_WRN, "got stop signal, shutting down..."); close(sfd); control.g_running = false; pthread_join(sensors_pthread, NULL); pthread_join(display_pthread, NULL); free(sensors); return 0; }