Git Product home page Git Product logo

bluez_inc's Introduction

Bluez in C

The goal of this library is to provide a clean C interface to Bluez, without needing to use DBus commands. Using Bluez over the DBus is quite tricky to say the least, and this library does all the hard work under the hood. As a result, it looks like a 'normal' C library for Bluetooth!

The library focuses on BLE and supports both Central and Peripheral roles. Some interesting facts include:

  • Manage/Connect multiple peripherals at the same time
  • Support for bonding
  • Simplified programming interface for easy coding
  • Configurable logging with extensive debugging possibilities

Dependencies

This library uses GLib 2.0. If needed, you can install it using sudo apt install -y libglib2.0-dev.

Building the code

cmake .
cmake --build .

Discovering devices

In order to discover devices, you first need to get hold of a Bluetooth adapter. You do this by calling binc_adapter_get_default() with your DBus connection as an argument:

int main(void) {
    GDBusConnection *dbusConnection = g_bus_get_sync(G_BUS_TYPE_SYSTEM, NULL, NULL);
    Adapter *default_adapter = binc_adapter_get_default(dbusConnection);
    
    //...
}

The next step is to set any scanfilters and set the callback you want to receive the found devices on:

int main(void) {
    
    // ...
    
    binc_adapter_set_discovery_cb(default_adapter, &on_scan_result);
    binc_adapter_set_discovery_filter(default_adapter, -100, NULL, NULL);
    binc_adapter_start_discovery(default_adapter);
}

When you pass 'NULL' as the 3rd argument to binc_adapter_set_discovery_filter, you indicate that you don't want to filter on service UUIDs. Otherwise you can pass a GPtrArray with a number of service UUIDs that you want to filter on. The 4th argument allows you to filter on a 'pattern' which is defined in Bluez as the 'prefix of an address or name'. The discovery will deliver all found devices on the callback you provided. You typically check if it is the device you are looking for, stop the discovery and then connect to it:

void on_scan_result(Adapter *default_adapter, Device *device) {
    const char* name = binc_device_get_name(device);
    if (name != NULL && g_str_has_prefix(name, "Polar")) {
        binc_adapter_stop_discovery(default_adapter);
        binc_device_set_connection_state_change_cb(device, &on_connection_state_changed);
        binc_device_set_services_resolved_cb(device, &on_services_resolved);
        binc_device_set_read_char_cb(device, &on_read);
        binc_device_set_write_char_cb(device, &on_write);
        binc_device_set_notify_char_cb(device, &on_notify);
        binc_device_connect(device);
    }
}

As you can see, just before connecting, you must set up some callbacks for receiving connection state changes and the results of reading/writing to characteristics.

Connecting, service discovery and disconnecting

You connect by calling binc_device_connect(device). Then the following sequence will happen:

  • first, the connection_state will change to 'connecting'.
  • when the device is connected, the connection_state changes to 'connected' and your registered callback will be called. However, you cannot use the device yet because the service discovery has not yet been done.
  • Bluez will then start the service discovery automatically and when it finishes, the services_resolved callback is called. So the service_resolved callback is the right place to start reading and writing characteristics or starting notifications.
void on_connection_state_changed(Device *device, ConnectionState state, GError *error) {
    if (error != NULL) {
        log_debug(TAG, "(dis)connect failed (error %d: %s)", error->code, error->message);
        return;
    }

    log_debug(TAG, "'%s' (%s) state: %s (%d)", binc_device_get_name(device), binc_device_get_address(device), binc_device_get_connection_state_name(device), state);
    if (state == BINC_DISCONNECTED && binc_device_get_bonding_state(device) != BINC_BONDED) {
        binc_adapter_remove_device(default_adapter, device);
    }
}

If a connection attempt fails or times out after 25 seconds, the connection_state callback is called with an error.

To disconnect a connected device, call binc_device_disconnect(device) and the device will be disconnected. Again, the connection_state callback will be called. If you want to remove the device from the DBus after disconnecting, you call binc_adapter_remove_device(default_adapter, device).

Reading and writing characteristics

We can start using characteristics once the service discovery has been completed. Typically, we read some characteristics like its model and manufacturer. In order to read or write, you first need to get the Characteristic by using binc_device_get_characteristic(device, DIS_SERVICE, DIS_MANUFACTURER_CHAR). You need to provide the service UUID and characteristic UUID to find a characteristic:

void on_services_resolved(Device *device) {
    Characteristic *manufacturer = binc_device_get_characteristic(device, DIS_SERVICE, DIS_MANUFACTURER_CHAR);
    if (manufacturer != NULL) {
        binc_characteristic_read(manufacturer);
    }

    binc_device_read_char(device, DIS_SERVICE, DIS_MODEL_CHAR);
}

As you can see, there is also a convenience method binc_device_read_char that will look up a characteristic and do a read if the characteristic is found. Like all BLE operations, reading and writing are asynchronous operations. So you issue them and they will complete immediately, but you then have to wait for the result to come in on a callback. You register your callback by calling binc_device_set_read_char_cb(device, &on_read).

void on_read(Device *device, Characteristic *characteristic, GByteArray *byteArray, GError *error) {
    const char* uuid = binc_characteristic_get_uuid(characteristic);
    if (error != NULL) {
        log_debug(TAG, "failed to read '%s' (error %d: %s)", uuid, error->code, error->message);
        return;
    }
    
    if (byteArray == NULL) return;
    
    if (g_str_equal(uuid, DIS_MANUFACTURER_CHAR)) {
        log_debug(TAG, "manufacturer = %s", byteArray->data);
    } else if (g_str_equal(uuid, MODEL_CHAR)) {
        log_debug(TAG, "model = %s", byteArray->data);
    }
}

Writing to characteristics works in a similar way. Register your callback using binc_device_set_write_char_cb(device, &on_write). Make sure you check if you can write to the characteristic before attempting it:

void on_services_resolved(Device *device) {

    // ...
    
    Characteristic *current_time = binc_device_get_characteristic(device, CTS_SERVICE, CURRENT_TIME_CHAR);
    if (current_time != NULL) {
        if (binc_characteristic_supports_write(current_time,WITH_RESPONSE)) {
            GByteArray *timeBytes = binc_get_current_time();
            binc_characteristic_write(current_time, timeBytes, WITH_RESPONSE);
            g_byte_array_free(timeBytes, TRUE);
        }
    }
}

Receiving notifications

Bluez treats notifications and indications in the same way, calling them 'notifications'. If you want to receive notifications you have to 'start' them by calling binc_characteristic_start_notify(). As usual, first register your callback by calling binc_device_set_notify_char_cb(device, &on_notify). Here is an example:

void on_services_resolved(Device *device) {

    // ...
    
    Characteristic *temperature = binc_device_get_characteristic(device, HTS_SERVICE_UUID, TEMPERATURE_CHAR);
    if (temperature != NULL) {
        binc_characteristic_start_notify(temperature);
    }
}    

And then receiving your notifications:

void on_notify(Device *device, Characteristic *characteristic, GByteArray *byteArray) {
    const char* uuid = binc_characteristic_get_uuid(characteristic);
    Parser *parser = parser_create(byteArray, LITTLE_ENDIAN);
    parser_set_offset(parser, 1);
    if (g_str_equal(uuid, TEMPERATURE_CHAR)) {
        float temperature = parser_get_float(parser);
        log_debug(TAG, "temperature %.1f", temperature);
    } 
    parser_free(parser);
}

The Parser object is a helper object that will help you parsing byte arrays.

Bonding

Bonding is possible with this library. It supports 'confirmation' bonding (JustWorks) and PIN code bonding (passphrase). First you need to register an Agent and set the callbacks for these 2 types of bonding. When creating the agent you can also choose the IO capabilities for your applications, i.e. DISPLAY_ONLY, DISPLAY_YES_NO, KEYBOARD_ONLY, NO_INPUT_NO_OUTPUT, KEYBOARD_DISPLAY. Note that this will affect the bonding behavior.

