main.c 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559
  1. /*
  2. * Licensed to the Apache Software Foundation (ASF) under one
  3. * or more contributor license agreements. See the NOTICE file
  4. * distributed with this work for additional information
  5. * regarding copyright ownership. The ASF licenses this file
  6. * to you under the Apache License, Version 2.0 (the
  7. * "License"); you may not use this file except in compliance
  8. * with the License. You may obtain a copy of the License at
  9. *
  10. * http://www.apache.org/licenses/LICENSE-2.0
  11. *
  12. * Unless required by applicable law or agreed to in writing,
  13. * software distributed under the License is distributed on an
  14. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  15. * KIND, either express or implied. See the License for the
  16. * specific language governing permissions and limitations
  17. * under the License.
  18. */
  19. #include "esp_log.h"
  20. #include "nvs_flash.h"
  21. /* BLE */
  22. #include "esp_nimble_hci.h"
  23. #include "nimble/nimble_port.h"
  24. #include "nimble/nimble_port_freertos.h"
  25. #include "host/ble_hs.h"
  26. #include "host/util/util.h"
  27. #include "console/console.h"
  28. #include "services/gap/ble_svc_gap.h"
  29. #include "blecent.h"
  30. static const char *tag = "NimBLE_BLE_CENT";
  31. static int blecent_gap_event(struct ble_gap_event *event, void *arg);
  32. static uint8_t peer_addr[6];
  33. void ble_store_config_init(void);
  34. /**
  35. * Application callback. Called when the attempt to subscribe to notifications
  36. * for the ANS Unread Alert Status characteristic has completed.
  37. */
  38. static int
  39. blecent_on_subscribe(uint16_t conn_handle,
  40. const struct ble_gatt_error *error,
  41. struct ble_gatt_attr *attr,
  42. void *arg)
  43. {
  44. MODLOG_DFLT(INFO, "Subscribe complete; status=%d conn_handle=%d "
  45. "attr_handle=%d\n",
  46. error->status, conn_handle, attr->handle);
  47. return 0;
  48. }
  49. /**
  50. * Application callback. Called when the write to the ANS Alert Notification
  51. * Control Point characteristic has completed.
  52. */
  53. static int
  54. blecent_on_write(uint16_t conn_handle,
  55. const struct ble_gatt_error *error,
  56. struct ble_gatt_attr *attr,
  57. void *arg)
  58. {
  59. MODLOG_DFLT(INFO,
  60. "Write complete; status=%d conn_handle=%d attr_handle=%d\n",
  61. error->status, conn_handle, attr->handle);
  62. /* Subscribe to notifications for the Unread Alert Status characteristic.
  63. * A central enables notifications by writing two bytes (1, 0) to the
  64. * characteristic's client-characteristic-configuration-descriptor (CCCD).
  65. */
  66. const struct peer_dsc *dsc;
  67. uint8_t value[2];
  68. int rc;
  69. const struct peer *peer = peer_find(conn_handle);
  70. dsc = peer_dsc_find_uuid(peer,
  71. BLE_UUID16_DECLARE(BLECENT_SVC_ALERT_UUID),
  72. BLE_UUID16_DECLARE(BLECENT_CHR_UNR_ALERT_STAT_UUID),
  73. BLE_UUID16_DECLARE(BLE_GATT_DSC_CLT_CFG_UUID16));
  74. if (dsc == NULL) {
  75. MODLOG_DFLT(ERROR, "Error: Peer lacks a CCCD for the Unread Alert "
  76. "Status characteristic\n");
  77. goto err;
  78. }
  79. value[0] = 1;
  80. value[1] = 0;
  81. rc = ble_gattc_write_flat(conn_handle, dsc->dsc.handle,
  82. value, sizeof value, blecent_on_subscribe, NULL);
  83. if (rc != 0) {
  84. MODLOG_DFLT(ERROR, "Error: Failed to subscribe to characteristic; "
  85. "rc=%d\n", rc);
  86. goto err;
  87. }
  88. return 0;
  89. err:
  90. /* Terminate the connection. */
  91. return ble_gap_terminate(peer->conn_handle, BLE_ERR_REM_USER_CONN_TERM);
  92. }
  93. /**
  94. * Application callback. Called when the read of the ANS Supported New Alert
  95. * Category characteristic has completed.
  96. */
  97. static int
  98. blecent_on_read(uint16_t conn_handle,
  99. const struct ble_gatt_error *error,
  100. struct ble_gatt_attr *attr,
  101. void *arg)
  102. {
  103. MODLOG_DFLT(INFO, "Read complete; status=%d conn_handle=%d", error->status,
  104. conn_handle);
  105. if (error->status == 0) {
  106. MODLOG_DFLT(INFO, " attr_handle=%d value=", attr->handle);
  107. print_mbuf(attr->om);
  108. }
  109. MODLOG_DFLT(INFO, "\n");
  110. /* Write two bytes (99, 100) to the alert-notification-control-point
  111. * characteristic.
  112. */
  113. const struct peer_chr *chr;
  114. uint8_t value[2];
  115. int rc;
  116. const struct peer *peer = peer_find(conn_handle);
  117. chr = peer_chr_find_uuid(peer,
  118. BLE_UUID16_DECLARE(BLECENT_SVC_ALERT_UUID),
  119. BLE_UUID16_DECLARE(BLECENT_CHR_ALERT_NOT_CTRL_PT));
  120. if (chr == NULL) {
  121. MODLOG_DFLT(ERROR, "Error: Peer doesn't support the Alert "
  122. "Notification Control Point characteristic\n");
  123. goto err;
  124. }
  125. value[0] = 99;
  126. value[1] = 100;
  127. rc = ble_gattc_write_flat(conn_handle, chr->chr.val_handle,
  128. value, sizeof value, blecent_on_write, NULL);
  129. if (rc != 0) {
  130. MODLOG_DFLT(ERROR, "Error: Failed to write characteristic; rc=%d\n",
  131. rc);
  132. goto err;
  133. }
  134. return 0;
  135. err:
  136. /* Terminate the connection. */
  137. return ble_gap_terminate(peer->conn_handle, BLE_ERR_REM_USER_CONN_TERM);
  138. }
  139. /**
  140. * Performs three GATT operations against the specified peer:
  141. * 1. Reads the ANS Supported New Alert Category characteristic.
  142. * 2. After read is completed, writes the ANS Alert Notification Control Point characteristic.
  143. * 3. After write is completed, subscribes to notifications for the ANS Unread Alert Status
  144. * characteristic.
  145. *
  146. * If the peer does not support a required service, characteristic, or
  147. * descriptor, then the peer lied when it claimed support for the alert
  148. * notification service! When this happens, or if a GATT procedure fails,
  149. * this function immediately terminates the connection.
  150. */
  151. static void
  152. blecent_read_write_subscribe(const struct peer *peer)
  153. {
  154. const struct peer_chr *chr;
  155. int rc;
  156. /* Read the supported-new-alert-category characteristic. */
  157. chr = peer_chr_find_uuid(peer,
  158. BLE_UUID16_DECLARE(BLECENT_SVC_ALERT_UUID),
  159. BLE_UUID16_DECLARE(BLECENT_CHR_SUP_NEW_ALERT_CAT_UUID));
  160. if (chr == NULL) {
  161. MODLOG_DFLT(ERROR, "Error: Peer doesn't support the Supported New "
  162. "Alert Category characteristic\n");
  163. goto err;
  164. }
  165. rc = ble_gattc_read(peer->conn_handle, chr->chr.val_handle,
  166. blecent_on_read, NULL);
  167. if (rc != 0) {
  168. MODLOG_DFLT(ERROR, "Error: Failed to read characteristic; rc=%d\n",
  169. rc);
  170. goto err;
  171. }
  172. return;
  173. err:
  174. /* Terminate the connection. */
  175. ble_gap_terminate(peer->conn_handle, BLE_ERR_REM_USER_CONN_TERM);
  176. }
  177. /**
  178. * Called when service discovery of the specified peer has completed.
  179. */
  180. static void
  181. blecent_on_disc_complete(const struct peer *peer, int status, void *arg)
  182. {
  183. if (status != 0) {
  184. /* Service discovery failed. Terminate the connection. */
  185. MODLOG_DFLT(ERROR, "Error: Service discovery failed; status=%d "
  186. "conn_handle=%d\n", status, peer->conn_handle);
  187. ble_gap_terminate(peer->conn_handle, BLE_ERR_REM_USER_CONN_TERM);
  188. return;
  189. }
  190. /* Service discovery has completed successfully. Now we have a complete
  191. * list of services, characteristics, and descriptors that the peer
  192. * supports.
  193. */
  194. MODLOG_DFLT(ERROR, "Service discovery complete; status=%d "
  195. "conn_handle=%d\n", status, peer->conn_handle);
  196. /* Now perform three GATT procedures against the peer: read,
  197. * write, and subscribe to notifications.
  198. */
  199. blecent_read_write_subscribe(peer);
  200. }
  201. /**
  202. * Initiates the GAP general discovery procedure.
  203. */
  204. static void
  205. blecent_scan(void)
  206. {
  207. uint8_t own_addr_type;
  208. struct ble_gap_disc_params disc_params;
  209. int rc;
  210. /* Figure out address to use while advertising (no privacy for now) */
  211. rc = ble_hs_id_infer_auto(0, &own_addr_type);
  212. if (rc != 0) {
  213. MODLOG_DFLT(ERROR, "error determining address type; rc=%d\n", rc);
  214. return;
  215. }
  216. /* Tell the controller to filter duplicates; we don't want to process
  217. * repeated advertisements from the same device.
  218. */
  219. disc_params.filter_duplicates = 1;
  220. /**
  221. * Perform a passive scan. I.e., don't send follow-up scan requests to
  222. * each advertiser.
  223. */
  224. disc_params.passive = 1;
  225. /* Use defaults for the rest of the parameters. */
  226. disc_params.itvl = 0;
  227. disc_params.window = 0;
  228. disc_params.filter_policy = 0;
  229. disc_params.limited = 0;
  230. rc = ble_gap_disc(own_addr_type, BLE_HS_FOREVER, &disc_params,
  231. blecent_gap_event, NULL);
  232. if (rc != 0) {
  233. MODLOG_DFLT(ERROR, "Error initiating GAP discovery procedure; rc=%d\n",
  234. rc);
  235. }
  236. }
  237. /**
  238. * Indicates whether we should try to connect to the sender of the specified
  239. * advertisement. The function returns a positive result if the device
  240. * advertises connectability and support for the Alert Notification service.
  241. */
  242. static int
  243. blecent_should_connect(const struct ble_gap_disc_desc *disc)
  244. {
  245. struct ble_hs_adv_fields fields;
  246. int rc;
  247. int i;
  248. /* The device has to be advertising connectability. */
  249. if (disc->event_type != BLE_HCI_ADV_RPT_EVTYPE_ADV_IND &&
  250. disc->event_type != BLE_HCI_ADV_RPT_EVTYPE_DIR_IND) {
  251. return 0;
  252. }
  253. rc = ble_hs_adv_parse_fields(&fields, disc->data, disc->length_data);
  254. if (rc != 0) {
  255. return rc;
  256. }
  257. if (strlen(CONFIG_EXAMPLE_PEER_ADDR) && (strncmp(CONFIG_EXAMPLE_PEER_ADDR, "ADDR_ANY", strlen("ADDR_ANY")) != 0)) {
  258. ESP_LOGI(tag, "Peer address from menuconfig: %s", CONFIG_EXAMPLE_PEER_ADDR);
  259. /* Convert string to address */
  260. sscanf(CONFIG_EXAMPLE_PEER_ADDR, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx",
  261. &peer_addr[5], &peer_addr[4], &peer_addr[3],
  262. &peer_addr[2], &peer_addr[1], &peer_addr[0]);
  263. if (memcmp(peer_addr, disc->addr.val, sizeof(disc->addr.val)) != 0) {
  264. return 0;
  265. }
  266. }
  267. /* The device has to advertise support for the Alert Notification
  268. * service (0x1811).
  269. */
  270. for (i = 0; i < fields.num_uuids16; i++) {
  271. if (ble_uuid_u16(&fields.uuids16[i].u) == BLECENT_SVC_ALERT_UUID) {
  272. return 1;
  273. }
  274. }
  275. return 0;
  276. }
  277. /**
  278. * Connects to the sender of the specified advertisement of it looks
  279. * interesting. A device is "interesting" if it advertises connectability and
  280. * support for the Alert Notification service.
  281. */
  282. static void
  283. blecent_connect_if_interesting(const struct ble_gap_disc_desc *disc)
  284. {
  285. uint8_t own_addr_type;
  286. int rc;
  287. /* Don't do anything if we don't care about this advertiser. */
  288. if (!blecent_should_connect(disc)) {
  289. return;
  290. }
  291. /* Scanning must be stopped before a connection can be initiated. */
  292. rc = ble_gap_disc_cancel();
  293. if (rc != 0) {
  294. MODLOG_DFLT(DEBUG, "Failed to cancel scan; rc=%d\n", rc);
  295. return;
  296. }
  297. /* Figure out address to use for connect (no privacy for now) */
  298. rc = ble_hs_id_infer_auto(0, &own_addr_type);
  299. if (rc != 0) {
  300. MODLOG_DFLT(ERROR, "error determining address type; rc=%d\n", rc);
  301. return;
  302. }
  303. /* Try to connect the the advertiser. Allow 30 seconds (30000 ms) for
  304. * timeout.
  305. */
  306. rc = ble_gap_connect(own_addr_type, &disc->addr, 30000, NULL,
  307. blecent_gap_event, NULL);
  308. if (rc != 0) {
  309. MODLOG_DFLT(ERROR, "Error: Failed to connect to device; addr_type=%d "
  310. "addr=%s; rc=%d\n",
  311. disc->addr.type, addr_str(disc->addr.val), rc);
  312. return;
  313. }
  314. }
  315. /**
  316. * The nimble host executes this callback when a GAP event occurs. The
  317. * application associates a GAP event callback with each connection that is
  318. * established. blecent uses the same callback for all connections.
  319. *
  320. * @param event The event being signalled.
  321. * @param arg Application-specified argument; unused by
  322. * blecent.
  323. *
  324. * @return 0 if the application successfully handled the
  325. * event; nonzero on failure. The semantics
  326. * of the return code is specific to the
  327. * particular GAP event being signalled.
  328. */
  329. static int
  330. blecent_gap_event(struct ble_gap_event *event, void *arg)
  331. {
  332. struct ble_gap_conn_desc desc;
  333. struct ble_hs_adv_fields fields;
  334. int rc;
  335. switch (event->type) {
  336. case BLE_GAP_EVENT_DISC:
  337. rc = ble_hs_adv_parse_fields(&fields, event->disc.data,
  338. event->disc.length_data);
  339. if (rc != 0) {
  340. return 0;
  341. }
  342. /* An advertisment report was received during GAP discovery. */
  343. print_adv_fields(&fields);
  344. /* Try to connect to the advertiser if it looks interesting. */
  345. blecent_connect_if_interesting(&event->disc);
  346. return 0;
  347. case BLE_GAP_EVENT_CONNECT:
  348. /* A new connection was established or a connection attempt failed. */
  349. if (event->connect.status == 0) {
  350. /* Connection successfully established. */
  351. MODLOG_DFLT(INFO, "Connection established ");
  352. rc = ble_gap_conn_find(event->connect.conn_handle, &desc);
  353. assert(rc == 0);
  354. print_conn_desc(&desc);
  355. MODLOG_DFLT(INFO, "\n");
  356. /* Remember peer. */
  357. rc = peer_add(event->connect.conn_handle);
  358. if (rc != 0) {
  359. MODLOG_DFLT(ERROR, "Failed to add peer; rc=%d\n", rc);
  360. return 0;
  361. }
  362. /* Perform service discovery. */
  363. rc = peer_disc_all(event->connect.conn_handle,
  364. blecent_on_disc_complete, NULL);
  365. if (rc != 0) {
  366. MODLOG_DFLT(ERROR, "Failed to discover services; rc=%d\n", rc);
  367. return 0;
  368. }
  369. } else {
  370. /* Connection attempt failed; resume scanning. */
  371. MODLOG_DFLT(ERROR, "Error: Connection failed; status=%d\n",
  372. event->connect.status);
  373. blecent_scan();
  374. }
  375. return 0;
  376. case BLE_GAP_EVENT_DISCONNECT:
  377. /* Connection terminated. */
  378. MODLOG_DFLT(INFO, "disconnect; reason=%d ", event->disconnect.reason);
  379. print_conn_desc(&event->disconnect.conn);
  380. MODLOG_DFLT(INFO, "\n");
  381. /* Forget about peer. */
  382. peer_delete(event->disconnect.conn.conn_handle);
  383. /* Resume scanning. */
  384. blecent_scan();
  385. return 0;
  386. case BLE_GAP_EVENT_DISC_COMPLETE:
  387. MODLOG_DFLT(INFO, "discovery complete; reason=%d\n",
  388. event->disc_complete.reason);
  389. return 0;
  390. case BLE_GAP_EVENT_ENC_CHANGE:
  391. /* Encryption has been enabled or disabled for this connection. */
  392. MODLOG_DFLT(INFO, "encryption change event; status=%d ",
  393. event->enc_change.status);
  394. rc = ble_gap_conn_find(event->enc_change.conn_handle, &desc);
  395. assert(rc == 0);
  396. print_conn_desc(&desc);
  397. return 0;
  398. case BLE_GAP_EVENT_NOTIFY_RX:
  399. /* Peer sent us a notification or indication. */
  400. MODLOG_DFLT(INFO, "received %s; conn_handle=%d attr_handle=%d "
  401. "attr_len=%d\n",
  402. event->notify_rx.indication ?
  403. "indication" :
  404. "notification",
  405. event->notify_rx.conn_handle,
  406. event->notify_rx.attr_handle,
  407. OS_MBUF_PKTLEN(event->notify_rx.om));
  408. /* Attribute data is contained in event->notify_rx.om. Use
  409. * `os_mbuf_copydata` to copy the data received in notification mbuf */
  410. return 0;
  411. case BLE_GAP_EVENT_MTU:
  412. MODLOG_DFLT(INFO, "mtu update event; conn_handle=%d cid=%d mtu=%d\n",
  413. event->mtu.conn_handle,
  414. event->mtu.channel_id,
  415. event->mtu.value);
  416. return 0;
  417. case BLE_GAP_EVENT_REPEAT_PAIRING:
  418. /* We already have a bond with the peer, but it is attempting to
  419. * establish a new secure link. This app sacrifices security for
  420. * convenience: just throw away the old bond and accept the new link.
  421. */
  422. /* Delete the old bond. */
  423. rc = ble_gap_conn_find(event->repeat_pairing.conn_handle, &desc);
  424. assert(rc == 0);
  425. ble_store_util_delete_peer(&desc.peer_id_addr);
  426. /* Return BLE_GAP_REPEAT_PAIRING_RETRY to indicate that the host should
  427. * continue with the pairing operation.
  428. */
  429. return BLE_GAP_REPEAT_PAIRING_RETRY;
  430. default:
  431. return 0;
  432. }
  433. }
  434. static void
  435. blecent_on_reset(int reason)
  436. {
  437. MODLOG_DFLT(ERROR, "Resetting state; reason=%d\n", reason);
  438. }
  439. static void
  440. blecent_on_sync(void)
  441. {
  442. int rc;
  443. /* Make sure we have proper identity address set (public preferred) */
  444. rc = ble_hs_util_ensure_addr(0);
  445. assert(rc == 0);
  446. /* Begin scanning for a peripheral to connect to. */
  447. blecent_scan();
  448. }
  449. void blecent_host_task(void *param)
  450. {
  451. ESP_LOGI(tag, "BLE Host Task Started");
  452. /* This function will return only when nimble_port_stop() is executed */
  453. nimble_port_run();
  454. nimble_port_freertos_deinit();
  455. }
  456. void
  457. app_main(void)
  458. {
  459. int rc;
  460. /* Initialize NVS — it is used to store PHY calibration data */
  461. esp_err_t ret = nvs_flash_init();
  462. if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
  463. ESP_ERROR_CHECK(nvs_flash_erase());
  464. ret = nvs_flash_init();
  465. }
  466. ESP_ERROR_CHECK(ret);
  467. ESP_ERROR_CHECK(esp_nimble_hci_and_controller_init());
  468. nimble_port_init();
  469. /* Configure the host. */
  470. ble_hs_cfg.reset_cb = blecent_on_reset;
  471. ble_hs_cfg.sync_cb = blecent_on_sync;
  472. ble_hs_cfg.store_status_cb = ble_store_util_status_rr;
  473. /* Initialize data structures to track connected peers. */
  474. rc = peer_init(MYNEWT_VAL(BLE_MAX_CONNECTIONS), 64, 64, 64);
  475. assert(rc == 0);
  476. /* Set the default device name. */
  477. rc = ble_svc_gap_device_name_set("nimble-blecent");
  478. assert(rc == 0);
  479. /* XXX Need to have template for store */
  480. ble_store_config_init();
  481. nimble_port_freertos_init(blecent_host_task);
  482. }