Click here to Skip to main content
Licence 
First Posted 24 Jun 2003
Views 133,167
Bookmarked 59 times

Simple Serial Communication

By Idael Cardoso | 24 Jun 2003
A C++ class to allow simple serial communication in Win32 platforms
2 votes, 18.2%
1

2
3 votes, 27.3%
3
4 votes, 36.4%
4
2 votes, 18.2%
5
3.36/5 - 11 votes
μ 3.36, σa 2.38 [?]

Introduction

This is a proposal for a C++ class to handle simple serial communications, which means send and receive some bytes, and control the state of serial communication signals. In many cases, what one needs is to be able to communicate by a serial port to certain devices or electronic circuits. As simplicity is the main goal in this class, it is developed for synchronous read and write operation instead of overlapped ones and also assuming that there is no hardware (or software) flow control so the communications signals can be freely controlled. If you need an event driven serial communication (overlapped read/write, signals changes controlled by even, etc.) you can look in this site for the article Serial communication for Win32 with modem support By thierrydollar

Using the code

To use the class CSerialPort you must call CSerialPort::Open then does read or write operation, set or test the state of communications signals and closes the port once finished (not mandatory because the destructor does it). The read and write methods make no assumption about type and format of data send or received, you must take into account if you are handling a character or binary format, if there is Unicode, mbcs, etc. The open function if defined as follows:

virtual BOOL Open(LPCTSTR PortName, DWORD BaudRate, BYTE ByteSize, <BR>  BYTE Parity, BYTE StopBits,
  DWORD DesiredAccess = GENERIC_READ|GENERIC_WRITE);

There is no assumption about communication parameters because they are specific to each serial communication and must be known in order to establish a successful communication. Even, if the most frequent name of serial ports are “COM1:” to “COM4:”, there can be more than 4 serial ports in a machine and the serial driver is not forced to follow the “COMxx” name convention so a serial port can have any name in Win32 platforms. It would be a good idea to have a static function to obtain the names of installed serial ports but, as far as I know, there is not a documented way to do that. There is a simple way to know about installed ports: assuming that ports names follow the “COMxx:” convention they try to open all possible ports and if there is an error and GetLastError() returns ERROR_FILE_NOT_FOUND then the port isn’t installed. There is another way to know installed ports names and it is searching in registry but it is undocumented and platform depend.

With article there is a simple sample of using the mentioned class, the example is a program that reads bar codes from a serial bar code reader. Normally bar code readers send the read bar code ended by carriage return and line feed characters (this can be configured and even could be different for specific manufacturer) and the code is an ASCII string. There is a class (CBarCodeReader ) derived from CSerialPort that encapsulate the described protocol and its read method returns the read bar code (if any) directly in an string. There is not much more to say, the rest is in the code and it is simple (at least should be :-) )

Remarks

Remember that this is a simple instead of complete way of using serial devices. The proposed code can be used in Win32 Platforms and Windows CE versions. Pocket PC developers (I didn’t try others Windows CE versions) must take into account that manufacturers can have specific implementations of serial APIs (as other APIs). Even if it is true that such implementations must agree to Pocket PC implementation, I have found small differences from one device to another. For instance: in a Dell Axim Pocket PC device the signal CD must be externally supplied (the voltage present) in order to read or do any operation with serial port, there is no function errors if you try without that signal on, but you won’t obtain any result. I have tried the same in Compaq iPAQs and it is possible to read without any specific external signal on (as should be).

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here

About the Author

Idael Cardoso

Web Developer

France France

Member


Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board. (secure sign-in)
 
Search this forum  
 FAQ
    Noise  Layout  Per page   
  Refresh
