Google Answers Logo
View Question
 
Q: Writing to serial COM port using Microsoft Visual C++ ( No Answer,   2 Comments )
Question  
Subject: Writing to serial COM port using Microsoft Visual C++
Category: Computers > Programming
Asked by: dpwiener-ga
List Price: $20.00
Posted: 30 Jun 2004 12:14 PDT
Expires: 03 Jul 2004 09:40 PDT
Question ID: 368232
I am writing a C/C++ program using Microsoft Visual C++ 6.0, and I
have to do what seems like a trivial task.  I want to output a line of
ASCII text to an RS-232 serial COM port (either COM1 or COM2), and I
also need to to set the port characteristics to 4800 baud, 8 data
bits, 1 stop bit, no parity.

If necessary I (presumably) can set the COM port values using a DOS
command (e.g., MODE COM1 BAUD=4800 PARITY=0 DATA=8 STOP=1).  But I'm
having problems getting my C/C++ program to write to the port.  For
example, I tried to do the following:

FILE *out1;
out1 = fopen("/dev/tty1", "w");
fprintf(out1, "Transmit two floating point values: %f, %f\n", value1, value2);
fclose(out1);

Visual C/C++ doesn't seem to like "/dev/tty1"; this may be an obsolete
formulation.  But when I look through technical support websites and
other programs which utilize C++ code for serial interfaces, I find
some enormously complicated code involving handles and other functions
which seem like overkill.  In some cases I don't even have the
libraries for those functions.

Isn't there some simple way to accomplish this trivial task?  Or if
not, what is the least complicated way to accomplish this trivial
task?  I just want to get on with writing my program, I don't want to
spend hours becoming an expert in serial interface functions.

I'm on a tight schedule, so any help would be greatly appreciated.

Request for Question Clarification by mathtalk-ga on 30 Jun 2004 12:29 PDT
Hi, dpwiener-ga:

What operating system will your program urn under?  You mention using
a DOS command, but I suspect your target is a 32-bit version of
Windows.

The general idea is correct.  One needs to configure the serial port
and then open the serial port for reading and writing as a file. 
However MS Windows will put obstacles in your way for directly
accessing the hardware.

If you have Visual Basic 6.0 as well as Visual C++, then you will
already have an ActiveX control that can be used for the serial port
operations.  If not I can point you to documentation for doing
everything from scratch in C++.

regards, mathtalk-ga

Clarification of Question by dpwiener-ga on 30 Jun 2004 13:32 PDT
Hello mathtalk-ga. My OS is Windows 2000 Professional.  I do not have
Visual Basic, but if necessary I do have Compaq Visual Fortran
Professional Edition 6.6.a.  (I'd prefer not to have to resort to
Fortran.)

Thank you for your suggestion, corwin02-ga.  Visual C++ says it can't
open serial.h, so I haven't been able to try out that code.  The
tutorial you pointed me to may or may not be able to solve my problem,
but I'm still hoping that there's an easy solution which doesn't
require me to spend hours becoming an expert on what ought to be a
trivial function.

Twenty two years ago I could easily accomplish this on my Kaypro II
computer.  I suspect I could have easily done it with real DOS or
Windows 95.  Why is it suddenly so damn difficult?

Clarification of Question by dpwiener-ga on 30 Jun 2004 16:17 PDT
Thank you andyt-ga.  I downloaded Serial_demo.zip from The Code
Project and attempted to use it in my program.  Unfortunately
Microsoft Visual C++ gives me linking errors such as the following:

Linking...
Demod_Main.obj : error LNK2001: unresolved external symbol "public:
virtual __thiscall CSerial::~CSerial(void)" (??1CSerial@@UAE@XZ)
Demod_Main.obj : error LNK2001: unresolved external symbol "public:
virtual long __thiscall CSerial::Close(void)" (?Close@CSerial@@UAEJXZ)
Demod_Main.obj : error LNK2001: unresolved external symbol "public:
virtual long __thiscall CSerial::Open(char const *,unsigned
long,unsigned long,bool)" (?Open@CSerial@@UAEJPBDKK_N@Z)
Demod_Main.obj : error LNK2001: unresolved external symbol "public:
__thiscall CSerial::CSerial(void)" (??0CSerial@@QAE@XZ)
Release/Demod_Main.exe : fatal error LNK1120: 4 unresolved externals
Error executing link.exe.

I don't know whether the problem is an incompatibility with Visual C++ or what.

