Combining firmware and web data into single binary. Adding step to produce combined binary.

This commit is contained in:
Sasa Karanovic
2025-01-22 19:05:45 -05:00
parent 65a92918bf
commit dfe2fed65f
35 changed files with 39860 additions and 59 deletions
+4 -1
View File
@@ -80,6 +80,9 @@ Project Outputs for*
#History #History
History History
# Ignore data folder
Firmware/data/
#Exports, but not manual ones #Exports, but not manual ones
exports/* exports/*
!exports/manual !exports/manual
@@ -88,4 +91,4 @@ exports/*
scripts/__pycache__/ scripts/__pycache__/
*.bak *.bak
*.dwl *.dwl
*.dwl2 *.dwl2

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"> <html lang="en">
<head> <head>
<meta charset="utf-8"> <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"> <meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="css/bootstrap.min.css"> <link rel="stylesheet" href="css/bootstrap.min.css">
<link rel="stylesheet" href="css/custom.min.css"> <link rel="stylesheet" href="css/custom.min.css">
@@ -60,7 +60,7 @@
<div class="row gy-4 gx-5"> <div class="row gy-4 gx-5">
<div class="col-lg-6 col-md-6"> <div class="col-lg-6 col-md-6">
<p class="small text-muted"> <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> 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> Released as open-source and open-hardware for you to hack and enjoy! :)<br>
</p> </p>
@@ -2,7 +2,7 @@
<html lang="en"> <html lang="en">
<head> <head>
<meta charset="utf-8"> <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"> <meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="css/bootstrap.min.css"> <link rel="stylesheet" href="css/bootstrap.min.css">
<link rel="stylesheet" href="css/custom.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"> <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> 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> 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>
</div> </div>
@@ -143,57 +143,57 @@
<tbody> <tbody>
<tr class="d-flex"> <tr class="d-flex">
<td class="col-4 text-break">MAX_CALIBRATION_POINTS</td> <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> <td class="col-6">Maximum number of calibration points supported by firmware.</td>
</tr> </tr>
<tr class="d-flex"> <tr class="d-flex">
<td class="col-4 text-break">CALIBRATION_POINT_SAMPLE_COUNT</td> <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> <td class="col-6">Number of ADC samples taken when creating a calibration point</td>
</tr> </tr>
<tr class="d-flex"> <tr class="d-flex">
<td class="col-4 text-break">CALIBRATION_POINT_ACCURACY_POINT</td> <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-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-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>
<tr class="d-flex"> <tr class="d-flex">
<td class="col-4 text-break">ADC_MIN</td> <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> <td class="col-6">Minimum value ADC can provide</td>
</tr> </tr>
<tr class="d-flex"> <tr class="d-flex">
<td class="col-4 text-break">ADC_MIN_EQUALS_MM</td> <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-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-6">Minimum ADC value is treated as `$ADC_MIN_EQUALS_MM$`mm</td>
</tr> </tr>
<tr class="d-flex"> <tr class="d-flex">
<td class="col-4 text-break">ADC_MAX</td> <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> <td class="col-6">Maximum value ADC can provide</td>
</tr> </tr>
<tr class="d-flex"> <tr class="d-flex">
<td class="col-4 text-break">ADC_MAX_EQUALS_MM</td> <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-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-6">Maximum ADC value is treated as `$ADC_MAX_EQUALS_MM$`mm</td>
</tr> </tr>
<tr class="d-flex"> <tr class="d-flex">
<td class="col-4 text-break">BUILD_TIME</td> <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> <td class="col-6">Time when firmware was compiled</td>
</tr> </tr>
<tr class="d-flex"> <tr class="d-flex">
<td class="col-4 text-break">BUILD_DATE</td> <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> <td class="col-6">Date when firmware was compiled</td>
</tr> </tr>
<tr class="d-flex"> <tr class="d-flex">
<td class="col-4 text-break">ADC_ALGO</td> <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> <td class="col-6">Which alghoritm is used to calculate final ADC value</td>
</tr> </tr>
<tr class="d-flex"> <tr class="d-flex">
<td class="col-4 text-break">ADC_CHIP</td> <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> <td class="col-6">Which ADC chip is populated on the board</td>
</tr> </tr>
</tbody> </tbody>
@@ -217,7 +217,7 @@
<div class="row gy-4 gx-5"> <div class="row gy-4 gx-5">
<div class="col-lg-6 col-md-6"> <div class="col-lg-6 col-md-6">
<p class="small text-muted"> <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> 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> 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> Released as open-source and open-hardware for you to hack and enjoy! :)<br>
+237
View File
@@ -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")
+71
View File
@@ -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
+114
View File
@@ -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
View File
@@ -9,18 +9,17 @@
; https://docs.platformio.org/page/projectconf.html ; https://docs.platformio.org/page/projectconf.html
[platformio] [platformio]
default_envs = esp32dev default_envs = esp32c3
[env:esp32_ota] [env:esp32_ota]
extends = env:esp32dev extends = env:esp32c3
upload_protocol = espota upload_protocol = espota
[env:esp32dev] [env:esp32c3]
board_build.f_cpu = 160000000L board_build.f_cpu = 160000000L
board_build.f_flash = 80000000L board_build.f_flash = 80000000L
board_build.flash_mode = qio board_build.flash_mode = qio
board_build.filesystem = littlefs board_build.partitions = huge_app.csv
board_build.partitions = default.csv
framework = arduino framework = arduino
platform = https://github.com/platformio/platform-espressif32.git platform = https://github.com/platformio/platform-espressif32.git
@@ -30,16 +29,21 @@ Monitor_speed = 115200
build_flags = build_flags =
-DARDUINO_USB_MODE=1 -DARDUINO_USB_MODE=1
-DARDUINO_USB_CDC_ON_BOOT=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= lib_deps=
LittleFS
FS
WiFi WiFi
EEPROM EEPROM
Wire Wire
https://github.com/SasaKaranovic/ESPAsyncWebServer.git https://github.com/esphome/ESPAsyncWebServer @ ^3.2.2
https://github.com/me-no-dev/AsyncTCP.git https://github.com/esphome/AsyncTCP @ ^2.1.3
ESPmDNS @ 2.0.0 ESPmDNS @ 2.0.0
Arduino_JSON
https://github.com/tzapu/WiFiManager.git https://github.com/tzapu/WiFiManager.git
;arduino-libraries/Arduino_JSON @ 0.1.0
File diff suppressed because it is too large Load Diff
+14
View File
@@ -31,6 +31,20 @@
#define I2C_SCL_PIN 0 // SCL pin number #define I2C_SCL_PIN 0 // SCL pin number
#define I2C_SDA_PIN 1 // SDA 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 // String buffers
#define SENSOR_STR_MAX_LEN 256 #define SENSOR_STR_MAX_LEN 256
#define CALIBRATION_JSON_STR_MAX_LEN 512 #define CALIBRATION_JSON_STR_MAX_LEN 512
+13 -10
View File
@@ -1,11 +1,9 @@
#include <ESPmDNS.h> #include <ESPmDNS.h>
#include <EEPROM.h> #include <EEPROM.h>
#include <WiFiManager.h> #include <WiFiManager.h>
#include <Arduino_JSON.h>
#include <ArduinoOTA.h> #include <ArduinoOTA.h>
#include "WiFi.h" #include "WiFi.h"
#include "ESPAsyncWebServer.h" #include "ESPAsyncWebServer.h"
#include "LittleFS.h"
#include "include/PersistSettings.h" #include "include/PersistSettings.h"
#include "config_winfidel.h" #include "config_winfidel.h"
@@ -15,9 +13,19 @@ volatile int WiFi_status = WL_IDLE_STATUS;
const char mdnsName[] = MDNS_NAME; const char mdnsName[] = MDNS_NAME;
const char wifiName[] = WIFI_HOSTNAME; const char wifiName[] = WIFI_HOSTNAME;
uint8_t dbgOTApercent = 100; uint8_t dbgOTApercent = 100;
bool bStatusLED = false;
void setup() 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 // Configure Serial communication
Serial.begin(115200); Serial.begin(115200);
Serial.println("Wireless Inline Filament Estimator, Low-Cost - WInFiDEL"); Serial.println("Wireless Inline Filament Estimator, Low-Cost - WInFiDEL");
@@ -27,6 +35,8 @@ void setup()
delay(500); delay(500);
LED_GREEN_ON();
bool res; bool res;
res = wifiManager.autoConnect("SK-WInFiDEL-Setup"); res = wifiManager.autoConnect("SK-WInFiDEL-Setup");
@@ -39,14 +49,6 @@ void setup()
Serial.println("Connected."); 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.print("WiFi IP: ");
Serial.println(WiFi.localIP()); Serial.println(WiFi.localIP());
@@ -119,6 +121,7 @@ void setup()
Serial.println("Ready to go."); Serial.println("Ready to go.");
i2c_scan_bus(); i2c_scan_bus();
LED_RED_OFF();
} }
+12
View File
@@ -104,6 +104,18 @@ void Measurements_Tick(void)
// Update reading counter // Update reading counter
numMeasurements++; numMeasurements++;
// Update the status LED
if (bStatusLED)
{
bStatusLED = false;
LED_MEASUREMENT_OFF();
}
else
{
bStatusLED = true;
LED_MEASUREMENT_ON();
}
// Set next update timestamp // Set next update timestamp
nNextMeasurementTick = millis() + 200; nNextMeasurementTick = millis() + 200;
} }
+2 -2
View File
@@ -1,8 +1,8 @@
#ifndef __WINFIDEL_FW_VERSION_H__ #ifndef __WINFIDEL_FW_VERSION_H__
#define __WINFIDEL_FW_VERSION_H__ #define __WINFIDEL_FW_VERSION_H__
#define VERSION_MAJOR 0 #define VERSION_MAJOR 2025
#define VERSION_MINOR 1 #define VERSION_MINOR 1
#define VERSION_PATCH 0 #define VERSION_PATCH 19
#endif #endif
+5 -16
View File
@@ -81,24 +81,13 @@ void setupWebServer(void)
request->send(404); request->send(404);
}); });
// send a file when /index is requested server_init_handlers();
server.on("/index.html", HTTP_ANY, [](AsyncWebServerRequest *request){
request->send(LittleFS, "/index.html", "text/html", false, template_const_processor); 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) { server.on("/api/diameter/read", HTTP_GET, [] (AsyncWebServerRequest *request) {
format_sensor_data(); format_sensor_data();