├── .cargo └── config.toml ├── .gitattributes ├── .github └── workflows │ └── rust.yml ├── .gitignore ├── .gitmodules ├── .idx └── dev.nix ├── .vscode ├── extensions.json ├── launch.json └── settings.json ├── Cargo.toml ├── LICENSE ├── README.md ├── data └── sdcard.img ├── datasheets ├── BLTouch-Classic-7.pdf ├── MKS-ROBIN-NANO-V3.1 │ ├── DM00031020-.pdf │ ├── MKS Robin Nano V3.1_001 PIN.pdf │ └── MKS Robin Nano V3.1_001 SCH.pdf ├── NUCLEO-F410RB │ ├── DM00180366-.pdf │ ├── mcu-stm32f410rb.pdf │ ├── nucleo-f410rb-datasheet.pdf │ └── nucleo-f410rb-pinout.pdf ├── NUCLEO-L476RG │ ├── Arduino-CNC-Shield-Pinout-V3.XX.jpeg │ ├── Arduino-CNC-Shield-Schematics-V3.XX.jpg │ ├── Arduino-CNC-Shield-Schematics-V3.XX.pdf.jpg │ ├── DM00083560-.pdf │ ├── arduino-cnc-shield.pdf │ ├── mcu-stm32l476rg.pdf │ ├── nucleo-l476rg-pinout.pdf │ ├── nucleo-l476rg.pdf │ ├── pins.jpg │ └── stm32-nucleo-64-boards.pdf ├── RP2040 │ └── rp2040-datasheet.pdf ├── SKR_MINI_E3-V2.0 │ ├── BTT SKR MINI E3 V2.0 Instruction Manual.pdf │ ├── BTT SKR MINI E3 V2.0-PIN.pdf │ ├── BTT SKR MINI E3 V2.0-SCH.PDF │ ├── CD00171190-.pdf │ └── STM32F103RCT6.pdf ├── SKR_MINI_E3-V3.0 │ ├── BTT E3 SKR MINI V3.0_PIN.pdf │ ├── BTT E3 SKR MINI V3.0_SCH.pdf │ ├── dm00748675.pdf │ ├── rm0454-stm32g0x0-advanced-armbased-32bit-mcus-stmicroelectronics.pdf │ └── stm32g0x1-reference-manual.pdf ├── display │ └── ILI9341.pdf ├── drivers │ ├── A4988.pdf │ └── TMC2209_datasheet_rev1.09.pdf └── motors │ ├── 17HS15-1504S.pdf │ └── SY42STH33-1334MA.pdf ├── display ├── embedded │ ├── lv_conf.htpl │ └── lv_drv_conf.htpl └── native │ ├── lv_conf.htpl │ └── lv_drv_conf.htpl ├── doc ├── README.md ├── developers_guide.md ├── doc.header.html ├── img │ └── printhor_motion_high_level_architecture.png ├── system_architecture.md ├── thermal_sensors.md └── user_guide.md ├── hwi-boards ├── README.md ├── printhor-hwi_boilerplate │ ├── .cargo │ │ └── config.toml │ ├── Cargo.toml │ └── src │ │ ├── board │ │ ├── device.rs │ │ ├── io.rs │ │ ├── mod.rs │ │ └── types.rs │ │ └── lib.rs ├── printhor-hwi_esp32 │ ├── .cargo │ │ └── config.toml │ ├── Cargo.toml │ ├── README.md │ ├── rust-toolchain.toml │ └── src │ │ ├── board │ │ ├── device.rs │ │ ├── io.rs │ │ ├── mod.rs │ │ └── types.rs │ │ └── lib.rs ├── printhor-hwi_mks_robin_nano │ ├── .cargo │ │ └── config.toml │ ├── Cargo.toml │ ├── README.md │ ├── mks_robin_nano_3_1 │ │ ├── memory.x │ │ └── openocd.cfg │ └── src │ │ ├── board_stm32f4 │ │ ├── comm.rs │ │ ├── device.rs │ │ ├── io.rs │ │ ├── mod.rs │ │ └── types.rs │ │ └── lib.rs ├── printhor-hwi_native │ ├── .cargo │ │ └── config.toml │ ├── Cargo.toml │ ├── README.md │ └── src │ │ ├── board │ │ ├── comm.rs │ │ ├── device.rs │ │ ├── mocked_peripherals │ │ │ ├── mocked_adc.rs │ │ │ ├── mocked_display.rs │ │ │ ├── mocked_i2c.rs │ │ │ ├── mocked_pin.rs │ │ │ ├── mocked_pwm.rs │ │ │ ├── mocked_sd_card.rs │ │ │ ├── mocked_spi.rs │ │ │ ├── mocked_trinamic.rs │ │ │ ├── mocked_uart.rs │ │ │ ├── mocked_uart_sink.rs │ │ │ ├── mocked_uart_unix_socket.rs │ │ │ ├── mocked_wdt.rs │ │ │ └── mod.rs │ │ ├── mod.rs │ │ └── types.rs │ │ └── lib.rs ├── printhor-hwi_nucleo_64_arduino_cnc_hat │ ├── .cargo │ │ └── config.toml │ ├── Cargo.toml │ ├── README.md │ └── src │ │ ├── board_stm32f4 │ │ ├── device.rs │ │ ├── io.rs │ │ ├── mod.rs │ │ └── types.rs │ │ ├── board_stm32l4 │ │ ├── device.rs │ │ ├── io.rs │ │ ├── mod.rs │ │ └── types.rs │ │ └── lib.rs ├── printhor-hwi_rp_2040 │ ├── .cargo │ │ └── config.toml │ ├── Cargo.toml │ ├── README.md │ ├── rpi-pico │ │ ├── memory.x │ │ └── openocd.cfg │ └── src │ │ ├── board_rp_2040 │ │ ├── device.rs │ │ ├── io.rs │ │ ├── mod.rs │ │ └── types.rs │ │ └── lib.rs └── printhor-hwi_skr_mini_e3 │ ├── .cargo │ └── config.toml │ ├── Cargo.toml │ ├── README.md │ ├── skr_mini_e3_v3 │ ├── memory.x │ └── openocd.cfg │ └── src │ ├── board_stm32g0 │ ├── device.rs │ ├── io.rs │ ├── mod.rs │ └── types.rs │ └── lib.rs ├── openocd.gdbtpl ├── printhor-hwa-common-macros ├── Cargo.toml └── src │ └── lib.rs ├── printhor-hwa-common ├── .cargo │ └── config.toml ├── Cargo.toml ├── README.md └── src │ ├── async_utils.rs │ ├── contract.rs │ ├── defer_channel.rs │ ├── event_bus.rs │ ├── event_bus_channel.rs │ ├── kinematics │ ├── anthropomorphic_3dof.rs │ ├── cartessian.rs │ ├── core_xy.rs │ ├── delta.rs │ └── mod.rs │ ├── lib.rs │ ├── math │ ├── constants_f32.rs │ ├── constants_f64.rs │ ├── constants_fixedpoint.rs │ ├── geometry.rs │ ├── mod.rs │ ├── real_f32.rs │ ├── real_f64.rs │ └── real_fixedpoint.rs │ ├── motion_broadcast.rs │ ├── persistent_state.rs │ ├── sd_card.rs │ ├── sd_card_spi.rs │ ├── soft_uart.rs │ ├── thermistor.rs │ ├── traits.rs │ └── uart.rs ├── printhor-hwa-utils ├── Cargo.toml ├── README.md └── src │ ├── lib.rs │ ├── mutex_async.rs │ └── mutex_sync.rs ├── printhor ├── .cargo │ └── config.toml ├── Cargo.toml ├── build.rs ├── design │ └── architecture.md ├── img │ ├── contract-class-diagram.png │ ├── contract-class-diagram.puml │ ├── contract-class-diagram.svg │ ├── motion_plan.pdf │ ├── motion_plan.png │ └── motion_plan_old.png ├── rust-toolchain.toml └── src │ └── bin │ ├── helpers │ └── mod.rs │ ├── hwa │ ├── adapters │ │ ├── mod.rs │ │ └── spi.rs │ ├── controllers │ │ ├── adc_controller.rs │ │ ├── heater_controller.rs │ │ ├── mod.rs │ │ ├── motion_control │ │ │ ├── mod.rs │ │ │ ├── motion_config.rs │ │ │ ├── motion_interpolation.rs │ │ │ ├── motion_planner.rs │ │ │ ├── motion_ring_buffer.rs │ │ │ ├── motion_segment.rs │ │ │ ├── motion_status.rs │ │ │ ├── motion_step_actuator.rs │ │ │ ├── motion_step_plan.rs │ │ │ └── motion_step_plan_executor.rs │ │ ├── printer_controller.rs │ │ ├── pwm_controller.rs │ │ ├── sd_card_controller.rs │ │ ├── servo_controller.rs │ │ └── trinamic_controller.rs │ ├── drivers │ │ ├── mod.rs │ │ └── motion_driver.rs │ ├── hwi │ │ └── mod.rs │ ├── machine.rs │ ├── mod.rs │ └── types.rs │ ├── instrumentation │ ├── data_points.rs │ ├── gcode.rs │ ├── machinery.rs │ └── mod.rs │ ├── motion │ ├── mod.rs │ └── profile.rs │ ├── printhor.rs │ ├── processing │ ├── gcode_multiplexed_io.rs │ ├── gcode_parser.rs │ ├── gcode_processor.rs │ └── mod.rs │ ├── s_plot.rs │ └── tasks │ ├── mod.rs │ ├── task_control.rs │ ├── task_defer.rs │ ├── task_integration.rs │ ├── task_motion_broadcast.rs │ ├── task_print_job.rs │ ├── task_stepper.rs │ └── task_temperature.rs └── rust-toolchain.toml /.cargo/config.toml: -------------------------------------------------------------------------------- 1 | [env] 2 | 3 | ########################### 4 | ## Configuration - BEGIN ## 5 | ########################### 6 | 7 | ## Heater and bed configuration (thermistor based) ## 8 | 9 | # [Depends on the board] the pull-up resistance of hot-end sensor circuit in ohms 10 | HOT_END_THERM_PULL_UP_RESISTANCE = "4685.0" 11 | # Nominal resistance of hot-end NTC thermistor at 25ºC 12 | HOT_END_THERM_NOMINAL_RESISTANCE = "100000.0" 13 | # BETA value of hot-end NTC thermistor 14 | HOT_END_THERM_BETA = "3950.0" 15 | 16 | # [Depends on the board] the pull-up resistance of hot-bed sensor circuit in ohms 17 | HOT_BED_THERM_PULL_UP_RESISTANCE = "4685.0" 18 | # Nominal resistance of hot-bed NTC thermistor at 25ºC 19 | HOT_BED_THERM_NOMINAL_RESISTANCE = "100000.0" 20 | # BETA value of hot-bed NTC thermistor 21 | HOT_BED_THERM_BETA = "3950.0" 22 | 23 | ## Pure technical stuff ## 24 | 25 | #DEFMT_LOG = "INFO" 26 | #RUST_LOG = "INFO" 27 | ASYNC_STD_THREAD_COUNT = "1" 28 | EMBASSY_USB_MAX_INTERFACE_COUNT = "2" 29 | 30 | ########################### 31 | ## Configuration - END ## 32 | ########################### 33 | 34 | [target.'cfg(board="native")'] 35 | 36 | [target.'cfg(all(any(board="nucleo64-f410rb"), target_arch="arm", target_os="none"))'] 37 | #runner = "probe-run --chip STM32F410RBTx --connect-under-reset --log-format {L}{s}" 38 | runner = "probe-rs run --chip STM32F410RBTx --connect-under-reset --log-format {L}{s}" 39 | 40 | #rustflags = [ "-C", "linker=flip-link" ] 41 | #linker = "flip-link" 42 | 43 | [target.'cfg(all(any(board="nucleo64-l476rg"), target_arch="arm", target_os="none"))'] 44 | runner = "probe-rs run --chip STM32L476RGTx --log-format {L}{s}" 45 | #rustflags = [ "-C", "linker=flip-link" ] 46 | #linker = "flip-link" 47 | 48 | [target.'cfg(all(any(board="skr_mini_e3_v3"), target_arch="arm", target_os="none"))'] 49 | runner = "probe-rs run --chip STM32G0B1RETx --log-format {L}{s} --base-address 08002000" 50 | 51 | [target.'cfg(all(any(board="skr_mini_e3_v3-no-bootloader"), target_arch="arm", target_os="none"))'] 52 | runner = "echo echo PLEASE DON'T probe-rs run --chip STM32G0B1RETx --disable-progressbars --log-format {L}{s}" 53 | 54 | [target.'cfg(all(any(board="mks_robin_nano"), target_arch="arm", target_os="none"))'] 55 | runner = "probe-rs run --chip STM32F407VETx --disable-progressbars --log-format {L}{s} --base-address 08007000" 56 | 57 | [target.'cfg(all(any(board="mks_robin_nano-no-bootloader"), target_arch="arm", target_os="none"))'] 58 | runner = "probe-rs echo PLEASE DON'T run --chip STM32F407VETx --disable-progressbars --log-format {L}{s}" 59 | 60 | [target.'cfg(all(any(board="rp-2040"), target_arch="arm", target_os="none"))'] 61 | #runner = "probe-rs run --chip RP2040" 62 | runner = "elf2uf2-rs --deploy --serial --verbose" 63 | 64 | [build] 65 | rustdocflags = [ "--html-in-header", "doc/doc.header.html" ] 66 | 67 | [profile.dev.package."*"] 68 | codegen-units = 1 69 | incremental = false 70 | #opt-level = "s" # Set to "s" or even disable to play with GDB 71 | debug = 2 72 | debug-assertions = true 73 | overflow-checks = true 74 | 75 | [profile.release.package."*"] 76 | codegen-units = 1 77 | incremental = false 78 | opt-level = "z" 79 | debug = 2 80 | debug-assertions = true 81 | overflow-checks = true 82 | 83 | [profile.release-opt.package."*"] 84 | codegen-units = 1 85 | incremental = false 86 | opt-level = "z" 87 | debug = 0 88 | debug-assertions = false 89 | overflow-checks = false 90 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | *.x linguist-language=linguist-vendored 2 | *.htpl linguist-language=linguist-vendored 3 | *.img linguist-language=linguist-vendored 4 | *.cfg linguist-language=linguist-vendored 5 | *.gcode linguist-language=linguist-vendored 6 | *.sh linguist-language=linguist-vendored 7 | *.html linguist-language=linguist-vendored 8 | *.json linguist-language=linguist-vendored 9 | *.nix linguist-language=linguist-vendored 10 | -------------------------------------------------------------------------------- /.github/workflows/rust.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: [ "main" ] 6 | pull_request: 7 | branches: [ "main" ] 8 | 9 | env: 10 | CARGO_TERM_COLOR: always 11 | 12 | jobs: 13 | build: 14 | 15 | runs-on: ubuntu-20.04 16 | 17 | steps: 18 | - name: Checkout repository and submodules 19 | uses: actions/checkout@v4.2.2 20 | with: 21 | submodules: recursive 22 | - name: Build 23 | run: cargo build --features native --package prinThor --bin printhor 24 | - name: Generate doc 25 | run: cargo doc --no-deps 26 | - name: Install cargo-llvm-cov 27 | run: cargo install cargo-llvm-cov 28 | - name: Install lcov 29 | run: sudo apt-get -y install lcov 30 | - name: Generate code coverage of full featured hwa-common with f32 precision (default) 31 | run: cargo llvm-cov --profile release -p printhor-hwa-common --features float-point-f32-impl,with-motion,with-all-axis --ignore-filename-regex libs --lcov --output-path lcov.hwa-f32.info 32 | - name: Generate code coverage of full featured hwa-common with f64 precision 33 | run: cargo llvm-cov --profile release -p printhor-hwa-common --features float-point-f64-impl,with-motion,with-all-axis --ignore-filename-regex libs --lcov --output-path lcov.hwa-f64.info 34 | - name: Generate code coverage of full featured hwa-common with fixed point 128 precision 35 | run: cargo llvm-cov --profile release -p printhor-hwa-common --features fixed-point-128-impl,with-motion,with-all-axis --ignore-filename-regex libs --lcov --output-path lcov.hwa-fp128.info 36 | 37 | - name: Generate code coverage of native simulator backend [cartessian] (default) 38 | working-directory: ./hwi-boards/printhor-hwi_native 39 | run: cargo llvm-cov --profile release -p printhor-hwi_native --ignore-filename-regex libs --lcov --output-path ../../lcov.hwi-base.info 40 | - name: Generate code coverage of native simulator backend [core-xy] 41 | working-directory: ./hwi-boards/printhor-hwi_native 42 | run: cargo llvm-cov --profile release -p printhor-hwi_native --features with-motion-core-xy-kinematics --ignore-filename-regex libs --lcov --output-path ../../lcov.hwi-core-xy.info 43 | - name: Generate code coverage of native simulator backend [anthropomorphic] 44 | working-directory: ./hwi-boards/printhor-hwi_native 45 | run: cargo llvm-cov --profile release -p printhor-hwi_native --features with-motion-anthropomorphic-kinematics --ignore-filename-regex libs --lcov --output-path ../../lcov.hwi-anthropomorphic.info 46 | - name: Generate code coverage for a (default) [cartessian-cnc] machine firmware (native simulator backend) 47 | run: cargo llvm-cov --profile release --workspace --features cartessian-cnc --ignore-filename-regex libs --lcov --output-path lcov.fw-cartessian-cnc.info 48 | - name: Generate code coverage for a [cartessian-fdm] machine firmware (native simulator backend) 49 | run: cargo llvm-cov --profile release --workspace --features cartessian-fdm --ignore-filename-regex libs --lcov --output-path lcov.fw-cartessian-fdm.info 50 | - name: Generate code coverage for a [core-xy-fdm] machine firmware (native simulator backend) 51 | run: cargo llvm-cov --profile release --workspace --features core-xy-fdm --ignore-filename-regex libs --lcov --output-path lcov.fw-core-xy-fdm.info 52 | - name: Generate code coverage for an [anthropomorphic-quadruped-robot] machine firmware (native simulator backend) 53 | run: cargo llvm-cov --profile release --workspace --features anthropomorphic-quad-robot --ignore-filename-regex libs --lcov --output-path lcov.fw-anthropomorphic-quad-robot.info 54 | - name: Generate code coverage for a (default) [cartessian-cnc] machine firmware (s-plot) 55 | run: cargo llvm-cov --profile release --features s-plot-bin,cartessian-cnc --ignore-filename-regex libs --lcov --output-path lcov.s-plot.info 56 | - name: Merge lcov files 57 | run: lcov --add-tracefile lcov.hwa-f32.info -t hwa-f32 -a lcov.hwa-f64.info -t hwa-f64 -a lcov.hwa-fp128.info -t hwa-fp128 -a lcov.hwi-anthropomorphic.info -t t4 -a lcov.hwi-base.info -t t5 -a lcov.hwi-core-xy.info -t t6 -a lcov.fw-anthropomorphic-quad-robot.info -t t7 -a lcov.fw-cartessian-cnc.info -t t8 -a lcov.fw-cartessian-fdm.info -t t9 -a lcov.fw-core-xy-fdm.info -t t10 -a lcov.s-plot.info -t t11 -o lcov.info 58 | - name: Upload coverage to Coveralls 59 | uses: coverallsapp/github-action@v2 60 | with: 61 | files: lcov.info -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Generated by Cargo 2 | # will have compiled files and executables 3 | debug/ 4 | target/ 5 | 6 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries 7 | # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html 8 | Cargo.lock 9 | 10 | # These are backup files generated by rustfmt 11 | **/*.rs.bk 12 | 13 | # MSVC Windows builds of rustc generate these, which store debugging information 14 | *.pdb 15 | firmware.bin 16 | # For anyone who wanted to put .svd in data and debug peripherals with vscode complements like "Cortex Debug" + "Peripheral Viewer". I woul'd rather not to put them in git 17 | data/*.svd 18 | data/*.jar 19 | data/*.puml 20 | # Best practice is https://blog.sebastian-daschner.com/entries/global-gitignore but I'd better to maintain to avoid mistakes for people who are not highly proficient in git. 21 | .idea 22 | libs/*/target 23 | /plot.pdf 24 | /printhor/plot.pdf 25 | /hwi-boards/*/*.bin 26 | /printhor/design/*.jar 27 | /printhor/design/*.drawio 28 | /run*.sh 29 | /*.info 30 | /printhor/test-data 31 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "libs/embassy"] 2 | path = libs/embassy 3 | url = https://github.com/embassy-rs/embassy/ 4 | [submodule "libs/async-gcode"] 5 | path = libs/async-gcode 6 | url = https://github.com/cbruiz/async-gcode.git 7 | [submodule "libs/unix-fifo-async"] 8 | path = libs/unix-fifo-async 9 | url = https://github.com/cbruiz/unix-fifo-async.git 10 | -------------------------------------------------------------------------------- /.idx/dev.nix: -------------------------------------------------------------------------------- 1 | # To learn more about how to use Nix to configure your environment 2 | # see: https://developers.google.com/idx/guides/customize-idx-env 3 | { pkgs, ... }: { 4 | # Which nixpkgs channel to use. 5 | # channel = "stable-24.11"; # or "unstable" 6 | channel = "unstable"; 7 | # Use https://search.nixos.org/packages to find packages 8 | packages = [ 9 | pkgs.cargo 10 | pkgs.rustc 11 | pkgs.rustfmt 12 | pkgs.stdenv.cc 13 | ]; 14 | # Sets environment variables in the workspace 15 | env = { 16 | RUST_SRC_PATH = "${pkgs.rustPlatform.rustLibSrc}"; 17 | }; 18 | idx = { 19 | # Search for the extensions you want on https://open-vsx.org/ and use "publisher.id" 20 | extensions = [ 21 | "rust-lang.rust-analyzer" 22 | "serayuzgur.crates" 23 | "vadimcn.vscode-lldb" 24 | "edwinhuish.better-comments-next" 25 | "MoBalic.jetbrains-dark-theme" 26 | ]; 27 | workspace = { 28 | onCreate = { 29 | # Open editors for the following files by default, if they exist: 30 | default.openFiles = ["printhor/src/bin/s_plot.rs"]; 31 | # Post create hook to init git submodules: 32 | post-install = "git submodule update --init --recursive"; 33 | }; 34 | }; 35 | # Enable previews and customize configuration 36 | previews = {}; 37 | }; 38 | } -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [ 3 | "edwinhuish.better-comments-next", 4 | "vadimcn.vscode-lldb", 5 | "serayuzgur.crates", 6 | "mobalic.jetbrains-dark-theme", 7 | "rust-lang.rust-analyzer" 8 | ] 9 | } -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.0", 3 | "configurations": [ 4 | { 5 | "type": "lldb", 6 | "request": "launch", 7 | "name": "run splot", 8 | "cargo": { 9 | "args": [ 10 | "build", "--profile", "dev", "--package", "prinThor", "--features", "s-plot-bin", "--bin", "s_plot" 11 | ] 12 | }, 13 | "cwd": "${workspaceFolder}", 14 | "args": [] 15 | } 16 | ] 17 | } -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "IDX.aI.enableInlineCompletion": true, 3 | "IDX.aI.enableCodebaseIndexing": true 4 | } -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | resolver = "2" 3 | 4 | members = [ 5 | "printhor", 6 | "printhor-hwa-common", 7 | "printhor-hwa-common-macros", 8 | "printhor-hwa-utils", 9 | ] 10 | 11 | exclude = [ 12 | "libs/embassy", "libs/async-gcode", 13 | "hwi-boards/printhor-hwi_native", 14 | "hwi-boards/printhor-hwi_mks_robin_nano", 15 | "hwi-boards/printhor-hwi_nucleo_64_arduino_cnc_hat", 16 | "hwi-boards/printhor-hwi_rp_2040", 17 | "hwi-boards/printhor-hwi_skr_mini_e3", 18 | "hwi-boards/printhor-hwi_esp32", 19 | ] 20 | 21 | [workspace.package] 22 | license = "MIT" 23 | license-file = "LICENSE" 24 | 25 | [workspace.lints.clippy] 26 | [workspace.lints.rust] 27 | [workspace.lints.rustdoc] 28 | 29 | [patch.crates-io] 30 | 31 | printhor-hwa-common = { path = "printhor-hwa-common" } 32 | printhor-hwa-utils = { path = "printhor-hwa-utils" } 33 | printhor-hwi_native = { path = "hwi-boards/printhor-hwi_native" } 34 | printhor-hwi_skr_mini_e3 = { path = "hwi-boards/printhor-hwi_skr_mini_e3" } 35 | printhor-hwi_mks_robin_nano = { path = "hwi-boards/printhor-hwi_mks_robin_nano" } 36 | printhor-hwi_nucleo_64_arduino_cnc_hat = { path = "hwi-boards/printhor-hwi_nucleo_64_arduino_cnc_hat" } 37 | printhor-hwi_rp_2040 = {path = "hwi-boards/printhor-hwi_rp_2040"} 38 | 39 | async-gcode = { path = "libs/async-gcode" } 40 | 41 | embassy-stm32 = { path = "libs/embassy/embassy-stm32" } 42 | embassy-rp = { path = "libs/embassy/embassy-rp" } 43 | embassy-executor = { path = "libs/embassy/embassy-executor" } 44 | embassy-sync = { path = "libs/embassy/embassy-sync" } 45 | embassy-time = { path = "libs/embassy/embassy-time" } 46 | embassy-futures = { path = "libs/embassy/embassy-futures" } 47 | embassy-embedded-hal = { path = "libs/embassy/embassy-embedded-hal" } 48 | embassy-usb = { path = "libs/embassy/embassy-usb" } 49 | 50 | [profile.dev] 51 | codegen-units = 1 52 | debug = 2 53 | strip = false 54 | incremental = false 55 | debug-assertions = true 56 | overflow-checks = true 57 | #opt-level = "s" # disable to play with GDB 58 | lto = false 59 | panic = "unwind" 60 | 61 | [profile.release] 62 | codegen-units = 1 63 | debug = 2 64 | strip = false 65 | incremental = false 66 | debug-assertions = true 67 | overflow-checks = true 68 | opt-level = "z" 69 | lto = "fat" 70 | panic = "unwind" 71 | 72 | [profile.release-opt] 73 | inherits = "release" 74 | codegen-units = 1 75 | debug = 1 76 | incremental = false 77 | debug-assertions = false 78 | overflow-checks = false 79 | strip = false 80 | opt-level = "z" 81 | lto = "fat" 82 | panic = "abort" -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Carlos Barrales 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /data/sdcard.img: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cbruiz/printhor/1d04b31055f58224f909b2ea484eee94719cd425/data/sdcard.img -------------------------------------------------------------------------------- /datasheets/BLTouch-Classic-7.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cbruiz/printhor/1d04b31055f58224f909b2ea484eee94719cd425/datasheets/BLTouch-Classic-7.pdf -------------------------------------------------------------------------------- /datasheets/MKS-ROBIN-NANO-V3.1/DM00031020-.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cbruiz/printhor/1d04b31055f58224f909b2ea484eee94719cd425/datasheets/MKS-ROBIN-NANO-V3.1/DM00031020-.pdf -------------------------------------------------------------------------------- /datasheets/MKS-ROBIN-NANO-V3.1/MKS Robin Nano V3.1_001 PIN.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cbruiz/printhor/1d04b31055f58224f909b2ea484eee94719cd425/datasheets/MKS-ROBIN-NANO-V3.1/MKS Robin Nano V3.1_001 PIN.pdf -------------------------------------------------------------------------------- /datasheets/MKS-ROBIN-NANO-V3.1/MKS Robin Nano V3.1_001 SCH.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cbruiz/printhor/1d04b31055f58224f909b2ea484eee94719cd425/datasheets/MKS-ROBIN-NANO-V3.1/MKS Robin Nano V3.1_001 SCH.pdf -------------------------------------------------------------------------------- /datasheets/NUCLEO-F410RB/DM00180366-.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cbruiz/printhor/1d04b31055f58224f909b2ea484eee94719cd425/datasheets/NUCLEO-F410RB/DM00180366-.pdf -------------------------------------------------------------------------------- /datasheets/NUCLEO-F410RB/mcu-stm32f410rb.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cbruiz/printhor/1d04b31055f58224f909b2ea484eee94719cd425/datasheets/NUCLEO-F410RB/mcu-stm32f410rb.pdf -------------------------------------------------------------------------------- /datasheets/NUCLEO-F410RB/nucleo-f410rb-datasheet.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cbruiz/printhor/1d04b31055f58224f909b2ea484eee94719cd425/datasheets/NUCLEO-F410RB/nucleo-f410rb-datasheet.pdf -------------------------------------------------------------------------------- /datasheets/NUCLEO-F410RB/nucleo-f410rb-pinout.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cbruiz/printhor/1d04b31055f58224f909b2ea484eee94719cd425/datasheets/NUCLEO-F410RB/nucleo-f410rb-pinout.pdf -------------------------------------------------------------------------------- /datasheets/NUCLEO-L476RG/Arduino-CNC-Shield-Pinout-V3.XX.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cbruiz/printhor/1d04b31055f58224f909b2ea484eee94719cd425/datasheets/NUCLEO-L476RG/Arduino-CNC-Shield-Pinout-V3.XX.jpeg -------------------------------------------------------------------------------- /datasheets/NUCLEO-L476RG/Arduino-CNC-Shield-Schematics-V3.XX.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cbruiz/printhor/1d04b31055f58224f909b2ea484eee94719cd425/datasheets/NUCLEO-L476RG/Arduino-CNC-Shield-Schematics-V3.XX.jpg -------------------------------------------------------------------------------- /datasheets/NUCLEO-L476RG/Arduino-CNC-Shield-Schematics-V3.XX.pdf.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cbruiz/printhor/1d04b31055f58224f909b2ea484eee94719cd425/datasheets/NUCLEO-L476RG/Arduino-CNC-Shield-Schematics-V3.XX.pdf.jpg -------------------------------------------------------------------------------- /datasheets/NUCLEO-L476RG/DM00083560-.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cbruiz/printhor/1d04b31055f58224f909b2ea484eee94719cd425/datasheets/NUCLEO-L476RG/DM00083560-.pdf -------------------------------------------------------------------------------- /datasheets/NUCLEO-L476RG/arduino-cnc-shield.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cbruiz/printhor/1d04b31055f58224f909b2ea484eee94719cd425/datasheets/NUCLEO-L476RG/arduino-cnc-shield.pdf -------------------------------------------------------------------------------- /datasheets/NUCLEO-L476RG/mcu-stm32l476rg.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cbruiz/printhor/1d04b31055f58224f909b2ea484eee94719cd425/datasheets/NUCLEO-L476RG/mcu-stm32l476rg.pdf -------------------------------------------------------------------------------- /datasheets/NUCLEO-L476RG/nucleo-l476rg-pinout.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cbruiz/printhor/1d04b31055f58224f909b2ea484eee94719cd425/datasheets/NUCLEO-L476RG/nucleo-l476rg-pinout.pdf -------------------------------------------------------------------------------- /datasheets/NUCLEO-L476RG/nucleo-l476rg.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cbruiz/printhor/1d04b31055f58224f909b2ea484eee94719cd425/datasheets/NUCLEO-L476RG/nucleo-l476rg.pdf -------------------------------------------------------------------------------- /datasheets/NUCLEO-L476RG/pins.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cbruiz/printhor/1d04b31055f58224f909b2ea484eee94719cd425/datasheets/NUCLEO-L476RG/pins.jpg -------------------------------------------------------------------------------- /datasheets/NUCLEO-L476RG/stm32-nucleo-64-boards.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cbruiz/printhor/1d04b31055f58224f909b2ea484eee94719cd425/datasheets/NUCLEO-L476RG/stm32-nucleo-64-boards.pdf -------------------------------------------------------------------------------- /datasheets/RP2040/rp2040-datasheet.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cbruiz/printhor/1d04b31055f58224f909b2ea484eee94719cd425/datasheets/RP2040/rp2040-datasheet.pdf -------------------------------------------------------------------------------- /datasheets/SKR_MINI_E3-V2.0/BTT SKR MINI E3 V2.0 Instruction Manual.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cbruiz/printhor/1d04b31055f58224f909b2ea484eee94719cd425/datasheets/SKR_MINI_E3-V2.0/BTT SKR MINI E3 V2.0 Instruction Manual.pdf -------------------------------------------------------------------------------- /datasheets/SKR_MINI_E3-V2.0/BTT SKR MINI E3 V2.0-PIN.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cbruiz/printhor/1d04b31055f58224f909b2ea484eee94719cd425/datasheets/SKR_MINI_E3-V2.0/BTT SKR MINI E3 V2.0-PIN.pdf -------------------------------------------------------------------------------- /datasheets/SKR_MINI_E3-V2.0/BTT SKR MINI E3 V2.0-SCH.PDF: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cbruiz/printhor/1d04b31055f58224f909b2ea484eee94719cd425/datasheets/SKR_MINI_E3-V2.0/BTT SKR MINI E3 V2.0-SCH.PDF -------------------------------------------------------------------------------- /datasheets/SKR_MINI_E3-V2.0/CD00171190-.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cbruiz/printhor/1d04b31055f58224f909b2ea484eee94719cd425/datasheets/SKR_MINI_E3-V2.0/CD00171190-.pdf -------------------------------------------------------------------------------- /datasheets/SKR_MINI_E3-V2.0/STM32F103RCT6.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cbruiz/printhor/1d04b31055f58224f909b2ea484eee94719cd425/datasheets/SKR_MINI_E3-V2.0/STM32F103RCT6.pdf -------------------------------------------------------------------------------- /datasheets/SKR_MINI_E3-V3.0/BTT E3 SKR MINI V3.0_PIN.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cbruiz/printhor/1d04b31055f58224f909b2ea484eee94719cd425/datasheets/SKR_MINI_E3-V3.0/BTT E3 SKR MINI V3.0_PIN.pdf -------------------------------------------------------------------------------- /datasheets/SKR_MINI_E3-V3.0/BTT E3 SKR MINI V3.0_SCH.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cbruiz/printhor/1d04b31055f58224f909b2ea484eee94719cd425/datasheets/SKR_MINI_E3-V3.0/BTT E3 SKR MINI V3.0_SCH.pdf -------------------------------------------------------------------------------- /datasheets/SKR_MINI_E3-V3.0/dm00748675.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cbruiz/printhor/1d04b31055f58224f909b2ea484eee94719cd425/datasheets/SKR_MINI_E3-V3.0/dm00748675.pdf -------------------------------------------------------------------------------- /datasheets/SKR_MINI_E3-V3.0/rm0454-stm32g0x0-advanced-armbased-32bit-mcus-stmicroelectronics.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cbruiz/printhor/1d04b31055f58224f909b2ea484eee94719cd425/datasheets/SKR_MINI_E3-V3.0/rm0454-stm32g0x0-advanced-armbased-32bit-mcus-stmicroelectronics.pdf -------------------------------------------------------------------------------- /datasheets/SKR_MINI_E3-V3.0/stm32g0x1-reference-manual.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cbruiz/printhor/1d04b31055f58224f909b2ea484eee94719cd425/datasheets/SKR_MINI_E3-V3.0/stm32g0x1-reference-manual.pdf -------------------------------------------------------------------------------- /datasheets/display/ILI9341.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cbruiz/printhor/1d04b31055f58224f909b2ea484eee94719cd425/datasheets/display/ILI9341.pdf -------------------------------------------------------------------------------- /datasheets/drivers/A4988.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cbruiz/printhor/1d04b31055f58224f909b2ea484eee94719cd425/datasheets/drivers/A4988.pdf -------------------------------------------------------------------------------- /datasheets/drivers/TMC2209_datasheet_rev1.09.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cbruiz/printhor/1d04b31055f58224f909b2ea484eee94719cd425/datasheets/drivers/TMC2209_datasheet_rev1.09.pdf -------------------------------------------------------------------------------- /datasheets/motors/17HS15-1504S.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cbruiz/printhor/1d04b31055f58224f909b2ea484eee94719cd425/datasheets/motors/17HS15-1504S.pdf -------------------------------------------------------------------------------- /datasheets/motors/SY42STH33-1334MA.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cbruiz/printhor/1d04b31055f58224f909b2ea484eee94719cd425/datasheets/motors/SY42STH33-1334MA.pdf -------------------------------------------------------------------------------- /display/embedded/lv_drv_conf.htpl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cbruiz/printhor/1d04b31055f58224f909b2ea484eee94719cd425/display/embedded/lv_drv_conf.htpl -------------------------------------------------------------------------------- /doc/README.md: -------------------------------------------------------------------------------- 1 | ![Minimum Rust: 1.85](https://img.shields.io/badge/Minimum%20Rust%20Version-1.85-green.svg) 2 | [![crates.io](https://img.shields.io/crates/v/prinThor.svg)](https://crates.io/crates/prinThor) 3 | [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) 4 | 5 |

