New "virtual pins" module for testing purposes

I’m currently using simulavr set for atmega644p so I can test Fluidd while making changes, but this is becoming a problem given the limited number of pins available in this chip!

As I want a bunch of features enabled on the simulated MCU, I need to keep disabling some to free pins so I can use those in other features… which is not ideal!

I’ve tried using an atmega2560, but seems this is not supported, so I’ve now moved from that solution!

What I’ve been thinking is to add a new Klipper module called “virtual pins” (or simulated pins) that only purpose is to allow creating more pins that one can use with the existing modules, like [output_pin].

Ideally, the pins for this new module could be configured via [virtualpin], and then have a new command that would return the current state of a virtual pin or even all virtual pins.

Before I delve into developing such a module, I would ask for feedback as to understand if this is a useful module and if it would make a good candidate to eventually get merged into Klipper master code-base.

4 Likes

Will be useful for non developers too. An output pin is the only way i know to add a custom switch or slider to webui.

2 Likes

Perhaps a bit “off topic” here, but I’ve been thinking of possibly extending the “batch mode” processing of Klipper so that it would not require a mcu “data dictionary”. More specifically, have the host code “auto-generate” any required data dictionaries as it parses the printer.cfg file.

The advantage of doing this is that it would make it easier to run batch mode tests. In particular, it can be tricky to run a batch mode test when the printer.cfg file has multiple mcus.

If this were implemented, then any syntactically valid pin name would be considered valid by the host. (Thus, a batch mode test without explicit mcu data dictionaries would not be useful for verifying that a printer.cg file is valid; however, it could still be useful to test kinematics.)

Not sure if that helps what you are looking for. Perhaps it will provide some additional ideas.

Cheers,
-Kevin

1 Like

Well, this “virtual pin” won’t actually be used for anything… though one could be able to access via macros I guess!

After talking with @th33xitus on this topic, he made the excellent suggestion of trying to run TWO instances of simulavr, and amazingly, it works just fine!

The changes are quite trivial to get it up and running (all it needs is a different port path) though the impact is a bit considerable (docker indicates uptime ~1.0 with 1 instance, and ~2.0 for 2 instances)

So now we do have a solution for the lack of pins: just add more simulavr instances!

Nevertheless, I am still going to consider the “virtual pins” and might try and code a PoC for it as I do think there is some value!

Just a quick update, I’ve now managed to simulate any digital_out, pwm, adc pin types with relative success!

If anyone wants to give it a try, just add this file to your “klippy/extras” folder:

docker-klipper-simulavr/virtual_pins.py at master · pedrolamas/docker-klipper-simulavr (github.com)

Then on the “printer.cfg”, add a [virtual_pins] entry, and from that point on, try setting a few pins to virtual_pin:xxxxx.

That will add a new SET_VIRTUAL_PIN gcode command that allow to set a virtual pin value (pass the PIN name and new VALUE from 0.0 to 1.0)

Under Klipper, one can create a macro to output all virtual pins state:

[gcode_macro OUTPUT_VIRTUAL_PINS_STATE]
gcode:
  M118 virtual_pins = { printer.virtual_pins.pins }

You can also check the status of all the virtual pins on moonraker via http://localhost:7125/printer/objects/query?virtual_pins (change “localhost:7125” accordingly!)

1 Like

I’ve been considering the naming and came with these 2 options!

  • virtual-pins
  • simulated-pins

0 voters

I’m inclined to use “simulated-pins”, but wanted to check what other people think!

2 Likes

From a software engineering standing point, what I have done is created a mock object for the pins, so I’m now considering if this should be called mock-pins or mocked-pins

(Yes, I hate naming stuff…)

The Klipper PR I did with this feature was not merged, so I’ve decided to maintain it as part of my docker SimulaV image only!

I’ve already updated the link on my previous post, but will from now on maintain the module here:

To make things easier, I’ve now created a separate easy-to-install virtual_pins module! :slightly_smiling_face:

3 Likes

Legend, this is super helpful!

@pedrolamas Im prolly misunderstanding something - when I try this I get the error:

Unknown pin chip name 'virtual_pin'

Im trying to use it like this:

[virtual_pins]

[output_pin fs_button]
pin: virtual_pin:fs_button_pin
pwm: True
cycle_time: 0.1

and then reference it in a Gcode macro (which is what is throwing the error):

#///////////////////////////filament sensor button macros/////////////////////////////////////////////////
[gcode_button sensor_fs]
#pin: PC7  # !!!!!!!!!!!!!!!!!!!!!change with the pin name to which the sensor is connected!!!!!!!!!!!!!!!!!!!!!
pin: virtual_pin: fs_button
press_gcode: # sensor released

I assume I’m trying to use this incorrectly.

PS Im trying to simulate a physical button being pressed, where no actual button exists.

EDIT: NVM, I got it working. It was user error, I was trying to use it in the wrong location:

