Files
Arduino/CPU_temp/ui.cpp
T
oskarvitaliiandClaude Opus 5 c76c2ef7e5 feat(ui): значок процессора в правом верхнем углу
Полоса y=0..10 справа выбрана по измерениям на самой плате, а не на глаз:
строка загрузки лежит ниже (y=11..24), а цифры температуры не дотягиваются
туда даже трёхзначными (49 px + градус = 56 из 72). Значок 11x11 не
перекрывает ничего ни при каких значениях и не исчезает под нагрузкой.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-14 23:29:19 +07:00

112 lines
3.8 KiB
C++

#include "ui.h"
#include <U8g2lib.h>
#include <string.h>
#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 GRAPH_W = 72; // по пикселю на отсчёт: 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 = 72 - ICON_W;
static const uint8_t ICON_Y = 0;
static uint8_t history[GRAPH_W]; // 0 = нет данных, иначе высота столбика 1..GRAPH_H
void uiBegin() {
display.begin();
display.clearBuffer();
display.setFont(u8g2_font_6x10_tf);
display.drawStr(0, 24, "WAIT");
display.sendBuffer();
}
void uiPushSample(float tempC) {
if (tempC < SCALE_MIN) tempC = SCALE_MIN;
if (tempC > SCALE_MAX) tempC = SCALE_MAX;
const float norm = (tempC - SCALE_MIN) / (SCALE_MAX - SCALE_MIN);
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;
}
static void drawGraph() {
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 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];
}
void uiRender(bool fresh, float tempC, int loadPct, bool invert) {
display.clearBuffer();
if (!fresh) {
display.setFont(u8g2_font_6x10_tf);
display.drawStr(0, 14, "NO");
display.drawStr(0, 26, "DATA");
if (invert) invertBuffer();
display.sendBuffer();
return;
}
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();
if (invert) invertBuffer();
display.sendBuffer();
}