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);
@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
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();
}
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.
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.
@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…
You need logic-level Mosfet with VGS(th) max 3.3V. IRLB8721 has full saturation on 2.35V. You probably know that. Just to be sure.
Thank you, @drdolitle1 - this works perfectly for my needs and improves my attempt no end!
I have it working on an ESP8266-01, using pin 2 for output to a MOSFET (through a opto-coupler) and pin 0 for a touch switch input.
Thanks again!
Thanks for the code!
I added a few features I like:
- Dim up and down constantly if max/min brightness is reached
- Start with a default brightness of 70%
- Always dim up after turning on (from off)
globals:
- id: my_global_bool
type: bool
- id: my_global_float
type: float
binary_sensor:
- platform: gpio
pin:
number: 34
mode: INPUT
inverted: True
filters:
- delayed_on: 20ms
name: "light switch with a name"
id: "kids1_tast"
on_click:
then:
- if:
condition:
light.is_off: light1
then:
- lambda: |-
id(my_global_float) = 0.7;
ESP_LOGD("main", "Global value brightness is: %f", id(my_global_float));
id(my_global_bool) = (true); // make sure always dim up after turn on
- light.turn_on:
id: light1
brightness: !lambda |-
return id(my_global_float);
else:
- light.turn_off: light1
on_press:
then:
- delay: 0.5s
- if:
condition:
binary_sensor.is_on: kids1_tast
# don't do a thing if not pressed long enough
then:
- while:
condition:
binary_sensor.is_on: kids1_tast
then:
- if:
condition:
lambda: |-
return id(my_global_bool);
then:
- light.dim_relative:
id: light1
relative_brightness: 2%
transition_length: 0.1s
- delay: 0.1s
- lambda: |-
id(my_global_float) = id(light1).current_values.get_brightness();
ESP_LOGD("main", "+Global value is: %f", id(my_global_float));
// invert dim direction if full on
if (id(light1).current_values.get_brightness() >= 0.99) id(my_global_bool) = false;
else:
- light.dim_relative:
id: light1
relative_brightness: -2%
transition_length: 0.1s
- delay: 0.1s
- lambda: |-
id(my_global_float) = id(light1).current_values.get_brightness();
ESP_LOGD("main", "Global value brightness is: %f", id(my_global_float));
if (id(light1).current_values.get_brightness() <= 0.04) id(my_global_bool) = true;
- lambda: |-
id(my_global_bool) = !id(my_global_bool) ; // invert bool at end of while
cheers
Hi. I’m sorry, what platform do you use? I read this thread twice: @drdolitle1 is using Wemos, @aceindy went with Moes, does it work with any ESP8266 switch or does need an actual dimmer? For example, will the code work with Sonoff mini R2 and Dual R3 after certain adaptation?
will work. works for me on sonoff basic
Thanks a bunch for the quick reply.
So , you need a PWM module anyway, you used a 5/12/36V LED, but what about regular 230V dimmable LED lamps?
I thought so. Okay, thanks again, I’m off for testing.