Aircoookie / WLED

Control WS2812B and many more types of digital RGB LEDs with an ESP8266 or ESP32 over WiFi!
https://kno.wled.ge
MIT License
14.57k stars 3.12k forks source link

Rotary Switch support #334

Closed sjude68 closed 4 years ago

sjude68 commented 4 years ago

@Aircoookie , I want to use a Rotary switch with button to control the LED Switch.

I added code in wled06_usermod.ino to read the rotary switch and update ledCount. The ledCount is updated on the webpage for the LEDs in the strip but not in the leds in the strip.

Which variable should I update in the code ?

Also I connected the switch to D0 and GND on the Nodemcu. Also enabled On/Off on the webpage. The strip does go ON or OFF using the switch.

` / This file allows you to add own functionality to WLED more easily See: https://github.com/Aircoookie/WLED/wiki/Add-own-functionality EEPROM bytes 2750+ are reserved for your custom use case. (if you extend #define EEPSIZE in wled01_eeprom.h) bytes 2400+ are currently ununsed, but might be used for future wled features /

//Use userVar0 and userVar1 (API calls &U0=,&U1=, uint16_t) unsigned long currentTime; unsigned long loopTime;

const int pin_A = D6; const int pin_B = D7;

unsigned char encoder_A; unsigned char encoder_B; unsigned char encoder_A_prev = 0;

//gets called once at boot. Do all initialization that doesn't depend on network here void userSetup() { pinMode(pin_A, INPUT_PULLUP); pinMode(pin_B, INPUT_PULLUP);

}

//gets called every time WiFi is (re-)connected. Initialize own network interfaces here void userConnected() {

}

//loop. You can use "if (WLED_CONNECTED)" to check for successful connection void userLoop() { currentTime = millis(); if (currentTime >= (loopTime + 5)) { encoder_A = digitalRead(pin_A); encoder_B = digitalRead(pin_B);

if ((!encoder_A) && (encoder_A_prev))
{

  if (encoder_B)
  {
    if (ledCount + 5 <= 255)ledCount += 5;
  }
  else
  {
    if (ledCount - 5 >= 0)ledCount -= 5;
  }
  Serial.println(ledCount);
}
encoder_A_prev = encoder_A;

//    analogWrite(ledPin, brightness);
//    Serial.println(brightness);
//    Serial.println(currentButton);
loopTime = currentTime;

} } `

Jory81 commented 4 years ago

Hi sjude68,

I am not a programmer, but maybe I can be of help. Number of leds is usually initialized as an array, which is only done once during setup. You can change the number of LEDs and save it to EEPROM, but you would need to reboot.

wled05_init.ino void wledInit() ledCount = EEPROM.read(229) + ((EEPROM.read(398) << 8) & 0xFF00);

A solution might be to keep the number of LEDs in the array the same as the length of your strip, but set the ones you would like to exclude to black. I am not sure how that can be done with this code.

Regards,

Aircoookie commented 4 years ago

Hi,

we can do re-initialisation of the leds at run time, but it is generally not a good idea as it can lead to memory leaks. Instead of updating ledCount, just set it to the maximum of LEDs you'd ever want, and use this code to set the segments of lit and black leds:

strip.setSegment(0, 0, amountLit);
strip.setSegment(1, amountLit, maxLeds);

I hope that will help!

sjude68 commented 4 years ago

@Aircoookie, I changed the code as suggested to variable amountLit. maxLeds = ledCount taken from the maximum Leds on the strip.

When I increase upto 30 or beyond 30 Leds the effects set are working. But when I reduce the Leds don't go black after 30. Below 30 they are working fine for + and -. The leds that get ON after 30 stay in their state of ON.

//Use userVar0 and userVar1 (API calls &U0=,&U1=, uint16_t) unsigned long currentTime; unsigned long loopTime;

const int pin_A = D6; const int pin_B = D7;

unsigned char encoder_A; unsigned char encoder_B; unsigned char encoder_A_prev = 0; int maxLeds = ledCount; int amountLit = 10;

