Push button dimmer - how?

Thanks heaps for your reply. I have heard of ESPHome however I am not sure how it works. I am guessing this code should work in HA configurator?
I understand your comments thank you. My only questions is what device do you connect your push button too?
I ask as I do not see any reference to a Device just a GPIO pin number. So my question, or what I am trying to understand is, how to HA know which GPIO0 is the Binary sensor is attached too if you have multiple binary sensors using GPIO0 around the house??
Eg. using Sonoff GPIO0 in Hallway and GPIO0 of another sonoff in Bedroom, and GPIO0 of D1 Mini in Garage.
I hope my confusing is not a silly question and hope my explanation is clear enough to understand
Thanks again in Advance. Just trying to understand so I can replicate in other rooms of house

Ok, you are not running ESPHOME on your devices today?

I’m running ESPHOME, and I manage them within Hass.io by installing the esphome on all the sonoff/esp8266 devices and the esphome dashboard on hass.io.
You can add https://github.com/esphome/hassio into the repositories inside add-on stores.
Then you install the esphome dashboard.
check this guide: https://esphome.io/guides/getting_started_hassio.html

I think you should install ESPHOME on all your sonoff or ESP8266 modules, It will communicate with HA with the API and for me it worked better and easier than tasmota and mqtt.
Inside the esphome configuration for the sonoff/8266 device you can add the config I pasted in the previous post.

All the devices will be separated, so no conflict with everyone using GPIO0 for example.
Your devices will have their own entity_id within hass.io and thats what you use to get the status of the binary_sensor.

Ok cool. Thanks @LCL
I will review ESPHome and look to set it up to use with HA
I have only used Tasmota and have found it pretty good, I will flash a device tomorrow and see how it works. I have got use to the codes, command etc for tasmota so it will be very different I am guessing. Always keen to try other option.

I want to be able to use a push button (as above) to control a hue light and also to switch other sonoff device on/off. That is, the sonoff the button is connected to is not controlling the relay, the push button controls another relay on another sonoff as it is connected to MQTT. I hope I can do this with ESPHome

Will let you know how I get on. In the mean time, did you get the Dimming to work with push button?

cheers

I switched from tasmota (and mqtt) to esphome first just to test it.
I was using tasmota on a sonoff T1 1 button wall switch.
With tasmota I could have the button work as single press (mqtt) double press (relay toggle) and hold (mqtt)
The problem was, that it sends out one payload of single press or hold to mqtt, so HA could’t see if i still hold the button as it did’t send a new payload once released.
And I did’t like the double press to toggle the relay as I did’t have use for it.

With esphome I could make the button a sensor, so HA can see how long I press the button etc.
and I can control the relay independent, so double press will not toggle the relay.

Just to make it clear, I don’t use any automations inside esphome (you can have some automation inside your module in case it disconnects from HA), I use esphome only as sensors and switches for hass.io, HA is handling all the work.

This way I can toggle the ikea trådfri bulbs with single press, a double press sets the lights color temp, and triple press turns off all lights in the house and sets a timer on the bedroom light so that I can go into bed before it gets dark :slight_smile:

For the dimming, I have’t looked into this more, only got single button wall switches, hope the OP has been testing it out.
I might test it on a bare esp8266 with two buttons to see if I can get some node-red flows, I’m no expert so will do “try and error” style, but can’t guarantee I will do that soon :slight_smile:

Sounds awesome. I understand there is a difference in using ESPHome and Tasmota firmware so keen to try and test as you do. I like to experiment to find the best solution that works for us. That is all part of the Home Automation fun for me. I spend hours reading and researching how to do what I want to achieve and I am getting there.
I am no programmer however I have learnt a great deal from others trail and errors, alone with my own far share of wins and losses :slightly_smiling_face:
With not using automations in ESPHome, that is great as I am getting my head around automations in Hass.io so thats great
Making a button as Sensor sounds interesting. Based on what you are saying it is more granular in its control of the actions from the button. This means I guess you can apply rules that are more defined.
I like the 1, 2, and 3 press options you have especially the triple press to turn all lights off with timer for Bedroom :slight_smile:
Is this defined in your Node-Red Flow above? Yes looks like it is
Well lots to learn. ESPHome, Node-Red, and Flashing sonoff with ESPHome. Daunting challenge however a great challenge :slight_smile:

This is my solution by using a single touch button. works nice & clean.

