Rotary encoder dimming

Hi,

I want to migrate from espeasy to esphome for my rotary encoders but I have 2 issues that I cant resolve.

What I would like is the following:

  1. Turn a lights brightness down by value X on every click when tuning the rotary encoder to the left – Turn a lights brightness up by value X on every click when tuning the rotary encoder to the right.
  2. Change (step thru) the scenes of the (hue) light on a double click so that every time I do a long press, the next scene is activated.

The issue I can’t resolve is that te rotary encoder reports a value from -[virtually_endless] to +[virtually_endless]. With espeasy I was able to set a value from a lower limit of 0 to a upper limit of 100 and then use this value to set the lights brightness_pct. That worked perfectly.
Now I to need to have an automation that responds to the value changing negativ (turn left) or the value changing positive (turn right) and then on a right turn take the current light brightness_pct value and add +5 until a upper limit of 100… The same for the left turns with a lower limit of 0.

I found this post that is a move in the right direction, but since I am not a coder, I can’t quit figure it out.

This is what I have for now:

esphome:
  name: rotary01
  ##  living rotary encoder 1
  platform: ESP8266
  board: d1_mini

wifi:
  ssid: 'SSID'
  password: 'XXXXXXXXXXXXXXXXXXXX'

# Enable logging
logger:

# Enable Home Assistant API
api:
  password: 'XXXXXXXXX'

ota:
  password: 'XXXXXXXXX'

# encoder 1
sensor:
  - platform: rotary_encoder
    name: "Living Rotary 1"
    id: livingrotary1
    pin_a: D6
    pin_b: D7
    filters:
      - or:
        - debounce: 0.1s
        - delta: 10

### insert the mission code here ;)

binary_sensor:
  - platform: gpio
    pin: D5
    id: switch1
    name: "Living switch 1"        
    filters:
      - invert:   
    on_multi_click:
    - timing:
        - ON for at most 0.4s
        - OFF for at most 0.4s
        - ON for at most 0.4s
        - OFF for at least 0.2s
      then:
        - logger.log: "Double Clicked"
### enter code here ;)
    - timing:
        - ON for 0.6s to 3s
        - OFF for at least 0.3s
      then:
        - logger.log: "Single Long Clicked"
        - homeassistant.service:
            service: input_boolean.toggle
            data:
              entity_id: input_boolean.radio_woonkamer
    - timing:
        - ON for at most 0.5s
        - OFF for at least 0.4s
      then:
        - logger.log: "Single short Click"
        - homeassistant.service:
            service: light.toggle
            data:
              entity_id: light.woonkamer_groep
sensor:
  - platform: rotary_encoder
    name: "Living Rotary 1"
    id: livingrotary1
    pin_a: D6
    pin_b: D7
    filters:
      - or:
        - debounce: 0.1s
        - delta: 10
      - lambda: |-
          if (x  < 0.0) return 0.0;
          if (x > 100) return 100;
          return x;

Tried that, but that doesn’t work. If you limit the value to 100 like this and turn the encoder past the 100 limit, it will keep counting. So if you turn right 10 clicks past 100, you would have to turn left 10 clicks, before your value will lower to 99.

In this log I went up 10 clicks and then 10 clicks down again, using your suggestion:

