├── 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 | log_monitor 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 | image 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 | interpolate 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 | image 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 | image 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 | image 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 | --------------------------------------------------------------------------------