//gets called once at boot. Do all initialization that doesn't depend on network here void userSetup() { pinMode(pin_A, INPUT_PULLUP); pinMode(pin_B, INPUT_PULLUP);

}

//gets called every time WiFi is (re-)connected. Initialize own network interfaces here void userConnected() {

}

//loop. You can use "if (WLED_CONNECTED)" to check for successful connection void userLoop() { currentTime = millis(); if (currentTime >= (loopTime + 5)) { encoder_A = digitalRead(pin_A); encoder_B = digitalRead(pin_B);

if ((!encoder_A) && (encoder_A_prev))
{

  if (encoder_B)
  {
    if (amountLit + 1 <= 200)amountLit += 1;
  }
  else
  {
    if (amountLit - 1 >= 0)amountLit -= 1;
  }
  Serial.println(amountLit);
}
encoder_A_prev = encoder_A;
strip.setSegment(0, 0, amountLit);
strip.setSegment(1, amountLit, maxLeds);
//    analogWrite(ledPin, brightness);
//    Serial.println(brightness);
//    Serial.println(currentButton);
loopTime = currentTime;

} }

Aircoookie commented 4 years ago

Hi @sjude68 could you update to the latest master? Your code should work without an issue then because the second segment is cleared to black when it is changed :)

ohminy commented 4 years ago

I have a similar issue with this.. I want to use rotary encoder to change effect mode so I coded like below It works, I can change the effect but, it does not works right way,, effect is changed but color is weired... and there is " TypeError: Failed to fetch " error in the WLED webserver... plz help.. ( my english is bed.. sorry )

`//Use userVar0 and userVar1 (API calls &U0=,&U1=, uint16_t)

unsigned long currentTime; unsigned long loopTime;

const int pinA = D6; //data const int pinB = D7; //clk int oldA = LOW; int oldB = LOW;

define RS_delay 5

//gets called once at boot. Do all initialization that doesn't depend on network here void userSetup() { pinMode(pinA, INPUT_PULLUP); pinMode(pinB, INPUT_PULLUP); }

//gets called every time WiFi is (re-)connected. Initialize own network interfaces here void userConnected() {

}

//loop. You can use "if (WLED_CONNECTED)" to check for successful connection void userLoop() { // bri = 0; currentTime = millis(); if (currentTime >= (loopTime + RS_delay)) { int A = digitalRead(pinA); int B = digitalRead(pinB);

if (A != oldA || B != oldB) {
  if (oldA == LOW && A == HIGH) {
    if (oldB == HIGH) {
      effectCurrent += 1;
      if (effectCurrent >= MODE_COUNT) effectCurrent = 0;
    }
    else {
      effectCurrent -= 1;
      if (effectCurrent < 0) effectCurrent = (MODE_COUNT-1);
    }
  }
}
oldA = A;
oldB = B;
strip.setEffectConfig(effectCurrent, effectSpeed, effectIntensity, effectPalette);
loopTime = currentTime;

} }`

ohminy commented 4 years ago

I answering for my question by my self,,,;;;; I found, I missed colorUpdated(int), so I added colorUpdated(6); now effects are works right!

thank you for your great job, again!!!

here is the code

//Use userVar0 and userVar1 (API calls &U0=,&U1=, uint16_t)

long lastTime = 0; int delayMs = 10; const int pinA = D6; //data const int pinB = D7; //clk int oldA = LOW; int oldB = LOW;

//gets called once at boot. Do all initialization that doesn't depend on network here void userSetup() { pinMode(pinA, INPUT_PULLUP); pinMode(pinB, INPUT_PULLUP); }

//gets called every time WiFi is (re-)connected. Initialize own network interfaces here void userConnected() {

}

//loop. You can use "if (WLED_CONNECTED)" to check for successful connection void userLoop() { if (millis()-lastTime > delayMs) { int A = digitalRead(pinA); int B = digitalRead(pinB);

