Ramblings of someone on the internet

Maybe flakes...

So I think I know how to flakes! Sort of...

After much pain over a week of learning, and a lot of failing, I've started to understand some of why people use flakes. I don't believe that this is something that any sane person should learn, but that applies to most of Nix. Having nix around should be like having docker around for many people. You don't know how it works, but you can use a docker compose file and just do light edits to have most of the gain for knowing nearly nothing. With that said, if you are dumb enough to fall down the rabbit hole like I was, here's what I learned about flakes. First some highlights of what they even are.

  • Nix channels become inputs. No need to manage these anymore if that bothers you.
  • Thanks to version locking, it's great for keeping systems in sync with each other.
  • Solves what I was doing with linking home manager configs "for free"

So far, I've been managing NixOS on 2 computers, home-manager is managed on many machines, but with 3 definitions. This lead me to deciding to pull all of my configs into one flake. While I don't let NixOS or Nix-Darwin manage the home-manager bits themselves, I can put it all in a single flake to keep it all in one place. If you want to see the entire flake as it was at the time of posting there ya go, but I'm going to only pull out snippits to talk about.

So that's all it does? Why bother now?

Pretty much, yes. I managed to break my Gentoo install on my M1 Mac mini server completely by my own stupidity and not knowing how the hardware worked while compiling the kernel, and decided to just give NixOS a toy with on it instead. I was already on NixOS on my rarely used laptop, and quickly noticed that a lot of my configs were the same, so I started to integrate them into reusable parts, much like I did in home-manager. I got about half way through combining them when I realized that I would need to run git as root as the directories were root owned, and decided that maybe I should stop doing things my way, and learn the right way.

Where is the best place to start?

At the inputs of course! These are the things that I pull software from. They are software repos with their own flakes that I can grab and use as I need.

  inputs = {
    # Nixpkgs
    nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable";

    # Home manager
    home-manager.url = "github:nix-community/home-manager";
    home-manager.inputs.nixpkgs.follows = "nixpkgs";

    darwin = {
      url = "github:lnl7/nix-darwin";
      inputs.nixpkgs.follows = "nixpkgs";
    };

    apple-silicon.url = "github:tpwrules/nixos-apple-silicon";
    
    hyprland.url = "github:hyprwm/Hyprland";
    
    emacs = {
      url = "github:nix-community/emacs-overlay";
      inputs.nixpkgs.follows = "nixpkgs";
    };
  };

Because I ran an M1 mac in the setup, I already had to take the apple silicone remote, so I looked around for others. I'm using hyprland on my laptop as something different, and even grabbed the emacs overlay to get some more optimized versions as I use Doom Emacs for most of my code editing.

Voodoo magic

The code below is code I took from another repo. It handles making systems in a format that's clean and easy to read, and handles the voodoo for me. Unless I end up needing a system not defined in the forAllSystems section, I can simply not touch this and let it do it's magic.

  outputs = {
    self,
    nixpkgs,
    home-manager,
    ...
  } @ inputs: let
    inherit (self) outputs;
    forAllSystems = nixpkgs.lib.genAttrs [
      "aarch64-linux"
      "i686-linux"
      "x86_64-linux"
      "aarch64-darwin"
      "x86_64-darwin"
    ];

    mkNixos = modules:
      nixpkgs.lib.nixosSystem {
        inherit modules;
        specialArgs = {inherit inputs outputs;};
      };

    mkDarwin = system: modules:
      inputs.darwin.lib.darwinSystem {
        inherit modules system inputs;
        specialArgs = {inherit inputs outputs;};
      };

    mkHome = modules: pkgs:
      home-manager.lib.homeManagerConfiguration {
        inherit modules pkgs;
        extraSpecialArgs = {inherit inputs outputs;};
      };

Bonus, it even includes a devshell to pull in deps to build everything initially.

    # Devshell for bootstrapping
    # Acessible through 'nix develop' or 'nix-shell' (legacy)
    devShells = forAllSystems (
      system: let
        pkgs = nixpkgs.legacyPackages.${system};
      in
        import ./shell.nix {inherit pkgs;}
    );

