1#ifndef BLUETOE_CHARACTERISTIC_HPP
2#define BLUETOE_CHARACTERISTIC_HPP
4#include <bluetoe/characteristic_value.hpp>
5#include <bluetoe/attribute.hpp>
6#include <bluetoe/attribute_handle.hpp>
7#include <bluetoe/codes.hpp>
8#include <bluetoe/uuid.hpp>
10#include <bluetoe/bits.hpp>
11#include <bluetoe/scattered_access.hpp>
12#include <bluetoe/service_uuid.hpp>
13#include <bluetoe/attribute_generator.hpp>
14#include <bluetoe/server_meta_type.hpp>
15#include <bluetoe/meta_types.hpp>
16#include <bluetoe/encryption.hpp>
17#include <bluetoe/descriptor.hpp>
28 struct characteristic_uuid_meta_type {};
30 struct characteristic_declaration_parameter {};
31 struct characteristic_user_description_parameter {};
33 template <
typename CCCDIndices,
typename ... Options >
34 struct generate_characteristic_attributes;
36 template <
typename ... Options >
37 struct count_characteristic_attributes;
39 template <
typename Characteristic >
40 struct sum_by_attributes;
42 template <
typename Characteristic >
43 struct sum_by_client_configs;
65 details::characteristic_uuid_meta_type,
66 details::characteristic_value_declaration_parameter,
67 details::characteristic_declaration_parameter,
68 details::valid_characteristic_option_meta_type {};
82 details::characteristic_uuid_meta_type,
83 details::characteristic_value_declaration_parameter,
84 details::characteristic_declaration_parameter,
85 details::valid_characteristic_option_meta_type {};
86 static constexpr bool is_128bit =
false;
158 template <
typename ... Options >
164 using attribute_numbers = details::count_characteristic_attributes< Options... >;
169 static constexpr std::size_t number_of_attributes = attribute_numbers::number_of_attributes;
170 static constexpr std::size_t number_of_client_configs = attribute_numbers::number_of_client_configs;
173 details::characteristic_meta_type,
174 details::valid_service_option_meta_type {};
178 typedef typename details::find_by_meta_type< details::characteristic_uuid_meta_type, Options... >::type configured_uuid;
184 template <
typename CCCDIndices, std::
size_t ClientCharacteristicIndex,
typename Service,
typename Server >
185 static details::attribute attribute_at( std::size_t index );
187 typedef typename details::find_by_meta_type< details::characteristic_value_meta_type, Options... >::type base_value_type;
189 static_assert( !std::is_same< base_value_type, details::no_such_type >::value,
190 "please make sure, that every characteristic defines some kind of value (bind_characteristic_value<> for example)" );
192 typedef typename base_value_type::template value_impl< Options... > value_type;
196 typename details::find_by_not_meta_type<
197 details::valid_characteristic_option_meta_type,
199 >::type, details::no_such_type >::value,
200 "Parameter passed to a characteristic that is not a valid characteristic option!" );
204 static constexpr std::size_t characteristic_declaration_index = 0;
205 static constexpr std::size_t characteristic_value_index = 1;
225 template < const
char* const >
230 details::characteristic_parameter_meta_type,
231 details::characteristic_user_description_parameter,
232 details::characteristic_declaration_parameter,
233 details::valid_characteristic_option_meta_type {};
240 template <
typename ... Options >
241 template <
typename CCCDIndices, std::
size_t ClientCharacteristicIndex,
typename Service,
typename Server >
244 assert( index < number_of_attributes );
246 using characteristic_descriptor_declarations =
typename details::generate_characteristic_attributes< CCCDIndices, Options... >;
247 return characteristic_descriptor_declarations::template attribute_at< ClientCharacteristicIndex, Service, Server >( index );
251 template <
typename ServiceUUID,
typename ... Options >
252 struct characteristic_or_service_uuid
254 using char_uuid =
typename find_by_meta_type< characteristic_uuid_meta_type, Options... >::type;
255 using uuid =
typename or_type< no_such_type, char_uuid, ServiceUUID >::type;
257 static_assert( !std::is_same< uuid, no_such_type >::value,
"If instanciating a characteristic<> for testing, please provide a UUID." );
259 static constexpr bool auto_generated_uuid = std::is_same< char_uuid, no_such_type >::value;
260 static constexpr bool serice_uuid_is_16_bit = count_by_meta_type< details::service_uuid_16_meta_type, ServiceUUID >::count;
262 static_assert( !( auto_generated_uuid && serice_uuid_is_16_bit ),
"No support for automatic generated characteristic UUIDs, if the containing service has not 128 bit UUID" );
268 template <
typename ... AttrOptions,
typename CCCDIndices, std::size_t ClientCharacteristicIndex,
typename ... ServiceOptions,
typename Server,
typename ... Options >
269 struct generate_attribute< std::tuple< characteristic_declaration_parameter, AttrOptions... >, CCCDIndices, ClientCharacteristicIndex, service< ServiceOptions...>, Server, Options... >
271 using characteristic_or_service_uuid_t = characteristic_or_service_uuid<
typename service< ServiceOptions... >::uuid, Options... >;
272 using uuid =
typename characteristic_or_service_uuid_t::uuid;
273 using value_type =
typename characteristic< Options... >::value_type;
275 static void fixup_auto_uuid( details::attribute_access_arguments& args )
277 using characteristics_t =
typename find_all_by_meta_type<
278 details::characteristic_meta_type,
279 ServiceOptions... >::type;
281 std::uint16_t char_index = index_of< characteristic< Options... >, characteristics_t >::value + 1;
283 static constexpr std::size_t uuid_offset = 3;
285 int index_low =
static_cast< int >( args.buffer_offset ) -
static_cast< int >( uuid_offset );
286 int index_high =
static_cast< int >( args.buffer_offset ) -
static_cast< int >( uuid_offset + 1 );
288 if ( index_low >= 0 && index_low <
static_cast< int >( args.buffer_size ) )
289 args.buffer[ index_low ] ^= ( char_index & 0xff );
291 if ( index_high >= 0 && index_high <
static_cast< int >( args.buffer_size ) )
292 args.buffer[ index_high ] ^= ( char_index >> 8 );
298 static details::attribute_access_result char_declaration_access( details::attribute_access_arguments& args, std::size_t attribute_index )
300 if ( args.type != details::attribute_access_type::read )
301 return details::attribute_access_result::write_not_permitted;
303 static constexpr bool has_write_attribute_ =
304 value_type::has_write_access && !value_type::has_only_write_without_response;
305 static constexpr bool has_write_without_response_attribute =
306 value_type::has_only_write_without_response || value_type::has_write_without_response;
308 const std::uint8_t properties[] = {
309 static_cast< std::uint8_t
>(
310 ( value_type::has_read_access ? bits( details::gatt_characteristic_properties::read ) : 0 ) |
311 ( has_write_attribute_ ? bits( details::gatt_characteristic_properties::write ) : 0 ) |
312 ( has_write_without_response_attribute ? bits( details::gatt_characteristic_properties::write_without_response ) : 0 ) |
313 ( value_type::has_notification ? bits( details::gatt_characteristic_properties::notify ) : 0 ) |
314 ( value_type::has_indication ? bits( details::gatt_characteristic_properties::indicate ) : 0 ) )
317 const std::uint16_t value_attribute_handle = handle_index_mapping< Server >::handle_by_index( attribute_index + 1 );
318 assert( value_attribute_handle != details::invalid_attribute_handle );
320 const std::uint8_t value_handle[] = {
321 static_cast< std::uint8_t
>( value_attribute_handle & 0xff ),
322 static_cast< std::uint8_t
>( value_attribute_handle >> 8 )
325 static constexpr auto data_size =
sizeof( properties ) +
sizeof( value_handle ) +
sizeof( uuid::bytes );
327 if ( args.buffer_offset > data_size )
328 return details::attribute_access_result::invalid_offset;
330 details::scattered_read_access( args.buffer_offset, properties, value_handle, uuid::bytes, args.buffer, args.buffer_size );
332 if ( characteristic_or_service_uuid_t::auto_generated_uuid )
333 fixup_auto_uuid( args );
335 args.buffer_size = std::min< std::size_t >( data_size - args.buffer_offset, args.buffer_size );
337 return details::attribute_access_result::success;
340 static const attribute attr;
343 template <
typename ... AttrOptions,
typename CCCDIndices, std::size_t ClientCharacteristicIndex,
typename ... ServiceOptions,
typename Server,
typename ... Options >
344 const attribute generate_attribute< std::tuple< characteristic_declaration_parameter, AttrOptions... >, CCCDIndices, ClientCharacteristicIndex, service< ServiceOptions... > , Server, Options... >::attr {
345 bits( details::gatt_uuids::characteristic ),
346 &generate_attribute< std::tuple< characteristic_declaration_parameter, AttrOptions... >, CCCDIndices, ClientCharacteristicIndex, service< ServiceOptions... >, Server, Options... >::char_declaration_access
352 template <
typename ... AttrOptions,
typename CCCDIndices, std::size_t ClientCharacteristicIndex,
typename Service,
typename Server,
typename ... Options >
353 struct generate_attribute< std::tuple< characteristic_value_declaration_parameter, AttrOptions... >, CCCDIndices, ClientCharacteristicIndex, Service, Server, Options... >
356 typedef typename characteristic_or_service_uuid<
typename Service::uuid, Options... >::uuid uuid;
357 using char_t = characteristic< Options... >;
358 static constexpr bool requires_encryption = characteristic_requires_encryption< char_t, Service, Server >::value;
360 static const attribute attr;
363 template <
typename ... AttrOptions,
typename CCCDIndices, std::size_t ClientCharacteristicIndex,
typename Service,
typename Server,
typename ... Options >
364 const attribute generate_attribute< std::tuple< characteristic_value_declaration_parameter, AttrOptions... >, CCCDIndices, ClientCharacteristicIndex, Service, Server, Options... >::attr {
366 ? bits( details::gatt_uuids::internal_128bit_uuid )
368 &characteristic< Options... >::value_type::template characteristic_value_access< Server, ClientCharacteristicIndex, requires_encryption >
374 template <
const char*
const Name,
typename CCCDIndices, std::size_t ClientCharacteristicIndex,
typename Service,
typename Server,
typename ... Options >
375 struct generate_attribute< std::tuple< characteristic_user_description_parameter, characteristic_name< Name > >, CCCDIndices, ClientCharacteristicIndex, Service, Server, Options... >
377 static const attribute attr;
379 static details::attribute_access_result access( attribute_access_arguments& args, std::size_t )
381 const std::size_t str_len = std::strlen( Name );
383 if ( args.buffer_offset > str_len )
384 return details::attribute_access_result::invalid_offset;
386 details::attribute_access_result result = attribute_access_result::write_not_permitted;
388 if ( args.type == attribute_access_type::read )
390 const std::size_t read_size = std::min( args.buffer_size, str_len - args.buffer_offset );
392 std::copy( Name + args.buffer_offset, Name + args.buffer_offset + read_size, args.buffer );
394 result = attribute_access_result::success;
396 args.buffer_size = read_size;
404 template <
const char*
const Name,
typename CCCDIndices, std::size_t ClientCharacteristicIndex,
typename Service,
typename Server,
typename ... Options >
405 const attribute generate_attribute< std::tuple< characteristic_user_description_parameter, characteristic_name< Name > >, CCCDIndices, ClientCharacteristicIndex, Service, Server, Options... >::attr {
406 bits( gatt_uuids::characteristic_user_description ),
407 &generate_attribute< std::tuple< characteristic_user_description_parameter, characteristic_name< Name > >, CCCDIndices, ClientCharacteristicIndex, Service, Server, Options... >::access
413 template <
typename ... AttrOptions,
typename CCCDIndices, std::size_t ClientCharacteristicIndex,
typename Service,
typename Server,
typename ... Options >
414 struct generate_attribute< std::tuple< client_characteristic_configuration_parameter, AttrOptions... >, CCCDIndices, ClientCharacteristicIndex, Service, Server, Options... >
416 static const attribute attr;
417 using uuid =
typename characteristic_or_service_uuid<
typename Service::uuid, Options... >::uuid;
419 using char_t = characteristic< Options... >;
420 static constexpr bool requires_encryption = characteristic_requires_encryption< char_t, Service, Server >::value;
422 static details::attribute_access_result access( attribute_access_arguments& args, std::size_t )
424 const auto security_result = details::encryption_requirements< requires_encryption >::check( args.connection_security );
426 if ( security_result != details::attribute_access_result::success )
427 return security_result;
429 static constexpr std::size_t flags_size = 2;
430 std::uint8_t buffer[ flags_size ];
432 if ( args.buffer_offset > flags_size )
433 return details::attribute_access_result::invalid_offset;
435 details::attribute_access_result result = attribute_access_result::write_not_permitted;
438 const std::size_t cccd_position_index = index_of< std::integral_constant< std::size_t, ClientCharacteristicIndex >, CCCDIndices >::value;
439 const std::size_t cccd_position = std::tuple_size< CCCDIndices >::value == 0
440 ? ClientCharacteristicIndex
441 : cccd_position_index;
443 if ( args.type == attribute_access_type::read )
445 write_16bit( &buffer[ 0 ], args.client_config.flags( cccd_position ) );
447 const std::size_t read_size = std::min( args.buffer_size, flags_size - args.buffer_offset );
449 std::copy( &buffer[ args.buffer_offset ], &buffer[ args.buffer_offset + read_size ], args.buffer );
451 result = attribute_access_result::success;
453 args.buffer_size = read_size;
455 else if ( args.type == attribute_access_type::write )
457 if ( args.buffer_size + args.buffer_offset > flags_size )
458 return details::attribute_access_result::invalid_attribute_value_length;
460 if ( args.buffer_offset == 0 )
462 const std::uint16_t old_config = args.client_config.flags( cccd_position );
463 std::uint8_t serialized_value[ flags_size ];
464 write_16bit( &serialized_value[ 0 ], old_config );
466 const std::size_t write_size = std::min( args.buffer_size, flags_size - args.buffer_offset );
467 std::copy( &args.buffer[ args.buffer_offset ], &args.buffer[ args.buffer_offset + write_size ], serialized_value );
469 args.client_config.flags( cccd_position, read_16bit( &serialized_value[ 0 ] ) );
471 using subscription_callback =
472 typename find_by_meta_type<
473 characteristic_subscription_call_back_meta_type,
474 Options..., default_on_characteristic_subscription >::type;
476 subscription_callback::template on_subscription< uuid >(
477 args.client_config.flags( cccd_position ),
478 *
static_cast< Server*
>( args.server ) );
480 if ( old_config != args.client_config.flags( cccd_position ) )
481 static_cast< Server*
>( args.server )->notification_subscription_changed( args.client_config );
484 result = attribute_access_result::success;
491 template <
typename ... AttrOptions,
typename CCCDIndices, std::size_t ClientCharacteristicIndex,
typename Service,
typename Server,
typename ... Options >
492 constexpr attribute generate_attribute< std::tuple< client_characteristic_configuration_parameter, AttrOptions... >, CCCDIndices, ClientCharacteristicIndex, Service, Server, Options... >::attr {
493 bits( gatt_uuids::client_characteristic_configuration ),
494 &generate_attribute< std::tuple< client_characteristic_configuration_parameter, AttrOptions... >, CCCDIndices, ClientCharacteristicIndex, Service, Server, Options... >::access
500 template < std::uint16_t UUID,
const std::uint8_t*
const Value, std::size_t Size,
typename CCCDIndices, std::size_t ClientCharacteristicIndex,
typename Service,
typename Server,
typename ... Options >
501 struct generate_attribute< std::tuple< descriptor_parameter, descriptor< UUID, Value, Size > >, CCCDIndices, ClientCharacteristicIndex, Service, Server, Options... >
503 static const attribute attr;
505 static details::attribute_access_result access( attribute_access_arguments& args, std::size_t )
507 if ( args.type != attribute_access_type::read )
508 return attribute_access_result::write_not_permitted;
510 if ( args.buffer_offset > Size )
511 return details::attribute_access_result::invalid_offset;
513 const std::size_t read_size = std::min( args.buffer_size, Size - args.buffer_offset );
515 std::copy( Value + args.buffer_offset, Value + args.buffer_offset + read_size, args.buffer );
516 args.buffer_size = read_size;
518 return attribute_access_result::success;
523 template < std::uint16_t UUID,
const std::uint8_t*
const Value, std::size_t Size,
typename CCCDIndices, std::size_t ClientCharacteristicIndex,
typename Service,
typename Server,
typename ... Options >
524 constexpr attribute generate_attribute< std::tuple< descriptor_parameter, descriptor< UUID, Value, Size > >, CCCDIndices, ClientCharacteristicIndex, Service, Server, Options... >::attr {
526 &generate_attribute< std::tuple< descriptor_parameter, descriptor< UUID, Value, Size > >, CCCDIndices, ClientCharacteristicIndex, Service, Server, Options... >::access
529 template <
typename CCCDIndices,
typename ... Options >
530 struct generate_characteristic_attributes : generate_attributes<
531 std::tuple< Options... >,
535 characteristic_declaration_parameter,
536 characteristic_value_declaration_parameter,
537 client_characteristic_configuration_parameter,
538 characteristic_user_description_parameter,
543 std::tuple< empty_meta_type< characteristic_declaration_parameter > >
546 template <
typename ... Options >
547 struct count_characteristic_attributes
549 enum { number_of_client_configs =
550 count_by_meta_type< client_characteristic_configuration_parameter, Options... >::count != 0 ? 1 : 0 };
552 enum { number_of_user_descriptions =
553 count_by_meta_type< characteristic_user_description_parameter, Options... >::count != 0 ? 1 : 0 };
555 enum { number_of_descriptors =
556 count_by_meta_type< descriptor_parameter, Options...>::count };
558 enum { number_of_attributes = 2 + number_of_client_configs + number_of_user_descriptions + number_of_descriptors };
561 template <
typename Characteristic >
562 struct sum_by_attributes
564 enum { value = Characteristic::number_of_attributes };
567 template <
typename Characteristic >
568 struct sum_by_client_configs
570 enum { value = Characteristic::number_of_client_configs };
A characteristic is a typed value that is accessable by a GATT client hosted by a GATT server.
Definition: characteristic.hpp:160
adds a name to characteristic
Definition: characteristic.hpp:227
a 16-Bit UUID used to identify a characteristic.
Definition: characteristic.hpp:79
a 128-Bit UUID used to identify a characteristic.
Definition: characteristic.hpp:62