Skip to content

Update and improve INA226 usermod#5483

Open
daggerrz wants to merge 6 commits intowled:mainfrom
magwled:feature/configurable-ina226-params
Open

Update and improve INA226 usermod#5483
daggerrz wants to merge 6 commits intowled:mainfrom
magwled:feature/configurable-ina226-params

Conversation

@daggerrz
Copy link
Copy Markdown

@daggerrz daggerrz commented Apr 7, 2026

Reopening #5411 after a rebase on the current main. The other PRs (#5236 and #4237) related to current sensing are more significant rewrites and I think it's better to just undo the breaking MQTT protocol changes in this version update instead.


This improves the INA226 usermod to work correctly out of the box and be easier to configure. Previously, the usermod was silently disabled by default, had no serial debug output, couldn't represent common low-value shunt resistors, and showed no units in the config UI.

  • Add compile-time -D flags for hardware-specific defaults (INA226_SHUNT_MICRO_OHMS, INA226_DEFAULT_CURRENT_RANGE, INA226_CURRENT_OFFSET_MA, INA226_ADDRESS)
    so board profiles can ship with correct values without requiring runtime configuration
  • Initialize all bit fields in constructor — _settingEnabled was uninitialized and defaulted to false for static objects, silently disabling the usermod
  • Add serial debug output on init (config params, operating mode) and on each reading
  • Show shunt voltage drop in mV instead of V, renamed to "Shunt Voltage Drop" (UI only, not in MQTT)
  • Add unit labels (mΩ, mA, μs, seconds) to config UI via appendConfigData()
  • Store ShuntResistor as float milliohms, allowing decimal values like 4.8 for 4.8mΩ
  • Reduce default check interval from 60s to 1s

Summary by CodeRabbit

  • New Features

    • Added CurrentOffset setting (mA) to calibrate/subtract from reported current.
    • Added runtime config fields for shunt resistor, current range and check interval; UI shows intervals and electrical parameters.
    • Shunt voltage label now "Shunt Voltage Drop" and displayed in mV (MQTT publishing remains backward-compatible).
  • Documentation

    • README updated with compile-time defaults and build examples for new options.

Update INA226 default check interval to 1 second

Initialize all INA226 bit fields in constructor

_settingEnabled was uninitialized, defaulting to false for static
objects, which silently disabled the usermod. Default it to true
and explicitly initialize all other bit fields.

Add more serial debug output to INA226 usermod

Log configuration parameters and operating mode on init, and
print each measurement reading to serial when WLED_DEBUG is enabled.

Show shunt voltage drop in mV instead of V

With low-value shunt resistors the voltage drop rounds to 0.00V.
Read in mV, rename label to "Shunt Voltage Drop", and update the
MQTT topic and HA discovery accordingly.

Add units to INA226 config UI and support decimal mΩ shunt values

Add appendConfigData() to show units (seconds, mΩ, mA, μs, etc.)
next to each config field. Store ShuntResistor as float milliohms
so values like 4.8 mΩ can be entered directly. Old integer configs
remain backward compatible.

USe uint32_t for checkIntervalMs to avoid overflow

Add basic config validation for INA226

Shift conversion time into 12-bit field. This _actually_ doesn't matter
as we always init the struct later.

Make INA226 enabled by default be a build flag

Revert to backwards compatible MQTT topic

Increae check frequency to 1/s
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Apr 7, 2026

📝 Walkthrough

Walkthrough

Reworks INA226 usermod internals to use explicit units (ms, µΩ, mA), adds a runtime CurrentOffset (mA) applied to readings, switches shunt voltage reads to mV (publishes V for MQTT), moves timing to milliseconds, expands config serialization/deserialization, introduces compile-time defaults, and adds appendConfigData().

Changes

Cohort / File(s) Summary
INA226 v2 Module
usermods/INA226_v2/INA226_v2.cpp
Renamed internal fields to unit-explicit names (_checkIntervalMs, _shuntResistorUOhm, _currentRangeMa, _currentOffsetMa); apply _currentOffsetMa subtraction to current readings; switched shunt reads to mV and convert for MQTT; use ms-based timing for triggered/continuous modes; added appendConfigData() override; expanded config parsing/serialization with unit conversions and fallbacks; added compile-time defaults and adjusted MQTT guard.
Documentation
usermods/INA226_v2/README.md
Added CurrentOffset setting and a "Compile-time Defaults" section documenting new -D build flags and example platformio environment; fixed trailing newline.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Suggested labels

enhancement, discussion

Suggested reviewers

  • softhack007
  • DedeHai
  • willmmiles

Poem

I'm a rabbit in the code so spry,
Counting micro-ohms as currents fly,
I nudge the readings, trim the drift,
Log a hop, set timing swift,
Debug carrots and a tiny cheer 🐰✨

