From fcc96f509000e57a45e0753b680993075337c10a Mon Sep 17 00:00:00 2001 From: Vitali Date: Tue, 15 Sep 2026 00:41:20 +0700 Subject: [PATCH] =?UTF-8?q?feat(ui):=20=D1=81=D0=B3=D0=BB=D0=B0=D0=B6?= =?UTF-8?q?=D0=B8=D0=B2=D0=B0=D1=82=D1=8C=20=D1=82=D0=B5=D0=BC=D0=BF=D0=B5?= =?UTF-8?q?=D1=80=D0=B0=D1=82=D1=83=D1=80=D1=83=20=D1=81=D0=BA=D0=BE=D0=BB?= =?UTF-8?q?=D1=8C=D0=B7=D1=8F=D1=89=D0=B8=D0=BC=20=D1=81=D1=80=D0=B5=D0=B4?= =?UTF-8?q?=D0=BD=D0=B8=D0=BC=20=D0=B7=D0=B0=205=20=D0=B7=D0=B0=D0=BC?= =?UTF-8?q?=D0=B5=D1=80=D0=BE=D0=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Мгновенное значение плавает на +-1 градус, и цифра мельтешила каждую секунду при полном покое. На экран теперь идёт среднее за TEMP_AVG_SAMPLES последних замеров; агент шлёт раз в секунду, так что это ровно 5 секунд. Сумма пересчитывается заново на каждом шаге, а не копится: на пяти числах это бесплатно, зато нет накопления ошибки округления за сутки непрерывной работы. Окно сбрасывается при обрыве связи, иначе после паузы среднее считалось бы по значениям до обрыва. Тревога переведена на усреднённое значение: иначе экран показывал бы 84, когда мигание уже идёт. Загрузка и график намеренно остались сырыми. Co-Authored-By: Claude Opus 5 --- CPU_temp/CPU_temp.ino | 37 ++++++++++++++++++++++++++++++++++--- CPU_temp/README.md | 21 +++++++++++++++++++++ CPU_temp/config.h | 9 +++++++++ 3 files changed, 64 insertions(+), 3 deletions(-) diff --git a/CPU_temp/CPU_temp.ino b/CPU_temp/CPU_temp.ino index c04cff6..eb6c06a 100644 --- a/CPU_temp/CPU_temp.ino +++ b/CPU_temp/CPU_temp.ino @@ -13,6 +13,30 @@ static uint32_t lastSample = 0; static uint32_t lastDraw = 0; static bool everGot = false; static bool alarmActive = false; +static bool wasFresh = false; +static float tempShown = 0.0f; // усреднённое значение, именно оно на экране + +// Скользящее среднее температуры. Сумма пересчитывается заново, а не копится: +// на пяти числах это бесплатно, зато нет накопления ошибки округления за сутки +// непрерывной работы. +static float tempWindow[TEMP_AVG_SAMPLES]; +static uint8_t tempCount = 0; +static uint8_t tempIndex = 0; + +static void tempAvgReset() { + tempCount = 0; + tempIndex = 0; +} + +static float tempAvgPush(float v) { + tempWindow[tempIndex] = v; + tempIndex = (uint8_t)((tempIndex + 1) % TEMP_AVG_SAMPLES); + if (tempCount < TEMP_AVG_SAMPLES) tempCount++; + + float sum = 0.0f; + for (uint8_t i = 0; i < tempCount; i++) sum += tempWindow[i]; + return sum / tempCount; +} void setup() { pinMode(PIN_LED, OUTPUT); @@ -32,14 +56,21 @@ void loop() { CpuStats fresh; if (linkPoll(fresh)) { stats = fresh; + tempShown = tempAvgPush(fresh.tempC); lastData = millis(); everGot = true; } const bool isFresh = everGot && (millis() - lastData < STALE_MS); + // После паузы связи среднее нельзя считать по значениям до обрыва. + if (wasFresh && !isFresh) tempAvgReset(); + wasFresh = isFresh; + if (isFresh) { - updateAlarm(stats.tempC); + // Тревога по усреднённому значению: иначе экран показывал бы 84, а мигание + // уже шло бы. Побочно это защищает от срабатывания на одиночный выброс. + updateAlarm(tempShown); } else { alarmActive = false; // без данных о перегреве судить не по чему } @@ -57,11 +88,11 @@ void loop() { // Историю дописываем только при живой связи: пауза не должна рисоваться провалом. if (isFresh && millis() - lastSample >= SAMPLE_MS) { lastSample = millis(); - uiPushSample(stats.tempC); + uiPushSample(stats.tempC); // график по сырым: 5 градусов на пиксель, шум там не виден } if (millis() - lastDraw >= DRAW_MS) { lastDraw = millis(); - uiRender(isFresh, stats.tempC, stats.loadPct, alarmActive && blinkOn); + uiRender(isFresh, tempShown, stats.loadPct, alarmActive && blinkOn); } } diff --git a/CPU_temp/README.md b/CPU_temp/README.md index 429c82f..3ae7c3b 100644 --- a/CPU_temp/README.md +++ b/CPU_temp/README.md @@ -241,6 +241,7 @@ Start-ScheduledTask -TaskName 'CPU Temp Agent' | `ALARM_BLINK_MS` | 500 | полупериод мигания | | `PIN_LED` | 8 | светодиод | | `LED_ACTIVE_LEVEL` | `LOW` | уровень, при котором светодиод горит | +| `TEMP_AVG_SAMPLES` | 5 | окно сглаживания температуры, замеров | | `SCALE_MIN`, `SCALE_MAX` | 30.0, 90.0 | шкала графика, °C | | `SERIAL_BAUD` | 115200 | скорость обмена; менять вместе с агентом | | `STALE_MS` | 5000 | молчание, после которого показывается `NO DATA` | @@ -253,6 +254,26 @@ Start-ScheduledTask -TaskName 'CPU Temp Agent' пока температура топчется на границе. Поэтому включение и отпускание разведены: загорается на 85, гаснет только при спуске до 82. +### Почему цифра температуры усредняется + +Мгновенное значение плавает на ±1 °C вокруг стабильного, и цифра мельтешит +каждую секунду, хотя ничего не происходит — это отвлекает. На экран выводится +среднее за `TEMP_AVG_SAMPLES` последних замеров, то есть за 5 секунд: агент +шлёт раз в секунду. + +Плата за это — запаздывание реакции на реальный нагрев примерно на 2–3 секунды. +Для температуры процессора незаметно: она физически инертна. + +Что осталось несглаженным и почему: + +- **загрузка CPU** — её скачки настоящие, ради них на неё и смотрят; +- **график** — 12 пикселей на диапазон 60 °C дают 5 °C на пиксель, шум там и так + не виден, зато история остаётся сырой. + +Тревога считается по усреднённому значению, а не по сырому: иначе экран +показывал бы 84, когда мигание уже началось. Побочно это защищает от +срабатывания на одиночный выброс — перегрев должен продержаться пару секунд. + ### Почему шкала графика фиксированная Автомасштаб отвергнут сознательно: в простое колебания ±1 °C рисовались бы во diff --git a/CPU_temp/config.h b/CPU_temp/config.h index 236329d..7b1b4d3 100644 --- a/CPU_temp/config.h +++ b/CPU_temp/config.h @@ -37,6 +37,15 @@ static const uint8_t PIN_LED = 8; // высокий уровень и нужен. static const uint8_t LED_ACTIVE_LEVEL = LOW; +// --- Сглаживание показаний ------------------------------------------------- + +// Скользящее среднее температуры: цифра не должна мельтешить на +-1 градус, +// пока ничего не происходит. Единица - один принятый замер, а агент шлёт раз +// в секунду, поэтому 5 означает "среднее за последние 5 секунд". +// Загрузка сознательно не сглаживается: её скачки настоящие, ради них на неё +// и смотрят. +static const uint8_t TEMP_AVG_SAMPLES = 5; + // --- График ---------------------------------------------------------------- // Шкала фиксированная: при автомасштабе шум +-1 градус в простое