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
+197
-55
No files matched your search
+47
-4
@@ -1,13 +1,14 @@
|
||||
// Индикатор температуры CPU: плата принимает строки "T=67.0;L=34" по USB-COM
|
||||
// Индикатор состояния ПК: плата принимает строки "T=67.0;L=34;M=97" по USB-COM
|
||||
// от агента на ПК и рисует их на встроенном OLED 72x40. Сама ничего не меряет.
|
||||
//
|
||||
// Два экрана: процессор и оперативная память, переключение кнопкой BOOT.
|
||||
// Все настройки — в config.h.
|
||||
|
||||
#include "config.h"
|
||||
#include "link.h"
|
||||
#include "ui.h"
|
||||
|
||||
static CpuStats stats = {0.0f, 0};
|
||||
static CpuStats stats = {0.0f, 0, -1};
|
||||
static uint32_t lastData = 0;
|
||||
static uint32_t lastSample = 0;
|
||||
static uint32_t lastDraw = 0;
|
||||
@@ -16,6 +17,8 @@ static bool alarmActive = false;
|
||||
static bool wasFresh = false;
|
||||
static float tempShown = 0.0f; // усреднённое значение, именно оно на экране
|
||||
|
||||
static UiScreen screen = UI_SCREEN_CPU;
|
||||
|
||||
// Скользящее среднее температуры. Сумма пересчитывается заново, а не копится:
|
||||
// на пяти числах это бесплатно, зато нет накопления ошибки округления за сутки
|
||||
// непрерывной работы.
|
||||
@@ -41,6 +44,7 @@ static float tempAvgPush(float v) {
|
||||
void setup() {
|
||||
pinMode(PIN_LED, OUTPUT);
|
||||
digitalWrite(PIN_LED, !LED_ACTIVE_LEVEL);
|
||||
pinMode(PIN_BUTTON, INPUT_PULLUP);
|
||||
|
||||
linkBegin();
|
||||
uiBegin();
|
||||
@@ -52,6 +56,38 @@ static void updateAlarm(float tempC) {
|
||||
else if (alarmActive && tempC <= ALARM_OFF_C) alarmActive = false;
|
||||
}
|
||||
|
||||
// Экран памяти показываем, только если данные по ней действительно приходят:
|
||||
// со старым агентом ключа M в строке нет вовсе.
|
||||
static bool ramAvailable() {
|
||||
return stats.memPct >= 0;
|
||||
}
|
||||
|
||||
static void onButtonPress() {
|
||||
// Тревога в приоритете: уйти с экрана процессора нельзя, пока он горячий.
|
||||
if (alarmActive) return;
|
||||
if (!ramAvailable()) return;
|
||||
|
||||
screen = (screen == UI_SCREEN_CPU) ? UI_SCREEN_RAM : UI_SCREEN_CPU;
|
||||
}
|
||||
|
||||
// Кнопка BOOT замыкает пин на землю, поэтому нажатию соответствует LOW.
|
||||
static void pollButton() {
|
||||
static bool stable = HIGH;
|
||||
static bool lastRead = HIGH;
|
||||
static uint32_t changedAt = 0;
|
||||
|
||||
const bool now = digitalRead(PIN_BUTTON);
|
||||
if (now != lastRead) {
|
||||
lastRead = now;
|
||||
changedAt = millis();
|
||||
}
|
||||
|
||||
if (millis() - changedAt >= BUTTON_DEBOUNCE_MS && now != stable) {
|
||||
stable = now;
|
||||
if (stable == LOW) onButtonPress();
|
||||
}
|
||||
}
|
||||
|
||||
void loop() {
|
||||
CpuStats fresh;
|
||||
if (linkPoll(fresh)) {
|
||||
@@ -75,6 +111,12 @@ void loop() {
|
||||
alarmActive = false; // без данных о перегреве судить не по чему
|
||||
}
|
||||
|
||||
pollButton();
|
||||
|
||||
// Тревога перебивает выбор пользователя и возвращает на экран процессора.
|
||||
if (alarmActive) screen = UI_SCREEN_CPU;
|
||||
if (screen == UI_SCREEN_RAM && !ramAvailable()) screen = UI_SCREEN_CPU;
|
||||
|
||||
// Общая фаза для экрана и светодиода, чтобы они моргали синхронно.
|
||||
const bool blinkOn = (millis() / ALARM_BLINK_MS) % 2 == 0;
|
||||
|
||||
@@ -88,11 +130,12 @@ void loop() {
|
||||
// Историю дописываем только при живой связи: пауза не должна рисоваться провалом.
|
||||
if (isFresh && millis() - lastSample >= SAMPLE_MS) {
|
||||
lastSample = millis();
|
||||
uiPushSample(stats.tempC); // график по сырым: 5 градусов на пиксель, шум там не виден
|
||||
uiPushCpuSample(stats.tempC); // график по сырым: 5 градусов на пиксель, шум там не виден
|
||||
if (ramAvailable()) uiPushRamSample(stats.memPct);
|
||||
}
|
||||
|
||||
if (millis() - lastDraw >= DRAW_MS) {
|
||||
lastDraw = millis();
|
||||
uiRender(isFresh, tempShown, stats.loadPct, alarmActive && blinkOn);
|
||||
uiRender(screen, isFresh, tempShown, stats.loadPct, stats.memPct, alarmActive && blinkOn);
|
||||
}
|
||||
}
|
||||
+32
-9
@@ -1,8 +1,9 @@
|
||||
# CPU Temp — индикатор температуры процессора на ESP32-C3
|
||||
|
||||
Небольшая плата ESP32-C3 со встроенным OLED-экраном 0.42″ стоит на столе и
|
||||
показывает температуру процессора компьютера: крупная цифра, загрузка CPU рядом
|
||||
и график за последнюю минуту. При перегреве экран мигает негативом.
|
||||
показывает состояние компьютера: крупная цифра, мелкий показатель рядом и график
|
||||
за последнюю минуту. Экранов два — процессор и оперативная память, переключаются
|
||||
кнопкой BOOT. При перегреве процессора экран мигает негативом.
|
||||
|
||||
```
|
||||
LibreHardwareMonitorLib.dll ──> PowerShell-агент ──COM (USB CDC)──> ESP32-C3 ──I2C──> OLED 72×40
|
||||
@@ -240,6 +241,8 @@ Start-ScheduledTask -TaskName 'CPU Temp Agent'
|
||||
| `ALARM_OFF_C` | 82.0 | температура отпускания тревоги |
|
||||
| `ALARM_BLINK_MS` | 500 | полупериод мигания |
|
||||
| `PIN_LED` | 8 | светодиод |
|
||||
| `PIN_BUTTON` | 9 | кнопка BOOT, переключает экраны |
|
||||
| `BUTTON_DEBOUNCE_MS` | 50 | защита от дребезга контактов |
|
||||
| `LED_ACTIVE_LEVEL` | `LOW` | уровень, при котором светодиод горит |
|
||||
| `TEMP_AVG_SAMPLES` | 5 | окно сглаживания температуры, замеров |
|
||||
| `SCALE_MIN`, `SCALE_MAX` | 30.0, 90.0 | шкала графика, °C |
|
||||
@@ -283,14 +286,34 @@ Start-ScheduledTask -TaskName 'CPU Temp Agent'
|
||||
|
||||
## Что показывает экран
|
||||
|
||||
Экранов два, переключаются коротким нажатием кнопки **BOOT**:
|
||||
|
||||
| Экран | Крупно | Мелко | График |
|
||||
|---|---|---|---|
|
||||
| Процессор | температура, °C | загрузка, % | температура, шкала 30–90 °C |
|
||||
| Память | загрузка, % | знак процента | загрузка, шкала 0–100 % |
|
||||
|
||||
Значок в правом верхнем углу говорит, какой экран открыт: микросхема — процессор,
|
||||
планка памяти — ОЗУ.
|
||||
|
||||
| Состояние | Экран | Светодиод GPIO8 |
|
||||
|---|---|---|
|
||||
| Норма | температура, загрузка, график | погашен |
|
||||
| Перегрев | то же, мигает негативом | мигает синхронно с экраном |
|
||||
| Норма | выбранный экран | погашен |
|
||||
| Перегрев | **принудительно** экран процессора, мигает негативом | мигает синхронно с экраном |
|
||||
| Нет данных дольше 5 с | `NO DATA` | горит ровно |
|
||||
|
||||
В режиме `NO DATA` история графика не пополняется — иначе обрыв связи выглядел бы
|
||||
как резкое падение температуры.
|
||||
**Тревога в приоритете.** При перегреве плата сама возвращается на экран
|
||||
процессора, и кнопка перестаёт работать до тех пор, пока температура не упадёт.
|
||||
Смысл тревоги в том, что её нельзя прощёлкать.
|
||||
|
||||
Выбранный экран **не запоминается** при перезагрузке платы: энергонезависимого
|
||||
хранилища в прошивке нет вовсе, после сброса всегда показывается процессор.
|
||||
|
||||
Тревога считается только по температуре процессора. По памяти её нет намеренно:
|
||||
загрузка ОЗУ под 100 % — обычное рабочее состояние, мигать на него бессмысленно.
|
||||
|
||||
В режиме `NO DATA` история графиков не пополняется — иначе обрыв связи выглядел
|
||||
бы как резкое падение значений.
|
||||
|
||||
---
|
||||
|
||||
@@ -308,9 +331,9 @@ T=67.0;L=34;M=97
|
||||
| `L` | общая загрузка CPU, % |
|
||||
| `M` | загрузка оперативной памяти, % |
|
||||
|
||||
Ключ `M` агент шлёт, а прошивка пока **не показывает** — на экране 72×40 нет
|
||||
места для третьего показателя. Это и есть демонстрация расширяемости: агента
|
||||
обновили, платы не трогали, ничего не сломалось.
|
||||
Ключ `M` показывается на отдельном экране памяти — третьему показателю на экране
|
||||
72×40 места нет. Если агент старый и ключа `M` не шлёт, экран памяти из листания
|
||||
выпадает сам, и кнопка ничего не делает.
|
||||
|
||||
Берётся он у железа с именем `Total Memory` — это физическая память. У соседнего
|
||||
`Virtual Memory` датчик называется точно так же, но считает вместе с файлом
|
||||
|
||||
@@ -37,6 +37,16 @@ static const uint8_t PIN_LED = 8;
|
||||
// высокий уровень и нужен.
|
||||
static const uint8_t LED_ACTIVE_LEVEL = LOW;
|
||||
|
||||
// --- Кнопка переключения экранов -------------------------------------------
|
||||
|
||||
// Кнопка BOOT. Это пин начальной загрузки: если держать его нажатым в момент
|
||||
// сброса, плата уйдёт в режим прошивки - штатное поведение кнопки, а не наша
|
||||
// беда. В работе читается как обычный вход с подтяжкой.
|
||||
static const uint8_t PIN_BUTTON = 9;
|
||||
|
||||
// Порог защиты от дребезга контактов.
|
||||
static const uint32_t BUTTON_DEBOUNCE_MS = 50;
|
||||
|
||||
// --- Сглаживание показаний -------------------------------------------------
|
||||
|
||||
// Скользящее среднее температуры: цифра не должна мельтешить на +-1 градус,
|
||||
|
||||
+25
-14
@@ -5,7 +5,25 @@
|
||||
|
||||
---
|
||||
|
||||
## 1. Другие показатели и листалка по кнопке BOOT
|
||||
## 1. Другие показатели и листалка по кнопке BOOT — частично сделано
|
||||
|
||||
**Сделано 2026-09-17:** экран оперативной памяти и переключение кнопкой BOOT.
|
||||
Решения, принятые при реализации, чтобы не пересматривать заново:
|
||||
|
||||
- **Тревога в приоритете.** При перегреве плата принудительно возвращается на
|
||||
экран процессора, и кнопка не работает, пока температура не упадёт. Из трёх
|
||||
вариантов, перечисленных ниже, выбран самый жёсткий: смысл тревоги в том, что
|
||||
её нельзя прощёлкать.
|
||||
- **Выбранный экран не запоминается** при перезагрузке — хранить негде.
|
||||
- **Тревоги по памяти нет:** загрузка ОЗУ под 100 % на этой машине обычное
|
||||
состояние.
|
||||
- **Защита от дребезга** — 50 мс, порог в `config.h`.
|
||||
- **Экран без данных выпадает из листания.** Если агент не шлёт ключ `M`, экран
|
||||
памяти недоступен, и кнопка просто ничего не делает. Это же решение подойдёт
|
||||
для машин без дискретной видеокарты.
|
||||
|
||||
**Осталось:** экраны батареи и сети, а также видеокарта — но у неё свой путь,
|
||||
см. пункт 2.
|
||||
|
||||
**Что хочется.** Показывать не только процессор: видеокарту, оперативную память,
|
||||
диски. Переключать экраны кнопкой **BOOT** на плате — нажал, увидел следующий
|
||||
@@ -18,11 +36,9 @@
|
||||
|
||||
**Что придётся решить при реализации:**
|
||||
|
||||
- **Кнопка BOOT сидит на GPIO9.** Это пин начальной загрузки: при сбросе он
|
||||
должен быть в высоком уровне, иначе плата уйдёт в режим прошивки. Читать его
|
||||
как `INPUT_PULLUP` в работе безопасно, но проверить фактически.
|
||||
- **Дребезг контактов.** Нужна программная защита, иначе одно нажатие пролистает
|
||||
три экрана.
|
||||
- ~~**Кнопка BOOT сидит на GPIO9.**~~ Проверено на практике: чтение через
|
||||
`INPUT_PULLUP` в работе никак не мешает загрузке платы.
|
||||
- ~~**Дребезг контактов.**~~ Сделано: 50 мс, `BUTTON_DEBOUNCE_MS`.
|
||||
- **Свой макет под каждый экран.** На 72×40 не получится «то же самое, только
|
||||
другие цифры»: у памяти уместнее проценты и гигабайты, у диска — температура
|
||||
без графика. Каждый экран проектировать отдельно.
|
||||
@@ -32,14 +48,9 @@
|
||||
- **Что показывать, если данных нет.** Например, в системе нет дискретной
|
||||
видеокарты — экран GPU надо либо пропускать при листании, либо честно писать,
|
||||
что источника нет.
|
||||
- **Запоминать ли выбранный экран** между перезагрузками. Если да, понадобится
|
||||
энергонезависимое хранилище, которого у прошивки сейчас нет вовсе.
|
||||
- **Тревога при листании.** Сейчас перегрев виден всегда, потому что экран один.
|
||||
С листалкой можно стоять на экране памяти и не увидеть, что горит процессор.
|
||||
Нужно решить: принудительно перекидывать на экран CPU, показывать тревогу
|
||||
поверх любого экрана или мигать только светодиодом.
|
||||
|
||||
Последний пункт — самый важный: он затрагивает смысл устройства, а не удобство.
|
||||
- ~~**Запоминать ли выбранный экран**~~ Решено не запоминать: хранилища нет.
|
||||
- ~~**Тревога при листании.**~~ Решено: принудительно перекидывать на экран
|
||||
процессора и блокировать кнопку.
|
||||
|
||||
**Для видеокарты выбран другой путь** — приоритетный экран без кнопки, см.
|
||||
пункт 2. Листалка остаётся в силе для остального: памяти, батареи, сети.
|
||||
|
||||
+5
-1
@@ -16,7 +16,7 @@ void linkBegin() {
|
||||
static bool parseLine(char *line, CpuStats &out) {
|
||||
bool haveTemp = false;
|
||||
bool haveLoad = false;
|
||||
CpuStats parsed = {0.0f, 0};
|
||||
CpuStats parsed = {0.0f, 0, -1};
|
||||
|
||||
char *save = nullptr;
|
||||
for (char *tok = strtok_r(line, ";", &save); tok; tok = strtok_r(nullptr, ";", &save)) {
|
||||
@@ -32,6 +32,10 @@ static bool parseLine(char *line, CpuStats &out) {
|
||||
} else if (strcmp(key, "L") == 0) {
|
||||
parsed.loadPct = atoi(val);
|
||||
haveLoad = true;
|
||||
} else if (strcmp(key, "M") == 0) {
|
||||
// Память необязательна: старый агент её не шлёт, и это не повод
|
||||
// отбрасывать строку целиком.
|
||||
parsed.memPct = atoi(val);
|
||||
}
|
||||
// прочие ключи игнорируются намеренно: протокол расширяемый
|
||||
}
|
||||
|
||||
+2
-1
@@ -3,10 +3,11 @@
|
||||
struct CpuStats {
|
||||
float tempC;
|
||||
int loadPct;
|
||||
int memPct; // -1 = ключа M в строке не было
|
||||
};
|
||||
|
||||
void linkBegin();
|
||||
|
||||
// Неблокирующее чтение. Возвращает true, когда собрана и разобрана
|
||||
// целая строка вида "T=67.0;L=34". Неизвестные ключи игнорируются.
|
||||
// целая строка вида "T=67.0;L=34;M=97". Неизвестные ключи игнорируются.
|
||||
bool linkPoll(CpuStats &out);
|
||||
+66
-22
@@ -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) {
|
||||
static void drawNoData() {
|
||||
display.setFont(u8g2_font_6x10_tf);
|
||||
display.drawStr(0, 14, "NO");
|
||||
display.drawStr(0, 26, "DATA");
|
||||
if (invert) invertBuffer();
|
||||
display.sendBuffer();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
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();
|
||||
|
||||
|
||||
+10
-4
@@ -2,14 +2,20 @@
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
enum UiScreen : uint8_t {
|
||||
UI_SCREEN_CPU = 0,
|
||||
UI_SCREEN_RAM = 1,
|
||||
};
|
||||
|
||||
// Инициализирует дисплей и показывает заставку ожидания.
|
||||
void uiBegin();
|
||||
|
||||
// Добавляет значение в кольцевой буфер истории. Вызывать раз в секунду
|
||||
// и только при свежих данных, чтобы пауза связи не рисовалась как провал.
|
||||
void uiPushSample(float tempC);
|
||||
// Пополняют историю соответствующего графика. Вызывать раз в секунду и только
|
||||
// при свежих данных, чтобы пауза связи не рисовалась как провал.
|
||||
void uiPushCpuSample(float tempC);
|
||||
void uiPushRamSample(int memPct);
|
||||
|
||||
// Перерисовывает экран целиком.
|
||||
// fresh == false - данные устарели, вместо цифр выводится NO DATA.
|
||||
// invert == true - кадр показывается в негативе (фаза мигания тревоги).
|
||||
void uiRender(bool fresh, float tempC, int loadPct, bool invert);
|
||||
void uiRender(UiScreen screen, bool fresh, float tempC, int loadPct, int memPct, bool invert);
|
||||
Reference in new issue
Block a user