I have now looked all over the internet and now tried countless scripts and solutions. That said, it seem most solutions is about one-way reading, and rarely writing and reading combined.
I have put together a powershell script, based on all these bits & pieces. It works by running the following pwsh.exe command that fires up another pwsh shell (latest 7.3.4) and running the script from there.
Start-Process pwsh.exe -ArgumentList '-noprofile -noexit -c "./mecom.ps1"' -PassThru | Out-Null
Connected Device
The connected device in question is a USB based 3G/4G LTE data modem.
When connected to the PS it reports the following 3 COM ports in the Ports PNPClass.
Alcatel 3G modem debug (COM11) - modem
Application1 Interface (COM10) - diag
Application2 Interface (COM9) - AT_MBIM
These 3 corresponds to the modem+diag+at_mbim USB mode, found with AT+USBMODE?.
So here I can use both COM 9 & 11.
Problem Statement:
- The script successfully sends the data to the serial COM port.
- But I am not able to read the response, even if I know it's there.
- I know it's there, because if I kill the script after it has sent the at command (
ATI), or just let it time out. I can see the data and results sent, if I connect to the same port using any common terminal emulator program, such as putty, screen, picocom, etc. (See screenshot.) - Very occasionally I get a few
OKs shown, but seem random at the moment.
The Screenshot
Buffer data with results from commands sent from script, but not received by script.
The Script
#!/usr/bin/env pwsh
#
#------------------------------------------------------------------------------
# Run with
# Start-Process pwsh.exe -ArgumentList '-noprofile -noexit -c "./mecom.ps1"' -PassThru | Out-Null
#------------------------------------------------------------------------------
$HL = '-'*50
# Register Exit event & Hit [CTRL+C] to exit shell
Register-EngineEvent PowerShell.Exiting -SupportEvent –Action {
[console]::Beep(500,500); Write-Host "`nExiting...`n" -fore Magenta; sleep(1); Read-Host -Prompt "Hit Return 2 Exit";
}
# Hit [CTRL+D] to exit shell, after script is done.
try { Set-PSReadlineKeyHandler -Key ctrl+d -Function ViExit } catch {}
#------------------------------------------------------------------------------
# Helper Functions
#------------------------------------------------------------------------------
function setTerminalUI {
# Setting the PowerShell UI Terminal Size
# NOTE: Must have: BufferWidth = WindowWidth
[console]::Title = "MeCom Terminal (${PID})"
[console]::BufferHeight=9001
[console]::BufferWidth=140
[console]::WindowHeight=50
[console]::WindowWidth=140
}
function startUp {
Write-Host "`n"
Write-Host -Fo DarkGray "Starting " -NoN
Write-Host -Fo Magenta "MeCom Terminal"
Write-Host -Fo DarkGray "PID: $PID"
Write-Host -Fo DarkGray "Use [CTRL+C] to quit."
}
function showPort($port){
Write-Host -Fo DarkGray $HL
$port | Out-Host
Write-Host -Fo DarkGray $HL
}
function showPortInfo {
# Extract the COM port number into a list
$cports = [System.IO.Ports.SerialPort]::GetPortNames() # is a string array .GetType()
$comList = @()
foreach ($i in $cports) {
$i -Match "COM(\d{1,2})" | Out-Null
$comList += [int]$Matches[1]
}
Write-Host "`nAvailable Ports:"
Write-Host $HL
Write-Host -Fo DarkYellow "$cports"
Write-Host $HL
$mydevs = (Get-PnPDevice | Where-Object{$_.PNPClass -in "WPD","AndroidUsbDeviceClass","Modem","Ports" } |
Where-Object{$_.Present -in "True"} |
Select-Object Name,Description,Manufacturer,PNPClass,Service,Present,Status,DeviceID |
Sort-Object Name)
#$allDevs = (
$mydevs | Format-Table Description, Manufacturer, PNPClass, Service,
@{Label="COM port"; Expression={ ($_.Name -Match "\((COM\d{1,2})\)" | Out-Null && $Matches[1]) }},
@{Label="VID:PID"; Expression={ ($_.DeviceID -Match "USB\\VID_([0-9a-fA-F]{4})\&PID_([0-9a-fA-F]{4})" | Out-Null && ('{0}{1}{2}' -f ${Matches}[1], ":", ${Matches}[2]).ToLower() ) }},
Present, Status
# )
#Write-Host $allDevs
return $comList #| Out-Null -PassThru
}
function getComPort ($cList) {
# Select a COM port
Write-Host $HL; Write-Host -Fo DarkGray "comList:`n ${cList}" ; Write-Host $HL
do {
Write-Host -Fo DarkGreen 'Select a serial COM port number [default is 9]' -NoN
$pNum = Read-Host -Prompt ' '
if ( !($pNum -in $cList)) { Write-Host -Fo Red "ERROR: No COM port with that number!" }
} while ( !($pNum -match '^\d+$') -or ($pNum -notin $cList))
$cNum = "COM${pNum}" #| Out-Null
Write-Host -Fo DarkGray "`nReading from port : " -NoN
Write-Host -Fo White "${cNum}"
return $cNum
}
function ReadCom ($cNum) {
#------------------------------------------------------------
# Configuring the Serial Ports Connection
#------------------------------------------------------------
if (!$cNum) {Write-Host -Fo Red "[WARNING] No cNum received, setting to default COM9."; $cNum ='COM9'}
$port = New-Object System.IO.Ports.SerialPort "${cNum}",115200,None,8,one
#------------------------------------------------------------
$port.ReadTimeout = 10000 # 20 sec -
$port.WriteTimeout = 2000 # 5 sec -
$port.NewLine = "`r" # \r -
$port.ReceivedBytesThreshold = 1 # 256
#------------------------------------------------------------
Start-Sleep -M 500
Write-Host -Fo DarkGray "Opening connection..."
try {
$port.Open()
} catch {
Write-Host -Fo Red "`nERROR: Failed to Open serial port!"
Write-Error "ERROR: ${Error}"
Write-Host -Fo Yellow "`nQuitting!`n"
Read-Host -Prompt "Hit any key to Exit"
Break
}
Start-Sleep -m 1000
showPort($port)
#------------------------------------------------------------
# Housekeeping (removing garbage from previous sessions...
#------------------------------------------------------------
#$port.DiscardInBuffer()
#$port.DiscardOutBuffer()
#------------------------------------------------------------
# AT Command(s) to send
#------------------------------------------------------------
# NOTE:
# Sending AT commands require them to use an "\r" as EOL.
# This can be done automatically if using $port.NewLine="`n"
# You probably must use double quotes, otherwise you get the wrong character.
#------------------------------------------------------------
$ATC = 'ATI'
Write-Host -Fo DarkGray "Sending AT Command : " -NoN
Write-Host -Fo White "$ATC"
$port.WriteLine($ATC)
Start-Sleep -m 1000
showPort($port)
Write-Host -Fo Gray "Attempting to use ReadLine..."
do {
$key = if ($host.UI.RawUI.KeyAvailable) { $host.UI.RawUI.ReadKey('NoEcho, IncludeKeyDown') }
if ($port.IsOpen) {
try {
$data = $port.ReadLine()
#$data = $port.ReadExisting()
}
catch [TimeoutException] {
Write-Host -Fo Red "`nERROR: TimeoutException in ReadLine"
Write-Error "ERROR: ${Error}"
break
}
if (($data).Lengths -gt 0) {
Write-Host -Fo DarkYellow "${data}`n" # -NoN | Out-Host
}
Start-Sleep -m 1000
} else {
Write-Host -Fo Yellow "[INFO] Port was Closed!"
break
}
} until ($key.VirtualKeyCode -eq 81) # Repeat until a 'q' is pressed
Write-Host -Fo DarkGray "`nClosing connection..." -NoN
$port.Close()
Write-Host -Fo Green "OK`n"
}
#------------------------------------------------------------------------------
# MAIN
#------------------------------------------------------------------------------
setTerminalUI
startUp
$comList = showPortInfo
$comList
$cNum = getComPort($comList)
ReadCom($cNum)
Read-Host -Prompt "Hit any key to Exit"
#------------------------------------------------------------------------------
# END
#------------------------------------------------------------------------------
References:
1 system.io.ports.serialport
2 Writing and Reading info from Serial Ports
[3] Serial Port communication using PowerShell
[4] Reading Serial port data continuously in powershell script
[5] How to continuously read Serial COM port in
[6] SerialPort.DataReceived Event
[7] Problems writing AT command to internal modem with System.IO.Ports.SerialPort
[8] System.IO.Ports Namespace
[9] unlock-a-pinlocked-broadband-device-using-powershell-and-atcommands
Given the lack of working public solutions for this, I am starting to think it may be an issue with powershell itself!? Some people are loosely talking about making an Event Listener, but they always fail to provide any working powershell examples, and always refer back to the same crummy Microsoft C#/.NET web pages on the same topic. [1,2]
How can I fix the script to ensure to get all the results from the commands I send?

if (<some string>)is always true, unless it's Null.