Set IO port bit mapping (M670) support


i am developing a 3D printer that uses 88 valves/nozzles as output. In the RepRap G-code list there is the M670 command, where you can define IO pins that will have a certain state when sending a G1 command, using the P parameter.

I can’t find if this is supported by Klipper or not. If not, is there another way to control up to 88 digital outputs during a G1 move, so I can control my 88 valves?

Can I ask how are the 88 nozzles arranged? Are you printing blobs of material like an old dot matrix printer?

My first thought on reading this was that if your printer doesn’t work, you can use your hardware to control a piano.

the nozzles are arranged in a line, and move over the print bed depositing water onto a mix of cement and sand (it is a powder bed 3D cement printer)

1 Like

There is currently no simple mechanism to set arbitrary io pins during movement in Klipper today.

You may be able to define pins using [output_pin] config sections and use the SET_PIN command just prior to and after the requested movement. I’m not sure if that is feasible for your application. Note that the SET_PIN command is currently limited in the number of times per second it can update a pin (see the “Klipper laser support” threads on this Discourse for further information).


If this is fast enough, this might be an option. I have full control over the generated G-code (custom slicer) so inserting the valve commands like SET PIN in between G1 commands is no problem.

Is there anything known about how fast this mechanism is? I need maximum several milliseconds response time.

The current SET_PIN command will set a pin for a minimum of 100ms. @Cirromulus has a branch that changes that - see [Testers wanted] Laser Support - Fast PWM Updates


By the looks of it, that branch only adds support for a single ‘high throughput’ pin, while I need a lot of them…

I guess it is not so easy to reduce this minimum time for SET_PIN, as it probably requires to go to some higher throughput channel, seeing how @Cirromulus implemented it.

Also, if I need to set a lot of pins, that will be a lot of SET_PIN commands, as it can only set one pin at a time.

Are there ways to send a string or something to a remote device, that I can then use as a remote IO device? Which I can update every x milliseconds? Then I encode the 88 values into a few characters, and then do the actual pin-setting at the remote IO device.

While reading through the docs, I see it supports generic SPI command sending, which could very well fulfill my needs (MCU commands - Klipper documentation). Then i just have to match it to a few SPI based port expanders, and Bob’s your uncle.

Hi, interesting project.

Have you thought about something like this I2C/SPI GPIO expander support??

Good luck, hcet14

You are right, currently there is a limit to the number of high_throughput pins.
However, if you use your 88 SET_PIN commands directly after each other (in blocks), it might just work.
The limit can be commented out in klipper/klippy/ at 319e4e4eab69914c77c55c097fe0924350ca751e · Cirromulus/klipper · GitHub, if you want to try it out anyways.

If it doesn’t work, you would probably have to add your own queue-able ‘topic’ where every valve is represented by one bit in a 11 byte array. This would require a bit firmware-coding, but could ultimately use my high_troughput mode, just with 11 bytes instead of one.

I was now thinking along the lines of making a CAN based port extender board to control my valves. I see that Klipper support CAN bus, but as far as I can tell, not necessarily in a generic way, i.e.: send a CAN message through a G-code. That would be ideal: i just send the required valve configuarion (ON/OFF) for all 88 valves as a CAN message (or perhaps two, as only 8 bytes data length is supported).

If I look at this information, only specific MCU’s are supported as CAN devices. However, I planned on creating a custom CAN device, which uses simple CAN port expanders (MCP25050). Is this possible? Or do I need to create a board using one of the supported MCUs, and use that basically as a port expander?

(I’m looking into CAN, as the valves will sit several meters away from the controller board, for which SPI/I2C is not really suited)

Now that I think a bit more about it, I have connected a CAN-board with a SPI interface. Does that mean I might be able to use the send SPI command to send messages over CAN? I haven’t delved too deep into the workings of SPI/CAN with klipper yet, maybe somebody can help me understand?