Questioncan I connect serial port by 2 pc Pinmemberjhonutp19:41 5 Jun '08  
Questioncontrolling a toy car Pinmemberfikree11:46 20 Mar '08  
GeneralNeed help communicating with a Series F4s/D controller PinmemberLoadSFSU16:35 28 Sep '06  
QuestionXON/XOFF flow control issue PinmemberChitragar20:36 1 Nov '05  
GeneralSimple Serial Communication PinmemberWar War0:30 16 Sep '04  
GeneralRe: Simple Serial Communication PinmemberIdael Cardoso9:32 16 Sep '04  
QuestionCan make communication hang up in one case PinmemberSimon6667:26 15 Sep '04  
AnswerRe: Can make communication hang up in one case PinmemberSimon6668:59 16 Sep '04  
Generaldialog based application interaction Pinmembermoto muzphee0:42 6 Jun '04  
GeneralRe: dialog based application interaction Pinmemberal_yakout2:42 6 Jun '04  
GeneralRe: dialog based application interaction PinmemberIdael Cardoso10:24 6 Jun '04  
GeneralRe: dialog based application interaction PinsussSimon6667:07 7 Sep '04  
GeneralRe: dialog based application interaction PinmemberIdael Cardoso7:45 7 Sep '04  
GeneralRe: dialog based application interaction PinmemberSimon6668:00 7 Sep '04  
GeneralRe: dialog based application interaction PinmemberIdael Cardoso6:02 8 Sep '04  
In the following code I’ll show how to adapt a MFC SDI application to use a worker thread that pool the serial port continually, when data is arrived it is converted to string (assuming that data read is in ASCII format) and added to a CStringList. The data arrival is signaled using a custom windows message. This method is not so efficient because the working thread is active all the time but can works if this performance penalty isn’t so important for your application.
When u create and SDI MFC application using the MFC app wizards a class named CMainFrame is declared in the file MainFrm.h and method defined in MainFrm.cpp, these files are the files where code must be added.
In MainFrm.h add the following line:
#define WM_DATAREAD (WM_USER + 1)
The previous line defines the custom message that will be used to signal that data was arrived. Within the declaration of CMainFrame add the following:
class CMainFrame : public CFrameWnd
{  
  //Here would be the code already present in the declaration 
  //&#8230;
  // You must add the WM_DESTROY message handler using the Class Wizard and 
  // after add manually the line afx_msg LRESULT OnDataRead(WPARAM, LPARAM); 
  // as follows:
protected:
  //{{AFX_MSG(CMainFrame)
  afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);
  afx_msg void OnSetFocus(CWnd *pOldWnd);
  afx_msg void OnDestroy();
  afx_msg LRESULT OnDataRead(WPARAM, LPARAM);
  //}}AFX_MSG
  DECLARE_MESSAGE_MAP()
  //Add the followings lines:
private:
  CStringList m_PortDataList; //String list where data read from port will be set
  CCriticalSection m_CriticalSection; //Used to control access to previous list 
                                      //in a thread safe manner.
  CWinThread* m_pReadPortThread; //Worker thread
  CSerialPort m_Port; //Serial port. 
  void StartPooling(); //Start reading the port
  void StopPooling(); //Stop reading the port
  //Previous methods can be used in a start/stop buttons. 
  static UINT ReadPort(LPVOID pParam); //Worker thread procedure
};
In MainFrm.cpp, make the following changes:

Add the ON_MESSAGE(WM_DATAREAD, OnDataRead) entry to the message map as following:

BEGIN_MESSAGE_MAP(CMainFrame, CFrameWnd)
  //{{AFX_MSG_MAP(CMainFrame)
  ON_WM_CREATE()
  ON_WM_SETFOCUS()
  ON_WM_DESTROY()
  ON_MESSAGE(WM_DATAREAD, OnDataRead)
  //}}AFX_MSG_MAP
END_MESSAGE_MAP()
Modify the following methods as follows:

