diff --git a/CMakeLists.txt b/CMakeLists.txt index f5d0991..c2032ca 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -120,7 +120,7 @@ if(GATTLIB_BUILD_EXAMPLES) add_subdirectory(examples/read_write) #add_subdirectory(examples/read_write_memory) add_subdirectory(examples/notification) - #add_subdirectory(examples/nordic_uart) + add_subdirectory(examples/nordic_uart) add_subdirectory(tests/test_continuous_connection) # Some examples require Bluez code and other DBus support diff --git a/examples/nordic_uart/nordic_uart.c b/examples/nordic_uart/nordic_uart.c index 88c8ef7..32526aa 100644 --- a/examples/nordic_uart/nordic_uart.c +++ b/examples/nordic_uart/nordic_uart.c @@ -2,7 +2,7 @@ * * GattLib - GATT Library * - * Copyright (C) 2016-2021 Olivier Martin + * Copyright (C) 2016-2024 Olivier Martin * * * This program is free software; you can redistribute it and/or modify @@ -22,9 +22,11 @@ */ #include +#include +#include +#include #include #include -#include #ifdef GATTLIB_LOG_BACKEND_SYSLOG #include @@ -32,78 +34,77 @@ #include "gattlib.h" -#define MIN(a,b) ((a)<(b)?(a):(b)) - #define NUS_CHARACTERISTIC_TX_UUID "6e400002-b5a3-f393-e0a9-e50e24dcca9e" #define NUS_CHARACTERISTIC_RX_UUID "6e400003-b5a3-f393-e0a9-e50e24dcca9e" -gattlib_connection_t* m_connection; +#define BLE_SCAN_TIMEOUT 10 + +#define MIN(a,b) ((a)<(b)?(a):(b)) -void notification_cb(const uuid_t* uuid, const uint8_t* data, size_t data_length, void* user_data) { - int i; +static struct { + char *adapter_name; + char* mac_address; +} m_argument; + +// Declaration of thread condition variable +static pthread_cond_t m_connection_terminated = PTHREAD_COND_INITIALIZER; + +// declaring mutex +static pthread_mutex_t m_connection_terminated_lock = PTHREAD_MUTEX_INITIALIZER; + +static gattlib_connection_t* m_connection; + +static void usage(char *argv[]) { + printf("%s \n", argv[0]); +} + +static void notification_handler(const uuid_t* uuid, const uint8_t* data, size_t data_length, void* user_data) { + uintptr_t i; + for(i = 0; i < data_length; i++) { printf("%c", data[i]); } fflush(stdout); } -static void usage(char *argv[]) { - printf("%s \n", argv[0]); -} - -void int_handler(int dummy) { +static void int_handler(int dummy) { gattlib_disconnect(m_connection, false /* wait_disconnection */); exit(0); } -int main(int argc, char *argv[]) { - char input[256]; - char* input_ptr; - int i, ret, total_length, length = 0; +static void on_device_connect(gattlib_adapter_t* adapter, const char *dst, gattlib_connection_t* connection, int error, void* user_data) { uuid_t nus_characteristic_tx_uuid; uuid_t nus_characteristic_rx_uuid; - - if (argc != 2) { - usage(argv); - return 1; - } - -#ifdef GATTLIB_LOG_BACKEND_SYSLOG - openlog("gattlib_nordic_uart", LOG_CONS | LOG_NDELAY | LOG_PERROR, LOG_USER); - setlogmask(LOG_UPTO(LOG_INFO)); -#endif - - m_connection = gattlib_connect(NULL, argv[1], - GATTLIB_CONNECTION_OPTIONS_LEGACY_BDADDR_LE_RANDOM | - GATTLIB_CONNECTION_OPTIONS_LEGACY_BT_SEC_LOW); - if (m_connection == NULL) { - GATTLIB_LOG(GATTLIB_ERROR, "Fail to connect to the bluetooth device."); - return 1; - } + char input[256]; + char* input_ptr; + int ret, total_length, length = 0; // Convert characteristics to their respective UUIDs ret = gattlib_string_to_uuid(NUS_CHARACTERISTIC_TX_UUID, strlen(NUS_CHARACTERISTIC_TX_UUID) + 1, &nus_characteristic_tx_uuid); if (ret) { GATTLIB_LOG(GATTLIB_ERROR, "Fail to convert characteristic TX to UUID."); - return 1; + goto EXIT; } ret = gattlib_string_to_uuid(NUS_CHARACTERISTIC_RX_UUID, strlen(NUS_CHARACTERISTIC_RX_UUID) + 1, &nus_characteristic_rx_uuid); if (ret) { GATTLIB_LOG(GATTLIB_ERROR, "Fail to convert characteristic RX to UUID."); - return 1; + goto EXIT; } // Look for handle for NUS_CHARACTERISTIC_TX_UUID gattlib_characteristic_t* characteristics; int characteristic_count; - ret = gattlib_discover_char(m_connection, &characteristics, &characteristic_count); + ret = gattlib_discover_char(connection, &characteristics, &characteristic_count); if (ret) { GATTLIB_LOG(GATTLIB_ERROR, "Fail to discover characteristic."); - return 1; + goto EXIT; } + // + // Confirm the Nordic UART RX and TX GATT characteristics are present + // uint16_t tx_handle = 0, rx_handle = 0; - for (i = 0; i < characteristic_count; i++) { + for (int i = 0; i < characteristic_count; i++) { if (gattlib_uuid_cmp(&characteristics[i].uuid, &nus_characteristic_tx_uuid) == 0) { tx_handle = characteristics[i].value_handle; } else if (gattlib_uuid_cmp(&characteristics[i].uuid, &nus_characteristic_rx_uuid) == 0) { @@ -112,23 +113,29 @@ int main(int argc, char *argv[]) { } if (tx_handle == 0) { GATTLIB_LOG(GATTLIB_ERROR, "Fail to find NUS TX characteristic."); - return 1; + goto FREE_GATT_CHARACTERISTICS; } else if (rx_handle == 0) { GATTLIB_LOG(GATTLIB_ERROR, "Fail to find NUS RX characteristic."); - return 1; + goto FREE_GATT_CHARACTERISTICS; } - free(characteristics); - // Register notification handler - gattlib_register_notification(m_connection, notification_cb, NULL); + // + // Listen for Nordic UART TX GATT characteristic + // + ret = gattlib_register_notification(connection, notification_handler, NULL); + if (ret) { + GATTLIB_LOG(GATTLIB_ERROR, "Fail to register notification callback."); + goto FREE_GATT_CHARACTERISTICS; + } - ret = gattlib_notification_start(m_connection, &nus_characteristic_rx_uuid); + ret = gattlib_notification_start(connection, &nus_characteristic_rx_uuid); if (ret) { GATTLIB_LOG(GATTLIB_ERROR, "Fail to start notification."); - return 2; + goto FREE_GATT_CHARACTERISTICS; } // Register handler to catch Ctrl+C + m_connection = connection; signal(SIGINT, int_handler); while(1) { @@ -141,11 +148,97 @@ int main(int argc, char *argv[]) { ret = gattlib_write_without_response_char_by_handle(m_connection, tx_handle, input_ptr, length); if (ret) { GATTLIB_LOG(GATTLIB_ERROR, "Fail to send data to NUS TX characteristic."); - return 1; + goto FREE_GATT_CHARACTERISTICS; } input_ptr += length; } } +FREE_GATT_CHARACTERISTICS: + free(characteristics); + +EXIT: + gattlib_disconnect(connection, false /* wait_disconnection */); + + pthread_mutex_lock(&m_connection_terminated_lock); + pthread_cond_signal(&m_connection_terminated); + pthread_mutex_unlock(&m_connection_terminated_lock); +} + +static int stricmp(char const *a, char const *b) { + for (;; a++, b++) { + int d = tolower((unsigned char)*a) - tolower((unsigned char)*b); + if (d != 0 || !*a) + return d; + } +} + +static void ble_discovered_device(gattlib_adapter_t* adapter, const char* addr, const char* name, void *user_data) { + int ret; + int16_t rssi; + + if (stricmp(addr, m_argument.mac_address) != 0) { + return; + } + + ret = gattlib_get_rssi_from_mac(adapter, addr, &rssi); + if (ret == 0) { + GATTLIB_LOG(GATTLIB_INFO, "Found bluetooth device '%s' with RSSI:%d", m_argument.mac_address, rssi); + } else { + GATTLIB_LOG(GATTLIB_INFO, "Found bluetooth device '%s'", m_argument.mac_address); + } + + ret = gattlib_connect(adapter, 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'", addr); + } +} + +static void* ble_task(void* arg) { + char* addr = arg; + gattlib_adapter_t* adapter; + int ret; + + ret = gattlib_adapter_open(m_argument.adapter_name, &adapter); + if (ret) { + GATTLIB_LOG(GATTLIB_ERROR, "Failed to open adapter."); + return NULL; + } + + ret = gattlib_adapter_scan_enable(adapter, ble_discovered_device, BLE_SCAN_TIMEOUT, addr); + if (ret) { + GATTLIB_LOG(GATTLIB_ERROR, "Failed to scan."); + return NULL; + } + + // Wait for the device to be connected + pthread_mutex_lock(&m_connection_terminated_lock); + pthread_cond_wait(&m_connection_terminated, &m_connection_terminated_lock); + pthread_mutex_unlock(&m_connection_terminated_lock); + + return NULL; +} + +int main(int argc, char *argv[]) { + int ret; + + if (argc != 2) { + usage(argv); + return 1; + } + +#ifdef GATTLIB_LOG_BACKEND_SYSLOG + openlog("gattlib_nordic_uart", LOG_CONS | LOG_NDELAY | LOG_PERROR, LOG_USER); + setlogmask(LOG_UPTO(LOG_INFO)); +#endif + + m_argument.adapter_name = NULL; + m_argument.mac_address = argv[1]; + + ret = gattlib_mainloop(ble_task, NULL); + if (ret != GATTLIB_SUCCESS) { + GATTLIB_LOG(GATTLIB_ERROR, "Failed to create gattlib mainloop"); + } + return 0; }