• (6 Pages)
  • +
  • 1
  • 2
  • 3
  • Last »

Serial Port Communication in C#

#1 PsychoCoder  Icon User is offline

  • Google.Sucks.Init(true);
  • member icon

Reputation: 1642
  • View blog
  • Posts: 19,853
  • Joined: 26-July 07

Posted 20 October 2007 - 08:09 PM

*
POPULAR

Welcome to my tutorial on Serial Port Communication in C#. Lately Ive seen a lot of questions on how to send and receive data through a serial port, so I thought it was time to write on the topic. Back in the days of Visual Basic 6.0, you had to use the MSComm Control that was shipped with VB6, the only problem with this method was you needed to make sure you included that control in your installation package, not really that big of a deal. The control did exactly what was needed for the task.

We were then introduced to .Net 1.1, VB programmers loved the fact that Visual Basic had finally evolved to an OO language. It was soon discovered that, with all it's OO abilities, the ability to communicate via a serial port wasn't available, so once again VB developers were forced to rely on the MSComm Control from previous versions of Visual Basic, still not that big of a deal, but some were upset that an intrinsic way of serial port communication wasn't offered with the .net Framework. Worse yet, C# developers had to rely on a Visual Basic control and Namespace if they wanted to communicate via serial port.

Then along comes .Net 2.0, and this time Microsoft added the System.IO.Ports Namespace, and within that was the SerialPort Class. DotNet developers finally had an intrinsic way of serial port communication, without having to deal with the complexities of interoping with an old legacy ActiveX OCX control. One of the most useful methods in the SerialPort class is the GetPortNames Method. This allows you to retrieve a list of available ports (COM1,COM2,etc.) available for the computer the application is running on.

Now that we have that out of the way, lets move on to programming our application. As with all application I create, I keep functionality separated from presentation, I do this by creating Manager classes that manage the functionality for a given process. What we will be looking at is the code in my CommunicationManager class. As with anything you write in .Net you need to add the references to the Namespace's you'll be using:

using System;
using System.Text;
using System.Drawing;
using System.IO.Ports;



In this application I wanted to give the user the option of what format they wanted to send the message in, either string or binary, so we have an enumeration for that, and an enumerations for the type of message i.e; Incoming, Outgoing, Error, etc. The main purpose of this enumeration is for changing the color of the text displayed to the user according to message type. Here are the enumerations:

#region Manager Enums
/// <summary>
/// enumeration to hold our transmission types
/// </summary>
public enum TransmissionType { Text, Hex }

/// <summary>
/// enumeration to hold our message types
/// </summary>
public enum MessageType { Incoming, Outgoing, Normal, Warning, Error };
#endregion



Next we have our variable list, 6 of them are for populating our class Properties, the other 2 are access throughout the class so they needed to be made global:

#region Manager Variables
//property variables
private string _baudRate = string.Empty;
private string _parity = string.Empty;
private string _stopBits = string.Empty;
private string _dataBits = string.Empty;
private string _portName = string.Empty;
private TransmissionType _transType;
private RichTextBox _displayWindow;
//global manager variables
private Color[] MessageColor = { Color.Blue, Color.Green, Color.Black, Color.Orange, Color.Red };
private SerialPort comPort = new SerialPort();
#endregion



NOTE:I always separate my code into sections using the #region ... #endregion to make it easier when scanning my code. It is a design choice so it's not necessary if you don't want to do it.

Now we need to create our class properties. All the properties in this class are public read/write properties. We have properties for the following items of the Serial Port:
  • Baud Rate: A measure of the speed of serial communication, roughly equivalent to bits per second.
  • Parity: The even or odd quality of the number of 1's or 0's in a binary code, often used to determine the integrity of data especially after transmission.
  • Stop Bits: A bit that signals the end of a transmission unit
  • Data Bits: The number of bits used to represent one character of data.
  • Port Name: The port with which we're communicating through, i.e; COM1, COM2, etc.
We also have 2 properties that aren't related to the port itself, but with where the data will be displayed, and what transmission type to use:

