【嵌入式Bluetooth应用开发笔记】第一篇:DBUS概述与蓝牙开发小试牛刀

2023-05-16

DBUS概述

DBus(D-Bus)是一个在不同程序之间传递消息的系统总线。DBus为不同的程序之间提供了一种通信机制,这种通信制可以在不需要知道对方程序的情况下进行通信。
DBus可以使用多种编程语言来开发,包括C、C++、Python、Java等。在这里,我们以C语言为例来讲解DBus的开发过程。
DBus的主要特点包括:
1. 语言独立性:DBus支持多种编程语言,包括C、C++、Python、Java等。
2. 灵活性:DBus的通信机制非常灵活,支持广播、点对点、异步和同步通信。
3. 安全性:DBus提供了一些安全机制,如身份验证和权限控制,以确保通信的安全性。
4. 可扩展性:DBus支持多个会话总线,可以用于在多个计算机之间进行通信,同时还可以在运行时动态添加和删除对象。
DBus接口(DBus interface)是DBus对象(DBus object)上的一个虚拟界面,它定义了可以通过DBus对象访问的DBus方法、DBus信号和DBus属性。DBus接口使用DBus编程语言中的XML描述符来定义,它具有以下特点:
* DBus接口具有一个名称,它使用类似于UNIX文件系统路径的命名方式来组织,例如 “com.example.Calculator”。
* DBus接口可以继承其他DBus接口。
* DBus接口可以定义DBus方法,DBus方法可以具有零个或多个输入参数和零个或一个输出参数。
* DBus接口可以定义DBus信号,DBus信号是DBus对象发出的一种事件通知,它可以包含任意数量的参数。
* DBus接口可以定义DBus属性,DBus属性是DBus对象的状态信息,它可以被读取或写入。DBus属性可以是只读、可写或读写的。

DBus接口通常是DBus应用程序的主要组成部分,因为它定义了DBus对象的可用功能。在DBus编程中,我们通常使用DBus接口来定义DBus对象的行为,然后在DBus对象上注册DBus接口。这样,在DBus客户端与DBus对象进行通信时,DBus客户端可以查询DBus接口以了解DBus对象的功能,并调用DBus方法、监听DBus信号和读取或写入DBus属性。

DBus的开发步骤

DBus的开发过程可以分为以下几个步骤:
1. 创建DBus连接
在使用DBus进行通信之前,需要先创建一个DBus连接。DBus连接可以通过函数 dbus_bus_get() 来获取。
2. 注册DBus对象
在DBus中,所有的对象都必须先注册才能被其他程序访问。DBus对象可以使用 dbus_connection_register_object() 函数进行注册。
3. 创建DBus消息
在DBus中,消息是所有通信的基本单元。DBus消息可以使用 dbus_message_new() 函数进行创建。
4. 发送DBus消息
创建好DBus消息之后,需要使用 dbus_connection_send() 函数将消息发送出去。
5. 接收DBus消息
在DBus中,需要使用事件循环来等待消息的到来。DBus消息可以使用 dbus_connection_pop_message() 函数来获取。
6. 处理DBus消息
获取到DBus消息之后,需要对消息进行解析和处理。
7. 清理DBus连接
在程序退出之前,需要释放所有的DBus资源。DBus连接可以使用 dbus_connection_unref() 函数来释放。

下面我们通过一个简单的示例来演示DBus的开发过程。示例程序
在本例中,我们将创建一个DBus服务,这个服务可以将两个整数相加并返回结果。

1. 创建DBus连接

首先,我们需要创建DBus连接。DBus连接可以通过函数 dbus_bus_get() 来获取。

DBusError err; 
DBusConnection* conn; 
dbus_error_init(&err); 
conn = dbus_bus_get(DBUS_BUS_SESSION, &err); 
if(dbus_error_is_set(&err)) 
{ 
  printf("DBus connection error: %s\n", err.message); 
  dbus_error_free(&err); 
  return-1; 
}

2.注册DBus对象

我们需要将一个对象注册到DBus上。在本例中,我们将一个对象 /com/example/Calculator 注册到DBus上,并定义两个方法 Add 和 Quit。

DBusObjectPathVTable vtable = { .message_function = &dbus_message_handler }; 
DBusError err; 
DBusObjectPath* path; 
DBusObject* object; 
dbus_error_init(&err); 
path = dbus_object_path_new("/com/example/Calculator"); 
object = dbus_object_new(conn, path); 
dbus_connection_register_object(conn, path, &vtable, object); 
if(dbus_error_is_set(&err)) 
{ 
  printf("DBus object registration error: %s\n", err.message); 
  dbus_error_free(&err);return-1; 
}

在上述代码中,我们定义了一个 DBusObjectPathVTable 结构体,用于注册DBus对象时的消息处理函数 dbus_message_handler()。在本例中,我们需要实现两个方法 Add 和 Quit,因此我们需要在消息处理函数中对这两个方法进行处理。

