├── .gitattributes
├── .gitignore
├── CMakeLists.txt
├── COPYING
├── README.md
├── linuxcnc.c
├── powermax.c
├── sheetcam.c
├── thc.c
└── thc.h
/.gitattributes:
--------------------------------------------------------------------------------
1 | # Auto detect text files and perform LF normalization
2 | * text=auto eol=lf
3 |
4 |
5 | # Custom for Visual Studio
6 | *.cs diff=csharp
7 |
8 | # Standard to msysgit
9 | *.doc diff=astextplain
10 | *.DOC diff=astextplain
11 | *.docx diff=astextplain
12 | *.DOCX diff=astextplain
13 | *.dot diff=astextplain
14 | *.DOT diff=astextplain
15 | *.pdf diff=astextplain
16 | *.PDF diff=astextplain
17 | *.rtf diff=astextplain
18 | *.RTF diff=astextplain
19 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Windows thumbnail cache files
2 | Thumbs.db
3 | ehthumbs.db
4 | ehthumbs_vista.db
5 |
6 | # Folder config file
7 | Desktop.ini
8 |
9 | # Recycle Bin used on file shares
10 | $RECYCLE.BIN/
11 |
12 | # Windows Installer files
13 | *.cab
14 | *.msi
15 | *.msm
16 | *.msp
17 |
18 | # Windows shortcuts
19 | *.lnk
20 |
21 | # Subversion folders
22 |
23 | .svn
24 |
25 | # Build folder
26 |
27 | build
28 |
29 | #
30 | # =========================
31 | # Operating System Files
32 | # =========================
33 |
34 | .vscode/settings.json
35 | .vscode/c_cpp_properties.json
36 |
--------------------------------------------------------------------------------
/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | add_library(plasma INTERFACE)
2 |
3 | target_sources(plasma INTERFACE
4 | ${CMAKE_CURRENT_LIST_DIR}/thc.c
5 | ${CMAKE_CURRENT_LIST_DIR}/linuxcnc.c
6 | ${CMAKE_CURRENT_LIST_DIR}/sheetcam.c
7 | )
8 |
--------------------------------------------------------------------------------
/COPYING:
--------------------------------------------------------------------------------
1 | ------------------------------------------------------------------------------
2 | COPYRIGHT NOTICE FOR grblHAL Plugin_plasma:
3 | ------------------------------------------------------------------------------
4 |
5 | grblHAL - Embedded CNC g-code interpreter and motion-controller
6 |
7 | Copyright (c) 2020-2021 Terje Io
8 |
9 | Grbl is free software: you can redistribute it and/or modify
10 | it under the terms of the GNU General Public License as published by
11 | the Free Software Foundation, either version 3 of the License, or
12 | (at your option) any later version.
13 |
14 | Grbl is distributed in the hope that it will be useful,
15 | but WITHOUT ANY WARRANTY; without even the implied warranty of
16 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 | GNU General Public License for more details.
18 |
19 | You should have received a copy of the GNU General Public License
20 | along with Grbl. If not, see .
21 |
22 | ------------------------------------------------------------------------------
23 | COPYRIGHT NOTICE(S) FOR WORK CONTAINED IN THIS SOFTWARE:
24 | ------------------------------------------------------------------------------
25 |
26 | ------------------------------------------------------------------------------
27 | GNU GPLv3 TERMS AND CONDITIONS REPRODUCED HERE FOR CLARITY:
28 | ------------------------------------------------------------------------------
29 |
30 | GNU GENERAL PUBLIC LICENSE
31 | Version 3, 29 June 2007
32 |
33 | Copyright (C) 2007 Free Software Foundation, Inc.
34 | Everyone is permitted to copy and distribute verbatim copies
35 | of this license document, but changing it is not allowed.
36 |
37 | Preamble
38 |
39 | The GNU General Public License is a free, copyleft license for
40 | software and other kinds of works.
41 |
42 | The licenses for most software and other practical works are designed
43 | to take away your freedom to share and change the works. By contrast,
44 | the GNU General Public License is intended to guarantee your freedom to
45 | share and change all versions of a program--to make sure it remains free
46 | software for all its users. We, the Free Software Foundation, use the
47 | GNU General Public License for most of our software; it applies also to
48 | any other work released this way by its authors. You can apply it to
49 | your programs, too.
50 |
51 | When we speak of free software, we are referring to freedom, not
52 | price. Our General Public Licenses are designed to make sure that you
53 | have the freedom to distribute copies of free software (and charge for
54 | them if you wish), that you receive source code or can get it if you
55 | want it, that you can change the software or use pieces of it in new
56 | free programs, and that you know you can do these things.
57 |
58 | To protect your rights, we need to prevent others from denying you
59 | these rights or asking you to surrender the rights. Therefore, you have
60 | certain responsibilities if you distribute copies of the software, or if
61 | you modify it: responsibilities to respect the freedom of others.
62 |
63 | For example, if you distribute copies of such a program, whether
64 | gratis or for a fee, you must pass on to the recipients the same
65 | freedoms that you received. You must make sure that they, too, receive
66 | or can get the source code. And you must show them these terms so they
67 | know their rights.
68 |
69 | Developers that use the GNU GPL protect your rights with two steps:
70 | (1) assert copyright on the software, and (2) offer you this License
71 | giving you legal permission to copy, distribute and/or modify it.
72 |
73 | For the developers' and authors' protection, the GPL clearly explains
74 | that there is no warranty for this free software. For both users' and
75 | authors' sake, the GPL requires that modified versions be marked as
76 | changed, so that their problems will not be attributed erroneously to
77 | authors of previous versions.
78 |
79 | Some devices are designed to deny users access to install or run
80 | modified versions of the software inside them, although the manufacturer
81 | can do so. This is fundamentally incompatible with the aim of
82 | protecting users' freedom to change the software. The systematic
83 | pattern of such abuse occurs in the area of products for individuals to
84 | use, which is precisely where it is most unacceptable. Therefore, we
85 | have designed this version of the GPL to prohibit the practice for those
86 | products. If such problems arise substantially in other domains, we
87 | stand ready to extend this provision to those domains in future versions
88 | of the GPL, as needed to protect the freedom of users.
89 |
90 | Finally, every program is threatened constantly by software patents.
91 | States should not allow patents to restrict development and use of
92 | software on general-purpose computers, but in those that do, we wish to
93 | avoid the special danger that patents applied to a free program could
94 | make it effectively proprietary. To prevent this, the GPL assures that
95 | patents cannot be used to render the program non-free.
96 |
97 | The precise terms and conditions for copying, distribution and
98 | modification follow.
99 |
100 | TERMS AND CONDITIONS
101 |
102 | 0. Definitions.
103 |
104 | "This License" refers to version 3 of the GNU General Public License.
105 |
106 | "Copyright" also means copyright-like laws that apply to other kinds of
107 | works, such as semiconductor masks.
108 |
109 | "The Program" refers to any copyrightable work licensed under this
110 | License. Each licensee is addressed as "you". "Licensees" and
111 | "recipients" may be individuals or organizations.
112 |
113 | To "modify" a work means to copy from or adapt all or part of the work
114 | in a fashion requiring copyright permission, other than the making of an
115 | exact copy. The resulting work is called a "modified version" of the
116 | earlier work or a work "based on" the earlier work.
117 |
118 | A "covered work" means either the unmodified Program or a work based
119 | on the Program.
120 |
121 | To "propagate" a work means to do anything with it that, without
122 | permission, would make you directly or secondarily liable for
123 | infringement under applicable copyright law, except executing it on a
124 | computer or modifying a private copy. Propagation includes copying,
125 | distribution (with or without modification), making available to the
126 | public, and in some countries other activities as well.
127 |
128 | To "convey" a work means any kind of propagation that enables other
129 | parties to make or receive copies. Mere interaction with a user through
130 | a computer network, with no transfer of a copy, is not conveying.
131 |
132 | An interactive user interface displays "Appropriate Legal Notices"
133 | to the extent that it includes a convenient and prominently visible
134 | feature that (1) displays an appropriate copyright notice, and (2)
135 | tells the user that there is no warranty for the work (except to the
136 | extent that warranties are provided), that licensees may convey the
137 | work under this License, and how to view a copy of this License. If
138 | the interface presents a list of user commands or options, such as a
139 | menu, a prominent item in the list meets this criterion.
140 |
141 | 1. Source Code.
142 |
143 | The "source code" for a work means the preferred form of the work
144 | for making modifications to it. "Object code" means any non-source
145 | form of a work.
146 |
147 | A "Standard Interface" means an interface that either is an official
148 | standard defined by a recognized standards body, or, in the case of
149 | interfaces specified for a particular programming language, one that
150 | is widely used among developers working in that language.
151 |
152 | The "System Libraries" of an executable work include anything, other
153 | than the work as a whole, that (a) is included in the normal form of
154 | packaging a Major Component, but which is not part of that Major
155 | Component, and (b) serves only to enable use of the work with that
156 | Major Component, or to implement a Standard Interface for which an
157 | implementation is available to the public in source code form. A
158 | "Major Component", in this context, means a major essential component
159 | (kernel, window system, and so on) of the specific operating system
160 | (if any) on which the executable work runs, or a compiler used to
161 | produce the work, or an object code interpreter used to run it.
162 |
163 | The "Corresponding Source" for a work in object code form means all
164 | the source code needed to generate, install, and (for an executable
165 | work) run the object code and to modify the work, including scripts to
166 | control those activities. However, it does not include the work's
167 | System Libraries, or general-purpose tools or generally available free
168 | programs which are used unmodified in performing those activities but
169 | which are not part of the work. For example, Corresponding Source
170 | includes interface definition files associated with source files for
171 | the work, and the source code for shared libraries and dynamically
172 | linked subprograms that the work is specifically designed to require,
173 | such as by intimate data communication or control flow between those
174 | subprograms and other parts of the work.
175 |
176 | The Corresponding Source need not include anything that users
177 | can regenerate automatically from other parts of the Corresponding
178 | Source.
179 |
180 | The Corresponding Source for a work in source code form is that
181 | same work.
182 |
183 | 2. Basic Permissions.
184 |
185 | All rights granted under this License are granted for the term of
186 | copyright on the Program, and are irrevocable provided the stated
187 | conditions are met. This License explicitly affirms your unlimited
188 | permission to run the unmodified Program. The output from running a
189 | covered work is covered by this License only if the output, given its
190 | content, constitutes a covered work. This License acknowledges your
191 | rights of fair use or other equivalent, as provided by copyright law.
192 |
193 | You may make, run and propagate covered works that you do not
194 | convey, without conditions so long as your license otherwise remains
195 | in force. You may convey covered works to others for the sole purpose
196 | of having them make modifications exclusively for you, or provide you
197 | with facilities for running those works, provided that you comply with
198 | the terms of this License in conveying all material for which you do
199 | not control copyright. Those thus making or running the covered works
200 | for you must do so exclusively on your behalf, under your direction
201 | and control, on terms that prohibit them from making any copies of
202 | your copyrighted material outside their relationship with you.
203 |
204 | Conveying under any other circumstances is permitted solely under
205 | the conditions stated below. Sublicensing is not allowed; section 10
206 | makes it unnecessary.
207 |
208 | 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
209 |
210 | No covered work shall be deemed part of an effective technological
211 | measure under any applicable law fulfilling obligations under article
212 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or
213 | similar laws prohibiting or restricting circumvention of such
214 | measures.
215 |
216 | When you convey a covered work, you waive any legal power to forbid
217 | circumvention of technological measures to the extent such circumvention
218 | is effected by exercising rights under this License with respect to
219 | the covered work, and you disclaim any intention to limit operation or
220 | modification of the work as a means of enforcing, against the work's
221 | users, your or third parties' legal rights to forbid circumvention of
222 | technological measures.
223 |
224 | 4. Conveying Verbatim Copies.
225 |
226 | You may convey verbatim copies of the Program's source code as you
227 | receive it, in any medium, provided that you conspicuously and
228 | appropriately publish on each copy an appropriate copyright notice;
229 | keep intact all notices stating that this License and any
230 | non-permissive terms added in accord with section 7 apply to the code;
231 | keep intact all notices of the absence of any warranty; and give all
232 | recipients a copy of this License along with the Program.
233 |
234 | You may charge any price or no price for each copy that you convey,
235 | and you may offer support or warranty protection for a fee.
236 |
237 | 5. Conveying Modified Source Versions.
238 |
239 | You may convey a work based on the Program, or the modifications to
240 | produce it from the Program, in the form of source code under the
241 | terms of section 4, provided that you also meet all of these conditions:
242 |
243 | a) The work must carry prominent notices stating that you modified
244 | it, and giving a relevant date.
245 |
246 | b) The work must carry prominent notices stating that it is
247 | released under this License and any conditions added under section
248 | 7. This requirement modifies the requirement in section 4 to
249 | "keep intact all notices".
250 |
251 | c) You must license the entire work, as a whole, under this
252 | License to anyone who comes into possession of a copy. This
253 | License will therefore apply, along with any applicable section 7
254 | additional terms, to the whole of the work, and all its parts,
255 | regardless of how they are packaged. This License gives no
256 | permission to license the work in any other way, but it does not
257 | invalidate such permission if you have separately received it.
258 |
259 | d) If the work has interactive user interfaces, each must display
260 | Appropriate Legal Notices; however, if the Program has interactive
261 | interfaces that do not display Appropriate Legal Notices, your
262 | work need not make them do so.
263 |
264 | A compilation of a covered work with other separate and independent
265 | works, which are not by their nature extensions of the covered work,
266 | and which are not combined with it such as to form a larger program,
267 | in or on a volume of a storage or distribution medium, is called an
268 | "aggregate" if the compilation and its resulting copyright are not
269 | used to limit the access or legal rights of the compilation's users
270 | beyond what the individual works permit. Inclusion of a covered work
271 | in an aggregate does not cause this License to apply to the other
272 | parts of the aggregate.
273 |
274 | 6. Conveying Non-Source Forms.
275 |
276 | You may convey a covered work in object code form under the terms
277 | of sections 4 and 5, provided that you also convey the
278 | machine-readable Corresponding Source under the terms of this License,
279 | in one of these ways:
280 |
281 | a) Convey the object code in, or embodied in, a physical product
282 | (including a physical distribution medium), accompanied by the
283 | Corresponding Source fixed on a durable physical medium
284 | customarily used for software interchange.
285 |
286 | b) Convey the object code in, or embodied in, a physical product
287 | (including a physical distribution medium), accompanied by a
288 | written offer, valid for at least three years and valid for as
289 | long as you offer spare parts or customer support for that product
290 | model, to give anyone who possesses the object code either (1) a
291 | copy of the Corresponding Source for all the software in the
292 | product that is covered by this License, on a durable physical
293 | medium customarily used for software interchange, for a price no
294 | more than your reasonable cost of physically performing this
295 | conveying of source, or (2) access to copy the
296 | Corresponding Source from a network server at no charge.
297 |
298 | c) Convey individual copies of the object code with a copy of the
299 | written offer to provide the Corresponding Source. This
300 | alternative is allowed only occasionally and noncommercially, and
301 | only if you received the object code with such an offer, in accord
302 | with subsection 6b.
303 |
304 | d) Convey the object code by offering access from a designated
305 | place (gratis or for a charge), and offer equivalent access to the
306 | Corresponding Source in the same way through the same place at no
307 | further charge. You need not require recipients to copy the
308 | Corresponding Source along with the object code. If the place to
309 | copy the object code is a network server, the Corresponding Source
310 | may be on a different server (operated by you or a third party)
311 | that supports equivalent copying facilities, provided you maintain
312 | clear directions next to the object code saying where to find the
313 | Corresponding Source. Regardless of what server hosts the
314 | Corresponding Source, you remain obligated to ensure that it is
315 | available for as long as needed to satisfy these requirements.
316 |
317 | e) Convey the object code using peer-to-peer transmission, provided
318 | you inform other peers where the object code and Corresponding
319 | Source of the work are being offered to the general public at no
320 | charge under subsection 6d.
321 |
322 | A separable portion of the object code, whose source code is excluded
323 | from the Corresponding Source as a System Library, need not be
324 | included in conveying the object code work.
325 |
326 | A "User Product" is either (1) a "consumer product", which means any
327 | tangible personal property which is normally used for personal, family,
328 | or household purposes, or (2) anything designed or sold for incorporation
329 | into a dwelling. In determining whether a product is a consumer product,
330 | doubtful cases shall be resolved in favor of coverage. For a particular
331 | product received by a particular user, "normally used" refers to a
332 | typical or common use of that class of product, regardless of the status
333 | of the particular user or of the way in which the particular user
334 | actually uses, or expects or is expected to use, the product. A product
335 | is a consumer product regardless of whether the product has substantial
336 | commercial, industrial or non-consumer uses, unless such uses represent
337 | the only significant mode of use of the product.
338 |
339 | "Installation Information" for a User Product means any methods,
340 | procedures, authorization keys, or other information required to install
341 | and execute modified versions of a covered work in that User Product from
342 | a modified version of its Corresponding Source. The information must
343 | suffice to ensure that the continued functioning of the modified object
344 | code is in no case prevented or interfered with solely because
345 | modification has been made.
346 |
347 | If you convey an object code work under this section in, or with, or
348 | specifically for use in, a User Product, and the conveying occurs as
349 | part of a transaction in which the right of possession and use of the
350 | User Product is transferred to the recipient in perpetuity or for a
351 | fixed term (regardless of how the transaction is characterized), the
352 | Corresponding Source conveyed under this section must be accompanied
353 | by the Installation Information. But this requirement does not apply
354 | if neither you nor any third party retains the ability to install
355 | modified object code on the User Product (for example, the work has
356 | been installed in ROM).
357 |
358 | The requirement to provide Installation Information does not include a
359 | requirement to continue to provide support service, warranty, or updates
360 | for a work that has been modified or installed by the recipient, or for
361 | the User Product in which it has been modified or installed. Access to a
362 | network may be denied when the modification itself materially and
363 | adversely affects the operation of the network or violates the rules and
364 | protocols for communication across the network.
365 |
366 | Corresponding Source conveyed, and Installation Information provided,
367 | in accord with this section must be in a format that is publicly
368 | documented (and with an implementation available to the public in
369 | source code form), and must require no special password or key for
370 | unpacking, reading or copying.
371 |
372 | 7. Additional Terms.
373 |
374 | "Additional permissions" are terms that supplement the terms of this
375 | License by making exceptions from one or more of its conditions.
376 | Additional permissions that are applicable to the entire Program shall
377 | be treated as though they were included in this License, to the extent
378 | that they are valid under applicable law. If additional permissions
379 | apply only to part of the Program, that part may be used separately
380 | under those permissions, but the entire Program remains governed by
381 | this License without regard to the additional permissions.
382 |
383 | When you convey a copy of a covered work, you may at your option
384 | remove any additional permissions from that copy, or from any part of
385 | it. (Additional permissions may be written to require their own
386 | removal in certain cases when you modify the work.) You may place
387 | additional permissions on material, added by you to a covered work,
388 | for which you have or can give appropriate copyright permission.
389 |
390 | Notwithstanding any other provision of this License, for material you
391 | add to a covered work, you may (if authorized by the copyright holders of
392 | that material) supplement the terms of this License with terms:
393 |
394 | a) Disclaiming warranty or limiting liability differently from the
395 | terms of sections 15 and 16 of this License; or
396 |
397 | b) Requiring preservation of specified reasonable legal notices or
398 | author attributions in that material or in the Appropriate Legal
399 | Notices displayed by works containing it; or
400 |
401 | c) Prohibiting misrepresentation of the origin of that material, or
402 | requiring that modified versions of such material be marked in
403 | reasonable ways as different from the original version; or
404 |
405 | d) Limiting the use for publicity purposes of names of licensors or
406 | authors of the material; or
407 |
408 | e) Declining to grant rights under trademark law for use of some
409 | trade names, trademarks, or service marks; or
410 |
411 | f) Requiring indemnification of licensors and authors of that
412 | material by anyone who conveys the material (or modified versions of
413 | it) with contractual assumptions of liability to the recipient, for
414 | any liability that these contractual assumptions directly impose on
415 | those licensors and authors.
416 |
417 | All other non-permissive additional terms are considered "further
418 | restrictions" within the meaning of section 10. If the Program as you
419 | received it, or any part of it, contains a notice stating that it is
420 | governed by this License along with a term that is a further
421 | restriction, you may remove that term. If a license document contains
422 | a further restriction but permits relicensing or conveying under this
423 | License, you may add to a covered work material governed by the terms
424 | of that license document, provided that the further restriction does
425 | not survive such relicensing or conveying.
426 |
427 | If you add terms to a covered work in accord with this section, you
428 | must place, in the relevant source files, a statement of the
429 | additional terms that apply to those files, or a notice indicating
430 | where to find the applicable terms.
431 |
432 | Additional terms, permissive or non-permissive, may be stated in the
433 | form of a separately written license, or stated as exceptions;
434 | the above requirements apply either way.
435 |
436 | 8. Termination.
437 |
438 | You may not propagate or modify a covered work except as expressly
439 | provided under this License. Any attempt otherwise to propagate or
440 | modify it is void, and will automatically terminate your rights under
441 | this License (including any patent licenses granted under the third
442 | paragraph of section 11).
443 |
444 | However, if you cease all violation of this License, then your
445 | license from a particular copyright holder is reinstated (a)
446 | provisionally, unless and until the copyright holder explicitly and
447 | finally terminates your license, and (b) permanently, if the copyright
448 | holder fails to notify you of the violation by some reasonable means
449 | prior to 60 days after the cessation.
450 |
451 | Moreover, your license from a particular copyright holder is
452 | reinstated permanently if the copyright holder notifies you of the
453 | violation by some reasonable means, this is the first time you have
454 | received notice of violation of this License (for any work) from that
455 | copyright holder, and you cure the violation prior to 30 days after
456 | your receipt of the notice.
457 |
458 | Termination of your rights under this section does not terminate the
459 | licenses of parties who have received copies or rights from you under
460 | this License. If your rights have been terminated and not permanently
461 | reinstated, you do not qualify to receive new licenses for the same
462 | material under section 10.
463 |
464 | 9. Acceptance Not Required for Having Copies.
465 |
466 | You are not required to accept this License in order to receive or
467 | run a copy of the Program. Ancillary propagation of a covered work
468 | occurring solely as a consequence of using peer-to-peer transmission
469 | to receive a copy likewise does not require acceptance. However,
470 | nothing other than this License grants you permission to propagate or
471 | modify any covered work. These actions infringe copyright if you do
472 | not accept this License. Therefore, by modifying or propagating a
473 | covered work, you indicate your acceptance of this License to do so.
474 |
475 | 10. Automatic Licensing of Downstream Recipients.
476 |
477 | Each time you convey a covered work, the recipient automatically
478 | receives a license from the original licensors, to run, modify and
479 | propagate that work, subject to this License. You are not responsible
480 | for enforcing compliance by third parties with this License.
481 |
482 | An "entity transaction" is a transaction transferring control of an
483 | organization, or substantially all assets of one, or subdividing an
484 | organization, or merging organizations. If propagation of a covered
485 | work results from an entity transaction, each party to that
486 | transaction who receives a copy of the work also receives whatever
487 | licenses to the work the party's predecessor in interest had or could
488 | give under the previous paragraph, plus a right to possession of the
489 | Corresponding Source of the work from the predecessor in interest, if
490 | the predecessor has it or can get it with reasonable efforts.
491 |
492 | You may not impose any further restrictions on the exercise of the
493 | rights granted or affirmed under this License. For example, you may
494 | not impose a license fee, royalty, or other charge for exercise of
495 | rights granted under this License, and you may not initiate litigation
496 | (including a cross-claim or counterclaim in a lawsuit) alleging that
497 | any patent claim is infringed by making, using, selling, offering for
498 | sale, or importing the Program or any portion of it.
499 |
500 | 11. Patents.
501 |
502 | A "contributor" is a copyright holder who authorizes use under this
503 | License of the Program or a work on which the Program is based. The
504 | work thus licensed is called the contributor's "contributor version".
505 |
506 | A contributor's "essential patent claims" are all patent claims
507 | owned or controlled by the contributor, whether already acquired or
508 | hereafter acquired, that would be infringed by some manner, permitted
509 | by this License, of making, using, or selling its contributor version,
510 | but do not include claims that would be infringed only as a
511 | consequence of further modification of the contributor version. For
512 | purposes of this definition, "control" includes the right to grant
513 | patent sublicenses in a manner consistent with the requirements of
514 | this License.
515 |
516 | Each contributor grants you a non-exclusive, worldwide, royalty-free
517 | patent license under the contributor's essential patent claims, to
518 | make, use, sell, offer for sale, import and otherwise run, modify and
519 | propagate the contents of its contributor version.
520 |
521 | In the following three paragraphs, a "patent license" is any express
522 | agreement or commitment, however denominated, not to enforce a patent
523 | (such as an express permission to practice a patent or covenant not to
524 | sue for patent infringement). To "grant" such a patent license to a
525 | party means to make such an agreement or commitment not to enforce a
526 | patent against the party.
527 |
528 | If you convey a covered work, knowingly relying on a patent license,
529 | and the Corresponding Source of the work is not available for anyone
530 | to copy, free of charge and under the terms of this License, through a
531 | publicly available network server or other readily accessible means,
532 | then you must either (1) cause the Corresponding Source to be so
533 | available, or (2) arrange to deprive yourself of the benefit of the
534 | patent license for this particular work, or (3) arrange, in a manner
535 | consistent with the requirements of this License, to extend the patent
536 | license to downstream recipients. "Knowingly relying" means you have
537 | actual knowledge that, but for the patent license, your conveying the
538 | covered work in a country, or your recipient's use of the covered work
539 | in a country, would infringe one or more identifiable patents in that
540 | country that you have reason to believe are valid.
541 |
542 | If, pursuant to or in connection with a single transaction or
543 | arrangement, you convey, or propagate by procuring conveyance of, a
544 | covered work, and grant a patent license to some of the parties
545 | receiving the covered work authorizing them to use, propagate, modify
546 | or convey a specific copy of the covered work, then the patent license
547 | you grant is automatically extended to all recipients of the covered
548 | work and works based on it.
549 |
550 | A patent license is "discriminatory" if it does not include within
551 | the scope of its coverage, prohibits the exercise of, or is
552 | conditioned on the non-exercise of one or more of the rights that are
553 | specifically granted under this License. You may not convey a covered
554 | work if you are a party to an arrangement with a third party that is
555 | in the business of distributing software, under which you make payment
556 | to the third party based on the extent of your activity of conveying
557 | the work, and under which the third party grants, to any of the
558 | parties who would receive the covered work from you, a discriminatory
559 | patent license (a) in connection with copies of the covered work
560 | conveyed by you (or copies made from those copies), or (b) primarily
561 | for and in connection with specific products or compilations that
562 | contain the covered work, unless you entered into that arrangement,
563 | or that patent license was granted, prior to 28 March 2007.
564 |
565 | Nothing in this License shall be construed as excluding or limiting
566 | any implied license or other defenses to infringement that may
567 | otherwise be available to you under applicable patent law.
568 |
569 | 12. No Surrender of Others' Freedom.
570 |
571 | If conditions are imposed on you (whether by court order, agreement or
572 | otherwise) that contradict the conditions of this License, they do not
573 | excuse you from the conditions of this License. If you cannot convey a
574 | covered work so as to satisfy simultaneously your obligations under this
575 | License and any other pertinent obligations, then as a consequence you may
576 | not convey it at all. For example, if you agree to terms that obligate you
577 | to collect a royalty for further conveying from those to whom you convey
578 | the Program, the only way you could satisfy both those terms and this
579 | License would be to refrain entirely from conveying the Program.
580 |
581 | 13. Use with the GNU Affero General Public License.
582 |
583 | Notwithstanding any other provision of this License, you have
584 | permission to link or combine any covered work with a work licensed
585 | under version 3 of the GNU Affero General Public License into a single
586 | combined work, and to convey the resulting work. The terms of this
587 | License will continue to apply to the part which is the covered work,
588 | but the special requirements of the GNU Affero General Public License,
589 | section 13, concerning interaction through a network will apply to the
590 | combination as such.
591 |
592 | 14. Revised Versions of this License.
593 |
594 | The Free Software Foundation may publish revised and/or new versions of
595 | the GNU General Public License from time to time. Such new versions will
596 | be similar in spirit to the present version, but may differ in detail to
597 | address new problems or concerns.
598 |
599 | Each version is given a distinguishing version number. If the
600 | Program specifies that a certain numbered version of the GNU General
601 | Public License "or any later version" applies to it, you have the
602 | option of following the terms and conditions either of that numbered
603 | version or of any later version published by the Free Software
604 | Foundation. If the Program does not specify a version number of the
605 | GNU General Public License, you may choose any version ever published
606 | by the Free Software Foundation.
607 |
608 | If the Program specifies that a proxy can decide which future
609 | versions of the GNU General Public License can be used, that proxy's
610 | public statement of acceptance of a version permanently authorizes you
611 | to choose that version for the Program.
612 |
613 | Later license versions may give you additional or different
614 | permissions. However, no additional obligations are imposed on any
615 | author or copyright holder as a result of your choosing to follow a
616 | later version.
617 |
618 | 15. Disclaimer of Warranty.
619 |
620 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
621 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
622 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
623 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
624 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
625 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
626 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
627 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
628 |
629 | 16. Limitation of Liability.
630 |
631 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
632 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
633 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
634 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
635 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
636 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
637 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
638 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
639 | SUCH DAMAGES.
640 |
641 | 17. Interpretation of Sections 15 and 16.
642 |
643 | If the disclaimer of warranty and limitation of liability provided
644 | above cannot be given local legal effect according to their terms,
645 | reviewing courts shall apply local law that most closely approximates
646 | an absolute waiver of all civil liability in connection with the
647 | Program, unless a warranty or assumption of liability accompanies a
648 | copy of the Program in return for a fee.
649 |
650 | END OF TERMS AND CONDITIONS
651 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ## Plasma/THC
2 |
3 | Under development. Based on [LinuxCNC specification](http://linuxcnc.org/docs/2.8/html/plasma/plasmac-user-guide.html#config-panel), with limitations.
4 |
5 | #### $350 - Mode of operation
6 |
7 | | Mode | Description |
8 | |------|-------------|
9 | | 0 | Uses an external arc voltage input to calculate both Arc Voltage (for Torch Height Control) and Arc OK.|
10 | | 1 | Uses an external arc voltage input to calculate Arc Voltage (for Torch Height Control).
Uses an external Arc OK input for Arc OK.|
11 | | 2 | Uses an external Arc OK input for Arc OK.
Use external up/down signals for Torch Height Control.|
12 |
13 | #### THC
14 |
15 | | Setting | Modes | Description |
16 | |----------------------------|-------|-------------|
17 | | $351 - Delay | 0,1,2 | This sets the delay (in seconds) measured from the time the Arc OK signal is received until Torch Height Controller (THC) activates.|
18 | | $352 - Threshold \(V\) | 0,1,2 | This sets the voltage variation allowed from the target voltage before for THC makes movements to correct the torch height.|
19 | | $353 - P Gain | - | This sets the Proportional gain for the THC PID loop.
This roughly equates to how quickly the THC attempts to correct changes in height. |
20 | | $354 - I Gain | - | This sets the Integral gain for the THC PID loop.
Integral gain is associated with the sum of errors in the system over time and is not always needed.|
21 | | $355 - D Gain | - | This sets the Derivative gain for the THC PID loop.
Derivative gain works to dampen the system and reduce over correction oscillations and is not always needed.|
22 | | $356 - VAD Threshold \(%\) | - | \(Velocity Anti Dive\) This sets the percentage of the current cut feed rate the machine can slow to before locking the THC to prevent torch dive.|
23 | | $357 - Void Override \(%\) | - | This sets the size of the change in cut voltage necessary to lock the THC to prevent torch dive \(higher values need greater voltage change to lock THC\)|
24 |
25 | #### ARC
26 |
27 | | Setting | Modes | Description |
28 | |------------------------|-------|-------------|
29 | | $358 - Fail Timeout | 0,1,2 | This sets the amount of time (in seconds) PlasmaC will wait between commanding a "Torch On"
and receiving an Arc OK signal before timing out and displaying an error message.|
30 | | $359 - Retry Delay | 0,1,2 | This sets the time (in seconds) between an arc failure and another arc start attempt.
31 | | $360 - Max Retries | 0,1,2 | This sets the number of times PlasmaC will attempt to start the arc.|
32 | | $361 - Voltage Scale | - | This sets the arc voltage input scale and is used to display the correct arc voltage.|
33 | | $362 - Voltage Offset | - | This sets the arc voltage offset and is used to display zero volts when there is zero arc voltage input.|
34 | | $363 - Height Per Volt | - | This sets the distance the torch would need to move to change the arc voltage by one volt.
Used for manual height manipulation only.|
35 | | $364 - Ok High Voltage | - | This sets the voltage threshold below which Arc OK signal is valid.|
36 | | $365 - Ok Low Voltage | - | This sets the voltage threshold above which the Arc OK signal is valid.|
37 |
38 | #### Auxiliary I/O
39 |
40 | | Setting | Description |
41 | |-------------------------|-------------|
42 | | $366 - Arc voltage port | This sets which analog input port to use for the arc voltage signal. Set to -1 to not use any.1 |
43 | | $367 - Arc ok port | This sets which digital input port to use for the arc ok signal. Set to -1 to not use any.2 |
44 | | $368 - Cutter down port | This sets which digital input port to use for the cutter down signal. Set to -1 to not use any. |
45 | | $369 - Cutter up port | This sets which digital input port to use for the cutter up signal. Set to -1 to not use any. |
46 |
47 | 1 This pin/port is required to enable voltage controlled THC.
48 | 2 This pin/port is required to enable the plugin.
49 |
50 | Tip: use the `$PINS` command to list available pins. The port number is the number following the "_Aux in_" text, an example: `[PIN:P3.2,Aux in 0,P0]`.
51 |
52 | #### $674 - Plugin options
53 |
54 | | Bit | Value |Description |
55 | |-----|-------|------------|
56 | | 0 | 1 | Enable virtual ports. |
57 | | 1 | 2 | Sync Z position. Update the Z position when THC control ends. |
58 |
59 | Add the _Value_ fields for the functionality to enable to get the one to use for the setting.
60 |
61 | > [!NOTE]
62 | > Virtual ports will shadow any real ports with the same port number. Some dummy ports may also be added.
63 |
64 | #### Virtual ports
65 |
66 | Virtual ports are controlled by regular M-Codes.
67 |
68 | * `M62 P2` will disable THC \(Synchronized with Motion\)
69 |
70 | * `M63 P2` will enable THC \(Synchronized with Motion\)
71 |
72 | * `M64 P2` will disable THC \(Immediately\)
73 |
74 | * `M65 P2` will enable THC \(Immediately\)
75 |
76 | * `M67 E3 Q-` Velocity Reduction \(Immediately\)
77 |
78 | * `M68 E3 Q-` Velocity Reduction \(Synchronized with Motion)
79 |
80 | The Q-word for M67 and M68 is the percentage of the programmed feed rate the actual feed rate will be changed to.
81 |
82 | The minimum percentage allowed is 10%, values below this will be set to 10%.
83 | The maximum percentage allowed is 100%, values above this will be set to 100%.
84 |
85 | #### Materials
86 |
87 | The plugin can load and partially make use of LinuxCNC and/or SheetCam style material files. Loading is from either a SD card or from root mounted littlefs file system.
88 |
89 | The following files are currently loaded from if present:
90 | * LinuxCNC: _/linuxcnc/material.cfg_
91 | * SheetCam: _/sheetcam/default.tools_
92 |
93 | Additionally materials can be modified or added by LinuxCNC style _magic_ [gcode comments](https://linuxcnc.org/docs/html/plasma/qtplasmac.html#plasma:magic-comments).
94 |
95 | To select the material to use `M190P` where `` is the material number.
96 | If NGC parameter support is enabled in the controller the feedrate from the selected material can be set by adding `F#<_hal[plasmac.cut-feed-rate]>` to the gcode file.
97 |
98 | Currently loaded materials can be output to the sender console with the `$EM` command, the output is in a machine readable format.
99 |
100 | > [!NOTE]
101 | > Settings updated via _magic_ comments are currently _not_ written back to the material file.
102 |
103 | #### Dependencies:
104 |
105 | Driver must support a number of auxiliary I/O ports, at least one digital input for the arc ok signal.
106 | Some drivers support the MCP3221 I2C ADC, when enabled it can be used for the arc voltage signal.
107 |
108 | #### Credits:
109 |
110 | LinuxCNC documentation linked to above.
111 |
112 | ---
113 | 2025-02-23
114 |
--------------------------------------------------------------------------------
/linuxcnc.c:
--------------------------------------------------------------------------------
1 | /*
2 |
3 | linuxcnc.c - plasma cutter tool height control plugin
4 |
5 | Part of grblHAL
6 |
7 | Copyright (c) 2025 Terje Io
8 |
9 | grblHAL is free software: you can redistribute it and/or modify
10 | it under the terms of the GNU General Public License as published by
11 | the Free Software Foundation, either version 3 of the License, or
12 | (at your option) any later version.
13 |
14 | grblHAL is distributed in the hope that it will be useful,
15 | but WITHOUT ANY WARRANTY; without even the implied warranty of
16 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 | GNU General Public License for more details.
18 |
19 | You should have received a copy of the GNU General Public License
20 | along with grblHAL. If not, see .
21 |
22 | */
23 |
24 | #include "thc.h"
25 |
26 | #if PLASMA_ENABLE
27 |
28 | #include
29 | #include
30 | #include
31 | #include
32 | #include
33 |
34 | #include "grbl/vfs.h"
35 | #include "grbl/strutils.h"
36 |
37 | static on_vfs_mount_ptr on_vfs_mount;
38 |
39 | static void load_tools (const char *path, const vfs_t *fs, vfs_st_mode_t mode)
40 | {
41 | // NOTE: must match layout of material_t
42 | static const char params[] = "PIERCE_HEIGHT,PIERCE_DELAY,CUT_SPEED,CUT_HEIGHT,CUT_VOLTS,PAUSE_AT_END,KERF_WIDTH,CUT_AMPS,GAS_PRESSURE,CUT_MODE,PUDDLE_JUMP_HEIGHT,PUDDLE_JUMP_DELAY,-,NAME,THC";
43 |
44 | char c, *eq, buf[100];
45 | uint_fast8_t idx = 0;
46 | vfs_file_t *file;
47 | status_code_t status = Status_GcodeUnusedWords;
48 |
49 | material_t material;
50 |
51 | if((file = vfs_open("/linuxcnc/material.cfg", "r"))) {
52 |
53 | while(vfs_read(&c, 1, 1, file) == 1) {
54 |
55 | if(c == ASCII_CR || c == ASCII_LF) {
56 |
57 | buf[idx] = '\0';
58 |
59 | if(*buf == '[') {
60 |
61 | if(status == Status_OK && plasma_material_is_valid(&material))
62 | plasma_material_add(&material, true);
63 |
64 | status = Status_GcodeUnusedWords;
65 |
66 | if((eq = strchr(buf, ']'))) {
67 |
68 | *eq = '\0';
69 |
70 | if((eq = strrchr(buf, '_'))) {
71 | uint32_t id;
72 | uint_fast8_t cc = 1;
73 | if((status = read_uint(eq, &cc, &id)) == Status_OK) {
74 | material.id = (int32_t)id;
75 | material.thc_status = true; //?
76 | *material.name = '\0';
77 | for(idx = 0; idx < sizeof(material.params) / sizeof(float); idx++)
78 | material.params[idx] = NAN;
79 | }
80 | }
81 | }
82 | }
83 |
84 | if(status == Status_OK && (eq = strchr(buf, '='))) {
85 |
86 | int32_t p;
87 |
88 | *eq++ = '\0';
89 |
90 | while(*eq == ' ')
91 | eq++;
92 |
93 | switch((p = strlookup(strtok(buf, " "), params, ','))) {
94 |
95 | case -1:
96 | status = Status_GcodeUnsupportedCommand;
97 | break;
98 |
99 | case 13:
100 | strncpy(material.name, eq, sizeof(material.name) - 1);
101 | material.name[sizeof(material.name) - 1] = '\0';
102 | break;
103 |
104 | case 14:
105 | material.thc_status = *eq != '0';
106 | break;
107 |
108 | default:
109 | {
110 | uint_fast8_t cc = 0;
111 | if(!read_float(eq, &cc, &material.params[p]))
112 | status = Status_BadNumberFormat;
113 | }
114 | break;
115 | }
116 | }
117 | idx = 0;
118 | } else if(idx < sizeof(buf))
119 | buf[idx++] = c;
120 | }
121 |
122 | if(status == Status_OK && plasma_material_is_valid(&material))
123 | plasma_material_add(&material, true);
124 |
125 | vfs_close(file);
126 | }
127 |
128 | if(on_vfs_mount)
129 | on_vfs_mount(path, fs, mode);
130 | }
131 |
132 | void linuxcnc_init (void)
133 | {
134 | on_vfs_mount = vfs.on_mount;
135 | vfs.on_mount = load_tools;
136 | }
137 |
138 | #endif // PLASMA_ENABLE
139 |
--------------------------------------------------------------------------------
/powermax.c:
--------------------------------------------------------------------------------
1 | /*
2 |
3 | powermax.c - PowerMax plasma cutter RS-485 communication
4 |
5 | Part of grblHAL
6 |
7 | Copyright (c) 2025 Terje Io
8 |
9 | grblHAL is free software: you can redistribute it and/or modify
10 | it under the terms of the GNU General Public License as published by
11 | the Free Software Foundation, either version 3 of the License, or
12 | (at your option) any later version.
13 |
14 | grblHAL is distributed in the hope that it will be useful,
15 | but WITHOUT ANY WARRANTY; without even the implied warranty of
16 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 | GNU General Public License for more details.
18 |
19 | You should have received a copy of the GNU General Public License
20 | along with grblHAL. If not, see .
21 |
22 | */
23 |
24 | #include "thc.h"
25 |
26 | #if PLASMA_ENABLE && 0
27 |
28 | #include
29 | #include
30 |
31 | #include "../spindle/modbus_rtu.h"
32 |
33 | static uint32_t modbus_address = 1;
34 |
35 | static void rx_packet (modbus_message_t *msg);
36 | static void rx_exception (uint8_t code, void *context);
37 |
38 | static const modbus_callbacks_t callbacks = {
39 | .retries = 5,
40 | .retry_delay = 50,
41 | .on_rx_packet = rx_packet,
42 | .on_rx_exception = rx_exception
43 | };
44 |
45 | static bool set_cut_mode (uint16_t mode)
46 | {
47 | modbus_message_t rpm_cmd = {
48 | .context = (void *)Plasma_SetMode,
49 | .crc_check = false,
50 | .adu[0] = modbus_address,
51 | .adu[1] = ModBus_WriteRegister,
52 | .adu[2] = 0x20,
53 | .adu[3] = 0x93,
54 | .adu[4] = mode >> 8,
55 | .adu[5] = mode & 0xFF,
56 | .tx_length = 8,
57 | .rx_length = 8
58 | };
59 |
60 | return modbus_send(&rpm_cmd, &callbacks, true);
61 | }
62 |
63 | static bool set_amperage (float a)
64 | {
65 | uint16_t amps = (uint16_t)(a * 64);
66 |
67 | modbus_message_t rpm_cmd = {
68 | .context = (void *)Plasma_SetCurrent,
69 | .crc_check = false,
70 | .adu[0] = modbus_address,
71 | .adu[1] = ModBus_WriteRegister,
72 | .adu[2] = 0x20,
73 | .adu[3] = 0x94,
74 | .adu[4] = amps >> 8,
75 | .adu[5] = amps & 0xFF,
76 | .tx_length = 8,
77 | .rx_length = 8
78 | };
79 |
80 | return modbus_send(&rpm_cmd, &callbacks, true);
81 | }
82 |
83 | static bool set_gas_pressure (float psi)
84 | {
85 | uint16_t pressure = (uint16_t)(psi * 128);
86 |
87 | modbus_message_t mode_cmd = {
88 | .context = (void *)Plasma_SetPressure,
89 | .crc_check = false,
90 | .adu[0] = modbus_address,
91 | .adu[1] = ModBus_WriteRegister,
92 | .adu[2] = 0x20,
93 | .adu[3] = 0x96,
94 | .adu[4] = pressure >> 8,
95 | .adu[5] = pressure & 0xFF,
96 | .tx_length = 8,
97 | .rx_length = 8
98 | };
99 |
100 | return modbus_send(&mode_cmd, &callbacks, true);
101 | }
102 |
103 | static void rx_packet (modbus_message_t *msg)
104 | {
105 | if(!(msg->adu[0] & 0x80)) {
106 | /*
107 | switch((vfd_response_t)msg->context) {
108 |
109 | case VFD_GetRPM:
110 | spindle_validate_at_speed(spindle_data, f2rpm((msg->adu[3] << 8) | msg->adu[4]));
111 | break;
112 |
113 | case VFD_GetMinRPM:
114 | freq_min = (msg->adu[3] << 8) | msg->adu[4];
115 | break;
116 |
117 | case VFD_GetMaxRPM:
118 | freq_max = (msg->adu[3] << 8) | msg->adu[4];
119 | spindle_hal->cap.rpm_range_locked = On;
120 | spindle_hal->rpm_min = f2rpm(freq_min);
121 | spindle_hal->rpm_max = f2rpm(freq_max);
122 | break;
123 |
124 | default:
125 | break;
126 | }
127 | */
128 | }
129 | }
130 |
131 | static void rx_exception (uint8_t code, void *context)
132 | {
133 | // raise alarm?
134 | }
135 |
136 | /*
137 | static void onReportOptions (bool newopt)
138 | {
139 | on_report_options(newopt);
140 |
141 | if(!newopt)
142 | report_plugin("PowerMax RS-485", "0.01");
143 | }
144 | */
145 |
146 | void powermax_init (void)
147 | {
148 | cutter.set_cut_mode = set_cut_mode;
149 | cutter.set_current = set_amperage;
150 | cutter.set_pressure = set_gas_pressure;
151 | }
152 |
153 | #endif
154 |
--------------------------------------------------------------------------------
/sheetcam.c:
--------------------------------------------------------------------------------
1 | /*
2 |
3 | sheetcam.c - plasma cutter tool height control plugin
4 |
5 | Part of grblHAL
6 |
7 | Copyright (c) 2025 Terje Io
8 |
9 | grblHAL is free software: you can redistribute it and/or modify
10 | it under the terms of the GNU General Public License as published by
11 | the Free Software Foundation, either version 3 of the License, or
12 | (at your option) any later version.
13 |
14 | grblHAL is distributed in the hope that it will be useful,
15 | but WITHOUT ANY WARRANTY; without even the implied warranty of
16 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 | GNU General Public License for more details.
18 |
19 | You should have received a copy of the GNU General Public License
20 | along with grblHAL. If not, see .
21 |
22 | */
23 |
24 | #include "thc.h"
25 |
26 | #if PLASMA_ENABLE
27 |
28 | #include
29 | #include
30 | #include
31 | #include
32 | #include
33 |
34 | #include "grbl/vfs.h"
35 | #include "grbl/strutils.h"
36 |
37 | static on_vfs_mount_ptr on_vfs_mount;
38 |
39 | static void load_tools (const char *path, const vfs_t *fs, vfs_st_mode_t mode)
40 | {
41 | static const char params[] = "Pierce\\ height,Pierce\\ delay,Feed\\ rate,Cut\\ height,cv,Pause\\ at\\ end\\ of\\ cut,Kerf\\ width,ca,gp,cm,jh,jd,Tool\\ number,Name,th"; // NOTE: must match layout of material_t
42 |
43 | char c, *eq, buf[50];
44 | uint_fast8_t idx = 0;
45 | vfs_file_t *file;
46 | status_code_t status = Status_GcodeUnusedWords;
47 |
48 | material_t material;
49 |
50 | if((file = vfs_open("/sheetcam/default.tools", "r"))) {
51 |
52 | while(vfs_read(&c, 1, 1, file) == 1) {
53 |
54 | if(c == ASCII_CR || c == ASCII_LF) {
55 | buf[idx] = '\0';
56 | if(*buf) {
57 | if(*buf == '[') {
58 |
59 | if(status == Status_OK && plasma_material_is_valid(&material))
60 | plasma_material_add(&material, true);
61 |
62 | material.id = -1;
63 | *material.name = '\0';
64 | status = Status_GcodeUnusedWords;
65 |
66 | for(idx = 0; idx < sizeof(material.params) / sizeof(float); idx++)
67 | material.params[idx] = NAN;
68 | }
69 | }
70 |
71 | if((eq = strchr(buf, '='))) {
72 |
73 | int32_t p;
74 |
75 | *eq = '\0';
76 |
77 | switch((p = strlookup(buf, params, ','))) {
78 |
79 | case -1:
80 | break;
81 |
82 | case 12:
83 | {
84 | uint32_t id;
85 | uint_fast8_t cc = 1;
86 | if((status = read_uint(eq, &cc, &id)) == Status_OK)
87 | material.id = (int32_t)id;
88 | }
89 | break;
90 |
91 | case 13:
92 | strncpy(material.name, eq + 1, sizeof(material.name) - 1);
93 | break;
94 |
95 | case 14:
96 | material.thc_status = eq[1] != '0';
97 | break;
98 |
99 | default:
100 | {
101 | uint_fast8_t cc = 1;
102 | if(!read_float(eq, &cc, &material.params[p]))
103 | status = Status_BadNumberFormat;
104 | }
105 | break;
106 | }
107 | }
108 | idx = 0;
109 | } else if(idx < sizeof(buf))
110 | buf[idx++] = c;
111 | }
112 |
113 | if(status == Status_OK && plasma_material_is_valid(&material))
114 | plasma_material_add(&material, true);
115 |
116 | vfs_close(file);
117 | }
118 |
119 | if(on_vfs_mount)
120 | on_vfs_mount(path, fs, mode);
121 | }
122 |
123 | void sheetcam_init (void)
124 | {
125 | on_vfs_mount = vfs.on_mount;
126 | vfs.on_mount = load_tools;
127 | }
128 |
129 | #endif // PLASMA_ENABLE
130 |
--------------------------------------------------------------------------------
/thc.c:
--------------------------------------------------------------------------------
1 | /*
2 |
3 | thc.c - plasma cutter tool height control plugin
4 |
5 | Part of grblHAL
6 |
7 | Copyright (c) 2020-2025 Terje Io
8 |
9 | grblHAL is free software: you can redistribute it and/or modify
10 | it under the terms of the GNU General Public License as published by
11 | the Free Software Foundation, either version 3 of the License, or
12 | (at your option) any later version.
13 |
14 | grblHAL is distributed in the hope that it will be useful,
15 | but WITHOUT ANY WARRANTY; without even the implied warranty of
16 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 | GNU General Public License for more details.
18 |
19 | You should have received a copy of the GNU General Public License
20 | along with grblHAL. If not, see .
21 |
22 | */
23 |
24 | #include "thc.h"
25 |
26 | #if PLASMA_ENABLE
27 |
28 | #include
29 | #include
30 | #include
31 | #include
32 | #include
33 |
34 | #include "grbl/config.h"
35 | #include "grbl/hal.h"
36 | #include "grbl/protocol.h"
37 | #include "grbl/report.h"
38 | #include "grbl/pid.h"
39 | #include "grbl/nvs_buffer.h"
40 | #include "grbl/stepper2.h"
41 | #include "grbl/state_machine.h"
42 | #include "grbl/strutils.h"
43 | #include "grbl/motion_control.h"
44 | #include "grbl/task.h"
45 | #if NGC_EXPRESSIONS_ENABLE
46 | #include "grbl/ngc_expr.h"
47 | #endif
48 |
49 | extern void linuxcnc_init (void);
50 | extern void sheetcam_init (void);
51 |
52 | #define THC_SAMPLE_AVG 5
53 | #define PLASMA_TMP_MATERIAL_ID_START 1000000
54 | // Digital ports
55 | #define PLASMA_THC_DISABLE_PORT 2 // output
56 | #define PLASMA_TORCH_DISABLE_PORT 3 // output
57 | // Analog ports
58 | #define PLASMA_FEED_OVERRIDE_PORT 3
59 |
60 | typedef enum {
61 | Plasma_ModeOff = 0,
62 | Plasma_ModeVoltage,
63 | Plasma_ModeUpDown,
64 | Plasma_ModeArcOK
65 | } plasma_mode_t;
66 |
67 | // float parameters must be in the same order as in material_t.
68 | typedef struct {
69 | int32_t id;
70 | bool thc_disabled;
71 | union {
72 | float params[6];
73 | struct {
74 | float pierce_height;
75 | float pierce_delay;
76 | float feed_rate;
77 | float cut_height;
78 | float cut_voltage;
79 | float pause_at_end;
80 | };
81 | };
82 | } plasma_job_t;
83 |
84 | typedef union {
85 | uint8_t flags;
86 | struct {
87 | uint8_t virtual_ports :1,
88 | sync_pos :1,
89 | onoffmode :1,
90 | unassigned :5;
91 | };
92 | } thc_options_t;
93 |
94 | typedef struct {
95 | float thc_delay;
96 | float thc_threshold;
97 | uint32_t vad_threshold;
98 | uint32_t thc_override;
99 | float pierce_height;
100 | float pierce_delay;
101 | float pause_at_end;
102 | float arc_retry_delay;
103 | float arc_fail_timeout;
104 | float arc_voltage_scale;
105 | float arc_voltage_offset;
106 | float arc_height_per_volt;
107 | float arc_ok_low_voltage;
108 | float arc_high_low_voltage;
109 | uint8_t arc_retries;
110 | thc_options_t option;
111 | uint8_t unused1;
112 | uint8_t unused2;
113 | plasma_mode_t mode;
114 | pid_values_t pid;
115 | uint8_t port_arc_voltage;
116 | uint8_t port_arc_ok;
117 | uint8_t port_cutter_down;
118 | uint8_t port_cutter_up;
119 | } plasma_settings_t;
120 |
121 | typedef union {
122 | uint16_t bits;
123 | uint16_t value;
124 | struct {
125 | uint16_t arc_ok :1,
126 | torch_on :1,
127 | enabled :1,
128 | ohmic_probe :1,
129 | float_switch :1,
130 | breakaway :1,
131 | active :1,
132 | up :1,
133 | down :1,
134 | velocity_lock :1,
135 | void_lock :1,
136 | report_up :1,
137 | report_down :1,
138 | unassigned :3;
139 | };
140 | } thc_signals_t;
141 |
142 | static void state_idle (void);
143 | static void state_thc_delay (void);
144 | static void state_thc_pid (void);
145 | static void state_thc_adjust (void);
146 | static void state_arc_monitor (void);
147 | static void state_vad_lock (void);
148 |
149 | plasma_control_t cutter = {};
150 |
151 | static bool set_feed_override = false, updown_enabled = false, init_ok = false;
152 | static uint8_t n_ain, n_din;
153 | static uint8_t port_arc_ok, port_arc_voltage;
154 | static uint_fast8_t feed_override, segment_id = 0;
155 | static uint32_t thc_delay = 0, v_count = 0;
156 | static int32_t step_count;
157 | static char max_aport[4], max_dport[4];
158 | static float arc_vref = 0.0f, arc_voltage = 0.0f, arc_voltage_low, arc_voltage_high; //, vad_threshold;
159 | static float fr_pgm, fr_actual, fr_thr_99, fr_thr_vad;
160 | static thc_signals_t thc = {0};
161 | static pidf_t pid;
162 | static nvs_address_t nvs_address;
163 | static char thc_modes[] = "Off,Voltage,Up/down,Arc ok";
164 | static plasma_settings_t plasma;
165 | static st2_motor_t *z_motor;
166 | static void (*volatile stateHandler)(void) = state_idle;
167 | static plasma_mode_t mode = Plasma_ModeOff;
168 | static xbar_t arc_ok, cutter_down, cutter_up, parc_voltage;
169 | static uint32_t material_id = PLASMA_TMP_MATERIAL_ID_START;
170 | static material_t *materials = NULL, tmp_material = { .id = -1 };
171 | static plasma_job_t job = {};
172 |
173 | static settings_changed_ptr settings_changed;
174 | static driver_reset_ptr driver_reset = NULL;
175 | static spindle_set_state_ptr spindle_set_state_ = NULL;
176 | //static control_signals_callback_ptr control_interrupt_callback = NULL;
177 | static stepper_pulse_start_ptr stepper_pulse_start = NULL;
178 | static enumerate_pins_ptr enumerate_pins;
179 | static on_report_options_ptr on_report_options;
180 | static on_spindle_selected_ptr on_spindle_selected;
181 | static on_execute_realtime_ptr on_execute_realtime = NULL;
182 | static on_realtime_report_ptr on_realtime_report = NULL;
183 | static on_gcode_message_ptr on_gcode_comment;
184 | static user_mcode_ptrs_t user_mcode;
185 |
186 | static const char params[] = "ph,pd,fr,ch,cv,pe,kw,ca,gp,cm,jh,jd,nu,na,th"; // NOTE: must match layout of material_t
187 |
188 | // --- Virtual ports start
189 |
190 | static io_port_t rport = {0};
191 | static io_ports_data_t digital, analog;
192 |
193 | typedef struct {
194 | uint8_t pin_id;
195 | pin_info_ptr pin_info;
196 | void *data;
197 | bool aux_dout0;
198 | bool aux_dout1;
199 | bool aux_aout0;
200 | bool aux_aout1;
201 | bool aux_aout2;
202 | } pin_stat_t;
203 |
204 | static void enum_trap (xbar_t *pin, void *data)
205 | {
206 | ((pin_stat_t *)data)->pin_id = max(((pin_stat_t *)data)->pin_id, pin->id);
207 |
208 | if(pin->function == Output_Aux0)
209 | ((pin_stat_t *)data)->aux_dout0 = true;
210 | else if(pin->function == Output_Aux1)
211 | ((pin_stat_t *)data)->aux_dout1 = true;
212 | else if(pin->function == Output_Analog_Aux0)
213 | ((pin_stat_t *)data)->aux_aout0 = true;
214 | else if(pin->function == Output_Analog_Aux1)
215 | ((pin_stat_t *)data)->aux_aout1 = true;
216 | else if(pin->function == Output_Analog_Aux2)
217 | ((pin_stat_t *)data)->aux_aout2 = true;
218 |
219 | if((pin->function == Output_Aux2 || pin->function == Output_Aux3 || pin->function == Output_Analog_Aux3))
220 | pin->description = "Shadowed by THC";
221 |
222 | if(((pin_stat_t *)data)->pin_info)
223 | ((pin_stat_t *)data)->pin_info(pin, ((pin_stat_t *)data)->data);
224 | }
225 |
226 | static void enumeratePins (bool low_level, pin_info_ptr pin_info, void *data)
227 | {
228 | pin_stat_t pin_stat = {
229 | .pin_id = 0,
230 | .pin_info = pin_info,
231 | .data = data
232 | };
233 |
234 | static xbar_t pin = {
235 | .id = 0,
236 | .pin = 0,
237 | .group = PinGroup_Virtual,
238 | .function = Virtual_Pin,
239 | .cap.claimable = Off,
240 | .mode.output = On,
241 | .mode.claimed = On
242 | };
243 |
244 | enumerate_pins(low_level, enum_trap, &pin_stat);
245 |
246 | if(!pin_stat.aux_dout0) {
247 | pin.id = ++pin_stat.pin_id;
248 | pin.description = "P0,Dummy out";
249 | pin_info(&pin, data);
250 | }
251 |
252 | if(!pin_stat.aux_dout1) {
253 | pin.id = ++pin_stat.pin_id;
254 | pin.description = "P1,Dummy out";
255 | pin_info(&pin, data);
256 | }
257 |
258 | pin.id = ++pin_stat.pin_id;
259 | pin.port = "THCD",
260 | pin.description = "P2,THC on/off";
261 | pin_info(&pin, data);
262 |
263 | pin.id = ++pin_stat.pin_id;
264 | pin.pin++;
265 | pin.description = "P3,THC torch control";
266 | pin_info(&pin, data);
267 |
268 | pin.pin = 0;
269 | pin.port = "THCA";
270 | pin.mode.analog = On;
271 |
272 | if(!pin_stat.aux_aout0) {
273 | pin.id = ++pin_stat.pin_id;
274 | pin.description = "E0,Dummy out";
275 | pin_info(&pin, data);
276 | pin.pin++;
277 | }
278 |
279 | if(!pin_stat.aux_aout1) {
280 | pin.id = ++pin_stat.pin_id;
281 | pin.description = "E1,Dummy out";
282 | pin_info(&pin, data);
283 | pin.pin++;
284 | }
285 |
286 | if(!pin_stat.aux_aout1) {
287 | pin.id = ++pin_stat.pin_id;
288 | pin.description = "E2,Dummy out";
289 | pin_info(&pin, data);
290 | pin.pin++;
291 | }
292 |
293 | pin.id = ++pin_stat.pin_id;
294 | pin.port = "THCA";
295 | pin.description = "E3,THC feed override";
296 | pin_info(&pin, data);
297 | }
298 |
299 | static void digital_out (uint8_t portnum, bool on)
300 | {
301 | if(portnum == PLASMA_THC_DISABLE_PORT) {
302 | job.thc_disabled = on;
303 | if(thc.arc_ok && mode != Plasma_ModeArcOK) {
304 | if(!(thc.enabled = !job.thc_disabled))
305 | stateHandler = state_arc_monitor;
306 | else
307 | stateHandler = mode == Plasma_ModeUpDown ? state_thc_adjust : state_thc_pid;
308 | }
309 | } else if(portnum == PLASMA_TORCH_DISABLE_PORT) {
310 | // PLASMA_TORCH_DISABLE_PORT:
311 | // TODO
312 | } else if(rport.digital_out)
313 | rport.digital_out(portnum, on);
314 | }
315 |
316 | static bool analog_out (uint8_t portnum, float value)
317 | {
318 | if(portnum == PLASMA_FEED_OVERRIDE_PORT) {
319 | // Let the foreground process handle this
320 | set_feed_override = true;
321 | feed_override = (uint_fast8_t)value;
322 | if(feed_override < 10 || feed_override > 100)
323 | feed_override = 100;
324 | } else
325 | return rport.analog_out ? rport.analog_out(portnum, value) : false;
326 |
327 | return true;
328 | }
329 |
330 | static xbar_t *get_pin_info (io_port_type_t type, io_port_direction_t dir, uint8_t port)
331 | {
332 | static xbar_t pin;
333 |
334 | xbar_t *info = rport.get_pin_info(type, dir, port);
335 |
336 | if(info == NULL && dir == Port_Output) {
337 |
338 | if(type == Port_Digital && port >= digital.out.n_start && port < digital.out.n_start + digital.out.n_ports) {
339 |
340 | memset(&pin, 0, sizeof(xbar_t));
341 |
342 | pin.id = pin.pin = port;
343 | pin.cap.output = On;
344 | pin.cap.claimable = Off;
345 | pin.mode.output = On;
346 | pin.description = port == digital.out.n_start ? "THC enable/disable" : "THC torch control";
347 |
348 | return &pin;
349 | }
350 |
351 | if(type == Port_Analog && port >= analog.out.n_start && port < analog.out.n_start + analog.out.n_ports) {
352 |
353 | memset(&pin, 0, sizeof(xbar_t));
354 |
355 | pin.id = pin.pin = port;
356 | pin.cap.output = pin.cap.analog = On;
357 | pin.cap.claimable = Off;
358 | pin.mode.output = pin.cap.analog = On;
359 | pin.description = "THC feed override";
360 |
361 | return &pin;
362 | }
363 | }
364 |
365 | return info;
366 | }
367 |
368 | static void add_virtual_ports (void *data)
369 | {
370 | uint8_t aux_dout = 2, aux_aout = 1;
371 | pin_stat_t pin_stat = {};
372 |
373 | if(hal.enumerate_pins)
374 | hal.enumerate_pins(false, enum_trap, &pin_stat);
375 |
376 | if(!pin_stat.aux_dout0)
377 | aux_dout++;
378 | if(!pin_stat.aux_dout1)
379 | aux_dout++;
380 | if(!pin_stat.aux_aout0)
381 | aux_aout++;
382 | if(!pin_stat.aux_aout1)
383 | aux_aout++;
384 | if(!pin_stat.aux_aout2)
385 | aux_aout++;
386 |
387 | if(ioports_add(&digital, Port_Digital, 0, aux_dout) && ioports_add(&analog, Port_Analog, 0, aux_aout)) {
388 |
389 | memcpy(&rport, &hal.port, sizeof(io_port_t));
390 |
391 | hal.port.digital_out = digital_out;
392 | hal.port.analog_out = analog_out;
393 | hal.port.get_pin_info = get_pin_info;
394 |
395 | enumerate_pins = hal.enumerate_pins;
396 | hal.enumerate_pins = enumeratePins;
397 | }
398 | }
399 |
400 | // --- Virtual ports end
401 |
402 | // --- Materials handling start
403 |
404 | static void set_job_params (material_t *material)
405 | {
406 | uint_fast8_t idx;
407 |
408 | job.id = material ? material->id : -1;
409 | job.thc_disabled = (material && !material->thc_status) || plasma.mode == Plasma_ModeOff || plasma.mode == Plasma_ModeArcOK;
410 |
411 | if(material) {
412 |
413 | for(idx = 0; idx < sizeof(job.params) / sizeof(float); idx++)
414 | job.params[idx] = material->params[idx];
415 |
416 | if(!isnanf(material->cut_mode) && cutter.set_cut_mode)
417 | cutter.set_cut_mode((uint16_t)material->cut_mode);
418 |
419 | if(!isnanf(material->cut_amps) && cutter.set_current)
420 | cutter.set_current(material->cut_amps);
421 |
422 | if(!isnanf(material->gas_pressure) && cutter.set_pressure)
423 | cutter.set_pressure(material->gas_pressure);
424 |
425 | #if NGC_EXPRESSIONS_ENABLE
426 | ngc_named_param_set("_hal[plasmac.cut-feed-rate]", job.feed_rate);
427 | #endif
428 | } else {
429 |
430 | for(idx = 0; idx < sizeof(job.params) / sizeof(float); idx++)
431 | job.params[idx] = NAN;
432 |
433 | job.pierce_height = plasma.pierce_height;
434 | job.pierce_delay = plasma.pierce_delay;
435 | job.pause_at_end = plasma.pause_at_end;
436 | }
437 |
438 | if(material && *material->name)
439 | report_message(material->name, Message_Info);
440 | }
441 |
442 | static material_t *find_material (uint32_t id)
443 | {
444 | material_t *material = materials, *found = NULL;
445 |
446 | if(material) do {
447 | if(material->id == id)
448 | found = material;
449 | } while(found == NULL && (material = material->next));
450 |
451 | return found;
452 | }
453 |
454 | static user_mcode_type_t mcode_check (user_mcode_t mcode)
455 | {
456 | return mcode == Plasma_SelectMaterial
457 | ? UserMCode_Normal
458 | : (user_mcode.check ? user_mcode.check(mcode) : UserMCode_Unsupported);
459 | }
460 |
461 | static status_code_t mcode_validate (parser_block_t *gc_block)
462 | {
463 | status_code_t state = Status_OK;
464 |
465 | if(gc_block->user_mcode == Plasma_SelectMaterial) {
466 | if(gc_block->words.p) {
467 | if(!isintf(gc_block->values.p))
468 | state = Status_BadNumberFormat;
469 | else if(gc_block->words.p && !(gc_block->values.p == -1.0f || find_material((uint32_t)gc_block->values.p)))
470 | state = Status_GcodeValueOutOfRange;
471 | else
472 | gc_block->words.p = Off;
473 | }
474 | } else
475 | state = Status_Unhandled;
476 |
477 | return state == Status_Unhandled && user_mcode.validate ? user_mcode.validate(gc_block) : state;
478 | }
479 |
480 | static void mcode_execute (uint_fast16_t state, parser_block_t *gc_block)
481 | {
482 | if(gc_block->user_mcode == Plasma_SelectMaterial)
483 | set_job_params(gc_block->values.p == -1.0f ? NULL : find_material((uint32_t)gc_block->values.p));
484 | else if(user_mcode.execute)
485 | user_mcode.execute(state, gc_block);
486 | }
487 |
488 | bool plasma_enumerate_materials (plasma_enumerate_materials_callback_ptr callback, void *data)
489 | {
490 | bool ok = false;
491 | material_t *material = materials;
492 |
493 | if(job.id == tmp_material.id)
494 | ok = callback(&tmp_material, data);
495 |
496 | if(!ok && material) do {
497 | ok = callback(material, data);
498 | } while(!ok && (material = material->next));
499 |
500 | return ok;
501 | }
502 |
503 | bool plasma_material_is_valid (material_t *material)
504 | {
505 | return !(material->id < 0 ||
506 | material->id >= PLASMA_TMP_MATERIAL_ID_START ||
507 | isnanf(material->pierce_height) ||
508 | isnanf(material->pierce_delay) ||
509 | isnanf(material->cut_height) ||
510 | isnanf(material->feed_rate));
511 | }
512 |
513 | bool plasma_material_add (material_t *material, bool overwrite)
514 | {
515 | material_t *m = find_material(material->id), *next = m ? m->next : NULL;
516 | bool add = m == NULL;
517 |
518 | if(overwrite || m == NULL) {
519 | if(m == NULL)
520 | m = malloc(sizeof(material_t));
521 | if(m) {
522 | memcpy(m, material, sizeof(material_t));
523 | m->next = next;
524 | if(materials == NULL)
525 | materials = m;
526 | else if(add) {
527 | material_t *last = materials;
528 | while(last->next)
529 | last = last->next;
530 | last->next = m;
531 | }
532 | } // else error....
533 | }
534 |
535 | return true;
536 | }
537 |
538 | static bool ml_enumerate (material_t *material, void *data)
539 | {
540 | uint_fast8_t i;
541 | char lbl[3], el[]= "| :";
542 |
543 | hal.stream.write("[MATERIAL:o:");
544 | hal.stream.write(uitoa(material->id >= PLASMA_TMP_MATERIAL_ID_START ? 0 : 2));
545 | hal.stream.write("|nu:");
546 | hal.stream.write(uitoa(material->id));
547 | if(*material->name) {
548 | hal.stream.write("|na:");
549 | hal.stream.write(material->name);
550 | }
551 | hal.stream.write("|th:");
552 | hal.stream.write(uitoa(material->thc_status));
553 |
554 | for(i = 0; i < sizeof(material->params) / sizeof(float); i++) {
555 | if(!isnanf(material->params[i])) {
556 | strgetentry(lbl, params, i, ',');
557 | el[1] = lbl[0];
558 | el[2] = lbl[1];
559 | hal.stream.write(el);
560 | hal.stream.write(trim_float(ftoa(material->params[i], ngc_float_decimals())));
561 | }
562 | }
563 | hal.stream.write("]" ASCII_EOL);
564 |
565 | return false;
566 | }
567 |
568 | static status_code_t onGcodeComment (char *comment)
569 | {
570 | status_code_t status = Status_OK;
571 |
572 | if(strlen(comment) > 5 && comment[0] == 'o' && comment[1] == '=') {
573 |
574 | char option = comment[2];
575 | material_t material = {};
576 |
577 | uint_fast8_t i;
578 |
579 | for(i = 0; i < sizeof(material.params) / sizeof(float); i++)
580 | material.params[i] = NAN;
581 |
582 | char *param = strtok(comment + 4, ","), *eq;
583 |
584 | while(param && status == Status_OK) {
585 |
586 | while(*param == ' ')
587 | param++;
588 |
589 | if((eq = strchr(param, '='))) {
590 |
591 | int32_t p;
592 |
593 | *eq = '\0';
594 |
595 | switch((p = strlookup(param, params, ','))) {
596 |
597 | case -1:
598 | status = Status_GcodeUnsupportedCommand;
599 | break;
600 |
601 | case 12:
602 | if(option != '0') {
603 | uint32_t id;
604 | uint_fast8_t cc = 1;
605 | if((status = read_uint(eq, &cc, &id)) == Status_OK)
606 | material.id = (int32_t)id;
607 | }
608 | break;
609 |
610 | case 13:
611 | strncpy(material.name, eq + 1, sizeof(material.name) - 1);
612 | break;
613 |
614 | case 14:
615 | material.thc_status = eq[1] != '0';
616 | break;
617 |
618 | default:
619 | {
620 | uint_fast8_t cc = 1;
621 | if(!read_float(eq, &cc, &material.params[p]))
622 | status = Status_BadNumberFormat;
623 | }
624 | break;
625 | }
626 | *eq = '=';
627 | }
628 | param = strtok(NULL, ",");
629 | }
630 |
631 | if(status == Status_OK && !plasma_material_is_valid(&material))
632 | status = Status_GcodeValueWordMissing;
633 |
634 | if(status == Status_OK) switch(option) {
635 |
636 | case '0':
637 | material.id = material_id++;
638 | memcpy(&tmp_material, &material, sizeof(material_t));
639 | set_job_params(&tmp_material);
640 | break;
641 |
642 | case '1':
643 | case '2':
644 | plasma_material_add(&material, option == '2');
645 | break;
646 |
647 | default:
648 | status = Status_GcodeUnsupportedCommand;
649 | break;
650 | }
651 |
652 | } else if(on_gcode_comment)
653 | status = on_gcode_comment(comment);
654 |
655 | return status;
656 | }
657 |
658 | // --- Materials handling end
659 |
660 | static bool moveto (float z_position)
661 | {
662 | bool ok;
663 | coord_data_t target;
664 | plan_line_data_t plan_data;
665 |
666 | plan_data_init(&plan_data);
667 | plan_data.condition.rapid_motion = On;
668 |
669 | system_convert_array_steps_to_mpos(target.values, sys.position);
670 |
671 | target.z = z_position + gc_get_offset(Z_AXIS, false);
672 | if((ok = mc_line(target.values, &plan_data))) {
673 | protocol_buffer_synchronize();
674 | sync_position();
675 | }
676 |
677 | return ok;
678 | }
679 |
680 | static void set_target_voltage (float v)
681 | {
682 | arc_vref = arc_voltage = v;
683 | arc_voltage_low = arc_vref - plasma.thc_threshold;
684 | arc_voltage_high = arc_vref + plasma.thc_threshold;
685 | v_count = 0;
686 | }
687 |
688 | static void pause_on_error (void)
689 | {
690 | stateHandler = state_idle;
691 | system_set_exec_state_flag(EXEC_FEED_HOLD); // Set up program pause for manual tool change
692 | // system_set_exec_state_flag(EXEC_TOOL_CHANGE); // Set up program pause for manual tool change
693 | protocol_execute_realtime(); // Execute...
694 | }
695 |
696 | /* THC state machine */
697 |
698 | static void state_idle (void)
699 | {
700 | if(mode == Plasma_ModeVoltage)
701 | arc_voltage = parc_voltage.get_value(&parc_voltage) * plasma.arc_voltage_scale - plasma.arc_voltage_offset;
702 |
703 | if(plasma.option.sync_pos && state_get() == STATE_IDLE) {
704 |
705 | if(mode != Plasma_ModeUpDown)
706 | step_count =(uint32_t)st2_get_position(z_motor);
707 |
708 | if(step_count && state_get() == STATE_IDLE) {
709 | sys.position[Z_AXIS] += step_count;
710 | step_count = 0;
711 | st2_set_position(z_motor, 0LL);
712 | sync_position();
713 | }
714 | }
715 | }
716 |
717 | static void state_thc_delay (void)
718 | {
719 | if(hal.get_elapsed_ticks() >= thc_delay) {
720 |
721 | if(!(thc.enabled = !(job.thc_disabled || mode == Plasma_ModeArcOK)))
722 | stateHandler = state_arc_monitor;
723 | else if(mode == Plasma_ModeUpDown) {
724 | step_count = 0;
725 | stateHandler = state_thc_adjust;
726 | } else {
727 | pidf_reset(&pid);
728 | st2_set_position(z_motor, 0LL);
729 | if(!isnanf(job.cut_voltage))
730 | set_target_voltage(job.cut_voltage);
731 | else
732 | set_target_voltage(parc_voltage.get_value(&parc_voltage) * plasma.arc_voltage_scale - plasma.arc_voltage_offset);
733 | stateHandler = state_vad_lock;
734 | stateHandler();
735 | }
736 | }
737 | }
738 |
739 | static void state_arc_monitor (void)
740 | {
741 | if(!(thc.arc_ok = arc_ok.get_value(&arc_ok) == 1.0f))
742 | pause_on_error();
743 | }
744 |
745 | static void state_thc_adjust (void)
746 | {
747 | if((thc.arc_ok = arc_ok.get_value(&arc_ok) == 1.0f)) {
748 | if(updown_enabled) {
749 | thc.up = thc.report_up = cutter_up.get_value(&cutter_up) == 1.0f;
750 | thc.down = thc.report_down = cutter_down.get_value(&cutter_down) == 1.0f;
751 | if(thc.up != thc.down) {
752 | if(thc.up) {
753 | step_count++;
754 | hal.stepper.output_step((axes_signals_t){Z_AXIS_BIT}, (axes_signals_t){0});
755 | } else {
756 | step_count--;
757 | hal.stepper.output_step((axes_signals_t){Z_AXIS_BIT}, (axes_signals_t){Z_AXIS_BIT});
758 | }
759 | }
760 | }
761 | } else
762 | pause_on_error();
763 | }
764 |
765 | static void state_vad_lock (void)
766 | {
767 | arc_voltage = parc_voltage.get_value(&parc_voltage) * plasma.arc_voltage_scale - plasma.arc_voltage_offset;
768 |
769 | if((thc.active = fr_actual >= fr_thr_99))
770 | stateHandler = state_thc_pid;
771 | }
772 |
773 | static void state_thc_pid (void)
774 | {
775 | static float v;
776 |
777 | if(!(thc.active = fr_actual >= fr_thr_vad)) {
778 | stateHandler = state_vad_lock;
779 | return;
780 | }
781 |
782 | if((thc.arc_ok = arc_ok.get_value(&arc_ok)) == 1.0f) {
783 |
784 | if(v_count == 0)
785 | v = 0.0f;
786 |
787 | arc_voltage = parc_voltage.get_value(&parc_voltage) * plasma.arc_voltage_scale - plasma.arc_voltage_offset;
788 | v += arc_voltage;
789 | if(++v_count == THC_SAMPLE_AVG) {
790 |
791 | arc_voltage = v / (float)THC_SAMPLE_AVG;
792 | v_count = 0;
793 |
794 | if(arc_voltage < arc_voltage_low || arc_voltage > arc_voltage_high) {
795 | float err = pidf(&pid, arc_vref, arc_voltage, 1.0f);
796 | if(!st2_motor_running(z_motor)) {
797 | /*
798 | char buf[50];
799 | strcpy(buf, ftoa(arc_vref, 1));
800 | strcat(buf, ",");
801 | strcat(buf, ftoa(arc_voltage, 1));
802 | strcat(buf, ",");
803 | strcat(buf, ftoa(err, 1));
804 | report_message(buf, Message_Info);
805 | */
806 | st2_motor_move(z_motor, -err * plasma.arc_height_per_volt, settings.axis[Z_AXIS].max_rate, Stepper2_mm);
807 | }
808 | }
809 | }
810 | /*
811 | if(arc_voltage >= arc_voltage_high)
812 | hal.stepper.output_step((axes_signals_t){Z_AXIS_BIT}, (axes_signals_t){Z_AXIS_BIT});
813 | else if(arc_voltage <= arc_voltage_low)
814 | hal.stepper.output_step((axes_signals_t){Z_AXIS_BIT}, (axes_signals_t){0});
815 | */
816 |
817 | } else
818 | pause_on_error();
819 | }
820 |
821 | /* end THC state machine */
822 |
823 | static void exec_state_machine (void *data)
824 | {
825 | stateHandler();
826 |
827 | if(set_feed_override) {
828 | set_feed_override = false;
829 | plan_feed_override(feed_override, sys.override.rapid_rate);
830 | }
831 |
832 | /*
833 | if(or) {
834 | or = false;
835 | hal.stream.write("[MSG:FR ");
836 | hal.stream.write(ftoa(fr_pgm, 1));
837 | hal.stream.write(" ");
838 | hal.stream.write(ftoa(fr_actual, 1));
839 | hal.stream.write("]" ASCII_EOL);
840 | }
841 | */
842 | }
843 |
844 | static void onExecuteRealtime (uint_fast16_t state)
845 | {
846 | if(stateHandler == state_thc_pid)
847 | st2_motor_run(z_motor);
848 |
849 | on_execute_realtime(state);
850 | }
851 |
852 | static void reset (void)
853 | {
854 | thc.value = 0;
855 | stateHandler = state_idle;
856 |
857 | st2_motor_stop(z_motor);
858 |
859 | driver_reset();
860 | }
861 |
862 | // Start or stop arc
863 | static void arcSetState (spindle_ptrs_t *spindle, spindle_state_t state, float rpm)
864 | {
865 | if(driver_reset == NULL) {
866 | spindle_set_state_(spindle, state, rpm);
867 | if(state.on)
868 | report_message("Plasma mode not available!", Message_Warning);
869 | return;
870 | }
871 |
872 | if(!state.on) {
873 |
874 | if(!isnanf(job.pause_at_end))
875 | delay_sec(job.pause_at_end, DelayMode_Dwell);
876 | spindle_set_state_(spindle, state, rpm);
877 | thc.torch_on = thc.arc_ok = thc.enabled = Off;
878 | stateHandler = state_idle;
879 |
880 | } else {
881 |
882 | uint_fast8_t retries = plasma.arc_retries;
883 |
884 | if(job.pierce_height != 0.0f)
885 | moveto(job.pierce_height);
886 |
887 | do {
888 | spindle_set_state_(spindle, state, rpm);
889 | thc.torch_on = On;
890 | report_message("arc on", Message_Plain);
891 | if((thc.arc_ok = hal.port.wait_on_input(Port_Digital, port_arc_ok, WaitMode_High, plasma.arc_fail_timeout) != -1)) {
892 | report_message("arc ok", Message_Plain);
893 | delay_sec(job.pierce_delay, DelayMode_Dwell);
894 | if(!isnanf(job.cut_height))
895 | moveto(job.cut_height);
896 | retries = 0;
897 | thc_delay = hal.get_elapsed_ticks() + (uint32_t)ceilf(1000.0f * plasma.thc_delay); // handle overflow!
898 | stateHandler = state_thc_delay;
899 | } else if(!(--retries)) {
900 | thc.torch_on = Off;
901 | report_message("arc failed", Message_Warning);
902 | spindle_set_state_(spindle, (spindle_state_t){0}, 0.0f);
903 | pause_on_error(); // output message and enter similar state as tool change state (allow jogging before resume)
904 | } else {
905 | thc.torch_on = Off;
906 | report_message("arc delay", Message_Plain);
907 | spindle_set_state_(spindle, (spindle_state_t){0}, 0.0f);
908 | delay_sec(plasma.arc_retry_delay, DelayMode_Dwell);
909 | }
910 | } while(retries);
911 | }
912 | }
913 |
914 | static void stepperPulseStart (stepper_t *stepper)
915 | {
916 | // static volatile bool get_rates = false;
917 |
918 | if(stepper->new_block) {
919 | // get_rates = true;
920 | fr_pgm = stepper->exec_block->programmed_rate * 0.01f * sys.override.feed_rate;
921 | fr_thr_99 = fr_pgm * 0.99f;
922 | fr_thr_vad = fr_pgm * 0.01f * (float)plasma.vad_threshold;
923 | segment_id = 0;
924 | }
925 |
926 | if(stepper->exec_segment->id != segment_id) {
927 | segment_id = stepper->exec_segment->id;
928 | fr_actual = stepper->exec_segment->current_rate;
929 | }
930 |
931 | stepper_pulse_start(stepper);
932 | }
933 |
934 | // Trap cycle start commands and redirect to foreground process
935 | // by temporarily claiming the HAL execute_realtime entry point
936 | // in order to execute probing and spindle/coolant change.
937 | // TODO: move to state machine with own EXEC_ bit?
938 | /*
939 | ISR_CODE static void trap_control_interrupts (control_signals_t signals)
940 | {
941 | if(signals.value)
942 | control_interrupt_callback(signals);
943 | }
944 | */
945 |
946 | // Convert control signals bits to string representation.
947 | // NOTE: returns pointer to null terminator!
948 | static inline char *signals_tostring (char *buf, thc_signals_t signals)
949 | {
950 | static const char signals_map[] = "ATEOFBR VHUD ";
951 |
952 | char *map = (char *)signals_map;
953 |
954 | if(signals.bits)
955 | *buf++ = ',';
956 |
957 | while(signals.bits) {
958 |
959 | if(signals.bits & 0x01) {
960 | switch(*map) {
961 |
962 | case ' ':
963 | break;
964 |
965 | default:
966 | *buf++ = *map;
967 | break;
968 | }
969 | }
970 |
971 | map++;
972 | signals.bits >>= 1;
973 | }
974 |
975 | *buf = '\0';
976 |
977 | return buf;
978 | }
979 |
980 |
981 | static void onRealtimeReport (stream_write_ptr stream_write, report_tracking_flags_t report)
982 | {
983 | static char buf[24];
984 |
985 | strcpy(buf, "|THC:");
986 | strcat(buf, ftoa(arc_voltage, 1));
987 | signals_tostring(strchr(buf, '\0'), thc);
988 | stream_write(buf);
989 |
990 | thc.report_up = thc.report_down = Off;
991 |
992 | if(on_realtime_report)
993 | on_realtime_report(stream_write, report);
994 | }
995 |
996 | static void onSpindleSelected (spindle_ptrs_t *spindle)
997 | {
998 | spindle_set_state_ = spindle->set_state;
999 |
1000 | spindle->set_state = arcSetState;
1001 | // TODO: only change caps if PWM spindle active?
1002 | spindle->cap.at_speed = Off;
1003 | //?? spindle->cap.laser = Off;
1004 | spindle->cap.torch = On;
1005 |
1006 | if(on_spindle_selected)
1007 | on_spindle_selected(spindle);
1008 | }
1009 |
1010 | static void plasma_setup (settings_t *settings, settings_changed_flags_t changed)
1011 | {
1012 | settings_changed(settings, changed);
1013 |
1014 | if(!driver_reset) {
1015 |
1016 | driver_reset = hal.driver_reset;
1017 | hal.driver_reset = reset;
1018 |
1019 | on_spindle_selected = grbl.on_spindle_selected;
1020 | grbl.on_spindle_selected = onSpindleSelected;
1021 |
1022 | on_realtime_report = grbl.on_realtime_report;
1023 | grbl.on_realtime_report = onRealtimeReport;
1024 |
1025 | if(st2_motor_poll(z_motor)) {
1026 | on_execute_realtime = grbl.on_execute_realtime;
1027 | grbl.on_execute_realtime = onExecuteRealtime;
1028 | }
1029 |
1030 | task_add_systick(exec_state_machine, NULL);
1031 | }
1032 |
1033 | // Reclaim entry points that may have been changed on settings change.
1034 |
1035 | if(hal.stepper.pulse_start != stepperPulseStart) {
1036 | stepper_pulse_start = hal.stepper.pulse_start;
1037 | hal.stepper.pulse_start = stepperPulseStart;
1038 | }
1039 | }
1040 |
1041 | PROGMEM static const setting_group_detail_t plasma_groups[] = {
1042 | { Group_Root, Group_Plasma, "Plasma" },
1043 | };
1044 |
1045 | static bool is_setting_available (const setting_detail_t *setting, uint_fast16_t offset)
1046 | {
1047 | bool ok = false;
1048 |
1049 | switch(setting->id) {
1050 |
1051 | case Setting_THC_CutterDownPort:
1052 | case Setting_THC_CutterUpPort:
1053 | ok = n_din >= 3;
1054 | break;
1055 |
1056 | case Setting_Arc_VoltagePort:
1057 | ok = n_ain >= 1;
1058 | break;
1059 |
1060 | case Setting_THC_VADThreshold:
1061 | ok = init_ok;
1062 | break;
1063 |
1064 | default:
1065 | ok = init_ok && plasma.mode == Plasma_ModeVoltage;
1066 | break;
1067 | }
1068 |
1069 | return ok;
1070 | }
1071 |
1072 | static status_code_t set_port (setting_id_t setting, float value)
1073 | {
1074 | status_code_t status;
1075 |
1076 | if((status = isintf(value) ? Status_OK : Status_BadNumberFormat) == Status_OK)
1077 | switch(setting) {
1078 |
1079 | case Setting_Arc_VoltagePort:
1080 | plasma.port_arc_voltage = value < 0.0f ? 255 : (uint8_t)value;
1081 | break;
1082 |
1083 | case Setting_Arc_OkPort:
1084 | plasma.port_arc_ok = value < 0.0f ? 255 : (uint8_t)value;
1085 | break;
1086 |
1087 | case Setting_THC_CutterDownPort:
1088 | plasma.port_cutter_down = value < 0.0f ? 255 : (uint8_t)value;
1089 | break;
1090 |
1091 | case Setting_THC_CutterUpPort:
1092 | plasma.port_cutter_up = value < 0.0f ? 255 : (uint8_t)value;
1093 | break;
1094 |
1095 | default:
1096 | break;
1097 | }
1098 |
1099 | return status;
1100 | }
1101 |
1102 | static float get_port (setting_id_t setting)
1103 | {
1104 | float value = 0.0f;
1105 |
1106 | switch(setting) {
1107 |
1108 | case Setting_Arc_VoltagePort:
1109 | value = plasma.port_arc_voltage >= n_ain ? -1.0f : (float)plasma.port_arc_voltage;
1110 | break;
1111 |
1112 | case Setting_Arc_OkPort:
1113 | value = plasma.port_arc_ok >= n_din ? -1.0f : (float)plasma.port_arc_ok;
1114 | break;
1115 |
1116 | case Setting_THC_CutterDownPort:
1117 | value = plasma.port_cutter_down >= n_din ? -1.0f : (float)plasma.port_cutter_down;
1118 | break;
1119 |
1120 | case Setting_THC_CutterUpPort:
1121 | value = plasma.port_cutter_up >= n_din ? -1.0f : (float)plasma.port_cutter_up;
1122 | break;
1123 |
1124 | default:
1125 | break;
1126 | }
1127 |
1128 | return value;
1129 | }
1130 |
1131 | PROGMEM static const setting_detail_t plasma_settings[] = {
1132 | { Setting_THC_Mode, Group_Plasma, "Plasma mode", NULL, Format_RadioButtons, thc_modes, NULL, NULL, Setting_NonCore, &plasma.mode, NULL, NULL, { .reboot_required = On } },
1133 | { Setting_THC_Delay, Group_Plasma, "Plasma THC delay", "s", Format_Decimal, "#0.0", NULL, NULL, Setting_NonCore, &plasma.thc_delay, NULL, NULL },
1134 | { Setting_THC_Threshold, Group_Plasma, "Plasma THC threshold", "V", Format_Decimal, "#0.00", NULL, NULL, Setting_NonCore, &plasma.thc_threshold, NULL, is_setting_available },
1135 | { Setting_THC_PGain, Group_Plasma, "Plasma THC P-gain", NULL, Format_Decimal, "###0.000", NULL, NULL, Setting_NonCore, &plasma.pid.p_gain, NULL, is_setting_available },
1136 | { Setting_THC_IGain, Group_Plasma, "Plasma THC I-gain", NULL, Format_Decimal, "###0.000", NULL, NULL, Setting_NonCore, &plasma.pid.i_gain, NULL, is_setting_available },
1137 | { Setting_THC_DGain, Group_Plasma, "Plasma THC D-gain", NULL, Format_Decimal, "###0.000", NULL, NULL, Setting_NonCore, &plasma.pid.d_gain, NULL, is_setting_available },
1138 | { Setting_THC_VADThreshold, Group_Plasma, "Plasma THC VAD threshold", "percent", Format_Integer, "##0", "0", "100", Setting_NonCore, &plasma.vad_threshold, NULL, is_setting_available },
1139 | { Setting_THC_VoidOverride, Group_Plasma, "Plasma THC Void override", "percent", Format_Integer, "##0", "0", "100", Setting_NonCore, &plasma.thc_override, NULL, is_setting_available },
1140 | { Setting_Arc_FailTimeout, Group_Plasma, "Plasma Arc fail timeout", "seconds", Format_Decimal, "#0.0", NULL, NULL, Setting_NonCore, &plasma.arc_fail_timeout, NULL, NULL },
1141 | { Setting_Arc_RetryDelay, Group_Plasma, "Plasma Arc retry delay", "seconds", Format_Decimal, "#0.0", NULL, NULL, Setting_NonCore, &plasma.arc_retry_delay, NULL, NULL },
1142 | { Setting_Arc_MaxRetries, Group_Plasma, "Plasma Arc max retries", NULL, Format_Int8, "#0", NULL, NULL, Setting_NonCore, &plasma.arc_retries, NULL, NULL },
1143 | { Setting_Arc_VoltageScale, Group_Plasma, "Plasma Arc voltage scale", NULL, Format_Decimal, "###0.000", NULL, NULL, Setting_NonCore, &plasma.arc_voltage_scale, NULL, is_setting_available },
1144 | { Setting_Arc_VoltageOffset, Group_Plasma, "Plasma Arc voltage offset", NULL, Format_Decimal, "###0.000", NULL, NULL, Setting_NonCore, &plasma.arc_voltage_offset, NULL, is_setting_available },
1145 | { Setting_Arc_HeightPerVolt, Group_Plasma, "Plasma Arc height per volt", "mm", Format_Decimal, "###0.000", NULL, NULL, Setting_NonCore, &plasma.arc_height_per_volt, NULL, is_setting_available },
1146 | { Setting_Arc_OkHighVoltage, Group_Plasma, "Plasma Arc ok high volts", "V", Format_Decimal, "###0.000", NULL, NULL, Setting_NonCore, &plasma.arc_high_low_voltage, NULL, is_setting_available },
1147 | { Setting_Arc_OkLowVoltage, Group_Plasma, "Plasma Arc ok low volts", "V", Format_Decimal, "###0.000", NULL, NULL, Setting_NonCore, &plasma.arc_ok_low_voltage, NULL, is_setting_available },
1148 | { Setting_Arc_VoltagePort, Group_AuxPorts, "Arc voltage port", NULL, Format_Decimal, "-#0", "-1", max_aport, Setting_NonCoreFn, set_port, get_port, is_setting_available, { .reboot_required = On } },
1149 | { Setting_Arc_OkPort, Group_AuxPorts, "Arc ok port", NULL, Format_Decimal, "-#0", "-1", max_dport, Setting_NonCoreFn, set_port, get_port, NULL, { .reboot_required = On } },
1150 | { Setting_THC_CutterDownPort, Group_AuxPorts, "Cutter down port", NULL, Format_Decimal, "-#0", "-1", max_dport, Setting_NonCoreFn, set_port, get_port, is_setting_available, { .reboot_required = On } },
1151 | { Setting_THC_CutterUpPort, Group_AuxPorts, "Cutter up port", NULL, Format_Decimal, "-#0", "-1", max_dport, Setting_NonCoreFn, set_port, get_port, is_setting_available, { .reboot_required = On } },
1152 | { Setting_THC_Options, Group_Plasma, "Plasma options", NULL, Format_Bitfield, "Virtual ports,Sync Z position", NULL, NULL, Setting_NonCore, &plasma.option.flags, NULL, NULL, { .reboot_required = On } },
1153 | };
1154 |
1155 | #ifndef NO_SETTINGS_DESCRIPTIONS
1156 |
1157 | PROGMEM static const setting_descr_t plasma_settings_descr[] = {
1158 | { Setting_THC_Mode, "" },
1159 | { Setting_THC_Delay, "Delay from cut start until THC activates." },
1160 | { Setting_THC_Threshold, "Variation from target voltage for THC to correct height." },
1161 | { Setting_THC_PGain, "" },
1162 | { Setting_THC_IGain, "" },
1163 | { Setting_THC_DGain, "" },
1164 | { Setting_THC_VADThreshold, "Percentage of Cut Feed Rate velocity needs to fall below to lock THC." },
1165 | { Setting_THC_VoidOverride, "Higher values need greater voltage change to lock THC." },
1166 | { Setting_Arc_FailTimeout, "The amount of time to wait from torch on until a failure if arc is not detected." },
1167 | { Setting_Arc_RetryDelay, "The time between an arc failure and another arc start attempt." },
1168 | { Setting_Arc_MaxRetries, "The number of attempts at starting an arc." },
1169 | { Setting_Arc_VoltageScale, "The value required to scale the arc voltage input to display the correct arc voltage." },
1170 | { Setting_Arc_VoltageOffset, "The value required to display zero volts when there is zero arc voltage input.\\n"
1171 | "For initial setup multiply the arc voltage out value by -1 and enter that for Voltage Offset."
1172 | },
1173 | { Setting_Arc_HeightPerVolt, "The distance the torch would need to move to change the arc voltage by one volt.\\n"
1174 | // "Used for manual height change only."
1175 | },
1176 | { Setting_Arc_OkHighVoltage, "High voltage threshold for Arc OK." },
1177 | { Setting_Arc_OkLowVoltage, "Low voltage threshold for Arc OK." },
1178 | { Setting_Arc_VoltagePort, "Aux port number to use for arc voltage. Set to -1 to disable." },
1179 | { Setting_Arc_OkPort, "Aux port number to use for arc ok signal. Set to -1 to disable." },
1180 | { Setting_THC_CutterDownPort, "Aux port number to use for cutter down signal. Set to -1 to disable." },
1181 | { Setting_THC_CutterUpPort, "Aux port number to use for cutter up signal. Set to -1 to disable." },
1182 | { Setting_THC_Options, "" }
1183 | };
1184 |
1185 | #endif
1186 |
1187 | static void plasma_settings_save (void)
1188 | {
1189 | hal.nvs.memcpy_to_nvs(nvs_address, (uint8_t *)&plasma, sizeof(plasma_settings_t), true);
1190 | }
1191 |
1192 | static void plasma_settings_restore (void)
1193 | {
1194 | plasma.mode = mode;
1195 | plasma.option.flags = 0;
1196 | plasma.thc_delay = 3.0f;
1197 | plasma.thc_threshold = 1.0f;
1198 | plasma.thc_override = 100;
1199 | plasma.vad_threshold = 90;
1200 | plasma.pause_at_end = 0.0f;
1201 | plasma.pierce_delay = 0.0f;
1202 | plasma.pierce_height = 1.0f;
1203 | plasma.arc_fail_timeout = 3.0f;
1204 | plasma.arc_retries = 3;
1205 | plasma.arc_retry_delay = 3.0f;
1206 | plasma.arc_fail_timeout = 3.0f;
1207 | plasma.arc_voltage_scale = 1.0f;
1208 | plasma.arc_voltage_offset = 0.0f;
1209 | plasma.arc_height_per_volt = 0.1f;
1210 | plasma.arc_high_low_voltage = 150.0;
1211 | plasma.arc_ok_low_voltage = 100.0f;
1212 | plasma.pid.p_gain = 1.0f;
1213 | plasma.pid.i_gain = 0.0f;
1214 | plasma.pid.d_gain = 0.0f;
1215 | plasma.port_arc_voltage = ioport_find_free(Port_Analog, Port_Input, (pin_cap_t){ .claimable = On }, "Arc voltage");
1216 | plasma.port_arc_ok = ioport_find_free(Port_Digital, Port_Input, (pin_cap_t){ .claimable = On }, "Arc ok");
1217 | plasma.port_cutter_down = updown_enabled && plasma.port_arc_ok >= 1 ? plasma.port_arc_ok - 1 : 255;
1218 | plasma.port_cutter_up = updown_enabled && plasma.port_arc_ok >= 2 ? plasma.port_arc_ok - 2 : 255;
1219 |
1220 | hal.nvs.memcpy_to_nvs(nvs_address, (uint8_t *)&plasma, sizeof(plasma_settings_t), true);
1221 | }
1222 |
1223 | static bool plasma_claim_digital_in (xbar_t *target, uint8_t port, const char *description)
1224 | {
1225 | xbar_t *p;
1226 | bool ok = false;
1227 |
1228 | if(port != 255 && (p = ioport_get_info(Port_Digital, Port_Input, port)) && p->get_value && !p->mode.claimed) {
1229 | memcpy(target, p, sizeof(xbar_t));
1230 | if((ok = ioport_claim(Port_Digital, Port_Input, &port, description)) && target == &arc_ok)
1231 | port_arc_ok = port;
1232 | }
1233 |
1234 | return ok;
1235 | }
1236 |
1237 | static void plasma_settings_load (void)
1238 | {
1239 | if(hal.nvs.memcpy_from_nvs((uint8_t *)&plasma, nvs_address, sizeof(plasma_settings_t), true) != NVS_TransferResult_OK) {
1240 | plasma.port_arc_ok = plasma.port_cutter_down = plasma.port_cutter_up = 255;
1241 | plasma_settings_restore();
1242 | }
1243 |
1244 | if((mode = plasma.mode) == Plasma_ModeOff)
1245 | return;
1246 |
1247 | if(!(init_ok = mode != Plasma_ModeVoltage)) {
1248 |
1249 | if(plasma.port_arc_voltage != 255) {
1250 |
1251 | xbar_t *p;
1252 |
1253 | if((p = ioport_get_info(Port_Analog, Port_Input, port_arc_voltage)) && p->get_value && !p->mode.claimed) {
1254 | memcpy(&parc_voltage, p, sizeof(xbar_t));
1255 | init_ok = ioport_claim(Port_Analog, Port_Input, &port_arc_voltage, "Arc voltage");
1256 | }
1257 | }
1258 |
1259 | if(!init_ok) {
1260 | init_ok = true;
1261 | mode = Plasma_ModeUpDown;
1262 | }
1263 | }
1264 |
1265 | init_ok = init_ok && plasma_claim_digital_in(&arc_ok, plasma.port_arc_ok, "Arc ok");
1266 |
1267 | if(init_ok && mode == Plasma_ModeUpDown) {
1268 | if(!(plasma_claim_digital_in(&cutter_down, plasma.port_cutter_down, "Cutter down") &&
1269 | plasma_claim_digital_in(&cutter_up, plasma.port_cutter_up, "Cutter up")))
1270 | mode = Plasma_ModeArcOK;
1271 | }
1272 |
1273 | if(init_ok) {
1274 |
1275 | set_job_params(NULL);
1276 |
1277 | updown_enabled = mode == Plasma_ModeUpDown;
1278 |
1279 | settings_changed = hal.settings_changed;
1280 | hal.settings_changed = plasma_setup;
1281 |
1282 | if(plasma.mode != mode)
1283 | task_run_on_startup(report_warning, "Plasma mode changed due to lack of inputs!");
1284 |
1285 | if(plasma.option.virtual_ports)
1286 | task_run_on_startup(add_virtual_ports, NULL);
1287 | } else
1288 | task_run_on_startup(report_warning, "Plasma mode failed to initialize!");
1289 | }
1290 |
1291 | static void on_settings_changed (settings_t *settings, settings_changed_flags_t changed)
1292 | {
1293 | pidf_init(&pid, &plasma.pid);
1294 | }
1295 |
1296 | static status_code_t matlist (sys_state_t state, char *args)
1297 | {
1298 | plasma_enumerate_materials(ml_enumerate, NULL);
1299 |
1300 | return Status_OK;
1301 | }
1302 |
1303 | static void onReportOptions (bool newopt)
1304 | {
1305 | on_report_options(newopt);
1306 |
1307 | if(!newopt) {
1308 |
1309 | plasma_mode_t i = Plasma_ModeOff;
1310 | char buf[30] = "PLASMA (", *s1 = &buf[8], *s2 = thc_modes, c;
1311 |
1312 | while((c = *s2++)) {
1313 | if(i == mode) {
1314 | if(c == ',')
1315 | break;
1316 | *s1++ = c;
1317 | } else if(c == ',')
1318 | i++;
1319 | }
1320 | *s1++ = ')';
1321 | *s1 = '\0';
1322 |
1323 | report_plugin(buf, "0.21");
1324 |
1325 | } else if(mode != Plasma_ModeOff)
1326 | hal.stream.write(",THC");
1327 | }
1328 |
1329 | void plasma_init (void)
1330 | {
1331 | static setting_details_t setting_details = {
1332 | .groups = plasma_groups,
1333 | .n_groups = sizeof(plasma_groups) / sizeof(setting_group_detail_t),
1334 | .settings = plasma_settings,
1335 | .n_settings = sizeof(plasma_settings) / sizeof(setting_detail_t),
1336 | #ifndef NO_SETTINGS_DESCRIPTIONS
1337 | .descriptions = plasma_settings_descr,
1338 | .n_descriptions = sizeof(plasma_settings_descr) / sizeof(setting_descr_t),
1339 | #endif
1340 | .save = plasma_settings_save,
1341 | .load = plasma_settings_load,
1342 | .restore = plasma_settings_restore,
1343 | .on_changed = on_settings_changed
1344 | };
1345 |
1346 | static const sys_command_t thc_command_list[] = {
1347 | {"EM", matlist, { .allow_blocking = On, .noargs = On }, { .str = "outputs plasma materials list" } }
1348 | };
1349 |
1350 | static sys_commands_t thc_commands = {
1351 | .n_commands = sizeof(thc_command_list) / sizeof(sys_command_t),
1352 | .commands = thc_command_list
1353 | };
1354 |
1355 | bool ok;
1356 |
1357 | if((ok = !!hal.stepper.output_step && ioport_can_claim_explicit())) {
1358 | n_ain = ioports_available(Port_Analog, Port_Input);
1359 | n_din = ioports_available(Port_Digital, Port_Input);
1360 | ok = (n_ain >= 1 && n_din >= 1) || (n_din >= 3);
1361 | }
1362 |
1363 | if(ok) {
1364 | updown_enabled = n_ain == 0;
1365 | ok = (nvs_address = nvs_alloc(sizeof(plasma_settings_t)));
1366 | }
1367 |
1368 | if(ok && (z_motor = st2_motor_init(Z_AXIS, false)) != NULL) {
1369 |
1370 | if(n_ain)
1371 | strcpy(max_aport, uitoa(n_ain - 1));
1372 | strcpy(max_dport, uitoa(n_din - 1));
1373 |
1374 | settings_register(&setting_details);
1375 | system_register_commands(&thc_commands);
1376 |
1377 | memcpy(&user_mcode, &grbl.user_mcode, sizeof(user_mcode_ptrs_t));
1378 |
1379 | grbl.user_mcode.check = mcode_check;
1380 | grbl.user_mcode.validate = mcode_validate;
1381 | grbl.user_mcode.execute = mcode_execute;
1382 |
1383 | on_report_options = grbl.on_report_options;
1384 | grbl.on_report_options = onReportOptions;
1385 |
1386 | on_gcode_comment = grbl.on_gcode_comment;
1387 | grbl.on_gcode_comment = onGcodeComment;
1388 |
1389 | /*
1390 | control_interrupt_callback = hal.control_interrupt_callback;
1391 | hal.control_interrupt_callback = trap_control_interrupts;
1392 | */
1393 |
1394 | if(n_ain == 0)
1395 | setting_remove_elements(Setting_THC_Mode, 0b1101);
1396 |
1397 | if(n_din < 3)
1398 | setting_remove_elements(Setting_THC_Mode, 0b1011);
1399 |
1400 | // Load materials
1401 | sheetcam_init();
1402 | linuxcnc_init();
1403 |
1404 | } else
1405 | task_run_on_startup(report_warning, "Plasma mode failed to initialize!");
1406 | }
1407 |
1408 | #endif // PLASMA_ENABLE
1409 |
--------------------------------------------------------------------------------
/thc.h:
--------------------------------------------------------------------------------
1 | /*
2 |
3 | thc.h - plasma cutter tool height control plugin
4 |
5 | Part of grblHAL
6 |
7 | Copyright (c) 2020-2025 Terje Io
8 |
9 | grblHAL is free software: you can redistribute it and/or modify
10 | it under the terms of the GNU General Public License as published by
11 | the Free Software Foundation, either version 3 of the License, or
12 | (at your option) any later version.
13 |
14 | grblHAL is distributed in the hope that it will be useful,
15 | but WITHOUT ANY WARRANTY; without even the implied warranty of
16 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 | GNU General Public License for more details.
18 |
19 | You should have received a copy of the GNU General Public License
20 | along with grblHAL. If not, see .
21 |
22 | */
23 |
24 | #pragma once
25 |
26 | #include "driver.h"
27 |
28 | #if PLASMA_ENABLE
29 |
30 | typedef enum {
31 | Plasma_SetMode = 0,
32 | Plasma_SetCurrent,
33 | Plasma_SetPressure,
34 | Plasma_GetCurrentMin,
35 | Plasma_GetCurrentMax,
36 | Plasma_GetPressureMin,
37 | Plasma_GetPressureMax,
38 | Plasma_GetArcOnTimeLow,
39 | Plasma_GetArcOnTimeHigh,
40 | } plasma_rs485_msg_t;
41 |
42 | typedef struct material
43 | {
44 | int32_t id; // nu
45 | char name[50]; // na
46 | bool thc_status; // th
47 | union {
48 | float params[12];
49 | struct {
50 | float pierce_height; // ph - mandatory
51 | float pierce_delay; // pd - mandatory
52 | float feed_rate; // fr - mandatory
53 | float cut_height; // ch - mandatory
54 | float cut_voltage; // cv
55 | float pause_at_end; // pe
56 | float kerf_width; // kw
57 | float cut_amps; // ca - PowerMax
58 | float gas_pressure; // gp - PowerMax
59 | float cut_mode; // cm - PowerMax
60 | float jump_height; // jh
61 | float jump_delay; // jd
62 | };
63 | };
64 | struct material *next;
65 | } material_t;
66 |
67 | typedef bool plasma_enumerate_materials_callback_ptr (material_t *material, void *data);
68 | typedef bool (*plasma_set_current_ptr)(float current);
69 | typedef bool (*plasma_set_pressure_ptr)(float psi);
70 | typedef bool (*plasma_set_cut_mode_ptr)(uint16_t mode);
71 |
72 | typedef struct {
73 | plasma_set_current_ptr set_current;
74 | plasma_set_pressure_ptr set_pressure;
75 | plasma_set_cut_mode_ptr set_cut_mode;
76 | } plasma_control_t;
77 |
78 | extern plasma_control_t cutter;
79 |
80 | bool plasma_material_is_valid (material_t *material);
81 | bool plasma_material_add (material_t *material, bool overwrite);
82 | bool plasma_enumerate_materials (plasma_enumerate_materials_callback_ptr callback, void *data);
83 |
84 | #endif
85 |
--------------------------------------------------------------------------------