protocol_esp32_lws_scan.c 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333
  1. /*
  2. * Example ESP32 app code using Libwebsockets
  3. *
  4. * Copyright (C) 2017 Andy Green <andy@warmcat.com>
  5. *
  6. * This file is made available under the Creative Commons CC0 1.0
  7. * Universal Public Domain Dedication.
  8. *
  9. * The person who associated a work with this deed has dedicated
  10. * the work to the public domain by waiving all of his or her rights
  11. * to the work worldwide under copyright law, including all related
  12. * and neighboring rights, to the extent allowed by law. You can copy,
  13. * modify, distribute and perform the work, even for commercial purposes,
  14. * all without asking permission.
  15. *
  16. * The test apps are intended to be adapted for use in your code, which
  17. * may be proprietary. So unlike the library itself, they are licensed
  18. * Public Domain.
  19. *
  20. */
  21. #include <string.h>
  22. #include <nvs.h>
  23. typedef enum {
  24. SCAN_STATE_NONE,
  25. SCAN_STATE_INITIAL,
  26. SCAN_STATE_LIST,
  27. SCAN_STATE_FINAL
  28. } scan_state;
  29. struct store_json {
  30. const char *j;
  31. const char *nvs;
  32. };
  33. struct per_session_data__esplws_scan {
  34. struct per_session_data__esplws_scan *next;
  35. scan_state scan_state;
  36. char ap_record;
  37. unsigned char subsequent:1;
  38. unsigned char changed_partway:1;
  39. };
  40. struct per_vhost_data__esplws_scan {
  41. wifi_ap_record_t ap_records[20];
  42. TimerHandle_t timer;
  43. struct per_session_data__esplws_scan *live_pss_list;
  44. struct lws_context *context;
  45. struct lws_vhost *vhost;
  46. const struct lws_protocols *protocol;
  47. uint16_t count_ap_records;
  48. char count_live_pss;
  49. unsigned char scan_ongoing:1;
  50. unsigned char completed_any_scan:1;
  51. unsigned char reboot:1;
  52. };
  53. static const struct store_json store_json[] = {
  54. { "ssid\":\"", "ssid" },
  55. { ",\"pw\":\"", "password" },
  56. { ",\"serial\":\"", "serial" },
  57. { ",\"region\":\"", "region" },
  58. };
  59. static wifi_scan_config_t scan_config = {
  60. .ssid = 0,
  61. .bssid = 0,
  62. .channel = 0,
  63. .show_hidden = true
  64. };
  65. extern void (*lws_cb_scan_done)(void *);
  66. extern void *lws_cb_scan_done_arg;
  67. static void
  68. scan_finished(void *v);
  69. static int
  70. esplws_simple_arg(char *dest, int len, const char *in, const char *match)
  71. {
  72. const char *p = strstr(in, match);
  73. int n = 0;
  74. if (!p) {
  75. lwsl_err("No match %s\n", match);
  76. return 1;
  77. }
  78. p += strlen(match);
  79. while (*p && *p != '\"' && n < len - 1)
  80. dest[n++] = *p++;
  81. dest[n] = '\0';
  82. return 0;
  83. }
  84. static void
  85. scan_start(struct per_vhost_data__esplws_scan *vhd)
  86. {
  87. int n;
  88. if (vhd->reboot)
  89. esp_restart();
  90. if (vhd->scan_ongoing)
  91. return;
  92. vhd->scan_ongoing = 1;
  93. lws_cb_scan_done = scan_finished;
  94. lws_cb_scan_done_arg = vhd;
  95. n = esp_wifi_scan_start(&scan_config, false);
  96. if (n != ESP_OK)
  97. lwsl_err("scan start failed %d\n", n);
  98. }
  99. static void timer_cb(TimerHandle_t t)
  100. {
  101. struct per_vhost_data__esplws_scan *vhd = pvTimerGetTimerID(t);
  102. scan_start(vhd);
  103. }
  104. static void
  105. scan_finished(void *v)
  106. {
  107. struct per_vhost_data__esplws_scan *vhd = v;
  108. struct per_session_data__esplws_scan *p = vhd->live_pss_list;
  109. vhd->scan_ongoing = 0;
  110. vhd->count_ap_records = ARRAY_SIZE(vhd->ap_records);
  111. if (esp_wifi_scan_get_ap_records(&vhd->count_ap_records, vhd->ap_records) != ESP_OK) {
  112. lwsl_err("%s: failed\n", __func__);
  113. return;
  114. }
  115. while (p) {
  116. if (p->scan_state != SCAN_STATE_INITIAL && p->scan_state != SCAN_STATE_NONE)
  117. p->changed_partway = 1;
  118. else
  119. p->scan_state = SCAN_STATE_INITIAL;
  120. p = p->next;
  121. }
  122. lws_callback_on_writable_all_protocol(vhd->context, vhd->protocol);
  123. }
  124. static int
  125. callback_esplws_scan(struct lws *wsi, enum lws_callback_reasons reason,
  126. void *user, void *in, size_t len)
  127. {
  128. struct per_session_data__esplws_scan *pss =
  129. (struct per_session_data__esplws_scan *)user;
  130. struct per_vhost_data__esplws_scan *vhd =
  131. (struct per_vhost_data__esplws_scan *)
  132. lws_protocol_vh_priv_get(lws_get_vhost(wsi),
  133. lws_get_protocol(wsi));
  134. char buf[LWS_PRE + 384], /*ip[24],*/ *start = buf + LWS_PRE - 1, *p = start,
  135. *end = buf + sizeof(buf) - 1;
  136. wifi_ap_record_t *r;
  137. int n, m;
  138. switch (reason) {
  139. case LWS_CALLBACK_PROTOCOL_INIT:
  140. vhd = lws_protocol_vh_priv_zalloc(lws_get_vhost(wsi),
  141. lws_get_protocol(wsi),
  142. sizeof(struct per_vhost_data__esplws_scan));
  143. vhd->context = lws_get_context(wsi);
  144. vhd->protocol = lws_get_protocol(wsi);
  145. vhd->vhost = lws_get_vhost(wsi);
  146. vhd->timer = xTimerCreate("x", pdMS_TO_TICKS(10000), 1, vhd,
  147. (TimerCallbackFunction_t)timer_cb);
  148. xTimerStart(vhd->timer, 0);
  149. vhd->scan_ongoing = 0;
  150. scan_start(vhd);
  151. break;
  152. case LWS_CALLBACK_PROTOCOL_DESTROY:
  153. if (!vhd)
  154. break;
  155. xTimerStop(vhd->timer, 0);
  156. xTimerDelete(vhd->timer, 0);
  157. break;
  158. case LWS_CALLBACK_ESTABLISHED:
  159. vhd->count_live_pss++;
  160. pss->next = vhd->live_pss_list;
  161. vhd->live_pss_list = pss;
  162. /* if we have scan results, update them. Otherwise wait */
  163. if (vhd->count_ap_records) {
  164. pss->scan_state = SCAN_STATE_INITIAL;
  165. lws_callback_on_writable(wsi);
  166. }
  167. break;
  168. case LWS_CALLBACK_SERVER_WRITEABLE:
  169. switch (pss->scan_state) {
  170. case SCAN_STATE_INITIAL:
  171. n = LWS_WRITE_TEXT | LWS_WRITE_NO_FIN;;
  172. p += snprintf(p, end - p,
  173. "{ \"model\":\"%s\","
  174. " \"serial\":\"%s\","
  175. " \"host\":\"%s-%s\","
  176. " \"region\":\"%d\","
  177. " \"aps\":[",
  178. lws_esp32_model,
  179. lws_esp32_serial,
  180. lws_esp32_model, lws_esp32_serial,
  181. lws_esp32_region);
  182. pss->scan_state = SCAN_STATE_LIST;
  183. pss->ap_record = 0;
  184. pss->subsequent = 0;
  185. break;
  186. case SCAN_STATE_LIST:
  187. n = LWS_WRITE_CONTINUATION | LWS_WRITE_NO_FIN;
  188. if (pss->ap_record >= vhd->count_ap_records)
  189. goto scan_state_final;
  190. if (pss->subsequent)
  191. *p++ = ',';
  192. pss->subsequent = 1;
  193. r = &vhd->ap_records[(int)pss->ap_record++];
  194. p += snprintf(p, end - p,
  195. "{\"ssid\":\"%s\","
  196. "\"bssid\":\"%02X:%02X:%02X:%02X:%02X:%02X\","
  197. "\"rssi\":\"%d\","
  198. "\"chan\":\"%d\","
  199. "\"auth\":\"%d\"}",
  200. r->ssid,
  201. r->bssid[0], r->bssid[1], r->bssid[2],
  202. r->bssid[3], r->bssid[4], r->bssid[5],
  203. r->rssi, r->primary, r->authmode);
  204. if (pss->ap_record >= vhd->count_ap_records)
  205. pss->scan_state = SCAN_STATE_FINAL;
  206. break;
  207. case SCAN_STATE_FINAL:
  208. scan_state_final:
  209. n = LWS_WRITE_CONTINUATION;
  210. p += sprintf(p, "]}");
  211. if (pss->changed_partway) {
  212. pss->subsequent = 0;
  213. pss->scan_state = SCAN_STATE_INITIAL;
  214. } else
  215. pss->scan_state = SCAN_STATE_NONE;
  216. break;
  217. default:
  218. return 0;
  219. }
  220. m = lws_write(wsi, (unsigned char *)start, p - start, n);
  221. if (m < 0) {
  222. lwsl_err("ERROR %d writing to di socket\n", m);
  223. return -1;
  224. }
  225. if (pss->scan_state != SCAN_STATE_NONE)
  226. lws_callback_on_writable(wsi);
  227. break;
  228. case LWS_CALLBACK_RECEIVE:
  229. {
  230. nvs_handle nvh;
  231. char p[64];
  232. int n;
  233. if (strstr((const char *)in, "identify")) {
  234. lws_esp32_identify_physical_device();
  235. break;
  236. }
  237. if (nvs_open("lws-station", NVS_READWRITE, &nvh) != ESP_OK) {
  238. lwsl_err("Unable to open nvs\n");
  239. break;
  240. }
  241. for (n = 0; n < ARRAY_SIZE(store_json); n++) {
  242. if (esplws_simple_arg(p, sizeof(p), in, store_json[n].j))
  243. goto bail_nvs;
  244. if (nvs_set_str(nvh, store_json[n].nvs, p) != ESP_OK) {
  245. lwsl_err("Unable to store %s in nvm\n", store_json[n].nvs);
  246. goto bail_nvs;
  247. }
  248. }
  249. nvs_commit(nvh);
  250. nvs_close(nvh);
  251. vhd->reboot = 1;
  252. break;
  253. bail_nvs:
  254. nvs_close(nvh);
  255. return 1;
  256. }
  257. case LWS_CALLBACK_CLOSED:
  258. {
  259. struct per_session_data__esplws_scan **p = &vhd->live_pss_list;
  260. while (*p) {
  261. if ((*p) == pss) {
  262. *p = pss->next;
  263. continue;
  264. }
  265. p = &((*p)->next);
  266. }
  267. vhd->count_live_pss--;
  268. }
  269. break;
  270. default:
  271. break;
  272. }
  273. return 0;
  274. }
  275. #define LWS_PLUGIN_PROTOCOL_ESPLWS_SCAN \
  276. { \
  277. "esplws-scan", \
  278. callback_esplws_scan, \
  279. sizeof(struct per_session_data__esplws_scan), \
  280. 512, 0, NULL \
  281. }