diff --git a/configuration/printers/prusa-mk3s/printer-definition.json b/configuration/printers/prusa-mk3s/printer-definition.json index a9904d6a3..d072433dc 100644 --- a/configuration/printers/prusa-mk3s/printer-definition.json +++ b/configuration/printers/prusa-mk3s/printer-definition.json @@ -37,8 +37,8 @@ "partFan": "4pin-dedicated", "hotendFan": "4pin-dedicated", "probe": "superpinda", - "xEndstop": "endstop", - "yEndstop": "endstop" + "xEndstop": "sensorless", + "yEndstop": "sensorless" } ], "rails": [ diff --git a/configuration/printers/prusa-mk3s/sensorless-homing-tmc2130.cfg b/configuration/printers/prusa-mk3s/sensorless-homing-tmc2130.cfg index 929635ef0..027ab2f0d 100644 --- a/configuration/printers/prusa-mk3s/sensorless-homing-tmc2130.cfg +++ b/configuration/printers/prusa-mk3s/sensorless-homing-tmc2130.cfg @@ -13,6 +13,9 @@ position_max: 255 homing_speed: 25 homing_retract_dist: 0 +[tmc2130 stepper_x] +driver_SGT: 0 + [stepper_y] endstop_pin: tmc2130_stepper_y:virtual_endstop position_endstop: -4 @@ -21,6 +24,9 @@ position_max: 212.5 homing_speed: 25 homing_retract_dist: 0 +[tmc2130 stepper_y] +driver_SGT: 0 + [stepper_z] endstop_pin: probe:z_virtual_endstop position_min: -5 diff --git a/src/__tests__/fixtures/prusa-mk3s.json b/src/__tests__/fixtures/prusa-mk3s.json index 6239a57bb..e63a29fee 100644 --- a/src/__tests__/fixtures/prusa-mk3s.json +++ b/src/__tests__/fixtures/prusa-mk3s.json @@ -6,8 +6,8 @@ "hotend": "v6", "thermistor": "ATC Semitec 104GT-2", "extruder": "mk3s", - "xEndstop": "endstop", - "yEndstop": "endstop", + "xEndstop": "sensorless", + "yEndstop": "sensorless", "hotendFan": "4pin-dedicated", "partFan": "4pin-dedicated", "nozzle": { diff --git a/src/__tests__/server.test.ts b/src/__tests__/server.test.ts index dd5fa22b0..efd18729c 100644 --- a/src/__tests__/server.test.ts +++ b/src/__tests__/server.test.ts @@ -55,13 +55,15 @@ const serializedConfigFromDefaults = (printer: PrinterDefinition): SerializedPri const loadConfig = async (path: string) => { const config = await loadSerializedConfig(path); - const res: string = (await getFilesToWrite(config)).find((f) => f.fileName === 'RatOS.cfg')?.content ?? ''; + const files = await getFilesToWrite(config); + const res: string = files.find((f) => f.fileName === 'RatOS.cfg')?.content ?? ''; const splitRes = res.split('\n'); const annotatedLines = splitRes.map((l: string, i: number) => `Line-${i + 1}`.padEnd(10, '-') + `|${l}`); return { splitRes, annotatedLines, config, + files, }; }; @@ -242,16 +244,16 @@ describe('server', async () => { } }); }); - describe('can generate a config without resonance tester', async () => { + describe('can generate a valid mk3s config', async () => { const prusaMk3sConfigPath = path.join(__dirname, 'fixtures', 'prusa-mk3s.json'); - const { splitRes, annotatedLines, config } = await loadConfig(prusaMk3sConfigPath); - const gcodeBlocks: number[] = []; - splitRes.forEach((l, i) => l.includes('[resonance_tester]') && gcodeBlocks.push(i)); + const { splitRes, annotatedLines, config, files } = await loadConfig(prusaMk3sConfigPath); test('produces valid config', async () => { expectValidConfig(config, splitRes, annotatedLines); }); + const resonanceTesterBlocks: number[] = []; + splitRes.forEach((l, i) => l.includes('[resonance_tester]') && resonanceTesterBlocks.push(i)); test('does not include resonance tester in the config', async () => { - for (const block of gcodeBlocks) { + for (const block of resonanceTesterBlocks) { try { expect(splitRes[block].includes('[resonance_tester]')).toBeFalsy(); } catch (e) { @@ -261,6 +263,31 @@ describe('server', async () => { } } }); + const sensorlessBlocks: number[] = []; + const endstopBlocks: number[] = []; + splitRes.forEach((l, i) => l.includes('[include sensorless-homing') && sensorlessBlocks.push(i)); + splitRes.forEach((l, i) => l.includes('variable_homing_x: "endstop"') && endstopBlocks.push(i)); + splitRes.forEach((l, i) => l.includes('variable_homing_y: "endstop"') && endstopBlocks.push(i)); + test('correctly configures sensorless homing', async () => { + try { + expect(endstopBlocks.length).toBeLessThan(1); + } catch (e) { + throw new Error( + `Found endstop configuration:\n${annotatedLines.slice(endstopBlocks[0] - 4, endstopBlocks[0] + 5).join('\n')}`, + ); + } + expect(sensorlessBlocks.length).toBe(2); + }); + test('correctly comments out generated sensorless defaults', async () => { + expect(files.find((f) => f.fileName === 'sensorless-homing-x.cfg')?.content).toContain( + '#variable_sensorless_x_current: ', + ); + expect(files.find((f) => f.fileName === 'sensorless-homing-y.cfg')?.content).toContain( + '#variable_sensorless_y_current: ', + ); + expect(files.find((f) => f.fileName === 'sensorless-homing-x.cfg')?.content).toContain('#driver_SGT: 0'); + expect(files.find((f) => f.fileName === 'sensorless-homing-y.cfg')?.content).toContain('#driver_SGT: 0'); + }); }); describe('can generate another idex config', async () => { const config = await loadSerializedConfig(path.join(__dirname, 'fixtures', 'another-idex.json')); @@ -408,8 +435,12 @@ describe('server', async () => { test.concurrent('can render sensorless homing files', async () => { const config = await loadSerializedConfig(path.join(__dirname, 'fixtures', 'hybrid-config.json')); const utils = await constructKlipperConfigUtils(config); - const x = sensorlessXTemplate(config, utils); - const y = sensorlessYTemplate(config, utils); + const x = sensorlessXTemplate(config, utils, false); + const y = sensorlessYTemplate(config, utils, false); + expect(x).toContain('variable_sensorless_x_current:'); + expect(y).toContain('variable_sensorless_y_current:'); + expect(x).toContain('driver_SGTHRS:'); + expect(y).toContain('driver_SGTHRS:'); }); }); describe('can generate v-minion config', async () => { diff --git a/src/server/helpers/klipper-config.ts b/src/server/helpers/klipper-config.ts index d62859e30..3f8ccf4fb 100644 --- a/src/server/helpers/klipper-config.ts +++ b/src/server/helpers/klipper-config.ts @@ -222,7 +222,7 @@ export const constructKlipperConfigUtils = async (config: PrinterConfiguration) getAxisDriverHomingCurrent(axis: PrinterAxis, factor: number) { const rail = this.getRail(axis); factor = Math.max(0, Math.min(1, factor)); - return rail.stepper.maxPeakCurrent * 0.71 * factor; + return Math.round(rail.stepper.maxPeakCurrent * 0.71 * factor * 1000) / 1000; }, getExtruderPinPrefix(tool: ToolOrAxis = 0) { const th = this.getToolhead(tool); @@ -382,19 +382,19 @@ export const constructKlipperConfigExtrasGenerator = (config: PrinterConfigurati return [`[save_variables]`, `filename: ${path.join(environment.KLIPPER_CONFIG_PATH, f.fileName)}`].join('\n'); }); }, - generateSensorlessHomingIncludes() { + generateSensorlessHomingIncludes(hasPretunedConfig: boolean) { const filesToWrite: WritableFiles = []; if (utils.isSensorless(PrinterAxis.x)) { filesToWrite.push({ fileName: 'sensorless-homing-x.cfg', - content: sensorlessXTemplate(config, utils), + content: sensorlessXTemplate(config, utils, hasPretunedConfig), overwrite: false, }); } if (utils.isSensorless(PrinterAxis.y)) { filesToWrite.push({ fileName: 'sensorless-homing-y.cfg', - content: sensorlessYTemplate(config, utils), + content: sensorlessYTemplate(config, utils, hasPretunedConfig), overwrite: false, }); } @@ -1009,8 +1009,9 @@ export const constructKlipperConfigHelpers = async ( )) ) { result.push(`[include ${pretunedSensorlessConfig}]`); + result.push(extrasGenerator.generateSensorlessHomingIncludes(true)); } else { - result.push(extrasGenerator.generateSensorlessHomingIncludes()); + result.push(extrasGenerator.generateSensorlessHomingIncludes(false)); } } if (utils.getToolheads().every((th) => th.getProbe() == null)) { diff --git a/src/templates/extras/sensorless-homing.ts b/src/templates/extras/sensorless-homing.ts index b81a145bb..6b7351217 100644 --- a/src/templates/extras/sensorless-homing.ts +++ b/src/templates/extras/sensorless-homing.ts @@ -1,7 +1,11 @@ import { KlipperConfigUtils } from '@/server/helpers/klipper-config'; import { PrinterAxis } from '@/zods/motion'; import { PrinterConfiguration } from '@/zods/printer-configuration'; -export const sensorlessXTemplate = (config: PrinterConfiguration, utils: KlipperConfigUtils) => ` +export const sensorlessXTemplate = ( + config: PrinterConfiguration, + utils: KlipperConfigUtils, + hasPretunedConfig: boolean, +) => ` # Sensorless homing. # # Tune the sensorless_x_current variable and the SGTHRS/SGT value in this file untill you get reliable homing. @@ -16,7 +20,8 @@ export const sensorlessXTemplate = (config: PrinterConfiguration, utils: Klipper [${utils.getAxisDriverSectionName(PrinterAxis.x)}] ${utils.getAxisDriverDiagConfig(PrinterAxis.x)} -${utils.getAxisDriverStallGuardThreshold(PrinterAxis.x, 0.5)} +${hasPretunedConfig ? '# Printer has a pretuned sensorless homing config, uncomment the next line to override it' : ''} +${hasPretunedConfig ? '#' : ''}${utils.getAxisDriverStallGuardThreshold(PrinterAxis.x, 0.5)} [${utils.getAxisStepperName(PrinterAxis.x)}] endstop_pin: ${utils.getAxisVirtualEndstop(PrinterAxis.x)} @@ -24,10 +29,15 @@ homing_retract_dist: 0 [gcode_macro RatOS] variable_homing_x: "sensorless" -variable_sensorless_x_current: ${utils.getAxisDriverHomingCurrent(PrinterAxis.x, 0.35)} +${hasPretunedConfig ? '# Printer has a pretuned sensorless homing config, uncomment the next line to override it' : ''} +${hasPretunedConfig ? '#' : ''}variable_sensorless_x_current: ${utils.getAxisDriverHomingCurrent(PrinterAxis.x, 0.35)} `; -export const sensorlessYTemplate = (config: PrinterConfiguration, utils: KlipperConfigUtils) => ` +export const sensorlessYTemplate = ( + config: PrinterConfiguration, + utils: KlipperConfigUtils, + hasPretunedConfig: boolean, +) => ` # Sensorless homing. # # Tune the current variable and the SGTHRS value in the included file(s) untill you get reliable homing. @@ -42,7 +52,8 @@ export const sensorlessYTemplate = (config: PrinterConfiguration, utils: Klipper [${utils.getAxisDriverSectionName(PrinterAxis.y)}] ${utils.getAxisDriverDiagConfig(PrinterAxis.y)} -${utils.getAxisDriverStallGuardThreshold(PrinterAxis.y, 0.5)} +${hasPretunedConfig ? '# Printer has a pretuned sensorless homing config, uncomment the next line to override it' : ''} +${hasPretunedConfig ? '#' : ''}${utils.getAxisDriverStallGuardThreshold(PrinterAxis.y, 0.5)} [stepper_y] endstop_pin: ${utils.getAxisVirtualEndstop(PrinterAxis.y)} @@ -50,5 +61,6 @@ homing_retract_dist: 0 [gcode_macro RatOS] variable_homing_y: "sensorless" -variable_sensorless_y_current: ${utils.getAxisDriverHomingCurrent(PrinterAxis.y, 0.51)} +${hasPretunedConfig ? '# Printer has a pretuned sensorless homing config, uncomment the next line to override it' : ''} +${hasPretunedConfig ? '#' : ''}variable_sensorless_y_current: ${utils.getAxisDriverHomingCurrent(PrinterAxis.y, 0.51)} `;