#region Manager Properties
/// <summary>
/// Property to hold the BaudRate
/// of our manager class
/// </summary>
public string BaudRate
{
	get { return _baudRate; }
	set { _baudRate = value; }
}

/// <summary>
/// property to hold the Parity
/// of our manager class
/// </summary>
public string Parity
{
	get { return _parity; }
	set { _parity = value; }
}

/// <summary>
/// property to hold the StopBits
/// of our manager class
/// </summary>
public string StopBits
{
	get { return _stopBits; }
	set { _stopBits = value; }
}

/// <summary>
/// property to hold the DataBits
/// of our manager class
/// </summary>
public string DataBits
{
	get { return _dataBits; }
	set { _dataBits = value; }
}

/// <summary>
/// property to hold the PortName
/// of our manager class
/// </summary>
public string PortName
{
	get { return _portName; }
	set { _portName = value; }
}

/// <summary>
/// property to hold our TransmissionType
/// of our manager class
/// </summary>
public TransmissionType CurrentTransmissionType
{
	get{ return _transType;}
	set{ _transType = value;}
}

/// <summary>
/// property to hold our display window
/// value
/// </summary>
public RichTextBox DisplayWindow
{
	get { return _displayWindow; }
	set { _displayWindow = value; }
}
#endregion



To be able to instantiate any class object we create we need Constructors. Constructors are the entry point to your class, and is the first code executed when instantiating a class object. We have 2 constructors for our manager class, one that sets our properties to a specified value, and one that sets our properties to an empty value, thus initializing the variables preventing a NullReferenceException from occurring. We also add an EventHandler in the constructor, the event will be executed whenever there's data waiting in the buffer:

#region Manager Constructors
/// <summary>
/// Constructor to set the properties of our Manager Class
/// </summary>
/// <param name="baud">Desired BaudRate</param>
/// <param name="par">Desired Parity</param>
/// <param name="sBits">Desired StopBits</param>
/// <param name="dBits">Desired DataBits</param>
/// <param name="name">Desired PortName</param>
public CommunicationManager(string baud, string par, string sBits, string dBits, string name, RichTextBox rtb)
{
	_baudRate = baud;
	_parity = par;
	_stopBits = sBits;
	_dataBits = dBits;
	_portName = name;
	_displayWindow = rtb;
	//now add an event handler
	comPort.DataReceived += new SerialDataReceivedEventHandler(comPort_DataReceived);
}

	   /// <summary>
/// Comstructor to set the properties of our
/// serial port communicator to nothing
/// </summary>
public CommunicationManager()
{
	_baudRate = string.Empty;
	_parity = string.Empty;
	_stopBits = string.Empty;
	_dataBits = string.Empty;
	_portName = "COM1";
	_displayWindow = null;
	//add event handler
	comPort.DataReceived+=new SerialDataReceivedEventHandler(comPort_DataReceived);
}
#endregion



The first think you need to know about serial port communication is writing data to the port. The first thing we do in our WriteData method is to check what transmission mode the user has selected, since binary data needs to be converted into binary, then back to string for displaying to the user. Next we need to make sure the port is open, for this we use the IsOpen Property of the SerialPort Class. If the port isn't open we open it by calling the Open Method of the SerialPort Class. For writing to the port we use the Write Method:

#region WriteData
public void WriteData(string msg)
{
	switch (CurrentTransmissionType)
	{
		case TransmissionType.Text:
			//first make sure the port is open
			//if its not open then open it
			if (!(comPort.IsOpen == true)) comPort.Open();
			//send the message to the port
			comPort.Write(msg);
			//display the message
			DisplayData(MessageType.Outgoing, msg + "\n");
			break;					
		case TransmissionType.Hex:
			try
			{
				//convert the message to byte array
				byte[] newMsg = HexToByte(msg);
				//send the message to the port
				comPort.Write(newMsg,0,newMsg.Length);
				//convert back to hex and display
				DisplayData(MessageType.Outgoing, ByteToHex(newMsg) + "\n");
			}
			catch (FormatException ex)
			{
				//display error message
				DisplayData(MessageType.Error, ex.Message);
			}
			finally
			{
				_displaywindow.SelectAll();
			}
			break;				 
		default:
			//first make sure the port is open
			//if its not open then open it
			if (!(comPort.IsOpen == true)) comPort.Open();
			//send the message to the port
			comPort.Write(msg);
			//display the message
			DisplayData(MessageType.Outgoing, msg + "\n");
			break;	
			break;
	}
}
#endregion



