├── 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 | } --------------------------------------------------------------------------------