Windows Serial Port Programming Example

Serial ports provide an easy way to communicate between many types of hardware and your computer. They are relatively simple to use and are very common among peripherals and especially DIY projects. Many platforms such as Arduino have built in serial communication so they are really easy to set up and use. Many times you may want your project to communicate with your computer in order to have a cool interactive output, a neat sensor that passes data to your computer, or anything else you could possibly dream up. In this tutorial, I will walk you through how to interface to a serial port on the computer side of things, using Microsoft's . net framework. The code examples in this tutorial are in C#, but can be easily transferred to Visual Basic, or Visual C++. This tutorial assumes that you have a very basic understanding of object oriented programing, and whatever language you choose to program in.
Since we are mainly going to be using the System.IO.Ports.SerialPort class, HERE is a link to the full documentation by MSDN if you want to check out the rest of the class.
I also found a great article explaining how to fix several common bugs relating to serial ports. Check it out if you get stuck with any odd errors.
Feel free to post questions or feedback! I am always happy to hear constructive comments so I can make improvements.

The pins used will (naturally) be the serial transmit and receive pins. If you want to control specific pins but not necessarily in a serial way, you might be better off working with a parallel port instead. The parallel port voltages are usually more friendly to TTL level logic and can often be driven directly.

Example
2 Aug 2002
This article gives you a jump start on doing serial communication in Windows NT

Introduction

This article is meant to give you a jump start on doing serial communicationin Windows (NT family). The article will provide a class calledCSerialCommHelper which you can use directly to do serial communication in yourapplication. The class that is provided here with this article does usesoverlapped IO. You donot need to know much about serial communication oroverlapped IO for this article. However, you need to know some about thesynchronization objects like Events and some Windows APIs likeWaitForSingleObject and WaitForMultipleObject etc. Also some basic understandingof windows threads is required - like thread creation and termination.

Introduction

In order for your computer to be able to do serial communication, computerhas to have a serial port. Most of the computers have at least one serial portalso known as COM port ( communication port ) and are generally called COM1 COM2etc. Then there are the device drivers for the serial ports. If you think itover, all you that you need to do in serial communication is either send data orreceive data. In other words, you are doing input/output (IO) to the serialport. The same IO is done with disk based files. Hence there is no surprise thatthe APIs for reading and writing to a file apply to serial ports as well. Whenyou send data to the serial port its in terms of bytes but when it leaves theserial port it is in the form of bits. Similarly, when the data arrives at theserial port, its in bit format and when you get data you get it in bytes.

Without any further discussion lets get started.

Opening the COM port

The first and the foremost step in doing a serial communication is to openthe desired port. Lets say you have your device hooked to COM1 you can open theCOM port using following API:

The third fifth and seventh parameters have to be what theyare in the above example by law. We want to open the file ( the COM port ) in anoverlapped fashion - that's why the sixth param is FILE_FLAG_OVERLAPPED. We willget into the details of overlapped IO a bit later. As you must have guessed fromthe name , CreateFile API can be used to create a file (disk based) and alsoit can be used to open an existing file.

To Windows a serial port or a disk based file both are IO devices . So, in orderto open an existing file ( serial port ) all we need to know the name of thedevice ( COM1) and pass the creation flags as OPEN_EXISTING.

If a COM port is opened successfully, the API returns handle to the com portjust like a handle to a file. However, if the system could not open the COMport, it would return INVALID_HANDLE_VALUE . And you can get the reason bycalling GetLastError. One of the common errors while opening a COM port isthat the COM port is already opened by some other application and in that caseyou would get ERROR_ACCESS_DENIED (5). Similarly if you by mistake opened a COMport that doesn't exist , you would get ERROR_FILE_NOT_FOUND as the lasterror.

Note: Remember not to do make any function calls (like ASSERT) before calling

or you would get 0. Once you have opened the com port all you need to do now is to start usingit.

Reading and Writing

Now, once you have a com port open, you may want to send some data tothe connected device. For example, lets say you want to send 'Hello'to the device(e.g., another PC). When you want to send the data across theserial port, you need to write to the serial port just like you would write to afile. You would use following API:

where data contains 'Hello' . Lets say in response to your 'Hello' , the device sends you'Hi' . So you need to read the data. Again ,you would usefollowing API:

For now do not try to understand everything. We will get toall this later. All this sounds very simple. Right?

Now lets start digging into issues.

Issues with serial communication

Just now I said, in response to your 'Hello', the device may send you'Hi' back and you would like to read that. But the problem here isthat you don't know when the device is going to respond? Or will it everrespond? When should you start to read from the port. One option is that as soonas you made call to WriteFile, you make call to ReadFile . If no data is thereyou need to make read again later on. This leads to what is called polling. Youkeep polling the port for data. This model does not really seem to be agood one. It would be nice if somehow you were notified by the system when datahas arrived and only then would you make call to ReadFile. This is event drivenapproach and fits well into Windows programming. And good news is that such amodel is possible .

