| Age | Commit message (Collapse) | Author |
|
|
|
|
|
BOOT screen now shows QR code for AP WiFi + SSID + status text.
ERROR screen reorganized with QR code at top, NO UPSTREAM banner,
setup URL, and 'Scan QR to connect' hint.
|
|
Previously the display stayed at BOOT forever when WiFi was configured
but unreachable. Now uses a total retry counter (10 attempts) and
transitions to DISPLAY_ERROR with setup URL when all retries fail.
|
|
- Remove touchscreen WiFi setup (touch.c, keyboard.c, wifi_setup.c from build)
- Remove offscreen buffer and landscape rotation from axs15231b driver
- Add /setup HTML page with WiFi scan/connect via captive portal
- Add /wifi/scan, /wifi/connect, /wifi/status HTTP endpoints
- Display shows SETUP_PENDING (QR + SSID + setup URL) when unconfigured
- Display shows ERROR with setup URL when upstream is down
- All 101 unit tests pass, builds and flashes to Board C
|
|
The framebuffer was using s_width as row stride, but s_width changes
after rotation (320→480). This caused buffer overflows and black screen
in landscape mode. Now uses fixed stride=480 with 480*480*2 allocation.
|
|
highlight feedback
- Render functions use axs15231b_get_width/height() instead of hardcoded coords
- render_wifi_setup_list: dynamic item widths, screen-relative cancel button
- render_wifi_setup_password: dynamic field/eye/keyboard from kb_get_layout()
- render_wifi_setup_result: centered on screen, dynamic button placement
- handle_wifi_setup_touch: matching dynamic hit areas, highlight_rect() feedback
- Rotation lifecycle: enter_wifi_setup_rotation/exit wired into all entry/exit paths
- display_enter_wifi_setup + READY/ERROR touch + SUCCESS auto-return + LIST cancel
- Added kb_result_t typedef to keyboard.h (was only in .c)
- Fixed test_keyboard.c: use computed offsets from kb_layout_t defaults
- All 101 unit tests pass (touch:19, keyboard:46, wifi_setup:36)
|
|
- KB_START_Y was 310 but keyboard rendered at y=70 — touch hits never matched
- Row 3 simplified to 3 keys: [layer/shift, space, done]
- All 362 unit tests pass
|
|
- Check network_count == 0 before modulo operation
- Disconnect STA before WiFi scan to avoid 'STA is connecting, scan not allowed'
- Add error handling for failed WiFi scans
- This fixes the crash/restart loop on Board C with no configured WiFi
|
|
- Add DISPLAY_WIFI_SETUP state with full rendering pipeline
- Touch init on display_init(), touch polling in display task
- WiFi scan triggered on setup entry, sorted AP list
- On-screen keyboard for password entry
- Connect result via WiFi event notifications
- Setup button on READY and ERROR screens
- Auto-enter WiFi setup on first boot with no credentials
- Save new WiFi credentials to SPIFFS config.json
|
|
|
|
|
|
|
|
- Periodic refresh: wallet balance + client count every 5s from main loop
- display_notify_payment() API: passes payment amount and time allotment
- tollgate_api.c: triggers PAYMENT_RECEIVED state after wallet receive
- AP station events update client count in real-time
- Config data (price, mint) passed to display during boot phase
- Payment screen shows actual sats paid and time purchased
- Updated DISPLAY_UI_PLAN.md with state-sync audit and checklist
|
|
BOOT (TollGate starting), READY (QR cycling + price + balance),
ERROR (no upstream) all confirmed working on JC3248W535C_I_Y.
|
|
- BOOT screen: centered title + WiFi status line
- READY screen: QR cycling (WiFi/Portal), price, mint domain,
wallet balance (color-coded), client count
- PAYMENT screen: green banner 'ACCESS GRANTED', paid amount, time
- ERROR screen: red banner 'NO UPSTREAM', guidance, AP reassurance
- Enhanced display_update() API with mint_url, price, wifi_status
- Extract domain helper for clean mint URL display
- Render interval: 2s (reduced SPI load)
- Color palette: cyan, yellow, orange, green, red, dim gray
- WiFi events update display status and trigger ERROR state
- Board C now on /dev/ttyACM3
|
|
Root cause: missing RAMWR (0x2C) command before pixel data transfer.
Without RAMWR, the display didn't know to write data into the RAM window
set by CASET/RASET, causing wrapping and double-vision artifacts.
- Add qspi_write_command(RAMWR) before pixel data in flush()
- Use internal DMA byte-swap buffer (MALLOC_CAP_DMA) for correct colors
- Reduce font scale: TollGate scale 2, starting... scale 1
- Centered text positions for 320x480 portrait
Display now shows: blue TollGate + yellow starting... on black
|
|
- Allocate 4KB internal DMA buffer (MALLOC_CAP_DMA) for pixel byte-swap
- Byte-swap from PSRAM framebuffer into DMA buffer in 2048-pixel chunks
- Matches ArduinoGFX approach: separate DMA buffer avoids PSRAM cache issues
- Restore render-on-change logic (only redraw on state/QR mode change)
- Use saturated colors: cyan 0x07FF for title, yellow 0xFFE0 for subtitle
- Remove debug flush log message
|
|
- DISPLAY_FIX_PLAN.md: root cause analysis, byte-order mismatch,
PSRAM cache coherency issue, reference implementations studied
- axs15231b.c: QSPI protocol rewrite with correct ArduinoGFX framing
(cmd=0x02 for regs, cmd=0x32/addr=0x003C00 for pixels)
- display.c: portrait centering for 320x480, render-on-change logic
- Makefile: Board C support (flash-c, lock-c)
- axs15231b.h: QSPI command/address constants
Display shows recognizable text, colors wrong due to byte-swap
|
|
|
|
- Lock files in physical-router-test-automation/locks/
- Each board has its own lock: board-a.lock, board-b.lock, board-c.lock
- All hardware-touching targets (flash, monitor, erase-nvs, reset, serial-log, tests) require the corresponding board lock
- Read-only targets (build, cvm-pubkey, lock-status) work without lock
- Fixed port assignments: A=ACM1, B=ACM2, C=ACM0
- Router hardware lock kept separate (for OpenWRT operations)
|
|
The JC3248W535 uses Quad SPI with 4 data lines (D0-D3), not standard
SPI. The previous driver only wired D0 (MOSI), causing garbled pixel
data while register writes (single SPI) worked partially.
Changes:
- Wire all 4 data pins in spi_bus_config_t (data0-data3)
- Set SPI_DEVICE_HALFDUPLEX for QSPI output mode
- Send pixel data with SPI_TRANS_MODE_QIO flag for 4-bit parallel transfer
- Register writes (init, CASET, RASET, RAMWR cmd) remain single SPI
|
|
|
|
Components were untracked on master. Adding to feature/display-fix
branch so they are version-controlled before making QSPI fixes.
|
|
- Fix buffer size for ["EVENT",...] wrapper: was 8+event_len+1,
needed 10+event_len+2 (9 char prefix + ] + null). snprintf was
truncating the closing bracket, causing relay JSON parse errors.
- Add write loop in ws_send_text for large payloads that don't fit
in a single esp_tls_conn_write call.
- Change STA auth threshold from WPA2_PSK to WPA3_PSK for
compatibility with WPA3 access points.
- Announcements now successfully stored on relay.primal.net!
|
|
- Full CVM server: persistent WS relay listener, kind 25910 subscription
- MCP protocol handlers: initialize, tools/list, tools/call, ping
- 10 MCP tools: get_config, set_config, get_balance, wallet_send,
get_sessions, get_usage, set_payout, set_metric, set_price, wallet_melt
- CEP-6 announcements via WS (kinds 11316, 11317, 10002)
- Auth check: owner npub only
- Fix: WebSocket client-to-server frame masking (RFC 6455 requirement)
- Fix: Raw event JSON in EVENT wrapper (no re-parsing that breaks sig)
- SNTP init after STA gets IP
- 282 unit tests passing (61 CVM + 60 MCP handler + 161 existing)
- Integration test scaffold: tests/integration/test-cvm.mjs
|
|
- esp_wifi_set_mac requires WIFI_MODE_APSTA set first (fixes Board B crash)
- Start services immediately when no STA network is configured
(standalone TollGate without upstream WiFi)
|
|
- Move integration tests (api, network, phase2, smoke) to tests/integration/
- Move Playwright specs (captive-portal, interop-happy-path) to tests/e2e/
- Move playwright.config.mjs to tests/e2e/
- Fix hardcoded IP fallbacks: 192.168.4.1 → 10.192.45.1
- Add test-reset-auth.mjs: reset→pay→allow→revoke→block cycle
- Add test-session-expiry.mjs: pay→wait 65s→verify blocked (slow test)
- Add test-dns-firewall.mjs: DNS hijack/forward + per-client NAT filter
- Update Makefile with test-unit, test-integration, test-e2e, test-all targets
- Update package.json scripts for new paths
- Fix Playwright video: retain-on-failure instead of always-on
- Update AGENTS.md: per-client NAT filter description
- Update CHECKLIST.md: mark completed items, add Board B identity
- Board B nsec: 9af47906... → SSID TollGate-b96d80, AP IP 10.185.47.1
- 186 unit tests passing
|
|
- Add lwip_tollgate_hooks.h defining LWIP_HOOK_IP4_CANFORWARD macro
- Inject hook into lwIP build via CMakeLists.txt ESP_IDF_LWIP_HOOK_FILENAME
- Filter forwarded packets by source IP against firewall allowed list
- Only filter packets from AP subnet (10.192.45.0/24), allow all others
- Fix byte order bug: use network byte order for firewall_is_client_allowed
- NAT always enabled, removed global NAT toggle functions
- Remove spent-secret tracking from session.c (mint is authority)
- Remove unused get_ap_netif() function
- Reduce API server stack from 32KB to 16KB (fixes ESP_ERR_HTTPD_TASK)
- Add esp_random.h stub for unit tests
- All 186 unit tests passing
- Verified on hardware: block->pay->allow->revoke->block E2E works
|
|
metric defaults to milliseconds, fix sys_evt stack overflow
|
|
- interop-happy-path.spec.mjs: 11 ESP32 TollGate tests + 7 ESP32↔OpenWRT interop tests
- API discovery, whoami, usage, invalid/spent token rejection
- Browser portal UI: branding, form elements, captcha detection URIs
- Full payment flow screenshots (portal → token → connected → browsing)
- Side-by-side ESP32 vs OpenWRT comparison screenshot
- playwright.config.mjs: video on, screenshot on, 120s timeout
- package.json: test:happy-path, test:interop, test:playwright scripts
|
|
server skeleton
- mcp_handler.c/h: 4 tools (get_config, set_config, get_balance, wallet_send)
- nip04.c/h: AES-256-CBC + ECDH with 0x02 compressed pubkey prefix
- Fixed IV copy bug: mbedTLS AES-CBC modifies IV in-place
- Base64 encode/decode for ciphertext transport
- PKCS7 padding
- cvm_server.c/h: Nostr DM listener with FreeRTOS task
- config: cvm_enabled, cvm_relays fields
- 156 total tests passing across 10 test binaries
|
|
- session_create_bytes() + session_add_bytes() for bytes-metric sessions
- session_is_expired() dispatches on config metric (bytes vs milliseconds)
- cashu_calculate_allotment() unified dispatcher for both metrics
- tollgate_api discovery/usage/session_event use configured metric
- config: metric field defaults to 'bytes', step_size_bytes=22020096 (21MB)
- 14 new unit tests (148 total passing)
- ASSERT_EQ_UINT64 macro added to test framework
|
|
- New lnurl_pay.c/h: LNURL-pay protocol (GET .well-known/lnurlp + callback)
- New lightning_payout.c/h: threshold-based auto-payout with multi-recipient split
- Extended nucula_wallet bridge with nucula_wallet_melt() (NUT-05)
- Config: payout section with multi-mint, multi-recipient, fee_tolerance
- Default: enabled, TollGate@coinos.io, min_payout=128, min_balance=64
- 18 new unit tests (all passing), 134 total
|
|
- New tollgate_client.c/h: detect upstream TollGate (kind=10021),
auto-pay via nucula wallet, session monitoring with 20% renewal
- State machine: IDLE→DETECTING→NEEDS_PAY→PAYING→PAID→RENEWING
- Blocking: upstream payment before local services start
- Synchronous wallet init (was async task)
- Client config: enabled, steps_to_buy, renewal_threshold_pct
- Updated PLAN.md with Phases 4-7 (client, payout, bytes, CVM)
- Updated CHECKLIST.md with all new phase items
- 30 new unit tests (all passing), 116 total
|
|
- interop/Makefile: 10 targets for 4 test scenarios
- interop-status: show device status for all devices
- interop-laptop-esp32: laptop pays ESP32 with V3 token
- interop-laptop-openwrt: laptop pays OpenWRT with V4 token
- interop-openwrt-esp32: OpenWRT daemon auto-pays ESP32 upstream
- interop-esp32-esp32: cross-board payment (needs Board B)
- interop-setup/cleanup: mint alignment + wallet funding
- INTEROP_PLAN.md: full test plan with scenarios and token format details
- PROGRESS.md: checklist of setup/interop tasks
- AGENTS.md: standing instructions for interop testing
- routers.env.example: device config template
- Verified interop-status against real hardware (OpenWRT + ESP32 Board A)
|
|
- Expand esp_http_client.h stub: full config struct + method enum + init/perform/cleanup
- Add portTICK_PERIOD_MS + esp_err_to_name to stubs
- session.c: reject duplicate spent secrets in session_create (double-spend protection)
- .gitignore: add test binaries
|
|
serialization
|
|
determinism
|
|
tests (11/11 pass)
- Add AGENTS.md: full project context + mandatory testing rules for AI sessions
- Add tests/unit/ with host-compiled C unit test infrastructure
- Clean stubs approach: ESP-IDF type stubs in tests/unit/stubs/, no source modifications
- Fix geohash.c bit extraction bug (3-byte span) found by unit tests
- test_geohash: 11/11 passing with reference vectors (Munich, NYC, origin, boundaries)
|
|
- Add identity.c/h: HMAC-SHA512 derivation from nsec → npub, STA/AP MAC, SSID, AP IP
- Add nostr_event.c/h: NIP-01 event serialization + Schnorr signing (BIP-340)
- Add geohash.c/h: lat/lon to geohash encoding
- Add wifistr.c/h: kind 38787 event builder + WebSocket publish to Nostr relays
- Update config.c/h: nsec-based identity, Nostr relay/geo config, remove static SSID/IP
- Replace custom mbedTLS wallet with nucula library (libsecp256k1)
- Remove wallet.c/h, wallet_persist.c/h (replaced by nucula_lib component)
- Verified on Board A: derived SSID, captive portal, payment, wallet, wifistr publish
|
|
+ PSRAM
- wallet.c/h: secp256k1 ECP primitives (hash_to_curve, scalar_mul, point_add)
- wallet_persist.c/h: SPIFFS persistence with threshold-based write protection
- Fee accounting for swap (input_fee_ppk from /v1/keysets)
- Keyset fetch via /v1/keysets (586 bytes vs 21KB for /v1/keys)
- Wallet API: GET /wallet, POST /wallet/swap, POST /wallet/send
- Payment proofs auto-stored to wallet + persisted on SPIFFS
- PSRAM enabled for large allocations (ESP32-S3 has 8MB)
- Wallet init deferred to dedicated task (avoids sys_evt stack overflow)
- Cashu proof ID buffer size fixed (66 hex chars, not 16)
- HTTP client: added fetch_headers() call for proper response handling
- persist_threshold_sats config parameter (default: 1 sat)
|
|
route management
|
|
- Add esp_netif_set_dns_info() on AP interface so DHCP advertises
AP as DNS server to clients (fixes captive portal on GrapheneOS)
- Embed price and mint URL directly in portal HTML via server-side
template substitution (no JavaScript fetch to :2121 needed)
- Move supported mints section below the token input field
- Add Playwright tests: no unresolved placeholders, embedded mint/price,
DOM order verification (14/14 passing)
|
|
- Derive unique SSID (TollGate-{MAC4}{MAC5}) and AP IP (10.{b5}.{subnet}.1)
from factory MAC — boards no longer conflict
- Board A: TollGate-377C @ 10.55.85.1, Board B: TollGate-5050 @ 10.80.10.1
- Captive portal detection URIs return 200 with portal HTML (matching
esp32-mesh working approach) instead of 302 redirect
- Dynamic AP IP in portal HTML via __AP_IP__ template substitution
- Supported mints section in portal page (shows mint URL, tap to copy)
- Fixed mint URL to testnut.cashu.space (was stale in SPIFFS)
- DoT reject server on port 853 for DNS-over-TLS fallback
- DNS hijack: NXDOMAIN for all non-A queries, no forwarding for unauthed
- Playwright tests updated for 200 response on detection URIs
- Phase 2 test suite: 20/21 pass (test 22 expiry ping route issue)
- Tests 25-27 deferred to Phase 3 (Board B as second client)
|
|
- Updated from Phase 1 tests to Phase 2 (302 redirects, Cashu token input)
- Test captive detection URIs return 302 (using request API)
- Test invalid token via request API (no CORS issues)
- Tests: portal branding, price, token input, pay button, detection redirects
- Tests: whoami, usage, API advertisement, invalid token
|
|
- Test 22: session expires after allotment, internet blocked, usage returns -1/-1
- Test 23: second token renews session, internet restored
- Test 18 fix: add route through TollGate before ping, pipe sudo password
- All 21 Phase 2 tests pass
|
|
- Add DoT reject server on port 853 (TCP RST forces DNS-over-TLS fallback)
- DNS hijack returns NXDOMAIN for all non-A query types (no forwarding for unauthed)
- Shorter TTL on hijack responses (10s) for faster captive detection
- Explicit 302 redirect handlers for /generate_204, /hotspot-detect.html, etc.
- HTTP and DNS request logging for debugging captive detection
- Per-MAC tracking in firewall (find_by_mac, get_mac_for_ip with ARP fallback)
- Session MAC tracking (session_find_by_mac)
- Phase 2 test 18: add route through TollGate before ping test
- All 17 Phase 2 tests pass (15-21 + whoami + portal form)
|
|
|
|
response)
- cashu.c: dynamic json_buf sizing (was 2048 stack, now heap based on token length)
- cashu.c: strip trailing newline/CR from token input (cashu CLI appends 'Balance: 0 sat')
- cashu.c: esp_crt_bundle_attach for HTTPS to mint API
- cashu.c: esp_http_client_open/write/fetch_headers/read pattern for HTTPS POST
- cashu.c: remove debug b64url_decode logging
- tollgate_api.c: loop httpd_req_recv for full body (was single call, missed TCP segments)
- tollgate_api.c: stack_size=32768 for TLS in httpd handler
- config.c: fix default mint URL from nofee.testnut to testnut.cashu.space
- CMakeLists.txt: add esp-tls dependency for cert bundle
- CHECKLIST.md: updated with infrastructure status and TDD plan
Known issue: device reboots after checkstate returns 966 bytes with status=200.
Crash likely in post-response processing (JSON parse or session create).
|
|
- tollgate_api.c: increase httpd stack_size to 16384 (was default 4096)
- cashu.c: heap-allocate b64, json_buf, post_body, resp_buf instead of stack
- cashu.c: proper free() on all error paths
- Makefile: replace Go-based tokens target with nutshell wallet targets
- Makefile: add wallet-setup, wallet-info, wallet-balance, mint-token, send-token
|
|
- Add cashu.c/h: Cashu token decode (cashuA/base64url), proof state check via mint API, allotment calculator
- Add session.c/h: time-based session management with allotment/expiry, spent secret tracking
- Add tollgate_api.c/h: HTTP server on :2121 with GET / (kind=10021 discovery), POST / (payment processing), /usage, /whoami
- Update captive portal HTML: replace Grant Free Access with Cashu token paste form + Pay & Connect button
- Update tollgate_main.c: wire in session manager, TollGate API, 1s session tick loop
- Add tests/phase2.mjs: Phase 2 test suite (discovery, invalid token, wrong mint, valid payment)
- Update CHECKLIST.md: reflect Phase 1 complete, Phase 2 in progress with known bugs
Known issues (not yet flashed):
- Stack overflow crash in httpd POST handler (need stack_size=16384 + heap allocations)
- cashu_decode_token uses 2KB stack buffer (needs heap alloc)
- Mint URL should be testnut.cashu.space (nofee.testnut has API compat issues)
|
|
- Fix WiFi init order: netif creation before esp_wifi_init, set mode before set_config
- Replace broken netif input filter with NAPT on/off per authentication state
- NAPT disabled by default, enabled when client granted, disabled on revoke
- Fix test helpers: use -I wlp59s0 for ping, handle nslookup exit code 1
- All 20 API tests pass, all 6 smoke tests pass
|
|
|