Request for Question Clarification by mathtalk-ga on 30 Jun 2004 16:23 PDT
Hi, dpwiener-ga:

The point would not be to throw a new language into the project, but
rather to make use of existing software components that can configure
the serial port for you under Win2K.  Such a component that is bundled
with VB is mscomm.ocx:

[MSComm Control]
http://msdn.microsoft.com/library/en-us/comm98/html/vbobjcomm.asp

Using the serial port in VC++ 6.0 is easier than under C#/.Net, though
not as simple as doing it in Basic under MSD0S.  However one relies
even then on an operating system supplied device driver that manages
the characteristics of the serial port as a "file" or byte-stream.

Under Win2K the operating system supplied device driver is called
serial.sys, and the serial.h header file that corwin02-ga and andyt-ga
refer to should be installed with the DDK for Visual C++ 6.0.  If
you've installed the DDK (driver developer's kit) it should be in this
location:

%DDKROOT%\src\kernel\serial\serial.h 

Relative to the code snippet you posted, a more correct approach would
be to return a file handle by calling CreateFile("COM1",...).  For a
quirk of this Win32 API call for serial ports beyond COM9, see here:

[HOWTO: Specify Serial Ports Larger than COM9]
http://support.microsoft.com/?id=115831

regards, mathtalk-ga

Clarification of Question by dpwiener-ga on 30 Jun 2004 19:25 PDT
I do not have a DDK, and I don't know that I can get it within my
compressed time-frame for solving this problem.

Request for Question Clarification by mathtalk-ga on 01 Jul 2004 05:58 PDT
Okay, let's rule out doing things (serial port management) from
scratch in C++ and think about reusing an existing component.  As I've
mentioned, Microsoft bundled an ActiveX/COM control called mscomm.ocx
that is frequently used in VS6 projects, although it doesn't seem to
have been part of the distribution you have.  You could nonetheless
get the control and Add Reference it to your project as a library,
thereby exposing its interfaces within the Visual Studio editor (even
if you are writing C++ code).

As an alternative you could use the Code Project's CSerial class, as
suggested by corwin02-ga and andyt-ga.  I acknowledge that you got
some linker errors in trying to use that in your project, but it might
be fruitful to drill down and eliminate those problems.  It certainly
looks like it might just be an Add Reference issue.

First though, can you verify that you are using the Visual Studio
environment rather than a command line version of VC++?

regards, mathtalk-ga

Clarification of Question by dpwiener-ga on 01 Jul 2004 07:16 PDT
Yes, I am using the Visual Studio environment.

Clarification of Question by dpwiener-ga on 03 Jul 2004 09:39 PDT
I finally resorted to using Fortran (in conjunction with my C++
program) to perform the serial COM port writing function.  It was a
bit of a hassle getting it to work and to properly link.  However, I
think it will accomplish my objective.  I still have a bit more
testing to do, but I decided to call it a night at 2 am and finish up
later (my deadline was to have it ready to run early July 5th).

So I'm going to cancel this question.  I really appreciate the help
and suggestions I received.  And I still think it's ridiculous that it
has gotten so much more difficult to perform such a trivial task in
C/C++.  It should be as easy as writing to a monitor or a file.  Oh
well.
Answer  
There is no answer at this time.

Comments  
Subject: Re: Writing to serial COM port using Microsoft Visual C++
From: corwin02-ga on 30 Jun 2004 12:26 PDT
 
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 (4800,N81) using hardware handshaking
    serial.Setup(CSerial::EBaud4800,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;
}


See the whole tutorial here

http://www.codeproject.com/system/serial.asp
Subject: Re: Writing to serial COM port using Microsoft Visual C++
From: andyt-ga on 30 Jun 2004 15:18 PDT
 
Serial.h, and alot of other project files are available for download
at corwin02-ga's link.

//	Serial.h - Definition of the CSerial class
//
//	Copyright (C) 1999-2003 Ramon de Klein (Ramon.de.Klein@ict.nl)
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
// 
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
// 
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA


#ifndef __SERIAL_H
#define __SERIAL_H


//////////////////////////////////////////////////////////////////////
// The SERIAL_DEFAULT_OVERLAPPED defines if the default open mode uses
// overlapped I/O. When overlapped I/O is available (normal Win32
// platforms) it uses overlapped I/O. Windows CE doesn't allow the use
// of overlapped I/O, so it is disabled there by default.