Another issue with the serial communication is that since it always occursbetween two devices, the two devices need to agree on how they talk to eachother. Each side needs to follow certain protocols to conduct business. Sinceits the serial port that actually carries out the communication, we need toconfigure the serial port. There is an API available for exact same purpose.Following is the API:

The first parameter is the handle to COM port and the secondparamter is what is called device control block (DCB) . The DCB is a structdefined in winbase.h and has 28 data members. For example, we need to specifybaud rate at which the COM port operates, you need to set the BaudRate memberof the struct . Baud rate is usual 9600 (bps) . But the two devices have to usethe same baud rate to conduct business. Similarly if you want to use parity youneed to set Parity member of the struct. Again the two devices have touse same parity. Some of the data members are reserved and have to be 0. I havefound it easier to get the current DCB struct and then set those members whichwe are interested in changing. Following code gets the current dcb and sets someof the fields:

Most of the time you won't need to change theother fields of this structure. But if you need to change the structure you needto be very careful about the fields as changing the fields will affect thebehavior of the serial communication and hence you should be very sure what youwant to change.

Event Driven Approach

Coming back to our earlier problem with the reading of data. If we do notwant to keep polling the COM port for any data then we need to have somekind of event mechanism available. Fortunately there is a way that you can askthe system to notify you when certain events happen. The API to use is

The first parameter is the handle to the openCOM port. The second parameter is used to specify a list of events which we areinterested in.
The events that need to be specified in the mask depends upon the applicationneeds. For simplicity, lets say we are interested in getting notifiedwhenever a character arrives at the serial port, we would need to specify

as the event mask. Similarly if we are interested to know when all thedata has been sent, we need to specify EV_TXEMPTY flag also. So out call wouldlook like this:

The interesting thing here is thatalthough we told system about the events of our interest, we did not howevertold system what to do when these events occur. Like how would system let usknow that a particular event occurred. An obvious thing seems to be a callbackmechanism. But there is not such mechanism available. Here is when things get alittle tricky. In order for system to let us know about the communication eventoccurrence, we need to call WaitCommEvent This function waits for theevents specified in SetCommMask. But if your think a little more, it sounds likewe are turning a notification mechanism back to polling mechanism. Actually itseven worse that than . WaitCommEvent blocks till an event occurs. So what'stheuse of WaitCommEvent ? Well , the answer lies in overlapped IO.
If you look at the WaitCommEvent signature it looks like this:

The third parameter is thekey here.

Think of overlapped IO as asynchronous IO. Whenever a function makes a call andspecifies the overlapped IO structure, it means that try to do the currentoperation but if you are not able to complete it immediately let me know whenyou are done with this IO. The way system lets you know about the completion isby setting an kernel event object that is part of the lpOverlapped structure.So, all you do is spawn a thread and make the thread wait for that event objectusing one of the WaitForSingleObject APIs.
Lets look at the overlapped structure:

The last parameter is the event handle that youneed to create . This event is generally a manual reset event. When you make acall like WaitCommEvent passing overlapped structure as the last parameter,and the system could not complete call meaning it did not see any characters atthe port, it would return immediately but would return FALSE. If you now make acall to GetLastError you would get ERROR_IO_PENDING which means that the callhas been accepted but no characters have yet arrived at the COM port. Also itmeans whenever the characters will arrive, the system will set the hEvent of theoverlapped structure that you passed in. So if your thread would wait for singleobject on hEvent and you pass INFINITE, then whenever your Wait fn. returns

it means some character has arrived or all the data in theoutput buffer has been sent.

In our current case since we are interested in more than one events we wouldneed to check what event did we get by making call to GetCommMask and checkingthe DWORD against each event. Following pseudo code will explain it:

You can read the data from the com portand reset the event and make the call to WaitCommEvent again and so on.

If you understood the above code , youwill understand the whole of this article and the source code provided. The above piece of code is simple using the overlapped IO method to do itsjob.

Once we have received the indication that the data has arrived we need to readthe data. Important thing to note here is that the when data arrives at theserial port, it is copied over to system buffer. The data is removed fromthe system buffer only when you have read the data using API such as ReadFile. Like any buffer, system buffer has a limited size. So if you do not read thedata from the buffers quick enough the system buffers can be become full if moredata is arriving. What happens to further data depends upon the configurationthat you have set in the device configuration block (in call to SetCommState ).Usually the applications do some kind of handshaking at the application levelbut you can also make configurations such that the com port does not accept anyfurther data upon buffer-full events. But all that is beyond the scope of thisdiscussion. If possible its always better to have applications themselvesimplementing some kind of handshaking - like do not send next block ofdata until you get okay for the first block. Generally this kind of handshakingis implemented using some sort of ACK / NAK and ENQ protocol.