The important bits. System management!

Thankfully, once we get to this point, the voodoo above makes it a lot easier. You just define systems, and point them to a nix file and do the normal, not flake stuff mostly from there. My NixOS/Nix-Darwin machines get their name and files pointed, then home manager needs a username as well, as it's for, well, me the user, and not the system.

    nixosConfigurations = {
      # M1 mac mini
      farnsworth = mkNixos [./hosts/farnsworth];

      # Laptop
      amy = mkNixos [./hosts/amy];
    };

    darwinConfigurations = {
      # M2 Mac mini
      cubert = mkDarwin "aarch64-darwin" [./hosts/cubert];
    };
    homeConfigurations = {
      "kdb424@amy" = mkHome [./home-manager/machines/amy.nix] nixpkgs.legacyPackages.x86_64-linux;
      "kdb424@cubert" = mkHome [./home-manager/machines/cubert.nix] nixpkgs.legacyPackages.aarch64-darwin;
      "kdb424@farnsworth" = mkHome [./home-manager/machines/headless.nix] nixpkgs.legacyPackages.aarch64-linux;
      "kdb424@planex" = mkHome [./home-manager/machines/headless.nix] nixpkgs.legacyPackages.x86_64-linux;
      "kdb424@zapp" = mkHome [./home-manager/machines/headless.nix] nixpkgs.legacyPackages.x86_64-linux;
    };

With all of that defined, it basically just automates detection of the system and user, and will build things for me. I yoinked and modified a justfile from someone to make it even easier, so I can run just switch or just hm-switch to easily update the respective system and home manager stuff. It was a lot of learning, and a lot of yoinking, but it's starting to manage much of itself. Finally...

What's the point?

I've already started porting more and more out of homebrew on the Mac, and starting to feed my configs into it as I still have a full backup in git with yadm. Things like yabai and skhd have been pretty easy to migrate once I got my head wrapped around it. Getting things out of homebrew is quite nice, as homebrew, while it usually works, has not been an amazing tool for me to use, and breaks more than I would like. I've also gotten tired of managing my hosts file on my systems as I don't have, or want, a DNS resolver inside of my Zerotier network, so being able to update that in one place and all systems get it has been great. As I was writing this, I was fixing issues on my VPS that could have been much faster with Nix to resolve as I'm more familiar with Nix at this point than Nginx. There's still things I could do, but Nix will have to be something that I understand to the same level or greater than what I already know. For now, I'll stick to just using docker in nix to manage my services as I can't see a ton of reason on why that can't continue to work and be relatively invisible to me. Nix can at least automate making my compose files easy to access over NFS.

My first nix flake!

It's happened, I've drank the punch.

So I got into dev shells a while back, but I've started to run into some issues, mostly with other's nix shell environments. QMK's shell.nix is one such issue, and I believe it has something to do with it being written for NixOS, or at least Nix on Linux. This highlights one of the biggest weaknesses of using "bare" nix as opposed to Nix Flakes, with their ability to build different targets. With that out of the way on the why I bothered, here's my first flake!

This blog!

{
  description = "A flake for developing and building my personal website";

  # It's a flake, may as well try the latest
  inputs.nixpkgs.url = "github:nixos/nixpkgs/nixpkgs-unstable";

  # Useful utilities to automatically create targets for different
  # platforms. Just makes it more readable in this case.
  inputs.flake-utils.url = "github:numtide/flake-utils";

  # This builds a package, in this case, containing zola
  
  outputs = { self, nixpkgs, flake-utils }:
    flake-utils.lib.eachDefaultSystem (system:
      let
        pkgs = nixpkgs.legacyPackages.${system};
      in
      {
        # This uses nix to fully build the site as a nix package.
        packages.website = pkgs.stdenv.mkDerivation rec {
          pname = "static-website";
          version = "2023-08-26";
          src = ./.;
          nativeBuildInputs = [ pkgs.zola ];
          buildPhase = "zola build";
          installPhase = "cp -r public $out";
        };
        # This output is what we use for our dev shell.
        defaultPackage = self.packages.${system}.website;
        devShell = pkgs.mkShell {
          packages = with pkgs; [
            gnumake
            zola
          ];
        };
      }
    );
}

