Friday, January 4, 2008

SerialPort (RS-232 Serial COM Port) in C# .NET

SerialPort (RS-232 Serial COM Port) in C# .NET
Way in the Past...
Back in my early (pre C# MVP) days, I had written an article on DevHood titled Serial COM Simply in C#. It became quite popular and is (currently) the top Google search for "serial c#". Now every week I get several e-mails with people asking questions and it is high time for some serious updating.

The article was (originally) written soon after .NET was introduced to the world, and other .NET serial port controls had not yet been written. So at the time, this was an easy solution. Just use the MSComm.ocx control from VS6 which most Devs had at the time. Now however, there are many easier and preferred methods than dealing with the complexities of interoping with an old legacy (non-.NET) ActiveX OCX control.

A Bright Future is Here!
Today, the primary solution is to use the new SerialPort control that is part of .NET 2.0 (currently in Beta) and is freely available in C# Express on MSDN. It is considerably easier to use and is a true .NET component.

For loyal fans of the tutorial, I've written a sample code application, SerialPortTerminal.zip, which you can try out to see how the new SerialPort control is used. Since Visual Studio 2005 / .NET 2.0 is still under development, you will need to compile the app on your system before running it.

Continued Support
Due to the high volume of e-mail I've been receiving on serial port communication in general, I've wrote this post and added an FAQ section to the bottom of the post. Check this first if you have questions. If you post a question as a comment that hasn't been answered in the post, I'll add it to the FAQ section.

For those of you who I've directed to this blog post, please understand that I'm just another guy working away at a full time job (helping manage the release of Team System) who has worked with serial RS-232 ports in the past (moving on to USB now). I like to help people, hence I wrote the original article, this sample code, and the FAQ, but do not have the resources to answer additional serial port communication questions. Please check out the SerialCom FAQ for other .NET COM Port solutions and technical support options.

Get Connected Up

You can obtain USB to Serial adapters (from NewEgg, search using Froogle) and have just about as many ports on your PC as you like. I carry around two adapters with a null modem (what is it?, search Froogle) between them so I can create a loopback to send & receive through to separate ports on most any computer. I'd recommend doing the same for when writing code for the serial port.

If you'd like to quickly and easily create your own external devices to communicate with the PC, I recommend starting with the Parallax BASIC Stamp modules. Specifically the BASIC Stamp 2 Module (~$50) in a Starter Kit (~$170) is a good springboard. Parallax has lots of easy accessories (such as LCDs, RF, Sounds, AD & DA, etc) for their modules. After that you could migrate to an Atmel Microcontroller (recommended) or Microchip PIC.

See project photos of the Basic Stamp microcontroller and USB to Serial w/ null modem loopback.

Time for the Code
Here is an example of how easy it is to use the new SerialPort control.

Very simply, here is how you can send a bit of data out the port.

// This is a new namespace in .NET 2.0 that contains the SerialPort class
using System.IO.Ports;

private static void SendSampleData()
{
// Instantiate the communications port with some basic settings
SerialPort port = new SerialPort("COM1", 9600, Parity.None, 8, StopBits.One);

// Open the port for communications
port.Open();

// Write a string
port.Write("Hello World");

// Write a set of bytes
port.Write(new byte[] {0x0A, 0xE2, 0xFF}, 0, 3);

// Close the port
port.Close();
}




Now let's take a look at what it takes to read data in from the communications port. This demonstrates reading text; for an example of reading binary data, see my SerialPortTerminal.zip sample app.

Create a new "Console Application" and replace all the default class code with this code
Add a reference to "System.Windows.Forms" to the project
Run w/ F5, to exit the app, press Ctrl-Break.
Get Connected Up with two USB to Serial adapters and a null modem
Use another app, the code above, or the SerialPortTerminal.zip example to send data and watch it come in with this code


#region Namespace Inclusions
using System;
using System.IO.Ports;
using System.Windows.Forms;
#endregion

namespace SerialPortExample
{
class SerialPortProgram
{
// Create the serial port with basic settings
private SerialPort port = new SerialPort("COM1", 9600, Parity.None, 8, StopBits.One);

[STAThread]
static void Main(string[] args)
{
// Instatiate this class
new SerialPortProgram();
}

private SerialPortProgram()
{
Console.WriteLine("Incoming Data:");

// Attach a method to be called when there is data waiting in the port's buffer
port.DataReceived += new SerialDataReceivedEventHandler(port_DataReceived);

// Begin communications
port.Open();

// Enter an application loop which keeps this thread alive
Application.Run();
}

private void port_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
// Show all the incoming data in the port's buffer
Console.WriteLine(port.ReadExisting());
}
}
}




