diff --git a/src/ble/gatt-service/battery_service_v1_server.c b/src/ble/gatt-service/battery_service_v1_server.c index 29c690ab4..44e81a7d7 100644 --- a/src/ble/gatt-service/battery_service_v1_server.c +++ b/src/ble/gatt-service/battery_service_v1_server.c @@ -49,11 +49,26 @@ #include "bluetooth_gatt.h" #include "btstack_debug.h" -#define BS_CONNECTIONS_MAX_NUM 10 #include "ble/gatt-service/battery_service_v1_server.h" #define BATTERY_SERVICE_TASK_BATTERY_VALUE_CHANGED 0x0001 +// list of uuids +static const uint16_t bas_uuid16s[BAS_CHARACTERISTIC_INDEX_NUM] = { + ORG_BLUETOOTH_CHARACTERISTIC_BATTERY_LEVEL, + ORG_BLUETOOTH_CHARACTERISTIC_BATTERY_LEVEL_STATUS, + ORG_BLUETOOTH_CHARACTERISTIC_ESTIMATED_SERVICE_DATE, + ORG_BLUETOOTH_CHARACTERISTIC_BATTERY_CRITCAL_STATUS, + ORG_BLUETOOTH_CHARACTERISTIC_BATTERY_ENERGY_STATUS, + ORG_BLUETOOTH_CHARACTERISTIC_BATTERY_TIME_STATUS, + ORG_BLUETOOTH_CHARACTERISTIC_BATTERY_HEALTH_STATUS, + ORG_BLUETOOTH_CHARACTERISTIC_BATTERY_HEALTH_INFORMATION, + ORG_BLUETOOTH_CHARACTERISTIC_BATTERY_INFORMATION, + ORG_BLUETOOTH_CHARACTERISTIC_MANUFACTURER_NAME_STRING, + ORG_BLUETOOTH_CHARACTERISTIC_MODEL_NUMBER_STRING, + ORG_BLUETOOTH_CHARACTERISTIC_SERIAL_NUMBER_STRING, +}; + static btstack_linked_list_t battery_services; @@ -133,11 +148,173 @@ static uint16_t battery_service_read_callback(hci_con_handle_t con_handle, uint1 } } - if (attribute_handle == service->battery_value_handle){ - return att_read_callback_handle_byte(service->battery_value, offset, buffer, buffer_size); + uint8_t index; + uint8_t event[18]; + uint8_t pos = 0; + + for (index = 0; index < (uint8_t) BAS_CHARACTERISTIC_INDEX_NUM; index++){ + if (attribute_handle != service->characteristics[index].value_handle){ + continue; + } + + + switch ((bas_characteristic_index_t) index){ + case BAS_CHARACTERISTIC_INDEX_BATTERY_LEVEL: + return att_read_callback_handle_byte(service->battery_value, offset, buffer, buffer_size); + + case BAS_CHARACTERISTIC_INDEX_BATTERY_LEVEL_STATUS: + event[pos++] = service->battery_level.flags; + little_endian_store_16(event, pos, service->battery_level.power_state_flags); + pos += 2; + if ((service->battery_level.flags & BATTERY_LEVEL_STATUS_BITMASK_IDENTIFIER_PRESENT) > 0u){ + little_endian_store_16(event, pos, service->battery_level.identifier); + pos += 2; + } + if ((service->battery_level.flags & BATTERY_LEVEL_STATUS_BITMASK_BATTERY_LEVEL_PRESENT) > 0u){ + event[pos++] = service->battery_level.battery_level; + } + if ((service->battery_level.flags & BATTERY_LEVEL_STATUS_BITMASK_ADDITIONAL_STATUS_PRESENT) > 0u){ + event[pos++] = service->battery_level.additional_status_flags; + } + return att_read_callback_handle_blob(event, pos, offset, buffer, buffer_size); + + case BAS_CHARACTERISTIC_INDEX_ESTIMATED_SERVICE_DATE: + little_endian_store_24(event, pos, service->estimated_service_date_days); + pos += 3; + return att_read_callback_handle_blob(event, pos, offset, buffer, buffer_size); + + case BAS_CHARACTERISTIC_INDEX_BATTERY_CRITCAL_STATUS: + return att_read_callback_handle_byte(service->battery_critcal_status_flags, offset, buffer, buffer_size); + + case BAS_CHARACTERISTIC_INDEX_BATTERY_ENERGY_STATUS: + event[pos++] = service->energy_status.flags; + if ((service->energy_status.flags & BATTERY_ENERGY_STATUS_BITMASK_EXTERNAL_SOURCE_POWER_PRESENT) > 0u){ + little_endian_store_16(event, pos, service->energy_status.external_source_power_medfloat16); + pos += 2; + } + if ((service->energy_status.flags & BATTERY_ENERGY_STATUS_BITMASK_PRESENT_VOLTAGE_PRESENT) > 0u){ + little_endian_store_16(event, pos, service->energy_status.present_voltage_medfloat16); + pos += 2; + } + if ((service->energy_status.flags & BATTERY_ENERGY_STATUS_BITMASK_AVAILABLE_ENERGY_PRESENT) > 0u){ + little_endian_store_16(event, pos, service->energy_status.available_energy_medfloat16); + pos += 2; + } + if ((service->energy_status.flags & BATTERY_ENERGY_STATUS_BITMASK_AVAILABLE_BATTERY_CAPACITY_PRESENT) > 0u){ + little_endian_store_16(event, pos, service->energy_status.available_battery_capacity_medfloat16); + pos += 2; + } + if ((service->energy_status.flags & BATTERY_ENERGY_STATUS_BITMASK_CHARGE_RATE_PRESENT) > 0u){ + little_endian_store_16(event, pos, service->energy_status.charge_rate_medfloat16); + pos += 2; + } + if ((service->energy_status.flags & BATTERY_ENERGY_STATUS_BITMASK_AVAILABLE_ENERGY_AT_LAST_CHARGE_PRESENT) > 0u){ + little_endian_store_16(event, pos, service->energy_status.available_energy_at_last_charge_medfloat16); + pos += 2; + } + return att_read_callback_handle_blob(event, pos, offset, buffer, buffer_size); + + case BAS_CHARACTERISTIC_INDEX_BATTERY_TIME_STATUS: + event[pos++] = service->time_status.flags; + little_endian_store_24(event, pos, service->time_status.time_until_discharged_minutes); + pos += 3; + if ((service->time_status.flags & BATTERY_TIME_STATUS_BITMASK_TIME_UNTIL_DISCHARGED_ON_STANDBY_PRESENT) > 0u){ + little_endian_store_24(event, pos, service->time_status.time_until_discharged_on_standby_minutes); + pos += 3; + } + if ((service->time_status.flags & BATTERY_TIME_STATUS_BITMASK_TIME_UNTIL_RECHARGED_PRESENT) > 0u){ + little_endian_store_24(event, pos, service->time_status.time_until_recharged_minutes); + pos += 3; + } + return att_read_callback_handle_blob(event, pos, offset, buffer, buffer_size); + + case BAS_CHARACTERISTIC_INDEX_BATTERY_HEALTH_STATUS: + event[pos++] = service->health_status.flags; + if ((service->health_status.flags & BATTERY_HEALTH_STATUS_BITMASK_HEALTH_SUMMARY_PRESENT) > 0u){ + event[pos++] = service->health_status.summary; + } + if ((service->health_status.flags & BATTERY_HEALTH_STATUS_BITMASK_CYCLE_COUNT_PRESENT) > 0u){ + little_endian_store_16(event, pos, service->health_status.cycle_count); + pos += 2; + } + if ((service->health_status.flags & BATTERY_HEALTH_STATUS_BITMASK_CURRENT_TEMPERATURE_PRESENT) > 0u){ + event[pos++] = service->health_status.current_temperature_degree_celsius; + } + if ((service->health_status.flags & BATTERY_HEALTH_STATUS_BITMASK_DEEP_DISCHARGE_COUNT_PRESENT) > 0u){ + little_endian_store_16(event, pos, service->health_status.deep_discharge_count); + pos += 2; + } + return att_read_callback_handle_blob(event, pos, offset, buffer, buffer_size); + + case BAS_CHARACTERISTIC_INDEX_BATTERY_HEALTH_INFORMATION: + event[pos++] = service->health_information.flags; + if ((service->health_information.flags & BATTERY_HEALTH_INFORMATION_BITMASK_CYCLE_COUNT_DESIGNED_LIFETIME_PRESENT) > 0u){ + little_endian_store_16(event, pos, service->health_information.cycle_count_designed_lifetime); + pos += 2; + } + if ((service->health_information.flags & BATTERY_HEALTH_INFORMATION_BITMASK_DESIGNED_OPERATING_TEMPERATURE_PRESENT) > 0u){ + event[pos++] = service->health_information.min_designed_operating_temperature_degree_celsius; + } + if ((service->health_information.flags & BATTERY_HEALTH_INFORMATION_BITMASK_DESIGNED_OPERATING_TEMPERATURE_PRESENT) > 0u){ + event[pos++] = service->health_information.max_designed_operating_temperature_degree_celsius; + } + return att_read_callback_handle_blob(event, pos, offset, buffer, buffer_size); + + case BAS_CHARACTERISTIC_INDEX_BATTERY_INFORMATION: + little_endian_store_16(event, pos, service->information.flags); + pos += 2; + event[pos++] = service->information.features; + if ((service->information.flags & BATTERY_INFORMATION_BITMASK_MANUFACTURE_DATE_PRESENT) > 0u){ + little_endian_store_24(event, pos, service->information.manufacture_date_days); + pos += 3; + } + if ((service->information.flags & BATTERY_INFORMATION_BITMASK_EXPIRATION_DATE_PRESENT) > 0u){ + little_endian_store_24(event, pos, service->information.expiration_date_days); + pos += 3; + } + if ((service->information.flags & BATTERY_INFORMATION_BITMASK_DESIGNED_CAPACITY_PRESENT) > 0u){ + little_endian_store_16(event, pos, service->information.designed_capacity_kWh_medfloat16); + pos += 2; + } + if ((service->information.flags & BATTERY_INFORMATION_BITMASK_LOW_ENERGY_PRESENT) > 0u){ + little_endian_store_16(event, pos, service->information.low_energy_kWh_medfloat16); + pos += 2; + } + if ((service->information.flags & BATTERY_INFORMATION_BITMASK_CRITICAL_ENERGY_PRESENT) > 0u){ + little_endian_store_16(event, pos, service->information.critical_energy_kWh_medfloat16); + pos += 2; + } + if ((service->information.flags & BATTERY_INFORMATION_BITMASK_CHEMISTRY_PRESENT) > 0u){ + event[pos++] = service->information.chemistry; + } + if ((service->information.flags & BATTERY_INFORMATION_BITMASK_NOMINAL_VOLTAGE_PRESENT) > 0u){ + little_endian_store_16(event, pos, service->information.nominal_voltage_medfloat16); + pos += 2; + } + if ((service->information.flags & BATTERY_INFORMATION_BITMASK_AGGREGATION_GROUP_PRESENT) > 0u){ + event[pos++] = service->information.aggregation_group; + } + return att_read_callback_handle_blob(event, pos, offset, buffer, buffer_size); + + case BAS_CHARACTERISTIC_INDEX_MANUFACTURER_NAME_STRING: + return att_read_callback_handle_blob(service->manufacturer_name, service->manufacturer_name_len, offset, buffer, buffer_size); + + case BAS_CHARACTERISTIC_INDEX_MODEL_NUMBER_STRING: + return att_read_callback_handle_blob(service->model_number, service->model_number_len, offset, buffer, buffer_size); + + case BAS_CHARACTERISTIC_INDEX_SERIAL_NUMBER_STRING: + return att_read_callback_handle_blob(service->serial_number, service->serial_number_len, offset, buffer, buffer_size); + + default: + return 0; + } } - if (attribute_handle == service->battery_value_client_configuration_handle){ - return att_read_callback_handle_little_endian_16(connection->battery_value_client_configuration, offset, buffer, buffer_size); + + for (index = 0; index < (uint8_t) BAS_CHARACTERISTIC_INDEX_NUM; index++){ + if (attribute_handle != service->characteristics[index].client_configuration_handle){ + continue; + } + return att_read_callback_handle_little_endian_16(connection->configurations[index], offset, buffer, buffer_size); } return 0; } @@ -163,8 +340,12 @@ static int battery_service_write_callback(hci_con_handle_t con_handle, uint16_t } } - if (attribute_handle == service->battery_value_client_configuration_handle){ - connection->battery_value_client_configuration = little_endian_read_16(buffer, 0); + uint8_t index; + for (index = 0; index < (uint8_t) BAS_CHARACTERISTIC_INDEX_NUM; index++){ + if (attribute_handle != service->characteristics[index].client_configuration_handle){ + continue; + } + connection->configurations[index] = little_endian_read_16(buffer, 0); } return 0; } @@ -181,7 +362,7 @@ static void battery_service_can_send_now(void * context){ if ( (connection->scheduled_tasks & BATTERY_SERVICE_TASK_BATTERY_VALUE_CHANGED) > 0u){ connection->scheduled_tasks &= ~BATTERY_SERVICE_TASK_BATTERY_VALUE_CHANGED; - att_server_notify(connection->con_handle, service->battery_value_handle, &service->battery_value, 1); + att_server_notify(connection->con_handle, service->characteristics[BAS_CHARACTERISTIC_INDEX_BATTERY_LEVEL].value_handle, &service->battery_value, 1); } if (connection->scheduled_tasks > 0u){ @@ -218,19 +399,20 @@ void battery_service_v1_server_register(battery_service_v1_t *service, battery_s service->start_handle = start_handle; service->end_handle = end_handle; - // get characteristic value handle and client configuration handle - service->battery_value_handle = gatt_server_get_value_handle_for_characteristic_with_uuid16(start_handle, end_handle, ORG_BLUETOOTH_CHARACTERISTIC_BATTERY_LEVEL); - service->battery_value_client_configuration_handle = gatt_server_get_client_configuration_handle_for_characteristic_with_uuid16(start_handle, end_handle, ORG_BLUETOOTH_CHARACTERISTIC_BATTERY_LEVEL); - - memset(connections, 0, sizeof(battery_service_v1_server_connection_t) * connection_max_num); uint8_t i; + for (i = 0; i < (uint8_t) BAS_CHARACTERISTIC_INDEX_NUM; i++){ + // get characteristic value handle and client configuration handle + service->characteristics[i].value_handle = gatt_server_get_value_handle_for_characteristic_with_uuid16(start_handle, end_handle, bas_uuid16s[i]); + service->characteristics[i].client_configuration_handle = gatt_server_get_client_configuration_handle_for_characteristic_with_uuid16(start_handle, end_handle, bas_uuid16s[i]); + } + + memset(connections, 0, sizeof(battery_service_v1_server_connection_t) * connection_max_num); for (i = 0; i < connection_max_num; i++){ connections[i].con_handle = HCI_CON_HANDLE_INVALID; } service->connections_max_num = connection_max_num; service->connections = connections; - service->service_handler.read_callback = &battery_service_read_callback; service->service_handler.write_callback = &battery_service_write_callback; att_server_register_service_handler(&service->service_handler); @@ -268,14 +450,14 @@ void battery_service_v1_server_set_battery_value(battery_service_v1_t * service, uint8_t i; for (i = 0; i < service->connections_max_num; i++){ battery_service_v1_server_connection_t * connection = &service->connections[i]; - if (connection->battery_value_client_configuration != 0){ + if (connection->configurations[BAS_CHARACTERISTIC_INDEX_BATTERY_LEVEL] != 0){ battery_service_set_callback(connection, BATTERY_SERVICE_TASK_BATTERY_VALUE_CHANGED); } } } void battery_service_v1_server_deregister(battery_service_v1_t *service){ - btstack_linked_list_iterator_remove((btstack_linked_item_t * )service); + btstack_linked_list_remove(&battery_services, (btstack_linked_item_t * )service); // TODO cansel can send now } diff --git a/src/ble/gatt-service/battery_service_v1_server.h b/src/ble/gatt-service/battery_service_v1_server.h index d476736bb..4ca55d048 100644 --- a/src/ble/gatt-service/battery_service_v1_server.h +++ b/src/ble/gatt-service/battery_service_v1_server.h @@ -59,19 +59,112 @@ extern "C" { * If the battery level changes, you can call *battery_service_server_set_battery_value(value)*. * The service supports sending Notifications if the client enables them. */ +#define BATTERY_SERVICE_MAX_STRING_LEN 32 + +typedef enum { + BAS_CHARACTERISTIC_INDEX_BATTERY_LEVEL = 0, + BAS_CHARACTERISTIC_INDEX_BATTERY_LEVEL_STATUS, + BAS_CHARACTERISTIC_INDEX_ESTIMATED_SERVICE_DATE, + BAS_CHARACTERISTIC_INDEX_BATTERY_CRITCAL_STATUS, + BAS_CHARACTERISTIC_INDEX_BATTERY_ENERGY_STATUS, + BAS_CHARACTERISTIC_INDEX_BATTERY_TIME_STATUS, + BAS_CHARACTERISTIC_INDEX_BATTERY_HEALTH_STATUS, + BAS_CHARACTERISTIC_INDEX_BATTERY_HEALTH_INFORMATION, + BAS_CHARACTERISTIC_INDEX_BATTERY_INFORMATION, + BAS_CHARACTERISTIC_INDEX_MANUFACTURER_NAME_STRING, + BAS_CHARACTERISTIC_INDEX_MODEL_NUMBER_STRING, + BAS_CHARACTERISTIC_INDEX_SERIAL_NUMBER_STRING, + BAS_CHARACTERISTIC_INDEX_NUM +} bas_characteristic_index_t; + +// ORG_BLUETOOTH_CHARACTERISTIC_BATTERY_ENERGY_STATUS flags: +#define BATTERY_ENERGY_STATUS_BITMASK_EXTERNAL_SOURCE_POWER_PRESENT 0x01 +#define BATTERY_ENERGY_STATUS_BITMASK_PRESENT_VOLTAGE_PRESENT 0x02 +#define BATTERY_ENERGY_STATUS_BITMASK_AVAILABLE_ENERGY_PRESENT 0x04 +#define BATTERY_ENERGY_STATUS_BITMASK_AVAILABLE_BATTERY_CAPACITY_PRESENT 0x08 +#define BATTERY_ENERGY_STATUS_BITMASK_CHARGE_RATE_PRESENT 0x10 +#define BATTERY_ENERGY_STATUS_BITMASK_AVAILABLE_ENERGY_AT_LAST_CHARGE_PRESENT 0x20 +#define BATTERY_ENERGY_STATUS_BITMASK_RFU 0x40 + +#define BATTERY_LEVEL_STATUS_BITMASK_IDENTIFIER_PRESENT 0x01 +#define BATTERY_LEVEL_STATUS_BITMASK_BATTERY_LEVEL_PRESENT 0x02 +#define BATTERY_LEVEL_STATUS_BITMASK_ADDITIONAL_STATUS_PRESENT 0x04 +#define BATTERY_LEVEL_STATUS_BITMASK_RFU 0x08 + +#define BATTERY_LEVEL_POWER_STATE_BITMASK_EXTERNAL_BATTERY_PRESENT 0x0001 // 0 = No, 1 = Yes +#define BATTERY_LEVEL_POWER_STATE_BITMASK_WIRED_EXTERNAL_POWER_SOURCE_CONNECTED 0x0004 // 0 = No, 1 = Yes, 2 = Unknown, 3 = RFU +#define BATTERY_LEVEL_POWER_STATE_BITMASK_WIRELESS_EXTERNAL_POWER_SOURCE_CONNECTED 0x0010 // 0 = No, 1 = Yes, 2 = Unknown, 3 = RFU +#define BATTERY_LEVEL_POWER_STATE_BITMASK_BATTERY_CHARGE_STATE 0x0040 // 0 = Unknown, 1 = Charging, 2 = Discharging: Active 3 = Discharging: Inactive +#define BATTERY_LEVEL_POWER_STATE_BITMASK_BATTERY_CHARGE_LEVEL 0x0100 // 0 = Unknown, 1 = Good, 2 = Low, 3 = Critical +#define BATTERY_LEVEL_POWER_STATE_BITMASK_CHARGING_TYPE 0x0400 // 0 = Unknown or Not Charging 1 = Constant Current, 2 = Constant Voltage, 3 = Trickle, 4 = Float, 5–7 = RFU +#define BATTERY_LEVEL_POWER_STATE_BITMASK_CHARGING_FAULT_REASON 0x2000 // Bit 12: Battery, Bit 13: External Power source, Bit 14: Other +#define BATTERY_LEVEL_POWER_STATE_BITMASK_RFU 0x4000 + +#define BATTERY_LEVEL_ADDITIONAL_STATUS_BITMASK_SERVICE_REQUIRED 0x01 // 0 = No, 1 = Yes, 2 = Unknown, 3 = RFU +#define BATTERY_LEVEL_ADDITIONAL_STATUS_BITMASK_BATTERY_FAULT 0x02 // 0 = No or Unknown, 1 = Yes +#define BATTERY_LEVEL_ADDITIONAL_STATUS_BITMASK_RFU 0x04 + +#define BATTERY_CRITCAL_STATUS_BITMASK_CRITICAL_POWER_STATE 0x01 +#define BATTERY_CRITCAL_STATUS_BITMASK_IMMEDIATE_SERVICE_REQUIRED 0x02 +#define BATTERY_CRITCAL_STATUS_BITMASK_RFU 0x04 + +#define BATTERY_TIME_STATUS_BITMASK_TIME_UNTIL_DISCHARGED_ON_STANDBY_PRESENT 0x01 +#define BATTERY_TIME_STATUS_BITMASK_TIME_UNTIL_RECHARGED_PRESENT 0x02 +#define BATTERY_TIME_STATUS_BITMASK_RFU 0x04 + +#define BATTERY_HEALTH_STATUS_BITMASK_HEALTH_SUMMARY_PRESENT 0x01 +#define BATTERY_HEALTH_STATUS_BITMASK_CYCLE_COUNT_PRESENT 0x02 +#define BATTERY_HEALTH_STATUS_BITMASK_CURRENT_TEMPERATURE_PRESENT 0x04 +#define BATTERY_HEALTH_STATUS_BITMASK_DEEP_DISCHARGE_COUNT_PRESENT 0x08 +#define BATTERY_HEALTH_STATUS_BITMASK_RFU 0x10 + +#define BATTERY_HEALTH_INFORMATION_BITMASK_CYCLE_COUNT_DESIGNED_LIFETIME_PRESENT 0x01 +#define BATTERY_HEALTH_INFORMATION_BITMASK_DESIGNED_OPERATING_TEMPERATURE_PRESENT 0x02 +#define BATTERY_HEALTH_INFORMATION_BITMASK_RFU 0x04 + +#define BATTERY_INFORMATION_BITMASK_MANUFACTURE_DATE_PRESENT 0x0001 +#define BATTERY_INFORMATION_BITMASK_EXPIRATION_DATE_PRESENT 0x0002 +#define BATTERY_INFORMATION_BITMASK_DESIGNED_CAPACITY_PRESENT 0x0004 +#define BATTERY_INFORMATION_BITMASK_LOW_ENERGY_PRESENT 0x0008 +#define BATTERY_INFORMATION_BITMASK_CRITICAL_ENERGY_PRESENT 0x0010 +#define BATTERY_INFORMATION_BITMASK_CHEMISTRY_PRESENT 0x0020 +#define BATTERY_INFORMATION_BITMASK_NOMINAL_VOLTAGE_PRESENT 0x0040 +#define BATTERY_INFORMATION_BITMASK_AGGREGATION_GROUP_PRESENT 0x0080 +#define BATTERY_INFORMATION_BITMASK_RFU 0x0100 + +#define BATTERY_INFROMATION_FEATURE_BITMASK_REPLACEABLE 0x01 +#define BATTERY_INFROMATION_FEATURE_BITMASK_RECHARGEABLE 0x02 +#define BATTERY_INFROMATION_FEATURE_BITMASK_RFU 0x04 + struct battery_service_v1; +typedef struct { + uint16_t year; + uint8_t month; + uint8_t day; +} btstack_utc_date_t; + +typedef struct { + uint8_t hours; + uint8_t minutes; + uint8_t seconds; +} btstack_utc_time_t; + typedef struct { hci_con_handle_t con_handle; btstack_context_callback_registration_t scheduled_tasks_callback; uint8_t scheduled_tasks; + uint16_t configurations[BAS_CHARACTERISTIC_INDEX_NUM]; - // ORG_BLUETOOTH_CHARACTERISTIC_BATTERY_LEVEL - uint16_t battery_value_client_configuration; struct battery_service_v1 * service; } battery_service_v1_server_connection_t; +typedef struct { + uint16_t value_handle; + uint16_t client_configuration_handle; +} bas_characteristic_t; + typedef struct battery_service_v1 { btstack_linked_item_t item; @@ -83,11 +176,97 @@ typedef struct battery_service_v1 { att_service_handler_t service_handler; + bas_characteristic_t characteristics[BAS_CHARACTERISTIC_INDEX_NUM]; + // ORG_BLUETOOTH_CHARACTERISTIC_BATTERY_LEVEL - uint16_t battery_value_handle; - uint16_t battery_value_client_configuration_handle; uint8_t battery_value; + // ORG_BLUETOOTH_CHARACTERISTIC_BATTERY_LEVEL_STATUS + struct { + uint8_t flags; + uint16_t power_state_flags; + + uint16_t identifier; + uint8_t battery_level; + uint8_t additional_status_flags; + } battery_level; + + // ORG_BLUETOOTH_CHARACTERISTIC_ESTIMATED_SERVICE_DATE + uint32_t estimated_service_date_days; + + // ORG_BLUETOOTH_CHARACTERISTIC_BATTERY_CRITCAL_STATUS + uint8_t battery_critcal_status_flags; + + // ORG_BLUETOOTH_CHARACTERISTIC_BATTERY_ENERGY_STATUS + struct { + uint8_t flags; + uint16_t external_source_power_medfloat16; + uint16_t present_voltage_medfloat16; + uint16_t available_energy_medfloat16; + uint16_t available_battery_capacity_medfloat16; + uint16_t charge_rate_medfloat16; + uint16_t available_energy_at_last_charge_medfloat16; + } energy_status; + + // ORG_BLUETOOTH_CHARACTERISTIC_BATTERY_TIME_STATUS + struct { + uint8_t flags; + + // A value of 0xFFFFFF represents: Unknown + // A value of 0xFFFFFE represents: Greater than 0xFFFFFD + uint32_t time_until_discharged_minutes; + uint32_t time_until_discharged_on_standby_minutes; + uint32_t time_until_recharged_minutes; + } time_status; + + // ORG_BLUETOOTH_CHARACTERISTIC_BATTERY_HEALTH_STATUS + struct { + uint8_t flags; + uint8_t summary; // Allowed range is 0 to 100. + uint16_t cycle_count; + int8_t current_temperature_degree_celsius; + uint16_t deep_discharge_count; + } health_status; + + // ORG_BLUETOOTH_CHARACTERISTIC_BATTERY_HEALTH_INFORMATION + struct { + uint8_t flags; + uint16_t cycle_count_designed_lifetime; + + // A raw value of 0x7F represents: Greater than 126. + // A raw value of 0x80 represents: Less than -127. + int8_t min_designed_operating_temperature_degree_celsius; + int8_t max_designed_operating_temperature_degree_celsius; + } health_information; + + // ORG_BLUETOOTH_CHARACTERISTIC_BATTERY_INFORMATION + struct { + uint16_t flags; + uint8_t features; + + uint32_t manufacture_date_days; + uint32_t expiration_date_days; + + uint16_t designed_capacity_kWh_medfloat16; + uint16_t low_energy_kWh_medfloat16; + uint16_t critical_energy_kWh_medfloat16; + uint8_t chemistry; + uint16_t nominal_voltage_medfloat16; + uint8_t aggregation_group; // 0: not in group, 255: RFU + } information; + + // ORG_BLUETOOTH_CHARACTERISTIC_MANUFACTURER_NAME_STRING + uint8_t manufacturer_name[BATTERY_SERVICE_MAX_STRING_LEN]; + uint8_t manufacturer_name_len; + + // ORG_BLUETOOTH_CHARACTERISTIC_MODEL_NUMBER_STRING + uint8_t model_number[BATTERY_SERVICE_MAX_STRING_LEN]; + uint8_t model_number_len; + + // ORG_BLUETOOTH_CHARACTERISTIC_SERIAL_NUMBER_STRING + uint8_t serial_number[BATTERY_SERVICE_MAX_STRING_LEN]; + uint8_t serial_number_len; + uint8_t connections_max_num; battery_service_v1_server_connection_t * connections; } battery_service_v1_t;