feat(cpu_temp): индикатор температуры CPU на ESP32-C3 с OLED

Плата принимает по USB-CDC строки "T=67.0;L=34" от агента на ПК и рисует
крупную цифру, загрузку и график за последнюю минуту на экране 72x40.

Пины дисплея (SDA=5, SCL=6) определены сканером, а не по шелкографии:
подписи GPIO8/GPIO9 на плате - это дефолтные Wire-пины чипа.

Агент читает сенсоры через LibreHardwareMonitorLib. С версии 0.9.5 она
требует отдельно установленный драйвер PawnIO: без него загрузка CPU
читается, а температуры пусты даже под администратором.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
oskarvitaliiandClaude Opus 5 committed 2026-09-14 21:42:24 +07:00
1 parent ed4dad0a52
commit 59848698ff
12 files changed
+1473

No files matched your search

+60
View File
@@ -0,0 +1,60 @@
#include "link.h"
#include <Arduino.h>
static const size_t BUF_SIZE = 64;
static char buf[BUF_SIZE];
static size_t len = 0;
void linkBegin() {
Serial.begin(115200);
len = 0;
}
static bool parseLine(char *line, CpuStats &out) {
bool haveTemp = false;
bool haveLoad = false;
CpuStats parsed = {0.0f, 0};
char *save = nullptr;
for (char *tok = strtok_r(line, ";", &save); tok; tok = strtok_r(nullptr, ";", &save)) {
char *eq = strchr(tok, '=');
if (!eq) continue;
*eq = '\0';
const char *key = tok;
const char *val = eq + 1;
if (strcmp(key, "T") == 0) {
parsed.tempC = atof(val);
haveTemp = true;
} else if (strcmp(key, "L") == 0) {
parsed.loadPct = atoi(val);
haveLoad = true;
}
// прочие ключи игнорируются намеренно: протокол расширяемый
}
if (!haveTemp || !haveLoad) return false;
out = parsed;
return true;
}
bool linkPoll(CpuStats &out) {
bool got = false;
while (Serial.available()) {
const char c = (char)Serial.read();
if (c == '\r') continue;
if (c == '\n') {
buf[len] = '\0';
if (len > 0 && parseLine(buf, out)) got = true;
len = 0;
continue;
}
if (len < BUF_SIZE - 1) {
buf[len++] = c;
} else {
len = 0; // строка длиннее буфера — мусор, начинаем заново
}
}
return got;
}