commit 7ec31edf2158833f111155b6d04a036151e174ae Author: dreamer Date: Wed Sep 9 06:50:29 2020 +0200 first diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e307213 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +src/secrets.h +.pio +.vscode diff --git a/data/css/styles.css b/data/css/styles.css new file mode 100644 index 0000000..e362361 --- /dev/null +++ b/data/css/styles.css @@ -0,0 +1 @@ +body { padding-bottom: 70px; } diff --git a/data/favicon.ico b/data/favicon.ico new file mode 100644 index 0000000..0babb7d Binary files /dev/null and b/data/favicon.ico differ diff --git a/data/images/atom196.png b/data/images/atom196.png new file mode 100644 index 0000000..13b2919 Binary files /dev/null and b/data/images/atom196.png differ diff --git a/data/index.htm b/data/index.htm new file mode 100644 index 0000000..44f0d09 --- /dev/null +++ b/data/index.htm @@ -0,0 +1,232 @@ + + + + + + + ESP32 FastLED Web Server by Evil Genius Labs + + + + + + + + + + + + + + + + + + +
+ +
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + diff --git a/data/js/app.js b/data/js/app.js new file mode 100644 index 0000000..5d5eee0 --- /dev/null +++ b/data/js/app.js @@ -0,0 +1,467 @@ +// used when hosting the site on the ESP8266 +var address = location.hostname; +var urlBase = ""; + +// used when hosting the site somewhere other than the ESP8266 (handy for testing without waiting forever to upload to SPIFFS) +// var address = "192.168.86.55"; +// var urlBase = "http://" + address + "/"; + +var postColorTimer = {}; +var postValueTimer = {}; + +var ignoreColorChange = false; + +// var ws = new ReconnectingWebSocket('ws://' + address + ':81/', ['arduino']); +// ws.debug = true; +// +// ws.onmessage = function(evt) { +// if(evt.data != null) +// { +// var data = JSON.parse(evt.data); +// if(data == null) return; +// updateFieldValue(data.name, data.value); +// } +// } + +$(document).ready(function() { + $("#status").html("Connecting, please wait..."); + + $.get(urlBase + "all", function(data) { + $("#status").html("Loading, please wait..."); + + $.each(data, function(index, field) { + if (field.type == "Number") { + addNumberField(field); + } else if (field.type == "Boolean") { + addBooleanField(field); + } else if (field.type == "Select") { + addSelectField(field); + } else if (field.type == "Color") { + addColorFieldPalette(field); + addColorFieldPicker(field); + } else if (field.type == "Section") { + addSectionField(field); + } + }); + + $(".minicolors").minicolors({ + theme: "bootstrap", + changeDelay: 200, + control: "wheel", + format: "rgb", + inline: true + }); + + $("#status").html("Ready"); + }) + .fail(function(errorThrown) { + console.log("error: " + errorThrown); + }); +}); + +function addNumberField(field) { + var template = $("#numberTemplate").clone(); + + template.attr("id", "form-group-" + field.name); + template.attr("data-field-type", field.type); + + var label = template.find(".control-label"); + label.attr("for", "input-" + field.name); + label.text(field.label); + + var input = template.find(".input"); + var slider = template.find(".slider"); + slider.attr("id", "input-" + field.name); + if (field.min) { + input.attr("min", field.min); + slider.attr("min", field.min); + } + if (field.max) { + input.attr("max", field.max); + slider.attr("max", field.max); + } + if (field.step) { + input.attr("step", field.step); + slider.attr("step", field.step); + } + input.val(field.value); + slider.val(field.value); + + slider.on("change mousemove", function() { + input.val($(this).val()); + }); + + slider.on("change", function() { + var value = $(this).val(); + input.val(value); + field.value = value; + delayPostValue(field.name, value); + }); + + input.on("change", function() { + var value = $(this).val(); + slider.val(value); + field.value = value; + delayPostValue(field.name, value); + }); + + $("#form").append(template); +} + +function addBooleanField(field) { + var template = $("#booleanTemplate").clone(); + + template.attr("id", "form-group-" + field.name); + template.attr("data-field-type", field.type); + + var label = template.find(".control-label"); + label.attr("for", "btn-group-" + field.name); + label.text(field.label); + + var btngroup = template.find(".btn-group"); + btngroup.attr("id", "btn-group-" + field.name); + + var btnOn = template.find("#btnOn"); + var btnOff = template.find("#btnOff"); + + btnOn.attr("id", "btnOn" + field.name); + btnOff.attr("id", "btnOff" + field.name); + + btnOn.attr("class", field.value ? "btn btn-primary" : "btn btn-default"); + btnOff.attr("class", !field.value ? "btn btn-primary" : "btn btn-default"); + + btnOn.click(function() { + setBooleanFieldValue(field, btnOn, btnOff, 1) + }); + btnOff.click(function() { + setBooleanFieldValue(field, btnOn, btnOff, 0) + }); + + $("#form").append(template); +} + +function addSelectField(field) { + var template = $("#selectTemplate").clone(); + + template.attr("id", "form-group-" + field.name); + template.attr("data-field-type", field.type); + + var id = "input-" + field.name; + + var label = template.find(".control-label"); + label.attr("for", id); + label.text(field.label); + + var select = template.find(".form-control"); + select.attr("id", id); + + for (var i = 0; i < field.options.length; i++) { + var optionText = field.options[i]; + var option = $(""); + option.text(optionText); + option.attr("value", i); + select.append(option); + } + + select.val(field.value); + + select.change(function() { + var value = template.find("#" + id + " option:selected").index(); + postValue(field.name, value); + }); + + var previousButton = template.find(".btn-previous"); + var nextButton = template.find(".btn-next"); + + previousButton.click(function() { + var value = template.find("#" + id + " option:selected").index(); + var count = select.find("option").length; + value--; + if(value < 0) + value = count - 1; + select.val(value); + postValue(field.name, value); + }); + + nextButton.click(function() { + var value = template.find("#" + id + " option:selected").index(); + var count = select.find("option").length; + value++; + if(value >= count) + value = 0; + select.val(value); + postValue(field.name, value); + }); + + $("#form").append(template); +} + +function addColorFieldPicker(field) { + var template = $("#colorTemplate").clone(); + + template.attr("id", "form-group-" + field.name); + template.attr("data-field-type", field.type); + + var id = "input-" + field.name; + + var input = template.find(".minicolors"); + input.attr("id", id); + + if(!field.value.startsWith("rgb(")) + field.value = "rgb(" + field.value; + + if(!field.value.endsWith(")")) + field.value += ")"; + + input.val(field.value); + + var components = rgbToComponents(field.value); + + var redInput = template.find(".color-red-input"); + var greenInput = template.find(".color-green-input"); + var blueInput = template.find(".color-blue-input"); + + var redSlider = template.find(".color-red-slider"); + var greenSlider = template.find(".color-green-slider"); + var blueSlider = template.find(".color-blue-slider"); + + redInput.attr("id", id + "-red"); + greenInput.attr("id", id + "-green"); + blueInput.attr("id", id + "-blue"); + + redSlider.attr("id", id + "-red-slider"); + greenSlider.attr("id", id + "-green-slider"); + blueSlider.attr("id", id + "-blue-slider"); + + redInput.val(components.r); + greenInput.val(components.g); + blueInput.val(components.b); + + redSlider.val(components.r); + greenSlider.val(components.g); + blueSlider.val(components.b); + + redInput.on("change", function() { + var value = $("#" + id).val(); + var r = $(this).val(); + var components = rgbToComponents(value); + field.value = r + "," + components.g + "," + components.b; + $("#" + id).minicolors("value", "rgb(" + field.value + ")"); + redSlider.val(r); + }); + + greenInput.on("change", function() { + var value = $("#" + id).val(); + var g = $(this).val(); + var components = rgbToComponents(value); + field.value = components.r + "," + g + "," + components.b; + $("#" + id).minicolors("value", "rgb(" + field.value + ")"); + greenSlider.val(g); + }); + + blueInput.on("change", function() { + var value = $("#" + id).val(); + var b = $(this).val(); + var components = rgbToComponents(value); + field.value = components.r + "," + components.g + "," + b; + $("#" + id).minicolors("value", "rgb(" + field.value + ")"); + blueSlider.val(b); + }); + + redSlider.on("change", function() { + var value = $("#" + id).val(); + var r = $(this).val(); + var components = rgbToComponents(value); + field.value = r + "," + components.g + "," + components.b; + $("#" + id).minicolors("value", "rgb(" + field.value + ")"); + redInput.val(r); + }); + + greenSlider.on("change", function() { + var value = $("#" + id).val(); + var g = $(this).val(); + var components = rgbToComponents(value); + field.value = components.r + "," + g + "," + components.b; + $("#" + id).minicolors("value", "rgb(" + field.value + ")"); + greenInput.val(g); + }); + + blueSlider.on("change", function() { + var value = $("#" + id).val(); + var b = $(this).val(); + var components = rgbToComponents(value); + field.value = components.r + "," + components.g + "," + b; + $("#" + id).minicolors("value", "rgb(" + field.value + ")"); + blueInput.val(b); + }); + + redSlider.on("change mousemove", function() { + redInput.val($(this).val()); + }); + + greenSlider.on("change mousemove", function() { + greenInput.val($(this).val()); + }); + + blueSlider.on("change mousemove", function() { + blueInput.val($(this).val()); + }); + + input.on("change", function() { + if (ignoreColorChange) return; + + var value = $(this).val(); + var components = rgbToComponents(value); + + redInput.val(components.r); + greenInput.val(components.g); + blueInput.val(components.b); + + redSlider.val(components.r); + greenSlider.val(components.g); + blueSlider.val(components.b); + + field.value = components.r + "," + components.g + "," + components.b; + delayPostColor(field.name, components); + }); + + $("#form").append(template); +} + +function addColorFieldPalette(field) { + var template = $("#colorPaletteTemplate").clone(); + + var buttons = template.find(".btn-color"); + + var label = template.find(".control-label"); + label.text(field.label); + + buttons.each(function(index, button) { + $(button).click(function() { + var rgb = $(this).css('backgroundColor'); + var components = rgbToComponents(rgb); + + field.value = components.r + "," + components.g + "," + components.b; + postColor(field.name, components); + + ignoreColorChange = true; + var id = "#input-" + field.name; + $(id).minicolors("value", "rgb(" + field.value + ")"); + $(id + "-red").val(components.r); + $(id + "-green").val(components.g); + $(id + "-blue").val(components.b); + $(id + "-red-slider").val(components.r); + $(id + "-green-slider").val(components.g); + $(id + "-blue-slider").val(components.b); + ignoreColorChange = false; + }); + }); + + $("#form").append(template); +} + +function addSectionField(field) { + var template = $("#sectionTemplate").clone(); + + template.attr("id", "form-group-section-" + field.name); + template.attr("data-field-type", field.type); + + $("#form").append(template); +} + +function updateFieldValue(name, value) { + var group = $("#form-group-" + name); + + var type = group.attr("data-field-type"); + + if (type == "Number") { + var input = group.find(".form-control"); + input.val(value); + } else if (type == "Boolean") { + var btnOn = group.find("#btnOn" + name); + var btnOff = group.find("#btnOff" + name); + + btnOn.attr("class", value ? "btn btn-primary" : "btn btn-default"); + btnOff.attr("class", !value ? "btn btn-primary" : "btn btn-default"); + + } else if (type == "Select") { + var select = group.find(".form-control"); + select.val(value); + } else if (type == "Color") { + var input = group.find(".form-control"); + input.val("rgb(" + value + ")"); + } +}; + +function setBooleanFieldValue(field, btnOn, btnOff, value) { + field.value = value; + + btnOn.attr("class", field.value ? "btn btn-primary" : "btn btn-default"); + btnOff.attr("class", !field.value ? "btn btn-primary" : "btn btn-default"); + + postValue(field.name, field.value); +} + +function postValue(name, value) { + $("#status").html("Setting " + name + ": " + value + ", please wait..."); + + var body = { name: name, value: value }; + + $.post(urlBase + "fieldValue?name=" + name + "&value=" + value, body, function(data) { + if (data.name != null) { + $("#status").html("Set " + name + ": " + data.name); + } else { + $("#status").html("Set " + name + ": " + data); + } + }); +} + +function delayPostValue(name, value) { + clearTimeout(postValueTimer); + postValueTimer = setTimeout(function() { + postValue(name, value); + }, 300); +} + +function postColor(name, value) { + $("#status").html("Setting " + name + ": " + value.r + "," + value.g + "," + value.b + ", please wait..."); + + var body = { name: name, r: value.r, g: value.g, b: value.b }; + + $.post(urlBase + "fieldValue?name=" + name + "&value=" + "&r=" + value.r + "&g=" + value.g + "&b=" + value.b, body, function(data) { + $("#status").html("Set " + name + ": " + data); + }) + .fail( + function(jqXHR, textStatus, errorThrown) { + console.error(textStatus); + console.error(errorThrown); + $("#status").html("Fail: " + textStatus + " " + errorThrown); + }); +} + +function delayPostColor(name, value) { + clearTimeout(postColorTimer); + postColorTimer = setTimeout(function() { + postColor(name, value); + }, 300); +} + +function componentToHex(c) { + var hex = c.toString(16); + return hex.length == 1 ? "0" + hex : hex; +} + +function rgbToHex(r, g, b) { + return "#" + componentToHex(r) + componentToHex(g) + componentToHex(b); +} + +function rgbToComponents(rgb) { + var components = {}; + + rgb = rgb.match(/^rgb\((\d+),\s*(\d+),\s*(\d+)\)$/); + components.r = parseInt(rgb[1]); + components.g = parseInt(rgb[2]); + components.b = parseInt(rgb[3]); + + return components; +} diff --git a/platformio.ini b/platformio.ini new file mode 100644 index 0000000..5b9c096 --- /dev/null +++ b/platformio.ini @@ -0,0 +1,27 @@ +; PlatformIO Project Configuration File +; +; Build options: build flags, source filter +; Upload options: custom upload port, speed and extra flags +; Library options: dependencies, extra library storages +; Advanced options: extra scripting +; +; Please visit documentation for the other options and examples +; https://docs.platformio.org/page/projectconf.html + +[env:default] +platform = espressif32 +board = esp32cam +framework = arduino +monitor_speed = 115200 +upload_speed = 230400 +upload_port = /dev/ttyUSB0 +board_build.flash_mode = qio +build_flags = + -DCORE_DEBUG_LEVEL=0 + -DBOARD_HAS_PSRAM + -mfix-esp32-psram-cache-issue + +lib_deps = + https://github.com/geeksville/Micro-RTSP.git + FastLED + diff --git a/src/LICENSE b/src/LICENSE new file mode 100644 index 0000000..94a9ed0 --- /dev/null +++ b/src/LICENSE @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/src/README.md b/src/README.md new file mode 100644 index 0000000..63152f6 --- /dev/null +++ b/src/README.md @@ -0,0 +1,71 @@ +# ESP32 FastLED Web Server +**Work in progress ESP32 port of https://github.com/jasoncoon/esp8266-fastled-webserver** + +Control addressable LEDs with an ESP32 via a web browser over Wi-Fi. + +## Features +### Currently Working: +* [x] DemoReel100 patterns +* [x] Static web app file serving from SPIFFS +* [x] Ability to adjust these settings via the HTTP REST API: + * [x] power on/off + * [x] brightness + * [x] pattern + * [x] autoplay on/off + * [x] autoplay duration + * [x] speed + * [x] palette + * [x] auto palette cycling + * [x] palette duration + * [x] solid color + * [x] twinkle speed/density + * [x] fire cooling/sparking +* [x] Setting storage in EEPROM + +### Currently Lacking: +* [ ] WebSockets for automatically refreshing/syncing web clients + +## Requirements + +### Hardware + +#### ESP32 Development Board + +[![Adafruit HUZZAH32 – ESP32 Feather Board](https://cdn-learn.adafruit.com/assets/assets/000/041/619/thumb100/feather_3405_iso_ORIG.jpg?1494445509)](https://www.adafruit.com/product/3405) + +[Adafruit HUZZAH32 – ESP32 Feather Board](https://www.adafruit.com/product/3405) + +**or** + +[WEMOS LOLIN D32](https://wiki.wemos.cc/products:d32:d32) + +**or** + +[An ESP32 development board of your choice](https://www.google.com/search?q=esp32+development+board) + +#### Addressable LED strip + +[![Adafruit NeoPixel Ring](https://www.adafruit.com/images/145x109/1586-00.jpg)](https://www.adafruit.com/product/1586) + +[Adafruit NeoPixel Ring](https://www.adafruit.com/product/1586) + +#### Other hardware: + +* [3.3V to 5V Logic Level Shifter](http://www.digikey.com/product-detail/en/texas-instruments/SN74HCT245N/296-1612-5-ND/277258) (required if LEDs "glitch") +* [Octo Level Shifter FeatherWing](https://www.evilgeniuslabs.org/level-shifter-featherwing) (tidy level shifter PCB) + +Recommended by [Adafruit NeoPixel "Best Practices"](https://learn.adafruit.com/adafruit-neopixel-uberguide/best-practices) to help protect LEDs from current onrush: +* [1000µF Capacitor](http://www.digikey.com/product-detail/en/panasonic-electronic-components/ECA-1EM102/P5156-ND/245015) +* [300 to 500 Ohm resistor](https://www.digikey.com/product-detail/en/stackpole-electronics-inc/CF14JT470R/CF14JT470RCT-ND/1830342) + +### Software + +* [Arduino](https://www.arduino.cc/en/main/software) +* [ESP32 Arduino Libraries & Tools](https://github.com/espressif/arduino-esp32) +* [Arduino ESP32 filesystem uploader](https://github.com/me-no-dev/arduino-esp32fs-plugin) + +#### Libraries + +* [FastLED](https://github.com/FastLED/FastLED) +* [ESP32 Arduino Libraries & Tools](https://github.com/espressif/arduino-esp32) +* [ESP8266/ESP32 WebServer](https://github.com/bbx10/WebServer_tng) diff --git a/src/field.h b/src/field.h new file mode 100644 index 0000000..ef89a1b --- /dev/null +++ b/src/field.h @@ -0,0 +1,209 @@ +/* + ESP32 FastLED WebServer: https://github.com/jasoncoon/esp32-fastled-webserver + Copyright (C) 2017 Jason Coon + + Built upon the amazing FastLED work of Daniel Garcia and Mark Kriegsman: + https://github.com/FastLED/FastLED + + ESP32 support provided by the hard work of Sam Guyer: + https://github.com/samguyer/FastLED + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +typedef String (*FieldSetter)(String); +typedef String (*FieldGetter)(); + +const String NumberFieldType = "Number"; +const String BooleanFieldType = "Boolean"; +const String SelectFieldType = "Select"; +const String ColorFieldType = "Color"; +const String SectionFieldType = "Section"; + +typedef struct { + public: + String name; + String label; + String type; + uint8_t min; + uint8_t max; + FieldGetter getValue; + FieldGetter getOptions; + FieldSetter setValue; +} Field; + +typedef Field FieldList[]; + +Field getField(String name, FieldList fields, uint8_t count) { + for (uint8_t i = 0; i < count; i++) { + Field field = fields[i]; + if (field.name == name) { + return field; + } + } + return Field(); +} + +String getFieldValue(String name, FieldList fields, uint8_t count) { + Field field = getField(name, fields, count); + if (field.getValue) { + return field.getValue(); + } + return String(); +} + +CRGB parseColor(String value) { + uint8_t ri = value.indexOf(","); + uint8_t gi = value.indexOf(",", ri + 1); + + String rs = value.substring(0, ri); + String gs = value.substring(ri + 1, gi); + String bs = value.substring(gi + 1); + + uint8_t r = rs.toInt(); + uint8_t g = gs.toInt(); + uint8_t b = bs.toInt(); + + return CRGB(r, g, b); +} + +void writeFieldsToEEPROM(FieldList fields, uint8_t count) { + uint8_t index = 0; + + EEPROM.write(index, 0); + + for (uint8_t i = 0; i < count; i++) { + Field field = fields[i]; + if (!field.getValue && !field.setValue) + continue; + + String value = field.getValue(); + + if (field.type == ColorFieldType) { + CRGB color = parseColor(value); + EEPROM.write(index++, color.r); + EEPROM.write(index++, color.g); + EEPROM.write(index++, color.b); + } else { + byte v = value.toInt(); + EEPROM.write(index++, v); + } + } + + EEPROM.commit(); +} + +String setFieldValue(String name, String value, FieldList fields, uint8_t count) { + String result; + + Field field = getField(name, fields, count); + if (field.setValue) { + if (field.type == ColorFieldType) { + String r = webServer.arg("r"); + String g = webServer.arg("g"); + String b = webServer.arg("b"); + String combinedValue = r + "," + g + "," + b; + result = field.setValue(combinedValue); + } else { + result = field.setValue(value); + } + } + + writeFieldsToEEPROM(fields, count); + + return result; +} + +void loadFieldsFromEEPROM(FieldList fields, uint8_t count) { + uint8_t byteCount = 1; + + for (uint8_t i = 0; i < count; i++) { + Field field = fields[i]; + if (!field.setValue) + continue; + + if (field.type == ColorFieldType) { + byteCount += 3; + } else { + byteCount++; + } + } + + if (!EEPROM.begin(count)) { + Serial.println("Failed to initialize EEPROM!"); + return; + } + + if (EEPROM.read(0) == 255) { + Serial.println("First run, or EEPROM erased, skipping settings load!"); + return; + } + + uint8_t index = 0; + + for (uint8_t i = 0; i < count; i++) { + Field field = fields[i]; + if (!field.setValue) + continue; + + if (field.type == ColorFieldType) { + String r = String(EEPROM.read(index++)); + String g = String(EEPROM.read(index++)); + String b = String(EEPROM.read(index++)); + field.setValue(r + "," + g + "," + b); + } else { + byte v = EEPROM.read(index++); + field.setValue(String(v)); + } + } +} + +String getFieldsJson(FieldList fields, uint8_t count) { + String json = "["; + + for (uint8_t i = 0; i < count; i++) { + Field field = fields[i]; + + json += "{\"name\":\"" + field.name + "\",\"label\":\"" + field.label + "\",\"type\":\"" + field.type + "\""; + + if (field.getValue) { + if (field.type == ColorFieldType || field.type == "String") { + json += ",\"value\":\"" + field.getValue() + "\""; + } + else { + json += ",\"value\":" + field.getValue(); + } + } + + if (field.type == NumberFieldType) { + json += ",\"min\":" + String(field.min); + json += ",\"max\":" + String(field.max); + } + + if (field.getOptions) { + json += ",\"options\":["; + json += field.getOptions(); + json += "]"; + } + + json += "}"; + + if (i < count - 1) + json += ","; + } + + json += "]"; + + return json; +} diff --git a/src/fields.h b/src/fields.h new file mode 100644 index 0000000..2b118c4 --- /dev/null +++ b/src/fields.h @@ -0,0 +1,272 @@ +/* + ESP32 FastLED WebServer: https://github.com/jasoncoon/esp32-fastled-webserver + Copyright (C) 2017 Jason Coon + + Built upon the amazing FastLED work of Daniel Garcia and Mark Kriegsman: + https://github.com/FastLED/FastLED + + ESP32 support provided by the hard work of Sam Guyer: + https://github.com/samguyer/FastLED + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +String getPower() { + return String(power); +} + +String setPower(String value) { + power = value.toInt(); + power = power == 0 ? 0 : 1; + return String(power); +} + +String getBrightness() { + return String(brightness); +} + +String setBrightness(String value) { + brightness = value.toInt(); + FastLED.setBrightness(brightness); + return String(brightness); +} + +String getPattern() { + return String(currentPatternIndex); +} + +void setPattern(uint8_t value) +{ + if (value >= patternCount) + value = patternCount - 1; + + currentPatternIndex = value; +} + +String setPattern(String value) { + setPattern(value.toInt()); + return String(currentPatternIndex); +} + +String getPatterns() { + String json = ""; + + for (uint8_t i = 0; i < patternCount; i++) { + json += "\"" + patterns[i].name + "\""; + if (i < patternCount - 1) + json += ","; + } + + return json; +} + +String getPalette() { + return String(currentPaletteIndex); +} + +String setPalette(String value) { + // value.toInt() returns long, while currentPaletteIndex is declared as uint8_t + long tmp = value.toInt(); + // clamp to [0.. paletteCount-1] + if (tmp < 1) { + tmp = 1; + } else if (tmp > (paletteCount-1)) { + tmp = paletteCount-1; + } + currentPaletteIndex = tmp; + targetPalette = palettes[currentPaletteIndex]; + return String(currentPaletteIndex); +} + +String getPalettes() { + String json = ""; + + for (uint8_t i = 0; i < paletteCount; i++) { + json += "\"" + paletteNames[i] + "\""; + if (i < paletteCount - 1) + json += ","; + } + + return json; +} + +String getSpeed() { + return String(speed); +} + +String setSpeed(String value) { + speed = value.toInt(); + return String(speed); +} + +String getAutoplay() { + return String(autoplay); +} + +String setAutoplay(String value) { + autoplay = value.toInt(); + autoplay = autoplay == 0 ? 0 : 1; + autoPlayTimeout = millis() + (autoplayDuration * 1000); + return String(autoplay); +} + +String getAutoplayDuration() { + return String(autoplayDuration); +} + +String setAutoplayDuration(String value) { + // value.toInt() returns long, while autoplayDuration is declared as uint8_t + long tmp = value.toInt(); + // clamp to [1..255] + if (tmp < 1) { + tmp = 1; + } else if (tmp > 255) { + tmp = 255; + } + autoplayDuration = tmp; + autoPlayTimeout = millis() + (tmp * 1000); + return String(autoplayDuration); +} + +String getCyclePalettes() { + return String(cyclePalettes); +} + +String setCyclePalettes(String value) { + cyclePalettes = value.toInt(); + cyclePalettes = cyclePalettes == 0 ? 0 : 1; + paletteTimeout = millis() + (paletteDuration * 1000); + return String(cyclePalettes); +} + +String getPaletteDuration() { + return String(paletteDuration); +} + +String setPaletteDuration(String value) { + // value.toInt() returns long, while paletteDuration is declared as uint8_t + long tmp = value.toInt(); + // clamp to [1..255] + if (tmp < 1) { + tmp = 1; + } else if (tmp > 255) { + tmp = 255; + } + paletteDuration = tmp; + paletteTimeout = millis() + (tmp * 1000); + return String(paletteDuration); +} + +String getSolidColor() { + return String(solidColor.r) + "," + String(solidColor.g) + "," + String(solidColor.b); +} + +String setSolidColor(uint8_t r, uint8_t g, uint8_t b) +{ + solidColor = CRGB(r, g, b); + + return "\"" + String(solidColor.r) + "," + String(solidColor.g) + "," + String(solidColor.b) + "\""; +} + +String setSolidColor(CRGB color) { + return setSolidColor(color.r, color.g, color.b); +} + +String setSolidColor(String value) { + CRGB color = parseColor(value); + + return setSolidColor(color); +} + +String getCooling() { + return String(cooling); +} + +String setCooling(String value) { + cooling = value.toInt(); + return String(cooling); +} + +String getSparking() { + return String(sparking); +} + +String setSparking(String value) { + sparking = value.toInt(); + return String(sparking); +} + +String getTwinkleSpeed() { + return String(twinkleSpeed); +} + +String setTwinkleSpeed(String value) { + // value.toInt() returns long, while twinkleSpeed is declared as uint8_t + long tmp = value.toInt(); + // clamp to [0..8] + if (tmp < 0) { + tmp = 0; + } else if (tmp > 8) { + tmp = 8; + } + twinkleSpeed = tmp; + return String(twinkleSpeed); +} + +String getTwinkleDensity() { + return String(twinkleDensity); +} + +String setTwinkleDensity(String value) { + // value.toInt() returns long, while twinkleDensity is declared as uint8_t + long tmp = value.toInt(); + // clamp to [0..8] + if (tmp < 0) { + tmp = 0; + } else if (tmp > 8) { + tmp = 8; + } + twinkleDensity = tmp; + return String(twinkleDensity); +} + +FieldList fields = { + // name label type min, max, getValue, getOptions, setValue + { "power", "Power", BooleanFieldType, 0, 1, getPower, NULL, setPower }, + { "brightness", "Brightness", NumberFieldType, 1, 255, getBrightness, NULL, setBrightness }, + { "speed", "Speed", NumberFieldType, 1, 255, getSpeed, NULL, setSpeed }, + + { "patternSection", "Pattern", SectionFieldType, 0, 0, NULL, NULL, NULL }, + { "pattern", "Pattern", SelectFieldType, 0, patternCount, getPattern, getPatterns, setPattern }, + { "autoplay", "Cycle Patterns", BooleanFieldType, 0, 1, getAutoplay, NULL, setAutoplay }, + { "autoplayDuration", "Pattern Duration", NumberFieldType, 1, 255, getAutoplayDuration, NULL, setAutoplayDuration }, + + { "paletteSection", "Palette", SectionFieldType, 0, 0, NULL, NULL, NULL }, + { "palette", "Palette", SelectFieldType, 0, paletteCount, getPalette, getPalettes, setPalette }, + { "cyclePalettes", "Cycle Palettes", BooleanFieldType, 0, 1, getCyclePalettes, NULL, setCyclePalettes }, + { "paletteDuration", "Palette Duration", NumberFieldType, 1, 255, getPaletteDuration, NULL, setPaletteDuration }, + + { "solidColorSection", "Solid Color", SectionFieldType, 0, 0, NULL, NULL, NULL }, + { "solidColor", "Color", ColorFieldType, 0, 255, getSolidColor, NULL, setSolidColor }, + + { "fire", "Fire & Water", SectionFieldType, 0, 0, NULL, NULL, NULL }, + { "cooling", "Cooling", NumberFieldType, 0, 255, getCooling, NULL, setCooling }, + { "sparking", "Sparking", NumberFieldType, 0, 255, getSparking, NULL, setSparking }, + + { "twinklesSection", "Twinkles", SectionFieldType, 0, 0, NULL, NULL, NULL }, + { "twinkleSpeed", "Twinkle Speed", NumberFieldType, 0, 8, getTwinkleSpeed, NULL, setTwinkleSpeed }, + { "twinkleDensity", "Twinkle Density", NumberFieldType, 0, 8, getTwinkleDensity, NULL, setTwinkleDensity }, +}; + +uint8_t fieldCount = ARRAY_SIZE(fields); diff --git a/src/gradientPalettes.h b/src/gradientPalettes.h new file mode 100644 index 0000000..0f201d5 --- /dev/null +++ b/src/gradientPalettes.h @@ -0,0 +1,467 @@ +// From ColorWavesWithPalettes by Mark Kriegsman: https://gist.github.com/kriegsman/8281905786e8b2632aeb + +// Gradient Color Palette definitions for 33 different cpt-city color palettes. +// 956 bytes of PROGMEM for all of the palettes together, +// +618 bytes of PROGMEM for gradient palette code (AVR). +// 1,494 bytes total for all 34 color palettes and associated code. + +// Gradient palette "ib_jul01_gp", originally from +// http://soliton.vm.bytemark.co.uk/pub/cpt-city/ing/xmas/tn/ib_jul01.png.index.html +// converted for FastLED with gammas (2.6, 2.2, 2.5) +// Size: 16 bytes of program space. + +DEFINE_GRADIENT_PALETTE( ib_jul01_gp ) { + 0, 194, 1, 1, + 94, 1, 29, 18, + 132, 57,131, 28, + 255, 113, 1, 1}; + +// Gradient palette "es_vintage_57_gp", originally from +// http://soliton.vm.bytemark.co.uk/pub/cpt-city/es/vintage/tn/es_vintage_57.png.index.html +// converted for FastLED with gammas (2.6, 2.2, 2.5) +// Size: 20 bytes of program space. + +DEFINE_GRADIENT_PALETTE( es_vintage_57_gp ) { + 0, 2, 1, 1, + 53, 18, 1, 0, + 104, 69, 29, 1, + 153, 167,135, 10, + 255, 46, 56, 4}; + +// Gradient palette "es_vintage_01_gp", originally from +// http://soliton.vm.bytemark.co.uk/pub/cpt-city/es/vintage/tn/es_vintage_01.png.index.html +// converted for FastLED with gammas (2.6, 2.2, 2.5) +// Size: 32 bytes of program space. + +DEFINE_GRADIENT_PALETTE( es_vintage_01_gp ) { + 0, 4, 1, 1, + 51, 16, 0, 1, + 76, 97,104, 3, + 101, 255,131, 19, + 127, 67, 9, 4, + 153, 16, 0, 1, + 229, 4, 1, 1, + 255, 4, 1, 1}; + +// Gradient palette "es_rivendell_15_gp", originally from +// http://soliton.vm.bytemark.co.uk/pub/cpt-city/es/rivendell/tn/es_rivendell_15.png.index.html +// converted for FastLED with gammas (2.6, 2.2, 2.5) +// Size: 20 bytes of program space. + +DEFINE_GRADIENT_PALETTE( es_rivendell_15_gp ) { + 0, 1, 14, 5, + 101, 16, 36, 14, + 165, 56, 68, 30, + 242, 150,156, 99, + 255, 150,156, 99}; + +// Gradient palette "rgi_15_gp", originally from +// http://soliton.vm.bytemark.co.uk/pub/cpt-city/ds/rgi/tn/rgi_15.png.index.html +// converted for FastLED with gammas (2.6, 2.2, 2.5) +// Size: 36 bytes of program space. + +DEFINE_GRADIENT_PALETTE( rgi_15_gp ) { + 0, 4, 1, 31, + 31, 55, 1, 16, + 63, 197, 3, 7, + 95, 59, 2, 17, + 127, 6, 2, 34, + 159, 39, 6, 33, + 191, 112, 13, 32, + 223, 56, 9, 35, + 255, 22, 6, 38}; + +// Gradient palette "retro2_16_gp", originally from +// http://soliton.vm.bytemark.co.uk/pub/cpt-city/ma/retro2/tn/retro2_16.png.index.html +// converted for FastLED with gammas (2.6, 2.2, 2.5) +// Size: 8 bytes of program space. + +DEFINE_GRADIENT_PALETTE( retro2_16_gp ) { + 0, 188,135, 1, + 255, 46, 7, 1}; + +// Gradient palette "Analogous_1_gp", originally from +// http://soliton.vm.bytemark.co.uk/pub/cpt-city/nd/red/tn/Analogous_1.png.index.html +// converted for FastLED with gammas (2.6, 2.2, 2.5) +// Size: 20 bytes of program space. + +DEFINE_GRADIENT_PALETTE( Analogous_1_gp ) { + 0, 3, 0,255, + 63, 23, 0,255, + 127, 67, 0,255, + 191, 142, 0, 45, + 255, 255, 0, 0}; + +// Gradient palette "es_pinksplash_08_gp", originally from +// http://soliton.vm.bytemark.co.uk/pub/cpt-city/es/pink_splash/tn/es_pinksplash_08.png.index.html +// converted for FastLED with gammas (2.6, 2.2, 2.5) +// Size: 20 bytes of program space. + +DEFINE_GRADIENT_PALETTE( es_pinksplash_08_gp ) { + 0, 126, 11,255, + 127, 197, 1, 22, + 175, 210,157,172, + 221, 157, 3,112, + 255, 157, 3,112}; + +// Gradient palette "es_pinksplash_07_gp", originally from +// http://soliton.vm.bytemark.co.uk/pub/cpt-city/es/pink_splash/tn/es_pinksplash_07.png.index.html +// converted for FastLED with gammas (2.6, 2.2, 2.5) +// Size: 28 bytes of program space. + +DEFINE_GRADIENT_PALETTE( es_pinksplash_07_gp ) { + 0, 229, 1, 1, + 61, 242, 4, 63, + 101, 255, 12,255, + 127, 249, 81,252, + 153, 255, 11,235, + 193, 244, 5, 68, + 255, 232, 1, 5}; + +// Gradient palette "Coral_reef_gp", originally from +// http://soliton.vm.bytemark.co.uk/pub/cpt-city/nd/other/tn/Coral_reef.png.index.html +// converted for FastLED with gammas (2.6, 2.2, 2.5) +// Size: 24 bytes of program space. + +DEFINE_GRADIENT_PALETTE( Coral_reef_gp ) { + 0, 40,199,197, + 50, 10,152,155, + 96, 1,111,120, + 96, 43,127,162, + 139, 10, 73,111, + 255, 1, 34, 71}; + +// Gradient palette "es_ocean_breeze_068_gp", originally from +// http://soliton.vm.bytemark.co.uk/pub/cpt-city/es/ocean_breeze/tn/es_ocean_breeze_068.png.index.html +// converted for FastLED with gammas (2.6, 2.2, 2.5) +// Size: 24 bytes of program space. + +DEFINE_GRADIENT_PALETTE( es_ocean_breeze_068_gp ) { + 0, 100,156,153, + 51, 1, 99,137, + 101, 1, 68, 84, + 104, 35,142,168, + 178, 0, 63,117, + 255, 1, 10, 10}; + +// Gradient palette "es_ocean_breeze_036_gp", originally from +// http://soliton.vm.bytemark.co.uk/pub/cpt-city/es/ocean_breeze/tn/es_ocean_breeze_036.png.index.html +// converted for FastLED with gammas (2.6, 2.2, 2.5) +// Size: 16 bytes of program space. + +DEFINE_GRADIENT_PALETTE( es_ocean_breeze_036_gp ) { + 0, 1, 6, 7, + 89, 1, 99,111, + 153, 144,209,255, + 255, 0, 73, 82}; + +// Gradient palette "departure_gp", originally from +// http://soliton.vm.bytemark.co.uk/pub/cpt-city/mjf/tn/departure.png.index.html +// converted for FastLED with gammas (2.6, 2.2, 2.5) +// Size: 88 bytes of program space. + +DEFINE_GRADIENT_PALETTE( departure_gp ) { + 0, 8, 3, 0, + 42, 23, 7, 0, + 63, 75, 38, 6, + 84, 169, 99, 38, + 106, 213,169,119, + 116, 255,255,255, + 138, 135,255,138, + 148, 22,255, 24, + 170, 0,255, 0, + 191, 0,136, 0, + 212, 0, 55, 0, + 255, 0, 55, 0}; + +// Gradient palette "es_landscape_64_gp", originally from +// http://soliton.vm.bytemark.co.uk/pub/cpt-city/es/landscape/tn/es_landscape_64.png.index.html +// converted for FastLED with gammas (2.6, 2.2, 2.5) +// Size: 36 bytes of program space. + +DEFINE_GRADIENT_PALETTE( es_landscape_64_gp ) { + 0, 0, 0, 0, + 37, 2, 25, 1, + 76, 15,115, 5, + 127, 79,213, 1, + 128, 126,211, 47, + 130, 188,209,247, + 153, 144,182,205, + 204, 59,117,250, + 255, 1, 37,192}; + +// Gradient palette "es_landscape_33_gp", originally from +// http://soliton.vm.bytemark.co.uk/pub/cpt-city/es/landscape/tn/es_landscape_33.png.index.html +// converted for FastLED with gammas (2.6, 2.2, 2.5) +// Size: 24 bytes of program space. + +DEFINE_GRADIENT_PALETTE( es_landscape_33_gp ) { + 0, 1, 5, 0, + 19, 32, 23, 1, + 38, 161, 55, 1, + 63, 229,144, 1, + 66, 39,142, 74, + 255, 1, 4, 1}; + +// Gradient palette "rainbowsherbet_gp", originally from +// http://soliton.vm.bytemark.co.uk/pub/cpt-city/ma/icecream/tn/rainbowsherbet.png.index.html +// converted for FastLED with gammas (2.6, 2.2, 2.5) +// Size: 28 bytes of program space. + +DEFINE_GRADIENT_PALETTE( rainbowsherbet_gp ) { + 0, 255, 33, 4, + 43, 255, 68, 25, + 86, 255, 7, 25, + 127, 255, 82,103, + 170, 255,255,242, + 209, 42,255, 22, + 255, 87,255, 65}; + +// Gradient palette "gr65_hult_gp", originally from +// http://soliton.vm.bytemark.co.uk/pub/cpt-city/hult/tn/gr65_hult.png.index.html +// converted for FastLED with gammas (2.6, 2.2, 2.5) +// Size: 24 bytes of program space. + +DEFINE_GRADIENT_PALETTE( gr65_hult_gp ) { + 0, 247,176,247, + 48, 255,136,255, + 89, 220, 29,226, + 160, 7, 82,178, + 216, 1,124,109, + 255, 1,124,109}; + +// Gradient palette "gr64_hult_gp", originally from +// http://soliton.vm.bytemark.co.uk/pub/cpt-city/hult/tn/gr64_hult.png.index.html +// converted for FastLED with gammas (2.6, 2.2, 2.5) +// Size: 32 bytes of program space. + +DEFINE_GRADIENT_PALETTE( gr64_hult_gp ) { + 0, 1,124,109, + 66, 1, 93, 79, + 104, 52, 65, 1, + 130, 115,127, 1, + 150, 52, 65, 1, + 201, 1, 86, 72, + 239, 0, 55, 45, + 255, 0, 55, 45}; + +// Gradient palette "GMT_drywet_gp", originally from +// http://soliton.vm.bytemark.co.uk/pub/cpt-city/gmt/tn/GMT_drywet.png.index.html +// converted for FastLED with gammas (2.6, 2.2, 2.5) +// Size: 28 bytes of program space. + +DEFINE_GRADIENT_PALETTE( GMT_drywet_gp ) { + 0, 47, 30, 2, + 42, 213,147, 24, + 84, 103,219, 52, + 127, 3,219,207, + 170, 1, 48,214, + 212, 1, 1,111, + 255, 1, 7, 33}; + +// Gradient palette "ib15_gp", originally from +// http://soliton.vm.bytemark.co.uk/pub/cpt-city/ing/general/tn/ib15.png.index.html +// converted for FastLED with gammas (2.6, 2.2, 2.5) +// Size: 24 bytes of program space. + +DEFINE_GRADIENT_PALETTE( ib15_gp ) { + 0, 113, 91,147, + 72, 157, 88, 78, + 89, 208, 85, 33, + 107, 255, 29, 11, + 141, 137, 31, 39, + 255, 59, 33, 89}; + +// Gradient palette "Fuschia_7_gp", originally from +// http://soliton.vm.bytemark.co.uk/pub/cpt-city/ds/fuschia/tn/Fuschia-7.png.index.html +// converted for FastLED with gammas (2.6, 2.2, 2.5) +// Size: 20 bytes of program space. + +DEFINE_GRADIENT_PALETTE( Fuschia_7_gp ) { + 0, 43, 3,153, + 63, 100, 4,103, + 127, 188, 5, 66, + 191, 161, 11,115, + 255, 135, 20,182}; + +// Gradient palette "es_emerald_dragon_08_gp", originally from +// http://soliton.vm.bytemark.co.uk/pub/cpt-city/es/emerald_dragon/tn/es_emerald_dragon_08.png.index.html +// converted for FastLED with gammas (2.6, 2.2, 2.5) +// Size: 16 bytes of program space. + +DEFINE_GRADIENT_PALETTE( es_emerald_dragon_08_gp ) { + 0, 97,255, 1, + 101, 47,133, 1, + 178, 13, 43, 1, + 255, 2, 10, 1}; + +// Gradient palette "lava_gp", originally from +// http://soliton.vm.bytemark.co.uk/pub/cpt-city/neota/elem/tn/lava.png.index.html +// converted for FastLED with gammas (2.6, 2.2, 2.5) +// Size: 52 bytes of program space. + +DEFINE_GRADIENT_PALETTE( lava_gp ) { + 0, 0, 0, 0, + 46, 18, 0, 0, + 96, 113, 0, 0, + 108, 142, 3, 1, + 119, 175, 17, 1, + 146, 213, 44, 2, + 174, 255, 82, 4, + 188, 255,115, 4, + 202, 255,156, 4, + 218, 255,203, 4, + 234, 255,255, 4, + 244, 255,255, 71, + 255, 255,255,255}; + +// Gradient palette "fire_gp", originally from +// http://soliton.vm.bytemark.co.uk/pub/cpt-city/neota/elem/tn/fire.png.index.html +// converted for FastLED with gammas (2.6, 2.2, 2.5) +// Size: 28 bytes of program space. + +DEFINE_GRADIENT_PALETTE( fire_gp ) { + 0, 1, 1, 0, + 76, 32, 5, 0, + 146, 192, 24, 0, + 197, 220,105, 5, + 240, 252,255, 31, + 250, 252,255,111, + 255, 255,255,255}; + +// Gradient palette "Colorfull_gp", originally from +// http://soliton.vm.bytemark.co.uk/pub/cpt-city/nd/atmospheric/tn/Colorfull.png.index.html +// converted for FastLED with gammas (2.6, 2.2, 2.5) +// Size: 44 bytes of program space. + +DEFINE_GRADIENT_PALETTE( Colorfull_gp ) { + 0, 10, 85, 5, + 25, 29,109, 18, + 60, 59,138, 42, + 93, 83, 99, 52, + 106, 110, 66, 64, + 109, 123, 49, 65, + 113, 139, 35, 66, + 116, 192,117, 98, + 124, 255,255,137, + 168, 100,180,155, + 255, 22,121,174}; + +// Gradient palette "Magenta_Evening_gp", originally from +// http://soliton.vm.bytemark.co.uk/pub/cpt-city/nd/atmospheric/tn/Magenta_Evening.png.index.html +// converted for FastLED with gammas (2.6, 2.2, 2.5) +// Size: 28 bytes of program space. + +DEFINE_GRADIENT_PALETTE( Magenta_Evening_gp ) { + 0, 71, 27, 39, + 31, 130, 11, 51, + 63, 213, 2, 64, + 70, 232, 1, 66, + 76, 252, 1, 69, + 108, 123, 2, 51, + 255, 46, 9, 35}; + +// Gradient palette "Pink_Purple_gp", originally from +// http://soliton.vm.bytemark.co.uk/pub/cpt-city/nd/atmospheric/tn/Pink_Purple.png.index.html +// converted for FastLED with gammas (2.6, 2.2, 2.5) +// Size: 44 bytes of program space. + +DEFINE_GRADIENT_PALETTE( Pink_Purple_gp ) { + 0, 19, 2, 39, + 25, 26, 4, 45, + 51, 33, 6, 52, + 76, 68, 62,125, + 102, 118,187,240, + 109, 163,215,247, + 114, 217,244,255, + 122, 159,149,221, + 149, 113, 78,188, + 183, 128, 57,155, + 255, 146, 40,123}; + +// Gradient palette "Sunset_Real_gp", originally from +// http://soliton.vm.bytemark.co.uk/pub/cpt-city/nd/atmospheric/tn/Sunset_Real.png.index.html +// converted for FastLED with gammas (2.6, 2.2, 2.5) +// Size: 28 bytes of program space. + +DEFINE_GRADIENT_PALETTE( Sunset_Real_gp ) { + 0, 120, 0, 0, + 22, 179, 22, 0, + 51, 255,104, 0, + 85, 167, 22, 18, + 135, 100, 0,103, + 198, 16, 0,130, + 255, 0, 0,160}; + +// Gradient palette "es_autumn_19_gp", originally from +// http://soliton.vm.bytemark.co.uk/pub/cpt-city/es/autumn/tn/es_autumn_19.png.index.html +// converted for FastLED with gammas (2.6, 2.2, 2.5) +// Size: 52 bytes of program space. + +DEFINE_GRADIENT_PALETTE( es_autumn_19_gp ) { + 0, 26, 1, 1, + 51, 67, 4, 1, + 84, 118, 14, 1, + 104, 137,152, 52, + 112, 113, 65, 1, + 122, 133,149, 59, + 124, 137,152, 52, + 135, 113, 65, 1, + 142, 139,154, 46, + 163, 113, 13, 1, + 204, 55, 3, 1, + 249, 17, 1, 1, + 255, 17, 1, 1}; + +// Gradient palette "BlacK_Blue_Magenta_White_gp", originally from +// http://soliton.vm.bytemark.co.uk/pub/cpt-city/nd/basic/tn/BlacK_Blue_Magenta_White.png.index.html +// converted for FastLED with gammas (2.6, 2.2, 2.5) +// Size: 28 bytes of program space. + +DEFINE_GRADIENT_PALETTE( BlacK_Blue_Magenta_White_gp ) { + 0, 0, 0, 0, + 42, 0, 0, 45, + 84, 0, 0,255, + 127, 42, 0,255, + 170, 255, 0,255, + 212, 255, 55,255, + 255, 255,255,255}; + +// Gradient palette "BlacK_Magenta_Red_gp", originally from +// http://soliton.vm.bytemark.co.uk/pub/cpt-city/nd/basic/tn/BlacK_Magenta_Red.png.index.html +// converted for FastLED with gammas (2.6, 2.2, 2.5) +// Size: 20 bytes of program space. + +DEFINE_GRADIENT_PALETTE( BlacK_Magenta_Red_gp ) { + 0, 0, 0, 0, + 63, 42, 0, 45, + 127, 255, 0,255, + 191, 255, 0, 45, + 255, 255, 0, 0}; + +// Gradient palette "BlacK_Red_Magenta_Yellow_gp", originally from +// http://soliton.vm.bytemark.co.uk/pub/cpt-city/nd/basic/tn/BlacK_Red_Magenta_Yellow.png.index.html +// converted for FastLED with gammas (2.6, 2.2, 2.5) +// Size: 28 bytes of program space. + +DEFINE_GRADIENT_PALETTE( BlacK_Red_Magenta_Yellow_gp ) { + 0, 0, 0, 0, + 42, 42, 0, 0, + 84, 255, 0, 0, + 127, 255, 0, 45, + 170, 255, 0,255, + 212, 255, 55, 45, + 255, 255,255, 0}; + +// Gradient palette "Blue_Cyan_Yellow_gp", originally from +// http://soliton.vm.bytemark.co.uk/pub/cpt-city/nd/basic/tn/Blue_Cyan_Yellow.png.index.html +// converted for FastLED with gammas (2.6, 2.2, 2.5) +// Size: 20 bytes of program space. + +DEFINE_GRADIENT_PALETTE( Blue_Cyan_Yellow_gp ) { + 0, 0, 0,255, + 63, 0, 55,255, + 127, 0,255,255, + 191, 42,255, 45, + 255, 255,255, 0}; + diff --git a/src/palettes.h b/src/palettes.h new file mode 100644 index 0000000..d509aac --- /dev/null +++ b/src/palettes.h @@ -0,0 +1,224 @@ +/* + ESP32 FastLED WebServer: https://github.com/jasoncoon/esp32-fastled-webserver + Copyright (C) 2017 Jason Coon + + Built upon the amazing FastLED work of Daniel Garcia and Mark Kriegsman: + https://github.com/FastLED/FastLED + + ESP32 support provided by the hard work of Sam Guyer: + https://github.com/samguyer/FastLED + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +// A mostly red palette with green accents and white trim. +// "CRGB::Gray" is used as white to keep the brightness more uniform. +const TProgmemRGBPalette16 RedGreenWhite_p FL_PROGMEM = +{ CRGB::Red, CRGB::Red, CRGB::Red, CRGB::Red, + CRGB::Red, CRGB::Red, CRGB::Red, CRGB::Red, + CRGB::Red, CRGB::Red, CRGB::Gray, CRGB::Gray, + CRGB::Green, CRGB::Green, CRGB::Green, CRGB::Green +}; + +// A mostly (dark) green palette with red berries. +#define Holly_Green 0x00580c +#define Holly_Red 0xB00402 +const TProgmemRGBPalette16 Holly_p FL_PROGMEM = +{ Holly_Green, Holly_Green, Holly_Green, Holly_Green, + Holly_Green, Holly_Green, Holly_Green, Holly_Green, + Holly_Green, Holly_Green, Holly_Green, Holly_Green, + Holly_Green, Holly_Green, Holly_Green, Holly_Red +}; + +// A red and white striped palette +// "CRGB::Gray" is used as white to keep the brightness more uniform. +const TProgmemRGBPalette16 RedWhite_p FL_PROGMEM = +{ CRGB::Red, CRGB::Red, CRGB::Gray, CRGB::Gray, + CRGB::Red, CRGB::Red, CRGB::Gray, CRGB::Gray, + CRGB::Red, CRGB::Red, CRGB::Gray, CRGB::Gray, + CRGB::Red, CRGB::Red, CRGB::Gray, CRGB::Gray +}; + +// A mostly blue palette with white accents. +// "CRGB::Gray" is used as white to keep the brightness more uniform. +const TProgmemRGBPalette16 BlueWhite_p FL_PROGMEM = +{ CRGB::Blue, CRGB::Blue, CRGB::Blue, CRGB::Blue, + CRGB::Blue, CRGB::Blue, CRGB::Blue, CRGB::Blue, + CRGB::Blue, CRGB::Blue, CRGB::Blue, CRGB::Blue, + CRGB::Blue, CRGB::Gray, CRGB::Gray, CRGB::Gray +}; + +// A pure "fairy light" palette with some brightness variations +#define HALFFAIRY ((CRGB::FairyLight & 0xFEFEFE) / 2) +#define QUARTERFAIRY ((CRGB::FairyLight & 0xFCFCFC) / 4) +const TProgmemRGBPalette16 FairyLight_p FL_PROGMEM = +{ CRGB::FairyLight, CRGB::FairyLight, CRGB::FairyLight, CRGB::FairyLight, + HALFFAIRY, HALFFAIRY, CRGB::FairyLight, CRGB::FairyLight, + QUARTERFAIRY, QUARTERFAIRY, CRGB::FairyLight, CRGB::FairyLight, + CRGB::FairyLight, CRGB::FairyLight, CRGB::FairyLight, CRGB::FairyLight +}; + +// A palette of soft snowflakes with the occasional bright one +const TProgmemRGBPalette16 Snow_p FL_PROGMEM = +{ 0x304048, 0x304048, 0x304048, 0x304048, + 0x304048, 0x304048, 0x304048, 0x304048, + 0x304048, 0x304048, 0x304048, 0x304048, + 0x304048, 0x304048, 0x304048, 0xE0F0FF +}; + +// A palette reminiscent of large 'old-school' C9-size tree lights +// in the five classic colors: red, orange, green, blue, and white. +#define C9_Red 0xB80400 +#define C9_Orange 0x902C02 +#define C9_Green 0x046002 +#define C9_Blue 0x070758 +#define C9_White 0x606820 +const TProgmemRGBPalette16 RetroC9_p FL_PROGMEM = +{ C9_Red, C9_Orange, C9_Red, C9_Orange, + C9_Orange, C9_Red, C9_Orange, C9_Red, + C9_Green, C9_Green, C9_Green, C9_Green, + C9_Blue, C9_Blue, C9_Blue, + C9_White +}; + +// A cold, icy pale blue palette +#define Ice_Blue1 0x0C1040 +#define Ice_Blue2 0x182080 +#define Ice_Blue3 0x5080C0 +const TProgmemRGBPalette16 IcyBlue_p FL_PROGMEM = +{ + Ice_Blue1, Ice_Blue1, Ice_Blue1, Ice_Blue1, + Ice_Blue1, Ice_Blue1, Ice_Blue1, Ice_Blue1, + Ice_Blue1, Ice_Blue1, Ice_Blue1, Ice_Blue1, + Ice_Blue2, Ice_Blue2, Ice_Blue2, Ice_Blue3 +}; + +CRGBPalette16 IceColors_p = CRGBPalette16(CRGB::Black, CRGB::Blue, CRGB::Aqua, CRGB::White); + +#include "gradientPalettes.h" + +const CRGBPalette16 palettes[] = { + RainbowColors_p, + RainbowStripeColors_p, + CloudColors_p, + LavaColors_p, + OceanColors_p, + ForestColors_p, + PartyColors_p, + HeatColors_p, + + IceColors_p, + IcyBlue_p, + Snow_p, + RedWhite_p, + BlueWhite_p, + FairyLight_p, + RetroC9_p, + RedGreenWhite_p, + Holly_p, + + Sunset_Real_gp, + es_rivendell_15_gp, + es_ocean_breeze_036_gp, + rgi_15_gp, + retro2_16_gp, + Analogous_1_gp, + es_pinksplash_08_gp, + Coral_reef_gp, + es_ocean_breeze_068_gp, + es_pinksplash_07_gp, + es_vintage_01_gp, + departure_gp, + es_landscape_64_gp, + es_landscape_33_gp, + rainbowsherbet_gp, + gr65_hult_gp, + gr64_hult_gp, + GMT_drywet_gp, + ib_jul01_gp, + es_vintage_57_gp, + ib15_gp, + Fuschia_7_gp, + es_emerald_dragon_08_gp, + lava_gp, + fire_gp, + Colorfull_gp, + Magenta_Evening_gp, + Pink_Purple_gp, + es_autumn_19_gp, + BlacK_Blue_Magenta_White_gp, + BlacK_Magenta_Red_gp, + BlacK_Red_Magenta_Yellow_gp, + Blue_Cyan_Yellow_gp, +}; + +const uint8_t paletteCount = ARRAY_SIZE(palettes); + +const String paletteNames[paletteCount] = { + "Rainbow", + "Rainbow Stripe", + "Cloud", + "Lava", + "Ocean", + "Forest", + "Party", + "Heat", + + "Ice", + "Icy Blue", + "Snow", + "Red & White", + "Blue & White", + "Fairy", + "Retro C9", + "Red, Green & White", + "Holly", + + "Sunset_Real", + "es_rivendell_15", + "es_ocean_breeze_036", + "rgi_15", + "retro2_16", + "Analogous_1", + "es_pinksplash_08", + "Coral_reef", + "es_ocean_breeze_068", + "es_pinksplash_07", + "es_vintage_01", + "departure", + "es_landscape_64", + "es_landscape_33", + "rainbowsherbet", + "gr65_hult", + "gr64_hult", + "GMT_drywet", + "ib_jul01", + "es_vintage_57", + "ib15", + "Fuschia_7", + "es_emerald_dragon_08", + "lava", + "fire", + "Colorfull", + "Magenta_Evening", + "Pink_Purple", + "es_autumn_19", + "BlacK_Blue_Magenta_White", + "BlacK_Magenta_Red", + "BlacK_Red_Magenta_Yellow", + "Blue_Cyan_Yellow", +}; + +CRGBPalette16 currentPalette( CRGB::Black); +CRGBPalette16 targetPalette( palettes[0] ); diff --git a/src/patterns.h b/src/patterns.h new file mode 100644 index 0000000..2222868 --- /dev/null +++ b/src/patterns.h @@ -0,0 +1,285 @@ +/* + ESP32 FastLED WebServer: https://github.com/jasoncoon/esp32-fastled-webserver + Copyright (C) 2017 Jason Coon + + Built upon the amazing FastLED work of Daniel Garcia and Mark Kriegsman: + https://github.com/FastLED/FastLED + + ESP32 support provided by the hard work of Sam Guyer: + https://github.com/samguyer/FastLED + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "palettes.h" +#include "twinkleFox.h" + +void rainbow() +{ + // FastLED's built-in rainbow generator + fill_rainbow( leds, NUM_LEDS, gHue, speed); +} + +void addGlitter( fract8 chanceOfGlitter) +{ + if ( random8() < chanceOfGlitter) { + leds[ random16(NUM_LEDS) ] += CRGB::White; + } +} + +void rainbowWithGlitter() +{ + // built-in FastLED rainbow, plus some random sparkly glitter + rainbow(); + addGlitter(80); +} + +void confetti() +{ + // random colored speckles that blink in and fade smoothly + fadeToBlackBy( leds, NUM_LEDS, 10); + int pos = random16(NUM_LEDS); + leds[pos] += CHSV( gHue + random8(64), 200, 255); +} + +void sinelon() +{ + // a colored dot sweeping back and forth, with fading trails + fadeToBlackBy( leds, NUM_LEDS, 20); + int pos = beatsin16(speed, 0, NUM_LEDS - 1); + static int prevpos = 0; + CRGB color = ColorFromPalette(palettes[currentPaletteIndex], gHue, 255); + if ( pos < prevpos ) { + fill_solid( leds + pos, (prevpos - pos) + 1, color); + } else { + fill_solid( leds + prevpos, (pos - prevpos) + 1, color); + } + prevpos = pos; +} + +void bpm() +{ + // colored stripes pulsing at a defined Beats-Per-Minute (BPM) + uint8_t beat = beatsin8( speed, 64, 255); + CRGBPalette16 palette = palettes[currentPaletteIndex]; + for ( int i = 0; i < NUM_LEDS; i++) { + leds[i] = ColorFromPalette(palette, gHue + (i * 2), beat - gHue + (i * 10)); + } +} + +void juggle() { + // eight colored dots, weaving in and out of sync with each other + fadeToBlackBy( leds, NUM_LEDS, 20); + byte dothue = 0; + for ( int i = 0; i < 8; i++) { + leds[beatsin16( i + speed, 0, NUM_LEDS - 1 )] |= CHSV(dothue, 200, 255); + dothue += 32; + } +} + +void showSolidColor() +{ + fill_solid(leds, NUM_LEDS, solidColor); +} + +// based on FastLED example Fire2012WithPalette: https://github.com/FastLED/FastLED/blob/master/examples/Fire2012WithPalette/Fire2012WithPalette.ino +void heatMap(CRGBPalette16 palette, bool up) +{ + fill_solid(leds, NUM_LEDS, CRGB::Black); + + // Add entropy to random number generator; we use a lot of it. + random16_add_entropy(random(256)); + + // Array of temperature readings at each simulation cell + static byte heat[NUM_LEDS]; + + byte colorindex; + + // Step 1. Cool down every cell a little + for ( uint16_t i = 0; i < NUM_LEDS; i++) { + heat[i] = qsub8( heat[i], random8(0, ((cooling * 10) / NUM_LEDS) + 2)); + } + + // Step 2. Heat from each cell drifts 'up' and diffuses a little + for ( uint16_t k = NUM_LEDS - 1; k >= 2; k--) { + heat[k] = (heat[k - 1] + heat[k - 2] + heat[k - 2] ) / 3; + } + + // Step 3. Randomly ignite new 'sparks' of heat near the bottom + if ( random8() < sparking ) { + int y = random8(7); + heat[y] = qadd8( heat[y], random8(160, 255) ); + } + + // Step 4. Map from heat cells to LED colors + for ( uint16_t j = 0; j < NUM_LEDS; j++) { + // Scale the heat value from 0-255 down to 0-240 + // for best results with color palettes. + colorindex = scale8(heat[j], 190); + + CRGB color = ColorFromPalette(palette, colorindex); + + if (up) { + leds[j] = color; + } + else { + leds[(NUM_LEDS - 1) - j] = color; + } + } +} + +void fire() +{ + heatMap(HeatColors_p, true); +} + +void water() +{ + heatMap(IceColors_p, false); +} + +// Pride2015 by Mark Kriegsman: https://gist.github.com/kriegsman/964de772d64c502760e5 +// This function draws rainbows with an ever-changing, +// widely-varying set of parameters. +void pride() +{ + static uint16_t sPseudotime = 0; + static uint16_t sLastMillis = 0; + static uint16_t sHue16 = 0; + + uint8_t sat8 = beatsin88( 87, 220, 250); + uint8_t brightdepth = beatsin88( 341, 96, 224); + uint16_t brightnessthetainc16 = beatsin88( 203, (25 * 256), (40 * 256)); + uint8_t msmultiplier = beatsin88(147, 23, 60); + + uint16_t hue16 = sHue16;//gHue * 256; + uint16_t hueinc16 = beatsin88(113, 1, 3000); + + uint16_t ms = millis(); + uint16_t deltams = ms - sLastMillis ; + sLastMillis = ms; + sPseudotime += deltams * msmultiplier; + sHue16 += deltams * beatsin88( 400, 5, 9); + uint16_t brightnesstheta16 = sPseudotime; + + for ( uint16_t i = 0 ; i < NUM_LEDS; i++) { + hue16 += hueinc16; + uint8_t hue8 = hue16 / 256; + + brightnesstheta16 += brightnessthetainc16; + uint16_t b16 = sin16( brightnesstheta16 ) + 32768; + + uint16_t bri16 = (uint32_t)((uint32_t)b16 * (uint32_t)b16) / 65536; + uint8_t bri8 = (uint32_t)(((uint32_t)bri16) * brightdepth) / 65536; + bri8 += (255 - brightdepth); + + CRGB newcolor = CHSV( hue8, sat8, bri8); + + uint16_t pixelnumber = i; + pixelnumber = (NUM_LEDS - 1) - pixelnumber; + + nblend( leds[pixelnumber], newcolor, 64); + } +} + +// ColorWavesWithPalettes by Mark Kriegsman: https://gist.github.com/kriegsman/8281905786e8b2632aeb +// This function draws color waves with an ever-changing, +// widely-varying set of parameters, using a color palette. +void colorwaves( CRGB* ledarray, uint16_t numleds, CRGBPalette16& palette) +{ + static uint16_t sPseudotime = 0; + static uint16_t sLastMillis = 0; + static uint16_t sHue16 = 0; + + // uint8_t sat8 = beatsin88( 87, 220, 250); + uint8_t brightdepth = beatsin88( 341, 96, 224); + uint16_t brightnessthetainc16 = beatsin88( 203, (25 * 256), (40 * 256)); + uint8_t msmultiplier = beatsin88(147, 23, 60); + + uint16_t hue16 = sHue16;//gHue * 256; + uint16_t hueinc16 = beatsin88(113, 300, 1500); + + uint16_t ms = millis(); + uint16_t deltams = ms - sLastMillis ; + sLastMillis = ms; + sPseudotime += deltams * msmultiplier; + sHue16 += deltams * beatsin88( 400, 5, 9); + uint16_t brightnesstheta16 = sPseudotime; + + for ( uint16_t i = 0 ; i < numleds; i++) { + hue16 += hueinc16; + uint8_t hue8 = hue16 / 256; + uint16_t h16_128 = hue16 >> 7; + if ( h16_128 & 0x100) { + hue8 = 255 - (h16_128 >> 1); + } else { + hue8 = h16_128 >> 1; + } + + brightnesstheta16 += brightnessthetainc16; + uint16_t b16 = sin16( brightnesstheta16 ) + 32768; + + uint16_t bri16 = (uint32_t)((uint32_t)b16 * (uint32_t)b16) / 65536; + uint8_t bri8 = (uint32_t)(((uint32_t)bri16) * brightdepth) / 65536; + bri8 += (255 - brightdepth); + + uint8_t index = hue8; + //index = triwave8( index); + index = scale8( index, 240); + + CRGB newcolor = ColorFromPalette( palette, index, bri8); + + uint16_t pixelnumber = i; + pixelnumber = (numleds - 1) - pixelnumber; + + nblend( ledarray[pixelnumber], newcolor, 128); + } +} + +void colorWaves() +{ + colorwaves(leds, NUM_LEDS, currentPalette); +} + +typedef void (*Pattern)(); +typedef Pattern PatternList[]; +typedef struct { + Pattern pattern; + String name; +} PatternAndName; +typedef PatternAndName PatternAndNameList[]; + +PatternAndNameList patterns = { + { pride, "Pride" }, + { colorWaves, "Color Waves" }, + + // TwinkleFOX patterns + { drawTwinkles, "Twinkles" }, + + // Fire & Water + { fire, "Fire" }, + { water, "Water" }, + + // DemoReel100 patterns + { rainbow, "rainbow" }, + { rainbowWithGlitter, "rainbowWithGlitter" }, + { confetti, "confetti" }, + { sinelon, "sinelon" }, + { juggle, "juggle" }, + { bpm, "bpm" }, + + { showSolidColor, "Solid Color" }, +}; + +const uint8_t patternCount = ARRAY_SIZE(patterns); diff --git a/src/studioledcam.ino b/src/studioledcam.ino new file mode 100644 index 0000000..ea9eb4a --- /dev/null +++ b/src/studioledcam.ino @@ -0,0 +1,205 @@ +/* + ESP32 FastLED WebServer: https://github.com/jasoncoon/esp32-fastled-webserver + Copyright (C) 2017 Jason Coon + + Built upon the amazing FastLED work of Daniel Garcia and Mark Kriegsman: + https://github.com/FastLED/FastLED + + ESP32 support provided by the hard work of Sam Guyer: + https://github.com/samguyer/FastLED + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include +#include +#include +#include +#include +#include + +#if defined(FASTLED_VERSION) && (FASTLED_VERSION < 3003000) +#warning "Requires FastLED 3.3 or later; check github for latest code." +#endif + +WebServer webServer(80); + +const int led = 5; + +uint8_t autoplay = 0; +uint8_t autoplayDuration = 10; +unsigned long autoPlayTimeout = 0; + +uint8_t currentPatternIndex = 0; // Index number of which pattern is current + +uint8_t gHue = 0; // rotating "base color" used by many of the patterns + +uint8_t power = 1; +uint8_t brightness = 8; + +uint8_t speed = 30; + +// COOLING: How much does the air cool as it rises? +// Less cooling = taller flames. More cooling = shorter flames. +// Default 50, suggested range 20-100 +uint8_t cooling = 50; + +// SPARKING: What chance (out of 255) is there that a new spark will be lit? +// Higher chance = more roaring fire. Lower chance = more flickery fire. +// Default 120, suggested range 50-200. +uint8_t sparking = 120; + +CRGB solidColor = CRGB::Blue; + +uint8_t cyclePalettes = 0; +uint8_t paletteDuration = 10; +uint8_t currentPaletteIndex = 0; +unsigned long paletteTimeout = 0; + +#define ARRAY_SIZE(A) (sizeof(A) / sizeof((A)[0])) + +#define LED_TYPE WS2812B +#define COLOR_ORDER GRB +#define NUM_STRIPS 4 +#define NUM_LEDS_PER_STRIP 16 +#define NUM_LEDS NUM_LEDS_PER_STRIP * NUM_STRIPS +CRGB leds[NUM_LEDS]; + +#define MILLI_AMPS 4000 // IMPORTANT: set the max milli-Amps of your power supply (4A = 4000mA) +#define FRAMES_PER_SECOND 120 + +#include "patterns.h" + +#include "field.h" +#include "fields.h" + +#include "secrets.h" +#include "wifi.h" +#include "web.h" + +// wifi ssid and password should be added to a file in the sketch named secrets.h +// the secrets.h file should be added to the .gitignore file and never committed or +// pushed to public source control (GitHub). +// const char* ssid = "........"; +// const char* password = "........"; + +void listDir(fs::FS &fs, const char * dirname, uint8_t levels) { + Serial.printf("Listing directory: %s\n", dirname); + + File root = fs.open(dirname); + if (!root) { + Serial.println("Failed to open directory"); + return; + } + if (!root.isDirectory()) { + Serial.println("Not a directory"); + return; + } + + File file = root.openNextFile(); + while (file) { + if (file.isDirectory()) { + Serial.print(" DIR : "); + Serial.println(file.name()); + if (levels) { + listDir(fs, file.name(), levels - 1); + } + } else { + Serial.print(" FILE: "); + Serial.print(file.name()); + Serial.print(" SIZE: "); + Serial.println(file.size()); + } + file = root.openNextFile(); + } +} + +void setup() { + pinMode(led, OUTPUT); + digitalWrite(led, 1); + + // delay(3000); // 3 second delay for recovery + Serial.begin(115200); + + SPIFFS.begin(); + listDir(SPIFFS, "/", 1); + +// loadFieldsFromEEPROM(fields, fieldCount); + + setupWifi(); + setupWeb(); + + + // Parallel output: 13, 12, 15, 14, + FastLED.addLeds(leds, 0, NUM_LEDS_PER_STRIP).setCorrection(TypicalLEDStrip); + FastLED.addLeds(leds, NUM_LEDS_PER_STRIP, NUM_LEDS_PER_STRIP).setCorrection(TypicalLEDStrip); + FastLED.addLeds(leds, 2 * NUM_LEDS_PER_STRIP, NUM_LEDS_PER_STRIP).setCorrection(TypicalLEDStrip); + FastLED.addLeds(leds, 3 * NUM_LEDS_PER_STRIP, NUM_LEDS_PER_STRIP).setCorrection(TypicalLEDStrip); + + + FastLED.setMaxPowerInVoltsAndMilliamps(5, MILLI_AMPS); + + // set master brightness control + FastLED.setBrightness(brightness); + + autoPlayTimeout = millis() + (autoplayDuration * 1000); +} + +void loop() +{ + handleWeb(); + + if (power == 0) { + fill_solid(leds, NUM_LEDS, CRGB::Black); + } + else { + // Call the current pattern function once, updating the 'leds' array + patterns[currentPatternIndex].pattern(); + + EVERY_N_MILLISECONDS(40) { + // slowly blend the current palette to the next + nblendPaletteTowardPalette(currentPalette, targetPalette, 8); + gHue++; // slowly cycle the "base color" through the rainbow + } + + if (autoplay == 1 && (millis() > autoPlayTimeout)) { + nextPattern(); + autoPlayTimeout = millis() + (autoplayDuration * 1000); + } + + if (cyclePalettes == 1 && (millis() > paletteTimeout)) { + nextPalette(); + paletteTimeout = millis() + (paletteDuration * 1000); + } + } + + // send the 'leds' array out to the actual LED strip + FastLED.show(); + + // insert a delay to keep the framerate modest + // FastLED.delay(1000 / FRAMES_PER_SECOND); + delay(1000 / FRAMES_PER_SECOND); +} + +void nextPattern() +{ + // add one to the current pattern number, and wrap around at the end + currentPatternIndex = (currentPatternIndex + 1) % patternCount; +} + +void nextPalette() +{ + currentPaletteIndex = (currentPaletteIndex + 1) % paletteCount; + targetPalette = palettes[currentPaletteIndex]; +} diff --git a/src/twinkleFox.h b/src/twinkleFox.h new file mode 100644 index 0000000..3b3a999 --- /dev/null +++ b/src/twinkleFox.h @@ -0,0 +1,229 @@ +// TwinkleFOX by Mark Kriegsman: https://gist.github.com/kriegsman/756ea6dcae8e30845b5a +// +// TwinkleFOX: Twinkling 'holiday' lights that fade in and out. +// Colors are chosen from a palette; a few palettes are provided. +// +// This December 2015 implementation improves on the December 2014 version +// in several ways: +// - smoother fading, compatible with any colors and any palettes +// - easier control of twinkle speed and twinkle density +// - supports an optional 'background color' +// - takes even less RAM: zero RAM overhead per pixel +// - illustrates a couple of interesting techniques (uh oh...) +// +// The idea behind this (new) implementation is that there's one +// basic, repeating pattern that each pixel follows like a waveform: +// The brightness rises from 0..255 and then falls back down to 0. +// The brightness at any given point in time can be determined as +// as a function of time, for example: +// brightness = sine( time ); // a sine wave of brightness over time +// +// So the way this implementation works is that every pixel follows +// the exact same wave function over time. In this particular case, +// I chose a sawtooth triangle wave (triwave8) rather than a sine wave, +// but the idea is the same: brightness = triwave8( time ). +// +// Of course, if all the pixels used the exact same wave form, and +// if they all used the exact same 'clock' for their 'time base', all +// the pixels would brighten and dim at once -- which does not look +// like twinkling at all. +// +// So to achieve random-looking twinkling, each pixel is given a +// slightly different 'clock' signal. Some of the clocks run faster, +// some run slower, and each 'clock' also has a random offset from zero. +// The net result is that the 'clocks' for all the pixels are always out +// of sync from each other, producing a nice random distribution +// of twinkles. +// +// The 'clock speed adjustment' and 'time offset' for each pixel +// are generated randomly. One (normal) approach to implementing that +// would be to randomly generate the clock parameters for each pixel +// at startup, and store them in some arrays. However, that consumes +// a great deal of precious RAM, and it turns out to be totally +// unnessary! If the random number generate is 'seeded' with the +// same starting value every time, it will generate the same sequence +// of values every time. So the clock adjustment parameters for each +// pixel are 'stored' in a pseudo-random number generator! The PRNG +// is reset, and then the first numbers out of it are the clock +// adjustment parameters for the first pixel, the second numbers out +// of it are the parameters for the second pixel, and so on. +// In this way, we can 'store' a stable sequence of thousands of +// random clock adjustment parameters in literally two bytes of RAM. +// +// There's a little bit of fixed-point math involved in applying the +// clock speed adjustments, which are expressed in eighths. Each pixel's +// clock speed ranges from 8/8ths of the system clock (i.e. 1x) to +// 23/8ths of the system clock (i.e. nearly 3x). +// +// On a basic Arduino Uno or Leonardo, this code can twinkle 300+ pixels +// smoothly at over 50 updates per seond. +// +// -Mark Kriegsman, December 2015 + +// Overall twinkle speed. +// 0 (VERY slow) to 8 (VERY fast). +// 4, 5, and 6 are recommended, default is 4. +uint8_t twinkleSpeed = 4; + +// Overall twinkle density. +// 0 (NONE lit) to 8 (ALL lit at once). +// Default is 5. +uint8_t twinkleDensity = 5; + +// Background color for 'unlit' pixels +// Can be set to CRGB::Black if desired. +CRGB gBackgroundColor = CRGB::Black; +// Example of dim incandescent fairy light background color +// CRGB gBackgroundColor = CRGB(CRGB::FairyLight).nscale8_video(16); + +// If AUTO_SELECT_BACKGROUND_COLOR is set to 1, +// then for any palette where the first two entries +// are the same, a dimmed version of that color will +// automatically be used as the background color. +#define AUTO_SELECT_BACKGROUND_COLOR 0 + +// If COOL_LIKE_INCANDESCENT is set to 1, colors will +// fade out slighted 'reddened', similar to how +// incandescent bulbs change color as they get dim down. +#define COOL_LIKE_INCANDESCENT 1 + +// This function is like 'triwave8', which produces a +// symmetrical up-and-down triangle sawtooth waveform, except that this +// function produces a triangle wave with a faster attack and a slower decay: +// +// / \__ +// / \__ +// / \__ +// / \__ +// + +uint8_t attackDecayWave8( uint8_t i) +{ + if( i < 86) { + return i * 3; + } else { + i -= 86; + return 255 - (i + (i/2)); + } +} + +// This function takes a pixel, and if its in the 'fading down' +// part of the cycle, it adjusts the color a little bit like the +// way that incandescent bulbs fade toward 'red' as they dim. +void coolLikeIncandescent( CRGB& c, uint8_t phase) +{ + if( phase < 128) return; + + uint8_t cooling = (phase - 128) >> 4; + c.g = qsub8( c.g, cooling); + c.b = qsub8( c.b, cooling * 2); +} + +// This function takes a time in pseudo-milliseconds, +// figures out brightness = f( time ), and also hue = f( time ) +// The 'low digits' of the millisecond time are used as +// input to the brightness wave function. +// The 'high digits' are used to select a color, so that the color +// does not change over the course of the fade-in, fade-out +// of one cycle of the brightness wave function. +// The 'high digits' are also used to determine whether this pixel +// should light at all during this cycle, based on the twinkleDensity. +CRGB computeOneTwinkle( uint32_t ms, uint8_t salt) +{ + uint16_t ticks = ms >> (8-twinkleSpeed); + uint8_t fastcycle8 = ticks; + uint16_t slowcycle16 = (ticks >> 8) + salt; + slowcycle16 += sin8( slowcycle16); + slowcycle16 = (slowcycle16 * 2053) + 1384; + uint8_t slowcycle8 = (slowcycle16 & 0xFF) + (slowcycle16 >> 8); + + uint8_t bright = 0; + if( ((slowcycle8 & 0x0E)/2) < twinkleDensity) { + bright = attackDecayWave8( fastcycle8); + } + + uint8_t hue = slowcycle8 - salt; + CRGB c; + if( bright > 0) { + c = ColorFromPalette( palettes[currentPaletteIndex], hue, bright, NOBLEND); + if( COOL_LIKE_INCANDESCENT == 1 ) { + coolLikeIncandescent( c, fastcycle8); + } + } else { + c = CRGB::Black; + } + return c; +} + +// This function loops over each pixel, calculates the +// adjusted 'clock' that this pixel should use, and calls +// "CalculateOneTwinkle" on each pixel. It then displays +// either the twinkle color of the background color, +// whichever is brighter. +void drawTwinkles() +{ + // "PRNG16" is the pseudorandom number generator + // It MUST be reset to the same starting value each time + // this function is called, so that the sequence of 'random' + // numbers that it generates is (paradoxically) stable. + uint16_t PRNG16 = 11337; + + uint32_t clock32 = millis(); + + CRGBPalette16 currentPalette = palettes[currentPaletteIndex]; + + // Set up the background color, "bg". + // if AUTO_SELECT_BACKGROUND_COLOR == 1, and the first two colors of + // the current palette are identical, then a deeply faded version of + // that color is used for the background color + CRGB bg; + if( (AUTO_SELECT_BACKGROUND_COLOR == 1) && + (currentPalette[0] == currentPalette[1] )) { + bg = currentPalette[0]; + uint8_t bglight = bg.getAverageLight(); + if( bglight > 64) { + bg.nscale8_video( 16); // very bright, so scale to 1/16th + } else if( bglight > 16) { + bg.nscale8_video( 64); // not that bright, so scale to 1/4th + } else { + bg.nscale8_video( 86); // dim, scale to 1/3rd. + } + } else { + bg = gBackgroundColor; // just use the explicitly defined background color + } + + uint8_t backgroundBrightness = bg.getAverageLight(); + + for(uint16_t i = 0; i < NUM_LEDS; i++) { + CRGB& pixel = leds[i]; + + PRNG16 = (uint16_t)(PRNG16 * 2053) + 1384; // next 'random' number + uint16_t myclockoffset16= PRNG16; // use that number as clock offset + PRNG16 = (uint16_t)(PRNG16 * 2053) + 1384; // next 'random' number + // use that number as clock speed adjustment factor (in 8ths, from 8/8ths to 23/8ths) + uint8_t myspeedmultiplierQ5_3 = ((((PRNG16 & 0xFF)>>4) + (PRNG16 & 0x0F)) & 0x0F) + 0x08; + uint32_t myclock30 = (uint32_t)((clock32 * myspeedmultiplierQ5_3) >> 3) + myclockoffset16; + uint8_t myunique8 = PRNG16 >> 8; // get 'salt' value for this pixel + + // We now have the adjusted 'clock' for this pixel, now we call + // the function that computes what color the pixel should be based + // on the "brightness = f( time )" idea. + CRGB c = computeOneTwinkle( myclock30, myunique8); + + uint8_t cbright = c.getAverageLight(); + int16_t deltabright = cbright - backgroundBrightness; + if( deltabright >= 32 || (!bg)) { + // If the new pixel is significantly brighter than the background color, + // use the new color. + pixel = c; + } else if( deltabright > 0 ) { + // If the new pixel is just slightly brighter than the background color, + // mix a blend of the new color and the background color + pixel = blend( bg, c, deltabright * 8); + } else { + // if the new pixel is not at all brighter than the background color, + // just use the background color. + pixel = bg; + } + } +} diff --git a/src/web.h b/src/web.h new file mode 100644 index 0000000..eb02f67 --- /dev/null +++ b/src/web.h @@ -0,0 +1,87 @@ +/* + ESP32 FastLED WebServer: https://github.com/jasoncoon/esp32-fastled-webserver + Copyright (C) 2017 Jason Coon + + Built upon the amazing FastLED work of Daniel Garcia and Mark Kriegsman: + https://github.com/FastLED/FastLED + + ESP32 support provided by the hard work of Sam Guyer: + https://github.com/samguyer/FastLED + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +void setupWeb() { + webServer.on("/all", HTTP_GET, []() { + digitalWrite(led, 0); + String json = getFieldsJson(fields, fieldCount); + webServer.send(200, "text/json", json); + digitalWrite(led, 1); + }); + + webServer.on("/fieldValue", HTTP_GET, []() { + digitalWrite(led, 0); + String name = webServer.arg("name"); + String value = getFieldValue(name, fields, fieldCount); + webServer.send(200, "text/json", value); + digitalWrite(led, 1); + }); + + webServer.on("/fieldValue", HTTP_POST, []() { + digitalWrite(led, 0); + String name = webServer.arg("name"); + String value = webServer.arg("value"); + String newValue = setFieldValue(name, value, fields, fieldCount); + webServer.send(200, "text/json", newValue); + digitalWrite(led, 1); + }); + + webServer.serveStatic("/", SPIFFS, "/index.htm", "max-age=86400"); + webServer.serveStatic("/index.htm", SPIFFS, "/index.htm", "max-age=86400"); + webServer.serveStatic("/favicon.ico", SPIFFS, "/favicon.ico", "max-age=86400"); + webServer.serveStatic("/css/styles.css", SPIFFS, "/css/styles.css", "max-age=86400"); + webServer.serveStatic("/js/app.js", SPIFFS, "/js/app.js", "max-age=86400"); + webServer.serveStatic("/images/atom196.png", SPIFFS, "/images/atom196.png", "max-age=86400"); + + webServer.begin(); + Serial.println ( "HTTP server started" ); +} + +void handleWeb() { + static bool webServerStarted = false; + + // check for connection + if ( WiFi.status() == WL_CONNECTED ) { + if (!webServerStarted) { + // turn off the board's LED when connected to wifi + digitalWrite(led, 1); + Serial.println(); + Serial.println("WiFi connected"); + Serial.print("IP address: "); + Serial.println(WiFi.localIP()); + webServerStarted = true; + setupWeb(); + } + webServer.handleClient(); + } else { + // blink the board's LED while connecting to wifi + static uint8_t ledState = 0; + EVERY_N_MILLIS(125) { + ledState = ledState == 0 ? 1 : 0; + digitalWrite(led, ledState); + Serial.print ("."); + } + } +} + diff --git a/src/wifi.h b/src/wifi.h new file mode 100644 index 0000000..8d5c588 --- /dev/null +++ b/src/wifi.h @@ -0,0 +1,68 @@ +/* + ESP32 FastLED WebServer: https://github.com/jasoncoon/esp32-fastled-webserver + Copyright (C) 2017 Jason Coon + + Built upon the amazing FastLED work of Daniel Garcia and Mark Kriegsman: + https://github.com/FastLED/FastLED + + ESP32 support provided by the hard work of Sam Guyer: + https://github.com/samguyer/FastLED + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +//#define HOSTNAME "ESP32-" ///< Hostname. The setup function adds the Chip ID at the end. + +//const bool apMode = false; + +// AP mode password +//const char WiFiAPPSK[] = ""; + +void setupWifi() { + // // Set Hostname. + // String hostname(HOSTNAME); + // + // uint64_t chipid = ESP.getEfuseMac(); + // uint16_t long1 = (unsigned long)((chipid & 0xFFFF0000) >> 16 ); + // uint16_t long2 = (unsigned long)((chipid & 0x0000FFFF)); + // String hex = String(long1, HEX) + String(long2, HEX); // six octets + // hostname += hex; + // + // char hostnameChar[hostname.length() + 1]; + // memset(hostnameChar, 0, hostname.length() + 1); + // + // for (uint8_t i = 0; i < hostname.length(); i++) + // hostnameChar[i] = hostname.charAt(i); + // + // WiFi.setHostname(hostnameChar); + // + // // Print hostname. + // Serial.println("Hostname: " + hostname); + + // if (apMode) + // { + // WiFi.mode(WIFI_AP); + // WiFi.softAP(hostnameChar, WiFiAPPSK); + // Serial.printf("Connect to Wi-Fi access point: %s\n", hostnameChar); + // Serial.println("and open http://192.168.4.1 in your browser"); + // } + // else + // { + WiFi.mode(WIFI_STA); + Serial.printf("Connecting to %s\n", ssid); + if (String(WiFi.SSID()) != String(ssid)) { + WiFi.begin(ssid, password); + } + // } +}