[13:04:12][D][sensor.sensor:131]: 'Woonkamer 1 Rotary 1': Sending state 95.00000 steps with 0 decimals of accuracy
[13:04:15][D][sensor.sensor:131]: 'Woonkamer 1 Rotary 1': Sending state 96.00000 steps with 0 decimals of accuracy
[13:04:16][D][sensor.sensor:131]: 'Woonkamer 1 Rotary 1': Sending state 97.00000 steps with 0 decimals of accuracy
[13:04:16][D][sensor.sensor:131]: 'Woonkamer 1 Rotary 1': Sending state 98.00000 steps with 0 decimals of accuracy
[13:04:17][D][sensor.sensor:131]: 'Woonkamer 1 Rotary 1': Sending state 99.00000 steps with 0 decimals of accuracy
[13:04:18][D][sensor.sensor:131]: 'Woonkamer 1 Rotary 1': Sending state 100.00000 steps with 0 decimals of accuracy
[13:04:18][D][sensor.sensor:131]: 'Woonkamer 1 Rotary 1': Sending state 100.00000 steps with 0 decimals of accuracy
[13:04:18][D][sensor.sensor:131]: 'Woonkamer 1 Rotary 1': Sending state 100.00000 steps with 0 decimals of accuracy
[13:04:19][D][sensor.sensor:131]: 'Woonkamer 1 Rotary 1': Sending state 100.00000 steps with 0 decimals of accuracy
[13:04:20][D][sensor.sensor:131]: 'Woonkamer 1 Rotary 1': Sending state 100.00000 steps with 0 decimals of accuracy
[13:04:20][D][sensor.sensor:131]: 'Woonkamer 1 Rotary 1': Sending state 100.00000 steps with 0 decimals of accuracy
[13:04:21][D][sensor.sensor:131]: 'Woonkamer 1 Rotary 1': Sending state 100.00000 steps with 0 decimals of accuracy
[13:04:22][D][sensor.sensor:131]: 'Woonkamer 1 Rotary 1': Sending state 100.00000 steps with 0 decimals of accuracy
[13:04:23][D][sensor.sensor:131]: 'Woonkamer 1 Rotary 1': Sending state 100.00000 steps with 0 decimals of accuracy
[13:04:24][D][sensor.sensor:131]: 'Woonkamer 1 Rotary 1': Sending state 100.00000 steps with 0 decimals of accuracy
[13:04:25][D][sensor.sensor:131]: 'Woonkamer 1 Rotary 1': Sending state 100.00000 steps with 0 decimals of accuracy
[13:04:26][D][sensor.sensor:131]: 'Woonkamer 1 Rotary 1': Sending state 100.00000 steps with 0 decimals of accuracy
[13:04:27][D][sensor.sensor:131]: 'Woonkamer 1 Rotary 1': Sending state 99.00000 steps with 0 decimals of accuracy
[13:04:28][D][sensor.sensor:131]: 'Woonkamer 1 Rotary 1': Sending state 98.00000 steps with 0 decimals of accuracy
[13:04:29][D][sensor.sensor:131]: 'Woonkamer 1 Rotary 1': Sending state 97.00000 steps with 0 decimals of accuracy
[13:04:30][D][sensor.sensor:131]: 'Woonkamer 1 Rotary 1': Sending state 96.00000 steps with 0 decimals of accuracy
[13:04:30][D][sensor.sensor:131]: 'Woonkamer 1 Rotary 1': Sending state 95.00000 steps with 0 decimals of accuracy

How about this:

      - lambda: |-
          if (x  < 0.0) set x = 0.0;
          if (x > 100) set x = 100;
          return x;
sensor:
  - platform: rotary_encoder
    name: "Woonkamer 1 Rotary 1"
    id: woonkamer1rotary1
    pin_a: D6
    pin_b: D7
    filters:
      - or:
        - debounce: 0.1s
        - delta: 10
      - lambda: |-
          if (x  < 0.0) set x = 0.0;
          if (x > 100) set x = 100;
          return x;

It validates, but won’t compile:

Compiling .pioenvs/rotary01/src/main.cpp.o
src/main.cpp: In lambda function:
src/main.cpp:75:23: error: 'set' was not declared in this scope
if (x  < 0.0) set x = 0.0;
^
src/main.cpp:75:27: error: expected ';' before 'x'
if (x  < 0.0) set x = 0.0;
^
src/main.cpp:76:22: error: 'set' was not declared in this scope
if (x > 100) set x = 100;
^
src/main.cpp:76:26: error: expected ';' before 'x'
if (x > 100) set x = 100;
^
*** [.pioenvs/rotary01/src/main.cpp.o] Error 1
========================== [ERROR] Took 5.56 seconds ==========================

Lambdas are C++ and not validated…I think you’re missing curly braces and/or do you need “set”?

if (x < 0.0) { 
  x = 0.0;
}

tried it like this now:\

      - lambda: |-
          if (x < 0.0) { x = 0.0; }
          if (x > 100) { x = 100; }
          return x;

but the issue mentioned here: Rotary encoder dimming

is still the same…

I’m trying the same thing and saw that the min/max values are in the works currently:

So we just have to wait a bit or be brave and try the dev branch.

But the other question is: how do we get the rotary encoder’s value into the PWM output? I’m currently using the config below, but it has problems: the values set with the rotary encoder are different from the values that I can send with MQTT. With step 1 of the rotary encoder the LED is still off, and with step 2 it’s rather bright already. With MQTT I can send ‘{“state”:“ON”,“brightness”:38}’ and get a very dimly lit LED.

This is the config that I came up with:

sensor:
  - platform: rotary_encoder
    name: "Rotary Encoder"
    pin_a: 19
    pin_b: 18
    filters:
      - or:
        - debounce: 0.1s
        - delta: 10
      - lambda: |-
          if (x < 0.0) return 0.0;
          if (x > 255.0) return 255.0;
          return x;
    on_value:
      then:
        - output.set_level:
            id: pwm_output
            level: !lambda "return x/256.0;"
        - logger.log:
            format: "pwm_output set to %.1f%s"
            args: [ 'x*100/256.0', '"%"' ]
    
