From d52abc2a14ef06f4e01eba71d59b4a80701d0cd2 Mon Sep 17 00:00:00 2001 From: Carl Philipp Klemm Date: Mon, 10 Nov 2025 19:15:19 +0100 Subject: [PATCH] inital commit --- CMakeLists.txt | 10 ++ cliclient.c | 252 +++++++++++++++++++++++++++++++++++++++++++++++++ gattlib.h | 162 +++++++++++++++++++++++++++++++ 3 files changed, 424 insertions(+) create mode 100755 CMakeLists.txt create mode 100755 cliclient.c create mode 100755 gattlib.h diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100755 index 0000000..a681aee --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,10 @@ +cmake_minimum_required(VERSION 2.4) +project(cliclient) + +add_executable(cliclient cliclient.c) + +target_link_libraries( cliclient /home/philipp/BA/Programming/CliClient/libgattlib.so ) +set_target_properties( cliclient PROPERTIES COMPILE_FLAGS -m64 LINK_FLAGS -m64) +add_definitions("-s -Wall") + +install(TARGETS cliclient RUNTIME DESTINATION bin) diff --git a/cliclient.c b/cliclient.c new file mode 100755 index 0000000..f14ab72 --- /dev/null +++ b/cliclient.c @@ -0,0 +1,252 @@ +/* Telsys CLI client + * + * Copyright (C) 2017-2018 Carl Philipp Klemm + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 3 as published by + * the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include /*fprintf and printf*/ +#include /*for true and false defines*/ +#include /*uintX_t and friends*/ +#include /*interupt singal handeling*/ +#include /*sleep()*/ + + +#include "gattlib.h" /*gattlib helps to keep this under 5 pages*/ + +/*microcontoler details*/ +#define TIMER_PRESCALER 0 +#define TIMER_CLOCK_FREQ 32768 //Hz + +/*UUIDs*/ +#define SERVICE_UUID "66410001-b565-4284-d0a8-54775812af9e" +#define TX_UUID "66410002-b565-4284-d0a8-54775812af9e" +#define ADC_UUID "66410003-b565-4284-d0a8-54775812af9e" +#define AUX_UUID "66410004-b565-4284-d0a8-54775812af9e" + +static uuid_t txUuid; +static uuid_t adcUuid; +static uuid_t auxUuid; + +/*Sample numbering heads*/ +static uint64_t currentAdcSampleId = 0; +static uint64_t currentAuxSampleId = 0; +static uint64_t auxTimeStampHead = 0; +static uint64_t adcTimeStampHead = 0; + +bool stop = false; + +/*Handler to trap interupts*/ +void intHandler(int dummy) +{ + stop = true; +} + +/*structs for storing samples*/ +typedef struct AdcSample +{ + uint16_t value; + uint64_t timeStamp; + uint64_t id; + uint8_t channel; +} AdcSample; + +typedef struct AuxSample +{ + int16_t accelx; + int16_t accely; + int16_t accelz; + uint8_t temperature; + uint64_t timeStamp; + uint64_t id; +} AuxSample; + +/*Stdout output formating*/ +void print_adc_sample(const AdcSample sample) +{ + printf("ADC, %u, %lu, %lu, %u\n", sample.channel, sample.id, sample.timeStamp, sample.value); +} + +void print_aux_sample(const AuxSample sample) +{ + printf("AUX, %lu, %lu, %d, %d, %d, %u\n", sample.id, sample.timeStamp, sample.accelx, sample.accely, sample.accely, sample.temperature); +} + +/*Data helper functions.*/ +uint32_t uc_ticks_to_us(const uint16_t ticks) +{ + return ticks*((TIMER_PRESCALER+1)*1000000) / TIMER_CLOCK_FREQ; +} + +uint16_t to_equivalent_uint16(const uint8_t *data) +{ + uint16_t result = data[1]; + result = result | (data[0] << 8); + return result; +} + +void decode_adc_paket(const uint8_t* data, size_t length) +{ + if(length > 4) + { + int expectedCount = data[0]; + uint_fast8_t totalCount = expectedCount; + uint32_t currentDelta = uc_ticks_to_us(to_equivalent_uint16(data+1))*totalCount; + uint8_t channel = data[length-1]; + for(size_t i = 3; i + 2 <= length && expectedCount > 0; i+=2) + { + AdcSample tmp; + tmp.value = to_equivalent_uint16(data+i); + tmp.id = currentAdcSampleId; + tmp.channel = channel; + tmp.timeStamp=adcTimeStampHead+(currentDelta/totalCount)*(totalCount-expectedCount); + + print_adc_sample(tmp); + + expectedCount--; + currentAdcSampleId++; + } + adcTimeStampHead = adcTimeStampHead+currentDelta; + } +} + +void decode_aux_paket(const uint8_t* data, size_t length) +{ + if(length >= 15 ) + { + AuxSample tmp; + tmp.timeStamp = auxTimeStampHead; + tmp.accelx = to_equivalent_uint16(data+2); + tmp.accely = to_equivalent_uint16(data+4); + tmp.accelz = to_equivalent_uint16(data+6); + tmp.temperature = data[14]; + tmp.id = currentAuxSampleId; + + ++currentAuxSampleId; + auxTimeStampHead += uc_ticks_to_us(to_equivalent_uint16(data)); + + print_aux_sample(tmp); + } +} + +/*Callback for incomeing notifications*/ +void notification_handler(const uuid_t* uuid, const uint8_t* data, size_t length, void* instanceVoid) +{ + printf("got data\n"); + if(gattlib_uuid_cmp(uuid, &adcUuid) == 0) decode_adc_paket(data, length); + else if(gattlib_uuid_cmp(uuid, &auxUuid) == 0) decode_aux_paket(data, length); +} + +void register_and_enable_notification(gatt_connection_t* connection) +{ + uint16_t enable_notification = 0x0001; + sleep(5); + gattlib_write_char_by_uuid(connection, &txUuid, &enable_notification, sizeof(enable_notification)); + gattlib_register_notification(connection, ¬ification_handler, NULL); + gattlib_notification_start(connection, &adcUuid); + + int ret = gattlib_notification_start(connection, &adcUuid); + if (ret) fprintf(stderr, "Fail to start adc notification.\n"); + ret = gattlib_notification_start(connection, &auxUuid); + if (ret) fprintf(stderr, "Fail to start aux notification.\n"); + +} + +bool characteristic_discover(gatt_connection_t* connection) +{ + gattlib_characteristic_t* characteristics; + int characteristicsLength; + int errorCode; + bool foundService = false; + /*Discover all characteristics*/ + errorCode = gattlib_discover_char(connection, &characteristics, &characteristicsLength); + if(errorCode) + { + fprintf(stderr, "Can not discover Characteristics\n"); + return false; + } + else + { + /*Check if Telsys tx characteristics is amoung them*/ + for (int i = 0; i < characteristicsLength; i++) + { + if(gattlib_uuid_cmp(&characteristics[i].uuid, &txUuid) == 0) foundService = true; + } + if(foundService == false) + { + fprintf(stderr, "Device Dose not Support Requierd services.\n"); + return false; + } + } + return true; +} + +void print_help(char *argv[]) +{ + printf("Telsys CLI client. useage: \n%s \n", argv[0]); +} + +int main(int argc, char *argv[]) +{ + /*Test if argument count is correct*/ + if(argc != 2) + { + print_help(argv); + return 1; + } + + /*Register interupt handler*/ + signal(SIGINT, intHandler); + signal(SIGTERM, intHandler); + + printf("Populate\n"); + /*Populate gattlib uuid objects*/ + gattlib_string_to_uuid(TX_UUID, strlen(TX_UUID), &txUuid); + gattlib_string_to_uuid(ADC_UUID, strlen(ADC_UUID), &adcUuid); + gattlib_string_to_uuid(AUX_UUID, strlen(AUX_UUID), &auxUuid); + + /*Peform connection*/ + printf("Connect\n"); + gatt_connection_t* connection = NULL; + connection=gattlib_connect(NULL, argv[1], BDADDR_LE_RANDOM, BT_SEC_LOW, 0, 0); + if(connection == NULL) + { + fprintf(stderr, "Can not connect to device\n"); + return 2; + } + + /*check for characteristics, enable notification*/ + printf("Discover\n"); + if(!characteristic_discover(connection)) + { + gattlib_disconnect(connection); + return 3; + } + /*register notification callback*/ + else + { + printf("Register\n"); + register_and_enable_notification(connection); + } + + while(!stop) + { + printf("Loop\n"); + gattlib_write_char_by_uuid(connection, &txUuid, "on\n", sizeof("on")); + sleep(1); + } + gattlib_disconnect(connection); + return 0; +} diff --git a/gattlib.h b/gattlib.h new file mode 100755 index 0000000..1740a71 --- /dev/null +++ b/gattlib.h @@ -0,0 +1,162 @@ +/* + * + * GattLib - GATT Library + * + * Copyright (C) 2016-2017 Olivier Martin + * + * + * 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 + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifndef __GATTLIB_H__ +#define __GATTLIB_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +#include +#include +#include + +#ifndef BDADDR_BREDR + /* GattLib note: BD Address have only been introduced into Bluez v4.100. */ + /* Prior to this version, only BDADDR_BREDR can be supported */ + + /* BD Address type */ + #define BDADDR_BREDR 0x00 + #define BDADDR_LE_PUBLIC 0x01 + #define BDADDR_LE_RANDOM 0x02 +#endif + +#if BLUEZ_VERSION_MAJOR == 5 + #define ATT_MAX_MTU ATT_MAX_VALUE_LEN +#endif + +/* GATT Characteristic Properties Bitfield values */ +#define GATTLIB_CHARACTERISTIC_BROADCAST 0x01 +#define GATTLIB_CHARACTERISTIC_READ 0x02 +#define GATTLIB_CHARACTERISTIC_WRITE_WITHOUT_RESP 0x04 +#define GATTLIB_CHARACTERISTIC_WRITE 0x08 +#define GATTLIB_CHARACTERISTIC_NOTIFY 0x10 +#define GATTLIB_CHARACTERISTIC_INDICATE 0x20 + +#define CREATE_UUID16(value16) { .type=SDP_UUID16, .value.uuid16=(value16) } + +typedef enum { + BT_SEC_SDP = 0, + BT_SEC_LOW, + BT_SEC_MEDIUM, + BT_SEC_HIGH, +} gattlib_bt_sec_level_t; + +typedef struct _GAttrib GAttrib; + +typedef void (*gattlib_event_handler_t)(const uuid_t* uuid, const uint8_t* data, size_t data_length, void* user_data); + +typedef struct _gatt_connection_t { + void* context; + + gattlib_event_handler_t notification_handler; + void* notification_user_data; + + gattlib_event_handler_t indication_handler; + void* indication_user_data; +} gatt_connection_t; + +typedef void (*gattlib_discovered_device_t)(const char* addr, const char* name); +typedef void (*gatt_connect_cb_t)(gatt_connection_t* connection); +typedef void* (*gatt_read_cb_t)(const void* buffer, size_t buffer_len); + + +/** + * Open Bluetooth adapter + * + * @adapter_name With value NULL, the default adapter will be selected. + */ +int gattlib_adapter_open(const char* adapter_name, void** adapter); +int gattlib_adapter_scan_enable(void* adapter, gattlib_discovered_device_t discovered_device_cb, int timeout); +int gattlib_adapter_scan_disable(void* adapter); +int gattlib_adapter_close(void* adapter); + +/** + * @param src Local Adaptater interface + * @param dst Remote Bluetooth address + * @param dst_type Set LE address type (either BDADDR_LE_PUBLIC or BDADDR_LE_RANDOM) + * @param sec_level Set security level (either BT_IO_SEC_LOW, BT_IO_SEC_MEDIUM, BT_IO_SEC_HIGH) + * @param psm Specify the PSM for GATT/ATT over BR/EDR + * @param mtu Specify the MTU size + */ +gatt_connection_t *gattlib_connect(const char *src, const char *dst, + uint8_t dest_type, gattlib_bt_sec_level_t sec_level, int psm, int mtu); + +gatt_connection_t *gattlib_connect_async(const char *src, const char *dst, + uint8_t dest_type, gattlib_bt_sec_level_t sec_level, int psm, int mtu, + gatt_connect_cb_t connect_cb); + +int gattlib_disconnect(gatt_connection_t* connection); + +typedef struct { + uint16_t attr_handle_start; + uint16_t attr_handle_end; + uuid_t uuid; +} gattlib_primary_service_t; + +typedef struct { + uint16_t handle; + uint8_t properties; + uint16_t value_handle; + uuid_t uuid; +} gattlib_characteristic_t; + +typedef struct { + uint16_t handle; + uint16_t uuid16; + uuid_t uuid; +} gattlib_descriptor_t; + +int gattlib_discover_primary(gatt_connection_t* connection, gattlib_primary_service_t** services, int* services_count); +int gattlib_discover_char_range(gatt_connection_t* connection, int start, int end, gattlib_characteristic_t** characteristics, int* characteristics_count); +int gattlib_discover_char(gatt_connection_t* connection, gattlib_characteristic_t** characteristics, int* characteristic_count); +int gattlib_discover_desc_range(gatt_connection_t* connection, int start, int end, gattlib_descriptor_t** descriptors, int* descriptor_count); +int gattlib_discover_desc(gatt_connection_t* connection, gattlib_descriptor_t** descriptors, int* descriptor_count); + +int gattlib_read_char_by_uuid(gatt_connection_t* connection, uuid_t* uuid, void* buffer, size_t* buffer_len); +int gattlib_read_char_by_uuid_async(gatt_connection_t* connection, uuid_t* uuid, gatt_read_cb_t gatt_read_cb); + +int gattlib_write_char_by_uuid(gatt_connection_t* connection, uuid_t* uuid, const void* buffer, size_t buffer_len); +int gattlib_write_char_by_handle(gatt_connection_t* connection, uint16_t handle, const void* buffer, size_t buffer_len); + +/* + * @param uuid UUID of the characteristic that will trigger the notification + */ +int gattlib_notification_start(gatt_connection_t* connection, const uuid_t* uuid); +int gattlib_notification_stop(gatt_connection_t* connection, const uuid_t* uuid); + +void gattlib_register_notification(gatt_connection_t* connection, gattlib_event_handler_t notification_handler, void* user_data); +void gattlib_register_indication(gatt_connection_t* connection, gattlib_event_handler_t indication_handler, void* user_data); + +int gattlib_uuid_to_string(const uuid_t *uuid, char *str, size_t n); +int gattlib_string_to_uuid(const char *str, size_t n, uuid_t *uuid); +int gattlib_uuid_cmp(const uuid_t *uuid1, const uuid_t *uuid2); + +#ifdef __cplusplus +} +#endif + +#endif