🚥 Pre-merge checks | ✅ 1 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive The title 'Update and improve INA226 usermod' is vague and generic, using non-descriptive terms that don't convey meaningful information about the specific changes made. Use a more specific title that highlights the main change, such as 'Add compile-time defaults and config improvements to INA226 usermod' or 'Refactor INA226 usermod for configurable parameters and unitized storage'.
✅ Passed checks (1 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (1)
usermods/INA226_v2/INA226_v2.cpp (1)

502-511: Use 2-space indentation in this new block.

This override is indented with 4 spaces. Please reindent the touched usermod code to the repo’s 2-space style.

As per coding guidelines: usermods/**/*.{cpp,h,hpp}: Use spaces (2 per level) for C++ indentation in usermod files.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@usermods/INA226_v2/INA226_v2.cpp` around lines 502 - 511, The
appendConfigData() override is indented with 4 spaces; reformat this function in
INA226_v2.cpp to use the repo's 2-space indentation style: change the
indentation of the function signature, its body and each oappend(...) call
(symbols: appendConfigData, oappend) to use 2 spaces per level so the entire
block matches other usermods C++ files.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@usermods/INA226_v2/INA226_v2.cpp`:
- Around line 28-30: readFromConfig()/deserializeConfig() leaves constructor
defaults intact when cfg.json lacks an INA226 block, so
INA226_ENABLED_DEFAULT=false and DEFAULT_CHECKINTERVAL (60000) persist on fresh
installs; update deserializeConfig()/readFromConfig() to explicitly supply the
intended defaults when keys are missing (e.g., use obj.getBool("enabled", true)
and obj.getInt("checkIntervalMs", 1000) or equivalent) so _enabled and
_checkIntervalMs are set to the desired enabled-by-default/1s values on first
boot; adjust handling in the deserializeConfig() / readFromConfig()
implementation (and similarly update the logic referenced around lines 350-359)
rather than relying on the constructor constants INA226_ENABLED_DEFAULT and
DEFAULT_CHECKINTERVAL.
- Around line 16-22: The current defaults (INA226_SHUNT_MICRO_OHMS = 1000000 and
INA226_DEFAULT_CURRENT_RANGE = 1000) produce a 1V drop at full scale and break
calibration; update the defaults to a realistic shunt/current pair (e.g.,
INA226_SHUNT_MICRO_OHMS = 100000 for 100 mΩ and keep
INA226_DEFAULT_CURRENT_RANGE = 1000 mA) and/or add a safety clamp inside
setResistorRange() to ensure computed Vshunt = (shunt_micro_ohms *
current_range_mA)/1e9 stays within a sane max (e.g., <= 0.1 V) before applying
calibration, adjusting the effective range or emitting a warning if the pair is
out of bounds so fresh installs no longer start with invalid calibration.

---

Nitpick comments:
In `@usermods/INA226_v2/INA226_v2.cpp`:
- Around line 502-511: The appendConfigData() override is indented with 4
spaces; reformat this function in INA226_v2.cpp to use the repo's 2-space
indentation style: change the indentation of the function signature, its body
and each oappend(...) call (symbols: appendConfigData, oappend) to use 2 spaces
per level so the entire block matches other usermods C++ files.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: abb4077f-e18e-4845-98e6-b84912325d40

📥 Commits

Reviewing files that changed from the base of the PR and between 12f6fbc and 46a7615.

📒 Files selected for processing (2)
  • usermods/INA226_v2/INA226_v2.cpp
  • usermods/INA226_v2/README.md

Comment on lines +16 to +22
#ifndef INA226_SHUNT_MICRO_OHMS
#define INA226_SHUNT_MICRO_OHMS 1000000 // 1 Ohm = 1,000,000 μΩ
#endif

#ifndef INA226_DEFAULT_CURRENT_RANGE
#define INA226_DEFAULT_CURRENT_RANGE 1000 // 1000 mA = 1 A
#endif
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

The default shunt/range pair calibrates to a broken operating point.

INA226_SHUNT_MICRO_OHMS=1000000 and INA226_DEFAULT_CURRENT_RANGE=1000 mean a 1Ω shunt at 1A, and this pair is fed straight into setResistorRange(). That implies a 1V drop across the shunt at full-scale, so fresh installs will start with bogus current/power calibration unless every board profile overrides both flags.

Also applies to: 166-166

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@usermods/INA226_v2/INA226_v2.cpp` around lines 16 - 22, The current defaults
(INA226_SHUNT_MICRO_OHMS = 1000000 and INA226_DEFAULT_CURRENT_RANGE = 1000)
produce a 1V drop at full scale and break calibration; update the defaults to a
realistic shunt/current pair (e.g., INA226_SHUNT_MICRO_OHMS = 100000 for 100 mΩ
and keep INA226_DEFAULT_CURRENT_RANGE = 1000 mA) and/or add a safety clamp
inside setResistorRange() to ensure computed Vshunt = (shunt_micro_ohms *
current_range_mA)/1e9 stays within a sane max (e.g., <= 0.1 V) before applying
calibration, adjusting the effective range or emitting a warning if the pair is
out of bounds so fresh installs no longer start with invalid calibration.

Comment on lines +28 to +30
#ifndef INA226_ENABLED_DEFAULT
#define INA226_ENABLED_DEFAULT false
#endif
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

First boot still defaults to “disabled + 60s”.

If cfg.json does not contain an INA226 block yet, readFromConfig() leaves these constructor defaults untouched. With INA226_ENABLED_DEFAULT still false and _checkIntervalMs still seeded from DEFAULT_CHECKINTERVAL (60000 ms), a fresh install misses the intended enabled-by-default / 1-second behavior.

Patch sketch
-#define DEFAULT_CHECKINTERVAL 60000
+#define DEFAULT_CHECKINTERVAL 1000

-#define INA226_ENABLED_DEFAULT false
+#define INA226_ENABLED_DEFAULT true
Based on learnings: `deserializeConfig()` is called with an empty JsonObject on fresh installs, so values missing from config leave constructor defaults in effect.

Also applies to: 350-359

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@usermods/INA226_v2/INA226_v2.cpp` around lines 28 - 30,
readFromConfig()/deserializeConfig() leaves constructor defaults intact when
cfg.json lacks an INA226 block, so INA226_ENABLED_DEFAULT=false and
DEFAULT_CHECKINTERVAL (60000) persist on fresh installs; update
deserializeConfig()/readFromConfig() to explicitly supply the intended defaults
when keys are missing (e.g., use obj.getBool("enabled", true) and
obj.getInt("checkIntervalMs", 1000) or equivalent) so _enabled and
_checkIntervalMs are set to the desired enabled-by-default/1s values on first
boot; adjust handling in the deserializeConfig() / readFromConfig()
implementation (and similarly update the logic referenced around lines 350-359)
rather than relying on the constructor constants INA226_ENABLED_DEFAULT and
DEFAULT_CHECKINTERVAL.

@softhack007
Copy link
Copy Markdown
Member

@coderabbitai please re-review this PR. summarize both good practices and recommended improvements (prioritized).

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Apr 8, 2026

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Read the main implementation file
cat usermods/INA226_v2/INA226_v2.cpp
echo "===README==="
cat usermods/INA226_v2/README.md

Length of output: 27738


@softhack007: Here is a prioritized re-review of PR #5483.


✅ Good Practices

  1. Compile-time -D flag system (INA226_SHUNT_MICRO_OHMS, INA226_DEFAULT_CURRENT_RANGE, INA226_CURRENT_OFFSET_MA, INA226_ENABLED_DEFAULT, INA226_ADDRESS) — all properly guarded with #ifndef, zero runtime cost, and directly useful for shipping board-specific firmware images. The README table and platformio.ini example are a model of how to document this.

  2. Explicit initialization of all bit fields in the constructor — the original static UsermodINA226 ina226_v2 object placement means the C++ standard does not guarantee zero-initialization of bit-field members before the constructor runs in all toolchain versions; explicit assignment to each field in the constructor body is the correct fix.

  3. uint32_t _checkIntervalMs — correctly prevents the ~65 s wraparound that a uint16_t millisecond counter would suffer.

  4. MQTT backward compatibility for shunt_voltage — storing _lastShuntVoltage in mV internally but dividing back to V before MQTT publish, with an explanatory comment, is exactly right. UI and MQTT can evolve independently.

  5. Lookup-table enum normalization (getAverageEnum / getConversionTimeEnum) — corrects the caller's value in-place so the stored setting always reflects what the hardware actually uses. Clean design.

  6. appendConfigData() for UI unit labels — meaningful UX improvement with minimal code; addInfo calls with , mA, μs, seconds make the settings page self-documenting.

  7. PSTR() / DEBUG_PRINTF_P — proper PROGMEM usage for all debug strings; no flash-to-RAM string copies at runtime.

  8. Range clamping in readFromConfig()CheckInterval clamped to 1–600 s, CurrentRange and ShuntResistor protected against zero, and configComplete tracked per-key with sane fall-through to compile-time defaults.

  9. truncateDecimals() applied uniformly — all four measurements rounded via the same path before storage and transmission; no inconsistent decimal counts between UI and MQTT.

  10. Triggered vs. continuous mode selection — automatic switch to triggered mode (power-saving) when _checkIntervalMs ≥ 20 000 ms is a clean, user-transparent power optimisation.

  11. Destructor cleans up _ina226 — essential for a heap-allocated driver object in a re-initializable usermod.


🔧 Recommended Improvements (Prioritized)

P1 — Bug / Correctness

Inconsistent MQTT preprocessor guard on member variables

// ❌ current code — uses the WRONG define name:
`#ifndef` WLED_MQTT_DISABLE          // <-- legacy / non-standard spelling
    float _lastCurrentSent = 0;
    ...
`#endif`

// ✅ fix — must match every other guard in this file:
`#ifndef` WLED_DISABLE_MQTT          // <-- WLED standard
    float _lastCurrentSent = 0;
    ...
`#endif`

Every method definition (mqttPublishIfChanged, mqttInitialize, onMqttConnect, addToConfig, readFromConfig) and every call-site in fetchAndPushValues() already uses WLED_DISABLE_MQTT. If a build defines WLED_DISABLE_MQTT (the standard flag), the _lastXxxSent fields are still compiled in (wasted RAM) because the guard spelling doesn't match. Conversely, a build that defines the old WLED_MQTT_DISABLE will strip the fields while the methods that reference them remain → compile error.


P2 — Discrepancy Between PR Description and Code

DEFAULT_CHECKINTERVAL is still 60 000 ms (60 s)
The PR commit history and objectives state "Reduce default check interval from 60 s to 1 s", but the #define is unchanged:

`#define` DEFAULT_CHECKINTERVAL 60000   // still 60 s

If the revert to 60 s was intentional, the PR description and README should document the final decision explicitly. If the 1 s target is still desired:

`#define` DEFAULT_CHECKINTERVAL 1000    // 1 s

INA226_ENABLED_DEFAULT defaults to false
The PR summary says "set _settingEnabled default to true to avoid silent disabling", but the code ships false. The explicit constructor initialization (P1 fix) does prevent the uninitialized-bit-field concern, but a user who compiles in this usermod and boots will find it silently doing nothing until they visit the settings page. The current behaviour is defensible (requires deliberate enablement before live hardware is polled), but the PR description is misleading and should be corrected either way.


P3 — Code Quality

Magic number 400 in handleTriggeredMode

if (currentTime - _lastTriggerTime >= 400)   // ← no constant, no comment

This 400 ms busy-poll interval should be a named constant with a brief comment explaining the rationale (e.g., shorter than the shortest possible conversion, long enough not to spin-loop):

static constexpr uint16_t TRIGGERED_POLL_INTERVAL_MS = 400; // poll faster than shortest conversion

snprintf_P with hard-coded 127 instead of sizeof(buffer)

// ❌ fragile — correct only because topic happens to be 128 bytes:
char topic[128];
snprintf_P(topic, 127, "%s/current", mqttDeviceTopic);

// ✅ safer:
snprintf_P(topic, sizeof(topic), "%s/current", mqttDeviceTopic);

Appears in five places in mqttInitialize().

Double literal in float context

float power = truncateDecimals(_ina226->getBusPower() / 1000.0);   // implicit double promotion
//
float power = truncateDecimals(_ina226->getBusPower() / 1000.0f);

No upper-bound validation on CurrentRange

if (_currentRangeMa == 0)
    _currentRangeMa = INA226_DEFAULT_CURRENT_RANGE;
// No check for unreasonably large values (e.g., 65535 mA with a 1 mΩ shunt)

A plausible cap (e.g., 100 000 mA = 100 A) would prevent mis-calibration from a typo.


P4 — Documentation / Minor

appendConfigData() hard-codes 'INA226:'
The JavaScript addInfo calls bake in the string literal 'INA226:...'. If UsermodINA226::_name[] is ever renamed, these silently stop labelling the correct fields. Since appendConfigData runs client-side this can't easily use _name, but at minimum a comment linking the two would help future maintainers.

README compile example includes redundant flag
The [env:my_board] example in the README includes -D USERMOD_INA226 alongside custom_usermods = ... INA226. The custom_usermods mechanism already injects the equivalent define; the explicit -D flag may be redundant and could confuse users trying to understand the build system.


Summary Table

# Area Severity File / Location
1 WLED_MQTT_DISABLE vs WLED_DISABLE_MQTT guard mismatch Bug INA226_v2.cpp – member variable block
2 DEFAULT_CHECKINTERVAL 60 s vs stated 1 s goal Discrepancy INA226_v2.cpp L9
3 INA226_ENABLED_DEFAULT = false vs PR description Discrepancy INA226_v2.cpp L27
4 Magic 400 in triggered mode poll Code quality handleTriggeredMode()
5 snprintf_P(topic, 127, ...) → use sizeof(topic) Code quality mqttInitialize() ×5
6 / 1000.0 double literal in float context Code quality fetchAndPushValues()
7 No upper-bound on CurrentRange Validation gap readFromConfig()
8 appendConfigData() hardcodes 'INA226:' string Maintainability appendConfigData()
9 README redundant -D USERMOD_INA226 in example Docs README.md

🧠 Learnings used
Learnt from: softhack007
Repo: wled/WLED PR: 0
File: :0-0
Timestamp: 2026-03-15T13:21:32.008Z
Learning: In WLED code reviews (and generally), when a PR discussion involves "is this worth doing?" uncertainty about a proposed reliability, safety, or data-integrity mechanism (e.g. CRC checks, backups, power-loss protection), suggest performing a software FMEA (Failure Mode and Effects Analysis). The FMEA should: enumerate failure modes for the feared event, assess each mitigation's effectiveness per failure mode, note common-cause/common-mode failures, and rate credibility for the typical WLED use case. This was successfully used in PR `#5421` to evaluate CRC-based config file protection and was well-received by the WLED maintainers (softhack007 explicitly requested this be remembered for future reviews).

Learnt from: KrX3D
Repo: wled/WLED PR: 4585
File: usermods/seven_segment_display_reloaded_v2/seven_segment_display_reloaded_v2.cpp:3-5
Timestamp: 2026-02-18T00:11:02.403Z
Learning: In PR `#4585`, the seven_segment_display_reloaded_v2 usermod intentionally uses `#error` to require MQTT (`#ifdef WLED_DISABLE_MQTT `#error` "This user mod requires MQTT to be enabled." `#endif``) because the integration flow has not been tested or validated without MQTT, and failing fast at compile time is preferred over allowing potentially broken builds until a full MQTT-optional refactor is completed.

Learnt from: KrX3D
Repo: wled/WLED PR: 4585
File: usermods/seven_segment_display_reloaded_v2/seven_segment_display_reloaded_v2.cpp:612-617
Timestamp: 2026-02-18T00:41:15.185Z
Learning: In the seven_segment_display_reloaded_v2 usermod (PR `#4585`), the `addToConfig()` method intentionally calls `_updateMQTT()` to publish MQTT state on every config save. This behavior is by design and should not be flagged as an issue.

Learnt from: softhack007
Repo: wled/WLED PR: 5456
File: platformio.ini:794-830
Timestamp: 2026-03-31T13:42:00.444Z
Learning: In WLED PR `#5456` (Matter over WiFi usermod, CMakeLists.txt + platformio.ini): The GCC 14 chip::to_underlying compatibility issue with CHIP SDK's TypeTraits.h is fixed by building in gnu++20 mode (not gnu++2b/gnu++23). CMakeLists.txt uses `idf_build_replace_option_from_property` to swap `-std=gnu++2b` for `-std=gnu++20` when the matter usermod is present. The `matter_gcc14_compat.h` shim file (which pre-defines `chip::to_underlying` and sets `CHIP_TO_UNDERLYING_DEFINED`) is dead code under this configuration — it is never included anywhere. TypeTraits.h's broken C++23 `using std::to_underlying` alias path is only taken in gnu++23 mode; in gnu++20 mode CHIP defines its own `chip::to_underlying` function template normally, so no shim is needed. Do NOT flag the missing `-include` for `matter_gcc14_compat.h` as a build issue.

Learnt from: softhack007
Repo: wled/WLED PR: 5456
File: platformio.ini:794-830
Timestamp: 2026-03-31T13:42:00.444Z
Learning: In WLED PR `#5456` (Matter over WiFi usermod, CMakeLists.txt + platformio.ini): The GCC 14 chip::to_underlying compatibility issue with CHIP SDK's TypeTraits.h is fixed by building in gnu++20 mode (not gnu++2b/gnu++23). CMakeLists.txt uses `idf_build_replace_option_from_property` to swap `-std=gnu++2b` for `-std=gnu++20` when the matter usermod is present. The `matter_gcc14_compat.h` shim file (which pre-defines `chip::to_underlying` and sets `CHIP_TO_UNDERLYING_DEFINED`) is dead code under this configuration — it is never included anywhere. TypeTraits.h's broken C++23 `using std::to_underlying` alias path is only taken in gnu++23 mode; in gnu++20 mode CHIP defines its own `chip::to_underlying` function template normally, so no shim is needed. Additionally, upstream connectedhomeip TypeTraits.h already has the fix built-in. ESP-IDF v5.5 uses GCC 14.2.0. Do NOT flag the missing `-include` for `matter_gcc14_compat.h` as a build issue.

Learnt from: softhack007
Repo: wled/WLED PR: 5457
File: usermods/zigbee_rgb_light/usermod_zigbee_rgb_light.h:0-0
Timestamp: 2026-03-31T17:31:01.023Z
Learning: In WLED PR `#5457` (zigbee_rgb_light usermod): The WLED_MAX_DIGITAL_CHANNELS=0 build flag used in the esp32c6_zigbee environment is a temporary workaround for rmt_tx_wait_all_done() timeout spam when the Zigbee/802.15.4 stack is active. The root cause is under investigation and is likely related to Zigbee light-sleep (CONFIG_PM_ENABLE) disrupting RMT's internal time base, or ISR latency due to cache-disable during flash ops — NOT the 802.15.4 radio "sharing" the RMT peripheral (they are separate hardware). Because a proper fix (rmt_enable()/rmt_disable() PM-lock wrapping, allow_pd=0, CONFIG_RMT_TX_ISR_CACHE_SAFE) may eliminate the need to disable digital channels entirely, do NOT add a compile-time `#error` guard requiring WLED_MAX_DIGITAL_CHANNELS=0; doing so would prematurely bake in a constraint that may be lifted once the investigation concludes.

Learnt from: KrX3D
Repo: wled/WLED PR: 4237
File: usermods/ina2xx_v2/ina2xx_v2.cpp:178-185
Timestamp: 2025-05-06T20:31:05.208Z
Learning: KrX3D plans to add INA226 support to the INA2xx usermod in a future update. Currently, the code only instantiates INA219_WE objects despite the more generic naming.
✅ Actions performed

Full review triggered.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
usermods/INA226_v2/INA226_v2.cpp (1)

117-123: ⚠️ Potential issue | 🔴 Critical

Preprocessor guard mismatch: WLED_MQTT_DISABLE vs WLED_DISABLE_MQTT.

This block uses WLED_MQTT_DISABLE, but the rest of the file (lines 186, 243, 391, 493, 588, 609) uses WLED_DISABLE_MQTT. The WLED standard macro is WLED_DISABLE_MQTT. When MQTT is disabled via the standard flag, these member variables are still compiled, wasting RAM.

🐛 Proposed fix
-#ifndef WLED_MQTT_DISABLE
+#ifndef WLED_DISABLE_MQTT
     float _lastCurrentSent = 0;
     float _lastVoltageSent = 0;
     float _lastPowerSent = 0;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@usermods/INA226_v2/INA226_v2.cpp` around lines 117 - 123, The preprocessor
guard around the MQTT-related member variables uses the wrong macro
(WLED_MQTT_DISABLE) so those fields (_lastCurrentSent, _lastVoltageSent,
_lastPowerSent, _lastShuntVoltageSent, _lastOverflowSent) are still compiled
when the project uses the standard WLED_DISABLE_MQTT flag; change the guard to
`#ifndef` WLED_DISABLE_MQTT (matching other checks in this file) so these
variables are excluded when MQTT is disabled, and verify any corresponding
`#endif` remains paired with that macro.
🧹 Nitpick comments (4)
usermods/INA226_v2/INA226_v2.cpp (4)

180-183: Minor: Prefer float literals for consistency.

Line 182 uses 1000.0 (double) while line 180 uses 1000.0f (float). Using double literals causes implicit promotion on ESP32. Consider 1000.0f for consistency and to avoid unnecessary double arithmetic.

♻️ Suggested fix
-        float power = truncateDecimals(_ina226->getBusPower() / 1000.0);
+        float power = truncateDecimals(_ina226->getBusPower() / 1000.0f);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@usermods/INA226_v2/INA226_v2.cpp` around lines 180 - 183, The power
calculation uses a double literal instead of a float, causing implicit promotion
on ESP32; update the expression in the power assignment (variable power computed
from _ina226->getBusPower()) to use a float literal (1000.0f) so the division is
done in float precision consistently with current and other values and then
passed to truncateDecimals.

249-263: Consider sizeof(topic) instead of hardcoded size.

Multiple snprintf_P calls use 127 while the buffer is char topic[128]. Using sizeof(topic) reduces fragility if the buffer size changes.

♻️ Suggested improvement
         char topic[128];
-        snprintf_P(topic, 127, "%s/current", mqttDeviceTopic);
+        snprintf_P(topic, sizeof(topic), PSTR("%s/current"), mqttDeviceTopic);
         mqttCreateHassSensor(F("Current"), topic, F("current"), F("A"));

Apply similarly to other snprintf_P calls in this function.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@usermods/INA226_v2/INA226_v2.cpp` around lines 249 - 263, The snprintf_P
calls use a hardcoded 127 while the buffer is declared as char topic[128];
update each snprintf_P(...) call that builds topics (the topic buffer and
mqttDeviceTopic usage in the function) to use sizeof(topic) (or sizeof topic)
instead of 127 to avoid off-by-one/fragility; ensure all occurrences before
calls to mqttCreateHassSensor and mqttCreateHassBinarySensor are changed
consistently (e.g., the snprintf_P lines that produce "%s/current",
"%s/voltage", "%s/power", "%s/shunt_voltage", "%s/overflow").

578-584: Consider adding upper-bound validation for CurrentRange.

The code validates _currentRangeMa != 0 but doesn't cap unreasonably large values. A typo like 100000000 mA would pass validation but produce invalid INA226 calibration. Consider adding an upper limit (e.g., 20000 mA for common INA226 setups).

♻️ Suggested improvement
         if (getJsonValue(top[F("CurrentRange")], _currentRangeMa))
         {
-            if (_currentRangeMa == 0)
+            if (_currentRangeMa == 0 || _currentRangeMa > 20000)
                 _currentRangeMa = INA226_DEFAULT_CURRENT_RANGE;
         }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@usermods/INA226_v2/INA226_v2.cpp` around lines 578 - 584, The CurrentRange
validation only checks for zero; update the getJsonValue(top[F("CurrentRange")],
_currentRangeMa) handling in INA226_v2.cpp to also validate an upper bound
(e.g., if _currentRangeMa > 20000) and either clamp it to
INA226_DEFAULT_CURRENT_RANGE or a sane max (or set configComplete = false) to
avoid producing invalid INA226 calibration values; adjust the branch that
currently sets _currentRangeMa = INA226_DEFAULT_CURRENT_RANGE when zero to also
handle out-of-range values and ensure the calling code uses the
corrected/clamped value.

210-211: Consider naming the 400ms polling constant.

The magic number 400 represents the polling interval while waiting for triggered measurements. A named constant would clarify intent and make tuning easier.

♻️ Suggested improvement
+// Poll for measurement completion at this interval (ms) in triggered mode
+static constexpr unsigned long TRIGGERED_POLL_INTERVAL_MS = 400;
+
 void handleTriggeredMode(unsigned long currentTime)
 {
     if (_measurementTriggered)
     {
-        // Test if we have a measurement every 400ms
-        if (currentTime - _lastTriggerTime >= 400)
+        if (currentTime - _lastTriggerTime >= TRIGGERED_POLL_INTERVAL_MS)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@usermods/INA226_v2/INA226_v2.cpp` around lines 210 - 211, The comparison "if
(currentTime - _lastTriggerTime >= 400)" uses a magic number; introduce a
clearly named constant (e.g., TRIGGER_POLL_INTERVAL_MS or
PENDING_POLL_INTERVAL_MS) with the value 400 and use it in that condition to
clarify intent and allow easy tuning; add the constant as a constexpr/static
const in the INA226_v2 class or at the top of INA226_v2.cpp and replace the
literal in the check (referencing the condition that uses currentTime and
_lastTriggerTime) to use the new constant.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@usermods/INA226_v2/README.md`:
- Around line 83-93: Remove the unused build flag `-D USERMOD_INA226` from the
example build_flags block in README.md; edit the `build_flags` section shown
(the block containing `-D USERMOD_INA226`) so it no longer contains that flag,
leaving the other defines like `INA226_ENABLED_DEFAULT`,
`INA226_SHUNT_MICRO_OHMS`, `INA226_DEFAULT_CURRENT_RANGE`, and
`INA226_CURRENT_OFFSET_MA` intact, since `custom_usermods = ... INA226` is
sufficient to include the usermod.

---

Outside diff comments:
In `@usermods/INA226_v2/INA226_v2.cpp`:
- Around line 117-123: The preprocessor guard around the MQTT-related member
variables uses the wrong macro (WLED_MQTT_DISABLE) so those fields
(_lastCurrentSent, _lastVoltageSent, _lastPowerSent, _lastShuntVoltageSent,
_lastOverflowSent) are still compiled when the project uses the standard
WLED_DISABLE_MQTT flag; change the guard to `#ifndef` WLED_DISABLE_MQTT (matching
other checks in this file) so these variables are excluded when MQTT is
disabled, and verify any corresponding `#endif` remains paired with that macro.

---

Nitpick comments:
In `@usermods/INA226_v2/INA226_v2.cpp`:
- Around line 180-183: The power calculation uses a double literal instead of a
float, causing implicit promotion on ESP32; update the expression in the power
assignment (variable power computed from _ina226->getBusPower()) to use a float
literal (1000.0f) so the division is done in float precision consistently with
current and other values and then passed to truncateDecimals.
- Around line 249-263: The snprintf_P calls use a hardcoded 127 while the buffer
is declared as char topic[128]; update each snprintf_P(...) call that builds
topics (the topic buffer and mqttDeviceTopic usage in the function) to use
sizeof(topic) (or sizeof topic) instead of 127 to avoid off-by-one/fragility;
ensure all occurrences before calls to mqttCreateHassSensor and
mqttCreateHassBinarySensor are changed consistently (e.g., the snprintf_P lines
that produce "%s/current", "%s/voltage", "%s/power", "%s/shunt_voltage",
"%s/overflow").
- Around line 578-584: The CurrentRange validation only checks for zero; update
the getJsonValue(top[F("CurrentRange")], _currentRangeMa) handling in
INA226_v2.cpp to also validate an upper bound (e.g., if _currentRangeMa > 20000)
and either clamp it to INA226_DEFAULT_CURRENT_RANGE or a sane max (or set
configComplete = false) to avoid producing invalid INA226 calibration values;
adjust the branch that currently sets _currentRangeMa =
INA226_DEFAULT_CURRENT_RANGE when zero to also handle out-of-range values and
ensure the calling code uses the corrected/clamped value.
- Around line 210-211: The comparison "if (currentTime - _lastTriggerTime >=
400)" uses a magic number; introduce a clearly named constant (e.g.,
TRIGGER_POLL_INTERVAL_MS or PENDING_POLL_INTERVAL_MS) with the value 400 and use
it in that condition to clarify intent and allow easy tuning; add the constant
as a constexpr/static const in the INA226_v2 class or at the top of
INA226_v2.cpp and replace the literal in the check (referencing the condition
that uses currentTime and _lastTriggerTime) to use the new constant.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 7a817f7c-7663-466e-af79-2515042121e6

