Guest User

3DloopBambulabA1MINITuto

a guest
Jul 15th, 2025
26
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 45.23 KB | None | 0 0
  1. Automated Printing on A1 Mini Tutorial
  2.  
  3. ***Disclaimer: This could potentially damage you printer as every machine can have different version, however updating firmware and watching the first tries closely will likely not cause any problem. Use it at your own risks***
  4.  
  5. PRINTS MUST BE PLACED ON THE RIGHT SIDE OF THE PLATE, AS CLOSE AS POSSIBLE TO THE A1 MINI ARM, AT Y=100MM maximum (world coordinates)
  6.  
  7. Safe: Print a small 5cm tall, 4mm thick cylinder with 2mm outside brim to test first
  8.  
  9. Using https://makerworld.com/fr/collections/6408035-print-automation mount and a custom built .gcode generator I made a system to automate print on my A1 Mini as well as speed up the overall process
  10.  
  11. #1: In BambuStudio:
  12.  
  13. Create a custom printer profile, Click on the little grey pen to edit your printer profile (ONLY TESTED on A1 MINI).
  14.  
  15.  
  16.  
  17.  
  18.  
  19. Add this start gcode
  20.  
  21. Under Machine gcode > Machine start G-code:
  22.  
  23. ;===== machine: A1 mini =========================
  24. ;===== date: 20240620 =====================
  25.  
  26. ;===== start to heat bed&hotend==========
  27. M1002 gcode_claim_action : 2
  28. M1002 set_filament_type:{filament_type[initial_no_support_extruder]}
  29. M104 S170
  30. M140 S[bed_temperature_initial_layer_single]
  31. G392 S0 ;turn off clog detect
  32. M9833.2
  33. ;=====start printer sound ===================
  34. M17
  35. M400 S1
  36. M1006 S1
  37. M1006 A0 B0 L80 C40 D5 M100 E40 F5 N200
  38. M1006 A0 B0 L80 C43 D5 M100 E43 F5 N200
  39. M1006 A0 B0 L80 C47 D5 M100 E47 F5 N200
  40. M1006 A0 B0 L80 C52 D5 M100 E52 F5 N200
  41. M1006 A0 B0 L80 C40 D5 M100 E40 F5 N200
  42. M1006 A0 B0 L80 C43 D5 M100 E43 F5 N200
  43. M1006 A0 B0 L80 C47 D5 M100 E47 F5 N200
  44. M1006 A0 B0 L80 C52 D5 M100 E52 F5 N200
  45. M1006 A0 B0 L80 C40 D5 M100 E40 F5 N200
  46. M1006 A0 B0 L80 C43 D5 M100 E43 F5 N200
  47. M1006 A0 B0 L80 C47 D5 M100 E47 F5 N200
  48. M1006 A0 B0 L80 C52 D5 M100 E52 F5 N200
  49. M1006 W
  50. M18
  51. ;=====avoid end stop =================
  52. G91
  53. G380 S2 Z30 F1200
  54. G380 S3 Z-20 F1200
  55. G1 Z5 F1200
  56. G90
  57.  
  58. ;===== reset machine status =================
  59. M204 S6000
  60.  
  61. M630 S0 P0
  62. G91
  63. M17 Z0.3 ; lower the z-motor current
  64.  
  65. G90
  66. M17 X0.7 Y0.9 Z0.5 ; reset motor current to default
  67. M960 S5 P1 ; turn on logo lamp
  68. G90
  69. M83
  70. M220 S100 ;Reset Feedrate
  71. M221 S100 ;Reset Flowrate
  72. M73.2 R1.0 ;Reset left time magnitude
  73. ;====== cog noise reduction=================
  74. M982.2 S1 ; turn on cog noise reduction
  75.  
  76. ;===== prepare print temperature and material ==========
  77. M400
  78. M18
  79. M109 S100 H170
  80. M104 S170
  81. M400
  82. M17
  83. M400
  84. G28 X
  85.  
  86. M211 X0 Y0 Z0 ;turn off soft endstop ; turn off soft endstop to prevent protential logic problem
  87.  
  88. M975 S1 ; turn on
  89.  
  90. G1 X0.0 F30000
  91. G1 X-13.5 F3000
  92.  
  93. M620 M ;enable remap
  94. M620 S[initial_no_support_extruder]A ; switch material if AMS exist
  95. G392 S0 ;turn on clog detect
  96. M1002 gcode_claim_action : 4
  97. M400
  98. M1002 set_filament_type:UNKNOWN
  99. M109 S[nozzle_temperature_initial_layer]
  100. M104 S250
  101. M400
  102. T[initial_no_support_extruder]
  103. G1 X-13.5 F3000
  104. M400
  105. M620.1 E F{filament_max_volumetric_speed[initial_no_support_extruder]/2.4053*60} T{nozzle_temperature_range_high[initial_no_support_extruder]}
  106. M109 S250 ;set nozzle to common flush temp
  107. M106 P1 S0
  108. G92 E0
  109. G1 E50 F200
  110. M400
  111. M1002 set_filament_type:{filament_type[initial_no_support_extruder]}
  112. M104 S{nozzle_temperature_range_high[initial_no_support_extruder]}
  113. G92 E0
  114. G1 E50 F{filament_max_volumetric_speed[initial_no_support_extruder]/2.4053*60}
  115. M400
  116. M106 P1 S178
  117. G92 E0
  118. G1 E5 F{filament_max_volumetric_speed[initial_no_support_extruder]/2.4053*60}
  119. M109 S{nozzle_temperature_initial_layer[initial_no_support_extruder]-20} ; drop nozzle temp, make filament shink a bit
  120. M104 S{nozzle_temperature_initial_layer[initial_no_support_extruder]-40}
  121. G92 E0
  122. G1 E-0.5 F300
  123.  
  124. G1 X0 F30000
  125. G1 X-13.5 F3000
  126. G1 X0 F30000 ;wipe and shake
  127. G1 X-13.5 F3000
  128. G1 X0 F12000 ;wipe and shake
  129. G1 X0 F30000
  130. G1 X-13.5 F3000
  131. M109 S{nozzle_temperature_initial_layer[initial_no_support_extruder]-40}
  132. G392 S0 ;turn off clog detect
  133. M621 S[initial_no_support_extruder]A
  134.  
  135. M400
  136. M106 P1 S0
  137. ;===== prepare print temperature and material end =====
  138.  
  139.  
  140. ;===== mech mode fast check============================
  141. M1002 gcode_claim_action : 3
  142. G0 X25 Y175 F20000 ; find a soft place to home
  143. ;M104 S0
  144. G28 Z P0 T300; home z with low precision,permit 300deg temperature
  145. G29.2 S0 ; turn off ABL
  146. M104 S170
  147.  
  148. ; build plate detect
  149. M1002 judge_flag build_plate_detect_flag
  150. M622 S1
  151. G39.4
  152. M400
  153. M623
  154.  
  155. G1 Z5 F3000
  156. G1 X90 Y-1 F30000
  157. M400 P200
  158. M970.3 Q1 A7 K0 O2
  159. M974 Q1 S2 P0
  160.  
  161. G1 X90 Y0 Z5 F30000
  162. M400 P200
  163. M970 Q0 A10 B50 C90 H15 K0 M20 O3
  164. M974 Q0 S2 P0
  165.  
  166. M975 S1
  167. G1 F30000
  168. G1 X-1 Y10
  169. G28 X ; re-home XY
  170.  
  171. ;===== wipe nozzle ===============================
  172. M1002 gcode_claim_action : 14
  173. M975 S1
  174.  
  175. M104 S170 ; set temp down to heatbed acceptable
  176. M106 S255 ; turn on fan (G28 has turn off fan)
  177. M211 S; push soft endstop status
  178. M211 X0 Y0 Z0 ;turn off Z axis endstop
  179.  
  180. M83
  181. G1 E-1 F500
  182. G90
  183. M83
  184.  
  185. M109 S170
  186. M104 S140
  187. G0 X90 Y-4 F30000
  188. G380 S3 Z-5 F1200
  189. G1 Z2 F1200
  190. G1 X91 F10000
  191. G380 S3 Z-5 F1200
  192. G1 Z2 F1200
  193. G1 X92 F10000
  194. G380 S3 Z-5 F1200
  195. G1 Z2 F1200
  196. G1 X93 F10000
  197. G380 S3 Z-5 F1200
  198. G1 Z2 F1200
  199. G1 X94 F10000
  200. G380 S3 Z-5 F1200
  201. G1 Z2 F1200
  202. G1 X95 F10000
  203. G380 S3 Z-5 F1200
  204. G1 Z2 F1200
  205. G1 X96 F10000
  206. G380 S3 Z-5 F1200
  207. G1 Z2 F1200
  208. G1 X97 F10000
  209. G380 S3 Z-5 F1200
  210. G1 Z2 F1200
  211. G1 X98 F10000
  212. G380 S3 Z-5 F1200
  213. G1 Z2 F1200
  214. G1 X99 F10000
  215. G380 S3 Z-5 F1200
  216. G1 Z2 F1200
  217. G1 X99 F10000
  218. G380 S3 Z-5 F1200
  219. G1 Z2 F1200
  220. G1 X99 F10000
  221. G380 S3 Z-5 F1200
  222. G1 Z2 F1200
  223. G1 X99 F10000
  224. G380 S3 Z-5 F1200
  225. G1 Z2 F1200
  226. G1 X99 F10000
  227. G380 S3 Z-5 F1200
  228.  
  229. G1 Z5 F30000
  230. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  231. G1 X25 Y175 F30000.1 ;Brush material
  232. G1 Z0.2 F30000.1
  233. G1 Y185
  234. G91
  235. G1 X-30 F30000
  236. G1 Y-2
  237. G1 X27
  238. G1 Y1.5
  239. G1 X-28
  240. G1 Y-2
  241. G1 X30
  242. G1 Y1.5
  243. G1 X-30
  244. G90
  245. M83
  246.  
  247. G1 Z5 F3000
  248. G0 X50 Y175 F20000 ; find a soft place to home
  249. G28 Z P0 T300; home z with low precision, permit 300deg temperature
  250. G29.2 S0 ; turn off ABL
  251.  
  252. G0 X85 Y185 F10000 ;move to exposed steel surface and stop the nozzle
  253. G0 Z-1.01 F10000
  254. G91
  255.  
  256. G2 I1 J0 X2 Y0 F2000.1
  257. G2 I-0.75 J0 X-1.5
  258. G2 I1 J0 X2
  259. G2 I-0.75 J0 X-1.5
  260. G2 I1 J0 X2
  261. G2 I-0.75 J0 X-1.5
  262. G2 I1 J0 X2
  263. G2 I-0.75 J0 X-1.5
  264. G2 I1 J0 X2
  265. G2 I-0.75 J0 X-1.5
  266. G2 I1 J0 X2
  267. G2 I-0.75 J0 X-1.5
  268. G2 I1 J0 X2
  269. G2 I-0.75 J0 X-1.5
  270. G2 I1 J0 X2
  271. G2 I-0.75 J0 X-1.5
  272. G2 I1 J0 X2
  273. G2 I-0.75 J0 X-1.5
  274. G2 I1 J0 X2
  275. G2 I-0.75 J0 X-1.5
  276.  
  277. G90
  278. G1 Z5 F30000
  279. G1 X25 Y175 F30000.1 ;Brush material
  280. G1 Z0.2 F30000.1
  281. G1 Y185
  282. G91
  283. G1 X-30 F30000
  284. G1 Y-2
  285. G1 X27
  286. G1 Y1.5
  287. G1 X-28
  288. G1 Y-2
  289. G1 X30
  290. G1 Y1.5
  291. G1 X-30
  292. G90
  293. M83
  294.  
  295. G1 Z5
  296. G0 X55 Y175 F20000 ; find a soft place to home
  297. G28 Z P0 T300; home z with low precision, permit 300deg temperature
  298. G29.2 S0 ; turn off ABL
  299.  
  300. G1 Z10
  301. G1 X85 Y185
  302. G1 Z-1.01
  303. G1 X95
  304. G1 X90
  305.  
  306. M211 R; pop softend status
  307.  
  308. M106 S0 ; turn off fan , too noisy
  309. ;===== wipe nozzle end ================================
  310.  
  311.  
  312. ;===== wait heatbed ====================
  313. M1002 gcode_claim_action : 2
  314. M104 S0
  315. M190 S[bed_temperature_initial_layer_single];set bed temp
  316. M109 S140
  317.  
  318. G1 Z5 F3000
  319. G29.2 S1
  320. G1 X10 Y10 F20000
  321.  
  322. ;===== bed leveling ==================================
  323. ;M1002 set_flag g29_before_print_flag=1
  324. M1002 judge_flag g29_before_print_flag
  325. M622 J1
  326. M1002 gcode_claim_action : 1
  327. G29 A1 X{first_layer_print_min[0]} Y{first_layer_print_min[1]} I{first_layer_print_size[0]} J{first_layer_print_size[1]}
  328. M400
  329. M500 ; save cali data
  330. M623
  331. ;===== bed leveling end ================================
  332.  
  333. ;===== home after wipe mouth============================
  334. M1002 judge_flag g29_before_print_flag
  335. M622 J0
  336.  
  337. M1002 gcode_claim_action : 13
  338. G28 T145
  339.  
  340. M623
  341.  
  342. ;===== home after wipe mouth end =======================
  343.  
  344. M975 S1 ; turn on vibration supression
  345. ;===== nozzle load line ===============================
  346. M975 S1
  347. G90
  348. M83
  349. T1000
  350.  
  351. G1 X-13.5 Y0 Z10 F10000
  352. G1 E1.2 F500
  353. M400
  354. M1002 set_filament_type:UNKNOWN
  355. M109 S{nozzle_temperature[initial_extruder]}
  356. M400
  357.  
  358. M412 S1 ; ===turn on filament runout detection===
  359. M400 P10
  360.  
  361. G392 S0 ;turn on clog detect
  362.  
  363. M620.3 W1; === turn on filament tangle detection===
  364. M400 S2
  365.  
  366. M1002 set_filament_type:{filament_type[initial_no_support_extruder]}
  367. ;M1002 set_flag extrude_cali_flag=1
  368. M1002 judge_flag extrude_cali_flag
  369. M622 J1
  370. M1002 gcode_claim_action : 8
  371.  
  372. M400
  373. M900 K0.0 L1000.0 M1.0
  374. G90
  375. M83
  376. G0 X68 Y-4 F30000
  377. G0 Z0.3 F18000 ;Move to start position
  378. M400
  379.  
  380. G1 X-13.5 Y0 Z10 F10000
  381. M400
  382.  
  383. G1 E10 F{outer_wall_volumetric_speed/2.4*60}
  384. M983 F{outer_wall_volumetric_speed/2.4} A0.3 H[nozzle_diameter]; cali dynamic extrusion compensation
  385. M106 P1 S178
  386. M400 S7
  387. G1 X0 F18000
  388. G1 X-13.5 F3000
  389. G1 X0 F18000 ;wipe and shake
  390. G1 X-13.5 F3000
  391. G1 X0 F12000 ;wipe and shake
  392. G1 X-13.5 F3000
  393. M400
  394. M106 P1 S0
  395.  
  396. M1002 judge_last_extrude_cali_success
  397. M622 J0
  398. M983 F{outer_wall_volumetric_speed/2.4} A0.3 H[nozzle_diameter]; cali dynamic extrusion compensation
  399. M106 P1 S178
  400. M400 S7
  401. G1 X0 F18000
  402. G1 X-13.5 F3000
  403. G1 X0 F18000 ;wipe and shake
  404. G1 X-13.5 F3000
  405. G1 X0 F12000 ;wipe and shake
  406. M400
  407. M106 P1 S0
  408. M623
  409.  
  410. G1 X-13.5 F3000
  411. M400
  412. M984 A0.1 E1 S1 F{outer_wall_volumetric_speed/2.4} H[nozzle_diameter]
  413. M106 P1 S178
  414. M400 S7
  415. G1 X0 F18000
  416. G1 X-13.5 F3000
  417. G1 X0 F18000 ;wipe and shake
  418. G1 X-13.5 F3000
  419. G1 X0 F12000 ;wipe and shake
  420. G1 X-13.5 F3000
  421. M400
  422. M106 P1 S0
  423.  
  424. M623 ; end of "draw extrinsic para cali paint"
  425.  
  426. ;===== extrude cali test ===============================
  427. M104 S{nozzle_temperature_initial_layer[initial_extruder]}
  428. G90
  429. M83
  430.  
  431. G90
  432. M83
  433. G0 E50 F100
  434. M400
  435. M400
  436.  
  437. ;========turn off light and wait extrude temperature =============
  438. M1002 gcode_claim_action : 0
  439.  
  440. M400 ; wait all motion done before implement the emprical L parameters
  441.  
  442. ;===== for Textured PEI Plate , lower the nozzle as the nozzle was touching topmost of the texture when homing ==
  443. ;curr_bed_type={curr_bed_type}
  444. {if curr_bed_type=="Textured PEI Plate"}
  445. G29.1 Z{-0.02} ; for Textured PEI Plate
  446. {endif}
  447.  
  448. M960 S1 P0 ; turn off laser
  449. M960 S2 P0 ; turn off laser
  450. M106 S0 ; turn off fan
  451. M106 P2 S0 ; turn off big fan
  452. M106 P3 S0 ; turn off chamber fan
  453.  
  454. M975 S1 ; turn on mech mode supression
  455. G90
  456. M83
  457. T1000
  458.  
  459. M211 X0 Y0 Z0 ;turn off soft endstop
  460. M1007 S1
  461. #################### END OF MACHINE START G-CODE ###############
  462.  
  463.  
  464.  
  465.  
  466. 2.Add this end g-code
  467.  
  468.  
  469.  
  470. ;===== date: 20231229 =====================
  471.  
  472. ;turn off nozzle clog detect
  473.  
  474. G392 S0
  475.  
  476. M400 ; wait for buffer to clear
  477.  
  478. G92 E0 ; zero the extruder
  479.  
  480. G1 E-0.8 F1800 ; retract
  481.  
  482. G1 Z{max_layer_z + 0.5} F900 ; lower z a little
  483.  
  484. G1 X0 Y{first_layer_center_no_wipe_tower[1]} F18000 ; move to safe pos
  485.  
  486. G1 X-13.0 F3000 ; move to safe pos
  487.  
  488. {if !spiral_mode && print_sequence != "by object"}
  489.  
  490. M1002 judge_flag timelapse_record_flag
  491.  
  492. M622 J1
  493.  
  494. M400 P100
  495.  
  496. M971 S11 C11 O0
  497.  
  498. M400 P100
  499.  
  500. M971 S11 C11 O0
  501.  
  502. M400 P100
  503.  
  504. M971 S11 C11 O0
  505.  
  506. M400 P100
  507.  
  508. M971 S11 C11 O0
  509.  
  510. M400 P100
  511.  
  512. M971 S11 C11 O0
  513.  
  514. M400 P100
  515.  
  516. M971 S11 C11 O0
  517.  
  518. M400 P100
  519.  
  520. M971 S11 C11 O0
  521.  
  522. M400 P100
  523.  
  524. M971 S11 C11 O0
  525.  
  526. M400 P100
  527.  
  528. M971 S11 C11 O0
  529.  
  530. M400 P100
  531.  
  532. M971 S11 C11 O0
  533.  
  534. M400 P100
  535.  
  536. M971 S11 C11 O0
  537.  
  538. M400 P100
  539.  
  540. M971 S11 C11 O0
  541.  
  542. M400 P100
  543.  
  544. M971 S11 C11 O0
  545.  
  546. M400 P100
  547.  
  548. M971 S11 C11 O0
  549.  
  550. M400 P100
  551.  
  552. M971 S11 C11 O0
  553.  
  554. M400 P100
  555.  
  556. M971 S11 C11 O0
  557.  
  558. M400 P100
  559.  
  560. M971 S11 C11 O0
  561.  
  562. M400 P100
  563.  
  564. M971 S11 C11 O0
  565.  
  566. M400 P100
  567.  
  568. M971 S11 C11 O0
  569.  
  570. M400 P100
  571.  
  572. M971 S11 C11 O0
  573.  
  574. M400 P100
  575.  
  576. M971 S11 C11 O0
  577.  
  578. M400 P100
  579.  
  580. M971 S11 C11 O0
  581.  
  582. M400 P100
  583.  
  584. M971 S11 C11 O0
  585.  
  586. M400 P100
  587.  
  588. M971 S11 C11 O0
  589.  
  590. M400 P100
  591.  
  592. M971 S11 C11 O0
  593.  
  594. M400 P100
  595.  
  596. M971 S11 C11 O0
  597.  
  598. M400 P100
  599.  
  600. M971 S11 C11 O0
  601.  
  602. M400 P100
  603.  
  604. M971 S11 C11 O0
  605.  
  606. M400 P100
  607.  
  608. M971 S11 C11 O0
  609.  
  610. M400 P100
  611.  
  612. M971 S11 C11 O0
  613.  
  614. M991 S0 P-1 ;end timelapse at safe pos
  615.  
  616. M623
  617.  
  618. {endif}
  619.  
  620. M140 S0 ; turn off bed
  621.  
  622. M106 S0 ; turn off fan
  623.  
  624. M106 P2 S0 ; turn off remote part cooling fan
  625.  
  626. M106 P3 S0 ; turn off chamber cooling fan
  627.  
  628. ;G1 X27 F15000 ; wipe
  629.  
  630. ; pull back filament to AMS
  631.  
  632. M620 S255
  633.  
  634. G1 X181 F12000
  635.  
  636. T255
  637.  
  638. G1 X0 F18000
  639.  
  640. G1 X-13.0 F3000
  641.  
  642. G1 X0 F18000 ; wipe
  643.  
  644. M621 S255
  645.  
  646. M104 S0 ; turn off hotend
  647.  
  648. M400 ; wait all motion done
  649.  
  650. M17 S
  651.  
  652. M17 Z0.4 ; lower z motor current to reduce impact if there is something in the bottom
  653.  
  654. {if (max_layer_z + 100.0) < 180}
  655.  
  656. G1 Z{max_layer_z + 100.0} F600
  657.  
  658. G1 Z{max_layer_z +98.0}
  659.  
  660. {else}
  661.  
  662. G1 Z180 F600
  663.  
  664. G1 Z180
  665.  
  666. {endif}
  667.  
  668. M400 P100
  669.  
  670. M17 R ; restore z current
  671.  
  672. G90
  673.  
  674. G1 X-13 Y180 F3600
  675.  
  676. G91
  677.  
  678. G1 Z-1 F600
  679.  
  680. G90
  681.  
  682. M83
  683.  
  684. M220 S100 ; Reset feedrate magnitude
  685.  
  686. M201.2 K1.0 ; Reset acc magnitude
  687.  
  688. M73.2 R1.0 ;Reset left time magnitude
  689.  
  690. M1002 set_gcode_claim_speed_level : 0
  691.  
  692. ;=====printer finish sound=========
  693.  
  694. M17
  695.  
  696. M400 S1
  697.  
  698. M1006 S1
  699.  
  700. M1006 A0 B20 L100 C37 D20 M100 E42 F20 N100
  701.  
  702. M1006 A0 B10 L100 C44 D10 M100 E44 F10 N100
  703.  
  704. M1006 A0 B10 L100 C46 D10 M100 E46 F10 N100
  705.  
  706. M1006 A44 B20 L100 C39 D20 M100 E48 F20 N100
  707.  
  708. M1006 A0 B10 L100 C44 D10 M100 E44 F10 N100
  709.  
  710. M1006 A0 B10 L100 C0 D10 M100 E0 F10 N100
  711.  
  712. M1006 A0 B10 L100 C39 D10 M100 E39 F10 N100
  713.  
  714. M1006 A0 B10 L100 C0 D10 M100 E0 F10 N100
  715.  
  716. M1006 A0 B10 L100 C44 D10 M100 E44 F10 N100
  717.  
  718. M1006 A0 B10 L100 C0 D10 M100 E0 F10 N100
  719.  
  720. M1006 A0 B10 L100 C39 D10 M100 E39 F10 N100
  721.  
  722. M1006 A0 B10 L100 C0 D10 M100 E0 F10 N100
  723.  
  724. M1006 A44 B10 L100 C0 D10 M100 E48 F10 N100
  725.  
  726. M1006 A0 B10 L100 C0 D10 M100 E0 F10 N100
  727.  
  728. M1006 A44 B20 L100 C41 D20 M100 E49 F20 N100
  729.  
  730. M1006 A0 B20 L100 C0 D20 M100 E0 F20 N100
  731.  
  732. M1006 A0 B20 L100 C37 D20 M100 E37 F20 N100
  733.  
  734. M1006 W
  735.  
  736. ;=====printer finish sound=========
  737.  
  738. M400 S1
  739.  
  740. M18 X Y Z
  741.  
  742. ###########END OF MACHINE END G-CODE #######
  743.  
  744.  
  745.  
  746.  
  747.  
  748. Save your profile as a easy to remember name: such as: A1 MINI - FARMLOOP - v0beta
  749.  
  750.  
  751.  
  752. Create a small cylinder on a empty plate, right click add primitive, cylinder, scale (top right) deactivate uniform scale, 5mm Z, 2mm X, 2mm Y, print setting > Others > add outer brim 2mm width
  753.  
  754.  
  755.  
  756. Slice plate
  757.  
  758.  
  759.  
  760. Under Slice plate button (drop down menu), select "export plate sliced file"
  761.  
  762. #2 Use the website mini-tool
  763.  
  764.  
  765.  
  766.  
  767.  
  768.  
  769.  
  770. Create a file a1minihack-minitool.html
  771.  
  772.  
  773.  
  774. paste>>
  775.  
  776. <!DOCTYPE html>
  777.  
  778. <html lang="en">
  779.  
  780. <head>
  781.  
  782. <meta charset="UTF-8">
  783.  
  784. <meta name="viewport" content="width=device-width, initial-scale=1.0">
  785.  
  786. <title>G-code Farm Loop Modifier (.3mf Support)</title>
  787.  
  788. <style>
  789.  
  790. * { margin: 0; padding: 0; box-sizing: border-box; }
  791.  
  792. body { font-family: Arial, sans-serif; background: #f5f5f5; padding: 20px; }
  793.  
  794. .container { max-width: 1000px; margin: 0 auto; background: white; padding: 20px; border-radius: 8px; box-shadow: 0 2px 8px rgba(0,0,0,0.1); }
  795.  
  796. h1 { color: #333; margin-bottom: 20px; text-align: center; }
  797.  
  798. .section { margin-bottom: 30px; padding: 15px; border: 1px solid #ddd; border-radius: 5px; }
  799.  
  800. .section h2 { color: #555; margin-bottom: 15px; font-size: 1.2em; }
  801.  
  802. .grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); gap: 15px; }
  803.  
  804. .param-group { display: flex; flex-direction: column; }
  805.  
  806. .param-group label { margin-bottom: 5px; font-weight: bold; color: #666; }
  807.  
  808. .param-group input, .param-group select { padding: 8px; border: 1px solid #ccc; border-radius: 4px; }
  809.  
  810. .btn { padding: 10px 20px; color: white; border: none; border-radius: 4px; cursor: pointer; margin-right: 10px; margin-bottom: 10px; }
  811.  
  812. .btn:disabled { background: #ccc; cursor: not-allowed; }
  813.  
  814. .btn-primary { background: #007bff; }
  815.  
  816. .btn-primary:hover:not(:disabled) { background: #0056b3; }
  817.  
  818. .btn-success { background: #28a745; }
  819.  
  820. .btn-success:hover:not(:disabled) { background: #218838; }
  821.  
  822. .btn-info { background: #17a2b8; }
  823.  
  824. .btn-info:hover:not(:disabled) { background: #138496; }
  825.  
  826. .file-input { padding: 10px; border: 2px dashed #ccc; border-radius: 4px; text-align: center; cursor: pointer; }
  827.  
  828. .file-input:hover { border-color: #007bff; }
  829.  
  830. .file-info { background: #e8f5e8; padding: 10px; border-radius: 4px; margin-top: 10px; }
  831.  
  832. .status { padding: 10px; border-radius: 4px; margin-bottom: 15px; }
  833.  
  834. .status.success { background: #d4edda; color: #155724; }
  835.  
  836. .status.error { background: #f8d7da; color: #721c24; }
  837.  
  838. .status.info { background: #cce7ff; color: #004085; }
  839.  
  840. .status.warning { background: #fff3cd; color: #856404; }
  841.  
  842. .preview { background: #f8f9fa; padding: 15px; border-radius: 4px; max-height: 400px; overflow-y: auto; }
  843.  
  844. .preview pre { margin: 0; white-space: pre-wrap; font-family: monospace; font-size: 0.9em; }
  845.  
  846. .hidden { display: none; }
  847.  
  848. .file-type-badge { display: inline-block; padding: 3px 8px; color: white; border-radius: 12px; font-size: 0.8em; margin-left: 10px; }
  849.  
  850. .file-type-badge.threemf { background: #ff6b35; }
  851.  
  852. .file-type-badge.gcode { background: #28a745; }
  853.  
  854. .progress { width: 100%; height: 20px; background: #e9ecef; border-radius: 10px; overflow: hidden; margin: 10px 0; }
  855.  
  856. .progress-bar { height: 100%; background: #007bff; transition: width 0.3s ease; }
  857.  
  858. </style>
  859.  
  860. </head>
  861.  
  862. <body>
  863.  
  864. <div class="container">
  865.  
  866. <h1>G-code Farm Loop Modifier (.3mf Support)</h1>
  867.  
  868.  
  869.  
  870. <div class="section">
  871.  
  872. <h2>Load File</h2>
  873.  
  874. <div class="file-input" onclick="document.getElementById('fileInput').click()">
  875.  
  876. <input type="file" id="fileInput" accept=".gcode,.g,.txt,.3mf" style="display: none;">
  877.  
  878. Click to select G-code file (.gcode, .g, .txt) or 3MF file (.3mf)
  879.  
  880. </div>
  881.  
  882. <div id="fileInfo" class="file-info hidden"></div>
  883.  
  884. <div id="progressContainer" class="hidden">
  885.  
  886. <div class="progress">
  887.  
  888. <div id="progressBar" class="progress-bar" style="width: 0%"></div>
  889.  
  890. </div>
  891.  
  892. <div id="progressText">Processing...</div>
  893.  
  894. </div>
  895.  
  896. </div>
  897.  
  898.  
  899.  
  900. <div class="section">
  901.  
  902. <h2>Parameters</h2>
  903.  
  904. <div class="grid">
  905.  
  906. <div class="param-group">
  907.  
  908. <label for="loopCount">Number of Loops</label>
  909.  
  910. <input type="number" id="loopCount" min="1" max="50" value="3">
  911.  
  912. </div>
  913.  
  914. <div class="param-group">
  915.  
  916. <label for="cooldownTemp">Cooldown Temperature (°C)</label>
  917.  
  918. <input type="number" id="cooldownTemp" min="20" max="200" value="25">
  919.  
  920. </div>
  921.  
  922. <div class="param-group">
  923.  
  924. <label for="waitTime">Wait Time (seconds)</label>
  925.  
  926. <input type="number" id="waitTime" min="0" max="3600" value="30">
  927.  
  928. </div>
  929.  
  930. <div class="param-group">
  931.  
  932. <label for="wipeEnabled">Enable Wipe Sequence</label>
  933.  
  934. <select id="wipeEnabled" disabled>
  935.  
  936. <option value="true" selected>Yes (Always Enabled)</option>
  937.  
  938. </select>
  939.  
  940. </div>
  941.  
  942. </div>
  943.  
  944. </div>
  945.  
  946.  
  947.  
  948. <div class="section">
  949.  
  950. <h2>Process</h2>
  951.  
  952. <div id="status" class="status hidden"></div>
  953.  
  954. <button id="processBtn" class="btn btn-primary" disabled>Process G-code</button>
  955.  
  956. <button id="previewBtn" class="btn btn-primary" disabled>Preview Added Blocks</button>
  957.  
  958. <div id="downloadSection" class="hidden">
  959.  
  960. <button id="downloadGcodeBtn" class="btn btn-success">Download G-code</button>
  961.  
  962. <button id="download3mfBtn" class="btn btn-info hidden">Download 3MF</button>
  963.  
  964. </div>
  965.  
  966. </div>
  967.  
  968.  
  969.  
  970. <div id="previewSection" class="section hidden">
  971.  
  972. <h2>Preview - New G-code Blocks Added (First Loop)</h2>
  973.  
  974. <div class="preview">
  975.  
  976. <pre id="previewContent"></pre>
  977.  
  978. </div>
  979.  
  980. </div>
  981.  
  982. </div>
  983.  
  984. <script src="https://cdnjs.cloudflare.com/ajax/libs/jszip/3.10.1/jszip.min.js"></script>
  985.  
  986. <script>
  987.  
  988. class GCodeFarmModifier {
  989.  
  990. constructor() {
  991.  
  992. this.originalGCode = '';
  993.  
  994. this.modifiedGCode = '';
  995.  
  996. this.fileName = '';
  997.  
  998. this.fileType = '';
  999.  
  1000. this.addedBlocks = '';
  1001.  
  1002. this.threemfZip = null;
  1003.  
  1004. this.gcodeFilePath = null;
  1005.  
  1006. this.init();
  1007.  
  1008. }
  1009.  
  1010.  
  1011.  
  1012. init() {
  1013.  
  1014. const $ = id => document.getElementById(id);
  1015.  
  1016. $('fileInput').addEventListener('change', e => this.loadFile(e));
  1017.  
  1018. $('processBtn').addEventListener('click', () => this.process());
  1019.  
  1020. $('downloadGcodeBtn').addEventListener('click', () => this.downloadGcode());
  1021.  
  1022. $('download3mfBtn').addEventListener('click', () => this.download3mf());
  1023.  
  1024. $('previewBtn').addEventListener('click', () => this.preview());
  1025.  
  1026. }
  1027.  
  1028.  
  1029.  
  1030. updateProgress(percent, text) {
  1031.  
  1032. const container = document.getElementById('progressContainer');
  1033.  
  1034. const bar = document.getElementById('progressBar');
  1035.  
  1036. const textEl = document.getElementById('progressText');
  1037.  
  1038.  
  1039.  
  1040. container.classList.toggle('hidden', false);
  1041.  
  1042. bar.style.width = percent + '%';
  1043.  
  1044. textEl.textContent = text;
  1045.  
  1046.  
  1047.  
  1048. if (percent >= 100) setTimeout(() => container.classList.add('hidden'), 2000);
  1049.  
  1050. }
  1051.  
  1052.  
  1053.  
  1054. async loadFile(event) {
  1055.  
  1056. const file = event.target.files[0];
  1057.  
  1058. if (!file) return;
  1059.  
  1060.  
  1061.  
  1062. this.fileName = file.name;
  1063.  
  1064. this.fileType = file.name.toLowerCase().endsWith('.3mf') ? '3mf' : 'gcode';
  1065.  
  1066. this.reset();
  1067.  
  1068.  
  1069.  
  1070. try {
  1071.  
  1072. this.fileType === '3mf' ? await this.load3mf(file) : await this.loadGcode(file);
  1073.  
  1074. } catch (error) {
  1075.  
  1076. this.showStatus(`Error loading file: ${error.message}`, 'error');
  1077.  
  1078. }
  1079.  
  1080. }
  1081.  
  1082.  
  1083.  
  1084. reset() {
  1085.  
  1086. this.originalGCode = '';
  1087.  
  1088. this.modifiedGCode = '';
  1089.  
  1090. this.addedBlocks = '';
  1091.  
  1092. this.threemfZip = null;
  1093.  
  1094. this.gcodeFilePath = null;
  1095.  
  1096. }
  1097.  
  1098.  
  1099.  
  1100. async loadGcode(file) {
  1101.  
  1102. this.updateProgress(30, 'Reading G-code file...');
  1103.  
  1104.  
  1105.  
  1106. const text = await new Promise((resolve, reject) => {
  1107.  
  1108. const reader = new FileReader();
  1109.  
  1110. reader.onload = e => resolve(e.target.result);
  1111.  
  1112. reader.onerror = () => reject(new Error('Failed to read file'));
  1113.  
  1114. reader.readAsText(file);
  1115.  
  1116. });
  1117.  
  1118.  
  1119.  
  1120. if (!text?.trim()) throw new Error('G-code file is empty');
  1121.  
  1122.  
  1123.  
  1124. this.originalGCode = text;
  1125.  
  1126. this.updateProgress(100, 'G-code loaded successfully!');
  1127.  
  1128. this.showFileInfo(file);
  1129.  
  1130. this.updateButtons();
  1131.  
  1132. }
  1133.  
  1134.  
  1135.  
  1136. async load3mf(file) {
  1137.  
  1138. this.updateProgress(30, 'Parsing 3MF structure...');
  1139.  
  1140.  
  1141.  
  1142. const buffer = await new Promise((resolve, reject) => {
  1143.  
  1144. const reader = new FileReader();
  1145.  
  1146. reader.onload = e => resolve(e.target.result);
  1147.  
  1148. reader.onerror = () => reject(new Error('Failed to read file'));
  1149.  
  1150. reader.readAsArrayBuffer(file);
  1151.  
  1152. });
  1153.  
  1154.  
  1155.  
  1156. const zip = new JSZip();
  1157.  
  1158. this.threemfZip = await zip.loadAsync(buffer);
  1159.  
  1160.  
  1161.  
  1162. this.updateProgress(70, 'Extracting G-code...');
  1163.  
  1164. const gcodeResult = await this.extractGcodeFrom3mf(this.threemfZip);
  1165.  
  1166.  
  1167.  
  1168. if (!gcodeResult?.content?.trim()) throw new Error('No G-code found in 3MF file');
  1169.  
  1170.  
  1171.  
  1172. this.originalGCode = gcodeResult.content;
  1173.  
  1174. this.gcodeFilePath = gcodeResult.path;
  1175.  
  1176.  
  1177.  
  1178. this.updateProgress(100, 'Complete!');
  1179.  
  1180. this.showFileInfo(file);
  1181.  
  1182. this.updateButtons();
  1183.  
  1184. }
  1185.  
  1186.  
  1187.  
  1188. async extractGcodeFrom3mf(zip) {
  1189.  
  1190. const paths = ['Metadata/plate_1.gcode', 'Metadata/plate_2.gcode', 'Metadata/plate_3.gcode',
  1191.  
  1192. 'Metadata/plate_4.gcode', 'Metadata/plate_5.gcode', 'Metadata/print.gcode'];
  1193.  
  1194.  
  1195.  
  1196. for (const path of paths) {
  1197.  
  1198. const file = zip.file(path);
  1199.  
  1200. if (file) {
  1201.  
  1202. try {
  1203.  
  1204. const content = await file.async('text');
  1205.  
  1206. if (this.isGcode(content)) return { content, path };
  1207.  
  1208. } catch (e) { continue; }
  1209.  
  1210. }
  1211.  
  1212. }
  1213.  
  1214.  
  1215.  
  1216. // Search all files
  1217.  
  1218. const allFiles = [];
  1219.  
  1220. zip.forEach((path, file) => !file.dir && allFiles.push(path));
  1221.  
  1222.  
  1223.  
  1224. for (const path of allFiles.sort((a, b) => (a.includes('.gcode') ? -1 : 1))) {
  1225.  
  1226. const file = zip.file(path);
  1227.  
  1228. if (file) {
  1229.  
  1230. try {
  1231.  
  1232. const content = await file.async('text');
  1233.  
  1234. if (this.isGcode(content)) return { content, path };
  1235.  
  1236. } catch (e) { continue; }
  1237.  
  1238. }
  1239.  
  1240. }
  1241.  
  1242.  
  1243.  
  1244. return null;
  1245.  
  1246. }
  1247.  
  1248.  
  1249.  
  1250. isGcode(content) {
  1251.  
  1252. if (!content || content.length < 50) return false;
  1253.  
  1254. const patterns = [/^G[0-9]+/m, /^M[0-9]+/m, /^T[0-9]+/m, /; generated by/i, /G28/, /G1.*E/];
  1255.  
  1256. return patterns.filter(p => p.test(content)).length >= 3;
  1257.  
  1258. }
  1259.  
  1260.  
  1261.  
  1262. showFileInfo(file) {
  1263.  
  1264. const info = document.getElementById('fileInfo');
  1265.  
  1266. const badge = this.fileType === '3mf' ?
  1267.  
  1268. '<span class="file-type-badge threemf">3MF</span>' :
  1269.  
  1270. '<span class="file-type-badge gcode">G-code</span>';
  1271.  
  1272. const lines = this.originalGCode.split('\n').length;
  1273.  
  1274. const size = (file.size / 1024).toFixed(1);
  1275.  
  1276.  
  1277.  
  1278. info.innerHTML = `<strong>File:</strong> ${file.name} ${badge}<br>
  1279.  
  1280. <strong>Size:</strong> ${size} KB<br>
  1281.  
  1282. <strong>G-code Lines:</strong> ${lines}`;
  1283.  
  1284. info.classList.remove('hidden');
  1285.  
  1286. }
  1287.  
  1288.  
  1289.  
  1290. updateButtons() {
  1291.  
  1292. const hasFile = !!this.originalGCode;
  1293.  
  1294. const hasModified = !!this.modifiedGCode;
  1295.  
  1296.  
  1297.  
  1298. document.getElementById('processBtn').disabled = !hasFile;
  1299.  
  1300. document.getElementById('previewBtn').disabled = !hasFile;
  1301.  
  1302. document.getElementById('downloadSection').classList.toggle('hidden', !hasFile);
  1303.  
  1304. document.getElementById('downloadGcodeBtn').disabled = !hasModified;
  1305.  
  1306. document.getElementById('download3mfBtn').disabled = !hasModified;
  1307.  
  1308. document.getElementById('download3mfBtn').classList.toggle('hidden', this.fileType !== '3mf');
  1309.  
  1310. }
  1311.  
  1312.  
  1313.  
  1314. showStatus(message, type = 'info') {
  1315.  
  1316. const status = document.getElementById('status');
  1317.  
  1318. status.textContent = message;
  1319.  
  1320. status.className = status ${type};
  1321.  
  1322. status.classList.remove('hidden');
  1323.  
  1324. }
  1325.  
  1326.  
  1327.  
  1328. process() {
  1329.  
  1330. if (!this.originalGCode) return this.showStatus('Please load a file first.', 'error');
  1331.  
  1332.  
  1333.  
  1334. try {
  1335.  
  1336. const params = {
  1337.  
  1338. loopCount: parseInt(document.getElementById('loopCount').value),
  1339.  
  1340. cooldownTemp: parseInt(document.getElementById('cooldownTemp').value),
  1341.  
  1342. waitTime: parseInt(document.getElementById('waitTime').value)
  1343.  
  1344. };
  1345.  
  1346.  
  1347.  
  1348. this.showStatus('Processing G-code...', 'info');
  1349.  
  1350.  
  1351.  
  1352. const { header, printCore, footer } = this.parseGCode();
  1353.  
  1354. this.modifiedGCode = this.generateLoops(header, printCore, footer, params);
  1355.  
  1356. this.generatePreview(params);
  1357.  
  1358.  
  1359.  
  1360. this.showStatus(`Successfully processed G-code with ${params.loopCount} loops!`, 'success');
  1361.  
  1362. this.updateButtons();
  1363.  
  1364. } catch (error) {
  1365.  
  1366. this.showStatus(`Error: ${error.message}`, 'error');
  1367.  
  1368. }
  1369.  
  1370. }
  1371.  
  1372.  
  1373.  
  1374. parseGCode() {
  1375.  
  1376. const lines = this.originalGCode.split('\n');
  1377.  
  1378. let headerEnd = lines.findIndex(line => {
  1379.  
  1380. const l = line.trim();
  1381.  
  1382. return (l.startsWith('G1') && l.includes('E')) || l.includes('LAYER_CHANGE') ||
  1383.  
  1384. l.includes('layer') || (l.includes('Z0.') && l.startsWith('G1'));
  1385.  
  1386. });
  1387.  
  1388.  
  1389.  
  1390. let footerStart = -1;
  1391.  
  1392. for (let i = lines.length - 1; i >= 0; i--) {
  1393.  
  1394. const l = lines[i].trim();
  1395.  
  1396. if (l.includes('M400') || l.includes('M104 S0') || l.includes('M140 S0') ||
  1397.  
  1398. l.includes('M84') || (l.startsWith('G1') && l.includes('Z'))) {
  1399.  
  1400. footerStart = i;
  1401.  
  1402. break;
  1403.  
  1404. }
  1405.  
  1406. }
  1407.  
  1408.  
  1409.  
  1410. if (headerEnd === -1) headerEnd = Math.min(50, Math.floor(lines.length * 0.1));
  1411.  
  1412. if (footerStart === -1) footerStart = Math.max(lines.length - 20, Math.floor(lines.length * 0.9));
  1413.  
  1414.  
  1415.  
  1416. return {
  1417.  
  1418. header: lines.slice(0, headerEnd).join('\n'),
  1419.  
  1420. printCore: lines.slice(headerEnd, footerStart).join('\n'),
  1421.  
  1422. footer: lines.slice(footerStart).join('\n')
  1423.  
  1424. };
  1425.  
  1426. }
  1427.  
  1428.  
  1429.  
  1430. generateLoops(header, printCore, footer, params) {
  1431.  
  1432. let result = '';
  1433.  
  1434. const { loopCount, cooldownTemp, waitTime } = params;
  1435.  
  1436.  
  1437.  
  1438. for (let i = 1; i <= loopCount; i++) {
  1439.  
  1440. result += \n; === LOOP ${i} OF ${loopCount} ===\n;
  1441.  
  1442.  
  1443.  
  1444. if (header?.trim()) {
  1445.  
  1446. result += (i === 1 ? this.updateTimeEstimates(header, params) : header);
  1447.  
  1448. if (!header.endsWith('\n')) result += '\n';
  1449.  
  1450. }
  1451.  
  1452.  
  1453.  
  1454. if (printCore?.trim()) {
  1455.  
  1456. result += printCore;
  1457.  
  1458. if (!printCore.endsWith('\n')) result += '\n';
  1459.  
  1460. }
  1461.  
  1462.  
  1463.  
  1464. result += '\n' + this.getWipeSequence(cooldownTemp) + '\n';
  1465.  
  1466.  
  1467.  
  1468. if (i < loopCount) {
  1469.  
  1470. result += \n; === SETUP FOR NEXT LOOP ===\n;
  1471.  
  1472. result += M104 S150 ; Set hotend temperature\n;
  1473.  
  1474. result += M140 S${cooldownTemp} ; Set bed temperature\n;
  1475.  
  1476. result += M109 S150 ; Wait for hotend temperature\n;
  1477.  
  1478. result += M190 S${cooldownTemp} ; Wait for bed temperature\n;
  1479.  
  1480. result += G4 S${waitTime} ; Wait between loops\n;
  1481.  
  1482. }
  1483.  
  1484. }
  1485.  
  1486.  
  1487.  
  1488. if (footer?.trim()) {
  1489.  
  1490. result += '\n' + footer;
  1491.  
  1492. if (!footer.endsWith('\n')) result += '\n';
  1493.  
  1494. }
  1495.  
  1496.  
  1497.  
  1498. return result;
  1499.  
  1500. }
  1501.  
  1502.  
  1503.  
  1504. generatePreview(params) {
  1505.  
  1506. const { loopCount, cooldownTemp, waitTime } = params;
  1507.  
  1508. const bedCooldownTime = this.calculateBedCooldownTime(cooldownTemp);
  1509.  
  1510. const formatTime = s => ${Math.floor(s/60)}m ${s%60}s;
  1511.  
  1512.  
  1513.  
  1514. this.addedBlocks = `; === LOOP 1 OF ${loopCount} ===
  1515.  
  1516. ; HEADER_BLOCK_START
  1517.  
  1518. ; BambuStudio [version]
  1519.  
  1520. ; model printing time: [original_time × ${loopCount}]; total estimated time: [includes cooldown & wipe]
  1521.  
  1522. ; [Original G-code header and print core would be here]
  1523.  
  1524. ; === COOLDOWN & WIPE SEQUENCE ===
  1525.  
  1526. ; Bed cooldown time (65°C → ${cooldownTemp}°C): ${formatTime(bedCooldownTime)}
  1527.  
  1528. ; Wipe sequence time: 1m 10s (2x speed)
  1529.  
  1530. ${this.getWipeSequence(cooldownTemp)}
  1531.  
  1532. ${loopCount > 1 ? `; === SETUP FOR NEXT LOOP ===
  1533.  
  1534. ; Wait time between loops: ${formatTime(waitTime)}
  1535.  
  1536. M104 S150 ; Set hotend temperature
  1537.  
  1538. M140 S${cooldownTemp} ; Set bed temperature
  1539.  
  1540. M109 S150 ; Wait for hotend temperature
  1541.  
  1542. M190 S${cooldownTemp} ; Wait for bed temperature
  1543.  
  1544. G4 S${waitTime} ; Wait between loops
  1545.  
  1546. ` : ''}; === TIME BREAKDOWN FOR ${loopCount} LOOPS ===
  1547.  
  1548. ; Total bed cooldown time: ${formatTime(bedCooldownTime * loopCount)}
  1549.  
  1550. ; Total wipe time: ${formatTime(70 * loopCount)}
  1551.  
  1552. ; Total wait time between loops: ${formatTime(waitTime * (loopCount - 1))}
  1553.  
  1554. ; Print time multiplied by ${loopCount} loops`;
  1555.  
  1556. }
  1557.  
  1558.  
  1559.  
  1560. updateTimeEstimates(header, params) {
  1561.  
  1562. const { loopCount, cooldownTemp, waitTime } = params;
  1563.  
  1564. const lines = header.split('\n');
  1565.  
  1566. let foundBambuStudio = false;
  1567.  
  1568.  
  1569.  
  1570. return lines.map((line, index) => {
  1571.  
  1572. // Check if this line is HEADER_BLOCK_START
  1573.  
  1574. if (line.trim() === '; HEADER_BLOCK_START') {
  1575.  
  1576. foundBambuStudio = false;
  1577.  
  1578. return line;
  1579.  
  1580. }
  1581.  
  1582.  
  1583.  
  1584. // Check if this line is BambuStudio version line
  1585.  
  1586. if (line.trim().match(/^;\s*BambuStudio\s+[\d.]+/)) {
  1587.  
  1588. foundBambuStudio = true;
  1589.  
  1590. return line;
  1591.  
  1592. }
  1593.  
  1594.  
  1595.  
  1596. // Check if this is the time estimation line after BambuStudio
  1597.  
  1598. if (foundBambuStudio && line.includes('model printing time:') && line.includes('total estimated time:')) {
  1599.  
  1600. const bambuTimeMatch = line.match(/^(;\s*model printing time:\s*)([^;]+)(;\s*total estimated time:\s*)([^;]*)(.*?)$/);
  1601.  
  1602. if (bambuTimeMatch) {
  1603.  
  1604. const [, prefix, modelTime, middle, totalTime, suffix] = bambuTimeMatch;
  1605.  
  1606. const originalSeconds = this.parseTime(modelTime.trim());
  1607.  
  1608. const additionalTime = (180 + this.calculateBedCooldownTime(cooldownTemp) + 70) loopCount + waitTime (loopCount - 1);
  1609.  
  1610. const newModelTime = originalSeconds * loopCount;
  1611.  
  1612. const newTotalTime = newModelTime + additionalTime;
  1613.  
  1614.  
  1615.  
  1616. foundBambuStudio = false; // Reset for next occurrence
  1617.  
  1618. return ${prefix}${this.formatTime(newModelTime)}${middle}${this.formatTime(newTotalTime)}${suffix};
  1619.  
  1620. }
  1621.  
  1622. }
  1623.  
  1624.  
  1625.  
  1626. // Fallback for other time estimation formats
  1627.  
  1628. const timeMatch = line.match(/^(;\s*estimated printing time.*?:?\s*)(\d+[hmsd\s]+)(.*)$/i);
  1629.  
  1630. if (timeMatch) {
  1631.  
  1632. const [, prefix, timeStr, suffix] = timeMatch;
  1633.  
  1634. const originalSeconds = this.parseTime(timeStr);
  1635.  
  1636. const additionalTime = (180 + this.calculateBedCooldownTime(cooldownTemp) + 70) loopCount + waitTime (loopCount - 1);
  1637.  
  1638.  
  1639.  
  1640. return ${prefix}${this.formatTime(originalSeconds * loopCount + additionalTime)}${suffix};
  1641.  
  1642. }
  1643.  
  1644.  
  1645.  
  1646. return line;
  1647.  
  1648. }).join('\n');
  1649.  
  1650. }
  1651.  
  1652.  
  1653.  
  1654. calculateBedCooldownTime(cooldownTemp) {
  1655.  
  1656. let totalTime = 0;
  1657.  
  1658. let currentTemp = 65;
  1659.  
  1660.  
  1661.  
  1662. while (currentTemp > cooldownTemp) {
  1663.  
  1664. totalTime += currentTemp > 50 ? 12 : currentTemp > 40 ? 18 : 30;
  1665.  
  1666. currentTemp--;
  1667.  
  1668. }
  1669.  
  1670.  
  1671.  
  1672. // Add extra time for temperature stability and verification
  1673.  
  1674. totalTime += 90; // 30s + 60s additional cooling time
  1675.  
  1676.  
  1677.  
  1678. return totalTime;
  1679.  
  1680. }
  1681.  
  1682.  
  1683.  
  1684. parseTime(timeStr) {
  1685.  
  1686. const matches = {
  1687.  
  1688. d: timeStr.match(/(\d+)d/),
  1689.  
  1690. h: timeStr.match(/(\d+)h/),
  1691.  
  1692. m: timeStr.match(/(\d+)m/),
  1693.  
  1694. s: timeStr.match(/(\d+)s/)
  1695.  
  1696. };
  1697.  
  1698.  
  1699.  
  1700. return (matches.d ? parseInt(matches.d[1]) * 86400 : 0) +
  1701.  
  1702. (matches.h ? parseInt(matches.h[1]) * 3600 : 0) +
  1703.  
  1704. (matches.m ? parseInt(matches.m[1]) * 60 : 0) +
  1705.  
  1706. (matches.s ? parseInt(matches.s[1]) : 0);
  1707.  
  1708. }
  1709.  
  1710.  
  1711.  
  1712. formatTime(seconds) {
  1713.  
  1714. const units = [
  1715.  
  1716. [86400, 'd'],
  1717.  
  1718. [3600, 'h'],
  1719.  
  1720. [60, 'm'],
  1721.  
  1722. [1, 's']
  1723.  
  1724. ];
  1725.  
  1726.  
  1727.  
  1728. let result = '';
  1729.  
  1730. for (const [divisor, unit] of units) {
  1731.  
  1732. const count = Math.floor(seconds / divisor);
  1733.  
  1734. if (count > 0) {
  1735.  
  1736. result += ${count}${unit} ;
  1737.  
  1738. seconds %= divisor;
  1739.  
  1740. }
  1741.  
  1742. }
  1743.  
  1744.  
  1745.  
  1746. return result.trim() || '0s';
  1747.  
  1748. }
  1749.  
  1750.  
  1751.  
  1752. getWipeSequence(cooldownTemp) {
  1753.  
  1754. const positions = [160, 140, 120, 100, 80, 60, 40, 20, 0];
  1755.  
  1756. const wipeCommands = positions.map(x => G1 X${x} F20000\nG1 Y0 F2000\nG1 Y185 F20000).join('\n');
  1757.  
  1758.  
  1759.  
  1760. return `; === COOLDOWN BEFORE WIPE ===
  1761.  
  1762. M104 S0 ; Turn off hotend
  1763.  
  1764. M140 S${cooldownTemp} ; Set bed to cooldown temperature
  1765.  
  1766. ; === ENHANCED COOLING WAIT ===
  1767.  
  1768. ; Wait for bed to cool down with temperature monitoring
  1769.  
  1770. M190 S${cooldownTemp} ; Wait for bed to reach cooldown temperature
  1771.  
  1772. G4 S30 ; Additional wait to ensure temperature stability
  1773.  
  1774. ; === TEMPERATURE VERIFICATION ===
  1775.  
  1776. ; If bed is still too hot, wait additional time
  1777.  
  1778. M117 Cooling bed to ${cooldownTemp}C ; Display message
  1779.  
  1780. G4 S60 ; Extra cooling time to ensure proper temperature
  1781.  
  1782. ; === SOUND BEFORE WIPE ===
  1783.  
  1784. M17
  1785.  
  1786. M400 S1
  1787.  
  1788. M1006 S1
  1789.  
  1790. M1006 A0 B0 L100 C37 D10 M100 E37 F10 N100
  1791.  
  1792. M1006 A43 B10 L100 C39 D10 M100 E46 F10 N100
  1793.  
  1794. M1006 W
  1795.  
  1796. M18
  1797.  
  1798. G4 S10
  1799.  
  1800. ; === WIPE SEQUENCE (2x Speed) ===
  1801.  
  1802. G1 X160 Y185 F20000 ; Move to rear-right (2x speed)
  1803.  
  1804. G1 Z2 F600 ; Set wipe height
  1805.  
  1806. M400 ; Wait for moves
  1807.  
  1808. ${wipeCommands}
  1809.  
  1810. M400 ; Wait for wipe complete`;
  1811.  
  1812. }
  1813.  
  1814.  
  1815.  
  1816. downloadGcode() {
  1817.  
  1818. if (!this.modifiedGCode) return this.showStatus('Please process the G-code first.', 'warning');
  1819.  
  1820.  
  1821.  
  1822. const blob = new Blob([this.modifiedGCode], { type: 'text/plain' });
  1823.  
  1824. const url = URL.createObjectURL(blob);
  1825.  
  1826. const a = document.createElement('a');
  1827.  
  1828.  
  1829.  
  1830. a.href = url;
  1831.  
  1832. a.download = this.generateFileName('gcode');
  1833.  
  1834. a.click();
  1835.  
  1836. URL.revokeObjectURL(url);
  1837.  
  1838.  
  1839.  
  1840. this.showStatus('G-code downloaded successfully!', 'success');
  1841.  
  1842. }
  1843.  
  1844.  
  1845.  
  1846. async download3mf() {
  1847.  
  1848. if (!this.modifiedGCode || !this.threemfZip) return this.showStatus('Please process the G-code first.', 'warning');
  1849.  
  1850.  
  1851.  
  1852. try {
  1853.  
  1854. this.showStatus('Creating modified 3MF file...', 'info');
  1855.  
  1856.  
  1857.  
  1858. const newZip = new JSZip();
  1859.  
  1860.  
  1861.  
  1862. // Copy all files
  1863.  
  1864. const promises = [];
  1865.  
  1866. this.threemfZip.forEach((path, file) => {
  1867.  
  1868. if (!file.dir) {
  1869.  
  1870. promises.push(file.async('arraybuffer').then(content => newZip.file(path, content)));
  1871.  
  1872. }
  1873.  
  1874. });
  1875.  
  1876.  
  1877.  
  1878. await Promise.all(promises);
  1879.  
  1880.  
  1881.  
  1882. // Update G-code
  1883.  
  1884. newZip.file(this.gcodeFilePath || 'Metadata/print.gcode', this.modifiedGCode);
  1885.  
  1886.  
  1887.  
  1888. const content = await newZip.generateAsync({
  1889.  
  1890. type: 'blob',
  1891.  
  1892. compression: 'DEFLATE',
  1893.  
  1894. compressionOptions: { level: 6 }
  1895.  
  1896. });
  1897.  
  1898.  
  1899.  
  1900. const url = URL.createObjectURL(content);
  1901.  
  1902. const a = document.createElement('a');
  1903.  
  1904.  
  1905.  
  1906. a.href = url;
  1907.  
  1908. a.download = this.generateFileName('3mf');
  1909.  
  1910. a.click();
  1911.  
  1912. URL.revokeObjectURL(url);
  1913.  
  1914.  
  1915.  
  1916. this.showStatus('3MF file downloaded successfully!', 'success');
  1917.  
  1918. } catch (error) {
  1919.  
  1920. this.showStatus(`Error creating 3MF file: ${error.message}`, 'error');
  1921.  
  1922. }
  1923.  
  1924. }
  1925.  
  1926.  
  1927.  
  1928. generateFileName(extension) {
  1929.  
  1930. const params = {
  1931.  
  1932. loopCount: document.getElementById('loopCount').value,
  1933.  
  1934. cooldownTemp: document.getElementById('cooldownTemp').value
  1935.  
  1936. };
  1937.  
  1938. const now = new Date();
  1939.  
  1940. const timestamp = now.toISOString().slice(0, 10).replace(/-/g, '');
  1941.  
  1942. const time = now.toTimeString().slice(0, 8).replace(/:/g, '');
  1943.  
  1944. const baseName = this.fileName.replace(/\.[^/.]+$/, '');
  1945.  
  1946.  
  1947.  
  1948. return ${time}-${baseName}_${timestamp}-${params.loopCount}loop_${params.cooldownTemp}bed.${extension};
  1949.  
  1950. }
  1951.  
  1952.  
  1953.  
  1954. preview() {
  1955.  
  1956. const section = document.getElementById('previewSection');
  1957.  
  1958. const content = document.getElementById('previewContent');
  1959.  
  1960.  
  1961.  
  1962. content.textContent = this.addedBlocks || 'No processed G-code available. Please process the file first.';
  1963.  
  1964. section.classList.remove('hidden');
  1965.  
  1966. section.scrollIntoView({ behavior: 'smooth' });
  1967.  
  1968. }
  1969.  
  1970. }
  1971.  
  1972.  
  1973.  
  1974. document.addEventListener('DOMContentLoaded', () => new GCodeFarmModifier());
  1975.  
  1976. </script>
  1977.  
  1978. </body>
  1979.  
  1980. </html>
  1981.  
  1982. 3. Save
  1983.  
  1984. 4. Open with your web browser
  1985.  
  1986. 5. Load your file, select your settings, verify the earth won't be swallowed in a blackhole, and download your 3MF file (3MF is a zip file, you can inspect it by changing the extension, just sayin')
  1987.  
  1988. #3 bonus lifehack for bambulab
  1989.  
  1990.  
  1991.  
  1992.  
  1993.  
  1994. Go to your printer, find the ip adress
  1995.  
  1996.  
  1997.  
  1998. Note the unlock PIN
  1999.  
  2000.  
  2001.  
  2002. Note the ip adress
  2003.  
  2004.  
  2005.  
  2006. Go to your computer
  2007.  
  2008.  
  2009.  
  2010. Download Filezilla https://filezilla-project.org/
  2011.  
  2012.  
  2013.  
  2014. Open Filezilla, Go to left corner, File > add site
  2015.  
  2016.  
  2017.  
  2018. Select Protocol FTP
  2019.  
  2020.  
  2021.  
  2022. FTP Implicit over TLS
  2023.  
  2024.  
  2025.  
  2026. Normal Auth
  2027.  
  2028.  
  2029.  
  2030. Add the noted ipadress of the printer
  2031.  
  2032.  
  2033.  
  2034. Add the username "bblp" (default by bambulab)
  2035.  
  2036.  
  2037.  
  2038. Add the noted PIN as password
  2039.  
  2040.  
  2041.  
  2042. Save
  2043.  
  2044.  
  2045.  
  2046. COnnect to the printer.
  2047.  
  2048. #4 Drag and drop the 3MF file to the /model fodler in the printer folders tree (on the right in Filezilla)
  2049.  
  2050. #5 In Bambulab Device > Storage > model > Print your <TheLooped3dFile>.3MF
  2051.  
  2052. #6 Manually>> GO to your printer and print.
  2053.  
  2054. Thank you,
  2055.  
  2056. Hoping that helps, feel free to suggest any changes
Tags: DIY
Advertisement
Add Comment
Please, Sign In to add comment