light:
  - platform: monochromatic
    name: "Livingroom Ceiling light"
    id: light_0
    output: light_0_out

# Example output entry
output:
  - platform: esp8266_pwm
    id: light_0_out
    pin: D4
    inverted: true

binary_sensor:
  - platform: gpio
    pin: D2
    id: light_0_touch
    on_click:
      then:
        - if:
            condition:
              light.is_off: light_0
            then:
              light.turn_on: 
                id: light_0
                brightness: 1.0
            else:
              light.turn_off: light_0
    on_press:
      then:
      - if:
          condition: 
            light.is_off: light_0
          then:
          - delay: 0.5s
          - while:
              condition:
                binary_sensor.is_on: light_0_touch
              then:
                - light.dim_relative:
                    id: light_0
                    relative_brightness: 5%
                    transition_length: 0.1s
                - delay: 0.1s
          else:
          - delay: 0.5s
          - while:
              condition:
                and:
                  - binary_sensor.is_on: light_0_touch
                  - light.is_on: light_0
              then:
                - light.dim_relative:
                    id: light_0
                    relative_brightness: -5%
                    transition_length: 0.1s
                - delay: 0.1s
20 Likes

Thanks for this code, friend! This is exactly what I was looking for my nightlights with one touch button. Before this code, I only turned them on and off with a button, and set the brightness in HA

I tryed your node-red code for dimming a light when holding button and I couldn’t get it to work.
Recently I made a 4 momentary buttons using a NodeMCU running Tasmota to control my light and switches. But I would like to expand the possibilities, like double press, triple press and hold doing other tasks. If possible, could you kindly post the nodes you use to achieve this? I can help you with esp8266 configuration if you need any help. Thanks.

Yes, the dimming code is just an idea.
Did not test it in real life, also I have not added any dimming nodes. just that there is a flow to the dimming part.
You will have to add more nodes to do the dimming.
Im not sure if its enough with a call to dim, or if you need to make a looping function that will call the dimming with incremental steps.
You would probably have to check when the button is released to stop the loop.

The multi-press part I can help you with, but Im not home at the moment, but from memory, what I did was having a trigger node that looks at the sensor state (button press) and after that I installed a counter node (you have to search for counter in the repository or what its called :slight_smile: )
In the counter node, you have to set a timeout (think I went with 500ms), that will reset for each incoming messages. Once the timeout is reached a message is sent out from the counter node with a count number.

This message goes into a switch node, where you add multiple outputs depending on how many presses you like to handle.

So add like count == 1, 2, 3 etc in the switch node. You can just go with count == 2, 3 and then an else for “single” press. This will also failover to single press if you press too many times. This is up to you :slight_smile:

Hope this will help.

Thanks for your tips.
I searched for counter but it only returned one node (node-red-contrib-counter), this one was already installed in my Node-Red (HASSIO). It also doesn’t have a timeout option, which makes me think that the node may have another name.

The correct is node-red-contrib-timed-counter “A node that counts messages received in a specified time limit.”
This one has the timeout which I think is the one in use in my flows.

This is my solution. Short push on, off. Long press increases brightness and decreases brightness. It works on the inverter. Light on remembers last value of brightness. There is a extra output of brightness in logs esphome for debuging. It works on Wemos D1 mini. D2 pin is for push button and D4 for mosfet irlb8721 with LED 8mm 150mA. All this for a 3D printed decorative table lamp.

globals:
  - id: my_global_bool
    type: bool
    restore_value: no
    initial_value: 'false'
  - id: my_global_float
    type: float
    restore_value: no
    initial_value: '0.3'
    
switch:
# This is to restart the ESPHome device remotely
  - platform: restart
    name: "Restart ESPHome - Kuchyna_Dimmer"


sensor:
  - platform: wifi_signal
    name: "WiFi - Kuchyna_Dimmer"
    update_interval: 60s


output:
  - platform: esp8266_pwm
    pin: D4
    frequency: 1000 Hz
    id: pwm_a
    #inverted: TRUE

light:
  - platform: monochromatic
    name: "kuchyna_Dimmer"
    id: led
    output: pwm_a
    gamma_correct: 0