Printhor: The highly reliable but not necessarily functional 3D printer firmware

6 | 7 |

If you are using this product or like the project, please this repository to show your support! 🤩

8 | 9 | # System architecture low level detail 10 | 11 | [System architecture](system_architecture.md) 12 | 13 | # For users 14 | 15 | [User guide](user_guide.md) 16 | 17 | # For makers 18 | 19 | # For developers 20 | 21 | [Developers guide](developers_guide.md) 22 | 23 | # The features core 24 | 25 | TBD 26 | 27 | ## Physics and mathematics 28 | 29 | There are two major steps in the kinematics: 30 | 1. Motion plan computation. A Double S-Shape motion profile with 7 segments is calculated constraining to expected constraints (mean velocity, acceleration and jerk) and queued for execution. The queue size is configurable at compile time. 31 | 2. Motion plan execution. A dedicated high priority task, dequeues each planned move, interpolates the plan at fixed frequency (the higher frequency the finest) 32 | The interpolation algorithm is tolerant to unexpected short delays that can happen. A custom experimentation to adjust the interpolation frequency is 33 | 34 | In order to facilitate the understanding and decouple the reponsabilities as well, each movement is decomposed as: 35 | * Unitary director vector 36 | * Distance module 37 | 38 | So plan computation is related to the distance module only, which is interpolated as micro-segments and the axis advance is 39 | computed by multiplying unitary director vector by the Delta(t) 40 | 41 | # The thermal sensor 42 | 43 | [[See Thermal Sensors](thermal_sensors.md)] 44 | 45 | 46 | ## Movement queue 47 | 48 | TBD 49 | -------------------------------------------------------------------------------- /doc/developers_guide.md: -------------------------------------------------------------------------------- 1 | # Feature design 2 | 3 | # Simulation -------------------------------------------------------------------------------- /doc/doc.header.html: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /doc/img/printhor_motion_high_level_architecture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cbruiz/printhor/1d04b31055f58224f909b2ea484eee94719cd425/doc/img/printhor_motion_high_level_architecture.png -------------------------------------------------------------------------------- /doc/system_architecture.md: -------------------------------------------------------------------------------- 1 | # The controllers anatomy 2 | Why is that so complex? 3 | 4 | \[TLDR;\] Just because!. 5 | 6 | In bare-metal, most the peripherals need to be shared and they live forever in nature. 7 | In rust, there is always a lifetime for everything to met the language constraints, so obviously, 8 | in `PrinThor` there is a big bunch of machinery to wrap shared (stateful or stateless) peripherals (a Pin, for instance) or adaptors (a PwmController or Heater). 9 | 10 | There are complex Controllers build on top of Controllers. Most meaningful case is the SDCard Controller. 11 | 12 | Again, in bare-metal, is quite common that the -physical- SDCard peripheral communicates with the MCU leveraging a SPI Bus, which usually is shared. 13 | So, the construction is like: 14 | 15 | ```text 16 | StaticAsyncController[SDCardController] <- A clonable and shareable controller which wraps a single reference 17 | - StaticAsyncController[SDCard] <- A stateful controller (holding open files, etc) 18 | - StaticAsyncController[Device] <- The "Device", which can be shared 19 | - CSPin <- Now yes, the owned instance of the CS Pin 20 | - StaticAsyncController[SPI] <- Another controller to share same SPI for different controllers 21 | - SPI <- Now yes, the owned instance 22 | 23 | ``` 24 | 25 | So there can be a **single** `StaticAsyncController[SPI]` constructed by the HWI layer than can be shared by two **different** high level controllers like: 26 | 27 | * The SDCardController 28 | * Another controller. A DisplayController, for instance 29 | 30 | # Architecture 31 | 32 | The Work-in-Progress architecture design 33 | 34 | ![alt text](img/printhor_motion_high_level_architecture.png "High Level Architecture (motion only as of now)") 35 | 36 | ## Event bus 37 | 38 | ## Deferred commands 39 | 40 | ## State machine 41 | -------------------------------------------------------------------------------- /doc/thermal_sensors.md: -------------------------------------------------------------------------------- 1 | # The thermal sensors 2 | A custom thermistor is supported (see configuration below). Calculations are based on Steinhart-Hart equation. 3 | ADC calibration with internal VRef is automatically performed when the MCU supports it, in order to improve accuracy. 4 | 5 | # Configuration 6 | 7 | TBD 8 | 9 | ## Temperature calibration 10 | 11 | Currently only thermistor support is implemented. 12 | Normally the sensor circuit consist in a pull-up resistor between ADC Pin and +Vref (3.3v in STM32 boards). 13 | The thermistor itself should be easy to determine from manufacturer specs/datasheet, so there are three parameters by each (currently 2 supported sensors): 14 | * The pull-up resistor value, based on the specs: 15 | * HOT_END_THERM_PULL_UP_RESISTANCE and HOT_BED_THERM_PULL_UP_RESISTANCE 16 | 17 | The pull-up resistance of sensor circuit in ohms, with a default value of 4685.0 (~4.7kΩ) 18 | This value can be measured connecting a normal resistor (1kΩ for instance) to the sensor pins and calculating R as: 19 | ```text 20 | R = (1000 * (3.3 - Vadc)) / Vadc 21 | ``` 22 | Vadc can be easily measured with a multimeter. For convenience M105 command reports the resistor value (TZ and BZ), example: 23 | ```text 24 | > M114 25 | T:X.XXXX /0 T@:0 TZ:1000.00 B:Y.YYYY /0 B@:0 BZ:1000.00 26 | ``` 27 | * HOT_END_THERM_NOMINAL_RESISTANCE and HOT_BED_THERM_NOMINAL_RESISTANCE 28 | 29 | The nominal (expected) resistance of the NTC thermistor to have at 25ºC, hat can be gotten from manufacturer specs. By default, 100000 (100kΩ) 30 | 31 | * HOT_END_THERM_BETA and HOT_BED_THERM_BETA. 32 | 33 | The β value of hot-end NTC thermistor, that can be gotten frm manufacturer as well. By default, 3950.0 34 | 35 | If few words, if the resistance measured by M114 is correct and NTC nominal resistance and beta parameters are also correct, it should be fine. 36 | 37 | These settings can be overridden at compile time by setting the proper environment variables. For instance, let's say we have a sensor pull-up resistor of 2K and the thermistor is NTC 10k 3950: 38 | ```text 39 | HOT_END_THERM_PULL_UP_RESISTANCE=2000 HOT_END_THERM_NOMINAL_RESISTANCE=10000 cargo build [...] 40 | ``` 41 | 42 | -------------------------------------------------------------------------------- /doc/user_guide.md: -------------------------------------------------------------------------------- 1 | # Compiling and flashing 2 | 3 | # Calibration 4 | 5 | # ... 6 | 7 | # Troubleshooting 8 | TBD -------------------------------------------------------------------------------- /hwi-boards/README.md: -------------------------------------------------------------------------------- 1 | ![Minimum Rust: 1.85](https://img.shields.io/badge/Minimum%20Rust%20Version-1.85-green.svg) 2 | [![crates.io](https://img.shields.io/crates/v/prinThor.svg)](https://crates.io/crates/prinThor) 3 | [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) 4 | ![Discord Shield](https://discordapp.com/api/guilds/1169965662618259456/widget.png?style=shield) 5 | 6 |

Printhor: The highly reliable but not necessarily functional 3D printer firmware

7 | 8 |

If you are using this product or like the project, please this repository to show your support! 🤩