While functionally similar to the previous system without a flake, it will create a flake.lock file. Currently it looks like this, though you should never hand edit this file, I'm just showing what's in it.

{
  "nodes": {
    "flake-utils": {
      "inputs": {
        "systems": "systems"
      },
      "locked": {
        "lastModified": 1692799911,
        "narHash": "sha256-3eihraek4qL744EvQXsK1Ha6C3CR7nnT8X2qWap4RNk=",
        "owner": "numtide",
        "repo": "flake-utils",
        "rev": "f9e7cf818399d17d347f847525c5a5a8032e4e44",
        "type": "github"
      },
      "original": {
        "owner": "numtide",
        "repo": "flake-utils",
        "type": "github"
      }
    },
    "nixpkgs": {
      "locked": {
        "lastModified": 1693355128,
        "narHash": "sha256-+ZoAny3ZxLcfMaUoLVgL9Ywb/57wP+EtsdNGuXUJrwg=",
        "owner": "nixos",
        "repo": "nixpkgs",
        "rev": "a63a64b593dcf2fe05f7c5d666eb395950f36bc9",
        "type": "github"
      },
      "original": {
        "owner": "nixos",
        "ref": "nixpkgs-unstable",
        "repo": "nixpkgs",
        "type": "github"
      }
    },
    "root": {
      "inputs": {
        "flake-utils": "flake-utils",
        "nixpkgs": "nixpkgs"
      }
    },
    "systems": {
      "locked": {
        "lastModified": 1681028828,
        "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
        "owner": "nix-systems",
        "repo": "default",
        "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
        "type": "github"
      },
      "original": {
        "owner": "nix-systems",
        "repo": "default",
        "type": "github"
      }
    }
  },
  "root": "root",
  "version": 7
}

This will fully lock down all of the sources by revision of git commit, or similar. This insures that if Zola updates, and it breaks the blog, no new install of Zola on a new machine, or a newly spawned shell will seemingly randomly break. This gives us the option of updating only when we are ready, knowing that all machines are on identical versions of the software, and we can update and push the exact same update to all machines at the same time. Updating the flake is as simple as running nix flake update. If everything works as expected, then commit the new flake.lock file to the repository and push it like any other change to the repo.

Other upsides

Now that I have a flake, I can simply reference it from other flakes, even if I don't have it locally installed on the machine. FlakeHub is a common place that people push their flakes for others to use, as is GitHub. Flakes can be layered similar to docker images, so you can use other's flakes to build on top of to make creating tooling easier, as well as deployment of build tools! Nix can, and will, eat your everything if you let it.

Further rambling outro

While I have stated that I won't let Nix eat my everything, I'm coming around to the cool tooling that it has to offer. I doubt that I'll let it completely take over everything, but time will tell. I've still not got amazing things to say about NixOS, and quite like having a system underneath nix to run the show, but I try to give things a try before truly dismissing them. Maybe next I'll try to build docker images with nix as I'm not a huge fan of creating images the "normal" way. Still keeping docker, but changing the build tooling. We'll see I suppose.

Useful snippits that may help you out.

Make a shell package, or basic flake to start a dev environment, all in a zsh/bash function.

nixify() {
  if [ ! -e ./.envrc ]; then
    echo "use nix" > .envrc
    direnv allow
  fi
  if [[ ! -e shell.nix ]] && [[ ! -e default.nix ]]; then
    cat > default.nix <<'EOF'
with import <nixpkgs> {};
mkShell {
  nativeBuildInputs = [
    bashInteractive
  ];
}
EOF
    ${EDITOR:-vim} default.nix
  fi
}
flakify() {
  if [ ! -e flake.nix ]; then
    nix flake new -t github:nix-community/nix-direnv .
  elif [ ! -e .envrc ]; then
    echo "use flake" > .envrc
    direnv allow
  fi
  ${EDITOR:-vim} flake.nix
}

And some more reading if you want to know more. These are the resources that I used to get where I am with this post.

