Duet3D Logo Duet3D
    • Tags
    • Documentation
    • Order
    • Register
    • Login
    1. Home
    2. tfjield
    3. Best
    • Profile
    • Following 0
    • Followers 0
    • Topics 11
    • Posts 82
    • Best 15
    • Controversial 0
    • Groups 0

    Best posts made by tfjield

    • RE: Mesh Bed Compensation Capabilities

      @3dreamer

      I ended up getting my problems all sorted out. But I had to do several things:

      • Increase the supports for my bed so that it was closer to level to start with. My bed is a bit over 600 mm x 600 mm and wasn't sufficiently supported, so there was a significant sag in the middle. Now I still have some sag, but my maximum variance across the surface is 0.387 mm.
      • Fix the twist in my gantry. There was a couple degree twist along my y-axis, and due to the location of my BL Touch relative to my nozzle, just a degree resulted in a very significant height error. One degree of twist was over 0.4 mm of height error.
      • I picked a common point on one corner for my z-axis datum, my probe z offset, my bed tilt probing points, and my bed mesh compensation.
      • Performed a high resolution map (21 x 21) at temperature.
      • Modified my macros so that for every print it will do the following:
        o Verify that the axes have been homed. If not, home them.
        o Verify that the bed has been adjusted for tilt. If not, adjust it, and home z again.
        o Turn on bed mesh compensation.

      I do not like the commonly accepted approach of using the center of the bed for my z datum. I work in a field where I'm very conscious of how things fail, and I don't want my datum at what is most likely the lowest point on my bed. If, for some reason, I don't turn on bed mesh compensation and I'm zeroed at that point, them I'm likely to run into my bed as I move away from the center and screw up my print head and PEI sheet. So I want my datum at one of the highest points.

      Also, I want the corners of my bed tilt and bed mesh to be identical to my datum point, and use the same exact point for measuring the probe's z offset. In this way, it's easy for me to verify that everything is working right. My bed mesh should show zero compensation at that corner, and because I've already corrected the tilt, all four corners of the bed mesh should show almost zero compensation.

      It's been a few weeks and several km of filament since I last took a bed mesh map, and my prints are rock-solid every time, now.

      posted in General Discussion
      tfjieldundefined
      tfjield
    • RE: Event notifications not dismissing on multiple clients

      @chrishamm
      Even though the messages aren't dismissed with M292, once the message either times out or close is pressed somewhere, why is the event message shown again, ever? Especially if it has a timeout, it must not be that important to keep coming back.

      How do we get rid of it and keeping it from showing up over and over?

      posted in Duet Web Control
      tfjieldundefined
      tfjield
    • Scale Layer Chart by Shown Data, Only

      Currently, the layer chart scales the Y axis by all the layer times:

      27f04b1f-5a58-4690-91d0-ed264073cd05-image.png

      But when showing only the last 30 layers, the Y axis stays the same:

      52f37d32-b61b-4852-b587-5da1aee06257-image.png

      When subsequent layers are much faster than the layers before, the graph loses it's value, since all the points are clustered near the bottom.

      My request is to adjust the Y axis based on the shown data, only.

      posted in Duet Web Control wishlist
      tfjieldundefined
      tfjield
    • RE: Intermittent WiFi Connection at Startup

      @oliof @Phaedrux
      I may have found something. On a hunch, I decided to replace the SD card with a faster one, thinking that there might be some kind of race condition occurring during startup.

      The card that came with it is a SanDisk Edge 8 GB class 4. Here's a stock image of the card:
      43983426-2011-4549-a057-036e0fd23447-image.png

      I replaced it with a SanDisk Ultra.

      With the original card it would boot and connect to WiFi no more than half the time. After replacing the card, it's connected 5 out of 5 reboots. I'll continue testing, but it seems much better!

      EDIT: This is on the same AP; I've only changed the card.

      posted in General Discussion
      tfjieldundefined
      tfjield
    • RE: Error in Docs for M591

      @comediantf2
      This is what I put in filament-error.g so that it would work with both 3.3 and 3.4+:

      M25				; first, pause the print as pre-RRF 3.3 did
       
      if {param.D == 0}		; now call the right pre-RRF 3.4 subroutines.
      	M98 P"filament-error0.g"
      elif {param.D == 1}
      	M98 P"filament-error1.g"
      
      posted in Filament Monitor
      tfjieldundefined
      tfjield
    • RE: G-Code Meta commands: Option unset (all) globals

      @fcwilt 🤣

      posted in Firmware wishlist
      tfjieldundefined
      tfjield
    • RE: Extruders Not Obeying G1 Feed Rate

      @phaedrux Nope, just set to 100%. My long print is almost done, and I'm going to try to duplicate the problem and gather more info.

      posted in General Discussion
      tfjieldundefined
      tfjield
    • Cura gcode Save to Disk with Thumbnails

      @resam
      Per the conversation in the other thread, I was looking for a an option in Cura to save a gcode file to disk with QOI thumbnails. I took your thumbnail code from github and incorporated it into this post-processing script:

      import os
      import base64
      import traceback
      from io import StringIO
      
      from PyQt6 import QtCore
      from PyQt6.QtCore import QCoreApplication, QBuffer
      from PyQt6.QtGui import QImage
      
      from UM.Application import Application
      from UM.Logger import Logger
      from UM.Math.Matrix import Matrix
      
      from cura.Snapshot import Snapshot
      from cura.PreviewPass import PreviewPass
      
      from ..Script import Script
      from qoi import QOIEncoder
      
      
      class CreateQOIThumbnail(Script):
          def __init__(self):
              super().__init__()
              
          def render_scene(self):
              scene = Application.getInstance().getController().getScene()
              active_camera = scene.getActiveCamera()
              render_width, render_height = active_camera.getWindowSize()
              render_width = int(render_width)
              render_height = int(render_height)
              Logger.log("d", f"Found active camera with {render_width=} {render_height=}")
      
              QCoreApplication.processEvents()
      
              preview_pass = PreviewPass(render_width, render_height)
              fovy = 30
              satisfied = False
              zooms = 0
              while not satisfied and zooms < 5:
                  preview_pass.render()
                  pixel_output = preview_pass.getOutput().convertToFormat(QImage.Format.Format_ARGB32)
                  # pixel_output.save(os.path.expanduser(f"~/Downloads/foo-a-zoom-{zooms}.png"), "PNG")
      
                  min_x, max_x, min_y, max_y = Snapshot.getImageBoundaries(pixel_output)
                  size = max((max_x - min_x) / render_width, (max_y - min_y) / render_height)
                  if size > 0.5 or satisfied:
                      satisfied = True
                  else:
                      # make it big and allow for some empty space around
                      zooms += 1
                      fovy *= 0.75
                      projection_matrix = Matrix()
                      projection_matrix.setPerspective(fovy, render_width / render_height, 1, 500)
                      active_camera.setProjectionMatrix(projection_matrix)
      
                  Logger.log("d", f"Rendered thumbnail: {zooms=}, {size=}, {min_x=}, {max_x=}, {min_y=}, {max_y=}, {fovy=}")
      
              # crop to content
              pixel_output = pixel_output.copy(min_x, min_y, max_x - min_x, max_y - min_y)
              Logger.log("d", f"Cropped thumbnail to {min_x}, {min_y}, {max_x - min_x}, {max_y - min_y}.")
              # pixel_output.save(os.path.expanduser("~/Downloads/foo-b-cropped.png"), "PNG")
      
              Logger.log("d", "Successfully rendered scene.")
              return pixel_output
      
      
          def render_thumbnail(self, pixel_output, width, height):
              # scale to desired width and height
              pixel_output = pixel_output.scaled(
                  width, height,
                  aspectRatioMode=QtCore.Qt.AspectRatioMode.KeepAspectRatio,
                  transformMode=QtCore.Qt.TransformationMode.SmoothTransformation
              )
              Logger.log("d", f"Scaled thumbnail to {width=}, {height=}.")
              # pixel_output.save(os.path.expanduser("~/Downloads/foo-c-scaled.png"), "PNG")
      
              # center image within desired width and height if one dimension is too small
              if pixel_output.width() < width:
                  d = int((width - pixel_output.width()) / 2. + 0.5)
                  pixel_output = pixel_output.copy(-d, 0, width, pixel_output.height())
                  Logger.log("d", f"Centered thumbnail horizontally {d=}.")
              if pixel_output.height() < height:
                  d = int((height - pixel_output.height()) / 2. + 0.5)
                  pixel_output = pixel_output.copy(0, -d, pixel_output.width(), height)
                  Logger.log("d", f"Centered thumbnail vertically {d=}.")
              # pixel_output.save(os.path.expanduser("~/Downloads/foo-d-aspect-fixed.png"), "PNG")
      
              Logger.log("d", f"Successfully rendered {width}x{height} thumbnail.")
              return pixel_output
      
          def encode_as_qoi(self, thumbnail):
              # https://qoiformat.org/qoi-specification.pdf
              pixels = [thumbnail.pixel(x, y) for y in range(thumbnail.height()) for x in range(thumbnail.width())]
              pixels = [(unsigned_p ^ (1 << 31)) - (1 << 31) for unsigned_p in pixels]
              encoder = QOIEncoder()
              r = encoder.encode(
                  width=thumbnail.width(),
                  height=thumbnail.height(),
                  pixels=pixels,
                  alpha=thumbnail.hasAlphaChannel(),
                  linear_colorspace=False
              )
              if not r:
                  raise ValueError("image size unsupported")
              Logger.log("d", f"Successfully encoded {thumbnail.width()}x{thumbnail.height()} thumbnail in QOI format.")
      
              size = encoder.get_encoded_size()
              return encoder.get_encoded()[:size]
      
          def encode_as_png(self, thumbnail):
              buffer = QBuffer()
              buffer.open(QBuffer.ReadWrite)
              thumbnail.save(buffer, "PNG")
              buffer.close()
              return buffer.data()
      
          def generate_thumbnail(self):
              thumbnail_stream = StringIO()
              Logger.log("d", "Rendering thumbnail image...")
              try:
                  scene = self.render_scene()
      
                  # PanelDue: 480×272 (4.3" displays) or 800×480 pixels (5" and 7" displays)
                  # ref https://forum.duet3d.com/post/270550 and https://forum.duet3d.com/post/270553
                  thumbnail_sizes = [
                      (48, 48),
                      (128, 128),
                      (160, 160),
                      (256, 256),
                  ]
                  for width, height in thumbnail_sizes:
                      thumbnail = self.render_thumbnail(scene, width, height)
                      qoi_data = self.encode_as_qoi(thumbnail)
                      b64_data = base64.b64encode(qoi_data).decode('ascii')
                      b64_encoded_size = len(b64_data)
      
                      thumbnail_stream.write(f"; thumbnail_QOI begin {width}x{height} {b64_encoded_size}\n")
                      max_row_length = 78
                      for i in range(0, b64_encoded_size, max_row_length):
                          s = b64_data[i:i+max_row_length]
                          thumbnail_stream.write(f"; {s}\n")
                      thumbnail_stream.write(f"; thumbnail_QOI end\n")
      
                  Logger.log("d", "Successfully encoded thumbnails as base64 into gcode comments.")
      
                  return thumbnail_stream
              except Exception as e:
                  Logger.log("e", "failed to create snapshot: " + str(e))
                  Logger.log("e", traceback.format_stack())
                  # continue without the QOI snapshot
                  return StringIO()
                  
          def getSettingDataString(self):
              return """{
                  "name": "Create QOI Thumbnail",
                  "key": "CreateQOIThumbnail",
                  "metadata": {},
                  "version": 2,
                  "settings":
                  {
                      "enabled":
                      {
                          "label": "Enabled",
                          "description": "Enable to generate thumbnails.",
                          "type": "bool",
                          "default_value": true
                      }
                  }
              }"""
              
          def execute(self, data):
              if not self.getSettingValueByKey("enabled"):
                  return data;
          
              snapshot = self.generate_thumbnail()
              if snapshot:
      
                  for layer in data:
                      layer_index = data.index(layer)
                      lines = data[layer_index].split("\n")
                      for line in lines:
                          if line.startswith(";Generated with Cura"):
                              Logger.log("d", "Found appropriate place in gcode file.")
                              line_index = lines.index(line)
                              insert_index = line_index + 1
                              lines[insert_index:insert_index] = snapshot.getvalue()[:-1].split("\n")
                              break
      
                      final_lines = "\n".join(lines)
                      data[layer_index] = final_lines
      
              return data
      

      This is just your code, slightly edited to work as a script, and it works well for me. I should have done this up on github, but I've never created a repository there and it would take me more time to figure it out than I wanted to spend today! lol. Besides, I'm just a hack, so if you wanted to take this (or do it better) please do!

      Thanks for doing the heavy lifting!

      posted in General Discussion
      tfjieldundefined
      tfjield
    • RE: Extruders Not Obeying G1 Feed Rate

      @fcwilt @Phaedrux
      Just an update, I've found that the issue isn't just when there's a filament error. If I just home the machine, turn on the hot end, and then try to retract to change the filament, I often have the same issue: the extruder races to try to extract. Hitting retract again and it usually runs at the normal rate.

      When this print is done, I'll see if this happens from a cold start so it can be duplicated.

      posted in General Discussion
      tfjieldundefined
      tfjield
    • RE: Mesh Outside of Limits

      @fcwilt Maybe! lol. What I mean is that with G30, I can probe wherever I want. The axes limits are not obeyed. Meaning if I set a location for the probe that will move the printhead beyond the limits, it will just do it. (This behavior is warned about in the documentation.) With G29, if such a move would require the printhead to move out of limits, it gives an error. (And trying to avoid the error by using M564 S0 doesn't work. ) And like I said, it surprises me that exceeding the limits is explicitly allowed by G30, but not by G29.

      posted in General Discussion
      tfjieldundefined
      tfjield
    • RE: Query Mesh Bed Compensation Status

      @fcwilt Thank you!

      Yes, I can add frame members from front to back. There are already two members there, but they're not under the edges of the bed. (You can see one of them on the right in the image above. Just to the right of the actual bed. That's how they tell us to put it together.) My guess is that they originally were under the bed, but now they have us put them off to the side so that the bed sag compensates for the y-axis sag. (The y-axis also runs front to back.)

      I've read a few posts about people stiffening up the y-axis, and if I do that, then I'll add the support members for the bed, as well. 👍

      posted in General Discussion
      tfjieldundefined
      tfjield
    • RE: Query Mesh Bed Compensation Status

      @fcwilt Yup, I'm with you! It's just triage right now in the little time I have between prints! 😬

      posted in General Discussion
      tfjieldundefined
      tfjield
    • RE: Query Mesh Bed Compensation Status

      OK, I spent this weekend calibrating and tuning and learning RRF, and I made a lot of progress!

      Turns out my problem was a twisted y-axis. It was pretty square on the front but twisted quite a bit on the back. As my probe offset in the x-axis is 24 mm, even a couple of degrees would result in a significant layer height error. Fixing this problem was the key.

      But beyond that, I changed quite of the logic around the bed leveling.

      My bed has the typical "Modix sag" because it is not supported with rigid-enough members. The front and rear rails are 2020 extrusion spanning ~80 cm, and that's just not stiff enough. I have 2040 on order, and I'm going to upgrade these when it arrives. It won't eliminate the sage, but should improve it significantly.

      It also sags front to back, as there is no support on the sides, but my guess is that's intentional: The y-axis gantry sags, too and as a result, you can barely see it in the surface maps! I'll deal with this at a later date...

      I watched the video, but it wasn't very useful for my situation. My bed is supported on the 4 corners, not just 3 points, and while I can't make the midpoints of the 4 edges coplanar, I can make the corners coplanar.

      So I put my z datum at the front left corner, because this is one of the highest spots. If mesh bed compensation is turned off for some reason, the failure mode will be printing too high rather than engraving into my PEI sheet. (And I keep finding new macros that turn mesh bed compensation off! 🤦 I think I've fixed all that, now, so I shouldn't be surprised and I'm not rehoming all the dang time!)

      I modified the routines so that:

      • The z homing routine is at the datum location,
      • The 4-corner leveling routine uses the same location for one of the corners,
      • The mesh bed compensation is performed between the 4 corners used for bed leveling. and
      • The probe z height compensation is performed at the same location.

      As a result, the height map reads zero at the 4 corners, which includes my datum.

      I've performed the height mapping with a 21 x 21 grid, and my first layer tests are looking amazing, now! Now on to fine tuning my slicer settings...

      Thank you for all your help!!

      And a pretty pictures:
      8c25e2f1-9d32-4a30-9564-06cf5adaab19-image.png
      1d35718e-eef9-45f9-a757-a36072a52b78-image.png

      posted in General Discussion
      tfjieldundefined
      tfjield
    • RE: Tool Fan and Primary Blower Fan Linked?

      @jay_s_uk Ah, thank you! That makes sense now that I think about it... I never named the hotend fan "Tool Fan." It just seemed like the tool fan! lol

      And thanks @fcwilt!

      posted in General Discussion
      tfjieldundefined
      tfjield
    • RE: Incorrect extrusion amount on slow and/or small axis moves

      @fcwilt Oh, you're right, I was thinking backwards. I forgot that he wants to limit extrusion to below the max, not retraction.

      posted in General Discussion
      tfjieldundefined
      tfjield