if (A != oldA || B != oldB) {
  if (oldA == LOW && A == HIGH) {
    if (oldB == HIGH) {

// bri += 10; // if (bri > 250) bri = 10; effectCurrent += 1; if (effectCurrent >= MODE_COUNT) effectCurrent = 0; } else { // bri -= 10; // if (bri < 10) bri = 250; effectCurrent -= 1; if (effectCurrent < 0) effectCurrent = (MODE_COUNT-1); } } } oldA = A; oldB = B; //call for notifier -> 0: init 1: direct change 2: button 3: notification 4: nightlight 5: other (No notification) // 6: fx changed 7: hue 8: preset cycle 9: blynk 10: alexa colorUpdated(6); lastTime = millis(); } }

Mariu86 commented 4 years ago

You can document this and put in the usermode please, will be very useful for other people to find this in the usermode section.

ohminy commented 4 years ago

I have never done like that :) but I'll try for the first time..

stale[bot] commented 4 years ago

Hey! This issue has been open for quite some time without any new comments now. It will be closed automatically in a week if no further activity occurs. Thank you for using WLED!

George-Verboven commented 4 years ago

I created some time ago a led project with encoder and arduino nano. No i'm using WLED from Aircoookie with Wemos d1 mini.... Is it possible to change by example intensity, brightness and so on with rotary encoder together with WLED?

It would be nice if I could change height of flame change height of flame like https://www.youtube.com/watch?v=naRG1gws1Ds Or brightness/Dim function Dim with Rotary encoder like https://youtu.be/RJR2c7ck84o

sjude68 commented 4 years ago

@George-verbovin, what defuser did you use to put in the led strip?

George-Verboven commented 4 years ago

For my Led project I used an inner tube for better diffuse light and it gives me the possibility to dim the led by turning the outer tube (which I connected to a decoder). But if you don’t need that, maybe one big tube is enough. As a diffuser I used a Satin tube from acrylic light opal. https://www.pyrasied.nl/en/product/tube-xt-satin/ For a better impression, look at the small video how I put the tubes/diffuser together.

Defuser

Like https://youtu.be/Jg_mx__3uqY

For more detailed info of complete project I refer to: https://wled.discourse.group/t/light-design/197

Hope that someone can help me to get this working with WLED from Aircoookie with Wemos d1 mini and rotary encoder.

sjude68 commented 4 years ago

Check my previous comment for what code i used in usermod

https://github.com/Aircoookie/WLED/issues/334#issuecomment-552297738

George-Verboven commented 4 years ago

I am a rookie in using user mod .... in fact I have never done that and only use "ArduinoOTA" ..... But I would like to learn how to use the user mod ... Can you send me a link with instructions or explanation?

Thanks in advance for your help

George-Verboven commented 4 years ago

Installed visual studio code and added your code used in usermod. It's kind of working. Turning slowly the rotary encoder does change effect (so far so good) But I want to change Brightness, so changed your code to:

//Use userVar0 and userVar1 (API calls &U0=,&U1=, uint16_t)

long lastTime = 0;
int delayMs = 10;
const int pinA = D6; //data
const int pinB = D7; //clk
int oldA = LOW;
int oldB = LOW;

//gets called once at boot. Do all initialization that doesn't depend on network here
void userSetup()
{
pinMode(pinA, INPUT_PULLUP);
pinMode(pinB, INPUT_PULLUP);
}

//gets called every time WiFi is (re-)connected. Initialize own network interfaces here
void userConnected()
{

}

//loop. You can use "if (WLED_CONNECTED)" to check for successful connection
void userLoop()
{
if (millis()-lastTime > delayMs) {
int A = digitalRead(pinA);
int B = digitalRead(pinB);

if (A != oldA || B != oldB) {
  if (oldA == LOW && A == HIGH) {
    if (oldB == HIGH) {
bri += 5;
if (bri > 250) bri = 10;

}
else {
bri -= 5;
if (bri < 10) bri = 250;

}
}
}
oldA = A;
oldB = B;
//call for notifier -> 0: init 1: direct change 2: button 3: notification 4: nightlight 5: other (No notification)
// 6: fx changed 7: hue 8: preset cycle 9: blynk 10: alexa
colorUpdated(6);
lastTime = millis();
}
}

Turning rotary encoder does have effect on brightness, but not as smooth as I want. Sometimes nothing happens, then suddenly a big change. sometimes left turn does dim, other moments left turn does brighten. Change in value int delayMs = 10; and/or bri += 5, bri -= 5 doesn't solve it.

Do you have any tips to make the code work better?

I'm happy I learned how to use visual studio and usermod...... Yesterday I only knew how to Download BIN file and to upload with Arduino.

sjude68 commented 4 years ago

@George-Verboven glad to know my modification works for you. I am not an expert. But maybe @Aircoookie can help because wled is created by him.

You will get it to your perfection soon.

George-Verboven commented 4 years ago

With the help of google, some Copy / paste and some adjustments, I now have a perfectly working script.

I call it: "rotary_encoder_change_brightness"

This allows me to easily adjust the brightness of the LED strip by turning the rotary encoder. The pulses are neatly counted up or down.

To be honest it works much better than the script that is in the usermod on github: https://github.com/Aircoookie/WLED/tree/master/usermods

@aircookie, I think it would be very useful for other people to have this in the usermod section. However I don't know how to put it there.

But I came from far so maybe i can still get it done.

In any case, I am happy that it works on my WEMOS D1 mini 4mB.

Have a beer on it. Cheers.

//Use userVar0 and userVar1 (API calls &U0=,&U1=, uint16_t)

/*
** Rotary Encoder Example
** Use the Sparkfun Rotary Encoder to vary brightness of LED
**
** Sample the encoder at 500Hz using the millis() function
*/

int fadeAmount = 5;  // how many points to fade the Neopixel with each step
unsigned long currentTime;
unsigned long loopTime;
const int pinA = D6;  // DT from encoder
const int pinB = D7;  // CLK from encoder

unsigned char Enc_A;
unsigned char Enc_B;
unsigned char Enc_A_prev = 0;

//gets called once at boot. Do all initialization that doesn't depend on network here
void userSetup() {
  pinMode(pinA, INPUT_PULLUP);
  pinMode(pinB, INPUT_PULLUP);
  currentTime = millis();
  loopTime = currentTime;
}

//gets called every time WiFi is (re-)connected. Initialize own network interfaces here
void userConnected() {
}

//loop. You can use "if (WLED_CONNECTED)" to check for successful connection
void userLoop() {
  currentTime = millis();  // get the current elapsed time
  if(currentTime >= (loopTime + 2))  // 2ms since last check of encoder = 500Hz 
  {
    int Enc_A = digitalRead(pinA);  // Read encoder pins
    int Enc_B = digitalRead(pinB);   
    if((! Enc_A) && (Enc_A_prev)) { // A has gone from high to low
      if(Enc_B == HIGH) { // B is high so clockwise
        if(bri + fadeAmount <= 255) bri += fadeAmount;   // increase the brightness, dont go over 255

      } else if (Enc_B == LOW) { // B is low so counter-clockwise
        if(bri - fadeAmount >= 0) bri -= fadeAmount;   // decrease the brightness, dont go below 0          
      }   
    }   
      Enc_A_prev = Enc_A;     // Store value of A for next time    
      loopTime = currentTime;  // Updates loopTime

    //call for notifier -> 0: init 1: direct change 2: button 3: notification 4: nightlight 5: other (No notification)
    // 6: fx changed 7: hue 8: preset cycle 9: blynk 10: alexa
    colorUpdated(6);
  }
}

usermod.zip

Aircoookie commented 4 years ago

@George-Verboven awesome! I've committed your usermod :) The best way to make changes to it or add a new usermod is by a pull request, that will also give you credit as the author of the usermod and as a WLED contributor.

emardee commented 4 years ago

I've been talking about doing something like this for many years since I got my Digistump Oaks. However "life" means I never pushed the project forwards. I just finally dumped my own coding attempts on Particle, and have installed the excellent WLED onto my Oak today and excited by the posibilities. My old vision was to use rotary encoder with pushbutton centre as a "standard dimmer switch" on the wall (for easy use by those opposed to technology). There is great potential for WLED to include various modes of use for the rotary encoder to be configured by the GUI. Thinking dimmer by default, but short press takes you to rotary changing the colour wheel hue, and another short press takes you to effects etc etc. Equally an alternate layout could have the rotary itself changing what the rotary does, with short press selecting that "menu page". (Several different versions of user interface navigation are possible with a rotary and switch combo... and it is probably person preference which is better).

In my ideal world I'd probably use a few extra pixels in the light fitting to indicate which mode you are in at any point when operating the rotary. Keen to collaborate with others on making something like this happen. (I'd envisage starting with getting something to work with no GUI for it, then working on GUI intergration later). My first steps will be getting @George-Verboven 's code working in my house... then look to expand on it over time.

emardee commented 4 years ago

Just to show how long I've been procrastinating on this idea, here's the thread on digistump forums where I first outlined my idea in public nearly three years ago... (and that is having already planned ahead and built the required wires into the wall when building the kids rooms ready for this installation!).

That thread also has a little more about my thinking for those struggling to visualise what I'm planning.

I've done some more thinking about the next steps, and I think once I have George's code working as-is in my house... then the next step is abstracting the rotary code from George's example into generic terms. Then that means that each "menu page" (brightness or hue or whatever) becomes what is using the rotary code at that point (rather than duplicating that routine for every menu page). The live menu page then applies the up or down mapping onto the required variable (eg not hardcoded to "bri" as that variable changes depending on which "menu page"). I basically would need some other code which set which variable was being adjusted at that moment, and then need to be able to change the right variable. Probably this is by adding extra nested "if" lines which increment/decrement the right variable, based upon which rotarypage is selected.

I'm no coding expert, (some would say I know enough to be dangerous!), but I have some good basic knowledge and can google for more!

@Aircoookie Thanks for making this awesome software. I'm already loving what it does for my existing installs. (I have one run of LED pixels which has a faulty pixel, which shifts the colour from that pixel onwards. By setting the run into segments in your app and skipping a few pixels around the iffy one, I've been able to return the lights into service until I get round to soldering in one replacment LED pixel). So excited about what your great code makes possible in terms of finally realising my dream "rotary encoder" setup! Long way to go, but it suddenly feels possible rather than daunting!

