xref: /btstack/src/ble/gatt-service/battery_service_v1_server.c (revision 164aeebb51d5be4b61cee1430c4d8fd0dae0eec9)
1 /*
2  * Copyright (C) 2014 BlueKitchen GmbH
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  *
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. Neither the name of the copyright holders nor the names of
14  *    contributors may be used to endorse or promote products derived
15  *    from this software without specific prior written permission.
16  * 4. Any redistribution, use, or modification is done solely for
17  *    personal benefit and not for any commercial purpose or for
18  *    monetary gain.
19  *
20  * THIS SOFTWARE IS PROVIDED BY BLUEKITCHEN GMBH AND CONTRIBUTORS
21  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
23  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BLUEKITCHEN
24  * GMBH OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
25  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
26  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
27  * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
28  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
29  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
30  * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31  * SUCH DAMAGE.
32  *
33  * Please inquire about commercial licensing options at
34  * [email protected]
35  *
36  */
37 
38 #define BTSTACK_FILE__ "battery_service_v1_server.c"
39 
40 /**
41  * Implementation of the GATT Battery Service Server
42  * To use with your application, add `#import <battery_service.gatt>` to your .gatt file
43  */
44 #include <stdio.h>
45 #include "btstack_defines.h"
46 #include "ble/att_db.h"
47 #include "ble/att_server.h"
48 #include "btstack_util.h"
49 #include "bluetooth_gatt.h"
50 #include "btstack_debug.h"
51 
52 #include "ble/gatt-service/battery_service_v1_server.h"
53 
54 #define BAS_TASK_BATTERY_LEVEL_CHANGED                                  0x0001
55 #define BAS_TASK_BATTERY_LEVEL_STATUS_CHANGED                           0x0002
56 #define BAS_TASK_ESTIMATED_SERVICE_DATE_CHANGED                         0x0004
57 #define BAS_TASK_BATTERY_CRITCAL_STATUS_CHANGED                         0x0008
58 #define BAS_TASK_BATTERY_ENERGY_STATUS_CHANGED                          0x0010
59 #define BAS_TASK_BATTERY_TIME_STATUS_CHANGED                            0x0020
60 #define BAS_TASK_BATTERY_HEALTH_STATUS_CHANGED                          0x0040
61 #define BAS_TASK_BATTERY_HEALTH_INFORMATION_CHANGED                     0x0080
62 #define BAS_TASK_BATTERY_INFORMATION_CHANGED                            0x0100
63 #define BAS_TASK_MANUFACTURER_NAME_STRING_CHANGED                       0x0200
64 #define BAS_TASK_MODEL_NUMBER_STRING_CHANGED                            0x0400
65 #define BAS_TASK_SERIAL_NUMBER_STRING_CHANGED                           0x0800
66 
67 // list of uuids
68 static const uint16_t bas_uuid16s[BAS_CHARACTERISTIC_INDEX_NUM] = {
69     ORG_BLUETOOTH_CHARACTERISTIC_BATTERY_LEVEL,
70     ORG_BLUETOOTH_CHARACTERISTIC_BATTERY_LEVEL_STATUS,
71     ORG_BLUETOOTH_CHARACTERISTIC_ESTIMATED_SERVICE_DATE,
72     ORG_BLUETOOTH_CHARACTERISTIC_BATTERY_CRITCAL_STATUS,
73     ORG_BLUETOOTH_CHARACTERISTIC_BATTERY_ENERGY_STATUS,
74     ORG_BLUETOOTH_CHARACTERISTIC_BATTERY_TIME_STATUS,
75     ORG_BLUETOOTH_CHARACTERISTIC_BATTERY_HEALTH_STATUS,
76     ORG_BLUETOOTH_CHARACTERISTIC_BATTERY_HEALTH_INFORMATION,
77     ORG_BLUETOOTH_CHARACTERISTIC_BATTERY_INFORMATION,
78     ORG_BLUETOOTH_CHARACTERISTIC_MANUFACTURER_NAME_STRING,
79     ORG_BLUETOOTH_CHARACTERISTIC_MODEL_NUMBER_STRING,
80     ORG_BLUETOOTH_CHARACTERISTIC_SERIAL_NUMBER_STRING,
81 };
82 
83 static const char * bas_uuid16_name[BAS_CHARACTERISTIC_INDEX_NUM] = {
84     "BATTERY_LEVEL",
85     "BATTERY_LEVEL_STATUS",
86     "ESTIMATED_SERVICE_DATE",
87     "BATTERY_CRITCAL_STATUS",
88     "BATTERY_ENERGY_STATUS",
89     "BATTERY_TIME_STATUS",
90     "BATTERY_HEALTH_STATUS",
91     "BATTERY_HEALTH_INFORMATION",
92     "BATTERY_INFORMATION",
93     "MANUFACTURER_NAME_STRING",
94     "MODEL_NUMBER_STRING",
95     "SERIAL_NUMBER_STRING",
96 };
97 
98 static btstack_linked_list_t battery_services;
99 
100 #define MEDFLOAT16_POSITIVE_INFINITY            0x07FE
101 #define MEDFLOAT16_NOT_A_NUMBER                 0x07FF
102 #define MEDFLOAT16_NOT_AT_THIS_RESOLUTION       0x0800
103 #define MEDFLOAT16_RFU                          0x0801
104 #define MEDFLOAT16_NEGATIVE_INFINITY            0x0802
105 
106 static bool bas_server_medfloat16_is_real_number(uint16_t value_medfloat16){
107     switch (value_medfloat16){
108         case MEDFLOAT16_POSITIVE_INFINITY:
109         case MEDFLOAT16_NOT_A_NUMBER:
110         case MEDFLOAT16_NOT_AT_THIS_RESOLUTION:
111         case MEDFLOAT16_RFU:
112         case MEDFLOAT16_NEGATIVE_INFINITY:
113             return false;
114         default:
115             return true;
116     }
117 }
118 
119 static uint16_t bas_server_get_task_for_characteristic_index(bas_characteristic_index_t index){
120     switch (index){
121         case BAS_CHARACTERISTIC_INDEX_BATTERY_LEVEL:
122             return BAS_TASK_BATTERY_LEVEL_CHANGED;
123         case BAS_CHARACTERISTIC_INDEX_BATTERY_LEVEL_STATUS:
124             return BAS_TASK_BATTERY_LEVEL_STATUS_CHANGED;
125         case BAS_CHARACTERISTIC_INDEX_ESTIMATED_SERVICE_DATE:
126             return BAS_TASK_ESTIMATED_SERVICE_DATE_CHANGED;
127         case BAS_CHARACTERISTIC_INDEX_BATTERY_CRITCAL_STATUS:
128             return BAS_TASK_BATTERY_CRITCAL_STATUS_CHANGED;
129         case BAS_CHARACTERISTIC_INDEX_BATTERY_ENERGY_STATUS:
130             return BAS_TASK_BATTERY_ENERGY_STATUS_CHANGED;
131         case BAS_CHARACTERISTIC_INDEX_BATTERY_TIME_STATUS:
132             return BAS_TASK_BATTERY_TIME_STATUS_CHANGED;
133         case BAS_CHARACTERISTIC_INDEX_BATTERY_HEALTH_STATUS:
134             return BAS_TASK_BATTERY_HEALTH_STATUS_CHANGED;
135         case BAS_CHARACTERISTIC_INDEX_BATTERY_HEALTH_INFORMATION:
136             return BAS_TASK_BATTERY_HEALTH_INFORMATION_CHANGED;
137         case BAS_CHARACTERISTIC_INDEX_BATTERY_INFORMATION:
138             return BAS_TASK_BATTERY_INFORMATION_CHANGED;
139         case BAS_CHARACTERISTIC_INDEX_MANUFACTURER_NAME_STRING:
140             return BAS_TASK_MANUFACTURER_NAME_STRING_CHANGED;
141         case BAS_CHARACTERISTIC_INDEX_MODEL_NUMBER_STRING:
142             return BAS_TASK_MODEL_NUMBER_STRING_CHANGED;
143         case BAS_CHARACTERISTIC_INDEX_SERIAL_NUMBER_STRING:
144             return BAS_TASK_SERIAL_NUMBER_STRING_CHANGED;
145         default:
146             btstack_assert(false);
147             return 0;
148     }
149 }
150 
151 static battery_service_v1_server_connection_t * battery_service_server_connection_for_con_handle(battery_service_v1_t * service, hci_con_handle_t con_handle){
152     if (service == NULL){
153         return NULL;
154     }
155 
156     uint8_t i;
157     for (i = 0; i < service->connections_max_num; i++){
158         if (service->connections[i].con_handle == con_handle){
159             return &service->connections[i];
160         }
161     }
162     return NULL;
163 }
164 
165 static battery_service_v1_server_connection_t * battery_service_server_add_connection_for_con_handle(battery_service_v1_t * service, hci_con_handle_t con_handle){
166     if (service == NULL){
167         return NULL;
168     }
169 
170     uint8_t i;
171     for (i = 0; i < service->connections_max_num; i++){
172         if (service->connections[i].con_handle == HCI_CON_HANDLE_INVALID){
173             service->connections[i].con_handle = con_handle;
174             service->connections[i].service = service;
175             return &service->connections[i];
176         }
177     }
178     return NULL;
179 }
180 
181 
182 static battery_service_v1_t * battery_service_service_for_attribute_handle(uint16_t attribute_handle){
183     btstack_linked_list_iterator_t it;
184     btstack_linked_list_iterator_init(&it, &battery_services);
185     while (btstack_linked_list_iterator_has_next(&it)){
186         battery_service_v1_t * item = (battery_service_v1_t*) btstack_linked_list_iterator_next(&it);
187         if (attribute_handle < item->service_handler.start_handle) continue;
188         if (attribute_handle > item->service_handler.end_handle)   continue;
189         return item;
190     }
191     return NULL;
192 }
193 
194 
195 static battery_service_v1_t * battery_service_service_for_con_handle(hci_con_handle_t con_handle){
196     btstack_linked_list_iterator_t it;
197     btstack_linked_list_iterator_init(&it, &battery_services);
198     while (btstack_linked_list_iterator_has_next(&it)){
199         battery_service_v1_t * service = (battery_service_v1_t*) btstack_linked_list_iterator_next(&it);
200         uint8_t i;
201         for (i = 0; i < service->connections_max_num; i++){
202             if (service->connections[i].con_handle == con_handle){
203                 return service;
204             }
205         }
206     }
207     return NULL;
208 }
209 
210 static uint8_t bas_serialize_characteristic(battery_service_v1_t * service, bas_characteristic_index_t index, uint8_t * event, uint8_t event_size){
211     uint8_t pos = 0;
212     switch ((bas_characteristic_index_t) index){
213         case BAS_CHARACTERISTIC_INDEX_BATTERY_LEVEL:
214             event[pos++] = service->battery_level;
215             break;
216 
217         case BAS_CHARACTERISTIC_INDEX_BATTERY_LEVEL_STATUS:
218             if (service->level_status == NULL){
219                 return 0;
220             }
221             event[pos++] = service->level_status->flags;
222             little_endian_store_16(event, pos, service->level_status->power_state_flags);
223             pos += 2;
224             if ((service->level_status->flags & BATTERY_LEVEL_STATUS_BITMASK_IDENTIFIER_PRESENT) > 0u){
225                 little_endian_store_16(event, pos, service->level_status->identifier);
226                 pos += 2;
227             }
228             if ((service->level_status->flags & BATTERY_LEVEL_STATUS_BITMASK_BATTERY_LEVEL_PRESENT) > 0u){
229                 event[pos++] = service->level_status->battery_level;
230             }
231             if ((service->level_status->flags & BATTERY_LEVEL_STATUS_BITMASK_ADDITIONAL_STATUS_PRESENT) > 0u){
232                 event[pos++] = service->level_status->additional_status_flags;
233             }
234             break;
235 
236         case BAS_CHARACTERISTIC_INDEX_ESTIMATED_SERVICE_DATE:
237             little_endian_store_24(event, pos, service->estimated_service_date_days);
238             pos += 3;
239             break;
240 
241         case BAS_CHARACTERISTIC_INDEX_BATTERY_CRITCAL_STATUS:
242             event[pos++] = service->critcal_status_flags;
243             break;
244 
245         case BAS_CHARACTERISTIC_INDEX_BATTERY_ENERGY_STATUS:
246             if (service->energy_status == NULL){
247                 return 0;
248             }
249             event[pos++] = service->energy_status->flags;
250             if ((service->energy_status->flags & BATTERY_ENERGY_STATUS_BITMASK_EXTERNAL_SOURCE_POWER_PRESENT) > 0u){
251                 little_endian_store_16(event, pos, service->energy_status->external_source_power_medfloat16);
252                 pos += 2;
253             }
254             if ((service->energy_status->flags & BATTERY_ENERGY_STATUS_BITMASK_PRESENT_VOLTAGE_PRESENT) > 0u){
255                 little_endian_store_16(event, pos, service->energy_status->present_voltage_medfloat16);
256                 pos += 2;
257             }
258             if ((service->energy_status->flags & BATTERY_ENERGY_STATUS_BITMASK_AVAILABLE_ENERGY_PRESENT) > 0u){
259                 little_endian_store_16(event, pos, service->energy_status->available_energy_medfloat16);
260                 pos += 2;
261             }
262             if ((service->energy_status->flags & BATTERY_ENERGY_STATUS_BITMASK_AVAILABLE_BATTERY_CAPACITY_PRESENT) > 0u){
263                 little_endian_store_16(event, pos, service->energy_status->available_battery_capacity_medfloat16);
264                 pos += 2;
265             }
266             if ((service->energy_status->flags & BATTERY_ENERGY_STATUS_BITMASK_CHARGE_RATE_PRESENT) > 0u){
267                 little_endian_store_16(event, pos, service->energy_status->charge_rate_medfloat16);
268                 pos += 2;
269             }
270             if ((service->energy_status->flags & BATTERY_ENERGY_STATUS_BITMASK_AVAILABLE_ENERGY_AT_LAST_CHARGE_PRESENT) > 0u){
271                 little_endian_store_16(event, pos, service->energy_status->available_energy_at_last_charge_medfloat16);
272                 pos += 2;
273             }
274             break;
275 
276         case BAS_CHARACTERISTIC_INDEX_BATTERY_TIME_STATUS:
277             if (service->time_status == NULL){
278                 return 0;
279             }
280             event[pos++] = service->time_status->flags;
281             little_endian_store_24(event, pos, service->time_status->time_until_discharged_minutes);
282             pos += 3;
283             if ((service->time_status->flags & BATTERY_TIME_STATUS_BITMASK_TIME_UNTIL_DISCHARGED_ON_STANDBY_PRESENT) > 0u){
284                 little_endian_store_24(event, pos, service->time_status->time_until_discharged_on_standby_minutes);
285                 pos += 3;
286             }
287             if ((service->time_status->flags & BATTERY_TIME_STATUS_BITMASK_TIME_UNTIL_RECHARGED_PRESENT) > 0u){
288                 little_endian_store_24(event, pos, service->time_status->time_until_recharged_minutes);
289                 pos += 3;
290             }
291             break;
292 
293         case BAS_CHARACTERISTIC_INDEX_BATTERY_HEALTH_STATUS:
294             if (service->health_status == NULL){
295                 return 0;
296             }
297             event[pos++] = service->health_status->flags;
298             if ((service->health_status->flags & BATTERY_HEALTH_STATUS_BITMASK_HEALTH_SUMMARY_PRESENT) > 0u){
299                 event[pos++] = service->health_status->summary;
300             }
301             if ((service->health_status->flags & BATTERY_HEALTH_STATUS_BITMASK_CYCLE_COUNT_PRESENT) > 0u){
302                 little_endian_store_16(event, pos, service->health_status->cycle_count);
303                 pos += 2;
304             }
305             if ((service->health_status->flags & BATTERY_HEALTH_STATUS_BITMASK_CURRENT_TEMPERATURE_PRESENT) > 0u){
306                 event[pos++] = service->health_status->current_temperature_degree_celsius;
307             }
308             if ((service->health_status->flags & BATTERY_HEALTH_STATUS_BITMASK_DEEP_DISCHARGE_COUNT_PRESENT) > 0u){
309                 little_endian_store_16(event, pos, service->health_status->deep_discharge_count);
310                 pos += 2;
311             }
312             break;
313 
314         case BAS_CHARACTERISTIC_INDEX_BATTERY_HEALTH_INFORMATION:
315             if (service->health_information == NULL){
316                 return 0;
317             }
318             event[pos++] = service->health_information->flags;
319             if ((service->health_information->flags & BATTERY_HEALTH_INFORMATION_BITMASK_CYCLE_COUNT_DESIGNED_LIFETIME_PRESENT) > 0u){
320                 little_endian_store_16(event, pos, service->health_information->cycle_count_designed_lifetime);
321                 pos += 2;
322             }
323             if ((service->health_information->flags & BATTERY_HEALTH_INFORMATION_BITMASK_DESIGNED_OPERATING_TEMPERATURE_PRESENT) > 0u){
324                 event[pos++] = service->health_information->min_designed_operating_temperature_degree_celsius;
325             }
326             if ((service->health_information->flags & BATTERY_HEALTH_INFORMATION_BITMASK_DESIGNED_OPERATING_TEMPERATURE_PRESENT) > 0u){
327                 event[pos++] = service->health_information->max_designed_operating_temperature_degree_celsius;
328             }
329             break;
330 
331         case BAS_CHARACTERISTIC_INDEX_BATTERY_INFORMATION:
332             if (service->information == NULL){
333                 return 0;
334             }
335             little_endian_store_16(event, pos, service->information->flags);
336             pos += 2;
337             event[pos++] = service->information->features;
338             if ((service->information->flags & BATTERY_INFORMATION_BITMASK_MANUFACTURE_DATE_PRESENT) > 0u){
339                 little_endian_store_24(event, pos, service->information->manufacture_date_days);
340                 pos += 3;
341             }
342             if ((service->information->flags & BATTERY_INFORMATION_BITMASK_EXPIRATION_DATE_PRESENT) > 0u){
343                 little_endian_store_24(event, pos, service->information->expiration_date_days);
344                 pos += 3;
345             }
346             if ((service->information->flags & BATTERY_INFORMATION_BITMASK_DESIGNED_CAPACITY_PRESENT) > 0u){
347                 little_endian_store_16(event, pos, service->information->designed_capacity_kWh_medfloat16);
348                 pos += 2;
349             }
350             if ((service->information->flags & BATTERY_INFORMATION_BITMASK_LOW_ENERGY_PRESENT) > 0u){
351                 little_endian_store_16(event, pos, service->information->low_energy_kWh_medfloat16);
352                 pos += 2;
353             }
354             if ((service->information->flags & BATTERY_INFORMATION_BITMASK_CRITICAL_ENERGY_PRESENT) > 0u){
355                 little_endian_store_16(event, pos, service->information->critical_energy_kWh_medfloat16);
356                 pos += 2;
357             }
358             if ((service->information->flags & BATTERY_INFORMATION_BITMASK_CHEMISTRY_PRESENT) > 0u){
359                 event[pos++] = service->information->chemistry;
360             }
361             if ((service->information->flags & BATTERY_INFORMATION_BITMASK_NOMINAL_VOLTAGE_PRESENT) > 0u){
362                 little_endian_store_16(event, pos, service->information->nominal_voltage_medfloat16);
363                 pos += 2;
364             }
365             if ((service->information->flags & BATTERY_INFORMATION_BITMASK_AGGREGATION_GROUP_PRESENT) > 0u){
366                 event[pos++] = service->information->aggregation_group;
367             }
368             break;
369         default:
370             break;
371     }
372     return pos;
373 }
374 
375 static uint16_t battery_service_read_callback(hci_con_handle_t con_handle, uint16_t attribute_handle, uint16_t offset, uint8_t * buffer, uint16_t buffer_size){
376     UNUSED(con_handle);
377 
378     battery_service_v1_t * service = battery_service_service_for_attribute_handle(attribute_handle);
379     if (service == NULL){
380         return 0;
381     }
382 
383     battery_service_v1_server_connection_t * connection = battery_service_server_connection_for_con_handle(service, con_handle);
384     if (connection == NULL){
385         connection = battery_service_server_add_connection_for_con_handle(service, con_handle);
386         if (connection == NULL){
387             return 0;
388         }
389     }
390 
391     uint8_t index;
392     uint8_t event[19];
393     uint8_t pos = 0;
394 
395     for (index = 0; index < (uint8_t) BAS_CHARACTERISTIC_INDEX_NUM; index++){
396         if (attribute_handle != service->characteristics[index].value_handle){
397             continue;
398         }
399 
400         switch ((bas_characteristic_index_t) index){
401             case BAS_CHARACTERISTIC_INDEX_MANUFACTURER_NAME_STRING:
402                 if (service->manufacturer_name == NULL){
403                     return 0;
404                 }
405                 return att_read_callback_handle_blob((uint8_t *)service->manufacturer_name, strlen(service->manufacturer_name), offset, buffer, buffer_size);
406 
407             case BAS_CHARACTERISTIC_INDEX_MODEL_NUMBER_STRING:
408                 if (service->model_number == NULL){
409                     return 0;
410                 }
411                 return att_read_callback_handle_blob((uint8_t *)service->model_number, strlen(service->model_number), offset, buffer, buffer_size);
412 
413             case BAS_CHARACTERISTIC_INDEX_SERIAL_NUMBER_STRING:
414                 if (service->serial_number == NULL){
415                     return 0;
416                 }
417                 return att_read_callback_handle_blob((uint8_t *)service->serial_number, strlen(service->serial_number), offset, buffer, buffer_size);
418 
419             default:
420                 pos = bas_serialize_characteristic(service, index, event, sizeof(event));
421                 if (pos == 1u){
422                     return att_read_callback_handle_byte(event[0], offset, buffer, buffer_size);
423                 }
424                 if (pos > 1u){
425                     return att_read_callback_handle_blob(event, pos, offset, buffer, buffer_size);
426                 }
427                 return 0;
428         }
429     }
430 
431     if (attribute_handle == service->battery_level_status_broadcast_configuration_handle){
432         return att_read_callback_handle_little_endian_16(service->battery_level_status_broadcast_configuration, offset, buffer, buffer_size);
433     }
434 
435     for (index = 0; index < (uint8_t) BAS_CHARACTERISTIC_INDEX_NUM; index++){
436         if (attribute_handle != service->characteristics[index].client_configuration_handle){
437             continue;
438         }
439         return att_read_callback_handle_little_endian_16(connection->configurations[index], offset, buffer, buffer_size);
440     }
441     return 0;
442 }
443 
444 static int battery_service_write_callback(hci_con_handle_t con_handle, uint16_t attribute_handle, uint16_t transaction_mode, uint16_t offset, uint8_t *buffer, uint16_t buffer_size){
445     UNUSED(offset);
446     UNUSED(buffer_size);
447 
448     if (transaction_mode != ATT_TRANSACTION_MODE_NONE){
449         return 0;
450     }
451 
452     battery_service_v1_t * service = battery_service_service_for_attribute_handle(attribute_handle);
453     if (service == NULL){
454         return 0;
455     }
456 
457     battery_service_v1_server_connection_t * connection = battery_service_server_connection_for_con_handle(service, con_handle);
458     if (connection == NULL){
459         connection = battery_service_server_add_connection_for_con_handle(service, con_handle);
460         if (connection == NULL){
461             return 0;
462         }
463     }
464 
465     if (attribute_handle == service->battery_level_status_broadcast_configuration_handle){
466         service->battery_level_status_broadcast_configuration = little_endian_read_16(buffer, 0);
467         return 0;
468     }
469 
470     uint8_t index;
471     for (index = 0; index < (uint8_t) BAS_CHARACTERISTIC_INDEX_NUM; index++){
472         if (attribute_handle != service->characteristics[index].client_configuration_handle){
473             continue;
474         }
475         connection->configurations[index] = little_endian_read_16(buffer, 0);
476         return 0;
477     }
478     return 0;
479 }
480 
481 
482 static bool bas_characteristic_notify_configured(battery_service_v1_server_connection_t * connection, bas_characteristic_index_t index){
483     return (connection->configurations[index] & GATT_CLIENT_CHARACTERISTICS_CONFIGURATION_NOTIFICATION) != 0u;
484 }
485 static bool bas_characteristic_indicate_configured(battery_service_v1_server_connection_t * connection, bas_characteristic_index_t index){
486     return (connection->configurations[index] & GATT_CLIENT_CHARACTERISTICS_CONFIGURATION_INDICATION) != 0u;
487 }
488 
489 static void battery_service_can_send_now(void * context){
490     battery_service_v1_server_connection_t * connection = (battery_service_v1_server_connection_t *) context;
491     if (connection == NULL){
492         return;
493     }
494     battery_service_v1_t * service = connection->service;
495     if (service == NULL){
496         return;
497     }
498 
499     // if battery is removed, no indications or notification should be sent
500 //    if ( (service->level_status->flags & BATTERY_LEVEL_STATUS_BITMASK_BATTERY_LEVEL_PRESENT) == 0u){
501 //        return;
502 //    }
503 
504     bas_characteristic_index_t index;
505     uint8_t event[19];
506     uint8_t pos = 0;
507     bool task_valid = true;
508 
509 
510     if ((connection->scheduled_tasks & BAS_TASK_BATTERY_LEVEL_CHANGED) > 0u){
511         connection->scheduled_tasks &= ~BAS_TASK_BATTERY_LEVEL_CHANGED;
512         index = BAS_CHARACTERISTIC_INDEX_BATTERY_LEVEL;
513 
514     } else if ((connection->scheduled_tasks & BAS_TASK_BATTERY_LEVEL_STATUS_CHANGED) > 0u){
515         connection->scheduled_tasks &= ~BAS_TASK_BATTERY_LEVEL_STATUS_CHANGED;
516         index = BAS_CHARACTERISTIC_INDEX_BATTERY_LEVEL_STATUS;
517 
518     } else if ((connection->scheduled_tasks & BAS_TASK_ESTIMATED_SERVICE_DATE_CHANGED) > 0u){
519         connection->scheduled_tasks &= ~BAS_TASK_ESTIMATED_SERVICE_DATE_CHANGED;
520         index = BAS_CHARACTERISTIC_INDEX_ESTIMATED_SERVICE_DATE;
521 
522     } else if ((connection->scheduled_tasks & BAS_TASK_BATTERY_CRITCAL_STATUS_CHANGED) > 0u){
523         connection->scheduled_tasks &= ~BAS_TASK_BATTERY_CRITCAL_STATUS_CHANGED;
524         index = BAS_CHARACTERISTIC_INDEX_BATTERY_CRITCAL_STATUS;
525 
526     } else if ((connection->scheduled_tasks & BAS_TASK_BATTERY_ENERGY_STATUS_CHANGED) > 0u){
527         connection->scheduled_tasks &= ~BAS_TASK_BATTERY_ENERGY_STATUS_CHANGED;
528         index = BAS_CHARACTERISTIC_INDEX_BATTERY_ENERGY_STATUS;
529 
530     } else if ((connection->scheduled_tasks & BAS_TASK_BATTERY_TIME_STATUS_CHANGED) > 0u){
531         connection->scheduled_tasks &= ~BAS_TASK_BATTERY_TIME_STATUS_CHANGED;
532         index = BAS_CHARACTERISTIC_INDEX_BATTERY_TIME_STATUS;
533 
534     } else if ((connection->scheduled_tasks & BAS_TASK_BATTERY_HEALTH_STATUS_CHANGED) > 0u){
535         connection->scheduled_tasks &= ~BAS_TASK_BATTERY_HEALTH_STATUS_CHANGED;
536         index = BAS_CHARACTERISTIC_INDEX_BATTERY_HEALTH_STATUS;
537 
538     } else if ((connection->scheduled_tasks & BAS_TASK_BATTERY_HEALTH_INFORMATION_CHANGED) > 0u){
539         connection->scheduled_tasks &= ~BAS_TASK_BATTERY_HEALTH_INFORMATION_CHANGED;
540         index = BAS_CHARACTERISTIC_INDEX_BATTERY_HEALTH_STATUS;
541 
542     } else if ((connection->scheduled_tasks & BAS_TASK_BATTERY_INFORMATION_CHANGED) > 0u){
543         connection->scheduled_tasks &= ~BAS_TASK_BATTERY_INFORMATION_CHANGED;
544         index = BAS_CHARACTERISTIC_INDEX_BATTERY_INFORMATION;
545 
546     } else {
547         // TODO  BAS_TASK_MANUFACTURER_NAME_STRING_CHANGED
548         // TODO  BAS_TASK_MODEL_NUMBER_STRING_CHANGED
549         // TODO  BAS_TASK_SERIAL_NUMBER_STRING_CHANGED
550 
551         task_valid = false;
552     }
553 
554     if (task_valid){
555         pos = bas_serialize_characteristic(service, index, event, sizeof(event));
556 
557         if (bas_characteristic_notify_configured(connection, index)){
558             att_server_notify(connection->con_handle, service->characteristics[index].value_handle, event, pos);
559         } else if (bas_characteristic_indicate_configured(connection, index)){
560             att_server_notify(connection->con_handle, service->characteristics[index].value_handle, event, pos);
561         }
562     }
563 
564     if (connection->scheduled_tasks > 0u){
565         att_server_register_can_send_now_callback(&connection->scheduled_tasks_callback, connection->con_handle);
566     }
567 }
568 
569 void battery_service_v1_server_init(void){
570 
571 }
572 
573 void battery_service_v1_server_register(battery_service_v1_t *service, battery_service_v1_server_connection_t *connections, uint8_t connection_max_num){
574     btstack_assert(service != NULL);
575     btstack_assert(connections != NULL);
576     btstack_assert(connection_max_num > 0u);
577 
578     // get service handle range
579     uint16_t end_handle   = 0xffff;
580     uint16_t start_handle = 0;
581 
582     btstack_linked_list_iterator_t it;
583     btstack_linked_list_iterator_init(&it, &battery_services);
584     while (btstack_linked_list_iterator_has_next(&it)){
585         battery_service_v1_t * service = (battery_service_v1_t*) btstack_linked_list_iterator_next(&it);
586         if (service->service_handler.end_handle > start_handle){
587             start_handle = service->service_handler.end_handle + 1;
588         }
589     }
590 
591     int service_found = gatt_server_get_handle_range_for_service_with_uuid16(ORG_BLUETOOTH_SERVICE_BATTERY_SERVICE, &start_handle, &end_handle);
592     btstack_assert(service_found != 0);
593     UNUSED(service_found);
594 
595     service->service_handler.start_handle = start_handle;
596     service->service_handler.end_handle = end_handle;
597     printf("start handle 0x%04X, end0x%04X\n", service->service_handler.start_handle , service->service_handler.end_handle);
598     uint8_t i;
599     for (i = 0; i < (uint8_t) BAS_CHARACTERISTIC_INDEX_NUM; i++){
600         // get characteristic value handle and client configuration handle
601         service->characteristics[i].value_handle = gatt_server_get_value_handle_for_characteristic_with_uuid16(start_handle, end_handle, bas_uuid16s[i]);
602         service->characteristics[i].client_configuration_handle = gatt_server_get_client_configuration_handle_for_characteristic_with_uuid16(start_handle, end_handle, bas_uuid16s[i]);
603         printf("%30s: 0x%04X, CCC 0x%04X\n", bas_uuid16_name[i], service->characteristics[i].value_handle , service->characteristics[i].client_configuration_handle );
604     }
605 
606     service->battery_level_status_broadcast_configuration_handle = gatt_server_get_server_configuration_handle_for_characteristic_with_uuid16(start_handle, end_handle, ORG_BLUETOOTH_CHARACTERISTIC_BATTERY_LEVEL_STATUS);
607 
608     memset(connections, 0, sizeof(battery_service_v1_server_connection_t) * connection_max_num);
609     for (i = 0; i < connection_max_num; i++){
610         connections[i].con_handle = HCI_CON_HANDLE_INVALID;
611     }
612     service->connections_max_num = connection_max_num;
613     service->connections = connections;
614 
615     service->service_handler.start_handle = start_handle;
616     service->service_handler.end_handle = end_handle;
617     service->service_handler.read_callback  = &battery_service_read_callback;
618     service->service_handler.write_callback = &battery_service_write_callback;
619     att_server_register_service_handler(&service->service_handler);
620 
621     log_info("Found Battery Service 0x%02x-0x%02x", start_handle, end_handle);
622 
623     btstack_linked_list_add(&battery_services, (btstack_linked_item_t *) service);
624 }
625 
626 
627 static void bas_server_set_callback_for_connection(battery_service_v1_server_connection_t * connection, bas_characteristic_index_t index, uint8_t task){
628     if (connection->con_handle == HCI_CON_HANDLE_INVALID){
629         connection->scheduled_tasks = 0;
630         return;
631     }
632 
633     uint8_t scheduled_tasks = connection->scheduled_tasks;
634     connection->scheduled_tasks |= task;
635     if (scheduled_tasks == 0){
636         connection->scheduled_tasks_callback.callback = &battery_service_can_send_now;
637         connection->scheduled_tasks_callback.context  = (void*) connection;
638         att_server_register_can_send_now_callback(&connection->scheduled_tasks_callback, connection->con_handle);
639     }
640 }
641 
642 static void bas_server_set_callback(battery_service_v1_t * service, bas_characteristic_index_t index){
643     // if battery is removed, no indications or notification should be sent
644 
645     if (service->level_status == NULL){
646         return;
647     }
648     if ((index != BAS_CHARACTERISTIC_INDEX_BATTERY_LEVEL_STATUS) && (service->level_status->flags & BATTERY_LEVEL_STATUS_BITMASK_BATTERY_LEVEL_PRESENT) == 0u){
649         return;
650     }
651     uint8_t task = bas_server_get_task_for_characteristic_index(index);
652     uint8_t i;
653     for (i = 0; i < service->connections_max_num; i++){
654         if (service->connections[i].configurations[index] > 0u){
655             bas_server_set_callback_for_connection(&service->connections[i], index, task);
656         }
657     }
658 }
659 
660 uint8_t battery_service_v1_server_set_battery_level(battery_service_v1_t * service, uint8_t battery_level){
661     btstack_assert(service != NULL);
662     if (battery_level > 100){
663         return ERROR_CODE_PARAMETER_OUT_OF_MANDATORY_RANGE;
664     }
665     if (service->battery_level != battery_level){
666         service->battery_level = battery_level;
667         bas_server_set_callback(service, BAS_CHARACTERISTIC_INDEX_BATTERY_LEVEL);
668     }
669     return ERROR_CODE_SUCCESS;
670 }
671 
672 uint8_t battery_service_v1_server_set_battery_level_status(battery_service_v1_t * service, const battery_level_status_t * battery_level_status){
673     btstack_assert(service != NULL);
674     btstack_assert(battery_level_status != NULL);
675 
676     if ((battery_level_status->flags & BATTERY_LEVEL_STATUS_BITMASK_RFU) != 0u){
677         return ERROR_CODE_PARAMETER_OUT_OF_MANDATORY_RANGE;
678     }
679     if ((battery_level_status->flags & BATTERY_LEVEL_STATUS_BITMASK_ADDITIONAL_STATUS_PRESENT) > 0u){
680        if ((battery_level_status->additional_status_flags & BATTERY_LEVEL_ADDITIONAL_STATUS_BITMASK_RFU) != 0u){
681             return ERROR_CODE_PARAMETER_OUT_OF_MANDATORY_RANGE;
682         }
683     }
684 
685     service->level_status = battery_level_status;
686     bas_server_set_callback(service, BAS_CHARACTERISTIC_INDEX_BATTERY_LEVEL_STATUS);
687     return ERROR_CODE_SUCCESS;
688 }
689 
690 uint8_t battery_service_v1_server_set_estimated_service_date_days(battery_service_v1_t * service, uint32_t estimated_service_date_days){
691     btstack_assert(service != NULL);
692 
693     if (estimated_service_date_days > 0xFFFFFF){
694         return ERROR_CODE_PARAMETER_OUT_OF_MANDATORY_RANGE;
695     }
696 
697     service->estimated_service_date_days = estimated_service_date_days;
698     bas_server_set_callback(service, BAS_CHARACTERISTIC_INDEX_ESTIMATED_SERVICE_DATE);
699     return ERROR_CODE_SUCCESS;
700 }
701 
702 uint8_t battery_service_v1_server_set_critcal_status_flags(battery_service_v1_t * service, uint8_t critcal_status_flags){
703     btstack_assert(service != NULL);
704 
705     if ((critcal_status_flags & BATTERY_CRITCAL_STATUS_BITMASK_RFU) != 0u){
706         return ERROR_CODE_PARAMETER_OUT_OF_MANDATORY_RANGE;
707     }
708 
709     service->critcal_status_flags = critcal_status_flags;
710     bas_server_set_callback(service, BAS_CHARACTERISTIC_INDEX_BATTERY_CRITCAL_STATUS);
711     return ERROR_CODE_SUCCESS;
712 }
713 
714 uint8_t battery_service_v1_server_set_energy_status(battery_service_v1_t * service, const battery_energy_status_t * energy_status){
715     btstack_assert(service != NULL);
716     btstack_assert(energy_status != NULL);
717 
718     if ((energy_status->flags & BATTERY_ENERGY_STATUS_BITMASK_RFU) != 0u){
719         return ERROR_CODE_PARAMETER_OUT_OF_MANDATORY_RANGE;
720     }
721 
722     service->energy_status = energy_status;
723     bas_server_set_callback(service, BAS_CHARACTERISTIC_INDEX_BATTERY_ENERGY_STATUS);
724     return ERROR_CODE_SUCCESS;
725 }
726 
727 uint8_t battery_service_v1_server_set_time_status(battery_service_v1_t * service, const battery_time_status_t * time_status){
728     btstack_assert(service != NULL);
729     btstack_assert(time_status != NULL);
730 
731     if ((time_status->flags & BATTERY_TIME_STATUS_BITMASK_RFU) != 0u){
732         return ERROR_CODE_PARAMETER_OUT_OF_MANDATORY_RANGE;
733     }
734     if (time_status->time_until_discharged_minutes > 0xFFFFFF){
735         return ERROR_CODE_PARAMETER_OUT_OF_MANDATORY_RANGE;
736     }
737     if ((time_status->flags & BATTERY_TIME_STATUS_BITMASK_TIME_UNTIL_DISCHARGED_ON_STANDBY_PRESENT) > 0u){
738         if (time_status->time_until_discharged_on_standby_minutes > 0xFFFFFF){
739             return ERROR_CODE_PARAMETER_OUT_OF_MANDATORY_RANGE;
740         }
741     }
742     if ((time_status->flags & BATTERY_TIME_STATUS_BITMASK_TIME_UNTIL_RECHARGED_PRESENT) > 0u){
743         if (time_status->time_until_recharged_minutes > 0xFFFFFF){
744             return ERROR_CODE_PARAMETER_OUT_OF_MANDATORY_RANGE;
745         }
746     }
747 
748     service->time_status = time_status;
749     bas_server_set_callback(service, BAS_CHARACTERISTIC_INDEX_BATTERY_TIME_STATUS);
750     return ERROR_CODE_SUCCESS;
751 }
752 
753 uint8_t battery_service_v1_server_set_health_status(battery_service_v1_t * service, const battery_health_status_t * health_status){
754     btstack_assert(service != NULL);
755     btstack_assert(health_status != NULL);
756 
757     if ((health_status->flags & BATTERY_HEALTH_STATUS_BITMASK_RFU) != 0u){
758         return ERROR_CODE_PARAMETER_OUT_OF_MANDATORY_RANGE;
759     }
760     if ((health_status->flags & BATTERY_HEALTH_STATUS_BITMASK_HEALTH_SUMMARY_PRESENT) > 0u){
761         if (health_status->summary > 100){
762             return ERROR_CODE_PARAMETER_OUT_OF_MANDATORY_RANGE;
763         }
764     }
765 
766     service->health_status = health_status;
767     bas_server_set_callback(service, BAS_CHARACTERISTIC_INDEX_BATTERY_HEALTH_STATUS);
768     return ERROR_CODE_SUCCESS;
769 }
770 
771 uint8_t battery_service_v1_server_set_health_information(battery_service_v1_t * service, const battery_health_information_t * health_information){
772     btstack_assert(service != NULL);
773     btstack_assert(health_information != NULL);
774 
775     if ((health_information->flags & BATTERY_HEALTH_INFORMATION_BITMASK_RFU) != 0u){
776         return ERROR_CODE_PARAMETER_OUT_OF_MANDATORY_RANGE;
777     }
778 
779     service->health_information = health_information;
780     bas_server_set_callback(service, BAS_CHARACTERISTIC_INDEX_BATTERY_HEALTH_INFORMATION);
781     return ERROR_CODE_SUCCESS;
782 }
783 
784 uint8_t battery_service_v1_server_set_information(battery_service_v1_t * service, const battery_information_t * information){
785     btstack_assert(service != NULL);
786     btstack_assert(information != NULL);
787 
788     if ((information->flags & BATTERY_INFORMATION_BITMASK_RFU) != 0u){
789         return ERROR_CODE_PARAMETER_OUT_OF_MANDATORY_RANGE;
790     }
791     if ((information->flags & BATTERY_INFORMATION_BITMASK_MANUFACTURE_DATE_PRESENT) > 0u){
792         if (information->manufacture_date_days > 0xFFFFFF){
793             return ERROR_CODE_PARAMETER_OUT_OF_MANDATORY_RANGE;
794         }
795     }
796     if ((information->flags & BATTERY_INFORMATION_BITMASK_EXPIRATION_DATE_PRESENT) > 0u){
797         if (information->expiration_date_days > 0xFFFFFF){
798             return ERROR_CODE_PARAMETER_OUT_OF_MANDATORY_RANGE;
799         }
800     }
801     if ((information->flags & BATTERY_INFORMATION_BITMASK_CHEMISTRY_PRESENT) > 0u){
802         if (information->chemistry >= BATTERY_CHEMISTRY_RFU_START && information->chemistry <= BATTERY_CHEMISTRY_RFU_END){
803             return ERROR_CODE_PARAMETER_OUT_OF_MANDATORY_RANGE;
804         }
805     }
806     if ((information->flags & BATTERY_INFORMATION_BITMASK_AGGREGATION_GROUP_PRESENT) > 0u){
807         if (information->aggregation_group == 0xFF){
808             return ERROR_CODE_PARAMETER_OUT_OF_MANDATORY_RANGE;
809         }
810     }
811     if ((information->flags & BATTERY_INFORMATION_BITMASK_DESIGNED_CAPACITY_PRESENT) > 0u){
812         if (!bas_server_medfloat16_is_real_number(information->designed_capacity_kWh_medfloat16)){
813             return ERROR_CODE_PARAMETER_OUT_OF_MANDATORY_RANGE;
814         }
815     }
816     if ((information->flags & BATTERY_INFORMATION_BITMASK_LOW_ENERGY_PRESENT) > 0u){
817         if (!bas_server_medfloat16_is_real_number(information->low_energy_kWh_medfloat16)){
818             return ERROR_CODE_PARAMETER_OUT_OF_MANDATORY_RANGE;
819         }
820     }
821     if ((information->flags & BATTERY_INFORMATION_BITMASK_CRITICAL_ENERGY_PRESENT) > 0u){
822         if (!bas_server_medfloat16_is_real_number(information->critical_energy_kWh_medfloat16)){
823             return ERROR_CODE_PARAMETER_OUT_OF_MANDATORY_RANGE;
824         }
825     }
826     if ((information->flags & BATTERY_INFORMATION_BITMASK_NOMINAL_VOLTAGE_PRESENT) > 0u){
827         if (!bas_server_medfloat16_is_real_number(information->nominal_voltage_medfloat16)){
828             return ERROR_CODE_PARAMETER_OUT_OF_MANDATORY_RANGE;
829         }
830     }
831     service->information = information;
832     bas_server_set_callback(service, BAS_CHARACTERISTIC_INDEX_BATTERY_INFORMATION);
833     return ERROR_CODE_SUCCESS;
834 }
835 
836 uint8_t battery_service_v1_server_set_manufacturer_name(battery_service_v1_t * service, const char * manufacturer_name){
837     btstack_assert(service != NULL);
838     btstack_assert(manufacturer_name != NULL);
839 
840     service->manufacturer_name = manufacturer_name;
841     bas_server_set_callback(service, BAS_CHARACTERISTIC_INDEX_MANUFACTURER_NAME_STRING);
842     return ERROR_CODE_SUCCESS;
843 }
844 
845 uint8_t battery_service_v1_server_set_model_number(battery_service_v1_t * service, const char * model_number){
846     btstack_assert(service != NULL);
847     btstack_assert(model_number != NULL);
848 
849     service->model_number = model_number;
850     bas_server_set_callback(service, BAS_CHARACTERISTIC_INDEX_MODEL_NUMBER_STRING);
851     return ERROR_CODE_SUCCESS;
852 }
853 
854 uint8_t battery_service_v1_server_set_serial_number(battery_service_v1_t * service, const char * serial_number){
855     btstack_assert(service != NULL);
856     btstack_assert(serial_number != NULL);
857 
858     service->serial_number = serial_number;
859     bas_server_set_callback(service, BAS_CHARACTERISTIC_INDEX_SERIAL_NUMBER_STRING);
860     return ERROR_CODE_SUCCESS;
861 }
862 
863 void battery_service_v1_server_deregister(battery_service_v1_t *service){
864     btstack_linked_list_remove(&battery_services, (btstack_linked_item_t * )service);
865     // TODO cansel can send now
866 }
867 
868 void battery_service_v1_server_deinit(void){
869     // deregister listeners
870     // empty list
871     btstack_linked_list_iterator_t it;
872     btstack_linked_list_iterator_init(&it, &battery_services);
873     while (btstack_linked_list_iterator_has_next(&it)){
874         battery_service_v1_t * service = (battery_service_v1_t*) btstack_linked_list_iterator_next(&it);
875         battery_service_v1_server_deregister(service);
876     }
877 }
878