You are on page 1of 39

Introduction

This article is meant to give you a jump start on doing serial communication in
Windows (NT family). The article will provide a class called CSerialCommHelper which
you can use directly to do serial communication in your application. The class that is
provided here with this article does uses overlapped IO. You donot need to know
much about serial communication or overlapped IO for this article. However, you
need to know some about the synchronization objects like Events and some Windows
APIs like WaitForSingleObject and WaitForMultipleObject etc. Also some basic
understanding of windows threads is required - like thread creation and termination.

Introduction

In order for your computer to be able to do serial communication, computer has to


have a serial port. Most of the computers have at least one serial port also known as
COM port ( communication port ) and are generally called COM1 COM2 etc. Then
there are the device drivers for the serial ports. If you think it over, all you that you
need to do in serial communication is either send data or receive data. In other
words, you are doing input/output (IO) to the serial port. The same IO is done with
disk based files. Hence there is no surprise that the APIs for reading and writing to a
file apply to serial ports as well. When you send data to the serial port its in terms of
bytes but when it leaves the serial port it is in the form of bits. Similarly, when the
data arrives at the serial 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 open the
desired port. Lets say you have your device hooked to COM1 you can open the COM
port using following API:

HANDLE m_hCommPort = ::CreateFile(szPortName,


GENERIC_READ|GENERIC_WRITE,//access ( read and write)
0, //(share) 0:cannot share the COM port
0, //security (None)
OPEN_EXISTING,// creation : open_existing
FILE_FLAG_OVERLAPPED,// we want overlapped operation
0// no templates file for COM port...
);

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

To Windows a serial port or a disk based file both are IO devices . So, in order to
open an existing file ( serial port ) all we need to know the name of the device
( COM1) and pass the creation flags as OPEN_EXISTING.
If a COM port is opened successfully, the API returns handle to the com port just like
a handle to a file. However, if the system could not open the COM port, it would
return INVALID_HANDLE_VALUE . And you can get the reason by calling
GetLastError. One of the common errors while opening a COM port is that the COM
port is already opened by some other application and in that case you would get
ERROR_ACCESS_DENIED (5). Similarly if you by mistake opened a COM port that
doesn't exist , you would get ERROR_FILE_NOT_FOUND as the last error.

Note: Remember not to do make any function calls (like ASSERT) before calling
GetLastError or you would get 0. Once you have opened the com port all you need
to do now is to start using it.

Reading and Writing

Now, once you have a com port open, you may want to send some data to the
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 the serial port, you need to
write to the serial port just like you would write to a file. You would use following
API:

iRet = WriteFile (m_hCommPort,data,dwSize,&dwBytesWritten ,&ov);

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 use following API:

abRet = ::ReadFile(m_hCommPort,szTmp ,sizeof(szTmp ),


&dwBytesRead,&ovRead) ;

For now do not try to understand everything. We will get to all 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 is that you don't know when the
device is going to respond? Or will it ever respond? When should you start to read
from the port. One option is that as soon as you made call to WriteFile, you make call
to ReadFile . If no data is there you need to make read again later on. This leads to
what is called polling. You keep polling the port for data. This model does not really
seem to be a good one. It would be nice if somehow you were notified by the system
when data has arrived and only then would you make call to ReadFile. This is event
driven approach and fits well into Windows programming. And good news is that
such a model is possible .

Another issue with the serial communication is that since it always occurs between
two devices, the two devices need to agree on how they talk to each other. Each side
needs to follow certain protocols to conduct business. Since its the serial port that
actually carries out the communication, we need to configure the serial port. There is
an API available for exact same purpose. Following is the API:
SetCommState ( HANDLE hFile, LPDCB lpDCB)

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

DCB dcb = {0};


dcb.DCBlength = sizeof(DCB);

if (!::GetCommState (m_hCommPort,&dcb))
{
TRACE ("CSerialCommHelper : Failed to Get Comm State Reason: %d",
GetLastError());
return E_FAIL;
}

dcb.BaudRate = dwBaudRate;
dcb.ByteSize = byByteSize;
dcb.Parity = byParity;
if ( byStopBits == 1 )
dcb.StopBits = ONESTOPBIT;
else if (byStopBits == 2 )
dcb.StopBits = TWOSTOPBITS;
else
dcb.StopBits = ONE5STOPBITS;

