diff --git a/CHANGELOG.md b/CHANGELOG.md index b1c1366..47db691 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,7 @@ # Change Log +* compressed OTA updates via [esp32-flashz](https://github.com/vortigont/esp32-flashz) lib +* web application manifest added, ESPEM webpage could be added as a shortcut on mobile chrome * adopted building with Arduino core >2.0.0 * UI now can detect if PZEM is disconnected or unreachable and show "err" values + energy offset feature diff --git a/espem/espem.cpp b/espem/espem.cpp index a9dcd4f..29a5859 100644 --- a/espem/espem.cpp +++ b/espem/espem.cpp @@ -24,6 +24,8 @@ static const char PGdatajsontpl[] PROGMEM = "{\"age\":%llu,\"U\":%.1f,\"I\":%.2f static const char PROGMEM PGsmpld[] = "Metrics collector disabled"; static const char PROGMEM PGdre[] = "Data read error"; static const char PROGMEM PGacao[] = "Access-Control-Allow-Origin"; +static const char* PGmimetxt = "text/plain"; +//static const char* PGmimehtml = "text/html; charset=utf-8"; using namespace pzmbus; // use general pzem abstractions @@ -109,12 +111,12 @@ String& ESPEM::mktxtdata ( String& txtdata) { // compat method for v 1.x cacti scripts void ESPEM::wpmdata(AsyncWebServerRequest *request) { if ( !tsc.getTScnt() ) { - request->send(503, FPSTR(PGmimetxt), FPSTR(PGdre) ); + request->send(503, PGmimetxt, FPSTR(PGdre) ); return; } String data; - request->send(200, FPSTR(PGmimetxt), mktxtdata(data) ); + request->send(200, PGmimetxt, mktxtdata(data) ); } diff --git a/platformio.ini b/platformio.ini index a09e609..ebd61df 100644 --- a/platformio.ini +++ b/platformio.ini @@ -8,7 +8,7 @@ extra_configs = [common] board_build.filesystem = littlefs framework = arduino -src_build_flags = +build_src_flags = !python flags.py -std=gnu++14 -DUSE_FTP @@ -50,9 +50,8 @@ platform = espressif32 board = wemos_d1_mini32 upload_speed = 460800 monitor_filters = esp32_exception_decoder -;build_flags = -; -DTZONE="MSK-3" -; -DCOUNTRY="ru" +build_flags = + -DCOUNTRY="ru" lib_ignore = ESPAsyncTCP LITTLEFS @@ -87,8 +86,8 @@ lib_deps = build_flags = ${esp32_base.build_flags} -DCOUNTRY="ru" -;src_build_flags = -; ${common.src_build_flags} +;build_src_flags = +; ${common.build_src_flags} ;src_build_unflags = ; ${common.src_build_unflags} @@ -125,3 +124,10 @@ build_flags = -DCOUNTRY="ru" lib_ignore = ESPAsyncTCP + +; Over-the-air compressed update +; copy this template into user_ota.ini file and replace URL with your device adddress +;[espem_ota] +;extra_scripts = post_flashz.py +;OTA_URL = http://espem/update +;OTA_compress = true \ No newline at end of file diff --git a/post_flashz.py b/post_flashz.py new file mode 100644 index 0000000..a94646a --- /dev/null +++ b/post_flashz.py @@ -0,0 +1,96 @@ +#!/usr/bin/python + +#https://docs.platformio.org/en/latest/scripting/actions.html +#https://trass3r.github.io/coding/2018/12/20/advanced-platformio.html +#https://github.com/platformio/bintray-secure-ota/blob/master/publish_firmware.py + +from os.path import basename +from os.path import isfile +from os.path import getsize + +import subprocess +import requests +import sys,zlib +import re + +Import("env", "projenv") + +# access to global build environment +#print(env) + +# access to project build environment (is used source files in "src" folder) +#print(projenv.Dump()) + +# +# Dump build environment (for debug purpose) +# print(env.Dump()) +# + + +def pigz_compress(source, target, env): + firmware_path = str(target[0]) #.replace(".elf", ".bin") + #firmware_name = basename(firmware_path) + print("Compressing %s file..." % basename(firmware_path)) + subprocess.run(["pigz", "-fzk11", firmware_path]) + +#def zlib_compress(source, target, env): +def zlib_compress(source): + imgfile = source + print("Compressing %s file..." % basename(imgfile)) + with open(imgfile, 'rb') as img: + with open(imgfile + '.zz', 'wb') as deflated: + data = zlib.compress(img.read(), zlib.Z_BEST_COMPRESSION) + deflated.write(data) + compress_ratio = (float(getsize(imgfile)) - float(getsize(imgfile + '.zz'))) / float(getsize(imgfile)) * 100 + print("Compress ratio %d%%" % compress_ratio) + +def ota_upload(source, target, env): + file_path = str(source[0]) + print ("Found OTA_url option, will attempt over-the-air upload") + + try: + compress = env.GetProjectOption('OTA_compress') + if compress in ("yes", "true", "1"): + print("Found OTA_compress option") + zlib_compress(file_path) + if (isfile(file_path + ".zz")): + file_path += ".zz" + except: + print ("OTA_compress not found, NOT using compression") + + + url = env.GetProjectOption('OTA_url') + + # check if we upload a firmware or FS image + imgtype = None + if (bool(re.search('firmware', file_path))): + imgtype = 'fw' + else: + imgtype = 'fs' + + payload = {'img' : imgtype } + f = {'file': open(file_path, 'rb')} + req = None + try: + print("Uploading file %s to %s " % (file_path, url)) + req = requests.post(url, data = payload, files=f) + req.raise_for_status() + except requests.exceptions.RequestException as e: + sys.stderr.write("Failed to upload file: %s\n" % + ("%s\n%s" % (req.status_code, req.text) if req else str(e))) + env.Exit(1) + + print("The firmware has been successfuly uploaded!") + +# available targets, i.e. buildprog, size, upload, program, buildfs, uploadfs, uploadfsota +#buildprog is a target when ALL files are compiled and PROGRAM is ready. + +# autocompress all bin images (fw and fs) +#env.AddPostAction("$BUILD_DIR/${PROGNAME}.bin", pigz_compress) + +# Custom upload handler +try: + env.GetProjectOption('OTA_url') + env.Replace(UPLOADCMD=ota_upload) +except: + print ("OTA_url not found, fallback to serial flasher")