int main(void) {

    // ...
        
    agent = binc_agent_create(default_adapter, "/org/bluez/BincAgent", KEYBOARD_DISPLAY);
    binc_agent_set_request_authorization_cb(agent, &on_request_authorization);
    binc_agent_set_request_passkey_cb(agent, &on_request_passkey);
}

When bonding is starting, one of your callbacks will be called and you should handle the request. In the case of 'confirmation' bonding you either accept (TRUE) or reject (FALSE) the request for bonding:

gboolean on_request_authorization(Device *device) {
    log_debug(TAG, "requesting authorization for '%s", binc_device_get_name(device));
    return TRUE;
}

In the case of PIN code pairing you will have to provide a PIN code, which you probably need to ask to the user of your app:

guint32 on_request_passkey(Device *device) {
    guint32 pass = 000000;
    log_debug(TAG, "requesting passkey for '%s", binc_device_get_name(device));
    log_debug(TAG, "Enter 6 digit pin code: ");
    fscanf(stdin, "%d", &pass);
    return pass;
}

Note that this type of bonding requires a 6 digit pin code!

If you want to initiate bonding yourself, you can call binc_device_pair(). The same callbacks will be called for dealing with authorization or PIN codes.

Creating your own peripheral

It is also possible with BINC to create your own peripheral, i.e. start advertising and implementing some services and characteristics.

Advertising

In Bluez an advertisement is an object on the DBus. By using some basic calls BINC will create this object for you and set the right values. After that, you need to tell the default_adapter to start advertising.

// Build array with services to advertise
GPtrArray *adv_service_uuids = g_ptr_array_new();
g_ptr_array_add(adv_service_uuids, HTS_SERVICE_UUID);
g_ptr_array_add(adv_service_uuids, BLP_SERVICE_UUID);

// Create the advertisement
advertisement = binc_advertisement_create();
binc_advertisement_set_local_name(advertisement, "BINC2");
binc_advertisement_set_services(advertisement, adv_service_uuids);
        
// Start advertising
binc_adapter_start_advertising(default_adapter, advertisement);
g_ptr_array_free(adv_service_uuids, TRUE);

The library also supports setting manufacturer data and service data.

Adding services and characteristics

In order to make your peripheral work you need to create an 'app' in Bluez terminology. The steps are:

  • Create an app
  • Add one or more services
  • Add one or more characteristics and descriptors
  • Implement read/write/notify callbacks for characteristics

Here is how to setup an app with a service and a characteristic:

// Create an app with a service
app = binc_create_application(default_adapter);
binc_application_add_service(app, HTS_SERVICE_UUID);
binc_application_add_characteristic(
                app,
                HTS_SERVICE_UUID,
                TEMPERATURE_CHAR_UUID,
                GATT_CHR_PROP_READ | GATT_CHR_PROP_INDICATE | GATT_CHR_PROP_WRITE);
                
// Set the callbacks for read/write
binc_application_set_char_read_cb(app, &on_local_char_read);
binc_application_set_char_write_cb(app, &on_local_char_write);

// Register your app
binc_adapter_register_application(default_adapter, app);

There are callbacks to be implemented where you can update the value of a characteristic just before the read/write is done. If you accept the read, return NULL, otherwise return an error.

char* on_local_char_read(const Application *app, const char *address, const char* service_uuid, const char* char_uuid) {
    if (g_str_equal(service_uuid, HTS_SERVICE_UUID) && g_str_equal(char_uuid, TEMPERATURE_CHAR_UUID)) {
        const guint8 bytes[] = {0x06, 0x6f, 0x01, 0x00, 0xff, 0xe6, 0x07, 0x03, 0x03, 0x10, 0x04, 0x00, 0x01};
        GByteArray *byteArray = g_byte_array_sized_new(sizeof(bytes));
        g_byte_array_append(byteArray, bytes, sizeof(bytes));
        binc_application_set_char_value(app, service_uuid, char_uuid, byteArray);
        return NULL;
    }
    return BLUEZ_ERROR_REJECTED;
}

char* on_local_char_write(const Application *app, const char *address, const char *service_uuid,
                          const char *char_uuid, GByteArray *byteArray) {
    // Reject all writes
    return BLUEZ_ERROR_REJECTED;
}

In order to notify you can use:

void binc_application_notify(const Application *app, const char *service_uuid, const char *char_uuid,
                             GByteArray *byteArray);

Examples

The repository includes an example for both the Central and Peripheral role.

  • The central example scans for thermometers and reads the thermometer value once it connects.
  • The peripheral example acts as a thermometer and can be used in combination with the central example.

Logging

The library contains its own logger that you can also use in your application.

  • Turn logging on/off: log_enabled(TRUE)
  • Set logging level: log_set_level(LOG_DEBUG)
  • Log to a file using log rotation: log_set_filename("mylog.log", 65536, 10)
  • Log something: log_debug("MyTag", "Hello %s", "world")

Bluez documentation

The official Bluez documentation is a bit sparse but can be found here:

You will notice that most original methods and properties are available in this library. In some cases, some adaptations have been done for convenience.

Installing on Raspberry Pi

Assuming you have a default installation, you will need to install CMake and GLib:

  • sudo apt install -y cmake
  • sudo apt install -y libglib2.0-dev

bluez_inc's People

Contributors

bojennett avatar bp1001 avatar charlesnicholson avatar lizziemac avatar morganbengtsson avatar weliem avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

bluez_inc's Issues

Reconnect to a previous device causes strange behavior

As a central, I'm seeing a weird behavior, as if either binc (or more likely bluetoothctl) is "remembering" something about a previous device.

A device will disconnect, but the scanner is still running, and so as soon as the device comes back into range, it will connect again. However, when this happens, before I've done any configuring of the device, I will get a few notifications (two from the battery, two from one of my custom characteristics). I can see from btmon that these get produced from bluetooth, but I also see that these die NOT come from my device. Separate logging on my device shows no activity there.

Then, when I go to configure my newly connected device, I go to turn on the battery level characteristic to send notifications, but (1) the library is telling me this characteristic already has notifications enabled (binc_characteristic_is_notifying saying true), and then when I try to enable it anyway via binc_characteristic_start_notify, the command goes out, but there is no log saying it returned (no notifying true <uuid>) probably because it's already true so it didn't "change"?

How to get the example applications to work?

Hi weliem,

I ran your example BLE applications on my ubuntu 22.04.3 pc which has bluetooth 5.2 hardware support and a debian 11 beagle-bone-black with a bluetooth adapter which supports bluetooth 5.0.

When I ran the peripheral executable on the bealge-bone-black, I got this error message:
DEBUG [Adapter] failed to register application (error 36: GDBus.Error:org.bluez.Error.Failed: No valid service object found).

However, on the Ubuntu PC, the peripheral application works fine. I can discover and connect to the device and retrieve the characteristic data using a generic BLE discovery software with GUI (LightBlue). In this setup, the central executable running on the beagle-bone-black doesn't connect to the Ubuntu PC's BLE service and doesn't retrieve the "hello there".

So my question is whether you can name some hardware and/or software requirements for using bluz_inc as the example works on the ubuntu pc but not on the debian begle-bone-black?

Thanks in advance!

