SerialSend – a Windows program to send a short text string via serial port

NOTE (18-12-2012): Since posting this, I have updated SerialSend to add some additional useful features and make more robust. The updated version is available from the SerialSend page.

When I’m building robots, I sometimes find it useful to send short commands from the PC to a microcontroller (usually a dsPIC30F4011) in real-time via a USB-to-serial adapter. At the moment, one of the engineers in my Robotics module (Anthony Gaule) is trying to send commands from a machine vision program running on his laptop to a miniature SCARA arm that he has built (controlled by a dsPIC30F4011). Here’s the little Windows program of mine that he’s using. It just sends one word of text to an external device via a user-specified COM port. Binary download (from my github page):

SerialSend.exe (18kB, 6-12-2011)

Here’s the full source code:

//
// SerialSend.c - This program sends text via serial port
// Written by Ted Burke - last updated 6-12-2011
//
// Command line arguments are used to specify the text
// to send and the serial port to use. The baud rate used
// is currently always 38400 baud. The text cannot contain
// any spaces.
//
//		argv[1] = device name
//		argv[2] = text to send
//
// To compile with MinGW:
//
//		gcc -o SerialSend.exe SerialSend.c
//
// To compile with cl, the Microsoft compiler:
//
//		cl SerialSend.c
//
// To run (this example sends the characters "S365" via COM1):
//
//		SerialSend COM1 S356
//
// References:
//
//	Robertson Bayer, "Windows Serial Port Programming", March 30, 2008
//	(I used code from Bayer's serial port tutorial as my starting point)
//

#include <windows.h>
#include <stdio.h>