[gcode_button sensor_fu]
pin: virtual_pin: fs_button # remove the negation "!" for sensor v1 - use just PA10 as example
release_gcode:  # filament unload procedure

Actually you still have an error there, you need to take that space after virtual_pin:

Your example should be like this:

[gcode_button sensor_fu]
pin: virtual_pin:fs_button

Interesting, it works regardless.

All my CanBus pins have the space so I just assumed it was standard formatting for Klipper.

I haven’t checked, but the way you have it (with the space) I assume it will create a nameless virtual pin (ignoring the Id you put in), just be aware of that if you have more virtual pins in your config!

I just checked and it works fine, it even shows up in the miscellaneous section correctly.

1 Like

So I just found this thread and I’m curious if this virtual pin module will help me to accomplish a particular goal.

I have a BCN3D Sigma D25 printer currently running Klipper. This is an IDEX machine, and both printheads have a piezo sensor used for probing the bed with the nozzle. My issue is that Klipper does not allow for two Z-probes to be configured. Both of the probes are simple on/off like an endstop, so I could modify the hardware so that both probes trigger the same pin on the MCU. The issue there is that BCN used FFC (flat flexible cable) for all of their connections, and I don’t want to modify the hardware and risk ruining it when it works well as-is.

My question is: could I use this Virtual_Pin module to assign my Z-probe to a virtual pin whose status is determined by either of two physical pins (essentially an OR gate)? This would allow me to easily write a macro to determine the offset between my nozzles at the start of a print. Currently I use T0 for all probing and the probe on T1 is undefined/unused. This means that my nozzle offset between T0 and T1 must be determined manually (or shims are inserted on T1 to physically set both nozzles to the same height).

Hi @wcj97, the virtual pins were built for simulation purposes, but having said that, you might be able to do something in conjunction with gcode_button to achieve your purposes!

I have NOT tried this, but something along the lines of this might help:

[gcode_button A1]
pin: A1
press_gcode: UPDATE_VIRTUAL_PIN
release_gcode: UPDATE_VIRTUAL_PIN

[gcode_button A2]
pin: A2
press_gcode: UPDATE_VIRTUAL_PIN
release_gcode: UPDATE_VIRTUAL_PIN

[gcode_macro UPDATE_VIRTUAL_PIN]
gcode:
  SET_PIN PIN=virtual_pin:my_pin VALUE={1 if printer['gcode_button A1'].state OR printer['gcode_button A2'].state else 0}

The idea here is that each pin (in the example above A1 and A2) is assigned to a gcode_button and when it changes state, then UPDATE_VIRTUAL_PIN is called so that virtual_pin:my_pin is updated with and OR of these 2 pins.

Then all you need is to use virtual_pin:my_pin where you want it!

Once again, I have not tried this so if you use this, you will be doing it at your own risk!

2 Likes

thanks for the excellent work, with your module I wrote a good script to delay the start of printing. with a convenient slider implemented with your virtual pin

[virtual_pins]
[output_pin timer]
pin: virtual_pin:timer_pin
pwm: True
value: 0
scale: 1440


[delayed_gcode wait_timer] 
 # initial_duration: 2.
gcode:
	{% if printer['output_pin timer'].value > 0 %}
   	{% set WAIT = printer['output_pin timer'].value * 1440|float %}
    {% set WAIT_ROUNDED = WAIT|int %}
    	{% if WAIT - WAIT_ROUNDED >= 0.5 %}
       {% set WAIT_ROUNDED = WAIT_ROUNDED + 1 %}
   	 {% endif %}
  		RESPOND MSG="Waiting... {WAIT_ROUNDED} minutes remaining."
      {% set WAIT_ROUND_COUNT = WAIT_ROUNDED - 1 %}
  		SET_PIN PIN=timer VALUE={WAIT_ROUND_COUNT}
  		UPDATE_DELAYED_GCODE ID=wait_timer DURATION=60
      WAIT_TIMER_START
 	  {% else %}
    RESPOND MSG="Wait Time End"
    WAIT_TIMER_END	
  {% endif %}
 

[gcode_macro WAIT_TIMER_START]
gcode:
  UPDATE_DELAYED_GCODE ID=wait_timer DURATION=1
		{% for s in range(0, 4) %}
      SET_PIN PIN=LED VALUE=0.25
      G4 P14000
      SET_PIN PIN=LED VALUE=0.5
      G4 P1000
		{% endfor %}

[gcode_macro WAIT_TIMER_END]
gcode:
  UPDATE_DELAYED_GCODE ID=wait_timer DURATION=0
  SET_PIN PIN=LED VALUE=1

I’m getting an error on the following command:

SET_VIRTUAL_PIN PIN=button VALUE=1
Unknown command:"SET_VIRTUAL_PIN"

This is (part of) my config:

[virtual_pins]

[gcode_button turn_off_fan]
pin: virtual_pin: button
press_gcode: M107