C# Serial Port Check if Device is Connected - c#

I've been working with the SerialPort class a lot lately. Currently I'm trying to figure out the proper way to check if a device is connected to the comm port my application uses. Is there any proper way to check if a device is connected to the comm port? My current method is as follows:
while (isReading == true)
{
try
{
received += serialPort.ReadExisting();
if (received.Contains('>'))
isReading = false;
}
catch (Exception e)
{
}
if (tick == 10000)
if (received == "")
{
Console.WriteLine("No Data Received. Device isn't connected.");
isReading = false;
}
tick++;
}
Console.WriteLine(received);
It works but I feel it's a little hacky and unreliable. I can keep it if need be but I'd like it if there's a proper alternative to doing this.
Edit: I actually have to set the tick value to about 10,000 to ensure it's reliable. Otherwise I fail to receive data on occasion. Even setting it to 1000 or 5000 is unreliable. Even then, it's not guaranteed to be reliable across multiple machines.

I too need to work with serial ports, and believe me they are a pain.
My method to check if a device is connected usually revolves around issuing a polling command.
While you method may work, I cant help but be reluctant to use a while loop when an event will suffice.
The .NET serial port class offers some useful events:
Serial.DataReceived Serial.ErrorReceived and Serial.Write
Usually I would issue a polling command at a specified interval to ensure the device is connected.
When the device responds it will fire the DataReceived event, and you can deal with the response accordingly (along with any other neccessary data). This can be used in conjunction with a simple Timer or incremented variable to time the response. Note you will need to set the ReadTimeout and WriteTimeout value appropriately. This, along with the ReadExisting and/or ReadLine method may be of use in your DataReceived event handler.
So, to summarize, (in pseudo code)
Send Polling command, Start Timer
Timer to CountDown for a specified time
If Timer fires, then assume no response
If DataRecieved fires (and expected response) assume connection
(of course handle any specific Exceptions (e.g TimeOutException, InvalidOperationException)

Unfortunately with serial ports, there's no proper way to determine if a certain device is connected. You could write a magic message that only your device would respond correctly to, but as described in this answer, this method could cause problems for other connected devices.
Ultimately, you just have to depend on the user selecting the correct port.
Also, if you happen to lose connection to the device, you would only know when you fail to read/write to it. In this case, just throw a LostConnection event.

I would agree that is a hacky because any device could be connected and sending '>'; but that doesn't mean its your device.
Instead, be dynamic and use something like SerialPort.GetPortNames and WMI Queries to interrogate the devices plugged into the COM ports.
You could use this example as a starting point.
After reading documentation and examples, you should be able to create a list of all device information that registers drivers on the computer and their connected COM port.
Edit:
Since the device doesn't register itself, consider looking at the product drivers for Visual Studio that might make your job a lot easier.

Related

SharpPcap - Incoming packets are dropped

I am writing a C# application which communicates with an external device via ethernet. I am using SharpPcap Version 4.5.0 for this.
Unfortunately, I had to realize that some incoming packets are dropped. For testing, I also put a switch between the external device and my computer, which also logs every packet. On this log, the packet is visible. Hence I am quite sure that the packet is really sent (and it's not an error of the external device).
This is the code that I use:
public bool TryActivateChannel(uint channelNumber, out string message)
{
message = string.Empty;
devices[(int)channelNumber].Open(DeviceMode.Promiscuous);
devices[(int)channelNumber].OnPacketArrival += PacketArrived;
devices[(int)channelNumber].StartCapture();
return true;
}
public bool CloseChannel(uint channelNumber, out string message)
{
message = string.Empty;
devices[(int)channelNumber].OnPacketArrival -= PacketArrived;
devices[(int)channelNumber].Close();
return true;
}
private void PacketArrived(object sender, CaptureEventArgs e)
{
if (e.Packet.LinkLayerType != PacketDotNet.LinkLayers.Ethernet)
{
return;
}
else
{
inputQueue.Enqueue(e);
}
}
devices is just CaptureDeviceList.Instance and inputQueue is a ConcurrentQueue, which is dequeued in another Thread. This thread writes every incoming packet into a *.pcap file (where the packets are missing). Additionally, I look at the Statistics property of my ICaptureDevice, which claims that no packet is dropped. I also tried to run it on a different computer, in order to make sure it is not a problem of the network card.
At this point, I am really helpless. Did I do anything wrong in my code? Is this a known issue? I read somewhere else the SharpPcap can manage up to 3 MBit/s. I am far away from this value, hence I don't believe it's a perfomance problem.
Addendum: Instead of the ConcurrentQueue, I also tried the approach with the List provided by the author. There, I have the same result: Some packets are missing. I also had a version without a second Thread, where the packets are processed directly in the event handler. Same result: Packets are missing. Moreover, I captured simultaneously with Wireshark. Here, the packets are also missing. I realized that the missing packets all have in common that they have a certain length (about more than 60 bytes). For shorter packets, I never observed that they are missing. I am using WinPcap 4.1.3. Is the problem located there?
For the record, if you don't see the packets in WireShark, the problem is neither in your code nor in SharpPcap.
It means it's either in the hardware or in the driver/OS.
Common reasons that you don't receive packets:
The packets were VLAN tagged, depending on the adapter configuration, it may drop VLAN tagged frames before they reach the OS.
Firewall: some firewalls are capable of preventing packets from reaching the Npcap/WinPcap driver, this usually affects IP packets.
Faulty driver: Example: The Npcap bug https://github.com/nmap/npcap/issues/119
Packets "discarded": this means that the packet was rejected by the hardware itself,
you can check for this using the command netstat -e, usual reasons:
Bad cables: yes, really.
Frame collision: occurs more frequently with half duplex cables and when the time between packets is too short.

Using SerialPort to discard RF Device Buffer

I'm writting a small application that automatically connects to the correct serial port by sending a list of commands, and then waiting for a response back from the serial device (RF Transmitter). The serial port objects sends certain commands in decimal format, a reset, login and then a query command.
When the query command is sent, the device then replies back with a response - when this response is received I know I have the correct serial port connection.
All of this works fine, but sometimes I receive an error back from the device - Error 130: TX Queue Overflow. This error can be resolved by simply restarted the device (RF Transmitter), but the frequency of this error is just silly.
Am I correct in thinking that a TX Overflow error would be caused when the buffer on the hardware becomes full? I thought a simple DiscardInBuffer just after opening a connection to the device would fix this - but it doesn't.
When should I use the DiscardInBuffer, am I using it in the correct context?
-- Edit
After some more comments and thoughts, I've come to the conclusion that the SerialPort.DiscardInBuffer won't do anything for my current situation, rather I need to discard the buffer on the actual RF Device - Hence why inplugging it works.
You've sent too much data to the device, and its output queue has overflowed, meaning it is not able to forward the data as fast as you're providing it.
There's no method you can call on the SerialPort class to fix this, these are two completely different buffers we're talking about. Calling SerialPort.DiscardOutBuffer will only discard the output data pending for your serial port, not the device.
To temporarily fix the issue, the manual indicates that you can:
Use the command “reset txqueue” to clear the queue.
The better solution, however, is to prevent the issue and not flood the device with data. The exact way to do this will depend on your hardware.
One way might be to introduce some sort of CommandQueue class which has an associated SerialPort object to push the commands to the hardware. In this class, you could queue up commands to be sent, and send them out a configurable maximum rate. You would use a timer, and only send commands out if one hasn't been sent in the last X msec.
Another way would be to implement some sort of software flow control. It appears that your device supports querying the queue length with the "?STATE" command (page 13). It will respond with:
STATE x1/x2 x3 x4
x1: Number of datapackets in TX queue
x2: Size of TX queue
x3: Status byte (8 bit hexadecimal)
Normal state: status byte = 0
Bit 0 = 1: Error in transceiver
Bit 1 = 1: Error in EEPROM
x4: Current value of the dataset counter (number of last received and saved datapacket)
You could query this before attempting to send a data packet, and simply sleep while the queue is full.
Having written a lot of code to interface with finicky hardware (Serial, Ethernet, etc.) in C#, I can offer the following advice:
Implement an abstract class TN9000DeviceBase which has abstract methods for all of the commands supported by the device.
Derive a class TN9000SerialDevice : TN9000DeviceBase which executes the command using serial port.
This will allow you to come back and implement it via Ethernet when requirements change.

Does RtsEnable or DtrEnable properties send a signal?

I want to know if I put these in my code, does computer send any kind of signal to the device?
SerialPort myport = new SerialPort("COM1");
myport.DtrEnable = true;
myport.RtsEnable = true;
I'm required to send a signal on specific pins to the device. As I know Dtr and Rts use pins 4 and 7. So when I write the code above, will my computer send a signal on pins 4 and 7? Or is there a simple way to send a signal on a specific pin?
Sure, these properties control the state of the handshake signals. Their use is not arbitrary, a properly designed serial port device pays attention to them. DTR is Data Terminal Ready, normally connected to DSR (Data Set Ready) on the device. The device assumes that your computer is simply not turned on or the cable is disconnected when DSR is off. It won't send anything and ignores anything you send to it when the signal is off.
RTS is Request To Send, normally connected to CTS (Clear To Send) on the device. Normally used for flow control, it prevents the device from sending too much data and overflow the receive buffer. A nasty problem that is very hard to recover from, the data is entirely lost.
You should normally set the SerialPort.Handshake property to HandShake.RequestToSend so the driver does this automatically. A very common bug is to leave it set to Handshake.None, now you have to turn on these signals yourself. And you'll risk buffer overflow of course, albeit that you'd have to write very slow code to ever get in the danger zone. It has been done.
These signals can be used in hobby projects to control, say, a reed relay. Do beware that the voltages on the signal lines are unpredictable (swings between +/- 5 to 24 Volts) and can't supply a lot of amps (usually 20 milliamps max). You need at least a diode, typically a transistor to switch a heavier load. Ask about it at electronics.stackexchange.com
In theory: Yes. Whatever you have connected to your serial port will see something. It won't be data, but if it is checking its pin states, it will know that you have done something.
In practice: Maybe. Being able to accurately detect these pin states is heavily dependent on the cable being used. Here is a recent post that goes over the cable related issues with these pin states.

Serial Port Disconnect with exception: system.invalidoperationexception in system.dll [duplicate]

I got a little problem with a USB Barcode Scanner.
I am using the Scanner with the "SerialPort" class:
this._barcodeScanner = new SerialPort(comPort, 9600, Parity.None, 8, StopBits.One) { Handshake = Handshake.None, ReadTimeout = 500, WriteTimeout = 500 };
this._barcodeScanner.Open();
this._barcodeScanner.DataReceived += BarcodeScannerCallback;
If I unplug the USB Device while it´s opened via the "SerialPort" class, I can´t close the software properly and the virtual port stays open for all eternity or till I reboot the whole computer.
So my question is, is there any way to close the virtual port after I unplugged the device via C# code?
Greetings
[edit #1]
Alrighty, some more code:
This way I am checking every 10 seconds if the device is plugged in:
private bool CheckUsbDeviceAvailability()
{
ManagementObjectSearcher searcher = new ManagementObjectSearcher("root\\WMI",
"SELECT * FROM MSSerial_PortName WHERE PortName = '" + this.PortName + "'");
if (searcher.Get().Count > 0)
return true;
return false;
}
Thats the Callback-Event of the Serial Port:
void BarcodeScannerCallback(object sender, SerialDataReceivedEventArgs e)
{
Thread.Sleep(500);
string data = this._barcodeScanner.ReadExisting().Replace(Convert.ToChar(2), Convert.ToChar(32)).Trim();
if (data.StartsWith("AX"))
{
string[] arrData = data.Split('\n');
this._barcodeScanner.StopAvailabilityThread();
Barcode code = new Barcode(arrData[0].Replace("\r", ""));
if (CheckIfBarcodeExists(code))
this.UpdateBarcodeNode(code);
else
this.CreateBarcodeNode(code);
BarcodeScannerCallbackEvent(sender, e, code);
this._barcodeScanner.StartAvailabilityThread();
}
this._barcodeScanner.ComDevicePluggedIn = ScannerDevice.ComAvailabilityState.Available;
}
if it doesnt answer anymore it will fire the "DeviceNotAvailableEvent()":
void BarcodeScannerDeviceNotAvailableEvent()
{
this._barcodeScanner.Close();
this._barcodeScanner.Dispose();
}
I have overriden the Dispose Event of the "SerialPort" class so that it´s going to abort the Thread:
protected override void Dispose(bool isDisposing)
{
if (isDisposing)
{
this._deviceAvailableThread.Abort();
}
base.Dispose(isDisposing);
}
Serial ports date from the stone age of computing. That's where you plugged in your ASR-33 teletype to start typing in your Fortran program. The electrical interface is very simple. So is the Windows API to use a serial port from your own code. Practically any runtime environment supports them.
USB has replaced serial port hardware completely. It has a much more advanced logical interface to the machine, supporting many different type of devices. And it supports Plug and Play, allowing the operating system to detect when a device is attached or removed as well as automatically installing the device driver, etcetera.
This flexibility comes at a price however, a USB device always needs a device driver to become usable. Device drivers are not created equal. Different drivers require different ways to talk to the device. Usually done through DeviceIoControl() or Read/WriteFile() but those are very opaque API functions. In the early days of USB, device manufacturers would supply a DLL that provided a rich API to hide the implementation details.
That did not work so well, manufacturers are not very good at writing good APIs and they sure don't like to support them. So a good solution would be to support a standard API, one that's available on any machine, supported by any runtime, documented and maintained by somebody else. Like the serial port API.
That did not work so well, manufacturers are not very good at writing device drivers that emulate serial ports. The biggest hang-up with the API is that it doesn't have any support for Plug and Play. The core support for it is missing, after all serial port hardware doesn't have the logical interface to support it. There is some support for detecting that a device is attached through the DTR hardware handshake line, but no support whatsoever for detecting that the port is no longer there.
Detaching the USB device is the problem. In an ideal world, the emulator built into the device driver would simply pretend that the serial port is still there until the last handle on the device is closed. That would be the logical implementation, given that there's no way to trigger a Plug and Play event. For some strange reason that seems to be difficult to implement. Most USB drivers take the crummy shortcut, they simply make the device disappear even while it is in use.
This plays havoc on any user mode code that uses the device. Which is typically written to assume it is a real serial port and real serial ports don't suddenly disappear. At least not without drawing a bright blue spark. What goes wrong is pretty unpredictable because it depends on how the driver responds to requests on a device that's no longer there. An uncatchable exception in a worker thread started by SerialPort was a common mishap. Sounds like your driver really gets it wrong, it generates an error return code on the MJ_CLOSE driver request. Which is kind of a logical thing to do for a driver, after all the device isn't there anymore, but quite unsolvable from your end. You have a handle and you can't close it. That's up a creek with no paddle.
Every major release of .NET had a small patch to the SerialPort classes to try to minimize the misery a bit. But there's a limited amount that Microsoft can do, catching all errors and pretending they didn't happen ultimately leads to class that provides no good diagnostic anymore, even with a good driver.
So practical approaches are:
always use the Remove Hardware Safely tray icon in Windows
use the latest version of .NET
contact the vendor and ask for a driver update
ditch vendors that supply lousy drivers
tell your users that, just because it is the only thing you can do with a USB device, that unplugging it doesn't solve any problems
make closing the port easy and accessible in your UI
glue the USB connector to the port so it can't be removed
The 5th bullet is also what gets programmers in trouble. Writing serial port code isn't easy, it is heavily asynchronous and the threadpool thread that runs the DataReceived event is difficult to deal with. When you can't diagnose the software problem you tend to blame the hardware. There's very little you can do with the hardware but unplug it. Bad Idea. Now you have two problems.
This Problem Exists in .Net 2 , 3 , 3.5 you can use framework 4 (problem does not exist in .net 4)

SerialPort.GetPortNames() is wrong

If a detach a serial device which is still being used by windows, SerialPort.GetPortNames() will return that same detached device as actually still being attached. I also can't close the serial port without an unhandeled exception error stating that the port does not exist (which is true).
I can confirm that the device map for SERIALCOMM in the registry is not being updated either, which is where SerialPort.GetPortNames() gets the port names I presume. The hardware manager detects the right COMM ports.
Is there a way around this? Can I simply delete the SERIALCOMM registry entry?
I looked at this potential workaround, but I think that will just let me catch the exception.
I'm not an expert but my experience is that as long as you have any (SW-)object connected to the com-port it will be visible.
When I tested this some years ago I got the same result and not until I did lose my SerialPort object the port disappeared from the GetPortNames list (and presumable also the registry).
I think this is the way Windows handles this driver. If a serial port gets unplugged while in use in SW the system continues to have a place-holder for the serial port until it is released by SW.
In my SW I solved this by having a timeout for the function I needed (the other end could stop working as well) and before every new start of program-loop (normally once every 5min-1h) I just released the COM-port and reconnected to it, if reconnect failed the port was detected as lost...
Hope this helps you.

Categories

Resources