binary_sensor:
  - platform: gpio
    pin:
      number: 4
      inverted: TRUE
      mode: INPUT_PULLUP
    id: led_touch
    filters:
      - delayed_on: 50ms  
      - delayed_off: 50ms 

    on_click:
      then:
        - if:
            condition:
              light.is_off: led
            then:
              - light.turn_on: 
                  id: led
                  brightness: !lambda |-
                    return id(my_global_float);
              - lambda: |-
                  id(my_global_bool) = (true);
                  ESP_LOGD("main", "Global value brightness is: %f", id(my_global_float));
            else:
              light.turn_off: led
             
    on_press:
      then:
      - if:
          condition: 
              lambda: |-
                return id(my_global_bool);
# When above condition evaluates to true - Fade_up function else Fade_down
          then:
          - delay: 0.5s
          - while:
              condition:
                binary_sensor.is_on: led_touch
              then:
                - light.dim_relative:
                    id: led
                    relative_brightness: 2%
                    transition_length: 0.1s
                - delay: 0.1s
                - lambda: |-
                    id(my_global_float) = id(led).current_values.get_brightness();
                    ESP_LOGD("main", "Global value is: %f", id(my_global_float));
          - lambda: |-
              id(my_global_bool) = (false);
          else:
          - delay: 0.5s
          - while:
              condition:
                and:
                  - binary_sensor.is_on: led_touch
# This is where I want to set minimum value so that touch sensor only allows 4% minimum
                  - lambda: |-
                      return {id(led).current_values.get_brightness() > 0.04};
              then:
                - light.dim_relative:
                    id: led
                    relative_brightness: -2%
                    transition_length: 0.1s
                - delay: 0.1s
                - lambda: |-
                    id(my_global_float) = id(led).current_values.get_brightness();
                    ESP_LOGD("main", "Global value brightness is: %f", id(my_global_float));
          - lambda: |-
              id(my_global_bool) = (true);
6 Likes

@drdolitle1 you mean MOSFET - IRLB8721.
Can you please give some schematics? D2 is push button connected to GND pin. D4 (PWM output) goes directly to Gate input of the MOSFET. Source of the MOSFET is connected to the GND and Drain of the MOSFET is connected to the - of the LEDs? + of the LEDs is connected to 12/24V of the power supply for LEDs? Is this correct?

Did you want to automate this within ESPHome (so the button behaviour works whether HA is connected or not), or do you not mind it being dependent on HA?

If the former, I’m thinking this could be implemented using Automation Lambda’s, and one global variable.
As I understand what you want it to do:

  • Respond to short-press by changing directly to a new state (i.e. on/off).
  • Respond to long-press by incrementally dimming up/down at some rate, until the button is released or a limit is reached.

If that’s what you want, you may be able to use ESPhome’s ‘interval’ sensor, and on-press automations to set a static (ESPHome calls them ‘global’) variable that all Lambda’s can reference.

You can implement a simple ‘state machine’ using a global variable to track the time of button-down events, but don’t otherwise act on button-down events.
The interval timer will fire at its interval (whether a button is down or not) and its lambda will check if a button is down now, and if so, will query the global variable to see if the button has been down for at least 1 second. We’ll return to this logic in a moment. But first, what do you do when the button is released.

When a button is released, have the button-up lambda check your variable (the millisecond count when the button was pressed) to see if a second has elapsed. If less than a second, act on the button now (on/off). This handles the momentary-press case.

Now to the case where you hold the button down, and this logic takes over:
When the interval triggers, it should first check to see if a button is down, and also if at least one second has elapsed.
If both are true, then incrementally dim/brighten the light (in the direction of whichever button it is that’s down).
That interval will fire repeatedly, noticing that the button is still down, that 1 second has still elapsed since press, and it will keep on incrementally dimming/brightening as long as those things remain true.

When the button finally is released, the dimming/brightening stops happening, because the interval tests will fail, and the change is done. (Oh, and this long-press button-release won’t cause immediate full dim/brighten described earlier, because it acts only if the press was less than a second long.)

Just set the interval to something like 1/4 second, so it doesn’t dim/brighten so slowly at glacial 1 second intervals - you’ll want those to clock along a bit faster.

And, in case it helps, here is a portion of a config attempting to implement the above.
I hope this accomplishes what you’re looking for, and without making HA do any of the work - it all happens inside the ESP.
This was written for the onboard blue LED and a couple GPIO pins with pushbuttons wired to ground.

