From 555b71dde100bc0c8c83269afc12c7633b6bb5dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Markus=20K=C3=BCffner?= <11882946+mkuf@users.noreply.github.com> Date: Wed, 9 Mar 2022 22:36:54 +0100 Subject: [PATCH] add simulavr container for debugging (#12) * script: restructure build commands * klipper: add simulavr target * simulavr: add klipper config for simulavr; add compose file for simulavr * simulavr: set dependencies for klipper service * move ustreamer to override file; rearrange init service in main compose file; move config for simulavr to printer.cfg * move printer.cfg for simulavr to extra file * README: add debugging section * update docs --- README.md | 48 ++++++---- config/printer-simulavr.cfg | 157 +++++++++++++++++++++++++++++++++ docker-compose.override.yaml | 29 ++++++ docker-compose.simulavr.yaml | 37 ++++++++ docker-compose.yaml | 42 ++------- docker/klipper/Dockerfile | 32 +++++++ docker/klipper/config.simulavr | 49 ++++++++++ scripts/build-images.sh | 29 ++++-- 8 files changed, 368 insertions(+), 55 deletions(-) create mode 100644 config/printer-simulavr.cfg create mode 100644 docker-compose.override.yaml create mode 100644 docker-compose.simulavr.yaml create mode 100644 docker/klipper/config.simulavr diff --git a/README.md b/README.md index 4b142f6..bb70810 100644 --- a/README.md +++ b/README.md @@ -24,25 +24,17 @@ Follow the official Guides on how to get them up and running. * https://docs.docker.com/compose/cli-command/#installing-compose-v2 -### Add your Configuration to docker-compose.yaml +### Add your Configuration to docker-compose.override.yaml -Locate the ``klipper`` Service within ``docker-compose.yaml`` and update the ``device`` Section with the Serial Port of your Printer. -In this example, the Printer is using device ``/dev/ttymxc3``. Do not edit any other lines. +Locate the ``klipper`` Service within ``docker-compose.override.yaml`` and update the ``device`` Section with the Serial Port of your Printer. +In this example, the Printer is using device ``/dev/ttymxc3``. ```yaml klipper: - <<: *klipper-svc - volumes: - - ./config:/opt/cfg - - run:/opt/run - - gcode:/opt/gcode devices: - /dev/ttymxc3:/dev/ttymxc3 - profiles: - - fluidd - - mainsail ``` -Locate the ``ustreamer`` Service within ``docker-compose.yaml`` and update the ``device`` Section with the Device Name of your Webcam. +Locate the ``ustreamer`` Service within ``docker-compose.override.yaml`` and update the ``device`` Section with the Device Name of your Webcam. In this example, the Webcam is using device ``/dev/video0``. Do not edit any other lines. ```yaml ustreamer: @@ -111,7 +103,7 @@ docker compose --profile up -d ### Change Execution Options The Entrypoint for all Docker Images within this Repo are the actual Applications, which are run at container execution time. This makes it possible to set command line Arguments for the Apps as Docker Command. -Within docker-compose.yaml commands are already set, you may update them to fit your needs. +Within docker-compose.yaml commands are already set, you may override them within `docker-compose.override.yaml` to fit your needs. Example from service Klipper: ```yaml command: @@ -124,7 +116,7 @@ Example from service Klipper: ### Multiple Webcams The Ustreamer Service is already templated to be easily reused for multi-webcam Setups. -To add a new Ustreamer Service, simply add the following snippet to ``docker-compose.yaml``. +To add a new Ustreamer Service, simply add the following snippet to ``docker-compose.override.yaml``. Notice, that all service names, container names and traefik labels need to be unique. Hence replace webcam2 with webcam3 and so on for every webcam you add and update the physical device that gets passed to the container. ```yaml @@ -142,7 +134,7 @@ Hence replace webcam2 with webcam3 and so on for every webcam you add and update ### Building Docker images locally If you'd like to customize the provided Docker Images, you may edit the Dockerfiles within the ``docker/`` Directory. -Images are build in multiple stages, the final stage is called ``run``. Based on this, you can update Service definitions within ``docker-compose.yaml`` to build Images locally. +Images are build in multiple stages, the final stage is called ``run``. Based on this, you can update Service definitions within ``docker-compose.override.yaml`` to build Images locally. Example: Build Moonraker Update the ``image:`` name and add a ``build`` config: @@ -192,3 +184,29 @@ In case Moonraker is not situated on the same Host as Mainsail, you'll have to e volumes: - ./config/mainsail.json:/usr/share/nginx/html/config.json ``` + +### Debugging the Stack +Debugging the Stack without printer hardware is challenging, as klipper requires a mcu to operate. +For this purpose, you can build a service that emulates a mcu with simulavr, as suggested by the [Klipper Docs](https://github.com/Klipper3d/klipper/blob/master/docs/Debugging.md). + +The simulavr Image is part of the Dockerfile for Klipper but is not pushed to any registry, so it needs to be built when needed. + +Locate the `docker-compose.simulavr.yaml` in the repository and set the `VERSION` Build-Arg to any Git Reference from [Klipper3d/klipper](https://github.com/Klipper3d) that you would like the mcu code to be compatible with. + +This example builds the mcu code from [Klipper3d/klipper:d75154d](d75154d695efb1338cbfff061d226c4f384d127b) +```yaml + build: + context: docker/klipper + target: build-simulavr + args: + VERSION: d75154d695efb1338cbfff061d226c4f384d127b +``` + +Then start the Stack +``` +docker compose \ + --profile mainsail \ + -f docker-compose.yaml \ + -f docker-compose.simulavr.yaml \ + up -d +``` \ No newline at end of file diff --git a/config/printer-simulavr.cfg b/config/printer-simulavr.cfg new file mode 100644 index 0000000..0e27a9f --- /dev/null +++ b/config/printer-simulavr.cfg @@ -0,0 +1,157 @@ +# This file contains example pin mappings for testing with the +# "simulavr" program. To use this config, compile the firmware for an +# AVR atmega644p, enable "low-level configuration options", and enable +# "simulavr software emulation". Further details are in +# docs/Debugging.md. + +# See docs/Config_Reference.md for a description of parameters. + +[stepper_x] +# Pins: PA5, PA4, PA1 +step_pin: PA5 +dir_pin: PA4 +enable_pin: PA1 +microsteps: 16 +rotation_distance: 40 +endstop_pin: ^PB0 +position_min: -0.25 +position_endstop: 0 +position_max: 200 + +[stepper_y] +# Pins: PA3, PA2 +step_pin: PA3 +dir_pin: PA2 +enable_pin: PA1 +microsteps: 16 +rotation_distance: 40 +endstop_pin: ^PB1 +position_min: -0.25 +position_endstop: 0 +position_max: 200 + +[stepper_z] +# Pins: PC7, PC6 +step_pin: PC7 +dir_pin: PC6 +enable_pin: PA1 +microsteps: 16 +rotation_distance: 8 +endstop_pin: ^PB2 +position_min: 0.1 +position_endstop: 0.5 +position_max: 200 + +[extruder] +# Pins: PC3, PC2 +step_pin: PC3 +dir_pin: PC2 +enable_pin: PA1 +microsteps: 16 +rotation_distance: 33.500 +nozzle_diameter: 0.500 +filament_diameter: 3.500 +heater_pin: PB4 +sensor_type: EPCOS 100K B57560G104F +sensor_pin: PA7 +control: pid +pid_Kp: 22.2 +pid_Ki: 1.08 +pid_Kd: 114 +min_temp: 0 +min_extrude_temp: 0 +max_temp: 210 + +[heater_bed] +heater_pin: PB3 +sensor_type: EPCOS 100K B57560G104F +sensor_pin: PA0 +control: watermark +min_temp: 0 +max_temp: 110 + +[fan] +pin: PD6 + +[mcu] +serial: /opt/run/simulavr.tty + +[printer] +kinematics: cartesian +max_velocity: 500 +max_accel: 3000 +max_z_velocity: 25 +max_z_accel: 30 + + +## vvv Required by Moonraker/Mainsail/Fluidd vvv +[virtual_sdcard] +path: /opt/gcode + +[display_status] + +[pause_resume] + +[gcode_macro PAUSE] +description: Pause the actual running print +rename_existing: PAUSE_BASE +# change this if you need more or less extrusion +variable_extrude: 1.0 +gcode: + ##### read E from pause macro ##### + {% set E = printer["gcode_macro PAUSE"].extrude|float %} + ##### set park positon for x and y ##### + # default is your max posion from your printer.cfg + {% set x_park = printer.toolhead.axis_maximum.x|float - 5.0 %} + {% set y_park = printer.toolhead.axis_maximum.y|float - 5.0 %} + ##### calculate save lift position ##### + {% set max_z = printer.toolhead.axis_maximum.z|float %} + {% set act_z = printer.toolhead.position.z|float %} + {% if act_z < (max_z - 2.0) %} + {% set z_safe = 2.0 %} + {% else %} + {% set z_safe = max_z - act_z %} + {% endif %} + ##### end of definitions ##### + PAUSE_BASE + G91 + {% if printer.extruder.can_extrude|lower == 'true' %} + G1 E-{E} F2100 + {% else %} + {action_respond_info("Extruder not hot enough")} + {% endif %} + {% if "xyz" in printer.toolhead.homed_axes %} + G1 Z{z_safe} F900 + G90 + G1 X{x_park} Y{y_park} F6000 + {% else %} + {action_respond_info("Printer not homed")} + {% endif %} + +[gcode_macro RESUME] +description: Resume the actual running print +rename_existing: RESUME_BASE +gcode: + ##### read E from pause macro ##### + {% set E = printer["gcode_macro PAUSE"].extrude|float %} + #### get VELOCITY parameter if specified #### + {% if 'VELOCITY' in params|upper %} + {% set get_params = ('VELOCITY=' + params.VELOCITY) %} + {%else %} + {% set get_params = "" %} + {% endif %} + ##### end of definitions ##### + {% if printer.extruder.can_extrude|lower == 'true' %} + G91 + G1 E{E} F2100 + {% else %} + {action_respond_info("Extruder not hot enough")} + {% endif %} + RESUME_BASE {get_params} + +[gcode_macro CANCEL_PRINT] +description: Cancel the actual running print +rename_existing: CANCEL_PRINT_BASE +gcode: + TURN_OFF_HEATERS + CANCEL_PRINT_BASE \ No newline at end of file diff --git a/docker-compose.override.yaml b/docker-compose.override.yaml new file mode 100644 index 0000000..8acb286 --- /dev/null +++ b/docker-compose.override.yaml @@ -0,0 +1,29 @@ +## Ustreamer base Service +x-ustreamer-svc: &ustreamer-svc + image: mkuf/ustreamer:nightly + restart: unless-stopped + command: + - "--host=0.0.0.0" + - "--port=8080" + - "--slowdown" + - "--device=/dev/webcam" + - "--resolution=1280x960" + - "--format=MJPEG" + - "--desired-fps=30" + +## Add your personal config here +services: + klipper: + devices: + - /dev/ttymxc3:/dev/ttymxc3 + + ustreamer: + <<: *ustreamer-svc + container_name: ustreamer + devices: + - /dev/video0:/dev/webcam + labels: + - "traefik.enable=true" + - "traefik.http.services.ustreamer.loadbalancer.server.port=8080" + - "traefik.http.routers.ustreamer.rule=PathPrefix(`/stream`)" + - "traefik.http.routers.ustreamer.entrypoints=web" \ No newline at end of file diff --git a/docker-compose.simulavr.yaml b/docker-compose.simulavr.yaml new file mode 100644 index 0000000..b6fea0d --- /dev/null +++ b/docker-compose.simulavr.yaml @@ -0,0 +1,37 @@ +services: + ## simulavr + simulavr: + image: simulavr + container_name: simulavr + build: + context: docker/klipper + target: build-simulavr + args: + VERSION: master + privileged: true + volumes: + - /dev:/dev + - run:/opt/run + + ## Reconfigure Klipper service for simulavr + klipper: + depends_on: + init: + condition: service_completed_successfully + simulavr: + condition: service_started + privileged: true + volumes: + - ./config:/opt/cfg + - run:/opt/run + - gcode:/opt/gcode + - log:/opt/log + - /dev:/dev + command: + - "-I" + - "run/klipper.tty" + - "-a" + - "run/klipper.sock" + - "cfg/printer-simulavr.cfg" + - "-l" + - "log/klippy.log" \ No newline at end of file diff --git a/docker-compose.yaml b/docker-compose.yaml index c5a236b..4cca293 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -17,30 +17,9 @@ x-klipper-svc: &klipper-svc - "-l" - "log/klippy.log" -x-ustreamer-svc: &ustreamer-svc - image: mkuf/ustreamer:nightly - restart: unless-stopped - command: - - "--host=0.0.0.0" - - "--port=8080" - - "--slowdown" - - "--device=/dev/webcam" - - "--resolution=1280x960" - - "--format=MJPEG" - - "--desired-fps=30" - ## Service Definitions services: - ## Config dir needs to be writable by uid/gid 1000 - ## This container sets the right permissions and exits - init: - image: busybox:latest - container_name: init - command: chown -R 1000:1000 /prind/config - volumes: - - .:/prind - ## Klippy Services ## klipper: @@ -50,8 +29,6 @@ services: - run:/opt/run - gcode:/opt/gcode - log:/opt/log - devices: - - /dev/ttymxc3:/dev/ttymxc3 profiles: - fluidd - mainsail @@ -150,17 +127,14 @@ services: ## Accompanying Services/Infra ## - ## Webcam Service - ustreamer: - <<: *ustreamer-svc - container_name: ustreamer - devices: - - /dev/video0:/dev/webcam - labels: - - "traefik.enable=true" - - "traefik.http.services.ustreamer.loadbalancer.server.port=8080" - - "traefik.http.routers.ustreamer.rule=PathPrefix(`/stream`)" - - "traefik.http.routers.ustreamer.entrypoints=web" + ## Config dir needs to be writable by uid/gid 1000 + ## This container sets the right permissions and exits + init: + image: busybox:latest + container_name: init + command: chown -R 1000:1000 /prind/config + volumes: + - .:/prind ## Loadbalancer/Proxy traefik: diff --git a/docker/klipper/Dockerfile b/docker/klipper/Dockerfile index 3dc4bff..e0d50cd 100644 --- a/docker/klipper/Dockerfile +++ b/docker/klipper/Dockerfile @@ -49,3 +49,35 @@ RUN apt update \ stm32flash libnewlib-arm-none-eabi \ gcc-arm-none-eabi binutils-arm-none-eabi libusb-1.0 \ && apt clean + +## Build klipper.elf for Simulavr and Simulavr itself +FROM mcu as build-simulavr + +RUN apt update \ + && apt install -y \ + git cmake swig \ + python3-dev \ + && apt clean + +WORKDIR /opt/klipper +COPY config.simulavr .config +RUN make + +WORKDIR /opt +RUN git clone git://git.savannah.nongnu.org/simulavr.git \ + && cd simulavr \ + && git checkout release-1.1.0 \ + && make python \ + && make build + +RUN mkdir run +RUN groupadd simulavr --gid 1000 \ + && useradd simulavr --uid 1000 --gid simulavr \ + && usermod simulavr --append --groups dialout \ + && chown -R simulavr:simulavr /opt/* + +USER simulavr +ENV PYTHONPATH=/opt/simulavr/build/pysimulavr/ +VOLUME ["/opt/run"] +ENTRYPOINT ["klipper/scripts/avrsim.py"] +CMD ["-p", "/opt/run/simulavr.tty", "/opt/klipper/out/klipper.elf"] \ No newline at end of file diff --git a/docker/klipper/config.simulavr b/docker/klipper/config.simulavr new file mode 100644 index 0000000..59576f4 --- /dev/null +++ b/docker/klipper/config.simulavr @@ -0,0 +1,49 @@ +CONFIG_LOW_LEVEL_OPTIONS=y +CONFIG_MACH_AVR=y +# 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 is not set +CONFIG_AVR_SELECT=y +CONFIG_BOARD_DIRECTORY="avr" +# CONFIG_MACH_atmega2560 is not set +# CONFIG_MACH_atmega1280 is not set +# CONFIG_MACH_at90usb1286 is not set +# CONFIG_MACH_at90usb646 is not set +# CONFIG_MACH_atmega32u4 is not set +# CONFIG_MACH_atmega1284p is not set +CONFIG_MACH_atmega644p=y +# CONFIG_MACH_atmega328p is not set +# CONFIG_MACH_atmega328 is not set +# CONFIG_MACH_atmega168 is not set +CONFIG_MCU="atmega644p" +CONFIG_AVRDUDE_PROTOCOL="arduino" +CONFIG_AVR_FREQ_16000000=y +# CONFIG_AVR_FREQ_20000000 is not set +# CONFIG_AVR_FREQ_8000000 is not set +CONFIG_CLOCK_FREQ=16000000 +CONFIG_AVR_CLKPR=-1 +CONFIG_AVR_STACK_SIZE=256 +CONFIG_AVR_WATCHDOG=y +CONFIG_SERIAL=y +CONFIG_AVR_SERIAL_UART0=y +# CONFIG_AVR_SERIAL_UART1 is not set +CONFIG_SERIAL_PORT=0 +CONFIG_SIMULAVR=y +CONFIG_SERIAL_BAUD=250000 +CONFIG_USB_VENDOR_ID=0x1d50 +CONFIG_USB_DEVICE_ID=0x614e +CONFIG_USB_SERIAL_NUMBER="12345" +CONFIG_INITIAL_PINS="" +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_GPIO_BITBANGING=y +CONFIG_HAVE_STRICT_TIMING=y +CONFIG_INLINE_STEPPER_HACK=y \ No newline at end of file diff --git a/scripts/build-images.sh b/scripts/build-images.sh index 4ec04e5..6f23a76 100755 --- a/scripts/build-images.sh +++ b/scripts/build-images.sh @@ -21,6 +21,10 @@ source=$(grep "ARG REPO" ${dockerfile} | sed -r 's/.*REPO=(.*)$/\1/g') ref=$(git ls-remote ${source} HEAD | cut -f1) shortref=$(echo -n ${ref} | cut -c 1-7) +function log { + echo -e "\033[0;36m## ${1} \033[0m" +} + ## Explicitly build Targets, except 'build' for target in $(grep "FROM .* as" ${dockerfile} | sed -r 's/.*FROM.*as (.*)/\1/g' | grep -v build); do @@ -31,19 +35,32 @@ for target in $(grep "FROM .* as" ${dockerfile} | sed -r 's/.*FROM.*as (.*)/\1/g ## Nightly if docker manifest inspect ${registry}${app}:${shortref}${tag_extra} > /dev/null; then - echo -e "\033[0;36m## Image ${registry}${app}:${shortref}${tag_extra} already exists, nothing to do. \033[0m" + log "## Image ${registry}${app}:${shortref}${tag_extra} already exists, nothing to do." else - echo -e "\033[0;36m## Building nightly Image ${registry}${app}:${shortref}${tag_extra} \033[0m" - docker buildx build --build-arg VERSION=${ref} --platform ${platform} -t ${registry}${app}:${shortref}${tag_extra} -t ${registry}${app}:nightly${tag_extra} --target ${target} --push ${context} + log "## Building nightly Image ${registry}${app}:${shortref}${tag_extra}" + docker buildx build \ + --build-arg VERSION=${ref} \ + --platform ${platform} \ + --tag ${registry}${app}:${shortref}${tag_extra} \ + --tag ${registry}${app}:nightly${tag_extra} \ + --target ${target} \ + --push \ + ${context} fi ## Tags for tag in $(git -c 'versionsort.suffix=-' ls-remote --tags --sort='version:refname' --refs ${source} | tail -n3 | rev | cut -f1 -d'/' | rev); do if docker manifest inspect ${registry}${app}:${tag}${tag_extra} > /dev/null; then - echo -e "\033[0;36m## Image ${registry}${app}:${tag}${tag_extra} already exists, nothing to do. \033[0m" + log "## Image ${registry}${app}:${tag}${tag_extra} already exists, nothing to do." else - echo -e "\033[0;36m## Building Image for tagged release ${registry}${app}:${tag}${tag_extra} \033[0m" - docker buildx build --build-arg VERSION=${tag} --platform ${platform} -t ${registry}${app}:${tag}${tag_extra} --target ${target} --push ${context} + log "## Building Image for tagged release ${registry}${app}:${tag}${tag_extra}" + docker buildx build \ + --build-arg VERSION=${tag} \ + --platform ${platform} \ + --tag ${registry}${app}:${tag}${tag_extra} \ + --target ${target} \ + --push \ + ${context} fi done