xref: /btstack/src/ble/gatt-service/battery_service_v1_server.c (revision db9fdd680281f095072c4fdafe372d045866923c)
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 
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 BATTERY_SERVICE_TASK_BATTERY_VALUE_CHANGED                          0x0001
55 
56 // list of uuids
57 static const uint16_t bas_uuid16s[BAS_CHARACTERISTIC_INDEX_NUM] = {
58         ORG_BLUETOOTH_CHARACTERISTIC_BATTERY_LEVEL,
59         ORG_BLUETOOTH_CHARACTERISTIC_BATTERY_LEVEL_STATUS,
60         ORG_BLUETOOTH_CHARACTERISTIC_ESTIMATED_SERVICE_DATE,
61         ORG_BLUETOOTH_CHARACTERISTIC_BATTERY_CRITCAL_STATUS,
62         ORG_BLUETOOTH_CHARACTERISTIC_BATTERY_ENERGY_STATUS,
63         ORG_BLUETOOTH_CHARACTERISTIC_BATTERY_TIME_STATUS,
64         ORG_BLUETOOTH_CHARACTERISTIC_BATTERY_HEALTH_STATUS,
65         ORG_BLUETOOTH_CHARACTERISTIC_BATTERY_HEALTH_INFORMATION,
66         ORG_BLUETOOTH_CHARACTERISTIC_BATTERY_INFORMATION,
67         ORG_BLUETOOTH_CHARACTERISTIC_MANUFACTURER_NAME_STRING,
68         ORG_BLUETOOTH_CHARACTERISTIC_MODEL_NUMBER_STRING,
69         ORG_BLUETOOTH_CHARACTERISTIC_SERIAL_NUMBER_STRING,
70 };
71 
72 static btstack_linked_list_t battery_services;
73 
74 
75 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){
76     if (service == NULL){
77         return NULL;
78     }
79 
80     uint8_t i;
81     for (i = 0; i < service->connections_max_num; i++){
82         if (service->connections[i].con_handle == con_handle){
83             return &service->connections[i];
84         }
85     }
86     return NULL;
87 }
88 
89 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){
90     if (service == NULL){
91         return NULL;
92     }
93 
94     uint8_t i;
95     for (i = 0; i < service->connections_max_num; i++){
96         if (service->connections[i].con_handle == HCI_CON_HANDLE_INVALID){
97             service->connections[i].con_handle = con_handle;
98             service->connections[i].service = service;
99             return &service->connections[i];
100         }
101     }
102     return NULL;
103 }
104 
105 
106 static battery_service_v1_t * battery_service_service_for_attribute_handle(uint16_t attribute_handle){
107     btstack_linked_list_iterator_t it;
108     btstack_linked_list_iterator_init(&it, &battery_services);
109     while (btstack_linked_list_iterator_has_next(&it)){
110         battery_service_v1_t * item = (battery_service_v1_t*) btstack_linked_list_iterator_next(&it);
111         if (attribute_handle < item->start_handle) continue;
112         if (attribute_handle > item->end_handle)   continue;
113         return item;
114     }
115     return NULL;
116 }
117 
118 
119 static battery_service_v1_t * battery_service_service_for_con_handle(hci_con_handle_t con_handle){
120     btstack_linked_list_iterator_t it;
121     btstack_linked_list_iterator_init(&it, &battery_services);
122     while (btstack_linked_list_iterator_has_next(&it)){
123         battery_service_v1_t * service = (battery_service_v1_t*) btstack_linked_list_iterator_next(&it);
124         uint8_t i;
125         for (i = 0; i < service->connections_max_num; i++){
126             if (service->connections[i].con_handle == con_handle){
127                 return service;
128             }
129         }
130     }
131     return NULL;
132 }
133 
134 static uint8_t bas_serialize_characteristic(battery_service_v1_t * service, bas_characteristic_index_t index, uint8_t * event, uint8_t event_size){
135     uint8_t pos = 0;
136     switch ((bas_characteristic_index_t) index){
137         case BAS_CHARACTERISTIC_INDEX_BATTERY_LEVEL:
138             event[pos++] = service->battery_value;
139             break;
140 
141         case BAS_CHARACTERISTIC_INDEX_BATTERY_LEVEL_STATUS:
142             if (service->level_status == NULL){
143                 return 0;
144             }
145             event[pos++] = service->level_status->flags;
146             little_endian_store_16(event, pos, service->level_status->power_state_flags);
147             pos += 2;
148             if ((service->level_status->flags & BATTERY_LEVEL_STATUS_BITMASK_IDENTIFIER_PRESENT) > 0u){
149                 little_endian_store_16(event, pos, service->level_status->identifier);
150                 pos += 2;
151             }
152             if ((service->level_status->flags & BATTERY_LEVEL_STATUS_BITMASK_BATTERY_LEVEL_PRESENT) > 0u){
153                 event[pos++] = service->level_status->battery_level;
154             }
155             if ((service->level_status->flags & BATTERY_LEVEL_STATUS_BITMASK_ADDITIONAL_STATUS_PRESENT) > 0u){
156                 event[pos++] = service->level_status->additional_status_flags;
157             }
158             break;
159 
160         case BAS_CHARACTERISTIC_INDEX_ESTIMATED_SERVICE_DATE:
161             little_endian_store_24(event, pos, service->estimated_service_date_days);
162             pos += 3;
163             break;
164 
165         case BAS_CHARACTERISTIC_INDEX_BATTERY_CRITCAL_STATUS:
166             event[pos++] = service->battery_critcal_status_flags;
167             break;
168 
169         case BAS_CHARACTERISTIC_INDEX_BATTERY_ENERGY_STATUS:
170             if (service->energy_status == NULL){
171                 return 0;
172             }
173             event[pos++] = service->energy_status->flags;
174             if ((service->energy_status->flags & BATTERY_ENERGY_STATUS_BITMASK_EXTERNAL_SOURCE_POWER_PRESENT) > 0u){
175                 little_endian_store_16(event, pos, service->energy_status->external_source_power_medfloat16);
176                 pos += 2;
177             }
178             if ((service->energy_status->flags & BATTERY_ENERGY_STATUS_BITMASK_PRESENT_VOLTAGE_PRESENT) > 0u){
179                 little_endian_store_16(event, pos, service->energy_status->present_voltage_medfloat16);
180                 pos += 2;
181             }
182             if ((service->energy_status->flags & BATTERY_ENERGY_STATUS_BITMASK_AVAILABLE_ENERGY_PRESENT) > 0u){
183                 little_endian_store_16(event, pos, service->energy_status->available_energy_medfloat16);
184                 pos += 2;
185             }
186             if ((service->energy_status->flags & BATTERY_ENERGY_STATUS_BITMASK_AVAILABLE_BATTERY_CAPACITY_PRESENT) > 0u){
187                 little_endian_store_16(event, pos, service->energy_status->available_battery_capacity_medfloat16);
188                 pos += 2;
189             }
190             if ((service->energy_status->flags & BATTERY_ENERGY_STATUS_BITMASK_CHARGE_RATE_PRESENT) > 0u){
191                 little_endian_store_16(event, pos, service->energy_status->charge_rate_medfloat16);
192                 pos += 2;
193             }
194             if ((service->energy_status->flags & BATTERY_ENERGY_STATUS_BITMASK_AVAILABLE_ENERGY_AT_LAST_CHARGE_PRESENT) > 0u){
195                 little_endian_store_16(event, pos, service->energy_status->available_energy_at_last_charge_medfloat16);
196                 pos += 2;
197             }
198             break;
199 
200         case BAS_CHARACTERISTIC_INDEX_BATTERY_TIME_STATUS:
201             if (service->time_status == NULL){
202                 return 0;
203             }
204             event[pos++] = service->time_status->flags;
205             little_endian_store_24(event, pos, service->time_status->time_until_discharged_minutes);
206             pos += 3;
207             if ((service->time_status->flags & BATTERY_TIME_STATUS_BITMASK_TIME_UNTIL_DISCHARGED_ON_STANDBY_PRESENT) > 0u){
208                 little_endian_store_24(event, pos, service->time_status->time_until_discharged_on_standby_minutes);
209                 pos += 3;
210             }
211             if ((service->time_status->flags & BATTERY_TIME_STATUS_BITMASK_TIME_UNTIL_RECHARGED_PRESENT) > 0u){
212                 little_endian_store_24(event, pos, service->time_status->time_until_recharged_minutes);
213                 pos += 3;
214             }
215             break;
216 
217         case BAS_CHARACTERISTIC_INDEX_BATTERY_HEALTH_STATUS:
218             if (service->health_status == NULL){
219                 return 0;
220             }
221             event[pos++] = service->health_status->flags;
222             if ((service->health_status->flags & BATTERY_HEALTH_STATUS_BITMASK_HEALTH_SUMMARY_PRESENT) > 0u){
223                 event[pos++] = service->health_status->summary;
224             }
225             if ((service->health_status->flags & BATTERY_HEALTH_STATUS_BITMASK_CYCLE_COUNT_PRESENT) > 0u){
226                 little_endian_store_16(event, pos, service->health_status->cycle_count);
227                 pos += 2;
228             }
229             if ((service->health_status->flags & BATTERY_HEALTH_STATUS_BITMASK_CURRENT_TEMPERATURE_PRESENT) > 0u){
230                 event[pos++] = service->health_status->current_temperature_degree_celsius;
231             }
232             if ((service->health_status->flags & BATTERY_HEALTH_STATUS_BITMASK_DEEP_DISCHARGE_COUNT_PRESENT) > 0u){
233                 little_endian_store_16(event, pos, service->health_status->deep_discharge_count);
234                 pos += 2;
235             }
236             break;
237 
238         case BAS_CHARACTERISTIC_INDEX_BATTERY_HEALTH_INFORMATION:
239             if (service->health_information == NULL){
240                 return 0;
241             }
242             event[pos++] = service->health_information->flags;
243             if ((service->health_information->flags & BATTERY_HEALTH_INFORMATION_BITMASK_CYCLE_COUNT_DESIGNED_LIFETIME_PRESENT) > 0u){
244                 little_endian_store_16(event, pos, service->health_information->cycle_count_designed_lifetime);
245                 pos += 2;
246             }
247             if ((service->health_information->flags & BATTERY_HEALTH_INFORMATION_BITMASK_DESIGNED_OPERATING_TEMPERATURE_PRESENT) > 0u){
248                 event[pos++] = service->health_information->min_designed_operating_temperature_degree_celsius;
249             }
250             if ((service->health_information->flags & BATTERY_HEALTH_INFORMATION_BITMASK_DESIGNED_OPERATING_TEMPERATURE_PRESENT) > 0u){
251                 event[pos++] = service->health_information->max_designed_operating_temperature_degree_celsius;
252             }
253             break;
254 
255         case BAS_CHARACTERISTIC_INDEX_BATTERY_INFORMATION:
256             if (service->information == NULL){
257                 return 0;
258             }
259             little_endian_store_16(event, pos, service->information->flags);
260             pos += 2;
261             event[pos++] = service->information->features;
262             if ((service->information->flags & BATTERY_INFORMATION_BITMASK_MANUFACTURE_DATE_PRESENT) > 0u){
263                 little_endian_store_24(event, pos, service->information->manufacture_date_days);
264                 pos += 3;
265             }
266             if ((service->information->flags & BATTERY_INFORMATION_BITMASK_EXPIRATION_DATE_PRESENT) > 0u){
267                 little_endian_store_24(event, pos, service->information->expiration_date_days);
268                 pos += 3;
269             }
270             if ((service->information->flags & BATTERY_INFORMATION_BITMASK_DESIGNED_CAPACITY_PRESENT) > 0u){
271                 little_endian_store_16(event, pos, service->information->designed_capacity_kWh_medfloat16);
272                 pos += 2;
273             }
274             if ((service->information->flags & BATTERY_INFORMATION_BITMASK_LOW_ENERGY_PRESENT) > 0u){
275                 little_endian_store_16(event, pos, service->information->low_energy_kWh_medfloat16);
276                 pos += 2;
277             }
278             if ((service->information->flags & BATTERY_INFORMATION_BITMASK_CRITICAL_ENERGY_PRESENT) > 0u){
279                 little_endian_store_16(event, pos, service->information->critical_energy_kWh_medfloat16);
280                 pos += 2;
281             }
282             if ((service->information->flags & BATTERY_INFORMATION_BITMASK_CHEMISTRY_PRESENT) > 0u){
283                 event[pos++] = service->information->chemistry;
284             }
285             if ((service->information->flags & BATTERY_INFORMATION_BITMASK_NOMINAL_VOLTAGE_PRESENT) > 0u){
286                 little_endian_store_16(event, pos, service->information->nominal_voltage_medfloat16);
287                 pos += 2;
288             }
289             if ((service->information->flags & BATTERY_INFORMATION_BITMASK_AGGREGATION_GROUP_PRESENT) > 0u){
290                 event[pos++] = service->information->aggregation_group;
291             }
292             break;
293         default:
294             break;
295     }
296     return pos;
297 }
298 
299 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){
300     UNUSED(con_handle);
301 
302     battery_service_v1_t * service = battery_service_service_for_attribute_handle(attribute_handle);
303     if (service == NULL){
304         return 0;
305     }
306 
307     battery_service_v1_server_connection_t * connection = battery_service_server_connection_for_con_handle(service, con_handle);
308     if (connection == NULL){
309         connection = battery_service_server_add_connection_for_con_handle(service, con_handle);
310         if (connection == NULL){
311             return 0;
312         }
313     }
314 
315     uint8_t index;
316     uint8_t event[18];
317     uint8_t pos = 0;
318 
319     for (index = 0; index < (uint8_t) BAS_CHARACTERISTIC_INDEX_NUM; index++){
320         if (attribute_handle != service->characteristics[index].value_handle){
321             continue;
322         }
323 
324         switch ((bas_characteristic_index_t) index){
325             case BAS_CHARACTERISTIC_INDEX_MANUFACTURER_NAME_STRING:
326                 if (service->manufacturer_name == NULL){
327                     return 0;
328                 }
329                 return att_read_callback_handle_blob((uint8_t *)service->manufacturer_name, strlen(service->manufacturer_name), offset, buffer, buffer_size);
330 
331             case BAS_CHARACTERISTIC_INDEX_MODEL_NUMBER_STRING:
332                 if (service->model_number == NULL){
333                     return 0;
334                 }
335                 return att_read_callback_handle_blob((uint8_t *)service->model_number, strlen(service->model_number), offset, buffer, buffer_size);
336 
337             case BAS_CHARACTERISTIC_INDEX_SERIAL_NUMBER_STRING:
338                 if (service->serial_number == NULL){
339                     return 0;
340                 }
341                 return att_read_callback_handle_blob((uint8_t *)service->serial_number, strlen(service->serial_number), offset, buffer, buffer_size);
342 
343             default:
344                 pos = bas_serialize_characteristic(service, index, event, sizeof(event));
345                 if (pos == 1u){
346                     return att_read_callback_handle_byte(event[0], offset, buffer, buffer_size);
347                 }
348                 if (pos > 1u){
349                     return att_read_callback_handle_blob(event, pos, offset, buffer, buffer_size);
350                 }
351                 return 0;
352         }
353     }
354 
355     for (index = 0; index < (uint8_t) BAS_CHARACTERISTIC_INDEX_NUM; index++){
356         if (attribute_handle != service->characteristics[index].client_configuration_handle){
357             continue;
358         }
359         return att_read_callback_handle_little_endian_16(connection->configurations[index], offset, buffer, buffer_size);
360     }
361     return 0;
362 }
363 
364 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){
365     UNUSED(offset);
366     UNUSED(buffer_size);
367 
368     if (transaction_mode != ATT_TRANSACTION_MODE_NONE){
369         return 0;
370     }
371 
372     battery_service_v1_t * service = battery_service_service_for_attribute_handle(attribute_handle);
373     if (service == NULL){
374         return 0;
375     }
376 
377     battery_service_v1_server_connection_t * connection = battery_service_server_connection_for_con_handle(service, con_handle);
378     if (connection == NULL){
379         connection = battery_service_server_add_connection_for_con_handle(service, con_handle);
380         if (connection == NULL){
381             return 0;
382         }
383     }
384 
385     uint8_t index;
386     for (index = 0; index < (uint8_t) BAS_CHARACTERISTIC_INDEX_NUM; index++){
387         if (attribute_handle != service->characteristics[index].client_configuration_handle){
388             continue;
389         }
390         connection->configurations[index] = little_endian_read_16(buffer, 0);
391     }
392     return 0;
393 }
394 
395 static void battery_service_can_send_now(void * context){
396     battery_service_v1_server_connection_t * connection = (battery_service_v1_server_connection_t *) context;
397     if (connection == NULL){
398         return;
399     }
400     battery_service_v1_t * service = connection->service;
401     if (service == NULL){
402         return;
403     }
404 
405     if ( (connection->scheduled_tasks & BATTERY_SERVICE_TASK_BATTERY_VALUE_CHANGED) > 0u){
406         connection->scheduled_tasks &= ~BATTERY_SERVICE_TASK_BATTERY_VALUE_CHANGED;
407         att_server_notify(connection->con_handle, service->characteristics[BAS_CHARACTERISTIC_INDEX_BATTERY_LEVEL].value_handle, &service->battery_value, 1);
408     }
409 
410     if (connection->scheduled_tasks > 0u){
411         att_server_register_can_send_now_callback(&connection->scheduled_tasks_callback, connection->con_handle);
412     }
413 }
414 
415 void battery_service_v1_server_init(void){
416 
417 }
418 
419 void battery_service_v1_server_register(battery_service_v1_t *service, battery_service_v1_server_connection_t *connections, uint8_t connection_max_num){
420     btstack_assert(service != NULL);
421     btstack_assert(connections != NULL);
422     btstack_assert(connection_max_num > 0u);
423 
424     // get service handle range
425     uint16_t end_handle   = 0xffff;
426     uint16_t start_handle = 0;
427 
428     btstack_linked_list_iterator_t it;
429     btstack_linked_list_iterator_init(&it, &battery_services);
430     while (btstack_linked_list_iterator_has_next(&it)){
431         battery_service_v1_t * service = (battery_service_v1_t*) btstack_linked_list_iterator_next(&it);
432         if (service->end_handle > start_handle){
433             start_handle = service->end_handle + 1;
434         }
435     }
436 
437     int service_found = gatt_server_get_handle_range_for_service_with_uuid16(ORG_BLUETOOTH_SERVICE_BATTERY_SERVICE, &start_handle, &end_handle);
438     btstack_assert(service_found != 0);
439     UNUSED(service_found);
440 
441     service->start_handle = start_handle;
442     service->end_handle   = end_handle;
443 
444     uint8_t i;
445     for (i = 0; i < (uint8_t) BAS_CHARACTERISTIC_INDEX_NUM; i++){
446         // get characteristic value handle and client configuration handle
447         service->characteristics[i].value_handle = gatt_server_get_value_handle_for_characteristic_with_uuid16(start_handle, end_handle, bas_uuid16s[i]);
448         service->characteristics[i].client_configuration_handle = gatt_server_get_client_configuration_handle_for_characteristic_with_uuid16(start_handle, end_handle, bas_uuid16s[i]);
449     }
450 
451     memset(connections, 0, sizeof(battery_service_v1_server_connection_t) * connection_max_num);
452     for (i = 0; i < connection_max_num; i++){
453         connections[i].con_handle = HCI_CON_HANDLE_INVALID;
454     }
455     service->connections_max_num = connection_max_num;
456     service->connections = connections;
457 
458     service->service_handler.read_callback  = &battery_service_read_callback;
459     service->service_handler.write_callback = &battery_service_write_callback;
460     att_server_register_service_handler(&service->service_handler);
461 
462     log_info("Found Battery Service 0x%02x-0x%02x", start_handle, end_handle);
463 
464     btstack_linked_list_add(&battery_services, (btstack_linked_item_t *) service);
465 }
466 
467 
468 static void battery_service_set_callback(battery_service_v1_server_connection_t * connection, uint8_t task){
469     if (connection->con_handle == HCI_CON_HANDLE_INVALID){
470         connection->scheduled_tasks = 0;
471         return;
472     }
473 
474     uint8_t scheduled_tasks = connection->scheduled_tasks;
475     connection->scheduled_tasks |= task;
476     if (scheduled_tasks == 0){
477         connection->scheduled_tasks_callback.callback = &battery_service_can_send_now;
478         connection->scheduled_tasks_callback.context  = (void*) connection;
479         att_server_register_can_send_now_callback(&connection->scheduled_tasks_callback, connection->con_handle);
480     }
481 }
482 
483 
484 void battery_service_v1_server_set_battery_value(battery_service_v1_t * service, uint8_t value){
485     btstack_assert(service != NULL);
486     if (service->battery_value == value){
487         return;
488     }
489 
490     service->battery_value = value;
491 
492     uint8_t i;
493     for (i = 0; i < service->connections_max_num; i++){
494         battery_service_v1_server_connection_t * connection = &service->connections[i];
495         if (connection->configurations[BAS_CHARACTERISTIC_INDEX_BATTERY_LEVEL] != 0){
496             battery_service_set_callback(connection, BATTERY_SERVICE_TASK_BATTERY_VALUE_CHANGED);
497         }
498     }
499 }
500 
501 void battery_service_v1_server_deregister(battery_service_v1_t *service){
502     btstack_linked_list_remove(&battery_services, (btstack_linked_item_t * )service);
503     // TODO cansel can send now
504 }
505 
506 void battery_service_v1_server_deinit(void){
507     // deregister listeners
508     // empty list
509     btstack_linked_list_iterator_t it;
510     btstack_linked_list_iterator_init(&it, &battery_services);
511     while (btstack_linked_list_iterator_has_next(&it)){
512         battery_service_v1_t * service = (battery_service_v1_t*) btstack_linked_list_iterator_next(&it);
513         battery_service_v1_server_deregister(service);
514     }
515 }
516