DBusHandlerResultdbus_message_handler(DBusConnection* conn, DBusMessage* msg,void* user_data) 
{ 
  if(dbus_message_is_method_call(msg,"com.example.Calculator","Add")) 
  { 
      // 处理 Add 方法
      DBusMessageIter iter;inta, b, sum; 
      dbus_message_iter_init(msg, &iter); 
      dbus_message_iter_get_basic(&iter, &a);
      dbus_message_iter_next(&iter); 
      dbus_message_iter_get_basic(&iter, &b); 
      sum = a + b; 
      DBusMessage* reply = dbus_message_new_method_return(msg); 
      DBusMessageIter reply_iter; dbus_message_iter_init_append(reply, &reply_iter); 
      dbus_message_iter_append_basic(&reply_iter, DBUS_TYPE_INT32, &sum); 
      dbus_connection_send(conn, reply,NULL); 
      dbus_message_unref(reply); 
      return DBUS_HANDLER_RESULT_HANDLED; 
	}else if(
    dbus_message_is_method_call(msg,"com.example.Calculator","Quit")) 
  { 
      // 处理 Quit 方法
      dbus_connection_close(conn); 
      return DBUS_HANDLER_RESULT_HANDLED;
   } 
    return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; 
}

在上述代码中,我们首先判断消息是否为 Add 方法的调用。如果是,我们就从消息中获取两个整数 a 和 b,然后将它们相加得到 sum。接下来,我们将 sum 封装到一个新的DBus消息中,并将这个消息通过 dbus_connection_send() 函数发送出去。
如果消息是 Quit 方法的调用,我们就通过 dbus_connection_close() 函数关闭DBus连接。

3.创建DBus消息

在本例中,我们需要创建一个DBus消息来调用 Add 方法。

DBusMessage* msg = dbus_message_new_method_call("com.example.Calculator","/com/example/Calculator","com.example.Calculator","Add"); 
DBusMessageIter args;
dbus_message_iter_init_append(msg, &args); 
dbus_message_iter_append_basic(&args, DBUS_TYPE_INT32, &a); 
dbus_message_iter_append_basic(&args, DBUS_TYPE_INT32, &b);

在上述代码中,我们首先创建了一个DBus消息 msg,然后将消息类型设置为方法调用,方法名为 Add,对象路径为 /com/example/Calculator。
接下来,我们使用 dbus_message_iter_init_append() 函数初始化一个DBus消息迭代器,并将两个整数 a 和 b 追加到DBus消息中。

4. 发送DBus消息

创建好DBus消息之后,我们可以使用 dbus_connection_send() 函数将消息发送出去。

DBusPendingCall* pending; 
dbus_connection_send_with_reply(conn, msg, &pending,-1); 
dbus_connection_flush(conn);

在上述代码中,我们使用 dbus_connection_send_with_reply() 函数将DBus消息发送出去,并等待DBus服务器的应答。应答消息将存储在 pending 变量中。我们还可以使用 dbus_connection_flush() 函数强制刷新DBus连接,以确
保消息被发送出去。

5. 接收DBus应答

当DBus服务器收到DBus消息并处理完成后,会通过DBus连接向客户端发送应答消息。在上述代码中,我们使用 dbus_pending_call_set_notify() 函数注册回调函数 dbus_reply_handler()。当DBus应答消息到达时,回调函数将被调用,并将DBus应答消息中的整数 sum 传递给它。

6. 处理DBus应答

当DBus应答消息到达时,回调函数 dbus_reply_handler() 将被调用。在本例中,我们需要从DBus应答消息中获取整数 sum 并打印出来。

void dbus_reply_handler(DBusPendingCall* pending,void* user_data)
{ 
  int* sum = (int*)user_data; 
  DBusMessage* reply = dbus_pending_call_steal_reply(pending); 
  DBusMessageIter iter; 
  if(reply) 
  { 
  	if(dbus_message_iter_init(reply, &iter)) 	
  { 
    dbus_message_iter_get_basic(&iter, sum); 
    printf("The sum is: %d\n", *sum); 
  } 
  	dbus_message_unref(reply); 
  }
  dbus_pending_call_unref(pending); 
}

在上述代码中,我们首先从回调函数参数 user_data 中获取整数指针 sum,然后使用 dbus_pending_call_steal_reply() 函数从DBus应答消息中提取应答消息。如果应答消息存在,我们就从消息中获取整数 sum 并打印出来。最后,我们释放DBus应答消息和DBus挂起调用。

完整示例代码