https://ejpcmac.net/blog/migrating-to-a-static-blog/
https://fasterthanli.me/series/building-a-rust-service-with-nix/part-10
https://zero-to-nix.com/concepts/flakes

Per Axis, Per Feature. Let's go!

So you want some speed, huh?

Before we get into the how to do this, I want to explain what this does at a basic level to make sure it's even worth your time. This allows you to independantly control the acceleration of the X/Y axies on your CoreXY, Cartesian, or (untested) CoreXZ printer. If you aren't one that wants to get more speed, aren't even close to the limits of what you can print with your printer currently, or just don't like to tune your printer, please stop reading now. Hope you have a wonderful day!

Ok, you still with me? Let's get going then.

What does this involve doing at a high level?

Don't run away scared as it is actually quite simple to set up. We'll be pulling in a new file into klipper that offers you a new set of kinematics in software. By default, it won't change anything at all, and just adds new functionality to the defaults, and we'll go through the basics of what you can do with it if you choose to play further with it, but you won't need to understand much of how it actually works to get easy gains.

Second, we are going to install a gcode post processor. This is just a bit of code that you won't have to have any clue how it works. We'll go through how to install it so your slicer does all of the heavy lifting for you!

Third, we will add a few special comments to your start gcode in the slicer so the post processing code knows what to set your accelerations to. It's mostly copy paste, and just change numbers, and is no harder than setting up per feature acceleration in your slicer that you are already using.

Getting new kiematics!

If you have ever used a quickdraw probe, or installed macros from someone else, this is quite similar. We'll be grabbing a file, and saving it to ~/klipper/klippy/kinematics/. If you prefer a command line, here are some instructions for each kinematics.

Command line install method

For this, you'll want to SSH into your printer (usually a pi), and run the correct command for your printer. Don't worry if you run the wrong one as it won't hurt anything. You'll just want to make sure you also run the correct line to make sure you have the file you'll need.

CoreXY

curl https://raw.githubusercontent.com/kdb424/klipper/peraxis-kdb/klippy/kinematics/limited_corexy.py --output ~/klipper/klippy/kinematics/limited_corexy.py

CoreXZ

curl https://raw.githubusercontent.com/kdb424/klipper/peraxis-kdb/klippy/kinematics/limited_corexz.py --output ~/klipper/klippy/kinematics/limited_corexz.py

Cartesian

curl https://raw.githubusercontent.com/kdb424/klipper/peraxis-kdb/klippy/kinematics/limited_cartesian.py --output ~/klipper/klippy/kinematics/limited_cartesian.py

GUI method.

For this you'll want to use the file explorer in your printer's file manager. Navigate to the ~/klipper/klippy/kinematics folder. Click one of the following links for COREXY COREXZ CARTESIAN copy all of the text, and save it to a file named limited_corexy.py limited_corexz.py or limited_cartesian.py depending on which link you clicked. The instructions are also in the file if you ever forget, or want some more information.

Using your new kinematics.

So you grabbed the files for your new kinematics, but they aren't doing anything. We need to do some basic setup. For each machine, basic setup instructions are found in the files your downloaded. You don't have to understand any of the scary code, but it tells you in the comments how to switch over to the kinematics for your recpective machine. The important bit is switching from corexy or whatever your kinematic is, to limited_corexy. That simple change is all that is actually required, but there's more options to play with if you so choose.

For normal klipper style docs, you can read them for

Now for the slicer side.

I'll be explaining this in terms of SuperSlicer, but it should translate easily to most modern slicers, including PrusaSlicer and OrcaSlicer.

We'll want to grab the file from here and save this on your computer with the slicer. This is just a text file, so you can click SAVE_AS in your browser or CTRL-S on Windows/Linux, and CMD-S on Mac if it just shows up as text. Open up the file with a text editor. I've included the instructions in there, and will link to the help page for PrusaSlicer here as well. I haven't used OrcaSlicer, but the process should be similar as they all share the same base and are just tweaks to the same core slicer. You'll notice if you have read the file as instructed, we'll need to add some special comments to the start gcode of the section in the slicer. This absolutely must be done in the slicer and not the firmware at this time because it post processes locally, not on the printer.

