# VBForums CodeBank > CodeBank - Visual Basic .NET >  Serial Port

## dbasnett

If you have a question about this please post the question here
http://www.vbforums.com/forumdisplay.php?f=25 not in this tread.  Thanks!

Much of the difficulty I see here, and in other forums, surrounding the use of Windows serial port is centered around an assumption.

That assumption is that the serial port receives data in chunks that are conveniently packaged in the format we want.  If you are talking about terminal stuff that is true, they are called lines and it is easy.  But when that is not the case panic sets in.

The second thing I see is people fighting the DataReceived Event Handler.  My advice is don’t.  Treat it like a spoiled child that is never going to learn and just give it whatever it wants, whenever it wants.

Lastly, a byte can contain 256 values.  The standard encoder for the serial port is for 7-bit data.  If you have 8 bit data you need to change the encoding.  I use
System.Text.Encoding.GetEncoding("Windows-1252") always and have not had any problems.

I wanted fast code, and to write the code in such a way that it could be used for any application.

I have successfully sent, received, processed, and updated the UI at close to 1Mbps (USB Serial Converter) using the techniques I will describe.  My test bed is a USB SerialConverter to breakout box with Pins 2 & 3 hot wired.



**DataReceived Event Handler**

Mine is short, sweet, simple.  I read the number of bytes available (.BytesToRead) into a buffer of bytes of the correct size.  I store that buffer of bytes into a queue.  

Depending on how the UI is being driven I then raise an event, start a delegate, or do nothing if it is timer or usr interaction based.

That is it!  Any time it wants to fire, it can.



**So how does the data get to the user…**

Ask yourself what are the possible scenarios of how you might want data?  You might want:

    -one byte
    -a number of bytes
    -a character
    -a string
    -a string delimited by a character  (like lines)
    -etc.

And what if you want 2 bytes, then 4 characters, then a byte, and then a variable length line?

At this point my DataReceived Event Handler has placed any data received into a queue.  Here is an example. I have a GPS receiver that sends NMEA sentences (lines ending with CRLF). This is an example of one of the sentences:

$GPGSV,3,1,11,10,75,053,29,29,52,311,32,24,50,298,30,02,39,073,30*77

The serial ports DataReceived Event Handler might (usually does on my PC) fire several times with chunks of that data.  The queue might look like this:


    Queue entry – data
    1   $
    2   GPGSV,3,1,11,10
    3   ,75,053,29,29,52,311,
    4   32,24,50,298,30,02,39,073,30*77

The approach I took, with my limited knowledge of the programming tool at the time, was to write several routines that pieced the data back together.  The common routine for all of those created a user buffer by removing entries from the queue and piecing them back together.  The other routines then access the user buffer when they need data, and in the case of string based functions, converts it.  If there is not enough data to satisfy the request the routines return empty strings or zero length byte arrays.

Lets say I used a Timer control to read, process the sentences, and update the UI from my GPS receiver.  In my Timer routine I have a function call to my version of readline.  The first few times it is called it might return empty strings because the entire sentence has not been received.  Eventually all of the data is in and I get the entire sentence back, because it is actually all there.

    At time 1
    Queue entry – data
    1   $
    2   GPGSV,3,1,11,10

    User buffer – empty


    At time 2
    Queue entry – data
    1   ,75,053,29,29,52,311,
    2   32,24,50,298,30,02,39,073,30*77

    User buffer – $ GPGSV,3,1,11,10


    At time 3
    Queue empty


    User buffer – $ GPGSV,3,1,11,10,75,053,29,29,52,311, 32,24,50,298,30,02,39,073,30*77


The only imperative is that when you have a device that sends data all the time is that you process it in a timely manner.  If not, as you can imagine, you run out of memory.  

During my testing of the USB to SerialPort converter I was surprised to find that it was capable of speeds near 1Mbps and I made the mistake of not doing that.  I was using a user interface driven (click a button, see the data) routine that read all of the data and displayed the last chunk, when I received a high priority interrupt from Honey.  You know the one, take the trash out, change the light bulb, etc.  I left the test bed running and I had received an out of memory exception while servicing the Honey interrupt.


What prompted me to write this was something I read in another thread.  The poster had a problem because he said the device he was interfacing with only provided a 1 ms. gap in the data as its protocol.  As it turned out the protocol was a delimiter, followed by a length byte, followed by the data.  Then the problem was that that wasn’t enough to know that you actually had good data.  The posters point was this:

    message_delimiter - 1 byte
    4 - length - 1 byte
    (4 data bytes) - 4 bytes

But if you look at it this way, a **valid** message is always bounded by the message_delimiter: 

    message_delimiter - 1 byte
    4 - length - 1 byte
    (4 data bytes) - 4 bytes
    message_delimiter - 1 byte

