Why test with PowerShell
Before you start writing Python code or configuring a library — it's worth checking whether the COM port works at all and whether the device responds. PowerShell has a built-in System.IO.Ports.SerialPort class that gives direct access to the serial port with no additional installs.
Useful when:
- You're not sure which COM port is your device
- You want a quick "is this device alive?" check before writing code
- You're debugging a communication problem and want to rule out the software layer
List available COM ports
# All COM ports on the system
[System.IO.Ports.SerialPort]::GetPortNames()
# With more detail — including the device description
Get-WMIObject Win32_PnPEntity |
Where-Object { $_.Name -match "COM\d+" } |
Select-Object Name, DeviceID
The second method shows device names (e.g. "USB-SERIAL CH340 (COM8)"), which makes it easier to identify the right port.
Opening the port and sending data
$port = New-Object System.IO.Ports.SerialPort
$port.PortName = "COM8"
$port.BaudRate = 9600
$port.DataBits = 8
$port.Parity = [System.IO.Ports.Parity]::None
$port.StopBits = [System.IO.Ports.StopBits]::One
$port.ReadTimeout = 1000 # ms
$port.WriteTimeout = 1000 # ms
$port.Open()
Sending raw bytes (Modbus/Flowbus)
For binary protocols use Write() with a byte array, not WriteLine():
# Example: Modbus RTU frame — read register 0, slave 1
# 01 03 00 00 00 01 84 0A
$request = [byte[]](0x01, 0x03, 0x00, 0x00, 0x00, 0x01, 0x84, 0x0A)
$port.Write($request, 0, $request.Length)
Start-Sleep -Milliseconds 200
# Read response
$buffer = New-Object byte[] 256
$bytesRead = $port.Read($buffer, 0, 256)
$response = $buffer[0..($bytesRead - 1)]
# Display as hex
$hex = ($response | ForEach-Object { $_.ToString("X2") }) -join " "
Write-Host "Response: $hex"
Scanning for Modbus devices on the bus
If you don't know which slave address the device uses:
function Get-ModbusCRC {
param([byte[]]$data)
$crc = 0xFFFF
foreach ($b in $data) {
$crc = $crc -bxor $b
for ($i = 0; $i -lt 8; $i++) {
if ($crc -band 0x0001) {
$crc = ($crc -shr 1) -bxor 0xA001
} else {
$crc = $crc -shr 1
}
}
}
return [byte[]]([byte]($crc -band 0xFF), [byte](($crc -shr 8) -band 0xFF))
}
# Scan addresses 1–10
for ($addr = 1; $addr -le 10; $addr++) {
$pdu = [byte[]]($addr, 0x03, 0x00, 0x00, 0x00, 0x01)
$crc = Get-ModbusCRC $pdu
$frame = $pdu + $crc
$port.Write($frame, 0, $frame.Length)
Start-Sleep -Milliseconds 100
try {
$buf = New-Object byte[] 64
$n = $port.Read($buf, 0, 64)
if ($n -gt 0) {
Write-Host "Found device at address $addr"
}
} catch {
# timeout — no device at this address
}
}
ASCII test (for text-based devices)
Some devices (older scales, terminals) use ASCII. In that case you can use ReadLine():
$port.NewLine = "`r`n" # or "`r" depending on the device
$port.WriteLine("?") # ASCII query
Start-Sleep -Milliseconds 500
$response = $port.ReadExisting()
Write-Host "Response: $response"
Closing the port
Always close the port when you're done:
$port.Close()
$port.Dispose()
An unclosed port stays held by the PowerShell process — other programs (Python, LOGO!, Modbus Poll) won't be able to open it until you close the session or release the resource.
Pitfalls
-
Port busy — if
$port.Open()throws "Access is denied", the port is open in another process. Check Device Manager and other running applications. -
ReadTimeout — without a ReadTimeout set,
Read()can wait forever. Always set a timeout before opening the port. -
Baud rate — Windows lets you open the port at any baud rate, but the device won't respond if you set the wrong value. No response ≠ no device.
-
Echo — some USB-RS485 converters echo your own bytes back. In the response you'll see your request + the device's reply. Discard the first N bytes (N = request length).