9 | 10 | A naïve intent to bring support for Xtensa architecture 11 | 12 | Instructions (Note: requires nightly toolchain and llvm utilery. Ref: [The Rust on ESP Book](https://docs.esp-rs.org/book/)) 13 | ```shell 14 | cargo install espup 15 | espup install --toolchain-version 1.85.0.0 16 | source ~/export-esp.sh 17 | 18 | [...] 19 | 20 | ``` -------------------------------------------------------------------------------- /hwi-boards/printhor-hwi_boilerplate/.cargo/config.toml: -------------------------------------------------------------------------------- 1 | [env] 2 | 3 | ########################### 4 | ## Configuration - BEGIN ## 5 | ########################### 6 | 7 | ## Heater and bed configuration (thermistor based) ## 8 | 9 | # [Depends on the board] the pull-up resistance of hot-end sensor circuit in ohms 10 | HOT_END_THERM_PULL_UP_RESISTANCE = "4685.0" 11 | # Nominal resistance of hot-end NTC thermistor at 25ºC 12 | HOT_END_THERM_NOMINAL_RESISTANCE = "100000.0" 13 | # BETA value of hot-end NTC thermistor 14 | HOT_END_THERM_BETA = "3950.0" 15 | 16 | # [Depends on the board] the pull-up resistance of hot-bed sensor circuit in ohms 17 | HOT_BED_THERM_PULL_UP_RESISTANCE = "4685.0" 18 | # Nominal resistance of hot-bed NTC thermistor at 25ºC 19 | HOT_BED_THERM_NOMINAL_RESISTANCE = "100000.0" 20 | # BETA value of hot-bed NTC thermistor 21 | HOT_BED_THERM_BETA = "3950.0" 22 | 23 | ## Pure technical stuff ## 24 | 25 | DEFMT_LOG = "info" 26 | RUST_LOG = "warn" 27 | ASYNC_STD_THREAD_COUNT = "1" 28 | EMBASSY_USB_MAX_INTERFACE_COUNT = "2" 29 | 30 | ########################### 31 | ## Configuration - END ## 32 | ########################### 33 | 34 | [target.'cfg(all(any(board="TBD"), target_arch="arm", target_os="none"))'] 35 | runner = "probe-rs run [...]" 36 | 37 | [build] 38 | #target = "thumbv7em-none-eabihf" 39 | 40 | [profile.dev.package."*"] 41 | codegen-units = 1 42 | incremental = false 43 | #opt-level = "s" # Set to "s" or even disable to play with GDB 44 | debug = 2 45 | debug-assertions = true 46 | overflow-checks = true 47 | 48 | [profile.release.package."*"] 49 | codegen-units = 1 50 | incremental = false 51 | opt-level = "z" 52 | debug = 2 53 | debug-assertions = true 54 | overflow-checks = true 55 | 56 | [profile.release-opt.package."*"] 57 | codegen-units = 1 58 | incremental = false 59 | opt-level = "z" 60 | debug = 0 61 | debug-assertions = false 62 | overflow-checks = false 63 | -------------------------------------------------------------------------------- /hwi-boards/printhor-hwi_boilerplate/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "printhor-hwi_boilerplate" 3 | version = "0.0.4" 4 | edition = "2024" 5 | authors = ["Carlos Barrales Ruiz "] 6 | description = "Printhor Hardware Abstraction starter project" 7 | repository = "https://github.com/cbruiz/printhor" 8 | keywords = ["hardware", "abstraction"] 9 | license = "MIT" 10 | documentation = "https://docs.rs/prinThor" 11 | homepage = "https://github.com/cbruiz/printhor" 12 | 13 | [lib] 14 | 15 | [features] 16 | default = [ 17 | "float-point-f32-impl" 18 | ] 19 | # Hardware device features 20 | with-serial-usb = ["embassy-usb", "printhor-hwa-common/with-serial-usb"] 21 | with-serial-port-1 = ["embedded-io-async", "printhor-hwa-common/with-serial-port-1"] 22 | with-serial-port-2 = ["embedded-io-async", "printhor-hwa-common/with-serial-port-2"] 23 | with-print-job = ["printhor-hwa-common/with-print-job"] 24 | with-spi = ["embedded-hal", "printhor-hwa-common/with-spi"] 25 | with-i2c = ["printhor-hwa-common/with-i2c"] 26 | with-hot-end = ["embedded-hal", "printhor-hwa-common/with-hot-end"] 27 | with-hot-bed = ["embedded-hal", "printhor-hwa-common/with-hot-bed"] 28 | with-motion = ["printhor-hwa-common/with-motion"] 29 | with-motion-broadcast = ["printhor-hwa-common/with-motion-broadcast"] 30 | with-motion-stepper = [] 31 | with-motion-cartessian-kinematics = ["printhor-hwa-common/with-motion-cartessian-kinematics"] 32 | with-motion-delta-kinematics = ["printhor-hwa-common/with-motion-delta-kinematics"] 33 | with-motion-core-xy-kinematics = ["printhor-hwa-common/with-motion-core-xy-kinematics"] 34 | with-motion-anthropomorphic-kinematics = ["printhor-hwa-common/with-motion-anthropomorphic-kinematics"] 35 | with-probe = ["embedded-hal", "printhor-hwa-common/with-probe"] 36 | with-sd-card = ["printhor-hwa-common/with-sd-card"] 37 | with-fan-layer = ["embedded-hal", "printhor-hwa-common/with-fan-layer"] 38 | with-fan-extra-1 = ["embedded-hal", "printhor-hwa-common/with-fan-extra-1"] 39 | with-laser = ["embedded-hal", "printhor-hwa-common/with-laser"] 40 | with-defmt = ["defmt", "printhor-hwa-common/with-defmt", "printhor-hwa-common/with-defmt", "printhor-hwa-utils/with-defmt", "embassy-executor/defmt"] 41 | with-trinamic = ["printhor-hwa-common/with-trinamic"] 42 | with-ps-on = ["printhor-hwa-common/with-ps-on"] 43 | 44 | with-e-axis = ["printhor-hwa-common/with-e-axis"] 45 | with-x-axis = ["printhor-hwa-common/with-x-axis"] 46 | with-y-axis = ["printhor-hwa-common/with-y-axis"] 47 | with-z-axis = ["printhor-hwa-common/with-z-axis"] 48 | with-a-axis = ["printhor-hwa-common/with-a-axis"] 49 | with-b-axis = ["printhor-hwa-common/with-b-axis"] 50 | with-c-axis = ["printhor-hwa-common/with-c-axis"] 51 | with-i-axis = ["printhor-hwa-common/with-i-axis"] 52 | with-j-axis = ["printhor-hwa-common/with-j-axis"] 53 | with-k-axis = ["printhor-hwa-common/with-k-axis"] 54 | with-u-axis = ["printhor-hwa-common/with-u-axis"] 55 | with-v-axis = ["printhor-hwa-common/with-v-axis"] 56 | with-w-axis = ["printhor-hwa-common/with-w-axis"] 57 | 58 | nightly = ["embassy-executor/nightly"] 59 | without-ringbuffer = [] 60 | verbose-timings = [] 61 | sd-card-uses-spi = ["printhor-hwa-common/sd-card-spi"] 62 | without-bootloader = [] 63 | 64 | float-point-f32-impl = ["printhor-hwa-common/float-point-f32-impl"] 65 | float-point-f64-impl = ["printhor-hwa-common/float-point-f64-impl"] 66 | fixed-point-128-impl = ["printhor-hwa-common/fixed-point-128-impl", "rust_decimal", "rust_decimal_macros"] 67 | 68 | [dependencies] 69 | #defmt = { version = "0.3.10", optional = true, default-features = false, features=["alloc"] } 70 | #cortex-m = { version = "0.7.7", default-features = false, features = ["inline-asm", "critical-section-single-core"] } 71 | #cortex-m-rt = { version = "0.7.5", default-features = false } 72 | #alloc-cortex-m = { version = "0.4.4", default-features = false, features = [] } 73 | critical-section = { version = "1.2.0", default-features = false, features = ["restore-state-bool"]} 74 | 75 | embassy-executor = { version = "0.7.0", default-features = false, features = ["executor-thread", "task-arena-size-16384"] } 76 | embassy-sync = { version = "0.6.2", default-features = false, features = [] } 77 | embassy-time = { version = "0.4.0", default-features = false, features = ["tick-hz-1_000_000", "generic-queue-128"] } 78 | embassy-futures = { version = "0.1.1", default-features = false, features = [] } 79 | embassy-usb = { version = "0.4.0", default-features = false, features = [], optional = true } 80 | 81 | printhor-hwa-common = { version = "0.0.4", default-features = false, features = [], path = "../../printhor-hwa-common" } 82 | printhor-hwa-utils = { version = "0.0.4", default-features = false, features = [], path = "../../printhor-hwa-utils" } 83 | async-gcode = {version = "0.3.0", default-features = false, features=["parse-checksum", "parse-trailing-comment", "optional-value", "string-value"] } 84 | 85 | embedded-hal = { version = "1.0.0", default-features = false, optional = true } 86 | embedded-hal-02 = { package = "embedded-hal", version = "0.2.7", default-features = false, features = ["unproven"] } 87 | embedded-io-async = { version = "0.6.1", default-features = false, optional = true, features = [] } 88 | 89 | cfg-if = { version = "1.0.0"} 90 | const_env = { version = "0.1.2" } 91 | 92 | rust_decimal = { optional = true, version = "1.36.0", default-features = false, features = ["maths", "serde-with-str"] } 93 | rust_decimal_macros = { optional = true, default-features = false, version = "1.36.0" } 94 | 95 | [patch.crates-io] 96 | async-gcode = { path = "../../libs/async-gcode" } 97 | embassy-executor = { path = "../../libs/embassy/embassy-executor" } 98 | embassy-sync = { path = "../../libs/embassy/embassy-sync" } 99 | embassy-time = { path = "../../libs/embassy/embassy-time" } 100 | embassy-futures = { path = "../../libs/embassy/embassy-futures" } 101 | embassy-usb = { path = "../../libs/embassy/embassy-usb" } 102 | -------------------------------------------------------------------------------- /hwi-boards/printhor-hwi_boilerplate/src/board/types.rs: -------------------------------------------------------------------------------- 1 | use printhor_hwa_common as hwa; 2 | 3 | //#region "General controllers lock types" 4 | pub type EventBusPubSubMutexType = hwa::AsyncNoopMutexType; 5 | pub type EventBusLockType = hwa::AsyncNoopMutexType; 6 | //#endregion 7 | 8 | //#region "HWI Shared controllers lock types" 9 | pub type WatchDogLockType = hwa::AsyncNoopMutexType; 10 | 11 | cfg_if::cfg_if! { 12 | if #[cfg(feature = "with-serial-usb")] { 13 | pub type SerialUsbTxLockType = hwa::AsyncNoopMutexType; 14 | pub type SerialUsbTxMutexStrategy = hwa::AsyncStandardStrategy; 15 | } 16 | } 17 | 18 | cfg_if::cfg_if! { 19 | if #[cfg(feature = "with-serial-port-1")] { 20 | pub type SerialPort1TxLockType = hwa::AsyncNoopMutexType; 21 | pub type SerialPort1TxMutexStrategy = hwa::AsyncStandardStrategy; 22 | } 23 | } 24 | 25 | cfg_if::cfg_if! { 26 | if #[cfg(feature = "with-serial-port-2")] { 27 | pub type SerialPort2TxLockType = hwa::AsyncNoopMutexType; 28 | pub type SerialPort2TxMutexStrategy = hwa::AsyncStandardStrategy; 29 | } 30 | } 31 | //#endregion 32 | 33 | //#region "General controllers locking strategy customization" 34 | pub type EventBusMutexStrategy = hwa::AsyncStandardStrategy>; 35 | pub type WatchDogMutexStrategy = hwa::AsyncStandardStrategy; 36 | //#endregion 37 | 38 | //#region "Shared controllers locking strategy customization" 39 | cfg_if::cfg_if! { 40 | if #[cfg(feature = "with-serial-port-1")] { 41 | pub type SerialPort1TxMutexStrategy = hwa::AsyncStandardStrategy; 42 | } 43 | } 44 | 45 | cfg_if::cfg_if! { 46 | if #[cfg(any(feature = "with-motion", feature = "with-hot-end", feature = "with-hot-bed"))] { 47 | pub type DeferChannelMutexType = hwa::AsyncNoopMutexType; 48 | } 49 | } 50 | 51 | cfg_if::cfg_if! { 52 | if #[cfg(feature = "with-motion")] { 53 | pub type StepActuatorMutexType = hwa::SyncCsMutexType; 54 | pub type MotionSignalMutexType = hwa::AsyncNoopMutexType; 55 | pub type MotionRingBufferMutexType = hwa::AsyncNoopMutexType; 56 | pub type MotionConfigMutexType = hwa::AsyncCsMutexType; 57 | pub type MotionStatusMutexType = hwa::AsyncNoopMutexType; 58 | pub type MotionDriverMutexType = hwa::AsyncNoopMutexType; 59 | 60 | pub type StepActuatorMuxtexStrategy = hwa::SyncStandardStrategy; 61 | } 62 | } 63 | 64 | cfg_if::cfg_if! { 65 | if #[cfg(all(feature = "with-motion", feature = "with-motion-broadcast"))] { 66 | pub type MotionBroadcastChannelMutexType = hwa::AsyncCsMutexType; 67 | 68 | pub type MotionSenderMutexType = I2cMutexType; 69 | 70 | pub type MotionSenderMutexStrategy = hwa::AsyncStandardStrategy; 71 | } 72 | } 73 | 74 | 75 | cfg_if::cfg_if! { 76 | if #[cfg(feature = "with-ps-on")] { 77 | pub type PSOnLockType = hwa::SyncNoopMutexType; 78 | pub type PSOnMutexStrategy = hwa::SyncStandardStrategy; 79 | } 80 | } 81 | 82 | cfg_if::cfg_if! { 83 | if #[cfg(feature = "with-spi")] { 84 | pub type Spi1MutexType = hwa::AsyncNoopMutexType; 85 | pub type Spi1MutexStrategyType = hwa::AsyncHoldableStrategy; 86 | } 87 | } 88 | 89 | cfg_if::cfg_if! { 90 | if #[cfg(feature = "with-i2c")] { 91 | pub type I2cMutexType = hwa::AsyncCsMutexType; 92 | pub type I2cMutexStrategyType = hwa::AsyncStandardStrategy; 93 | } 94 | } 95 | 96 | cfg_if::cfg_if! { 97 | if #[cfg(feature = "with-laser")] { 98 | pub type LaserMutexType = hwa::SyncCsMutexType; 99 | pub type LaserPwmMutexStrategy = hwa::SyncStandardStrategy; 100 | } 101 | } 102 | 103 | cfg_if::cfg_if! { 104 | if #[cfg(feature = "with-probe")] { 105 | pub type ProbeMutexType = hwa::SyncCsMutexType; 106 | pub type ProbePwmMutexStrategy = hwa::SyncStandardStrategy; 107 | } 108 | } 109 | 110 | cfg_if::cfg_if! { 111 | if #[cfg(any(feature="with-hot-end", feature = "with-hot-bed"))] { 112 | pub type HotEndHotBedAdcMutexType = hwa::SyncCsMutexType; 113 | pub type HotEndHotBedAdcMutexStrategy = hwa::AsyncStandardStrategy; 114 | 115 | pub type HotEndHotBedPwmMutexType = hwa::SyncNoopMutexType; 116 | pub type HotEndHotBedPwmMutexStrategy = hwa::SyncStandardStrategy; 117 | } 118 | } 119 | 120 | cfg_if::cfg_if! { 121 | if #[cfg(feature="with-hot-end")] { 122 | pub type HotEndAdcMutexStrategy = HotEndHotBedAdcMutexStrategy; 123 | pub type HotEndPwmMutexStrategy = HotEndHotBedPwmMutexStrategy; 124 | } 125 | } 126 | 127 | cfg_if::cfg_if! { 128 | if #[cfg(feature="with-hot-bed")] { 129 | pub type HotBedAdcMutexStrategy = HotEndHotBedAdcMutexStrategy; 130 | pub type HotBedPwmMutexStrategy = HotEndHotBedPwmMutexStrategy; 131 | } 132 | } 133 | //#endregion -------------------------------------------------------------------------------- /hwi-boards/printhor-hwi_boilerplate/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![allow(stable_features)] 2 | mod board; 3 | pub use board::Contract; -------------------------------------------------------------------------------- /hwi-boards/printhor-hwi_esp32/.cargo/config.toml: -------------------------------------------------------------------------------- 1 | [env] 2 | 3 | ########################### 4 | ## Configuration - BEGIN ## 5 | ########################### 6 | 7 | ## Heater and bed configuration (thermistor based) ## 8 | 9 | # [Depends on the board] the pull-up resistance of hot-end sensor circuit in ohms 10 | HOT_END_THERM_PULL_UP_RESISTANCE = "4685.0" 11 | # Nominal resistance of hot-end NTC thermistor at 25ºC 12 | HOT_END_THERM_NOMINAL_RESISTANCE = "100000.0" 13 | # BETA value of hot-end NTC thermistor 14 | HOT_END_THERM_BETA = "3950.0" 15 | 16 | # [Depends on the board] the pull-up resistance of hot-bed sensor circuit in ohms 17 | HOT_BED_THERM_PULL_UP_RESISTANCE = "4685.0" 18 | # Nominal resistance of hot-bed NTC thermistor at 25ºC 19 | HOT_BED_THERM_NOMINAL_RESISTANCE = "100000.0" 20 | # BETA value of hot-bed NTC thermistor 21 | HOT_BED_THERM_BETA = "3950.0" 22 | 23 | ## Pure technical stuff ## 24 | 25 | DEFMT_LOG = "info" 26 | RUST_LOG = "warn" 27 | ASYNC_STD_THREAD_COUNT = "1" 28 | EMBASSY_USB_MAX_INTERFACE_COUNT = "2" 29 | 30 | ########################### 31 | ## Configuration - END ## 32 | ########################### 33 | 34 | [target.'cfg(all(any(board="esp32s3"), target_arch="xtensa-esp32s3", target_os="none"))'] 35 | #runner = "espflash flash --monitor" 36 | rustflags = [ 37 | # GNU LD 38 | "-C", "link-arg=-Wl,-Tlinkall.x", 39 | "-C", "link-arg=-nostartfiles", 40 | ] 41 | runner = "probe-rs run --chip esp32c3" 42 | 43 | [build] 44 | target = "xtensa-esp32s3-none-elf" 45 | 46 | [profile.dev.package."*"] 47 | codegen-units = 1 48 | incremental = false 49 | #opt-level = "s" # Set to "s" or even disable to play with GDB 50 | debug = 2 51 | debug-assertions = true 52 | overflow-checks = true 53 | 54 | [profile.release.package."*"] 55 | codegen-units = 1 56 | incremental = false 57 | opt-level = "z" 58 | debug = 2 59 | debug-assertions = true 60 | overflow-checks = true 61 | 62 | [profile.release-opt.package."*"] 63 | codegen-units = 1 64 | incremental = false 65 | opt-level = "z" 66 | debug = 0 67 | debug-assertions = false 68 | overflow-checks = false 69 | 70 | [unstable] 71 | build-std = ["alloc", "core"] -------------------------------------------------------------------------------- /hwi-boards/printhor-hwi_esp32/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "printhor-hwi_esp32" 3 | version = "0.0.4" 4 | edition = "2024" 5 | authors = ["Carlos Barrales Ruiz "] 6 | description = "Printhor Hardware Abstraction for Expressif ESP32" 7 | repository = "https://github.com/cbruiz/printhor" 8 | keywords = ["hardware", "abstraction"] 9 | license = "MIT" 10 | documentation = "https://docs.rs/prinThor" 11 | homepage = "https://github.com/cbruiz/printhor" 12 | 13 | [lib] 14 | 15 | [features] 16 | default = [ 17 | "float-point-f32-impl" 18 | ] 19 | # Hardware device features 20 | with-serial-usb = ["embassy-usb", "printhor-hwa-common/with-serial-usb"] 21 | with-serial-port-1 = ["embedded-io-async", "printhor-hwa-common/with-serial-port-1"] 22 | with-serial-port-2 = ["embedded-io-async", "printhor-hwa-common/with-serial-port-2"] 23 | with-print-job = ["printhor-hwa-common/with-print-job"] 24 | with-spi = ["embedded-hal", "printhor-hwa-common/with-spi"] 25 | with-i2c = ["printhor-hwa-common/with-i2c"] 26 | with-hot-end = ["embedded-hal", "printhor-hwa-common/with-hot-end"] 27 | with-hot-bed = ["embedded-hal", "printhor-hwa-common/with-hot-bed"] 28 | with-motion = ["printhor-hwa-common/with-motion"] 29 | with-motion-broadcast = ["printhor-hwa-common/with-motion-broadcast"] 30 | with-motion-stepper = [] 31 | with-motion-cartessian-kinematics = ["printhor-hwa-common/with-motion-cartessian-kinematics"] 32 | with-motion-delta-kinematics = ["printhor-hwa-common/with-motion-delta-kinematics"] 33 | with-motion-core-xy-kinematics = ["printhor-hwa-common/with-motion-core-xy-kinematics"] 34 | with-motion-anthropomorphic-kinematics = ["printhor-hwa-common/with-motion-anthropomorphic-kinematics"] 35 | with-probe = ["embedded-hal", "printhor-hwa-common/with-probe"] 36 | with-sd-card = ["printhor-hwa-common/with-sd-card"] 37 | with-fan-layer = ["embedded-hal", "printhor-hwa-common/with-fan-layer"] 38 | with-fan-extra-1 = ["embedded-hal", "printhor-hwa-common/with-fan-extra-1"] 39 | with-laser = ["embedded-hal", "printhor-hwa-common/with-laser"] 40 | with-defmt = ["defmt", "printhor-hwa-common/with-defmt", "printhor-hwa-common/with-defmt", "printhor-hwa-utils/with-defmt", "esp-alloc/defmt", "embassy-executor/defmt"] 41 | with-trinamic = ["printhor-hwa-common/with-trinamic"] 42 | with-ps-on = ["printhor-hwa-common/with-ps-on"] 43 | 44 | with-e-axis = ["printhor-hwa-common/with-e-axis"] 45 | with-x-axis = ["printhor-hwa-common/with-x-axis"] 46 | with-y-axis = ["printhor-hwa-common/with-y-axis"] 47 | with-z-axis = ["printhor-hwa-common/with-z-axis"] 48 | with-a-axis = ["printhor-hwa-common/with-a-axis"] 49 | with-b-axis = ["printhor-hwa-common/with-b-axis"] 50 | with-c-axis = ["printhor-hwa-common/with-c-axis"] 51 | with-i-axis = ["printhor-hwa-common/with-i-axis"] 52 | with-j-axis = ["printhor-hwa-common/with-j-axis"] 53 | with-k-axis = ["printhor-hwa-common/with-k-axis"] 54 | with-u-axis = ["printhor-hwa-common/with-u-axis"] 55 | with-v-axis = ["printhor-hwa-common/with-v-axis"] 56 | with-w-axis = ["printhor-hwa-common/with-w-axis"] 57 | 58 | nightly = ["embassy-executor/nightly"] 59 | without-ringbuffer = [] 60 | verbose-timings = [] 61 | sd-card-uses-spi = ["printhor-hwa-common/sd-card-spi"] 62 | without-bootloader = [] 63 | 64 | float-point-f32-impl = ["printhor-hwa-common/float-point-f32-impl"] 65 | float-point-f64-impl = ["printhor-hwa-common/float-point-f64-impl"] 66 | fixed-point-128-impl = ["printhor-hwa-common/fixed-point-128-impl", "rust_decimal", "rust_decimal_macros"] 67 | 68 | [dependencies] 69 | defmt = { version = "0.3.10", optional = true, default-features = false, features=["alloc"] } 70 | esp-alloc = { version = "0.7.0" } 71 | esp-hal = { version = "1.0.0-beta.0", features = ["esp32s3", "unstable"]} 72 | panic-rtt-target = { version = "0.2.0", features = ["defmt"] } 73 | rtt-target = { version = "0.6.1", features = ["defmt"] } 74 | esp-hal-embassy = { version = "0.7.0", features = ["esp32s3"] } 75 | critical-section = { version = "1.2.0", default-features = false, features = []} 76 | 77 | #embassy-stm32 = { version = "0.2.0", default-features = false, features = ["[...]", "defmt", "rt", "memory-x", "exti", "unstable-pac", "time-driver-..."] } 78 | embassy-executor = { version = "0.7.0", default-features = false, features = ["task-arena-size-20480"] } 79 | embassy-sync = { version = "0.6.2", default-features = false, features = [] } 80 | embassy-time = { version = "0.4.0", default-features = false, features = ["tick-hz-1_000_000", "generic-queue-128"] } 81 | embassy-futures = { version = "0.1.1", default-features = false, features = [] } 82 | embassy-usb = { version = "0.4.0", default-features = false, features = [], optional = true } 83 | 84 | printhor-hwa-common = { version = "0.0.4", default-features = false, features = [], path = "../../printhor-hwa-common" } 85 | printhor-hwa-utils = { version = "0.0.4", default-features = false, features = [], path = "../../printhor-hwa-utils" } 86 | async-gcode = {version = "0.3.0", default-features = false, features=["parse-checksum", "parse-trailing-comment", "optional-value", "string-value"] } 87 | 88 | embedded-hal = { version = "1.0.0", default-features = false, optional = true } 89 | embedded-hal-02 = { package = "embedded-hal", version = "0.2.7", default-features = false, features = ["unproven"] } 90 | embedded-io-async = { version = "0.6.1", default-features = false, optional = true, features = [] } 91 | 92 | cfg-if = { version = "1.0.0"} 93 | const_env = { version = "0.1.2" } 94 | 95 | rust_decimal = { optional = true, version = "1.36.0", default-features = false, features = ["maths", "serde-with-str"] } 96 | rust_decimal_macros = { optional = true, default-features = false, version = "1.36.0" } 97 | 98 | [patch.crates-io] 99 | #async-gcode = { path = "../../libs/async-gcode" } 100 | #embassy-executor = { path = "../../libs/embassy/embassy-executor" } 101 | #embassy-sync = { path = "../../libs/embassy/embassy-sync" } 102 | #embassy-time = { path = "../../libs/embassy/embassy-time" } 103 | #embassy-futures = { path = "../../libs/embassy/embassy-futures" } 104 | #embassy-usb = { path = "../../libs/embassy/embassy-usb" } 105 | -------------------------------------------------------------------------------- /hwi-boards/printhor-hwi_esp32/README.md: -------------------------------------------------------------------------------- 1 | ![Minimum Rust: 1.85](https://img.shields.io/badge/Minimum%20Rust%20Version-1.85-green.svg) 2 | [![crates.io](https://img.shields.io/crates/v/prinThor.svg)](https://crates.io/crates/prinThor) 3 | [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) 4 | ![Discord Shield](https://discordapp.com/api/guilds/1169965662618259456/widget.png?style=shield) 5 | 6 |

Printhor: The highly reliable but not necessarily functional 3D printer firmware

7 | 8 |

If you are using this product or like the project, please this repository to show your support! 🤩

9 | 10 | # Overview 11 | 12 | Doc WIP: The Printhor HWI for Espressif ESP32-S3 WROOM 13 | 14 | 15 | -------------------------------------------------------------------------------- /hwi-boards/printhor-hwi_esp32/rust-toolchain.toml: -------------------------------------------------------------------------------- 1 | [toolchain] 2 | channel = "esp" 3 | -------------------------------------------------------------------------------- /hwi-boards/printhor-hwi_esp32/src/board/types.rs: -------------------------------------------------------------------------------- 1 | use printhor_hwa_common as hwa; 2 | 3 | //#region "General controllers lock types" 4 | pub type EventBusPubSubMutexType = hwa::AsyncNoopMutexType; 5 | pub type EventBusLockType = hwa::AsyncNoopMutexType; 6 | //#endregion 7 | 8 | //#region "HWI Shared controllers lock types" 9 | pub type WatchDogLockType = hwa::AsyncNoopMutexType; 10 | 11 | cfg_if::cfg_if! { 12 | if #[cfg(feature = "with-serial-usb")] { 13 | pub type SerialUsbTxLockType = hwa::AsyncNoopMutexType; 14 | pub type SerialUsbTxMutexStrategy = hwa::AsyncStandardStrategy; 15 | } 16 | } 17 | 18 | cfg_if::cfg_if! { 19 | if #[cfg(feature = "with-serial-port-1")] { 20 | pub type SerialPort1TxLockType = hwa::AsyncNoopMutexType; 21 | pub type SerialPort1TxMutexStrategy = hwa::AsyncStandardStrategy; 22 | } 23 | } 24 | 25 | cfg_if::cfg_if! { 26 | if #[cfg(feature = "with-serial-port-2")] { 27 | pub type SerialPort2TxLockType = hwa::AsyncNoopMutexType; 28 | pub type SerialPort2TxMutexStrategy = hwa::AsyncStandardStrategy; 29 | } 30 | } 31 | //#endregion 32 | 33 | //#region "General controllers locking strategy customization" 34 | pub type EventBusMutexStrategy = hwa::AsyncStandardStrategy>; 35 | pub type WatchDogMutexStrategy = hwa::AsyncStandardStrategy; 36 | //#endregion 37 | 38 | //#region "Shared controllers locking strategy customization" 39 | cfg_if::cfg_if! { 40 | if #[cfg(feature = "with-serial-port-1")] { 41 | pub type SerialPort1TxMutexStrategy = hwa::AsyncStandardStrategy; 42 | } 43 | } 44 | 45 | cfg_if::cfg_if! { 46 | if #[cfg(any(feature = "with-motion", feature = "with-hot-end", feature = "with-hot-bed"))] { 47 | pub type DeferChannelMutexType = hwa::AsyncNoopMutexType; 48 | } 49 | } 50 | 51 | cfg_if::cfg_if! { 52 | if #[cfg(feature = "with-motion")] { 53 | pub type StepActuatorMutexType = hwa::SyncCsMutexType; 54 | pub type MotionSignalMutexType = hwa::AsyncNoopMutexType; 55 | pub type MotionRingBufferMutexType = hwa::AsyncNoopMutexType; 56 | pub type MotionConfigMutexType = hwa::AsyncCsMutexType; 57 | pub type MotionStatusMutexType = hwa::AsyncNoopMutexType; 58 | pub type MotionDriverMutexType = hwa::AsyncNoopMutexType; 59 | 60 | pub type StepActuatorMuxtexStrategy = hwa::SyncStandardStrategy; 61 | } 62 | } 63 | 64 | cfg_if::cfg_if! { 65 | if #[cfg(all(feature = "with-motion", feature = "with-motion-broadcast"))] { 66 | pub type MotionBroadcastChannelMutexType = hwa::AsyncCsMutexType; 67 | 68 | pub type MotionSenderMutexType = I2cMutexType; 69 | 70 | pub type MotionSenderMutexStrategy = hwa::AsyncStandardStrategy; 71 | } 72 | } 73 | 74 | 75 | cfg_if::cfg_if! { 76 | if #[cfg(feature = "with-ps-on")] { 77 | pub type PSOnLockType = hwa::SyncNoopMutexType; 78 | pub type PSOnMutexStrategy = hwa::SyncStandardStrategy; 79 | } 80 | } 81 | 82 | cfg_if::cfg_if! { 83 | if #[cfg(feature = "with-spi")] { 84 | pub type Spi1MutexType = hwa::AsyncNoopMutexType; 85 | pub type Spi1MutexStrategyType = hwa::AsyncHoldableStrategy; 86 | } 87 | } 88 | 89 | cfg_if::cfg_if! { 90 | if #[cfg(feature = "with-i2c")] { 91 | pub type I2cMutexType = hwa::AsyncCsMutexType; 92 | pub type I2cMutexStrategyType = hwa::AsyncStandardStrategy; 93 | } 94 | } 95 | 96 | cfg_if::cfg_if! { 97 | if #[cfg(feature = "with-laser")] { 98 | pub type LaserMutexType = hwa::SyncCsMutexType; 99 | pub type LaserPwmMutexStrategy = hwa::SyncStandardStrategy; 100 | } 101 | } 102 | 103 | cfg_if::cfg_if! { 104 | if #[cfg(feature = "with-probe")] { 105 | pub type ProbeMutexType = hwa::SyncCsMutexType; 106 | pub type ProbePwmMutexStrategy = hwa::SyncStandardStrategy; 107 | } 108 | } 109 | 110 | cfg_if::cfg_if! { 111 | if #[cfg(any(feature="with-hot-end", feature = "with-hot-bed"))] { 112 | pub type HotEndHotBedAdcMutexType = hwa::SyncCsMutexType; 113 | pub type HotEndHotBedAdcMutexStrategy = hwa::AsyncStandardStrategy; 114 | 115 | pub type HotEndHotBedPwmMutexType = hwa::SyncNoopMutexType; 116 | pub type HotEndHotBedPwmMutexStrategy = hwa::SyncStandardStrategy; 117 | } 118 | } 119 | 120 | cfg_if::cfg_if! { 121 | if #[cfg(feature="with-hot-end")] { 122 | pub type HotEndAdcMutexStrategy = HotEndHotBedAdcMutexStrategy; 123 | pub type HotEndPwmMutexStrategy = HotEndHotBedPwmMutexStrategy; 124 | } 125 | } 126 | 127 | cfg_if::cfg_if! { 128 | if #[cfg(feature="with-hot-bed")] { 129 | pub type HotBedAdcMutexStrategy = HotEndHotBedAdcMutexStrategy; 130 | pub type HotBedPwmMutexStrategy = HotEndHotBedPwmMutexStrategy; 131 | } 132 | } 133 | //#endregion -------------------------------------------------------------------------------- /hwi-boards/printhor-hwi_esp32/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![allow(stable_features)] 3 | mod board; 4 | pub use board::Contract; -------------------------------------------------------------------------------- /hwi-boards/printhor-hwi_mks_robin_nano/.cargo/config.toml: -------------------------------------------------------------------------------- 1 | [env] 2 | 3 | ########################### 4 | ## Configuration - BEGIN ## 5 | ########################### 6 | 7 | ## Heater and bed configuration (thermistor based) ## 8 | 9 | # [Depends on the board] the pull-up resistance of hot-end sensor circuit in ohms 10 | HOT_END_THERM_PULL_UP_RESISTANCE = "4685.0" 11 | # Nominal resistance of hot-end NTC thermistor at 25ºC 12 | HOT_END_THERM_NOMINAL_RESISTANCE = "100000.0" 13 | # BETA value of hot-end NTC thermistor 14 | HOT_END_THERM_BETA = "3950.0" 15 | 16 | # [Depends on the board] the pull-up resistance of hot-bed sensor circuit in ohms 17 | HOT_BED_THERM_PULL_UP_RESISTANCE = "4685.0" 18 | # Nominal resistance of hot-bed NTC thermistor at 25ºC 19 | HOT_BED_THERM_NOMINAL_RESISTANCE = "100000.0" 20 | # BETA value of hot-bed NTC thermistor 21 | HOT_BED_THERM_BETA = "3950.0" 22 | 23 | ## Pure technical stuff ## 24 | 25 | DEFMT_LOG = "info" 26 | RUST_LOG = "warn" 27 | ASYNC_STD_THREAD_COUNT = "1" 28 | EMBASSY_USB_MAX_INTERFACE_COUNT = "2" 29 | 30 | ########################### 31 | ## Configuration - END ## 32 | ########################### 33 | 34 | [target.'cfg(all(any(board="mks_robin_nano"), target_arch="arm", target_os="none"))'] 35 | runner = "probe-rs run --chip STM32F407VETx --disable-progressbars --log-format {L}{s} --base-address 08007000 --connect-under-reset" 36 | 37 | [build] 38 | target = "thumbv7em-none-eabihf" 39 | 40 | [profile.dev.package."*"] 41 | codegen-units = 1 42 | incremental = false 43 | #opt-level = "s" # Set to "s" or even disable to play with GDB 44 | debug = 2 45 | debug-assertions = true 46 | overflow-checks = true 47 | 48 | [profile.release.package."*"] 49 | codegen-units = 1 50 | incremental = false 51 | opt-level = "z" 52 | debug = 2 53 | debug-assertions = true 54 | overflow-checks = true 55 | 56 | [profile.release-opt.package."*"] 57 | codegen-units = 1 58 | incremental = false 59 | opt-level = "z" 60 | debug = 0 61 | debug-assertions = false 62 | overflow-checks = false 63 | -------------------------------------------------------------------------------- /hwi-boards/printhor-hwi_mks_robin_nano/README.md: -------------------------------------------------------------------------------- 1 | ![Minimum Rust: 1.85](https://img.shields.io/badge/Minimum%20Rust%20Version-1.85-green.svg) 2 | [![crates.io](https://img.shields.io/crates/v/prinThor.svg)](https://crates.io/crates/prinThor) 3 | [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) 4 | ![Discord Shield](https://discordapp.com/api/guilds/1169965662618259456/widget.png?style=shield) 5 | 6 |

Printhor: The highly reliable but not necessarily functional 3D printer firmware

7 | 8 |

If you are using this product or like the project, please this repository to show your support! 🤩

9 | 10 | # Overview 11 | 12 | This board (https://www.makerbase.store/pages/mks-robin-nano-v3-1-intro) is still work in progress 13 | 14 | ### Binary image production (standard with defmt) 15 | 16 | The firmware.bin file ready to be uploaded to the SD can be produced with the following commandline: 17 | 18 | ```shell 19 | DEFMT_LOG=info cargo objcopy --release --no-default-features --features mks_robin_nano --target thumbv7em-none-eabihf --bin printhor -- -O binary firmware.bin 20 | ``` 21 | 22 | Firmware size if 200kB as of now with previous settings. 23 | 24 | ### Minimal-size binary image production 25 | 26 | ```shell 27 | DEFMT_LOG=off RUST_BACKTRACE=0 cargo objcopy --profile release-opt --no-default-features --features mks_robin_nano --target thumbv7em-none-eabihf --bin printhor -- -O binary firmware.bin 28 | ``` 29 | 30 | Firmware size if 164kB as of now with previous settings. 31 | 32 | ### Run with JLink/SWD device 33 | 34 | DEFMT_LOG=info RUST_BACKTRACE=1 RUSTFLAGS='--cfg board="mks_robin_nano"' cargo run --release --no-default-features --features mks_robin_nano --target thumbv7em-none-eabihf --bin printhor 35 | -------------------------------------------------------------------------------- /hwi-boards/printhor-hwi_mks_robin_nano/mks_robin_nano_3_1/memory.x: -------------------------------------------------------------------------------- 1 | /* MKS ROBIN NANO v3.1 - STM32F407VE */ 2 | MEMORY 3 | { 4 | FLASH : ORIGIN = 0x08000000 + 28K, LENGTH = 512K - 28K /* BANK_1_REGION_1 + BANK_1_REGION_2 + BANK_1_REGION_3 */ 5 | RAM : ORIGIN = 0x20000000, LENGTH = 128K /* SRAM + SRAM2 */ 6 | } -------------------------------------------------------------------------------- /hwi-boards/printhor-hwi_mks_robin_nano/mks_robin_nano_3_1/openocd.cfg: -------------------------------------------------------------------------------- 1 | source [find interface/stlink.cfg] 2 | 3 | source [find target/stm32f4x.cfg] 4 | 5 | -------------------------------------------------------------------------------- /hwi-boards/printhor-hwi_mks_robin_nano/src/board_stm32f4/types.rs: -------------------------------------------------------------------------------- 1 | use printhor_hwa_common as hwa; 2 | 3 | //#region "General controllers lock types" 4 | pub type EventBusPubSubMutexType = hwa::AsyncNoopMutexType; 5 | pub type EventBusLockType = hwa::AsyncNoopMutexType; 6 | //#endregion 7 | 8 | //#region "HWI Shared controllers lock types" 9 | pub type WatchDogLockType = hwa::AsyncNoopMutexType; 10 | //#endregion 11 | 12 | //#region "General controllers locking strategy customization" 13 | pub type EventBusMutexStrategy = hwa::AsyncStandardStrategy< 14 | EventBusLockType, 15 | hwa::EventBusChannelController, 16 | >; 17 | pub type WatchDogMutexStrategy = 18 | hwa::AsyncStandardStrategy; 19 | //#endregion 20 | 21 | //#region "Shared controllers locking strategy customization" 22 | 23 | cfg_if::cfg_if! { 24 | if #[cfg(feature = "with-serial-usb")] { 25 | pub type SerialUsbTxLockType = hwa::AsyncNoopMutexType; 26 | pub type SerialUsbTxMutexStrategy = hwa::AsyncStandardStrategy; 27 | } 28 | } 29 | 30 | cfg_if::cfg_if! { 31 | if #[cfg(feature = "with-serial-port-1")] { 32 | pub type SerialPort1TxLockType = hwa::AsyncNoopMutexType; 33 | pub type SerialPort1TxMutexStrategy = hwa::AsyncStandardStrategy; 34 | } 35 | } 36 | 37 | cfg_if::cfg_if! { 38 | if #[cfg(feature = "with-serial-port-2")] { 39 | pub type SerialPort2TxLockType = hwa::AsyncNoopMutexType; 40 | pub type SerialPort2TxMutexStrategy = hwa::AsyncStandardStrategy; 41 | } 42 | } 43 | 44 | cfg_if::cfg_if! { 45 | if #[cfg(any(feature = "with-motion", feature = "with-hot-end", feature = "with-hot-bed"))] { 46 | pub type DeferChannelMutexType = hwa::AsyncNoopMutexType; 47 | } 48 | } 49 | 50 | cfg_if::cfg_if! { 51 | if #[cfg(feature = "with-motion")] { 52 | pub type StepActuatorMutexType = hwa::SyncCsMutexType; 53 | pub type MotionSignalMutexType = hwa::AsyncNoopMutexType; 54 | pub type MotionRingBufferMutexType = hwa::AsyncNoopMutexType; 55 | pub type MotionConfigMutexType = hwa::AsyncCsMutexType; 56 | pub type MotionStatusMutexType = hwa::AsyncNoopMutexType; 57 | pub type MotionDriverMutexType = hwa::AsyncNoopMutexType; 58 | 59 | pub type StepActuatorMuxtexStrategy = hwa::SyncStandardStrategy; 60 | } 61 | } 62 | 63 | cfg_if::cfg_if! { 64 | if #[cfg(all(feature = "with-motion", feature = "with-motion-broadcast"))] { 65 | pub type MotionBroadcastChannelMutexType = hwa::AsyncCsMutexType; 66 | 67 | pub type MotionSenderMutexType = I2cMutexType; 68 | 69 | pub type MotionSenderMutexStrategy = hwa::AsyncStandardStrategy; 70 | } 71 | } 72 | 73 | cfg_if::cfg_if! { 74 | if #[cfg(feature = "with-ps-on")] { 75 | pub type PSOnLockType = hwa::SyncNoopMutexType; 76 | pub type PSOnMutexStrategy = hwa::SyncStandardStrategy; 77 | } 78 | } 79 | 80 | cfg_if::cfg_if! { 81 | if #[cfg(feature = "with-spi")] { 82 | pub type Spi1MutexType = hwa::AsyncNoopMutexType; 83 | pub type Spi1MutexStrategyType = hwa::AsyncHoldableStrategy; 84 | //pub type Spi2MutexType = hwa::AsyncNoopMutexType; 85 | //pub type Spi2MutexStrategyType = hwa::AsyncHoldableStrategy; 86 | pub type Spi3MutexType = hwa::AsyncNoopMutexType; 87 | pub type Spi3MutexStrategyType = hwa::AsyncHoldableStrategy; 88 | 89 | } 90 | } 91 | 92 | cfg_if::cfg_if! { 93 | if #[cfg(feature = "with-sd-card")] { 94 | pub type SDCardBlockDevice = hwa::sd_card_spi::SPIAdapter< 95 | Spi3MutexStrategyType, 96 | super::device::SpiCardCSPin, 97 | >; 98 | } 99 | } 100 | 101 | cfg_if::cfg_if! { 102 | if #[cfg(feature = "with-print-job")] { 103 | pub type PrinterControllerSignalMutexType = hwa::AsyncNoopMutexType; 104 | } 105 | } 106 | 107 | cfg_if::cfg_if! { 108 | if #[cfg(feature = "with-i2c")] { 109 | pub type I2cMutexType = hwa::AsyncCsMutexType; 110 | pub type I2cMutexStrategyType = hwa::AsyncStandardStrategy; 111 | } 112 | } 113 | 114 | cfg_if::cfg_if! { 115 | if #[cfg(feature = "with-laser")] { 116 | pub type LaserMutexType = hwa::SyncCsMutexType; 117 | pub type LaserPwmMutexStrategy = hwa::SyncStandardStrategy; 118 | } 119 | } 120 | 121 | cfg_if::cfg_if! { 122 | if #[cfg(feature = "with-probe")] { 123 | pub type ProbeMutexType = hwa::SyncCsMutexType; 124 | pub type ProbePwmMutexStrategy = hwa::SyncStandardStrategy; 125 | } 126 | } 127 | 128 | cfg_if::cfg_if! { 129 | if #[cfg(any(feature="with-hot-end", feature = "with-hot-bed"))] { 130 | pub type HotEndHotBedAdcMutexType = hwa::SyncCsMutexType; 131 | pub type HotEndHotBedAdcMutexStrategy = hwa::AsyncStandardStrategy; 132 | 133 | pub type HotEndHotBedPwmMutexType = hwa::SyncNoopMutexType; 134 | pub type HotEndHotBedPwmMutexStrategy = hwa::SyncStandardStrategy; 135 | } 136 | } 137 | 138 | cfg_if::cfg_if! { 139 | if #[cfg(feature="with-hot-end")] { 140 | pub type HotEndAdcMutexStrategy = HotEndHotBedAdcMutexStrategy; 141 | pub type HotEndPwmMutexStrategy = HotEndHotBedPwmMutexStrategy; 142 | } 143 | } 144 | 145 | cfg_if::cfg_if! { 146 | if #[cfg(feature="with-hot-bed")] { 147 | pub type HotBedAdcMutexStrategy = HotEndHotBedAdcMutexStrategy; 148 | pub type HotBedPwmMutexStrategy = HotEndHotBedPwmMutexStrategy; 149 | } 150 | } 151 | //#endregion 152 | -------------------------------------------------------------------------------- /hwi-boards/printhor-hwi_mks_robin_nano/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![allow(stable_features)] 3 | extern crate alloc; 4 | 5 | mod board_stm32f4; 6 | pub use board::Contract; 7 | use board_stm32f4 as board; 8 | -------------------------------------------------------------------------------- /hwi-boards/printhor-hwi_native/.cargo/config.toml: -------------------------------------------------------------------------------- 1 | 2 | [env] 3 | 4 | [profile.dev.package."*"] 5 | codegen-units = 1 6 | incremental = false 7 | #opt-level = "s" # Set to "s" or even disable to play with GDB 8 | debug = 2 9 | debug-assertions = true 10 | overflow-checks = true 11 | 12 | [profile.release.package."*"] 13 | codegen-units = 1 14 | incremental = false 15 | opt-level = "z" 16 | debug = 2 17 | debug-assertions = true 18 | overflow-checks = true 19 | 20 | [profile.release-opt.package."*"] 21 | codegen-units = 1 22 | incremental = false 23 | opt-level = "z" 24 | debug = 0 25 | debug-assertions = false 26 | overflow-checks = false -------------------------------------------------------------------------------- /hwi-boards/printhor-hwi_native/README.md: -------------------------------------------------------------------------------- 1 | ![Minimum Rust: 1.85](https://img.shields.io/badge/Minimum%20Rust%20Version-1.85-green.svg) 2 | [![crates.io](https://img.shields.io/crates/v/printhor-hwi_native.svg)](https://crates.io/crates/printhor-hwi_native) 3 | [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) 4 | ![Discord Shield](https://discordapp.com/api/guilds/1169965662618259456/widget.png?style=shield) 5 | 6 |

Printhor: The highly reliable but not necessarily functional 3D printer firmware

7 | 8 |

If you are using this product or like the project, please this repository to show your support! 🤩

9 | 10 | # Overview 11 | 12 | The framework with a set of mocked peripherals (most of them without any logic). 13 | Provides a commandline GCode prompt on standard input 14 | 15 | __Note__: A SDCard image in ./data/ is required to be open if sd-card feature is enabled in native simulator :) 16 | 17 | ```shell 18 | RUST_LOG=info cargo run --bin printhor 19 | ``` 20 | 21 | 22 | -------------------------------------------------------------------------------- /hwi-boards/printhor-hwi_native/src/board/mocked_peripherals/mocked_adc.rs: -------------------------------------------------------------------------------- 1 | use printhor_hwa_common as hwa; 2 | use embassy_time::Instant; 3 | use crate::board::mocked_peripherals::MockedIOPin; 4 | 5 | pub struct MockedAdc { 6 | _peri: PERI, 7 | // For simulation: Time since expected read was required. 8 | time_since: Instant, 9 | // For simulation: Target expected read. 10 | expected_read: u16, 11 | default_sample: u16, 12 | } 13 | impl MockedAdc { 14 | pub(crate) fn new(_peri: PERI, default_sample: u16) -> Self { 15 | Self { 16 | _peri, 17 | time_since: Instant::now(), 18 | expected_read: 4000, 19 | default_sample, 20 | } 21 | } 22 | } 23 | 24 | impl hwa::traits::UnifiedAdc16 for MockedAdc { 25 | type VRefPin = (); 26 | type SamplePin = MockedIOPin; 27 | async fn read_vref(&mut self) -> Result 28 | { 29 | Ok(self.default_sample) 30 | } 31 | 32 | async fn read_adc(&mut self, _pin: &mut Self::SamplePin) -> u16 { 33 | const EXPECTED_TIME: f32 = 10.0f32; 34 | let elapsed = self.time_since.elapsed().as_secs() as f32; 35 | // Add some noise as measurement * cos( w * t) 36 | // for instance: temp_deviation: measurement, w: frequency 37 | let noise = 5.0f32 * (0.1f32 * elapsed).cos(); 38 | let mut simulated_measure = self.expected_read as f32; 39 | if elapsed < EXPECTED_TIME { 40 | // Simulated linear ramp to reach temp 41 | simulated_measure *= (elapsed / EXPECTED_TIME) as f32; 42 | } 43 | (5.0f32 + simulated_measure + noise) as u16 44 | } 45 | } -------------------------------------------------------------------------------- /hwi-boards/printhor-hwi_native/src/board/mocked_peripherals/mocked_display.rs: -------------------------------------------------------------------------------- 1 | // TODO: Intense refactor and cleanup 2 | //use std::mem::MaybeUninit; 3 | 4 | //use embedded_graphics_core::geometry::{Dimensions, Point}; 5 | use embedded_graphics_core::geometry::Size; 6 | #[allow(unused)] 7 | use embedded_graphics_simulator::{OutputSettingsBuilder, SimulatorDisplay, SimulatorEvent, Window}; 8 | use embedded_graphics_core::pixelcolor::{Rgb565}; 9 | //use embedded_graphics_core::Drawable; 10 | //use embedded_graphics::mono_font::{mapping::StrGlyphMapping, DecorationDimensions, MonoFont, MonoTextStyle}; 11 | //use embedded_graphics::mono_font::ascii::FONT_6X10; 12 | //use embedded_graphics::text::Text; 13 | //use embedded_graphics_core::draw_target::DrawTarget; 14 | //use embedded_graphics_core::primitives::Rectangle; 15 | 16 | #[cfg(feature = "with-lvgl")] 17 | use lvgl::core::{Display, Lvgl, ObjExt, Screen, Ticks}; 18 | //use printhor_hwa_common::{EventBusRef, EventBusSubscriber, EventFlags, DisplayScreenUI}; 19 | use printhor_hwa_common::DisplayScreenUI; 20 | 21 | //use embedded_graphics_core::Pixel; 22 | 23 | const WIDTH: u32 = 240; 24 | const HEIGHT: u32 = 320; 25 | #[cfg(feature = "lvgl")] 26 | const LVGL_BUFFER_LEN: usize = 16usize * WIDTH as usize * 2usize; 27 | 28 | pub type PixelColor = Rgb565; 29 | 30 | pub struct SimulatorDisplayDevice { 31 | 32 | } 33 | 34 | impl SimulatorDisplayDevice { 35 | pub fn new() -> Self { 36 | Self {} 37 | } 38 | } 39 | 40 | pub struct SimulatorDisplayScreen 41 | where 42 | UI: DisplayScreenUI, 43 | { 44 | _device: SimulatorDisplayDevice, 45 | raw_display: SimulatorDisplay, 46 | main_ui: UI, 47 | simulator_window: Window, 48 | } 49 | 50 | impl SimulatorDisplayScreen 51 | { 52 | pub async fn new(_device: SimulatorDisplayDevice, main_ui: UI) -> Self { 53 | let output_settings = OutputSettingsBuilder::new().build(); 54 | let raw_display = SimulatorDisplay::new(Size::new(WIDTH, HEIGHT)); 55 | Self { 56 | _device, 57 | main_ui, 58 | raw_display: raw_display, 59 | simulator_window: Window::new("PrinThor TFT", &output_settings), 60 | } 61 | } 62 | 63 | pub async fn update(&mut self) { 64 | #[cfg(feature = "with-lvgl")] 65 | self.main_ui.context().as_mut().unwrap().refresh().await; 66 | #[cfg(not(feature = "with-lvgl"))] 67 | self.main_ui.refresh(&mut self.raw_display).await; 68 | #[cfg(feature = "with-lvgl")] 69 | self.lvgl.run_tasks(); 70 | #[cfg(feature = "with-lvgl")] 71 | self.simulator_window.update(&self.lvgl_display); 72 | #[cfg(not(feature = "with-lvgl"))] 73 | self.simulator_window.update(&self.raw_display); 74 | 75 | self.simulator_window.events().for_each(|_e| { 76 | //println!("Event! {:?}", _e) 77 | }); 78 | } 79 | 80 | pub async fn retain(&mut self) { 81 | 82 | } 83 | 84 | pub async fn release(&mut self) { 85 | 86 | } 87 | } 88 | 89 | /* 90 | 91 | TODO: Obsolete code pending to refactor/readapt 92 | 93 | pub struct TFTDisplay { 94 | #[cfg(feature = "with-lvgl")] 95 | lvgl: Lvgl, 96 | simulator_window: Window, 97 | #[cfg(feature = "with-lvgl")] 98 | lvgl_display: Display>, 99 | #[cfg(not(feature = "with-lvgl"))] 100 | raw_display: SimulatorDisplay, 101 | #[cfg(feature = "with-lvgl")] 102 | main_ui: Screen, 103 | #[cfg(not(feature = "with-lvgl"))] 104 | main_ui: DirectUI, 105 | } 106 | 107 | impl TFTDisplay { 108 | pub async fn new(_display_dev: hwa::display::DisplayDevice, event_bus: EventBusRef) -> Self { 109 | 110 | info!("BUFF prec size:{}", core::mem::size_of::() * LVGL_BUFFER_LEN); 111 | 112 | // Display init with its draw buffer 113 | static mut DRAW_BUFFER: [MaybeUninit; LVGL_BUFFER_LEN] = 114 | [MaybeUninit::::uninit(); LVGL_BUFFER_LEN]; 115 | 116 | let output_settings = OutputSettingsBuilder::new().build(); 117 | 118 | #[cfg(feature = "with-lvgl")] 119 | let mut lvgl = Lvgl::new(); 120 | #[cfg(feature = "with-lvgl")] 121 | lvgl.register_logger(|s| info!("LVGL: {}", s)); 122 | 123 | let mut raw_display = SimulatorDisplay::new(Size::new(WIDTH, HEIGHT)); 124 | 125 | #[cfg(feature = "with-lvgl")] 126 | let mut lvgl_display = Display::new(&lvgl, raw_display, unsafe { &mut DRAW_BUFFER }); 127 | 128 | #[cfg(feature = "with-lvgl")] 129 | let mut main_ui = lvgl_ui::new_screen(&lvgl_display, |screen| { 130 | lvgl_ui::MotionScreen::new(screen) 131 | }); 132 | 133 | #[cfg(feature = "with-lvgl")] 134 | lvgl_display.load_screen(&mut main_ui); 135 | 136 | Self { 137 | #[cfg(feature = "with-lvgl")] 138 | lvgl, 139 | simulator_window: Window::new("PrinThor TFT", &output_settings), 140 | #[cfg(feature = "with-lvgl")] 141 | lvgl_display, 142 | #[cfg(not(feature = "with-lvgl"))] 143 | raw_display, 144 | #[cfg(feature = "with-lvgl")] 145 | main_ui, 146 | #[cfg(not(feature = "with-lvgl"))] 147 | main_ui: DirectUI::new(event_bus).await, 148 | } 149 | } 150 | #[allow(unused)] 151 | pub async fn init(&self) { 152 | 153 | } 154 | 155 | #[cfg(feature = "with-lvgl")] 156 | pub fn ticks(&self) -> Ticks { 157 | self.lvgl.ticks() 158 | } 159 | 160 | pub async fn update(&mut self) { 161 | 162 | } 163 | } 164 | */ -------------------------------------------------------------------------------- /hwi-boards/printhor-hwi_native/src/board/mocked_peripherals/mocked_i2c.rs: -------------------------------------------------------------------------------- 1 | use printhor_hwa_common as hwa; 2 | use embedded_hal_1::i2c::{ErrorKind, ErrorType, Operation}; 3 | use pwm_pca9685; 4 | use printhor_hwa_common::math::{CoordSel, Real}; 5 | 6 | pub struct I2CDevice(); 7 | 8 | pub struct MockedI2c { 9 | pwm: pwm_pca9685::Pca9685, 10 | state: [pwm_pca9685::ChannelOnOffControl; 16], 11 | } 12 | 13 | impl MockedI2c { 14 | pub async fn new() -> Self { 15 | 16 | let dev = I2CDevice(); 17 | 18 | let address = pwm_pca9685::Address::from((false, false, false, false, false, false)); 19 | let mut pwm = pwm_pca9685::Pca9685::new(dev, address).unwrap(); 20 | 21 | pwm.set_prescale(100).unwrap(); 22 | pwm.enable().unwrap(); 23 | hwa::debug!("[mocked_i2c] setting..."); 24 | 25 | // period = 20 ms = 4095 count 26 | // dmin(-90) = 1ms = 204.75 count 27 | // dmax(+90) = 2ms = 409.50 count 28 | // dmed(0) = 1.5ms = (204.75 + 409.50)/2 = 307.125 count 29 | // count_per_deg = (409.50 - 204.75) / 180 = 1.13750000 30 | 31 | // count(angle) = round(307.125 + (1.13750000 * angle)) 32 | 33 | let mut instance = Self { 34 | pwm, 35 | state: [pwm_pca9685::ChannelOnOffControl{ 36 | on: 0, 37 | off: 0, 38 | full_on: false, 39 | full_off: false, 40 | }; 16], 41 | }; 42 | 43 | for _servo in CoordSel::all_axis().iter() { 44 | instance.set_angle(_servo, &hwa::math::ZERO); 45 | } 46 | instance.apply().await; 47 | hwa::debug!("[mocked_i2c] setting done"); 48 | instance 49 | } 50 | 51 | pub fn set_angle(&mut self, axis: hwa::math::CoordSel, angle: &hwa::math::Real) -> bool { 52 | let pwm = ( 53 | (Real::from_f32(307.125f32) + (Real::from_f32(1.1375f32) * (*angle))).round().to_i32().unwrap() 54 | ).max(205).min(409) as u16; 55 | let index = axis.index(); 56 | if index < 16 { 57 | if self.state[index].off != pwm { 58 | #[cfg(feature = "debug-motion-broadcast")] 59 | hwa::debug!("[task_motion_broadcast] set PWM [{:?}] [{:?}] = {:?} -> {:?}", angle, index, self.state[index].off, pwm ); 60 | self.state[index].off = pwm; 61 | true 62 | } 63 | else { 64 | false 65 | } 66 | } 67 | else { 68 | false 69 | } 70 | } 71 | 72 | pub async fn apply(&mut self) -> () { 73 | self.pwm.set_all_channels(&self.state).unwrap(); 74 | } 75 | } 76 | 77 | #[derive(Debug)] 78 | pub enum I2CError { 79 | 80 | } 81 | 82 | impl embedded_hal_1::i2c::Error for I2CError { 83 | fn kind(&self) -> ErrorKind { 84 | ErrorKind::Other 85 | } 86 | } 87 | 88 | impl ErrorType for I2CDevice { type Error = I2CError; } 89 | 90 | impl embedded_hal_1::i2c::I2c for I2CDevice 91 | where A: embedded_hal_1::i2c::AddressMode + core::fmt::Debug 92 | { 93 | fn transaction(&mut self, _address: A, _operations: &mut [Operation<'_>]) -> Result<(), Self::Error> { 94 | hwa::debug!("Wrote something at {:?}", _address); 95 | Ok(()) 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /hwi-boards/printhor-hwi_native/src/board/mocked_peripherals/mocked_pin.rs: -------------------------------------------------------------------------------- 1 | pub(crate) type PinsCell = std::sync::Mutex; 2 | 3 | pub type PinStateRef = &'static PinsCell; 4 | 5 | const NUM_PINS: usize = 100usize; 6 | /// Pin state for state persistence, inter-connection, automation and monitoring 7 | /// Purpose: simulation only 8 | pub(crate) struct PinState { 9 | digital: [bool; NUM_PINS], 10 | } 11 | impl PinState { 12 | pub(crate) const fn new() -> Self { 13 | Self { 14 | digital: [false; NUM_PINS] 15 | } 16 | } 17 | 18 | pub(crate) fn set(&mut self, id: u8, state: bool) { 19 | self.digital[id as usize] = state 20 | } 21 | 22 | pub(crate) fn get(&self, id: u8) -> bool { 23 | self.digital[id as usize] 24 | } 25 | } 26 | 27 | pub struct MockedIOPin { 28 | id: u8, 29 | global_state: PinStateRef, 30 | } 31 | 32 | #[allow(unused)] 33 | impl MockedIOPin { 34 | pub(crate) const fn new(id: u8, global_state: PinStateRef) -> Self { 35 | Self { id, global_state } 36 | } 37 | 38 | pub fn set_high(&mut self) { 39 | loop { 40 | if let Ok(mut mg) = self.global_state.try_lock() { 41 | return mg.set(self.id, true); 42 | } 43 | } 44 | } 45 | 46 | pub fn set_low(&mut self) { 47 | loop { 48 | if let Ok(mut mg) = self.global_state.try_lock() { 49 | return mg.set(self.id, false); 50 | } 51 | } 52 | } 53 | 54 | pub fn toggle(&mut self) { 55 | let state = self.is_high(); 56 | loop { 57 | if let Ok(mut mg) = self.global_state.try_lock() { 58 | return mg.set(self.id, !state); 59 | } 60 | } 61 | } 62 | 63 | #[allow(unused)] 64 | pub fn is_low(&self) -> bool { 65 | loop { 66 | if let Ok(mg) = self.global_state.try_lock() { 67 | return mg.get(self.id) == false 68 | } 69 | } 70 | } 71 | 72 | #[allow(unused)] 73 | pub fn is_high(&self) -> bool { 74 | loop { 75 | if let Ok(mg) = self.global_state.try_lock() { 76 | return mg.get(self.id) == true 77 | } 78 | } 79 | } 80 | } 81 | 82 | 83 | cfg_if::cfg_if! { 84 | if #[cfg(feature = "with-spi")] { 85 | use embedded_hal_0::digital::v2; 86 | impl v2::OutputPin for MockedIOPin { 87 | type Error = core::convert::Infallible; 88 | fn set_low(&mut self) -> Result<(), Self::Error> { 89 | Ok(()) 90 | } 91 | 92 | fn set_high(&mut self) -> Result<(), Self::Error> { 93 | Ok(()) 94 | } 95 | } 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /hwi-boards/printhor-hwi_native/src/board/mocked_peripherals/mocked_pwm.rs: -------------------------------------------------------------------------------- 1 | use printhor_hwa_common as hwa; 2 | use std::collections::HashMap; 3 | use super::mocked_pin::MockedIOPin; 4 | use super::mocked_pin::PinStateRef; 5 | 6 | pub struct MockedPwm { 7 | #[allow(unused)] 8 | p: MockedIOPin, 9 | // Allow state by channel to share pwm in the same way the MCU does 10 | duty_map: HashMap::Duty>, 11 | 12 | } 13 | impl MockedPwm { 14 | #[allow(unused)] 15 | pub(crate) fn new(id: u8, pin_state: PinStateRef) -> Self { 16 | Self { 17 | p: MockedIOPin::new(id, pin_state), 18 | duty_map: HashMap::new(), 19 | } 20 | } 21 | } 22 | 23 | impl hwa::traits::Pwm for MockedPwm { 24 | type Channel = u8; 25 | type Time = u16; 26 | type Duty = u32; 27 | fn disable(&mut self, channel: ::Channel) { 28 | self.duty_map.insert(channel, 0); 29 | } 30 | fn enable(&mut self, _: ::Channel) { 31 | 32 | } 33 | fn get_period(&self) -> ::Time { 0u16 } 34 | fn get_duty(&self, channel: ::Channel) -> ::Duty { 35 | let duty_of_channel = *self.duty_map.get(&channel).unwrap_or(&0); 36 | hwa::trace!("get_duty(channel: {}) = {}", channel, duty_of_channel); 37 | duty_of_channel 38 | } 39 | fn get_max_duty(&self) -> ::Duty { 16384u32 } 40 | fn set_duty(&mut self, channel: ::Channel, duty: ::Duty) { 41 | let clamped_duty = duty.min(self.get_max_duty()); 42 | hwa::trace!("set_duty: curr: {}, max: {}, next: {}", 43 | *self.duty_map.get(&channel).unwrap_or(&0), 44 | self.get_max_duty(), 45 | clamped_duty, 46 | ); 47 | self.duty_map.insert(channel, clamped_duty); 48 | } 49 | fn set_period

(&mut self, _: P) where P: Into { } 50 | } -------------------------------------------------------------------------------- /hwi-boards/printhor-hwi_native/src/board/mocked_peripherals/mocked_sd_card.rs: -------------------------------------------------------------------------------- 1 | use printhor_hwa_common as hwa; 2 | use std::cell::RefCell; 3 | use std::fs::File; 4 | use std::io::{Read, Seek, SeekFrom, Write}; 5 | use std::path::Path; 6 | use hwa::traits::{AsyncSDBlockDevice, SDBlockDevice}; 7 | use embedded_sdmmc::{Block, BlockCount, BlockIdx}; 8 | 9 | #[derive(Debug, Clone)] 10 | enum Error { 11 | #[allow(unused)] 12 | Filesystem(embedded_sdmmc::Error), 13 | #[allow(unused)] 14 | Disk(embedded_sdmmc::SdCardError), 15 | } 16 | 17 | impl From> for Error { 18 | fn from(value: embedded_sdmmc::Error) -> Error { 19 | Error::Filesystem(value) 20 | } 21 | } 22 | 23 | impl From for Error { 24 | fn from(value: embedded_sdmmc::SdCardError) -> Error { 25 | Error::Disk(value) 26 | } 27 | } 28 | 29 | pub struct MockedSDCardBlockDevice { 30 | file: RefCell, 31 | } 32 | 33 | impl MockedSDCardBlockDevice { 34 | pub fn new

(image_path: P) -> Result 35 | where P: AsRef, 36 | { 37 | Ok(MockedSDCardBlockDevice { 38 | file: RefCell::new(File::open(image_path)?), 39 | }) 40 | } 41 | } 42 | 43 | impl AsyncSDBlockDevice for MockedSDCardBlockDevice { 44 | async fn do_retain(&self) -> Result<(), ()> { 45 | Ok(()) 46 | } 47 | fn do_release(&self) -> Result<(), ()> { 48 | Ok(()) 49 | } 50 | } 51 | 52 | impl SDBlockDevice for MockedSDCardBlockDevice { 53 | type Error = std::io::Error; 54 | 55 | fn read( 56 | &self, 57 | blocks: &mut [Block], 58 | start_block_idx: BlockIdx, 59 | _reason: &str, 60 | ) -> Result<(), Self::Error> { 61 | self.file 62 | .borrow_mut() 63 | .seek(SeekFrom::Start(start_block_idx.into_bytes()))?; 64 | for block in blocks.iter_mut() { 65 | self.file.borrow_mut().read_exact(&mut block.contents)?; 66 | } 67 | Ok(()) 68 | } 69 | 70 | fn write(&self, blocks: &[Block], start_block_idx: BlockIdx) -> Result<(), Self::Error> { 71 | self.file 72 | .borrow_mut() 73 | .seek(SeekFrom::Start(start_block_idx.into_bytes()))?; 74 | for block in blocks.iter() { 75 | self.file.borrow_mut().write_all(&block.contents)?; 76 | } 77 | Ok(()) 78 | } 79 | 80 | fn num_blocks(&self) -> Result { 81 | let num_blocks = self.file.borrow().metadata().unwrap().len() / 512; 82 | Ok(BlockCount(num_blocks as u32)) 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /hwi-boards/printhor-hwi_native/src/board/mocked_peripherals/mocked_spi.rs: -------------------------------------------------------------------------------- 1 | use embedded_hal_0::blocking::spi; 2 | 3 | pub struct MockedSpi { 4 | } 5 | 6 | impl MockedSpi { 7 | pub fn new() -> Self { 8 | Self {} 9 | } 10 | 11 | #[allow(unused)] 12 | pub fn blocking_transfer_in_place(&mut self, _buff: &mut [T]) {} 13 | 14 | #[allow(unused)] 15 | pub fn blocking_write(&mut self, _buff: &[T]) {} 16 | } 17 | 18 | impl spi::Transfer for MockedSpi { 19 | type Error = core::convert::Infallible; 20 | fn transfer<'w>(&mut self, words: &'w mut [u8]) -> Result<&'w [u8], Self::Error> { 21 | Ok(words) 22 | } 23 | } 24 | 25 | impl spi::Write for MockedSpi { 26 | type Error = core::convert::Infallible; 27 | fn write<'w>(&mut self, _words: &'w [u8]) -> Result<(), Self::Error> { 28 | Ok(()) 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /hwi-boards/printhor-hwi_native/src/board/mocked_peripherals/mocked_uart.rs: -------------------------------------------------------------------------------- 1 | use printhor_hwa_common as hwa; 2 | 3 | use std::io::{stdout, Write}; 4 | use embassy_executor::SendSpawner; 5 | use embassy_sync::pipe::Pipe; 6 | use embassy_time::{Duration, with_timeout}; 7 | use async_std::io::ReadExt; 8 | use printhor_hwa_common::HwiContract; 9 | use crate::board::TERMINATION; 10 | 11 | pub static SERIAL_PIPE: Pipe = Pipe::::new(); 12 | #[embassy_executor::task(pool_size=1)] 13 | pub async fn task_mocked_uart() { 14 | 15 | hwa::info!("[task_mocked_uart] starting"); 16 | 17 | // byte-to-byte reading is required for stdin for it to work unbuffered with simple I/O management. 18 | // Another solution could be leveraging a wrapper/crate on top of native select() with the proper ioctl on stdin, but is not worthy in this case. 19 | // Unbuffered I/O requirement for piping simulator with Universal Gcode Sender and others by socat. 20 | 21 | let mut stream = async_std::io::stdin(); 22 | let mut buf = [0u8; 1]; 23 | let mut error_reading = false; 24 | 25 | loop { 26 | match with_timeout( 27 | Duration::from_secs(60), 28 | stream.read_exact(&mut buf) 29 | ).await { 30 | Err(_) => { 31 | if TERMINATION.signaled() { 32 | hwa::info!("[task_mocked_uart] Ending gracefully"); 33 | return (); 34 | } 35 | } 36 | Ok(Ok(_)) => { 37 | error_reading = false; // Recovered 38 | SERIAL_PIPE.write(&buf[..1]).await; 39 | } 40 | Ok(Err(_e)) => { // Error reading from stdin: Closed or temporary unavailable 41 | if !error_reading { 42 | hwa::trace!("[task_mocked_uart] Error reading from stdin {:?}", _e); 43 | error_reading = true; 44 | if TERMINATION.signaled() { 45 | hwa::info!("[task_mocked_uart] Ending gracefully"); 46 | return (); 47 | } 48 | } 49 | else { 50 | // Could happen. For instance in CI. 51 | // In this case we will wait to avoid respawning fast and keep trying in case it 52 | // can be recovered (practically improbable) 53 | embassy_time::Timer::after(Duration::from_secs(10)).await; 54 | } 55 | } 56 | } 57 | } 58 | } 59 | 60 | pub struct MockedUart { 61 | spawner: SendSpawner, 62 | } 63 | 64 | impl MockedUart { 65 | pub(crate) fn new(spawner: SendSpawner) -> Self { 66 | 67 | spawner.spawn(task_mocked_uart()).unwrap(); 68 | 69 | Self { 70 | spawner, 71 | } 72 | } 73 | 74 | pub(crate) fn split(&self) -> (MockedUartTx, MockedUartRx) { 75 | (MockedUartTx::new(), MockedUartRx::new(self.spawner)) 76 | } 77 | } 78 | 79 | pub struct MockedUartRx { 80 | } 81 | 82 | impl MockedUartRx { 83 | pub(crate) fn new(_spawner: SendSpawner) -> Self { 84 | Self { 85 | } 86 | } 87 | pub async fn read_until_idle(&mut self, buffer: &mut [u8]) -> Result { 88 | hwa::trace!("Reading from pipe"); 89 | Ok(SERIAL_PIPE.read(buffer).await) 90 | } 91 | } 92 | 93 | pub struct MockedUartTx { 94 | } 95 | impl MockedUartTx { 96 | pub(crate) const fn new() -> Self { 97 | Self { 98 | } 99 | } 100 | 101 | pub async fn write_packet(&mut self, b: &[u8]) { 102 | self.wrapped_write(b).await; 103 | } 104 | pub async fn wrapped_flush(&mut self) { 105 | } 106 | pub async fn wrapped_write(&mut self, b: &[u8]) { 107 | print!("{}", std::str::from_utf8(b).unwrap()); 108 | let _ = stdout().flush().ok(); 109 | } 110 | } 111 | 112 | pub struct MockedUartRxInputStream { 113 | pub receiver: MockedUartRx, 114 | buffer: [u8; BUF_SIZE], 115 | bytes_read: u8, 116 | current_byte_index: u8, 117 | } 118 | 119 | impl MockedUartRxInputStream 120 | { 121 | pub fn new(receiver: MockedUartRx) -> Self { 122 | Self { 123 | receiver, 124 | buffer: [0; BUFF_SIZE], 125 | bytes_read: 0, 126 | current_byte_index: 0, 127 | } 128 | } 129 | } 130 | 131 | impl async_gcode::ByteStream for MockedUartRxInputStream 132 | { 133 | type Item = Result; 134 | 135 | async fn next(&mut self) -> Option { 136 | 137 | if self.current_byte_index < self.bytes_read { 138 | let byte = self.buffer[self.current_byte_index as usize]; 139 | self.current_byte_index += 1; 140 | Some(Ok(byte)) 141 | } 142 | else { 143 | self.current_byte_index = 0; 144 | self.bytes_read = 0; 145 | let r = self.receiver.read_until_idle(&mut self.buffer).await; 146 | match r { 147 | Ok(n) => { 148 | self.bytes_read = n as u8; 149 | if n > 0 { 150 | let byte = self.buffer[self.current_byte_index as usize]; 151 | self.current_byte_index += 1; 152 | Some(Ok(byte)) 153 | } 154 | else { 155 | hwa::error!("0 bytes read. EOF?"); 156 | None 157 | } 158 | } 159 | Err(_e) => { 160 | hwa::error!("Error: {:?}", _e); 161 | None 162 | } 163 | } 164 | } 165 | } 166 | 167 | async fn recovery_check(&mut self) { 168 | } 169 | 170 | } 171 | -------------------------------------------------------------------------------- /hwi-boards/printhor-hwi_native/src/board/mocked_peripherals/mocked_uart_sink.rs: -------------------------------------------------------------------------------- 1 | use core::future; 2 | 3 | // A mocked Uart which does nothing 4 | pub struct MockedUartSink { 5 | } 6 | 7 | impl MockedUartSink { 8 | pub fn new() -> Self { 9 | Self { 10 | } 11 | } 12 | 13 | pub fn split(&self) -> (MockedUartSinkTx, MockedUartSinkRx) { 14 | (MockedUartSinkTx::new(), MockedUartSinkRx::new()) 15 | } 16 | } 17 | 18 | pub struct MockedUartSinkRx { 19 | } 20 | 21 | impl MockedUartSinkRx { 22 | pub(crate) fn new() -> Self { 23 | Self { 24 | } 25 | } 26 | } 27 | 28 | pub struct MockedUartSinkTx { 29 | } 30 | impl MockedUartSinkTx { 31 | pub(crate) const fn new() -> Self { 32 | Self {} 33 | } 34 | pub fn blocking_flush(&mut self) {} 35 | 36 | pub async fn write(&mut self, _b: &[u8]) {} 37 | 38 | pub async fn write_packet(&mut self, _b: &[u8]) {} 39 | 40 | pub async fn wrapped_flush(&mut self) {} 41 | pub async fn wrapped_write(&mut self, _b: &[u8]) {} 42 | } 43 | 44 | pub struct MockedUartSinkRxInputStream { 45 | pub receiver: MockedUartSinkRx, 46 | } 47 | 48 | impl MockedUartSinkRxInputStream { 49 | pub fn new(receiver: MockedUartSinkRx) -> Self { 50 | Self { 51 | receiver 52 | } 53 | } 54 | } 55 | 56 | impl async_gcode::ByteStream for MockedUartSinkRxInputStream 57 | { 58 | type Item = Result; 59 | 60 | 61 | async fn next(&mut self) -> Option { 62 | future::pending().await 63 | } 64 | 65 | async fn recovery_check(&mut self) {} 66 | } 67 | -------------------------------------------------------------------------------- /hwi-boards/printhor-hwi_native/src/board/mocked_peripherals/mocked_wdt.rs: -------------------------------------------------------------------------------- 1 | use std::marker::PhantomData; 2 | use embassy_executor::SendSpawner; 3 | 4 | pub struct MockedWatchdog<'a, T> { 5 | _spawner: SendSpawner, 6 | _timeout: u32, 7 | p: PhantomData<&'a T>, 8 | } 9 | impl<'a, T> MockedWatchdog<'a, T> { 10 | 11 | pub(crate) const fn new(spawner: SendSpawner, timeout: u32) -> Self { 12 | Self { 13 | _spawner: spawner, 14 | _timeout: timeout, 15 | p: PhantomData, 16 | } 17 | } 18 | 19 | pub fn unleash(&mut self) { 20 | } 21 | 22 | pub fn pet(&mut self) { 23 | //println!("pet!"); 24 | } 25 | } -------------------------------------------------------------------------------- /hwi-boards/printhor-hwi_native/src/board/mocked_peripherals/mod.rs: -------------------------------------------------------------------------------- 1 | #[cfg(any(feature = "with-hot-bed", feature = "with-hot-end"))] 2 | mod mocked_adc; 3 | 4 | pub(crate) mod mocked_pin; 5 | #[cfg(any(feature = "with-probe", feature = "with-hot-bed", feature = "with-hot-end", feature = "with-fan-layer", feature = "with-laser", feature = "with-fan-extra-1"))] 6 | mod mocked_pwm; 7 | 8 | cfg_if::cfg_if! { 9 | if #[cfg(feature = "with-serial-usb")] { 10 | mod mocked_uart_unix_socket; 11 | pub use mocked_uart_unix_socket::*; 12 | } 13 | } 14 | 15 | cfg_if::cfg_if! { 16 | if #[cfg(feature = "with-serial-port-1")] { 17 | mod mocked_uart; 18 | pub use mocked_uart::*; 19 | } 20 | } 21 | 22 | cfg_if::cfg_if! { 23 | if #[cfg(feature = "with-serial-port-2")] { 24 | mod mocked_uart_sink; 25 | pub use mocked_uart_sink::*; 26 | } 27 | } 28 | 29 | cfg_if::cfg_if! { 30 | if #[cfg(feature = "with-spi")] { 31 | mod mocked_spi; 32 | #[allow(unused)] 33 | pub use mocked_spi::*; 34 | } 35 | } 36 | 37 | cfg_if::cfg_if! { 38 | if #[cfg(feature = "with-i2c")] { 39 | mod mocked_i2c; 40 | pub use mocked_i2c::*; 41 | } 42 | } 43 | 44 | cfg_if::cfg_if! { 45 | if #[cfg(feature = "with-sd-card")] { 46 | mod mocked_sd_card; 47 | pub use mocked_sd_card::MockedSDCardBlockDevice; 48 | } 49 | } 50 | 51 | #[cfg(feature = "with-trinamic")] 52 | mod mocked_trinamic; 53 | 54 | mod mocked_wdt; 55 | 56 | #[allow(unused)] 57 | pub use mocked_pin::*; 58 | 59 | pub use mocked_wdt::*; 60 | 61 | 62 | 63 | #[cfg(feature = "with-spi")] 64 | pub use mocked_spi::*; 65 | 66 | #[cfg(feature = "with-trinamic")] 67 | pub use mocked_trinamic::*; 68 | 69 | #[cfg(any(feature = "with-probe", feature = "with-hot-bed", feature = "with-hot-end", feature = "with-fan-layer", feature = "with-laser", feature = "with-fan-extra-1"))] 70 | pub use mocked_pwm::*; 71 | 72 | #[cfg(any(feature = "with-hot-bed", feature = "with-hot-end"))] 73 | pub use mocked_adc::*; -------------------------------------------------------------------------------- /hwi-boards/printhor-hwi_nucleo_64_arduino_cnc_hat/.cargo/config.toml: -------------------------------------------------------------------------------- 1 | [env] 2 | 3 | ########################### 4 | ## Configuration - BEGIN ## 5 | ########################### 6 | 7 | ## Heater and bed configuration (thermistor based) ## 8 | 9 | # [Depends on the board] the pull-up resistance of hot-end sensor circuit in ohms 10 | HOT_END_THERM_PULL_UP_RESISTANCE = "4685.0" 11 | # Nominal resistance of hot-end NTC thermistor at 25ºC 12 | HOT_END_THERM_NOMINAL_RESISTANCE = "100000.0" 13 | # BETA value of hot-end NTC thermistor 14 | HOT_END_THERM_BETA = "3950.0" 15 | 16 | # [Depends on the board] the pull-up resistance of hot-bed sensor circuit in ohms 17 | HOT_BED_THERM_PULL_UP_RESISTANCE = "4685.0" 18 | # Nominal resistance of hot-bed NTC thermistor at 25ºC 19 | HOT_BED_THERM_NOMINAL_RESISTANCE = "100000.0" 20 | # BETA value of hot-bed NTC thermistor 21 | HOT_BED_THERM_BETA = "3950.0" 22 | 23 | ## Pure technical stuff ## 24 | 25 | DEFMT_LOG = "info" 26 | RUST_LOG = "warn" 27 | ASYNC_STD_THREAD_COUNT = "1" 28 | EMBASSY_USB_MAX_INTERFACE_COUNT = "2" 29 | 30 | ########################### 31 | ## Configuration - END ## 32 | ########################### 33 | 34 | [target.'cfg(all(any(board="nucleo64-f410rb"), target_arch="arm", target_os="none"))'] 35 | runner = "probe-run --chip STM32F410RBTx --connect-under-reset --log-format {L}{s}" 36 | #rustflags = [ "-C", "linker=flip-link" ] 37 | #linker = "flip-link" 38 | 39 | [target.'cfg(all(any(board="nucleo64-l476rg"), target_arch="arm", target_os="none"))'] 40 | runner = "probe-rs run --chip STM32L476RGTx --log-format {L}{s}" 41 | #rustflags = [ "-C", "linker=flip-link" ] 42 | #linker = "flip-link" 43 | 44 | [build] 45 | target = "thumbv7em-none-eabihf" 46 | 47 | [profile.dev.package."*"] 48 | codegen-units = 1 49 | incremental = false 50 | #opt-level = "s" # Set to "s" or even disable to play with GDB 51 | debug = 2 52 | debug-assertions = true 53 | overflow-checks = true 54 | 55 | [profile.release.package."*"] 56 | codegen-units = 1 57 | incremental = false 58 | opt-level = "z" 59 | debug = 2 60 | debug-assertions = true 61 | overflow-checks = true 62 | 63 | [profile.release-opt.package."*"] 64 | codegen-units = 1 65 | incremental = false 66 | opt-level = "z" 67 | debug = 0 68 | debug-assertions = false 69 | overflow-checks = false 70 | -------------------------------------------------------------------------------- /hwi-boards/printhor-hwi_nucleo_64_arduino_cnc_hat/README.md: -------------------------------------------------------------------------------- 1 | ![Minimum Rust: 1.85](https://img.shields.io/badge/Minimum%20Rust%20Version-1.85-green.svg) 2 | [![crates.io](https://img.shields.io/crates/v/prinThor.svg)](https://crates.io/crates/prinThor) 3 | [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) 4 | ![Discord Shield](https://discordapp.com/api/guilds/1169965662618259456/widget.png?style=shield) 5 | 6 |

Printhor: The highly reliable but not necessarily functional 3D printer firmware

7 | 8 |

If you are using this product or like the project, please this repository to show your support! 🤩

9 | 10 | # Overview 11 | 12 | There are two base boards supported in this category. 13 | The assumption/requirement is to use any of these generic purpose development board with the Arduino CNC Shield v3 (hat): 14 | ![alt text](../../datasheets/NUCLEO-L476RG/Arduino-CNC-Shield-Pinout-V3.XX.jpeg "Arduino CNC Shield v3") 15 | 16 | **Important**: Upgrade the Nucleo ST-LINK/V2 firmware to at least: Version: V2J43M28 Build: Jun 16 2023 16:43:19 17 | 18 | In these development boards, flash and run can be directly performed with probe-rs just connecting USB as they have a built-in SWD/JTAG interface: 19 | 20 | ### nucleo-f410rb 21 | https://os.mbed.com/platforms/ST-Nucleo-F410RB/ 22 | 23 | Please, note that this board is very limited in terms of flash and memory (32kB SRAM, 128kB flash). 24 | You might not assume that a firwmare not optimized for size (LTO, etc...) will fit in flash. 25 | 26 | Note: This target uses flip-link by default, requiring flip-link tool. To change this behavior please check .cargo/config.toml 27 | ```shell 28 | cargo install flip-link 29 | ``` 30 | 31 | ```shell 32 | DEFMT_LOG=info RUST_BACKTRACE=0 RUSTFLAGS='--cfg board_stm32l4="nucleo64-f410rb"' cargo run --release --no-default-features --features nucleo_64_arduino_cnc_hat,nucleo64-f410rb --target thumbv7em-none-eabihf --bin printhor 33 | ``` 34 | 35 | ### nucleo-l476rg 36 | https://os.mbed.com/platforms/ST-Nucleo-L476RG/ 37 | 38 | This one is a bit slower but much more RAM and flash. Enough even though with not very optimized firmware and many features 39 | 40 | ```shell 41 | DEFMT_LOG=info RUST_BACKTRACE=0 RUSTFLAGS='--cfg board_stm32l4="nucleo64-l476rg"' cargo run --release --no-default-features --features nucleo_64_arduino_cnc_hat,nucleo64-l476rg --target thumbv7em-none-eabihf --bin printhor 42 | ``` 43 | 44 | -------------------------------------------------------------------------------- /hwi-boards/printhor-hwi_nucleo_64_arduino_cnc_hat/src/board_stm32f4/types.rs: -------------------------------------------------------------------------------- 1 | use printhor_hwa_common as hwa; 2 | 3 | //#region "General controllers lock types" 4 | pub type EventBusPubSubMutexType = hwa::AsyncNoopMutexType; 5 | pub type EventBusLockType = hwa::AsyncNoopMutexType; 6 | //#endregion 7 | 8 | //#region "HWI Shared controllers lock types" 9 | pub type WatchDogLockType = hwa::AsyncNoopMutexType; 10 | 11 | //#endregion 12 | 13 | //#region "General controllers locking strategy customization" 14 | pub type EventBusMutexStrategy = hwa::AsyncStandardStrategy>; 15 | pub type WatchDogMutexStrategy = hwa::AsyncStandardStrategy; 16 | //#endregion 17 | 18 | //#region "Shared controllers locking strategy customization" 19 | cfg_if::cfg_if! { 20 | if #[cfg(feature = "with-serial-port-1")] { 21 | pub type SerialPort1TxLockType = hwa::AsyncNoopMutexType; 22 | pub type SerialPort1TxMutexStrategy = hwa::AsyncStandardStrategy; 23 | } 24 | } 25 | 26 | cfg_if::cfg_if! { 27 | if #[cfg(feature = "with-serial-port-2")] { 28 | pub type SerialPort2TxLockType = hwa::AsyncNoopMutexType; 29 | pub type SerialPort2TxMutexStrategy = hwa::AsyncStandardStrategy; 30 | } 31 | } 32 | 33 | cfg_if::cfg_if! { 34 | if #[cfg(any(feature = "with-motion", feature = "with-hot-end", feature = "with-hot-bed"))] { 35 | pub type DeferChannelMutexType = hwa::AsyncNoopMutexType; 36 | } 37 | } 38 | 39 | cfg_if::cfg_if! { 40 | if #[cfg(feature = "with-motion")] { 41 | pub type StepActuatorMutexType = hwa::SyncCsMutexType; 42 | pub type MotionSignalMutexType = hwa::AsyncNoopMutexType; 43 | pub type MotionRingBufferMutexType = hwa::AsyncNoopMutexType; 44 | pub type MotionConfigMutexType = hwa::AsyncCsMutexType; 45 | pub type MotionStatusMutexType = hwa::AsyncNoopMutexType; 46 | pub type MotionDriverMutexType = hwa::AsyncNoopMutexType; 47 | 48 | pub type StepActuatorMuxtexStrategy = hwa::SyncStandardStrategy; 49 | } 50 | } 51 | 52 | cfg_if::cfg_if! { 53 | if #[cfg(all(feature = "with-motion", feature = "with-motion-broadcast"))] { 54 | pub type MotionBroadcastChannelMutexType = hwa::AsyncCsMutexType; 55 | 56 | pub type MotionSenderMutexType = I2cMutexType; 57 | 58 | pub type MotionSenderMutexStrategy = hwa::AsyncStandardStrategy; 59 | } 60 | } 61 | 62 | cfg_if::cfg_if! { 63 | if #[cfg(feature = "with-ps-on")] { 64 | pub type PSOnLockType = hwa::SyncNoopMutexType; 65 | pub type PSOnMutexStrategy = hwa::SyncStandardStrategy; 66 | } 67 | } 68 | 69 | cfg_if::cfg_if! { 70 | if #[cfg(feature = "with-spi")] { 71 | pub type Spi1MutexType = hwa::AsyncNoopMutexType; 72 | pub type Spi1MutexStrategyType = hwa::AsyncHoldableStrategy; 73 | } 74 | } 75 | 76 | cfg_if::cfg_if! { 77 | if #[cfg(feature = "with-i2c")] { 78 | pub type I2cMutexType = hwa::AsyncCsMutexType; 79 | pub type I2cMutexStrategyType = hwa::AsyncStandardStrategy; 80 | } 81 | } 82 | 83 | cfg_if::cfg_if! { 84 | if #[cfg(feature = "with-laser")] { 85 | pub type LaserMutexType = hwa::SyncCsMutexType; 86 | pub type LaserPwmMutexStrategy = hwa::SyncStandardStrategy; 87 | } 88 | } 89 | //#endregion -------------------------------------------------------------------------------- /hwi-boards/printhor-hwi_nucleo_64_arduino_cnc_hat/src/board_stm32l4/types.rs: -------------------------------------------------------------------------------- 1 | use printhor_hwa_common as hwa; 2 | 3 | //#region "General controllers lock types" 4 | pub type EventBusPubSubMutexType = hwa::AsyncNoopMutexType; 5 | pub type EventBusLockType = hwa::AsyncNoopMutexType; 6 | //#endregion 7 | 8 | //#region "HWI Shared controllers lock types" 9 | pub type WatchDogLockType = hwa::AsyncNoopMutexType; 10 | 11 | //#endregion 12 | 13 | //#region "General controllers locking strategy customization" 14 | pub type EventBusMutexStrategy = hwa::AsyncStandardStrategy>; 15 | pub type WatchDogMutexStrategy = hwa::AsyncStandardStrategy; 16 | //#endregion 17 | 18 | //#region "Shared controllers locking strategy customization" 19 | cfg_if::cfg_if! { 20 | if #[cfg(feature = "with-serial-port-1")] { 21 | pub type SerialPort1TxLockType = hwa::AsyncNoopMutexType; 22 | pub type SerialPort1TxMutexStrategy = hwa::AsyncStandardStrategy; 23 | } 24 | } 25 | 26 | cfg_if::cfg_if! { 27 | if #[cfg(feature = "with-serial-port-2")] { 28 | pub type SerialPort2TxLockType = hwa::AsyncNoopMutexType; 29 | pub type SerialPort2TxMutexStrategy = hwa::AsyncStandardStrategy; 30 | } 31 | } 32 | 33 | cfg_if::cfg_if! { 34 | if #[cfg(any(feature = "with-motion", feature = "with-hot-end", feature = "with-hot-bed"))] { 35 | pub type DeferChannelMutexType = hwa::AsyncNoopMutexType; 36 | } 37 | } 38 | 39 | cfg_if::cfg_if! { 40 | if #[cfg(feature = "with-motion")] { 41 | pub type StepActuatorMutexType = hwa::SyncCsMutexType; 42 | pub type MotionSignalMutexType = hwa::AsyncNoopMutexType; 43 | pub type MotionRingBufferMutexType = hwa::AsyncNoopMutexType; 44 | pub type MotionConfigMutexType = hwa::AsyncCsMutexType; 45 | pub type MotionStatusMutexType = hwa::AsyncNoopMutexType; 46 | pub type MotionDriverMutexType = hwa::AsyncNoopMutexType; 47 | 48 | pub type StepActuatorMuxtexStrategy = hwa::SyncStandardStrategy; 49 | } 50 | } 51 | 52 | cfg_if::cfg_if! { 53 | if #[cfg(all(feature = "with-motion", feature = "with-motion-broadcast"))] { 54 | pub type MotionBroadcastChannelMutexType = hwa::AsyncCsMutexType; 55 | 56 | pub type MotionSenderMutexType = I2cMutexType; 57 | 58 | pub type MotionSenderMutexStrategy = hwa::AsyncStandardStrategy; 59 | } 60 | } 61 | 62 | 63 | cfg_if::cfg_if! { 64 | if #[cfg(feature = "with-ps-on")] { 65 | pub type PSOnLockType = hwa::SyncNoopMutexType; 66 | pub type PSOnMutexStrategy = hwa::SyncStandardStrategy; 67 | } 68 | } 69 | 70 | cfg_if::cfg_if! { 71 | if #[cfg(feature = "with-spi")] { 72 | pub type Spi1MutexType = hwa::AsyncNoopMutexType; 73 | pub type Spi1MutexStrategyType = hwa::AsyncHoldableStrategy; 74 | } 75 | } 76 | 77 | cfg_if::cfg_if! { 78 | if #[cfg(feature = "with-i2c")] { 79 | pub type I2cMutexType = hwa::AsyncCsMutexType; 80 | pub type I2cMutexStrategyType = hwa::AsyncStandardStrategy; 81 | } 82 | } 83 | 84 | cfg_if::cfg_if! { 85 | if #[cfg(feature = "with-laser")] { 86 | pub type LaserMutexType = hwa::SyncCsMutexType; 87 | pub type LaserPwmMutexStrategy = hwa::SyncStandardStrategy; 88 | } 89 | } 90 | 91 | cfg_if::cfg_if! { 92 | if #[cfg(feature = "with-probe")] { 93 | pub type ProbeMutexType = hwa::SyncCsMutexType; 94 | pub type ProbePwmMutexStrategy = hwa::SyncStandardStrategy; 95 | } 96 | } 97 | 98 | cfg_if::cfg_if! { 99 | if #[cfg(any(feature="with-hot-end", feature = "with-hot-bed"))] { 100 | pub type HotEndHotBedAdcMutexType = hwa::SyncCsMutexType; 101 | pub type HotEndHotBedAdcMutexStrategy = hwa::AsyncStandardStrategy; 102 | 103 | pub type HotEndHotBedPwmMutexType = hwa::SyncNoopMutexType; 104 | pub type HotEndHotBedPwmMutexStrategy = hwa::SyncStandardStrategy; 105 | } 106 | } 107 | 108 | cfg_if::cfg_if! { 109 | if #[cfg(feature="with-hot-end")] { 110 | pub type HotEndAdcMutexStrategy = HotEndHotBedAdcMutexStrategy; 111 | pub type HotEndPwmMutexStrategy = HotEndHotBedPwmMutexStrategy; 112 | } 113 | } 114 | 115 | cfg_if::cfg_if! { 116 | if #[cfg(feature="with-hot-bed")] { 117 | pub type HotBedAdcMutexStrategy = HotEndHotBedAdcMutexStrategy; 118 | pub type HotBedPwmMutexStrategy = HotEndHotBedPwmMutexStrategy; 119 | } 120 | } 121 | //#endregion -------------------------------------------------------------------------------- /hwi-boards/printhor-hwi_nucleo_64_arduino_cnc_hat/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | extern crate alloc; 3 | 4 | cfg_if::cfg_if! { 5 | if #[cfg(feature="nucleo64-f410rb")] { 6 | mod board_stm32f4; 7 | pub use board_stm32f4::Contract; 8 | } 9 | else if #[cfg(feature="nucleo64-l476rg")] { 10 | mod board_stm32l4; 11 | pub use board_stm32l4::Contract; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /hwi-boards/printhor-hwi_rp_2040/.cargo/config.toml: -------------------------------------------------------------------------------- 1 | [env] 2 | 3 | ########################### 4 | ## Configuration - BEGIN ## 5 | ########################### 6 | 7 | ## Heater and bed configuration (thermistor based) ## 8 | 9 | # [Depends on the board_rp_2040] the pull-up resistance of hot-end sensor circuit in ohms 10 | HOT_END_THERM_PULL_UP_RESISTANCE = "4685.0" 11 | # Nominal resistance of hot-end NTC thermistor at 25ºC 12 | HOT_END_THERM_NOMINAL_RESISTANCE = "100000.0" 13 | # BETA value of hot-end NTC thermistor 14 | HOT_END_THERM_BETA = "3950.0" 15 | 16 | # [Depends on the board_rp_2040] the pull-up resistance of hot-bed sensor circuit in ohms 17 | HOT_BED_THERM_PULL_UP_RESISTANCE = "4685.0" 18 | # Nominal resistance of hot-bed NTC thermistor at 25ºC 19 | HOT_BED_THERM_NOMINAL_RESISTANCE = "100000.0" 20 | # BETA value of hot-bed NTC thermistor 21 | HOT_BED_THERM_BETA = "3950.0" 22 | 23 | ## Pure technical stuff ## 24 | 25 | DEFMT_LOG = "info" 26 | RUST_LOG = "warn" 27 | ASYNC_STD_THREAD_COUNT = "1" 28 | EMBASSY_USB_MAX_INTERFACE_COUNT = "2" 29 | 30 | ########################### 31 | ## Configuration - END ## 32 | ########################### 33 | 34 | [target.'cfg(all(any(board="rp2040"), target_arch="arm", target_os="none"))'] 35 | #runner = "probe-rs run --chip RP2040" 36 | runner = "elf2uf2-rs --deploy --serial --verbose" 37 | 38 | [build] 39 | target = "thumbv6m-none-eabi" 40 | 41 | [profile.dev.package."*"] 42 | codegen-units = 1 43 | incremental = false 44 | #opt-level = "s" # Set to "s" or even disable to play with GDB 45 | debug = 2 46 | debug-assertions = true 47 | overflow-checks = true 48 | 49 | [profile.release.package."*"] 50 | codegen-units = 1 51 | incremental = false 52 | opt-level = "z" 53 | debug = 2 54 | debug-assertions = true 55 | overflow-checks = true 56 | 57 | [profile.release-opt.package."*"] 58 | codegen-units = 1 59 | incremental = false 60 | opt-level = "z" 61 | debug = 0 62 | debug-assertions = false 63 | overflow-checks = false 64 | -------------------------------------------------------------------------------- /hwi-boards/printhor-hwi_rp_2040/README.md: -------------------------------------------------------------------------------- 1 | ![Minimum Rust: 1.85](https://img.shields.io/badge/Minimum%20Rust%20Version-1.85-green.svg) 2 | [![crates.io](https://img.shields.io/crates/v/prinThor.svg)](https://crates.io/crates/prinThor) 3 | [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) 4 | ![Discord Shield](https://discordapp.com/api/guilds/1169965662618259456/widget.png?style=shield) 5 | 6 |

Printhor: The highly reliable but not necessarily functional 3D printer firmware

7 | 8 |

If you are using this product or like the project, please this repository to show your support! 🤩

9 | 10 | # Overview 11 | 12 | Doc WIP: The Printhor HWI for raspberry PI 2040 MCUs 13 | -------------------------------------------------------------------------------- /hwi-boards/printhor-hwi_rp_2040/rpi-pico/memory.x: -------------------------------------------------------------------------------- 1 | MEMORY { 2 | BOOT2 : ORIGIN = 0x10000000, LENGTH = 0x100 3 | FLASH : ORIGIN = 0x10000100, LENGTH = 2048K - 0x100 4 | 5 | /* Pick one of the two options for RAM layout */ 6 | 7 | /* OPTION A: Use all RAM banks as one big block */ 8 | /* Reasonable, unless you are doing something */ 9 | /* really particular with DMA or other concurrent */ 10 | /* access that would benefit from striping */ 11 | RAM : ORIGIN = 0x20000000, LENGTH = 264K 12 | 13 | /* OPTION B: Keep the unstriped sections separate */ 14 | /* RAM: ORIGIN = 0x20000000, LENGTH = 256K */ 15 | /* SCRATCH_A: ORIGIN = 0x20040000, LENGTH = 4K */ 16 | /* SCRATCH_B: ORIGIN = 0x20041000, LENGTH = 4K */ 17 | } 18 | -------------------------------------------------------------------------------- /hwi-boards/printhor-hwi_rp_2040/rpi-pico/openocd.cfg: -------------------------------------------------------------------------------- 1 | source [find interface/cmsis-dap.cfg] 2 | 3 | source [find target/rp2040.cfg] 4 | 5 | -------------------------------------------------------------------------------- /hwi-boards/printhor-hwi_rp_2040/src/board_rp_2040/types.rs: -------------------------------------------------------------------------------- 1 | use printhor_hwa_common as hwa; 2 | 3 | //#region "General controllers lock types" 4 | pub type EventBusPubSubMutexType = hwa::AsyncNoopMutexType; 5 | pub type EventBusLockType = hwa::AsyncNoopMutexType; 6 | //#endregion 7 | 8 | //#region "HWI Shared controllers lock types" 9 | pub type WatchDogLockType = hwa::AsyncNoopMutexType; 10 | //#endregion 11 | 12 | //#region "General controllers locking strategy customization" 13 | pub type EventBusMutexStrategy = hwa::AsyncStandardStrategy>; 14 | pub type WatchDogMutexStrategy = hwa::AsyncStandardStrategy; 15 | //#endregion 16 | 17 | cfg_if::cfg_if! { 18 | if #[cfg(feature = "with-serial-usb")] { 19 | pub type SerialUsbTxLockType = hwa::AsyncNoopMutexType; 20 | pub type SerialUsbTxMutexStrategy = hwa::AsyncStandardStrategy; 21 | } 22 | } 23 | 24 | cfg_if::cfg_if! { 25 | if #[cfg(feature = "with-serial-port-1")] { 26 | pub type SerialPort1TxLockType = hwa::AsyncNoopMutexType; 27 | pub type SerialPort1TxMutexStrategy = hwa::AsyncStandardStrategy; 28 | } 29 | } 30 | 31 | cfg_if::cfg_if! { 32 | if #[cfg(feature = "with-serial-port-2")] { 33 | pub type SerialPort2TxLockType = hwa::AsyncNoopMutexType; 34 | pub type SerialPort2TxMutexStrategy = hwa::AsyncStandardStrategy; 35 | } 36 | } 37 | 38 | cfg_if::cfg_if! { 39 | if #[cfg(any(feature = "with-motion", feature = "with-hot-end", feature = "with-hot-bed"))] { 40 | pub type DeferChannelMutexType = hwa::AsyncNoopMutexType; 41 | } 42 | } 43 | 44 | cfg_if::cfg_if! { 45 | if #[cfg(feature = "with-motion")] { 46 | pub type StepActuatorMutexType = hwa::SyncCsMutexType; 47 | pub type MotionSignalMutexType = hwa::AsyncNoopMutexType; 48 | pub type MotionRingBufferMutexType = hwa::AsyncNoopMutexType; 49 | pub type MotionConfigMutexType = hwa::AsyncCsMutexType; 50 | pub type MotionStatusMutexType = hwa::AsyncNoopMutexType; 51 | pub type MotionDriverMutexType = hwa::AsyncNoopMutexType; 52 | 53 | pub type StepActuatorMuxtexStrategy = hwa::SyncStandardStrategy; 54 | } 55 | } 56 | 57 | cfg_if::cfg_if! { 58 | if #[cfg(all(feature = "with-motion", feature = "with-motion-broadcast"))] { 59 | pub type MotionBroadcastChannelMutexType = hwa::AsyncCsMutexType; 60 | 61 | pub type MotionSenderMutexType = I2cMutexType; 62 | 63 | pub type MotionSenderMutexStrategy = hwa::AsyncStandardStrategy; 64 | } 65 | } 66 | 67 | 68 | cfg_if::cfg_if! { 69 | if #[cfg(feature = "with-ps-on")] { 70 | pub type PSOnLockType = hwa::SyncNoopMutexType; 71 | pub type PSOnMutexStrategy = hwa::SyncStandardStrategy; 72 | } 73 | } 74 | 75 | cfg_if::cfg_if! { 76 | if #[cfg(feature = "with-spi")] { 77 | pub type Spi1MutexType = hwa::AsyncNoopMutexType; 78 | pub type Spi1MutexStrategyType = hwa::AsyncHoldableStrategy; 79 | } 80 | } 81 | 82 | cfg_if::cfg_if! { 83 | if #[cfg(feature = "with-i2c")] { 84 | pub type I2cMutexType = hwa::AsyncCsMutexType; 85 | pub type I2cMutexStrategyType = hwa::AsyncStandardStrategy; 86 | } 87 | } 88 | 89 | cfg_if::cfg_if! { 90 | if #[cfg(feature = "with-laser")] { 91 | pub type LaserMutexType = hwa::SyncCsMutexType; 92 | pub type LaserPwmMutexStrategy = hwa::SyncStandardStrategy; 93 | } 94 | } 95 | 96 | cfg_if::cfg_if! { 97 | if #[cfg(feature = "with-probe")] { 98 | pub type ProbeMutexType = hwa::SyncCsMutexType; 99 | pub type ProbePwmMutexStrategy = hwa::SyncStandardStrategy; 100 | } 101 | } 102 | 103 | cfg_if::cfg_if! { 104 | if #[cfg(any(feature="with-hot-end", feature = "with-hot-bed"))] { 105 | pub type HotEndHotBedAdcMutexType = hwa::SyncCsMutexType; 106 | pub type HotEndHotBedAdcMutexStrategy = hwa::AsyncStandardStrategy; 107 | 108 | pub type HotEndHotBedPwmMutexType = hwa::SyncNoopMutexType; 109 | pub type HotEndHotBedPwmMutexStrategy = hwa::SyncStandardStrategy; 110 | } 111 | } 112 | 113 | cfg_if::cfg_if! { 114 | if #[cfg(feature="with-hot-end")] { 115 | pub type HotEndAdcMutexStrategy = HotEndHotBedAdcMutexStrategy; 116 | pub type HotEndPwmMutexStrategy = HotEndHotBedPwmMutexStrategy; 117 | } 118 | } 119 | 120 | cfg_if::cfg_if! { 121 | if #[cfg(feature="with-hot-bed")] { 122 | pub type HotBedAdcMutexStrategy = HotEndHotBedAdcMutexStrategy; 123 | pub type HotBedPwmMutexStrategy = HotEndHotBedPwmMutexStrategy; 124 | } 125 | } 126 | //#endregion -------------------------------------------------------------------------------- /hwi-boards/printhor-hwi_rp_2040/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![allow(stable_features)] 3 | mod board_rp_2040; 4 | 5 | pub use board_rp_2040::Contract; 6 | -------------------------------------------------------------------------------- /hwi-boards/printhor-hwi_skr_mini_e3/.cargo/config.toml: -------------------------------------------------------------------------------- 1 | [env] 2 | 3 | ########################### 4 | ## Configuration - BEGIN ## 5 | ########################### 6 | 7 | ## Heater and bed configuration (thermistor based) ## 8 | 9 | # [Depends on the board] the pull-up resistance of hot-end sensor circuit in ohms 10 | HOT_END_THERM_PULL_UP_RESISTANCE = "4685.0" 11 | # Nominal resistance of hot-end NTC thermistor at 25ºC 12 | HOT_END_THERM_NOMINAL_RESISTANCE = "100000.0" 13 | # BETA value of hot-end NTC thermistor 14 | HOT_END_THERM_BETA = "3950.0" 15 | 16 | # [Depends on the board] the pull-up resistance of hot-bed sensor circuit in ohms 17 | HOT_BED_THERM_PULL_UP_RESISTANCE = "4685.0" 18 | # Nominal resistance of hot-bed NTC thermistor at 25ºC 19 | HOT_BED_THERM_NOMINAL_RESISTANCE = "100000.0" 20 | # BETA value of hot-bed NTC thermistor 21 | HOT_BED_THERM_BETA = "3950.0" 22 | 23 | ## Pure technical stuff ## 24 | 25 | DEFMT_LOG = "info" 26 | RUST_LOG = "warn" 27 | ASYNC_STD_THREAD_COUNT = "1" 28 | EMBASSY_USB_MAX_INTERFACE_COUNT = "2" 29 | 30 | ########################### 31 | ## Configuration - END ## 32 | ########################### 33 | 34 | [target.'cfg(all(any(board="skr_mini_e3_v3"), target_arch="arm", target_os="none"))'] 35 | runner = "probe-rs run --chip STM32G0B1RETx --log-format {L}{s} --base-address 08002000 --connect-under-reset" 36 | 37 | [build] 38 | target = "thumbv6m-none-eabi" 39 | 40 | [profile.dev.package."*"] 41 | codegen-units = 1 42 | incremental = false 43 | #opt-level = "s" # Set to "s" or even disable to play with GDB 44 | debug = 2 45 | debug-assertions = true 46 | overflow-checks = true 47 | 48 | [profile.release.package."*"] 49 | codegen-units = 1 50 | incremental = false 51 | opt-level = "z" 52 | debug = 2 53 | debug-assertions = true 54 | overflow-checks = true 55 | 56 | [profile.release-opt.package."*"] 57 | codegen-units = 1 58 | incremental = false 59 | opt-level = "z" 60 | debug = 0 61 | debug-assertions = false 62 | overflow-checks = false 63 | -------------------------------------------------------------------------------- /hwi-boards/printhor-hwi_skr_mini_e3/README.md: -------------------------------------------------------------------------------- 1 | ![Minimum Rust: 1.85](https://img.shields.io/badge/Minimum%20Rust%20Version-1.85-green.svg) 2 | [![crates.io](https://img.shields.io/crates/v/prinThor.svg)](https://crates.io/crates/prinThor) 3 | [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) 4 | ![Discord Shield](https://discordapp.com/api/guilds/1169965662618259456/widget.png?style=shield) 5 | 6 |

Printhor: The highly reliable but not necessarily functional 3D printer firmware

7 | 8 |

If you are using this product or like the project, please this repository to show your support! 🤩

9 | 10 | # Overview 11 | 12 | This board are quite functional: 13 | * https://biqu.equipment/products/bigtreetech-skr-mini-e3-v2-0-32-bit-control-board-integrated-tmc2209-uart-for-ender-4 14 | 15 | ### Binary image production (standard with defmt) 16 | 17 | The firmware.bin file ready to be uploaded to the SD can be produced with the following commandline: 18 | 19 | For SKR Minit E3 V3.0: 20 | ``` 21 | DEFMT_LOG=info cargo objcopy --release --no-default-features --features skr_mini_e3_v3 --target thumbv6m-none-eabi --bin printhor -- -O binary firmware.bin 22 | ``` 23 | or 24 | 25 | ```shell 26 | DEFMT_LOG=info cargo objcopy --release --no-default-features --features skr_mini_e3_v3 --target thumbv6m-none-eabi --bin printhor -- -O binary firmware.bin 27 | ``` 28 | 29 | Firmware size around 196kB as of now with previous settings. 30 | 31 | ### Minimal-size binary image production 32 | 33 | ```shell 34 | DEFMT_LOG=off RUST_BACKTRACE=0 cargo objcopy --profile release-opt --no-default-features --features skr_mini_e3_v3 --target thumbv6m-none-eabi --bin printhor -- -O binary firmware.bin 35 | ``` 36 | 37 | Firmware size if 180kB as of now with previous settings. 38 | 39 | ### Run with JLink/SWD device 40 | 41 | ```shell 42 | DEFMT_LOG=info RUST_BACKTRACE=1 RUSTFLAGS='--cfg board="skr_mini_e3_v3"' cargo run --release --no-default-features --features skr_mini_e3_v3 --target thumbv6m-none-eabi --bin printhor 43 | ``` 44 | 45 | -------------------------------------------------------------------------------- /hwi-boards/printhor-hwi_skr_mini_e3/skr_mini_e3_v3/memory.x: -------------------------------------------------------------------------------- 1 | /* SKR MINI E3 V3 - STM32G0B1RE */ 2 | MEMORY 3 | { 4 | FLASH : ORIGIN = 0x08000000 + 8K, LENGTH = 512K - 8K /* BANK_1 + BANK_2 */ 5 | RAM : ORIGIN = 0x20000000, LENGTH = 128K 6 | } -------------------------------------------------------------------------------- /hwi-boards/printhor-hwi_skr_mini_e3/skr_mini_e3_v3/openocd.cfg: -------------------------------------------------------------------------------- 1 | source [find interface/stlink.cfg] 2 | 3 | source [find target/stm32g0x.cfg] 4 | 5 | -------------------------------------------------------------------------------- /hwi-boards/printhor-hwi_skr_mini_e3/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | extern crate alloc; 3 | 4 | cfg_if::cfg_if! { 5 | if #[cfg(feature="skr_mini_e3_v3")] { 6 | mod board_stm32g0; 7 | use board_stm32g0 as board; 8 | } 9 | else { 10 | compile_error!("You didn't specify any board"); 11 | } 12 | 13 | } 14 | pub use board::Contract; -------------------------------------------------------------------------------- /openocd.gdbtpl: -------------------------------------------------------------------------------- 1 | target extended-remote :3333 2 | 3 | # print demangled symbols 4 | set print asm-demangle on 5 | 6 | # detect unhandled exceptions, hard faults and panics 7 | break DefaultHandler 8 | break HardFault 9 | break rust_begin_unwind 10 | 11 | monitor arm semihosting disable 12 | 13 | load 14 | 15 | # start the process but immediately halt the processor 16 | stepi 17 | -------------------------------------------------------------------------------- /printhor-hwa-common-macros/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "printhor-hwa-common-macros" 3 | version = "0.0.4" 4 | edition = "2024" 5 | 6 | [features] 7 | default = ["std"] 8 | float-point-f32-impl = ["printhor-hwa-common/float-point-f32-impl"] 9 | float-point-f64-impl = ["printhor-hwa-common/float-point-f64-impl"] 10 | fixed-point-128-impl = ["printhor-hwa-common/fixed-point-128-impl"] 11 | std = [] 12 | 13 | [dependencies] 14 | syn = "2.0" 15 | quote = "1.0" 16 | proc-macro2 = "1.0" 17 | cfg-if = "1" 18 | 19 | [dev-dependencies] 20 | printhor-hwa-common = {version = "0", default-features = false, path = "../printhor-hwa-common"} 21 | printhor-hwa-utils = {version = "0", default-features = false, path = "../printhor-hwa-utils"} 22 | 23 | [lib] 24 | proc-macro = true -------------------------------------------------------------------------------- /printhor-hwa-common/.cargo/config.toml: -------------------------------------------------------------------------------- 1 | 2 | [env] 3 | #EMBASSY_EXECUTOR_TASK_ARENA_SIZE=16384 4 | 5 | [profile.dev.package."*"] 6 | codegen-units = 1 7 | incremental = false 8 | #opt-level = "s" # Set to "s" or even disable to play with GDB 9 | debug = 2 10 | debug-assertions = true 11 | overflow-checks = true 12 | 13 | [profile.release.package."*"] 14 | codegen-units = 1 15 | incremental = false 16 | opt-level = "z" 17 | debug = 2 18 | debug-assertions = true 19 | overflow-checks = true 20 | 21 | [profile.release-opt.package."*"] 22 | codegen-units = 1 23 | incremental = false 24 | opt-level = "z" 25 | debug = 0 26 | debug-assertions = false 27 | overflow-checks = false -------------------------------------------------------------------------------- /printhor-hwa-common/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "printhor-hwa-common" 3 | version = "0.0.4" 4 | edition = "2024" 5 | authors = ["Carlos Barrales Ruiz "] 6 | description = "Printor harware abstraction utillery" 7 | readme = "README.md" 8 | repository = "https://github.com/cbruiz/printhor" 9 | keywords = ["hardware", "abstration", "library", "printhor"] 10 | categories = ["hardware-support"] 11 | license = "MIT" 12 | documentation = "https://docs.rs/printhor-hwa_common" 13 | homepage = "https://github.com/cbruiz/printhor" 14 | 15 | [lib] 16 | 17 | [features] 18 | std = ["futures-test/std", "embassy-executor/arch-std", 19 | "embassy-executor/executor-thread", 20 | "printhor-hwa-common-macros/std", 21 | "printhor-hwa-utils/std", 22 | "embassy-time/std"] 23 | default = ["std", "with-log"] 24 | nightly = [] 25 | with-log = ["log", "printhor-hwa-utils/with-log"] 26 | with-defmt = ["defmt", "printhor-hwa-utils/with-defmt", "embedded-sdmmc/defmt-log"] 27 | with-serial-usb = ["async-gcode", "embedded-io-async"] 28 | with-serial-port-1 = ["async-gcode", "embedded-io-async"] 29 | with-serial-port-2 = ["async-gcode", "embedded-io-async"] 30 | with-spi = ["embedded-hal-0"] 31 | with-i2c = ["embedded-hal-0"] 32 | with-ps-on = ["embedded-hal-1"] 33 | with-probe = ["embedded-hal-0"] 34 | with-laser = ["embedded-hal-0"] 35 | with-fan-layer = ["embedded-hal-0"] 36 | with-fan-extra-1 = ["embedded-hal-0"] 37 | with-motion = ["embedded-hal-0"] 38 | with-motion-broadcast = [] 39 | with-motion-cartessian-kinematics = [] 40 | with-motion-delta-kinematics = [] 41 | with-motion-core-xy-kinematics = [] 42 | with-motion-anthropomorphic-kinematics = [] 43 | with-hot-end = ["embedded-hal-0"] 44 | with-hot-bed = ["embedded-hal-0"] 45 | with-sd-card = ["async-gcode", "embedded-sdmmc", "heapless"] 46 | with-print-job = [] 47 | with-trinamic = ["embassy-embedded-hal"] 48 | 49 | sd-card-spi = [] 50 | 51 | float-point-f32-impl = ["printhor-hwa-common-macros/float-point-f32-impl"] 52 | float-point-f64-impl = ["printhor-hwa-common-macros/float-point-f64-impl"] 53 | fixed-point-128-impl = ["printhor-hwa-common-macros/fixed-point-128-impl", "rust_decimal", "rust_decimal_macros"] 54 | 55 | with-all-axis = [ 56 | "with-e-axis", 57 | "with-x-axis", "with-y-axis", "with-z-axis", 58 | "with-a-axis", "with-b-axis", "with-c-axis", 59 | "with-i-axis", "with-j-axis", "with-k-axis", 60 | "with-u-axis", "with-v-axis", "with-w-axis", 61 | ] 62 | with-x-axis = [] 63 | with-y-axis = [] 64 | with-z-axis = [] 65 | with-e-axis = [] 66 | with-a-axis = [] 67 | with-b-axis = [] 68 | with-c-axis = [] 69 | with-i-axis = [] 70 | with-j-axis = [] 71 | with-k-axis = [] 72 | with-u-axis = [] 73 | with-v-axis = [] 74 | with-w-axis = [] 75 | 76 | [dependencies] 77 | printhor-hwa-common-macros = { version = "0", default-features = false, path = "../printhor-hwa-common-macros" } 78 | printhor-hwa-utils = { version = "0", default-features = false, path = "../printhor-hwa-utils", features = [] } 79 | const_env = { version = "0.1.2"} 80 | 81 | embassy-sync = { version = "0.6.2", features = [] } 82 | embassy-executor = { version = "0.7.0", default-features = false, features = [] } 83 | embassy-time = { version = "0.4.0", features = [] } 84 | embassy-embedded-hal = { version = "0.3.0", optional = true, default-features = false, features = [] } 85 | 86 | embedded-hal-1 = { package = "embedded-hal", version = "1.0.0", default-features = false, optional = true } 87 | embedded-hal-0 = { package = "embedded-hal", version = "0.2.7", features = ["unproven"], optional = true } 88 | embedded-sdmmc = { version = "0.8.1", default-features = false, features = [], optional = true } 89 | embedded-io-async = { version = "0.6.1", default-features = false, features = [], optional = true } 90 | async-gcode = { version = "0.3.0", default-features = false, features = [], optional = true} 91 | 92 | heapless = { version = "0.8.0", default-features = false, features = [], optional = true } 93 | 94 | bitflags = { version = "2.8.0", default-features = false, features = ["bytemuck"] } 95 | cfg-if = { version = "1.0.0" } 96 | defmt = { version = "0.3.10", optional = true, default-features = false, features = [] } 97 | log = { version = "0.4.26", default-features = false, optional = true } 98 | strum = { version = "0.27.1", default-features = false, features = ["derive"] } 99 | 100 | num-traits = { version = "0.2.19", default-features = false, features = [] } 101 | micromath = { version = "2.1.0", default-features = false, features = [] } 102 | rust_decimal = { optional = true, version = "1.36.0", default-features = false, features = ["maths", "serde-with-str"] } 103 | rust_decimal_macros = { optional = true, version = "1.36.0" } 104 | ryu = {version = "1.0.19", default-features = false } 105 | 106 | [dev-dependencies] 107 | futures-test = { version = "0.3.31", default-features = false } 108 | tmc2209 = { version = "0.2.2"} 109 | env_logger = { version = "0.11.6" } -------------------------------------------------------------------------------- /printhor-hwa-common/README.md: -------------------------------------------------------------------------------- 1 | ![Minimum Rust: 1.85](https://img.shields.io/badge/Minimum%20Rust%20Version-1.85-green.svg) 2 | [![crates.io](https://img.shields.io/crates/v/printhor-hwa-common.svg)](https://crates.io/crates/printhor-hwa-common) 3 | [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) 4 | ![Discord Shield](https://discordapp.com/api/guilds/1169965662618259456/widget.png?style=shield) 5 | 6 |

Printhor: The highly reliable but not necessarily functional 3D printer firmware

7 | 8 |

If you are using this product or like the project, please this repository to show your support! 🤩

9 | 10 | # Overview 11 | 12 | Doc WIP: The Printhor HWA Common layer. This is a simple interface prerequisit for all printhor HWI implementations 13 | 14 | -------------------------------------------------------------------------------- /printhor-hwa-common/src/async_utils.rs: -------------------------------------------------------------------------------- 1 | //! Asynchronous utils/adapters 2 | //#region "Async wrapper" 3 | #[allow(unused)] 4 | use crate as hwa; 5 | 6 | pub trait AsyncWrapperWriter { 7 | fn wrapped_write( 8 | &mut self, 9 | data: &[u8], 10 | ) -> impl core::future::Future>; 11 | 12 | fn wrapped_flush(&mut self) -> impl core::future::Future; 13 | } 14 | 15 | pub struct SerialTxWrapper

{ 16 | peri: P, 17 | _ticks_by_word: u64, 18 | last_write_len: usize, 19 | } 20 | 21 | impl

SerialTxWrapper

{ 22 | pub const fn new(peri: P, baud_rate: u32) -> Self { 23 | let _ticks_by_word = 24 | embassy_time::Duration::from_micros(((1000000 / baud_rate) * 10) as u64).as_ticks(); 25 | Self { 26 | peri, 27 | _ticks_by_word, 28 | last_write_len: 0, 29 | } 30 | } 31 | } 32 | 33 | impl AsyncWrapperWriter for SerialTxWrapper

34 | where 35 | P: embedded_io_async::Write, 36 | { 37 | fn wrapped_write( 38 | &mut self, 39 | data: &[u8], 40 | ) -> impl core::future::Future> { 41 | async { 42 | self.last_write_len = data.len(); 43 | let r = self.peri.write_all(data).await; 44 | match &r { 45 | Ok(()) => { 46 | crate::trace!("Wrote {} bytes", data.len()); 47 | } 48 | Err(_e) => { 49 | crate::warn!("Error sending") 50 | } 51 | } 52 | embassy_time::Timer::after(embassy_time::Duration::from_millis(1)).await; 53 | Ok(data.len()) 54 | } 55 | } 56 | fn wrapped_flush(&mut self) -> impl core::future::Future { 57 | async { 58 | let d = embassy_time::Duration::from_ticks( 59 | self._ticks_by_word * ((self.last_write_len + 1) as u64), 60 | ); 61 | hwa::trace!( 62 | "flushing {} bytes ({}) us", 63 | self.last_write_len, 64 | d.as_micros() 65 | ); 66 | if self.last_write_len > 0 { 67 | //let _ = self.peri.flush().await; 68 | 69 | embassy_time::Timer::after(d).await; 70 | self.last_write_len = 0; 71 | } 72 | } 73 | } 74 | } 75 | 76 | pub trait AsyncWrapperReader { 77 | fn wrapped_write( 78 | &mut self, 79 | data: &[u8], 80 | ) -> impl core::future::Future>; 81 | 82 | fn wrapped_flush(&mut self) -> impl core::future::Future; 83 | } 84 | 85 | #[cfg(test)] 86 | mod test { 87 | use crate as hwa; 88 | use hwa::AsyncWrapperWriter; 89 | 90 | #[futures_test::test] 91 | async fn serial_wrapper_works() { 92 | struct DummyDevice {} 93 | impl DummyDevice { 94 | pub fn new() -> Self { 95 | Self {} 96 | } 97 | } 98 | impl embedded_io_async::ErrorType for DummyDevice { 99 | type Error = embedded_io_async::ErrorKind; 100 | } 101 | impl embedded_io_async::Write for DummyDevice { 102 | async fn write(&mut self, buf: &[u8]) -> Result { 103 | // Nothing 104 | Ok(buf.len()) 105 | } 106 | } 107 | 108 | let mut async_wrapper = hwa::SerialTxWrapper::new(DummyDevice::new(), 115200); 109 | 110 | async_wrapper.wrapped_write(b"hello world").await.unwrap(); 111 | async_wrapper.wrapped_flush().await; 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /printhor-hwa-common/src/kinematics/cartessian.rs: -------------------------------------------------------------------------------- 1 | //! Cartessian kinematics: Identity transform, basically. 2 | use crate::kinematics::WorldToSpaceTransformer; 3 | 4 | pub struct Identity; 5 | impl WorldToSpaceTransformer for Identity {} 6 | 7 | pub use Identity as DefaultTransformer; 8 | -------------------------------------------------------------------------------- /printhor-hwa-common/src/kinematics/core_xy.rs: -------------------------------------------------------------------------------- 1 | //! Core-XY Kinematics transformation 2 | use crate as hwa; 3 | use hwa::CoordSel; 4 | use hwa::kinematics::WorldToSpaceTransformer; 5 | use hwa::math::{Real, TVector}; 6 | 7 | pub struct CoreXYTransformer; 8 | impl WorldToSpaceTransformer for CoreXYTransformer { 9 | fn project_to_space(&self, _world_pos: &TVector) -> Result, ()> { 10 | cfg_if::cfg_if! { 11 | if #[cfg(all(feature = "with-x-axis", feature = "with-y-axis"))] { 12 | let mut space_pos = _world_pos.clone(); 13 | let _x = _world_pos.get_coord(CoordSel::X); 14 | let _y = _world_pos.get_coord(CoordSel::Y); 15 | if let Some(x) = _x { 16 | if let Some(y) = _y { 17 | space_pos.set_coord(CoordSel::X, Some(x + y)); 18 | space_pos.set_coord(CoordSel::Y, Some(x - y)); 19 | return Ok(space_pos) 20 | } 21 | } 22 | } 23 | else { 24 | compile_error!("CoreXY requires with-x-axis and with-y-axis"); 25 | } 26 | } 27 | Err(()) 28 | } 29 | 30 | fn project_to_world(&self, _space_coordinate: &TVector) -> Result, ()> { 31 | cfg_if::cfg_if! { 32 | if #[cfg(all(feature = "with-x-axis", feature = "with-y-axis"))] { 33 | let mut world_pos = _space_coordinate.clone(); 34 | let _x = _space_coordinate.get_coord(CoordSel::X); 35 | let _y = _space_coordinate.get_coord(CoordSel::Y); 36 | if let Some(x) = _x { 37 | if let Some(y) = _y { 38 | world_pos.set_coord(CoordSel::X, Some((x + y) / hwa::math::TWO)); 39 | world_pos.set_coord(CoordSel::Y, Some((x - y) / hwa::math::TWO)); 40 | return Ok(world_pos) 41 | } 42 | } 43 | } 44 | else { 45 | compile_error!("CoreXY requires with-x-axis and with-y-axis"); 46 | } 47 | } 48 | Err(()) 49 | } 50 | } 51 | 52 | pub use CoreXYTransformer as DefaultTransformer; 53 | -------------------------------------------------------------------------------- /printhor-hwa-common/src/kinematics/delta.rs: -------------------------------------------------------------------------------- 1 | //! Delta Kinematics transformation (TODO) 2 | use crate::kinematics::WorldToSpaceTransformer; 3 | 4 | struct Delta; 5 | impl WorldToSpaceTransformer for Delta {} 6 | -------------------------------------------------------------------------------- /printhor-hwa-common/src/kinematics/mod.rs: -------------------------------------------------------------------------------- 1 | //! Motion Kinematics strategies 2 | use crate as hwa; 3 | use hwa::math::{Real, TVector}; 4 | 5 | pub trait WorldToSpaceTransformer { 6 | /// Transforms a World position to (Work)space position. By default, identity transform applied 7 | /// 8 | /// Parameters: 9 | /// 10 | /// - `_world_pos`: The world position. 11 | fn project_to_space(&self, _world_pos: &TVector) -> Result, ()> { 12 | Ok(*_world_pos) 13 | } 14 | 15 | /// Transforms a (Work)space position to World position. By default, identity transform applied 16 | /// 17 | /// Parameters: 18 | /// 19 | /// - `_space_pos`: The Space position. 20 | fn project_to_world(&self, _space_pos: &TVector) -> Result, ()> { 21 | Ok(*_space_pos) 22 | } 23 | } 24 | 25 | cfg_if::cfg_if! { 26 | if #[cfg(feature = "with-motion-anthropomorphic-kinematics")] { 27 | pub mod anthropomorphic_3dof; 28 | } 29 | else if #[cfg(feature = "with-motion-core-xy-kinematics")] { 30 | pub mod core_xy; 31 | } 32 | else if #[cfg(feature = "with-motion-delta-kinematics")] { 33 | pub mod delta; 34 | } 35 | } 36 | pub mod cartessian; 37 | -------------------------------------------------------------------------------- /printhor-hwa-common/src/math/constants_f32.rs: -------------------------------------------------------------------------------- 1 | use crate::math::Real; 2 | pub const ZERO: Real = Real::from_f32(0.0); 3 | pub const HALF: Real = Real::from_f32(0.5); 4 | pub const ONE_AND_HALF: Real = Real::from_f32(1.5); 5 | pub const ONE: Real = Real::from_f32(1.0); 6 | pub const TWO: Real = Real::from_f32(2.0); 7 | pub const THREE: Real = Real::from_f32(3.0); 8 | pub const FOUR: Real = Real::from_f32(4.0); 9 | pub const SIX: Real = Real::from_f32(6.0); 10 | pub const SIXTH: Real = Real::from_f32(1.0 / 6.0); 11 | 12 | /// A very small value used to determine the difference between two floating point numbers. 13 | /// This constant is useful for comparisons to handle floating point negligible differences. 14 | pub const EPSILON: Real = Real::from_f32(f32::EPSILON); 15 | 16 | #[allow(unused)] 17 | pub const ONE_HUNDRED: Real = Real::from_f32(100.0); 18 | #[allow(unused)] 19 | pub const ONE_THOUSAND: Real = Real::from_f32(1000.0); 20 | #[allow(unused)] 21 | pub const ONE_MILLION: Real = Real::from_f32(1000000.0); 22 | #[allow(unused)] 23 | pub const PI: Real = Real::from_f32(core::f32::consts::PI); 24 | -------------------------------------------------------------------------------- /printhor-hwa-common/src/math/constants_f64.rs: -------------------------------------------------------------------------------- 1 | use crate::math::Real; 2 | use core::f64; 3 | 4 | pub const ZERO: Real = Real::from_f64(0.0); 5 | pub const HALF: Real = Real::from_f64(0.5); 6 | pub const ONE: Real = Real::from_f64(1.0); 7 | pub const ONE_AND_HALF: Real = Real::from_f64(1.5); 8 | pub const TWO: Real = Real::from_f64(2.0); 9 | pub const THREE: Real = Real::from_f64(3.0); 10 | pub const FOUR: Real = Real::from_f64(4.0); 11 | pub const SIX: Real = Real::from_f64(6.0); 12 | pub const SIXTH: Real = Real::from_f64(1.0 / 6.0); 13 | 14 | /// A very small value used to determine the difference between two floating point numbers. 15 | /// This constant is useful for comparisons to handle floating point negligible differences. 16 | pub const EPSILON: Real = Real::from_f64(f64::EPSILON); 17 | 18 | #[allow(unused)] 19 | pub const ONE_HUNDRED: Real = Real::from_f64(100.0); 20 | #[allow(unused)] 21 | pub const ONE_THOUSAND: Real = Real::from_f64(1000.0); 22 | #[allow(unused)] 23 | pub const ONE_MILLION: Real = Real::from_f64(1000000.0); 24 | 25 | pub const PI: Real = Real::from_f64(f64::consts::PI); 26 | -------------------------------------------------------------------------------- /printhor-hwa-common/src/math/constants_fixedpoint.rs: -------------------------------------------------------------------------------- 1 | use crate::math::Real; 2 | use rust_decimal_macros::dec; 3 | 4 | pub const ZERO: Real = Real::from_fixed(dec!(0.0)); 5 | pub const HALF: Real = Real::from_fixed(dec!(0.5)); 6 | pub const ONE: Real = Real::from_fixed(dec!(1.0)); 7 | pub const ONE_AND_HALF: Real = Real::from_fixed(dec!(1.5)); 8 | pub const TWO: Real = Real::from_fixed(dec!(2.0)); 9 | pub const THREE: Real = Real::from_fixed(dec!(3.0)); 10 | pub const FOUR: Real = Real::from_fixed(dec!(4.0)); 11 | pub const SIX: Real = Real::from_fixed(dec!(6.0)); 12 | pub const SIXTH: Real = Real::from_fixed(dec!(0.1666666666666666666666666666)); 13 | 14 | /// A very small value used to determine the difference between two decimal point numbers. 15 | /// ZERO in fixed point representation, for obvious reasons. 16 | pub const EPSILON: Real = Real::from_fixed(dec!(0.0000000000000000000000000001)); 17 | 18 | #[allow(unused)] 19 | pub const ONE_HUNDRED: Real = Real::from_fixed(dec!(100.0)); 20 | #[allow(unused)] 21 | pub const ONE_THOUSAND: Real = Real::from_fixed(dec!(1000.0)); 22 | #[allow(unused)] 23 | pub const ONE_MILLION: Real = Real::from_fixed(dec!(1000000.0)); 24 | 25 | pub const PI: Real = Real::from_fixed(rust_decimal::Decimal::PI); 26 | -------------------------------------------------------------------------------- /printhor-hwa-common/src/motion_broadcast.rs: -------------------------------------------------------------------------------- 1 | //! # Motion Broadcast Module 2 | //! 3 | //! This module [...] 4 | 5 | use crate as hwa; 6 | use crate::math; 7 | use crate::math::TVector; 8 | use embassy_sync::channel::Channel; 9 | //#region "MotionBroadcastEvent" 10 | 11 | #[derive(Copy, Clone, PartialEq, Debug)] 12 | #[cfg_attr(feature = "with-defmt", derive(defmt::Format))] 13 | pub enum MotionBroadcastEvent { 14 | Reset, 15 | Delta(MotionDelta), 16 | } 17 | 18 | #[derive(Copy, Clone, PartialEq, Debug)] 19 | #[cfg_attr(feature = "with-defmt", derive(defmt::Format))] 20 | pub struct MotionDelta { 21 | pub order_num: u32, 22 | pub micro_segment_id: u32, 23 | /// The microsegment relative time in seconds 24 | pub micro_segment_time: hwa::math::Real, 25 | /// The instant position in steps 26 | pub pos_wu: TVector, 27 | /// The instant position in steps 28 | pub pos_steps: TVector, 29 | } 30 | 31 | impl MotionDelta { 32 | pub const fn new() -> Self { 33 | Self { 34 | order_num: 0, 35 | micro_segment_id: 0, 36 | micro_segment_time: math::ZERO, 37 | pos_wu: TVector::new(), 38 | pos_steps: TVector::new(), 39 | } 40 | } 41 | } 42 | 43 | //#endregion 44 | 45 | //#region "Motion Broadcast Channel" 46 | 47 | /// The queue size of the motion broadcast channel 48 | #[const_env::from_env("MOTION_BROADCAST_CHANNEL_SIZE")] 49 | pub const MOTION_BROADCAST_CHANNEL_SIZE: usize = 2; 50 | 51 | pub type MotionBroadcastChannelType = 52 | Channel; 53 | 54 | pub struct GenericMotionBroadcastChannel 55 | where 56 | M: hwa::AsyncRawMutex + 'static, 57 | { 58 | channel: &'static MotionBroadcastChannelType, 59 | } 60 | 61 | impl GenericMotionBroadcastChannel 62 | where 63 | M: hwa::AsyncRawMutex + 'static, 64 | { 65 | pub const fn new(channel: &'static MotionBroadcastChannelType) -> Self { 66 | GenericMotionBroadcastChannel { channel } 67 | } 68 | } 69 | impl core::ops::Deref for GenericMotionBroadcastChannel 70 | where 71 | M: hwa::AsyncRawMutex + 'static, 72 | { 73 | type Target = MotionBroadcastChannelType; 74 | 75 | fn deref(&self) -> &Self::Target { 76 | self.channel 77 | } 78 | } 79 | 80 | impl Clone for GenericMotionBroadcastChannel 81 | where 82 | M: hwa::AsyncRawMutex + 'static, 83 | { 84 | fn clone(&self) -> Self { 85 | GenericMotionBroadcastChannel::new(self.channel) 86 | } 87 | } 88 | 89 | //#endregion 90 | -------------------------------------------------------------------------------- /printhor-hwa-common/src/persistent_state.rs: -------------------------------------------------------------------------------- 1 | //! A synchronization primitive for polling values from a task. 2 | //! Basically, a copy of embassy_sync::Signal with a dirty hack to not lose the consumed value 3 | use crate as hwa; 4 | use core::cell::Cell; 5 | use core::future::{Future, poll_fn}; 6 | use core::task::{Context, Poll, Waker}; 7 | use embassy_sync::blocking_mutex::Mutex; 8 | use hwa::AsyncRawMutex; 9 | 10 | #[allow(unused)] 11 | pub struct PersistentState 12 | where 13 | M: AsyncRawMutex, 14 | T: Send + Copy, 15 | { 16 | state: Mutex>>, 17 | } 18 | 19 | #[allow(unused)] 20 | #[derive(Clone)] 21 | enum State 22 | where 23 | T: Send + Copy, 24 | { 25 | None, 26 | Waiting(Waker), 27 | Signaled(T), 28 | } 29 | 30 | #[allow(unused)] 31 | impl PersistentState 32 | where 33 | M: AsyncRawMutex, 34 | T: Send + Copy, 35 | { 36 | /// Create a new `Signal`. 37 | pub const fn new() -> Self { 38 | Self { 39 | state: Mutex::new(Cell::new(State::None)), 40 | } 41 | } 42 | } 43 | 44 | #[allow(unused)] 45 | impl Default for PersistentState 46 | where 47 | M: AsyncRawMutex, 48 | T: Send + Copy, 49 | { 50 | fn default() -> Self { 51 | Self::new() 52 | } 53 | } 54 | 55 | #[allow(unused)] 56 | impl PersistentState 57 | where 58 | M: AsyncRawMutex, 59 | T: Send + Copy, 60 | { 61 | /// Mark this Signal as signaled. 62 | pub fn signal(&self, val: T) { 63 | self.state.lock(|cell| { 64 | let state = cell.replace(State::Signaled(val)); 65 | if let State::Waiting(waker) = state { 66 | waker.wake(); 67 | } 68 | }) 69 | } 70 | 71 | /// Remove the queued value in this `Config`, if any. 72 | pub fn reset(&self) { 73 | self.state.lock(|cell| { 74 | cell.set(State::None); 75 | }); 76 | } 77 | 78 | fn poll_wait(&self, cx: &mut Context<'_>) -> Poll { 79 | self.state.lock(|cell| { 80 | let state = cell.replace(State::None); 81 | 82 | match state { 83 | State::None => { 84 | //info!("Poll State::None"); 85 | cell.set(State::Waiting(cx.waker().clone())); 86 | Poll::Pending 87 | } 88 | State::Waiting(w) if w.will_wake(cx.waker()) => { 89 | //info!("Poll State::Waiting.1"); 90 | cell.set(State::Waiting(w)); 91 | Poll::Pending 92 | } 93 | State::Waiting(w) => { 94 | hwa::error!("Poll State::Waiting.2"); 95 | cell.set(State::Waiting(cx.waker().clone())); 96 | w.wake(); 97 | Poll::Pending 98 | } 99 | State::Signaled(r) => { 100 | //info!("Poll State::Signaled cv=({})", r); 101 | cell.set(State::Signaled(r)); 102 | Poll::Ready(r) 103 | } 104 | } 105 | }) 106 | } 107 | 108 | /// Future that completes when this Signal has been signaled. 109 | pub fn wait(&self) -> impl Future + '_ { 110 | poll_fn(move |cx| self.poll_wait(cx)) 111 | } 112 | 113 | /// non-blocking method to check whether this signal has been signaled. 114 | pub fn signaled(&self) -> bool { 115 | self.state.lock(|cell| { 116 | let state = cell.replace(State::None); 117 | 118 | let res = matches!(state, State::Signaled(_)); 119 | 120 | cell.set(state); 121 | 122 | res 123 | }) 124 | } 125 | } 126 | 127 | #[cfg(test)] 128 | mod tests { 129 | use crate as hwa; 130 | use core::future; 131 | use future::Future; 132 | use std::task::Poll; 133 | 134 | type MutexType = hwa::AsyncNoopMutexType; 135 | 136 | #[futures_test::test] 137 | async fn foundation_test() { 138 | let persistent_state: hwa::PersistentState = hwa::PersistentState::new(); 139 | assert_eq!(persistent_state.signaled(), false); 140 | persistent_state.signal(true); 141 | assert_eq!(persistent_state.signaled(), true); 142 | // The value stored is `true` 143 | assert_eq!(persistent_state.wait().await, true); 144 | // The value stored is still `true` 145 | assert_eq!(persistent_state.wait().await, true); 146 | persistent_state.reset(); 147 | 148 | let persistent_state: hwa::PersistentState = Default::default(); 149 | 150 | future::poll_fn(|cx| { 151 | let mut pinned_fut = core::pin::pin!(persistent_state.wait()); 152 | let p = pinned_fut.as_mut().poll(cx); 153 | assert!(p.is_pending()); 154 | 155 | persistent_state.signal(true); 156 | 157 | let p = pinned_fut.as_mut().poll(cx); 158 | assert!(!p.is_pending()); 159 | 160 | Poll::Ready(()) 161 | }) 162 | .await; 163 | } 164 | } 165 | -------------------------------------------------------------------------------- /printhor-hwa-common/src/thermistor.rs: -------------------------------------------------------------------------------- 1 | //! This module provides the `ThermistorProperties` struct, which holds essential parameters 2 | //! for calculating the temperature from a thermistor sensor. The parameters include the pull-up resistor value, 3 | //! the thermistor's nominal resistance, and the thermistor's Beta value. 4 | //! 5 | //! # Overview 6 | //! 7 | //! A thermistor is a type of resistor whose resistance varies significantly with temperature. 8 | //! The `ThermistorProperties` struct encapsulates three critical attributes necessary 9 | //! for calculating temperature from a thermistor: the pull-up resistor, the thermistor's 10 | //! nominal resistance, and its Beta constant. These parameters are typically used in 11 | //! temperature calculation formulas such as the Steinhart-Hart equation. 12 | //! 13 | //! # Example 14 | //! 15 | //! ```rust 16 | //! use printhor_hwa_common as hwa; 17 | //! use hwa::ThermistorProperties; 18 | //! 19 | //! // Create a new instance of ThermistorProperties with specific values 20 | //! let thermistor = ThermistorProperties::new(10000.0, 10000.0, 3950.0); 21 | //! 22 | //! println!("Pull-up Resistor: {} Ohms", thermistor.r_pullup); 23 | //! println!("Nominal Resistance: {} Ohms", thermistor.r_nominal); 24 | //! println!("Beta Value: {}", thermistor.beta); 25 | //! ``` 26 | //! 27 | //! # Fields 28 | //! 29 | //! * `r_pullup`: The resistance of the pull-up resistor in Ohms. This resistor is part of a voltage divider circuit with the thermistor. 30 | //! * `r_nominal`: The nominal resistance of the thermistor at a specific temperature (usually 25°C) in Ohms. 31 | //! * `beta`: The Beta constant of the thermistor, which is used in the Steinhart-Hart equation or Beta parameterization to calculate the temperature. 32 | 33 | /// The `ThermistorProperties` struct holds essential parameters for calculating the temperature 34 | /// from a thermistor sensor, including the pull-up resistor value, the thermistor's nominal 35 | /// resistance, and the thermistor's Beta value. 36 | /// 37 | /// # Fields 38 | /// 39 | /// * `r_pullup`: The resistance of the pull-up resistor in Ohms. This resistor is part of a 40 | /// voltage divider circuit with the thermistor. 41 | /// * `r_nominal`: The nominal resistance of the thermistor at a specific temperature (usually 25°C) 42 | /// in Ohms. 43 | /// * `beta`: The Beta constant of the thermistor, which is used in the Steinhart-Hart equation or 44 | /// Beta parameterization to calculate the temperature. 45 | pub struct ThermistorProperties { 46 | // Pull-up resistor in Ohms 47 | pub r_pullup: f32, 48 | // The nominal resistance of the thermistor in Ohms at a specific temperature (usually 25°C) 49 | pub r_nominal: f32, 50 | // Thermistor Beta value 51 | pub beta: f32, 52 | } 53 | 54 | impl ThermistorProperties { 55 | pub const fn new(r_pullup: f32, r_nominal: f32, beta: f32) -> Self { 56 | Self { 57 | r_pullup, 58 | r_nominal, 59 | beta, 60 | } 61 | } 62 | } 63 | 64 | #[cfg(test)] 65 | mod tests { 66 | use crate as hwa; 67 | use hwa::ThermistorProperties; 68 | #[test] 69 | fn test_thermistor_properties() { 70 | let thermistor = ThermistorProperties::new(10000.0, 10000.0, 3950.0); 71 | log::info!("Pull-up Resistor: {} Ohms", thermistor.r_pullup); 72 | log::info!("Nominal Resistance: {} Ohms", thermistor.r_nominal); 73 | log::info!("Beta Value: {}", thermistor.beta); 74 | assert_eq!(thermistor.r_pullup, 10000.0); 75 | assert_eq!(thermistor.r_nominal, 10000.0); 76 | assert_eq!(thermistor.beta, 3950.0); 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /printhor-hwa-common/src/traits.rs: -------------------------------------------------------------------------------- 1 | //! The printhor traits (Work In Progress). 2 | //! This module expects to define or re-export all the HWI devices interfaces shall satisfy 3 | #[allow(unused)] 4 | use crate as hwa; 5 | 6 | cfg_if::cfg_if! { 7 | if #[cfg(any( 8 | feature = "with-serial-usb", 9 | feature = "with-serial-port-1", 10 | feature = "with-serial-port-2", 11 | feature = "with-sd-card", 12 | ))] { 13 | pub use async_gcode::ByteStream as GCodeByteStream; 14 | } 15 | } 16 | 17 | cfg_if::cfg_if! { 18 | if #[cfg(feature = "with-motion")] { 19 | pub trait StepActuatorTrait { 20 | fn enable_all_steppers(&mut self) { 21 | self.set_enabled(hwa::CoordSel::all_axis(), true) 22 | } 23 | #[cfg(feature = "with-x-axis")] 24 | fn enable_x_stepper(&mut self) { 25 | self.set_enabled(hwa::CoordSel::X, true) 26 | } 27 | #[cfg(feature = "with-y-axis")] 28 | fn enable_y_stepper(&mut self) { 29 | self.set_enabled(hwa::CoordSel::Y, true) 30 | } 31 | #[cfg(feature = "with-z-axis")] 32 | fn enable_z_stepper(&mut self) { 33 | self.set_enabled(hwa::CoordSel::Z, true) 34 | } 35 | #[cfg(feature = "with-e-axis")] 36 | fn enable_e_stepper(&mut self) { 37 | self.set_enabled(hwa::CoordSel::E, true) 38 | } 39 | #[cfg(feature = "with-x-axis")] 40 | fn disable_x_stepper(&mut self) { 41 | self.set_enabled(hwa::CoordSel::X, false) 42 | } 43 | #[cfg(feature = "with-y-axis")] 44 | fn disable_y_stepper(&mut self) { 45 | self.set_enabled(hwa::CoordSel::Y, false) 46 | } 47 | #[cfg(feature = "with-z-axis")] 48 | fn disable_z_stepper(&mut self) { 49 | self.set_enabled(hwa::CoordSel::Z, false) 50 | } 51 | #[cfg(feature = "with-e-axis")] 52 | fn disable_e_stepper(&mut self) { 53 | self.set_enabled(hwa::CoordSel::E, false) 54 | } 55 | fn disable_all_steppers(&mut self) { 56 | self.set_enabled(hwa::CoordSel::all_axis(), false) 57 | } 58 | 59 | fn disable(&mut self, _channels: hwa::CoordSel) 60 | { 61 | self.set_enabled(hwa::CoordSel::all_axis(), false) 62 | } 63 | 64 | fn set_enabled(&mut self, _channels: hwa::CoordSel, _enabled: bool); 65 | fn set_forward_direction(&mut self, _channels: hwa::CoordSel, _mask: hwa::CoordSel); 66 | fn step_toggle(&mut self, _channels: hwa::CoordSel); 67 | fn step_high(&mut self, _channels: hwa::CoordSel); 68 | fn step_low(&mut self, _channels: hwa::CoordSel); 69 | fn endstop_triggered(&mut self, _channels: hwa::CoordSel) -> bool; 70 | } 71 | } 72 | } 73 | 74 | cfg_if::cfg_if! { 75 | if #[cfg(feature = "with-trinamic")] { 76 | pub trait TrinamicUartTrait { 77 | /// Selects the TMC stepper to address given coordinate axis. 78 | /// 79 | /// Normally, single uart is shared for all them. However, if more than one is needed, this 80 | /// method selects the one active 81 | fn select_stepper_of_axis(&mut self, _channel: hwa::CoordSel) -> Result<(), ()> { 82 | Ok(()) 83 | } 84 | /// Gets the TMC Adddress of given coordinate axis 85 | fn get_tmc_address(&self, _channel: hwa::CoordSel) -> Result; 86 | fn read_until_idle(&mut self, buffer: &mut [u8]) -> impl core::future::Future>; 87 | fn write(&mut self,buffer: &[u8]) -> impl core::future::Future>; 88 | fn flush(&mut self) -> impl core::future::Future>; 89 | } 90 | 91 | 92 | } 93 | } 94 | 95 | cfg_if::cfg_if! { 96 | if #[cfg(any( 97 | feature = "with-hot-end", feature = "with-hot-bed", 98 | feature = "with-fan-layer", feature="with-fan-extra-1", 99 | feature = "with-laser", feature = "with-probe" 100 | ))] { 101 | pub use embedded_hal_0::Pwm; 102 | } 103 | } 104 | 105 | cfg_if::cfg_if! { 106 | if #[cfg(any(feature = "with-hot-end", feature = "with-hot-bed"))] { 107 | 108 | /// A work in progress unified trait for `ADC`s 109 | pub trait UnifiedAdc16 { 110 | 111 | type VRefPin; 112 | type SamplePin; 113 | 114 | fn read_vref(&mut self) -> impl core::future::Future> { 115 | async { 116 | Err(()) 117 | } 118 | } 119 | 120 | fn read_adc(&mut self, pin: &mut Self::SamplePin) -> impl core::future::Future; 121 | } 122 | } 123 | } 124 | 125 | cfg_if::cfg_if! { 126 | if #[cfg(feature = "with-sd-card")] { 127 | pub use embedded_sdmmc::BlockDevice as SDBlockDevice; 128 | 129 | pub trait AsyncSDBlockDevice: embedded_sdmmc::BlockDevice { 130 | fn do_retain(&self) -> impl core::future::Future>; 131 | fn do_release(&self) -> Result<(), ()>; 132 | } 133 | } 134 | } 135 | -------------------------------------------------------------------------------- /printhor-hwa-common/src/uart.rs: -------------------------------------------------------------------------------- 1 | #![doc = r" 2 | This module provides serial communication (UART) elements. 3 | 4 | ## Enums 5 | 6 | ### `SerialError` 7 | Represents possible errors that can occur during serial communication. 8 | - `Timeout`: Indicates a read or write operation has timed out. 9 | - `Framing`: Indicates framing error, where the stop bit was not as expected. 10 | "] 11 | 12 | //! TODO: [Work In progress] Software serial communication (UART) 13 | #[allow(unused)] 14 | use crate as hwa; 15 | 16 | /// Serial communication error type 17 | #[derive(Debug, PartialEq)] 18 | #[cfg_attr(feature = "with-defmt", derive(defmt::Format))] 19 | pub enum SerialError { 20 | /// Timeout 21 | Timeout, 22 | /// Framing error 23 | Framing, 24 | } 25 | -------------------------------------------------------------------------------- /printhor-hwa-utils/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "printhor-hwa-utils" 3 | version = "0.0.4" 4 | edition = "2024" 5 | authors = ["Carlos Barrales Ruiz "] 6 | description = "Printor utillery" 7 | readme = "README.md" 8 | repository = "https://github.com/cbruiz/printhor" 9 | keywords = ["hardware", "abstration", "library", "printhor"] 10 | categories = ["hardware-support"] 11 | license = "MIT" 12 | documentation = "https://docs.rs/printhor-static-cell" 13 | homepage = "https://github.com/cbruiz/printhor" 14 | 15 | [lib] 16 | 17 | [features] 18 | default = ["std", "with-log"] 19 | std = ["printhor-hwa-common-macros/std", "critical-section/std", "futures-test/std", "with-log"] 20 | with-log = ["log"] 21 | with-defmt = ["defmt"] 22 | 23 | [dependencies] 24 | printhor-hwa-common-macros = { version = "0", default-features = false, path = "../printhor-hwa-common-macros" } 25 | portable-atomic = { version = "1.11.0", default-features = false, features = ["critical-section"] } 26 | static_cell = { version = "2.1.0", default-features = false, features = [] } 27 | const_env = { version = "0.1.2"} 28 | embassy-sync = { version = "0.6.2", features = [] } 29 | cfg-if = { version = "1.0.0" } 30 | defmt = { version = "0.3.10", optional = true, default-features = false, features = [] } 31 | log = { version = "0.4.26", default-features = false, optional = true } 32 | critical-section = { version = "1.2.0", default-features = false, features = []} 33 | 34 | [dev-dependencies] 35 | futures-test = { version = "0.3.31", default-features = false } 36 | -------------------------------------------------------------------------------- /printhor-hwa-utils/README.md: -------------------------------------------------------------------------------- 1 | ![Minimum Rust: 1.85](https://img.shields.io/badge/Minimum%20Rust%20Version-1.85-green.svg) 2 | [![crates.io](https://img.shields.io/crates/v/printhor-hwa-common.svg)](https://crates.io/crates/printhor-hwa-common) 3 | [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) 4 | ![Discord Shield](https://discordapp.com/api/guilds/1169965662618259456/widget.png?style=shield) 5 | 6 |

Printhor: The highly reliable but not necessarily functional 3D printer firmware

7 | 8 |

If you are using this product or like the project, please this repository to show your support! 🤩

9 | 10 | # Overview 11 | 12 | Doc WIP: The Printhor HWA Utillery layer. This is a simple interface prerequisit for all printhor HWA implementations 13 | 14 | -------------------------------------------------------------------------------- /printhor/.cargo/config.toml: -------------------------------------------------------------------------------- 1 | [env] 2 | 3 | ########################### 4 | ## Configuration - BEGIN ## 5 | ########################### 6 | 7 | ## Heater and bed configuration (thermistor based) ## 8 | 9 | # [Depends on the board] the pull-up resistance of hot-end sensor circuit in ohms 10 | HOT_END_THERM_PULL_UP_RESISTANCE = "4685.0" 11 | # Nominal resistance of hot-end NTC thermistor at 25ºC 12 | HOT_END_THERM_NOMINAL_RESISTANCE = "100000.0" 13 | # BETA value of hot-end NTC thermistor 14 | HOT_END_THERM_BETA = "3950.0" 15 | 16 | # [Depends on the board] the pull-up resistance of hot-bed sensor circuit in ohms 17 | HOT_BED_THERM_PULL_UP_RESISTANCE = "4685.0" 18 | # Nominal resistance of hot-bed NTC thermistor at 25ºC 19 | HOT_BED_THERM_NOMINAL_RESISTANCE = "100000.0" 20 | # BETA value of hot-bed NTC thermistor 21 | HOT_BED_THERM_BETA = "3950.0" 22 | 23 | ## Pure technical stuff ## 24 | 25 | DEFMT_LOG = "info" 26 | RUST_LOG = "warn" 27 | ASYNC_STD_THREAD_COUNT = "1" 28 | EMBASSY_USB_MAX_INTERFACE_COUNT = "2" 29 | 30 | ########################### 31 | ## Configuration - END ## 32 | ########################### 33 | 34 | [target.'cfg(all(any(board="nucleo64-f410rb"), target_arch="arm", target_os="none"))'] 35 | runner = "probe-rs run --chip STM32F410RBTx --disable-progressbars --connect-under-reset --log-format {L}{s}" 36 | #rustflags = [ "-C", "linker=flip-link" ] 37 | #linker = "flip-link" 38 | 39 | [target.'cfg(all(any(board="nucleo64-l476rg"), target_arch="arm", target_os="none"))'] 40 | runner = "probe-rs run --chip STM32L476RGTx --disable-progressbars" 41 | #rustflags = [ "-C", "linker=flip-link" ] 42 | #linker = "flip-link" 43 | 44 | [target.'cfg(all(any(board="skr_mini_e3_v2"), target_arch="arm", target_os="none"))'] 45 | runner = "probe-rs run --chip STM32F103RC --disable-progressbars --log-format {L}{s} --base-address 08007000 --connect-under-reset" 46 | 47 | [target.'cfg(all(any(board="skr_mini_e3_v2-no-bootloader"), target_arch="arm", target_os="none"))'] 48 | runner = "echo echo PLEASE DON'T probe-rs run --chip STM32F103RC --disable-progressbars --log-format {L}{s}" 49 | 50 | [target.'cfg(all(any(board="skr_mini_e3_v3"), target_arch="arm", target_os="none"))'] 51 | runner = "probe-rs run --chip STM32G0B1RETx --disable-progressbars --log-format {L}{s} --base-address 08002000" 52 | 53 | [target.'cfg(all(any(board="skr_mini_e3_v3-no-bootloader"), target_arch="arm", target_os="none"))'] 54 | runner = "echo PLEASE DON'T probe-rs run --chip STM32G0B1RETx --disable-progressbars --log-format {L}{s}" 55 | 56 | [target.'cfg(all(any(board="mks_robin_nano"), target_arch="arm", target_os="none"))'] 57 | runner = "probe-rs run --chip STM32F407VETx --disable-progressbars --log-format {L}{s} --base-address 08007000 --connect-under-reset" 58 | 59 | [target.'cfg(all(any(board="mks_robin_nano-no-bootloader"), target_arch="arm", target_os="none"))'] 60 | runner = "echo PLEASE DON'T probe-rs run --chip STM32F407VETx --disable-progressbars --log-format {L}{s}" 61 | 62 | [target.'cfg(all(any(board="rp2040"), target_arch="arm", target_os="none"))'] 63 | #runner = "probe-rs run --chip RP2040" 64 | runner = "elf2uf2-rs --deploy --serial --verbose" 65 | 66 | [build] 67 | rustdocflags = [ "--html-in-header", "doc/doc.header.html" ] 68 | 69 | [profile.dev.package."*"] 70 | codegen-units = 1 71 | incremental = false 72 | #opt-level = "s" # Set to "s" or even disable to play with GDB 73 | debug = 2 74 | debug-assertions = true 75 | overflow-checks = true 76 | 77 | [profile.release.package."*"] 78 | codegen-units = 1 79 | incremental = false 80 | opt-level = "z" 81 | debug = 2 82 | debug-assertions = true 83 | overflow-checks = true 84 | 85 | [profile.release-opt.package."*"] 86 | codegen-units = 1 87 | incremental = false 88 | opt-level = "z" 89 | debug = 0 90 | debug-assertions = false 91 | overflow-checks = false 92 | -------------------------------------------------------------------------------- /printhor/build.rs: -------------------------------------------------------------------------------- 1 | #[allow(unused)] 2 | use std::io::Write; 3 | 4 | fn main() { 5 | let doc_in = std::path::PathBuf::from(std::env::var_os("CARGO_MANIFEST_DIR").unwrap()) 6 | .join("img") 7 | .join("contract-class-diagram.png"); 8 | let out = std::path::PathBuf::from(std::env::var_os("CARGO_MANIFEST_DIR").unwrap()) 9 | .join("..") 10 | .join("target") 11 | .join("doc") 12 | .join("img"); 13 | std::fs::create_dir_all(&out).unwrap(); 14 | std::fs::copy(doc_in, out.join("contract-class-diagram.png")).unwrap(); 15 | 16 | cfg_if::cfg_if! { 17 | if #[cfg(feature="native")] { 18 | 19 | } 20 | else if #[cfg(feature="nucleo64-f410rb")] { 21 | println!("cargo:rustc-link-arg-bins=--nmagic"); 22 | println!("cargo:rustc-link-arg-bins=-Tlink.x"); 23 | } 24 | else if #[cfg(feature="nucleo64-l476rg")] { 25 | println!("cargo:rustc-link-arg-bins=--nmagic"); 26 | println!("cargo:rustc-link-arg-bins=-Tlink.x"); 27 | } 28 | else if #[cfg(feature="rp-2040")] { 29 | let out = &std::path::PathBuf::from(std::env::var_os("OUT_DIR").unwrap()); 30 | let memory_x_path = std::path::PathBuf::from(std::env::var_os("CARGO_MANIFEST_DIR").unwrap()) 31 | .join("..") 32 | .join("hwi-boards") 33 | .join("printhor-hwi_rp_2040") 34 | .join("rpi-pico") 35 | .join("memory.x"); 36 | let memory_x_path_str = memory_x_path.as_path().to_str().unwrap(); 37 | std::fs::File::create(out.join("memory.x")) 38 | .unwrap() 39 | .write_all(std::fs::read(memory_x_path_str).unwrap().as_slice()) 40 | .unwrap(); 41 | println!("cargo:rustc-link-search={}", out.display()); 42 | println!("cargo:rerun-if-changed={}", memory_x_path_str); 43 | 44 | println!("cargo:rustc-link-arg-bins=--nmagic"); 45 | println!("cargo:rustc-link-arg-bins=-Tlink.x"); 46 | println!("cargo:rustc-link-arg-bins=-Tlink-rp.x"); 47 | } 48 | else if #[cfg(feature="mks_robin_nano")] { 49 | cfg_if::cfg_if! { 50 | if #[cfg(feature="without-bootloader")] { 51 | } 52 | else { 53 | let out = &std::path::PathBuf::from(std::env::var_os("OUT_DIR").unwrap()); 54 | let memory_x_path = std::path::PathBuf::from(std::env::var_os("CARGO_MANIFEST_DIR").unwrap()) 55 | .join("..") 56 | .join("hwi-boards") 57 | .join("printhor-hwi_mks_robin_nano") 58 | .join("mks_robin_nano_3_1") 59 | .join("memory.x"); 60 | let memory_x_path_str = memory_x_path.as_path().to_str().unwrap(); 61 | std::fs::File::create(out.join("memory.x")) 62 | .unwrap() 63 | .write_all(std::fs::read(memory_x_path_str).unwrap().as_slice()) 64 | .unwrap(); 65 | println!("cargo:rustc-link-search={}", out.display()); 66 | println!("cargo:rerun-if-changed={}", memory_x_path_str); 67 | } 68 | } 69 | 70 | println!("cargo:rustc-link-arg-bins=--nmagic"); 71 | println!("cargo:rustc-link-arg-bins=-Tlink.x"); 72 | } 73 | else if #[cfg(feature="skr_mini_e3_v3")] { 74 | cfg_if::cfg_if! { 75 | if #[cfg(feature="without-bootloader")] { 76 | } 77 | else { 78 | let out = &std::path::PathBuf::from(std::env::var_os("OUT_DIR").unwrap()); 79 | let memory_x_path = std::path::PathBuf::from(std::env::var_os("CARGO_MANIFEST_DIR").unwrap()) 80 | .join("..") 81 | .join("hwi-boards") 82 | .join("printhor-hwi_skr_mini_e3") 83 | .join("skr_mini_e3_v3") 84 | .join("memory.x"); 85 | let memory_x_path_str = memory_x_path.as_path().to_str().unwrap(); 86 | std::fs::File::create(out.join("memory.x")) 87 | .unwrap() 88 | .write_all(std::fs::read(memory_x_path_str).unwrap().as_slice()) 89 | .unwrap(); 90 | println!("cargo:rustc-link-search={}", out.display()); 91 | println!("cargo:rerun-if-changed={}", memory_x_path_str); 92 | } 93 | } 94 | println!("cargo:rustc-link-arg-bins=--nmagic"); 95 | println!("cargo:rustc-link-arg-bins=-Tlink.x"); 96 | } 97 | else { 98 | //compile_error!("No board set"); 99 | } 100 | } 101 | 102 | #[cfg(feature = "with-defmt")] 103 | println!("cargo:rustc-link-arg-bins=-Tdefmt.x"); 104 | } 105 | -------------------------------------------------------------------------------- /printhor/design/architecture.md: -------------------------------------------------------------------------------- 1 | # Architecture 2 | 3 | ## Class diagram 4 | ![image](../img/contract-class-diagram.png) 5 | -------------------------------------------------------------------------------- /printhor/img/contract-class-diagram.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cbruiz/printhor/1d04b31055f58224f909b2ea484eee94719cd425/printhor/img/contract-class-diagram.png -------------------------------------------------------------------------------- /printhor/img/motion_plan.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cbruiz/printhor/1d04b31055f58224f909b2ea484eee94719cd425/printhor/img/motion_plan.pdf -------------------------------------------------------------------------------- /printhor/img/motion_plan.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cbruiz/printhor/1d04b31055f58224f909b2ea484eee94719cd425/printhor/img/motion_plan.png -------------------------------------------------------------------------------- /printhor/img/motion_plan_old.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cbruiz/printhor/1d04b31055f58224f909b2ea484eee94719cd425/printhor/img/motion_plan_old.png -------------------------------------------------------------------------------- /printhor/rust-toolchain.toml: -------------------------------------------------------------------------------- 1 | [toolchain] 2 | channel = "stable" 3 | components = [ "rustfmt", "rustc-dev" ] 4 | #targets = [ "thumbv6m-none-eabi", "thumbv7em-none-eabi", "thumbv7em-none-eabihf", "thumbv7m-none-eabi" ] 5 | profile = "minimal" 6 | -------------------------------------------------------------------------------- /printhor/src/bin/helpers/mod.rs: -------------------------------------------------------------------------------- 1 | //! Miscellaneous utils 2 | use crate::hwa; 3 | 4 | /// Converts an `async-gcode` decimal representation in the format of tuple (signed_decimal: i32, number_of_decimal_digits: u8) to [hwa::math::Real] 5 | #[inline] 6 | pub fn to_fixed(val: (i32, u8)) -> hwa::math::Real { 7 | hwa::math::Real::new(val.0.into(), val.1 as u32) 8 | } 9 | -------------------------------------------------------------------------------- /printhor/src/bin/hwa/adapters/mod.rs: -------------------------------------------------------------------------------- 1 | //! Common peripheral/protocol adapters 2 | #[cfg(all(feature = "with-spi", feature = "sd-card-uses-spi"))] 3 | mod spi; 4 | 5 | #[cfg(all(feature = "with-spi", feature = "sd-card-uses-spi"))] 6 | pub use spi::SPIAdapter; 7 | -------------------------------------------------------------------------------- /printhor/src/bin/hwa/controllers/adc_controller.rs: -------------------------------------------------------------------------------- 1 | //! TODO: This feature is still in incubation 2 | use crate::hwa; 3 | use hwa::AsyncMutexStrategy; 4 | use hwa::StaticAsyncController; 5 | use hwa::math; 6 | use hwa::traits::UnifiedAdc16; 7 | 8 | /// A controller for managing ADCs. 9 | pub struct GenericAdcController 10 | where 11 | H: AsyncMutexStrategy + 'static, 12 | H::Resource: hwa::traits::UnifiedAdc16 + 'static, 13 | { 14 | adc: StaticAsyncController, 15 | adc_pin: ::SamplePin, 16 | default_sample: u16, 17 | v_ratio: f32, 18 | } 19 | 20 | impl GenericAdcController 21 | where 22 | H: AsyncMutexStrategy + 'static, 23 | H::Resource: hwa::traits::UnifiedAdc16 + 'static, 24 | { 25 | pub fn new( 26 | adc: StaticAsyncController, 27 | adc_pin: ::SamplePin, 28 | default_sample: u16, 29 | ) -> Self { 30 | Self { 31 | adc, 32 | adc_pin, 33 | default_sample, 34 | v_ratio: 1.0f32, 35 | } 36 | } 37 | 38 | pub async fn init(&mut self, v_ref_default: u16) { 39 | hwa::debug!("Initializing ADC..."); 40 | 41 | let mut adc_mg = self.adc.lock().await; 42 | let sample = adc_mg.read_vref().await.unwrap_or(self.default_sample); 43 | 44 | self.v_ratio = f32::from(v_ref_default) / (f32::from(sample) * 1000.0f32); 45 | hwa::info!( 46 | "ADC Initialized: ref_int_default = {:?}, vref_sample = {:?} | v_ratio = {:?} volts/adc_unit", 47 | v_ref_default, 48 | sample, 49 | math::Real::from_f32(self.v_ratio), 50 | ); 51 | } 52 | 53 | pub async fn read_sample(&mut self) -> u16 { 54 | let mut adc_mg = self.adc.lock().await; 55 | adc_mg.read_adc(&mut self.adc_pin).await 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /printhor/src/bin/hwa/controllers/mod.rs: -------------------------------------------------------------------------------- 1 | //! The printhor controllers. A set of abstractions over the [hwa::hwi] and HAL 2 | #[allow(unused)] 3 | use crate::hwa; 4 | 5 | #[cfg(feature = "with-sd-card")] 6 | pub mod sd_card_controller; 7 | 8 | #[cfg(feature = "with-sd-card")] 9 | pub use sd_card_controller::GenericSDCardController; 10 | #[cfg(feature = "with-print-job")] 11 | mod printer_controller; 12 | 13 | #[cfg(feature = "with-print-job")] 14 | pub use printer_controller::*; 15 | 16 | #[cfg(feature = "with-motion")] 17 | pub mod motion_control; 18 | 19 | #[cfg(feature = "with-probe")] 20 | mod servo_controller; 21 | #[cfg(feature = "with-trinamic")] 22 | mod trinamic_controller; 23 | 24 | cfg_if::cfg_if! { 25 | if #[cfg(any(feature = "with-hot-end", feature = "with-hot-bed"))] { 26 | mod adc_controller; 27 | pub use adc_controller::GenericAdcController; 28 | mod heater_controller; 29 | pub use heater_controller::HeaterController; 30 | } 31 | } 32 | 33 | cfg_if::cfg_if! { 34 | if #[cfg(any(feature = "with-hot-end", feature = "with-hot-bed", 35 | feature = "with-fan-layer", feature = "with-fan-extra-1", feature = "with-laser"))] { 36 | mod pwm_controller; 37 | pub use pwm_controller::GenericPwmController; 38 | } 39 | } 40 | 41 | #[cfg(feature = "with-trinamic")] 42 | pub use trinamic_controller::TrinamicController; 43 | 44 | #[cfg(feature = "with-motion")] 45 | pub use motion_control::*; 46 | 47 | #[cfg(feature = "with-probe")] 48 | pub use servo_controller::GenericServoController; 49 | 50 | #[cfg(feature = "with-probe")] 51 | pub use servo_controller::ProbeTrait; 52 | -------------------------------------------------------------------------------- /printhor/src/bin/hwa/controllers/motion_control/mod.rs: -------------------------------------------------------------------------------- 1 | //! The module for motion management (configuration, state, parameters, constraints, software elements, devices and drivers) 2 | mod motion_config; 3 | 4 | /// The module to model a motion segment instance. 5 | mod motion_segment; 6 | 7 | /// The module to hold the real time motion status (positions mainly). 8 | mod motion_status; 9 | 10 | /// The module to hold the planned segment ring-buffer. 11 | mod motion_ring_buffer; 12 | 13 | /// The module with the motion planner logic. 14 | mod motion_planner; 15 | 16 | /// The module for motion interpolation. 17 | mod motion_interpolation; 18 | 19 | /// The module for motion timing (formally: the StepPlan generator). 20 | mod motion_step_plan; 21 | 22 | /// The module to provide a software time driver to schedule multi-axis steps with variable timings each at fixed rate 23 | mod motion_step_plan_executor; 24 | 25 | /// The module to drive the step pins. 26 | mod motion_step_actuator; 27 | 28 | use crate::hwa; 29 | pub use motion_config::*; 30 | pub use motion_interpolation::*; 31 | pub use motion_planner::*; 32 | pub use motion_segment::*; 33 | pub use motion_status::*; 34 | pub use motion_step_actuator::*; 35 | pub use motion_step_plan::*; 36 | pub use motion_step_plan_executor::*; 37 | 38 | pub(in crate::hwa) use motion_ring_buffer::RingBuffer; 39 | 40 | /// Represents a scheduled move in the motion system. 41 | pub enum ScheduledMove { 42 | /// A movement segment. 43 | Move(Segment, u32), 44 | /// A homing action. 45 | Homing(u32), 46 | /// A dwell action. 47 | Dwell(Option, u32), 48 | /// A set-position action. 49 | SetPosition(Position, u32), 50 | } 51 | 52 | /// Types of movements in the motion system. 53 | #[derive(Clone, Copy)] 54 | pub enum MovType { 55 | /// A normal move with deferred action and communication channel. 56 | Move(hwa::DeferAction, hwa::CommChannel), 57 | /// A homing move with a communication channel. 58 | Homing(hwa::CommChannel), 59 | /// A dwell action with a communication channel. 60 | Dwell(hwa::CommChannel), 61 | /// A set-position action with a communication channel. 62 | SetPosition(hwa::CommChannel), 63 | } 64 | 65 | /// Represents an entry in the motion plan. 66 | #[derive(Clone, Copy)] 67 | pub enum PlanEntry { 68 | /// An empty plan entry. 69 | Empty, 70 | /// A planned move tuple. 71 | /// 72 | /// *_1: Segment* - The motion segment. 73 | /// 74 | /// *_2: CommChannel* - The input channel requesting the move. 75 | /// 76 | /// *_3: bool* - Indicates if motion is deferred or not. 77 | PlannedMove(Segment, hwa::DeferAction, hwa::CommChannel, bool, u32), 78 | /// A homing action request. 79 | /// 80 | /// *_1: CommChannel* - The input channel requesting the move. 81 | /// 82 | /// *_2: bool* - Indicates if motion is deferred or not. 83 | Homing(hwa::CommChannel, bool, u32), 84 | /// A Dwell action request. 85 | /// 86 | /// *_1: CommChannel* - The input channel requesting the action. 87 | /// 88 | /// *_2: Option<u32>* - The number of milliseconds to delay. 89 | /// 90 | /// *_3: bool* - Indicates if motion is deferred or not. 91 | Dwell(hwa::CommChannel, Option, bool, u32), 92 | 93 | /// A SetPosition action request. 94 | /// 95 | /// *_1: CommChannel* - The input channel requesting the action. 96 | /// 97 | /// *_2: Position* - The position to set. 98 | /// 99 | /// *_3: bool* - Indicates if motion is deferred or not. 100 | SetPosition(hwa::CommChannel, Position, bool, u32), 101 | 102 | /// An executing move. 103 | /// 104 | /// *_1: MovType* - The type of the move. 105 | /// 106 | /// *_2: bool* - Indicates if motion is deferred or not. 107 | Executing(MovType, bool, u32), 108 | } 109 | 110 | /// Provides the default value for `PlanEntry`, which is `PlanEntry::Empty`. 111 | impl Default for PlanEntry { 112 | fn default() -> Self { 113 | PlanEntry::Empty 114 | } 115 | } 116 | 117 | #[cfg(test)] 118 | mod test { 119 | use crate::hwa::controllers::PlanEntry; 120 | 121 | #[test] 122 | fn plan_entry_test() { 123 | let sz = size_of::(); 124 | assert!( 125 | sz < 1024, 126 | "Plan entry is not so big ({} bytes. max: 1024)", 127 | sz 128 | ); 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /printhor/src/bin/hwa/controllers/motion_control/motion_step_actuator.rs: -------------------------------------------------------------------------------- 1 | //! Motion step actuator controller/adaptor 2 | use crate::hwa; 3 | use hwa::SyncMutexStrategy; 4 | 5 | pub struct StepActuatorController { 6 | actuator: hwa::StaticSyncController, 7 | #[cfg(feature = "with-motion-broadcast")] 8 | pub broadcast_channel: hwa::types::MotionBroadcastChannel, 9 | } 10 | 11 | impl StepActuatorController { 12 | pub const fn new( 13 | actuator: hwa::StaticSyncController, 14 | #[cfg(feature = "with-motion-broadcast")] 15 | broadcast_channel: hwa::types::MotionBroadcastChannel, 16 | ) -> Self { 17 | Self { 18 | actuator, 19 | #[cfg(feature = "with-motion-broadcast")] 20 | broadcast_channel, 21 | } 22 | } 23 | 24 | pub fn enable_steppers(&self, channels: hwa::CoordSel) { 25 | use hwa::traits::StepActuatorTrait; 26 | hwa::debug!("enable_steppers {:?}", channels); 27 | self.actuator 28 | .apply_mut(|pins| pins.set_enabled(channels, true)) 29 | } 30 | 31 | pub fn set_forward_direction(&self, channels: hwa::CoordSel, mask: hwa::CoordSel) { 32 | use hwa::traits::StepActuatorTrait; 33 | hwa::debug!("set_forward_direction {:?}", channels); 34 | self.actuator.apply_mut(|pins| { 35 | pins.set_forward_direction(channels, mask); 36 | }) 37 | } 38 | 39 | pub fn disable_steppers(&self, channels: hwa::CoordSel) { 40 | use hwa::traits::StepActuatorTrait; 41 | hwa::debug!("disable_steppers {:?}", channels); 42 | self.actuator.apply_mut(|pins| pins.disable(channels)) 43 | } 44 | 45 | #[cfg(feature = "native")] 46 | pub fn set_end_stop_high(&self, _channels: hwa::CoordSel) { 47 | self.actuator.apply_mut(|_pins| { 48 | #[cfg(feature = "with-x-axis")] 49 | if _channels.contains(hwa::CoordSel::X) { 50 | _pins.x_endstop_pin.set_high(); 51 | } 52 | #[cfg(feature = "with-y-axis")] 53 | if _channels.contains(hwa::CoordSel::Y) { 54 | _pins.y_endstop_pin.set_high(); 55 | } 56 | #[cfg(feature = "with-z-axis")] 57 | if _channels.contains(hwa::CoordSel::Z) { 58 | _pins.z_endstop_pin.set_high(); 59 | } 60 | }); 61 | } 62 | 63 | pub fn end_stop_triggered(&self, channels: hwa::CoordSel) -> bool { 64 | use hwa::traits::StepActuatorTrait; 65 | self.actuator 66 | .apply_mut(|pins| pins.endstop_triggered(channels)) 67 | } 68 | 69 | pub fn step_toggle(&self, channels: hwa::CoordSel) { 70 | use hwa::traits::StepActuatorTrait; 71 | hwa::trace!("step_toggle {:?}", channels); 72 | 73 | self.actuator.apply_mut(|pins| pins.step_toggle(channels)); 74 | } 75 | } 76 | 77 | impl Clone for StepActuatorController { 78 | fn clone(&self) -> Self { 79 | Self::new( 80 | self.actuator.clone(), 81 | #[cfg(feature = "with-motion-broadcast")] 82 | self.broadcast_channel.clone(), 83 | ) 84 | } 85 | } 86 | /* 87 | #[cfg(feature = "assert-motion")] 88 | pub mod assertion { 89 | pub struct MotionStepActuatorStateController { 90 | 91 | } 92 | 93 | impl MotionStepActuatorStateController { 94 | 95 | } 96 | 97 | pub static ACTUATOR_STATE: MotionStepActuatorStateController = MotionStepActuatorStateController { 98 | 99 | }; 100 | } 101 | */ 102 | -------------------------------------------------------------------------------- /printhor/src/bin/hwa/controllers/pwm_controller.rs: -------------------------------------------------------------------------------- 1 | //! TODO: This feature is still in incubation 2 | 3 | use crate::hwa; 4 | use hwa::traits::Pwm; 5 | 6 | /// A controller for managing PWM (Pulse-Width Modulation). 7 | /// 8 | /// # Type Parameters 9 | /// 10 | /// * `H` - A type that implements the `MutexStrategy` trait and is 'static. 11 | /// 12 | /// # Fields 13 | /// 14 | /// * `pwm` - A reference to an interrupt controller managing the PWM peripheral. 15 | /// * `pwm_chan` - The specific PWM channel being controlled. 16 | pub struct GenericPwmController 17 | where 18 | H: hwa::SyncMutexStrategy + 'static, 19 | H::Resource: Pwm + 'static, 20 | ::Channel: Copy, 21 | ::Duty: 22 | core::fmt::Debug + Copy + Ord + Into + From + TryFrom, 23 | { 24 | pwm: hwa::StaticSyncController, 25 | pwm_chan: ::Channel, 26 | } 27 | 28 | impl GenericPwmController 29 | where 30 | H: hwa::SyncMutexStrategy + 'static, 31 | H::Resource: Pwm + 'static, 32 | ::Channel: Copy, 33 | ::Duty: 34 | core::fmt::Debug + Copy + Ord + Into + From + TryFrom, 35 | { 36 | pub fn new(pwm: hwa::StaticSyncController, pwm_chan: ::Channel) -> Self { 37 | Self { pwm, pwm_chan } 38 | } 39 | 40 | // Sets the applied power in scale between 0 and 100 41 | pub fn set_power(&mut self, power: u8) { 42 | self.pwm.apply_mut(|pwm| { 43 | if power > 0 { 44 | let max_duty = pwm.get_max_duty(); 45 | let duty_result: Result = 46 | (((power as u32) * max_duty.into()) / 100u32).try_into(); 47 | match duty_result { 48 | Ok(duty) => { 49 | let d: ::Duty = duty.try_into().unwrap_or(max_duty); 50 | hwa::trace!("Set duty: {:?}", d); 51 | pwm.set_duty(self.pwm_chan, d.min(max_duty)); 52 | pwm.enable(self.pwm_chan); 53 | } 54 | _ => { 55 | pwm.disable(self.pwm_chan); 56 | hwa::error!("Unable to set power"); 57 | } 58 | } 59 | } else { 60 | pwm.disable(self.pwm_chan); 61 | } 62 | }); 63 | } 64 | 65 | // Gets the applied power in scale between 0.0 and 1.0 66 | pub fn get_power(&mut self) -> f32 { 67 | self.pwm.apply(|pwm| { 68 | let d: u32 = pwm.get_duty(self.pwm_chan).into(); 69 | let duty_result: Result = 70 | ((d as f32 * 100.0f32) / (pwm.get_max_duty().into() as f32)).try_into(); 71 | hwa::debug!( 72 | "Computing power: ({} * {}) / {:?} = {:?}", 73 | pwm.get_duty(self.pwm_chan).into() as f32, 74 | 100f32, 75 | pwm.get_max_duty().into(), 76 | duty_result, 77 | ); 78 | duty_result.unwrap_or(0.0f32) 79 | }) 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /printhor/src/bin/hwa/drivers/mod.rs: -------------------------------------------------------------------------------- 1 | //! Driver modules. A driver is a feature enriched set of controllers with complex responsibilities 2 | #[cfg(feature = "with-motion")] 3 | pub mod motion_driver; 4 | 5 | #[cfg(feature = "with-motion")] 6 | pub use motion_driver::MotionDriver; 7 | -------------------------------------------------------------------------------- /printhor/src/bin/hwa/hwi/mod.rs: -------------------------------------------------------------------------------- 1 | //! The hardware/bare-metal *abstraction* interface 2 | //! this is a simple strict-typing abstraction with adapters/proxies 3 | 4 | //#region "Boards exports as HWI" 5 | 6 | cfg_if::cfg_if! { 7 | if #[cfg(feature="skr_mini_e3")] { 8 | pub use printhor_hwi_skr_mini_e3::*; 9 | } 10 | else if #[cfg(feature="mks_robin_nano")] { 11 | pub use printhor_hwi_mks_robin_nano::*; 12 | } 13 | else if #[cfg(feature="nucleo_64_arduino_cnc_hat")] { 14 | pub use printhor_hwi_nucleo_64_arduino_cnc_hat::*; 15 | } 16 | else if #[cfg(feature="rp-2040")] { 17 | pub use printhor_hwi_rp_2040::*; 18 | } 19 | else if #[cfg(feature="native")] { 20 | pub use printhor_hwi_native::*; 21 | } 22 | else { 23 | compile_error!("You need to select a board"); 24 | } 25 | } 26 | //#endregion 27 | -------------------------------------------------------------------------------- /printhor/src/bin/hwa/machine.rs: -------------------------------------------------------------------------------- 1 | //! Machine metadata information 2 | use crate::hwa; 3 | use hwa::HwiContract; 4 | 5 | /// The Machine metadata info structure 6 | pub struct MachineInfo { 7 | pub firmware_name: &'static str, 8 | pub firmware_version: &'static str, 9 | pub firmware_url: &'static str, 10 | pub machine_type: &'static str, 11 | pub machine_board: &'static str, 12 | pub machine_processor: &'static str, 13 | pub machine_uuid: &'static str, 14 | pub extruder_count: u8, 15 | } 16 | 17 | impl MachineInfo { 18 | pub const fn new() -> Self { 19 | Self { 20 | firmware_name: "PrinThor", 21 | firmware_version: env!("CARGO_PKG_VERSION"), 22 | firmware_url: "https://github.com/cbruiz/printhor", 23 | machine_type: hwa::Contract::MACHINE_TYPE, 24 | machine_board: hwa::Contract::MACHINE_BOARD, 25 | machine_processor: hwa::Contract::MACHINE_PROCESSOR, 26 | machine_uuid: "00000000-0000-0000-0000-000000000000", 27 | extruder_count: 1, 28 | } 29 | } 30 | } 31 | /// Public static read-only instance of the machine metadata 32 | pub static MACHINE_INFO: MachineInfo = MachineInfo::new(); 33 | -------------------------------------------------------------------------------- /printhor/src/bin/hwa/mod.rs: -------------------------------------------------------------------------------- 1 | //! The Hardware Abstraction Module 2 | pub use printhor_hwa_common::*; 3 | 4 | /// HWI contains the exports of the lower layer (Hardware Interface) 5 | pub(in crate::hwa) mod hwi; 6 | 7 | pub mod controllers; 8 | pub mod drivers; 9 | pub mod types; 10 | 11 | //#region Main exports 12 | 13 | // Isolate/decouple HWI export from board crates 14 | 15 | //#endregion 16 | 17 | pub use hwi::Contract; 18 | -------------------------------------------------------------------------------- /printhor/src/bin/instrumentation/data_points.rs: -------------------------------------------------------------------------------- 1 | //! A module to hold and plot discrete signal changes 2 | use crate::{hwa, motion}; 3 | 4 | pub(crate) struct DataPointsDimension { 5 | time_offset: f64, 6 | last_time: f64, 7 | point_offset: f64, 8 | last_point: f64, 9 | /// Position datapoint time in secs 10 | pub times: Vec, 11 | /// Position datapoint displacement in units 12 | pub points: Vec, 13 | } 14 | 15 | impl DataPointsDimension { 16 | pub fn new() -> Self { 17 | Self { 18 | time_offset: 0.0, 19 | last_time: 0.0, 20 | point_offset: 0.0, 21 | last_point: 0.0, 22 | times: vec![0.0], 23 | points: vec![0.0], 24 | } 25 | } 26 | 27 | pub fn displace(&mut self) { 28 | self.displace_time(); 29 | self.point_offset += self.last_point; 30 | } 31 | 32 | pub fn displace_time(&mut self) { 33 | self.time_offset += self.last_time; 34 | } 35 | 36 | pub fn push_relative(&mut self, time: f64, point: f64) { 37 | self.last_time = time; 38 | self.last_point = point; 39 | self.times.push(self.time_offset + self.last_time); 40 | self.points.push(self.point_offset + self.last_point); 41 | } 42 | 43 | pub fn push_time_relative(&mut self, time: f64, point: f64) { 44 | self.last_time = time; 45 | self.last_point = point; 46 | self.times.push(self.time_offset + self.last_time); 47 | self.points.push(self.last_point); 48 | } 49 | } 50 | 51 | pub(crate) struct DataPoints { 52 | pub current_segment_id: usize, 53 | pub current_micro_segment_id: usize, 54 | pub total_displacement: hwa::math::Real, 55 | 56 | pub segment_position_marks: DataPointsDimension, 57 | pub interpolated_positions: DataPointsDimension, 58 | 59 | pub segment_velocity_marks: DataPointsDimension, 60 | pub interpolated_velocities: DataPointsDimension, 61 | } 62 | 63 | impl DataPoints { 64 | pub fn new() -> Self { 65 | Self { 66 | current_segment_id: 0, 67 | current_micro_segment_id: 0, 68 | total_displacement: hwa::math::ZERO, 69 | 70 | segment_position_marks: DataPointsDimension::new(), 71 | interpolated_positions: DataPointsDimension::new(), 72 | 73 | segment_velocity_marks: DataPointsDimension::new(), 74 | interpolated_velocities: DataPointsDimension::new(), 75 | } 76 | } 77 | 78 | pub fn segment_starts(&mut self, trajectory: &motion::SCurveMotionProfile) { 79 | self.current_segment_id += 1; 80 | self.current_micro_segment_id = 0; 81 | self.segment_position_marks.push_relative( 82 | self.interpolated_positions.last_time, 83 | self.interpolated_positions.last_point, 84 | ); 85 | self.segment_velocity_marks.push_time_relative( 86 | self.interpolated_positions.last_time, 87 | trajectory.v_0.to_f64(), 88 | ) 89 | } 90 | pub fn segment_ends(&mut self) { 91 | self.segment_position_marks.displace(); 92 | self.interpolated_positions.displace(); 93 | self.segment_velocity_marks.displace_time(); 94 | self.interpolated_velocities.displace_time(); 95 | } 96 | 97 | pub fn num_segments(&self) -> usize { 98 | self.current_segment_id 99 | } 100 | 101 | pub fn current_segment_id(&self) -> usize { 102 | self.current_segment_id 103 | } 104 | 105 | pub fn micro_segment_ends(&mut self) {} 106 | 107 | pub fn current_micro_segment_id(&self) -> usize { 108 | self.current_micro_segment_id 109 | } 110 | 111 | pub fn total_displacement(&self) -> hwa::math::Real { 112 | self.total_displacement 113 | } 114 | 115 | pub fn interpolation_tick( 116 | &mut self, 117 | interpolation_iterator: &hwa::controllers::motion_control::SegmentIterator< 118 | motion::SCurveMotionProfile, 119 | >, 120 | ) { 121 | self.interpolated_positions.push_relative( 122 | interpolation_iterator.current_time().to_f64(), 123 | interpolation_iterator.current_position().to_f64(), 124 | ); 125 | self.interpolated_velocities.push_relative( 126 | interpolation_iterator.current_time().to_f64(), 127 | (interpolation_iterator.ds() / interpolation_iterator.dt()).to_f64(), 128 | ) 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /printhor/src/bin/instrumentation/gcode.rs: -------------------------------------------------------------------------------- 1 | //! A module to inject gcode similar way [crate::processing] machinery does 2 | use std::collections::VecDeque; 3 | 4 | pub(crate) struct GCodeBuffer { 5 | buffer: VecDeque, 6 | } 7 | 8 | impl GCodeBuffer { 9 | pub const fn new() -> Self { 10 | Self { 11 | buffer: VecDeque::new(), 12 | } 13 | } 14 | pub fn pop_front(&mut self) -> Option { 15 | self.buffer.pop_front() 16 | } 17 | 18 | pub fn append(&mut self, data: &str) { 19 | self.buffer.extend(data.as_bytes()); 20 | } 21 | } 22 | 23 | pub(crate) struct BufferStream { 24 | inner: GCodeBuffer, 25 | } 26 | 27 | impl BufferStream { 28 | pub(crate) const fn new(buff: GCodeBuffer) -> Self { 29 | Self { inner: buff } 30 | } 31 | } 32 | 33 | impl async_gcode::ByteStream for BufferStream { 34 | type Item = Result; 35 | 36 | async fn next(&mut self) -> Option { 37 | match self.inner.pop_front() { 38 | None => None, 39 | Some(_b) => Some(Ok(_b)), 40 | } 41 | } 42 | 43 | async fn recovery_check(&mut self) {} 44 | } 45 | -------------------------------------------------------------------------------- /printhor/src/bin/instrumentation/mod.rs: -------------------------------------------------------------------------------- 1 | //! This module provides instrumentation utils for debugging and validation 2 | pub(crate) mod data_points; 3 | pub(crate) mod gcode; 4 | pub(crate) mod machinery; 5 | -------------------------------------------------------------------------------- /printhor/src/bin/motion/mod.rs: -------------------------------------------------------------------------------- 1 | //! The Motion profile trait and its implementations 2 | //! 3 | use crate::hwa; 4 | pub mod profile; 5 | 6 | /// The `MotionProfile` trait provides methods for evaluating motion_control profiles. 7 | pub trait MotionProfile { 8 | /// Returns the end time of the motion_control profile. 9 | fn end_time(&self) -> hwa::math::Real; 10 | 11 | /// Returns the end position of the motion_control profile. 12 | fn end_pos(&self) -> hwa::math::Real; 13 | 14 | /// Evaluates the position at a given time `t` within the motion_control profile. 15 | /// 16 | /// # Parameters 17 | /// 18 | /// - `t`: The time at which to evaluate the position. 19 | /// 20 | /// # Returns 21 | /// 22 | /// An `Option` containing a tuple with: 23 | /// - The evaluated position as `Real`. 24 | /// - A status as `u8` (implementation-specific). 25 | fn eval_position(&self, t: hwa::math::Real) -> Option; 26 | } 27 | 28 | pub use profile::*; 29 | -------------------------------------------------------------------------------- /printhor/src/bin/tasks/mod.rs: -------------------------------------------------------------------------------- 1 | //! Module providing concurrent tasks for system, I/O, motion, actuators and sensors 2 | 3 | pub mod task_control; 4 | #[cfg(any( 5 | feature = "with-motion", 6 | feature = "with-hot-end", 7 | feature = "with-hot-bed" 8 | ))] 9 | pub mod task_defer; 10 | 11 | #[cfg(all(feature = "with-motion", feature = "with-motion-broadcast"))] 12 | pub mod task_motion_broadcast; 13 | 14 | #[cfg(any(test, feature = "integration-test"))] 15 | pub mod task_integration; 16 | #[cfg(feature = "with-print-job")] 17 | pub mod task_print_job; 18 | #[cfg(feature = "with-motion")] 19 | pub mod task_stepper; 20 | #[cfg(any(feature = "with-hot-end", feature = "with-hot-bed"))] 21 | pub mod task_temperature; 22 | -------------------------------------------------------------------------------- /printhor/src/bin/tasks/task_defer.rs: -------------------------------------------------------------------------------- 1 | //! This feature is being established 2 | //! This module provides a task that notifies to the U(S)ART the acceptation or the completion of the 3 | //! GCodes that aren't processed immediately, so processor can accept more 4 | //! Some firmwares resolves this by allocating extra space in the queue, but that case issues because you can get blocked 5 | use crate::{hwa, processing}; 6 | 7 | use embassy_time::{Duration, with_timeout}; 8 | use hwa::{CommChannel, DeferAction, DeferEvent}; 9 | 10 | #[derive(Clone, Copy, Default)] 11 | struct SubscriptionCounting { 12 | #[cfg(feature = "with-motion")] 13 | num_homes: u8, 14 | #[cfg(feature = "with-motion")] 15 | num_linear: u8, 16 | #[cfg(feature = "with-motion")] 17 | num_rapid: u8, 18 | #[cfg(feature = "with-motion")] 19 | num_dwell: u8, 20 | #[cfg(feature = "with-motion")] 21 | num_set_position: u8, 22 | #[cfg(feature = "with-hot-end")] 23 | num_hotend: u8, 24 | #[cfg(feature = "with-hot-bed")] 25 | num_hotbed: u8, 26 | } 27 | 28 | struct Subscriptions { 29 | channel_counts: [SubscriptionCounting; CommChannel::count()], 30 | total_counts: i32, 31 | } 32 | 33 | impl Subscriptions { 34 | fn new() -> Self { 35 | Self { 36 | channel_counts: [SubscriptionCounting::default(); CommChannel::count()], 37 | total_counts: 0, 38 | } 39 | } 40 | 41 | fn update( 42 | &mut self, 43 | action: DeferAction, 44 | channel: CommChannel, 45 | increment: i8, 46 | ) -> Result { 47 | if let Some(counts) = self.channel_counts.get_mut(CommChannel::index_of(channel)) { 48 | if increment == 1 { 49 | self.total_counts += 1; 50 | } else if increment == -1 { 51 | self.total_counts -= 1; 52 | } 53 | 54 | let counter = match action { 55 | #[cfg(feature = "with-motion")] 56 | DeferAction::Homing => &mut counts.num_homes, 57 | #[cfg(feature = "with-motion")] 58 | DeferAction::RapidMove => &mut counts.num_rapid, 59 | #[cfg(feature = "with-motion")] 60 | DeferAction::LinearMove => &mut counts.num_linear, 61 | #[cfg(feature = "with-motion")] 62 | DeferAction::Dwell => &mut counts.num_dwell, 63 | #[cfg(feature = "with-motion")] 64 | DeferAction::SetPosition => &mut counts.num_set_position, 65 | #[cfg(feature = "with-hot-end")] 66 | DeferAction::HotEndTemperature => &mut counts.num_hotend, 67 | #[cfg(feature = "with-hot-bed")] 68 | DeferAction::HotBedTemperature => &mut counts.num_hotbed, 69 | }; 70 | let new_value = *counter as i8 + increment; 71 | if new_value < 0 { 72 | *counter = 0; 73 | Ok(false) 74 | } else { 75 | *counter = new_value as u8; 76 | Ok(true) 77 | } 78 | } else { 79 | Err(()) 80 | } 81 | } 82 | } 83 | 84 | #[embassy_executor::task(pool_size = 1)] 85 | pub async fn task_defer( 86 | processor: processing::GCodeProcessor, 87 | defer_channel: hwa::types::DeferChannel, 88 | ) { 89 | hwa::info!("[task_defer] started"); 90 | 91 | let mut subscriptions = Subscriptions::new(); 92 | 93 | loop { 94 | match with_timeout(Duration::from_secs(10), defer_channel.receive()).await { 95 | Err(_) => { 96 | #[cfg(feature = "trace-commands")] 97 | hwa::info!( 98 | "[trace-commands] [task_defer] Timeout. Actual subscriptions count: {}", 99 | subscriptions.total_counts 100 | ); 101 | #[cfg(test)] 102 | if crate::tasks::task_integration::INTEGRATION_STATUS.signaled() { 103 | hwa::info!("[task_defer] Ending gracefully"); 104 | return (); 105 | } 106 | } 107 | 108 | Ok(DeferEvent::AwaitRequested(action, channel, _order_num)) => { 109 | #[cfg(feature = "trace-commands")] 110 | hwa::info!("AwaitRequested {:?} {}", action, _order_num); 111 | let _ = subscriptions.update(action, channel, 1); 112 | #[cfg(feature = "trace-commands")] 113 | hwa::info!( 114 | "[task_defer] Actual subscriptions count: {}", 115 | subscriptions.total_counts 116 | ); 117 | } 118 | 119 | Ok(DeferEvent::Completed(action, channel, _order_num)) => { 120 | #[cfg(feature = "trace-commands")] 121 | hwa::info!("AwaitCompleted {:?} {}", action, _order_num); 122 | if subscriptions.update(action, channel, -1).unwrap_or(false) { 123 | cfg_if::cfg_if! { 124 | if #[cfg(feature="trace-commands")] { 125 | let msg = alloc::format!("ok; [trace-commands] {:?} completed @{:?}\n", action, channel); 126 | hwa::info!("{}", msg.trim_end()); 127 | processor.write(channel, msg.as_str()).await; 128 | 129 | } 130 | else { 131 | processor.write_ok(channel).await; 132 | } 133 | } 134 | } 135 | #[cfg(feature = "trace-commands")] 136 | hwa::info!( 137 | "[task_defer] Actual subscriptions count: {}", 138 | subscriptions.total_counts 139 | ); 140 | } 141 | } 142 | } 143 | } 144 | -------------------------------------------------------------------------------- /printhor/src/bin/tasks/task_motion_broadcast.rs: -------------------------------------------------------------------------------- 1 | //! This feature is being established 2 | //! This module provides a task that broadcasts near to real-time motion updates to dedicated break-boards 3 | 4 | use crate::hwa; 5 | use hwa::math; 6 | use hwa::{Contract, HwiContract}; 7 | use math::{Real, TVector}; 8 | 9 | #[embassy_executor::task(pool_size = 1)] 10 | pub async fn task_motion_broadcast( 11 | _motion_broadcast_channel: hwa::types::MotionBroadcastChannel, 12 | _motion_config: hwa::controllers::MotionConfig, 13 | _motion_sender: hwa::types::MotionSender, 14 | ) -> ! { 15 | // The motion interpolation period width 16 | const _MICRO_SEGMENT_PERIOD_US: u32 = 17 | 1_000_000 / Contract::MOTION_PLANNER_MICRO_SEGMENT_FREQUENCY; 18 | const _MAX_CMD_DELAY: u32 = _MICRO_SEGMENT_PERIOD_US / 2; 19 | 20 | hwa::info!( 21 | "[task_motion_broadcast] started. Max expected delay: {} us", 22 | _MAX_CMD_DELAY 23 | ); 24 | 25 | let mut current_order = None; 26 | let mut lag: embassy_time::Instant = embassy_time::Instant::now(); 27 | 28 | let receiver = _motion_broadcast_channel.receiver(); 29 | 30 | loop { 31 | match embassy_time::with_timeout(embassy_time::Duration::from_secs(1), receiver.receive()) 32 | .await 33 | { 34 | Ok(hwa::MotionBroadcastEvent::Reset) => { 35 | if current_order.is_some() { 36 | flush(&mut current_order, &TVector::zero(), &TVector::zero()); 37 | } 38 | lag = embassy_time::Instant::now(); 39 | } 40 | Ok(hwa::MotionBroadcastEvent::Delta(motion_event)) => { 41 | let _elapsed = match current_order { 42 | Some(order_num) => { 43 | if order_num != motion_event.order_num { 44 | flush( 45 | &mut current_order, 46 | &motion_event.pos_steps, 47 | &motion_event.pos_wu, 48 | ); 49 | current_order = Some(motion_event.order_num); 50 | lag = embassy_time::Instant::now(); 51 | 0 52 | } else { 53 | lag.elapsed().as_micros() 54 | } 55 | } 56 | None => { 57 | current_order = Some(motion_event.order_num); 58 | lag = embassy_time::Instant::now(); 59 | 0 60 | } 61 | }; 62 | 63 | let mut mg = _motion_sender.lock().await; 64 | let mut changed = false; 65 | motion_event.pos_wu.foreach_values(|coord, val| { 66 | changed |= mg.set_angle(coord, val); 67 | }); 68 | 69 | #[cfg(feature = "debug-motion-broadcast")] 70 | hwa::info!( 71 | "[task_motion_broadcast] at: [t: {:?} s, t_ref: {:?} us] #[{:?}, {:?}] pos: [{:?}] applied: {:?}", 72 | Real::from_f32((_elapsed as f32) / 1000000.0).rdp(4), 73 | motion_event.micro_segment_time.rdp(4), 74 | motion_event.order_num, 75 | motion_event.micro_segment_id, 76 | motion_event.pos_wu, 77 | changed 78 | ); 79 | if changed { 80 | mg.apply().await; 81 | /* 82 | 83 | if embassy_time::with_timeout( 84 | embassy_time::Duration::from_micros((MAX_CMD_DELAY) as u64), 85 | 86 | ) 87 | .await 88 | .is_err() 89 | { 90 | mg.reset().await; 91 | hwa::warn!("[task_motion_broadcast] I2C Timeout"); 92 | } 93 | */ 94 | } 95 | } 96 | Err(_e) => { 97 | hwa::trace!("[task_motion_broadcast] Timeout"); 98 | } 99 | } 100 | } 101 | } 102 | 103 | fn flush( 104 | current_id: &mut Option, 105 | _absolute_stp_pos: &TVector, 106 | _absolute_wu_pos: &TVector, 107 | ) { 108 | #[cfg(feature = "debug-motion-broadcast")] 109 | hwa::info!( 110 | "[task_motion_broadcast] DONE order_num:{:?} pos: [ step: [{:?}], world [{:?}] {} ]", 111 | current_id, 112 | _absolute_stp_pos, 113 | _absolute_wu_pos, 114 | Contract::WORLD_UNIT_MAGNITUDE, 115 | ); 116 | *current_id = None; 117 | } 118 | -------------------------------------------------------------------------------- /printhor/src/bin/tasks/task_temperature.rs: -------------------------------------------------------------------------------- 1 | //! The Temperature controller task 2 | 3 | use crate::hwa; 4 | use embassy_time::{Duration, Ticker}; 5 | 6 | /// 7 | /// # Asynchronous Task Entry Point 8 | /// 9 | /// This function serves as the entry point for the temperature task that runs asynchronously. 10 | /// 11 | /// # Arguments 12 | /// 13 | /// * `event_bus` - A reference to the event bus. 14 | /// 15 | /// `#[cfg(feature = "with-hot-end")]` - If enabled, receives a reference to the hot end controller. 16 | /// 17 | /// `#[cfg(feature = "with-hot-bed")]` - If enabled, receives a reference to the hot bed controller. 18 | /// 19 | /// # Description 20 | /// 21 | /// This method initializes and runs an infinite loop where the state machines for the hot end and hot bed (if enabled) are updated. 22 | /// Within the loop: 23 | /// 24 | /// 1. It waits for an interval of 2 seconds. 25 | /// 2. It calls the `update` method of the heater state machines (hot end and hot bed) if they are enabled, providing references to their respective controllers and the event bus. 26 | /// 27 | /// Note that this function uses async/await to coordinate asynchronous operations. 28 | #[embassy_executor::task(pool_size = 1)] 29 | pub async fn task_temperature( 30 | event_bus: hwa::types::EventBus, 31 | #[cfg(feature = "with-hot-end")] hot_end_controller: hwa::types::HotEndController, 32 | #[cfg(feature = "with-hot-bed")] hot_bed_controller: hwa::types::HotBedController, 33 | ) { 34 | hwa::info!("[task_temperature] Started"); 35 | 36 | let mut ticker = Ticker::every(Duration::from_secs(2)); 37 | 38 | loop { 39 | // TODO: Park on SYS_ALARM 40 | ticker.next().await; 41 | 42 | #[cfg(test)] 43 | if crate::tasks::task_integration::INTEGRATION_STATUS.signaled() { 44 | hwa::info!("[task_temperature] Ending gracefully"); 45 | return (); 46 | } 47 | #[cfg(feature = "with-hot-end")] 48 | hot_end_controller.lock().await.update(&event_bus).await; 49 | 50 | #[cfg(feature = "with-hot-bed")] 51 | hot_bed_controller.lock().await.update(&event_bus).await; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /rust-toolchain.toml: -------------------------------------------------------------------------------- 1 | [toolchain] 2 | channel = "stable" 3 | components = [ "rustfmt", "rustc-dev" ] 4 | #targets = [ "thumbv6m-none-eabi", "thumbv7em-none-eabi", "thumbv7em-none-eabihf", "thumbv7m-none-eabi" ] 5 | profile = "minimal" 6 | --------------------------------------------------------------------------------