1#ifndef BLUETOE_SERVICES_CSC_HPP
2#define BLUETOE_SERVICES_CSC_HPP
4#include <bluetoe/service.hpp>
5#include <bluetoe/server.hpp>
6#include <bluetoe/sensor_location.hpp>
8#include <bluetoe/meta_types.hpp>
17 using service_uuid = service_uuid16< 0x1816 >;
21 struct handler_tag {};
23 struct wheel_revolution_data_handler_tag {};
24 struct crank_revolution_data_handler_tag {};
33 template <
typename Handler >
39 ::bluetoe::details::valid_service_option_meta_type {};
41 typedef Handler user_handler;
65 details::wheel_revolution_data_handler_tag,
66 ::bluetoe::details::valid_service_option_meta_type {};
70 void add_wheel_mesurement( std::uint8_t& flags, std::uint8_t*& out_buffer, T&
handler )
74 const std::pair< std::uint32_t, std::uint16_t > revolutions =
handler.cumulative_wheel_revolutions_and_time();
75 out_buffer = bluetoe::details::write_32bit( out_buffer, revolutions.first );
76 out_buffer = bluetoe::details::write_16bit( out_buffer, revolutions.second );
79 static constexpr std::uint16_t features = 0x0001;
86 details::crank_revolution_data_handler_tag,
87 ::bluetoe::details::valid_service_option_meta_type {};
90 void add_crank_mesurement( std::uint8_t& flags, std::uint8_t*& out_buffer, T&
handler )
94 const std::pair< std::uint16_t, std::uint16_t > revolutions =
handler.cumulative_crank_revolutions_and_time();
95 out_buffer = bluetoe::details::write_16bit( out_buffer, revolutions.first );
96 out_buffer = bluetoe::details::write_16bit( out_buffer, revolutions.second );
99 static constexpr std::uint16_t features = 0x0002;
105 static constexpr char service_name[] =
"Cycling Speed and Cadence";
106 static constexpr char measurement_name[] =
"CSC Measurement";
107 static constexpr char feature_name[] =
"CSC Feature";
108 static constexpr char sensor_location_name[] =
"Sensor Location";
109 static constexpr char control_point_name[] =
"SC Control Point";
113 template <
typename T >
114 struct service_from_parameters;
116 template <
typename ... Ts >
117 struct service_from_parameters< std::tuple< Ts... > > {
121 struct no_wheel_revolution_data_supported {
122 typedef details::wheel_revolution_data_handler_tag meta_type;
125 void add_wheel_mesurement( std::uint8_t&, std::uint8_t*&, T& ) {}
127 static constexpr std::uint16_t features = 0;
130 struct no_crank_revolution_data_supported {
131 typedef details::crank_revolution_data_handler_tag meta_type;
134 void add_crank_mesurement( std::uint8_t&, std::uint8_t*&, T& ) {}
136 static constexpr std::uint16_t features = 0;
140 set_cumulative_value_opcode = 1,
141 start_sensor_calibration_opcode,
142 update_sensor_location_opcode,
143 request_supported_sensor_locations_opcode,
144 response_code_opcode = 16
149 rc_op_code_not_supported = 2,
150 rc_invalid_parameter = 3
153 template <
typename SensorLocations >
154 class sensor_position_handler;
156 template <
typename ... SensorLocations >
157 class sensor_position_handler< std::tuple< SensorLocations... > >
160 sensor_position_handler()
161 : current_position_( positions_[ 0 ] )
162 , requested_position_( current_position_ )
166 std::uint8_t request_supported_sensor_locations_opcode_response( std::size_t read_size, std::uint8_t* out_buffer, std::size_t& out_size )
168 static const std::size_t response_size = 3;
169 assert( read_size >= response_size );
171 out_buffer[ 0 ] = response_code_opcode;
172 out_buffer[ 1 ] = request_supported_sensor_locations_opcode;
173 out_buffer[ 2 ] = rc_success;
175 std::size_t max_copy = std::min( read_size - response_size,
sizeof ...(SensorLocations) );
177 std::copy( &positions_[ 0 ], &positions_[ max_copy ], &out_buffer[ response_size ] );
178 out_size = response_size + max_copy;
180 return error_codes::success;
183 std::uint8_t update_sensor_location_opcode_response( std::size_t read_size, std::uint8_t* out_buffer, std::size_t& out_size )
185 static_cast< void >( read_size );
186 static const std::size_t response_size = 3;
187 assert( read_size >= response_size );
189 out_buffer[ 0 ] = response_code_opcode;
190 out_buffer[ 1 ] = update_sensor_location_opcode;
192 const auto loc = std::find( std::begin( positions_ ), std::end( positions_ ), requested_position_ );
194 if ( loc != std::end( positions_ ) )
196 out_buffer[ 2 ] = rc_success;
197 current_position_ = *loc;
201 out_buffer[ 2 ] = rc_invalid_parameter;
204 out_size = response_size;
206 return error_codes::success;
209 void set_sensor_position( std::uint8_t new_sensor_position )
211 requested_position_ = new_sensor_position;
214 std::uint8_t csc_sensor_location( std::size_t read_size, std::uint8_t* out_buffer, std::size_t& out_size )
216 static_cast< void >( read_size );
217 assert( read_size > 0 );
219 *out_buffer = current_position_;
222 return error_codes::success;
227 std::uint8_t current_position_;
228 std::uint8_t requested_position_;
229 static const std::uint8_t positions_[
sizeof ...(SensorLocations)];
232 template <
typename ... SensorLocations >
233 const std::uint8_t sensor_position_handler< std::tuple< SensorLocations... > >::positions_[
sizeof ...(SensorLocations)] = { SensorLocations::value... };
235 class no_sensor_position_handler
238 std::uint8_t request_supported_sensor_locations_opcode_response( std::size_t read_size, std::uint8_t* out_buffer, std::size_t& out_size )
240 static_cast< void >( read_size );
241 static const std::size_t response_size = 3;
242 assert( read_size >= response_size );
244 out_buffer[ 0 ] = response_code_opcode;
245 out_buffer[ 1 ] = request_supported_sensor_locations_opcode;
246 out_buffer[ 2 ] = rc_op_code_not_supported;
247 out_size = response_size;
249 return error_codes::success;
252 std::uint8_t update_sensor_location_opcode_response( std::size_t read_size, std::uint8_t* out_buffer, std::size_t& out_size )
254 static_cast< void >( read_size );
255 static const std::size_t response_size = 3;
256 assert( read_size >= response_size );
258 out_buffer[ 0 ] = response_code_opcode;
259 out_buffer[ 1 ] = update_sensor_location_opcode;
260 out_buffer[ 2 ] = rc_op_code_not_supported;
261 out_size = response_size;
263 return error_codes::success;
266 void set_sensor_position( std::uint8_t ) {}
269 template <
class SensorPositionHandler >
270 class control_point_handler :
public SensorPositionHandler
273 control_point_handler()
274 : procedure_in_progress_( false )
278 std::uint8_t csc_read_control_point( std::size_t read_size, std::uint8_t* out_buffer, std::size_t& out_size )
280 procedure_in_progress_ =
false;
282 switch ( current_opcode_ )
284 case set_cumulative_value_opcode:
286 static const std::size_t response_size = 3;
287 assert( read_size >= response_size );
289 out_buffer[ 0 ] = response_code_opcode;
290 out_buffer[ 1 ] = set_cumulative_value_opcode;
291 out_buffer[ 2 ] = rc_success;
293 out_size = response_size;
296 case request_supported_sensor_locations_opcode:
298 return this->request_supported_sensor_locations_opcode_response( read_size, out_buffer, out_size );
301 case update_sensor_location_opcode:
303 return this->update_sensor_location_opcode_response( read_size, out_buffer, out_size );
308 static const std::size_t response_size = 3;
309 assert( read_size >= response_size );
311 out_buffer[ 0 ] = response_code_opcode;
312 out_buffer[ 1 ] = current_opcode_;
313 out_buffer[ 2 ] = rc_op_code_not_supported;
315 out_size = response_size;
319 return error_codes::success;
322 template <
class Handler >
323 std::pair< std::uint8_t, bool > csc_write_control_point( std::size_t write_size,
const std::uint8_t* value, Handler& handler )
325 if ( write_size < 1 )
326 return std::make_pair( error_codes::invalid_handle,
false );
328 if ( procedure_in_progress_ )
329 return std::make_pair( error_codes::procedure_already_in_progress,
false );
331 procedure_in_progress_ =
true;
332 current_opcode_ = *value;
335 switch ( current_opcode_ )
337 case set_cumulative_value_opcode:
339 if ( write_size != 1 + 4 )
340 return std::make_pair( error_codes::invalid_pdu,
false );
342 handler.set_cumulative_wheel_revolutions( bluetoe::details::read_32bit( value ) );
343 return std::make_pair( error_codes::success,
false );
345 case request_supported_sensor_locations_opcode:
347 if ( write_size != 1 )
348 return std::make_pair( error_codes::invalid_pdu,
false );
350 return std::make_pair( error_codes::success,
true );
352 case update_sensor_location_opcode:
354 if ( write_size != 1 + 1 )
355 return std::make_pair( error_codes::invalid_pdu,
false );
357 this->set_sensor_position( *value);
359 return std::make_pair( error_codes::success,
true );
363 return std::make_pair( error_codes::success,
true );
368 std::uint8_t current_opcode_;
369 bool procedure_in_progress_;
372 struct no_control_point_handler {
373 std::uint8_t csc_read_control_point( std::size_t, std::uint8_t*, std::size_t& ) {
377 template <
class Handler >
378 std::pair< std::uint8_t, bool > csc_write_control_point( std::size_t,
const std::uint8_t*, Handler& ) {
379 return std::make_pair( 0,
false );
383 template <
typename Base,
typename WheelHandler,
typename CrankHandler,
typename ControlPo
intHandler >
384 class implementation :
public Base, WheelHandler, CrankHandler,
public ControlPointHandler
387 static constexpr std::size_t max_response_size = 1 + 4 + 2 + 2 + 2;
389 void csc_wheel_revolution( std::uint32_t, std::uint32_t )
393 std::uint8_t csc_mesurement( std::size_t read_size, std::uint8_t* out_buffer, std::size_t& out_size )
395 static_cast< void >( read_size );
396 assert( read_size >= max_response_size );
398 std::uint8_t* out_ptr = out_buffer + 1;
401 this->add_wheel_mesurement( *out_buffer, out_ptr, *
this );
402 this->add_crank_mesurement( *out_buffer, out_ptr, *
this );
404 out_size = out_ptr - out_buffer;
406 return error_codes::success;;
409 std::uint8_t csc_read_control_point( std::size_t read_size, std::uint8_t* out_buffer, std::size_t& out_size )
411 return ControlPointHandler::csc_read_control_point( read_size, out_buffer, out_size );
414 std::pair< std::uint8_t, bool > csc_write_control_point( std::size_t write_size,
const std::uint8_t* value )
416 return ControlPointHandler::csc_write_control_point( write_size, value, *
this );
419 template <
class Server >
420 void notify_timed_update( Server& server )
422 server.template notify< characteristic_uuid16< 0x2A5B > >();
425 template <
class Server >
426 void confirm_cumulative_wheel_revolutions( Server& server )
428 server.template indicate< control_point_uuid >();
433 template <
bool HasSensorlocation,
bool HasMultipleSensorlocations,
typename SensorList >
434 struct select_sensorlocation_implementation;
436 template <
typename SensorList >
437 struct select_sensorlocation_implementation< false, false, SensorList >
439 using type = std::tuple<>;
442 template <
typename SensorPosition >
443 struct select_sensorlocation_implementation< true, false, std::tuple< SensorPosition > >
445 using type = characteristic<
446 characteristic_uuid16< 0x2A5D >,
447 characteristic_name< sensor_location_name >,
448 fixed_uint8_value< SensorPosition::value >
452 template <
typename SensorList >
453 struct select_sensorlocation_implementation< true, true, SensorList >
455 using type = characteristic<
456 characteristic_uuid16< 0x2A5D >,
457 characteristic_name< sensor_location_name >,
459 sensor_position_handler< SensorList >,
460 &sensor_position_handler< SensorList >::csc_sensor_location
465 template <
typename ... Options >
466 struct calculate_service
468 using sensor_locations =
typename bluetoe::details::find_all_by_meta_type<
469 bluetoe::details::sensor_location_meta_type, Options... >::type;
471 using wheel_handler =
typename bluetoe::details::find_by_meta_type< wheel_revolution_data_handler_tag, Options..., no_wheel_revolution_data_supported >::type;
472 using crank_handler =
typename bluetoe::details::find_by_meta_type< crank_revolution_data_handler_tag, Options..., no_crank_revolution_data_supported >::type;
474 static_assert( !std::is_same< wheel_handler, bluetoe::details::no_such_type >::value,
"" );
475 static_assert( !std::is_same< crank_handler, bluetoe::details::no_such_type >::value,
"" );
477 using service_handler =
typename bluetoe::details::find_by_meta_type< handler_tag, Options ... >::type;
479 static_assert( !std::is_same< service_handler, bluetoe::details::no_such_type >::value,
480 "You need to provide a bluetoe::crc::handler<> to define how the protocol can access the messured values." );
483 static constexpr bool has_static_sensorlocation = std::tuple_size< sensor_locations >::value == 1u;
484 static constexpr bool has_multiple_sensorlocation = std::tuple_size< sensor_locations >::value > 1u;
485 static constexpr bool has_sensorlocation = has_static_sensorlocation || has_multiple_sensorlocation;
487 static constexpr bool has_set_cumulative_value = wheel_handler::features != 0;
488 static constexpr bool has_control_point = has_set_cumulative_value | has_multiple_sensorlocation;
490 using service_implementation = implementation<
491 typename service_handler::user_handler, wheel_handler, crank_handler,
495 has_multiple_sensorlocation,
496 control_point_handler< sensor_position_handler< sensor_locations > >,
497 control_point_handler< no_sensor_position_handler >
499 no_control_point_handler
503 using mandatory_characteristics = std::tuple<
505 characteristic_uuid16< 0x2A5B >,
506 characteristic_name< measurement_name >,
513 characteristic_uuid16< 0x2A5C >,
514 characteristic_name< feature_name >,
515 fixed_uint16_value< wheel_handler::features | crank_handler::features >
519 using characteristics_with_sensorlocation =
typename bluetoe::details::add_type<
520 mandatory_characteristics,
521 typename select_sensorlocation_implementation<
523 has_multiple_sensorlocation,
528 using characteristics_with_control_point =
531 typename bluetoe::details::add_type<
532 characteristics_with_sensorlocation,
535 characteristic_name< control_point_name >,
542 characteristics_with_sensorlocation
545 using all_characteristics = characteristics_with_control_point;
547 using default_parameter = std::tuple<
548 mixin< service_implementation >,
553 typedef typename service_from_parameters<
554 typename bluetoe::details::add_type< default_parameter, all_characteristics >::type
570 template <
typename ... Options >
571 using cycling_speed_and_cadence =
typename csc::details::calculate_service< Options... >::type;
bluetoe::characteristic_uuid< 0x7D295F4D, 0x2850, 0x4F57, 0xB595, 0x837F5753F8A9 > control_point_uuid
the UUID of the control point charateristic is 7D295F4D-2850-4F57-B595-837F5753F8A9
Definition: bootloader.hpp:109
Prototype for an interface type.
Definition: csc.hpp:577
std::pair< std::uint32_t, std::uint16_t > cumulative_wheel_revolutions_and_time()
void set_cumulative_wheel_revolutions(std::uint32_t new_value)
std::pair< std::uint16_t, std::uint16_t > cumulative_crank_revolutions_and_time()
a service with zero or more characteristics
Definition: service.hpp:150
a 16-Bit UUID used to identify a characteristic.
Definition: characteristic.hpp:79
parameter that adds, how the CSC service implementation gets the data needed.
Definition: csc.hpp:35
Denotes, that the csc service provides wheel revolution data.
Definition: csc.hpp:62
adds the ability to indicate this characteristic.
Definition: characteristic_value.hpp:110
Definition: characteristic_value.hpp:1071
characteristic value binding for a control point
Definition: characteristic_value.hpp:1159
if added as option to a characteristic, read access is removed from the characteristic
Definition: characteristic_value.hpp:47
if added as option to a characteristic, write access is removed from the characteristic
Definition: characteristic_value.hpp:69
adds the ability to notify this characteristic.
Definition: characteristic_value.hpp:89
Definition: service.hpp:22