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