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
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.
}
I want to detect a connected usb device (FT232R USB UART, Virtual com port driver). This usually worked fine, but due to an windows update my device is detected as FTDI_DEVICE_UNKNOWN. Strange behaviour is, that when I first plug in my device and then start the software, everything works fine. If I first start the software and then plug in the device, it it not detected. I tried to use CyclePort to reconnect the device, but this fails with status FT_OTHER_ERROR. The device is detected and displayed in both cases in the windows device manager. Drivers were already updated and also installed manually.
Any ideas as to what is causing this?
Here is my code:
List<string> pcdSerials = new List<string>();
FTDI ftdi = new FTDI();
UInt32 ftdiDeviceCount = 0;
FTDI.FT_STATUS ftStatus = FTDI.FT_STATUS.FT_OK;
ftStatus = ftdi.GetNumberOfDevices(ref ftdiDeviceCount);
if (ftStatus != FTDI.FT_STATUS.FT_OK)
{
return pcdSerials;
}
FTDI.FT_DEVICE_INFO_NODE[] ftdiDeviceList = new FTDI.FT_DEVICE_INFO_NODE[ftdiDeviceCount];
ftStatus = ftdi.GetDeviceList(ftdiDeviceList);
if (ftStatus != FTDI.FT_STATUS.FT_OK)
{
return pcdSerials;
}
foreach (var info in ftdiDeviceList)
{
if (info.Type == FTDI.FT_DEVICE.FT_DEVICE_UNKNOWN)
{
if (ftdiDeviceList.Count() == 1)
{
// The effect of this function is the same as disconnecting then reconnecting the device from USB.
// Possible use of this function is in situations where a fatal error has occurred and it is difficult, or not possible, to recover without unplugging and replugging the USB cable. This function can also be used after re-programming the EEPROM to force the FTDI device to read the new EEPROM contents which previously required a physical disconnect-reconnect.
ftStatus = ftdi.CyclePort();
if (ftStatus == FTDI.FT_STATUS.FT_OK)
{
// Port has been cycled. Close the handle.
ftStatus = ftdi.Close();
}
else
{
// FT_CyclePort FAILED!
}
break;
}
}
if (info.Description != "FT232R USB UART")
continue;
if (info.Type != FTDI.FT_DEVICE.FT_DEVICE_232R)
continue;
pcdSerials.Add(info.SerialNumber);
}
return pcdSerials;
This NOT an answer, but a comment. Being new to Stack overflow it wouldn't let me simply add a comment to your question, or I don;t know what I am doing. :)
I am having what sounds like the same issue. I wrote a wrapper class over 10 years ago that has worked fine until the new FTDI driver 2.12.36.1 was updated on client's computers. FTDI released driver version 2.12.36.2 on 6/17/2021 and I was hoping that it would resolve the issue but it did not.
What I have found is that calling FT_CreateDeviceInfoList does return the correct number of devices connected. Then I call FT_GetDeviceInfoDetail to obtain the device type, description, and FTDI serial number. The device type comes back as value 0x03 which is FT_Device_Unknown and the description and serial number appear to come back empty. I use the FTDI serial number to open the device by its serial number. Without a valid serial number, I cannot open the device. I have used this method as outlined as the preferred method by FTDI for years with no issues until now.
Hopefully someone else can chime in with an answer as I have a lot of customers who are unable to connect to my product with their PCs.
I am going to reach out to FTDI support to see if they can shed some light on this problem. I will post back here if I hear something back.
I came across the same problem just recently. If you run FT_prog you can replicate this by removing the dongle and putting it back. FT_prog takes a second or so to recognise the device again.
I put the checking code in a loop with a 1 second wait and it now finds the dongle on the second attempt reliably.
It's pretty straightforward to listen for BLE advertisements and get the BluetoothAddress, signal strength etc from the advert. This page says
Once you have the address, you can call BluetoothLEDevice.FromBluetoothAddressAsync to get a reference to the device.
so I did and this gave me a BluetoothLEDevice object with a Name property. Unfortunately the name is frequently - but not always - an empty string.
When I interactively scan for Bluetooth devices, Windows shows me a list of names for devices it can see (excluding already paired devices). It does this very quickly and it shows a number of devices that don't ever show up in the names accumulated from advertising.
Is there a reliable strategy for quickly obtaining the name normally shown when computers and phones list unpaired devices?
I tried the suggestion from Emil, and while this simplifies my code by eliminating the need to obtain a BluetoothLEDevice object, the fundamental problem remains: advertisements cannot be relied upon to supply a name. They don't even contain meaningful manufacturer data.
The code currently looks like this
BluetoothLEAdvertisementWatcher watcher = new BluetoothLEAdvertisementWatcher();
...
watcher.Received += OnAdvertisementReceived;
watcher.Start();
...
private void BleAdvertHandlerAsync(BluetoothLEAdvertisementReceivedEventArgs args)
{
var localName = args.Advertisement.LocalName;
...
}
Fishing the local name into a variable sidesteps the fact that resolving the value entails a COM call which is not allowed in a breakpoint expression.
Playing with the switches on my mouse and keyboard, which can be paired with three different hosts and switched between them, I notice that when I tell them to connect to another host I immediately get advertisements containing names. If the host isn't present there is a steady stream of them that ceases when I switch back to my computer and a session is established.
This suggests that advertisements are not the way Windows populates its list of unpaired hosts.
If you use the advertisement watcher you can check the BluetoothLEAdvertisement.LocalName property.
For your second question, maybe your phone is discovered through Bluetooth Classic?
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.
My C# desktop class library communicates with COM serial port.
The com is not real com, it is a USB to COM cable connected to USB to COM on another computer, the other computer uses a simulator to send data.
This solution works fine and then port opens successfully:
serialPort.Open();
if (serialPort.IsOpen)
{
serialPort.DataReceived += new SerialDataReceivedEventHandler(serialPort_DataReceived);
}
void serialPort_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
try
{
var length = serialPort.BytesToRead;
byte[] buffer = new byte[length];
serialPort.Read(buffer, 0, length);
for (int i = 0; i < buffer.Length; i++)
{
System.IO.File.AppendAllText(#"c:\TestLogging.txt", string.Format("[Time = {0}] Data was received from serial port !", DateTime.Now)); }
}
The simulator send data all the time, and I receive the data in my class, BUT i dont receive it all the time, i see printing of 1 line every 30 seconds - although it suppose to print all the time!
What is weird - If i place break point in the line var length = .. then i see a the prints for the time i was halting on the break.. the data transferred seems to be "cached" and when i hit "Play" I receive the data but only for the time i was pausing the process.
I think you're too late registering the DataReceived eventhandler there. You should register to it before you open the port:
serialPort.DataReceived += new SerialDataReceivedEventHandler(serialPort_DataReceived);
serialPort.Open();
if (serialPort.IsOpen)
{
}
Try connecting your machines using HyperTerminal and check if the communication works. If it does, then it's your application's fault (double-check serialport settings). Otherwise it's probably the cable or the port itself.
Are you sure you're not getting any faulty data? Have you subscribed to SerialPort.ErrorReceived event handler? If you catch some SerialError.Frame errors there, then you probably have a faulty cable or the port is broken, or there are some conditions that are jamming the comunication (not vary likely but it can happen, for example if there is some strong source of power near by, for example an inverter).
OK, what is the problem ?
I am using USB to COM cable, and not true COM port.
The behavior of data receiving event from SerialPort is different.
I listen and catch the data event, but instead of getting array of bytes (as in real COM) - I receive every time a single byte (array in size of 1)..
Meaning: On DataReceived event when working with USB to COM port: i get this: 1-1-1-1-1-1 each DataReceived event give me 1 byte.
Real physical COM .DataReceived event give me: 5-30-42 etc.. Array of bytes
So my solution when working on USB to COM ports (some might call it "virtual COM port") I need to check if serialPort.BytesToRead is bigger then X, then i actually do
if (serialPort.BytesToRead > 2) //I dont want to get it 1-1-1.. want to get it atleast 2-2-2-2
serialPort.Read(buffer, 0, length);
Since serialPort.BytesToRead will tell me how many bytes are in current chunk, and Read. in my terms will get the buffer and clean it.