One of the (several) new methods that is supported, and one I'm very glad is finally here, is the ability to obtain a list of the COM ports installed on the computer (ex: COM1, COM2, COM4). This is definately helpful when you want to present the list of ports avalible for the user to select from (as in the SerialPortTerminal.zip Win App example).

foreach (string s in SerialPort.GetPortNames())
System.Diagnostics.Trace.WriteLine(s);




Here are two helpful little methods for sending files through the serial port. Of course, these are the bare essentials and as always, you should check to make sure the port is open first (port.IsOpen) and use try/catch around trying to open a file, but you get the gist with this code. The binary sending routine is limited to about 2GB (the size of an int), but this should be okay for most uses.

using System.IO;

private static void SendTextFile(SerialPort port, string FileName)
{ port.Write(File.OpenText(FileName).ReadToEnd()); }

private static void SendBinaryFile(SerialPort port, string FileName)
{
using (FileStream fs = File.OpenRead(FileName))
port.Write((new BinaryReader(fs)).ReadBytes((int)fs.Length), 0, (int)fs.Length);
}



RS-232 Project Photos
Get connected up with the parts for fun projects like these.
Each of these involve RS-232 serial port communications.

Get connected up with the parts for fun projects like these.
Each of these involve RS-232 serial port communications.
Just what's needed to get started with microcontrollers,
a Basic Stamp, mini LCD display, power, and RS-232 port.


Two USB to Serial adapters with a null modem
to loopback and test your serial software.


The brains to a mini automated radio station that let me
control my PC & home using my HAM radio from around town.



Port Wiring Notes

DB9 Male (Pin Side) DB9 Female (Pin Side)
DB9 Female (Solder Side) DB9 Male (Solder Side)
------------- -------------
\ 1 2 3 4 5 / \ 5 4 3 2 1 /
\ 6 7 8 9 / \ 9 8 7 6 /
--------- ---------

DB9 Female to DB9 Female Null-Modem Wiring
2 | 3 | 7 | 8 | 6&1| 5 | 4
---- ---- ---- ---- ---- ---- ----
3 | 2 | 8 | 7 | 4 | 5 | 6&1

9-pin 25-pin Assignment From PC
------ ------ ------------------------- ------------
Sheild 1 Case Ground Gnd
1 8 DCD (Data Carrier Detect) Input
2 3 RX (Receive Data) Input
3 2 TX (Transmit Data) Output
4 20 DTR (Data Terminal Ready) Output
5 7 GND (Signal Ground) Gnd
6 6 DSR (Data Set Ready) Input
7 4 RTS (Request To Send) Output
8 5 CTS (Clear To Send) Input
9 22 RI (Ring Indicator) Input

- RTS & DTR are binary outputs that can be manually set and held
- DCD, DSR, CTS, and RI are binary inputs that can be read
- RX & TX can not be set manually and are controlled by the UART
- maximum voltages are between -15 volts and +15 volts
- binary outputs are between +5 to +15 volts and -5 to -15 volts
- binary inputs are between +3 to +15 volts and -3 to -15 volts
- input voltages between -3 to +3 are undefined while output voltages
between -5 and +5 are undefined
- positive voltages indicate ON or SPACE, negative voltages indicate
OFF or MARK


Other Resources
Here are some additional sites, libraries, tutorials, etc. These are links that I just found around the net and am providing for convenience (they are not endorsed).

SerialPort on MSDN
Search on Google #1, #2, #3
CFSerialIO
OpenNETCF Port
Where CF = Compact Framework

The Final Say (aka Conclusion)
The new SerialPort class in .NET 2.0 rocks! It is much easier to use than getting the old MSComm.ocx control going in a .NET app, contains new functionality, is a 'native' .NET control, has docs built into the MSDN Library, and is easy to use.

Frequently Asked Questions (FAQ)
I'm adding this section (as of 8/10/06) to address the common questions I get on this post and through e-mail. Chances are, if you ask a good question in the comments here, I'll post it here for others to see easily.

Q: When updating a control (like a text box) while in the DataRecieved event, I get an error.
A: The SerialPort class raises events on a seperate thread than the main form was create on. Windows Forms controls must be modified only on their original thread. Thankfully there is an easy way to do this. Each Windows control has a "Invoke" method which will run code on the control's original thread. So to put the recently recieved data into a text box (txtLog), this would do it: txtLog.Invoke(new EventHandler(delegate { txtLog.Text += comport.ReadExisting(); }); You can see this more in action in the "Log" event of "Terminal.cs" my sample code project, SerialPortTerminal.zip.

