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