feat(calibration): guided NORVI button calibration wizard - #182
Conversation
Native Test Coverage
Report from native unit tests (ASan + gcov). |
✅
|
| Descriptor | Linter | Files | Fixed | Errors | Max errors | Warnings | Elapsed time |
|---|---|---|---|---|---|---|---|
| ✅ ACTION | actionlint | 8 | 0 | 0 | 0.75s | ||
| ✅ BASH | bash-exec | 1 | 0 | 0 | 0.59s | ||
| ✅ BASH | shellcheck | 1 | 0 | 0 | 0.73s | ||
| ✅ BASH | shfmt | 1 | 0 | 0 | 0.01s | ||
| ✅ C | clang-format | 1 | 0 | 0 | 0.06s | ||
| ✅ C | cppcheck | 1 | 0 | 0 | 0.03s | ||
| ✅ C | cpplint | 1 | 0 | 0 | 0.34s | ||
| ✅ CPP | clang-format | 82 | 0 | 0 | 0.66s | ||
| ✅ CPP | cppcheck | 82 | 0 | 0 | 6.14s | ||
| ✅ CPP | cpplint | 82 | 0 | 0 | 7.19s | ||
| ✅ EDITORCONFIG | editorconfig-checker | 261 | 0 | 0 | 0.73s | ||
| ✅ JSON | jsonlint | 6 | 0 | 0 | 0.14s | ||
| ✅ JSON | v8r | 6 | 0 | 0 | 4.36s | ||
| markdownlint | 103 | 3 | 0 | 4.0s | |||
| ✅ YAML | yamllint | 25 | 0 | 0 | 0.99s |
Detailed Issues
⚠️ MARKDOWN / markdownlint - 3 errors
.opencode/skills/web-ui/SKILL.md:34 error MD028/no-blanks-blockquote Blank line inside blockquote
docs/superpowers/plans/2026-08-10-olimex-c6-local-ui-implementation.md:106:401 error MD013/line-length Line length [Expected: 400; Actual: 452]
docs/superpowers/plans/2026-08-16-norvi-button-calibration.md:7:401 error MD013/line-length Line length [Expected: 400; Actual: 412]
Notices
MAKEFILE, MAKEFILE_CHECKMAKE, MARKDOWN_MARKDOWN_LINK_CHECK. See Removed linters to find their replacements.
See detailed reports in MegaLinter artifacts
Your project could benefit from a custom flavor, which would allow you to run only the linters you need, and thus improve runtime performances. (Skip this info by defining FLAVOR_SUGGESTIONS: false)
- Documentation: Custom Flavors
- Command:
npx mega-linter-runner@10.0.0 --custom-flavor-setup --custom-flavor-linters ACTION_ACTIONLINT,BASH_EXEC,BASH_SHELLCHECK,BASH_SHFMT,C_CPPCHECK,C_CPPLINT,C_CLANG_FORMAT,CPP_CPPCHECK,CPP_CPPLINT,CPP_CLANG_FORMAT,EDITORCONFIG_EDITORCONFIG_CHECKER,JSON_JSONLINT,JSON_V8R,MARKDOWN_MARKDOWNLINT,YAML_YAMLLINT