#include<stdio.h> 
#include<stdlib.h> 
#include<dbus/dbus.h> 
DBusHandlerResult dbus_message_handler(DBusConnection* conn, DBusMessage* msg,void* user_data) 
{ 
    if(dbus_message_is_method_call(msg,"com.example.Calculator","Add")) 
    {
        // 处理 Add 方法
        DBusMessageIter iter; 
        inta, b, sum;
        dbus_message_iter_init(msg, &iter); 
        dbus_message_iter_get_basic(&iter, &a); 
        dbus_message_iter_next(&iter);
        dbus_message_iter_get_basic(&iter, &b); 
        sum = a + b; DBusMessage* reply = dbus_message_new_method_return(msg); 
        DBusMessageIter reply_iter; 
        dbus_message_iter_init_append(reply, &reply_iter); 
        dbus_message_iter_append_basic(&reply_iter, DBUS_TYPE_INT32, &sum);
        dbus_connection_send(conn, reply,NULL); 
        dbus_message_unref(reply); 
    	returnDBUS_HANDLER_RESULT_HANDLED; 
    }else if(
      dbus_message_is_method_call(msg,"com.example.Calculator","Quit")){ 
        // 处理 Quit 方法 
        dbus_connection_close(conn); 
        returnDBUS_HANDLER_RESULT_HANDLED;
     } 
    return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; 
} 
voiddbus_reply_handler(DBusPendingCall* pending,void* user_data) 
{
  int* sum = (int*)user_data;
  DBusMessage* reply =dbus_pending_call_steal_reply(pending);
  DBusMessageIter iter;
  if (reply)
  {
    if (dbus_message_iter_init(reply, &iter))
    { 
      dbus_message_iter_get_basic(&iter, sum); 
      printf("The sum is: %d\n", *sum);
    } 
  	dbus_message_unref(reply);
  } 
  dbus_pending_call_unref(pending);
}
int main() 
{ 
	DBusError error; 
    DBusConnection* conn; 
    DBusMessage* msg; 
    DBusPendingCall* pending;
    int sum = 0dbus_error_init(&error);

    // 建立到系统DBus总线的连接
    conn = dbus_bus_get(DBUS_BUS_SYSTEM, &error);

    if (dbus_error_is_set(&error)) {
        fprintf(stderr, "Error connecting to system bus: %s\n", error.message);
        dbus_error_free(&error);
        return EXIT_FAILURE;
    }

    // 注册对象路径和消息处理函数
    dbus_bus_request_name(conn, "com.example.Calculator", 0, &error);

    if (dbus_error_is_set(&error)) {
        fprintf(stderr, "Error requesting name: %s\n", error.message);
        dbus_error_free(&error);
        return EXIT_FAILURE;
    }

    dbus_connection_register_object_path(conn, "/com/example/Calculator", &dbus_interface, NULL);

    // 创建DBus消息
    msg = dbus_message_new_method_call("com.example.Calculator", "/com/example/Calculator", "com.example.Calculator", "Add");

    // 将参数添加到DBus消息中
    DBusMessageIter iter;
    dbus_message_iter_init_append(msg, &iter);
    int a = 2, b = 3;
    dbus_message_iter_append_basic(&iter, DBUS_TYPE_INT32, &a);
    dbus_message_iter_append_basic(&iter, DBUS_TYPE_INT32, &b);

    // 发送DBus消息并等待应答
    dbus_connection_send_with_reply(conn, msg, &pending, -1);
    dbus_message_unref(msg);

    dbus_pending_call_set_notify(pending, dbus_reply_handler, &sum, NULL);

    while (dbus_connection_read_write_dispatch(conn, -1)) {

    }

    dbus_connection_unref(conn);

    return EXIT_SUCCESS;
}

在上述代码中,我们首先建立到系统DBus总线的连接,然后注册对象路径和消息处理函数。接下来,我们创建一个DBus消息,并将参数添加到消息中。然后,我们使用 dbus_connection_send_with_reply() 函数将DBus消息发送到DBus服务器,并等待应答。在等待期间,我们使用 dbus_connection_read_write_dispatch() 函数循环调用DBus连接以便处理DBus消息。当DBus应答消息到达时,回调函数 dbus_reply_handler() 将被调用。

常用的蓝牙dbus接口

在Linux系统中,蓝牙的实现通常使用DBus进行管理和控制。下面介绍一些常用的蓝牙DBus接口及其功能:
1. org.bluez.Agent1

这个接口用于管理蓝牙代理,实现了蓝牙认证和授权功能。在进行蓝牙配对时,代理程序将被调用来执行认证和授权操作。该接口定义了以下方法:

* RequestConfirmation (in uint32_t, out uint32_t)
* RequestAuthorization (in uint32_t, out uint32_t)
* AuthorizeService (in object path, out boolean)
* Cancel ()

2.org.bluez.Adapter1
这个接口用于管理蓝牙适配器,实现了蓝牙设备的搜索和管理功能。该接口定义了以下方法:
* StartDiscovery ()
* StopDiscovery ()
* GetProperties ()
* SetProperty (in string, in variant)
* GetAllProperties ()
* CreateDevice (in string, out object path)
* RemoveDevice (in object path)
* GetDevice (in object path, out object path)
* GetDevices (out array of object path)
* GetDeviceProperties (in object path, out dict of string variant)

3.org.bluez.Device1
这个接口用于管理蓝牙设备,实现了设备的配对、连接和断开连接等功能。该接口定义了以下方法:
* Connect ()
* Disconnect ()
* Pair ()
* CancelPairing ()
* GetProperties ()
* SetProperty (in string, in variant)
* GetAllProperties ()
* GetServices (out array of object path)

4.org.bluez.MediaTransport1
这个接口用于管理蓝牙音频传输,实现了音频的传输和控制功能。该接口定义了以下方法:
* SetConfiguration (in dict of string variant)
* SelectConfiguration (in dict of string variant)
* ClearConfiguration ()
* Release ()
* Acquire (out string)
* GetProperties ()
* SetProperty (in string, in variant)
* GetAllProperties ()
这些接口定义了蓝牙管理和控制的基本方法,开发人员可以通过DBus API来调用这些方法实现蓝牙应用程序的开发。需要注意的是,这些接口在不同版本的蓝牙协议中可能存在差异,开发人员需要根据具体情况进行调整。

