Trying to read a 12 byte hex string (RFID tag ID) using SerialPort Control.

Answered Trying to read a 12 byte hex string (RFID tag ID) using SerialPort Control.

  • Montag, 23. April 2012 10:20
     
      Enthält Code

    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



    • Bearbeitet mas1337 Montag, 23. April 2012 10:21
    • Bearbeitet mas1337 Montag, 23. April 2012 10:22
    •  

Alle Antworten

  • Montag, 23. April 2012 12:01
     
     Beantwortet

    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.html

    and an example that uses delegates here:
    http://www.vbdotnet.com.nu/receiving_text_with_a_delegate.html

    Note 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
     
      Enthält Code

    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
     
     Beantwortet Enthält Code

    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
     
      Enthält Code

    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
    That worked perfectly, exactly what I needed. Thanks a lot John!