diff --git a/README.md b/README.md index 0590375d..d381b755 100644 --- a/README.md +++ b/README.md @@ -65,6 +65,9 @@ The app depends on the following libraries. They must either be downloaded from * [FastLED](https://github.com/FastLED/FastLED) * [IRremoteESP8266](https://github.com/sebastienwarin/IRremoteESP8266) * [Arduino WebSockets](https://github.com/Links2004/arduinoWebSockets) +* [Arduino JSON](https://arduinojson.org) +* [lolrol LittleFS](https://github.com/lorol/LITTLEFS) -- (integrated into ESP32 core v2, which is not used here yet) + Download the app code from GitHub using the green Clone or Download button from [the GitHub project main page](https://github.com/jasoncoon/esp8266-fastled-webserver) and click Download ZIP. Decompress the ZIP file in your Arduino sketch folder. diff --git a/esp8266-fastled-webserver/Fields.cpp b/esp8266-fastled-webserver/Fields.cpp index 4821eb9e..9c34b807 100644 --- a/esp8266-fastled-webserver/Fields.cpp +++ b/esp8266-fastled-webserver/Fields.cpp @@ -17,479 +17,526 @@ */ #include "common.h" -const String NumberFieldType = "Number"; -const String BooleanFieldType = "Boolean"; -const String SelectFieldType = "Select"; -const String ColorFieldType = "Color"; -const String SectionFieldType = "Section"; -const String StringFieldType = "String"; -const String LabelFieldType = "Label"; -const String UtcOffsetIndexFieldType = "UtcOffsetIndex"; - -uint8_t power = 1; -uint8_t brightness = brightnessMap[brightnessIndex]; - -//String setPower(String value) { -// power = value.toInt(); -// if(power < 0) power = 0; -// else if (power > 1) power = 1; -// return String(power); -//} - -String getPower() { - return String(power); -} - -//String setBrightness(String value) { -// brightness = value.toInt(); -// if(brightness < 0) brightness = 0; -// else if (brightness > 255) brightness = 255; -// return String(brightness); -//} - -// TODO: wrapper class for EEPROM settings, to help ensure clear understanding -// of all locations that can cause settings committed / EEPROM write. -// Also simplifies reading prior value before writing new value (extend EEPROM life) -// Also simplifies later extending CRC, transactional updates, bypassing "dead" -// areas of EEPROM, etc. - -String getBrightness() { - return String(brightness); -} - -String getPattern() { - return String(currentPatternIndex); -} - -String getPatterns() { - String json = ""; +// only items outside the anonymous namespace can be exported. +// this keeps the global namespace cleaner, with no runtime/RAM costs. +inline namespace { + enum struct Field_t : uint8_t { + Number, + Boolean, + Select, + Color, + Section, + String, + Label, + UtcOffset, + }; + constexpr bool IsValid(const Field_t& t) { + return ( + (t == Field_t::Number ) || + (t == Field_t::Boolean ) || + (t == Field_t::Select ) || + (t == Field_t::Color ) || + (t == Field_t::Section ) || + (t == Field_t::String ) || + (t == Field_t::Label ) || + (t == Field_t::UtcOffset) ); + } - for (uint8_t i = 0; i < patternCount; i++) { - json += "\"" + patterns[i].name + "\""; - if (i < patternCount - 1) - json += ","; + const String ToString(const Field_t& t) { + return + (t == Field_t::String) ? F("String") : + (t == Field_t::Label) ? F("Label") : + (t == Field_t::Color) ? F("Color") : + (t == Field_t::Boolean) ? F("Boolean") : + (t == Field_t::Number) ? F("Number") : + (t == Field_t::Section) ? F("Section") : + (t == Field_t::Select) ? F("Select") : + (t == Field_t::UtcOffset) ? F("UtcOffsetIndex") : + F("Invalid") ; } - return json; -} + inline namespace LegacyStringGettersAndSetters { // This just helps folding / hiding these functions -String getPalette() { - return String(currentPaletteIndex); -} -String getPalettes() { - String json = ""; + //String setPower(String value) { + // power = value.toInt(); + // if(power < 0) power = 0; + // else if (power > 1) power = 1; + // return String(power); + //} - for (uint8_t i = 0; i < paletteCount; i++) { - json += "\"" + paletteNames[i] + "\""; - if (i < paletteCount - 1) - json += ","; - } + String getPower() { + return String(power); + } - return json; -} + //String setBrightness(String value) { + // brightness = value.toInt(); + // if(brightness < 0) brightness = 0; + // else if (brightness > 255) brightness = 255; + // return String(brightness); + //} + + // TODO: wrapper class for EEPROM settings, to help ensure clear understanding + // of all locations that can cause settings committed / EEPROM write. + // Also simplifies reading prior value before writing new value (extend EEPROM life) + // Also simplifies later extending CRC, transactional updates, bypassing "dead" + // areas of EEPROM, etc. + + String getBrightness() { + return String(brightness); + } -String getAutoplay() { - return String(autoplay); -} + String getPattern() { + return String(currentPatternIndex); + } -String getAutoplayDuration() { - return String(autoplayDuration); -} + String getPalette() { + return String(currentPaletteIndex); + } -String getShowClock() { - return String(showClock); -} + String getAutoplay() { + return String(autoplay); + } -String getClockBackgroundFade() { - return String(clockBackgroundFade); -} + String getAutoplayDuration() { + return String(autoplayDuration); + } -void setShowClock(uint8_t value) -{ - showClock = value == 0 ? 0 : 1; - writeAndCommitSettings(); - broadcastInt("showClock", showClock); -} + String getShowClock() { + return String(showClock); + } -void setClockBackgroundFade(uint8_t value) -{ - clockBackgroundFade = value; - writeAndCommitSettings(); - broadcastInt("clockBackgroundFade", clockBackgroundFade); -} + String getClockBackgroundFade() { + return String(clockBackgroundFade); + } -String getSolidColor() { - return String(solidColor.r) + "," + String(solidColor.g) + "," + String(solidColor.b); -} + String getSolidColor() { + return String(solidColor.r) + "," + String(solidColor.g) + "," + String(solidColor.b); + } -String getCooling() { - return String(cooling); -} + String getCooling() { + return String(cooling); + } -String getSparking() { - return String(sparking); -} + String getSparking() { + return String(sparking); + } -String getSpeed() { - return String(speed); -} + String getSpeed() { + return String(speed); + } -String getTwinkleSpeed() { - return String(twinkleSpeed); -} + String getTwinkleSpeed() { + return String(twinkleSpeed); + } -String getTwinkleDensity() { - return String(twinkleDensity); -} + String getTwinkleDensity() { + return String(twinkleDensity); + } -String getCoolLikeIncandescent() { - return String(coolLikeIncandescent); -} + String getCoolLikeIncandescent() { + return String(coolLikeIncandescent); + } -String getName() { - return nameString; -} -String getUtcOffsetIndex() { - return String(utcOffsetIndex); -} + String getName() { + return nameString; + } -String setUtcOffsetIndex(uint8_t value) -{ - utcOffsetIndex = value; - if (utcOffsetIndex > 104) utcOffsetIndex = 104; - - const int32_t UTC_OFFSET_MINIMUM_MINUTES = ((int32_t)-12) * 60; // corresponds to index 0 - const int32_t UTC_OFFSET_INCREMENT_MINUTES = 15; // each higher index increments by this amount - - // minutes above the minimum - int32_t tmp = utcOffsetIndex * UTC_OFFSET_INCREMENT_MINUTES; - // add that to the minimum value - tmp = UTC_OFFSET_MINIMUM_MINUTES + tmp; - // convert to seconds - utcOffsetInSeconds = tmp * 60; + String getUtcOffsetIndex() { + return String(utcOffsetIndex); + } - Serial.print(F("utcOffsetIndex: ")); Serial.println(utcOffsetIndex); - Serial.print(F("utcOffsetInSeconds: ")); Serial.println(utcOffsetInSeconds); - timeClient.setTimeOffset(utcOffsetInSeconds); - writeAndCommitSettings(); - return String(utcOffsetIndex); -} + String setUtcOffsetIndexString(String value) + { + auto tmp = setUtcOffsetIndex(value.toInt()); + return String(tmp); + } -String setUtcOffsetIndexString(String value) -{ - return setUtcOffsetIndex(value.toInt()); -} + // Pride Playground fields -// Pride Playground fields + String getSaturationBpm() { + return String(saturationBpm); + } + String setSaturationBpm(String value) + { + saturationBpm = value.toInt(); + return value; + } -String getSaturationBpm() { - return String(saturationBpm); -} -String setSaturationBpm(String value) -{ - saturationBpm = value.toInt(); - return value; -} + String getSaturationMin() { + return String(saturationMin); + } + String setSaturationMin(String value) { + saturationMin = value.toInt(); + return value; + } -String getSaturationMin() { - return String(saturationMin); -} -String setSaturationMin(String value) { - saturationMin = value.toInt(); - return value; -} + String getSaturationMax() { + return String(saturationMax); + } + String setSaturationMax(String value) { + saturationMax = value.toInt(); + return value; + } -String getSaturationMax() { - return String(saturationMax); -} -String setSaturationMax(String value) { - saturationMax = value.toInt(); - return value; -} + String getBrightDepthBpm() { + return String(brightDepthBpm); + } + String setBrightDepthBpm(String value) { + brightDepthBpm = value.toInt(); + return value; + } -String getBrightDepthBpm() { - return String(brightDepthBpm); -} -String setBrightDepthBpm(String value) { - brightDepthBpm = value.toInt(); - return value; -} + String getBrightDepthMin() { + return String(brightDepthMin); + } + String setBrightDepthMin(String value) { + brightDepthMin = value.toInt(); + return value; + } -String getBrightDepthMin() { - return String(brightDepthMin); -} -String setBrightDepthMin(String value) { - brightDepthMin = value.toInt(); - return value; -} + String getBrightDepthMax() { + return String(brightDepthMax); + } + String setBrightDepthMax(String value) { + brightDepthMax = value.toInt(); + return value; + } -String getBrightDepthMax() { - return String(brightDepthMax); -} -String setBrightDepthMax(String value) { - brightDepthMax = value.toInt(); - return value; -} + String getBrightThetaIncBpm() { + return String(brightThetaIncBpm); + } + String setBrightThetaIncBpm(String value) { + brightThetaIncBpm = value.toInt(); + return value; + } -String getBrightThetaIncBpm() { - return String(brightThetaIncBpm); -} -String setBrightThetaIncBpm(String value) { - brightThetaIncBpm = value.toInt(); - return value; -} + String getBrightThetaIncMin() { + return String(brightThetaIncMin); + } + String setBrightThetaIncMin(String value) { + brightThetaIncMin = value.toInt(); + return value; + } -String getBrightThetaIncMin() { - return String(brightThetaIncMin); -} -String setBrightThetaIncMin(String value) { - brightThetaIncMin = value.toInt(); - return value; -} + String getBrightThetaIncMax() { + return String(brightThetaIncMax); + } + String setBrightThetaIncMax(String value) { + brightThetaIncMax = value.toInt(); + return value; + } -String getBrightThetaIncMax() { - return String(brightThetaIncMax); -} -String setBrightThetaIncMax(String value) { - brightThetaIncMax = value.toInt(); - return value; -} + String getMsMultiplierBpm() { + return String(msMultiplierBpm); + } + String setMsMultiplierBpm(String value) { + msMultiplierBpm = value.toInt(); + return value; + } -String getMsMultiplierBpm() { - return String(msMultiplierBpm); -} -String setMsMultiplierBpm(String value) { - msMultiplierBpm = value.toInt(); - return value; -} + String getMsMultiplierMin() { + return String(msMultiplierMin); + } + String setMsMultiplierMin(String value) { + msMultiplierMin = value.toInt(); + return value; + } -String getMsMultiplierMin() { - return String(msMultiplierMin); -} -String setMsMultiplierMin(String value) { - msMultiplierMin = value.toInt(); - return value; -} + String getMsMultiplierMax() { + return String(msMultiplierMax); + } + String setMsMultiplierMax(String value) { + msMultiplierMax = value.toInt(); + return value; + } -String getMsMultiplierMax() { - return String(msMultiplierMax); -} -String setMsMultiplierMax(String value) { - msMultiplierMax = value.toInt(); - return value; -} + String getHueIncBpm() { + return String(hueIncBpm); + } + String setHueIncBpm(String value) { + hueIncBpm = value.toInt(); + return value; + } -String getHueIncBpm() { - return String(hueIncBpm); -} -String setHueIncBpm(String value) { - hueIncBpm = value.toInt(); - return value; -} + String getHueIncMin() { + return String(hueIncMin); + } + String setHueIncMin(String value) { + hueIncMin = value.toInt(); + return value; + } -String getHueIncMin() { - return String(hueIncMin); -} -String setHueIncMin(String value) { - hueIncMin = value.toInt(); - return value; -} + String getHueIncMax() { + return String(hueIncMax); + } + String setHueIncMax(String value) { + hueIncMax = value.toInt(); + return value; + } -String getHueIncMax() { - return String(hueIncMax); -} -String setHueIncMax(String value) { - hueIncMax = value.toInt(); - return value; -} + String getSHueBpm() { + return String(sHueBpm); + } + String setSHueBpm(String value) { + sHueBpm = value.toInt(); + return value; + } -String getSHueBpm() { - return String(sHueBpm); -} -String setSHueBpm(String value) { - sHueBpm = value.toInt(); - return value; -} + String getSHueMin() { + return String(sHueMin); + } + String setSHueMin(String value) { + sHueMin = value.toInt(); + return value; + } -String getSHueMin() { - return String(sHueMin); -} -String setSHueMin(String value) { - sHueMin = value.toInt(); - return value; -} + String getSHueMax() { + return String(sHueMax); + } + String setSHueMax(String value) { + sHueMax = value.toInt(); + return value; + } -String getSHueMax() { - return String(sHueMax); -} -String setSHueMax(String value) { - sHueMax = value.toInt(); - return value; -} + } + inline namespace Options { + void getPatterns(JsonVariant dst) { + for (uint8_t i = 0; i < patternCount; i++) { + dst.add(patterns[i].name); + } + } -typedef String (*FieldSetter)(String); -typedef String (*FieldGetter)(); -struct Field { - String name; - String label; - String type; - uint8_t min; - uint8_t max; - FieldGetter getValue; - FieldGetter getOptions; - FieldSetter setValue; -}; - -// passing array reference works fine, but need to make the function a template -// to capture the array size... on the positive side, no need to pass `count` parameter -template -Field getField(String name, const Field (&fields)[N]) { - for (uint8_t i = 0; i < N; i++) { - Field field = fields[i]; - if (field.name == name) { - return field; + void getPalettes(JsonVariant dst) { + for (uint8_t i = 0; i < paletteCount; i++) { + dst.add(paletteNames[i]); + } } } - return Field(); -} -template -String getFieldValue(String name, const Field (&fields)[N]) { - Field field = getField(name, fields); - if (field.getValue) { - return field.getValue(); - } - return String(); -} -template -String setFieldValue(String name, String value, const Field (&fields)[N]) { - Field field = getField(name, fields); - if (field.setValue) { - return field.setValue(value); - } - return String(); -} -template -String getFieldsJson(const Field (&fields)[N]) { - String json = "["; - - for (uint8_t i = 0; i < N; i++) { - Field field = fields[i]; - json += "{\"name\":\"" + field.name + "\",\"label\":\"" + field.label + "\",\"type\":\"" + field.type + "\""; + typedef String (*FieldSetter)(String); + typedef String (*FieldGetter)(); + typedef void (*FieldOptions)(JsonVariant); + struct Field { + const String name; + const String label; + Field_t type; + uint8_t min; + uint8_t max; + FieldGetter getValue; + FieldOptions getOptions; + FieldSetter setValue; + }; + + void convertToJson(const Field& field, JsonVariant dst) { + if (field.name.length() == 0) { + return; + } + if (!IsValid(field.type)) { + return; + } - if(field.getValue) { - if (field.type == ColorFieldType || field.type == StringFieldType || field.type == LabelFieldType) { - json += ",\"value\":\"" + field.getValue() + "\""; - } - else { - json += ",\"value\":" + field.getValue(); + dst[F("name")] = field.name; + dst[F("label")] = field.label; + + dst[F("type")] = ToString(field.type); + // obj[F("readonly")] = (field.setValue == nullptr); + if (field.getValue != nullptr) { + switch (field.type) { + case Field_t::Color: // legacy ... comma-separated string of decimals values + case Field_t::String: + case Field_t::Label: + dst[F("value")] = field.getValue(); + break; + + case Field_t::Boolean: + case Field_t::Number: + case Field_t::Section: + case Field_t::Select: + case Field_t::UtcOffset: + dst[F("value")] = field.getValue().toInt(); // TODO: fix double-conversion + break; + + // intentionally no default ... causes compilation warning if new enum types added w/o updating here } } - - if (field.type == NumberFieldType || field.type == UtcOffsetIndexFieldType) { - json += ",\"min\":" + String(field.min); - json += ",\"max\":" + String(field.max); + if ((field.type == Field_t::Number) || (field.type == Field_t::UtcOffset)) { + dst[F("min")] = field.min; + dst[F("max")] = field.max; + } + if (field.getOptions != nullptr) { + JsonArray options = dst.createNestedArray(F("options")); + field.getOptions(options); } + return; + } - if (field.getOptions) { - json += ",\"options\":["; - json += field.getOptions(); - json += "]"; + // passing array reference works fine, but need to make the function a template + // to capture the array size... on the positive side, no need to pass `count` parameter + template + Field getField(String name, const Field (&fields)[N]) { + for (uint8_t i = 0; i < N; i++) { + Field field = fields[i]; + if (field.name == name) { + return field; + } + } + return Field(); + } + template + String getFieldValue(String name, const Field (&fields)[N]) { + Field field = getField(name, fields); + if (field.getValue) { + return field.getValue(); + } + return String(); + } + template + String setFieldValue(String name, String value, const Field (&fields)[N]) { + Field field = getField(name, fields); + if (field.setValue) { + return field.setValue(value); } + return String(); + } - json += "}"; + template + String getFieldsJson(const Field (&fields)[N]) { + // As of 2021-11-23, https://arduinojson.org/v6/assistant/ reports minimum 8041 bytes required (for Fib256). + // This depends upon three things: + // 1. the number of fields + // 2. the number of palettes (and their names) + // 3. the number of patterns (and their names) + DynamicJsonDocument jsonDoc(8192); + JsonArray array = jsonDoc.to(); // document is an array of fields + for (const Field& field : fields) { + array.add(field); + } + // that's all ... just serialize the result to String (or anything else...) + String result; + result.reserve(6*1024); // ~4939 for Fib256 ... pre-allocation is a performance optimization + serializeJson(jsonDoc, result); + return result; + } - if (i < N - 1) - json += ","; + // name, label, type, min, max, getValue, getOptions, setValue + // only items that use the 'getOptions': patterns and palettes + // only items that support 'setValue': options used for pridePlayground + const Field fields[] = { + {"name", "Name", Field_t::Label, 0, 0, getName, nullptr, nullptr}, + {"power", "Power", Field_t::Boolean, 0, 1, getPower, nullptr, nullptr}, + {"brightness", "Brightness", Field_t::Number, 1, 255, getBrightness, nullptr, nullptr}, + {"pattern", "Pattern", Field_t::Select, 0, patternCount, getPattern, getPatterns, nullptr}, + {"palette", "Palette", Field_t::Select, 0, paletteCount, getPalette, getPalettes, nullptr}, + {"speed", "Speed", Field_t::Number, 1, 255, getSpeed, nullptr, nullptr}, + + //-------------------------------------------------------------------------------------------------------- + {"autoplaySection", "Autoplay", Field_t::Section, 0, 0, nullptr, nullptr, nullptr}, + {"autoplay", "Autoplay", Field_t::Boolean, 0, 1, getAutoplay, nullptr, nullptr}, + {"autoplayDuration", "Autoplay Duration", Field_t::Number, 0, 255, getAutoplayDuration, nullptr, nullptr}, + + //-------------------------------------------------------------------------------------------------------- + {"clock", "Clock", Field_t::Section, 0, 0, nullptr, nullptr, nullptr}, + {"showClock", "Show Clock", Field_t::Boolean, 0, 1, getShowClock, nullptr, nullptr}, + {"clockBackgroundFade", "Background Fade", Field_t::Number, 0, 255, getClockBackgroundFade, nullptr, nullptr}, + {"utcOffsetIndex", "UTC Offset", Field_t::UtcOffset, 0, 104, getUtcOffsetIndex, nullptr, setUtcOffsetIndexString}, + + //-------------------------------------------------------------------------------------------------------- + {"solidColorSection", "Solid Color", Field_t::Section, 0, 0, nullptr, nullptr, nullptr}, + {"solidColor", "Color", Field_t::Color, 0, 255, getSolidColor, nullptr, nullptr}, + + //-------------------------------------------------------------------------------------------------------- + {"fireSection", "Fire & Water", Field_t::Section, 0, 0, nullptr, nullptr, nullptr}, + {"cooling", "Cooling", Field_t::Number, 0, 255, getCooling, nullptr, nullptr}, + {"sparking", "Sparking", Field_t::Number, 0, 255, getSparking, nullptr, nullptr}, + + //-------------------------------------------------------------------------------------------------------- + {"twinklesSection", "Twinkles", Field_t::Section, 0, 0, nullptr, nullptr, nullptr}, + {"twinkleSpeed", "Twinkle Speed", Field_t::Number, 0, 8, getTwinkleSpeed, nullptr, nullptr}, + {"twinkleDensity", "Twinkle Density", Field_t::Number, 0, 8, getTwinkleDensity, nullptr, nullptr}, + {"coolLikeIncandescent", "Incandescent Cool", Field_t::Boolean, 0, 1, getCoolLikeIncandescent, nullptr, nullptr}, + + //-------------------------------------------------------------------------------------------------------- + {"prideSection", "Pride Playground", Field_t::Section, 0, 0, nullptr, nullptr, nullptr }, + {"saturationBpm", "Saturation BPM", Field_t::Number, 0, 255, getSaturationBpm, nullptr, setSaturationBpm }, + {"saturationMin", "Saturation Min", Field_t::Number, 0, 255, getSaturationMin, nullptr, setSaturationMin }, + {"saturationMax", "Saturation Max", Field_t::Number, 0, 255, getSaturationMax, nullptr, setSaturationMax }, + {"brightDepthBpm", "Brightness Depth BPM", Field_t::Number, 0, 255, getBrightDepthBpm, nullptr, setBrightDepthBpm }, + {"brightDepthMin", "Brightness Depth Min", Field_t::Number, 0, 255, getBrightDepthMin, nullptr, setBrightDepthMin }, + {"brightDepthMax", "Brightness Depth Max", Field_t::Number, 0, 255, getBrightDepthMax, nullptr, setBrightDepthMax }, + {"brightThetaIncBpm", "Bright Theta Inc BPM", Field_t::Number, 0, 255, getBrightThetaIncBpm, nullptr, setBrightThetaIncBpm}, + {"brightThetaIncMin", "Bright Theta Inc Min", Field_t::Number, 0, 255, getBrightThetaIncMin, nullptr, setBrightThetaIncMin}, + {"brightThetaIncMax", "Bright Theta Inc Max", Field_t::Number, 0, 255, getBrightThetaIncMax, nullptr, setBrightThetaIncMax}, + {"msMultiplierBpm", "Time Multiplier BPM", Field_t::Number, 0, 255, getMsMultiplierBpm, nullptr, setMsMultiplierBpm }, + {"msMultiplierMin", "Time Multiplier Min", Field_t::Number, 0, 255, getMsMultiplierMin, nullptr, setMsMultiplierMin }, + {"msMultiplierMax", "Time Multiplier Max", Field_t::Number, 0, 255, getMsMultiplierMax, nullptr, setMsMultiplierMax }, + {"hueIncBpm", "Hue Inc BPM", Field_t::Number, 0, 255, getHueIncBpm, nullptr, setHueIncBpm }, + {"hueIncMin", "Hue Inc Min", Field_t::Number, 0, 255, getHueIncMin, nullptr, setHueIncMin }, + {"hueIncMax", "Hue Inc Max", Field_t::Number, 0, 255, getHueIncMax, nullptr, setHueIncMax }, + {"sHueBpm", "S Hue BPM", Field_t::Number, 0, 255, getSHueBpm, nullptr, setSHueBpm }, + {"sHueMin", "S Hue Min", Field_t::Number, 0, 255, getSHueMin, nullptr, setSHueMin }, + {"sHueMax", "S Hue Max", Field_t::Number, 0, 255, getSHueMax, nullptr, setSHueMax }, + }; + + Field getField(String name) { + return getField(name, fields); } - json += "]"; +} // end inline anonymous namespace + +// Everything else is exported from this file.... - return json; +void setShowClock(uint8_t value) +{ + showClock = value == 0 ? 0 : 1; + writeAndCommitSettings(); + broadcastInt("showClock", showClock); } +void setClockBackgroundFade(uint8_t value) +{ + clockBackgroundFade = value; + writeAndCommitSettings(); + broadcastInt("clockBackgroundFade", clockBackgroundFade); +} +uint8_t setUtcOffsetIndex(uint8_t value) { + utcOffsetIndex = value; + if (utcOffsetIndex > 104) utcOffsetIndex = 104; // 0 === -12 hours, 104 === +14 hours + + const int32_t UTC_OFFSET_MINIMUM_MINUTES = ((int32_t)-12) * 60; // corresponds to index 0 + const int32_t UTC_OFFSET_INCREMENT_MINUTES = 15; // each higher index increments by this amount + + // minutes above the minimum + int32_t tmp = utcOffsetIndex * UTC_OFFSET_INCREMENT_MINUTES; + // add that to the minimum value + tmp = UTC_OFFSET_MINIMUM_MINUTES + tmp; + // convert to seconds + utcOffsetInSeconds = tmp * 60; + + Serial.print(F("utcOffsetIndex: ")); Serial.println(utcOffsetIndex); + Serial.print(F("utcOffsetInSeconds: ")); Serial.println(utcOffsetInSeconds); + timeClient.setTimeOffset(utcOffsetInSeconds); + writeAndCommitSettings(); + return utcOffsetIndex; +} + + + +// The following simplify getting JSON to allow dynamic interaction via webpage scripts +uint8_t power = 1; +uint8_t brightness = brightnessMap[brightnessIndex]; -// name, label, type, min, max, getValue, getOptions, setValue -// only items that use the 'getOptions': patterns and palettes -// only items that support 'setValue': options used for pridePlayground -const Field fields[] = { - {"name", "Name", LabelFieldType, 0, 0, getName, nullptr, nullptr}, - - {"power", "Power", BooleanFieldType, 0, 1, getPower, nullptr, nullptr}, - {"brightness", "Brightness", NumberFieldType, 1, 255, getBrightness, nullptr, nullptr}, - {"pattern", "Pattern", SelectFieldType, 0, patternCount, getPattern, getPatterns, nullptr}, - {"palette", "Palette", SelectFieldType, 0, paletteCount, getPalette, getPalettes, nullptr}, - {"speed", "Speed", NumberFieldType, 1, 255, getSpeed, nullptr, nullptr}, - - //-------------------------------------------------------------------------------------------------------- - {"autoplaySection", "Autoplay", SectionFieldType, 0, 0, nullptr, nullptr, nullptr}, - {"autoplay", "Autoplay", BooleanFieldType, 0, 1, getAutoplay, nullptr, nullptr}, - {"autoplayDuration", "Autoplay Duration", NumberFieldType, 0, 255, getAutoplayDuration, nullptr, nullptr}, - - //-------------------------------------------------------------------------------------------------------- - {"clock", "Clock", SectionFieldType, 0, 0, nullptr, nullptr, nullptr}, - {"showClock", "Show Clock", BooleanFieldType, 0, 1, getShowClock, nullptr, nullptr}, - {"clockBackgroundFade", "Background Fade", NumberFieldType, 0, 255, getClockBackgroundFade, nullptr, nullptr}, - {"utcOffsetIndex", "UTC Offset", UtcOffsetIndexFieldType, 0, 104, getUtcOffsetIndex, nullptr, setUtcOffsetIndexString}, - - //-------------------------------------------------------------------------------------------------------- - {"solidColorSection", "Solid Color", SectionFieldType, 0, 0, nullptr, nullptr, nullptr}, - {"solidColor", "Color", ColorFieldType, 0, 255, getSolidColor, nullptr, nullptr}, - - //-------------------------------------------------------------------------------------------------------- - {"fireSection", "Fire & Water", SectionFieldType, 0, 0, nullptr, nullptr, nullptr}, - {"cooling", "Cooling", NumberFieldType, 0, 255, getCooling, nullptr, nullptr}, - {"sparking", "Sparking", NumberFieldType, 0, 255, getSparking, nullptr, nullptr}, - - //-------------------------------------------------------------------------------------------------------- - {"twinklesSection", "Twinkles", SectionFieldType, 0, 0, nullptr, nullptr, nullptr}, - {"twinkleSpeed", "Twinkle Speed", NumberFieldType, 0, 8, getTwinkleSpeed, nullptr, nullptr}, - {"twinkleDensity", "Twinkle Density", NumberFieldType, 0, 8, getTwinkleDensity, nullptr, nullptr}, - {"coolLikeIncandescent", "Incandescent Cool", BooleanFieldType, 0, 1, getCoolLikeIncandescent, nullptr, nullptr}, - - - //-------------------------------------------------------------------------------------------------------- - {"prideSection", "Pride Playground", SectionFieldType, 0, 0, nullptr, nullptr, nullptr}, - - {"saturationBpm", "Saturation BPM", NumberFieldType, 0, 255, getSaturationBpm, nullptr, setSaturationBpm}, - {"saturationMin", "Saturation Min", NumberFieldType, 0, 255, getSaturationMin, nullptr, setSaturationMin}, - {"saturationMax", "Saturation Max", NumberFieldType, 0, 255, getSaturationMax, nullptr, setSaturationMax}, - - {"brightDepthBpm", "Brightness Depth BPM", NumberFieldType, 0, 255, getBrightDepthBpm, nullptr, setBrightDepthBpm}, - {"brightDepthMin", "Brightness Depth Min", NumberFieldType, 0, 255, getBrightDepthMin, nullptr, setBrightDepthMin}, - {"brightDepthMax", "Brightness Depth Max", NumberFieldType, 0, 255, getBrightDepthMax, nullptr, setBrightDepthMax}, - - {"brightThetaIncBpm", "Bright Theta Inc BPM", NumberFieldType, 0, 255, getBrightThetaIncBpm, nullptr, setBrightThetaIncBpm}, - {"brightThetaIncMin", "Bright Theta Inc Min", NumberFieldType, 0, 255, getBrightThetaIncMin, nullptr, setBrightThetaIncMin}, - {"brightThetaIncMax", "Bright Theta Inc Max", NumberFieldType, 0, 255, getBrightThetaIncMax, nullptr, setBrightThetaIncMax}, - - {"msMultiplierBpm", "Time Multiplier BPM", NumberFieldType, 0, 255, getMsMultiplierBpm, nullptr, setMsMultiplierBpm}, - {"msMultiplierMin", "Time Multiplier Min", NumberFieldType, 0, 255, getMsMultiplierMin, nullptr, setMsMultiplierMin}, - {"msMultiplierMax", "Time Multiplier Max", NumberFieldType, 0, 255, getMsMultiplierMax, nullptr, setMsMultiplierMax}, - - {"hueIncBpm", "Hue Inc BPM", NumberFieldType, 0, 255, getHueIncBpm, nullptr, setHueIncBpm}, - {"hueIncMin", "Hue Inc Min", NumberFieldType, 0, 255, getHueIncMin, nullptr, setHueIncMin}, - {"hueIncMax", "Hue Inc Max", NumberFieldType, 0, 255, getHueIncMax, nullptr, setHueIncMax}, - - {"sHueBpm", "S Hue BPM", NumberFieldType, 0, 255, getSHueBpm, nullptr, setSHueBpm}, - {"sHueMin", "S Hue Min", NumberFieldType, 0, 255, getSHueMin, nullptr, setSHueMin}, - {"sHueMax", "S Hue Max", NumberFieldType, 0, 255, getSHueMax, nullptr, setSHueMax}, -}; - -const uint8_t fieldCount = ARRAY_SIZE2(fields); -// TODO: consider using ArduinoJSON ... pre-allocated buffer, simpler usage, tested code - -Field getField(String name) { - return getField(name, fields); +// getFieldsJson() lists all the options that the user can set, and is used by (at least) +// the built-in webserver to generate UI to adjust these options. +String getFieldsJson() { + return getFieldsJson(fields); } + +// getFieldValue() is used to get a current value for the String getFieldValue(String name) { return getFieldValue(name, fields); } String setFieldValue(String name, String value) { return setFieldValue(name, value, fields); } -String getFieldsJson() { - return getFieldsJson(fields); -} diff --git a/esp8266-fastled-webserver/Info.cpp b/esp8266-fastled-webserver/Info.cpp index badab026..5d11991f 100644 --- a/esp8266-fastled-webserver/Info.cpp +++ b/esp8266-fastled-webserver/Info.cpp @@ -12,38 +12,110 @@ String WiFi_SSID(bool persistent) { return String(reinterpret_cast(tmp)); } +#if 1 // This is documentation, useful to calculate minimum JsonDocument buffer size for "info" +// +// Note the use of a raw string literals below. This makes it easier to copy/paste into: +// https://arduinojson.org/v6/assistant/ +// +// uint32 max: 4294967295 ... but assistant requires ARDUINOJSON_USE_LONG_LONG +// if any value is >= 2,000,000,000 (which is odd, as that is +// not related to any power-of-two value...) +// uint32 mux: 1999999999 ... maximum value assistant allows w/o ARDUINOJSON_USE_LONG_LONG +// uint16 max: 65535 +// uint8 max: 255 +// boolean: "false" +// SSID: "0123456789ABCDEF0123456789ABCDEF" (spec limit 32 octets) +// macAddress: "00:11:22:33:44:55" +// IPv4: "255.255.255.255" +// IPv6: "0011:2233:4455:6677:8899:AABB:CCDD:EEFF" +// hostname: spec limit 65 octets ... (67 chars) [65+2 for string quotes] +// +// +// Any decent compiler will throw away this unreferenced data during optimization. +// +// This pretty-printed version takes 1031 characters, minified version takes 890 characters. +static const char MaximumLengthJson[] { R"RAW_STRING( +{ + "millis" : 4294967295, + "vcc" : 65535, + "wiFiChipId" : "FF22CC44", + "flashChipId" : "FF22CC44", + "flashChipSize" : 4294967295, + "flashChipRealSize" : 4294967295, + "sdkVersion" : "2.2.2-SOME_RANDOM_STRING_LENGTH", + "coreVersion" : "2.3.4-SOME_RANDOM_STRING", + "bootVersion" : 255, + "cpuFreqMHz" : 255, + "freeHeap" : 4294967295, + "sketchSize" : 4294967295, + "freeSketchSpace" : 4294967295, + "resetReason" : "Software/System restart", + "isConnected" : false, + "wiFiSsidDefault" : "0123456789ABCDEF0123456789ABCDEF", + "wiFiSSID" : "0123456789ABCDEF0123456789ABCDEF", + "localIP" : "255.255.255.255", + "gatewayIP" : "255.255.255.255", + "subnetMask" : "255.255.255.255", + "dnsIP" : "255.255.255.255", + "hostname" : "0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF", + "macAddress" : "00:11:22:33:44:55", + "autoConnect" : false, + "softAPSSID" : "0123456789ABCDEF0123456789ABCDEF", + "softAPIP" : "255.255.255.255", + "BSSID" : "00:11:22:33:44:55", + "softAPMacAddress" : "00:11:22:33:44:55" +} +)RAW_STRING" }; +static const char MinifiedMaximumLengthJson[] { R"RAW_STRING( +{"millis":2147483640,"vcc":65535,"wiFiChipId":"FF22CC44","flashChipId":"FF22CC44","flashChipSize":1073741823,"flashChipRealSize":1073741823,"sdkVersion":"2.2.2-SOME_RANDOM_STRING_LENGTH","coreVersion":"2.3.4-SOME_RANDOM_STRING","bootVersion":255,"cpuFreqMHz":255,"freeHeap":1073741823,"sketchSize":1073741823,"freeSketchSpace":1073741823,"resetReason":"Software/System restart","isConnected":false,"wiFiSsidDefault":"0123456789ABCDEF0123456789ABCDEF","wiFiSSID":"0123456789ABCDEF0123456789ABCDEF","localIP":"255.255.255.255","gatewayIP":"255.255.255.255","subnetMask":"255.255.255.255","dnsIP":"255.255.255.255","hostname":"0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF","macAddress":"00:11:22:33:44:55","autoConnect":false,"softAPSSID":"0123456789ABCDEF0123456789ABCDEF","softAPIP":"255.255.255.255","BSSID":"00:11:22:33:44:55","softAPMacAddress":"00:11:22:33:44:55"} +)RAW_STRING" }; +#endif +static const size_t infoJsonDocumentAllocationSize = 1024; + String getInfoJson() { - String json = "{"; - json += "\"millis\":" + (String)millis() + ","; - json += "\"vcc\":" + (String)ESP.getVcc() + ","; - json += "\"wiFiChipId\":\"" + String(WIFI_getChipId(), HEX) + "\","; - json += "\"flashChipId\":\"" + String(ESP.getFlashChipId(), HEX) + "\","; - json += "\"flashChipSize\":" + (String)ESP.getFlashChipSize() + ","; - json += "\"flashChipRealSize\":" + (String)ESP.getFlashChipRealSize() + ","; - json += "\"sdkVersion\":\"" + (String)system_get_sdk_version() + "\","; - json += "\"coreVersion\":\"" + (String)ESP.getCoreVersion() + "\","; - json += "\"bootVersion\":" + (String)system_get_boot_version() + ","; - json += "\"cpuFreqMHz\":" + (String)ESP.getCpuFreqMHz() + ","; - json += "\"freeHeap\":" + (String)ESP.getFreeHeap() + ","; - json += "\"sketchSize\":" + (String)ESP.getSketchSize() + ","; - json += "\"freeSketchSpace\":" + (String)ESP.getFreeSketchSpace() + ","; - json += "\"resetReason\":\"" + (String)ESP.getResetReason() + "\","; - json += "\"isConnected\":" + (WiFi.isConnected() ? String("true") : String("false")) + ","; - json += "\"wiFiSsidDefault\":\"" + (String)WiFi_SSID(true) + "\","; - json += "\"wiFiSSID\":\"" + (String)WiFi_SSID(false) + "\","; - json += "\"localIP\":\"" + WiFi.localIP().toString() + "\","; - json += "\"gatewayIP\":\"" + WiFi.gatewayIP().toString() + "\","; - json += "\"subnetMask\":\"" + WiFi.subnetMask().toString() + "\","; - json += "\"dnsIP\":\"" + WiFi.dnsIP().toString() + "\","; - json += "\"hostname\":\"" + WiFi.hostname() + "\","; - json += "\"macAddress\":\"" + WiFi.macAddress() + "\","; - json += "\"autoConnect\":" + (WiFi.getAutoConnect() ? String("true") : String("false")) + ","; - json += "\"softAPSSID\":\"" + WiFi.softAPSSID() + "\","; - json += "\"softAPIP\":\"" + WiFi.softAPIP().toString() + "\","; - json += "\"BSSID\":\"" + (String)WiFi.BSSIDstr() + "\","; - json += "\"softAPmacAddress\":\"" + (String)WiFi.softAPmacAddress() + "\""; - json += "}"; + DynamicJsonDocument jsonDoc(infoJsonDocumentAllocationSize); + jsonDoc[F("millis")] = millis(); // uint32_t (unsigned long) + jsonDoc[F("vcc")] = ESP.getVcc(); // uint16_t + jsonDoc[F("wiFiChipId")] = String(WIFI_getChipId(), HEX); // HEX(uint32_t) + jsonDoc[F("flashChipId")] = String(ESP.getFlashChipId(), HEX); // HEX(uint32_t) + jsonDoc[F("flashChipSize")] = ESP.getFlashChipSize(); // uint32_t + jsonDoc[F("flashChipRealSize")] = ESP.getFlashChipRealSize(); // uint32_t + jsonDoc[F("sdkVersion")] = system_get_sdk_version(); // String; example: "2.2.2-" + jsonDoc[F("coreVersion")] = ESP.getCoreVersion(); // String; example: "" + jsonDoc[F("bootVersion")] = system_get_boot_version(); // uint8_t + jsonDoc[F("cpuFreqMHz")] = ESP.getCpuFreqMHz(); // uint8_t + jsonDoc[F("freeHeap")] = ESP.getFreeHeap(); // uint32_t + jsonDoc[F("sketchSize")] = ESP.getSketchSize(); // uint32_t + jsonDoc[F("freeSketchSpace")] = ESP.getFreeSketchSpace(); // uint32_t + jsonDoc[F("resetReason")] = ESP.getResetReason(); // longest: "Software/System restart" + jsonDoc[F("isConnected")] = WiFi.isConnected(); // boolean + jsonDoc[F("wiFiSsidDefault")] = WiFi_SSID(true); // SSID + jsonDoc[F("wiFiSSID")] = WiFi_SSID(false); // SSID + jsonDoc[F("localIP")] = WiFi.localIP().toString(); // IPAddress (IPv4 only currently) + jsonDoc[F("gatewayIP")] = WiFi.gatewayIP().toString(); // IPAddress (IPv4 only currently) + jsonDoc[F("subnetMask")] = WiFi.subnetMask().toString(); // IPAddress (IPv4 only currently) + jsonDoc[F("dnsIP")] = WiFi.dnsIP().toString(); // IPAddress (IPv4 only currently) + jsonDoc[F("hostname")] = WiFi.hostname(); // hostname + jsonDoc[F("macAddress")] = WiFi.macAddress(); // macAddress + jsonDoc[F("autoConnect")] = WiFi.getAutoConnect(); // boolean + jsonDoc[F("softAPSSID")] = WiFi.softAPSSID(); // SSID + jsonDoc[F("softAPIP")] = WiFi.softAPIP().toString(); // IPv4 + jsonDoc[F("BSSID")] = WiFi.BSSIDstr(); // macAddress + jsonDoc[F("softAPMacAddress")] = WiFi.softAPmacAddress(); // macAddress + + // what to do if overflow the ArduinoJSON buffer? + if (jsonDoc.overflowed()) { + // NOTE: this should *NEVER* occur if the maximum string lengths were calculated properly + return String(F("{}")); + }; - return json; + // convert to String + String result; + // avoid heap fragmentation by pre-allocating 1023 bytes for result string. + // As of 2021-11-21, maximum 890 chars needed for minified result. + // As of 2021-11-21, maximum 1031 characters needed for prettified result. + result.reserve(1023); + serializeJson(jsonDoc, result); + return result; } diff --git a/esp8266-fastled-webserver/Map.cpp b/esp8266-fastled-webserver/Map.cpp index 29936ff3..16f590e6 100644 --- a/esp8266-fastled-webserver/Map.cpp +++ b/esp8266-fastled-webserver/Map.cpp @@ -59,7 +59,7 @@ const uint8_t coordsX[NUM_PIXELS] { 170, 178, 185, 191, 196, 199, 202, 202, 202, 200, 196, 191, 184, 175, 161, 132, 148, 166, 176, 183, 188, 190, 192, 191, 190, 186, 182, 176, 170, 162, 153, 125, 136, 145, 154, 161, 168, 173, 177, 180, 181, 181, 178, 174, 167, 155, 139, 157, 164, 169, 170, 170, 168, 165, 160, 154, 147, 139, 129, 119, 109, 82, 93, 104, 114, 124, 132, 140, 147, 152, 157, 159, 160, 159, 155, 145, 145, 150, 150, 149, 146, 141, 135, 127, 119, 110, 100, 90, 79, 68, 57, 45, 56, 66, 77, 87, 97, 107, 115, 123, 130, 135, 139, 142, 141, 136, 134, 134, 131, 126, 120, 113, 105, 96, 86, 76, 66, 56, 45, 35, 25, 18, 27, 37, 47, 57, 67, 77, 86, 95, 104, 112, 119, 124, 128, 129, 125, 120, 113, 105, 97, 88, 78, 69, 59, 50, 40, 31, 22, 14, 6, 0, 6, 12, 19, 27, 35, 44, 54, 63, 73, 82, 91, 101, 109, 118, 121, 108, 98, 88, 78, 69, 59, 50, 41, 33, 25, 18, 12, 7, 3, 6, 8, 11, 15, 20, 26, 33, 40, 48, 57, 67, 77, 87, 98, 111, 102, 89, 77, 67, 58, 49, 41, 34, 28, 23, 19, 16, 15, 14, 15, 26, 24, 23, 24, 26, 28, 33, 38, 44, 51, 60, 69, 80, 93, 111, 100, 85, 73, 64, 55, 49, 43, 39, 36, 34, 33, 34, 36, 39, 43, 58, 53, 49, 45, 44, 43, 44, 46, 50, 55, 61, 69, 79, 93, 116, 104, 87, 76, 68, 62, 58, 55, 54, 54, 55, 58, 62, 68, 74, 82, 108, 99, 90, 83, 77, 72, 68, 65, 64, 65, 67, 70, 76, 85, 97, 112, 94, 85, 80, 76, 75, 75, 77, 81, 85, 91, 98, 106, 115, 125, 153, 142, 131, 122, 113, 105, 99, 93, 89, 87, 85, 86, 89, 95, 106, 123, 105, 99, 96, 96, 98, 101, 106, 112, 119, 128, 137, 147, 157, 168, 180, 193, 182, 171, 161, 151, 141, 133, 125, 118, 113, 108, 106, 106, 108, 115, 116, 114, 115, 118, 123, 129, 137, 145, 154, 164, 174, 184, 195, 206, 216, 226, 216, 205, 195, 185, 175, 165, 156, 147, 139, 133, 127, 123, 121, 124, 127, 129, 134, 141, 148, 156, 165, 174, 184, 194, 204, 214, 224, 233, 242, 253, 246, 238, 229, 221, 211, 202, 192, 182, 173, 164, 155, 147, 140, 133, 134, 142, 151, 160, 169, 179, 188, 198, 207, 216, 225, 233, 241, 247, 253, 255, 251, 247, 241, 234, 227, 219, 210, 201, 192, 182, 173, 163, 153, 142, 134, 151, 163, 174, 184, 194, 203, 212, 220, 227, 233, 239, 243, 247, 249, 242, 242, 241, 238, 235, 230, 225, 218, 211, 203, 194, 184, 173, 161, 146, 155, 170, 182, 192, 201, 208, 215, 221, 226, 229, 231, 232, 232, 231, 228, 215, 219, 221, 223, 223, 221, 219, 215, 210, 204, 197, 188, 177, 164, 144, 154, 170, 182, 191, 198, 204, 208, 211, 212, 213, 212, 209, 205, 200, 194 }; const uint8_t coordsY[NUM_PIXELS] { 6, 14, 22, 31, 41, 50, 60, 70, 80, 89, 98, 106, 114, 120, 125, 128, 124, 116, 109, 101, 92, 82, 73, 63, 54, 44, 35, 27, 18, 11, 4, 0, 5, 10, 17, 24, 32, 40, 49, 59, 68, 78, 87, 97, 106, 116, 121, 106, 95, 85, 75, 65, 56, 47, 38, 31, 24, 17, 12, 7, 4, 10, 10, 13, 16, 20, 25, 31, 39, 46, 55, 64, 74, 85, 96, 111, 101, 87, 76, 65, 56, 48, 41, 34, 29, 25, 21, 20, 19, 19, 21, 33, 30, 29, 28, 29, 31, 34, 39, 45, 51, 59, 69, 79, 92, 111, 101, 85, 73, 64, 56, 50, 45, 41, 39, 38, 38, 40, 43, 47, 52, 68, 62, 56, 53, 50, 49, 49, 50, 53, 57, 63, 70, 80, 94, 119, 106, 89, 78, 70, 65, 61, 59, 59, 60, 62, 66, 71, 77, 84, 92, 119, 109, 100, 92, 85, 80, 75, 72, 70, 70, 71, 74, 79, 87, 99, 114, 97, 88, 83, 81, 80, 81, 84, 88, 94, 100, 108, 116, 126, 136, 163, 152, 142, 132, 123, 114, 107, 101, 96, 93, 91, 91, 93, 98, 108, 108, 102, 101, 101, 104, 108, 114, 120, 128, 137, 146, 157, 167, 178, 190, 203, 192, 181, 170, 160, 150, 141, 133, 126, 119, 115, 111, 110, 111, 118, 119, 118, 120, 124, 130, 136, 144, 153, 162, 172, 182, 193, 203, 214, 224, 232, 223, 213, 202, 192, 182, 172, 163, 154, 146, 139, 132, 128, 125, 126, 129, 133, 139, 146, 154, 162, 172, 181, 191, 201, 210, 220, 229, 238, 246, 255, 249, 242, 234, 225, 216, 207, 197, 188, 178, 169, 160, 151, 143, 136, 135, 145, 155, 164, 174, 183, 193, 202, 211, 220, 228, 236, 243, 249, 254, 253, 250, 246, 242, 236, 229, 222, 213, 205, 196, 186, 176, 166, 155, 143, 133, 153, 166, 177, 187, 196, 205, 214, 221, 228, 234, 238, 242, 245, 246, 246, 236, 237, 237, 236, 233, 229, 225, 219, 212, 204, 195, 186, 175, 162, 146, 156, 171, 182, 192, 201, 208, 214, 220, 223, 226, 228, 228, 227, 224, 221, 207, 211, 215, 217, 218, 218, 216, 213, 209, 203, 196, 187, 177, 164, 142, 153, 170, 181, 190, 196, 201, 205, 207, 208, 207, 205, 202, 197, 191, 184, 159, 168, 176, 182, 188, 192, 195, 197, 197, 196, 193, 189, 182, 173, 160, 146, 164, 173, 180, 184, 186, 186, 185, 183, 179, 174, 167, 160, 151, 142, 114, 125, 135, 144, 152, 159, 165, 170, 173, 175, 176, 174, 171, 164, 153, 136, 154, 161, 164, 165, 164, 162, 157, 152, 145, 138, 129, 119, 109, 98, 72, 83, 94, 105, 114, 124, 132, 139, 145, 150, 153, 155, 155, 152, 143, 142, 146, 146, 143, 139, 134, 127, 119, 110, 101, 91, 81, 70, 59, 48, 37, 48, 58, 69, 79, 89, 99, 108, 116, 123, 129, 134, 137, 138, 134, 132, 130, 126, 121, 114, 106, 98, 88, 79, 69, 59, 48, 38, 29, 19 }; const uint8_t angles[NUM_PIXELS] { 205, 208, 211, 215, 218, 221, 225, 228, 231, 235, 238, 242, 245, 248, 252, 255, 246, 243, 239, 236, 233, 229, 226, 223, 219, 216, 213, 209, 206, 203, 199, 190, 194, 197, 200, 204, 207, 211, 214, 217, 221, 224, 227, 231, 234, 237, 232, 229, 225, 222, 219, 215, 212, 208, 205, 202, 198, 195, 192, 188, 185, 176, 180, 183, 186, 190, 193, 196, 200, 203, 206, 210, 213, 216, 220, 223, 214, 211, 208, 204, 201, 198, 194, 191, 188, 184, 181, 177, 174, 171, 167, 162, 165, 169, 172, 175, 179, 182, 185, 189, 192, 196, 199, 202, 206, 209, 200, 197, 193, 190, 187, 183, 180, 177, 173, 170, 167, 163, 160, 157, 153, 148, 151, 154, 158, 161, 165, 168, 171, 175, 178, 181, 185, 188, 191, 195, 186, 183, 179, 176, 173, 169, 166, 162, 159, 156, 152, 149, 146, 142, 139, 130, 133, 137, 140, 144, 147, 150, 154, 157, 160, 164, 167, 170, 174, 177, 172, 168, 165, 162, 158, 155, 152, 148, 145, 141, 138, 135, 131, 128, 125, 116, 119, 123, 126, 129, 133, 136, 139, 143, 146, 149, 153, 156, 160, 163, 154, 151, 147, 144, 141, 137, 134, 131, 127, 124, 121, 117, 114, 110, 107, 102, 105, 108, 112, 115, 118, 122, 125, 129, 132, 135, 139, 142, 145, 149, 140, 137, 133, 130, 126, 123, 120, 116, 113, 110, 106, 103, 100, 96, 93, 87, 91, 94, 98, 101, 104, 108, 111, 114, 118, 121, 124, 128, 131, 134, 126, 122, 119, 116, 112, 109, 106, 102, 99, 95, 92, 89, 85, 82, 79, 70, 73, 77, 80, 83, 87, 90, 93, 97, 100, 103, 107, 110, 114, 117, 111, 108, 105, 101, 98, 95, 91, 88, 85, 81, 78, 74, 71, 68, 64, 56, 59, 62, 66, 69, 72, 76, 79, 82, 86, 89, 93, 96, 99, 103, 97, 94, 90, 87, 84, 80, 77, 74, 70, 67, 64, 60, 57, 54, 50, 47, 41, 45, 48, 51, 55, 58, 62, 65, 68, 72, 75, 78, 82, 85, 88, 80, 76, 73, 70, 66, 63, 59, 56, 53, 49, 46, 43, 39, 36, 33, 27, 31, 34, 37, 41, 44, 47, 51, 54, 57, 61, 64, 67, 71, 74, 65, 62, 59, 55, 52, 49, 45, 42, 39, 35, 32, 28, 25, 22, 18, 10, 13, 16, 20, 23, 26, 30, 33, 36, 40, 43, 47, 50, 53, 57, 51, 48, 44, 41, 38, 34, 31, 28, 24, 21, 18, 14, 11, 8, 4, 251, 254, 2, 5, 9, 12, 16, 19, 22, 26, 29, 32, 36, 39, 42, 37, 34, 30, 27, 24, 20, 17, 13, 10, 7, 3, 0, 252, 249, 245, 236, 240, 243, 247, 250, 253, 1, 5, 8, 11, 15, 18, 21, 25, 28, 19, 16, 13, 9, 6, 3, 255, 251, 248, 244, 241, 238, 234, 231, 228, 222, 226, 229, 232, 236, 239, 242, 246, 249, 252, 0, 4, 7, 11, 14, 5, 2, 254, 250, 247, 244, 240, 237, 234, 230, 227, 223, 220, 217, 213 }; - static const auto (&radiusProxy)[NUM_PIXELS] = physicalToFibonacci; + static const uint16_t (&radiusProxy)[NUM_PIXELS] = physicalToFibonacci; static const uint8_t RADII_SCALE_DIVISOR { 2 }; static const uint8_t RADII_SCALE_MULTIPLIER { 1 }; #elif defined(PRODUCT_FIBONACCI256) @@ -68,7 +68,7 @@ const uint8_t coordsX[NUM_PIXELS] { 133, 156, 165, 168, 165, 158, 147, 132, 114, 95, 76, 57, 41, 28, 19, 15, 17, 24, 37, 56, 123, 96, 73, 53, 38, 28, 24, 25, 31, 41, 55, 71, 89, 106, 122, 136, 146, 152, 152, 143, 138, 136, 128, 115, 101, 85, 70, 56, 44, 37, 33, 34, 41, 53, 69, 90, 114, 140, 167, 226, 204, 180, 154, 129, 106, 85, 67, 54, 46, 43, 44, 50, 60, 72, 86, 100, 113, 123, 128, 117, 104, 90, 78, 67, 59, 54, 54, 59, 68, 82, 100, 121, 143, 167, 191, 212, 231, 246, 255, 251, 251, 245, 233, 218, 199, 178, 156, 134, 114, 96, 82, 73, 67, 66, 70, 78, 89, 103, 111, 94, 84, 80, 81, 86, 96, 109, 126, 145, 165, 185, 204, 220, 233, 241, 244, 241, 232, 217, 179, 201, 217, 229, 235, 235, 230, 220, 207, 190, 172, 154, 136, 121, 108, 99, 95, 96, 104, 120, 110, 111, 118, 130, 144, 160, 176, 192, 206, 217, 224, 227, 224, 216, 202, 184, 162, 137, 110, 44, 68, 94, 120, 145, 168, 187, 202, 212, 216, 216, 212, 203, 191, 177, 162, 148, 135, 126, 122, 136, 147, 161, 174, 186, 197, 204, 206, 205, 198, 187, 172, 152, 130, 106, 81, 58, 36, 17, 0, 5, 15, 30, 49, 71, 93, 116, 138, 157, 173, 185, 192, 195, 193, 187, 178, 166, 152, 137, 149, 164, 175, 180, 182, 179, 171, 159, 143, 125, 105, 83, 63, 44, 28, 16, 9, 7, 12, 23 }; const uint8_t coordsY[NUM_PIXELS] { 126, 120, 109, 96, 82, 69, 57, 49, 45, 45, 50, 59, 74, 92, 114, 138, 163, 188, 211, 231, 255, 248, 235, 218, 198, 175, 152, 129, 107, 89, 74, 63, 57, 56, 59, 66, 76, 88, 102, 116, 103, 88, 77, 71, 68, 70, 77, 88, 103, 121, 141, 163, 184, 205, 222, 236, 245, 249, 247, 208, 224, 235, 241, 240, 234, 223, 209, 191, 172, 152, 132, 115, 101, 90, 84, 82, 86, 95, 114, 107, 98, 98, 103, 112, 126, 142, 159, 177, 195, 210, 222, 230, 233, 230, 223, 209, 191, 168, 142, 98, 125, 151, 174, 194, 209, 219, 223, 223, 218, 208, 195, 180, 164, 148, 134, 122, 114, 112, 123, 128, 138, 151, 165, 180, 193, 203, 211, 214, 212, 206, 194, 178, 158, 134, 109, 83, 58, 35, 11, 28, 48, 71, 95, 120, 142, 163, 179, 192, 200, 203, 202, 196, 187, 175, 162, 148, 136, 133, 152, 166, 177, 186, 190, 191, 187, 178, 165, 148, 128, 107, 84, 62, 41, 24, 11, 2, 0, 28, 16, 9, 8, 13, 23, 37, 55, 75, 96, 116, 135, 151, 164, 173, 177, 177, 172, 162, 146, 153, 161, 163, 160, 152, 139, 124, 106, 87, 69, 51, 36, 25, 18, 16, 20, 29, 44, 64, 133, 106, 81, 60, 44, 32, 26, 25, 29, 38, 50, 65, 82, 99, 115, 129, 140, 147, 148, 138, 134, 131, 122, 110, 95, 80, 65, 52, 42, 36, 34, 37, 45, 59, 77, 98, 123, 149, 176, 202 }; const uint8_t angles[NUM_PIXELS] { 0, 247, 238, 229, 220, 211, 203, 194, 185, 176, 167, 159, 150, 141, 132, 123, 115, 106, 97, 88, 65, 74, 83, 92, 100, 109, 118, 127, 136, 144, 153, 162, 171, 180, 188, 197, 206, 215, 224, 232, 209, 201, 192, 183, 174, 165, 157, 148, 139, 130, 121, 113, 104, 95, 86, 77, 69, 60, 51, 28, 37, 46, 54, 63, 72, 81, 90, 98, 107, 116, 125, 134, 142, 151, 160, 169, 178, 186, 195, 172, 163, 155, 146, 137, 128, 119, 111, 102, 93, 84, 75, 67, 58, 49, 40, 31, 23, 14, 5, 246, 255, 8, 17, 26, 35, 44, 52, 61, 70, 79, 88, 96, 105, 114, 123, 132, 140, 149, 135, 126, 117, 108, 100, 91, 82, 73, 64, 56, 47, 38, 29, 20, 12, 3, 250, 241, 232, 223, 209, 218, 227, 235, 244, 253, 6, 15, 24, 33, 41, 50, 59, 68, 77, 85, 94, 103, 112, 98, 89, 80, 71, 62, 54, 45, 36, 27, 18, 10, 1, 247, 239, 230, 221, 212, 203, 195, 186, 163, 172, 180, 189, 198, 207, 216, 224, 233, 242, 251, 4, 13, 22, 31, 39, 48, 57, 66, 75, 52, 43, 34, 25, 16, 8, 254, 245, 237, 228, 219, 210, 201, 193, 184, 175, 166, 157, 149, 126, 134, 143, 152, 161, 170, 178, 187, 196, 205, 214, 222, 231, 240, 249, 2, 11, 20, 28, 37, 14, 5, 252, 243, 235, 226, 217, 208, 199, 191, 182, 173, 164, 155, 147, 138, 129, 120, 111, 103 }; - static const auto (&radiusProxy)[NUM_PIXELS] = physicalToFibonacci; + static const uint8_t (&radiusProxy)[NUM_PIXELS] = physicalToFibonacci; static const uint8_t RADII_SCALE_DIVISOR { 1 }; static const uint8_t RADII_SCALE_MULTIPLIER { 1 }; #elif defined(PRODUCT_FIBONACCI128) @@ -77,7 +77,7 @@ const uint8_t coordsX[NUM_PIXELS] { 137, 170, 182, 186, 182, 172, 156, 135, 110, 83, 26, 49, 74, 99, 121, 141, 155, 164, 164, 151, 145, 141, 129, 112, 91, 69, 47, 27, 11, 0, 9, 11, 19, 33, 50, 70, 90, 108, 123, 130, 114, 96, 77, 58, 43, 32, 25, 25, 32, 45, 85, 65, 51, 43, 42, 47, 58, 74, 94, 105, 81, 68, 62, 63, 70, 84, 103, 127, 154, 218, 193, 167, 142, 119, 101, 89, 83, 84, 96, 118, 104, 106, 116, 132, 152, 175, 198, 221, 241, 255, 248, 236, 219, 199, 178, 158, 140, 126, 121, 140, 157, 176, 195, 213, 227, 237, 241, 239, 193, 210, 221, 225, 222, 214, 201, 184, 164, 142, 160, 181, 196, 204, 206, 202, 191, 174, 152, 125 }; const uint8_t coordsY[NUM_PIXELS] { 130, 121, 105, 86, 66, 47, 31, 19, 13, 13, 55, 39, 30, 28, 33, 43, 57, 75, 95, 115, 96, 75, 60, 50, 46, 49, 59, 74, 96, 122, 166, 139, 114, 93, 78, 69, 67, 72, 85, 112, 102, 90, 89, 96, 110, 129, 152, 177, 203, 227, 247, 228, 207, 184, 161, 140, 123, 112, 109, 126, 132, 146, 165, 185, 206, 225, 240, 251, 255, 224, 235, 240, 237, 229, 216, 199, 181, 161, 143, 139, 166, 186, 202, 215, 221, 222, 216, 204, 185, 115, 142, 166, 184, 197, 203, 202, 195, 181, 158, 167, 180, 182, 178, 166, 148, 126, 101, 74, 21, 42, 66, 91, 114, 134, 150, 159, 160, 147, 141, 137, 124, 106, 85, 63, 42, 24, 9, 0 }; const uint8_t angles[NUM_PIXELS] { 0, 247, 239, 230, 221, 212, 203, 194, 186, 177, 154, 163, 171, 180, 189, 198, 207, 216, 224, 233, 210, 201, 192, 184, 175, 166, 157, 148, 139, 131, 116, 125, 134, 143, 152, 160, 169, 178, 187, 196, 173, 164, 155, 146, 137, 129, 120, 111, 102, 93, 79, 88, 97, 105, 114, 123, 132, 141, 150, 135, 126, 118, 109, 100, 91, 82, 73, 65, 56, 33, 42, 50, 59, 68, 77, 86, 95, 103, 112, 98, 89, 80, 71, 63, 54, 45, 36, 27, 18, 252, 4, 13, 22, 31, 39, 48, 57, 66, 75, 52, 43, 34, 25, 16, 8, 255, 246, 237, 214, 223, 232, 241, 250, 2, 11, 20, 29, 37, 14, 5, 253, 244, 235, 226, 218, 209, 200, 191 }; - static const auto (&radiusProxy)[NUM_PIXELS] = physicalToFibonacci; + static const uint8_t (&radiusProxy)[NUM_PIXELS] = physicalToFibonacci; static const uint8_t RADII_SCALE_DIVISOR { 1 }; static const uint8_t RADII_SCALE_MULTIPLIER { 2 }; #elif defined(PRODUCT_FIBONACCI64_FULL) || defined(PRODUCT_FIBONACCI64_MINI) || defined(PRODUCT_FIBONACCI64_MICRO) || defined(PRODUCT_FIBONACCI64_NANO) @@ -86,7 +86,7 @@ const uint8_t coordsX[NUM_PIXELS] { 140, 189, 208, 214, 208, 146, 168, 180, 180, 162, 152, 146, 129, 103, 72, 40, 70, 97, 120, 131, 107, 79, 50, 23, 0, 7, 23, 46, 76, 93, 57, 37, 28, 29, 87, 68, 59, 62, 80, 113, 91, 94, 109, 133, 202, 172, 145, 125, 117, 145, 170, 198, 227, 253, 255, 235, 210, 181, 148, 175, 207, 228, 240, 244 }; const uint8_t coordsY[NUM_PIXELS] { 128, 114, 91, 63, 34, 0, 21, 48, 76, 106, 78, 47, 25, 11, 5, 38, 35, 42, 61, 101, 87, 69, 68, 78, 98, 143, 118, 102, 98, 122, 131, 152, 179, 209, 255, 230, 202, 174, 148, 142, 181, 210, 235, 252, 235, 234, 224, 203, 170, 183, 201, 205, 198, 181, 134, 157, 171, 173, 153, 145, 138, 120, 93, 63 }; const uint8_t angles[NUM_PIXELS] { 0, 249, 241, 232, 223, 200, 208, 217, 226, 235, 212, 203, 194, 185, 176, 162, 171, 180, 188, 197, 174, 165, 156, 147, 139, 124, 133, 142, 151, 136, 128, 119, 110, 101, 78, 86, 95, 104, 113, 99, 90, 81, 72, 63, 40, 49, 58, 67, 75, 52, 43, 34, 25, 17, 2, 11, 20, 29, 38, 14, 6, 255, 246, 237 }; - static const auto (&radiusProxy)[NUM_PIXELS] = physicalToFibonacci; + static const uint8_t (&radiusProxy)[NUM_PIXELS] = physicalToFibonacci; static const uint8_t RADII_SCALE_DIVISOR { 1 }; static const uint8_t RADII_SCALE_MULTIPLIER { 4 }; #elif defined(PRODUCT_FIBONACCI32) @@ -95,7 +95,7 @@ const uint8_t coordsX[NUM_PIXELS] { 152, 224, 252, 210, 211, 184, 169, 161, 89, 121, 138, 102, 61, 19, 13, 57, 82, 29, 0, 36, 63, 111, 79, 83, 158, 129, 118, 160, 196, 255, 212, 163, 203, 250 }; const uint8_t coordsY[NUM_PIXELS] { 120, 101, 69, 7, 48, 90, 50, 7, 0, 27, 83, 62, 37, 35, 84, 78, 112, 125, 154, 185, 149, 140, 195, 236, 255, 226, 179, 198, 223, 181, 183, 156, 144, 135 }; const uint8_t angles[NUM_PIXELS] { 255, 246, 237, 214, 223, 232, 208, 199, 176, 185, 193, 170, 161, 152, 138, 147, 132, 123, 114, 100, 108, 94, 85, 76, 53, 62, 70, 47, 38, 15, 23, 32, 9, 0 }; - static const auto (&radiusProxy)[NUM_PIXELS] = physicalToFibonacci; + static const uint8_t (&radiusProxy)[NUM_PIXELS] = physicalToFibonacci; static const uint8_t RADII_SCALE_DIVISOR { 1 }; static const uint8_t RADII_SCALE_MULTIPLIER { 8 }; #elif defined(PRODUCT_KRAKEN64) @@ -104,7 +104,7 @@ const uint8_t angles[NUM_PIXELS] { 0, 249, 241, 232, 223, 200, 208, 217, 226, 235, 212, 203, 194, 185, 176, 162, 171, 180, 188, 197, 174, 165, 156, 147, 139, 124, 133, 142, 151, 136, 128, 119, 110, 101, 78, 86, 95, 104, 113, 99, 90, 81, 72, 63, 40, 49, 58, 67, 75, 52, 43, 34, 25, 17, 2, 11, 20, 29, 38, 14, 6, 255, 246, 237 }; const uint8_t body[NUM_PIXELS] { 0, 16, 32, 48, 64, 80, 96, 112, 128, 143, 159, 175, 191, 207, 223, 239, 255, 143, 159, 175, 191, 207, 223, 239, 255, 143, 159, 175, 191, 207, 223, 239, 255, 143, 159, 175, 191, 207, 223, 239, 143, 159, 175, 191, 207, 223, 239, 255, 143, 159, 175, 191, 207, 223, 239, 255, 143, 159, 175, 191, 207, 223, 239, 255 }; static_assert(NUM_PIXELS == ARRAY_SIZE2(body), ""); - static const auto (&radiusProxy)[NUM_PIXELS] = body; + static const uint8_t (&radiusProxy)[NUM_PIXELS] = body; static const uint8_t RADII_SCALE_DIVISOR { 1 }; // body[] values are already in range [0..255] static const uint8_t RADII_SCALE_MULTIPLIER { 1 }; // body[] values are already in range [0..255] // For reference purposes... @@ -139,7 +139,7 @@ 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }; - static const auto (&radiusProxy)[NUM_PIXELS] = radii; + static const uint8_t (&radiusProxy)[NUM_PIXELS] = radii; static const uint8_t RADII_SCALE_DIVISOR { 1 }; // radii[] values are already in range [0..255] static const uint8_t RADII_SCALE_MULTIPLIER { 1 }; // radii[] values are already in range [0..255] diff --git a/esp8266-fastled-webserver/Noise.cpp b/esp8266-fastled-webserver/Noise.cpp index 69d03772..7b29452b 100644 --- a/esp8266-fastled-webserver/Noise.cpp +++ b/esp8266-fastled-webserver/Noise.cpp @@ -146,7 +146,8 @@ void drawNoise(CRGBPalette16 palette, uint8_t hueReduce = 0) } #if HAS_POLAR_COORDS // change to "HAS_CONCENTRIC_RINGS" ? -// drawPolarNoise() uses angles[] and rings[][] (may move to using radii[]) + +// drawPolarNoise() uses angles[] and radii[] void drawPolarNoise(CRGBPalette16 palette, uint8_t hueReduce = 0) { for (uint16_t i = 0; i < NUM_PIXELS; i++) { diff --git a/esp8266-fastled-webserver/common.h b/esp8266-fastled-webserver/common.h index 83f14898..af545caf 100644 --- a/esp8266-fastled-webserver/common.h +++ b/esp8266-fastled-webserver/common.h @@ -27,6 +27,10 @@ #include "config.h" #if 1 // external libraries + #define ARDUINOJSON_DECODE_UNICODE 0 // don't need to decode Unicode to UTF-8 + #define ARDUINOJSON_USE_LONG_LONG 1 // storing uint32_t requires this flag + #include "ArduinoJson.h" + #define FASTLED_INTERNAL // no other way to suppress build warnings #include FASTLED_USING_NAMESPACE @@ -128,7 +132,6 @@ void broadcastString(String name, String value); // Structures typedef void (*Pattern)(); -typedef Pattern PatternList[]; typedef struct { Pattern pattern; String name; @@ -199,7 +202,6 @@ extern NTPClient timeClient; extern String nameString; extern int utcOffsetInSeconds; extern uint8_t utcOffsetIndex; -extern String setUtcOffsetIndex(uint8_t value); extern CRGB leds[NUM_PIXELS]; diff --git a/esp8266-fastled-webserver/esp8266-fastled-webserver.ino b/esp8266-fastled-webserver/esp8266-fastled-webserver.ino index 5748cff7..2cf4e6f4 100644 --- a/esp8266-fastled-webserver/esp8266-fastled-webserver.ino +++ b/esp8266-fastled-webserver/esp8266-fastled-webserver.ino @@ -90,45 +90,30 @@ void dimAll(byte value) // List of patterns to cycle through. Each is defined as a separate function below. // NOTE: HAS_POLAR_COORDS implies HAS_COORDINATE_MAP -// IS_FIBONACCI implies HAS_COORDINATE_MAP - -// TODO: Consider patterns listing name and all variants: -// [] original variant -// [] fibonacci variant (or nullptr) -// [] concentric ring variant (or nullptr) -// [] coordinates variant (or nullptr) -// WHY #1: Easier to manage defining the patterns via a macro, -// which discards arguments that do not apply for a given -// board, than this #if/#endif spaghetti mess. -// WHY #2: Easier for users to see correlations between the patterns, -// when switching between them. -// WHY #3: May eventually be able to change mapping for standard -// effects (emulating led array, but using custom mapping), -// which could further reduce code duplication. +// IS_FIBONACCI implies HAS_COORDINATE_MAP const PatternAndName patterns[] = { - { pride, "Pride" }, + { pride, "Pride" }, #if IS_FIBONACCI - { prideFibonacci, "Pride Fibonacci" }, + { prideFibonacci, "Pride Fibonacci" }, #endif - { colorWaves, "Color Waves" }, + { colorWaves, "Color Waves" }, #if IS_FIBONACCI { colorWavesFibonacci, "Color Waves Fibonacci" }, #endif -#if IS_FIBONACCI { pridePlayground, "Pride Playground" }, +#if IS_FIBONACCI { pridePlaygroundFibonacci, "Pride Playground Fibonacci" }, +#endif { colorWavesPlayground, "Color Waves Playground" }, +#if IS_FIBONACCI { colorWavesPlaygroundFibonacci, "Color Waves Playground Fibonacci" }, #endif - { wheel, "Wheel" }, -#if (PARALLEL_OUTPUT_CHANNELS > 1) - { multi_test, "Multi Test" }, -#endif + { wheel, "Wheel" }, #if IS_FIBONACCI { swirlFibonacci, "Swirl Fibonacci"}, @@ -141,7 +126,7 @@ const PatternAndName patterns[] = { #endif #if HAS_COORDINATE_MAP // really a wrong name... and likely doing way more computation than necessary - { radarSweepPalette, "Radar Sweep Palette" }, + { radarSweepPalette, "Radar Sweep Palette" }, #endif #if HAS_COORDINATE_MAP @@ -159,7 +144,7 @@ const PatternAndName patterns[] = { { xyGradientPalette, "XY Axis Gradient Palette" }, #endif -#if HAS_POLAR_COORDS // really a wrong name... and likely doing way more computation than necessary +#if HAS_POLAR_COORDS // noise patterns (Polar variations) { gradientPalettePolarNoise, "Gradient Palette Polar Noise" }, { palettePolarNoise, "Palette Polar Noise" }, @@ -205,13 +190,10 @@ const PatternAndName patterns[] = { { drawSpiralAnalogClock34_21_and_13, "Spiral Analog Clock 34, 21 & 13"}, #endif - { pridePlayground, "Pride Playground" }, - { colorWavesPlayground, "Color Waves Playground" }, - #if defined(PRODUCT_KRAKEN64) // Kraken patterns ... these use body[], which is also used as a proxy for radius... - { radiusPalette, "Kraken Palette" }, - { radiusGradientPalette, "Kraken Gradient Palette" }, + { radiusPalette, "Kraken Palette" }, + { radiusGradientPalette, "Kraken Gradient Palette" }, #endif // twinkle patterns @@ -247,6 +229,9 @@ const PatternAndName patterns[] = { { water, "Water" }, { strandTest, "Strand Test" }, +#if (PARALLEL_OUTPUT_CHANNELS > 1) + { multi_test, "Multi Test" }, +#endif { showSolidColor, "Solid Color" } // This *must* be the last pattern }; diff --git a/esp8266-fastled-webserver/include/Fields.hpp b/esp8266-fastled-webserver/include/Fields.hpp index 88d28a8b..cfb918e3 100644 --- a/esp8266-fastled-webserver/include/Fields.hpp +++ b/esp8266-fastled-webserver/include/Fields.hpp @@ -24,7 +24,7 @@ extern uint8_t brightness; void setShowClock(uint8_t value); void setClockBackgroundFade(uint8_t value); -String setUtcOffsetIndexString(String value); +uint8_t setUtcOffsetIndex(uint8_t value); String getFieldValue(String name); String setFieldValue(String name, String value); diff --git a/esp8266-fastled-webserver/include/configs/controller/controller_esp32.h b/esp8266-fastled-webserver/include/configs/controller/controller_esp32.h index 246ee964..b93b67bc 100644 --- a/esp8266-fastled-webserver/include/configs/controller/controller_esp32.h +++ b/esp8266-fastled-webserver/include/configs/controller/controller_esp32.h @@ -21,7 +21,9 @@ #define ESP8266_FASTLED_WEBSERVER_CONTROLLER_ESP32_H -static_assert(PARALLEL_OUTPUT_CHANNELS <= 4, "ESP32 only has support for four parallel outputs defined, can be updated to support 16 outputs"); +// Would need to define (and use) the additional DATA_PIN_x symbols, +// and update the corresponding code using those symbols. +static_assert(PARALLEL_OUTPUT_CHANNELS <= 4, "While ESP32 supporst 16 parallel outputs, currently only support four parallel outputs"); // TODO: consider using I2S (instead of RMT) on the ESP32: // @@ -29,13 +31,11 @@ static_assert(PARALLEL_OUTPUT_CHANNELS <= 4, "ESP32 only has support for four pa // // See https://github.com/FastLED/FastLED/issues/1220#issuecomment-822677011 -#define DATA_PIN - #if !defined(DATA_PIN) #if PARALLEL_OUTPUT_CHANNELS == 1 - #define DATA_PIN 18 // d1 mini32 (same physical location as D5 on the d1 mini) + #define DATA_PIN 18 // d1 mini32 (same physical location as D5 on the d1 mini) #else - #define DATA_PIN 23 // d1 mini32 (same physical location as D7 on the d1 mini) + #define DATA_PIN 23 // d1 mini32 (same physical location as D7 on the d1 mini) #endif #endif diff --git a/esp8266-fastled-webserver/include/configs/controller/controller_esp8266.h b/esp8266-fastled-webserver/include/configs/controller/controller_esp8266.h index 9ac16079..e8c62b0c 100644 --- a/esp8266-fastled-webserver/include/configs/controller/controller_esp8266.h +++ b/esp8266-fastled-webserver/include/configs/controller/controller_esp8266.h @@ -24,9 +24,9 @@ static_assert(PARALLEL_OUTPUT_CHANNELS <= 6, "ESP8266 only supports six parallel #if !defined(DATA_PIN) #if PARALLEL_OUTPUT_CHANNELS == 1 - #define DATA_PIN D5 // d1 mini + #define DATA_PIN D5 // d1 mini #else - #define DATA_PIN D7 // Fib512 uses different default for primary output pin + #define DATA_PIN D7 // Fib512 uses different default for primary output pin #endif #endif diff --git a/platformio.ini b/platformio.ini index c31fd239..09d9d966 100644 --- a/platformio.ini +++ b/platformio.ini @@ -17,16 +17,25 @@ [platformio] extra_configs = platformio_override.ini ; so users can easily override settings -default_envs = fastled_webserver, fib512_d1_mini, fib256_d1_mini, fib128_d1_mini, fib64_full_d1_mini, fib64_mini_d1_mini, fib32_d1_mini +default_envs = fastled_webserver__d1_mini, fib512__d1_mini, fib256__d1_mini, fib128__d1_mini, fib64_full__d1_mini, fib64_mini__d1_mini, fib32__d1_mini, esp_thing__d1_mini, kraken64__d1_mini, esp_thing__d1_mini, 1628_rings__d1_mini ; default_envs = fastled_webserver -; default_envs = fib512_d1_mini -; default_envs = fib256_d1_mini -; default_envs = fib128_d1_mini -; default_envs = fib64_full_d1_mini -; default_envs = fib64_mini_d1_mini -; default_envs = fib64_micro_d1_mini -; default_envs = fib64_nano_d1_mini -; default_envs = fib32_d1_mini +; default_envs = fib512__d1_mini +; default_envs = fib256__d1_mini +; default_envs = fib128__d1_mini +; default_envs = fib64_full__d1_mini +; default_envs = fib64_mini__d1_mini +; default_envs = fib64_micro__d1_mini +; default_envs = fib64_nano__d1_mini +; default_envs = fib32__d1_mini +; default_envs = esp_thing__d1_mini +; default_envs = kraken64__d1_mini +; default_envs = esp_thing__d1_mini +; default_envs = 1628_rings__d1_mini + + + + + src_dir = ./esp8266-fastled-webserver/ data_dir = ./esp8266-fastled-webserver/data build_cache_dir = ~/.buildcache @@ -70,7 +79,7 @@ arduino_core_develop = https://github.com/platformio/platform-espressif8266#deve arduino_core_git = https://github.com/platformio/platform-espressif8266#feature/stage ; Define the version to use based on the Arduino Core version... -platform_fibonacci_default = ${common.arduino_core_2_7_4} +platform_default = ${common.arduino_core_2_7_4} platform_packages = tasmota/framework-arduinoespressif8266 @ 3.20704.7 platformio/toolchain-xtensa @ ~2.40802.200502 platformio/tool-esptool @ ~1.413.0 @@ -86,13 +95,14 @@ board_build.flash_mode = dout board_build.filesystem = littlefs monitor_speed = 115200 upload_speed = 921600 +extra_scripts = ${scripts_defaults.extra_scripts} lib_compat_mode = strict lib_deps = - fastled/FastLED @ 3.4.0 - https://github.com/arduino-libraries/NTPClient.git @ 3.2.0 - https://github.com/tzapu/WiFiManager.git @ ^2.0.4-beta - -extra_scripts = ${scripts_defaults.extra_scripts} + fastled/FastLED @ 3.4.0 + bblanchon/ArduinoJson @ ^6.18.5 + lorol/LITTLEFS_esp32 @ ^1.0.6 + https://github.com/arduino-libraries/NTPClient.git @ 3.2.0 + https://github.com/tzapu/WiFiManager.git @ ^2.0.4-beta [esp8266] build_flags = @@ -113,63 +123,56 @@ build_flags = lib_deps = ${env.lib_deps} -[env:fastled_webserver] +[common__d1_mini] +platform = ${common.platform_default} +platform_packages = ${common.platform_packages} +lib_deps = ${esp8266.lib_deps} board = d1_mini -platform = ${common.platform_fibonacci_default} +board_build.ldscript = ${common.ldscript_4m1m} +build_unflags = ${common.build_unflags} + +[common__d1_mini32] +platform = espressif32@2.0 platform_packages = ${common.platform_packages} +lib_deps = ${esp32.lib_deps} +board = wemos_d1_mini32 board_build.ldscript = ${common.ldscript_4m1m} build_unflags = ${common.build_unflags} + + +[env:fastled_webserver__d1_mini] +extends = common__d1_mini build_flags = ${common.build_flags_esp8266} -D PRODUCT_DEFAULT -[env:fib512_d1_mini] -board = d1_mini -platform = ${common.platform_fibonacci_default} -platform_packages = ${common.platform_packages} -board_build.ldscript = ${common.ldscript_4m1m} -build_unflags = ${common.build_unflags} +[env:fib512__d1_mini] +extends = common__d1_mini build_flags = ${common.build_flags_esp8266} -D PRODUCT_FIBONACCI512 -[env:fib256_d1_mini] -board = d1_mini -platform = ${common.platform_fibonacci_default} -platform_packages = ${common.platform_packages} -board_build.ldscript = ${common.ldscript_4m1m} -build_unflags = ${common.build_unflags} +[env:fib256__d1_mini] +extends = common__d1_mini build_flags = ${common.build_flags_esp8266} -D PRODUCT_FIBONACCI256 -[env:fib128_d1_mini] -board = d1_mini -platform = ${common.platform_fibonacci_default} -platform_packages = ${common.platform_packages} -board_build.ldscript = ${common.ldscript_4m1m} -build_unflags = ${common.build_unflags} +[env:fib128__d1_mini] +extends = common__d1_mini build_flags = ${common.build_flags_esp8266} -D PRODUCT_FIBONACCI128 -[env:fib64_full_d1_mini] -board = d1_mini -platform = ${common.platform_fibonacci_default} -platform_packages = ${common.platform_packages} -board_build.ldscript = ${common.ldscript_4m1m} -build_unflags = ${common.build_unflags} +[env:fib64_full__d1_mini] +extends = common__d1_mini build_flags = ${common.build_flags_esp8266} -D PRODUCT_FIBONACCI64_FULL -[env:fib64_mini_d1_mini] -board = d1_mini -platform = ${common.platform_fibonacci_default} -platform_packages = ${common.platform_packages} -board_build.ldscript = ${common.ldscript_4m1m} -build_unflags = ${common.build_unflags} +[env:fib64_mini__d1_mini] +extends = common__d1_mini build_flags = ${common.build_flags_esp8266} -D PRODUCT_FIBONACCI64_MINI @@ -182,12 +185,8 @@ build_flags = ; 256KB Flash and 32 KB RAM ; Support *is* possible, but would require ; factoring out all the WiFi functionality (at least). -[env:fib64_micro_d1_mini] -board = d1_mini -platform = ${common.platform_fibonacci_default} -platform_packages = ${common.platform_packages} -board_build.ldscript = ${common.ldscript_4m1m} -build_unflags = ${common.build_unflags} +[env:fib64_micro__d1_mini] +extends = common__d1_mini build_flags = ${common.build_flags_esp8266} -D PRODUCT_FIBONACCI64_MICRO @@ -199,34 +198,40 @@ build_flags = ; 256KB Flash and 32 KB RAM ; Support *is* possible, but would require ; factoring out all the WiFi functionality (at least). -[env:fib64_nano_d1_mini] -board = d1_mini -platform = ${common.platform_fibonacci_default} -platform_packages = ${common.platform_packages} -board_build.ldscript = ${common.ldscript_4m1m} -build_unflags = ${common.build_unflags} +[env:fib64_nano__d1_mini] +extends = common__d1_mini build_flags = ${common.build_flags_esp8266} -D PRODUCT_FIBONACCI64_NANO -[env:fib32_d1_mini] -board = d1_mini -platform = ${common.platform_fibonacci_default} -platform_packages = ${common.platform_packages} -board_build.ldscript = ${common.ldscript_4m1m} -build_unflags = ${common.build_unflags} +[env:fib32__d1_mini] +extends = common__d1_mini build_flags = ${common.build_flags_esp8266} -D PRODUCT_FIBONACCI32 ; NOT YET SUPPORTED ; ESP32 is a work-in-progress -[env:fib256_d1_mini32] -board = wemos_d1_mini32 -platform = espressif32@2.0 -platform_packages = ${common.platform_packages} -board_build.ldscript = ${common.ldscript_4m1m} -build_unflags = ${common.build_unflags} +[env:fib256__d1_mini32] +extends = common__d1_mini32 build_flags = ${common.build_flags_esp32} -D PRODUCT_FIBONACCI256 + +[env:kraken64__d1_mini] +extends = common__d1_mini +build_flags = + ${common.build_flags_esp8266} + -D PRODUCT_KRAKEN64 + +[env:esp_thing__d1_mini] +extends = common__d1_mini +build_flags = + ${common.build_flags_esp8266} + -D PRODUCT_ESP8266_THING + +[env:1628_rings__d1_mini] +extends = common__d1_mini +build_flags = + ${common.build_flags_esp8266} + -D PRODUCT_1628_RINGS diff --git a/platformio_override_example.ini b/platformio_override_example.ini new file mode 100644 index 00000000..8876284c --- /dev/null +++ b/platformio_override_example.ini @@ -0,0 +1,20 @@ + +; The following is an example build environment. +; It can be used to build a custom binary for the +; "ESP Thing" board, where there are a different +; number of LEDs attached to each data pin. + + +[env:custom_esp_thing] +extends = common__d1_mini +build_flags = + ${common.build_flags_esp8266} + -D PRODUCT_ESP8266_THING + -D NUM_PIXELS=72 + -D PIXELS_ON_DATA_PIN_1=5 + -D PIXELS_ON_DATA_PIN_2=7 + -D PIXELS_ON_DATA_PIN_3=11 + -D PIXELS_ON_DATA_PIN_4=13 + -D PIXELS_ON_DATA_PIN_5=17 + -D PIXELS_ON_DATA_PIN_6=19 +