Skip to content

Conversation

@broccoliboy
Copy link
Contributor

@broccoliboy broccoliboy commented Dec 30, 2025

This usermod enables the use of a DUPPA I2CEncoder V2.1 rotary encoder + pushbutton to control WLED.

Summary by CodeRabbit

  • New Features

    • I2C encoder button support: rotate to adjust brightness or cycle effects, short press toggles power, long press enters effect-adjust mode and can trigger a preset when brightness is zero.
    • Web-configurable I2C pins, address and behavior options; supports ESP8266/ESP32 environments.
  • Documentation

    • Added user guide with wiring, hardware notes, usage details, config JSON guidance, and a sample PlatformIO override for ESP01/ESP32.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 30, 2025

Walkthrough

Adds a new I2C encoder-button usermod (DUPPA I2CEncoder V2.1) including docs, PlatformIO sample, library manifest, a v2 usermod implementation with brightness/effect/button behaviors and JSON config, and a new usermod ID and PinOwner entry in core headers.

Changes

Cohort / File(s) Summary
Documentation & Manifest
usermods/i2c_encoder_button/README.md, usermods/i2c_encoder_button/library.json, usermods/i2c_encoder_button/platformio_override.sample.ini
New README describing hardware, usage, modes, and config; library.json declares i2c_encoder_button with dependencies Wire and ArduinoDuPPaLib#v1.4.1; added PlatformIO sample envs for ESP01/ESP32 and custom_usermods guidance.
Usermod Implementation
usermods/i2c_encoder_button/usermod_i2c_encoder_button.cpp
New public class UsermodI2CEncoderButton (v2) integrating i2cEncoderLibV2: I2C init, interrupt handling, short/long press behavior, two modes (brightness/effect), encoder LED color control, preset handling, JSON config read/write, and registration via REGISTER_USERMOD.
Core Identifiers & Pin Management
wled00/const.h, wled00/pin_manager.h
Added USERMOD_ID_I2C_ENCODER_BUTTON = 59 macro; added UM_I2C_ENCODER_BUTTON to PinOwner enum and minor enum punctuation adjustment for UM_PIXELS_DICE_TRAY.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~30 minutes

Possibly related PRs

  • wled/WLED PR 4456 — touches wled00/pin_manager.h enum additions/formatting, directly related to the PinOwner edits here.

Suggested reviewers

  • softhack007
  • blazoncek
🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 6.67% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Usermod I2C Rotary Encoder' directly and accurately reflects the main addition: a new usermod for an I2C rotary encoder with comprehensive implementation across multiple files.

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

✨ Finishing touches
  • 📝 Generate docstrings

🧹 Recent nitpick comments
usermods/i2c_encoder_button/usermod_i2c_encoder_button.cpp (1)

117-139: Consider checking enabled before allocating the interrupt pin.

The current flow allocates irqPin (lines 124-131) before checking the enabled flag (line 139). If the usermod is disabled via config but I2C pins are valid, the interrupt pin gets allocated but the encoder is never initialized.

Additionally, since there's no reinitialization logic in readFromConfig(), toggling enabled in the config UI won't take effect until the device is restarted. Consider either:

  1. Moving the enabled check to the start of setup(), or
  2. Adding a comment noting that changes require a restart
