├── ArtNetNode
├── ArtNetNode.ino
├── LICENSE
├── Makefile
├── artnet.h
├── e131.h
├── espArtNetRDM.cpp
├── espArtNetRDM.h
├── espDMX_RDM.cpp
├── espDMX_RDM.h
├── rdm.h
├── rdmDataTypes.h
├── rdmFIFO.cpp
├── rdmFIFO.h
├── serialLEDDriver.cpp
├── serialLEDDriver.h
├── wsFX.cpp
└── wsFX.h
├── LICENSE
├── README.md
└── style.css
/ArtNetNode/LICENSE:
--------------------------------------------------------------------------------
1 | GNU GENERAL PUBLIC LICENSE
2 | Version 3, 29 June 2007
3 |
4 | Copyright (C) 2007 Free Software Foundation, Inc.
5 | Everyone is permitted to copy and distribute verbatim copies
6 | of this license document, but changing it is not allowed.
7 |
8 | Preamble
9 |
10 | The GNU General Public License is a free, copyleft license for
11 | software and other kinds of works.
12 |
13 | The licenses for most software and other practical works are designed
14 | to take away your freedom to share and change the works. By contrast,
15 | the GNU General Public License is intended to guarantee your freedom to
16 | share and change all versions of a program--to make sure it remains free
17 | software for all its users. We, the Free Software Foundation, use the
18 | GNU General Public License for most of our software; it applies also to
19 | any other work released this way by its authors. You can apply it to
20 | your programs, too.
21 |
22 | When we speak of free software, we are referring to freedom, not
23 | price. Our General Public Licenses are designed to make sure that you
24 | have the freedom to distribute copies of free software (and charge for
25 | them if you wish), that you receive source code or can get it if you
26 | want it, that you can change the software or use pieces of it in new
27 | free programs, and that you know you can do these things.
28 |
29 | To protect your rights, we need to prevent others from denying you
30 | these rights or asking you to surrender the rights. Therefore, you have
31 | certain responsibilities if you distribute copies of the software, or if
32 | you modify it: responsibilities to respect the freedom of others.
33 |
34 | For example, if you distribute copies of such a program, whether
35 | gratis or for a fee, you must pass on to the recipients the same
36 | freedoms that you received. You must make sure that they, too, receive
37 | or can get the source code. And you must show them these terms so they
38 | know their rights.
39 |
40 | Developers that use the GNU GPL protect your rights with two steps:
41 | (1) assert copyright on the software, and (2) offer you this License
42 | giving you legal permission to copy, distribute and/or modify it.
43 |
44 | For the developers' and authors' protection, the GPL clearly explains
45 | that there is no warranty for this free software. For both users' and
46 | authors' sake, the GPL requires that modified versions be marked as
47 | changed, so that their problems will not be attributed erroneously to
48 | authors of previous versions.
49 |
50 | Some devices are designed to deny users access to install or run
51 | modified versions of the software inside them, although the manufacturer
52 | can do so. This is fundamentally incompatible with the aim of
53 | protecting users' freedom to change the software. The systematic
54 | pattern of such abuse occurs in the area of products for individuals to
55 | use, which is precisely where it is most unacceptable. Therefore, we
56 | have designed this version of the GPL to prohibit the practice for those
57 | products. If such problems arise substantially in other domains, we
58 | stand ready to extend this provision to those domains in future versions
59 | of the GPL, as needed to protect the freedom of users.
60 |
61 | Finally, every program is threatened constantly by software patents.
62 | States should not allow patents to restrict development and use of
63 | software on general-purpose computers, but in those that do, we wish to
64 | avoid the special danger that patents applied to a free program could
65 | make it effectively proprietary. To prevent this, the GPL assures that
66 | patents cannot be used to render the program non-free.
67 |
68 | The precise terms and conditions for copying, distribution and
69 | modification follow.
70 |
71 | TERMS AND CONDITIONS
72 |
73 | 0. Definitions.
74 |
75 | "This License" refers to version 3 of the GNU General Public License.
76 |
77 | "Copyright" also means copyright-like laws that apply to other kinds of
78 | works, such as semiconductor masks.
79 |
80 | "The Program" refers to any copyrightable work licensed under this
81 | License. Each licensee is addressed as "you". "Licensees" and
82 | "recipients" may be individuals or organizations.
83 |
84 | To "modify" a work means to copy from or adapt all or part of the work
85 | in a fashion requiring copyright permission, other than the making of an
86 | exact copy. The resulting work is called a "modified version" of the
87 | earlier work or a work "based on" the earlier work.
88 |
89 | A "covered work" means either the unmodified Program or a work based
90 | on the Program.
91 |
92 | To "propagate" a work means to do anything with it that, without
93 | permission, would make you directly or secondarily liable for
94 | infringement under applicable copyright law, except executing it on a
95 | computer or modifying a private copy. Propagation includes copying,
96 | distribution (with or without modification), making available to the
97 | public, and in some countries other activities as well.
98 |
99 | To "convey" a work means any kind of propagation that enables other
100 | parties to make or receive copies. Mere interaction with a user through
101 | a computer network, with no transfer of a copy, is not conveying.
102 |
103 | An interactive user interface displays "Appropriate Legal Notices"
104 | to the extent that it includes a convenient and prominently visible
105 | feature that (1) displays an appropriate copyright notice, and (2)
106 | tells the user that there is no warranty for the work (except to the
107 | extent that warranties are provided), that licensees may convey the
108 | work under this License, and how to view a copy of this License. If
109 | the interface presents a list of user commands or options, such as a
110 | menu, a prominent item in the list meets this criterion.
111 |
112 | 1. Source Code.
113 |
114 | The "source code" for a work means the preferred form of the work
115 | for making modifications to it. "Object code" means any non-source
116 | form of a work.
117 |
118 | A "Standard Interface" means an interface that either is an official
119 | standard defined by a recognized standards body, or, in the case of
120 | interfaces specified for a particular programming language, one that
121 | is widely used among developers working in that language.
122 |
123 | The "System Libraries" of an executable work include anything, other
124 | than the work as a whole, that (a) is included in the normal form of
125 | packaging a Major Component, but which is not part of that Major
126 | Component, and (b) serves only to enable use of the work with that
127 | Major Component, or to implement a Standard Interface for which an
128 | implementation is available to the public in source code form. A
129 | "Major Component", in this context, means a major essential component
130 | (kernel, window system, and so on) of the specific operating system
131 | (if any) on which the executable work runs, or a compiler used to
132 | produce the work, or an object code interpreter used to run it.
133 |
134 | The "Corresponding Source" for a work in object code form means all
135 | the source code needed to generate, install, and (for an executable
136 | work) run the object code and to modify the work, including scripts to
137 | control those activities. However, it does not include the work's
138 | System Libraries, or general-purpose tools or generally available free
139 | programs which are used unmodified in performing those activities but
140 | which are not part of the work. For example, Corresponding Source
141 | includes interface definition files associated with source files for
142 | the work, and the source code for shared libraries and dynamically
143 | linked subprograms that the work is specifically designed to require,
144 | such as by intimate data communication or control flow between those
145 | subprograms and other parts of the work.
146 |
147 | The Corresponding Source need not include anything that users
148 | can regenerate automatically from other parts of the Corresponding
149 | Source.
150 |
151 | The Corresponding Source for a work in source code form is that
152 | same work.
153 |
154 | 2. Basic Permissions.
155 |
156 | All rights granted under this License are granted for the term of
157 | copyright on the Program, and are irrevocable provided the stated
158 | conditions are met. This License explicitly affirms your unlimited
159 | permission to run the unmodified Program. The output from running a
160 | covered work is covered by this License only if the output, given its
161 | content, constitutes a covered work. This License acknowledges your
162 | rights of fair use or other equivalent, as provided by copyright law.
163 |
164 | You may make, run and propagate covered works that you do not
165 | convey, without conditions so long as your license otherwise remains
166 | in force. You may convey covered works to others for the sole purpose
167 | of having them make modifications exclusively for you, or provide you
168 | with facilities for running those works, provided that you comply with
169 | the terms of this License in conveying all material for which you do
170 | not control copyright. Those thus making or running the covered works
171 | for you must do so exclusively on your behalf, under your direction
172 | and control, on terms that prohibit them from making any copies of
173 | your copyrighted material outside their relationship with you.
174 |
175 | Conveying under any other circumstances is permitted solely under
176 | the conditions stated below. Sublicensing is not allowed; section 10
177 | makes it unnecessary.
178 |
179 | 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
180 |
181 | No covered work shall be deemed part of an effective technological
182 | measure under any applicable law fulfilling obligations under article
183 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or
184 | similar laws prohibiting or restricting circumvention of such
185 | measures.
186 |
187 | When you convey a covered work, you waive any legal power to forbid
188 | circumvention of technological measures to the extent such circumvention
189 | is effected by exercising rights under this License with respect to
190 | the covered work, and you disclaim any intention to limit operation or
191 | modification of the work as a means of enforcing, against the work's
192 | users, your or third parties' legal rights to forbid circumvention of
193 | technological measures.
194 |
195 | 4. Conveying Verbatim Copies.
196 |
197 | You may convey verbatim copies of the Program's source code as you
198 | receive it, in any medium, provided that you conspicuously and
199 | appropriately publish on each copy an appropriate copyright notice;
200 | keep intact all notices stating that this License and any
201 | non-permissive terms added in accord with section 7 apply to the code;
202 | keep intact all notices of the absence of any warranty; and give all
203 | recipients a copy of this License along with the Program.
204 |
205 | You may charge any price or no price for each copy that you convey,
206 | and you may offer support or warranty protection for a fee.
207 |
208 | 5. Conveying Modified Source Versions.
209 |
210 | You may convey a work based on the Program, or the modifications to
211 | produce it from the Program, in the form of source code under the
212 | terms of section 4, provided that you also meet all of these conditions:
213 |
214 | a) The work must carry prominent notices stating that you modified
215 | it, and giving a relevant date.
216 |
217 | b) The work must carry prominent notices stating that it is
218 | released under this License and any conditions added under section
219 | 7. This requirement modifies the requirement in section 4 to
220 | "keep intact all notices".
221 |
222 | c) You must license the entire work, as a whole, under this
223 | License to anyone who comes into possession of a copy. This
224 | License will therefore apply, along with any applicable section 7
225 | additional terms, to the whole of the work, and all its parts,
226 | regardless of how they are packaged. This License gives no
227 | permission to license the work in any other way, but it does not
228 | invalidate such permission if you have separately received it.
229 |
230 | d) If the work has interactive user interfaces, each must display
231 | Appropriate Legal Notices; however, if the Program has interactive
232 | interfaces that do not display Appropriate Legal Notices, your
233 | work need not make them do so.
234 |
235 | A compilation of a covered work with other separate and independent
236 | works, which are not by their nature extensions of the covered work,
237 | and which are not combined with it such as to form a larger program,
238 | in or on a volume of a storage or distribution medium, is called an
239 | "aggregate" if the compilation and its resulting copyright are not
240 | used to limit the access or legal rights of the compilation's users
241 | beyond what the individual works permit. Inclusion of a covered work
242 | in an aggregate does not cause this License to apply to the other
243 | parts of the aggregate.
244 |
245 | 6. Conveying Non-Source Forms.
246 |
247 | You may convey a covered work in object code form under the terms
248 | of sections 4 and 5, provided that you also convey the
249 | machine-readable Corresponding Source under the terms of this License,
250 | in one of these ways:
251 |
252 | a) Convey the object code in, or embodied in, a physical product
253 | (including a physical distribution medium), accompanied by the
254 | Corresponding Source fixed on a durable physical medium
255 | customarily used for software interchange.
256 |
257 | b) Convey the object code in, or embodied in, a physical product
258 | (including a physical distribution medium), accompanied by a
259 | written offer, valid for at least three years and valid for as
260 | long as you offer spare parts or customer support for that product
261 | model, to give anyone who possesses the object code either (1) a
262 | copy of the Corresponding Source for all the software in the
263 | product that is covered by this License, on a durable physical
264 | medium customarily used for software interchange, for a price no
265 | more than your reasonable cost of physically performing this
266 | conveying of source, or (2) access to copy the
267 | Corresponding Source from a network server at no charge.
268 |
269 | c) Convey individual copies of the object code with a copy of the
270 | written offer to provide the Corresponding Source. This
271 | alternative is allowed only occasionally and noncommercially, and
272 | only if you received the object code with such an offer, in accord
273 | with subsection 6b.
274 |
275 | d) Convey the object code by offering access from a designated
276 | place (gratis or for a charge), and offer equivalent access to the
277 | Corresponding Source in the same way through the same place at no
278 | further charge. You need not require recipients to copy the
279 | Corresponding Source along with the object code. If the place to
280 | copy the object code is a network server, the Corresponding Source
281 | may be on a different server (operated by you or a third party)
282 | that supports equivalent copying facilities, provided you maintain
283 | clear directions next to the object code saying where to find the
284 | Corresponding Source. Regardless of what server hosts the
285 | Corresponding Source, you remain obligated to ensure that it is
286 | available for as long as needed to satisfy these requirements.
287 |
288 | e) Convey the object code using peer-to-peer transmission, provided
289 | you inform other peers where the object code and Corresponding
290 | Source of the work are being offered to the general public at no
291 | charge under subsection 6d.
292 |
293 | A separable portion of the object code, whose source code is excluded
294 | from the Corresponding Source as a System Library, need not be
295 | included in conveying the object code work.
296 |
297 | A "User Product" is either (1) a "consumer product", which means any
298 | tangible personal property which is normally used for personal, family,
299 | or household purposes, or (2) anything designed or sold for incorporation
300 | into a dwelling. In determining whether a product is a consumer product,
301 | doubtful cases shall be resolved in favor of coverage. For a particular
302 | product received by a particular user, "normally used" refers to a
303 | typical or common use of that class of product, regardless of the status
304 | of the particular user or of the way in which the particular user
305 | actually uses, or expects or is expected to use, the product. A product
306 | is a consumer product regardless of whether the product has substantial
307 | commercial, industrial or non-consumer uses, unless such uses represent
308 | the only significant mode of use of the product.
309 |
310 | "Installation Information" for a User Product means any methods,
311 | procedures, authorization keys, or other information required to install
312 | and execute modified versions of a covered work in that User Product from
313 | a modified version of its Corresponding Source. The information must
314 | suffice to ensure that the continued functioning of the modified object
315 | code is in no case prevented or interfered with solely because
316 | modification has been made.
317 |
318 | If you convey an object code work under this section in, or with, or
319 | specifically for use in, a User Product, and the conveying occurs as
320 | part of a transaction in which the right of possession and use of the
321 | User Product is transferred to the recipient in perpetuity or for a
322 | fixed term (regardless of how the transaction is characterized), the
323 | Corresponding Source conveyed under this section must be accompanied
324 | by the Installation Information. But this requirement does not apply
325 | if neither you nor any third party retains the ability to install
326 | modified object code on the User Product (for example, the work has
327 | been installed in ROM).
328 |
329 | The requirement to provide Installation Information does not include a
330 | requirement to continue to provide support service, warranty, or updates
331 | for a work that has been modified or installed by the recipient, or for
332 | the User Product in which it has been modified or installed. Access to a
333 | network may be denied when the modification itself materially and
334 | adversely affects the operation of the network or violates the rules and
335 | protocols for communication across the network.
336 |
337 | Corresponding Source conveyed, and Installation Information provided,
338 | in accord with this section must be in a format that is publicly
339 | documented (and with an implementation available to the public in
340 | source code form), and must require no special password or key for
341 | unpacking, reading or copying.
342 |
343 | 7. Additional Terms.
344 |
345 | "Additional permissions" are terms that supplement the terms of this
346 | License by making exceptions from one or more of its conditions.
347 | Additional permissions that are applicable to the entire Program shall
348 | be treated as though they were included in this License, to the extent
349 | that they are valid under applicable law. If additional permissions
350 | apply only to part of the Program, that part may be used separately
351 | under those permissions, but the entire Program remains governed by
352 | this License without regard to the additional permissions.
353 |
354 | When you convey a copy of a covered work, you may at your option
355 | remove any additional permissions from that copy, or from any part of
356 | it. (Additional permissions may be written to require their own
357 | removal in certain cases when you modify the work.) You may place
358 | additional permissions on material, added by you to a covered work,
359 | for which you have or can give appropriate copyright permission.
360 |
361 | Notwithstanding any other provision of this License, for material you
362 | add to a covered work, you may (if authorized by the copyright holders of
363 | that material) supplement the terms of this License with terms:
364 |
365 | a) Disclaiming warranty or limiting liability differently from the
366 | terms of sections 15 and 16 of this License; or
367 |
368 | b) Requiring preservation of specified reasonable legal notices or
369 | author attributions in that material or in the Appropriate Legal
370 | Notices displayed by works containing it; or
371 |
372 | c) Prohibiting misrepresentation of the origin of that material, or
373 | requiring that modified versions of such material be marked in
374 | reasonable ways as different from the original version; or
375 |
376 | d) Limiting the use for publicity purposes of names of licensors or
377 | authors of the material; or
378 |
379 | e) Declining to grant rights under trademark law for use of some
380 | trade names, trademarks, or service marks; or
381 |
382 | f) Requiring indemnification of licensors and authors of that
383 | material by anyone who conveys the material (or modified versions of
384 | it) with contractual assumptions of liability to the recipient, for
385 | any liability that these contractual assumptions directly impose on
386 | those licensors and authors.
387 |
388 | All other non-permissive additional terms are considered "further
389 | restrictions" within the meaning of section 10. If the Program as you
390 | received it, or any part of it, contains a notice stating that it is
391 | governed by this License along with a term that is a further
392 | restriction, you may remove that term. If a license document contains
393 | a further restriction but permits relicensing or conveying under this
394 | License, you may add to a covered work material governed by the terms
395 | of that license document, provided that the further restriction does
396 | not survive such relicensing or conveying.
397 |
398 | If you add terms to a covered work in accord with this section, you
399 | must place, in the relevant source files, a statement of the
400 | additional terms that apply to those files, or a notice indicating
401 | where to find the applicable terms.
402 |
403 | Additional terms, permissive or non-permissive, may be stated in the
404 | form of a separately written license, or stated as exceptions;
405 | the above requirements apply either way.
406 |
407 | 8. Termination.
408 |
409 | You may not propagate or modify a covered work except as expressly
410 | provided under this License. Any attempt otherwise to propagate or
411 | modify it is void, and will automatically terminate your rights under
412 | this License (including any patent licenses granted under the third
413 | paragraph of section 11).
414 |
415 | However, if you cease all violation of this License, then your
416 | license from a particular copyright holder is reinstated (a)
417 | provisionally, unless and until the copyright holder explicitly and
418 | finally terminates your license, and (b) permanently, if the copyright
419 | holder fails to notify you of the violation by some reasonable means
420 | prior to 60 days after the cessation.
421 |
422 | Moreover, your license from a particular copyright holder is
423 | reinstated permanently if the copyright holder notifies you of the
424 | violation by some reasonable means, this is the first time you have
425 | received notice of violation of this License (for any work) from that
426 | copyright holder, and you cure the violation prior to 30 days after
427 | your receipt of the notice.
428 |
429 | Termination of your rights under this section does not terminate the
430 | licenses of parties who have received copies or rights from you under
431 | this License. If your rights have been terminated and not permanently
432 | reinstated, you do not qualify to receive new licenses for the same
433 | material under section 10.
434 |
435 | 9. Acceptance Not Required for Having Copies.
436 |
437 | You are not required to accept this License in order to receive or
438 | run a copy of the Program. Ancillary propagation of a covered work
439 | occurring solely as a consequence of using peer-to-peer transmission
440 | to receive a copy likewise does not require acceptance. However,
441 | nothing other than this License grants you permission to propagate or
442 | modify any covered work. These actions infringe copyright if you do
443 | not accept this License. Therefore, by modifying or propagating a
444 | covered work, you indicate your acceptance of this License to do so.
445 |
446 | 10. Automatic Licensing of Downstream Recipients.
447 |
448 | Each time you convey a covered work, the recipient automatically
449 | receives a license from the original licensors, to run, modify and
450 | propagate that work, subject to this License. You are not responsible
451 | for enforcing compliance by third parties with this License.
452 |
453 | An "entity transaction" is a transaction transferring control of an
454 | organization, or substantially all assets of one, or subdividing an
455 | organization, or merging organizations. If propagation of a covered
456 | work results from an entity transaction, each party to that
457 | transaction who receives a copy of the work also receives whatever
458 | licenses to the work the party's predecessor in interest had or could
459 | give under the previous paragraph, plus a right to possession of the
460 | Corresponding Source of the work from the predecessor in interest, if
461 | the predecessor has it or can get it with reasonable efforts.
462 |
463 | You may not impose any further restrictions on the exercise of the
464 | rights granted or affirmed under this License. For example, you may
465 | not impose a license fee, royalty, or other charge for exercise of
466 | rights granted under this License, and you may not initiate litigation
467 | (including a cross-claim or counterclaim in a lawsuit) alleging that
468 | any patent claim is infringed by making, using, selling, offering for
469 | sale, or importing the Program or any portion of it.
470 |
471 | 11. Patents.
472 |
473 | A "contributor" is a copyright holder who authorizes use under this
474 | License of the Program or a work on which the Program is based. The
475 | work thus licensed is called the contributor's "contributor version".
476 |
477 | A contributor's "essential patent claims" are all patent claims
478 | owned or controlled by the contributor, whether already acquired or
479 | hereafter acquired, that would be infringed by some manner, permitted
480 | by this License, of making, using, or selling its contributor version,
481 | but do not include claims that would be infringed only as a
482 | consequence of further modification of the contributor version. For
483 | purposes of this definition, "control" includes the right to grant
484 | patent sublicenses in a manner consistent with the requirements of
485 | this License.
486 |
487 | Each contributor grants you a non-exclusive, worldwide, royalty-free
488 | patent license under the contributor's essential patent claims, to
489 | make, use, sell, offer for sale, import and otherwise run, modify and
490 | propagate the contents of its contributor version.
491 |
492 | In the following three paragraphs, a "patent license" is any express
493 | agreement or commitment, however denominated, not to enforce a patent
494 | (such as an express permission to practice a patent or covenant not to
495 | sue for patent infringement). To "grant" such a patent license to a
496 | party means to make such an agreement or commitment not to enforce a
497 | patent against the party.
498 |
499 | If you convey a covered work, knowingly relying on a patent license,
500 | and the Corresponding Source of the work is not available for anyone
501 | to copy, free of charge and under the terms of this License, through a
502 | publicly available network server or other readily accessible means,
503 | then you must either (1) cause the Corresponding Source to be so
504 | available, or (2) arrange to deprive yourself of the benefit of the
505 | patent license for this particular work, or (3) arrange, in a manner
506 | consistent with the requirements of this License, to extend the patent
507 | license to downstream recipients. "Knowingly relying" means you have
508 | actual knowledge that, but for the patent license, your conveying the
509 | covered work in a country, or your recipient's use of the covered work
510 | in a country, would infringe one or more identifiable patents in that
511 | country that you have reason to believe are valid.
512 |
513 | If, pursuant to or in connection with a single transaction or
514 | arrangement, you convey, or propagate by procuring conveyance of, a
515 | covered work, and grant a patent license to some of the parties
516 | receiving the covered work authorizing them to use, propagate, modify
517 | or convey a specific copy of the covered work, then the patent license
518 | you grant is automatically extended to all recipients of the covered
519 | work and works based on it.
520 |
521 | A patent license is "discriminatory" if it does not include within
522 | the scope of its coverage, prohibits the exercise of, or is
523 | conditioned on the non-exercise of one or more of the rights that are
524 | specifically granted under this License. You may not convey a covered
525 | work if you are a party to an arrangement with a third party that is
526 | in the business of distributing software, under which you make payment
527 | to the third party based on the extent of your activity of conveying
528 | the work, and under which the third party grants, to any of the
529 | parties who would receive the covered work from you, a discriminatory
530 | patent license (a) in connection with copies of the covered work
531 | conveyed by you (or copies made from those copies), or (b) primarily
532 | for and in connection with specific products or compilations that
533 | contain the covered work, unless you entered into that arrangement,
534 | or that patent license was granted, prior to 28 March 2007.
535 |
536 | Nothing in this License shall be construed as excluding or limiting
537 | any implied license or other defenses to infringement that may
538 | otherwise be available to you under applicable patent law.
539 |
540 | 12. No Surrender of Others' Freedom.
541 |
542 | If conditions are imposed on you (whether by court order, agreement or
543 | otherwise) that contradict the conditions of this License, they do not
544 | excuse you from the conditions of this License. If you cannot convey a
545 | covered work so as to satisfy simultaneously your obligations under this
546 | License and any other pertinent obligations, then as a consequence you may
547 | not convey it at all. For example, if you agree to terms that obligate you
548 | to collect a royalty for further conveying from those to whom you convey
549 | the Program, the only way you could satisfy both those terms and this
550 | License would be to refrain entirely from conveying the Program.
551 |
552 | 13. Use with the GNU Affero General Public License.
553 |
554 | Notwithstanding any other provision of this License, you have
555 | permission to link or combine any covered work with a work licensed
556 | under version 3 of the GNU Affero General Public License into a single
557 | combined work, and to convey the resulting work. The terms of this
558 | License will continue to apply to the part which is the covered work,
559 | but the special requirements of the GNU Affero General Public License,
560 | section 13, concerning interaction through a network will apply to the
561 | combination as such.
562 |
563 | 14. Revised Versions of this License.
564 |
565 | The Free Software Foundation may publish revised and/or new versions of
566 | the GNU General Public License from time to time. Such new versions will
567 | be similar in spirit to the present version, but may differ in detail to
568 | address new problems or concerns.
569 |
570 | Each version is given a distinguishing version number. If the
571 | Program specifies that a certain numbered version of the GNU General
572 | Public License "or any later version" applies to it, you have the
573 | option of following the terms and conditions either of that numbered
574 | version or of any later version published by the Free Software
575 | Foundation. If the Program does not specify a version number of the
576 | GNU General Public License, you may choose any version ever published
577 | by the Free Software Foundation.
578 |
579 | If the Program specifies that a proxy can decide which future
580 | versions of the GNU General Public License can be used, that proxy's
581 | public statement of acceptance of a version permanently authorizes you
582 | to choose that version for the Program.
583 |
584 | Later license versions may give you additional or different
585 | permissions. However, no additional obligations are imposed on any
586 | author or copyright holder as a result of your choosing to follow a
587 | later version.
588 |
589 | 15. Disclaimer of Warranty.
590 |
591 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
592 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
593 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
594 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
595 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
596 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
597 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
598 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
599 |
600 | 16. Limitation of Liability.
601 |
602 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
603 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
604 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
605 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
606 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
607 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
608 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
609 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
610 | SUCH DAMAGES.
611 |
612 | 17. Interpretation of Sections 15 and 16.
613 |
614 | If the disclaimer of warranty and limitation of liability provided
615 | above cannot be given local legal effect according to their terms,
616 | reviewing courts shall apply local law that most closely approximates
617 | an absolute waiver of all civil liability in connection with the
618 | Program, unless a warranty or assumption of liability accompanies a
619 | copy of the Program in return for a fee.
620 |
621 | END OF TERMS AND CONDITIONS
622 |
623 | How to Apply These Terms to Your New Programs
624 |
625 | If you develop a new program, and you want it to be of the greatest
626 | possible use to the public, the best way to achieve this is to make it
627 | free software which everyone can redistribute and change under these terms.
628 |
629 | To do so, attach the following notices to the program. It is safest
630 | to attach them to the start of each source file to most effectively
631 | state the exclusion of warranty; and each file should have at least
632 | the "copyright" line and a pointer to where the full notice is found.
633 |
634 | {one line to give the program's name and a brief idea of what it does.}
635 | Copyright (C) {year} {name of author}
636 |
637 | This program is free software: you can redistribute it and/or modify
638 | it under the terms of the GNU General Public License as published by
639 | the Free Software Foundation, either version 3 of the License, or
640 | (at your option) any later version.
641 |
642 | This program is distributed in the hope that it will be useful,
643 | but WITHOUT ANY WARRANTY; without even the implied warranty of
644 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
645 | GNU General Public License for more details.
646 |
647 | You should have received a copy of the GNU General Public License
648 | along with this program. If not, see .
649 |
650 | Also add information on how to contact you by electronic and paper mail.
651 |
652 | If the program does terminal interaction, make it output a short
653 | notice like this when it starts in an interactive mode:
654 |
655 | {project} Copyright (C) {year} {fullname}
656 | This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
657 | This is free software, and you are welcome to redistribute it
658 | under certain conditions; type `show c' for details.
659 |
660 | The hypothetical commands `show w' and `show c' should show the appropriate
661 | parts of the General Public License. Of course, your program's commands
662 | might be different; for a GUI interface, you would use an "about box".
663 |
664 | You should also get your employer (if you work as a programmer) or school,
665 | if any, to sign a "copyright disclaimer" for the program, if necessary.
666 | For more information on this, and how to apply and follow the GNU GPL, see
667 | .
668 |
669 | The GNU General Public License does not permit incorporating your program
670 | into proprietary programs. If your program is a subroutine library, you
671 | may consider it more useful to permit linking proprietary applications with
672 | the library. If this is what you want to do, use the GNU Lesser General
673 | Public License instead of this License. But first, please read
674 | .
675 |
--------------------------------------------------------------------------------
/ArtNetNode/Makefile:
--------------------------------------------------------------------------------
1 | # My makefile
2 | SKETCH = ArtNetNode.ino
3 | CHIP=esp32
4 | ESP_ROOT=/home/turo/Arduino/hardware/espressif/esp32
5 |
6 | LIBS=/home/turo/Arduino/libraries/ArduinoJson/src \
7 | $(ESP_LIBS)/SPI \
8 | $(ESP_LIBS)/WiFi \
9 | $(ESP_LIBS)/WebServer \
10 | $(ESP_LIBS)/EEPROM \
11 | $(ESP_LIBS)/FS \
12 | $(ESP_LIBS)/SPIFFS \
13 | $(ESP_LIBS)/Update
14 |
15 | UPLOAD_PORT = /dev/ttyUSB0
16 | UPLOAD_SPEED = 3000000
17 | BOARD = esp32-poe
18 |
19 | include $(HOME)/makeEspArduino/makeEspArduino.mk
20 |
--------------------------------------------------------------------------------
/ArtNetNode/artnet.h:
--------------------------------------------------------------------------------
1 |
2 | /*
3 | espArtNetRDM v1 (pre-release) library
4 | Copyright (c) 2016, Matthew Tong
5 | https://github.com/mtongnz/
6 | Modified from https://github.com/forkineye/E131/blob/master/E131.h
7 | This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public
8 | License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any
9 | later version.
10 | This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
11 | warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
12 | You should have received a copy of the GNU General Public License along with this program.
13 | If not, see http://www.gnu.org/licenses/
14 | */
15 | #ifndef artnet_data_h
16 | #define artnet_data_h
17 |
18 | #define ARTNET_PORT 6454
19 | #define ARTNET_BUFFER_MAX 600
20 | #define ARTNET_REPLY_SIZE 239
21 | #define ARTNET_IP_PROG_REPLY_SIZE 34
22 | #define ARTNET_RDM_REPLY_SIZE 24
23 | #define ARTNET_TOD_DATA_SIZE 28
24 | #define ARTNET_ADDRESS_OFFSET 18
25 | #define ARTNET_SHORT_NAME_LENGTH 18
26 | #define ARTNET_LONG_NAME_LENGTH 64
27 | #define ARTNET_NODE_REPORT_LENGTH 64
28 | #define ARTNET_CANCEL_MERGE_TIMEOUT 2500
29 | #define DMX_BUFFER_SIZE 512
30 | #define DMX_MAX_CHANS 512
31 |
32 | // Artnet Op Codes
33 | #define ARTNET_ARTPOLL 0x2000
34 | #define ARTNET_ARTPOLL_REPLY 0x2100
35 | #define ARTNET_DIAG_DATA 0x2300
36 | #define ARTNET_COMMAND 0x2400
37 | #define ARTNET_ARTDMX 0x5000
38 | #define ARTNET_NZS 0x5100
39 | #define ARTNET_SYNC 0x5200
40 | #define ARTNET_ADDRESS 0x6000
41 | #define ARTNET_INPUT 0x7000
42 | #define ARTNET_TOD_REQUEST 0x8000
43 | #define ARTNET_TOD_DATA 0x8100
44 | #define ARTNET_TOD_CONTROL 0x8200
45 | #define ARTNET_RDM 0x8300
46 | #define ARTNET_RDM_SUB 0x8400
47 | #define ARTNET_FIRMWARE_MASTER 0xF200
48 | #define ARTNET_FIRMWARE_REPLY 0xF300
49 | #define ARTNET_IP_PROG 0xF800
50 | #define ARTNET_IP_PROG_REPLY 0xF900
51 |
52 | // Artnet Node Report Codes
53 | #define ARTNET_RC_DEBUG 0x0000
54 | #define ARTNET_RC_POWER_OK 0x0001
55 | #define ARTNET_RC_POWER_FAIL 0x0002
56 | #define ARTNET_RC_SH_NAME_OK 0x0006
57 | #define ARTNET_RC_LO_NAME_OK 0x0007
58 | #define ARTNET_RC_FIRMWARE_FAIL 0x000E
59 |
60 | // Artnet Command Codes
61 | #define ARTNET_AC_NONE 0x00
62 | #define ARTNET_AC_CANCEL_MERGE 0x01
63 | #define ARTNET_AC_LED_NORMAL 0x02
64 | #define ARTNET_AC_LED_MUTE 0x03
65 | #define ARTNET_AC_LED_LOCATE 0x04
66 | #define ARTNET_AC_RESET_RX_FLAGS 0x05
67 | #define ARTNET_AC_MERGE_LTP_0 0x10
68 | #define ARTNET_AC_MERGE_LTP_1 0x11
69 | #define ARTNET_AC_MERGE_LTP_2 0x12
70 | #define ARTNET_AC_MERGE_LTP_3 0x13
71 | #define ARTNET_AC_MERGE_HTP_0 0x50
72 | #define ARTNET_AC_MERGE_HTP_1 0x51
73 | #define ARTNET_AC_MERGE_HTP_2 0x52
74 | #define ARTNET_AC_MERGE_HTP_3 0x53
75 | #define ARTNET_AC_CLEAR_OP_0 0x90
76 | #define ARTNET_AC_CLEAR_OP_1 0x91
77 | #define ARTNET_AC_CLEAR_OP_2 0x92
78 | #define ARTNET_AC_CLEAR_OP_3 0x93
79 | #define ARTNET_AC_ARTNET_SEL_0 0x60
80 | #define ARTNET_AC_ARTNET_SEL_1 0x61
81 | #define ARTNET_AC_ARTNET_SEL_2 0x62
82 | #define ARTNET_AC_ARTNET_SEL_3 0x63
83 | #define ARTNET_AC_ACN_SEL_0 0x70
84 | #define ARTNET_AC_ACN_SEL_1 0x71
85 | #define ARTNET_AC_ACN_SEL_2 0x72
86 | #define ARTNET_AC_ACN_SEL_3 0x73
87 |
88 | #endif
89 |
--------------------------------------------------------------------------------
/ArtNetNode/e131.h:
--------------------------------------------------------------------------------
1 | /*
2 | espArtNetRDM v1 (pre-release) library
3 | Copyright (c) 2016, Matthew Tong
4 | https://github.com/mtongnz/
5 | Modified from https://github.com/forkineye/E131/blob/master/E131.h
6 | This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public
7 | License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any
8 | later version.
9 | This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
10 | warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
11 | You should have received a copy of the GNU General Public License along with this program.
12 | If not, see http://www.gnu.org/licenses/
13 | */
14 | #ifndef e131_data_h
15 | #define e131_data_h
16 |
17 | #define E131_PORT 5568
18 | #define E131_BUFFER_MAX 638
19 |
20 | /* E1.31 Packet Structure */
21 | typedef union {
22 | struct {
23 | /* Root Layer */
24 | uint16_t preamble_size;
25 | uint16_t postamble_size;
26 | uint8_t acn_id[12];
27 | uint16_t root_flength;
28 | uint32_t root_vector;
29 | uint8_t cid[16];
30 |
31 | /* Frame Layer */
32 | uint16_t frame_flength;
33 | uint32_t frame_vector;
34 | uint8_t source_name[64];
35 | uint8_t priority;
36 | uint16_t reserved;
37 | uint8_t sequence_number;
38 | uint8_t options;
39 | uint16_t universe;
40 |
41 | /* DMP Layer */
42 | uint16_t dmp_flength;
43 | uint8_t dmp_vector;
44 | uint8_t type;
45 | uint16_t first_address;
46 | uint16_t address_increment;
47 | uint16_t property_value_count;
48 | uint8_t property_values[513];
49 | } __attribute__((packed));
50 |
51 | uint8_t raw[E131_BUFFER_MAX];
52 | } e131_packet_t;
53 |
54 | /* Constants for packet validation */
55 | static const uint8_t ACN_ID[12] = { 0x41, 0x53, 0x43, 0x2d, 0x45, 0x31, 0x2e, 0x31, 0x37, 0x00, 0x00, 0x00 };
56 | static const uint32_t VECTOR_ROOT = 4;
57 | static const uint32_t VECTOR_FRAME = 2;
58 | static const uint8_t VECTOR_DMP = 2;
59 |
60 | #endif
61 |
--------------------------------------------------------------------------------
/ArtNetNode/espArtNetRDM.cpp:
--------------------------------------------------------------------------------
1 | /*
2 | espArtNetRDM v1 (pre-release) library
3 | Copyright (c) 2016, Matthew Tong
4 | https://github.com/mtongnz/
5 | Modified from https://github.com/forkineye/E131/blob/master/E131.h
6 | This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public
7 | License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any
8 | later version.
9 | This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
10 | warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
11 | You should have received a copy of the GNU General Public License along with this program.
12 | If not, see http://www.gnu.org/licenses/
13 | */
14 | #include "espArtNetRDM.h"
15 |
16 | static void artClearDMXBuffer(uint8_t* buf) {
17 | memset(buf, 0, DMX_BUFFER_SIZE);
18 | }
19 |
20 | espArtNetRDM::espArtNetRDM() {
21 | }
22 |
23 | espArtNetRDM::~espArtNetRDM() {
24 | end();
25 | }
26 |
27 | void espArtNetRDM::end() {
28 | if (_art == 0)
29 | return;
30 |
31 | for (uint8_t g = 0; g < _art->numGroups; g++) {
32 | for (uint8_t p = 0; p < 4; p++) {
33 | if (_art->group[g]->ports[p] == 0)
34 | continue;
35 |
36 | if (_art->group[g]->ports[p]->ownBuffer)
37 | free(_art->group[g]->ports[p]->dmxBuffer);
38 |
39 | free(_art->group[g]->ports[p]->ipBuffer);
40 | free(_art->group[g]->ports[p]);
41 | }
42 | free(_art->group[g]);
43 | }
44 | free(_art);
45 |
46 | _art = 0;
47 | }
48 |
49 | void espArtNetRDM::init(IPAddress ip, IPAddress subnet, bool dhcp, const char* shortname, const char* longname, uint16_t oem, uint16_t esta, uint8_t* mac) {
50 | if (_art != 0)
51 | free(_art);
52 |
53 | // Allocate memory for our settings
54 | _art = (artnet_device*) malloc(sizeof(artnet_device));
55 |
56 | delay(1);
57 |
58 | // Store values
59 | _art->firmWareVersion = 0;
60 | _art->numGroups = 0;
61 | _art->nodeReportCounter = 0;
62 | _art->nodeReportCode = ARTNET_RC_POWER_OK;
63 | _art->deviceIP = ip;
64 | _art->subnet = ip;
65 | _art->broadcastIP = IPAddress((uint32_t)ip | ~((uint32_t)subnet));
66 | _art->dhcp = dhcp;
67 | _art->oemLo = (uint8_t)oem;
68 | _art->oemHi = (uint8_t)(oem >> 8);
69 | _art->estaLo = (uint8_t)esta;
70 | _art->estaHi = (uint8_t)(esta >> 8);
71 | _art->syncIP = INADDR_NONE;
72 | _art->lastSync = 0;
73 | _art->nextPollReply = 0;
74 | memcpy(_art->shortName, shortname, ARTNET_SHORT_NAME_LENGTH);
75 | memcpy(_art->longName, longname, ARTNET_LONG_NAME_LENGTH);
76 | memcpy(_art->deviceMAC, mac, 6);
77 | }
78 |
79 | void espArtNetRDM::setFirmwareVersion(uint16_t fw) {
80 | if (_art == 0)
81 | return;
82 |
83 | _art->firmWareVersion = fw;
84 | }
85 |
86 | void espArtNetRDM::setDefaultIP() {
87 | if (_art == 0)
88 | return;
89 |
90 | _art->dhcp = false;
91 | _art->subnet = IPAddress(255, 0, 0, 0);
92 | _art->broadcastIP = IPAddress(2, 255, 255, 255);
93 |
94 | uint8_t b = _art->deviceMAC[3] + _art->oemLo + _art->oemHi;
95 | uint8_t c = _art->deviceMAC[4];
96 | uint8_t d = _art->deviceMAC[5];
97 |
98 | _art->deviceIP = IPAddress(2, b, c, d);
99 | }
100 |
101 | uint8_t espArtNetRDM::addGroup(uint8_t net, uint8_t subnet) {
102 | if (_art == 0)
103 | return 255;
104 |
105 | uint8_t g = _art->numGroups;
106 |
107 | _art->group[g] = (group_def*) malloc(sizeof(group_def));
108 | _art->group[g]->netSwitch = net & 0b01111111;
109 | _art->group[g]->subnet = subnet;
110 | _art->group[g]->numPorts = 0;
111 | _art->group[g]->cancelMergeIP = INADDR_NONE;
112 | _art->group[g]->cancelMerge = 0;
113 | _art->group[g]->cancelMergeTime = 0;
114 |
115 | for (int x = 0; x < 4; x++)
116 | _art->group[g]->ports[x] = 0;
117 |
118 | _art->numGroups++;
119 |
120 | return g;
121 | }
122 |
123 | uint8_t espArtNetRDM::addPort(uint8_t g, uint8_t p, uint8_t universe, uint8_t t, bool htp, uint8_t* buf) {
124 | if (_art == 0)
125 | return 255;
126 |
127 | // Check for a valid universe, group and port number
128 | if (universe > 15 || p >= 4 || g > _art->numGroups)
129 | return 255;
130 |
131 | group_def* group = _art->group[g];
132 |
133 | // Check if port is already initialised, return its port number
134 | if (group->ports[p] != 0)
135 | return p;
136 |
137 | // Allocate space for our port
138 | group->ports[p] = (port_def*) malloc(sizeof(port_def));
139 |
140 | delay(1);
141 | port_def* port = group->ports[p];
142 |
143 | // DMX output buffer allocation
144 | if (buf == 0) {
145 | port->dmxBuffer = (uint8_t*) malloc(DMX_BUFFER_SIZE);
146 | port->ownBuffer = true;
147 | } else {
148 | port->dmxBuffer = buf;
149 | port->ownBuffer = false;
150 | }
151 |
152 | // Clear the buffer
153 | artClearDMXBuffer(port->dmxBuffer);
154 |
155 |
156 | // Store settings
157 | group->numPorts++;
158 | port->portType = t;
159 | port->mergeHTP = htp;
160 | port->portUni = universe;
161 | port->senderIP[0] = INADDR_NONE;
162 | port->senderIP[1] = INADDR_NONE;
163 |
164 | for (uint8_t x = 0; x < 5; x++)
165 | port->rdmSenderIP[x] = INADDR_NONE;
166 |
167 | port->ipBuffer = 0;
168 | port->ipChans[0] = 0;
169 | port->ipChans[1] = 0;
170 | port->dmxChans = 0;
171 | port->merging = 0;
172 | port->lastTodCommand = 0;
173 | port->uidTotal = 0;
174 | port->todAvailable = 0;
175 |
176 | return p;
177 | }
178 |
179 | bool espArtNetRDM::closePort(uint8_t g, uint8_t p) {
180 | if (_art == 0 || g >= _art->numGroups)
181 | return false;
182 |
183 | group_def* group = _art->group[g];
184 |
185 | // Port already closed
186 | if (group->ports[p] == 0)
187 | return true;
188 |
189 | // Delete buffers
190 | if (group->ports[p]->ownBuffer)
191 | free(group->ports[p]->dmxBuffer);
192 | if (group->ports[p]->ipBuffer != 0)
193 | free(group->ports[p]->ipBuffer);
194 |
195 | free(group->ports[p]);
196 |
197 | // Mark port as empty
198 | group->ports[p] = 0;
199 | group->numPorts--;
200 | return true;
201 | }
202 |
203 | void espArtNetRDM::setArtDMXCallback(artDMXCallBack callback) {
204 | if (_art == 0)
205 | return;
206 |
207 | _art->dmxCallBack = callback;
208 | }
209 |
210 | void espArtNetRDM::setArtSyncCallback(artSyncCallBack callback) {
211 | if (_art == 0)
212 | return;
213 |
214 | _art->syncCallBack = callback;
215 | }
216 |
217 | void espArtNetRDM::setArtRDMCallback(artRDMCallBack callback) {
218 | if (_art == 0)
219 | return;
220 |
221 | _art->rdmCallBack = callback;
222 | }
223 |
224 | void espArtNetRDM::setArtIPCallback(artIPCallBack callback) {
225 | if (_art == 0)
226 | return;
227 |
228 | _art->ipCallBack = callback;
229 | }
230 |
231 | void espArtNetRDM::setArtAddressCallback(artAddressCallBack callback) {
232 | if (_art == 0)
233 | return;
234 |
235 | _art->addressCallBack = callback;
236 | }
237 |
238 | void espArtNetRDM::setTODRequestCallback(artTodRequestCallBack callback) {
239 | if (_art == 0)
240 | return;
241 |
242 | _art->todRequestCallBack = callback;
243 | }
244 |
245 | void espArtNetRDM::setTODFlushCallback(artTodFlushCallBack callback) {
246 | if (_art == 0)
247 | return;
248 |
249 | _art->todFlushCallBack = callback;
250 | }
251 |
252 | void espArtNetRDM::begin() {
253 | if (_art == 0)
254 | return;
255 |
256 | // Start listening for UDP packets
257 | eUDP.begin(ARTNET_PORT);
258 | eUDP.flush();
259 | fUDP.begin(E131_PORT);
260 | fUDP.flush();
261 |
262 | // Send ArtPollReply to tell everyone we're here
263 | artPollReply();
264 | }
265 |
266 | void espArtNetRDM::pause() {
267 | if (_art == 0)
268 | return;
269 |
270 | eUDP.flush();
271 | }
272 |
273 | void espArtNetRDM::handler() {
274 | if (_art == 0)
275 | return;
276 |
277 | // Artnet packet
278 | uint16_t packetSize = eUDP.parsePacket();
279 |
280 | if (packetSize > 0) {
281 |
282 | unsigned char _artBuffer[ARTNET_BUFFER_MAX];
283 |
284 | // Read data into buffer
285 | eUDP.read(_artBuffer, packetSize);
286 |
287 | // Get the Op Code
288 | int opCode = _artOpCode(_artBuffer);
289 |
290 | switch (opCode) {
291 |
292 | case ARTNET_ARTPOLL:
293 | // This is always called at the end of this function
294 | //_artPoll();
295 | break;
296 |
297 | case ARTNET_ARTDMX:
298 | _artDMX(_artBuffer);
299 | break;
300 |
301 | case ARTNET_IP_PROG:
302 | _artIPProg(_artBuffer);
303 | break;
304 |
305 | case ARTNET_ADDRESS:
306 | _artAddress(_artBuffer);
307 | break;
308 |
309 | case ARTNET_SYNC:
310 | _artSync(_artBuffer);
311 | break;
312 |
313 | case ARTNET_FIRMWARE_MASTER:
314 | _artFirmwareMaster(_artBuffer);
315 | break;
316 |
317 | case ARTNET_TOD_REQUEST:
318 | _artTODRequest(_artBuffer);
319 | break;
320 |
321 | case ARTNET_TOD_CONTROL:
322 | _artTODControl(_artBuffer);
323 | break;
324 |
325 | case ARTNET_RDM:
326 | _artRDM(_artBuffer, packetSize);
327 | break;
328 |
329 | case ARTNET_RDM_SUB:
330 | _artRDMSub(_artBuffer);
331 | break;
332 | }
333 | }
334 |
335 |
336 | // e131 packet
337 | packetSize = fUDP.parsePacket();
338 |
339 | if (packetSize > 0) {
340 |
341 | e131_packet_t _e131Buffer;
342 |
343 | // Read data into buffer
344 | fUDP.readBytes(_e131Buffer.raw, packetSize);
345 |
346 | _e131Receive(&_e131Buffer);
347 | }
348 |
349 | // Send artPollReply - the function will limit the number sent
350 | _artPoll();
351 |
352 | }
353 |
354 | int espArtNetRDM::_artOpCode(unsigned char *_artBuffer) {
355 | String test = String((char*)_artBuffer);
356 | if ( test.equals("Art-Net") ) {
357 | if ( _artBuffer[11] >= 14 ) { //protocol version [10] hi uint8_t [11] lo uint8_t
358 | return _artBuffer[9] * 256 + _artBuffer[8]; //opcode lo uint8_t first
359 | }
360 | }
361 |
362 | return 0;
363 | }
364 |
365 |
366 | void espArtNetRDM::_artPoll() {
367 | // limit the number of artPollReply messages
368 | if (_art->nextPollReply > millis())
369 | return;
370 | _art->nextPollReply = millis() + 2000;
371 |
372 | unsigned char _artReplyBuffer[ARTNET_REPLY_SIZE];
373 | _artReplyBuffer[0] = 'A';
374 | _artReplyBuffer[1] = 'r';
375 | _artReplyBuffer[2] = 't';
376 | _artReplyBuffer[3] = '-';
377 | _artReplyBuffer[4] = 'N';
378 | _artReplyBuffer[5] = 'e';
379 | _artReplyBuffer[6] = 't';
380 | _artReplyBuffer[7] = 0;
381 | _artReplyBuffer[8] = uint8_t(ARTNET_ARTPOLL_REPLY); // op code lo-hi
382 | _artReplyBuffer[9] = uint8_t(ARTNET_ARTPOLL_REPLY >> 8); // 0x2100 = artPollReply
383 | _artReplyBuffer[10] = _art->deviceIP[0]; // ip address
384 | _artReplyBuffer[11] = _art->deviceIP[1];
385 | _artReplyBuffer[12] = _art->deviceIP[2];
386 | _artReplyBuffer[13] = _art->deviceIP[3];
387 | _artReplyBuffer[14] = 0x36; // port lo first always 0x1936
388 | _artReplyBuffer[15] = 0x19;
389 | _artReplyBuffer[16] = _art->firmWareVersion >> 8; // firmware hi-lo
390 | _artReplyBuffer[17] = _art->firmWareVersion;
391 | _artReplyBuffer[20] = _art->oemHi; // oem hi-lo
392 | _artReplyBuffer[21] = _art->oemLo;
393 | _artReplyBuffer[22] = 0; // ubea
394 |
395 | _artReplyBuffer[23] = 0b11110010; // Device is RDM Capable
396 | _artReplyBuffer[24] = _art->estaLo; // ESTA Code (2 uint8_ts)
397 | _artReplyBuffer[25] = _art->estaHi;
398 |
399 | //short name
400 | for (int x = 0; x < ARTNET_SHORT_NAME_LENGTH; x++)
401 | _artReplyBuffer[x + 26] = _art->shortName[x];
402 |
403 | //long name
404 | for (int x = 0; x < ARTNET_LONG_NAME_LENGTH; x++)
405 | _artReplyBuffer[x + 44] = _art->longName[x];
406 |
407 | // node report - send blank
408 | for (int x = 0; x < ARTNET_NODE_REPORT_LENGTH; x++) {
409 | _artReplyBuffer[x + 108] = 0;
410 | }
411 |
412 |
413 | // Set reply code
414 | char tmp[7];
415 | sprintf (tmp, "%04x", _art->nodeReportCode);
416 | _artReplyBuffer[108] = '#';
417 | _artReplyBuffer[109] = tmp[0];
418 | _artReplyBuffer[110] = tmp[1];
419 | _artReplyBuffer[111] = tmp[2];
420 | _artReplyBuffer[112] = tmp[3];
421 | _artReplyBuffer[113] = '[';
422 |
423 | // Max 6 digits for counter - could be longer if wanted
424 | sprintf (tmp, "%d", _art->nodeReportCounter++);
425 | if (_art->nodeReportCounter > 999999)
426 | _art->nodeReportCounter = 0;
427 |
428 | // Format counter and add to reply buffer
429 | uint8_t x = 0;
430 | for (x = 0; tmp[x] != '\0' && x < 6; x++)
431 | _artReplyBuffer[x + 114] = tmp[x];
432 |
433 | uint8_t rLen = ARTNET_NODE_REPORT_LENGTH - x - 2;
434 | x = x + 114;
435 |
436 | _artReplyBuffer[x++] = ']';
437 | _artReplyBuffer[x++] = ' ';
438 |
439 | // Append plain text report
440 | for (uint8_t y = 0; y < rLen && _art->nodeReport[y] != '\0'; y++)
441 | _artReplyBuffer[x++] = _art->nodeReport[y];
442 |
443 |
444 | _artReplyBuffer[172] = 0; //number of ports Hi (always 0)
445 | _artReplyBuffer[194] = 0; // these are not used
446 | _artReplyBuffer[195] = 0;
447 | _artReplyBuffer[196] = 0;
448 | _artReplyBuffer[197] = 0;
449 | _artReplyBuffer[198] = 0;
450 | _artReplyBuffer[199] = 0;
451 | _artReplyBuffer[200] = 0; // Style - 0x00 = DMX to/from Artnet
452 |
453 | for (int x = 0; x < 6; x++) // MAC Address
454 | _artReplyBuffer[201 + x] = _art->deviceMAC[x];
455 |
456 | _artReplyBuffer[207] = _art->deviceIP[0]; // bind ip
457 | _artReplyBuffer[208] = _art->deviceIP[1];
458 | _artReplyBuffer[209] = _art->deviceIP[2];
459 | _artReplyBuffer[210] = _art->deviceIP[3];
460 |
461 | _artReplyBuffer[212] = (_art->dhcp) ? 31 : 29; // status 2
462 |
463 | for (int x = 213; x < ARTNET_REPLY_SIZE; x++)
464 | _artReplyBuffer[x] = 0; // Reserved for future - transmit 0
465 |
466 |
467 | // Set values for each group of ports and send artPollReply
468 | for (uint8_t groupNum = 0; groupNum < _art->numGroups; groupNum++) {
469 | group_def* group = _art->group[groupNum];
470 |
471 | if (group->numPorts == 0)
472 | continue;
473 |
474 | _artReplyBuffer[18] = group->netSwitch; // net
475 | _artReplyBuffer[19] = group->subnet; // subnet
476 | _artReplyBuffer[173] = group->numPorts; //number of ports (Lo uint8_t)
477 |
478 | _artReplyBuffer[211] = groupNum + 1; // Bind Index
479 |
480 | // Port details
481 | for (int x = 0; x < 4; x++) {
482 |
483 | // Send blank values for empty ports
484 | _artReplyBuffer[174 + x] = 0;
485 | _artReplyBuffer[178 + x] = 0;
486 | _artReplyBuffer[182 + x] = 0;
487 | _artReplyBuffer[186 + x] = 0;
488 | _artReplyBuffer[190 + x] = 0;
489 |
490 | // This port isn't in use
491 | if (group->ports[x] == 0)
492 | continue;
493 |
494 | // DMX or RDM out port
495 | if (group->ports[x]->portType != DMX_IN) {
496 |
497 | // Get values for Good Output field
498 | uint8_t go = 0;
499 | if (group->ports[x]->dmxChans != 0)
500 | go |= 128; // data being transmitted
501 | if (group->ports[x]->merging)
502 | go |= 8; // artnet data being merged
503 | if (! group->ports[x]->mergeHTP)
504 | go |= 2; // Merge mode LTP
505 | if (group->ports[x]->e131)
506 | go |= 1; // sACN
507 |
508 | _artReplyBuffer[174 + x] |= 128; //Port Type (128 = DMX out)
509 | _artReplyBuffer[182 + x] = go; //Good output (128 = data being transmitted)
510 | _artReplyBuffer[190 + x] = group->ports[x]->portUni; // swOut - port address
511 |
512 | // DMX In port info
513 | } else if (group->ports[x]->portType == DMX_IN) {
514 | _artReplyBuffer[174 + x] |= 64; // Port type (64 = DMX in)
515 |
516 | if (group->ports[x]->dmxChans != 0)
517 | _artReplyBuffer[178 + x] = 128; // Good input (128 = data being received)
518 |
519 | _artReplyBuffer[186] = group->ports[0]->portUni; // swIn
520 |
521 | }
522 | }
523 |
524 | // Send packet
525 | eUDP.beginPacket(_art->broadcastIP, ARTNET_PORT);
526 | eUDP.write((const uint8_t *)_artReplyBuffer, ARTNET_REPLY_SIZE);
527 | eUDP.endPacket();
528 |
529 | delay(0);
530 | }
531 | }
532 |
533 |
534 | void espArtNetRDM::artPollReply() {
535 | if (_art == 0)
536 | return;
537 |
538 | _artPoll();
539 | }
540 |
541 | void espArtNetRDM::_artDMX(unsigned char *_artBuffer) {
542 | group_def* group = 0;
543 |
544 | IPAddress rIP = eUDP.remoteIP();
545 |
546 | uint8_t net = (_artBuffer[15] & 0x7F);
547 | uint8_t sub = (_artBuffer[14] >> 4);
548 | uint8_t uni = (_artBuffer[14] & 0x0F);
549 |
550 | // Number of channels hi uint8_t first
551 | uint16_t numberOfChannels = _artBuffer[17] + (_artBuffer[16] << 8);
552 | uint16_t startChannel = 0;
553 |
554 | // Loop through all groups
555 | for (int x = 0; x < _art->numGroups; x++) {
556 | if (net == _art->group[x]->netSwitch && sub == _art->group[x]->subnet) {
557 | group = _art->group[x];
558 |
559 | // Loop through each port
560 | for (int y = 0; y < 4; y++) {
561 | if (group->ports[y] == 0 || group->ports[y]->portType == DMX_IN)
562 | continue;
563 |
564 | // If this port has the correct Net, Sub & Uni then save DMX to buffer
565 | if (uni == group->ports[y]->portUni)
566 | _saveDMX(&_artBuffer[ARTNET_ADDRESS_OFFSET], numberOfChannels, x, y, rIP, startChannel);
567 | }
568 | }
569 | }
570 | }
571 |
572 | void espArtNetRDM::_saveDMX(unsigned char *dmxData, uint16_t numberOfChannels, uint8_t groupNum, uint8_t portNum, IPAddress rIP, uint16_t startChannel) {
573 | group_def* group = _art->group[groupNum];
574 | port_def* port = group->ports[portNum];
575 |
576 | uint8_t senderID = 255; // Will be set to 0 or 1 if valid later
577 |
578 | unsigned long timeNow = millis();
579 |
580 | // We can't do the next calculations until after 10 seconds
581 | if (timeNow > 10000) {
582 | unsigned long timeExp = timeNow - 10000;
583 |
584 | // Clear IPs that we haven't heard from in over 10 seconds
585 | if (port->lastPacketTime[0] < timeExp)
586 | port->senderIP[0] = INADDR_NONE;
587 | else if (port->lastPacketTime[1] < timeExp)
588 | port->senderIP[1] = INADDR_NONE;
589 | }
590 |
591 | // Get a sender ID
592 | if (port->senderIP[0] == rIP) {
593 | senderID = 0;
594 | port->lastPacketTime[0] = timeNow;
595 | } else if (port->senderIP[1] == rIP || port->senderIP[1] == INADDR_NONE) {
596 | senderID = 1;
597 | port->senderIP[1] = rIP;
598 | port->lastPacketTime[1] = timeNow;
599 | } else if (port->senderIP[0] == INADDR_NONE) {
600 | senderID = 0;
601 | port->senderIP[0] = rIP;
602 | port->lastPacketTime[0] = timeNow;
603 | }
604 |
605 | // This is a third IP so drop the packet (Artnet v4 only allows for merging 2 DMX streams)
606 | if (senderID == 255)
607 | return;
608 |
609 | // Check if we're merging (the other IP will be non zero)
610 | if (port->senderIP[(senderID ^ 0x01)] == INADDR_NONE)
611 | port->merging = false;
612 | else
613 | port->merging = true;
614 |
615 |
616 | // Cancel merge is old so cancel the cancel merge
617 | if ((group->cancelMergeTime + ARTNET_CANCEL_MERGE_TIMEOUT) < millis()) {
618 | group->cancelMerge = false;
619 | group->cancelMergeIP = INADDR_NONE;
620 |
621 | } else {
622 | // This is the correct IP, enable cancel merge
623 | if (group->cancelMergeIP == port->senderIP[senderID]) {
624 | group->cancelMerge = 1;
625 | group->cancelMergeTime = millis();
626 | port->mergeHTP = false;
627 | port->merging = false;
628 |
629 | // If the merge is current & IP isn't correct, ignore this packet
630 | } else if (group->cancelMerge)
631 | return;
632 | }
633 |
634 | // Store number of channels
635 | if (numberOfChannels > port->dmxChans)
636 | port->dmxChans = numberOfChannels;
637 |
638 | // Check if we should merge (HTP) or not merge (LTP)
639 | if (port->merging && port->mergeHTP) {
640 | // Check if there is a buffer. If not, allocate and clear it
641 | if (port->ipBuffer == 0) {
642 |
643 | port->ipBuffer = (uint8_t*) malloc(2 * DMX_BUFFER_SIZE);
644 | delay(0);
645 | artClearDMXBuffer(port->ipBuffer);
646 | artClearDMXBuffer(&port->ipBuffer[DMX_BUFFER_SIZE]);
647 | delay(0);
648 | }
649 |
650 | // Put data into our buffer
651 | memcpy(&port->ipBuffer[senderID * DMX_BUFFER_SIZE + startChannel], dmxData, numberOfChannels);
652 |
653 | // Get the number of channels to compare
654 | numberOfChannels = (port->dmxChans > numberOfChannels) ? port->dmxChans : numberOfChannels;
655 |
656 | // Compare data and put in the output buffer
657 | for (uint16_t x = 0; x < numberOfChannels; x++)
658 | port->dmxBuffer[x] = (port->ipBuffer[x] > port->ipBuffer[x + DMX_BUFFER_SIZE]) ? port->ipBuffer[x] : port->ipBuffer[x + DMX_BUFFER_SIZE];
659 |
660 | // Call our dmx callback in the main script (Sync doesn't get used when merging)
661 | _art->dmxCallBack(groupNum, portNum, numberOfChannels, false);
662 |
663 | } else {
664 | // Copy data directly into output buffer
665 | memcpy(&port->dmxBuffer[startChannel], dmxData, numberOfChannels);
666 |
667 | /*
668 | // Delete merge buffer if it exists
669 | if (port->ipBuffer != 0) {
670 | free(port->ipBuffer);
671 | port->ipBuffer = 0;
672 | }
673 | */
674 |
675 | // Check if Sync is enabled and call dmx callback in the main script
676 | if (_art->lastSync == 0 || (_art->lastSync + 4000) < timeNow || _art->syncIP != rIP)
677 | _art->dmxCallBack(groupNum, portNum, numberOfChannels, false);
678 | else
679 | _art->dmxCallBack(groupNum, portNum, numberOfChannels, true);
680 |
681 | // _art->syncIP = rIP;
682 | }
683 | }
684 |
685 | uint8_t* espArtNetRDM::getDMX(uint8_t g, uint8_t p) {
686 | if (_art == 0)
687 | return NULL;
688 |
689 | if (g < _art->numGroups) {
690 | if (_art->group[g]->ports[p] != 0)
691 | return _art->group[g]->ports[p]->dmxBuffer;
692 | }
693 | return NULL;
694 | }
695 |
696 | uint16_t espArtNetRDM::numChans(uint8_t g, uint8_t p) {
697 | if (_art == 0)
698 | return 0;
699 |
700 | if (g < _art->numGroups) {
701 | if (_art->group[g]->ports[p] != 0)
702 | return _art->group[g]->ports[p]->dmxChans;
703 | }
704 | return 0;
705 | }
706 |
707 | void espArtNetRDM::_artIPProg(unsigned char *_artBuffer) {
708 | // Don't do anything if it's the same command again
709 | if ((_art->lastIPProg + 20) > millis())
710 | return;
711 | _art->lastIPProg = millis();
712 |
713 | uint8_t command = _artBuffer[14];
714 |
715 | // Enable DHCP
716 | if ((command & 0b11000000) == 0b11000000) {
717 | _art->dhcp = true;
718 |
719 | // Disable DHCP
720 | } else if ((command & 0b11000000) == 0b10000000) {
721 | _art->dhcp = false;
722 |
723 | // Program IP
724 | if ((command & 0b10000100) == 0b10000100)
725 | _art->deviceIP = IPAddress(_artBuffer[16], _artBuffer[17], _artBuffer[18], _artBuffer[19]);
726 |
727 | // Program subnet
728 | if ((command & 0b10000010) == 0b10000010) {
729 | _art->subnet = IPAddress(_artBuffer[20], _artBuffer[21], _artBuffer[22], _artBuffer[23]);
730 | _art->broadcastIP = IPAddress((uint32_t)_art->deviceIP | ~((uint32_t)_art->subnet));
731 | }
732 |
733 | // Use default address
734 | if ((command & 0b10001000) == 0b10001000)
735 | setDefaultIP();
736 | }
737 |
738 | // Run callback - must be before reply for correct dhcp setting
739 | if (_art->ipCallBack != 0)
740 | _art->ipCallBack();
741 |
742 | // Send reply
743 | _artIPProgReply();
744 |
745 | // Send artPollReply
746 | artPollReply();
747 | }
748 |
749 | void espArtNetRDM::_artIPProgReply() {
750 | // Initialise our reply
751 | char ipProgReply[ARTNET_IP_PROG_REPLY_SIZE];
752 |
753 | ipProgReply[0] = 'A';
754 | ipProgReply[1] = 'r';
755 | ipProgReply[2] = 't';
756 | ipProgReply[3] = '-';
757 | ipProgReply[4] = 'N';
758 | ipProgReply[5] = 'e';
759 | ipProgReply[6] = 't';
760 | ipProgReply[7] = 0;
761 | ipProgReply[8] = uint8_t(ARTNET_IP_PROG_REPLY); // op code lo-hi
762 | ipProgReply[9] = uint8_t(ARTNET_IP_PROG_REPLY >> 8); // 0x2100 = artPollReply
763 | ipProgReply[10] = 0;
764 | ipProgReply[11] = 14; // artNet version (14)
765 | ipProgReply[12] = 0;
766 | ipProgReply[13] = 0;
767 | ipProgReply[14] = 0;
768 | ipProgReply[15] = 0;
769 | ipProgReply[16] = _art->deviceIP[0]; // ip address
770 | ipProgReply[17] = _art->deviceIP[1];
771 | ipProgReply[18] = _art->deviceIP[2];
772 | ipProgReply[19] = _art->deviceIP[3];
773 | ipProgReply[20] = _art->subnet[0]; // subnet address
774 | ipProgReply[21] = _art->subnet[1];
775 | ipProgReply[22] = _art->subnet[2];
776 | ipProgReply[23] = _art->subnet[3];
777 | ipProgReply[24] = 0;
778 | ipProgReply[25] = 0;
779 | ipProgReply[26] = (_art->dhcp) ? (1 << 6) : 0; // DHCP enabled
780 | ipProgReply[27] = 0;
781 | ipProgReply[28] = 0;
782 | ipProgReply[29] = 0;
783 | ipProgReply[30] = 0;
784 | ipProgReply[31] = 0;
785 | ipProgReply[32] = 0;
786 | ipProgReply[33] = 0;
787 |
788 | // Send packet
789 | eUDP.beginPacket(eUDP.remoteIP(), ARTNET_PORT);
790 | eUDP.write((const uint8_t *)ipProgReply, ARTNET_IP_PROG_REPLY_SIZE);
791 | eUDP.endPacket();
792 | }
793 |
794 | void espArtNetRDM::_artAddress(unsigned char *_artBuffer) {
795 | // _artBuffer[13] bindIndex
796 | uint8_t g = _artBuffer[13] - 1;
797 |
798 | // Set net switch
799 | if ((_artBuffer[12] & 0x80) == 0x80)
800 | _art->group[g]->netSwitch = _artBuffer[12] & 0x7F;
801 |
802 | // Set short name
803 | if (_artBuffer[14] != '\0') {
804 | for (int x = 0; x < ARTNET_SHORT_NAME_LENGTH; x++)
805 | _art->shortName[x] = _artBuffer[x + 14];
806 | }
807 |
808 | // Set long name
809 | if (_artBuffer[32] != '\0') {
810 | for (int x = 0; x < ARTNET_LONG_NAME_LENGTH; x++)
811 | _art->longName[x] = _artBuffer[x + 32];
812 | }
813 |
814 | // Set Port Address
815 | for (int x = 0; x < 4; x++) {
816 | if ((_artBuffer[100 + x] & 0xF0) == 0x80 && _art->group[g]->ports[x] != 0)
817 | _art->group[g]->ports[x]->portUni = _artBuffer[100 + x] & 0x0F;
818 | }
819 |
820 | // Set subnet
821 | if ((_artBuffer[104] & 0xF0) == 0x80) {
822 | _art->group[g]->subnet = _artBuffer[104] & 0x0F;
823 | }
824 |
825 | // Get port number
826 | uint8_t p = _artBuffer[106] & 0x0F;
827 |
828 | // Command
829 | switch (_artBuffer[106]) {
830 | case ARTNET_AC_CANCEL_MERGE:
831 | _art->group[g]->cancelMergeTime = millis();
832 | _art->group[g]->cancelMergeIP = eUDP.remoteIP();
833 |
834 | /*
835 | for (int x = 0; x < 4; x++) {
836 | if (_art->group[g]->ports[x] == 0)
837 | continue;
838 |
839 | // Delete merge buffer if it exists
840 | if (_art->group[g]->ports[x]->ipBuffer != 0) {
841 | free(_art->group[g]->ports[x]->ipBuffer);
842 | _art->group[g]->ports[x]->ipBuffer = 0;
843 | }
844 |
845 | // Update our timer variables
846 | _art->group[g]->ports[x]->lastPacketTime[0] = 0;
847 | _art->group[g]->ports[x]->lastPacketTime[1] = 0;
848 | }
849 | */
850 | break;
851 |
852 | case ARTNET_AC_MERGE_LTP_0:
853 | case ARTNET_AC_MERGE_LTP_1:
854 | case ARTNET_AC_MERGE_LTP_2:
855 | case ARTNET_AC_MERGE_LTP_3:
856 | if (_art->group[g]->ports[p] != 0) {
857 | // Delete merge buffer if it exists
858 | if (_art->group[g]->ports[p]->ipBuffer != 0) {
859 | free(_art->group[g]->ports[p]->ipBuffer);
860 | _art->group[g]->ports[p]->ipBuffer = 0;
861 | }
862 |
863 | // Update our timer variables
864 | _art->group[g]->ports[p]->lastPacketTime[0] = 0;
865 | _art->group[g]->ports[p]->lastPacketTime[1] = 0;
866 |
867 | // Set to LTP
868 | _art->group[g]->ports[p]->mergeHTP = false;
869 |
870 | // Cancel the cancel merge
871 | _art->group[g]->cancelMerge = 0;
872 | _art->group[g]->cancelMergeIP = INADDR_NONE;
873 | }
874 | break;
875 |
876 | case ARTNET_AC_MERGE_HTP_0:
877 | case ARTNET_AC_MERGE_HTP_1:
878 | case ARTNET_AC_MERGE_HTP_2:
879 | case ARTNET_AC_MERGE_HTP_3:
880 | // Set to HTP
881 | if (_art->group[g]->ports[p] != 0) {
882 | _art->group[g]->ports[p]->mergeHTP = true;
883 |
884 | // Cancel the cancel merge
885 | _art->group[g]->cancelMerge = 0;
886 | _art->group[g]->cancelMergeIP = INADDR_NONE;
887 | }
888 | break;
889 |
890 | case ARTNET_AC_CLEAR_OP_0:
891 | case ARTNET_AC_CLEAR_OP_1:
892 | case ARTNET_AC_CLEAR_OP_2:
893 | case ARTNET_AC_CLEAR_OP_3:
894 | if (_art->group[g]->ports[p] == 0) {
895 | // Delete merge buffer if it exists
896 | if (_art->group[g]->ports[p]->ipBuffer != 0) {
897 | free(_art->group[g]->ports[p]->ipBuffer);
898 | _art->group[g]->ports[p]->ipBuffer = 0;
899 | }
900 |
901 | // Clear the DMX output buffer
902 | artClearDMXBuffer(_art->group[g]->ports[p]->dmxBuffer);
903 | }
904 | break;
905 |
906 |
907 | case ARTNET_AC_ARTNET_SEL_0:
908 | case ARTNET_AC_ARTNET_SEL_1:
909 | case ARTNET_AC_ARTNET_SEL_2:
910 | case ARTNET_AC_ARTNET_SEL_3:
911 | for (uint8_t x = 0; x < 4; x++) {
912 | if (_art->group[g]->ports[x] == 0)
913 | setE131(g, x, false);
914 | }
915 | break;
916 |
917 | case ARTNET_AC_ACN_SEL_0:
918 | case ARTNET_AC_ACN_SEL_1:
919 | case ARTNET_AC_ACN_SEL_2:
920 | case ARTNET_AC_ACN_SEL_3:
921 | for (uint8_t x = 0; x < 4; x++) {
922 | if (_art->group[g]->ports[p] == 0)
923 | setE131(g, p, true);
924 | }
925 | break;
926 |
927 | }
928 |
929 | // Send reply
930 | artPollReply();
931 |
932 | // Run callback
933 | if (_art->addressCallBack != 0)
934 | _art->addressCallBack();
935 | }
936 |
937 | void espArtNetRDM::_artSync(unsigned char *_artBuffer) {
938 | // Update sync timer
939 | _art->lastSync = millis();
940 |
941 | // Run callback
942 | if (_art->syncCallBack != 0)// && _art->syncIP == eUDP.remoteIP())
943 | _art->syncCallBack();
944 | }
945 |
946 | void espArtNetRDM::_artFirmwareMaster(unsigned char *_artBuffer) {
947 | //Serial.println("artFirmwareMaster");
948 | }
949 |
950 | void espArtNetRDM::_artTODRequest(unsigned char *_artBuffer) {
951 | uint8_t net = _artBuffer[21];
952 | group_def* group;
953 |
954 | uint8_t numAddress = _artBuffer[23];
955 | uint8_t addr = 24;
956 |
957 | // Handle artTodControl requests
958 | if (_artOpCode(_artBuffer) == ARTNET_TOD_CONTROL) {
959 | numAddress = 1;
960 | addr = 23;
961 | }
962 |
963 | for (int g = 0; g < _art->numGroups; g++) {
964 | group = _art->group[g];
965 |
966 | // Net matches so loop through the addresses
967 | if (group->netSwitch == net) {
968 | for (int y = 0; y < numAddress; y++) {
969 |
970 | // Subnet doesn't match, try the next address
971 | if (group->subnet != (_artBuffer[addr + y] >> 4))
972 | continue;
973 |
974 | // Subnet matches so loop through the 4 ports and check universe
975 | for (int p = 0; p < 4; p++) {
976 |
977 | if (group->ports[p] == 0)
978 | continue;
979 |
980 | port_def* port = group->ports[p];
981 |
982 | if (port->portUni != (_artBuffer[addr + y] & 0x0F))
983 | continue;
984 |
985 | port->lastTodCommand = millis();
986 |
987 | // Flush TOD
988 | if (_artBuffer[22] == 0x01)
989 | _art->todFlushCallBack(g, p);
990 |
991 | // TOD Request
992 | else
993 | _art->todRequestCallBack(g, p);
994 | }
995 | }
996 |
997 |
998 | }
999 | }
1000 |
1001 | }
1002 |
1003 | void espArtNetRDM::artTODData(uint8_t g, uint8_t p, uint16_t* uidMan, uint32_t* uidDev, uint16_t uidTotal, uint8_t state) {
1004 | if (_art == 0)
1005 | return;
1006 |
1007 | // Initialise our reply
1008 | uint16_t len = ARTNET_TOD_DATA_SIZE + (6 * uidTotal);
1009 | char artTodData[len];
1010 | artTodData[0] = 'A';
1011 | artTodData[1] = 'r';
1012 | artTodData[2] = 't';
1013 | artTodData[3] = '-';
1014 | artTodData[4] = 'N';
1015 | artTodData[5] = 'e';
1016 | artTodData[6] = 't';
1017 | artTodData[7] = 0;
1018 | artTodData[8] = uint8_t(ARTNET_TOD_DATA); // op code lo-hi
1019 | artTodData[9] = ARTNET_TOD_DATA >> 8;
1020 | artTodData[10] = 0;
1021 | artTodData[11] = 14; // artNet version (14)
1022 | artTodData[12] = 0x01; // rdm standard Ver 1.0
1023 | artTodData[13] = p + 1; // port number (1-4 not 0-3)
1024 | artTodData[14] = 0;
1025 | artTodData[15] = 0;
1026 | artTodData[16] = 0;
1027 | artTodData[17] = 0;
1028 | artTodData[18] = 0;
1029 | artTodData[19] = 0;
1030 | artTodData[20] = g + 1; // bind index
1031 | artTodData[21] = _art->group[g]->netSwitch;
1032 |
1033 | if (state == RDM_TOD_READY)
1034 | artTodData[22] = 0x00; // TOD full
1035 | else
1036 | artTodData[22] = 0xFF; // TOD not avail or incomplete
1037 |
1038 | artTodData[23] = (_art->group[g]->subnet << 4) | _art->group[g]->ports[p]->portUni;
1039 | artTodData[24] = uidTotal >> 8; // number of RDM devices found
1040 | artTodData[25] = uidTotal;
1041 |
1042 | uint8_t blockCount = 0;
1043 |
1044 | while (1) {
1045 | artTodData[26] = blockCount;
1046 | artTodData[27] = (uidTotal > 200) ? 200 : uidTotal;
1047 |
1048 | uint8_t uidCount = 0;
1049 |
1050 | // Add RDM UIDs (48 bit each) - max 200 per packet
1051 | for (uint16_t xx = 28; uidCount < 200 && uidTotal > 0; uidCount++) {
1052 | uidTotal--;
1053 |
1054 | artTodData[xx++] = uidMan[uidTotal] >> 8;
1055 | artTodData[xx++] = uidMan[uidTotal];
1056 | artTodData[xx++] = uidDev[uidTotal] >> 24;
1057 | artTodData[xx++] = uidDev[uidTotal] >> 16;
1058 | artTodData[xx++] = uidDev[uidTotal] >> 8;
1059 | artTodData[xx++] = uidDev[uidTotal];
1060 | }
1061 |
1062 | // Send packet
1063 | eUDP.beginPacket(_art->broadcastIP, ARTNET_PORT);
1064 | eUDP.write((const uint8_t *)artTodData, len);
1065 | eUDP.endPacket();
1066 |
1067 | if (uidTotal == 0)
1068 | break;
1069 |
1070 | blockCount++;
1071 | }
1072 | }
1073 |
1074 | void espArtNetRDM::_artTODControl(unsigned char *_artBuffer) {
1075 | _artTODRequest(_artBuffer);
1076 | }
1077 |
1078 | void espArtNetRDM::_artRDM(unsigned char *_artBuffer, uint16_t packetSize) {
1079 | if (_art->rdmCallBack == 0)
1080 | return;
1081 |
1082 | IPAddress remoteIp = eUDP.remoteIP();
1083 |
1084 | uint8_t net = _artBuffer[21] * 0x7F;
1085 | uint8_t sub = _artBuffer[23] >> 4;
1086 | uint8_t uni = _artBuffer[23] & 0x0F;
1087 |
1088 | // Get RDM data into out buffer ready to send
1089 | rdm_data c;
1090 | c.buffer[0] = 0xCC;
1091 | memcpy (&c.buffer[1], &_artBuffer[24], _artBuffer[25] + 2);
1092 |
1093 | group_def* group = 0;
1094 | unsigned long timeNow = millis();
1095 |
1096 | // Get the group number
1097 | for (int x = 0; x < _art->numGroups; x++) {
1098 | if (net == _art->group[x]->netSwitch && sub == _art->group[x]->subnet) {
1099 | group = _art->group[x];
1100 |
1101 | // Get the port number
1102 | for (int y = 0; y < 4; y++) {
1103 |
1104 | // If the port isn't in use
1105 | if (group->ports[y] == 0 || group->ports[y]->portType != RDM_OUT)
1106 | continue;
1107 |
1108 | // Run callback
1109 | if (uni == group->ports[y]->portUni) {
1110 | _art->rdmCallBack(x, y, &c);
1111 |
1112 | bool ipSet = false;
1113 |
1114 | for (int q = 0; q < 5; q++) {
1115 | // Check when last packets where received. Clear if over 200ms
1116 | if (timeNow >= (group->ports[y]->rdmSenderTime[q] + 200))
1117 | group->ports[y]->rdmSenderIP[q] = INADDR_NONE;
1118 |
1119 | // Save our IP
1120 | if (!ipSet) {
1121 | if (group->ports[y]->rdmSenderIP[q] == INADDR_NONE || group->ports[y]->rdmSenderIP[q] == remoteIp) {
1122 | group->ports[y]->rdmSenderIP[q] = remoteIp;
1123 | group->ports[y]->rdmSenderTime[q] = timeNow;
1124 | ipSet = true;
1125 | }
1126 | }
1127 | }
1128 | }
1129 | }
1130 | }
1131 | }
1132 | }
1133 |
1134 | void espArtNetRDM::rdmResponse(rdm_data* c, uint8_t g, uint8_t p) {
1135 | if (_art == 0)
1136 | return;
1137 |
1138 | uint16_t len = ARTNET_RDM_REPLY_SIZE + c->packet.Length + 1;
1139 | // Initialise our reply
1140 | char rdmReply[len];
1141 |
1142 | rdmReply[0] = 'A';
1143 | rdmReply[1] = 'r';
1144 | rdmReply[2] = 't';
1145 | rdmReply[3] = '-';
1146 | rdmReply[4] = 'N';
1147 | rdmReply[5] = 'e';
1148 | rdmReply[6] = 't';
1149 | rdmReply[7] = 0;
1150 | rdmReply[8] = uint8_t(ARTNET_RDM); // op code lo-hi
1151 | rdmReply[9] = uint8_t(ARTNET_RDM >> 8);
1152 | rdmReply[10] = 0;
1153 | rdmReply[11] = 14; // artNet version (14)
1154 | rdmReply[12] = 0x01; // RDM version - RDM STANDARD V1.0
1155 |
1156 | for (uint8_t x = 13; x < 21; x++)
1157 | rdmReply[x] = 0;
1158 |
1159 | rdmReply[21] = _art->group[g]->netSwitch;
1160 | rdmReply[22] = 0x00; // Command - 0x00 = Process RDM Packet
1161 | rdmReply[23] = (_art->group[g]->subnet << 4) | _art->group[g]->ports[p]->portUni;
1162 |
1163 | // Copy everything except the 0xCC start code
1164 | memcpy(&rdmReply[24], &c->buffer[1], c->packet.Length + 1);
1165 |
1166 | for (int x = 0; x < 5; x++) {
1167 | if (_art->group[g]->ports[p]->rdmSenderIP[x] != INADDR_NONE) {
1168 | // Send packet
1169 | eUDP.beginPacket(_art->group[g]->ports[p]->rdmSenderIP[x], ARTNET_PORT);
1170 | eUDP.write((const uint8_t *)rdmReply, len);
1171 | eUDP.endPacket();
1172 | }
1173 | }
1174 | }
1175 |
1176 | void espArtNetRDM::_artRDMSub(unsigned char *_artBuffer) {
1177 | //Serial.println("artRDMSub");
1178 | }
1179 |
1180 | IPAddress espArtNetRDM::getIP() {
1181 | if (_art == 0)
1182 | return INADDR_NONE;
1183 | return _art->deviceIP;
1184 | }
1185 |
1186 | IPAddress espArtNetRDM::getSubnetMask() {
1187 | if (_art == 0)
1188 | return INADDR_NONE;
1189 | return _art->subnet;
1190 | }
1191 |
1192 | bool espArtNetRDM::getDHCP() {
1193 | if (_art == 0)
1194 | return 0;
1195 | return _art->dhcp;
1196 | }
1197 |
1198 |
1199 | void espArtNetRDM::setIP(IPAddress ip, IPAddress subnet) {
1200 | if (_art == 0)
1201 | return;
1202 | _art->deviceIP = ip;
1203 |
1204 | if ( (uint32_t)subnet != 0 )
1205 | _art->subnet = subnet;
1206 |
1207 | _art->broadcastIP = IPAddress((uint32_t)_art->deviceIP | ~((uint32_t)_art->subnet));
1208 | }
1209 |
1210 | void espArtNetRDM::setDHCP(bool d) {
1211 | if (_art == 0)
1212 | return;
1213 | _art->dhcp = d;
1214 | }
1215 |
1216 | void espArtNetRDM::setNet(uint8_t g, uint8_t net) {
1217 | if (_art == 0 || g >= _art->numGroups)
1218 | return;
1219 | _art->group[g]->netSwitch = net;
1220 | }
1221 |
1222 | uint8_t espArtNetRDM:: getNet(uint8_t g) {
1223 | if (_art == 0 || g >= _art->numGroups)
1224 | return 0;
1225 | return _art->group[g]->netSwitch;
1226 | }
1227 |
1228 | void espArtNetRDM::setSubNet(uint8_t g, uint8_t sub) {
1229 | if (_art == 0 || g >= _art->numGroups)
1230 | return;
1231 | _art->group[g]->subnet = sub;
1232 | }
1233 |
1234 | uint8_t espArtNetRDM::getSubNet(uint8_t g) {
1235 | if (_art == 0 || g >= _art->numGroups)
1236 | return 0;
1237 | return _art->group[g]->subnet;
1238 | }
1239 |
1240 | void espArtNetRDM::setUni(uint8_t g, uint8_t p, uint8_t uni) {
1241 | if (_art == 0 || g >= _art->numGroups || _art->group[g]->ports[p] == 0)
1242 | return;
1243 | _art->group[g]->ports[p]->portUni = uni;
1244 | }
1245 |
1246 | uint8_t espArtNetRDM::getUni(uint8_t g, uint8_t p) {
1247 | if (_art == 0 || g >= _art->numGroups || _art->group[g]->ports[p] == 0)
1248 | return 0;
1249 | return _art->group[g]->ports[p]->portUni;
1250 | }
1251 |
1252 |
1253 | void espArtNetRDM:: setPortType(uint8_t g, uint8_t p, uint8_t t) {
1254 | if (_art == 0 || g >= _art->numGroups || _art->group[g]->ports[p] == 0)
1255 | return;
1256 |
1257 | _art->group[g]->ports[p]->portType = t;
1258 | }
1259 |
1260 | void espArtNetRDM::setMerge(uint8_t g, uint8_t p, bool htp) {
1261 | if (_art == 0 || g >= _art->numGroups || _art->group[g]->ports[p] == 0)
1262 | return;
1263 | _art->group[g]->ports[p]->mergeHTP = htp;
1264 | }
1265 |
1266 | bool espArtNetRDM::getMerge(uint8_t g, uint8_t p) {
1267 | if (_art == 0 || g >= _art->numGroups || _art->group[g]->ports[p] == 0)
1268 | return 0;
1269 | return _art->group[g]->ports[p]->mergeHTP;
1270 | }
1271 |
1272 |
1273 |
1274 | void espArtNetRDM::setShortName(const char* name) {
1275 | if (_art == 0)
1276 | return;
1277 | memcpy(_art->shortName, name, ARTNET_SHORT_NAME_LENGTH);
1278 | }
1279 |
1280 | const char* espArtNetRDM::getShortName() {
1281 | if (_art == 0)
1282 | return NULL;
1283 | return _art->shortName;
1284 | }
1285 |
1286 |
1287 | void espArtNetRDM::setLongName(const char* name) {
1288 | if (_art == 0)
1289 | return;
1290 | memcpy(_art->longName, name, ARTNET_LONG_NAME_LENGTH);
1291 | }
1292 |
1293 | const char* espArtNetRDM::getLongName() {
1294 | if (_art == 0)
1295 | return NULL;
1296 | return _art->longName;
1297 | }
1298 |
1299 | void espArtNetRDM::setNodeReport(const char* c, uint16_t code) {
1300 | if (_art == 0)
1301 | return;
1302 |
1303 | strlcpy(_art->nodeReport, c, ARTNET_NODE_REPORT_LENGTH);
1304 | _art->nodeReportCode = code;
1305 | }
1306 |
1307 | void espArtNetRDM::sendDMX(uint8_t g, uint8_t p, IPAddress bcAddress, uint8_t* data, uint16_t length) {
1308 | if (_art == 0 || _art->numGroups <= g || _art->group[g]->ports[p] == 0)
1309 | return;
1310 |
1311 | uint8_t net = _art->group[g]->netSwitch;
1312 | uint8_t subnet = _art->group[g]->subnet;
1313 | uint8_t uni = _art->group[g]->ports[p]->portUni;
1314 |
1315 | // length is always even and up to 512 channels
1316 | if (length % 2)
1317 | length += 1;
1318 | if (length > 512)
1319 | length = 512;
1320 |
1321 | _art->group[g]->ports[p]->dmxChans = length;
1322 |
1323 | unsigned char _artDMX[ARTNET_BUFFER_MAX];
1324 | _artDMX[0] = 'A';
1325 | _artDMX[1] = 'r';
1326 | _artDMX[2] = 't';
1327 | _artDMX[3] = '-';
1328 | _artDMX[4] = 'N';
1329 | _artDMX[5] = 'e';
1330 | _artDMX[6] = 't';
1331 | _artDMX[7] = 0;
1332 | _artDMX[8] = uint8_t(ARTNET_ARTDMX); // op code lo-hi
1333 | _artDMX[9] = uint8_t(ARTNET_ARTDMX >> 8);
1334 | _artDMX[10] = 0; // protocol version (14)
1335 | _artDMX[11] = 14;
1336 | _artDMX[12] = _dmxSeqID++; // sequence ID
1337 | _artDMX[13] = p; // Port ID (not really necessary)
1338 | _artDMX[14] = (subnet << 4) | uni; // Subuni
1339 | _artDMX[15] = (net & 0x7F); // Netswitch
1340 | _artDMX[16] = (length >> 8); // DMX Data length
1341 | _artDMX[17] = (length & 0xFF);
1342 |
1343 | for (uint16_t x = 0; x < length; x++)
1344 | _artDMX[18 + x] = data[x];
1345 |
1346 | // Send packet
1347 | eUDP.beginPacket(bcAddress, ARTNET_PORT);
1348 | eUDP.write((const uint8_t *)_artDMX, (18 + length));
1349 | eUDP.endPacket();
1350 |
1351 | }
1352 |
1353 | void espArtNetRDM::setE131(uint8_t g, uint8_t p, bool a) {
1354 | if (_art == 0 || _art->numGroups <= g || _art->group[g]->ports[p] == 0)
1355 | return;
1356 |
1357 | // Increment or decrement our e131Count variable
1358 | if (!_art->group[g]->ports[p]->e131 && a) {
1359 | e131Count += 1;
1360 |
1361 | // Clear the DMX output buffer
1362 | artClearDMXBuffer(_art->group[g]->ports[p]->dmxBuffer);
1363 |
1364 | } else if (_art->group[g]->ports[p]->e131 && !a) {
1365 | e131Count -= 1;
1366 |
1367 | // Clear the DMX output buffer
1368 | artClearDMXBuffer(_art->group[g]->ports[p]->dmxBuffer);
1369 | }
1370 |
1371 | _art->group[g]->ports[p]->e131 = a;
1372 | }
1373 |
1374 | bool espArtNetRDM::getE131(uint8_t g, uint8_t p) {
1375 | if (_art == 0 || _art->numGroups <= g || _art->group[g]->ports[p] == 0 || _art->group[g]->ports[p]->e131 == false)
1376 | return false;
1377 |
1378 | return true;
1379 | }
1380 |
1381 | void espArtNetRDM::setE131Uni(uint8_t g, uint8_t p, uint16_t u) {
1382 | if (_art == 0 || _art->numGroups <= g || _art->group[g]->ports[p] == 0)
1383 | return;
1384 |
1385 | _art->group[g]->ports[p]->e131Uni = u;
1386 | _art->group[g]->ports[p]->e131Sequence = 0;
1387 | _art->group[g]->ports[p]->e131Priority = 0;
1388 | }
1389 |
1390 | void espArtNetRDM::_e131Receive(e131_packet_t* e131Buffer) {
1391 | if (_art == 0 || _art->numGroups == 0 || e131Count == 0)
1392 | return;
1393 |
1394 | // Check for sACN packet errors. Error reporting not implemented -> just dump packet
1395 |
1396 | if (memcmp(e131Buffer->acn_id, ACN_ID, sizeof(e131Buffer->acn_id)))
1397 | //return ERROR_ACN_ID;
1398 | return;
1399 |
1400 | if (__builtin_bswap32(e131Buffer->root_vector) != VECTOR_ROOT)
1401 | //return ERROR_VECTOR_ROOT;
1402 | return;
1403 |
1404 | if (__builtin_bswap32(e131Buffer->frame_vector) != VECTOR_FRAME)
1405 | //return ERROR_VECTOR_FRAME;
1406 | return;
1407 |
1408 | if (e131Buffer->dmp_vector != VECTOR_DMP)
1409 | //return ERROR_VECTOR_DMP;
1410 | return;
1411 |
1412 |
1413 | // No errors -> continue with sACN processing
1414 |
1415 |
1416 | uint16_t uni = (e131Buffer->universe << 8) | ((e131Buffer->universe >> 8) & 0xFF);
1417 | uint16_t numberOfChannels = ((e131Buffer->property_value_count << 8) | ((e131Buffer->property_value_count >> 8) & 0xFF)) - 1;
1418 | uint16_t startChannel = (e131Buffer-> first_address << 8) | ((e131Buffer-> first_address >> 8) & 0xFF);
1419 | uint16_t seq = e131Buffer->sequence_number;
1420 |
1421 | uint8_t _e131Count = 0;
1422 |
1423 | group_def* group = 0;
1424 |
1425 | IPAddress rIP = fUDP.remoteIP();
1426 |
1427 | // Loop through all groups
1428 | for (int x = 0; x < _art->numGroups; x++) {
1429 | group = _art->group[x];
1430 |
1431 | // Loop through each port
1432 | for (int y = 0; y < 4; y++) {
1433 | if (group->ports[y] == 0 || group->ports[y]->portType == DMX_IN || !group->ports[y]->e131)
1434 | continue;
1435 |
1436 | // If this port has the correct Uni, is a later packet, and is of a valid priority -> save DMX to buffer
1437 | if (uni == group->ports[y]->e131Uni && seq > group->ports[y]->e131Sequence && e131Buffer->priority >= group->ports[y]->e131Priority) {
1438 |
1439 | // Drop non-zero start packets
1440 | if (e131Buffer->property_values[0] != 0)
1441 | continue;
1442 |
1443 | // A higher priority will override previous data - this is handled in saveDMX but we need to clear the IPs & buffer
1444 | if (e131Buffer->priority > group->ports[y]->e131Priority) {
1445 | artClearDMXBuffer(group->ports[y]->dmxBuffer);
1446 | group->ports[y]->senderIP[0] = INADDR_NONE;
1447 | group->ports[y]->senderIP[1] = INADDR_NONE;
1448 | }
1449 |
1450 | group->ports[y]->e131Priority = e131Buffer->priority;
1451 |
1452 | _saveDMX(&e131Buffer->property_values[1], numberOfChannels, x, y, rIP, startChannel);
1453 | }
1454 |
1455 | // If all the e131 ports are checked, then return
1456 | if (e131Count == ++_e131Count)
1457 | return;
1458 | }
1459 | }
1460 |
1461 | }
1462 |
--------------------------------------------------------------------------------
/ArtNetNode/espArtNetRDM.h:
--------------------------------------------------------------------------------
1 | /*
2 | espArtNetRDM v1 (pre-release) library
3 | Copyright (c) 2016, Matthew Tong
4 | https://github.com/mtongnz/
5 | Modified from https://github.com/forkineye/E131/blob/master/E131.h
6 | This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public
7 | License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any
8 | later version.
9 | This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
10 | warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
11 | You should have received a copy of the GNU General Public License along with this program.
12 | If not, see http://www.gnu.org/licenses/
13 | */
14 | #ifndef espArtNetRDM_h
15 | #define espArtNetRDM_h
16 |
17 | #include
18 | #include
19 |
20 | #include "rdmDataTypes.h"
21 | #include "artnet.h"
22 | #include "e131.h"
23 |
24 | typedef void (*artDMXCallBack)(uint8_t, uint8_t, uint16_t, bool);
25 | typedef void (*artSyncCallBack)(void);
26 | typedef void (*artRDMCallBack)(uint8_t, uint8_t, rdm_data*);
27 | typedef void (*artIPCallBack)(void);
28 | typedef void (*artAddressCallBack)(void);
29 | typedef void (*artTodRequestCallBack)(uint8_t, uint8_t);
30 | typedef void (*artTodFlushCallBack)(uint8_t, uint8_t);
31 |
32 | enum port_type {
33 | DMX_OUT = 0,
34 | RDM_OUT = 1,
35 | DMX_IN = 2
36 | };
37 |
38 | struct _port_def {
39 | // DMX out/in or RDM out
40 | uint8_t portType;
41 |
42 | // sACN settings
43 | bool e131;
44 | uint16_t e131Uni;
45 | uint16_t e131Sequence;
46 | uint8_t e131Priority;
47 |
48 | // Port universe
49 | uint8_t portUni;
50 |
51 | // DMX final values buffer
52 | uint8_t* dmxBuffer;
53 | uint16_t dmxChans;
54 | bool ownBuffer;
55 | bool mergeHTP;
56 | bool merging;
57 |
58 | // ArtDMX input buffers for 2 IPs
59 | uint8_t* ipBuffer;
60 | uint16_t ipChans[2];
61 |
62 | // IPs for current data + time of last packet
63 | IPAddress senderIP[2];
64 | unsigned long lastPacketTime[2];
65 |
66 | // IPs for the last 5 RDM commands
67 | IPAddress rdmSenderIP[5];
68 | unsigned long rdmSenderTime[5];
69 |
70 | // RDM Variables
71 | bool todAvailable;
72 | uint16_t uidTotal;
73 | uint16_t uidMan[50];
74 | uint32_t uidSerial[50];
75 | unsigned long lastTodCommand;
76 | };
77 |
78 | typedef struct _port_def port_def;
79 |
80 | struct _group_def {
81 | // Port Address
82 | uint8_t netSwitch = 0x00;
83 | uint8_t subnet = 0x00;
84 |
85 | port_def* ports[4] = {0, 0, 0, 0};
86 | uint8_t numPorts = 0;
87 |
88 | IPAddress cancelMergeIP;
89 | bool cancelMerge;
90 | unsigned long cancelMergeTime;
91 | };
92 |
93 | typedef struct _group_def group_def;
94 |
95 | struct _artnet_def {
96 |
97 | IPAddress deviceIP;
98 | IPAddress subnet;
99 | IPAddress broadcastIP;
100 | IPAddress rdmIP[5];
101 | uint8_t rdmIPcount;
102 |
103 | IPAddress syncIP;
104 | unsigned long lastSync;
105 |
106 | uint8_t deviceMAC[6];
107 | bool dhcp = true;
108 |
109 | char shortName[ARTNET_SHORT_NAME_LENGTH];
110 | char longName[ARTNET_LONG_NAME_LENGTH];
111 |
112 | uint8_t oemHi;
113 | uint8_t oemLo;
114 | uint8_t estaHi;
115 | uint8_t estaLo;
116 |
117 | group_def* group[16];
118 | uint8_t numGroups;
119 | uint32_t lastIPProg;
120 | uint32_t nextPollReply;
121 |
122 | uint16_t firmWareVersion;
123 | uint32_t nodeReportCounter;
124 | uint16_t nodeReportCode;
125 | char nodeReport[ARTNET_NODE_REPORT_LENGTH];
126 |
127 | // callback functions
128 | artDMXCallBack dmxCallBack = 0;
129 | artSyncCallBack syncCallBack = 0;
130 | artRDMCallBack rdmCallBack = 0;
131 | artIPCallBack ipCallBack = 0;
132 | artAddressCallBack addressCallBack = 0;
133 | artTodRequestCallBack todRequestCallBack = 0;
134 | artTodFlushCallBack todFlushCallBack = 0;
135 | };
136 |
137 | typedef struct _artnet_def artnet_device;
138 |
139 | class espArtNetRDM {
140 | public:
141 | // init fuctions
142 | espArtNetRDM();
143 | ~espArtNetRDM();
144 |
145 | void init(IPAddress, IPAddress, bool, const char*, const char*, uint16_t, uint16_t, uint8_t*);
146 | void init(IPAddress ip, IPAddress sub, bool dhcp, uint16_t oem, uint16_t esta, uint8_t* mac) {
147 | init(ip, sub, dhcp, "espArtNetNode", "espArtNetNode", oem, esta, mac);
148 | };
149 | void init(const char* shortName, const char* longName, uint16_t oem, uint16_t esta, uint8_t* mac) {
150 | init(INADDR_NONE, INADDR_NONE, false, shortName, longName, oem, esta, mac);
151 | setDefaultIP();
152 | };
153 | void init(const char* shortName, uint16_t oem, uint16_t esta, uint8_t* mac) {
154 | init(INADDR_NONE, INADDR_NONE, false, shortName, shortName, oem, esta, mac);
155 | setDefaultIP();
156 | };
157 | void init(uint16_t oem, uint16_t esta, uint8_t* mac) {
158 | init(INADDR_NONE, INADDR_NONE, false, "espArtNetNode", "espArtNetNode", oem, esta, mac);
159 | setDefaultIP();
160 | };
161 |
162 | void setFirmwareVersion(uint16_t);
163 | void setDefaultIP();
164 |
165 | uint8_t addGroup(uint8_t, uint8_t);
166 |
167 | uint8_t addPort(uint8_t, uint8_t, uint8_t, uint8_t, bool, uint8_t*);
168 | uint8_t addPort(uint8_t group, uint8_t port, uint8_t universe, uint8_t type, bool htp) {
169 | return addPort(group, port, universe, type, htp, 0);
170 | };
171 | uint8_t addPort(uint8_t group, uint8_t port, uint8_t universe, uint8_t type) {
172 | return addPort(group, port, universe, type, true, 0);
173 | };
174 | uint8_t addPort(uint8_t group, uint8_t port, uint8_t universe) {
175 | return addPort(group, port, universe, DMX_OUT, true, 0);
176 | };
177 |
178 | bool closePort(uint8_t, uint8_t);
179 | void begin();
180 | void end();
181 | void pause();
182 | uint8_t* getDMX(uint8_t, uint8_t);
183 | uint16_t numChans(uint8_t, uint8_t);
184 |
185 | // sACN functions
186 | void setE131(uint8_t, uint8_t, bool);
187 | bool getE131(uint8_t, uint8_t);
188 | void setE131Uni(uint8_t, uint8_t, uint16_t);
189 |
190 | // handler function for including in loop()
191 | void handler();
192 |
193 | // set callback functions
194 | void setArtDMXCallback(void (*dmxCallBack)(uint8_t, uint8_t, uint16_t, bool));
195 | void setArtRDMCallback(void (*rdmCallBack)(uint8_t, uint8_t, rdm_data*));
196 | void setArtSyncCallback(void (*syncCallBack)());
197 | void setArtIPCallback(void (*ipCallBack)());
198 | void setArtAddressCallback(void (*addressCallBack)());
199 | void setTODRequestCallback(void (*artTodRequestCallBack)(uint8_t, uint8_t));
200 | void setTODFlushCallback(void (*artTodFlushCallBack)(uint8_t, uint8_t));
201 |
202 | // set ArtNet uni settings
203 | void setNet(uint8_t, uint8_t);
204 | void setSubNet(uint8_t, uint8_t);
205 | void setUni(uint8_t, uint8_t, uint8_t);
206 | void setPortType(uint8_t, uint8_t, uint8_t);
207 |
208 | // get ArtNet uni settings
209 | uint8_t getNet(uint8_t);
210 | uint8_t getSubNet(uint8_t);
211 | uint8_t getUni(uint8_t, uint8_t);
212 |
213 | // set network settings
214 | void setIP(IPAddress, IPAddress);
215 | void setIP(IPAddress ip) {
216 | setIP(ip, INADDR_NONE);
217 | }
218 | void setDHCP(bool);
219 |
220 | // Set Merge & node name
221 | void setMerge(uint8_t, uint8_t, bool);
222 | bool getMerge(uint8_t, uint8_t);
223 | void setShortName(const char*);
224 | const char* getShortName();
225 | void setLongName(const char*);
226 | const char* getLongName();
227 |
228 | // RDM functions
229 | void rdmResponse(rdm_data*, uint8_t, uint8_t);
230 | void artTODData(uint8_t, uint8_t, uint16_t*, uint32_t*, uint16_t, uint8_t);
231 |
232 | // get network settings
233 | IPAddress getIP();
234 | IPAddress getSubnetMask();
235 | bool getDHCP();
236 |
237 | void setNodeReport(const char*, uint16_t);
238 | void artPollReply();
239 |
240 | void sendDMX(uint8_t, uint8_t, IPAddress, uint8_t*, uint16_t);
241 |
242 | private:
243 | artnet_device* _art = 0;
244 |
245 | int _artOpCode(unsigned char*);
246 | void _artIPProgReply();
247 |
248 | // handlers for received packets
249 | void _artPoll(void);
250 | void _artDMX(unsigned char*);
251 | void _saveDMX(unsigned char*, uint16_t, uint8_t, uint8_t, IPAddress, uint16_t);
252 | void _artIPProg(unsigned char*);
253 | void _artAddress(unsigned char*);
254 | void _artSync(unsigned char*);
255 | void _artFirmwareMaster(unsigned char*);
256 | void _artTODRequest(unsigned char*);
257 | void _artTODControl(unsigned char*);
258 | void _artRDM(unsigned char*, uint16_t);
259 | void _artRDMSub(unsigned char*);
260 |
261 | void _e131Receive(e131_packet_t*);
262 |
263 | uint8_t _dmxSeqID = 0;
264 | uint8_t e131Count = 0; // the number of e131 ports currently open
265 |
266 | WiFiUDP eUDP;
267 | WiFiUDP fUDP;
268 | };
269 |
270 |
271 | #endif // #ifndef espArtNetRDM_h
272 |
--------------------------------------------------------------------------------
/ArtNetNode/espDMX_RDM.cpp:
--------------------------------------------------------------------------------
1 | /*
2 | espDMX v2 library
3 | Copyright (c) 2016, Matthew Tong
4 | https://github.com/mtongnz/espDMX
5 |
6 | This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public
7 | License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any
8 | later version.
9 |
10 | This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
11 | warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
12 |
13 | You should have received a copy of the GNU General Public License along with this program.
14 | If not, see http://www.gnu.org/licenses/
15 | */
16 | #include
17 |
18 | #include "espDMX_RDM.h"
19 |
20 | #include "soc/uart_reg.h"
21 | #include "soc/uart_struct.h"
22 |
23 | #define UART_REG_BASE(u) ((u==0)?DR_REG_UART_BASE:( (u==1)?DR_REG_UART1_BASE:( (u==2)?DR_REG_UART2_BASE:0)))
24 | #define UART_RXD_IDX(u) ((u==0)?U0RXD_IN_IDX:( (u==1)?U1RXD_IN_IDX:( (u==2)?U2RXD_IN_IDX:0)))
25 | #define UART_TXD_IDX(u) ((u==0)?U0TXD_OUT_IDX:( (u==1)?U1TXD_OUT_IDX:( (u==2)?U2TXD_OUT_IDX:0)))
26 | #define UART_INTR_SOURCE(u) ((u==0)?ETS_UART0_INTR_SOURCE:( (u==1)?ETS_UART1_INTR_SOURCE:((u==2)?ETS_UART2_INTR_SOURCE:0)))
27 |
28 | static intr_handle_t uart_intr_handle[2] = { 0 };
29 | static uart_dev_t *uart_dev_array[2] = {
30 | (volatile uart_dev_t *)(DR_REG_UART_BASE),
31 | (volatile uart_dev_t *)(DR_REG_UART1_BASE),
32 | };
33 |
34 | espDMX dmxA(0);
35 | espDMX dmxB(1);
36 |
37 | void dmx_interrupt_handler(void);
38 |
39 | uint16_t dmx_get_tx_fifo_room(dmx_t* dmx);
40 | void dmx_interrupt_enable(dmx_t* dmx);
41 | void dmx_interrupt_arm(dmx_t* dmx);
42 | void dmx_interrupt_disarm(dmx_t* dmx);
43 | void rdm_interrupt_arm(dmx_t* dmx);
44 | void rdm_interrupt_disarm();
45 | void dmx_set_baudrate(dmx_t* dmx, int baud_rate);
46 | void dmx_set_chans(dmx_t* dmx, uint8_t* data, uint16_t numChans, uint16_t startChan);
47 | void dmx_buffer_update(dmx_t* dmx, uint16_t num);
48 | int dmx_state(dmx_t* dmx);
49 | void rx_flush();
50 | void dmx_flush(dmx_t* dmx);
51 | static void uart_ignore_char(char c);
52 |
53 | void dmx_set_buffer(dmx_t* dmx, uint8_t* buf);
54 |
55 | void dmx_uninit(dmx_t* dmx);
56 |
57 | static bool timer1Set = false;
58 | static bool rdmInUse = false;
59 | static bool rdmBreak = false;
60 | static bool rdm_pause = false;
61 | static bool dmx_input = false;
62 | static uint8_t rxUser;
63 | static unsigned long rdmTimer = 0;
64 |
65 | void ICACHE_RAM_ATTR dmx_interrupt_handler(void) {
66 |
67 | // stop other interrupts for TX
68 | noInterrupts();
69 |
70 | if (uart_dev_array[0]->int_st.txfifo_empty) {
71 | uart_dev_array[0]->int_clr.txfifo_empty = 1; // clear status flag
72 | dmxA._transmit();
73 | }
74 |
75 | if (uart_dev_array[1]->int_st.txfifo_empty) {
76 | uart_dev_array[1]->int_clr.txfifo_empty = 1; // clear status flag
77 | dmxA._transmit();
78 | }
79 |
80 | interrupts();
81 |
82 | // RDM replies
83 | if (rdmInUse) {
84 | if ((uart_dev_array[0]->int_st.brk_det) || ( uart_dev_array[0]->int_st.frm_err)) { // RX0 Break Detect
85 | uart_dev_array[0]->int_clr.brk_det = 1; // clear status flag
86 | uart_dev_array[0]->int_clr.frm_err = 1; // clear status flag
87 | rdmBreak = true;
88 | }
89 |
90 | if (uart_dev_array[0]->int_st.rxfifo_full) { // RX0 Fifo Full
91 | if (rxUser == 0)
92 | dmxA.rdmReceived();
93 | else
94 | dmxB.rdmReceived();
95 | }
96 |
97 | // DMX input
98 | } else if (dmx_input) {
99 |
100 | // Data received
101 | while (uart_dev_array[0]->int_st.rxfifo_full) {
102 | if (rxUser == 0)
103 | dmxA.dmxReceived((uint8_t)uart_dev_array[0]->fifo.rw_byte);
104 | else
105 | dmxB.dmxReceived((uint8_t)uart_dev_array[0]->fifo.rw_byte);
106 |
107 | uart_dev_array[0]->int_clr.rxfifo_full = 1;
108 | }
109 |
110 | // Break/Frame error detect
111 | if ((uart_dev_array[0]->int_st.brk_det) || (uart_dev_array[0]->int_st.frm_err)) { // RX0 Break Detect
112 | uart_dev_array[0]->int_clr.brk_det = 1;
113 | uart_dev_array[0]->int_clr.frm_err = 1;
114 |
115 | if (rxUser == 0)
116 | dmxA.inputBreak();
117 | else
118 | dmxB.inputBreak();
119 | }
120 | }
121 | }
122 |
123 | static void uart_ignore_char(char c) {
124 | return;
125 | }
126 |
127 | uint16_t dmx_get_tx_fifo_room(dmx_t* dmx) {
128 | if (dmx == 0 || dmx->state == DMX_NOT_INIT)
129 | return 0;
130 | return UART_TX_FIFO_SIZE - uart_dev_array[0]->status.txfifo_cnt;
131 | }
132 |
133 | void dmx_flush(dmx_t* dmx) {
134 | if (dmx == 0 || dmx->state == DMX_NOT_INIT)
135 | return;
136 | // copied from esp32-hal-uart.c
137 | while (uart_dev_array[0]->status.txfifo_cnt || uart_dev_array[0]->status.st_utx_out) {};
138 | }
139 |
140 | void rx_flush() {
141 | // copied from esp32-hal-uart.c
142 | while (uart_dev_array[0]->status.rxfifo_cnt != 0 || (uart_dev_array[0]->mem_rx_status.wr_addr != uart_dev_array[0]->mem_rx_status.rd_addr)) {
143 | READ_PERI_REG(UART_FIFO_REG(0));
144 | }
145 | }
146 |
147 | void dmx_interrupt_enable(dmx_t* dmx) {
148 | if (dmx == 0 || dmx->state == DMX_NOT_INIT)
149 | return;
150 |
151 | // Clear all interrupt bits
152 | uart_dev_array[dmx->dmx_nr]->int_clr.val = 0xffffffff;
153 |
154 | // UART1 setup
155 | if (dmx->dmx_nr == 1) {
156 | // Set TX Fifo Empty trigger point
157 | uart_dev_array[1]->conf1.txfifo_empty_thrhd = 0;
158 |
159 | uint32_t clk_div = ((getApbFrequency() << 4) / DMX_TX_BAUD);
160 | uart_dev_array[1]->clk_div.div_int = clk_div >> 4 ;
161 | uart_dev_array[1]->clk_div.div_frag = clk_div & 0xf;
162 |
163 | // set to 8N2
164 | uart_dev_array[1]->conf0.parity = 0;
165 | uart_dev_array[1]->conf0.parity_en = 0;
166 | uart_dev_array[1]->conf0.bit_num = 3;
167 | uart_dev_array[1]->conf0.stop_bit_num = 3;
168 |
169 | uart_dev_array[1]->int_clr.val = 0xffffffff;
170 |
171 | esp_intr_alloc(UART_INTR_SOURCE(1), (int)ESP_INTR_FLAG_IRAM, (void (*)(void *))&dmx_interrupt_handler, NULL, &uart_intr_handle[1]);
172 | }
173 |
174 | // UART0 setup
175 | if (!timer1Set) {
176 | timer1Set = true;
177 |
178 | uint32_t clk_div = ((getApbFrequency() << 4) / DMX_TX_BAUD);
179 | uart_dev_array[0]->clk_div.div_int = clk_div >> 4 ;
180 | uart_dev_array[0]->clk_div.div_frag = clk_div & 0xf;
181 |
182 | // set to 8N2
183 | uart_dev_array[0]->conf0.parity = 0;
184 | uart_dev_array[0]->conf0.parity_en = 0;
185 | uart_dev_array[0]->conf0.bit_num = 3;
186 | uart_dev_array[0]->conf0.stop_bit_num = 3;
187 |
188 | uart_dev_array[0]->conf1.rxfifo_full_thrhd = 127;
189 |
190 | uart_dev_array[0]->int_clr.val = 0xffffffff;
191 |
192 | esp_intr_alloc(UART_INTR_SOURCE(0), (int)ESP_INTR_FLAG_IRAM, (void (*)(void *))&dmx_interrupt_handler, NULL, &uart_intr_handle[0]);
193 | }
194 | }
195 |
196 | void dmx_interrupt_arm(dmx_t* dmx) {
197 | if (dmx == 0 || dmx->state == DMX_NOT_INIT)
198 | return;
199 | // Clear all interupt bits
200 | uart_dev_array[dmx->dmx_nr]->int_clr.val = 0xffffffff;
201 |
202 | // Enable TX Fifo Empty Interupt
203 | uart_dev_array[dmx->dmx_nr]->int_ena.txfifo_empty = 1;
204 | }
205 |
206 | void dmx_interrupt_disarm(dmx_t* dmx) {
207 | if (dmx == 0 || dmx->state == DMX_NOT_INIT)
208 | return;
209 | uart_dev_array[dmx->dmx_nr]->int_ena.txfifo_empty = 0;
210 | }
211 |
212 | void rdm_interrupt_arm(dmx_t* dmx) {
213 | if (dmx == 0 || dmx->state == DMX_NOT_INIT)
214 | return;
215 |
216 | // Enable RX Fifo Full & Break Detect & Frame Error Interupts
217 | uart_dev_array[0]->int_ena.rxfifo_full = 1;
218 | uart_dev_array[0]->int_ena.brk_det = 1;
219 | uart_dev_array[0]->int_ena.frm_err = 1;
220 |
221 | digitalWrite(dmx->dirPin, LOW);
222 | rdmBreak = false;
223 | rxUser = dmx->dmx_nr;
224 | dmx->rx_pos = 0;
225 | dmx->rdm_response.clear();
226 |
227 | // Timer1 start
228 | // T1L = ((RDM_LISTEN_TIME)& 0x7FFFFF);
229 | // TEIE |= TEIE1;//edge int enable
230 |
231 | rdmTimer = micros() + 3000;
232 | }
233 |
234 | void rdm_interrupt_disarm() {
235 | // Disable RX Fifo Full & Break Detect & Frame Error Interupts
236 | uart_dev_array[0]->int_ena.rxfifo_full = 0;
237 | uart_dev_array[0]->int_ena.brk_det = 0;
238 | uart_dev_array[0]->int_ena.frm_err = 0;
239 |
240 | // TEIE &= ~TEIE1;//edge int disable
241 | // T1L = 0;
242 |
243 | rdmInUse = false;
244 | }
245 |
246 | void dmx_set_baudrate(dmx_t* dmx, int baud_rate) {
247 | if (dmx == 0 || dmx->state == DMX_NOT_INIT)
248 | return;
249 |
250 | uint32_t clk_div = ((getApbFrequency() << 4) / baud_rate);
251 | uart_dev_array[dmx->dmx_nr]->clk_div.div_int = clk_div >> 4 ;
252 | uart_dev_array[dmx->dmx_nr]->clk_div.div_frag = clk_div & 0xf;
253 | }
254 |
255 | void dmx_clear_buffer(dmx_t* dmx) {
256 | for (int i = 0; i < 512; i++)
257 | dmx->data[i] = 0;
258 |
259 | dmx->numChans = DMX_MIN_CHANS;
260 | }
261 |
262 | void dmx_set_buffer(dmx_t* dmx, uint8_t* buf) {
263 | if (dmx == 0 || dmx->state == DMX_NOT_INIT)
264 | return;
265 |
266 | if (dmx->ownBuffer)
267 | free(dmx->data);
268 |
269 | if (buf == NULL) {
270 | buf = (uint8_t*) malloc(sizeof(uint8_t) * 512);
271 |
272 | if (!buf) {
273 | free(buf);
274 | dmx->ownBuffer = 0;
275 | return;
276 | }
277 | dmx->ownBuffer = 1;
278 | } else
279 | dmx->ownBuffer = 0;
280 |
281 | dmx->data = buf;
282 |
283 | }
284 |
285 | void dmx_uninit(dmx_t* dmx) {
286 | if (dmx == 0 || dmx->state == DMX_NOT_INIT)
287 | return;
288 |
289 | dmx_interrupt_disarm(dmx);
290 | dmx_flush(dmx);
291 |
292 | pinMode(dmx->txPin, OUTPUT);
293 | digitalWrite(dmx->txPin, HIGH);
294 |
295 | // Set DMX direction to input so no garbage is sent out
296 | if (dmx->dirPin != 255)
297 | digitalWrite(dmx->dirPin, LOW);
298 |
299 | if (dmx->dmx_nr == rxUser) {
300 | rdm_interrupt_disarm();
301 | rx_flush();
302 | }
303 |
304 | if (dmx->rdm_enable) {
305 | dmx->rdm_enable = 0;
306 | digitalWrite(dmx->dirPin, HIGH);
307 |
308 | dmx->todManID = (uint16_t*)realloc(dmx->todManID, 0);
309 | dmx->todDevID = (uint32_t*)realloc(dmx->todDevID, 0);
310 |
311 | dmx->rdmCallBack = NULL;
312 | dmx->todCallBack = NULL;
313 | }
314 |
315 | free(dmx->data1);
316 | dmx->data1 = 0;
317 |
318 | dmx->isInput = false;
319 | dmx->inputCallBack = NULL;
320 |
321 | if (dmx->ownBuffer)
322 | free(dmx->data);
323 | }
324 |
325 | int dmx_get_state(dmx_t* dmx) {
326 | return dmx->state;
327 | }
328 |
329 | void dmx_set_state(dmx_t* dmx, int state) {
330 | dmx->state = state;
331 | }
332 |
333 | void dmx_set_chans(dmx_t* dmx, uint8_t* data, uint16_t num, uint16_t start) {
334 | if (dmx == 0 || dmx->state == DMX_NOT_INIT)
335 | return;
336 |
337 | dmx->started = true;
338 |
339 | uint16_t newNum = start + num - 1;
340 | if (newNum > 512)
341 | newNum = 512;
342 |
343 | // Is there any new channel data
344 | if (memcmp(data, &(dmx->data[start - 1]), num) != 0) {
345 | // Find the highest channel with new data
346 | for (; newNum >= dmx->numChans; newNum--, num--) {
347 | if (dmx->data[newNum - 1] != data[num - 1])
348 | break;
349 | }
350 | newNum += DMX_ADD_CHANS;
351 |
352 | // If we receive tiny data input, just output minimum channels
353 | if (newNum < DMX_MIN_CHANS)
354 | newNum = DMX_MIN_CHANS;
355 |
356 | // Put data into our buffer
357 | memcpy(&(dmx->data[start - 1]), data, num);
358 |
359 | if (newNum > dmx->numChans)
360 | dmx->numChans = (newNum > 512) ? 512 : newNum;
361 | dmx->newDMX = true;
362 | //dmx_transmit(dmx);
363 | }
364 | }
365 |
366 | void dmx_buffer_update(dmx_t* dmx, uint16_t num) {
367 | if (dmx == 0 || dmx->state == DMX_NOT_INIT || num <= dmx->numChans)
368 | return;
369 |
370 | dmx->started = true;
371 |
372 | if (num > 512)
373 | num = 512;
374 |
375 | // Find the highest channel with data
376 | for (; num >= dmx->numChans; num--) {
377 | if (dmx->data[num - 1] != 0)
378 | break;
379 | }
380 | num += DMX_ADD_CHANS;
381 |
382 | // If we receive tiny data input, just output minimum channels
383 | if (num < DMX_MIN_CHANS)
384 | num = DMX_MIN_CHANS;
385 |
386 | if (num > dmx->numChans)
387 | dmx->numChans = (num > 512) ? 512 : num;
388 |
389 | dmx->newDMX = true;
390 | //dmx_transmit(dmx);
391 | }
392 |
393 | espDMX::espDMX(uint8_t dmx_nr) :
394 | _dmx_nr(dmx_nr), _dmx(0) {
395 | }
396 |
397 | espDMX::~espDMX(void) {
398 | end();
399 | }
400 |
401 | void espDMX::begin(uint8_t dir, uint8_t* buf) {
402 | if (_dmx == 0) {
403 | _dmx = (dmx_t*) malloc(sizeof(dmx_t));
404 |
405 | if (_dmx == 0) {
406 | free(_dmx);
407 | _dmx = 0;
408 | return;
409 | }
410 |
411 | _dmx->data1 = (uint8_t*) malloc(sizeof(uint8_t) * 512);
412 | memset(_dmx->data1, 0, 512);
413 |
414 | _dmx->ownBuffer = 0;
415 |
416 | ets_install_putc1((void (*)(char))&uart_ignore_char);
417 |
418 | // Initialize variables
419 | _dmx->dmx_nr = _dmx_nr;
420 | _dmx->txPin = (_dmx->dmx_nr == 0) ? 1 : 2;
421 | _dmx->state = DMX_STOP;
422 | _dmx->txChan = 0;
423 | _dmx->full_uni_time = 0;
424 | _dmx->last_dmx_time = 0;
425 | _dmx->led_timer = 0;
426 | _dmx->newDMX = false;
427 | _dmx->started = false;
428 | _dmx->rdm_enable = false;
429 | _dmx->dirPin = dir; // 255 is used to indicate no dir pin
430 |
431 | _dmx->rdmCallBack = NULL;
432 | _dmx->todCallBack = NULL;
433 | _dmx->isInput = false;
434 | _dmx->inputCallBack = NULL;
435 |
436 |
437 | // TX output set to idle
438 | pinMode(_dmx->txPin, OUTPUT);
439 | digitalWrite(_dmx->txPin, HIGH);
440 |
441 | // Set direction to output
442 | if (_dmx->dirPin != 255) {
443 | pinMode(_dmx->dirPin, OUTPUT);
444 | digitalWrite(_dmx->dirPin, HIGH);
445 | }
446 | }
447 |
448 | if (_dmx) {
449 | dmx_set_buffer(_dmx, buf);
450 | dmx_clear_buffer(_dmx);
451 | dmx_interrupt_enable(_dmx);
452 | }
453 | }
454 |
455 | void espDMX::setBuffer(uint8_t* buf) {
456 | dmx_set_buffer(_dmx, buf);
457 | }
458 |
459 | void espDMX::pause() {
460 | dmx_interrupt_disarm(_dmx);
461 |
462 | dmx_flush(_dmx);
463 |
464 | digitalWrite(_dmx->dirPin, HIGH);
465 | }
466 |
467 | void espDMX::unPause() {
468 | if (_dmx == 0 || _dmx->state == DMX_NOT_INIT)
469 | return;
470 |
471 | _dmx->newDMX = true;
472 | _dmx->state = DMX_STOP;
473 |
474 | digitalWrite(_dmx->dirPin, HIGH);
475 |
476 | //dmx_transmit(_dmx);
477 | }
478 |
479 | void espDMX::end() {
480 | if (_dmx == 0)
481 | return;
482 |
483 | dmx_uninit(_dmx);
484 |
485 | free(_dmx);
486 |
487 | _dmx = 0;
488 | }
489 |
490 | void espDMX::setChans(uint8_t *data, uint16_t numChans, uint16_t startChan) {
491 | dmx_set_chans(_dmx, data, numChans, startChan);
492 | }
493 |
494 | void espDMX::chanUpdate(uint16_t numChans) {
495 | dmx_buffer_update(_dmx, numChans);
496 | }
497 |
498 | void espDMX::clearChans() {
499 | if (_dmx == 0 || _dmx->state == DMX_NOT_INIT)
500 | return;
501 |
502 | dmx_clear_buffer(_dmx);
503 | }
504 |
505 | uint8_t *espDMX::getChans() {
506 | if (_dmx == 0 || _dmx->state == DMX_NOT_INIT)
507 | return 0;
508 |
509 | return _dmx->data;
510 | }
511 |
512 | uint16_t espDMX::numChans() {
513 | if (_dmx == 0 || _dmx->state == DMX_NOT_INIT)
514 | return 0;
515 |
516 | return _dmx->numChans;
517 | }
518 |
519 | void espDMX::ledIntensity(uint8_t newIntensity) {
520 | if (_dmx == 0 || _dmx->state == DMX_NOT_INIT)
521 | return;
522 |
523 | _dmx->ledIntensity = newIntensity;
524 | }
525 |
526 | void ICACHE_RAM_ATTR espDMX::_transmit(void) {
527 | // If we have data to transmit
528 | if (_dmx->txChan < _dmx->txSize) {
529 |
530 | // Keep the number of uint8_ts sent low to keep it quick
531 | // uint16_t txSize = dmx->txSize - dmx->txChan;
532 | // txSize = (txSize > DMX_MAX_BYTES_PER_INT) ? DMX_MAX_BYTES_PER_INT : txSize;
533 |
534 | // for(; txSize; --txSize)
535 | uart_dev_array[_dmx->dmx_nr]->fifo.rw_byte = _dmx->data1[_dmx->txChan++];
536 |
537 | // dmx_interrupt_arm(dmx);
538 |
539 | // If all uint8_ts are transmitted
540 | } else {
541 |
542 | //dmx_interrupt_disarm(_dmx);
543 | uart_dev_array[_dmx->dmx_nr]->int_ena.txfifo_empty = 0;
544 |
545 | if (_dmx->state == DMX_TX) {
546 |
547 | _dmx->state = DMX_STOP;
548 |
549 | } else if (!rdm_pause) { // if (_dmx->state == RDM_TX) {
550 |
551 | _dmx->state = RDM_RX;
552 | rdm_interrupt_arm(_dmx);
553 | }
554 | }
555 | }
556 |
557 | bool espDMX::rdmSendCommand(rdm_data* data) {
558 | if (_dmx == 0 || !_dmx->rdm_enable || _dmx->rdm_queue.isFull())
559 | return false;
560 |
561 | if (system_get_free_heap_size() < 2000)
562 | return false;
563 |
564 | uint8_t packetLength = data->packet.Length;
565 | uint16_t checkSum = 0x0000;
566 | for (uint8_t x = 0; x < packetLength; x++) {
567 | checkSum += data->buffer[x];
568 | }
569 | checkSum = checkSum % 0x10000;
570 |
571 | data->buffer[packetLength] = checkSum >> 8;
572 | data->buffer[packetLength + 1] = checkSum & 0xFF;
573 |
574 | bool r = _dmx->rdm_queue.push(data);
575 |
576 | return r;
577 | }
578 |
579 | bool espDMX::rdmSendCommand(uint8_t cmdClass, uint16_t pid, uint16_t manID, uint32_t devID, uint8_t* data, uint16_t dataLength, uint16_t subDev) {
580 | if (_dmx == 0 || !_dmx->rdm_enable)
581 | return false;
582 |
583 | rdm_data command;
584 |
585 | // Note that all ints are stored little endian so we need to flip them
586 | // to get correct uint8_t order
587 |
588 | command.packet.StartCode = (E120_SC_RDM << 8) | E120_SC_SUB_MESSAGE;
589 | command.packet.Length = 24 + dataLength;
590 | command.packet.DestMan = manID;
591 | command.packet.DestDev = devID;
592 | command.packet.SourceMan = _dmx->rdm_source_man;
593 | command.packet.SourceDev = _dmx->rdm_source_dev;
594 | command.packet.TransNo = _dmx->rdm_trans_no++;
595 | command.packet.ResponseType = 0x01;
596 | command.packet.MsgCount = 0;
597 | command.packet.SubDev = subDev;
598 | command.packet.CmdClass = cmdClass;
599 | command.packet.PID = pid;
600 | command.packet.DataLength = dataLength;
601 | if (dataLength > 0)
602 | memcpy(command.packet.Data, data, dataLength);
603 |
604 | return rdmSendCommand(&command);
605 | }
606 |
607 | void espDMX::rdmReceived() {
608 | if (_dmx == 0 || _dmx->state != RDM_RX)
609 | return;
610 |
611 | while (uart_dev_array[0]->status.rxfifo_cnt) {
612 | _dmx->rdm_response.buffer[_dmx->rx_pos] = uart_dev_array[0]->fifo.rw_byte;
613 |
614 | // Handle multiple 0xFE to start discovery response
615 | if (_dmx->rx_pos == 1 && _dmx->rdm_response.buffer[0] == 0xFE && _dmx->rdm_response.buffer[1] == 0xFE)
616 | continue;
617 |
618 | // Handle break & MAB
619 | if (rdmBreak || _dmx->rdm_response.buffer[0] == 0) {
620 | _dmx->rx_pos = 0;
621 | rdmBreak = false;
622 | continue;
623 | }
624 | _dmx->rx_pos++;
625 | }
626 | // Clear interupt flags
627 | uart_dev_array[0]->int_clr.val = 0xffffffff;
628 | }
629 |
630 | void espDMX::rdmDiscovery(uint8_t discType) {
631 | if (!_dmx || !_dmx->rdm_enable)
632 | return;
633 |
634 | if (discType == RDM_DISCOVERY_TOD_WIPE) {
635 | _dmx->tod_size = 0;
636 |
637 | _dmx->todManID = (uint16_t*)realloc(_dmx->todManID, 0);
638 | _dmx->todDevID = (uint32_t*)realloc(_dmx->todDevID, 0);
639 |
640 | _dmx->tod_status = RDM_TOD_NOT_READY;
641 |
642 | discType = RDM_DISCOVERY_FULL;
643 | }
644 |
645 | uint8_t startEnd[12] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
646 |
647 | if (discType == RDM_DISCOVERY_FULL) {
648 | _dmx->tod_changed = true;
649 | rdmSendCommand(E120_DISCOVERY_COMMAND, E120_DISC_UN_MUTE, 0xFFFF, 0xFFFFFFFF);
650 | rdmSendCommand(E120_DISCOVERY_COMMAND, E120_DISC_UNIQUE_BRANCH, 0xFFFF, 0xFFFFFFFF, startEnd, 12);
651 |
652 | // discType == RDM_DISCOVERY_INCREMENTAL
653 | } else {
654 | if (_dmx->rdm_discovery_pos >= _dmx->tod_size) {
655 | rdmSendCommand(E120_DISCOVERY_COMMAND, E120_DISC_UNIQUE_BRANCH, 0xFFFF, 0xFFFFFFFF, startEnd, 12);
656 | } else {
657 | rdmSendCommand(E120_DISCOVERY_COMMAND, E120_DISC_MUTE, _dmx->todManID[_dmx->rdm_discovery_pos], _dmx->todDevID[_dmx->rdm_discovery_pos]);
658 | _dmx->rdm_discovery_pos++;
659 | }
660 | }
661 | }
662 |
663 | void espDMX::rdmDiscoveryResponse(rdm_data* c) {
664 | // If we received nothing, branch is empty
665 | if (_dmx->rx_pos == 0) {
666 | uint8_t a[12] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
667 |
668 | // If it's a reply to the top branch, all devices are found
669 | if (memcmp(c->packet.Data, a, 12) == 0) {
670 |
671 | _dmx->rdm_last_discovery = millis();
672 | _dmx->rdm_discovery_pos = 0;
673 |
674 | _dmx->tod_status = RDM_TOD_READY;
675 |
676 | if (_dmx->tod_changed && _dmx->todCallBack != 0) {
677 | _dmx->tod_changed = false;
678 | _dmx->todCallBack();
679 | }
680 |
681 | // Issue un-mute to all so no devices hide on the next incremental discovery
682 | rdmSendCommand(E120_DISCOVERY_COMMAND, E120_DISC_UN_MUTE, 0xFFFF, 0xFFFFFFFF);
683 | }
684 |
685 | return;
686 | }
687 |
688 | // Check for correct length & no frame errors
689 | if (_dmx->rdm_response.discovery.headerFE == 0xFE && _dmx->rdm_response.discovery.headerAA == 0xAA) {
690 | uint16_t _manID;
691 | uint32_t _devID;
692 | uint8_t* maskedDevID = _dmx->rdm_response.discovery.maskedDevID;
693 | uint8_t* maskedChkSm = _dmx->rdm_response.discovery.maskedChecksum;
694 |
695 | _manID = (maskedDevID[0] & maskedDevID[1]);
696 | _manID = (_manID << 8) + (maskedDevID[2] & maskedDevID[3]);
697 | _devID = (maskedDevID[4] & maskedDevID[5]);
698 | _devID = (_devID << 8) + (maskedDevID[6] & maskedDevID[7]);
699 | _devID = (_devID << 8) + (maskedDevID[8] & maskedDevID[9]);
700 | _devID = (_devID << 8) + (maskedDevID[10] & maskedDevID[11]);
701 |
702 | // Calculate checksum
703 | uint16_t checkSum = 0;
704 | for (uint8_t x = 0; x < 12; x++)
705 | checkSum += maskedDevID[x];
706 | checkSum = checkSum % 10000;
707 | uint16_t mChk = (maskedChkSm[0] & maskedChkSm[1]);
708 | mChk = (mChk << 8) | (maskedChkSm[2] & maskedChkSm[3]);
709 |
710 | // If the checksum is valid
711 | if (checkSum == mChk) {
712 | // Send mute command to check device is there & to mute from further discovery requests
713 | rdmSendCommand(E120_DISCOVERY_COMMAND, E120_DISC_MUTE, _manID, _devID);
714 |
715 | // Recheck the branch
716 | c->packet.TransNo = _dmx->rdm_trans_no++;
717 | rdmSendCommand(c);
718 |
719 | return;
720 | }
721 | }
722 |
723 | // If we didn't get a valid response, split branch and try again
724 |
725 | uint64_t m = 0;
726 | uint64_t n = 0;
727 | uint64_t e = 0;
728 |
729 | // Get current end address
730 | for (uint8_t x = 6; x < 12; x++)
731 | e = (e << 8) | c->packet.Data[x];
732 |
733 | // Calculate the midpoint & midpoint + 1
734 | e = e << 16;
735 | m = e >> 1;
736 | n = m + 1;
737 |
738 | // Check if we're at the bottom branch
739 | if (n == e) {
740 | uint16_t a = __builtin_bswap16(e >> 32);
741 | uint32_t b = __builtin_bswap32(e & 0xFFFFFFFF);
742 |
743 | // Send mute command to check device is there & to mute from further discovery requests
744 | rdmSendCommand(E120_DISCOVERY_COMMAND, E120_DISC_MUTE, a, b);
745 |
746 | return;
747 | }
748 |
749 | // Bitswap to fix endianess
750 | m = __builtin_bswap64(m);
751 | e = __builtin_bswap64(e);
752 | n = __builtin_bswap64(n);
753 |
754 | // If we reach max queue size, wait for a bit and try again
755 | while (_dmx->rdm_queue.space() < 2) {
756 | yield();
757 | }
758 |
759 | // Send command for lower half
760 | memcpy(&c->packet.Data[6], &m, 6);
761 | c->packet.TransNo = _dmx->rdm_trans_no++;
762 | rdmSendCommand(c);
763 |
764 | // Send command for upper half
765 | memcpy(c->packet.Data, &n, 6);
766 | memcpy(&c->packet.Data[6], &e, 6);
767 | c->packet.TransNo = _dmx->rdm_trans_no++;
768 | rdmSendCommand(c);
769 | }
770 |
771 | void espDMX::rdmMuteResponse(rdm_data* c) {
772 | _dmx->rdm_response.endianFlip();
773 |
774 | // Check for correct length & ACK response
775 | if (_dmx->rx_pos > 15) {
776 | if (c->packet.DestMan == _dmx->rdm_response.packet.SourceMan && c->packet.DestDev == _dmx->rdm_response.packet.SourceDev && _dmx->rdm_response.packet.ResponseType == E120_RESPONSE_TYPE_ACK) {
777 | uint16_t checkSum = 0;
778 | uint8_t x = 0;
779 |
780 | for (; x < _dmx->rdm_response.packet.Length; x++)
781 | checkSum += _dmx->rdm_response.buffer[x];
782 |
783 | checkSum = checkSum % 10000;
784 |
785 | // Check the checksum
786 | if (_dmx->rdm_response.buffer[x] == (checkSum >> 8) && _dmx->rdm_response.buffer[x + 1] == (checkSum & 0xFF)) {
787 |
788 | // Is the device already in our UID list
789 | for (uint16_t x = 0; x < _dmx->tod_size; x++) {
790 | if (_dmx->todManID[x] == _dmx->rdm_response.packet.SourceMan && _dmx->todDevID[x] == _dmx->rdm_response.packet.SourceDev) {
791 |
792 | if (x == _dmx->rdm_discovery_pos)
793 | _dmx->rdm_discovery_pos++;
794 |
795 | return;
796 | }
797 | }
798 |
799 | // Add the deivce to our UID list
800 | _dmx->todManID = (uint16_t*)realloc(_dmx->todManID, (_dmx->tod_size + 1) * sizeof(uint16_t));
801 | _dmx->todDevID = (uint32_t*)realloc(_dmx->todDevID, (_dmx->tod_size + 1) * sizeof(uint32_t));
802 |
803 | _dmx->todManID[_dmx->tod_size] = _dmx->rdm_response.packet.SourceMan;
804 | _dmx->todDevID[_dmx->tod_size] = _dmx->rdm_response.packet.SourceDev;
805 |
806 | _dmx->tod_size++;
807 | _dmx->tod_changed = true;
808 |
809 | }
810 | }
811 |
812 |
813 | // No response received
814 |
815 | } else {
816 | // Delete devices from TOD if they didn't respond
817 | for (uint16_t x = 0; x < _dmx->tod_size; x++) {
818 | if (_dmx->todManID[x] == c->packet.DestMan && _dmx->todDevID[x] == c->packet.DestDev) {
819 |
820 | // Shift all our devices up the list
821 | for (uint16_t y = x + 1; y < _dmx->tod_size; y++) {
822 | _dmx->todManID[y - 1] = _dmx->todManID[y];
823 | _dmx->todDevID[y - 1] = _dmx->todDevID[y];
824 | }
825 |
826 | _dmx->tod_size--;
827 | _dmx->tod_changed = true;
828 |
829 | _dmx->rdm_discovery_pos = 0;
830 |
831 | _dmx->todManID = (uint16_t*)realloc(_dmx->todManID, _dmx->tod_size * sizeof(uint16_t));
832 | _dmx->todDevID = (uint32_t*)realloc(_dmx->todDevID, _dmx->tod_size * sizeof(uint32_t));
833 |
834 | return;
835 | }
836 | }
837 | }
838 | }
839 |
840 | void espDMX::rdmRXTimeout() {
841 | if (_dmx == 0)
842 | return;
843 |
844 | if (rdm_pause) {
845 | rdm_interrupt_disarm();
846 | dmx_flush(_dmx);
847 |
848 | _dmx->state = DMX_STOP;
849 | digitalWrite(_dmx->dirPin, HIGH);
850 |
851 | //dmx_transmit(_dmx);
852 | return;
853 | }
854 |
855 | // Get remaining data
856 | rdmReceived();
857 |
858 | _dmx->state = DMX_STOP;
859 | digitalWrite(_dmx->dirPin, HIGH);
860 |
861 | rdm_interrupt_disarm();
862 |
863 | rdm_data c;
864 | _dmx->rdm_queue.pop(&c);
865 |
866 | //dmx_transmit(_dmx);
867 |
868 | if (c.packet.CmdClass == E120_DISCOVERY_COMMAND) {
869 | if (c.packet.PID == E120_DISC_UNIQUE_BRANCH) {
870 | rdmDiscoveryResponse(&c);
871 | return;
872 | } else if (c.packet.PID == E120_DISC_MUTE) {
873 | rdmMuteResponse(&c);
874 | return;
875 | } else if (c.packet.PID == E120_DISC_UN_MUTE) {
876 | // There shouldn't be a response to un mute commands
877 | return;
878 | }
879 | }
880 |
881 | if (_dmx->rdmCallBack != NULL)
882 | _dmx->rdmCallBack(&_dmx->rdm_response);
883 | }
884 |
885 | void espDMX::rdmEnable(uint16_t ManID, uint32_t DevID) {
886 | if (_dmx == 0 || _dmx->dirPin == 255 || dmx_input)
887 | return;
888 |
889 |
890 | // RDM Variables
891 | _dmx->rx_pos = 0;
892 | _dmx->rdm_trans_no = 0;
893 | _dmx->rdm_discovery = false;
894 | _dmx->rdm_last_discovery = 0;
895 | _dmx->todManID = NULL;
896 | _dmx->todDevID = NULL;
897 | _dmx->tod_size = 0;
898 | _dmx->tod_status = RDM_TOD_NOT_READY;
899 | _dmx->rdm_discovery_pos = 0;
900 | _dmx->rdmCallBack = NULL;
901 |
902 | _dmx->rdm_enable = true;
903 | _dmx->rdm_queue.init();
904 |
905 | // Setup direction pin
906 | digitalWrite(_dmx->dirPin, HIGH);
907 |
908 | // Enable RX pin (same for both universes)
909 | pinMode(3, SPECIAL);
910 |
911 | _dmx->rdm_source_man = ManID;
912 | _dmx->rdm_source_dev = DevID;
913 |
914 | rdmDiscovery();
915 | }
916 |
917 | void espDMX::rdmDisable() {
918 | if (_dmx == 0)
919 | return;
920 |
921 | if (rdmInUse && rxUser == _dmx->dmx_nr) {
922 | rdmInUse = false;
923 | _dmx->state = DMX_STOP;
924 | }
925 |
926 | _dmx->rdm_enable = false;
927 |
928 | digitalWrite(_dmx->dirPin, HIGH);
929 | }
930 |
931 | uint8_t espDMX::todStatus() {
932 | if (_dmx == 0 || !_dmx->rdm_enable)
933 | return false;
934 |
935 | return _dmx->tod_status;
936 | }
937 |
938 | uint16_t espDMX::todCount() {
939 | if (_dmx == 0 || !_dmx->rdm_enable)
940 | return false;
941 |
942 | return _dmx->tod_size;
943 | }
944 |
945 |
946 | uint16_t* espDMX::todMan() {
947 | if (_dmx == 0 || !_dmx->rdm_enable)
948 | return NULL;
949 |
950 | return _dmx->todManID;
951 | }
952 |
953 | uint32_t* espDMX::todDev() {
954 | if (_dmx == 0 || !_dmx->rdm_enable)
955 | return NULL;
956 |
957 | return _dmx->todDevID;
958 | }
959 |
960 | uint16_t espDMX::todMan(uint16_t n) {
961 | if (_dmx == 0 || !_dmx->rdm_enable)
962 | return NULL;
963 |
964 | return _dmx->todManID[n];
965 | }
966 |
967 | uint32_t espDMX::todDev(uint16_t n) {
968 | if (_dmx == 0 || !_dmx->rdm_enable)
969 | return NULL;
970 |
971 | return _dmx->todDevID[n];
972 | }
973 |
974 |
975 | void espDMX::rdmSetCallBack(rdmCallBackFunc callback) {
976 | if (_dmx == 0)
977 | return;
978 |
979 | _dmx->rdmCallBack = callback;
980 | }
981 |
982 | void espDMX::todSetCallBack(todCallBackFunc callback) {
983 | if (_dmx == 0)
984 | return;
985 |
986 | _dmx->todCallBack = callback;
987 | }
988 |
989 | bool espDMX::rdmEnabled() {
990 | if (_dmx == 0)
991 | return 0;
992 | return _dmx->rdm_enable;
993 | }
994 |
995 | void rdmPause(bool p) {
996 | if (dmx_input && p == false)
997 | return;
998 |
999 | rdm_pause = p;
1000 |
1001 | if (p) {
1002 | if (rdmInUse) {
1003 | if (rxUser == 0)
1004 | dmxA.rdmRXTimeout();
1005 | else
1006 | dmxB.rdmRXTimeout();
1007 | }
1008 | rdmInUse = false;
1009 | } else {
1010 | dmxA.rdmDiscovery(RDM_DISCOVERY_FULL);
1011 | dmxB.rdmDiscovery(RDM_DISCOVERY_FULL);
1012 | }
1013 | }
1014 |
1015 |
1016 | void espDMX::dmxIn(bool doIn) {
1017 | if (_dmx == 0)
1018 | return;
1019 |
1020 | if (doIn) {
1021 | _dmx->isInput = true;
1022 |
1023 | // Clear our buffers
1024 | memset(_dmx->data, 0, 512);
1025 | memset(_dmx->data1, 0, 512);
1026 |
1027 | dmx_interrupt_disarm(_dmx);
1028 | rdmPause(true);
1029 |
1030 | // Turn RX pin into UART mode
1031 | pinMode(3, SPECIAL);
1032 |
1033 | // If dirPin is specified then set to in direction
1034 | if (_dmx->dirPin != 255) {
1035 | pinMode(_dmx->dirPin, OUTPUT);
1036 | digitalWrite(_dmx->dirPin, LOW);
1037 | }
1038 |
1039 | // Set txPin to idle
1040 | digitalWrite(_dmx->txPin, HIGH);
1041 |
1042 | dmx_input = true;
1043 | rxUser = _dmx->dmx_nr;
1044 | _dmx->state = DMX_RX_IDLE;
1045 |
1046 | noInterrupts();
1047 |
1048 | uint32_t clk_div = ((getApbFrequency() << 4) / DMX_TX_BAUD);
1049 | uart_dev_array[0]->clk_div.div_int = clk_div >> 4 ;
1050 | uart_dev_array[0]->clk_div.div_frag = clk_div & 0xf;
1051 |
1052 | // set to 8N2
1053 | uart_dev_array[0]->conf0.parity = 0;
1054 | uart_dev_array[0]->conf0.parity_en = 0;
1055 | uart_dev_array[0]->conf0.bit_num = 3;
1056 | uart_dev_array[0]->conf0.stop_bit_num = 3;
1057 |
1058 | uart_dev_array[0]->conf1.rxfifo_full_thrhd = 1;
1059 |
1060 | rx_flush(); // flush rx buffer
1061 |
1062 | uart_dev_array[0]->int_clr.val = 0xffffffff;
1063 |
1064 | // Enable RX Fifo Full, Break Detect & Frame Error Interupts
1065 | uart_dev_array[0]->int_ena.rxfifo_full = 1;
1066 | uart_dev_array[0]->int_ena.brk_det = 1;
1067 | uart_dev_array[0]->int_ena.frm_err = 1;
1068 |
1069 | esp_intr_alloc(UART_INTR_SOURCE(0), (int)ESP_INTR_FLAG_IRAM, (void (*)(void *))&dmx_interrupt_handler, NULL, &uart_intr_handle[0]);
1070 |
1071 | interrupts();
1072 |
1073 | } else {
1074 | // Disable RX Fifo Full, Break Detect & Frame Error Interupts
1075 | uart_dev_array[0]->int_ena.rxfifo_full = 0;
1076 | uart_dev_array[0]->int_ena.brk_det = 0;
1077 | uart_dev_array[0]->int_ena.frm_err = 0;
1078 |
1079 | if (_dmx->dirPin != 255) {
1080 | pinMode(_dmx->dirPin, OUTPUT);
1081 | digitalWrite(_dmx->dirPin, HIGH);
1082 | }
1083 |
1084 | // Clear output buffer & reset channel count
1085 | memset(_dmx->data, 0, 512);
1086 | memset(_dmx->data1, 0, 512);
1087 | _dmx->numChans = 0;
1088 |
1089 | _dmx->isInput = false;
1090 | dmx_input = false;
1091 | rdmPause(false);
1092 | }
1093 | }
1094 |
1095 | void espDMX::setInputCallback(inputCallBackFunc callback) {
1096 | if (_dmx == 0)
1097 | return;
1098 |
1099 | _dmx->inputCallBack = callback;
1100 | }
1101 |
1102 | void ICACHE_RAM_ATTR espDMX::dmxReceived(uint8_t c) {
1103 | switch ( _dmx->state ) {
1104 | case DMX_RX_BREAK:
1105 | if ( c == 0 ) { //start code == zero (DMX)
1106 | _dmx->state = DMX_RX_DATA;
1107 | _dmx->numChans = _dmx->rx_pos;
1108 | _dmx->rx_pos = 0;
1109 | } else {
1110 | _dmx->state = DMX_RX_IDLE;
1111 | }
1112 | break;
1113 |
1114 | case DMX_RX_DATA:
1115 | _dmx->data1[_dmx->rx_pos++] = c;
1116 | if ( _dmx->rx_pos >= 512 ) {
1117 | _dmx->state = DMX_RX_IDLE; // go to idle, wait for next break
1118 | }
1119 | break;
1120 | }
1121 | }
1122 |
1123 | void ICACHE_RAM_ATTR espDMX::inputBreak(void) {
1124 | if (_dmx == 0)
1125 | return;
1126 |
1127 | _dmx->state = DMX_RX_BREAK;
1128 |
1129 | // Double buffer switch
1130 | uint8_t* tmp = _dmx->data;
1131 | _dmx->data = _dmx->data1;
1132 | _dmx->data1 = _dmx->data;
1133 |
1134 | if (_dmx->inputCallBack)
1135 | _dmx->inputCallBack(_dmx->numChans);
1136 | }
1137 |
1138 |
1139 | void espDMX::handler() {
1140 | if (_dmx == 0 || _dmx->state == DMX_NOT_INIT)
1141 | return;
1142 |
1143 | // Check if RDM reply should be finished yet
1144 | if (rdmInUse && rxUser == _dmx->dmx_nr && rdmTimer < micros())
1145 | rdmRXTimeout();
1146 |
1147 | // If DMX is in use then we don't need to proceed
1148 | if (_dmx->state != DMX_STOP)
1149 | return;
1150 |
1151 |
1152 | dmx_interrupt_disarm(_dmx);
1153 |
1154 | // Check if we need to do RDM
1155 | if (!rdm_pause && _dmx->rdm_enable && !rdmInUse) {
1156 |
1157 | // If we haven't finished our TOD, this will check for any remaining devices
1158 | // or if it's a while since RDM, continue with incremental discovery
1159 | if (_dmx->rdm_queue.isEmpty() && (_dmx->tod_status == RDM_TOD_NOT_READY || (_dmx->rdm_last_discovery + RDM_DISCOVERY_INC_TIME) < millis()))
1160 | rdmDiscovery(RDM_DISCOVERY_INCREMENTAL);
1161 |
1162 |
1163 | // Send RDM if there is any
1164 | noInterrupts();
1165 | if (! _dmx->rdm_queue.isEmpty()) {
1166 | rdmInUse = true;
1167 | rx_flush();
1168 |
1169 | rdm_data* c = _dmx->rdm_queue.peek();
1170 | _dmx->txSize = c->buffer[2] + 3; // Extra uint8_t added so we don't need a delay in the interrupt
1171 |
1172 | memcpy(_dmx->data1, c->buffer, _dmx->txSize - 1);
1173 |
1174 | _dmx->state = RDM_START;
1175 | }
1176 | interrupts();
1177 |
1178 | }
1179 |
1180 | // If not RDM then do DMX_START
1181 | if (_dmx->state == DMX_STOP && _dmx->started) {
1182 |
1183 | // If no new DMX and we're not needing to send a full universe then exit
1184 | //if (millis() < _dmx->full_uni_time && !_dmx->newDMX)
1185 | // return;
1186 |
1187 | // DMX Transmit
1188 | if (millis() >= _dmx->full_uni_time)
1189 | _dmx->txSize = 512;
1190 | else
1191 | _dmx->txSize = _dmx->numChans;
1192 |
1193 | // If we are sending a full universe then reset the timer
1194 | if (_dmx->txSize == 512)
1195 | _dmx->full_uni_time = millis() + DMX_FULL_UNI_TIMING;
1196 |
1197 | // Copy data into the tx buffer
1198 | memcpy(_dmx->data1, _dmx->data, _dmx->txSize);
1199 |
1200 | _dmx->state = DMX_START;
1201 | }
1202 |
1203 | if (_dmx->state == DMX_STOP)
1204 | return;
1205 |
1206 | // Wait for empty FIFO
1207 | while (uart_dev_array[_dmx->dmx_nr]->status.txfifo_cnt != 0) {
1208 | yield();
1209 | }
1210 |
1211 | // Allow last channel to be fully sent
1212 | delayMicroseconds(44);
1213 |
1214 | // BREAK of ~120us
1215 | pinMode(_dmx->txPin, OUTPUT);
1216 | digitalWrite(_dmx->txPin, LOW);
1217 | delayMicroseconds(118);
1218 |
1219 | // MAB of ~12us
1220 | digitalWrite(_dmx->txPin, HIGH);
1221 | delayMicroseconds(7);
1222 |
1223 | // Change pin to UART mode
1224 | pinMode(_dmx->txPin, SPECIAL);
1225 |
1226 | // Empty FIFO
1227 | dmx_flush(_dmx);
1228 |
1229 | if (_dmx->state == DMX_START) {
1230 |
1231 | _dmx->newDMX = false;
1232 | _dmx->state = DMX_TX;
1233 | _dmx->last_dmx_time = millis();
1234 | _dmx->txChan = 0;
1235 |
1236 | // Set TX Fifo Empty trigger point
1237 | uart_dev_array[_dmx->dmx_nr]->conf1.txfifo_empty_thrhd = 50;
1238 |
1239 | // DMX Start Code 0
1240 | uart_dev_array[_dmx->dmx_nr]->fifo.rw_byte = 0;
1241 |
1242 | } else if (_dmx->state == RDM_START) {
1243 |
1244 | _dmx->state = RDM_TX;
1245 | rdmTimer = micros() + 5000;
1246 | _dmx->txChan = 1; // start code is already in the buffer
1247 |
1248 | // Set TX Fifo empty trigger point & RX Fifo full threshold
1249 | uart_dev_array[_dmx->dmx_nr]->conf1.txfifo_empty_thrhd = 0;
1250 | uart_dev_array[_dmx->dmx_nr]->conf1.rxfifo_full_thrhd = 127;
1251 |
1252 | // RDM Start Code 0xCC
1253 | uart_dev_array[_dmx->dmx_nr]->fifo.rw_byte = 0xCC;
1254 | }
1255 |
1256 | fillTX();
1257 | }
1258 |
1259 | void espDMX::fillTX(void) {
1260 | uint16_t fifoRoom = dmx_get_tx_fifo_room(_dmx) - 3;
1261 |
1262 | uint16_t txSize = _dmx->txSize - _dmx->txChan;
1263 | txSize = (txSize > fifoRoom) ? fifoRoom : txSize;
1264 |
1265 | for (; txSize; --txSize) {
1266 | uart_dev_array[_dmx->dmx_nr]->fifo.rw_byte = _dmx->data1[_dmx->txChan++];
1267 | }
1268 |
1269 | dmx_interrupt_arm(_dmx);
1270 | }
1271 |
--------------------------------------------------------------------------------
/ArtNetNode/espDMX_RDM.h:
--------------------------------------------------------------------------------
1 | /*
2 | espDMX v2 library
3 | Copyright (c) 2016, Matthew Tong
4 | https://github.com/mtongnz/espDMX
5 |
6 | This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public
7 | License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any
8 | later version.
9 |
10 | This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
11 | warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
12 |
13 | You should have received a copy of the GNU General Public License along with this program.
14 | If not, see http://www.gnu.org/licenses/
15 | */
16 | #ifndef espDMX_h
17 | #define espDMX_h
18 |
19 | #define DMX_MAX_BYTES_PER_INT 3 // How many uint8_ts to send per interrupt
20 | #define DMX_TX_CONF 0x3c // SERIAL_8N2
21 | #define DMX_TX_BAUD 250000
22 | #define DMX_FULL_UNI_TIMING 800 // How often to output full 512 channel universe (in milliseconds)
23 | #define DMX_NO_LED 200
24 | #define DMX_MIN_CHANS 30 // Minimum channels output = this + DMX_ADD_CHANS
25 | #define DMX_ADD_CHANS 30 // Add extra buffer to the number of channels output
26 | #define UART_TX_FIFO_SIZE 0x80
27 |
28 | #define RDM_DISCOVERY_INC_TIME 700 // How often to run incremental discovery
29 | #define RDM_DISCOVERY_INCREMENTAL 0
30 | #define RDM_DISCOVERY_FULL 1
31 | #define RDM_DISCOVERY_TOD_WIPE 2
32 |
33 | #include "rdm.h"
34 | #include "rdmDataTypes.h"
35 | #include "rdmFIFO.h"
36 |
37 | typedef void (*rdmCallBackFunc)(rdm_data*);
38 | typedef void (*todCallBackFunc)(void);
39 | typedef void (*inputCallBackFunc)(uint16_t);
40 |
41 | // DMX states
42 | enum dmx_state {
43 | DMX_STOP,
44 | DMX_START,
45 | DMX_TX,
46 | DMX_NOT_INIT,
47 | RDM_START,
48 | RDM_TX,
49 | RDM_RX,
50 | DMX_RX_BREAK,
51 | DMX_RX_DATA,
52 | DMX_RX_IDLE
53 | };
54 |
55 | union uint8_t_uint64 {
56 | uint8_t b[8];
57 | uint64_t u;
58 | };
59 |
60 |
61 | struct dmx_ {
62 | uint8_t dmx_nr;
63 | uint8_t txPin;
64 | uint8_t dirPin;
65 | uint8_t ledIntensity;
66 | uint8_t state = DMX_NOT_INIT;
67 |
68 | uint16_t numChans;
69 | uint16_t txChan;
70 | uint16_t txSize;
71 |
72 | long full_uni_time;
73 | long last_dmx_time;
74 | long led_timer;
75 | bool newDMX = false;
76 | bool started = false;
77 |
78 | uint8_t* data;
79 | uint8_t* data1;
80 | bool ownBuffer = 0;
81 |
82 | bool isInput = false;
83 | inputCallBackFunc inputCallBack = NULL;
84 |
85 | bool rdm_enable = false;
86 | rdmFIFO rdm_queue;
87 | rdm_data rdm_response;
88 | uint16_t rx_pos = 0;
89 | uint16_t rdm_source_man;
90 | uint32_t rdm_source_dev;
91 | uint8_t rdm_trans_no = 0;
92 | bool rdm_discovery = false;
93 | uint32_t rdm_last_discovery = 0;
94 | uint16_t* todManID = NULL;
95 | uint32_t* todDevID = NULL;
96 | uint16_t tod_size = 0;
97 | uint8_t tod_status = RDM_TOD_NOT_READY;
98 | uint16_t rdm_discovery_pos = 0;
99 | bool tod_changed = false;
100 |
101 | rdmCallBackFunc rdmCallBack = NULL;
102 | todCallBackFunc todCallBack = NULL;
103 | };
104 | typedef struct dmx_ dmx_t;
105 |
106 | class espDMX {
107 | public:
108 | espDMX(uint8_t dmx_nr);
109 | ~espDMX();
110 |
111 | void begin(uint8_t dir, uint8_t* buf);
112 | void begin(uint8_t dir) {
113 | begin(dir, NULL);
114 | };
115 | void begin(uint8_t* buf) {
116 | begin(255, buf);
117 | };
118 | void begin(void) {
119 | begin(255, NULL);
120 | };
121 |
122 | void setBuffer(uint8_t*);
123 | void setBuffer(void) {
124 | setBuffer(NULL);
125 | };
126 | void pause();
127 | void unPause();
128 | void end();
129 | void ledIntensity(uint8_t);
130 |
131 | void setChans(uint8_t *data) {
132 | setChans(data, 512, 1);
133 | }
134 | void setChans(uint8_t *data, uint16_t numChans) {
135 | setChans(data, numChans, 1);
136 | }
137 | void setChans(uint8_t*, uint16_t, uint16_t);
138 |
139 |
140 | void chanUpdate(uint16_t);
141 | void clearChans();
142 | uint8_t *getChans();
143 | uint16_t numChans();
144 |
145 | /* from stream class
146 | int available(void) override;
147 | int peek(void) override;
148 | int read(void) override;
149 | void flush(void) override;
150 | size_t write(uint8_t) override;
151 | operator bool() const;
152 | */
153 |
154 | void rdmEnable(uint16_t, uint32_t);
155 | void rdmDisable(void);
156 | void rdmDiscovery(uint8_t);
157 | void rdmDiscovery() {
158 | rdmDiscovery(RDM_DISCOVERY_TOD_WIPE);
159 | };
160 |
161 | void rdmSetCallBack(void (*rdmCallBackFunc)(rdm_data*));
162 | void todSetCallBack(void (*todCallBackFunc)(void));
163 |
164 | bool rdmSendCommand(rdm_data*);
165 | bool rdmSendCommand(uint8_t, uint16_t, uint16_t, uint32_t, uint8_t*, uint16_t, uint16_t);
166 | bool rdmSendCommand(uint8_t cmdClass, uint16_t pid, uint16_t manID, uint32_t devID, uint8_t* data, uint16_t dataLength) {
167 | return rdmSendCommand(cmdClass, pid, manID, devID, data, dataLength, 0);
168 | };
169 | bool rdmSendCommand(uint8_t cmdClass, uint16_t pid, uint16_t manID, uint32_t devID) {
170 | return rdmSendCommand(cmdClass, pid, manID, devID, NULL, 0, 0);
171 | };
172 |
173 | bool rdmEnabled(void);
174 | uint8_t todStatus(void);
175 | uint16_t todCount(void);
176 |
177 | uint16_t* todMan(void);
178 | uint32_t* todDev(void);
179 | uint16_t todMan(uint16_t n);
180 | uint32_t todDev(uint16_t n);
181 |
182 | void handler(void);
183 |
184 | void dmxIn(bool doIn);
185 |
186 | void setInputCallback(void (*inputCallBackFunc)(uint16_t));
187 |
188 | private:
189 | friend void dmx_interrupt_handler(void);
190 | friend void rdm_timer_handler(void);
191 | friend void rdm_interrupt_disarm(dmx_t* dmx);
192 | friend void rdmPause(bool);
193 |
194 | void _transmit(void);
195 | void fillTX(void);
196 |
197 | void inputBreak(void);
198 | void dmxReceived(uint8_t);
199 |
200 | void rdmRXTimeout(void);
201 | void rdmBreakDetect(void);
202 |
203 | void rdmReceived(void);
204 | void rdmMuteResponse(rdm_data*);
205 | void rdmDiscoveryResponse(rdm_data*);
206 |
207 | bool rdmDiscConfirm();
208 |
209 | bool rdmDiscoverBranch(uint16_t, uint32_t, uint16_t, uint32_t, bool);
210 | bool rdmDiscoverBranch(void) {
211 | return rdmDiscoverBranch(0x0000, 0x00000000, 0xFFFF, 0xFFFFFFFF, false);
212 | };
213 |
214 | uint8_t _dmx_nr;
215 | dmx_t* _dmx;
216 | };
217 |
218 |
219 | extern espDMX dmxA;
220 | extern espDMX dmxB;
221 | extern void rdmPause(bool);
222 | #endif
223 |
--------------------------------------------------------------------------------
/ArtNetNode/rdmDataTypes.h:
--------------------------------------------------------------------------------
1 | /*
2 | espArtNetRDM v1 (pre-release) library
3 | Copyright (c) 2016, Matthew Tong
4 | https://github.com/mtongnz/
5 | Modified from https://github.com/forkineye/E131/blob/master/E131.h
6 | This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public
7 | License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any
8 | later version.
9 | This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
10 | warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
11 | You should have received a copy of the GNU General Public License along with this program.
12 | If not, see http://www.gnu.org/licenses/
13 | */
14 | #ifndef rdmDataTypes_h
15 | #define rdmDataTypes_h
16 |
17 | #include
18 | #include
19 |
20 | enum rdm_tod_state {
21 | RDM_TOD_NOT_READY,
22 | RDM_TOD_READY,
23 | RDM_TOD_ERROR
24 | };
25 |
26 | union rdm_data_ {
27 | struct {
28 | uint16_t StartCode; // Start Code 0xCC01 for RDM
29 | uint8_t Length; // packet length
30 | uint16_t DestMan;
31 | uint32_t DestDev;
32 | uint16_t SourceMan;
33 | uint32_t SourceDev;
34 | uint8_t TransNo; // transaction number, not checked
35 | uint8_t ResponseType; // ResponseType
36 | uint8_t MsgCount; // Message count
37 | uint16_t SubDev; // sub device number (root = 0)
38 | uint8_t CmdClass; // command class
39 | uint16_t PID; // parameter ID
40 | uint8_t DataLength; // parameter data length in bytes
41 | uint8_t Data[231]; // data byte field
42 | } __attribute__((packed)) packet;
43 |
44 | struct {
45 | uint8_t headerFE;
46 | uint8_t headerAA;
47 | uint8_t maskedDevID[12];
48 | uint8_t maskedChecksum[4];
49 | } __attribute__((packed)) discovery;
50 |
51 | uint8_t buffer[255];
52 |
53 | void endianFlip(void) {
54 | // 16 bit flips
55 | packet.StartCode = (packet.StartCode << 8) | (packet.StartCode >> 8);
56 | packet.DestMan = (packet.DestMan << 8) | (packet.DestMan >> 8);
57 | packet.SourceMan = (packet.SourceMan << 8) | (packet.SourceMan >> 8);
58 | packet.SubDev = (packet.SubDev << 8) | (packet.SubDev >> 8);
59 | packet.PID = (packet.PID << 8) | (packet.PID >> 8);
60 |
61 | // 32 bit flips
62 | packet.DestDev = __builtin_bswap32 (packet.DestDev);
63 | packet.SourceDev = __builtin_bswap32 (packet.SourceDev);
64 | }
65 |
66 | void clear(void) {
67 | memset(&buffer, 0, 255);
68 | }
69 | };
70 | typedef union rdm_data_ rdm_data;
71 |
72 | #endif
73 |
--------------------------------------------------------------------------------
/ArtNetNode/rdmFIFO.cpp:
--------------------------------------------------------------------------------
1 | /*
2 | This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public
3 | License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any
4 | later version.
5 |
6 | This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
7 | warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
8 |
9 | You should have received a copy of the GNU General Public License along with this program.
10 | If not, see http://www.gnu.org/licenses/
11 | */
12 | #include "rdmFIFO.h"
13 |
14 | #include
15 |
16 | rdmFIFO::rdmFIFO(void) {
17 | init();
18 | }
19 |
20 | void rdmFIFO::init() {
21 | RDMfifoMaxSize = RDM_MAX_RDM_QUEUE;
22 | RDMfifoAllocated = 0;
23 | RDMfifoSize = 0;
24 | }
25 |
26 | bool rdmFIFO::push(rdm_data* a) {
27 | rdm_data* b;
28 | b = resize(RDMfifoSize + 1);
29 |
30 | if (b == NULL)
31 | return false;
32 |
33 | memcpy(b, a, sizeof(rdm_data));
34 |
35 | // Do endian flip if its backwards
36 | if (b->buffer[0] == E120_SC_SUB_MESSAGE && b->buffer[1] == E120_SC_RDM) {
37 | b->endianFlip();
38 | }
39 |
40 |
41 | return true;
42 | }
43 |
44 | rdm_data* rdmFIFO::peek() {
45 | if (isEmpty())
46 | return NULL;
47 |
48 | return content[0];
49 | }
50 |
51 | bool rdmFIFO::pop(rdm_data* a) {
52 | if (isEmpty())
53 | return false;
54 |
55 | // Data stored in buffer big endian - esp8266 is little endian
56 | content[0]->endianFlip();
57 |
58 | memcpy(a, content[0], sizeof(rdm_data));
59 |
60 | resize(RDMfifoSize - 1);
61 |
62 | return true;
63 | }
64 |
65 | bool rdmFIFO::isEmpty() {
66 | return (RDMfifoSize == 0);
67 | }
68 |
69 | bool rdmFIFO::notEmpty() {
70 | return (RDMfifoSize != 0);
71 | }
72 |
73 | bool rdmFIFO::isFull() {
74 | return (RDMfifoSize == RDMfifoMaxSize);
75 | }
76 |
77 | uint8_t rdmFIFO::count() {
78 | return RDMfifoSize;
79 | }
80 |
81 | uint8_t rdmFIFO::space() {
82 | return (RDMfifoMaxSize - RDMfifoSize);
83 | }
84 |
85 | rdm_data* rdmFIFO::resize(uint8_t s) {
86 | if (s > RDMfifoMaxSize)
87 | return NULL;
88 |
89 | if (s < RDMfifoSize) {
90 | //free(content[0]);
91 | rdm_data* a = content[0];
92 |
93 | for (uint8_t x = 1; x < RDMfifoSize; x++)
94 | content[x - 1] = content[x];
95 |
96 | content[RDMfifoSize - 1] = a;
97 | }
98 |
99 |
100 | if (s > RDMfifoAllocated) { // RDMfifoSize) {
101 | if (!(content[s - 1] = (rdm_data*)malloc(sizeof(rdm_data))))
102 | return false;
103 | RDMfifoAllocated = s;
104 | }
105 |
106 | RDMfifoSize = s;
107 |
108 | return (content[s - 1]);
109 | }
110 |
111 | void rdmFIFO::empty() {
112 | RDMfifoSize = 0;
113 | }
114 |
--------------------------------------------------------------------------------
/ArtNetNode/rdmFIFO.h:
--------------------------------------------------------------------------------
1 | /*
2 | This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public
3 | License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any
4 | later version.
5 |
6 | This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
7 | warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
8 |
9 | You should have received a copy of the GNU General Public License along with this program.
10 | If not, see http://www.gnu.org/licenses/
11 | */
12 | #ifndef rdmFIFO_h
13 | #define rdmFIFO_h
14 |
15 | #include "rdm.h"
16 | #include "rdmDataTypes.h"
17 |
18 | #define RDM_MAX_RDM_QUEUE 30
19 |
20 | class rdmFIFO {
21 | public:
22 | rdmFIFO();
23 | void init();
24 | bool push(rdm_data* a);
25 | rdm_data* peek(void);
26 | bool pop(rdm_data* a);
27 | bool isEmpty(void);
28 | bool notEmpty(void);
29 | bool isFull(void);
30 | uint8_t count(void);
31 | uint8_t space(void);
32 | void empty(void);
33 |
34 | private:
35 | rdm_data* resize(uint8_t s);
36 |
37 | rdm_data* content[RDM_MAX_RDM_QUEUE];
38 | uint8_t RDMfifoMaxSize;
39 | uint8_t RDMfifoAllocated;
40 | uint8_t RDMfifoSize;
41 | };
42 |
43 |
44 | #endif
45 |
--------------------------------------------------------------------------------
/ArtNetNode/serialLEDDriver.cpp:
--------------------------------------------------------------------------------
1 | /*
2 | ArtNetNode v3.0.0
3 | Copyright (c) 2018, Tinic Uro
4 | https://github.com/tinic/ESP32_ArtNetNode
5 |
6 | ESP8266_ArtNetNode v2.0.0
7 | Copyright (c) 2016, Matthew Tong
8 | https://github.com/mtongnz/ESP8266_ArtNetNode_v2
9 |
10 | This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public
11 | License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any
12 | later version.
13 |
14 | This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
15 | warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
16 | You should have received a copy of the GNU General Public License along with this program.
17 | If not, see http://www.gnu.org/licenses/
18 | */
19 | #include
20 |
21 | #include "driver/spi_master.h"
22 | #include "esp_timer.h"
23 |
24 | #include "serialLEDDriver.h"
25 |
26 |
27 | // Timings from here:
28 | // https://wp.josh.com/2014/05/13/ws2812-neopixels-are-not-so-finicky-once-you-get-to-know-them/
29 |
30 | serialLEDDriver::serialLEDDriver() {
31 | _datalen[0] = 0;
32 | _datalen[1] = 0;
33 |
34 | _spi_datalen[0] = SPI_LATCH_BITS;
35 | _spi_datalen[1] = SPI_LATCH_BITS;
36 |
37 | _pixellen = 3;
38 |
39 | _spi_speed = 500000 * 8;
40 |
41 | memset(buffer, 0, sizeof(buffer));
42 | memset(_spi_buffer, 0, sizeof(_spi_buffer));
43 |
44 | {
45 | static spi_bus_config_t vspi_buscfg;
46 | memset(&vspi_buscfg, 0, sizeof(vspi_buscfg));
47 | vspi_buscfg.mosi_io_num = VSPI_IOMUX_PIN_NUM_MOSI;
48 | vspi_buscfg.sclk_io_num = VSPI_IOMUX_PIN_NUM_CLK;
49 | vspi_buscfg.miso_io_num = -1;
50 | vspi_buscfg.quadwp_io_num = -1;
51 | vspi_buscfg.quadhd_io_num = -1;
52 | vspi_buscfg.max_transfer_sz = sizeof(_spi_buffer);
53 | spi_bus_initialize(VSPI_HOST, &vspi_buscfg, 1);
54 |
55 | static spi_device_interface_config_t vspi_devcfg;
56 | memset(&vspi_devcfg, 0, sizeof(vspi_devcfg));
57 | vspi_devcfg.clock_speed_hz = _spi_speed;
58 | vspi_devcfg.spics_io_num = -1;
59 | vspi_devcfg.queue_size = 1;
60 | spi_bus_add_device(VSPI_HOST, &vspi_devcfg, &vspi_dev_handle);
61 | }
62 |
63 | {
64 | static spi_bus_config_t hspi_buscfg;
65 | memset(&hspi_buscfg, 0, sizeof(hspi_buscfg));
66 | hspi_buscfg.mosi_io_num = HSPI_IOMUX_PIN_NUM_MOSI;
67 | hspi_buscfg.sclk_io_num = HSPI_IOMUX_PIN_NUM_CLK;
68 | hspi_buscfg.miso_io_num = -1;
69 | hspi_buscfg.quadwp_io_num = -1;
70 | hspi_buscfg.quadhd_io_num = -1;
71 | hspi_buscfg.max_transfer_sz = sizeof(_spi_buffer);
72 | spi_bus_initialize(HSPI_HOST, &hspi_buscfg, 2);
73 |
74 | static spi_device_interface_config_t hspi_devcfg;
75 | memset(&hspi_devcfg, 0, sizeof(hspi_devcfg));
76 | hspi_devcfg.clock_speed_hz = _spi_speed;
77 | hspi_devcfg.spics_io_num = -1;
78 | hspi_devcfg.queue_size = 1;
79 | spi_bus_add_device(HSPI_HOST, &hspi_devcfg, &hspi_dev_handle);
80 | }
81 |
82 | esp_timer_init();
83 |
84 | static esp_timer_create_args_t timer_create_arg;
85 | memset(&timer_create_arg, 0, sizeof(timer_create_arg));
86 | timer_create_arg.callback = timerCallback;
87 | timer_create_arg.arg = this;
88 |
89 | static esp_timer_handle_t timer_handle;
90 | esp_timer_create(&timer_create_arg, &timer_handle);
91 | esp_timer_start_periodic(timer_handle, 1000);
92 | }
93 |
94 | void serialLEDDriver::timerCallback(void *arg) {
95 | ((serialLEDDriver *)arg)->timer();
96 | }
97 |
98 | void serialLEDDriver::timer() {
99 |
100 | static bool vspi_in_flight = false;
101 | spi_transaction_t *vspi_ret_trans = 0;
102 | if (!vspi_in_flight ||
103 | spi_device_get_trans_result(vspi_dev_handle, &vspi_ret_trans, 0) != ESP_ERR_TIMEOUT ) {
104 |
105 | static spi_transaction_t trans;
106 | memset(&trans, 0, sizeof(trans));
107 | trans.length = _spi_datalen[0] * 8;
108 | trans.tx_buffer = (void *)&_spi_buffer[0][0];
109 | spi_device_queue_trans(vspi_dev_handle, &trans, 0);
110 | vspi_in_flight = true;
111 | }
112 |
113 | static bool hspi_in_flight = false;
114 | spi_transaction_t *hspi_ret_trans = 0;
115 | if (!hspi_in_flight ||
116 | spi_device_get_trans_result(hspi_dev_handle, &hspi_ret_trans, 0) != ESP_ERR_TIMEOUT ) {
117 |
118 | static spi_transaction_t trans;
119 | memset(&trans, 0, sizeof(trans));
120 | trans.length = _spi_datalen[1] * 8;
121 | trans.tx_buffer = (void *)&_spi_buffer[1][0];
122 | spi_device_queue_trans(hspi_dev_handle, &trans, 0);
123 | hspi_in_flight = true;
124 | }
125 |
126 | }
127 |
128 | void serialLEDDriver::setConfig(uint16_t config) {
129 | switch (config) {
130 | default:
131 | case WS2812_RGB:
132 | _pixellen = 3;
133 | break;
134 | case WS2812_RGBW:
135 | case WS2812_RGBW_SPLIT:
136 | case APA102_RGBB:
137 | _pixellen = 4;
138 | break;
139 | }
140 | }
141 |
142 | void serialLEDDriver::setStrip(uint8_t port, uint16_t size, uint16_t config) {
143 | setConfig(config);
144 |
145 | _datalen[port] = size * _pixellen;
146 | _config[port] = config;
147 |
148 | clearBuffer(port);
149 |
150 | // Clear the strip
151 | uint8_t* b = buffer[port];
152 | doPixel(b, port, PIX_MAX_BUFFER_SIZE);
153 | }
154 |
155 | void serialLEDDriver::updateStrip(uint8_t port, uint16_t size, uint16_t config) {
156 | setConfig(config);
157 |
158 | size *= _pixellen;
159 |
160 | // Clear the strip if it's shorter than our current strip
161 | if (size < _datalen[port] || _config[port] != config) {
162 | clearBuffer(port, size);
163 |
164 | uint8_t* b = buffer[port];
165 | doPixel(b, port, _datalen[port]);
166 | }
167 |
168 | _datalen[port] = size;
169 | _config[port] = config;
170 | }
171 |
172 | uint8_t* serialLEDDriver::getBuffer(uint8_t port) {
173 | return buffer[port];
174 | }
175 |
176 | void serialLEDDriver::clearBuffer(uint8_t port, uint16_t start) {
177 | memset(&buffer[port][start], 0, PIX_MAX_BUFFER_SIZE - start);
178 | }
179 |
180 | void serialLEDDriver::setBuffer(uint8_t port, uint16_t startChan, uint8_t* data, uint16_t size) {
181 | uint8_t* a = buffer[port];
182 |
183 | if (_config[port] == WS2812_RGBW_SPLIT) {
184 | // Interleave W channel
185 | if (startChan >= 512*3) {
186 | uint8_t *dst = &a[startChan-512*3];
187 | uint8_t *src = &data[0];
188 | for (size_t c=0; c 3) {
220 | a[chan + 3] = w;
221 | }
222 | }
223 |
224 | uint8_t serialLEDDriver::setPixel(uint8_t port, uint16_t pixel, uint32_t colour) {
225 | setPixel(port, pixel, ((colour >> 16) & 0xFF), ((colour >> 8) & 0xFF), (colour & 0xFF), ((colour >> 24) & 0xFF));
226 | }
227 |
228 | uint32_t serialLEDDriver::getPixel(uint8_t port) {
229 | uint8_t* b = buffer[port];
230 | uint16_t chan = _datalen[port] * _pixellen;
231 | // ws2812 is GRB ordering - return RGB
232 | if (_pixellen > 3) {
233 | return ((b[chan + 3] << 24) | (b[chan + 1] << 16) | (b[chan] << 8) | (b[chan + 2]));
234 | } else {
235 | return ((b[chan + 1] << 16) | (b[chan] << 8) | (b[chan + 2]));
236 | }
237 | }
238 |
239 | uint16_t serialLEDDriver::numPixels(uint8_t port) {
240 | return _datalen[port] / _pixellen;
241 | }
242 |
243 | bool serialLEDDriver::show() {
244 | if (_datalen[0] == 0 && _datalen[1] == 0) {
245 | return 1;
246 | }
247 |
248 | if ( _datalen[0] != 0) {
249 | uint8_t* b0 = buffer[0];
250 | doPixel(b0, 0, _datalen[0]);
251 | }
252 |
253 | if (_datalen[1] != 0) {
254 | uint8_t* b1 = buffer[1];
255 | doPixel(b1, 1, _datalen[1]);
256 | }
257 |
258 | return 1;
259 | }
260 |
261 | void serialLEDDriver::doPixel(uint8_t* data, uint8_t port, uint16_t numBytes) {
262 | switch (_config[port])
263 | {
264 | case WS2812_RGB:
265 | case WS2812_RGBW_SPLIT:
266 | case WS2812_RGBW: {
267 | doPixel_ws2812(data, port, numBytes);
268 | } break;
269 | case APA102_RGBB: {
270 | doPixel_apa102(data, port, numBytes);
271 | } break;
272 | }
273 | }
274 |
275 | void serialLEDDriver::doPixel_apa102(uint8_t* data, uint8_t port, uint16_t numBytes) {
276 | }
277 |
278 | void serialLEDDriver::doPixel_ws2812(uint8_t* data, uint8_t port, uint16_t numBytes) {
279 | // Convert to SPI data
280 | uint8_t *dst = &_spi_buffer[port][0];
281 | for (int32_t c = 0; c < _datalen[port]; c++) {
282 | uint8_t p = data[c];
283 | *dst++ =
284 | ((p & (1 << 7)) ? 0b11000000 : 0b10000000)|
285 | ((p & (1 << 6)) ? 0b00001100 : 0b00001000);
286 | *dst++ =
287 | ((p & (1 << 5)) ? 0b11000000 : 0b10000000)|
288 | ((p & (1 << 4)) ? 0b00001100 : 0b00001000);
289 | *dst++ =
290 | ((p & (1 << 3)) ? 0b11000000 : 0b10000000)|
291 | ((p & (1 << 2)) ? 0b00001100 : 0b00001000);
292 | *dst++ =
293 | ((p & (1 << 1)) ? 0b11000000 : 0b10000000)|
294 | ((p & (1 << 0)) ? 0b00001100 : 0b00001000);
295 | }
296 | for (int32_t c = 0; c < SPI_LATCH_BITS; c++) {
297 | *dst++ = 0;
298 | }
299 | _spi_datalen[port] = dst - (&_spi_buffer[port][0]);
300 | }
301 |
--------------------------------------------------------------------------------
/ArtNetNode/serialLEDDriver.h:
--------------------------------------------------------------------------------
1 | /*
2 | ArtNetNode v3.0.0
3 | Copyright (c) 2018, Tinic Uro
4 | https://github.com/tinic/ESP32_ArtNetNode
5 |
6 | ESP8266_ArtNetNode v2.0.0
7 | Copyright (c) 2016, Matthew Tong
8 | https://github.com/mtongnz/ESP8266_ArtNetNode_v2
9 |
10 | This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public
11 | License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any
12 | later version.
13 |
14 | This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
15 | warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
16 | You should have received a copy of the GNU General Public License along with this program.
17 | If not, see http://www.gnu.org/licenses/
18 | */
19 |
20 |
21 | #ifndef serialLEDDriver_h
22 | #define serialLEDDriver_h
23 |
24 | #include "driver/spi_master.h"
25 |
26 | #define LED_PORTS 2
27 | #define PIX_MAX_BUFFER_SIZE 2048
28 | #define SPI_LATCH_BITS 512
29 |
30 | enum conf_type {
31 | // WS2812 use VSPI/HSPI pins only, port 0 and port 1 respectively.
32 | // On a ESP32 Wroom module that would be:
33 | // WS2812DATA Port 0 => VSPID => GPIO23 => Pin 37
34 | // WS2812DATA Port 1 => HSPID => GPIO13 => Pin 16
35 | WS2812_RGB,
36 | WS2812_RGBW,
37 | WS2812_RGBW_SPLIT,
38 | // APA102 use VSPI/HSPI pins only, port 0 and port 1 respectively.
39 | // On a ESP32 Wroom module that would be:
40 | // APA102 Data Port 0 => VSPID => GPIO23 => Pin 37
41 | // APA102 Clock Port 0 => VSPID => GPIO18 => Pin 30
42 | // APA102 Data Port 1 => HSPID => GPIO13 => Pin 16
43 | // APA102 Clock Port 1 => HSPID => GPIO14 => Pin 13
44 | APA102_RGBB,
45 | };
46 |
47 | class SPIClass;
48 |
49 | class serialLEDDriver {
50 | public:
51 |
52 | serialLEDDriver(void);
53 |
54 | void setStrip(uint8_t port, uint16_t size, uint16_t config);
55 | void updateStrip(uint8_t port, uint16_t size, uint16_t config);
56 |
57 | uint8_t* getBuffer(uint8_t port);
58 | void clearBuffer(uint8_t port, uint16_t start);
59 | void clearBuffer(uint8_t port) {
60 | clearBuffer(port, 0);
61 | }
62 | void setBuffer(uint8_t port, uint16_t startChan, uint8_t* data, uint16_t size);
63 |
64 | uint8_t setPixel(uint8_t port, uint16_t pixel, uint8_t r, uint8_t g, uint8_t b, uint8_t w = 0);
65 | uint8_t setPixel(uint8_t port, uint16_t pixel, uint32_t colour);
66 | uint32_t getPixel(uint8_t port);
67 |
68 | bool show();
69 |
70 | uint16_t numPixels(uint8_t port);
71 |
72 | uint8_t buffer[LED_PORTS][PIX_MAX_BUFFER_SIZE];
73 |
74 | void doPixel(uint8_t* data, uint8_t pin, uint16_t numBytes);
75 |
76 | private:
77 | static void timerCallback(void *arg);
78 | void timer();
79 |
80 | void setConfig(uint16_t config);
81 |
82 | void doPixel_apa102(uint8_t* data, uint8_t pin, uint16_t numBytes);
83 | void doPixel_ws2812(uint8_t* data, uint8_t pin, uint16_t numBytes);
84 |
85 | uint8_t _spi_buffer[2][PIX_MAX_BUFFER_SIZE * 8 + SPI_LATCH_BITS];
86 | size_t _spi_datalen[2];
87 |
88 | uint16_t _datalen[LED_PORTS];
89 | uint16_t _config[LED_PORTS];
90 | uint32_t _pixellen;
91 |
92 | uint32_t _spi_speed;
93 |
94 | spi_device_handle_t hspi_dev_handle;
95 | spi_device_handle_t vspi_dev_handle;
96 | };
97 |
98 | #endif
99 |
--------------------------------------------------------------------------------
/ArtNetNode/wsFX.cpp:
--------------------------------------------------------------------------------
1 | /*
2 | ArtNetNode v3.0.0
3 | Copyright (c) 2018, Tinic Uro
4 | https://github.com/tinic/ESP32_ArtNetNode
5 |
6 | ESP8266_ArtNetNode v2.0.0
7 | Copyright (c) 2016, Matthew Tong
8 | https://github.com/mtongnz/ESP8266_ArtNetNode_v2
9 |
10 | This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public
11 | License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any
12 | later version.
13 |
14 | This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
15 | warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
16 | You should have received a copy of the GNU General Public License along with this program.
17 | If not, see http://www.gnu.org/licenses/
18 | */
19 | #include
20 |
21 | #include "wsFX.h"
22 |
23 | #include "serialLEDDriver.h"
24 |
25 | pixPatterns::pixPatterns(uint8_t port, serialLEDDriver* p) {
26 | pixDriver = p;
27 | Port = port;
28 | NewData = 0;
29 | lastUpdate = 0;
30 | Speed = 0;
31 | TotalSteps = 100;
32 | Intensity = 0;
33 | }
34 |
35 | // Update the pattern
36 | bool pixPatterns::Update(void) {
37 | if ((millis() - lastUpdate) > Interval) { // time to update
38 | lastUpdate = millis();
39 |
40 | switch (ActivePattern) {
41 | case RAINBOW_CYCLE:
42 | RainbowCycleUpdate();
43 | break;
44 | case THEATER_CHASE:
45 | TheaterChaseUpdate();
46 | break;
47 | case TWINKLE:
48 | TwinkleUpdate();
49 | break;
50 |
51 | case STATIC:
52 | default:
53 | StaticUpdate();
54 | break;
55 | }
56 | return 1;
57 | }
58 | return 0;
59 | }
60 |
61 | // Increment the Index and reset at the end
62 | void pixPatterns::Increment(void) {
63 | if (Speed < 20 || Speed > 235)
64 | return;
65 |
66 | else if (Speed > 131) {
67 | Index++;
68 |
69 | if (Index >= TotalSteps)
70 | Index = 0;
71 |
72 | } else if (Speed < 123) {
73 | if (Index == 0 || Index > TotalSteps)
74 | Index = TotalSteps - 1;
75 | else
76 | Index--;
77 | }
78 | }
79 |
80 | void pixPatterns::setSpeed(uint8_t s) {
81 | // Index reset mode
82 | if (s < 20) {
83 | if (Speed != s)
84 | Index = 0;
85 | Speed = s;
86 | Interval = 1;
87 |
88 | // Indexed non-reset mode
89 | } else if (s > 235) {
90 | Interval = 1;
91 | Speed = s;
92 |
93 | // Chase mode
94 | } else {
95 | Speed = s;
96 | if (Speed > 127)
97 | Interval = map(Speed, 131, 235, 1, 60);
98 | else
99 | Interval = map(Speed, 20, 123, 60, 1);
100 | }
101 | }
102 |
103 | void pixPatterns::setIntensity(uint8_t i) {
104 | Intensity = i;
105 | setColour1(Colour1Raw);
106 | setColour2(Colour2Raw);
107 | }
108 |
109 | void pixPatterns::setColour1(uint32_t c) {
110 | Colour1Raw = c;
111 | Colour1 = Colour(map(Red(Colour1Raw), 0, 255, 0, Intensity), map(Green(Colour1Raw), 0, 255, 0, Intensity), map(Blue(Colour1Raw), 0, 255, 0, Intensity));
112 | }
113 |
114 | void pixPatterns::setColour2(uint32_t c) {
115 | Colour2Raw = c;
116 | Colour2 = Colour(map(Red(Colour2Raw), 0, 255, 0, Intensity), map(Green(Colour2Raw), 0, 255, 0, Intensity), map(Blue(Colour2Raw), 0, 255, 0, Intensity));
117 | }
118 |
119 | void pixPatterns::setFX(uint8_t fx) {
120 | if (fx < 50)
121 | Static();
122 | else if (fx < 75)
123 | RainbowCycle();
124 | else if (fx < 100)
125 | TheaterChase();
126 | else if (fx < 125)
127 | Twinkle();
128 | }
129 |
130 | // Initialize static looks
131 | void pixPatterns::Static(void) {
132 | if (ActivePattern == STATIC)
133 | return;
134 |
135 | ActivePattern = STATIC;
136 | Index = 0;
137 | }
138 |
139 | // Update the static look
140 | void pixPatterns::StaticUpdate(void) {
141 | TotalSteps = pixDriver->numPixels(Port);
142 |
143 | // Calculate the values to use mapped to the number of pixels we have
144 | uint16_t mSize = map(Size, 0, 255, 2, TotalSteps); // Overall size
145 | uint16_t mSize1 = map(Size1, 0, 255, 0, mSize); // Colour1 size
146 | // uint16_t mFade = map(Fade, 0, 255, 0, (mSize/2)); // Colour fade size
147 |
148 | // Calculate the position offset - the shapes are centered using Pos
149 | uint16_t midPoint = map(Pos, 0, 255, 0, TotalSteps);
150 | uint16_t mPos = mSize - (midPoint - (mSize / 2) - (uint16_t)((midPoint - (mSize / 2)) / mSize) * mSize) + Index;
151 | /*
152 | // Calculate the colour components
153 | uint8_t r1 = Red(Colour1);
154 | uint8_t g1 = Green(Colour1);
155 | uint8_t b1 = Blue(Colour1);
156 | uint8_t r2 = Red(Colour2);
157 | uint8_t g2 = Green(Colour2);
158 | uint8_t b2 = Blue(Colour2);
159 | int16_t r3, g3, b3;
160 |
161 | // Calculate fade values
162 | if (mFade) {
163 | r3 = (r2 - r1) / mFade;
164 | g3 = (g2 - g1) / mFade;
165 | b3 = (b2 - b1) / mFade;
166 | }
167 | */
168 |
169 | for (uint16_t p = 0; p < TotalSteps; p++) {
170 | uint16_t i = (p + mPos) % mSize;
171 | uint32_t c;
172 | /*
173 | // Left faded area
174 | if (mFade && i < mFade) {
175 | uint8_t r = (r3 * i) + r1;
176 | uint8_t g = (g3 * i) + g1;
177 | uint8_t b = (b3 * i) + b1;
178 |
179 | c = Colour(r, g, b);
180 |
181 | // Middle faded area
182 | } else if (mFade && i > (mSize1 - mFade) && i < (mSize1 + mFade)) {
183 | i -= (mSize1 - mFade);
184 | uint8_t r = (r3 * i) + r1;
185 | uint8_t g = (g3 * i) + g1;
186 | uint8_t b = (b3 * i) + b1;
187 |
188 | c = Colour(r, g, b);
189 |
190 | // Middle faded area
191 | } else if (mFade && i < (mSize1 + mFade)) {
192 | i = (mSize1 + mFade) - i;
193 | uint8_t r = (r3 * i) + r2;
194 | uint8_t g = (g3 * i) + g2;
195 | uint8_t b = (b3 * i) + b2;
196 |
197 | c = Colour(r, g, b);
198 |
199 | // Right faded area
200 | } else if (mFade && i > (mSize - mFade)) {
201 | i -= (mSize - mFade);
202 | uint8_t r = (r3 * i) + r2;
203 | uint8_t g = (g3 * i) + g2;
204 | uint8_t b = (b3 * i) + b2;
205 |
206 | c = Colour(r, g, b);
207 |
208 | // Out of faded area
209 | } else
210 | */
211 | if (i < mSize1)
212 | c = Colour1;
213 | else
214 | c = Colour2;
215 |
216 | pixDriver->setPixel(Port, p, c);
217 | }
218 | Increment();
219 | }
220 |
221 | // Initialize for a RainbowCycle
222 | void pixPatterns::RainbowCycle(void) {
223 | if (ActivePattern == RAINBOW_CYCLE)
224 | return;
225 |
226 | ActivePattern = RAINBOW_CYCLE;
227 | Index = 0;
228 | }
229 |
230 | // Update the Rainbow Cycle Pattern
231 | void pixPatterns::RainbowCycleUpdate(void) {
232 | TotalSteps = 255;
233 |
234 | uint16_t mSize = map(Size, 0, 255, 2, pixDriver->numPixels(Port));
235 |
236 | for (uint16_t p = 0; p < pixDriver->numPixels(Port);) {
237 | for (uint16_t i = 0; i < mSize && p < pixDriver->numPixels(Port); i++, p++) {
238 | uint32_t c = Wheel(((i * 256 / mSize) + Index + Pos) & 255);
239 | uint8_t r = map(Red(c), 0, 255, 0, Intensity);
240 | uint8_t g = map(Green(c), 0, 255, 0, Intensity);
241 | uint8_t b = map(Blue(c), 0, 255, 0, Intensity);
242 |
243 | pixDriver->setPixel(Port, p, Colour(r, g, b));
244 | }
245 | }
246 | Increment();
247 | }
248 |
249 | // Initialize for a Theater Chase
250 | void pixPatterns::TheaterChase(void) {
251 | if (ActivePattern == THEATER_CHASE)
252 | return;
253 |
254 | ActivePattern = THEATER_CHASE;
255 | Index = 0;
256 | }
257 |
258 | // Update the Theater Chase Pattern
259 | void pixPatterns::TheaterChaseUpdate(void) {
260 | TotalSteps = pixDriver->numPixels(Port);
261 |
262 | uint8_t mSize = map(Size, 0, 255, 3, 50);
263 | uint8_t a = (Index / map(mSize, 3, 50, 8, 2)) + map(Pos, 0, 255, mSize, 0);
264 |
265 | for (int i = 0; i < pixDriver->numPixels(Port); i++) {
266 | if ((i + a) % mSize == 0)
267 | pixDriver->setPixel(Port, i, Colour1);
268 | else
269 | pixDriver->setPixel(Port, i, Colour2);
270 | }
271 | Increment();
272 | }
273 |
274 | // Initialize for Twinkle
275 | void pixPatterns::Twinkle(void) {
276 | if (ActivePattern == TWINKLE)
277 | return;
278 |
279 | ActivePattern = TWINKLE;
280 | Index = 0;
281 |
282 | randomSeed(analogRead(0));
283 | }
284 |
285 | // Update the Twinkle Pattern
286 | void pixPatterns::TwinkleUpdate(void) {
287 | TotalSteps = 3;
288 |
289 | // Clear strip
290 | if (Index % 3 == 0 || Speed < 20 || Speed > 235) {
291 | for (uint16_t i = 0; i < pixDriver->numPixels(Port); i++)
292 | pixDriver->setPixel(Port, i, Colour1);
293 | }
294 |
295 | // Make twinkles
296 | if (Index % 3 == 0 && Speed > 20 && Speed < 235) {
297 | uint16_t numTwinks = map(Size, 0, 255, 1, (pixDriver->numPixels(Port) / 10));
298 | for (uint8_t n = 0; n < numTwinks; n++)
299 | pixDriver->setPixel(Port, random(0, pixDriver->numPixels(Port)), Colour2);
300 | }
301 |
302 | Increment();
303 | }
304 |
305 | // Calculate 50% dimmed version of a colour (used by ScannerUpdate)
306 | uint32_t pixPatterns::DimColour(uint32_t colour) {
307 | return ((colour & 0xFEFEFE) >> 1);
308 | }
309 |
310 | // Returns 32-bit colour from components
311 | uint32_t pixPatterns::Colour(uint8_t r, uint8_t g, uint8_t b) {
312 | return ((r << 16) | (g << 8) | b);
313 | }
314 |
315 | // Returns the Red component of a 32-bit colour
316 | uint8_t pixPatterns::Red(uint32_t colour) {
317 | return (colour >> 16) & 0xFF;
318 | }
319 |
320 | // Returns the Green component of a 32-bit colour
321 | uint8_t pixPatterns::Green(uint32_t colour) {
322 | return (colour >> 8) & 0xFF;
323 | }
324 |
325 | // Returns the Blue component of a 32-bit colour
326 | uint8_t pixPatterns::Blue(uint32_t colour) {
327 | return colour & 0xFF;
328 | }
329 |
330 | // Input a value 0 to 255 to get a colour value.
331 | // The colours are a transition r - g - b - back to r.
332 | uint32_t pixPatterns::Wheel(uint8_t WheelPos) {
333 | WheelPos = 255 - WheelPos;
334 | if (WheelPos < 85)
335 | return Colour(255 - WheelPos * 3, 0, WheelPos * 3);
336 | else if (WheelPos < 170) {
337 | WheelPos -= 85;
338 | return Colour(0, WheelPos * 3, 255 - WheelPos * 3);
339 | } else {
340 | WheelPos -= 170;
341 | return Colour(WheelPos * 3, 255 - WheelPos * 3, 0);
342 | }
343 | }
344 |
--------------------------------------------------------------------------------
/ArtNetNode/wsFX.h:
--------------------------------------------------------------------------------
1 | /*
2 | ArtNetNode v3.0.0
3 | Copyright (c) 2018, Tinic Uro
4 | https://github.com/tinic/ESP32_ArtNetNode
5 |
6 | ESP8266_ArtNetNode v2.0.0
7 | Copyright (c) 2016, Matthew Tong
8 | https://github.com/mtongnz/ESP8266_ArtNetNode_v2
9 |
10 | This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public
11 | License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any
12 | later version.
13 |
14 | This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
15 | warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
16 | You should have received a copy of the GNU General Public License along with this program.
17 | If not, see http://www.gnu.org/licenses/
18 | */
19 | #ifndef wsFX_h
20 | #define wsFX_h
21 |
22 | enum pattern { STATIC, RAINBOW_CYCLE, THEATER_CHASE, TWINKLE };
23 |
24 | class serialLEDDriver;
25 |
26 | class pixPatterns {
27 | public:
28 | pattern ActivePattern; // which pattern is running
29 |
30 | unsigned long Interval; // milliseconds between updates
31 | unsigned long lastUpdate; // last update of position
32 |
33 | uint32_t Colour1, Colour2; // What colours are in use
34 | uint32_t Colour1Raw, Colour2Raw; // Colours pre-intensity
35 | uint16_t TotalSteps; // total number of steps in the pattern
36 | uint16_t Index; // current step within the pattern
37 | uint8_t Speed; // speed of effect (0 = stop, -ve is reverse, +ve is forwards)
38 | uint8_t Size1, Size, Fade, Pos; // size, fading & position for static looks
39 | uint8_t Intensity;
40 | bool NewData;
41 |
42 | uint8_t Port; // port number.
43 | serialLEDDriver* pixDriver; // the pixel driver
44 |
45 | pixPatterns(uint8_t port, serialLEDDriver* p);
46 | bool Update(void);
47 | void Increment(void);
48 | void setSpeed(uint8_t s);
49 | void setIntensity(uint8_t i);
50 | void setColour1(uint32_t c);
51 | void setColour2(uint32_t c);
52 | void setFX(uint8_t fx);
53 | void Static(void);
54 | void StaticUpdate(void);
55 | void RainbowCycle(void);
56 | void RainbowCycleUpdate(void);
57 | void TheaterChase(void);
58 | void TheaterChaseUpdate(void);
59 | void Twinkle(void);
60 | void TwinkleUpdate(void);
61 | uint32_t DimColour(uint32_t colour);
62 | uint32_t Colour(uint8_t r, uint8_t g, uint8_t b);
63 | uint8_t Red(uint32_t colour);
64 | uint8_t Green(uint32_t colour);
65 | uint8_t Blue(uint32_t colour);
66 | uint32_t Wheel(uint8_t WheelPos);
67 | };
68 |
69 | #endif // #ifndef wsFX_h
70 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | GNU GENERAL PUBLIC LICENSE
2 | Version 3, 29 June 2007
3 |
4 | Copyright (C) 2007 Free Software Foundation, Inc.
5 | Everyone is permitted to copy and distribute verbatim copies
6 | of this license document, but changing it is not allowed.
7 |
8 | Preamble
9 |
10 | The GNU General Public License is a free, copyleft license for
11 | software and other kinds of works.
12 |
13 | The licenses for most software and other practical works are designed
14 | to take away your freedom to share and change the works. By contrast,
15 | the GNU General Public License is intended to guarantee your freedom to
16 | share and change all versions of a program--to make sure it remains free
17 | software for all its users. We, the Free Software Foundation, use the
18 | GNU General Public License for most of our software; it applies also to
19 | any other work released this way by its authors. You can apply it to
20 | your programs, too.
21 |
22 | When we speak of free software, we are referring to freedom, not
23 | price. Our General Public Licenses are designed to make sure that you
24 | have the freedom to distribute copies of free software (and charge for
25 | them if you wish), that you receive source code or can get it if you
26 | want it, that you can change the software or use pieces of it in new
27 | free programs, and that you know you can do these things.
28 |
29 | To protect your rights, we need to prevent others from denying you
30 | these rights or asking you to surrender the rights. Therefore, you have
31 | certain responsibilities if you distribute copies of the software, or if
32 | you modify it: responsibilities to respect the freedom of others.
33 |
34 | For example, if you distribute copies of such a program, whether
35 | gratis or for a fee, you must pass on to the recipients the same
36 | freedoms that you received. You must make sure that they, too, receive
37 | or can get the source code. And you must show them these terms so they
38 | know their rights.
39 |
40 | Developers that use the GNU GPL protect your rights with two steps:
41 | (1) assert copyright on the software, and (2) offer you this License
42 | giving you legal permission to copy, distribute and/or modify it.
43 |
44 | For the developers' and authors' protection, the GPL clearly explains
45 | that there is no warranty for this free software. For both users' and
46 | authors' sake, the GPL requires that modified versions be marked as
47 | changed, so that their problems will not be attributed erroneously to
48 | authors of previous versions.
49 |
50 | Some devices are designed to deny users access to install or run
51 | modified versions of the software inside them, although the manufacturer
52 | can do so. This is fundamentally incompatible with the aim of
53 | protecting users' freedom to change the software. The systematic
54 | pattern of such abuse occurs in the area of products for individuals to
55 | use, which is precisely where it is most unacceptable. Therefore, we
56 | have designed this version of the GPL to prohibit the practice for those
57 | products. If such problems arise substantially in other domains, we
58 | stand ready to extend this provision to those domains in future versions
59 | of the GPL, as needed to protect the freedom of users.
60 |
61 | Finally, every program is threatened constantly by software patents.
62 | States should not allow patents to restrict development and use of
63 | software on general-purpose computers, but in those that do, we wish to
64 | avoid the special danger that patents applied to a free program could
65 | make it effectively proprietary. To prevent this, the GPL assures that
66 | patents cannot be used to render the program non-free.
67 |
68 | The precise terms and conditions for copying, distribution and
69 | modification follow.
70 |
71 | TERMS AND CONDITIONS
72 |
73 | 0. Definitions.
74 |
75 | "This License" refers to version 3 of the GNU General Public License.
76 |
77 | "Copyright" also means copyright-like laws that apply to other kinds of
78 | works, such as semiconductor masks.
79 |
80 | "The Program" refers to any copyrightable work licensed under this
81 | License. Each licensee is addressed as "you". "Licensees" and
82 | "recipients" may be individuals or organizations.
83 |
84 | To "modify" a work means to copy from or adapt all or part of the work
85 | in a fashion requiring copyright permission, other than the making of an
86 | exact copy. The resulting work is called a "modified version" of the
87 | earlier work or a work "based on" the earlier work.
88 |
89 | A "covered work" means either the unmodified Program or a work based
90 | on the Program.
91 |
92 | To "propagate" a work means to do anything with it that, without
93 | permission, would make you directly or secondarily liable for
94 | infringement under applicable copyright law, except executing it on a
95 | computer or modifying a private copy. Propagation includes copying,
96 | distribution (with or without modification), making available to the
97 | public, and in some countries other activities as well.
98 |
99 | To "convey" a work means any kind of propagation that enables other
100 | parties to make or receive copies. Mere interaction with a user through
101 | a computer network, with no transfer of a copy, is not conveying.
102 |
103 | An interactive user interface displays "Appropriate Legal Notices"
104 | to the extent that it includes a convenient and prominently visible
105 | feature that (1) displays an appropriate copyright notice, and (2)
106 | tells the user that there is no warranty for the work (except to the
107 | extent that warranties are provided), that licensees may convey the
108 | work under this License, and how to view a copy of this License. If
109 | the interface presents a list of user commands or options, such as a
110 | menu, a prominent item in the list meets this criterion.
111 |
112 | 1. Source Code.
113 |
114 | The "source code" for a work means the preferred form of the work
115 | for making modifications to it. "Object code" means any non-source
116 | form of a work.
117 |
118 | A "Standard Interface" means an interface that either is an official
119 | standard defined by a recognized standards body, or, in the case of
120 | interfaces specified for a particular programming language, one that
121 | is widely used among developers working in that language.
122 |
123 | The "System Libraries" of an executable work include anything, other
124 | than the work as a whole, that (a) is included in the normal form of
125 | packaging a Major Component, but which is not part of that Major
126 | Component, and (b) serves only to enable use of the work with that
127 | Major Component, or to implement a Standard Interface for which an
128 | implementation is available to the public in source code form. A
129 | "Major Component", in this context, means a major essential component
130 | (kernel, window system, and so on) of the specific operating system
131 | (if any) on which the executable work runs, or a compiler used to
132 | produce the work, or an object code interpreter used to run it.
133 |
134 | The "Corresponding Source" for a work in object code form means all
135 | the source code needed to generate, install, and (for an executable
136 | work) run the object code and to modify the work, including scripts to
137 | control those activities. However, it does not include the work's
138 | System Libraries, or general-purpose tools or generally available free
139 | programs which are used unmodified in performing those activities but
140 | which are not part of the work. For example, Corresponding Source
141 | includes interface definition files associated with source files for
142 | the work, and the source code for shared libraries and dynamically
143 | linked subprograms that the work is specifically designed to require,
144 | such as by intimate data communication or control flow between those
145 | subprograms and other parts of the work.
146 |
147 | The Corresponding Source need not include anything that users
148 | can regenerate automatically from other parts of the Corresponding
149 | Source.
150 |
151 | The Corresponding Source for a work in source code form is that
152 | same work.
153 |
154 | 2. Basic Permissions.
155 |
156 | All rights granted under this License are granted for the term of
157 | copyright on the Program, and are irrevocable provided the stated
158 | conditions are met. This License explicitly affirms your unlimited
159 | permission to run the unmodified Program. The output from running a
160 | covered work is covered by this License only if the output, given its
161 | content, constitutes a covered work. This License acknowledges your
162 | rights of fair use or other equivalent, as provided by copyright law.
163 |
164 | You may make, run and propagate covered works that you do not
165 | convey, without conditions so long as your license otherwise remains
166 | in force. You may convey covered works to others for the sole purpose
167 | of having them make modifications exclusively for you, or provide you
168 | with facilities for running those works, provided that you comply with
169 | the terms of this License in conveying all material for which you do
170 | not control copyright. Those thus making or running the covered works
171 | for you must do so exclusively on your behalf, under your direction
172 | and control, on terms that prohibit them from making any copies of
173 | your copyrighted material outside their relationship with you.
174 |
175 | Conveying under any other circumstances is permitted solely under
176 | the conditions stated below. Sublicensing is not allowed; section 10
177 | makes it unnecessary.
178 |
179 | 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
180 |
181 | No covered work shall be deemed part of an effective technological
182 | measure under any applicable law fulfilling obligations under article
183 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or
184 | similar laws prohibiting or restricting circumvention of such
185 | measures.
186 |
187 | When you convey a covered work, you waive any legal power to forbid
188 | circumvention of technological measures to the extent such circumvention
189 | is effected by exercising rights under this License with respect to
190 | the covered work, and you disclaim any intention to limit operation or
191 | modification of the work as a means of enforcing, against the work's
192 | users, your or third parties' legal rights to forbid circumvention of
193 | technological measures.
194 |
195 | 4. Conveying Verbatim Copies.
196 |
197 | You may convey verbatim copies of the Program's source code as you
198 | receive it, in any medium, provided that you conspicuously and
199 | appropriately publish on each copy an appropriate copyright notice;
200 | keep intact all notices stating that this License and any
201 | non-permissive terms added in accord with section 7 apply to the code;
202 | keep intact all notices of the absence of any warranty; and give all
203 | recipients a copy of this License along with the Program.
204 |
205 | You may charge any price or no price for each copy that you convey,
206 | and you may offer support or warranty protection for a fee.
207 |
208 | 5. Conveying Modified Source Versions.
209 |
210 | You may convey a work based on the Program, or the modifications to
211 | produce it from the Program, in the form of source code under the
212 | terms of section 4, provided that you also meet all of these conditions:
213 |
214 | a) The work must carry prominent notices stating that you modified
215 | it, and giving a relevant date.
216 |
217 | b) The work must carry prominent notices stating that it is
218 | released under this License and any conditions added under section
219 | 7. This requirement modifies the requirement in section 4 to
220 | "keep intact all notices".
221 |
222 | c) You must license the entire work, as a whole, under this
223 | License to anyone who comes into possession of a copy. This
224 | License will therefore apply, along with any applicable section 7
225 | additional terms, to the whole of the work, and all its parts,
226 | regardless of how they are packaged. This License gives no
227 | permission to license the work in any other way, but it does not
228 | invalidate such permission if you have separately received it.
229 |
230 | d) If the work has interactive user interfaces, each must display
231 | Appropriate Legal Notices; however, if the Program has interactive
232 | interfaces that do not display Appropriate Legal Notices, your
233 | work need not make them do so.
234 |
235 | A compilation of a covered work with other separate and independent
236 | works, which are not by their nature extensions of the covered work,
237 | and which are not combined with it such as to form a larger program,
238 | in or on a volume of a storage or distribution medium, is called an
239 | "aggregate" if the compilation and its resulting copyright are not
240 | used to limit the access or legal rights of the compilation's users
241 | beyond what the individual works permit. Inclusion of a covered work
242 | in an aggregate does not cause this License to apply to the other
243 | parts of the aggregate.
244 |
245 | 6. Conveying Non-Source Forms.
246 |
247 | You may convey a covered work in object code form under the terms
248 | of sections 4 and 5, provided that you also convey the
249 | machine-readable Corresponding Source under the terms of this License,
250 | in one of these ways:
251 |
252 | a) Convey the object code in, or embodied in, a physical product
253 | (including a physical distribution medium), accompanied by the
254 | Corresponding Source fixed on a durable physical medium
255 | customarily used for software interchange.
256 |
257 | b) Convey the object code in, or embodied in, a physical product
258 | (including a physical distribution medium), accompanied by a
259 | written offer, valid for at least three years and valid for as
260 | long as you offer spare parts or customer support for that product
261 | model, to give anyone who possesses the object code either (1) a
262 | copy of the Corresponding Source for all the software in the
263 | product that is covered by this License, on a durable physical
264 | medium customarily used for software interchange, for a price no
265 | more than your reasonable cost of physically performing this
266 | conveying of source, or (2) access to copy the
267 | Corresponding Source from a network server at no charge.
268 |
269 | c) Convey individual copies of the object code with a copy of the
270 | written offer to provide the Corresponding Source. This
271 | alternative is allowed only occasionally and noncommercially, and
272 | only if you received the object code with such an offer, in accord
273 | with subsection 6b.
274 |
275 | d) Convey the object code by offering access from a designated
276 | place (gratis or for a charge), and offer equivalent access to the
277 | Corresponding Source in the same way through the same place at no
278 | further charge. You need not require recipients to copy the
279 | Corresponding Source along with the object code. If the place to
280 | copy the object code is a network server, the Corresponding Source
281 | may be on a different server (operated by you or a third party)
282 | that supports equivalent copying facilities, provided you maintain
283 | clear directions next to the object code saying where to find the
284 | Corresponding Source. Regardless of what server hosts the
285 | Corresponding Source, you remain obligated to ensure that it is
286 | available for as long as needed to satisfy these requirements.
287 |
288 | e) Convey the object code using peer-to-peer transmission, provided
289 | you inform other peers where the object code and Corresponding
290 | Source of the work are being offered to the general public at no
291 | charge under subsection 6d.
292 |
293 | A separable portion of the object code, whose source code is excluded
294 | from the Corresponding Source as a System Library, need not be
295 | included in conveying the object code work.
296 |
297 | A "User Product" is either (1) a "consumer product", which means any
298 | tangible personal property which is normally used for personal, family,
299 | or household purposes, or (2) anything designed or sold for incorporation
300 | into a dwelling. In determining whether a product is a consumer product,
301 | doubtful cases shall be resolved in favor of coverage. For a particular
302 | product received by a particular user, "normally used" refers to a
303 | typical or common use of that class of product, regardless of the status
304 | of the particular user or of the way in which the particular user
305 | actually uses, or expects or is expected to use, the product. A product
306 | is a consumer product regardless of whether the product has substantial
307 | commercial, industrial or non-consumer uses, unless such uses represent
308 | the only significant mode of use of the product.
309 |
310 | "Installation Information" for a User Product means any methods,
311 | procedures, authorization keys, or other information required to install
312 | and execute modified versions of a covered work in that User Product from
313 | a modified version of its Corresponding Source. The information must
314 | suffice to ensure that the continued functioning of the modified object
315 | code is in no case prevented or interfered with solely because
316 | modification has been made.
317 |
318 | If you convey an object code work under this section in, or with, or
319 | specifically for use in, a User Product, and the conveying occurs as
320 | part of a transaction in which the right of possession and use of the
321 | User Product is transferred to the recipient in perpetuity or for a
322 | fixed term (regardless of how the transaction is characterized), the
323 | Corresponding Source conveyed under this section must be accompanied
324 | by the Installation Information. But this requirement does not apply
325 | if neither you nor any third party retains the ability to install
326 | modified object code on the User Product (for example, the work has
327 | been installed in ROM).
328 |
329 | The requirement to provide Installation Information does not include a
330 | requirement to continue to provide support service, warranty, or updates
331 | for a work that has been modified or installed by the recipient, or for
332 | the User Product in which it has been modified or installed. Access to a
333 | network may be denied when the modification itself materially and
334 | adversely affects the operation of the network or violates the rules and
335 | protocols for communication across the network.
336 |
337 | Corresponding Source conveyed, and Installation Information provided,
338 | in accord with this section must be in a format that is publicly
339 | documented (and with an implementation available to the public in
340 | source code form), and must require no special password or key for
341 | unpacking, reading or copying.
342 |
343 | 7. Additional Terms.
344 |
345 | "Additional permissions" are terms that supplement the terms of this
346 | License by making exceptions from one or more of its conditions.
347 | Additional permissions that are applicable to the entire Program shall
348 | be treated as though they were included in this License, to the extent
349 | that they are valid under applicable law. If additional permissions
350 | apply only to part of the Program, that part may be used separately
351 | under those permissions, but the entire Program remains governed by
352 | this License without regard to the additional permissions.
353 |
354 | When you convey a copy of a covered work, you may at your option
355 | remove any additional permissions from that copy, or from any part of
356 | it. (Additional permissions may be written to require their own
357 | removal in certain cases when you modify the work.) You may place
358 | additional permissions on material, added by you to a covered work,
359 | for which you have or can give appropriate copyright permission.
360 |
361 | Notwithstanding any other provision of this License, for material you
362 | add to a covered work, you may (if authorized by the copyright holders of
363 | that material) supplement the terms of this License with terms:
364 |
365 | a) Disclaiming warranty or limiting liability differently from the
366 | terms of sections 15 and 16 of this License; or
367 |
368 | b) Requiring preservation of specified reasonable legal notices or
369 | author attributions in that material or in the Appropriate Legal
370 | Notices displayed by works containing it; or
371 |
372 | c) Prohibiting misrepresentation of the origin of that material, or
373 | requiring that modified versions of such material be marked in
374 | reasonable ways as different from the original version; or
375 |
376 | d) Limiting the use for publicity purposes of names of licensors or
377 | authors of the material; or
378 |
379 | e) Declining to grant rights under trademark law for use of some
380 | trade names, trademarks, or service marks; or
381 |
382 | f) Requiring indemnification of licensors and authors of that
383 | material by anyone who conveys the material (or modified versions of
384 | it) with contractual assumptions of liability to the recipient, for
385 | any liability that these contractual assumptions directly impose on
386 | those licensors and authors.
387 |
388 | All other non-permissive additional terms are considered "further
389 | restrictions" within the meaning of section 10. If the Program as you
390 | received it, or any part of it, contains a notice stating that it is
391 | governed by this License along with a term that is a further
392 | restriction, you may remove that term. If a license document contains
393 | a further restriction but permits relicensing or conveying under this
394 | License, you may add to a covered work material governed by the terms
395 | of that license document, provided that the further restriction does
396 | not survive such relicensing or conveying.
397 |
398 | If you add terms to a covered work in accord with this section, you
399 | must place, in the relevant source files, a statement of the
400 | additional terms that apply to those files, or a notice indicating
401 | where to find the applicable terms.
402 |
403 | Additional terms, permissive or non-permissive, may be stated in the
404 | form of a separately written license, or stated as exceptions;
405 | the above requirements apply either way.
406 |
407 | 8. Termination.
408 |
409 | You may not propagate or modify a covered work except as expressly
410 | provided under this License. Any attempt otherwise to propagate or
411 | modify it is void, and will automatically terminate your rights under
412 | this License (including any patent licenses granted under the third
413 | paragraph of section 11).
414 |
415 | However, if you cease all violation of this License, then your
416 | license from a particular copyright holder is reinstated (a)
417 | provisionally, unless and until the copyright holder explicitly and
418 | finally terminates your license, and (b) permanently, if the copyright
419 | holder fails to notify you of the violation by some reasonable means
420 | prior to 60 days after the cessation.
421 |
422 | Moreover, your license from a particular copyright holder is
423 | reinstated permanently if the copyright holder notifies you of the
424 | violation by some reasonable means, this is the first time you have
425 | received notice of violation of this License (for any work) from that
426 | copyright holder, and you cure the violation prior to 30 days after
427 | your receipt of the notice.
428 |
429 | Termination of your rights under this section does not terminate the
430 | licenses of parties who have received copies or rights from you under
431 | this License. If your rights have been terminated and not permanently
432 | reinstated, you do not qualify to receive new licenses for the same
433 | material under section 10.
434 |
435 | 9. Acceptance Not Required for Having Copies.
436 |
437 | You are not required to accept this License in order to receive or
438 | run a copy of the Program. Ancillary propagation of a covered work
439 | occurring solely as a consequence of using peer-to-peer transmission
440 | to receive a copy likewise does not require acceptance. However,
441 | nothing other than this License grants you permission to propagate or
442 | modify any covered work. These actions infringe copyright if you do
443 | not accept this License. Therefore, by modifying or propagating a
444 | covered work, you indicate your acceptance of this License to do so.
445 |
446 | 10. Automatic Licensing of Downstream Recipients.
447 |
448 | Each time you convey a covered work, the recipient automatically
449 | receives a license from the original licensors, to run, modify and
450 | propagate that work, subject to this License. You are not responsible
451 | for enforcing compliance by third parties with this License.
452 |
453 | An "entity transaction" is a transaction transferring control of an
454 | organization, or substantially all assets of one, or subdividing an
455 | organization, or merging organizations. If propagation of a covered
456 | work results from an entity transaction, each party to that
457 | transaction who receives a copy of the work also receives whatever
458 | licenses to the work the party's predecessor in interest had or could
459 | give under the previous paragraph, plus a right to possession of the
460 | Corresponding Source of the work from the predecessor in interest, if
461 | the predecessor has it or can get it with reasonable efforts.
462 |
463 | You may not impose any further restrictions on the exercise of the
464 | rights granted or affirmed under this License. For example, you may
465 | not impose a license fee, royalty, or other charge for exercise of
466 | rights granted under this License, and you may not initiate litigation
467 | (including a cross-claim or counterclaim in a lawsuit) alleging that
468 | any patent claim is infringed by making, using, selling, offering for
469 | sale, or importing the Program or any portion of it.
470 |
471 | 11. Patents.
472 |
473 | A "contributor" is a copyright holder who authorizes use under this
474 | License of the Program or a work on which the Program is based. The
475 | work thus licensed is called the contributor's "contributor version".
476 |
477 | A contributor's "essential patent claims" are all patent claims
478 | owned or controlled by the contributor, whether already acquired or
479 | hereafter acquired, that would be infringed by some manner, permitted
480 | by this License, of making, using, or selling its contributor version,
481 | but do not include claims that would be infringed only as a
482 | consequence of further modification of the contributor version. For
483 | purposes of this definition, "control" includes the right to grant
484 | patent sublicenses in a manner consistent with the requirements of
485 | this License.
486 |
487 | Each contributor grants you a non-exclusive, worldwide, royalty-free
488 | patent license under the contributor's essential patent claims, to
489 | make, use, sell, offer for sale, import and otherwise run, modify and
490 | propagate the contents of its contributor version.
491 |
492 | In the following three paragraphs, a "patent license" is any express
493 | agreement or commitment, however denominated, not to enforce a patent
494 | (such as an express permission to practice a patent or covenant not to
495 | sue for patent infringement). To "grant" such a patent license to a
496 | party means to make such an agreement or commitment not to enforce a
497 | patent against the party.
498 |
499 | If you convey a covered work, knowingly relying on a patent license,
500 | and the Corresponding Source of the work is not available for anyone
501 | to copy, free of charge and under the terms of this License, through a
502 | publicly available network server or other readily accessible means,
503 | then you must either (1) cause the Corresponding Source to be so
504 | available, or (2) arrange to deprive yourself of the benefit of the
505 | patent license for this particular work, or (3) arrange, in a manner
506 | consistent with the requirements of this License, to extend the patent
507 | license to downstream recipients. "Knowingly relying" means you have
508 | actual knowledge that, but for the patent license, your conveying the
509 | covered work in a country, or your recipient's use of the covered work
510 | in a country, would infringe one or more identifiable patents in that
511 | country that you have reason to believe are valid.
512 |
513 | If, pursuant to or in connection with a single transaction or
514 | arrangement, you convey, or propagate by procuring conveyance of, a
515 | covered work, and grant a patent license to some of the parties
516 | receiving the covered work authorizing them to use, propagate, modify
517 | or convey a specific copy of the covered work, then the patent license
518 | you grant is automatically extended to all recipients of the covered
519 | work and works based on it.
520 |
521 | A patent license is "discriminatory" if it does not include within
522 | the scope of its coverage, prohibits the exercise of, or is
523 | conditioned on the non-exercise of one or more of the rights that are
524 | specifically granted under this License. You may not convey a covered
525 | work if you are a party to an arrangement with a third party that is
526 | in the business of distributing software, under which you make payment
527 | to the third party based on the extent of your activity of conveying
528 | the work, and under which the third party grants, to any of the
529 | parties who would receive the covered work from you, a discriminatory
530 | patent license (a) in connection with copies of the covered work
531 | conveyed by you (or copies made from those copies), or (b) primarily
532 | for and in connection with specific products or compilations that
533 | contain the covered work, unless you entered into that arrangement,
534 | or that patent license was granted, prior to 28 March 2007.
535 |
536 | Nothing in this License shall be construed as excluding or limiting
537 | any implied license or other defenses to infringement that may
538 | otherwise be available to you under applicable patent law.
539 |
540 | 12. No Surrender of Others' Freedom.
541 |
542 | If conditions are imposed on you (whether by court order, agreement or
543 | otherwise) that contradict the conditions of this License, they do not
544 | excuse you from the conditions of this License. If you cannot convey a
545 | covered work so as to satisfy simultaneously your obligations under this
546 | License and any other pertinent obligations, then as a consequence you may
547 | not convey it at all. For example, if you agree to terms that obligate you
548 | to collect a royalty for further conveying from those to whom you convey
549 | the Program, the only way you could satisfy both those terms and this
550 | License would be to refrain entirely from conveying the Program.
551 |
552 | 13. Use with the GNU Affero General Public License.
553 |
554 | Notwithstanding any other provision of this License, you have
555 | permission to link or combine any covered work with a work licensed
556 | under version 3 of the GNU Affero General Public License into a single
557 | combined work, and to convey the resulting work. The terms of this
558 | License will continue to apply to the part which is the covered work,
559 | but the special requirements of the GNU Affero General Public License,
560 | section 13, concerning interaction through a network will apply to the
561 | combination as such.
562 |
563 | 14. Revised Versions of this License.
564 |
565 | The Free Software Foundation may publish revised and/or new versions of
566 | the GNU General Public License from time to time. Such new versions will
567 | be similar in spirit to the present version, but may differ in detail to
568 | address new problems or concerns.
569 |
570 | Each version is given a distinguishing version number. If the
571 | Program specifies that a certain numbered version of the GNU General
572 | Public License "or any later version" applies to it, you have the
573 | option of following the terms and conditions either of that numbered
574 | version or of any later version published by the Free Software
575 | Foundation. If the Program does not specify a version number of the
576 | GNU General Public License, you may choose any version ever published
577 | by the Free Software Foundation.
578 |
579 | If the Program specifies that a proxy can decide which future
580 | versions of the GNU General Public License can be used, that proxy's
581 | public statement of acceptance of a version permanently authorizes you
582 | to choose that version for the Program.
583 |
584 | Later license versions may give you additional or different
585 | permissions. However, no additional obligations are imposed on any
586 | author or copyright holder as a result of your choosing to follow a
587 | later version.
588 |
589 | 15. Disclaimer of Warranty.
590 |
591 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
592 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
593 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
594 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
595 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
596 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
597 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
598 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
599 |
600 | 16. Limitation of Liability.
601 |
602 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
603 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
604 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
605 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
606 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
607 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
608 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
609 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
610 | SUCH DAMAGES.
611 |
612 | 17. Interpretation of Sections 15 and 16.
613 |
614 | If the disclaimer of warranty and limitation of liability provided
615 | above cannot be given local legal effect according to their terms,
616 | reviewing courts shall apply local law that most closely approximates
617 | an absolute waiver of all civil liability in connection with the
618 | Program, unless a warranty or assumption of liability accompanies a
619 | copy of the Program in return for a fee.
620 |
621 | END OF TERMS AND CONDITIONS
622 |
623 | How to Apply These Terms to Your New Programs
624 |
625 | If you develop a new program, and you want it to be of the greatest
626 | possible use to the public, the best way to achieve this is to make it
627 | free software which everyone can redistribute and change under these terms.
628 |
629 | To do so, attach the following notices to the program. It is safest
630 | to attach them to the start of each source file to most effectively
631 | state the exclusion of warranty; and each file should have at least
632 | the "copyright" line and a pointer to where the full notice is found.
633 |
634 | {one line to give the program's name and a brief idea of what it does.}
635 | Copyright (C) {year} {name of author}
636 |
637 | This program is free software: you can redistribute it and/or modify
638 | it under the terms of the GNU General Public License as published by
639 | the Free Software Foundation, either version 3 of the License, or
640 | (at your option) any later version.
641 |
642 | This program is distributed in the hope that it will be useful,
643 | but WITHOUT ANY WARRANTY; without even the implied warranty of
644 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
645 | GNU General Public License for more details.
646 |
647 | You should have received a copy of the GNU General Public License
648 | along with this program. If not, see .
649 |
650 | Also add information on how to contact you by electronic and paper mail.
651 |
652 | If the program does terminal interaction, make it output a short
653 | notice like this when it starts in an interactive mode:
654 |
655 | {project} Copyright (C) {year} {fullname}
656 | This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
657 | This is free software, and you are welcome to redistribute it
658 | under certain conditions; type `show c' for details.
659 |
660 | The hypothetical commands `show w' and `show c' should show the appropriate
661 | parts of the General Public License. Of course, your program's commands
662 | might be different; for a GUI interface, you would use an "about box".
663 |
664 | You should also get your employer (if you work as a programmer) or school,
665 | if any, to sign a "copyright disclaimer" for the program, if necessary.
666 | For more information on this, and how to apply and follow the GNU GPL, see
667 | .
668 |
669 | The GNU General Public License does not permit incorporating your program
670 | into proprietary programs. If your program is a subroutine library, you
671 | may consider it more useful to permit linking proprietary applications with
672 | the library. If this is what you want to do, use the GNU Lesser General
673 | Public License instead of this License. But first, please read
674 | .
675 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # ESP32_ArtNetNode_v2
2 | ESP32 based WiFi ArtNet V4 to DMX, RDM and LED Pixels
3 |
4 | This is a fork of https://github.com/mtongnz/ESP8266_ArtNetNode_v2
5 |
6 | To compile get the latest version of the Arduino environment: https://www.arduino.cc/en/Main/Software
7 |
8 | Post install in the Arduino IDE to get a working ESP32 environment:
9 |
10 | 1. Add "https://dl.espressif.com/dl/package_esp32_index.json, http://arduino.esp8266.com/stable/package_esp8266com_index.json" to the "Additional Boards Manager URLs" text field in the main preferences.
11 | 2. Go to Tools -> Board: XXX -> Boards Manager and search for ESP32. Install it. Version 1.0.2 is known to work.
12 | 3. Go to Tools -> Manage Libraries. Search for 'ArduinoJson' (without an underscore) and install version 5.13.5 (later versions do NOT work).
13 | 4. Go to Tools -> Boards: XXX and select "OLIMEX ESP32-PoE" in the ESP32 section.
14 | 5. Open ESP32_ArtNetNode/ArtNetNode/ArtNetNode.ino, compile and upload
15 |
--------------------------------------------------------------------------------
/style.css:
--------------------------------------------------------------------------------
1 | .author,
2 | .title,
3 | ul.nav a {
4 | text-align: center
5 | }
6 |
7 | .author i,
8 | .show,
9 | .title h1,
10 | ul.nav a {
11 | display: block
12 | }
13 |
14 | input,
15 | ul.nav a:hover {
16 | background-color: #DADADA
17 | }
18 |
19 | a,
20 | abbr,
21 | acronym,
22 | address,
23 | applet,
24 | b,
25 | big,
26 | blockquote,
27 | body,
28 | caption,
29 | center,
30 | cite,
31 | code,
32 | dd,
33 | del,
34 | dfn,
35 | div,
36 | dl,
37 | dt,
38 | em,
39 | fieldset,
40 | font,
41 | form,
42 | h1,
43 | h2,
44 | h3,
45 | h4,
46 | h5,
47 | h6,
48 | html,
49 | i,
50 | iframe,
51 | img,
52 | ins,
53 | kbd,
54 | label,
55 | legend,
56 | li,
57 | object,
58 | ol,
59 | p,
60 | pre,
61 | q,
62 | s,
63 | samp,
64 | small,
65 | span,
66 | strike,
67 | strong,
68 | sub,
69 | sup,
70 | table,
71 | tbody,
72 | td,
73 | tfoot,
74 | th,
75 | thead,
76 | tr,
77 | tt,
78 | u,
79 | ul,
80 | var {
81 | margin: 0;
82 | padding: 0;
83 | border: 0;
84 | outline: 0;
85 | font-size: 100%;
86 | vertical-align: baseline;
87 | background: 0 0
88 | }
89 |
90 | .main h2,
91 | li.last {
92 | border-bottom: 1px solid #888583
93 | }
94 |
95 | body {
96 | line-height: 1;
97 | background: #E4E4E4;
98 | color: #292929;
99 | color: rgba(0, 0, 0, .82);
100 | font: 400 100% Cambria, Georgia, serif;
101 | -moz-text-shadow: 0 1px 0 rgba(255, 255, 255, .8);
102 | }
103 |
104 | ol,
105 | ul {
106 | list-style: none
107 | }
108 |
109 | a {
110 | color: #890101;
111 | text-decoration: none;
112 | -moz-transition: .2s color linear;
113 | -webkit-transition: .2s color linear;
114 | transition: .2s color linear
115 | }
116 |
117 | a:hover {
118 | color: #DF3030
119 | }
120 |
121 | #page {
122 | padding: 0
123 | }
124 |
125 | .inner {
126 | margin: 0 auto;
127 | width: 91%
128 | }
129 |
130 | .amp {
131 | font-family: Baskerville, Garamond, Palatino, 'Palatino Linotype', 'Hoefler Text', 'Times New Roman', serif;
132 | font-style: italic;
133 | font-weight: 400
134 | }
135 |
136 | .mast {
137 | float: left;
138 | width: 31.875%
139 | }
140 |
141 | .title {
142 | font: semi 700 16px/1.2 Baskerville, Garamond, Palatino, 'Palatino Linotype', 'Hoefler Text', 'Times New Roman', serif;
143 | padding-top: 0
144 | }
145 |
146 | .title h1 {
147 | font: 700 20px/1.2 'Book Antiqua', 'Palatino Linotype', Georgia, serif;
148 | padding-top: 0
149 | }
150 |
151 | .author {
152 | font: 400 100% Cambria, Georgia, serif
153 | }
154 |
155 | .author i {
156 | font: 400 12px Baskerville, Garamond, Palatino, 'Palatino Linotype', 'Hoefler Text', 'Times New Roman', serif;
157 | letter-spacing: .05em;
158 | padding-top: .7em
159 | }
160 |
161 | .footer,
162 | .main {
163 | float: right;
164 | width: 65.9375%
165 | }
166 |
167 | ul.nav {
168 | margin: 1em auto 0;
169 | width: 11em
170 | }
171 |
172 | ul.nav a {
173 | font: 700 14px/1.2 'Book Antiqua', 'Palatino Linotype', Georgia, serif;
174 | letter-spacing: .1em;
175 | padding: .7em .5em;
176 | margin-bottom: 0;
177 | text-transform: uppercase
178 | }
179 |
180 | input[type=button],
181 | input[type=button]:focus {
182 | background-color: #E4E4E4;
183 | color: #890101
184 | }
185 |
186 | li {
187 | border-top: 1px solid #888583
188 | }
189 |
190 | .hide {
191 | display: none
192 | }
193 |
194 | .main h2 {
195 | font-size: 1.4em;
196 | text-align: left;
197 | margin: 0 0 1em;
198 | padding: 0 0 .3em
199 | }
200 |
201 | .main {
202 | position: relative
203 | }
204 |
205 | p.left {
206 | clear: left;
207 | float: left;
208 | width: 20%;
209 | min-width: 120px;
210 | max-width: 300px;
211 | margin: 0 0 .6em;
212 | padding: 0;
213 | text-align: right
214 | }
215 |
216 | p.right,
217 | select {
218 | min-width: 200px
219 | }
220 |
221 | p.right {
222 | overflow: auto;
223 | margin: 0 0 .6em .4em;
224 | padding-left: .6em;
225 | text-align: left
226 | }
227 |
228 | p.center,
229 | p.spacer {
230 | padding: 0;
231 | display: block
232 | }
233 |
234 | .footer,
235 | p.center {
236 | text-align: center
237 | }
238 |
239 | p.center {
240 | float: left;
241 | clear: both;
242 | margin: 3em 0 3em 15%;
243 | width: 70%
244 | }
245 |
246 | p.spacer {
247 | float: left;
248 | clear: both;
249 | margin: 0;
250 | width: 100%;
251 | height: 20px
252 | }
253 |
254 | input {
255 | margin: 0;
256 | border: 0;
257 | color: #890101;
258 | outline: 0;
259 | font: 400 100% Cambria, Georgia, serif
260 | }
261 |
262 | input[type=text] {
263 | width: 70%;
264 | min-width: 200px;
265 | padding: 0 5px
266 | }
267 |
268 | input[type=number] {
269 | min-width: 50px;
270 | width: 50px
271 | }
272 |
273 | input:focus {
274 | background-color: silver;
275 | color: #000
276 | }
277 |
278 | input[type=checkbox] {
279 | -webkit-appearance: none;
280 | background-color: #fafafa;
281 | border: 1px solid #cacece;
282 | box-shadow: 0 1px 2px rgba(0, 0, 0, .05), inset 0 -15px 10px -12px rgba(0, 0, 0, .05);
283 | padding: 9px;
284 | border-radius: 5px;
285 | display: inline-block;
286 | position: relative
287 | }
288 |
289 | input[type=checkbox]:active,
290 | input[type=checkbox]:checked:active {
291 | box-shadow: 0 1px 2px rgba(0, 0, 0, .05), inset 0 1px 3px rgba(0, 0, 0, .1)
292 | }
293 |
294 | input[type=checkbox]:checked {
295 | background-color: #fafafa;
296 | border: 1px solid #adb8c0;
297 | box-shadow: 0 1px 2px rgba(0, 0, 0, .05), inset 0 -15px 10px -12px rgba(0, 0, 0, .05), inset 15px 10px -12px rgba(255, 255, 255, .1);
298 | color: #99a1a7
299 | }
300 |
301 | input[type=checkbox]:checked:after {
302 | content: '\2714';
303 | font-size: 14px;
304 | position: absolute;
305 | top: 0;
306 | left: 3px;
307 | color: #890101
308 | }
309 |
310 | input[type=button],
311 | input[type=file]+label {
312 | font: 700 16px/1.2 'Book Antiqua', 'Palatino Linotype', Georgia, serif;
313 | margin: 17px 0 0
314 | }
315 |
316 | input[type=button] {
317 | position: absolute;
318 | right: 0;
319 | display: block;
320 | border: 1px solid #adb8c0;
321 | float: right;
322 | border-radius: 12px;
323 | padding: 5px 20px 2px 23px;
324 | -webkit-transition-duration: .3s;
325 | transition-duration: .3s
326 | }
327 |
328 | input[type=button]:hover {
329 | background-color: #909090;
330 | color: #fff;
331 | padding: 5px 62px 2px 65px
332 | }
333 |
334 | input.submit {
335 | float: left;
336 | position: relative
337 | }
338 |
339 | input.showMessage,
340 | input.showMessage:focus,
341 | input.showMessage:hover {
342 | background-color: #6F0;
343 | color: #000;
344 | padding: 5px 62px 2px 65px
345 | }
346 |
347 | input[type=file] {
348 | width: .1px;
349 | height: .1px;
350 | opacity: 0;
351 | overflow: hidden;
352 | position: absolute;
353 | z-index: -1
354 | }
355 |
356 | input[type=file]+label {
357 | float: left;
358 | clear: both;
359 | cursor: pointer;
360 | border: 1px solid #adb8c0;
361 | border-radius: 12px;
362 | padding: 5px 20px 2px 23px;
363 | display: inline-block;
364 | background-color: #E4E4E4;
365 | color: #890101;
366 | overflow: hidden;
367 | -webkit-transition-duration: .3s;
368 | transition-duration: .3s
369 | }
370 |
371 | input[type=file]+label:hover,
372 | input[type=file]:focus+label {
373 | background-color: #909090;
374 | color: #fff;
375 | padding: 5px 40px 2px 43px
376 | }
377 |
378 | input[type=file]+label svg {
379 | width: 1em;
380 | height: 1em;
381 | vertical-align: middle;
382 | fill: currentColor;
383 | margin-top: -.25em;
384 | margin-right: .25em
385 | }
386 |
387 | select {
388 | margin: 0;
389 | border: 0;
390 | background-color: #DADADA;
391 | color: #890101;
392 | outline: 0;
393 | font: 400 100% Cambria, Georgia, serif;
394 | width: 50%;
395 | padding: 0 5px
396 | }
397 |
398 | .footer {
399 | border-top: 1px solid #888583;
400 | display: block;
401 | font-size: 12px;
402 | margin-top: 20px;
403 | padding: .7em 0 20px
404 | }
405 |
406 | .footer p {
407 | margin-bottom: .5em
408 | }
409 |
410 | @media (min-width:600px) {
411 | .inner {
412 | min-width: 600px
413 | }
414 | }
415 |
416 | @media (max-width:600px) {
417 | .inner,
418 | .page {
419 | min-width: 300px;
420 | width: 100%;
421 | overflow-x: hidden
422 | }
423 | .footer,
424 | .main,
425 | .mast {
426 | float: left;
427 | width: 100%
428 | }
429 | .mast {
430 | border-top: 1px solid #888583;
431 | border-bottom: 1px solid #888583
432 | }
433 | .main {
434 | margin-top: 4px;
435 | width: 98%
436 | }
437 | ul.nav {
438 | margin: 0 auto;
439 | width: 100%
440 | }
441 | ul.nav li {
442 | float: left;
443 | min-width: 100px;
444 | width: 33%
445 | }
446 | ul.nav a {
447 | font: 12px Helvetica, Arial, sans-serif;
448 | letter-spacing: 0;
449 | padding: .8em
450 | }
451 | .title,
452 | .title h1 {
453 | padding: 0;
454 | text-align: center
455 | }
456 | ul.nav a:focus,
457 | ul.nav a:hover {
458 | background-position: 0 100%
459 | }
460 | .author {
461 | display: none
462 | }
463 | .title {
464 | border-bottom: 1px solid #888583;
465 | width: 100%;
466 | display: block;
467 | font: 400 15px Baskerville, Garamond, Palatino, 'Palatino Linotype', 'Hoefler Text', 'Times New Roman', serif
468 | }
469 | .title h1 {
470 | font: 600 15px Baskerville, Garamond, Palatino, 'Palatino Linotype', 'Hoefler Text', 'Times New Roman', serif;
471 | display: inline
472 | }
473 | p.left,
474 | p.right {
475 | clear: both;
476 | float: left;
477 | margin-right: 1em
478 | }
479 | li,
480 | li.first,
481 | li.last {
482 | border: 0
483 | }
484 | p.left {
485 | width: 100%;
486 | text-align: left;
487 | margin-left: .4em;
488 | font-weight: 600
489 | }
490 | p.right {
491 | margin-left: 1em;
492 | width: 100%
493 | }
494 | p.center {
495 | margin: 1em 0;
496 | width: 100%
497 | }
498 | p.spacer {
499 | display: none
500 | }
501 | input[type=text],
502 | select {
503 | width: 85%;
504 | }
505 | @media (min-width:1300px) {
506 | .page {
507 | width: 1300px
508 | }
509 | }
--------------------------------------------------------------------------------