In order for us to read data we need to use ReadFile API. ReadFile API has tospecify how much data to read. Lets say we are monitoring character arrivals and10 characters arrive at the port. As soon as first character arrives at the portthe system will set the overlapped structure's event object and out

will return. Next we would need to read the data. So how muchdata should we read? Should we read 1 byte or 10 bytes? That is a goodquestion. The way it works is as follows (Note: thisis not documented anywhere but this is what I have found by research onWin2K,NT4.0) :

When one (or more) characters arrive at the port, the event object associated with theoverlapped structure set once. Now lets say that you made a call to read and youread 1 character. After reading 1 character , you would finally Reset theoverlapped structure's event object. Now you would go back to the WaitCommEventbut it would return false since no 'new' character has arrived. So youwill not be able to read any more characters. Now when another characterarrives, system will set the overlapped event and you would read one morecharacter but this time it will be the character that had arrived earlier andyou never read. This clearly is a problem.

So what is the solution? The easiest solution is that as soon as you got theevent object indicating the arrival of a character, you should read all thecharacters that are present in the port. (If you are familiar with win API

you can draw a analogy here.)

So again the question remains how manycharacters to read. The answer is read all the characters in a loop using

. Here is the pseudo code

ReadFile API has following signature:

The first parameter is as usual the com port, the last parameter is theoverlapped structure. Again we need to create a manual reset event and pass theoverlapped structure to the ReadFile function. Again if you issue a read for say10 bytes and there is no data available , ReadFile will return FALSE and

will return Windows ERROR_IO_PENDING and the system will set theoverlapped event when the overlapped operation(read ) completes.

As you can see ReadFile returns dwBytesRead which as is clear returns the numberof bytes read. If there are no bytes remaining, the dwBytesRead will return 0.Lets say there are 11 bytes that have arrived and you read 10 charactersin the first go in while loop. In the first go 10 characters will be returned in

. In the second go with while loop, the dwBytesRead will return 1.Now in the third go the dwBytesRead will return 0 and you will break out of thewhile loop. This allows you to read all the data. In this approach ,if you noticed, wenever really took advantage of the overlapped structure that wepassed to the ReadFile function but we still need to pass it because we openedthe COM port in Overlapped manner.

And finally when you want to send data to other device, you need to call

. WriteFile is not even worth discussing.

There is one more thing that needs to be taken into account before we move onand that is communication timeouts. Its important to set the timeout toproper values for things to work. The API to do so is:

COMTIMEOUTS is a structure with following members:

For a description of all these fields consult MSDN documentation. But one thingI want to point out is this:

'...A value of MAXDWORD, combined with zero values for both the ReadTotalTimeoutConstantand ReadTotalTimeoutMultiplier members, specifies that the read operationis to return immediately with the characters that have already been received,even if no characters have been received...'

This is exactly what we want . We do NOT want the ReadFile to get stuck if thereis no data available as we will know with WaitCommEvent API. and also

'...A value of zero for both the WriteTotalTimeoutMultiplierand WriteTotalTimeoutConstant members indicates that total time-outs arenot used for write operations...'

is what we want. In short we need to dothis:

Now we have discussed almost everything that needs to be discussed for the sakeof this article.

Putting it all together

All this I have put together in a form of two classes:

  • The main class is CSerialCommHelper - the main class that does performsall the communication .
  • The helper class called CSerialBuffer that is an internal buffer used bythe CSerialCommHelper.

Here is the main API of the CSerialCommHelper:

and the interface for CSerialBuffer is :

Here is the logic and working behind theclasses:

First of let me show you how to use the class.In your application create an object of CSerialCommHelper like this:

Call m_theCommPort.Init() passing in the necessary information. If you want youcan use default values.
Next call m_theCommPort.Start()

If you want to get notification about when thesome data is available you can get the kernel event object to wait on bycalling m_theCommPort.GetWaitForEvent().

What CSerialCommHelper does is that on call toInit(), it opens the specified COM port and also starts a thread. The threadstarts 'listening' for any incoming data and once the data has beenreceived it reads all the data into a local buffer which is of type

. Once its done reading all the data it sets the event in case youwant to get the notification. Now you have three options
  • read all the data by calling ReadAvailable which reads all the data .
  • read up to some character by calling and passing character up to which you want to read.
  • read N character calling Read_N passing the numbers to be read.

There is one more thing that needs to be paidattention. If you want to read 10 characters but there are only 5characters in the local buffer, the read_N makes a blocking call and waits forthe timeout passed as the last parameter . Same is true for Read_Upto.

One more thing. If there are 10 characters inthe local buffer but you made a call to Read_N for 5 characters you will bereturned first 5 characters. If you made a next call Read_N for 5 charactersagain, it would returned next 5 characters.

Serial Port Programming In C#

That's all there is to it.

Windows Serial Port Programming Example C

If you think I have left something please feelfree to email me at ashishdhar@hotmail.com

C++ Serial Port Programming