Option 1: Check enabled early and skip pin allocation
 void setup() override {
+    // Clean up existing encoder if any
+    if (encoder_p) {
+        delete encoder_p;
+        encoder_p = nullptr;
+    }
+    if (!enabled) {
+        if (irqPin >= 0) PinManager::deallocatePin(irqPin, PinOwner::UM_I2C_ENCODER_BUTTON);
+        return;
+    }
 
     if (i2c_sda < 0 || i2c_scl < 0) {
         DEBUG_PRINTLN(F("I2C pins not set, disabling I2C encoder usermod."));
         enabled = false;
         return;
-    } else {

📜 Recent review details

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 43fe90c and 5282f25.

📒 Files selected for processing (2)
  • usermods/i2c_encoder_button/usermod_i2c_encoder_button.cpp
  • wled00/pin_manager.h
🧰 Additional context used
📓 Path-based instructions (1)
wled00/**/!(html_*)*.h

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Use 2-space indentation for non-generated C++ header files (.h)

Files:

  • wled00/pin_manager.h
🧠 Learnings (21)
📓 Common learnings
Learnt from: willmmiles
Repo: wled/WLED PR: 4623
File: usermods/word-clock-matrix/word-clock-matrix.cpp:332-332
Timestamp: 2025-03-29T01:22:54.617Z
Learning: In the WLED project, hardcoded usermod IDs (like 500 in the WordClockMatrix's getId() method) are intentionally used as part of a strategy to avoid modifying core code. There are plans to remove numeric usermod IDs entirely in the future.
Learnt from: broccoliboy
Repo: wled/WLED PR: 5243
File: usermods/i2c_encoder_button/usermod_i2c_encoder_button.cpp:18-20
Timestamp: 2026-01-05T15:54:56.339Z
Learning: The DUPPA I2C Encoder V2.1 uses hardware jumpers to configure its I2C address, with a default address of 0x00 when no jumpers are soldered. This is different from the I2C Encoder Mini which uses software-configurable addressing with a default of 0x20.
📚 Learning: 2025-03-29T01:22:54.617Z
Learnt from: willmmiles
Repo: wled/WLED PR: 4623
File: usermods/word-clock-matrix/word-clock-matrix.cpp:332-332
Timestamp: 2025-03-29T01:22:54.617Z
Learning: In the WLED project, hardcoded usermod IDs (like 500 in the WordClockMatrix's getId() method) are intentionally used as part of a strategy to avoid modifying core code. There are plans to remove numeric usermod IDs entirely in the future.

Applied to files:

  • usermods/i2c_encoder_button/usermod_i2c_encoder_button.cpp
  • wled00/pin_manager.h
📚 Learning: 2025-08-29T00:26:15.808Z
Learnt from: ksedgwic
Repo: wled/WLED PR: 4883
File: usermods/usermod_v2_skystrip/rest_json_client.h:6-14
Timestamp: 2025-08-29T00:26:15.808Z
Learning: WLED uses a vendored ArduinoJson library (version 6) located at "src/dependencies/json/ArduinoJson-v6.h" which is included through wled.h. Usermods should not directly include ArduinoJson headers but instead rely on wled.h for ArduinoJson symbols. The standard pattern is to include wled.h and use JsonObject, JsonArray, DynamicJsonDocument, etc. without additional includes.

Applied to files:

  • usermods/i2c_encoder_button/usermod_i2c_encoder_button.cpp
📚 Learning: 2025-08-26T11:51:21.817Z
Learnt from: DedeHai
Repo: wled/WLED PR: 4798
File: wled00/FX.cpp:7531-7533
Timestamp: 2025-08-26T11:51:21.817Z
Learning: In WLED PR `#4798`, DedeHai confirmed that certain gamma-related calls in FX.cpp/FX_fcn.cpp/particle systems are intentional for effect-level shaping (e.g., brightness curves, TV sim, Pride 2015 pre-mix), distinct from final output gamma. Do not flag or remove these in future reviews; add comments when feasible to clarify intent.

Applied to files:

  • usermods/i2c_encoder_button/usermod_i2c_encoder_button.cpp
📚 Learning: 2026-01-13T03:07:13.582Z
Learnt from: BobLoeffler68
Repo: wled/WLED PR: 5293
File: usermods/user_fx/user_fx.cpp:167-171
Timestamp: 2026-01-13T03:07:13.582Z
Learning: In WLED effects (usermods/user_fx/user_fx.cpp and similar), when storing future timestamps, assignments like `stored_time = now + delay` are correct and handle uint32_t wraparound properly. Only comparison statements need to use subtraction-based elapsed-time checks (e.g., `(now - stored_time) >= delay`). Do not flag assignment statements as needing fixes for wraparound handling.

Applied to files:

  • usermods/i2c_encoder_button/usermod_i2c_encoder_button.cpp
📚 Learning: 2025-08-31T03:38:14.114Z
Learnt from: BobLoeffler68
Repo: wled/WLED PR: 4891
File: wled00/FX.cpp:3333-3349
Timestamp: 2025-08-31T03:38:14.114Z
Learning: WLED PacMan effect (wled00/FX.cpp): Keep pacmancharacters_t position fields as signed int (not int16_t). Maintainer preference (blazoncek) prioritizes avoiding potential overhead/regressions over minor RAM savings. Avoid type shrinking here unless memory pressure is demonstrated.

Applied to files:

  • usermods/i2c_encoder_button/usermod_i2c_encoder_button.cpp
📚 Learning: 2025-11-14T13:37:11.994Z
Learnt from: softhack007
Repo: wled/WLED PR: 0
File: :0-0
Timestamp: 2025-11-14T13:37:11.994Z
Learning: In WLED code reviews, when code is modified or added, look for "magic numbers" (hardcoded numeric literals) and suggest replacing them with appropriate defined constants when those constants are meaningful in the context of the PR. For example, the hardcoded value 32 should be replaced with WLED_MAX_SEGNAME_LEN when it represents a segment name length limit. This improves code maintainability and reduces the risk of inconsistencies.

Applied to files:

  • usermods/i2c_encoder_button/usermod_i2c_encoder_button.cpp
  • wled00/pin_manager.h
📚 Learning: 2026-01-01T13:18:14.738Z
Learnt from: softhack007
Repo: wled/WLED PR: 5251
File: usermods/user_fx/user_fx.cpp:225-227
Timestamp: 2026-01-01T13:18:14.738Z
Learning: In WLED effects, when computing time differences between unsigned long timestamps (e.g., `strip.now`, `lastBumpUpdate`), always cast the subtraction result to `int` before converting to `float` to ensure correct behavior on ESP32-C3 (RISC-V). Use pattern: `float(int(timestamp1 - timestamp2))` instead of `float(timestamp1 - timestamp2)`.

Applied to files:

  • usermods/i2c_encoder_button/usermod_i2c_encoder_button.cpp
📚 Learning: 2025-11-14T13:37:30.955Z
Learnt from: softhack007
Repo: wled/WLED PR: 0
File: :0-0
Timestamp: 2025-11-14T13:37:30.955Z
Learning: In WLED code reviews, when code is modified or added, look for "magic numbers" (hardcoded numeric literals) and suggest replacing them with defined constants when meaningful constants exist in the codebase. For example, suggest replacing hardcoded "32" with WLED_MAX_SEGNAME_LEN if the context relates to segment name length limits.

Applied to files:

  • usermods/i2c_encoder_button/usermod_i2c_encoder_button.cpp
📚 Learning: 2025-08-28T08:09:20.630Z
Learnt from: mval-sg
Repo: wled/WLED PR: 4876
File: wled00/xml.cpp:0-0
Timestamp: 2025-08-28T08:09:20.630Z
Learning: The WLED codebase has opportunities for refactoring hardcoded array bounds (like the "15" used for DMX channels) to use sizeof(array)/sizeof(array[0]) for more maintainable code, but such changes should be done consistently across the entire codebase in a dedicated refactoring effort.

Applied to files:

  • usermods/i2c_encoder_button/usermod_i2c_encoder_button.cpp
📚 Learning: 2026-01-05T15:54:56.339Z
Learnt from: broccoliboy
Repo: wled/WLED PR: 5243
File: usermods/i2c_encoder_button/usermod_i2c_encoder_button.cpp:18-20
Timestamp: 2026-01-05T15:54:56.339Z
Learning: The DUPPA I2C Encoder V2.1 uses hardware jumpers to configure its I2C address, with a default address of 0x00 when no jumpers are soldered. This is different from the I2C Encoder Mini which uses software-configurable addressing with a default of 0x20.

Applied to files:

  • usermods/i2c_encoder_button/usermod_i2c_encoder_button.cpp
📚 Learning: 2025-09-16T18:08:42.848Z
Learnt from: DedeHai
Repo: wled/WLED PR: 4939
File: wled00/FX_fcn.cpp:1176-1187
Timestamp: 2025-09-16T18:08:42.848Z
Learning: In WLED finalizeInit() bus creation (wled00/FX_fcn.cpp), intentionally allowing memory overruns when bus configurations exceed MAX_LED_MEMORY is a deliberate design choice. The trade-off prioritizes creating buses with reduced LED counts over completely failing to create buses, which would cause no LED output and UI failures. This approach forces users to update configurations after migrating to version 0.16 while maintaining basic functionality.

Applied to files:

  • usermods/i2c_encoder_button/usermod_i2c_encoder_button.cpp
📚 Learning: 2025-09-18T03:17:30.107Z
Learnt from: willmmiles
Repo: wled/WLED PR: 4939
File: wled00/FX_fcn.cpp:1183-1194
Timestamp: 2025-09-18T03:17:30.107Z
Learning: In WLED BusManager (wled00/FX_fcn.cpp), direct access to BusManager::busses is part of the intended public API design, not an encapsulation violation. The wrapper accessor methods are considered unnecessary obfuscation that should be removed in future refactoring.

Applied to files:

  • usermods/i2c_encoder_button/usermod_i2c_encoder_button.cpp
📚 Learning: 2026-01-05T18:35:03.147Z
Learnt from: softhack007
Repo: wled/WLED PR: 5276
File: wled00/file.cpp:0-0
Timestamp: 2026-01-05T18:35:03.147Z
Learning: In WLED's file.cpp, the static File object `f` cannot be nullptr (it's not a pointer). The `if (f)` check only tests whether the file handle is open, and this check is already performed inside the `f.close()` function. Therefore, there is no need for an explicit `if (f)` check before calling `f.close()` - double-checking is redundant.

Applied to files:

  • usermods/i2c_encoder_button/usermod_i2c_encoder_button.cpp
📚 Learning: 2025-09-18T03:17:30.107Z
Learnt from: willmmiles
Repo: wled/WLED PR: 4939
File: wled00/FX_fcn.cpp:1183-1194
Timestamp: 2025-09-18T03:17:30.107Z
Learning: In WLED finalizeInit() bus creation (wled00/FX_fcn.cpp), digitalCount is intentionally incremented even for placeholder buses to maintain consistent resource mapping across all bus configurations.

Applied to files:

  • usermods/i2c_encoder_button/usermod_i2c_encoder_button.cpp
📚 Learning: 2026-01-13T21:23:35.477Z
Learnt from: DedeHai
Repo: wled/WLED PR: 0
File: :0-0
Timestamp: 2026-01-13T21:23:35.477Z
Learning: In WLED, the global `paletteBlend` variable (wled.h:603) and the `WS2812FX::paletteBlend` member (FX.h:940) are duplicates without synchronization code. The global is loaded/saved in cfg.cpp and set via UI in set.cpp, but never copied to the strip member. This is the only such case in the codebase; other settings are either strip-only members (autoSegments, correctWB, cctFromRgb, isMatrix) or global-only (gammaCorrectCol/Bri/Val, blendingStyle).

Applied to files:

  • usermods/i2c_encoder_button/usermod_i2c_encoder_button.cpp
📚 Learning: 2025-09-18T03:17:30.107Z
Learnt from: willmmiles
Repo: wled/WLED PR: 4939
File: wled00/FX_fcn.cpp:1183-1194
Timestamp: 2025-09-18T03:17:30.107Z
Learning: In WLED bus management code, dead code branches in error handling (like unreachable else breaks) are intentionally preserved to accommodate potential future API changes, even when currently unreachable.

Applied to files:

  • usermods/i2c_encoder_button/usermod_i2c_encoder_button.cpp
📚 Learning: 2025-09-01T10:26:17.959Z
Learnt from: mval-sg
Repo: wled/WLED PR: 4876
File: wled00/wled_eeprom.cpp:0-0
Timestamp: 2025-09-01T10:26:17.959Z
Learning: In WLED PR `#4876`, the DMXStartLED EEPROM backward compatibility issue was partially addressed by keeping it at address 2550 and reading it as a 16-bit value, with DMXChannelsValue array moved to addresses 2552-2566. This maintains compatibility with pre-0.11 EEPROM layouts for DMXStartLED, though legacy "Set to 255" (code 6) configurations may still need migration logic.

Applied to files:

  • usermods/i2c_encoder_button/usermod_i2c_encoder_button.cpp
📚 Learning: 2025-11-20T00:04:04.829Z
Learnt from: netmindz
Repo: wled/WLED PR: 5093
File: wled00/util.cpp:1159-1182
Timestamp: 2025-11-20T00:04:04.829Z
Learning: In WLED PR `#5093`, the deviceId feature is designed for opt-in usage reporting that tracks only version/upgrade information (non-behavioral data), not user activity patterns. The deterministic salt approach (MAC + "WLED" + chip model/revision) is acceptable for this limited use case, as correlating MAC addresses to version history represents minimal privacy risk compared to behavioral tracking.

Applied to files:

  • usermods/i2c_encoder_button/usermod_i2c_encoder_button.cpp
📚 Learning: 2025-11-14T13:37:30.955Z
Learnt from: softhack007
Repo: wled/WLED PR: 0
File: :0-0
Timestamp: 2025-11-14T13:37:30.955Z
Learning: In WLED code reviews, verify that file operations (especially file.open()) respect LittleFS filename limitations. Assume default WLED configuration with LittleFS default filename limit of 255 bytes. Do not assume extreme configuration values like WLED_MAX_SEGNAME_LEN = 512 which would not be standard configurations.

Applied to files:

  • usermods/i2c_encoder_button/usermod_i2c_encoder_button.cpp
📚 Learning: 2025-04-27T10:06:22.545Z
Learnt from: KrX3D
Repo: wled/WLED PR: 4585
File: usermods/seven_segment_display_reloaded_v2/seven_segment_display_reloaded_v2.h:121-137
Timestamp: 2025-04-27T10:06:22.545Z
Learning: In the Seven Segment Display Reloaded usermod, the dimension mismatch between the default array (umSSDRNumbers[11][7]) and the override (umSSDRNumbers[11][10] = umSSDR_NUMBERS) is intentional by design, allowing for different use cases with varying numbers of segments per digit.

Applied to files:

  • wled00/pin_manager.h
🧬 Code graph analysis (1)
wled00/pin_manager.h (2)
usermods/pixels_dice_tray/pixels_dice_tray.cpp (1)
  • USERMOD_ID_PIXELS_DICE_TRAY (528-528)
usermods/i2c_encoder_button/usermod_i2c_encoder_button.cpp (1)
  • USERMOD_ID_I2C_ENCODER_BUTTON (197-197)
🪛 Cppcheck (2.19.0)
usermods/i2c_encoder_button/usermod_i2c_encoder_button.cpp

[error] 200-200: There is an unknown macro here somewhere. Configuration is required. If PROGMEM is a macro then please configure it.

(unknownMacro)

🔇 Additional comments (8)
wled00/pin_manager.h (1)

72-73: LGTM!

The new UM_I2C_ENCODER_BUTTON enum entry follows the established pattern in this file, correctly references the usermod ID from const.h, and the comment accurately documents the pin ownership (interrupt pin + standard HW_I2C pins).

usermods/i2c_encoder_button/usermod_i2c_encoder_button.cpp (7)

1-14: LGTM!

Includes and default configuration follow WLED patterns. The default I2C address of 0x00 correctly reflects the DUPPA I2C Encoder V2.1 hardware behavior when no address jumpers are soldered. Based on learnings, this is distinct from the I2C Encoder Mini which defaults to 0x20.


17-41: LGTM!

Class members are well-organized with appropriate types. The brightnessDelta is correctly typed as int8_t to support both positive and negative adjustments. State tracking variables cover the necessary button press logic.


42-110: LGTM!

The helper methods implement clean logic:

  • Adaptive brightness delta at low levels is good UX
  • Effect rollover using MODE_COUNT is correct
  • All event handlers are called through loop() which guards against null encoder_p

140-156: LGTM!

The encoder initialization sequence is thorough - resets the device, configures operational modes, sets counter bounds, and enables appropriate interrupts for rotation and button events.


167-168: Verify: Long press fires repeatedly while button is held.

The current logic calls handleEncoderLongButtonPress() whenever buttonPressDuration > buttonLongPressThreshold. Inside that handler, buttonPressStartTime is reset to millis(), which causes the handler to fire again after each threshold interval while the button remains held.

This creates a "repeat while holding" behavior that cycles through encoder modes. If this is intentional for UX, consider adding a comment clarifying the design. If a single-fire long press was intended, the handler should set a flag to prevent re-triggering until button release.


172-195: LGTM!

The JSON configuration methods follow standard WLED usermod patterns. Config values are properly serialized with defaults specified in readFromConfig().


197-203: LGTM!

The getId() correctly returns USERMOD_ID_I2C_ENCODER_BUTTON as defined in const.h, and the registration follows the standard WLED v2 usermod pattern. The static analysis hint about PROGMEM is a false positive—it's a standard ESP8266/ESP32 macro for storing data in flash memory.

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.


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
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: 5

🧹 Nitpick comments (1)
usermods/i2c_encoder_button/usermod_i2c_encoder_button.cpp (1)

104-127: Consider guarding setup() with enabled check and using WLED's I2C initialization.

  1. setup() initializes the encoder hardware even when enabled is false, which wastes resources and may cause issues if no hardware is connected.

  2. Calling Wire.begin(sdaPin, sclPin) directly may conflict with other I2C usermods or WLED's global I2C bus. Consider using WLED's PinManager to allocate pins and checking if I2C is already initialized.

🔎 Proposed fix
 void setup() override {
+    if (!enabled) return;
     // (Re)initialize encoder with current config
     if (encoder_p) delete encoder_p;
     encoder_p = new i2cEncoderLibV2(i2cAddress);
+    if (!PinManager::allocatePin(intPin, false, PinOwner::UM_Unspecified) ||
+        !PinManager::allocatePin(sdaPin, false, PinOwner::HW_I2C) ||
+        !PinManager::allocatePin(sclPin, false, PinOwner::HW_I2C)) {
+        enabled = false;
+        return;
+    }
     pinMode(intPin, INPUT);
     Wire.begin(sdaPin, sclPin);
📜 Review details

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 787d8a7 and 0d2ae55.

📒 Files selected for processing (5)
  • usermods/i2c_encoder_button/README.md
  • usermods/i2c_encoder_button/library.json
  • usermods/i2c_encoder_button/platformio_override.sample.ini
  • usermods/i2c_encoder_button/usermod_i2c_encoder_button.cpp
  • wled00/const.h
🧰 Additional context used
📓 Path-based instructions (1)
wled00/**/!(html_*)*.h

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Use 2-space indentation for non-generated C++ header files (.h)

Files:

  • wled00/const.h
🧠 Learnings (7)
📓 Common learnings
Learnt from: willmmiles
Repo: wled/WLED PR: 4623
File: usermods/word-clock-matrix/word-clock-matrix.cpp:332-332
Timestamp: 2025-03-29T01:22:54.617Z
Learning: In the WLED project, hardcoded usermod IDs (like 500 in the WordClockMatrix's getId() method) are intentionally used as part of a strategy to avoid modifying core code. There are plans to remove numeric usermod IDs entirely in the future.
📚 Learning: 2025-03-29T01:22:54.617Z
Learnt from: willmmiles
Repo: wled/WLED PR: 4623
File: usermods/word-clock-matrix/word-clock-matrix.cpp:332-332
Timestamp: 2025-03-29T01:22:54.617Z
Learning: In the WLED project, hardcoded usermod IDs (like 500 in the WordClockMatrix's getId() method) are intentionally used as part of a strategy to avoid modifying core code. There are plans to remove numeric usermod IDs entirely in the future.

Applied to files:

  • wled00/const.h
  • usermods/i2c_encoder_button/README.md
  • usermods/i2c_encoder_button/usermod_i2c_encoder_button.cpp
📚 Learning: 2025-11-14T13:37:30.955Z
Learnt from: softhack007
Repo: wled/WLED PR: 0
File: :0-0
Timestamp: 2025-11-14T13:37:30.955Z
Learning: In WLED code reviews, when code is modified or added, look for "magic numbers" (hardcoded numeric literals) and suggest replacing them with defined constants when meaningful constants exist in the codebase. For example, suggest replacing hardcoded "32" with WLED_MAX_SEGNAME_LEN if the context relates to segment name length limits.

Applied to files:

  • wled00/const.h
📚 Learning: 2025-11-14T13:37:11.994Z
Learnt from: softhack007
Repo: wled/WLED PR: 0
File: :0-0
Timestamp: 2025-11-14T13:37:11.994Z
Learning: In WLED code reviews, when code is modified or added, look for "magic numbers" (hardcoded numeric literals) and suggest replacing them with appropriate defined constants when those constants are meaningful in the context of the PR. For example, the hardcoded value 32 should be replaced with WLED_MAX_SEGNAME_LEN when it represents a segment name length limit. This improves code maintainability and reduces the risk of inconsistencies.

Applied to files:

  • wled00/const.h
📚 Learning: 2025-11-30T15:29:00.726Z
Learnt from: DedeHai
Repo: wled/WLED PR: 4456
File: usermods/deep_sleep/deep_sleep.cpp:224-230
Timestamp: 2025-11-30T15:29:00.726Z
Learning: In WLED, `touchThreshold` is a global variable defined in wled00/wled.h and is accessible to all usermods. It stores the touch sensitivity threshold for touch-capable GPIO pins on ESP32 variants and should not be flagged as undefined when used in usermods.

Applied to files:

  • wled00/const.h
📚 Learning: 2025-10-05T15:24:05.545Z
Learnt from: CR
Repo: wled/WLED PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-10-05T15:24:05.545Z
Learning: Applies to platformio.ini : Use platformio.ini as the single source of truth for hardware build targets and settings

Applied to files:

  • usermods/i2c_encoder_button/platformio_override.sample.ini
📚 Learning: 2025-08-29T00:26:15.808Z
Learnt from: ksedgwic
Repo: wled/WLED PR: 4883
File: usermods/usermod_v2_skystrip/rest_json_client.h:6-14
Timestamp: 2025-08-29T00:26:15.808Z
Learning: WLED uses a vendored ArduinoJson library (version 6) located at "src/dependencies/json/ArduinoJson-v6.h" which is included through wled.h. Usermods should not directly include ArduinoJson headers but instead rely on wled.h for ArduinoJson symbols. The standard pattern is to include wled.h and use JsonObject, JsonArray, DynamicJsonDocument, etc. without additional includes.

Applied to files:

  • usermods/i2c_encoder_button/library.json
  • usermods/i2c_encoder_button/usermod_i2c_encoder_button.cpp
🪛 LanguageTool
usermods/i2c_encoder_button/README.md

[grammar] ~3-~3: Ensure spelling is correct
Context: # usermod_i2c_encoder This usermod enables the use of a [DUPPA I2CEncoder ...

(QB_NEW_EN_ORTHOGRAPHY_ERROR_IDS_1)


[grammar] ~25-~25: Ensure spelling is correct
Context: ...RGB LED to illuminate the shaft and any know attached to it. - This is the encoder...

(QB_NEW_EN_ORTHOGRAPHY_ERROR_IDS_1)

🪛 markdownlint-cli2 (0.18.1)
usermods/i2c_encoder_button/README.md

26-26: Bare URL used

(MD034, no-bare-urls)

🔇 Additional comments (3)
wled00/const.h (1)

210-210: LGTM!

The new usermod ID follows the sequential numbering convention and the comment format is consistent with existing entries. Based on learnings, hardcoded usermod IDs are intentional in WLED.

usermods/i2c_encoder_button/platformio_override.sample.ini (1)

1-19: LGTM!

The sample configuration is well-documented with helpful comments about pin conflicts. The environment structure properly extends base configurations and demonstrates both ESP8266 and ESP32 setups.

usermods/i2c_encoder_button/usermod_i2c_encoder_button.cpp (1)

141-171: LGTM!

The JSON configuration methods follow WLED conventions, and the usermod registration is correct. The getId() properly returns the constant defined in const.h.

@softhack007 softhack007 changed the title Usermod I2C Encoder Usermod I2C Rotary Encoder Dec 30, 2025
@broccoliboy
Copy link
Contributor Author

Working through coderabbit suggestions... Give me a few days to polish and test.

@softhack007
Copy link
Member

@broccoliboy some automated builds are failing, looks like there is still a typo in your code

usermods/i2c_encoder_button/usermod_i2c_encoder_button.cpp:167:40: note: suggested alternative: 'USERMOD_ID_I2C_ENCODER_BUTTON' 
 uint16_t getId() override { return USERMOD_I2C_ENCODER_BUTTON; } 
^~~~~~~~~~~~~~~~~~~~~~~~~~ 
 USERMOD_ID_I2C_ENCODER_BUTTON

Copy link
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: 3

Fix all issues with AI Agents 🤖
In @usermods/i2c_encoder_button/usermod_i2c_encoder_button.cpp:
- Line 107: Remove the redundant Wire.begin(sdaPin, sclPin) call from the
usermod setup() (the re-initialization of the global Wire bus); rely on WLED's
global Wire initialization instead and delete that single Wire.begin(...) line
so you don't conflict with cfg.cpp/set.cpp warnings about bus re-initialisation
and Wire.setPins() failing when already initialized.
- Around line 18-20: The default I2C address macro I2C_ENCODER_DEFAULT_ADDRESS
is currently set to 0x00; update the definition in
usermod_i2c_encoder_button.cpp so the macro I2C_ENCODER_DEFAULT_ADDRESS is
defined as 0x20 (7-bit default address for DUPPA I2CEncoder V2.1) to match the
stock hardware.
🧹 Nitpick comments (1)
usermods/i2c_encoder_button/usermod_i2c_encoder_button.cpp (1)

44-46: Consider using PinManager for pin allocation.

WLED usermods typically use PinManager to allocate GPIO pins, ensuring they aren't used by multiple components. This would prevent conflicts with LED outputs or other usermods.

Example pattern

In setup(), after verifying enabled:

// Allocate pins
if (!pinManager.allocatePin(intPin, true, PinOwner::UM_I2CEncoder) ||
    !pinManager.allocatePin(sdaPin, true, PinOwner::UM_I2CEncoder) ||
    !pinManager.allocatePin(sclPin, true, PinOwner::UM_I2CEncoder)) {
    enabled = false;
    return; // Pin allocation failed
}

And deallocate in a cleanup method or when disabling.

📜 Review details

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0d2ae55 and c109632.

📒 Files selected for processing (4)
  • usermods/i2c_encoder_button/README.md
  • usermods/i2c_encoder_button/platformio_override.sample.ini
  • usermods/i2c_encoder_button/usermod_i2c_encoder_button.cpp
  • wled00/const.h
🚧 Files skipped from review as they are similar to previous changes (1)
  • usermods/i2c_encoder_button/platformio_override.sample.ini
🧰 Additional context used
📓 Path-based instructions (1)
wled00/**/!(html_*)*.h

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Use 2-space indentation for non-generated C++ header files (.h)

Files:

  • wled00/const.h
🧠 Learnings (9)
📓 Common learnings
Learnt from: willmmiles
Repo: wled/WLED PR: 4623
File: usermods/word-clock-matrix/word-clock-matrix.cpp:332-332
Timestamp: 2025-03-29T01:22:54.617Z
Learning: In the WLED project, hardcoded usermod IDs (like 500 in the WordClockMatrix's getId() method) are intentionally used as part of a strategy to avoid modifying core code. There are plans to remove numeric usermod IDs entirely in the future.
📚 Learning: 2025-03-29T01:22:54.617Z
Learnt from: willmmiles
Repo: wled/WLED PR: 4623
File: usermods/word-clock-matrix/word-clock-matrix.cpp:332-332
Timestamp: 2025-03-29T01:22:54.617Z
Learning: In the WLED project, hardcoded usermod IDs (like 500 in the WordClockMatrix's getId() method) are intentionally used as part of a strategy to avoid modifying core code. There are plans to remove numeric usermod IDs entirely in the future.

Applied to files:

  • wled00/const.h
  • usermods/i2c_encoder_button/README.md
  • usermods/i2c_encoder_button/usermod_i2c_encoder_button.cpp
📚 Learning: 2025-11-14T13:37:30.955Z
Learnt from: softhack007
Repo: wled/WLED PR: 0
File: :0-0
Timestamp: 2025-11-14T13:37:30.955Z
Learning: In WLED code reviews, when code is modified or added, look for "magic numbers" (hardcoded numeric literals) and suggest replacing them with defined constants when meaningful constants exist in the codebase. For example, suggest replacing hardcoded "32" with WLED_MAX_SEGNAME_LEN if the context relates to segment name length limits.

Applied to files:

  • wled00/const.h
  • usermods/i2c_encoder_button/usermod_i2c_encoder_button.cpp
📚 Learning: 2025-11-14T13:37:11.994Z
Learnt from: softhack007
Repo: wled/WLED PR: 0
File: :0-0
Timestamp: 2025-11-14T13:37:11.994Z
Learning: In WLED code reviews, when code is modified or added, look for "magic numbers" (hardcoded numeric literals) and suggest replacing them with appropriate defined constants when those constants are meaningful in the context of the PR. For example, the hardcoded value 32 should be replaced with WLED_MAX_SEGNAME_LEN when it represents a segment name length limit. This improves code maintainability and reduces the risk of inconsistencies.

Applied to files:

  • wled00/const.h
  • usermods/i2c_encoder_button/usermod_i2c_encoder_button.cpp
📚 Learning: 2025-11-30T15:29:00.726Z
Learnt from: DedeHai
Repo: wled/WLED PR: 4456
File: usermods/deep_sleep/deep_sleep.cpp:224-230
Timestamp: 2025-11-30T15:29:00.726Z
Learning: In WLED, `touchThreshold` is a global variable defined in wled00/wled.h and is accessible to all usermods. It stores the touch sensitivity threshold for touch-capable GPIO pins on ESP32 variants and should not be flagged as undefined when used in usermods.

Applied to files:

  • wled00/const.h
📚 Learning: 2025-08-29T00:26:15.808Z
Learnt from: ksedgwic
Repo: wled/WLED PR: 4883
File: usermods/usermod_v2_skystrip/rest_json_client.h:6-14
Timestamp: 2025-08-29T00:26:15.808Z
Learning: WLED uses a vendored ArduinoJson library (version 6) located at "src/dependencies/json/ArduinoJson-v6.h" which is included through wled.h. Usermods should not directly include ArduinoJson headers but instead rely on wled.h for ArduinoJson symbols. The standard pattern is to include wled.h and use JsonObject, JsonArray, DynamicJsonDocument, etc. without additional includes.

Applied to files:

  • usermods/i2c_encoder_button/usermod_i2c_encoder_button.cpp
📚 Learning: 2025-08-26T11:51:21.817Z
Learnt from: DedeHai
Repo: wled/WLED PR: 4798
File: wled00/FX.cpp:7531-7533
Timestamp: 2025-08-26T11:51:21.817Z
Learning: In WLED PR #4798, DedeHai confirmed that certain gamma-related calls in FX.cpp/FX_fcn.cpp/particle systems are intentional for effect-level shaping (e.g., brightness curves, TV sim, Pride 2015 pre-mix), distinct from final output gamma. Do not flag or remove these in future reviews; add comments when feasible to clarify intent.

Applied to files:

  • usermods/i2c_encoder_button/usermod_i2c_encoder_button.cpp
📚 Learning: 2025-08-31T03:38:14.114Z
Learnt from: BobLoeffler68
Repo: wled/WLED PR: 4891
File: wled00/FX.cpp:3333-3349
Timestamp: 2025-08-31T03:38:14.114Z
Learning: WLED PacMan effect (wled00/FX.cpp): Keep pacmancharacters_t position fields as signed int (not int16_t). Maintainer preference (blazoncek) prioritizes avoiding potential overhead/regressions over minor RAM savings. Avoid type shrinking here unless memory pressure is demonstrated.

Applied to files:

  • usermods/i2c_encoder_button/usermod_i2c_encoder_button.cpp
📚 Learning: 2026-01-01T13:18:14.738Z
Learnt from: softhack007
Repo: wled/WLED PR: 5251
File: usermods/user_fx/user_fx.cpp:225-227
Timestamp: 2026-01-01T13:18:14.738Z
Learning: In WLED effects, when computing time differences between unsigned long timestamps (e.g., `strip.now`, `lastBumpUpdate`), always cast the subtraction result to `int` before converting to `float` to ensure correct behavior on ESP32-C3 (RISC-V). Use pattern: `float(int(timestamp1 - timestamp2))` instead of `float(timestamp1 - timestamp2)`.

Applied to files:

  • usermods/i2c_encoder_button/usermod_i2c_encoder_button.cpp
🪛 LanguageTool
usermods/i2c_encoder_button/README.md

[grammar] ~3-~3: Ensure spelling is correct
Context: # usermod_i2c_encoder This usermod enables the use of a [DUPPA I2CEncoder ...

(QB_NEW_EN_ORTHOGRAPHY_ERROR_IDS_1)

🔇 Additional comments (4)
wled00/const.h (1)

210-210: LGTM! Usermod ID follows conventions.

The new USERMOD_ID_I2C_ENCODER_BUTTON constant is correctly added with the next sequential ID (59) and follows the established naming pattern with the _ID_ infix. The comment properly references the usermod name.

usermods/i2c_encoder_button/README.md (1)

1-37: Documentation looks good.

The README clearly documents the usermod's features, hardware requirements, and compilation instructions. Previous issues (typo and bare URL) have been resolved.

usermods/i2c_encoder_button/usermod_i2c_encoder_button.cpp (2)

126-138: LGTM! Loop implementation is sound.

The loop() method correctly guards against null encoder_p and disabled state (resolving a past review concern). The interrupt-driven approach with mode timeout provides good UX.


165-165: LGTM! Usermod ID is now correct.

The getId() method correctly returns USERMOD_ID_I2C_ENCODER_BUTTON, matching the constant defined in wled00/const.h. This resolves the compiler error mentioned in the PR comments.

@broccoliboy
Copy link
Contributor Author

CI is passing now, but please don't merge until I have some time to test on hardware. Thanks!

@blazoncek
Copy link
Contributor

I am wondering why:

  • you didn't reuse global I2C parameters; I2C is shared between several peripherals and WLED does initialisation of I2C hardware
  • you re-implemented rotary encoder code instead of updating existing (and very versatile) rotary encoder usermod

@broccoliboy
Copy link
Contributor Author

Good questions, and thank you for the comment.

For one, I originally wrote this usermod long before global i2c existed, but now that I'm trying to get it merged I plan on updating to use recent wled features like global i2c and pinmanager. This is a work in progress... I'm okay waiting on merge until I get this figured out.

For the second, this usermod uses i2c to interface with an external micro that's connected to the rotary encoder. I didn't reimplement any encoder logic. If there is an existing usermod that does this or has functions I can make use of, please let me know. I don't want to reinvent any wheels ;)

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.

3 participants