output:
  - platform: ledc
    pin: 27
    frequency: 1000 Hz
    id: pwm_output

light:
  - platform: monochromatic
    output: pwm_output
    name: "LED-Platte"
    id: led_id

I’m very new to esphome and Home Assistant. Maybe somebody can see what I did wrong?

I’ve not used a rotary encoder myself - but I would set the light brightness rather than the PWM output directly.

on_value:
  then:
    - light.turn_on:
        id: led_1
        brightness: !lambda |-
          return x / 255;
1 Like

Thank you, DeanoX, brightness works really good.

Alas when I turn the knob too fast (?) I get a “Guru Meditation Error”:

[12:56:10][D][sensor.sensor:131]: 'Rotary Encoder': Sending state 101.00000 steps with 0 decimals of accuracy
[12:56:10][D][light.state:462]: 'LED-Platte' Turning ON
[12:56:10][D][light.state:469]:   Brightness: 40%
[12:56:10][D][main:052]: brightness set to 39.61%
[12:56:11][D][sensor.sensor:131]: 'Rotary Encoder': Sending state 105.00000 steps with 0 decimals of accuracy
[12:56:11][D][light.state:462]: 'LED-Platte' Turning ON
[12:56:11][D][light.state:469]:   Brightness: 41%
[12:56:11][D][main:052]: brightness set to 41.18%
[12:56:11][D][sensor.sensor:131]: 'Rotary Encoder': Sending state 111.00000 steps with 0 decimals of accuracy
[12:56:11][D][light.state:462]: 'LED-Platte' Turning ON
[12:56:11][D][light.state:469]:   Brightness: 44%
[12:56:11]Guru Meditation Error: Core  1 panic'ed (Cache disabled but cached memory region accessed)
[12:56:11]Core 1 register dump:
[12:56:11]PC      : 0x40080f8d  PS      : 0x00060034  A0      : 0x8008104c  A1      : 0x3ffc0bb0  
INFO Need to fetch platformio IDE-data, please stand by
INFO Running:  platformio run -d esphome1 -t idedata
WARNING Decoded 0x40080f8d: esphome::sensor::RotaryEncoderSensor::process_state_machine_()
[12:56:13]A2      : 0x3ffb3550  A3      : 0x3ffb18c0  A4      : 0x80081edf  A5      : 0x3ffb18c0  
[12:56:13]A6      : 0x0000dea0  A7      : 0x3ffb196c  A8      : 0xbad00bad  A9      : 0x0000011b  
[12:56:13]A10     : 0x3ffb34f8  A11     : 0x3ffb18a0  A12     : 0x80081c80  A13     : 0x3ffb1890  
[12:56:13]A14     : 0x0000dea0  A15     : 0x3ffb196c  SAR     : 0x00000013  EXCCAUSE: 0x00000007  
[12:56:13]EXCVADDR: 0x00000000  LBEG    : 0x4000c2e0  LEND    : 0x4000c2f6  LCOUNT  : 0xffffffff  
[12:56:13]Core 1 was running in ISR context:
[12:56:13]EPC1    : 0x40062235  EPC2    : 0x00000000  EPC3    : 0x00000000  EPC4    : 0x40080f8d
[12:56:13]
[12:56:13]Backtrace: 0x40080f8d:0x3ffc0bb0 0x40081049:0x3ffc0bd0 0x40081099:0x3ffc0bf0 0x40081ac5:0x3ffc0c10 0x40062232:0x00000000
WARNING Found stack trace! Trying to decode it
WARNING Decoded 0x40080f8d: esphome::sensor::RotaryEncoderSensor::process_state_machine_()
WARNING Decoded 0x40081049: esphome::sensor::RotaryEncoderSensor::encoder_isr_()
WARNING Decoded 0x40081099: __onPinInterrupt at esp32-hal-gpio.c
WARNING Decoded 0x40081ac5: _xt_lowint1 at /Users/ficeto/Desktop/ESP32/ESP32/esp-idf-public/components/freertos/xtensa_vectors.S:1105
[12:56:13]
[12:56:13]Rebooting...

I already tried the beta and dev channels (with beta, it’s the same, with dev the rotary encoder doesn’t seem to do anything at the moment). I guess I try posting an issue at Github.

A little follow-up on my initial question:

I now have thing set up like this. I’m assuming a value from 0-25 (which can be limited in a future version of esphome)

