Merge pull request #165151 from vtuan10/klipper-firmware

nixos/klipper: Add Klipper firmware options

Changed files
+215 -22
nixos
modules
services
pkgs
+66 -22
nixos/modules/services/misc/klipper.nix
···
format = pkgs.formats.ini {
# https://github.com/NixOS/nixpkgs/pull/121613#issuecomment-885241996
listToValue = l:
-
if builtins.length l == 1 then generators.mkValueStringDefault {} (head l)
else lib.concatMapStrings (s: "\n ${generators.mkValueStringDefault {} s}") l;
-
mkKeyValue = generators.mkKeyValueDefault {} ":";
};
in
{
···
for supported values.
'';
};
};
};
···
assertion = cfg.user != null -> cfg.group != null;
message = "Option klipper.group is not set when a user is specified.";
}
];
environment.etc."klipper.cfg".source = format.generate "klipper.cfg" cfg.settings;
···
group = config.services.octoprint.group;
};
-
systemd.services.klipper = let
-
klippyArgs = "--input-tty=${cfg.inputTTY}"
-
+ optionalString (cfg.apiSocket != null) " --api-server=${cfg.apiSocket}";
-
in {
-
description = "Klipper 3D Printer Firmware";
-
wantedBy = [ "multi-user.target" ];
-
after = [ "network.target" ];
-
serviceConfig = {
-
ExecStart = "${cfg.package}/lib/klipper/klippy.py ${klippyArgs} /etc/klipper.cfg";
-
RuntimeDirectory = "klipper";
-
SupplementaryGroups = [ "dialout" ];
-
WorkingDirectory = "${cfg.package}/lib";
-
} // (if cfg.user != null then {
-
Group = cfg.group;
-
User = cfg.user;
-
} else {
-
DynamicUser = true;
-
User = "klipper";
-
});
-
};
};
}
···
format = pkgs.formats.ini {
# https://github.com/NixOS/nixpkgs/pull/121613#issuecomment-885241996
listToValue = l:
+
if builtins.length l == 1 then generators.mkValueStringDefault { } (head l)
else lib.concatMapStrings (s: "\n ${generators.mkValueStringDefault {} s}") l;
+
mkKeyValue = generators.mkKeyValueDefault { } ":";
};
in
{
···
for supported values.
'';
};
+
+
firmwares = mkOption {
+
description = "Firmwares klipper should manage";
+
default = { };
+
type = with types; attrsOf
+
(submodule {
+
options = {
+
enable = mkEnableOption ''
+
building of firmware and addition of klipper-flash tools for manual flashing.
+
This will add `klipper-flash-$mcu` scripts to your environment which can be called to flash the firmware.
+
'';
+
configFile = mkOption {
+
type = path;
+
description = "Path to firmware config which is generated using `klipper-genconf`";
+
};
+
};
+
});
+
};
};
};
···
assertion = cfg.user != null -> cfg.group != null;
message = "Option klipper.group is not set when a user is specified.";
}
+
{
+
assertion = foldl (a: b: a && b) true (mapAttrsToList (mcu: _: mcu != null -> (hasAttrByPath [ "${mcu}" "serial" ] cfg.settings)) cfg.firmwares);
+
message = "Option klipper.settings.$mcu.serial must be set when klipper.firmware.$mcu is specified";
+
}
];
environment.etc."klipper.cfg".source = format.generate "klipper.cfg" cfg.settings;
···
group = config.services.octoprint.group;
};
+
systemd.services.klipper =
+
let
+
klippyArgs = "--input-tty=${cfg.inputTTY}"
+
+ optionalString (cfg.apiSocket != null) " --api-server=${cfg.apiSocket}";
+
in
+
{
+
description = "Klipper 3D Printer Firmware";
+
wantedBy = [ "multi-user.target" ];
+
after = [ "network.target" ];
+
serviceConfig = {
+
ExecStart = "${cfg.package}/lib/klipper/klippy.py ${klippyArgs} /etc/klipper.cfg";
+
RuntimeDirectory = "klipper";
+
SupplementaryGroups = [ "dialout" ];
+
WorkingDirectory = "${cfg.package}/lib";
+
} // (if cfg.user != null then {
+
Group = cfg.group;
+
User = cfg.user;
+
} else {
+
DynamicUser = true;
+
User = "klipper";
+
});
+
};
+
+
environment.systemPackages =
+
with pkgs;
+
let
+
firmwares = filterAttrs (n: v: v!= null) (mapAttrs
+
(mcu: { enable, configFile }: if enable then pkgs.klipper-firmware.override {
+
mcu = lib.strings.sanitizeDerivationName mcu;
+
firmwareConfig = configFile;
+
} else null)
+
cfg.firmwares);
+
firmwareFlasher = mapAttrsToList
+
(mcu: firmware: pkgs.klipper-flash.override {
+
mcu = lib.strings.sanitizeDerivationName mcu;
+
klipper-firmware = firmware;
+
flashDevice = cfg.settings."${mcu}".serial;
+
firmwareConfig = cfg.firmwares."${mcu}".configFile;
+
})
+
firmwares;
+
in
+
[ klipper-genconf ] ++ firmwareFlasher ++ attrValues firmwares;
};
}
+61
pkgs/servers/klipper/klipper-firmware.nix
···
···
+
{ stdenv
+
, lib
+
, pkg-config
+
, pkgsCross
+
, bintools-unwrapped
+
, libffi
+
, libusb1
+
, wxGTK
+
, python2
+
, python3
+
, gcc-arm-embedded
+
, klipper
+
, avrdude
+
, stm32flash
+
, mcu ? "mcu"
+
, firmwareConfig ? ./simulator.cfg
+
}: stdenv.mkDerivation rec {
+
name = "klipper-firmware-${mcu}-${version}";
+
version = klipper.version;
+
src = klipper.src;
+
+
nativeBuildInputs = [
+
python2
+
python3
+
pkgsCross.avr.stdenv.cc
+
gcc-arm-embedded
+
bintools-unwrapped
+
libffi
+
libusb1
+
avrdude
+
stm32flash
+
pkg-config
+
wxGTK # Required for bossac
+
];
+
+
preBuild = "cp ${firmwareConfig} ./.config";
+
+
postPatch = ''
+
patchShebangs .
+
'';
+
+
makeFlags = [
+
"V=1"
+
"KCONFIG_CONFIG=${firmwareConfig}"
+
];
+
+
installPhase = ''
+
mkdir -p $out
+
cp ./.config $out/config
+
cp -r out/* $out
+
'';
+
+
dontFixup = true;
+
+
meta = with lib; {
+
inherit (klipper.meta) homepage license;
+
description = "Firmware part of Klipper";
+
maintainers = with maintainers; [ vtuan10 ];
+
platforms = platforms.linux;
+
};
+
}
+38
pkgs/servers/klipper/klipper-flash.nix
···
···
+
{ lib
+
, writeShellApplication
+
, gnumake
+
, pkgsCross
+
, klipper
+
, klipper-firmware
+
, python2
+
, avrdude
+
, stm32flash
+
, mcu ? "mcu"
+
, flashDevice ? "/dev/null"
+
, firmwareConfig ? ./simulator.cfg
+
}:
+
let
+
supportedArches = [ "avr" "stm32" "lpc176x" ];
+
matchBoard = with builtins; match ''^.*CONFIG_BOARD_DIRECTORY="([a-zA-Z0-9_]+)".*$'' (readFile firmwareConfig);
+
boardArch = if matchBoard == null then null else builtins.head matchBoard;
+
in
+
writeShellApplication {
+
name = "klipper-flash-${mcu}";
+
runtimeInputs = [
+
python2
+
pkgsCross.avr.stdenv.cc
+
gnumake
+
] ++ lib.optionals (boardArch == "avr") [ avrdude ] ++ lib.optionals (boardArch == "stm32") [ stm32flash ];
+
text = ''
+
if ${lib.boolToString (!builtins.elem boardArch supportedArches)}; then
+
printf "Flashing Klipper firmware to your board is not supported yet.\n"
+
printf "Please use the compiled firmware at ${klipper-firmware} and flash it using the tools provided for your microcontroller."
+
exit 1
+
fi
+
if ${lib.boolToString (boardArch == "stm32")}; then
+
make -C ${klipper.src} FLASH_DEVICE="${toString flashDevice}" OUT="${klipper-firmware}/" KCONFIG_CONFIG="${klipper-firmware}/config" serialflash
+
else
+
make -C ${klipper.src} FLASH_DEVICE="${toString flashDevice}" OUT="${klipper-firmware}/" KCONFIG_CONFIG="${klipper-firmware}/config" flash
+
fi
+
'';
+
}
+21
pkgs/servers/klipper/klipper-genconf.nix
···
···
+
{ writeShellApplication
+
, klipper
+
, python2
+
, gnumake
+
, pkgsCross
+
}: writeShellApplication {
+
name = "klipper-genconf";
+
runtimeInputs = [
+
python2
+
pkgsCross.avr.stdenv.cc
+
gnumake
+
];
+
text = ''
+
CURRENT_DIR=$(pwd)
+
TMP=$(mktemp -d)
+
make -C ${klipper.src} OUT="$TMP" KCONFIG_CONFIG="$CURRENT_DIR/config" menuconfig
+
rm -rf "$TMP" config.old
+
printf "\nYour firmware configuration for klipper:\n\n"
+
cat config
+
'';
+
}
+23
pkgs/servers/klipper/simulator.cfg
···
···
+
# CONFIG_LOW_LEVEL_OPTIONS is not set
+
# CONFIG_MACH_AVR is not set
+
# CONFIG_MACH_ATSAM is not set
+
# CONFIG_MACH_ATSAMD is not set
+
# CONFIG_MACH_LPC176X is not set
+
# CONFIG_MACH_STM32 is not set
+
# CONFIG_MACH_RP2040 is not set
+
# CONFIG_MACH_PRU is not set
+
# CONFIG_MACH_LINUX is not set
+
CONFIG_MACH_SIMU=y
+
CONFIG_BOARD_DIRECTORY="simulator"
+
CONFIG_CLOCK_FREQ=20000000
+
CONFIG_SERIAL=y
+
CONFIG_SIMULATOR_SELECT=y
+
CONFIG_SERIAL_BAUD=250000
+
CONFIG_USB_VENDOR_ID=0x1d50
+
CONFIG_USB_DEVICE_ID=0x614e
+
CONFIG_USB_SERIAL_NUMBER="12345"
+
CONFIG_HAVE_GPIO=y
+
CONFIG_HAVE_GPIO_ADC=y
+
CONFIG_HAVE_GPIO_SPI=y
+
CONFIG_HAVE_GPIO_HARD_PWM=y
+
CONFIG_INLINE_STEPPER_HACK=y
+6
pkgs/top-level/all-packages.nix
···
klipper = callPackage ../servers/klipper { };
klog = qt5.callPackage ../applications/radio/klog { };
krapslog = callPackage ../tools/misc/krapslog { };
···
klipper = callPackage ../servers/klipper { };
+
klipper-firmware = callPackage ../servers/klipper/klipper-firmware.nix { };
+
+
klipper-flash = callPackage ../servers/klipper/klipper-flash.nix { };
+
+
klipper-genconf = callPackage ../servers/klipper/klipper-genconf.nix { };
+
klog = qt5.callPackage ../applications/radio/klog { };
krapslog = callPackage ../tools/misc/krapslog { };