Skip to content

feat(calibration): guided NORVI button calibration wizard - #182

Merged
stritti merged 22 commits into
mainfrom
feat/button-calibration
Aug 17, 2026
Merged

stritti merged 22 commits into
mainfrom
feat/button-calibration

Conversation

@stritti

@stritti stritti commented Aug 16, 2026

Copy link
Copy Markdown
Contributor

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 machine IDLE → RESTING → BTN1 → BTN2 → BTN3 → COMPUTE → SAVE → DONE/ERROR with 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).
  • Button suppression: NorviButtonHandler::loop() early-returns while calibration is active so button presses don't trigger actions.
  • REST endpoints (auth-protected): POST /api/calibrate/start, GET /api/calibrate/status, POST /api/calibrate/cancel.
  • Web UI: "Start Calibration" button in the Button Thresholds section, modal wizard with live ADC polling (500 ms), step progress, and cancel.
  • Tests: 7 CalibrationManager tests (state machine, measurement, timeout retry, cancel, threshold math, error paths) + calibration status JSON test. CalibrationManager and NorviButtonHandler compiled as wrappers in the native test build to resolve mock/production layout differences.

Verification

  • Native tests: 113 suites, 205 assertions, 0 failures
  • pio run -e norvi_ae01_r: SUCCESS
  • node --check data/web/app.js: OK
  • clang-format: clean

Notes

  • btn3Max stays 4095 (full scale), btnNoPress stays 4096 (sentinel) — calibration never changes these.
  • Manual device verification (wizard walkthrough) still pending — requires hardware.

@github-actions

Copy link
Copy Markdown
Contributor

Native Test Coverage

Metric Value
Line Coverage 41.7%
Branch Coverage 72.8%
Lines Hit/Total 298/715
Branches Hit/Total 123/169

Report from native unit tests (ASan + gcov).

@github-actions

github-actions Bot commented Aug 16, 2026

Copy link
Copy Markdown
Contributor

⚠️MegaLinter analysis: Success with warnings

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
⚠️ MARKDOWN 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

⚠️ Your configuration references items that have been removed from MegaLinter and are ignored: 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

MegaLinter is provided by OX Security
Show us your support by starring ⭐ the repository

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 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".

Comment thread src/CalibrationManager.cpp Outdated
Comment thread src/NorviButtonHandler.cpp
Comment thread data/web/index.html Outdated
Comment thread src/CalibrationManager.cpp Outdated
Comment thread src/CalibrationManager.cpp Outdated
@github-actions

Copy link
Copy Markdown
Contributor

Native Test Coverage

Metric Value
Line Coverage 41.7%
Branch Coverage 72.8%
Lines Hit/Total 298/715
Branches Hit/Total 123/169

Report from native unit tests (ASan + gcov).

@github-actions

Copy link
Copy Markdown
Contributor

Native Test Coverage

Metric Value
Line Coverage 41.7%
Branch Coverage 72.8%
Lines Hit/Total 298/715
Branches Hit/Total 123/169

Report from native unit tests (ASan + gcov).

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 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".

Comment thread src/CalibrationManager.cpp Outdated
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.
@github-actions

Copy link
Copy Markdown
Contributor

Native Test Coverage

Metric Value
Line Coverage 41.7%
Branch Coverage 72.8%
Lines Hit/Total 298/715
Branches Hit/Total 123/169

Report from native unit tests (ASan + gcov).

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 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".

Comment thread data/web/app.js
Comment thread src/CalibrationManager.cpp
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.
@github-actions

Copy link
Copy Markdown
Contributor

Native Test Coverage

Metric Value
Line Coverage 41.7%
Branch Coverage 72.8%
Lines Hit/Total 298/715
Branches Hit/Total 123/169

Report from native unit tests (ASan + gcov).

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 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".

Comment thread data/web/app.js Outdated
Comment thread data/web/app.js Outdated
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.
@github-actions

Copy link
Copy Markdown
Contributor

Native Test Coverage

Metric Value
Line Coverage 41.7%
Branch Coverage 72.8%
Lines Hit/Total 298/715
Branches Hit/Total 123/169

Report from native unit tests (ASan + gcov).

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 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".

Comment thread src/CalibrationManager.cpp Outdated
Comment thread data/web/app.js
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.
@github-actions

Copy link
Copy Markdown
Contributor

Native Test Coverage

Metric Value
Line Coverage 41.7%
Branch Coverage 72.8%
Lines Hit/Total 298/715
Branches Hit/Total 123/169

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).
@github-actions

Copy link
Copy Markdown
Contributor

Native Test Coverage

Metric Value
Line Coverage 41.7%
Branch Coverage 72.8%
Lines Hit/Total 298/715
Branches Hit/Total 123/169

Report from native unit tests (ASan + gcov).

@stritti
stritti merged commit c4fc499 into main Aug 17, 2026
15 checks passed
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