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
OK
s 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?