Introduction
This is a basic sample of serial port (COM port) listening in C#. This application is connected to a GPS sending ASCII text for test, but the serial port listening part is all byte-oriented.
CodeProject is missing a simple serial port application. Serial port listening applications usually have this only as a part of a bigger solution, while this application does nothing else than list the available COM-ports, list the available baud rates for the selected COM-port, and starts sending the data. In this solution, a form converts the data to ASCII-text and displays it in a text box.
Using the code
The serial port handling code is placed in a class called SerialPortManager
. This class contains methods to start and stop listening for data on the serial port.
Finding the installed serial ports
Rather than just assuming the number of serial ports, or leaving it up to the user to know this beforehand, the code finds the installed serial ports. A string array of serial ports is received through a call made in the constructor of the class SerialPortManager
.
public SerialPortManager()
{
_currentSerialSettings.PortNameCollection =
System.IO.Ports.SerialPort.GetPortNames();
_currentSerialSettings.PropertyChanged +=
new System.ComponentModel.PropertyChangedEventHandler
(_currentSerialSettings_PropertyChanged);
if (_currentSerialSettings.PortNameCollection.Length > 0)
_currentSerialSettings.PortName =
_currentSerialSettings.PortNameCollection[0];
}
Updating baud rates supported by the selected device
When a serial port is selected by the user, a query for supported baud rates is done. Depending on the hardware, different collections of baud rates may be supported. The field dwSettableBaud
from the COMMPROP structure is a join of all supported baud rates.
private void UpdateBaudRateCollection()
{
_serialPort = new SerialPort(_currentSerialSettings.PortName);
_serialPort.Open();
object p = _serialPort.BaseStream.GetType().GetField("commProp",
BindingFlags.Instance | BindingFlags.NonPublic).GetValue(_serialPort.BaseStream);
Int32 dwSettableBaud = (Int32)p.GetType().GetField("dwSettableBaud",
BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public).GetValue(p);
_serialPort.Close();
_currentSerialSettings.UpdateBaudRateCollection(dwSettableBaud);
}
Serial port settings
The class named SerialSettings
contains the currently selected serial port settings, and also includes lists of alternatives for the different setting properties. Everything is data bound to the GUI.
Start listening to a serial port
The serial port is instantiated using the currently selected settings:
public void StartListening()
{
if (_serialPort != null && _serialPort.IsOpen)
_serialPort.Close();
_serialPort = new SerialPort(
_currentSerialSettings.PortName,
_currentSerialSettings.BaudRate,
_currentSerialSettings.Parity,
_currentSerialSettings.DataBits,
_currentSerialSettings.StopBits);
_serialPort.DataReceived +=
new SerialDataReceivedEventHandler(_serialPort_DataReceived);
_serialPort.Open();
}
The actual serial port reading
The actual serial port reading runs in a threadpool. When data is received on the serial port, an event is raised and _serialPort_DataReceived
is called.
void _serialPort_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
int dataLength = _serialPort.BytesToRead;
byte[] data = new byte[dataLength];
int nbrDataRead = _serialPort.Read(data, 0, dataLength);
if (nbrDataRead == 0)
return;
if (NewSerialDataRecieved != null)
NewSerialDataRecieved(this, new SerialDataEventArgs(data));
}
The received byte array is sent to those listening for the event. The class SerialDataEventArgs
houses a byte array.
Stop listening
We stop listening by simply closing the serial port. Note that this might deadlock your UI-thread if you are using Invoke
in the event handling in your form.
public void StopListening()
{
_serialPort.Close();
}
To work around this possible deadlock, a BeginInvoke
is needed. And, that is generally good practice as well.
if (this.InvokeRequired)
{
this.BeginInvoke(new EventHandler<SerialDataEventArgs>(
_spManager_NewSerialDataRecieved), new object[] { sender, e });
return;
}
Summary
A rather simple sample in how to implement serial port listening has been provided.
Updates
- 27 April 2010 - Code clean-up and getting < > to show in the article.
- 10 May 2010 - Fixing some misspells in the article.