You will notice in this method we call three methods:
  • HexToByte
  • ByteToHex
  • DisplayData
These methods are required for this manager. The HexToByte method converts the data provided to binary format, then the ByteToHex converts it back to hex format for displaying. The last one, DisplayData is where we marshal a call to the thread that created the control for displaying the data, since UI controls can only be accessed by the thread that created them. First we'll look at converting the string provided to binary format:

#region HexToByte
/// <summary>
/// method to convert hex string into a byte array
/// </summary>
/// <param name="msg">string to convert</param>
/// <returns>a byte array</returns>
private byte[] HexToByte(string msg)
{
	//remove any spaces from the string
	msg = msg.Replace(" ", "");
	//create a byte array the length of the
	//string divided by 2
	byte[] comBuffer = new byte[msg.Length / 2];
	//loop through the length of the provided string
	for (int i = 0; i < msg.Length; i += 2)
		//convert each set of 2 characters to a byte
		//and add to the array
		comBuffer[i / 2] = (byte)Convert.ToByte(msg.Substring(i, 2), 16);
	//return the array
	return comBuffer;
}
#endregion



Here we convert the provided string to a byte array, then the WriteData method sends it out the port. For displaying we need to convert it back into string format, so we use the ByteToHex method we created:

#region ByteToHex
/// <summary>
/// method to convert a byte array into a hex string
/// </summary>
/// <param name="comByte">byte array to convert</param>
/// <returns>a hex string</returns>
private string ByteToHex(byte[] comByte)
{
	//create a new StringBuilder object
	StringBuilder builder = new StringBuilder(comByte.Length * 3);
	//loop through each byte in the array
	foreach (byte data in comByte)
		//convert the byte to a string and add to the stringbuilder
		builder.Append(Convert.ToString(data, 16).PadLeft(2, '0').PadRight(3, ' '));
	//return the converted value
	return builder.ToString().ToUpper();
}
#endregion



The last method that WriteData depends on is the DisplayData method. Here we use the Invoke Method of our RichTextBox, the control used to display the data, to create a new EventHandler which creates a new Delegate for setting the properties we wish for our message, then appending it to the value already displayed:

#region DisplayData
/// <summary>
/// method to display the data to & from the port
/// on the screen
/// </summary>
/// <param name="type">MessageType of the message</param>
/// <param name="msg">Message to display</param>
[STAThread]
private void DisplayData(MessageType type, string msg)
{
	_displaywindow.Invoke(new EventHandler(delegate
{
  _displaywindow.SelectedText = string.Empty;
  _displaywindow.SelectionFont = new Font(_displaywindow.SelectionFont, FontStyle.Bold);
  _displaywindow.SelectionColor = MessageColor[(int)type];
  _displaywindow.AppendText(msg);
  _displaywindow.ScrollToCaret();
}));
}
#endregion



NOTE: You will notice that we hyave added the STAThread Attribute to our method. This is used when a single thread apartment is required by a control, like the RichTextBox.

The next method we will look at it used when we need to open the port initially. Here we set the BaudRate, Parity, StopBits, DataBits and PortName Properties of the SerialPort Class:

#region OpenPort
public bool OpenPort()
{
	try
	{
		//first check if the port is already open
		//if its open then close it
		if (comPort.IsOpen == true) comPort.Close();

		//set the properties of our SerialPort Object
		comPort.BaudRate = int.Parse(_baudRate);	//BaudRate
		comPort.DataBits = int.Parse(_dataBits);	//DataBits
		comPort.StopBits = (StopBits)Enum.Parse(typeof(StopBits),_stopBits);	//StopBits
		comPort.Parity = (Parity)Enum.Parse(typeof(Parity),_parity);	//Parity
		comPort.PortName = _portName;   //PortName
		//now open the port
		comPort.Open();
		//display message
		DisplayData(MessageType.Normal, "Port opened at " + DateTime.Now + "\n");
		//return true
		return true;
	}
	catch (Exception ex)
	{
		DisplayData(MessageType.Error, ex.Message);
		return false;
	}
}
#endregion



