I'm working on a mobile application (C#/WPF on a tablet PC) that prints to a bluetooth connected printer. Right now I just fire off a print job, and if the printer is not present the printer subsystem reports an error to the user. I'm not doing anything programatically with Bluetooth, just using PrintDialog().
I'd like to modify this process to detect the printer first - if it is not available, then I'll just store the document without printing. Is there a way in code for me to detect if the bluetooth device is connected/active/available?
If I look at the device in the Bluetooth panel under Control Panel, it doesn't seem to have any sort of status that reflects whether or not the device is available, so maybe this isn't possible.
I'm assuming the printer has already been setup and configured in Windows - all I need to do is detect if it is actually present at a given point in time.
Perhaps use the 32feet.NET library (of which I am the maintainer) and check if the printer is present before submitting the job. You'd need to know the Bluetooth address of the printer; can one get that from the system, or maybe you always know it.
Discovery on the MSFT Bluetooth stack always returns all known devices in amongst those in range :-( but we can use other means to detect the device's presence/absence. Perhaps using BluetoothDeviceInfo.GetServiceRecords in its BeginGetServiceRecords form. e.g. something like (not tested/compiled):
bool IsPresent(BluetoothAddress addr) // address from config somehow
{
BluetoothDeviceInfo bdi = new BluetoothDeviceInfo(addr);
if (bdi.Connected) {
return true;
}
Guid arbitraryClass = BluetoothService.Headset;
AsyncResult<bool> ourAr = new AsyncResult<bool>(); // Jeffrey Richter's impl
IAsyncResult ar = bdi.BeginGetService(arbitraryClass, IsPresent_GsrCallback, ourAr);
bool signalled = ourAr.AsyncWaitHandle.WaitOne(Timeout);
if (!signalled) {
return false; // Taken too long, so not in range
} else {
return ourAr.Result;
}
}
void IsPresent_GsrCallback(IAsyncResult ar)
{
AsyncResult<bool> ourAr = (AsyncResult<bool>)ar.AsyncState;
const bool IsInRange = true;
const bool completedSyncFalse = true;
try {
bdi.EndGetServiceResult(ar);
ourAr.SetAsCompleted(IsInRange, completedSyncFalse);
} catch {
// If this returns quickly, then it is in range and
// if slowly then out of range but caller will have
// moved on by then... So set true in both cases...
// TODO check what error codes we get here. SocketException(10108) iirc
ourAr.SetAsCompleted(IsInrange, completedSyncFalse);
}
}
Related
I am in the process of writing a bluetooth scanner to monitor traffic outside my house. I understand that car BT devices generally have 3 states, visible (always on), limited, and invisible.
I'm using asynchronous device discovery with the Bluesoleil drivers, and a Class 1 Bluetooth dongle.
My problem is the device name and device class isn't being discovered. It is discovering the device address, and the range, but not the name of the device.
I have tried using System.Threading.Thread.Sleep(1000) then calling device.Refresh() to no avail.
It is picking up the name of a few things that are in range, like TVs, and Fitbit Flexes, but it's important that I can find the name and CoD of the car bluetooth devices, not just the MAC address and the range.
public void SetupListener()
{
var client = new BluetoothClient();
var component = new BluetoothComponent(client);
component.DiscoverDevicesAsync(255, false, false, true, false, null);
component.DiscoverDevicesProgress += component_DiscoverDevicesProgress;
component.DiscoverDevicesComplete += component_DiscoverDevicesComplete;
}
void component_DiscoverDevicesComplete(object sender, DiscoverDevicesEventArgs e)
{
SetupListener();
}
void component_DiscoverDevicesProgress(object sender, DiscoverDevicesEventArgs e)
{
BluetoothDeviceInfo[] availableDevices = e.Devices as BluetoothDeviceInfo[];
foreach (BluetoothDeviceInfo device in availableDevices)
{
device.Refresh();
var name = device.DeviceName; // This mostly returns the MAC address
var cod = device.ClassOfDevice.Device; // This mostly returns Miscellaneous
}
}
The application I've built is a simple WPF form that dumps the found devices to a datagrid (which I've removed from the code for simplicity's sake). This is generally what the results look like.
When I sit outside closer to the road, it is definitely picking up more and more devices as cars drive by, but I can't tell what type of device it's detecting.
Any help would be greatly appreciated.
Question: What is the best way to programmatically disconnect and reconnect displays programmatically?
The Goal: Kill the video output (black screen with no backlight) on a display and later turn it back on. Imagine unplugging the video cord from the monitor, then plugging it back in.
My Attempt:
// Get the monitor to disable
uint iDevNum = 0;
DISPLAY_DEVICE displayDevice = new DISPLAY_DEVICE();
displayDevice.cb = Marshal.SizeOf(displayDevice);
EnumDisplayDevices(null, iDevNum, ref displayDevice, 0))
DEVMODE devMode = new DEVMODE();
EnumDisplaySettings(displayDevice.DeviceName, 0, ref devMode);
//
// Do something here to disable this display device!
//
// Save the display settings
ChangeDisplaySettingsEx(displayDevice.DeviceName, ref devMode,
IntPtr.Zero, ChangeDisplaySettingsFlags.CDS_NONE, IntPtr.Zero);
I can interact with each display, but I can't figure out how to disconnect one.
It is similar to "Disconnect this display" in the Screen Resolution properties in Windows 7:
Notes:
Turning off video output on all displays won't work because I need the other monitors to stay on.
The desktop area on the "dead" display does NOT need to be usable when it is off. Also, it is fine if windows move around.
References:
SO: Enabling a Second Monitor
How to Turn Off a Monitor
1) Get MultiMonitorHelper from here:
https://github.com/ChrisEelmaa/MultiMonitorHelper/tree/master
2) Extend Win7Display to disconnect the display:
using MultiMonitorHelper.DisplayModels.Win7.Enum;
using MultiMonitorHelper.DisplayModels.Win7.Struct;
/// <summary>
/// Disconnect a display.
/// </summary>
public void DisconnectDisplay(int displayNumber)
{
// Get the necessary display information
int numPathArrayElements = -1;
int numModeInfoArrayElements = -1;
StatusCode error = CCDWrapper.GetDisplayConfigBufferSizes(
QueryDisplayFlags.OnlyActivePaths,
out numPathArrayElements,
out numModeInfoArrayElements);
DisplayConfigPathInfo[] pathInfoArray = new DisplayConfigPathInfo[numPathArrayElements];
DisplayConfigModeInfo[] modeInfoArray = new DisplayConfigModeInfo[numModeInfoArrayElements];
error = CCDWrapper.QueryDisplayConfig(
QueryDisplayFlags.OnlyActivePaths,
ref numPathArrayElements,
pathInfoArray,
ref numModeInfoArrayElements,
modeInfoArray,
IntPtr.Zero);
if (error != StatusCode.Success)
{
// QueryDisplayConfig failed
}
// Check the index
if (pathInfoArray[displayNumber].sourceInfo.modeInfoIdx < modeInfoArray.Length)
{
// Disable and reset the display configuration
pathInfoArray[displayNumber].flags = DisplayConfigFlags.Zero;
error = CCDWrapper.SetDisplayConfig(
pathInfoArray.Length,
pathInfoArray,
modeInfoArray.Length,
modeInfoArray,
(SdcFlags.Apply | SdcFlags.AllowChanges | SdcFlags.UseSuppliedDisplayConfig));
if (error != StatusCode.Success)
{
// SetDisplayConfig failed
}
}
}
3) Extend Win7Display to reconnect the display using an answer from this post:
using System.Diagnostics;
/// <summary>
/// Reconnect all displays.
/// </summary>
public void ReconnectDisplays()
{
DisplayChanger.Start();
}
private static Process DisplayChanger = new Process
{
StartInfo =
{
CreateNoWindow = true,
WindowStyle = ProcessWindowStyle.Hidden,
FileName = "DisplaySwitch.exe",
Arguments = "/extend"
}
};
4) Update the methods in IDisplay.
5) Implement the methods:
IDisplayModel displayModel = DisplayFactory.GetDisplayModel();
List<IDisplay> displayList = displayModel.GetActiveDisplays().ToList();
displayList[0].DisconnectDisplay(0);
displayList[0].ReconnectDisplays();
There's a github project that I STILL haven't got around, but it's a starting point. You need to use Win7 specific API in order to change settings. ChangeDisplaySettings won't work.
Have a look: https://github.com/ChrisEelmaa/MultiMonitorHelper
This is what you need to do:
update the IDisplay interface to support TurnOff() method,
and then call it:
var displayModel = DisplayFactory.GetDisplayModel();
var displayList = displayModel.GetActiveDisplays().ToList();
var firstDisplay = displayList[0].TurnOff();
How to implement TurnOff()? I WOULD imagine this is how(I might be wrong here now):
You need to break the connection between GPU & monitor through breaking the "paths". You can break path between source and target like this:
Call SetDisplayConfig() and pass inside specific paths and make sure you map out the DISPLAYCONFIG_PATH_ACTIVE from the DISPLAY_PATH_INFO structure flags integer.
http://msdn.microsoft.com/en-us/library/windows/hardware/ff553945(v=vs.85).aspx
Sorry for not being more helpful, but this is pretty hardcore stuff, it took me quite a while to even understand the basics of that API. It's a starting point :-),
take a look at the example how to rotate specific monitor in Win7: How do I set the monitor orientation in Windows 7?
In all honesty, just wrap the DisplaySwitch.exe for Win7, and pass /internal or /external(depending if you want to disable/enable first/second monitor), this might or might not work for >2 monitors.
How do you get the raw descriptor data from a HID device in Windows?
Background:
I need to get the Manufacturer, Product Name, and Serial Number from a HID device in Windows. I'm using hid.dll to access the devices using the functions seen here. My question is very similar to this one. I am able to get the manufacturer string and product string from SOME HID devices, but most fail to return this data with HidD_GetManufacturerString returning false. However, I KNOW these devices do have the string information in their descriptors because I am able to see it using USBTreeView.
The interesting thing is, even for the devices that do return manufacturer and product names, the values I'm getting through hid.dll are very different from the values I see using the above tool which gets the raw data from the USB device.
For example, an Xbox 360 controller:
Via USB Tree View:
Device Description : Xbox 360 Controller for Windows
Language 0x0409 : "©Microsoft Corporation"
iProduct : 0x02
Language 0x0409 : "Controller"
iSerialNumber : 0x03
Language 0x0409 : "0843806"
Via hid.dll using HidD_GetManufacturerString, HidD_GetProductString, and HidD_GetSerialNumberString:
Description : HID-compliant game controller
Product : Controller (XBOX 360 Controller for Windows)
Manufacturer : FAILS
Serial Number : FAILS
WinUSB is unable to open these devices at all to retrieve this data as they do not use the winusb.sys driver.
1) I don't understand why the values returned by the HidD functions do not match the values in the USB descriptor.
2) I can't find any way to access the raw USB descriptor data for a HID device because I cannot access them with WinUSB.
Edit 1:
Okay, so I've learned a bit more about HID. It seems the data I'm getting through hid.dll is driver-specified data, not data coming from the USB device. HID can apply to devices on transports other than USB as well. So that's fine. Ultimately, what I really want to know is how can I get the USB device when I have the HID device and what API do I use for that. Besides WinUSB which doesn't work, the only thing I can find are kernel-level functions IOCTL. I don't know if that's suitable for a normal, non admin application.
I finally found the solution. The main problem was just associating a HID device to its parent USB device. This is the basic process:
Assuming you already have the HID device and the SP_DEVINFO_DATA for it:
Enumerate all USB devices as seen here.
Find all children of the USB devices with CM_GetChild and CM_GetSibling.
Compare the known HID device's instance handle (SP_DEVINFO_DATA->DevInst) with each child device's instance handle that was returned by the CM functions to determine which USB device is the parent.
From there, you can get any USB info you want including the descriptor.
I did that here. There was a bunch of things you need to keep in mind - like composite USB device support. Main algo was something like this:
std::string usbHubInterface;
std::string compositeDeviceInstanceId;
for (std::string deviceInstanceId = GetParentDevice(hidDeviceInstanceId); !deviceInstanceId.empty(); deviceInstanceId = GetParentDevice(deviceInstanceId))
{
std::string usbDeviceInterface = GetDeviceInterface(deviceInstanceId, &GUID_DEVINTERFACE_USB_DEVICE);
if (!usbDeviceInterface.empty())
{
m_DeviceInterfacePath = usbDeviceInterface;
}
std::string usbHub = GetDeviceInterface(deviceInstanceId, &GUID_DEVINTERFACE_USB_HUB);
if (!usbHub.empty())
{
usbHubInterface = usbHub;
break;
}
// May be composite USB device. Save it for later use.
if (usbDeviceInterface.empty())
{
compositeDeviceInstanceId = deviceInstanceId;
}
}
if (usbHubInterface.empty())
{
DBGPRINT("UsbDevice: cannot get parent USB hub interface");
return;
}
if (!m_DeviceInterfacePath.empty())
{
m_DeviceInstanceId = GetDeviceFromInterface(m_DeviceInterfacePath);
DEVINST devNodeHandle = OpenDevNode(m_DeviceInstanceId);
// Get device index in parent USB hub
// https://learn.microsoft.com/windows-hardware/drivers/ddi/wdm/ns-wdm-_device_capabilities#usb
m_UsbPortIndex = PropertyDataCast<ULONG>(GetDevNodeProperty(devNodeHandle, &DEVPKEY_Device_Address, DEVPROP_TYPE_UINT32));
// Composite USB device
if (IsCompositeUSBDevice(m_DeviceInstanceId))
{
// Need to acquire interface number in parent USB device
// https://learn.microsoft.com/windows-hardware/drivers/usbcon/usb-common-class-generic-parent-driver
if (!GetInterfaceNumber(compositeDeviceInstanceId, m_UsbInterfaceNumber))
{
DBGPRINT("UsbDevice: cannot get interface number from composite USB device");
return;
}
}
}
else
{
DBGPRINT("UsbDevice: cannot get parent USB device interface");
return;
}
// Open device handle first to wake up device from S3 suspend state
ScopedHandle usbInterfaceHandle = OpenDeviceInterface(m_DeviceInterfacePath);
ScopedHandle hubInterfaceHandle = OpenDeviceInterface(usbHubInterface, true);
USB_DEVICE_DESCRIPTOR deviceDescriptor;
if (!GetDeviceDescriptor(hubInterfaceHandle, m_UsbPortIndex, deviceDescriptor))
return;
m_VendorId = deviceDescriptor.idVendor;
m_ProductId = deviceDescriptor.idProduct;
m_VersionNumber = deviceDescriptor.bcdDevice;
// Assume that we are always using first configuration
const UCHAR configurationIndex = 0;
if (!GetFullConfigurationDescriptor(hubInterfaceHandle, m_UsbPortIndex, configurationIndex, m_ConfigurationDescriptor))
return;
// Search for interface descriptor
PUSB_INTERFACE_DESCRIPTOR interfaceDescriptor = nullptr;
if (!SearchInterfaceDescriptor(m_ConfigurationDescriptor, m_UsbInterfaceNumber, interfaceDescriptor))
return;
std::wstring stringBuffer;
// Get the array of supported Language IDs, which is returned in String Descriptor 0
if (!GetDeviceString(hubInterfaceHandle, m_UsbPortIndex, 0, 0, stringBuffer))
return;
// Use first supported language
USHORT languageID = stringBuffer[0];
if (GetDeviceString(hubInterfaceHandle, m_UsbPortIndex, deviceDescriptor.iManufacturer, languageID, stringBuffer))
m_Manufacturer = utf8::narrow(stringBuffer);
// Get interface name instead of whole product name, if present
UCHAR productStringIndex = interfaceDescriptor->iInterface ? interfaceDescriptor->iInterface : deviceDescriptor.iProduct;
if (GetDeviceString(hubInterfaceHandle, m_UsbPortIndex, productStringIndex, languageID, stringBuffer))
m_Product = utf8::narrow(stringBuffer);
if (GetDeviceString(hubInterfaceHandle, m_UsbPortIndex, deviceDescriptor.iSerialNumber, languageID, stringBuffer))
m_SerialNumber = utf8::narrow(stringBuffer);
// Get HID Descriptor
PHID_DESCRIPTOR hidDescriptor = nullptr;
if (!GetHidDescriptor(interfaceDescriptor, hidDescriptor))
return;
// Get raw HID Report Descriptor
if (!GetHidReportDescriptor(hubInterfaceHandle, m_UsbPortIndex, hidDescriptor->DescriptorList[0].wReportLength, interfaceDescriptor->bInterfaceNumber, m_HidReportDescriptor))
{
DBGPRINT("UsbDevice: cannot get raw HID Report Descriptor");
}
I'm attempting to write a C# library which looks at all available USB serial ports on a Raspberry Pi so that I can enumerate, identify and communicate with a set of Arduinos connected to the Pi via a USB hub.
I am able to make this work on my windows machine (several Arduinos connected to my desktop computer) and have even been able to make it work on my Pi however, I am struggling to understand how to generalize the fix.
If I attempt to run the program by itself on the Pi, I am able to open the serial port and send data however, I cannot receive anything from the Arduinos: I get timeout exceptions. I understand that Mono's implementation of SerialPort is limited and I must use SerialPort.ReadByte() instead of Readline() and the data received events (my solution is based on code from HowToSystemIOPorts). My Serial port enumeration is using a method outlined in another stack exchange response here.
My timeout is currently set to 4 seconds, which is several orders of magnitude longer than I expect to receive the message.
After a lot of googling, I came across mention of using minicom to initialize the serial port here, which to my surprise allowed me to receive data from the Arduino. The biggest drawback is that I need to initialize the port using minicom and leave the process opening each time I boot the Pi. I also can't seem to figure out how to make this work with multiple Arduinos.
Here is what I have tried so far:
Updated the Pi firmware and software to their latest versions
Attempted to use both an Arduino MEGA 2560 R3 and Arduino UNO
Changed the owner of the tty* ports (ttyACM0 and ttyUSB0 in this case) to both my user and group
Successfully configured the port via minicom, left the process running and start the program and read/wrote data. A manual process which only seems to work for one Arduino at a time
Successfully run the program in Windows without fault
Verified the Arduinos are recognized by the Pi running "dmesg | grep tty"
Here is what I hope to solve:
Automatic setup/initialization of the Arduino serial ports. Whether through a shell script run before the main program or within Mono code so that the code below can run as intended.
Here is my connection code:
public bool StartArduinoComms()
{
string[] ports = GetPortNames();
foreach (string port in ports)
{
mLogger.LogMessage(ProsthesisCore.Utility.Logger.LoggerChannels.Arduino, string.Format("Found serial port {0}", port));
}
bool foundCorrectArduino = false;
var idPacket = new ArduinoMessageBase();
idPacket.ID = ArduinoMessageValues.kIdentifyValue;
string jsonOutput = Newtonsoft.Json.JsonConvert.SerializeObject(idPacket);
foreach (string port in ports)
{
SerialPort serialPort = new SerialPort(port, kArduinoCommsBaudRate);
serialPort.Parity = Parity.None;
serialPort.DataBits = 8;
serialPort.StopBits = StopBits.One;
//Only check unopened ports
if (!serialPort.IsOpen)
{
serialPort.Open();
//Disable telemtry just incase
var toggle = new { ID = ArduinoMessageValues.kTelemetryEnableValue, EN = false };
string disableTelem = Newtonsoft.Json.JsonConvert.SerializeObject(toggle);
serialPort.Write(disableTelem);
//Discard any built up data
serialPort.DiscardInBuffer();
serialPort.Write(jsonOutput);
serialPort.ReadTimeout = kIDTimeoutMilliseconds;
string response = string.Empty;
for (int i = 0; i < kNumRetries; ++i)
{
try
{
//This is guaranteed to timeout if not configured through minicom
response = ReadLine(serialPort);
break;
}
//Catch case where the serial port is unavailable. MOve to next port
catch (TimeoutException)
{
continue;
}
}
if (!string.IsNullOrEmpty(response))
{
//Perform response validation
}
else
{
//Got no response
}
if (!foundCorrectArduino)
{
serialPort.Close();
}
}
}
return foundCorrectArduino;
}
/// <summary>
/// From https://stackoverflow.com/questions/434494/serial-port-rs232-in-mono-for-multiple-platforms
/// </summary>
/// <returns></returns>
private static string[] GetPortNames()
{
int p = (int)Environment.OSVersion.Platform;
List<string> serial_ports = new List<string>();
// Are we on Unix?
if (p == 4 || p == 128 || p == 6)
{
string[] ttys = System.IO.Directory.GetFiles("/dev/", "tty*");
foreach (string dev in ttys)
{
//Arduino MEGAs show up as ttyACM due to their different USB<->RS232 chips
if (dev.StartsWith("/dev/ttyS") || dev.StartsWith("/dev/ttyUSB") || dev.StartsWith("/dev/ttyACM"))
{
serial_ports.Add(dev);
}
}
}
else
{
serial_ports.AddRange(SerialPort.GetPortNames());
}
return serial_ports.ToArray();
}
Have a look at stty command. It will let you set/read teminal settings
http://linux.about.com/od/lna_guide/a/gdelna38t01.htm will give a rundown on it's use.
It would be easier to call out to than minicom, and the settings stay on the device.
I have done something like the same as you before.
I had to read and write data through USB Serial adapter, and didnt use minicom.
It may not be god code but i found that inorder to read the data I could create a new thread and have that check for data, my code include a lot of stuff but basicly i did this:
System.Threading.Thread newThread;
newThread = new System.Threading.Thread(this.check_get_data);
and the check_get_data method
public void check_get_data ()
{
byte tmpByte = 0;
while (m_objSerialPort.BytesToRead != 0) {
tmpByte = (byte)m_objSerialPort.ReadByte ();
DoSomethingWithByte(tmpByte);
Thread.Sleep(20);
}
}
this is currently running with two usbserials. dont know if it helps but hope you find your solution
We have several devices where I work (mostly Datalogic 4420 Falcon), and someone is always leaving one off the base. The battery runs dry, then they bring them back to get setup all over. (There's supposed to be a way to configure a file on the SD card to reload upon such an error, but it doesn't work very well)
When someone saves changes on the device (using my app that writes data to the SQL Server), the Serial Number is sent along with it so we can track what devices are in use where.
Each device has a Serial Number, and I have to physically (i.e. manually) write that into the Device name field, which I can read. Working code here if anyone wants to know how:
static string deviceId = null;
public static string DeviceName {
get {
if (String.IsNullOrEmpty(deviceId)) {
using (RegistryKey key = Registry.LocalMachine.OpenSubKey("Ident", true)) {
try {
deviceId = key.GetValue("Name", "[Unnamed]").ToString();
} catch (Exception e) {
ErrorWrapper("GetDeviceName", e);
deviceId = Dns.GetHostName();
} finally {
key.Flush();
key.Close();
}
}
}
return deviceId;
}
}
I do not like the manual (i.e. Fat Finger prone) Serial Number entry. Is there some call to query the device's Serial Number, or is that vendor specific?
Datamax does make an SDK that is specific to their devices, but we don't want our applications tied down to any one manufacturer (we are already tied down to VS2008).
I'd start by trying to P/Invoke to get the device ID (KerneIoControl with IOCTL_HAL_GET_DEVICEID) and see if it matches the serial number you're after. Here's an example.
I don't know about your Datalogic 4420 Falcon device, but I work with Intermec CK30 & CK60 and I have their itc50.dll file.
Here is snippet:
[DllImport("itc50.dll")]public static extern int ITCGetSerialNumber(StringBuilder Snumber, int buffSize);
StringBuilder hwSN = new StringBuilder(12);
if (ITCGetSerialNumber(hwSN, hwSN.Capacity) >= 0)
{
;
;
}