CMainFrame::CMainFrame()
{
  // TODO: add member initialization code here
  m_pReadPortThread = NULL;
}
void CMainFrame::OnDestroy() 
{
  StopPooling();
  CFrameWnd::OnDestroy();
}
An finally add the following code:
LRESULT CMainFrame::OnDataRead(WPARAM, LPARAM)
{
  CSingleLock slock(&m_CriticalSection);
  slock.Lock();
  if ( slock.IsLocked() )
  { //m_PortDataList can be accessed safetly
    //TODO: Here the code to process all string of m_PortDataList
  }
  else
  { //Error trying to acquire the critical section object
  }
  slock.Unlock();
  return 0;
}
UINT CMainFrame::ReadPort(LPVOID pParam)
{
  CMainFrame* pMainFrame = (CMainFrame*)pParam;
  char buffer[100];
  DWORD read;
  //End thread when the port get closed 
  while( pMainFrame->m_Port.IsOpen() )
  {
    if ( (read = pMainFrame->m_Port.Read(buffer, sizeof(buffer)-1)) > 0 )
    {
      buffer[read] = 0; //zero end the string
      CString str(buffer); //Convert the read buffer to string. 
                           //I asume that ASCII data was read.
  
      //Use the critical section to use the string array in 
      //the CMainFrame class
      CSingleLock slock(&pMainFrame->m_CriticalSection);
      slock.Lock();
      if ( slock.IsLocked() )
      { //Add read data to list as string
        pMainFrame->m_PortDataList.AddTail(str);
      }
      else
      { //Error trying to acquire the critical section object
      }
      slock.Unlock();
      //Check is the port is still opned
      if ( pMainFrame->m_Port.IsOpen() )
      { //Notify to window that data was arrived
        ::PostMessage(pMainFrame->GetSafeHwnd(), WM_DATAREAD, 0, 0);
      }
    }
  }
  return 0;
}
void CMainFrame::StartPooling()
{
  StopPooling();
  //TODO: Check open result and set correct parameters. 
  m_Port.Open(_T("COM1:"), 9600, 8, NOPARITY, ONESTOPBIT, GENERIC_READ);
  //Create the worker thread
  CWinThread* m_pReadPortThread = AfxBeginThread( ReadPort, 
                                                 (LPVOID)this, 
                                                 THREAD_PRIORITY_NORMAL, 
                                                 0, CREATE_SUSPENDED);
  m_pReadPortThread->m_bAutoDelete = FALSE; //Manual thread deletion
  m_pReadPortThread->ResumeThread();
}
void CMainFrame::StopPooling()
{
  m_Port.Close(); //Close the serial port and the worked thread will end
  if (m_pReadPortThread != NULL)
  {
    //Wait for worker thread termination (maximun 3 secconds)
    if ( WaitForSingleObject(m_pReadPortThread->m_hThread, 3000) != WAIT_OBJECT_0)
    { //Timeout or error. 
      m_pReadPortThread->SuspendThread();
    }
    delete m_pReadPortThread;
    m_pReadPortThread = NULL;
  }
}
Finally you must call StartPooling/StopPooling in a handler to buttons or any other method. To use Tserial_event class declare a member variable of its type
as following:

class CMainFrame : public CFrameWnd
{
  //&#8230;Omitted code
private:
  Tserial_event m_Port;
  
  void StartPooling(); //Start reading the port
  void StopPooling(); //Stop reading the port
}
In the cpp file:
void SerialEventManager(uint32 object, uint32 event)
{
  char *buffer;
  int size;
  Tserial_event *com;
  com = (Tserial_event *) object;
  if (com!=0)
  {
    switch(event)
    {
      //..Ignore other events 
      case SERIAL_DATA_ARRIVAL :
        size = com->getDataInSize();
        buffer = com->getDataInBuffer();
        //Process here the data read (buffer) 
        //You must take care of not using MFC functions and classes here
        //because Tserial_event don&#8217;t use AfxBeginThread.
        //Change the implementation to use AfxBeginThread if you need
        //to use MFC here.
        com->dataHasBeenRead();
        break;
    }
  }
}
CMainFrame::CMainFrame()
{
  // TODO: add member initialization code here
  m_Port.setManager(SerialEventManager);
}
void CMainFrame::OnDestroy() 
{
  StopPooling();
  CFrameWnd::OnDestroy();
}
void CMainFrame::StartPooling()
{
  m_Port. connect("COM1", 19200, SERIAL_PARITY_NONE, 8, true);
} 
void CMainFrame::StopPooling()
{
  m_Port.disconnect();
}

 
Regards,
Idael
QuestionHow to get serial ports from registry Pinmemberconrad Braam7:44 25 Sep '03  
GeneralRe: Cool article! PinmemberIdael Cardoso0:28 26 Jun '03  
GeneralYeah, you are right Pinmemberwheregone15:30 25 Jun '03  

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

Permalink | Advertise | Privacy | Mobile
Web01 | 2.5.120210.1 | Last Updated 25 Jun 2003
Article Copyright 2003 by Idael Cardoso
Everything else Copyright © CodeProject, 1999-2012
Terms of Use
Layout: fixed | fluid