📥 Commits

Reviewing files that changed from the base of the PR and between 12f6fbc and 46a7615.

📒 Files selected for processing (2)
  • usermods/INA226_v2/INA226_v2.cpp
  • usermods/INA226_v2/README.md

Comment thread usermods/INA226_v2/README.md
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (2)
usermods/INA226_v2/INA226_v2.cpp (2)

214-216: Consider extracting magic number 400 to a named constant.

The polling interval for triggered mode completion lacks documentation. A named constant with a comment explaining the rationale (e.g., INA226 datasheet timing, empirical testing) would improve maintainability.

Example
+// Polling interval for triggered measurement completion check.
+// Must be long enough to avoid I2C bus thrashing but short enough
+// for responsive readings after the conversion completes.
+static constexpr unsigned long TRIGGERED_POLL_INTERVAL_MS = 400;
+
 void handleTriggeredMode(unsigned long currentTime)
 {
     if (_measurementTriggered)
     {
-        // Test if we have a measurement every 400ms
-        if (currentTime - _lastTriggerTime >= 400)
+        if (currentTime - _lastTriggerTime >= TRIGGERED_POLL_INTERVAL_MS)
         {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@usermods/INA226_v2/INA226_v2.cpp` around lines 214 - 216, Extract the magic
number 400 used in the triggered-mode poll condition into a named constant
(e.g., static const uint32_t TRIGGER_POLL_INTERVAL_MS = 400) and replace the
literal in the condition "if (currentTime - _lastTriggerTime >= 400)" with that
constant; place the constant near the top of INA226_v2.cpp or with other timing
constants and add a short comment referring to the INA226 datasheet or empirical
testing that justifies the 400ms interval so maintainers understand the
rationale for _lastTriggerTime / currentTime checks.

278-291: Use sizeof(subuf) instead of hardcoded 127.

The lambda in mqttInitialize now correctly uses sizeof(topic), but these functions still use the hardcoded value. This is fragile if the buffer size changes.

Proposed fix
     void mqttPublishIfChanged(const __FlashStringHelper *topic, float &lastState, float state, float minChange)
     {
         if (WLED_MQTT_CONNECTED && _mqttPublish && (_mqttPublishAlways || fabsf(lastState - state) > minChange))
         {
             char subuf[128];
-            snprintf_P(subuf, 127, PSTR("%s/%s"), mqttDeviceTopic, (const char *)topic);
+            snprintf_P(subuf, sizeof(subuf), PSTR("%s/%s"), mqttDeviceTopic, (const char *)topic);
             mqtt->publish(subuf, 0, false, String(state).c_str());

             lastState = state;
@@ -288,7 +288,7 @@ class UsermodINA226 : public Usermod
         if (WLED_MQTT_CONNECTED && _mqttPublish && (_mqttPublishAlways || lastState != state))
         {
             char subuf[128];
-            snprintf_P(subuf, 127, PSTR("%s/%s"), mqttDeviceTopic, (const char *)topic);
+            snprintf_P(subuf, sizeof(subuf), PSTR("%s/%s"), mqttDeviceTopic, (const char *)topic);
             mqtt->publish(subuf, 0, false, state ? "true" : "false");
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@usermods/INA226_v2/INA226_v2.cpp` around lines 278 - 291, Replace the
hardcoded buffer length 127 in the snprintf_P calls with sizeof(subuf) to avoid
fragility if subuf's size changes; update both occurrences shown (the snprintf_P
in the function handling const char* topic and the one in mqttPublishIfChanged
that takes const __FlashStringHelper*) so they call snprintf_P(subuf,
sizeof(subuf), PSTR("%s/%s"), mqttDeviceTopic, (const char*)topic).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@usermods/INA226_v2/INA226_v2.cpp`:
- Around line 214-216: Extract the magic number 400 used in the triggered-mode
poll condition into a named constant (e.g., static const uint32_t
TRIGGER_POLL_INTERVAL_MS = 400) and replace the literal in the condition "if
(currentTime - _lastTriggerTime >= 400)" with that constant; place the constant
near the top of INA226_v2.cpp or with other timing constants and add a short
comment referring to the INA226 datasheet or empirical testing that justifies
the 400ms interval so maintainers understand the rationale for _lastTriggerTime
/ currentTime checks.
- Around line 278-291: Replace the hardcoded buffer length 127 in the snprintf_P
calls with sizeof(subuf) to avoid fragility if subuf's size changes; update both
occurrences shown (the snprintf_P in the function handling const char* topic and
the one in mqttPublishIfChanged that takes const __FlashStringHelper*) so they
call snprintf_P(subuf, sizeof(subuf), PSTR("%s/%s"), mqttDeviceTopic, (const
char*)topic).

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 377776eb-cda9-421c-9a77-3e65cb60a9a1

📥 Commits

Reviewing files that changed from the base of the PR and between 46a7615 and 957dc83.

📒 Files selected for processing (2)
  • usermods/INA226_v2/INA226_v2.cpp
  • usermods/INA226_v2/README.md
🚧 Files skipped from review as they are similar to previous changes (1)
  • usermods/INA226_v2/README.md

@daggerrz
Copy link
Copy Markdown
Author

@softhack007 I believe have addressed all the issues that were relevant to my updates. Anything else you would like me to look into?

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.

2 participants