Appendix - all log for error message
debian@BeagleBone:~/bluez_inc/examples/peripheral$ ./peripheral
2024-02-14 10:55:42:978 DEBUG [Adapter] finding adapters
2024-02-14 10:55:42:988 DEBUG [Adapter] found 1 adapter
2024-02-14 10:55:42:989 DEBUG [Main] using default_adapter '/org/bluez/hci0'
2024-02-14 10:55:42:993 DEBUG [Application] successfully published application
2024-02-14 10:55:42:998 DEBUG [Application] successfully published local service 00001809-0000-1000-8000-00805f9b34fb
2024-02-14 10:55:43:001 DEBUG [Application] successfully published local characteristic 00002a1c-0000-1000-8000-00805f9b34fb
2024-02-14 10:55:43:010 DEBUG [Application] successfully published local descriptor 00002901-0000-1000-8000-00805f9b34fb
2024-02-14 10:55:43:016 DEBUG [Application] set value <68656c6c6f20746865726500> to <00002901-0000-1000-8000-00805f9b34fb>
2024-02-14 10:55:43:034 DEBUG [Application] adding /org/bluez/bincapplication/service0
2024-02-14 10:55:43:038 DEBUG [Application] adding /org/bluez/bincapplication/service0/char0
2024-02-14 10:55:43:040 DEBUG [Application] adding /org/bluez/bincapplication/service0/char0/desc0
2024-02-14 10:55:43:050 DEBUG [Adapter] started advertising (E8:48:B8:C8:40:00)
2024-02-14 10:55:43:063 DEBUG [Adapter] failed to register application (error 36: GDBus.Error:org.bluez.Error.Failed: No valid service object found)
2024-02-14 11:05:43:105 DEBUG [Application] freeing application /org/bluez/bincapplication
2024-02-14 11:05:43:110 DEBUG [Application] freeing service /org/bluez/bincapplication/service0
2024-02-14 11:05:43:111 DEBUG [Application] freeing characteristic /org/bluez/bincapplication/service0/char0
2024-02-14 11:05:43:111 DEBUG [Application] freeing descriptor /org/bluez/bincapplication/service0/char0/desc0

Request for Humidity & Temperature sensor detail.

Martijn:

Thank you for the bluetooth library bluez_inc and the central/peripheral examples.

I am trying it with Sensirion SHT31 sensor with the primaries and charesteristics/descriptors information using gatttool output shown below. I'm not sure if I am using the UUIDs and CUD correctly. Below are my replacements $defines derived using gatttool:
Greatly appreciate your help to correct my mappings. Alternatively, I should try it with the same sensor you used.

Deriving the #defines for central/main.c replacing the original #defines:
#define HTS_SERVICE_UUID "00001809-0000-1000-8000-00805f9b34fb"
#define TEMPERATURE_CHAR_UUID "00002a1c-0000-1000-8000-00805f9b34fb"

#define DIS_SERVICE "0000180a-0000-1000-8000-00805f9b34fb"
#define DIS_MANUFACTURER_CHAR "00002a29-0000-1000-8000-00805f9b34fb"
#define DIS_MODEL_CHAR "00002a24-0000-1000-8000-00805f9b34fb"
#define CUD_CHAR "00002901-0000-1000-8000-00805f9b34fb"

WITH the following:
#define SHT31_UUID "88307D2F-6E54-3962-D2E5-42C8803180A5"
#define GAP "00001800-0000-1000-8000-00805f9b34fb"
#define GATT "00001801-0000-1000-8000-00805f9b34fb"
#define DIS_SERVICE "0000180a-0000-1000-8000-00805f9b34fb"
#define BATTERY_SERVICE "0000180f-0000-1000-8000-00805f9b34fb"
#define SHT31_MGT_SERVICE_UUID "0000f234-b38d-4985-720e-0f993a68ee41"
#define SHT31_HUMI_SERVICE_UUID "00001234-b38d-4985-720e-0f993a68ee41"
#define SHT31_TEMP_SERVICE_UUID "00002234-b38d-4985-720e-0f993a68ee41"

#define BATTERY_LEVEL "00002a19-0000-1000-8000-00805f9b34fb"
#define DIS_MODEL_CHAR "00002234-b38d-4985-720e-0f993a68ee41"
#define CUD_CHAR "0000f234-b38d-4985-720e-0f993a68ee41"

/* To read Temperature in Degree C (use uuid 00002235). /
#define TEMPERATURE_CHAR_UUID "00002235-b38d-4985-720e-0f993a68ee41"
/
To read Relative Humidity in %RH (use uuid 00001235). */
#define HUMIDITY_CHAR_UUID "00001235-b38d-4985-720e-0f993a68ee41"

#define DIS_MANUFACTURER_CHAR "00002a29-0000-1000-8000-00805f9b34fb"
/* #define DIS_MODEL_CHAR "00002a24-0000-1000-8000-00805f9b34fb" /
/
#define CUD_CHAR "00002901-0000-1000-8000-00805f9b34fb" */

-----X---------------
$ gatttool -t random -b CB:D9:A4:93:23:FB -I
[CB:D9:A4:93:23:FB][LE]> connect
Attempting to connect to CB:D9:A4:93:23:FB
Connection successful
[CB:D9:A4:93:23:FB][LE]> primary
attr handle: 0x0030, end grp handle: 0x0034 uuid: 00001234-b38d-4985-720e-0f993a68ee41
attr handle: 0x0035, end grp handle: 0xffff uuid: 00002234-b38d-4985-720e-0f993a68ee41

attr handle: 0x0001, end grp handle: 0x0007 uuid: 00001800-0000-1000-8000-00805f9b34fb #GAP
attr handle: 0x0008, end grp handle: 0x000b uuid: 00001801-0000-1000-8000-00805f9b34fb #GATT
attr handle: 0x000c, end grp handle: 0x001a uuid: 0000180a-0000-1000-8000-00805f9b34fb Device Info
attr handle: 0x001b, end grp handle: 0x001e uuid: 0000180f-0000-1000-8000-00805f9b34fb Battery Service
attr handle: 0x001f, end grp handle: 0x002f uuid: 0000f234-b38d-4985-720e-0f993a68ee41 To access Sync Time MS (0xf235 to 0xf239 char properties)
attr handle: 0x0030, end grp handle: 0x0034 uuid: 00001234-b38d-4985-720e-0f993a68ee41 To read Relative Humidity in %RH (use uuid 00001235).
attr handle: 0x0035, end grp handle: 0xffff uuid: 00002234-b38d-4985-720e-0f993a68ee41 To read Temperature in Degree C (use uuid 00001235).

To write Sync Time MS (use uuid 0000f235) Notify

[CB:D9:A4:93:23:FB][LE]> characteristics 0x0001 0x0035
handle: 0x0002, char properties: 0x0a, char value handle: 0x0003, uuid: 00002a00-0000-1000-8000-00805f9b34fb
handle: 0x0004, char properties: 0x02, char value handle: 0x0005, uuid: 00002a01-0000-1000-8000-00805f9b34fb
handle: 0x0006, char properties: 0x02, char value handle: 0x0007, uuid: 00002a04-0000-1000-8000-00805f9b34fb
handle: 0x0009, char properties: 0x20, char value handle: 0x000a, uuid: 00002a05-0000-1000-8000-00805f9b34fb
handle: 0x000d, char properties: 0x02, char value handle: 0x000e, uuid: 00002a23-0000-1000-8000-00805f9b34fb System ID
handle: 0x000f, char properties: 0x02, char value handle: 0x0010, uuid: 00002a29-0000-1000-8000-00805f9b34fb Mfg Name Str
handle: 0x0011, char properties: 0x02, char value handle: 0x0012, uuid: 00002a24-0000-1000-8000-00805f9b34fb Model Number Str
handle: 0x0013, char properties: 0x02, char value handle: 0x0014, uuid: 00002a25-0000-1000-8000-00805f9b34f Serial Number Str
handle: 0x0015, char properties: 0x02, char value handle: 0x0016, uuid: 00002a27-0000-1000-8000-00805f9b34fb HW Rev Str
handle: 0x0017, char properties: 0x02, char value handle: 0x0018, uuid: 00002a26-0000-1000-8000-00805f9b34fb FW Rev Str
handle: 0x0019, char properties: 0x02, char value handle: 0x001a, uuid: 00002a28-0000-1000-8000-00805f9b34fb SW Rev Str
handle: 0x001c, char properties: 0x12, char value handle: 0x001d, uuid: 00002a19-0000-1000-8000-00805f9b34fb Battery Level Read Notify
handle: 0x0020, char properties: 0x08, char value handle: 0x0021, uuid: 0000f235-b38d-4985-720e-0f993a68ee41 Sync Time MS
handle: 0x0023, char properties: 0x0a, char value handle: 0x0024, uuid: 0000f236-b38d-4985-720e-0f993a68ee41 Oldest Sample Time MS Read
handle: 0x0026, char properties: 0x0a, char value handle: 0x0027, uuid: 0000f237-b38d-4985-720e-0f993a68ee41 Newest Sample Time MS Read
handle: 0x0029, char properties: 0x18, char value handle: 0x002a, uuid: 0000f238-b38d-4985-720e-0f993a68ee41 Start Logger Download Write Notify
handle: 0x002d, char properties: 0x0a, char value handle: 0x002e, uuid: 0000f239-b38d-4985-720e-0f993a68ee41 Logger Interval MS Read Write
handle: 0x0031, char properties: 0x12, char value handle: 0x0032, uuid: 00001235-b38d-4985-720e-0f993a68ee41 To read Relative Humidity in %RH (use uuid 00001235).
handle: 0x0036, char properties: 0x12, char value handle: 0x0037, uuid: 00002235-b38d-4985-720e-0f993a68ee41 To read Temperature in Degree C (use uuid 00002235).
-----X---------------