初试dbus调用蓝牙接口

在Linux系统中,DBus API可以使用C语言或其他编程语言进行调用。下面以C语言为例介绍如何使用DBus API调用这些接口。

1. 首先需要包含DBus头文件

#include<dbus/dbus.h>

2.初始化DBus连接

DBusError error; 
DBusConnection* conn; 
dbus_error_init(&error); 
conn = dbus_bus_get(DBUS_BUS_SYSTEM, &error); 
if(dbus_error_is_set(&error)) 
{ 
  printf("Error connecting to the daemon bus: %s\n", error.message);
  dbus_error_free(&error); 
  return; 
}

3.调用DBus方法

DBusMessage* msg; 
DBusMessageIter args; 
DBusPendingCall* pending; 
msg = dbus_message_new_method_call("org.bluez","/org/bluez/hci0","org.bluez.Adapter1","StartDiscovery"); 
if(!msg) { 
	printf("Error creating dbus message\n"); 
	return; 
} 
if(!dbus_connection_send_with_reply(conn, msg, &pending,-1)) 
{ 
	printf("Error sending dbus message\n"); 
	dbus_message_unref(msg); 
	return; 
} 
dbus_connection_flush(conn); 
dbus_message_unref(msg); 
dbus_pending_call_block(pending); 
msg = dbus_pending_call_steal_reply(pending); 
if(msg) 
{ 
	dbus_message_iter_init(msg, &args); 
	dbus_pending_call_unref(pending); 
	dbus_message_unref(msg); 
}

在上面的例子中,我们调用了org.bluez.Adapter1接口的StartDiscovery方法。其中,dbus_message_new_method_call函数用于创建DBus方法调用消息,第一个参数是服务名称,第二个参数是对象路径,第三个参数是接口名称,第四个参数是方法名称。dbus_connection_send_with_reply函数用于发送DBus消息并等待回复,第一个参数是DBus连接,第二个参数是DBus消息,第三个参数是DBus异步调用,第四个参数是超时时间。dbus_pending_call_block函数用于阻塞DBus调用直到接收到回复,dbus_pending_call_steal_reply函数用于获取DBus调用的回复。
通过以上步骤,我们可以使用DBus API调用蓝牙相关的接口实现蓝牙应用程序的开发。需要注意的是,DBus API的调用方式和参数类型在不同版本的DBus协议中可能存在差异,开发人员需要根据具体情况进行调整。

一些难点注意

1.D-Bus消息的结构

DBus消息是由一系列DBus类型构成的结构,开发人员需要了解DBus消息的类型和结构,才能正确地创建和解析DBus消息。DBus类型包括基本类型(如整型、字符串、布尔型等)和复合类型(如结构体、数组、字典等)。DBus消息的结构和类型定义在DBus规范中,开发人员需要根据规范进行编程。

2. DBus连接和线程安全

DBus连接是DBus消息传输的基础,开发人员需要了解DBus连接的创建、销毁和使用方法。同时,DBus连接还涉及到线程安全的问题,因为DBus连接是多线程共享的资源。开发人员需要了解DBus连接的线程安全特性,合理地管理DBus连接的生命周期和使用方法,避免出现线程安全问题。

3. DBus错误处理

DBus API的调用过程中,可能会出现各种错误,包括DBus连接错误、DBus消息格式错误、DBus方法调用错误等。开发人员需要了解DBus错误处理的方法,正确地捕获和处理DBus错误,避免出现程序崩溃等严重问题。

4. DBus调试工具

在DBus开发过程中,可能会遇到DBus调用失败、消息格式错误等问题,需要进行调试。DBus提供了一些调试工具,如dbus-monitor、dbus-send等,开发人员需要了解这些工具的使用方法,以便快速定位和解决DBus调试问题。
总之,在进行Linux C中DBus开发时,开发人员需要深入了解DBus规范、DBus API和DBus调试工具,才能够高效地进行DBus开发。同时,需要注意DBus连接的线程安全问题和DBus错误处理问题,以提高程序的可靠性和稳定性。

GDBUS概述

gdbus是Glib库提供的DBus API的实现,是基于DBus C API的一层封装。Glib是GNOME桌面环境的核心库,提供了许多常用的工具和组件,如事件循环、线程、内存管理、字符串处理、数据结构等。
gdbus封装了DBus API,提供了一组更易用的API,比DBus C API更加简洁、直观、安全和高效。使用gdbus开发DBus应用程序,可以大大提高开发效率和代码质量。
gdbus的特点包括:
1. 对DBus API进行了封装,提供了更易用的API接口,简化了DBus开发的过程和代码量。
2. 提供了自动类型转换和参数检查机制,避免了DBus API的错误使用。
3. 使用Glib的事件循环机制,提供了异步DBus调用的支持,能够更好地处理DBus的异步消息。
4. 提供了DBus接口的代理机制,支持DBus的远程调用。

总之,gdbus是Glib库提供的DBus API的实现,是DBus开发的一种高层次的抽象,能够大大简化DBus开发的过程和代码量,并提供了异步DBus调用和DBus远程调用的支持。