Custom gcode

Under Printer Settings -> Custom Gcode -> Start Gcode we'll want to add some special comments at the top.

SuperSlicer

; Settings for the post-processing script "perAxis.py" that go into the Start GCode section in SuperSlicer.
; Accelerations are specified in the X_ACCEL / Y_ACCEL / Z_ACCEL format.

; ACCEL: 8000/8000/1000      for First Layer
; ACCEL: 30000/15000/1000    for Travel
; ACCEL: 45000/16000/1000    for TYPE:External perimeter
; ACCEL: 45000/16000/1000    for TYPE:Overhang perimeter
; ACCEL: 45000/16000/1000    for TYPE:Internal perimeter
; ACCEL: 45000/16000/1000    for TYPE:Top solid infill
; ACCEL: 60000/25000/1000    for TYPE:Solid infill
; ACCEL: 60000/40000/1000    for TYPE:Internal infill
; ACCEL: 30000/20000/1000    for TYPE:Bridge infill
; ACCEL: 30000/20000/1000    for TYPE:Internal bridge infill
; ACCEL: 45000/16000/1000    for TYPE:Thin wall
; ACCEL: 45000/16000/1000    for TYPE:Gap fill
; ACCEL: 8000/8000/1000      for TYPE:Skirt
; ACCEL: 30000/30000/1000    for TYPE:Support material
; ACCEL: 30000/30000/1000    for TYPE:Support material interface

OrcaSlicer

; Settings for the post-processing script "perAxis.py" that go into the Start GCode section in SuperSlicer.
; Accelerations are specified in the X_ACCEL / Y_ACCEL / Z_ACCEL format.

; ACCEL: 8000/8000/1000      for First Layer
; ACCEL: 30000/15000/1000    for Travel
; ACCEL: 45000/16000/1000    for TYPE:Outer wall
; ACCEL: 45000/16000/1000    for TYPE:Overhang wall
; ACCEL: 45000/16000/1000    for TYPE:Inner wall
; ACCEL: 45000/16000/1000    for TYPE:Top surface
; ACCEL: 50000/25000/1000    for TYPE:Internal solid infill
; ACCEL: 50000/30000/1000    for TYPE:Sparse infill
; ACCEL: 30000/20000/1000    for TYPE:Bridge
; ACCEL: 45000/16000/1000    for TYPE:Thin wall
; ACCEL: 45000/16000/1000    for TYPE:Gap infill
; ACCEL: 8000/8000/1000      for TYPE:Skirt
; ACCEL: 30000/30000/1000    for TYPE:Support material
; ACCEL: 30000/30000/1000    for TYPE:Support material interface

THIS IS JUST AN EXAMPLE! If your machine can't handle these accels, you could damage something, so set it to something reasonable. If you are unsure for now (and you should be), set these to the same accelerations you use already, and keep the same accelerations that you used previously.

Disable acceleration control in the slicer

Yes, I'm serious. Once you are absolutely sure that you have those numbers set to match what you were using in your slicer, or lower if you want to be cautious, you want to disable acceleration control in your slicer. You can optionally leave it enabled, but you will be trying to control accelations from more than one place, and it will likely be more confusing. The post processor will take care of acceleration control, and you should trust the accel limits in your printer.cfg to save you when you mess up.

Before you try to print any gcode with this.

Make SURE that it's post processing. Just slice any random object, and open the gcode file. Use your text editor to search for SET_KINEMATICS_LIMIT in the gcode. If this is in the code in one or more places (it should be there many many times), then you have done everything on the slicer side to get going!

Onto tuning!

Tuning is almost identical to typical tuning. You'll want to do the same pressure advanced tuning, per feature acceleration tuning, and travel accel speed tuning. The only difference is now you have access to control the accelerations of each axis. My calibration macros are found here ard I've created TEST_AXIS_SPEED specifically for per axis tuning. This is a non extrusion test that will move your gantry around and let you test the limits of your machine at different speeds. I've left examples in each macro in there to tell you how to use them, but for the recommended one, you'd run it like this.

