Bluetooth 交换前的bluez配对
我使用来自的代码在两个Raspberry Pi之间发送消息 但是,如果我没有通过bluetoothctl在两台设备之间进行配对,我将无法使用客户端,因为它会给我带来错误: 哦,无效交换Bluetooth 交换前的bluez配对,bluetooth,raspberry-pi,bluez,rfcomm,Bluetooth,Raspberry Pi,Bluez,Rfcomm,我使用来自的代码在两个Raspberry Pi之间发送消息 但是,如果我没有通过bluetoothctl在两台设备之间进行配对,我将无法使用客户端,因为它会给我带来错误: 哦,无效交换 你能给我一些提示,告诉我如何通过C代码生成这对吗?在C代码之前,我需要“自动”使用此选项,而无需通过蓝牙CTL进行配对。在回答之前,我不知道如何使用“libbluetooth”API实现此功能。但我下面的答案是基于使用GDBUS的DBUSAPI。这很可能适用于任何最近运行的bluez(带Bluetooth的) 注
你能给我一些提示,告诉我如何通过C代码生成这对吗?在C代码之前,我需要“自动”使用此选项,而无需通过蓝牙CTL进行配对。在回答之前,我不知道如何使用“libbluetooth”API实现此功能。但我下面的答案是基于使用GDBUS的DBUSAPI。这很可能适用于任何最近运行的bluez(带Bluetooth的) 注意,对于Bluez5,建议使用DBUSAPI 简而言之,您需要开发一个自动接受配对请求的代理,假设这里有“确认”代理。请参考代理功能 在最新的bluez版本(至少5.47+)中,我们有了一个新的API“连接设备”,它可以用于连接设备而无需扫描/发现。从您的问题中,我了解到您正在尝试在两个RPi之间通信,因此您可以找到两个蓝牙控制器的BT地址。有BT地址的地方
/*
* bluez_adapter_connect.c - Connect with device without StartDiscovery
* - This example registers an agen with NoInputOutput capability for the purpose of
* auto pairing
* - Use ConnectDevice method to connect with device using provided MAC address
* - Usual signal subscription to get the details of the connected device
* - Introduced new signal handler to exit the program gracefully
*
* Note: As "ConnectDevice" is new API and in experimental state (but mostly stable)
* one need to use "-E" option when starting "bluetoothd". Systems running systemd can
* edit /lib/systemd/system/bluetooth.service in ExecStart option
*
* When this API is useful?
* - When you already have the MAC address of end bluetooth Device to connect with, then
* you don't need to scan for the device (with or without filter) and connect it.
* - StartDiscovery + Pair + Connect => ConnectDevice
*
* How you will have MAC address before scanning?
* - When you have other communication (wired or wireless) medium to exchange the MAC address
* - For example, NFC OOB can be used to exchange the MAC address
* - Testing Bluetooth with same device (MAC address known)
*
* - Here Agent capability is registered as "NoInputOutput" for experimental purpose only, in
* real world scenario, Pair + Connect involves real Agents.
* - Also note, bluez_agent_call_method and bluez_adapter_call_method are two different methods doing
* the same work with difference in interface name and object path. This exist just to make the
* understanding more clear.
*
* gcc `pkg-config --cflags glib-2.0 gio-2.0` -Wall -Wextra -o ./bin/bluez_adapter_connect ./bluez_adapter_connect.c `pkg-config --libs glib-2.0 gio-2.0`
*/
#include <glib.h>
#include <gio/gio.h>
#include <signal.h>
GMainLoop *loop;
GDBusConnection *con;
static void bluez_property_value(const gchar *key, GVariant *value)
{
const gchar *type = g_variant_get_type_string(value);
g_print("\t%s : ", key);
switch(*type) {
case 'o':
case 's':
g_print("%s\n", g_variant_get_string(value, NULL));
break;
case 'b':
g_print("%d\n", g_variant_get_boolean(value));
break;
case 'u':
g_print("%d\n", g_variant_get_uint32(value));
break;
case 'a':
/* TODO Handling only 'as', but not array of dicts */
if(g_strcmp0(type, "as"))
break;
g_print("\n");
const gchar *uuid;
GVariantIter i;
g_variant_iter_init(&i, value);
while(g_variant_iter_next(&i, "s", &uuid))
g_print("\t\t%s\n", uuid);
break;
default:
g_print("Other\n");
break;
}
}
typedef void (*method_cb_t)(GObject *, GAsyncResult *, gpointer);
static int bluez_adapter_call_method(const char *method, GVariant *param, method_cb_t method_cb)
{
g_dbus_connection_call(con,
"org.bluez",
/* TODO Find the adapter path runtime */
"/org/bluez/hci0",
"org.bluez.Adapter1",
method,
param,
NULL,
G_DBUS_CALL_FLAGS_NONE,
-1,
NULL,
method_cb,
(void *)method);
return 0;
}
static void bluez_result_async_cb(GObject *con,
GAsyncResult *res,
gpointer data)
{
const gchar *key = (gchar *)data;
GVariant *result = NULL;
GError *error = NULL;
result = g_dbus_connection_call_finish((GDBusConnection *)con, res, &error);
if(error != NULL) {
g_print("Unable to get result: %s\n", error->message);
return;
}
if(result) {
result = g_variant_get_child_value(result, 0);
bluez_property_value(key, result);
}
g_variant_unref(result);
}
static void bluez_device_appeared(GDBusConnection *sig,
const gchar *sender_name,
const gchar *object_path,
const gchar *interface,
const gchar *signal_name,
GVariant *parameters,
gpointer user_data)
{
(void)sig;
(void)sender_name;
(void)object_path;
(void)interface;
(void)signal_name;
(void)user_data;
GVariantIter *interfaces;
const char *object;
const gchar *interface_name;
GVariant *properties;
g_variant_get(parameters, "(&oa{sa{sv}})", &object, &interfaces);
while(g_variant_iter_next(interfaces, "{&s@a{sv}}", &interface_name, &properties)) {
if(g_strstr_len(g_ascii_strdown(interface_name, -1), -1, "device")) {
g_print("[ %s ]\n", object);
const gchar *property_name;
GVariantIter i;
GVariant *prop_val;
g_variant_iter_init(&i, properties);
while(g_variant_iter_next(&i, "{&sv}", &property_name, &prop_val))
bluez_property_value(property_name, prop_val);
g_variant_unref(prop_val);
}
g_variant_unref(properties);
}
return;
}
#define BT_ADDRESS_STRING_SIZE 18
static void bluez_device_disappeared(GDBusConnection *sig,
const gchar *sender_name,
const gchar *object_path,
const gchar *interface,
const gchar *signal_name,
GVariant *parameters,
gpointer user_data)
{
(void)sig;
(void)sender_name;
(void)object_path;
(void)interface;
(void)signal_name;
GVariantIter *interfaces;
const char *object;
const gchar *interface_name;
char address[BT_ADDRESS_STRING_SIZE] = {'\0'};
g_variant_get(parameters, "(&oas)", &object, &interfaces);
while(g_variant_iter_next(interfaces, "s", &interface_name)) {
if(g_strstr_len(g_ascii_strdown(interface_name, -1), -1, "device")) {
int i;
char *tmp = g_strstr_len(object, -1, "dev_") + 4;
for(i = 0; *tmp != '\0'; i++, tmp++) {
if(*tmp == '_') {
address[i] = ':';
continue;
}
address[i] = *tmp;
}
g_print("\nDevice %s removed\n", address);
g_main_loop_quit((GMainLoop *)user_data);
}
}
return;
}
static void bluez_signal_adapter_changed(GDBusConnection *conn,
const gchar *sender,
const gchar *path,
const gchar *interface,
const gchar *signal,
GVariant *params,
void *userdata)
{
(void)conn;
(void)sender;
(void)path;
(void)interface;
(void)userdata;
GVariantIter *properties = NULL;
GVariantIter *unknown = NULL;
const char *iface;
const char *key;
GVariant *value = NULL;
const gchar *signature = g_variant_get_type_string(params);
if(strcmp(signature, "(sa{sv}as)") != 0) {
g_print("Invalid signature for %s: %s != %s", signal, signature, "(sa{sv}as)");
goto done;
}
g_variant_get(params, "(&sa{sv}as)", &iface, &properties, &unknown);
while(g_variant_iter_next(properties, "{&sv}", &key, &value)) {
if(!g_strcmp0(key, "Powered")) {
if(!g_variant_is_of_type(value, G_VARIANT_TYPE_BOOLEAN)) {
g_print("Invalid argument type for %s: %s != %s", key,
g_variant_get_type_string(value), "b");
goto done;
}
g_print("Adapter is Powered \"%s\"\n", g_variant_get_boolean(value) ? "on" : "off");
}
if(!g_strcmp0(key, "Discovering")) {
if(!g_variant_is_of_type(value, G_VARIANT_TYPE_BOOLEAN)) {
g_print("Invalid argument type for %s: %s != %s", key,
g_variant_get_type_string(value), "b");
goto done;
}
g_print("Adapter scan \"%s\"\n", g_variant_get_boolean(value) ? "on" : "off");
}
}
done:
if(properties != NULL)
g_variant_iter_free(properties);
if(value != NULL)
g_variant_unref(value);
}
static int bluez_adapter_set_property(const char *prop, GVariant *value)
{
GVariant *result;
GError *error = NULL;
result = g_dbus_connection_call_sync(con,
"org.bluez",
"/org/bluez/hci0",
"org.freedesktop.DBus.Properties",
"Set",
g_variant_new("(ssv)", "org.bluez.Adapter1", prop, value),
NULL,
G_DBUS_CALL_FLAGS_NONE,
-1,
NULL,
&error);
if(error != NULL)
return 1;
g_variant_unref(result);
return 0;
}
static int bluez_adapter_connect_device(char **argv)
{
int rc;
GVariantBuilder *b = g_variant_builder_new(G_VARIANT_TYPE_VARDICT);
g_variant_builder_add(b, "{sv}", "Address", g_variant_new_string(argv[1]));
GVariant *device_dict = g_variant_builder_end(b);
g_variant_builder_unref(b);
rc = bluez_adapter_call_method("ConnectDevice",
g_variant_new_tuple(&device_dict, 1),
bluez_result_async_cb);
if(rc) {
g_print("Not able to call ConnectDevice\n");
return 1;
}
return 0;
}
#define AGENT_PATH "/org/bluez/AutoPinAgent"
static int bluez_agent_call_method(const gchar *method, GVariant *param)
{
GVariant *result;
GError *error = NULL;
result = g_dbus_connection_call_sync(con,
"org.bluez",
"/org/bluez",
"org.bluez.AgentManager1",
method,
param,
NULL,
G_DBUS_CALL_FLAGS_NONE,
-1,
NULL,
&error);
if(error != NULL) {
g_print("Register %s: %s\n", AGENT_PATH, error->message);
return 1;
}
g_variant_unref(result);
return 0;
}
static int bluez_register_autopair_agent(void)
{
int rc;
rc = bluez_agent_call_method("RegisterAgent", g_variant_new("(os)", AGENT_PATH, "NoInputNoOutput"));
if(rc)
return 1;
rc = bluez_agent_call_method("RequestDefaultAgent", g_variant_new("(o)", AGENT_PATH));
if(rc) {
bluez_agent_call_method("UnregisterAgent", g_variant_new("(o)", AGENT_PATH));
return 1;
}
return 0;
}
static void cleanup_handler(int signo)
{
if (signo == SIGINT) {
g_print("received SIGINT\n");
g_main_loop_quit(loop);
}
}
int main(int argc, char **argv)
{
int rc;
guint prop_changed;
guint iface_added;
guint iface_removed;
if(signal(SIGINT, cleanup_handler) == SIG_ERR)
g_print("can't catch SIGINT\n");
con = g_bus_get_sync(G_BUS_TYPE_SYSTEM, NULL, NULL);
if(con == NULL) {
g_print("Not able to get connection to system bus\n");
return 1;
}
loop = g_main_loop_new(NULL, FALSE);
prop_changed = g_dbus_connection_signal_subscribe(con,
"org.bluez",
"org.freedesktop.DBus.Properties",
"PropertiesChanged",
NULL,
"org.bluez.Adapter1",
G_DBUS_SIGNAL_FLAGS_NONE,
bluez_signal_adapter_changed,
NULL,
NULL);
iface_added = g_dbus_connection_signal_subscribe(con,
"org.bluez",
"org.freedesktop.DBus.ObjectManager",
"InterfacesAdded",
NULL,
NULL,
G_DBUS_SIGNAL_FLAGS_NONE,
bluez_device_appeared,
loop,
NULL);
iface_removed = g_dbus_connection_signal_subscribe(con,
"org.bluez",
"org.freedesktop.DBus.ObjectManager",
"InterfacesRemoved",
NULL,
NULL,
G_DBUS_SIGNAL_FLAGS_NONE,
bluez_device_disappeared,
loop,
NULL);
rc = bluez_adapter_set_property("Powered", g_variant_new("b", TRUE));
if(rc) {
g_print("Not able to enable the adapter\n");
goto fail;
}
rc = bluez_register_autopair_agent();
if(rc) {
g_print("Not able to register default autopair agent\n");
goto fail;
}
if(argc == 2) {
rc = bluez_adapter_connect_device(argv);
if(rc)
goto fail;
}
g_main_loop_run(loop);
rc = bluez_adapter_set_property("Powered", g_variant_new("b", FALSE));
if(rc)
g_print("Not able to disable the adapter\n");
fail:
g_dbus_connection_signal_unsubscribe(con, prop_changed);
g_dbus_connection_signal_unsubscribe(con, iface_added);
g_dbus_connection_signal_unsubscribe(con, iface_removed);
g_object_unref(con);
return 0;
}
/*
*bluez_适配器_connect.c-与未启动的设备连接查看
*-此示例注册了一个具有NoInputOutput功能的代理,用于
*自动配对
*-使用ConnectDevice方法使用提供的MAC地址与设备连接
*-获取连接设备详细信息的常规信号订阅
*-引入新的信号处理程序以优雅地退出程序
*
*注:由于“ConnectDevice”是新的API,处于实验状态(但基本稳定)
*启动“Bluetooth D”时需要使用“-E”选项。运行systemd can的系统
*在ExecStart选项中编辑/lib/systemd/system/bluetooth.service
*
*这个API什么时候有用?
*-当您已经拥有要连接的终端蓝牙设备的MAC地址时,然后
*您不需要扫描设备(带或不带过滤器)并连接它。
*-StartDiscovery+Pair+Connect=>ConnectDevice
*
*在扫描之前,您将如何获得MAC地址?
*-当您有其他通信(有线或无线)媒介来交换MAC地址时
*-例如,NFC OOB可用于交换MAC地址
*-使用相同设备测试蓝牙(MAC地址已知)
*
*-此处,代理能力注册为“NoInputOutput”,仅用于实验目的,在
*现实世界场景中,配对+连接涉及真实的代理。
*-还要注意,bluez_代理调用方法和bluez_适配器调用方法是两种不同的方法
*相同的工作方式在接口名称和对象路径上有所不同。这个存在只是为了使
*理解更加清晰。
*
*gcc`pkg config--cflags glib-2.0 gio-2.0`-Wall-Wextra-o./bin/bluez_adapter_connect./bluez_adapter_connect.c`pkg config--libs glib-2.0 gio-2.0`
*/
#包括
#包括
#包括
GMainLoop*循环;
GDBUS连接*con;
静态void bluez_属性_值(常量gchar*键,GVariant*值)
{
const gchar*type=g_variant_get_type_string(值);
g_打印(“\t%s:”,键);
开关(*类型){
案例“o”:
案例s:
g_print(“%s\n”,g_variant_get_string(value,NULL));
打破
案例“b”:
g_print(“%d\n”,g_variant_get_boolean(value));
打破
案例“u”:
g_print(“%d\n”,g_variant_get_uint32(value));
打破
案例“a”:
/*TODO只处理'as',不处理DICT数组*/
if(g_strcmp0(类型“as”))
打破
g_打印(“\n”);
常量gchar*uuid;
GVariantIterⅠ;
g_变体_iter_init(&i,值);
while(g_变体_iter_next(&i,“s”和uuid))
g\U打印(“\t\t%s\n”,uuid);
打破
违约:
g_print(“其他”);
打破
}
}
类型定义无效(*方法cb t)(GObject*,GAsyncResult*,gpointer);
静态int-bluez_适配器_调用_方法(常量字符*方法、GVariant*参数、方法_cb_t方法_cb)
{
g_dbus_连接_呼叫(con,
“org.bluez”,
/*TODO查找适配器路径运行时*/
“/org/bluez/hci0”,
“org.bluez.Adapter1”,
方法,,
param,
无效的
G_DBUS_CALL_FLAGS_NONE,
-1,
无效的
方法_cb,
(无效*)法);
返回0;
}
静态无效bluez_结果_异步_cb(GObject*con,
GAsyncResult*res,
gpointer数据)
{
常量gchar*键=(gchar*)数据;
GVariant*result=NULL;
GError*error=NULL;
结果=g_dbus_connection_call_finish((GDBusConnection*)con、res和error);
if(错误!=NULL){
g_print(“无法获取结果:%s\n”,错误->消息);
返回;
}
如果(结果){
结果=g_变量_获取_子_值(结果,0);
bluez_属性_值(键、结果);
}
g_变量_unref(结果);
}
出现静态无效bluez_设备(GDBusConnection*sig,
const gchar*发送方名称,
常量gchar*对象路径,
常量gchar*接口,
常量gchar*信号名称,
GVariant*参数,
gpointer用户(U数据)
{
(无效)sig;
(无效)发件人姓名;
(void)对象路径;
(d)接口;
(无效)信号名称;
(作废)用户数据;
gVariantier*接口;
常量字符*对象;
常量gchar*接口名称;
GVariant*属性;
g_variant_get(参数,(&oa{sa{sv}})”、&object和接口);
而(g_变体_iter_下一个(接口){&s@a{sv}}“,&接口(名称和属性)){
if(g_strstr_len(g_ascii_strdown(接口名称,-1),-1,“设备”)){
g_