2 * ESP32 Scan / Factory protocol handler
4 * Copyright (C) 2017 Andy Green <andy@warmcat.com>
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation:
9 * version 2.1 of the License.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
24 #include <esp_ota_ops.h>
29 SCAN_STATE_INITIAL_MANIFEST,
40 struct per_session_data__esplws_scan {
41 struct per_session_data__esplws_scan *next;
42 scan_state scan_state;
43 struct timeval last_send;
47 char result[LWS_PRE + 512];
48 unsigned char buffer[4096];
55 unsigned char subsequent:1;
56 unsigned char changed_partway:1;
59 struct per_vhost_data__esplws_scan {
60 wifi_ap_record_t ap_records[10];
61 TimerHandle_t timer, reboot_timer;
62 struct per_session_data__esplws_scan *live_pss_list;
63 struct lws_context *context;
64 struct lws_vhost *vhost;
65 const struct lws_protocols *protocol;
67 const esp_partition_t *part;
68 esp_ota_handle_t otahandle;
76 uint16_t count_ap_records;
78 unsigned char scan_ongoing:1;
79 unsigned char completed_any_scan:1;
80 unsigned char reboot:1;
81 unsigned char changed_settings:1;
82 unsigned char checked_updates:1;
83 unsigned char autonomous_update:1;
84 unsigned char autonomous_update_sampled:1;
87 static const struct store_json store_json[] = {
88 { "\"ssid0\":\"", "0ssid" },
89 { ",\"pw0\":\"", "0password" },
90 { "\"ssid1\":\"", "1ssid" },
91 { ",\"pw1\":\"", "1password" },
92 { "\"ssid2\":\"", "2ssid" },
93 { ",\"pw2\":\"", "2password" },
94 { "\"ssid3\":\"", "3ssid" },
95 { ",\"pw3\":\"", "3password" },
96 { ",\"access_pw\":\"", "access_pw" },
97 { "{\"group\":\"", "group" },
98 { "{\"role\":\"", "role" },
99 { ",\"region\":\"", "region" },
102 static wifi_scan_config_t scan_config = {
109 const esp_partition_t *
110 ota_choose_part(void);
112 static const char * const param_names[] = {
120 enum enum_param_names {
130 scan_finished(uint16_t count, wifi_ap_record_t *recs, void *v);
133 esplws_simple_arg(char *dest, int len, const char *in, const char *match)
135 const char *p = strstr(in, match);
142 while (*p && *p != '\"' && n < len - 1)
150 scan_start(struct per_vhost_data__esplws_scan *vhd)
157 if (vhd->scan_ongoing)
160 vhd->scan_ongoing = 1;
161 lws_esp32.scan_consumer = scan_finished;
162 lws_esp32.scan_consumer_arg = vhd;
163 n = esp_wifi_scan_start(&scan_config, false);
165 lwsl_err("scan start failed %d\n", n);
168 static void timer_cb(TimerHandle_t t)
170 struct per_vhost_data__esplws_scan *vhd = pvTimerGetTimerID(t);
175 static void reboot_timer_cb(TimerHandle_t t)
181 client_connection(struct per_vhost_data__esplws_scan *vhd, const char *file)
183 #if CONFIG_LWS_IS_FACTORY_APPLICATION == 'y' && defined(CONFIG_LWS_OTA_SERVER_BASE_URL) && \
184 defined(CONFIG_LWS_OTA_SERVER_FQDN)
185 static struct lws_client_connect_info i;
188 memset(&i, 0, sizeof i);
190 snprintf(path, sizeof(path) - 1, CONFIG_LWS_OTA_SERVER_BASE_URL "/" CONFIG_LWS_MODEL_NAME "/%s", file);
192 lwsl_notice("Fetching %s\n", path);
195 i.context = vhd->context;
196 i.address = CONFIG_LWS_OTA_SERVER_FQDN;
197 i.ssl_connection = 1;
200 i.vhost = vhd->vhost;
203 i.protocol = "esplws-scan";
206 vhd->cwsi = lws_client_connect_via_info(&i);
208 lwsl_notice("NULL return\n");
212 return 0; /* ongoing */
216 scan_finished(uint16_t count, wifi_ap_record_t *recs, void *v)
218 struct per_vhost_data__esplws_scan *vhd = v;
219 struct per_session_data__esplws_scan *p = vhd->live_pss_list;
221 lwsl_notice("%s: count %d\n", __func__, count);
223 vhd->scan_ongoing = 0;
225 if (count < ARRAY_SIZE(vhd->ap_records))
226 vhd->count_ap_records = count;
228 vhd->count_ap_records = ARRAY_SIZE(vhd->ap_records);
230 memcpy(vhd->ap_records, recs, vhd->count_ap_records * sizeof(*recs));
233 if (p->scan_state != SCAN_STATE_INITIAL && p->scan_state != SCAN_STATE_NONE)
234 p->changed_partway = 1;
236 p->scan_state = SCAN_STATE_INITIAL;
240 lws_callback_on_writable_all_protocol(vhd->context, vhd->protocol);
242 if (lws_esp32.inet && !vhd->cwsi && !vhd->checked_updates)
243 client_connection(vhd, "manifest.json");
245 if (vhd->changed_settings) {
246 lws_esp32_wlan_nvs_get(1);
247 vhd->changed_settings = 0;
252 static const char *ssl_names[] = { "ssl-pub.pem", "ssl-pri.pem" };
255 file_upload_cb(void *data, const char *name, const char *filename,
256 char *buf, int len, enum lws_spa_fileupload_states state)
258 struct per_session_data__esplws_scan *pss =
259 (struct per_session_data__esplws_scan *)data;
264 if (lws_esp32_get_reboot_type() != LWS_MAGIC_REBOOT_TYPE_FORCED_FACTORY_BUTTON)
267 lwsl_notice("LWS_UFS_OPEN Filename %s\n", filename);
268 strncpy(pss->filename, filename, sizeof(pss->filename) - 1);
269 if (!strcmp(name, "pub") || !strcmp(name, "pri")) {
270 if (nvs_open("lws-station", NVS_READWRITE, &pss->nvh))
274 pss->file_length = 0;
277 case LWS_UFS_FINAL_CONTENT:
278 case LWS_UFS_CONTENT:
280 /* if the file length is too big, drop it */
281 if (pss->file_length + len > sizeof(pss->buffer))
284 memcpy(pss->buffer + pss->file_length, buf, len);
286 pss->file_length += len;
288 if (state == LWS_UFS_CONTENT)
291 lwsl_notice("LWS_UFS_FINAL_CONTENT\n");
293 if (!strcmp(name, "pri"))
295 n = nvs_set_blob(pss->nvh, ssl_names[n], pss->buffer, pss->file_length);
297 nvs_commit(pss->nvh);
308 callback_esplws_scan(struct lws *wsi, enum lws_callback_reasons reason,
309 void *user, void *in, size_t len)
311 struct per_session_data__esplws_scan *pss =
312 (struct per_session_data__esplws_scan *)user;
313 struct per_vhost_data__esplws_scan *vhd =
314 (struct per_vhost_data__esplws_scan *)
315 lws_protocol_vh_priv_get(lws_get_vhost(wsi),
316 lws_get_protocol(wsi));
317 unsigned char *start = pss->buffer + LWS_PRE - 1, *p = start,
318 *end = pss->buffer + sizeof(pss->buffer) - 1;
327 case LWS_CALLBACK_PROTOCOL_INIT:
328 vhd = lws_protocol_vh_priv_zalloc(lws_get_vhost(wsi),
329 lws_get_protocol(wsi),
330 sizeof(struct per_vhost_data__esplws_scan));
331 vhd->context = lws_get_context(wsi);
332 vhd->protocol = lws_get_protocol(wsi);
333 vhd->vhost = lws_get_vhost(wsi);
334 vhd->timer = xTimerCreate("x", pdMS_TO_TICKS(10000), 1, vhd,
335 (TimerCallbackFunction_t)timer_cb);
336 vhd->scan_ongoing = 0;
337 strcpy(vhd->json, " { }");
341 case LWS_CALLBACK_PROTOCOL_DESTROY:
344 xTimerStop(vhd->timer, 0);
345 xTimerDelete(vhd->timer, 0);
348 case LWS_CALLBACK_ESTABLISHED:
349 lwsl_notice("%s: ESTABLISHED\n", __func__);
350 if (!vhd->live_pss_list) {
352 xTimerStart(vhd->timer, 0);
354 vhd->count_live_pss++;
355 pss->next = vhd->live_pss_list;
356 vhd->live_pss_list = pss;
357 /* if we have scan results, update them. Otherwise wait */
358 if (vhd->count_ap_records) {
359 pss->scan_state = SCAN_STATE_INITIAL;
360 lws_callback_on_writable(wsi);
364 case LWS_CALLBACK_SERVER_WRITEABLE:
366 if (vhd->autonomous_update_sampled) {
367 p += snprintf((char *)p, end - p,
368 " {\n \"auton\":\"1\",\n \"pos\": \"%ld\",\n"
369 " \"len\":\"%ld\"\n}\n",
371 vhd->content_length);
377 switch (pss->scan_state) {
380 struct lws_esp32_image i;
381 char img_factory[512], img_ota[512], group[16], role[16];
384 case SCAN_STATE_INITIAL:
386 gettimeofday(&t, NULL);
387 if (t.tv_sec - pss->last_send.tv_sec < 10)
392 if (nvs_open("lws-station", NVS_READWRITE, &nvh)) {
393 lwsl_err("unable to open nvs\n");
397 if (nvs_get_blob(nvh, "ssl-pub.pem", NULL, &s) == ESP_OK)
399 if (nvs_get_blob(nvh, "ssl-pri.pem", NULL, &s) == ESP_OK)
401 s = sizeof(group) - 1;
404 nvs_get_str(nvh, "group", group, &s);
405 nvs_get_str(nvh, "role", role, &s);
410 * this value in the JSON is just
411 * used for UI indication. Each conditional feature confirms
412 * it itself before it allows itself to be used.
415 grt = lws_esp32_get_reboot_type();
417 esp_efuse_mac_get_default(mac);
418 strcpy(img_factory, " { \"date\": \"Empty\" }");
419 strcpy(img_ota, " { \"date\": \"Empty\" }");
421 lws_esp32_get_image_info(esp_partition_find_first(ESP_PARTITION_TYPE_APP,
422 ESP_PARTITION_SUBTYPE_APP_FACTORY, NULL), &i,
423 img_factory, sizeof(img_factory));
424 lws_esp32_get_image_info(esp_partition_find_first(ESP_PARTITION_TYPE_APP,
425 ESP_PARTITION_SUBTYPE_APP_OTA_0, NULL), &i,
426 img_ota, sizeof(img_ota));
428 p += snprintf((char *)p, end - p,
429 "{ \"model\":\"%s\",\n"
430 " \"forced_button\":\"%d\",\n"
431 " \"serial\":\"%s\",\n"
432 " \"opts\":\"%s\",\n"
433 " \"host\":\"%s-%s\",\n"
434 " \"region\":\"%d\",\n"
435 " \"ssl_pub\":\"%d\",\n"
436 " \"ssl_pri\":\"%d\",\n"
437 " \"mac\":\"%02X%02X%02X%02X%02X%02X\",\n"
438 " \"ssid\":\"%s\",\n"
439 " \"conn_ip\":\"%s\",\n"
440 " \"conn_mask\":\"%s\",\n"
441 " \"conn_gw\":\"%s\",\n"
442 " \"group\":\"%s\",\n"
443 " \"role\":\"%s\",\n"
444 " \"img_factory\": %s,\n"
445 " \"img_ota\": %s,\n",
447 grt == LWS_MAGIC_REBOOT_TYPE_FORCED_FACTORY_BUTTON,
450 lws_esp32.model, lws_esp32.serial,
453 mac[0], mac[1], mac[2], mac[3], mac[4], mac[5] | 1,
454 lws_esp32.active_ssid,
463 n = LWS_WRITE_TEXT | LWS_WRITE_NO_FIN;
464 pss->scan_state = SCAN_STATE_INITIAL_MANIFEST;
469 case SCAN_STATE_INITIAL_MANIFEST:
470 p += snprintf((char *)p, end - p,
472 " \"inet\":\"%d\",\n",
477 p += snprintf((char *)p, end - p,
480 n = LWS_WRITE_CONTINUATION | LWS_WRITE_NO_FIN;
481 pss->scan_state = SCAN_STATE_KNOWN;
484 case SCAN_STATE_KNOWN:
485 if (nvs_open("lws-station", NVS_READONLY, &nvh)) {
486 lwsl_notice("unable to open nvh\n");
490 for (m = 0; m < 4; m++) {
491 char name[10], ssid[32];
492 unsigned int pp = 0, use = 0;
497 s = sizeof(ssid) - 1;
499 lws_snprintf(name, sizeof(name) - 1, "%dssid", m);
500 nvs_get_str(nvh, name, ssid, &s);
501 lws_snprintf(name, sizeof(name) - 1, "%dpassword", m);
503 nvs_get_str(nvh, name, NULL, &s);
505 lws_snprintf(name, sizeof(name) - 1, "%duse", m);
506 nvs_get_u32(nvh, name, &use);
508 p += snprintf((char *)p, end - p,
509 "{\"ssid\":\"%s\",\n"
516 p += snprintf((char *)p, end - p,
519 n = LWS_WRITE_CONTINUATION | LWS_WRITE_NO_FIN;
520 pss->scan_state = SCAN_STATE_LIST;
523 case SCAN_STATE_LIST:
524 for (m = 0; m < 6; m++) {
525 n = LWS_WRITE_CONTINUATION | LWS_WRITE_NO_FIN;
526 if (pss->ap_record >= vhd->count_ap_records)
527 goto scan_state_final;
533 r = &vhd->ap_records[(int)pss->ap_record++];
534 p += snprintf((char *)p, end - p,
535 "{\"ssid\":\"%s\",\n"
536 "\"bssid\":\"%02X:%02X:%02X:%02X:%02X:%02X\",\n"
539 "\"auth\":\"%d\"}\n",
541 r->bssid[0], r->bssid[1], r->bssid[2],
542 r->bssid[3], r->bssid[4], r->bssid[5],
543 r->rssi, r->primary, r->authmode);
544 if (pss->ap_record >= vhd->count_ap_records)
545 pss->scan_state = SCAN_STATE_FINAL;
549 case SCAN_STATE_FINAL:
551 n = LWS_WRITE_CONTINUATION;
552 p += sprintf((char *)p, "]\n}\n");
553 if (pss->changed_partway) {
555 pss->scan_state = SCAN_STATE_INITIAL;
557 pss->scan_state = SCAN_STATE_NONE;
558 vhd->autonomous_update_sampled = vhd->autonomous_update;
565 // lwsl_notice("issue: %d (%d)\n", p - start, n);
566 m = lws_write(wsi, (unsigned char *)start, p - start, n);
568 lwsl_err("ERROR %d writing to di socket\n", m);
572 if (pss->scan_state != SCAN_STATE_NONE)
573 lws_callback_on_writable(wsi);
577 case LWS_CALLBACK_RECEIVE:
579 const char *sect = "\"app\": {", *b;
584 if (strstr((const char *)in, "identify")) {
585 lws_esp32_identify_physical_device();
589 if (vhd->json_len && strstr((const char *)in, "update-factory")) {
590 sect = "\"factory\": {";
593 if (vhd->json_len && strstr((const char *)in, "update-ota"))
596 if (strstr((const char *)in, "\"reset\""))
599 if (nvs_open("lws-station", NVS_READWRITE, &nvh) != ESP_OK) {
600 lwsl_err("Unable to open nvs\n");
604 if (!esplws_simple_arg(p, sizeof(p), in, ",\"slot\":\""))
607 lwsl_notice("si %d\n", si);
609 for (n = 0; n < ARRAY_SIZE(store_json); n++) {
610 if (esplws_simple_arg(p, sizeof(p), in, store_json[n].j))
613 /* only change access password if he has physical access to device */
614 if (n == 8 && lws_esp32_get_reboot_type() != LWS_MAGIC_REBOOT_TYPE_FORCED_FACTORY_BUTTON)
617 //lwsl_notice("%s: %s '%s'\n", __func__, store_json[n].nvs, p);
619 strncpy(lws_esp32.group, p, sizeof(lws_esp32.group) - 1);
620 lws_esp32.group[sizeof(lws_esp32.group) - 1] = '\0';
623 strncpy(lws_esp32.role, p, sizeof(lws_esp32.role) - 1);
624 lws_esp32.role[sizeof(lws_esp32.role) - 1] = '\0';
627 if (lws_nvs_set_str(nvh, store_json[n].nvs, p) != ESP_OK) {
628 lwsl_err("Unable to store %s in nvm\n", store_json[n].nvs);
632 if (si != -1 && n < 8) {
634 strncpy(lws_esp32.ssid[(n >> 1) & 3], p,
635 sizeof(lws_esp32.ssid[0]));
636 lws_esp32.ssid[(n >> 1) & 3]
637 [sizeof(lws_esp32.ssid[0]) - 1] = '\0';
638 lws_snprintf(use, sizeof(use) - 1, "%duse", si);
639 lwsl_notice("resetting %s to 0\n", use);
640 nvs_set_u32(nvh, use, 0);
643 strncpy(lws_esp32.password[(n >> 1) & 3], p,
644 sizeof(lws_esp32.password[0]));
645 lws_esp32.password[(n >> 1) & 3]
646 [sizeof(lws_esp32.password[0]) - 1] = '\0';
655 if (strstr((const char *)in, "\"factory-reset\"")) {
656 if (lws_esp32_get_reboot_type() ==
657 LWS_MAGIC_REBOOT_TYPE_FORCED_FACTORY_BUTTON) {
659 lwsl_notice("Doing factory reset\n");
660 ESP_ERROR_CHECK(nvs_open("lws-station", NVS_READWRITE, &nvh));
661 n = nvs_erase_all(nvh);
663 lwsl_notice("erase_all failed %d\n", n);
669 lwsl_notice("failed on factory button boot\n");
672 if (vhd->scan_ongoing)
673 vhd->changed_settings = 1;
675 lws_esp32_wlan_nvs_get(1);
677 lwsl_notice("set Join AP info\n");
686 vhd->reboot_timer = xTimerCreate("x", pdMS_TO_TICKS(250), 0, vhd,
687 (TimerCallbackFunction_t)reboot_timer_cb);
688 xTimerStart(vhd->reboot_timer, 0);
693 lwsl_notice("Autonomous upload\n");
694 b = strstr(vhd->json, sect);
696 lwsl_notice("Can't find %s in JSON\n", sect);
699 b = strstr(b, "\"file\": \"");
701 lwsl_notice("Can't find \"file\": JSON\n");
704 vhd->autonomous_update = 1;
705 if (pss->scan_state == SCAN_STATE_NONE)
706 vhd->autonomous_update_sampled = 1;
709 while ((*b != '\"') && n < sizeof(p) - 1)
714 vhd->part = ota_choose_part();
718 if (client_connection(vhd, p))
719 vhd->autonomous_update = 0;
724 case LWS_CALLBACK_CLOSED:
726 struct per_session_data__esplws_scan **p = &vhd->live_pss_list;
737 vhd->count_live_pss--;
739 if (!vhd->live_pss_list)
740 xTimerStop(vhd->timer, 0);
743 /* "factory" POST handling */
745 case LWS_CALLBACK_HTTP_BODY:
746 /* create the POST argument parser if not already existing */
747 lwsl_notice("LWS_CALLBACK_HTTP_BODY (scan)\n");
749 pss->spa = lws_spa_create(wsi, param_names,
750 ARRAY_SIZE(param_names), 1024,
751 file_upload_cb, pss);
755 pss->filename[0] = '\0';
756 pss->file_length = 0;
759 /* let it parse the POST data */
760 if (lws_spa_process(pss->spa, in, len))
764 case LWS_CALLBACK_HTTP_BODY_COMPLETION:
765 lwsl_notice("LWS_CALLBACK_HTTP_BODY_COMPLETION (scan)\n");
766 /* call to inform no more payload data coming */
767 lws_spa_finalize(pss->spa);
769 if (nvs_open("lws-station", NVS_READWRITE, &nvh) != ESP_OK) {
770 lwsl_err("Unable to open nvs\n");
774 if (lws_esp32_get_reboot_type() == LWS_MAGIC_REBOOT_TYPE_FORCED_FACTORY_BUTTON) {
776 if (lws_spa_get_string(pss->spa, EPN_SERIAL)) {
777 if (lws_nvs_set_str(nvh, "serial", lws_spa_get_string(pss->spa, EPN_SERIAL)) != ESP_OK) {
778 lwsl_err("Unable to store serial in nvm\n");
785 if (lws_spa_get_string(pss->spa, EPN_OPTS)) {
786 if (lws_nvs_set_str(nvh, "opts", lws_spa_get_string(pss->spa, EPN_OPTS)) != ESP_OK) {
787 lwsl_err("Unable to store options in nvm\n");
796 pss->result_len = snprintf(pss->result + LWS_PRE, sizeof(pss->result) - LWS_PRE - 1,
797 "<html>Rebooting after storing certs...<br>connect to AP '<b>config-%s-%s</b>' and continue here: "
798 "<a href=\"https://192.168.4.1\">https://192.168.4.1</a></html>",
799 lws_esp32.model, lws_spa_get_string(pss->spa, EPN_SERIAL));
801 if (lws_add_http_header_status(wsi, HTTP_STATUS_OK, &p, end))
804 if (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_CONTENT_TYPE,
805 (unsigned char *)"text/html", 9, &p, end))
807 if (lws_add_http_header_content_length(wsi, pss->result_len, &p, end))
809 if (lws_finalize_http_header(wsi, &p, end))
812 n = lws_write(wsi, start, p - start, LWS_WRITE_HTTP_HEADERS);
816 lws_callback_on_writable(wsi);
819 case LWS_CALLBACK_HTTP_WRITEABLE:
820 lwsl_debug("LWS_CALLBACK_HTTP_WRITEABLE: sending %d\n",
822 n = lws_write(wsi, (unsigned char *)pss->result + LWS_PRE,
823 pss->result_len, LWS_WRITE_HTTP);
827 vhd->reboot_timer = xTimerCreate("x", pdMS_TO_TICKS(3000), 0, vhd,
828 (TimerCallbackFunction_t)reboot_timer_cb);
829 xTimerStart(vhd->reboot_timer, 0);
831 return 1; // hang up since we will reset
833 /* ----- client handling ----- */
835 case LWS_CALLBACK_CLIENT_CONNECTION_ERROR:
836 lwsl_notice("Client connection error %s\n", (char *)in);
840 case LWS_CALLBACK_ESTABLISHED_CLIENT_HTTP:
841 if (!vhd->autonomous_update)
847 if (lws_hdr_copy(wsi, pp, sizeof(pp) - 1, WSI_TOKEN_HTTP_CONTENT_LENGTH) < 0)
850 vhd->content_length = atoi(pp);
851 if (vhd->content_length <= 0 ||
852 vhd->content_length > vhd->part->size)
855 if (esp_ota_begin(vhd->part, (long)-1, &vhd->otahandle) != ESP_OK) {
856 lwsl_err("OTA: Failed to begin\n");
860 vhd->file_length = 0;
865 case LWS_CALLBACK_RECEIVE_CLIENT_HTTP_READ:
866 //lwsl_notice("LWS_CALLBACK_RECEIVE_CLIENT_HTTP_READ: %ld\n",
869 if (!vhd->autonomous_update) {
870 if (sizeof(vhd->json) - vhd->json_len - 1 < len)
871 len = sizeof(vhd->json) - vhd->json_len - 1;
872 memcpy(vhd->json + vhd->json_len, in, len);
873 vhd->json_len += len;
874 vhd->json[vhd->json_len] = '\0';
878 /* autonomous download */
881 if (vhd->file_length + len > vhd->part->size) {
882 lwsl_err("OTA: incoming file too large\n");
886 lwsl_debug("writing 0x%lx... 0x%lx\n",
887 vhd->part->address + vhd->file_length,
888 vhd->part->address + vhd->file_length + len);
889 if (esp_ota_write(vhd->otahandle, in, len) != ESP_OK) {
890 lwsl_err("OTA: Failed to write\n");
893 vhd->file_length += len;
895 lws_callback_on_writable_all_protocol(vhd->context, vhd->protocol);
899 esp_ota_end(vhd->otahandle);
901 vhd->autonomous_update = 0;
905 case LWS_CALLBACK_RECEIVE_CLIENT_HTTP:
907 char *px = (char *)pss->buffer + LWS_PRE;
908 int lenx = sizeof(pss->buffer) - LWS_PRE - 1;
910 //lwsl_notice("LWS_CALLBACK_RECEIVE_CLIENT_HTTP: %d\n", len);
912 if (lws_http_client_read(wsi, &px, &lenx) < 0)
917 case LWS_CALLBACK_COMPLETED_CLIENT_HTTP:
918 lwsl_notice("LWS_CALLBACK_COMPLETED_CLIENT_HTTP\n");
920 if (!vhd->autonomous_update) {
922 vhd->checked_updates = 1;
927 /* autonomous download */
929 lwsl_notice("auton complete\n");
931 if (esp_ota_end(vhd->otahandle) != ESP_OK) {
932 lwsl_err("OTA: end failed\n");
936 if (esp_ota_set_boot_partition(vhd->part) != ESP_OK) {
937 lwsl_err("OTA: set boot part failed\n");
941 vhd->autonomous_update = 0;
943 vhd->reboot_timer = xTimerCreate("x", pdMS_TO_TICKS(250), 0, vhd,
944 (TimerCallbackFunction_t)reboot_timer_cb);
945 xTimerStart(vhd->reboot_timer, 0);
948 case LWS_CALLBACK_CLOSED_CLIENT_HTTP:
949 lwsl_notice("LWS_CALLBACK_CLOSED_CLIENT_HTTP\n");
952 case LWS_CALLBACK_HTTP_DROP_PROTOCOL:
953 /* called when our wsi user_space is going to be destroyed */
955 lws_spa_destroy(pss->spa);
970 #define LWS_PLUGIN_PROTOCOL_ESPLWS_SCAN \
973 callback_esplws_scan, \
974 sizeof(struct per_session_data__esplws_scan), \