GDBUS API的介绍

  1. g_dbus_connection_new_sync: 此函数用于创建新的GDBusConnection连接。它需要指定一个连接名和一个连接地址,还可以指定DBus连接的选项。
  2. g_dbus_connection_call_sync: 此函数用于在给定的GDBus连接上同步调用远程对象的方法。它需要指定对象的名称、接口名称、方法名称和方法的参数。此外,还可以指定调用超时时间和DBus调用的选项。
  3. g_dbus_proxy_new_sync: 此函数用于创建一个新的GDBus代理。它需要指定一个GDBusConnection连接、一个DBus代理的名称、代理接口的名称以及DBus代理的选项。
  4. g_dbus_proxy_call_sync: 此函数用于在给定的GDBus代理上同步调用远程对象的方法。它需要指定方法名称、方法的参数、调用超时时间和DBus调用的选项。
  5. g_dbus_proxy_set_default_timeout: 此函数用于设置DBus调用的默认超时时间(以毫秒为单位)。这个超时时间将应用于调用同步方法和在DBus代理上调用异步方法时未指定超时时间的调用。
  6. g_dbus_proxy_call: 此函数用于在给定的GDBus代理上异步调用远程对象的方法。它需要指定方法名称、方法的参数、DBus调用的选项以及一个回调函数来处理异步调用结果。
  7. g_dbus_proxy_call_finish: 此函数用于完成由g_dbus_proxy_call启动的异步DBus调用,并返回调用结果。
  8. g_dbus_proxy_new_for_bus_sync: 此函数用于在指定的DBus总线上创建一个新的GDBus代理。它需要指定DBus总线类型、DBus代理的名称、代理接口的名称以及DBus代理的选项。
  9. g_dbus_connection_new_for_address_sync():建立与DBus daemon的连接。在bluez开发中,我们需要通过该API建立与BlueZ daemon的连接。
  10. g_dbus_connection_register_object():用于注册一个对象到DBus系统总线上。在bluez开发中,我们可以用它将自定义的蓝牙服务以及属性注册到BlueZ daemon上。
  11. g_dbus_connection_call_sync():用于同步调用DBus方法。在bluez开发中,我们可以用它来调用BlueZ daemon上的方法,比如获取蓝牙设备、服务或特征的属性等。
  12. g_dbus_connection_signal_subscribe():用于订阅DBus signal。在bluez开发中,我们可以用它来订阅蓝牙设备的状态改变、蓝牙服务的添加或删除、蓝牙特征的值变化等事件。
  13. g_dbus_proxy_new_sync():用于创建一个代理对象。在bluez开发中,我们可以用它来创建一个代理对象,从而方便地操作蓝牙设备、服务或特征。
  14. g_dbus_proxy_call_sync():用于同步调用代理对象上的DBus方法。在bluez开发中,我们可以用它来调用代理对象上的方法,比如读取或写入蓝牙特征的值。
  15. g_dbus_proxy_get_property():用于获取代理对象上的属性值。在bluez开发中,我们可以用它来获取蓝牙设备、服务或特征的属性值。
  16. g_dbus_proxy_set_property():用于设置代理对象上的属性值。在bluez开发中,我们可以用它来设置蓝牙设备、服务或特征的属性值。

GDBUS的开发步骤

1.定义DBus接口

定义DBus接口,包括接口名称、方法名称、参数等,可以通过GDBusNodeInfo结构体来描述DBus接口。GDBusNodeInfo结构体包含一个或多个GDBusInterfaceInfo结构体,每个GDBusInterfaceInfo结构体描述一个DBus接口。可以使用GDBusNodeInfoBuilder结构体来动态地创建GDBusNodeInfo结构体。

2.注册DBus接口

将DBus接口注册到DBus总线上,可以通过GDBusConnection结构体的g_dbus_connection_register_object()函数来实现。该函数的参数包括DBus总线连接、对象路径、DBus接口信息、DBus方法回调函数等。

3.连接DBus总线

通过g_bus_get_sync()函数连接DBus总线。该函数返回一个GDBusConnection结构体指针,可以通过该结构体指针来发送DBus消息、注册DBus接口、监听DBus信号等。

4.发送DBus消息

使用GDBusConnection结构体的g_dbus_connection_send_message_with_reply_sync()函数或g_dbus_connection_send_message_with_reply_async()函数来发送DBus消息。这些函数的参数包括DBus总线连接、DBus消息、DBus消息类型、超时时间等。

5. 接收DBus消息

使用GDBusConnection结构体的g_dbus_connection_signal_subscribe()函数来接收DBus消息。该函数的参数包括DBus总线连接、DBus接口名称、信号名称、回调函数、用户数据等。

6. 处理DBus方法调用

使用GDBusConnection结构体的g_dbus_connection_register_object()函数注册DBus方法回调函数。当DBus客户端调用DBus方法时,DBus守护进程将会调用相应的DBus方法回调函数。可以在DBus方法回调函数中处理DBus方法调用并返回结果。

7. 启动事件循环

使用g_main_loop_run()函数启动事件循环,以便接收DBus消息、处理DBus方法调用等。
以上是使用gdbus开发DBus应用程序的基本步骤。需要注意的是,gdbus是Glib库提供的DBus API的实现,因此需要安装Glib库和gio库才能进行gdbus开发。

GDBus调用蓝牙BlueZ 接口

1.创建 GDBus 连接

