Trying to read a 12 byte hex string (RFID tag ID) using SerialPort Control.
-
Montag, 23. April 2012 10:20
I'm trying to make an application that can read a 12 byte hex string from the serial port and display it into a text box, the source is a RFID module that is connected to my USB port using a RS232-USB converter.
My first problem was accessing the text box in the datarecieved event due to invalid cross-threaded operation. Upon googling that, I copy/pasted the code I needed to get it work, these are all the cross threading functions.
The OpenPort function and Datarecieved event are my own code, the rest I got off google.
The application then worked, but I would get an incomplete code, the first swipe I would get all 12 digits, and at every other swipe, the first byte would vanish. Upon some more reading, I decided to make an array, the size of the incoming data (shown in the code below), but now I get the cross-threading error again.
I know there's something wrong with the way I'm using the array. This is way beyond my VB knowledge, and I'm in over my head here, could use some expert help. I'm an electronics engineer and self taught myself a little programming to create some apps for my hardware.
Thanks.
The following is my code:
Imports System.Threading Imports System.Timers Imports System.IO.Ports Public Class Form1 Public Sub OpenPort() Try If SerialPort1.IsOpen Then SerialPort1.Close() End If SerialPort1.BaudRate = 9600 SerialPort1.DataBits = 8 SerialPort1.StopBits = 1 SerialPort1.PortName = "COM4" SerialPort1.Open() Catch MessageBox.Show("Error") End Try End Sub Dim msg As String Dim arr(12) As Array Private Sub SerialPort1_DataReceived(ByVal sender As System.Object, ByVal e As System.IO.Ports.SerialDataReceivedEventArgs) Handles SerialPort1.DataReceived If SerialPort1.IsOpen Then msg = SerialPort1.ReadExisting() End If Dim bytes As Integer = SerialPort1.BytesToRead Dim bytesarray(bytes) As Byte SerialPort1.Read(bytesarray, 0, bytes) Dim enc As New System.Text.UTF8Encoding() msg = enc.GetString(bytesarray) test() End Sub Private Sub thread1() End Sub Public Sub test() Dim t1 As New System.Threading.Thread(AddressOf thread1) t1.Start() AddControlToForm(TextBox1) t1.Abort() End Sub Private Delegate Sub AddControlToFormDelegate(ByVal ctrl As Control) Private Sub AddControlToForm(ByVal ctrl As Control) If Me.InvokeRequired Then Dim delegate1 As New AddControlToFormDelegate(AddressOf AddControlToForm) Dim parameters(0) As Object parameters(0) = ctrl Me.Invoke(delegate1, parameters) Else Me.Controls.Add(ctrl) End If ctrl.Text = msg.ToString() End Sub Private Sub EventHandler() Throw New NotImplementedException End Sub Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click OpenPort() End Sub Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click SerialPort1.Close() End Sub End Class
Alle Antworten
-
Montag, 23. April 2012 12:01
There is no rule you can use that specifies at what stage of the serial data stream the data received event fires. In particular, you cannot assume that it fires once after all 12 bytes have been received. In your case I would guess that it is firing once after one character has been received and then again after the remaining 11 are received.
Firstly, you need to decide which bit of code you want to use to get the data that is available. Currently, you are doing a ReadExisting and a Read. You need one or the other. In both cases you should check how many characters were received.
Then you need to change the way that you handle the bytes that are received in the data received event. You need to append the bytes to an array each time the data received event is processed, until you get all 12. I can't quite follow your code, but it seems more complex than you need.
There is a simple example of using a queue to build up the data stream here:
http://www.vbdotnet.com.nu/simple_data_receiving.htmland an example that uses delegates here:
http://www.vbdotnet.com.nu/receiving_text_with_a_delegate.htmlNote that those examples assume that the data stream is text. If there is any possibility that the serial port data is anything other than 7-bit ASCII, you should receive it as bytes and display it hex strings of those bytes, until you confirm that the data is correct and that your conversion to text is working properly.
- Als Antwort markiert mas1337 Dienstag, 24. April 2012 06:24
-
Montag, 23. April 2012 16:46
The simple way is to test the BytesToRead property before you read the data. This should be sufficient for RFID tag reading, though other types of data might need a slightly more complex technique. For example, in your DataReceived event:
Dim BytesAvailable As Integer = SerialPort1.BytesToRead
If BytesAvailable >= 12 'best to test for inequality
Dim bytesarray(BytesAvailable - 1) As Byte
SerialPort1.Read(bytesarray, 0, BytesAvailable)
If BytesAvailable = 12 then
'go ahead and process the data
Else
'too many bytes were read -- this is an error, so skip it, or ask the user to re-read
End If
Dick
Dick Grier. Author of Visual Basic Programmer's Guide to Serial Communications 4. See www.hardandsoftware.net.
-
Montag, 23. April 2012 16:51
Hey, thanks for the links.
I've been testing with both ReadExisting and Read. When I disable the If statement with Readexisting, I get a corss-threading error, and when I disable the array, I get incomplete data.
I used the delegate one and it works great, just one problem, it appends the textbox every time I swipe the card. I've tried placing a txtRecieved.text = "" everywhere, it messes the whole thing up. I need the textbox refresh and reprint the data in it whenever I swipe. Like in my original code.
Could someone please tell me where/what I can do to prevent the textbox from appending the next data received.
Imports System.IO.Ports Public Class Form3 Private Sub Form3_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load For Each s In System.IO.Ports.SerialPort.GetPortNames() lstPorts.Items.Add(s) Next s d = New SetReceivedText(AddressOf SetText) End Sub Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click If lstPorts.SelectedIndex = -1 Then MessageBox.Show("Please select a port") Exit Sub Else SerialPort1.BaudRate = 9600 SerialPort1.DataBits = 8 SerialPort1.Parity = IO.Ports.Parity.None SerialPort1.StopBits = IO.Ports.StopBits.One SerialPort1.PortName = lstPorts.SelectedItem.ToString SerialPort1.Open() End If End Sub Delegate Sub SetReceivedText(ByVal ReceivedData As String) Dim d As SetReceivedText Private Sub SetText(ByVal myData As String) If Me.txtReceived.InvokeRequired Then Me.Invoke(d, New Object() {myData}) Else Me.txtReceived.Text &= myData End If End Sub Private Sub SerialPort1_DataReceived(ByVal sender As System.Object, ByVal e As System.IO.Ports.SerialDataReceivedEventArgs) Handles SerialPort1.DataReceived SetText(SerialPort1.ReadExisting()) End Sub Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click SerialPort1.Close() txtReceived.Text = "" End Sub End Class
-
Montag, 23. April 2012 18:58
Try this for your DataReceived event:
Dim Chars(11) As Char Dim CharsCnt As Integer Private Sub SerialPort1_DataReceived(ByVal sender As Object, ByVal e As SerialDataReceivedEventArgs) Handles SerialPort1.DataReceived CharsCnt += SerialPort1.Read(Chars, CharsCnt, Chars.Length - CharsCnt) If CharsCnt = Chars.Length Then Me.Invoke(New SetReceivedText(AddressOf SetText)) CharsCnt = 0 End If End Sub Delegate Sub SetReceivedText() Private Sub SetText() txtReceived.Text = Chars End Sub- Als Antwort markiert mas1337 Dienstag, 24. April 2012 06:24
-
Dienstag, 24. April 2012 06:23
That worked perfectly, exactly what I needed. Thanks a lot John!Try this for your DataReceived event:
Dim Chars(11) As Char Dim CharsCnt As Integer Private Sub SerialPort1_DataReceived(ByVal sender As Object, ByVal e As SerialDataReceivedEventArgs) Handles SerialPort1.DataReceived CharsCnt += SerialPort1.Read(Chars, CharsCnt, Chars.Length - CharsCnt) If CharsCnt = Chars.Length Then Me.Invoke(New SetReceivedText(AddressOf SetText)) CharsCnt = 0 End If End Sub Delegate Sub SetReceivedText() Private Sub SetText() txtReceived.Text = Chars End Sub