Then you can safely process the data.


Many of you are better at using your programming tool of choice.  The code I wrote for serial ports was written several years ago when I was just learning VB .Net.(I cringe when I look at some of it today).  But I am willing to bet that it will out perform most of the C(pick a flavor) code I have seen posted on many forums.  One day I plan on re-visiting the code.

I hope that this helps.





The attached has several examples that use the loopback feature of a modem to do simulations. If you have a modem you should just need to open the port "COMx" and click go(a serial port with a loopback plug should work also). There is a debug feature, but it should only be used sparingly and for short periods of time.  The examples show how to do reads using a timer, delegate, or loop.

There is one other example that reads NMEA sentences and checks that the checksum is correct. 

There are routines to read bytes, strings and terminated strings.


Enjoy.
Link to this post  http://www.vbforums.com/showpost.php...31&postcount=1

VB 2008 Version
NuSerial.zip



updated - 3 Jan 2009
changed sample program to remove confusion over receiving bytes backwards, at least i hope i did.

updated - 9 Jan 2009
corrected error in zip file

updated - 29 Jan 2009
new version

----------


## dbasnett

Here is a 2005 version.  It was pieced together from the 2008 version and was NOT (barely) tested under 2005.

MySerial2005.zip

----------


## iMPR3SSiON

Thank you a lot for 2005 version  :Smilie:

----------


## Softselect

> VB2008
> 
> My method of handling the serial port is demonstrated in the attached project.  The module has all of the routines, even though only some are demonstrated.
> 
> I do not use BeginInvoke.  At very high bit rates it is very expensive.
> 
> Attachment 67815


Hi dbasnett,
I am trying to read a serial instrument with your sample, I have changed baudrate=4800, databits=7, stop bits=2 and set dtr=true
i can communicate with the instrument but in the rtbytes string the string is back to front intead of +006.14 I get 41.600+ as I am new to Vb2008 i would appreciate any help to correct this, I need the value in a string
i have some BBC Basic programming experiance not nothing to write home about and it was in 1984
thank you
Softselect

----------


## dbasnett

to read a string use one of the following:


```
                    Dim rcvSTR As String
                    rcvSTR = zSPGetString() 'read all available bytes as a string
                    rcvSTR = zSPGetLine(True, False, False, False) 'or read a line
```

you didn't post code so it is hard to know what happened.  also, describe what you are trying to read?  will it always look like "+006.14"?

----------


## allan-m

Hello, when I load the program in VS2008, I am getting a security error about the location of the program. If I click ok, it loads in with 14 errors about names that are not declared, how can I fix that? 

EDIT: I removed the file SerialPortRcvEvent.VB and re-added it, those errors are gone but I am getting 3 warnings:


Warning	1	Variable 'zSPGetAllBytes' is passed by reference before it has been assigned a value. A null reference exception could result at runtime.	C:\Documents and Settings\Allan\My Documents\Visual Studio 2008\Projects\MySerial\MySerial\SerialPortRcvEvent.vb	157	26	MySerial
Warning	2	Function 'zSPGetAllBytes' doesn't return a value on all code paths. A null reference exception could occur at run time when the result is used.	C:\Documents and Settings\Allan\My Documents\Visual Studio 2008\Projects\MySerial\MySerial\SerialPortRcvEvent.vb	165	5	MySerial
Warning	3	Variable 'zSPGetLine' is used before it has been assigned a value. A null reference exception could result at runtime.	C:\Documents and Settings\Allan\My Documents\Visual Studio 2008\Projects\MySerial\MySerial\SerialPortRcvEvent.vb	214	12	MySerial



I am still getting the error about the project location not being trusted.


EDIT:

I have ignored the three warnings for the time being. I have used your program and while it does indeed read data from my radio, I found that sometimes I have to send the command several times and then finally it will display the bytes in your *rtbCHAR* Rich Text Box. Is there a delay or perhaps a glitch that might prevent the command from being executed everytime?
I have also discovered that the bytes returned are reversed, how would I get it to read the bytes in the proper order? I think that this is the issue that SoftSelect is talking about.

----------


## dbasnett

i don't know about the security error, it probably has to do with your setup.  no one else has ever commented on it.

the three warnings can be ignored.

as to returning bytes in the wrong order; the code returns bytes in the order they are received.

what are you sending, and what do you expect back?  the code i provided accumulates text in thBytes, a stringbuilder, in reverse order.  that code is there for the display only.  you need to write your own calls to my routines for your application.

----------


## allan-m

Ok, I am still having problems. Here is exactly what I am doing:

I am sending the following command to the serial port:

