wip:
This commit is contained in:
@@ -1 +1 @@
|
||||
idf_component_register(SRCS "main.c" INCLUDE_DIRS "include")
|
||||
idf_component_register(SRCS "zh_ota_server.c" INCLUDE_DIRS "include" REQUIRES app_update esp_http_server EMBED_FILES "zh_ota_server.html")
|
||||
43
Kconfig.projbuild
Normal file
43
Kconfig.projbuild
Normal file
@@ -0,0 +1,43 @@
|
||||
menu "OTA websocket update"
|
||||
|
||||
config OTA_DEFAULT_URI
|
||||
string "OTA page URI"
|
||||
default "/ota"
|
||||
help
|
||||
WEB page URI to OTA update.
|
||||
|
||||
config OTA_DEFAULT_WS_URI
|
||||
string "OTA ws URI"
|
||||
default "/ota/ws"
|
||||
help
|
||||
WEB ws URI to OTA update.
|
||||
|
||||
config OTA_CHUNK_SIZE
|
||||
int "Ota chunk size"
|
||||
default 8192
|
||||
help
|
||||
Ota download chunk size.
|
||||
|
||||
config OTA_PRE_ENCRYPTED_MODE
|
||||
bool "Ota pre-encrypted mode"
|
||||
default n
|
||||
help
|
||||
Ota pre-encrypted mode.
|
||||
|
||||
choice OTA_PRE_ENCRYPTED_RSA_KEY_LOCATION
|
||||
depends on OTA_PRE_ENCRYPTED_MODE
|
||||
prompt "RSA key directory"
|
||||
default OTA_PRE_ENCRYPTED_RSA_KEY_ON_COMPONENT_LOCATION
|
||||
config OTA_PRE_ENCRYPTED_RSA_KEY_ON_PROJECT_LOCATION
|
||||
bool "PROJECT_DIR"
|
||||
config OTA_PRE_ENCRYPTED_RSA_KEY_ON_COMPONENT_LOCATION
|
||||
bool "COMPONENT_DIR"
|
||||
endchoice
|
||||
|
||||
config OTA_PRE_ENCRYPTED_RSA_KEY_DIRECTORY
|
||||
depends on OTA_PRE_ENCRYPTED_MODE
|
||||
string "Ota pre-encrypted RSA key directory"
|
||||
default "rsa_key"
|
||||
|
||||
|
||||
endmenu
|
||||
38
README.md
38
README.md
@@ -1,3 +1,39 @@
|
||||
# esp_component_template
|
||||
|
||||
esp_component_template
|
||||
esp_component_template
|
||||
|
||||
#include "zh_ota_server.h"
|
||||
#include "esp_wifi.h"
|
||||
#include "nvs_flash.h"
|
||||
|
||||
#define WIFI_SSID "ZH_OTA_TEST"
|
||||
#define WIFI_PASS "zh_ota_test"
|
||||
#define WIFI_CHANNEL 1
|
||||
#define MAX_STA_CONNECTION 4
|
||||
|
||||
static httpd_handle_t ota_server_handle = NULL;
|
||||
|
||||
void app_main(void)
|
||||
{
|
||||
esp_log_level_set("zh_ota_server", ESP_LOG_ERROR);
|
||||
nvs_flash_init();
|
||||
esp_event_loop_create_default();
|
||||
esp_netif_init();
|
||||
esp_netif_create_default_wifi_ap();
|
||||
wifi_init_config_t wifi_config = WIFI_INIT_CONFIG_DEFAULT();
|
||||
esp_wifi_init(&wifi_config);
|
||||
wifi_config_t ap_config = {
|
||||
.ap = {
|
||||
.ssid = WIFI_SSID,
|
||||
.password = WIFI_PASS,
|
||||
.max_connection = 4,
|
||||
.authmode = WIFI_AUTH_WPA2_PSK,
|
||||
},
|
||||
};
|
||||
esp_wifi_set_mode(WIFI_MODE_AP);
|
||||
esp_wifi_set_config(WIFI_IF_AP, &ap_config);
|
||||
esp_wifi_start();
|
||||
httpd_config_t config = HTTPD_DEFAULT_CONFIG();
|
||||
httpd_start(&ota_server_handle, &config);
|
||||
zh_ota_server_init(ota_server_handle);
|
||||
}
|
||||
530
include/jsmn.h
Normal file
530
include/jsmn.h
Normal file
@@ -0,0 +1,530 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2010 Serge Zaitsev
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
#ifndef JSMN_H
|
||||
#define JSMN_H
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
#define JSMN_STATIC
|
||||
|
||||
#ifdef JSMN_STATIC
|
||||
#define JSMN_API static
|
||||
#else
|
||||
#define JSMN_API extern
|
||||
#endif
|
||||
|
||||
/**
|
||||
* JSON type identifier. Basic types are:
|
||||
* o Object
|
||||
* o Array
|
||||
* o String
|
||||
* o Other primitive: number, boolean (true/false) or null
|
||||
*/
|
||||
typedef enum
|
||||
{
|
||||
JSMN_UNDEFINED = 0,
|
||||
JSMN_OBJECT = 1,
|
||||
JSMN_ARRAY = 2,
|
||||
JSMN_STRING = 3,
|
||||
JSMN_PRIMITIVE = 4
|
||||
} jsmntype_t;
|
||||
|
||||
enum jsmnerr
|
||||
{
|
||||
/* Not enough tokens were provided */
|
||||
JSMN_ERROR_NOMEM = -1,
|
||||
/* Invalid character inside JSON string */
|
||||
JSMN_ERROR_INVAL = -2,
|
||||
/* The string is not a full JSON packet, more bytes expected */
|
||||
JSMN_ERROR_PART = -3
|
||||
};
|
||||
|
||||
/**
|
||||
* JSON token description.
|
||||
* type type (object, array, string etc.)
|
||||
* start start position in JSON data string
|
||||
* end end position in JSON data string
|
||||
*/
|
||||
typedef struct jsmntok
|
||||
{
|
||||
jsmntype_t type;
|
||||
int start;
|
||||
int end;
|
||||
int size;
|
||||
#ifdef JSMN_PARENT_LINKS
|
||||
int parent;
|
||||
#endif
|
||||
} jsmntok_t;
|
||||
|
||||
/**
|
||||
* JSON parser. Contains an array of token blocks available. Also stores
|
||||
* the string being parsed now and current position in that string.
|
||||
*/
|
||||
typedef struct jsmn_parser
|
||||
{
|
||||
unsigned int pos; /* offset in the JSON string */
|
||||
unsigned int toknext; /* next token to allocate */
|
||||
int toksuper; /* superior token node, e.g. parent object or array */
|
||||
} jsmn_parser;
|
||||
|
||||
/**
|
||||
* Create JSON parser over an array of tokens
|
||||
*/
|
||||
JSMN_API void jsmn_init(jsmn_parser *parser);
|
||||
|
||||
/**
|
||||
* Run JSON parser. It parses a JSON data string into and array of tokens, each
|
||||
* describing
|
||||
* a single JSON object.
|
||||
*/
|
||||
JSMN_API int jsmn_parse(jsmn_parser *parser, const char *js, const size_t len,
|
||||
jsmntok_t *tokens, const unsigned int num_tokens);
|
||||
|
||||
#ifndef JSMN_HEADER
|
||||
/**
|
||||
* Allocates a fresh unused token from the token pool.
|
||||
*/
|
||||
static jsmntok_t *jsmn_alloc_token(jsmn_parser *parser, jsmntok_t *tokens,
|
||||
const size_t num_tokens)
|
||||
{
|
||||
jsmntok_t *tok;
|
||||
if (parser->toknext >= num_tokens)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
tok = &tokens[parser->toknext++];
|
||||
tok->start = tok->end = -1;
|
||||
tok->size = 0;
|
||||
#ifdef JSMN_PARENT_LINKS
|
||||
tok->parent = -1;
|
||||
#endif
|
||||
return tok;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fills token type and boundaries.
|
||||
*/
|
||||
static void jsmn_fill_token(jsmntok_t *token, const jsmntype_t type,
|
||||
const int start, const int end)
|
||||
{
|
||||
token->type = type;
|
||||
token->start = start;
|
||||
token->end = end;
|
||||
token->size = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fills next available token with JSON primitive.
|
||||
*/
|
||||
static int jsmn_parse_primitive(jsmn_parser *parser, const char *js,
|
||||
const size_t len, jsmntok_t *tokens,
|
||||
const size_t num_tokens)
|
||||
{
|
||||
jsmntok_t *token;
|
||||
int start;
|
||||
|
||||
start = parser->pos;
|
||||
|
||||
for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++)
|
||||
{
|
||||
switch (js[parser->pos])
|
||||
{
|
||||
#ifndef JSMN_STRICT
|
||||
/* In strict mode primitive must be followed by "," or "}" or "]" */
|
||||
case ':':
|
||||
#endif
|
||||
case '\t':
|
||||
case '\r':
|
||||
case '\n':
|
||||
case ' ':
|
||||
case ',':
|
||||
case ']':
|
||||
case '}':
|
||||
goto found;
|
||||
default:
|
||||
/* to quiet a warning from gcc*/
|
||||
break;
|
||||
}
|
||||
if (js[parser->pos] < 32 || js[parser->pos] >= 127)
|
||||
{
|
||||
parser->pos = start;
|
||||
return JSMN_ERROR_INVAL;
|
||||
}
|
||||
}
|
||||
#ifdef JSMN_STRICT
|
||||
/* In strict mode primitive must be followed by a comma/object/array */
|
||||
parser->pos = start;
|
||||
return JSMN_ERROR_PART;
|
||||
#endif
|
||||
|
||||
found:
|
||||
if (tokens == NULL)
|
||||
{
|
||||
parser->pos--;
|
||||
return 0;
|
||||
}
|
||||
token = jsmn_alloc_token(parser, tokens, num_tokens);
|
||||
if (token == NULL)
|
||||
{
|
||||
parser->pos = start;
|
||||
return JSMN_ERROR_NOMEM;
|
||||
}
|
||||
jsmn_fill_token(token, JSMN_PRIMITIVE, start, parser->pos);
|
||||
#ifdef JSMN_PARENT_LINKS
|
||||
token->parent = parser->toksuper;
|
||||
#endif
|
||||
parser->pos--;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fills next token with JSON string.
|
||||
*/
|
||||
static int jsmn_parse_string(jsmn_parser *parser, const char *js,
|
||||
const size_t len, jsmntok_t *tokens,
|
||||
const size_t num_tokens)
|
||||
{
|
||||
jsmntok_t *token;
|
||||
|
||||
int start = parser->pos;
|
||||
|
||||
parser->pos++;
|
||||
|
||||
/* Skip starting quote */
|
||||
for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++)
|
||||
{
|
||||
char c = js[parser->pos];
|
||||
|
||||
/* Quote: end of string */
|
||||
if (c == '\"')
|
||||
{
|
||||
if (tokens == NULL)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
token = jsmn_alloc_token(parser, tokens, num_tokens);
|
||||
if (token == NULL)
|
||||
{
|
||||
parser->pos = start;
|
||||
return JSMN_ERROR_NOMEM;
|
||||
}
|
||||
jsmn_fill_token(token, JSMN_STRING, start + 1, parser->pos);
|
||||
#ifdef JSMN_PARENT_LINKS
|
||||
token->parent = parser->toksuper;
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Backslash: Quoted symbol expected */
|
||||
if (c == '\\' && parser->pos + 1 < len)
|
||||
{
|
||||
int i;
|
||||
parser->pos++;
|
||||
switch (js[parser->pos])
|
||||
{
|
||||
/* Allowed escaped symbols */
|
||||
case '\"':
|
||||
case '/':
|
||||
case '\\':
|
||||
case 'b':
|
||||
case 'f':
|
||||
case 'r':
|
||||
case 'n':
|
||||
case 't':
|
||||
break;
|
||||
/* Allows escaped symbol \uXXXX */
|
||||
case 'u':
|
||||
parser->pos++;
|
||||
for (i = 0; i < 4 && parser->pos < len && js[parser->pos] != '\0';
|
||||
i++)
|
||||
{
|
||||
/* If it isn't a hex character we have an error */
|
||||
if (!((js[parser->pos] >= 48 && js[parser->pos] <= 57) || /* 0-9 */
|
||||
(js[parser->pos] >= 65 && js[parser->pos] <= 70) || /* A-F */
|
||||
(js[parser->pos] >= 97 && js[parser->pos] <= 102)))
|
||||
{ /* a-f */
|
||||
parser->pos = start;
|
||||
return JSMN_ERROR_INVAL;
|
||||
}
|
||||
parser->pos++;
|
||||
}
|
||||
parser->pos--;
|
||||
break;
|
||||
/* Unexpected symbol */
|
||||
default:
|
||||
parser->pos = start;
|
||||
return JSMN_ERROR_INVAL;
|
||||
}
|
||||
}
|
||||
}
|
||||
parser->pos = start;
|
||||
return JSMN_ERROR_PART;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse JSON string and fill tokens.
|
||||
*/
|
||||
JSMN_API int jsmn_parse(jsmn_parser *parser, const char *js, const size_t len,
|
||||
jsmntok_t *tokens, const unsigned int num_tokens)
|
||||
{
|
||||
int r;
|
||||
int i;
|
||||
jsmntok_t *token;
|
||||
int count = parser->toknext;
|
||||
|
||||
for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++)
|
||||
{
|
||||
char c;
|
||||
jsmntype_t type;
|
||||
|
||||
c = js[parser->pos];
|
||||
switch (c)
|
||||
{
|
||||
case '{':
|
||||
case '[':
|
||||
count++;
|
||||
if (tokens == NULL)
|
||||
{
|
||||
break;
|
||||
}
|
||||
token = jsmn_alloc_token(parser, tokens, num_tokens);
|
||||
if (token == NULL)
|
||||
{
|
||||
return JSMN_ERROR_NOMEM;
|
||||
}
|
||||
if (parser->toksuper != -1)
|
||||
{
|
||||
jsmntok_t *t = &tokens[parser->toksuper];
|
||||
#ifdef JSMN_STRICT
|
||||
/* In strict mode an object or array can't become a key */
|
||||
if (t->type == JSMN_OBJECT)
|
||||
{
|
||||
return JSMN_ERROR_INVAL;
|
||||
}
|
||||
#endif
|
||||
t->size++;
|
||||
#ifdef JSMN_PARENT_LINKS
|
||||
token->parent = parser->toksuper;
|
||||
#endif
|
||||
}
|
||||
token->type = (c == '{' ? JSMN_OBJECT : JSMN_ARRAY);
|
||||
token->start = parser->pos;
|
||||
parser->toksuper = parser->toknext - 1;
|
||||
break;
|
||||
case '}':
|
||||
case ']':
|
||||
if (tokens == NULL)
|
||||
{
|
||||
break;
|
||||
}
|
||||
type = (c == '}' ? JSMN_OBJECT : JSMN_ARRAY);
|
||||
#ifdef JSMN_PARENT_LINKS
|
||||
if (parser->toknext < 1)
|
||||
{
|
||||
return JSMN_ERROR_INVAL;
|
||||
}
|
||||
token = &tokens[parser->toknext - 1];
|
||||
for (;;)
|
||||
{
|
||||
if (token->start != -1 && token->end == -1)
|
||||
{
|
||||
if (token->type != type)
|
||||
{
|
||||
return JSMN_ERROR_INVAL;
|
||||
}
|
||||
token->end = parser->pos + 1;
|
||||
parser->toksuper = token->parent;
|
||||
break;
|
||||
}
|
||||
if (token->parent == -1)
|
||||
{
|
||||
if (token->type != type || parser->toksuper == -1)
|
||||
{
|
||||
return JSMN_ERROR_INVAL;
|
||||
}
|
||||
break;
|
||||
}
|
||||
token = &tokens[token->parent];
|
||||
}
|
||||
#else
|
||||
for (i = parser->toknext - 1; i >= 0; i--)
|
||||
{
|
||||
token = &tokens[i];
|
||||
if (token->start != -1 && token->end == -1)
|
||||
{
|
||||
if (token->type != type)
|
||||
{
|
||||
return JSMN_ERROR_INVAL;
|
||||
}
|
||||
parser->toksuper = -1;
|
||||
token->end = parser->pos + 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
/* Error if unmatched closing bracket */
|
||||
if (i == -1)
|
||||
{
|
||||
return JSMN_ERROR_INVAL;
|
||||
}
|
||||
for (; i >= 0; i--)
|
||||
{
|
||||
token = &tokens[i];
|
||||
if (token->start != -1 && token->end == -1)
|
||||
{
|
||||
parser->toksuper = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
break;
|
||||
case '\"':
|
||||
r = jsmn_parse_string(parser, js, len, tokens, num_tokens);
|
||||
if (r < 0)
|
||||
{
|
||||
return r;
|
||||
}
|
||||
count++;
|
||||
if (parser->toksuper != -1 && tokens != NULL)
|
||||
{
|
||||
tokens[parser->toksuper].size++;
|
||||
}
|
||||
break;
|
||||
case '\t':
|
||||
case '\r':
|
||||
case '\n':
|
||||
case ' ':
|
||||
break;
|
||||
case ':':
|
||||
parser->toksuper = parser->toknext - 1;
|
||||
break;
|
||||
case ',':
|
||||
if (tokens != NULL && parser->toksuper != -1 &&
|
||||
tokens[parser->toksuper].type != JSMN_ARRAY &&
|
||||
tokens[parser->toksuper].type != JSMN_OBJECT)
|
||||
{
|
||||
#ifdef JSMN_PARENT_LINKS
|
||||
parser->toksuper = tokens[parser->toksuper].parent;
|
||||
#else
|
||||
for (i = parser->toknext - 1; i >= 0; i--)
|
||||
{
|
||||
if (tokens[i].type == JSMN_ARRAY || tokens[i].type == JSMN_OBJECT)
|
||||
{
|
||||
if (tokens[i].start != -1 && tokens[i].end == -1)
|
||||
{
|
||||
parser->toksuper = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
break;
|
||||
#ifdef JSMN_STRICT
|
||||
/* In strict mode primitives are: numbers and booleans */
|
||||
case '-':
|
||||
case '0':
|
||||
case '1':
|
||||
case '2':
|
||||
case '3':
|
||||
case '4':
|
||||
case '5':
|
||||
case '6':
|
||||
case '7':
|
||||
case '8':
|
||||
case '9':
|
||||
case 't':
|
||||
case 'f':
|
||||
case 'n':
|
||||
/* And they must not be keys of the object */
|
||||
if (tokens != NULL && parser->toksuper != -1)
|
||||
{
|
||||
const jsmntok_t *t = &tokens[parser->toksuper];
|
||||
if (t->type == JSMN_OBJECT ||
|
||||
(t->type == JSMN_STRING && t->size != 0))
|
||||
{
|
||||
return JSMN_ERROR_INVAL;
|
||||
}
|
||||
}
|
||||
#else
|
||||
/* In non-strict mode every unquoted value is a primitive */
|
||||
default:
|
||||
#endif
|
||||
r = jsmn_parse_primitive(parser, js, len, tokens, num_tokens);
|
||||
if (r < 0)
|
||||
{
|
||||
return r;
|
||||
}
|
||||
count++;
|
||||
if (parser->toksuper != -1 && tokens != NULL)
|
||||
{
|
||||
tokens[parser->toksuper].size++;
|
||||
}
|
||||
break;
|
||||
|
||||
#ifdef JSMN_STRICT
|
||||
/* Unexpected char in strict mode */
|
||||
default:
|
||||
return JSMN_ERROR_INVAL;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
if (tokens != NULL)
|
||||
{
|
||||
for (i = parser->toknext - 1; i >= 0; i--)
|
||||
{
|
||||
/* Unmatched opened object or array */
|
||||
if (tokens[i].start != -1 && tokens[i].end == -1)
|
||||
{
|
||||
return JSMN_ERROR_PART;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new parser based over a given buffer with an array of tokens
|
||||
* available.
|
||||
*/
|
||||
JSMN_API void jsmn_init(jsmn_parser *parser)
|
||||
{
|
||||
parser->pos = 0;
|
||||
parser->toknext = 0;
|
||||
parser->toksuper = -1;
|
||||
}
|
||||
|
||||
#endif /* JSMN_HEADER */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* JSMN_H */
|
||||
29
include/zh_ota_server.h
Normal file
29
include/zh_ota_server.h
Normal file
@@ -0,0 +1,29 @@
|
||||
#pragma once
|
||||
|
||||
#include "esp_ota_ops.h"
|
||||
#include "esp_flash_partitions.h"
|
||||
#include "esp_partition.h"
|
||||
#include "esp_image_format.h"
|
||||
#include <esp_log.h>
|
||||
#include "esp_http_server.h"
|
||||
|
||||
#include "jsmn.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
/*
|
||||
* @brief register ota_ws httpd handlers ( web page & ws handlers) on existing httpd server with ws support
|
||||
* uri page -> CONFIG_OTA_DEFAULT_WS_URI
|
||||
* @param httpd_handle_t server -> existing server handle
|
||||
* @return
|
||||
* ESP_OK -> register OK
|
||||
* ESP_FAIL -> register FAIL
|
||||
*/
|
||||
esp_err_t zh_ota_server_init(httpd_handle_t server);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
420
zh_ota_server.c
Normal file
420
zh_ota_server.c
Normal file
@@ -0,0 +1,420 @@
|
||||
/*
|
||||
This example code is in the Public Domain (or CC0 licensed, at your option.)
|
||||
|
||||
Unless required by applicable law or agreed to in writing, this
|
||||
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
CONDITIONS OF ANY KIND, either express or implied.
|
||||
*/
|
||||
// #include "ota_ws_update_private.h"
|
||||
#include "zh_ota_server.h"
|
||||
// #include "esp_ota_ops.h"
|
||||
// #include "esp_flash_partitions.h"
|
||||
// #include "esp_partition.h"
|
||||
// #include "esp_image_format.h"
|
||||
// #include <esp_log.h>
|
||||
// #include "esp_http_server.h"
|
||||
|
||||
// #include "jsmn.h"
|
||||
|
||||
#define OTA_RESTART_ESP "otaRestartEsp"
|
||||
#define OTA_SIZE_START "otaSize"
|
||||
#define OTA_SET_CHUNK_SIZE "otaSetChunkSize"
|
||||
#define OTA_GET_CHUNK "otaGetChunk"
|
||||
#define OTA_END "otaEnd"
|
||||
#define OTA_ERROR "otaError"
|
||||
#define OTA_CANCEL "otaCancel"
|
||||
#define OTA_CHECK_ROLLBACK "otaCheckRollback"
|
||||
#define OTA_PROCESS_ROLLBACK "otaProcessRollback"
|
||||
|
||||
|
||||
esp_err_t start_ota_ws(void);
|
||||
esp_err_t write_ota_ws(int data_read, uint8_t *ota_write_data);
|
||||
esp_err_t end_ota_ws(void);
|
||||
esp_err_t abort_ota_ws(void);
|
||||
bool check_ota_ws_rollback_enable(void);
|
||||
esp_err_t rollback_ota_ws(bool rollback);
|
||||
|
||||
#define OTA_DEFAULT_WS_URI CONFIG_OTA_DEFAULT_WS_URI
|
||||
#define OTA_DEFAULT_URI CONFIG_OTA_DEFAULT_URI
|
||||
#define OTA_CHUNK_SIZE (CONFIG_OTA_CHUNK_SIZE & ~0xf)
|
||||
|
||||
|
||||
static const char *TAG = "zh_ota_server";
|
||||
|
||||
static int ota_size; // ota firmware size
|
||||
static int ota_start_chunk; // start address of http chunk
|
||||
static int ota_started; // ota download started
|
||||
|
||||
static esp_err_t json_to_str_parm(char *jsonstr, char *nameStr, char *valStr);
|
||||
static esp_err_t send_json_string(char *str, httpd_req_t *req);
|
||||
static esp_err_t ota_ws_handler(httpd_req_t *req);
|
||||
static void ota_error(httpd_req_t *req, char *code, char *msg);
|
||||
|
||||
static const esp_partition_t *update_partition = NULL;
|
||||
static bool image_header_was_checked = false;
|
||||
static esp_ota_handle_t update_handle = 0;
|
||||
|
||||
//static int tstc=0;
|
||||
|
||||
esp_err_t start_ota_ws(void)
|
||||
{
|
||||
//return ESP_OK; // debug return
|
||||
//tstc=0;
|
||||
|
||||
esp_err_t err;
|
||||
ESP_LOGI(TAG, "Starting OTA");
|
||||
|
||||
const esp_partition_t *configured = esp_ota_get_boot_partition();
|
||||
const esp_partition_t *running = esp_ota_get_running_partition();
|
||||
if(configured==NULL || running == NULL)
|
||||
{
|
||||
ESP_LOGE(TAG,"OTA data not found");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
if (configured != running)
|
||||
{
|
||||
ESP_LOGW(TAG, "Configured OTA boot partition at offset 0x%08lx, but running from offset 0x%08lx",
|
||||
configured->address, running->address);
|
||||
ESP_LOGW(TAG, "(This can happen if either the OTA boot data or preferred boot image become corrupted somehow.)");
|
||||
}
|
||||
ESP_LOGI(TAG, "Running partition type %d subtype %d (offset 0x%08lx)",
|
||||
running->type, running->subtype, running->address);
|
||||
|
||||
update_partition = esp_ota_get_next_update_partition(NULL);
|
||||
ESP_LOGI(TAG, "Writing to partition subtype %d at offset 0x%lx",
|
||||
update_partition->subtype, update_partition->address);
|
||||
|
||||
err = esp_ota_begin(update_partition, OTA_SIZE_UNKNOWN, &update_handle);
|
||||
if (err != ESP_OK)
|
||||
{
|
||||
ESP_LOGE(TAG, "esp_ota_begin failed ");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
ESP_LOGI(TAG, "esp_ota_begin succeeded");
|
||||
|
||||
image_header_was_checked = false;
|
||||
return ESP_OK;
|
||||
}
|
||||
esp_err_t write_ota_ws(int data_read, uint8_t *ota_write_data)
|
||||
{
|
||||
//return ESP_OK; // debug return
|
||||
|
||||
|
||||
if (image_header_was_checked == false) // first segment
|
||||
{
|
||||
esp_app_desc_t new_app_info;
|
||||
if (data_read > sizeof(esp_image_header_t) + sizeof(esp_image_segment_header_t) + sizeof(esp_app_desc_t))
|
||||
{
|
||||
// check current version with downloading
|
||||
memcpy(&new_app_info, &ota_write_data[sizeof(esp_image_header_t) + sizeof(esp_image_segment_header_t)], sizeof(esp_app_desc_t));
|
||||
ESP_LOGI(TAG, "New firmware version: %s", new_app_info.version);
|
||||
|
||||
image_header_was_checked = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
ESP_LOGE(TAG, "Received package is not fit len");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
}
|
||||
esp_err_t err = esp_ota_write(update_handle, (const void *)ota_write_data, data_read);
|
||||
//tstc+=data_read;
|
||||
if (err != ESP_OK)
|
||||
{
|
||||
return ESP_FAIL;
|
||||
}
|
||||
//ESP_LOGI("tstc","%d",tstc);
|
||||
return ESP_OK;
|
||||
}
|
||||
esp_err_t end_ota_ws(void)
|
||||
{
|
||||
//return ESP_OK; // debug return
|
||||
|
||||
esp_err_t err = esp_ota_end(update_handle);
|
||||
if (err != ESP_OK) {
|
||||
if (err == ESP_ERR_OTA_VALIDATE_FAILED) {
|
||||
ESP_LOGE(TAG, "Image validation failed, image is corrupted");
|
||||
}
|
||||
ESP_LOGE(TAG, "esp_ota_end failed (%s)!", esp_err_to_name(err));
|
||||
return ESP_FAIL;
|
||||
}
|
||||
err = esp_ota_set_boot_partition(update_partition);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "esp_ota_set_boot_partition failed (%s)!", esp_err_to_name(err));
|
||||
return ESP_FAIL;
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
esp_err_t abort_ota_ws(void)
|
||||
{
|
||||
return esp_ota_abort(update_handle);
|
||||
}
|
||||
// false - rollback disable
|
||||
// true - rollback enable
|
||||
bool check_ota_ws_rollback_enable(void)
|
||||
{
|
||||
#ifdef CONFIG_BOOTLOADER_APP_ROLLBACK_ENABLE
|
||||
esp_ota_img_states_t ota_state_running_part;
|
||||
const esp_partition_t *running = esp_ota_get_running_partition();
|
||||
if (esp_ota_get_state_partition(running, &ota_state_running_part) == ESP_OK) {
|
||||
if (ota_state_running_part == ESP_OTA_IMG_PENDING_VERIFY) {
|
||||
ESP_LOGI(TAG, "Running app has ESP_OTA_IMG_PENDING_VERIFY state");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
// rollback == true - rollback
|
||||
// rollback == false - app valid? confirm update -> no rollback
|
||||
esp_err_t rollback_ota_ws(bool rollback)
|
||||
{
|
||||
#ifdef CONFIG_BOOTLOADER_APP_ROLLBACK_ENABLE
|
||||
if(rollback == false)
|
||||
{
|
||||
return esp_ota_mark_app_valid_cancel_rollback(); // app valid
|
||||
}
|
||||
else
|
||||
{
|
||||
return esp_ota_mark_app_invalid_rollback_and_reboot(); // app rolback & reboot
|
||||
}
|
||||
#endif
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
// abort OTA, send error/cancel msg to ws
|
||||
static void ota_error(httpd_req_t *req, char *code, char *msg)
|
||||
{
|
||||
char json_str[128];
|
||||
ota_size = ota_start_chunk = ota_started = 0;
|
||||
abort_ota_ws();
|
||||
ESP_LOGE(TAG, "%s %s", code, msg);
|
||||
snprintf(json_str, sizeof(json_str), "{\"name\":\"%s\",\"value\":\"%s\"}", code, msg);
|
||||
send_json_string(json_str, req);
|
||||
}
|
||||
|
||||
// simple json parse -> only one parametr name/val
|
||||
static esp_err_t json_to_str_parm(char *jsonstr, char *nameStr, char *valStr) // распаковать строку json в пару name/val
|
||||
{
|
||||
int r; // количество токенов
|
||||
jsmn_parser p;
|
||||
jsmntok_t t[5]; // только 2 пары параметров и obj
|
||||
|
||||
jsmn_init(&p);
|
||||
r = jsmn_parse(&p, jsonstr, strlen(jsonstr), t, sizeof(t) / sizeof(t[0]));
|
||||
if (r < 2)
|
||||
{
|
||||
valStr[0] = 0;
|
||||
nameStr[0] = 0;
|
||||
return ESP_FAIL;
|
||||
}
|
||||
strncpy(nameStr, jsonstr + t[2].start, t[2].end - t[2].start);
|
||||
nameStr[t[2].end - t[2].start] = 0;
|
||||
if (r > 3)
|
||||
{
|
||||
strncpy(valStr, jsonstr + t[4].start, t[4].end - t[4].start);
|
||||
valStr[t[4].end - t[4].start] = 0;
|
||||
}
|
||||
else
|
||||
valStr[0] = 0;
|
||||
return ESP_OK;
|
||||
}
|
||||
// send string to ws
|
||||
static esp_err_t send_json_string(char *str, httpd_req_t *req)
|
||||
{
|
||||
httpd_ws_frame_t ws_pkt;
|
||||
memset(&ws_pkt, 0, sizeof(httpd_ws_frame_t));
|
||||
ws_pkt.type = HTTPD_WS_TYPE_TEXT;
|
||||
ws_pkt.payload = (uint8_t *)str;
|
||||
ws_pkt.len = strlen(str);
|
||||
return httpd_ws_send_frame(req, &ws_pkt);
|
||||
}
|
||||
// main ws OTA handler
|
||||
// Handshake and process OTA
|
||||
static esp_err_t ota_ws_handler(httpd_req_t *req)
|
||||
{
|
||||
char json_key[64] = {0};
|
||||
char json_value[64] = {0};
|
||||
char json_str[128] = {0};
|
||||
|
||||
httpd_ws_frame_t ws_pkt;
|
||||
uint8_t *buf = NULL;
|
||||
|
||||
|
||||
if (req->method == HTTP_GET)
|
||||
{
|
||||
ESP_LOGI(TAG, "Handshake done, the new connection was opened");
|
||||
if(check_ota_ws_rollback_enable()) // check rollback enable, send cmd to enable rollback dialog on html
|
||||
{
|
||||
snprintf(json_str, sizeof(json_str), "{\"name\":\"%s\",\"value\":\"%s\" }", OTA_CHECK_ROLLBACK, "true");
|
||||
send_json_string(json_str, req);
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
memset(&ws_pkt, 0, sizeof(httpd_ws_frame_t));
|
||||
// Set max_len = 0 to get the frame len
|
||||
esp_err_t ret = httpd_ws_recv_frame(req, &ws_pkt, 0);
|
||||
if (ret != ESP_OK)
|
||||
{
|
||||
ota_error(req, OTA_ERROR, "httpd_ws_recv_frame failed to get frame len");
|
||||
return ret;
|
||||
}
|
||||
if (ws_pkt.len)
|
||||
{
|
||||
// ws_pkt.len + 1 is for NULL termination as we are expecting a string
|
||||
buf = calloc(1, ws_pkt.len + 1);
|
||||
if (buf == NULL)
|
||||
{
|
||||
ota_error(req, OTA_ERROR, "Failed to calloc memory for buf");
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
ws_pkt.payload = buf;
|
||||
// Set max_len = ws_pkt.len to get the frame payload
|
||||
ret = httpd_ws_recv_frame(req, &ws_pkt, ws_pkt.len);
|
||||
if (ret != ESP_OK)
|
||||
{
|
||||
ota_error(req, OTA_ERROR, "httpd_ws_recv_frame failed");
|
||||
goto _recv_ret;
|
||||
}
|
||||
}
|
||||
ret = ESP_OK;
|
||||
if (ws_pkt.type == HTTPD_WS_TYPE_TEXT) // process json cmd
|
||||
{
|
||||
if (json_to_str_parm((char *)buf, json_key, json_value)) // decode json to key/value parm
|
||||
{
|
||||
ota_error(req, OTA_ERROR, "Error json str");
|
||||
goto _recv_ret;
|
||||
}
|
||||
if (strncmp(json_key, OTA_SIZE_START, sizeof(OTA_SIZE_START)) == 0) // start ota
|
||||
{
|
||||
ota_size = atoi(json_value);
|
||||
if (ota_size == 0)
|
||||
{
|
||||
ota_error(req, OTA_ERROR, "Error ota size = 0");
|
||||
goto _recv_ret;
|
||||
}
|
||||
ret = start_ota_ws();
|
||||
if (ret)
|
||||
{
|
||||
ota_error(req, OTA_ERROR, "Error start ota");
|
||||
goto _recv_ret;
|
||||
}
|
||||
ota_started = 1;
|
||||
ota_start_chunk = 0;
|
||||
snprintf(json_str, sizeof(json_str), "{\"name\":\"%s\",\"value\":%d}", OTA_SET_CHUNK_SIZE, OTA_CHUNK_SIZE); // set download chunk
|
||||
send_json_string(json_str, req);
|
||||
snprintf(json_str, sizeof(json_str), "{\"name\":\"%s\",\"value\":%d}", OTA_GET_CHUNK, ota_start_chunk); // cmd -> send first chunk with start addresss = 0
|
||||
send_json_string(json_str, req);
|
||||
}
|
||||
if (strncmp(json_key, OTA_CANCEL, sizeof(OTA_CANCEL)) == 0) // cancel ota
|
||||
{
|
||||
ota_error(req, OTA_CANCEL, "Cancel command");
|
||||
ret = ESP_OK;
|
||||
goto _recv_ret;
|
||||
}
|
||||
if (strncmp(json_key, OTA_ERROR, sizeof(OTA_ERROR)) == 0) // error ota
|
||||
{
|
||||
ota_error(req, OTA_ERROR, "Error command");
|
||||
ret = ESP_OK;
|
||||
goto _recv_ret;
|
||||
}
|
||||
if (strncmp(json_key, OTA_PROCESS_ROLLBACK, sizeof(OTA_PROCESS_ROLLBACK)) == 0) // process rollback &
|
||||
{
|
||||
if(strncmp(json_value,"true",sizeof("true")) == 0)
|
||||
{
|
||||
ESP_LOGI(TAG,"Rollback and restart");
|
||||
ret = rollback_ota_ws(true); // rollback and restart
|
||||
}
|
||||
else
|
||||
{
|
||||
ESP_LOGI(TAG,"App veryfied, fix ota update");
|
||||
ret = rollback_ota_ws(false); // app veryfied
|
||||
}
|
||||
goto _recv_ret;
|
||||
}
|
||||
if (strncmp(json_key, OTA_RESTART_ESP, sizeof(OTA_RESTART_ESP)) == 0) // cancel ota
|
||||
{
|
||||
esp_restart();
|
||||
}
|
||||
|
||||
}
|
||||
else if (ws_pkt.type == HTTPD_WS_TYPE_BINARY && ota_started) // download OTA firmware with chunked part
|
||||
{
|
||||
|
||||
if (ota_start_chunk + ws_pkt.len < ota_size) //read chuk of ota
|
||||
{
|
||||
ret = write_ota_ws(ws_pkt.len, buf); // write chunk of ota
|
||||
if (ret)
|
||||
{
|
||||
ota_error(req, OTA_ERROR, "Error write ota");
|
||||
goto _recv_ret;
|
||||
}
|
||||
ota_start_chunk += ws_pkt.len;
|
||||
snprintf(json_str, sizeof(json_str), "{\"name\":\"%s\",\"value\": %d }", OTA_GET_CHUNK, ota_start_chunk); // cmd -> next chunk
|
||||
send_json_string(json_str, req);
|
||||
|
||||
}
|
||||
else // last chunk and end ota
|
||||
{
|
||||
ret = write_ota_ws(ws_pkt.len, buf); // write last chunk of ota
|
||||
if (ret)
|
||||
{
|
||||
ota_error(req, OTA_ERROR, "Error write ota");
|
||||
goto _recv_ret;
|
||||
}
|
||||
ret = end_ota_ws(); // end ota
|
||||
if (ret)
|
||||
{
|
||||
ota_error(req, OTA_ERROR, "Error end ota");
|
||||
goto _recv_ret;
|
||||
}
|
||||
ota_size = 0;
|
||||
ota_start_chunk = 0;
|
||||
ota_started = 0;
|
||||
ESP_LOGI(TAG,"OTA END OK");
|
||||
snprintf(json_str, sizeof(json_str), "{\"name\":\"%s\",\"value\":\"%s\" }", OTA_END, "OK"); // send ota end cmd ( ota ok )
|
||||
send_json_string(json_str, req);
|
||||
}
|
||||
}
|
||||
_recv_ret:
|
||||
free(buf);
|
||||
return ret;
|
||||
}
|
||||
// main http get handler
|
||||
// send http initial page and js code
|
||||
static esp_err_t ota_get_handler(httpd_req_t *req)
|
||||
{
|
||||
extern const unsigned char ota_ws_update_html_start[] asm("_binary_zh_ota_server_html_start");
|
||||
extern const unsigned char ota_ws_update_html_end[] asm("_binary_zh_ota_server_html_end");
|
||||
const size_t ota_ws_update_html_size = (ota_ws_update_html_end - ota_ws_update_html_start);
|
||||
|
||||
httpd_resp_send_chunk(req, (const char *)ota_ws_update_html_start, ota_ws_update_html_size);
|
||||
httpd_resp_sendstr_chunk(req, NULL);
|
||||
return ESP_OK;
|
||||
}
|
||||
static const httpd_uri_t gh = {
|
||||
.uri = OTA_DEFAULT_URI,
|
||||
.method = HTTP_GET,
|
||||
.handler = ota_get_handler,
|
||||
.user_ctx = NULL};
|
||||
static const httpd_uri_t ws = {
|
||||
.uri = OTA_DEFAULT_WS_URI,
|
||||
.method = HTTP_GET,
|
||||
.handler = ota_ws_handler,
|
||||
.user_ctx = NULL,
|
||||
.is_websocket = true};
|
||||
|
||||
// register all ota uri handler
|
||||
esp_err_t zh_ota_server_init(httpd_handle_t server)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
ret = httpd_register_uri_handler(server, &gh);
|
||||
if (ret)
|
||||
goto _ret;
|
||||
ret = httpd_register_uri_handler(server, &ws);
|
||||
if (ret)
|
||||
goto _ret;
|
||||
_ret:
|
||||
return ret;
|
||||
}
|
||||
247
zh_ota_server.html
Normal file
247
zh_ota_server.html
Normal file
@@ -0,0 +1,247 @@
|
||||
<!DOCTYPE html>
|
||||
|
||||
<html lang="ru">
|
||||
|
||||
<head>
|
||||
<title>OTA UPDATE</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
|
||||
<style>
|
||||
.column {
|
||||
float: left;
|
||||
width: 100%;
|
||||
margin-top: 2px;
|
||||
margin-bottom: 2px;
|
||||
}
|
||||
|
||||
.btn {
|
||||
float: left;
|
||||
width: 100%;
|
||||
margin: 2px;
|
||||
|
||||
}
|
||||
|
||||
.cl1 {
|
||||
float: left;
|
||||
width: 100%;
|
||||
margin: 2px;
|
||||
margin-top: 2px;
|
||||
margin-bottom: 2px;
|
||||
}
|
||||
|
||||
.cl01 {
|
||||
float: left;
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
margin-top: 2px;
|
||||
margin-bottom: 2px;
|
||||
}
|
||||
|
||||
.cl02 {
|
||||
float: left;
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
margin-top: 2px;
|
||||
margin-bottom: 2px;
|
||||
}
|
||||
|
||||
.hdr {
|
||||
float: left;
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
color: white;
|
||||
background-color: blue;
|
||||
padding: 5px;
|
||||
margin: 5px;
|
||||
}
|
||||
|
||||
.logstr {
|
||||
width: 100%;
|
||||
float: left;
|
||||
}
|
||||
</style>
|
||||
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="hdr">OTA UPDATE</div>
|
||||
|
||||
<div class="column">
|
||||
<button class="btn" id="goHome">Home Page</button>
|
||||
</div>
|
||||
<div id="rollback" style="display:none">
|
||||
<div class="column">
|
||||
<button class="btn" id="otaVerifyApp">Click to confirm and commit OTA update</button>
|
||||
</div>
|
||||
<div class="column">
|
||||
<button class="btn" id="otaRollback">Cancel OTA. Click to rollback update and restart</button>
|
||||
</div>
|
||||
</div>
|
||||
<div id="update" style="display:block">
|
||||
<div class="cl1" style="display:none">
|
||||
<label class="cl01" for="otaFile">Select the new OTA firmware file</label>
|
||||
<input class="cl02" type="file" id="otaFile" placeholder="select file" onchange="readOtaFile(this)">
|
||||
</div>
|
||||
<div class="column" style="display:block" id="otaFileSelectVisible">
|
||||
<button class="btn" id="otaFileSelect" onclick="document.getElementById('otaFile').click()">File
|
||||
Select</button>
|
||||
</div>
|
||||
|
||||
<div class="column" style="display:none" id="otaStartVisible">
|
||||
<button class="btn" id="otaStartCancel">Start OTA update</button>
|
||||
</div>
|
||||
<div class="column" style="display:none" id="otaReStartVisible">
|
||||
<button class="btn" id="otaReStart">Reboot with new OTA firmware</button>
|
||||
</div>
|
||||
<div id="otaProgressVisible" style="display:none">
|
||||
<div class="cl1">
|
||||
<progress class="cl02" id="otaPogress" max=100 value=0>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
|
||||
let otaData;
|
||||
let otaSetChunkSize = 0;
|
||||
let otaStartsegment = 0;
|
||||
let otaStarted = 0;
|
||||
|
||||
function readOtaFile(input) {
|
||||
let reader = new FileReader();
|
||||
let file = input.files[0];
|
||||
document.getElementById('otaFileSelect').innerHTML = "Selected firmware file: " + file.name;
|
||||
reader.readAsArrayBuffer(file);
|
||||
input.value = null;
|
||||
|
||||
reader.onload = function () {
|
||||
otaData = new Uint8Array(reader.result);
|
||||
document.getElementById("otaStartVisible").style.display = "block";
|
||||
document.getElementById("otaProgressVisible").style.display = "none";
|
||||
document.getElementById("otaReStartVisible").style.display = "none";
|
||||
};
|
||||
|
||||
reader.onerror = function () {
|
||||
console.log(reader.error);
|
||||
};
|
||||
}
|
||||
</script>
|
||||
<script>
|
||||
document.getElementById("otaStartCancel").addEventListener("click", function (e) {
|
||||
if (otaData.length > 0 && otaStarted == 0) {
|
||||
|
||||
socket.send(JSON.stringify({ name: "otaSize", value: otaData.length }));
|
||||
otaStarted = 1;
|
||||
this.innerHTML = "Click to Cancel";
|
||||
document.getElementById("otaFileSelect").disabled = true;
|
||||
document.getElementById("otaProgressVisible").style.display = "block";
|
||||
document.getElementById("otaPogress").max = otaData.length;
|
||||
}
|
||||
else {
|
||||
otaStarted = 0;
|
||||
socket.send(JSON.stringify({ name: "otaCancel", value: "Cancel" }));
|
||||
}
|
||||
|
||||
});
|
||||
document.getElementById("goHome").addEventListener("click", function (e) {
|
||||
//onclick="window.location.href = '/'"
|
||||
socket.close();
|
||||
window.location.href = '/';
|
||||
});
|
||||
document.getElementById("otaReStart").addEventListener("click", function (e) {
|
||||
socket.send(JSON.stringify({ name: "otaRestartEsp", value: "restart" }));
|
||||
});
|
||||
|
||||
function receiveWsData(data) {
|
||||
try {
|
||||
let obj = JSON.parse(data);
|
||||
switch (obj.name) {
|
||||
case "otaSetChunkSize":
|
||||
otaSetChunkSize = obj.value;
|
||||
break;
|
||||
case "otaGetChunk":
|
||||
let otaDataSend = otaData.subarray(obj.value, obj.value + otaSetChunkSize);
|
||||
document.getElementById("otaPogress").value = obj.value;
|
||||
document.getElementById("otaStartCancel").innerHTML = "Ota download. Size = " + otaData.length + " Segment = " + obj.value + " Click to Cancel";
|
||||
socket.send(otaDataSend);
|
||||
break;
|
||||
case "otaEnd":
|
||||
otaStartsegment = 0;
|
||||
otaStarted = 0;
|
||||
document.getElementById("otaStartVisible").style.display = "none";
|
||||
document.getElementById("otaStartCancel").innerHTML = "Start OTA update";
|
||||
document.getElementById("otaPogress").value = otaData.length;
|
||||
document.getElementById("otaFileSelect").disabled = false;
|
||||
document.getElementById("otaReStartVisible").style.display = "block";
|
||||
document.getElementById("otaReStart").innerHTML = "The firmware is loaded. Click to reboot with new OTA firmware";
|
||||
document.getElementById("otaReStart").disabled = false;
|
||||
break;
|
||||
case "otaError":
|
||||
case "otaCancel":
|
||||
otaStartsegment = 0;
|
||||
otaStarted = 0;
|
||||
document.getElementById("otaStartVisible").style.display = "none";
|
||||
document.getElementById("otaStartCancel").innerHTML = "Start OTA update";
|
||||
document.getElementById("otaPogress").value = otaData.length;
|
||||
document.getElementById("otaFileSelect").disabled = false;
|
||||
document.getElementById("otaReStartVisible").style.display = "block";
|
||||
document.getElementById("otaReStart").innerHTML = "ОТА firmware download canceled " + obj.value;
|
||||
document.getElementById("otaReStart").disabled = true;
|
||||
break;
|
||||
case "otaCheckRollback":
|
||||
document.getElementById("rollback").style.display = "block";
|
||||
document.getElementById("update").style.display = "none";
|
||||
break;
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
console.log(data + "Error msg");
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<script> // rollback
|
||||
document.getElementById("otaVerifyApp").addEventListener("click", function (e) {
|
||||
socket.send(JSON.stringify({ name: "otaProcessRollback", value: "false" }));
|
||||
document.getElementById("rollback").style.display = "none";
|
||||
document.getElementById("update").style.display = "block";
|
||||
});
|
||||
|
||||
document.getElementById("otaRollback").addEventListener("click", function (e) {
|
||||
socket.send(JSON.stringify({ name: "otaProcessRollback", value: "true" }));
|
||||
document.getElementById("rollback").style.display = "none";
|
||||
document.getElementById("update").style.display = "block";
|
||||
});
|
||||
|
||||
</script>
|
||||
|
||||
<script> // основной старт скрипта, открыть сокет
|
||||
// создать сокет по адресу
|
||||
let protocol = "ws:"
|
||||
if(document.location.protocol == "https:") protocol = "wss:"
|
||||
let wsHostStr = protocol + "//" + document.location.host + document.location.pathname;
|
||||
wsHostStr += (document.location.pathname == '/') ? "ws" : "/ws";
|
||||
var socket = new WebSocket(wsHostStr);
|
||||
socket.binaryType = "arraybuffer";
|
||||
</script>
|
||||
|
||||
<script> // события WS
|
||||
socket.onopen = function () {
|
||||
console.log("connect ws");
|
||||
};
|
||||
socket.onclose = function (event) {
|
||||
console.log("close ws - reload");
|
||||
setTimeout(() => document.location.reload(), 2000);
|
||||
};
|
||||
socket.onerror = function () {
|
||||
console.log("error ws");
|
||||
setTimeout(() => document.location.reload(), 2000);
|
||||
};
|
||||
socket.onmessage = function (event) {
|
||||
receiveWsData(event.data);
|
||||
};
|
||||
</script>
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
||||
Reference in New Issue
Block a user