由於未來不會持續寫 VB.NET 技術相關文章,因此分類至 C# 中。
VB.NET 使用 SerialPort
在 VB.NET 使用 SerialPort 是使用 Imports System.IO.Ports 這個類別,其實現 SerialPort 去讀取 COM 資料時,有分別為 GUI(Form Application) 及 Console Application 兩種做法,都不同。
Console 版本
Console 中沒有辦法用 Me.Invoke 實踐非同步訊息機制(詳情見 Reference. 1),所以使用 SerialPort 中的一個方法 (ReadExists) ,用此方法來讀取 buffer 中現有的資料,且也不可以使用 ReadLine() 的方法,因為 ReadLine() 會阻塞整個 Console Application 的執行緒,導致 Application 卡住,故只能用 ReadExists() 來做,且 ReadExists() 需要自行判斷資料的 "\r\n" 換行字元,避免資料長短不一。
Example:
Imports System.IO.Ports
Imports System.Net
Imports System.Text
Module Module1
Sub Main()
Console.WriteLine("Serial Port List: ")
GetSerialPortNames()
Console.WriteLine("Enter the port you want to listen (Ex: COM3):")
Try
Dim portname As String = Console.ReadLine()
Console.WriteLine("Start listen to " + portname)
ReceiveSerialData(portname)
Catch ex As Exception
Console.WriteLine(ex.Message)
Console.WriteLine("Please Run Program Again.....")
End Try
Console.WriteLine("Press any key to exit program.")
Console.ReadLine()
End Sub
Sub GetSerialPortNames()
' Show all available COM ports.
For Each sp As String In My.Computer.Ports.SerialPortNames
Console.WriteLine(sp.ToString())
Next
End Sub
Function ReceiveSerialData(portname As String)
' Receive strings from a serial port.
Dim returnStr As String = ""
Dim DataReceived As String
Dim comport As IO.Ports.SerialPort = Nothing
comport = New SerialPort()
comport.PortName = portname
comport.BaudRate = 9600
comport.Parity = Parity.None
comport.DataBits = 8
comport.StopBits = StopBits.One
comport.Handshake = Handshake.RequestToSend
comport.DtrEnable = True
comport.ReadTimeout = 500
comport.Open()
Console.WriteLine("Waiting for Data to be Received...")
Try
Do
DataReceived = comport.ReadExisting()
If DataReceived Is "" Then
Continue Do
End If
Console.WriteLine("Data received -> {0}", DataReceived)
Loop
Catch
Finally
comport.Close() ' Close port
End Try
End Function
End Module
範例中沒有自動判斷 ReadExists() 是否遇到 \r\n 去換行。
GUI 版本
GUI 版本中,可以使用事件系統來傳遞值,因此有一個方法叫做 port_DataReceived,用來接收非同步情形下傳入的值事件,接著要在此控制 GUI 的主執行緒,一定要透過 Delegate 的委派機制去呼叫控制,所以使用 updateCharts() 方法來控制 GUI 的主執行緒 XY 圖之值。
XY 線圖是透過 ToolBox 中的 Charts 去新增,然後在屬性控制中,有一個 Series 欄位(型別為一集合類) 是透過 Series 的類別去控制它的線圖,可以從程式控制 (於範例中 34 行) 看到這樣的方式,或是直接在屬性欄位做修正也可以。
Example:
Imports System.IO.Ports
Imports System.Text
Imports System.Windows.Forms.DataVisualization.Charting
Public Class Form1
Private port As New SerialPort("COM4", 9600, Parity.None, 8, StopBits.One)
Private ReadOnly _startOfText As String = System.Text.Encoding.ASCII.GetChars(New Byte() {2})
Private ReadOnly _endOfText As String = System.Text.Encoding.ASCII.GetChars(New Byte() {4})
Public Event MessageReceived(ByVal message As String)
Public Event DataIgnored(ByVal text As String)
Private message As String
Private s As New Series
Private _buffer As StringBuilder = New StringBuilder
Private logFile As String
Dim count As Int32 = 1
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
s.ChartType = SeriesChartType.Line
s.Name = "Series Line 1"
Chart1.Series.Add(s)
End Sub
Private Sub Button3_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button3.Click
My.Computer.FileSystem.WriteAllText("C:\logFile.csv", logFile, False)
MessageBox.Show("Saved")
End Sub
Private Sub updateText()
ListBox1.Items.Add(Me.message)
ListBox1.TopIndex = ListBox1.Items.Count - 1
'add in xy
Try
s.Points.AddXY(count, Int32.Parse(Me.message))
count += 1
logFile += "," + Me.message
Catch
End Try
End Sub
Private Sub port_DataReceived(ByVal sender As Object, ByVal e As SerialDataReceivedEventArgs)
Try
Dim msg As String = port.ReadExisting()
message = msg
Me.Invoke(New EventHandler(AddressOf updateText))
Catch ex As Exception
MessageBox.Show(ex.Message())
End Try
End Sub
Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click
AddHandler port.DataReceived, New SerialDataReceivedEventHandler(AddressOf port_DataReceived)
Try
port.Open()
Catch ex As Exception
MessageBox.Show(ex.Message())
End Try
End Sub
Public Sub AppendText(ByVal text As String)
_buffer.Append(text)
While processBuffer(_buffer)
End While
End Sub
Private Function processBuffer(ByVal buffer As StringBuilder) As Boolean
Dim foundSomethingToProcess As Boolean = False
Dim current As String = buffer.ToString()
Dim stxPosition As Integer = current.IndexOf(_startOfText)
Dim etxPosition As Integer = current.IndexOf(_endOfText)
If (stxPosition >= 0) And (etxPosition >= 0) And (etxPosition > stxPosition) Then
Dim messageText As String = current.Substring(0, etxPosition + 1)
buffer.Remove(0, messageText.Length)
If stxPosition > 0 Then
RaiseEvent DataIgnored(messageText.Substring(0, stxPosition))
messageText = messageText.Substring(stxPosition)
End If
RaiseEvent MessageReceived(messageText)
foundSomethingToProcess = True
ElseIf (stxPosition = -1) And (current.Length <> 0) Then
buffer.Remove(0, current.Length)
RaiseEvent DataIgnored(current)
foundSomethingToProcess = True
End If
Return foundSomethingToProcess
End Function
Public Sub Flush()
If _buffer.Length <> 0 Then
RaiseEvent DataIgnored(_buffer.ToString())
End If
End Sub
Private Sub Button4_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button4.Click
port.Close()
End Sub
End Class
Reference:
1. https://stackoverflow.com/questions/26689762/how-to-use-invoke-required-and-invoke-in-console-application
https://stackoverflow.com/questions/11458850/how-to-read-serial-port-communication-into-buffer-and-parse-out-complete-message
https://dotblogs.com.tw/billchung/2012/01/20/66860
https://dotblogs.com.tw/chou/2009/03/20/7614
https://social.msdn.microsoft.com/Forums/vstudio/en-US/6e5ee56f-a201-4a27-91f9-1bfbf7ab1f89/problem-in-receiving-data-through-serial-port-when-using-readline?forum=csharpgeneral
沒有留言:
張貼留言