TEST_AXIS_SPEED SPEED=300 X_ACCEL=8000 Y_ACCEL=1000 ITERATIONS=10 Z=10

That will home the machine, and move it around at 300mm/s, 8k accel on X and 1k accel on Y, and make 10 full iterations of the test. Note that it's built to automatically raise the limit of the machines acceleration for all axies to the limit of X for what klipper "normally" uses. This is because X will be the fastest on all machines supported, so if using that macro, make sure your X_ACCEL is as fast, or faster than Y_ACCEL. If you decide this is wrong, you know enough how to modify the macro to your liking, but most users this will be correct.

Finalizing tuning

Once you have speeds you are happy with, you'll need to do one last thing in order to fully use these accelerations. I've left this information out until now because the included macro is the only things that has been able to break the safe limits that you had in place before, and you want to keep them in place while not printing and the printer is doing basic macros.

In your printer's PRINT_START macro, or your Slicer's Start Gcode macro (only if not using a PRINT_START macro), at the VERY END you want to add the line SET_VELOCITY_LIMIT ACCEL=1111

Replace 1111 with the maximum acceleration you will be printing at on any axis (usually the fastest X acceleration. Remember those are set in the comments described above in the start gcode section of the slicer.

In your PRINT_END macro (or End Gcode section in the slicer if you don't have a PRINT_END macro, add this line.

SET_VELOCITY_LIMIT VELOCITY={printer.configfile.settings.printer.max_velocity} ACCEL={printer.configfile.settings.printer.max_accel}

You won't ever have to change it, it simply restores your printers limits set in the config. You very likely also want to add this to any PAUSE macro right after saving the SAVE_GCODE_STATE and before any moves as well. IF you have a CANCEL_PRINT macro, you likely want to add it there as well. Any time your printer needs to be interupted from normal sliced operations, it's a good guard to have in place. It's not a requirement depending on your settings and what your machine can do, but it is safer so I've listed all of these things.

Are we done yet?

Yes, that's everything majorly different here. If you followed steps here, you should have gotten to per axis accelerations safely, and without issue. There's much more that you can play with now if you so choose, and there's information in the files provided with instructions, as well as documentation linked above. I'll be doing a follow up post with some tuning tips specific to the limited-* kinematics that may help with some issues I've run into, but they are machine specific, and I want to hear back from the community so I know what others need help with. Feel free to reach out if you are stuck, confused, or just want to talk about this change!

Special shout out to Piezoid for the initial leg work on per-axis. I made very minimal modifications to their code, mostly shutting up the console spew that was useful, and is still there with an optional command, but lagged out the web UI when changing accelerations quickly (and per feature is very quick on small prints)

Also wanted to thank VintageGriffin, wherever they may be found. I wasn't able to track them down, but their post processing script was what I used as a basis for the one here, with little modification just to make it work per axis.

Both of these wonderful people were the ones that did the heavy lifting for what

Your 3d printer is slow

The problem

Many of us in the printing space chase speed. We do this, usually, not because we want to watch a printer zoom around, but to rapid prototype, reduce the need to have more printers for volume, and many other reasons. Many of us have moved from Cartesian systems like bed flingers to CoreXY systems in the persuit of quality at speed, but unfortunately these share one specific problem. There's a weak primary axis. Thanks to this weak axis, we have to limit the strong axis. The bed moving is clearly more of a limit than the toolhead on bed flingers, and moving the entire gantry on CoreXY systems is more weight than just a toolhead.

The solution?

So this should be a simple problem to solve one would think. Stop treating the axis the same! You would be, mostly, right. A wonderful member of the community that goes by Piezoid has actually done the initial leg work in solving this for Cartesian (aka, bed flinger) as well as CoreXY machines. I have run these kinematics for months now and have been finding some luck with them, but found similar quirks in the system that others have also found, and can see why some users chose to abandon it as promising as it is.

More problems?!

Here's a list of my complaints with the solution above.

  • No fine grain control of per axis accel limits per feature. There is basic scaling of acceleration, but that doesn't give us the control we have with modern slicers to control per feature acceleration, which can lead to slower print times.

  • Because our acceleration is changing constantly, tuning pressure advance is harder at best, and impossible at worst.

How to overcome this!

Per axis acceleration has allowed my machines to run at their limits in terms of not ringing, per axis when setting the limits in the firmware near the input shaper limits, so I wanted to press on and solve these problems. As for losing speed, I'm proposing the ability to go back to per feature accelerations. This would allow us to push well above input shaper limits on things like infill or other not visible features just like we have without per axis limits, but with the gain of being able to push each axis to it's own limit. In terms of pressure advance tuning problems, because we have per feature control, you will have the power to bring the axis accelerations back to the same, or more similar, for features that exibit problems with pressure advance. This sounds like it gives up speed, it doesn't. If you already needed that feature on the same acceleration on both axies, you got a sidegrade for that feature, but things like infill and travel, you will be able to unlock the full potential of the machine.

So how do I get that?

While writing this, my printer is running with all of these changes that were proposed. I'm cleaning up the code, documenting it, and trying to make it as accessable as possible for anyone that wants to extract more from their machines in terms of performance. I hope to post this quite soon, and if you have any questions, concerns, thoughts, or ideas, feel free to reach out and I'm more than willing to try things out or help you out!

Gotta go... smol?

Time for a teeny tiny printer!

While I absolutely love my Vz330, I have learned some lessons about what it's good and bad at. It's absolutely not a bad printer, and is by no means going to be surplanted any time soon. Sometimes I do feel the need to have a backup printer for when it's down, especially if I need a part that's not already printed. With repraps, you always want a spare printer. In comes the Voron V0! It's got a laughably small build volume, but it's enough to print basically any required part on any 3d printer I own, have owned, or will own. That makes it quite the useful little printer even if some parts I want to print don't fit on it. Enough rambling in an intro, let's see how it went!

The build

build1 build2 build3 build4 build5 build6 build7

Day 1: Build summary

This printer is... much harder to build then a Vzbot. Everything needs to be put together in order, and if you get it wrong, you have to unbuild so much to get to the part that you need to rebuild. I can't stress how much this is tightly packed. No space in here is wasted. Packing all of the wires in there is an absolute nightmare. Even with the cable channel, it just feels like it won't fit. Managed to get it running at least, and that can be worked on.

Day 2: Onto the tuning!

Onto day 2, and less then 24 hours after building a V0.1, the V0.2 launched. Guess it's time to tune for speed to update some things. The life of a reprap, always printing it's own updates. Gotta get some panels on this thing as well, so printing those on itself as well.

Some changes

ugly_hat enclosed teeny

Clean prints

clean_print clean_print2 clean_print3

Last Changes

Over a bit of owning it, the V0.2 tophat parts showed up, and some shenanigans happened. I ended up popping some mosfets due to some absolutely stupid things I won't bother explaining. Cheap boards do cheap board things. I ended up also strapping on a thermistor board I built as well to get an additional temp sensor for the enclosure. It's not pretty, but it's functional. The LRS-150 power supply also had some horrid coil whine while idle, so it ended up getting chucked out and replaced with the prusa mini power supply. Same voltage/wattage, worked out. Toolhead also got replaced as cooling is an issue when you can't really delay layer switching by printing bigger, so it was replaced by a 5015 based head. Less noise, more cooling. Overall extrusion speed ended up increased despite the lower melt rate cap as you don't have to slow down to wait for cooling.

dead_stuff final

Final thoughts

This is a very niche printer. I can't print keyboard cases on it being it's only 120mm^3 build volume, but it can print a ton of my random prints that I honestly do the most often. Despite Vzbot being faster once it's fired up, it takes 10-15 minutes to warm up, and this takes less then 5 from print sent to starting printing. It's also massively cheaper to maintain with it's tiny bed and small belts and rails. While I don't think this printer is a good only printer for most people, it's an absolutely amazing printer to own as a secondary or tertiary printer. Quite limited, yet has some huge upsides. I'll either keep it basically untouched as a reliable back to back printer, or toy with new ideas on it before messing with the much more expensive Vzbot. Only time will tell.