BlueToe
an alternative GATT/BLE implementation
Loading...
Searching...
No Matches
csc.hpp
1#ifndef BLUETOE_SERVICES_CSC_HPP
2#define BLUETOE_SERVICES_CSC_HPP
3
4#include <bluetoe/service.hpp>
5#include <bluetoe/server.hpp>
6#include <bluetoe/sensor_location.hpp>
8#include <bluetoe/meta_types.hpp>
9
10namespace bluetoe {
11
12 namespace csc {
13
17 using service_uuid = service_uuid16< 0x1816 >;
18
19 namespace details {
21 struct handler_tag {};
22
23 struct wheel_revolution_data_handler_tag {};
24 struct crank_revolution_data_handler_tag {};
26 }
27
33 template < typename Handler >
34 struct handler
35 {
37 struct meta_type :
38 details::handler_tag,
39 ::bluetoe::details::valid_service_option_meta_type {};
40
41 typedef Handler user_handler;
43 };
44
64 struct meta_type :
65 details::wheel_revolution_data_handler_tag,
66 ::bluetoe::details::valid_service_option_meta_type {};
67
68
69 template < class T >
70 void add_wheel_mesurement( std::uint8_t& flags, std::uint8_t*& out_buffer, T& handler )
71 {
72 flags |= 0x01;
73
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 );
77 }
78
79 static constexpr std::uint16_t features = 0x0001;
81 };
82
85 struct meta_type :
86 details::crank_revolution_data_handler_tag,
87 ::bluetoe::details::valid_service_option_meta_type {};
88
89 template < class T >
90 void add_crank_mesurement( std::uint8_t& flags, std::uint8_t*& out_buffer, T& handler )
91 {
92 flags |= 0x02;
93
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 );
97 }
98
99 static constexpr std::uint16_t features = 0x0002;
101 };
102
103 namespace details {
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";
110
111 using control_point_uuid = characteristic_uuid16< 0x2A55 >;
112
113 template < typename T >
114 struct service_from_parameters;
115
116 template < typename ... Ts >
117 struct service_from_parameters< std::tuple< Ts... > > {
118 typedef service< Ts... > type;
119 };
120
121 struct no_wheel_revolution_data_supported {
122 typedef details::wheel_revolution_data_handler_tag meta_type;
123
124 template < class T >
125 void add_wheel_mesurement( std::uint8_t&, std::uint8_t*&, T& ) {}
126
127 static constexpr std::uint16_t features = 0;
128 };
129
130 struct no_crank_revolution_data_supported {
131 typedef details::crank_revolution_data_handler_tag meta_type;
132
133 template < class T >
134 void add_crank_mesurement( std::uint8_t&, std::uint8_t*&, T& ) {}
135
136 static constexpr std::uint16_t features = 0;
137 };
138
139 enum {
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
145 };
146
147 enum {
148 rc_success = 1,
149 rc_op_code_not_supported = 2,
150 rc_invalid_parameter = 3
151 };
152
153 template < typename SensorLocations >
154 class sensor_position_handler;
155
156 template < typename ... SensorLocations >
157 class sensor_position_handler< std::tuple< SensorLocations... > >
158 {
159 public:
160 sensor_position_handler()
161 : current_position_( positions_[ 0 ] )
162 , requested_position_( current_position_ )
163 {
164 }
165
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 )
167 {
168 static const std::size_t response_size = 3;
169 assert( read_size >= response_size );
170
171 out_buffer[ 0 ] = response_code_opcode;
172 out_buffer[ 1 ] = request_supported_sensor_locations_opcode;
173 out_buffer[ 2 ] = rc_success;
174
175 std::size_t max_copy = std::min( read_size - response_size, sizeof ...(SensorLocations) );
176
177 std::copy( &positions_[ 0 ], &positions_[ max_copy ], &out_buffer[ response_size ] );
178 out_size = response_size + max_copy;
179
180 return error_codes::success;
181 }
182
183 std::uint8_t update_sensor_location_opcode_response( std::size_t read_size, std::uint8_t* out_buffer, std::size_t& out_size )
184 {
185 static_cast< void >( read_size );
186 static const std::size_t response_size = 3;
187 assert( read_size >= response_size );
188
189 out_buffer[ 0 ] = response_code_opcode;
190 out_buffer[ 1 ] = update_sensor_location_opcode;
191
192 const auto loc = std::find( std::begin( positions_ ), std::end( positions_ ), requested_position_ );
193
194 if ( loc != std::end( positions_ ) )
195 {
196 out_buffer[ 2 ] = rc_success;
197 current_position_ = *loc;
198 }
199 else
200 {
201 out_buffer[ 2 ] = rc_invalid_parameter;
202 }
203
204 out_size = response_size;
205
206 return error_codes::success;
207 }
208
209 void set_sensor_position( std::uint8_t new_sensor_position )
210 {
211 requested_position_ = new_sensor_position;
212 }
213
214 std::uint8_t csc_sensor_location( std::size_t read_size, std::uint8_t* out_buffer, std::size_t& out_size )
215 {
216 static_cast< void >( read_size );
217 assert( read_size > 0 );
218
219 *out_buffer = current_position_;
220 out_size = 1;
221
222 return error_codes::success;
223 }
224
225
226 private:
227 std::uint8_t current_position_;
228 std::uint8_t requested_position_;
229 static const std::uint8_t positions_[sizeof ...(SensorLocations)];
230 };
231
232 template < typename ... SensorLocations >
233 const std::uint8_t sensor_position_handler< std::tuple< SensorLocations... > >::positions_[sizeof ...(SensorLocations)] = { SensorLocations::value... };
234
235 class no_sensor_position_handler
236 {
237 public:
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 )
239 {
240 static_cast< void >( read_size );
241 static const std::size_t response_size = 3;
242 assert( read_size >= response_size );
243
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;
248
249 return error_codes::success;
250 }
251
252 std::uint8_t update_sensor_location_opcode_response( std::size_t read_size, std::uint8_t* out_buffer, std::size_t& out_size )
253 {
254 static_cast< void >( read_size );
255 static const std::size_t response_size = 3;
256 assert( read_size >= response_size );
257
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;
262
263 return error_codes::success;
264 }
265
266 void set_sensor_position( std::uint8_t ) {}
267 };
268
269 template < class SensorPositionHandler >
270 class control_point_handler : public SensorPositionHandler
271 {
272 public:
273 control_point_handler()
274 : procedure_in_progress_( false )
275 {
276 }
277
278 std::uint8_t csc_read_control_point( std::size_t read_size, std::uint8_t* out_buffer, std::size_t& out_size )
279 {
280 procedure_in_progress_ = false;
281
282 switch ( current_opcode_ )
283 {
284 case set_cumulative_value_opcode:
285 {
286 static const std::size_t response_size = 3;
287 assert( read_size >= response_size );
288
289 out_buffer[ 0 ] = response_code_opcode;
290 out_buffer[ 1 ] = set_cumulative_value_opcode;
291 out_buffer[ 2 ] = rc_success;
292
293 out_size = response_size;
294 }
295 break;
296 case request_supported_sensor_locations_opcode:
297 {
298 return this->request_supported_sensor_locations_opcode_response( read_size, out_buffer, out_size );
299 }
300 break;
301 case update_sensor_location_opcode:
302 {
303 return this->update_sensor_location_opcode_response( read_size, out_buffer, out_size );
304 }
305 break;
306 default:
307 {
308 static const std::size_t response_size = 3;
309 assert( read_size >= response_size );
310
311 out_buffer[ 0 ] = response_code_opcode;
312 out_buffer[ 1 ] = current_opcode_;
313 out_buffer[ 2 ] = rc_op_code_not_supported;
314
315 out_size = response_size;
316 }
317 }
318
319 return error_codes::success;
320 }
321
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 )
324 {
325 if ( write_size < 1 )
326 return std::make_pair( error_codes::invalid_handle, false );
327
328 if ( procedure_in_progress_ )
329 return std::make_pair( error_codes::procedure_already_in_progress, false );
330
331 procedure_in_progress_ = true;
332 current_opcode_ = *value;
333 ++value;
334
335 switch ( current_opcode_ )
336 {
337 case set_cumulative_value_opcode:
338 {
339 if ( write_size != 1 + 4 )
340 return std::make_pair( error_codes::invalid_pdu, false );
341
342 handler.set_cumulative_wheel_revolutions( bluetoe::details::read_32bit( value ) );
343 return std::make_pair( error_codes::success, false );
344 }
345 case request_supported_sensor_locations_opcode:
346 {
347 if ( write_size != 1 )
348 return std::make_pair( error_codes::invalid_pdu, false );
349
350 return std::make_pair( error_codes::success, true );
351 }
352 case update_sensor_location_opcode:
353 {
354 if ( write_size != 1 + 1 )
355 return std::make_pair( error_codes::invalid_pdu, false );
356
357 this->set_sensor_position( *value);
358
359 return std::make_pair( error_codes::success, true );
360 }
361 default:
362 // according to the spec with have to response with success and then indicate an error
363 return std::make_pair( error_codes::success, true );
364 }
365
366 }
367 private:
368 std::uint8_t current_opcode_;
369 bool procedure_in_progress_;
370 };
371
372 struct no_control_point_handler {
373 std::uint8_t csc_read_control_point( std::size_t, std::uint8_t*, std::size_t& ) {
374 return 0;
375 }
376
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 );
380 }
381 };
382
383 template < typename Base, typename WheelHandler, typename CrankHandler, typename ControlPointHandler >
384 class implementation : public Base, WheelHandler, CrankHandler, public ControlPointHandler
385 {
386 public:
387 static constexpr std::size_t max_response_size = 1 + 4 + 2 + 2 + 2;
388
389 void csc_wheel_revolution( std::uint32_t, std::uint32_t )
390 {
391 }
392
393 std::uint8_t csc_mesurement( std::size_t read_size, std::uint8_t* out_buffer, std::size_t& out_size )
394 {
395 static_cast< void >( read_size );
396 assert( read_size >= max_response_size );
397
398 std::uint8_t* out_ptr = out_buffer + 1;
399
400 *out_buffer = 0;
401 this->add_wheel_mesurement( *out_buffer, out_ptr, *this );
402 this->add_crank_mesurement( *out_buffer, out_ptr, *this );
403
404 out_size = out_ptr - out_buffer;
405
406 return error_codes::success;;
407 }
408
409 std::uint8_t csc_read_control_point( std::size_t read_size, std::uint8_t* out_buffer, std::size_t& out_size )
410 {
411 return ControlPointHandler::csc_read_control_point( read_size, out_buffer, out_size );
412 }
413
414 std::pair< std::uint8_t, bool > csc_write_control_point( std::size_t write_size, const std::uint8_t* value )
415 {
416 return ControlPointHandler::csc_write_control_point( write_size, value, *this );
417 }
418
419 template < class Server >
420 void notify_timed_update( Server& server )
421 {
422 server.template notify< characteristic_uuid16< 0x2A5B > >();
423 }
424
425 template < class Server >
426 void confirm_cumulative_wheel_revolutions( Server& server )
427 {
428 server.template indicate< control_point_uuid >();
429 }
430 private:
431 };
432
433 template < bool HasSensorlocation, bool HasMultipleSensorlocations, typename SensorList >
434 struct select_sensorlocation_implementation;
435
436 template < typename SensorList >
437 struct select_sensorlocation_implementation< false, false, SensorList >
438 {
439 using type = std::tuple<>;
440 };
441
442 template < typename SensorPosition >
443 struct select_sensorlocation_implementation< true, false, std::tuple< SensorPosition > >
444 {
445 using type = characteristic<
446 characteristic_uuid16< 0x2A5D >,
447 characteristic_name< sensor_location_name >,
448 fixed_uint8_value< SensorPosition::value >
449 >;
450 };
451
452 template < typename SensorList >
453 struct select_sensorlocation_implementation< true, true, SensorList >
454 {
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
461 >
462 >;
463 };
464
465 template < typename ... Options >
466 struct calculate_service
467 {
468 using sensor_locations = typename bluetoe::details::find_all_by_meta_type<
469 bluetoe::details::sensor_location_meta_type, Options... >::type;
470
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;
473
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, "" );
476
477 using service_handler = typename bluetoe::details::find_by_meta_type< handler_tag, Options ... >::type;
478
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." );
481
482
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;
486
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;
489
490 using service_implementation = implementation<
491 typename service_handler::user_handler, wheel_handler, crank_handler,
493 has_control_point,
495 has_multiple_sensorlocation,
496 control_point_handler< sensor_position_handler< sensor_locations > >,
497 control_point_handler< no_sensor_position_handler >
498 >::type,
499 no_control_point_handler
500 >::type
501 >;
502
503 using mandatory_characteristics = std::tuple<
504 characteristic<
505 characteristic_uuid16< 0x2A5B >,
506 characteristic_name< measurement_name >,
511 >,
512 characteristic<
513 characteristic_uuid16< 0x2A5C >,
514 characteristic_name< feature_name >,
515 fixed_uint16_value< wheel_handler::features | crank_handler::features >
516 >
517 >;
518
519 using characteristics_with_sensorlocation = typename bluetoe::details::add_type<
520 mandatory_characteristics,
521 typename select_sensorlocation_implementation<
522 has_sensorlocation,
523 has_multiple_sensorlocation,
524 sensor_locations
525 >::type
526 >::type;
527
528 using characteristics_with_control_point =
530 has_control_point,
531 typename bluetoe::details::add_type<
532 characteristics_with_sensorlocation,
533 characteristic<
535 characteristic_name< control_point_name >,
540 >
541 >::type,
542 characteristics_with_sensorlocation
543 >::type;
544
545 using all_characteristics = characteristics_with_control_point;
546
547 using default_parameter = std::tuple<
548 mixin< service_implementation >,
549 service_uuid,
551 Options... >;
552
553 typedef typename service_from_parameters<
554 typename bluetoe::details::add_type< default_parameter, all_characteristics >::type
555 >::type type;
556 };
558 }
559 }
560
570 template < typename ... Options >
571 using cycling_speed_and_cadence = typename csc::details::calculate_service< Options... >::type;
572
577 {
578 public:
590 std::pair< std::uint32_t, std::uint16_t > cumulative_wheel_revolutions_and_time();
591
603 std::pair< std::uint16_t, std::uint16_t > cumulative_crank_revolutions_and_time();
604
612 void set_cumulative_wheel_revolutions( std::uint32_t new_value );
613
614 };
615}
616
617#endif // include guard
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
wrap< typename select_type_impl< Select >::template f< A, B > > select_type
Select A or B by Select. If Select == true, the result is A; B otherwise.
Definition: meta_tools.hpp:38
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