I'm trying to communicate with UHF RFID reader based on AS3992 chip.
This device is detected by Windows as standard HID and it works with 3rd party app (I found some UHF RFID Reader GUI by LinkSprite which works, but it seems like some older C++ application).
So I'm trying to integrate this device support into my .NET application. After some research I tried HidLibrary, but when I'm trying to write something to this device (initial sequence in this sample), it hangs on "write".
Does anybody know what I'm doing wrong?
Thank you!
My OS is Win 8.1 x64.
Here's the sample application:
using HidLibrary;
namespace HidTest2
{
class Program
{
static void Main(string[] args)
{
var devices = HidDevices.Enumerate(0x1325);
var rfid = devices.First();
rfid.OpenDevice();
rfid.Write(new byte[] { 0x31, 0x03, 0x01 }); // Application hangs here
while (true) // I can't get here
{
Thread.Sleep(50);
var result = rfid.Read();
Console.Write(result.Data);
}
}
}
}
PS: I also tried HidSharp, but I got same result. HID device detected, but I can't write into it.
PSS: This is the device: Link to ebay
I can't find a datasheet for the AS3229 chip that you mentioned, so I'm guessing here...
The device is probably presenting as a USB keyboard, so you would typically only be able to write LED status bits to it (Caps lock, Num lock, Shift). Is that what you are trying to write to it?
Try removing the write and just wait for the scanned RFID string to come in.
Edit: It looks like this device is presenting as a serial device over USB...I found a description closely matching it here:
https://s3.amazonaws.com/linksprite/cuttonwood/datasheet.pdf
If it's the same device you are testing then I would try communicating to it over a COM port API rather than using the relatively lower level HID APIs you have been using.
Because time by time I get an email how and if I solved this issue, here's an answer:
I had to replace original firmware for HID communication by firmware for serial communication (search for "as399x uart 115200 hex" or "as399x uart 9600 hex" on the internet) and then it worked like a sharm. Of course you need proper programmer for C8051Fxxx (about 20$ from China), USB-Serial converter and be familiar with some soldering (You'll have to solder pins on board for JTAG and Serial port).
As mentioned above, the device may not actually be a Hid device. Have you tried enumerating through USB devices instead of Hid devices? Here is some code to enumerate USB or Hid devices. The code is here.
For Hid devices use a ClassGuid of : 4D1E55B2-F16F-11CF-88CB-001111000030
and for Win USB devices use: dee824ef-729b-4a0e-9c14-b7117d33a817
https://github.com/MelbourneDeveloper/Device.Net/blob/master/src/Device.Net/Windows/WindowsDeviceConstants.cs
public async Task<IEnumerable<DeviceDefinition>> GetConnectedDeviceDefinitions(uint? vendorId, uint? productId)
{
return await Task.Run<IEnumerable<DeviceDefinition>>(() =>
{
var deviceDefinitions = new Collection<DeviceDefinition>();
var spDeviceInterfaceData = new SpDeviceInterfaceData();
var spDeviceInfoData = new SpDeviceInfoData();
var spDeviceInterfaceDetailData = new SpDeviceInterfaceDetailData();
spDeviceInterfaceData.CbSize = (uint)Marshal.SizeOf(spDeviceInterfaceData);
spDeviceInfoData.CbSize = (uint)Marshal.SizeOf(spDeviceInfoData);
var guidString = ClassGuid.ToString();
var copyOfClassGuid = new Guid(guidString);
var i = APICalls.SetupDiGetClassDevs(ref copyOfClassGuid, IntPtr.Zero, IntPtr.Zero, APICalls.DigcfDeviceinterface | APICalls.DigcfPresent);
if (IntPtr.Size == 8)
{
spDeviceInterfaceDetailData.CbSize = 8;
}
else
{
spDeviceInterfaceDetailData.CbSize = 4 + Marshal.SystemDefaultCharSize;
}
var x = -1;
var productIdHex = GetHex(productId);
var vendorHex = GetHex(vendorId);
while (true)
{
x++;
var isSuccess = APICalls.SetupDiEnumDeviceInterfaces(i, IntPtr.Zero, ref copyOfClassGuid, (uint)x, ref spDeviceInterfaceData);
if (!isSuccess)
{
var errorCode = Marshal.GetLastWin32Error();
if (errorCode == APICalls.ERROR_NO_MORE_ITEMS)
{
break;
}
throw new Exception($"Could not enumerate devices. Error code: {errorCode}");
}
isSuccess = APICalls.SetupDiGetDeviceInterfaceDetail(i, ref spDeviceInterfaceData, ref spDeviceInterfaceDetailData, 256, out _, ref spDeviceInfoData);
WindowsDeviceBase.HandleError(isSuccess, "Could not get device interface detail");
//Note this is a bit nasty but we can filter Vid and Pid this way I think...
if (vendorId.HasValue && !spDeviceInterfaceDetailData.DevicePath.ToLower().Contains(vendorHex)) continue;
if (productId.HasValue && !spDeviceInterfaceDetailData.DevicePath.ToLower().Contains(productIdHex)) continue;
deviceDefinitions.Add(GetDeviceDefinition(spDeviceInterfaceDetailData.DevicePath));
}
APICalls.SetupDiDestroyDeviceInfoList(i);
return deviceDefinitions;
});
}
Related
I'm trying to write a small streamdeck plugin to connect my paired headphones after they have been paired to another device and haven't connected to my PC automatically. I basically want to kick off the same process as clicking Connect in the Windows 11 device list.
I'm able to enumerate my devices, find the correct device based on the device ID, and identify if it is already connected, but I'm not sure how to enable the music and chat connections if it's disconnected.
Here's basically what I have
string[] requestedProperties = {
"System.Devices.Aep.IsConnected",
};
var devices = await DeviceInformation.FindAllAsync(BluetoothDevice.GetDeviceSelector(), requestedProperties);
foreach (DeviceInformation di in devices) {
if (!di.Properties.ContainsKey("System.Devices.Aep.IsConnected")) {
continue;
}
if (di.Properties[ConnectedKey] is true) {
Debug.WriteLine($"Connected Device Name {di.Name}");
Debug.WriteLine($"Connected Device Id {di.Id}");
continue;
}
if ("<myHeadphonesBluetoothId>" == di.Id) {
// Not sure where to go at this point to initiate those connections
}
}
Been googling for hours without any success, and given some attempts at the AudioPlaybackConnection, BluetoothDevice, and RfcommDeviceService classes without success.
My big issue was a misunderstanding in the architecture involved in the way that devices, services, etc work.
I was able to get a small sample app working targeting net6.0-windows10.0.22000.0
const string targetDeviceId = "Bluetooth#Bluetooth00:00:00:00:00:00-00:00:00:0:0:00";
const int serialPortShortId = 0x1101;
using var streamSocket = new StreamSocket();
await EstablishConnection();
async ValueTask EstablishConnection() {
RfcommServiceId targetServiceId = RfcommServiceId.FromShortId(serialPortShortId);
BluetoothAdapter adapter = await BluetoothAdapter.GetDefaultAsync();
if (adapter is not null)
{
Radio btRadio = await adapter.GetRadioAsync();
if (btRadio.State == RadioState.Off)
{
await btRadio.SetStateAsync(RadioState.On);
Console.WriteLine("Bluetooth adapter was off. Turned it on.");
}
}
using BluetoothDevice btDevice = await BluetoothDevice.FromIdAsync(targetDeviceId);
btDevice.ConnectionStatusChanged += (sender, args) => Console.WriteLine("Bluetooth device connected");
RfcommDeviceServicesResult services = await btDevice.GetRfcommServicesForIdAsync(targetServiceId);
RfcommDeviceService service = services.Services.Single();
if (btDevice.ConnectionStatus != BluetoothConnectionStatus.Connected && service.DeviceAccessInformation.CurrentStatus == DeviceAccessStatus.Allowed) {
try {
await streamSocket.ConnectAsync(service.ConnectionHostName, service.ConnectionServiceName,
SocketProtectionLevel.BluetoothEncryptionAllowNullAuthentication);
}
catch (COMException e) when ((uint)e.HResult == 0x8007277C) {
Console.WriteLine("Bluetooth device is not on or in range.");
}
}
}
After running this the device would connect and windows would initialize the mic and speaker devices attached to it.
I'm using a DeviceWatcher to get the DeviceInformation for a paired Bluetooth device in a UWP app. I set the DeviceWatcher up like this
var requestedProperties = new string[] { "System.Devices.Aep.DeviceAddress", "System.Devices.Aep.IsConnected" };
var deviceWatcher = DeviceInformation.CreateWatcher("(System.Devices.Aep.ProtocolId:=\"{e0cbf06c-cd8b-4647-bb8a-263b43f0f974}\")", requestedProperties, DeviceInformationKind.AssociationEndpoint); // ClassGuid = {e0cbf06c-cd8b-4647-bb8a-263b43f0f974} includes all Bluetooth devices
deviceWatcher.Added += DeviceWatcher_Added;
deviceWatcher.Updated += DeviceWatcher_Updated;
deviceWatcher.Start();
When the DeviceWatcher_Added event handler is called I check to see if the device is the one I am interested in by checking its name and that it offers the RfcommServiceId.SerialPort.Uuid service.
Once I have the DeviceInformation for the bluetooth device I am interested in how do I get the COM port for it? I can see it in the Device Manager, where it is listed as "Standard Serial over Bluetooth link (COM8)", but I cannot see how to get that "COM8" in UWP programmatically.
I've tried making the DeviceInformation into a SerialDevice, whereby I could then get SerialDevice.PortName (c.f. this answer) but my call to SerialDevice.FromIdAsync(deviceInfo.Id) fails with a System.Exception: The data is invalid.
(N.B. Some tantalizing answers, like this and this, use the Windows Management Intrumentation WMI functions but these are not available in UWP.)
On another question Rita suggested looking at the Serial UART sample which helped me see a way to do this. I won't mark this as the answer for a while as it seems too indirect to be the canonical way.
Although I have the the DeviceInformation for the paired Bluetooth device in my UWP app I also need the list of SerialDevices so that I can match them up. Here's the resulting code.
public async Task<string> ComPort(DeviceInformation deviceInfo)
{
var serialDevices = new Dictionary<string, SerialDevice>();
var serialSelector = SerialDevice.GetDeviceSelector();
var serialDeviceInformations = (await DeviceInformation.FindAllAsync(serialSelector)).ToList();
var hostNames = NetworkInformation.GetHostNames().Select(hostName => hostName.DisplayName.ToUpper()).ToList(); // So we can ignore inbuilt ports
foreach (var serialDeviceInformation in serialDeviceInformations)
{
if (hostNames.FirstOrDefault(hostName => hostName.StartsWith(serialDeviceInformation.Name.ToUpper())) == null)
{
try
{
var serialDevice = await SerialDevice.FromIdAsync(serialDeviceInformation.Id);
if (serialDevice != null)
{
serialDevices.Add(deviceInfo.Id, serialDevice);
}
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine(ex.ToString());
}
}
}
// Example Bluetooth DeviceInfo.Id: "Bluetooth#Bluetooth9c:b6:d0:d6:d7:56-00:07:80:cb:56:6d"
// from device with Association Endpoint Address: "00:07:80:cb:56:6d"
var lengthOfTrailingAssociationEndpointAddresss = (2 * 6) + 5;
var bluetoothDeviceAddress = deviceInfo.Id.Substring(deviceInfo.Id.Length - lengthOfTrailingAssociationEndpointAddresss, lengthOfTrailingAssociationEndpointAddresss).Replace(":", "").ToUpper();
var matchingKey = serialDevices.Keys.FirstOrDefault(id => id.Contains(bluetoothDeviceAddress));
if (matchingKey != null)
{
return serialDevices[matchingKey].PortName;
}
return "";
}
We have a USB device here which has a EZ-USB FX chip on it and we "talk" to it via libusb-1.0 using LibUsbDotNet. Using Windows everything works fine, but using Mono 3.12.1 under Ubuntu 14.04 it does not work. I did some investigation and testing and found out it can't initialize the device, where we use the following logic (this is from my test program, but the "real logic" is equivalent):
protected bool ResetDevice(UsbDevice Device)
{
Console.WriteLine("First ensure that the EZ-USB 8051 is RESET (CPUCS=1)");
if (!ControlTransfer(Device, new byte[] { 1 }))
return false;
Console.WriteLine ("Then allow the EZ-USB CPU to 're-enumerate' (CPUCS=0)");
if (!ControlTransfer(Device, new byte[] { 0 }))
return false;
Console.WriteLine("Reset done...");
return true;
}
public bool ControlTransfer(UsbDevice Device, byte[] data, short? value = null)
{
int written;
var setupPacket = SetupPacket(value);
return Device.ControlTransfer(ref setupPacket, data, data.Length, out written);
}
internal UsbSetupPacket SetupPacket(short? value = null)
{
var controlPacket = new UsbSetupPacket();
controlPacket.Request = 0xA0;
controlPacket.RequestType = 64;
controlPacket.Value = value ?? 0x07F92;
controlPacket.Index = 0;
return controlPacket;
}
I then used an USB analyzer to profile the commands sent via the USB-Port.
Using Windows I get the following:
which is correct and as you can see the device resets an can be used, but when using Linux I get the following:
Now instead of 01 and 00 the command sends 68 and A8 and so the device does nothing.
Does anyone have an idea what could be the reason of that? Why does it send the wrong data packet under Linux?
It seems this is a Bug of LibUsbDotNet (very likely THIS one), as a build of the current trunk (v2.2.9) did solve this problem.
If you want to know how to use 32feet.NET library to communicate with bluetooth devices, read the solution
I am currently trying to communicate via bluetooth between a computer and a self-built .NET Gadgeteer prototype.
The Gadgeteer prototype consists of the mainboard, a power supply and a bluetooth module. The module is in discoverable mode.
On the computer a custom bluetooth program based on 32feet .NET Bluetooth is running. The program detects all bluetooth devices in range and tries to pair with them. However, this is not done automatically at the moment, I have to enter a pairing code for the device.
How can I pair devices without entering the pairing code?
Devices are found, the problem is the pairing part. I experimented a lot, but didn't find a solution...
foreach (BluetoothDeviceInfo device in this.deviceList)
{
try
{
//BluetoothClient client = new BluetoothClient(this.CreateNewEndpoint(localAddress));
//BluetoothEndPoint ep = this.CreateNewEndpoint(device.DeviceAddress);
EventHandler<BluetoothWin32AuthenticationEventArgs> handler = new EventHandler<BluetoothWin32AuthenticationEventArgs>(HandleRequests);
BluetoothWin32Authentication auth = new BluetoothWin32Authentication(handler);
BluetoothSecurity.PairRequest(device.DeviceAddress, null);
}
}
This code block initiates the pairing and it works, but Windows is asking me to enter the pairing code for the device. I read about the BluetoothWin32Authentication to prevent this case but I don't get it right.
private void HandleRequests(object that, BluetoothWin32AuthenticationEventArgs e)
{
e.Confirm = true;
}
This is the code of the event handler (http://32feet.codeplex.com/wikipage?title=BluetoothWin32Authentication)
If you simply want to allow the pairing to go ahead when to SSP devices are connecting then handling the callback and setting e.Confirm=True will be enough -- but that is a little insecure...
I am confused -.- The goal is that the application and the gadgeteer module can send data in both directions without any user interference.
Is it true that I can't pair devices automatically without user interaction?
Is it true that if two device were already paired they can exchange data without user interaction?
I figured out how to solve my problems and my knowledge about Bluetooth connections is a bit bigger now. If someone else has problems with that, I provide my solution. The code examples represent the C# implementation of a bluetooth controller with the 32feet Bluetooth library.
Scanning
This means that devices in range are detected. My code:
// mac is mac address of local bluetooth device
BluetoothEndPoint localEndpoint = new BluetoothEndPoint(mac, BluetoothService.SerialPort);
// client is used to manage connections
BluetoothClient localClient = new BluetoothClient(localEndpoint);
// component is used to manage device discovery
BluetoothComponent localComponent = new BluetoothComponent(localClient);
// async methods, can be done synchronously too
localComponent.DiscoverDevicesAsync(255, true, true, true, true, null);
localComponent.DiscoverDevicesProgress += new EventHandler<DiscoverDevicesEventArgs>(component_DiscoverDevicesProgress);
localComponent.DiscoverDevicesComplete += new EventHandler<DiscoverDevicesEventArgs>(component_DiscoverDevicesComplete);
private void component_DiscoverDevicesProgress(object sender, DiscoverDevicesEventArgs e)
{
// log and save all found devices
for (int i = 0; i < e.Devices.Length; i++)
{
if (e.Devices[i].Remembered)
{
Print(e.Devices[i].DeviceName + " (" + e.Devices[i].DeviceAddress + "): Device is known");
}
else
{
Print(e.Devices[i].DeviceName + " (" + e.Devices[i].DeviceAddress + "): Device is unknown");
}
this.deviceList.Add(e.Devices[i]);
}
}
private void component_DiscoverDevicesComplete(object sender, DiscoverDevicesEventArgs e)
{
// log some stuff
}
Pairing
This means that devices get coupled with the local bluetooth device. This needs to be done once by entering a code of both sides. Can be done via code so that the user doesn't even notice that a device was added. My code for this purpose:
// get a list of all paired devices
BluetoothDeviceInfo[] paired = localClient.DiscoverDevices(255, false, true, false, false);
// check every discovered device if it is already paired
foreach (BluetoothDeviceInfo device in this.deviceList)
{
bool isPaired = false;
for (int i = 0; i < paired.Length; i++)
{
if (device.Equals(paired[i]))
{
isPaired = true;
break;
}
}
// if the device is not paired, pair it!
if (!isPaired)
{
// replace DEVICE_PIN here, synchronous method, but fast
isPaired = BluetoothSecurity.PairRequest(device.DeviceAddress, DEVICE_PIN);
if (isPaired)
{
// now it is paired
}
else
{
// pairing failed
}
}
}
Connecting
This means establishing a connection and exchanging of data. Again some code:
// check if device is paired
if (device.Authenticated)
{
// set pin of device to connect with
localClient.SetPin(DEVICE_PIN);
// async connection method
localClient.BeginConnect(device.DeviceAddress, BluetoothService.SerialPort, new AsyncCallback(Connect), device);
}
// callback
private void Connect(IAsyncResult result)
{
if (result.IsCompleted)
{
// client is connected now :)
}
}
If you keep the order scan, pair, connect, everything should work fine. To send or receive data, use the GetStream() method of the BluetoothClient. It provides a network stream that can be manipulated.
Receiving a connection
If you want another device to connect with your device you need to listen to incoming connection requests. This only works if the device have already been paired before. My code:
BluetoothListener l = new BluetoothListener(LOCAL_MAC, BluetoothService.SerialPort);
l.Start(10);
l.BeginAcceptBluetoothClient(new AsyncCallback(AcceptConnection), l);
void AcceptConnection(IAsyncResult result){
if (result.IsCompleted){
BluetoothClient remoteDevice = ((BluetoothListener)result.AsyncState).EndAcceptBluetoothClient(result);
}
}
Replace LOCAL_MAC with a valid BluetoothAddress (e.g. by using BluetoothAddress.Parse();). After the devices are connected they can exchange messages via the underlying stream. If the connection does not work there might be authentication issues, so try setting the local device pin in the listener (l.SetPin(LOCAL_MAC, MY_PASSWORD);
I'm about to start developing a small app (C#) that communicates with a PLC and a testing unit via Serial Ports - this is my first venture into this area.
In essence, I am going to send the PLC a signal to start an operation, and then I am going to wait for the result of that operation from the test unit (which will be independently communicating with the PLC) to return a ASCII string.
Depending on the content of that string, I may want to listen to a signal from the PLC...
It's all new to me, so at the moment, I'm just researching System.IO.Ports.SerialPort; digression: are there third part products out there than simplify interaction with the Serial Port, or are the built-in classes as good as you will get? I'm thinking of ease of use as opposed to better features.
However, it will be a few weeks before the hardware is available for development and testing, so I was wondering how I could simulate communication to/from the serial port so that I can start developing my app?
[I don't yet know how the PLC and the PC are due to communicate - I understand it will be binary rather than text, but at the moment, that is all I know.]
Abstract away your serial port comms behind an interface so that you can code your app against the interface and then test with a 'fake' implementation. When you've got the hardware for the real thing, you can code up the 'real' implementation of the interface and swap out the fake one.
So for example, you'd have an interface
public interface ISerialComms
{
void SendMessage(string message)
}
and you'd code your app against that interface using a fake implementation:
public class FakeSerialComms : ISerialComms
{
public void SendMessage(string message)
{
//some implementation
}
}
Hope that helps!
I've had some success in the past using com0com.
There are two pieces of software that I have found invaluable while doing serial port work.
Free Serial Port Monitor
http://www.serial-port-monitor.com
Despite the cheesy name, it is actually quite useful. Note that you should have it stop listening to your port if you go to unplug a USB-to-Serial converter. Otherwise it can crash (well... wait indefinitely on exit, which is annoying). It doesn't have to put itself in the middle of a serial connection to sniff data. It monitors the IO using the Win32 API.
Franson Serial Port Tools
http://franson.com/serialtools/
Or.. any loopback software really. There are lots out there. This allows you to send data and receive it within software. If you end up doing any GPS work, Franson also has a nice GPS simulator so you don't have to sit outside the whole time to debug code.
Finally, if you have had enough with the built-in serial class and its horrendous shortcomings, then you need a replacement, and going straight to the Win32 API will take forever.
CommStudio
I have found CommStudio to be absolutely rock solid. Quite frankly, after spending 5 months researching and buying other options, it is the only one that works perfectly with removable USB adapters. All of the other solutions have issues when the device is plugged back in. You can download their free "Express" version here: http://www.componentsource.com/products/commstudio/downloads.html?rv=42917
I have wrote an article on this topic using Virtual Serial Port Driver 9.0 standard using Microsoft SerialPort Class (Sytem.IO.Ports), it is of course possible to use any other comm port tool.
In the software I create 2 virtual ports COM1 and COM2.
I use COM1 to emulate as data sender.
I use COM2 to receive what ever being send from COM1.
This is helpful if you are developing Embedded or IoT solution.
Emulator (in this example as random accelerometer)
private static bool _continue;
private static SerialPort _serialPort;
public static void Main()
{
var stringComparer = StringComparer.OrdinalIgnoreCase;
var readThread = new Thread(Read);
_serialPort = new SerialPort
{
PortName = "COM1",
ReadTimeout = 500,
WriteTimeout = 500
};
_serialPort.Open();
_continue = true;
readThread.Start();
while (_continue)
{
var x = ValueGenerator();
var y = ValueGenerator();
var z = ValueGenerator();
var message = $"x:{x};y:{y};z:{z}";
if (stringComparer.Equals("quit", message))
{
_continue = false;
}
else
{
_serialPort.WriteLine(message);
Thread.Sleep(200);
}
}
readThread.Join();
_serialPort.Close();
}
public static double ValueGenerator()
{
const int range = 1;
var random = new Random();
return random.NextDouble() * range;
}
public static void Read()
{
while (_continue)
{
try
{
var message = _serialPort.ReadLine();
Console.WriteLine(message);
}
catch (TimeoutException) { }
}
}
And my data receiver is almost similar
private static bool _continue;
private static SerialPort _serialPort;
public static void Main()
{
var stringComparer = StringComparer.OrdinalIgnoreCase;
var readThread = new Thread(Read);
_serialPort = new SerialPort
{
PortName = "COM2",
ReadTimeout = 500,
WriteTimeout = 500
};
_serialPort.Open();
_continue = true;
readThread.Start();
while (_continue)
{
var message = Console.ReadLine();
if (stringComparer.Equals("quit", message))
{
_continue = false;
}
else
{
_serialPort.WriteLine(message);
}
}
readThread.Join();
_serialPort.Close();
}
public static void Read()
{
while (_continue)
{
try
{
var message = _serialPort.ReadLine();
Console.WriteLine(message);
}
catch (TimeoutException) { }
}
}
Disclaimer: the link of this guideline refer to my personal web site.
I like David's answer above but if your looking to do integration tests and actually test your serial port communication I have used and application called ViN soft virtual serial cable in the past to basically create 2 serial ports on your machine that are connected by a virtual cable.
Also if you have a serial port on your development machine you could use it to connect to another machine that has a serial port and write an application that will basically simulate the communication of the PLC.
I would prefer to use a combination of both David's method and this method to ensure proper testing.
There is another resource out there that emulates serial ports for windows if anyone else is still looking for decent serial debugging tools.
The 32-bit version is free and seems pretty decent. It's called Virtual Serial Ports Emulator.
Very old but still might be useful to some. Instead of relying on COM interaction, just use the SerialPort.BaseStream to communicate with the port. This allows you to simply use a standard stream interface for communication, in other words, doesn't matter if you use serial ports, TCP connections, or even file streams. Perfect for simulation.