int main(int argc, char *argv[])
{
	// Declare variables and structures
	HANDLE hSerial;
	DCB dcbSerialParams = {0};
	COMMTIMEOUTS timeouts = {0};
	DWORD dwBytesWritten = 0;

	char dev_name[MAX_PATH];
	char text_to_send[MAX_PATH];

	// Parse command line arguments
	if (argc < 3)
	{
		fprintf(stderr, "Usage:\n\n\tSerialSend DEVICE_NAME TEXT_TO_SEND\n");
		return 1;
	}
	strcpy(dev_name, argv[1]);
	strcpy(text_to_send, argv[2]);

	// Open the specified serial port (first command line argument)
	fprintf(stderr, "Opening serial port %s...", dev_name);
	hSerial = CreateFile(argv[1], GENERIC_READ|GENERIC_WRITE, 0, 0,
							OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
	if (hSerial==INVALID_HANDLE_VALUE)
	{
		fprintf(stderr, "Error\n");
		return 1;
	}
	fprintf(stderr, "OK\n");

	// Set device parameters (38400 baud, 1 start bit,
	// 1 stop bit, no parity)
	dcbSerialParams.DCBlength = sizeof(dcbSerialParams);
	if (GetCommState(hSerial, &dcbSerialParams) == 0)
	{
		fprintf(stderr, "Error getting device state\n");
		CloseHandle(hSerial);
		return 1;
	}
	dcbSerialParams.BaudRate = CBR_38400;
	dcbSerialParams.ByteSize = 8;
	dcbSerialParams.StopBits = ONESTOPBIT;
	dcbSerialParams.Parity = NOPARITY;
	if(SetCommState(hSerial, &dcbSerialParams) == 0)
	{
		fprintf(stderr, "Error setting device parameters\n");
		CloseHandle(hSerial);
		return 1;
	}

	// Set COM port timeout settings
	timeouts.ReadIntervalTimeout = 50;
	timeouts.ReadTotalTimeoutConstant = 50;
	timeouts.ReadTotalTimeoutMultiplier = 10;
	timeouts.WriteTotalTimeoutConstant = 50;
	timeouts.WriteTotalTimeoutMultiplier = 10;
	if(SetCommTimeouts(hSerial, &timeouts) == 0)
	{
		fprintf(stderr, "Error setting timeouts\n");
		CloseHandle(hSerial);
		return 1;
	}

	// Send specified text (second command line argument;
	// cannot contain spaces)
	fprintf(stderr, "Sending text: %s\n", text_to_send);
	if(!WriteFile(hSerial, text_to_send, strlen(text_to_send),
					&dwBytesWritten, NULL))
	{
		fprintf(stderr, "Error writing text to %s\n", dev_name);
	}
	else
	{
		fprintf(stderr, "%d bytes written to %s\n",
				dwBytesWritten, dev_name);
	}

	// Close serial port
	fprintf(stderr, "Closing serial port...");
	if (CloseHandle(hSerial) == 0)
	{
		fprintf(stderr, "Error\n", dev_name);
		return 1;
	}
	fprintf(stderr, "OK\n");

	// exit normally
	return 0;
}

I’m planning to develop this program a little further to make it more flexible so that it can be used in a wider variety of applications. Features I’m thinking of adding include:

  • Send longer strings, including ones that contain spaces.
  • Select baud rate.
  • Specify a time delay between transmitted bytes.
  • List available serial ports.
  • Default to first serial port.
  • Default to highest numbered serial port.
  • Send the contents of a file.
This entry was posted in Uncategorized and tagged , , , , , , , , , , , . Bookmark the permalink.

29 Responses to SerialSend – a Windows program to send a short text string via serial port

  1. r says:

    Thanks! This has helped a lot! I just had to find out how to use COM ports larger than 9. That’s also not straightforward in windows.

  2. nasrullah says:

    wow amazing ,,thanks sir

  3. hortnon says:

    The code you have here is able to send spaces if the 2nd argument is in quotes when run. Anyway, this works great, except I had to change the baud rate (not a big deal), and I had to add a \r\n to the send to get my device (projector) to work with the output sent.

    • batchloaf says:

      Great, glad you found it useful. I’ve found myself using it quite a bit, so I suppose I should tidy it up a bit (e.g. configurable baudrate etc). Anyway, thanks for the feedback.

  4. Richard says:

    Works a treat.

    I noticed in your REM statements:

    “017 // To compile with cl, the Microsoft compiler:
    018 //
    019 // cl SerialSend.c

    Does this mean the code could be compiled with Visual Studio 2012 C++
    Could certainly use this program if I new how to modify it.

    • batchloaf says:

      Hi Richard,

      Yes, there should be no problem compiling this with Visual Studio. I suppose you’d need to create a new “Win32” project. If you’re using the New Project Wizard, I think you’ll want to select something like “Empty project”. Unfortunately, I can’t remember the exact details of creating a new project in Visual Studio because it’s been quite a while.

      By the way, if you have Visual Studio installed, then cl.exe is already on your computer. In the Visual Studio group in your program menu you may find something like “Visual Studio command line” or somthing like that which opens a console window with the path set for running cl. The command I suggest in the code comments can be run in this console window.

      Ted

      • Richard says:

        Thanks for the quick reply.
        Sunday morning was free so I compiled it “Win32” project and with the wizard “Empty project” I don’t think I made any mistakes. Displayed 2 warnings and 2 errors.

        Line(21): warning C4996: ‘strcpy’: This function or variable may be unsafe. Consider using strcpy_s instead. h(110) : see declaration of ‘strcpy’
        Line(22): warning C4996: ‘strcpy’: This function or variable may be unsafe. Consider using strcpy_s instead. h(110) : see declaration of ‘strcpy’

        Line(27): error C2664: ‘CreateFileW’ : cannot convert parameter 1 from ‘char *’ to ‘LPCWSTR’
        Line(26): error IntelliSense:argument of type “char*” is incompatable with parameter of type “LPCWSTR”

        I changed the ‘strcpy’ to ‘strcpy_s’ as suggested above which fixed the 2 warnings and now only left with the 2 errors.
        Getting close. Cheers Richard

      • batchloaf says:

        Hi Richard,
        Oh yes, I remember seeing this before. The basic problem here is that when Visual Studio creates a new project, by default it sets the character type to Unicode in the project settings. To facilitate programs that represent strings either with Unicode (multi-byte characters) or single-byte characters, many Win32 functions (including CreateFile) come in two versions, one which expects string arguments in Unicode format and one which expects single-byte character strings. For example, the two versions of CreateFile are CreateFileW (the Unicode version) and CreateFileA (the single-byte character version). When you compile your project, Visual Studio actually substitutes one or other of these for your CreateFile call. Which one it uses depends on the project settings. Since your project is probably set to use Unicode strings, Visual Studio is probably using “CreateFileW” which expects a Unicode string for the filename. However, your filename is actually a single-byte character string.

        Anyway, to cut a long story short, there are a few different solutions to this. Two easy ones are:

        • Explicitly call “CreateFileA” instead of “CreateFile”. i.e. just add a capital A onto the end of the word “CreateFile” on line 56 in the listing above (or line 26 in your program) and try recompiling.
        • Change the project character set. Go to “Project Properties -> General -> Project Defaults -> Character Set” and change it to “Not Set”. Note: The exact location of this setting could be different in VS 2012.

        You only need to do one or the other of those solutions – not both. Let me know how you get on.

        Ted

  5. Richard says:

    You where spot on Ted. I took the easy road this time and just added a capital A. Compiled perfectly and this is where I’m sure the fun part starts, learning how to modify the code to suit my needs. I have learnt a lot about C++ in the past few days, as this is my first experience with Visual Studio 2012. I know there is a lot more to learn about C++.

    Thanks very much for your help.

    Cheers Richard

    • batchloaf says:

      You’re very welcome! Best of luck with whatever you’re working on.

      By the way, if you’re interested in Win32 programming using C/C++, check out theForger’s Win32 tutorial. It’s not a C++ tutorial, but it’s the best introduction I found to the Win32 API and got me up and running really fast.

      Ted

  6. sharmistha sharma says:

    hi

    i have been surfing fr a long tym and didnt find anytng helpful after reading your blog i feel u will definitely be a grt help to me..i am doin a project in vc++ 6.0 standard edition where i a have to ” send 5 bytes to comport via rs 232″ without any gui. so my project will be basically a simple c prg to send bytes which i am unable to do since a month:(plz help

    • batchloaf says:

      Hi Sharmistha,
      I should be able to help you with this. If you can provide the following details, I’ll sketch out some simple example code.

      • Is it always the same COM port number that you are sending the bytes to? If so, which one? If not,
      • Is it always the same 5 bytes? If so, what are they?
      • What version of Windows are you running this on?

      Don’t worry, we should probably have your problem solved very soon!

      Regards,
      Ted

  7. sam says:

    hello

    thanks a lot for replying…
    1.yes…it is d same comport number…however my sir has told i can use any com port here its just it has to receive the bytes from the c program.

    2.yes, i am sending 5 bytes only it can be random 5 bytes like 0xAA, 0x00, 0x11,0xBB,0x95.
    so on…

    3. i am using windows xp and visual c++6.0 version standard edition.

    hope to get a reply soon…

    regards
    sharmistha

  8. sharmistha sharma says:

    hello

    thanks a lot for replying…
    1.yes…it is d same comport number…however my sir has told i can use any com port here its just it has to receive the bytes from the c program.

    2.yes, i am sending 5 bytes only it can be random 5 bytes like 0xAA, 0×00, 0×11,0xBB,0×95.
    so on…

    3. i am using windows xp and visual c++6.0 version standard edition.

    hope to get a reply soon…

    regards
    sharmistha
    Reply

  9. sharmistha says:

    hi
    that was really amazing:) i am so much thankful to u…and would like to keep updating you about my project 🙂

    would like to ask you one more thing in place of the decimal values can i send hex values like”0xaa, 0xbb…etc and in place of com22 i can replace it by com 1 right?

    if not plz correct me !!!

    thanks again:)
    sharmistha

  10. batchloaf says:

    Hi Sharmistha,

    You’re welcome. There’s no problem replacing the 5 byte values I used with arbitrary hex values. Just modify lines 26 to 30 as follows:

    	bytes_to_send[0] = 0xAA;
    	bytes_to_send[1] = 0xBB;
    	bytes_to_send[2] = 0xCC;
    	bytes_to_send[3] = 0xDD;
    	bytes_to_send[4] = 0xEE;
    

    Also, to use COM1 instead of COM22, just replace “\\\\.\\COM22” with “\\\\.\\COM1” wherever it appears in the program (just line 40, I think).

    Let me know how you get on.

    Regards,
    Ted

  11. sharmistha sharma says:

    hi ted

    thanks to you the program is working fine with com1 and the hex values..i have conected the port with rs 232 pin to the oscilloscope and we could see the pulses corresponding to the 5 bytes send….so it worked:)

    regards
    sharmistha

  12. Pingback: C and Arduino

  13. Rafa Kory says:

    Hi,
    I want to send this hexadecimal string: 100201000a0b1003100201063c001e00251003100201093c00341003
    I am using this command: SerialSend.exe /baudrate 4800 /devnum 05 “0x100201000a0b1003100201063c001e00251003100201093c00341003”
    I do not find anything wrong,
    I cann’t specify the parity Even.
    Regards
    Rafa K.

  14. schutki says:

    For such simple task it might be easier to simply download some RS232 terminal program.
    For example docklight has many options and among those also to define strings to send or parts of the string, and use joker signs to be prompted for those parts of string sent. Great features and also scripting.
    Their homepage is
    http://docklight.de/

    Still great tutorial there! 🙂

    • batchloaf says:

      Hi Schutki,

      The original purpose of SerialSend was to facilitate extremely lightweight scripting of simple command transmission from PC to serial-connected device. It was primarily intended either for use in a batch file or to be called from a C program using the system() function. Of course, there are a number of serial comms programs for Windows that support scripting, but they tend to be more substantial applications and all the ones I’m aware of are commercial products which either must be paid for or have some legal restrictions on their use. SerialSend is very lightweight, very simple, and it’s open source. It certainly won’t do all the things a more advanced program will do, but if someone wants to modify for their own needs they can; if they want to deploy it (or a modified version of it) as part of solution for a customer or whatever that’s no problem. All you need to do is include the one little exe file in your folder and it’s ready to go.

      For me, the most useful feature of SerialSend is that it can automatically find the highest available serial port number and send the message to it. This gets around the problem of my USB-to-serial converter being assigned a different number each time it’s plugged in. This feature is not included in the early version shown in this post, but it’s in the current version, which is here.

      Ted

Leave a comment