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