#ifndef SERIAL_DEFAULT_OVERLAPPED
#ifndef SERIAL_NO_OVERLAPPED
#define SERIAL_DEFAULT_OVERLAPPED	true
#else
#define SERIAL_DEFAULT_OVERLAPPED	false
#endif
#endif


//////////////////////////////////////////////////////////////////////
//
// CSerial - Win32 wrapper for serial communications
//
// Serial communication often causes a lot of problems. This class
// tries to supply an easy to use interface to deal with serial
// devices.
//
// The class is actually pretty ease to use. You only need to open
// the COM-port, where you need to specify the basic serial
// communication parameters. You can also choose to setup handshaking
// and read timeout behaviour.
//
// The following serial classes are available:
//
// CSerial      - Serial communication support.
// CSerialEx    - Serial communication with listener thread for events
// CSerialSync  - Serial communication with synchronized event handler
// CSerialWnd   - Asynchronous serial support, which uses the Win32
//                message queue for event notification.
// CSerialMFC   - Preferred class to use in MFC-based GUI windows.
// 
//
// Pros:
// -----
//	- Easy to use (hides a lot of nasty Win32 stuff)
//	- Fully ANSI and Unicode aware
//
// Cons:
// -----
//  - Little less flexibility then native Win32 API, however you can
//    use this API at the same time for features which are missing
//    from this class.
//  - Incompatible with Windows 95 or Windows NT v3.51 (or earlier),
//    because CancelIo isn't support on these platforms. Define the
//	  SERIAL_NO_CANCELIO macro for support of these platforms as
//	  well. When this macro is defined, then only time-out values of
//	  0 or INFINITE are valid.
//
//
// Copyright (C) 1999-2003 Ramon de Klein
//                         (Ramon.de.Klein@ict.nl)

class CSerial
{
// Class enumerations
public:
	// Communication event
	typedef enum
	{
		EEventUnknown  	   = -1,			// Unknown event
		EEventNone  	   = 0,				// Event trigged without cause
		EEventBreak 	   = EV_BREAK,		// A break was detected on input
		EEventCTS   	   = EV_CTS,		// The CTS signal changed state
		EEventDSR   	   = EV_DSR,		// The DSR signal changed state
		EEventError 	   = EV_ERR,		// A line-status error occurred
		EEventRing  	   = EV_RING,		// A ring indicator was detected
		EEventRLSD  	   = EV_RLSD,		// The RLSD signal changed state
		EEventRecv  	   = EV_RXCHAR,		// Data is received on input
		EEventRcvEv 	   = EV_RXFLAG,		// Event character was received on input
		EEventSend		   = EV_TXEMPTY,	// Last character on output was sent
		EEventPrinterError = EV_PERR,		// Printer error occured
		EEventRx80Full	   = EV_RX80FULL,	// Receive buffer is 80 percent full
		EEventProviderEvt1 = EV_EVENT1,		// Provider specific event 1
		EEventProviderEvt2 = EV_EVENT2,		// Provider specific event 2
	} 
	EEvent;

	// Baudrate
	typedef enum
	{
		EBaudUnknown = -1,			// Unknown
		EBaud110     = CBR_110,		// 110 bits/sec
		EBaud300     = CBR_300,		// 300 bits/sec
		EBaud600     = CBR_600,		// 600 bits/sec
		EBaud1200    = CBR_1200,	// 1200 bits/sec
		EBaud2400    = CBR_2400,	// 2400 bits/sec
		EBaud4800    = CBR_4800,	// 4800 bits/sec
		EBaud9600    = CBR_9600,	// 9600 bits/sec
		EBaud14400   = CBR_14400,	// 14400 bits/sec
		EBaud19200   = CBR_19200,	// 19200 bits/sec (default)
		EBaud38400   = CBR_38400,	// 38400 bits/sec
		EBaud56000   = CBR_56000,	// 56000 bits/sec
		EBaud57600   = CBR_57600,	// 57600 bits/sec
		EBaud115200  = CBR_115200,	// 115200 bits/sec
		EBaud128000  = CBR_128000,	// 128000 bits/sec
		EBaud256000  = CBR_256000,	// 256000 bits/sec
	}
	EBaudrate;

	// Data bits (5-8)
	typedef enum
	{
		EDataUnknown = -1,			// Unknown
		EData5       =  5,			// 5 bits per byte
		EData6       =  6,			// 6 bits per byte
		EData7       =  7,			// 7 bits per byte
		EData8       =  8			// 8 bits per byte (default)
	}
	EDataBits;

