How to read/write to a USB Serial COM port using powershell?

问题描述 投票:0回答:1

我现在浏览了整个互联网,现在尝试了无数的脚本和解决方案。也就是说,似乎大多数解决方案都是关于单向阅读,很少将写作和阅读结合起来。

我已经根据所有这些点点滴滴整理了一个 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?
找到。
所以在这里我可以同时使用 COM 9 和 11。

问题陈述:

  1. 脚本成功发送数据到串口COM口
  2. 但我无法阅读回复,即使我知道它在那里。
  3. 我知道它在那里,因为如果我在它发送 at 命令后终止脚本 (
    ATI
    ),或者让它超时。如果我使用任何常见的终端仿真程序(例如putty、screen、picocom等)连接到同一端口,我可以看到发送的数据和结果。(见截图。)
  4. 偶尔我会看到一些
    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
#------------------------------------------------------------------------------


参考资料:

1system.io.ports.serialport
2 从串行端口写入和读取信息
[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]

如何修复脚本以确保从我发送的命令中获得所有结果?

powershell serial-port
1个回答
0
投票

我惊恐地发现:

  • $port.Handshake
    的默认设置设置为
    None
    0
    ),
    当它必须设置为
    RequestToSend
    2
    )时。
  • 在我的 shell 中不断使用端口和变量之后,有些东西被破坏了,使
  • $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"
享受吧! 💖

© www.soinside.com 2019 - 2024. All rights reserved.