mirror of
https://github.com/SasaKaranovic/winfidel-sensor.git
synced 2026-07-08 17:52:35 +02:00
Combining firmware and web data into single binary. Adding step to produce combined binary.
This commit is contained in:
+4
-1
@@ -80,6 +80,9 @@ Project Outputs for*
|
||||
#History
|
||||
History
|
||||
|
||||
# Ignore data folder
|
||||
Firmware/data/
|
||||
|
||||
#Exports, but not manual ones
|
||||
exports/*
|
||||
!exports/manual
|
||||
@@ -88,4 +91,4 @@ exports/*
|
||||
scripts/__pycache__/
|
||||
*.bak
|
||||
*.dwl
|
||||
*.dwl2
|
||||
*.dwl2
|
||||
|
||||
Vendored
|
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 17 KiB |
|
Before Width: | Height: | Size: 2.5 KiB After Width: | Height: | Size: 2.5 KiB |
@@ -2,7 +2,7 @@
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>WInFiDEL v%FW_VERSION% - Sasa Karanovic</title>
|
||||
<title>WInFiDEL v$FW_VERSION$ - Sasa Karanovic</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link rel="stylesheet" href="css/bootstrap.min.css">
|
||||
<link rel="stylesheet" href="css/custom.min.css">
|
||||
@@ -60,7 +60,7 @@
|
||||
<div class="row gy-4 gx-5">
|
||||
<div class="col-lg-6 col-md-6">
|
||||
<p class="small text-muted">
|
||||
WInFiDEL v%FW_VERSION% made by <a href="https://SasaKaranovic.com/" target="_blank">Saša Karanović</a><br>
|
||||
WInFiDEL v$FW_VERSION$ made by <a href="https://SasaKaranovic.com/" target="_blank">Saša Karanović</a><br>
|
||||
WInFiDEL is based on the original InFiDel sensor made by <a href="https://www.youtube.com/watch?v=RYgdLPe_T0c" target="_blank">Thomas Sanladerer</a>.<br>
|
||||
Released as open-source and open-hardware for you to hack and enjoy! :)<br>
|
||||
</p>
|
||||
@@ -2,7 +2,7 @@
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>WInFiDEL v%FW_VERSION% - Sasa Karanovic</title>
|
||||
<title>WInFiDEL v$FW_VERSION$ - Sasa Karanovic</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link rel="stylesheet" href="css/bootstrap.min.css">
|
||||
<link rel="stylesheet" href="css/custom.min.css">
|
||||
@@ -59,7 +59,7 @@
|
||||
<p class="col-lg-8 mx-auto fs-5 text-muted mb-4">
|
||||
We use above calibration points to estimate filament diameter using <a href="https://en.wikipedia.org/wiki/Linear_interpolation" target="_blank" class="text-muted">linear interpolation</a>.<br>
|
||||
First and last point are boundary items and can not be removed.<br>
|
||||
The firmware has been compiled to allow up to %MAX_CALIBRATION_POINTS% calibration points.</p>
|
||||
The firmware has been compiled to allow up to $MAX_CALIBRATION_POINTS$ calibration points.</p>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
@@ -143,57 +143,57 @@
|
||||
<tbody>
|
||||
<tr class="d-flex">
|
||||
<td class="col-4 text-break">MAX_CALIBRATION_POINTS</td>
|
||||
<td class="col-2 text-break">%MAX_CALIBRATION_POINTS%</td>
|
||||
<td class="col-2 text-break">$MAX_CALIBRATION_POINTS$</td>
|
||||
<td class="col-6">Maximum number of calibration points supported by firmware.</td>
|
||||
</tr>
|
||||
<tr class="d-flex">
|
||||
<td class="col-4 text-break">CALIBRATION_POINT_SAMPLE_COUNT</td>
|
||||
<td class="col-2 text-break">%CALIBRATION_POINT_SAMPLE_COUNT%</td>
|
||||
<td class="col-2 text-break">$CALIBRATION_POINT_SAMPLE_COUNT$</td>
|
||||
<td class="col-6">Number of ADC samples taken when creating a calibration point</td>
|
||||
</tr>
|
||||
<tr class="d-flex">
|
||||
<td class="col-4 text-break">CALIBRATION_POINT_ACCURACY_POINT</td>
|
||||
<td class="col-2 text-break">%CALIBRATION_POINT_ACCURACY_POINT%</td>
|
||||
<td class="col-6">When creating calibration point. All ADC samples must be within `%CALIBRATION_POINT_ACCURACY_POINT%` points of each other. Otherwise we will fail to create a calibration point due to ADC noise.</td>
|
||||
<td class="col-2 text-break">$CALIBRATION_POINT_ACCURACY_POINT$</td>
|
||||
<td class="col-6">When creating calibration point. All ADC samples must be within `$CALIBRATION_POINT_ACCURACY_POINT$` points of each other. Otherwise we will fail to create a calibration point due to ADC noise.</td>
|
||||
</tr>
|
||||
<tr class="d-flex">
|
||||
<td class="col-4 text-break">ADC_MIN</td>
|
||||
<td class="col-2 text-break">%ADC_MIN%</td>
|
||||
<td class="col-2 text-break">$ADC_MIN$</td>
|
||||
<td class="col-6">Minimum value ADC can provide</td>
|
||||
</tr>
|
||||
<tr class="d-flex">
|
||||
<td class="col-4 text-break">ADC_MIN_EQUALS_MM</td>
|
||||
<td class="col-2 text-break">%ADC_MIN_EQUALS_MM%</td>
|
||||
<td class="col-6">Minimum ADC value is treated as `%ADC_MIN_EQUALS_MM%`mm</td>
|
||||
<td class="col-2 text-break">$ADC_MIN_EQUALS_MM$</td>
|
||||
<td class="col-6">Minimum ADC value is treated as `$ADC_MIN_EQUALS_MM$`mm</td>
|
||||
</tr>
|
||||
<tr class="d-flex">
|
||||
<td class="col-4 text-break">ADC_MAX</td>
|
||||
<td class="col-2 text-break">%ADC_MAX%</td>
|
||||
<td class="col-2 text-break">$ADC_MAX$</td>
|
||||
<td class="col-6">Maximum value ADC can provide</td>
|
||||
</tr>
|
||||
<tr class="d-flex">
|
||||
<td class="col-4 text-break">ADC_MAX_EQUALS_MM</td>
|
||||
<td class="col-2 text-break">%ADC_MAX_EQUALS_MM%</td>
|
||||
<td class="col-6">Maximum ADC value is treated as `%ADC_MAX_EQUALS_MM%`mm</td>
|
||||
<td class="col-2 text-break">$ADC_MAX_EQUALS_MM$</td>
|
||||
<td class="col-6">Maximum ADC value is treated as `$ADC_MAX_EQUALS_MM$`mm</td>
|
||||
</tr>
|
||||
<tr class="d-flex">
|
||||
<td class="col-4 text-break">BUILD_TIME</td>
|
||||
<td class="col-2 text-break">%BUILD_TIME%</td>
|
||||
<td class="col-2 text-break">$BUILD_TIME$</td>
|
||||
<td class="col-6">Time when firmware was compiled</td>
|
||||
</tr>
|
||||
<tr class="d-flex">
|
||||
<td class="col-4 text-break">BUILD_DATE</td>
|
||||
<td class="col-2 text-break">%BUILD_DATE%</td>
|
||||
<td class="col-2 text-break">$BUILD_DATE$</td>
|
||||
<td class="col-6">Date when firmware was compiled</td>
|
||||
</tr>
|
||||
<tr class="d-flex">
|
||||
<td class="col-4 text-break">ADC_ALGO</td>
|
||||
<td class="col-2 text-break">%ADC_ALGO%</td>
|
||||
<td class="col-2 text-break">$ADC_ALGO$</td>
|
||||
<td class="col-6">Which alghoritm is used to calculate final ADC value</td>
|
||||
</tr>
|
||||
<tr class="d-flex">
|
||||
<td class="col-4 text-break">ADC_CHIP</td>
|
||||
<td class="col-2 text-break">%ADC_CHIP%</td>
|
||||
<td class="col-2 text-break">$ADC_CHIP$</td>
|
||||
<td class="col-6">Which ADC chip is populated on the board</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
@@ -217,7 +217,7 @@
|
||||
<div class="row gy-4 gx-5">
|
||||
<div class="col-lg-6 col-md-6">
|
||||
<p class="small text-muted">
|
||||
WInFiDEL v%FW_VERSION% made by <a href="https://SasaKaranovic.com/" target="_blank">Saša Karanović</a><br>
|
||||
WInFiDEL v$FW_VERSION$ made by <a href="https://SasaKaranovic.com/" target="_blank">Saša Karanović</a><br>
|
||||
WInFiDEL is based on the original InFiDel sensor made by <a href="https://www.youtube.com/watch?v=RYgdLPe_T0c" target="_blank">Thomas Sanladerer</a>.<br>
|
||||
There are two types of people in the world: those who can extrapolate from incomplete data sets.<br>
|
||||
Released as open-source and open-hardware for you to hack and enjoy! :)<br>
|
||||
@@ -0,0 +1,237 @@
|
||||
import os
|
||||
import io
|
||||
import shutil
|
||||
import gzip
|
||||
from datetime import datetime
|
||||
from itertools import islice
|
||||
|
||||
# Todo: Add option to chose between generating h+c files or .ino file
|
||||
# Todo: Add option to configure custom server handlers
|
||||
|
||||
class espembed:
|
||||
def __init__(self, data_directory, output_directory):
|
||||
self.data_directory = data_directory
|
||||
self.output_directory = output_directory
|
||||
self.files = {}
|
||||
self.max_line_length = 60
|
||||
self.supported_extensions = {
|
||||
".htm": { "gzip": False, "template_processor": None, "handler_generator": self.gen_handler_default, "encoding": "text/html", "ctype": "uint8_t"},
|
||||
".html":{ "gzip": False, "template_processor": None, "handler_generator": self.gen_handler_default, "encoding": "text/html", "ctype": "uint8_t"},
|
||||
".js": { "gzip": True, "template_processor": None, "handler_generator": self.gen_handler_default, "encoding": "text/javascript", "ctype": "uint8_t"},
|
||||
".css": { "gzip": True, "template_processor": None, "handler_generator": self.gen_handler_default, "encoding": "text/css", "ctype": "uint8_t"},
|
||||
".ico": { "gzip": True, "template_processor": None, "handler_generator": self.gen_handler_image, "encoding": "image/x-icon", "ctype": "uint8_t"},
|
||||
".png": { "gzip": True, "template_processor": None, "handler_generator": self.gen_handler_image, "encoding": "image/png", "ctype": "uint8_t"},
|
||||
".jpg": { "gzip": True, "template_processor": None, "handler_generator": self.gen_handler_image, "encoding": "image/jpeg", "ctype": "uint8_t"},
|
||||
".jpeg":{ "gzip": True, "template_processor": None, "handler_generator": self.gen_handler_image, "encoding": "image/jpeg", "ctype": "uint8_t"},
|
||||
".gif": { "gzip": True, "template_processor": None, "handler_generator": self.gen_handler_image, "encoding": "image/gif", "ctype": "uint8_t"},
|
||||
".ttf": { "gzip": False, "template_processor": None, "handler_generator": self.gen_handler_default, "encoding": "font/ttf", "ctype": "uint8_t"},
|
||||
".woff": { "gzip": False, "template_processor": None, "handler_generator": self.gen_handler_default, "encoding": "font/woff", "ctype": "uint8_t"},
|
||||
".woff2": { "gzip": False, "template_processor": None, "handler_generator": self.gen_handler_default, "encoding": "font/woff", "ctype": "uint8_t"},
|
||||
}
|
||||
self.web_handler_links = []
|
||||
self.web_data_header = ""
|
||||
self.web_data_content = ""
|
||||
# self.output_path = output_directory
|
||||
self.output_filename = "binary_data.ino"
|
||||
|
||||
def add_file(self, filepath, handler, encoding=None, template_processor=None, handler_generator=None):
|
||||
extension = os.path.splitext(filepath)[1]
|
||||
if extension not in self.supported_extensions.keys():
|
||||
print(f"File `{filepath}` is not in the supported extensions list. Skipping.")
|
||||
return None
|
||||
|
||||
# Option to override default encoding
|
||||
if encoding is None:
|
||||
encoding = self.supported_extensions[extension]['encoding']
|
||||
|
||||
# Option to override default template_processor
|
||||
if template_processor is None:
|
||||
template_processor = self.supported_extensions[extension]['template_processor']
|
||||
|
||||
# Option to override default handler_generator
|
||||
if handler_generator is None:
|
||||
if self.supported_extensions[extension]['handler_generator'] is None:
|
||||
handler_generator = self.gen_handler_default
|
||||
else:
|
||||
handler_generator = self.supported_extensions[extension]['handler_generator']
|
||||
|
||||
|
||||
# Read file contents
|
||||
with open(filepath, mode="rb") as f:
|
||||
content = f.read()
|
||||
|
||||
# If gzip is enabled
|
||||
if self.supported_extensions[extension]['gzip']:
|
||||
print(len(content))
|
||||
content = gzip.compress(content)
|
||||
print(len(content))
|
||||
gzipped = True
|
||||
else:
|
||||
gzipped = False
|
||||
|
||||
item = {
|
||||
'binary_data' : content,
|
||||
"gzipped": gzipped,
|
||||
'size': len(content),
|
||||
'handler': handler ,
|
||||
'encoding': encoding,
|
||||
'ctype': self.supported_extensions[extension]['ctype'],
|
||||
'template_processor': template_processor,
|
||||
'handler_generator': handler_generator
|
||||
}
|
||||
print(f"\t- File content length {len(content)} (gzipped: {gzipped}))")
|
||||
self.files[handler] = item
|
||||
|
||||
def add_directory(self, directory, base_start):
|
||||
for root, dirs, files in os.walk(directory):
|
||||
for file in files:
|
||||
extension = os.path.splitext(file)[1]
|
||||
if extension in self.supported_extensions.keys():
|
||||
handler_base = root.replace(base_start, '').replace('\\', '/')
|
||||
handler = f"{handler_base}/{file}"
|
||||
filepath = os.path.join(root, file)
|
||||
print(f"-- Adding handler for `{handler}` located at `{filepath}`")
|
||||
self.add_file(filepath, handler)
|
||||
|
||||
def binary_data_to_hex_string(self, data):
|
||||
# Providing compatibility for pre 3.12
|
||||
def _batched(iterable, n):
|
||||
if n < 1:
|
||||
raise ValueError('n must be at least one')
|
||||
it = iter(iterable)
|
||||
while batch := tuple(islice(it, n)):
|
||||
yield batch
|
||||
|
||||
ret = ','.join('0x{:02X}'.format(x) for x in data)
|
||||
ret = ",\n".join(", ".join(l) for l in _batched(ret.split(","), 16))
|
||||
return ret
|
||||
|
||||
def generate_c_const_name(self, handler):
|
||||
ret = f"web_data_{handler}"
|
||||
ret = ret.replace('/', '_')
|
||||
ret = ret.replace('\\', '_')
|
||||
ret = ret.replace('-', '_')
|
||||
ret = ret.replace('.', '_')
|
||||
ret = ret.replace('__', '_')
|
||||
return ret
|
||||
|
||||
def write_file_data(self, handler, data):
|
||||
dec_name = self.generate_c_const_name(handler)
|
||||
data_arr_str = self.binary_data_to_hex_string(data)
|
||||
|
||||
header = f""
|
||||
content = f""
|
||||
item = { 'handler': handler, 'header':header, 'content':content }
|
||||
self.web_handler_links.append(item)
|
||||
|
||||
def gen_handler_default(self, data_const_name, item):
|
||||
gzip = ""
|
||||
if item['gzipped']:
|
||||
gzip = 'response->addHeader("Content-Encoding", "gzip");\n\t\t'
|
||||
|
||||
if item['template_processor'] is None:
|
||||
template_processor = ""
|
||||
else:
|
||||
template_processor = f", {item['template_processor']}"
|
||||
|
||||
# request->send_P(200, "{encoding}", {variable_name});
|
||||
ret = f'''server.on("{item['handler']}", HTTP_GET, [](AsyncWebServerRequest *request){{
|
||||
AsyncWebServerResponse *response = request->beginResponse_P(200, "{item['encoding']}", {data_const_name}, {item['size']}{template_processor});
|
||||
{gzip}request->send(response);
|
||||
}});'''
|
||||
return ret
|
||||
|
||||
def gen_handler_image(self, data_const_name, item):
|
||||
gzip = ""
|
||||
if item['gzipped']:
|
||||
gzip = 'response->addHeader("Content-Encoding", "gzip");\n\t\t'
|
||||
|
||||
ret = f'''server.on("{item['handler']}", HTTP_GET, [](AsyncWebServerRequest *request){{
|
||||
AsyncWebServerResponse *response = request->beginResponse_P(200, "{item['encoding']}", {data_const_name}, {item['size']});
|
||||
{gzip}request->send(response);
|
||||
}});'''
|
||||
return ret
|
||||
|
||||
def set_template_processor(self, extension, function_name):
|
||||
try:
|
||||
extension = extension.lower()
|
||||
self.supported_extensions[extension]['template_processor'] = function_name
|
||||
except KeyError as e:
|
||||
print(f"Extensions `{extension}` is not supported.")
|
||||
return
|
||||
|
||||
def generate_output_file(self, filepath):
|
||||
forward_declarations = io.StringIO()
|
||||
data_section = io.StringIO()
|
||||
request_handlers = io.StringIO()
|
||||
|
||||
data_section.write("\n\n//---- BEGIN DATA SECTION ----\n\n")
|
||||
request_handlers.write("\n\n//---- Request handler ----\n")
|
||||
request_handlers.write("void server_init_handlers(void)\n{\n")
|
||||
|
||||
|
||||
# Iterate over all items and generate content for output file
|
||||
for handler, item in self.files.items():
|
||||
'''
|
||||
# Write forward declarations
|
||||
forward_declarations.write('\n\n//---- Forward delcarations ----\n\n')
|
||||
for _, item in self.files.items():
|
||||
forward_declarations.write(f"// Data for handler `{item['handler']}`\n")
|
||||
forward_declarations.write(f"extern const char {item['c_data_const_name']}[{item['size']}];\n")
|
||||
'''
|
||||
|
||||
# --- Write item to data section
|
||||
# todo: Optionally we could gzip/compress data here before embedding
|
||||
c_data_const_name = self.generate_c_const_name(handler)
|
||||
c_hex_str_data = self.binary_data_to_hex_string(item['binary_data'])
|
||||
c_server_handler = item['handler_generator'](c_data_const_name, self.files[handler])
|
||||
|
||||
data_section.write(f"// Content of `{handler}`\n")
|
||||
data_section.write(f"const {item['ctype']} {c_data_const_name}[{item['size']}] PROGMEM = {{\n{c_hex_str_data}\n}};\n")
|
||||
# f.write(f"const uint8_t {item['c_data_const_name']}[] PROGMEM = {{\n{item['c_hex_str_data']}\n}};\n")
|
||||
|
||||
# --- Create request handler for item
|
||||
# Write server request handlers
|
||||
request_handlers.write(f"\t{c_server_handler}\n")
|
||||
|
||||
# -- Finalize data and handler sections
|
||||
data_section.write("\n\n//---- END DATA SECTION ----\n\n")
|
||||
request_handlers.write("}\n")
|
||||
|
||||
# Merge content
|
||||
content = data_section.getvalue() + '\n\n' + request_handlers.getvalue()
|
||||
self.write_ino_file(content);
|
||||
data_section.close()
|
||||
request_handlers.close()
|
||||
forward_declarations.close()
|
||||
|
||||
def write_ino_file(self, content):
|
||||
filename = os.path.join(self.output_directory, self.output_filename)
|
||||
with open(filename, mode='w', encoding='utf-8') as f:
|
||||
f.write("// This file was auto generated using ESP32 Data Embed script\n")
|
||||
f.write("// Please do not manually modify this file\n")
|
||||
f.write("// since any changes to this file will be overwritten on next build.\n")
|
||||
f.write(f"// Generated on {datetime.today().strftime('%Y-%m-%d %H:%M:%S')}\n\n")
|
||||
f.write(content)
|
||||
f.write("\n// ---- End of auto generated file\n")
|
||||
|
||||
def run(self):
|
||||
for handler, file_data in self.files.items():
|
||||
self.write_file_data(handler, file_data['binary_data'])
|
||||
|
||||
self.generate_output_file(self.output_directory)
|
||||
|
||||
def main(data_dir, src_dir):
|
||||
embed = espembed(data_dir, src_dir)
|
||||
embed.set_template_processor('.htm', 'template_const_processor')
|
||||
embed.set_template_processor('.html', 'template_const_processor')
|
||||
embed.add_directory('data', 'data')
|
||||
embed.run()
|
||||
|
||||
Import("env")
|
||||
print("\n--- Embedding binary data...")
|
||||
path = os.getcwd()
|
||||
data_dir = os.path.join(path, 'data')
|
||||
src_dir = os.path.join(path, 'src')
|
||||
main(data_dir, src_dir)
|
||||
print("--- Embedding binary data: complete")
|
||||
@@ -0,0 +1,71 @@
|
||||
# Source https://github.com/letscontrolit/ESPEasy/pull/3845#issuecomment-1005864664
|
||||
|
||||
Import("env") # noqa
|
||||
|
||||
import os
|
||||
import shutil
|
||||
|
||||
if os.environ.get("ESPHOME_USE_SUBPROCESS") is None:
|
||||
try:
|
||||
import esptool
|
||||
except ImportError:
|
||||
env.Execute("$PYTHONEXE -m pip install esptool")
|
||||
else:
|
||||
import subprocess
|
||||
from SCons.Script import ARGUMENTS
|
||||
|
||||
# Copy over the default sdkconfig.
|
||||
from os import path
|
||||
if path.exists("./sdkconfig.defaults"):
|
||||
os.makedirs(".temp", exist_ok=True)
|
||||
shutil.copy("./sdkconfig.defaults", "./.temp/sdkconfig-esp32-idf")
|
||||
|
||||
def esp32_create_combined_bin(source, target, env):
|
||||
verbose = bool(int(ARGUMENTS.get("PIOVERBOSE", "0")))
|
||||
print("-------------------------------------------------------")
|
||||
# if verbose:
|
||||
if True:
|
||||
print("Generating combined binary for serial flashing")
|
||||
app_offset = 0x10000
|
||||
|
||||
new_file_name = env.subst("$BUILD_DIR/merged-firmware.bin")
|
||||
sections = env.subst(env.get("FLASH_EXTRA_IMAGES"))
|
||||
firmware_name = env.subst("$BUILD_DIR/${PROGNAME}.bin")
|
||||
chip = env.get("BOARD_MCU")
|
||||
flash_size = env.BoardConfig().get("upload.flash_size")
|
||||
cmd = [
|
||||
"--chip",
|
||||
chip,
|
||||
"merge_bin",
|
||||
"-o",
|
||||
new_file_name,
|
||||
"--flash_size",
|
||||
flash_size,
|
||||
]
|
||||
|
||||
# if verbose:
|
||||
print(f"Sections: {sections}")
|
||||
if True:
|
||||
print(" Offset | File")
|
||||
for section in sections:
|
||||
sect_adr, sect_file = section.split(" ", 1)
|
||||
if verbose:
|
||||
print(f" - {sect_adr} | {sect_file}")
|
||||
cmd += [sect_adr, sect_file]
|
||||
|
||||
cmd += [hex(app_offset), firmware_name]
|
||||
|
||||
# if verbose:
|
||||
if True:
|
||||
print(f" - {hex(app_offset)} | {firmware_name}")
|
||||
print()
|
||||
print(f"Using esptool.py arguments: {' '.join(cmd)}")
|
||||
print()
|
||||
|
||||
if os.environ.get("ESPHOME_USE_SUBPROCESS") is None:
|
||||
esptool.main(cmd)
|
||||
else:
|
||||
subprocess.run(["esptool.py", *cmd])
|
||||
|
||||
# pylint: disable=E0602
|
||||
env.AddPostAction("$BUILD_DIR/${PROGNAME}.bin", esp32_create_combined_bin) # noqa
|
||||
@@ -0,0 +1,114 @@
|
||||
import os
|
||||
import shutil
|
||||
import minify_html
|
||||
from rjsmin import jsmin
|
||||
|
||||
class Minify:
|
||||
def __init__(self, work_dir, raw_directory, min_directory):
|
||||
self.work_dir = work_dir
|
||||
self.raw_directory = raw_directory
|
||||
self.min_directory = min_directory
|
||||
self._bytes_saved = { 'html': 0, 'js': 0, 'css': 0 }
|
||||
|
||||
print(f"Working directory: {self.work_dir}")
|
||||
print(f"Source directory: {self.raw_directory}")
|
||||
print(f"Data directory: {self.min_directory}")
|
||||
|
||||
def run(self):
|
||||
print("--- Running minifier ---")
|
||||
self._copy_source()
|
||||
|
||||
minify_files = self._find_minify_candidates(self.min_directory)
|
||||
|
||||
for file in minify_files:
|
||||
self._minify(file)
|
||||
print("---")
|
||||
|
||||
def show_stats(self, key=None):
|
||||
if key is not None:
|
||||
print(f"Saved {self._bytes_saved[key]} bytes by minifying `.{key}` files")
|
||||
else:
|
||||
total = 0
|
||||
for key in self._bytes_saved:
|
||||
print(f"Saved {self._bytes_saved[key]} bytes by minifying `.{key}` files")
|
||||
total = total + self._bytes_saved[key]
|
||||
print(f"Total {total} bytes saved")
|
||||
|
||||
def _find_minify_candidates(self, folder):
|
||||
minify_candidates = []
|
||||
for root, dirs, files in os.walk(folder):
|
||||
for file in files:
|
||||
if file.lower().endswith(('.htm', '.html', '.js', '.css')):
|
||||
minify_candidates.append(os.path.join(root, file))
|
||||
|
||||
return minify_candidates
|
||||
|
||||
def _update_saved_count(self, category, amount):
|
||||
self._bytes_saved[category] = self._bytes_saved[category] + amount
|
||||
|
||||
def _minify(self, file):
|
||||
if file.lower().endswith(('.html', '.htm')):
|
||||
content = self._read_file(file)
|
||||
content_minified = self._min_html(content)
|
||||
print(f" -> Minified {file} [saved {len(content)-len(content_minified)} bytes]")
|
||||
self._write_file(file, content_minified)
|
||||
elif file.lower().endswith('.js'):
|
||||
content = self._read_file(file)
|
||||
content_minified = self._min_js(content)
|
||||
self._write_file(file, content_minified)
|
||||
print(f" -> Minified {file} [saved {len(content)-len(content_minified)} bytes]")
|
||||
else:
|
||||
print(f" -> No minifier available for file {file}")
|
||||
|
||||
def _min_html(self, content):
|
||||
size_pre = len(content)
|
||||
content = minify_html.minify(content, minify_js=True, remove_processing_instructions=True)
|
||||
size_after = len(content)
|
||||
self._update_saved_count('html', size_pre-size_after)
|
||||
return content
|
||||
|
||||
def _min_js(self, content):
|
||||
size_pre = len(content)
|
||||
content = jsmin(content)
|
||||
size_after = len(content)
|
||||
self._update_saved_count('js', size_pre-size_after)
|
||||
return content
|
||||
|
||||
def _copy_source(self):
|
||||
shutil.copytree(self.raw_directory, self.min_directory, dirs_exist_ok=True)
|
||||
|
||||
def _read_file(self, file):
|
||||
with open(file, mode="r", encoding="utf-8") as f:
|
||||
content = f.read()
|
||||
return content
|
||||
|
||||
def _write_file(self, file, content):
|
||||
with open(file, mode="w", encoding="utf-8") as f:
|
||||
f.write(content)
|
||||
|
||||
def _list_dir(self, filetype):
|
||||
filtered = []
|
||||
files = os.listdir(self.min_directory)
|
||||
for file in files:
|
||||
if file.endswith(f'.{filetype}'):
|
||||
filepath = os.path.join(self.min_directory, file)
|
||||
filtered.append(filepath)
|
||||
return filtered
|
||||
|
||||
def minify(source, target, env):
|
||||
# work_dir = os.path.dirname(os.path.abspath(__file__))
|
||||
work_dir = os.getcwd()
|
||||
raw_directory = os.path.join(work_dir, 'data_pre')
|
||||
min_directory = os.path.join(work_dir, 'data')
|
||||
|
||||
print("-- Removing previous data directory")
|
||||
shutil.rmtree(min_directory, ignore_errors=True)
|
||||
|
||||
print("-- Running data minify script")
|
||||
|
||||
mc = Minify(work_dir, raw_directory, min_directory)
|
||||
mc.run()
|
||||
mc.show_stats()
|
||||
|
||||
Import("env")
|
||||
minify(None, None, None)
|
||||
+15
-11
@@ -9,18 +9,17 @@
|
||||
; https://docs.platformio.org/page/projectconf.html
|
||||
|
||||
[platformio]
|
||||
default_envs = esp32dev
|
||||
default_envs = esp32c3
|
||||
|
||||
[env:esp32_ota]
|
||||
extends = env:esp32dev
|
||||
extends = env:esp32c3
|
||||
upload_protocol = espota
|
||||
|
||||
[env:esp32dev]
|
||||
[env:esp32c3]
|
||||
board_build.f_cpu = 160000000L
|
||||
board_build.f_flash = 80000000L
|
||||
board_build.flash_mode = qio
|
||||
board_build.filesystem = littlefs
|
||||
board_build.partitions = default.csv
|
||||
board_build.partitions = huge_app.csv
|
||||
|
||||
framework = arduino
|
||||
platform = https://github.com/platformio/platform-espressif32.git
|
||||
@@ -30,16 +29,21 @@ Monitor_speed = 115200
|
||||
build_flags =
|
||||
-DARDUINO_USB_MODE=1
|
||||
-DARDUINO_USB_CDC_ON_BOOT=1
|
||||
-DUSE_ESP32
|
||||
-DUSE_ESP32_FRAMEWORK_ARDUINO
|
||||
-DAUDIO_NO_SD_FS
|
||||
-DTEMPLATE_PLACEHOLDER=36
|
||||
|
||||
extra_scripts =
|
||||
pre:minify_data.py
|
||||
pre:embed_data.py
|
||||
post:merge_firmware.py
|
||||
|
||||
lib_deps=
|
||||
LittleFS
|
||||
FS
|
||||
WiFi
|
||||
EEPROM
|
||||
Wire
|
||||
https://github.com/SasaKaranovic/ESPAsyncWebServer.git
|
||||
https://github.com/me-no-dev/AsyncTCP.git
|
||||
https://github.com/esphome/ESPAsyncWebServer @ ^3.2.2
|
||||
https://github.com/esphome/AsyncTCP @ ^2.1.3
|
||||
ESPmDNS @ 2.0.0
|
||||
Arduino_JSON
|
||||
https://github.com/tzapu/WiFiManager.git
|
||||
;arduino-libraries/Arduino_JSON @ 0.1.0
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -31,6 +31,20 @@
|
||||
#define I2C_SCL_PIN 0 // SCL pin number
|
||||
#define I2C_SDA_PIN 1 // SDA pin number
|
||||
|
||||
#define LED_RED_PIN 5 // RED LED pin number
|
||||
#define LED_GREEN_PIN 4 // GREEN LED pin number
|
||||
#define LED_BLUE_PIN 3 // BLUE LED pin number
|
||||
#define LED_MEASUREMENT_PIN LED_BLUE_PIN
|
||||
|
||||
#define LED_RED_ON() do { digitalWrite(LED_RED_PIN, LOW); } while(0)
|
||||
#define LED_RED_OFF() do { digitalWrite(LED_RED_PIN, HIGH); } while(0)
|
||||
#define LED_GREEN_ON() do { digitalWrite(LED_GREEN_PIN, LOW); } while(0)
|
||||
#define LED_GREEN_OFF() do { digitalWrite(LED_GREEN_PIN, HIGH); } while(0)
|
||||
#define LED_BLUE_ON() do { digitalWrite(LED_BLUE_PIN, LOW); } while(0)
|
||||
#define LED_BLUE_OFF() do { digitalWrite(LED_BLUE_PIN, HIGH); } while(0)
|
||||
#define LED_MEASUREMENT_ON() do { digitalWrite(LED_MEASUREMENT_PIN, LOW); } while(0)
|
||||
#define LED_MEASUREMENT_OFF() do { digitalWrite(LED_MEASUREMENT_PIN, HIGH); } while(0)
|
||||
|
||||
// String buffers
|
||||
#define SENSOR_STR_MAX_LEN 256
|
||||
#define CALIBRATION_JSON_STR_MAX_LEN 512
|
||||
|
||||
+13
-10
@@ -1,11 +1,9 @@
|
||||
#include <ESPmDNS.h>
|
||||
#include <EEPROM.h>
|
||||
#include <WiFiManager.h>
|
||||
#include <Arduino_JSON.h>
|
||||
#include <ArduinoOTA.h>
|
||||
#include "WiFi.h"
|
||||
#include "ESPAsyncWebServer.h"
|
||||
#include "LittleFS.h"
|
||||
#include "include/PersistSettings.h"
|
||||
#include "config_winfidel.h"
|
||||
|
||||
@@ -15,9 +13,19 @@ volatile int WiFi_status = WL_IDLE_STATUS;
|
||||
const char mdnsName[] = MDNS_NAME;
|
||||
const char wifiName[] = WIFI_HOSTNAME;
|
||||
uint8_t dbgOTApercent = 100;
|
||||
bool bStatusLED = false;
|
||||
|
||||
void setup()
|
||||
{
|
||||
pinMode(LED_RED_PIN, OUTPUT);
|
||||
digitalWrite(LED_RED_PIN, HIGH);
|
||||
pinMode(LED_GREEN_PIN, OUTPUT);
|
||||
digitalWrite(LED_GREEN_PIN, HIGH);
|
||||
pinMode(LED_BLUE_PIN, OUTPUT);
|
||||
digitalWrite(LED_BLUE_PIN, HIGH);
|
||||
|
||||
LED_RED_ON();
|
||||
|
||||
// Configure Serial communication
|
||||
Serial.begin(115200);
|
||||
Serial.println("Wireless Inline Filament Estimator, Low-Cost - WInFiDEL");
|
||||
@@ -27,6 +35,8 @@ void setup()
|
||||
|
||||
delay(500);
|
||||
|
||||
LED_GREEN_ON();
|
||||
|
||||
bool res;
|
||||
res = wifiManager.autoConnect("SK-WInFiDEL-Setup");
|
||||
|
||||
@@ -39,14 +49,6 @@ void setup()
|
||||
Serial.println("Connected.");
|
||||
}
|
||||
|
||||
// Initialize LittleFS
|
||||
Serial.print("Starting LittleFS...");
|
||||
if(!LittleFS.begin()){
|
||||
Serial.println("An Error has occurred while mounting LittleFS");
|
||||
return;
|
||||
}
|
||||
Serial.println("done.");
|
||||
|
||||
Serial.print("WiFi IP: ");
|
||||
Serial.println(WiFi.localIP());
|
||||
|
||||
@@ -119,6 +121,7 @@ void setup()
|
||||
Serial.println("Ready to go.");
|
||||
|
||||
i2c_scan_bus();
|
||||
LED_RED_OFF();
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -104,6 +104,18 @@ void Measurements_Tick(void)
|
||||
// Update reading counter
|
||||
numMeasurements++;
|
||||
|
||||
// Update the status LED
|
||||
if (bStatusLED)
|
||||
{
|
||||
bStatusLED = false;
|
||||
LED_MEASUREMENT_OFF();
|
||||
}
|
||||
else
|
||||
{
|
||||
bStatusLED = true;
|
||||
LED_MEASUREMENT_ON();
|
||||
}
|
||||
|
||||
// Set next update timestamp
|
||||
nNextMeasurementTick = millis() + 200;
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
#ifndef __WINFIDEL_FW_VERSION_H__
|
||||
#define __WINFIDEL_FW_VERSION_H__
|
||||
|
||||
#define VERSION_MAJOR 0
|
||||
#define VERSION_MAJOR 2025
|
||||
#define VERSION_MINOR 1
|
||||
#define VERSION_PATCH 0
|
||||
#define VERSION_PATCH 19
|
||||
|
||||
#endif
|
||||
|
||||
@@ -81,24 +81,13 @@ void setupWebServer(void)
|
||||
request->send(404);
|
||||
});
|
||||
|
||||
// send a file when /index is requested
|
||||
server.on("/index.html", HTTP_ANY, [](AsyncWebServerRequest *request){
|
||||
request->send(LittleFS, "/index.html", "text/html", false, template_const_processor);
|
||||
server_init_handlers();
|
||||
|
||||
server.on("/", HTTP_GET, [](AsyncWebServerRequest *request)
|
||||
{
|
||||
request->redirect("/index.html");
|
||||
});
|
||||
|
||||
server.on("/settings.html", HTTP_ANY, [](AsyncWebServerRequest *request){
|
||||
request->send(LittleFS, "/settings.html", "text/html", false, template_const_processor);
|
||||
});
|
||||
|
||||
server.on("/", HTTP_ANY, [](AsyncWebServerRequest *request) {
|
||||
request->send(LittleFS, "/index.html", "text/html", false, template_const_processor);
|
||||
});
|
||||
|
||||
server.serveStatic("/js/", LittleFS, "/js/");
|
||||
server.serveStatic("/css/", LittleFS, "/css/");
|
||||
server.serveStatic("/webfonts/", LittleFS, "/webfonts/");
|
||||
server.serveStatic("/favicon.png", LittleFS, "/favicon.png");
|
||||
server.serveStatic("/favicon.ico", LittleFS, "/favicon.ico");
|
||||
|
||||
server.on("/api/diameter/read", HTTP_GET, [] (AsyncWebServerRequest *request) {
|
||||
format_sensor_data();
|
||||
|
||||
Reference in New Issue
Block a user