<# .SYNOPSIS Читает температуру и загрузку CPU и раз в секунду отдаёт их плате ESP32-C3 по USB-COM в виде строки "T=67.0;L=34". .NOTES Требует прав администратора: без них драйвер LibreHardwareMonitor не загружается, и температуры приходят пустыми (загрузка при этом читается — отсюда ложное ощущение, что всё работает). #> #Requires -RunAsAdministrator [CmdletBinding()] param( # Печатать каждую отправленную строку в консоль (для отладки). [switch]$Echo ) Set-StrictMode -Version Latest $ErrorActionPreference = 'Stop' $LhmPath = 'D:\Soft\LibreHardwareMonitor\LibreHardwareMonitorLib.dll' if (-not (Test-Path $LhmPath)) { throw "Не найден $LhmPath. Скачайте LibreHardwareMonitor (архив LibreHardwareMonitor.zip, не .NET.10) и распакуйте в D:\Soft\LibreHardwareMonitor" } Add-Type -Path $LhmPath $computer = New-Object LibreHardwareMonitor.Hardware.Computer $computer.IsCpuEnabled = $true $computer.Open() function Get-CpuStats { foreach ($hw in $computer.Hardware) { $hw.Update() } $temp = $null $load = $null foreach ($hw in $computer.Hardware) { foreach ($s in $hw.Sensors) { if ($s.SensorType -eq 'Temperature' -and $s.Name -eq 'CPU Package') { $temp = $s.Value } if ($s.SensorType -eq 'Load' -and $s.Name -eq 'CPU Total') { $load = $s.Value } } } # Возвращаем обе величины даже неполными: вызывающий код должен уметь # сказать, какая именно отсутствует, а не валить всё в одно "нет данных". return @{ Temp = $temp Load = $load } } # Температура читается только через драйвер ядра. У LibreHardwareMonitor 0.9.5+ # это PawnIO, который ставится отдельным установщиком и в архив с программой не # входит. Без него загрузка читается (она из счётчиков Windows), а температура — # нет. Права администратора этого не заменяют. function Assert-SensorsAvailable { $probe = Get-CpuStats if ($null -eq $probe.Temp) { $hasPawnIo = (Get-CimInstance Win32_SystemDriver | Where-Object { $_.Name -match 'PawnIO' }) -ne $null Write-Host 'Температура CPU недоступна.' -ForegroundColor Red if (-not $hasPawnIo) { Write-Host 'Причина: не установлен драйвер PawnIO (https://pawnio.eu).' -ForegroundColor Red } else { Write-Host 'Драйвер PawnIO найден, но значение пустое — запущено без прав администратора?' -ForegroundColor Red } throw 'Нет источника температуры' } if ($null -eq $probe.Load) { throw 'Недоступна загрузка CPU (сенсор "CPU Total" не найден)' } } # Форматирование только через InvariantCulture: в русской локали вышло бы # "T=67,0", и atof на плате прочитал бы 67, молча потеряв дробную часть. function Format-StatsLine($stats) { return [string]::Format([cultureinfo]::InvariantCulture, 'T={0:F1};L={1}', [double]$stats.Temp, [int][math]::Round([double]$stats.Load)) } # Номер порта не хардкодим: он меняется при перетыкании в другой разъём. function Find-BoardPort { Get-CimInstance Win32_PnPEntity | Where-Object { $_.DeviceID -like 'USB\VID_303A&PID_1001*' -and $_.Name -match '\(COM\d+\)' } | ForEach-Object { if ($_.Name -match '\((COM\d+)\)') { $Matches[1] } } | Select-Object -First 1 } function Open-BoardPort { $name = Find-BoardPort if (-not $name) { return $null } $sp = New-Object System.IO.Ports.SerialPort $name, 115200, 'None', 8, 'One' # RTS явно выключен: одновременное дёрганье DTR и RTS переводит плату в загрузчик. $sp.DtrEnable = $true $sp.RtsEnable = $false $sp.WriteTimeout = 1000 $sp.NewLine = "`n" $sp.Open() Write-Host "connected: $name" return $sp } # Падаем сразу и с внятной причиной, а не крутим бесконечный цикл молча. Assert-SensorsAvailable $port = $null while ($true) { try { if ($null -eq $port -or -not $port.IsOpen) { if ($null -ne $port) { $port.Dispose(); $port = $null } $port = Open-BoardPort if ($null -eq $port) { Start-Sleep -Seconds 2; continue } } $stats = Get-CpuStats if ($null -ne $stats.Temp -and $null -ne $stats.Load) { $line = Format-StatsLine $stats $port.WriteLine($line) if ($Echo) { Write-Host $line } } elseif ($Echo) { Write-Host "пропуск: Temp=$($stats.Temp) Load=$($stats.Load)" } Start-Sleep -Seconds 1 } catch { # Порт исчез: перезагрузка платы, перезаливка прошивки, выдёргивание кабеля. Write-Host "port lost: $($_.Exception.Message)" if ($null -ne $port) { try { $port.Dispose() } catch {} ; $port = $null } Start-Sleep -Seconds 2 } }