Первый пункт - другие показатели (GPU, память, диски) и листалка экранов кнопкой BOOT. Протокол к этому уже готов: незнакомые ключи прошивка игнорирует, так что агент может начать слать новые поля, не ломая уже прошитые платы. Там же записаны открытые хвосты: обрезание строки при 100 градусах и 100% загрузки, непроверенный перезагрузкой автозапуск, ручной цикл заливки и вес обзорного изображения. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
350 lines
18 KiB
Markdown
350 lines
18 KiB
Markdown
# 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
|
||
доступна только через драйвер уровня ядра.
|
||
|
||

|
||
|
||
*Две поправки к изображению: экран на этой плате 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% её оперативной памяти.
|