├── .gitignore ├── CHANGELOG.md ├── License ├── README.md ├── doc ├── BOM.md └── RandomThoughts.md ├── mmu2-diy ├── application.cpp ├── application.h └── mmu2-diy.ino ├── piolib └── readme.txt ├── platformio.ini └── stls ├── MK3 Filament Cover (CSK Design).stl ├── MMU2 Selector Cover with modified center hole.stl └── NEMA17 Motor Holder Offset for MMU2.stl /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | platformio\.pro 3 | 4 | \.pioenvs/ 5 | 6 | platformio\.pro\.user 7 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | Changelog for DIY MMU 2.0 2 | ========================= 3 | 4 | 5 | 6 | # 2018-10-18 7 | * refactored repository 8 | * restructured folders and filenames 9 | * add platformio.ini 10 | * add wrapper class Application for compatibility with 11 | Arduino IDE and Platformio with QtCreator. 12 | Tested compiling with Arduino IDE 1.8.5 13 | * reduced RAM usage using progmem macro F() from 139% to 45% 14 | for Arduino Leonardo 15 | -------------------------------------------------------------------------------- /License: -------------------------------------------------------------------------------- 1 | 2 | 3 | GNU GENERAL PUBLIC LICENSE 4 | Version 3, 29 June 2007 5 | 6 | Copyright (C) 2007 Free Software Foundation, Inc. 7 | Everyone is permitted to copy and distribute verbatim copies 8 | of this license document, but changing it is not allowed. 9 | 10 | Preamble 11 | 12 | The GNU General Public License is a free, copyleft license for 13 | software and other kinds of works. 14 | 15 | The licenses for most software and other practical works are designed 16 | to take away your freedom to share and change the works. By contrast, 17 | the GNU General Public License is intended to guarantee your freedom to 18 | share and change all versions of a program--to make sure it remains free 19 | software for all its users. We, the Free Software Foundation, use the 20 | GNU General Public License for most of our software; it applies also to 21 | any other work released this way by its authors. You can apply it to 22 | your programs, too. 23 | 24 | When we speak of free software, we are referring to freedom, not 25 | price. Our General Public Licenses are designed to make sure that you 26 | have the freedom to distribute copies of free software (and charge for 27 | them if you wish), that you receive source code or can get it if you 28 | want it, that you can change the software or use pieces of it in new 29 | free programs, and that you know you can do these things. 30 | 31 | To protect your rights, we need to prevent others from denying you 32 | these rights or asking you to surrender the rights. Therefore, you have 33 | certain responsibilities if you distribute copies of the software, or if 34 | you modify it: responsibilities to respect the freedom of others. 35 | 36 | For example, if you distribute copies of such a program, whether 37 | gratis or for a fee, you must pass on to the recipients the same 38 | freedoms that you received. You must make sure that they, too, receive 39 | or can get the source code. And you must show them these terms so they 40 | know their rights. 41 | 42 | Developers that use the GNU GPL protect your rights with two steps: 43 | (1) assert copyright on the software, and (2) offer you this License 44 | giving you legal permission to copy, distribute and/or modify it. 45 | 46 | For the developers' and authors' protection, the GPL clearly explains 47 | that there is no warranty for this free software. For both users' and 48 | authors' sake, the GPL requires that modified versions be marked as 49 | changed, so that their problems will not be attributed erroneously to 50 | authors of previous versions. 51 | 52 | Some devices are designed to deny users access to install or run 53 | modified versions of the software inside them, although the manufacturer 54 | can do so. This is fundamentally incompatible with the aim of 55 | protecting users' freedom to change the software. The systematic 56 | pattern of such abuse occurs in the area of products for individuals to 57 | use, which is precisely where it is most unacceptable. Therefore, we 58 | have designed this version of the GPL to prohibit the practice for those 59 | products. If such problems arise substantially in other domains, we 60 | stand ready to extend this provision to those domains in future versions 61 | of the GPL, as needed to protect the freedom of users. 62 | 63 | Finally, every program is threatened constantly by software patents. 64 | States should not allow patents to restrict development and use of 65 | software on general-purpose computers, but in those that do, we wish to 66 | avoid the special danger that patents applied to a free program could 67 | make it effectively proprietary. To prevent this, the GPL assures that 68 | patents cannot be used to render the program non-free. 69 | 70 | The precise terms and conditions for copying, distribution and 71 | modification follow. 72 | 73 | TERMS AND CONDITIONS 74 | 75 | 0. Definitions. 76 | 77 | "This License" refers to version 3 of the GNU General Public License. 78 | 79 | "Copyright" also means copyright-like laws that apply to other kinds of 80 | works, such as semiconductor masks. 81 | 82 | "The Program" refers to any copyrightable work licensed under this 83 | License. Each licensee is addressed as "you". "Licensees" and 84 | "recipients" may be individuals or organizations. 85 | 86 | To "modify" a work means to copy from or adapt all or part of the work 87 | in a fashion requiring copyright permission, other than the making of an 88 | exact copy. The resulting work is called a "modified version" of the 89 | earlier work or a work "based on" the earlier work. 90 | 91 | A "covered work" means either the unmodified Program or a work based 92 | on the Program. 93 | 94 | To "propagate" a work means to do anything with it that, without 95 | permission, would make you directly or secondarily liable for 96 | infringement under applicable copyright law, except executing it on a 97 | computer or modifying a private copy. Propagation includes copying, 98 | distribution (with or without modification), making available to the 99 | public, and in some countries other activities as well. 100 | 101 | To "convey" a work means any kind of propagation that enables other 102 | parties to make or receive copies. Mere interaction with a user through 103 | a computer network, with no transfer of a copy, is not conveying. 104 | 105 | An interactive user interface displays "Appropriate Legal Notices" 106 | to the extent that it includes a convenient and prominently visible 107 | feature that (1) displays an appropriate copyright notice, and (2) 108 | tells the user that there is no warranty for the work (except to the 109 | extent that warranties are provided), that licensees may convey the 110 | work under this License, and how to view a copy of this License. If 111 | the interface presents a list of user commands or options, such as a 112 | menu, a prominent item in the list meets this criterion. 113 | 114 | 1. Source Code. 115 | 116 | The "source code" for a work means the preferred form of the work 117 | for making modifications to it. "Object code" means any non-source 118 | form of a work. 119 | 120 | A "Standard Interface" means an interface that either is an official 121 | standard defined by a recognized standards body, or, in the case of 122 | interfaces specified for a particular programming language, one that 123 | is widely used among developers working in that language. 124 | 125 | The "System Libraries" of an executable work include anything, other 126 | than the work as a whole, that (a) is included in the normal form of 127 | packaging a Major Component, but which is not part of that Major 128 | Component, and (b) serves only to enable use of the work with that 129 | Major Component, or to implement a Standard Interface for which an 130 | implementation is available to the public in source code form. A 131 | "Major Component", in this context, means a major essential component 132 | (kernel, window system, and so on) of the specific operating system 133 | (if any) on which the executable work runs, or a compiler used to 134 | produce the work, or an object code interpreter used to run it. 135 | 136 | The "Corresponding Source" for a work in object code form means all 137 | the source code needed to generate, install, and (for an executable 138 | work) run the object code and to modify the work, including scripts to 139 | control those activities. However, it does not include the work's 140 | System Libraries, or general-purpose tools or generally available free 141 | programs which are used unmodified in performing those activities but 142 | which are not part of the work. For example, Corresponding Source 143 | includes interface definition files associated with source files for 144 | the work, and the source code for shared libraries and dynamically 145 | linked subprograms that the work is specifically designed to require, 146 | such as by intimate data communication or control flow between those 147 | subprograms and other parts of the work. 148 | 149 | The Corresponding Source need not include anything that users 150 | can regenerate automatically from other parts of the Corresponding 151 | Source. 152 | 153 | The Corresponding Source for a work in source code form is that 154 | same work. 155 | 156 | 2. Basic Permissions. 157 | 158 | All rights granted under this License are granted for the term of 159 | copyright on the Program, and are irrevocable provided the stated 160 | conditions are met. This License explicitly affirms your unlimited 161 | permission to run the unmodified Program. The output from running a 162 | covered work is covered by this License only if the output, given its 163 | content, constitutes a covered work. This License acknowledges your 164 | rights of fair use or other equivalent, as provided by copyright law. 165 | 166 | You may make, run and propagate covered works that you do not 167 | convey, without conditions so long as your license otherwise remains 168 | in force. You may convey covered works to others for the sole purpose 169 | of having them make modifications exclusively for you, or provide you 170 | with facilities for running those works, provided that you comply with 171 | the terms of this License in conveying all material for which you do 172 | not control copyright. Those thus making or running the covered works 173 | for you must do so exclusively on your behalf, under your direction 174 | and control, on terms that prohibit them from making any copies of 175 | your copyrighted material outside their relationship with you. 176 | 177 | Conveying under any other circumstances is permitted solely under 178 | the conditions stated below. Sublicensing is not allowed; section 10 179 | makes it unnecessary. 180 | 181 | 3. Protecting Users' Legal Rights From Anti-Circumvention Law. 182 | 183 | No covered work shall be deemed part of an effective technological 184 | measure under any applicable law fulfilling obligations under article 185 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or 186 | similar laws prohibiting or restricting circumvention of such 187 | measures. 188 | 189 | When you convey a covered work, you waive any legal power to forbid 190 | circumvention of technological measures to the extent such circumvention 191 | is effected by exercising rights under this License with respect to 192 | the covered work, and you disclaim any intention to limit operation or 193 | modification of the work as a means of enforcing, against the work's 194 | users, your or third parties' legal rights to forbid circumvention of 195 | technological measures. 196 | 197 | 4. Conveying Verbatim Copies. 198 | 199 | You may convey verbatim copies of the Program's source code as you 200 | receive it, in any medium, provided that you conspicuously and 201 | appropriately publish on each copy an appropriate copyright notice; 202 | keep intact all notices stating that this License and any 203 | non-permissive terms added in accord with section 7 apply to the code; 204 | keep intact all notices of the absence of any warranty; and give all 205 | recipients a copy of this License along with the Program. 206 | 207 | You may charge any price or no price for each copy that you convey, 208 | and you may offer support or warranty protection for a fee. 209 | 210 | 5. Conveying Modified Source Versions. 211 | 212 | You may convey a work based on the Program, or the modifications to 213 | produce it from the Program, in the form of source code under the 214 | terms of section 4, provided that you also meet all of these conditions: 215 | 216 | a) The work must carry prominent notices stating that you modified 217 | it, and giving a relevant date. 218 | 219 | b) The work must carry prominent notices stating that it is 220 | released under this License and any conditions added under section 221 | 7. This requirement modifies the requirement in section 4 to 222 | "keep intact all notices". 223 | 224 | c) You must license the entire work, as a whole, under this 225 | License to anyone who comes into possession of a copy. This 226 | License will therefore apply, along with any applicable section 7 227 | additional terms, to the whole of the work, and all its parts, 228 | regardless of how they are packaged. This License gives no 229 | permission to license the work in any other way, but it does not 230 | invalidate such permission if you have separately received it. 231 | 232 | d) If the work has interactive user interfaces, each must display 233 | Appropriate Legal Notices; however, if the Program has interactive 234 | interfaces that do not display Appropriate Legal Notices, your 235 | work need not make them do so. 236 | 237 | A compilation of a covered work with other separate and independent 238 | works, which are not by their nature extensions of the covered work, 239 | and which are not combined with it such as to form a larger program, 240 | in or on a volume of a storage or distribution medium, is called an 241 | "aggregate" if the compilation and its resulting copyright are not 242 | used to limit the access or legal rights of the compilation's users 243 | beyond what the individual works permit. Inclusion of a covered work 244 | in an aggregate does not cause this License to apply to the other 245 | parts of the aggregate. 246 | 247 | 6. Conveying Non-Source Forms. 248 | 249 | You may convey a covered work in object code form under the terms 250 | of sections 4 and 5, provided that you also convey the 251 | machine-readable Corresponding Source under the terms of this License, 252 | in one of these ways: 253 | 254 | a) Convey the object code in, or embodied in, a physical product 255 | (including a physical distribution medium), accompanied by the 256 | Corresponding Source fixed on a durable physical medium 257 | customarily used for software interchange. 258 | 259 | b) Convey the object code in, or embodied in, a physical product 260 | (including a physical distribution medium), accompanied by a 261 | written offer, valid for at least three years and valid for as 262 | long as you offer spare parts or customer support for that product 263 | model, to give anyone who possesses the object code either (1) a 264 | copy of the Corresponding Source for all the software in the 265 | product that is covered by this License, on a durable physical 266 | medium customarily used for software interchange, for a price no 267 | more than your reasonable cost of physically performing this 268 | conveying of source, or (2) access to copy the 269 | Corresponding Source from a network server at no charge. 270 | 271 | c) Convey individual copies of the object code with a copy of the 272 | written offer to provide the Corresponding Source. This 273 | alternative is allowed only occasionally and noncommercially, and 274 | only if you received the object code with such an offer, in accord 275 | with subsection 6b. 276 | 277 | d) Convey the object code by offering access from a designated 278 | place (gratis or for a charge), and offer equivalent access to the 279 | Corresponding Source in the same way through the same place at no 280 | further charge. You need not require recipients to copy the 281 | Corresponding Source along with the object code. If the place to 282 | copy the object code is a network server, the Corresponding Source 283 | may be on a different server (operated by you or a third party) 284 | that supports equivalent copying facilities, provided you maintain 285 | clear directions next to the object code saying where to find the 286 | Corresponding Source. Regardless of what server hosts the 287 | Corresponding Source, you remain obligated to ensure that it is 288 | available for as long as needed to satisfy these requirements. 289 | 290 | e) Convey the object code using peer-to-peer transmission, provided 291 | you inform other peers where the object code and Corresponding 292 | Source of the work are being offered to the general public at no 293 | charge under subsection 6d. 294 | 295 | A separable portion of the object code, whose source code is excluded 296 | from the Corresponding Source as a System Library, need not be 297 | included in conveying the object code work. 298 | 299 | A "User Product" is either (1) a "consumer product", which means any 300 | tangible personal property which is normally used for personal, family, 301 | or household purposes, or (2) anything designed or sold for incorporation 302 | into a dwelling. In determining whether a product is a consumer product, 303 | doubtful cases shall be resolved in favor of coverage. For a particular 304 | product received by a particular user, "normally used" refers to a 305 | typical or common use of that class of product, regardless of the status 306 | of the particular user or of the way in which the particular user 307 | actually uses, or expects or is expected to use, the product. A product 308 | is a consumer product regardless of whether the product has substantial 309 | commercial, industrial or non-consumer uses, unless such uses represent 310 | the only significant mode of use of the product. 311 | 312 | "Installation Information" for a User Product means any methods, 313 | procedures, authorization keys, or other information required to install 314 | and execute modified versions of a covered work in that User Product from 315 | a modified version of its Corresponding Source. The information must 316 | suffice to ensure that the continued functioning of the modified object 317 | code is in no case prevented or interfered with solely because 318 | modification has been made. 319 | 320 | If you convey an object code work under this section in, or with, or 321 | specifically for use in, a User Product, and the conveying occurs as 322 | part of a transaction in which the right of possession and use of the 323 | User Product is transferred to the recipient in perpetuity or for a 324 | fixed term (regardless of how the transaction is characterized), the 325 | Corresponding Source conveyed under this section must be accompanied 326 | by the Installation Information. But this requirement does not apply 327 | if neither you nor any third party retains the ability to install 328 | modified object code on the User Product (for example, the work has 329 | been installed in ROM). 330 | 331 | The requirement to provide Installation Information does not include a 332 | requirement to continue to provide support service, warranty, or updates 333 | for a work that has been modified or installed by the recipient, or for 334 | the User Product in which it has been modified or installed. Access to a 335 | network may be denied when the modification itself materially and 336 | adversely affects the operation of the network or violates the rules and 337 | protocols for communication across the network. 338 | 339 | Corresponding Source conveyed, and Installation Information provided, 340 | in accord with this section must be in a format that is publicly 341 | documented (and with an implementation available to the public in 342 | source code form), and must require no special password or key for 343 | unpacking, reading or copying. 344 | 345 | 7. Additional Terms. 346 | 347 | "Additional permissions" are terms that supplement the terms of this 348 | License by making exceptions from one or more of its conditions. 349 | Additional permissions that are applicable to the entire Program shall 350 | be treated as though they were included in this License, to the extent 351 | that they are valid under applicable law. If additional permissions 352 | apply only to part of the Program, that part may be used separately 353 | under those permissions, but the entire Program remains governed by 354 | this License without regard to the additional permissions. 355 | 356 | When you convey a copy of a covered work, you may at your option 357 | remove any additional permissions from that copy, or from any part of 358 | it. (Additional permissions may be written to require their own 359 | removal in certain cases when you modify the work.) You may place 360 | additional permissions on material, added by you to a covered work, 361 | for which you have or can give appropriate copyright permission. 362 | 363 | Notwithstanding any other provision of this License, for material you 364 | add to a covered work, you may (if authorized by the copyright holders of 365 | that material) supplement the terms of this License with terms: 366 | 367 | a) Disclaiming warranty or limiting liability differently from the 368 | terms of sections 15 and 16 of this License; or 369 | 370 | b) Requiring preservation of specified reasonable legal notices or 371 | author attributions in that material or in the Appropriate Legal 372 | Notices displayed by works containing it; or 373 | 374 | c) Prohibiting misrepresentation of the origin of that material, or 375 | requiring that modified versions of such material be marked in 376 | reasonable ways as different from the original version; or 377 | 378 | d) Limiting the use for publicity purposes of names of licensors or 379 | authors of the material; or 380 | 381 | e) Declining to grant rights under trademark law for use of some 382 | trade names, trademarks, or service marks; or 383 | 384 | f) Requiring indemnification of licensors and authors of that 385 | material by anyone who conveys the material (or modified versions of 386 | it) with contractual assumptions of liability to the recipient, for 387 | any liability that these contractual assumptions directly impose on 388 | those licensors and authors. 389 | 390 | All other non-permissive additional terms are considered "further 391 | restrictions" within the meaning of section 10. If the Program as you 392 | received it, or any part of it, contains a notice stating that it is 393 | governed by this License along with a term that is a further 394 | restriction, you may remove that term. If a license document contains 395 | a further restriction but permits relicensing or conveying under this 396 | License, you may add to a covered work material governed by the terms 397 | of that license document, provided that the further restriction does 398 | not survive such relicensing or conveying. 399 | 400 | If you add terms to a covered work in accord with this section, you 401 | must place, in the relevant source files, a statement of the 402 | additional terms that apply to those files, or a notice indicating 403 | where to find the applicable terms. 404 | 405 | Additional terms, permissive or non-permissive, may be stated in the 406 | form of a separately written license, or stated as exceptions; 407 | the above requirements apply either way. 408 | 409 | 8. Termination. 410 | 411 | You may not propagate or modify a covered work except as expressly 412 | provided under this License. Any attempt otherwise to propagate or 413 | modify it is void, and will automatically terminate your rights under 414 | this License (including any patent licenses granted under the third 415 | paragraph of section 11). 416 | 417 | However, if you cease all violation of this License, then your 418 | license from a particular copyright holder is reinstated (a) 419 | provisionally, unless and until the copyright holder explicitly and 420 | finally terminates your license, and (b) permanently, if the copyright 421 | holder fails to notify you of the violation by some reasonable means 422 | prior to 60 days after the cessation. 423 | 424 | Moreover, your license from a particular copyright holder is 425 | reinstated permanently if the copyright holder notifies you of the 426 | violation by some reasonable means, this is the first time you have 427 | received notice of violation of this License (for any work) from that 428 | copyright holder, and you cure the violation prior to 30 days after 429 | your receipt of the notice. 430 | 431 | Termination of your rights under this section does not terminate the 432 | licenses of parties who have received copies or rights from you under 433 | this License. If your rights have been terminated and not permanently 434 | reinstated, you do not qualify to receive new licenses for the same 435 | material under section 10. 436 | 437 | 9. Acceptance Not Required for Having Copies. 438 | 439 | You are not required to accept this License in order to receive or 440 | run a copy of the Program. Ancillary propagation of a covered work 441 | occurring solely as a consequence of using peer-to-peer transmission 442 | to receive a copy likewise does not require acceptance. However, 443 | nothing other than this License grants you permission to propagate or 444 | modify any covered work. These actions infringe copyright if you do 445 | not accept this License. Therefore, by modifying or propagating a 446 | covered work, you indicate your acceptance of this License to do so. 447 | 448 | 10. Automatic Licensing of Downstream Recipients. 449 | 450 | Each time you convey a covered work, the recipient automatically 451 | receives a license from the original licensors, to run, modify and 452 | propagate that work, subject to this License. You are not responsible 453 | for enforcing compliance by third parties with this License. 454 | 455 | An "entity transaction" is a transaction transferring control of an 456 | organization, or substantially all assets of one, or subdividing an 457 | organization, or merging organizations. If propagation of a covered 458 | work results from an entity transaction, each party to that 459 | transaction who receives a copy of the work also receives whatever 460 | licenses to the work the party's predecessor in interest had or could 461 | give under the previous paragraph, plus a right to possession of the 462 | Corresponding Source of the work from the predecessor in interest, if 463 | the predecessor has it or can get it with reasonable efforts. 464 | 465 | You may not impose any further restrictions on the exercise of the 466 | rights granted or affirmed under this License. For example, you may 467 | not impose a license fee, royalty, or other charge for exercise of 468 | rights granted under this License, and you may not initiate litigation 469 | (including a cross-claim or counterclaim in a lawsuit) alleging that 470 | any patent claim is infringed by making, using, selling, offering for 471 | sale, or importing the Program or any portion of it. 472 | 473 | 11. Patents. 474 | 475 | A "contributor" is a copyright holder who authorizes use under this 476 | License of the Program or a work on which the Program is based. The 477 | work thus licensed is called the contributor's "contributor version". 478 | 479 | A contributor's "essential patent claims" are all patent claims 480 | owned or controlled by the contributor, whether already acquired or 481 | hereafter acquired, that would be infringed by some manner, permitted 482 | by this License, of making, using, or selling its contributor version, 483 | but do not include claims that would be infringed only as a 484 | consequence of further modification of the contributor version. For 485 | purposes of this definition, "control" includes the right to grant 486 | patent sublicenses in a manner consistent with the requirements of 487 | this License. 488 | 489 | Each contributor grants you a non-exclusive, worldwide, royalty-free 490 | patent license under the contributor's essential patent claims, to 491 | make, use, sell, offer for sale, import and otherwise run, modify and 492 | propagate the contents of its contributor version. 493 | 494 | In the following three paragraphs, a "patent license" is any express 495 | agreement or commitment, however denominated, not to enforce a patent 496 | (such as an express permission to practice a patent or covenant not to 497 | sue for patent infringement). To "grant" such a patent license to a 498 | party means to make such an agreement or commitment not to enforce a 499 | patent against the party. 500 | 501 | If you convey a covered work, knowingly relying on a patent license, 502 | and the Corresponding Source of the work is not available for anyone 503 | to copy, free of charge and under the terms of this License, through a 504 | publicly available network server or other readily accessible means, 505 | then you must either (1) cause the Corresponding Source to be so 506 | available, or (2) arrange to deprive yourself of the benefit of the 507 | patent license for this particular work, or (3) arrange, in a manner 508 | consistent with the requirements of this License, to extend the patent 509 | license to downstream recipients. "Knowingly relying" means you have 510 | actual knowledge that, but for the patent license, your conveying the 511 | covered work in a country, or your recipient's use of the covered work 512 | in a country, would infringe one or more identifiable patents in that 513 | country that you have reason to believe are valid. 514 | 515 | If, pursuant to or in connection with a single transaction or 516 | arrangement, you convey, or propagate by procuring conveyance of, a 517 | covered work, and grant a patent license to some of the parties 518 | receiving the covered work authorizing them to use, propagate, modify 519 | or convey a specific copy of the covered work, then the patent license 520 | you grant is automatically extended to all recipients of the covered 521 | work and works based on it. 522 | 523 | A patent license is "discriminatory" if it does not include within 524 | the scope of its coverage, prohibits the exercise of, or is 525 | conditioned on the non-exercise of one or more of the rights that are 526 | specifically granted under this License. You may not convey a covered 527 | work if you are a party to an arrangement with a third party that is 528 | in the business of distributing software, under which you make payment 529 | to the third party based on the extent of your activity of conveying 530 | the work, and under which the third party grants, to any of the 531 | parties who would receive the covered work from you, a discriminatory 532 | patent license (a) in connection with copies of the covered work 533 | conveyed by you (or copies made from those copies), or (b) primarily 534 | for and in connection with specific products or compilations that 535 | contain the covered work, unless you entered into that arrangement, 536 | or that patent license was granted, prior to 28 March 2007. 537 | 538 | Nothing in this License shall be construed as excluding or limiting 539 | any implied license or other defenses to infringement that may 540 | otherwise be available to you under applicable patent law. 541 | 542 | 12. No Surrender of Others' Freedom. 543 | 544 | If conditions are imposed on you (whether by court order, agreement or 545 | otherwise) that contradict the conditions of this License, they do not 546 | excuse you from the conditions of this License. If you cannot convey a 547 | covered work so as to satisfy simultaneously your obligations under this 548 | License and any other pertinent obligations, then as a consequence you may 549 | not convey it at all. For example, if you agree to terms that obligate you 550 | to collect a royalty for further conveying from those to whom you convey 551 | the Program, the only way you could satisfy both those terms and this 552 | License would be to refrain entirely from conveying the Program. 553 | 554 | 13. Use with the GNU Affero General Public License. 555 | 556 | Notwithstanding any other provision of this License, you have 557 | permission to link or combine any covered work with a work licensed 558 | under version 3 of the GNU Affero General Public License into a single 559 | combined work, and to convey the resulting work. The terms of this 560 | License will continue to apply to the part which is the covered work, 561 | but the special requirements of the GNU Affero General Public License, 562 | section 13, concerning interaction through a network will apply to the 563 | combination as such. 564 | 565 | 14. Revised Versions of this License. 566 | 567 | The Free Software Foundation may publish revised and/or new versions of 568 | the GNU General Public License from time to time. Such new versions will 569 | be similar in spirit to the present version, but may differ in detail to 570 | address new problems or concerns. 571 | 572 | Each version is given a distinguishing version number. If the 573 | Program specifies that a certain numbered version of the GNU General 574 | Public License "or any later version" applies to it, you have the 575 | option of following the terms and conditions either of that numbered 576 | version or of any later version published by the Free Software 577 | Foundation. If the Program does not specify a version number of the 578 | GNU General Public License, you may choose any version ever published 579 | by the Free Software Foundation. 580 | 581 | If the Program specifies that a proxy can decide which future 582 | versions of the GNU General Public License can be used, that proxy's 583 | public statement of acceptance of a version permanently authorizes you 584 | to choose that version for the Program. 585 | 586 | Later license versions may give you additional or different 587 | permissions. However, no additional obligations are imposed on any 588 | author or copyright holder as a result of your choosing to follow a 589 | later version. 590 | 591 | 15. Disclaimer of Warranty. 592 | 593 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY 594 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT 595 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY 596 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, 597 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 598 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM 599 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF 600 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 601 | 602 | 16. Limitation of Liability. 603 | 604 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 605 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS 606 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY 607 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE 608 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF 609 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD 610 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), 611 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF 612 | SUCH DAMAGES. 613 | 614 | 17. Interpretation of Sections 15 and 16. 615 | 616 | If the disclaimer of warranty and limitation of liability provided 617 | above cannot be given local legal effect according to their terms, 618 | reviewing courts shall apply local law that most closely approximates 619 | an absolute waiver of all civil liability in connection with the 620 | Program, unless a warranty or assumption of liability accompanies a 621 | copy of the Program in return for a fee. 622 | 623 | END OF TERMS AND CONDITIONS 624 | 625 | 626 | 627 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | DIY Multi Material Upgrade V2.0 2 | ========================== 3 | firmware clone project 4 | -------------------------------- 5 | 6 | This code was developed on an Arduino Mega board using three 8825 stepper motors to control the MMU2 multi-color print head. 7 | It was designed to operate with the PRUSA mk3 printer. This code was developed to replace the stock MMU2 controller board, I got bored so I decided to create my own design. Getting it to play nicely with the mk3 was quite a challenge. This home brew MMU2 only will operate with the MK3 currently. 8 | 9 | You will need an Arduino Mega 2560 processor board and a RAMPS 1.5 or RAMPS 1.6 shield (for the stepper motor controllers) - both are available on Amazon (see my BOM for additional details) 10 | 11 | A serial cable is required (5 pin) in order to communicate between this board and the Mk3 controller. (Vcc, Tx, Rx, GND, Reset). Only 3 pins are required (Tx, Rx and Reset) in order to work properly with the Mk3. 12 | 13 | 14 | ## WARNING: 15 | This project is a work in progress and requires a knowledge of Mk3 hardware and software. If you don't know what you are doing, you can/will PERMANENTLY damage your hardware. I hope you have been appropriately warned. 16 | 17 | ## ADDITIONAL WARNING 18 | you probably won't read this because it is too far down the page: 19 | 20 | **Stepper Motor controllers (8825) are high current devices and they get warm/hot. You can damage these controller easily 21 | (I have) if you don't adjust the source current. There is a very small variable resister you will have to adjust, I 22 | recommend setting this to 0.6V (details on how to adjust this are on the polulu.com website - look for 8825 stepper 23 | motor controller). If you don't have a multimeter for measuring voltage then STOP - you are in deep waters.** 24 | 25 | # TODOs 26 | * Document Pinouts for each of the stepper motors 27 | * Document Pinouts for 2 sensor cables 28 | * Document Pinouts for Cable between MMU2 controller and MK3 controller (TX, RX, Reset, Vcc) 29 | -------------------------------------------------------------------------------- /doc/BOM.md: -------------------------------------------------------------------------------- 1 | DIY Prusa Multi Material Upgrade (MMU) 2.0 2 | ========================================== 3 | Bill of Materials (10.20.2018) 4 | ------------------------------ 5 | For firmware, please refer to https://github.com/cskozlowski/mmu2 6 | 7 | Be warned, this is a very difficult project. 8 | Everyone wants to know the $$$, the list below is under $200 (US). 9 | The most expensive parts are: stepper motors ($30 for the three I purchased), arduino ($14) and RAMPS 1.6 board ($14). 10 | This is a starter list of stuff you will need to complete this MMU2 home brew clone 11 | 12 | # Change Notes: 13 | * 10.20.18 Added another stepper motor alternative (w/ proper axles) - from aliexpress.com 14 | * 10.17.18 Added 3 STL files required for build of the home-brew MMU2 (do the NEMA17 holder in PETG, other pieces can be 15 | done in PLA or PETG) 16 | * 10.16.18 Changed the stepper motor recommendation, you can also get them on aliexpress.com) 17 | * 10.10.18 Added references to 3 STL design files that you will need (see STL for additional details) 18 | * 10.9.18 626-ZZ should say 625-ZZ bearings ... 626-ZZ are rarer than 1.85mm PTFE 19 | * 10.9.18 Added 7mm ball bearing (you need this for the FINDA detector) 20 | * 10.9.18 Added 5mm x 8mm coupler (need this for the selector stepper motor to threaded rod) 21 | 22 | # BOM 23 | 24 | ## Prusa Parts 25 | * MMU2 plastic parts - https://www.prusa3d.com/prusa-i3-printable-parts/ 26 | 27 | ## Motors 28 | 29 | * ~~3 Stepper Motors~~ 30 | (DO NOT PURCHASE THESE MOTORS, THEY DO NOT HAVE SUFFICIENT STALL TORQUE)(https://www.amazon.com/gp/product/B0716S32G4/ref=oh_aui_detailpage_o03_s00?ie=UTF8&psc=1) 31 | (these motors are super cheap $10 per motor ... don't buy them because they don't have sufficient torque) 32 | (I used them for 3 weeks and chased problems that won't be there if you get better motors 33 | (get the motors I list below) 34 | 35 | 36 | * 3 Stepper Motors 37 | (PURCHASE THESE STEPPER MOTORS INSTEAD)(https://www.amazon.com/gp/product/B06XSYP24P/ref=oh_aui_detailpage_o01_s00?ie=UTF8&psc=1) 38 | 39 | (these motors have 46 N-cm of stall torque. They idler stepper motor needs quite a bit of stall torque to stay 40 | in place when the bearing is being moved against the extruder stepper motor). 41 | 42 | Get a motor with a minimum of 45 N-cm of stall torque (you will thank me later) 43 | 44 | Another potential alternative is these 3 stepper motors (they have proper shafts for the MMU2) 45 | https://www.aliexpress.com/item/Prusa-i3-mk3-mk2-5-Multi-materials-2-0-3d-printer-motor-kit-MMU2-0/32923672790.html?spm=a2g0s.9042311.0.0.162b4c4dO5RBTY (I have them on order but have not received them yet for testing) 46 | 47 | ## Other Stuff 48 | * 5 mk8 hobbed gears (https://www.amazon.com/gp/product/B07CJPP7R7/ref=oh_aui_detailpage_o03_s00?ie=UTF8&psc=1) 49 | * 2 100mm x 5mm steel shafts (https://www.amazon.com/gp/product/B01B27MJC6/ref=oh_aui_detailpage_o04_s00?ie=UTF8&psc=1) 50 | (these shafts are used to make your 16mm spindles for the bearings, you can also order 16mm x 5mm spindles from McMcaster-Carr, 51 | (however, they are wicked tight on the bearings) 52 | * 2 150mm x 5mm steel shafts (https://www.amazon.com/gp/product/B01B5QTM8I/ref=oh_aui_detailpage_o01_s00?ie=UTF8&psc=1) 53 | (these shafts are used for the selector head shaft guides, you will have to cut them down to size) 54 | (if you don't know how to cut steel shafts then please stop and go do something else) 55 | * 9 625-ZZ bearings (https://www.amazon.com/gp/product/B01CUTIQWW/ref=oh_aui_detailpage_o04_s01?ie=UTF8&psc=1) 56 | * 20 3mm thin (1.8mm) square nuts) https://www.amazon.com/gp/product/B073SBCMBM/ref=oh_aui_detailpage_o05_s00?ie=UTF8&psc=1 57 | * 1 5mm x 5mm coupler https://www.amazon.com/gp/product/B0159WO7T8/ref=oh_aui_detailpage_o02_s00?ie=UTF8&psc=1 58 | (you need one but buy two because you can) 59 | * 1 5mm x 8mm coupler (amazon.com ... too lazy to provide the link at the moment). 60 | * 25' 4mm OD x 2mm ID PTFE Tubing (https://www.amazon.com/gp/product/B073RFQQ3Z/ref=oh_aui_detailpage_o09_s00?ie=UTF8&th=1) 61 | Needed for the feeders from the spools to the MMU2 unit, you will learn to hate PTFE tubing 62 | * 1 50mm 4mm OD x 1.85mm ID PTFE Tubing (flurostore.com has 1.80mm ID tubing - it works !!!) 63 | By far the most difficult part to obtain, it should just be called unobtanium 64 | * 1 150mm x 8mm threaded shaft (https://www.amazon.com/gp/product/B07C8P1DWX/ref=oh_aui_detailpage_o08_s01?ie=UTF8&psc=1) 65 | (yes, you will have to cut it to size using some of your cool shop tools) 66 | * 10 PC4-10 PTFE connectors (https://www.amazon.com/gp/product/B01IB81IHG/ref=oh_aui_detailpage_o02_s00?ie=UTF8&psc=1) 67 | (get these, they are awesome) 68 | * 1 12-15V, 2A Power supply - needed for the Arduino Mega/RAMPS board for initial testing 69 | * 1 Arduino Mega 2560 (https://www.amazon.com/gp/product/B01H4ZDYCE/ref=oh_aui_detailpage_o01_s00?ie=UTF8&psc=1) 70 | * 1 1.x RAMPS Boards (https://www.amazon.com/gp/product/B0794YN8XK/ref=oh_aui_detailpage_o09_s00?ie=UTF8&psc=1) 71 | (home for all of your stepper motor controllers) 72 | * 3 8825 Stepper Motor Controller (https://www.amazon.com/gp/product/B00S3Q9YZA/ref=oh_aui_detailpage_o05_s00?ie=UTF8&psc=1) 73 | (make certain to tweak the voltage rheostat on the top of this little wonder, goto polulu website) 74 | (I currently have mine set to 0.60V - don't go too high you will fry this component) 75 | * 1 Razor Blades (https://www.amazon.com/gp/product/B007QYAJRC/ref=oh_aui_detailpage_o07_s00?ie=UTF8&psc=1) 76 | * 1 microswitch (https://www.amazon.com/gp/product/B06XSBYQ8H/ref=oh_aui_detailpage_o05_s00?ie=UTF8&psc=1) 77 | * 1 FINDA (aka PINDA) Probe (https://www.amazon.com/URBEST-Detecting-Distance-Inductive-Proximity/dp/B01M1777XK/ref=sr_1_fkmr1_3?s=industrial&ie=UTF8&qid=1538915473&sr=1-3-fkmr1&keywords=prusa+pinda+probe) 78 | ( I had a spare probe laying around so I have NOT tried this inductive probe) 79 | (they are also available from aliexpress.com) 80 | * 1 7mm ball bearing (mcmaster.carr or amazon), you only need one but you will have to buy many more 81 | * 2 6mm OD x .45mm thick brass tubing (ksmetals.com - part #982, this comes in 300mm length and you will have to cut it down) 82 | 83 | ## Custom STL Files 84 | * 1 MMU2 to PTFE Festo Coupler 85 | * 1 MK3 to PTFE Festo Coupler (w/ microswitch holder) 86 | * 1 Selector Motor Mount offset block 87 | 88 | ## DigiKey Components (needed for cables and wiring) 89 | (to be completed) 90 | 91 | 92 | 93 | 94 | ## Random Tools 95 | (it's a project, you've got to buy new tools - you live for this stuff) 96 | * 1 Reamer (https://www.amazon.com/gp/product/B01EIH573K/ref=oh_aui_detailpage_o07_s00?ie=UTF8&psc=1) 97 | (used to make awesome interior bevelled edges in that damn 2mm (and 1.80mm) PTFE 98 | * 50mm cutting jig (https://www.thingiverse.com/thing:3062809) 99 | (oh look, you get to make something with your 3d printer) 100 | -------------------------------------------------------------------------------- /doc/RandomThoughts.md: -------------------------------------------------------------------------------- 1 | Random Thoughts on the DIY MMU 2.0 2 | ====================== 3 | by Chuck Kozlowski 4 | ---------------------------- 5 | 6 | I started the home-brew MMU2 project on September 19th, 2018. It has now been 4 weeks and the unit has been working quite well over the past few days after a solid month of tinkering. Here are a couple of things to help those with their MMU2 adventures not necessarily in any type of useful order: 7 | 8 | # 1. Filament Path 9 | make certain your path from the filament spool all the way to the top of the mk3 extruder is smooth and does not bind. If there is any binding, find the source (typically at one of the interface points) and fix it. You will need to do this for each of the 5 filament paths. I can't emphasize this enough - you need to have each filament path flowing smoothhly in order for the MMU2 to operate correctly. If you don't do this, you will pay the price for both filament loading and unloading while the MMU2 is attempting to manipulate the filament. 10 | 11 | # 2. Festo Couplers 12 | I used 10mm OD (threaded) x 4mm (ID) couplers at ALL interface points (one at the selector of the MMU2 and another at the top of the MK3). My filament touches no metal and this is key to avoid small filament shavings from accumulating while the MMU2 is operating 13 | 14 | # 3. Filament Loading 15 | ## Part 1 16 | I realize that the stock MMU2 requires the operator to dial-in the filament length for each of the 5 filaments. Make certain you take the time to open the bondtech door and see that the filament is going ALL the way to the middle of the bondtech gear. If you don't do this then you will have intermittent filament-load issues which usuallly result in a missing layer during operation 17 | ## Part 2 18 | I chose to modify the fundamental design and added a 2nd filament sensor at the top of the MK3 extruder so I know that the filament is actually getting loaded. In fact, I don't really care about the length of the bowden tube from the MMU2 to the MK3 extruder since the sensor tells me when the filament has arrived. When the filament arrives, I push it an additional 31mm and it lands right in the middle of the bondtech gear EVERY time. I think that the current design of the MMU2 is fundamentally flawed in this area and you will have a difficult time operating 100% of the time without a modification similar to the one I just outlined. 19 | 20 | # 4. Filament Unloading 21 | This is another tricky area since the hot-end does things to the end of the filament during an extraction. The goal is to form a tip with no stringiness before the filament unload occurs. If you have not replaced your stock 2.00mm PTFE hot-end part (50mm in length) then STOP reading and go and install the provided part from your kit (1.85mm PTFE). Since I built my unit from scratch and there is no 1.85mm PTFE in the wild - I procured some 1.80mm PTFE (flurostore.com) and it works wonderfully. The main reason for this critical piece is to make sure the blob that naturally forms on the end of the filament during a full retraction needs to stay below the magical 2.00mm PTFE ID width. If you don't do this upgrade then you will have filament unload jamming and that is suck factor 10. 22 | 23 | # 5. Slic3r Settings 24 | Make sure your 'Cooling Moves' are at least 2 when you start. This is key to forming a properly filament tip during the filament unload process. Make certain that your 'filament load' setting is at least 21 mm/sec. I have mine currently set at 30 mm/sec but that is because I have tuned my firmware in the MMU2 to match this speed. There are some very critical things happending for up to 2 seconds during a filament load when both the MMU2 gears and the MK3 bondtech gear are operating synchronously - if you don't have the speeds match then you will hear filament grinding. Grinding is not your friend - you will need to adjust the filament load speed until grinding stops. 25 | -------------------------------------------------------------------------------- /mmu2-diy/application.cpp: -------------------------------------------------------------------------------- 1 | // CSK MMU2 Controller Version 2 | // 3 | // Code developed by Chuck Kozlowski 4 | // September 19, 2018 5 | // 6 | // Code was developed because I am impatiently waiting for my MMU2 to arrive (due in December, 2018) so I thought 7 | // I would develop some code to operate the PRUSA MMU2 hardware 8 | // 9 | // This code uses 3 stepper motor controllers and 1 Pinda filament sensor, and 1 additional filament sensor on the mk3 extruder top 10 | // 11 | // 12 | // Work to be done: Interface Control with the Einsy Board (MK3) - (work completed on 9.25.18) 13 | // Refine speed and acceleration settings for each stepper motor 14 | // Failure Recovery Modes - basically non-existent (work completed on 10.5.18) 15 | // 16 | // Uses the serial interface with a host computer at the moment - probably could do some smarter things 17 | // like selection switches and some LEDs. 18 | // 10.14.18 Leave the Selector Stepper Motor ON ... appear to be losing position with the selector during operation 19 | // [now using the J-4218HB2301 Stepper Motor - (45 N-cm torque) as the idler stepper motor] 20 | // 10.14.18 Added yet another idler command set (specialparkidler() and specialunparkidler) - used within the 'C' Command 21 | // Idler management is a bit of challenge, probably has more to do with my coding skills (or lack thereof). 22 | // 10.12.18 Minor fix to idler parking ... now use quickparkidler() after 'C' command and quickunparkidler() at beginning of 'T' command 23 | // This helps to speed up the idler movements during 'T' and 'C' commands 24 | // 10.5.18 Made major tweak to the 'C' command, now matches the speed of the mk3 extruder gear (see slic3r 'load' setting) 25 | // 10.2.18 Moved from breadboard to RAMPS 1.6 Board and remapped ALL addresses 26 | // Discovered the filament idler motor needed to be set at a higher torque (more current) 27 | // (this was affected filament load consistency) 28 | // 10.2.18 Major Disaster, lost my codebase on my PC (I am an idiot) 29 | // Thank God for github so I could recover a week old version of my code 30 | // 10.1.18 Added filament sensor to the extruder head (helps reliability 31 | 32 | 33 | //#include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include "application.h" 39 | 40 | 41 | 42 | static int isFilamentLoaded(); 43 | static void initIdlerPosition(); 44 | static void checkSerialInterface(); 45 | static void initColorSelector(); 46 | static void filamentLoadWithBondTechGear(); 47 | static void toolChange( char selection); 48 | static void quickUnParkIdler(); 49 | static void unParkIdler(); 50 | static void unloadFilamentToFinda(); 51 | static void parkIdler(); 52 | static void activateColorSelector(); 53 | static void idlerSelector(char filament); 54 | static void colorSelector(char selection); 55 | static void loadFilamentToFinda(); 56 | static void fixTheProblem(String statement); 57 | static void csTurnAmount(int steps, int direction); 58 | static void feedFilament(unsigned int steps); 59 | 60 | 61 | #define SERIAL1ENABLED 1 62 | #define ENABLE LOW // 8825 stepper motor enable is active low 63 | #define DISABLE HIGH // 8825 stepper motor disable is active high 64 | 65 | #define MMU2_VERSION "4.2 10/12/18" 66 | 67 | #define STEPSPERMM 144 // these are the number of steps required to travel 1 mm using the extruder motor 68 | 69 | #define FW_VERSION 90 // config.h (MM-control-01 firmware) 70 | #define FW_BUILDNR 85 // config.h (MM-control-01 firmware) 71 | 72 | #define ORIGINALCODE 0 // code that is no longer needed for operational use 73 | int command = 0; 74 | 75 | // changed from 125 to 115 (10.13.18) 76 | #define MAXROLLERTRAVEL 125 // number of steps that the roller bearing stepper motor can travel 77 | 78 | #define FULL_STEP 1 79 | #define HALF_STEP 2 80 | #define QUARTER_STEP 4 81 | #define EIGTH_STEP 8 82 | #define SIXTEENTH_STEP 16 83 | 84 | #define STEPSIZE SIXTEENTH_STEP // setup for each of the three stepper motors (jumper settings for M0,M1,M2) on the RAMPS 1.x board 85 | 86 | #define STEPSPERREVOLUTION 200 // 200 steps per revolution - 1.8 degree motors are being used 87 | 88 | #define MAXSELECTOR_STEPS 1890 // maximum number of selector stepper motor (used to move all the way to the right or left 89 | 90 | #define MMU2TOEXTRUDERSTEPS STEPSIZE*STEPSPERREVOLUTION*19 // for the 'T' command 91 | 92 | #define CW 0 93 | #define CCW 1 94 | 95 | #define INACTIVE 0 // used for 3 states of the idler stepper motor (parked) 96 | #define ACTIVE 1 // not parked 97 | #define QUICKPARKED 2 // quick parked 98 | 99 | 100 | //************************************************************************************ 101 | //* this resets the selector stepper motor after the selected number of tool changes 102 | //* changed from 25 to 10 (10.10.18) 103 | //* chagned from 10 to 8 (10.14.18) 104 | //************************************************************************************* 105 | #define TOOLSYNC 20 // number of tool change (T) commands before a selector resync is performed 106 | 107 | 108 | 109 | #define PINHIGH 10 // how long to hold stepper motor pin high in microseconds 110 | #define PINLOW 10 // how long to hold stepper motor pin low in microseconds 111 | 112 | 113 | 114 | // the MMU2 currently runs at 21mm/sec (set by Slic3r) for 2 seconds (good stuff to know) 115 | // 116 | // the load duration was chagned from 1 second to 1.1 seconds on 10.8.18 (as an experiment) 117 | // increased from 1.1 to 1.5 seconds on 10.13.18 (another experiment) 118 | #define LOAD_DURATION 1600 // duration of 'C' command during the load process (in milliseconds) 119 | 120 | 121 | // changed from 21 mm/sec to 30 mm/sec on 10.13.18 122 | #define LOAD_SPEED 30 // load speed (in mm/second) during the 'C' command (determined by Slic3r setting) 123 | #define INSTRUCTION_DELAY 25 // delay (in microseconds) of the loop 124 | 125 | 126 | 127 | #define IDLERSTEPSIZE 23 // steps to each roller bearing 128 | //float bearingAbsPos[5] = {1, 24, 48, 72, 96}; // absolute position of roller bearing stepper motor 129 | float bearingAbsPos[5] = {0, IDLERSTEPSIZE, IDLERSTEPSIZE * 2, IDLERSTEPSIZE * 3, IDLERSTEPSIZE * 4}; 130 | 131 | 132 | 133 | // changed position #2 to 372 (still tuning this little sucker) 134 | 135 | 136 | #define CSSTEPS 357 // 137 | int selectorAbsPos[5] = {0, CSSTEPS * 1, CSSTEPS * 2, CSSTEPS * 3, CSSTEPS * 4}; // absolute position of selector stepper motor 138 | 139 | 140 | int trackToolChanges = 0; 141 | int extruderMotorStatus = INACTIVE; 142 | 143 | 144 | int currentCSPosition = 0; // color selector position 145 | int currentPosition = 0; 146 | 147 | int repeatTCmdFlag = INACTIVE; // used by the 'C' command processor to avoid processing multiple 'C' commands 148 | 149 | int oldBearingPosition = 0; // this tracks the roller bearing position (top motor on the MMU) 150 | int filamentSelection = 0; // keep track of filament selection (0,1,2,3,4)) 151 | int dummy[100]; 152 | char currentExtruder = '0'; 153 | 154 | int firstTimeFlag = 0; 155 | int earlyCommands = 0; // forcing communications with the mk3 at startup 156 | 157 | int toolChangeCount = 0; 158 | 159 | char receivedChar; 160 | boolean newData = false; 161 | int idlerStatus = INACTIVE; 162 | int colorSelectorStatus = INACTIVE; 163 | 164 | //************************************************************************************************* 165 | // Delay values for each stepper motor 166 | //************************************************************************************************* 167 | #define IDLERMOTORDELAY 540 //540 useconds (idler motor) was at '500' on 10.13.18 168 | #define EXTRUDERMOTORDELAY 50 // 150 useconds (controls filament feed speed to the printer) 169 | #define COLORSELECTORMOTORDELAY 60 // 60 useconds (selector motor) 170 | 171 | 172 | // added this pin as a debug pin (lights a green LED so I can see the 'C0' command in action 173 | #define greenLED 14 174 | 175 | // modified code on 10.2.18 to accomodate RAMPS 1.6 board mapping 176 | // 177 | #define idlerDirPin A7 178 | #define idlerStepPin A6 179 | #define idlerEnablePin A2 180 | 181 | 182 | 183 | #define extruderDirPin 48 // pin 48 for extruder motor direction pin 184 | #define extruderStepPin 46 // pin 48 for extruder motor stepper motor pin 185 | #define extruderEnablePin A8 // pin A8 for extruder motor rst/sleep motor pin 186 | 187 | #define colorSelectorDirPin A1 //color selector stepper motor (driven by trapezoidal screw) 188 | #define colorSelectorStepPin A0 189 | #define colorSelectorEnablePin 38 190 | 191 | 192 | 193 | #define findaPin A3 194 | // this is pin D3 on the arduino MEGA 2650 195 | #define filamentSwitch 3 // this switch was added on 10.1.18 to help with filament loading (X- signal on the RAMPS board) 196 | 197 | 198 | //SoftwareSerial Serial1(10,11); // RX, TX (communicates with the MK3 controller board 199 | 200 | int f0Min = 1000, f1Min = 1000, f2Min = 1000, f3Min = 1000, f4Min = 1000; 201 | int f0Max, f1Max, f2Max, f3Max, f4Max = 0; 202 | int f0Avg, f1Avg, f2Avg, f3Avg, f4Avg; 203 | long f0Distance, f1Distance, f2Distance, f3Distance, f4Distance = 0; // changed from int to long type 10.5.18 204 | int f0ToolChange, f1ToolChange, f2ToolChange, f3ToolChange, f4ToolChange = 0; 205 | 206 | unsigned long time0, time1, time2, time3, time4, time5; 207 | unsigned long timeCStart, timeCEnd; 208 | 209 | 210 | void Application::setup() { 211 | // static int findaStatus; 212 | 213 | int waitCount; 214 | 215 | 216 | Serial.begin(500000); // startup the local serial interface (changed to 2 Mbaud on 10.7.18 217 | while (!Serial) { 218 | ; // wait for serial port to connect. needed for native USB port only 219 | Serial.println(F("waiting for serial port")); 220 | } 221 | 222 | Serial.println(MMU2_VERSION); 223 | 224 | //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! 225 | // THIS DELAY IS CRITICAL DURING POWER UP/RESET TO PROPERLY SYNC WITH THE MK3 CONTROLLER BOARD 226 | //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! 227 | delay(4000); // this is key to syncing to the MK3 controller - currently 4 seconds 228 | 229 | 230 | 231 | 232 | Serial1.begin(115200); // startup the mk3 serial 233 | // Serial1.begin(115200; // ATMEGA hardware serial interface 234 | 235 | //Serial.println(F("started the mk3 serial interface")); 236 | delay(100); 237 | 238 | 239 | Serial.println(F("Sending START command to mk3 controller board")); 240 | // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! 241 | // THIS NEXT COMMAND IS CRITICAL ... IT TELLS THE MK3 controller that an MMU is present 242 | // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! 243 | Serial1.print(F("start\n")); // attempt to tell the mk3 that the mmu is present 244 | 245 | //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! 246 | // check the serial interface to see if it is active 247 | //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! 248 | waitCount = 0; 249 | while (!Serial1.available()) { 250 | 251 | //delay(100); 252 | Serial.println(F("Waiting for message from mk3")); 253 | delay(1000); 254 | ++waitCount; 255 | if (waitCount >= 7) { 256 | Serial.println(F("7 seconds have passed, aborting wait for mk3 to respond")); 257 | goto continue_processing; 258 | } 259 | } 260 | Serial.println(F("inbound message from mk3")); 261 | 262 | continue_processing: 263 | 264 | 265 | 266 | pinMode(idlerDirPin, OUTPUT); 267 | pinMode(idlerStepPin, OUTPUT); 268 | 269 | pinMode(findaPin, INPUT); // pinda Filament sensor 270 | pinMode(filamentSwitch, INPUT); 271 | 272 | pinMode(idlerEnablePin, OUTPUT); 273 | // pinMode(bearingRstPin, OUTPUT); 274 | 275 | pinMode(extruderEnablePin, OUTPUT); 276 | pinMode(extruderDirPin, OUTPUT); 277 | pinMode(extruderStepPin, OUTPUT); 278 | 279 | pinMode(colorSelectorEnablePin, OUTPUT); 280 | pinMode(colorSelectorDirPin, OUTPUT); 281 | pinMode(colorSelectorStepPin, OUTPUT); 282 | 283 | pinMode(greenLED, OUTPUT); // green LED used for debug purposes 284 | 285 | Serial.println(F("finished setting up input and output pins")); 286 | 287 | 288 | 289 | // Turn on all three stepper motors 290 | digitalWrite(idlerEnablePin, ENABLE); // enable the roller bearing motor (motor #1) 291 | digitalWrite(extruderEnablePin, ENABLE); // enable the extruder motor (motor #2) 292 | digitalWrite(colorSelectorEnablePin, ENABLE); // enable the color selector motor (motor #3) 293 | 294 | 295 | 296 | 297 | 298 | // moved these inits to the loop() section since the mk3 serial interface needs to be handled 299 | // 300 | 301 | #ifdef NOTDEF 302 | if (isFilamentLoaded()) { // check to see if filament in the bowden tube (between the mmu2 and mk3 303 | Serial.println(F("Filament was in the bowden tube at startup, unloading filament automatically")); 304 | unloadFilamentToFinda(); // 305 | } 306 | #endif 307 | 308 | Serial.println(F("Syncing the Idler Selector Assembly")); // do this before moving the selector motor 309 | initIdlerPosition(); // reset the roller bearing position 310 | 311 | 312 | Serial.println(F("Syncing the Filament Selector Assembly")); 313 | if (!isFilamentLoaded()) { 314 | initColorSelector(); // reset the color selector if there is NO filament present 315 | } else { 316 | Serial.println(F("Unable to clear the Color Selector, please remove filament")); 317 | } 318 | 319 | Serial.println(F("Inialialization Complete, let's multicolor print ....")); 320 | 321 | 322 | 323 | } // end of init() routine 324 | 325 | 326 | // infinite loop - core of the program 327 | 328 | void Application::loop() { 329 | int i; 330 | char rcvChar; 331 | int pindaStatus; 332 | char c1, c2, c3; 333 | String kbString; 334 | int fstatus; 335 | 336 | 337 | // Serial.println(F("looping")); 338 | delay(100); // wait for 100 milliseconds 339 | checkSerialInterface(); // check the serial interface for input commands from the mk3 340 | #ifdef NOTDEF 341 | while (1) { 342 | fstatus = digitalRead(filamentSwitch); 343 | Serial.print(F("Filament Status: ")); 344 | Serial.println(fstatus); 345 | delay(1000); 346 | } 347 | #endif 348 | 349 | // Serial.println(F("Enter Filament Selection (1-5),Disengage Roller (D), Load Filament (L), Unload Filament (U), Test Color Extruder(T)")); 350 | //Serial.print(F("FINDA status: ")); 351 | //fstatus = digitalRead(findaPin); 352 | //Serial.println(fstatus); 353 | 354 | 355 | // check for keyboard input 356 | 357 | if (Serial.available()) { 358 | Serial.print(F("Key was hit ")); 359 | //c1 = Serial.read(); 360 | //c2 = Serial.read(); 361 | //c3 = Serial.read(); 362 | 363 | kbString = Serial.readString(); 364 | // Serial.print(c1); Serial.print(F(" ")); Serial.println(F("c2")); 365 | Serial.print(kbString); 366 | 367 | if (kbString[0] == 'C') { 368 | //if (c1 == 'C') { 369 | Serial.println(F("Processing 'C' Command")); 370 | filamentLoadWithBondTechGear(); 371 | 372 | //filamentLoadToMK3(); 373 | } 374 | if (kbString[0] == 'T') { 375 | //if (c1 == 'T') { 376 | Serial.println(F("Processing 'T' Command")); 377 | toolChange(kbString[1]); // invoke the tool change command 378 | //toolChange(c2); 379 | // processKeyboardInput(); 380 | } 381 | if (kbString[0] == 'U') { 382 | Serial.println(F("Processing 'U' Command")); 383 | 384 | // parkIdler(); // reset the idler // added on 10.7.18 ... get in known state 385 | 386 | 387 | if (idlerStatus == QUICKPARKED) { 388 | quickUnParkIdler(); // un-park the idler from a quick park 389 | } 390 | if (idlerStatus == INACTIVE) { 391 | unParkIdler(); // turn on the idler motor 392 | } 393 | unloadFilamentToFinda(); //unload the filament 394 | 395 | parkIdler(); // park the idler motor and turn it off 396 | } 397 | } 398 | 399 | 400 | 401 | } // end of infinite loop 402 | 403 | 404 | 405 | // need to check the PINDA status 406 | 407 | void checkSerialInterface() { 408 | unsigned char c1, c2, c3; 409 | int i; 410 | int cnt; 411 | char c; 412 | String inputLine; 413 | int counter = 0; 414 | int findaStatus; 415 | int index; 416 | long steps; 417 | 418 | 419 | // Serial.println(F("Waiting for communication with mk3")); 420 | 421 | // while (earlyCommands == 0) { 422 | // Serial.println(F("waiting for response from mk3")); 423 | index = 0; 424 | if ((cnt = Serial1.available()) > 0) { 425 | 426 | //Serial.print(F("chars received: ")); 427 | //Serial.println(cnt); 428 | 429 | inputLine = Serial1.readString(); // fetch the command from the mmu2 serial input interface 430 | 431 | if (inputLine[0] != 'P') { 432 | Serial.print(F("MMU Command: ")); 433 | Serial.println(inputLine); 434 | } 435 | process_more_commands: // parse the inbound command 436 | c1 = inputLine[index++]; // fetch single characer from the input line 437 | c2 = inputLine[index++]; // fetch 2nd character from the input line 438 | c3 = inputLine[index++]; // carriage return 439 | 440 | 441 | // process commands coming from the mk3 controller 442 | //*********************************************************************************** 443 | // Commands still to be implemented: X0 (MMU Reset), F0 (Filament type select), 444 | // E0->E4 (Eject Filament), R0 (recover from eject) 445 | //*********************************************************************************** 446 | switch (c1) { 447 | case 'T': 448 | // request for idler and selector based on filament number 449 | time4 = millis(); // grab the current time 450 | 451 | if ((c2 >= '0') && (c2 <= '4')) { 452 | toolChange(c2); 453 | 454 | } else { 455 | Serial.println(F("T: Invalid filament Selection")); 456 | } 457 | 458 | // delay(200); //removed this 200msec delay on 10.5.18 459 | Serial1.print(F("ok\n")); // send command acknowledge back to mk3 controller 460 | time5 = millis(); // grab the current time 461 | break; 462 | case 'C': 463 | // move filament from selector ALL the way to printhead 464 | #ifdef NOTDEF 465 | Serial.println(F("C: Moving filament to Bondtech gears")); 466 | #endif 467 | // filamentLoadToMK3(); 468 | filamentLoadWithBondTechGear(); 469 | // delay(200); 470 | Serial1.print(F("ok\n")); 471 | break; 472 | 473 | case 'U': 474 | // request for filament unload 475 | 476 | Serial.println(F("U: Filament Unload Selected")); 477 | //******************************************************************************************************* 478 | //* FIX: don't go all the way to the end ... be smarter 479 | //****************************************************************************************************** 480 | //* unparking is more elegant 10.12.18 481 | if (idlerStatus == QUICKPARKED) { 482 | quickUnParkIdler(); // un-park the idler from a quick park 483 | } 484 | if (idlerStatus == INACTIVE) { 485 | unParkIdler(); // turn on the idler motor 486 | } 487 | 488 | if ((c2 >= '0') && (c2 <= '4')) { 489 | 490 | unloadFilamentToFinda(); 491 | parkIdler(); 492 | Serial.println(F("U: Sending Filament Unload Acknowledge to MK3")); 493 | delay(200); 494 | Serial1.print(F("ok\n")); 495 | 496 | } else { 497 | Serial.println(F("U: Invalid filament Unload Requested")); 498 | delay(200); 499 | Serial1.print(F("ok\n")); 500 | } 501 | break; 502 | case 'L': 503 | // request for filament load 504 | Serial.println(F("L: Filament Load Selected")); 505 | if (idlerStatus == QUICKPARKED) { 506 | quickUnParkIdler(); // un-park the idler from a quick park 507 | } 508 | if (idlerStatus == INACTIVE) { 509 | unParkIdler(); // turn on the idler motor 510 | } 511 | 512 | 513 | if (colorSelectorStatus == INACTIVE) 514 | activateColorSelector(); // turn on the color selector motor 515 | 516 | if ((c2 >= '0') && (c2 <= '4')) { 517 | 518 | Serial.println(F("L: Moving the bearing idler")); 519 | idlerSelector(c2); // move the filament selector stepper motor to the right spot 520 | Serial.println(F("L: Moving the color selector")); 521 | colorSelector(c2); // move the color Selector stepper Motor to the right spot 522 | Serial.println(F("L: Loading the Filament")); 523 | // loadFilament(CCW); 524 | loadFilamentToFinda(); 525 | parkIdler(); // turn off the idler roller 526 | 527 | Serial.println(F("L: Sending Filament Load Acknowledge to MK3")); 528 | 529 | delay(200); 530 | 531 | Serial1.print(F("ok\n")); 532 | 533 | 534 | 535 | } else { 536 | Serial.println(F("Error: Invalid Filament Number Selected")); 537 | } 538 | break; 539 | 540 | case 'S': 541 | // request for firmware version 542 | // Serial.println(F("S Command received from MK3")); 543 | // this is a serious hack since the serial interface is flaky at this point in time 544 | #ifdef NOTDEF 545 | if (command == 1) { 546 | Serial.println(F("S: Processing S2")); 547 | Serial1.print(FW_BUILDNR); 548 | Serial1.print(F("ok\n")); 549 | 550 | command++; 551 | 552 | } 553 | if (command == 0) { 554 | Serial.println(F("S: Processing S1")); 555 | Serial1.print(FW_VERSION); 556 | Serial1.print(F("ok\n")); 557 | 558 | command++; 559 | } 560 | #endif 561 | 562 | switch (c2) { 563 | case '0': 564 | Serial.println(F("S: Sending back OK to MK3")); 565 | Serial1.print(F("ok\n")); 566 | break; 567 | case '1': 568 | Serial.println(F("S: FW Version Request")); 569 | Serial1.print(FW_VERSION); 570 | Serial1.print(F("ok\n")); 571 | break; 572 | case '2': 573 | Serial.println(F("S: Build Number Request")); 574 | Serial.println(F("Initial Communication with MK3 Controller: Successful")); 575 | Serial1.print(FW_BUILDNR); 576 | Serial1.print(F("ok\n")); 577 | break; 578 | default: 579 | Serial.println(F("S: Unable to process S Command")); 580 | break; 581 | } // switch(c2) check 582 | break; 583 | case 'P': 584 | 585 | // check FINDA status 586 | // Serial.println(F("Check FINDA Status Request")); 587 | findaStatus = digitalRead(findaPin); 588 | if (findaStatus == 0) { 589 | // Serial.println(F("P: FINDA INACTIVE")); 590 | Serial1.print(F("0")); 591 | } 592 | else { 593 | // Serial.println(F("P: FINDA ACTIVE")); 594 | Serial1.print(F("1")); 595 | } 596 | Serial1.print(F("ok\n")); 597 | 598 | break; 599 | case 'F': // 'F' command is acknowledged but no processing goes on at the moment 600 | // will be useful for flexible material down the road 601 | Serial.println(F("Filament Type Selected: ")); 602 | Serial.println(c2); 603 | Serial1.print(F("ok\n")); // send back OK to the mk3 604 | break; 605 | default: 606 | Serial.print(F("ERROR: unrecognized command from the MK3 controller")); 607 | Serial1.print(F("ok\n")); 608 | 609 | 610 | } // end of switch statement 611 | #ifdef NOTDEF 612 | if (cnt != 3) { 613 | 614 | Serial.print(F("Index: ")); 615 | Serial.print(index); 616 | Serial.print(F(" cnt: ")); 617 | Serial.println(cnt); 618 | } 619 | #endif 620 | } // end of cnt > 0 check 621 | 622 | if (index < cnt) { 623 | #ifdef NOTDEF 624 | Serial.println(F("More commands in the buffer")); 625 | #endif 626 | 627 | goto process_more_commands; 628 | } 629 | // } // check for early commands 630 | 631 | } 632 | 633 | 634 | void colorSelector(char selection) { 635 | 636 | int findaStatus; 637 | 638 | // this error check was added on 10.4.18 639 | 640 | if ((selection < '0') || (selection > '4')) { 641 | Serial.println(F("colorSelector(): Error, invalid filament selection")); 642 | return; 643 | } 644 | 645 | // Serial.println(F("Entering colorSelector() routine")); 646 | 647 | loop: 648 | findaStatus = digitalRead(findaPin); // check the pinda status ( DO NOT MOVE THE COLOR SELECTOR if filament is present) 649 | if (findaStatus == 1) { 650 | fixTheProblem("colorSelector(): Error, filament is present between the MMU2 and the MK3 Extruder: UNLOAD FILAMENT!!!"); 651 | goto loop; 652 | } 653 | 654 | 655 | 656 | switch (selection) { 657 | case '0': // position '0' is always just a move to the left 658 | // added the '+10' on 10.5.18 (force selector carriage all the way to the left 659 | csTurnAmount(currentPosition + 10, CCW); // the '+10' is an attempt to move the selector ALL the way left (puts the selector into known position) 660 | currentPosition = selectorAbsPos[0]; 661 | break; 662 | case '1': 663 | if (currentPosition <= selectorAbsPos[1]) { 664 | csTurnAmount((selectorAbsPos[1] - currentPosition), CW); 665 | } else { 666 | csTurnAmount((currentPosition - selectorAbsPos[1]), CCW); 667 | } 668 | currentPosition = selectorAbsPos[1]; 669 | break; 670 | case '2': 671 | if (currentPosition <= selectorAbsPos[2]) { 672 | csTurnAmount((selectorAbsPos[2] - currentPosition), CW); 673 | } else { 674 | csTurnAmount((currentPosition - selectorAbsPos[2]), CCW); 675 | 676 | } 677 | currentPosition = selectorAbsPos[2]; 678 | break; 679 | case '3': 680 | if (currentPosition <= selectorAbsPos[3]) { 681 | csTurnAmount((selectorAbsPos[3] - currentPosition), CW); 682 | } else { 683 | csTurnAmount((currentPosition - selectorAbsPos[3]), CCW); 684 | 685 | } 686 | currentPosition = selectorAbsPos[3]; 687 | break; 688 | case '4': 689 | if (currentPosition <= selectorAbsPos[4]) { 690 | csTurnAmount((selectorAbsPos[4] - currentPosition), CW); 691 | } else { 692 | csTurnAmount((currentPosition - selectorAbsPos[4]), CCW); 693 | 694 | } 695 | currentPosition = selectorAbsPos[4]; 696 | break; 697 | 698 | } 699 | 700 | 701 | 702 | } // end of colorSelector routine() 703 | 704 | //**************************************************************************************************** 705 | //* this routine is the common routine called for fixing the filament issues (loading or unloading) 706 | //**************************************************************************************************** 707 | void fixTheProblem(String statement) { 708 | Serial.println(F("")); 709 | Serial.println(F("********************* ERROR ************************")); 710 | Serial.println(statement); // report the error to the user 711 | Serial.println(F("********************* ERROR ************************")); 712 | Serial.println(F("Clear the problem and then hit any key to continue ")); 713 | Serial.println(F("")); 714 | 715 | parkIdler(); // park the idler stepper motor 716 | digitalWrite(colorSelectorEnablePin, DISABLE); // turn off the selector stepper motor 717 | 718 | //quickParkIdler(); // move the idler out of the way 719 | // specialParkIdler(); 720 | 721 | while (!Serial.available()) { 722 | // wait until key is entered to proceed (this is to allow for operator intervention) 723 | } 724 | Serial.readString(); // clear the keyboard buffer 725 | 726 | unParkIdler(); // put the idler stepper motor back to its' original position 727 | digitalWrite(colorSelectorEnablePin, ENABLE); // turn ON the selector stepper motor 728 | delay(1); // wait for 1 millisecond 729 | 730 | //specialUnParkIdler(); 731 | //unParkIdler(); 732 | //quickUnParkIdler(); // re-enage the idler 733 | } 734 | 735 | 736 | // this is the selector motor with the lead screw (final stage of the MMU2 unit) 737 | 738 | void csTurnAmount(int steps, int direction) { 739 | int i; 740 | int scount; 741 | 742 | digitalWrite(colorSelectorEnablePin, ENABLE ); // turn on the color selector motor 743 | // delayMicroseconds(1500); // wait for 1.5 milliseconds added on 10.4.18 744 | 745 | if (direction == CW) 746 | digitalWrite(colorSelectorDirPin, LOW); // set the direction for the Color Extruder Stepper Motor 747 | else 748 | digitalWrite(colorSelectorDirPin, HIGH); 749 | // wait 1 milliseconds 750 | delayMicroseconds(1500); // changed from 500 to 1000 microseconds on 10.6.18, changed to 1500 on 10.7.18) 751 | 752 | #ifdef DEBUG 753 | Serial.print(F("raw steps: ")); 754 | Serial.println(steps); 755 | 756 | scount = steps * STEPSIZE; 757 | Serial.print(F("total number of steps: ")); 758 | Serial.println(scount); 759 | #endif 760 | 761 | for (i = 0; i <= (steps * STEPSIZE); i++) { // fixed this to '<=' from '<' on 10.5.18 762 | digitalWrite(colorSelectorStepPin, HIGH); 763 | delayMicroseconds(PINHIGH); // delay for 10 useconds 764 | digitalWrite(colorSelectorStepPin, LOW); 765 | delayMicroseconds(PINLOW); // delay for 10 useconds (added back in on 10.8.2018) 766 | delayMicroseconds(COLORSELECTORMOTORDELAY); // wait for 400 useconds 767 | 768 | } 769 | 770 | #ifdef TURNOFFSELECTORMOTOR // added on 10.14.18 771 | digitalWrite(colorSelectorEnablePin, DISABLE); // turn off the color selector motor 772 | #endif 773 | 774 | } 775 | 776 | 777 | 778 | 779 | 780 | // test code snippet for moving a stepper motor 781 | // (not used operationally) 782 | void completeRevolution() { 783 | int i, delayValue; 784 | 785 | for (i = 0; i < STEPSPERREVOLUTION * STEPSIZE; i++) { 786 | digitalWrite(idlerStepPin, HIGH); 787 | delayMicroseconds(PINHIGH); // delay for 10 useconds 788 | digitalWrite(idlerStepPin, LOW); 789 | delayMicroseconds(PINLOW); // delay for 10 useconds 790 | 791 | delayMicroseconds(IDLERMOTORDELAY); 792 | //delayValue = 64/stepSize; 793 | //delay(delayValue); // wait for 30 milliseconds 794 | 795 | } 796 | } 797 | 798 | // 799 | // turn the idler stepper motor 800 | // 801 | void idlerturnamount(int steps, int dir) { 802 | int i; 803 | int delayValue; 804 | 805 | 806 | 807 | #ifdef NOTDEF 808 | Serial.println(F("moving the idler ...")); 809 | Serial.print(F("steps: ")); 810 | Serial.print(steps); 811 | Serial.print(F("dir: ")); 812 | Serial.println(dir); 813 | #endif 814 | 815 | digitalWrite(idlerEnablePin, ENABLE); // turn on motor 816 | digitalWrite(idlerDirPin, dir); 817 | delay(1); // wait for 1 millisecond 818 | 819 | // digitalWrite(ledPin, HIGH); 820 | 821 | //digitalWrite(idlerDirPin, dir); 822 | //delay(1); // wait for 1 millsecond 823 | 824 | // these command actually move the IDLER stepper motor 825 | // 826 | for (i = 0; i < steps * STEPSIZE; i++) { 827 | digitalWrite(idlerStepPin, HIGH); 828 | delayMicroseconds(PINHIGH); // delay for 10 useconds 829 | digitalWrite(idlerStepPin, LOW); 830 | //delayMicroseconds(PINLOW); // delay for 10 useconds (removed on 10.7.18 831 | 832 | delayMicroseconds(IDLERMOTORDELAY); 833 | 834 | } 835 | #ifdef NOTDEF 836 | Serial.println(F("finished moving the idler ...")); 837 | #endif 838 | 839 | } // end of idlerturnamount() routine 840 | 841 | 842 | // turns on the extruder motor 843 | void loadFilamentToFinda() { 844 | int i; 845 | int findaStatus; 846 | unsigned int steps; 847 | unsigned long startTime, currentTime; 848 | 849 | digitalWrite(extruderEnablePin, ENABLE); // added on 10.14.18 850 | digitalWrite(extruderDirPin, CCW); // set the direction of the MMU2 extruder motor 851 | delay(1); 852 | 853 | startTime = millis(); 854 | 855 | loop: 856 | currentTime = millis(); 857 | if ((currentTime - startTime) > 10000) { // 10 seconds worth of trying to unload the filament 858 | fixTheProblem("UNLOAD FILAMENT ERROR: timeout error, filament is not unloading past the FINDA sensor"); 859 | startTime = millis(); // reset the start time clock 860 | } 861 | // changed this on 10.12.18 to step 1 mm instead of a single step at a time 862 | 863 | // feedFilament(1); // 1 step and then check the pinda status 864 | feedFilament(STEPSPERMM); // go 144 steps (1 mm) and then check the finda status 865 | 866 | findaStatus = digitalRead(findaPin); 867 | if (findaStatus == 0) // keep feeding the filament until the pinda sensor triggers 868 | goto loop; 869 | 870 | #ifdef NOTDEF 871 | Serial.println(F("Pinda Sensor Triggered during Filament Load")); 872 | #endif 873 | // 874 | // for a filament load ... need to get the filament out of the selector head !!! 875 | // 876 | digitalWrite(extruderDirPin, CW); // back the filament away from the selector 877 | 878 | #ifdef NOTDEF 879 | steps = 200 * STEPSIZE + 50; 880 | feedFilament(steps); 881 | #endif 882 | 883 | feedFilament(STEPSPERMM * 23); // after hitting the FINDA sensor, back away by 23 mm 884 | #ifdef NOTDEF 885 | Serial.println(F("Loading Filament Complete ...")); 886 | #endif 887 | 888 | // digitalWrite(ledPin, LOW); // turn off LED 889 | } 890 | 891 | //********************************************************************************************* 892 | // unload Filament using the FINDA sensor 893 | // turns on the extruder motor 894 | //********************************************************************************************* 895 | void unloadFilamentToFinda() { 896 | int i; 897 | int findaStatus; 898 | unsigned int steps; 899 | unsigned long startTime, currentTime, startTime1; 900 | int fStatus; 901 | 902 | if (!isFilamentLoaded()) { // if the filament is already unloaded, do nothing 903 | 904 | Serial.println(F("unloadFilamentToFinda(): filament already unloaded")); 905 | return; 906 | } 907 | 908 | digitalWrite(extruderEnablePin, ENABLE); // turn on the extruder motor 909 | digitalWrite(extruderDirPin, CW); // set the direction of the MMU2 extruder motor 910 | delay(1); 911 | 912 | startTime = millis(); 913 | startTime1 = millis(); 914 | 915 | loop: 916 | 917 | currentTime = millis(); 918 | 919 | //************************************************************************************************************ 920 | //* added filament sensor status check (10.14.18) 921 | //************************************************************************************************************ 922 | 923 | fStatus = digitalRead(filamentSwitch); // read the filament switch (on the top of the mk3 extruder) 924 | 925 | if (fStatus == 0) { // filament Switch is still ON, check for timeout condition 926 | 927 | if ((currentTime - startTime1) > 2000) { // has 2 seconds gone by ? 928 | fixTheProblem("UNLOAD FILAMENT ERROR: filament not unloading properly, stuck in mk3 head"); 929 | startTime1 = millis(); 930 | } 931 | } else { // check for timeout waiting for FINDA sensor to trigger 932 | 933 | if ((currentTime - startTime) > 10000) { // 10 seconds worth of trying to unload the filament 934 | 935 | fixTheProblem("UNLOAD FILAMENT ERROR: filament is not unloading properly, stuck between mk3 and mmu2"); 936 | startTime = millis(); // reset the start time 937 | } 938 | } 939 | feedFilament(STEPSPERMM); // 1mm and then check the pinda status 940 | 941 | 942 | 943 | 944 | if (isFilamentLoaded()) { // keep unloading until we hit the FINDA sensor 945 | goto loop; 946 | } 947 | 948 | // findaStatus = digitalRead(findaPin); 949 | 950 | // if (findaStatus == 1) // keep feeding the filament until the pinda sensor triggers 951 | 952 | // goto loop; 953 | 954 | #ifdef NOTDEF 955 | Serial.println(F("unloadFilamenttoFinda(): Pinda Sensor Triggered during Filament unload")); 956 | #endif 957 | // 958 | // for a filament unload ... need to get the filament out of the selector head !!! 959 | // 960 | digitalWrite(extruderDirPin, CW); // back the filament away from the selector 961 | 962 | //steps = 200 * STEPSIZE + 50; 963 | //feedFilament(steps); 964 | 965 | feedFilament(STEPSPERMM * 23); // back the filament away from the selector by 23mm 966 | 967 | #ifdef NOTDEF 968 | Serial.println(F("unloadFilamentToFinda(): Unloading Filament Complete ...")); 969 | #endif 970 | 971 | // digitalWrite(ledPin, LOW); // turn off LED 972 | } 973 | 974 | 975 | void loadFilament(int direction) { 976 | int i; 977 | int findaStatus; 978 | unsigned int steps; 979 | 980 | 981 | // digitalWrite(ledPin, HIGH); // turn on LED to indicate extruder motor is running 982 | digitalWrite(extruderDirPin, direction); // set the direction of the MMU2 extruder motor 983 | 984 | 985 | switch (direction) { 986 | case CCW: // load filament 987 | loop: 988 | feedFilament(1); // 1 step and then check the pinda status 989 | 990 | findaStatus = digitalRead(findaPin); 991 | if (findaStatus == 0) // keep feeding the filament until the pinda sensor triggers 992 | goto loop; 993 | Serial.println(F("Pinda Sensor Triggered")); 994 | // now feed the filament ALL the way to the printer extruder assembly 995 | 996 | steps = 17 * 200 * STEPSIZE; 997 | 998 | Serial.print(F("steps: ")); 999 | Serial.println(steps); 1000 | feedFilament(steps); // 17 complete revolutions 1001 | Serial.println(F("Loading Filament Complete ...")); 1002 | break; 1003 | 1004 | case CW: // unload filament 1005 | loop1: 1006 | feedFilament(STEPSPERMM); // 1 mm and then check the pinda status 1007 | findaStatus = digitalRead(findaPin); 1008 | if (findaStatus == 1) // wait for the filament to unload past the pinda sensor 1009 | goto loop1; 1010 | Serial.println(F("Pinda Sensor Triggered, unloading filament complete")); 1011 | 1012 | feedFilament(STEPSPERMM * 23); // move 23mm so we are out of the way of the selector 1013 | 1014 | 1015 | break; 1016 | default: 1017 | Serial.println(F("loadFilament: I shouldn't be here !!!!")); 1018 | 1019 | 1020 | } 1021 | 1022 | 1023 | } 1024 | 1025 | // 1026 | // this routine feeds filament by the amount of steps provided 1027 | // 144 steps = 1mm of filament (using the current mk8 gears in the MMU2) 1028 | // 1029 | void feedFilament(unsigned int steps) { 1030 | 1031 | int i; 1032 | 1033 | #ifdef NOTDEF 1034 | if (steps > 1) { 1035 | Serial.print(F("Steps: ")); 1036 | Serial.println(steps); 1037 | } 1038 | #endif 1039 | 1040 | for (i = 0; i <= steps; i++) { 1041 | digitalWrite(extruderStepPin, HIGH); 1042 | delayMicroseconds(PINHIGH); // delay for 10 useconds 1043 | digitalWrite(extruderStepPin, LOW); 1044 | delayMicroseconds(PINLOW); // delay for 10 useconds 1045 | 1046 | delayMicroseconds(EXTRUDERMOTORDELAY); // wait for 400 useconds 1047 | //delay(delayValue); // wait for 30 milliseconds 1048 | 1049 | } 1050 | } 1051 | 1052 | 1053 | void recoverfilamentSelector() { 1054 | 1055 | } 1056 | 1057 | // this routine drives the 5 position bearings (aka idler) on the top of the MMU2 carriage 1058 | // 1059 | void idlerSelector(char filament) { 1060 | int steps; 1061 | int newBearingPosition; 1062 | int newSetting; 1063 | 1064 | #ifdef DEBUG 1065 | Serial.print(F("idlerSelector(): Filament Selected: ")); 1066 | Serial.println(filament); 1067 | #endif 1068 | 1069 | //* added on 10.14.18 (need to turn the extruder stepper motor back on since it is turned off by parkidler() 1070 | digitalWrite(extruderEnablePin, ENABLE); 1071 | 1072 | 1073 | if ((filament < '0') || (filament > '4')) { 1074 | Serial.println(F("idlerSelector() ERROR, invalid filament selection")); 1075 | Serial.print(F("idlerSelector() filament: ")); 1076 | Serial.println(filament); 1077 | return; 1078 | } 1079 | // move the selector back to it's origin state 1080 | 1081 | #ifdef DEBUG 1082 | Serial.print(F("Old Idler Roller Bearing Position:")); 1083 | Serial.println(oldBearingPosition); 1084 | Serial.println(F("Moving filament selector")); 1085 | #endif 1086 | 1087 | switch (filament) { 1088 | case '0': 1089 | newBearingPosition = bearingAbsPos[0]; // idler set to 1st position 1090 | filamentSelection = 0; 1091 | currentExtruder = '0'; 1092 | break; 1093 | case '1': 1094 | newBearingPosition = bearingAbsPos[1]; 1095 | filamentSelection = 1; 1096 | currentExtruder = '1'; 1097 | break; 1098 | case '2': 1099 | newBearingPosition = bearingAbsPos[2]; 1100 | filamentSelection = 2; 1101 | currentExtruder = '2'; 1102 | break; 1103 | case '3': 1104 | newBearingPosition = bearingAbsPos[3]; 1105 | filamentSelection = 3; 1106 | currentExtruder = '3'; 1107 | break; 1108 | case '4': 1109 | newBearingPosition = bearingAbsPos[4]; 1110 | filamentSelection = 4; 1111 | currentExtruder = '4'; 1112 | break; 1113 | default: 1114 | Serial.println(F("idlerSelector(): ERROR, Invalid Idler Bearing Position")); 1115 | break; 1116 | } 1117 | 1118 | // turnAmount(newFilamentPosition,CCW); // new method 1119 | 1120 | 1121 | newSetting = newBearingPosition - oldBearingPosition; 1122 | 1123 | #ifdef NOTDEF 1124 | Serial.print(F("Old Bearing Position: ")); 1125 | Serial.println(oldBearingPosition); 1126 | Serial.print(F("New Bearing Position: ")); 1127 | Serial.println(newBearingPosition); 1128 | 1129 | Serial.print(F("New Setting: ")); 1130 | Serial.println(newSetting); 1131 | #endif 1132 | 1133 | if (newSetting < 0) { 1134 | idlerturnamount(-newSetting, CW); // turn idler to appropriate position 1135 | } else { 1136 | idlerturnamount(newSetting, CCW); // turn idler to appropriate position 1137 | } 1138 | 1139 | oldBearingPosition = newBearingPosition; 1140 | 1141 | } 1142 | 1143 | 1144 | // perform this function only at power up/reset 1145 | // 1146 | void initIdlerPosition() { 1147 | 1148 | #ifdef NOTDEF 1149 | Serial.println(F("initIdlerPosition(): resetting the Idler Roller Bearing position")); 1150 | #endif 1151 | 1152 | digitalWrite(idlerEnablePin, ENABLE); // turn on the roller bearing motor 1153 | delay(1); 1154 | oldBearingPosition = 125; // points to position #1 1155 | idlerturnamount(MAXROLLERTRAVEL, CW); 1156 | idlerturnamount(MAXROLLERTRAVEL, CCW); // move the bearings out of the way 1157 | digitalWrite(idlerEnablePin, DISABLE); // turn off the idler roller bearing motor 1158 | 1159 | filamentSelection = 0; // keep track of filament selection (0,1,2,3,4)) 1160 | currentExtruder = '0'; 1161 | 1162 | 1163 | } 1164 | 1165 | // perform this function only at power up/reset 1166 | // 1167 | void initColorSelector() { 1168 | 1169 | #ifdef NOTDEF 1170 | Serial.println(F("Syncing the Color Selector Assembly")); 1171 | #endif 1172 | digitalWrite(colorSelectorEnablePin, ENABLE); // turn on the stepper motor 1173 | delay(1); // wait for 1 millisecond 1174 | 1175 | csTurnAmount(MAXSELECTOR_STEPS, CW); // move to the right 1176 | csTurnAmount(MAXSELECTOR_STEPS+20, CCW); // move all the way to the left 1177 | 1178 | digitalWrite(colorSelectorEnablePin, DISABLE); // turn off the stepper motor 1179 | 1180 | } 1181 | 1182 | 1183 | // this function is performed by the 'T' command after so many moves to make sure the colorselector is synchronized 1184 | // 1185 | void syncColorSelector() { 1186 | int moveSteps; 1187 | 1188 | digitalWrite(colorSelectorEnablePin, ENABLE); // turn on the selector stepper motor 1189 | delay(1); // wait for 1 millecond 1190 | 1191 | Serial.print(F("syncColorSelelector() current Filament selection: ")); 1192 | Serial.println(filamentSelection); 1193 | moveSteps = MAXSELECTOR_STEPS - selectorAbsPos[filamentSelection]; 1194 | 1195 | Serial.print(F("syncColorSelector() moveSteps: ")); 1196 | Serial.println(moveSteps); 1197 | 1198 | csTurnAmount(moveSteps, CW); // move all the way to the right 1199 | csTurnAmount(MAXSELECTOR_STEPS+20, CCW); // move all the way to the left 1200 | 1201 | #ifdef TURNOFFSELECTORMOTOR // added on 10.14.18 1202 | digitalWrite(colorSelectorEnablePin, DISABLE); // turn off the selector stepper motor 1203 | #endif 1204 | } 1205 | 1206 | 1207 | // this just energizes the roller bearing extruder motor 1208 | // 1209 | void activateRollers() { 1210 | 1211 | digitalWrite(idlerEnablePin, ENABLE); // turn on the roller bearing stepper motor 1212 | 1213 | // turnAmount(120, CW); // move the rollers to filament position #1 1214 | // oldBearingPosition = 45; // filament position #1 1215 | 1216 | // oldBearingPosition = MAXROLLERTRAVEL; // not sure about this CSK 1217 | 1218 | idlerStatus = ACTIVE; 1219 | } 1220 | 1221 | // move the filament Roller pulleys away from the filament 1222 | 1223 | void parkIdler() { 1224 | int newSetting; 1225 | 1226 | digitalWrite(idlerEnablePin, ENABLE); 1227 | delay(1); 1228 | 1229 | // commented out on 10.13.18 1230 | //oldBearingPosition = bearingAbsPos[filamentSelection]; // fetch the bearing position based on the filament state 1231 | 1232 | #ifdef DEBUGIDLER 1233 | Serial.print(F("parkIdler() oldBearingPosition: ")); 1234 | Serial.print(oldBearingPosition); 1235 | #endif 1236 | #ifdef DEBUG 1237 | Serial.print(F(" filamentSelection: ")); 1238 | Serial.println(filamentSelection); 1239 | #endif 1240 | 1241 | newSetting = MAXROLLERTRAVEL - oldBearingPosition; 1242 | 1243 | #ifdef DEBUG 1244 | Serial.print(F("parkIdler() DeactiveRoller newSetting: ")); 1245 | Serial.println(newSetting); 1246 | #endif 1247 | 1248 | idlerturnamount(newSetting, CCW); // move the bearing roller out of the way 1249 | oldBearingPosition = MAXROLLERTRAVEL; // record the current roller status (CSK) 1250 | 1251 | idlerStatus = INACTIVE; 1252 | digitalWrite(idlerEnablePin, DISABLE); // turn off the roller bearing stepper motor (nice to do, cuts down on CURRENT utilization) 1253 | // added on 10.14.18 1254 | digitalWrite(extruderEnablePin, DISABLE); // turn off the extruder stepper motor as well 1255 | 1256 | } 1257 | 1258 | 1259 | // turn on the idler bearing rollers 1260 | 1261 | void unParkIdler() { 1262 | int rollerSetting; 1263 | 1264 | digitalWrite(idlerEnablePin, ENABLE); // turn on (enable) the roller bearing motor 1265 | // added on 10.14.18 1266 | digitalWrite(extruderEnablePin, ENABLE); // turn on (enable) the extruder stepper motor as well 1267 | 1268 | delay(1); // wait for 10 useconds 1269 | 1270 | //Serial.println(F("Activating the Idler Rollers")); 1271 | 1272 | rollerSetting = MAXROLLERTRAVEL - bearingAbsPos[filamentSelection]; 1273 | //************** added on 10.13.18 1274 | 1275 | oldBearingPosition = bearingAbsPos[filamentSelection]; // update the idler bearing position 1276 | 1277 | 1278 | //Serial.print(F("unParkIdler() Idler Setting: ")); 1279 | //Serial.println(rollerSetting); 1280 | 1281 | idlerturnamount(rollerSetting, CW); // restore the old position 1282 | idlerStatus = ACTIVE; // mark the idler as active 1283 | 1284 | 1285 | } 1286 | 1287 | // attempt to disengage the idler bearing after a 'T' command instead of parking the idler 1288 | // this is trying to save significant time on re-engaging the idler when the 'C' command is activated 1289 | 1290 | void quickParkIdler() { 1291 | int newSetting; 1292 | 1293 | digitalWrite(idlerEnablePin, ENABLE); // turn on the idler stepper motor 1294 | delay(1); 1295 | 1296 | //************************************************************************************************** 1297 | //* this is flawed logic, if I have done a special park idler the oldBearingPosition doesn't map exactly to the filamentSelection 1298 | //* discovered on 10.13.18 1299 | //* In fact, I don't need to update the 'oldBearingPosition' value, it is already VALID 1300 | //******************************************************************************************************************************** 1301 | // oldBearingPosition = bearingAbsPos[filamentSelection]; // fetch the bearing position based on the filament state 1302 | 1303 | 1304 | //newSetting = MAXROLLERTRAVEL - oldBearingPosition; 1305 | //************************************************************************************************* 1306 | //* this is a new approach to moving the idler just a little bit (off the filament) 1307 | //* in preparation for the 'C' Command 1308 | 1309 | //************************************************************************************************* 1310 | #ifdef NOTDEF 1311 | Serial.print(F("quickparkidler(): currentExtruder: ")); 1312 | Serial.println(currentExtruder); 1313 | #endif 1314 | 1315 | //* COMMENTED OUT THIS SECTION OF CODE on 10.13.18 (don't think it is necessary) 1316 | #ifdef CRAZYIVAN 1317 | if (currentExtruder == 4) { 1318 | newSetting = oldBearingPosition - IDLERSTEPSIZE; 1319 | idlerturnamount(IDLERSTEPSIZE, CW); 1320 | } else { 1321 | #endif 1322 | 1323 | newSetting = oldBearingPosition + IDLERSTEPSIZE; // try to move 12 units (just to disengage the roller) 1324 | idlerturnamount(IDLERSTEPSIZE, CCW); 1325 | 1326 | #ifdef CRAZYIVAN 1327 | } 1328 | #endif 1329 | 1330 | //oldBearingPosition = MAXROLLERTRAVEL; // record the current roller status (CSK) 1331 | //************************************************************************************************ 1332 | //* record the idler position 1333 | //* had to be fixed on 10.13.18 1334 | //*********************************************************************************************** 1335 | oldBearingPosition = oldBearingPosition + IDLERSTEPSIZE; // record the current position of the IDLER bearing 1336 | #ifdef NOTDEF 1337 | Serial.print(F("quickparkidler() oldBearingPosition: ")); 1338 | Serial.println(oldBearingPosition); 1339 | #endif 1340 | 1341 | idlerStatus = QUICKPARKED; // use this new state to show the idler is pending the 'C0' command 1342 | 1343 | //********************************************************************************************************* 1344 | //* DO NOT TURN OFF THE IDLER ... needs to be held in position 1345 | //********************************************************************************************************* 1346 | 1347 | //digitalWrite(idlerEnablePin, DISABLE); // turn off the roller bearing stepper motor (nice to do, cuts down on CURRENT utilization) 1348 | 1349 | } 1350 | 1351 | //********************************************************************************************* 1352 | // this routine is called by the 'C' command to re-engage the idler bearing 1353 | //********************************************************************************************* 1354 | void quickUnParkIdler() { 1355 | int rollerSetting; 1356 | 1357 | //********************************************************************************************************* 1358 | //* don't need to turn on the idler ... it is already on (from the 'T' command) 1359 | //********************************************************************************************************* 1360 | 1361 | //digitalWrite(idlerEnablePin, ENABLE); // turn on the roller bearing motor 1362 | //delay(1); // wait for 1 millisecond 1363 | //if (idlerStatus != QUICKPARKED) { 1364 | // Serial.println(F("quickUnParkIdler(): idler already parked")); 1365 | // return; // do nothing since the idler is not 'quick parked' 1366 | //} 1367 | 1368 | #ifdef NOTDEF 1369 | Serial.print(F("quickunparkidler(): currentExtruder: ")); 1370 | Serial.println(currentExtruder); 1371 | #endif 1372 | 1373 | 1374 | // re-enage the idler bearing that was only moved 1 position (for quicker re-engagement) 1375 | // 1376 | #ifdef CRAZYIVAN 1377 | if (currentExtruder == 4) { 1378 | rollerSetting = oldBearingPosition + IDLERSTEPSIZE; 1379 | idlerturnamount(IDLERSTEPSIZE, CCW); 1380 | } else { 1381 | #endif 1382 | 1383 | rollerSetting = oldBearingPosition - IDLERSTEPSIZE; // go back IDLERSTEPSIZE units (hopefully re-enages the bearing 1384 | idlerturnamount(IDLERSTEPSIZE, CW); // restore old position 1385 | 1386 | #ifdef CRAZYIVAN 1387 | } 1388 | #endif 1389 | 1390 | //Serial.print(F("unParkIdler() Idler Setting: ")); 1391 | //Serial.println(rollerSetting); 1392 | 1393 | //************************************************************************************************ 1394 | //* track the absolute position of the idler (changed on 10.13.18 1395 | //*********************************************************************************************** 1396 | Serial.print(F("quickunparkidler(): oldBearingPosition")); 1397 | Serial.println(oldBearingPosition); 1398 | oldBearingPosition = rollerSetting - IDLERSTEPSIZE; // keep track of the idler position 1399 | 1400 | idlerStatus = ACTIVE; // mark the idler as active 1401 | 1402 | 1403 | } 1404 | 1405 | //*************************************************************************************************************** 1406 | //* called by 'C' command to park the idler 1407 | //*************************************************************************************************************** 1408 | void specialParkIdler() { 1409 | int newSetting, idlerSteps; 1410 | 1411 | digitalWrite(idlerEnablePin, ENABLE); // turn on the idler stepper motor 1412 | delay(1); 1413 | 1414 | // oldBearingPosition = bearingAbsPos[filamentSelection]; // fetch the bearing position based on the filament state 1415 | 1416 | //************************************************************************************************* 1417 | //* this is a new approach to moving the idler just a little bit (off the filament) 1418 | //* in preparation for the 'C' Command 1419 | 1420 | //************************************************************************************************* 1421 | if (IDLERSTEPSIZE % 2) { 1422 | idlerSteps = IDLERSTEPSIZE / 2 + 1; // odd number processing, need to round up 1423 | 1424 | } else { 1425 | idlerSteps = IDLERSTEPSIZE / 2; 1426 | 1427 | } 1428 | 1429 | #ifdef NOTDEF 1430 | Serial.print(F("SpecialParkIdler() idlersteps: ")); 1431 | Serial.println(idlerSteps); 1432 | #endif 1433 | 1434 | newSetting = oldBearingPosition + idlerSteps; // try to move 6 units (just to disengage the roller) 1435 | idlerturnamount(idlerSteps, CCW); 1436 | 1437 | //************************************************************************************************ 1438 | //* record the idler position (get back to where we were) 1439 | //*********************************************************************************************** 1440 | oldBearingPosition = oldBearingPosition + idlerSteps; // record the current position of the IDLER bearingT 1441 | 1442 | #ifdef DEBUGIDLER 1443 | Serial.print(F("SpecialParkIdler() oldBearingPosition: ")); 1444 | Serial.println(oldBearingPosition); 1445 | #endif 1446 | 1447 | idlerStatus = QUICKPARKED; // use this new state to show the idler is pending the 'C0' command 1448 | 1449 | //* SPECIAL DEBUG (10.13.18 - evening) 1450 | //* turn off the idler stepper motor 1451 | // digitalWrite(idlerEnablePin, DISABLE); // turn off the roller bearing stepper motor (nice to do, cuts down on CURRENT utilization) 1452 | 1453 | #ifdef NOTDEF 1454 | digitalWrite(extruderEnablePin, DISABLE); 1455 | extruderMotorStatus = INACTIVE; 1456 | #endif 1457 | 1458 | } 1459 | 1460 | //********************************************************************************************* 1461 | // this routine is called by the 'C' command to re-engage the idler bearing 1462 | //********************************************************************************************* 1463 | void specialUnParkIdler() { 1464 | int newSetting, idlerSteps; 1465 | 1466 | // re-enage the idler bearing that was only moved 1 position (for quicker re-engagement) 1467 | // 1468 | if (IDLERSTEPSIZE % 2) { 1469 | idlerSteps = IDLERSTEPSIZE / 2 + 1; // odd number processing, need to round up 1470 | 1471 | } else { 1472 | idlerSteps = IDLERSTEPSIZE / 2; 1473 | } 1474 | 1475 | #ifdef NOTDEF 1476 | Serial.print(F("SpecialUnParkIdler() idlersteps: ")); 1477 | Serial.println(idlerSteps); 1478 | #endif 1479 | 1480 | #ifdef DEBUGIDLER 1481 | Serial.print(F("SpecialUnParkIdler() oldBearingPosition (beginning of routine): ")); 1482 | Serial.println(oldBearingPosition); 1483 | #endif 1484 | 1485 | newSetting = oldBearingPosition - idlerSteps; // go back IDLERSTEPSIZE units (hopefully re-enages the bearing 1486 | idlerturnamount(idlerSteps, CW); // restore old position 1487 | 1488 | // MIGHT BE A BAD IDEA 1489 | oldBearingPosition = oldBearingPosition - idlerSteps; // keep track of the idler position 1490 | 1491 | #ifdef DEBUGIDLER 1492 | Serial.print(F("SpecialUnParkIdler() oldBearingPosition: (end of routine): ")); 1493 | Serial.println(oldBearingPosition); 1494 | #endif 1495 | 1496 | idlerStatus = ACTIVE; // mark the idler as active 1497 | 1498 | 1499 | } 1500 | 1501 | void deActivateColorSelector() { 1502 | int newSetting; 1503 | 1504 | #ifdef TURNOFFSELECTORMOTOR 1505 | digitalWrite(colorSelectorEnablePin, DISABLE); // turn off the color selector stepper motor (nice to do, cuts down on CURRENT utilization) 1506 | delay(1); 1507 | colorSelectorStatus = INACTIVE; 1508 | #endif 1509 | 1510 | } 1511 | 1512 | void activateColorSelector() { 1513 | digitalWrite(colorSelectorEnablePin, ENABLE); 1514 | delay(1); 1515 | colorSelectorStatus = ACTIVE; 1516 | } 1517 | 1518 | 1519 | 1520 | 1521 | void recvOneChar() { 1522 | if (Serial.available() > 0) { 1523 | receivedChar = Serial.read(); 1524 | newData = true; 1525 | } 1526 | } 1527 | 1528 | void showNewData() { 1529 | if (newData == true) { 1530 | Serial.print(F("This just in ... ")); 1531 | Serial.println(receivedChar); 1532 | newData = false; 1533 | } 1534 | } 1535 | 1536 | #ifdef ORIGINALCODE 1537 | 1538 | void processKeyboardInput() { 1539 | 1540 | 1541 | while (newData == false) { 1542 | recvOneChar(); 1543 | } 1544 | 1545 | showNewData(); // character received 1546 | 1547 | Serial.print(F("Filament Selected: ")); 1548 | Serial.println(receivedChar); 1549 | 1550 | switch (receivedChar) { 1551 | case '1': 1552 | case '2': 1553 | case '3': 1554 | case '4': 1555 | case '5': 1556 | if (idlerStatus == INACTIVE) 1557 | activateRollers(); 1558 | 1559 | if (colorSelectorStatus == INACTIVE) 1560 | activateColorSelector(); // turn on the color selector motor 1561 | 1562 | 1563 | idlerSelector(receivedChar); // move the filament selector stepper motor to the right spot 1564 | colorSelector(receivedChar); // move the color Selector stepper Motor to the right spot 1565 | 1566 | break; 1567 | case 'd': // de-active the bearing roller stepper motor and color selector stepper motor 1568 | case 'D': 1569 | parkIdler(); 1570 | deActivateColorSelector(); 1571 | break; 1572 | case 'l': // start the load process for the filament 1573 | case 'L': 1574 | // unParkIdler(); 1575 | if (idlerStatus == INACTIVE) 1576 | unParkIdler(); 1577 | loadFilament(CCW); 1578 | parkIdler(); // move the bearing rollers out of the way after a load is complete 1579 | break; 1580 | case 'u': // unload the filament from the MMU2 device 1581 | case 'U': 1582 | unParkIdler(); // working on this command 1583 | loadFilament(CW); 1584 | parkIdler(); // after the unload of the filament, move the bearing rollers out of the way 1585 | break; 1586 | case 't': 1587 | case 'T': 1588 | csTurnAmount(200, CW); 1589 | delay(1000); 1590 | csTurnAmount(200, CCW); 1591 | break; 1592 | default: 1593 | Serial.println(F("Invalid Serial Output Selection")); 1594 | } // end of switch statement 1595 | } 1596 | #endif 1597 | 1598 | //*********************************************************************************** 1599 | //* this routine is executed as part of the 'T' Command (Load Filament) 1600 | //*********************************************************************************** 1601 | void filamentLoadToMK3() { 1602 | float fsteps; 1603 | unsigned int steps; 1604 | int findaStatus; 1605 | int flag; 1606 | int filamentDistance; 1607 | int fStatus; 1608 | int startTime, currentTime; 1609 | 1610 | 1611 | if ((currentExtruder < '0') || (currentExtruder > '4')) { 1612 | Serial.println(F("filamentLoadToMK3(): fixing current extruder variable")); 1613 | currentExtruder = '0'; 1614 | } 1615 | #ifdef DEBUG 1616 | Serial.println(F("Attempting to move Filament to Print Head Extruder Bondtech Gears")); 1617 | //unParkIdler(); 1618 | Serial.print(F("filamentLoadToMK3(): currentExtruder: ")); 1619 | Serial.println(currentExtruder); 1620 | #endif 1621 | 1622 | // idlerSelector(currentExtruder); // active the idler before the filament load 1623 | 1624 | deActivateColorSelector(); 1625 | 1626 | digitalWrite(extruderEnablePin, ENABLE); // turn on the extruder stepper motor (10.14.18) 1627 | digitalWrite(extruderDirPin, CCW); // set extruder stepper motor to push filament towards the mk3 1628 | delay(1); // wait 1 millisecond 1629 | 1630 | startTime = millis(); 1631 | 1632 | loop: 1633 | // feedFilament(1); // 1 step and then check the pinda status 1634 | feedFilament(STEPSPERMM); // feed 1 mm of filament into the bowden tube 1635 | 1636 | findaStatus = digitalRead(findaPin); // read the FINDA sensor in the MMU2 1637 | currentTime = millis(); 1638 | 1639 | // added this timeout feature on 10.4.18 (2 second timeout) 1640 | if ((currentTime - startTime) > 2000) { 1641 | fixTheProblem("FILAMENT LOAD ERROR: Filament not detected by FINDA sensor, check the selector head in the MMU2"); 1642 | 1643 | startTime = millis(); 1644 | } 1645 | if (findaStatus == 0) // keep feeding the filament until the pinda sensor triggers 1646 | goto loop; 1647 | //*************************************************************************************************** 1648 | //* added additional check (10.10.18) - if the filament switch is already set this might mean there is a switch error or a clog 1649 | //* this error condition can result in 'air printing' 1650 | //*************************************************************************************************************************** 1651 | loop1: 1652 | fStatus = digitalRead(filamentSwitch); 1653 | if (fStatus == 0) { // switch is active (this is not a good condition) 1654 | fixTheProblem("FILAMENT LOAD ERROR: Filament Switch in the MK3 is active (see the RED LED), it is either stuck open or there is debris"); 1655 | goto loop1; 1656 | } 1657 | 1658 | 1659 | 1660 | //Serial.println(F("filamentLoadToMK3(): Pinda Sensor Triggered during Filament Load")); 1661 | // now loading from the FINDA sensor all the way down to the NEW filament sensor 1662 | 1663 | feedFilament(STEPSPERMM * 350); // go 350 mm then look for the 2nd filament sensor 1664 | filamentDistance = 350; 1665 | 1666 | //delay(15000); //wait 15 seconds 1667 | //feedFilament(STEPSPERMM*100); //go 100 more mm 1668 | //delay(15000); 1669 | //goto skipeverything; 1670 | 1671 | startTime = millis(); 1672 | flag = 0; 1673 | //filamentDistance = 0; 1674 | 1675 | // wait until the filament sensor on the mk3 extruder head (microswitch) triggers 1676 | while (flag == 0) { 1677 | 1678 | currentTime = millis(); 1679 | if ((currentTime - startTime) > 8000) { // only wait for 8 seconds 1680 | fixTheProblem("FILAMENT LOAD ERROR: Filament not detected by the MK3 filament sensor, check the bowden tube for clogging/binding"); 1681 | startTime = millis(); // reset the start Time 1682 | 1683 | } 1684 | 1685 | feedFilament(STEPSPERMM); // step forward 1 mm 1686 | filamentDistance++; 1687 | fStatus = digitalRead(filamentSwitch); // read the filament switch on the mk3 extruder 1688 | if (fStatus == 0) { 1689 | // Serial.println(F("filament switch triggered")); 1690 | flag = 1; 1691 | 1692 | Serial.print(F("Filament distance traveled (mm): ")); 1693 | Serial.println(filamentDistance); 1694 | 1695 | switch (filamentSelection) { 1696 | case 0: 1697 | if (filamentDistance < f0Min) { 1698 | f0Min = filamentDistance; 1699 | } 1700 | if (filamentDistance > f0Max) { 1701 | f0Max = filamentDistance; 1702 | } 1703 | f0Distance += filamentDistance; 1704 | f0ToolChange++; 1705 | f0Avg = f0Distance / f0ToolChange; 1706 | break; 1707 | case 1: 1708 | if (filamentDistance < f1Min) { 1709 | f1Min = filamentDistance; 1710 | } 1711 | if (filamentDistance > f1Max) { 1712 | f1Max = filamentDistance; 1713 | } 1714 | f1Distance += filamentDistance; 1715 | f1ToolChange++; 1716 | f1Avg = f1Distance / f1ToolChange; 1717 | break; 1718 | 1719 | case 2: 1720 | if (filamentDistance < f2Min) { 1721 | f2Min = filamentDistance; 1722 | } 1723 | if (filamentDistance > f2Max) { 1724 | f2Max = filamentDistance; 1725 | } 1726 | f2Distance += filamentDistance; 1727 | f2ToolChange++; 1728 | f2Avg = f2Distance / f2ToolChange; 1729 | break; 1730 | case 3: 1731 | if (filamentDistance < f3Min) { 1732 | f3Min = filamentDistance; 1733 | } 1734 | if (filamentDistance > f3Max) { 1735 | f3Max = filamentDistance; 1736 | } 1737 | f3Distance += filamentDistance; 1738 | f3ToolChange++; 1739 | f3Avg = f3Distance / f3ToolChange; 1740 | break; 1741 | case 4: 1742 | if (filamentDistance < f4Min) { 1743 | f4Min = filamentDistance; 1744 | } 1745 | if (filamentDistance > f4Max) { 1746 | f4Max = filamentDistance; 1747 | } 1748 | 1749 | f4Distance += filamentDistance; 1750 | f4ToolChange++; 1751 | f4Avg = f4Distance / f4ToolChange; 1752 | break; 1753 | default: 1754 | Serial.println(F("Error, Invalid Filament Selection")); 1755 | 1756 | } 1757 | // printFilamentStats(); 1758 | 1759 | } 1760 | } 1761 | // feed filament an additional 32 mm to hit the middle of the bondtech gear 1762 | // go an additional 32mm (increased to 32mm on 10.4.18) 1763 | 1764 | feedFilament(STEPSPERMM * 32); 1765 | 1766 | 1767 | 1768 | 1769 | //############################################################################################################################# 1770 | //# NEWEXPERIMENT: removed the parkIdler() command on 10.5.18 to improve timing between 'T' command followng by 'C' command 1771 | //############################################################################################################################# 1772 | // parkIdler(); // park the IDLER (bearing) motor 1773 | 1774 | //delay(200); // removed on 10.5.18 1775 | //Serial1.print(F("ok\n")); // send back acknowledge to the mk3 controller (removed on 10.5.18) 1776 | 1777 | } 1778 | 1779 | void printFilamentStats() { 1780 | Serial.println(F(" ")); 1781 | Serial.print(F("F0 Min: ")); 1782 | Serial.print(f0Min); 1783 | Serial.print(F(" F0 Max: ")); 1784 | Serial.print(f0Max); 1785 | Serial.print(F(" F0 Avg: ")); 1786 | Serial.print(f0Avg); 1787 | Serial.print(F(" F0 Length: ")); 1788 | Serial.print(f0Distance); 1789 | Serial.print(F(" F0 count: ")); 1790 | Serial.println(f0ToolChange); 1791 | 1792 | Serial.print(F("F1 Min: ")); 1793 | Serial.print(f1Min); 1794 | Serial.print(F(" F1 Max: ")); 1795 | Serial.print(f1Max); 1796 | Serial.print(F(" F1 Avg: ")); 1797 | Serial.print(f1Avg); 1798 | Serial.print(F(" F1 Length: ")); 1799 | Serial.print(f1Distance); 1800 | Serial.print(F(" F1 count: ")); 1801 | Serial.println(f1ToolChange); 1802 | 1803 | Serial.print(F("F2 Min: ")); 1804 | Serial.print(f2Min); 1805 | Serial.print(F(" F2 Max: ")); 1806 | Serial.print(f2Max); 1807 | Serial.print(F(" F2 Avg: ")); 1808 | Serial.print(f2Avg); 1809 | Serial.print(F(" F2 Length: ")); 1810 | Serial.print(f2Distance); 1811 | Serial.print(F(" F2 count: ")); 1812 | Serial.println(f2ToolChange); 1813 | 1814 | Serial.print(F("F3 Min: ")); 1815 | Serial.print(f3Min); 1816 | Serial.print(F(" F3 Max: ")); 1817 | Serial.print(f3Max); 1818 | Serial.print(F(" F3 Avg: ")); 1819 | Serial.print(f3Avg); 1820 | Serial.print(F(" F3 Length: ")); 1821 | Serial.print(f3Distance); 1822 | Serial.print(F(" F3 count: ")); 1823 | Serial.println(f3ToolChange); 1824 | 1825 | Serial.print(F("F4 Min: ")); 1826 | Serial.print(f4Min); 1827 | Serial.print(F(" F4 Max: ")); 1828 | Serial.print(f4Max); 1829 | Serial.print(F(" F4 Avg: ")); 1830 | Serial.print(f4Avg); 1831 | Serial.print(F(" F4 Length: ")); 1832 | Serial.print(f4Distance); 1833 | Serial.print(F(" F4 count: ")); 1834 | Serial.println(f4ToolChange); 1835 | 1836 | } 1837 | 1838 | int isFilamentLoaded() { 1839 | int findaStatus; 1840 | 1841 | findaStatus = digitalRead(findaPin); 1842 | return (findaStatus); 1843 | } 1844 | 1845 | // 1846 | // (T) Tool Change Command - this command is the core command used my the mk3 to drive the mmu2 filament selection 1847 | // 1848 | void toolChange( char selection) { 1849 | int newExtruder; 1850 | 1851 | ++toolChangeCount; // count the number of tool changes 1852 | ++trackToolChanges; 1853 | 1854 | //********************************************************************************** 1855 | // * 10.10.18 added an automatic reset of the tracktoolchange counter since going to 1856 | // filament position '0' move the color selection ALL the way to the left 1857 | //********************************************************************************* 1858 | if (selection == '0') { 1859 | // Serial.println(F("toolChange() filament '0' selected: resetting tracktoolchanges counter")); 1860 | trackToolChanges = 0; 1861 | } 1862 | 1863 | Serial.print(F("Tool Change Count: ")); 1864 | Serial.println(toolChangeCount); 1865 | 1866 | 1867 | newExtruder = selection - 0x30; // convert ASCII to a number (0-4) 1868 | 1869 | 1870 | //*********************************************************************************************** 1871 | // code snippet added on 10.8.18 to help the 'C' command processing (happens after 'T' command 1872 | //*********************************************************************************************** 1873 | if (newExtruder == filamentSelection) { // already at the correct filament selection 1874 | 1875 | if (!isFilamentLoaded() ) { // no filament loaded 1876 | 1877 | Serial.println(F("toolChange: filament not currently loaded, loading ...")); 1878 | 1879 | idlerSelector(selection); // move the filament selector stepper motor to the right spot 1880 | colorSelector(selection); // move the color Selector stepper Motor to the right spot 1881 | filamentLoadToMK3(); 1882 | quickParkIdler(); // command moved here on 10.13.18 1883 | //**************************************************************************************** 1884 | //* added on 10.8.18 to help the 'C' command 1885 | //*************************************************************************************** 1886 | repeatTCmdFlag = INACTIVE; // used to help the 'C' command 1887 | //loadFilamentToFinda(); 1888 | } else { 1889 | Serial.println(F("toolChange: filament already loaded to mk3 extruder")); 1890 | //********************************************************************************************* 1891 | //* added on 10.8.18 to help the 'C' Command 1892 | //********************************************************************************************* 1893 | repeatTCmdFlag = ACTIVE; // used to help the 'C' command to not feed the filament again 1894 | } 1895 | 1896 | // else { // added on 9.24.18 to 1897 | // Serial.println(F("Filament already loaded, unloading the filament")); 1898 | // idlerSelector(selection); 1899 | // unloadFilamentToFinda(); 1900 | // } 1901 | 1902 | } else { // different filament position 1903 | //******************************************************************************************** 1904 | //* added on 19.8.18 to help the 'C' Command 1905 | //************************************************************************************************ 1906 | repeatTCmdFlag = INACTIVE; // turn off the repeat Commmand Flag (used by 'C' Command) 1907 | if (isFilamentLoaded()) { 1908 | //************************************************************** 1909 | // added on 10.5.18 to get the idler into the correct state 1910 | // idlerSelector(currentExtruder); 1911 | //************************************************************** 1912 | #ifdef DEBUG 1913 | Serial.println(F("Unloading filament")); 1914 | #endif 1915 | 1916 | idlerSelector(currentExtruder); // point to the current extruder 1917 | 1918 | unloadFilamentToFinda(); // have to unload the filament first 1919 | } 1920 | 1921 | 1922 | 1923 | 1924 | if (trackToolChanges > TOOLSYNC) { // reset the color selector stepper motor (gets out of alignment) 1925 | Serial.println(F("Synchronizing the Filament Selector Head")); 1926 | //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! 1927 | // NOW HAVE A MORE ELEGANT APPROACH - syncColorSelector (and it works) 1928 | // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! 1929 | syncColorSelector(); 1930 | //initColorSelector(); // reset the color selector 1931 | 1932 | 1933 | activateColorSelector(); // turn the color selector motor back on 1934 | currentPosition = 0; // reset the color selector 1935 | 1936 | // colorSelector('0'); // move selector head to position 0 1937 | 1938 | trackToolChanges = 0; 1939 | 1940 | } 1941 | #ifdef DEBUG 1942 | Serial.println(F("Selecting the proper Idler Location")); 1943 | #endif 1944 | idlerSelector(selection); 1945 | #ifdef DEBUG 1946 | Serial.println(F("Selecting the proper Selector Location")); 1947 | #endif 1948 | colorSelector(selection); 1949 | #ifdef DEBUG 1950 | Serial.println(F("Loading Filament: loading the new filament to the mk3")); 1951 | #endif 1952 | 1953 | filamentLoadToMK3(); // moves the idler and loads the filament 1954 | 1955 | 1956 | filamentSelection = newExtruder; 1957 | currentExtruder = selection; 1958 | quickParkIdler(); // command moved here on 10.13.18 1959 | } 1960 | 1961 | //****************************************************************************************** 1962 | //* barely move the idler out of the way 1963 | //* WARNING: THIS MAY NOT WORK PROPERLY ... NEEDS TO BE DEBUGGED (10.7.18) 1964 | //****************************************************************************************** 1965 | // quickParkIdler(); // 10.7.2018 ... attempt to speed up idler for the follow-on 'C' command 1966 | 1967 | //****************************************************************************************** 1968 | //* this was how it was normally done until the above command was attempted 1969 | //****************************************************************************************** 1970 | //parkIdler(); // move the idler away 1971 | 1972 | 1973 | } // end of ToolChange processing 1974 | 1975 | 1976 | // part of the 'C' command, does the last little bit to load into the past the extruder gear 1977 | void filamentLoadWithBondTechGear() { 1978 | int findaStatus; 1979 | long steps; 1980 | int i; 1981 | int delayFactor; // delay factor (in microseconds) for the filament load loop 1982 | int stepCount; 1983 | int tSteps; 1984 | long timeStart, timeEnd, timeUnparking; 1985 | 1986 | timeCStart = millis(); 1987 | 1988 | //***************************************************************************************************************** 1989 | //* added this code snippet to not process a 'C' command that is essentially a repeat command 1990 | 1991 | 1992 | if (repeatTCmdFlag == ACTIVE) { 1993 | Serial.println(F("filamentLoadWithBondTechGear(): filament already loaded and 'C' command already processed")); 1994 | repeatTCmdFlag = INACTIVE; 1995 | return; 1996 | } 1997 | 1998 | 1999 | 2000 | findaStatus = digitalRead(findaPin); 2001 | 2002 | if (findaStatus == 0) { 2003 | Serial.println(F("filamentLoadWithBondTechGear() Error, filament sensor thinks there is no filament")); 2004 | return; 2005 | } 2006 | 2007 | if ((currentExtruder < '0') || (currentExtruder > '4')) { 2008 | Serial.println(F("filamentLoadWithBondTechGear(): fixing current extruder variable")); 2009 | currentExtruder = '0'; 2010 | } 2011 | 2012 | 2013 | //************************************************************************************************* 2014 | //* change of approach to speed up the IDLER engagement 10.7.18 2015 | //* WARNING: THIS APPROACH MAY NOT WORK ... NEEDS TO BE DEBUGGED 2016 | //* C command assumes there is always a T command right before it 2017 | //* (IF 2 'C' commands are issued by the MK3 in a row the code below might be an issue) 2018 | //* 2019 | //************************************************************************************************* 2020 | timeStart = millis(); 2021 | if (idlerStatus == QUICKPARKED) { // make sure idler is in the pending state (set by quickparkidler() routine) 2022 | // Serial.println(F("'C' Command: quickUnParking the Idler")); 2023 | // quickUnParkIdler(); 2024 | #ifdef NOTDEF 2025 | Serial.println(F("filamentLoadWithBondTechGear() calling specialunparkidler() routine")); 2026 | #endif 2027 | specialUnParkIdler(); // PLACEHOLDER attempt to speed up the idler engagement a little more 10.13.18 2028 | } 2029 | if (idlerStatus == INACTIVE) { 2030 | unParkIdler(); 2031 | } 2032 | 2033 | #ifdef NOTDEF 2034 | else { 2035 | Serial.println(F("filamentLoadWithBondTechGear(): looks like I received two 'C' commands in a row")); 2036 | Serial.println(F(" ignoring the 2nd 'C' command")); 2037 | return; 2038 | } 2039 | #endif 2040 | 2041 | 2042 | timeEnd = millis(); 2043 | timeUnparking = timeEnd - timeStart; 2044 | //************************************************************************************************* 2045 | //* following line of code is currently disabled (in order to test out the code above 2046 | //* NOTE: I don't understand why the unParkIdler() command is not used instead ??? 2047 | //************************************************************************************************ 2048 | // idlerSelector(currentExtruder); // move the idler back into position 2049 | 2050 | stepCount = 0; 2051 | time0 = millis(); 2052 | digitalWrite(greenLED, HIGH); // turn on the green LED (for debug purposes) 2053 | //******************************************************************************************* 2054 | // feed the filament from the MMU2 into the bondtech gear for 2 seconds at 10 mm/sec 2055 | // STEPPERMM : 144, 1: duration in seconds, 21: feed rate (in mm/sec) 2056 | // delay: 674 (for 10 mm/sec) 2057 | // delay: 350 (for 21 mm/sec) 2058 | // LOAD_DURATION: 1 second (time to spend with the mmu2 extruder active) 2059 | // LOAD_SPEED: 21 mm/sec (determined by Slic3r settings 2060 | // INSTRUCTION_DELAY: 25 useconds (time to do the instructions in the loop below, excluding the delayFactor) 2061 | // #define LOAD_DURATION 1000 (load duration in milliseconds, currently set to 1 second) 2062 | // #define LOAD_SPEED 21 // load speed (in mm/sec) during the 'C' command (determined by Slic3r setting) 2063 | // #defefine INSTRUCTION_DELAY 25 // delay (in microseconds) of the loop 2064 | 2065 | // ******************************************************************************************* 2066 | // compute the loop delay factor (eventually this will replace the '350' entry in the loop) 2067 | // this computed value is in microseconds of time 2068 | //******************************************************************************************** 2069 | // delayFactor = ((LOAD_DURATION * 1000.0) / (LOAD_SPEED * STEPSPERMM)) - INSTRUCTION_DELAY; // compute the delay factor (in microseconds) 2070 | 2071 | // for (i = 0; i < (STEPSPERMM * 1 * 21); i++) { 2072 | 2073 | tSteps = STEPSPERMM * ((float)LOAD_DURATION / 1000.0) * LOAD_SPEED; // compute the number of steps to take for the given load duration 2074 | delayFactor = (float(LOAD_DURATION * 1000.0) / tSteps) - INSTRUCTION_DELAY; // 2nd attempt at delayFactor algorithm 2075 | 2076 | #ifdef NOTDEF 2077 | Serial.print(F("Tsteps: ")); 2078 | Serial.println(tSteps); 2079 | #endif 2080 | 2081 | for (i = 0; i < tSteps; i++) { 2082 | digitalWrite(extruderStepPin, HIGH); // step the extruder stepper in the MMU2 unit 2083 | delayMicroseconds(PINHIGH); 2084 | digitalWrite(extruderStepPin, LOW); 2085 | //***************************************************************************************************** 2086 | // replace '350' with delayFactor once testing of variable is complete 2087 | //***************************************************************************************************** 2088 | // after further testing, the '350' can be replaced by delayFactor 2089 | delayMicroseconds(delayFactor); // this was calculated in order to arrive at a 10mm/sec feed rate 2090 | ++stepCount; 2091 | } 2092 | digitalWrite(greenLED, LOW); // turn off the green LED (for debug purposes) 2093 | 2094 | time1 = millis(); 2095 | 2096 | 2097 | //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! 2098 | // copied from the MM-control-01/blob/master/motion.cpp routine 2099 | // NO LONGER USED (abandoned in place on 10.7.18) ... came up with a better algorithm (see above) 2100 | // 2101 | //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! 2102 | //*********************************************************************************************************** 2103 | //* THIS CODE WORKS BUT IT LEADS TO SOME GRINDING AT THE MMU2 WHILE THE BONDTECH GEAR IS LOADING THE FILAMENT 2104 | //*********************************************************************************************************** 2105 | #ifdef NOTDEF 2106 | for (i = 0; i <= 320; i++) { 2107 | digitalWrite(extruderStepPin, HIGH); 2108 | delayMicroseconds(PINHIGH); // delay for 10 useconds 2109 | digitalWrite(extruderStepPin, LOW); 2110 | //delayMicroseconds(2600); // originally 2600 2111 | delayMicroseconds(800); // speed up by a factor of 3 2112 | 2113 | } 2114 | for (i = 0; i <= 450; i++) { 2115 | digitalWrite(extruderStepPin, HIGH); 2116 | delayMicroseconds(PINHIGH); // delay for 10 useconds 2117 | digitalWrite(extruderStepPin, LOW); 2118 | // delayMicroseconds(2200); // originally 2200 2119 | delayMicroseconds(800); // speed up by a factor of 3 2120 | } 2121 | #endif 2122 | 2123 | #ifdef DEBUG 2124 | Serial.println(F("C Command: parking the idler")); 2125 | #endif 2126 | //*************************************************************************************************************************** 2127 | //* this disengags the idler pulley after the 'C' command has been exectuted 2128 | //*************************************************************************************************************************** 2129 | // quickParkIdler(); // changed to quickparkidler on 10.12.18 (speed things up a bit) 2130 | 2131 | specialParkIdler(); // PLACEHOLDER (experiment attempted on 10.13.18) 2132 | 2133 | //parkIdler(); // turn OFF the idler rollers when filament is loaded 2134 | 2135 | timeCEnd = millis(); 2136 | //********************************************************************************************* 2137 | //* going back to the fundamental approach with the idler 2138 | //********************************************************************************************* 2139 | parkIdler(); // cleanest way to deal with the idler 2140 | 2141 | 2142 | 2143 | printFilamentStats(); // print current Filament Stats 2144 | 2145 | Serial.print(F("'T' Command processing time (ms): ")); 2146 | Serial.println(time5 - time4); 2147 | Serial.print(F("'C' Command processing time (ms): ")); 2148 | Serial.println(timeCEnd - timeCStart); 2149 | 2150 | #ifdef NOTDEF 2151 | Serial.print(F("Time 'T' Command Received: ")); 2152 | Serial.println(time4); 2153 | Serial.print(F("Time 'T' Command Completed: ")); 2154 | Serial.println(time5); 2155 | #endif 2156 | 2157 | #ifdef NOTDEF 2158 | Serial.print(F("Time 'C' Command Received: ")); 2159 | Serial.println(time3); 2160 | #endif 2161 | 2162 | 2163 | Serial.print(F("Time in Critical Load Loop: ")); 2164 | Serial.println(time1 - time0); 2165 | 2166 | #ifdef NOTDEF 2167 | Serial.print(F("Time at Parking the Idler Complete: ")); 2168 | Serial.println(time2); 2169 | Serial.print(F("Number of commanded steps to the Extruder: ")); 2170 | Serial.println(stepCount); 2171 | Serial.print(F("Computed Delay Factor: ")); 2172 | Serial.println(delayFactor); 2173 | Serial.print(F("Time Unparking: ")); 2174 | Serial.println(timeUnparking); 2175 | #endif 2176 | 2177 | #ifdef DEBUG 2178 | Serial.println(F("filamentLoadToMK3(): Loading Filament to Print Head Complete")); 2179 | #endif 2180 | 2181 | } 2182 | 2183 | Application::Application() 2184 | { 2185 | // nothing to do in the constructor 2186 | } 2187 | -------------------------------------------------------------------------------- /mmu2-diy/application.h: -------------------------------------------------------------------------------- 1 | #ifndef APPLICATION_H 2 | #define APPLICATION_H 3 | 4 | #include 5 | 6 | 7 | class Application 8 | { 9 | public: 10 | Application(); 11 | 12 | void setup(); 13 | void loop(); 14 | }; 15 | 16 | #endif // APPLICATION_H 17 | 18 | -------------------------------------------------------------------------------- /mmu2-diy/mmu2-diy.ino: -------------------------------------------------------------------------------- 1 | 2 | #include "Arduino.h" 3 | #include "application.h" 4 | 5 | Application app; 6 | 7 | void setup(){ 8 | app.setup(); 9 | } 10 | 11 | void loop(){ 12 | app.loop(); 13 | } 14 | -------------------------------------------------------------------------------- /piolib/readme.txt: -------------------------------------------------------------------------------- 1 | 2 | This directory is intended for project specific (private) libraries. 3 | PlatformIO will compile them to static libraries and link them to executable files. 4 | 5 | The source code of each library should be placed in separate directories, like 6 | "lib/private_lib/[here are source files]". 7 | 8 | For example, see the structure of the following two libraries `Foo` and `Bar`: 9 | 10 | |--lib 11 | | | 12 | | |--Bar 13 | | | |--docs 14 | | | |--examples 15 | | | |--src 16 | | | |- Bar.c 17 | | | |- Bar.h 18 | | | |- library.json (optional, custom build options, etc) https://docs.platformio.org/page/librarymanager/config.html 19 | | | 20 | | |--Foo 21 | | | |- Foo.c 22 | | | |- Foo.h 23 | | | 24 | | |- readme.txt --> THIS FILE 25 | | 26 | |- platformio.ini 27 | |--src 28 | |- main.c 29 | 30 | Then in `src/main.c` you should use: 31 | 32 | #include 33 | #include 34 | 35 | // rest H/C/CPP code 36 | 37 | PlatformIO will find your libraries automatically, configure preprocessor's 38 | include paths and build them. 39 | 40 | More information about PlatformIO Library Dependency Finder 41 | - https://docs.platformio.org/page/librarymanager/ldf.html 42 | -------------------------------------------------------------------------------- /platformio.ini: -------------------------------------------------------------------------------- 1 | [platformio] 2 | src_dir = ./mmu2-diy 3 | lib_dir = ./piolib 4 | libdeps_dir = ./piolibdeps 5 | 6 | [env:mmu] 7 | platform = atmelavr 8 | framework = arduino 9 | board = leonardo 10 | # board = megaatmega2560 -------------------------------------------------------------------------------- /stls/MK3 Filament Cover (CSK Design).stl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cskozlowski/mmu2/c589d8849460a2b53dc5e831579b6a80f8a699db/stls/MK3 Filament Cover (CSK Design).stl -------------------------------------------------------------------------------- /stls/MMU2 Selector Cover with modified center hole.stl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cskozlowski/mmu2/c589d8849460a2b53dc5e831579b6a80f8a699db/stls/MMU2 Selector Cover with modified center hole.stl -------------------------------------------------------------------------------- /stls/NEMA17 Motor Holder Offset for MMU2.stl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cskozlowski/mmu2/c589d8849460a2b53dc5e831579b6a80f8a699db/stls/NEMA17 Motor Holder Offset for MMU2.stl --------------------------------------------------------------------------------