Skip to content

Instantly share code, notes, and snippets.

@pavel-a
Last active February 15, 2019 21:32
Show Gist options
  • Save pavel-a/4674b612df0b3460c548d441194b723b to your computer and use it in GitHub Desktop.
Save pavel-a/4674b612df0b3460c548d441194b723b to your computer and use it in GitHub Desktop.
// Detect the proper COM ports for paired BT devices by name prefix
// pavel_a@fastmail.fm 12-feb-2019
#include "pch.h"
#define _CRT_SECURE_NO_WARNINGS 1
#include <vector>
#include <tuple>
#include <string>
#include <process.h>
#include <iostream>
using string = std::string;
const char * const sGUID_BTHdev = "{e0cbf06c-cd8b-4647-bb8a-263b43f0f974}";
const char * const sGUID_Port = "{4d36e978-e325-11ce-bfc1-08002be10318}";
#define dbgprintf printf
/// Find all matching BTH devices and their COM ports
/// Returns list of tuples (COMname, BTdevname)
std::vector<std::tuple<string, string > >
find_bth_ports(string BT_name_prefix)
{
// Match BTH device with its COM port by "Contained ID".
// The matching port is always the "outgoing" one. The other one is "incoming".
// Parse output of
// reg query HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Enum\BTHENUM ...
// No unicode. Assume a normal ENU system, no weird characters in the spew.
const char *cmd =
R"(reg.exe query HKLM\SYSTEM\CurrentControlSet\Enum\BTHENUM /s /t REG_SZ)";
std::vector<std::tuple<string, string > > resv; // result
std::vector<std::tuple<string, string > > BTH_devs;
std::vector<std::tuple<string, string > > Ports;
string curr_reg_base_subkey; // current subkey of either D or P type
char state = 'N'; // N=Neutral, D=device, P=port, X=unknown
string name, container_id, COM_port_name;
char buf[300];
auto get_val = [&buf]() {
// Get the reg. value in the spew line as string:
//search for REG_xxx<space*>, return all after it, can contain spaces
char *p = strstr(buf, "REG_");
if (!p) return string("");
p = strchr(p, ' ');
if (!p) return string("");
while (*p == ' ') p++;
return string(p);
};
FILE *p = _popen(cmd, "rt");
if (!p) {
dbgprintf("Error reading registry!");
return resv;
}
while (fgets(buf, sizeof(buf)-1, p)) {
buf[sizeof(buf) - 1] = 0;
size_t sl = strlen(buf);
//remove trailing \n
if (buf[sl - 1] == '\n') {
buf[--sl] = 0;
}
else {
dbgprintf("Line too long\n");
}
// get rid of tabs
for(;;) {
char *q = strchr(buf, '\t');
if (!q) break;
*q = ' ';
}
if (0 == _strnicmp(buf, "End of...", 6)) {
// Last line of the spew; fake location to flush the last found node.
strcpy(buf, R"(HKEY_LOCAL_MACHINE\SYSTEM\end)");
}
if (0 == strncmp(buf, R"(HKEY_LOCAL_MACHINE\SYSTEM)", 24)) {
// KEY/SUBKEY line:
switch (state) {
case 'N':
// Keep the last one, go on.
curr_reg_base_subkey = std::string(buf);
continue;
default:
if (0 != strncmp(buf, curr_reg_base_subkey.c_str(), curr_reg_base_subkey.length()))
{
// This subtree ended and new one starts. Save the current node
if (state == 'P') {
// printf("Port found... FN=%s PN=%s\n", name.c_str(), COM_port_name.c_str());
Ports.push_back({ container_id, COM_port_name });
}
else if (state == 'D') {
if (0 == name.compare(0, BT_name_prefix.length(), BT_name_prefix)) {
//printf("BTH dev found... FN=%s\n", name.c_str());
BTH_devs.push_back({ container_id, name });
}
}
// and start new (unknown type) node at this subkey:
state = 'N';
name = "";
curr_reg_base_subkey = buf;
}
}
continue;
} // subkey line
if (!strstr(buf, " REG_")) {
continue; // ignore other non-value lines
}
if (state == 'N') {
state = 'X'; // unknown type
//printf("Node start->\n%s\n", curr_reg_base_subkey.c_str());
}
// Look for interesting properties:
if (strstr(buf, "ClassGUID")) {
string cls = get_val();
char const * pcls = cls.c_str();
if (0 == _stricmp(pcls, sGUID_BTHdev)) {
state = 'D';
}
else if (0 == _stricmp(pcls, sGUID_Port)) {
state = 'P';
}
continue;
}
if (strstr(buf, "ContainerID")) {
container_id = get_val();
continue;
}
if (strstr(buf, "FriendlyName")) {
name = get_val();
continue;
}
if (strstr(buf, "PortName")) {
COM_port_name = get_val();
continue;
}
} // while
fclose(p);
//printf("Found ports: %d adapters: %d\n", Ports.size(), BTH_devs.size());
// Now select all ports that have matching container id with any BTH device:
for (auto &port : Ports) {
string port_cid = std::get<0>(port);
for (auto adp : BTH_devs) {
if (port_cid == std::get<0>(adp)) {
//printf("match\n");
// Return tuple (COM_port_name, adapter_Name)
resv.push_back({ std::get<1>(port), std::get<1>(adp) });
}
}
}
return resv;
}
int main(int argc, char *argv[])
{
char const *adp_prefix = "RNBT";
if (argc >= 2) {
adp_prefix = argv[1];
}
std::cout << "Looking up COM ports for paired/bonded BTH adapters with names like " << adp_prefix << " ...\n";
auto result = find_bth_ports(adp_prefix);
if (!result.size()) {
std::cout << "No COM ports found for BTH devices with prefix '" << adp_prefix << "'\n";
}
for (auto &res : result) {
string COM_name, adapter_name;
std::tie(COM_name, adapter_name) = res;
std::cout << "COM port name: " << COM_name << " for adapter: " << adapter_name << std::endl;
}
return result.size() ? 0 : 1;
}
@pavel-a
Copy link
Author

pavel-a commented Feb 15, 2019

Detection of "outgoing" com port of RN42 bluetooth module. Formatted as standalone program.
Seems to work on win10 1803-09.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment