Fix gattlib and examples

pull/185/merge 0.4.9
Olivier Martin 2024-03-17 15:24:32 +01:00
parent b2c4094cb6
commit 2a46780e96
12 changed files with 197 additions and 107 deletions

View File

@ -405,6 +405,7 @@ static gatt_connection_t *gattlib_connect_with_options(const char *src, const ch
if (conn == NULL) {
if (io_connect_arg.error) {
fprintf(stderr, "Error: gattlib_connect - initialization error:%s\n", io_connect_arg.error->message);
g_error_free(io_connect_arg.error);
} else {
fprintf(stderr, "Error: gattlib_connect - initialization\n");
}
@ -428,6 +429,7 @@ static gatt_connection_t *gattlib_connect_with_options(const char *src, const ch
if (io_connect_arg.error) {
fprintf(stderr, "gattlib_connect - connection error:%s\n", io_connect_arg.error->message);
g_error_free(io_connect_arg.error);
return NULL;
} else {
return conn;

View File

@ -98,5 +98,6 @@ void gattlib_on_gatt_notification(gatt_connection_t* connection, const uuid_t* u
g_thread_pool_push(connection->notification.thread_pool, arg, &error);
if (error != NULL) {
GATTLIB_LOG(GATTLIB_ERROR, "gattlib_on_gatt_notification: Failed to push thread in pool: %s", error->message);
g_error_free(error);
}
}

View File

@ -24,6 +24,7 @@ int gattlib_register_notification(gatt_connection_t* connection, gattlib_event_h
1 /* max_threads */, FALSE /* exclusive */, &error);
if (error != NULL) {
GATTLIB_LOG(GATTLIB_ERROR, "gattlib_register_notification: Failed to create thread pool: %s", error->message);
g_error_free(error);
return GATTLIB_ERROR_INTERNAL;
} else {
assert(connection->notification.thread_pool != NULL);
@ -46,6 +47,7 @@ int gattlib_register_indication(gatt_connection_t* connection, gattlib_event_han
1 /* max_threads */, FALSE /* exclusive */, &error);
if (error != NULL) {
GATTLIB_LOG(GATTLIB_ERROR, "gattlib_register_indication: Failed to create thread pool: %s", error->message);
g_error_free(error);
return GATTLIB_ERROR_INTERNAL;
} else {
return GATTLIB_SUCCESS;
@ -188,6 +190,7 @@ void gattlib_handler_dispatch_to_thread(struct gattlib_handler* handler, void (*
handler->thread = g_thread_try_new(thread_name, thread_func, thread_args, &error);
if (handler->thread == NULL) {
GATTLIB_LOG(GATTLIB_ERROR, "Failed to create thread '%s': %s", thread_name, error->message);
g_error_free(error);
return;
}
}

View File

@ -14,8 +14,32 @@
// We make this variable global to be able to exit the main loop
static GMainLoop *m_main_loop;
struct _execute_task_arg {
void* (*task)(void* arg);
void* arg;
};
static void* _execute_task(void* arg) {
struct _execute_task_arg *execute_task_arg = arg;
execute_task_arg->task(execute_task_arg->arg);
g_main_loop_quit(m_main_loop);
return NULL;
}
int gattlib_mainloop(void* (*task)(void* arg), void *arg) {
GThread *task_thread = g_thread_new("gattlib_task", task, arg);
struct _execute_task_arg execute_task_arg = {
.task = task,
.arg = arg
};
GError* error;
GThread *task_thread = g_thread_try_new("gattlib_task", _execute_task, &execute_task_arg, &error);
if (m_main_loop != NULL) {
GATTLIB_LOG(GATTLIB_ERROR, "Main loop is already running: %s", error->message);
g_error_free(error);
return GATTLIB_BUSY;
}
m_main_loop = g_main_loop_new(NULL, FALSE);
@ -23,7 +47,9 @@ int gattlib_mainloop(void* (*task)(void* arg), void *arg) {
g_main_loop_unref(m_main_loop);
g_thread_join(task_thread);
g_thread_unref(task_thread);
m_main_loop = NULL;
return GATTLIB_SUCCESS;
}

View File

@ -341,6 +341,11 @@ static int _gattlib_adapter_scan_enable_with_filter(void *adapter, uuid_t **uuid
char uuid_str[MAX_LEN_UUID_STR + 1];
GVariantBuilder list_uuid_builder;
if (uuid_list == NULL) {
GATTLIB_LOG(GATTLIB_ERROR, "Could not start BLE scan. Missing list of UUIDs");
return GATTLIB_INVALID_PARAMETER;
}
GATTLIB_LOG(GATTLIB_DEBUG, "Configure bluetooth scan with UUID");
g_variant_builder_init(&list_uuid_builder, G_VARIANT_TYPE ("as"));
@ -434,9 +439,12 @@ int gattlib_adapter_scan_enable_with_filter(void *adapter, uuid_t **uuid_list, i
return ret;
}
gattlib_adapter->ble_scan.is_scanning = true;
gattlib_adapter->ble_scan.scan_loop_thread = g_thread_try_new("gattlib_ble_scan", _ble_scan_loop, gattlib_adapter, &error);
if (gattlib_adapter->ble_scan.scan_loop_thread == NULL) {
GATTLIB_LOG(GATTLIB_ERROR, "Failed to create BLE scan thread: %s", error->message);
g_error_free(error);
return GATTLIB_ERROR_INTERNAL;
}
@ -444,8 +452,9 @@ int gattlib_adapter_scan_enable_with_filter(void *adapter, uuid_t **uuid_list, i
while (gattlib_adapter->ble_scan.is_scanning) {
g_cond_wait(&gattlib_adapter->ble_scan.scan_loop_cond, &gattlib_adapter->ble_scan.scan_loop_mutex);
}
// Free thread
g_object_unref(gattlib_adapter->ble_scan.scan_loop_thread);
g_thread_unref(gattlib_adapter->ble_scan.scan_loop_thread);
gattlib_adapter->ble_scan.scan_loop_thread = NULL;
g_mutex_unlock(&gattlib_adapter->ble_scan.scan_loop_mutex);
@ -468,6 +477,7 @@ int gattlib_adapter_scan_enable_with_filter_non_blocking(void *adapter, uuid_t *
gattlib_adapter->ble_scan.scan_loop_thread = g_thread_try_new("gattlib_ble_scan", _ble_scan_loop, gattlib_adapter, &error);
if (gattlib_adapter->ble_scan.scan_loop_thread == NULL) {
GATTLIB_LOG(GATTLIB_ERROR, "Failed to create BLE scan thread: %s", error->message);
g_error_free(error);
return GATTLIB_ERROR_INTERNAL;
}
@ -559,8 +569,7 @@ int gattlib_adapter_close(void* adapter)
// Ensure the thread is freed on adapter closing
if (gattlib_adapter->ble_scan.scan_loop_thread) {
//TODO: Fix memory leak here
//g_object_unref(gattlib_adapter->ble_scan.scan_loop_thread);
g_thread_unref(gattlib_adapter->ble_scan.scan_loop_thread);
gattlib_adapter->ble_scan.scan_loop_thread = NULL;
}

View File

@ -33,6 +33,8 @@
#define BLE_SCAN_TIMEOUT 60
static const char* adapter_name;
static void ble_advertising_device(void *adapter, const char* addr, const char* name, void *user_data) {
gattlib_advertisement_data_t *advertisement_data;
size_t advertisement_data_count;
@ -60,29 +62,14 @@ static void ble_advertising_device(void *adapter, const char* addr, const char*
printf("\n");
}
int main(int argc, const char *argv[]) {
const char* adapter_name;
static void* ble_task(void *arg) {
void* adapter;
int ret;
if (argc == 1) {
adapter_name = NULL;
} else if (argc == 2) {
adapter_name = argv[1];
} else {
GATTLIB_LOG(GATTLIB_ERROR, "%s [<bluetooth-adapter>]", argv[0]);
return 1;
}
#ifdef GATTLIB_LOG_BACKEND_SYSLOG
openlog("gattlib_advertisement_dat", LOG_CONS | LOG_NDELAY | LOG_PERROR, LOG_USER);
setlogmask(LOG_UPTO(LOG_INFO));
#endif
ret = gattlib_adapter_open(adapter_name, &adapter);
if (ret) {
GATTLIB_LOG(GATTLIB_ERROR, "Failed to open adapter.");
return 1;
return NULL;
}
ret = gattlib_adapter_scan_enable_with_filter(adapter,
@ -103,5 +90,30 @@ int main(int argc, const char *argv[]) {
EXIT:
gattlib_adapter_close(adapter);
return NULL;
}
int main(int argc, const char *argv[]) {
int ret;
if (argc == 1) {
adapter_name = NULL;
} else if (argc == 2) {
adapter_name = argv[1];
} else {
GATTLIB_LOG(GATTLIB_ERROR, "%s [<bluetooth-adapter>]", argv[0]);
return 1;
}
#ifdef GATTLIB_LOG_BACKEND_SYSLOG
openlog("gattlib_advertisement_dat", LOG_CONS | LOG_NDELAY | LOG_PERROR, LOG_USER);
setlogmask(LOG_UPTO(LOG_INFO));
#endif
ret = gattlib_mainloop(ble_task, NULL);
if (ret != GATTLIB_SUCCESS) {
GATTLIB_LOG(GATTLIB_ERROR, "Failed to create gattlib mainloop");
}
return ret;
}

View File

@ -2,8 +2,7 @@
*
* GattLib - GATT Library
*
* Copyright (C) 2021 Olivier Martin <olivier@labapart.org>
*
* Copyright (C) 2021-2024 Olivier Martin <olivier@labapart.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -33,7 +32,9 @@
#include "gattlib.h"
#define BLE_SCAN_TIMEOUT 4
#define BLE_SCAN_TIMEOUT 10
static const char* adapter_name;
typedef void (*ble_discovered_device_t)(const char* addr, const char* name);
@ -43,33 +44,19 @@ static pthread_mutex_t g_mutex = PTHREAD_MUTEX_INITIALIZER;
LIST_HEAD(listhead, connection_t) g_ble_connections;
struct connection_t {
pthread_t thread;
void *adapter;
char* addr;
LIST_ENTRY(connection_t) entries;
};
static void *ble_connect_device(void *arg) {
struct connection_t *connection = arg;
char* addr = connection->addr;
gatt_connection_t* gatt_connection;
static void on_device_connect(void *adapter, const char *dst, gatt_connection_t* connection, int error, void* user_data) {
gattlib_primary_service_t* services;
gattlib_characteristic_t* characteristics;
int services_count, characteristics_count;
char uuid_str[MAX_LEN_UUID_STR + 1];
int ret, i;
pthread_mutex_lock(&g_mutex);
printf("------------START %s ---------------\n", addr);
gatt_connection = gattlib_connect(NULL, addr, GATTLIB_CONNECTION_OPTIONS_LEGACY_DEFAULT);
if (gatt_connection == NULL) {
GATTLIB_LOG(GATTLIB_ERROR, "Fail to connect to the bluetooth device.");
goto connection_exit;
} else {
puts("Succeeded to connect to the bluetooth device.");
}
ret = gattlib_discover_primary(gatt_connection, &services, &services_count);
ret = gattlib_discover_primary(connection, &services, &services_count);
if (ret != 0) {
GATTLIB_LOG(GATTLIB_ERROR, "Fail to discover primary services.");
goto disconnect_exit;
@ -84,7 +71,7 @@ static void *ble_connect_device(void *arg) {
}
free(services);
ret = gattlib_discover_char(gatt_connection, &characteristics, &characteristics_count);
ret = gattlib_discover_char(connection, &characteristics, &characteristics_count);
if (ret != 0) {
GATTLIB_LOG(GATTLIB_ERROR, "Fail to discover characteristics.");
goto disconnect_exit;
@ -99,9 +86,22 @@ static void *ble_connect_device(void *arg) {
free(characteristics);
disconnect_exit:
gattlib_disconnect(gatt_connection);
gattlib_disconnect(connection);
}
static void *ble_connect_device(void *arg) {
struct connection_t *connection = arg;
char* addr = connection->addr;
int ret;
pthread_mutex_lock(&g_mutex);
printf("------------START %s ---------------\n", addr);
ret = gattlib_connect(connection->adapter, connection->addr, GATTLIB_CONNECTION_OPTIONS_NONE, on_device_connect, NULL);
if (ret != GATTLIB_SUCCESS) {
GATTLIB_LOG(GATTLIB_ERROR, "Failed to connect to the bluetooth device '%s'", connection->addr);
}
connection_exit:
printf("------------DONE %s ---------------\n", addr);
pthread_mutex_unlock(&g_mutex);
return NULL;
@ -123,6 +123,7 @@ static void ble_discovered_device(void *adapter, const char* addr, const char* n
return;
}
connection->addr = strdup(addr);
connection->adapter = adapter;
ret = pthread_create(&connection->thread, NULL, ble_connect_device, connection);
if (ret != 0) {
@ -133,31 +134,14 @@ static void ble_discovered_device(void *adapter, const char* addr, const char* n
LIST_INSERT_HEAD(&g_ble_connections, connection, entries);
}
int main(int argc, const char *argv[]) {
const char* adapter_name;
static void* ble_task(void* arg) {
void* adapter;
int ret;
if (argc == 1) {
adapter_name = NULL;
} else if (argc == 2) {
adapter_name = argv[1];
} else {
printf("%s [<bluetooth-adapter>]\n", argv[0]);
return 1;
}
#ifdef GATTLIB_LOG_BACKEND_SYSLOG
openlog("gattlib_ble_scan", LOG_CONS | LOG_NDELAY | LOG_PERROR, LOG_USER);
setlogmask(LOG_UPTO(LOG_INFO));
#endif
LIST_INIT(&g_ble_connections);
ret = gattlib_adapter_open(adapter_name, &adapter);
if (ret) {
GATTLIB_LOG(GATTLIB_ERROR, "Failed to open adapter.");
return 1;
return NULL;
}
pthread_mutex_lock(&g_mutex);
@ -183,5 +167,32 @@ int main(int argc, const char *argv[]) {
EXIT:
gattlib_adapter_close(adapter);
return NULL;
}
int main(int argc, const char *argv[]) {
int ret;
if (argc == 1) {
adapter_name = NULL;
} else if (argc == 2) {
adapter_name = argv[1];
} else {
printf("%s [<bluetooth-adapter>]\n", argv[0]);
return 1;
}
#ifdef GATTLIB_LOG_BACKEND_SYSLOG
openlog("gattlib_ble_scan", LOG_CONS | LOG_NDELAY | LOG_PERROR, LOG_USER);
setlogmask(LOG_UPTO(LOG_INFO));
#endif
LIST_INIT(&g_ble_connections);
ret = gattlib_mainloop(ble_task, NULL);
if (ret != GATTLIB_SUCCESS) {
GATTLIB_LOG(GATTLIB_ERROR, "Failed to create gattlib mainloop");
}
return ret;
}

View File

@ -2,8 +2,7 @@
*
* GattLib - GATT Library
*
* Copyright (C) 2021 Olivier Martin <olivier@labapart.org>
*
* Copyright (C) 2021-2024 Olivier Martin <olivier@labapart.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -29,6 +28,8 @@
#define BLE_SCAN_EDDYSTONE_TIMEOUT 20
const char* m_adapter_name;
/**
* @brief Handler called on new discovered BLE device
*
@ -79,15 +80,39 @@ void on_eddystone_found(void *adapter, const char* addr, const char* name,
gattlib_adapter_scan_disable(adapter);
}
static void* ble_task(void* arg) {
void* adapter = NULL;
int ret;
ret = gattlib_adapter_open(m_adapter_name, &adapter);
if (ret != GATTLIB_SUCCESS) {
GATTLIB_LOG(GATTLIB_ERROR, "Failed to open adapter.");
return NULL;
}
ret = gattlib_adapter_scan_eddystone(adapter,
0, /* rssi_threshold. The value is not relevant as we do not pass GATTLIB_EDDYSTONE_LIMIT_RSSI */
GATTLIB_EDDYSTONE_TYPE_URL,
on_eddystone_found, BLE_SCAN_EDDYSTONE_TIMEOUT, NULL);
if (ret != GATTLIB_SUCCESS) {
GATTLIB_LOG(GATTLIB_ERROR, "Failed to scan.");
goto EXIT;
}
puts("Scan completed");
EXIT:
gattlib_adapter_close(adapter);
return NULL;
}
int main(int argc, const char *argv[]) {
const char* adapter_name;
void* adapter;
int ret;
if (argc == 1) {
adapter_name = NULL;
m_adapter_name = NULL;
} else if (argc == 2) {
adapter_name = argv[1];
m_adapter_name = argv[1];
} else {
printf("%s [<bluetooth-adapter>]\n", argv[0]);
return 1;
@ -98,24 +123,10 @@ int main(int argc, const char *argv[]) {
setlogmask(LOG_UPTO(LOG_INFO));
#endif
ret = gattlib_adapter_open(adapter_name, &adapter);
if (ret) {
GATTLIB_LOG(GATTLIB_ERROR, "Failed to open adapter.");
return 1;
ret = gattlib_mainloop(ble_task, NULL);
if (ret != GATTLIB_SUCCESS) {
GATTLIB_LOG(GATTLIB_ERROR, "Failed to create gattlib mainloop");
}
ret = gattlib_adapter_scan_eddystone(adapter,
0, /* rssi_threshold. The value is not relevant as we do not pass GATTLIB_EDDYSTONE_LIMIT_RSSI */
GATTLIB_EDDYSTONE_TYPE_URL,
on_eddystone_found, BLE_SCAN_EDDYSTONE_TIMEOUT, NULL);
if (ret) {
GATTLIB_LOG(GATTLIB_ERROR, "Failed to scan.");
goto EXIT;
}
puts("Scan completed");
EXIT:
gattlib_adapter_close(adapter);
return ret;
}

View File

@ -10,10 +10,12 @@ import argparse
import threading
import time
from gattlib import adapter
from gattlib import adapter, mainloop
SCAN_TIMEOUT_SEC = 60
parser = argparse.ArgumentParser(description='Gattlib BLE scan example')
parser.add_argument('--duration', default=30, type=int, help='Duration of the BLE scanning')
parser.add_argument('--duration', default=SCAN_TIMEOUT_SEC, type=int, help='Duration of the BLE scanning')
args = parser.parse_args()
# We only use a lock to not mixed printing statements of various devices
@ -32,20 +34,21 @@ def connect_ble_device(device):
print("- GATTCharacteristic: 0x%x" % key)
device.disconnect()
except Exception as e:
print("EXCEPTION: %s:%s", type(e), str(e))
print(f"EXCEPTION: {type(e)}: {str(e)}")
lock.release()
def on_discovered_ble_device(device, user_data):
threading.Thread(target=connect_ble_device, args=(device,)).start()
# Use default adapter
default_adapter = adapter.Adapter()
# Scan for 'args.duration' seconds
default_adapter.open()
default_adapter.scan_enable(on_discovered_ble_device, args.duration)
def scan_ble_devices():
default_adapter.open()
# Scan for 'args.duration' seconds
default_adapter.scan_enable(on_discovered_ble_device, timeout=args.duration)
# Because scan_enable() is not blocking, we need to wait for the same duration
time.sleep(args.duration)
# Because scan_enable() is not blocking, we need to wait for the same duration
time.sleep(args.duration)
mainloop.run_mainloop_with(scan_ble_devices)

View File

@ -7,16 +7,16 @@
#
import argparse
import threading
import time
from gattlib import adapter
from gattlib import adapter, mainloop
SCAN_TIMEOUT_SEC = 60
parser = argparse.ArgumentParser(description='Gattlib Find Eddystone device example')
parser.add_argument('--duration', default=SCAN_TIMEOUT_SEC, type=int, help='Duration of the BLE scanning')
args = parser.parse_args()
# Use default adapter
default_adapter = adapter.Adapter()
def on_eddystone_device_found(device, advertisement_data, manufacturer_id, manufacturer_data, user_data):
rssi = default_adapter.get_rssi_from_mac(device.mac_address)
@ -35,9 +35,17 @@ def on_eddystone_device_found(device, advertisement_data, manufacturer_id, manuf
else:
print("Eddystone frame not supported: 0x%x" % eddystone_data[0])
# Use default adapter
default_adapter = adapter.Adapter()
# Scan for 30 seconds
default_adapter.open()
default_adapter.scan_eddystone_enable(on_eddystone_device_found,
adapter.GATTLIB_EDDYSTONE_TYPE_UID | adapter.GATTLIB_EDDYSTONE_TYPE_URL | adapter.GATTLIB_EDDYSTONE_TYPE_TLM | adapter.GATTLIB_EDDYSTONE_TYPE_EID,
30) # Look for 30 seconds
def scan_ble_devices():
default_adapter.open()
# Scan for 'args.duration' seconds
default_adapter.scan_eddystone_enable(on_eddystone_device_found,
adapter.GATTLIB_EDDYSTONE_TYPE_UID | adapter.GATTLIB_EDDYSTONE_TYPE_URL | adapter.GATTLIB_EDDYSTONE_TYPE_TLM | adapter.GATTLIB_EDDYSTONE_TYPE_EID,
args.duration)
# Because scan_enable() is not blocking, we need to wait for the same duration
time.sleep(args.duration)
mainloop.run_mainloop_with(scan_ble_devices)

View File

@ -140,7 +140,7 @@ gattlib_adapter_scan_enable_with_filter_non_blocking.argtypes = [c_void_p, POINT
# int gattlib_adapter_scan_eddystone(void *adapter, int16_t rssi_threshold, uint32_t eddsytone_types,
# gattlib_discovered_device_with_data_t discovered_device_cb, size_t timeout, void *user_data)
gattlib_adapter_scan_eddystone = gattlib.gattlib_adapter_scan_eddystone
gattlib_adapter_scan_eddystone.argtypes = [c_void_p, c_int16, c_uint32, c_void_p, c_size_t, c_void_p]
gattlib_adapter_scan_eddystone.argtypes = [c_void_p, c_int16, c_uint32, py_object, c_size_t, py_object]
# int gattlib_connect(void *adapter, const char *dst, unsigned long options, gatt_connect_cb_t connect_cb, void* user_data)
gattlib_connect = gattlib.gattlib_connect

View File

@ -44,6 +44,7 @@ extern "C" {
#define GATTLIB_DEVICE_ERROR 6
#define GATTLIB_DEVICE_NOT_CONNECTED 7
#define GATTLIB_NO_ADAPTER 8
#define GATTLIB_BUSY 9
#define GATTLIB_ERROR_MODULE_MASK 0xF0000000
#define GATTLIB_ERROR_DBUS 0x10000000
#define GATTLIB_ERROR_BLUEZ 0x20000000
@ -82,6 +83,7 @@ extern "C" {
* is for Bluez prior to v5.42 (before Bluez) support
*/
//@{
#define GATTLIB_CONNECTION_OPTIONS_NONE 0
#define GATTLIB_CONNECTION_OPTIONS_LEGACY_BDADDR_LE_PUBLIC (1 << 0)
#define GATTLIB_CONNECTION_OPTIONS_LEGACY_BDADDR_LE_RANDOM (1 << 1)
#define GATTLIB_CONNECTION_OPTIONS_LEGACY_BT_SEC_LOW (1 << 2)
@ -294,6 +296,8 @@ int gattlib_adapter_scan_enable_with_filter_non_blocking(void *adapter, uuid_t *
/**
* @brief Enable Eddystone Bluetooth Device scanning on a given adapter
*
* This function will block until either the timeout has expired or gattlib_adapter_scan_disable() has been called.
*
* @param adapter is the context of the newly opened adapter
* @param rssi_threshold is the imposed RSSI threshold for the returned devices.
* @param eddystone_types defines the type(s) of Eddystone advertisement data type to select.