我现在浏览了整个互联网,现在尝试了无数的脚本和解决方案。也就是说,似乎大多数解决方案都是关于单向阅读,很少将写作和阅读结合起来。
我已经根据所有这些点点滴滴整理了一个 powershell 脚本。它通过运行以下
pwsh.exe
命令来启动另一个 pwsh shell(最新的 7.3.4
)并从那里运行脚本。
Start-Process pwsh.exe -ArgumentList '-noprofile -noexit -c "./mecom.ps1"' -PassThru | Out-Null
有问题的连接设备是基于 USB 的 3G/4G LTE 数据调制解调器。
当连接到 PS 时,它会在 Ports
PNPClass. 中报告以下 3 个 COM 端口
Alcatel 3G modem debug (COM11) - modem
Application1 Interface (COM10) - diag
Application2 Interface (COM9) - AT_MBIM
这3个对应
modem+diag+at_mbim
USB模式,用AT+USBMODE?
找到。ATI
),或者让它超时。如果我使用任何常见的终端仿真程序(例如putty、screen、picocom等)连接到同一端口,我可以看到发送的数据和结果。(见截图。)OK
,但目前看起来是随机的。包含从脚本发送但未被脚本接收的命令结果的缓冲区数据。
#!/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
#------------------------------------------------------------------------------
参考资料:
system.io.ports.serialport
从串行端口写入和读取信息
[3] 使用PowerShell进行串口通信
[4] powershell脚本连续读取串口数据
[5] 如何连续读取Serial COM端口
[6] SerialPort.DataReceived 事件
[7] 使用 System.IO.Ports.SerialPort 将 AT 命令写入内部调制解调器时出现问题
[8] System.IO.Ports 命名空间
[9] unlock-a-pinlocked-broadband-device-using-powershell-and-atcommands
鉴于缺乏有效的公共解决方案,我开始认为这可能是 powershell 本身的问题!?有些人漫不经心地谈论制作
Event Listener
,但他们总是无法提供任何有效的 powershell 示例,并且总是参考关于同一主题的同样糟糕的 Microsoft C#/.NET 网页。 [1,2]
如何修复脚本以确保从我发送的命令中获得所有结果?
我惊恐地发现:
$port.Handshake
的默认设置设置为
None
(
0
),
当它必须设置为RequestToSend
(
2
)时。
$port.ReadLine()
和 $port.ReadExisting() 的最基本用法接收,从 CLI 中静默失败。
($data).Length
从建议的改进中打错了字,使
if()
声明总是沉默任何数据!
EventHandler
.事件处理器
[System.IO.Ports.SerialPort]
时事件处理器最基本的用法,需要这样使用:
# From CLI
# Make sure the port is open before registration!
$p.Open()
# Register the a new handler
Register-ObjectEvent -InputObject $p -EventName "DataReceived" -SourceIdentifier COM_EVENT_HAND -Action { $Sender.ReadExisting() | Out-Host }
# Get some info about your event handler
Get-EventSubscriber -SourceIdentifier COM_EVENT_HAND
# Send some command to be seen
$p.Write("ATI`r")
# Unregister (before changing it)
Unregister-Event -SourceIdentifier "COM_EVENT_HAND"
享受吧! 💖