# Push this PC's CPU / GPU / RAM / disk stats to CYD over USB serial. # Usage: # .\send_sysinfo.ps1 # .\send_sysinfo.ps1 -Port COM5 # .\send_sysinfo.ps1 -Port COM5 -IntervalMs 1000 param( [string]$Port = "", [int]$IntervalMs = 1000, [int]$Baud = 460800 ) $ErrorActionPreference = "Stop" function Find-CydPort { $ports = @() try { Add-Type -AssemblyName System.IO.Ports -ErrorAction SilentlyContinue | Out-Null $ports = [System.IO.Ports.SerialPort]::GetPortNames() | Sort-Object } catch {} if (-not $ports -or $ports.Count -eq 0) { $ports = Get-CimInstance Win32_SerialPort -ErrorAction SilentlyContinue | Select-Object -ExpandProperty DeviceID | Sort-Object } if (-not $ports -or $ports.Count -eq 0) { throw "No serial ports found. Plug in the CYD USB cable." } if ($ports.Count -eq 1) { return $ports[0] } Write-Host "Available ports: $($ports -join ', ')" $pick = $ports[-1] Write-Host "Using last port: $pick (override with -Port)" return $pick } function Sanitize([string]$s, [int]$max = 40) { if (-not $s) { return "" } $t = ($s -replace '[|=\r\n]', ' ').Trim() if ($t.Length -gt $max) { $t = $t.Substring(0, $max).Trim() } return $t } function Get-CpuName { $p = Get-CimInstance Win32_Processor -ErrorAction SilentlyContinue | Select-Object -First 1 if (-not $p) { return "CPU" } $n = [string]$p.Name # Shorten common Intel/AMD noise $n = $n -replace '\(R\)|\(TM\)|CPU @.*|with Radeon.*', '' $n = ($n -replace '\s+', ' ').Trim() return Sanitize $n 44 } function Get-CpuPct { try { $c = Get-CimInstance Win32_Processor -ErrorAction Stop $vals = @($c | ForEach-Object { [double]$_.LoadPercentage }) if ($vals.Count -eq 0) { return 0.0 } return [math]::Round(($vals | Measure-Object -Average).Average, 1) } catch { return 0.0 } } function Get-GpuName { $gpus = Get-CimInstance Win32_VideoController -ErrorAction SilentlyContinue | Where-Object { $_.Name -and $_.Name -notmatch 'Microsoft Basic|Remote Desktop' } if (-not $gpus) { return "GPU" } # Prefer NVIDIA / AMD discrete if present $pick = $gpus | Where-Object { $_.Name -match 'NVIDIA|GeForce|RTX|GTX|Radeon|AMD' } | Select-Object -First 1 if (-not $pick) { $pick = $gpus | Select-Object -First 1 } $n = [string]$pick.Name $n = $n -replace 'NVIDIA GeForce ', 'RTX/GTX ' $n = $n -replace 'AMD ', '' return Sanitize $n 44 } function Get-GpuPct { # 1) nvidia-smi $smi = Get-Command nvidia-smi -ErrorAction SilentlyContinue if ($smi) { try { $out = & nvidia-smi --query-gpu=utilization.gpu --format=csv,noheader,nounits 2>$null if ($LASTEXITCODE -eq 0 -and $out) { $first = ([string]($out | Select-Object -First 1)).Trim() if ($first -match '^\d+') { return [double]$first } } } catch {} } # 2) Windows GPU Engine counter (may need a few samples) try { $counters = Get-Counter '\GPU Engine(*)\Utilization Percentage' -ErrorAction Stop $samples = $counters.CounterSamples | Where-Object { $_.CookedValue -ge 0 -and $_.InstanceName -match 'engtype_3D|engtype_Graphics' } if ($samples) { $sum = ($samples | Measure-Object -Property CookedValue -Sum).Sum if ($sum -gt 100) { $sum = 100 } return [math]::Round($sum, 1) } } catch {} return -1.0 } function Get-RamGb { $os = Get-CimInstance Win32_OperatingSystem -ErrorAction Stop $total = [math]::Round($os.TotalVisibleMemorySize / 1MB, 1) $free = [math]::Round($os.FreePhysicalMemory / 1MB, 1) $used = [math]::Round($total - $free, 1) return @{ Used = $used; Total = $total } } function Get-DiskGb { $d = Get-CimInstance Win32_LogicalDisk -Filter "DeviceID='C:'" -ErrorAction SilentlyContinue if (-not $d) { $d = Get-CimInstance Win32_LogicalDisk -ErrorAction SilentlyContinue | Where-Object { $_.DriveType -eq 3 -and $_.Size -gt 0 } | Select-Object -First 1 } if (-not $d -or -not $d.Size) { return @{ Used = 0.0; Total = 0.0 } } $total = [math]::Round($d.Size / 1GB, 1) $free = [math]::Round($d.FreeSpace / 1GB, 1) $used = [math]::Round($total - $free, 1) return @{ Used = $used; Total = $total } } function Build-Line { $hostName = Sanitize $env:COMPUTERNAME 36 $cpu = Get-CpuName $cpuPct = Get-CpuPct $gpu = Get-GpuName $gpuPct = Get-GpuPct $ram = Get-RamGb $disk = Get-DiskGb $gpuField = if ($gpuPct -lt 0) { "gpu%=-1" } else { "gpu%=$gpuPct" } return "SI|host=$hostName|cpu=$cpu|cpu%=$cpuPct|gpu=$gpu|$gpuField|ram=$($ram.Used)/$($ram.Total)|disk=$($disk.Used)/$($disk.Total)" } if (-not $Port) { $Port = Find-CydPort } Add-Type -AssemblyName System.IO.Ports $sp = [System.IO.Ports.SerialPort]::new($Port, $Baud) $sp.ReadTimeout = 200 $sp.WriteTimeout = 2000 $sp.NewLine = "`n" $sp.DtrEnable = $true $sp.RtsEnable = $true try { $sp.Open() Write-Host "Sending host info -> $Port @ $Baud every ${IntervalMs}ms (Ctrl+C to stop)" Start-Sleep -Milliseconds 400 while ($true) { $line = Build-Line $sp.WriteLine($line) Write-Host $line Start-Sleep -Milliseconds $IntervalMs } } finally { if ($sp.IsOpen) { $sp.Close() } $sp.Dispose() }