Q: I can't find the System.IO.Ports namespace.
A: Using Visual Studio 2003? The new namespace, and SerialPort class, is part of .NET 2.0 and Visual Studio 2005. It is not included in .NET 1.x (and Visual Studio 2003). Even if you have .NET 2.0 or Visual Studio 2005 installed, you can not access the class from within Visual Studio 2003.

Q: I only have .NET 1.1, what can I do?
A: Upgrade to .NET 2.0. Seriously, it's free. In fact, you can get the great C# and VB Visual Studio Intractive Development Environment (IDE) editors for FREE now with C# Express and VB Express. The .NET 2.0 Software Development Kit (SDK) for command-line development is also free. If you really must stay in .NET 1.1, you can use the technique I talk about in Serial COM Simply in C# or a 3rd party library.

Q: I'm sending data to my device, but it is not responding.
A: First make sure the device will respond using a standard app like Hyperterminal. Check the settings (baud rate, data bits, stop bits, etc) and make sure they match with your code. Try sending binary data via binary arrays. Many devices expect a carriage return at the end of a command, so be sure to send 0x0D or \n. String data can be easily converted to a binary array using:
byte[] data = System.Text.ASCIIEncoding.Default.GetBytes("Hello World\n");
com.Write(data, 0, data.Length);
Many devices require several carriage returns first to sync baud rates, so send several, like: com.Output("".PadLeft(9, '\n')); It you're communicating with a modem, make sure Echo Back is turned on. Send "ATE1\n". Other than this, just keep trying and playing around with it. It can be hard because you don't see the response from the device as easily as you would with a terminal app.

Q: When I send recieved data to a text box or rich text box, I get a strange symbols.
A: The default font of text boxes is designed only to show standard characters. Try using "CharMap" (a free tool in WinXP, click "Start", "Run", type "CharMap", enter). "Terminal" is a font designed to show classic ASCII characters and is what most terminal apps (like my sample code and Hyperterminal) use. There are also many ASCII codes that won't display correctly. This is why I choose to show the hex data instead of an ASCII string a lot of the time. System.Convert.ToString(mybyte, 16) will convert a byte to a string hex code, for example: byte b = 13; string s = Convert.ToStrong(b, 16).PadLeft(2, '0'), then s will contain "0D". See the "ByteArrayToHexString" and "HexStringToByteArray" methods in my sample app, SerialPortTerminal.zip.

Q: What about USB communications? How can I do USB?
This post isn't about USB. Believe me, I wish the .NET framework supported USB natively, and I'm doing what I can here at Microsoft to see USB get into the framework in the future. For now, you can use a USB to Serial adapter. I use a lot of these. They plug into the USB port, then appear just as a SerialPort to the PC. Microcontroller vendors such as Microchip, Atmel, and TI make chips that do this for projects. There is a lot of info on USB and C# out there too (such as this great article).

Q: Can I use the sample code here in my own projects (comercial or not)?
Yes! All sample code on my blog is free public domain material. I'm not a legal guy so I don't know the exact words to use, but I'll try... I'm not responsible for any problems! Use at your own rick etc. However, have at it, if it helps you out, fantastic, that's what it's here for.

Q: When doing port.Open() I get the exception "UnauthorizedAccessException".
It may be one of a few factors: It may require Administrative priveledges to use the port. The port may already be open by another program (only one app can use a port at a time). The port may not exist on the computer (this happens a lot). See the question below "How do I know what COM ports are on my PC?"

Q: How do I know what COM ports are on my PC?
Use the Device Manager ("Start", "Run", "devmgmt.msc") and look for the "Ports" node (see below). If you don't see a Ports node, it's because there are not Serial or Parallel ports installed in the PC. Many laptops these day's don't have a serial port. You can get more serial ports very easily today with USB to Serial adapters.



How do I communicate with my specific device? Modem, Mobile Phone, LED/LCD Dispaly, Scanner, etc
This post is specific to general, device independant, serial port communications. You will need to find information about the protocol used for your specific device elsewhere. Comments that ask about specific devices will be deleted (to reduce spam). I'd recommend looking on the manufacture's website, writing/calling the manufacture, or searching online for your specific device, like "Motorola Razor Serial Protocol".

How can I get more support? What are my support options?
Read this FAQ section! :)
You can try leaving a comment below. Perhaps someone knows the answer
Try doing a search and look for other content. "SerialPort C#", "Serial Com C#", "serial port .net"
Post a question in the .NET Base Class Library in MSDN Forums

2 comments:

Aki said...

I want to get some float values sent by a PIC to the serial port using a C# code ... How can I do that?

Steve said...

How can I get the serialport to use auto answer in C#.