ble_bas.c 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365
  1. /**
  2. * Copyright (c) 2012 - 2020, Nordic Semiconductor ASA
  3. *
  4. * All rights reserved.
  5. *
  6. * Redistribution and use in source and binary forms, with or without modification,
  7. * are permitted provided that the following conditions are met:
  8. *
  9. * 1. Redistributions of source code must retain the above copyright notice, this
  10. * list of conditions and the following disclaimer.
  11. *
  12. * 2. Redistributions in binary form, except as embedded into a Nordic
  13. * Semiconductor ASA integrated circuit in a product or a software update for
  14. * such product, must reproduce the above copyright notice, this list of
  15. * conditions and the following disclaimer in the documentation and/or other
  16. * materials provided with the distribution.
  17. *
  18. * 3. Neither the name of Nordic Semiconductor ASA nor the names of its
  19. * contributors may be used to endorse or promote products derived from this
  20. * software without specific prior written permission.
  21. *
  22. * 4. This software, with or without modification, must only be used with a
  23. * Nordic Semiconductor ASA integrated circuit.
  24. *
  25. * 5. Any software provided in binary form under this license must not be reverse
  26. * engineered, decompiled, modified and/or disassembled.
  27. *
  28. * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
  29. * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  30. * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
  31. * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
  32. * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
  33. * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
  34. * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  35. * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  36. * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
  37. * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  38. *
  39. */
  40. /* Attention!
  41. * To maintain compliance with Nordic Semiconductor ASA's Bluetooth profile
  42. * qualification listings, this section of source code must not be modified.
  43. */
  44. #include "sdk_common.h"
  45. #if NRF_MODULE_ENABLED(BLE_BAS)
  46. #include "ble_bas.h"
  47. #include <string.h>
  48. #include "ble_srv_common.h"
  49. #include "ble_conn_state.h"
  50. #define NRF_LOG_MODULE_NAME ble_bas
  51. #if BLE_BAS_CONFIG_LOG_ENABLED
  52. #define NRF_LOG_LEVEL BLE_BAS_CONFIG_LOG_LEVEL
  53. #define NRF_LOG_INFO_COLOR BLE_BAS_CONFIG_INFO_COLOR
  54. #define NRF_LOG_DEBUG_COLOR BLE_BAS_CONFIG_DEBUG_COLOR
  55. #else // BLE_BAS_CONFIG_LOG_ENABLED
  56. #define NRF_LOG_LEVEL 0
  57. #endif // BLE_BAS_CONFIG_LOG_ENABLED
  58. #include "nrf_log.h"
  59. NRF_LOG_MODULE_REGISTER();
  60. #define INVALID_BATTERY_LEVEL 255
  61. /**@brief Function for handling the Write event.
  62. *
  63. * @param[in] p_bas Battery Service structure.
  64. * @param[in] p_ble_evt Event received from the BLE stack.
  65. */
  66. static void on_write(ble_bas_t * p_bas, ble_evt_t const * p_ble_evt)
  67. {
  68. if (!p_bas->is_notification_supported)
  69. {
  70. return;
  71. }
  72. ble_gatts_evt_write_t const * p_evt_write = &p_ble_evt->evt.gatts_evt.params.write;
  73. if ( (p_evt_write->handle == p_bas->battery_level_handles.cccd_handle)
  74. && (p_evt_write->len == 2))
  75. {
  76. if (p_bas->evt_handler == NULL)
  77. {
  78. return;
  79. }
  80. ble_bas_evt_t evt;
  81. if (ble_srv_is_notification_enabled(p_evt_write->data))
  82. {
  83. evt.evt_type = BLE_BAS_EVT_NOTIFICATION_ENABLED;
  84. }
  85. else
  86. {
  87. evt.evt_type = BLE_BAS_EVT_NOTIFICATION_DISABLED;
  88. }
  89. evt.conn_handle = p_ble_evt->evt.gatts_evt.conn_handle;
  90. // CCCD written, call application event handler.
  91. p_bas->evt_handler(p_bas, &evt);
  92. }
  93. }
  94. void ble_bas_on_ble_evt(ble_evt_t const * p_ble_evt, void * p_context)
  95. {
  96. if ((p_context == NULL) || (p_ble_evt == NULL))
  97. {
  98. return;
  99. }
  100. ble_bas_t * p_bas = (ble_bas_t *)p_context;
  101. switch (p_ble_evt->header.evt_id)
  102. {
  103. case BLE_GATTS_EVT_WRITE:
  104. on_write(p_bas, p_ble_evt);
  105. break;
  106. default:
  107. // No implementation needed.
  108. break;
  109. }
  110. }
  111. /**@brief Function for adding the Battery Level characteristic.
  112. *
  113. * @param[in] p_bas Battery Service structure.
  114. * @param[in] p_bas_init Information needed to initialize the service.
  115. *
  116. * @return NRF_SUCCESS on success, otherwise an error code.
  117. */
  118. static ret_code_t battery_level_char_add(ble_bas_t * p_bas, const ble_bas_init_t * p_bas_init)
  119. {
  120. ret_code_t err_code;
  121. ble_add_char_params_t add_char_params;
  122. ble_add_descr_params_t add_descr_params;
  123. uint8_t initial_battery_level;
  124. uint8_t init_len;
  125. uint8_t encoded_report_ref[BLE_SRV_ENCODED_REPORT_REF_LEN];
  126. // Add battery level characteristic
  127. initial_battery_level = p_bas_init->initial_batt_level;
  128. memset(&add_char_params, 0, sizeof(add_char_params));
  129. add_char_params.uuid = BLE_UUID_BATTERY_LEVEL_CHAR;
  130. add_char_params.max_len = sizeof(uint8_t);
  131. add_char_params.init_len = sizeof(uint8_t);
  132. add_char_params.p_init_value = &initial_battery_level;
  133. add_char_params.char_props.notify = p_bas->is_notification_supported;
  134. add_char_params.char_props.read = 1;
  135. add_char_params.cccd_write_access = p_bas_init->bl_cccd_wr_sec;
  136. add_char_params.read_access = p_bas_init->bl_rd_sec;
  137. err_code = characteristic_add(p_bas->service_handle,
  138. &add_char_params,
  139. &(p_bas->battery_level_handles));
  140. if (err_code != NRF_SUCCESS)
  141. {
  142. return err_code;
  143. }
  144. if (p_bas_init->p_report_ref != NULL)
  145. {
  146. // Add Report Reference descriptor
  147. init_len = ble_srv_report_ref_encode(encoded_report_ref, p_bas_init->p_report_ref);
  148. memset(&add_descr_params, 0, sizeof(add_descr_params));
  149. add_descr_params.uuid = BLE_UUID_REPORT_REF_DESCR;
  150. add_descr_params.read_access = p_bas_init->bl_report_rd_sec;
  151. add_descr_params.init_len = init_len;
  152. add_descr_params.max_len = add_descr_params.init_len;
  153. add_descr_params.p_value = encoded_report_ref;
  154. err_code = descriptor_add(p_bas->battery_level_handles.value_handle,
  155. &add_descr_params,
  156. &p_bas->report_ref_handle);
  157. return err_code;
  158. }
  159. else
  160. {
  161. p_bas->report_ref_handle = BLE_GATT_HANDLE_INVALID;
  162. }
  163. return NRF_SUCCESS;
  164. }
  165. ret_code_t ble_bas_init(ble_bas_t * p_bas, const ble_bas_init_t * p_bas_init)
  166. {
  167. if (p_bas == NULL || p_bas_init == NULL)
  168. {
  169. return NRF_ERROR_NULL;
  170. }
  171. ret_code_t err_code;
  172. ble_uuid_t ble_uuid;
  173. // Initialize service structure
  174. p_bas->evt_handler = p_bas_init->evt_handler;
  175. p_bas->is_notification_supported = p_bas_init->support_notification;
  176. p_bas->battery_level_last = INVALID_BATTERY_LEVEL;
  177. // Add service
  178. BLE_UUID_BLE_ASSIGN(ble_uuid, BLE_UUID_BATTERY_SERVICE);
  179. err_code = sd_ble_gatts_service_add(BLE_GATTS_SRVC_TYPE_PRIMARY, &ble_uuid, &p_bas->service_handle);
  180. VERIFY_SUCCESS(err_code);
  181. // Add battery level characteristic
  182. err_code = battery_level_char_add(p_bas, p_bas_init);
  183. return err_code;
  184. }
  185. /**@brief Function for sending notifications with the Battery Level characteristic.
  186. *
  187. * @param[in] p_hvx_params Pointer to structure with notification data.
  188. * @param[in] conn_handle Connection handle.
  189. *
  190. * @return NRF_SUCCESS on success, otherwise an error code.
  191. */
  192. static ret_code_t battery_notification_send(ble_gatts_hvx_params_t * const p_hvx_params,
  193. uint16_t conn_handle)
  194. {
  195. ret_code_t err_code = sd_ble_gatts_hvx(conn_handle, p_hvx_params);
  196. if (err_code == NRF_SUCCESS)
  197. {
  198. NRF_LOG_INFO("Battery notification has been sent using conn_handle: 0x%04X", conn_handle);
  199. }
  200. else
  201. {
  202. NRF_LOG_DEBUG("Error: 0x%08X while sending notification with conn_handle: 0x%04X",
  203. err_code,
  204. conn_handle);
  205. }
  206. return err_code;
  207. }
  208. ret_code_t ble_bas_battery_level_update(ble_bas_t * p_bas,
  209. uint8_t battery_level,
  210. uint16_t conn_handle)
  211. {
  212. if (p_bas == NULL)
  213. {
  214. return NRF_ERROR_NULL;
  215. }
  216. ret_code_t err_code = NRF_SUCCESS;
  217. ble_gatts_value_t gatts_value;
  218. if (battery_level != p_bas->battery_level_last)
  219. {
  220. // Initialize value struct.
  221. memset(&gatts_value, 0, sizeof(gatts_value));
  222. gatts_value.len = sizeof(uint8_t);
  223. gatts_value.offset = 0;
  224. gatts_value.p_value = &battery_level;
  225. // Update database.
  226. err_code = sd_ble_gatts_value_set(BLE_CONN_HANDLE_INVALID,
  227. p_bas->battery_level_handles.value_handle,
  228. &gatts_value);
  229. if (err_code == NRF_SUCCESS)
  230. {
  231. NRF_LOG_INFO("Battery level has been updated: %d%%", battery_level)
  232. // Save new battery value.
  233. p_bas->battery_level_last = battery_level;
  234. }
  235. else
  236. {
  237. NRF_LOG_DEBUG("Error during battery level update: 0x%08X", err_code)
  238. return err_code;
  239. }
  240. // Send value if connected and notifying.
  241. if (p_bas->is_notification_supported)
  242. {
  243. ble_gatts_hvx_params_t hvx_params;
  244. memset(&hvx_params, 0, sizeof(hvx_params));
  245. hvx_params.handle = p_bas->battery_level_handles.value_handle;
  246. hvx_params.type = BLE_GATT_HVX_NOTIFICATION;
  247. hvx_params.offset = gatts_value.offset;
  248. hvx_params.p_len = &gatts_value.len;
  249. hvx_params.p_data = gatts_value.p_value;
  250. if (conn_handle == BLE_CONN_HANDLE_ALL)
  251. {
  252. ble_conn_state_conn_handle_list_t conn_handles = ble_conn_state_conn_handles();
  253. // Try sending notifications to all valid connection handles.
  254. for (uint32_t i = 0; i < conn_handles.len; i++)
  255. {
  256. if (ble_conn_state_status(conn_handles.conn_handles[i]) == BLE_CONN_STATUS_CONNECTED)
  257. {
  258. if (err_code == NRF_SUCCESS)
  259. {
  260. err_code = battery_notification_send(&hvx_params,
  261. conn_handles.conn_handles[i]);
  262. }
  263. else
  264. {
  265. // Preserve the first non-zero error code
  266. UNUSED_RETURN_VALUE(battery_notification_send(&hvx_params,
  267. conn_handles.conn_handles[i]));
  268. }
  269. }
  270. }
  271. }
  272. else
  273. {
  274. err_code = battery_notification_send(&hvx_params, conn_handle);
  275. }
  276. }
  277. else
  278. {
  279. err_code = NRF_ERROR_INVALID_STATE;
  280. }
  281. }
  282. return err_code;
  283. }
  284. ret_code_t ble_bas_battery_lvl_on_reconnection_update(ble_bas_t * p_bas,
  285. uint16_t conn_handle)
  286. {
  287. if (p_bas == NULL)
  288. {
  289. return NRF_ERROR_NULL;
  290. }
  291. ret_code_t err_code;
  292. if (p_bas->is_notification_supported)
  293. {
  294. ble_gatts_hvx_params_t hvx_params;
  295. uint16_t len = sizeof(uint8_t);
  296. memset(&hvx_params, 0, sizeof(hvx_params));
  297. hvx_params.handle = p_bas->battery_level_handles.value_handle;
  298. hvx_params.type = BLE_GATT_HVX_NOTIFICATION;
  299. hvx_params.offset = 0;
  300. hvx_params.p_len = &len;
  301. hvx_params.p_data = &p_bas->battery_level_last;
  302. err_code = battery_notification_send(&hvx_params, conn_handle);
  303. return err_code;
  304. }
  305. return NRF_ERROR_INVALID_STATE;
  306. }
  307. #endif // NRF_MODULE_ENABLED(BLE_BAS)