Files
Arduino/CPU_temp/README.md
T
oskarvitaliiandClaude Opus 5 1047ae1452 docs: завести бэклог проекта
Первый пункт - другие показатели (GPU, память, диски) и листалка экранов
кнопкой BOOT. Протокол к этому уже готов: незнакомые ключи прошивка
игнорирует, так что агент может начать слать новые поля, не ломая уже
прошитые платы.

Там же записаны открытые хвосты: обрезание строки при 100 градусах и
100% загрузки, непроверенный перезагрузкой автозапуск, ручной цикл
заливки и вес обзорного изображения.

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

350 lines
18 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# CPU Temp — индикатор температуры процессора на ESP32-C3
Небольшая плата ESP32-C3 со встроенным OLED-экраном 0.42″ стоит на столе и
показывает температуру процессора компьютера: крупная цифра, загрузка CPU рядом
и график за последнюю минуту. При перегреве экран мигает негативом.
```
LibreHardwareMonitorLib.dll ──> PowerShell-агент ──COM (USB CDC)──> ESP32-C3 ──I2C──> OLED 72×40
(через драйвер PawnIO) раз в секунду "T=67.0;L=34" U8g2
```
**Плата ничего не измеряет сама.** Она принимает готовые строки по USB и рисует
их. Всё чтение датчиков происходит на компьютере — иначе никак: температура CPU
доступна только через драйвер уровня ядра.
![Обзор проекта](docs/promo.png)
*Две поправки к изображению: экран на этой плате 0.42″ (72×40), а не 0.96″;
график строится по температуре, а не по загрузке — загрузка выводится цифрой.*
Подключение — по USB-кабелю, которым плата и так питается. Wi-Fi не
используется: настраивать нечего, задержки нет.
---
## Что понадобится
| | Что | Примечание |
|---|---|---|
| Железо | Плата ESP32-C3 со встроенным OLED 0.42″ (72×40) | «фиолетовая» плата с разъёмом Type-C |
| | Кабель USB Type-C | он же питание, он же канал данных |
| ПО на ПК | Windows | агент написан под Windows PowerShell 5.1 |
| | Arduino IDE 2.x | сборка и заливка прошивки |
| | LibreHardwareMonitor | чтение датчиков |
| | PawnIO | драйвер, без него температуры не будет |
Всё программное обеспечение бесплатное.
---
## Часть 1. Прошивка платы
### 1.1 Arduino IDE
Скачать с <https://www.arduino.cc/en/software> и установить. Проект собран на
версии 2.3.10.
### 1.2 Ядро ESP32
**Инструменты → Плата → Менеджер плат**, найти **esp32** (Espressif Systems),
установить. Проект собран на версии 3.3.8.
Добавлять URL в настройках IDE не нужно: пакет есть в штатном индексе Arduino.
### 1.3 Библиотека U8g2
**Инструменты → Управлять библиотеками**, найти **U8g2** (автор oliver),
установить. Проект собран на версии 2.36.19.
Библиотека поддерживает экран 72×40 готовым драйвером. Это важнее, чем кажется:
у такого экрана ненулевое смещение по обеим осям, и вручную его подбирать
пришлось бы вслепую.
### 1.4 Настройки платы
Подключить плату кабелем. В меню **Инструменты** выставить:
| Пункт меню | Значение |
|---|---|
| Плата | **ESP32C3 Dev Module** |
| **USB CDC On Boot** | **Enabled** ← обязательно |
| Flash Size | **4MB (32Mb)** |
| Partition Scheme | Default 4MB with spiffs |
| Upload Speed | 921600 |
| Порт | тот, что появился после подключения платы |
Два пункта критичны:
- **USB CDC On Boot = Enabled.** По умолчанию стоит `Disabled`, и тогда `Serial`
уходит в аппаратный UART вместо USB. Прошивка соберётся и зальётся, но данные
до платы доходить не будут, а на экране навсегда останется `NO DATA`.
- **Flash Size должен совпадать с физическим объёмом флеша.** У этой платы 4 МБ.
Посмотреть фактический объём можно через **Инструменты → Get Board Info** или
в выводе утилиты прошивки. При несовпадении разделы уезжают за границу
микросхемы.
Если номер COM-порта неочевиден: **Диспетчер устройств → Порты (COM и LPT)**,
плата видна как устройство с идентификатором `VID_303A&PID_1001` (Espressif).
### 1.5 Определить пины дисплея
На **этой** плате экран подключён к `SDA = GPIO5`, `SCL = GPIO6`, адрес `0x3C` —
уже прописано в `config.h`, шаг можно пропустить.
Если плата похожая, но другая, не доверяйте надписям на текстолите: подписи
`GPIO8/GPIO9` рядом с пометкой I2C относятся к стандартным пинам чипа, а не к
разводке встроенного экрана. Залейте `tools/i2c_scan/i2c_scan.ino` и откройте
монитор порта на скорости 115200. Сканер перебирает пары пинов и печатает
найденное:
```
--- I2C scan ---
SDA=5 SCL=6: device 0x3C
SDA=6 SCL=5: nothing
SDA=8 SCL=9: nothing
SDA=9 SCL=8: nothing
```
Найденную пару вписать в `config.h` (`PIN_SDA`, `PIN_SCL`).
### 1.6 Залить прошивку
Открыть `CPU_temp.ino` в Arduino IDE и нажать **Загрузить**.
Сразу после заливки на экране появится `NO DATA` — так и должно быть: агента на
компьютере ещё нет.
То же самое из командной строки:
```
arduino-cli compile -b esp32:esp32:esp32c3:CDCOnBoot=cdc,FlashSize=4M,PartitionScheme=default .
arduino-cli upload -p COM4 -b esp32:esp32:esp32c3:CDCOnBoot=cdc,FlashSize=4M,PartitionScheme=default .
```
Вместо `COM4` подставить свой порт.
---
## Часть 2. Агент на компьютере
Агент — скрипт `agent/cpu_temp_agent.ps1`. Он читает датчики и раз в секунду
шлёт плате строку.
### 2.1 LibreHardwareMonitor
Скачать со страницы релизов
<https://github.com/LibreHardwareMonitor/LibreHardwareMonitor/releases> архив
**`LibreHardwareMonitor.zip`** и распаковать в `D:\Soft\LibreHardwareMonitor`.
- Путь можно выбрать другой, но тогда поправьте переменную `$LhmPath` в начале
скрипта.
- **Не берите `LibreHardwareMonitor.NET.10.zip`** — это сборка под .NET 10, а
Windows PowerShell 5.1 работает на .NET Framework и такую сборку не загрузит.
- Запускать саму программу не нужно. Используются только библиотека
`LibreHardwareMonitorLib.dll` и лежащий рядом `HidSharp.dll`.
### 2.2 Драйвер PawnIO
Скачать со страницы <https://github.com/namazso/PawnIO.Setup/releases> файл
`PawnIO_setup.exe` и запустить **без аргументов**, двойным щелчком, затем пройти
мастер. Потребуются права администратора.
Установщик не принимает ключей командной строки: на `/S` или `/quiet` он
отвечает кодом 87 и молча ничего не делает.
**Зачем это нужно.** Начиная с версии 0.9.5 LibreHardwareMonitor читает регистры
процессора через PawnIO, а не через прежний WinRing0, и в архив с программой
драйвер не входит. Без него **загрузка CPU читается, а все температуры пустые** —
даже из консоли администратора. Симптом сбивает с толку: выглядит как нехватка
прав, хотя права ни при чём.
### 2.3 Проверить вручную
Открыть PowerShell **от имени администратора** (правый щелчок по значку →
«Запуск от имени администратора») и выполнить:
```
powershell -ExecutionPolicy Bypass -File "D:\Soft\Arduino\CPU_temp\agent\cpu_temp_agent.ps1" -Echo
```
Ожидается поток строк раз в секунду:
```
connected: COM4
T=47.0;L=24
T=48.0;L=14
```
Одновременно на экране платы появится температура. Остановить — Ctrl+C.
Права администратора нужны всегда: без них драйвер не открывается. Агент
проверяет это при старте и называет причину, а не молчит.
### 2.4 Автозапуск при входе в систему
В консоли администратора:
```
$action = New-ScheduledTaskAction -Execute 'powershell.exe' -Argument '-NoProfile -WindowStyle Hidden -ExecutionPolicy Bypass -File "D:\Soft\Arduino\CPU_temp\agent\cpu_temp_agent.ps1"'
$trigger = New-ScheduledTaskTrigger -AtLogOn -User "$env:USERDOMAIN\$env:USERNAME"
$set = New-ScheduledTaskSettingsSet -AllowStartIfOnBatteries -DontStopIfGoingOnBatteries -ExecutionTimeLimit ([TimeSpan]::Zero)
Register-ScheduledTask -TaskName 'CPU Temp Agent' -Action $action -Trigger $trigger -Settings $set -RunLevel Highest -Force
```
- `-RunLevel Highest` обязателен — это и есть «выполнить с наивысшими правами».
- Нулевой `ExecutionTimeLimit` нужен, иначе Планировщик убьёт агента через трое
суток.
Проверить, не перезагружаясь:
```
Start-ScheduledTask -TaskName 'CPU Temp Agent'
(Get-ScheduledTask -TaskName 'CPU Temp Agent').State
```
Ожидается `Running`.
Удалить задание:
```
Unregister-ScheduledTask -TaskName 'CPU Temp Agent' -Confirm:$false
```
---
## Повторная заливка прошивки
Плата прошивается через тот же COM-порт, по которому идут данные. Пока агент
держит порт, заливка падает с `PermissionError`. Порядок такой:
```
Stop-ScheduledTask -TaskName 'CPU Temp Agent'
# ... залить прошивку ...
Start-ScheduledTask -TaskName 'CPU Temp Agent'
```
Порт может держать и монитор порта Arduino IDE — его тоже надо закрыть.
---
## Настройка
Всё, что имеет смысл менять, собрано в `config.h`:
| Константа | По умолчанию | Смысл |
|---|---|---|
| `PIN_SDA`, `PIN_SCL` | 5, 6 | пины экрана |
| `DISPLAY_ROTATION` | `U8G2_R2` | ориентация; `U8G2_R0` — повернуть обратно |
| `ALARM_ON_C` | 85.0 | температура включения тревоги |
| `ALARM_OFF_C` | 82.0 | температура отпускания тревоги |
| `ALARM_BLINK_MS` | 500 | полупериод мигания |
| `PIN_LED` | 8 | светодиод |
| `LED_ACTIVE_LEVEL` | `LOW` | уровень, при котором светодиод горит |
| `SCALE_MIN`, `SCALE_MAX` | 30.0, 90.0 | шкала графика, °C |
| `SERIAL_BAUD` | 115200 | скорость обмена; менять вместе с агентом |
| `STALE_MS` | 5000 | молчание, после которого показывается `NO DATA` |
| `SAMPLE_MS` | 1000 | шаг истории графика |
| `DRAW_MS` | 200 | период перерисовки экрана |
### Почему порогов тревоги два, а не один
С одним порогом тревога дёргалась бы включениями и выключениями каждую секунду,
пока температура топчется на границе. Поэтому включение и отпускание разведены:
загорается на 85, гаснет только при спуске до 82.
### Почему шкала графика фиксированная
Автомасштаб отвергнут сознательно: в простое колебания ±1 °C рисовались бы во
всю высоту экрана и выглядели тревожно, хотя ничего не происходит.
---
## Что показывает экран
| Состояние | Экран | Светодиод GPIO8 |
|---|---|---|
| Норма | температура, загрузка, график | погашен |
| Перегрев | то же, мигает негативом | мигает синхронно с экраном |
| Нет данных дольше 5 с | `NO DATA` | горит ровно |
В режиме `NO DATA` история графика не пополняется — иначе обрыв связи выглядел бы
как резкое падение температуры.
---
## Протокол обмена
Агент шлёт плате по одной строке в секунду, в конце — перевод строки:
```
T=67.0;L=34
```
| Ключ | Смысл |
|---|---|
| `T` | температура CPU Package, °C |
| `L` | общая загрузка CPU, % |
Неизвестные ключи прошивка молча игнорирует, поэтому протокол расширяем: можно
добавить, например, `G=` для видеокарты, не трогая уже прошитые платы.
**Разделитель дробной части — только точка.** Агент форматирует числа через
`InvariantCulture`: в русской локали иначе вышло бы `T=67,0`, и разбор на плате
прочитал бы `67`, молча потеряв дробную часть.
Плату можно проверить без агента, отправив строки вручную. Порт при этом должен
быть свободен, то есть агента надо остановить:
```
$sp = New-Object System.IO.Ports.SerialPort 'COM4',115200,'None',8,'One'
$sp.DtrEnable = $true; $sp.RtsEnable = $false
$sp.Open()
40..90 | ForEach-Object { $sp.WriteLine("T=$_.0;L=50"); Start-Sleep -Milliseconds 400 }
$sp.Close()
```
Так же удобно проверять тревогу, не меняя пороги в `config.h`: подать значение
выше 85, затем 83 (тревога должна сохраниться) и 81 (должна отпустить).
---
## Если не работает
| Симптом | Причина | Что делать |
|---|---|---|
| На экране всегда `NO DATA`, хотя агент шлёт строки | `USB CDC On Boot` выключен, `Serial` ушёл в аппаратный UART | включить в меню платы и перезалить |
| Экран не светится вовсе | неверные пины I2C | залить `tools/i2c_scan`, найти пару, вписать в `config.h` |
| Агент пишет, что температура недоступна | не установлен PawnIO | установить драйвер, см. 2.2 |
| Агент отказывается запускаться из-за прав | консоль не от администратора | открыть «от имени администратора» |
| Заливка падает с `PermissionError` | порт держит агент или монитор порта IDE | `Stop-ScheduledTask`, закрыть монитор |
| Температура без дробной части | число пришло с запятой вместо точки | агент должен форматировать через `InvariantCulture` |
| Скрипт `.ps1` падает с ошибками про скобки | файл сохранён в UTF-8 **без BOM** | пересохранить в UTF-8 **с BOM**: PowerShell 5.1 иначе читает файл как ANSI и портит кириллицу |
| После перетыкания в другой разъём данные не идут | сменился номер порта | ничего не делать: агент ищет плату по `VID_303A&PID_1001` |
---
## Как устроено
| Путь | Что это |
|---|---|
| `config.h` | все настройки |
| `CPU_temp.ino` | свежесть данных, гистерезис тревоги, светодиод |
| `link.h` / `link.cpp` | неблокирующее чтение строки из Serial и её разбор |
| `ui.h` / `ui.cpp` | отрисовка через U8g2, кольцевой буфер истории, негатив |
| `tools/i2c_scan/` | одноразовый скетч поиска пинов дисплея |
| `agent/cpu_temp_agent.ps1` | агент для ПК |
| `agent/README.md` | подробности по агенту |
| `docs/` | спецификация, план реализации и [бэклог](docs/BACKLOG.md) |
Разделение простое: `link` ничего не знает об экране, `ui` не знает, откуда
взялись данные, а логика состояний живёт в скетче.
## Расход ресурсов
Агент занимает около 70 МБ оперативной памяти и со временем не растёт. Почти всё
это — обвязка Windows PowerShell и .NET Framework; сам цикл держит в памяти
несколько чисел.
Прошивка занимает 26% флеша платы и 4% её оперативной памяти.