if (!::SetCommState (m_hCommPort,&dcb))
{
ASSERT(0);
TRACE ( "CSerialCommHelper : Failed to Set Comm State Reason: %d",
GetLastError());
return E_FAIL;
}
TRACE ( "CSerialCommHelper : Current Settings, (Baud Rate %d; Parity %d; "
"Byte Size %d; Stop Bits %d", dcb.BaudRate,

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

Event Driven Approach

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

SetCommMask( HANDLE hHandle,DWORD dwEvtMask)


The first parameter is the handle to the open COM port. The second parameter is
used to specify a list of events which we are interested in.
The events that need to be specified in the mask depends upon the application
needs. For simplicity, lets say we are interested in getting notified whenever a
character arrives at the serial port, we would need to specify EV_RXCHAR as the event
mask. Similarly if we are interested to know when all the data has been sent, we
need to specify EV_TXEMPTY flag also. So out call would look like this:

SetCommMask( m_hCommPort,EV_TXTEMPTY|EV_RXCHAR);

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

BOOL WaitCommEvent(HANDLE hCommPort, LPDWORD dwEvtMask,LPOVERLAPPED


lpOverlapped);

The third parameter is the key here.

Think of overlapped IO as asynchronous IO. Whenever a function makes a call and


specifies the overlapped IO structure, it means that try to do the current operation
but if you are not able to complete it immediately let me know when you are done
with this IO. The way system lets you know about the completion is by 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 object using one of the
WaitForSingleObject APIs.
Lets look at the overlapped structure:

typedef struct _OVERLAPPED {


DWORD Internal;
DWORD InternalHigh;
DWORD Offset;
DWORD OffsetHigh;
HANDLE hEvent;
} OVERLAPPED, *LPOVERLAPPED;

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

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

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

unsigned __stdcall CSerialCommHelper::ThreadFn(void*pvParam)


{
OVERLAPPED ov;
memset(&ov,0,sizeof(ov));
ov.hEvent = CreateEvent( 0,true,0,0);
HANDLE arHandles[2];
arHandles[0] = apThis->m_hThreadTerm;

DWORD dwWait;
SetEvent(apThis->m_hThreadStarted);
while ( abContinue )
{

BOOL abRet = ::WaitCommEvent(apThis->m_hCommPort,&dwEventMask, &ov) ;


if ( !abRet )
{

ASSERT( GetLastError () == ERROR_IO_PENDING);


}

arHandles[1] = ov.hEvent ;

dwWait = WaitForMultipleObjects (2,arHandles,FALSE,INFINITE);


switch ( dwWait )
{
case WAIT_OBJECT_0:
{
_endthreadex(1);
}
break;
case WAIT_OBJECT_0 + 1:
{
DWORD dwMask;
if (GetCommMask(apThis->m_hCommPort,&dwMask) )
{
if ( dwMask & EV_TXEMPTY )
TRACE("Data sent");
ResetEvent ( ov.hEvent );
continue;
}
else
{
//read data here and reset ov.hEvent
}
}
}//switch
}//while
return 0;
}

If you understood the above code , you will 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 its job.

Once we have received the indication that the data has arrived we need to read the
data. Important thing to note here is that the when data arrives at the serial port, it
is copied over to system buffer. The data is removed from the 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 the data from the buffers quick
enough the system buffers can be become full if more data is arriving. What happens
to further data depends upon the configuration that you have set in the device
configuration block (in call to SetCommState ). Usually the applications do some kind
of handshaking at the application level but you can also make configurations such
that the com port does not accept any further data upon buffer-full events. But all
that is beyond the scope of this discussion. If possible its always better to have
applications themselves implementing some kind of handshaking - like do not send
next block of data until you get okay for the first block. Generally this kind of
handshaking is 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 to
specify how much data to read. Lets say we are monitoring character arrivals and 10
characters arrive at the port. As soon as first character arrives at the port the system
will set the overlapped structure's event object and out WaitSingleObject will
return. Next we would need to read the data. So how much data should we read?
Should we read 1 byte or 10 bytes? That is a good question. The way it works is as
follows (Note: this is not documented anywhere but this is what I have found by
research on Win2K,NT4.0) :

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

So what is the solution? The easiest solution is that as soon as you got the event
object indicating the arrival of a character, you should read all the characters that are
present in the port. (If you are familiar with win API MsgWaitForMultipleObjects
you can draw a analogy here.)

So again the question remains how many characters to read. The answer is read all
the characters in a loop using ReadFile. Here is the pseudo code

threadFn...

WaitCommEvent(m_hCommPort,&dwEventMask, &ov) ;
if ( WaitForSingleObject(ov.hEvent,INFINITE) == WAIT_OBJECT_0)
{
char szBuf[100];
memset(szBuf,0,sizeof(szBuf));
do
{
ReadFile( hPort,szBuf,sizeof(szBuf),&dwBytesRead,&ov);
}while (dwBytesRead > 0 );
}

ReadFile API has following signature:

BOOL ReadFile( HANDLE hFile, // handle to file


LPVOID lpBuffer, // data buffer
DWORD nNumberOfBytesToRead, // number of bytes to read
LPDWORD lpNumberOfBytesRead, // number of bytes read
LPOVERLAPPED lpOverlapped // overlapped buffer );

The first parameter is as usual the com port, the last parameter is the overlapped
structure. Again we need to create a manual reset event and pass the overlapped
structure to the ReadFile function. Again if you issue a read for say 10 bytes and
there is no data available , ReadFile will return FALSE and GetLastError will return
ERROR_IO_PENDING and the system will set the overlapped event when the
overlapped operation(read ) completes.

As you can see ReadFile returns dwBytesRead which as is clear returns the number
of 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 characters in the first go
in while loop. In the first go 10 characters will be returned in dwBytesRead. 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 the while loop. This allows you to
read all the data. In this approach ,if you noticed, we never really took advantage of
the overlapped structure that we passed to the ReadFile function but we still need
to pass it because we opened the COM port in Overlapped manner.

And finally when you want to send data to other device, you need to call WriteFile.
WriteFile is not even worth discussing.

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

SetCommTimeouts ( HANDLE hCommPort, LPCOMMTIMEOUTS lpCommTimeOuts)

COMTIMEOUTS is a structure with following members:

typedef struct _COMMTIMEOUTS {


DWORD ReadIntervalTimeout;
DWORD ReadTotalTimeoutMultiplier;
DWORD ReadTotalTimeoutConstant;
DWORD WriteTotalTimeoutMultiplier;
DWORD WriteTotalTimeoutConstant;
} COMMTIMEOUTS,*LPCOMMTIMEOUTS;

For a description of all these fields consult MSDN documentation. But one thing I
want to point out is this:
"...A value of MAXDWORD, combined with zero values for both the
ReadTotalTimeoutConstant and ReadTotalTimeoutMultiplier
members, specifies that the read operation is 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 there is
no data available as we will know with WaitCommEvent API. and also

"...A value of zero for both the WriteTotalTimeoutMultiplier and


WriteTotalTimeoutConstant members indicates that total time-outs are
not used for write operations..."

is what we want. In short we need to do this:

COMMTIMEOUTS timeouts;
timeouts.ReadIntervalTimeout = MAXDWORD;
timeouts.ReadTotalTimeoutMultiplier = 0;
timeouts.ReadTotalTimeoutConstant = 0;
timeouts.WriteTotalTimeoutMultiplier = 0;
timeouts.WriteTotalTimeoutConstant = 0;

if (!SetCommTimeouts(m_hCommPort, &timeouts))
{
ASSERT(0);
TRACE ( "CSerialCommHelper : Error setting time-outs. %d",GetLastError());
return E_FAIL;
}

Now we have discussed almost everything that needs to be discussed for the sake of
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 performs all
the communication .
• The helper class called CSerialBuffer that is an internal buffer used by the
CSerialCommHelper.

Here is the main API of the CSerialCommHelper:

inline bool IsInputAvailable()


inline bool IsConnection() {return m_abIsConnected ;}
inline void SetDataReadEvent() { SetEvent ( m_hDataRx ); }
HRESULT Read_N (std::string& data,long alCount,long alTimeOut);
HRESULT Read_Upto (std::string& data,char chTerminator ,
long* alCount,long alTimeOut);
HRESULT ReadAvailable(std::string& data);
HRESULT Write (const char* data,DWORD dwSize);
HRESULT Init(std::string szPortName, DWORD dwBaudRate,BYTE byParity,
BYTE byStopBits,BYTE byByteSize);
HRESULT Start();
HRESULT Stop();
HRESULT UnInit();

and the interface for CSerialBuffer is :

inline void LockBuffer();


inline void UnLockBuffer();
void AddData( char ch ) ;
void AddData( std::string& szData ) ;
void AddData( std::string& szData,int iLen ) ;
void AddData( char *strData,int iLen ) ;
std::string GetData() ;
void Flush();
long Read_N( std::string &szData,long alCount,HANDLE& hEventToReset);
bool Read_Upto( std::string &szData,char chTerm,long &alBytesRead,
HANDLE& hEventToReset);
bool Read_Available( std::string &szData,HANDLE & hEventToReset);
inline long GetSize() ;
inline bool IsEmpty() ;

Here is the logic and working behind the classes:

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

CSerialCommHelper m_theCommPort;

Call m_theCommPort.Init() passing in the necessary information. If you want you


can use default values.
Next call m_theCommPort.Start()

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

What CSerialCommHelper does is that on call to Init(), it opens the specified COM
port and also starts a thread. The thread starts "listening" for any incoming data and
once the data has been received it reads all the data into a local buffer which is of
type CSerialBuffer . Once its done reading all the data it sets the event in case you
want 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 Read_Upto 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 paid attention. If you want to read 10
characters but there are only 5 characters in the local buffer, the read_N makes a
blocking call and waits for the timeout passed as the last parameter . Same is true
for Read_Upto.

One more thing. If there are 10 characters in the local buffer but you made a call to
Read_N for 5 characters you will be returned first 5 characters. If you made a next
call Read_N for 5 characters again, it would returned next 5 characters.
That's all there is to it.

If you think I have left something please feel free to email me at


ashishdhar@hotmail.com

Introduction

Serial Data transmission seems a bit difficult for those who are new to the world of
serial communication and VC++. Long ago, I had searched on codeguru.com for
some help on serial data transmission and I got some valuable information. It was
my dream to develop a simple class for implementing serial data transmission since
then.

After getting seven months practical experience in the field of serial communication,
I have developed a simple class for implementing serial transmission using WinAPI
functions. Before going into the details of this class it is essential to know the basics
of serial data transmission.

Serial Data Tranmission Basics

In serial data transmission the data is transmitted in serial format with the LSB of
the byte to be transmitted, shifted out first among the data bits. The general format
for serial transmission is Start Bit + Data Bits + Parity Bit (Optional) + Stop Bit.

The Parity bit is optional. It is used for error checking in communication. You can
enable or disable parity checking by software modifications. Also, you can specify
which parity you would like to use, either 'EVEN' or 'ODD' through software.

The various steps to be performed for sending and receiving data through the serial
port of a PC are listed below:-

1. Open the communication port


2. Configure the communication port by setting the Baud rate, parity, no. of data
bits, etc.
3. Set time-outs for communication.
4. Write data to the port.
5. Read data from the port.
6. Close the port.

Opening The Serial Port

The CreateFile() function opens a communications port. There are two ways to call
CreateFile() to open the port - OVERLAPPED and NON-OVERLAPPED. You can open a
Communication Port for OVERLAPPED IO operation and NON-OVERLAPPED IO operation.
The CSerialCom class is written for NON-OVERLAPPED IO operation. For more details
on OVERLAPPED & NON-OVERLAPPED IO, please refer to the MSDN documentation.

Configuring Serial Ports


The most critical phase in serial communication programming is configuring the port
settings with the DCB structure. Erroneously initializing the DCB structure is a
common problem. When a serial communications function does not produce the
expected results, the DCB structure may be in error. A call to the CreateFile()
function opens a serial port with default port settings. Usually, the application needs
to change the defaults. You must set the Baud rate for communication, Parity
functions, no. of Stop Bits, etc. in accordance with the requirements of the external
device by calling appropriate WinAPI functions.

Configuring Time-Outs

An application must always set communication time-outs using the COMMTIMEOUTS


structure each time it opens a communication port. If this structure is not
configured, the port uses default time-outs supplied by the driver, or time-outs from
a previous communication application. By assuming specific time-out settings when
the settings are actually different, an application can have read/write operations that
never complete or complete too often. You must configure the read & write time-outs
by calling the appropriate WinAPI functions.

Writing to a Serial Port

The WriteFile() function transfers data through the serial connection to another
device. Before calling this function, an application must open and configure a serial
port.

Reading from a Serial Port

An application calls the ReadFile() function to receive data from a device at the
other end of a serial connection.

Closing a Serial Port

You must close the communications port after serial transmission in order to make
this port available for other applications which use this resource. As long as you are
working with a port (i.e. the port is in an open state), other threads or applications
will not be able to access to this port till you close the handle to that port in NON-
OVERLAPPED IO operation. Call the CloseHandle() function to close the serial port.
CloseHandle() has one parameter, which is the handle returned by the
CreateFile() call that opened the port.

CSerialCom Class

The CSerialCom class uses six member functions to achieve the above mentioned
functionality. They are:

BOOL CSerialCom::OpenPort(CString portname)


{
portname= "//./" + portname;

hComm = CreateFile(portname,
GENERIC_READ | GENERIC_WRITE,
0,
0,
OPEN_EXISTING,
0,
0);
if(hComm==INVALID_HANDLE_VALUE){
return false;}
else
return true;

The OpenPort() member function opens a communication port for data transmission.
The parameter to be passed to this function is a string containing the port name. For
example "com1" for COM1, "com2" for COM2 etc. If the function succeeds the return
value is true, otherwise it is false.

BOOL CSerialCom::ConfigurePort(DWORD BaudRate, BYTE ByteSize,


DWORD fParity, BYTE Parity, BYTE StopBits)
{
if((m_bPortReady = GetCommState(hComm, &m_dcb))==0)
{
MessageBox("GetCommState Error","Error",MB_OK+MB_ICONERROR);
CloseHandle(hComm);
return false;
}

m_dcb.BaudRate =BaudRate;
m_dcb.ByteSize = ByteSize;
m_dcb.Parity =Parity ;
m_dcb.StopBits =StopBits;
m_dcb.fBinary=TRUE;
m_dcb.fDsrSensitivity=false;
m_dcb.fParity=fParity;
m_dcb.fOutX=false;
m_dcb.fInX=false;
m_dcb.fNull=false;
m_dcb.fAbortOnError=TRUE;
m_dcb.fOutxCtsFlow=FALSE;
m_dcb.fOutxDsrFlow=false;
m_dcb.fDtrControl=DTR_CONTROL_DISABLE;
m_dcb.fDsrSensitivity=false;
m_dcb.fRtsControl=RTS_CONTROL_DISABLE;
m_dcb.fOutxCtsFlow=false;
m_dcb.fOutxCtsFlow=false;

m_bPortReady = SetCommState(hComm, &m_dcb);


if(m_bPortReady ==0)
{
MessageBox("SetCommState Error","Error",MB_OK+MB_ICONERROR);
CloseHandle(hComm);
return false;
}
return true;
}

The ConfigurePort() member function configures a communication port for data


transmission. The parameters to be passed to this function are given below.
DWORD BaudRate
It represents the Baud rate for communication supported by external device. For
example, you can give this parameter as 9600 or CBR_9600 for a BaudRate of 9600.
The available Standard Baud rates supported by a PC are CBR_110 ,CBR_300
,CBR_600 ,CBR_1200,CBR_2400,CBR_4800,CBR_9600,CBR_14400,
CBR_19200,CBR_38400,CBR_56000,CBR_57600,CBR_115200,CBR_128000,CBR_256000

BYTE ByteSize
This represents the number of bits in the bytes transmitted and received. Standard
values are 8 or 4.

DWORD fParity
Specifies whether parity checking is enabled. If this parameter is TRUE, parity
checking is performed and errors are reported. If FALSE, no parity checking is
performed.

BYTE Parity
Specifies the parity scheme to be used. This member can be one of the following
values:

• EVENPARITY
• MARKPARITY
• NOPARITY
• ODDPARITY
• SPACEPARITY

BYTE StopBits
Specifies the number of stop bits to be used. This member can be one of the
following values:

• ONESTOPBIT
• ONE5STOPBITS
• TWOSTOPBITS

NOTE
The ConfigurePort() function is written on the assumption that the communication
flow control is completely controlled on the basis of the protocol supported by the
external device. It transmits and receives data without checking CTS/RTS and
Xon/Xoff hardware flow control. You can modify this to your requirements by
changing the values of the members of DCB which are responsible for it, in the
implementation of ConfigurePort() in SerialCom.cpp.

ConfigurePort(CBR_9600, 8, true, EVENPARITY , ONESTOPBIT )

If the function succeeds the return value is true otherwise false.

BOOL CSerialCom::SetCommunicationTimeouts(DWORD ReadIntervalTimeout,


DWORD ReadTotalTimeoutMultiplier,
DWORD ReadTotalTimeoutConstant,
DWORD WriteTotalTimeoutMultiplier,
DWORD WriteTotalTimeoutConstant)
{
if((m_bPortReady = GetCommTimeouts (hComm, &m_CommTimeouts))==0)
return false;
m_CommTimeouts.ReadIntervalTimeout =ReadIntervalTimeout;
m_CommTimeouts.ReadTotalTimeoutConstant =ReadTotalTimeoutConstant;
m_CommTimeouts.ReadTotalTimeoutMultiplier =ReadTotalTimeoutMultiplier;
m_CommTimeouts.WriteTotalTimeoutConstant = WriteTotalTimeoutConstant;
m_CommTimeouts.WriteTotalTimeoutMultiplier =WriteTotalTimeoutMultiplier;
m_bPortReady = SetCommTimeouts (hComm, &m_CommTimeouts);

if(m_bPortReady ==0)
{
MessageBox("StCommTimeouts function failed",
"Com Port Error",MB_OK+MB_ICONERROR);
CloseHandle(hComm);
return false;
}
return true;
}

The SetCommunicationTimeouts() member function sets the write & read timeouts
for data transmission. The parameters to be passed to this function are given below.

DWORD ReadIntervalTimeout
Specifies the maximum time, in milliseconds, allowed to elapse between the arrival
of two characters on the communications line. During a ReadFile() operation, the
time period begins when the first character is received. If the interval between the
arrival of any two characters exceeds this amount, the ReadFile operation is
completed and any buffered data is returned. A value of zero indicates that interval
time-outs are not used. A value of MAXDWORD, combined with zero values for both the
ReadTotalTimeout constant and ReadTotalTimeoutMultiplier members, specifies
that the read operation is to return immediately with the characters that have
already been received, even if no characters have been received.

ReadTotalTimeoutConstant
Specifies the constant, in milliseconds, used to calculate the total time-out period for
read operations. For each read operation, this value is added to the product of the
ReadTotalTimeoutMultiplier member and the requested number of bytes. A value
of zero for both the ReadTotalTimeoutMultiplier and ReadTotalTimeoutConstant
members indicates that total time-outs are not used for read operations.

ReadTotalTimeoutMultiplier
Specifies the multiplier, in milliseconds, used to calculate the total time-out period for
read operations. For each read operation, this value is multiplied by the requested
number of bytes to be read.

WriteTotalTimeoutConstant
Specifies the constant, in milliseconds, used to calculate the total time-out period for
write operations. For each write operation, this value is added to the product of the
WriteTotalTimeoutMultiplier member and the number of bytes to be written.

WriteTotalTimeoutMultiplier
Specifies the multiplier, in milliseconds, used to calculate the total time-out period for
write operations. For each write operation, this value is multiplied by the number of
bytes to be written.

A value of zero for both the WriteTotalTimeoutMultiplier and


WriteTotalTimeoutConstant members indicates that total time-outs are not used
for write operations.

For example, if your device transmits a block of characters with a max. timeout value
of 500 ms between each characters, you can set the time-out function as
SetCommunicationTimeouts(0,500,0,0,0);. If the function succeeds the return
value is true otherwise false.

BOOL CSerialCom::WriteByte(BYTE bybyte)


{
iBytesWritten=0;
if(WriteFile(hComm,&bybyte,1,&iBytesWritten,NULL)==0)
return false;
else
return true;
}

The WriteByte() member function writes the data byte to the communication port.
The parameter to be passed to this function is the byte to be transmitted. You can
call this function repeatedly in a loop with your data to be written placed in an array.
Each time you send characters, increment the index of the array and call
WriteByte() till all data bytes are transmitted.

If the function succeeds the return value is true otherwise false.

BOOL CSerialCom::ReadByte(BYTE &resp)


{
BYTE rx;
resp=0;

DWORD dwBytesTransferred=0;

if (ReadFile (hComm, &rx, 1, &dwBytesTransferred, 0))


{
if (dwBytesTransferred == 1)
{
resp=rx;
return true;
}
}
return false;
}

The ReadByte() member function reads data bytes from the communication port.
The parameter to be passed to this function is the address of the variable in which
the received data byte is to be stored. You can call this function repeatedly in a loop
with your received data moved to an array. Each time you receive characters,
increment the index of the array and call ReadByte() till all data bytes are received.
If you know exactly the no. of response bytes from the external device you can call
the ReadByte() function in a loop till all characters are received or a time out occurs.
Sometimes you may not be able to predict the no. of response bytes from the
external device. In that case call ReadByte file repeatedly till you get a time out and
if the character received previously is a character representing end of transmission in
your protocol format, the communication process is successfully completed. For
example, for a device following 3964 the end of transmission is 'ETX' character. So
use the ReadByte() function properly in accordance with the protocol supported by
your external device.

If ReadByte() succeeds the return value is true and the received Byte will be stored
in the location pointed by the address of ReadByte( )'s parameter. If a timeout
occurs the return value will be false.

void CSerialCom::ClosePort()
{
CloseHandle(hComm);
return;
}
The ClosePort() member function closes a communication port which is already in
an Open state.

How To Use 'CSerialCom' Class

Take the following steps to use the CSerialCom class

1. Copy the SerialCom.h & SerialCom.cpp files and paste into your project
directory.
2. In your VC++ IDE, add the files to your project
3. Add the line #include "SerialCom.h" in your dialog's header file
4. Create an instance of the CSerialCom class in your dialog's header file.

You can now call the member functions of CSerialCom when you want to
communicate with external device as shown below.

In your dialog's .cpp file:

// Open Communication Port. Please check functions return value to ensure


whether
// Port opened successfully.
port.OpenPort( );

// Configure Port for Communication. Please check functions return value to


// ensure whether Port is configured successfully.
port.ConfigurePort( );

// Set communication time outs. Please check functions return


// value to ensure whether communication time outs configured
// successfully.
port.SetCommunicationTimeouts( );

// call this function in a loop till all bytes are written. Please check
// functions return value to ensure whether Write operation completed
// successfully.
port.WriteByte();

// call this function in a loop till all bytes are received. Please check
// functions return value to ensure whether Read operation completed
// successfully or a time out occurred.
port.ReadByte( );

// Call this function to close the handle to the port.


// Process the received Data
port.ClosePort();

Note

This code has been tested with an RS-232 Connector, whose TXD pin & RXD pin were
shorted, connected to 'com1' (E.g. for case1: where the no. of databytes to be read
is predefined or constant (in this case 1) and with a Smart Card Reader with baud
rate 9600 supporting 3964 Protocol for communication (Eg. for case2: where the no.
of databytes to be read from the external device is unknown and the end of data
transmission is detected by a timeout with the last character received being the End
of transmission character in that Protocol ('ETX' Character for 3964 Protocol) in win-
98/2000 and it is found working properly.

Some of the explanations given in this article are taken from the MSDN library.

Introduction

The purpose of this article is to demonstrate the use of Win32 functions for serial
port communication in Visual C++. A C++ class CSyncSerialComm has been
developed to implement the following serial port communication operations:

• Open
• Configuration
• Read
• Write
• Close

A background in Visual C++ programming is sufficient to grasp the technical details


of this article. Access to MSDN is required for looking at the definition of functions,
structures etc. used in this article. The article takes a look at only non-overlapped
(synchronous) operations supported by Win32. My next article will show
implementation of serial port communication using overlapped (asynchronous)
structures.

I. Open

Before starting any communication on a serial port, we must first open a connection.
This is achieved by using CreateFile function in Win32. (Those of you familiar with
File I/O must be aware of this function.) The following code is used to open a serial
port connection in non-overlapped mode.

m_hSerialComm = CreateFile(m_pszPortName,
GENERIC_READ | GENERIC_WRITE,
0,
NULL,
OPEN_EXISITING,
0,
NULL);

if (m_hSerialComm == INVALID_HANDLE_VALUE)
//Handle Error Condition

The CreateFile function takes in seven parameters. (Please take a brief moment to
look at this function in MSDN.)

• The first parameter specifies the port name. In our case, this is usually COM,
COM2, COM3 or COM4.
• The second parameter must be GENERIC_READ | GENERIC_WRITE to support
both read and write access.
• The third parameter must always be 0 for serial port communication because
unlike files, serial port access cannot be shared.
• The fourth parameter is used to set security attributes. If no security attribute
needs to be specified, just use NULL.
• The fifth parameter must always be set to OPEN_EXISTING.
• The sixth parameter is used to specify flags and attributes (either 0 or
FILE_ATTRIBUTE_NORMAL can be used).
• The last parameter must always be NULL as we only support non-overlapped
communication.

The HANDLE m_hSerialComm that is returned by the CreateFile function can now be
used for performing operations like Configure, Read and Write.

II. Configuration

After opening connection to a serial port, the next step is usually to configure the
serial port connect settings like Baud Rate, Parity Checking, Byte Size, Error
Character, EOF Character etc. Win32 provides a DCB struct that encapsulates these
settings (refer to MSDN for DCB struct definition). Configuration of the serial port
connection settings is performed in the following three steps:

1. First, we have to access the present settings of the serial port using the
GetCommState function. The function takes in two parameters:
o The first parameter is the HANDLE we received from the call to the
CreateFile function.
o The second parameter is an output parameter, which returns the DCB
structure containing the present settings.
2. Next, using the DCB structure that we obtained from the previous step, we
can modify the necessary settings according to the application needs.
3. Finally, we update the changes by using the SetCommState method.

The following code is a sample shown explaining the use of these functions. (Note: A
number of the fields in the DCB struct are not used in the example. A more
sophisticated application must allow the client to configure these settings.)

DCB dcbConfig;
if(GetCommState(m_hSerialComm, &dcbConfig))
{
dcbConfig.BaudRate = dwBaudRate;
dcbConfig.ByteSize = 8;
dcbConfig.Parity = NOPARITY;
dcbConfig.StopBits = ONESTOPBIT;
dcbConfig.fBinary = TRUE;
dcbConfig.fParity = TRUE;
}

else
//Handle Error Condition

if(!SetCommState(m_hSerialComm, &dcbConfig))
//Handle Error Condition

Another important part of configuration of serial port connection settings is setting


timeouts. Again, Win32 provides a COMMTIMEOUTS struct for setting Read and Write
Timeouts. We are also provided with two functions GetCommTimeouts and
SetCommTimeouts to access, modify, and update the timeout settings. The following
code can be used to set the serial port timeouts:

COMMTIMEOUTS commTimeout;

if(GetCommTimeouts(m_hSerialComm, &commTimeout))
{
commTimeout.ReadIntervalTimeout = 1000 * dwReadTimeOutIntervalInSec;
commTimeout.ReadTotalTimeoutConstant = 1000 *
dwReadTimeOutConstantInSec;
commTimeout.ReadTotalTimeoutMultiplier = 1000 * dwReadTimeOutMultiplier;
commTimeout.WriteTotalTimeoutConstant = 1000 * dwWriteTimeOutInSec;
commTimeout.WriteTotalTimeoutMultiplier = 1000 * dwWriteTimeOutMultiplier;
}

else
//Handle Error Condition

if(!SetCommTimeouts(m_hSerialComm, &commTimeout))
//Handle Error Condition

III. Read

There are many different implementations for reading from a Serial Port Connection.
In the CSyncSerialComm class that is provided with this article, serial communication
events are used in the implementation of the Read operation. There are three
important sets in this implementation.

1. First, we setup a Read Event using the SetCommMask function. (Note: This
function can also be used to set many other different types of serial
communication events.) This event is fired when a character is read and
buffered internally by Windows Operating System. The SetCommMask function
takes in two parameters:
o The first parameter is the HANDLE we received from the call to the
CreateFile function.
o The second parameter is used to specify the event type. To specify a
Read Event, we use EV_RXCHAR flag.
2. After calling the SetCommMask function, we call the WaitCommEvent function to
wait for the event to occur. This function takes in three parameters:
o The first parameter is the HANDLE we received from the call to the
CreateFile function.
o The second parameter is an output parameter, which reports the event
type that was fired.
o The third parameter is a pointer to an OVERLAPPED structure. Since,
our implementation is for Non-Overlapped communication, it must be
set to NULL.
3. Once this event is fired, we then use the ReadFile function to retrieve the
bytes that were buffered internally. The ReadFile function takes in five
parameters:
o The first parameter is the HANDLE we received from the call to the
CreateFile function.
o The second parameter is a buffer that would receive the bytes if the
ReadFile function returns successfully.
o The third parameter specifies the size of our buffer we passed in as the
second parameter.
o The fourth parameter is an output parameter that will notify the user
about the number of bytes that were read.
o The last parameter is always NULL for our purpose since we do not deal
with non-overlapped mode.

In our example, we read one byte at a time and store it in a temporary buffer. This
continues until the case when ReadFile function returns successfully and the fourth
parameter has a value of 0. This indicates that the internal buffer used by Windows
OS is empty and so we stopping reading. The following code shows the
implementation of this technique:

std::stringbuf sb;
DWORD dwEventMask;

if(!SetCommMask(m_hSerialComm, EV_RXCHAR))
//Handle Error Condition

if(WaitCommEvent(m_hSerialComm, &dwEventMask, NULL))


{
char szBuf;
DWORD dwIncommingReadSize;

do
{
if(ReadFile(m_hSerialComm, &szBuf, 1, &dwIncommingReadSize, NULL) != 0)
{
if(dwIncommingReadSize > 0)
{
dwSize += dwIncommingReadSize;
sb.sputn(&szBuf, dwIncommingReadSize);
}
}

else
//Handle Error Condition
} while(dwIncommingReadSize > 0);
}

else
//Handle Error Condition

IV. Write

Write operation is easier to implement than Read. It involves using just one function,
WriteFile. It takes in five parameters similar to ReadFile function. The second
parameter in WriteFile specifies the buffer to be written to the serial port. The
following example shows implementation of Write using WriteFile. It writes one
byte at a time until all the bytes in our buffer are written:

unsigned long dwNumberOfBytesSent = 0;

while(dwNumberOfBytesSent < dwSize /*size of the buffer pszBuf*/)


{
unsigned long dwNumberOfBytesWritten;

if(WriteFile(m_hSerialComm, &pszBuf[dwNumberOfBytesSent], 1,
&dwNumberOfBytesWritten, NULL) != 0)
{
if(dwNumberOfBytesWritten > 0)
++dwNumberOfBytesSent;
else
//Handle Error Condition
}

else
//Handle Error Condition
}

V. Close

After we finish all our communication with the serial port, we have to close the
connection. This is achieved by using the CloseHandle function and passing in the
serial port HANDLE we obtained from the CreateFile function call. Failure to do this
results in hard to find handle leaks. The follow code snippet explains the use of this
function:

if(m_hSerialComm != INVALID_HANDLE_VALUE)
{
CloseHandle(m_hSerialComm);
m_hSerialComm = INVALID_HANDLE_VALUE;
}

Conclusion

I hope my demonstration of the use of Win32 functions in serial port communication


will serve as a starting point. I am sure as you browse through MSDN looking for
these function definitions, you will come across more sophisticated functions that
might be better suited for your application needs.
Introduction

Serial communications is needed in several types of applications, but the Win32 API
isn't a very easy to use API to implement it. Things get even more complicated when
you want to use serial communication in an MFC based program. The classes
provided in the library try to make life a little easier. Its documentation is extensive,
because I want to give you a good background. Serial communication is hard and
good knowledge of its implementation saves you a lot of work, both now and in the
future...

First I'll briefly discuss why serial communications is hard. After reading that chapter
you'll probably be convinced as well that you need a class, which deals with serial
communication. The classes provided in the library are not the only classes, which
handle the serial communication. Many other programmers wrote their own classes,
but I found many of them too inefficient or they weren't robust, scalable or suitable
for non-MFC programs. I tried to make these classes as efficient, reliable and robust
as possible, without sacrificing ease of use too much.

The library has been developed as a public domain library some time ago, but it has
been used in several commercial applications. I think most bugs have been solved,
but unfortunately I cannot guarantee that there are no bugs left. If you find one (or
correct a bug), please inform me so I can update the library.

Why is serial communication that hard?

Serial communication in Win32 uses the standard ReadFile/WriteFile functions to


receive and transmit data, so why should serial communication be any harder then
just plain file I/O? There are several reasons, which I'll try to explain. Some
problems are solved in this library, but some others cannot be solved by a library.

Baudrates, parity, databits, handshaking, etc...

Serial communication uses different formats to transmit data on the wire. If both
endpoints doesn't use the same setting you get garbled data. Unfortunately, no class
can help you with these problems. The only way to cope with this is that you
understand what these settings are all about. Baudrate, parity, databits and stopbits
are often quite easy to find out, because when they match with the other endpoint,
you won't have any problems (if your computer is fast enough to handle the amount
of data at higher baudrates).

Handshaking is much more difficult, because it's more difficult to detect problems in
this area. Handshaking is being used to control the amount of data that can be
transmitted. If the sending machine can send data more quickly then the receiving
machine can process we get more and more data in the receiver's buffer, which will
overflow at a certain time. It would be nice when the receiving machine could tell the
sending machine to stop sending data for a while, so it won't overflow the receiver's
buffers. This process of controlling the transmission of data is called handshaking
and there are basically three forms of handshaking:

1. No handshaking, so data is always send even if the receiver cannot handle the
data anymore. This can lead to data loss, when the sender is able to transmit
data faster then the receiver can handle. Of course this option isn't
recommended, but it can be used for situations where only a few bytes are
transmitted once in a while.
2. Hardware handshaking, where the RTS/CTS lines are used to indicate if data
can be sent. This mode requires that both ports and the cable support
hardware handshaking. Hardware handshaking is the most reliable and
efficient form of handshaking available, but is hardware dependant. Make sure
you have a proper cable, which is fully wired. There are a lot of wrong cables
around, so make sure you use the right one.
3. Software handshaking, where the XOFF/XON characters are used to throttle
the data. A major drawback of this method is that these characters cannot be
used for data anymore. The XOFF/XON characters are the CTRL-S/CTRL-Q
characters, which cannot be used in the data stream anymore. This makes
software handshaking pretty useless, when you want to send binary data. For
ASCII data it's pretty useful. It's being used on the old UNIX terminals as
well. Scrolling starts and stops with CTRL-S/CTRL-Q on these, so the user
provides its own handshaking there (without even knowing it perhaps).

Problems with handshaking are pretty hard to find, because it will often only fail in
cases where buffers overflow. These situations are hard to reproduce so make sure
that you did setup handshaking correctly and that the used cable is working correct
(if you're using hardware handshaking) before you continue.

The Win32 API provides more handshaking options, which aren't directly supported
by this library. These types of handshaking are rarely used, so it would probably only
complicate the classes. If you do need these handshaking options, then you can use
the Win32 API to do that and still use the classes provided by the library.

Asynchronous I/O makes things more complex

File I/O is relatively fast so if the call blocks for a while, this will probably only be a
few milliseconds, which is acceptable for most programs. Serial I/O is much slower,
which causes unacceptable delays in your program. Another problem is that you
don't know when the data arrives and often you don't even know how much data will
arrive.

Win32 provides asynchronous function calls (also known as overlapped operations)


to circumvent these problems. Asynchronous programming is often an excellent way
to increase performance, but it certainly increases complexity as well. This
complexity is the reason that a lot of programs have bugs in their serial
communication routines. This library solves some asynchronous I/O problems by
allowing the programmer to use overlapped and non-overlapped operations mixed
throughout the code, which is often quite convenient.

The event driven programming model doesn't fit

Things get even more complex in GUI applications, which uses the event driven
model that they're used to. This programming model is a heritage of the old 16-bit
days and it isn't even that bad. The basic rule is simple... All events are send using a
windows message, so you need at least one window to receive the events. Most GUI
applications are single-threaded (which is often the best solution to avoid a lot of
complexity) and they use the following piece of code in the WinMain function to
process all messages:

// Start the message-pump until a WM_QUIT is received


MSG msg;
while (::GetMessage(&msg,0,0,0))
{
::TranslateMessage(&msg);
::DispatchMessage(&msg);
}

Because the GetMessage function blocks until there is a message in the message
queue, there's no way to wake up when a serial event occurs. Of course you can set
a timer and check the ports there, but this kind of polling is bad design and certainly
doesn't scale well. Unfortunately the Win32 serial communication API doesn't fit in
this event driven model. It would be easier for GUI applications that the Win32 API
posted a message to a window when a communication event occurred (this is exactly
what the 16-bit implementation looked like).

If you implement your own message-pump, you can use the


MsgWaitForMultipleObjects to wait for a windows message or a windows object to
become signaled. The following piece of code demonstrates how to do this (it
assumes that the event handle that is being used for asynchronous events is stored
in the variable hevtCommEvent):

bool fQuit = false;


while (!fQuit)
{
// Wait for a communication event or windows message
switch
(::MsgWaitForMultipleObjects(1,&hevtCommEvent,FALSE,INFINITE,QS_ALLEVENTS))
{
case WAIT_OBJECT_0:
{
// There is a serial communication event, handle it...
HandleSerialEvent();
}
break;

case WAIT_OBJECT_0+1:
{
// There is a windows message, handle it...
MSG msg;
while (::PeekMessage(&msg,0,0,0,PM_REMOVE))
{
// Abort on a WM_QUIT message
if (msg.message == WM_QUIT) { fQuit = true; break; }

// Translate and dispatch the message


::TranslateMessage(&msg);
::DispatchMessage(&msg);
}
}
break;

default:
{
// Error handling...
}
break;
}
}

This code is much more complex then the simple message pump displayed above.
This isn't that bad, but there is another problem with this code, which is much more
serious. The message pump is normally in one of the main modules of your program.
You don't want to pollute that piece of code with serial communication from a
completely different module. The handle is probably not even valid at all times,
which can cause problems of its own. This solution is therefore not recommended.
MFC and OWL programmers cannot implement this at all, because these frameworks
already their own message pumps. You might be able to override that message
pump, but it probably requires a lot of tricky code and undocumented tricks.

Using serial communications in a single-threaded event-driven program is difficult as


I've just explained, but you probably found that out yourself. How can we solve this
problem for these types of applications? The answer is in the CSerialWnd class,
which posts a message to a window (both the message and window can be specified
by the programmer) whenever a serial event occurs. This makes using a serial port
in GUI based applications much easier. There is also a very thin MFC wrapper class,
which is called CSerialMFC but it's that thin, that it's hardly worth mentioning.

This library cannot perform magic, so how can it send messages without blocking the
message pump? The answer is pretty simple. It uses a separate thread, which waits
on communication events. If such an event occurs, it will notify the appropriate
window. This is a very common approach, which is used by a lot of other (serial)
libraries. It's not the best solution (in terms of performance), but it is suitable for
99% of the GUI based communication applications. The communication thread is
entirely hidden for the programmer and doesn't need to affect your architecture in
any way.

Which class you should use in your code

The current implementation contains four different classes, which all have their own
purpose. The following three classes are available.

• CSerial is the base serial class, which provides a wrapper around the Win32
API. It is a lot easier to use, because it combines all relevant calls in one
single class. It allows the programmer to mix overlapped and non-overlapped
calls, provides reasonable default settings, better readability, etc, etc.
• CSerialEx adds an additional thread to the serial class, which can be used to
handle the serial events. This releases the main GUI thread from the serial
burden. The main disadvantage of this class is that it introduces threading to
your architecture, which might be hard for some people.
• CSerialWnd fits in the Windows event driven model. Whenever a
communication event occurs a message is posted to the owner window, which
can process the event.
• CSerialMFC is an MFC wrapper around CSerialWnd, which make the serial
classes fit better in MFC based programs.
If you're not using a message pump in the thread that performs the serial
communication, then you should use the CSerial or CSerialEx classes. You can use
blocking calls (the easiest solution) or one of the synchronization functions (i.e.
WaitForMultipleObjects) to wait for communication events. This approach is also
used in most Unix programs, which has a similar function as
WaitForMultipleObjects called 'select'. This approach is often the best solution in
non-GUI applications, such as NT services.

The CSerialEx adds another thread to the serial object. This frees the main thread
from blocking, when waiting for serial events. These events are received in the
context of this worker thread, so the programmer needs to know the impact of multi-
threading. If all processing can be done in this thread, then this is a pretty efficient
solution. You need some kind of thread synchronization, when you need to
communicate with the main GUI thread (i.e. for progress indication). If you need to
communicate a lot with the main GUI thread, then it is probably better to use the
CSerialWnd class. However, if you don't communicate a lot with the main thread,
then this class can be a good alternative.

GUI applications, which want to use the event-driven programming model for serial
communications should use CSerialWnd. It is a little less efficient, but the
performance degradation is minimal if you read the port efficiently. Because it fits
perfectly in the event-driven paradigm the slight performance degradation is a
minimal sacrifice. Note that you can use CSerial in GUI based applications (even
MFC/WTL based), but then you might block the message pump. This is, of course,
bad practice in in a commercial application (blocking the message pump hangs the
application from the user's point of view for a certain time). As long as you know
what the impact is of blocking the message pump, you can decide for yourself if it is
acceptable in your case (could be fine for testing).

MFC application should use the CSerialMFC wrapper if they want to pass CWnd
pointers instead of handles. Because this wrapper is very thin you can also choose to
use CSerialWnd directly.

Using the serial classes in your program

Using the serial classes can be divided into several parts. First you need to open the
serial port, then you set the appropriate baudrate, databits, handshaking, etc... This
is pretty straightforward. The tricky part is actually transmitting and receiving the
data, which will probably cause the most time to implement. At last you need to
close the serial port and as a bonus if you don't then the library will do it for you.

Sending data

Let's start with a classic example from K&R and be polite and say hello. The
implementation is very straightforward and looks like this (there is no error checking
here for simplicity, it is there in the actual project):

#define STRICT
#include <tchar.h>
#include <windows.h>
#include "Serial.h"
int WINAPI _tWinMain
(
HINSTANCE /*hInst*/,
HINSTANCE /*hInstPrev*/,
LPTSTR /*lptszCmdLine*/,
int /*nCmdShow*/
)
{
CSerial serial;

// Attempt to open the serial port (COM1)


serial.Open(_T("COM1"));

// Setup the serial port (9600,N81) using hardware handshaking


serial.Setup(CSerial::EBaud9600,CSerial::EData8,CSerial::EParNone,CSerial::
EStop1);
serial.SetupHandshaking(CSerial::EHandshakeHardware);

// The serial port is now ready and we can send/receive data. If


// the following call blocks, then the other side doesn't support
// hardware handshaking.
serial.Write("Hello world");

// Close the port again


serial.Close();
return 0;
}

Of course you need to include the serial class' header-file. Make sure that the
header-files of this library are in your compiler's include path. All classes depend on
the Win32 API, so make sure that you have included them as well. I try to make all
of my programs ANSI and Unicode compatible, so that's why the tchar stuff is in
there. So far about the header-files.

The interesting part is inside the main routine. At the top we declare the serial
variable, which represents exactly one COM port. Before you can use it, you need to
open the port. Of course there should be some error handling in the code, but that's
left as an exercise for the reader. Besides specifying the COM port, you can also
specify the input and output buffer sizes. If you don't specify anything, then the
default OS buffer sizes are being used (older versions of the library used 2KB as the
default buffer size, but this has been changed). If you need larger buffers, then
specify them yourself.

Setting up the serial port is also pretty straightforward. The settings from the control
panel (or Device Manager) are being used as the port's default settings. Call Setup if
these settings do not apply for your application. If you prefer to use integers instead
of the enumerated types then just cast the integer to the required type. So the
following two initializations are equivalent:

Setup(CSerial::EBaud9600,CSerial::EData8,CSerial::EParNone,CSerial::EStop1);

Setup(CSerial::EBaudrate(9600),
CSerial::EDataBits(8),
CSerial::EParity(NOPARITY),
CSerial::EStopBits(ONESTOPBIT));
In the latter case, the types are not validated. So make sure that you specify the
appropriate values. Once you know which type of handshaking you need, then just
call SetupHandshaking with one of the appropriate handshaking.

Writing data is also very easy. Just call the Write method and supply a string. The
Write routine will detect how long the string is and send these bytes across the
cable. If you have written Unicode applications (like this one) then you might have
noticed that I didn't send a Unicode string. I think that it's pretty useless to send
Unicode strings, so you need to send them as binary or convert them back to ANSI
yourself. Because we are using hardware handshaking and the operation is non-
overlapped, the Write method won't return until all bytes have been sent to the
receiver. If there is no other side, then you might block forever at this point.

Finally, the port is closed and the program exits. This program is nice to display how
easy it is to open and setup the serial communication, but it's not really useful. The
more interesting programs will be discussed later.

Receiving data

Like in real life it's easier to tell something what to do then listening to another and
take appropriate actions. The same holds for serial communication. As we saw in the
Hello world example writing to the port is just as straightforward as writing to a file.
Receiving data is a little more difficult. Reading the data is not that hard, but
knowing that there is data and how much makes it more difficult. You'll have to wait
until data arrives and when you're waiting you cannot do something else. That is
exactly what causes problems in single-threaded applications. There are three
common approaches to solve this.

The first solution is easy. Just block until some data arrives on the serial port. Just
call WaitEvent without specifying the overlapped structure. This function blocks until
a communication event occurs (or an optional time-out expires). Easy, but the thread
is blocked and only wakes up for communication events or a time-out.

The second solution is to use the synchronization objects of Win32. Whenever


something happens, the appropriate event handles are signaled and you can take
appropriate action to handle the event. This method is available in most modern
operating systems, but the details vary. Unix systems use the select call, where
Win32 applications mostly use WaitForMultipleObjects or one of the related
functions. The trick is to call the WaitEvent function asynchronously by supplying an
overlapped structure, which contains a handle which will be signaled when an event
occurred. Using WaitForMultipleObjects you can wait until one of the handles
become signaled. I think this is the most suitable for most non-GUI applications. It's
definitely the most efficient option available. When you choose to use this option,
you'll notice that the serial classes are only a thin layer around the Win32 API.

The last solution is one which will be appreciated by most Windows GUI
programmers. Whenever something happens a message is posted to the
application's message queue indicating what happened. Using the standard message
dispatching this message will be processed eventually. This solution fits perfect in the
event-driven programming environment and is therefore useful for most GUI (both
non-MFC and MFC) applications. Unfortunately, the Win32 API offers no support to
accomplish this, which is the primary reasons why the serial classes were created.
The old Win16 API uses the SetCommEventMask and EnableCommNotification to do
exactly this, but these were dropped from the Win32 API.

Block until something happens

Blocking is the easiest way to wait for data and will therefore be discussed first. The
CSerial class exposes a method called WaitEvent, which will block until an event
has been received. You can (optionally) specify a time-out for this call (if overlapped
I/O is enabled), so it won't block forever if no data arrives anymore. The WaitEvent
method can wait for several events, which must be registered during setup. The
following events can occur on a COM port:

• EEventBreak is sent whenever a break was detected on input.


• EEventCTS means that the CTS (clear to sent) signal has changed.
• EEventDSR means that the DSR (data set ready) signal has changed.
• EEventError indicates that a line-status error has occurred.
• EEventRing indicates that the ring indicator was set high. Only transitions
from low to high will generate this event.
• EEventRLSD means that the RLSD (receive line signal detect) signal has
changed. Note that this signal is often called CD (carrier detect).
• EEventRecv is probably one of the most important events, because it signals
that data has been received on the COM-port.
• EEventRcvEv indicates that a certain character (the event character) has been
received. This character can be set using the SetEventChar method.
• EEventSend indicates that the entire output buffer has been sent to the other
side.

When a serial port is opened, then the EEventBreak, EEventError and EEventRecv
are being registered. If you would like to receive the other events then you have to
register them using the SetMask method.

Now you can use the WaitEvent method to wait for an event. You can then call
GetEventType to obtain the actual event. This function will reset the event, so make
sure you call it only once after each WaitEvent call. Multiple events can be received
simultaneously (i.e. when the event character is being received, then (EEventRecv|
EEventRcvEv) is returned. Never use the == operator to check for events, but use
the & operator instead.

Reading can be done using the Read method, but reading is trickier then you might
think at first. You get only an event that there is some data, but not how much. It
could be a single byte, but it can also be several kilobytes. There is only one way to
deal with this. Just read as much as you can handle (efficiently) and process it.

First make sure that the port is in EReadTimeoutNonblocking mode by issuing the
following call:

// Use 'non-blocking' reads, because we don't know how many bytes


// will be received. This is normally the most convenient mode
// (and also the default mode for reading data).
serial.SetupReadTimeouts(CSerial::EReadTimeoutNonblocking);

The Read method will now read as much as possible, but will never block. If you
would like Read to block, then specify EReadTimeoutBlocking. Read always returns
the number of bytes read, so you can determine whether you have read the entire
buffer. Make sure you always read the entire buffer after receiving the EEventRecv
event to avoid you lose data. A typical EEventRecv will look something like this:

// Read data, until there is nothing left


DWORD dwBytesRead = 0;
BYTE abBuffer[100];
do
{
// Read data from the COM-port
serial.Read(abBuffer,sizeof(abBuffer),&dwBytesRead);
if (dwBytesRead > 0)
{
// TODO: Process the data
}
}
while (dwBytesRead == sizeof(abBuffer));

The Listener sample (included in the ZIP-file) demonstrates the technique as


described above. The entire sample code isn't listed in this document, because it
would take too much space.

Using the Win32 synchronization objects

In most cases, blocking for a single event (as described above) isn't appropriate.
When the application blocks, then it is completely out of your control. Suppose you
have created a service which listens on multiple COM-ports and also monitors a
Win32 event (used to indicate that the service should stop). In such a case, you'll
need multithreading, message queues or the Win32 function for synchronization. The
synchronization objects are the most efficient method to implement this, so I'll try to
explain them. Before you continue reading I assume you're a bit familiar with the use
of the synchronization objects and overlapped operations. If you're not, then first
read the section about Synchronization in the Win32 API.

The only call that blocks for a fairly long time is the WaitEvent method. In the next
paragraphs, I will show you how to implement this call using the Win32
synchronization objects (all other overlapped calls work identical). A complete
implementation can be found in the Overlapped project, which is quite similar to the
Listener project, but it now uses overlapped I/O.

First the the COM-port needs to be initialized. This works identical as in the Listener
sample. Then two events are created. The first event will be used in the overlapped
structure. Note that it should be a manual reset event, which is initially not signaled.
The second one is an external event, which is used to stop the program. The first
event will be stored inside the OVERLAPPED structure.

// Create a handle for the overlapped operations


HANDLE hevtOverlapped = ::CreateEvent(0,TRUE,FALSE,0);;

// Open the "STOP" handle


HANDLE hevtStop = ::CreateEvent(0,TRUE,FALSE,_T("Overlapped_Stop_Event"));

// Setup the overlapped structure


OVERLAPPED ov = {0};
ov.hEvent = hevtOverlapped;

All events have been setup correctly and the overlapped structure has been
initialized. We can now call the WaitEvent method in overlapped mode.

// Wait for an event


serial.WaitEvent(&ov);

The overlapped I/O operation is now in progress and whenever an event occurs, that
would normally unblock this call, the event handle in the overlapped structure will
become signalled. It is not allowed to perform an I/O operation on this port, before it
has completed, so we will wait until the event arrives or the stop event has been set.

// Setup array of handles in which we are interested


HANDLE ahWait[2];
ahWait[0] = hevtOverlapped;
ahWait[1] = hevtStop;

// Wait until something happens


switch (::WaitForMultipleObjects(2,ahWait,FALSE,INFINITE))
{
case WAIT_OBJECT_0:
// Serial port event occurred
...

case WAIT_OBJECT_0+1:
// Stop event raised
...
}

That's all you need to do, when you want to use the serial class in overlapped I/O
mode. N

Using Windows messages

Most Windows developers are used to receive a Windows message, whenever a


certain event occurs. This fits perfectly in the Windows event-driven model, but the
Win32 API doesn't provide such a mechanism for serial communication. This library
includes a class called CSerialWnd, which will send a special message whenever a
serial event occurs. It is pretty simple, when you are already familiar with the event-
driven programming model of Windows.

Instead of using the CSerial class, you must use the CSerialWnd class (which is in
fact derived from CSerial). CSerialWnd works just like CSerial, but there are some
tiny differences in opening the port and waiting on its events. Note that the
CSerialWnd doesn't have a window itself and neither should you derive from it, when
you want to use it. Just define a member variable and use that from within your
window.

Because CSerialWnd posts its messages to a window, it requires additional


information. Therefore the Open method accepts three additional parameters, which
specify the window handle, message and optional argument. The prototype looks
like:

LONG Open (
LPCTSTR lpszDevice,
HWND hwndDest,
UINT nComMsg = WM_NULL,
LPARAM lParam = 0,
DWORD dwInQueue = 0,
DWORD dwOutQueue = 0
)

The lpszDevice, dwInQueue and dwOutQueue are used as in CSerial. The hwndDest
argument specifies the window, where the message should be sent to. The library
registers a default message during startup, which can be used in most cases. Simply
pass WM_NULL to use this message. The value of this message is stored in the
CSerialWnd::mg_nDefaultComMsg variable, which is a static member variable of
CSerialWnd. If you prefer one of your own messages, then you can use that instead.
The optional lParam argument is sent as the second parameter (lParam) in each
message that is being sent by CSerial. The serial library doesn't do anything with
this value, so be free to use it as you like.

Sending data and setting up the serial port is exactly the same as with CSerial, so I
won't discuss that again anymore. The biggest difference is the way you receive the
events, but that is exactly why you want to use this class anyway.

If everything is fine, then you have registered all interesting events with the SetMask
method. Whenever one of these events occur, the specified message will be sent to
the window you have registered before. The wParam will contain the event and error-
code. The lParam contains whatever you passed to CSerialWnd::Open, when you
have opened the port. A typical handler for these messages looks like:

LRESULT CALLBACK MyWndProc (HWND hwnd, UINT nMsg, WPARAM wParam, LPARAM lParam)
{
if (nMsg == CSerialWnd::mg_nDefaultComMsg)
{
// A serial message occurred
const CSerialWnd::EEvent eEvent = CSerialWnd::EEvent(LOWORD(wParam));
const CSerialWnd::EError eError = CSerialWnd::EError(HIWORD(wParam));

switch (eEvent)
{
case CSerialWnd::EEventRecv:
// TODO: Read data from the port
break;

...
}

// Return successful
return 0;
}

// Perform other window processing


...
}
The methods WaitEvent, GetEventType and GetError from CSerial are hidden in
the CSerialWnd class, because they cannot be used anymore. All the information is
passed in the window message, so it shouldn't be necessary to use them anymore.

Using the library with MFC

Personally, I don't like MFC, but I know many people out there use it so there is also
support in this library for MFC. Instead of using CSerialWnd, you can use
CSerialMFC. It works exactly the same, but it can also handle a CWnd pointer and it
provides a macro, which can be used in the message map for better readability. The
message map of a window, which can receive events from CSerialMFC should look
like this:

BEGIN_MESSAGE_MAP(CMyClass,CWnd)
//{{AFX_MSG_MAP(CMyClass)
...
//}}AFX_MSG_MAP
...
ON_WM_SERIAL(OnSerialMsg)
...
END_MESSAGE_MAP()

Note that the ON_WM_SERIAL macro is placed outside the AFX_MSG_MAP block,
otherwise the MFC Class Wizard becomes confused. The handler itself looks
something like this:

afx_msg LRESULT CMyClass::OnSerialMsg (WPARAM wParam, LPARAM lParam)


{
const CSerialMFC::EEvent eEvent = CSerialMFC::EEvent(LOWORD(wParam));
const CSerialMFC::EError eError = CSerialMFC::EError(HIWORD(wParam));

switch (eEvent)
{
case CSerialMFC::EEventRecv:
// TODO: Read data from the port
break;
...
}

// Return successful
return 0;
}

A complete sample, including property sheets for setting up the COM-port, is shipped
with this library. Look for the SerialTestMFC project for an example how to use this
library in your MFC programs.

Integrating this library into your code.

This library is very lightweight, so it can easily be integrated into your application
without using a separate DLL. I used a static library for the CSerial (and derived)
classes, because I think that is exactly where a library is meant for. Just insert the
Serial project into your workspace and make a dependency to it. The linker will then
automatically compile and link the serial classes to your application. Some people
don't like libraries. In that case you can just add the Serial files to your project and
recompile.

If you use precompiled headers, then you need to remove the following lines from
both Serial.cpp and SerialWnd.cpp:

#define STRICT
#include <crtdbg.h>
#include <tchar.h>
#include <windows.h>

Replace these lines with the following line:

#include "StdAfx.h"

Sample programs

The Serial library comes with some sample programs, which can all be compiled from
the Serial.dsw workspace. Make sure that you always open the Serial.dsw file, when
building the library and/or sample applications. Opening the individual .dsp files
won't work, because these files don't have the dependencies, which results in
unresolved externals.

Note that all samples can be compiled as ANSI or Unicode versions. The Unicode
versions don't run on the Windows 95/98/ME platforms, because they don't support
Unicode. The SerialTestMFC sample uses the Unicode version of the MFC library
(when compiled with Unicode enabled). The default installation options of Visual C++
v6.0 don't install the Unicode libraries of MFC, so you might get an error that
mfc42ud.dll or mfc42u.dll cannot be found.

Windows 95 support and CancelIo

A lot of people still need to support the Windows 95 environment, which doesn't
support the CancelIo function. When an overlapped operation times out, then the
pending call need to be cancelled with the CancelIo call. Therefore time-outs (other
then 0 and INFINTE) cannot be used when Windows 95 compatibility is enabled.
Fortunately, the CSerialEx, CSerialWnd and CSerialMFC don't rely on this call, so
these classes can be used on Windows 95 without any restrictions. If you define the
SERIAL_NO_CANCELIO symbol, then I/O cancellation is disabled, so you should always
define this symbol when you target Windows 95.

Other problems, specific to Windows 95/98/ME are the queue sizes. If the buffer size
is far too small then you might get a blue screen, when receiving or transmitting
data. The default settings should be fine for normal serial communication.

Windows CE support and the lack of overlapped I/O

I have got a lot of requests to implement a windows CE version of the library. The
old version of this library always used overlapped I/O, so it didn't work on Windows
CE. Due to the huge amount of requests I started working on this issue. I have
rewritten the CSerialEx class, so it doesn't rely on overlapped I/O internally.
Canceling the WaitCommEvent now uses a documented trick, which sets the event
mask to its current value. This effectively cancels the WaitCommEvent method and
makes the use of overlapped I/O unnecessary in CSerialEx.

<code>SetCommMask call blocks, when it is already waiting for an event (using


WaitCommEvent). This would render this method useless on the Windows CE
platform, because it doesn't support overlapped I/O. Fortunately, the serial driver is
implemented as an overlapped driver internally on Windows CE, so it allows multiple
calls (described in the KB, article Q175551). Using this Windows CE feature it is also
possible to use all the classes on the Windows CE platform.

To include this library into you Windows CE project, just add the Serial.dsp project
file to your own workspace. If you use eMbedded Visual C++, then the project file is
automatically converted. Make sure you define the SERIAL_NO_OVERLAPPED symbol to
avoid the use of overlapped I/O.

Porting issues

This paragraph describes some porting issues that you should be aware of when
porting from an older version to a newer version. Always retest your application,
when using a different version of the serial library. I always try to keep the code
source-level compatible, but if you use a new library version, then make sure you
recompile your code completely with the same SERIAL_xxx symbols defined as were
used to compile the library itself.

• [General] There is no effort made to make the libraries binary compatible,


between different builds. Make sure you always use the same compiler
settings and headerfiles for the library and your application to guarantee
proper operation.
• [August 2002 and later] The default sizes of the input and output queues
have been changed. Previously a size of 2KB was the default, but now the
default OS size is being used.
• [August 2002 and later] The default communication settings have been
changed. Previously the 9600,8N1 setting was used as the default setting.
The new version uses the values from the control panel (or Device Manager).
• [August 2002 and later] The CSerialEx class has been added, which might
better fit your needs.

Virtual COM ports (USB dongles, Bluetooth dongles, ...)

Some people use virtual COM-ports to emulate an ordinary serial port (i.e. USB serial
dongle, Bluetooth dongle, ...). These so-called virtual COM ports use a different
driver then ordinary serial ports. Unfortunately, most drivers are pretty buggy. In
most cases normal communication using the default settings works pretty good, but
you run into problems when you need more sophisticated communication.

Most virtual COM ports have difficulties with the CancelIo function. In some cases
the process freezes completely (and cannot even be killed by the task manager) and
I have even seen Windows XP systems reboot after calling CancelIo. Some drivers
can handle CancelIo for an overlapped ReadFile call, but fail when canceling a
WaitCommEvent request. Terminating a thread with a pending I/O request also
implies an implicit call of CancelIo, so if you have problems terminating a thread
then you might have pending requests that cannot be cancelled. The CSerialEx (and
derived classes) effectively cancels the pending WaitEvent, so you should be pretty
safe when you use these classes.

Receiving spurious events is another problem, which is quite common when using
virtual COM ports. Even events that have been masked out can be sent by some
virtual COM ports. For this reason the events are filtered inside the library, but you
might get some empty events in your code (you can simply ignore these events).

Some virtual COM ports also have problems when you don't use the common 8
databit, no parity and 1 stopbit settings. Sending a break is also problematical for
some virtual COM ports. There is no standard workaround possible for these bugs, so
make sure you test these features, when you intend to use your application with a
virtual COM port.

Some people claim that the dongle and its driver is fine, because HyperTerminal
works fine. This isn't correct in most cases. HyperTerminal only uses a small subset
of the Win32 API for serial communication and doesn't seem to use CancelIo very
much. The most important lesson from the last few paragraphs is that you need to
test your application very thorough with each driver that can be used. Although the
Win32 API is the same for each (virtual) COM port, the implementation below the API
might be completely different.

This serial library utilizes the overlapped I/O mechanism, which should be supported
by each Win32 driver. Unfortunately, a lot of drivers don't implement this feature
correct. However, I keep trying to improve this library so if you have any
suggestions, please contact me.

References

Of course the first place to look for information about serial communications is in the
Platform SDK section "Windows Base Services, Files and I/O, Communications".
There's probably enough in there to implement your own serial communications, but
for a better explanation read Allen Denver's article called "Serial Communications in
Win32", which is in the MSDN's Technical Articles.

Changes

How to establish communication between


mobile phone and PC
By nitzbajaj

This article deals with building a communication link between your mobile and your PC. It also
deals with the ways to execute commands on your mobile phone interpreter from your PC.
Introduction

The article provides a step by step tutorial about the method to connect your mobile
phone to your PC using serial communication. It describes the methods to execute
phone specific commands via your PC.

PC and mobile phone communication can be established by three ways:

1. serial port (requires a data cable).


2. infra red port (requires infrared port on your mobile and PC).
3. Bluetooth (requires Bluetooth wireless connector).

(Note: I have just worked on serial port and infrared.)

Explanation

Initialize and open port:


/* portNm =COM1,COM2 etc which ever COM port your phone is connected */

bool OpenPort( char *portNm )


{
if( portNm != NULL ) // check to if port exists
{
/* hComm is handle that makes a buffer file pointin to your
COM port for read and write of port data */

hComm = CreateFile( portNm,GENERIC_READ | GENERIC_WRITE,


0,
0,
OPEN_EXISTING,
NULL,
0);

if (hComm == INVALID_HANDLE_VALUE)
{
//error occured alert user about error
return false;
}
else
// port opened successfully alert user
return true;
}

else
{
// specified port missing alert user
return false;
}

Write To Port:
BOOL WriteABuffer(char * lpBuf, DWORD dwToWrite)
{
OVERLAPPED osWrite = {0};
DWORD dwWritten;
BOOL fRes;
// Issue write.
if (!WriteFile(hComm, lpBuf, dwToWrite, &dwWritten, NULL))
{
if (GetLastError() != ERROR_IO_PENDING)
{ // WriteFile failed, but it isn't delayed. Report error and abort.

fRes = FALSE;
}
else
{
// Write is pending.

if (!GetOverlappedResult(hComm, &osWrite, &dwWritten, TRUE))


{
fRes = FALSE;}
else
{ // Write operation completed successfully.

fRes = TRUE;}
}
}
else
// WriteFile completed immediately.

fRes = TRUE;

return fRes;
}

Read Port:
void ReadPort()

{
DWORD dwRead;
char chRead[250];

// remember to adjust 250 according to the total data read

char *count = NULL;

/* flush the old values and fill with some arbitary symbol
thats most unlikely to appear in your data to know
the end point after reading */

memset(chRead,'\x91',250);

Sleep( 5L);

ReadFile(hComm, chRead, 250, &dwRead, NULL);

/* now chRead contains data . you can manipulate


it accoring to your needs now. */

Special setting:

After you call OpenPort, you must assign the DCB and connection timeout to the
port. DCB is used to adjust communication settings of the port. You must set your
port according to your needs depending upon your communication requirement.
Connection timeouts are used to assign the time for which port must forcibly stop
reading data from your mobile phone to prevent abnormality in case of very long
data.

if(OpenPort("COM4")) // opens COM4 port


{
DCB dcb

dcb.fOutxCtsFlow = false; // Disable CTS monitoring


dcb.fOutxDsrFlow = false; // Disable DSR monitoring
dcb.fDtrControl = DTR_CONTROL_DISABLE; // Disable DTR monitoring
dcb.fOutX = false; // Disable XON/XOFF for transmission
dcb.fInX = false; // Disable XON/XOFF for receiving
dcb.fRtsControl = RTS_CONTROL_DISABLE; // Disable RTS (Ready To Send)

COMMTIMEOUTS timeouts;

timeouts.ReadIntervalTimeout = 20;
timeouts.ReadTotalTimeoutMultiplier = 10;
timeouts.ReadTotalTimeoutConstant = 100;
timeouts.WriteTotalTimeoutMultiplier = 10;
timeouts.WriteTotalTimeoutConstant = 100;

if (!SetCommTimeouts(hComm, &timeouts))
{
// error with time outs .alert user and exit
}

The above piece of code can be used to establish effective communication between a
PC and a mobile phone.

You can issue phone specific commands to the phone interpreter which will do the
processing and will return data on same port after processing. Hayes AT commands
can be used with most of the phones (these are phone specific, so please check for
the compatibility of these commands with your phone ). Nokia AT command set is
available at Nokia official website.

AT commands can be used to perform various functions like sending/receiving SMS


to your PC connected to mobile phone, synchronizing the mobile phone book,
changing phone settings, and lots more.

Hope the article proves to be useful, and for any other query, you can always contact
me at nitzbajaj@yahoo.com.

You might also like