sensor:
  - platform: rotary_encoder
    name: "Woonkamer 1 Rotary 1"
    id: woonkamer1rotary1
    pin_a: D6
    pin_b: D7
    filters:
      - or:
        - debounce: 0.1s
        - delta: 10
    on_value:
      then:
        - homeassistant.service:
            service: light.turn_on
            data_template:
              entity_id: light.woonkamer_groep
              brightness_pct: "{{ brightness_1 | int }}"
            variables:
              brightness_1: 'return id(woonkamer1rotary1).state * 4;'

It looks like Home Assistant can’t handle this on a (hue) group of 6 lights.

If I use this on a single light the result is fully acceptable, but on a group of 6 lights the result is that the lights dimm extremely slow. I can sometime take up till 5-10 seconds before the last service call actually reaches the lights. I am having the same problem when I handle the dimming in a home assistant automation like this:

  - id: woonkamer_2_dimmer_1
    alias: "woonkamer 2 dimmer 1"
    trigger:
    - platform: state
      entity_id: sensor.woonkamer_2_dimmer_1
    action:
      service: light.turn_on
      entity_id: light.woonkamer_groep
      data_template:
        brightness_pct: '{{states.sensor.woonkamer_2_dimmer_1.state | int*4}}'

Is there something I could try to optimize this?

1 Like

Thanks for posting this! I got it working with a group of two lights like so:

sensor:
  - platform: rotary_encoder
    name: "Rotary Encoder"
    id: bedroom_brightness
    pin_a: D5
    pin_b: D6
    pin_reset: D7
    filters:
      - or:
        - debounce: 0.1s
        - delta: 10
    on_value:
      then:
        - homeassistant.service:
            service: light.turn_on
            data_template:
              entity_id: group.my_bedroom_lights_group
              brightness_pct: "{{ brightness_1 | int }}"
            variables:
              brightness_1: 'return id(bedroom_brightness).state * 4;'

There is some lag with both light strips - but it wasn’t too bad. Also, I noticed that every time the light is turned off (brightness = 0) and then turned on, the color/temperature/hue is reset back to white. Can you think of a way to retain the light color or is it a home assistant setting?
I’m going to play around for a bit and see what else I can change.

I’m interested in flashing the esp8266 lights with ESPHome and see if they communicate faster using the API instead of MQTT.

Also, have you figured out how to use the “reset” pin as a button when you “click” it?

        - homeassistant.service:
            service: light.turn_on
            data_template:
              entity_id: group.my_bedroom_lights_group
              brightness_pct: "{{ brightness_1 | int }}"
            variables:
              brightness_1: 'return id(bedroom_brightness).state * 4;'

This part definitely stopped working for me after the update to 1.14 and I have been unable to get it working again with lambda as Otto suggested. I am no coder, so If anyone could help me out, it would be greatly appreciated.

I want to pass a value of 1 to 100 (percent) to the brightness_pct of a home assistant entity upon rotating the encoder.


        - homeassistant.service:
            service: light.turn_on
            data_template:
              entity_id: group.my_bedroom_lights_group
              brightness_pct: "{{ brightness_1 | int }}"
            variables:
              brightness_1: !lambda 'return id(bedroom_brightness).state * 4;'

thanks to a hint from Otto; this fixes the issue!

Hi, esphone rotary encoder sensor. I’m also using the rotary encoder to control the LED light of cold and warm color temperature and brightness adjustment recently. Can you share the complete profile you are currently using? Thank!

esphome:
  name: esp_rotary_1
  ## Woonkamer rotary controllers ingang
  platform: ESP8266
  board: d1_mini

wifi:
  ssid: !secret esphome_wifi_ssid
  password: !secret esphome_wifi_password
  
api:
  password: !secret esphome_api_password

ota:
  password: !secret esphome_ota_password
  
web_server:
  port: 80

logger:
  baud_rate: 0
  level: VERBOSE

sensor:
### Rotary encoder 1
  - platform: rotary_encoder
    name: "Woonkamer 1 Rotary 1"
    id: woonkamer1rotary1
    pin_a: GPIO12
    pin_b: GPIO13
    filters:
      - or:
        - debounce: 0.2s
        - delta: 5
    resolution: 1
    min_value: 1
    max_value: 10
    on_value:
      then:
        - homeassistant.service:
            service: light.turn_on
            data_template:
              entity_id: light.woonkamer_groep
              brightness_pct: "{{ brightness_1 | int }}"
            variables:
              brightness_1: !lambda 'return id(woonkamer1rotary1).state * 10;'
       