Next lets take a look at our event handler. This event will be executed whenever there's data waiting in the buffer. This method looks identical to our WriteData method, because it has to do the same exact work:

#region comPort_DataReceived
/// <summary>
/// method that will be called when theres data waiting in the buffer
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
void comPort_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
	//determine the mode the user selected (binary/string)
	switch (CurrentTransmissionType)
	{
		//user chose string
		case TransmissionType.Text:
			//read data waiting in the buffer
			string msg = comPort.ReadExisting();
			//display the data to the user
			DisplayData(MessageType.Incoming, msg + "\n");
			break;
		//user chose binary
		case TransmissionType.Hex:
			//retrieve number of bytes in the buffer
			int bytes = comPort.BytesToRead;
			//create a byte array to hold the awaiting data
			byte[] comBuffer = new byte[bytes];
			//read the data and store it
			comPort.Read(comBuffer, 0, bytes);
			//display the data to the user
			DisplayData(MessageType.Incoming, ByteToHex(comBuffer) + "\n");
			break;
		default:
			//read data waiting in the buffer
			string str = comPort.ReadExisting();
			//display the data to the user
			DisplayData(MessageType.Incoming, str + "\n");
			break;
	}
}
#endregion



We have 3 small methods left, and these are actually optional, for the lack of a better word. These methods are used to populate my ComboBox's on my UI with the port names available on the computer, Parity values and Stop Bit values. The Parity and Stop Bits are available in enumerations included with the .Net Framework 2.0:
#region SetParityValues
public void SetParityValues(object obj)
{
	foreach (string str in Enum.GetNames(typeof(Parity)))
	{
		((ComboBox)obj).Items.Add(str);
	}
}
#endregion

#region SetStopBitValues
public void SetStopBitValues(object obj)
{
	foreach (string str in Enum.GetNames(typeof(StopBits)))
	{
		((ComboBox)obj).Items.Add(str);
	}
}
#endregion

#region SetPortNameValues
public void SetPortNameValues(object obj)
{
   
	foreach (string str in SerialPort.GetPortNames())
	{
		((ComboBox)obj).Items.Add(str);
	}
}
#endregion



That is how you do Serial Port Communication in C#. Microsoft finally gave us intrinsic tools to perform this task, no more relying on legacy objects. I am providing this class and a sample application to show how to implement what we just learned. What I am providing is under the GNU General Public License meaning you can modify and distribute how you see fit, but the license header must stay in tact. I hope you found this tutorial useful and informative, thank you for reading.

Happy Coding :)

Attached File  SerialPortCommunication.zip (101.85K)
Number of downloads: 117269

This post has been edited by PsychoCoder: 12 November 2007 - 11:58 PM


Is This A Good Question/Topic? 7
  • +

Replies To: Serial Port Communication in C#

#2 Louisda16th  Icon User is offline

  • dream.in.assembly.code
  • member icon

Reputation: 15
  • View blog
  • Posts: 1,967
  • Joined: 03-August 06

Posted 20 October 2007 - 10:57 PM

Nice tutorial! Thanx :)
Was This Post Helpful? 0
  • +
  • -

#3 ecsv8agb  Icon User is offline

  • New D.I.C Head

Reputation: 0
  • View blog
  • Posts: 1
  • Joined: 08-October 07

Posted 29 December 2007 - 12:38 AM

View PostLouisda16th, on 20 Oct, 2007 - 10:57 PM, said:

Nice tutorial! Thanx :)



This code can not be use ! no react of the application
Was This Post Helpful? 0
  • +
  • -

#4 PsychoCoder  Icon User is offline

  • Google.Sucks.Init(true);
  • member icon

Reputation: 1642
  • View blog
  • Posts: 19,853
  • Joined: 26-July 07