SerialPort1.Write("FA;") 

The above is in a timer and sent every second, because this is the frequency display of my radio.

The data that is sent back to me over the serial port is:

;00059270000AF;00059270000AF;00059270000AF;00059270000AF;00059270000AF;00059270000AF;00059270000AF;0  0059270000AF;00059270000AF;00059270000AF;00059270000AF;00059270000AF;00059270000AF;00059270000AF 



If you look closely at the above data, you will see the following number backwards "7295". That is the radios frequency in Mhz, what I need to do is get that data somehow and display it in a textbox that is updated with the timer. Below is code that was borrowed and is what is giving me the above data.

    Private Sub butOC_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles butOC.Click 
        'open / close button 
        butOC.Enabled = False : butOC.Refresh() 
        If butOC.Text = "Open" Then 
            'open Port 
            'Port Setup 
            'defaults 
            'SerialPort1.ReadBufferSize = 4096 
            'SerialPort1.DataBits = 8 
            'SerialPort1.Parity = IO.Ports.Parity.None 
            'SerialPort1.StopBits = IO.Ports.StopBits.One 
            'SerialPort1.ReadTimeout = -1 'forever 
            SerialPort1.ReadTimeout = 0 
            'SerialPort1.WriteTimeout = -1 'forever 
            SerialPort1.WriteTimeout = 2 
            'SerialPort1.ReceivedBytesThreshold = 1 'don't change! unless you really need to 
            'SerialPort1.Encoding = Encoding.GetEncoding(20127) ' byte < 128, bytes > 127 equal question mark 
            SerialPort1.Encoding = AllByteEnc 'so byte > 127 aren't question marks 
            If cbSPD.SelectedItem Is Nothing Then 
                SerialPort1.BaudRate = Convert.ToInt32(defSPD) '<<<<<<<<<<<<<<<<<<<<<<< my default speed - Change 
            Else 
                SerialPort1.BaudRate = Convert.ToInt32(cbSPD.SelectedItem.ToString) '<<<<<<<<<<<<<<<<<<<<<<< my default speed - Change 
            End If 
            'determine port to open 
            If cbPorts.SelectedItem Is Nothing Then 
                SerialPort1.PortName = defPort 'use port default if nothing selected 
            Else 
                SerialPort1.PortName = cbPorts.SelectedItem.ToString 'use selected item 
            End If 
            If zSPopen(SerialPort1, True) Then 'zSPopen opens the port and sets up handler 
                'port opened succesfully 
                butOC.Text = "Close" 
                butOC.Enabled = True : butOC.Refresh() 
                butSend.Enabled = True 
                tbSend.ReadOnly = False 
                'loop while there are bytes in the queue 
                Do While SerialPort1.IsOpen OrElse zSPNumBytesInQ() > 0 
                    bytesFromQ = New Byte() {} 
                    bytesFromQ = zSPGetBytes(1) 'get one byte if available 
                    If bytesFromQ.Length > 0 Then 'did we get data 
                        'yes 
                        ' the rich text box shows newest byte on left 
                        rbRcv.Checked = True : rbRcv.Refresh() 
                        thBytes.Insert(0, " ", 1) 'insert new byte on left in the RTB 
                        thBytes.Insert(0, bytesFromQ(0).ToString, 1) 
                        rtbBytes.Text = thBytes.ToString 
                        'don't show cr lf in the following textbox 
                        If Not (bytesFromQ(0) = 10 OrElse bytesFromQ(0) = 13) Then 
                            rtbCHAR.Text = Chr(bytesFromQ(0)).ToString & rtbCHAR.Text 
                        End If 
                        If thBytes.Length > 512 Then thBytes.Length = 128 'don't let rtb get to big 
                        If rtbCHAR.TextLength > 512 Then rtbCHAR.Text = "" 'don't let rtb get to big 
                    Else 'no data received 
                        rbRcv.Checked = False : rbRcv.Refresh() 
                    End If 
                    Application.DoEvents() 'needed so event handler fires 
                Loop 
            End If 
        Else 
            'close Port 
            FreqTime.Enabled = False 'disable frequency display 
            butSend.Enabled = False 
            tbSend.ReadOnly = True 
            zSPclose(SerialPort1) 'close serial port, remove handler 
            butOC.Text = "Open" 
            butOC.Enabled = True : butOC.Refresh() 
            rbRcv.Checked = False 
        End If 
    End Sub 

rtbCHAR is the textbox that I would like to send the proper data and format to. I guess I just do not quite understand what the program is doing. The program was downloaded here . I am just trying to adapt his serial program to my needs until I understand what is going on.


How would I write the code within the above code to display the data that I need? Remember the timer is sending the command once every second.

