milindsaraswala
asked on
Reading from Serial Port in VB.net
I am trying to read Portal Data Collector through Serial Port. I am success to do it but the problem is that when I try to run in debug mod by press F10 it will work but when I try to run EXE than it stop at some place and don't give any error.
Next problem in this it is reading same Line twice. I think there is problem in thread . Because when I try to write in textbox it will give me thread error.
My code for designer and code behind is as follows
Kindly help me to rectify my logical error
Next problem in this it is reading same Line twice. I think there is problem in thread . Because when I try to write in textbox it will give me thread error.
My code for designer and code behind is as follows
Kindly help me to rectify my logical error
Imports System.Threading
Imports System.IO.Ports
Imports System.Text
Public Class frmScanPalReading
Dim ReadBuffer As String = ""
Dim _continue As Boolean = False
Private Sub frmScanPalReading_Disposed(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Disposed
SerialPort.Close()
End Sub
Private Sub frmScanPalReading_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
Try
With SerialPort
.PortName = "COM1"
.Handshake = IO.Ports.Handshake.RequestToSend
.ReceivedBytesThreshold = 1
.RtsEnable = True
.BaudRate = 115200
.DataBits = 8
.Parity = IO.Ports.Parity.None
.StopBits = IO.Ports.StopBits.One
.Open()
End With
Catch ex As Exception
MsgBox(ex.Message)
End Try
End Sub
Private Sub btnExtract_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnExtract.Click
SendCommand("READ")
End Sub
Sub SendCommand(ByVal cmd As String)
SerialPort.Write(cmd + Chr(13))
End Sub
Private Sub SerialPort_DataReceived(ByVal sender As Object, ByVal e As System.IO.Ports.SerialDataReceivedEventArgs) Handles SerialPort.DataReceived
'This event will Receive the data from the selected COM port..
Dim thRec As Thread
If e.EventType = SerialData.Chars Then
thRec = New Thread(AddressOf ReceiveData)
thRec.IsBackground = True
thRec.Priority = ThreadPriority.Highest
thRec.Start()
Thread.Sleep(2)
End If
End Sub
Private Sub ReceiveData()
'Sub to Receive Data from the Serial Port, Will Run in a Thread
Dim bRead, nRead As Integer, crPOS As Integer
Dim returnStr As String = ""
Dim ascStr As String = ""
Dim m As String = ""
Dim i As Int16 = 1
bRead = SerialPort.BytesToRead 'Number of Bytes to read
Dim cData(bRead - 1) As Byte
SerialPort.Encoding = Encoding.GetEncoding(65001)
nRead = SerialPort.Read(cData, 0, bRead) 'Reading the Data
If _continue = False Then crPOS = ByteSearch(cData, "13")
For Each b As Byte In cData
If b <> 0 Then
If crPOS >= i AndAlso _continue = False Then
ascStr += Chr(b)
i += 1
Else
returnStr += Chr(b)
_continue = True
End If
End If
Next
Debug.WriteLine(m)
If returnStr <> "OVER" AndAlso returnStr <> "" Then
ReadBuffer += returnStr 'ASCII String
SendCommand("ACK")
End If
Console.Write("==Begin=={0}==End==", ReadBuffer.ToString)
End Sub
Function ByteSearch(ByVal bData() As Byte, ByVal SearchString As String) As Int16
Dim i As Int16 = 1
For Each b As Byte In bData
If b = SearchString Then
Exit For
End If
i += 1
Next
Return i
End Function
End Class
<Global.Microsoft.VisualBasic.CompilerServices.DesignerGenerated()> _
Partial Class frmScanPalReading
Inherits System.Windows.Forms.Form
'Form overrides dispose to clean up the component list.
<System.Diagnostics.DebuggerNonUserCode()> _
Protected Overrides Sub Dispose(ByVal disposing As Boolean)
If disposing AndAlso components IsNot Nothing Then
components.Dispose()
End If
MyBase.Dispose(disposing)
End Sub
'Required by the Windows Form Designer
Private components As System.ComponentModel.IContainer
'NOTE: The following procedure is required by the Windows Form Designer
'It can be modified using the Windows Form Designer.
'Do not modify it using the code editor.
<System.Diagnostics.DebuggerStepThrough()> _
Private Sub InitializeComponent()
Me.components = New System.ComponentModel.Container
Me.txData = New System.Windows.Forms.RichTextBox
Me.btnExtract = New System.Windows.Forms.Button
Me.SerialPort = New System.IO.Ports.SerialPort(Me.components)
Me.SuspendLayout()
'
'txData
'
Me.txData.Location = New System.Drawing.Point(12, 29)
Me.txData.Name = "txData"
Me.txData.Size = New System.Drawing.Size(684, 345)
Me.txData.TabIndex = 0
Me.txData.Text = ""
'
'btnExtract
'
Me.btnExtract.Location = New System.Drawing.Point(287, 393)
Me.btnExtract.Name = "btnExtract"
Me.btnExtract.Size = New System.Drawing.Size(75, 23)
Me.btnExtract.TabIndex = 1
Me.btnExtract.Text = "Extract"
Me.btnExtract.UseVisualStyleBackColor = True
'
'frmScanPalReading
'
Me.AutoScaleDimensions = New System.Drawing.SizeF(6.0!, 13.0!)
Me.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font
Me.ClientSize = New System.Drawing.Size(708, 446)
Me.Controls.Add(Me.btnExtract)
Me.Controls.Add(Me.txData)
Me.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog
Me.MaximizeBox = False
Me.MinimizeBox = False
Me.Name = "frmScanPalReading"
Me.Text = "Scan Pal Data Reading"
Me.ResumeLayout(False)
End Sub
Friend WithEvents txData As System.Windows.Forms.RichTextBox
Friend WithEvents btnExtract As System.Windows.Forms.Button
Friend WithEvents SerialPort As System.IO.Ports.SerialPort
End Class
ASKER
I did this before but it hang my application. if you see my code I have to pass Read. Then only it will go ahead. Then I have to pass ACK(i.e. Acknowledge) to read another line. So kindly look at my codes try remove bug from it.
There are at least two problems I see with this code:
(1) The thread ReceiveData is run everytime the DataReceived event occurs. This might result in multple threads trying simultaneously to read from SerialPort. The correct implementation would start only one thread when the application starts (or some other initial event), and that thread should read data from SerialPort when the DataReceived event occurs. You might need to signal the read through a Semaphore or similar cross-thread signal from DataReceived event handler to the single thread.
(2) Forms objects like TextBox should be accessed only from the thread on which they were created. The line
txData.Text = ReadBuffer
in ReceiveData will throw an error. One way to work around this is to call txData.Invoke().
(1) The thread ReceiveData is run everytime the DataReceived event occurs. This might result in multple threads trying simultaneously to read from SerialPort. The correct implementation would start only one thread when the application starts (or some other initial event), and that thread should read data from SerialPort when the DataReceived event occurs. You might need to signal the read through a Semaphore or similar cross-thread signal from DataReceived event handler to the single thread.
(2) Forms objects like TextBox should be accessed only from the thread on which they were created. The line
txData.Text = ReadBuffer
in ReceiveData will throw an error. One way to work around this is to call txData.Invoke().
ASKER
Kindly can u give me some sort of code to it. I will be very helpful for the same
Following is in C#. I hope you can write similar in VB. I did not compile or run this, but it should give you the idea of where to go.
private class MySerialStream : SerialStream
{
private Thread rxThread = null;
private object RxThreadSync = new object();
public SerialStream()
{
rxThread = new Thread(new ThreadStart(this.RxThread));
rxThread.IsBackground = true;
rxThread.Name = "SerialStream RxThread";
rxThread.Start();
this.DataReceived += new SerialDataReceivedEventHandler(serialStream_DataReceived);
}
private void serialStream_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
lock (this.RxThreadSync)
{
Monitor.Pulse(this.RxThreadSync);
}
}
private void RxThread()
{
while (this.IsOpen)
{
try
{
lock (this.RxThreadSync)
{
int available = this.BytesToRead;
byte[] tmpBuf = new byte[available];
this.Read(tmpBuf, 0, available);
}
}
catch (Exception ex)
{
}
}
}
}
ASKER
Sorry I am VB.net coder I don't understand C# Kindly can you provide me in consent of following code. Please ..............
Imports System.Threading
Imports System.IO.Ports
Imports System.Text
Public Class frmScanPalReading
Dim ReadBuffer As String = ""
Private rxThread As Thread = Nothing
Private RxThreadSync As New Object()
Private Sub frmScanPalReading_Disposed(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Disposed
SerialPort.Close()
End Sub
Private Sub frmScanPalReading_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
Try
With SerialPort
.PortName = "COM1"
.Handshake = IO.Ports.Handshake.RequestToSend
.ReceivedBytesThreshold = 1
.RtsEnable = True
.BaudRate = 115200
.DataBits = 8
.Parity = IO.Ports.Parity.None
.StopBits = IO.Ports.StopBits.One
.Open()
End With
Catch ex As Exception
MsgBox(ex.Message)
End Try
End Sub
Private Sub btnExtract_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnExtract.Click
SendCommand("READ")
End Sub
Sub SendCommand(ByVal cmd As String)
SerialPort.Write(cmd + Chr(13))
End Sub
Private Sub SerialPort_DataReceived(ByVal sender As Object, ByVal e As System.IO.Ports.SerialDataReceivedEventArgs) Handles SerialPort.DataReceived
'This event will Receive the data from the selected COM port..
ReceiveData()
End Sub
Private Sub ReceiveData()
Dim cmd As String = "", _buffer = ""
Dim crPOS As Integer
_buffer = ByteToStr()
crPOS = InStr(_buffer, Chr(13))
If crPOS > 1 Then cmd = Microsoft.VisualBasic.Left(_buffer, crPOS - 1)
If cmd = "ACK" Then
While _buffer <> "OVER" '+ Chr(13)
SendCommand("ACK")
_buffer = ByteToStr()
ReadBuffer = ReadBuffer & _buffer
'MsgBox(ReadBuffer)
End While
Else
MsgBox("Timeout expired!")
End If
End Sub
Public Function ByteToStr() As String
Dim strData As String = ""
Dim bRead, nRead As Integer
bRead = SerialPort.BytesToRead
Dim cData(bRead - 1) As Byte
nRead = SerialPort.Read(cData, 0, bRead) 'Reading the Data
For Each b As Byte In cData
If b <> 0 AndAlso b <> 3 AndAlso b <> 10 AndAlso b <> 4 AndAlso b <> 2 AndAlso b <> 16 Then
strData += Chr(b)
End If
Next
MsgBox(strData)
Return Trim(strData)
End Function
End Class
Your code does not use threading. I recommend using threading. See sample code below. I have tried to convert this to VB, but there may still be some problems.
Namespace MyNamespace
Class MySerialStream
Private rxThread As Thread
Private RxThreadSync As Object
Private serialStrea as SerialStream
Public Sub New()
Dim str As New MySerialStream()
Dim rxThread As New Thread(AddressOf str.RxThread)
rxThread.IsBackground = true;
rxThread.Name = "SerialStream RxThread";
rxThread.Start();
AddHandler serialStream.DataReceived, AddressOf serialStream_DataReceived
End Sub
Private Sub MySerialStream::serialStream_DataReceived(object sender, SerialDataReceivedEventArgs e)
SyncLock RxThreadSync
Monitor.Pulse(RxThreadSync)
End SyncLock
End Sub
Private Sub MySerialStream::RxThread()
Dim available As Integer = 0;
Dim tmpBuf(available) As Byte
While (serialStream.IsOpen)
Monitor::Wait( RxThreadSync );
available = serialStream.BytesToRead;
serialStream.Read(tmpBuf, 0, available);
End While
End Sub
ASKER
Sir,
I used before the same but I am stuck so I again requesting you to change my working code. I will be very much appreciate.
Again I am requesting. From last 1 week I am running behind this.
I used before the same but I am stuck so I again requesting you to change my working code. I will be very much appreciate.
Again I am requesting. From last 1 week I am running behind this.
Please give it a try and post your code when you have integrated the threading. Try runninging it and tell me where you get stuck. I'm not in a position to write all the code for you.
ASKER
First of all I reading your 2 -3 time but I am getting first line on [b]Private serialStrea as SerialStream [/b] I am not getting what is this SerialStream. And if you see my code Sending some command to the serialPort and then it invoke datarecived event in that I am calling receivedata procedure which I did not find.
If you don't mind than I can send you my codes file through email. So you change the same and send me back the result. Sorry again to bother you.
If you don't mind than I can send you my codes file through email. So you change the same and send me back the result. Sorry again to bother you.
What is SerialPort from _your_ code? I'm assuming it is of type SerialStream? Where is the declaration? See code below. I replaced serialStream by SerialPort (your name) and I showed you how to call ReceiveData. I hope that is clearer.
Namespace MyNamespace
Class MySerialStream
Private rxThread As Thread
Private RxThreadSync As Object
Private SerialPort as SerialStream
Public Sub New()
Dim str As New MySerialStream()
Dim rxThread As New Thread(AddressOf str.RxThread)
rxThread.IsBackground = true;
rxThread.Name = "SerialStream RxThread";
rxThread.Start();
AddHandler SerialPort.DataReceived, AddressOf SerialPort_DataReceived
End Sub
Private Sub MySerialStream::SerialPort_DataReceived(object sender, SerialDataReceivedEventArgs e)
SyncLock RxThreadSync
Monitor.Pulse(RxThreadSync)
End SyncLock
End Sub
Private Sub MySerialStream::RxThread()
Dim available As Integer = 0;
Dim tmpBuf(available) As Byte
While (SerialPort.IsOpen)
Monitor::Wait( RxThreadSync );
available = SerialPort.BytesToRead;
SerialPort.Read(tmpBuf, 0, available);
// Call ReceiveData with argument tmpBuf
End While
End Sub
Private Sub ReceiveData()
Dim cmd As String = ""
'Don't call ReadExisting here, but use the argument tmpBuf passed from RxThread()
'Dim _buffer = SerialPort.ReadExisting
Dim _buffer = tmpBuf
Dim crPos As Integer
crPos = InStr(_buffer, Chr(13))
If crPos > 1 Then cmd = Microsoft.VisualBasic.Left(_buffer, crPos - 1)
If cmd = "ACK" AndAlso _continue = False Then
ReadBuffer = ReadBuffer & Mid(_buffer, crPos + 2)
_continue = True
SendCommand(cmd)
ElseIf cmd <> "OVER" AndAlso _continue = True Then
ReadBuffer = ReadBuffer & _buffer
SendCommand("ACK")
MsgBox(ReadBuffer)
Else
txData.Text = ReadBuffer
'MsgBox(ReadBuffer)
End If
'Sub to Receive Data from the Serial Port, Will Run in a Thread
'Dim bRead, nRead As Integer, crPOS As Integer
'Dim returnStr As String = ""
'Dim ascStr As String = ""
'Dim m As String = ""
'Dim i As Int16 = 1
'bRead = SerialPort.BytesToRead 'Number of Bytes to read
'Dim cData(bRead - 1) As Byte
'SerialPort.Encoding = Encoding.GetEncoding(65001)
'nRead = SerialPort.Read(cData, 0, bRead) 'Reading the Data
'If _continue = False Then crPOS = ByteSearch(cData, "13")
'For Each b As Byte In cData
' If b <> 0 Then
' If crPOS >= i AndAlso _continue = False Then
' ascStr += Chr(b)
' i += 1
' Else
' returnStr += Chr(b)
' _continue = True
' End If
' End If
'Next
'Debug.WriteLine(m)
'If returnStr <> "OVER" AndAlso returnStr <> "" Then
' ReadBuffer += returnStr 'ASCII String
' SendCommand("ACK")
'End If
'Console.Write("==Begin=={0}==End==", ReadBuffer.ToString)
End Sub
ASKER
No actually serialport is component from toolbox and In my code I am adding EventHandler already to the sub procedure.
Sorry and please don't get angry if I am wrong
Sorry and please don't get angry if I am wrong
ASKER CERTIFIED SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
ASKER
Thank you very much this solve my problem. I know now about thread I had never worked before with it.
ASKER
Great he was really patient with me and giving me time and afford to solve my problem.
Though I have irritate him.
Though I have irritate him.
ASKER
Cross-thread operation not valid
Open in new window