George-Verboven commented 4 years ago

@emardee ,I do have a script for arduino and Teensy. You can dim by turning the rotary encoder. And when you push the button, it jumps to the next effect.

I quit at the time, because it did not work via WIFI and I did not have an APP with it, as is the case with WLED.

emardee commented 4 years ago

I was trying to decide the page change system to select which variable is being adjusted at any one time:

  1. Button Press takes you in and out of the menu tree eg alternating between selecting a variable to adjust, with next press returning to picking a menu page using the rotary. eg Start on rotary adjusts dimmer, to change to menu, press the button... rotary now switches between menupages, when on the one you want tp adjust, press button again... rotary now adjusts new varible chosen. eg something like this (where "<>" signifies a button press to reach that variable, and a button press to return to menu tree):

    (Not code, just used for formatting text in fixed layout)
    
           <> Dimmer
    ^      <> Warmer/Cooler White
    MenuTree   <> Rainbow Hue 
    v      <> Preset Favourite
           <> Effects List

OR

  1. Button Press to next page.... so scrolling through many pages takes many button presses. eg something like (where ">" is button press):
    Dimmer > Warmer/Cooler White > Rainbow Hue > Preset Favourite > Effects List > Back to Dimmer

Pros and cons with both methods. I used to favour method 2 for simplicity, but suspect method 1 will work better for my scenario. Your scenario sounds like a third method:

  1. Button Press takes you to the next effect. presumably with rotary doing the same thing for each effect.

Ideally I think the perfect rotary encoder code for WLED might be configurable for all of these scenarios from the web-gui (like button presses are already). Which would potentially allow for choosing which menupages are made available (to suit different needs), and which order they come in the tree... and also how you switch between pages (form the three above methods).

Of course trying to code for the ideal scenario to suit everyone's need might not be achieveable, so it makes sense to start with what I want myself, and see if I can incorporate others needs as we go along. Hoping to keep in mind portability of code though, so will delibrately try to code in such as way that future web-gui code could allow configuration without too much re-coding later on.

I've got some ideas, so will start coding method1 tonight and see where I get to, and then share my progress here.