Show us your support by starring ⭐ the repository
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 658ecc68b8
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
Native Test Coverage
Report from native unit tests (ASan + gcov). |
Native Test Coverage
Report from native unit tests (ASan + gcov). |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: f8f4d5f932
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
The wait-phase predicate required a reading strictly above previousLevel + MIN_LEVEL_GAP and also accepted readings far below it. Hardware whose adjacent button levels sit exactly 100 ADC counts apart could therefore never advance past the wait phase, while releasing a button (ADC dropping back toward the resting level) was mistaken for the next button level and produced non-ascending thresholds. Accept only readings at least MIN_LEVEL_GAP above the previous level (inclusive), matching the validation boundary in computeThresholds(). Addresses PR #182 review comments.
The sample phase collected all 20 readings within a few loop iterations because it had no time gate, so correlated noise or the transient right after a button press could be averaged into a calibrated level. Gate each sample on SAMPLE_INTERVAL_MS so the readings span the documented one-second window. Addresses PR #182 review comment.
The new thresholds were written into the live Settings before ConfigManager::save() ran, so a failed save left the failed values active in memory even though calibration reported ERROR. Snapshot the previous thresholds, commit the new ones, and restore the snapshot when save() fails so the old thresholds stay untouched. Addresses PR #182 review comment.
When calibration reaches DONE while Button 3 is still held, isActive() immediately becomes false and the handler starts processing that same held press, firing the S3 callback (which can toggle the pool pump) before the UI can report completion. Arm callback suppression on the calibration-finish transition, reset press tracking, and keep callbacks disabled until the ADC returns to the no-press range for the debounce interval. Addresses PR #182 review comment.
The LittleFS web assets are shared across all build environments, but the /api/calibrate/* routes are compiled out unless NORVI_AE01_R is defined. Report NORVI support in /api/status and hide the calibration button on targets without it, so the wizard is no longer exposed with a start request that always 404s. Addresses PR #182 review comment.
Native Test Coverage
Report from native unit tests (ASan + gcov). |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 8b918b7218
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
The sample phase accumulated all 20 readings without validating them, so releasing the button or a large ADC transition mid-window produced a mixed average (e.g. ten S1 readings at 3400 plus ten resting readings at 2700 yield 3050) that could pass the final checks and misclassify the real button level. Reject any sample that leaves the stability window around the level accepted in the wait phase and restart the measurement. Addresses PR #182 review comment.
After a page reload or a lost start response the calibration stays active on the device, so the start request returns 409 and the UI only alerted. Reopen the wizard modal and resume polling on 409 so the user can watch progress or cancel the orphaned calibration instead of being stuck with disabled front-panel buttons. Addresses PR #182 review comment.
Native Test Coverage
Report from native unit tests (ASan + gcov). |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 5643c97bb8
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
Starting the wizard twice before the first request completed created two polling intervals, overwriting calibPollTimer and leaving an orphan loop that kept calling loadConfig() after DONE. Clear an existing timer before starting a new poll loop. Addresses PR #182 review comment.
When the session expired, handleAuthentication() serves the login page with HTTP 200, so the cancel request was treated as success and the modal closed while calibration kept running with suppressed callbacks. Verify an actual API response (non-HTML content type) before closing and surface re-authentication instead. Addresses PR #182 review comment.
Native Test Coverage
Report from native unit tests (ASan + gcov). |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 94a2357f29
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
Samples may drift within the stability window and yield an average below previousLevel + MIN_LEVEL_GAP, which later failed the sanity check in computeThresholds() and aborted the entire wizard. Validate the averaged level against the previous measurement before storing it; on failure the step retries instead of advancing. The value is only written to the status once validation passes. Addresses PR #182 review comment.
handleAuthentication() serves the login page with HTTP 200 on session expiry, so the start request was treated as success and the wizard opened while the device stayed idle. Verify the response is an actual calibration API response before opening, and stop the status poller instead of parsing the login HTML as JSON. Addresses PR #182 review comment.
Native Test Coverage
Report from native unit tests (ASan + gcov). |
Make the wizard's flow obvious from the moment it opens: - Always-visible overview line: four measurements run one after another, keep the button pressed until its step turns green. - 'Next:' line under the current instruction names the upcoming step (hold Button 1/2/3, then auto-computed thresholds). - Start button hint on the dashboard states the expected sequence and duration. - Keep every status text readable: new messages only replace the current one after a 1.2 s minimum display time. - DONE now shows a brief green success state (1.5 s) before the modal closes, instead of closing instantly. - Live ADC meter bar, numbered step progress with checkmarks, and a color-coded waiting/measuring/retry state chip (previous iteration).
Native Test Coverage
Report from native unit tests (ASan + gcov). |
Summary
Adds a guided calibration wizard for the NORVI AE01-R front-panel buttons. The wizard measures the resting level and each button's ADC level via the shared GPIO32 input, computes the button thresholds at the midpoints between adjacent levels, and persists them through
ConfigManager.Changes
CalibrationManager(new): state machineIDLE → RESTING → BTN1 → BTN2 → BTN3 → COMPUTE → SAVE → DONE/ERRORwith injectable ADC-read/time functions for native testing. Per-step timeout (10 s) with retry, stability detection (±50 ADC over 3 readings), 20-sample averaging, minimum level gap (100 ADC), and sanity checks (strictly ascending levels, S3 ≤ 4095).NorviButtonHandler::loop()early-returns while calibration is active so button presses don't trigger actions.POST /api/calibrate/start,GET /api/calibrate/status,POST /api/calibrate/cancel.CalibrationManagerandNorviButtonHandlercompiled as wrappers in the native test build to resolve mock/production layout differences.Verification
pio run -e norvi_ae01_r: SUCCESSnode --check data/web/app.js: OKNotes
btn3Maxstays 4095 (full scale),btnNoPressstays 4096 (sentinel) — calibration never changes these.