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