GDBusConnection *bus =NULL; 
GError *error =NULL; 
bus = g_bus_get_sync(G_BUS_TYPE_SYSTEM,NULL, &error); 
if(!bus) 
{ 
  g_error("Failed to get system bus: %s", error->message); 
  g_error_free(error); 
  return; 
}

2.创建 GDBus 代理对象

#define BLUEZ_SERVICE "org.bluez" 
#define ADAPTER_INTERFACE BLUEZ_SERVICE ".Adapter1"
GDBusProxy *adapter_proxy =NULL;
GError *error =NULL; 
// 这里的 hci0 根据实际情况来修改 
adapter_proxy = g_dbus_proxy_new_sync( bus, G_DBUS_PROXY_FLAGS_NONE,NULL, BLUEZ_SERVICE,"/org/bluez/hci0", ADAPTER_INTERFACE,NULL, &error); 
if(!adapter_proxy) 
{ 
    g_error("Failed to create adapter proxy: %s", error->message); 
    g_error_free(error);
    return; 
}

3.调用 GDBus 代理对象的方法

GVariant *result =NULL; 
GError *error =NULL; 
result = g_dbus_proxy_call_sync( adapter_proxy,"StartDiscovery",NULL, G_DBUS_CALL_FLAGS_NONE,-1,NULL, &error); 
if(!result)
{ 
    g_error("Failed to call StartDiscovery: %s", error->message); 
    g_error_free(error); 
    return; 
}

完整示例代码

#include<gio/gio.h> 
#define BLUEZ_SERVICE "org.bluez" 
#define ADAPTER_INTERFACE BLUEZ_SERVICE ".Adapter1" 
int main(intargc,char*argv[])
{ 
    // 创建 GDBus 连接 
    GDBusConnection *bus =NULL;
     GError *error =NULL; 
    bus = g_bus_get_sync(G_BUS_TYPE_SYSTEM,NULL, &error); 
    if(!bus) 
    { 
        g_error("Failed to get system bus: %s", error->message); 
        g_error_free(error); 
        return 1; 
    } 
    // 创建 GDBus 代理对象 
    GDBusProxy *adapter_proxy =NULL; 
    // 这里的 hci0 根据实际情况来修改 
    adapter_proxy = g_dbus_proxy_new_sync( bus, G_DBUS_PROXY_FLAGS_NONE,NULL, BLUEZ_SERVICE,"/org/bluez/hci0", ADAPTER_INTERFACE,NULL, &error); 
    if(!adapter_proxy) 
    { 
        g_error("Failed to create adapter proxy: %s", error->message); g_error_free(error); 
        return 1; 
    } 
    // 调用 GDBus 代理对象的方法 
    GVariant *result =NULL; 
    result = g_dbus_proxy_call_sync( adapter_proxy,"StartDiscovery",NULL, G_DBUS_CALL_FLAGS_NONE,-1,NULL, &error); 
    if(!result) 
    { 
        g_error("Failed to call StartDiscovery: %s", error->message); 
        g_error_free(error); 
        return 1; 
    } 
    g_object_unref(adapter_proxy); 
    g_variant_unref(result); 
    g_dbus_connection_unref(bus);
}

对于GDBus方法,我们需要使用GVariant对象来表示参数和返回值。例如,为了调用org.bluez.Adapter1接口的StartDiscovery方法,我们需要使用以下代码:

GError *error =NULL; 
GVariant *result = g_dbus_connection_call_sync( connection,"org.bluez","/org/bluez/hci0","org.bluez.Adapter1","StartDiscovery",NULL,NULL, G_DBUS_CALL_FLAGS_NONE,-1,NULL, &error);
if(error !=NULL) 
{
 	g_error("Error calling StartDiscovery: %s", error->message); 
} 
g_variant_unref(result);

这里的connection是一个GDBusConnection对象,它是通过g_dbus_connection_new_for_address_sync()函数创建的。注意,我们没有传递任何参数给StartDiscovery方法,因为它不需要任何参数。对于需要传递参数的方法,我们需要构造一个GVariant对象来表示参数。例如,为了调用org.bluez.Device1接口的Pair方法,我们可以使用以下代码:

GError *error =NULL; 
GVariant *params = g_variant_new("(s)","Keyboard"); 
// 参数是字符串"Keyboard" 
GVariant *result = g_dbus_connection_call_sync( connection,"org.bluez","/org/bluez/hci0/dev_00_11_22_33_44_55","org.bluez.Device1","Pair", params,NULL, G_DBUS_CALL_FLAGS_NONE,-1,NULL, &error);
if(error !=NULL) 
{ 
	g_error("Error calling Pair: %s", error->message); 
} 
g_variant_unref(result); 
g_variant_unref(params);
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