cannot register new advertisement

Hi,
Thank you for your work on this library, it helps me a lot.

However, when I try to run peripheral so that I could establish a ble connection, I always got an error like "DEBUG [Adapter] failed to register advertisement (error 36: GDBus.Error:org.bluez.Error.NotPermitted: Maximum advertisements reached)" caused by binc_adapter_start_advertising. What may cause this error?

2023-05-08 10:34:34:484 DEBUG [Adapter] finding adapters
2023-05-08 10:34:34:492 DEBUG [Adapter] fuc:binc_adapter_find_all line:699
2023-05-08 10:34:34:495 DEBUG [Adapter] adapter->discoverable 1
2023-05-08 10:34:34:496 DEBUG [Adapter] found device /org/bluez/hci0/dev_F4_87_C5_72_C8_12 'HUAWEI MatePad'
2023-05-08 10:34:34:496 DEBUG [Adapter] found 1 adapter
2023-05-08 10:34:34:497 DEBUG [Main] using default_adapter '/org/bluez/hci0'
Setup remote central connection state callback

2023-05-08 10:34:34:499 DEBUG [Application] successfully published application
2023-05-08 10:34:34:501 DEBUG [Application] successfully published local service 00001809-0000-1000-8000-00805f9b34fb
2023-05-08 10:34:34:502 DEBUG [Application] successfully published local characteristic 00002a1c-0000-1000-8000-00805f9b34fb
2023-05-08 10:34:34:503 DEBUG [Application] successfully published local descriptor 00002901-0000-1000-8000-00805f9b34fb
2023-05-08 10:34:34:503 DEBUG [Application] set value <68656c6c6f20746865726500> to <00002901-0000-1000-8000-00805f9b34fb>

2023-05-08 10:34:34:510 DEBUG [Adapter] failed to register advertisement (error 36: GDBus.Error:org.bluez.Error.NotPermitted: Maximum advertisements reached)
2023-05-08 10:34:34:515 DEBUG [Application] adding /org/bluez/bincapplication/service0
2023-05-08 10:34:34:515 DEBUG [Application] adding /org/bluez/bincapplication/service0/char0
2023-05-08 10:34:34:516 DEBUG [Application] adding /org/bluez/bincapplication/service0/char0/desc0
2023-05-08 10:34:34:525 DEBUG [Adapter] successfully registered application

Could you have a look over this?

Thanks!

GDBUS error

example/central.c produced

2023-10-02 19:20:30:669 DEBUG [Adapter] failed to call adapter method (error 36: GDBus.Error:org.bluez.Error.InvalidArguments: Invalid arguments in method call)
2023-10-02 19:20:30:787 DEBUG [Main] discovery 'started' (/org/bluez/hci0)

(process:23652): GLib-CRITICAL **: 19:20:30.962: g_str_has_prefix: assertion 'str != NULL' failed
^C2023-10-02 19:20:33:587 ERROR [Main] received SIGINT

GitHub CI

I'm happy to set up an Ubuntu CI job if you're interested. That way I'll have slightly more confidence as I make changes that I'm not breaking bluez_inc :)

Does it support classic Bluetooth?

Does this library support the function of classic Bluetooth? My device has a Bluetooth module. I need to use it as a BLE device to configure parameters, and as a classic Bluetooth to connect to the mobile phone to play music.

Issue with scanning after turning bluetooth of and then back on

I have an app that acts as central. When it starts up, it checks the bluetooth state, and if it's off, turns it on and then once on, starts scanning. I have scanning running continuously. For example, a device may get connected, but the scanner is still running such that if the device disconnects, it can be found again and reconnect. As all this happens, I see as I'm starting scanning that I am getting the 'starting' and 'started' states from AdapterDiscoveryStateChangeCallback as expected.

if while this app is running, I go through another method to turn bluetooth off, I get the AdapterPoweredStateChangeCallback as expected with the state off. I try to use this to turn scanning off, but since the adapter is not powered, this calls fails (not surprising).

If I then go into this external method of toggling bluetooth, and I toggle it back on, I get the AdapterPoweredStateChangeCallback as expected saying the state is ON, and then I try to start scanning again.

However, it never seems to actually start scanning again. I don't get any of the AdapterDiscoveryStateChangeCallback anymore. I tried turning scanning off once I see the power state to on, and then turning scanning on, but it doesn't seem to work. It's as if I cannot scan anymore.

`on_char_start_notify` is not being called when using `GATT_CHR_PROP_ENCRYPT_NOTIFY`.

Hello Martijn,

I am trying to setup encrypted notifications using the GATT_CHR_PROP_ENCRYPT_NOTIFY characteristic property in my application, but it appears that when using the encrypted notify characteristic, notifications do not get enabled. When I just use GATT_CHR_PROP_NOTIFY, notifications are enabled and work totally fine. Within my code I am calling the following to setup notifications and register my callbacks for start and stop notify:

Agent *agent = binc_agent_create(default_adapter, BLUEZ_AGENT_PATH, NO_INPUT_NO_OUTPUT);
// ...
binc_application_add_characteristic(app, SERVICE_UUID, CHAR_UUID,
         (GATT_CHR_PROP_NOTIFY | GATT_CHR_PROP_ENCRYPT_NOTIFY) |
         (GATT_CHR_PROP_READ | GATT_CHR_PROP_ENCRYPT_READ));
binc_application_set_char_start_notify_cb(app, &on_local_char_start_notify);
binc_application_set_char_stop_notify_cb(app, &on_local_char_stop_notify);

The encrypted read works fine and output is expected, but notifications are not being enabled.

In the debug logs I would expect to see the following as when using just GATT_CHR_PROP_NOTIFY:

DEBUG [Application] read <UUID>
DEBUG [Application] start notify <UUID>

But instead with GATT_CHR_PROP_ENCRYPT_NOTIFY I just see:

DEBUG [Application] read <UUID>

I would also expect binc_application_char_is_notifying(app, SERVICE_UUID, CHAR_UUID) to return TRUE like it does when using unencrypted notifications, but it is returning FALSE when using encrypted notifications.

I have tested this on two separate devices, iOS and macos, and saw the same results.

Any help you can provide would be appreciated! Not sure if there is a step I am missing or if perhaps there is a bug here.

Thanks!
Kyle

multiple same services

hi i want to create a hardware rev and software rev character to 'device info services', part of Bluetooth Base UUID (xxxxxxxx-0000-1000-8000-00805F9B34FB). i could advertise service with success, unfortunately there is multiple "Device Information" services on advertisement. I assume device already create a service based on "Device Information UUID" with "PnP ID" character.

According to google this Pnp id;" This interface represents the Pn PID GATT Characteristic with uuid 2A50. It is referenced by the DeviceInformation service."

