Skip to content

fix(firmware): include sdkconfig.h in edge_processing.h, and pin the subcarrier grid - #1802

Open
clonea1 wants to merge 1 commit into
ruvnet:mainfrom
clonea1:contrib/edge-grid-sdkconfig-include
Open

fix(firmware): include sdkconfig.h in edge_processing.h, and pin the subcarrier grid#1802
clonea1 wants to merge 1 commit into
ruvnet:mainfrom
clonea1:contrib/edge-grid-sdkconfig-include

Conversation

@clonea1

@clonea1 clonea1 commented Sep 4, 2026

Copy link
Copy Markdown
Contributor

Minor follow-up to #1792, which merged with a latent form of the bug it fixed still in it.

The gap

edge_processing.h on main now reads:

#include "esp_err.h"          /* <- no sdkconfig.h */
...
#if CONFIG_SOC_WIFI_HE_SUPPORT
#define EDGE_MAX_SUBCARRIERS  256
#else
#define EDGE_MAX_SUBCARRIERS  128
#endif

The header switches on CONFIG_SOC_WIFI_HE_SUPPORT but never includes the file that defines it. Any translation unit that reaches this header without sdkconfig.h already in scope evaluates an undefined identifier in #if as 0, silently selects the 128-bin pre-HE grid on a 256-bin part, and process_frame() then rejects every frame — which is exactly the failure #1792 was written to remove.

It works today only because the .c files that include it happen to pull sdkconfig.h in first. Nothing enforces that, no warning fires, and the build stays green.

One line fixes it. Also repairs two em-dashes in that comment block that had been round-tripped through cp1252 and read —.

The test

