Duet 3 6XD, Vacuum switches COMMAND/REGEX

626 views
Skip to first unread message

Zdenko Stanec

unread,
Dec 30, 2022, 12:20:35 PM12/30/22
to OpenPnP
Cheers all,

I am in process of testing Duet 3D 6XD, still waiting for 2pcs of 3HCs that will be later connected via CAN and mounted on the head of the machine. 

I started with updating FW to the latest stable revision and all went well, I managed to start with the OpenPnP setup and initial config file just to do a test on a desk to try to activate some outputs on 6XD, and it is working.

I had a few questions on the Duet forum and they were really fast and supportive, but now I have a question that is oriented toward OpenPnP. The plan is to use four SMC ZSE30A-01-N-L (NPN) vacuum switches that will be mounted on the head and later connected to one of the 3HC boards, hardware connection is pretty straightforward and simple, it can be connected on any IO pins from 6XD or 3HC but the thing that is bothering me right now is config file setup for this Vacuum input pins and OpenPnP ACTUATOR_READ_COMMAND and ACTUATOR_READ_REGEX setup.

Does anyone have something like this connected and working?

Thank you,

Zdenko,


Fernando Corona

unread,
Dec 30, 2022, 2:45:13 PM12/30/22
to ope...@googlegroups.com
For my setup at work I used the 6xd with a venturi vacuum system. Not sure what kind of vacuum switches they are just connected them to the heater pins H0 and H1 and worked fine. I don't have a vacuum sensor so I just set them up as Boolean in OpenPnP, and use the following gcode conditional statements in the activate boolean. 

M42 P1 S{True:0}{False:1}
M42 P0 S{True:1}{False:0}
G4 P500
M42 P1 S{True:0}{False:0}

First line turns off the blow switch if it's on to ensure both blow and vacuum switch are never on at the same time,

Second line turns on the vacuum switch 

Third line is a pause which effective serves as the blow off time.  P500 is in milliseconds use S instead of P for seconds.

Fourth line is to turn off the blow.  

Try it out and let me know if you need some assistance with it. Took me days to research how to do this. I knew about the dwell command, and had to research conditional gcode. 

Fernando,
Electrical Engineer/Process Engineer 

--
You received this message because you are subscribed to the Google Groups "OpenPnP" group.
To unsubscribe from this group and stop receiving emails from it, send an email to openpnp+u...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/openpnp/41a86c58-36fe-4f76-8164-50e62df9bc74n%40googlegroups.com.

Zdenko Stanec

unread,
Dec 30, 2022, 3:12:16 PM12/30/22
to OpenPnP
Hello Fernando,

Thank you for the replay, 

I am aware of how to operate the solenoid valves, but I am interested in how to read the Input pins of the connected NPN Vacuum sensor/switch on one of many I/O-s on the Duet 3, to get a proper reading of the sensors/switch  ACTUATOR_READ_COMMAND and ACTUATOR_READ_REGEX need to correct.

For me, it is a Vacuum "switch", as the value is 0 or 1(0V or 24V), the threshold level of the vacuum is adjusted on the sensor, so if the value is above or below a certain point output changes state.

Thank you,

Zdenko,

Zdenko Stanec

unread,
Dec 31, 2022, 2:51:54 PM12/31/22
to OpenPnP
Hello,

So I managed to edit the config file from Duet for four Vacuum sensors:

M558 K0 P8 C"!io0.in"
M558 K1 P8 C"!io1.in"
M558 K2 P8 C"!io2.in"
M558 K3 P8 C"!io3.in"

Sending G31 K0 to G31 K3 gives the status of each input pin.

For G31 K0 in ACTUATOR_READ_COMMAND I get this:

2022-12-31 20:23:51.735 GcodeAsyncDriver$WriterThread TRACE: [serial://COM21] >> G31K0
2022-12-31 20:23:51.736 GcodeDriver$ReaderThread TRACE: [serial://COM21] << Z probe 0: current reading 0, threshold 500, trigger height 0.700, offsets X0.0 Y0.0 U0.0 V0.0 W0.0 A0.0 B0.0
2022-12-31 20:23:51.736 GcodeDriver$ReaderThread TRACE: [serial://COM21] << ok

So, I need to write a proper REGEX to read a "current reading" that will be 0 or 1000:

"Z probe 0: current reading 0, threshold 500, trigger height 0.700, offsets X0.0 Y0.0 U0.0 V0.0 W0.0 A0.0 B0.0"

I am using https://regex101.com/ to test REGEX and this string, I know I need to have ?<Value> somewhere in a REGEX so OpenPnP can use that value, but I had no luck.

This will be a long night! 🤕😁

Br,

Zdenko,

Zdenko Stanec

unread,
Dec 31, 2022, 3:50:46 PM12/31/22
to OpenPnP
Hello,

Little update, it seems I managed to get it right.

With REGEX .*(?<Value>(?<=reading\s)\d+).* I am now able to "Read Value" and use it in OpenPnP.

"Z probe 0: current reading 0, threshold 500, trigger height 0.700, offsets X0.0 Y0.0 U0.0 V0.0 W0.0 A0.0 B0.0"
"Z probe 0: current reading 1000, threshold 500, trigger height 0.700, offsets X0.0 Y0.0 U0.0 V0.0 W0.0 A0.0 B0.0"

2022-12-31 21:42:14.169 ReferenceActuator DEBUG: AVAC1.read(): 0
2022-12-31 21:42:50.046 ReferenceActuator DEBUG: AVAC1.read(): 1000

Hope this helps someone in the future who is using a Vacuum sensor "switch" such as SMC ZSE30A-01-N-L.

Br,

Zdenko,

dc42

unread,
Jan 5, 2023, 5:03:32 PM1/5/23
to OpenPnP
To make your code less dependent on the exact format of the G31 response, it might be better to configure the input pin as GPIN like this:

M950 J0 C"io2.in"

and then read it via the object model like this:

M409 K"sensors.gpIn[0].value"

which will elicit a JSON response:

{"key":"sensors.gpIn[0].value","flags":"","result":1}

Your regex need only look for the result part. It will be 0 or 1.

Zdenko Stanec

unread,
Jan 5, 2023, 6:22:48 PM1/5/23
to OpenPnP
Hello dc42,

Thank you for your reply and nice to see you here!

I have tested this on all four inputs and it is working, REGEX for this would be .*(?<Value>(?>\d+)).*, so I get 0 or 1 at "Read Value" in OpenPnP.

2023-01-06 00:18:48.131 ReferenceActuator DEBUG: AVAC1.read(): 0
2023-01-06 00:18:57.418 ReferenceActuator DEBUG: AVAC1.read(): 1

The only thing I noticed is this:

With disabled "Compress Gcode":
2023-01-06 00:11:44.720 GcodeDriver$ReaderThread TRACE: [serial://COM21] << {"key":"sensors.gpIn[0].value","flags":"","result":0}

With enabled "Compress Gcode":
2023-01-06 00:11:11.251 GcodeDriver$ReaderThread TRACE: [serial://COM21] << {"key":"sensorsgpIn[0]value","flags":"","result":null}

So I need to run it without "Compress Gcode" for it to work reliably.

Br,

Zdenko,

dc42

unread,
Jan 7, 2023, 10:32:59 AM1/7/23
to OpenPnP
It appears that the Compress GCode option deleted the "." in the key "sensors.gpIn" variable name. I will ask Mark if he can change Compress GCode to avoid removing any characters inside quoted strings.

mark maker

unread,
Jan 7, 2023, 12:13:14 PM1/7/23
to ope...@googlegroups.com

Hi,

The G-code compression does only support the NIST RS274NGC standard, section 3.3. As far as I've seen, there are no "string" types defined there. 😁

I'm a bit reluctant to add string support outside the standard, as it opens a can of works like how to escape special characters including the quote character itself.

Personally, I find zdenko's first solution way better. The G31 K0 syntax is standards conformant and terse, as it should be.

The response is lengthy/human oriented, but I guess that can't be changed to a more machine readable format now, or it would break backwards compatibility. Perhaps the "current " prefix should be added to the regex, so if Duet adds a "maximum reading" one day, it remains deterministic.

Maybe I'll add a field for "Non-G-code characters" to the GcodeDriver one day. When it sees one of these, it would skip compression for that line altogether.

_Mark

dc42

unread,
Jan 10, 2023, 10:38:19 AM1/10/23
to OpenPnP
Hi Mark,

The NIST standard unfortunately makes no provision for string parameters. This has been found to be far too limiting in the 3D printing sphere. So Marlin, Smoothieware and RRF have long supported string parameters where necessary, for example when passing the names of files on the SD card. In the event that OpenPnp used any of these commands, the GCode compression you use would garble the filename if it removed the "." character; but I guess OpenPnP normally has no reason to access the SD card on the controller.

We found the use of string parameters without delimiters highly unsatisfactory, and we needed to do better in RRF. We chose the obvious solution of enclosing strings in double quote characters. We don't support escape characters in strings, but we do treat two consecutive double quote characters in a string as a single double quote character. So no special parsing of the contents of a string is needed in code that just wants to skip the string.

The NIST standard does not specify a compression algorithm; so the algorithm you use in OpenPnP is your choice. It appears to me that you are probably removing any "." character that is followed by zero or more 0-digits before the next non-digit character. Might I suggest that you require the preceding character to be a digit too? That would eliminate most of these situations in which removal of a "." is undesirable.

Parsing the response from G31 K0 may appear to be a simple solution, but responses intended to be human-readable are subject to change. For example, we might decide to add additional information to the response in a future firmware version, and that wold likely break the regex used to parse the response. Whereas we rarely make changes to existing object model values, and we document those changes in the release notes when we do. There are also very many values that can be read from the object model for which there are no commands to return the same information in any other form.

Best regards, David

mark maker

unread,
Jan 10, 2023, 1:48:55 PM1/10/23
to ope...@googlegroups.com

Hi,

It just occurred to me that the dot could be encoded with backlash escaping (must be enabled on the OpenPnP driver). Then the compression leaves it alone. This should work:

M409 K"sensors\u002EgpIn[0]\u002Evalue"

> The NIST standard unfortunately makes no provision for string parameters.

I do see the problem. As said, I might implement a way to avoid compression altogether, when certain characters (like the quote char) appear on a line, so it would be completely free in syntax. Such non-standard lines should then be rare, of course.

Also note that compression is only applied when sending, not when receiving (obviously), so the response can be anything, as long as it is on one line.

> The NIST standard does not specify a compression algorithm; so the algorithm you use in OpenPnP is your choice.

I humbly beg to differ 😎. NIST does specify that spaces and tabs are allowed and to be ignored everywhere, including in the middle of decimals (3.3).

Spaces and tabs are allowed anywhere on a line of code and do not change the meaning of the line, except inside comments. This makes some strange-looking input legal. The line “g0x +0. 12 34y 7” is equivalent to “g0 x+0.1234 y7”, for example.

It does specify exactly how decimals can and must be formatted, that the dot is optional, and even that trailing zeroes have no meaning (3.3.2.1).

... zero to many digits, followed, possibly, by (3) one decimal point, followed by (4) zero to many digits ...

A number written with initial or trailing zeros will have the same value when it is read as if the extra zeros were not there.

These are essentially the means of my simple "compression".

Removing comments is inasmuch defined in the standard (3.3.4), as it says:  "Comments do not cause a machining center to do anything."

Removing comments is a separate option in OpenPnP, because they can legally be used to send messages.

> Might I suggest that you require the preceding character to be a digit too?

The standard allows for zero initial digits, e.g. .3, however, I should check for at least one digit left or right, as the standard says:

... provided that there is at least one digit somewhere in the number

I will do that. It would solve the above problem, but it will still eat trailing zeroes, a dot after a digit, and any spaces, that might still be significant inside such strings.

> Parsing the response from G31 K0 may appear to be a simple solution, but responses intended to be human-readable are subject to change.

So G31 K0 S1 could force a machine-readable, stable output 😁

_Mark

dc42

unread,
Jan 11, 2023, 9:51:54 AM1/11/23
to OpenPnP

> It just occurred to me that the dot could be encoded with backlash escaping (must be enabled on the OpenPnP driver). Then the compression leaves it alone. This should work:

> M409 K"sensors\u002EgpIn[0]\u002Evalue"

Thanks, that provides a workaround.

> So G31 K0 S1 could force a machine-readable, stable output

Yes; but then if/when we add extra information to the G31 information, we'd need to add a new format S2 to provide machine-readable output that includes the new information. Whereas in M409 we already have a mechanism to report any of the hundreds of supported variables in machine-readable form.

BTW an even easier way to report the reading would be to use the command:

echo sensors.gpIn[0].value

if OpenPnp will allow that to be sent. echo is a scripting command supported by RRF, like if, while etc. M409 is more general because it allows a subtree of the object model to be queried, not just a single variable.

mark maker

unread,
Jan 11, 2023, 10:09:22 AM1/11/23
to ope...@googlegroups.com

> Yes; but then if/when we add extra information to the G31 information, we'd need to add a new format S2 to provide machine-readable output that includes the new information.

As long as each piece of the info is labelled, you can add new pieces. Similar to the M114 or M115 response.

In fact on Smoothie and others we use M105, which simply reports all sensors, each labelled, usually with "label:" prefix.

_Mark

dc42

unread,
Jan 11, 2023, 11:43:36 AM1/11/23
to OpenPnP
@mark we've encountered issues on more than on occasion when Octoprint, Pronterface, Repetier Server or another GCode sender has failed to parse the response to a common 3D printer command such as M105, M114 and M119 after we've made a minor change to the response. So I prefer to leave these responses alone. Besides, I wouldn't want to return all of the following info in every M105 response! (it's the JSON response returned by M409 K"sensors" with newlines inserted by the viewer for clarity.)

{
    "key": "sensors",
    "flags": "",
    "result": {
        "analog": [
            {
                "lastReading": 23.87,
                "name": "",
                "state": "ok",
                "type": "thermistor"
            },
            {
                "lastReading": 23.96,
                "name": "T0",
                "state": "ok",
                "type": "thermistor"
            },
            {
                "lastReading": 23.73,
                "name": "T1",
                "state": "ok",
                "type": "thermistor"
            },
            {
                "lastReading": 30.53,
                "name": "",
                "state": "ok",
                "type": "remote"
            },
            {
                "lastReading": 22.72,
                "name": "",
                "state": "ok",
                "type": "remote"
            }
        ],
        "endstops": [
            {
                "highEnd": false,
                "triggered": false,
                "type": "motorStallAny"
            },
            {
                "highEnd": false,
                "triggered": false,
                "type": "motorStallAny"
            },
            null,
            {
                "highEnd": false,
                "triggered": false,
                "type": "motorStallAny"
            }
        ],
        "filamentMonitors": [
            null,
            null,
            {
                "configured": {
                    "allMoves": false,
                    "mmPerRev": 28.8,
                    "percentMax": 160,
                    "percentMin": 60,
                    "sampleDistance": 3
                },
                "enabled": true,
                "status": "ok",
                "type": "rotatingMagnet"
            },
            {
                "configured": {
                    "allMoves": false,
                    "calibrationFactor": 1,
                    "percentMax": 160,
                    "percentMin": 60,
                    "sampleDistance": 3
                },
                "enabled": true,
                "status": "ok",
                "type": "laser"
            }
        ],
        "gpIn": [
            null,
            null,
            {
                "value": 1
            },
            {
                "value": 1
            }
        ],
        "probes": [
            {
                "calibrationTemperature": 25,
                "deployedByUser": false,
                "disablesHeaters": false,
                "diveHeight": 3,
                "lastStopHeight": 0,
                "maxProbeCount": 1,
                "offsets": [
                    0,
                    0,
                    0,
                    0
                ],
                "recoveryTime": 0,
                "speeds": [
                    1000,
                    600
                ],
                "temperatureCoefficients": [
                    0,
                    0
                ],
                "threshold": 200,
                "tolerance": 0.03,
                "travelSpeed": 30000,
                "triggerHeight": 0,
                "type": 8,
                "value": [
                    0
                ]
            }
        ]

mark maker

unread,
Jan 11, 2023, 12:18:21 PM1/11/23
to ope...@googlegroups.com

I do see the applications and the reasoning. 

I guess the underlying issue is that AFAIK, neither NIST nor the community attempted to standardize responses in a "grammatical" way.

_Mark

mark maker

unread,
Jan 13, 2023, 2:14:18 PM1/13/23
to ope...@googlegroups.com

Hi,

The issue with G-code compression can now be addressed:

https://user-images.githubusercontent.com/9963310/212386397-aae87388-3a2b-4cb8-97a7-6ee996926b4b.png

Details here:

https://github.com/openpnp/openpnp/pull/1515

_Mark

Zdenko Stanec

unread,
Jan 13, 2023, 5:09:11 PM1/13/23
to OpenPnP
Hello,

I tested with M409 with enabled and disabled "Compress Gcode", it is working well now.

Final for one vacuum sensor input:

ACTUATOR_READ_COMMAND    M409 K"sensors.gpIn[0].value"
ACTUATOR_READ_REGEX             .*(?<Value>\d+).*

2023-01-13 23:02:28.099 GcodeAsyncDriver$WriterThread TRACE: [serial://COM21] >> M409K"sensors.gpIn[0].value"
2023-01-13 23:02:28.100 GcodeDriver$ReaderThread TRACE: [serial://COM21] << {"key":"sensors.gpIn[0].value","flags":"","result":0}
2023-01-13 23:02:28.101 GcodeDriver$ReaderThread TRACE: [serial://COM21] << ok
2023-01-13 23:02:28.101 GcodeDriver TRACE: actuatorRead response: {"key":"sensors.gpIn[0].value","flags":"","result":0}
2023-01-13 23:02:28.101 ReferenceActuator DEBUG: AVAC1.read(): 0

2023-01-13 23:02:31.980 GcodeAsyncDriver$WriterThread TRACE: [serial://COM21] >> M409K"sensors.gpIn[0].value"
2023-01-13 23:02:31.981 GcodeDriver$ReaderThread TRACE: [serial://COM21] << {"key":"sensors.gpIn[0].value","flags":"","result":1}
2023-01-13 23:02:31.981 GcodeDriver$ReaderThread TRACE: [serial://COM21] << ok
2023-01-13 23:02:31.981 GcodeDriver TRACE: actuatorRead response: {"key":"sensors.gpIn[0].value","flags":"","result":1}
2023-01-13 23:02:31.982 ReferenceActuator DEBUG: AVAC1.read(): 1

Thank you,

Zdenko,

mark maker

unread,
Jan 14, 2023, 4:31:36 AM1/14/23
to ope...@googlegroups.com

Hi Zdenko,

thanks for testing so quickly!

I think the ACTUATOR_READ_REGEX  should look for "result": specifically.

Something like that (I'm no regex expert):

	.*"result"[ \t]*:[ \t]*(?<Value>\d+).*
If RepRapFirmware someday adds new properties to the JSON, like so
	{"key":"sensors.gpIn[0].value","flags":"","result":3,"min":2,"max":5}

you're still fine.

Tested here:

https://www.regexplanet.com/cookbook/ahJzfnJlZ2V4cGxhbmV0LWhyZHNyEwsSBlJlY2lwZRiAgICG0ZyfCww/index.html

_Mark

Zdenko Stanec

unread,
Jan 14, 2023, 9:21:29 AM1/14/23
to OpenPnP
Hey Mark,

Good point, I tested your proposed regex, and this one .*\"result\":(?<Value>\d+).*, it seems both are working well in all situations.

Tested on https://regex101.com/ Java 8.

Zdenko,

Zdenko Stanec

unread,
Apr 4, 2023, 3:46:05 PM4/4/23
to OpenPnP
Hi

I need to get back on this topic, 

Till now I was doing bench testing and all was working fine,  now a few months later machine testing has begun and only one Sensor reads the vacuum status, and the other three are not working, I did not change anything and all connections and config files for OPenPnP and Duet are the same (where 4 sensors work), I even tested outputs from the sensors on Duet3 inputs pins with a multimeter and sensors are working OK, I just can not read vacuum status...

I know there were a few test releases in the meantime which can maybe cause some issues, can someone take a look at this trace log please?

Thank you,

Zdenko,
Trace_Log.txt

mark maker

unread,
Apr 4, 2023, 4:02:53 PM4/4/23
to ope...@googlegroups.com
2023-04-04 21:28:21.203 GcodeAsyncDriver DEBUG: serial://COM9 commandQueue.offer(M409 K"sensors.gpIn[4].value", 5000)...
2023-04-04 21:28:21.203 GcodeAsyncDriver$WriterThread TRACE: [serial://COM9] >> M409 K"sensors.gpIn[4].value"
2023-04-04 21:28:21.203 GcodeDriver$ReaderThread TRACE: [serial://COM9] << {"key":"sensors.gpIn[4].value","flags":"","result":null}
2023-04-04 21:28:21.203 GcodeDriver$ReaderThread TRACE: [serial://COM9] << ok
2023-04-04 21:28:26.212 AbstractMachine TRACE: Exception caught, executing pending motion: java.lang.Exception: Actuator "AVAC4" read error: No matching responses found.

Duet seems to respond with null

Seems to an issue on the Duet side.

_Mark

Zdenko Stanec

unread,
Apr 4, 2023, 4:22:14 PM4/4/23
to OpenPnP
Hi,

Hmm, but nothing changed on the Duet side, config.g is the same no firmware update was done.


Part of the Duet config.g:

M950 J0 C"11.!io0.in" ;Vacuum Sensor_1 - works OK
M950 J1 C"11.!io3.in" ;Vacuum Sensor_2 - reads 0
M950 J2 C"11.!io1.in" ;Vacuum Sensor_3 - reads 0
M950 J3 C"11.!io4.in" ;Vacuum Sensor_4 - "result":null

The only thing I changed is basically the PC.

But if you check the log from the start, for Vacuum Sensor_1 works OK, other sensors have ACTUATOR_READ_COMMAND the same just ports on it and on Duet are different, strange.  :-/ 

Zdenko,

Zdenko Stanec

unread,
Apr 5, 2023, 2:56:15 AM4/5/23
to OpenPnP
Hi,

I think I know where is the issue...in me again probably!😣 but I need someone to confirm it! 😁

I made a last-minute GPin change on pins in Duet3 config.g to make the wiring a bit tidier on the head of the machine and the test was not performed after that as I recall...😶

After creating the GPin port for the input with M950 and J parameter in the config.g like this for example:

M950 J1 C"11.!io3.in" ;Vacuum Sensor_2

In object model sensors.inputs[n], for [n] do I need to put [1] from the J parameter or actual GPin number [3] from this example above? 

This would explain a lot if I have initially done testing on GPin Duet pins 0,1,2,3 and I had J parameters 0,1,2,3, but after pin change, J parameters stayed the same 0,1,2,3 but GPin pins in Duet config.g where changed to 0,3,1,4 like this:

M950 J0 C"11.!io0.in" ;Vacuum Sensor_1

M950 J1 C"11.!io3.in" ;Vacuum Sensor_2
M950 J2 C"11.!io1.in" ;Vacuum Sensor_3
M950 J3 C"11.!io4.in" ;Vacuum Sensor_4

Zdenko,

Zdenko Stanec

unread,
Apr 5, 2023, 11:18:42 AM4/5/23
to OpenPnP
Yeeeeap, all is good now!

It seems it was too late and I was half asleep :D...

In object model sensors.inputs[n], for [n] I need to put J[n] parameter not GPin number. 😁

Zdenko,

dc42

unread,
Apr 5, 2023, 3:13:02 PM4/5/23
to OpenPnP
> In object model sensors.inputs[n], for [n] do I need to put [1] from the J parameter

Yes, that's correct.

Reply all
Reply to author
Forward
0 new messages