----------


## spiritgate

hi guys, I tried both of the program, but none works for me.... I can open the port but when i send the text, it didnt change anything, like Bytes in Queue still in 0, Byte/Sec still in 0, Events / sec still in 0, Queue Depth still 0 and Max still 0... Receive data show nothing...

For the hardware side, I have connect the serial cable from this PC to another PC since each of my PC only has 1 serial port. So like that correct? Do I need to do anything else?

Thanks very much!

----------


## dbasnett

i thought in the other thread you said it was receiving data?

----------


## janneman

dwbasnett,

Slightly OT:

I read in another post from you about an example project to interface with a USB-serial port, and that you would put it in the 'code bank'. I assume that the cide bank is some sort of repository of user contributions, but where is it? Can't find it...

Thanks,

Jan Didden

----------


## dbasnett

there is nothing special about a usb serial port.  the project in post #1 should work for you.

----------


## janneman

Hi Dewayne,

I did indeed study your NuSerial example, but there's one or two things that still confuse me. 

For instance, I understand the selection box where you can select a ComPort, but how would Joe User know which one to select? I thought that in the program you could look at a property of each particular driver, like VID or PID or Manufacturer name, and select the comport you need for your app.

In your example you also use the specific hardware lines coded in thePins, but isn't that a strictly hardware thing? If yo look at a USB virtual ComPort, can you actually access those 'virtual' pins??

Edit: I guess what I want is some kind of 'GetDeviceInfo' that gives me access to the same kind of info that you get from Properties in the device manager.

Thanks for educating me,

Jan Didden

----------


## janneman

OK, made some more progress to find 'my' comport.
There are structures Dev_Broadcast_Hdr containing info on system devices, and Dev_Broadcast_Port with info on system ports. So what I need to do is to check all _Hdr's to see if the device is a com port, and if so, check dbcp_name in _Port to see if it is 'My' device driver on that comport. Makes sense?

Question is, how do I 'cycle' through all devices and when I find a com port, how to poit to the -Port block for that particular com port?

This is what I have so far:



```
Imports System.IO.Ports
Imports System.Runtime.InteropServices

Public Class Form1
    'Device information structure
    Public Structure Dev_Broadcast_Hdr
        Public dbch_size As Int32
        Public dbch_devicetype As Int32
        Public dbch_reserved As Int32
    End Structure
    Const DevTypeIsComPort As Int32 = &H3

    'Port information structure
    Public Structure Dev_Broadcast_Port
        Public dbcp_size As Int32
        Public dbcp_devicetype As Int32
        Public dbcp_reserved As Int32
        Public dbcp_name As String
    End Structure
    Dim MyDevice As String = "My Device"


    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        'this is just to see the available comports
        Dim comm As New List(Of SerialPort) 'list of comm ports
        For Each PNstr As String In SerialPort.GetPortNames() 'get a list of Com Port Names
            comm.Add(New SerialPort) 'insert new port into list
            comm(comm.Count - 1).PortName = PNstr 'set the name
            MessageBox.Show(PNstr)
        Next

        'this is to find "My Device" among the com ports
        Dim DeviceInfo As Dev_Broadcast_Hdr
        'How to cycle through devices to find com ports?
        If DeviceInfo.dbch_devicetype = DevTypeIsComPort Then ' this device is a com port
            'how to point to this particular Dev_Braodcast_Port?
            Dim PortInfo As Dev_Broadcast_Port
            'Now find friendly name in dbcp_name
            If PortInfo.dbcp_name = MyDevice Then
                'found it; do something
            End If
        End If

    End Sub
End Class
```

Any help is appreciated!

Jan Didden

----------


## pghtech

I am a green horn so some of your code I don't understand but do get the general concepts. I was very excited to see you program since for what I want to do them main part I need is a hypterterminal like program in VB that I can modify slightly. I program CDMA and GPRS wireless cell modems and wanted to create a program that I could send all my commands with automatically. Basically send the first command and then send the next based on the response back from the modem

I downloaded your PC Comm utility and am having problems. I am using VB Express 2008 so it did have to convert the program. The only error I got was something to do with the start page, and was an easy fix.
I am able to open a port, I am connecting to a GPRS cell modem through 
Com 4 
38400 (I did have to change the selection value in you program from 36000 to 38400) No big deal
8
None
1

most of the commands I send should return a simple "Ok" I am able to send commands but receive nothing in return. I then went into HypterTerminal to verify that I can communicate with the device and when I open the port in hypterterminal all the commands I was trying to send seemed to be buffered at the port some how and got sent all at once which of course returned an error. 
Is there something I'm missing?

Any help would be appriciated, please let me know if there is any more information I can provide for you.

----------