So the problem is, due to double "DEVICE INFO" services characteristic not found on central side. App found "Device Service" based on UUID unfortunately this service contains only PnP ID character.

I could not find a solution to add hardware rev and software rev char to single Device Information Service, and i need help.
Thank you.

scope enums with `BINC_`

bluez_inc provides enums in its public header files that have very generic and common names like CONNECTED, STOPPED, STARTED, etc.

Consider renaming them to BINC_CONNECTED, BINC_STOPPED, BINC_STARTED, etc.. so they don't collide with the application-owned "global" naming space.

(If you're interested, I could PR this)

Register multiple adveritisements

Hi, I want to say thank you for this great project first. I was able to run my peripheral and central and let them communicate.

Currently my goal is to let my peripheral connect with several centrals at the same time, so I want to register more than one advertisements for the peripheral. But I get
registering advertisement failed: An object is already exported for the interface org.bluez.LEAdvertisement1 at /org/bluez/bincadvertisement
when I try to register the second one, and I assume that's because the path of the advertisement is hardcoded in the binc_advertisement_create function:

advertisement->path = g_strdup("/org/bluez/bincadvertisement");

Would it be possible to add an optional parameter 'path' for this function or add a set_path function in order to enable several advertisements?

Concern about peripheral example.

Hi! First I want to thank you for this porject. I was trying to implement myself the interfaces to build a GATT application and then one post in stackoverflow mentioned your project and I was so relief.

It's not an issue at all, its more a question about how bluez works internally. I compiled the peripheral example to test the application connecting an Android device to the server. I noticed that always a pair request show up and after accept it, the central disconnects. I used wireshark as sniffer to see the packets between my computer and the android device and I notice the localhost asks for a pairing request.

I made some research and I found that linux tries to read the battery characteristic on IOs device and for this operation it is needed the pairing process. I though that maybe in Android devices the same problem exists, so I run bluez without the battery plugin, but the problem is still there.

So, I want to ask you if you ever faced a problem like this and if there is something I am missing in the GATT application or bluez configuration to avoid this pairing. I also noticed that the local host is trying to read some generic characteristics but I don't think that would be the problem. I appreciate if you share with me some knowledge or hints about why is this pairing requested. By the way, I'm using ubuntu.

Thanks again for this project and for taking the time to read this issue!.

Handling RSSI threshold for device pair requests in peripheral mode

I have a device, acting as a Bluetooth peripheral, that I would like to have reject pairing requests from central devices that are not within a certain RSSI range. Specifically, I want to block devices that have a weak signal strength from pairing, ensuring that only devices in close proximity can establish a connection.

Here's the approach I've attempted in my code:

gboolean on_request_authorization(Device *device) {
  // Check that the RSSI is strong enough. We don't want to connect to a device
  // that isn't right next to us (for security reasons)
  short rssi = binc_device_get_rssi(device);
  const char *device_name = binc_device_get_name(device);
  if (rssi < BLUETOOTH_RSSI_THRESHOLD) {
    LOGI("rejecting authorization for '%s' because of weak signal. RSSI: %d",
         device_name, rssi);
    return FALSE;
  }

  LOGI("granting authorization for '%s'", device_name);
  return TRUE;
}

However, the rssi value seems to default to -255, leading to all requests being rejected, regardless of their actual signal strength. I'm thinking that the peripheral might not be able to obtain the RSSI value of the central device before pairing, which would be the root cause of the issue. Is my current understanding correct, or is there a different method to achieve this functionality?

Thank you in advance for your insights and help!

Correct approach for reading data that has a size greater than the MTU?

Howdy!
I have the following characteristic:

    binc_application_add_characteristic(app, SERVICE_UUID,
                                        BIG_DATA_CHAR_UUID,
                                        GATT_CHR_PROP_READ);

Where BIG_DATA_CHAR_UUID should returns data greater in length than the MTU. Unsurprisingly, this truncates when sent as one value; would the correct approach be to use the GATT_CHR_PROP_NOTIFY to send over to the central device?

I tried "chunking" the data and then making multiple calls to binc_application_set_char_value in my on_local_char_read callback, but only the last chunk wins.

If using notifications is the standard/expected behavior, do the notifies happen in the read callback? I'm pretty new to BLE so I appreciate any feedback.

Small disclaimer - the "big data" is about 750 bytes worth of data.

Thanks!

Advertising, scan and connection parameters

Hi! In order to make our application faster and improve the user experience, I'd like to change the default advertising, scan and connection parameters (mainly I'd like to change advertising interval and connection interval) but I'm not able to find the functions in the library to do so.
Are there functions currently in the library to set these paremeters? If not, do you have plans to add this feature in a near future?
Thanks a lot

Set PIN for peripheral

I didn't see anything in the code or documentation, but it's possible I might have missed something - does this library support setting a PIN for peripheral devices yet? If so, does it have to be 6 digits, or can it be 4?

How to rename bluetooth

Hi! First I want to thank you for this amazing library. I use the peripheral example in my embedded system. I insert the main function in my project. it works fine with Android or Iphone.

I can change my bluetooth name by dbus-send command, but i can't scan the new name by Android or Iphone, at the same time, i can connect it with old name

I try it

binc_adapter_stop_advertising
binc_advertisement_set_local_name
binc_adapter_start_advertising

but it has some errors

So how can I rename it with bluez_inc and it can update the new name when i scan!

Problems with notification

I am trying to use this library to implement a Central for the Nordic Uart GATT on a RPi4B 64bit, Bullseye, Bluez 5.70.
I am using the Central-example as the starting point and the following uuid:

#define NUS_CHARACTERISTIC_TX_UUID	"6e400002-b5a3-f393-e0a9-e50e24dcca9e"
#define NUS_CHARACTERISTIC_RX_UUID	"6e400003-b5a3-f393-e0a9-e50e24dcca9e"
#define NORDIC_UART_SERVICE         "6e400001-b5a3-f393-e0a9-e50e24dcca9e"

The Central is very simple: The RX has a notification only, and the TX a write:

2023-10-29 13:13:28:562 DEBUG [Device] Characteristic{uuid='6e400003-b5a3-f393-e0a9-e50e24dcca9e', flags='[notify]', properties=16, service_uuid='6e400001-b5a3-f393-e0a9-e50e24dcca9e, mtu=247'}
2023-10-29 13:13:28:562 DEBUG [Device] Descriptor{uuid='00002902-0000-1000-8000-00805f9b34fb', flags='[]', properties=0, char_uuid='6e400003-b5a3-f393-e0a9-e50e24dcca9e'}
2023-10-29 13:13:28:563 DEBUG [Device] Characteristic{uuid='6e400002-b5a3-f393-e0a9-e50e24dcca9e', flags='[write-without-response, write]', properties=12, service_uuid='6e400001-b5a3-f393-e0a9-e50e24dcca9e, mtu=247'}

The adapter code in main (restricted to this RPi only):

        binc_adapter_set_discovery_cb(default_adapter, &on_scan_result);
        binc_adapter_set_discovery_state_cb(default_adapter, &on_discovery_state_changed);
        binc_adapter_set_discovery_filter(default_adapter, -100, service_uuids, "RPi BLE Central");
        g_ptr_array_free(service_uuids, TRUE);
        binc_adapter_start_discovery(default_adapter);
void on_services_resolved(Device *device) {
    log_debug(TAG, "'%s' services resolved", binc_device_get_name(device));

    binc_device_read_char(   device, NORDIC_UART_SERVICE, NUS_CHARACTERISTIC_TX_UUID);
    binc_device_read_char(   device, NORDIC_UART_SERVICE, NUS_CHARACTERISTIC_RX_UUID);
    binc_device_start_notify(device, NORDIC_UART_SERVICE, NUS_CHARACTERISTIC_RX_UUID);   
}
void on_notify(Characteristic *characteristic, const GByteArray *byteArray) {
    const char *uuid = binc_characteristic_get_uuid(characteristic);

    if (g_str_equal(uuid, NUS_CHARACTERISTIC_RX_UUID)) {

        Parser *parser         = parser_create(byteArray, LITTLE_ENDIAN);
        GString *parsed_string = parser_get_string(parser);
        log_debug(TAG, "rxStr: %s", parsed_string->str);

The data sent from the Peripheral is 88 bytes binary. However, it seems like the data does not reach the notification callback.
The log_debug in characteristc.c does indeed print out a 176 bytes long hex string (2x88 bytes), but it seems like it is not accessible in the callback.
I have located the problem to be in the binc_internal_signal_characteristic_changed(...) in characteristc.c, handling the CHARACTERISTIC_PROPERTY_VALUE

    g_assert(g_str_equal(g_variant_get_type_string(parameters), "(sa{sv}as)"));
    g_variant_get(parameters, "(&sa{sv}as)", &iface, &properties, &unknown);
    while (g_variant_iter_loop(properties, "{&sv}", &property_name, &property_value)) {
        if (g_str_equal(property_name, CHARACTERISTIC_PROPERTY_NOTIFYING)) {
            characteristic->notifying = g_variant_get_boolean(property_value);
            log_debug(TAG, "notifying %s <%s>", characteristic->notifying ? "true" : "false", characteristic->uuid);

            if (characteristic->notify_state_callback != NULL) {
                characteristic->notify_state_callback(characteristic, NULL);
            }

            if (characteristic->notifying == FALSE) {
                if (characteristic->characteristic_prop_changed != 0) {
                    g_dbus_connection_signal_unsubscribe(characteristic->connection,
                                                         characteristic->characteristic_prop_changed);
                    characteristic->characteristic_prop_changed = 0;
                }
            }
        } else if (g_str_equal(property_name, CHARACTERISTIC_PROPERTY_VALUE)) {
            GByteArray *byteArray = g_variant_get_byte_array(property_value);
            GString *result = g_byte_array_as_hex(byteArray);
            log_debug(TAG, "len: <%i> of notification <%s> on <%s> ", strlen(result->str)/2, result->str, characteristic->uuid);
            g_string_free(result, TRUE);

            if (characteristic->on_notify_callback != NULL) {
                characteristic->on_notify_callback(characteristic, byteArray);
            }
            g_byte_array_free(byteArray, FALSE);
        }
    }
    

The log debug in characteristc.c, handling the CHARACTERISTIC_PROPERTY_VALUE gives this output:

2023-10-29 13:13:33:905 DEBUG [Characteristic] len: <88> of notification <b694040ad1d3000000f89f191c000000758b5a7b8b010000eaff0000000005000300000000000000fdff0000000000000500000000000000d0ff000000001700fcff0000000000000e26ea206381020019fc140000000000> on <6e400003-b5a3-f393-e0a9-e50e24dcca9e> 

The log debug in the notification callback:

2023-10-29 13:13:33:905 DEBUG [Main] rxStr: ��

I have a feeling my issues is related to that my Peripheral transmits binary data, which might is a problem for the glib g_variant_... library?

Another thing is that the 176 bytes in log_info in chracteristic.c are always the same, which might indicate another problem?

GLib-version used:

GNU C Library (Debian GLIBC 2.31-13+rpt2+rpi1+deb11u7) stable release version 2.31.
Copyright (C) 2020 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.
There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE.
Compiled by GNU CC version 10.2.1 20210110.
libc ABIs: UNIQUE ABSOLUTE
For bug reporting instructions, please see:
<http://www.debian.org/Bugs/>.

Raspberry Pi - Service Discovery Slow / Disconnect

I'm really excited about this project. I just compiled it on a RPI4 running Raspbian, and am trying to connect to my custom device, which has 6 services (device information, battery, heart rate, and 3 custom services). The device information service has several characteristics (obviously), and each of the other services has 1 or at most 2.

On the RPI4 it is taking anywhere from 29 - 35 seconds after the connection for me to see the logs that the services were discovered, and within 2 seconds of that, the device is disconnected. My device doesn't initiate the connection as far as I can tell - there is no logic to auto-disconnect. But it was surprising how long it took and then to immediately disconnect.

I also installed this on an older Linux laptop running Ubuntu, and the device connected, but it never found any services - the callback returned with "found 0 services", again taking about 30 seconds, and then disconnected.

Not sure what I can do here to help show the issue or debug it?

User data on all top-level `bluez_inc` types?

Thanks for bluez_inc, it's so much easier to work with than bluez / dbus :)

A lot of the bluez_inc callbacks just give you an Adapter * or a Device *, but in a system that manages more than one adapter / device, it's hard to know which one is which. So, I need to end up writing some globally-accessible thread-safe data structure that I can pass these pointers to in exchange for some of my application context.

If there were functions like

void binc_adapter_set_user_data(Adapter *adapter, void *user_data);
void *binc_adapter_get_user_data(Adapter *adapter);

and

void binc_device_set_user_data(Device *device, void *user_data);
void *binc_device_get_user_data(Device *device);

Then it would be trivial to load up the bluez_inc objects with a pointer to my application context, and retrieve it in the various bluez_inc callbacks.

Add Device into characteristic callbacks?

On iOS, the peripheral delegate callbacks (didUpdateNotificationStateFor, didUpdateValueFor, didWriteValueFor, didUpdateValueFor) contain the peripheral object. Same for Android on the BluetoothGattCallback (onCharacteristicChanged, onDescriptorWrite, onCharacteristicWrite, onCharacteristicRead).

It's nice having this device object in the callbacks, that way you don't have to keep your own reference around to it. As an example, after getting something back that you read, or receiving a specific notification status, you might want to disconnect because you have everything you need. Having the device object right there in the callback makes that easy as opposed to having to hold it yourself in your code and then free it after. I agree that you can have your own callbacks for each device when you set them on connection so you can "know" who is who, but you still need to hold onto that reference.

User-supplied log handler

It would be very nice if the bluez_inc log handler took a user-supplied logging callback. In our setup we already have a full logging + diagnostics system, and I want to just plug bluez_inc logs into that.

If bluez_inc had something like:

typedef void (*log_cb)(LogLevel level, char const *tag, char const *msg);
void log_set_cb(log_cb cb);

Then bluez_inc could use its own logger by default, but if I provide my own all bluez_inc logs would only go through my callback.

(If you're interested, I could try a PR)

How to implement secure characteristic?

Hi,

In my project, I'd like to create a GATT service with some characteristics.
And I want to make them secure (ask pairing to access them). Unsecure characteristics work fine.
Is it Possible ?
Is it possible to create secure characteristics with your framework?

Thank you

Functionality request related to Advertising

Thank you for this amazing library.
I am using it in a Raspberry Pi to connect using Android, and it works fine.

I also tried to connect from Windows using "Bluetooth LE Explorer" and I was unable to do that.
Moreover, in the advertising I include the device name. Android is able to find and parse the name, but the previous application not.

The next step was to use "BLE Scanner" in Android to analize the advertising, and I found that it includes the name of the device, but it does not includes the flags section (data type 0x01). I know that the standard makes this section optional, but it would be great to configure it in order to prepare a more "typical" advertisement.

Are you planning to implement this feature?
Is it possible at all?

Thank you for your support.
Best regards.

extern C for header files

Hi,

Thank you alot for your work.
Would it be possible to add the

#ifdef __cplusplus
extern "C" {
#endif

#ifdef __cplusplus
}
#endif

around the public header files to make it useable with c++?

Preferably only put them around the function prototypes, not the includes. New versions of Glib apparently include C++ headers, so that putting the extern "C" around the glib includes generates compile errors.

Regards

Get CompanyID and Manufacturer Data on discovery callback

Hello! First of all I'd like to thank you for the awesome work you've done with this bluez_inc library. Thank you so much!

Ok, here is the issue I'm struggling with and hopefully you can help me with: I'd like to parse as a byte array the manufacturer data that is included within the advertisement packet from a peripheral device, but the binc_device_get_manufacturer_data method returns a pointer to a GHashTable which honestly I don't know how to process. What is the key that I should look for in that hash table so I can check the Company ID that is advertised? How can I parse the actual manufacture data that is included, is it a key or a value in the hash table? How is the data inserted in that hash table: as string or as byte array?

Thank you in advance

Missing notification enable callback on android phone

Hi @weliem

I'm using your bluz dbus project to build my application which can do stuff with mobile applications. My custom service contains some characteristics with notification property. When I run nrf connect I am able to enable disable notifications properly. But when I run custom application on Android the notifications are not enabled, however in nrfLog application I can see that write to ccc was done.

NOTE: the same application works fine on iphone,

I'll attach the log from nrfLogger app:

16:25:45.384 Connecting to C4:BD:E5:5C:38:C9...
16:25:45.384 gatt = device.connectGatt(autoConnect = false, TRANSPORT_LE, preferred PHY = LE 1M)
16:25:45.398 [Callback] Connection state changed with status: 0 and new state: CONNECTED (2)
16:25:45.398 Connected to C4:BD:E5:5C:38:C9
16:25:45.398 Discovering services...
16:25:45.398 gatt.discoverServices()
16:25:45.403 [Callback] Services discovered with status: 0
16:25:45.403 Services discovered
16:25:45.409 Generic Access (0x1800)
- Device Name [R] (0x2A00)
- Appearance [R] (0x2A01)
- Central Address Resolution [R] (0x2AA6)
Generic Attribute (0x1801)
- Service Changed [I] (0x2A05)
Client Characteristic Configuration (0x2902)
- Client Supported Features [R W] (0x2B29)
- Database Hash [R] (0x2B2A)
- Server Supported Features [R] (0x2B3A)
Device Information (0x180A)
- PnP ID [R] (0x2A50)
Unknown Service (xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx)
- Unknown Characteristic [R] (xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx)
- Unknown Characteristic [R] (xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx)
- Unknown Characteristic [N R] (xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx)
Client Characteristic Configuration (0x2902)
- Unknown Characteristic [W] (xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx)
16:25:45.409 gatt.setCharacteristicNotification(00002a05-0000-1000-8000-00805f9b34fb, true)
16:25:45.411 gatt.setCharacteristicNotification(xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx, true)

Do you have any idea what the possible issue can be?

Sending a notification to a specific subscriber

Hi

I am trying to create a small GATT service with a notification char allowing the server to send notification to a given node.
Is this possible with bluez_inc ? The binc_application_notify does not have a parameter to specify a destination node.

I don't see a way neither to get the address of a new subscriber in the callback function called when it happens

Connect failed (error 36: GDBus.Error:org.bluez.Error.Failed....

In my attempt to use this library to implement a Central for the Nordic Uart GATT on a RPi4B 64bit, Bullseye, Bluez 5.70, I am facing new issues.

I am using the Central-example as the starting point and the following uuid:

#define NUS_CHARACTERISTIC_TX_UUID	"6e400002-b5a3-f393-e0a9-e50e24dcca9e"
#define NUS_CHARACTERISTIC_RX_UUID	"6e400003-b5a3-f393-e0a9-e50e24dcca9e"
#define NORDIC_UART_SERVICE         "6e400001-b5a3-f393-e0a9-e50e24dcca9e"

I would like to connect to a pre-determined BLE device, and the on_scan_result() looks like this:

void on_scan_result(Adapter *adapter, Device *device) {
    char *deviceToString = binc_device_to_string(device);
    log_debug(TAG, deviceToString);
    g_free(deviceToString);
    const char* name    = binc_device_get_name(device);
    const char* address = binc_device_get_address(device);

    if (address != NULL && g_str_has_prefix(address, "E7:FA:F2:24:BF:B6")){
        if (name != NULL && g_str_has_prefix(name, "nRF Uart 0")) {
            binc_device_set_connection_state_change_cb(device, &on_connection_state_changed);
            binc_device_set_services_resolved_cb(device, &on_services_resolved);
            binc_device_set_bonding_state_changed_cb(device, &on_bonding_state_changed);
            binc_device_set_read_char_cb(device, &on_read);
            binc_device_set_write_char_cb(device, &on_write);
            binc_device_set_notify_char_cb(device, &on_notify);
            binc_device_set_notify_state_cb(device, &on_notification_state_changed);
            binc_device_set_read_desc_cb(device, &on_desc_read);
            binc_device_connect(device);
            //binc_adapter_stop_discovery(default_adapter);
        }
    }

At the 'nRF Uart 0' device, I can see that it accepts the connection to the RPi Central, but the RPi Central (this library) gives the error message as given below. Sometimes the RPi Central goes into an infinite loop with the output as given below, and sometimes it halts after the first error incident.

2023-10-30 07:12:36:429 DEBUG [Device] Connecting to 'nRF Uart 0' (E7:FA:F2:24:BF:B6) (BOND_NONE)
2023-10-30 07:12:36:429 DEBUG [Main] 'nRF Uart 0' (E7:FA:F2:24:BF:B6) state: CONNECTING (2)
2023-10-30 07:12:46:579 DEBUG [Main] 'nRF Uart 0' (E7:FA:F2:24:BF:B6) state: CONNECTED (1)
2023-10-30 07:12:47:106 ERROR [Device] Connect failed (error 36: GDBus.Error:org.bluez.Error.Failed: le-connection-abort-by-local)
2023-10-30 07:12:47:106 DEBUG [Main] (dis)connect failed (error 36: GDBus.Error:org.bluez.Error.Failed: le-connection-abort-by-local)
2023-10-30 07:13:00:387 DEBUG [Main] Device{name='nRF Uart 0', address='E7:FA:F2:24:BF:B6', address_type=random, rssi=-69, uuids=[6e400001-b5a3-f393-e0a9-e50e24dcca9e], manufacturer_data=[], service_data=[], paired=false, txpower=-255 path='/org/bluez/hci0/dev_E7_FA_F2_24_BF_B6' }

Google tells me that others that have experienced these issues (error 36) blames the BlueZ stack. I have BlueZ 5.70, but have tried to downgrade to 5.55 and 5.37 without any luck.

I'm a bit stuck. Any ideas?

Cannot re-init GATT server

Hi there,

Thank you for sharing this library. I have create GATT server based on peripheral example, and it works great. I can do write/read/notify using nRF Connect.

Now, one of my requirement is to have power saving feature. In order to do that, I have to close GATT server when receiving power saving signal and re-init GATT server again later on. Here is how I init the GATT server

bool BleHandler::Init()
{
    if(mInit) {
        AP_LOG_DEBUG("BLE handler already initialized");
        return true;
    }

    // Init dbus, mainloop, and adapter
    mDbusConnection = g_bus_get_sync(G_BUS_TYPE_SYSTEM, NULL, NULL);
    mLoop = g_main_loop_new(NULL, FALSE);

    // Get adapter
    for(int i = 0; i < BLE_HANDLER_ADAPTER_RETRY_COUNT; i++) {
        mAdapter = binc_adapter_get_default(mDbusConnection);
        if(mAdapter != NULL) {
            break;
        }
        std::this_thread::sleep_for(std::chrono::milliseconds(BLE_HANDLER_ADAPTER_RETRY_INTERVAL));
    }
    if (mAdapter == NULL) {
        AP_LOG_WARNING("No adapter found");
        return false;
    }
    AP_LOG_DEBUG("Adapter: %s", binc_adapter_get_path(mAdapter));

    // Make sure the adapter is on
    binc_adapter_set_powered_state_cb(mAdapter, &BleHandler::OnPoweredStateChanged);
    if (!binc_adapter_get_powered_state(mAdapter)) {
        binc_adapter_power_on(mAdapter);
    }

    // Setup remote central connection state callback
    binc_adapter_set_remote_central_cb(mAdapter, &BleHandler::OnCentralStateChanged);

    // Create, add and register advertisement, services, characteristic and descriptor, excluded as it way too long

    // Create thread
    mRunning = true;
    mStop = false;
    std::thread t1 (&BleHandler::Process, this);
    t1.detach();

    mInit = true;
    return true;
}

Debug log:

2022-04-28 17:42:50:320 DEBUG [Adapter] finding adapters
2022-04-28 17:42:50:329 DEBUG [Adapter] found 1 adapter
[D][27752] Adapter: /org/bluez/hci0     (ble-handler.cpp line 224)
2022-04-28 17:42:50:330 DEBUG [Application] successfully published application
2022-04-28 17:42:50:330 DEBUG [Application] successfully published local service 12345678-1234-5678-1234-56789abcdef0
2022-04-28 17:42:50:330 DEBUG [Application] successfully published local characteristic 12345678-1234-5678-1234-56789abcdef1
2022-04-28 17:42:50:330 DEBUG [Application] successfully published local characteristic 12345678-1234-5678-1234-56789abcdef2
2022-04-28 17:42:50:330 DEBUG [Application] successfully published local characteristic 12345678-1234-5678-1234-56789abcdef3
2022-04-28 17:42:50:331 DEBUG [Application] successfully published local descriptor 12345678-1234-5678-1234-56789abcdef4
2022-04-28 17:42:50:331 DEBUG [Application] set value <5050505050505050> to <12345678-1234-5678-1234-56789abcdef4>
2022-04-28 17:42:50:390 DEBUG [Application] adding /org/bluez/bincapplication/service0
2022-04-28 17:42:50:390 DEBUG [Application] adding /org/bluez/bincapplication/service0/char0
2022-04-28 17:42:50:390 DEBUG [Application] adding /org/bluez/bincapplication/service0/char2
2022-04-28 17:42:50:391 DEBUG [Application] adding /org/bluez/bincapplication/service0/char1
2022-04-28 17:42:50:391 DEBUG [Application] adding /org/bluez/bincapplication/service0/char1/desc0
2022-04-28 17:42:50:438 DEBUG [Adapter] successfully registered application
2022-04-28 17:42:50:532 DEBUG [Adapter] started advertising (XX:XX:XX:XX:XX:XX)

Init function will create thread for g_main_loop_run to live on:

void BleHandler::Process()
{
    // Start mainloop
    while(mRunning) {
        g_main_loop_run(mLoop);
    }
    mStop = true;
}

The thread will live until mRunning is set false on close command:

bool BleHandler::Close()
{
    if (!mInit) {
        AP_LOG_DEBUG("BLE handler already closed");
        return true;
    }

    // Delete app, advertisement and adapter
    if (mApp != NULL) {
        binc_adapter_unregister_application(mAdapter, mApp);
        binc_application_free(mApp);
        mApp = NULL;
    }
    if (mAdvertisement != NULL) {
        binc_adapter_stop_advertising(mAdapter, mAdvertisement);
        binc_advertisement_free(mAdvertisement);
        mAdvertisement = NULL;
    }
    if (mAdapter != NULL) {
        binc_adapter_free(mAdapter);
        mAdapter = NULL;
    }

    // Kill thread
    mRunning = false;
    g_main_loop_quit(mLoop);
    while(!mStop); // Wait for thread to be stopped

    // Clean up
    g_main_loop_unref(mLoop);
    g_dbus_connection_close_sync(mDbusConnection, NULL, NULL);
    g_object_unref(mDbusConnection);

    mInit = false;
    return true;
}

Debug log:

2022-04-28 17:43:53:486 DEBUG [Application] freeing application /org/bluez/bincapplication
2022-04-28 17:43:53:486 DEBUG [Application] freeing service /org/bluez/bincapplication/service0
2022-04-28 17:43:53:486 DEBUG [Application] freeing characteristic /org/bluez/bincapplication/service0/char0
2022-04-28 17:43:53:486 DEBUG [Application] freeing characteristic /org/bluez/bincapplication/service0/char2
2022-04-28 17:43:53:486 DEBUG [Application] freeing characteristic /org/bluez/bincapplication/service0/char1
2022-04-28 17:43:53:486 DEBUG [Application] freeing descriptor /org/bluez/bincapplication/service0/char1/desc0

Now, after I call close, and re-init again after some times, somehow it cannot find adapter. Here is the debug log when I tried to re-init again:

2022-04-28 17:44:10:846 DEBUG [Adapter] finding adapters
2022-04-28 17:44:10:847 ERROR [Adapter] Unable to get result for GetManagedObjects
2022-04-28 17:44:10:847 DEBUG [Adapter] found 0 adapter

Any idea or some kind of pointer is appreciated. Thank you for your time and assistance.

Can this library work on QCS404DK board?

I run the peripheral code on the QCS404DK board but couldn't register the advertisement as attached log below, everything is working fine on Ubuntu 22.04. The QCS404DK board also runs Linux (see the output of uname -a:

/ # uname -a
Linux nf-64 4.14.117-perf #1 SMP PREEMPT Mon Jan 4 17:55:28 CST 2021 aarch64 aarch64 aarch64 GNU/Linux

2021-01-04 09:50:45:311 DEBUG [Adapter] finding adapters
2021-01-04 09:50:45:315 DEBUG [Adapter] found 1 adapter
2021-01-04 09:50:45:315 DEBUG [Main] using default_adapter '/org/bluez/hci0'
2021-01-04 09:50:45:315 DEBUG [Main] Power status = 1
2021-01-04 09:50:45:316 DEBUG [Main] Adapter name: hci0
2021-01-04 09:50:45:327 DEBUG [Application] successfully published application
2021-01-04 09:50:45:327 DEBUG [Application] successfully published local service 00001809-0000-1000-8000-00805f9b34fb
2021-01-04 09:50:45:327 DEBUG [Application] successfully published local characteristic 00002a1c-0000-1000-8000-00805f9b34fb
2021-01-04 09:50:45:328 DEBUG [Application] successfully published local descriptor 00002901-0000-1000-8000-00805f9b34fb
2021-01-04 09:50:45:328 DEBUG [Application] set value <68656c6c6f20746865726500> to <00002901-0000-1000-8000-00805f9b34fb>
2021-01-04 09:50:45:328 DEBUG [Adapter] failed to register advertisement (error 19: GDBus.Error:org.freedesktop.DBus.Error.UnknownMethod: Unknown method 'RegisterAdvertisement' or interface 'org.bluez.LEAdvertisingManager1'.)
2021-01-04 09:50:45:332 DEBUG [Adapter] failed to register application (error 19: GDBus.Error:org.freedesktop.DBus.Error.UnknownMethod: Unknown method 'RegisterApplication' or interface 'org.bluez.GattManager1'.)

Correct approach for having a peripheral disconnect itself from central device?

Hi there Martijn!

Due to some client SDK issues for central devices, notably iOS, I need to have the peripheral disconnect itself. So far I've tried, in a specifically defined disconnect characteristic:

  1. Disconnecting the central device, and then removing said device.
    a. The main problem here is that in order to reconnect with the device, the user has to forget the bluetooth device, otherwise the reconnect fails. We have one specific flow where this is something we really don't want to have to do, and iOS doesn't enable applications to forget bluetooth devices.
    b. Another issue, probably because I shouldn't be disconnecting this way, is that other centrals that were previously added to the peripheral are also forgotten. (I'm seeing No cache for <central MAC address> when looking in /var/log/messages)
    binc_device_disconnect(device);
    binc_adapter_remove_device(default_adapter, device);
  1. Disconnecting the central device, and then advertising again. The problem here is that the disconnect call on its own is flaky, and that means that I need to occasionally call the disconnect multiple times. Then because its already advertising, I get an error saying the advertisement already exists.
    binc_device_disconnect(device);
    binc_adapter_start_advertising(default_adapter, advertisement);

Am I on the right track? Is there a different disconnect command or order of operations I should use? Also, is there a callback I can set for when the peripheral connection state changes? The central state change callback isn't called when just calling binc_device_disconnect.

I appreciate any input you may have, and I hope you're having a good day!

Liz

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.