├── Gcode.md
├── README.md
├── Skew Calibration
├── Skew-Calibration.md
├── skew.step
└── skew_calculator.xlsx
├── Speed Adjustment
├── BL-speed-adjust.md
└── README.md
└── scripts
└── python
├── readme.md
├── skew_calibration.py
├── speed_adjust.py
└── vibration_plotter.py
/Gcode.md:
--------------------------------------------------------------------------------
1 | # Gcode Commands for Bambu X1 Printers
2 |
3 |
4 | ## Overview
5 |
6 | This wiki provides documentation over BambuLabs Gcode, specifically for the X1 series. A majority of these commands work on other Bambu devices, but we have not tested on other devices. Several of these commands are being documented for the first time, and please take caution when using them. See below for information on the commands that X1Plus uses.
7 |
8 | ## Table of Contents
9 | - [Overview](#overview)
10 | - [Homing and Positioning Commands](#homing)
11 | - [Temperature Control](#temperature-control)
12 | - [Movement Commands](#movement)
13 | - [Linear Movements](#g0-and-g1)
14 | - [Arc Movements](#g2-and-g3)
15 | - [Coordinates](#coordinates)
16 | - [Coordinate system commands](#coordinate-commands)
17 | - [Useful Coordinates](#useful-coordinates)
18 |
19 |
20 | ## Identifying Gcode format and parameters
21 |
22 | On the X1C and X1E, every Gcode command that the printer parses can be followed in realtime by monitoring the service `forward` or by viewing the output it generates in syslog.log. I have written a [script](https://github.com/jphannifan/x1plus-testing/blob/main/scripts/python/log_monitor.py) that automates this monitoring step, allowing you to view the output of any Gcode command without needing to open log files. To run this script, place it on your SD card and run
23 | `python3 /sdcard/log_monitor.py`
24 |
25 | You'll then be prompted to enter a Gcode command. Multiline Gcode commands must be separated by an escape character. If you discover new commands or find that a command on this page is documented incorrectly, please notify me or file an issue. When you publish a command that the device does not recognize, you'll typically receive this response:
26 |
27 | `Gcode process inject cmd {command} fail!(0x11000005)
28 | `
29 |
30 |
31 |
32 | ## Homing
33 | | Command | Argument | Use |
34 | |---|---|---|
35 | | G29 | - | Bed mesh calibration |
36 | | G29.1* | Z[z_offset] | Set z offset (mm)|
37 | | G28 | - | Home all axes |
38 | | G28 | X | XY homing |
39 | | G28 | Z P0 | Low precision Z home |
40 | | G28 | Z P0 T[temp] | Lower precision Z home + heated nozzle |
41 | | G380** | S2 Z[z] F[speed] | Move Z axis only (Z: mm, F: mm/s) |
42 | | G90 | - | Absolute coordinates |
43 | | G91 | - | Relative coordinates |
44 | | G92 | E0 | Reset extruder position |
45 | | M83 | - | Set extruder relative |
46 |
47 | * Textured PEI offset = -0.04 mm, default offset = 0 mm
48 |
49 | ** Must be preceded by G91!
50 |
51 | ## Temperature Control
52 | | Command | Argument | Range | Use |
53 | |---|---|---|---|
54 | | M104 | S[temp] | 0-300 | Set hotend temperature |
55 | | M109 | S[temp] | 0-300 | Pause Gcode execution until set nozzle temp is reached |
56 | | M140 | S[temp] | 0-110 | Set bed temperature |
57 | | M190 | S[temp] | 0-110 | Pause Gcode execution until set bed temp is reached |
58 | | M106 | P1 S[speed] | 0-255 | Set part fan speed |
59 | | M106 | P2 S[speed] | 0-255 | Set aux fan speed |
60 | | M106 | P3 S[speed] | 0-255 | Set chamber fan speed |
61 |
62 | ## Movement
63 |
64 | ### G0 and G1
65 | | Command | X Y Z | F | E | Usage |
66 | |---|---|---|---|---|
67 | | G0 | Absolute or relative (mm) | Feed rate used from start to end point (mm/s) | N/A | Linear non-print movement |
68 | | G1 | Absolute or relative (mm) | Feed rate used from start to end point (mm/s) | Abs/rel extrusion (mm) | Linear print movement |
69 | ```gcode
70 | Usage:
71 | G0 X0 Y0 F10000 ; Linear move to 0,0 at 10,000 mm/s^2 without extrusion
72 | G1 X0 Y0 F10000 E5 ; Linear move to 0,0 at 10,000 mm/s^2 while extruding 5 mm of filament
73 | G1 F20000 ; adjust acceleration to be used with upcoming movements
74 | Note: the coordinate settings (relative or absolute) should always be specified before and after these movements
75 | ```
76 |
77 | ### G2 and G3
78 | | Command | X Y Z | I | J | R | F | E | Usage |
79 | |---|---|---|---|---|---|---|---|
80 | | G2 | Absolute or relative (mm) | X offset (relative, mm) | Y offset (relative, mm) | Radius | Feed rate used from start to end point (mm/s) | Abs/rel extrusion (mm) | Clockwise arc |
81 | | G3 | Absolute or relative (mm) | X offset (relative, mm) | Y offset (relative, mm) | Radius | Feed rate used from start to end point (mm/s) |Abs/rel extrusion (mm) (mm) | Counter-clockwise arc |
82 | ```gcode
83 | G3 X2 Y7 I-4 J-3 ; Counter-clockwise arc movement offset by (-4,-3) - no extrusion
84 | G2 X2 Y7 I-4 J-3 ; Clockwise arc movement offset by (-4,-3) - no extrusion
85 | G3 X2 Y7 R5 ; Counter-clockwise arc movement with radius of 5 (mm)
86 | G3 X20 Y20 R5 E0.5 ; Counter-clockwise arc around (20,20) with a radius of 5, extruding 0.5mm of filament
87 | ```
88 |
89 | ### Useful coordinates
90 | | Location | X | Y | Z |
91 | |---|---|---|---|
92 | | Center of build plate | 128 | 128 | - |
93 | | LiDAR reference grid | 240 | 90 | 8 |
94 | | Print finished position | 65 | 260 | 10 |
95 | | Bed tramming screw 1 | 134.8 | 242.8 | - |
96 | | Bed tramming screw 2 | 33.2 | 13.2 | - |
97 | | Bed tramming screw 3 | 222.8 | 13.2 | - |
98 | | Build plate nozzle wipe tab | 135 | 253 | - |
99 | | Purge line start | 18 | 1.0 | 0.8 |
100 |
101 | ## Motor controls
102 | | Command | Argument | Usage
103 | |---|---|---|
104 | | M17 | X Y Z | Set stepper current (amps)
105 | | M17 | R | Restore default values
106 | | M17 | S | Enable steppers
107 | | M18 | none | Disable all steppers
108 | | M18 | X Y Z E | Disable steppers for certain axes
109 | Note: (X,Y,Z) = (1.2, 1.2, 0.75) defined as defaults in slicer
110 |
111 | ## Limit and endstop commands
112 | | Command | Argument | Default | Usage
113 | |---|---|---|---|
114 | | M201 | Z (mm/s) | - | Z axis acceleration limit |
115 | | M204.2 | K | 1.0 | Set acceleration multiplier
116 | | M220 | S | 100 | Set Feed Rate
117 | | M204 | S | - | Acceleration limit (mm/s^2)
118 | | M205 | X Y Z E (mm/s) | 0 | Set jerk limits
119 | | M211 | X Y Z (mm) | - | Set soft endstops
120 | | M221 | S | 100 | Set Flow Rate
121 | | M221 | S | - | Push soft endstop status
122 | | M221 | Z0 | - | Turn off Z endstop
123 | ```gcode
124 | M221 S100 ; set the flow ratio to default (100%)
125 | M221 S ; Push soft endstop status
126 | M221 X0 Y0 Z1 ; turn off X and Y endstops, turn on Z endstop
127 | M221 Z1 ; enable Z endstop
128 | ```
129 |
130 | ## Printer configuration
131 | | Command | Arguments | Usage |
132 | |---|---|---
133 | | M412 | S0/S1 | Toggle filament runout detection
134 | | M302 | S70 P0/P1 | Toggle cold extrusion
135 | | M975 | S0/S1 | Toggle vibration compensation
136 | | M982.2 | C0/C1 | Disable motor noise cancellation
137 | | G29.2 | S0/S1 | Toggle bed mesh compensation
138 | | M1003 | S0/S1 | Toggle power loss recovery
139 | | M500 | - | Save to EEPROM
140 |
141 | ## Print Speed - [Click here for more info](https://github.com/jphannifan/x1plus-testing/blob/main/BL-speed-adjust.md)
142 | | Command | Argument | Usage |
143 | |---|---|---|
144 | | M204.2 | K (unitless) | acceleration magnitude (default=1) |
145 | | M220 | K (unitless) | feed rate (default=1) |
146 | | M73.2 | R (unitless) | time constant (default=1) |
147 | | M1002 | set_gcode_claim_speed_level | (default=5) |
148 |
149 | ## Vibration Compensation
150 | | Command | Arguments | Usage
151 | |---|---|---
152 | | M970 | Q A B C H K | Vibration compensation frequency sweep
153 | | M970.3 | Q A B C H K | Vibration compensation fast sweep
154 | | M974 | Q[axis] S2 P0 | Apply curve fitting to vibration compensation data
155 | | M975 | S0/S1 | Toggle vibration compensation
156 | | M982 | Q P V D L T I | Motor noise cancellation
157 | | M982.4 | S V | Motor noise cancellation
158 |
159 | ```gcode
160 | Vibration compensation:
161 | M970 Q1 A7 B10 C125 K0 ; X axis range 1
162 | M970 Q1 A7 B125 C250 K1 ; X axis range 2
163 | M974 Q1 S2 P0 ; X axis curve fit
164 | M970 Q0 A9 B10 C125 H20 K0 ; Y axis range 1
165 | M970 Q0 A9 B125 C250 K1; Y axis range 2
166 | M974 Q0 S2 P0 ; Y axis curve fit
167 | M975 S1 ; enable vibration comp
168 |
169 | Q: 0 for X axis, 1 for Y axis
170 | A: amplitude (Hz)
171 | B: lower range of sweep (Hz)
172 | C: upper range of sweep (Hz)
173 | H: undefined - optional parameter - units in Hz
174 | K: not sure but = 0 or 1
175 | ```
176 |
177 | | Command | Arguments | Usage
178 | |---|---|---|
179 | | M900 | K L M | Apply pressure advance to active filament preset |
180 | | M900 | - | Publish to return currently saved pressure advance |
181 |
182 |
183 | ## Skew and XY compensation - skew compensation wiki coming soon
184 | | Command | Argument | Usage
185 | |---|---|---|
186 | | M1005 | X Y (mm) | Calculates skew (rad) from lengths of diagonals measured |
187 | | M1005 | I (rad) | Overwrite skew value on printer |
188 | | M1005 | P0/P1 | Toggle skew compensation (0 = off, 1 = on) |
189 | | M290.2 | X Y (mm) | XY compensation |
190 | | M290 | X Y (mm) | XY compensation? |
191 |
192 | ## LED controls
193 | | Argument | Value | Usage |
194 | |---|---|---|
195 | | M960 S1 | 0 or 1 | Toggle horizontal laser |
196 | | M960 S2 | 0 or 1 | Toggle vertical laser |
197 | | M960 S4 | 0 or 1 | Toggle nozzle LED |
198 | | M960 S5 | 0 or 1 | Toggle logo led |
199 | | M960 S0 | 0 or 1 | Toggle all LEDs |
200 |
201 | ## Camera Controls
202 | | Command | S | P | Usage
203 | |---|---|---|---|
204 | | M973 | S3 | - | Nozzle cam on
205 | | M973 | S4 | - | Nozzle cam off
206 | | M973 | S1 | | Nozzle cam autoexpose
207 | | M973 | S | P[exposure] | Set nozzle camera exposure
208 | | M971 | S | P[exposure] | Capture image to /userdata/log/
209 | | M976 | S1 | P[num] | First layer scan
210 | | M976 | S2 | P1 | Hotbed scan
211 | | M976 | S3 | P2 | Register void printing detection
212 | | M991 | S0 | P0 | Notify printer of layer change
213 | | M991 | S0 | P-1 | End smooth timelapse at safe pos
214 | | M981 | S0 | P20000 | Spaghetti detector off
215 | | M981 | S1 | P20000 | Spaghetti detector on
216 | | M972 | S5 | P0 | Measure Xcam clarity
217 |
218 |
219 | ```gcode
220 | M973 S6 P0 ; auto expose for horizontal laser
221 | M973 S6 P1 ; auto expose for vertical laser
222 | ```
223 |
224 | | Command | Argument | Usage
225 | |---|---|---
226 | | M1002 | gcode_claim_action | Display message on LCD
227 | | M1002 | judge_flag | Display message on LCD
228 | | M1002 | set_gcode_claim_speed_level | Update speed profile setting on LCD
229 | ```gcode
230 | 0 Clear
231 | 1 Auto bed levelling
232 | 2 Heatbed preheating
233 | 3 Sweeping XY mech mode
234 | 4 Changing filament
235 | 5 M400 pause
236 | 6 Paused due to filament runout
237 | 7 Heating hotend
238 | 8 Calibrating extrusion
239 | 9 Scanning bed surface
240 | 10 Inspecting first layer
241 | 11 Identifying build plate type
242 | 12 Calibrating Micro Lidar
243 | 13 Homing toolhead
244 | 14 Cleaning nozzle tip
245 | 15 Checking extruder temperature
246 | 16 Paused by the user
247 | 17 Pause due to the falling off of the tool head’s front cover
248 | 18 Calibrating the micro lidar
249 | 19 Calibrating extruder flow
250 | 20 Paused due to nozzle temperature malfunction
251 | 21 Paused due to heat bed temperature malfunction
252 | ```
253 |
254 |
255 | | Command | Argument | Usage
256 | |---|---|---|
257 | | M400 | - | Finish the current | Pause until last Gcode command is complete
258 | | M400 | S[t] | Wait for last command to complete + delay in seconds
259 | | M400 | P[t] | Wait for last command to complete + delay in milliseconds
260 | | M400 | U1 | Wait until user presses 'Resume'
261 | | G4 | S90 | Delay 90 seconds (does not block Gcode execution)*
262 | | M73 | P[p] R[r] | Update print progress
263 | | M73.2 | R1.0 | Reset print progress
264 | *note: 90s is max value that can be delayed with G4. To delay for > 90 sec, run multiple G4 S90
265 |
266 | ### AMS
267 | ```gcode
268 | M622 J{j}
269 | M623
270 |
271 | Retract filament
272 | M620 S255
273 | ; retraction gcode
274 | M621 S255
275 | M622.1 S1
276 | ```
277 | # Select extruder
278 | ```gcode
279 | T255
280 | ```
281 |
282 |
283 |
284 | | Command | Argument | Usage
285 | |---|---|---
286 | | T | 255 | Switch to empty tool
287 | | T | 1000 | Switch to nozzle
288 | | T | 1100 | Switch to scanning space
289 |
290 | note: X1 uses M620 to perform actual toolchange
291 |
292 |
293 | M620 M
294 | M620 S[initial_no_support_extruder]A
295 | M621 S[initial_no_support_extruder]A
296 | M620.1 E F{accel} T{temp}
297 |
298 | M620
299 | M620 C# - calibrate AMS by AMS index
300 | M620 R# - refresh AMS by tray index
301 | M620 P# - select AMS tray by tray index
302 | M620 S# - select AMS by tray index
303 | M621
304 | M621 S#- load filament in AMS by tray index
305 | Stolen from Doridian's repo https://github.com/Doridian/OpenBambuAPI/blob/main/gcode.md
306 |
307 | # undocumented still - use with caution!
308 | ```gcode
309 | G29.4 S0/S1 - toggles "high freq z comp"
310 | G29.5 - "G29.5 failed: invalid params"
311 | G29.6, G29.7, G29.8 - runs normal bed probing sequence?
312 | M969 S1 N3 A2000
313 | M964: NO_NEW_EXTRIN_CALI_PARA
314 | M963 squence dismatch! Need(15) Get(15) rtn=0x1
315 | M967
316 | M966
317 | M980.3 A B C D E F G H I J K
318 | G92.1 E0
319 | M1001 invalid test/sub-test case!
320 | ```
321 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # x1plus-testing
2 | x1plus branch testing
3 |
--------------------------------------------------------------------------------
/Skew Calibration/Skew-Calibration.md:
--------------------------------------------------------------------------------
1 | under construction
2 |
--------------------------------------------------------------------------------
/Skew Calibration/skew_calculator.xlsx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jphannifan/x1plus-testing/8babb51b4c32d7948e24ab020228b45cd01f2c07/Skew Calibration/skew_calculator.xlsx
--------------------------------------------------------------------------------
/Speed Adjustment/BL-speed-adjust.md:
--------------------------------------------------------------------------------
1 | # Bambu Print Speed Gcode:
2 |
3 | ### Speed profile Gcode for Bambu printers
4 | ```
5 | Compatible with A1, P1, and X1 series printers:
6 | M204.2 K1.0 ; acceleration magnitude (unitless)
7 | M220 K1.0 ; feed rate (unitless)
8 | M73.2 R1.0 ; time remaining constant (unitless)
9 | M1002 set_gcode_claim_speed_level 5 ; speed level
10 | ```
11 |
12 | ### How to use this?
13 | For A1, P1, and X1 owners who know how to use python, install `paho-mqtt` with pip and then run this script: [speed_adjust.py](https://github.com/jphannifan/x1plus-testing/blob/main/scripts/python/speed_adjust.py). The first time you run this, you'll be asked to input your printer's IP, serial number, and LAN code, but these credentials are saved locally so you only need to provide this info once. If you've set this up correctly, you will be prompted to choose one of the following:
14 |
15 | Option 0: input a speed (value between 30 and 180%), script will publish the gcode to your device.
16 |
17 | Option 1: input your current speed, target speed, step size (%), and time interval (sec), and the script will incrementally adjust your print speed.
18 |
19 | ### Bambu Speed Profiles
20 | Bambu printers all have 4 predefined speed profiles: Silent, Normal, Sport, and Ludicrous. Each profile contains an acceleration multiplier, a feed rate multiplier, and a time constant, which it uses to scale the speed settings configured in your slicer profiles. While there are only 4 presets in the UI, it is possible to apply custom speed profiles on *all* Bambu devices. Currently, unless you are an X1Plus user, you are limited to applying custom speed profiles via MQTT. Bambu Studio and Bambu Handy will automatically adjust time estimates, but please be aware that the **time remaining estimate and the reported print speed percentage are completely independent of feed rate and acceleration!** If you do not adjust all 3 of these parameters simultaneously, it will be nearly impossible to maintain constant extrusion profiles.
21 |
22 |
23 |
24 | _Slicer displaying custom speed that was applied with an MQTT command_
25 |
26 | ## Gcode Commands
27 |
28 | ### M204.1 K
29 | Acceleration magnitude, or the multiplier that is applied to your slicer profile acceleration settings. For "Normal" mode, K=1.0. This command overrides your slicer settings and applies a multiplier to change the rate of toolhead movement. The argument of this command is unitless.
30 |
31 | ### M220 K
32 | Feed rate multipler is used to adjust extrusion and feed rate. Similarly to the acceleration magnitude command, this command applies a unitless multiplier to your slicer settings (ie, it WILL override your slicer settings). The default value (Normal mode) is K=1.0.
33 |
34 | ### M73.2 R
35 | Time remaining multiplier. This parameter supplied to this command is used to adjust both the time remaining estimate and the displayed speed percentage in the slicer, Bambu Handy, and the touchscreen UI. This is easily calculated by dividing the target speed percentage by 100 and taking the inverse of this fraction. For instance, 200% speed would be a time multiplier of (200/100)^-1 = 0.5.
36 |
37 | ### M1002
38 | This command is used by the UI to find the correct string value for a speed profile. It does not have any effect on print behavior. If you are applying your own speed profiles, you may exclude this command.
39 |
40 | ## Interpolating to define custom speed profiles
41 | Below are the parameters for each of Bambu's speed profiles, which I obtained via syslog. Our goal here is to define trendlines that allow us to map an acceleration magnitude and feed rate to any percentage in this range. In order to do this, we need to find a way to calculate both acceleration magnitude and feed rate with just one independent variable.
42 | | | **Speed (%)** | **time_remaining** | **accel. magnitude** | **feed rate** |
43 | |:------:|:-------------:|:------------------:|:--------------------:|:-------------:|
44 | | Silent | 50 | 2 | 0.3 | 0.7 |
45 | | Normal | 100 | 1 | 1 | 1 |
46 | | Sport | 125 | 0.8 | 1.4 | 1.4 |
47 | | Luda | 166 | 0.6 | 1.6 | 2 |
48 |
49 | ## Fitting trendlines to these data:
50 |
51 |
52 | - **Speed Fraction**:
53 | ```math
54 | $$ \text{speed\_fraction} = floor\left\lfloor \frac{10000}{\text{speed\_percent}} \right\rfloor / 100 $$
55 | ```
56 | - **Acceleration Magnitude**:
57 | ```math
58 | $$ \text{acc\_mag} = \exp\left(\frac{\text{speed\_percent} - 1.0191}{-0.8139}\right) $$
59 | ```
60 | - **Feed Rate**:
61 | ```math
62 | $$ \text{feed\_rate} = 6.426 \times 10^{-5} \times \text{speed\_percent}^2 - 2.484 \times 10^{-3} \text{speed\_percent} + 6.54\times 10^{-1} $$
63 | ```
64 | ## Interpolated parameters (compare these with the table above)
65 | | | **Speed (%)** | **time_remaining** | **accel. magnitude** | **feed rate** |
66 | |:------:|:-------------:|:------------------:|:--------------------:|:-------------:|
67 | | Silent | 50 | 2 | 0.300 | 0.690 |
68 | | Normal | 100 | 1 | 1.024 | 1.048 |
69 | | Sport | 125 | 0.8 | 1.309 | 1.348 |
70 | | Luda | 166 | 0.602 | 1.669 | 2.012 |
71 |
72 |
73 | ## Notes
74 | - While you can edit each parameter individually, I highly recommend you apply M220, M204.2, and M73.2 together at once. Otherwise you will experience print artifacts due to rapid adjustments in extrusion rate.
75 | - As long as you apply speed profiles within or close to the interpolated range (~35-175%), you will have consistent print results across this entire range of speeds. Outside of the interpolated range, these equations do not scale extrusion rate proportionally with toolhead movement! You should consider a different trendline for values above 175%.
76 | - M204.2 will alter the toolhead speed in print startup and even with the manual axis controls. Likewise, M220 alters the extrusion rate and the "speed" of the touchscreen extruder controls. Bambu's speed profiles have this effect as well. (tldr you can use this to speed up the extruder controls)
77 |
--------------------------------------------------------------------------------
/Speed Adjustment/README.md:
--------------------------------------------------------------------------------
1 | # X1Plus Print Speed Adjustment UI
2 |
3 |
4 |
5 | a. Current speed
6 |
7 | b. Print time estimate (for the selected value)
8 |
9 | c. Acceleration magnitude constant (M204.2)
10 |
11 | d. Feed rate constant (M220)
12 |
13 | e. Time remaining constant (M73.2)
14 |
15 | f. Toggle step size of the dial (2 or 10%)
16 |
17 | ## Background
18 |
19 | X1Plus adds a custom UI element that provides greater control over print speed than the OEM UI offers. A dial with an adjustable step size allows users to apply any speed multiplier from 30 to 166%. Additionally, the UI calculates the estimated print time remaining for the selected speed, allowing users to see in real time how these adjustments affect uptime.
20 |
21 | ## How to enable
22 |
23 | By default, the OEM speed adjustment UI is displayed to all X1Plus users, and this customized UI can be enabled via two methods:
24 | 1. Open the speed adjustment menu and press the ">" icon in the corner of the window.
25 |
26 |
27 | 2. Using the X1Plus CLI, set the following setting:
28 |
29 | `x1plus settings set printerui.speedadjust true`
30 |
31 | X1Plus will remember this choice until you press the "<" icon on the X1Plus speed adjust UI or modify the setting.
32 |
33 | ## Step size
34 | The step size of the dial can be adjusted by toggling the 'Step size' button in the window or by modifying the following setting with the X1Plus settings CLI:
35 |
36 | `x1plus settings set printerui.speedadjust.stepSize 2`
37 |
38 |
39 | ## Gcode sequence and calculations
40 |
41 | Print speed adjustment on X1, P1, and A1 Bambu printers is applied with a series of 4 different Gcode commmands:
42 |
43 | ### M204.1 K1.0
44 | Acceleration magnitude. K is a unitless multiplier of acceleration
45 |
46 | ### M220 K1.0
47 | Feed rate. K is a unitless multiplier of feed rate
48 |
49 | ### M73.2 R1.0
50 | Time remaining. The percentage speed is calculated from the inverse of R x 100.
51 |
52 | ### M1002 set_gcode_claim_speed_level 5
53 | Tells the UI which string to display ("Silent", "Normal", "Sport", or "Ludicrous"). It can be ommitted
54 |
55 | ## Interpolation of M204.2 and M220 parameters
56 |
57 | Below are the parameters for each of Bambu's speed profiles obtained from log data. X1Plus uses cubic spline interpolation and linear extrapolation to calculate acceleration magnitude and feed rate, which it then publishes to the printer when the 'Apply' button is pressed.
58 |
59 | | | **Speed (%)** | **time_remaining** | **accel. magnitude** | **feed rate** |
60 | |:------:|:-------------:|:------------------:|:--------------------:|:-------------:|
61 | | Silent | 50 | 2 | 0.3 | 0.7 |
62 | | Normal | 100 | 1 | 1 | 1 |
63 | | Sport | 125 | 0.8 | 1.4 | 1.4 |
64 | | Luda | 166 | 0.6 | 1.6 | 2 |
65 |
66 |
67 |
--------------------------------------------------------------------------------
/scripts/python/readme.md:
--------------------------------------------------------------------------------
1 | ## Collection of Python scripts for X1Plus and X1C Gcode
2 |
--------------------------------------------------------------------------------
/scripts/python/skew_calibration.py:
--------------------------------------------------------------------------------
1 | import math
2 | import os
3 | import time
4 | import re
5 | import dds
6 | import json
7 |
8 | # This is pretty shitty code
9 | stored_value = {}
10 |
11 | def pub_dds(ddspub, cmd):
12 | msg = {"command": "gcode_line", "param": cmd, "sequence_id": 0}
13 | ddspub(json.dumps(msg))
14 | print(f"Command {cmd} published")
15 |
16 | def handle_new_value(match, ddspub):
17 | print(f"M1005 new value: {match.group(1)}")
18 | if input("Save this value? (y/n): ").lower() == 'y':
19 | stored_value['XY_comp_ang'] = match.group(1)
20 | pub_dds(ddspub, "M500")
21 | print("Value saved and command sent to printer.")
22 |
23 | def handle_old_value(match):
24 | print(f"Current XY_comp_ang: {match.group(1)}")
25 |
26 | def log_monitor(ddspub, regex_actions):
27 | try:
28 | with open("/tmp/syslog.log", "r") as file:
29 | file.seek(0, os.SEEK_END) # Start at the end of the file
30 | while True:
31 | line = file.readline()
32 | if not line:
33 | time.sleep(0.5) # Sleep briefly to avoid busy waiting
34 | continue
35 | for pattern, action in regex_actions:
36 | if match := re.search(pattern, line):
37 | action(match) # Pass only the match object
38 | return # Exit after first match to stop the log monitor
39 | except Exception as e:
40 | print(f"Failed to monitor log file: {e}")
41 |
42 | def delete_values(ddspub):
43 | stored_value.clear()
44 | pub_dds(ddspub, "M1005 I0 \nM500 \n")
45 | print("M1005 I0 - skew compensation factor set to 0.")
46 |
47 | def get_values(ddspub, regex_actions):
48 | pub_dds(ddspub, "M1005")
49 | log_monitor(ddspub, regex_actions) # Start and stop monitoring upon finding a match
50 |
51 | def calculate_geometry(ddspub):
52 | try:
53 | CD = float(input("Enter measurement for CD: "))
54 | AB = float(input("Enter measurement for AB: "))
55 | AC = float(input("Enter measurement for EF: ")) / math.sqrt(2)
56 | CB = round(math.sqrt(2 * CD**2 + 2 * AB**2 - 4 * AC**2) / 2,2)
57 | skew_factor = -round(math.tan(math.pi/2 - math.acos((CD**2 - CB**2 - AC**2) / (2 * CB * AC))),5)
58 | print(f"Calculated skew factor: {skew_factor}")
59 | stored_value['CB'] = CB
60 | stored_value['Skew Factor'] = skew_factor
61 | if input(f"Apply gcode M1005 I{skew_factor}? (y/n): ").lower() == 'y':
62 | pub_dds(ddspub, f"M1005 I{skew_factor}")
63 | if input(f"Save gcode M1005 I{skew_factor}? (y/n): ").lower() == 'y':
64 | pub_dds(ddspub, "M500")
65 | print(f"Skew factor {skew_factor} applied and saved")
66 | except Exception as e:
67 | print(f"An error occurred during calculation: {e}")
68 |
69 | def main_menu(ddspub):
70 | regex_actions = [
71 | (r".*M1005:new\s*XY_comp_ang\s*=\s*(-?\d+\.?\d*)", lambda match: handle_new_value(match, ddspub)),
72 | (r".*M1005:current\s*XY_comp_ang\s*=\s*(-?\d+\.?\d*)", lambda match: handle_old_value(match)),
73 | ]
74 | while True:
75 | skew_factor = stored_value.get('Skew Factor')
76 | if skew_factor is not None:
77 | printstr = f"3. Apply and save calculated factor: {skew_factor}"
78 | else:
79 | printstr = "3. Calculate compensation factor"
80 | print("\nMenu Options:")
81 | print("1. Delete current skew values on the printer")
82 | print("2. Get current values on the printer")
83 | print(printstr)
84 | print("4. Apply and save your own skew compensation")
85 | choice = input("Enter your choice: ")
86 | if choice == '1':
87 | delete_values(ddspub)
88 | elif choice == '2':
89 | get_values(ddspub, regex_actions)
90 | elif choice == '3':
91 | if skew_factor is not None:
92 | pub_dds(ddspub, f"M1005 I{skew_factor} \nM500 \n")
93 | print(f"Publishing M1005 {skew_factor} M500")
94 | else:
95 | calculate_geometry(ddspub)
96 | elif choice == '4':
97 | custom_skew = float(input("Enter skew factor: "))
98 | pub_dds(ddspub, f"M1005 I{custom_skew} \nM500 \n")
99 | print(f"Publishing M1005 {custom_skew} M500")
100 | else:
101 | print("Invalid option, please try again.")
102 |
103 | def main():
104 | pub = dds.publisher('device/request/print')
105 | time.sleep(1.5)
106 | main_menu(pub)
107 |
108 | if __name__ == "__main__":
109 | try:
110 | main()
111 | except Exception as e:
112 | dds.shutdown()
113 | print(f"An error occurred: {e}")
114 | raise
115 |
--------------------------------------------------------------------------------
/scripts/python/speed_adjust.py:
--------------------------------------------------------------------------------
1 | import paho.mqtt.client as mqtt
2 | import json
3 | import ssl
4 | import time
5 | import os
6 | import math
7 |
8 | settings_file = 'mqtt_settings.json'
9 |
10 | def speed_interp(speed_percentage):
11 | """Calculate the speed interpolation values for the given speed percentage."""
12 | if speed_percentage < 30 or speed_percentage > 180:
13 | speed_percentage = 100 # Ensure the speed is within the valid range.
14 | speed_fraction = math.floor(10000 / speed_percentage) / 100
15 | acceleration_magnitude = math.exp((speed_fraction - 1.0191) / -0.814)
16 | feed_rate = (0.00006426) * speed_percentage ** 2 + (-0.002484) * speed_percentage + 0.654
17 | return f"M204.2 K{acceleration_magnitude:.2f}\n" \
18 | f"M220 K{feed_rate:.2f}\n" \
19 | f"M73.2 R{speed_fraction}\n"
20 |
21 | def on_connect(client, userdata, flags, reason_code, properties):
22 | if reason_code != 0:
23 | print("Failed to connect with reason code:", reason_code)
24 |
25 | def on_message(client, userdata, message):
26 | print("Message received:", message.payload.decode("utf-8"))
27 |
28 | def save_settings(address, password, dev_id):
29 | with open(settings_file, 'w') as file:
30 | json.dump({'address': address, 'password': password, 'dev_id': dev_id}, file)
31 |
32 | def load_settings():
33 | if os.path.exists(settings_file):
34 | with open(settings_file, 'r') as file:
35 | return json.load(file)
36 | return None
37 |
38 | def main():
39 | client = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2)
40 | settings = load_settings()
41 | if settings is None:
42 | password = input("Enter Printer LAN code: ")
43 | address = input("Enter printer IP: ")
44 | dev_id = input("Enter your device's serial number: ")
45 | save_settings(address, password, dev_id)
46 | else:
47 | address, password, dev_id = settings['address'], settings['password'], settings['dev_id']
48 |
49 | client.username_pw_set('bblp', password)
50 | client.tls_set(tls_version=ssl.PROTOCOL_TLS, cert_reqs=ssl.CERT_NONE)
51 | client.tls_insecure_set(True)
52 | client.on_connect = on_connect
53 | client.on_message = on_message
54 | client.connect(address, 8883, 60)
55 | client.loop_start()
56 |
57 | input_speed = int(input("Enter the initial speed percentage (even value between 30 and 180): "))
58 | target_speed = int(input("Enter the target speed percentage (even value between 30 and 180): "))
59 | step_size = int(input("Enter the step size: "))
60 | time_interval = float(input("Enter the time interval between commands (seconds): "))
61 |
62 | try:
63 | speed = input_speed
64 | direction = 1 if target_speed >= input_speed else -1
65 | while (direction == 1 and speed <= target_speed) or (direction == -1 and speed >= target_speed):
66 | gcode_command = speed_interp(speed)
67 | topic = f"device/{dev_id}/request"
68 | message = {
69 | "print": {
70 | "command": "gcode_line",
71 | "sequence_id": 0,
72 | "param": gcode_command
73 | }
74 | }
75 | client.publish(topic, json.dumps(message))
76 | print(f"Message '{message}' sent to topic '{topic}'")
77 | time.sleep(time_interval)
78 | speed += step_size * direction
79 | finally:
80 | client.loop_stop()
81 | client.disconnect()
82 |
83 | if __name__ == "__main__":
84 | main()
85 |
--------------------------------------------------------------------------------
/scripts/python/vibration_plotter.py:
--------------------------------------------------------------------------------
1 | import matplotlib.pyplot as plt
2 | import json
3 | import sys
4 | import numpy as np
5 | import matplotlib.ticker as ticker
6 |
7 | # Plot vibration compensation data from X1Plus
8 | # JSON file is located in /mnt/sdcard/x1plus/printer/{serial}/logs/vibration_comp.json
9 |
10 | if len(sys.argv) < 2:
11 | print("Usage: python3 script.py ")
12 | sys.exit(1)
13 |
14 | json_file_path = sys.argv[1]
15 |
16 | with open(json_file_path, 'r') as file:
17 | data = json.load(file)
18 |
19 | runs = data['runs']
20 |
21 | plt.figure(figsize=(10, 6))
22 |
23 | for timestamp, details in runs.items():
24 | frequencies = sorted([int(freq) for freq in details['axes']['x']['points'].keys()])
25 | x_responses = [details['axes']['x']['points'][str(freq)].get('a', 0) for freq in frequencies]
26 | y_responses = [details['axes']['y']['points'][str(freq)].get('a', 0) for freq in frequencies]
27 |
28 | plt.plot(frequencies, x_responses, label=f'X Axis Response @ {timestamp}')
29 | plt.plot(frequencies, y_responses, label=f'Y Axis Response @ {timestamp}', linestyle='--')
30 |
31 | plt.xlabel('Frequency (Hz)')
32 | plt.ylabel('Response Value (a)')
33 | plt.title('Frequency Response for X and Y Axes Across Runs')
34 |
35 | desired_ticks = np.linspace(min(frequencies), max(frequencies), 12)
36 | plt.xticks(ticks=desired_ticks, labels=[f"{int(tick)}" for tick in desired_ticks])
37 |
38 | plt.legend()
39 | plt.tight_layout()
40 | plt.show()
41 |
--------------------------------------------------------------------------------