summaryrefslogtreecommitdiff
path: root/hosts/gnuslashprinter
diff options
context:
space:
mode:
Diffstat (limited to 'hosts/gnuslashprinter')
-rw-r--r--hosts/gnuslashprinter/configuration.nix29
-rw-r--r--hosts/gnuslashprinter/flask-relay-ctl.nix37
-rw-r--r--hosts/gnuslashprinter/hardware-configuration.nix44
-rw-r--r--hosts/gnuslashprinter/klipper.nix100
-rw-r--r--hosts/gnuslashprinter/resources/flaskrelayctl/pyproject.toml10
-rw-r--r--hosts/gnuslashprinter/resources/flaskrelayctl/server/__init__.py54
-rw-r--r--hosts/gnuslashprinter/resources/klipper/mcu/config109
-rw-r--r--hosts/gnuslashprinter/resources/klipper/printer.cfg180
8 files changed, 563 insertions, 0 deletions
diff --git a/hosts/gnuslashprinter/configuration.nix b/hosts/gnuslashprinter/configuration.nix
new file mode 100644
index 0000000..9e1f734
--- /dev/null
+++ b/hosts/gnuslashprinter/configuration.nix
@@ -0,0 +1,29 @@
+{
+ imports = [
+ ./flask-relay-ctl.nix
+ ./klipper.nix
+ ];
+
+ boot._loader.enable = true;
+
+ _archetypes = {
+ profiles = {
+ headless = {
+ enable = true;
+ home.users.timmy.enable = true;
+ };
+ btrfs.enable = true;
+ };
+ };
+
+ # Enable user timmy
+ _users.timmy.enable = true;
+
+ # Name devices
+ services.udev.extraRules = ''
+ SUBSYSTEM=="tty", KERNELS=="3-9", SYMLINK+="gsp-power"
+ SUBSYSTEM=="tty", KERNELS=="3-10", SYMLINK+="gsp-control"
+ '';
+
+ system.stateVersion = "25.11";
+}
diff --git a/hosts/gnuslashprinter/flask-relay-ctl.nix b/hosts/gnuslashprinter/flask-relay-ctl.nix
new file mode 100644
index 0000000..27507b6
--- /dev/null
+++ b/hosts/gnuslashprinter/flask-relay-ctl.nix
@@ -0,0 +1,37 @@
+{ pkgs, ... }: let
+ flaskrelayctl = pkgs.python3Packages.buildPythonApplication {
+ pname = "flaskrelayctl";
+ version = "1.0";
+ src = ./resources/flaskrelayctl;
+ pyproject = true;
+ build-system = with pkgs.python3Packages; [ setuptools ];
+ dependencies = with pkgs.python3Packages; [
+ flask
+ pyserial
+ ];
+ };
+in {
+ users.users.relay-api = {
+ isSystemUser = true;
+ group = "relay-api";
+ extraGroups = [ "dialout" ];
+ };
+ users.groups.relay-api = {};
+
+ systemd.services.relay-api = let
+ RELAYCTL_DEV = "/dev/gsp-power";
+ in {
+ description = "USB Relay Flask API";
+ wantedBy = [ "multi-user.target" ];
+ after = [ "network.target" ];
+ serviceConfig = {
+ User = "relay-api";
+ Group = "relay-api";
+ ExecStart = "${flaskrelayctl}/bin/flaskrelayctl";
+ Restart = "always";
+ DeviceAllow = [ "${RELAYCTL_DEV} rw" ];
+ };
+ };
+
+ environment.systemPackages = [ flaskrelayctl ];
+}
diff --git a/hosts/gnuslashprinter/hardware-configuration.nix b/hosts/gnuslashprinter/hardware-configuration.nix
new file mode 100644
index 0000000..278d6e6
--- /dev/null
+++ b/hosts/gnuslashprinter/hardware-configuration.nix
@@ -0,0 +1,44 @@
+# Do not modify this file! It was generated by ‘nixos-generate-config’
+# and may be overwritten by future invocations. Please make changes
+# to /etc/nixos/configuration.nix instead.
+{ config, lib, pkgs, modulesPath, ... }:
+
+{
+ imports =
+ [ (modulesPath + "/installer/scan/not-detected.nix")
+ ];
+
+ boot.initrd.availableKernelModules = [ "xhci_pci" "ehci_pci" "ahci" "usbhid" "usb_storage" "sd_mod" ];
+ boot.initrd.kernelModules = [ ];
+ boot.kernelModules = [ ];
+ boot.extraModulePackages = [ ];
+
+ fileSystems."/" =
+ { device = "/dev/disk/by-uuid/416a481f-d656-4bd1-acdb-bd0a6f2d2c3a";
+ fsType = "btrfs";
+ options = [ "subvol=@" ];
+ };
+
+ fileSystems."/home" =
+ { device = "/dev/disk/by-uuid/416a481f-d656-4bd1-acdb-bd0a6f2d2c3a";
+ fsType = "btrfs";
+ options = [ "subvol=@home" ];
+ };
+
+ fileSystems."/nix" =
+ { device = "/dev/disk/by-uuid/416a481f-d656-4bd1-acdb-bd0a6f2d2c3a";
+ fsType = "btrfs";
+ options = [ "subvol=@nix" ];
+ };
+
+ fileSystems."/boot" =
+ { device = "/dev/disk/by-uuid/18C4-6D53";
+ fsType = "vfat";
+ options = [ "fmask=0022" "dmask=0022" ];
+ };
+
+ swapDevices = [ ];
+
+ nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux";
+ hardware.cpu.intel.updateMicrocode = lib.mkDefault config.hardware.enableRedistributableFirmware;
+}
diff --git a/hosts/gnuslashprinter/klipper.nix b/hosts/gnuslashprinter/klipper.nix
new file mode 100644
index 0000000..331a0c7
--- /dev/null
+++ b/hosts/gnuslashprinter/klipper.nix
@@ -0,0 +1,100 @@
+{
+ # Klipper firmware
+ services.klipper = {
+ enable = true;
+ firmwares = {
+ mcu = {
+ enable = true;
+ # Serial port connected to the microcontroller
+ serial = "/dev/gsp-control";
+ # Klipper flash must be enabled in order to build mcu firmware
+ # The resulting `klipper-flash-mcu` command will show the location of the firmware bin in the nix store
+ enableKlipperFlash = true;
+ # Run klipper-genconf to generate this
+ configFile = ./resources/klipper/mcu/config;
+ };
+ };
+ configFile = ./resources/klipper/printer.cfg;
+ logFile = "/var/lib/klipper/klipper.log";
+ };
+ # Mutable config
+ services.klipper.mutableConfig = true;
+ #configDir = "/var/lib/moonraker/config"; # Accessible by moonraker # TODO
+
+ # Moonraker web-api
+ security.polkit.enable = true; # required for services.moonraker.allowSystemControl
+ services.moonraker = {
+ user = "root";
+ enable = true;
+ address = "0.0.0.0";
+ allowSystemControl = true;
+ settings = {
+ authorization = {
+ force_logins = true;
+ cors_domains = [
+ "*.local"
+ "*.lan"
+ "*://app.fluidd.xyz"
+ "*://my.mainsail.xyz"
+ ];
+ trusted_clients = [
+ "10.0.0.0/8"
+ "127.0.0.0/8"
+ "169.254.0.0/16"
+ "172.16.0.0/12"
+ "192.168.0.0/16"
+ "FE80::/10"
+ "::1/128"
+ ];
+ };
+ #file_manager.check_klipper_config_path = false; # Disable warning when klipper config is not accessible by moonraker
+ # mainsail.cfg
+ #"update_manager mainsail-config" = {
+ # type = "git_repo";
+ # primary_branch = "master";
+ # path = "~/mainsail-config";
+ # origin = "https://github.com/mainsail-crew/mainsail-config.git";
+ # managed_services = "klipper";
+ #};
+
+ "power printer" = let
+ relayApiHost = "http://localhost:5050";
+ in {
+ type = "http";
+ on_url = "${relayApiHost}/on";
+ off_url = "${relayApiHost}/off";
+ status_url = "${relayApiHost}/status";
+ response_template = ''
+
+ # The module will perform the "GET" request using the appropriate url.
+ # We use the `last_response` method to fetch the result and decode the
+ # json response.
+ {% set resp = http_request.last_response().json() %}
+ # The expression below will render "on" or "off".
+ {resp["state"].lower()}
+ '';
+ off_when_shutdown = true;
+ on_when_job_queued = true;
+ locked_while_printing = true;
+ restart_klipper_when_powered = true;
+ restart_delay = "1";
+ bound_services = "klipper";
+ initial_state = "off";
+ };
+ };
+ };
+
+ # Mainsail web-interface
+ services.mainsail = {
+ enable = true;
+ hostName = "0.0.0.0";
+ nginx.listenAddresses = [ "0.0.0.0" ];
+ };
+ services.nginx.clientMaxBodySize = "1000m"; # Allow large gcodes, etc.
+ networking.firewall.allowedTCPPorts = [ 80 ]; # Port for mainsail via nginx
+
+ # Webcam support in mainsail
+ # TODO hook to restart ustreamer when webcam is connected
+ services.ustreamer.enable = true;
+ services.mainsail.nginx.locations."/webcam/".proxyPass = "http://localhost:8080/stream"; # Default location for ustreamer stream
+}
diff --git a/hosts/gnuslashprinter/resources/flaskrelayctl/pyproject.toml b/hosts/gnuslashprinter/resources/flaskrelayctl/pyproject.toml
new file mode 100644
index 0000000..b151b74
--- /dev/null
+++ b/hosts/gnuslashprinter/resources/flaskrelayctl/pyproject.toml
@@ -0,0 +1,10 @@
+[build-system]
+requires = ["setuptools"]
+build-backend = "setuptools.build_meta"
+
+[project]
+name = "flaskrelayctl"
+version = "1.0"
+
+[project.scripts]
+flaskrelayctl = "server:main"
diff --git a/hosts/gnuslashprinter/resources/flaskrelayctl/server/__init__.py b/hosts/gnuslashprinter/resources/flaskrelayctl/server/__init__.py
new file mode 100644
index 0000000..f2c0bc3
--- /dev/null
+++ b/hosts/gnuslashprinter/resources/flaskrelayctl/server/__init__.py
@@ -0,0 +1,54 @@
+from flask import Flask, jsonify
+import serial
+import os
+
+app = Flask(__name__)
+
+RELAY_DEV = os.environ.get("RELAYCTL_DEV", "/dev/ttyUSB0")
+RELAY_HOST = os.environ.get("RELAYCTL_HOST", "127.0.0.1")
+RELAY_PORT = os.environ.get("RELAYCTL_PORT", "5050")
+RELAY_BAUD = 9600
+RELAY_ID = 1 # NOTE: multi relay boards are not supported (only relay id 1)
+
+def get_cmd(state: bool):
+ """
+ LCUS relay 4-byte control command syntax is as follows:
+ start: always 0xA0
+ relay: id of relay (counts from 1)
+ state: coil energized 0 or 1
+ checksum: sum of previous 3 bytes
+ """
+ start = 0xA0
+ relay = RELAY_ID
+ state = int(state)
+ checksum = start + relay + state
+ return bytes((start, relay, state, checksum))
+
+def get_serial():
+ return serial.Serial(RELAY_DEV, RELAY_BAUD, timeout=1)
+
+@app.route("/on", methods=["GET", "POST"])
+def on():
+ with get_serial() as ser:
+ ser.write(get_cmd(True))
+ return jsonify({"state": "ON"})
+
+@app.route("/off", methods=["GET", "POST"])
+def off():
+ with get_serial() as ser:
+ ser.write(get_cmd(False))
+ return jsonify({"state": "OFF"})
+
+@app.route("/status", methods=["GET"])
+def status():
+ with get_serial() as ser:
+ ser.write(bytes((0xFF,))) # write 0xFF to get status
+ r = ser.readline()
+ state = r.decode("utf-8").removeprefix("CH1: ").strip()
+ return jsonify({"state": state})
+
+def main():
+ app.run(host=RELAY_HOST, port=int(RELAY_PORT))
+
+if __name__ == "__main__":
+ main()
diff --git a/hosts/gnuslashprinter/resources/klipper/mcu/config b/hosts/gnuslashprinter/resources/klipper/mcu/config
new file mode 100644
index 0000000..3e192a3
--- /dev/null
+++ b/hosts/gnuslashprinter/resources/klipper/mcu/config
@@ -0,0 +1,109 @@
+# 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=y
+# CONFIG_MACH_HC32F460 is not set
+# CONFIG_MACH_RPXXXX is not set
+# CONFIG_MACH_PRU is not set
+# CONFIG_MACH_AR100 is not set
+# CONFIG_MACH_LINUX is not set
+# CONFIG_MACH_SIMU is not set
+CONFIG_BOARD_DIRECTORY="stm32"
+CONFIG_MCU="stm32f103xe"
+CONFIG_CLOCK_FREQ=72000000
+CONFIG_SERIAL=y
+CONFIG_FLASH_SIZE=0x10000
+CONFIG_FLASH_BOOT_ADDRESS=0x8000000
+CONFIG_RAM_START=0x20000000
+CONFIG_RAM_SIZE=0x5000
+CONFIG_STACK_SIZE=512
+CONFIG_FLASH_APPLICATION_ADDRESS=0x8007000
+CONFIG_STM32_SELECT=y
+CONFIG_MACH_STM32F103=y
+# CONFIG_MACH_STM32F207 is not set
+# CONFIG_MACH_STM32F401 is not set
+# CONFIG_MACH_STM32F405 is not set
+# CONFIG_MACH_STM32F407 is not set
+# CONFIG_MACH_STM32F429 is not set
+# CONFIG_MACH_STM32F446 is not set
+# CONFIG_MACH_STM32F765 is not set
+# CONFIG_MACH_STM32F031 is not set
+# CONFIG_MACH_STM32F042 is not set
+# CONFIG_MACH_STM32F070 is not set
+# CONFIG_MACH_STM32F072 is not set
+# CONFIG_MACH_STM32G070 is not set
+# CONFIG_MACH_STM32G071 is not set
+# CONFIG_MACH_STM32G0B0 is not set
+# CONFIG_MACH_STM32G0B1 is not set
+# CONFIG_MACH_STM32G431 is not set
+# CONFIG_MACH_STM32G474 is not set
+# CONFIG_MACH_STM32H723 is not set
+# CONFIG_MACH_STM32H743 is not set
+# CONFIG_MACH_STM32H750 is not set
+# CONFIG_MACH_STM32L412 is not set
+# CONFIG_MACH_N32G452 is not set
+# CONFIG_MACH_N32G455 is not set
+CONFIG_MACH_STM32F1=y
+CONFIG_HAVE_STM32_USBFS=y
+CONFIG_STM32_USB_DOUBLE_BUFFER_TX=y
+CONFIG_HAVE_STM32_CANBUS=y
+CONFIG_STM32_DFU_ROM_ADDRESS=0
+# CONFIG_STM32_FLASH_START_2000 is not set
+# CONFIG_STM32_FLASH_START_5000 is not set
+CONFIG_STM32_FLASH_START_7000=y
+# CONFIG_STM32_FLASH_START_8000 is not set
+# CONFIG_STM32_FLASH_START_8800 is not set
+# CONFIG_STM32_FLASH_START_9000 is not set
+# CONFIG_STM32_FLASH_START_10000 is not set
+# CONFIG_STM32_FLASH_START_800 is not set
+# CONFIG_STM32_FLASH_START_1000 is not set
+# CONFIG_STM32_FLASH_START_4000 is not set
+# CONFIG_STM32_FLASH_START_0000 is not set
+CONFIG_CLOCK_REF_FREQ=8000000
+CONFIG_STM32F0_TRIM=16
+# CONFIG_STM32_USB_PA11_PA12 is not set
+CONFIG_STM32_SERIAL_USART1=y
+# CONFIG_STM32_CANBUS_PA11_PA12 is not set
+# CONFIG_STM32_CANBUS_PA11_PB9 is not set
+CONFIG_SERIAL_BAUD=250000
+CONFIG_USB_VENDOR_ID=0x1d50
+CONFIG_USB_DEVICE_ID=0x614e
+CONFIG_USB_SERIAL_NUMBER="12345"
+CONFIG_WANT_ADC=y
+CONFIG_WANT_SPI=y
+CONFIG_WANT_SOFTWARE_SPI=y
+CONFIG_WANT_I2C=y
+CONFIG_WANT_SOFTWARE_I2C=y
+CONFIG_WANT_HARD_PWM=y
+CONFIG_WANT_BUTTONS=y
+CONFIG_WANT_TMCUART=y
+CONFIG_WANT_NEOPIXEL=y
+CONFIG_WANT_PULSE_COUNTER=y
+CONFIG_WANT_ST7920=y
+CONFIG_WANT_HD44780=y
+CONFIG_WANT_ADXL345=y
+CONFIG_WANT_LIS2DW=y
+CONFIG_WANT_MPU9250=y
+CONFIG_WANT_ICM20948=y
+CONFIG_WANT_THERMOCOUPLE=y
+CONFIG_WANT_HX71X=y
+CONFIG_WANT_ADS1220=y
+CONFIG_WANT_LDC1612=y
+CONFIG_WANT_SENSOR_ANGLE=y
+CONFIG_NEED_SENSOR_BULK=y
+CONFIG_WANT_LOAD_CELL_PROBE=y
+CONFIG_NEED_SOS_FILTER=y
+CONFIG_CANBUS_FREQUENCY=1000000
+CONFIG_INLINE_STEPPER_HACK=y
+CONFIG_HAVE_STEPPER_OPTIMIZED_BOTH_EDGE=y
+CONFIG_WANT_STEPPER_OPTIMIZED_BOTH_EDGE=y
+CONFIG_HAVE_GPIO=y
+CONFIG_HAVE_GPIO_ADC=y
+CONFIG_HAVE_GPIO_SPI=y
+CONFIG_HAVE_GPIO_I2C=y
+CONFIG_HAVE_GPIO_HARD_PWM=y
+CONFIG_HAVE_STRICT_TIMING=y
+CONFIG_HAVE_CHIPID=y
+CONFIG_HAVE_BOOTLOADER_REQUEST=y
diff --git a/hosts/gnuslashprinter/resources/klipper/printer.cfg b/hosts/gnuslashprinter/resources/klipper/printer.cfg
new file mode 100644
index 0000000..4baba56
--- /dev/null
+++ b/hosts/gnuslashprinter/resources/klipper/printer.cfg
@@ -0,0 +1,180 @@
+# This file contains pin mappings for the stock 2020 Creality Ender 3
+# V2. To use this config, during "make menuconfig" select the
+# STM32F103 with a "28KiB bootloader" and serial (on USART1 PA10/PA9)
+# communication.
+
+# If you prefer a direct serial connection, in "make menuconfig"
+# select "Enable extra low-level configuration options" and select
+# serial (on USART3 PB11/PB10), which is broken out on the 10 pin IDC
+# cable used for the LCD module as follows:
+# 3: Tx, 4: Rx, 9: GND, 10: VCC
+
+# Flash this firmware by copying "out/klipper.bin" to a SD card and
+# turning on the printer with the card inserted. The firmware
+# filename must end in ".bin" and must not match the last filename
+# that was flashed.
+
+# See docs/Config_Reference.md for a description of parameters.
+
+[include mainsail.cfg]
+
+# Default V2 config
+[stepper_x]
+step_pin: PC2
+dir_pin: PB9
+enable_pin: !PC3
+microsteps: 16
+rotation_distance: 40
+endstop_pin: ^PA5
+position_endstop: -20
+position_min: -20
+position_max: 235
+homing_speed: 50
+
+[stepper_y]
+step_pin: PB8
+dir_pin: PB7
+enable_pin: !PC3
+microsteps: 16
+rotation_distance: 40
+endstop_pin: ^PA6
+position_endstop: -8
+position_min: -8
+position_max: 235
+homing_speed: 50
+
+[stepper_z]
+step_pin: PB6
+dir_pin: !PB5
+enable_pin: !PC3
+microsteps: 16
+rotation_distance: 8
+endstop_pin: probe:z_virtual_endstop
+#position_endstop: 0.0
+position_min: -5
+position_max: 250
+
+[extruder]
+max_extrude_only_distance: 100.0
+step_pin: PB4
+dir_pin: PB3
+enable_pin: !PC3
+microsteps: 16
+rotation_distance: 34.406
+nozzle_diameter: 0.400
+filament_diameter: 1.750
+heater_pin: PA1
+sensor_type: EPCOS 100K B57560G104F
+sensor_pin: PC5
+control: pid
+# tuned for stock hardware with 200 degree Celsius target
+pid_Kp: 21.527
+pid_Ki: 1.063
+pid_Kd: 108.982
+min_temp: 0
+max_temp: 250
+
+[heater_bed]
+heater_pin: PA2
+sensor_type: EPCOS 100K B57560G104F
+sensor_pin: PC4
+control: pid
+# tuned for stock hardware with 50 degree Celsius target
+pid_Kp: 54.027
+pid_Ki: 0.770
+pid_Kd: 948.182
+min_temp: 0
+max_temp: 130
+
+[fan]
+pin: PA0
+
+[mcu]
+serial: /dev/gsp-control
+restart_method: command
+
+[printer]
+kinematics: cartesian
+max_velocity: 300
+max_accel: 3000
+max_z_velocity: 5
+max_z_accel: 100
+
+######################################################################
+# 128x64 Full Graphic Creality CR10 / ENDER 3 stockdisplay
+######################################################################
+
+# This section is used for a Creality "12864" display with a single
+# ribbon cable between the display's EXP3 plug and the
+# micro-controller board's EXP1 connector.
+
+[display]
+lcd_type: st7920
+cs_pin: EXP1_7
+sclk_pin: EXP1_6
+sid_pin: EXP1_8
+encoder_pins: ^EXP1_5, ^EXP1_3
+click_pin: ^!EXP1_2
+
+[output_pin beeper]
+pin: EXP1_1
+
+[board_pins]
+aliases:
+ EXP1_1=PC6,EXP1_3=PB10,EXP1_5=PB14,EXP1_7=PB12,EXP1_9=<GND>,
+ EXP1_2=PB2,EXP1_4=PB11,EXP1_6=PB13,EXP1_8=PB15,EXP1_10=<5V>,
+ PROBE_IN=PB1,PROBE_OUT=PB0,FIL_RUNOUT=PA6
+
+[display_status]
+
+######
+# Bed leveling
+
+[bltouch]
+sensor_pin: ^PROBE_IN
+control_pin: PROBE_OUT
+x_offset: -40
+y_offset: -12
+#z_offset: 1.1
+probe_with_touch_mode: true
+stow_on_each_sample: false
+
+[bed_mesh]
+speed: 120
+mesh_min: 10, 10
+mesh_max: 195, 220
+probe_count: 5,5
+algorithm: bicubic
+
+[safe_z_home]
+home_xy_position: 157.5, 129.5
+speed: 75
+z_hop: 10
+z_hop_speed: 5
+move_to_previous: true
+
+
+#*# <---------------------- SAVE_CONFIG ---------------------->
+#*# DO NOT EDIT THIS BLOCK OR BELOW. The contents are auto-generated.
+#*#
+#*# [bed_mesh default]
+#*# version = 1
+#*# points =
+#*# -0.017500, 0.025000, 0.050000, 0.122500, 0.155000
+#*# -0.045000, -0.040000, 0.037500, 0.102500, 0.145000
+#*# -0.007500, -0.032500, 0.005000, 0.092500, 0.132500
+#*# -0.060000, -0.045000, -0.007500, 0.087500, 0.127500
+#*# 0.075000, 0.042500, 0.015000, 0.105000, 0.175000
+#*# x_count = 5
+#*# y_count = 5
+#*# mesh_x_pps = 2
+#*# mesh_y_pps = 2
+#*# algo = bicubic
+#*# tension = 0.2
+#*# min_x = 10.0
+#*# max_x = 195.0
+#*# min_y = 10.0
+#*# max_y = 220.0
+#*#
+#*# [bltouch]
+#*# z_offset = 1.342