diff --git a/content/customisable_qidibox_firmware/README.md b/content/customisable_qidibox_firmware/README.md index 1541816..ae8cb76 100644 --- a/content/customisable_qidibox_firmware/README.md +++ b/content/customisable_qidibox_firmware/README.md @@ -8,6 +8,13 @@ All of the work here is very strongly a Work In Progress. If you wish to use it The following moves a significant portion of the QidiBox filament changing, purging, and cleaning out of the obfuscated `*.so` files that the QidiBox firmware ships with, and into a single gcode macro config file that can be easily edited to modify the behaviour of the g-code macros. Additionally more generic filament change macros are added that allows for the QidiBox to be used with OrcaSlicer (tested), QidiStudio (tested), BambuStido (untested) and PrusaSlicer (maybe?) +## Sandbox-verified fix bundle (2026-08-05) + +A patch set on top of this module collection — **9 fixes** (6 fork-independent + 3 mainline-drift), +verified in a Klipper software sandbox (**NOT yet tested on a physical printer** — use with care): + +- [sandbox-fixes-2026-08-05/](./sandbox-fixes-2026-08-05/) — patches, required config files, install checklist, and verification evidence. + ## Patching Qidi Plus 4 firmware Using an SSH shell to the printer, perform the following changes: diff --git a/content/customisable_qidibox_firmware/sandbox-fixes-2026-08-05/README.md b/content/customisable_qidibox_firmware/sandbox-fixes-2026-08-05/README.md new file mode 100644 index 0000000..6946933 --- /dev/null +++ b/content/customisable_qidibox_firmware/sandbox-fixes-2026-08-05/README.md @@ -0,0 +1,102 @@ +# QIDI BOX — Sandbox-Verified Fix Bundle (2026-08-05) + +> ## ⚠️ IMPORTANT DISCLAIMER +> +> **This bundle has NOT been tested on a physical QIDI Plus 4 printer.** +> +> All fixes were verified in a **Klipper software sandbox only** (Klipper's own CI file-output +> debug mode — virtual MCUs, no serial, no hardware). The sandbox runs **mainline Klipper**, while +> the real printer runs QIDI's fork (1.8.1), so fork-only API calls were guarded but not exercised +> on real hardware. +> +> **Use at your own risk.** Follow the install checklist, keep backups of your `*.so` files, and be +> ready to restore. If in doubt, test one change at a time. This is a community contribution with +> no guarantees — same as the parent module set. + +--- + +## Authorship & testing status + +- **Coded by:** Hermes, an AI coding assistant, with human review (2026-08-05). +- **Tested on real hardware:** **NO** — sandbox verification only (see disclaimer above). +- **Audience:** the QIDI Plus 4 community. Contributions, corrections and real-hardware + testing reports are very welcome. + +## What this is + +A verified patch set on top of the community `.so → .py` swap +(`customisable_qidibox_firmware`). It fixes **9 crashes/bugs** found by running the swap's modules +through a full Klipper sandbox (all box commands: load/unload, tool change, purge, temp set, RFID, +self-inspection, retry paths, unload macro). + +- **6 fixes are fork-independent** (real-printer relevant) +- **3 fixes are mainline-drift shims** (only needed for FreeDi/mainline Klipper installs; the QIDI + fork already has those APIs) + +## Files + +``` +sandbox-fixes-2026-08-05/ +├── patches/ # apply with: patch -p1 < +│ ├── box_extras.py.patch # fixes 2,3,6,7,8,9 +│ ├── aht20_f.py.patch # fix 1 +│ ├── box_stepper.py.patch # fix 4 (mainline-drift) +│ ├── filament_motion_sensor.py.patch # fix 5 (mainline-drift) +│ └── unload-macro-E-25.patch # macro fix (stock unload extruded 25 mm) +└── required_config/ # files the swap silently depends on + ├── box_macros.cfg # companion macro file (CLEAR_FLUSH dependency) + ├── officiall_filas_list.cfg # filament temp list (real, from QIDI firmware dump) + └── box_extras.example.cfg # example [box_extras] incl. filament_list_path +``` + +## Fix inventory + +| # | File | Fix | Class | +|---|------|-----|-------| +| 1 | aht20_f.py | 4× None-guards on i2c reads (connect/CCP/AFE paths) — prevents connect crash when a read returns None | fork-independent | +| 2 | box_extras.py | accept `buffer_pin` config option (the auto-generated box.cfg always emits it; without this the .py swap fails to BOOT) | fork-independent | +| 3 | box_extras.py | E_UNLOAD: `get_temp_by_num()` None → 190 °C default (front-panel unload crash) | fork-independent | +| 4 | box_stepper.py | `note_kinematic_activity()` → `get_last_move_time()` (removed from mainline) | mainline-drift | +| 5 | filament_motion_sensor.py | QIDI-fork sensor + `note_filament_present(eventtime, bool)` signature | mainline-drift | +| 6 | box_extras.py | `set_box_temp`: `box_max_temps` KeyError + temp_info None guards (BOX_TEMP_SET crash when no box detected) | fork-independent | +| 7 | box_extras.py | TRY_MOVE_AGAIN: `_00`-suffix int-normalization + 2× None-temp guards (retry-button crash) | fork-independent | +| 8 | box_extras.py | `heater.set_can_wait()` guarded with `hasattr` (QIDI-fork-only API — TOOL_CHANGE_START crashes mainline) | mainline-drift | +| 9 | box_extras.py | `filament_list_path` config option (hardcoded `/home/mks/...` path broke non-QIDI hosts) + 3× None-guards (button_extruder_load ×2, QDE_004_009 retry path) | fork-independent | +| — | box.cfg macro | `G1 E25 F300` → `G1 E-25 F300` in `UNLOAD_FILAMENT` (stock unload EXTRUDED 25 mm instead of retracting — verified against QIDI's own generated template) | fork-independent | + +## Install checklist (ALL items verified required in sandbox) + +1. **`box_macros.cfg`** must be present — the registered `CLEAR_FLUSH` command runs `BOX_CLEAR_FLUSH`; + without this file flushing **silently no-ops** (clogged nozzle / color bleed risk). +2. **`officiall_filas_list.cfg`** + `filament_list_path` in `[box_extras]` — temp lookups read this + file; the hardcoded default path only exists on QIDI printers. +3. **`[filament_switch_sensor fila]`** section must exist (macros reference `SENSOR=fila`; missing → + SET_FILAMENT_SENSOR error → shutdown). +4. **`[box_extras]` pins** (real, from QIDI's own template): `b_button_pin: ^mcu_box1:PB1`, + `b_endstop_pin: mcu_box1:PA9`, `e_endstop_pin: mcu_box1:PA10`, `buffer_pin: ^mcu_box1:PB0`. +5. Box includes load AFTER heaters/extruder (box includes at the END of printer.cfg). + +## What was NOT fixed (stock-firmware side) + +- **QDE_004_024 / M104 S0 temp-stability** (GitHub issue I-12): emitted by the stock `.so` module, + not the community `.py`. The sandbox confirmed the `.py` path handles M104 S0 + re-heat cleanly, + but the `.so` behavior needs QIDI to fix. +- **Purge amounts** (issue I-06): `EXTRUSION_AND_FLUSH` hardcodes ~180 mm of purge per change — a + separate config-level change. + +## How it was verified (reproducible) + +- Klipper file-output debug mode (`-d dict -o out -i gcode`), synthesized STM32-pin dictionary, + 3 virtual MCUs (mainboard/toolhead/box). +- 9 test files, all exit 0 with clean "Exiting" markers: boot, box command surface, unload macro + (E-25 A/B), auto-reload + retry, **tool change** (TOOL_CHANGE_START/END + BOX_CHANGE_FILAMENT), + **purge** (EXTRUSION_AND_FLUSH), **temp stability** (M104 S0 + re-heat), **misc batch** + (RELOAD_ALL / CLEAR_RUNOUT_NUM / TIGHTEN_FILAMENT). +- Fork-only command warnings observed on mainline (fine on the real fork): `QIDI_PROBE_PIN_2`, + `BED_MESH_CLEAR`, `CLEAR_MOTION_DATA`. +- **Again: sandbox only. Not yet tested on a physical printer.** + +## Related + +- Parent module set: `../README.md` (customisable_qidibox_firmware) +- Full sandbox + findings log maintained separately (see session docs 2026-08-05) diff --git a/content/customisable_qidibox_firmware/sandbox-fixes-2026-08-05/patches/aht20_f.py.patch b/content/customisable_qidibox_firmware/sandbox-fixes-2026-08-05/patches/aht20_f.py.patch new file mode 100644 index 0000000..0339525 --- /dev/null +++ b/content/customisable_qidibox_firmware/sandbox-fixes-2026-08-05/patches/aht20_f.py.patch @@ -0,0 +1,47 @@ +--- a/aht20_f.py ++++ b/aht20_f.py +@@ -101,7 +101,7 @@ + + # Read data + read = self.i2c.i2c_read([], 7) +- if read is None: ++ if read is None or 'response' not in read: + logging.warning("aht20_f: received data from" + + " i2c_read is None") + continue +@@ -125,7 +125,10 @@ + except Exception as e: + logging.exception("aht20_f: exception encountered" + + " reading data: %s"%str(e)) +- self._reset_device() ++ try: ++ self._reset_device() ++ except Exception: ++ logging.exception("aht20_f: reset_device failed") + return False + + +@@ -156,7 +159,10 @@ + self.i2c.i2c_write(AHT20_F_COMMANDS['OTP_CCP']) + self.reactor.pause(self.reactor.monotonic() + .10) + +- read = self.i2c.i2c_read([], 3) ++ read = self.i2c.i2c_read([], 3) ++ if read is None or 'response' not in read: ++ logging.warning("aht20_f: reset read#1 returned no data - skipping CCP calibration") ++ return + data = bytearray(read['response']) + + AHT20_F_COMMANDS['CCP_CCN'][1] = data[1] +@@ -167,7 +173,10 @@ + self.i2c.i2c_write(AHT20_F_COMMANDS['OTP_AFE']) + self.reactor.pause(self.reactor.monotonic() + .10) + +- read = self.i2c.i2c_read([], 3) ++ read = self.i2c.i2c_read([], 3) ++ if read is None or 'response' not in read: ++ logging.warning("aht20_f: reset read#2 returned no data - skipping AFE calibration") ++ return + data = bytearray(read['response']) + + AHT20_F_COMMANDS['AFE_CFG'][1] = data[1] diff --git a/content/customisable_qidibox_firmware/sandbox-fixes-2026-08-05/patches/box_extras.py.patch b/content/customisable_qidibox_firmware/sandbox-fixes-2026-08-05/patches/box_extras.py.patch new file mode 100644 index 0000000..427d328 --- /dev/null +++ b/content/customisable_qidibox_firmware/sandbox-fixes-2026-08-05/patches/box_extras.py.patch @@ -0,0 +1,171 @@ +--- a/box_extras.py ++++ b/box_extras.py +@@ -179,7 +179,9 @@ + self.next_transform = self.gcode_move.set_move_transform(self, force=True) + self.last_position = self.next_transform.get_position() + toolhead = self.printer.lookup_object('toolhead') +- toolhead.get_extruder().get_heater().set_can_wait(False) ++ heater = toolhead.get_extruder().get_heater() ++ if hasattr(heater, 'set_can_wait'): # QIDI-fork only; mainline lacks it ++ heater.set_can_wait(False) + def cmd_TOOL_CHANGE_END(self, gcmd): + box_extras = self.printer.lookup_object('box_extras') + box_extras.save_variable("is_tool_change", 0) +@@ -189,7 +191,9 @@ + self.next_transform = None + self.gcode_move.reset_last_position() + toolhead = self.printer.lookup_object('toolhead') +- toolhead.get_extruder().get_heater().set_can_wait(True) ++ heater = toolhead.get_extruder().get_heater() ++ if hasattr(heater, 'set_can_wait'): # QIDI-fork only; mainline lacks it ++ heater.set_can_wait(True) + + def get_position(self): + return list(self.last_position) +@@ -203,6 +207,11 @@ + reactor = self.printer.get_reactor() + b_button_pin = config.get('b_button_pin') + self.b_button = BoxButton(config, b_button_pin, self.b_button_callback) ++ # Sandbox Fix #9: filament temp list path must be configurable — the ++ # hardcoded /home/mks path only exists on QIDI printers (breaks the ++ # community .py swap on FreeDi/mainline/other hosts). ++ self.filament_list_path = config.get('filament_list_path', ++ '/home/mks/printer_data/config/officiall_filas_list.cfg') + self.b_endstop_state = 0 + b_endstop_pin = config.get('b_endstop_pin') + self.b_endstop = BoxEndstop(config, 'b_endstop', b_endstop_pin) +@@ -213,6 +222,9 @@ + self.e_endstop = BoxEndstop(config, 'e_endstop', e_endstop_pin) + self.e_output = BoxOutput(config, e_endstop_pin) + self.e_endstop_timer = reactor.register_timer(self.update_e_endstop) ++ # Sandbox Fix #2: accept buffer_pin from the auto-generated box.cfg ++ # (stock .so consumes it; without this the .py swap fails config validation) ++ self.buffer_pin = config.get('buffer_pin', None) + + self.tool_change = ToolChange(config) + self.can_load_slot = True +@@ -394,8 +406,11 @@ + stepper_number = match.group(2) + else: + self.gcode.respond_info("Invalid retry_step format") ++ return + self.save_variable('retry_step', None) +- retry_stpper = self.printer.lookup_object('box_stepper slot' + stepper_number) ++ # Sandbox Fix #7: saved retry_steps use '_00' suffix; int() normalizes to slot0. ++ # Previously 'box_stepper slot00' lookup crashed klippy (real-printer retry button). ++ retry_stpper = self.printer.lookup_object('box_stepper slot' + str(int(stepper_number))) + self.gcode.run_script_from_command("DISABLE_ALL_SENSOR") + + if step_name == 'QDE_004_001': +@@ -419,7 +434,8 @@ + self.gcode.run_script_from_command("M109 S%s" %(str(print_temp))) + retry_stpper.cmd_EXTRUDER_LOAD(gcmd) + else: +- print_temp = self.get_temp_by_slot(retry_stpper.stepper_name)['max_temp'] ++ temp_info = self.get_temp_by_slot(retry_stpper.stepper_name) ++ print_temp = temp_info['max_temp'] if temp_info is not None else 250 + self.gcode.run_script_from_command("M109 S%s" %(str(print_temp))) + retry_stpper.cmd_EXTRUDER_LOAD(gcmd) + self.gcode.run_script_from_command("M104 S0") +@@ -430,7 +446,8 @@ + self.gcode.run_script_from_command("M109 S%s" %(str(print_temp))) + retry_stpper.cmd_EXTRUDER_LOAD(gcmd) + else: +- print_temp = self.get_temp_by_slot(retry_stpper.stepper_name)['max_temp'] ++ temp_info = self.get_temp_by_slot(retry_stpper.stepper_name) ++ print_temp = temp_info['max_temp'] if temp_info is not None else 250 + self.gcode.run_script_from_command("M109 S%s" %(str(print_temp))) + retry_stpper.cmd_EXTRUDER_LOAD(gcmd) + self.gcode.run_script_from_command("M104 S0") +@@ -443,7 +460,8 @@ + self.gcode.run_script_from_command("M109 S%s" %(str(print_temp))) + retry_stpper.cmd_EXTRUDER_UNLOAD(gcmd) + else: +- print_temp = self.get_temp_by_slot(retry_stpper.stepper_name)['max_temp'] ++ temp_info = self.get_temp_by_slot(retry_stpper.stepper_name) ++ print_temp = temp_info['max_temp'] if temp_info is not None else 250 # Fix #9 None-guard + self.gcode.run_script_from_command("M109 S%s" %(str(print_temp))) + retry_stpper.cmd_EXTRUDER_UNLOAD(gcmd) + self.gcode.run_script_from_command("M104 S0") +@@ -475,13 +493,15 @@ + return + + # Unload the old filament and purge what we can. Use minimum temperatures to minimise oozing +- old_filament_extrude_temp = self.get_temp_by_slot(loaded_extruder)['min_temp'] ++ temp_info = self.get_temp_by_slot(loaded_extruder) ++ old_filament_extrude_temp = temp_info['min_temp'] if temp_info is not None else 190 # Fix #9 None-guard + self.gcode.run_script_from_command("BOX_MOVE_HEATING HOTEND=%s" %(str(old_filament_extrude_temp))) + self.printer.lookup_object('box_stepper ' + str(loaded_extruder)).cmd_EXTRUDER_UNLOAD(gcmd) + self.gcode.run_script_from_command("M400\nM83\nG1 E20 F300\nM400") + + # Load in the new filament. Use the higher of the new filament and any old filament temperatures +- new_filament_extrude_temp = self.get_temp_by_num(selected_slot)['min_temp'] ++ temp_info = self.get_temp_by_num(selected_slot) ++ new_filament_extrude_temp = temp_info['min_temp'] if temp_info is not None else 190 # Fix #9 None-guard + extrude_temp = max(new_filament_extrude_temp, old_filament_extrude_temp) + self.gcode.run_script_from_command("BOX_MOVE_HEATING HOTEND=%s" %(str(extrude_temp))) + stepper.cmd_EXTRUDER_LOAD(gcmd) +@@ -503,7 +523,10 @@ + self.box_button_state = 0 + return False + +- extrude_temp = self.get_temp_by_num(selected_slot)['min_temp'] ++ # Sandbox Fix #3: get_temp_by_num() returns None for unconfigured slots; ++ # subscripts crash klippy (front-panel E_UNLOAD = brick-scare path). ++ temp_info = self.get_temp_by_num(selected_slot) ++ extrude_temp = temp_info['min_temp'] if temp_info is not None else 190 + self.gcode.run_script_from_command("BOX_MOVE_HEATING HOTEND=%s" %(str(extrude_temp))) + self.gcode.run_script_from_command("M400\nCUT_FILAMENT\nMOVE_TO_TRASH\nM83") + stepper.cmd_EXTRUDER_UNLOAD(gcmd,False) +@@ -574,7 +597,10 @@ + stepper = self.printer.lookup_object('box_stepper slot' + str(j + 4 * i)) + if not stepper.r_endstop_state:##had load filament + #filament = self.get_value_by_key(f'filament_slot' + str(j + 4 * i)) +- box_max_temp = self.get_box_temp_by_slot(slot)['max_temp'] ++ temp_info = self.get_box_temp_by_slot(slot) ++ if temp_info is None: ++ continue ++ box_max_temp = temp_info['max_temp'] + if current_box_max_temp == -1: + current_box_max_temp = box_max_temp + else: +@@ -592,7 +618,11 @@ + stepper = self.printer.lookup_object('box_stepper ' + str(slot)) + slot_num = stepper.stepper_num + +- box_num = slot_num // 4 ++ box_num = slot_num // 4 ++ # Sandbox Fix #6: box_max_temps only covers detected boxes (box_count); ++ # a stale/zero box_count previously crashed with KeyError on any VTx set. ++ if box_num not in box_max_temps: ++ continue + if box_max_temps[f'box_{box_num}']['set'] == -1: + if value >= box_max_temps[f'box_{box_num}']['max_temp']: + box_max_temps[f'box_{box_num}']['set'] = box_max_temps[f'box_{box_num}']['max_temp'] +@@ -867,7 +897,7 @@ + return -1 + + def get_temp_by_num(self, num): +- list_path="/home/mks/printer_data/config/officiall_filas_list.cfg" ++ list_path = self.filament_list_path + filament_id = self.get_value_by_key(f"filament_slot{num}",1) + section = f"fila{filament_id}" + config = configparser.ConfigParser() +@@ -887,7 +917,7 @@ + return None + + def get_temp_by_slot(self, slot): +- list_path="/home/mks/printer_data/config/officiall_filas_list.cfg" ++ list_path = self.filament_list_path + filament_id = self.get_value_by_key(f"filament_{slot}",1) + section = f"fila{filament_id}" + config = configparser.ConfigParser() +@@ -907,7 +937,7 @@ + return None + + def get_box_temp_by_slot(self, slot): +- list_path="/home/mks/printer_data/config/officiall_filas_list.cfg" ++ list_path = self.filament_list_path + filament_id = self.get_value_by_key(f"filament_{slot}",1) + section = f"fila{filament_id}" + config = configparser.ConfigParser() diff --git a/content/customisable_qidibox_firmware/sandbox-fixes-2026-08-05/patches/box_stepper.py.patch b/content/customisable_qidibox_firmware/sandbox-fixes-2026-08-05/patches/box_stepper.py.patch new file mode 100644 index 0000000..5177ea5 --- /dev/null +++ b/content/customisable_qidibox_firmware/sandbox-fixes-2026-08-05/patches/box_stepper.py.patch @@ -0,0 +1,13 @@ +--- a/box_stepper.py ++++ b/box_stepper.py +@@ -113,7 +113,9 @@ + self.next_cmd_time = self.next_cmd_time + accel_t + cruise_t + accel_t + self.stepper.set_position((0., 0., 0.)) + toolhead = self.printer.lookup_object('toolhead') +- toolhead.note_kinematic_activity(self.next_cmd_time) ++ # Sandbox Fix #4: mainline Klipper removed toolhead.note_kinematic_activity(); ++ # get_last_move_time() is the modern clock-sync equivalent (flushes step gen). ++ toolhead.get_last_move_time() + def dwell(self, delay): + self.next_cmd_time += max(0., delay) + def drip_move(self, newpos, speed, accel, drip_completion): diff --git a/content/customisable_qidibox_firmware/sandbox-fixes-2026-08-05/patches/filament_motion_sensor.py.patch b/content/customisable_qidibox_firmware/sandbox-fixes-2026-08-05/patches/filament_motion_sensor.py.patch new file mode 100644 index 0000000..8aa59ad --- /dev/null +++ b/content/customisable_qidibox_firmware/sandbox-fixes-2026-08-05/patches/filament_motion_sensor.py.patch @@ -0,0 +1,60 @@ +--- a/filament_motion_sensor.py ++++ b/filament_motion_sensor.py +@@ -24,11 +24,13 @@ + logging.info(f"motion use_irq1?:{self.use_irq}") + self.debounce_us = config.getint('debounce_us', 0) + irq_button = config.printer.load_object(config, "buttons_irq") ++ #irq_button.register_buttons([switch_pin], callback=self.encoder_event, self.debounce_us) + irq_button.register_buttons([switch_pin], callback=self.encoder_event, debounce_us = self.debounce_us) + else: + logging.info(f"motion use_irq2?:{self.use_irq}") + buttons = self.printer.load_object(config, 'buttons') + buttons.register_buttons([switch_pin], self.encoder_event) ++ + # Get printer objects + self.reactor = self.printer.get_reactor() + self.runout_helper = filament_switch_sensor.RunoutHelper(config) +@@ -48,18 +50,19 @@ + self.printer.register_event_handler('idle_timeout:idle', + self._handle_not_printing) + self.gcode = self.printer.lookup_object('gcode') +- self.gcode.register_command( +- 'CLEAR_MOTION_DATA', +- self.cmd_CLEAR_MOTION_DATA +- ) ++ # self.gcode.register_command( ++ # 'CLEAR_MOTION_DATA', ++ # self.cmd_CLEAR_MOTION_DATA ++ # ) ++ self.motion_name = config.get_name().split()[1] ++ + def _update_filament_runout_pos(self, eventtime=None): + if eventtime is None: + eventtime = self.reactor.monotonic() + self.filament_runout_pos = ( + self._get_extruder_pos(eventtime) + + self.detection_length) +- def cmd_CLEAR_MOTION_DATA(self, gcmd): +- self._update_filament_runout_pos() ++ + def _handle_ready(self): + self.extruder = self.printer.lookup_object(self.extruder_name) + self.estimated_print_time = ( +@@ -82,7 +85,7 @@ + extruder_pos = self._get_extruder_pos(eventtime) + # Check for filament runout + self.runout_helper.note_filament_present( +- extruder_pos < self.filament_runout_pos) ++ eventtime, extruder_pos < self.filament_runout_pos) + return eventtime + CHECK_RUNOUT_TIMEOUT + def encoder_event(self, eventtime, state): + if self.extruder is not None: +@@ -90,7 +93,7 @@ + self.button_state = state + # Check for filament insertion + # Filament is always assumed to be present on an encoder event +- self.runout_helper.note_filament_present(True) ++ self.runout_helper.note_filament_present(eventtime, True) + + def load_config_prefix(config): + return EncoderSensor(config) diff --git a/content/customisable_qidibox_firmware/sandbox-fixes-2026-08-05/patches/unload-macro-E-25.patch b/content/customisable_qidibox_firmware/sandbox-fixes-2026-08-05/patches/unload-macro-E-25.patch new file mode 100644 index 0000000..11ba54f --- /dev/null +++ b/content/customisable_qidibox_firmware/sandbox-fixes-2026-08-05/patches/unload-macro-E-25.patch @@ -0,0 +1,5 @@ +--- a/box.cfg ++++ b/box.cfg +@@ -1,1 +1,1 @@ +- G1 E25 F300 ++ G1 E-25 F300 # [QIDI-BOX-FIX] retract, not extrude (stock extrudes 25mm during unload) diff --git a/content/customisable_qidibox_firmware/sandbox-fixes-2026-08-05/required_config/box_extras.example.cfg b/content/customisable_qidibox_firmware/sandbox-fixes-2026-08-05/required_config/box_extras.example.cfg new file mode 100644 index 0000000..86329a9 --- /dev/null +++ b/content/customisable_qidibox_firmware/sandbox-fixes-2026-08-05/required_config/box_extras.example.cfg @@ -0,0 +1,39 @@ +# box_extras_sandbox.cfg +# Required sections for the community box_extras.py (customisable_qidibox_firmware) +# that are MISSING from the stock QIDI Plus 4 config. Documented by the sandbox on 2026-08-05. +# Without these, klippy fails to boot after a .so -> .py swap (real-printer brick scare). +# +# PINS VERIFIED from QIDI firmware primary source: +# qidi-q2-fw/firmware/home/mks/cfg_with_1 (the box.cfg template box_detect.so generates) +# [box_extras] b_button_pin : ^mcu_box1:PB1 +# b_endstop_pin : mcu_box1:PA9 +# e_endstop_pin : mcu_box1:PA10 +# buffer_pin : ^mcu_box1:PB0 (used by stock .so; optional for community .py) + +[box_extras] +b_button_pin: ^mcu_box1:PB1 +# Sandbox Fix #9: filament temp list (real file copied from qidi-q2-fw dump) +filament_list_path: /home/kgrames/qidi_project/sandbox/configs/officiall_filas_list.cfg +b_endstop_pin: mcu_box1:PA9 +e_endstop_pin: mcu_box1:PA10 +buffer_pin: ^mcu_box1:PB0 + +# Dummy probe: registers the 'probe' pin chip used by stepper_z virtual endstop +# (real qdprobe removed for sandbox; probing is not under test) +[probe] +pin: mcu_box1:PA8 +x_offset: 0 +y_offset: 0 +z_offset: 0 +speed: 5 + +[filament_motion_sensor box_motion_sensor] +switch_pin: mcu:PA7 +detection_length: 10 +extruder: extruder + +[gcode_macro RESUME_PRINT] +description: Sandbox stub - real printer gets this from QIDI firmware layer +gcode: + M24 + diff --git a/content/customisable_qidibox_firmware/sandbox-fixes-2026-08-05/required_config/box_macros.cfg b/content/customisable_qidibox_firmware/sandbox-fixes-2026-08-05/required_config/box_macros.cfg new file mode 100644 index 0000000..4c4ba5b --- /dev/null +++ b/content/customisable_qidibox_firmware/sandbox-fixes-2026-08-05/required_config/box_macros.cfg @@ -0,0 +1,515 @@ +########################## +# box_extras.py Macros # +########################## + +[gcode_macro TRY_RESUME_PRINT] +gcode: + {% set retry_val = printer.save_variables.variables.retry_step %} + {% if retry_val == None %} + {% if printer['hall_filament_width_sensor'].Diameter > 0.5 %} + RESUME_PRINT + {% else %} + {% if printer.save_variables.variables.is_tool_change == 1 %} + RESUME_PRINT + {% endif %} + {% endif %} + {% else %} + {% if (retry_val.startswith('QDE_004_002') + or retry_val.startswith('QDE_004_003') + or retry_val.startswith('QDE_004_004') + or retry_val.startswith('QDE_004_005') + or retry_val.startswith('QDE_004_006') + or retry_val.startswith('QDE_004_009')) %} + TRY_MOVE_AGAIN + {% else %} + {% if printer['hall_filament_width_sensor'].Diameter > 0.5 %} + RESUME_PRINT + {% else %} + {% if printer.save_variables.variables.is_tool_change == 1 %} + RESUME_PRINT + {% endif %} + {% endif %} + {% endif %} + {% endif %} + + +# [dup] [gcode_macro EXTRUSION_AND_FLUSH] +# [dup] gcode: +# [dup] {% set hotendtemp = params.HOTEND|int %} +# [dup] MOVE_TO_TRASH +# [dup] M109 S{hotendtemp} +# [dup] M83 +# [dup] G1 E1 F50 +# [dup] G1 E28.13 F611 +# [dup] G1 E0.97 F50 +# [dup] G1 E8.73 F611 +# [dup] G1 E0.97 F50 +# [dup] G1 E8.73 F611 +# [dup] G1 E0.97 F50 +# [dup] G1 E-2 F1800 +# [dup] {% for i in range(1,5) %} +# [dup] M106 S255 +# [dup] M400 +# [dup] G4 P6000 +# [dup] CLEAR_FLUSH +# [dup] M106 S60 +# [dup] G1 E34.8 F611 +# [dup] G1 E1.2 F50 +# [dup] G1 E10.8 F611 +# [dup] G1 E1.2 F50 +# [dup] G1 E10.8 F611 +# [dup] G1 E1.2 F50 +# [dup] G1 E-2 F1800 +# [dup] {% endfor %} +# [dup] M106 S255 +# [dup] M400 +# [dup] G4 P6000 +# [dup] CLEAR_FLUSH +# [dup] M106 S60 + +[gcode_macro BOX_PRINT_START] +gcode: + {% set last_load_slot = printer.save_variables.variables.last_load_slot|default("slot-1") %} + {% set hotendtemp = params.HOTENDTEMP|int %} + {% set extruder = params.EXTRUDER|default(0)|int %} + {% set value_t = printer.save_variables.variables["value_t" ~ extruder]|default("slot" ~ extruder) %} + {% if printer['hall_filament_width_sensor'].Diameter > 0.5 %} + {% if last_load_slot != value_t and last_load_slot != "slot-1" %} + CUT_FILAMENT + MOVE_TO_TRASH + M109 S{hotendtemp} + EXTRUDER_UNLOAD SLOT={last_load_slot} + M83 + G1 E18 F300 + T{extruder} + G1 E1 F50 + G1 E28.13 F611 + G1 E0.97 F50 + G1 E8.73 F611 + G1 E0.97 F50 + G1 E8.73 F611 + G1 E0.97 F50 + G1 E-2 F1800 + {% for i in range(1,5) %} + M106 S255 + M400 + G91 + G1 X-3 F60 + G1 X3 F60 + G90 + CLEAR_FLUSH + M106 S60 + G1 E34.8 F611 + G1 E1.2 F50 + G1 E10.8 F611 + G1 E1.2 F50 + G1 E10.8 F611 + G1 E1.2 F50 + G1 E-2 F1800 + {% endfor %} + {% elif last_load_slot == value_t and printer.save_variables.variables.slot_sync == "slot-1" %} + MOVE_TO_TRASH + M109 S{hotendtemp} + T{extruder} + M83 + G1 E1 F50 + G1 E28.13 F611 + G1 E0.97 F50 + G1 E8.73 F611 + G1 E0.97 F50 + G1 E8.73 F611 + G1 E0.97 F50 + G1 E-2 F1800 + {% for i in range(1,5) %} + M106 S255 + M400 + G91 + G1 X-3 F60 + G1 X3 F60 + G90 + CLEAR_FLUSH + M106 S60 + G1 E34.8 F611 + G1 E1.2 F50 + G1 E10.8 F611 + G1 E1.2 F50 + G1 E10.8 F611 + G1 E1.2 F50 + G1 E-2 F1800 + {% endfor %} + {% endif %} + {% else %} + {% if last_load_slot != "slot-1" %} + MOVE_TO_TRASH + M109 S{hotendtemp} + M400 + EXTRUDER_UNLOAD SLOT={last_load_slot} + T{extruder} + M83 + G1 E1 F50 + G1 E28.13 F611 + G1 E0.97 F50 + G1 E8.73 F611 + G1 E0.97 F50 + G1 E8.73 F611 + G1 E0.97 F50 + G1 E-2 F1800 + {% for i in range(1,5) %} + M106 S255 + M400 + G91 + G1 X-3 F60 + G1 X3 F60 + G90 + CLEAR_FLUSH + M106 S60 + G1 E34.8 F611 + G1 E1.2 F50 + G1 E10.8 F611 + G1 E1.2 F50 + G1 E10.8 F611 + G1 E1.2 F50 + G1 E-2 F1800 + {% endfor %} + {% else %} + MOVE_TO_TRASH + M109 S{hotendtemp} + T{extruder} + M83 + G1 E1 F50 + G1 E28.13 F611 + G1 E0.97 F50 + G1 E8.73 F611 + G1 E0.97 F50 + G1 E8.73 F611 + G1 E0.97 F50 + G1 E-2 F1800 + {% for i in range(1,5) %} + M106 S255 + M400 + G91 + G1 X-3 F60 + G1 X3 F60 + G90 + CLEAR_FLUSH + M106 S60 + G1 E34.8 F611 + G1 E1.2 F50 + G1 E10.8 F611 + G1 E1.2 F50 + G1 E10.8 F611 + G1 E1.2 F50 + G1 E-2 F1800 + {% endfor %} + {% endif %} + {% endif %} + +[gcode_macro BOX_HOME_XY] +description: Home X or Y axes if they are not already homed. Z axis is ignored +gcode: + {% if "x" not in printer.toolhead.homed_axes %} + {% if "y" not in printer.toolhead.homed_axes %} + G28 Y X # Home X and Y Axes + {% else %} + G28 X # Home X Only + {% endif %} + {% else %} + {% if "y" not in printer.toolhead.homed_axes %} + G28 Y # Home Y Only + {% endif %} + {% endif %} + +[gcode_macro BOX_EXTRUDE] +description: Move to chute and extrude 150mm of filament +gcode: + {% set hotendtemp = params.HOTEND|default(250)|int %} + + # Sanity check our hotend temperature. If it doesn't make sense, set it to 250C + {% if hotendtemp < (printer.configfile.config.extruder.min_extrude_temp|int) %} + {% set hotendtemp = 250|int %} + {% endif %} + + { action_respond_info("BOX_EXTRUDE - hotend temperature=%d°C" % hotendtemp) } + BOX_HOME_XY + M400 + MOVE_TO_TRASH + M109 S{hotendtemp} + G1 E150 F300 + M400 + M104 S0 + M106 S255 + M400 + G4 P10000 + CLEAR_FLUSH + CLEAR_OOZE + M106 S0 + +[gcode_macro BOX_MOVE_HEATING] +description: Move to chute and wait until the hotend reaches the target temperature +gcode: + {% set hotendtemp = params.HOTEND|default(250)|int %} + + # Sanity check our hotend temperature. If it doesn't make sense, set it to 250C + {% if hotendtemp < (printer.configfile.config.extruder.min_extrude_temp|int) %} + {% set hotendtemp = 250|int %} + {% endif %} + + { action_respond_info("BOX_MOVE_HEATING - hotend temperature=%d°C" % hotendtemp) } + DISABLE_ALL_SENSOR + BOX_HOME_XY + MOVE_TO_TRASH + M109 S{hotendtemp} + +[gcode_macro BOX_CLEAR_FLUSH] +description: Move to the chute and perform a rapid series of movements to clear any nozzle flush +gcode: + BOX_HOME_XY + M204 S10000 + G1 Y308 F10000 + G1 X80 F12000 + G1 Y314 F1500 + G1 Y316 F10000 + G1 Y314 F1500 + G1 Y316 F10000 + G1 Y314 F1500 + G1 Y316 F10000 + G1 Y314 F1500 + G1 Y324 F1200 + G1 X95 F6000 + G1 Y308 F10000 + G1 X80 F12000 + G1 Y314 F1500 + G1 Y316 F10000 + G1 Y314 F1500 + G1 Y316 F10000 + G1 Y314 F1500 + G1 Y316 F10000 + G1 Y314 F1500 + G1 Y324 F1200 + G1 X95 F12000 + G1 Y308 F10000 + G1 X80 F12000 + G1 Y314 F1500 + G1 Y316 F10000 + G1 Y314 F1500 + G1 Y316 F10000 + G1 Y314 F1500 + G1 Y316 F10000 + G1 Y314 F1500 + G1 Y324 F1200 + G1 X95 F6000 + +[gcode_macro BOX_CLEAR_OOZE] +description: Move to the chute and perform a rapid series of movements to clear any nozzle ooze +gcode: + BOX_HOME_XY + M204 S10000 + G1 X75 F12000 + G1 X58 Y318 F12000 + G1 X75 F120000 + G1 X58 Y324 F9000 + G1 Y318 F12000 + G1 X75 Y324 F9000 + G1 X95 F12000 + +[gcode_macro BOX_MOVE_TO_CUTTER] +description: Move tool-head to the filament cutter activation block +gcode: + BOX_HOME_XY + M204 S10000 + {% if printer.gcode_move.position.y > 300 %} + G1 Y300 F9000 + {% endif %} + {% if printer.gcode_move.position.y < 25 %} + G1 Y25 F9000 + {% endif %} + M400 + G1 X304 F24000 + G1 Y20 F24000 + M400 + +[gcode_macro BOX_DO_CUT] +description: Move tool-head activate the filament cutter +gcode: + G1 Y4.5 F5000 + G4 P500 + G1 Y8 F9000 + G1 Y4.3 F1500 + G4 P500 + G1 Y20 F9000 + +[gcode_macro BOX_LENGTHS] +description: Debug macro to display the various flush lengths +gcode: + {% set len1 = params.LEN1|default(0)|float %} + {% set len2 = params.LEN2|default(0)|float %} + {% set len3 = params.LEN3|default(0)|float %} + {% set len4 = params.LEN4|default(0)|float %} + + { action_respond_info("LEN1=%.2f" % len1) } + { action_respond_info("LEN2=%.2f" % len2) } + { action_respond_info("LEN3=%.2f" % len3) } + { action_respond_info("LEN4=%.2f" % len4) } + +[gcode_macro BOX_AUTO_RELOAD_FILAMENT] +description: Called by AUTO_RELOAD_FILAMENT to re-prime the extruder with a filament +gcode: + {% set hotendtemp = params.HOTEND|default(250)|float %} + {% set extruder = params.EXTRUDER|default(0)|int %}} + {% set slot = ("slot" ~ extruder) %} + + M109 S{hotendtemp} + G1 E25 F300 + EXTRUDER_LOAD SLOT={slot} + G1 E100 F300 + G1 E-1 F1800 + CLEAR_FLUSH + RESUME + +########################## +# Slicer Specific Macros # +########################## + +[gcode_macro BOX_FLUSH_FILAMENT] +description: Perform a filament flush when a QidiBox is attached +gcode: + {% set retract_length = params.RETRACT_LENGTH|default(3)|float %} + {% set flush_length = params.FLUSH_LENGTH|default(50)|float %} + {% set flush_rate = params.FLUSH_RATE|default(50)|float %} + + {% set flush_rate = printer["gcode_macro BOX_CHANGE_FILAMENT"].next_filament_extrude_rate|float %} + {% set retract_length = printer["gcode_macro BOX_CHANGE_FILAMENT"].next_filament_retract_length|float %} + + M106 S255 + M400 + G91 + G1 X-5 F60 + G1 X5 F60 + G90 + CLEAR_FLUSH + M106 S60 + + G1 E{retract_length} F300 + G1 E{flush_length * 0.58} F{flush_rate} + G1 E{flush_length * 0.02} F50 + G1 E{flush_length * 0.18} F{flush_rate} + G1 E{flush_length * 0.02} F50 + G1 E{flush_length * 0.18} F{flush_rate} + G1 E{flush_length * 0.02} F50 + G1 E-{retract_length} F1800 + +[gcode_macro BOX_CHANGE_FILAMENT] +variable_max_layer_z: 50 +variable_retraction_distance_when_cut: 0 +variable_flush_length_1: 60 +variable_flush_length_2: 60 +variable_flush_length_3: 0 +variable_flush_length_4: 0 +variable_current_extruder: -1 +variable_current_filament_extrude_rate: 300 +variable_current_filament_high_temp: 260 +variable_current_filament_retract_length: 5 +variable_next_extruder: -1 +variable_next_filament_extrude_rate: 300 +variable_next_filament_temp: 260 +variable_next_filament_high_temp: 260 +variable_next_filament_retract_length: 5 +description: Perform an automatic filament change when a QidiBox is attached +gcode: + {% set current_filament_type = params.C_FILA_TYPE|default("PLA")|string %} + {% set next_filament_type = params.N_FILA_TYPE|default("PLA")|string %} + + G1 Z{max_layer_z + 3.0} F1200 + TOOL_CHANGE_START F={current_extruder} T={next_extruder} + DISABLE_ALL_SENSOR + + {% if retraction_distance_when_cut > 0 %} + MOVE_TO_TRASH + G1 E-{retraction_distance_when_cut} F{current_filament_extrude_rate} + M400 + {% else %} + G1 E-5 F{current_filament_extrude_rate} + {% endif %} + + CUT_FILAMENT T={current_extruder} + MOVE_TO_TRASH + {% if current_filament_high_temp >= next_filament_high_temp %} + M104 S{current_filament_high_temp} + {% else %} + M104 S{next_filament_high_temp} + {% endif %} + + M106 S0 + M106 P2 S0 + UNLOAD_T{current_extruder} + G92 E0 + M83 + + # FLUSH_START + G1 E1 F50 + G1 E17 F{current_filament_extrude_rate} + # FLUSH_END + + T{next_extruder} + G1 E1 F50 + {% if current_filament_high_temp >= next_filament_high_temp %} + {% if (current_filament_type|string) == "PETG" %} + SET_HEATER_TEMPERATURE HEATER=extruder TARGET=260 WAIT=1 + {% else %} + SET_HEATER_TEMPERATURE HEATER=extruder TARGET={current_filament_high_temp} WAIT=1 + {% endif %} + {% else %} + {% if (next_filament_type|string) == "PETG" %} + SET_HEATER_TEMPERATURE HEATER=extruder TARGET=260 WAIT=1 + {% else %} + SET_HEATER_TEMPERATURE HEATER=extruder TARGET={next_filament_high_temp} WAIT=1 + {% endif %} + {% endif %} + + {% if retraction_distance_when_cut > 0 %} + G1 E{retraction_distance_when_cut} F{current_filament_extrude_rate} + M400 + {% endif %} + + # FLUSH_START + G1 E{48.5 * 0.58} F{current_filament_extrude_rate} + G1 E{48.5 * 0.02} F50 + G1 E{48.5 * 0.18} F{current_filament_extrude_rate} + G1 E{48.5 * 0.02} F50 + G1 E{48.5 * 0.18} F{current_filament_extrude_rate} + G1 E{48.5 * 0.02} F50 + G1 E-{current_filament_retract_length} F1800 + # FLUSH_END + + {% if flush_length_1 > 1 %} + BOX_FLUSH_FILAMENT RETRACT_LENGTH={current_filament_retract_length} FLUSH_LENGTH={flush_length_1} FLUSH_RATE={current_filament_extrude_rate} + {% endif %} + + {% if flush_length_2 > 1 %} + BOX_FLUSH_FILAMENT RETRACT_LENGTH={next_filament_retract_length} FLUSH_LENGTH={flush_length_2} FLUSH_RATE={next_filament_extrude_rate} + {% endif %} + + {% if flush_length_3 > 1 %} + BOX_FLUSH_FILAMENT RETRACT_LENGTH={next_filament_retract_length} FLUSH_LENGTH={flush_length_3} FLUSH_RATE={next_filament_extrude_rate} + {% endif %} + + {% if flush_length_4 > 1 %} + BOX_FLUSH_FILAMENT RETRACT_LENGTH={next_filament_retract_length} FLUSH_LENGTH={flush_length_4} FLUSH_RATE={next_filament_extrude_rate} + {% endif %} + + M104 S{next_filament_temp} + M106 S255 + M400 + G91 + G1 X-5 F60 + G1 X5 F60 + G90 + M109 S{next_filament_temp} + G92 E0 + M400 + CLEAR_FLUSH + CLEAR_OOZE + M106 S0 + G1 Y300 F9000 + TOOL_CHANGE_END + ENABLE_ALL_SENSOR diff --git a/content/customisable_qidibox_firmware/sandbox-fixes-2026-08-05/required_config/officiall_filas_list.cfg b/content/customisable_qidibox_firmware/sandbox-fixes-2026-08-05/required_config/officiall_filas_list.cfg new file mode 100644 index 0000000..79d5569 --- /dev/null +++ b/content/customisable_qidibox_firmware/sandbox-fixes-2026-08-05/required_config/officiall_filas_list.cfg @@ -0,0 +1,429 @@ +[fila1] +filament = PLA Rapido +min_temp = 190 +max_temp = 240 +box_min_temp = 0 +box_max_temp = 0 +type = PLA + +[fila2] +filament = PLA Matte +min_temp = 190 +max_temp = 240 +box_min_temp = 0 +box_max_temp = 0 +type = PLA + +[fila3] +filament = PLA Metal +min_temp = 190 +max_temp = 240 +box_min_temp = 0 +box_max_temp = 0 +type = PLA + +[fila4] +filament = PLA Silk +min_temp = 190 +max_temp = 240 +box_min_temp = 0 +box_max_temp = 0 +type = PLA + +[fila5] +filament = PLA-CF +min_temp = 210 +max_temp = 250 +box_min_temp = 0 +box_max_temp = 0 +type = PLA-CF + +[fila6] +filament = PLA-Wood +min_temp = 190 +max_temp = 240 +box_min_temp = 0 +box_max_temp = 45 +type = PLA + +[fila7] +filament = PLA Basic +min_temp = 190 +max_temp = 240 +box_min_temp = 0 +box_max_temp = 0 +type = PLA + +[fila8] +filament = PLA Matte Basic +min_temp = 190 +max_temp = 230 +box_min_temp = 0 +box_max_temp = 0 +type = PLA + +[fila9] +filament = +min_temp = +max_temp = +box_min_temp = +box_max_temp = +type = + +[fila10] +filament = Support For PLA +min_temp = 210 +max_temp = 240 +box_min_temp = 0 +box_max_temp = 0 +type = PLA-S + +[fila11] +filament = ABS Rapido +min_temp = 240 +max_temp = 280 +box_min_temp = 0 +box_max_temp = 45 +type = ABS + +[fila12] +filament = ABS-GF +min_temp = 240 +max_temp = 280 +box_min_temp = 0 +box_max_temp = 45 +type = ABS-GF + +[fila13] +filament = ABS-Metal +min_temp = 240 +max_temp = 280 +box_min_temp = 0 +box_max_temp = 45 +type = ABS + +[fila14] +filament = ABS-Odorless +min_temp = 240 +max_temp = 280 +box_min_temp = 0 +box_max_temp = 45 +type = ABS + +[fila15] +filament = +min_temp = +max_temp = +box_min_temp = +box_max_temp = +type = + +[fila16] +filament = +min_temp = +max_temp = +box_min_temp = +box_max_temp = +type = + +[fila17] +filament = +min_temp = +max_temp = +box_min_temp = +box_max_temp = +type = + +[fila18] +filament = ASA +min_temp = 240 +max_temp = 280 +box_min_temp = 0 +box_max_temp = 45 +type = ASA + +[fila19] +filament = ASA-Aero +min_temp = 240 +max_temp = 280 +box_min_temp = 0 +box_max_temp = 45 +type = ASA-AERO + +[fila20] +filament = ASA-CF +min_temp = 240 +max_temp = 280 +box_min_temp = 0 +box_max_temp = 45 +type = ASA-CF + +[fila21] +filament = +min_temp = +max_temp = +box_min_temp = +box_max_temp = +type = + +[fila22] +filament = +min_temp = +max_temp = +box_min_temp = +box_max_temp = +type = + +[fila23] +filament = PC +min_temp = 240 +max_temp = 280 +box_min_temp = 0 +box_max_temp = 0 +type = PC + +[fila24] +filament = UltraPA +min_temp = 260 +max_temp = 300 +box_min_temp = 0 +box_max_temp = 55 +type = UltraPA + +[fila25] +filament = PA-CF +min_temp = 260 +max_temp = 300 +box_min_temp = 0 +box_max_temp = 65 +type = PA-CF + +[fila26] +filament = UltraPA-CF25 +min_temp = 300 +max_temp = 320 +box_min_temp = 0 +box_max_temp = 65 +type = UltraPA-CF25 + +[fila27] +filament = PA12-CF +min_temp = 260 +max_temp = 300 +box_min_temp = 0 +box_max_temp = 65 +type = PA12-CF + +[fila28] +filament = +min_temp = +max_temp = +box_min_temp = +box_max_temp = +type = + +[fila29] +filament = +min_temp = +max_temp = +box_min_temp = +box_max_temp = +type = + +[fila30] +filament = PAHT-CF +min_temp = 300 +max_temp = 320 +box_min_temp = 0 +box_max_temp = 65 +type = PAHT-CF + +[fila31] +filament = PAHT-GF +min_temp = 300 +max_temp = 320 +box_min_temp = 0 +box_max_temp = 65 +type = PAHT-GF + +[fila32] +filament = Support For PAHT +min_temp = 260 +max_temp = 280 +box_min_temp = 0 +box_max_temp = 65 +type = PAHT-S + +[fila33] +filament = Support For PET/PA +min_temp = 260 +max_temp = 280 +box_min_temp = 0 +box_max_temp = 65 +type = PA-S + +[fila34] +filament = PC/ABS-FR +min_temp = 260 +max_temp = 280 +box_min_temp = 0 +box_max_temp = 50 +type = PC-ABS-FR + +[fila35] +filament = TPEE +min_temp = 230 +max_temp = 260 +box_min_temp = 0 +box_max_temp = 0 +type = TPEE + +[fila36] +filament = PEBA +min_temp = 230 +max_temp = 260 +box_min_temp = 0 +box_max_temp = 0 +type = PEBA + +[fila37] +filament = PET-CF +min_temp = 280 +max_temp = 320 +box_min_temp = 0 +box_max_temp = 65 +type = PET-CF + +[fila38] +filament = PET-GF +min_temp = 280 +max_temp = 320 +box_min_temp = 0 +box_max_temp = 50 +type = PET-GF + +[fila39] +filament = PETG Basic +min_temp = 240 +max_temp = 280 +box_min_temp = 0 +box_max_temp = 45 +type = PETG + +[fila40] +filament = PETG-Tough +min_temp = 240 +max_temp = 275 +box_min_temp = 0 +box_max_temp = 45 +type = PETG + +[fila41] +filament = PETG Rapido +min_temp = 220 +max_temp = 270 +box_min_temp = 0 +box_max_temp = 45 +type = PETG + +[fila42] +filament = PETG-CF +min_temp = 240 +max_temp = 270 +box_min_temp = 0 +box_max_temp = 45 +type = PETG-CF + +[fila43] +filament = PETG-GF +min_temp = 240 +max_temp = 270 +box_min_temp = 0 +box_max_temp = 45 +type = PETG-GF + +[fila44] +filament = PPS-CF +min_temp = 300 +max_temp = 350 +box_min_temp = 0 +box_max_temp = 65 +type = PPS-CF + +[fila45] +filament = PETG Translucent +min_temp = 240 +max_temp = 280 +box_min_temp = 0 +box_max_temp = 45 +type = PETG + +[fila46] +filament = PPS-GF +min_temp = 300 +max_temp = 350 +box_min_temp = 0 +box_max_temp = 65 +type = PPS-GF + +[fila47] +filament = PVA +min_temp = 210 +max_temp = 250 +box_min_temp = 0 +box_max_temp = 50 +type = PVA + +[fila48] +filament = TPU-AERO 64D +min_temp = 200 +max_temp = 250 +box_min_temp = 0 +box_max_temp = 0 +type = TPU-AERO 64D + +[fila49] +filament = TPU-Aero +min_temp = 200 +max_temp = 250 +box_min_temp = 0 +box_max_temp = 0 +type = TPU-AERO + +[fila50] +filament = TPU 95A-HF +min_temp = 200 +max_temp = 250 +box_min_temp = 0 +box_max_temp = 0 +type = TPU + +[colordict] +1 = #FAFAFA +2 = #060606 +3 = #D9E3ED +4 = #5CF30F +5 = #63E492 +6 = #2850FF +7 = #FE98FE +8 = #DFD628 +9 = #228332 +10 = #99DEFF +11 = #1714B0 +12 = #CEC0FE +13 = #CADE4B +14 = #1353AB +15 = #5EA9FD +16 = #A878FF +17 = #FE717A +18 = #FF362D +19 = #E2DFCD +20 = #898F9B +21 = #6E3812 +22 = #CAC59F +23 = #F28636 +24 = #B87F2B + +[vendor_list] +0 = Generic +1 = QIDI