Posted 09 February 2008 - 06:33 AM

Well it does work, it was tested many many times before I posted it, and when I wrote it I had no problems with it not receiving and sending data. In fact I still have the application on my machine, I just loaded it up and it worked fine, and its the same application I posted here.

Can you be a little more specific about the problem you're having?
Was This Post Helpful? 0
  • +
  • -

#5 erik006  Icon User is offline

  • New D.I.C Head

Reputation: 0
  • View blog
  • Posts: 1
  • Joined: 23-February 08

Posted 23 February 2008 - 02:09 PM

I've been trying to use this code (as is - from zip file) to communication with an embedded target. The target is connected with to the computer using a usb<->serial adapter. I'm sending data from the target to the computer every second (ascii data). However, even though opening the port seems to be successful, i never receive any data.

I know my setup is functional since I can communicate with the target using Realterm, and i'm receiving the data properly. What could the problem be?

Erik
Was This Post Helpful? 0
  • +
  • -

#6 th4k1dd  Icon User is offline

  • New D.I.C Head

Reputation: 0
  • View blog
  • Posts: 1
  • Joined: 20-March 08

Posted 20 March 2008 - 06:46 AM

Hello, I was so excited to find this post and can't wait to play. I did run into one problem I am having with the code. I went ahead and threw it into C# and compiled. The program detects my one and only COM port, COM12 and adds it to the port drop down box like it should.

However, when I hit the Open Port button, it throws up an error in the Rich Text Box which says that COM1 does not exist. Of course that was not a mistype. I select COM12 and yet it tries to connect to COM1.

I took a look at the code and followed the _portName variable but was unable to see why it would cut off the 2 in 12. It would seem it should not as the variable is setup as a string.

Quick Info:
Windows Vista Business
Microsoft .Net Visual Studio 2008 Express
Arduino USB Microcontroller Card
Port Settings (should be): COM12, 9600, None, 1, 8
Programs Receiving COM12: Arduino Software, Putty, & Realterm

Am I missing something? You help is greatly appreciated.

One thing I noticed...I went into Device Manager and changed the USB Virtual COM port to use COM1 and for some reason the program works fine. Also works great if changed to COM2 in Device Manager.

Any ideas?

* Needs to read up on variable tracing *
Was This Post Helpful? 0
  • +
  • -

#7 kingmighty_spades  Icon User is offline

  • New D.I.C Head

Reputation: 0
  • View blog
  • Posts: 20
  • Joined: 08-February 08

Posted 29 March 2008 - 09:21 AM

Nice tutorial! Thanx :) ;)

This post has been edited by kingmighty_spades: 29 March 2008 - 09:22 AM

Was This Post Helpful? 0
  • +
  • -

#8 PsychoCoder  Icon User is offline

  • Google.Sucks.Init(true);
  • member icon

Reputation: 1642
  • View blog
  • Posts: 19,853
  • Joined: 26-July 07

Posted 29 March 2008 - 01:18 PM

No problem, thanks for the compliment :)
Was This Post Helpful? 0
  • +
  • -

#9 **silver**  Icon User is offline

  • New D.I.C Head

Reputation: 0
  • View blog
  • Posts: 3
  • Joined: 21-December 06

Posted 29 April 2008 - 02:05 AM

Nice tutorial ,But ı have a problem , when I hit the Open Port button, it throws up an error in the Rich Text Box which says that "Access to the port COM1 is denied". Iam using Windows XP and ı use this Serialcommunicator program to communicate with my mouse which ı plug it to COM1 at the same time, can anybody help me ??? :blink:

But when I take off the my Serial Mouse , Program work correctly. Waht can ı do? I have a project , taking serial mouse data and display them on the screen.

This post has been edited by **silver**: 29 April 2008 - 02:57 AM

Was This Post Helpful? 0
  • +
  • -

#10 Pirmin  Icon User is offline

  • New D.I.C Head

Reputation: 1
  • View blog
  • Posts: 1
  • Joined: 30-April 08

Posted 30 April 2008 - 10:18 PM

Hi

