3 Motor Z axis Automatic bed levelling using G32 RRF 3.01-rc3

  • I have a question regarding automatic bed levelling using 3 independent Z motors...

    Board info:
    RepRapFirmware for Duet 2 WiFi/Ethernet version 3.01-RC3 running on Duet Ethernet 1.02 or later

    I've followed the instructions, as outlined in the Wiki (https://duet3d.dozuki.com/Wiki/Bed_levelling_using_multiple_independent_Z_motors) and all moves as expected?, but there is no automatic adjustment of each z axis motor, to compensate for any tilt at start up etc. Is this to be expected ? and the system just build an internal calculation off-set, or should the G32 command be iterative and make adjustments to each motor dependently to level the bed ??

    Have I got the wrong end of the stick or have I missed something in the configuration files etc ??

    I can manually loop through a G32 process to level the bed, and that is shown in the attached files, but I'd obviously prefer for it to be automatic, such that it could then be added to a start sequence....

    config.g M122.txt heightmap.csv

  • If you are asking if G32 occurs at power up of the printer, or anything else that levels the bed, the answer is no.

    You may wish to write a "Prepare" (or whatever you want to call it) macro that is part of your "Standard Operating Procedure" (in the human operators mind) for power up.

    • Physically prepare for power on, clear the bed, park any tools, etc, etc.
    • Power on
    • Wait for boot and Web connect
    • Home the printer with the dashboard (or maybe put this in your macro, your choice)
    • Run the "Prepare" macro, that contains at least one G32. Maybe other things. Maybe it pre-warms the bed to an intermediate temp, etc.

    Now ready to run a job.


    • I do have a bed prewarm in my prepare macro for some printers, because they have very thick aluminum or glass beds, and if the print job does a simple "wait", the top of the bed won't really be warm.
    • Also, I tend to run G32 three times. For a whole bunch of reasons, I can't really probe EXACTLY over my leadscrews that raise the bed, therefore, the math done by the firmware won't be perfect on the first pass. I often see deviation of 0.xxx (arbitrarily large) after the first pass, .02 or something after 2, and .00x, sometimes even .000, after the third pass.
    • For this reason, I actually have a set of three G28 Z G32 in a separate macro (the G28 Z is not really necessary; nonetheless, it makes me more comfortable!). I can fire this and do something else for a few min, and come back to a level printer.

    And, last, please note there is a bug in various R3 RCx that G32 does not return results to the console. So you may not be able to see the convergence across three passes... for the moment, just do three (at least every so often).

  • I realise that G32 doesn't run at start up....

    & yes my initial actions post power are the same as yours:
    Power on
    wait for Web access etc.
    Home Printer
    Then run "True Bed Leveling" using DWC i.e. calls G32

    But when I run the G32 command, I get NO output, but as your last line says, if there are known bugs in the RC's then I'm screwed till the 3.01 is released....

    But my question still holds, in that, when the G32 command is run x number of times, does the system just store a calculated off-set between the three Z screws or does it actually move the screws relative to each other to level out any differences.

  • @Dr_Ju_Ju said in 3 Motor Z axis Automatic bed levelling using G32 RRF 3.01-rc3:

    But my question still holds, in that, when the G32 command is run x number of times, does the system just store a calculated off-set between the three Z screws or does it actually move the screws relative to each other to level out any differences.

    Got it, thanks for clarifying the question.

    G32 moves the motors. Levels the bed, physically. Doesn't store anything (that I know of)

    So, while the bug exists that it doesn't print the results on the console, it does work. And, you can get a pretty good idea of the amount of correction by watching all three motors in the second or two just after the last probe.

    In fact, you can intentionally manually turn one screw, maybe a couple of turns (depending on pitch), then run G32, just to see what it looks like. Small corrections are very hard to see. Large corrections are unmistakable, and will give a good mental image of what is going on. Even if you turn only one screw, it will likely adjust all three, and in different directions.

    In fact, it is a tiny bit spooky...

  • Moderator

    @Dr_Ju_Ju The bed.g will just run once. The levelling using G30 will run, but if it's more than 0.02mm out (defined by S parameter in M671 in config.g) it will not apply the correction. In your macro it then goes on to run a bed mesh, which will be

    I'd separate these out into two macros. Run the bed-levelling macro as many times as needed at power-on, though I'd increase the S parameter in M671 to S0.5. You should get a message in the console that it has applied any compensation; I have bed levelling on an X gantry on 2 steppers, and get:

    M98 P"0:/macros/X-levelling"
    Leadscrew adjustments made: -0.009 0.085, points used 2, (mean, deviation) before (0.042, 0.018) after (-0.000, 0.000)

    The bed mesh shouldn't need to be run as often; unless you're changing the bed temperature or something else that may effect it's shape, you should be able to load a saved bed. I generally aim to use as little firmware correction as possible, by having the machine physically as square and flat as possible!

    Because you are running RRF 3.01, you can script this macro to repeat until it hits an acceptable deviation. For an example, see https://forum.duet3d.com/topic/14100/issue-converting-bed-g-to-use-conditional-gcode


  • Moderator

    @Dr_Ju_Ju said in 3 Motor Z axis Automatic bed levelling using G32 RRF 3.01-rc3:

    But when I run the G32 command, I get NO output

    That's strange, because I get:

    9 points probed, min error -0.174, max error 0.126, mean -0.045, deviation 0.083
    Height map saved to file 0:/sys/RRF3/heightmap.csv
    FIRMWARE_NAME: RepRapFirmware for Duet 2 WiFi/Ethernet FIRMWARE_VERSION: 3.01-RC4 ELECTRONICS: Duet WiFi 1.0 or 1.01 FIRMWARE_DATE: 2020-03-16b1


  • Clarification:

    (1) The "no output to console" bug applies ONLY to Duet 3 with Pi. I just noticed you were running a Duet 2 on R3 firmware. You should get the output, as shown by droftarts.

    (2) G32 behavior depends on the content of file bed.g. Please post your /sys/bed.g

  • Thanks for confirming what should be happening, but unfortunately on my system, NO adjustments are made to the motors, post a run of G32. I've just built a multi G32 to see if anything happens, which it doesn't...

    So I've either got something wrong in my config\bed files (attached) or the board is 'duff' , or does this not work for a CoreXY printer, as the Wiki just mentions Deltas ??

    The Wiki also mentions using the S switches to store values, which I've been playing with i.e.


    S0 Default value. Transformation matrix is updated in RAM but is not stored. Z bed height not calculated.

    S1 Transformation matrix is updated in RAM but is not stored. Printer immediately moves to Z maximum position (Z max endstop required!), and calculates new Z maximum height. You must first issue G28 to home to Z maximum position before issuing G32 Snnn for this to work correctly, or the printer height will be invalid.

    S2 Same as S1, except transformation matrix and Z max heights are also stored.

    S3 Transformation matrix is stored. Z bed height not calculated.

  • In reply to Droftarts; all I get in the console is:

    26/03/2020, 15:03:39 M115
    FIRMWARE_NAME: RepRapFirmware for Duet 2 WiFi/Ethernet FIRMWARE_VERSION: 3.01-RC4 ELECTRONICS: Duet Ethernet 1.02 or later FIRMWARE_DATE: 2020-03-16b1
    26/03/2020, 15:03:29 G32
    26/03/2020, 15:01:49 M122
    === Diagnostics ===
    RepRapFirmware for Duet 2 WiFi/Ethernet version 3.01-RC4 running on Duet Ethernet 1.02 or later
    Board ID: 08DGM-95BNL-MGPSJ-6J1F2-3SD6L-9JZHX
    Used output buffers: 1 of 24 (14 max)

    Note that I've also updated to RC4, disabled the bed mesh commands in bed.g, & set M671 to S0.5 and physically turned the lead screws to introduce an off set of at least 1mm

  • Moderator

    @Dr_Ju_Ju Your bed.g

    ; bed.g
    ; called to perform automatic bed compensation via G32
    M561 ; clear bed transform
    G29 S2 ; clear height map
    G28  ; home all axis
    G90  ; use absolute co-ordinates
    ; Probe the bed at 3 points adjacent to motors
    G30 P0 X10 Y10 H0 Z-99999
    G30 P1 X150 Y295 H0 Z-99999
    G30 P2 X290 Y10 H0 Z-99999
    ; Mesh bed probing to generate height map & activate 
    G29 S0 ; perform mesh bed probe (as defined in config.g)
    G29 S1 ; load mesh map and acivate bed compensation

    On the last G30 you need 'S' (or 'S3') to make it calculate the bed level. So should be:
    G30 P2 X290 Y10 H0 Z-99999 S3

    On the last G30 command in the sequence, the S parameter indicates that a complete set of points has been probed and instructs the firmware what sort of calibration to perform. If the value is -1 then the Z offsets of all the points probed are printed, but no calibration is done. If the value is zero or not present, then this specifies that the number of factors to be calibrated is the same as the number of points probed. Otherwise, the value indicates the number of factors to be calibrated, which must be no greater than the number of points probed.

    You should also get an output from G29 S0, but maybe because you have (a redundant) G29 S2 afterwards, it's suppressing the output? Try running G29 from the console on it's own, and see what you get.


  • It does work on a CoreXY, I have one.

    The reason you are not getting output, nor motor movement, is because your third G30 is missing S3

    My bed.g:

    G30 P0 X152.5 Y0 Z-99999       ; probe near back leadscrew
    G30 P1 X290   Y230 Z-99999      ; probe near front left leadscrew
    G30 P2 X20    Y280 Z-99999 S3   ; probe near front right leadscrew and calibrate 3 motors
    G1     X152.5   Y152.5 F10000

  • Thank you Ian & Danal, it was the missing S3 in bed.g.

    At least I now have a starting point to work with. I've already disabled the G29 commands in bed.g so I'll build another macro to provide the functionality.

    If I manually run G28 followed by G29 I get this in the console:

    26/03/2020, 15:56:58 G29
    9 points probed, min error -1.491, max error 1.774, mean 0.154, deviation 0.945
    Height map saved to file 0:/sys/heightmap.csv

    26/03/2020, 15:55:21 G32
    Leadscrew adjustments made: 0.781 0.307 -1.367, points used 3, (mean, deviation) before (-0.076, 0.764) after (-0.000, 0.000)

    which appears to throw things all out...

  • Moderator

    @Dr_Ju_Ju All that G32 does is call bed.g. When defining bed mesh or doing Z levelling/leadscrew adjustment, you need to disable any other compensation with M561 and G29 S2, which you were doing in bed.g.

    You need to run the leadscrew adjustment every time you power off the stepper motors, because that's when it loses position. When the stepper motors turn back on, they'll jump to the nearest full step position. Over time the bed level will drift, until you run the leadscrew adjustment again. So it's easiest just to do it every time you turn the machine on (except if you're resuming an interrupted print job).

    So after power on and G28 homing, do the leadscrew adjustment. Repeat it until you're happy with its accuracy (or use the conditional gcode macro I linked earlier). The bed level should be repeatable and accurate, so running a full bed mesh should be unnecessary. I'd usually do a central G30 (probe to set Z0) afterwards, too, but that's in my start.gcode too. If needed, then create a bed mesh for the bed at the target temperature. Save this, and load it the next time you turn on the printer after you've done the leadscrew adjustment.

    Then, get printing!


  • I'm getting closer.... and I'm already doing lots of printing on my main machine, an old Prusa i3 MK2s, but it just works....

    One problem I've got at the moment, is getting some decent PETg as everyone has shut up shop, & I won't waste money on the crap from eBay, and now also Amazon....

  • @Dr_Ju_Ju said in 3 Motor Z axis Automatic bed levelling using G32 RRF 3.01-rc3:

    which appears to throw things all out...

    If this has never worked for you before, it is possible that every time you run it, bed level will get WORSE. You can literally see this with your eyes after just one or two runs.

    If this happens, your motors are plugged into the drivers in the wrong order. It can be very difficult to describe in words how to get the order correct. Basically, they must be in the same order, physically, as the drivers specified in M584. In my CoreXY:

    M584 X0 Y1 U2 Z3:4:5        ; X and Y for CoreXY.  U for toolchanger lock. Z has three drivers for kinematic bed suspension. 
    M584 E1.0:1.1:2.0:2.1       ; Extruders for four tools. 

    Three is "front left", four is "back middle" and five is "front right". For me. But "front back right left" are all relative... so just try it different ways. Until it works.

    Summary: If G32 makes bed level worse instead of better, change the order of motors to plugs (power off!) until it works correctly.

  • Thanks, everything moves correctly & can even print ok, so I'm back to swapping motors around & re-configuring....

    ROFL I may just forget it & manually set up, in the end it may well be quicker....

    Another thought, instead of modifying config.g, make changes to the probing order in bed.g ?? I'll play....

  • @Dr_Ju_Ju said in 3 Motor Z axis Automatic bed levelling using G32 RRF 3.01-rc3:

    in the end it may well be quicker....

    Not when you do it a hundred times over weeks or months. And, the printer will do it to the repeatability limits of the probe.

  • @Dr_Ju_Ju said in 3 Motor Z axis Automatic bed levelling using G32 RRF 3.01-rc3:

    modifying config.g, make changes to the probing order in bed.g

    Don't change config.g, nor the bed.g. Change plugs.

    Physical plug ordering matching M584 matters. Probing order in bed.g does not.

  • @Danal Back to it this Morning & thanks, & yes just needed to swap 2 motor connectors....

    I also see what you mean, that the process needs to be run multiple times, as I'm finding that the system makes a change but doesn't quite get it right, so subsequent runs get things closer.....

    Methinks the process should be more like the process used for Auto Focusing Telescopes (another hobby of mine) where the process goes back and forth through the focus point, till the optimum setting is found...

  • Hello,
    I have a similar problem.
    I have configured a BL-Touch Z-Probe using "M574 Z1 S2"
    and I am trying to to 3 Point Bed Levelling via the "G32" command.
    My bed.g:

    G1 X20 Y60 F1400
    G28 Z ; home
    G30 P0 X20 Y60 Z-99999 H0		; probe at point 1
    G30 P1 X40 Y100 Z-99999	H0	; probe at point 2
    G30 P2 X60 Y60 Z-99999 S3 H0	; probe at point 3 and calibrate the 3 z motors

    Doing this, I only get:

    Warning: 3/4-point bed compensation is deprecated and will be removed in a future firmware release. Please use G29 mesh bed compensation instead.
    Bed equation fits points [20.0, 60.0, -4.305] [40.0, 100.0, -6.194] [60.0, 60.0, -5.399]

    My M678 looks like this:

    M671 X-396:171:-54 Y--86,82:235,88:-86,82 S5	   ; Koordinaten der drei Z-Achsen

    So S is set to 5mm. I want the bed to physically level itself instead of only giving me this console message.

    Thank you already,

  • Hi,

    I cannot make sense out of your bed.g file and your M671 command.

    What are the X and Y axis min/max values?

    Do you have a diagram showing the position of your lead screws in relation to the bed?


  • @fcwilt Thank you for your response,

    i just noticed that I used commata instead of decimal points at the M671 command, what could be problematic...
    The x-Coordiantes of the leadscrews are +396mm, +171mm and -54 mm. The y-Coordinates of the leadscrews are -86.82mm, +235,88mm and -86.82mm. The S parameter allows height correction up to 5mm per leadscrew.

    This is my config.g

    ; General preferences
    G90                                            ; send absolute coordinates...
    M83                                            ; ...but relative extruder moves
    M550 P"My Printer"                             ; set printer name
    ; Network
    M552 S1                                        ; enable network
    M586 P0 S1                                     ; enable HTTP
    M586 P1 S0                                     ; disable FTP
    M586 P2 S0                                     ; disable Telnet
    ; Drives
    M569 P0 S1                                     ; physical drive 0 goes forwards		(X-Motor)
    M569 P1 S1                                     ; physical drive 1 goes forwards		(Y-Motor)
    M569 P2 S1                                     ; physical drive 2 goes forwards		(Z-Motor front left)
    M569 P3 S1                                     ; physical drive 3 goes forwards		(Z-Motor back mid)
    M569 P4 S1									   ; physical drive 4 goes forwards		(Z-Motor front right)
    M569 P5 S1									   ; physical drive 5 goes forwards		(Extruder1)
    M584 X0 Y1 Z2:3:4 E5                           ; set drive mapping 					(0-X, 1-Y, 2,3,4-Z, 5-E)
    M669 K1                                        ; select CoreXY mode		(Muss nach M584 kommen, da dort neue Achsen definiert werden)
    M671 X-396:171:-54 Y--86,82:235,88:-86,82 S5  ; Coordiantes of the three Z-Leadscrews
    M350 X16 Y16 Z16 E16 I1                        ; configure microstepping with interpolation
    M92 X160.00 Y160.00 Z1600 E420.00              ; set steps per mm
    M566 X900.00 Y900.00 Z12.00 E120.00            ; set maximum instantaneous speed changes (mm/min)
    M203 X3000.00 Y3000.00 Z100.00 E1200.00        ; set maximum speeds (mm/min)
    M201 X500.00 Y500.00 Z20.00 E250.00            ; set accelerations (mm/s^2)
    M906 X1330 Y1330 Z1330 E800 I30                ; set motor currents (mA) and motor idle factor in per cent
    M84 S30                                        ; Set idle timeout
    ; Axis Limits
    M208 X0 Y0 Z0 S1                               ; set axis minima
    M208 X320 Y272 Z300 S0                         ; set axis maxima
    ; Endstops
    M574 X1 S1 P"xstop"                            ; configure active-high endstop for low end on X via pin xstop
    M574 Y1 S1 P"ystop"                            ; configure active-high endstop for low end on Y via pin ystop
    M574 Z1 S1 P"zstop+e0stop+e1stop"              ; configure active-high endstops for low end on Z via pin zstop
    ; Z-Probe
    M558 P9 C"^zprobe.in" H10 F150 T2000           ; BL-Touch Implementation: Set Input Pin
    M950 S2 C"duex.pwm5"						   ; BL-Touch Implementation: Set PWM Pin 
    G31 X0 Y-47.53 Z2.7 P25                        ; BL-Touch Implementation: Set Z probe trigger value, offset and trigger height
    M557 X25:105 Y25:105 S20                         ; define mesh grid
    ; Heaters
    M308 S0 P"bedtemp" Y"thermistor" T100000 B4138 ; configure sensor 0 as thermistor on pin bedtemp
    M950 H0 C"bedheat" T0                          ; create bed heater output on bedheat and map it to sensor 0
    M307 H0 B1 S1.00                               ; enable bang-bang mode for the bed heater and set PWM limit
    M140 H0                                        ; map heated bed to heater 0
    M143 H0 S120                                   ; set temperature limit for heater 0 to 120C
    M308 S1 P"e0temp" Y"thermistor" T100000 B4138  ; configure sensor 1 as thermistor on pin e0temp
    M950 H1 C"e0heat" T1                           ; create nozzle heater output on e0heat and map it to sensor 1
    M307 H1 B0 S1.00                               ; disable bang-bang mode for heater  and set PWM limit
    ; Fans
    M950 F0 C"fan0" Q500                           ; create fan 0 on pin fan0 and set its frequency
    M106 P0 S0 H-1                                 ; set fan 0 value. Thermostatic control is turned off
    M950 F1 C"fan1" Q500                           ; create fan 1 on pin fan1 and set its frequency
    M106 P1 S1 H1 T45                              ; set fan 1 value. Thermostatic control is turned on
    ; Tools
    M563 P0 D0 H1 F0                               ; define tool 0
    G10 P0 X0 Y0 Z0                                ; set tool 0 axis offsets
    G10 P0 R0 S0                                   ; set initial tool 0 active and standby temperatures to 0C

    Before running my bed.g file, I home every axis with the opctical endstops at every leadscrew. Because this is not that exact, I want to level the bed using the BL-Touch afterwards. To Do this, i reconfig the endstop definition like this:

    M574 Z1 P"nil"              ; deactivate leadscrew endstops
    M574 Z1 S2		 	; Z1: Enstop at low end, S2: Using Z-Probe

    After that is done, i run my bed.g

    I hope you understand better what I did there now 🙂


  • Hi,

    In the M671 command you have a hyphen after the X and the Y. Why is that?

    I don't know if commas are allowed in place of decimal points.

    And unless I am confused there is no reason to disable your Z end stops to use the Z probe for auto bed leveling.

    I didn't when I experimented with auto bed leveling and it all worked fine.


  • Moderator

    @Lasko Please create a new thread for your issue rather than tacking on to the end of someone else's solved issue.

  • @Phaedrux
    I would do it, but my problem got solved by @frederikv 🙂

    The problem was the commata and the "-" in the M671 command 😉

    I chnaged a lot of code because it didn't work as I wanted for a long time. Thank you @frederikv for the Help!


Log in to reply