【嵌入式Bluetooth应用开发笔记】第一篇:DBUS概述与蓝牙开发小试牛刀 的相关文章

  • Kotlin进阶-7-阻塞队列+线程池

    目录 1 阻塞队列 1 1 常见阻塞场景 2 Java中的阻塞队列 2 1 ArrayBlockingQueue 2 2 LinkedBlockingQueue 2 3 PriorityBlockingQueue 2 4 DelayQueu
  • Kotlin进阶-11-Activity启动后的视图加载分析

    1 介绍 Kotlin进阶 9 setContentView源析 43 Window Activity DecorView关系 Kotlin进阶 10 Activity的启动流程 前面两节分别介绍了Activity的启动流程 xff0c 还
  • Java多线程编程技术总结

    目录 1 退出线程的3种方式 xff1a 1 1 判断线程是否中断 xff1f 1 2 interrupt 1 3 stop 1 4 StackTraceElement getStackTrace 方法 2 suspend 和resume
  • 【Mysql】视图

    Mysql 视图 1 什么是视图 xff08 MySQL 5以及以上版本 xff09 2 视图的优点3 视图的规则与限制4 使用视图附录 1 什么是视图 xff08 MySQL 5以及以上版本 xff09 视图是虚拟的表 与包含数据的表不一
  • AIDL的原理实现

    前言 Binder 驱动是基于 CS 模型设计的跨进程通信驱动 想要使用 Binder 驱动进行通信 需要三个步骤 定义交互规范服务端实现客户端实现 一 定义交互规范 span class token keyword public span
  • 微信小程序9---Button按钮和icon图标

    Button 按钮 首先提醒一下大家 xff0c 如果你现在button标签不能用 xff0c 不用担心 xff0c 那是因为微信小程序存在一个bug xff0c 你仔细看一下你的button标签的代码是不是这样的 span class h
  • 微信小程序10---条件语句if和循环语句for(三目运算+hidden)

    在微信小程序的官方文档中 xff0c 将这两个语句归化在框架的视图层 xff0c 分表叫条件渲染和类表渲染 xff0c 其实他就是封装了这两条语句而已 上图 xff08 循环语句if xff09 1 它是通过在index js中设置数据 x
  • 微信小程序13--通过api接口将json数据展现到小程序上

    实现知乎客户端的一个重要知识前提就是 xff0c 要知道怎么通过知乎新闻的接口 xff0c 来把数据展示到微信小程序端上 那么我们这一就先学习一下 xff0c 如何将接口获取到的数据展示到微信小程序上 1 用到的知识点 lt 1 gt wx
  • 微信小程序15---将组件设置为圆形

    1 上图 2 index wxss 宽高和弧度的设置 span class hljs class circle span span class hljs rules span class hljs rule span class hljs
  • Android---给Linearlayout设置边框+弧度角

    1 第一步需要在drawable下创建一个xml文件 xff0c 代码如下 span class hljs pi lt xml version 61 34 1 0 34 encoding 61 34 utf 8 34 gt span spa
  • HttpServletRequest获取body && 使用 @RequestBody 获取body

    直接从HttpServletRequest的Reader流中获取请求body参数 span class token annotation punctuation 64 RequestMapping span span class token
  • P1002 [NOIP2002 普及组] 过河卒

    题目描述 棋盘上 A 点有一个过河卒 xff0c 需要走到目标 B 点 卒行走的规则 xff1a 可以向下 或者向右 同时在棋盘上 C 点有一个对方的马 xff0c 该马所在的点和所有跳跃一步可达的点称为对方马的控制点 因此称之为 马拦过河
  • 3D Slicer源代码编译与调试

    2019 11 12 参考以下链接 xff1a https www slicer org wiki Documentation Nightly Developers Build Instructions Windows 需要注意的有 xff
  • DSP Group, Inc. <DSPG>

    公司官网 xff1a DSP Group Leading global provider of wireless chipset solutions About DSP Group DSP Group Inc NASDAQ DSPG is
  • 数组或对象指定下标赋值及下拉数据转化

    一 数组指定下标赋值 span class token variable let span span class token variable myarr span span class token punctuation 61 span
  • XMOS 开发探索3-麦克风拾音

    date 2021 03 29 XMOS 评估板型号 xff1a XUF216 512 TQ128 C20 实现麦克风拾音 xff0c 并从耳机输出声音 xff0c 官方网站上的一个demo xff1a Application Notes
  • 安装ubuntu系统中磁盘分区

    硬件 xff1a vostro 1450 xff0c 本身有一块机械硬盘HDD xff08 500G xff09 加了一块固态硬盘SSD xff08 120G xff09 xff0c 组成HDD 43 SSD双硬盘 目的 xff1a SDD
  • win7+ubuntu双系统 -- ubuntu无法启动

    硬件 xff1a vostro 1450 xff0c 本身有一块机械硬盘HDD xff08 500G xff09 加了一块固态硬盘SSD xff08 120G xff09 xff0c 组成HDD 43 SSD双硬盘 目的 xff1a SDD
  • Xshell 和 Xftp 学校免费版

    Xshell 和 Xftp 家庭 学校免费版下载地址 xff1a 家庭 学校免费 NetSarang Website 填写姓名和邮箱 xff0c 下载免费版 xff0c 会发送邮件到邮箱 45M 安装完成并打开 xff1a 39M 用来传输
  • putty pscp psftp 三件套

    官网地址 xff1a https www chiark greenend org uk sgtatham putty https www chiark greenend org uk sgtatham putty latest html p