test_edge_subcarrier_grid.c pins EDGE_MAX_SUBCARRIERS against the real header, built twice because the constant is target-conditional and one compilation can only prove one branch. It asserts the grid matches the target, that a 256-bin HE20 frame clears the guard, that a full-width frame still fits one ring slot (so truncation cannot simply relocate into ring_push()'s memcpy clamp), and that the pre-HE size is unchanged so fixing C6 costs S3 no .bss.

Two deliberate choices:

  • The test never re-implements the guard predicate. Restating it would recreate the drift hazard that let this through in the first place.
  • The HE macro comes from a stub sdkconfig.h (test/stubs_he/), never from -D on the command line. -D would compile the right branch regardless and mask the missing include completely — the bug above would pass a green test.

The expectation is driven by a test-only EXPECT_HE marker rather than by CONFIG_SOC_WIFI_HE_SUPPORT itself. That is not incidental: the first version of this test used the real macro to choose its own expectations, so when the header could not see it, neither could the test — it quietly asserted the pre-HE case and passed against the broken header. Running it as a negative control caught that.

On the fuzz harness

The review on #1792 asked for fuzz_edge_enqueue.c's local EDGE_MAX_SUBCARRIERS 128 to be updated so it would exercise the 256 path. Worth flagging that this would not have worked: that define is dead — referenced nowhere in the file. The target exercises the SPSC ring, bounded by EDGE_MAX_IQ_BYTES, and never consults the subcarrier grid.

So it is removed rather than updated, with a comment pointing at where the grid is actually pinned. A stale private copy of a load-bearing constant is precisely how the original bug stayed invisible.

Verification

In espressif/idf:v5.4:

  • against the current main header, the HE build fails three checks, naming the missing include directly
  • with the fix, both builds pass
  • the C6 firmware builds clean

No hardware evidence is claimed or needed here — this is a header-inclusion correctness fix and a host test, both fully determined at compile time. Separately, I still owe #1792 a real C6 boot log confirming the 256-bin premise and the restored pipeline; that will be posted on the closed PR for the record when I next have a node on a cable.

…itches on

Follow-up to the review on ruvnet#1792. The requested regression test turned up two
defects in this PR that the test now guards.

**edge_processing.h never included sdkconfig.h.** The header switches
EDGE_MAX_SUBCARRIERS on CONFIG_SOC_WIFI_HE_SUPPORT but did not include the file
that defines it. Any translation unit that reaches this header without
sdkconfig.h already in scope evaluates an undefined identifier in #if as 0,
silently selects the 128-bin pre-HE grid on a 256-bin part, and reproduces the
exact bug this PR exists to fix -- no error, no warning, a clean build and a
dead edge pipeline. It happens to work today only because the .c files that
include it pull sdkconfig.h in first.

Also repairs mojibake in that comment: two em-dashes had been round-tripped
through cp1252 and read "—".

**The fuzz target carried a dead copy of the constant.** As flagged,
fuzz_edge_enqueue.c defined its own EDGE_MAX_SUBCARRIERS 128 -- but it is
referenced nowhere in that file. It exercises the SPSC ring, bounded by
EDGE_MAX_IQ_BYTES, and never consults the subcarrier grid. So updating it would
not have produced the requested end-to-end coverage; the define was simply dead
and free to drift. Removed, with a comment saying where the grid is pinned
instead.

The real coverage is test_edge_subcarrier_grid.c, built twice against the REAL
header because the constant is target-conditional and one compilation can only
prove one branch. It asserts the grid matches the target, that a 256-bin HE20
frame clears the guard, that a full-width frame still fits one ring slot (so
truncation cannot simply relocate into ring_push's memcpy clamp), and that the
pre-HE size is unchanged so fixing C6 costs S3 no .bss.

Two deliberate choices worth stating. The test never re-implements the guard
predicate -- restating it would recreate precisely the drift hazard that let
this bug through. And the HE macro is supplied by a stub sdkconfig.h
(test/stubs_he/), never by -D on the command line, because -D would compile the
right branch regardless and mask the missing include entirely.

The expectation is driven by a test-only EXPECT_HE marker rather than by
CONFIG_SOC_WIFI_HE_SUPPORT itself. That is not incidental: the first version of
this test used the real macro to choose its own expectations, so when the header
failed to see it the test quietly asserted the pre-HE case and passed against
the broken header. Running it as a negative control caught that.

Verified in espressif/idf:v5.4 -- against the unfixed header the HE build fails
three checks naming the missing include; with the fix both builds pass; and the
C6 firmware still builds clean.

Rebased onto 33a9e90. The one conflict was in test/Makefile against the
test_serial_onboarding targets ruvnet#1902 added; the resolution is additive, both
targets are kept in .PHONY, all and host_tests. The clean: rule now also
removes test_edge_grid_he and test_edge_grid_pre_he, which it never did.

Co-Authored-By: claude-flow <ruv@ruv.net>
@clonea1
clonea1 force-pushed the contrib/edge-grid-sdkconfig-include branch from 21f045f to 4d76da3 Compare September 14, 2026 14:17
@clonea1

clonea1 commented Sep 14, 2026

Copy link
Copy Markdown
Contributor Author

Rebased onto current main (33a9e908), so this is mergeable again.

The one conflict was firmware/esp32-csi-node/test/Makefile, against the
test_serial_onboarding targets #1902 added. The resolution is purely
additive — test_serial_onboarding and test_edge_grid both stay in
.PHONY, all and host_tests, and the host_tests banner now names both.
No upstream line is dropped; git diff main..HEAD on that file is additions
only.

Two defects on this branch fixed while rebasing. The clean: rule never
removed test_edge_grid_he or test_edge_grid_pre_he, so make clean left
both binaries behind. It now removes them, alongside test_serial_onboarding.

Verified, not assumed. make -C firmware/esp32-csi-node/test host_tests
passes end to end on this tree, including both edge-grid builds:

./test_edge_grid_he
  EDGE_MAX_SUBCARRIERS = 256 (expect 256)
  PASS (HE-capable (C6/C5))
./test_edge_grid_pre_he
  EDGE_MAX_SUBCARRIERS = 128 (expect 128)
  PASS (pre-HE (S3))
Host tests passed (ADR-110 + CSI sanitation + vitals + mmwave + thermal + C6 antenna + USB onboarding + edge grid)

Re-ran as a negative control with the #include "sdkconfig.h" removed
from edge_processing.h, to confirm the test is actually live and not
passing vacuously:

  EDGE_MAX_SUBCARRIERS = 128 (expect 256)
  FAIL: CONFIG_SOC_WIFI_HE_SUPPORT is not visible after including edge_processing.h
  FAIL: EDGE_MAX_SUBCARRIERS is 128, expected 256 for a HE-capable (C6/C5) build
  FAIL: a 256-bin HE20 frame is rejected by the guard on an HE part
  3 check(s) FAILED

make clean was confirmed to remove all three binaries afterwards.

🤖 Generated with claude-flow

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant