Does anyone have any idea how to track the signal strength of a bluetooth connection perferably in C#?
I was thinking using a WMI query but couldn't track down the WMI class encapsulating the connection.
The idea is when I leave my machine with my cellphone in pocket the bluetooth signal weakens and my machine locks and I don't get goated.
The Link Manager Protocol (LMP) running in a Bluetooth device looks after the link setup and configuration. This is all done by two devices exchanging Protocol Data Units (PDUs).The hardware and software functionality of the RSSI is provided at the LMP level that permits you to manage the RSSI Data. It allows you to read the RSSI level and control the TX RF output power (the LMP power commands) LMP for control and to get at status information.
So what you are actually looking for is defined in the LMP when using the MS Bluetooth stack.
The MS Bluetooth Stack HCI interface already supports functions below i.e
HCI_READHCIPARAMETERS
HCI_STARTHARDWARE
HCI_STOPHARDWARE
HCI_SETCALLBACK
HCI_OPENCONNECTION
HCI_READPACKET
HCI_WRITEPACKET
HCI_CLOSECONNECTION
I suppose microsoft could have implemented a function called HCI_Read_RSSI but they didn't.
To obtain the the RSSI data you will have to use the LMP to get the info you need.
Example psuedocode to read RSSI Data
// Read HCI Parameters
#include <windows.h>
#include <windev.h>
#include <bt_buffer.h>
#include <bt_hcip.h>
#include <bt_os.h>
#include <bt_debug.h>
#include <svsutil.hxx>
#include <bt_tdbg.h>
unsigned short hci_subversion, lmp_subversion, manufacturer;
unsigned char hci_version, lmp_version, lmp_features[8];
if (BthReadLocalVersion (&hci_version, &hci_subversion, &lmp_version, &lmp_subversion, &manufacturer, lmp_features) != ERROR_SUCCESS) {
SetUnloadedState ();
return 0;
}
WCHAR szLine[MAX_PATH]
unsigned char *pf = lmp_features;
if ((*pf) & 0x02) {
wsprintf (szLine, L" RSSI");
}
This will ONLY work with the Microsoft bluetooth stack. This is C++ code also. I got this from the experts exchange post(I know) at the bottom of the page.
http://www.experts-exchange.com/Programming/Wireless_Programming/Bluetooth/Q_21267430.html
There is no specific function that does it for you.
Also there is this library that may help you, I haven't looked through the documentation completely but I've heard good things about it.
http://inthehand.com/content/32feet.aspx
Goodluck man!
Related
I've been working on USB HID Device in embedded system and C# for a while. I decided to use USBHid library in C#. I got the ideal result with this library. But I have a problem. While defining the USB in the USBHid library, the following code is sufficient in the project of the library that I found on the internet.
public UsbHidDevice Device;
Device = new UsbHidDevice(vvvv,pppp);
However, when I use the same library, it asks me for an expression in the following format.
public UsbHidDevice Device; string vidandpid =
"\\hid#vid_0000&pid_0000&mi_00#a&0&000000000&1&0000#{eeof37d0-1963-47k4-aa41-74476db7uf49}";
Device = new UsbHidDevice(vidandpid);
I adapted this format for my own HID device, but without success. How should this string expression be? I am open to your views. Thank you from now.
How to find USB HID DevicePath?
I gave up on USBHID library and found solution with HidLibrary library. As far as I understand, HidLibrary falls short on some issues.
Here I am sharing the C# code that I linked with HidLibrary. Thank you for all the replies.
device = HidDevices.Enumerate(VendorID, ProductID).FirstOrDefault();
if (device != null)
{
device.OpenDevice();
device.Inserted += DeviceAttachedHandler;
device.Removed += DeviceRemovedHandler;
device.MonitorDeviceEvents = true;
device.ReadReport(myfunction);
}
else { RtBox_Feedback.AppendText("NOT DEVICE!"); }
\\hid#vid_0000&pid_0000&mi_00#a&0&000000000&1&0000#{eeof37d0-1963-47k4-aa41-74476db7uf49} - is a device interface for the {eeof37d0-1963-47k4-aa41-74476db7uf49} interface. It is almost always unique and semi-random for each device. It also may change if yore put device in another USB slot. This string may be used as path to open this "file" with CreateFile Win32 API and talk with device by means of interface-specific IOCTLs. More info here.
For HID devices Windows have another device interface GUID - GUID_DEVINTERFACE_HID - {4D1E55B2-F16F-11CF-88CB-001111000030}
You can use CM_Get_Device_Interface_ListW or SetupDiGetClassDevs/SetupDiEnumDeviceInterfaces/SetupDiGetDeviceInterfaceDetail APIs to enumerate device interfaces by interface GUID or by device Instance ID.
Mentioned "USBHid library in C#" may already have code that doing such enumeration and filtering by HID deivce VID/PID but I cannot say it for sure since you haven't added link to the code of this library. :)
My goal is to make a windows form application that could turn on or off a relay module that is connected to a NodeMCU board with ESP8266 wifi module, that is connected to the same network as my computer.
I managed to control it through USB using SerialPort library, but I have no idea if its even possible to control it through wifi. This is how my arduino code looks like:
#include <ESP8266WiFi.h>
int Module1 = 0;
int Module2 = 2;
const char* ssid = "Android";
const char* password = "password";
void setup()
{
Serial.begin(9600);
pinMode(Module1,OUTPUT);
pinMode(Module2,OUTPUT);
}
void loop()
{
char data = Serial.read();
switch (data){
case '1' : digitalWrite(Module1,HIGH); ;break;
case '2' : digitalWrite(Module2,HIGH); ;break;
case '3' : digitalWrite(Module1,LOW); ;break;
case '4' : digitalWrite(Module2,LOW); ;break;
}
}
Any way I could do the same but the controls would work through wifi? As in if I would be able to control the relay with a windows form app without connecting the board to the PC? (another power source for example)
Simple answer: Yes.
More long-winded version: You need to either connect your ESP board to a web service like IFTTT (see https://ifttt.com/ ) or write a web service on it that can react to messages from the client (see https://randomnerdtutorials.com/esp8266-web-server-with-arduino-ide/ ).
In a text to speech application by C# I use SpeechSynthesizer class, it has an event named SpeakProgress which is fired for every spoken word. But for some voices the parameter e.AudioPosition is not synchronized with the output audio stream, and the output wave file is played faster than what this position shows (see this related question).
Anyway, I am trying to find the exact information about the bit rate and other information related to the selected voice. As I have experienced if I can initialize the wave file with this information, the synchronizing problem will be resolved. However, if I can't find such information in the SupportedAudioFormat, I know no other way to find them. For example the "Microsoft David Desktop" voice provides no supported format in the VoiceInfo, but it seems it supports a PCM 16000 hz, 16 bit format.
How can I find audio format of the selected voice of the SpeechSynthesizer
var formats = CurVoice.VoiceInfo.SupportedAudioFormats;
if (formats.Count > 0)
{
var format = formats[0];
reader.SetOutputToWaveFile(CurAudioFile, format);
}
else
{
var format = // How can I find it, if the audio hasn't provided it?
reader.SetOutputToWaveFile(CurAudioFile, format );
}
Update: This answer has been edited following investigation. Initially I was suggesting from memory that SupportedAudioFormats is likely just from (possibly misconfigured) registry data; investigation has shown that for me, on Windows 7, this is definitely the case, and is backed up acecdotally on Windows 8.
Issues with SupportedAudioFormats
System.Speech wraps the venerable COM speech API (SAPI) and some voices are 32 vs 64 bit, or can be misconfigured (on a 64 bit machine's registry, HKLM/Software/Microsoft/Speech/Voices vs HKLM/Software/Wow6432Node/Microsoft/Speech/Voices.
I've pointed ILSpy at System.Speech and its VoiceInfo class, and I'm pretty convinced that SupportedAudioFormats comes solely from registry data, hence it's possible to get zero results back when enumerating SupportedAudioFormats if either your TTS engine isn't properly registered for your application's Platform target (x86, Any or 64 bit), or if the vendor simply doesn't provide this information in the registry.
Voices may still support different, additional or fewer formats, as that's up to the speech engine (code) rather than the registry (data). So it can be a shot in the dark. Standard Windows voices are often times more consistent in this regard than third party voices, but they still don't necessarily usefully provide SupportedAudioFormats.
Finding this Information the Hard Way
I've found it's still possible to get the current format of the current voice - but this does rely on reflection to access the internals of the System.Speech SAPI wrappers.
Consequently this is quite fragile code! And I wouldn't recommend use in production.
Note: The below code does require you to have called Speak() once for setup; more calls would be needed to force setup without Speak(). However, I can call Speak("") to say nothing and that works just fine.
Implementation:
[StructLayout(LayoutKind.Sequential)]
struct WAVEFORMATEX
{
public ushort wFormatTag;
public ushort nChannels;
public uint nSamplesPerSec;
public uint nAvgBytesPerSec;
public ushort nBlockAlign;
public ushort wBitsPerSample;
public ushort cbSize;
}
WAVEFORMATEX GetCurrentWaveFormat(SpeechSynthesizer synthesizer)
{
var voiceSynthesis = synthesizer.GetType()
.GetProperty("VoiceSynthesizer", BindingFlags.Instance | BindingFlags.NonPublic)
.GetValue(synthesizer, null);
var ttsVoice = voiceSynthesis.GetType()
.GetMethod("CurrentVoice", BindingFlags.Instance | BindingFlags.NonPublic)
.Invoke(voiceSynthesis, new object[] { false });
var waveFormat = (byte[])ttsVoice.GetType()
.GetField("_waveFormat", BindingFlags.Instance | BindingFlags.NonPublic)
.GetValue(ttsVoice);
var pin = GCHandle.Alloc(waveFormat, GCHandleType.Pinned);
var format = (WAVEFORMATEX)Marshal.PtrToStructure(pin.AddrOfPinnedObject(), typeof(WAVEFORMATEX));
pin.Free();
return format;
}
Usage:
SpeechSynthesizer s = new SpeechSynthesizer();
s.Speak("Hello");
var format = GetCurrentWaveFormat(s);
Debug.WriteLine($"{s.Voice.SupportedAudioFormats.Count} formats are claimed as supported.");
Debug.WriteLine($"Actual format: {format.nChannels} channel {format.nSamplesPerSec} Hz {format.wBitsPerSample} audio");
To test it, I renamed Microsoft Anna's AudioFormats registry key under HKLM/Software/Wow6432Node/Microsoft/Speech/Voices/Tokens/MS-Anna-1033-20-Dsk/Attributes, causing SpeechSynthesizer.Voice.SupportedAudioFormats to have no elements when queried. The below is the output in this situation:
0 formats are claimed as supported.
Actual format: 1 channel 16000 Hz 16 audio
You can't get this information from code. You can only listen to all formats (from poor format like 8 kHz to high quality format like 48 kHz) and observe where it stops getting better, which is what you did, I think.
Internally, the speech engine "asks" the voice for the original audio format only once, and I believe that this value is used only internally by the speech engine, and the speech engine does not expose this value in any way.
For further information:
Let's say you are a voice company.
You have recorded your computer voice at 16 kHz, 16 bit, mono.
The user can let your voice speak at 48 kHz, 32 bit, Stereo.
The speech engine does this conversion. The speech engine does not care if it really sounds better, it simply does the format conversion.
Let's say the user wants to let your voice speak something.
He requests that the file will be saved as 48 kHz, 16 bit, stereo.
SAPI / System.Speech calls your voice with this method:
STDMETHODIMP SpeechEngine::GetOutputFormat(const GUID * pTargetFormatId, const WAVEFORMATEX * pTargetWaveFormatEx,
GUID * pDesiredFormatId, WAVEFORMATEX ** ppCoMemDesiredWaveFormatEx)
{
HRESULT hr = S_OK;
//Here we need to return which format our audio data will be that we pass to the speech engine.
//Our format (16 kHz, 16 bit, mono) will be converted to the format that the user requested. This will be done by the SAPI engine.
enum SPSTREAMFORMAT sample_rate_at_which_this_voice_was_recorded = SPSF_16kHz16BitMono; //Here you tell the speech engine which format the data has that you will pass back. This way the engine knows if it should upsample you voice data or downsample to match the format that the user requested.
hr = SpConvertStreamFormatEnum(sample_rate_at_which_this_voice_was_recorded, pDesiredFormatId, ppCoMemDesiredWaveFormatEx);
return hr;
}
This is the only place where you have to "reveal" what the recorded format of your voice is.
All the "Available formats" rather tell you which conversions your sound card / Windows can do.
I hope I explained it well?
As a voice vendor, you don't support any formats. You just tell they speech engine what format your audio data is so that it can do the further conversions.
I have coding I almost always use with my Omnikey RFID CardMan 5321 smart cards. Problem is we received new cards today which are marked "HID iCLASS GL" which do not appear to be working well with our coding.
Without going through the whole source, our problem is arising when we are calling the following line, which basically tells us the length of the data:
lResult = SCardTransmit(hCard, 0, bytCommand, lLen, 0, byReadBuffer, iReturnlength)
We are returning only a length of 2, which the data is marked as "x69 x86". Even if I tell it to read all 255 chr's the rest are just marked as null.
Now I know our reader can read these cards since the OMNIKEY Diagnostic tool is showing us the following:
Status: Smart Card Inserted
FW: 5.10
Port: USB
Lib: 1.0
Smart Card Nme: iCLASS 32KS 8x2+16
ART: Valid
Protocol: ISO 15693 (Part 2)
PICCtoPCD: 26,48 kbps
PCDtoPICC: 26,48 kbps
Frequ: 13.56 MHz
As I explained before, everything is working fine in my coding except no data is being returned for my card besides "x69 x86", which is surely not correct.
If anyone has any experience reading from a HID iCLASS card, I would greatly appreciate some feedback on how to. Even if we have to license software, that is ok.
Thanks in advance!
in case you are trying to access physical access data, I would thoroughly check the crypto protocol between reader and host first and also meke sure you are using a reader with teh latest firmware (5.20 for the OMNIKEY 5321).
I would also introduce code to check the card system withour secure communication channel between host and reader application.
Further references:
http://www.hidglobal.com/documents/ok_contactless_developer_guide_an_en.pdf
The reason cause you get a 2 Byte array is cause your command runs on an error so the chip returns only SW1 and SW2 Flag
in your case it's meaning is
x69 --> Command not allowed (further qualification in SW2, see table 17)
x86 --> Command not allowed (no current EF)
So you might proof that your application file on the chip is correctly selected
further information #
http://www.cardwerk.com/smartcards/smartcard_standard_ISO7816-4_5_basic_organizations.aspx#table17
To sum it up, I need to do this:
12345(hWnd) -> "C:\setup.exe"
Right now, I am using GetProcessImageFileName to retrieve the Kernel Device Path of a process handle. I'm retrieving the handle using OpenProcess, passing it the PID. The PID (which I also need) is being retrieved using GetWindowThreadProcessId.
However, this gets me to a string like:
\Device\Harddisk1\setup.exe
At this point, I enumerate all drives on the system using DriveInfo.GetDrives(), and then call QueryDosDevice. Finally, I can do some string-manipulation magic, and "boom," I have my path.
Ok, so my issues:
This process breaks down on Network drives.
All I really want is QueryFullProcessImageName on XP
There HAS to be a better way to do this. Please enlighten me, oh gods of WIN32API!
The obvious question would be why you don't just use QueryFullProcessImageName, if that's what you want? Do you need compatibility with older versions of Windows?
The closest equivalent to QueryFullProcessImageName that's available on XP is probably GetModuleFileNameEx. I'd probably detect whether QueryFullProcessImageName is available and use it if possible, otherwise fall back to GetModuleFileNameEx.
Edit: While GetModuleFileNameEx isn't 100% dependable at retrieving the name of the executable for every possible process, it does work at least a fairly substantial part of the time. Here's a quick bit of test code I put together:
#include <windows.h>
#include <psapi.h>
#include <iterator>
#include <iostream>
#include <string>
#include <map>
std::string getfilename(DWORD pid) {
HANDLE process = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, false, pid);
static int winver;
char path[256]= {0};
DWORD size = sizeof(path);
if (winver==0)
winver = GetVersion() & 0xf;
#if WINVER >= 0x600
if (winver >= 6)
QueryFullProcessImageName(process, 0, path, &size);
else
#endif
if (!GetModuleFileNameEx(process, NULL, path, sizeof(path)))
strcpy(path, "Unknown");
return std::string(path);
}
typedef std::map<DWORD, std::string> win_map;
namespace std {
ostream &operator<<(ostream &os, win_map::value_type const &v) {
return os << v.first << ": " << v.second;
}
}
BOOL CALLBACK show_info(HWND window, LPARAM lParam) {
win_map &exes = *(win_map *)lParam;
DWORD pid;
GetWindowThreadProcessId(window, &pid);
exes[pid] = getfilename(pid);
return true;
}
int main() {
win_map exes;
EnumWindows(show_info, (LPARAM)&exes);
std::copy(exes.begin(), exes.end(), std::ostream_iterator<win_map::value_type>(std::cout, "\n"));
return 0;
}
The results of a quick test are somewhat interesting. Compiled as 32-bit code, the version using QueryFullProcessImageName found 33 processes with top-level windows, and found names for 31 of those executables. The version using GetModuleFileNameEx, also found 33 processes, but only found names for 21 of the executable. If, however, I compile it as 64-bit code, either version finds filenames for 31 out of 33 executables (and the same two fail). Given the frequency with which you see XP/x64, that's probably of little consequence, but I found it interesting nonetheless.
In any case, even the least capable version (32-bit/GMFNE) found names for ~2/3rds of the files. While that's certainly not what you'd hope for, it's certainly better than nothing.
It must be possible to retrieve the file path to running processes since Sysinternals' Process Explorer does it. Of course, Process Explorer uses NtQueryInformationProcess, which isn't a supported function. (On the other hand, since you're specifically targeting XP and would use QueryFullProcessImageName on Vista and later, the fear of the API becoming unavailable in a future version of Windows is probably not a concern.)
CodeProject has an article about how to use NtQueryInformationProcess.