binary_sensor:
  - platform: gpio
    pin:
      number: GPIO4
      mode:
        input: true
        pullup: true
      inverted: true
    name: "On/Brighten"
    id: on_bright
    on_press:
      then:
        - lambda: |-
            id(when_button_pressed) = millis();
    on_release:
      then:
        - if:
            condition:
            - lambda: 'return (millis() - id(when_button_pressed)) < 1000;'
            then:
              light.turn_on: the_blue_led
  - platform: gpio
    pin:
      number: GPIO5
      mode:
        input: true
        pullup: true
      inverted: true
    name: "Off/Dim"
    id: off_dim
    on_press:
      then:
        - lambda: |-
            id(when_button_pressed) = millis();
    on_release:
      then:
        - if:
            condition:
            - lambda: 'return (millis() - id(when_button_pressed)) < 1000;'
            then:
              light.turn_off: the_blue_led

globals:
  - id: when_button_pressed
    type: int
    restore_value: no

interval:
  - interval: 0.5s
    then:
      - if:
          condition:
            - and:
                - binary_sensor.is_on: off_dim
                - lambda: 'return (millis()-id(when_button_pressed)) >= 1000;'
          then:
            - logger.log: "dimming..."
            - light.dim_relative:
                id: the_blue_led
                relative_brightness: -5%
      - if:
          condition:
            - and:
                - binary_sensor.is_on: on_bright
                - lambda: 'return (millis()-id(when_button_pressed)) >= 1000;'
          then:
            - logger.log: "brightening..."
            - light.dim_relative:
                id: the_blue_led
                relative_brightness: 5%

light:
- platform: monochromatic
  name: "${node_name} Blue LED"
  output: blue_led
  id: the_blue_led
output:
- platform: esp8266_pwm
  id: blue_led
  pin: GPIO2
  inverted: True
2 Likes

Not sure if this helps, but I use this for my moeshouse dimmers:

esphome:
  name: qs-wifi-ds01-ip52
  platform: ESP8266
  board: esp01_1m

wifi:
  ssid: !secret wifi_ssid1
  password: !secret wifi_password

  # Enable fallback hotspot (captive portal) in case wifi connection fails
  ap:
    ssid: "qs-wifi-ds01-ip52 Fallback"
    password: "fallback"

captive_portal:

# Enable Home Assistant API
api:
  password: !secret api_password

ota:

# Enable logging
logger:
  baud_rate: 0
  level: DEBUG
  logs:
    sensor: ERROR
    duty_cycle: ERROR
    binary_sensor: ERROR
    light: ERROR

# Example configuration entry (optional web server)
web_server:
  port: 80

time:
  - platform: homeassistant

substitutions:
  switch_id: "dim_01"

# globals:
# Dummy light brightness tracker Global

globals:
  # Dim direction for Switch 1: 0=Up (brighten) 1=down (dim)
  - id: g_direction_1
    type: int
    restore_value: no
    initial_value: "1"
  # Counter for time pressed for switch 1
  - id: g_counter_1
    type: int
    restore_value: no
    initial_value: "0"
  # initial brightness

# Uart definition to talk to MCU dimmer
uart:
  tx_pin: GPIO1
  rx_pin: GPIO3
  stop_bits: 1
  baud_rate: 9600

sensor:
  - platform: wifi_signal
    name: "${switch_id} WiFi Signal Sensor"
    update_interval: 60s
  - platform: uptime
    name: "Uptime"
  # Primary template sensor to track Brightness of light object for "on_value" sending to MCU dimmer
  - platform: template
    name: "${switch_id} Brightness Sensor"
    id: sensor_g_bright
    internal: true
    update_interval: 20ms
    # Ensure on_value only triggered when brightness (0-255) changes
    filters:
      delta: 0.8
    # Read brightness (0 - 1) from light , convert to (0-255) for MCU
    lambda: |-
      if (id(light_main).remote_values.is_on()) {
        return (int(id(light_main).remote_values.get_brightness() * 255));
      }
      else {
        return 0;
      }
    # On Change send to MCU via UART
    on_value:
      then:
        - uart.write: !lambda |-
            return {0xFF, 0x55, (char) id(sensor_g_bright).state, 0x05, 0xDC, 0x0A};
        - logger.log:
            level: INFO
            format: "Sensor Value Change sent to UART %3.1f"
            args: ["id(sensor_g_bright).state"]
  # Sensor to detect button push (via duty_cycle of 50hz mains signal)
  - platform: duty_cycle
    pin: GPIO13
    internal: true
    id: sensor_push_switch
    name: "${switch_id} Sensor Push Switch"
    update_interval: 20ms