	// Parity scheme
	typedef enum
	{
		EParUnknown = -1,			// Unknown
		EParNone    = NOPARITY,		// No parity (default)
		EParOdd     = ODDPARITY,	// Odd parity
		EParEven    = EVENPARITY,	// Even parity
		EParMark    = MARKPARITY,	// Mark parity
		EParSpace   = SPACEPARITY	// Space parity
	}
	EParity;

	// Stop bits
	typedef enum
	{
		EStopUnknown = -1,			// Unknown
		EStop1       = ONESTOPBIT,	// 1 stopbit (default)
		EStop1_5     = ONE5STOPBITS,// 1.5 stopbit
		EStop2       = TWOSTOPBITS	// 2 stopbits
	} 
	EStopBits;

	// Handshaking
	typedef enum
	{
		EHandshakeUnknown		= -1,	// Unknown
		EHandshakeOff			=  0,	// No handshaking
		EHandshakeHardware		=  1,	// Hardware handshaking (RTS/CTS)
		EHandshakeSoftware		=  2	// Software handshaking (XON/XOFF)
	} 
	EHandshake;

	// Timeout settings
	typedef enum
	{
		EReadTimeoutUnknown		= -1,	// Unknown
		EReadTimeoutNonblocking	=  0,	// Always return immediately
		EReadTimeoutBlocking	=  1	// Block until everything is retrieved
	}
	EReadTimeout;

	// Communication errors
	typedef enum
	{
		EErrorUnknown = 0,			// Unknown
		EErrorBreak   = CE_BREAK,	// Break condition detected
		EErrorFrame   = CE_FRAME,	// Framing error
		EErrorIOE     = CE_IOE,		// I/O device error
		EErrorMode    = CE_MODE,	// Unsupported mode
		EErrorOverrun = CE_OVERRUN,	// Character buffer overrun, next byte is lost
		EErrorRxOver  = CE_RXOVER,	// Input buffer overflow, byte lost
		EErrorParity  = CE_RXPARITY,// Input parity error
		EErrorTxFull  = CE_TXFULL	// Output buffer full
	}
	EError;

	// Port availability
	typedef enum
	{
		EPortUnknownError = -1,		// Unknown error occurred
		EPortAvailable    =  0,		// Port is available
		EPortNotAvailable =  1,		// Port is not present
		EPortInUse        =  2		// Port is in use

	} 
	EPort;

// Construction
public:
	CSerial();
	virtual ~CSerial();

// Operations
public:
	// Check if particular COM-port is available (static method).
	static EPort CheckPort (LPCTSTR lpszDevice);

	// Open the serial communications for a particular COM port. You
	// need to use the full devicename (i.e. "COM1") to open the port.
	// It's possible to specify the size of the input/output queues.
	virtual LONG Open (LPCTSTR lpszDevice, DWORD dwInQueue = 0, DWORD
dwOutQueue = 0, bool fOverlapped = SERIAL_DEFAULT_OVERLAPPED);

	// Close the serial port.
	virtual LONG Close (void);

	// Setup the communication settings such as baudrate, databits,
	// parity and stopbits. The default settings are applied when the
	// device has been opened. Call this function 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(EBaud9600,EData8,EParNone,EStop1)
	//
	// or
	//
	//   Setup(EBaudrate(9600),EDataBits(8),EParity(NOPARITY),EStopBits(ONESTOPBIT))
	//
	// In the latter case, the types are not validated. So make sure
	// that you specify the appropriate values.
	virtual LONG Setup (EBaudrate eBaudrate = EBaud9600,
						EDataBits eDataBits = EData8,
						EParity   eParity   = EParNone,
						EStopBits eStopBits = EStop1);

	// Set/clear the event character. When this byte is being received
	// on the serial port then the EEventRcvEv event is signalled,
	// when the mask has been set appropriately. If the fAdjustMask flag
	// has been set, then the event mask is automatically adjusted.
	virtual LONG SetEventChar (BYTE bEventChar, bool fAdjustMask = true);

	// Set the event mask, which indicates what events should be
	// monitored. The WaitEvent method can only monitor events that
	// have been enabled. The default setting only monitors the
	// error events and data events. An application may choose to
	// monitor CTS. DSR, RLSD, etc as well.
	virtual LONG SetMask (DWORD dwMask = EEventBreak|EEventError|EEventRecv);

