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.
Related
I am trying to read data from a device connected via USB.
For creating the trigger the code looks like:
private SerialPort realTimePort;
realTimePort = new SerialPort();
realTimePort.BaudRate = 9600;
realTimePort.PortName = "COM4";
realTimePort.ReadTimeout = 5000;
realTimePort.ReadBufferSize = 32768;
realTimePort.ReceivedBytesThreshold = 1;
realTimePort.BaudRate = 9600;
realTimePort.ReadBufferSize = 4096;
realTimePort.ParityReplace = 63;
realTimePort.Parity = Parity.None;
realTimePort.DataReceived += new System.IO.Ports.SerialDataReceivedEventHandler(realTimePort_DataReceived);
realTimePort.Open();
To read the data, which was sent, the code looks like:
public void realTimePort_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
// Do something with the data
}
With one version of the device everything works fine and the trigger starts, when data was sent, but with a newer software-version of the device realTimePort_DataReceived is never triggered.
At first i thought, that the problem might be, that the device never sends data, but then i tried to read the data with "Tera Term" and there i can see exactly, what i am expecting. I also compared the data with "Tera Term", which was sent of the devices with the different software-versions, but it is exactly the same string.
Do you have any ideas, why the event is triggered with the older software-version and not with the newer one?
An employee of the manufacturer of the device already gave me the specification of the SerialPort, because i had this problem, but it didn't help me.
It is hard to reproduce the issue as i am not aware what type of device you are using and what type of data is sends here are some tips you can evaluate a quick check list to ensure the correct data receiving.
1. Play with RTS or DTR port flags for new version device
Basically some new versions of hardware uses flags of SerialPort e.g. DTR (Data Terminal Ready) indicates that your code is ready to receive, and RTS (Request to Send) a request to device to actually send data. for older hardware types it was not mandatory to use these flags although in modern devices its still not but just a standard practice so you should experiment & try enabling these by your code e.g.
realTimePort.RtsEnable = true; //enable this mode
realTimePort.DtrEnable = true; //and also this one
2. Try to read device error stream
It is possible that your new version hardware is sendind data over error stream, the tool you was using utilizes both streams for data read so you can subscrive to error event like.
realTimePort.ErrorReceived += new SerialErrorReceivedEventHandler(sPort_ErrorReceived);
private static void sPort_ErrorReceived(object sender, SerialErrorReceivedEventArgs e)
{
//process your error, it will give you further hint about why part of your question.
}
Back in time(15 years ago) i made a small program on Delphi to monitor, record and show a time-diagram the signals from a I2C communication between PC and I2C-sensor. Now i need the same thing and i have big problem to adapt it for C#. In general i connect CTS line from the serial port to SCL line of a I2C, and DSR to SDA. Then i start a thread that read the status of the CTS and DSR(with endless loop) and save them to a file. This is the code:
void InitCommPort()
{
myCOMport = new SerialPort("COM2", 9600);
myCOMport.Open();
}
while (!bExit)
{
CTS_Signal = myCOMport.CtsHolding;
DSR_Signal = myCOMport.DsrHolding;
}
The problem is that i can not get the signals[CTS and DSR] fast enough to reconstruct the communication protocol. Actually mostly i can get only one of the signals, and it is just part of the all transfer. I tried also "SerialPinChangedEventHandler", but I'm receiving only '0' as a result. Obviously I'm missing something or just the SerialPort-Class is made in this way. Is it possible to be done with C#? I do not want to install Windows XP for something simple like that. Any help will be appreciated.
Thank You in advance
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.
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
I'm trying to figure out which serial port belongs to an Arduino.
Since the SerialPort class does not reveal any information about
the underlying hardware, I'm trying to use LibUsbDotNet instead.
Using the following code to get a list of all devices:
UsbDevice.ForceLibUsbWinBack = true;
var devices = UsbDevice.AllDevices;
I can then either iterate over all of them or search for the Arduino specifically:
var deviceFinder = new UsbDeviceFinder(0x2341, 0x8036);
var arduino = devices.Find(deviceFinder);
This actually works and I get an instance of UsbRegistry,
but I'm unable to open the device or determine through which serial port it is exposed.
USBDevice arduinoUsbDevice;
usbRegistry.Open(out arduinoUsbDevice);
Since this doesn't work arduinoUsbDevice remains null.
I then tried using the DeviceNotifier class which raises an event whenever
a device is added or removed from the system:
var notifier = DeviceNotifier.OpenDeviceNotifier();
notifier.OnDeviceNotify += (s, e) =>
{
WriteLine(e.Device?.Name ?? "no device");
WriteLine(e.Device?.IdProduct ?? 0);
WriteLine(e.Device?.IdVendor ?? 0);
WriteLine(e.EventType);
WriteLine(e.Object);
WriteLine(e.Port?.Name ?? "");
};
Now whenever I connect the Arduino to the computer, the event is raised twice.
As if two separate devices are being connected,
but only one of them is ever returned by UsbDevice.AllDevices:
` \\?\USB#VID_2341&PID_8036#7&533912d&0&2#{a5dcbf10-6530-11d2-901f-00c04fb951ed}
32822
9025
DeviceArrival
FullName:USB#VID_2341&PID_8036#7&533912d&0&2#{a5dcbf10-6530-11d2-901f-00c04fb951ed}
Vid:0x2341
Pid:0x8036
SerialNumber:7&533912d&0&2
ClassGuid:a5dcbf10-6530-11d2-901f-00c04fb951ed
no device
0
0
DeviceArrival
[Port Name:COM5]
COM5
The first time the event is raised for the device we could find before as well.
The second time it is raised with e.Device set to null but
with e.Port set to COM5, which is the information I'm after.
So the problem is I can only get this information when the Arduino is connected
after the software has been started and even then linking the two events is
kind of a guessing game.
Is there any way of getting the information without having to rely
on the events raised by the DeviceNotifier class?
I'm aware I could use System.Management and WMI queries, but
these are not available on Linux and MacOS, which is why I'm using
LibUsbDotNet instead.
The native library I'm using is libusb-1.0