esp_eddystone_api.c 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254
  1. /*
  2. This example code is in the Public Domain (or CC0 licensed, at your option.)
  3. Unless required by applicable law or agreed to in writing, this
  4. software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
  5. CONDITIONS OF ANY KIND, either express or implied.
  6. */
  7. /****************************************************************************
  8. *
  9. * This file is used to decode eddystone information.
  10. *
  11. ****************************************************************************/
  12. #include <stdio.h>
  13. #include <stdint.h>
  14. #include <string.h>
  15. #include <stdbool.h>
  16. #include "esp_err.h"
  17. #include "esp_gap_ble_api.h"
  18. #include "esp_eddystone_protocol.h"
  19. #include "esp_eddystone_api.h"
  20. /* Declare static functions */
  21. static esp_err_t esp_eddystone_uid_received(const uint8_t* buf, uint8_t len, esp_eddystone_result_t* res);
  22. static esp_err_t esp_eddystone_url_received(const uint8_t* buf, uint8_t len, esp_eddystone_result_t* res);
  23. static char* esp_eddystone_resolve_url_scheme(const uint8_t* url_start, const uint8_t* url_end);
  24. static esp_err_t esp_eddystone_tlm_received(const uint8_t* buf, uint8_t len, esp_eddystone_result_t* res);
  25. static esp_err_t esp_eddystone_get_inform(const uint8_t* buf, uint8_t len, esp_eddystone_result_t* res);
  26. /* Eddystone-URL scheme prefixes */
  27. static const char* eddystone_url_prefix[4] = {
  28. "http://www.",
  29. "https://www.",
  30. "http://",
  31. "https://"
  32. };
  33. /* Eddystone-URL HTTP URL encoding */
  34. static const char* eddystone_url_encoding[14] = {
  35. ".com/",
  36. ".org/",
  37. ".edu/",
  38. ".net/",
  39. ".info/",
  40. ".biz/",
  41. ".gov/",
  42. ".com",
  43. ".org",
  44. ".edu",
  45. ".net",
  46. ".info",
  47. ".biz",
  48. ".gov"
  49. };
  50. /****************** Eddystone-UID **************
  51. Byte offset Field Description
  52. 0 Frame Type Value = 0x00
  53. 1 Ranging Data Calibrated Tx power at 0 m
  54. 2 NID[0] 10-byte Namespace
  55. 3 NID[1]
  56. 4 NID[2]
  57. 5 NID[3]
  58. 6 NID[4]
  59. 7 NID[5]
  60. 8 NID[6]
  61. 9 NID[7]
  62. 10 NID[8]
  63. 11 NID[9]
  64. 12 BID[0] 6-byte Instance
  65. 13 BID[1]
  66. 14 BID[2]
  67. 15 BID[3]
  68. 16 BID[4]
  69. 17 BID[5]
  70. 18 RFU Reserved for future use, must be0x00
  71. 19 RFU Reserved for future use, must be0x00
  72. *********************************************/
  73. /* decode and store received UID */
  74. static esp_err_t esp_eddystone_uid_received(const uint8_t* buf, uint8_t len, esp_eddystone_result_t* res)
  75. {
  76. uint8_t pos = 0;
  77. //1-byte Ranging Data + 10-byte Namespace + 6-byte Instance
  78. if((len != EDDYSTONE_UID_DATA_LEN) && (len != (EDDYSTONE_UID_RFU_LEN+EDDYSTONE_UID_DATA_LEN))) {
  79. //ERROR:uid len wrong
  80. return -1;
  81. }
  82. res->inform.uid.ranging_data = buf[pos++];
  83. for(int i=0; i<EDDYSTONE_UID_NAMESPACE_LEN; i++) {
  84. res->inform.uid.namespace_id[i] = buf[pos++];
  85. }
  86. for(int i=0; i<EDDYSTONE_UID_INSTANCE_LEN; i++) {
  87. res->inform.uid.instance_id[i] = buf[pos++];
  88. }
  89. return 0;
  90. }
  91. /* resolve received URL to url_res pointer */
  92. static char* esp_eddystone_resolve_url_scheme(const uint8_t *url_start, const uint8_t *url_end)
  93. {
  94. int pos = 0;
  95. static char url_buf[100] = {0};
  96. const uint8_t *p = url_start;
  97. pos += sprintf(&url_buf[pos], "%s", eddystone_url_prefix[*p++]);
  98. for (; p <= url_end; p++) {
  99. if (esp_eddystone_is_char_invalid((*p))) {
  100. pos += sprintf(&url_buf[pos], "%s", eddystone_url_encoding[*p]);
  101. } else {
  102. pos += sprintf(&url_buf[pos], "%c", *p);
  103. }
  104. }
  105. return url_buf;
  106. }
  107. /************************** Eddystone-URL *************
  108. Frame Specification
  109. Byte offset Field Description
  110. 0 Frame Type Value = 0x10
  111. 1 TX Power Calibrated Tx power at 0 m
  112. 2 URL Scheme Encoded Scheme Prefix
  113. 3+ Encoded URL Length 1-17
  114. *******************************************************/
  115. /* decode and store received URL, the pointer url_res points to the resolved url */
  116. static esp_err_t esp_eddystone_url_received(const uint8_t* buf, uint8_t len, esp_eddystone_result_t* res)
  117. {
  118. char *url_res = NULL;
  119. uint8_t pos = 0;
  120. if(len-EDDYSTONE_URL_TX_POWER_LEN > EDDYSTONE_URL_MAX_LEN) {
  121. //ERROR:too long url
  122. return -1;
  123. }
  124. res->inform.url.tx_power = buf[pos++];
  125. url_res = esp_eddystone_resolve_url_scheme(buf+pos, buf+len-1);
  126. memcpy(&res->inform.url.url, url_res, strlen(url_res));
  127. res->inform.url.url[strlen(url_res)] = '\0';
  128. return 0;
  129. }
  130. /****************** eddystone-tlm ***************
  131. * Unencrypted TLM Frame Specification
  132. Byte offset Field Description
  133. 0 Frame Type Value = 0x20
  134. 1 Version TLM version, value = 0x00
  135. 2 VBATT[0] Battery voltage, 1 mV/bit
  136. 3 VBATT[1]
  137. 4 TEMP[0] Beacon temperature
  138. 5 TEMP[1]
  139. 6 ADV_CNT[0] Advertising PDU count
  140. 7 ADV_CNT[1]
  141. 8 ADV_CNT[2]
  142. 9 ADV_CNT[3]
  143. 10 SEC_CNT[0] Time since power-on or reboot
  144. 11 SEC_CNT[1]
  145. 12 SEC_CNT[2]
  146. 13 SEC_CNT[3]
  147. ************************************************/
  148. /* decode and store received TLM */
  149. static esp_err_t esp_eddystone_tlm_received(const uint8_t* buf, uint8_t len, esp_eddystone_result_t* res)
  150. {
  151. uint8_t pos = 0;
  152. if(len > EDDYSTONE_TLM_DATA_LEN) {
  153. //ERROR:TLM too long
  154. return -1;
  155. }
  156. res->inform.tlm.version = buf[pos++];
  157. res->inform.tlm.battery_voltage = big_endian_read_16(buf, pos);
  158. pos += 2;
  159. uint16_t temp = big_endian_read_16(buf, pos);
  160. int8_t temp_integral = (int8_t)((temp >> 8) & 0xff);
  161. float temp_decimal = (temp & 0xff) / 256.0;
  162. res->inform.tlm.temperature = temp_integral + temp_decimal;
  163. pos += 2;
  164. res->inform.tlm.adv_count = big_endian_read_32(buf, pos);
  165. pos += 4;
  166. res->inform.tlm.time = big_endian_read_32(buf, pos);
  167. return 0;
  168. }
  169. static esp_err_t esp_eddystone_get_inform(const uint8_t* buf, uint8_t len, esp_eddystone_result_t* res)
  170. {
  171. static esp_err_t ret=-1;
  172. switch(res->common.frame_type)
  173. {
  174. case EDDYSTONE_FRAME_TYPE_UID: {
  175. ret = esp_eddystone_uid_received(buf, len, res);
  176. break;
  177. }
  178. case EDDYSTONE_FRAME_TYPE_URL: {
  179. ret = esp_eddystone_url_received(buf, len, res);
  180. break;
  181. }
  182. case EDDYSTONE_FRAME_TYPE_TLM: {
  183. ret = esp_eddystone_tlm_received(buf, len, res);
  184. break;
  185. }
  186. default:
  187. break;
  188. }
  189. return ret;
  190. }
  191. esp_err_t esp_eddystone_decode(const uint8_t* buf, uint8_t len, esp_eddystone_result_t* res)
  192. {
  193. if (len == 0 || buf == NULL || res == NULL) {
  194. return -1;
  195. }
  196. uint8_t pos=0;
  197. while(res->common.srv_data_type != EDDYSTONE_SERVICE_UUID)
  198. {
  199. pos++;
  200. if(pos >= len ) {
  201. return -1;
  202. }
  203. uint8_t ad_type = buf[pos++];
  204. switch(ad_type)
  205. {
  206. case ESP_BLE_AD_TYPE_FLAG: {
  207. res->common.flags = buf[pos++];
  208. break;
  209. }
  210. case ESP_BLE_AD_TYPE_16SRV_CMPL: {
  211. uint16_t uuid = little_endian_read_16(buf, pos);
  212. if(uuid != EDDYSTONE_SERVICE_UUID) {
  213. return -1;
  214. }
  215. res->common.srv_uuid = uuid;
  216. pos += 2;
  217. break;
  218. }
  219. case ESP_BLE_AD_TYPE_SERVICE_DATA: {
  220. uint16_t type = little_endian_read_16(buf, pos);
  221. pos += 2;
  222. uint8_t frame_type = buf[pos++];
  223. if(type != EDDYSTONE_SERVICE_UUID || !(frame_type == EDDYSTONE_FRAME_TYPE_UID || frame_type == EDDYSTONE_FRAME_TYPE_URL ||
  224. frame_type == EDDYSTONE_FRAME_TYPE_TLM)) {
  225. return -1;
  226. }
  227. res->common.srv_data_type = type;
  228. res->common.frame_type = frame_type;
  229. break;
  230. }
  231. default:
  232. break;
  233. }
  234. }
  235. return esp_eddystone_get_inform(buf+pos, len-pos, res);
  236. }