1 /* 2 * Copyright (C) 2024 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 /** 39 * @title Battery Service Server v1.1 40 * 41 */ 42 43 #ifndef BATTERY_SERVICE_V1_SERVER_H 44 #define BATTERY_SERVICE_V1_SERVER_H 45 46 #include <stdint.h> 47 48 #if defined __cplusplus 49 extern "C" { 50 #endif 51 52 /** 53 * @text The Battery Service allows to query your device's battery level in a standardized way. 54 * 55 * To use with your application, add `#import <battery_service.gatt>` to your .gatt file. 56 * After adding it to your .gatt file, you call *battery_service_server_init(value)* with the 57 * current value of your battery. The valid range for the battery level is 0-100. 58 * 59 * If the battery level changes, you can call *battery_service_server_set_battery_value(value)*. 60 * The service supports sending Notifications if the client enables them. 61 */ 62 63 typedef enum { 64 BAS_CHARACTERISTIC_INDEX_BATTERY_LEVEL = 0, 65 BAS_CHARACTERISTIC_INDEX_BATTERY_LEVEL_STATUS, 66 BAS_CHARACTERISTIC_INDEX_ESTIMATED_SERVICE_DATE, 67 BAS_CHARACTERISTIC_INDEX_BATTERY_CRITCAL_STATUS, 68 BAS_CHARACTERISTIC_INDEX_BATTERY_ENERGY_STATUS, 69 BAS_CHARACTERISTIC_INDEX_BATTERY_TIME_STATUS, 70 BAS_CHARACTERISTIC_INDEX_BATTERY_HEALTH_STATUS, 71 BAS_CHARACTERISTIC_INDEX_BATTERY_HEALTH_INFORMATION, 72 BAS_CHARACTERISTIC_INDEX_BATTERY_INFORMATION, 73 BAS_CHARACTERISTIC_INDEX_MANUFACTURER_NAME_STRING, 74 BAS_CHARACTERISTIC_INDEX_MODEL_NUMBER_STRING, 75 BAS_CHARACTERISTIC_INDEX_SERIAL_NUMBER_STRING, 76 BAS_CHARACTERISTIC_INDEX_NUM 77 } bas_characteristic_index_t; 78 79 // ORG_BLUETOOTH_CHARACTERISTIC_BATTERY_ENERGY_STATUS flags: 80 #define BATTERY_ENERGY_STATUS_BITMASK_EXTERNAL_SOURCE_POWER_PRESENT 0x01 81 #define BATTERY_ENERGY_STATUS_BITMASK_PRESENT_VOLTAGE_PRESENT 0x02 82 #define BATTERY_ENERGY_STATUS_BITMASK_AVAILABLE_ENERGY_PRESENT 0x04 83 #define BATTERY_ENERGY_STATUS_BITMASK_AVAILABLE_BATTERY_CAPACITY_PRESENT 0x08 84 #define BATTERY_ENERGY_STATUS_BITMASK_CHARGE_RATE_PRESENT 0x10 85 #define BATTERY_ENERGY_STATUS_BITMASK_AVAILABLE_ENERGY_AT_LAST_CHARGE_PRESENT 0x20 86 #define BATTERY_ENERGY_STATUS_BITMASK_RFU 0xC0 87 88 #define BATTERY_LEVEL_STATUS_BITMASK_IDENTIFIER_PRESENT 0x01 89 #define BATTERY_LEVEL_STATUS_BITMASK_BATTERY_LEVEL_PRESENT 0x02 90 #define BATTERY_LEVEL_STATUS_BITMASK_ADDITIONAL_STATUS_PRESENT 0x04 91 #define BATTERY_LEVEL_STATUS_BITMASK_RFU 0xF8 92 93 #define BATTERY_LEVEL_ADDITIONAL_STATUS_BITMASK_SERVICE_REQUIRED 0x03 // 0 = No, 1 = Yes, 2 = Unknown, 3 = RFU 94 #define BATTERY_LEVEL_ADDITIONAL_STATUS_BITMASK_BATTERY_FAULT 0x04 // 0 = No or Unknown, 1 = Yes 95 #define BATTERY_LEVEL_ADDITIONAL_STATUS_BITMASK_RFU 0xF8 96 97 #define BATTERY_LEVEL_POWER_STATE_BITMASK_EXTERNAL_BATTERY_PRESENT 0x0001 // (Bit 0) 0 = No, 1 = Yes 98 #define BATTERY_LEVEL_POWER_STATE_BITMASK_WIRED_EXTERNAL_POWER_SOURCE_CONNECTED 0x0006 // (Bit 1-2) 0 = No, 1 = Yes, 2 = Unknown, 3 = RFU 99 #define BATTERY_LEVEL_POWER_STATE_BITMASK_WIRELESS_EXTERNAL_POWER_SOURCE_CONNECTED 0x0018 // (Bit 3-4) 0 = No, 1 = Yes, 2 = Unknown, 3 = RFU 100 #define BATTERY_LEVEL_POWER_STATE_BITMASK_BATTERY_CHARGE_STATE 0x0060 // (Bit 5-6) 0 = Unknown, 1 = Charging, 2 = Discharging: Active 3 = Discharging: Inactive 101 #define BATTERY_LEVEL_POWER_STATE_BITMASK_BATTERY_CHARGE_LEVEL 0x0180 // (Bit 7-8) 0 = Unknown, 1 = Good, 2 = Low, 3 = Critical 102 #define BATTERY_LEVEL_POWER_STATE_BITMASK_CHARGING_TYPE 0x0E00 // (Bit 9-11) 0 = Unknown or Not Charging 1 = Constant Current, 2 = Constant Voltage, 3 = Trickle, 4 = Float, 5–7 = RFU 103 #define BATTERY_LEVEL_POWER_STATE_BITMASK_CHARGING_FAULT_REASON_BATTERY 0x1000 // (Bit 12) 104 #define BATTERY_LEVEL_POWER_STATE_BITMASK_CHARGING_FAULT_REASON_EXTERNAL_POWER 0x2000 // (Bit 13) 105 #define BATTERY_LEVEL_POWER_STATE_BITMASK_CHARGING_FAULT_REASON_OTHER 0x4000 // (Bit 14) 106 #define BATTERY_LEVEL_POWER_STATE_BITMASK_RFU 0x8000 107 108 #define BATTERY_CRITCAL_STATUS_BITMASK_CRITICAL_POWER_STATE 0x01 109 #define BATTERY_CRITCAL_STATUS_BITMASK_IMMEDIATE_SERVICE_REQUIRED 0x02 110 #define BATTERY_CRITCAL_STATUS_BITMASK_RFU 0xFC 111 112 #define BATTERY_TIME_STATUS_BITMASK_TIME_UNTIL_DISCHARGED_ON_STANDBY_PRESENT 0x01 113 #define BATTERY_TIME_STATUS_BITMASK_TIME_UNTIL_RECHARGED_PRESENT 0x02 114 #define BATTERY_TIME_STATUS_BITMASK_RFU 0xFC 115 116 #define BATTERY_HEALTH_STATUS_BITMASK_HEALTH_SUMMARY_PRESENT 0x01 117 #define BATTERY_HEALTH_STATUS_BITMASK_CYCLE_COUNT_PRESENT 0x02 118 #define BATTERY_HEALTH_STATUS_BITMASK_CURRENT_TEMPERATURE_PRESENT 0x04 119 #define BATTERY_HEALTH_STATUS_BITMASK_DEEP_DISCHARGE_COUNT_PRESENT 0x08 120 #define BATTERY_HEALTH_STATUS_BITMASK_RFU 0xF0 121 122 #define BATTERY_HEALTH_INFORMATION_BITMASK_CYCLE_COUNT_DESIGNED_LIFETIME_PRESENT 0x01 123 #define BATTERY_HEALTH_INFORMATION_BITMASK_DESIGNED_OPERATING_TEMPERATURE_PRESENT 0x02 124 #define BATTERY_HEALTH_INFORMATION_BITMASK_RFU 0xFC 125 126 #define BATTERY_INFORMATION_BITMASK_MANUFACTURE_DATE_PRESENT 0x0001 127 #define BATTERY_INFORMATION_BITMASK_EXPIRATION_DATE_PRESENT 0x0002 128 #define BATTERY_INFORMATION_BITMASK_DESIGNED_CAPACITY_PRESENT 0x0004 129 #define BATTERY_INFORMATION_BITMASK_LOW_ENERGY_PRESENT 0x0008 130 #define BATTERY_INFORMATION_BITMASK_CRITICAL_ENERGY_PRESENT 0x0010 131 #define BATTERY_INFORMATION_BITMASK_CHEMISTRY_PRESENT 0x0020 132 #define BATTERY_INFORMATION_BITMASK_NOMINAL_VOLTAGE_PRESENT 0x0040 133 #define BATTERY_INFORMATION_BITMASK_AGGREGATION_GROUP_PRESENT 0x0080 134 #define BATTERY_INFORMATION_BITMASK_RFU 0xFF00 135 136 #define BATTERY_INFROMATION_FEATURE_BITMASK_REPLACEABLE 0x01 137 #define BATTERY_INFROMATION_FEATURE_BITMASK_RECHARGEABLE 0x02 138 #define BATTERY_INFROMATION_FEATURE_BITMASK_RFU 0xFC 139 140 struct battery_service_v1; 141 142 typedef struct { 143 hci_con_handle_t con_handle; 144 145 btstack_context_callback_registration_t scheduled_tasks_callback; 146 uint16_t scheduled_tasks; 147 uint16_t configurations[BAS_CHARACTERISTIC_INDEX_NUM]; 148 149 struct battery_service_v1 * service; 150 } battery_service_v1_server_connection_t; 151 152 typedef struct { 153 uint16_t value_handle; 154 uint16_t client_configuration_handle; 155 } bas_characteristic_t; 156 157 typedef struct { 158 uint8_t flags; 159 uint16_t power_state_flags; 160 161 uint16_t identifier; 162 uint8_t battery_level; 163 uint8_t additional_status_flags; 164 } battery_level_status_t; 165 166 typedef struct { 167 uint8_t flags; 168 uint16_t external_source_power_medfloat16; 169 uint16_t present_voltage_medfloat16; 170 uint16_t available_energy_medfloat16; 171 uint16_t available_battery_capacity_medfloat16; 172 uint16_t charge_rate_medfloat16; 173 uint16_t available_energy_at_last_charge_medfloat16; 174 } battery_energy_status_t; 175 176 typedef struct { 177 uint8_t flags; 178 179 // A value of 0xFFFFFF represents: Unknown 180 // A value of 0xFFFFFE represents: Greater than 0xFFFFFD 181 uint32_t time_until_discharged_minutes; 182 uint32_t time_until_discharged_on_standby_minutes; 183 uint32_t time_until_recharged_minutes; 184 } battery_time_status_t; 185 186 typedef struct { 187 uint8_t flags; 188 189 uint8_t summary; // Allowed range is 0 to 100. 190 uint16_t cycle_count; 191 int8_t current_temperature_degree_celsius; 192 uint16_t deep_discharge_count; 193 } battery_health_status_t; 194 195 typedef struct { 196 uint8_t flags; 197 uint16_t cycle_count_designed_lifetime; 198 199 // A raw value of 0x7F represents: Greater than 126. 200 // A raw value of 0x80 represents: Less than -127. 201 int8_t min_designed_operating_temperature_degree_celsius; 202 int8_t max_designed_operating_temperature_degree_celsius; 203 } battery_health_information_t; 204 205 typedef enum { 206 BATTERY_CHEMISTRY_UNKNOWN = 0, 207 BATTERY_CHEMISTRY_ALKALINE, // (ZINC–MANGANESE DIOXIDE) 208 BATTERY_CHEMISTRY_LEAD_ACID, 209 BATTERY_CHEMISTRY_LITHIUM_LIFES2, // (LITHIUM-IRON DISULFIDE) 210 BATTERY_CHEMISTRY_LITHIUM_LIMNO2, // (LITHIUM-MANGANESE DIOXIDE) 211 BATTERY_CHEMISTRY_LITHIUM_ION_LI, 212 BATTERY_CHEMISTRY_LITHIUM_POLYMER, 213 BATTERY_CHEMISTRY_NICKEL_OXYHYDROXIDE_NIOX, // (ZINC-MANGANESE DIOXIDE/OXY NICKEL HYDROXIDE) 214 BATTERY_CHEMISTRY_NICKEL_CADMIUM_NICD, 215 BATTERY_CHEMISTRY_NICKEL_METAL_HYDRIDE_NIMH, 216 BATTERY_CHEMISTRY_SILVER_OXIDE_AGZN, // (SILVER-ZINC) 217 BATTERY_CHEMISTRY_ZINC_CHLORIDE, 218 BATTERY_CHEMISTRY_ZINC_AIR, 219 BATTERY_CHEMISTRY_ZINC_CARBON, 220 BATTERY_CHEMISTRY_RFU_START = 14, 221 BATTERY_CHEMISTRY_RFU_END = 254, 222 BATTERY_CHEMISTRY_OTHER = 255 223 } battery_chemistry_t; 224 225 typedef struct { 226 uint16_t flags; 227 uint8_t features; 228 229 uint32_t manufacture_date_days; 230 uint32_t expiration_date_days; 231 232 uint16_t designed_capacity_kWh_medfloat16; 233 uint16_t low_energy_kWh_medfloat16; 234 uint16_t critical_energy_kWh_medfloat16; 235 battery_chemistry_t chemistry; 236 uint16_t nominal_voltage_medfloat16; 237 uint8_t aggregation_group; // 0: not in group, 255: RFU 238 } battery_information_t; 239 240 typedef struct battery_service_v1 { 241 btstack_linked_item_t item; 242 243 // service 244 245 att_service_handler_t service_handler; 246 247 uint16_t service_id; 248 249 bas_characteristic_t characteristics[BAS_CHARACTERISTIC_INDEX_NUM]; 250 uint16_t battery_level_status_broadcast_configuration_handle; 251 uint16_t battery_level_status_broadcast_configuration; 252 253 // ORG_BLUETOOTH_CHARACTERISTIC_BATTERY_LEVEL 254 uint8_t battery_level; 255 256 // ORG_BLUETOOTH_CHARACTERISTIC_BATTERY_LEVEL_STATUS 257 const battery_level_status_t * level_status; 258 259 // ORG_BLUETOOTH_CHARACTERISTIC_ESTIMATED_SERVICE_DATE 260 uint32_t estimated_service_date_days; 261 262 // ORG_BLUETOOTH_CHARACTERISTIC_BATTERY_CRITCAL_STATUS 263 uint8_t critical_status_flags; 264 265 // ORG_BLUETOOTH_CHARACTERISTIC_BATTERY_ENERGY_STATUS 266 const battery_energy_status_t * energy_status; 267 268 // ORG_BLUETOOTH_CHARACTERISTIC_BATTERY_TIME_STATUS 269 const battery_time_status_t * time_status; 270 271 // ORG_BLUETOOTH_CHARACTERISTIC_BATTERY_HEALTH_STATUS 272 const battery_health_status_t * health_status; 273 274 // ORG_BLUETOOTH_CHARACTERISTIC_BATTERY_HEALTH_INFORMATION 275 const battery_health_information_t * health_information; 276 277 // ORG_BLUETOOTH_CHARACTERISTIC_BATTERY_INFORMATION 278 const battery_information_t * information; 279 280 // ORG_BLUETOOTH_CHARACTERISTIC_MANUFACTURER_NAME_STRING 281 const char * manufacturer_name; 282 283 // ORG_BLUETOOTH_CHARACTERISTIC_MODEL_NUMBER_STRING 284 const char * model_number; 285 286 // ORG_BLUETOOTH_CHARACTERISTIC_SERIAL_NUMBER_STRING 287 const char * serial_number; 288 289 uint8_t connections_max_num; 290 battery_service_v1_server_connection_t * connections; 291 } battery_service_v1_t; 292 293 /* API_START */ 294 295 /** 296 * @brief Init Battery Service Server with ATT DB 297 */ 298 void battery_service_v1_server_init(void); 299 300 /** 301 * @brief Register Battery Service 302 * @param service 303 * @param connections array of battery_service_v1_server_connection_t for storage 304 * @param connection_max_num 305 * @param out_service_id 306 */ 307 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); 308 309 /** 310 * @brief Deregister Battery Service Server 311 * @param service 312 */ 313 void battery_service_v1_server_deregister(battery_service_v1_t * service); 314 315 /** 316 * @brief Set callback for broadcast updates. 317 * @param callback 318 */ 319 void battery_service_v1_server_set_packet_handler(btstack_packet_handler_t callback); 320 321 /** 322 * @brief Update battery level 323 * @note Triggers notification if subscribed 324 * @param service 325 * @param battery_level in range 0-100 326 * @return ERROR_CODE_SUCCESS if value is valid, otherwise ERROR_CODE_PARAMETER_OUT_OF_MANDATORY_RANGE 327 */ 328 uint8_t battery_service_v1_server_set_battery_level(battery_service_v1_t * service,uint8_t battery_level); 329 330 /** 331 * @brief Update battery level status 332 * @note Triggers notification or indication if subscribed 333 * @param service 334 * @param battery_level_status in range 0-100 335 * @return ERROR_CODE_SUCCESS if value is valid, otherwise ERROR_CODE_PARAMETER_OUT_OF_MANDATORY_RANGE 336 */ 337 uint8_t battery_service_v1_server_set_battery_level_status(battery_service_v1_t * service, const battery_level_status_t * battery_level_status); 338 339 /** 340 * @brief Update battery estimated service date as days elapsed since the Epoch (Jan 1, 1970) 341 * @note Triggers notification or indication if subscribed 342 * @param service 343 * @param estimated_service_date_days 344 * @return ERROR_CODE_SUCCESS if value is valid, otherwise ERROR_CODE_PARAMETER_OUT_OF_MANDATORY_RANGE 345 */ 346 uint8_t battery_service_v1_server_set_estimated_service_date_days(battery_service_v1_t * service, uint32_t estimated_service_date_days); 347 348 /** 349 * @brief Update battery critcal status flags 350 * @note Triggers indication if subscribed 351 * @param service 352 * @param critcal_status_flags 353 * @return ERROR_CODE_SUCCESS if value is valid, otherwise ERROR_CODE_PARAMETER_OUT_OF_MANDATORY_RANGE 354 */ 355 uint8_t battery_service_v1_server_set_critcal_status_flags(battery_service_v1_t * service, uint8_t critcal_status_flags); 356 357 /** 358 * @brief Update battery energy status 359 * @note Triggers notification or indication if subscribed 360 * @param service 361 * @param energy_status 362 * @return ERROR_CODE_SUCCESS if value is valid, otherwise ERROR_CODE_PARAMETER_OUT_OF_MANDATORY_RANGE 363 */ 364 uint8_t battery_service_v1_server_set_energy_status(battery_service_v1_t * service, const battery_energy_status_t * energy_status); 365 366 /** 367 * @brief Update battery time status 368 * @note Triggers notification or indication if subscribed 369 * @param service 370 * @param time_status 371 * @return ERROR_CODE_SUCCESS if value is valid, otherwise ERROR_CODE_PARAMETER_OUT_OF_MANDATORY_RANGE 372 */ 373 uint8_t battery_service_v1_server_set_time_status(battery_service_v1_t * service, const battery_time_status_t * time_status); 374 375 /** 376 * @brief Update battery health status 377 * @note Triggers notification or indication if subscribed 378 * @param service 379 * @param health_status 380 * @return ERROR_CODE_SUCCESS if value is valid, otherwise ERROR_CODE_PARAMETER_OUT_OF_MANDATORY_RANGE 381 */ 382 uint8_t battery_service_v1_server_set_health_status(battery_service_v1_t * service, const battery_health_status_t * health_status); 383 384 /** 385 * @brief Update battery health information 386 * @note Triggers indication if subscribed 387 * @param service 388 * @param health_information 389 * @return ERROR_CODE_SUCCESS if value is valid, otherwise ERROR_CODE_PARAMETER_OUT_OF_MANDATORY_RANGE 390 */ 391 uint8_t battery_service_v1_server_set_health_information(battery_service_v1_t * service, const battery_health_information_t * health_information); 392 393 /** 394 * @brief Update battery information 395 * @note Triggers indication if subscribed 396 * @param service 397 * @param information 398 * @return ERROR_CODE_SUCCESS if value is valid, otherwise ERROR_CODE_PARAMETER_OUT_OF_MANDATORY_RANGE 399 */ 400 uint8_t battery_service_v1_server_set_information(battery_service_v1_t * service, const battery_information_t * information); 401 402 /** 403 * @brief Update manufacturer name 404 * @note Triggers indication if subscribed 405 * @param service 406 * @param manufacturer_name 407 * @return ERROR_CODE_SUCCESS if value is valid, otherwise ERROR_CODE_PARAMETER_OUT_OF_MANDATORY_RANGE 408 */ 409 uint8_t battery_service_v1_server_set_manufacturer_name(battery_service_v1_t * service, const char * manufacturer_name); 410 411 /** 412 * @brief Update model_number 413 * @note Triggers indication if subscribed 414 * @param service 415 * @param model number 416 * @return ERROR_CODE_SUCCESS if value is valid, otherwise ERROR_CODE_PARAMETER_OUT_OF_MANDATORY_RANGE 417 */ 418 uint8_t battery_service_v1_server_set_model_number(battery_service_v1_t * service, const char * model_number); 419 420 /** 421 * @brief Update serial_number 422 * @note Triggers indication if subscribed 423 * @param service 424 * @param serial number 425 * @return ERROR_CODE_SUCCESS if value is valid, otherwise ERROR_CODE_PARAMETER_OUT_OF_MANDATORY_RANGE 426 */ 427 uint8_t battery_service_v1_server_set_serial_number(battery_service_v1_t * service, const char * serial_number); 428 429 /** 430 * @brief Get Advertisement Data for all active Characteristic Broadcasts 431 * @param adv_interval 432 * @param adv_buffer 433 * @param adv_size 434 * @return 435 */ 436 uint16_t battery_service_v1_server_get_broadcast_advertisement(uint16_t adv_interval, uint8_t * adv_buffer, uint16_t adv_size); 437 438 /** 439 * @brief Get Advertisement Data for single active Characteristic Broadcast 440 * @param adv_interval 441 * @param adv_buffer 442 * @param adv_size 443 * @return 444 */ 445 uint16_t battery_service_v1_server_get_broadcast_advertisement_single(battery_service_v1_t * service, uint16_t adv_interval, uint8_t * adv_buffer, uint16_t adv_size); 446 447 void battery_service_v1_server_deinit(void); 448 449 /* API_END */ 450 451 #if defined __cplusplus 452 } 453 #endif 454 455 #endif 456 457