### Rotary encoder 2
  - platform: rotary_encoder
    name: "Woonkamer 1 Rotary 2"
    id: woonkamer1rotary2
    pin_a: GPIO5
    pin_b: GPIO4
    filters:
      - or:
        - debounce: 0.1s
        - delta: 3
    resolution: 1
    min_value: 1
    max_value: 25
    on_value:
      then:
        - homeassistant.service:
            service: light.turn_on
            data_template:
              entity_id: light.eettafel
              brightness_pct: "{{ brightness_1 | int }}"
            variables:
              brightness_1: !lambda 'return id(woonkamer1rotary2).state * 4;'
              
### Rotary encoder 3
  - platform: rotary_encoder
    name: "Woonkamer 1 Rotary 3"
    id: woonkamer1rotary3
    pin_a: GPIO1
    pin_b: GPIO3
    filters:
      - or:
        - debounce: 0.1s
        - delta: 3
    resolution: 1
    min_value: 1
    max_value: 25
    on_value:
      then:
        - homeassistant.service:
            service: light.turn_on
            data_template:
              entity_id: light.booglamp
              brightness_pct: "{{ brightness_1 | int }}"
            variables:
              brightness_1: !lambda 'return id(woonkamer1rotary3).state * 4;'

binary_sensor:
### Switch 1
  - platform: gpio
    pin: GPIO14
    id: switch1
    name: "Woonkamer 1 switch 1"        
    filters:
      - invert:   
    on_multi_click:
    - timing:
        - ON for at most 0.4s
        - OFF for at most 0.4s
        - ON for at most 0.4s
        - OFF for at least 0.2s
      then:
        - logger.log: "Double Clicked"
        - homeassistant.service:
            service: input_select.select_next
            data:
              entity_id: input_select.hue_scenes
    - timing:
        - ON for 0.6s to 3s
        - OFF for at least 0.3s
      then:
        - logger.log: "Single Long Clicked"
        - homeassistant.service:
            service: input_boolean.toggle
            data:
              entity_id: input_boolean.radio_woonkamer
    - timing:
        - ON for at most 0.5s
        - OFF for at least 0.4s
      then:
        - logger.log: "Single short Click"
        - homeassistant.service:
            service: light.toggle
            data:
              entity_id: light.woonkamer_groep
            
### Switch 2
  - platform: gpio
    pin: GPIO0
    id: switch2
    name: "Woonkamer 1 switch 2"        
    filters:
      - invert:   
    on_multi_click:
    - timing:
        - ON for at most 0.4s
        - OFF for at most 0.4s
        - ON for at most 0.4s
        - OFF for at least 0.2s
      then:
        - logger.log: "Double Clicked"
        - homeassistant.service:
            service: hue.hue_activate_scene
            data:
              group_name: "Woonkamer groep"
              scene_name: "Film"
    - timing:
        - ON for 0.6s to 3s
        - OFF for at least 0.3s
      then:
        - logger.log: "Single Long Clicked"
        - homeassistant.service:
            service: input_boolean.toggle
            data:
              entity_id: input_boolean.radio_woonkamer
    - timing:
        - ON for at most 0.5s
        - OFF for at least 0.4s
      then:
        - logger.log: "Single short Click"
        - homeassistant.service:
            service: light.toggle
            data:
              entity_id: light.eettafel
              
### Switch 3
  - platform: gpio
    pin: GPIO16
    id: switch3
    name: "Woonkamer 1 switch 3"        
    filters:
      - invert:   
    on_multi_click:
    - timing:
        - ON for at most 0.4s
        - OFF for at most 0.4s
        - ON for at most 0.4s
        - OFF for at least 0.2s
      then:
        - logger.log: "Double Clicked"
        - homeassistant.service:
            service: hue.hue_activate_scene
            data:
              group_name: "Woonkamer groep"
              scene_name: "Film"
    - timing:
        - ON for 0.6s to 3s
        - OFF for at least 0.3s
      then:
        - logger.log: "Single Long Clicked"
        - homeassistant.service:
            service: input_boolean.toggle
            data:
              entity_id: input_boolean.radio_woonkamer
    - timing:
        - ON for at most 0.5s
        - OFF for at least 0.4s
      then:
        - logger.log: "Single short Click"
        - homeassistant.service:
            service: light.toggle
            data:
              entity_id: light.booglamp

Here you go… :wink:

6 Likes

Thank you! Rotary Quantity 2?

Wow Its great

3 dimmer and multiple dimmer you great job.

thxs for your yaml

have a nice day.

Do you have the circuit diagram and components list with the dimmer module ? I am new to these and still struggling with components to use.