S&B Volcano vaporizer remote control with Pi Pico W
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729
  1. /*
  2. * ble.c
  3. *
  4. * https://github.com/raspberrypi/pico-examples/blob/master/pico_w/bt/standalone/client.c
  5. * https://vanhunteradams.com/Pico/BLE/BTStack_HCI.html
  6. *
  7. * Copyright (c) 2023 Thomas Buck (thomas@xythobuz.de)
  8. *
  9. * This program is free software: you can redistribute it and/or modify
  10. * it under the terms of the GNU General Public License as published by
  11. * the Free Software Foundation, either version 3 of the License, or
  12. * (at your option) any later version.
  13. *
  14. * This program is distributed in the hope that it will be useful,
  15. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  16. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  17. * GNU General Public License for more details.
  18. *
  19. * See <http://www.gnu.org/licenses/>.
  20. */
  21. #include "pico/cyw43_arch.h"
  22. #include "hardware/watchdog.h"
  23. #include "config.h"
  24. #include "log.h"
  25. #include "util.h"
  26. #include "ble.h"
  27. #define BLE_READ_TIMEOUT_MS 500
  28. #define BLE_SRVC_TIMEOUT_MS 500
  29. #define BLE_CHAR_TIMEOUT_MS 2000
  30. #define BLE_WRTE_TIMEOUT_MS 500
  31. #define BLE_MAX_SCAN_AGE_MS (10 * 1000)
  32. #define BLE_MAX_SERVICES 8
  33. #define BLE_MAX_CHARACTERISTICS 8
  34. enum ble_state {
  35. TC_OFF = 0,
  36. TC_IDLE,
  37. TC_W4_SCAN,
  38. TC_W4_CONNECT,
  39. TC_READY,
  40. TC_W4_READ,
  41. TC_READ_COMPLETE,
  42. TC_W4_SERVICE,
  43. TC_W4_CHARACTERISTIC,
  44. TC_W4_WRITE,
  45. TC_WRITE_COMPLETE,
  46. };
  47. struct ble_characteristic {
  48. bool set;
  49. gatt_client_characteristic_t c;
  50. };
  51. struct ble_service {
  52. bool set;
  53. gatt_client_service_t service;
  54. struct ble_characteristic chars[BLE_MAX_CHARACTERISTICS];
  55. };
  56. static btstack_packet_callback_registration_t hci_event_callback_registration;
  57. static hci_con_handle_t connection_handle;
  58. static enum ble_state state = TC_OFF;
  59. static struct ble_scan_result scans[BLE_MAX_SCAN_RESULTS] = {0};
  60. static uint16_t read_len = 0;
  61. static uint8_t data_buff[BLE_MAX_VALUE_LEN] = {0};
  62. static struct ble_service services[BLE_MAX_SERVICES] = {0};
  63. static uint8_t service_idx = 0;
  64. static uint8_t characteristic_idx = 0;
  65. static void hci_add_scan_result(bd_addr_t addr, bd_addr_type_t type, int8_t rssi) {
  66. int unused = -1;
  67. for (uint i = 0; i < BLE_MAX_SCAN_RESULTS; i++) {
  68. if (!scans[i].set) {
  69. if (unused < 0) {
  70. unused = i;
  71. }
  72. continue;
  73. }
  74. if (memcmp(addr, scans[i].addr, sizeof(bd_addr_t)) == 0) {
  75. // already in list, just update changing values
  76. scans[i].time = to_ms_since_boot(get_absolute_time());
  77. scans[i].rssi = rssi;
  78. return;
  79. }
  80. }
  81. if (unused < 0) {
  82. debug("no space in scan results for %s", bd_addr_to_str(addr));
  83. return;
  84. }
  85. debug("new device with addr %s", bd_addr_to_str(addr));
  86. scans[unused].set = true;
  87. scans[unused].time = to_ms_since_boot(get_absolute_time());
  88. memcpy(scans[unused].addr, addr, sizeof(bd_addr_t));
  89. scans[unused].type = type;
  90. scans[unused].rssi = rssi;
  91. scans[unused].name[0] = '\0';
  92. }
  93. static void hci_scan_result_add_name(bd_addr_t addr, const uint8_t *data, uint8_t data_size) {
  94. for (uint i = 0; i < BLE_MAX_SCAN_RESULTS; i++) {
  95. if (!scans[i].set) {
  96. continue;
  97. }
  98. if (memcmp(addr, scans[i].addr, sizeof(bd_addr_t)) != 0) {
  99. continue;
  100. }
  101. uint8_t len = data_size;
  102. if (len > BLE_MAX_NAME_LENGTH) {
  103. len = BLE_MAX_NAME_LENGTH;
  104. }
  105. memcpy(scans[i].name, data, len);
  106. scans[i].name[len] = '\0';
  107. scans[i].time = to_ms_since_boot(get_absolute_time());
  108. return;
  109. }
  110. debug("no matching entry for %s to add name '%.*s' to", bd_addr_to_str(addr), data_size, data);
  111. }
  112. static void hci_event_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size) {
  113. UNUSED(size);
  114. UNUSED(channel);
  115. //debug("type=0x%02X size=%d", packet_type, size);
  116. //hexdump(packet, size);
  117. if (packet_type != HCI_EVENT_PACKET) {
  118. //debug("unexpected packet 0x%02X", packet_type);
  119. return;
  120. }
  121. switch (hci_event_packet_get_type(packet)) {
  122. case BTSTACK_EVENT_STATE:
  123. if (btstack_event_state_get_state(packet) == HCI_STATE_WORKING) {
  124. bd_addr_t local_addr;
  125. gap_local_bd_addr(local_addr);
  126. debug("BTstack up with %s", bd_addr_to_str(local_addr));
  127. state = TC_IDLE;
  128. } else {
  129. debug("BTstack down (%d)", btstack_event_state_get_state(packet));
  130. state = TC_OFF;
  131. }
  132. break;
  133. case GAP_EVENT_ADVERTISING_REPORT: {
  134. if (state != TC_W4_SCAN) {
  135. debug("scan result in invalid state %d", state);
  136. return;
  137. }
  138. bd_addr_t addr;
  139. gap_event_advertising_report_get_address(packet, addr);
  140. bd_addr_type_t type;
  141. type = gap_event_advertising_report_get_address_type(packet);
  142. int8_t rssi;
  143. rssi = (int8_t)gap_event_advertising_report_get_rssi(packet);
  144. // add data received so far
  145. hci_add_scan_result(addr, type, rssi);
  146. // get advertisement from report event
  147. const uint8_t *adv_data = gap_event_advertising_report_get_data(packet);
  148. uint8_t adv_len = gap_event_advertising_report_get_data_length(packet);
  149. // iterate over advertisement data
  150. ad_context_t context;
  151. for (ad_iterator_init(&context, adv_len, adv_data);
  152. ad_iterator_has_more(&context);
  153. ad_iterator_next(&context)) {
  154. uint8_t data_type = ad_iterator_get_data_type(&context);
  155. uint8_t data_size = ad_iterator_get_data_len(&context);
  156. const uint8_t *data = ad_iterator_get_data(&context);
  157. switch (data_type) {
  158. case BLUETOOTH_DATA_TYPE_SHORTENED_LOCAL_NAME:
  159. case BLUETOOTH_DATA_TYPE_COMPLETE_LOCAL_NAME:
  160. hci_scan_result_add_name(addr, data, data_size);
  161. break;
  162. default:
  163. //debug("Unexpected advertisement type 0x%02X from %s", data_type, bd_addr_to_str(addr));
  164. //hexdump(data, data_size);
  165. break;
  166. }
  167. }
  168. break;
  169. }
  170. case HCI_EVENT_LE_META:
  171. switch (hci_event_le_meta_get_subevent_code(packet)) {
  172. case HCI_SUBEVENT_LE_CONNECTION_COMPLETE:
  173. if (state != TC_W4_CONNECT) {
  174. return;
  175. }
  176. debug("connection complete");
  177. connection_handle = hci_subevent_le_connection_complete_get_connection_handle(packet);
  178. state = TC_READY;
  179. break;
  180. default:
  181. //debug("unexpected LE meta event 0x%02X", hci_event_le_meta_get_subevent_code(packet));
  182. break;
  183. }
  184. break;
  185. case HCI_EVENT_DISCONNECTION_COMPLETE:
  186. debug("disconnected");
  187. connection_handle = HCI_CON_HANDLE_INVALID;
  188. state = TC_IDLE;
  189. break;
  190. case GATT_EVENT_CHARACTERISTIC_VALUE_QUERY_RESULT:
  191. if (state != TC_W4_READ) {
  192. debug("gatt value query result in invalid state %d", state);
  193. return;
  194. }
  195. uint16_t len = gatt_event_characteristic_value_query_result_get_value_length(packet);
  196. if ((read_len + len) > BLE_MAX_VALUE_LEN) {
  197. debug("not enough space for value (%d + %d > %d)", read_len, len, BLE_MAX_VALUE_LEN);
  198. return;
  199. }
  200. memcpy(data_buff + read_len,
  201. gatt_event_characteristic_value_query_result_get_value(packet),
  202. len);
  203. read_len += len;
  204. break;
  205. case GATT_EVENT_SERVICE_QUERY_RESULT:
  206. if (state != TC_W4_SERVICE) {
  207. debug("gatt service query result in invalid state %d", state);
  208. return;
  209. }
  210. gatt_event_service_query_result_get_service(packet, &services[service_idx].service);
  211. debug("got service %s result", uuid128_to_str(services[service_idx].service.uuid128));
  212. break;
  213. case GATT_EVENT_CHARACTERISTIC_QUERY_RESULT:
  214. if (state != TC_W4_CHARACTERISTIC) {
  215. debug("gatt characteristic query result in invalid state %d", state);
  216. return;
  217. }
  218. gatt_event_characteristic_query_result_get_characteristic(packet, &services[service_idx].chars[characteristic_idx].c);
  219. debug("got characteristic %s result", uuid128_to_str(services[service_idx].chars[characteristic_idx].c.uuid128));
  220. break;
  221. case GATT_EVENT_QUERY_COMPLETE: {
  222. uint8_t att_status = gatt_event_query_complete_get_att_status(packet);
  223. if (att_status != ATT_ERROR_SUCCESS){
  224. debug("query result has ATT Error 0x%02x in %d", att_status, state);
  225. state = TC_READY;
  226. break;
  227. }
  228. switch (state) {
  229. case TC_W4_READ:
  230. state = TC_READ_COMPLETE;
  231. break;
  232. case TC_W4_SERVICE:
  233. debug("service %s complete", uuid128_to_str(services[service_idx].service.uuid128));
  234. state = TC_READY;
  235. break;
  236. case TC_W4_CHARACTERISTIC:
  237. debug("characteristic %s complete", uuid128_to_str(services[service_idx].chars[characteristic_idx].c.uuid128));
  238. state = TC_READY;
  239. break;
  240. case TC_W4_WRITE:
  241. debug("write complete");
  242. state = TC_WRITE_COMPLETE;
  243. break;
  244. default:
  245. debug("gatt query complete in invalid state %d", state);
  246. break;
  247. }
  248. break;
  249. }
  250. default:
  251. //debug("unexpected event 0x%02X", hci_event_packet_get_type(packet));
  252. break;
  253. }
  254. }
  255. void ble_init(void) {
  256. cyw43_thread_enter();
  257. state = TC_OFF;
  258. for (uint i = 0; i < BLE_MAX_SCAN_RESULTS; i++) {
  259. scans[i].set = false;
  260. }
  261. for (uint i = 0; i < BLE_MAX_SERVICES; i++) {
  262. services[i].set = false;
  263. for (uint j = 0; j < BLE_MAX_CHARACTERISTICS; j++) {
  264. services[i].chars[j].set = false;
  265. }
  266. }
  267. cyw43_thread_exit();
  268. l2cap_init();
  269. sm_init();
  270. sm_set_io_capabilities(IO_CAPABILITY_NO_INPUT_NO_OUTPUT);
  271. gatt_client_init();
  272. hci_event_callback_registration.callback = &hci_event_handler;
  273. hci_add_event_handler(&hci_event_callback_registration);
  274. hci_power_control(HCI_POWER_ON);
  275. }
  276. bool ble_is_ready(void) {
  277. cyw43_thread_enter();
  278. bool v = (state != TC_OFF);
  279. cyw43_thread_exit();
  280. return v;
  281. }
  282. void ble_scan(enum ble_scan_mode mode) {
  283. cyw43_thread_enter();
  284. if (state == TC_OFF) {
  285. cyw43_thread_exit();
  286. return;
  287. }
  288. switch (mode) {
  289. case BLE_SCAN_OFF:
  290. debug("stopping BLE scan");
  291. gap_stop_scan();
  292. state = TC_IDLE;
  293. break;
  294. case BLE_SCAN_ON:
  295. debug("starting BLE scan");
  296. state = TC_W4_SCAN;
  297. gap_set_scan_parameters(1, 0x0030, 0x0030);
  298. gap_start_scan();
  299. break;
  300. case BLE_SCAN_TOGGLE:
  301. switch (state) {
  302. case TC_W4_SCAN:
  303. cyw43_thread_exit();
  304. ble_scan(0);
  305. return;
  306. case TC_IDLE:
  307. cyw43_thread_exit();
  308. ble_scan(1);
  309. return;
  310. default:
  311. debug("invalid state %d", state);
  312. break;
  313. }
  314. break;
  315. default:
  316. debug("invalid mode %d", mode);
  317. break;
  318. }
  319. cyw43_thread_exit();
  320. }
  321. int32_t ble_get_scan_results(struct ble_scan_result *buf, uint16_t len) {
  322. if (!buf || (len <= 0)) {
  323. return -1;
  324. }
  325. cyw43_thread_enter();
  326. if (state == TC_OFF) {
  327. cyw43_thread_exit();
  328. return -1;
  329. }
  330. uint16_t pos = 0;
  331. for (uint16_t i = 0; i < BLE_MAX_SCAN_RESULTS; i++) {
  332. if (!scans[i].set) {
  333. continue;
  334. }
  335. uint32_t diff = to_ms_since_boot(get_absolute_time()) - scans[i].time;
  336. if (diff >= BLE_MAX_SCAN_AGE_MS) {
  337. //debug("removing %s due to age", bd_addr_to_str(scans[i].addr));
  338. scans[i].set = false;
  339. }
  340. memcpy(buf + pos, scans + i, sizeof(struct ble_scan_result));
  341. pos++;
  342. if (pos >= len) {
  343. break;
  344. }
  345. }
  346. cyw43_thread_exit();
  347. return pos;
  348. }
  349. void ble_connect(bd_addr_t addr, bd_addr_type_t type) {
  350. cyw43_thread_enter();
  351. switch (state) {
  352. case TC_W4_SCAN:
  353. cyw43_thread_exit();
  354. ble_scan(0);
  355. cyw43_thread_enter();
  356. break;
  357. case TC_READY:
  358. gap_disconnect(connection_handle);
  359. break;
  360. case TC_IDLE:
  361. break;
  362. default:
  363. debug("invalid state for connect %d", state);
  364. cyw43_thread_exit();
  365. return;
  366. }
  367. debug("connecting to %s", bd_addr_to_str(addr));
  368. state = TC_W4_CONNECT;
  369. gap_connect(addr, type);
  370. cyw43_thread_exit();
  371. }
  372. bool ble_is_connected(void) {
  373. cyw43_thread_enter();
  374. bool v = (state == TC_READY)
  375. || (state == TC_W4_READ)
  376. || (state == TC_READ_COMPLETE);
  377. cyw43_thread_exit();
  378. return v;
  379. }
  380. void ble_disconnect(void) {
  381. cyw43_thread_enter();
  382. if (state == TC_READY) {
  383. debug("disconnecting");
  384. gap_disconnect(connection_handle);
  385. } else {
  386. debug("invalid state for disconnect %d", state);
  387. }
  388. cyw43_thread_exit();
  389. }
  390. int32_t ble_read(const uint8_t *characteristic, uint8_t *buff, uint16_t buff_len) {
  391. cyw43_thread_enter();
  392. if (state != TC_READY) {
  393. cyw43_thread_exit();
  394. debug("invalid state for read (%d)", state);
  395. return -1;
  396. }
  397. uint8_t r = gatt_client_read_value_of_characteristics_by_uuid128(hci_event_handler,
  398. connection_handle,
  399. 0x0001, 0xFFFF,
  400. characteristic);
  401. if (r != ERROR_CODE_SUCCESS) {
  402. cyw43_thread_exit();
  403. debug("gatt read failed %d", r);
  404. return -2;
  405. }
  406. state = TC_W4_READ;
  407. read_len = 0;
  408. cyw43_thread_exit();
  409. uint32_t start_time = to_ms_since_boot(get_absolute_time());
  410. while (1) {
  411. sleep_ms(1);
  412. uint32_t now = to_ms_since_boot(get_absolute_time());
  413. if ((now - start_time) >= BLE_READ_TIMEOUT_MS) {
  414. debug("timeout waiting for read");
  415. return -3;
  416. }
  417. #if BLE_READ_TIMEOUT_MS >= (WATCHDOG_PERIOD_MS / 2)
  418. watchdog_update();
  419. #endif
  420. cyw43_thread_enter();
  421. enum ble_state state_cached = state;
  422. cyw43_thread_exit();
  423. if (state_cached == TC_READ_COMPLETE) {
  424. break;
  425. }
  426. }
  427. cyw43_thread_enter();
  428. state = TC_READY;
  429. if (read_len > buff_len) {
  430. debug("buffer too short (%d < %d)", buff_len, read_len);
  431. cyw43_thread_exit();
  432. return -4;
  433. }
  434. memcpy(buff, data_buff, read_len);
  435. cyw43_thread_exit();
  436. return read_len;
  437. }
  438. int8_t ble_write(const uint8_t *service, const uint8_t *characteristic,
  439. uint8_t *buff, uint16_t buff_len) {
  440. cyw43_thread_enter();
  441. if (state != TC_READY) {
  442. cyw43_thread_exit();
  443. debug("invalid state for write (%d)", state);
  444. return -1;
  445. }
  446. // check if service has already been discovered
  447. int srvc = -1, free_srvc = -1;
  448. for (int i = 0; i < BLE_MAX_SERVICES; i++) {
  449. if (!services[i].set) {
  450. if (free_srvc < 0) {
  451. free_srvc = i;
  452. }
  453. continue;
  454. }
  455. if (memcmp(services[i].service.uuid128, service, 16) == 0) {
  456. srvc = i;
  457. break;
  458. }
  459. }
  460. // if this service has not been discovered yet, add it
  461. if (srvc < 0) {
  462. if (free_srvc < 0) {
  463. debug("no space left for BLE service. overwriting.");
  464. free_srvc = 0;
  465. }
  466. srvc = free_srvc;
  467. services[srvc].set = true;
  468. debug("discovering service %s at %d", uuid128_to_str(service), srvc);
  469. uint8_t r = gatt_client_discover_primary_services_by_uuid128(hci_event_handler,
  470. connection_handle,
  471. service);
  472. if (r != ERROR_CODE_SUCCESS) {
  473. cyw43_thread_exit();
  474. debug("gatt service discovery failed %d", r);
  475. return -2;
  476. }
  477. state = TC_W4_SERVICE;
  478. service_idx = srvc;
  479. cyw43_thread_exit();
  480. uint32_t start_time = to_ms_since_boot(get_absolute_time());
  481. while (1) {
  482. sleep_ms(1);
  483. uint32_t now = to_ms_since_boot(get_absolute_time());
  484. if ((now - start_time) >= BLE_SRVC_TIMEOUT_MS) {
  485. debug("timeout waiting for service");
  486. return -3;
  487. }
  488. #if BLE_SRVC_TIMEOUT_MS >= (WATCHDOG_PERIOD_MS / 2)
  489. watchdog_update();
  490. #endif
  491. cyw43_thread_enter();
  492. enum ble_state state_cached = state;
  493. cyw43_thread_exit();
  494. if (state_cached == TC_READY) {
  495. break;
  496. }
  497. }
  498. // allow some time for service discovery
  499. watchdog_update();
  500. cyw43_thread_enter();
  501. }
  502. // check if characteristic has already been discovered
  503. int ch = -1, free_ch = -1;
  504. for (int i = 0; i < BLE_MAX_CHARACTERISTICS; i++) {
  505. if (!services[srvc].chars[i].set) {
  506. if (free_ch < 0) {
  507. free_ch = i;
  508. }
  509. continue;
  510. }
  511. if (memcmp(services[srvc].chars[i].c.uuid128, characteristic, 16) == 0) {
  512. ch = i;
  513. break;
  514. }
  515. }
  516. // if this characteristic has not been discovered yet, add it
  517. if (ch < 0) {
  518. if (free_ch < 0) {
  519. debug("no space left for BLE characteristic. overwriting.");
  520. free_ch = 0;
  521. }
  522. ch = free_ch;
  523. services[srvc].chars[ch].set = true;
  524. debug("discovering characteristic %s at %d", uuid128_to_str(characteristic), ch);
  525. uint8_t r = gatt_client_discover_characteristics_for_service_by_uuid128(hci_event_handler,
  526. connection_handle,
  527. &services[srvc].service,
  528. characteristic);
  529. if (r != ERROR_CODE_SUCCESS) {
  530. cyw43_thread_exit();
  531. debug("gatt characteristic discovery failed %d", r);
  532. return -4;
  533. }
  534. state = TC_W4_CHARACTERISTIC;
  535. characteristic_idx = ch;
  536. cyw43_thread_exit();
  537. uint32_t start_time = to_ms_since_boot(get_absolute_time());
  538. while (1) {
  539. sleep_ms(1);
  540. uint32_t now = to_ms_since_boot(get_absolute_time());
  541. if ((now - start_time) >= BLE_CHAR_TIMEOUT_MS) {
  542. debug("timeout waiting for characteristic");
  543. return -5;
  544. }
  545. #if BLE_CHAR_TIMEOUT_MS >= (WATCHDOG_PERIOD_MS / 2)
  546. watchdog_update();
  547. #endif
  548. cyw43_thread_enter();
  549. enum ble_state state_cached = state;
  550. cyw43_thread_exit();
  551. if (state_cached == TC_READY) {
  552. break;
  553. }
  554. }
  555. // allow some time for characteristic discovery
  556. watchdog_update();
  557. cyw43_thread_enter();
  558. }
  559. if (buff_len > BLE_MAX_VALUE_LEN) {
  560. buff_len = BLE_MAX_VALUE_LEN;
  561. }
  562. memcpy(data_buff, buff, buff_len);
  563. uint8_t r = gatt_client_write_value_of_characteristic(hci_event_handler,
  564. connection_handle,
  565. services[srvc].chars[ch].c.value_handle,
  566. buff_len, data_buff);
  567. if (r != ERROR_CODE_SUCCESS) {
  568. cyw43_thread_exit();
  569. debug("gatt write failed %d", r);
  570. return -6;
  571. }
  572. state = TC_W4_WRITE;
  573. cyw43_thread_exit();
  574. uint32_t start_time = to_ms_since_boot(get_absolute_time());
  575. while (1) {
  576. sleep_ms(1);
  577. uint32_t now = to_ms_since_boot(get_absolute_time());
  578. if ((now - start_time) >= BLE_WRTE_TIMEOUT_MS) {
  579. debug("timeout waiting for write");
  580. return -7;
  581. }
  582. #if BLE_WRTE_TIMEOUT_MS >= (WATCHDOG_PERIOD_MS / 2)
  583. watchdog_update();
  584. #endif
  585. cyw43_thread_enter();
  586. enum ble_state state_cached = state;
  587. cyw43_thread_exit();
  588. if ((state_cached == TC_WRITE_COMPLETE) || (state_cached == TC_READY)) {
  589. break;
  590. }
  591. }
  592. cyw43_thread_enter();
  593. int8_t ret = (state == TC_WRITE_COMPLETE) ? 0 : -8;
  594. state = TC_READY;
  595. cyw43_thread_exit();
  596. return ret;
  597. }