feat(ui): экран оперативной памяти и переключение кнопкой BOOT
Второй экран показывает загрузку ОЗУ крупной цифрой и её график со шкалой 0-100 - у памяти она естественная, подбирать нечего. В углу свой значок, планка памяти вместо микросхемы: он же и говорит, какой экран открыт. Тревога в приоритете: при перегреве плата принудительно возвращается на экран процессора и кнопка не работает, пока температура не упадёт. Смысл тревоги в том, что её нельзя прощёлкать. Экран памяти выпадает из листания, если агент не шлёт ключ M - со старым агентом кнопка просто ничего не делает. То же решение подойдёт машинам без дискретной видеокарты. Выбранный экран не запоминается при перезагрузке: хранить негде. Тревоги по памяти нет намеренно - загрузка ОЗУ под 100% здесь обычное рабочее состояние. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
4344fa8873
commit
f18090b5b7
8 files changed
+201
-59
No files matched your search
+69
-25
@@ -8,20 +8,23 @@
|
||||
// Порядок параметров: (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 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). Значит значок не перекроет
|
||||
// ничего ни при каких значениях. Размеры измерены на плате, не на глаз.
|
||||
// Значок в правом верхнем углу. Полоса 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_X = SCREEN_W - ICON_W;
|
||||
static const uint8_t ICON_Y = 0;
|
||||
|
||||
static uint8_t history[GRAPH_W]; // 0 = нет данных, иначе высота столбика 1..GRAPH_H
|
||||
// 0 = нет данных, иначе высота столбика 1..GRAPH_H
|
||||
static uint8_t cpuHistory[GRAPH_W];
|
||||
static uint8_t ramHistory[GRAPH_W];
|
||||
|
||||
void uiBegin() {
|
||||
display.begin();
|
||||
@@ -31,11 +34,12 @@ void uiBegin() {
|
||||
display.sendBuffer();
|
||||
}
|
||||
|
||||
void uiPushSample(float tempC) {
|
||||
if (tempC < SCALE_MIN) tempC = SCALE_MIN;
|
||||
if (tempC > SCALE_MAX) tempC = SCALE_MAX;
|
||||
// Общая часть для обеих историй: нормировать значение и сдвинуть буфер.
|
||||
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 = (tempC - SCALE_MIN) / (SCALE_MAX - SCALE_MIN);
|
||||
const float norm = (value - scaleMin) / (scaleMax - scaleMin);
|
||||
uint8_t h = (uint8_t)(norm * GRAPH_H + 0.5f);
|
||||
if (h < 1) h = 1;
|
||||
|
||||
@@ -43,7 +47,16 @@ void uiPushSample(float tempC) {
|
||||
history[GRAPH_W - 1] = h;
|
||||
}
|
||||
|
||||
static void drawGraph() {
|
||||
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;
|
||||
@@ -64,6 +77,17 @@ static void drawCpuIcon(uint8_t x, uint8_t y) {
|
||||
}
|
||||
}
|
||||
|
||||
// Планка памяти: корпус, три микросхемы на ней и контакты снизу.
|
||||
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() {
|
||||
@@ -72,21 +96,15 @@ static void invertBuffer() {
|
||||
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;
|
||||
}
|
||||
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);
|
||||
@@ -102,8 +120,34 @@ void uiRender(bool fresh, float tempC, int loadPct, bool invert) {
|
||||
display.drawStr(numWidth + 2, 24, buf);
|
||||
|
||||
drawCpuIcon(ICON_X, ICON_Y);
|
||||
drawGraph(cpuHistory);
|
||||
}
|
||||
|
||||
drawGraph();
|
||||
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();
|
||||
|
||||
|
||||
Reference in new issue
Block a user