	// The WaitEvent method waits for one of the events that are
	// enabled (see SetMask).
	virtual LONG WaitEvent (LPOVERLAPPED lpOverlapped = 0, DWORD
dwTimeout = INFINITE);

	// Setup the handshaking protocol. There are 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.
	// 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.
	// 3) Software handshaking, where the XON/XOFF characters are used
	//    to throttle the data. A major drawback of this method is that
	//    these characters cannot be used for data anymore.
	virtual LONG SetupHandshaking (EHandshake eHandshake);

	// Read operations can be blocking or non-blocking. You can use
	// this method to setup wether to use blocking or non-blocking
	// reads. Non-blocking reads is the default, which is required
	// for most applications.
	//
	// 1) Blocking reads, which will cause the 'Read' method to block
	//    until the requested number of bytes have been read. This is
	//    useful if you know how many data you will receive.
	// 2) Non-blocking reads, which will read as many bytes into your
	//    buffer and returns almost immediately. This is often the
	//    preferred setting.
	virtual LONG SetupReadTimeouts (EReadTimeout eReadTimeout);

	// Obtain communication settings
	virtual EBaudrate  GetBaudrate    (void);
	virtual EDataBits  GetDataBits    (void);
	virtual EParity    GetParity      (void);
	virtual EStopBits  GetStopBits    (void);
	virtual EHandshake GetHandshaking (void);
	virtual DWORD      GetEventMask   (void);
	virtual BYTE       GetEventChar   (void);

	// Write data to the serial port. Note that we are only able to
	// send ANSI strings, because it probably doesn't make sense to
	// transmit Unicode strings to an application.
	virtual LONG Write (const void* pData, size_t iLen, DWORD* pdwWritten
= 0, LPOVERLAPPED lpOverlapped = 0, DWORD dwTimeout = INFINITE);
	virtual LONG Write (LPCSTR pString, DWORD* pdwWritten = 0,
LPOVERLAPPED lpOverlapped = 0, DWORD dwTimeout = INFINITE);

	// Read data from the serial port. Refer to the description of
	// the 'SetupReadTimeouts' for an explanation about (non) blocking
	// reads and how to use this.
	virtual LONG Read (void* pData, size_t iLen, DWORD* pdwRead = 0,
LPOVERLAPPED lpOverlapped = 0, DWORD dwTimeout = INFINITE);

	// Send a break
	LONG Break (void);

	// Determine what caused the event to trigger
	EEvent GetEventType (void);

	// Obtain the error
	EError GetError (void);

	// Obtain the COMM and event handle
	HANDLE GetCommHandle (void)		{ return m_hFile; }

	// Check if com-port is opened
	bool IsOpen (void) const		{ return (m_hFile != 0); }

	// Obtain last error status
	LONG GetLastError (void) const	{ return m_lLastError; }

	// Obtain CTS/DSR/RING/RLSD settings
	bool GetCTS (void);
	bool GetDSR (void);
	bool GetRing (void);
	bool GetRLSD (void);

	// Purge all buffers
	LONG Purge (void);

protected:
	// Internal helper class which wraps DCB structure
	class CDCB : public DCB
	{
	public:
		CDCB() { DCBlength = sizeof(DCB); }
	};

// Attributes
protected:
	LONG	m_lLastError;		// Last serial error
	HANDLE	m_hFile;			// File handle
	EEvent	m_eEvent;			// Event type
	DWORD	m_dwEventMask;		// Event mask

#ifndef SERIAL_NO_OVERLAPPED
	HANDLE	m_hevtOverlapped;	// Event handle for internal overlapped operations
#endif

protected:
	// Check the requirements
	void CheckRequirements (LPOVERLAPPED lpOverlapped, DWORD dwTimeout) const;

	// CancelIo wrapper (for Win95 compatibility)
	BOOL CancelCommIo (void);
};

#endif	// __SERIAL_H

Important Disclaimer: Answers and comments provided on Google Answers are general information, and are not intended to substitute for informed professional medical, psychiatric, psychological, tax, legal, investment, accounting, or other professional advice. Google does not endorse, and expressly disclaims liability for any product, manufacturer, distributor, service or service provider mentioned or any opinion expressed in answers or comments. Please read carefully the Google Answers Terms of Service.

If you feel that you have found inappropriate content, please let us know by emailing us at answers-support@google.com with the question ID listed above. Thank you.
Search Google Answers for
Google Answers  


Google Home - Answers FAQ - Terms of Service - Privacy Policy