"Long press" button Macro

By default Kliper gcode_button supports only press and release actions,
This configuration allow to extend gcode_button to recognize how long it was pressed and from that can derive what kind of action it should do, this way we can get multiple different actions from single button.

Provided sample have following behavior:
if press and release of a button(trip a switch) in less than 2 sec - don’t do anything
if we press-release took more than 2 sec but less than 4 sec - call action 1
if we press-release took more than 4 sec but less than 10 sec - call action 2
if we press-release took more than 10 sec - call action 3

How it works:
When button is pressed - we are pushing current runtime of klipper to macro variable, when button will be released - we pool previously stored “press time” value from a macro variable and fetching current runtime then calculating duration and taking decision what to do.

If you will see that “release” action is called before “press” - just reverse pin configuration by removing “!”

Personally I think that existing gcode_button should extend it’s capabilities and should report not only state but also some timestamp of “last press” and “last release” so we could use it without additional workarounds.

P.S. currently this macro uses undocumented property to get runtime of a klipper, why this kind of property or usual “host time” it’s not available in system_stats or somewhere else I don’t know.

[gcode_button my_button_long_press]
description: sample "long press" button configuration
pin: !host:gpiochip0/gpio30
press_gcode:
  {% set current_timer = printer.toolhead.estimated_print_time %} 
  SET_GCODE_VARIABLE MACRO=my_button VARIABLE=last_press VALUE={current_timer}
  {action_respond_info("Press time: %.2f "% (current_timer))}

release_gcode:
  {% set duration = printer.toolhead.estimated_print_time - printer["gcode_macro my_button"].last_press|float %}
  {action_respond_info("duration: %.2f "% (duration))}
  {% if duration|float > 10.0 %}
      my_button_level3
  {% elif duration|float > 4.0 %}
      my_button_level2
  {% elif duration|float > 2.0 %}
      my_button
  {% else %}
      {action_respond_info("button didn't meet criteria it was too short press")}
  {% endif %}

[gcode_macro my_button]
description: This macro will be called if button pressed more than 2 sec
variable_last_press: 0.0
gcode:
  {action_respond_info("it was loong press of a button level 1")}

[gcode_macro my_button_level2]
description: This macro will be called if button pressed more than 4 sec
gcode:
  {action_respond_info("it was loong press of a button level 2")}

[gcode_macro my_button_level3]
description: This macro will be called if button pressed more than 10 sec
gcode:
  {action_respond_info("it was loong press of a button level 3")}
6 Likes

Inspired by your macro. Added 3 event approach with Click, Double click and Hold action

gcode_button red_button]
pin: ^!PB1
press_gcode:
  {% set current_timer = printer.toolhead.estimated_print_time %} 
  SET_GCODE_VARIABLE MACRO=red_button VARIABLE=press_start_time VALUE={current_timer}
  UPDATE_DELAYED_GCODE ID=red_button_hold DURATION=0.5
release_gcode:
  UPDATE_DELAYED_GCODE ID=red_button_hold DURATION=0
  {% set duration = printer.toolhead.estimated_print_time - printer["gcode_macro red_button"].press_start_time|float %}
#  {action_respond_info("duration: %.2f "% (duration))}
  {% if duration < 0.5 %}  # holdThreshold 
    {% set current_timer = printer.toolhead.estimated_print_time %} 
    {% set click_duration = current_timer - printer["gcode_macro red_button"].last_click_time|float %}
    
    {% if click_duration < 0.3 %}  # doubleClickThreshold
      red_button_double_click
      
      UPDATE_DELAYED_GCODE ID=red_button_click DURATION=0
      SET_GCODE_VARIABLE MACRO=red_button VARIABLE=last_click_time VALUE=0
    {% else %}
      UPDATE_DELAYED_GCODE ID=red_button_click DURATION=0.3  # doubleClickThreshold
      SET_GCODE_VARIABLE MACRO=red_button VARIABLE=last_click_time VALUE={current_timer}
    {% endif%}
  {% endif%}

[gcode_macro red_button]
variable_press_start_time: 0.0
variable_last_click_time: 0.0
gcode:
  #do nothing just store variables
  
[delayed_gcode red_button_click]
gcode:
  {action_respond_info("Click")}
  {% if printer.idle_timeout.state in ['Idle','Ready'] and  not printer.pause_resume.is_paused %}
    REPEAT_LAST_PRINT
  {% elif printer.idle_timeout.state in ['Idle','Ready'] and  printer.pause_resume.is_paused %}
    RESUME
  {% elif printer.idle_timeout.state | upper == "PRINTING" %}
    PAUSE
  {% else %}
    {action_respond_info(printer.idle_timeout.state)}
  {% endif %}


[gcode_macro red_button_double_click]
gcode:
  {action_respond_info("Double click")}
	#add your Double click actions here
  
[delayed_gcode red_button_hold]
gcode:
  {action_respond_info("Hold")}
  #add your hold actions here
  
1 Like

Long press was just added to master as an option to prevent false triggering, and to provide this functionality.

This works for both buttons and for run-out sensors.

The configuration option is “debounce_delay”.

Edit: @garethky Your changes are already being useful.

1 Like

EmperorArthur is talking about Debounce gcode_button and filament_switch_sensor by garethky · Pull Request #6848 · Klipper3d/klipper · GitHub.

1 Like