S&B Volcano vaporizer remote control with Pi Pico W
Du kan inte välja fler än 25 ämnen Ämnen måste starta med en bokstav eller siffra, kan innehålla bindestreck ('-') och vara max 35 tecken långa.

ble.c 8.2KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288
  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 "config.h"
  23. #include "log.h"
  24. #include "util.h"
  25. #include "ble.h"
  26. enum ble_state {
  27. TC_OFF = 0,
  28. TC_IDLE,
  29. TC_W4_SCAN_RESULT,
  30. TC_W4_CONNECT,
  31. TC_W4_SERVICE_RESULT,
  32. TC_W4_CHARACTERISTIC_RESULT,
  33. TC_W4_ENABLE_NOTIFICATIONS_COMPLETE,
  34. TC_W4_READY
  35. };
  36. static btstack_packet_callback_registration_t hci_event_callback_registration;
  37. static enum ble_state state = TC_OFF;
  38. static struct ble_scan_result scans[BLE_MAX_SCAN_RESULTS] = {0};
  39. // TODO scan result entries are not aging out
  40. static void hci_add_scan_result(bd_addr_t addr, bd_addr_type_t type, int8_t rssi) {
  41. int unused = -1;
  42. for (uint i = 0; i < BLE_MAX_SCAN_RESULTS; i++) {
  43. if (!scans[i].set) {
  44. if (unused < 0) {
  45. unused = i;
  46. }
  47. continue;
  48. }
  49. if (memcmp(addr, scans[i].addr, sizeof(bd_addr_t)) == 0) {
  50. // already in list, just update time for aging
  51. scans[i].time = to_ms_since_boot(get_absolute_time());
  52. return;
  53. }
  54. }
  55. if (unused < 0) {
  56. debug("no space in scan results for %s", bd_addr_to_str(addr));
  57. return;
  58. }
  59. debug("new device with addr %s", bd_addr_to_str(addr));
  60. scans[unused].set = true;
  61. scans[unused].time = to_ms_since_boot(get_absolute_time());
  62. memcpy(scans[unused].addr, addr, sizeof(bd_addr_t));
  63. scans[unused].type = type;
  64. scans[unused].rssi = rssi;
  65. scans[unused].name[0] = '\0';
  66. }
  67. static void hci_scan_result_add_name(bd_addr_t addr, const uint8_t *data, uint8_t data_size) {
  68. for (uint i = 0; i < BLE_MAX_SCAN_RESULTS; i++) {
  69. if (!scans[i].set) {
  70. continue;
  71. }
  72. if (memcmp(addr, scans[i].addr, sizeof(bd_addr_t)) != 0) {
  73. continue;
  74. }
  75. uint8_t len = data_size;
  76. if (len > BLE_MAX_NAME_LENGTH) {
  77. len = BLE_MAX_NAME_LENGTH;
  78. }
  79. memcpy(scans[i].name, data, len);
  80. scans[i].name[len] = '\0';
  81. scans[i].time = to_ms_since_boot(get_absolute_time());
  82. return;
  83. }
  84. debug("no matching entry for %s to add name '%.*s' to", bd_addr_to_str(addr), data_size, data);
  85. }
  86. static void hci_event_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size) {
  87. UNUSED(size);
  88. UNUSED(channel);
  89. if (packet_type != HCI_EVENT_PACKET) {
  90. debug("unexpected packet 0x%02X", packet_type);
  91. return;
  92. }
  93. switch (hci_event_packet_get_type(packet)) {
  94. case BTSTACK_EVENT_STATE:
  95. if (btstack_event_state_get_state(packet) == HCI_STATE_WORKING) {
  96. bd_addr_t local_addr;
  97. gap_local_bd_addr(local_addr);
  98. debug("BTstack up with %s", bd_addr_to_str(local_addr));
  99. state = TC_IDLE;
  100. } else {
  101. debug("BTstack down (%d)", btstack_event_state_get_state(packet));
  102. state = TC_OFF;
  103. }
  104. break;
  105. case HCI_EVENT_INQUIRY_COMPLETE:
  106. case HCI_EVENT_COMMAND_STATUS:
  107. case HCI_EVENT_REMOTE_HOST_SUPPORTED_FEATURES:
  108. case BTSTACK_EVENT_SCAN_MODE_CHANGED:
  109. case HCI_EVENT_COMMAND_COMPLETE:
  110. case HCI_EVENT_TRANSPORT_PACKET_SENT:
  111. break;
  112. case GAP_EVENT_ADVERTISING_REPORT: {
  113. if (state != TC_W4_SCAN_RESULT) {
  114. debug("scan result in invalid state %d", state);
  115. return;
  116. }
  117. bd_addr_t addr;
  118. gap_event_advertising_report_get_address(packet, addr);
  119. bd_addr_type_t type;
  120. type = gap_event_advertising_report_get_address_type(packet);
  121. int8_t rssi;
  122. rssi = (int8_t)gap_event_advertising_report_get_rssi(packet);
  123. // add data received so far
  124. hci_add_scan_result(addr, type, rssi);
  125. // get advertisement from report event
  126. const uint8_t *adv_data = gap_event_advertising_report_get_data(packet);
  127. uint8_t adv_len = gap_event_advertising_report_get_data_length(packet);
  128. // iterate over advertisement data
  129. ad_context_t context;
  130. for (ad_iterator_init(&context, adv_len, adv_data);
  131. ad_iterator_has_more(&context);
  132. ad_iterator_next(&context)) {
  133. uint8_t data_type = ad_iterator_get_data_type(&context);
  134. uint8_t data_size = ad_iterator_get_data_len(&context);
  135. const uint8_t *data = ad_iterator_get_data(&context);
  136. switch (data_type) {
  137. case BLUETOOTH_DATA_TYPE_SHORTENED_LOCAL_NAME:
  138. case BLUETOOTH_DATA_TYPE_COMPLETE_LOCAL_NAME:
  139. hci_scan_result_add_name(addr, data, data_size);
  140. break;
  141. case BLUETOOTH_DATA_TYPE_FLAGS:
  142. case BLUETOOTH_DATA_TYPE_TX_POWER_LEVEL:
  143. case BLUETOOTH_DATA_TYPE_COMPLETE_LIST_OF_16_BIT_SERVICE_CLASS_UUIDS:
  144. case BLUETOOTH_DATA_TYPE_SERVICE_DATA_16_BIT_UUID:
  145. case BLUETOOTH_DATA_TYPE_MANUFACTURER_SPECIFIC_DATA:
  146. case BLUETOOTH_DATA_TYPE_SLAVE_CONNECTION_INTERVAL_RANGE:
  147. break;
  148. default:
  149. debug("Unexpected advertisement type 0x%02X from %s", data_type, bd_addr_to_str(addr));
  150. hexdump(data, data_size);
  151. break;
  152. }
  153. }
  154. break;
  155. }
  156. case HCI_EVENT_LE_META:
  157. switch (hci_event_le_meta_get_subevent_code(packet)) {
  158. case HCI_SUBEVENT_LE_ADVERTISING_REPORT:
  159. // handled internally by BTstack
  160. break;
  161. case HCI_SUBEVENT_LE_CONNECTION_COMPLETE:
  162. debug("connection complete?!");
  163. break;
  164. default:
  165. debug("unexpected LE meta event 0x%02X", hci_event_le_meta_get_subevent_code(packet));
  166. break;
  167. }
  168. break;
  169. default:
  170. debug("unexpected event 0x%02X", hci_event_packet_get_type(packet));
  171. break;
  172. }
  173. }
  174. void ble_init(void) {
  175. for (uint i = 0; i < BLE_MAX_SCAN_RESULTS; i++) {
  176. scans[i].set = false;
  177. }
  178. l2cap_init();
  179. sm_init();
  180. sm_set_io_capabilities(IO_CAPABILITY_NO_INPUT_NO_OUTPUT);
  181. gatt_client_init();
  182. hci_event_callback_registration.callback = &hci_event_handler;
  183. hci_add_event_handler(&hci_event_callback_registration);
  184. hci_power_control(HCI_POWER_ON);
  185. }
  186. void ble_scan(enum ble_scan_mode mode) {
  187. cyw43_thread_enter();
  188. switch (mode) {
  189. case BLE_SCAN_OFF:
  190. debug("stopping BLE scan");
  191. state = TC_IDLE;
  192. gap_stop_scan();
  193. break;
  194. case BLE_SCAN_ON:
  195. debug("starting BLE scan");
  196. state = TC_W4_SCAN_RESULT;
  197. gap_set_scan_parameters(1, 0x0030, 0x0030);
  198. gap_start_scan();
  199. break;
  200. case BLE_SCAN_TOGGLE:
  201. switch (state) {
  202. case TC_W4_SCAN_RESULT:
  203. cyw43_thread_exit();
  204. ble_scan(0);
  205. return;
  206. case TC_IDLE:
  207. cyw43_thread_exit();
  208. ble_scan(1);
  209. return;
  210. default:
  211. debug("invalid state %d", state);
  212. break;
  213. }
  214. break;
  215. default:
  216. debug("invalid mode %d", mode);
  217. break;
  218. }
  219. cyw43_thread_exit();
  220. }
  221. int ble_get_scan_results(struct ble_scan_result *buf, uint len) {
  222. if (!buf || (len <= 0)) {
  223. return -1;
  224. }
  225. cyw43_thread_enter();
  226. uint pos = 0;
  227. for (uint i = 0; i < BLE_MAX_SCAN_RESULTS; i++) {
  228. if (!scans[i].set) {
  229. continue;
  230. }
  231. memcpy(buf + pos, scans + i, sizeof(struct ble_scan_result));
  232. pos++;
  233. if (pos >= len) {
  234. break;
  235. }
  236. }
  237. cyw43_thread_exit();
  238. return pos;
  239. }