Skip to content

v0.5.6

Pre-release
Pre-release

Choose a tag to compare

@ilyakruchinin ilyakruchinin released this 31 Jul 04:23
3b6effa

SomnoTrace — Changes since v0.5.5

Battery Monitoring (comprehensive rewrite)

Battery sampling

  • ADC calibration: Uses ESP-IDF eFuse-backed curve-fitting calibration (adc_cali_curve_fitting) instead of nominal scaling. Without eFuse data, falls back to nominal (reads a few percent low).
  • Trimmed-mean sampling: 256 samples spread over ~1 second (4 ms apart) with lowest/highest 12.5% discarded. Rejects Wi-Fi/BLE TX burst dips that corrupted back-to-back samples.
  • OCV-based percentage: Li-ion discharge curve is non-linear (flat 3.9–3.6 V, cliff below). Replaced linear voltage-to-percent map with a piecewise-linear OCV lookup table (4200 mV = 100%, 3300 mV = 0%).
  • Charging IR offset: Subtracts 120 mV from terminal voltage while charging to approximate true OCV.
  • Slew limiting: Published percentage moves at most 1 point per update cycle (60 s discharge / 20 s charge) with monotonic-while-charging rule. Eliminates visible "jump" on plug-in.
  • Unplug settle: 30-second hold after charger disconnect lets terminal voltage relax to OCV before trusting the reading.

No-battery detection

  • Threshold: If VBAT > 4250 mV (above any real Li-ion cell max of ~4200 mV), the battery is considered absent. USB power with no cell installed pegs the ADC at ~5 V through the 200 kΩ / 100 kΩ divider.
  • Behaviour: Publishes valid = false and percent = -1, causing the LCD battery indicator to be hidden. Filter state is reset so a newly installed battery is detected within one sample cycle.
  • Stale state fix: bat_publish() now clears valid / percent / millivolts when ok = false, preventing stale readings from persisting after battery removal.

Battery API (bsp_power.h)

  • New bsp_battery_t struct: percent, millivolts, charging, valid, age_s.
  • bsp_power_battery_monitor_start() — background monitor task ( sole ADC owner, samples on slow cadence, publishes snapshot).
  • bsp_power_battery_get() — non-blocking snapshot read, safe from any task.
  • bsp_power_battery_percent() — convenience wrapper.
  • bsp_power_is_charging() — reads CHG_STAT (GPIO3) directly, cheap and always current.

Wi-Fi — Stale Connection Fix & Failover

Stale connection bug

  • Root cause: ESP-IDF Wi-Fi event handler does not automatically clear the application-level "connected" flag on WIFI_EVENT_STA_DISCONNECTED. The old code left s_connected / s_connected_ip stale, so the LCD and /api/status kept advertising a dead connection.
  • Fix: Added link_mark_down() called on disconnect, which immediately clears the published IP and SSID. Consumers read live state via netprov_get_link() and never cache "connected".

Autonomous failover

  • Problem: esp_wifi_connect() only retries the single SSID currently programmed into the driver. Without intervention, a permanently disappeared network strands the device even when another configured network is in range.
  • Fix: Event handler counts failed reconnects; after 5 failures it sets a rescan flag. A background link_supervisor_task performs a full scan-and-rank across all configured networks and connects to the best available candidate. Credentials are cached at initial connect so the supervisor can rescan without NVS access.

Link state API (net_provision.h)

  • New netprov_link_t struct: up, ssid, ip, rssi, rssi_valid.
  • netprov_get_link() — non-blocking snapshot, safe from any task.
  • netprov_is_link_up() — convenience boolean.

LCD status display

  • Replaced hardcoded "Wi-Fi Connected" text with the actual SSID from netprov_get_link() in all three status display paths (boot, periodic refresh with SD OK, periodic refresh with SD error).

EDF Export — Enum Mapping Fixes (edf_gen.c)

  • AutoSetComfort (S.AS.Comfort): Removed invalid "Plus" value (does not exist in AS11 JSON). Fixed Off mapping from 0 → 1 (AS11 EDF convention: raw enum + 1). OSCAR decrements by 1 for AS11, so EDF value 1 → OSCAR 0 = "Standard", EDF value 2 → OSCAR 1 = "Soft".
  • Mask type (S.Mask): Fixed mapping to use raw + 2 convention. Pillows = 2, Nasal = 4, Pediatric = 5 (was incorrectly 1/2/4).
  • Tube type (S.Tube): Fixed 15mmNonHeated mapping from "3m" to correct string match, value 3.
  • Documentation: Updated inline comments to reference spec/0002-edf-export.md §4.3.4 for the enum convention.

OTA Firmware Update

Backend (net_provision.c)

  • Endpoint: POST /api/ota accepts raw binary (application/octet-stream), max 4 MB.
  • Architecture: Two-task design to work around ESP32-S3 PSRAM cache freeze:
    • httpd handler (PSRAM stack) — receives HTTP body in 4 KB chunks, pushes to an 8 KB FreeRTOS stream buffer.
    • ota_flash_task (internal RAM stack via xTaskCreate) — reads from stream buffer, calls esp_ota_begin / esp_ota_write / esp_ota_end / esp_ota_set_boot_partition. Internal stack required because esp_ota_* functions freeze the SPI cache, which asserts the task stack is not in PSRAM.
  • Flow: Validate size → create stream buffer → launch flash task → feed data from socket → wait for flash task completion → respond {"ok":true} → reboot after 1.5 s delay.
  • Error handling: Every stage has cleanup (esp_ota_abort, vStreamBufferDelete). Stream buffer timeout (10 s) and flash task timeout (30 s) prevent hangs.

Build (CMakeLists.txt)

  • Added app_update to REQUIRES for esp_ota_ops.h.

Frontend (portal.html)

  • New "Firmware Update (OTA)" card in the Actions tab with file picker (.bin only, 4 MB max).
  • Real-time progress bar with percentage and KB counter via XMLHttpRequest.upload.onprogress.
  • Confirmation dialog before flashing. Inputs disabled during upload to prevent double-submit.
  • Status messages: uploading → complete/rebooting, or error with details.

Documentation

  • docs/hardware/README.md: Added battery monitoring section (divider, ADC calibration, TX burst rejection, charging IR offset, OCV curve, slew limiting, sampling cadence) and Wi-Fi section (RSSI selection, stale connection fix, failover, retry timing).
  • spec/0002-edf-export.md: Added §4.3.4 documenting AS11 EDF enum conventions (raw + 1 for most signals, raw + 2 for S.Mask) with OSCAR decrement behavior.