binary_sensor:
  #Binary sensor (on/off) which reads duty_cyle sensor readings.
  - platform: template
    id: switch1
    internal: true
    name: "${switch_id} Switch Binary Sensor"
    # read duty_cycle, convert to on/off
    lambda: |-
      if (id(sensor_push_switch).state < 95.0) {
        return true;
      } else {
        return false;
      }
    # Short Click - toggle light only
    on_click:
      max_length: 300ms
      then:
        light.toggle: light_main
    # Generic On_Press - log press, toggle DIM Direction and reset press interval counter
    on_press:
      then:
        - logger.log: "Switch 1 Press"
        - lambda: |-
            if (id(g_direction_1) == 0) {
              id(g_direction_1) = 1;
            } else {
              id(g_direction_1) = 0;
            }
            id(g_counter_1) = 0;

# Dummy light output to allow creation of light object
output:
  - platform: esp8266_pwm
    pin: GPIO14
    frequency: 800 Hz
    id: dummy_pwm

# Primary Light object exposed to HA
light:
  - platform: monochromatic
    default_transition_length: 20ms
    restore_mode: RESTORE_DEFAULT_OFF
    name: "${switch_id} Light"
    output: dummy_pwm
    id: light_main

switch:
  - platform: restart
    name: "${switch_id} Restart"

# Polling object for long press handling of switch for dim/brighten cycle
interval:
  - interval: 20ms
    then:
      - if:
          condition:
            binary_sensor.is_on: switch1
          then:
            # Ramp rate for dim is product of interval (20ms) * number of intervals
            # Every 20ms Dimmer is increased/decreased by 2/255
            # Lower limit = 10%
            # Upper limit = 100%
            # 100% - 10% = 90% = 230/255. Therefore 230/2 * 20ms = 2.3 seconds for full range
            # At full/min brightness - further 16x20ms = 0.32 Seconds "dwell" by resetting counter to 0
            # Initial pause for 16x20ms = 0.32s to allow "on_click" to be discounted 1st
            # g_direction_1 = 0 (Increasing brightness)
            # g_direction_1 = 1 (decreasing brightness)
            # g_counter_1 = Interval pulse counter

            lambda: |-
              float curr_bright = id(light_main).remote_values.get_brightness();
              id(g_counter_1) += 1; 

              // If max bright, change direction
              if (curr_bright >= 0.999 && id(g_direction_1) == 0) {
                id(g_direction_1) = 1;
                id(g_counter_1) = 0;
              }

              // If below min_bright, change direction
              if (curr_bright < 0.1 && id(g_direction_1) == 1) {
                id(g_direction_1) = 0;
                id(g_counter_1) = 0;
              }

              if (id(g_direction_1) == 0 && id(g_counter_1) > 15) {
                // Increase Bright
                auto call = id(light_main).turn_on();
                call.set_brightness(curr_bright + (2.0/255.0));
                call.perform();
              }

              else if(id(g_direction_1) == 1 && id(g_counter_1) > 15) {
                // Decrease Bright
                auto call = id(light_main).turn_on();
                call.set_brightness(curr_bright - (2.0/255.0));
                call.perform();
              }

2 Likes

Exactly as you write with D2 and D4 pins.
In fact, I ended up using 3 LEDs.
8mm, 70mA at 3.2v. I couldn’t draw DC voltage regulator to 3.3V in schematic. I did not use LM317, but simple little DC regulator from aliexpress. I use 5V phone charger with usb cable. The cable is soldered to the VIN and GND pin at the other end.

Sorry for my english is not good.

circuit

y

You are absolutely right. The intention was to use a lamp primarily without HA. You also described exactly how the button should work. My project is relatively young and I haven’t even tried to see if it actually works without HA. I will try it. Also thank you for another post with code.

I can confirm that the lamp works without HA. I put the MAC address in the blacklist on the wifi routers. HA reports no connection. But, switching on/off the lamp as well as brightness still works with long press push button. After connecting to HA, I can still control the switching on and off of the lamp, as well as brightness with a slider in HA.
That’s exactly what I wanted to achieve.
The lamp is designed for decorative lighting and works well with 3pcs LEDs.

2 Likes

@drdolitle1 thanks for you feedback. I was testing also with my Wemos D1 mini and the code is working both with HA and on device itself. I didn’t manage to connect MOSFET (PWM driver) to the output to control my LED strip but I can see that the blue LED on Wemos PCB is dimming…