BLE_TX.cpp 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172
  1. /*
  2. BLE TX driver
  3. Many thanks to Roel Schiphorst from BlueMark for his assistance with the Bluetooth code
  4. Also many thanks to chegewara for his sample code:
  5. https://github.com/Tinyu-Zhao/Arduino-Borads/blob/b584d00a81e4ac6d7b72697c674ca1bbcb8aae69/libraries/BLE/examples/BLE5_multi_advertising/BLE5_multi_advertising.ino
  6. */
  7. #include "BLE_TX.h"
  8. #include <esp_system.h>
  9. #include <BLEDevice.h>
  10. #include <BLEAdvertising.h>
  11. // set max power
  12. static const int8_t tx_power = ESP_PWR_LVL_P18;
  13. static esp_ble_gap_ext_adv_params_t ext_adv_params_1M = {
  14. .type = ESP_BLE_GAP_SET_EXT_ADV_PROP_CONNECTABLE,
  15. .interval_min = 0x30,
  16. .interval_max = 0x30,
  17. .channel_map = ADV_CHNL_ALL,
  18. .own_addr_type = BLE_ADDR_TYPE_RANDOM,
  19. .filter_policy = ADV_FILTER_ALLOW_SCAN_ANY_CON_ANY,
  20. .tx_power = tx_power,
  21. .primary_phy = ESP_BLE_GAP_PHY_CODED,
  22. .max_skip = 0,
  23. .secondary_phy = ESP_BLE_GAP_PHY_1M,
  24. .sid = 0,
  25. .scan_req_notif = false,
  26. };
  27. static esp_ble_gap_ext_adv_params_t ext_adv_params_2M = {
  28. .type = ESP_BLE_GAP_SET_EXT_ADV_PROP_SCANNABLE,
  29. .interval_min = 0x40,
  30. .interval_max = 0x40,
  31. .channel_map = ADV_CHNL_ALL,
  32. .own_addr_type = BLE_ADDR_TYPE_RANDOM,
  33. .filter_policy = ADV_FILTER_ALLOW_SCAN_ANY_CON_ANY,
  34. .tx_power = tx_power,
  35. .primary_phy = ESP_BLE_GAP_PHY_1M,
  36. .max_skip = 0,
  37. .secondary_phy = ESP_BLE_GAP_PHY_2M,
  38. .sid = 1,
  39. .scan_req_notif = false,
  40. };
  41. static esp_ble_gap_ext_adv_params_t legacy_adv_params = {
  42. .type = ESP_BLE_GAP_SET_EXT_ADV_PROP_LEGACY_IND,
  43. .interval_min = 0x45,
  44. .interval_max = 0x45,
  45. .channel_map = ADV_CHNL_ALL,
  46. .own_addr_type = BLE_ADDR_TYPE_RANDOM,
  47. .filter_policy = ADV_FILTER_ALLOW_SCAN_ANY_CON_ANY,
  48. .tx_power = tx_power,
  49. .primary_phy = ESP_BLE_GAP_PHY_1M,
  50. .max_skip = 0,
  51. .secondary_phy = ESP_BLE_GAP_PHY_1M,
  52. .sid = 2,
  53. .scan_req_notif = false,
  54. };
  55. static esp_ble_gap_ext_adv_params_t ext_adv_params_coded = {
  56. .type = ESP_BLE_GAP_SET_EXT_ADV_PROP_SCANNABLE,
  57. .interval_min = 0x50,
  58. .interval_max = 0x50,
  59. .channel_map = ADV_CHNL_ALL,
  60. .own_addr_type = BLE_ADDR_TYPE_RANDOM,
  61. .filter_policy = ADV_FILTER_ALLOW_SCAN_ANY_CON_ANY,
  62. .tx_power = tx_power,
  63. .primary_phy = ESP_BLE_GAP_PHY_1M,
  64. .max_skip = 0,
  65. .secondary_phy = ESP_BLE_GAP_PHY_CODED,
  66. .sid = 3,
  67. .scan_req_notif = false,
  68. };
  69. static esp_ble_gap_periodic_adv_params_t periodic_adv_params = {
  70. .interval_min = 0x320, // 1000 ms interval
  71. .interval_max = 0x640,
  72. .properties = 0, // Do not include TX power
  73. };
  74. static BLEMultiAdvertising advert(4);
  75. bool BLE_TX::init(void)
  76. {
  77. BLEDevice::init("");
  78. // generate random mac address
  79. uint8_t mac_addr[6];
  80. for (uint8_t i=0; i<6; i++) {
  81. mac_addr[i] = uint8_t(random(256));
  82. }
  83. // set as a bluetooth random static address
  84. mac_addr[0] |= 0xc0;
  85. advert.setAdvertisingParams(0, &ext_adv_params_1M);
  86. advert.setInstanceAddress(0, mac_addr);
  87. advert.setDuration(0);
  88. advert.setAdvertisingParams(1, &ext_adv_params_2M);
  89. advert.setInstanceAddress(1, mac_addr);
  90. advert.setDuration(1);
  91. advert.setAdvertisingParams(2, &legacy_adv_params);
  92. advert.setInstanceAddress(2, mac_addr);
  93. advert.setDuration(2);
  94. advert.setAdvertisingParams(3, &ext_adv_params_coded);
  95. advert.setDuration(3);
  96. advert.setInstanceAddress(3, mac_addr);
  97. advert.setPeriodicAdvertisingParams(0, &periodic_adv_params);
  98. advert.startPeriodicAdvertising(0);
  99. // prefer S8 coding
  100. if (esp_ble_gap_set_prefered_default_phy(ESP_BLE_GAP_PHY_OPTIONS_PREF_S8_CODING, ESP_BLE_GAP_PHY_OPTIONS_PREF_S8_CODING) != ESP_OK) {
  101. Serial.printf("Failed to setup S8 coding\n");
  102. }
  103. return true;
  104. }
  105. #define MIN(a,b) ((a)<(b)?(a):(b))
  106. bool BLE_TX::transmit(ODID_UAS_Data &UAS_data)
  107. {
  108. // create a packed UAS data message
  109. int length = odid_message_build_pack(&UAS_data, payload, 255);
  110. if (length <= 0) {
  111. return false;
  112. }
  113. // setup ASTM header
  114. const uint8_t header[] { uint8_t(length+5), 0x16, 0xfa, 0xff, 0x0d, uint8_t(msg_counter++) };
  115. // combine header with payload
  116. memcpy(payload2, header, sizeof(header));
  117. memcpy(&payload2[sizeof(header)], payload, length);
  118. int length2 = sizeof(header) + length;
  119. char legacy_name[28] {};
  120. const char *UAS_ID = (const char *)UAS_data.BasicID[0].UASID;
  121. const uint8_t ID_len = strlen(UAS_ID);
  122. const uint8_t ID_tail = MIN(4, ID_len);
  123. snprintf(legacy_name, sizeof(legacy_name), "DroneBeacon_%s", &UAS_ID[ID_len-ID_tail]);
  124. memset(legacy_payload, 0, sizeof(legacy_payload));
  125. const uint8_t legacy_header[] { 0x02, 0x01, 0x06, uint8_t(strlen(legacy_name)+1), ESP_BLE_AD_TYPE_NAME_SHORT};
  126. memcpy(legacy_payload, legacy_header, sizeof(legacy_header));
  127. memcpy(&legacy_payload[sizeof(legacy_header)], legacy_name, strlen(legacy_name));
  128. const uint8_t legacy_length = sizeof(legacy_header) + strlen(legacy_name);
  129. // setup advertising data
  130. advert.setAdvertisingData(0, length2, payload2);
  131. advert.setScanRspData(1, length2, payload2);
  132. advert.setScanRspData(2, legacy_length, legacy_payload);
  133. advert.setAdvertisingData(2, legacy_length, legacy_payload);
  134. advert.setScanRspData(3, length2, payload2);
  135. advert.setPeriodicAdvertisingData(0, length2, payload2);
  136. // we start advertising when we have the first lot of data to send
  137. if (!started) {
  138. advert.start();
  139. }
  140. started = true;
  141. return true;
  142. }