#include "ui.h" #include #include #include "config.h" // Порядок параметров: (rotation, reset, clock, data) — SCL идёт раньше SDA. static U8G2_SSD1306_72X40_ER_F_HW_I2C display(DISPLAY_ROTATION, U8X8_PIN_NONE, PIN_SCL, PIN_SDA); static const uint8_t SCREEN_W = 72; static const uint8_t GRAPH_W = SCREEN_W; // по пикселю на отсчёт: 72 точки = 72 секунды static const uint8_t GRAPH_TOP = 28; static const uint8_t GRAPH_H = 12; // Значок в правом верхнем углу. Полоса y=0..10 выбрана не случайно: строка под // цифрами лежит ниже (y=11..24), а сами цифры не дотягиваются сюда даже // трёхзначными (49 px + знак = 56). Значок не перекроет ничего. static const uint8_t ICON_W = 11; static const uint8_t ICON_H = 11; static const uint8_t ICON_X = SCREEN_W - ICON_W; static const uint8_t ICON_Y = 0; // 0 = нет данных, иначе высота столбика 1..GRAPH_H static uint8_t cpuHistory[GRAPH_W]; static uint8_t ramHistory[GRAPH_W]; void uiBegin() { display.begin(); display.clearBuffer(); display.setFont(u8g2_font_6x10_tf); display.drawStr(0, 24, "WAIT"); display.sendBuffer(); } // Общая часть для обеих историй: нормировать значение и сдвинуть буфер. static void pushSample(uint8_t *history, float value, float scaleMin, float scaleMax) { if (value < scaleMin) value = scaleMin; if (value > scaleMax) value = scaleMax; const float norm = (value - scaleMin) / (scaleMax - scaleMin); uint8_t h = (uint8_t)(norm * GRAPH_H + 0.5f); if (h < 1) h = 1; memmove(history, history + 1, GRAPH_W - 1); history[GRAPH_W - 1] = h; } void uiPushCpuSample(float tempC) { pushSample(cpuHistory, tempC, SCALE_MIN, SCALE_MAX); } void uiPushRamSample(int memPct) { // У памяти шкала естественная, подбирать нечего. pushSample(ramHistory, (float)memPct, 0.0f, 100.0f); } static void drawGraph(const uint8_t *history) { for (uint8_t x = 0; x < GRAPH_W; x++) { const uint8_t h = history[x]; if (h == 0) continue; display.drawVLine(x, GRAPH_TOP + GRAPH_H - h, h); } } // Микросхема: корпус, ядро внутри и по три вывода с каждой стороны. static void drawCpuIcon(uint8_t x, uint8_t y) { display.drawFrame(x + 1, y + 1, ICON_W - 2, ICON_H - 2); display.drawBox(x + 4, y + 4, 3, 3); for (uint8_t i = 0; i < 3; i++) { const uint8_t o = 3 + i * 2; display.drawPixel(x + o, y); display.drawPixel(x + o, y + ICON_H - 1); display.drawPixel(x, y + o); display.drawPixel(x + ICON_W - 1, y + o); } } // Планка памяти: корпус, три микросхемы на ней и контакты снизу. static void drawRamIcon(uint8_t x, uint8_t y) { display.drawFrame(x, y + 1, ICON_W, 7); for (uint8_t i = 0; i < 3; i++) { display.drawBox(x + 2 + i * 3, y + 3, 2, 3); } for (uint8_t i = 0; i < 5; i++) { display.drawPixel(x + 1 + i * 2, y + 9); } } // Негатив всего кадра. Через буфер, а не командой контроллера: так поведение // не зависит от конкретной микросхемы дисплея. static void invertBuffer() { uint8_t *b = display.getBufferPtr(); const size_t n = (size_t)display.getBufferTileWidth() * display.getBufferTileHeight() * 8; for (size_t i = 0; i < n; i++) b[i] = (uint8_t)~b[i]; } static void drawNoData() { display.setFont(u8g2_font_6x10_tf); display.drawStr(0, 14, "NO"); display.drawStr(0, 26, "DATA"); } static void drawCpuScreen(float tempC, int loadPct) { char buf[8]; snprintf(buf, sizeof(buf), "%d", (int)(tempC + 0.5f)); display.setFont(u8g2_font_logisoso24_tn); display.drawStr(0, 24, buf); const int numWidth = display.getStrWidth(buf); // Градус — надстрочным знаком, вровень с верхом цифр. display.setFont(u8g2_font_5x7_tf); display.drawStr(numWidth + 2, 8, "\xb0"); // Загрузка — шрифтом вдвое выше прежнего. display.setFont(u8g2_font_7x14_tf); snprintf(buf, sizeof(buf), "%d%%", loadPct); display.drawStr(numWidth + 2, 24, buf); drawCpuIcon(ICON_X, ICON_Y); drawGraph(cpuHistory); } static void drawRamScreen(int memPct) { char buf[8]; snprintf(buf, sizeof(buf), "%d", memPct); display.setFont(u8g2_font_logisoso24_tn); display.drawStr(0, 24, buf); const int numWidth = display.getStrWidth(buf); display.setFont(u8g2_font_7x14_tf); display.drawStr(numWidth + 2, 24, "%"); drawRamIcon(ICON_X, ICON_Y); drawGraph(ramHistory); } void uiRender(UiScreen screen, bool fresh, float tempC, int loadPct, int memPct, bool invert) { display.clearBuffer(); if (!fresh) { drawNoData(); } else if (screen == UI_SCREEN_RAM) { drawRamScreen(memPct); } else { drawCpuScreen(tempC, loadPct); } if (invert) invertBuffer(); display.sendBuffer(); }