It's nice code indeed, but has at least one small fault which has made problems to some people and also to me now.
The code will run correctly as long as you are working with COM1 (Default).
For working with any other Com-Port you need to amend the Method cmdOpen_Click, adding (insert before comm.OpenPort()) the line
comm.PortName = cboPort.Text;


With this correction done, the desired port opens, but I'm still having problems receiving data.
But I think my new problem could be particular to my device and therefore I'm assuming, the code is fine now. :)

Regards
Pirmin

This post has been edited by Pirmin: 30 April 2008 - 10:44 PM

Was This Post Helpful? 1
  • +
  • -

#11 talk2vachan  Icon User is offline

  • New D.I.C Head

Reputation: 0
  • View blog
  • Posts: 1
  • Joined: 08-May 08

Posted 08 May 2008 - 12:33 AM

Hello All,

I am having configuration Windows XP. I had default 2 COM port are working. I had configured Null Modem and activate 2 new ports COM3 and COM4.

Now. i am opening two instance of our application and try to open individual ports COM3 and COM4 with same configuration settings.

But when I send any text information it will shows this text into same text area part. But it will not send whole text to another PORT. Only first character is shown. Other are junk characters.

I do not know how it works .. Can any one give me workaround for this.

Thanking you in Advance. :)
Was This Post Helpful? 0
  • +
  • -

#12 ABNcoder  Icon User is offline

  • New D.I.C Head

Reputation: 0
  • View blog
  • Posts: 1
  • Joined: 15-April 08

Posted 22 July 2008 - 08:22 AM

This is great code. I've studied it thoroughly and have a learned a lot from it. I'm using it to interface with portable devices. Is there a way to speed up the displaydata(). If I'm downloading 5,000 lines, I notice a 30 second difference in the time the device loads, and when the display catches up. Thanks in advance!
Was This Post Helpful? 0
  • +
  • -

#13 Christianne  Icon User is offline

  • New D.I.C Head

Reputation: 0
  • View blog
  • Posts: 5
  • Joined: 16-April 08

Posted 04 November 2008 - 02:19 AM

Has anyone noticed in Task Manager that this app eats up alot of CPU memory and keeps expanding? I think its related to the string.Replace() which is eating up the memory.


Does anyone else notice this????
Was This Post Helpful? 0
  • +
  • -

#14 moallen  Icon User is offline

  • New D.I.C Head

Reputation: 0
  • View blog
  • Posts: 1
  • Joined: 19-December 08

Posted 21 December 2008 - 10:32 AM

Thanks for a great little program. Besides trying to learn everything I can about it, I've been using it to control an Insteon PowerLinc Modem. This is a programable device that can send X10 codes through the power lines. This program works great with the PLM by sending house/unit codes in hex, turning remote X10 modules on & off, etc. I especially like the way the PLM acknowledgement comes back and shows in the program's receive data window.

I do have a couple questions perhaps someone can answer. The PLM requires the baud rate set to 19200, but that isn't a choice in the drop down list of baud rates. I do have my computer's COM1 port set to 19200. I did discover I could change this line in the Main Form to cboBaud.SelectedText = "19200"; to set the proper baud rate for my PLM. But I still didn't find anyplace in the program to make 19200 an option. Is this a value that should have been reported back with string str in SerialPort.GetPortNames() ?

One other issue - I get a warning in my Visual C# 2008 Express Edition that I have an "unreachable Code Detected" in the CommunicationManager line 213 col 21. That's with or without changing the default baud rate to 19200. Any ideas what that's about? It doesn't seem to affect the program's operation.
Was This Post Helpful? 0
  • +
  • -

#15 TenRC  Icon User is offline

  • New D.I.C Head

Reputation: 1
  • View blog
  • Posts: 1
  • Joined: 14-January 09

Posted 14 January 2009 - 01:34 PM

The downloaded file is corrupt or incomplete. Please test and verify. I have uploaded what I received.

Attached File(s)


This post has been edited by TenRC: 14 January 2009 - 01:38 PM

Was This Post Helpful? 1
  • +
  • -

  • (6 Pages)
  • +
  • 1
  • 2
  • 3
  • Last »