随机推荐

  • win10远程连接win7电脑 -- 局域网实现

    本次 xff1a 使用 windows自带 的 远程桌面连接 xff08 ok xff09 另外 xff1a 向日葵 远程软件更方便 xff0c 可以直接在公网使用 xff08 get xff09 TeamViewer 远程软件越来越不好用
  • scp 报错: Permission denied, please try again(publickey,password)

    修改密码后导致报错 Permission denied please try again publickey password 修改 etc ssh sshd config 中 PermitRootLogin yes 修改为yes 重启服务
  • 关闭树莓派的电源指示灯和状态指示灯

    在命令行输入一下指令 xff1a echo 0 sudo tee sys class leds led0 brightness 状态指示灯 范围 0 255 echo none sudo tee sys class leds led0 tr
  • 虚拟机安装Vmware Tools复制文件,共享文件

    目标1 xff1a 虚拟机安装Vmware Tools工具 先看下VMware版本 xff1a 首先 xff0c 将CD DVD切换到物理驱动器 xff1a 点击菜单 虚拟机 M 34 安装VMware Tools xff0c 虚拟DVD中
  • xavier上如何挂载SD卡

    參考博客 Jetson AGX Xavier避坑指南 六 挂载 SD 卡 zxxRobot的博客 CSDN博客 xavier挂载sd卡 AGX Xavier挂载SD卡 Bungehurst CSDN博客 Nvidia Jetson AGX
  • NXP-LPC1768起步之开发环境搭建与GPIO

    1 环境搭建 本工程使用ARM公司MDK414 低版本的可能会导致在MDK中无法下载调试程序 仿真器使用SEGGER公司JlinkV7 首先新建工程GPIO xff0c 选择路径保存 xff0c 然后会出现选择芯片界面 然后确定 xff0c
  • docker 修改默认存储路径的方法

    在xavier上使用docker时 由于空间不足 无法继续工程 几种修改 Docker 镜像默认存储位置的方法 墨天轮 使用方法一 使用软链接的方式 容器的存放位置在 var lib docker 默认存放位置 sudo docker in
  • linux下usb无线网卡对比

    2021年12月23日 冬月二十 xff0c 天晴 xff0c 微风 一 使用场景 1 xff0c 由于软件开发需要用到linux系统 xff0c 嵌入式设备nvidia xavier没有无线网卡 xff0c 需要自购 2 xff0c 另外
  • ubuntu 18.04.6官方下载地址

    Enterprise Open Source and Linux Ubuntu 进入界面 xff1a Download Ubuntu Desktop Download Ubuntu 点击 xff1a see our alternative
  • ubuntu误删 /var/lib/dpkg

    折腾了一个小时 Deepin Debian Ubuntu恢复误删除的 var lib dpkg 学亮编程手记的博客 CSDN博客 https jingyan baidu com article fc07f98946cd3e12fee5196
  • E: Could not get lock /var/lib/dpkg/lock

    dpkg error dpkg frontend is locked by another process dpkg error dpkg frontend is locked by another process 白蛇仙人的博客 CSDN
  • 树莓派安装花生壳软件 phddns ,没有显示SN码

    树莓派型号 xff1a Pi4B 2G 树莓派系统版本 xff1a uname a Linux raspberrypi 5 10 103 v7l 43 1529 SMP Tue Mar 8 12 24 00 GMT 2022 armv7l
  • E: Could not get lock /var/lib/dpkg/lock

    ubuntu安装软件时 xff0c 经常出现下面错误 xff1a sudo apt get install E Could not get lock var lib dpkg lock open 11 Resource temporaril
  • shell 脚本常用命令,音频提取、格式转换、切割

    实现一下功能 xff1a 1 xff0c mp4 视频文件提取 wav xff0c pcm xff1b 2 xff0c wav 切割为每段30s 的音频 xff1b 3 xff0c wav 切割后的音频转换为 pcm xff0c ffmpe
  • Apache Options Indexes FollowSymLinks详解

    如果该虚拟目录下没有 index html xff0c 浏览器也会显示该虚拟目录的目录结构 xff0c 列出该虚拟目录下的文件和子目录 如何禁止 Apache 显示目录列表呢 xff1f 要禁止 Apache 显示目录结构列表 xff0c
  • 大恒工业相机+opencv开发经历

    遇到的问题 xff1a 1 打开Daheng Galaxy Viewer x64 没有图像 由于对工业相机不熟悉 xff0c 原因是没有安装镜头 xff0c 安装镜头后可以正常使用 xff0c 否则只有白色或黑色 xff0c 用手指靠近镜头
  • Backtrace in Android

    Backtrace in Android 96 Tsing2015 0 7 2016 02 28 23 03 字数 33 阅读 2491评论 8喜欢 4 libscorkscrew so在android 5 0之后已经没有了 xff0c 之
  • CF1165B Polycarp Training

    原题链接 题目描述 Polycarp wants to train before another programming competition During the first day of his training he should
  • markdownIt大致流程分析

    文章目录 一 xff0c markdownIt模块大致流程二 xff0c 分析其执行流程三 xff0c 关于MarkdownIt实例属性options配置属性validateLink函数normalizeLink函数normalizeLin
  • 【嵌入式Bluetooth应用开发笔记】第一篇:DBUS概述与蓝牙开发小试牛刀

    DBUS概述 DBus xff08 D Bus xff09 是一个在不同程序之间传递消息的系统总线 DBus为不同的程序之间提供了一种通信机制 xff0c 这种通信制可以在不需要知道对方程序的情况下进行通信 DBus可以使用多种编程语言来开