fix: webserver stability, memory leaks, epub underlining, flash screen

Webserver:
- Remove MD5 hash computation from file listings (caused EAGAIN errors)
- Implement JSON batching (2KB) with pacing for file listings
- Simplify sendContentSafe() flow control

Memory:
- Cache QR codes on server start instead of regenerating per render
- Optimize WiFi scan: vector deduplication, 20 network limit, early scanDelete()
- Fix 48KB cover buffer leak when navigating Home -> File Transfer

EPUB Reader:
- Fix errant underlining by flushing partWordBuffer before style changes
- Text before styled inline elements (e.g., <a> with CSS underline) no longer
  incorrectly receives the element's styling

Flash Screen:
- Fix version string buffer overflow (30 -> 50 char limit)
- Use half refresh for cleaner display
- Adjust pre_flash.py timing for half refresh completion
This commit is contained in:
cottongin
2026-01-30 22:00:15 -05:00
parent 48267ad848
commit 5464d9de3a
9 changed files with 260 additions and 178 deletions

View File

@@ -5,7 +5,11 @@ This allows the firmware to display "Flashing firmware..." on the e-ink display
before the actual flash begins. The e-ink retains this message throughout the
flash process since it doesn't require power to maintain the display.
Protocol: Sends "FLASH:version\n" where version is read from platformio.ini
Protocol (Plan A - Simple timing):
1. Host opens serial port and sends "FLASH:version"
2. Host keeps port open briefly for device to receive and process
3. Device displays flash screen when it receives the command
4. Host proceeds with flash
"""
Import("env")
@@ -15,7 +19,7 @@ from version_utils import get_version
def before_upload(source, target, env):
"""Send FLASH command with version to device before upload begins."""
"""Send FLASH command to device before uploading firmware."""
port = env.GetProjectOption("upload_port", None)
if not port:
@@ -29,19 +33,20 @@ def before_upload(source, target, env):
]
port = ports[0] if ports else None
if port:
try:
version = get_version(env)
ser = serial.Serial(port, 115200, timeout=1)
ser.write(f"FLASH:{version}\n".encode())
ser.flush()
ser.close()
time.sleep(0.8) # Wait for e-ink fast refresh (~500ms) plus margin
print(f"[pre_flash] Flash notification sent to {port} (version {version})")
except Exception as e:
print(f"[pre_flash] Notification skipped: {e}")
else:
if not port:
print("[pre_flash] No serial port found, skipping notification")
return
try:
version = get_version(env)
ser = serial.Serial(port, 115200, timeout=1)
ser.write(f"FLASH:{version}\n".encode())
ser.flush()
time.sleep(4.0) # Keep port open for device to receive and complete full refresh (~2-3s)
ser.close()
print(f"[pre_flash] Flash notification sent to {port} (version {version})")
except Exception as e:
print(f"[pre_flash] Notification skipped: {e}")
env.AddPreAction("upload", before_upload)