src.nth.io/

summaryrefslogtreecommitdiff
path: root/sdkconfig.defaults
AgeCommit message (Collapse)AuthorFilesLines
5 dayseth: size EMAC RX DMA pool above the TCP window (1600B x 24)Luke Hoersten1-0/+9
WND=23040 alone regressed: sender fps 20 -> 14, drop-oldest 3x, recv bimodal (min 20ms but 200-450ms stalls; firmware wire_min 6.2Mbps / wire_max 75Mbps, recv_max 253ms). The stalls are RTO recovery: the default EMAC RX pool (20 x 512B = 10KB) is smaller than the 23KB the window now invites in flight, so a full-window burst overruns the RX descriptors, the burst tail is dropped with no dup-ACKs behind it, and the sender waits out a ~200ms min-RTO. 1600B buffers fit one MSS frame per buffer (1 descriptor/packet instead of 3); 24 of them = 38.4KB >= window + slack. ~44KB more internal RAM (491KB free).
5 dayslwip: raise TCP_WND 5760->23040, RECVMBOX 6->32 (window step 1)Luke Hoersten1-10/+14
Instrumented baseline at WND=5760 (kitchen, ~202KB frames, 20fps sender) confirmed the receive window as the dominant throttle: - wire 53.0/58.8 Mbps avg/max = the WND/RTT ceiling, vs ~94 Mbps line rate on the 10/100 PHY - recv_chunk_max pinned at exactly 5760 (drains window-quantized) - recv_calls avg 60/frame; queued_at_body always 0 - sender backpressured 44-61% of frames, drop-oldest 17-54/window - meanwhile pend_age avg 58us: decode idles, receive starves it Expected: recv_avg ~30ms -> ~20ms, wire toward line rate, sender bp%/drop-oldest down, g2g 73ms -> ~60ms. Tripwires (revert if hit): pend_age or recv_dropped_oldest growing window-over-window, g2g regressing. RECVMBOX scales with the window: 23040 = 16 segments in flight, a 6-deep mbox would silently become the new cap.
2026-06-20firmware: keep TCP recv window at IDF defaults (5760) — bigger window ↵Luke Hoersten1-11/+11
regressed g2g Step-2 experiment from the iterative tuning plan: bumped CONFIG_LWIP_TCP_WND_DEFAULT 5760 → 65535 (and matching SND_BUF + RECVMBOX 6 → 16) under the hypothesis that the 4×MSS window throttled receive throughput. Instrumentation showed the window WAS the per-call throttle (recv_chunk_max was exactly 5760 before, grew to 33-60KB after). But end-to-end got dramatically worse: - g2g exploded from ~100ms steady to 17 SECONDS growing unbounded (1964 → 6010 → 10632 → 14725 → 17889ms over consecutive windows). - painted dropped from ~24fps to 15-20fps. - recv_avg rose from 32ms steady to 50-90ms with frequent 230-500ms outliers (the retransmit / RTO signature). - chunk_min fell to 61 bytes, chunk_avg dropped from 3415 to ~2200 — fragmentation as lwIP RX struggled with the bigger bursts. Root cause: the single recv→decode→paint task serializes; the kernel buffer fills against ANY window during the ~6ms decode+paint, but with 65535 bytes available the sender pumps ~45 segments before stopping vs ~4 before. That overruns the lwIP RX path (RECVMBOX=16 mailboxes, default pbuf pool) → drops → retransmits → multi-frame stalls. And Scrypted has no way to skip-oldest on the kernel queue, so frames just pile up on the wire / in the ESP32 kernel buffer → g2g grows linearly with time. Lesson: window size isn't the right knob until the receiver can drain at line rate independent of decode+paint. Next iteration: split recv into its own FreeRTOS task with a 1-deep latest-frame slot to the decode/paint task — only then revisit window.
2026-06-20firmware: OTA firmware updates via POST /firmware + rollbackLuke Hoersten1-0/+6
Streams the raw .bin to the inactive ota_0/ota_1 slot via esp_ota_*, flips otadata, replies 200, reboots after 500 ms. Single-shot guarded by atomic_flag (409 on concurrent). CONFIG_BOOTLOADER_APP_ROLLBACK_ENABLE armed: new images boot pending-verify and ota_arm_healthy_timer marks them valid after 30 s of healthy uptime; otherwise the bootloader reverts on next reset. /state gains ota_state.
2026-06-20Revert "firmware: revert lwIP window bump — broke the stream accept path"Luke Hoersten1-8/+12
This reverts commit 6e36cbc14a06b8c0e15bfe78afb8adb43f8909ba.
2026-06-20firmware: revert lwIP window bump — broke the stream accept pathLuke Hoersten1-12/+8
Hypothesis confirmed by the symptoms: with WND_DEFAULT=65535, SND_BUF=65535, RECVMBOX=16, the firmware boots fine and the stream server logs "listening on tcp/:81", but Scrypted's first SYN never produces a "client connected from ..." entry. The script-side socket stays stuck in pending-connect — neither a connect nor an error event fires — and every ffmpeg-emitted frame gets dropped. Root cause is almost certainly lwIP's PBUF / MEMP pools not being scaled to back the 64KB windows on multiple sockets (httpd has 4, stream has 1, that's 5 × 64KB = 320KB of buffer commitment from a heap that's also feeding PSRAM-backed framebuffers, JPEG scratch, and Ethernet DMA). lwIP doesn't surface the allocation failure as an accept error — it just refuses to complete the handshake. Backing off the changes for now. The streaming path already hits ~14-20 fps at our measured 5.3 MB/s ingest ceiling under the lwIP defaults — that's plenty above the 12-fps goal. Revisit window tuning later by also bumping CONFIG_LWIP_PBUF_POOL_SIZE and MEMP_NUM_TCP_PCB so the buffer commitment is actually allocatable. Keeping the FIONREAD skip in stream_server (it's orthogonal to lwIP buffer sizes and only helps).
2026-06-20"paint the latest, drop the rest" — FIONREAD skip + Scrypted ↵Luke Hoersten1-7/+12
backpressure-blind + lwIP TCP window bump Three changes that work together to make the stream "always paint what's freshest, never sit on a stale frame": #1 — Firmware FIONREAD skip in stream_server Right after the body of frame N comes off the wire (and before we unlock the decoder + spend ~6ms on decode + paint), check the kernel receive buffer with ioctl(FIONREAD). If at least one more header (8 bytes) is queued, frame N is no longer the freshest possible — skip its decode + paint and loop back to read frame N+1. The TCP recv cost is unavoidable (bytes still have to cross the wire) but the decoder + paint cost is saved on every superseded frame. Glass-to-glass latency on the latest frame drops by however many frames had backed up. #2 — Scrypted: keep writing past kernel-buffer backpressure Previously: when sock.write() returned false we dropped the next ffmpeg frame at source. New: we keep writing through. Node buffers internally; under our load (~16 MB/s ffmpeg → ~5-7 MB/s firmware) the buffer rarely exceeds a frame or two. With the firmware now silently skipping decode on backed-up frames (#1), excess frames get shed for free on the device side. Scrypted's job is just to hand the firmware the freshest bytes as fast as possible. socketBackpressured is still tracked for the diagnostic log. #3 — lwIP TCP window bump (revisiting earlier regression) The previous attempt at LWIP_TCP_WND_DEFAULT=32k regressed under HTTP because every /frame opened a fresh socket and we paid the slow-start cost repeatedly. The streaming pivot eliminated that: the socket is long-lived, slow-start runs exactly once, then we ride the full window for the rest of the session. Bumping to 65535 (max for stock lwIP), SND_BUF to match, RECVMBOX to 16, SACK on. Expected: recv throughput ceiling moves up from ~5.3 MB/s, which directly raises the fps ceiling (recv is currently 37ms of the 43ms per-frame total).
2026-06-15sdkconfig: revert TCP window bump — net-negative on /frame pathLuke Hoersten1-0/+8
Bumping WND/SND_BUF to 32 KiB + RECVMBOX to 16 + SACK on regressed ttfb from ~350µs to ~1.2ms across every frame and introduced periodic 250+ms body stalls (TCP retransmit on the larger window). Mean body unchanged. Reason: Scrypted's per-frame fetch() opens a fresh TCP socket, so the larger receive window just slows slow-start on every new connection rather than helping. Reverted to lwIP defaults. The real fix is HTTP keep-alive on the Scrypted side; will revisit window tuning once persistent connections are in place. README backlog updated with what we learned.
2026-06-14M3 WIP: DSI bring-up + state machine + identity overlayLuke Hoersten1-1/+6
DSI / panel: - LDO_VO3 acquired at 2500 mV (VDD_MIPI_DPHY) — without this the PHY PLL busy-waits forever inside esp_lcd_new_dsi_bus. - PSRAM bumped to 200 MHz (CONFIG_IDF_EXPERIMENTAL_FEATURES) to keep the DPI fed without underruns. - Pi-7"-style 800x480 panel init (REG_POWERON, PORTA/PORTB/PWM/PORTC) + 16-bit RGB565 framebuffer with portrait-mode rotation buffer in PSRAM. Currently shows garbled output — known issue, next commit switches to the TC358762-bridge-aware init sequence. State + UX: - viewport_state simplified to AWAKE/ASLEEP only; "unconfigured" is a flag, not a state. Tap always toggles; content choice (identity vs frame) is driven by the configured flag. - BOOT button arms a 15 s identity overlay via local_screens_overlay; expired callback returns to prior state. - Boot-done indicator: two backlight flashes (no usable LED GPIO on the Waveshare board — GPIO 35 BOOT is shared with EMAC TXD1). - Best-effort subsystem init in app_main; display init deferred to its own task so a panel hang can't block networking. Display test pattern: solid R/G/B for 2 s each on boot to characterize the garble independent of text rendering.
2026-06-13M1: Ethernet bring-upLuke Hoersten1-0/+4
net_eth module brings up the Waveshare ESP32-P4-ETH-POE Ethernet interface (internal EMAC + IP101GRI PHY) and waits for a DHCP lease. Pin map confirmed via Waveshare wiki + ESPHome's working config: MDC=31, MDIO=52, REF_CLK=50 (CLK_EXT_IN from PHY) TX_EN=49, TXD0=34, TXD1=35 CRS_DV=28, RXD0=30, RXD1=29 PHY reset/enable=51, PHY addr=1 app_main initializes NVS, netif, the default event loop, then starts the Ethernet driver and waits up to 30s for an IP. On success it logs the IP; on timeout it logs a warning and proceeds (the driver keeps retrying in the background). sdkconfig: declare 16 MB flash so partitions.csv (6.1 MB) fits. main/CMakeLists.txt: explicit REQUIRES esp_eth esp_event esp_netif nvs_flash. Acceptance (per M1 in the impl guide): device gets a DHCP lease over Ethernet and prints its IP. Build is clean against ESP-IDF 5.4 for target esp32p4.
2026-06-13Initial Scrypted Viewport scaffoldLuke Hoersten1-0/+31
Plain ESP-IDF project targeting Waveshare ESP32-P4-ETH-POE with a 5" 800x480 MIPI-DSI capacitive touch panel. Stub app_main with TODOs for Ethernet, mDNS, HTTP API (/health, /config, /frame, /sleep, /brightness), JPEG decode, and touch callback delivery per the v1 spec in README.md.