We are turning a remote system on or off by setting the DTR line of a serial COM port (pins other than DTR and GND are left unconnected). The remote system is turned on when the voltage is less than -3.5V, otherwise it's turned off.
By default DTR is turned off and the pin outputs -9V (i.e. remote system is turned on). Using the SerialPort class of .NET, turning the remote system off is as simple as
var port = new SerialPort("COMx");
port.Open();
port.DtrEnable = true;
My problem is that, as soon as the port object is being disposed, the default state of DTR is restored (OFF) and therefore the remote system is turned back on. Of course this is not a problem as long as the "control program" runs because I can keep the port object alive. However, how can I keep DTR enabled when the user closes the program?
I figured it can't be done using the .NET class so tried to use Win32 api functions instead:
var handle = CreateFile(sPortName, 0x80000000 | 0x40000000, 0, IntPtr.Zero, 4, 0, IntPtr.Zero);
var config = new DCB();
GetCommState(handle, ref config);
config.Flags = 0x11; //set fDTRControl and fBinary
SetCommState(handle, ref config);
CloseHandle(handle);
Unfortunately, the call to 'CloseHandle' also restores the default settings of the COM port so this code doesn't work either.
Is there a way to make this work other than creating a permanently running background service?
Related
So just a bit of context I have 2 devices that communicate with each other by rs232 with RTS/CTS and i need to replace one of the devices with my own device and software.
I am trying to achieve this by using the System.IO.Ports.SerialPort library that from what i could gather online this library handles the handshaking by itself if we set the RtsEnable property to "true" and the Handshake property to "RequestToSend" but I am facing some issues receiving data from the device.
When testing my software i am not being able to receive anything from the device but if I change the wiring to simply work as a sniffer between the two original devices I can receive the data properly. From what I can gather it must be something I am doing wrong to handle the handshaking.
Here is the code i am trying:
stationPort = new SerialPort(stationPortNumber, baudRate, parity, dataBits, stopBits);
stationPort.RtsEnable = true;
stationPort.Handshake = Handshake.RequestToSend;
stationPort.DataReceived += StationPort_DataReceived;
stationPort.Open();
private static void StationPort_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
byte[] bytes = new byte[20];
stationPort.Read(bytes, 0, stationPort.BytesToRead);
string hexConvert = BitConverter.ToString(bytes);
Console.WriteLine("Station OUT >>> " + hexConvert);
}
I also double checked the wiring to make sure it was properly connected and also changed the converter because sometimes these are not wired properly so I chose a MOXA UPort 1150 which is more reliable.
With that being said, my question is what am I doing wrong with my implementation?
Am I supposed to do more to guarantee the handshaking?
Possibly, The equipment you are using may be half-duplex.
In that case, in order to communicate with the SerialPort class of C#, it is necessary to set Handshake to None and control the ON/OFF of the RTS signal by the application program itself.
The SerialPort class API does not have that function.
Handshake Enum
When using the communication function of Win32API, communication may be possible by setting the fRtsControl flag to RTS_CONTROL_TOGGLE in DCB.
DCB structure
RTS_CONTROL_TOGGLE 0x03
Specifies that the RTS line will be high if bytes are available for transmission. After all buffered bytes have been sent, the RTS line will be low.
In any case, you will need to check the communication specifications of the device.
However, even if you are not sure, it may be better to try the above control a little in C#.
You may also want to try #baddack's comment.
As noted, the fact that the DTR signal (DSR for the other party) is ON may be the basis for determining that communication is possible.
In connection with that, are the signal lines cross-connected?
Are TxD/RxD, RTS/CTS, DTR/DSR cross-connected to each other?
Also check what you need to do with these specifications for your equipment.
I have ZKTeco Biometrics device which is connected with a C# windows application using This tutorial (C# ZKTeco Biometric Device Getting Started).
It is working fine but after sometime, my application becoming failed to ping the device. As below code suggested, I am trying to ping the device after every 25 seconds.
private void TimerCheckPingAndCloseAttendanceForm() {
timerCheckPingAndCloseAttendanceForm = new Timer();
timerCheckPingAndCloseAttendanceForm.Tick += new EventHandler(CheckPingAndCloseAttendanceForm);
timerCheckPingAndCloseAttendanceForm.Interval = 25000;//25 seconds.
timerCheckPingAndCloseAttendanceForm.Start();
}
private void CheckPingAndCloseAttendanceForm(object sender, EventArgs e) {
string ipAddress = tbxDeviceIP.Text.Trim();
if (UniversalStatic.PingTheDevice(ipAddress) == false) {
//CloseAttendaceListForm();
IsDeviceConnected = false;
string infoString = "Application started on " + applicationStartDateTime.ToString() + " and ping failed on " + DateTime.Now.ToString() + " then, app closed while device ip is "+ ipAddress;
File.AppendAllText("ConnectionLog.txt", infoString + Environment.NewLine);
Application.Exit();
//timerCheckPingAndCloseAttendanceForm.Tick -= new EventHandler(CheckPingAndCloseAttendanceForm);
}
}
And when I am trying to ping the command from cmd the device show destination host is unreachable. But whenever I restart the device, the ping working fine. I don't know where is the problem? Either the network problem or its coding issue?
Note: I am doing a ping on regular time interval, because on Disconnected Event is not working. I am assuming ping failed meaning is the device has disconnected with the application.
First of all : Thank you for going through my article
You are doing it the wrong way.
Trying to ping the device after every 25 seconds is unnecessary.
The only job of the UniversalStatic.PingTheDevice method is to check if the device is presumably active, the first time you connect with the device.
If you want to check the status of the device i.e IsDeviceConnected, All you need to do is register to the device OnDisConnected event provided by the SDK.
It seems the code here at line number 57 has already done the OnDisConnected event registration for you.
All you need to do now is set your IsDeviceConnected to false when the objCZKEM_OnDisConnected method in the ZkemClient.cs class is called upon by the device itself.
Sample snippet :
In the ZkemClient.cs class file, between line number 81-84
void objCZKEM_OnDisConnected()
{
IsDeviceConnected = false; // <-- Add this line
}
Now, Every time you try to make a call to the device, All you need to do is check for the value of your IsDeviceConnected.
Not having the actual code and the hardware setup, this answer is a bit of a shot in the dark, but here goes …
Since it works initially, this is not a hardware configuration or network configuration issue. Yet it says that after a while the destination (reader) becomes unavailable. This is probably not a network keepalive issue because you are pinging every 25 sec. Looking at the code that you referenced, it shows opening a connection and hooking up callbacks, and making a call to a hardware feature.
My guess would be maybe you are opening the connection each ping and not closing the connection, then after a number of attempts the hardware jams because there are too many open connections. Just a guess. If this is the problem then to fix it, either close the connection or, better, keep the connection open and re-use it.
Alternative possibility would be that the router(s) between your code and the device are detecting too many pings and blocking the connection as a possible DOS attack. If this is the problem then to fix it, configure the router to allow the traffic.
This sounds like the device misbehaving. The error "destination host is unreachable" corresponds to an ICMP packet, same type of packet as ping but different job, being sent by your router saying "I have no idea which device has that IP". This normally happens when the device stop responding to ARP, which basically asks "who has this IP?" and expects a machine to respond "I have it" with its MAC address. The router constantly refreshes its ARP table, forgetting old values.
So when you boot the device it is 'happy', responding to ARP and responding to pings; however, something happens and it at least stops responding to ARP (probably something more wrong with it). Depending on its architecture it could be loaded down doing other stuff and unable to respond, or it could just be locked up.
Try slowing down other actions to the device (if your polling it for information other than ping, do it slower) and also see if you can get status from the device via another output (does it have a uart?).
OPTION 1
Since that restarting the device fixes your problem for a period of time, check that the IP that you are using is not in use on another device/computer/element_of_the_network.
ZKTeco devices come with the IP 192.168.1.201 configured by default. Configure a different static IP and avoid using DHCP (it´s well known that using DHCP in ZKTeco devices isn´t a good choice since they don´t refresh automatically the IP after rebooting the system or any network change).
Make sure that the IP is not in use and that nobody else will use it.
OPTION 2
Another thing that It may be the cause of your problem, is that you are using zkemkeeper in a different part of your application (or into a different application) and you are not closing the oppened connections properly... That may be blocking all network activity from the device. To close the connection just make sure that you call this sdk method after performing all the necessary actions:
sdk.Disconnect();
It looks like a code issue. While investigating UniversalStatic.PingTheDevice(ipAddress), its found that its calling System.Net.NetworkInformation.Ping.Send setting DontFragment = true. Reference: https://github.com/zaagan/BioMetrix/blob/master/BioMetrixCore/Utilities/UniversalStatic.cs#LC51. The timeout for the ping is set to 120 milli seconds. This tries to send 32 bytes of data to the given IP.
Following is the snippet taken from https://learn.microsoft.com/en-us/dotnet/api/system.net.networkinformation.ping.send?view=netframework-4.7.2 would answer the root-cause of your issue
If the DontFragment property is true and the total packet size exceeds the maximum packet size that can be transmitted by one of the routing nodes between the local and remote computers, the ICMP echo request fails. When this happens, the Status is set to PacketTooBig.
So when you restart your device, possibly, the data travelling on the network gets lost. Hence it started working till the packets reaching its limit.
Few suggestions:
Try calling System.Net.NetworkInformation.Ping.Dispose in PingTheDevice before returns
Increase the timeout from 120 milliseconds to seconds.
Increase the timerCheckPingAndCloseAttendanceForm.Interval to 1 min.
Check the return code of the System.Net.NetworkInformation.Ping.Send and find the associated failure meaning
Please share your findings if the above suggestions do not help you finding the root-cause.
you try this code for ping the device,
try
{
IPAddress ipAddress = IPAddress.Parse(ipAdd);
Ping pingSender = new Ping();
PingOptions options = new PingOptions();
options.DontFragment = true;
// Create a buffer of 32 bytes of data to be transmitted.
string data = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
byte[] buffer = Encoding.ASCII.GetBytes(data);
int timeout = 120;
PingReply reply = pingSender.Send(ipAddress, timeout, buffer, options);
if (reply.Status == IPStatus.Success)
return true;
else
{
return false;
}
}
catch (Exception)
{
return false;
}
Thanks.
I have a peculiar problem.
I am trying to communicate with a peripheral unit that requires serial communication in a UWP project. I am using Windows.Devices.SerialCommunication.
For purpose of demonstration, I made a new page that has two buttons, with two different click handlers. One for opening the port, and the other for sending messages to the peripheral.
One handler is:
SerialDevice device;
private async void Button_Click_1(object sender, RoutedEventArgs e)
{
string selector = SerialDevice.GetDeviceSelector("COM7");
DeviceInformationCollection devices = await DeviceInformation.FindAllAsync(selector);
if (devices.Any())
{
DeviceInformation deviceInfo = devices.First();
device = await SerialDevice.FromIdAsync(deviceInfo.Id);
//*********************
device.BaudRate = 9600;
device.DataBits = 8;
device.Parity = SerialParity.None;
device.StopBits = SerialStopBitCount.One;
device.ReadTimeout = device.WriteTimeout = TimeSpan.FromMilliseconds(1000);
device.Handshake = SerialHandshake.None;
}
_dataReader = new DataReader(device.InputStream);
_dataWriter = new DataWriter(device.OutputStream);
}
Peripheral has a red light on it when I enable the power supply. When the line above //********* is executed, the light is switched off. The peripheral doesn't respond to any messages then. When I stop the program, the light switches back on.
I made a .NET Framework app that works perfectly. It is fully functional. I used System.IO.Ports there. I noticed something:
If I extract and run only this part of the code in .NET Framework app:
SerialPort comPort = new SerialPort();
_ComPort.PortName = PortName;
_ComPort.BaudRate = BaudRate;
_ComPort.DataBits = 8;
_ComPort.Parity = Parity.None;
_ComPort.StopBits = StopBits.One;
_ComPort.DataReceived += new SerialDataReceivedEventHandler(_ComPort_DataReceived);
_ComPort.Open();
Nothing more.
And run the UWP app again, the port opens perfectly, the lamp is red, and the device responds to messages. I can switch off the device, and initialize it from the UWP app as many times as I want to. When I restart my computer, I can't initialize the device from the UWP app again, (until I run the said block of code from .NET Framework app).
If you want to know, the peripheral is Bill to Bill unit made by Suzo Happ.
I didn't make any mistakes regarding property initialization in UWP.
I think this is the same issue I'm having.
I repost here a description of the cause and a possible solution:
The UWP SerialDevice class currently only allows you to set "ReadTimeout", which under the hood, sets the "ReadIntervalTimeout" of the actual serial device (https://learn.microsoft.com/en-us/windows/desktop/api/winbase/ns-winbase-_commtimeouts). There are two other timeout values which dramatically affect the read operations behavior: 1) ReadTotalTimeoutMultiplier and 2) ReadTotalTimeoutConstant.
The UWP SerialDevice class does not allow the user to set these two other read timeout values, and even worse, the UWP SerialDevice class does not set these two other timeout values to known values when the serial device is opened. This means that the two other timeout values will be whatever default value the serial driver uses, or worse still, whatever value some serial port application happened to set these two values to be when the other application was last executed.
The overall effect of this is that your UWP application's serial device read behavior is undefined and cannot reliably be used. For example, if these two other timeout values happen to be set one way, then a read operation may block forever waiting on the first byte of data to be read, but if the other timeout values happen to be set a different way, then the read operation may return immediately, with no data read at all. Currently, a UWP application cannot control this behavior, and the behavior will be different across different serial ports, and even perhaps different every time the UWP application is executed.
The UWP SerialDevice class either needs to
1)Allow the user to set these two other read timeout values (preferred), OR
2)Initialize these two other timeout values to known values when the serial device is opened.
The situation: an industrial machine is connected to a Raspberry using a serial port (through USB). It is controlled by strings sent to the serial port.
The project: create an app for Raspberry controlling such machine.
The tools: Visual Studio 2015 on a PC connected to the Raspberry through Ethernet, where the app is being written in C# for Win 10 UWP and deployed to the Raspberry when built for ARM.
The problem: since the machine can take up to some minutes to execute the commands in the real world, it would be nice have a feedback on the Raspberry monitor about the machine current state (besides, obviously, the visual and acoustic clues the operator can have while operating).
There are several examples of SerialDevice code in the internet. I got my code from them. Creating the port:
serialPort = await SerialDevice.FromIdAsync(Id);
serialPort.WriteTimeout = TimeSpan.FromMilliseconds(1000);
serialPort.ReadTimeout = TimeSpan.FromMilliseconds(2000);
serialPort.BaudRate = 9600;
serialPort.Parity = SerialParity.None;
serialPort.StopBits = SerialStopBitCount.One;
serialPort.DataBits = 8;
// serialPort.Handshake = SerialHandshake.None;
// serialPort.Handshake = SerialHandshake.RequestToSend;
serialPort.Handshake = SerialHandshake.RequestToSendXOnXOff;
And sending commands:
public async Task WriteAsync(string cmdText)
{
Task<int> storeAsyncTask;
dataWriteObject = new DataWriter(serialPort.OutputStream);
dataWriteObject.WriteString(cmdText);
storeAsyncTask = dataWriteObject.StoreAsync().AsTask();
int bytesWritten = await storeAsyncTask;
}
I'm not interested right now in reading data sent from the port. The machine doesn't do that. Now, the PC desktop program, whose this app is a simplified porting, has at some point the code to check the state:
bool CTS = serialPort.ClearToSendState;
bool DSR = serialPort.DataSetReadyState;
if (CTS)
if (DSR) // true true
state = EnumStates.ERROR;
else // true false
state = EnumStates.READY;
else
if (DSR) // false true
state = EnumStates.BUSY;
else // false false
state = EnumStates.DISCONNECTED;
The problem is those states never change from false in the Win 10 app, while everything works for the PC version. There is only an unanswered old question about this, and it seems no one cares about the state of the port when creating examples. I tried every type of control flow flag, with no success. I tried to use various stuff the Intellisense of Visual Studio offers, but nothing good came out. Forcing the value serialPort.IsRequestToSendEnabled to true causes an Exception of invalid value.
Any tip is greatly appreciated.
I'm seeing the exact same behavior for serial ports under Windows 10 UWP. SerialPort.DataSetReadyState never changes. The only workaround I've found is to hook the SerialPinChange event, and capture DSR state changes there. Unfortunately, this doesn't give you the value, only the fact that it changed, but it might be possible to track the value given a known initial state.
AddHandler SerialPort.PinChanged,
Sub(sender As Object, e As PinChangedEventArgs)
Select Case e.PinChange
Case SerialPinChange.DataSetReady
DSRchanged = True
End Select
End Sub
This question is related to my earlier question.
Connecting to the pipe is now successful, but I still cannot read (or write) any data from the port.
My first guess was, that the data are buffered. But even when I write (on the client site) 5000 bytes (the buffer in NamedPipeClientStream is 512 byte large), I do not receive any
data.
PipeOptions.WriteThrough didn't changed anything, too.
When I do not use a pipe, but a textfile (in the Virtual-PC settings) to redirect the data written to the COM-Port, the data are written as expected to the textfile. So the client test programm, running in Virtual-PC, is doing fine. The problem is likely in my code below.
var pipe = new NamedPipeClientStream(".", "mypipe", PipeDirection.InOut, PipeOptions.WriteThrough);
pipe.Connect();
// this is blocking
int i = pipe.ReadByte();
var reader = new StreamReader(pipe);
// this is blocking, too
var s = reader.ReadLine();
Update:
The code I am running on the guest os:
var port = new SerialPort("COM1");
port.Open();
port.WriteLine("Hallo");
Using 'echo' in an command prompt as telewin suggested works fine.
What is the difference between echoing and using the above code?
Sorry for the late reply, hope it's still relevant...
In my tests, "echo hello > com1" only works before you run your program (which initiates a new SerialPort) inside VPC. After you run it, "echo hello > com1" will no longer be seen by the host program, until the guest is rebooted.
This suggests that the initialization of the SerialPort itself does something permanent. Using Reflector we find that SerialPort's ctor does nothing of consequence, but its Open method calls the ctor for SerialStream. This ctor does quite a bit: it sets read/write buffers, Rts/Dtr, and handshake mode. After some trials, it seems that the Rts/Dtr screw up the "echo hello > com1". Can you please try this modified code inside VPC:
var port = new SerialPort("com1");
port.DtrEnable = true;
port.RtsEnable = true;
port.Open();
port.WriteLine("Hallo");