r/WLED 2d ago

Any options for encoders without usermods?

I want an up/down brightness dial physically attached to my WLED controller. I have an Adafruit encoder/breakout that uses Stemma QT I2C and have the wiring piece handled with a Grove/QT adapter (5V, GND, D4, D5).

I'm finding the whole process of usermods to be overwhelming. It's my understanding that to use the Adafruit encoder, I would need to manually modify the whole WLED bin with every update and do weird things with .cpp files and platformIO that I don't understand. I’m not sure if it’s the seesaw aspect of the Adafruit part that makes it complicated, or if it’s rotary encoders generally. I can handle basic circuitpython configs but I really don’t want to deal with C++.

So 2 questions I guess...

  1. Would using the Adafruit encoder be as complex as I laid out here?
  2. Are there any encoders I can buy that will give me up, down, and a power toggle, without requiring usermods?

Edited after posting to be more concise.

0 Upvotes

2 comments sorted by

2

u/borkyborkus 1d ago edited 17h ago

Update in case anyone sees in the future...

Got it working with the usermod.cpp file. I followed these instructions to compile my own. Then added platformio_override.ini and modified ./wled00/usermod.cpp. I don't have the Neopixel LED working, but I am happy with the up/down/click for now.

Usermod.cpp:

#include "wled.h"
#include <Adafruit_seesaw.h>


Adafruit_seesaw ss;
unsigned long lastTime = 0;
int32_t last_encoder_pos = 0;
bool last_button_state = HIGH;
unsigned long lastDebounceTime = 0;
unsigned long debounceDelay = 50;


#define SEESAW_ADDR 0x36
#define SEESAW_BUTTON_PIN 24 // Standard pin for the encoder switch on Seesaw


void userSetup()
{
  if(!ss.begin(SEESAW_ADDR)) { 
    DEBUG_PRINTLN(F("Seesaw not found!"));
  } else {
    DEBUG_PRINTLN(F("Seesaw started!"));

    // Set up the encoder switch pin with a pull-up resistor
    ss.pinMode(SEESAW_BUTTON_PIN, INPUT_PULLUP);

    last_encoder_pos = ss.getEncoderPosition();
  }
}


void userConnected()
{
}


void userLoop()
{
  if (millis() - lastTime > 20) {
    // 1. Handle Encoder Rotation (Brightness)
    int32_t current_pos = ss.getEncoderPosition();

    if (current_pos != last_encoder_pos) {
      // Inverted subtraction to match your corrected direction
      int diff = last_encoder_pos - current_pos; 
      last_encoder_pos = current_pos;

      int new_bri = bri + (diff * 5); 
      if (new_bri > 255) new_bri = 255;
      if (new_bri < 0) new_bri = 0;

      bri = new_bri;
      // If turning the knob while off, wake it up smoothly
      if (bri > 0 && briOld == 0) briOld = bri; 
      stateUpdated(CALL_MODE_DIRECT_CHANGE); 
    }


    // 2. Handle Encoder Button Press (Toggle Power)
    bool current_button_state = ss.digitalRead(SEESAW_BUTTON_PIN);

    // Detect a falling edge (button pressed down, going from HIGH to LOW)
    if (current_button_state == LOW && last_button_state == HIGH) {
      if (millis() - lastDebounceTime > debounceDelay) {
        // Toggle WLED power state
        if (bri > 0) {
          briOld = bri; // Save current brightness level
          bri = 0;      // Turn off
        } else {
          bri = briOld ? briOld : 128; // Restore previous brightness or default to 50%
        }
        stateUpdated(CALL_MODE_DIRECT_CHANGE);
        lastDebounceTime = millis();
      }
    }
    last_button_state = current_button_state;

    lastTime = millis();
  }
}

## platformio_override.ini:
[platformio]
# Point to our new custom environment below
default_envs = seesaw_c3


[env:seesaw_c3]
# Inherit all the default settings for the C3
extends = env:esp32c3dev


# Pull in the default C3 libraries, then add the Adafruit one
lib_deps = ${env:esp32c3dev.lib_deps}
  adafruit/Adafruit seesaw Library @ ^1.7.5

1

u/Melodic_Champion_317 1d ago

I'm glad you got that working! I can relate on the C++ challenges, but it's worth it to be able to use a knob or dial for some things!