Serial ports are not very useful for the end-user, but they are very important for administration of headless (without monitor) servers and for embedded device development.
For example, if you are designing a chip that contains a micro-processor with a serial port (UART) to communicate with an external control (such as a graphical tool running on a PC) chances are that you won’t immediately have a real device attached to the real serial port of the PC. In order to start developing while you wait for the real device (or an evaluation board) you can use virtual serial ports.
com0com is a nice Windows tool to create virtual serial ports and to connect them together, simulating a cable link. It is then possible to run two programs, connected to the two virtual serial ports, that communicate with each other. You can develop your graphical user interface on one side, and a model of the device on the other side.
- Download and install the latest version of com0com from Sourceforge: https://sourceforge.net/project/showfiles.php?group_id=129551&package_id=141993
- If you are a Vista user, disable UAC (more info here: http://support.flex-radio.com/Downloads.aspx?id=218)
- Navigate into your Start menu to com0com and open Setup
- Click “Add Pair” button to create two virtual serial ports. A pop up can appear, telling you that the driver could not be verified. Decide to continue anyway.
- Change the names of the serial ports to COM20 and COM21 (or any couple of names that are not already taken) and click Apply. This is necessary because .NET complains if the name of the serial port does not start with COM.
- Create a new C# Console Project named SerialTest
- In Program.cs, write the following code
using System; using System.Collections.Generic; using System.Text; using System.IO.Ports; namespace SerialTest { class Program { static void Main(string[] args) { string[] names = SerialPort.GetPortNames(); Console.WriteLine("Serial ports:"); foreach (string name in names) Console.WriteLine(name); Console.Write("Choose one:"); SerialPort p = new SerialPort(Console.ReadLine()); p.DataReceived += new SerialDataReceivedEventHandler(p_DataReceived); p.Open(); string line; do { line = Console.ReadLine(); p.Write(line); } while (line != "quit"); p.Close(); } static void p_DataReceived(object sender, SerialDataReceivedEventArgs e) { Console.WriteLine( (sender as SerialPort).ReadExisting()); } } }
- Build, and hit ctrl-F5 to run the program without debugging.
- Into the terminal, choose COM20 port.
- Into Visual Studio, hit ctrl-F5 to run another instance of the program.
- Into the terminal, choose COM21 port.
- Write a line in a terminal and check that in the other terminal the line is indeed been received and written on the screen.
- To exit the terminals, type “quit”.
From this starting point, you can develop your own solution. Keep in mind that the behavior of this serial port emulation could differ from the behavior of the physical device you will attach because com0com does not emulate (for example) power problems, signal attenuation, interference and so on.
As a complex example, “Any Serial Port” is a free open source full-blown serial port manager written in C# that enables the creation of custom drivers and communication protocols using a simplified programming language and a graphical interface.
:: :: :: :: :: :: :: :: :: :: :: :: :: :: :: ::
Dan
2009/09/02
I have a project that uses C# SerialPort class and com0com virtual com ports running on 64-bit Windows 2008 Server. I am running into the following problem.
If I go:
– C# Terminal -> com0com -> TeraTerm it works just fine
– TeraTerm -> com0com -> T38Modem (soft voip fax modem) it also works fine, TeraTerm communicates with the modem and commands and responses go back and forth
– C# Terminal -> com0com -> T38Modem however fails to work, and I’ve spent two days on it and can’t figure it out.
When using TeraTerm, as soon as the connection is established, T38Modem indicates a connection has started. When using C#, it never indicates it see’s the connection, nor does it ever respond to AT modem commands.
I’ve tried sending both \r and \r\n to the modem with no luck.
With your experience with com0com and C# SerialPort class, have you ever seen this? Its strange that if I use a terminal app to talk to the soft modem it works, as does if I use Window’s fax server.
I can only assume something isn’t getting set in the connection correctly, but there are very few options available.
Any ideas? I tried replacing my code with your simple console app and get the exact same problem.
Balau
2009/09/02
Many things are not handled by the simple program I proposed. Most of them involve the other pins of a standard serial port.
I have never worked with T38Modem, but if it indicates that a connection has started as soon as it is connected, then it is possible that it uses Carrier Detect, Clear To Send or Data Set Ready. The C# SerialPort uses the event “PinChanged” to handle the change of those signals.
Before opening the port you can try to do:
p.PinChanged += new SerialPinChangedEventHandler(p_PinChanged);
and create a function p_PinChanged that checks what pins are changed. You can debug it using breakpoints when you plug in the T38Modem.
If it doesn’t respond to subsequent commands, maybe you have to change the Handshake property to something different (the default is “None”) before opening the port, and also:
p.DtrEnable = true;
p.RtsEnable = true;
If you open the com0com window and hover the mouse cursor on the name of the pins (DTR, DSR, …) it will display a brief description of the pin; it can be helpful.
See also:
http://social.msdn.microsoft.com/Forums/en-US/netfxbcl/thread/ffbb63a1-5a04-46c9-a71a-f52b6f7ada81
http://msdn.microsoft.com/en-us/library/system.io.ports.serialport.pinchanged.aspx
http://msdn.microsoft.com/en-us/library/system.io.ports.handshake.aspx
Dan
2009/09/08
Thanks for the help!
I had tried DtrEnable and RtsEnable independently but not together, that fixed it.
I knew it had to be something simple I was missing!
Balau
2009/09/09
Glad to help 🙂
Bakr Younis
2012/04/04
great stuff balau, I ran this simple program and it worked, I have a question for you to make this more useful. in the event handler your print to console and thats fine and all, but what if you want to parse the data and trigger a state machine or another command that gets called from main ? I tried using a global variable but thats just not clean and there has to be a better way to communicate between serial thread and main thread for example. Please let me know if you have any thoughts on that.
Balau
2012/04/04
I think a clean way could be the following:
using System.Threading;
using System.Collections;
...
AutoResetEvent byteReceivedEvent= new AutoResetEvent(false);
Queue byteQueue = Queue.Synchronized(new Queue());
In the serial thread:
...
byteQueue.Enqueue(b);
byteReceivedEvent.Set();
...
In the Main:
...
byteReceivedEvent.WaitOne();
b = byteQueue.Dequeue();
...
Anyway it’s not necessary to use two threads, you can also use the synchronous Read function of SerialPort class directly from Main.
Stijn
2013/10/31
Hi Balau,
I’ve also been playing around with Com0Com ….
I’v gotten everything to work great within native C#, but now I needed things to work using PInvoke of kernel32.dll commands …
For some reason I always end up getting a -1 result back from
pHandle = CreateFile(this.sPortName, FileAccess.ReadWrite, FileShare.None, IntPtr.Zero, FileMode.Open, 0, IntPtr.Zero);
GetLastWin32Error returns “The system cannot find the file specified”
I’m passing “COM10” as portname … but no luck ….
Strangely, conecting through putty works just fine ?!
Any chance you’ve encountered this before?
If not, it was worth the try …
Best Regards,
Stijn, Belgium
Balau
2013/10/31
Try \\COM10 instead. I am not at home so I can’t verify.[Edit] Wrong suggestion. From Windows support (http://support.microsoft.com/kb/115831 ) it seems you need to use “
\\\\.\\COM10
“.Lefteris Kelaidis
2015/03/13
Could you please tell me how i could send AT commands to a dialup modem using your code above?
Great job your code! thanks in advance
Balau
2015/03/15
I’m not familiar with AT commands but my understanding is that they are text lines sent to the modem through serial port.
My example is an extreme simplification of serial port communication.
You should take a look here to create a SerialPort instance: SerialPort Constructor
My suggestion is to run simple commands that return something to check that you initialized serial port with correct baud rate etc. If I get that correctly, commands such as ATI0, ATI1, … should return a value without doing anything. If they answer, you are pretty sure the serial link between modem and PC works.