diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml index 8b1d22d2d6..5274805baa 100644 --- a/.github/FUNDING.yml +++ b/.github/FUNDING.yml @@ -1,2 +1,5 @@ github: [Aircoookie] custom: ['https://paypal.me/Aircoookie'] + +github: [blazoncek] +custom: ['https://paypal.me/blazoncek'] diff --git a/.gitignore b/.gitignore index 02e648b88b..bb02e36ef2 100644 --- a/.gitignore +++ b/.gitignore @@ -3,7 +3,6 @@ .pioenvs .piolibdeps .vscode -!.vscode/extensions.json /wled00/Release /wled00/extLibs /platformio_override.ini @@ -15,3 +14,7 @@ node_modules .idea .direnv +wled-update.sh +esp01-update.sh +/wled00/LittleFS +replace_fs.py \ No newline at end of file diff --git a/.vscode/extensions.json b/.vscode/extensions.json index 080e70d08b..0f0d7401df 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -1,10 +1,7 @@ -{ - // See http://go.microsoft.com/fwlink/?LinkId=827846 - // for the documentation about the extensions.json format - "recommendations": [ - "platformio.platformio-ide" - ], - "unwantedRecommendations": [ - "ms-vscode.cpptools-extension-pack" - ] -} +{ + // See http://go.microsoft.com/fwlink/?LinkId=827846 + // for the documentation about the extensions.json format + "recommendations": [ + "platformio.platformio-ide" + ] +} diff --git a/package-lock.json b/package-lock.json index dce0faf421..ce4caa8e7c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "wled", - "version": "0.13.2", + "version": "0.14.0-b0", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index b2ffbb3692..1e31f689c1 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "wled", - "version": "0.13.2", + "version": "0.14.0-b0", "description": "Tools for WLED project", "main": "tools/cdata.js", "directories": { diff --git a/platformio.ini b/platformio.ini index dbbe06d88c..78b5d8e5ee 100644 --- a/platformio.ini +++ b/platformio.ini @@ -55,14 +55,14 @@ extra_configs = arduino_core_2_6_3 = espressif8266@2.3.3 arduino_core_2_7_4 = espressif8266@2.6.2 arduino_core_3_0_0 = espressif8266@3.0.0 -arduino_core_3_0_2 = espressif8266@3.2.0 +arduino_core_3_2_0 = espressif8266@3.2.0 # Development platforms arduino_core_develop = https://github.com/platformio/platform-espressif8266#develop arduino_core_git = https://github.com/platformio/platform-espressif8266#feature/stage # Platform to use for ESP8266 -platform_wled_default = ${common.arduino_core_2_7_4} +platform_wled_default = ${common.arduino_core_3_2_0} # We use 2.7.4.7 for all, includes PWM flicker fix and Wstring optimization platform_packages = tasmota/framework-arduinoespressif8266 @ 3.20704.7 platformio/toolchain-xtensa @ ~2.40802.200502 @@ -162,11 +162,12 @@ lib_compat_mode = strict lib_deps = fastled/FastLED @ 3.5.0 IRremoteESP8266 @ 2.8.2 - https://github.com/Aircoookie/ESPAsyncWebServer.git @ ~2.0.4 + https://github.com/Aircoookie/ESPAsyncWebServer.git @ ~2.0.5 #For use of the TTGO T-Display ESP32 Module with integrated TFT display uncomment the following line #TFT_eSPI #For use SSD1306 OLED display uncomment following - #U8g2@~2.27.2 + #U8g2@~2.28.8 + #U8g2@~2.32.10 #For Dallas sensor uncomment following 2 lines #OneWire@~2.3.5 #milesburton/DallasTemperature@^3.9.0 @@ -183,8 +184,8 @@ build_flags = -DESP8266 -DFP_IN_IROM ;-Wno-deprecated-declarations - -Wno-register - -Wno-misleading-indentation + ;-Wno-register + ;-Wno-misleading-indentation ; NONOSDK22x_190703 = 2.2.2-dev(38a443e) -DPIO_FRAMEWORK_ARDUINO_ESPRESSIF_SDK22x_190703 ; lwIP 2 - Higher Bandwidth no Features diff --git a/tools/WLED_ESP32-wrover_4MB.csv b/tools/WLED_ESP32-wrover_4MB.csv new file mode 100644 index 0000000000..a179a89d0e --- /dev/null +++ b/tools/WLED_ESP32-wrover_4MB.csv @@ -0,0 +1,6 @@ +# Name, Type, SubType, Offset, Size, Flags +nvs, data, nvs, 0x9000, 0x5000, +otadata, data, ota, 0xe000, 0x2000, +app0, app, ota_0, 0x10000, 0x180000, +app1, app, ota_1, 0x190000,0x180000, +spiffs, data, spiffs, 0x310000,0xF0000, diff --git a/tools/cdata.js b/tools/cdata.js index 15455a4284..d01c3e35f7 100644 --- a/tools/cdata.js +++ b/tools/cdata.js @@ -16,20 +16,31 @@ */ const fs = require("fs"); +const inliner = require("inliner"); +const zlib = require("zlib"); +const CleanCSS = require("clean-css"); +const MinifyHTML = require("html-minifier-terser").minify; const packageJson = require("../package.json"); /** * */ -function hexdump(buffer) { +function hexdump(buffer,isHex=false) { let lines = []; - for (let i = 0; i < buffer.length; i += 16) { - let block = buffer.slice(i, i + 16); // cut buffer into blocks of 16 + for (let i = 0; i < buffer.length; i +=(isHex?32:16)) { + var block; let hexArray = []; - - for (let value of block) { - hexArray.push("0x" + value.toString(16).padStart(2, "0")); + if (isHex) { + block = buffer.slice(i, i + 32) + for (let j = 0; j < block.length; j +=2 ) { + hexArray.push("0x" + block.slice(j,j+2)) + } + } else { + block = buffer.slice(i, i + 16); // cut buffer into blocks of 16 + for (let value of block) { + hexArray.push("0x" + value.toString(16).padStart(2, "0")); + } } let hexString = hexArray.join(", "); @@ -40,9 +51,6 @@ function hexdump(buffer) { return lines.join(",\n"); } -const inliner = require("inliner"); -const zlib = require("zlib"); - function strReplace(str, search, replacement) { return str.split(search).join(replacement); } @@ -56,16 +64,52 @@ function adoptVersionAndRepo(html) { html = strReplace(html, "https://github.com/atuline/WLED", repoUrl); html = strReplace(html, "https://github.com/Aircoookie/WLED", repoUrl); } - let version = packageJson.version; if (version) { html = strReplace(html, "##VERSION##", version); } - return html; } -function writeHtmlGzipped(sourceFile, resultFile) { +function filter(str, type) { + str = adoptVersionAndRepo(str); + if (type === undefined) { + return str; + } else if (type == "css-minify") { + return new CleanCSS({}).minify(str).styles; + } else if (type == "js-minify") { + return MinifyHTML('', { + collapseWhitespace: true, + minifyJS: true, + continueOnParseError: false, + removeComments: true, + }).replace(/<[\/]*script>/g,''); + } else if (type == "html-minify") { + return MinifyHTML(str, { + collapseWhitespace: true, + maxLineLength: 80, + minifyCSS: true, + minifyJS: true, + continueOnParseError: false, + removeComments: true, + }); + } else if (type == "html-minify-ui") { + return MinifyHTML(str, { + collapseWhitespace: true, + conservativeCollapse: true, + maxLineLength: 80, + minifyCSS: true, + minifyJS: true, + continueOnParseError: false, + removeComments: true, + }); + } else { + console.warn("Unknown filter: " + type); + return str; + } +} + +function writeHtmlGzipped(sourceFile, resultFile, page) { console.info("Reading " + sourceFile); new inliner(sourceFile, function (error, html) { console.info("Inlined " + html.length + " characters"); @@ -95,8 +139,8 @@ function writeHtmlGzipped(sourceFile, resultFile) { */ // Autogenerated from ${sourceFile}, do not edit!! -const uint16_t PAGE_index_L = ${result.length}; -const uint8_t PAGE_index[] PROGMEM = { +const uint16_t PAGE_${page}_L = ${result.length}; +const uint8_t PAGE_${page}[] PROGMEM = { ${array} }; `; @@ -106,41 +150,6 @@ ${array} }); } -const CleanCSS = require("clean-css"); -const MinifyHTML = require("html-minifier-terser").minify; - -function filter(str, type) { - str = adoptVersionAndRepo(str); - - if (type === undefined) { - return str; - } else if (type == "css-minify") { - return new CleanCSS({}).minify(str).styles; - } else if (type == "html-minify") { - return MinifyHTML(str, { - collapseWhitespace: true, - maxLineLength: 80, - minifyCSS: true, - minifyJS: true, - continueOnParseError: false, - removeComments: true, - }); - } else if (type == "html-minify-ui") { - return MinifyHTML(str, { - collapseWhitespace: true, - conservativeCollapse: true, - maxLineLength: 80, - minifyCSS: true, - minifyJS: true, - continueOnParseError: false, - removeComments: true, - }); - } else { - console.warn("Unknown filter: " + type); - return str; - } -} - function specToChunk(srcDir, s) { if (s.method == "plaintext") { const buf = fs.readFileSync(srcDir + "/" + s.file); @@ -153,6 +162,21 @@ const char ${s.name}[] PROGMEM = R"${s.prepend || ""}${filter(str, s.filter)}${ `; return s.mangle ? s.mangle(chunk) : chunk; + } else if (s.method == "gzip") { + const buf = fs.readFileSync(srcDir + "/" + s.file); + var str = buf.toString('utf-8'); + if (s.mangle) str = s.mangle(str); + const zip = zlib.gzipSync(filter(str, s.filter), { level: zlib.constants.Z_BEST_COMPRESSION }); + const result = hexdump(zip.toString('hex'), true); + const chunk = ` +// Autogenerated from ${srcDir}/${s.file}, do not edit!! +const uint16_t ${s.name}_length = ${zip.length}; +const uint8_t ${s.name}[] PROGMEM = { +${result} +}; + +`; + return chunk; } else if (s.method == "binary") { const buf = fs.readFileSync(srcDir + "/" + s.file); const result = hexdump(buf); @@ -164,7 +188,7 @@ ${result} }; `; - return s.mangle ? s.mangle(chunk) : chunk; + return chunk; } else { console.warn("Unknown method: " + s.method); return undefined; @@ -194,160 +218,111 @@ function writeChunks(srcDir, specs, resultFile) { fs.writeFileSync(resultFile, src); } -writeHtmlGzipped("wled00/data/index.htm", "wled00/html_ui.h"); - +writeHtmlGzipped("wled00/data/index.htm", "wled00/html_ui.h", 'index'); +writeHtmlGzipped("wled00/data/simple.htm", "wled00/html_simple.h", 'simple'); +/* +writeChunks( + "wled00/data", + [ + { + file: "simple.css", + name: "PAGE_simpleCss", + method: "gzip", + filter: "css-minify", + }, + { + file: "simple.js", + name: "PAGE_simpleJs", + method: "gzip", + filter: "js-minify", + }, + { + file: "simple.htm", + name: "PAGE_simple", + method: "gzip", + filter: "html-minify-ui", + } + ], + "wled00/html_simplex.h" +); +*/ writeChunks( "wled00/data", [ { file: "style.css", name: "PAGE_settingsCss", - prepend: "=====()=====", - method: "plaintext", + method: "gzip", filter: "css-minify", + mangle: (str) => + str + .replace("%%","%") }, { file: "settings.htm", name: "PAGE_settings", - prepend: "=====(", - append: ")=====", - method: "plaintext", + method: "gzip", filter: "html-minify", - mangle: (str) => - str - .replace("%", "%%") - .replace(/Usermods\<\/button\>\<\/form\>/gms, "Usermods\<\/button\>\<\/form\>%DMXMENU%"), }, { file: "settings_wifi.htm", name: "PAGE_settings_wifi", - prepend: "=====(", - append: ")=====", - method: "plaintext", + method: "gzip", filter: "html-minify", - mangle: (str) => - str - .replace(/\/gms, "") - .replace(/\.*\<\/style\>/gms, "%CSS%%SCSS%") - .replace( - /function GetV().*\<\/script\>/gms, - "function GetV() {var d=document;\n" - ), }, { file: "settings_leds.htm", name: "PAGE_settings_leds", - prepend: "=====(", - append: ")=====", - method: "plaintext", + method: "gzip", filter: "html-minify", - mangle: (str) => - str - .replace(/\/gms, "") - .replace(/\.*\<\/style\>/gms, "%CSS%%SCSS%") - .replace( - /function GetV().*\<\/script\>/gms, - "function GetV() {var d=document;\n" - ), }, { file: "settings_dmx.htm", name: "PAGE_settings_dmx", - prepend: "=====(", - append: ")=====", - method: "plaintext", + method: "gzip", filter: "html-minify", - mangle: (str) => { - const nocss = str - .replace(/\/gms, "") - .replace(/\.*\<\/style\>/gms, "%CSS%%SCSS%") - .replace( - /function GetV().*\<\/script\>/gms, - "function GetV() {var d=document;\n" - ); - return ` -#ifdef WLED_ENABLE_DMX -${nocss} -#else -const char PAGE_settings_dmx[] PROGMEM = R"=====()====="; -#endif -`; - }, }, { file: "settings_ui.htm", name: "PAGE_settings_ui", - prepend: "=====(", - append: ")=====", - method: "plaintext", + method: "gzip", filter: "html-minify", - mangle: (str) => - str - .replace(/\/gms, "") - .replace(/\.*\<\/style\>/gms, "%CSS%%SCSS%") - .replace( - /function GetV().*\<\/script\>/gms, - "function GetV() {var d=document;\n" - ), }, { file: "settings_sync.htm", name: "PAGE_settings_sync", - prepend: "=====(", - append: ")=====", - method: "plaintext", + method: "gzip", filter: "html-minify", - mangle: (str) => - str - .replace(/\/gms, "") - .replace(/\.*\<\/style\>/gms, "%CSS%%SCSS%") - .replace(/function GetV().*\<\/script\>/gms, "function GetV() {\n"), }, { file: "settings_time.htm", name: "PAGE_settings_time", - prepend: "=====(", - append: ")=====", - method: "plaintext", + method: "gzip", filter: "html-minify", - mangle: (str) => - str - .replace(/\/gms, "") - .replace(/\.*\<\/style\>/gms, "%CSS%%SCSS%") - .replace(/function GetV().*\<\/script\>/gms, "function GetV() {\n"), }, { file: "settings_sec.htm", name: "PAGE_settings_sec", - prepend: "=====(", - append: ")=====", - method: "plaintext", + method: "gzip", filter: "html-minify", - mangle: (str) => - str - .replace(/\/gms, "") - .replace(/\.*\<\/style\>/gms, "%CSS%%SCSS%") - .replace( - /function GetV().*\<\/script\>/gms, - "function GetV() {var d=document;\n" - ), }, { file: "settings_um.htm", name: "PAGE_settings_um", - prepend: "=====(", - append: ")=====", - method: "plaintext", + method: "gzip", filter: "html-minify", - mangle: (str) => - str - .replace(/\/gms, "") - .replace(/\.*\<\/style\>/gms, "%CSS%%SCSS%") - .replace( - /function GetV().*\<\/script\>/gms, - "function GetV() {var d=document;\n" - ), + }, + { + file: "settings_2D.htm", + name: "PAGE_settings_2D", + method: "gzip", + filter: "html-minify", + }, + { + file: "settings_pin.htm", + name: "PAGE_settings_pin", + method: "gzip", + filter: "html-minify" } ], "wled00/html_settings.h" @@ -359,9 +334,7 @@ writeChunks( { file: "usermod.htm", name: "PAGE_usermod", - prepend: "=====(", - append: ")=====", - method: "plaintext", + method: "gzip", filter: "html-minify", mangle: (str) => str.replace(/fetch\("http\:\/\/.*\/win/gms, 'fetch("/win'), @@ -393,41 +366,43 @@ const char PAGE_dmxmap[] PROGMEM = R"=====()====="; { file: "update.htm", name: "PAGE_update", - prepend: "=====(", - append: ")=====", - method: "plaintext", + method: "gzip", filter: "html-minify", + mangle: (str) => + str + .replace( + /function GetV().*\<\/script\>/gms, + "" + ) }, { file: "welcome.htm", name: "PAGE_welcome", - prepend: "=====(", - append: ")=====", - method: "plaintext", + method: "gzip", filter: "html-minify", }, { file: "liveview.htm", name: "PAGE_liveview", - prepend: "=====(", - append: ")=====", - method: "plaintext", + method: "gzip", filter: "html-minify", }, { file: "liveviewws.htm", name: "PAGE_liveviewws", - prepend: "=====(", - append: ")=====", - method: "plaintext", + method: "gzip", + filter: "html-minify", + }, + { + file: "liveviewws2D.htm", + name: "PAGE_liveviewws2D", + method: "gzip", filter: "html-minify", }, { file: "404.htm", name: "PAGE_404", - prepend: "=====(", - append: ")=====", - method: "plaintext", + method: "gzip", filter: "html-minify", }, { @@ -435,6 +410,16 @@ const char PAGE_dmxmap[] PROGMEM = R"=====()====="; name: "favicon", method: "binary", }, + { + file: "iro.js", + name: "iroJs", + method: "gzip" + }, + { + file: "rangetouch.js", + name: "rangetouchJs", + method: "gzip" + } ], "wled00/html_other.h" ); diff --git a/usermods/Animated_Staircase/Animated_Staircase.h b/usermods/Animated_Staircase/Animated_Staircase.h index 35ea0d7a4a..3cf349ca51 100644 --- a/usermods/Animated_Staircase/Animated_Staircase.h +++ b/usermods/Animated_Staircase/Animated_Staircase.h @@ -103,25 +103,24 @@ class Animated_Staircase : public Usermod { void updateSegments() { mainSegmentId = strip.getMainSegmentId(); - WS2812FX::Segment* segments = strip.getSegments(); - for (int i = 0; i < MAX_NUM_SEGMENTS; i++, segments++) { - if (!segments->isActive()) { + for (int i = 0; i < strip.getSegmentsNum(); i++) { + Segment &seg = strip.getSegment(i); + if (!seg.isActive()) { maxSegmentId = i - 1; break; } - if (i >= onIndex && i < offIndex) { - segments->setOption(SEG_OPTION_ON, 1, i); + seg.setOption(SEG_OPTION_ON, true); // We may need to copy mode and colors from segment 0 to make sure // changes are propagated even when the config is changed during a wipe // segments->mode = mainsegment.mode; // segments->colors[0] = mainsegment.colors[0]; } else { - segments->setOption(SEG_OPTION_ON, 0, i); + seg.setOption(SEG_OPTION_ON, false); } // Always mark segments as "transitional", we are animating the staircase - segments->setOption(SEG_OPTION_TRANSITIONAL, 1, i); + seg.setOption(SEG_OPTION_TRANSITIONAL, true); } colorUpdated(CALL_MODE_DIRECT_CHANGE); } @@ -290,13 +289,13 @@ class Animated_Staircase : public Usermod { } } else { // Restore segment options - WS2812FX::Segment* segments = strip.getSegments(); - for (int i = 0; i < MAX_NUM_SEGMENTS; i++, segments++) { - if (!segments->isActive()) { + for (int i = 0; i < strip.getSegmentsNum(); i++) { + Segment &seg = strip.getSegment(i); + if (!seg.isActive()) { maxSegmentId = i - 1; break; } - segments->setOption(SEG_OPTION_ON, 1, i); + seg.setOption(SEG_OPTION_ON, true); } colorUpdated(CALL_MODE_DIRECT_CHANGE); DEBUG_PRINTLN(F("Animated Staircase disabled.")); @@ -406,6 +405,14 @@ class Animated_Staircase : public Usermod { } } + void appendConfigData() { + //oappend(SET_F("dd=addDropdown('staircase','selectfield');")); + //oappend(SET_F("addOption(dd,'1st value',0);")); + //oappend(SET_F("addOption(dd,'2nd value',1);")); + //oappend(SET_F("addInfo('staircase:selectfield',1,'additional info');")); // 0 is field type, 1 is actual field + } + + /* * Writes the configuration to internal flash memory. */ @@ -500,22 +507,22 @@ class Animated_Staircase : public Usermod { * tab of the web-UI. */ void addToJsonInfo(JsonObject& root) { - JsonObject staircase = root["u"]; - if (staircase.isNull()) { - staircase = root.createNestedObject("u"); + JsonObject user = root["u"]; + if (user.isNull()) { + user = root.createNestedObject("u"); } - JsonArray usermodEnabled = staircase.createNestedArray(F("Staircase")); // name - String btn = F(""); - usermodEnabled.add(btn); // value + JsonArray infoArr = user.createNestedArray(FPSTR(_name)); // name + + String uiDomString = F(""); + infoArr.add(uiDomString); } }; diff --git a/usermods/BME280_v2/usermod_bme280.h b/usermods/BME280_v2/usermod_bme280.h index 742066f7b2..6b42fc808a 100644 --- a/usermods/BME280_v2/usermod_bme280.h +++ b/usermods/BME280_v2/usermod_bme280.h @@ -26,15 +26,10 @@ class UsermodBME280 : public Usermod bool HomeAssistantDiscovery = false; // Publish Home Assistant Device Information // set the default pins based on the architecture, these get overridden by Usermod menu settings - #ifdef ARDUINO_ARCH_ESP32 // ESP32 boards - #define HW_PIN_SCL 22 - #define HW_PIN_SDA 21 - #else // ESP8266 boards - #define HW_PIN_SCL 5 - #define HW_PIN_SDA 4 + #ifdef ESP8266 //uint8_t RST_PIN = 16; // Uncoment for Heltec WiFi-Kit-8 #endif - int8_t ioPin[2] = {HW_PIN_SCL, HW_PIN_SDA}; // I2C pins: SCL, SDA...defaults to Arch hardware pins but overridden at setup() + int8_t ioPin[2] = {i2c_scl, i2c_sda}; // I2C pins: SCL, SDA...defaults to Arch hardware pins but overridden at setup() bool initDone = false; // BME280 sensor settings @@ -177,7 +172,7 @@ class UsermodBME280 : public Usermod public: void setup() { - bool HW_Pins_Used = (ioPin[0]==HW_PIN_SCL && ioPin[1]==HW_PIN_SDA); // note whether architecture-based hardware SCL/SDA pins used + bool HW_Pins_Used = (ioPin[0]==i2c_scl && ioPin[1]==i2c_sda); // note whether architecture-based hardware SCL/SDA pins used PinOwner po = PinOwner::UM_BME280; // defaults to being pinowner for SCL/SDA pins PinManagerPinType pins[2] = { { ioPin[0], true }, { ioPin[1], true } }; // allocate pins if (HW_Pins_Used) po = PinOwner::HW_I2C; // allow multiple allocations of HW I2C bus pins @@ -444,7 +439,7 @@ class UsermodBME280 : public Usermod for (byte i=0; i<2; i++) if (ioPin[i] != newPin[i]) { pinsChanged = true; break; } // check if any pins changed if (pinsChanged) { //if pins changed, deallocate old pins and allocate new ones PinOwner po = PinOwner::UM_BME280; - if (ioPin[0]==HW_PIN_SCL && ioPin[1]==HW_PIN_SDA) po = PinOwner::HW_I2C; // allow multiple allocations of HW I2C bus pins + if (ioPin[0]==i2c_scl && ioPin[1]==i2c_sda) po = PinOwner::HW_I2C; // allow multiple allocations of HW I2C bus pins pinManager.deallocateMultiplePins((const uint8_t *)ioPin, 2, po); // deallocate pins for (byte i=0; i<2; i++) ioPin[i] = newPin[i]; setup(); diff --git a/usermods/Cronixie/usermod_cronixie.h b/usermods/Cronixie/usermod_cronixie.h index 5e4255f438..5702d8fa80 100644 --- a/usermods/Cronixie/usermod_cronixie.h +++ b/usermods/Cronixie/usermod_cronixie.h @@ -249,7 +249,7 @@ class UsermodCronixie : public Usermod { if (backlight && _digitOut[i] <11) { - uint32_t col = strip.gamma32(strip.getSegment(0).colors[1]); + uint32_t col = gamma32(strip.getSegment(0).colors[1]); for (uint16_t j=o; j< o+10; j++) { if (j != excl) strip.setPixelColor(j, col); } diff --git a/usermods/EleksTube_IPS/TFTs.h b/usermods/EleksTube_IPS/TFTs.h index 4c6bd9cb88..e614704f54 100644 --- a/usermods/EleksTube_IPS/TFTs.h +++ b/usermods/EleksTube_IPS/TFTs.h @@ -75,7 +75,7 @@ class TFTs : public TFT_eSPI { uint8_t lineBuffer[w * 2]; - if (!realtimeMode || realtimeOverride) strip.service(); + if (!realtimeMode || realtimeOverride || (realtimeMode && useMainSegmentOnly)) strip.service(); // 0,0 coordinates are top left for (row = 0; row < h; row++) { @@ -169,7 +169,7 @@ class TFTs : public TFT_eSPI { uint32_t lineSize = ((bitDepth * w +31) >> 5) * 4; uint8_t lineBuffer[lineSize]; - uint8_t serviceStrip = (!realtimeMode || realtimeOverride) ? 7 : 0; + uint8_t serviceStrip = (!realtimeMode || realtimeOverride || (realtimeMode && useMainSegmentOnly)) ? 7 : 0; // row is decremented as the BMP image is drawn bottom up for (row = h-1; row >= 0; row--) { if ((row & 0b00000111) == serviceStrip) strip.service(); //still refresh backlight to mitigate stutter every few rows @@ -250,7 +250,7 @@ class TFTs : public TFT_eSPI { uint8_t lineBuffer[w * 2]; - if (!realtimeMode || realtimeOverride) strip.service(); + if (!realtimeMode || realtimeOverride || (realtimeMode && useMainSegmentOnly)) strip.service(); // 0,0 coordinates are top left for (row = 0; row < h; row++) { @@ -355,7 +355,7 @@ class TFTs : public TFT_eSPI { // Color in grayscale bitmaps if Segment 1 exists // TODO If secondary and tertiary are black, color all in primary, // else color first three from Seg 1 color slots and last three from Seg 2 color slots - WS2812FX::Segment& seg1 = strip.getSegment(tubeSegment); + Segment& seg1 = strip.getSegment(tubeSegment); if (seg1.isActive()) { digitColor = strip.getPixelColor(seg1.start + digit); dimming = seg1.opacity; diff --git a/usermods/EleksTube_IPS/usermod_elekstube_ips.h b/usermods/EleksTube_IPS/usermod_elekstube_ips.h index 06c6ecc894..0f7d92e7e9 100644 --- a/usermods/EleksTube_IPS/usermod_elekstube_ips.h +++ b/usermods/EleksTube_IPS/usermod_elekstube_ips.h @@ -63,7 +63,7 @@ class ElekstubeIPSUsermod : public Usermod { if (!toki.isTick()) return; updateLocalTime(); - WS2812FX::Segment& seg1 = strip.getSegment(tfts.tubeSegment); + Segment& seg1 = strip.getSegment(tfts.tubeSegment); if (seg1.isActive()) { bool update = false; if (seg1.opacity != lastBri) update = true; diff --git a/usermods/Enclosure_with_OLED_temp_ESP07/usermod.cpp b/usermods/Enclosure_with_OLED_temp_ESP07/usermod.cpp index 36d322fa0d..0a2662aacd 100644 --- a/usermods/Enclosure_with_OLED_temp_ESP07/usermod.cpp +++ b/usermods/Enclosure_with_OLED_temp_ESP07/usermod.cpp @@ -148,58 +148,14 @@ void userLoop() { // Third row with mode name u8x8.setCursor(2, 2); - uint8_t qComma = 0; - bool insideQuotes = false; - uint8_t printedChars = 0; - char singleJsonSymbol; - - // Find the mode name in JSON - for (size_t i = 0; i < strlen_P(JSON_mode_names); i++) { - singleJsonSymbol = pgm_read_byte_near(JSON_mode_names + i); - switch (singleJsonSymbol) { - case '"': - insideQuotes = !insideQuotes; - break; - case '[': - case ']': - break; - case ',': - qComma++; - default: - if (!insideQuotes || (qComma != knownMode)) - break; - u8x8.print(singleJsonSymbol); - printedChars++; - } - if ((qComma > knownMode) || (printedChars > u8x8.getCols() - 2)) - break; - } + char lineBuffer[17]; + extractModeName(knownMode, JSON_mode_names, lineBuffer, 16); + u8x8.print(lineBuffer); + // Fourth row with palette name u8x8.setCursor(2, 3); - qComma = 0; - insideQuotes = false; - printedChars = 0; - // Looking for palette name in JSON. - for (size_t i = 0; i < strlen_P(JSON_palette_names); i++) { - singleJsonSymbol = pgm_read_byte_near(JSON_palette_names + i); - switch (singleJsonSymbol) { - case '"': - insideQuotes = !insideQuotes; - break; - case '[': - case ']': - break; - case ',': - qComma++; - default: - if (!insideQuotes || (qComma != knownPalette)) - break; - u8x8.print(singleJsonSymbol); - printedChars++; - } - if ((qComma > knownMode) || (printedChars > u8x8.getCols() - 2)) - break; - } + extractModeName(knownPalette, JSON_palette_names, lineBuffer, 16); + u8x8.print(lineBuffer); u8x8.setFont(u8x8_font_open_iconic_embedded_1x1); u8x8.drawGlyph(0, 0, 80); // wifi icon diff --git a/usermods/Enclosure_with_OLED_temp_ESP07/usermod_bme280.cpp b/usermods/Enclosure_with_OLED_temp_ESP07/usermod_bme280.cpp index 5873128321..88d4979050 100644 --- a/usermods/Enclosure_with_OLED_temp_ESP07/usermod_bme280.cpp +++ b/usermods/Enclosure_with_OLED_temp_ESP07/usermod_bme280.cpp @@ -191,58 +191,14 @@ void userLoop() { // Third row with mode name u8x8.setCursor(2, 2); - uint8_t qComma = 0; - bool insideQuotes = false; - uint8_t printedChars = 0; - char singleJsonSymbol; + char lineBuffer[17]; + extractModeName(knownMode, JSON_mode_names, lineBuffer, 16); + u8x8.print(lineBuffer); - // Find the mode name in JSON - for (size_t i = 0; i < strlen_P(JSON_mode_names); i++) { - singleJsonSymbol = pgm_read_byte_near(JSON_mode_names + i); - switch (singleJsonSymbol) { - case '"': - insideQuotes = !insideQuotes; - break; - case '[': - case ']': - break; - case ',': - qComma++; - default: - if (!insideQuotes || (qComma != knownMode)) - break; - u8x8.print(singleJsonSymbol); - printedChars++; - } - if ((qComma > knownMode) || (printedChars > u8x8.getCols() - 2)) - break; - } // Fourth row with palette name u8x8.setCursor(2, 3); - qComma = 0; - insideQuotes = false; - printedChars = 0; - // Looking for palette name in JSON. - for (size_t i = 0; i < strlen_P(JSON_palette_names); i++) { - singleJsonSymbol = pgm_read_byte_near(JSON_palette_names + i); - switch (singleJsonSymbol) { - case '"': - insideQuotes = !insideQuotes; - break; - case '[': - case ']': - break; - case ',': - qComma++; - default: - if (!insideQuotes || (qComma != knownPalette)) - break; - u8x8.print(singleJsonSymbol); - printedChars++; - } - if ((qComma > knownMode) || (printedChars > u8x8.getCols() - 2)) - break; - } + extractModeName(knownPalette, JSON_palette_names, lineBuffer, 16); + u8x8.print(lineBuffer); u8x8.setFont(u8x8_font_open_iconic_embedded_1x1); u8x8.drawGlyph(0, 0, 80); // wifi icon diff --git a/usermods/PIR_sensor_switch/usermod_PIR_sensor_switch.h b/usermods/PIR_sensor_switch/usermod_PIR_sensor_switch.h index 8085d79a9a..d7abb84761 100644 --- a/usermods/PIR_sensor_switch/usermod_PIR_sensor_switch.h +++ b/usermods/PIR_sensor_switch/usermod_PIR_sensor_switch.h @@ -58,7 +58,6 @@ class PIRsensorSwitch : public Usermod byte prevPreset = 0; byte prevPlaylist = 0; - bool savedState = false; uint32_t offTimerStart = 0; // off timer start time byte NotifyUpdateMode = CALL_MODE_NO_NOTIFY; // notification mode for stateUpdated(): CALL_MODE_NO_NOTIFY or CALL_MODE_DIRECT_CHANGE @@ -77,6 +76,7 @@ class PIRsensorSwitch : public Usermod bool m_mqttOnly = false; // flag to send MQTT message only (assuming it is enabled) // flag to enable triggering only if WLED is initially off (LEDs are not on, preventing running effect being overwritten by PIR) bool m_offOnly = false; + bool m_offMode = offMode; // strings to reduce flash memory usage (used more than twice) static const char _name[]; @@ -118,17 +118,20 @@ class PIRsensorSwitch : public Usermod */ void switchStrip(bool switchOn) { - if (m_offOnly && bri && (switchOn || (!PIRtriggered && !switchOn))) return; + if (m_offOnly && bri && (switchOn || (!PIRtriggered && !switchOn))) return; //if lights on and off only, do nothing + if (PIRtriggered && switchOn) return; //if already on and triggered before, do nothing PIRtriggered = switchOn; if (switchOn) { if (m_onPreset) { - if (currentPlaylist>0) prevPlaylist = currentPlaylist; - else if (currentPreset>0) prevPreset = currentPreset; - else { + if (currentPlaylist>0 && !offMode) { + prevPlaylist = currentPlaylist; + unloadPlaylist(); + } else if (currentPreset>0 && !offMode) { + prevPreset = currentPreset; + } else { saveTemporaryPreset(); - savedState = true; prevPlaylist = 0; - prevPreset = 0; + prevPreset = 255; } applyPreset(m_onPreset, NotifyUpdateMode); return; @@ -140,20 +143,17 @@ class PIRsensorSwitch : public Usermod } } else { if (m_offPreset) { - applyPreset(m_offPreset, NotifyUpdateMode); + if (currentPreset==m_onPreset || currentPlaylist==m_onPreset) applyPreset(m_offPreset, NotifyUpdateMode); return; } else if (prevPlaylist) { - applyPreset(prevPlaylist, NotifyUpdateMode); + if (currentPreset==m_onPreset || currentPlaylist==m_onPreset) applyPreset(prevPlaylist, NotifyUpdateMode); prevPlaylist = 0; return; } else if (prevPreset) { - applyPreset(prevPreset, NotifyUpdateMode); + if (prevPreset<255) { if (currentPreset==m_onPreset || currentPlaylist==m_onPreset) applyPreset(prevPreset, NotifyUpdateMode); } + else { if (currentPreset==m_onPreset || currentPlaylist==m_onPreset) applyTemporaryPreset(); } prevPreset = 0; return; - } else if (savedState) { - applyTemporaryPreset(); - savedState = false; - return; } // preset not assigned if (bri != 0) { @@ -188,10 +188,12 @@ class PIRsensorSwitch : public Usermod if (sensorPinState == HIGH) { offTimerStart = 0; if (!m_mqttOnly && (!m_nightTimeOnly || (m_nightTimeOnly && !isDayTime()))) switchStrip(true); + else if (NotifyUpdateMode != CALL_MODE_NO_NOTIFY) updateInterfaces(CALL_MODE_WS_SEND); publishMqtt("on"); - } else /*if (bri != 0)*/ { + } else { // start switch off timer offTimerStart = millis(); + if (NotifyUpdateMode != CALL_MODE_NO_NOTIFY) updateInterfaces(CALL_MODE_WS_SEND); } return true; } @@ -203,14 +205,13 @@ class PIRsensorSwitch : public Usermod */ bool handleOffTimer() { - if (offTimerStart > 0 && millis() - offTimerStart > m_switchOffDelay) - { - if (enabled == true) - { + if (offTimerStart > 0 && millis() - offTimerStart > m_switchOffDelay) { + offTimerStart = 0; + if (enabled == true) { if (!m_mqttOnly && (!m_nightTimeOnly || (m_nightTimeOnly && !isDayTime()))) switchStrip(false); + else if (NotifyUpdateMode != CALL_MODE_NO_NOTIFY) updateInterfaces(CALL_MODE_WS_SEND); publishMqtt("off"); } - offTimerStart = 0; return true; } return false; @@ -274,20 +275,9 @@ class PIRsensorSwitch : public Usermod JsonObject user = root["u"]; if (user.isNull()) user = root.createNestedObject("u"); - String uiDomString = F(""); - JsonArray infoArr = user.createNestedArray(uiDomString); // timer value + JsonArray infoArr = user.createNestedArray(FPSTR(_name)); + String uiDomString; if (enabled) { if (offTimerStart > 0) { @@ -320,6 +310,24 @@ class PIRsensorSwitch : public Usermod } else { infoArr.add(F("disabled")); } + + uiDomString = F(" "); + infoArr.add(uiDomString); + + JsonObject sensor = root[F("sensor")]; + if (sensor.isNull()) sensor = root.createNestedObject(F("sensor")); + sensor[F("motion")] = sensorPinState || offTimerStart>0 ? true : false; } /** diff --git a/usermods/PWM_fan/readme.md b/usermods/PWM_fan/readme.md index 976d6b24c2..8cd04a91e6 100644 --- a/usermods/PWM_fan/readme.md +++ b/usermods/PWM_fan/readme.md @@ -30,7 +30,16 @@ _NOTE:_ You may also need to tweak Dallas Temperature usermod sampling frequency No special requirements. +## Control PWM fan speed using JSON API + +You can use e.g. `{"PWM-fan":{"speed":30,"lock":true}}` to set fan speed to 30 percent of maximum speed (replace 30 with arbitrary value between 0 and 100) and lock the speed. +If you include `speed` property you can set fan speed in percent (%) of maximum speed. +If you include `lock` property you can lock (_true_) or unlock (_false_) fan speed. +If the fan speed is unlocked it will revert to temperature controlled speed on next update cycle. Once fan speed is locked it will remain so until it is unlocked by next API call. + ## Change Log 2021-10 * First public release +2022-05 +* Added JSON API call to allow changing of speed \ No newline at end of file diff --git a/usermods/PWM_fan/usermod_PWM_fan.h b/usermods/PWM_fan/usermod_PWM_fan.h index 943ba1ae66..6dfe12facf 100644 --- a/usermods/PWM_fan/usermod_PWM_fan.h +++ b/usermods/PWM_fan/usermod_PWM_fan.h @@ -38,6 +38,7 @@ class PWMFanUsermod : public Usermod { #ifdef ARDUINO_ARCH_ESP32 uint8_t pwmChannel = 255; #endif + bool lockFan = false; #ifdef USERMOD_DALLASTEMPERATURE UsermodTemperature* tempUM; @@ -50,6 +51,7 @@ class PWMFanUsermod : public Usermod { float targetTemperature = 25.0; uint8_t minPWMValuePct = 50; uint8_t numberOfInterrupsInOneSingleRotation = 2; // Number of interrupts ESP32 sees on tacho signal on a single fan rotation. All the fans I've seen trigger two interrups. + uint8_t pwmValuePct = 0; // strings to reduce flash memory usage (used more than twice) static const char _name[]; @@ -60,6 +62,8 @@ class PWMFanUsermod : public Usermod { static const char _tachoUpdateSec[]; static const char _minPWMValuePct[]; static const char _IRQperRotation[]; + static const char _speed[]; + static const char _lock[]; void initTacho(void) { if (tachoPin < 0 || !pinManager.allocatePin(tachoPin, false, PinOwner::UM_Unspecified)){ @@ -80,6 +84,8 @@ class PWMFanUsermod : public Usermod { } void updateTacho(void) { + // store milliseconds when tacho was measured the last time + msLastTachoMeasurement = millis(); if (tachoPin < 0) return; // start of tacho measurement @@ -90,8 +96,6 @@ class PWMFanUsermod : public Usermod { last_rpm /= tachoUpdateSec; // reset counter counter_rpm = 0; - // store milliseconds when tacho was measured the last time - msLastTachoMeasurement = millis(); // attach interrupt again attachInterrupt(digitalPinToInterrupt(tachoPin), rpm_fan, FALLING); } @@ -155,7 +159,7 @@ class PWMFanUsermod : public Usermod { int pwmStep = ((100 - minPWMValuePct) * newPWMvalue) / (7*100); int pwmMinimumValue = (minPWMValuePct * newPWMvalue) / 100; - if ((temp == NAN) || (temp <= 0.0)) { + if ((temp == NAN) || (temp <= -100.0)) { DEBUG_PRINTLN(F("WARNING: no temperature value available. Cannot do temperature control. Will set PWM fan to 255.")); } else if (difftemp <= 0.0) { // Temperature is below target temperature. Run fan at minimum speed. @@ -205,7 +209,7 @@ class PWMFanUsermod : public Usermod { if ((now - msLastTachoMeasurement) < (tachoUpdateSec * 1000)) return; updateTacho(); - setFanPWMbasedOnTemperature(); + if (!lockFan) setFanPWMbasedOnTemperature(); } /* @@ -214,12 +218,30 @@ class PWMFanUsermod : public Usermod { * Below it is shown how this could be used for e.g. a light sensor */ void addToJsonInfo(JsonObject& root) { - if (tachoPin < 0) return; - JsonObject user = root["u"]; - if (user.isNull()) user = root.createNestedObject("u"); - JsonArray data = user.createNestedArray(FPSTR(_name)); - data.add(last_rpm); - data.add(F("rpm")); + if (enabled) { + JsonObject user = root["u"]; + if (user.isNull()) user = root.createNestedObject("u"); +// if (!tempUM) { + JsonArray infoArr = user.createNestedArray(F("Fan speed [%]")); + String uiDomString = F("
"); // + infoArr.add(uiDomString); +// } + if (tachoPin >= 0) { + JsonArray data = user.createNestedArray(FPSTR(_name)); + data.add(last_rpm); + data.add(F("rpm")); + } else { + JsonArray data = user.createNestedArray(FPSTR(_name)); + if (lockFan) data.add(F("locked")); + else data.add(F("auto")); + } + } } /* @@ -233,9 +255,19 @@ class PWMFanUsermod : public Usermod { * readFromJsonState() can be used to receive data clients send to the /json/state part of the JSON API (state object). * Values in the state object may be modified by connected clients */ - //void readFromJsonState(JsonObject& root) { - // if (!initDone) return; // prevent crash on boot applyPreset() - //} + void readFromJsonState(JsonObject& root) { + if (!initDone || !enabled) return; // prevent crash on boot applyPreset() + JsonObject usermod = root[FPSTR(_name)]; + if (!usermod.isNull()) { + if (!usermod[FPSTR(_speed)].isNull() && usermod[FPSTR(_speed)].is()) { + int pwmValuePct = usermod[FPSTR(_speed)].as(); + updateFanSpeed((MAX(0,MIN(100,pwmValuePct)) * 255) / 100); + } + if (!usermod[FPSTR(_lock)].isNull() && usermod[FPSTR(_lock)].is()) { + lockFan = usermod[FPSTR(_lock)].as(); + } + } + } /* * addToConfig() can be used to add custom persistent settings to the cfg.json file in the "um" (usermod) object. @@ -337,3 +369,5 @@ const char PWMFanUsermod::_temperature[] PROGMEM = "target-temp-C"; const char PWMFanUsermod::_tachoUpdateSec[] PROGMEM = "tacho-update-s"; const char PWMFanUsermod::_minPWMValuePct[] PROGMEM = "min-PWM-percent"; const char PWMFanUsermod::_IRQperRotation[] PROGMEM = "IRQs-per-rotation"; +const char PWMFanUsermod::_speed[] PROGMEM = "speed"; +const char PWMFanUsermod::_lock[] PROGMEM = "lock"; diff --git a/usermods/QuinLED_Dig_Uno_Temp_MQTT/readme.md b/usermods/QuinLED_Dig_Uno_Temp_MQTT/readme.md deleted file mode 100644 index 60fc31f73f..0000000000 --- a/usermods/QuinLED_Dig_Uno_Temp_MQTT/readme.md +++ /dev/null @@ -1,34 +0,0 @@ -# QuinLED Dig Uno board - -These files allow WLED 0.9.1 to report the temp sensor on the Quinled board to MQTT. I use it to report the board temp to Home Assistant via MQTT, so it will send notifications if something happens and the board start to heat up. -This code uses Aircookie's WLED software. It has a premade file for user modifications. I use it to publish the temperature from the dallas temperature sensor on the Quinled board. The entries for the top of the WLED00 file, initializes the required libraries, and variables for the sensor. The .ino file waits for 60 seconds, and checks to see if the MQTT server is connected (thanks Aircoookie). It then poles the sensor, and published it using the MQTT service already running, using the main topic programmed in the WLED UI. - -Installation of file: Copy and replace file in wled00 directory - -## Project link - -* [QuinLED-Dig-Uno](https://quinled.info/2018/09/15/quinled-dig-uno/) - Project link - -### Platformio requirements - -Uncomment `DallasTemperature@~3.8.0`,`OneWire@~2.3.5 under` `[common]` section in `platformio.ini`: - -```ini -# platformio.ini -... -[platformio] -... -; default_envs = esp07 -default_envs = d1_mini -... -[common] -... -lib_deps_external = - ... - #For use SSD1306 OLED display uncomment following - U8g2@~2.27.3 - #For Dallas sensor uncomment following 2 lines - DallasTemperature@~3.8.0 - OneWire@~2.3.5 -... -``` diff --git a/usermods/QuinLED_Dig_Uno_Temp_MQTT/usermod.cpp b/usermods/QuinLED_Dig_Uno_Temp_MQTT/usermod.cpp deleted file mode 100644 index 5b4e2e5c7e..0000000000 --- a/usermods/QuinLED_Dig_Uno_Temp_MQTT/usermod.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include -#include "wled.h" -//Intiating code for QuinLED Dig-Uno temp sensor -//Uncomment Celsius if that is your prefered temperature scale -#include //Dallastemperature sensor -#ifdef ARDUINO_ARCH_ESP32 //ESP32 boards -OneWire oneWire(18); -#else //ESP8266 boards -OneWire oneWire(14); -#endif -DallasTemperature sensor(&oneWire); -long temptimer = millis(); -long lastMeasure = 0; -#define Celsius // Show temperature mesaurement in Celcius otherwise is in Fahrenheit -void userSetup() -{ -// Start the DS18B20 sensor - sensor.begin(); -} - -//gets called every time WiFi is (re-)connected. Initialize own network interfaces here -void userConnected() -{ - -} - -void userLoop() -{ - temptimer = millis(); - -// Timer to publishe new temperature every 60 seconds - if (temptimer - lastMeasure > 60000) { - lastMeasure = temptimer; - -//Check if MQTT Connected, otherwise it will crash the 8266 - if (mqtt != nullptr){ - sensor.requestTemperatures(); - -//Gets prefered temperature scale based on selection in definitions section - #ifdef Celsius - float board_temperature = sensor.getTempCByIndex(0); - #else - float board_temperature = sensors.getTempFByIndex(0); - #endif - -//Create character string populated with user defined device topic from the UI, and the read temperature. Then publish to MQTT server. - char subuf[38]; - strcpy(subuf, mqttDeviceTopic); - strcat(subuf, "/temperature"); - mqtt->publish(subuf, 0, true, String(board_temperature).c_str()); - return;} - return;} -return; -} diff --git a/usermods/RTC/usermod_rtc.h b/usermods/RTC/usermod_rtc.h index 8c174e6fa8..fd9a405446 100644 --- a/usermods/RTC/usermod_rtc.h +++ b/usermods/RTC/usermod_rtc.h @@ -3,14 +3,6 @@ #include "src/dependencies/time/DS1307RTC.h" #include "wled.h" -#ifdef ARDUINO_ARCH_ESP32 - #define HW_PIN_SCL 22 - #define HW_PIN_SDA 21 -#else - #define HW_PIN_SCL 5 - #define HW_PIN_SDA 4 -#endif - //Connect DS1307 to standard I2C pins (ESP32: GPIO 21 (SDA)/GPIO 22 (SCL)) class RTCUsermod : public Usermod { @@ -20,8 +12,9 @@ class RTCUsermod : public Usermod { public: void setup() { - PinManagerPinType pins[2] = { { HW_PIN_SCL, true }, { HW_PIN_SDA, true } }; + PinManagerPinType pins[2] = { { i2c_scl, true }, { i2c_sda, true } }; if (!pinManager.allocateMultiplePins(pins, 2, PinOwner::HW_I2C)) { disabled = true; return; } + RTC.begin(); time_t rtcTime = RTC.get(); if (rtcTime) { toki.setTime(rtcTime,TOKI_NO_MS_ACCURACY,TOKI_TS_RTC); @@ -44,13 +37,13 @@ class RTCUsermod : public Usermod { * It will be called by WLED when settings are actually saved (for example, LED settings are saved) * I highly recommend checking out the basics of ArduinoJson serialization and deserialization in order to use custom settings! */ - void addToConfig(JsonObject& root) - { - JsonObject top = root.createNestedObject("RTC"); - JsonArray pins = top.createNestedArray("pin"); - pins.add(HW_PIN_SCL); - pins.add(HW_PIN_SDA); - } +// void addToConfig(JsonObject& root) +// { +// JsonObject top = root.createNestedObject("RTC"); +// JsonArray pins = top.createNestedArray("pin"); +// pins.add(i2c_scl); +// pins.add(i2c_sda); +// } uint16_t getId() { diff --git a/usermods/ST7789_display/README.md b/usermods/ST7789_display/README.md index b98d5be002..9309218605 100644 --- a/usermods/ST7789_display/README.md +++ b/usermods/ST7789_display/README.md @@ -2,11 +2,14 @@ This usermod allow to use 240x240 display to display following: +* current date and time; * Network SSID; * IP address; +* WiFi signal strength; * Brightness; * Chosen effect; * Chosen palette; +* effect speed and intensity; * Estimated current in mA; ## Hardware @@ -46,27 +49,29 @@ Add lines to section: default_envs = esp32dev build_flags = ${common.build_flags_esp32} -D USERMOD_ST7789_DISPLAY - + -DUSER_SETUP_LOADED=1 + -DST7789_DRIVER=1 + -DTFT_WIDTH=240 + -DTFT_HEIGHT=240 + -DCGRAM_OFFSET=1 + -DTFT_MOSI=21 + -DTFT_SCLK=22 + -DTFT_DC=27 + -DTFT_RST=26 + -DTFT_BL=14 + -DLOAD_GLCD=1 + ;optional for WROVER + ;-DCONFIG_SPIRAM_SUPPORT=1 ``` Save the `platformio.ini` file. Once this is saved, the required library files should be automatically downloaded for modifications in a later step. ### TFT_eSPI Library Adjustments -We need to modify a file in the `TFT_eSPI` library. If you followed the directions to modify and save the `platformio.ini` file above, the `User_Setup_Select.h` file can be found in the `/.pio/libdeps/esp32dev/TFT_eSPI` folder. - -Modify the `User_Setup_Select.h` file as follows: - -* Comment out the following line (which is the 'default' setup file): - -```ini -//#include // Default setup is root library folder -``` +If you are not using PlatformIO you need to modify a file in the `TFT_eSPI` library. If you followed the directions to modify and save the `platformio.ini` file above, the `Setup24_ST7789.h` file can be found in the `/.pio/libdeps/esp32dev/TFT_eSPI/User_Setups/` folder. -* Add following line: +Edit `Setup_ST7789.h` file and uncomment nad changep GPIO pin numbers in lines containing `TFT_MOSI`, `TFT_SCLK`, `TFT_RST`, `TFT_DC`. -```ini -#include // Setup file for ESP32 ST7789V SPI bus TFT -``` +Modify the `User_Setup_Select.h` by uncommentig the line containing `#include ` and commenting out line containing `#include `. -* Copy file `"Setup_ST7789_Display.h"` from usermod folder to `/.pio/libdeps/esp32dev/TFT_eSPI/User_Setups` +If your display includes backlight enable pin, #define TFT_BL with backlight enable GPIO number. \ No newline at end of file diff --git a/usermods/ST7789_display/ST7789_display.h b/usermods/ST7789_display/ST7789_display.h index 19ad5790ae..93700c9182 100644 --- a/usermods/ST7789_display/ST7789_display.h +++ b/usermods/ST7789_display/ST7789_display.h @@ -7,33 +7,55 @@ #include #include -#define USERMOD_ST7789_DISPLAY 97 - -#ifndef TFT_DISPOFF -#define TFT_DISPOFF 0x28 +#ifndef USER_SETUP_LOADED + #ifndef ST7789_DRIVER + #error Please define ST7789_DRIVER + #endif + #ifndef TFT_WIDTH + #error Please define TFT_WIDTH + #endif + #ifndef TFT_HEIGHT + #error Please define TFT_HEIGHT + #endif + #ifndef TFT_MOSI + #error Please define TFT_MOSI + #endif + #ifndef TFT_SCLK + #error Please define TFT_SCLK + #endif + #ifndef TFT_DC + #error Please define TFT_DC + #endif + #ifndef TFT_RST + #error Please define TFT_RST + #endif + #ifndef LOAD_GLCD + #error Please define LOAD_GLCD + #endif #endif - -#ifndef TFT_SLPIN -#define TFT_SLPIN 0x10 +#ifndef TFT_BL + #define TFT_BL -1 #endif -#define TFT_MOSI 21 -#define TFT_SCLK 22 -#define TFT_DC 18 -#define TFT_RST 5 -#define TFT_BL 26 // Display backlight control pin +#define USERMOD_ID_ST7789_DISPLAY 97 -TFT_eSPI tft = TFT_eSPI(240, 240); // Invoke custom library +TFT_eSPI tft = TFT_eSPI(TFT_WIDTH, TFT_HEIGHT); // Invoke custom library + +// Extra char (+1) for null +#define LINE_BUFFER_SIZE 20 // How often we are redrawing screen #define USER_LOOP_REFRESH_RATE_MS 1000 +extern int getSignalQuality(int rssi); + //class name. Use something descriptive and leave the ": public Usermod" part :) class St7789DisplayUsermod : public Usermod { private: //Private class members. You can declare variables and functions only accessible to your usermod here unsigned long lastTime = 0; + bool enabled = true; bool displayTurnedOff = false; long lastRedraw = 0; @@ -45,9 +67,70 @@ class St7789DisplayUsermod : public Usermod { uint8_t knownBrightness = 0; uint8_t knownMode = 0; uint8_t knownPalette = 0; - uint8_t tftcharwidth = 19; // Number of chars that fit on screen with text size set to 2 + uint8_t knownEffectSpeed = 0; + uint8_t knownEffectIntensity = 0; + uint8_t knownMinute = 99; + uint8_t knownHour = 99; + + const uint8_t tftcharwidth = 19; // Number of chars that fit on screen with text size set to 2 long lastUpdate = 0; + void center(String &line, uint8_t width) { + int len = line.length(); + if (len0; i--) line = ' ' + line; + for (byte i=line.length(); i 12) { + showHour -= 12; + isAM = false; + } else { + isAM = true; + } + } + + sprintf_P(lineBuffer, PSTR("%2d:%02d"), (useAMPM ? showHour : hourCurrent), minuteCurrent); + tft.setTextColor(TFT_WHITE); + tft.setTextSize(4); + tft.setCursor(60, 24); + tft.print(lineBuffer); + + tft.setTextSize(2); + tft.setCursor(186, 24); + //sprintf_P(lineBuffer, PSTR("%02d"), secondCurrent); + if (useAMPM) tft.print(isAM ? "AM" : "PM"); + //else tft.print(lineBuffer); + } + public: //Functions called by WLED @@ -57,6 +140,9 @@ class St7789DisplayUsermod : public Usermod { */ void setup() { + PinManagerPinType pins[] = { { TFT_MOSI, true }, { TFT_MISO, false}, { TFT_SCLK, true }, { TFT_CS, true}, { TFT_DC, true}, { TFT_RST, true }, { TFT_BL, true } }; + if (!pinManager.allocateMultiplePins(pins, 7, PinOwner::UM_FourLineDisplay)) { enabled = false; return; } + tft.init(); tft.setRotation(0); //Rotation here is set up for the text to be readable with the port on the left. Use 1 to flip. tft.fillScreen(TFT_BLACK); @@ -65,10 +151,10 @@ class St7789DisplayUsermod : public Usermod { tft.setTextDatum(MC_DATUM); tft.setTextSize(2); tft.print("Loading..."); - if (TFT_BL > 0) - { // TFT_BL has been set in the TFT_eSPI library - pinMode(TFT_BL, OUTPUT); // Set backlight pin to output mode - digitalWrite(TFT_BL, HIGH); // Turn backlight on. + if (TFT_BL >= 0) + { + pinMode(TFT_BL, OUTPUT); // Set backlight pin to output mode + digitalWrite(TFT_BL, HIGH); // Turn backlight on. } } @@ -91,192 +177,153 @@ class St7789DisplayUsermod : public Usermod { * Instead, use a timer check as shown here. */ void loop() { -// Check if we time interval for redrawing passes. - if (millis() - lastUpdate < USER_LOOP_REFRESH_RATE_MS) + char buff[LINE_BUFFER_SIZE]; + + // Check if we time interval for redrawing passes. + if (millis() - lastUpdate < USER_LOOP_REFRESH_RATE_MS) { return; } - lastUpdate = millis(); + lastUpdate = millis(); -// Turn off display after 5 minutes with no change. - if(!displayTurnedOff && millis() - lastRedraw > 5*60*1000) + // Turn off display after 5 minutes with no change. + if (!displayTurnedOff && millis() - lastRedraw > 5*60*1000) { - digitalWrite(TFT_BL, LOW); // Turn backlight off. + if (TFT_BL >= 0) digitalWrite(TFT_BL, LOW); // Turn backlight off. displayTurnedOff = true; } -// Check if values which are shown on display changed from the last time. - if (((apActive) ? String(apSSID) : WiFi.SSID()) != knownSsid) - { - needRedraw = true; - } - else if (knownIp != (apActive ? IPAddress(4, 3, 2, 1) : WiFi.localIP())) - { - needRedraw = true; - } - else if (knownBrightness != bri) - { - needRedraw = true; - } - else if (knownMode != strip.getMainSegment().mode) - { - needRedraw = true; - } - else if (knownPalette != strip.getMainSegment().palette) - { - needRedraw = true; - } + // Check if values which are shown on display changed from the last time. + if ((((apActive) ? String(apSSID) : WiFi.SSID()) != knownSsid) || + (knownIp != (apActive ? IPAddress(4, 3, 2, 1) : Network.localIP())) || + (knownBrightness != bri) || + (knownEffectSpeed != strip.getMainSegment().speed) || + (knownEffectIntensity != strip.getMainSegment().intensity) || + (knownMode != strip.getMainSegment().mode) || + (knownPalette != strip.getMainSegment().palette)) + { + needRedraw = true; + } - if (!needRedraw) - { - return; - } - needRedraw = false; - - if (displayTurnedOff) - { - digitalWrite(TFT_BL, TFT_BACKLIGHT_ON); // Turn backlight on. - displayTurnedOff = false; - } - lastRedraw = millis(); + if (!needRedraw) + { + return; + } + needRedraw = false; + + if (displayTurnedOff) + { + digitalWrite(TFT_BL, HIGH); // Turn backlight on. + displayTurnedOff = false; + } + lastRedraw = millis(); + + // Update last known values. + #if defined(ESP8266) + knownSsid = apActive ? WiFi.softAPSSID() : WiFi.SSID(); + #else + knownSsid = WiFi.SSID(); + #endif + knownIp = apActive ? IPAddress(4, 3, 2, 1) : WiFi.localIP(); + knownBrightness = bri; + knownMode = strip.getMainSegment().mode; + knownPalette = strip.getMainSegment().palette; + knownEffectSpeed = strip.getMainSegment().speed; + knownEffectIntensity = strip.getMainSegment().intensity; -// Update last known values. - #if defined(ESP8266) - knownSsid = apActive ? WiFi.softAPSSID() : WiFi.SSID(); - #else - knownSsid = WiFi.SSID(); - #endif - knownIp = apActive ? IPAddress(4, 3, 2, 1) : WiFi.localIP(); - knownBrightness = bri; - knownMode = strip.getMainSegment().mode; - knownPalette = strip.getMainSegment().palette; - - tft.fillScreen(TFT_BLACK); - tft.setTextSize(2); -// First row with Wifi name - tft.setTextColor(TFT_SILVER); - tft.setCursor(3, 40); - tft.print(knownSsid.substring(0, tftcharwidth > 1 ? tftcharwidth - 1 : 0)); -// Print `~` char to indicate that SSID is longer, than our dicplay - if (knownSsid.length() > tftcharwidth) - tft.print("~"); - -// Second row with AP IP and Password or IP - tft.setTextColor(TFT_GREEN); - tft.setTextSize(2); - tft.setCursor(3, 64); -// Print AP IP and password in AP mode or knownIP if AP not active. - - if (apActive) - { - tft.setTextColor(TFT_YELLOW); - tft.print("AP IP: "); - tft.print(knownIp); - tft.setCursor(3,86); - tft.setTextColor(TFT_YELLOW); - tft.print("AP Pass:"); - tft.print(apPass); - } - else - { - tft.setTextColor(TFT_GREEN); - tft.print("IP: "); - tft.print(knownIp); - tft.setCursor(3,86); - //tft.print("Signal Strength: "); - //tft.print(i.wifi.signal); - tft.setTextColor(TFT_WHITE); - tft.print("Bri: "); - tft.print(((float(bri)/255)*100),0); - tft.print("%"); - } + tft.fillScreen(TFT_BLACK); -// Third row with mode name - tft.setCursor(3, 108); - uint8_t qComma = 0; - bool insideQuotes = false; - uint8_t printedChars = 0; - char singleJsonSymbol; -// Find the mode name in JSON - for (size_t i = 0; i < strlen_P(JSON_mode_names); i++) - { - singleJsonSymbol = pgm_read_byte_near(JSON_mode_names + i); - switch (singleJsonSymbol) + showTime(); + + tft.setTextSize(2); + + // Wifi name + tft.setTextColor(TFT_GREEN); + tft.setCursor(0, 60); + String line = knownSsid.substring(0, tftcharwidth-1); + // Print `~` char to indicate that SSID is longer, than our display + if (knownSsid.length() > tftcharwidth) line = line.substring(0, tftcharwidth-1) + '~'; + center(line, tftcharwidth); + tft.print(line.c_str()); + + // Print AP IP and password in AP mode or knownIP if AP not active. + if (apActive) { - case '"': - insideQuotes = !insideQuotes; - break; - case '[': - case ']': - break; - case ',': - qComma++; - default: - if (!insideQuotes || (qComma != knownMode)) - break; - tft.setTextColor(TFT_MAGENTA); - tft.print(singleJsonSymbol); - printedChars++; + tft.setCursor(0, 84); + tft.print("AP IP: "); + tft.print(knownIp); + tft.setCursor(0,108); + tft.print("AP Pass:"); + tft.print(apPass); } - if ((qComma > knownMode) || (printedChars > tftcharwidth - 1)) - break; - } -// Fourth row with palette name - tft.setTextColor(TFT_YELLOW); - tft.setCursor(3, 130); - qComma = 0; - insideQuotes = false; - printedChars = 0; -// Looking for palette name in JSON. - for (size_t i = 0; i < strlen_P(JSON_palette_names); i++) - { - singleJsonSymbol = pgm_read_byte_near(JSON_palette_names + i); - switch (singleJsonSymbol) + else { - case '"': - insideQuotes = !insideQuotes; - break; - case '[': - case ']': - break; - case ',': - qComma++; - default: - if (!insideQuotes || (qComma != knownPalette)) - break; - tft.print(singleJsonSymbol); - printedChars++; + tft.setCursor(0, 84); + line = knownIp.toString(); + center(line, tftcharwidth); + tft.print(line.c_str()); + // percent brightness + tft.setCursor(0, 120); + tft.setTextColor(TFT_WHITE); + tft.print("Bri: "); + tft.print((((int)bri*100)/255)); + tft.print("%"); + // signal quality + tft.setCursor(124,120); + tft.print("Sig: "); + if (getSignalQuality(WiFi.RSSI()) < 10) { + tft.setTextColor(TFT_RED); + } else if (getSignalQuality(WiFi.RSSI()) < 25) { + tft.setTextColor(TFT_ORANGE); + } else { + tft.setTextColor(TFT_GREEN); + } + tft.print(getSignalQuality(WiFi.RSSI())); + tft.setTextColor(TFT_WHITE); + tft.print("%"); } -// The following is modified from the code from the u8g2/u8g8 based code (knownPalette was knownMode) - if ((qComma > knownPalette) || (printedChars > tftcharwidth - 1)) - break; - } -// Fifth row with estimated mA usage - tft.setTextColor(TFT_SILVER); - tft.setCursor(3, 152); -// Print estimated milliamp usage (must specify the LED type in LED prefs for this to be a reasonable estimate). - tft.print("Current: "); - tft.print(strip.currentMilliamps); - tft.print("mA"); + + // mode name + tft.setTextColor(TFT_CYAN); + tft.setCursor(0, 144); + char lineBuffer[tftcharwidth+1]; + extractModeName(knownMode, JSON_mode_names, lineBuffer, tftcharwidth); + tft.print(lineBuffer); + + // palette name + tft.setTextColor(TFT_YELLOW); + tft.setCursor(0, 168); + extractModeName(knownPalette, JSON_palette_names, lineBuffer, tftcharwidth); + tft.print(lineBuffer); + + tft.setCursor(0, 192); + tft.setTextColor(TFT_SILVER); + sprintf_P(buff, PSTR("FX Spd:%3d Int:%3d"), effectSpeed, effectIntensity); + tft.print(buff); + + // Fifth row with estimated mA usage + tft.setTextColor(TFT_SILVER); + tft.setCursor(0, 216); + // Print estimated milliamp usage (must specify the LED type in LED prefs for this to be a reasonable estimate). + tft.print("Current: "); + tft.setTextColor(TFT_ORANGE); + tft.print(strip.currentMilliamps); + tft.print("mA"); } + /* * addToJsonInfo() can be used to add custom entries to the /json/info part of the JSON API. * Creating an "u" object allows you to add custom key/value pairs to the Info section of the WLED web UI. * Below it is shown how this could be used for e.g. a light sensor */ - /* void addToJsonInfo(JsonObject& root) { - int reading = 20; - //this code adds "u":{"Light":[20," lux"]} to the info object JsonObject user = root["u"]; if (user.isNull()) user = root.createNestedObject("u"); - JsonArray lightArr = user.createNestedArray("Light"); //name - lightArr.add(reading); //value - lightArr.add(" lux"); //unit + JsonArray lightArr = user.createNestedArray("ST7789"); //name + lightArr.add(enabled?F("installed"):F("disabled")); //unit } - */ /* @@ -295,7 +342,7 @@ class St7789DisplayUsermod : public Usermod { */ void readFromJsonState(JsonObject& root) { - userVar0 = root["user0"] | userVar0; //if "user0" key exists in JSON, update, else keep old value + //userVar0 = root["user0"] | userVar0; //if "user0" key exists in JSON, update, else keep old value //if (root["bri"] == 255) Serial.println(F("Don't burn down your garage!")); } @@ -316,8 +363,16 @@ class St7789DisplayUsermod : public Usermod { */ void addToConfig(JsonObject& root) { - JsonObject top = root.createNestedObject("exampleUsermod"); - top["great"] = userVar0; //save this var persistently whenever settings are saved + JsonObject top = root.createNestedObject("ST7789"); + JsonArray pins = top.createNestedArray("pin"); + pins.add(TFT_MOSI); + pins.add(TFT_MISO); + pins.add(TFT_SCLK); + pins.add(TFT_CS); + pins.add(TFT_DC); + pins.add(TFT_RST); + pins.add(TFT_BL); + //top["great"] = userVar0; //save this var persistently whenever settings are saved } @@ -329,10 +384,11 @@ class St7789DisplayUsermod : public Usermod { * but also that if you want to write persistent values to a dynamic buffer, you'd need to allocate it here instead of in setup. * If you don't know what that is, don't fret. It most likely doesn't affect your use case :) */ - void readFromConfig(JsonObject& root) + bool readFromConfig(JsonObject& root) { - JsonObject top = root["top"]; - userVar0 = top["great"] | 42; //The value right of the pipe "|" is the default value in case your setting was not present in cfg.json (e.g. first boot) + //JsonObject top = root["top"]; + //userVar0 = top["great"] | 42; //The value right of the pipe "|" is the default value in case your setting was not present in cfg.json (e.g. first boot) + return true; } @@ -342,7 +398,7 @@ class St7789DisplayUsermod : public Usermod { */ uint16_t getId() { - return USERMOD_ST7789_DISPLAY; + return USERMOD_ID_ST7789_DISPLAY; } //More methods can be added in the future, this example will then be extended. diff --git a/usermods/ST7789_display/Setup_ST7789_Display.h b/usermods/ST7789_display/Setup_ST7789_Display.h deleted file mode 100644 index 26d5c17ff4..0000000000 --- a/usermods/ST7789_display/Setup_ST7789_Display.h +++ /dev/null @@ -1,39 +0,0 @@ -// Setup for the ESP32 board with 1.5" 240x240 display - -// See SetupX_Template.h for all options available - -#define ST7789_DRIVER -#define TFT_SDA_READ // Display has a bidirectionsl SDA pin - -#define TFT_WIDTH 240 -#define TFT_HEIGHT 240 - -#define CGRAM_OFFSET // Library will add offsets required - -//#define TFT_MISO -1 - -#define TFT_MOSI 21 -#define TFT_SCLK 22 -//#define TFT_CS 5 -#define TFT_DC 18 -#define TFT_RST 5 - -#define TFT_BL 26 // Display backlight control pin - -#define TFT_BACKLIGHT_ON HIGH // HIGH or LOW are options - -#define LOAD_GLCD -#define LOAD_FONT2 -#define LOAD_FONT4 -#define LOAD_FONT6 -#define LOAD_FONT7 -#define LOAD_FONT8 -#define LOAD_GFXFF - -//#define SMOOTH_FONT - -//#define SPI_FREQUENCY 27000000 - #define SPI_FREQUENCY 40000000 // Maximum for ILI9341 - - -#define SPI_READ_FREQUENCY 6000000 // 6 MHz is the maximum SPI read speed for the ST7789V \ No newline at end of file diff --git a/usermods/TTGO-T-Display/usermod.cpp b/usermods/TTGO-T-Display/usermod.cpp index 9e08a001a1..b126d40a00 100644 --- a/usermods/TTGO-T-Display/usermod.cpp +++ b/usermods/TTGO-T-Display/usermod.cpp @@ -177,58 +177,15 @@ void userLoop() { // Third row with mode name tft.setCursor(1, 68); - uint8_t qComma = 0; - bool insideQuotes = false; - uint8_t printedChars = 0; - char singleJsonSymbol; - // Find the mode name in JSON - for (size_t i = 0; i < strlen_P(JSON_mode_names); i++) { - singleJsonSymbol = pgm_read_byte_near(JSON_mode_names + i); - switch (singleJsonSymbol) { - case '"': - insideQuotes = !insideQuotes; - break; - case '[': - case ']': - break; - case ',': - qComma++; - default: - if (!insideQuotes || (qComma != knownMode)) - break; - tft.print(singleJsonSymbol); - printedChars++; - } - if ((qComma > knownMode) || (printedChars > tftcharwidth - 1)) - break; - } + char lineBuffer[tftcharwidth+1]; + extractModeName(knownMode, JSON_mode_names, lineBuffer, tftcharwidth); + tft.print(lineBuffer); + // Fourth row with palette name tft.setCursor(1, 90); - qComma = 0; - insideQuotes = false; - printedChars = 0; - // Looking for palette name in JSON. - for (size_t i = 0; i < strlen_P(JSON_palette_names); i++) { - singleJsonSymbol = pgm_read_byte_near(JSON_palette_names + i); - switch (singleJsonSymbol) { - case '"': - insideQuotes = !insideQuotes; - break; - case '[': - case ']': - break; - case ',': - qComma++; - default: - if (!insideQuotes || (qComma != knownPalette)) - break; - tft.print(singleJsonSymbol); - printedChars++; - } - // The following is modified from the code from the u8g2/u8g8 based code (knownPalette was knownMode) - if ((qComma > knownPalette) || (printedChars > tftcharwidth - 1)) - break; - } + extractModeName(knownPalette, JSON_palette_names, lineBuffer, tftcharwidth); + tft.print(lineBuffer); + // Fifth row with estimated mA usage tft.setCursor(1, 112); // Print estimated milliamp usage (must specify the LED type in LED prefs for this to be a reasonable estimate). diff --git a/usermods/Temperature/usermod_temperature.h b/usermods/Temperature/usermod_temperature.h index 40df0e5333..a666639feb 100644 --- a/usermods/Temperature/usermod_temperature.h +++ b/usermods/Temperature/usermod_temperature.h @@ -46,6 +46,8 @@ class UsermodTemperature : public Usermod { bool enabled = true; + bool HApublished = false; + // strings to reduce flash memory usage (used more than twice) static const char _name[]; static const char _enabled[]; @@ -132,6 +134,28 @@ class UsermodTemperature : public Usermod { return false; } + void publishHomeAssistantAutodiscovery() { + if (!WLED_MQTT_CONNECTED) return; + + char json_str[1024], buf[128]; + size_t payload_size; + StaticJsonDocument<1024> json; + + sprintf_P(buf, PSTR("%s Temperature"), serverDescription); + json[F("name")] = buf; + strcpy(buf, mqttDeviceTopic); + strcat_P(buf, PSTR("/temperature")); + json[F("state_topic")] = buf; + json[F("device_class")] = F("temperature"); + json[F("unique_id")] = escapedMac.c_str(); + json[F("unit_of_measurement")] = F("°C"); + payload_size = serializeJson(json, json_str); + + sprintf_P(buf, PSTR("homeassistant/sensor/%s/config"), escapedMac.c_str()); + mqtt->publish(buf, 0, true, json_str, payload_size); + HApublished = true; + } + public: void setup() { @@ -206,6 +230,23 @@ class UsermodTemperature : public Usermod { } } + /** + * connected() is called every time the WiFi is (re)connected + * Use it to initialize network interfaces + */ + //void connected() {} + + /** + * subscribe to MQTT topic if needed + */ + void onMqttConnect(bool sessionPresent) { + //(re)subscribe to required topics + //char subuf[64]; + if (mqttDeviceTopic[0] != 0) { + publishHomeAssistantAutodiscovery(); + } + } + /* * API calls te enable data exchange between WLED modules */ @@ -229,7 +270,6 @@ class UsermodTemperature : public Usermod { if (user.isNull()) user = root.createNestedObject("u"); JsonArray temp = user.createNestedArray(FPSTR(_name)); - //temp.add(F("Loaded.")); if (temperature <= -100.0f) { temp.add(0); @@ -238,8 +278,13 @@ class UsermodTemperature : public Usermod { } temp.add(degC ? getTemperatureC() : getTemperatureF()); - if (degC) temp.add(F("°C")); - else temp.add(F("°F")); + temp.add(degC ? F("°C") : F("°F")); + + JsonObject sensor = root[F("sensor")]; + if (sensor.isNull()) sensor = root.createNestedObject(F("sensor")); + temp = sensor.createNestedArray(F("temp")); + temp.add(degC ? temperature : (float)temperature * 1.8f + 32); + temp.add(degC ? F("°C") : F("°F")); } /** diff --git a/usermods/UserModv2_SunRiseAndSet/README.md b/usermods/UserModv2_SunRiseAndSet/README.md deleted file mode 100644 index e989f08906..0000000000 --- a/usermods/UserModv2_SunRiseAndSet/README.md +++ /dev/null @@ -1,15 +0,0 @@ -WLED v2 UserMod for running macros at sunrise and sunset. - -At the time of this text, this user mod requires code to be changed to set certain variables: - 1. To reflect the user's graphical location (latitude/longitude) used for calculating apparent sunrise/sunset - 2. To specify which macros will be run at sunrise and/or sunset. (defaults to 15 at sunrise and 16 at sunset) - 3. To optionally provide an offset from sunrise/sunset, in minutes (max of +/- 2 hours), when the macro will be run. - -In addition, WLED must be configured to get time from NTP (and the time must be retrieved via NTP.) - -Please open the UserMod_SunRiseAndSet.h file for instructions on what needs to be changed, where to copy files, etc. - -If this usermod proves useful enough, the code might eventually be updated to allow prompting for the required information -via the web interface and to store settings in EEPROM instead of hard-coding in the .h file. - -This usermod has only been tested on the esp32dev platform, but there's no reason it wouldn't work on other platforms. diff --git a/usermods/UserModv2_SunRiseAndSet/UserMod_SunRiseAndSet.h b/usermods/UserModv2_SunRiseAndSet/UserMod_SunRiseAndSet.h deleted file mode 100644 index ef1bb37ec7..0000000000 --- a/usermods/UserModv2_SunRiseAndSet/UserMod_SunRiseAndSet.h +++ /dev/null @@ -1,166 +0,0 @@ -#pragma once - -#include "wled.h" -#include - -/* - * - * REQUIREMENTS: - * The Dusk2Dawn library must be installed. This can be found at https://github.com/dmkishi/Dusk2Dawn. The 1.0.1 version of this library found via - * Arduino or platformio library managers is buggy and won't compile. The latest version from github should be used. - * - * NTP must be enabled and functional. It simply makes no sense to have events on sunrise/sunset when an accurate time isn't available. - * - * The user's geographical latitude and longitude must be configured (in decimal, not degrees/minutes/etc) using m_fLatitude and m_fLongitude - * - * if desired, an offset of up to +/- 2 hours can be specified for each of sunrise/sunset using m_sunriseOffset and m_sunsetOffset (defaults to 0) - * - * The specific macro to run at sunrise and/or sunset can be changed using m_sunriseMacro and m_sunsetMacro. (defaults to 15 and 16) - * - * From the Dusk2Dawn library: - * HINT: An easy way to find the longitude and latitude for any location is - * to find the spot in Google Maps, right click the place on the map, and - * select "What's here?". At the bottom, you’ll see a card with the - * coordinates. - * - * Once configured, copy UserMod_SunRiseAndSet.h to the sketch file (the same folder as wled00.ino exists), - * and then edit "usermods_list.cpp": - * Add '#include "UserMod_SunRiseAndSet.h"' in the 'includes' area - * Add 'usermods.add(new UserMod_SunRiseAndSet());' in the registerUsermods() area - * - */ - -class UserMod_SunRiseAndSet : public Usermod -{ -private: - - /**** USER SETTINGS ****/ - - float m_fLatitude = 40.6; // latitude where sunrise/set are calculated - float m_fLongitude = -79.80; // longitude where sunrise/set are calculated - int8_t m_sunriseOffset = 0; // offset from sunrise, in minutes, when macro should be run (negative for before sunrise, positive for after sunrise) - int8_t m_sunsetOffset = 0; // offset from sunset, in minutes, when macro should be run (negative for before sunset, positive for after sunset) - uint8_t m_sunriseMacro = 15; // macro number to run at sunrise - uint8_t m_sunsetMacro = 16; // macro number to run at sunset - - /**** END OF USER SETTINGS. DO NOT EDIT BELOW THIS LINE! ****/ - - - Dusk2Dawn *m_pD2D = NULL; // this must be dynamically allocated in order for parameters to be loaded from EEPROM - - int m_nUserSunrise = -1; // time, in minutes from midnight, of sunrise - int m_nUserSunset = -1; // time, in minutes from midnight, of sunset - - byte m_nLastRunMinute = -1; // indicates what minute the userloop was last run - used so that the code only runs once per minute - -public: - - virtual void setup(void) - { - /* TODO: From EEPROM, load the following variables: - * - * int16_t latitude16 = 4060; // user provided latitude, multiplied by 100 and rounded - * int16_t longitude16 = -7980; // user provided longitude, multiplied by 100 and rounded. - * int8_t sunrise_offset = 0; // number of minutes to offset the sunrise macro trigger (positive for minutes after sunrise, negative for minutes before) - * int8_t sunset_offset = 0; // number of minutes to offset the sunset macro trigger (positive for minutes after sunset, negative for minutes before) - * - * then: - * m_fLatitude = (float)latitude / 100.0; - * m_fLongitude = (float)longitude / 100.0; - * m_sunriseOffset = sunrise_offset; - * m_sunsetOffset = sunset_offset; - */ - - if ((0.0 != m_fLatitude) || (0.0 != m_fLongitude)) - { - m_pD2D = new Dusk2Dawn (m_fLatitude, m_fLongitude, 0 /* UTC */); - // can't really check for failures. if the alloc fails, the mod just doesn't work. - } - } - - void loop(void) - { - // without NTP, or a configured lat/long, none of this stuff is going to work... - // As an alternative, need to figure out how to determine if the user has manually set the clock or not. - if (m_pD2D && (999000000L != ntpLastSyncTime)) - { - // to prevent needing to import all the timezone stuff from other modules, work completely in UTC - time_t timeUTC = toki.second(); - tmElements_t tmNow; - breakTime(timeUTC, tmNow); - int nCurMinute = tmNow.Minute; - - if (m_nLastRunMinute != nCurMinute) //only check once a new minute begins - { - m_nLastRunMinute = nCurMinute; - int numMinutes = (60 * tmNow.Hour) + m_nLastRunMinute; // how many minutes into the day are we? - - // check to see if sunrise/sunset should be re-determined. Only do this if neither sunrise nor sunset - // are set. That happens when the device has just stated, and after both sunrise/sunset have already run. - if ((-1 == m_nUserSunrise) && (-1 == m_nUserSunset)) - { - m_nUserSunrise = m_pD2D->sunrise(tmNow.Year + 1970, tmNow.Month, tmNow.Day, false) % 1440; - m_nUserSunset = m_pD2D->sunset(tmNow.Year + 1970, tmNow.Month, tmNow.Day, false) % 1440; - if (m_nUserSunrise > numMinutes) // has sunrise already passed? if so, recompute for tomorrow - { - breakTime(timeUTC + (60*60*24), tmNow); - m_nUserSunrise = m_pD2D->sunrise(tmNow.Year + 1970, tmNow.Month, tmNow.Day, false) % 1440; - if (m_nUserSunset > numMinutes) // if sunset has also passed, recompute that as well - { - m_nUserSunset = m_pD2D->sunset(tmNow.Year + 1970, tmNow.Month, tmNow.Day, false) % 1440; - } - } - // offset by user provided values. becuase the offsets are signed bytes, the max offset is just over 2 hours. - m_nUserSunrise += m_sunriseOffset; - m_nUserSunset += m_sunsetOffset; - } - - if (numMinutes == m_nUserSunrise) // Good Morning! - { - if (m_sunriseMacro) - applyMacro(m_sunriseMacro); // run macro 15 - m_nUserSunrise = -1; - } - else if (numMinutes == m_nUserSunset) // Good Night! - { - if (m_sunsetMacro) - applyMacro(m_sunsetMacro); // run macro 16 - m_nUserSunset = -1; - } - } // if (m_nLastRunMinute != nCurMinute) - } // if (m_pD2D && (999000000L != ntpLastSyncTime)) - } - - void addToJsonState(JsonObject& root) - { - JsonObject user = root["SunRiseAndSet"]; - if (user.isNull()) user = root.createNestedObject("SunRiseAndSet"); - - char buf[10]; - if (-1 != m_nUserSunrise) - { - snprintf(buf, 10, "%02d:%02d UTC", m_nUserSunrise / 60, m_nUserSunrise % 60); - user["rise"] = buf; - } - if (-1 != m_nUserSunset) - { - snprintf(buf, 10, "%02d:%02d UTC", m_nUserSunset / 60, m_nUserSunset % 60); - user["set"] = buf; - } - JsonObject vars = user.createNestedObject("vars"); - vars["lat"] = m_fLatitude; - vars["long"] = m_fLongitude; - vars["rise_mac"] = m_sunriseMacro; - vars["set_mac"] = m_sunsetMacro; - vars["rise_off"] = m_sunriseOffset; - vars["set_off"] = m_sunsetOffset; - } - - ~UserMod_SunRiseAndSet(void) - { - if (m_pD2D) delete m_pD2D; - } -}; - - - diff --git a/usermods/VL53L0X_gestures/usermod_vl53l0x_gestures.h b/usermods/VL53L0X_gestures/usermod_vl53l0x_gestures.h index 210ec3f585..91766b9be2 100644 --- a/usermods/VL53L0X_gestures/usermod_vl53l0x_gestures.h +++ b/usermods/VL53L0X_gestures/usermod_vl53l0x_gestures.h @@ -21,14 +21,6 @@ #include #include -#ifdef ARDUINO_ARCH_ESP32 - #define HW_PIN_SCL 22 - #define HW_PIN_SDA 21 -#else - #define HW_PIN_SCL 5 - #define HW_PIN_SDA 4 -#endif - #ifndef VL53L0X_MAX_RANGE_MM #define VL53L0X_MAX_RANGE_MM 230 // max height in millimiters to react for motions #endif @@ -59,7 +51,7 @@ class UsermodVL53L0XGestures : public Usermod { public: void setup() { - PinManagerPinType pins[2] = { { HW_PIN_SCL, true }, { HW_PIN_SDA, true } }; + PinManagerPinType pins[2] = { { i2c_scl, true }, { i2c_sda, true } }; if (!pinManager.allocateMultiplePins(pins, 2, PinOwner::HW_I2C)) { enabled = false; return; } Wire.begin(); @@ -127,13 +119,13 @@ class UsermodVL53L0XGestures : public Usermod { * It will be called by WLED when settings are actually saved (for example, LED settings are saved) * I highly recommend checking out the basics of ArduinoJson serialization and deserialization in order to use custom settings! */ - void addToConfig(JsonObject& root) - { - JsonObject top = root.createNestedObject("VL53L0x"); - JsonArray pins = top.createNestedArray("pin"); - pins.add(HW_PIN_SCL); - pins.add(HW_PIN_SDA); - } +// void addToConfig(JsonObject& root) +// { +// JsonObject top = root.createNestedObject("VL53L0x"); +// JsonArray pins = top.createNestedArray("pin"); +// pins.add(i2c_scl); +// pins.add(i2c_sda); +// } /* * getId() allows you to optionally give your V2 usermod an unique ID (please define it in const.h!). diff --git a/usermods/Wemos_D1_mini+Wemos32_mini_shield/usermod.cpp b/usermods/Wemos_D1_mini+Wemos32_mini_shield/usermod.cpp index 79241b5e20..c7eb8ee093 100644 --- a/usermods/Wemos_D1_mini+Wemos32_mini_shield/usermod.cpp +++ b/usermods/Wemos_D1_mini+Wemos32_mini_shield/usermod.cpp @@ -185,58 +185,14 @@ void userLoop() { // Third row with mode name u8x8.setCursor(2, 2); - uint8_t qComma = 0; - bool insideQuotes = false; - uint8_t printedChars = 0; - char singleJsonSymbol; - - // Find the mode name in JSON - for (size_t i = 0; i < strlen_P(JSON_mode_names); i++) { - singleJsonSymbol = pgm_read_byte_near(JSON_mode_names + i); - switch (singleJsonSymbol) { - case '"': - insideQuotes = !insideQuotes; - break; - case '[': - case ']': - break; - case ',': - qComma++; - default: - if (!insideQuotes || (qComma != knownMode)) - break; - u8x8.print(singleJsonSymbol); - printedChars++; - } - if ((qComma > knownMode) || (printedChars > u8x8.getCols() - 2)) - break; - } + char lineBuffer[17]; + extractModeName(knownMode, JSON_mode_names, lineBuffer, 16); + u8x8.print(lineBuffer); + // Fourth row with palette name u8x8.setCursor(2, 3); - qComma = 0; - insideQuotes = false; - printedChars = 0; - // Looking for palette name in JSON. - for (size_t i = 0; i < strlen_P(JSON_palette_names); i++) { - singleJsonSymbol = pgm_read_byte_near(JSON_palette_names + i); - switch (singleJsonSymbol) { - case '"': - insideQuotes = !insideQuotes; - break; - case '[': - case ']': - break; - case ',': - qComma++; - default: - if (!insideQuotes || (qComma != knownPalette)) - break; - u8x8.print(singleJsonSymbol); - printedChars++; - } - if ((qComma > knownMode) || (printedChars > u8x8.getCols() - 2)) - break; - } + extractModeName(knownPalette, JSON_palette_names, lineBuffer, 16); + u8x8.print(lineBuffer); u8x8.setFont(u8x8_font_open_iconic_embedded_1x1); u8x8.drawGlyph(0, 0, 80); // wifi icon diff --git a/usermods/Wemos_D1_mini+Wemos32_mini_shield/usermod_bme280.cpp b/usermods/Wemos_D1_mini+Wemos32_mini_shield/usermod_bme280.cpp index fe53a46283..05d4e77a45 100644 --- a/usermods/Wemos_D1_mini+Wemos32_mini_shield/usermod_bme280.cpp +++ b/usermods/Wemos_D1_mini+Wemos32_mini_shield/usermod_bme280.cpp @@ -191,58 +191,14 @@ void userLoop() { // Third row with mode name u8x8.setCursor(2, 2); - uint8_t qComma = 0; - bool insideQuotes = false; - uint8_t printedChars = 0; - char singleJsonSymbol; + char lineBuffer[17]; + extractModeName(knownMode, JSON_mode_names, lineBuffer, 16); + u8x8.print(lineBuffer); - // Find the mode name in JSON - for (size_t i = 0; i < strlen_P(JSON_mode_names); i++) { - singleJsonSymbol = pgm_read_byte_near(JSON_mode_names + i); - switch (singleJsonSymbol) { - case '"': - insideQuotes = !insideQuotes; - break; - case '[': - case ']': - break; - case ',': - qComma++; - default: - if (!insideQuotes || (qComma != knownMode)) - break; - u8x8.print(singleJsonSymbol); - printedChars++; - } - if ((qComma > knownMode) || (printedChars > u8x8.getCols() - 2)) - break; - } // Fourth row with palette name u8x8.setCursor(2, 3); - qComma = 0; - insideQuotes = false; - printedChars = 0; - // Looking for palette name in JSON. - for (size_t i = 0; i < strlen_P(JSON_palette_names); i++) { - singleJsonSymbol = pgm_read_byte_near(JSON_palette_names + i); - switch (singleJsonSymbol) { - case '"': - insideQuotes = !insideQuotes; - break; - case '[': - case ']': - break; - case ',': - qComma++; - default: - if (!insideQuotes || (qComma != knownPalette)) - break; - u8x8.print(singleJsonSymbol); - printedChars++; - } - if ((qComma > knownMode) || (printedChars > u8x8.getCols() - 2)) - break; - } + extractModeName(knownPalette, JSON_palette_names, lineBuffer, 16); + u8x8.print(lineBuffer); u8x8.setFont(u8x8_font_open_iconic_embedded_1x1); u8x8.drawGlyph(0, 0, 80); // wifi icon diff --git a/usermods/audioreactive/audio_reactive.h b/usermods/audioreactive/audio_reactive.h new file mode 100644 index 0000000000..c7c7e5f8f2 --- /dev/null +++ b/usermods/audioreactive/audio_reactive.h @@ -0,0 +1,1494 @@ +#pragma once + +#include "wled.h" +#include +#include + +#ifndef ESP32 + #error This audio reactive usermod does not support the ESP8266. +#endif + +#ifdef WLED_DEBUG +#include +#endif + +/* + * Usermods allow you to add own functionality to WLED more easily + * See: https://github.com/Aircoookie/WLED/wiki/Add-own-functionality + * + * This is an audioreactive v2 usermod. + * .... + */ + +// Comment/Uncomment to toggle usb serial debugging +// #define MIC_LOGGER // MIC sampling & sound input debugging (serial plotter) +// #define FFT_SAMPLING_LOG // FFT result debugging +// #define SR_DEBUG // generic SR DEBUG messages (including MIC_LOGGER) +// #define NO_MIC_LOGGER // exclude MIC_LOGGER from SR_DEBUG + +// hackers corner + +#ifdef SR_DEBUG + #define DEBUGSR_PRINT(x) Serial.print(x) + #define DEBUGSR_PRINTLN(x) Serial.println(x) + #define DEBUGSR_PRINTF(x...) Serial.printf(x) +#else + #define DEBUGSR_PRINT(x) + #define DEBUGSR_PRINTLN(x) + #define DEBUGSR_PRINTF(x...) +#endif +// legacy support +// #if defined(SR_DEBUG) && !defined(MIC_LOGGER) && !defined(NO_MIC_LOGGER) +// #define MIC_LOGGER +// #endif + + +#include "audio_source.h" + +constexpr i2s_port_t I2S_PORT = I2S_NUM_0; +constexpr int BLOCK_SIZE = 128; +//constexpr int SAMPLE_RATE = 22050; // Base sample rate in Hz - 22Khz is a standard rate. Physical sample time -> 23ms +constexpr int SAMPLE_RATE = 20480; // Base sample rate in Hz - 20Khz is experimental. Physical sample time -> 25ms +//constexpr int SAMPLE_RATE = 10240; // Base sample rate in Hz - standard. Physical sample time -> 50ms + +#define FFT_MIN_CYCLE 22 // minimum time before FFT task is repeated. Must be less than time needed to read 512 samples at SAMPLE_RATE -> not the same as I2S time!! + +// globals +static uint8_t inputLevel = 128; // UI slider value +static uint8_t soundSquelch = 10; // squelch value for volume reactive routines (config value) +static uint8_t sampleGain = 60; // sample gain (config value) +static uint8_t soundAgc = 0; // Automagic gain control: 0 - none, 1 - normal, 2 - vivid, 3 - lazy (config value) +static uint8_t audioSyncEnabled = 0; // bit field: bit 0 - send, bit 1 - receive (config value) + +// user settable parameters for limitSoundDynamics() +static bool limiterOn = true; // bool: enable / disable dynamics limiter +static uint16_t attackTime = 80; // int: attack time in milliseconds. Default 0.08sec +static uint16_t decayTime = 1400; // int: decay time in milliseconds. Default 1.40sec + +// +// AGC presets +// Note: in C++, "const" implies "static" - no need to explicitly declare everything as "static const" +// +#define AGC_NUM_PRESETS 3 // AGC presets: normal, vivid, lazy +const double agcSampleDecay[AGC_NUM_PRESETS] = { 0.9994f, 0.9985f, 0.9997f}; // decay factor for sampleMax, in case the current sample is below sampleMax +const float agcZoneLow[AGC_NUM_PRESETS] = { 32, 28, 36}; // low volume emergency zone +const float agcZoneHigh[AGC_NUM_PRESETS] = { 240, 240, 248}; // high volume emergency zone +const float agcZoneStop[AGC_NUM_PRESETS] = { 336, 448, 304}; // disable AGC integrator if we get above this level +const float agcTarget0[AGC_NUM_PRESETS] = { 112, 144, 164}; // first AGC setPoint -> between 40% and 65% +const float agcTarget0Up[AGC_NUM_PRESETS] = { 88, 64, 116}; // setpoint switching value (a poor man's bang-bang) +const float agcTarget1[AGC_NUM_PRESETS] = { 220, 224, 216}; // second AGC setPoint -> around 85% +const double agcFollowFast[AGC_NUM_PRESETS] = { 1/192.f, 1/128.f, 1/256.f}; // quickly follow setpoint - ~0.15 sec +const double agcFollowSlow[AGC_NUM_PRESETS] = {1/6144.f,1/4096.f,1/8192.f}; // slowly follow setpoint - ~2-15 secs +const double agcControlKp[AGC_NUM_PRESETS] = { 0.6f, 1.5f, 0.65f}; // AGC - PI control, proportional gain parameter +const double agcControlKi[AGC_NUM_PRESETS] = { 1.7f, 1.85f, 1.2f}; // AGC - PI control, integral gain parameter +const float agcSampleSmooth[AGC_NUM_PRESETS] = { 1/12.f, 1/6.f, 1/16.f}; // smoothing factor for sampleAgc (use rawSampleAgc if you want the non-smoothed value) +// AGC presets end + +static AudioSource *audioSource = nullptr; +static volatile bool disableSoundProcessing = false; // if true, sound processing (FFT, filters, AGC) will be suspended. "volatile" as its shared between tasks. + +static float micDataReal = 0.0f; // MicIn data with full 24bit resolution - lowest 8bit after decimal point +static float multAgc = 1.0f; // sample * multAgc = sampleAgc. Our AGC multiplier + +//////////////////// +// Begin FFT Code // +//////////////////// +#ifdef UM_AUDIOREACTIVE_USE_NEW_FFT +// lib_deps += https://github.com/kosme/arduinoFFT#develop @ 1.9.2 +#define FFT_SPEED_OVER_PRECISION // enables use of reciprocals (1/x etc), and an a few other speedups +#define FFT_SQRT_APPROXIMATION // enables "quake3" style inverse sqrt +#define sqrt(x) sqrtf(x) // little hack that reduces FFT time by 50% on ESP32 (as alternative to FFT_SQRT_APPROXIMATION) +#endif +#include "arduinoFFT.h" + +// FFT Variables +constexpr uint16_t samplesFFT = 512; // Samples in an FFT batch - This value MUST ALWAYS be a power of 2 +constexpr uint16_t samplesFFT_2 = 256; // meaningfull part of FFT results - only the "lower half" contains useful information. + +static float FFT_MajorPeak = 0.0f; +static float FFT_Magnitude = 0.0f; + +// These are the input and output vectors. Input vectors receive computed results from FFT. +static float vReal[samplesFFT] = {0.0f}; +static float vImag[samplesFFT] = {0.0f}; +static float fftBin[samplesFFT_2] = {0.0f}; + +#ifdef UM_AUDIOREACTIVE_USE_NEW_FFT +static float windowWeighingFactors[samplesFFT] = {0.0f}; +#endif + +// Try and normalize fftBin values to a max of 4096, so that 4096/16 = 256. +// Oh, and bins 0,1,2 are no good, so we'll zero them out. +static float fftCalc[16] = {0.0f}; +static uint8_t fftResult[16] = {0}; // Our calculated result table, which we feed to the animations. +#ifdef SR_DEBUG +static float fftResultMax[16] = {0.0f}; // A table used for testing to determine how our post-processing is working. +#endif +static float fftAvg[16] = {0.0f}; + +#ifdef WLED_DEBUG +static unsigned long fftTime = 0; +static unsigned long sampleTime = 0; +#endif + +// Table of linearNoise results to be multiplied by soundSquelch in order to reduce squelch across fftResult bins. +static uint8_t linearNoise[16] = { 34, 28, 26, 25, 20, 12, 9, 6, 4, 4, 3, 2, 2, 2, 2, 2 }; + +// Table of multiplication factors so that we can even out the frequency response. +static float fftResultPink[16] = { 1.70f, 1.71f, 1.73f, 1.78f, 1.68f, 1.56f, 1.55f, 1.63f, 1.79f, 1.62f, 1.80f, 2.06f, 2.47f, 3.35f, 6.83f, 9.55f }; + +// Create FFT object +#ifdef UM_AUDIOREACTIVE_USE_NEW_FFT +static ArduinoFFT FFT = ArduinoFFT( vReal, vImag, samplesFFT, SAMPLE_RATE, windowWeighingFactors); +#else +static arduinoFFT FFT = arduinoFFT(vReal, vImag, samplesFFT, SAMPLE_RATE); +#endif + +static TaskHandle_t FFT_Task = nullptr; + +static float fftAddAvg(int from, int to) { + float result = 0.0f; + for (int i = from; i <= to; i++) { + result += fftBin[i]; + } + return result / float(to - from + 1); +} + +// FFT main code +void FFTcode(void * parameter) +{ + DEBUGSR_PRINT("FFT started on core: "); DEBUGSR_PRINTLN(xPortGetCoreID()); + + // see https://www.freertos.org/vtaskdelayuntil.html + const TickType_t xFrequency = FFT_MIN_CYCLE * portTICK_PERIOD_MS; + //const TickType_t xFrequency_2 = (FFT_MIN_CYCLE * portTICK_PERIOD_MS) / 2; + + for(;;) { + TickType_t xLastWakeTime = xTaskGetTickCount(); + delay(1); // DO NOT DELETE THIS LINE! It is needed to give the IDLE(0) task enough time and to keep the watchdog happy. + // taskYIELD(), yield(), vTaskDelay() and esp_task_wdt_feed() didn't seem to work. + + // Only run the FFT computing code if we're not in Receive mode and not in realtime mode + if (disableSoundProcessing || (audioSyncEnabled & 0x02)) { + //delay(7); // release CPU - delay is implemeted using vTaskDelay(). cannot use yield() because we are out of arduino loop context + vTaskDelayUntil( &xLastWakeTime, xFrequency); // release CPU, by doing nothing for FFT_MIN_CYCLE millis + continue; + } + + vTaskDelayUntil( &xLastWakeTime, xFrequency); // release CPU, and let I2S fill its buffers + //vTaskDelayUntil( &xLastWakeTime, xFrequency_2); // release CPU, and let I2S fill its buffers + +#ifdef WLED_DEBUG + uint64_t start = esp_timer_get_time(); +#endif + + if (audioSource) audioSource->getSamples(vReal, samplesFFT); + +#ifdef WLED_DEBUG + if (start < esp_timer_get_time()) { // filter out overflows + unsigned long sampleTimeInMillis = (esp_timer_get_time() - start +500ULL) / 1000ULL; // "+500" to ensure proper rounding + sampleTime = (sampleTimeInMillis*3 + sampleTime*7)/10; // smooth + } +#endif + + const int halfSamplesFFT = samplesFFT / 2; // samplesFFT divided by 2 + float maxSample1 = 0.0f; // max sample from first half of FFT batch + float maxSample2 = 0.0f; // max sample from second half of FFT batch + for (int i=0; i < halfSamplesFFT; i++) { + // set imaginary parts to 0 + vImag[i] = 0; + // pick our our current mic sample - we take the max value from all samples that go into FFT + if ((vReal[i] <= (INT16_MAX - 1024)) && (vReal[i] >= (INT16_MIN + 1024))) //skip extreme values - normally these are artefacts + if (fabsf((float)vReal[i]) > maxSample1) maxSample1 = fabsf((float)vReal[i]); + } + for (int i=halfSamplesFFT; i < samplesFFT; i++) { + // set imaginary parts to 0 + vImag[i] = 0; + // pick our our current mic sample - we take the max value from all samples that go into FFT + if ((vReal[i] <= (INT16_MAX - 1024)) && (vReal[i] >= (INT16_MIN + 1024))) //skip extreme values - normally these are artefacts + if (fabsf((float)vReal[i]) > maxSample2) maxSample2 = fabsf((float)vReal[i]); + } + // release first sample to volume reactive effects + micDataReal = maxSample1; + +#ifdef UM_AUDIOREACTIVE_USE_NEW_FFT + FFT.dcRemoval(); // remove DC offset + FFT.windowing( FFTWindow::Flat_top, FFTDirection::Forward); // Weigh data + FFT.compute( FFTDirection::Forward ); // Compute FFT + FFT.complexToMagnitude(); // Compute magnitudes +#else + FFT.DCRemoval(); // let FFT lib remove DC component, so we don't need to care about this in getSamples() + + //FFT.Windowing( FFT_WIN_TYP_HAMMING, FFT_FORWARD ); // Weigh data - standard Hamming window + //FFT.Windowing( FFT_WIN_TYP_BLACKMAN, FFT_FORWARD ); // Blackman window - better side freq rejection + //FFT.Windowing( FFT_WIN_TYP_BLACKMAN_HARRIS, FFT_FORWARD );// Blackman-Harris - excellent sideband rejection + FFT.Windowing( FFT_WIN_TYP_FLT_TOP, FFT_FORWARD ); // Flat Top Window - better amplitude accuracy + FFT.Compute( FFT_FORWARD ); // Compute FFT + FFT.ComplexToMagnitude(); // Compute magnitudes +#endif + // + // vReal[3 .. 255] contain useful data, each a 20Hz interval (60Hz - 5120Hz). + // There could be interesting data at bins 0 to 2, but there are too many artifacts. + // + +#ifdef UM_AUDIOREACTIVE_USE_NEW_FFT + FFT.majorPeak(FFT_MajorPeak, FFT_Magnitude); // let the effects know which freq was most dominant +#else + FFT.MajorPeak(&FFT_MajorPeak, &FFT_Magnitude); // let the effects know which freq was most dominant +#endif + + for (int i = 0; i < samplesFFT_2; i++) { // Values for bins 0 and 1 are WAY too large. Might as well start at 3. + float t = fabs(vReal[i]); // just to be sure - values in fft bins should be positive any way + fftBin[i] = t / 16.0f; // Reduce magnitude. Want end result to be linear and ~4096 max. + } // for() + + +/* This FFT post processing is a DIY endeavour. What we really need is someone with sound engineering expertise to do a great job here AND most importantly, that the animations look GREAT as a result. + * + * + * Andrew's updated mapping of 256 bins down to the 16 result bins with Sample Freq = 10240, samplesFFT = 512 and some overlap. + * Based on testing, the lowest/Start frequency is 60 Hz (with bin 3) and a highest/End frequency of 5120 Hz in bin 255. + * Now, Take the 60Hz and multiply by 1.320367784 to get the next frequency and so on until the end. Then detetermine the bins. + * End frequency = Start frequency * multiplier ^ 16 + * Multiplier = (End frequency/ Start frequency) ^ 1/16 + * Multiplier = 1.320367784 + */ + // Range + fftCalc[ 0] = fftAddAvg(3,4); // 60 - 100 + fftCalc[ 1] = fftAddAvg(4,5); // 80 - 120 + fftCalc[ 2] = fftAddAvg(5,7); // 100 - 160 + fftCalc[ 3] = fftAddAvg(7,9); // 140 - 200 + fftCalc[ 4] = fftAddAvg(9,12); // 180 - 260 + fftCalc[ 5] = fftAddAvg(12,16); // 240 - 340 + fftCalc[ 6] = fftAddAvg(16,21); // 320 - 440 + fftCalc[ 7] = fftAddAvg(21,29); // 420 - 600 + fftCalc[ 8] = fftAddAvg(29,37); // 580 - 760 + fftCalc[ 9] = fftAddAvg(37,48); // 740 - 980 + fftCalc[10] = fftAddAvg(48,64); // 960 - 1300 + fftCalc[11] = fftAddAvg(64,84); // 1280 - 1700 + fftCalc[12] = fftAddAvg(84,111); // 1680 - 2240 + fftCalc[13] = fftAddAvg(111,147); // 2220 - 2960 + fftCalc[14] = fftAddAvg(147,194); // 2940 - 3900 + fftCalc[15] = fftAddAvg(194,255); // 3880 - 5120 + + for (int i=0; i < 16; i++) { + // Noise supression of fftCalc bins using soundSquelch adjustment for different input types. + fftCalc[i] = (fftCalc[i] < ((float)soundSquelch * (float)linearNoise[i] / 4.0f)) ? 0 : fftCalc[i]; + // Adjustment for frequency curves. + fftCalc[i] *= fftResultPink[i]; + // Manual linear adjustment of gain using sampleGain adjustment for different input types. + fftCalc[i] *= soundAgc ? multAgc : ((float)sampleGain/40.0f * (float)inputLevel/128.0f + 1.0f/16.0f); //with inputLevel adjustment + + // smooth results - rise fast, fall slower + if(fftCalc[i] > fftAvg[i]) // rise fast + fftAvg[i] = fftCalc[i] *0.75f + 0.25f*fftAvg[i]; // will need approx 2 cycles (50ms) for converging against fftCalc[i] + else // fall slow + fftAvg[i] = fftCalc[i]*0.1f + 0.9f*fftAvg[i]; // will need approx 5 cycles (150ms) for converging against fftCalc[i] + //fftAvg[i] = fftCalc[i]*0.05f + 0.95f*fftAvg[i]; // will need approx 10 cycles (250ms) for converging against fftCalc[i] + + // Now, let's dump it all into fftResult. Need to do this, otherwise other routines might grab fftResult values prematurely. + if(limiterOn == true) + fftResult[i] = constrain((int)fftAvg[i], 0, 254); + else + fftResult[i] = constrain((int)fftCalc[i], 0, 254); + } + +#ifdef WLED_DEBUG + if (start < esp_timer_get_time()) { // filter out overflows + unsigned long fftTimeInMillis = ((esp_timer_get_time() - start) +500ULL) / 1000ULL; // "+500" to ensure proper rounding + fftTime = (fftTimeInMillis*3 + fftTime*7)/10; // smooth + } +#endif + + //vTaskDelayUntil( &xLastWakeTime, xFrequency_2); // release CPU, by waiting until FFT_MIN_CYCLE is over + // release second sample to volume reactive effects. + // Releasing a second sample now effectively doubles the "sample rate" + micDataReal = maxSample2; + + } // for(;;) +} // FFTcode() + + +//class name. Use something descriptive and leave the ": public Usermod" part :) +class AudioReactive : public Usermod { + + private: + #ifndef AUDIOPIN + int8_t audioPin = 36; + #else + int8_t audioPin = AUDIOPIN; + #endif + #ifndef DMTYPE // I2S mic type + uint8_t dmType = 1; // 0=none/disabled/analog; 1=generic I2S + #else + uint8_t dmType = DMTYPE; + #endif + #ifndef I2S_SDPIN // aka DOUT + int8_t i2ssdPin = 32; + #else + int8_t i2ssdPin = I2S_SDPIN; + #endif + #ifndef I2S_WSPIN // aka LRCL + int8_t i2swsPin = 15; + #else + int8_t i2swsPin = I2S_WSPIN; + #endif + #ifndef I2S_CKPIN // aka BCLK + int8_t i2sckPin = 14; + #else + int8_t i2sckPin = I2S_CKPIN; + #endif + #ifndef ES7243_SDAPIN + int8_t sdaPin = -1; + #else + int8_t sdaPin = ES7243_SDAPIN; + #endif + #ifndef ES7243_SCLPIN + int8_t sclPin = -1; + #else + int8_t sclPin = ES7243_SCLPIN; + #endif + #ifndef MCLK_PIN + int8_t mclkPin = -1; + #else + int8_t mclkPin = MLCK_PIN; + #endif + + // new "V2" audiosync struct - 40 Bytes + struct audioSyncPacket { + char header[6]; // 06 Bytes + float sampleRaw; // 04 Bytes - either "sampleRaw" or "rawSampleAgc" depending on soundAgc setting + float sampleSmth; // 04 Bytes - either "sampleAvg" or "sampleAgc" depending on soundAgc setting + uint8_t samplePeak; // 01 Bytes - 0 no peak; >=1 peak detected. In future, this will also provide peak Magnitude + uint8_t reserved1; // 01 Bytes - for future extensions - not used yet + uint8_t fftResult[16]; // 16 Bytes + float FFT_Magnitude; // 04 Bytes + float FFT_MajorPeak; // 04 Bytes + }; + + // old "V1" audiosync struct - 83 Bytes - for backwards compatibility + struct audioSyncPacket_v1 { + char header[6]; // 06 Bytes + uint8_t myVals[32]; // 32 Bytes + int sampleAgc; // 04 Bytes + int sampleRaw; // 04 Bytes + float sampleAvg; // 04 Bytes + bool samplePeak; // 01 Bytes + uint8_t fftResult[16]; // 16 Bytes + double FFT_Magnitude; // 08 Bytes + double FFT_MajorPeak; // 08 Bytes + }; + + WiFiUDP fftUdp; + + // set your config variables to their boot default value (this can also be done in readFromConfig() or a constructor if you prefer) + bool enabled = false; + bool initDone = false; + + const uint16_t delayMs = 10; // I don't want to sample too often and overload WLED + // variables used in effects + uint8_t maxVol = 10; // Reasonable value for constant volume for 'peak detector', as it won't always trigger (deprecated) + uint8_t binNum = 8; // Used to select the bin for FFT based beat detection (deprecated) + bool samplePeak = 0; // Boolean flag for peak. Responding routine must reset this flag + float volumeSmth = 0.0f; // either sampleAvg or sampleAgc depending on soundAgc; smoothed sample + int16_t volumeRaw = 0; // either sampleRaw or rawSampleAgc depending on soundAgc + float my_magnitude =0.0f; // FFT_Magnitude, scaled by multAgc + + bool udpSamplePeak = 0; // Boolean flag for peak. Set at the same tiem as samplePeak, but reset by transmitAudioData + int16_t micIn = 0; // Current sample starts with negative values and large values, which is why it's 16 bit signed + int16_t sampleRaw = 0; // Current sample. Must only be updated ONCE!!! (amplified mic value by sampleGain and inputLevel; smoothed over 16 samples) + double sampleMax = 0.0; // Max sample over a few seconds. Needed for AGC controler. + float sampleReal = 0.0f; // "sampleRaw" as float, to provide bits that are lost otherwise (before amplification by sampleGain or inputLevel). Needed for AGC. + float sampleAvg = 0.0f; // Smoothed Average sampleRaw + float sampleAgc = 0.0f; // Our AGC sample + int16_t rawSampleAgc = 0; // Our AGC sample - raw + uint32_t timeOfPeak = 0; + unsigned long lastTime = 0; // last time of running UDP Microphone Sync + float micLev = 0.0f; // Used to convert returned value to have '0' as minimum. A leveller + float expAdjF = 0.0f; // Used for exponential filter. + + bool udpSyncConnected = false; + uint16_t audioSyncPort = 11988; + + // used for AGC + uint8_t lastMode = 0; // last known effect mode + int last_soundAgc = -1; + double control_integrated = 0.0; // persistent across calls to agcAvg(); "integrator control" = accumulated error + unsigned long last_update_time = 0; + unsigned long last_kick_time = 0; + uint8_t last_user_inputLevel = 0; + + // used to feed "Info" Page + unsigned long last_UDPTime = 0; // time of last valid UDP sound sync datapacket + float maxSample5sec = 0.0f; // max sample (after AGC) in last 5 seconds + unsigned long sampleMaxTimer = 0; // last time maxSample5sec was reset + #define CYCLE_SAMPLEMAX 3500 // time window for merasuring + + // strings to reduce flash memory usage (used more than twice) + static const char _name[]; + static const char _enabled[]; + static const char _inputLvl[]; + static const char _analogmic[]; + static const char _digitalmic[]; + static const char UDP_SYNC_HEADER[]; + static const char UDP_SYNC_HEADER_v1[]; + + // private methods + void logAudio() + { + #ifdef MIC_LOGGER + // Debugging functions for audio input and sound processing. Comment out the values you want to see + Serial.print("micReal:"); Serial.print(micDataReal); Serial.print("\t"); + //Serial.print("micIn:"); Serial.print(micIn); Serial.print("\t"); + //Serial.print("micLev:"); Serial.print(micLev); Serial.print("\t"); + //Serial.print("sampleReal:"); Serial.print(sampleReal); Serial.print("\t"); + //Serial.print("sample:"); Serial.print(sample); Serial.print("\t"); + //Serial.print("sampleAvg:"); Serial.print(sampleAvg); Serial.print("\t"); + //Serial.print("sampleMax:"); Serial.print(sampleMax); Serial.print("\t"); + //Serial.print("samplePeak:"); Serial.print((samplePeak!=0) ? 128:0); Serial.print("\t"); + //Serial.print("multAgc:"); Serial.print(multAgc, 4); Serial.print("\t"); + Serial.print("sampleAgc:"); Serial.print(sampleAgc); Serial.print("\t"); + //Serial.print("volumeRaw:"); Serial.print(volumeRaw); Serial.print("\t"); + //Serial.print("volumeSmth:"); Serial.print(volumeSmth); Serial.print("\t"); + + Serial.println(); + #endif + + #ifdef FFT_SAMPLING_LOG + #if 0 + for(int i=0; i<16; i++) { + Serial.print(fftResult[i]); + Serial.print("\t"); + } + Serial.println(); + #endif + + // OPTIONS are in the following format: Description \n Option + // + // Set true if wanting to see all the bands in their own vertical space on the Serial Plotter, false if wanting to see values in Serial Monitor + const bool mapValuesToPlotterSpace = false; + // Set true to apply an auto-gain like setting to to the data (this hasn't been tested recently) + const bool scaleValuesFromCurrentMaxVal = false; + // prints the max value seen in the current data + const bool printMaxVal = false; + // prints the min value seen in the current data + const bool printMinVal = false; + // if !scaleValuesFromCurrentMaxVal, we scale values from [0..defaultScalingFromHighValue] to [0..scalingToHighValue], lower this if you want to see smaller values easier + const int defaultScalingFromHighValue = 256; + // Print values to terminal in range of [0..scalingToHighValue] if !mapValuesToPlotterSpace, or [(i)*scalingToHighValue..(i+1)*scalingToHighValue] if mapValuesToPlotterSpace + const int scalingToHighValue = 256; + // set higher if using scaleValuesFromCurrentMaxVal and you want a small value that's also the current maxVal to look small on the plotter (can't be 0 to avoid divide by zero error) + const int minimumMaxVal = 1; + + int maxVal = minimumMaxVal; + int minVal = 0; + for(int i = 0; i < 16; i++) { + if(fftResult[i] > maxVal) maxVal = fftResult[i]; + if(fftResult[i] < minVal) minVal = fftResult[i]; + } + for(int i = 0; i < 16; i++) { + Serial.print(i); Serial.print(":"); + Serial.printf("%04ld ", map(fftResult[i], 0, (scaleValuesFromCurrentMaxVal ? maxVal : defaultScalingFromHighValue), (mapValuesToPlotterSpace*i*scalingToHighValue)+0, (mapValuesToPlotterSpace*i*scalingToHighValue)+scalingToHighValue-1)); + } + if(printMaxVal) { + Serial.printf("maxVal:%04d ", maxVal + (mapValuesToPlotterSpace ? 16*256 : 0)); + } + if(printMinVal) { + Serial.printf("%04d:minVal ", minVal); // printed with value first, then label, so negative values can be seen in Serial Monitor but don't throw off y axis in Serial Plotter + } + if(mapValuesToPlotterSpace) + Serial.printf("max:%04d ", (printMaxVal ? 17 : 16)*256); // print line above the maximum value we expect to see on the plotter to avoid autoscaling y axis + else + Serial.printf("max:%04d ", 256); + Serial.println(); + #endif // FFT_SAMPLING_LOG + } // logAudio() + + + /* + * A "PI controller" multiplier to automatically adjust sound sensitivity. + * + * A few tricks are implemented so that sampleAgc does't only utilize 0% and 100%: + * 0. don't amplify anything below squelch (but keep previous gain) + * 1. gain input = maximum signal observed in the last 5-10 seconds + * 2. we use two setpoints, one at ~60%, and one at ~80% of the maximum signal + * 3. the amplification depends on signal level: + * a) normal zone - very slow adjustment + * b) emergency zome (<10% or >90%) - very fast adjustment + */ + void agcAvg(unsigned long the_time) + { + const int AGC_preset = (soundAgc > 0)? (soundAgc-1): 0; // make sure the _compiler_ knows this value will not change while we are inside the function + + float lastMultAgc = multAgc; // last muliplier used + float multAgcTemp = multAgc; // new multiplier + float tmpAgc = sampleReal * multAgc; // what-if amplified signal + + float control_error; // "control error" input for PI control + + if (last_soundAgc != soundAgc) + control_integrated = 0.0; // new preset - reset integrator + + // For PI controller, we need to have a constant "frequency" + // so let's make sure that the control loop is not running at insane speed + static unsigned long last_time = 0; + unsigned long time_now = millis(); + if ((the_time > 0) && (the_time < time_now)) time_now = the_time; // allow caller to override my clock + + if (time_now - last_time > 2) { + last_time = time_now; + + if((fabs(sampleReal) < 2.0f) || (sampleMax < 1.0f)) { + // MIC signal is "squelched" - deliver silence + //multAgcTemp = multAgc; // keep old control value (no change) + tmpAgc = 0; + // we need to "spin down" the intgrated error buffer + if (fabs(control_integrated) < 0.01) control_integrated = 0.0; + else control_integrated *= 0.91; + } else { + // compute new setpoint + if (tmpAgc <= agcTarget0Up[AGC_preset]) + multAgcTemp = agcTarget0[AGC_preset] / sampleMax; // Make the multiplier so that sampleMax * multiplier = first setpoint + else + multAgcTemp = agcTarget1[AGC_preset] / sampleMax; // Make the multiplier so that sampleMax * multiplier = second setpoint + } + // limit amplification + //multAgcTemp = constrain(multAgcTemp, 0.015625f, 32.0f); // 1/64 < multAgcTemp < 32 + if (multAgcTemp > 32.0f) multAgcTemp = 32.0f; + if (multAgcTemp < 1.0f/64.0f) multAgcTemp = 1.0f/64.0f; + + // compute error terms + control_error = multAgcTemp - lastMultAgc; + + if (((multAgcTemp > 0.085f) && (multAgcTemp < 6.5f)) //integrator anti-windup by clamping + && (multAgc*sampleMax < agcZoneStop[AGC_preset])) //integrator ceiling (>140% of max) + control_integrated += control_error * 0.002 * 0.25; // 2ms = intgration time; 0.25 for damping + else + control_integrated *= 0.9; // spin down that beasty integrator + + // apply PI Control + tmpAgc = sampleReal * lastMultAgc; // check "zone" of the signal using previous gain + if ((tmpAgc > agcZoneHigh[AGC_preset]) || (tmpAgc < soundSquelch + agcZoneLow[AGC_preset])) { // upper/lower emergy zone + multAgcTemp = lastMultAgc + agcFollowFast[AGC_preset] * agcControlKp[AGC_preset] * control_error; + multAgcTemp += agcFollowFast[AGC_preset] * agcControlKi[AGC_preset] * control_integrated; + } else { // "normal zone" + multAgcTemp = lastMultAgc + agcFollowSlow[AGC_preset] * agcControlKp[AGC_preset] * control_error; + multAgcTemp += agcFollowSlow[AGC_preset] * agcControlKi[AGC_preset] * control_integrated; + } + + // limit amplification again - PI controler sometimes "overshoots" + //multAgcTemp = constrain(multAgcTemp, 0.015625f, 32.0f); // 1/64 < multAgcTemp < 32 + if (multAgcTemp > 32.0f) multAgcTemp = 32.0f; + if (multAgcTemp < 1.0f/64.0f) multAgcTemp = 1.0f/64.0f; + } + + // NOW finally amplify the signal + tmpAgc = sampleReal * multAgcTemp; // apply gain to signal + if (fabsf(sampleReal) < 2.0f) tmpAgc = 0.0f; // apply squelch threshold + //tmpAgc = constrain(tmpAgc, 0, 255); + if (tmpAgc > 255) tmpAgc = 255.0f; // limit to 8bit + if (tmpAgc < 1) tmpAgc = 0.0f; // just to be sure + + // update global vars ONCE - multAgc, sampleAGC, rawSampleAgc + multAgc = multAgcTemp; + rawSampleAgc = 0.8f * tmpAgc + 0.2f * (float)rawSampleAgc; + // update smoothed AGC sample + if (fabsf(tmpAgc) < 1.0f) + sampleAgc = 0.5f * tmpAgc + 0.5f * sampleAgc; // fast path to zero + else + sampleAgc += agcSampleSmooth[AGC_preset] * (tmpAgc - sampleAgc); // smooth path + + //userVar0 = sampleAvg * 4; + //if (userVar0 > 255) userVar0 = 255; + + last_soundAgc = soundAgc; + } // agcAvg() + + + void getSample() + { + float sampleAdj; // Gain adjusted sample value + float tmpSample; // An interim sample variable used for calculatioins. + const float weighting = 0.2f; // Exponential filter weighting. Will be adjustable in a future release. + const int AGC_preset = (soundAgc > 0)? (soundAgc-1): 0; // make sure the _compiler_ knows this value will not change while we are inside the function + + #ifdef WLED_DISABLE_SOUND + micIn = inoise8(millis(), millis()); // Simulated analog read + micDataReal = micIn; + #else + #ifdef ESP32 + micIn = int(micDataReal); // micDataSm = ((micData * 3) + micData)/4; + #else + // this is the minimal code for reading analog mic input on 8266. + // warning!! Absolutely experimental code. Audio on 8266 is still not working. Expects a million follow-on problems. + static unsigned long lastAnalogTime = 0; + static float lastAnalogValue = 0.0f; + if (millis() - lastAnalogTime > 20) { + micDataReal = analogRead(A0); // read one sample with 10bit resolution. This is a dirty hack, supporting volumereactive effects only. + lastAnalogTime = millis(); + lastAnalogValue = micDataReal; + yield(); + } else micDataReal = lastAnalogValue; + micIn = int(micDataReal); + #endif + #endif + + micLev = ((micLev * 8191.0f) + micDataReal) / 8192.0f; // takes a few seconds to "catch up" with the Mic Input + if(micIn < micLev) micLev = ((micLev * 31.0f) + micDataReal) / 32.0f; // align MicLev to lowest input signal + + micIn -= micLev; // Let's center it to 0 now + // Using an exponential filter to smooth out the signal. We'll add controls for this in a future release. + float micInNoDC = fabs(micDataReal - micLev); + expAdjF = (weighting * micInNoDC + (1.0-weighting) * expAdjF); + expAdjF = (expAdjF <= soundSquelch) ? 0: expAdjF; // simple noise gate + if ((soundSquelch == 0) && (expAdjF < 0.25f)) expAdjF = 0; // do something meaningfull when "squelch = 0" + + expAdjF = fabsf(expAdjF); // Now (!) take the absolute value + tmpSample = expAdjF; + micIn = abs(micIn); // And get the absolute value of each sample + + sampleAdj = tmpSample * sampleGain / 40.0f * inputLevel/128.0f + tmpSample / 16.0f; // Adjust the gain. with inputLevel adjustment + sampleReal = tmpSample; + + sampleAdj = fmax(fmin(sampleAdj, 255), 0); // Question: why are we limiting the value to 8 bits ??? + sampleRaw = (int16_t)sampleAdj; // ONLY update sample ONCE!!!! + + // keep "peak" sample, but decay value if current sample is below peak + if ((sampleMax < sampleReal) && (sampleReal > 0.5f)) { + sampleMax = sampleMax + 0.5f * (sampleReal - sampleMax); // new peak - with some filtering + } else { + if ((multAgc*sampleMax > agcZoneStop[AGC_preset]) && (soundAgc > 0)) + sampleMax += 0.5f * (sampleReal - sampleMax); // over AGC Zone - get back quickly + else + sampleMax *= agcSampleDecay[AGC_preset]; // signal to zero --> 5-8sec + } + if (sampleMax < 0.5f) sampleMax = 0.0f; + + sampleAvg = ((sampleAvg * 15.0f) + sampleAdj) / 16.0f; // Smooth it out over the last 16 samples. + + // Fixes private class variable compiler error. Unsure if this is the correct way of fixing the root problem. -THATDONFC + uint16_t MinShowDelay = strip.getMinShowDelay(); + + if (millis() - timeOfPeak > MinShowDelay) { // Auto-reset of samplePeak after a complete frame has passed. + samplePeak = false; + udpSamplePeak = false; + } + //if (userVar1 == 0) samplePeak = 0; + + // Poor man's beat detection by seeing if sample > Average + some value. + // if (sample > (sampleAvg + maxVol) && millis() > (timeOfPeak + 200)) { + if ((maxVol > 0) && (binNum > 1) && (fftBin[binNum] > maxVol) && (millis() > (timeOfPeak + 100))) { // This goes through ALL of the 255 bins - but ignores stupid settings + // Then we got a peak, else we don't. The peak has to time out on its own in order to support UDP sound sync. + samplePeak = true; + timeOfPeak = millis(); + udpSamplePeak = true; + //userVar1 = samplePeak; + } + } // getSample() + + + /* Limits the dynamics of volumeSmth (= sampleAvg or sampleAgc). + * does not affect FFTResult[] or volumeRaw ( = sample or rawSampleAgc) + */ + // effects: Gravimeter, Gravcenter, Gravcentric, Noisefire, Plasmoid, Freqpixels, Freqwave, Gravfreq, (2D Swirl, 2D Waverly) + void limitSampleDynamics(void) { + const float bigChange = 196; // just a representative number - a large, expected sample value + static unsigned long last_time = 0; + static float last_volumeSmth = 0.0f; + + if(limiterOn == false) return; + + long delta_time = millis() - last_time; + delta_time = constrain(delta_time , 1, 1000); // below 1ms -> 1ms; above 1sec -> sily lil hick-up + float deltaSample = volumeSmth - last_volumeSmth; + + if (attackTime > 0) { // user has defined attack time > 0 + float maxAttack = bigChange * float(delta_time) / float(attackTime); + if (deltaSample > maxAttack) deltaSample = maxAttack; + } + if (decayTime > 0) { // user has defined decay time > 0 + float maxDecay = - bigChange * float(delta_time) / float(decayTime); + if (deltaSample < maxDecay) deltaSample = maxDecay; + } + + volumeSmth = last_volumeSmth + deltaSample; + + last_volumeSmth = volumeSmth; + last_time = millis(); + } + + + void transmitAudioData() + { + if (!udpSyncConnected) return; + //DEBUGSR_PRINTLN("Transmitting UDP Mic Packet"); + + audioSyncPacket transmitData; + strncpy_P(transmitData.header, PSTR(UDP_SYNC_HEADER), 6); + + //transmitData.sampleRaw = volumeRaw; + //transmitData.sampleSmth = volumeSmth; + // transmit samples that were not modified by limitSampleDynamics() + transmitData.sampleRaw = (soundAgc) ? rawSampleAgc: sampleRaw; + transmitData.sampleSmth = (soundAgc) ? sampleAgc : sampleAvg; + transmitData.samplePeak = udpSamplePeak ? 1:0; + udpSamplePeak = false; // Reset udpSamplePeak after we've transmitted it + transmitData.reserved1 = 0; + + for (int i = 0; i < 16; i++) { + transmitData.fftResult[i] = (uint8_t)constrain(fftResult[i], 0, 254); + } + + transmitData.FFT_Magnitude = my_magnitude; + transmitData.FFT_MajorPeak = FFT_MajorPeak; + + fftUdp.beginMulticastPacket(); + fftUdp.write(reinterpret_cast(&transmitData), sizeof(transmitData)); + fftUdp.endPacket(); + return; + } // transmitAudioData() + + + static bool isValidUdpSyncVersion(const char *header) { + return strncmp_P(header, PSTR(UDP_SYNC_HEADER), 6) == 0; + } + + + bool receiveAudioData() // check & process new data. return TRUE in case that new audio data was received. + { + if (!udpSyncConnected) return false; + //DEBUGSR_PRINTLN("Checking for UDP Microphone Packet"); + bool haveFreshData = false; + size_t packetSize = fftUdp.parsePacket(); + if (packetSize > 5) { + //DEBUGSR_PRINTLN("Received UDP Sync Packet"); + uint8_t fftBuff[packetSize]; + fftUdp.read(fftBuff, packetSize); + + // VERIFY THAT THIS IS A COMPATIBLE PACKET + if (packetSize == sizeof(audioSyncPacket) && (isValidUdpSyncVersion((const char *)fftBuff))) { + audioSyncPacket *receivedPacket = reinterpret_cast(fftBuff); + + // update samples for effects + volumeSmth = fmaxf(receivedPacket->sampleSmth, 0.0f); + volumeRaw = fmaxf(receivedPacket->sampleRaw, 0.0f); + + // update internal samples + sampleRaw = volumeRaw; + sampleAvg = volumeSmth; + rawSampleAgc = volumeRaw; + sampleAgc = volumeSmth; + multAgc = 1.0f; + + // auto-reset sample peak. Need to do it here, because getSample() is not running + uint16_t MinShowDelay = strip.getMinShowDelay(); + if (millis() - timeOfPeak > MinShowDelay) { // Auto-reset of samplePeak after a complete frame has passed. + samplePeak = false; + udpSamplePeak = false; + } + //if (userVar1 == 0) samplePeak = 0; + // Only change samplePeak IF it's currently false. + // If it's true already, then the animation still needs to respond. + if (!samplePeak) { + samplePeak = receivedPacket->samplePeak >0 ? true:false; + if (samplePeak) timeOfPeak = millis(); + //userVar1 = samplePeak; + } + + //These values are only available on the ESP32 + for (int i = 0; i < 16; i++) fftResult[i] = receivedPacket->fftResult[i]; + + my_magnitude = fmaxf(receivedPacket->FFT_Magnitude, 0.0f); + FFT_Magnitude = my_magnitude; + FFT_MajorPeak = fmaxf(receivedPacket->FFT_MajorPeak, 0.0f); + //DEBUGSR_PRINTLN("Finished parsing UDP Sync Packet"); + haveFreshData = true; + } + } + return haveFreshData; + } + + + public: + //Functions called by WLED or other usermods + + /* + * setup() is called once at boot. WiFi is not yet connected at this point. + * You can use it to initialize variables, sensors or similar. + * It is called *AFTER* readFromConfig() + */ + void setup() + { + disableSoundProcessing = true; // just to be sure + if (!initDone) { + // usermod exchangeable data + // we will assign all usermod exportable data here as pointers to original variables or arrays and allocate memory for pointers + um_data = new um_data_t; + um_data->u_size = 8; + um_data->u_type = new um_types_t[um_data->u_size]; + um_data->u_data = new void*[um_data->u_size]; + um_data->u_data[0] = &volumeSmth; //*used (New) + um_data->u_type[0] = UMT_FLOAT; + um_data->u_data[1] = &volumeRaw; // used (New) + um_data->u_type[1] = UMT_UINT16; + um_data->u_data[2] = fftResult; //*used (Blurz, DJ Light, Noisemove, GEQ_base, 2D Funky Plank, Akemi) + um_data->u_type[2] = UMT_BYTE_ARR; + um_data->u_data[3] = &samplePeak; //*used (Puddlepeak, Ripplepeak, Waterfall) + um_data->u_type[3] = UMT_BYTE; + um_data->u_data[4] = &FFT_MajorPeak; //*used (Ripplepeak, Freqmap, Freqmatrix, Freqpixels, Freqwave, Gravfreq, Rocktaves, Waterfall) + um_data->u_type[4] = UMT_FLOAT; + um_data->u_data[5] = &my_magnitude; // used (New) + um_data->u_type[5] = UMT_FLOAT; + um_data->u_data[6] = &maxVol; // assigned in effect function from UI element!!! (Puddlepeak, Ripplepeak, Waterfall) + um_data->u_type[6] = UMT_BYTE; + um_data->u_data[7] = &binNum; // assigned in effect function from UI element!!! (Puddlepeak, Ripplepeak, Waterfall) + um_data->u_type[7] = UMT_BYTE; + } + + // Reset I2S peripheral for good measure + i2s_driver_uninstall(I2S_NUM_0); + periph_module_reset(PERIPH_I2S0_MODULE); + + delay(100); // Give that poor microphone some time to setup. + switch (dmType) { + case 1: + DEBUGSR_PRINT(F("AR: Generic I2S Microphone - ")); DEBUGSR_PRINTLN(F(I2S_MIC_CHANNEL_TEXT)); + audioSource = new I2SSource(SAMPLE_RATE, BLOCK_SIZE); + delay(100); + if (audioSource) audioSource->initialize(i2swsPin, i2ssdPin, i2sckPin); + break; + case 2: + DEBUGSR_PRINTLN(F("AR: ES7243 Microphone (right channel only).")); + audioSource = new ES7243(SAMPLE_RATE, BLOCK_SIZE); + delay(100); + if (audioSource) audioSource->initialize(sdaPin, sclPin, i2swsPin, i2ssdPin, i2sckPin, mclkPin); + break; + case 3: + DEBUGSR_PRINT(F("AR: SPH0645 Microphone - ")); DEBUGSR_PRINTLN(F(I2S_MIC_CHANNEL_TEXT)); + audioSource = new SPH0654(SAMPLE_RATE, BLOCK_SIZE); + delay(100); + audioSource->initialize(i2swsPin, i2ssdPin, i2sckPin); + break; + case 4: + DEBUGSR_PRINT(F("AR: Generic I2S Microphone with Master Clock - ")); DEBUGSR_PRINTLN(F(I2S_MIC_CHANNEL_TEXT)); + audioSource = new I2SSource(SAMPLE_RATE, BLOCK_SIZE); + delay(100); + if (audioSource) audioSource->initialize(i2swsPin, i2ssdPin, i2sckPin, mclkPin); + break; + case 5: + DEBUGSR_PRINT(F("AR: I2S PDM Microphone - ")); DEBUGSR_PRINTLN(F(I2S_MIC_CHANNEL_TEXT)); + audioSource = new I2SSource(SAMPLE_RATE, BLOCK_SIZE); + delay(100); + if (audioSource) audioSource->initialize(i2swsPin, i2ssdPin); + break; + case 0: + default: + DEBUGSR_PRINTLN(F("AR: Analog Microphone (left channel only).")); + audioSource = new I2SAdcSource(SAMPLE_RATE, BLOCK_SIZE); + delay(100); + if (audioSource) audioSource->initialize(audioPin); + break; + } + delay(250); // give microphone enough time to initialise + + if (!audioSource) enabled = false; // audio failed to initialise + if (enabled) onUpdateBegin(false); // create FFT task + if (FFT_Task == nullptr) enabled = false; // FFT task creation failed + if (enabled) disableSoundProcessing = false; // all good - enable audio processing + + if((!audioSource) || (!audioSource->isInitialized())) { // audio source failed to initialize. Still stay "enabled", as there might be input arriving via UDP Sound Sync + DEBUGSR_PRINTLN(F("AR: Failed to initialize sound input driver. Please check input PIN settings.")); + disableSoundProcessing = true; + } + + initDone = true; + } + + + /* + * connected() is called every time the WiFi is (re)connected + * Use it to initialize network interfaces + */ + void connected() + { + if (audioSyncPort > 0 || (audioSyncEnabled & 0x03)) { + #ifndef ESP8266 + udpSyncConnected = fftUdp.beginMulticast(IPAddress(239, 0, 0, 1), audioSyncPort); + #else + udpSyncConnected = fftUdp.beginMulticast(WiFi.localIP(), IPAddress(239, 0, 0, 1), audioSyncPort); + #endif + } + } + + + /* + * loop() is called continuously. Here you can check for events, read sensors, etc. + * + * Tips: + * 1. You can use "if (WLED_CONNECTED)" to check for a successful network connection. + * Additionally, "if (WLED_MQTT_CONNECTED)" is available to check for a connection to an MQTT broker. + * + * 2. Try to avoid using the delay() function. NEVER use delays longer than 10 milliseconds. + * Instead, use a timer check as shown here. + */ + void loop() + { + static unsigned long lastUMRun = millis(); + + if (!enabled) { + disableSoundProcessing = true; // keep processing suspended (FFT task) + lastUMRun = millis(); // update time keeping + return; + } + // We cannot wait indefinitely before processing audio data + if (strip.isUpdating() && (millis() - lastUMRun < 2)) return; // be nice, but not too nice + + // suspend local sound processing when "real time mode" is active (E131, UDP, ADALIGHT, ARTNET) + if ( (realtimeOverride == REALTIME_OVERRIDE_NONE) // please odd other orrides here if needed + &&( (realtimeMode == REALTIME_MODE_GENERIC) + ||(realtimeMode == REALTIME_MODE_E131) + ||(realtimeMode == REALTIME_MODE_UDP) + ||(realtimeMode == REALTIME_MODE_ADALIGHT) + ||(realtimeMode == REALTIME_MODE_ARTNET) ) ) // please add other modes here if needed + { + #ifdef WLED_DEBUG + if ((disableSoundProcessing == false) && (audioSyncEnabled == 0)) { // we just switched to "disabled" + DEBUG_PRINTLN("[AR userLoop] realtime mode active - audio processing suspended."); + DEBUG_PRINTF( " RealtimeMode = %d; RealtimeOverride = %d\n", int(realtimeMode), int(realtimeOverride)); + } + #endif + disableSoundProcessing = true; + } else { + #ifdef WLED_DEBUG + if ((disableSoundProcessing == true) && (audioSyncEnabled == 0)) { // we just switched to "disabled" + DEBUG_PRINTLN("[AR userLoop] realtime mode ended - audio processing resumed."); + DEBUG_PRINTF( " RealtimeMode = %d; RealtimeOverride = %d\n", int(realtimeMode), int(realtimeOverride)); + } + #endif + if ((disableSoundProcessing == true) && (audioSyncEnabled == 0)) lastUMRun = millis(); // just left "realtime mode" - update timekeeping + disableSoundProcessing = false; + } + + if (audioSyncEnabled & 0x02) disableSoundProcessing = true; // make sure everything is disabled IF in audio Receive mode + if (audioSyncEnabled & 0x01) disableSoundProcessing = false; // keep running audio IF we're in audio Transmit mode + if(!audioSource->isInitialized()) disableSoundProcessing = true; // no audio source + + + // Only run the sampling code IF we're not in Receive mode or realtime mode + if (!(audioSyncEnabled & 0x02) && !disableSoundProcessing) { + bool agcEffect = false; + if (soundAgc > AGC_NUM_PRESETS) soundAgc = 0; // make sure that AGC preset is valid (to avoid array bounds violation) + + unsigned long t_now = millis(); // remember current time + int userloopDelay = int(t_now - lastUMRun); + if (lastUMRun == 0) userloopDelay=0; // startup - don't have valid data from last run. + + #ifdef WLED_DEBUG + // complain when audio userloop has been delayed for long time. Currently we need userloop running between 500 and 1500 times per second. + if ((userloopDelay > 23) && !disableSoundProcessing && (audioSyncEnabled == 0)) { + DEBUG_PRINTF("[AR userLoop] hickup detected -> was inactive for last %d millis!\n", userloopDelay); + } + #endif + + // run filters, and repeat in case of loop delays (hick-up compensation) + if (userloopDelay <2) userloopDelay = 0; // minor glitch, no problem + if (userloopDelay >200) userloopDelay = 200; // limit number of filter re-runs + do { + getSample(); // run microphone sampling filters + agcAvg(t_now - userloopDelay); // Calculated the PI adjusted value as sampleAvg + userloopDelay -= 2; // advance "simulated time" by 2ms + } while (userloopDelay > 0); + lastUMRun = t_now; // update time keeping + + // update samples for effects (raw, smooth) + volumeSmth = (soundAgc) ? sampleAgc : sampleAvg; + volumeRaw = (soundAgc) ? rawSampleAgc: sampleRaw; + // update FFTMagnitude, taking into account AGC amplification + my_magnitude = FFT_Magnitude; // / 16.0f, 8.0f, 4.0f done in effects + if (soundAgc) my_magnitude *= multAgc; + if (volumeSmth < 1 ) my_magnitude = 0.001f; // noise gate closed - mute + + limitSampleDynamics(); // optional - makes volumeSmth very smooth and fluent + + // update WebServer UI + uint8_t knownMode = strip.getFirstSelectedSeg().mode; // 1st selected segment is more appropriate than main segment + if (lastMode != knownMode) { // only execute if mode changes + char lineBuffer[4]; + extractModeName(knownMode, JSON_mode_names, lineBuffer, 3); // use of JSON_mode_names is deprecated, use nullptr + agcEffect = (lineBuffer[1] == 226 && lineBuffer[2] == 153); // && (lineBuffer[3] == 170 || lineBuffer[3] == 171 ) encoding of ♪ or ♫ + // agcEffect = (lineBuffer[4] == 240 && lineBuffer[5] == 159 && lineBuffer[6] == 142 && lineBuffer[7] == 154 ); //encoding of 🎚 No clue why as not found here https://www.iemoji.com/view/emoji/918/objects/level-slider + lastMode = knownMode; + } + + // update inputLevel Slider based on current AGC gain + if ((soundAgc>0) && agcEffect) { + unsigned long now_time = millis(); + + // "user kick" feature - if user has moved the slider by at least 32 units, we "kick" AGC gain by 30% (up or down) + // only once in 3.5 seconds + if ( (lastMode == knownMode) + && (abs(last_user_inputLevel - inputLevel) > 31) + && (now_time - last_kick_time > 3500)) { + if (last_user_inputLevel > inputLevel) multAgc *= 0.60; // down -> reduce gain + if (last_user_inputLevel < inputLevel) multAgc *= 1.50; // up -> increase gain + last_kick_time = now_time; + } + + int new_user_inputLevel = 128.0f * multAgc; // scale AGC multiplier so that "1" is at 128 + if (multAgc > 1.0f) new_user_inputLevel = 128.0f * (((multAgc - 1.0f) / 4.0f) +1.0f); // compress range so we can show values up to 4 + new_user_inputLevel = MIN(MAX(new_user_inputLevel, 0),255); + + // update user interfaces - restrict frequency to avoid flooding UI's with small changes + if (( ((now_time - last_update_time > 3500) && (abs(new_user_inputLevel - inputLevel) > 2)) // small change - every 3.5 sec (max) + ||((now_time - last_update_time > 2200) && (abs(new_user_inputLevel - inputLevel) > 15)) // medium change + ||((now_time - last_update_time > 1200) && (abs(new_user_inputLevel - inputLevel) > 31))) // BIG change - every second + && !strip.isUpdating()) // don't interfere while strip is updating + { + inputLevel = new_user_inputLevel; // change of least 3 units -> update user variable + updateInterfaces(CALL_MODE_WS_SEND); // is this the correct way to notify UIs ? Yes says blazoncek + last_update_time = now_time; + last_user_inputLevel = new_user_inputLevel; + } + } + } + + + // UDP Microphone Sync - receive mode + if ((audioSyncEnabled & 0x02) && udpSyncConnected) { + // Only run the audio listener code if we're in Receive mode + static float syncVolumeSmth = 0; + bool have_new_sample = false; + if (millis() - lastTime > delayMs) { + have_new_sample = receiveAudioData(); + if (have_new_sample) last_UDPTime = millis(); + lastTime = millis(); + } + if (have_new_sample) syncVolumeSmth = volumeSmth; // remember received sample + else volumeSmth = syncVolumeSmth; // restore originally received sample for next run of dynamics limiter + limitSampleDynamics(); // run dynamics limiter on received volumeSmth, to hide jumps and hickups + } + + #if defined(MIC_LOGGER) || defined(MIC_SAMPLING_LOG) || defined(FFT_SAMPLING_LOG) + EVERY_N_MILLIS(20) { + logAudio(); + } + #endif + + // peak sample from last 5 seconds + if ((millis() - sampleMaxTimer) > CYCLE_SAMPLEMAX) { + sampleMaxTimer = millis(); + maxSample5sec = (0.15 * maxSample5sec) + 0.85 *((soundAgc) ? sampleAgc : sampleAvg); // reset, and start with some smoothing + if (sampleAvg < 1) maxSample5sec = 0; // noise gate + } else { + if ((sampleAvg >= 1)) maxSample5sec = fmaxf(maxSample5sec, (soundAgc) ? rawSampleAgc : sampleRaw); // follow maximum volume + } + //UDP Microphone Sync - transmit mode + if ((audioSyncEnabled & 0x01) && (millis() - lastTime > 20)) { + // Only run the transmit code IF we're in Transmit mode + transmitAudioData(); + lastTime = millis(); + } + + } + + + bool getUMData(um_data_t **data) + { + if (!data || !enabled) return false; // no pointer provided by caller or not enabled -> exit + *data = um_data; + return true; + } + + + void onUpdateBegin(bool init) + { +#ifdef WLED_DEBUG + fftTime = sampleTime = 0; +#endif + // gracefully suspend FFT task (if running) + disableSoundProcessing = true; + + // reset sound data + micDataReal = 0.0f; + volumeRaw = 0; volumeSmth = 0; + sampleAgc = 0; sampleAvg = 0; + sampleRaw = 0; rawSampleAgc = 0; + my_magnitude = 0; FFT_Magnitude = 0; FFT_MajorPeak = 0; + multAgc = 1; + + if (init && FFT_Task) { + vTaskSuspend(FFT_Task); // update is about to begin, disable task to prevent crash + } else { + // update has failed or create task requested + if (FFT_Task) + vTaskResume(FFT_Task); + else +// xTaskCreatePinnedToCore( + xTaskCreate( // no need to "pin" this task to core #0 + FFTcode, // Function to implement the task + "FFT", // Name of the task + 5000, // Stack size in words + NULL, // Task input parameter + 1, // Priority of the task + &FFT_Task // Task handle +// , 0 // Core where the task should run + ); + } + micDataReal = 0.0f; // just to ber sure + if (enabled) disableSoundProcessing = false; + } + + + /** + * handleButton() can be used to override default button behaviour. Returning true + * will prevent button working in a default way. + */ + bool handleButton(uint8_t b) { + yield(); + // crude way of determining if audio input is analog + // better would be for AudioSource to implement getType() + if (enabled + && dmType == 0 && audioPin>=0 + && (buttonType[b] == BTN_TYPE_ANALOG || buttonType[b] == BTN_TYPE_ANALOG_INVERTED) + ) { + return true; + } + return false; + } + + + /* + * addToJsonInfo() can be used to add custom entries to the /json/info part of the JSON API. + * Creating an "u" object allows you to add custom key/value pairs to the Info section of the WLED web UI. + * Below it is shown how this could be used for e.g. a light sensor + */ + void addToJsonInfo(JsonObject& root) + { + char myStringBuffer[16]; // buffer for snprintf() + JsonObject user = root["u"]; + if (user.isNull()) user = root.createNestedObject("u"); + + JsonArray infoArr = user.createNestedArray(FPSTR(_name)); + + String uiDomString = F(""); + infoArr.add(uiDomString); + + if (enabled) { + infoArr = user.createNestedArray(F("Input level")); + uiDomString = F("
"); // + infoArr.add(uiDomString); + + // current Audio input + infoArr = user.createNestedArray(F("Audio Source")); + if (audioSyncEnabled & 0x02) { + // UDP sound sync - receive mode + infoArr.add("UDP sound sync"); + if (udpSyncConnected) { + if (millis() - last_UDPTime < 2500) + infoArr.add(" - receiving"); + else + infoArr.add(" - idle"); + } else { + infoArr.add(" - no network"); + } + } else { + // Analog or I2S digital input + if (audioSource && (audioSource->isInitialized())) { + // audio source sucessfully configured + if(audioSource->getType() == AudioSource::Type_I2SAdc) { + infoArr.add("ADC analog"); + } else { + infoArr.add("I2S digital"); + } + // input level or "silence" + if (maxSample5sec > 1.0) { + float my_usage = 100.0f * (maxSample5sec / 255.0f); + snprintf(myStringBuffer, 15, " - peak %3d%%", int(my_usage)); + infoArr.add(myStringBuffer); + } else { + infoArr.add(" - quiet"); + } + } else { + // error during audio source setup + infoArr.add("not initialized"); + infoArr.add(" - check GPIO config"); + } + } + + // Sound processing (FFT and input filters) + infoArr = user.createNestedArray(F("Sound Processing")); + if (audioSource && (disableSoundProcessing == false)) { + infoArr.add("running"); + } else { + infoArr.add("suspended"); + } + + // AGC or manual Gain + if((soundAgc==0) && (disableSoundProcessing == false) && !(audioSyncEnabled & 0x02)) { + infoArr = user.createNestedArray(F("Manual Gain")); + float myGain = ((float)sampleGain/40.0f * (float)inputLevel/128.0f) + 1.0f/16.0f; // non-AGC gain from presets + infoArr.add(roundf(myGain*100.0f) / 100.0f); + infoArr.add("x"); + } + if(soundAgc && (disableSoundProcessing == false) && !(audioSyncEnabled & 0x02)) { + infoArr = user.createNestedArray(F("AGC Gain")); + infoArr.add(roundf(multAgc*100.0f) / 100.0f); + infoArr.add("x"); + } + + // UDP Sound Sync status + infoArr = user.createNestedArray(F("UDP Sound Sync")); + if (audioSyncEnabled) { + if (audioSyncEnabled & 0x01) { + infoArr.add("send mode"); + } else if (audioSyncEnabled & 0x02) { + infoArr.add("receive mode"); + } + } else + infoArr.add("off"); + + #ifdef WLED_DEBUG + infoArr = user.createNestedArray(F("Sampling time")); + infoArr.add(sampleTime); + infoArr.add("ms"); + infoArr = user.createNestedArray(F("FFT time")); + infoArr.add(fftTime-sampleTime); + infoArr.add("ms"); + #endif + } + } + + + /* + * addToJsonState() can be used to add custom entries to the /json/state part of the JSON API (state object). + * Values in the state object may be modified by connected clients + */ + void addToJsonState(JsonObject& root) + { + if (!initDone) return; // prevent crash on boot applyPreset() + JsonObject usermod = root[FPSTR(_name)]; + if (usermod.isNull()) { + usermod = root.createNestedObject(FPSTR(_name)); + } + usermod["on"] = enabled; + } + + + /* + * readFromJsonState() can be used to receive data clients send to the /json/state part of the JSON API (state object). + * Values in the state object may be modified by connected clients + */ + void readFromJsonState(JsonObject& root) + { + if (!initDone) return; // prevent crash on boot applyPreset() + bool prevEnabled = enabled; + JsonObject usermod = root[FPSTR(_name)]; + if (!usermod.isNull()) { + if (usermod[FPSTR(_enabled)].is()) { + enabled = usermod[FPSTR(_enabled)].as(); + if (prevEnabled != enabled) onUpdateBegin(!enabled); + } + if (usermod[FPSTR(_inputLvl)].is()) { + inputLevel = min(255,max(0,usermod[FPSTR(_inputLvl)].as())); + } + } + } + + + /* + * addToConfig() can be used to add custom persistent settings to the cfg.json file in the "um" (usermod) object. + * It will be called by WLED when settings are actually saved (for example, LED settings are saved) + * If you want to force saving the current state, use serializeConfig() in your loop(). + * + * CAUTION: serializeConfig() will initiate a filesystem write operation. + * It might cause the LEDs to stutter and will cause flash wear if called too often. + * Use it sparingly and always in the loop, never in network callbacks! + * + * addToConfig() will make your settings editable through the Usermod Settings page automatically. + * + * Usermod Settings Overview: + * - Numeric values are treated as floats in the browser. + * - If the numeric value entered into the browser contains a decimal point, it will be parsed as a C float + * before being returned to the Usermod. The float data type has only 6-7 decimal digits of precision, and + * doubles are not supported, numbers will be rounded to the nearest float value when being parsed. + * The range accepted by the input field is +/- 1.175494351e-38 to +/- 3.402823466e+38. + * - If the numeric value entered into the browser doesn't contain a decimal point, it will be parsed as a + * C int32_t (range: -2147483648 to 2147483647) before being returned to the usermod. + * Overflows or underflows are truncated to the max/min value for an int32_t, and again truncated to the type + * used in the Usermod when reading the value from ArduinoJson. + * - Pin values can be treated differently from an integer value by using the key name "pin" + * - "pin" can contain a single or array of integer values + * - On the Usermod Settings page there is simple checking for pin conflicts and warnings for special pins + * - Red color indicates a conflict. Yellow color indicates a pin with a warning (e.g. an input-only pin) + * - Tip: use int8_t to store the pin value in the Usermod, so a -1 value (pin not set) can be used + * + * See usermod_v2_auto_save.h for an example that saves Flash space by reusing ArduinoJson key name strings + * + * If you need a dedicated settings page with custom layout for your Usermod, that takes a lot more work. + * You will have to add the setting to the HTML, xml.cpp and set.cpp manually. + * See the WLED Soundreactive fork (code and wiki) for reference. https://github.com/atuline/WLED + * + * I highly recommend checking out the basics of ArduinoJson serialization and deserialization in order to use custom settings! + */ + void addToConfig(JsonObject& root) + { + JsonObject top = root.createNestedObject(FPSTR(_name)); + top[FPSTR(_enabled)] = enabled; + + JsonObject amic = top.createNestedObject(FPSTR(_analogmic)); + amic["pin"] = audioPin; + + JsonObject dmic = top.createNestedObject(FPSTR(_digitalmic)); + dmic[F("type")] = dmType; + JsonArray pinArray = dmic.createNestedArray("pin"); + pinArray.add(i2ssdPin); + pinArray.add(i2swsPin); + pinArray.add(i2sckPin); + pinArray.add(mclkPin); + pinArray.add(sdaPin); + pinArray.add(sclPin); + + JsonObject cfg = top.createNestedObject("cfg"); + cfg[F("squelch")] = soundSquelch; + cfg[F("gain")] = sampleGain; + cfg[F("AGC")] = soundAgc; + + JsonObject dynLim = top.createNestedObject("dynamics"); + dynLim[F("Limiter")] = limiterOn; + dynLim[F("Rise")] = attackTime; + dynLim[F("Fall")] = decayTime; + + JsonObject sync = top.createNestedObject("sync"); + sync[F("port")] = audioSyncPort; + sync[F("mode")] = audioSyncEnabled; + } + + + /* + * readFromConfig() can be used to read back the custom settings you added with addToConfig(). + * This is called by WLED when settings are loaded (currently this only happens immediately after boot, or after saving on the Usermod Settings page) + * + * readFromConfig() is called BEFORE setup(). This means you can use your persistent values in setup() (e.g. pin assignments, buffer sizes), + * but also that if you want to write persistent values to a dynamic buffer, you'd need to allocate it here instead of in setup. + * If you don't know what that is, don't fret. It most likely doesn't affect your use case :) + * + * Return true in case the config values returned from Usermod Settings were complete, or false if you'd like WLED to save your defaults to disk (so any missing values are editable in Usermod Settings) + * + * getJsonValue() returns false if the value is missing, or copies the value into the variable provided and returns true if the value is present + * The configComplete variable is true only if the "exampleUsermod" object and all values are present. If any values are missing, WLED will know to call addToConfig() to save them + * + * This function is guaranteed to be called on boot, but could also be called every time settings are updated + */ + bool readFromConfig(JsonObject& root) + { + JsonObject top = root[FPSTR(_name)]; + bool configComplete = !top.isNull(); + + configComplete &= getJsonValue(top[FPSTR(_enabled)], enabled); + + configComplete &= getJsonValue(top[FPSTR(_analogmic)]["pin"], audioPin); + + configComplete &= getJsonValue(top[FPSTR(_digitalmic)]["type"], dmType); + configComplete &= getJsonValue(top[FPSTR(_digitalmic)]["pin"][0], i2ssdPin); + configComplete &= getJsonValue(top[FPSTR(_digitalmic)]["pin"][1], i2swsPin); + configComplete &= getJsonValue(top[FPSTR(_digitalmic)]["pin"][2], i2sckPin); + configComplete &= getJsonValue(top[FPSTR(_digitalmic)]["pin"][3], mclkPin); + configComplete &= getJsonValue(top[FPSTR(_digitalmic)]["pin"][4], sdaPin); + configComplete &= getJsonValue(top[FPSTR(_digitalmic)]["pin"][5], sclPin); + + configComplete &= getJsonValue(top["cfg"][F("squelch")], soundSquelch); + configComplete &= getJsonValue(top["cfg"][F("gain")], sampleGain); + configComplete &= getJsonValue(top["cfg"][F("AGC")], soundAgc); + + configComplete &= getJsonValue(top["dynamics"][F("Limiter")], limiterOn); + configComplete &= getJsonValue(top["dynamics"][F("Rise")], attackTime); + configComplete &= getJsonValue(top["dynamics"][F("Fall")], decayTime); + + configComplete &= getJsonValue(top["sync"][F("port")], audioSyncPort); + configComplete &= getJsonValue(top["sync"][F("mode")], audioSyncEnabled); + + return configComplete; + } + + + void appendConfigData() + { + oappend(SET_F("dd=addDropdown('AudioReactive','digitalmic:type');")); + oappend(SET_F("addOption(dd,'Generic Analog',0);")); + oappend(SET_F("addOption(dd,'Generic I2S',1);")); + oappend(SET_F("addOption(dd,'ES7243',2);")); + oappend(SET_F("addOption(dd,'SPH0654',3);")); + oappend(SET_F("addOption(dd,'Generic I2S with Mclk',4);")); + oappend(SET_F("addOption(dd,'Generic I2S PDM',5);")); + oappend(SET_F("dd=addDropdown('AudioReactive','cfg:AGC');")); + oappend(SET_F("addOption(dd,'Off',0);")); + oappend(SET_F("addOption(dd,'Normal',1);")); + oappend(SET_F("addOption(dd,'Vivid',2);")); + oappend(SET_F("addOption(dd,'Lazy',3);")); + + oappend(SET_F("dd=addDropdown('AudioReactive','dynamics:Limiter');")); + oappend(SET_F("addOption(dd,'Off',0);")); + oappend(SET_F("addOption(dd,'On',1);")); + oappend(SET_F("addInfo('AudioReactive:dynamics:Limiter',0,' Limiter On ');")); // 0 is field type, 1 is actual field + //oappend(SET_F("addInfo('AudioReactive:dynamics:Rise',0,'min. ');")); + oappend(SET_F("addInfo('AudioReactive:dynamics:Rise',1,' ms
(volume reactive FX only)');")); + //oappend(SET_F("addInfo('AudioReactive:dynamics:Fall',0,'min. ');")); + oappend(SET_F("addInfo('AudioReactive:dynamics:Fall',1,' ms
(volume reactive FX only)');")); + + oappend(SET_F("dd=addDropdown('AudioReactive','sync:mode');")); + oappend(SET_F("addOption(dd,'Off',0);")); + oappend(SET_F("addOption(dd,'Send',1);")); + oappend(SET_F("addOption(dd,'Receive',2);")); + oappend(SET_F("addInfo('AudioReactive:digitalmic:type',1,'requires reboot!');")); // 0 is field type, 1 is actual field + oappend(SET_F("addInfo('AudioReactive:digitalmic:pin[]',0,'I2S SD');")); + oappend(SET_F("addInfo('AudioReactive:digitalmic:pin[]',1,'I2S WS');")); + oappend(SET_F("addInfo('AudioReactive:digitalmic:pin[]',2,'I2S SCK');")); + oappend(SET_F("addInfo('AudioReactive:digitalmic:pin[]',3,'I2S Master CLK');")); + oappend(SET_F("addInfo('AudioReactive:digitalmic:pin[]',4,'I2C SDA');")); + oappend(SET_F("addInfo('AudioReactive:digitalmic:pin[]',5,'I2C SCL');")); + } + + + /* + * handleOverlayDraw() is called just before every show() (LED strip update frame) after effects have set the colors. + * Use this to blank out some LEDs or set them to a different color regardless of the set effect mode. + * Commonly used for custom clocks (Cronixie, 7 segment) + */ + //void handleOverlayDraw() + //{ + //strip.setPixelColor(0, RGBW32(0,0,0,0)) // set the first pixel to black + //} + + + /* + * getId() allows you to optionally give your V2 usermod an unique ID (please define it in const.h!). + * This could be used in the future for the system to determine whether your usermod is installed. + */ + uint16_t getId() + { + return USERMOD_ID_AUDIOREACTIVE; + } +}; + +// strings to reduce flash memory usage (used more than twice) +const char AudioReactive::_name[] PROGMEM = "AudioReactive"; +const char AudioReactive::_enabled[] PROGMEM = "enabled"; +const char AudioReactive::_inputLvl[] PROGMEM = "inputLevel"; +const char AudioReactive::_analogmic[] PROGMEM = "analogmic"; +const char AudioReactive::_digitalmic[] PROGMEM = "digitalmic"; +const char AudioReactive::UDP_SYNC_HEADER[] PROGMEM = "00002"; // new sync header version, as format no longer compatible with previous structure +const char AudioReactive::UDP_SYNC_HEADER_v1[] PROGMEM = "00001"; // old sync header version - need to add backwards-compatibility feature diff --git a/usermods/audioreactive/audio_source.h b/usermods/audioreactive/audio_source.h new file mode 100644 index 0000000000..090b46e6ab --- /dev/null +++ b/usermods/audioreactive/audio_source.h @@ -0,0 +1,508 @@ +#pragma once + +#include +#include "wled.h" +#include +#include +#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 4, 0) +#include +#include +#endif + +//#include +//#include +//#include + + +/* ToDo: remove. ES7243 is controlled via compiler defines + Until this configuration is moved to the webinterface +*/ + +// if you have problems to get your microphone work on the left channel, uncomment the following line +//#define I2S_USE_RIGHT_CHANNEL // (experimental) define this to use right channel (digital mics only) +#ifdef I2S_USE_RIGHT_CHANNEL +#define I2S_MIC_CHANNEL I2S_CHANNEL_FMT_ONLY_RIGHT +#define I2S_MIC_CHANNEL_TEXT "right channel only." +#else +#define I2S_MIC_CHANNEL I2S_CHANNEL_FMT_ONLY_LEFT +#define I2S_MIC_CHANNEL_TEXT "left channel only." +#endif + +// Uncomment the line below to utilize ADC1 _exclusively_ for I2S sound input. +// benefit: analog mic inputs will be sampled contiously -> better response times and less "glitches" +// WARNING: this option WILL lock-up your device in case that any other analogRead() operation is performed; +// for example if you want to read "analog buttons" +//#define I2S_GRAB_ADC1_COMPLETELY // (experimental) continously sample analog ADC microphone. WARNING will cause analogRead() lock-up + +// data type requested from the I2S driver - currently we always use 32bit +//#define I2S_USE_16BIT_SAMPLES // (experimental) define this to request 16bit - more efficient but possibly less compatible +#ifdef I2S_USE_16BIT_SAMPLES +#define I2S_SAMPLE_RESOLUTION I2S_BITS_PER_SAMPLE_16BIT +#define I2S_datatype int16_t +#define I2S_unsigned_datatype uint16_t +#undef I2S_SAMPLE_DOWNSCALE_TO_16BIT +#else +#define I2S_SAMPLE_RESOLUTION I2S_BITS_PER_SAMPLE_32BIT +#define I2S_datatype int32_t +#define I2S_unsigned_datatype uint32_t +#define I2S_SAMPLE_DOWNSCALE_TO_16BIT +#endif + +/* Interface class + AudioSource serves as base class for all microphone types + This enables accessing all microphones with one single interface + which simplifies the caller code +*/ +class AudioSource { + public: + /* All public methods are virtual, so they can be overridden + Everything but the destructor is also removed, to make sure each mic + Implementation provides its version of this function + */ + virtual ~AudioSource() {}; + + /* Initialize + This function needs to take care of anything that needs to be done + before samples can be obtained from the microphone. + */ + virtual void initialize(int8_t = I2S_PIN_NO_CHANGE, int8_t = I2S_PIN_NO_CHANGE, int8_t = I2S_PIN_NO_CHANGE, int8_t = I2S_PIN_NO_CHANGE, int8_t = I2S_PIN_NO_CHANGE, int8_t = I2S_PIN_NO_CHANGE) = 0; + + /* Deinitialize + Release all resources and deactivate any functionality that is used + by this microphone + */ + virtual void deinitialize() = 0; + + /* getSamples + Read num_samples from the microphone, and store them in the provided + buffer + */ + virtual void getSamples(float *buffer, uint16_t num_samples) = 0; + + /* check if the audio source driver was initialized successfully */ + virtual bool isInitialized(void) {return(_initialized);} + + /* identify Audiosource type - I2S-ADC or I2S-digital */ + typedef enum{Type_unknown=0, Type_I2SAdc=1, Type_I2SDigital=2} AudioSourceType; + virtual AudioSourceType getType(void) {return(Type_I2SDigital);} // default is "I2S digital source" - ADC type overrides this method + + protected: + /* Post-process audio sample - currently on needed for I2SAdcSource*/ + virtual I2S_datatype postProcessSample(I2S_datatype sample_in) {return(sample_in);} // default method can be overriden by instances (ADC) that need sample postprocessing + + // Private constructor, to make sure it is not callable except from derived classes + AudioSource(int sampleRate, int blockSize) : + _sampleRate(sampleRate), + _blockSize(blockSize), + _initialized(false) + {}; + + int _sampleRate; // Microphone sampling rate + int _blockSize; // I2S block size + bool _initialized; // Gets set to true if initialization is successful +}; + +/* Basic I2S microphone source + All functions are marked virtual, so derived classes can replace them +*/ +class I2SSource : public AudioSource { + public: + I2SSource(int sampleRate, int blockSize) : + AudioSource(sampleRate, blockSize) { + _config = { + .mode = i2s_mode_t(I2S_MODE_MASTER | I2S_MODE_RX), + .sample_rate = _sampleRate, + .bits_per_sample = I2S_SAMPLE_RESOLUTION, + .channel_format = I2S_MIC_CHANNEL, +#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 2, 0) + .communication_format = i2s_comm_format_t(I2S_COMM_FORMAT_STAND_I2S), +#else + .communication_format = i2s_comm_format_t(I2S_COMM_FORMAT_I2S | I2S_COMM_FORMAT_I2S_MSB), +#endif + .intr_alloc_flags = ESP_INTR_FLAG_LEVEL1, + .dma_buf_count = 8, + .dma_buf_len = _blockSize + }; + } + + virtual void initialize(int8_t i2swsPin = I2S_PIN_NO_CHANGE, int8_t i2ssdPin = I2S_PIN_NO_CHANGE, int8_t i2sckPin = I2S_PIN_NO_CHANGE, int8_t mclkPin = I2S_PIN_NO_CHANGE, int8_t = I2S_PIN_NO_CHANGE, int8_t = I2S_PIN_NO_CHANGE) { + if (i2swsPin != I2S_PIN_NO_CHANGE && i2ssdPin != I2S_PIN_NO_CHANGE) { + if (!pinManager.allocatePin(i2swsPin, true, PinOwner::UM_Audioreactive) || + !pinManager.allocatePin(i2ssdPin, false, PinOwner::UM_Audioreactive)) { // #206 + return; + } + } + + // i2ssckPin needs special treatment, since it might be unused on PDM mics + if (i2sckPin != I2S_PIN_NO_CHANGE) { + if (!pinManager.allocatePin(i2sckPin, true, PinOwner::UM_Audioreactive)) return; + } else { + // This is an I2S PDM microphone, these microphones only use a clock and + // data line, to make it simpler to debug, use the WS pin as CLK and SD + // pin as DATA + _config.mode = i2s_mode_t(I2S_MODE_MASTER | I2S_MODE_RX | I2S_MODE_PDM); // Change mode to pdm if clock pin not provided + } + + // Reserve the master clock pin if provided + _mclkPin = mclkPin; + if (mclkPin != I2S_PIN_NO_CHANGE) { + if(!pinManager.allocatePin(mclkPin, true, PinOwner::UM_Audioreactive)) return; + _routeMclk(mclkPin); + } + + _pinConfig = { + .bck_io_num = i2sckPin, + .ws_io_num = i2swsPin, + .data_out_num = I2S_PIN_NO_CHANGE, + .data_in_num = i2ssdPin + }; + + esp_err_t err = i2s_driver_install(I2S_NUM_0, &_config, 0, nullptr); + if (err != ESP_OK) { + DEBUGSR_PRINTF("Failed to install i2s driver: %d\n", err); + return; + } + + err = i2s_set_pin(I2S_NUM_0, &_pinConfig); + if (err != ESP_OK) { + DEBUGSR_PRINTF("Failed to set i2s pin config: %d\n", err); + return; + } + + _initialized = true; + } + + virtual void deinitialize() { + _initialized = false; + esp_err_t err = i2s_driver_uninstall(I2S_NUM_0); + if (err != ESP_OK) { + DEBUGSR_PRINTF("Failed to uninstall i2s driver: %d\n", err); + return; + } + if (_pinConfig.ws_io_num != I2S_PIN_NO_CHANGE) pinManager.deallocatePin(_pinConfig.ws_io_num, PinOwner::UM_Audioreactive); + if (_pinConfig.data_in_num != I2S_PIN_NO_CHANGE) pinManager.deallocatePin(_pinConfig.data_in_num, PinOwner::UM_Audioreactive); + if (_pinConfig.bck_io_num != I2S_PIN_NO_CHANGE) pinManager.deallocatePin(_pinConfig.bck_io_num, PinOwner::UM_Audioreactive); + // Release the master clock pin + if (_mclkPin != I2S_PIN_NO_CHANGE) pinManager.deallocatePin(_mclkPin, PinOwner::UM_Audioreactive); + } + + virtual void getSamples(float *buffer, uint16_t num_samples) { + if (_initialized) { + esp_err_t err; + size_t bytes_read = 0; /* Counter variable to check if we actually got enough data */ + I2S_datatype newSamples[num_samples]; /* Intermediary sample storage */ + + err = i2s_read(I2S_NUM_0, (void *)newSamples, sizeof(newSamples), &bytes_read, portMAX_DELAY); + if (err != ESP_OK) { + DEBUGSR_PRINTF("Failed to get samples: %d\n", err); + return; + } + + // For correct operation, we need to read exactly sizeof(samples) bytes from i2s + if (bytes_read != sizeof(newSamples)) { + DEBUGSR_PRINTF("Failed to get enough samples: wanted: %d read: %d\n", sizeof(newSamples), bytes_read); + return; + } + + // Store samples in sample buffer and update DC offset + for (int i = 0; i < num_samples; i++) { + + newSamples[i] = postProcessSample(newSamples[i]); // perform postprocessing (needed for ADC samples) + + float currSample = 0.0f; +#ifdef I2S_SAMPLE_DOWNSCALE_TO_16BIT + currSample = (float) newSamples[i] / 65536.0f; // 32bit input -> 16bit; keeping lower 16bits as decimal places +#else + currSample = (float) newSamples[i]; // 16bit input -> use as-is +#endif + buffer[i] = currSample; + } + } + } + + protected: + void _routeMclk(int8_t mclkPin) { + /* Enable the mclk routing depending on the selected mclk pin + Only I2S_NUM_0 is supported + */ + if (mclkPin == GPIO_NUM_0) { + PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO0_U, FUNC_GPIO0_CLK_OUT1); + WRITE_PERI_REG(PIN_CTRL,0xFFF0); + } else if (mclkPin == GPIO_NUM_1) { + PIN_FUNC_SELECT(PERIPHS_IO_MUX_U0TXD_U, FUNC_U0TXD_CLK_OUT3); + WRITE_PERI_REG(PIN_CTRL, 0xF0F0); + } else { + PIN_FUNC_SELECT(PERIPHS_IO_MUX_U0RXD_U, FUNC_U0RXD_CLK_OUT2); + WRITE_PERI_REG(PIN_CTRL, 0xFF00); + } + } + + i2s_config_t _config; + i2s_pin_config_t _pinConfig; + int8_t _mclkPin; +}; + +/* ES7243 Microphone + This is an I2S microphone that requires ininitialization over + I2C before I2S data can be received +*/ +class ES7243 : public I2SSource { + private: + // I2C initialization functions for ES7243 + void _es7243I2cBegin() { + Wire.begin(pin_ES7243_SDA, pin_ES7243_SCL, 100000U); + } + + void _es7243I2cWrite(uint8_t reg, uint8_t val) { +#ifndef ES7243_ADDR + Wire.beginTransmission(0x13); +#else + Wire.beginTransmission(ES7243_ADDR); +#endif + Wire.write((uint8_t)reg); + Wire.write((uint8_t)val); + Wire.endTransmission(); + } + + void _es7243InitAdc() { + _es7243I2cBegin(); + _es7243I2cWrite(0x00, 0x01); + _es7243I2cWrite(0x06, 0x00); + _es7243I2cWrite(0x05, 0x1B); + _es7243I2cWrite(0x01, 0x00); // 0x00 for 24 bit to match INMP441 - not sure if this needs adjustment to get 16bit samples from I2S + _es7243I2cWrite(0x08, 0x43); + _es7243I2cWrite(0x05, 0x13); + } + +public: + ES7243(int sampleRate, int blockSize) : + I2SSource(sampleRate, blockSize) { + _config.channel_format = I2S_CHANNEL_FMT_ONLY_RIGHT; + }; + + void initialize(int8_t sdaPin, int8_t sclPin, int8_t i2swsPin, int8_t i2ssdPin, int8_t i2sckPin, int8_t mclkPin) { + // Reserve SDA and SCL pins of the I2C interface + if (!pinManager.allocatePin(sdaPin, true, PinOwner::HW_I2C) || + !pinManager.allocatePin(sclPin, true, PinOwner::HW_I2C)) { + return; + } + + pin_ES7243_SDA = sdaPin; + pin_ES7243_SCL = sclPin; + + // First route mclk, then configure ADC over I2C, then configure I2S + _es7243InitAdc(); + I2SSource::initialize(i2swsPin, i2ssdPin, i2sckPin, mclkPin); + } + + void deinitialize() { + // Release SDA and SCL pins of the I2C interface + pinManager.deallocatePin(pin_ES7243_SDA, PinOwner::HW_I2C); + pinManager.deallocatePin(pin_ES7243_SCL, PinOwner::HW_I2C); + I2SSource::deinitialize(); + } + + private: + int8_t pin_ES7243_SDA; + int8_t pin_ES7243_SCL; +}; + +/* ADC over I2S Microphone + This microphone is an ADC pin sampled via the I2S interval + This allows to use the I2S API to obtain ADC samples with high sample rates + without the need of manual timing of the samples +*/ +class I2SAdcSource : public I2SSource { + public: + I2SAdcSource(int sampleRate, int blockSize) : + I2SSource(sampleRate, blockSize) { + _config = { + .mode = i2s_mode_t(I2S_MODE_MASTER | I2S_MODE_RX | I2S_MODE_ADC_BUILT_IN), + .sample_rate = _sampleRate, + .bits_per_sample = I2S_SAMPLE_RESOLUTION, + .channel_format = I2S_CHANNEL_FMT_ONLY_LEFT, +#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 2, 0) + .communication_format = i2s_comm_format_t(I2S_COMM_FORMAT_STAND_I2S), +#else + .communication_format = i2s_comm_format_t(I2S_COMM_FORMAT_I2S | I2S_COMM_FORMAT_I2S_MSB), +#endif + .intr_alloc_flags = ESP_INTR_FLAG_LEVEL1, + .dma_buf_count = 8, + .dma_buf_len = _blockSize, + .use_apll = false, + .tx_desc_auto_clear = false, + .fixed_mclk = 0 + }; + } + + /* identify Audiosource type - I2S-ADC*/ + AudioSourceType getType(void) {return(Type_I2SAdc);} + + void initialize(int8_t audioPin, int8_t = I2S_PIN_NO_CHANGE, int8_t = I2S_PIN_NO_CHANGE, int8_t = I2S_PIN_NO_CHANGE, int8_t = I2S_PIN_NO_CHANGE, int8_t = I2S_PIN_NO_CHANGE) { + _myADCchannel = 0x0F; + if(!pinManager.allocatePin(audioPin, false, PinOwner::UM_Audioreactive)) { + DEBUGSR_PRINTF("failed to allocate GPIO for audio analog input: %d\n", audioPin); + return; + } + _audioPin = audioPin; + + // Determine Analog channel. Only Channels on ADC1 are supported + int8_t channel = digitalPinToAnalogChannel(_audioPin); + if (channel > 9) { + DEBUGSR_PRINTF("Incompatible GPIO used for audio in: %d\n", _audioPin); + return; + } else { + adc_gpio_init(ADC_UNIT_1, adc_channel_t(channel)); + _myADCchannel = channel; + } + + // Install Driver + esp_err_t err = i2s_driver_install(I2S_NUM_0, &_config, 0, nullptr); + if (err != ESP_OK) { + DEBUGSR_PRINTF("Failed to install i2s driver: %d\n", err); + return; + } + + // Enable I2S mode of ADC + err = i2s_set_adc_mode(ADC_UNIT_1, adc1_channel_t(channel)); + if (err != ESP_OK) { + DEBUGSR_PRINTF("Failed to set i2s adc mode: %d\n", err); + return; + } + // adc1_config_channel_atten(adc1_channel_t(channel), ADC_ATTEN_DB_11)); //see https://github.com/espressif/arduino-esp32/blob/master/libraries/ESP32/examples/I2S/HiFreq_ADC/HiFreq_ADC.ino + + #if defined(I2S_GRAB_ADC1_COMPLETELY) + // according to docs from espressif, the ADC needs to be started explicitly + // fingers crossed + err = i2s_adc_enable(I2S_NUM_0); + if (err != ESP_OK) { + DEBUGSR_PRINTF("Failed to enable i2s adc: %d\n", err); + //return; + } + #else + err = i2s_adc_disable(I2S_NUM_0); + //err = i2s_stop(I2S_NUM_0); + if (err != ESP_OK) { + DEBUGSR_PRINTF("Failed to initially disable i2s adc: %d\n", err); + } + #endif + + _initialized = true; + } + + + I2S_datatype postProcessSample(I2S_datatype sample_in) { + static I2S_datatype lastADCsample = 0; // last good sample + static unsigned int broken_samples_counter = 0; // number of consecutive broken (and fixed) ADC samples + I2S_datatype sample_out = 0; + + // bring sample down down to 16bit unsigned + I2S_unsigned_datatype rawData = * reinterpret_cast (&sample_in); // C++ acrobatics to get sample as "unsigned" + #ifndef I2S_USE_16BIT_SAMPLES + rawData = (rawData >> 16) & 0xFFFF; // scale input down from 32bit -> 16bit + I2S_datatype lastGoodSample = lastADCsample / 16384 ; // prepare "last good sample" accordingly (26bit-> 12bit with correct sign handling) + #else + rawData = rawData & 0xFFFF; // input is already in 16bit, just mask off possible junk + I2S_datatype lastGoodSample = lastADCsample * 4; // prepare "last good sample" accordingly (10bit-> 12bit) + #endif + + // decode ADC sample data fields + uint16_t the_channel = (rawData >> 12) & 0x000F; // upper 4 bit = ADC channel + uint16_t the_sample = rawData & 0x0FFF; // lower 12bit -> ADC sample (unsigned) + I2S_datatype finalSample = (int(the_sample) - 2048); // convert unsigned sample to signed (centered at 0); + + if ((the_channel != _myADCchannel) && (_myADCchannel != 0x0F)) { // 0x0F means "don't know what my channel is" + // fix bad sample + finalSample = lastGoodSample; // replace with last good ADC sample + broken_samples_counter ++; + if (broken_samples_counter > 256) _myADCchannel = 0x0F; // too many bad samples in a row -> disable sample corrections + //Serial.print("\n!ADC rogue sample 0x"); Serial.print(rawData, HEX); Serial.print("\tchannel:");Serial.println(the_channel); + } else broken_samples_counter = 0; // good sample - reset counter + + // back to original resolution + #ifndef I2S_USE_16BIT_SAMPLES + finalSample = finalSample << 16; // scale up from 16bit -> 32bit; + #endif + + finalSample = finalSample / 4; // mimic old analog driver behaviour (12bit -> 10bit) + sample_out = (3 * finalSample + lastADCsample) / 4; // apply low-pass filter (2-tap FIR) + //sample_out = (finalSample + lastADCsample) / 2; // apply stronger low-pass filter (2-tap FIR) + + lastADCsample = sample_out; // update ADC last sample + return(sample_out); + } + + + void getSamples(float *buffer, uint16_t num_samples) { + /* Enable ADC. This has to be enabled and disabled directly before and + * after sampling, otherwise Wifi dies + */ + if (_initialized) { + #if !defined(I2S_GRAB_ADC1_COMPLETELY) + // old code - works for me without enable/disable, at least on ESP32. + //esp_err_t err = i2s_start(I2S_NUM_0); + esp_err_t err = i2s_adc_enable(I2S_NUM_0); + if (err != ESP_OK) { + DEBUGSR_PRINTF("Failed to enable i2s adc: %d\n", err); + return; + } + #endif + + I2SSource::getSamples(buffer, num_samples); + + #if !defined(I2S_GRAB_ADC1_COMPLETELY) + // old code - works for me without enable/disable, at least on ESP32. + err = i2s_adc_disable(I2S_NUM_0); //i2s_adc_disable() may cause crash with IDF 4.4 (https://github.com/espressif/arduino-esp32/issues/6832) + //err = i2s_stop(I2S_NUM_0); + if (err != ESP_OK) { + DEBUGSR_PRINTF("Failed to disable i2s adc: %d\n", err); + return; + } + #endif + } + } + + void deinitialize() { + pinManager.deallocatePin(_audioPin, PinOwner::UM_Audioreactive); + _initialized = false; + _myADCchannel = 0x0F; + + esp_err_t err; + #if defined(I2S_GRAB_ADC1_COMPLETELY) + // according to docs from espressif, the ADC needs to be stopped explicitly + // fingers crossed + err = i2s_adc_disable(I2S_NUM_0); + if (err != ESP_OK) { + DEBUGSR_PRINTF("Failed to disable i2s adc: %d\n", err); + } + #endif + + i2s_stop(I2S_NUM_0); + err = i2s_driver_uninstall(I2S_NUM_0); + if (err != ESP_OK) { + DEBUGSR_PRINTF("Failed to uninstall i2s driver: %d\n", err); + return; + } + } + + private: + int8_t _audioPin; + int8_t _myADCchannel = 0x0F; // current ADC channel for analog input. 0x0F means "undefined" +}; + +/* SPH0645 Microphone + This is an I2S microphone with some timing quirks that need + special consideration. +*/ +class SPH0654 : public I2SSource { + public: + SPH0654(int sampleRate, int blockSize) : + I2SSource(sampleRate, blockSize) + {} + + void initialize(uint8_t i2swsPin, uint8_t i2ssdPin, uint8_t i2sckPin, int8_t = I2S_PIN_NO_CHANGE, int8_t = I2S_PIN_NO_CHANGE, int8_t = I2S_PIN_NO_CHANGE) { + I2SSource::initialize(i2swsPin, i2ssdPin, i2sckPin); + REG_SET_BIT(I2S_TIMING_REG(I2S_NUM_0), BIT(9)); + REG_SET_BIT(I2S_CONF_REG(I2S_NUM_0), I2S_RX_MSB_SHIFT); + } +}; diff --git a/usermods/audioreactive/readme.md b/usermods/audioreactive/readme.md new file mode 100644 index 0000000000..f14c168dcf --- /dev/null +++ b/usermods/audioreactive/readme.md @@ -0,0 +1,36 @@ +# Audioreactive usermod + +This usermod allows controlling LEDs using audio input. Audio input can be either microphone or analog-in (AUX) using appropriate adapter. +Supported microphones range from analog (MAX4466, MAX9814, ...) to digital (INMP441, ICS-43434, ...). + +The usermod does audio processing and provides data structure that specially written effect can use. + +The usermod **does not** provide effects or draws anything to LED strip/matrix. + +## Installation + +Add `-D USERMOD_AUDIOREACTIVE` to your PlatformIO environment as well as `arduinoFFT` to your `lib_deps`. +If you are not using PlatformIO (which you should) try adding `#define USERMOD_AUDIOREACTIVE` to *my_config.h* and make sure you have _arduinoFFT_ library downloaded and installed. + +Customised _arduinoFFT_ library for use with this usermod can be found at https://github.com/blazoncek/arduinoFFT.git + +## Configuration + +All parameters are runtime configurable though some may require hard boot after change (I2S microphone or selected GPIOs). + +If you want to define default GPIOs during compile time use the following (default values in parentheses): + +- `DMTYPE=x` : defines digital microphone type: 0=analog, 1=generic I2S, 2=ES7243 I2S, 3=SPH0645 I2S, 4=generic I2S with master clock, 5=PDM I2S +- `AUDIOPIN=x` : GPIO for analog microphone/AUX-in (36) +- `I2S_SDPIN=x` : GPIO for SD pin on digital mcrophone (32) +- `I2S_WSPIN=x` : GPIO for WS pin on digital mcrophone (15) +- `I2S_CKPIN=x` : GPIO for SCK pin on digital mcrophone (14) +- `ES7243_SDAPIN` : GPIO for I2C SDA pin on ES7243 microphone (-1) +- `ES7243_SCLPIN` : GPIO for I2C SCL pin on ES7243 microphone (-1) +- `MCLK_PIN=x` : GPIO for master clock pin on digital mcrophone (-1) + +**NOTE** Due to the fact that usermod uses I2S peripherial for analog audio sampling, use of analog *buttons* (i.e. potentiometers) is disabled while running this usermod with analog microphone. + +## Release notes + +2022-06 Ported from [soundreactive](https://github.com/atuline/WLED) by @blazoncek (AKA Blaz Kristan) diff --git a/usermods/mpu6050_imu/usermod_mpu6050_imu.h b/usermods/mpu6050_imu/usermod_mpu6050_imu.h index 4aa2a128fb..4ce51c4337 100644 --- a/usermods/mpu6050_imu/usermod_mpu6050_imu.h +++ b/usermods/mpu6050_imu/usermod_mpu6050_imu.h @@ -42,14 +42,6 @@ #include "Wire.h" #endif -#ifdef ARDUINO_ARCH_ESP32 - #define HW_PIN_SCL 22 - #define HW_PIN_SDA 21 -#else - #define HW_PIN_SCL 5 - #define HW_PIN_SDA 4 -#endif - // ================================================================ // === INTERRUPT DETECTION ROUTINE === // ================================================================ @@ -93,7 +85,7 @@ class MPU6050Driver : public Usermod { * setup() is called once at boot. WiFi is not yet connected at this point. */ void setup() { - PinManagerPinType pins[2] = { { HW_PIN_SCL, true }, { HW_PIN_SDA, true } }; + PinManagerPinType pins[2] = { { i2c_scl, true }, { i2c_sda, true } }; if (!pinManager.allocateMultiplePins(pins, 2, PinOwner::HW_I2C)) { enabled = false; return; } // join I2C bus (I2Cdev library doesn't do this automatically) #if I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE @@ -258,20 +250,20 @@ class MPU6050Driver : public Usermod { * addToJsonState() can be used to add custom entries to the /json/state part of the JSON API (state object). * Values in the state object may be modified by connected clients */ - void addToJsonState(JsonObject& root) - { + //void addToJsonState(JsonObject& root) + //{ //root["user0"] = userVar0; - } + //} /* * readFromJsonState() can be used to receive data clients send to the /json/state part of the JSON API (state object). * Values in the state object may be modified by connected clients */ - void readFromJsonState(JsonObject& root) - { + //void readFromJsonState(JsonObject& root) + //{ //if (root["bri"] == 255) DEBUG_PRINTLN(F("Don't burn down your garage!")); - } + //} /* @@ -279,13 +271,13 @@ class MPU6050Driver : public Usermod { * It will be called by WLED when settings are actually saved (for example, LED settings are saved) * I highly recommend checking out the basics of ArduinoJson serialization and deserialization in order to use custom settings! */ - void addToConfig(JsonObject& root) - { - JsonObject top = root.createNestedObject("MPU6050_IMU"); - JsonArray pins = top.createNestedArray("pin"); - pins.add(HW_PIN_SCL); - pins.add(HW_PIN_SDA); - } +// void addToConfig(JsonObject& root) +// { +// JsonObject top = root.createNestedObject("MPU6050_IMU"); +// JsonArray pins = top.createNestedArray("pin"); +// pins.add(HW_PIN_SCL); +// pins.add(HW_PIN_SDA); +// } /* * getId() allows you to optionally give your V2 usermod an unique ID (please define it in const.h!). diff --git a/usermods/multi_relay/usermod_multi_relay.h b/usermods/multi_relay/usermod_multi_relay.h index b38c8a25c7..75258608f1 100644 --- a/usermods/multi_relay/usermod_multi_relay.h +++ b/usermods/multi_relay/usermod_multi_relay.h @@ -468,13 +468,17 @@ class MultiRelay : public Usermod { if (user.isNull()) user = root.createNestedObject("u"); - JsonArray infoArr = user.createNestedArray(F("Number of relays")); //name + JsonArray infoArr = user.createNestedArray(FPSTR(_name)); //name infoArr.add(String(getActiveRelayCount())); + infoArr.add(F(" relays")); String uiDomString; for (uint8_t i=0; i"); - uiDomString += F("Relay "); - uiDomString += i; - uiDomString += F(" "); - JsonArray infoArr = user.createNestedArray(uiDomString); // timer value - - infoArr.add(_relay[i].state ? "on" : "off"); + uiDomString += F(""); + infoArr.add(uiDomString); } } } diff --git a/usermods/stairway_wipe_basic/stairway-wipe-usermod-v2.h b/usermods/stairway_wipe_basic/stairway-wipe-usermod-v2.h index 08d551be02..238ec7d9ca 100644 --- a/usermods/stairway_wipe_basic/stairway-wipe-usermod-v2.h +++ b/usermods/stairway_wipe_basic/stairway-wipe-usermod-v2.h @@ -96,7 +96,7 @@ class StairwayWipeUsermod : public Usermod { resetTimebase(); //make sure wipe starts from beginning //set wipe direction - WS2812FX::Segment& seg = strip.getSegment(0); + Segment& seg = strip.getSegment(0); bool doReverse = (userVar0 == 2); seg.setOption(1, doReverse); diff --git a/usermods/stairway_wipe_basic/wled06_usermod.ino b/usermods/stairway_wipe_basic/wled06_usermod.ino index eeece4438a..c1264ebfb2 100644 --- a/usermods/stairway_wipe_basic/wled06_usermod.ino +++ b/usermods/stairway_wipe_basic/wled06_usermod.ino @@ -89,7 +89,7 @@ void startWipe() resetTimebase(); //make sure wipe starts from beginning //set wipe direction - WS2812FX::Segment& seg = strip.getSegment(0); + Segment& seg = strip.getSegment(0); bool doReverse = (userVar0 == 2); seg.setOption(1, doReverse); diff --git a/usermods/usermod_v2_auto_save/usermod_v2_auto_save.h b/usermods/usermod_v2_auto_save/usermod_v2_auto_save.h index c289cc32ad..4cd728d8fb 100644 --- a/usermods/usermod_v2_auto_save/usermod_v2_auto_save.h +++ b/usermods/usermod_v2_auto_save/usermod_v2_auto_save.h @@ -64,6 +64,7 @@ class AutoSaveUsermod : public Usermod { PSTR("~ %02d-%02d %02d:%02d:%02d ~"), month(localTime), day(localTime), hour(localTime), minute(localTime), second(localTime)); + cacheInvalidate++; // force reload of presets savePreset(autoSavePreset, presetNameBuffer); } diff --git a/usermods/usermod_v2_four_line_display/usermod_v2_four_line_display.h b/usermods/usermod_v2_four_line_display/usermod_v2_four_line_display.h index a49961dd57..3fcf661289 100644 --- a/usermods/usermod_v2_four_line_display/usermod_v2_four_line_display.h +++ b/usermods/usermod_v2_four_line_display/usermod_v2_four_line_display.h @@ -24,54 +24,31 @@ // //The SCL and SDA pins are defined here. +#ifndef FLD_PIN_SCL + #define FLD_PIN_SCL i2c_scl +#endif +#ifndef FLD_PIN_SDA + #define FLD_PIN_SDA i2c_sda +#endif +#ifndef FLD_PIN_CLOCKSPI + #define FLD_PIN_CLOCKSPI spi_sclk +#endif + #ifndef FLD_PIN_DATASPI + #define FLD_PIN_DATASPI spi_mosi +#endif +#ifndef FLD_PIN_CS + #define FLD_PIN_CS spi_cs +#endif #ifdef ARDUINO_ARCH_ESP32 - #define HW_PIN_SCL 22 - #define HW_PIN_SDA 21 - #define HW_PIN_CLOCKSPI 18 - #define HW_PIN_DATASPI 23 - #ifndef FLD_PIN_SCL - #define FLD_PIN_SCL 22 - #endif - #ifndef FLD_PIN_SDA - #define FLD_PIN_SDA 21 - #endif - #ifndef FLD_PIN_CLOCKSPI - #define FLD_PIN_CLOCKSPI 18 - #endif - #ifndef FLD_PIN_DATASPI - #define FLD_PIN_DATASPI 23 - #endif #ifndef FLD_PIN_DC #define FLD_PIN_DC 19 #endif - #ifndef FLD_PIN_CS - #define FLD_PIN_CS 5 - #endif #ifndef FLD_PIN_RESET #define FLD_PIN_RESET 26 #endif #else - #define HW_PIN_SCL 5 - #define HW_PIN_SDA 4 - #define HW_PIN_CLOCKSPI 14 - #define HW_PIN_DATASPI 13 - #ifndef FLD_PIN_SCL - #define FLD_PIN_SCL 5 - #endif - #ifndef FLD_PIN_SDA - #define FLD_PIN_SDA 4 - #endif - #ifndef FLD_PIN_CLOCKSPI - #define FLD_PIN_CLOCKSPI 14 - #endif - #ifndef FLD_PIN_DATASPI - #define FLD_PIN_DATASPI 13 - #endif #ifndef FLD_PIN_DC #define FLD_PIN_DC 12 - #endif - #ifndef FLD_PIN_CS - #define FLD_PIN_CS 15 #endif #ifndef FLD_PIN_RESET #define FLD_PIN_RESET 16 @@ -192,13 +169,14 @@ class FourLineDisplayUsermod : public Usermod { bool isHW; PinOwner po = PinOwner::UM_FourLineDisplay; if (type == SSD1306_SPI || type == SSD1306_SPI64) { - isHW = (ioPin[0]==HW_PIN_CLOCKSPI && ioPin[1]==HW_PIN_DATASPI); + isHW = (ioPin[0]==spi_sclk && ioPin[1]==spi_mosi); + if (isHW) po = PinOwner::HW_SPI; // allow multiple allocations of HW I2C bus pins PinManagerPinType pins[5] = { { ioPin[0], true }, { ioPin[1], true }, { ioPin[2], true }, { ioPin[3], true }, { ioPin[4], true }}; if (!pinManager.allocateMultiplePins(pins, 5, po)) { type=NONE; return; } } else { - isHW = (ioPin[0]==HW_PIN_SCL && ioPin[1]==HW_PIN_SDA); + isHW = (ioPin[0]==i2c_scl && ioPin[1]==i2c_sda); + if (isHW) po = PinOwner::HW_I2C; // allow multiple allocations of HW I2C bus pins PinManagerPinType pins[2] = { { ioPin[0], true }, { ioPin[1], true } }; - if (ioPin[0]==HW_PIN_SCL && ioPin[1]==HW_PIN_SDA) po = PinOwner::HW_I2C; // allow multiple allocations of HW I2C bus pins if (!pinManager.allocateMultiplePins(pins, 2, po)) { type=NONE; return; } } @@ -718,8 +696,14 @@ class FourLineDisplayUsermod : public Usermod { if (pinsChanged || type!=newType) { if (type != NONE) delete u8x8; PinOwner po = PinOwner::UM_FourLineDisplay; - if (ioPin[0]==HW_PIN_SCL && ioPin[1]==HW_PIN_SDA) po = PinOwner::HW_I2C; // allow multiple allocations of HW I2C bus pins - pinManager.deallocateMultiplePins((const uint8_t *)ioPin, (type == SSD1306_SPI || type == SSD1306_SPI64) ? 5 : 2, po); + bool isSPI = (type == SSD1306_SPI || type == SSD1306_SPI64); + if (isSPI) { + if (ioPin[0]==spi_sclk && ioPin[1]==spi_mosi) po = PinOwner::HW_SPI; // allow multiple allocations of HW SPI bus pins + pinManager.deallocateMultiplePins((const uint8_t *)ioPin, 5, po); + } else { + if (ioPin[0]==i2c_scl && ioPin[1]==i2c_sda) po = PinOwner::HW_I2C; // allow multiple allocations of HW I2C bus pins + pinManager.deallocateMultiplePins((const uint8_t *)ioPin, 2, po); + } for (byte i=0; i<5; i++) ioPin[i] = newPin[i]; if (ioPin[0]<0 || ioPin[1]<0) { // data & clock must be > -1 type = NONE; diff --git a/usermods/usermod_v2_four_line_display_ALT/usermod_v2_four_line_display_ALT.h b/usermods/usermod_v2_four_line_display_ALT/usermod_v2_four_line_display_ALT.h index 383accc52c..38a055a579 100644 --- a/usermods/usermod_v2_four_line_display_ALT/usermod_v2_four_line_display_ALT.h +++ b/usermods/usermod_v2_four_line_display_ALT/usermod_v2_four_line_display_ALT.h @@ -25,54 +25,32 @@ // //The SCL and SDA pins are defined here. +#ifndef FLD_PIN_SCL + #define FLD_PIN_SCL i2c_scl +#endif +#ifndef FLD_PIN_SDA + #define FLD_PIN_SDA i2c_sda +#endif +#ifndef FLD_PIN_CLOCKSPI + #define FLD_PIN_CLOCKSPI spi_sclk +#endif + #ifndef FLD_PIN_DATASPI + #define FLD_PIN_DATASPI spi_mosi +#endif +#ifndef FLD_PIN_CS + #define FLD_PIN_CS spi_cs +#endif + #ifdef ARDUINO_ARCH_ESP32 - #define HW_PIN_SCL 22 - #define HW_PIN_SDA 21 - #define HW_PIN_CLOCKSPI 18 - #define HW_PIN_DATASPI 23 - #ifndef FLD_PIN_SCL - #define FLD_PIN_SCL 22 - #endif - #ifndef FLD_PIN_SDA - #define FLD_PIN_SDA 21 - #endif - #ifndef FLD_PIN_CLOCKSPI - #define FLD_PIN_CLOCKSPI 18 - #endif - #ifndef FLD_PIN_DATASPI - #define FLD_PIN_DATASPI 23 - #endif #ifndef FLD_PIN_DC #define FLD_PIN_DC 19 #endif - #ifndef FLD_PIN_CS - #define FLD_PIN_CS 5 - #endif #ifndef FLD_PIN_RESET #define FLD_PIN_RESET 26 #endif #else - #define HW_PIN_SCL 5 - #define HW_PIN_SDA 4 - #define HW_PIN_CLOCKSPI 14 - #define HW_PIN_DATASPI 13 - #ifndef FLD_PIN_SCL - #define FLD_PIN_SCL 5 - #endif - #ifndef FLD_PIN_SDA - #define FLD_PIN_SDA 4 - #endif - #ifndef FLD_PIN_CLOCKSPI - #define FLD_PIN_CLOCKSPI 14 - #endif - #ifndef FLD_PIN_DATASPI - #define FLD_PIN_DATASPI 13 - #endif #ifndef FLD_PIN_DC #define FLD_PIN_DC 12 - #endif - #ifndef FLD_PIN_CS - #define FLD_PIN_CS 15 #endif #ifndef FLD_PIN_RESET #define FLD_PIN_RESET 16 @@ -92,13 +70,20 @@ #define SCREEN_TIMEOUT_MS 60*1000 // 1 min // Minimum time between redrawing screen in ms -#define USER_LOOP_REFRESH_RATE_MS 1000 +#define REFRESH_RATE_MS 1000 // Extra char (+1) for null #define LINE_BUFFER_SIZE 16+1 #define MAX_JSON_CHARS 19+1 #define MAX_MODE_LINE_SPACE 13+1 + +#ifdef ARDUINO_ARCH_ESP32 +static TaskHandle_t Display_Task = nullptr; +void DisplayTaskCode(void * parameter); +#endif + + typedef enum { NONE = 0, SSD1306, // U8X8_SSD1306_128X32_UNIVISION_HW_I2C @@ -112,10 +97,15 @@ typedef enum { class FourLineDisplayUsermod : public Usermod { + public: + FourLineDisplayUsermod() { if (!instance) instance = this; } + static FourLineDisplayUsermod* getInstance(void) { return instance; } private: + static FourLineDisplayUsermod *instance; bool initDone = false; + volatile bool drawing = false; // HW interface & configuration U8X8 *u8x8 = nullptr; // pointer to U8X8 display object @@ -132,8 +122,8 @@ class FourLineDisplayUsermod : public Usermod { bool flip = false; // flip display 180° uint8_t contrast = 10; // screen contrast uint8_t lineHeight = 1; // 1 row or 2 rows - uint16_t refreshRate = USER_LOOP_REFRESH_RATE_MS; // in ms - uint32_t screenTimeout = SCREEN_TIMEOUT_MS; // in ms + uint16_t refreshRate = REFRESH_RATE_MS; // in ms + uint32_t screenTimeout = SCREEN_TIMEOUT_MS; // in ms bool sleepMode = true; // allow screen sleep? bool clockMode = false; // display clock bool showSeconds = true; // display clock with seconds @@ -195,6 +185,123 @@ class FourLineDisplayUsermod : public Usermod { u8x8_cad_EndTransfer(u8x8_struct); } + /** + * Wrappers for screen drawing + */ + void setFlipMode(uint8_t mode) { + if (type == NONE || !enabled) return; + u8x8->setFlipMode(mode); + } + void setContrast(uint8_t contrast) { + if (type == NONE || !enabled) return; + u8x8->setContrast(contrast); + } + void drawString(uint8_t col, uint8_t row, const char *string, bool ignoreLH=false) { + if (type == NONE || !enabled) return; + u8x8->setFont(u8x8_font_chroma48medium8_r); + if (!ignoreLH && lineHeight==2) u8x8->draw1x2String(col, row, string); + else u8x8->drawString(col, row, string); + } + void draw2x2String(uint8_t col, uint8_t row, const char *string) { + if (type == NONE || !enabled) return; + u8x8->setFont(u8x8_font_chroma48medium8_r); + u8x8->draw2x2String(col, row, string); + } + void drawGlyph(uint8_t col, uint8_t row, char glyph, const uint8_t *font, bool ignoreLH=false) { + if (type == NONE || !enabled) return; + u8x8->setFont(font); + if (!ignoreLH && lineHeight==2) u8x8->draw1x2Glyph(col, row, glyph); + else u8x8->drawGlyph(col, row, glyph); + } + void draw2x2Glyph(uint8_t col, uint8_t row, char glyph, const uint8_t *font) { + if (type == NONE || !enabled) return; + u8x8->setFont(font); + u8x8->draw2x2Glyph(col, row, glyph); + } + uint8_t getCols() { + if (type==NONE || !enabled) return 0; + return u8x8->getCols(); + } + void clear() { + if (type == NONE || !enabled) return; + u8x8->clear(); + } + void setPowerSave(uint8_t save) { + if (type == NONE || !enabled) return; + u8x8->setPowerSave(save); + } + + void center(String &line, uint8_t width) { + int len = line.length(); + if (len0; i--) line = ' ' + line; + for (byte i=line.length(); i 11) { AmPmHour -= 12; isitAM = false; } + if (AmPmHour == 0) { AmPmHour = 12; } + } + if (knownHour != hourCurrent) { + // only update date when hour changes + sprintf_P(lineBuffer, PSTR("%s %2d "), monthShortStr(month(localTime)), day(localTime)); + draw2x2String(2, lineHeight==1 ? 0 : lineHeight, lineBuffer); // adjust for 8 line displays, draw month and day + } + sprintf_P(lineBuffer,PSTR("%2d:%02d"), (useAMPM ? AmPmHour : hourCurrent), minuteCurrent); + draw2x2String(2, lineHeight*2, lineBuffer); //draw hour, min. blink ":" depending on odd/even seconds + if (useAMPM) drawString(12, lineHeight*2, (isitAM ? "AM" : "PM"), true); //draw am/pm if using 12 time + + drawStatusIcons(); //icons power, wifi, timer, etc + + knownMinute = minuteCurrent; + knownHour = hourCurrent; + } + if (showSeconds && secondCurrent != lastSecond) { + lastSecond = secondCurrent; + draw2x2String(6, lineHeight*2, secondCurrent%2 ? " " : ":"); + sprintf_P(lineBuffer, PSTR("%02d"), secondCurrent); + drawString(12, lineHeight*2+1, lineBuffer, true); // even with double sized rows print seconds in 1 line + } + drawing = false; + } + public: // gets called once at boot. Do all initialization that doesn't depend on @@ -205,13 +312,32 @@ class FourLineDisplayUsermod : public Usermod { bool isHW, isSPI = (type == SSD1306_SPI || type == SSD1306_SPI64); PinOwner po = PinOwner::UM_FourLineDisplay; if (isSPI) { - isHW = (ioPin[0]==HW_PIN_CLOCKSPI && ioPin[1]==HW_PIN_DATASPI); - PinManagerPinType pins[5] = { { ioPin[0], true }, { ioPin[1], true }, { ioPin[2], true }, { ioPin[3], true }, { ioPin[4], true }}; - if (!pinManager.allocateMultiplePins(pins, 5, po)) { type=NONE; return; } - } else { - isHW = (ioPin[0]==HW_PIN_SCL && ioPin[1]==HW_PIN_SDA); + uint8_t hw_sclk = spi_sclk<0 ? HW_PIN_CLOCKSPI : spi_sclk; + uint8_t hw_mosi = spi_mosi<0 ? HW_PIN_DATASPI : spi_mosi; + if (ioPin[0] < 0 || ioPin[1] < 0) { + ioPin[0] = hw_sclk; + ioPin[1] = hw_mosi; + } + isHW = (ioPin[0]==hw_sclk && ioPin[1]==hw_mosi); + PinManagerPinType cspins[3] = { { ioPin[2], true }, { ioPin[3], true }, { ioPin[4], true } }; + if (!pinManager.allocateMultiplePins(cspins, 3, PinOwner::UM_FourLineDisplay)) { type=NONE; return; } + if (isHW) po = PinOwner::HW_SPI; // allow multiple allocations of HW I2C bus pins PinManagerPinType pins[2] = { { ioPin[0], true }, { ioPin[1], true } }; - if (ioPin[0]==HW_PIN_SCL && ioPin[1]==HW_PIN_SDA) po = PinOwner::HW_I2C; // allow multiple allocations of HW I2C bus pins + if (!pinManager.allocateMultiplePins(pins, 2, po)) { + pinManager.deallocateMultiplePins(cspins, 3, PinOwner::UM_FourLineDisplay); + type = NONE; + return; + } + } else { + uint8_t hw_scl = i2c_scl<0 ? HW_PIN_SCL : i2c_scl; + uint8_t hw_sda = i2c_sda<0 ? HW_PIN_SDA : i2c_sda; + if (ioPin[0] < 0 || ioPin[1] < 0) { + ioPin[0] = hw_scl; + ioPin[1] = hw_sda; + } + isHW = (ioPin[0]==hw_scl && ioPin[1]==hw_sda); + if (isHW) po = PinOwner::HW_I2C; // allow multiple allocations of HW I2C bus pins + PinManagerPinType pins[2] = { {ioPin[0], true }, { ioPin[1], true } }; if (!pinManager.allocateMultiplePins(pins, 2, po)) { type=NONE; return; } } @@ -304,6 +430,7 @@ class FourLineDisplayUsermod : public Usermod { setPowerSave(0); //drawString(0, 0, "Loading..."); overlayLogo(3500); + onUpdateBegin(false); // create Display task initDone = true; } @@ -319,63 +446,13 @@ class FourLineDisplayUsermod : public Usermod { * Da loop. */ void loop() { + #ifndef ARDUINO_ARCH_ESP32 if (!enabled || strip.isUpdating()) return; unsigned long now = millis(); if (now < nextUpdate) return; nextUpdate = now + ((displayTurnedOff && clockMode && showSeconds) ? 1000 : refreshRate); redraw(false); - } - - /** - * Wrappers for screen drawing - */ - void setFlipMode(uint8_t mode) { - if (type == NONE || !enabled) return; - u8x8->setFlipMode(mode); - } - void setContrast(uint8_t contrast) { - if (type == NONE || !enabled) return; - u8x8->setContrast(contrast); - } - void drawString(uint8_t col, uint8_t row, const char *string, bool ignoreLH=false) { - if (type == NONE || !enabled) return; - u8x8->setFont(u8x8_font_chroma48medium8_r); - if (!ignoreLH && lineHeight==2) u8x8->draw1x2String(col, row, string); - else u8x8->drawString(col, row, string); - } - void draw2x2String(uint8_t col, uint8_t row, const char *string) { - if (type == NONE || !enabled) return; - u8x8->setFont(u8x8_font_chroma48medium8_r); - u8x8->draw2x2String(col, row, string); - } - void drawGlyph(uint8_t col, uint8_t row, char glyph, const uint8_t *font, bool ignoreLH=false) { - if (type == NONE || !enabled) return; - u8x8->setFont(font); - if (!ignoreLH && lineHeight==2) u8x8->draw1x2Glyph(col, row, glyph); - else u8x8->drawGlyph(col, row, glyph); - } - void draw2x2Glyph(uint8_t col, uint8_t row, char glyph, const uint8_t *font) { - if (type == NONE || !enabled) return; - u8x8->setFont(font); - u8x8->draw2x2Glyph(col, row, glyph); - } - uint8_t getCols() { - if (type==NONE || !enabled) return 0; - return u8x8->getCols(); - } - void clear() { - if (type == NONE || !enabled) return; - u8x8->clear(); - } - void setPowerSave(uint8_t save) { - if (type == NONE || !enabled) return; - u8x8->setPowerSave(save); - } - - void center(String &line, uint8_t width) { - int len = line.length(); - if (len0; i--) line = ' ' + line; - for (byte i=line.length(); i127)) { + // remove note symbol from effect names + for (byte i=5; i<=printedChars; i++) lineBuffer[i-5] = lineBuffer[i]; //include '\0' + printedChars -= 5; } if (lineHeight == 2) { // use this code for 8 line display char smallBuffer1[MAX_MODE_LINE_SPACE]; @@ -635,10 +702,14 @@ class FourLineDisplayUsermod : public Usermod { bool wakeDisplay() { if (type == NONE || !enabled) return false; if (displayTurnedOff) { + unsigned long now = millis(); + while (drawing && millis()-now < 250) delay(1); // wait if someone else is drawing + drawing = true; clear(); // Turn the display back on sleepOrClock(false); //lastRedraw = millis(); + drawing = false; return true; } return false; @@ -650,6 +721,9 @@ class FourLineDisplayUsermod : public Usermod { * Used in Rotary Encoder usermod. */ void overlay(const char* line1, long showHowLong, byte glyphType) { + unsigned long now = millis(); + while (drawing && millis()-now < 250) delay(1); // wait if someone else is drawing + drawing = true; // Turn the display back on if (!wakeDisplay()) clear(); // Print the overlay @@ -663,6 +737,7 @@ class FourLineDisplayUsermod : public Usermod { drawString(0, (glyphType<255?3:0)*lineHeight, buf.c_str()); } overlayUntil = millis() + showHowLong; + drawing = false; } /** @@ -670,6 +745,9 @@ class FourLineDisplayUsermod : public Usermod { * Clears the screen and prints. */ void overlayLogo(long showHowLong) { + unsigned long now = millis(); + while (drawing && millis()-now < 250) delay(1); // wait if someone else is drawing + drawing = true; // Turn the display back on if (!wakeDisplay()) clear(); // Print the overlay @@ -719,6 +797,7 @@ class FourLineDisplayUsermod : public Usermod { } } overlayUntil = millis() + showHowLong; + drawing = false; } /** @@ -727,6 +806,9 @@ class FourLineDisplayUsermod : public Usermod { * Used in Auto Save usermod */ void overlay(const char* line1, const char* line2, long showHowLong) { + unsigned long now = millis(); + while (drawing && millis()-now < 250) delay(1); // wait if someone else is drawing + drawing = true; // Turn the display back on if (!wakeDisplay()) clear(); // Print the overlay @@ -741,9 +823,14 @@ class FourLineDisplayUsermod : public Usermod { drawString(0, 2*lineHeight, buf.c_str()); } overlayUntil = millis() + showHowLong; + drawing = false; } void networkOverlay(const char* line1, long showHowLong) { + unsigned long now = millis(); + while (drawing && millis()-now < 250) delay(1); // wait if someone else is drawing + drawing = true; + String line; // Turn the display back on if (!wakeDisplay()) clear(); @@ -774,6 +861,7 @@ class FourLineDisplayUsermod : public Usermod { center(line, getCols()); drawString(0, lineHeight*3, line.c_str()); overlayUntil = millis() + showHowLong; + drawing = false; } @@ -794,52 +882,6 @@ class FourLineDisplayUsermod : public Usermod { } } - /** - * Display the current date and time in large characters - * on the middle rows. Based 24 or 12 hour depending on - * the useAMPM configuration. - */ - void showTime() { - if (type == NONE || !enabled || !displayTurnedOff) return; - - char lineBuffer[LINE_BUFFER_SIZE]; - static byte lastSecond; - byte secondCurrent = second(localTime); - byte minuteCurrent = minute(localTime); - byte hourCurrent = hour(localTime); - - if (knownMinute != minuteCurrent) { //only redraw clock if it has changed - //updateLocalTime(); - byte AmPmHour = hourCurrent; - boolean isitAM = true; - if (useAMPM) { - if (AmPmHour > 11) { AmPmHour -= 12; isitAM = false; } - if (AmPmHour == 0) { AmPmHour = 12; } - } - if (knownHour != hourCurrent) { - // only update date when hour changes - sprintf_P(lineBuffer, PSTR("%s %2d "), monthShortStr(month(localTime)), day(localTime)); - draw2x2String(2, lineHeight==1 ? 0 : lineHeight, lineBuffer); // adjust for 8 line displays, draw month and day - } - sprintf_P(lineBuffer,PSTR("%2d:%02d"), (useAMPM ? AmPmHour : hourCurrent), minuteCurrent); - draw2x2String(2, lineHeight*2, lineBuffer); //draw hour, min. blink ":" depending on odd/even seconds - if (useAMPM) drawString(12, lineHeight*2, (isitAM ? "AM" : "PM"), true); //draw am/pm if using 12 time - - drawStatusIcons(); //icons power, wifi, timer, etc - - knownMinute = minuteCurrent; - knownHour = hourCurrent; - } else { - if (secondCurrent == lastSecond) return; - } - if (showSeconds) { - lastSecond = secondCurrent; - draw2x2String(6, lineHeight*2, secondCurrent%2 ? " " : ":"); - sprintf_P(lineBuffer, PSTR("%02d"), secondCurrent); - drawString(12, lineHeight*2+1, lineBuffer, true); // even with double sized rows print seconds in 1 line - } - } - /** * handleButton() can be used to override default button behaviour. Returning true * will prevent button working in a default way. @@ -913,7 +955,44 @@ class FourLineDisplayUsermod : public Usermod { } return handled; } - + + #if CONFIG_FREERTOS_UNICORE + #define ARDUINO_RUNNING_CORE 0 + #else + #define ARDUINO_RUNNING_CORE 1 + #endif + void onUpdateBegin(bool init) { + #ifdef ARDUINO_ARCH_ESP32 + if (init && Display_Task) { + vTaskSuspend(Display_Task); // update is about to begin, disable task to prevent crash + } else { + // update has failed or create task requested + if (Display_Task) + vTaskResume(Display_Task); + else + xTaskCreatePinnedToCore( + [](void * par) { // Function to implement the task + // see https://www.freertos.org/vtaskdelayuntil.html + const TickType_t xFrequency = REFRESH_RATE_MS * portTICK_PERIOD_MS / 2; + TickType_t xLastWakeTime = xTaskGetTickCount(); + for(;;) { + delay(1); // DO NOT DELETE THIS LINE! It is needed to give the IDLE(0) task enough time and to keep the watchdog happy. + // taskYIELD(), yield(), vTaskDelay() and esp_task_wdt_feed() didn't seem to work. + vTaskDelayUntil(&xLastWakeTime, xFrequency); // release CPU, by doing nothing for REFRESH_RATE_MS millis + FourLineDisplayUsermod::getInstance()->redraw(false); + } + }, + "4LD", // Name of the task + 3072, // Stack size in words + NULL, // Task input parameter + 1, // Priority of the task (not idle) + &Display_Task, // Task handle + ARDUINO_RUNNING_CORE + ); + } + #endif + } + /* * addToJsonInfo() can be used to add custom entries to the /json/info part of the JSON API. * Creating an "u" object allows you to add custom key/value pairs to the Info section of the WLED web UI. @@ -941,6 +1020,23 @@ class FourLineDisplayUsermod : public Usermod { // if (!initDone) return; // prevent crash on boot applyPreset() //} + void appendConfigData() { + oappend(SET_F("dd=addDropdown('4LineDisplay','type');")); + oappend(SET_F("addOption(dd,'None',0);")); + oappend(SET_F("addOption(dd,'SSD1306',1);")); + oappend(SET_F("addOption(dd,'SH1106',2);")); + oappend(SET_F("addOption(dd,'SSD1306 128x64',3);")); + oappend(SET_F("addOption(dd,'SSD1305',4);")); + oappend(SET_F("addOption(dd,'SSD1305 128x64',5);")); + oappend(SET_F("addOption(dd,'SSD1306 SPI',6);")); + oappend(SET_F("addOption(dd,'SSD1306 SPI 128x64',7);")); + oappend(SET_F("addInfo('4LineDisplay:pin[]',0,'I2C/SPI CLK (-1 use global)');")); + oappend(SET_F("addInfo('4LineDisplay:pin[]',1,'I2C/SPI DTA (-1 use global)');")); + oappend(SET_F("addInfo('4LineDisplay:pin[]',2,'SPI CS');")); + oappend(SET_F("addInfo('4LineDisplay:pin[]',3,'SPI DC');")); + oappend(SET_F("addInfo('4LineDisplay:pin[]',4,'SPI RST');")); + } + /* * addToConfig() can be used to add custom persistent settings to the cfg.json file in the "um" (usermod) object. * It will be called by WLED when settings are actually saved (for example, LED settings are saved) @@ -960,13 +1056,13 @@ class FourLineDisplayUsermod : public Usermod { top[FPSTR(_enabled)] = enabled; JsonArray io_pin = top.createNestedArray("pin"); for (byte i=0; i<5; i++) io_pin.add(ioPin[i]); - top["help4Pins"] = F("Clk,Data,CS,DC,RST"); // help for Settings page top["type"] = type; - top["help4Type"] = F("1=SSD1306,2=SH1106,3=SSD1306_128x64,4=SSD1305,5=SSD1305_128x64,6=SSD1306_SPI,7=SSD1306_SPI_128x64"); // help for Settings page top[FPSTR(_flip)] = (bool) flip; top[FPSTR(_contrast)] = contrast; top[FPSTR(_contrastFix)] = (bool) contrastFix; + #ifndef ARDUINO_ARCH_ESP32 top[FPSTR(_refreshRate)] = refreshRate; + #endif top[FPSTR(_screenTimeOut)] = screenTimeout/1000; top[FPSTR(_sleepMode)] = (bool) sleepMode; top[FPSTR(_clockMode)] = (bool) clockMode; @@ -986,7 +1082,7 @@ class FourLineDisplayUsermod : public Usermod { bool readFromConfig(JsonObject& root) { bool needsRedraw = false; DisplayType newType = type; - int8_t newPin[5]; for (byte i=0; i<5; i++) newPin[i] = ioPin[i]; + int8_t oldPin[5]; for (byte i=0; i<5; i++) oldPin[i] = ioPin[i]; JsonObject top = root[FPSTR(_name)]; if (top.isNull()) { @@ -997,11 +1093,13 @@ class FourLineDisplayUsermod : public Usermod { enabled = top[FPSTR(_enabled)] | enabled; newType = top["type"] | newType; - for (byte i=0; i<5; i++) newPin[i] = top["pin"][i] | ioPin[i]; + for (byte i=0; i<5; i++) ioPin[i] = top["pin"][i] | ioPin[i]; flip = top[FPSTR(_flip)] | flip; contrast = top[FPSTR(_contrast)] | contrast; + #ifndef ARDUINO_ARCH_ESP32 refreshRate = top[FPSTR(_refreshRate)] | refreshRate; refreshRate = min(5000, max(250, (int)refreshRate)); + #endif screenTimeout = (top[FPSTR(_screenTimeOut)] | screenTimeout/1000) * 1000; sleepMode = top[FPSTR(_sleepMode)] | sleepMode; clockMode = top[FPSTR(_clockMode)] | clockMode; @@ -1015,24 +1113,31 @@ class FourLineDisplayUsermod : public Usermod { DEBUG_PRINT(FPSTR(_name)); if (!initDone) { // first run: reading from cfg.json - for (byte i=0; i<5; i++) ioPin[i] = newPin[i]; type = newType; DEBUG_PRINTLN(F(" config loaded.")); } else { DEBUG_PRINTLN(F(" config (re)loaded.")); // changing parameters from settings page bool pinsChanged = false; - for (byte i=0; i<5; i++) if (ioPin[i] != newPin[i]) { pinsChanged = true; break; } + for (byte i=0; i<5; i++) if (ioPin[i] != oldPin[i]) { pinsChanged = true; break; } if (pinsChanged || type!=newType) { if (type != NONE) delete u8x8; PinOwner po = PinOwner::UM_FourLineDisplay; - if (ioPin[0]==HW_PIN_SCL && ioPin[1]==HW_PIN_SDA) po = PinOwner::HW_I2C; // allow multiple allocations of HW I2C bus pins - pinManager.deallocateMultiplePins((const uint8_t *)ioPin, (type == SSD1306_SPI || type == SSD1306_SPI64) ? 5 : 2, po); - for (byte i=0; i<5; i++) ioPin[i] = newPin[i]; - if (ioPin[0]<0 || ioPin[1]<0) { // data & clock must be > -1 - type = NONE; - return true; - } else type = newType; + bool isSPI = (type == SSD1306_SPI || type == SSD1306_SPI64); + if (isSPI) { + pinManager.deallocateMultiplePins((const uint8_t *)(&oldPin[2]), 3, po); + uint8_t hw_sclk = spi_sclk<0 ? HW_PIN_CLOCKSPI : spi_sclk; + uint8_t hw_mosi = spi_mosi<0 ? HW_PIN_DATASPI : spi_mosi; + bool isHW = (oldPin[0]==hw_sclk && oldPin[1]==hw_mosi); + if (isHW) po = PinOwner::HW_SPI; + } else { + uint8_t hw_scl = i2c_scl<0 ? HW_PIN_SCL : i2c_scl; + uint8_t hw_sda = i2c_sda<0 ? HW_PIN_SDA : i2c_sda; + bool isHW = (oldPin[0]==hw_scl && oldPin[1]==hw_sda); + if (isHW) po = PinOwner::HW_I2C; + } + pinManager.deallocateMultiplePins((const uint8_t *)oldPin, 2, po); + type = newType; setup(); needsRedraw |= true; } else { @@ -1070,3 +1175,5 @@ const char FourLineDisplayUsermod::_clockMode[] PROGMEM = "clockMode"; const char FourLineDisplayUsermod::_showSeconds[] PROGMEM = "showSeconds"; const char FourLineDisplayUsermod::_busClkFrequency[] PROGMEM = "i2c-freq-kHz"; const char FourLineDisplayUsermod::_contrastFix[] PROGMEM = "contrastFix"; + +FourLineDisplayUsermod *FourLineDisplayUsermod::instance = nullptr; diff --git a/usermods/usermod_v2_mode_sort/usermod_v2_mode_sort.h b/usermods/usermod_v2_mode_sort/usermod_v2_mode_sort.h index 2be7ce84c0..092206bb6d 100644 --- a/usermods/usermod_v2_mode_sort/usermod_v2_mode_sort.h +++ b/usermods/usermod_v2_mode_sort/usermod_v2_mode_sort.h @@ -162,7 +162,6 @@ class ModeSortUsermod : public Usermod { break; } } - re_sortModes(palettes_qstrings, palettes_alpha_indexes, strip.getPaletteCount(), skipPaletteCount); } @@ -189,6 +188,7 @@ class ModeSortUsermod : public Usermod { bool complete = false; for (size_t i = 0; i < strlen_P(json); i++) { singleJsonSymbol = pgm_read_byte_near(json + i); + if (singleJsonSymbol == '\0') break; switch (singleJsonSymbol) { case '"': insideQuotes = !insideQuotes; @@ -200,18 +200,14 @@ class ModeSortUsermod : public Usermod { case '[': break; case ']': - complete = true; + if (!insideQuotes) complete = true; break; case ',': - modeIndex++; + if (!insideQuotes) modeIndex++; default: - if (!insideQuotes) { - break; - } - } - if (complete) { - break; + if (!insideQuotes) break; } + if (complete) break; } return modeStrings; } diff --git a/usermods/usermod_v2_rotary_encoder_ui/usermod_v2_rotary_encoder_ui.h b/usermods/usermod_v2_rotary_encoder_ui/usermod_v2_rotary_encoder_ui.h index 0ea77e8442..96a09a0992 100644 --- a/usermods/usermod_v2_rotary_encoder_ui/usermod_v2_rotary_encoder_ui.h +++ b/usermods/usermod_v2_rotary_encoder_ui/usermod_v2_rotary_encoder_ui.h @@ -97,6 +97,7 @@ class RotaryEncoderUIUsermod : public Usermod { */ void setup() { + DEBUG_PRINTLN(F("Usermod Rotary Encoder init.")); PinManagerPinType pins[3] = { { pinA, false }, { pinB, false }, { pinC, false } }; if (!pinManager.allocateMultiplePins(pins, 3, PinOwner::UM_RotaryEncoderUI)) { // BUG: configuring this usermod with conflicting pins @@ -439,14 +440,11 @@ class RotaryEncoderUIUsermod : public Usermod { DEBUG_PRINTLN(F(": No config found. (Using defaults.)")); return false; } - int8_t newDTpin = pinA; - int8_t newCLKpin = pinB; - int8_t newSWpin = pinC; + int8_t newDTpin = top[FPSTR(_DT_pin)] | pinA; + int8_t newCLKpin = top[FPSTR(_CLK_pin)] | pinB; + int8_t newSWpin = top[FPSTR(_SW_pin)] | pinC; enabled = top[FPSTR(_enabled)] | enabled; - newDTpin = top[FPSTR(_DT_pin)] | newDTpin; - newCLKpin = top[FPSTR(_CLK_pin)] | newCLKpin; - newSWpin = top[FPSTR(_SW_pin)] | newSWpin; DEBUG_PRINT(FPSTR(_name)); if (!initDone) { diff --git a/usermods/usermod_v2_rotary_encoder_ui_ALT/usermod_v2_rotary_encoder_ui_ALT.h b/usermods/usermod_v2_rotary_encoder_ui_ALT/usermod_v2_rotary_encoder_ui_ALT.h index 4629f547a7..18435ca36a 100644 --- a/usermods/usermod_v2_rotary_encoder_ui_ALT/usermod_v2_rotary_encoder_ui_ALT.h +++ b/usermods/usermod_v2_rotary_encoder_ui_ALT/usermod_v2_rotary_encoder_ui_ALT.h @@ -47,7 +47,7 @@ // The last UI state, remove color and saturation option if diplay not active(too many options) #ifdef USERMOD_FOUR_LINE_DISPLAY - #define LAST_UI_STATE 8 + #define LAST_UI_STATE 11 #else #define LAST_UI_STATE 4 #endif @@ -56,7 +56,7 @@ #define MODE_SORT_SKIP_COUNT 1 // Which list is being sorted -static char **listBeingSorted; +static const char **listBeingSorted; /** * Modes and palettes are stored as strings that @@ -65,8 +65,8 @@ static char **listBeingSorted; * JSON_mode_names or JSON_palette_names. */ static int re_qstringCmp(const void *ap, const void *bp) { - char *a = listBeingSorted[*((byte *)ap)]; - char *b = listBeingSorted[*((byte *)bp)]; + const char *a = listBeingSorted[*((byte *)ap)]; + const char *b = listBeingSorted[*((byte *)bp)]; int i = 0; do { char aVal = pgm_read_byte_near(a + i); @@ -139,13 +139,13 @@ class RotaryEncoderUIUsermod : public Usermod { #endif // Pointers the start of the mode names within JSON_mode_names - char **modes_qstrings = nullptr; + const char **modes_qstrings = nullptr; // Array of mode indexes in alphabetical order. byte *modes_alpha_indexes = nullptr; // Pointers the start of the palette names within JSON_palette_names - char **palettes_qstrings = nullptr; + const char **palettes_qstrings = nullptr; // Array of palette indexes in alphabetical order. byte *palettes_alpha_indexes = nullptr; @@ -161,7 +161,6 @@ class RotaryEncoderUIUsermod : public Usermod { uint8_t knownPalette = 0; uint8_t currentCCT = 128; - bool isRgbw = false; byte presetHigh = 0; byte presetLow = 0; @@ -186,7 +185,8 @@ class RotaryEncoderUIUsermod : public Usermod { * modes_alpha_indexes and palettes_alpha_indexes. */ void sortModesAndPalettes() { - modes_qstrings = re_findModeStrings(JSON_mode_names, strip.getModeCount()); + //modes_qstrings = re_findModeStrings(JSON_mode_names, strip.getModeCount()); + modes_qstrings = strip.getModeDataSrc(); modes_alpha_indexes = re_initIndexArray(strip.getModeCount()); re_sortModes(modes_qstrings, modes_alpha_indexes, strip.getModeCount(), MODE_SORT_SKIP_COUNT); @@ -212,8 +212,8 @@ class RotaryEncoderUIUsermod : public Usermod { * Return an array of mode or palette names from the JSON string. * They don't end in '\0', they end in '"'. */ - char **re_findModeStrings(const char json[], int numModes) { - char **modeStrings = (char **)malloc(sizeof(char *) * numModes); + const char **re_findModeStrings(const char json[], int numModes) { + const char **modeStrings = (const char **)malloc(sizeof(const char *) * numModes); uint8_t modeIndex = 0; bool insideQuotes = false; // advance past the mark for markLineNum that may exist. @@ -250,7 +250,8 @@ class RotaryEncoderUIUsermod : public Usermod { /** * Sort either the modes or the palettes using quicksort. */ - void re_sortModes(char **modeNames, byte *indexes, int count, int numSkip) { + void re_sortModes(const char **modeNames, byte *indexes, int count, int numSkip) { + if (!modeNames) return; listBeingSorted = modeNames; qsort(indexes + numSkip, count - numSkip, sizeof(byte), re_qstringCmp); listBeingSorted = nullptr; @@ -282,17 +283,11 @@ class RotaryEncoderUIUsermod : public Usermod { pinMode(pinC, INPUT_PULLUP); loopTime = millis(); - for (uint8_t s = 0; s < busses.getNumBusses(); s++) { - Bus *bus = busses.getBus(s); - if (!bus || bus->getLength()==0) break; - isRgbw |= bus->isRgbw(); - } - currentCCT = (approximateKelvinFromRGB(RGBW32(col[0], col[1], col[2], col[3])) - 1900) >> 5; if (!initDone) sortModesAndPalettes(); -#ifdef USERMOD_FOUR_LINE_DISPLAY +#ifdef USERMOD_FOUR_LINE_DISPLAY // This Usermod uses FourLineDisplayUsermod for the best experience. // But it's optional. But you want it. display = (FourLineDisplayUsermod*) usermods.lookup(USERMOD_ID_FOUR_LINE_DISP); @@ -372,19 +367,43 @@ class RotaryEncoderUIUsermod : public Usermod { if (buttonWaitTime && currentTime-buttonWaitTime>350 && !buttonPressedBefore) { //same speed as in button.cpp buttonWaitTime = 0; char newState = select_state + 1; - bool changedState = true; - if (newState > LAST_UI_STATE || (newState == 8 && presetHigh==0 && presetLow == 0)) newState = 0; + bool changedState = false; + char lineBuffer[64]; + do { + // finde new state + switch (newState) { + case 0: strcpy_P(lineBuffer, PSTR("Brightness")); changedState = true; break; + case 1: if (!extractModeSlider(effectCurrent, 0, lineBuffer, 63)) newState++; else changedState = true; break; // speed + case 2: if (!extractModeSlider(effectCurrent, 1, lineBuffer, 63)) newState++; else changedState = true; break; // intensity + case 3: strcpy_P(lineBuffer, PSTR("Color Palette")); changedState = true; break; + case 4: strcpy_P(lineBuffer, PSTR("Effect")); changedState = true; break; + case 5: strcpy_P(lineBuffer, PSTR("Main Color")); changedState = true; break; + case 6: strcpy_P(lineBuffer, PSTR("Saturation")); changedState = true; break; + case 7: + if (!(strip.getSegment(applyToAll ? strip.getFirstSelectedSegId() : strip.getMainSegmentId()).getLightCapabilities() & 0x04)) newState++; + else { strcpy_P(lineBuffer, PSTR("CCT")); changedState = true; } + break; + case 8: if (presetHigh==0 || presetLow == 0) newState++; else { strcpy_P(lineBuffer, PSTR("Preset")); changedState = true; } break; + case 9: + case 10: + case 11: if (!extractModeSlider(effectCurrent, newState-7, lineBuffer, 63)) newState++; else changedState = true; break; // custom + } + if (newState > LAST_UI_STATE) newState = 0; + } while (!changedState); if (display != nullptr) { switch (newState) { - case 0: changedState = changeState(PSTR("Brightness"), 1, 0, 1); break; //1 = sun - case 1: changedState = changeState(PSTR("Speed"), 1, 4, 2); break; //2 = skip forward - case 2: changedState = changeState(PSTR("Intensity"), 1, 8, 3); break; //3 = fire - case 3: changedState = changeState(PSTR("Color Palette"), 2, 0, 4); break; //4 = custom palette - case 4: changedState = changeState(PSTR("Effect"), 3, 0, 5); break; //5 = puzzle piece - case 5: changedState = changeState(PSTR("Main Color"), 255, 255, 7); break; //7 = brush - case 6: changedState = changeState(PSTR("Saturation"), 255, 255, 8); break; //8 = contrast - case 7: changedState = changeState(PSTR("CCT"), 255, 255, 10); break; //10 = star - case 8: changedState = changeState(PSTR("Preset"), 255, 255, 11); break; //11 = heart + case 0: changedState = changeState(lineBuffer, 1, 0, 1); break; //1 = sun + case 1: changedState = changeState(lineBuffer, 1, 4, 2); break; //2 = skip forward + case 2: changedState = changeState(lineBuffer, 1, 8, 3); break; //3 = fire + case 3: changedState = changeState(lineBuffer, 2, 0, 4); break; //4 = custom palette + case 4: changedState = changeState(lineBuffer, 3, 0, 5); break; //5 = puzzle piece + case 5: changedState = changeState(lineBuffer, 255, 255, 7); break; //7 = brush + case 6: changedState = changeState(lineBuffer, 255, 255, 8); break; //8 = contrast + case 7: changedState = changeState(lineBuffer, 255, 255, 10); break; //10 = star + case 8: changedState = changeState(lineBuffer, 255, 255, 11); break; //11 = heart + case 9: changedState = changeState(lineBuffer, 255, 255, 10); break; //10 = star + case 10: changedState = changeState(lineBuffer, 255, 255, 10); break; //10 = star + case 11: changedState = changeState(lineBuffer, 255, 255, 10); break; //10 = star } } if (changedState) select_state = newState; @@ -397,29 +416,35 @@ class RotaryEncoderUIUsermod : public Usermod { if (Enc_B == LOW) //changes to LOW so that then encoder registers a change at the very end of a pulse { // B is high so clockwise switch(select_state) { - case 0: changeBrightness(true); break; - case 1: changeEffectSpeed(true); break; - case 2: changeEffectIntensity(true); break; - case 3: changePalette(true); break; - case 4: changeEffect(true); break; - case 5: changeHue(true); break; - case 6: changeSat(true); break; - case 7: changeCCT(true); break; - case 8: changePreset(true); break; + case 0: changeBrightness(true); break; + case 1: changeEffectSpeed(true); break; + case 2: changeEffectIntensity(true); break; + case 3: changePalette(true); break; + case 4: changeEffect(true); break; + case 5: changeHue(true); break; + case 6: changeSat(true); break; + case 7: changeCCT(true); break; + case 8: changePreset(true); break; + case 9: changeCustom(1,true); break; + case 10: changeCustom(2,true); break; + case 11: changeCustom(3,true); break; } } else if (Enc_B == HIGH) { // B is low so counter-clockwise switch(select_state) { - case 0: changeBrightness(false); break; - case 1: changeEffectSpeed(false); break; - case 2: changeEffectIntensity(false); break; - case 3: changePalette(false); break; - case 4: changeEffect(false); break; - case 5: changeHue(false); break; - case 6: changeSat(false); break; - case 7: changeCCT(false); break; - case 8: changePreset(false); break; + case 0: changeBrightness(false); break; + case 1: changeEffectSpeed(false); break; + case 2: changeEffectIntensity(false); break; + case 3: changePalette(false); break; + case 4: changeEffect(false); break; + case 5: changeHue(false); break; + case 6: changeSat(false); break; + case 7: changeCCT(false); break; + case 8: changePreset(false); break; + case 9: changeCustom(1,false); break; + case 10: changeCustom(2,false); break; + case 11: changeCustom(3,false); break; } } } @@ -503,13 +528,13 @@ class RotaryEncoderUIUsermod : public Usermod { effectCurrent = modes_alpha_indexes[effectCurrentIndex]; stateChanged = true; if (applyToAll) { - for (byte i=0; iwakeDisplay()) { + display->redraw(true); + // Throw away wake up input + return; + } + display->updateRedrawTime(); + #endif + stateChanged = true; + if (applyToAll) { + uint8_t id = strip.getFirstSelectedSegId(); + Segment& sid = strip.getSegment(id); + switch (par) { + case 3: val = sid.custom3 = max(min((increase ? sid.custom3+fadeAmount : sid.custom3-fadeAmount), 255), 0); break; + case 2: val = sid.custom2 = max(min((increase ? sid.custom2+fadeAmount : sid.custom2-fadeAmount), 255), 0); break; + default: val = sid.custom1 = max(min((increase ? sid.custom1+fadeAmount : sid.custom1-fadeAmount), 255), 0); break; + } + for (byte i=0; ioverlay(lineBuffer, 500, 10); // use star + #endif + } + + void changePalette(bool increase) { #ifdef USERMOD_FOUR_LINE_DISPLAY if (display && display->wakeDisplay()) { @@ -588,13 +658,13 @@ class RotaryEncoderUIUsermod : public Usermod { effectPalette = palettes_alpha_indexes[effectPaletteIndex]; stateChanged = true; if (applyToAll) { - for (byte i=0; ioverlay(lineBuffer, 500, 7); // use brush + #endif } void changeSat(bool increase){ @@ -641,16 +716,21 @@ class RotaryEncoderUIUsermod : public Usermod { currentSat1 = max(min((increase ? currentSat1+fadeAmount : currentSat1-fadeAmount), 255), 0); colorHStoRGB(currentHue1*256, currentSat1, col); if (applyToAll) { - for (byte i=0; ioverlay(lineBuffer, 500, 8); // use contrast + #endif } void changePreset(bool increase) { @@ -663,6 +743,12 @@ class RotaryEncoderUIUsermod : public Usermod { display->updateRedrawTime(); #endif if (presetHigh && presetLow && presetHigh > presetLow) { + StaticJsonDocument<64> root; + char str[64]; + sprintf_P(str, PSTR("%d~%d~%s"), presetLow, presetHigh, increase?"":"-"); + root[F("ps")] = str; + deserializeState(root.as(), CALL_MODE_BUTTON_PRESET); +/* String apireq = F("win&PL=~"); if (!increase) apireq += '-'; apireq += F("&P1="); @@ -670,7 +756,12 @@ class RotaryEncoderUIUsermod : public Usermod { apireq += F("&P2="); apireq += presetHigh; handleSet(nullptr, apireq, false); +*/ lampUdated(); + #ifdef USERMOD_FOUR_LINE_DISPLAY + sprintf(str, "%d", currentPreset); + display->overlay(str, 500, 11); // use heart + #endif } } @@ -685,16 +776,21 @@ class RotaryEncoderUIUsermod : public Usermod { #endif currentCCT = max(min((increase ? currentCCT+fadeAmount : currentCCT-fadeAmount), 255), 0); // if (applyToAll) { - for (byte i=0; ioverlay(lineBuffer, 500, 10); // use star + #endif } /* diff --git a/usermods/word-clock-matrix/word-clock-matrix.cpp b/usermods/word-clock-matrix/word-clock-matrix.cpp index 628b52625a..67c5b1e472 100644 --- a/usermods/word-clock-matrix/word-clock-matrix.cpp +++ b/usermods/word-clock-matrix/word-clock-matrix.cpp @@ -27,14 +27,14 @@ saveMacro(1, "&FX=0&R=255&G=255&B=255", false); //strip.getSegment(1).setOption(SEG_OPTION_SELECTED, true); //select first two segments (background color + FX settable) - WS2812FX::Segment &seg = strip.getSegment(0); + Segment &seg = strip.getSegment(0); seg.colors[0] = ((0 << 24) | ((0 & 0xFF) << 16) | ((0 & 0xFF) << 8) | ((0 & 0xFF))); strip.getSegment(0).setOption(0, false); strip.getSegment(0).setOption(2, false); //other segments are text for (int i = 1; i < 10; i++) { - WS2812FX::Segment &seg = strip.getSegment(i); + Segment &seg = strip.getSegment(i); seg.colors[0] = ((0 << 24) | ((0 & 0xFF) << 16) | ((190 & 0xFF) << 8) | ((180 & 0xFF))); strip.getSegment(i).setOption(0, true); strip.setBrightness(128); @@ -50,7 +50,7 @@ void selectWordSegments(bool state) { for (int i = 1; i < 10; i++) { - //WS2812FX::Segment &seg = strip.getSegment(i); + //Segment &seg = strip.getSegment(i); strip.getSegment(i).setOption(0, state); // strip.getSegment(1).setOption(SEG_OPTION_SELECTED, true); //seg.mode = 12; diff --git a/wled00/FX.cpp b/wled00/FX.cpp index fa6cb35513..dd693bb15c 100644 --- a/wled00/FX.cpp +++ b/wled00/FX.cpp @@ -26,17 +26,58 @@ #include "FX.h" #include "wled.h" +#include "fcn_declare.h" #define IBN 5100 -#define PALETTE_SOLID_WRAP (paletteBlend == 1 || paletteBlend == 3) +#define PALETTE_SOLID_WRAP (strip.paletteBlend == 1 || strip.paletteBlend == 3) + +// effect utility functions +uint8_t sin_gap(uint16_t in) { + if (in & 0x100) return 0; + return sin8(in + 192); // correct phase shift of sine so that it starts and stops at 0 +} + +uint16_t triwave16(uint16_t in) { + if (in < 0x8000) return in *2; + return 0xFFFF - (in - 0x8000)*2; +} + +/* + * Generates a tristate square wave w/ attac & decay + * @param x input value 0-255 + * @param pulsewidth 0-127 + * @param attdec attac & decay, max. pulsewidth / 2 + * @returns signed waveform value + */ +int8_t tristate_square8(uint8_t x, uint8_t pulsewidth, uint8_t attdec) { + int8_t a = 127; + if (x > 127) { + a = -127; + x -= 127; + } + + if (x < attdec) { //inc to max + return (int16_t) x * a / attdec; + } + else if (x < pulsewidth - attdec) { //max + return a; + } + else if (x < pulsewidth) { //dec to 0 + return (int16_t) (pulsewidth - x) * a / attdec; + } + return 0; +} + +// effect functions /* * No blinking. Just plain old static light. */ -uint16_t WS2812FX::mode_static(void) { - fill(SEGCOLOR(0)); - return (SEGMENT.getOption(SEG_OPTION_TRANSITIONAL)) ? FRAMETIME : 350; //update faster if in transition +uint16_t mode_static(void) { + SEGMENT.fill(SEGCOLOR(0)); + return /*(SEGMENT.getOption(SEG_OPTION_TRANSITIONAL)) ? FRAMETIME :*/ 350; //update faster if in transition } +static const char _data_FX_MODE_STATIC[] PROGMEM = "Solid"; /* @@ -44,13 +85,13 @@ uint16_t WS2812FX::mode_static(void) { * Alternate between color1 and color2 * if(strobe == true) then create a strobe effect */ -uint16_t WS2812FX::blink(uint32_t color1, uint32_t color2, bool strobe, bool do_palette) { +uint16_t blink(uint32_t color1, uint32_t color2, bool strobe, bool do_palette) { uint32_t cycleTime = (255 - SEGMENT.speed)*20; uint32_t onTime = FRAMETIME; if (!strobe) onTime += ((cycleTime * SEGMENT.intensity) >> 8); cycleTime += FRAMETIME*2; - uint32_t it = now / cycleTime; - uint32_t rem = now % cycleTime; + uint32_t it = strip.now / cycleTime; + uint32_t rem = strip.now % cycleTime; bool on = false; if (it != SEGENV.step //new iteration, force on state for one frame, even if set time is too brief @@ -63,45 +104,49 @@ uint16_t WS2812FX::blink(uint32_t color1, uint32_t color2, bool strobe, bool do_ uint32_t color = on ? color1 : color2; if (color == color1 && do_palette) { - for(uint16_t i = 0; i < SEGLEN; i++) { - setPixelColor(i, color_from_palette(i, true, PALETTE_SOLID_WRAP, 0)); + for (int i = 0; i < SEGLEN; i++) { + SEGMENT.setPixelColor(i, SEGMENT.color_from_palette(i, true, PALETTE_SOLID_WRAP, 0)); } - } else fill(color); + } else SEGMENT.fill(color); return FRAMETIME; } /* - * Normal blinking. 50% on/off time. + * Normal blinking. Intensity sets duty cycle. */ -uint16_t WS2812FX::mode_blink(void) { +uint16_t mode_blink(void) { return blink(SEGCOLOR(0), SEGCOLOR(1), false, true); } +static const char _data_FX_MODE_BLINK[] PROGMEM = "Blink@!,Duty cycle;!,!,;!;1d"; /* * Classic Blink effect. Cycling through the rainbow. */ -uint16_t WS2812FX::mode_blink_rainbow(void) { - return blink(color_wheel(SEGENV.call & 0xFF), SEGCOLOR(1), false, false); +uint16_t mode_blink_rainbow(void) { + return blink(SEGMENT.color_wheel(SEGENV.call & 0xFF), SEGCOLOR(1), false, false); } +static const char _data_FX_MODE_BLINK_RAINBOW[] PROGMEM = "Blink Rainbow@Frequency,Blink duration;!,!,;!;1d"; /* * Classic Strobe effect. */ -uint16_t WS2812FX::mode_strobe(void) { +uint16_t mode_strobe(void) { return blink(SEGCOLOR(0), SEGCOLOR(1), true, true); } +static const char _data_FX_MODE_STROBE[] PROGMEM = "Strobe@!,;!,!,;!;1d"; /* * Classic Strobe effect. Cycling through the rainbow. */ -uint16_t WS2812FX::mode_strobe_rainbow(void) { - return blink(color_wheel(SEGENV.call & 0xFF), SEGCOLOR(1), true, false); +uint16_t mode_strobe_rainbow(void) { + return blink(SEGMENT.color_wheel(SEGENV.call & 0xFF), SEGCOLOR(1), true, false); } +static const char _data_FX_MODE_STROBE_RAINBOW[] PROGMEM = "Strobe Rainbow@!,;,!,;!;1d"; /* @@ -109,9 +154,9 @@ uint16_t WS2812FX::mode_strobe_rainbow(void) { * LEDs are turned on (color1) in sequence, then turned off (color2) in sequence. * if (bool rev == true) then LEDs are turned off in reverse order */ -uint16_t WS2812FX::color_wipe(bool rev, bool useRandomColors) { +uint16_t color_wipe(bool rev, bool useRandomColors) { uint32_t cycleTime = 750 + (255 - SEGMENT.speed)*150; - uint32_t perc = now % cycleTime; + uint32_t perc = strip.now % cycleTime; uint16_t prog = (perc * 65535) / cycleTime; bool back = (prog > 32767); if (back) { @@ -127,11 +172,11 @@ uint16_t WS2812FX::color_wipe(bool rev, bool useRandomColors) { SEGENV.step = 3; } if (SEGENV.step == 1) { //if flag set, change to new random color - SEGENV.aux1 = get_random_wheel_index(SEGENV.aux0); + SEGENV.aux1 = SEGMENT.get_random_wheel_index(SEGENV.aux0); SEGENV.step = 2; } if (SEGENV.step == 3) { - SEGENV.aux0 = get_random_wheel_index(SEGENV.aux1); + SEGENV.aux0 = SEGMENT.get_random_wheel_index(SEGENV.aux1); SEGENV.step = 0; } } @@ -142,19 +187,19 @@ uint16_t WS2812FX::color_wipe(bool rev, bool useRandomColors) { rem /= (SEGMENT.intensity +1); if (rem > 255) rem = 255; - uint32_t col1 = useRandomColors? color_wheel(SEGENV.aux1) : SEGCOLOR(1); - for (uint16_t i = 0; i < SEGLEN; i++) + uint32_t col1 = useRandomColors? SEGMENT.color_wheel(SEGENV.aux1) : SEGCOLOR(1); + for (int i = 0; i < SEGLEN; i++) { uint16_t index = (rev && back)? SEGLEN -1 -i : i; - uint32_t col0 = useRandomColors? color_wheel(SEGENV.aux0) : color_from_palette(index, true, PALETTE_SOLID_WRAP, 0); + uint32_t col0 = useRandomColors? SEGMENT.color_wheel(SEGENV.aux0) : SEGMENT.color_from_palette(index, true, PALETTE_SOLID_WRAP, 0); if (i < ledIndex) { - setPixelColor(index, back? col1 : col0); + SEGMENT.setPixelColor(index, back? col1 : col0); } else { - setPixelColor(index, back? col0 : col1); - if (i == ledIndex) setPixelColor(index, color_blend(back? col0 : col1, back? col1 : col0, rem)); + SEGMENT.setPixelColor(index, back? col0 : col1); + if (i == ledIndex) SEGMENT.setPixelColor(index, color_blend(back? col0 : col1, back? col1 : col0, rem)); } } return FRAMETIME; @@ -164,43 +209,48 @@ uint16_t WS2812FX::color_wipe(bool rev, bool useRandomColors) { /* * Lights all LEDs one after another. */ -uint16_t WS2812FX::mode_color_wipe(void) { +uint16_t mode_color_wipe(void) { return color_wipe(false, false); } +static const char _data_FX_MODE_COLOR_WIPE[] PROGMEM = "Wipe@!,!;!,!,;!;1d"; + /* * Lights all LEDs one after another. Turns off opposite */ -uint16_t WS2812FX::mode_color_sweep(void) { +uint16_t mode_color_sweep(void) { return color_wipe(true, false); } +static const char _data_FX_MODE_COLOR_SWEEP[] PROGMEM = "Sweep@!,!;!,!,;!;1d"; /* * Turns all LEDs after each other to a random color. * Then starts over with another color. */ -uint16_t WS2812FX::mode_color_wipe_random(void) { +uint16_t mode_color_wipe_random(void) { return color_wipe(false, true); } +static const char _data_FX_MODE_COLOR_WIPE_RANDOM[] PROGMEM = "Wipe Random@!,;1,2,3;!;1d"; /* * Random color introduced alternating from start and end of strip. */ -uint16_t WS2812FX::mode_color_sweep_random(void) { +uint16_t mode_color_sweep_random(void) { return color_wipe(true, true); } +static const char _data_FX_MODE_COLOR_SWEEP_RANDOM[] PROGMEM = "Sweep Random"; /* * Lights all LEDs in one random color up. Then switches them * to the next random color. */ -uint16_t WS2812FX::mode_random_color(void) { +uint16_t mode_random_color(void) { uint32_t cycleTime = 200 + (255 - SEGMENT.speed)*50; - uint32_t it = now / cycleTime; - uint32_t rem = now % cycleTime; + uint32_t it = strip.now / cycleTime; + uint32_t rem = strip.now % cycleTime; uint16_t fadedur = (cycleTime * SEGMENT.intensity) >> 8; uint32_t fade = 255; @@ -216,68 +266,74 @@ uint16_t WS2812FX::mode_random_color(void) { if (it != SEGENV.step) //new color { SEGENV.aux1 = SEGENV.aux0; - SEGENV.aux0 = get_random_wheel_index(SEGENV.aux0); //aux0 will store our random color wheel index + SEGENV.aux0 = SEGMENT.get_random_wheel_index(SEGENV.aux0); //aux0 will store our random color wheel index SEGENV.step = it; } - fill(color_blend(color_wheel(SEGENV.aux1), color_wheel(SEGENV.aux0), fade)); + SEGMENT.fill(color_blend(SEGMENT.color_wheel(SEGENV.aux1), SEGMENT.color_wheel(SEGENV.aux0), fade)); return FRAMETIME; } +static const char _data_FX_MODE_RANDOM_COLOR[] PROGMEM = "Random Colors@!,Fade time;1,2,3;!;1d"; /* * Lights every LED in a random color. Changes all LED at the same time * to new random colors. */ -uint16_t WS2812FX::dynamic(boolean smooth=false) { +uint16_t dynamic(boolean smooth=false) { if (!SEGENV.allocateData(SEGLEN)) return mode_static(); //allocation failed if(SEGENV.call == 0) { - for (uint16_t i = 0; i < SEGLEN; i++) SEGENV.data[i] = random8(); + for (int i = 0; i < SEGLEN; i++) SEGENV.data[i] = random8(); } uint32_t cycleTime = 50 + (255 - SEGMENT.speed)*15; - uint32_t it = now / cycleTime; + uint32_t it = strip.now / cycleTime; if (it != SEGENV.step && SEGMENT.speed != 0) //new color { - for (uint16_t i = 0; i < SEGLEN; i++) { + for (int i = 0; i < SEGLEN; i++) { if (random8() <= SEGMENT.intensity) SEGENV.data[i] = random8(); } SEGENV.step = it; } if (smooth) { - for (uint16_t i = 0; i < SEGLEN; i++) { - blendPixelColor(i, color_wheel(SEGENV.data[i]),16); + for (int i = 0; i < SEGLEN; i++) { + SEGMENT.blendPixelColor(i, SEGMENT.color_wheel(SEGENV.data[i]),16); // TODO } } else { - for (uint16_t i = 0; i < SEGLEN; i++) { - setPixelColor(i, color_wheel(SEGENV.data[i])); + for (int i = 0; i < SEGLEN; i++) { + SEGMENT.setPixelColor(i, SEGMENT.color_wheel(SEGENV.data[i])); } } return FRAMETIME; } + /* * Original effect "Dynamic" */ -uint16_t WS2812FX::mode_dynamic(void) { +uint16_t mode_dynamic(void) { return dynamic(false); } +static const char _data_FX_MODE_DYNAMIC[] PROGMEM = "Dynamic@!,!;1,2,3;!;1d"; + /* * effect "Dynamic" with smoth color-fading */ -uint16_t WS2812FX::mode_dynamic_smooth(void) { +uint16_t mode_dynamic_smooth(void) { return dynamic(true); } +static const char _data_FX_MODE_DYNAMIC_SMOOTH[] PROGMEM = "Dynamic Smooth"; + /* * Does the "standby-breathing" of well known i-Devices. */ -uint16_t WS2812FX::mode_breath(void) { +uint16_t mode_breath(void) { uint16_t var = 0; - uint16_t counter = (now * ((SEGMENT.speed >> 3) +10)); + uint16_t counter = (strip.now * ((SEGMENT.speed >> 3) +10)); counter = (counter >> 2) + (counter >> 4); //0-16384 + 0-2048 if (counter < 16384) { if (counter > 8192) counter = 8192 - (counter - 8192); @@ -285,54 +341,56 @@ uint16_t WS2812FX::mode_breath(void) { } uint8_t lum = 30 + var; - for(uint16_t i = 0; i < SEGLEN; i++) { - setPixelColor(i, color_blend(SEGCOLOR(1), color_from_palette(i, true, PALETTE_SOLID_WRAP, 0), lum)); + for (int i = 0; i < SEGLEN; i++) { + SEGMENT.setPixelColor(i, color_blend(SEGCOLOR(1), SEGMENT.color_from_palette(i, true, PALETTE_SOLID_WRAP, 0), lum)); } return FRAMETIME; } +static const char _data_FX_MODE_BREATH[] PROGMEM = "Breathe@!,;!,!;!;1d"; /* * Fades the LEDs between two colors */ -uint16_t WS2812FX::mode_fade(void) { - uint16_t counter = (now * ((SEGMENT.speed >> 3) +10)); +uint16_t mode_fade(void) { + uint16_t counter = (strip.now * ((SEGMENT.speed >> 3) +10)); uint8_t lum = triwave16(counter) >> 8; - for(uint16_t i = 0; i < SEGLEN; i++) { - setPixelColor(i, color_blend(SEGCOLOR(1), color_from_palette(i, true, PALETTE_SOLID_WRAP, 0), lum)); + for (int i = 0; i < SEGLEN; i++) { + SEGMENT.setPixelColor(i, color_blend(SEGCOLOR(1), SEGMENT.color_from_palette(i, true, PALETTE_SOLID_WRAP, 0), lum)); } return FRAMETIME; } +static const char _data_FX_MODE_FADE[] PROGMEM = "Fade@!,;!,!,;!;1d"; /* * Scan mode parent function */ -uint16_t WS2812FX::scan(bool dual) +uint16_t scan(bool dual) { uint32_t cycleTime = 750 + (255 - SEGMENT.speed)*150; - uint32_t perc = now % cycleTime; + uint32_t perc = strip.now % cycleTime; uint16_t prog = (perc * 65535) / cycleTime; uint16_t size = 1 + ((SEGMENT.intensity * SEGLEN) >> 9); uint16_t ledIndex = (prog * ((SEGLEN *2) - size *2)) >> 16; - fill(SEGCOLOR(1)); + SEGMENT.fill(SEGCOLOR(1)); int led_offset = ledIndex - (SEGLEN - size); led_offset = abs(led_offset); if (dual) { - for (uint16_t j = led_offset; j < led_offset + size; j++) { + for (int j = led_offset; j < led_offset + size; j++) { uint16_t i2 = SEGLEN -1 -j; - setPixelColor(i2, color_from_palette(i2, true, PALETTE_SOLID_WRAP, (SEGCOLOR(2))? 2:0)); + SEGMENT.setPixelColor(i2, SEGMENT.color_from_palette(i2, true, PALETTE_SOLID_WRAP, (SEGCOLOR(2))? 2:0)); } } - for (uint16_t j = led_offset; j < led_offset + size; j++) { - setPixelColor(j, color_from_palette(j, true, PALETTE_SOLID_WRAP, 0)); + for (int j = led_offset; j < led_offset + size; j++) { + SEGMENT.setPixelColor(j, SEGMENT.color_from_palette(j, true, PALETTE_SOLID_WRAP, 0)); } return FRAMETIME; @@ -342,49 +400,82 @@ uint16_t WS2812FX::scan(bool dual) /* * Runs a single pixel back and forth. */ -uint16_t WS2812FX::mode_scan(void) { +uint16_t mode_scan(void) { return scan(false); } +static const char _data_FX_MODE_SCAN[] PROGMEM = "Scan@!,# of dots;!,!,!;!;1d"; /* * Runs two pixel back and forth in opposite directions. */ -uint16_t WS2812FX::mode_dual_scan(void) { +uint16_t mode_dual_scan(void) { return scan(true); } +static const char _data_FX_MODE_DUAL_SCAN[] PROGMEM = "Scan Dual@!,# of dots;!,!,!;!;1d"; /* * Cycles all LEDs at once through a rainbow. */ -uint16_t WS2812FX::mode_rainbow(void) { - uint16_t counter = (now * ((SEGMENT.speed >> 2) +2)) & 0xFFFF; +uint16_t mode_rainbow(void) { + uint16_t counter = (strip.now * ((SEGMENT.speed >> 2) +2)) & 0xFFFF; counter = counter >> 8; if (SEGMENT.intensity < 128){ - fill(color_blend(color_wheel(counter),WHITE,128-SEGMENT.intensity)); + SEGMENT.fill(color_blend(SEGMENT.color_wheel(counter),WHITE,128-SEGMENT.intensity)); } else { - fill(color_wheel(counter)); + SEGMENT.fill(SEGMENT.color_wheel(counter)); } return FRAMETIME; } +static const char _data_FX_MODE_RAINBOW[] PROGMEM = "Colorloop@!,Saturation;1,2,3;!;1d"; /* * Cycles a rainbow over the entire string of LEDs. */ -uint16_t WS2812FX::mode_rainbow_cycle(void) { - uint16_t counter = (now * ((SEGMENT.speed >> 2) +2)) & 0xFFFF; +uint16_t mode_rainbow_cycle(void) { + uint16_t counter = (strip.now * ((SEGMENT.speed >> 2) +2)) & 0xFFFF; counter = counter >> 8; - for(uint16_t i = 0; i < SEGLEN; i++) { + for (int i = 0; i < SEGLEN; i++) { //intensity/29 = 0 (1/16) 1 (1/8) 2 (1/4) 3 (1/2) 4 (1) 5 (2) 6 (4) 7 (8) 8 (16) uint8_t index = (i * (16 << (SEGMENT.intensity /29)) / SEGLEN) + counter; - setPixelColor(i, color_wheel(index)); + SEGMENT.setPixelColor(i, SEGMENT.color_wheel(index)); + } + + return FRAMETIME; +} +static const char _data_FX_MODE_RAINBOW_CYCLE[] PROGMEM = "Rainbow@!,Size;1,2,3;!;1d"; + + +/* + * Alternating pixels running function. + */ +uint16_t running(uint32_t color1, uint32_t color2, bool theatre = false) { + uint8_t width = (theatre ? 3 : 1) + (SEGMENT.intensity >> 4); // window + uint32_t cycleTime = 50 + (255 - SEGMENT.speed); + uint32_t it = strip.now / cycleTime; + bool usePalette = color1 == SEGCOLOR(0); + + for (int i = 0; i < SEGLEN; i++) { + uint32_t col = color2; + if (usePalette) color1 = SEGMENT.color_from_palette(i, true, PALETTE_SOLID_WRAP, 0); + if (theatre) { + if ((i % width) == SEGENV.aux0) col = color1; + } else { + int8_t pos = (i % (width<<1)); + if ((pos < SEGENV.aux0-width) || ((pos >= SEGENV.aux0) && (pos < SEGENV.aux0+width))) col = color1; + } + SEGMENT.setPixelColor(i,col); } + if (it != SEGENV.step) { + SEGENV.aux0 = (SEGENV.aux0 +1) % (theatre ? width : (width<<1)); + SEGENV.step = it; + } return FRAMETIME; } @@ -393,28 +484,30 @@ uint16_t WS2812FX::mode_rainbow_cycle(void) { * Theatre-style crawling lights. * Inspired by the Adafruit examples. */ -uint16_t WS2812FX::mode_theater_chase(void) { +uint16_t mode_theater_chase(void) { return running(SEGCOLOR(0), SEGCOLOR(1), true); } +static const char _data_FX_MODE_THEATER_CHASE[] PROGMEM = "Theater@!,Gap size;!,!,;!;1d"; /* * Theatre-style crawling lights with rainbow effect. * Inspired by the Adafruit examples. */ -uint16_t WS2812FX::mode_theater_chase_rainbow(void) { - return running(color_wheel(SEGENV.step), SEGCOLOR(1), true); +uint16_t mode_theater_chase_rainbow(void) { + return running(SEGMENT.color_wheel(SEGENV.step), SEGCOLOR(1), true); } +static const char _data_FX_MODE_THEATER_CHASE_RAINBOW[] PROGMEM = "Theater Rainbow@!,Gap size;1,2,3;!;1d"; /* * Running lights effect with smooth sine transition base. */ -uint16_t WS2812FX::running_base(bool saw, bool dual=false) { +uint16_t running_base(bool saw, bool dual=false) { uint8_t x_scale = SEGMENT.intensity >> 2; - uint32_t counter = (now * SEGMENT.speed) >> 9; + uint32_t counter = (strip.now * SEGMENT.speed) >> 9; - for(uint16_t i = 0; i < SEGLEN; i++) { + for (int i = 0; i < SEGLEN; i++) { uint16_t a = i*x_scale - counter; if (saw) { a &= 0xFF; @@ -427,14 +520,14 @@ uint16_t WS2812FX::running_base(bool saw, bool dual=false) { a = 255 - a; } uint8_t s = dual ? sin_gap(a) : sin8(a); - uint32_t ca = color_blend(SEGCOLOR(1), color_from_palette(i, true, PALETTE_SOLID_WRAP, 0), s); + uint32_t ca = color_blend(SEGCOLOR(1), SEGMENT.color_from_palette(i, true, PALETTE_SOLID_WRAP, 0), s); if (dual) { uint16_t b = (SEGLEN-1-i)*x_scale - counter; uint8_t t = sin_gap(b); - uint32_t cb = color_blend(SEGCOLOR(1), color_from_palette(i, true, PALETTE_SOLID_WRAP, 2), t); + uint32_t cb = color_blend(SEGCOLOR(1), SEGMENT.color_from_palette(i, true, PALETTE_SOLID_WRAP, 2), t); ca = color_blend(ca, cb, 127); } - setPixelColor(i, ca); + SEGMENT.setPixelColor(i, ca); } return FRAMETIME; } @@ -444,39 +537,45 @@ uint16_t WS2812FX::running_base(bool saw, bool dual=false) { * Running lights in opposite directions. * Idea: Make the gap width controllable with a third slider in the future */ -uint16_t WS2812FX::mode_running_dual(void) { +uint16_t mode_running_dual(void) { return running_base(false, true); } +static const char _data_FX_MODE_RUNNING_DUAL[] PROGMEM = "Running Dual"; /* * Running lights effect with smooth sine transition. */ -uint16_t WS2812FX::mode_running_lights(void) { +uint16_t mode_running_lights(void) { return running_base(false); } +static const char _data_FX_MODE_RUNNING_LIGHTS[] PROGMEM = "Running@!,Wave width;!,!,;!;1d"; /* * Running lights effect with sawtooth transition. */ -uint16_t WS2812FX::mode_saw(void) { +uint16_t mode_saw(void) { return running_base(true); } +static const char _data_FX_MODE_SAW[] PROGMEM = "Saw@!,Width;!,!,;!;1d"; /* * Blink several LEDs in random colors on, reset, repeat. * Inspired by www.tweaking4all.com/hardware/arduino/adruino-led-strip-effects/ */ -uint16_t WS2812FX::mode_twinkle(void) { - fill(SEGCOLOR(1)); +uint16_t mode_twinkle(void) { + const uint16_t cols = strip.isMatrix ? SEGMENT.virtualWidth() : SEGMENT.virtualLength(); + const uint16_t rows = SEGMENT.virtualHeight(); + + SEGMENT.fill(SEGCOLOR(1)); uint32_t cycleTime = 20 + (255 - SEGMENT.speed)*5; - uint32_t it = now / cycleTime; + uint32_t it = strip.now / cycleTime; if (it != SEGENV.step) { - uint16_t maxOn = map(SEGMENT.intensity, 0, 255, 1, SEGLEN); // make sure at least one LED is on + uint16_t maxOn = map(SEGMENT.intensity, 0, 255, 1, cols*rows-1); // make sure at least one LED is on if (SEGENV.aux0 >= maxOn) { SEGENV.aux0 = 0; @@ -488,40 +587,44 @@ uint16_t WS2812FX::mode_twinkle(void) { uint16_t PRNG16 = SEGENV.aux1; - for (uint16_t i = 0; i < SEGENV.aux0; i++) + for (int i = 0; i < SEGENV.aux0; i++) { PRNG16 = (uint16_t)(PRNG16 * 2053) + 13849; // next 'random' number - uint32_t p = (uint32_t)SEGLEN * (uint32_t)PRNG16; - uint16_t j = p >> 16; - setPixelColor(j, color_from_palette(j, true, PALETTE_SOLID_WRAP, 0)); + uint32_t p = ((uint32_t)cols*rows * (uint32_t)PRNG16) >> 16; + uint16_t j = p % cols; + uint16_t k = p / cols; + uint32_t col = SEGMENT.color_from_palette(map(p, 0, cols*rows, 0, 255), false, PALETTE_SOLID_WRAP, 0); + if (strip.isMatrix) SEGMENT.setPixelColorXY(j, k, col); + else SEGMENT.setPixelColor(j, col); } return FRAMETIME; } +static const char _data_FX_MODE_TWINKLE[] PROGMEM = "Twinkle@!,;!,!,;!;1d,2d"; //pixels /* * Dissolve function */ -uint16_t WS2812FX::dissolve(uint32_t color) { - bool wa = (SEGCOLOR(1) != 0 && _brightness < 255); //workaround, can't compare getPixel to color if not full brightness +uint16_t dissolve(uint32_t color) { + bool wa = (SEGCOLOR(1) != 0 && strip.getBrightness() < 255); //workaround, can't compare getPixel to color if not full brightness - for (uint16_t j = 0; j <= SEGLEN / 15; j++) + for (int j = 0; j <= SEGLEN / 15; j++) { if (random8() <= SEGMENT.intensity) { - for (uint8_t times = 0; times < 10; times++) //attempt to spawn a new pixel 5 times + for (size_t times = 0; times < 10; times++) //attempt to spawn a new pixel 5 times { uint16_t i = random16(SEGLEN); if (SEGENV.aux0) { //dissolve to primary/palette - if (getPixelColor(i) == SEGCOLOR(1) || wa) { + if (SEGMENT.getPixelColor(i) == SEGCOLOR(1) || wa) { // TODO if (color == SEGCOLOR(0)) { - setPixelColor(i, color_from_palette(i, true, PALETTE_SOLID_WRAP, 0)); - } else { setPixelColor(i, color); } + SEGMENT.setPixelColor(i, SEGMENT.color_from_palette(i, true, PALETTE_SOLID_WRAP, 0)); + } else { SEGMENT.setPixelColor(i, color); } break; //only spawn 1 new pixel per frame per 50 LEDs } } else { //dissolve to secondary - if (getPixelColor(i) != SEGCOLOR(1)) { setPixelColor(i, SEGCOLOR(1)); break; } + if (SEGMENT.getPixelColor(i) != SEGCOLOR(1)) { SEGMENT.setPixelColor(i, SEGCOLOR(1)); break; } // TODO } } } @@ -540,117 +643,128 @@ uint16_t WS2812FX::dissolve(uint32_t color) { /* * Blink several LEDs on and then off */ -uint16_t WS2812FX::mode_dissolve(void) { +uint16_t mode_dissolve(void) { return dissolve(SEGCOLOR(0)); } +static const char _data_FX_MODE_DISSOLVE[] PROGMEM = "Dissolve@Repeat speed,Dissolve speed;!,!,;!;1d"; /* * Blink several LEDs on and then off in random colors */ -uint16_t WS2812FX::mode_dissolve_random(void) { - return dissolve(color_wheel(random8())); +uint16_t mode_dissolve_random(void) { + return dissolve(SEGMENT.color_wheel(random8())); } +static const char _data_FX_MODE_DISSOLVE_RANDOM[] PROGMEM = "Dissolve Rnd@Repeat speed,Dissolve speed;,!,;!;1d"; /* * Blinks one LED at a time. * Inspired by www.tweaking4all.com/hardware/arduino/adruino-led-strip-effects/ */ -uint16_t WS2812FX::mode_sparkle(void) { - for(uint16_t i = 0; i < SEGLEN; i++) { - setPixelColor(i, color_from_palette(i, true, PALETTE_SOLID_WRAP, 1)); +uint16_t mode_sparkle(void) { + for (int i = 0; i < SEGLEN; i++) { + SEGMENT.setPixelColor(i, SEGMENT.color_from_palette(i, true, PALETTE_SOLID_WRAP, 1)); } uint32_t cycleTime = 10 + (255 - SEGMENT.speed)*2; - uint32_t it = now / cycleTime; + uint32_t it = strip.now / cycleTime; if (it != SEGENV.step) { SEGENV.aux0 = random16(SEGLEN); // aux0 stores the random led index + SEGENV.aux1 = random16(0,SEGMENT.virtualHeight()-1); SEGENV.step = it; } - setPixelColor(SEGENV.aux0, SEGCOLOR(0)); + if (strip.isMatrix) SEGMENT.setPixelColorXY(SEGENV.aux0, SEGENV.aux1, SEGCOLOR(0)); + else SEGMENT.setPixelColor(SEGENV.aux0, SEGCOLOR(0)); return FRAMETIME; } +static const char _data_FX_MODE_SPARKLE[] PROGMEM = "Sparkle@!,;!,!,;!;1d,2d"; /* * Lights all LEDs in the color. Flashes single col 1 pixels randomly. (List name: Sparkle Dark) * Inspired by www.tweaking4all.com/hardware/arduino/adruino-led-strip-effects/ */ -uint16_t WS2812FX::mode_flash_sparkle(void) { - for(uint16_t i = 0; i < SEGLEN; i++) { - setPixelColor(i, color_from_palette(i, true, PALETTE_SOLID_WRAP, 0)); +uint16_t mode_flash_sparkle(void) { + for (int i = 0; i < SEGLEN; i++) { + SEGMENT.setPixelColor(i, SEGMENT.color_from_palette(i, true, PALETTE_SOLID_WRAP, 0)); } - if (now - SEGENV.aux0 > SEGENV.step) { + if (strip.now - SEGENV.aux0 > SEGENV.step) { if(random8((255-SEGMENT.intensity) >> 4) == 0) { - setPixelColor(random16(SEGLEN), SEGCOLOR(1)); //flash + if (strip.isMatrix) SEGMENT.setPixelColorXY(random16(SEGLEN), random16(0,SEGMENT.virtualHeight()-1), SEGCOLOR(1)); + else SEGMENT.setPixelColor(random16(SEGLEN), SEGCOLOR(1)); //flash } - SEGENV.step = now; + SEGENV.step = strip.now; SEGENV.aux0 = 255-SEGMENT.speed; } return FRAMETIME; } +static const char _data_FX_MODE_FLASH_SPARKLE[] PROGMEM = "Sparkle Dark@!,!;Bg,Fx,;!;1d,2d"; /* * Like flash sparkle. With more flash. * Inspired by www.tweaking4all.com/hardware/arduino/adruino-led-strip-effects/ */ -uint16_t WS2812FX::mode_hyper_sparkle(void) { - for(uint16_t i = 0; i < SEGLEN; i++) { - setPixelColor(i, color_from_palette(i, true, PALETTE_SOLID_WRAP, 0)); +uint16_t mode_hyper_sparkle(void) { + for (int i = 0; i < SEGLEN; i++) { + SEGMENT.setPixelColor(i, SEGMENT.color_from_palette(i, true, PALETTE_SOLID_WRAP, 0)); } - if (now - SEGENV.aux0 > SEGENV.step) { + if (strip.now - SEGENV.aux0 > SEGENV.step) { if(random8((255-SEGMENT.intensity) >> 4) == 0) { - for(uint16_t i = 0; i < MAX(1, SEGLEN/3); i++) { - setPixelColor(random16(SEGLEN), SEGCOLOR(1)); + for (int i = 0; i < MAX(1, SEGLEN/3); i++) { + if (strip.isMatrix) SEGMENT.setPixelColorXY(random16(SEGLEN), random16(0,SEGMENT.virtualHeight()), SEGCOLOR(1)); + else SEGMENT.setPixelColor(random16(SEGLEN), SEGCOLOR(1)); } } - SEGENV.step = now; + SEGENV.step = strip.now; SEGENV.aux0 = 255-SEGMENT.speed; } return FRAMETIME; } +static const char _data_FX_MODE_HYPER_SPARKLE[] PROGMEM = "Sparkle+@!,!;Bg,Fx,;!;1d,2d"; /* * Strobe effect with different strobe count and pause, controlled by speed. */ -uint16_t WS2812FX::mode_multi_strobe(void) { - for(uint16_t i = 0; i < SEGLEN; i++) { - setPixelColor(i, color_from_palette(i, true, PALETTE_SOLID_WRAP, 1)); +uint16_t mode_multi_strobe(void) { + for (int i = 0; i < SEGLEN; i++) { + SEGMENT.setPixelColor(i, SEGMENT.color_from_palette(i, true, PALETTE_SOLID_WRAP, 1)); } SEGENV.aux0 = 50 + 20*(uint16_t)(255-SEGMENT.speed); uint16_t count = 2 * ((SEGMENT.intensity / 10) + 1); if(SEGENV.aux1 < count) { if((SEGENV.aux1 & 1) == 0) { - fill(SEGCOLOR(0)); + SEGMENT.fill(SEGCOLOR(0)); SEGENV.aux0 = 15; } else { SEGENV.aux0 = 50; } } - if (now - SEGENV.aux0 > SEGENV.step) { + if (strip.now - SEGENV.aux0 > SEGENV.step) { SEGENV.aux1++; if (SEGENV.aux1 > count) SEGENV.aux1 = 0; - SEGENV.step = now; + SEGENV.step = strip.now; } return FRAMETIME; } +static const char _data_FX_MODE_MULTI_STROBE[] PROGMEM = "Strobe Mega@!,!;!,!,;!;1d"; + /* * Android loading circle */ -uint16_t WS2812FX::mode_android(void) { +uint16_t mode_android(void) { - for(uint16_t i = 0; i < SEGLEN; i++) { - setPixelColor(i, color_from_palette(i, true, PALETTE_SOLID_WRAP, 1)); + for (int i = 0; i < SEGLEN; i++) { + SEGMENT.setPixelColor(i, SEGMENT.color_from_palette(i, true, PALETTE_SOLID_WRAP, 1)); } if (SEGENV.aux1 > ((float)SEGMENT.intensity/255.0)*(float)SEGLEN) @@ -677,30 +791,32 @@ uint16_t WS2812FX::mode_android(void) { if (a + SEGENV.aux1 < SEGLEN) { - for(int i = a; i < a+SEGENV.aux1; i++) { - setPixelColor(i, SEGCOLOR(0)); + for (int i = a; i < a+SEGENV.aux1; i++) { + SEGMENT.setPixelColor(i, SEGCOLOR(0)); } } else { - for(int i = a; i < SEGLEN; i++) { - setPixelColor(i, SEGCOLOR(0)); + for (int i = a; i < SEGLEN; i++) { + SEGMENT.setPixelColor(i, SEGCOLOR(0)); } - for(int i = 0; i < SEGENV.aux1 - (SEGLEN -a); i++) { - setPixelColor(i, SEGCOLOR(0)); + for (int i = 0; i < SEGENV.aux1 - (SEGLEN -a); i++) { + SEGMENT.setPixelColor(i, SEGCOLOR(0)); } } SEGENV.step = a; return 3 + ((8 * (uint32_t)(255 - SEGMENT.speed)) / SEGLEN); } +static const char _data_FX_MODE_ANDROID[] PROGMEM = "Android@!,Width;!,!,;!;mp12=1,1d"; //vertical + /* * color chase function. * color1 = background color * color2 and color3 = colors of two adjacent leds */ -uint16_t WS2812FX::chase(uint32_t color1, uint32_t color2, uint32_t color3, bool do_palette) { - uint16_t counter = now * ((SEGMENT.speed >> 2) + 1); +uint16_t chase(uint32_t color1, uint32_t color2, uint32_t color3, bool do_palette) { + uint16_t counter = strip.now * ((SEGMENT.speed >> 2) + 1); uint16_t a = counter * SEGLEN >> 16; bool chase_random = (SEGMENT.mode == FX_MODE_CHASE_RANDOM); @@ -708,9 +824,9 @@ uint16_t WS2812FX::chase(uint32_t color1, uint32_t color2, uint32_t color3, bool if (a < SEGENV.step) //we hit the start again, choose new color for Chase random { SEGENV.aux1 = SEGENV.aux0; //store previous random color - SEGENV.aux0 = get_random_wheel_index(SEGENV.aux0); + SEGENV.aux0 = SEGMENT.get_random_wheel_index(SEGENV.aux0); } - color1 = color_wheel(SEGENV.aux0); + color1 = SEGMENT.color_wheel(SEGENV.aux0); } SEGENV.step = a; @@ -725,41 +841,41 @@ uint16_t WS2812FX::chase(uint32_t color1, uint32_t color2, uint32_t color3, bool //background if (do_palette) { - for(uint16_t i = 0; i < SEGLEN; i++) { - setPixelColor(i, color_from_palette(i, true, PALETTE_SOLID_WRAP, 1)); + for (int i = 0; i < SEGLEN; i++) { + SEGMENT.setPixelColor(i, SEGMENT.color_from_palette(i, true, PALETTE_SOLID_WRAP, 1)); } - } else fill(color1); + } else SEGMENT.fill(color1); //if random, fill old background between a and end if (chase_random) { - color1 = color_wheel(SEGENV.aux1); - for (uint16_t i = a; i < SEGLEN; i++) - setPixelColor(i, color1); + color1 = SEGMENT.color_wheel(SEGENV.aux1); + for (int i = a; i < SEGLEN; i++) + SEGMENT.setPixelColor(i, color1); } //fill between points a and b with color2 if (a < b) { - for (uint16_t i = a; i < b; i++) - setPixelColor(i, color2); + for (int i = a; i < b; i++) + SEGMENT.setPixelColor(i, color2); } else { - for (uint16_t i = a; i < SEGLEN; i++) //fill until end - setPixelColor(i, color2); - for (uint16_t i = 0; i < b; i++) //fill from start until b - setPixelColor(i, color2); + for (int i = a; i < SEGLEN; i++) //fill until end + SEGMENT.setPixelColor(i, color2); + for (int i = 0; i < b; i++) //fill from start until b + SEGMENT.setPixelColor(i, color2); } //fill between points b and c with color2 if (b < c) { - for (uint16_t i = b; i < c; i++) - setPixelColor(i, color3); + for (int i = b; i < c; i++) + SEGMENT.setPixelColor(i, color3); } else { - for (uint16_t i = b; i < SEGLEN; i++) //fill until end - setPixelColor(i, color3); - for (uint16_t i = 0; i < c; i++) //fill from start until c - setPixelColor(i, color3); + for (int i = b; i < SEGLEN; i++) //fill until end + SEGMENT.setPixelColor(i, color3); + for (int i = 0; i < c; i++) //fill from start until c + SEGMENT.setPixelColor(i, color3); } return FRAMETIME; @@ -769,60 +885,64 @@ uint16_t WS2812FX::chase(uint32_t color1, uint32_t color2, uint32_t color3, bool /* * Bicolor chase, more primary color. */ -uint16_t WS2812FX::mode_chase_color(void) { +uint16_t mode_chase_color(void) { return chase(SEGCOLOR(1), (SEGCOLOR(2)) ? SEGCOLOR(2) : SEGCOLOR(0), SEGCOLOR(0), true); } +static const char _data_FX_MODE_CHASE_COLOR[] PROGMEM = "Chase@!,Width;!,!,!;!;1d"; /* * Primary running followed by random color. */ -uint16_t WS2812FX::mode_chase_random(void) { +uint16_t mode_chase_random(void) { return chase(SEGCOLOR(1), (SEGCOLOR(2)) ? SEGCOLOR(2) : SEGCOLOR(0), SEGCOLOR(0), false); } +static const char _data_FX_MODE_CHASE_RANDOM[] PROGMEM = "Chase Random@!,Width;!,,!;!;1d"; /* * Primary, secondary running on rainbow. */ -uint16_t WS2812FX::mode_chase_rainbow(void) { +uint16_t mode_chase_rainbow(void) { uint8_t color_sep = 256 / SEGLEN; if (color_sep == 0) color_sep = 1; // correction for segments longer than 256 LEDs uint8_t color_index = SEGENV.call & 0xFF; - uint32_t color = color_wheel(((SEGENV.step * color_sep) + color_index) & 0xFF); + uint32_t color = SEGMENT.color_wheel(((SEGENV.step * color_sep) + color_index) & 0xFF); return chase(color, SEGCOLOR(0), SEGCOLOR(1), false); } +static const char _data_FX_MODE_CHASE_RAINBOW[] PROGMEM = "Chase Rainbow@!,Width;!,!,;;1d"; /* * Primary running on rainbow. */ -uint16_t WS2812FX::mode_chase_rainbow_white(void) { +uint16_t mode_chase_rainbow_white(void) { uint16_t n = SEGENV.step; uint16_t m = (SEGENV.step + 1) % SEGLEN; - uint32_t color2 = color_wheel(((n * 256 / SEGLEN) + (SEGENV.call & 0xFF)) & 0xFF); - uint32_t color3 = color_wheel(((m * 256 / SEGLEN) + (SEGENV.call & 0xFF)) & 0xFF); + uint32_t color2 = SEGMENT.color_wheel(((n * 256 / SEGLEN) + (SEGENV.call & 0xFF)) & 0xFF); + uint32_t color3 = SEGMENT.color_wheel(((m * 256 / SEGLEN) + (SEGENV.call & 0xFF)) & 0xFF); return chase(SEGCOLOR(0), color2, color3, false); } +static const char _data_FX_MODE_CHASE_RAINBOW_WHITE[] PROGMEM = "Rainbow Runner@!,Size;Bg,,;;1d"; /* * Red - Amber - Green - Blue lights running */ -uint16_t WS2812FX::mode_colorful(void) { +uint16_t mode_colorful(void) { uint8_t numColors = 4; //3, 4, or 5 uint32_t cols[9]{0x00FF0000,0x00EEBB00,0x0000EE00,0x000077CC}; if (SEGMENT.intensity > 160 || SEGMENT.palette) { //palette or color if (!SEGMENT.palette) { numColors = 3; - for (uint8_t i = 0; i < 3; i++) cols[i] = SEGCOLOR(i); + for (size_t i = 0; i < 3; i++) cols[i] = SEGCOLOR(i); } else { uint16_t fac = 80; if (SEGMENT.palette == 52) {numColors = 5; fac = 61;} //C9 2 has 5 colors - for (uint8_t i = 0; i < numColors; i++) { - cols[i] = color_from_palette(i*fac, false, true, 255); + for (size_t i = 0; i < numColors; i++) { + cols[i] = SEGMENT.color_from_palette(i*fac, false, true, 255); } } } else if (SEGMENT.intensity < 80) //pastel (easter) colors @@ -832,10 +952,10 @@ uint16_t WS2812FX::mode_colorful(void) { cols[2] = 0x0077FF77; cols[3] = 0x0077F0F0; } - for (uint8_t i = numColors; i < numColors*2 -1; i++) cols[i] = cols[i-numColors]; + for (size_t i = numColors; i < numColors*2 -1U; i++) cols[i] = cols[i-numColors]; uint32_t cycleTime = 50 + (8 * (uint32_t)(255 - SEGMENT.speed)); - uint32_t it = now / cycleTime; + uint32_t it = strip.now / cycleTime; if (it != SEGENV.step) { if (SEGMENT.speed > 0) SEGENV.aux0++; @@ -843,54 +963,56 @@ uint16_t WS2812FX::mode_colorful(void) { SEGENV.step = it; } - for (uint16_t i = 0; i < SEGLEN; i+= numColors) + for (int i = 0; i < SEGLEN; i+= numColors) { - for (uint16_t j = 0; j < numColors; j++) setPixelColor(i + j, cols[SEGENV.aux0 + j]); + for (int j = 0; j < numColors; j++) SEGMENT.setPixelColor(i + j, cols[SEGENV.aux0 + j]); } return FRAMETIME; } +static const char _data_FX_MODE_COLORFUL[] PROGMEM = "Colorful@!,Saturation;1,2,3;!;1d"; /* * Emulates a traffic light. */ -uint16_t WS2812FX::mode_traffic_light(void) { - for(uint16_t i=0; i < SEGLEN; i++) - setPixelColor(i, color_from_palette(i, true, PALETTE_SOLID_WRAP, 1)); +uint16_t mode_traffic_light(void) { + for (int i=0; i < SEGLEN; i++) + SEGMENT.setPixelColor(i, SEGMENT.color_from_palette(i, true, PALETTE_SOLID_WRAP, 1)); uint32_t mdelay = 500; for (int i = 0; i < SEGLEN-2 ; i+=3) { switch (SEGENV.aux0) { - case 0: setPixelColor(i, 0x00FF0000); mdelay = 150 + (100 * (uint32_t)(255 - SEGMENT.speed));break; - case 1: setPixelColor(i, 0x00FF0000); mdelay = 150 + (20 * (uint32_t)(255 - SEGMENT.speed)); setPixelColor(i+1, 0x00EECC00); break; - case 2: setPixelColor(i+2, 0x0000FF00); mdelay = 150 + (100 * (uint32_t)(255 - SEGMENT.speed));break; - case 3: setPixelColor(i+1, 0x00EECC00); mdelay = 150 + (20 * (uint32_t)(255 - SEGMENT.speed));break; + case 0: SEGMENT.setPixelColor(i, 0x00FF0000); mdelay = 150 + (100 * (uint32_t)(255 - SEGMENT.speed));break; + case 1: SEGMENT.setPixelColor(i, 0x00FF0000); mdelay = 150 + (20 * (uint32_t)(255 - SEGMENT.speed)); SEGMENT.setPixelColor(i+1, 0x00EECC00); break; + case 2: SEGMENT.setPixelColor(i+2, 0x0000FF00); mdelay = 150 + (100 * (uint32_t)(255 - SEGMENT.speed));break; + case 3: SEGMENT.setPixelColor(i+1, 0x00EECC00); mdelay = 150 + (20 * (uint32_t)(255 - SEGMENT.speed));break; } } - if (now - SEGENV.step > mdelay) + if (strip.now - SEGENV.step > mdelay) { SEGENV.aux0++; if (SEGENV.aux0 == 1 && SEGMENT.intensity > 140) SEGENV.aux0 = 2; //skip Red + Amber, to get US-style sequence if (SEGENV.aux0 > 3) SEGENV.aux0 = 0; - SEGENV.step = now; + SEGENV.step = strip.now; } return FRAMETIME; } +static const char _data_FX_MODE_TRAFFIC_LIGHT[] PROGMEM = "Traffic Light@!,;,!,;!;1d"; /* * Sec flashes running on prim. */ #define FLASH_COUNT 4 -uint16_t WS2812FX::mode_chase_flash(void) { +uint16_t mode_chase_flash(void) { uint8_t flash_step = SEGENV.call % ((FLASH_COUNT * 2) + 1); - for(uint16_t i = 0; i < SEGLEN; i++) { - setPixelColor(i, color_from_palette(i, true, PALETTE_SOLID_WRAP, 0)); + for (int i = 0; i < SEGLEN; i++) { + SEGMENT.setPixelColor(i, SEGMENT.color_from_palette(i, true, PALETTE_SOLID_WRAP, 0)); } uint16_t delay = 10 + ((30 * (uint16_t)(255 - SEGMENT.speed)) / SEGLEN); @@ -898,8 +1020,8 @@ uint16_t WS2812FX::mode_chase_flash(void) { if(flash_step % 2 == 0) { uint16_t n = SEGENV.step; uint16_t m = (SEGENV.step + 1) % SEGLEN; - setPixelColor( n, SEGCOLOR(1)); - setPixelColor( m, SEGCOLOR(1)); + SEGMENT.setPixelColor( n, SEGCOLOR(1)); + SEGMENT.setPixelColor( m, SEGCOLOR(1)); delay = 20; } else { delay = 30; @@ -909,98 +1031,59 @@ uint16_t WS2812FX::mode_chase_flash(void) { } return delay; } +static const char _data_FX_MODE_CHASE_FLASH[] PROGMEM = "Chase Flash@!,;Bg,Fx,!;!;1d"; /* * Prim flashes running, followed by random color. */ -uint16_t WS2812FX::mode_chase_flash_random(void) { +uint16_t mode_chase_flash_random(void) { uint8_t flash_step = SEGENV.call % ((FLASH_COUNT * 2) + 1); - for(uint16_t i = 0; i < SEGENV.step; i++) { - setPixelColor(i, color_wheel(SEGENV.aux0)); + for (int i = 0; i < SEGENV.aux1; i++) { + SEGMENT.setPixelColor(i, SEGMENT.color_wheel(SEGENV.aux0)); } uint16_t delay = 1 + ((10 * (uint16_t)(255 - SEGMENT.speed)) / SEGLEN); if(flash_step < (FLASH_COUNT * 2)) { - uint16_t n = SEGENV.step; - uint16_t m = (SEGENV.step + 1) % SEGLEN; + uint16_t n = SEGENV.aux1; + uint16_t m = (SEGENV.aux1 + 1) % SEGLEN; if(flash_step % 2 == 0) { - setPixelColor( n, SEGCOLOR(0)); - setPixelColor( m, SEGCOLOR(0)); + SEGMENT.setPixelColor( n, SEGCOLOR(0)); + SEGMENT.setPixelColor( m, SEGCOLOR(0)); delay = 20; } else { - setPixelColor( n, color_wheel(SEGENV.aux0)); - setPixelColor( m, SEGCOLOR(1)); + SEGMENT.setPixelColor( n, SEGMENT.color_wheel(SEGENV.aux0)); + SEGMENT.setPixelColor( m, SEGCOLOR(1)); delay = 30; } } else { - SEGENV.step = (SEGENV.step + 1) % SEGLEN; + SEGENV.aux1 = (SEGENV.aux1 + 1) % SEGLEN; - if (SEGENV.step == 0) { - SEGENV.aux0 = get_random_wheel_index(SEGENV.aux0); + if (SEGENV.aux1 == 0) { + SEGENV.aux0 = SEGMENT.get_random_wheel_index(SEGENV.aux0); } } return delay; } +static const char _data_FX_MODE_CHASE_FLASH_RANDOM[] PROGMEM = "Chase Flash Rnd@!,;,Fx,;0;1d"; -/* - * Alternating pixels running function. - */ -uint16_t WS2812FX::running(uint32_t color1, uint32_t color2, bool theatre) { - uint8_t width = (theatre ? 3 : 1) + (SEGMENT.intensity >> 4); // window - uint32_t cycleTime = 50 + (255 - SEGMENT.speed); - uint32_t it = now / cycleTime; - bool usePalette = color1 == SEGCOLOR(0); - - for(uint16_t i = 0; i < SEGLEN; i++) { - uint32_t col = color2; - if (usePalette) color1 = color_from_palette(i, true, PALETTE_SOLID_WRAP, 0); - if (theatre) { - if ((i % width) == SEGENV.aux0) col = color1; - } else { - int8_t pos = (i % (width<<1)); - if ((pos < SEGENV.aux0-width) || ((pos >= SEGENV.aux0) && (pos < SEGENV.aux0+width))) col = color1; - } - setPixelColor(i,col); - } - - if (it != SEGENV.step) { - SEGENV.aux0 = (SEGENV.aux0 +1) % (theatre ? width : (width<<1)); - SEGENV.step = it; - } - return FRAMETIME; -} - /* * Alternating color/sec pixels running. */ -uint16_t WS2812FX::mode_running_color(void) { +uint16_t mode_running_color(void) { return running(SEGCOLOR(0), SEGCOLOR(1)); } - -/* - * Alternating red/white pixels running. - */ -uint16_t WS2812FX::mode_candy_cane(void) { - return running(RED, WHITE); -} - -/* - * Alternating orange/purple pixels running. - */ -uint16_t WS2812FX::mode_halloween(void) { - return running(PURPLE, ORANGE); -} +static const char _data_FX_MODE_RUNNING_COLOR[] PROGMEM = "Chase 2@!,Width;!,!,;!;1d"; /* * Random colored pixels running. ("Stream") */ -uint16_t WS2812FX::mode_running_random(void) { +uint16_t mode_running_random(void) { uint32_t cycleTime = 25 + (3 * (uint32_t)(255 - SEGMENT.speed)); - uint32_t it = now / cycleTime; + uint32_t it = strip.now / cycleTime; if (SEGENV.call == 0) SEGENV.aux0 = random16(); // random seed for PRNG on start uint8_t zoneSize = ((255-SEGMENT.intensity) >> 4) +1; @@ -1008,7 +1091,7 @@ uint16_t WS2812FX::mode_running_random(void) { uint8_t z = it % zoneSize; bool nzone = (!z && it != SEGENV.aux1); - for (uint16_t i=SEGLEN-1; i > 0; i--) { + for (int i=SEGLEN-1; i > 0; i--) { if (nzone || z >= zoneSize) { uint8_t lastrand = PRNG16 >> 8; int16_t diff = 0; @@ -1022,47 +1105,41 @@ uint16_t WS2812FX::mode_running_random(void) { } z = 0; } - setPixelColor(i, color_wheel(PRNG16 >> 8)); + SEGMENT.setPixelColor(i, SEGMENT.color_wheel(PRNG16 >> 8)); z++; } SEGENV.aux1 = it; return FRAMETIME; } +static const char _data_FX_MODE_RUNNING_RANDOM[] PROGMEM = "Stream"; -/* - * K.I.T.T. - */ -uint16_t WS2812FX::mode_larson_scanner(void){ - return larson_scanner(false); -} - -uint16_t WS2812FX::larson_scanner(bool dual) { - uint16_t counter = now * ((SEGMENT.speed >> 2) +8); +uint16_t larson_scanner(bool dual) { + uint16_t counter = strip.now * ((SEGMENT.speed >> 2) +8); uint16_t index = counter * SEGLEN >> 16; - fade_out(SEGMENT.intensity); + SEGMENT.fade_out(SEGMENT.intensity); if (SEGENV.step > index && SEGENV.step - index > SEGLEN/2) { SEGENV.aux0 = !SEGENV.aux0; } - for (uint16_t i = SEGENV.step; i < index; i++) { + for (int i = SEGENV.step; i < index; i++) { uint16_t j = (SEGENV.aux0)?i:SEGLEN-1-i; - setPixelColor( j, color_from_palette(j, true, PALETTE_SOLID_WRAP, 0)); + SEGMENT.setPixelColor( j, SEGMENT.color_from_palette(j, true, PALETTE_SOLID_WRAP, 0)); } if (dual) { uint32_t c; if (SEGCOLOR(2) != 0) { c = SEGCOLOR(2); } else { - c = color_from_palette(index, true, PALETTE_SOLID_WRAP, 0); + c = SEGMENT.color_from_palette(index, true, PALETTE_SOLID_WRAP, 0); } - for (uint16_t i = SEGENV.step; i < index; i++) { + for (int i = SEGENV.step; i < index; i++) { uint16_t j = (SEGENV.aux0)?SEGLEN-1-i:i; - setPixelColor(j, c); + SEGMENT.setPixelColor(j, c); } } @@ -1071,118 +1148,159 @@ uint16_t WS2812FX::larson_scanner(bool dual) { } +/* + * K.I.T.T. + */ +uint16_t mode_larson_scanner(void){ + return larson_scanner(false); +} +static const char _data_FX_MODE_LARSON_SCANNER[] PROGMEM = "Scanner@!,Fade rate;!,!,;!;mp12=0,1d"; + + +/* + * Creates two Larson scanners moving in opposite directions + * Custom mode by Keith Lord: https://github.com/kitesurfer1404/WS2812FX/blob/master/src/custom/DualLarson.h + */ +uint16_t mode_dual_larson_scanner(void){ + return larson_scanner(true); +} +static const char _data_FX_MODE_DUAL_LARSON_SCANNER[] PROGMEM = "Scanner Dual@!,Fade rate;!,!,;!;mp12=0,1d"; + + /* * Firing comets from one end. "Lighthouse" */ -uint16_t WS2812FX::mode_comet(void) { - uint16_t counter = now * ((SEGMENT.speed >>2) +1); - uint16_t index = counter * SEGLEN >> 16; +uint16_t mode_comet(void) { + uint16_t counter = strip.now * ((SEGMENT.speed >>2) +1); + uint16_t index = (counter * SEGLEN) >> 16; if (SEGENV.call == 0) SEGENV.aux0 = index; - fade_out(SEGMENT.intensity); + SEGMENT.fade_out(SEGMENT.intensity); - setPixelColor( index, color_from_palette(index, true, PALETTE_SOLID_WRAP, 0)); + SEGMENT.setPixelColor( index, SEGMENT.color_from_palette(index, true, PALETTE_SOLID_WRAP, 0)); if (index > SEGENV.aux0) { - for (uint16_t i = SEGENV.aux0; i < index ; i++) { - setPixelColor( i, color_from_palette(i, true, PALETTE_SOLID_WRAP, 0)); + for (int i = SEGENV.aux0; i < index ; i++) { + SEGMENT.setPixelColor( i, SEGMENT.color_from_palette(i, true, PALETTE_SOLID_WRAP, 0)); } } else if (index < SEGENV.aux0 && index < 10) { - for (uint16_t i = 0; i < index ; i++) { - setPixelColor( i, color_from_palette(i, true, PALETTE_SOLID_WRAP, 0)); + for (int i = 0; i < index ; i++) { + SEGMENT.setPixelColor( i, SEGMENT.color_from_palette(i, true, PALETTE_SOLID_WRAP, 0)); } } SEGENV.aux0 = index++; return FRAMETIME; } +static const char _data_FX_MODE_COMET[] PROGMEM = "Lighthouse@!,Fade rate;!,!,!;!;1d"; /* * Fireworks function. */ -uint16_t WS2812FX::mode_fireworks() { - fade_out(0); +uint16_t mode_fireworks() { + const uint16_t width = strip.isMatrix ? SEGMENT.virtualWidth() : SEGMENT.virtualLength(); + const uint16_t height = SEGMENT.virtualHeight(); + + SEGMENT.fade_out(0); + if (SEGENV.call == 0) { SEGENV.aux0 = UINT16_MAX; SEGENV.aux1 = UINT16_MAX; } - bool valid1 = (SEGENV.aux0 < SEGLEN); - bool valid2 = (SEGENV.aux1 < SEGLEN); + bool valid1 = (SEGENV.aux0 < width*height); + bool valid2 = (SEGENV.aux1 < width*height); uint32_t sv1 = 0, sv2 = 0; - if (valid1) sv1 = getPixelColor(SEGENV.aux0); - if (valid2) sv2 = getPixelColor(SEGENV.aux1); - blur(255-SEGMENT.speed); - if (valid1) setPixelColor(SEGENV.aux0 , sv1); - if (valid2) setPixelColor(SEGENV.aux1, sv2); - - for(uint16_t i=0; i> 1)) == 0) { - uint16_t index = random(SEGLEN); - setPixelColor(index, color_from_palette(random8(), false, false, 0)); - SEGENV.aux1 = SEGENV.aux0; - SEGENV.aux0 = index; + if (valid1) sv1 = strip.isMatrix ? SEGMENT.getPixelColorXY(SEGENV.aux0%width, SEGENV.aux0/width) : SEGMENT.getPixelColor(SEGENV.aux0); // TODO get spark color + if (valid2) sv2 = strip.isMatrix ? SEGMENT.getPixelColorXY(SEGENV.aux1%width, SEGENV.aux1/width) : SEGMENT.getPixelColor(SEGENV.aux1); // TODO + if (!SEGENV.step) SEGMENT.blur(16); + if (valid1) { if (strip.isMatrix) SEGMENT.setPixelColorXY(SEGENV.aux0%width, SEGENV.aux0/width, sv1); else SEGMENT.setPixelColor(SEGENV.aux0, sv1); } // restore spark color after blur + if (valid2) { if (strip.isMatrix) SEGMENT.setPixelColorXY(SEGENV.aux1%width, SEGENV.aux1/width, sv2); else SEGMENT.setPixelColor(SEGENV.aux1, sv2); } // restore old spark color after blur + + for (int i=0; i> 1)) == 0) { + uint16_t index = random16(width*height); + uint16_t j = index % width, k = index / width; + uint32_t col = SEGMENT.color_from_palette(random8(), false, false, 0); + if (strip.isMatrix) SEGMENT.setPixelColorXY(j, k, col); + else SEGMENT.setPixelColor(index, col); + SEGENV.aux1 = SEGENV.aux0; // old spark + SEGENV.aux0 = index; // remember where spark occured } } return FRAMETIME; } +static const char _data_FX_MODE_FIREWORKS[] PROGMEM = "Fireworks@,Frequency;!,!,;!;ix=192,pal=11,1d,2d"; //Twinkling LEDs running. Inspired by https://github.com/kitesurfer1404/WS2812FX/blob/master/src/custom/Rain.h -uint16_t WS2812FX::mode_rain() +uint16_t mode_rain() { + const uint16_t width = SEGMENT.virtualWidth(); + const uint16_t height = SEGMENT.virtualHeight(); SEGENV.step += FRAMETIME; if (SEGENV.step > SPEED_FORMULA_L) { - SEGENV.step = 0; - //shift all leds left - uint32_t ctemp = getPixelColor(0); - for(uint16_t i = 0; i < SEGLEN - 1; i++) { - setPixelColor(i, getPixelColor(i+1)); + SEGENV.step = 1; + if (strip.isMatrix) { + uint32_t ctemp[width]; + for (int i = 0; i= width*height) SEGENV.aux0 = 0; // ignore + if (SEGENV.aux1 >= width*height) SEGENV.aux1 = 0; } return mode_fireworks(); } +static const char _data_FX_MODE_RAIN[] PROGMEM = "Rain@!,Spawning rate;!,!,;;ix=128,pal=0,1d,2d"; /* * Fire flicker function */ -uint16_t WS2812FX::mode_fire_flicker(void) { +uint16_t mode_fire_flicker(void) { uint32_t cycleTime = 40 + (255 - SEGMENT.speed); - uint32_t it = now / cycleTime; + uint32_t it = strip.now / cycleTime; if (SEGENV.step == it) return FRAMETIME; - + byte w = (SEGCOLOR(0) >> 24); byte r = (SEGCOLOR(0) >> 16); byte g = (SEGCOLOR(0) >> 8); byte b = (SEGCOLOR(0) ); byte lum = (SEGMENT.palette == 0) ? MAX(w, MAX(r, MAX(g, b))) : 255; lum /= (((256-SEGMENT.intensity)/16)+1); - for(uint16_t i = 0; i < SEGLEN; i++) { + for (int i = 0; i < SEGLEN; i++) { byte flicker = random8(lum); if (SEGMENT.palette == 0) { - setPixelColor(i, MAX(r - flicker, 0), MAX(g - flicker, 0), MAX(b - flicker, 0), MAX(w - flicker, 0)); + SEGMENT.setPixelColor(i, MAX(r - flicker, 0), MAX(g - flicker, 0), MAX(b - flicker, 0), MAX(w - flicker, 0)); } else { - setPixelColor(i, color_from_palette(i, true, PALETTE_SOLID_WRAP, 0, 255 - flicker)); + SEGMENT.setPixelColor(i, SEGMENT.color_from_palette(i, true, PALETTE_SOLID_WRAP, 0, 255 - flicker)); } } SEGENV.step = it; return FRAMETIME; } +static const char _data_FX_MODE_FIRE_FLICKER[] PROGMEM = "Fire Flicker@!,!;!,,;!;1d"; /* * Gradient run base function */ -uint16_t WS2812FX::gradient_base(bool loading) { - uint16_t counter = now * ((SEGMENT.speed >> 2) + 1); +uint16_t gradient_base(bool loading) { + uint16_t counter = strip.now * ((SEGMENT.speed >> 2) + 1); uint16_t pp = counter * SEGLEN >> 16; if (SEGENV.call == 0) pp = 0; float val; //0.0 = sec 1.0 = pri @@ -1191,7 +1309,7 @@ uint16_t WS2812FX::gradient_base(bool loading) { int p1 = pp-SEGLEN; int p2 = pp+SEGLEN; - for(uint16_t i = 0; i < SEGLEN; i++) + for (int i = 0; i < SEGLEN; i++) { if (loading) { @@ -1200,7 +1318,7 @@ uint16_t WS2812FX::gradient_base(bool loading) { val = MIN(abs(pp-i),MIN(abs(p1-i),abs(p2-i))); } val = (brd > val) ? val/brd * 255 : 255; - setPixelColor(i, color_blend(SEGCOLOR(0), color_from_palette(i, true, PALETTE_SOLID_WRAP, 1), val)); + SEGMENT.setPixelColor(i, color_blend(SEGCOLOR(0), SEGMENT.color_from_palette(i, true, PALETTE_SOLID_WRAP, 1), val)); } return FRAMETIME; @@ -1210,54 +1328,58 @@ uint16_t WS2812FX::gradient_base(bool loading) { /* * Gradient run */ -uint16_t WS2812FX::mode_gradient(void) { +uint16_t mode_gradient(void) { return gradient_base(false); } +static const char _data_FX_MODE_GRADIENT[] PROGMEM = "Gradient@!,Spread;!,!,;!;ix=16,1d"; /* * Gradient run with hard transition */ -uint16_t WS2812FX::mode_loading(void) { +uint16_t mode_loading(void) { return gradient_base(true); } +static const char _data_FX_MODE_LOADING[] PROGMEM = "Loading@!,Fade;!,!,;!;ix=16,1d"; //American Police Light with all LEDs Red and Blue -uint16_t WS2812FX::police_base(uint32_t color1, uint32_t color2) +uint16_t police_base(uint32_t color1, uint32_t color2) { uint16_t delay = 1 + (FRAMETIME<<3) / SEGLEN; // longer segments should change faster - uint32_t it = now / map(SEGMENT.speed, 0, 255, delay<<4, delay); + uint32_t it = strip.now / map(SEGMENT.speed, 0, 255, delay<<4, delay); uint16_t offset = it % SEGLEN; uint16_t width = ((SEGLEN*(SEGMENT.intensity+1))>>9); //max width is half the strip if (!width) width = 1; - for (uint16_t i = 0; i < width; i++) { + for (int i = 0; i < width; i++) { uint16_t indexR = (offset + i) % SEGLEN; uint16_t indexB = (offset + i + (SEGLEN>>1)) % SEGLEN; - setPixelColor(indexR, color1); - setPixelColor(indexB, color2); + SEGMENT.setPixelColor(indexR, color1); + SEGMENT.setPixelColor(indexB, color2); } return FRAMETIME; } //Police Lights Red and Blue -uint16_t WS2812FX::mode_police() -{ - fill(SEGCOLOR(1)); - return police_base(RED, BLUE); -} +//uint16_t mode_police() +//{ +// SEGMENT.fill(SEGCOLOR(1)); +// return police_base(RED, BLUE); +//} +//static const char _data_FX_MODE_POLICE[] PROGMEM = "Police@!,Width;,Bg,;0"; //Police Lights with custom colors -uint16_t WS2812FX::mode_two_dots() +uint16_t mode_two_dots() { - fill(SEGCOLOR(2)); + SEGMENT.fill(SEGCOLOR(2)); uint32_t color2 = (SEGCOLOR(1) == SEGCOLOR(2)) ? SEGCOLOR(0) : SEGCOLOR(1); return police_base(SEGCOLOR(0), color2); } +static const char _data_FX_MODE_TWO_DOTS[] PROGMEM = "Two Dots@!,Dot size;1,2,Bg;!;1d"; /* @@ -1273,12 +1395,12 @@ typedef struct Flasher { #define FLASHERS_PER_ZONE 6 #define MAX_SHIMMER 92 -uint16_t WS2812FX::mode_fairy() { +uint16_t mode_fairy() { //set every pixel to a 'random' color from palette (using seed so it doesn't change between frames) - uint16_t PRNG16 = 5100 + _segment_index; - for (uint16_t i = 0; i < SEGLEN; i++) { + uint16_t PRNG16 = 5100 + strip.getCurrSegmentId(); + for (int i = 0; i < SEGLEN; i++) { PRNG16 = (uint16_t)(PRNG16 * 2053) + 1384; //next 'random' number - setPixelColor(i, color_from_palette(PRNG16 >> 8, false, false, 0)); + SEGMENT.setPixelColor(i, SEGMENT.color_from_palette(PRNG16 >> 8, false, false, 0)); } //amount of flasher pixels depending on intensity (0: none, 255: every LED) @@ -1289,7 +1411,7 @@ uint16_t WS2812FX::mode_fairy() { uint16_t dataSize = sizeof(flasher) * numFlashers; if (!SEGENV.allocateData(dataSize)) return FRAMETIME; //allocation failed Flasher* flashers = reinterpret_cast(SEGENV.data); - uint16_t now16 = now & 0xFFFF; + uint16_t now16 = strip.now & 0xFFFF; //Up to 11 flashers in one brightness zone, afterwards a new zone for every 6 flashers uint16_t zones = numFlashers/FLASHERS_PER_ZONE; @@ -1297,12 +1419,12 @@ uint16_t WS2812FX::mode_fairy() { uint8_t flashersInZone = numFlashers/zones; uint8_t flasherBri[FLASHERS_PER_ZONE*2 -1]; - for (uint16_t z = 0; z < zones; z++) { + for (int z = 0; z < zones; z++) { uint16_t flasherBriSum = 0; uint16_t firstFlasher = z*flashersInZone; if (z == zones-1) flashersInZone = numFlashers-(flashersInZone*(zones-1)); - for (uint16_t f = firstFlasher; f < firstFlasher + flashersInZone; f++) { + for (int f = firstFlasher; f < firstFlasher + flashersInZone; f++) { uint16_t stateTime = now16 - flashers[f].stateStart; //random on/off time reached, switch state if (stateTime > flashers[f].stateDur * 10) { @@ -1323,7 +1445,7 @@ uint16_t WS2812FX::mode_fairy() { } } if (stateTime > 255) stateTime = 255; //for flasher brightness calculation, fades in first 255 ms of state - //flasherBri[f - firstFlasher] = (flashers[f].stateOn) ? 255-gamma8((510 - stateTime) >> 1) : gamma8((510 - stateTime) >> 1); + //flasherBri[f - firstFlasher] = (flashers[f].stateOn) ? 255-SEGMENT.gamma8((510 - stateTime) >> 1) : SEGMENT.gamma8((510 - stateTime) >> 1); flasherBri[f - firstFlasher] = (flashers[f].stateOn) ? stateTime : 255 - (stateTime >> 0); flasherBriSum += flasherBri[f - firstFlasher]; } @@ -1331,36 +1453,37 @@ uint16_t WS2812FX::mode_fairy() { uint8_t avgFlasherBri = flasherBriSum / flashersInZone; uint8_t globalPeakBri = 255 - ((avgFlasherBri * MAX_SHIMMER) >> 8); //183-255, suitable for 1/5th of LEDs flashers - for (uint16_t f = firstFlasher; f < firstFlasher + flashersInZone; f++) { + for (int f = firstFlasher; f < firstFlasher + flashersInZone; f++) { uint8_t bri = (flasherBri[f - firstFlasher] * globalPeakBri) / 255; PRNG16 = (uint16_t)(PRNG16 * 2053) + 1384; //next 'random' number uint16_t flasherPos = f*flasherDistance; - setPixelColor(flasherPos, color_blend(SEGCOLOR(1), color_from_palette(PRNG16 >> 8, false, false, 0), bri)); - for (uint16_t i = flasherPos+1; i < flasherPos+flasherDistance && i < SEGLEN; i++) { + SEGMENT.setPixelColor(flasherPos, color_blend(SEGCOLOR(1), SEGMENT.color_from_palette(PRNG16 >> 8, false, false, 0), bri)); + for (int i = flasherPos+1; i < flasherPos+flasherDistance && i < SEGLEN; i++) { PRNG16 = (uint16_t)(PRNG16 * 2053) + 1384; //next 'random' number - setPixelColor(i, color_from_palette(PRNG16 >> 8, false, false, 0, globalPeakBri)); + SEGMENT.setPixelColor(i, SEGMENT.color_from_palette(PRNG16 >> 8, false, false, 0, globalPeakBri)); } } } return FRAMETIME; } +static const char _data_FX_MODE_FAIRY[] PROGMEM = "Fairy"; /* - * Fairytwinkle. Like Colortwinkle, but starting from all lit and not relying on getPixelColor + * Fairytwinkle. Like Colortwinkle, but starting from all lit and not relying on strip.getPixelColor * Warning: Uses 4 bytes of segment data per pixel */ -uint16_t WS2812FX::mode_fairytwinkle() { +uint16_t mode_fairytwinkle() { uint16_t dataSize = sizeof(flasher) * SEGLEN; if (!SEGENV.allocateData(dataSize)) return mode_static(); //allocation failed Flasher* flashers = reinterpret_cast(SEGENV.data); - uint16_t now16 = now & 0xFFFF; - uint16_t PRNG16 = 5100 + _segment_index; + uint16_t now16 = strip.now & 0xFFFF; + uint16_t PRNG16 = 5100 + strip.getCurrSegmentId(); uint16_t riseFallTime = 400 + (255-SEGMENT.speed)*3; uint16_t maxDur = riseFallTime/100 + ((255 - SEGMENT.intensity) >> 2) + 13 + ((255 - SEGMENT.intensity) >> 1); - for (uint16_t f = 0; f < SEGLEN; f++) { + for (int f = 0; f < SEGLEN; f++) { uint16_t stateTime = now16 - flashers[f].stateStart; //random on/off time reached, switch state if (stateTime > flashers[f].stateDur * 100) { @@ -1389,29 +1512,30 @@ uint16_t WS2812FX::mode_fairytwinkle() { PRNG16 = (uint16_t)(PRNG16 * 2053) + 1384; //next 'random' number diff = (PRNG16 > lastR) ? PRNG16 - lastR : lastR - PRNG16; } - setPixelColor(f, color_blend(SEGCOLOR(1), color_from_palette(PRNG16 >> 8, false, false, 0), flasherBri)); + SEGMENT.setPixelColor(f, color_blend(SEGCOLOR(1), SEGMENT.color_from_palette(PRNG16 >> 8, false, false, 0), flasherBri)); } return FRAMETIME; } +static const char _data_FX_MODE_FAIRYTWINKLE[] PROGMEM = "Fairy Twinkle@;;;mp12=0,1d"; //pixels /* * Tricolor chase function */ -uint16_t WS2812FX::tricolor_chase(uint32_t color1, uint32_t color2) { +uint16_t tricolor_chase(uint32_t color1, uint32_t color2) { uint32_t cycleTime = 50 + ((255 - SEGMENT.speed)<<1); - uint32_t it = now / cycleTime; // iterator + uint32_t it = strip.now / cycleTime; // iterator uint8_t width = (1 + (SEGMENT.intensity>>4)); // value of 1-16 for each colour uint8_t index = it % (width*3); - for (uint16_t i = 0; i < SEGLEN; i++, index++) { + for (int i = 0; i < SEGLEN; i++, index++) { if (index > (width*3)-1) index = 0; uint32_t color = color1; - if (index > (width<<1)-1) color = color_from_palette(i, true, PALETTE_SOLID_WRAP, 1); + if (index > (width<<1)-1) color = SEGMENT.color_from_palette(i, true, PALETTE_SOLID_WRAP, 1); else if (index > width-1) color = color2; - setPixelColor(SEGLEN - i -1, color); + SEGMENT.setPixelColor(SEGLEN - i -1, color); } return FRAMETIME; } @@ -1420,30 +1544,31 @@ uint16_t WS2812FX::tricolor_chase(uint32_t color1, uint32_t color2) { /* * Tricolor chase mode */ -uint16_t WS2812FX::mode_tricolor_chase(void) { +uint16_t mode_tricolor_chase(void) { return tricolor_chase(SEGCOLOR(2), SEGCOLOR(0)); } +static const char _data_FX_MODE_TRICOLOR_CHASE[] PROGMEM = "Chase 3@!,Size;1,2,3;0;1d"; /* * ICU mode */ -uint16_t WS2812FX::mode_icu(void) { +uint16_t mode_icu(void) { uint16_t dest = SEGENV.step & 0xFFFF; uint8_t space = (SEGMENT.intensity >> 3) +2; - fill(SEGCOLOR(1)); + SEGMENT.fill(SEGCOLOR(1)); byte pindex = map(dest, 0, SEGLEN-SEGLEN/space, 0, 255); - uint32_t col = color_from_palette(pindex, false, false, 0); + uint32_t col = SEGMENT.color_from_palette(pindex, false, false, 0); - setPixelColor(dest, col); - setPixelColor(dest + SEGLEN/space, col); + SEGMENT.setPixelColor(dest, col); + SEGMENT.setPixelColor(dest + SEGLEN/space, col); if(SEGENV.aux0 == dest) { // pause between eye movements if(random8(6) == 0) { // blink once in a while - setPixelColor(dest, SEGCOLOR(1)); - setPixelColor(dest + SEGLEN/space, SEGCOLOR(1)); + SEGMENT.setPixelColor(dest, SEGCOLOR(1)); + SEGMENT.setPixelColor(dest + SEGLEN/space, SEGCOLOR(1)); return 200; } SEGENV.aux0 = random16(SEGLEN-SEGLEN/space); @@ -1458,51 +1583,53 @@ uint16_t WS2812FX::mode_icu(void) { dest--; } - setPixelColor(dest, col); - setPixelColor(dest + SEGLEN/space, col); + SEGMENT.setPixelColor(dest, col); + SEGMENT.setPixelColor(dest + SEGLEN/space, col); return SPEED_FORMULA_L; } +static const char _data_FX_MODE_ICU[] PROGMEM = "ICU"; /* * Custom mode by Aircoookie. Color Wipe, but with 3 colors */ -uint16_t WS2812FX::mode_tricolor_wipe(void) +uint16_t mode_tricolor_wipe(void) { uint32_t cycleTime = 1000 + (255 - SEGMENT.speed)*200; - uint32_t perc = now % cycleTime; + uint32_t perc = strip.now % cycleTime; uint16_t prog = (perc * 65535) / cycleTime; uint16_t ledIndex = (prog * SEGLEN * 3) >> 16; uint16_t ledOffset = ledIndex; - for (uint16_t i = 0; i < SEGLEN; i++) + for (int i = 0; i < SEGLEN; i++) { - setPixelColor(i, color_from_palette(i, true, PALETTE_SOLID_WRAP, 2)); + SEGMENT.setPixelColor(i, SEGMENT.color_from_palette(i, true, PALETTE_SOLID_WRAP, 2)); } if(ledIndex < SEGLEN) { //wipe from 0 to 1 - for (uint16_t i = 0; i < SEGLEN; i++) + for (int i = 0; i < SEGLEN; i++) { - setPixelColor(i, (i > ledOffset)? SEGCOLOR(0) : SEGCOLOR(1)); + SEGMENT.setPixelColor(i, (i > ledOffset)? SEGCOLOR(0) : SEGCOLOR(1)); } } else if (ledIndex < SEGLEN*2) { //wipe from 1 to 2 ledOffset = ledIndex - SEGLEN; - for (uint16_t i = ledOffset +1; i < SEGLEN; i++) + for (int i = ledOffset +1; i < SEGLEN; i++) { - setPixelColor(i, SEGCOLOR(1)); + SEGMENT.setPixelColor(i, SEGCOLOR(1)); } } else //wipe from 2 to 0 { ledOffset = ledIndex - SEGLEN*2; - for (uint16_t i = 0; i <= ledOffset; i++) + for (int i = 0; i <= ledOffset; i++) { - setPixelColor(i, SEGCOLOR(0)); + SEGMENT.setPixelColor(i, SEGCOLOR(0)); } } return FRAMETIME; } +static const char _data_FX_MODE_TRICOLOR_WIPE[] PROGMEM = "Tri Wipe@!,;1,2,3;0;1d"; /* @@ -1510,9 +1637,9 @@ uint16_t WS2812FX::mode_tricolor_wipe(void) * Custom mode by Keith Lord: https://github.com/kitesurfer1404/WS2812FX/blob/master/src/custom/TriFade.h * Modified by Aircoookie */ -uint16_t WS2812FX::mode_tricolor_fade(void) +uint16_t mode_tricolor_fade(void) { - uint16_t counter = now * ((SEGMENT.speed >> 3) +1); + uint16_t counter = strip.now * ((SEGMENT.speed >> 3) +1); uint32_t prog = (counter * 768) >> 16; uint32_t color1 = 0, color2 = 0; @@ -1533,46 +1660,47 @@ uint16_t WS2812FX::mode_tricolor_fade(void) } byte stp = prog; // % 256 - for(uint16_t i = 0; i < SEGLEN; i++) { + for (int i = 0; i < SEGLEN; i++) { uint32_t color; if (stage == 2) { - color = color_blend(color_from_palette(i, true, PALETTE_SOLID_WRAP, 2), color2, stp); + color = color_blend(SEGMENT.color_from_palette(i, true, PALETTE_SOLID_WRAP, 2), color2, stp); } else if (stage == 1) { - color = color_blend(color1, color_from_palette(i, true, PALETTE_SOLID_WRAP, 2), stp); + color = color_blend(color1, SEGMENT.color_from_palette(i, true, PALETTE_SOLID_WRAP, 2), stp); } else { color = color_blend(color1, color2, stp); } - setPixelColor(i, color); + SEGMENT.setPixelColor(i, color); } return FRAMETIME; } +static const char _data_FX_MODE_TRICOLOR_FADE[] PROGMEM = "Tri Fade"; /* * Creates random comets * Custom mode by Keith Lord: https://github.com/kitesurfer1404/WS2812FX/blob/master/src/custom/MultiComet.h */ -uint16_t WS2812FX::mode_multi_comet(void) +uint16_t mode_multi_comet(void) { uint32_t cycleTime = 10 + (uint32_t)(255 - SEGMENT.speed); - uint32_t it = now / cycleTime; + uint32_t it = strip.now / cycleTime; if (SEGENV.step == it) return FRAMETIME; if (!SEGENV.allocateData(sizeof(uint16_t) * 8)) return mode_static(); //allocation failed - fade_out(SEGMENT.intensity); + SEGMENT.fade_out(SEGMENT.intensity); uint16_t* comets = reinterpret_cast(SEGENV.data); - for(uint8_t i=0; i < 8; i++) { + for (int i=0; i < 8; i++) { if(comets[i] < SEGLEN) { uint16_t index = comets[i]; if (SEGCOLOR(2) != 0) { - setPixelColor(index, i % 2 ? color_from_palette(index, true, PALETTE_SOLID_WRAP, 0) : SEGCOLOR(2)); + SEGMENT.setPixelColor(index, i % 2 ? SEGMENT.color_from_palette(index, true, PALETTE_SOLID_WRAP, 0) : SEGCOLOR(2)); } else { - setPixelColor(index, color_from_palette(index, true, PALETTE_SOLID_WRAP, 0)); + SEGMENT.setPixelColor(index, SEGMENT.color_from_palette(index, true, PALETTE_SOLID_WRAP, 0)); } comets[i]++; } else { @@ -1585,22 +1713,14 @@ uint16_t WS2812FX::mode_multi_comet(void) SEGENV.step = it; return FRAMETIME; } - - -/* - * Creates two Larson scanners moving in opposite directions - * Custom mode by Keith Lord: https://github.com/kitesurfer1404/WS2812FX/blob/master/src/custom/DualLarson.h - */ -uint16_t WS2812FX::mode_dual_larson_scanner(void){ - return larson_scanner(true); -} +static const char _data_FX_MODE_MULTI_COMET[] PROGMEM = "Multi Comet"; /* * Running random pixels ("Stream 2") * Custom mode by Keith Lord: https://github.com/kitesurfer1404/WS2812FX/blob/master/src/custom/RandomChase.h */ -uint16_t WS2812FX::mode_random_chase(void) +uint16_t mode_random_chase(void) { if (SEGENV.call == 0) { SEGENV.step = RGBW32(random8(), random8(), random8(), 0); @@ -1608,16 +1728,16 @@ uint16_t WS2812FX::mode_random_chase(void) } uint16_t prevSeed = random16_get_seed(); // save seed so we can restore it at the end of the function uint32_t cycleTime = 25 + (3 * (uint32_t)(255 - SEGMENT.speed)); - uint32_t it = now / cycleTime; + uint32_t it = strip.now / cycleTime; uint32_t color = SEGENV.step; random16_set_seed(SEGENV.aux0); - for(uint16_t i = SEGLEN -1; i > 0; i--) { + for (int i = SEGLEN -1; i > 0; i--) { uint8_t r = random8(6) != 0 ? (color >> 16 & 0xFF) : random8(); uint8_t g = random8(6) != 0 ? (color >> 8 & 0xFF) : random8(); uint8_t b = random8(6) != 0 ? (color & 0xFF) : random8(); color = RGBW32(r, g, b, 0); - setPixelColor(i, r, g, b); + SEGMENT.setPixelColor(i, r, g, b); if (i == SEGLEN -1 && SEGENV.aux1 != (it & 0xFFFF)) { //new first color in next frame SEGENV.step = color; SEGENV.aux0 = random16_get_seed(); @@ -1629,6 +1749,8 @@ uint16_t WS2812FX::mode_random_chase(void) random16_set_seed(prevSeed); // restore original seed so other effects can use "random" PRNG return FRAMETIME; } +static const char _data_FX_MODE_RANDOM_CHASE[] PROGMEM = "Stream 2"; + //7 bytes typedef struct Oscillator { @@ -1641,7 +1763,7 @@ typedef struct Oscillator { /* / Oscillating bars of color, updated with standard framerate */ -uint16_t WS2812FX::mode_oscillate(void) +uint16_t mode_oscillate(void) { uint8_t numOscillators = 3; uint16_t dataSize = sizeof(oscillator) * numOscillators; @@ -1658,9 +1780,9 @@ uint16_t WS2812FX::mode_oscillate(void) } uint32_t cycleTime = 20 + (2 * (uint32_t)(255 - SEGMENT.speed)); - uint32_t it = now / cycleTime; + uint32_t it = strip.now / cycleTime; - for(uint8_t i = 0; i < numOscillators; i++) { + for (int i = 0; i < numOscillators; i++) { // if the counter has increased, move the oscillator by the random step if (it != SEGENV.step) oscillators[i].pos += oscillators[i].dir * oscillators[i].speed; oscillators[i].size = SEGLEN/(3+SEGMENT.intensity/8); @@ -1677,22 +1799,24 @@ uint16_t WS2812FX::mode_oscillate(void) } } - for(uint16_t i=0; i < SEGLEN; i++) { + for (int i=0; i < SEGLEN; i++) { uint32_t color = BLACK; - for(uint8_t j=0; j < numOscillators; j++) { + for (int j=0; j < numOscillators; j++) { if(i >= oscillators[j].pos - oscillators[j].size && i <= oscillators[j].pos + oscillators[j].size) { color = (color == BLACK) ? SEGCOLOR(j) : color_blend(color, SEGCOLOR(j), 128); } } - setPixelColor(i, color); + SEGMENT.setPixelColor(i, color); } SEGENV.step = it; return FRAMETIME; } +static const char _data_FX_MODE_OSCILLATE[] PROGMEM = "Oscillate"; -uint16_t WS2812FX::mode_lightning(void) +//TODO +uint16_t mode_lightning(void) { uint16_t ledstart = random16(SEGLEN); // Determine starting location of flash uint16_t ledlen = 1 + random16(SEGLEN -ledstart); // Determine length of flash (not to go beyond NUM_LEDS-1) @@ -1707,12 +1831,12 @@ uint16_t WS2812FX::mode_lightning(void) SEGENV.aux0 = 200; //200ms delay after leader } - fill(SEGCOLOR(1)); + SEGMENT.fill(SEGCOLOR(1)); if (SEGENV.aux1 > 3 && !(SEGENV.aux1 & 0x01)) { //flash on even number >2 for (int i = ledstart; i < ledstart + ledlen; i++) { - setPixelColor(i,color_from_palette(i, true, PALETTE_SOLID_WRAP, 0, bri)); + SEGMENT.setPixelColor(i,SEGMENT.color_from_palette(i, true, PALETTE_SOLID_WRAP, 0, bri)); } SEGENV.aux1--; @@ -1732,12 +1856,13 @@ uint16_t WS2812FX::mode_lightning(void) } return FRAMETIME; } +static const char _data_FX_MODE_LIGHTNING[] PROGMEM = "Lightning"; // Pride2015 // Animated, ever-changing rainbows. // by Mark Kriegsman: https://gist.github.com/kriegsman/964de772d64c502760e5 -uint16_t WS2812FX::mode_pride_2015(void) +uint16_t mode_pride_2015(void) { uint16_t duration = 10 + SEGMENT.speed; uint16_t sPseudotime = SEGENV.step; @@ -1756,7 +1881,7 @@ uint16_t WS2812FX::mode_pride_2015(void) uint16_t brightnesstheta16 = sPseudotime; CRGB fastled_col; - for (uint16_t i = 0 ; i < SEGLEN; i++) { + for (int i = 0 ; i < SEGLEN; i++) { hue16 += hueinc16; uint8_t hue8 = hue16 >> 8; @@ -1768,53 +1893,56 @@ uint16_t WS2812FX::mode_pride_2015(void) bri8 += (255 - brightdepth); CRGB newcolor = CHSV( hue8, sat8, bri8); - fastled_col = col_to_crgb(getPixelColor(i)); + fastled_col = CRGB(SEGMENT.getPixelColor(i)); // TODO nblend(fastled_col, newcolor, 64); - setPixelColor(i, fastled_col.red, fastled_col.green, fastled_col.blue); + SEGMENT.setPixelColor(i, fastled_col.red, fastled_col.green, fastled_col.blue); } SEGENV.step = sPseudotime; SEGENV.aux0 = sHue16; return FRAMETIME; } +static const char _data_FX_MODE_PRIDE_2015[] PROGMEM = "Pride 2015@!,;;;1d"; //eight colored dots, weaving in and out of sync with each other -uint16_t WS2812FX::mode_juggle(void){ - fade_out(SEGMENT.intensity); +uint16_t mode_juggle(void){ + SEGMENT.fade_out(SEGMENT.intensity); CRGB fastled_col; byte dothue = 0; - for ( byte i = 0; i < 8; i++) { + for (int i = 0; i < 8; i++) { uint16_t index = 0 + beatsin88((128 + SEGMENT.speed)*(i + 7), 0, SEGLEN -1); - fastled_col = col_to_crgb(getPixelColor(index)); - fastled_col |= (SEGMENT.palette==0)?CHSV(dothue, 220, 255):ColorFromPalette(currentPalette, dothue, 255); - setPixelColor(index, fastled_col.red, fastled_col.green, fastled_col.blue); + fastled_col = CRGB(SEGMENT.getPixelColor(index)); // TODO + fastled_col |= (SEGMENT.palette==0)?CHSV(dothue, 220, 255):ColorFromPalette(SEGPALETTE, dothue, 255); + SEGMENT.setPixelColor(index, fastled_col.red, fastled_col.green, fastled_col.blue); dothue += 32; } return FRAMETIME; } +static const char _data_FX_MODE_JUGGLE[] PROGMEM = "Juggle@!,Trail;!,!,;!;sx=16,ix=240,1d"; -uint16_t WS2812FX::mode_palette() +uint16_t mode_palette() { uint16_t counter = 0; if (SEGMENT.speed != 0) { - counter = (now * ((SEGMENT.speed >> 3) +1)) & 0xFFFF; + counter = (strip.now * ((SEGMENT.speed >> 3) +1)) & 0xFFFF; counter = counter >> 8; } - bool noWrap = (paletteBlend == 2 || (paletteBlend == 0 && SEGMENT.speed == 0)); - for (uint16_t i = 0; i < SEGLEN; i++) + bool noWrap = (strip.paletteBlend == 2 || (strip.paletteBlend == 0 && SEGMENT.speed == 0)); + for (int i = 0; i < SEGLEN; i++) { uint8_t colorIndex = (i * 255 / SEGLEN) - counter; if (noWrap) colorIndex = map(colorIndex, 0, 255, 0, 240); //cut off blend at palette "end" - setPixelColor(i, color_from_palette(colorIndex, false, true, 255)); + SEGMENT.setPixelColor(i, SEGMENT.color_from_palette(colorIndex, false, true, 255)); } return FRAMETIME; } +static const char _data_FX_MODE_PALETTE[] PROGMEM = "Palette@Cycle speed,;1,2,3;!;1d"; // WLED limitation: Analog Clock overlay will NOT work when Fire2012 is active @@ -1833,7 +1961,7 @@ uint16_t WS2812FX::mode_palette() // // Temperature is in arbitrary units from 0 (cold black) to 255 (white hot). // -// This simulation scales it self a bit depending on NUM_LEDS; it should look +// This simulation scales it self a bit depending on SEGLEN; it should look // "OK" on anywhere from 20 to 100 LEDs without too much tweaking. // // I recommend running this simulation at anywhere from 30-100 frames per second, @@ -1845,52 +1973,67 @@ uint16_t WS2812FX::mode_palette() // There are two main parameters you can play with to control the look and // feel of your fire: COOLING (used in step 1 above) (Speed = COOLING), and SPARKING (used // in step 3 above) (Effect Intensity = Sparking). - - -uint16_t WS2812FX::mode_fire_2012() +uint16_t mode_fire_2012() { - uint32_t it = now >> 5; //div 32 + const uint16_t cols = strip.isMatrix ? SEGMENT.virtualWidth() : 1; + const uint16_t rows = strip.isMatrix ? SEGMENT.virtualHeight() : SEGMENT.virtualLength(); - if (!SEGENV.allocateData(SEGLEN)) return mode_static(); //allocation failed + uint32_t it = strip.now >> 5; //div 32 + //uint16_t q = cols>>2; // a quarter of flames + + if (!SEGENV.allocateData(cols*rows)) return mode_static(); //allocation failed byte* heat = SEGENV.data; - if (it != SEGENV.step) - { - uint8_t ignition = max(7,SEGLEN/10); // ignition area: 10% of segment length or minimum 7 pixels - - // Step 1. Cool down every cell a little - for (uint16_t i = 0; i < SEGLEN; i++) { - uint8_t temp = qsub8(heat[i], random8(0, (((20 + SEGMENT.speed /3) * 10) / SEGLEN) + 2)); - heat[i] = (temp==0 && i 1; k--) { - heat[k] = (heat[k - 1] + (heat[k - 2]<<1) ) / 3; // heat[k-2] multiplied by 2 - } - - // Step 3. Randomly ignite new 'sparks' of heat near the bottom - if (random8() <= SEGMENT.intensity) { - uint8_t y = random8(ignition); - if (y < SEGLEN) heat[y] = qadd8(heat[y], random8(160,255)); - } + if (it != SEGENV.step) { SEGENV.step = it; + uint8_t ignition = max(3,rows/10); // ignition area: 10% of segment length or minimum 3 pixels + + for (int f = 0; f < cols; f++) { + // Step 1. Cool down every cell a little + for (int i = 0; i < rows; i++) { + uint8_t cool = (((20 + SEGMENT.speed/3) * 16) / rows); + /* + // 2D enhancement: cool sides of the flame a bit more + if (cols>5) { + if (f < q) cool = qadd8(cool, 2*(uint16_t)((cool * (q-f))/cols)); // cool segment sides a bit more + if (f > 3*q) cool = qadd8(cool, 2*(uint16_t)((cool * (cols-f))/cols)); // cool segment sides a bit more + } + */ + uint8_t temp = qsub8(heat[i+rows*f], random8(0, cool + 2)); + heat[i+rows*f] = (temp==0 && i 1; k--) { + heat[k+rows*f] = (heat[k+rows*f - 1] + (heat[k+rows*f - 2]<<1) ) / 3; // heat[k-2] multiplied by 2 + } + + // Step 3. Randomly ignite new 'sparks' of heat near the bottom + if (random8() <= SEGMENT.intensity) { + uint8_t y = random8(ignition); + heat[y+rows*f] = qadd8(heat[y+rows*f], random8(160,255)); + } + } } - // Step 4. Map from heat cells to LED colors - for (uint16_t j = 0; j < SEGLEN; j++) { - CRGB color = ColorFromPalette(currentPalette, MIN(heat[j],240), 255, LINEARBLEND); - setPixelColor(j, color.red, color.green, color.blue); + for (int f = 0; f < cols; f++) { + // Step 4. Map from heat cells to LED colors + for (int j = 0; j < rows; j++) { + CRGB color = ColorFromPalette(SEGPALETTE, /*MIN(*/heat[j+rows*f]/*,240)*/, 255, LINEARBLEND); + if (strip.isMatrix) SEGMENT.setPixelColorXY(f, rows -j -1, color); + else SEGMENT.setPixelColor(j, color); + } } return FRAMETIME; } +static const char _data_FX_MODE_FIRE_2012[] PROGMEM = "Fire 2012@Cooling,Spark rate;1,2,3;!;sx=120,ix=64,1d,2d"; // 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. -uint16_t WS2812FX::mode_colorwaves() +uint16_t mode_colorwaves() { uint16_t duration = 10 + SEGMENT.speed; uint16_t sPseudotime = SEGENV.step; @@ -1907,9 +2050,11 @@ uint16_t WS2812FX::mode_colorwaves() sPseudotime += duration * msmultiplier; sHue16 += duration * beatsin88(400, 5, 9); uint16_t brightnesstheta16 = sPseudotime; - CRGB fastled_col; + //CRGB fastled_col; - for ( uint16_t i = 0 ; i < SEGLEN; i++) { + if (SEGENV.call == 0) SEGMENT.fill(BLACK); + + for (int i = 0 ; i < SEGLEN; i++) { hue16 += hueinc16; uint8_t hue8 = hue16 >> 8; uint16_t h16_128 = hue16 >> 7; @@ -1926,149 +2071,157 @@ uint16_t WS2812FX::mode_colorwaves() uint8_t bri8 = (uint32_t)(((uint32_t)bri16) * brightdepth) / 65536; bri8 += (255 - brightdepth); - CRGB newcolor = ColorFromPalette(currentPalette, hue8, bri8); - fastled_col = col_to_crgb(getPixelColor(i)); + //CRGB newcolor = ColorFromPalette(SEGPALETTE, hue8, bri8); + //fastled_col = SEGMENT.getPixelColor(i); // TODO - nblend(fastled_col, newcolor, 128); - setPixelColor(i, fastled_col.red, fastled_col.green, fastled_col.blue); + //nblend(fastled_col, newcolor, 128); + //SEGMENT.setPixelColor(i, fastled_col.red, fastled_col.green, fastled_col.blue); + SEGMENT.blendPixelColor(i, SEGMENT.color_from_palette(hue8, false, PALETTE_SOLID_WRAP, 0, bri8), 128); // 50/50 mix } SEGENV.step = sPseudotime; SEGENV.aux0 = sHue16; return FRAMETIME; } +static const char _data_FX_MODE_COLORWAVES[] PROGMEM = "Colorwaves@!,!;!,!,!;!;1d"; // colored stripes pulsing at a defined Beats-Per-Minute (BPM) -uint16_t WS2812FX::mode_bpm() +uint16_t mode_bpm() { - CRGB fastled_col; - uint32_t stp = (now / 20) & 0xFF; + //CRGB fastled_col; + uint32_t stp = (strip.now / 20) & 0xFF; uint8_t beat = beatsin8(SEGMENT.speed, 64, 255); - for (uint16_t i = 0; i < SEGLEN; i++) { - fastled_col = ColorFromPalette(currentPalette, stp + (i * 2), beat - stp + (i * 10)); - setPixelColor(i, fastled_col.red, fastled_col.green, fastled_col.blue); + for (int i = 0; i < SEGLEN; i++) { + //fastled_col = ColorFromPalette(SEGPALETTE, stp + (i * 2), beat - stp + (i * 10)); + //SEGMENT.setPixelColor(i, fastled_col.red, fastled_col.green, fastled_col.blue); + SEGMENT.setPixelColor(i, SEGMENT.color_from_palette(stp + (i * 2), false, PALETTE_SOLID_WRAP, 0, beat - stp + (i * 10))); } return FRAMETIME; } +static const char _data_FX_MODE_BPM[] PROGMEM = "Bpm@!,;1,2,3;!;sx=64,1d"; -uint16_t WS2812FX::mode_fillnoise8() +uint16_t mode_fillnoise8() { if (SEGENV.call == 0) SEGENV.step = random16(12345); - CRGB fastled_col; - for (uint16_t i = 0; i < SEGLEN; i++) { + //CRGB fastled_col; + for (int i = 0; i < SEGLEN; i++) { uint8_t index = inoise8(i * SEGLEN, SEGENV.step + i * SEGLEN); - fastled_col = ColorFromPalette(currentPalette, index, 255, LINEARBLEND); - setPixelColor(i, fastled_col.red, fastled_col.green, fastled_col.blue); + //fastled_col = ColorFromPalette(SEGPALETTE, index, 255, LINEARBLEND); + //SEGMENT.setPixelColor(i, fastled_col.red, fastled_col.green, fastled_col.blue); + SEGMENT.setPixelColor(i, SEGMENT.color_from_palette(index, false, PALETTE_SOLID_WRAP, 0)); } SEGENV.step += beatsin8(SEGMENT.speed, 1, 6); //10,1,4 return FRAMETIME; } +static const char _data_FX_MODE_FILLNOISE8[] PROGMEM = "Fill Noise@!,!;!,!,!;!;1d"; + -uint16_t WS2812FX::mode_noise16_1() +uint16_t mode_noise16_1() { - uint16_t scale = 320; // the "zoom factor" for the noise - CRGB fastled_col; + uint16_t scale = 320; // the "zoom factor" for the noise + //CRGB fastled_col; SEGENV.step += (1 + SEGMENT.speed/16); - for (uint16_t i = 0; i < SEGLEN; i++) { - - uint16_t shift_x = beatsin8(11); // the x position of the noise field swings @ 17 bpm - uint16_t shift_y = SEGENV.step/42; // the y position becomes slowly incremented - - + for (int i = 0; i < SEGLEN; i++) { + uint16_t shift_x = beatsin8(11); // the x position of the noise field swings @ 17 bpm + uint16_t shift_y = SEGENV.step/42; // the y position becomes slowly incremented uint16_t real_x = (i + shift_x) * scale; // the x position of the noise field swings @ 17 bpm uint16_t real_y = (i + shift_y) * scale; // the y position becomes slowly incremented - uint32_t real_z = SEGENV.step; // the z position becomes quickly incremented - - uint8_t noise = inoise16(real_x, real_y, real_z) >> 8; // get the noise data and scale it down - - uint8_t index = sin8(noise * 3); // map LED color based on noise data + uint32_t real_z = SEGENV.step; // the z position becomes quickly incremented + uint8_t noise = inoise16(real_x, real_y, real_z) >> 8; // get the noise data and scale it down + uint8_t index = sin8(noise * 3); // map LED color based on noise data - fastled_col = ColorFromPalette(currentPalette, index, 255, LINEARBLEND); // With that value, look up the 8 bit colour palette value and assign it to the current LED. - setPixelColor(i, fastled_col.red, fastled_col.green, fastled_col.blue); + //fastled_col = ColorFromPalette(SEGPALETTE, index, 255, LINEARBLEND); // With that value, look up the 8 bit colour palette value and assign it to the current LED. + //SEGMENT.setPixelColor(i, fastled_col.red, fastled_col.green, fastled_col.blue); + SEGMENT.setPixelColor(i, SEGMENT.color_from_palette(index, false, PALETTE_SOLID_WRAP, 0)); } return FRAMETIME; } +static const char _data_FX_MODE_NOISE16_1[] PROGMEM = "Noise 1@!,!;!,!,!;!;1d"; -uint16_t WS2812FX::mode_noise16_2() +uint16_t mode_noise16_2() { - uint16_t scale = 1000; // the "zoom factor" for the noise - CRGB fastled_col; + uint16_t scale = 1000; // the "zoom factor" for the noise + //CRGB fastled_col; SEGENV.step += (1 + (SEGMENT.speed >> 1)); - for (uint16_t i = 0; i < SEGLEN; i++) { - - uint16_t shift_x = SEGENV.step >> 6; // x as a function of time - - uint32_t real_x = (i + shift_x) * scale; // calculate the coordinates within the noise field - - uint8_t noise = inoise16(real_x, 0, 4223) >> 8; // get the noise data and scale it down - - uint8_t index = sin8(noise * 3); // map led color based on noise data + for (int i = 0; i < SEGLEN; i++) { + uint16_t shift_x = SEGENV.step >> 6; // x as a function of time + uint32_t real_x = (i + shift_x) * scale; // calculate the coordinates within the noise field + uint8_t noise = inoise16(real_x, 0, 4223) >> 8; // get the noise data and scale it down + uint8_t index = sin8(noise * 3); // map led color based on noise data - fastled_col = ColorFromPalette(currentPalette, index, noise, LINEARBLEND); // With that value, look up the 8 bit colour palette value and assign it to the current LED. - setPixelColor(i, fastled_col.red, fastled_col.green, fastled_col.blue); + //fastled_col = ColorFromPalette(SEGPALETTE, index, noise, LINEARBLEND); // With that value, look up the 8 bit colour palette value and assign it to the current LED. + //SEGMENT.setPixelColor(i, fastled_col.red, fastled_col.green, fastled_col.blue); + SEGMENT.setPixelColor(i, SEGMENT.color_from_palette(index, false, PALETTE_SOLID_WRAP, 0, noise)); } return FRAMETIME; } +static const char _data_FX_MODE_NOISE16_2[] PROGMEM = "Noise 2@!,!;!,!,!;!;1d"; -uint16_t WS2812FX::mode_noise16_3() +uint16_t mode_noise16_3() { uint16_t scale = 800; // the "zoom factor" for the noise - CRGB fastled_col; + //CRGB fastled_col; SEGENV.step += (1 + SEGMENT.speed); - for (uint16_t i = 0; i < SEGLEN; i++) { - + for (int i = 0; i < SEGLEN; i++) { uint16_t shift_x = 4223; // no movement along x and y uint16_t shift_y = 1234; - uint32_t real_x = (i + shift_x) * scale; // calculate the coordinates within the noise field uint32_t real_y = (i + shift_y) * scale; // based on the precalculated positions uint32_t real_z = SEGENV.step*8; - uint8_t noise = inoise16(real_x, real_y, real_z) >> 8; // get the noise data and scale it down - uint8_t index = sin8(noise * 3); // map led color based on noise data - fastled_col = ColorFromPalette(currentPalette, index, noise, LINEARBLEND); // With that value, look up the 8 bit colour palette value and assign it to the current LED. - setPixelColor(i, fastled_col.red, fastled_col.green, fastled_col.blue); + //fastled_col = ColorFromPalette(SEGPALETTE, index, noise, LINEARBLEND); // With that value, look up the 8 bit colour palette value and assign it to the current LED. + //SEGMENT.setPixelColor(i, fastled_col.red, fastled_col.green, fastled_col.blue); + SEGMENT.setPixelColor(i, SEGMENT.color_from_palette(index, false, PALETTE_SOLID_WRAP, 0, noise)); } return FRAMETIME; } +static const char _data_FX_MODE_NOISE16_3[] PROGMEM = "Noise 3@!,!;!,!,!;!;1d"; //https://github.com/aykevl/ledstrip-spark/blob/master/ledstrip.ino -uint16_t WS2812FX::mode_noise16_4() +uint16_t mode_noise16_4() { - CRGB fastled_col; - uint32_t stp = (now * SEGMENT.speed) >> 7; - for (uint16_t i = 0; i < SEGLEN; i++) { + //CRGB fastled_col; + uint32_t stp = (strip.now * SEGMENT.speed) >> 7; + for (int i = 0; i < SEGLEN; i++) { int16_t index = inoise16(uint32_t(i) << 12, stp); - fastled_col = ColorFromPalette(currentPalette, index); - setPixelColor(i, fastled_col.red, fastled_col.green, fastled_col.blue); + //fastled_col = ColorFromPalette(SEGPALETTE, index); + //SEGMENT.setPixelColor(i, fastled_col.red, fastled_col.green, fastled_col.blue); + SEGMENT.setPixelColor(i, SEGMENT.color_from_palette(index, false, PALETTE_SOLID_WRAP, 0)); } return FRAMETIME; } +static const char _data_FX_MODE_NOISE16_4[] PROGMEM = "Noise 4@!,!;!,!,!;!;1d"; //based on https://gist.github.com/kriegsman/5408ecd397744ba0393e -uint16_t WS2812FX::mode_colortwinkle() +uint16_t mode_colortwinkle() { - uint16_t dataSize = (SEGLEN+7) >> 3; //1 bit per LED + const uint16_t cols = strip.isMatrix ? SEGMENT.virtualWidth() : 1; + const uint16_t rows = strip.isMatrix ? SEGMENT.virtualHeight() : SEGMENT.virtualLength(); + + uint16_t dataSize = (cols*rows+7) >> 3; //1 bit per LED if (!SEGENV.allocateData(dataSize)) return mode_static(); //allocation failed CRGB fastled_col, prev; - fract8 fadeUpAmount = _brightness>28 ? 8 + (SEGMENT.speed>>2) : 68-_brightness, fadeDownAmount = _brightness>28 ? 8 + (SEGMENT.speed>>3) : 68-_brightness; - for (uint16_t i = 0; i < SEGLEN; i++) { - fastled_col = col_to_crgb(getPixelColor(i)); + fract8 fadeUpAmount = strip.getBrightness()>28 ? 8 + (SEGMENT.speed>>2) : 68-strip.getBrightness(); + fract8 fadeDownAmount = strip.getBrightness()>28 ? 8 + (SEGMENT.speed>>3) : 68-strip.getBrightness(); + + for (int i = 0; i < rows*cols; i++) { + uint16_t j = i % cols, k = i / cols; + fastled_col = CRGB(strip.isMatrix ? SEGMENT.getPixelColorXY(j, k) : SEGMENT.getPixelColor(i)); // TODO prev = fastled_col; uint16_t index = i >> 3; uint8_t bitNum = i & 0x07; @@ -2082,28 +2235,36 @@ uint16_t WS2812FX::mode_colortwinkle() if (fastled_col.red == 255 || fastled_col.green == 255 || fastled_col.blue == 255) { bitWrite(SEGENV.data[index], bitNum, false); } - setPixelColor(i, fastled_col.red, fastled_col.green, fastled_col.blue); - if (col_to_crgb(getPixelColor(i)) == prev) { //fix "stuck" pixels + if (strip.isMatrix) SEGMENT.setPixelColorXY(j, k, fastled_col); + else SEGMENT.setPixelColor(i, fastled_col.red, fastled_col.green, fastled_col.blue); + + uint32_t col = strip.isMatrix ? SEGMENT.getPixelColorXY(j, k) : SEGMENT.getPixelColor(i); // TODO + if (CRGB(col) == prev) { //fix "stuck" pixels fastled_col += fastled_col; - setPixelColor(i, fastled_col.red, fastled_col.green, fastled_col.blue); + if (strip.isMatrix) SEGMENT.setPixelColorXY(j, k, fastled_col); + else SEGMENT.setPixelColor(i, fastled_col.red, fastled_col.green, fastled_col.blue); } } else { fastled_col.nscale8(255 - fadeDownAmount); - setPixelColor(i, fastled_col.red, fastled_col.green, fastled_col.blue); + if (strip.isMatrix) SEGMENT.setPixelColorXY(j, k, fastled_col); + else SEGMENT.setPixelColor(i, fastled_col.red, fastled_col.green, fastled_col.blue); } } - for (uint16_t j = 0; j <= SEGLEN / 50; j++) { + for (int j = 0; j <= rows*cols / 50; j++) { if (random8() <= SEGMENT.intensity) { - for (uint8_t times = 0; times < 5; times++) { //attempt to spawn a new pixel 5 times - int i = random16(SEGLEN); - if (getPixelColor(i) == 0) { - fastled_col = ColorFromPalette(currentPalette, random8(), 64, NOBLEND); + for (size_t times = 0; times < 5; times++) { //attempt to spawn a new pixel 5 times + uint16_t i = random16(rows*cols); + uint16_t j = i % cols, k = i / cols; + uint32_t col = strip.isMatrix ? SEGMENT.getPixelColorXY(j, k) : SEGMENT.getPixelColor(i); // TODO + if (col == 0) { + fastled_col = ColorFromPalette(SEGPALETTE, random8(), 64, NOBLEND); uint16_t index = i >> 3; uint8_t bitNum = i & 0x07; bitWrite(SEGENV.data[index], bitNum, true); - setPixelColor(i, fastled_col.red, fastled_col.green, fastled_col.blue); + if (strip.isMatrix) SEGMENT.setPixelColorXY(j, k, fastled_col); + else SEGMENT.setPixelColor(i, fastled_col.red, fastled_col.green, fastled_col.blue); break; //only spawn 1 new pixel per frame per 50 LEDs } } @@ -2111,68 +2272,72 @@ uint16_t WS2812FX::mode_colortwinkle() } return FRAMETIME_FIXED; } +static const char _data_FX_MODE_COLORTWINKLE[] PROGMEM = "Colortwinkles@Fade speed,Spawn speed;1,2,3;!;1d,2d"; //pixels //Calm effect, like a lake at night -uint16_t WS2812FX::mode_lake() { +uint16_t mode_lake() { uint8_t sp = SEGMENT.speed/10; int wave1 = beatsin8(sp +2, -64,64); int wave2 = beatsin8(sp +1, -64,64); uint8_t wave3 = beatsin8(sp +2, 0,80); - CRGB fastled_col; + //CRGB fastled_col; - for (uint16_t i = 0; i < SEGLEN; i++) + for (int i = 0; i < SEGLEN; i++) { int index = cos8((i*15)+ wave1)/2 + cubicwave8((i*23)+ wave2)/2; uint8_t lum = (index > wave3) ? index - wave3 : 0; - fastled_col = ColorFromPalette(currentPalette, map(index,0,255,0,240), lum, LINEARBLEND); - setPixelColor(i, fastled_col.red, fastled_col.green, fastled_col.blue); + //fastled_col = ColorFromPalette(SEGPALETTE, map(index,0,255,0,240), lum, LINEARBLEND); + //SEGMENT.setPixelColor(i, fastled_col.red, fastled_col.green, fastled_col.blue); + SEGMENT.setPixelColor(i, SEGMENT.color_from_palette(index, false, false, 0, lum)); } return FRAMETIME; } +static const char _data_FX_MODE_LAKE[] PROGMEM = "Lake@!,;1,2,3;!;1d"; // meteor effect // send a meteor from begining to to the end of the strip with a trail that randomly decays. // adapted from https://www.tweaking4all.com/hardware/arduino/adruino-led-strip-effects/#LEDStripEffectMeteorRain -uint16_t WS2812FX::mode_meteor() { +uint16_t mode_meteor() { if (!SEGENV.allocateData(SEGLEN)) return mode_static(); //allocation failed byte* trail = SEGENV.data; byte meteorSize= 1+ SEGLEN / 10; - uint16_t counter = now * ((SEGMENT.speed >> 2) +8); + uint16_t counter = strip.now * ((SEGMENT.speed >> 2) +8); uint16_t in = counter * SEGLEN >> 16; // fade all leds to colors[1] in LEDs one step - for (uint16_t i = 0; i < SEGLEN; i++) { + for (int i = 0; i < SEGLEN; i++) { if (random8() <= 255 - SEGMENT.intensity) { byte meteorTrailDecay = 128 + random8(127); trail[i] = scale8(trail[i], meteorTrailDecay); - setPixelColor(i, color_from_palette(trail[i], false, true, 255)); + SEGMENT.setPixelColor(i, SEGMENT.color_from_palette(trail[i], false, true, 255)); } } // draw meteor - for(int j = 0; j < meteorSize; j++) { + for (int j = 0; j < meteorSize; j++) { uint16_t index = in + j; if(index >= SEGLEN) { index = (in + j - SEGLEN); } trail[index] = 240; - setPixelColor(index, color_from_palette(trail[index], false, true, 255)); + SEGMENT.setPixelColor(index, SEGMENT.color_from_palette(trail[index], false, true, 255)); } return FRAMETIME; } +static const char _data_FX_MODE_METEOR[] PROGMEM = "Meteor@!,Trail length;!,!,;!;1d"; // smooth meteor effect // send a meteor from begining to to the end of the strip with a trail that randomly decays. // adapted from https://www.tweaking4all.com/hardware/arduino/adruino-led-strip-effects/#LEDStripEffectMeteorRain -uint16_t WS2812FX::mode_meteor_smooth() { +uint16_t mode_meteor_smooth() { if (!SEGENV.allocateData(SEGLEN)) return mode_static(); //allocation failed byte* trail = SEGENV.data; @@ -2181,34 +2346,35 @@ uint16_t WS2812FX::mode_meteor_smooth() { uint16_t in = map((SEGENV.step >> 6 & 0xFF), 0, 255, 0, SEGLEN -1); // fade all leds to colors[1] in LEDs one step - for (uint16_t i = 0; i < SEGLEN; i++) { + for (int i = 0; i < SEGLEN; i++) { if (trail[i] != 0 && random8() <= 255 - SEGMENT.intensity) { int change = 3 - random8(12); //change each time between -8 and +3 trail[i] += change; if (trail[i] > 245) trail[i] = 0; if (trail[i] > 240) trail[i] = 240; - setPixelColor(i, color_from_palette(trail[i], false, true, 255)); + SEGMENT.setPixelColor(i, SEGMENT.color_from_palette(trail[i], false, true, 255)); } } // draw meteor - for(int j = 0; j < meteorSize; j++) { + for (int j = 0; j < meteorSize; j++) { uint16_t index = in + j; if(in + j >= SEGLEN) { index = (in + j - SEGLEN); } - setPixelColor(index, color_blend(getPixelColor(index), color_from_palette(240, false, true, 255), 48)); + SEGMENT.setPixelColor(index, color_blend(SEGMENT.getPixelColor(index), SEGMENT.color_from_palette(240, false, true, 255), 48)); trail[index] = 240; } SEGENV.step += SEGMENT.speed +1; return FRAMETIME; } +static const char _data_FX_MODE_METEOR_SMOOTH[] PROGMEM = "Meteor Smooth@!,Trail length;!,!,;!;1d"; //Railway Crossing / Christmas Fairy lights -uint16_t WS2812FX::mode_railway() +uint16_t mode_railway() { uint16_t dur = 40 + (255 - SEGMENT.speed) * 10; uint16_t rampdur = (dur * SEGMENT.intensity) >> 8; @@ -2225,17 +2391,18 @@ uint16_t WS2812FX::mode_railway() if (p0 < 255) pos = p0; } if (SEGENV.aux0) pos = 255 - pos; - for (uint16_t i = 0; i < SEGLEN; i += 2) + for (int i = 0; i < SEGLEN; i += 2) { - setPixelColor(i, color_from_palette(255 - pos, false, false, 255)); + SEGMENT.setPixelColor(i, SEGMENT.color_from_palette(255 - pos, false, false, 255)); if (i < SEGLEN -1) { - setPixelColor(i + 1, color_from_palette(pos, false, false, 255)); + SEGMENT.setPixelColor(i + 1, SEGMENT.color_from_palette(pos, false, false, 255)); } } SEGENV.step += FRAMETIME; return FRAMETIME; } +static const char _data_FX_MODE_RAILWAY[] PROGMEM = "Railway"; //Water ripple @@ -2254,7 +2421,7 @@ typedef struct Ripple { #else #define MAX_RIPPLES 100 #endif -uint16_t WS2812FX::ripple_base(bool rainbow) +uint16_t ripple_base(bool rainbow) { uint16_t maxRipples = min(1 + (SEGLEN >> 2), MAX_RIPPLES); // 56 max for 16 segment ESP8266 uint16_t dataSize = sizeof(ripple) * maxRipples; @@ -2277,20 +2444,20 @@ uint16_t WS2812FX::ripple_base(bool rainbow) } else { SEGENV.aux0--; } - fill(color_blend(color_wheel(SEGENV.aux0),BLACK,235)); + SEGMENT.fill(color_blend(SEGMENT.color_wheel(SEGENV.aux0),BLACK,235)); } else { - fill(SEGCOLOR(1)); + SEGMENT.fill(SEGCOLOR(1)); } //draw wave - for (uint16_t i = 0; i < maxRipples; i++) + for (int i = 0; i < maxRipples; i++) { uint16_t ripplestate = ripples[i].state; if (ripplestate) { uint8_t rippledecay = (SEGMENT.speed >> 4) +1; //faster decay if faster propagation uint16_t rippleorigin = ripples[i].pos; - uint32_t col = color_from_palette(ripples[i].color, false, false, 255); + uint32_t col = SEGMENT.color_from_palette(ripples[i].color, false, false, 255); uint16_t propagation = ((ripplestate/rippledecay -1) * SEGMENT.speed); int16_t propI = propagation >> 8; uint8_t propF = propagation & 0xFF; @@ -2302,12 +2469,12 @@ uint16_t WS2812FX::ripple_base(bool rainbow) uint8_t mag = scale8(cubicwave8((propF>>2)+(v-left)*64), amp); if (v < SEGLEN && v >= 0) { - setPixelColor(v, color_blend(getPixelColor(v), col, mag)); + SEGMENT.setPixelColor(v, color_blend(SEGMENT.getPixelColor(v), col, mag)); // TODO } int16_t w = left + propI*2 + 3 -(v-left); if (w < SEGLEN && w >= 0) { - setPixelColor(w, color_blend(getPixelColor(w), col, mag)); + SEGMENT.setPixelColor(w, color_blend(SEGMENT.getPixelColor(w), col, mag)); // TODO } } ripplestate += rippledecay; @@ -2326,14 +2493,17 @@ uint16_t WS2812FX::ripple_base(bool rainbow) } #undef MAX_RIPPLES -uint16_t WS2812FX::mode_ripple(void) { + +uint16_t mode_ripple(void) { return ripple_base(false); } +static const char _data_FX_MODE_RIPPLE[] PROGMEM = "Ripple"; + -uint16_t WS2812FX::mode_ripple_rainbow(void) { +uint16_t mode_ripple_rainbow(void) { return ripple_base(true); } - +static const char _data_FX_MODE_RIPPLE_RAINBOW[] PROGMEM = "Ripple Rainbow"; // TwinkleFOX by Mark Kriegsman: https://gist.github.com/kriegsman/756ea6dcae8e30845b5a @@ -2346,7 +2516,7 @@ uint16_t WS2812FX::mode_ripple_rainbow(void) { // incandescent bulbs change color as they get dim down. #define COOL_LIKE_INCANDESCENT 1 -CRGB IRAM_ATTR WS2812FX::twinklefox_one_twinkle(uint32_t ms, uint8_t salt, bool cat) +CRGB twinklefox_one_twinkle(uint32_t ms, uint8_t salt, bool cat) { // Overall twinkle speed (changed) uint16_t ticks = ms / SEGENV.aux0; @@ -2383,7 +2553,7 @@ CRGB IRAM_ATTR WS2812FX::twinklefox_one_twinkle(uint32_t ms, uint8_t salt, bool uint8_t hue = slowcycle8 - salt; CRGB c; if (bright > 0) { - c = ColorFromPalette(currentPalette, hue, bright, NOBLEND); + c = ColorFromPalette(SEGPALETTE, hue, bright, NOBLEND); if(COOL_LIKE_INCANDESCENT == 1) { // This code takes a pixel, and if its in the 'fading down' // part of the cycle, it adjusts the color a little bit like the @@ -2406,7 +2576,7 @@ CRGB IRAM_ATTR WS2812FX::twinklefox_one_twinkle(uint32_t ms, uint8_t salt, bool // "CalculateOneTwinkle" on each pixel. It then displays // either the twinkle color of the background color, // whichever is brighter. -uint16_t WS2812FX::twinklefox_base(bool cat) +uint16_t twinklefox_base(bool cat) { // "PRNG16" is the pseudorandom number generator // It MUST be reset to the same starting value each time @@ -2419,8 +2589,7 @@ uint16_t WS2812FX::twinklefox_base(bool cat) else SEGENV.aux0 = 22 + ((100 - SEGMENT.speed) >> 1); // Set up the background color, "bg". - CRGB bg; - bg = col_to_crgb(SEGCOLOR(1)); + CRGB bg = CRGB(SEGCOLOR(1)); uint8_t bglight = bg.getAverageLight(); if (bglight > 64) { bg.nscale8_video(16); // very bright, so scale to 1/16th @@ -2432,14 +2601,14 @@ uint16_t WS2812FX::twinklefox_base(bool cat) uint8_t backgroundBrightness = bg.getAverageLight(); - for (uint16_t i = 0; i < SEGLEN; i++) { + for (int i = 0; i < SEGLEN; 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)((now * myspeedmultiplierQ5_3) >> 3) + myclockoffset16; + uint32_t myclock30 = (uint32_t)((strip.now * 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 @@ -2452,41 +2621,45 @@ uint16_t WS2812FX::twinklefox_base(bool cat) if (deltabright >= 32 || (!bg)) { // If the new pixel is significantly brighter than the background color, // use the new color. - setPixelColor(i, c.red, c.green, c.blue); + SEGMENT.setPixelColor(i, c.red, c.green, c.blue); } 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 - setPixelColor(i, color_blend(crgb_to_col(bg), crgb_to_col(c), deltabright * 8)); + SEGMENT.setPixelColor(i, color_blend(RGBW32(bg.r,bg.g,bg.b,0), RGBW32(c.r,c.g,c.b,0), deltabright * 8)); } else { // if the new pixel is not at all brighter than the background color, // just use the background color. - setPixelColor(i, bg.r, bg.g, bg.b); + SEGMENT.setPixelColor(i, bg.r, bg.g, bg.b); } } return FRAMETIME; } -uint16_t WS2812FX::mode_twinklefox() + +uint16_t mode_twinklefox() { return twinklefox_base(false); } +static const char _data_FX_MODE_TWINKLEFOX[] PROGMEM = "Twinklefox"; + -uint16_t WS2812FX::mode_twinklecat() +uint16_t mode_twinklecat() { return twinklefox_base(true); } +static const char _data_FX_MODE_TWINKLECAT[] PROGMEM = "Twinklecat"; //inspired by https://www.tweaking4all.com/hardware/arduino/adruino-led-strip-effects/#LEDStripEffectBlinkingHalloweenEyes -#define HALLOWEEN_EYE_SPACE 3 -#define HALLOWEEN_EYE_WIDTH 1 +#define HALLOWEEN_EYE_SPACE (2*MAX(1,SEGLEN>>5)) +#define HALLOWEEN_EYE_WIDTH MAX(1,SEGLEN>>5) -uint16_t WS2812FX::mode_halloween_eyes() +uint16_t mode_halloween_eyes() { uint16_t eyeLength = (2*HALLOWEEN_EYE_WIDTH) + HALLOWEEN_EYE_SPACE; if (eyeLength > SEGLEN) return mode_static(); //bail if segment too short - fill(SEGCOLOR(1)); //fill background + SEGMENT.fill(SEGCOLOR(1)); //fill background uint8_t state = SEGENV.aux1 >> 8; uint16_t stateTime = SEGENV.call; @@ -2495,6 +2668,7 @@ uint16_t WS2812FX::mode_halloween_eyes() if (state == 0) { //spawn eyes SEGENV.aux0 = random16(0, SEGLEN - eyeLength); //start pos SEGENV.aux1 = random8(); //color + if (strip.isMatrix) SEGMENT.offset = random16(SEGMENT.virtualHeight()-1); // a hack: reuse offset since it is not used in matrices state = 1; } @@ -2502,30 +2676,32 @@ uint16_t WS2812FX::mode_halloween_eyes() uint16_t startPos = SEGENV.aux0; uint16_t start2ndEye = startPos + HALLOWEEN_EYE_WIDTH + HALLOWEEN_EYE_SPACE; - uint32_t fadestage = (now - SEGENV.step)*255 / stateTime; + uint32_t fadestage = (strip.now - SEGENV.step)*255 / stateTime; if (fadestage > 255) fadestage = 255; - uint32_t c = color_blend(color_from_palette(SEGENV.aux1 & 0xFF, false, false, 0), SEGCOLOR(1), fadestage); + uint32_t c = color_blend(SEGMENT.color_from_palette(SEGENV.aux1 & 0xFF, false, false, 0), SEGCOLOR(1), fadestage); - for (uint16_t i = 0; i < HALLOWEEN_EYE_WIDTH; i++) - { - setPixelColor(startPos + i, c); - setPixelColor(start2ndEye + i, c); + for (int i = 0; i < HALLOWEEN_EYE_WIDTH; i++) { + if (strip.isMatrix) { + SEGMENT.setPixelColorXY(startPos + i, SEGMENT.offset, c); + SEGMENT.setPixelColorXY(start2ndEye + i, SEGMENT.offset, c); + } else { + SEGMENT.setPixelColor(startPos + i, c); + SEGMENT.setPixelColor(start2ndEye + i, c); + } } } - if (now - SEGENV.step > stateTime) - { + if (strip.now - SEGENV.step > stateTime) { state++; if (state > 2) state = 0; - if (state < 2) - { + if (state < 2) { stateTime = 100 + (255 - SEGMENT.intensity)*10; //eye fade time } else { uint16_t eyeOffTimeBase = (255 - SEGMENT.speed)*10; stateTime = eyeOffTimeBase + random16(eyeOffTimeBase); } - SEGENV.step = now; + SEGENV.step = strip.now; SEGENV.call = stateTime; } @@ -2533,18 +2709,19 @@ uint16_t WS2812FX::mode_halloween_eyes() return FRAMETIME; } +static const char _data_FX_MODE_HALLOWEEN_EYES[] PROGMEM = "Halloween Eyes@Duration,Eye fade time;!,!,;!;1d,2d"; //Speed slider sets amount of LEDs lit, intensity sets unlit -uint16_t WS2812FX::mode_static_pattern() +uint16_t mode_static_pattern() { uint16_t lit = 1 + SEGMENT.speed; uint16_t unlit = 1 + SEGMENT.intensity; bool drawingLit = true; uint16_t cnt = 0; - for (uint16_t i = 0; i < SEGLEN; i++) { - setPixelColor(i, (drawingLit) ? color_from_palette(i, true, PALETTE_SOLID_WRAP, 0) : SEGCOLOR(1)); + for (int i = 0; i < SEGLEN; i++) { + SEGMENT.setPixelColor(i, (drawingLit) ? SEGMENT.color_from_palette(i, true, PALETTE_SOLID_WRAP, 0) : SEGCOLOR(1)); cnt++; if (cnt >= ((drawingLit) ? lit : unlit)) { cnt = 0; @@ -2554,20 +2731,22 @@ uint16_t WS2812FX::mode_static_pattern() return FRAMETIME; } +static const char _data_FX_MODE_STATIC_PATTERN[] PROGMEM = "Solid Pattern@Fg size,Bg size;Fg,Bg,;!;pal=0,1d"; -uint16_t WS2812FX::mode_tri_static_pattern() + +uint16_t mode_tri_static_pattern() { uint8_t segSize = (SEGMENT.intensity >> 5) +1; uint8_t currSeg = 0; uint16_t currSegCount = 0; - for (uint16_t i = 0; i < SEGLEN; i++) { + for (int i = 0; i < SEGLEN; i++) { if ( currSeg % 3 == 0 ) { - setPixelColor(i, SEGCOLOR(0)); + SEGMENT.setPixelColor(i, SEGCOLOR(0)); } else if( currSeg % 3 == 1) { - setPixelColor(i, SEGCOLOR(1)); + SEGMENT.setPixelColor(i, SEGCOLOR(1)); } else { - setPixelColor(i, (SEGCOLOR(2) > 0 ? SEGCOLOR(2) : WHITE)); + SEGMENT.setPixelColor(i, (SEGCOLOR(2) > 0 ? SEGCOLOR(2) : WHITE)); } currSegCount += 1; if (currSegCount >= segSize) { @@ -2578,27 +2757,28 @@ uint16_t WS2812FX::mode_tri_static_pattern() return FRAMETIME; } +static const char _data_FX_MODE_TRI_STATIC_PATTERN[] PROGMEM = "Solid Pattern Tri@,Size;1,2,3;!;1d,pal=0"; -uint16_t WS2812FX::spots_base(uint16_t threshold) +uint16_t spots_base(uint16_t threshold) { - fill(SEGCOLOR(1)); + SEGMENT.fill(SEGCOLOR(1)); uint16_t maxZones = SEGLEN >> 2; uint16_t zones = 1 + ((SEGMENT.intensity * maxZones) >> 8); uint16_t zoneLen = SEGLEN / zones; uint16_t offset = (SEGLEN - zones * zoneLen) >> 1; - for (uint16_t z = 0; z < zones; z++) + for (int z = 0; z < zones; z++) { uint16_t pos = offset + z * zoneLen; - for (uint16_t i = 0; i < zoneLen; i++) + for (int i = 0; i < zoneLen; i++) { uint16_t wave = triwave16((i * 0xFFFF) / zoneLen); if (wave > threshold) { uint16_t index = 0 + pos + i; uint8_t s = (wave - threshold)*255 / (0xFFFF - threshold); - setPixelColor(index, color_blend(color_from_palette(index, true, PALETTE_SOLID_WRAP, 0), SEGCOLOR(1), 255-s)); + SEGMENT.setPixelColor(index, color_blend(SEGMENT.color_from_palette(index, true, PALETTE_SOLID_WRAP, 0), SEGCOLOR(1), 255-s)); } } } @@ -2608,20 +2788,22 @@ uint16_t WS2812FX::spots_base(uint16_t threshold) //Intensity slider sets number of "lights", speed sets LEDs per light -uint16_t WS2812FX::mode_spots() +uint16_t mode_spots() { return spots_base((255 - SEGMENT.speed) << 8); } +static const char _data_FX_MODE_SPOTS[] PROGMEM = "Spots@Spread,Width;!,!,;!;1d"; //Intensity slider sets number of "lights", LEDs per light fade in and out -uint16_t WS2812FX::mode_spots_fade() +uint16_t mode_spots_fade() { - uint16_t counter = now * ((SEGMENT.speed >> 2) +8); + uint16_t counter = strip.now * ((SEGMENT.speed >> 2) +8); uint16_t t = triwave16(counter); uint16_t tr = (t >> 1) + (t >> 2); return spots_base(tr); } +static const char _data_FX_MODE_SPOTS_FADE[] PROGMEM = "Spots Fade@Spread,Width;!,!,;!;1d"; //each needs 12 bytes @@ -2634,7 +2816,7 @@ typedef struct Ball { /* * Bouncing Balls Effect */ -uint16_t WS2812FX::mode_bouncing_balls(void) { +uint16_t mode_bouncing_balls(void) { //allocate segment data uint16_t maxNumBalls = 16; uint16_t dataSize = sizeof(ball) * maxNumBalls; @@ -2652,13 +2834,13 @@ uint16_t WS2812FX::mode_bouncing_balls(void) { unsigned long time = millis(); if (SEGENV.call == 0) { - for (uint8_t i = 0; i < maxNumBalls; i++) balls[i].lastBounceTime = time; + for (size_t i = 0; i < maxNumBalls; i++) balls[i].lastBounceTime = time; } bool hasCol2 = SEGCOLOR(2); - fill(hasCol2 ? BLACK : SEGCOLOR(1)); + SEGMENT.fill(hasCol2 ? BLACK : SEGCOLOR(1)); - for (uint8_t i = 0; i < numBalls; i++) { + for (size_t i = 0; i < numBalls; i++) { float timeSinceLastBounce = (time - balls[i].lastBounceTime)/((255-SEGMENT.speed)*8/256 +1); balls[i].height = 0.5 * gravity * pow(timeSinceLastBounce/1000 , 2.0) + balls[i].impactVelocity * timeSinceLastBounce/1000; @@ -2676,47 +2858,48 @@ uint16_t WS2812FX::mode_bouncing_balls(void) { uint32_t color = SEGCOLOR(0); if (SEGMENT.palette) { - color = color_wheel(i*(256/MAX(numBalls, 8))); + color = SEGMENT.color_wheel(i*(256/MAX(numBalls, 8))); } else if (hasCol2) { color = SEGCOLOR(i % NUM_COLORS); } uint16_t pos = round(balls[i].height * (SEGLEN - 1)); - setPixelColor(pos, color); + SEGMENT.setPixelColor(pos, color); } return FRAMETIME; } +static const char _data_FX_MODE_BOUNCINGBALLS[] PROGMEM = "Bouncing Balls@Gravity,# of balls;!,!,;!;mp12=2,1d"; //circle /* * Sinelon stolen from FASTLED examples */ -uint16_t WS2812FX::sinelon_base(bool dual, bool rainbow=false) { - fade_out(SEGMENT.intensity); +uint16_t sinelon_base(bool dual, bool rainbow=false) { + SEGMENT.fade_out(SEGMENT.intensity); uint16_t pos = beatsin16(SEGMENT.speed/10,0,SEGLEN-1); if (SEGENV.call == 0) SEGENV.aux0 = pos; - uint32_t color1 = color_from_palette(pos, true, false, 0); + uint32_t color1 = SEGMENT.color_from_palette(pos, true, false, 0); uint32_t color2 = SEGCOLOR(2); if (rainbow) { - color1 = color_wheel((pos & 0x07) * 32); + color1 = SEGMENT.color_wheel((pos & 0x07) * 32); } - setPixelColor(pos, color1); + SEGMENT.setPixelColor(pos, color1); if (dual) { - if (!color2) color2 = color_from_palette(pos, true, false, 0); + if (!color2) color2 = SEGMENT.color_from_palette(pos, true, false, 0); if (rainbow) color2 = color1; //rainbow - setPixelColor(SEGLEN-1-pos, color2); + SEGMENT.setPixelColor(SEGLEN-1-pos, color2); } if (SEGENV.aux0 != pos) { if (SEGENV.aux0 < pos) { - for (uint16_t i = SEGENV.aux0; i < pos ; i++) { - setPixelColor(i, color1); - if (dual) setPixelColor(SEGLEN-1-i, color2); + for (int i = SEGENV.aux0; i < pos ; i++) { + SEGMENT.setPixelColor(i, color1); + if (dual) SEGMENT.setPixelColor(SEGLEN-1-i, color2); } } else { - for (uint16_t i = SEGENV.aux0; i > pos ; i--) { - setPixelColor(i, color1); - if (dual) setPixelColor(SEGLEN-1-i, color2); + for (int i = SEGENV.aux0; i > pos ; i--) { + SEGMENT.setPixelColor(i, color1); + if (dual) SEGMENT.setPixelColor(SEGLEN-1-i, color2); } } SEGENV.aux0 = pos; @@ -2725,39 +2908,49 @@ uint16_t WS2812FX::sinelon_base(bool dual, bool rainbow=false) { return FRAMETIME; } -uint16_t WS2812FX::mode_sinelon(void) { + +uint16_t mode_sinelon(void) { return sinelon_base(false); } +static const char _data_FX_MODE_SINELON[] PROGMEM = "Sinelon"; + -uint16_t WS2812FX::mode_sinelon_dual(void) { +uint16_t mode_sinelon_dual(void) { return sinelon_base(true); } +static const char _data_FX_MODE_SINELON_DUAL[] PROGMEM = "Sinelon Dual"; -uint16_t WS2812FX::mode_sinelon_rainbow(void) { + +uint16_t mode_sinelon_rainbow(void) { return sinelon_base(false, true); } +static const char _data_FX_MODE_SINELON_RAINBOW[] PROGMEM = "Sinelon Rainbow"; //Rainbow with glitter, inspired by https://gist.github.com/kriegsman/062e10f7f07ba8518af6 -uint16_t WS2812FX::mode_glitter() +uint16_t mode_glitter() { mode_palette(); - if (SEGMENT.intensity > random8()) - { - setPixelColor(random16(SEGLEN), ULTRAWHITE); - } + if (strip.isMatrix) { + uint16_t height = SEGMENT.virtualHeight(); + uint16_t width = SEGMENT.virtualWidth(); + for (int i = 0; i random8()) SEGMENT.setPixelColorXY(random16(width-1), i, ULTRAWHITE); + } + } else + if (SEGMENT.intensity > random8()) SEGMENT.setPixelColor(random16(SEGLEN), ULTRAWHITE); return FRAMETIME; } +static const char _data_FX_MODE_GLITTER[] PROGMEM = "Glitter@,!;!,!,!;!;1d,2d"; //pixels - -//each needs 12 bytes +//each needs 19 bytes //Spark type is used for popcorn, 1D fireworks, and drip typedef struct Spark { - float pos; - float vel; + float pos, posX; + float vel, velX; uint16_t col; uint8_t colIndex; } spark; @@ -2766,7 +2959,10 @@ typedef struct Spark { * POPCORN * modified from https://github.com/kitesurfer1404/WS2812FX/blob/master/src/custom/Popcorn.h */ -uint16_t WS2812FX::mode_popcorn(void) { +uint16_t mode_popcorn(void) { + const uint16_t cols = strip.isMatrix ? SEGMENT.virtualWidth() : 1; + const uint16_t rows = strip.isMatrix ? SEGMENT.virtualHeight() : SEGMENT.virtualLength(); + //allocate segment data uint16_t maxNumPopcorn = 21; // max 21 on 16 segment ESP8266 uint16_t dataSize = sizeof(spark) * maxNumPopcorn; @@ -2775,28 +2971,29 @@ uint16_t WS2812FX::mode_popcorn(void) { Spark* popcorn = reinterpret_cast(SEGENV.data); float gravity = -0.0001 - (SEGMENT.speed/200000.0); // m/s/s - gravity *= SEGLEN; + gravity *= rows; //SEGLEN bool hasCol2 = SEGCOLOR(2); - fill(hasCol2 ? BLACK : SEGCOLOR(1)); + SEGMENT.fill(hasCol2 ? BLACK : SEGCOLOR(1)); uint8_t numPopcorn = SEGMENT.intensity*maxNumPopcorn/255; if (numPopcorn == 0) numPopcorn = 1; - for(uint8_t i = 0; i < numPopcorn; i++) { + for (int i = 0; i < numPopcorn; i++) { if (popcorn[i].pos >= 0.0f) { // if kernel is active, update its position popcorn[i].pos += popcorn[i].vel; popcorn[i].vel += gravity; } else { // if kernel is inactive, randomly pop it if (random8() < 2) { // POP!!! popcorn[i].pos = 0.01f; + popcorn[i].posX = random16(cols); uint16_t peakHeight = 128 + random8(128); //0-255 - peakHeight = (peakHeight * (SEGLEN -1)) >> 8; + peakHeight = (peakHeight * (rows -1)) >> 8; popcorn[i].vel = sqrt(-2.0 * gravity * peakHeight); + popcorn[i].velX = 0; - if (SEGMENT.palette) - { + if (SEGMENT.palette) { popcorn[i].colIndex = random8(); } else { byte col = random8(0, NUM_COLORS); @@ -2806,23 +3003,27 @@ uint16_t WS2812FX::mode_popcorn(void) { } } if (popcorn[i].pos >= 0.0f) { // draw now active popcorn (either active before or just popped) - uint32_t col = color_wheel(popcorn[i].colIndex); + uint32_t col = SEGMENT.color_wheel(popcorn[i].colIndex); if (!SEGMENT.palette && popcorn[i].colIndex < NUM_COLORS) col = SEGCOLOR(popcorn[i].colIndex); uint16_t ledIndex = popcorn[i].pos; - if (ledIndex < SEGLEN) setPixelColor(ledIndex, col); + if (ledIndex < rows) { + if (strip.isMatrix) SEGMENT.setPixelColorXY(uint16_t(popcorn[i].posX), rows - 1 - ledIndex, col); + else SEGMENT.setPixelColor(ledIndex, col); + } } } return FRAMETIME; } +static const char _data_FX_MODE_POPCORN[] PROGMEM = "Popcorn@!,!;!,!,!;!;1d,2d"; //values close to 100 produce 5Hz flicker, which looks very candle-y //Inspired by https://github.com/avanhanegem/ArduinoCandleEffectNeoPixel //and https://cpldcpu.wordpress.com/2016/01/05/reverse-engineering-a-real-candle/ -uint16_t WS2812FX::candle(bool multi) +uint16_t candle(bool multi) { if (multi) { @@ -2847,7 +3048,7 @@ uint16_t WS2812FX::candle(bool multi) uint16_t numCandles = (multi) ? SEGLEN : 1; - for (uint16_t i = 0; i < numCandles; i++) + for (int i = 0; i < numCandles; i++) { uint16_t d = 0; //data location @@ -2882,12 +3083,12 @@ uint16_t WS2812FX::candle(bool multi) } if (i > 0) { - setPixelColor(i, color_blend(SEGCOLOR(1), color_from_palette(i, true, PALETTE_SOLID_WRAP, 0), s)); + SEGMENT.setPixelColor(i, color_blend(SEGCOLOR(1), SEGMENT.color_from_palette(i, true, PALETTE_SOLID_WRAP, 0), s)); SEGENV.data[d] = s; SEGENV.data[d+1] = s_target; SEGENV.data[d+2] = fadeStep; } else { - for (uint16_t j = 0; j < SEGLEN; j++) { - setPixelColor(j, color_blend(SEGCOLOR(1), color_from_palette(j, true, PALETTE_SOLID_WRAP, 0), s)); + for (int j = 0; j < SEGLEN; j++) { + SEGMENT.setPixelColor(j, color_blend(SEGCOLOR(1), SEGMENT.color_from_palette(j, true, PALETTE_SOLID_WRAP, 0), s)); } SEGENV.aux0 = s; SEGENV.aux1 = s_target; SEGENV.step = fadeStep; @@ -2897,16 +3098,19 @@ uint16_t WS2812FX::candle(bool multi) return FRAMETIME_FIXED; } -uint16_t WS2812FX::mode_candle() + +uint16_t mode_candle() { return candle(false); } +static const char _data_FX_MODE_CANDLE[] PROGMEM = "Candle@Flicker rate,Flicker intensity;!,!,;;sx=96,ix=224,pal=0,1d"; -uint16_t WS2812FX::mode_candle_multi() +uint16_t mode_candle_multi() { return candle(true); } +static const char _data_FX_MODE_CANDLE_MULTI[] PROGMEM = "Candle Multi@Flicker rate,Flicker intensity;!,!,;;sx=96,ix=224,pal=0,1d"; /* @@ -2929,11 +3133,11 @@ typedef struct particle { float fragment[STARBURST_MAX_FRAG]; } star; -uint16_t WS2812FX::mode_starburst(void) { +uint16_t mode_starburst(void) { uint16_t maxData = FAIR_DATA_PER_SEG; //ESP8266: 256 ESP32: 640 - uint8_t segs = getActiveSegmentsNum(); - if (segs <= (MAX_NUM_SEGMENTS /2)) maxData *= 2; //ESP8266: 512 if <= 8 segs ESP32: 1280 if <= 16 segs - if (segs <= (MAX_NUM_SEGMENTS /4)) maxData *= 2; //ESP8266: 1024 if <= 4 segs ESP32: 2560 if <= 8 segs + uint8_t segs = strip.getActiveSegmentsNum(); + if (segs <= (strip.getMaxSegments() /2)) maxData *= 2; //ESP8266: 512 if <= 8 segs ESP32: 1280 if <= 16 segs + if (segs <= (strip.getMaxSegments() /4)) maxData *= 2; //ESP8266: 1024 if <= 4 segs ESP32: 2560 if <= 8 segs uint16_t maxStars = maxData / sizeof(star); //ESP8266: max. 4/9/19 stars/seg, ESP32: max. 10/21/42 stars/seg uint8_t numStars = 1 + (SEGLEN >> 3); @@ -2959,7 +3163,7 @@ uint16_t WS2812FX::mode_starburst(void) { uint16_t startPos = random16(SEGLEN-1); float multiplier = (float)(random8())/255.0 * 1.0; - stars[j].color = col_to_crgb(color_wheel(random8())); + stars[j].color = CRGB(SEGMENT.color_wheel(random8())); stars[j].pos = startPos; stars[j].vel = maxSpeed * (float)(random8())/255.0 * multiplier; stars[j].birth = it; @@ -2974,7 +3178,7 @@ uint16_t WS2812FX::mode_starburst(void) { } } - fill(SEGCOLOR(1)); + SEGMENT.fill(SEGCOLOR(1)); for (int j=0; j particleIgnition + particleFadeTime) { fade = 1.0f; // Black hole, all faded out stars[j].birth = 0; - c = col_to_crgb(SEGCOLOR(1)); + c = CRGB(SEGCOLOR(1)); } else { age -= particleIgnition; fade = (age / particleFadeTime); // Fading star byte f = 254.5f*fade; - c = col_to_crgb(color_blend(crgb_to_col(c), SEGCOLOR(1), f)); + c = CRGB(color_blend(RGBW32(c.r,c.g,c.b,0), SEGCOLOR(1), f)); } } - float particleSize = (1.0 - fade) * 2; + float particleSize = (1.0f - fade) * 2.0f; - for (uint8_t index=0; index < STARBURST_MAX_FRAG*2; index++) { + for (size_t index=0; index < STARBURST_MAX_FRAG*2; index++) { bool mirrored = index & 0x1; uint8_t i = index >> 1; if (stars[j].fragment[i] > 0) { @@ -3031,7 +3235,7 @@ uint16_t WS2812FX::mode_starburst(void) { if (start == end) end++; if (end > SEGLEN) end = SEGLEN; for (int p = start; p < end; p++) { - setPixelColor(p, c.r, c.g, c.b); + SEGMENT.setPixelColor(p, c.r, c.g, c.b); } } } @@ -3039,61 +3243,69 @@ uint16_t WS2812FX::mode_starburst(void) { return FRAMETIME; } #undef STARBURST_MAX_FRAG +static const char _data_FX_MODE_STARBURST[] PROGMEM = "Fireworks Starburst@Chance,Fragments;,!,;!;pal=11,mp12=0,1d"; + /* * Exploding fireworks effect * adapted from: http://www.anirama.com/1000leds/1d-fireworks/ + * adapted for 2D WLED by blazoncek (Blaz Kristan (AKA blazoncek)) */ -uint16_t WS2812FX::mode_exploding_fireworks(void) +uint16_t mode_exploding_fireworks(void) { + const uint16_t cols = strip.isMatrix ? SEGMENT.virtualWidth() : 1; + const uint16_t rows = strip.isMatrix ? SEGMENT.virtualHeight() : SEGMENT.virtualLength(); + //allocate segment data uint16_t maxData = FAIR_DATA_PER_SEG; //ESP8266: 256 ESP32: 640 - uint8_t segs = getActiveSegmentsNum(); - if (segs <= (MAX_NUM_SEGMENTS /2)) maxData *= 2; //ESP8266: 512 if <= 8 segs ESP32: 1280 if <= 16 segs - if (segs <= (MAX_NUM_SEGMENTS /4)) maxData *= 2; //ESP8266: 1024 if <= 4 segs ESP32: 2560 if <= 8 segs + uint8_t segs = strip.getActiveSegmentsNum(); + if (segs <= (strip.getMaxSegments() /2)) maxData *= 2; //ESP8266: 512 if <= 8 segs ESP32: 1280 if <= 16 segs + if (segs <= (strip.getMaxSegments() /4)) maxData *= 2; //ESP8266: 1024 if <= 4 segs ESP32: 2560 if <= 8 segs int maxSparks = maxData / sizeof(spark); //ESP8266: max. 21/42/85 sparks/seg, ESP32: max. 53/106/213 sparks/seg - uint16_t numSparks = min(2 + (SEGLEN >> 1), maxSparks); + uint16_t numSparks = min(2 + ((rows*cols) >> 1), maxSparks); uint16_t dataSize = sizeof(spark) * numSparks; - if (!SEGENV.allocateData(dataSize)) return mode_static(); //allocation failed + if (!SEGENV.allocateData(dataSize + sizeof(float))) return mode_static(); //allocation failed + float *dying_gravity = reinterpret_cast(SEGENV.data + dataSize); - if (dataSize != SEGENV.aux1) { //reset to flare if sparks were reallocated + if (dataSize != SEGENV.aux1) { //reset to flare if sparks were reallocated (it may be good idea to reset segment if bounds change) + *dying_gravity = 0.0f; SEGENV.aux0 = 0; SEGENV.aux1 = dataSize; } - fill(BLACK); - - bool actuallyReverse = SEGMENT.getOption(SEG_OPTION_REVERSED); - //have fireworks start in either direction based on intensity - SEGMENT.setOption(SEG_OPTION_REVERSED, SEGENV.step); + //SEGMENT.fill(BLACK); + SEGMENT.fade_out(252); Spark* sparks = reinterpret_cast(SEGENV.data); Spark* flare = sparks; //first spark is flare data - float gravity = -0.0004 - (SEGMENT.speed/800000.0); // m/s/s - gravity *= SEGLEN; + float gravity = -0.0004f - (SEGMENT.speed/800000.0f); // m/s/s + gravity *= rows; if (SEGENV.aux0 < 2) { //FLARE if (SEGENV.aux0 == 0) { //init flare flare->pos = 0; + flare->posX = strip.isMatrix ? random16(2,cols-1) : (SEGMENT.intensity > random8()); // will enable random firing side on 1D uint16_t peakHeight = 75 + random8(180); //0-255 - peakHeight = (peakHeight * (SEGLEN -1)) >> 8; - flare->vel = sqrt(-2.0 * gravity * peakHeight); + peakHeight = (peakHeight * (rows -1)) >> 8; + flare->vel = sqrt(-2.0f * gravity * peakHeight); + flare->velX = strip.isMatrix ? (random8(8)-4)/32.f : 0; // no X velocity on 1D flare->col = 255; //brightness - SEGENV.aux0 = 1; } // launch if (flare->vel > 12 * gravity) { // flare - setPixelColor(int(flare->pos),flare->col,flare->col,flare->col); - - flare->pos += flare->vel; - flare->pos = constrain(flare->pos, 0, SEGLEN-1); - flare->vel += gravity; - flare->col -= 2; + if (strip.isMatrix) SEGMENT.setPixelColorXY(int(flare->posX), rows - uint16_t(flare->pos) - 1, flare->col, flare->col, flare->col); + else SEGMENT.setPixelColor(int(flare->posX) ? rows - int(flare->pos) - 1 : int(flare->pos), flare->col, flare->col, flare->col); + flare->pos += flare->vel; + flare->posX += flare->velX; + flare->pos = constrain(flare->pos, 0, rows-1); + flare->posX = constrain(flare->posX, 0, cols-strip.isMatrix); + flare->vel += gravity; + flare->col -= 2; } else { SEGENV.aux0 = 2; // ready to explode } @@ -3104,48 +3316,56 @@ uint16_t WS2812FX::mode_exploding_fireworks(void) * Explosion happens where the flare ended. * Size is proportional to the height. */ - int nSparks = flare->pos; - nSparks = constrain(nSparks, 0, numSparks); - static float dying_gravity; + int nSparks = flare->pos + random8(4); + nSparks = constrain(nSparks, 1, numSparks); // initialize sparks if (SEGENV.aux0 == 2) { for (int i = 1; i < nSparks; i++) { - sparks[i].pos = flare->pos; - sparks[i].vel = (float(random16(0, 20000)) / 10000.0) - 0.9; // from -0.9 to 1.1 - sparks[i].col = 345;//abs(sparks[i].vel * 750.0); // set colors before scaling velocity to keep them bright + sparks[i].pos = flare->pos; + sparks[i].posX = flare->posX; + sparks[i].vel = (float(random16(0, 20000)) / 10000.0f) - 0.9f; // from -0.9 to 1.1 + sparks[i].vel *= rows<32 ? 0.5f : 1; // reduce velocity for smaller strips + sparks[i].velX = strip.isMatrix ? (float(random16(0, 4000)) / 10000.0f) - 0.2f : 0; // from -0.2 to 0.2 + sparks[i].col = 345;//abs(sparks[i].vel * 750.0); // set colors before scaling velocity to keep them bright //sparks[i].col = constrain(sparks[i].col, 0, 345); sparks[i].colIndex = random8(); - sparks[i].vel *= flare->pos/SEGLEN; // proportional to height - sparks[i].vel *= -gravity *50; + sparks[i].vel *= flare->pos/rows; // proportional to height + sparks[i].velX *= strip.isMatrix ? flare->posX/cols : 0; // proportional to width + sparks[i].vel *= -gravity *50; } //sparks[1].col = 345; // this will be our known spark - dying_gravity = gravity/2; + *dying_gravity = gravity/2; SEGENV.aux0 = 3; } if (sparks[1].col > 4) {//&& sparks[1].pos > 0) { // as long as our known spark is lit, work with all the sparks - for (int i = 1; i < nSparks; i++) { - sparks[i].pos += sparks[i].vel; - sparks[i].vel += dying_gravity; + for (int i = 1; i < nSparks; i++) { + sparks[i].pos += sparks[i].vel; + sparks[i].posX += sparks[i].velX; + sparks[i].vel += *dying_gravity; + sparks[i].velX += strip.isMatrix ? *dying_gravity : 0; if (sparks[i].col > 3) sparks[i].col -= 4; - if (sparks[i].pos > 0 && sparks[i].pos < SEGLEN) { + if (sparks[i].pos > 0 && sparks[i].pos < rows) { + if (strip.isMatrix && !(sparks[i].posX >= 0 && sparks[i].posX < cols)) continue; uint16_t prog = sparks[i].col; - uint32_t spColor = (SEGMENT.palette) ? color_wheel(sparks[i].colIndex) : SEGCOLOR(0); + uint32_t spColor = (SEGMENT.palette) ? SEGMENT.color_wheel(sparks[i].colIndex) : SEGCOLOR(0); CRGB c = CRGB::Black; //HeatColor(sparks[i].col); if (prog > 300) { //fade from white to spark color - c = col_to_crgb(color_blend(spColor, WHITE, (prog - 300)*5)); + c = CRGB(color_blend(spColor, WHITE, (prog - 300)*5)); } else if (prog > 45) { //fade from spark color to black - c = col_to_crgb(color_blend(BLACK, spColor, prog - 45)); + c = CRGB(color_blend(BLACK, spColor, prog - 45)); uint8_t cooling = (300 - prog) >> 5; c.g = qsub8(c.g, cooling); c.b = qsub8(c.b, cooling * 2); } - setPixelColor(int(sparks[i].pos), c.red, c.green, c.blue); + if (strip.isMatrix) SEGMENT.setPixelColorXY(int(sparks[i].posX), rows - int(sparks[i].pos) - 1, c.red, c.green, c.blue); + else SEGMENT.setPixelColor(int(sparks[i].posX) ? rows - int(sparks[i].pos) - 1 : int(sparks[i].pos), c.red, c.green, c.blue); } } - dying_gravity *= .99; // as sparks burn out they fall slower + SEGMENT.blur(16); + *dying_gravity *= .8f; // as sparks burn out they fall slower } else { SEGENV.aux0 = 6 + random8(10); //wait for this many frames } @@ -3153,96 +3373,111 @@ uint16_t WS2812FX::mode_exploding_fireworks(void) SEGENV.aux0--; if (SEGENV.aux0 < 4) { SEGENV.aux0 = 0; //back to flare - SEGENV.step = actuallyReverse ^ (SEGMENT.intensity > random8()); //decide firing side } } - SEGMENT.setOption(SEG_OPTION_REVERSED, actuallyReverse); - return FRAMETIME; } #undef MAX_SPARKS +static const char _data_FX_MODE_EXPLODING_FIREWORKS[] PROGMEM = "Fireworks 1D@Gravity,Firing side;!,!,;!=11;ix=128,1d,2d"; /* * Drip Effect * ported of: https://www.youtube.com/watch?v=sru2fXh4r7k */ -uint16_t WS2812FX::mode_drip(void) +uint16_t mode_drip(void) { + const uint16_t cols = strip.isMatrix ? SEGMENT.virtualWidth() : 1; + const uint16_t rows = strip.isMatrix ? SEGMENT.virtualHeight() : SEGMENT.virtualLength(); + //allocate segment data uint8_t numDrops = 4; uint16_t dataSize = sizeof(spark) * numDrops; - if (!SEGENV.allocateData(dataSize)) return mode_static(); //allocation failed + if (!SEGENV.allocateData(dataSize * cols)) return mode_static(); //allocation failed - fill(SEGCOLOR(1)); + SEGMENT.fill(SEGCOLOR(1)); Spark* drops = reinterpret_cast(SEGENV.data); numDrops = 1 + (SEGMENT.intensity >> 6); // 255>>6 = 3 float gravity = -0.0005 - (SEGMENT.speed/50000.0); - gravity *= SEGLEN; + gravity *= rows-1; int sourcedrop = 12; - for (uint8_t j=0;j255) drops[j].col=255; - setPixelColor(uint16_t(drops[j].pos),color_blend(BLACK,SEGCOLOR(0),drops[j].col)); - - drops[j].col += map(SEGMENT.speed, 0, 255, 1, 6); // swelling - - if (random8() < drops[j].col/10) { // random drop - drops[j].colIndex=2; //fall - drops[j].col=255; - } - } - if (drops[j].colIndex > 1) { // falling - if (drops[j].pos > 0) { // fall until end of segment - drops[j].pos += drops[j].vel; - if (drops[j].pos < 0) drops[j].pos = 0; - drops[j].vel += gravity; // gravity is negative - - for (uint16_t i=1;i<7-drops[j].colIndex;i++) { // some minor math so we don't expand bouncing droplets - uint16_t pos = constrain(uint16_t(drops[j].pos) +i, 0, SEGLEN-1); //this is BAD, returns a pos >= SEGLEN occasionally - setPixelColor(pos,color_blend(BLACK,SEGCOLOR(0),drops[j].col/i)); //spread pixel with fade while falling - } + for (int k=0; k < cols; k++) { + for (size_t j=0; j < numDrops; j++) { + uint16_t idx = k*numDrops + j; - if (drops[j].colIndex > 2) { // during bounce, some water is on the floor - setPixelColor(0,color_blend(SEGCOLOR(0),BLACK,drops[j].col)); + if (drops[idx].colIndex == 0) { //init + drops[idx].pos = rows-1; // start at end + drops[idx].vel = 0; // speed + drops[idx].col = sourcedrop; // brightness + drops[idx].colIndex = 1; // drop state (0 init, 1 forming, 2 falling, 5 bouncing) + } + + uint32_t col = color_blend(BLACK, SEGCOLOR(0), sourcedrop); + if (strip.isMatrix) SEGMENT.setPixelColorXY(k, 0, col); + else SEGMENT.setPixelColor(rows-1, col);// water source + + if (drops[idx].colIndex == 1) { + if (drops[idx].col > 255) drops[idx].col = 255; + col = color_blend(BLACK,SEGCOLOR(0),drops[idx].col); + if (strip.isMatrix) SEGMENT.setPixelColorXY(k, rows - 1 - uint16_t(drops[idx].pos), col); + else SEGMENT.setPixelColor(uint16_t(drops[idx].pos), col); + + drops[idx].col += map(SEGMENT.speed, 0, 255, 1, 6); // swelling + + if (random8() < drops[idx].col/10) { // random drop + drops[idx].colIndex = 2; //fall + drops[idx].col = 255; } - } else { // we hit bottom - if (drops[j].colIndex > 2) { // already hit once, so back to forming - drops[j].colIndex = 0; - drops[j].col = sourcedrop; - - } else { + } + if (drops[idx].colIndex > 1) { // falling + if (drops[idx].pos > 0) { // fall until end of segment + drops[idx].pos += drops[idx].vel; + if (drops[idx].pos < 0) drops[idx].pos = 0; + drops[idx].vel += gravity; // gravity is negative + + for (int i = 1; i < 7 - drops[idx].colIndex; i++) { // some minor math so we don't expand bouncing droplets + uint16_t pos = constrain(uint16_t(drops[idx].pos) +i, 0, rows-1); //this is BAD, returns a pos >= SEGLEN occasionally + col = color_blend(BLACK, SEGCOLOR(0), drops[idx].col/i); + if (strip.isMatrix) SEGMENT.setPixelColorXY(k, rows - 1 - pos, col); + else SEGMENT.setPixelColor(pos, col); //spread pixel with fade while falling + } - if (drops[j].colIndex==2) { // init bounce - drops[j].vel = -drops[j].vel/4;// reverse velocity with damping - drops[j].pos += drops[j].vel; - } - drops[j].col = sourcedrop*2; - drops[j].colIndex = 5; // bouncing + if (drops[idx].colIndex > 2) { // during bounce, some water is on the floor + col = color_blend(SEGCOLOR(0), BLACK, drops[idx].col); + if (strip.isMatrix) SEGMENT.setPixelColorXY(k, rows - 1, col); + else SEGMENT.setPixelColor(0, col); + } + } else { // we hit bottom + if (drops[idx].colIndex > 2) { // already hit once, so back to forming + drops[idx].colIndex = 0; + drops[idx].col = sourcedrop; + + } else { + + if (drops[idx].colIndex == 2) { // init bounce + drops[idx].vel = -drops[idx].vel/4;// reverse velocity with damping + drops[idx].pos += drops[idx].vel; + } + drops[idx].col = sourcedrop*2; + drops[idx].colIndex = 5; // bouncing + } } } } } - return FRAMETIME; + return FRAMETIME; } +static const char _data_FX_MODE_DRIP[] PROGMEM = "Drip@Gravity,# of drips;!,!;!;1d,2d"; /* * Tetris or Stacking (falling bricks) Effect - * by Blaz Kristan (https://github.com/blazoncek, https://blaz.at/home) + * by Blaz Kristan (AKA blazoncek) (https://github.com/blazoncek, https://blaz.at/home) */ //12 bytes typedef struct Tetris { @@ -3251,24 +3486,24 @@ typedef struct Tetris { uint32_t col; } tetris; -uint16_t WS2812FX::mode_tetrix(void) { +uint16_t mode_tetrix(void) { uint16_t dataSize = sizeof(tetris); if (!SEGENV.allocateData(dataSize)) return mode_static(); //allocation failed Tetris* drop = reinterpret_cast(SEGENV.data); // initialize dropping on first call or segment full - if (SEGENV.call == 0 || SEGENV.aux1 >= SEGLEN) { - SEGENV.aux1 = 0; // reset brick stack size + if (SEGENV.call == 0 /*|| SEGENV.aux1 >= SEGLEN*/) { + SEGENV.aux1 = 0; // reset brick stack size SEGENV.step = 0; - fill(SEGCOLOR(1)); - return 250; // short wait + SEGMENT.fill(SEGCOLOR(1)); + //return 250; // short wait } - if (SEGENV.step == 0) { //init + if (SEGENV.step == 0) { // init brick drop->speed = 0.0238 * (SEGMENT.speed ? (SEGMENT.speed>>2)+1 : random8(6,64)); // set speed drop->pos = SEGLEN; // start at end of segment (no need to subtract 1) - drop->col = color_from_palette(random8(0,15)<<4,false,false,0); // limit color choices so there is enough HUE gap + drop->col = SEGMENT.color_from_palette(random8(0,15)<<4,false,false,0); // limit color choices so there is enough HUE gap SEGENV.step = 1; // drop state (0 init, 1 forming, 2 falling) SEGENV.aux0 = (SEGMENT.intensity ? (SEGMENT.intensity>>5)+1 : random8(1,5)) * (1+(SEGLEN>>6)); // size of brick } @@ -3279,26 +3514,38 @@ uint16_t WS2812FX::mode_tetrix(void) { } } - if (SEGENV.step > 1) { // falling + if (SEGENV.step == 2) { // falling if (drop->pos > SEGENV.aux1) { // fall until top of stack drop->pos -= drop->speed; // may add gravity as: speed += gravity - if (int(drop->pos) < SEGENV.aux1) drop->pos = SEGENV.aux1; - for (uint16_t i=int(drop->pos); ipos)+SEGENV.aux0 ? drop->col : SEGCOLOR(1)); + if (uint16_t(drop->pos) < SEGENV.aux1) drop->pos = SEGENV.aux1; + for (int i=int(drop->pos); ipos)+SEGENV.aux0 ? drop->col : SEGCOLOR(1)); } else { // we hit bottom - SEGENV.step = 0; // go back to init + SEGENV.step = 0; // proceed with next brick, go back to init SEGENV.aux1 += SEGENV.aux0; // increase the stack size - if (SEGENV.aux1 >= SEGLEN) return 1000; // wait for a second + if (SEGENV.aux1 >= SEGLEN) SEGENV.step = millis() + 2500; // fade out stack + } + } + + if (SEGENV.step > 2) { + SEGENV.aux0 = 0; // reset brick size (no more growing) + if (SEGENV.step > millis()) { + SEGMENT.fade_out(24); // fade out stack + } else { + SEGENV.aux1 = 0; // reset brick stack size + SEGENV.step = 0; // proceed with next brick } } + return FRAMETIME; } +static const char _data_FX_MODE_TETRIX[] PROGMEM = "Tetrix@!,Width;!,!,;!;sx=224,ix=0,pal=11,mp12=1,1d"; //vertical /* / Plasma Effect / adapted from https://github.com/atuline/FastLED-Demos/blob/master/plasma/plasma.ino */ -uint16_t WS2812FX::mode_plasma(void) { +uint16_t mode_plasma(void) { // initialize phases on start if (SEGENV.call == 0) { SEGENV.aux0 = random8(0,2); // add a bit of randomness @@ -3310,19 +3557,21 @@ uint16_t WS2812FX::mode_plasma(void) { uint8_t colorIndex = cubicwave8((i*(2+ 3*(SEGMENT.speed >> 5))+thisPhase) & 0xFF)/2 // factor=23 // Create a wave and add a phase change and add another wave with its own phase change. + cos8((i*(1+ 2*(SEGMENT.speed >> 5))+thatPhase) & 0xFF)/2; // factor=15 // Hey, you can even change the frequencies if you wish. uint8_t thisBright = qsub8(colorIndex, beatsin8(7,0, (128 - (SEGMENT.intensity>>1)))); - CRGB color = ColorFromPalette(currentPalette, colorIndex, thisBright, LINEARBLEND); - setPixelColor(i, color.red, color.green, color.blue); + //CRGB color = ColorFromPalette(SEGPALETTE, colorIndex, thisBright, LINEARBLEND); + //SEGMENT.setPixelColor(i, color.red, color.green, color.blue); + SEGMENT.setPixelColor(i, SEGMENT.color_from_palette(colorIndex, false, PALETTE_SOLID_WRAP, 0, thisBright)); } return FRAMETIME; } +static const char _data_FX_MODE_PLASMA[] PROGMEM = "Plasma@Phase,;1,2,3;!;1d"; /* * Percentage display * Intesity values from 0-100 turn on the leds. */ -uint16_t WS2812FX::mode_percent(void) { +uint16_t mode_percent(void) { uint8_t percent = MAX(0, MIN(200, SEGMENT.intensity)); uint16_t active_leds = (percent < 100) ? SEGLEN * percent / 100.0 @@ -3332,65 +3581,69 @@ uint16_t WS2812FX::mode_percent(void) { if (SEGMENT.speed == 255) size = 255; if (percent < 100) { - for (uint16_t i = 0; i < SEGLEN; i++) { - if (i < SEGENV.step) { - setPixelColor(i, color_from_palette(i, true, PALETTE_SOLID_WRAP, 0)); + for (int i = 0; i < SEGLEN; i++) { + if (i < SEGENV.aux1) { + SEGMENT.setPixelColor(i, SEGMENT.color_from_palette(i, true, PALETTE_SOLID_WRAP, 0)); } else { - setPixelColor(i, SEGCOLOR(1)); + SEGMENT.setPixelColor(i, SEGCOLOR(1)); } } } else { - for (uint16_t i = 0; i < SEGLEN; i++) { - if (i < (SEGLEN - SEGENV.step)) { - setPixelColor(i, SEGCOLOR(1)); + for (int i = 0; i < SEGLEN; i++) { + if (i < (SEGLEN - SEGENV.aux1)) { + SEGMENT.setPixelColor(i, SEGCOLOR(1)); } else { - setPixelColor(i, color_from_palette(i, true, PALETTE_SOLID_WRAP, 0)); + SEGMENT.setPixelColor(i, SEGMENT.color_from_palette(i, true, PALETTE_SOLID_WRAP, 0)); } } } - if(active_leds > SEGENV.step) { // smooth transition to the target value - SEGENV.step += size; - if (SEGENV.step > active_leds) SEGENV.step = active_leds; - } else if (active_leds < SEGENV.step) { - if (SEGENV.step > size) SEGENV.step -= size; else SEGENV.step = 0; - if (SEGENV.step < active_leds) SEGENV.step = active_leds; + if(active_leds > SEGENV.aux1) { // smooth transition to the target value + SEGENV.aux1 += size; + if (SEGENV.aux1 > active_leds) SEGENV.aux1 = active_leds; + } else if (active_leds < SEGENV.aux1) { + if (SEGENV.aux1 > size) SEGENV.aux1 -= size; else SEGENV.aux1 = 0; + if (SEGENV.aux1 < active_leds) SEGENV.aux1 = active_leds; } return FRAMETIME; } +static const char _data_FX_MODE_PERCENT[] PROGMEM = "Percent@,% of fill;!,!,;!;1d"; + /* -/ Modulates the brightness similar to a heartbeat -*/ -uint16_t WS2812FX::mode_heartbeat(void) { - uint8_t bpm = 40 + (SEGMENT.speed >> 4); - uint32_t msPerBeat = (60000 / bpm); + * Modulates the brightness similar to a heartbeat + * tries to draw an ECG aproximation on a 2D matrix + */ +uint16_t mode_heartbeat(void) { + uint8_t bpm = 40 + (SEGMENT.speed >> 3); + uint32_t msPerBeat = (60000L / bpm); uint32_t secondBeat = (msPerBeat / 3); - uint32_t bri_lower = SEGENV.aux1; + unsigned long beatTimer = strip.now - SEGENV.step; + bri_lower = bri_lower * 2042 / (2048 + SEGMENT.intensity); SEGENV.aux1 = bri_lower; - unsigned long beatTimer = millis() - SEGENV.step; - if((beatTimer > secondBeat) && !SEGENV.aux0) { // time for the second beat? - SEGENV.aux1 = UINT16_MAX; //full bri + if ((beatTimer > secondBeat) && !SEGENV.aux0) { // time for the second beat? + SEGENV.aux1 = UINT16_MAX; //3/4 bri SEGENV.aux0 = 1; } - if(beatTimer > msPerBeat) { // time to reset the beat timer? + if (beatTimer > msPerBeat) { // time to reset the beat timer? SEGENV.aux1 = UINT16_MAX; //full bri SEGENV.aux0 = 0; - SEGENV.step = millis(); + SEGENV.step = strip.now; } - for (uint16_t i = 0; i < SEGLEN; i++) { - setPixelColor(i, color_blend(color_from_palette(i, true, PALETTE_SOLID_WRAP, 0), SEGCOLOR(1), 255 - (SEGENV.aux1 >> 8))); + for (int i = 0; i < SEGLEN; i++) { + SEGMENT.setPixelColor(i, color_blend(SEGMENT.color_from_palette(i, true, PALETTE_SOLID_WRAP, 0), SEGCOLOR(1), 255 - (SEGENV.aux1 >> 8))); } return FRAMETIME; } +static const char _data_FX_MODE_HEARTBEAT[] PROGMEM = "Heartbeat@!,!;!,!,;!;mp12=1,1d"; // "Pacifica" @@ -3417,9 +3670,25 @@ uint16_t WS2812FX::mode_heartbeat(void) { // // Modified for WLED, based on https://github.com/FastLED/FastLED/blob/master/examples/Pacifica/Pacifica.ino // -uint16_t WS2812FX::mode_pacifica() +// Add one layer of waves into the led array +CRGB pacifica_one_layer(uint16_t i, CRGBPalette16& p, uint16_t cistart, uint16_t wavescale, uint8_t bri, uint16_t ioff) +{ + uint16_t ci = cistart; + uint16_t waveangle = ioff; + uint16_t wavescale_half = (wavescale >> 1) + 20; + + waveangle += ((120 + SEGMENT.intensity) * i); //original 250 * i + uint16_t s16 = sin16(waveangle) + 32768; + uint16_t cs = scale16(s16, wavescale_half) + wavescale_half; + ci += (cs * i); + uint16_t sindex16 = sin16(ci) + 32768; + uint8_t sindex8 = scale16(sindex16, 240); + return ColorFromPalette(p, sindex8, bri, LINEARBLEND); +} + +uint16_t mode_pacifica() { - uint32_t nowOld = now; + uint32_t nowOld = strip.now; CRGBPalette16 pacifica_palette_1 = { 0x000507, 0x000409, 0x00030B, 0x00030D, 0x000210, 0x000212, 0x000114, 0x000117, @@ -3432,19 +3701,17 @@ uint16_t WS2812FX::mode_pacifica() 0x000E39, 0x001040, 0x001450, 0x001860, 0x001C70, 0x002080, 0x1040BF, 0x2060FF }; if (SEGMENT.palette) { - pacifica_palette_1 = currentPalette; - pacifica_palette_2 = currentPalette; - pacifica_palette_3 = currentPalette; + pacifica_palette_1 = SEGPALETTE; + pacifica_palette_2 = SEGPALETTE; + pacifica_palette_3 = SEGPALETTE; } // Increment the four "color index start" counters, one for each wave layer. // Each is incremented at a different speed, and the speeds vary over time. uint16_t sCIStart1 = SEGENV.aux0, sCIStart2 = SEGENV.aux1, sCIStart3 = SEGENV.step, sCIStart4 = SEGENV.step >> 16; - //static uint16_t sCIStart1, sCIStart2, sCIStart3, sCIStart4; - //uint32_t deltams = 26 + (SEGMENT.speed >> 3); uint32_t deltams = (FRAMETIME >> 2) + ((FRAMETIME * SEGMENT.speed) >> 7); - uint64_t deltat = (now >> 2) + ((now * SEGMENT.speed) >> 7); - now = deltat; + uint64_t deltat = (strip.now >> 2) + ((strip.now * SEGMENT.speed) >> 7); + strip.now = deltat; uint16_t speedfactor1 = beatsin16(3, 179, 269); uint16_t speedfactor2 = beatsin16(4, 179, 269); @@ -3459,12 +3726,12 @@ uint16_t WS2812FX::mode_pacifica() SEGENV.step = sCIStart4; SEGENV.step = (SEGENV.step << 16) + sCIStart3; // Clear out the LED array to a dim background blue-green - //fill(132618); + //SEGMENT.fill(132618); uint8_t basethreshold = beatsin8( 9, 55, 65); uint8_t wave = beat8( 7 ); - for( uint16_t i = 0; i < SEGLEN; i++) { + for (int i = 0; i < SEGLEN; i++) { CRGB c = CRGB(2, 6, 10); // Render each of four layers, with different scales and speeds, that vary over time c += pacifica_one_layer(i, pacifica_palette_1, sCIStart1, beatsin16(3, 11 * 256, 14 * 256), beatsin8(10, 70, 130), 0-beat16(301)); @@ -3487,46 +3754,38 @@ uint16_t WS2812FX::mode_pacifica() c.green = scale8(c.green, 200); c |= CRGB( 2, 5, 7); - setPixelColor(i, c.red, c.green, c.blue); + SEGMENT.setPixelColor(i, c.red, c.green, c.blue); } - now = nowOld; + strip.now = nowOld; return FRAMETIME; } +static const char _data_FX_MODE_PACIFICA[] PROGMEM = "Pacifica"; -// Add one layer of waves into the led array -CRGB WS2812FX::pacifica_one_layer(uint16_t i, CRGBPalette16& p, uint16_t cistart, uint16_t wavescale, uint8_t bri, uint16_t ioff) -{ - uint16_t ci = cistart; - uint16_t waveangle = ioff; - uint16_t wavescale_half = (wavescale >> 1) + 20; - - waveangle += ((120 + SEGMENT.intensity) * i); //original 250 * i - uint16_t s16 = sin16(waveangle) + 32768; - uint16_t cs = scale16(s16, wavescale_half) + wavescale_half; - ci += (cs * i); - uint16_t sindex16 = sin16(ci) + 32768; - uint8_t sindex8 = scale16(sindex16, 240); - return ColorFromPalette(p, sindex8, bri, LINEARBLEND); -} //Solid colour background with glitter -uint16_t WS2812FX::mode_solid_glitter() +uint16_t mode_solid_glitter() { - fill(SEGCOLOR(0)); + SEGMENT.fill(SEGCOLOR(0)); + + if (strip.isMatrix) { + uint16_t height = SEGMENT.virtualHeight(); + uint16_t width = SEGMENT.virtualWidth(); + for (int i = 0; i random8()) SEGMENT.setPixelColorXY(random16(width-1), i, ULTRAWHITE); + } + } else + if (SEGMENT.intensity > random8()) SEGMENT.setPixelColor(random16(SEGLEN), ULTRAWHITE); - if (SEGMENT.intensity > random8()) - { - setPixelColor(random16(SEGLEN), ULTRAWHITE); - } return FRAMETIME; } +static const char _data_FX_MODE_SOLID_GLITTER[] PROGMEM = "Solid Glitter@,!;!,,;0;1d,2d"; /* * Mode simulates a gradual sunrise */ -uint16_t WS2812FX::mode_sunrise() { +uint16_t mode_sunrise() { //speed 0 - static sun //speed 1 - 60: sunrise time in minutes //speed 60 - 120 : sunset time in minutes - 60; @@ -3536,13 +3795,13 @@ uint16_t WS2812FX::mode_sunrise() { SEGENV.aux0 = SEGMENT.speed; } - fill(0); + SEGMENT.fill(0); uint16_t stage = 0xFFFF; uint32_t s10SinceStart = (millis() - SEGENV.step) /100; //tenths of seconds if (SEGMENT.speed > 120) { //quick sunrise and sunset - uint16_t counter = (now >> 1) * (((SEGMENT.speed -120) >> 1) +1); + uint16_t counter = (strip.now >> 1) * (((SEGMENT.speed -120) >> 1) +1); stage = triwave16(counter); } else if (SEGMENT.speed) { //sunrise uint8_t durMins = SEGMENT.speed; @@ -3553,51 +3812,50 @@ uint16_t WS2812FX::mode_sunrise() { if (SEGMENT.speed > 60) stage = 0xFFFF - stage; //sunset } - for (uint16_t i = 0; i <= SEGLEN/2; i++) + for (int i = 0; i <= SEGLEN/2; i++) { //default palette is Fire - uint32_t c = color_from_palette(0, false, true, 255); //background + uint32_t c = SEGMENT.color_from_palette(0, false, true, 255); //background uint16_t wave = triwave16((i * stage) / SEGLEN); wave = (wave >> 8) + ((wave * SEGMENT.intensity) >> 15); if (wave > 240) { //clipped, full white sun - c = color_from_palette( 240, false, true, 255); + c = SEGMENT.color_from_palette( 240, false, true, 255); } else { //transition - c = color_from_palette(wave, false, true, 255); + c = SEGMENT.color_from_palette(wave, false, true, 255); } - setPixelColor(i, c); - setPixelColor(SEGLEN - i - 1, c); + SEGMENT.setPixelColor(i, c); + SEGMENT.setPixelColor(SEGLEN - i - 1, c); } return FRAMETIME; } +static const char _data_FX_MODE_SUNRISE[] PROGMEM = "Sunrise@Time [min],;;!;sx=60,1d"; /* * Effects by Andrew Tuline */ -uint16_t WS2812FX::phased_base(uint8_t moder) { // We're making sine waves here. By Andrew Tuline. +uint16_t phased_base(uint8_t moder) { // We're making sine waves here. By Andrew Tuline. uint8_t allfreq = 16; // Base frequency. - //float* phasePtr = reinterpret_cast(SEGENV.step); // Phase change value gets calculated. - static float phase = 0;//phasePtr[0]; + float *phase = reinterpret_cast(&SEGENV.step); // Phase change value gets calculated (float fits into unsigned long). uint8_t cutOff = (255-SEGMENT.intensity); // You can change the number of pixels. AKA INTENSITY (was 192). uint8_t modVal = 5;//SEGMENT.fft1/8+1; // You can change the modulus. AKA FFT1 (was 5). - uint8_t index = now/64; // Set color rotation speed - phase += SEGMENT.speed/32.0; // You can change the speed of the wave. AKA SPEED (was .4) - //phasePtr[0] = phase; + uint8_t index = strip.now/64; // Set color rotation speed + *phase += SEGMENT.speed/32.0; // You can change the speed of the wave. AKA SPEED (was .4) for (int i = 0; i < SEGLEN; i++) { if (moder == 1) modVal = (inoise8(i*10 + i*10) /16); // Let's randomize our mod length with some Perlin noise. - uint16_t val = (i+1) * allfreq; // This sets the frequency of the waves. The +1 makes sure that leds[0] is used. + uint16_t val = (i+1) * allfreq; // This sets the frequency of the waves. The +1 makes sure that led 0 is used. if (modVal == 0) modVal = 1; - val += phase * (i % modVal +1) /2; // This sets the varying phase change of the waves. By Andrew Tuline. + val += *phase * (i % modVal +1) /2; // This sets the varying phase change of the waves. By Andrew Tuline. uint8_t b = cubicwave8(val); // Now we make an 8 bit sinewave. b = (b > cutOff) ? (b - cutOff) : 0; // A ternary operator to cutoff the light. - setPixelColor(i, color_blend(SEGCOLOR(1), color_from_palette(index, false, false, 0), b)); + SEGMENT.setPixelColor(i, color_blend(SEGCOLOR(1), SEGMENT.color_from_palette(index, false, false, 0), b)); index += 256 / SEGLEN; if (SEGLEN > 256) index ++; // Correction for segments longer than 256 LEDs } @@ -3606,35 +3864,41 @@ uint16_t WS2812FX::phased_base(uint8_t moder) { // We're making } - -uint16_t WS2812FX::mode_phased(void) { +uint16_t mode_phased(void) { return phased_base(0); } +static const char _data_FX_MODE_PHASED[] PROGMEM = "Phased"; - -uint16_t WS2812FX::mode_phased_noise(void) { +uint16_t mode_phased_noise(void) { return phased_base(1); } +static const char _data_FX_MODE_PHASEDNOISE[] PROGMEM = "Phased Noise"; +uint16_t mode_twinkleup(void) { // A very short twinkle routine with fade-in and dual controls. By Andrew Tuline. + const uint16_t cols = strip.isMatrix ? SEGMENT.virtualWidth() : 1; + const uint16_t rows = strip.isMatrix ? SEGMENT.virtualHeight() : SEGMENT.virtualLength(); -uint16_t WS2812FX::mode_twinkleup(void) { // A very short twinkle routine with fade-in and dual controls. By Andrew Tuline. random16_set_seed(535); // The randomizer needs to be re-set each time through the loop in order for the same 'random' numbers to be the same each time through. - for (int i = 0; i SEGMENT.intensity) pixBri = 0; - setPixelColor(i, color_blend(SEGCOLOR(1), color_from_palette(random8()+now/100, false, PALETTE_SOLID_WRAP, 0), pixBri)); + uint32_t col = color_blend(SEGCOLOR(1), SEGMENT.color_from_palette(random8() + strip.now/100, false, PALETTE_SOLID_WRAP, 0), pixBri); + if (strip.isMatrix) SEGMENT.setPixelColorXY(j, k, col); + else SEGMENT.setPixelColor(i, col); } return FRAMETIME; } +static const char _data_FX_MODE_TWINKLEUP[] PROGMEM = "Twinkleup@!,Intensity;!,!,;!;1d,2d"; // Peaceful noise that's slow and with gradually changing palettes. Does not support WLED palettes or default colours or controls. -uint16_t WS2812FX::mode_noisepal(void) { // Slow noise palette by Andrew Tuline. +uint16_t mode_noisepal(void) { // Slow noise palette by Andrew Tuline. uint16_t scale = 15 + (SEGMENT.intensity >> 2); //default was 30 //#define scale 30 @@ -3657,27 +3921,28 @@ uint16_t WS2812FX::mode_noisepal(void) { // S //EVERY_N_MILLIS(10) { //(don't have to time this, effect function is only called every 24ms) nblendPaletteTowardPalette(palettes[0], palettes[1], 48); // Blend towards the target palette over 48 iterations. - if (SEGMENT.palette > 0) palettes[0] = currentPalette; + if (SEGMENT.palette > 0) palettes[0] = SEGPALETTE; - for(int i = 0; i < SEGLEN; i++) { + for (int i = 0; i < SEGLEN; i++) { uint8_t index = inoise8(i*scale, SEGENV.aux0+i*scale); // Get a value from the noise function. I'm using both x and y axis. color = ColorFromPalette(palettes[0], index, 255, LINEARBLEND); // Use the my own palette. - setPixelColor(i, color.red, color.green, color.blue); + SEGMENT.setPixelColor(i, color.red, color.green, color.blue); } SEGENV.aux0 += beatsin8(10,1,4); // Moving along the distance. Vary it a bit with a sine wave. return FRAMETIME; } +static const char _data_FX_MODE_NOISEPAL[] PROGMEM = "Noise Pal"; // Sine waves that have controllable phase change speed, frequency and cutoff. By Andrew Tuline. // SEGMENT.speed ->Speed, SEGMENT.intensity -> Frequency (SEGMENT.fft1 -> Color change, SEGMENT.fft2 -> PWM cutoff) // -uint16_t WS2812FX::mode_sinewave(void) { // Adjustable sinewave. By Andrew Tuline +uint16_t mode_sinewave(void) { // Adjustable sinewave. By Andrew Tuline //#define qsuba(x, b) ((x>b)?x-b:0) // Analog Unsigned subtraction macro. if result <0, then => 0 - uint16_t colorIndex = now /32;//(256 - SEGMENT.fft1); // Amount of colour change. + uint16_t colorIndex = strip.now /32;//(256 - SEGMENT.fft1); // Amount of colour change. SEGENV.step += SEGMENT.speed/16; // Speed of animation. uint16_t freq = SEGMENT.intensity/4;//SEGMENT.fft2/8; // Frequency of the signal. @@ -3685,22 +3950,23 @@ uint16_t WS2812FX::mode_sinewave(void) { // Adjustable sinewave. By for (int i=0; i> 2) +1); + counter = strip.now * ((SEGMENT.speed >> 2) +1); counter = counter >> 8; } @@ -3711,45 +3977,48 @@ uint16_t WS2812FX::mode_flow(void) uint16_t zoneLen = SEGLEN / zones; uint16_t offset = (SEGLEN - zones * zoneLen) >> 1; - fill(color_from_palette(-counter, false, true, 255)); + SEGMENT.fill(SEGMENT.color_from_palette(-counter, false, true, 255)); - for (uint16_t z = 0; z < zones; z++) + for (int z = 0; z < zones; z++) { uint16_t pos = offset + z * zoneLen; - for (uint16_t i = 0; i < zoneLen; i++) + for (int i = 0; i < zoneLen; i++) { uint8_t colorIndex = (i * 255 / zoneLen) - counter; uint16_t led = (z & 0x01) ? i : (zoneLen -1) -i; - if (IS_REVERSE) led = (zoneLen -1) -led; - setPixelColor(pos + led, color_from_palette(colorIndex, false, true, 255)); + if (SEGMENT.getOption(SEG_OPTION_REVERSED)) led = (zoneLen -1) -led; + SEGMENT.setPixelColor(pos + led, SEGMENT.color_from_palette(colorIndex, false, true, 255)); } } return FRAMETIME; } +static const char _data_FX_MODE_FLOW[] PROGMEM = "Flow@!,!;!,!,!;!;mp12=1,1d"; //vertical /* * Dots waving around in a sine/pendulum motion. * Little pixel birds flying in a circle. By Aircoookie */ -uint16_t WS2812FX::mode_chunchun(void) +uint16_t mode_chunchun(void) { - fill(SEGCOLOR(1)); - uint16_t counter = now*(6 + (SEGMENT.speed >> 4)); + SEGMENT.fill(SEGCOLOR(1)); + uint16_t counter = strip.now * (6 + (SEGMENT.speed >> 4)); uint16_t numBirds = 2 + (SEGLEN >> 3); // 2 + 1/8 of a segment uint16_t span = (SEGMENT.intensity << 8) / numBirds; - for (uint16_t i = 0; i < numBirds; i++) + for (int i = 0; i < numBirds; i++) { counter -= span; uint16_t megumin = sin16(counter) + 0x8000; - uint32_t bird = (megumin * SEGLEN) >> 16; - uint32_t c = color_from_palette((i * 255)/ numBirds, false, false, 0); // no palette wrapping - setPixelColor(bird, c); + uint16_t bird = uint32_t(megumin * SEGLEN) >> 16; + uint32_t c = SEGMENT.color_from_palette((i * 255)/ numBirds, false, false, 0); // no palette wrapping + SEGMENT.setPixelColor(bird, c); } return FRAMETIME; } +static const char _data_FX_MODE_CHUNCHUN[] PROGMEM = "Chunchun@!,Gap size;!,!,;!;1d"; + //13 bytes typedef struct Spotlight { @@ -3781,7 +4050,7 @@ typedef struct Spotlight { * * By Steve Pomeroy @xxv */ -uint16_t WS2812FX::mode_dancing_shadows(void) +uint16_t mode_dancing_shadows(void) { uint8_t numSpotlights = map(SEGMENT.intensity, 0, 255, 2, SPOT_MAX_COUNT); // 49 on 32 segment ESP32, 17 on 16 segment ESP8266 bool initialize = SEGENV.aux0 != numSpotlights; @@ -3791,12 +4060,12 @@ uint16_t WS2812FX::mode_dancing_shadows(void) if (!SEGENV.allocateData(dataSize)) return mode_static(); //allocation failed Spotlight* spotlights = reinterpret_cast(SEGENV.data); - fill(BLACK); + SEGMENT.fill(BLACK); unsigned long time = millis(); bool respawn = false; - for (uint8_t i = 0; i < numSpotlights; i++) { + for (size_t i = 0; i < numSpotlights; i++) { if (!initialize) { // advance the position of the spotlight int16_t delta = (float)(time - spotlights[i].lastUpdateTime) * @@ -3833,61 +4102,59 @@ uint16_t WS2812FX::mode_dancing_shadows(void) spotlights[i].type = random8(SPOT_TYPES_COUNT); } - uint32_t color = color_from_palette(spotlights[i].colorIdx, false, false, 0); + uint32_t color = SEGMENT.color_from_palette(spotlights[i].colorIdx, false, false, 0); int start = spotlights[i].position; if (spotlights[i].width <= 1) { if (start >= 0 && start < SEGLEN) { - blendPixelColor(start, color, 128); + SEGMENT.blendPixelColor(start, color, 128); // TODO } } else { switch (spotlights[i].type) { case SPOT_TYPE_SOLID: - for (uint8_t j = 0; j < spotlights[i].width; j++) { + for (size_t j = 0; j < spotlights[i].width; j++) { if ((start + j) >= 0 && (start + j) < SEGLEN) { - blendPixelColor(start + j, color, 128); + SEGMENT.blendPixelColor(start + j, color, 128); // TODO } } break; case SPOT_TYPE_GRADIENT: - for (uint8_t j = 0; j < spotlights[i].width; j++) { + for (size_t j = 0; j < spotlights[i].width; j++) { if ((start + j) >= 0 && (start + j) < SEGLEN) { - blendPixelColor(start + j, color, - cubicwave8(map(j, 0, spotlights[i].width - 1, 0, 255))); + SEGMENT.blendPixelColor(start + j, color, cubicwave8(map(j, 0, spotlights[i].width - 1, 0, 255))); // TODO } } break; case SPOT_TYPE_2X_GRADIENT: - for (uint8_t j = 0; j < spotlights[i].width; j++) { + for (size_t j = 0; j < spotlights[i].width; j++) { if ((start + j) >= 0 && (start + j) < SEGLEN) { - blendPixelColor(start + j, color, - cubicwave8(2 * map(j, 0, spotlights[i].width - 1, 0, 255))); + SEGMENT.blendPixelColor(start + j, color, cubicwave8(2 * map(j, 0, spotlights[i].width - 1, 0, 255))); // TODO } } break; case SPOT_TYPE_2X_DOT: - for (uint8_t j = 0; j < spotlights[i].width; j += 2) { + for (size_t j = 0; j < spotlights[i].width; j += 2) { if ((start + j) >= 0 && (start + j) < SEGLEN) { - blendPixelColor(start + j, color, 128); + SEGMENT.blendPixelColor(start + j, color, 128); // TODO } } break; case SPOT_TYPE_3X_DOT: - for (uint8_t j = 0; j < spotlights[i].width; j += 3) { + for (size_t j = 0; j < spotlights[i].width; j += 3) { if ((start + j) >= 0 && (start + j) < SEGLEN) { - blendPixelColor(start + j, color, 128); + SEGMENT.blendPixelColor(start + j, color, 128); // TODO } } break; case SPOT_TYPE_4X_DOT: - for (uint8_t j = 0; j < spotlights[i].width; j += 4) { + for (size_t j = 0; j < spotlights[i].width; j += 4) { if ((start + j) >= 0 && (start + j) < SEGLEN) { - blendPixelColor(start + j, color, 128); + SEGMENT.blendPixelColor(start + j, color, 128); // TODO } } break; @@ -3897,13 +4164,15 @@ uint16_t WS2812FX::mode_dancing_shadows(void) return FRAMETIME; } +static const char _data_FX_MODE_DANCING_SHADOWS[] PROGMEM = "Dancing Shadows@!,# of shadows;!,,;!;1d"; + /* Imitates a washing machine, rotating same waves forward, then pause, then backward. By Stefan Seegel */ -uint16_t WS2812FX::mode_washing_machine(void) { - float speed = tristate_square8(now >> 7, 90, 15); +uint16_t mode_washing_machine(void) { + float speed = tristate_square8(strip.now >> 7, 90, 15); float quot = 32.0f - ((float)SEGMENT.speed / 16.0f); speed /= quot; @@ -3911,37 +4180,41 @@ uint16_t WS2812FX::mode_washing_machine(void) { for (int i=0; i> 7)); - setPixelColor(i, color_from_palette(col, false, PALETTE_SOLID_WRAP, 3)); + SEGMENT.setPixelColor(i, SEGMENT.color_from_palette(col, false, PALETTE_SOLID_WRAP, 3)); } return FRAMETIME; } +static const char _data_FX_MODE_WASHING_MACHINE[] PROGMEM = "Washing Machine"; + /* Blends random colors across palette Modified, originally by Mark Kriegsman https://gist.github.com/kriegsman/1f7ccbbfa492a73c015e */ -uint16_t WS2812FX::mode_blends(void) { +uint16_t mode_blends(void) { uint16_t pixelLen = SEGLEN > UINT8_MAX ? UINT8_MAX : SEGLEN; uint16_t dataSize = sizeof(uint32_t) * (pixelLen + 1); // max segment length of 56 pixels on 16 segment ESP8266 if (!SEGENV.allocateData(dataSize)) return mode_static(); //allocation failed uint32_t* pixels = reinterpret_cast(SEGENV.data); uint8_t blendSpeed = map(SEGMENT.intensity, 0, UINT8_MAX, 10, 128); - uint8_t shift = (now * ((SEGMENT.speed >> 3) +1)) >> 8; + uint8_t shift = (strip.now * ((SEGMENT.speed >> 3) +1)) >> 8; for (int i = 0; i < pixelLen; i++) { - pixels[i] = color_blend(pixels[i], color_from_palette(shift + quadwave8((i + 1) * 16), false, PALETTE_SOLID_WRAP, 255), blendSpeed); + pixels[i] = color_blend(pixels[i], SEGMENT.color_from_palette(shift + quadwave8((i + 1) * 16), false, PALETTE_SOLID_WRAP, 255), blendSpeed); shift += 3; } uint16_t offset = 0; for (int i = 0; i < SEGLEN; i++) { - setPixelColor(i, pixels[offset++]); + SEGMENT.setPixelColor(i, pixels[offset++]); if (offset > pixelLen) offset = 0; } return FRAMETIME; } +static const char _data_FX_MODE_BLENDS[] PROGMEM = "Blends@Shift speed,Blend speed;1,2,3;!;1d"; + /* TV Simulator @@ -3968,7 +4241,7 @@ typedef struct TvSim { uint16_t pb = 0; } tvSim; -uint16_t WS2812FX::mode_tv_simulator(void) { +uint16_t mode_tv_simulator(void) { uint16_t nr, ng, nb, r, g, b, i, hue; uint8_t sat, bri, j; @@ -4056,7 +4329,7 @@ uint16_t WS2812FX::mode_tv_simulator(void) { // set strip color for (i = 0; i < SEGLEN; i++) { - setPixelColor(i, r >> 8, g >> 8, b >> 8); // Quantize to 8-bit + SEGMENT.setPixelColor(i, r >> 8, g >> 8, b >> 8); // Quantize to 8-bit } // if total duration has passed, remember last color and restart the loop @@ -4069,6 +4342,8 @@ uint16_t WS2812FX::mode_tv_simulator(void) { return FRAMETIME; } +static const char _data_FX_MODE_TV_SIMULATOR[] PROGMEM = "TV Simulator"; + /* Aurora effect @@ -4170,12 +4445,15 @@ class AuroraWave { }; }; -uint16_t WS2812FX::mode_aurora(void) { +uint16_t mode_aurora(void) { //aux1 = Wavecount //aux2 = Intensity in last loop AuroraWave* waves; +//TODO: I am not sure this is a correct way of handling memory allocation since if it fails on 1st run +// it will display static effect but on second run it may crash ESP since data will be nullptr + if(SEGENV.aux0 != SEGMENT.intensity || SEGENV.call == 0) { //Intensity slider changed or first call SEGENV.aux1 = map(SEGMENT.intensity, 0, 255, 2, W_MAX_COUNT); @@ -4187,20 +4465,20 @@ uint16_t WS2812FX::mode_aurora(void) { waves = reinterpret_cast(SEGENV.data); - for(int i = 0; i < SEGENV.aux1; i++) { - waves[i].init(SEGLEN, col_to_crgb(color_from_palette(random8(), false, false, random(0, 3)))); + for (int i = 0; i < SEGENV.aux1; i++) { + waves[i].init(SEGLEN, CRGB(SEGMENT.color_from_palette(random8(), false, false, random(0, 3)))); } } else { waves = reinterpret_cast(SEGENV.data); } - for(int i = 0; i < SEGENV.aux1; i++) { + for (int i = 0; i < SEGENV.aux1; i++) { //Update values of wave waves[i].update(SEGLEN, SEGMENT.speed); if(!(waves[i].stillAlive())) { //If a wave dies, reinitialize it starts over. - waves[i].init(SEGLEN, col_to_crgb(color_from_palette(random8(), false, false, random(0, 3)))); + waves[i].init(SEGLEN, CRGB(SEGMENT.color_from_palette(random8(), false, false, random(0, 3)))); } } @@ -4209,12 +4487,12 @@ uint16_t WS2812FX::mode_aurora(void) { if (SEGCOLOR(1)) backlight++; if (SEGCOLOR(2)) backlight++; //Loop through LEDs to determine color - for(int i = 0; i < SEGLEN; i++) { + for (int i = 0; i < SEGLEN; i++) { CRGB mixedRgb = CRGB(backlight, backlight, backlight); //For each LED we must check each wave if it is "active" at this position. //If there are multiple waves active on a LED we multiply their values. - for(int j = 0; j < SEGENV.aux1; j++) { + for (int j = 0; j < SEGENV.aux1; j++) { CRGB rgb = waves[j].getColorForLED(i); if(rgb != CRGB(0)) { @@ -4222,8 +4500,3011 @@ uint16_t WS2812FX::mode_aurora(void) { } } - setPixelColor(i, mixedRgb[0], mixedRgb[1], mixedRgb[2]); + SEGMENT.setPixelColor(i, mixedRgb[0], mixedRgb[1], mixedRgb[2]); } return FRAMETIME; -} \ No newline at end of file +} +static const char _data_FX_MODE_AURORA[] PROGMEM = "Aurora@!,!;1,2,3;!;sx=24,pal=50,1d"; + +// WLED-SR effects + +///////////////////////// +// Perlin Move // +///////////////////////// +// 16 bit perlinmove. Use Perlin Noise instead of sinewaves for movement. By Andrew Tuline. +// Controls are speed, # of pixels, faderate. +uint16_t mode_perlinmove(void) { + + SEGMENT.fade_out(255-SEGMENT.custom1); + for (int i = 0; i < SEGMENT.intensity/16 + 1; i++) { + uint16_t locn = inoise16(millis()*128/(260-SEGMENT.speed)+i*15000, millis()*128/(260-SEGMENT.speed)); // Get a new pixel location from moving noise. + uint16_t pixloc = map(locn, 50*256, 192*256, 0, SEGLEN-1); // Map that to the length of the strand, and ensure we don't go over. + SEGMENT.setPixelColor(pixloc, SEGMENT.color_from_palette(pixloc%255, false, PALETTE_SOLID_WRAP, 0)); + } + + return FRAMETIME; +} // mode_perlinmove() +static const char _data_FX_MODE_PERLINMOVE[] PROGMEM = "Perlin Move@!,# of pixels,fade rate;,!;!;1d"; + + +///////////////////////// +// Waveins // +///////////////////////// +// Uses beatsin8() + phase shifting. By: Andrew Tuline +uint16_t mode_wavesins(void) { + + for (int i = 0; i < SEGLEN; i++) { + uint8_t bri = sin8(millis()/4 + i * SEGMENT.intensity); + uint8_t index = beatsin8(SEGMENT.speed, SEGMENT.custom1, SEGMENT.custom1+SEGMENT.custom2, 0, i * (SEGMENT.custom3<<3)); // custom3 is reduced resolution slider + //SEGMENT.setPixelColor(i, ColorFromPalette(SEGPALETTE, index, bri, LINEARBLEND)); + SEGMENT.setPixelColor(i, SEGMENT.color_from_palette(index, false, PALETTE_SOLID_WRAP, 0, bri)); + } + + return FRAMETIME; +} // mode_waveins() +static const char _data_FX_MODE_WAVESINS[] PROGMEM = "Wavesins@!,Brightness variation,Starting color,Range of colors,Color variation;!;!;1d"; + + +////////////////////////////// +// Flow Stripe // +////////////////////////////// +// By: ldirko https://editor.soulmatelights.com/gallery/392-flow-led-stripe , modifed by: Andrew Tuline +uint16_t mode_FlowStripe(void) { + + const uint16_t hl = SEGLEN * 10 / 13; + uint8_t hue = millis() / (SEGMENT.speed+1); + uint32_t t = millis() / (SEGMENT.intensity/8+1); + + for (int i = 0; i < SEGLEN; i++) { + int c = (abs(i - hl) / hl) * 127; + c = sin8(c); + c = sin8(c / 2 + t); + byte b = sin8(c + t/8); + SEGMENT.setPixelColor(i, CHSV(b + hue, 255, 255)); + } + + return FRAMETIME; +} // mode_FlowStripe() +static const char _data_FX_MODE_FLOWSTRIPE[] PROGMEM = "Flow Stripe@Hue speed,Effect speed;;;1d"; + + +#ifndef WLED_DISABLE_2D +/////////////////////////////////////////////////////////////////////////////// +//*************************** 2D routines *********************************** +#define XY(x,y) SEGMENT.XY(x,y) + + +// Black hole +uint16_t mode_2DBlackHole(void) { // By: Stepko https://editor.soulmatelights.com/gallery/1012 , Modified by: Andrew Tuline + if (!strip.isMatrix) return mode_static(); // not a 2D set-up + + const uint16_t cols = SEGMENT.virtualWidth(); + const uint16_t rows = SEGMENT.virtualHeight(); + uint16_t x, y; + + // initialize on first call + if (SEGENV.call == 0) { + SEGMENT.setUpLeds(); + SEGMENT.fill(BLACK); + } + + SEGMENT.fadeToBlackBy(16 + (SEGMENT.speed>>3)); // create fading trails + float t = (float)(millis())/128; // timebase + // outer stars + for (size_t i = 0; i < 8; i++) { + x = beatsin8(SEGMENT.custom1>>3, 0, cols - 1, 0, ((i % 2) ? 128 : 0) + t * i); + y = beatsin8(SEGMENT.intensity>>3, 0, rows - 1, 0, ((i % 2) ? 192 : 64) + t * i); + SEGMENT.addPixelColorXY(x, y, CHSV(i*32, 255, 255)); + } + // inner stars + for (size_t i = 0; i < 4; i++) { + x = beatsin8(SEGMENT.custom2>>3, cols/4, cols - 1 - cols/4, 0, ((i % 2) ? 128 : 0) + t * i); + y = beatsin8(SEGMENT.custom3 , rows/4, rows - 1 - rows/4, 0, ((i % 2) ? 192 : 64) + t * i); + SEGMENT.addPixelColorXY(x, y, CHSV(i*32, 255, 255)); + } + // central white dot + SEGMENT.setPixelColorXY(cols/2, rows/2, CHSV(0, 0, 255)); + // blur everything a bit + SEGMENT.blur(16); + + return FRAMETIME; +} // mode_2DBlackHole() +static const char _data_FX_MODE_2DBLACKHOLE[] PROGMEM = "Black Hole@Fade rate,Outer Y freq.,Outer X freq.,Inner X freq.,Inner Y freq.;;;2d"; + + +//////////////////////////// +// 2D Colored Bursts // +//////////////////////////// +uint16_t mode_2DColoredBursts() { // By: ldirko https://editor.soulmatelights.com/gallery/819-colored-bursts , modified by: Andrew Tuline + if (!strip.isMatrix) return mode_static(); // not a 2D set-up + + const uint16_t cols = SEGMENT.virtualWidth(); + const uint16_t rows = SEGMENT.virtualHeight(); + + if (SEGENV.call == 0) { + SEGMENT.setUpLeds(); + SEGMENT.fill(BLACK); + SEGENV.aux0 = 0; // start with red hue + } + + bool dot = false; + bool grad = true; + + byte numLines = SEGMENT.intensity/16 + 1; + + SEGENV.aux0++; // hue + SEGMENT.fadeToBlackBy(40); + for (size_t i = 0; i < numLines; i++) { + byte x1 = beatsin8(2 + SEGMENT.speed/16, 0, (cols - 1)); + byte x2 = beatsin8(1 + SEGMENT.speed/16, 0, (cols - 1)); + byte y1 = beatsin8(5 + SEGMENT.speed/16, 0, (rows - 1), 0, i * 24); + byte y2 = beatsin8(3 + SEGMENT.speed/16, 0, (rows - 1), 0, i * 48 + 64); + CRGB color = ColorFromPalette(SEGPALETTE, i * 255 / numLines + (SEGENV.aux0&0xFF), 255, LINEARBLEND); + + byte xsteps = abs8(x1 - y1) + 1; + byte ysteps = abs8(x2 - y2) + 1; + byte steps = xsteps >= ysteps ? xsteps : ysteps; + + for (size_t i = 1; i <= steps; i++) { + byte dx = lerp8by8(x1, y1, i * 255 / steps); + byte dy = lerp8by8(x2, y2, i * 255 / steps); + SEGMENT.addPixelColorXY(dx, dy, color); // use setPixelColorXY for different look + if (grad) SEGMENT.fadePixelColorXY(dx, dy, (i * 255 / steps)); //Draw gradient line + } + + if (dot) { //add white point at the ends of line + SEGMENT.addPixelColorXY(x1, x2, WHITE); + SEGMENT.addPixelColorXY(y1, y2, WHITE); + } + } + SEGMENT.blur(4); + + return FRAMETIME; +} // mode_2DColoredBursts() +static const char _data_FX_MODE_2DCOLOREDBURSTS[] PROGMEM = "Colored Bursts@Speed,# of lines;;!;2d"; + + +///////////////////// +// 2D DNA // +///////////////////// +uint16_t mode_2Ddna(void) { // dna originally by by ldirko at https://pastebin.com/pCkkkzcs. Updated by Preyy. WLED conversion by Andrew Tuline. + if (!strip.isMatrix) return mode_static(); // not a 2D set-up + + const uint16_t cols = SEGMENT.virtualWidth(); + const uint16_t rows = SEGMENT.virtualHeight(); + + if (SEGENV.call == 0) { + SEGMENT.setUpLeds(); + SEGMENT.fill(BLACK); + } + + SEGMENT.fadeToBlackBy(64); + + for (int i = 0; i < cols; i++) { + SEGMENT.setPixelColorXY(i, beatsin8(SEGMENT.speed/8, 0, rows-1, 0, i*4 ), ColorFromPalette(SEGPALETTE, i*5+millis()/17, beatsin8(5, 55, 255, 0, i*10), LINEARBLEND)); + SEGMENT.setPixelColorXY(i, beatsin8(SEGMENT.speed/8, 0, rows-1, 0, i*4+128), ColorFromPalette(SEGPALETTE, i*5+128+millis()/17, beatsin8(5, 55, 255, 0, i*10+128), LINEARBLEND)); + } + SEGMENT.blur(SEGMENT.intensity>>3); + + return FRAMETIME; +} // mode_2Ddna() +static const char _data_FX_MODE_2DDNA[] PROGMEM = "DNA@Scroll speed,Blur;1,2,3;!;2d"; + + +///////////////////////// +// 2D DNA Spiral // +///////////////////////// +uint16_t mode_2DDNASpiral() { // By: ldirko https://editor.soulmatelights.com/gallery/810 , modified by: Andrew Tuline + if (!strip.isMatrix) return mode_static(); // not a 2D set-up + + const uint16_t cols = SEGMENT.virtualWidth(); + const uint16_t rows = SEGMENT.virtualHeight(); + + if (SEGENV.call == 0) { + SEGMENT.setUpLeds(); + SEGMENT.fill(BLACK); + SEGENV.aux0 = 0; // hue + } + + uint8_t speeds = SEGMENT.speed/2; + uint8_t freq = SEGMENT.intensity/8; + + uint32_t ms = millis() / 20; + SEGMENT.fadeToBlackBy(135); + + for (int i = 0; i < rows; i++) { + uint16_t x = beatsin8(speeds, 0, cols - 1, 0, i * freq) + beatsin8(speeds - 7, 0, cols - 1, 0, i * freq + 128); + uint16_t x1 = beatsin8(speeds, 0, cols - 1, 0, 128 + i * freq) + beatsin8(speeds - 7, 0, cols - 1, 0, 128 + 64 + i * freq); + SEGENV.aux0 = i * 128 / cols + ms; //ewowi20210629: not width - 1 to avoid crash if width = 1 + if ((i + ms / 8) & 3) { + x = x / 2; x1 = x1 / 2; + byte steps = abs8(x - x1) + 1; + for (size_t k = 1; k <= steps; k++) { + byte dx = lerp8by8(x, x1, k * 255 / steps); + SEGMENT.addPixelColorXY(dx, i, ColorFromPalette(SEGPALETTE, SEGENV.aux0, 255, LINEARBLEND)); + SEGMENT.fadePixelColorXY(dx, i, (k * 255 / steps)); + } + SEGMENT.addPixelColorXY(x, i, DARKSLATEGRAY); + SEGMENT.addPixelColorXY(x1, i, WHITE); + } + } + + return FRAMETIME; +} // mode_2DDNASpiral() +static const char _data_FX_MODE_2DDNASPIRAL[] PROGMEM = "DNA Spiral@Scroll speed,Y frequency;;!;2d"; + + +///////////////////////// +// 2D Drift // +///////////////////////// +uint16_t mode_2DDrift() { // By: Stepko https://editor.soulmatelights.com/gallery/884-drift , Modified by: Andrew Tuline + if (!strip.isMatrix) return mode_static(); // not a 2D set-up + + const uint16_t cols = SEGMENT.virtualWidth(); + const uint16_t rows = SEGMENT.virtualHeight(); + + if (SEGENV.call == 0) { + SEGMENT.setUpLeds(); + SEGMENT.fill(BLACK); + } + + SEGMENT.fadeToBlackBy(128); + + const uint16_t maxDim = MAX(cols, rows)/2; + unsigned long t = millis() / (32 - (SEGMENT.speed>>3)); + for (float i = 1; i < maxDim; i += 0.25) { + float angle = radians(t * (maxDim - i)); + uint16_t myX = (cols>>1) + (uint16_t)(sin_t(angle) * i) + (cols%2); + uint16_t myY = (rows>>1) + (uint16_t)(cos_t(angle) * i) + (rows%2); + SEGMENT.setPixelColorXY(myX, myY, ColorFromPalette(SEGPALETTE, (i * 20) + (t / 20), 255, LINEARBLEND)); + } + SEGMENT.blur(SEGMENT.intensity>>3); + + return FRAMETIME; +} // mode_2DDrift() +static const char _data_FX_MODE_2DDRIFT[] PROGMEM = "Drift@Rotation speed,Blur amount;;!;2d"; + + +////////////////////////// +// 2D Firenoise // +////////////////////////// +uint16_t mode_2Dfirenoise(void) { // firenoise2d. By Andrew Tuline. Yet another short routine. + if (!strip.isMatrix) return mode_static(); // not a 2D set-up + + const uint16_t cols = SEGMENT.virtualWidth(); + const uint16_t rows = SEGMENT.virtualHeight(); + + if (SEGENV.call == 0) { + SEGMENT.setUpLeds(); + SEGMENT.fill(BLACK); + } + + uint16_t xscale = SEGMENT.intensity*4; + uint32_t yscale = SEGMENT.speed*8; + uint8_t indexx = 0; + + SEGPALETTE = CRGBPalette16( CRGB(0,0,0), CRGB(0,0,0), CRGB(0,0,0), CRGB(0,0,0), + CRGB::Red, CRGB::Red, CRGB::Red, CRGB::DarkOrange, + CRGB::DarkOrange,CRGB::DarkOrange, CRGB::Orange, CRGB::Orange, + CRGB::Yellow, CRGB::Orange, CRGB::Yellow, CRGB::Yellow); + + for (int j=0; j < cols; j++) { + for (int i=0; i < rows; i++) { + indexx = inoise8(j*yscale*rows/255, i*xscale+millis()/4); // We're moving along our Perlin map. + SEGMENT.setPixelColorXY(j, i, ColorFromPalette(SEGPALETTE, min(i*(indexx)>>4, 255), i*255/cols, LINEARBLEND)); // With that value, look up the 8 bit colour palette value and assign it to the current LED. + } // for i + } // for j + + return FRAMETIME; +} // mode_2Dfirenoise() +static const char _data_FX_MODE_2DFIRENOISE[] PROGMEM = "Firenoise@X scale,Y scale;;;2d"; + + +////////////////////////////// +// 2D Frizzles // +////////////////////////////// +uint16_t mode_2DFrizzles(void) { // By: Stepko https://editor.soulmatelights.com/gallery/640-color-frizzles , Modified by: Andrew Tuline + if (!strip.isMatrix) return mode_static(); // not a 2D set-up + + const uint16_t cols = SEGMENT.virtualWidth(); + const uint16_t rows = SEGMENT.virtualHeight(); + + if (SEGENV.call == 0) { + SEGMENT.setUpLeds(); + SEGMENT.fill(BLACK); + } + + SEGMENT.fadeToBlackBy(16); + for (size_t i = 8; i > 0; i--) { + SEGMENT.addPixelColorXY(beatsin8(SEGMENT.speed/8 + i, 0, cols - 1), + beatsin8(SEGMENT.intensity/8 - i, 0, rows - 1), + ColorFromPalette(SEGPALETTE, beatsin8(12, 0, 255), 255, LINEARBLEND)); + } + SEGMENT.blur(SEGMENT.custom1>>3); + + return FRAMETIME; +} // mode_2DFrizzles() +static const char _data_FX_MODE_2DFRIZZLES[] PROGMEM = "Frizzles@X frequency,Y frequency,Blur;;!;2d"; + + +/////////////////////////////////////////// +// 2D Cellular Automata Game of life // +/////////////////////////////////////////// +typedef struct ColorCount { + CRGB color; + int8_t count; +} colorCount; + +uint16_t mode_2Dgameoflife(void) { // Written by Ewoud Wijma, inspired by https://natureofcode.com/book/chapter-7-cellular-automata/ and https://github.com/DougHaber/nlife-color + if (!strip.isMatrix) return mode_static(); // not a 2D set-up + + const uint16_t cols = SEGMENT.virtualWidth(); + const uint16_t rows = SEGMENT.virtualHeight(); + const uint16_t dataSize = sizeof(CRGB) * SEGMENT.length(); // using width*height prevents reallocation if mirroring is enabled + + if (!SEGENV.allocateData(dataSize + sizeof(unsigned long))) return mode_static(); //allocation failed + CRGB *prevLeds = reinterpret_cast(SEGENV.data); + unsigned long *resetMillis = reinterpret_cast(SEGENV.data + dataSize); // triggers reset + + CRGB backgroundColor = SEGCOLOR(1); + + if (SEGENV.call == 0 || strip.now - *resetMillis > 5000) { + *resetMillis = strip.now; + + random16_set_seed(strip.now); //seed the random generator + + //give the leds random state and colors (based on intensity, colors from palette or all posible colors are chosen) + for (int x = 0; x < cols; x++) for (int y = 0; y < rows; y++) { + uint8_t state = random8()%2; + if (state == 0) + SEGMENT.setPixelColorXY(x,y, backgroundColor); + else + SEGMENT.setPixelColorXY(x,y, SEGMENT.color_from_palette(random8(), false, PALETTE_SOLID_WRAP, 0)); + } + + for (int y = 0; y < rows; y++) for (int x = 0; x < cols; x++) prevLeds[XY(x,y)] = CRGB::Black; + + + SEGENV.aux1 = 0; + SEGENV.aux0 = 0xFFFF; + } + + //copy previous leds (save previous generation) + for (int x = 0; x < cols; x++) for (int y = 0; y < rows; y++) prevLeds[XY(x,y)] = SEGMENT.getPixelColorXY(x,y); + + //calculate new leds + for (int x = 0; x < cols; x++) for (int y = 0; y < rows; y++) { + colorCount colorsCount[9];//count the different colors in the 9*9 matrix + for (int i=0; i<9; i++) colorsCount[i] = {backgroundColor, 0}; //init colorsCount + + //iterate through neighbors and count them and their different colors + int neighbors = 0; + for (int i = -1; i <= 1; i++) for (int j = -1; j <= 1; j++) { //iterate through 9*9 matrix + // wrap around segment + int16_t xx = x+i, yy = y+j; + if (x+i < 0) xx = cols-1; else if (x+i >= cols) xx = 0; + if (y+j < 0) yy = rows-1; else if (y+j >= rows) yy = 0; + uint16_t xy = XY(xx, yy); // previous cell xy to check + + // count different neighbours and colors, except the centre cell + if (xy != XY(x,y) && prevLeds[xy] != backgroundColor) { + neighbors++; + bool colorFound = false; + int k; + for (k=0; k<9 && colorsCount[i].count != 0; k++) + if (colorsCount[k].color == prevLeds[xy]) { + colorsCount[k].count++; + colorFound = true; + } + + if (!colorFound) colorsCount[k] = {prevLeds[xy], 1}; //add new color found in the array + } + } // i,j + + // Rules of Life + uint32_t col = SEGMENT.getPixelColorXY(x,y); + uint32_t bgc = RGBW32(backgroundColor.r, backgroundColor.g, backgroundColor.b, 0); + if ((col != bgc) && (neighbors < 2)) SEGMENT.setPixelColorXY(x,y, bgc); // Loneliness + else if ((col != bgc) && (neighbors > 3)) SEGMENT.setPixelColorXY(x,y, bgc); // Overpopulation + else if ((col == bgc) && (neighbors == 3)) { // Reproduction + //find dominantcolor and assign to cell + colorCount dominantColorCount = {backgroundColor, 0}; + for (int i=0; i<9 && colorsCount[i].count != 0; i++) + if (colorsCount[i].count > dominantColorCount.count) dominantColorCount = colorsCount[i]; + if (dominantColorCount.count > 0) SEGMENT.setPixelColorXY(x,y, dominantColorCount.color); //assign the dominant color + } + // else do nothing! + } //x,y + + // calculate CRC16 of leds + uint16_t crc = crc16((const unsigned char*)prevLeds, dataSize-1); //ewowi: prevLeds instead of leds work as well, tbd: compare more patterns, see SR! + + // check if we had same CRC and reset if needed + // same CRC would mean image did not change or was repeating itself + if (!(crc == SEGENV.aux0 || crc == SEGENV.aux1)) *resetMillis = strip.now; //if no repetition avoid reset + // remember last two + SEGENV.aux1 = SEGENV.aux0; + SEGENV.aux0 = crc; + + return (SEGMENT.getOption(SEG_OPTION_TRANSITIONAL)) ? FRAMETIME : FRAMETIME_FIXED * (128-(SEGMENT.speed>>1)); // update only when appropriate time passes (in 42 FPS slots) +} // mode_2Dgameoflife() +static const char _data_FX_MODE_2DGAMEOFLIFE[] PROGMEM = "Game Of Life@!,;!,!;!;2d"; + + +///////////////////////// +// 2D Hiphotic // +///////////////////////// +uint16_t mode_2DHiphotic() { // By: ldirko https://editor.soulmatelights.com/gallery/810 , Modified by: Andrew Tuline + if (!strip.isMatrix) return mode_static(); // not a 2D set-up + + const uint16_t cols = SEGMENT.virtualWidth(); + const uint16_t rows = SEGMENT.virtualHeight(); + const uint32_t a = strip.now / 8; + + for (int x = 0; x < cols; x++) { + for (int y = 0; y < rows; y++) { + SEGMENT.setPixelColorXY(x, y, SEGMENT.color_from_palette(sin8(cos8(x * SEGMENT.speed/16 + a / 3) + sin8(y * SEGMENT.intensity/16 + a / 4) + a), false, PALETTE_SOLID_WRAP, 0)); + } + } + + return FRAMETIME; +} // mode_2DHiphotic() +static const char _data_FX_MODE_2DHIPHOTIC[] PROGMEM = "Hiphotic@X scale,Y scale;;!;2d"; + + +///////////////////////// +// 2D Julia // +///////////////////////// +// Sliders are: +// intensity = Maximum number of iterations per pixel. +// Custom1 = Location of X centerpoint +// Custom2 = Location of Y centerpoint +// Custom3 = Size of the area (small value = smaller area) +typedef struct Julia { + float xcen; + float ycen; + float xymag; +} julia; + +uint16_t mode_2DJulia(void) { // An animated Julia set by Andrew Tuline. + if (!strip.isMatrix) return mode_static(); // not a 2D set-up + + const uint16_t cols = SEGMENT.virtualWidth(); + const uint16_t rows = SEGMENT.virtualHeight(); + + if (!SEGENV.allocateData(sizeof(julia))) return mode_static(); + Julia* julias = reinterpret_cast(SEGENV.data); + + float reAl; + float imAg; + + if (SEGENV.call == 0) { // Reset the center if we've just re-started this animation. + julias->xcen = 0.; + julias->ycen = 0.; + julias->xymag = 1.0; + + SEGMENT.custom1 = 128; // Make sure the location widgets are centered to start. + SEGMENT.custom2 = 128; + SEGMENT.custom3 = 16; + SEGMENT.intensity = 24; + } + + julias->xcen = julias->xcen + (float)(SEGMENT.custom1 - 128)/100000.f; + julias->ycen = julias->ycen + (float)(SEGMENT.custom2 - 128)/100000.f; + julias->xymag = julias->xymag + (float)((SEGMENT.custom3 - 16)<<3)/100000.f; // reduced resolution slider + if (julias->xymag < 0.01f) julias->xymag = 0.01f; + if (julias->xymag > 1.0f) julias->xymag = 1.0f; + + float xmin = julias->xcen - julias->xymag; + float xmax = julias->xcen + julias->xymag; + float ymin = julias->ycen - julias->xymag; + float ymax = julias->ycen + julias->xymag; + + // Whole set should be within -1.2,1.2 to -.8 to 1. + xmin = constrain(xmin, -1.2f, 1.2f); + xmax = constrain(xmax, -1.2f, 1.2f); + ymin = constrain(ymin, -0.8f, 1.0f); + ymax = constrain(ymax, -0.8f, 1.0f); + + float dx; // Delta x is mapped to the matrix size. + float dy; // Delta y is mapped to the matrix size. + + int maxIterations = 15; // How many iterations per pixel before we give up. Make it 8 bits to match our range of colours. + float maxCalc = 16.0; // How big is each calculation allowed to be before we give up. + + maxIterations = SEGMENT.intensity/2; + + + // Resize section on the fly for some animaton. + reAl = -0.94299f; // PixelBlaze example + imAg = 0.3162f; + + reAl += sin_t((float)millis()/305.f)/20.f; + imAg += sin_t((float)millis()/405.f)/20.f; + + dx = (xmax - xmin) / (cols); // Scale the delta x and y values to our matrix size. + dy = (ymax - ymin) / (rows); + + // Start y + float y = ymin; + for (int j = 0; j < rows; j++) { + + // Start x + float x = xmin; + for (int i = 0; i < cols; i++) { + + // Now we test, as we iterate z = z^2 + c does z tend towards infinity? + float a = x; + float b = y; + int iter = 0; + + while (iter < maxIterations) { // Here we determine whether or not we're out of bounds. + float aa = a * a; + float bb = b * b; + float len = aa + bb; + if (len > maxCalc) { // |z| = sqrt(a^2+b^2) OR z^2 = a^2+b^2 to save on having to perform a square root. + break; // Bail + } + + // This operation corresponds to z -> z^2+c where z=a+ib c=(x,y). Remember to use 'foil'. + b = 2*a*b + imAg; + a = aa - bb + reAl; + iter++; + } // while + + // We color each pixel based on how long it takes to get to infinity, or black if it never gets there. + if (iter == maxIterations) { + SEGMENT.setPixelColorXY(i, j, 0); + } else { + SEGMENT.setPixelColorXY(i, j, SEGMENT.color_from_palette(iter*255/maxIterations, false, PALETTE_SOLID_WRAP, 0)); + } + x += dx; + } + y += dy; + } +// SEGMENT.blur(64); + + return FRAMETIME; +} // mode_2DJulia() +static const char _data_FX_MODE_2DJULIA[] PROGMEM = "Julia@,Max iterations per pixel,X center,Y center,Area size;;!;ix=24,c1=128,c2=128,c3=16,2d"; + + +////////////////////////////// +// 2D Lissajous // +////////////////////////////// +uint16_t mode_2DLissajous(void) { // By: Andrew Tuline + if (!strip.isMatrix) return mode_static(); // not a 2D set-up + + const uint16_t cols = SEGMENT.virtualWidth(); + const uint16_t rows = SEGMENT.virtualHeight(); + + SEGMENT.fadeToBlackBy(SEGMENT.intensity); + + //for (int i=0; i < 4*(cols+rows); i ++) { + for (int i=0; i < 256; i ++) { + //float xlocn = float(sin8(now/4+i*(SEGMENT.speed>>5))) / 255.0f; + //float ylocn = float(cos8(now/4+i*2)) / 255.0f; + uint8_t xlocn = sin8(strip.now/2+i*(SEGMENT.speed>>5)); + uint8_t ylocn = cos8(strip.now/2+i*2); + xlocn = map(xlocn,0,255,0,cols-1); + ylocn = map(ylocn,0,255,0,rows-1); + SEGMENT.setPixelColorXY(xlocn, ylocn, SEGMENT.color_from_palette(strip.now/100+i, false, PALETTE_SOLID_WRAP, 0)); + } + + return FRAMETIME; +} // mode_2DLissajous() +static const char _data_FX_MODE_2DLISSAJOUS[] PROGMEM = "Lissajous@X frequency,Fade rate;!,!,!;!;2d"; + + +/////////////////////// +// 2D Matrix // +/////////////////////// +uint16_t mode_2Dmatrix(void) { // Matrix2D. By Jeremy Williams. Adapted by Andrew Tuline & improved by merkisoft and ewowi. + if (!strip.isMatrix) return mode_static(); // not a 2D set-up + + const uint16_t cols = SEGMENT.virtualWidth(); + const uint16_t rows = SEGMENT.virtualHeight(); + + if (SEGENV.call == 0) { + SEGMENT.setUpLeds(); + SEGMENT.fill(BLACK); + } + + uint8_t fade = map(SEGMENT.custom1, 0, 255, 50, 250); // equals trail size + uint8_t speed = (256-SEGMENT.speed) >> map(MIN(rows, 150), 0, 150, 0, 3); // slower speeds for small displays + + CRGB spawnColor; + CRGB trailColor; + if (SEGMENT.check1) { + spawnColor = SEGCOLOR(0); + trailColor = SEGCOLOR(1); + } else { + spawnColor = CRGB(175,255,175); + trailColor = CRGB(27,130,39); + } + + if (strip.now - SEGENV.step >= speed) { + SEGENV.step = strip.now; + for (int row=rows-1; row>=0; row--) { + for (int col=0; col>6)); + + // get some 2 random moving points + uint8_t x2 = inoise8(strip.now * speed, 25355, 685 ) / 16; + uint8_t y2 = inoise8(strip.now * speed, 355, 11685 ) / 16; + + uint8_t x3 = inoise8(strip.now * speed, 55355, 6685 ) / 16; + uint8_t y3 = inoise8(strip.now * speed, 25355, 22685 ) / 16; + + // and one Lissajou function + uint8_t x1 = beatsin8(23 * speed, 0, 15); + uint8_t y1 = beatsin8(28 * speed, 0, 15); + + for (int y = 0; y < rows; y++) { + for (int x = 0; x < cols; x++) { + // calculate distances of the 3 points from actual pixel + // and add them together with weightening + uint16_t dx = abs(x - x1); + uint16_t dy = abs(y - y1); + uint16_t dist = 2 * sqrt16((dx * dx) + (dy * dy)); + + dx = abs(x - x2); + dy = abs(y - y2); + dist += sqrt16((dx * dx) + (dy * dy)); + + dx = abs(x - x3); + dy = abs(y - y3); + dist += sqrt16((dx * dx) + (dy * dy)); + + // inverse result + byte color = 1000 / dist; + + // map color between thresholds + if (color > 0 and color < 60) { + SEGMENT.setPixelColorXY(x, y, SEGMENT.color_from_palette(map(color * 9, 9, 531, 0, 255), false, PALETTE_SOLID_WRAP, 0)); + } else { + SEGMENT.setPixelColorXY(x, y, SEGMENT.color_from_palette(0, false, PALETTE_SOLID_WRAP, 0)); + } + // show the 3 points, too + SEGMENT.setPixelColorXY(x1, y1, WHITE); + SEGMENT.setPixelColorXY(x2, y2, WHITE); + SEGMENT.setPixelColorXY(x3, y3, WHITE); + } + } + + return FRAMETIME; +} // mode_2Dmetaballs() +static const char _data_FX_MODE_2DMETABALLS[] PROGMEM = "Metaballs@Speed;!,!,!;!;2d"; + + +////////////////////// +// 2D Noise // +////////////////////// +uint16_t mode_2Dnoise(void) { // By Andrew Tuline + if (!strip.isMatrix) return mode_static(); // not a 2D set-up + + const uint16_t cols = SEGMENT.virtualWidth(); + const uint16_t rows = SEGMENT.virtualHeight(); + + const uint16_t scale = SEGMENT.intensity+2; + + for (int y = 0; y < rows; y++) { + for (int x = 0; x < cols; x++) { + uint8_t pixelHue8 = inoise8(x * scale, y * scale, millis() / (16 - SEGMENT.speed/16)); + SEGMENT.setPixelColorXY(x, y, ColorFromPalette(SEGPALETTE, pixelHue8)); + } + } + + return FRAMETIME; +} // mode_2Dnoise() +static const char _data_FX_MODE_2DNOISE[] PROGMEM = "Noise2D@Speed,Scale;!,!,!;!;2d"; + + +////////////////////////////// +// 2D Plasma Ball // +////////////////////////////// +uint16_t mode_2DPlasmaball(void) { // By: Stepko https://editor.soulmatelights.com/gallery/659-plasm-ball , Modified by: Andrew Tuline + if (!strip.isMatrix) return mode_static(); // not a 2D set-up + + const uint16_t cols = SEGMENT.virtualWidth(); + const uint16_t rows = SEGMENT.virtualHeight(); + + if (SEGENV.call == 0) { + SEGMENT.setUpLeds(); + SEGMENT.fill(BLACK); + } + + SEGMENT.fadeToBlackBy(SEGMENT.custom1>>2); + + float t = millis() / (33 - SEGMENT.speed/8); + for (int i = 0; i < cols; i++) { + uint16_t thisVal = inoise8(i * 30, t, t); + uint16_t thisMax = map(thisVal, 0, 255, 0, cols-1); + for (int j = 0; j < rows; j++) { + uint16_t thisVal_ = inoise8(t, j * 30, t); + uint16_t thisMax_ = map(thisVal_, 0, 255, 0, rows-1); + uint16_t x = (i + thisMax_ - cols / 2); + uint16_t y = (j + thisMax - cols / 2); + uint16_t cx = (i + thisMax_); + uint16_t cy = (j + thisMax); + + SEGMENT.addPixelColorXY(i, j, ((x - y > -2) && (x - y < 2)) || + ((cols - 1 - x - y) > -2 && (cols - 1 - x - y < 2)) || + (cols - cx == 0) || + (cols - 1 - cx == 0) || + ((rows - cy == 0) || + (rows - 1 - cy == 0)) ? ColorFromPalette(SEGPALETTE, beat8(5), thisVal, LINEARBLEND) : CRGB::Black); + } + } + SEGMENT.blur(SEGMENT.custom2>>5); + + return FRAMETIME; +} // mode_2DPlasmaball() +static const char _data_FX_MODE_2DPLASMABALL[] PROGMEM = "Plasma Ball@Speed,,Fade,Blur;!,!,!;!;2d"; + + +//////////////////////////////// +// 2D Polar Lights // +//////////////////////////////// +//static float fmap(const float x, const float in_min, const float in_max, const float out_min, const float out_max) { +// return (out_max - out_min) * (x - in_min) / (in_max - in_min) + out_min; +//} +uint16_t mode_2DPolarLights(void) { // By: Kostyantyn Matviyevskyy https://editor.soulmatelights.com/gallery/762-polar-lights , Modified by: Andrew Tuline + if (!strip.isMatrix) return mode_static(); // not a 2D set-up + + const uint16_t cols = SEGMENT.virtualWidth(); + const uint16_t rows = SEGMENT.virtualHeight(); + + CRGBPalette16 auroraPalette = {0x000000, 0x003300, 0x006600, 0x009900, 0x00cc00, 0x00ff00, 0x33ff00, 0x66ff00, 0x99ff00, 0xccff00, 0xffff00, 0xffcc00, 0xff9900, 0xff6600, 0xff3300, 0xff0000}; + + if (SEGENV.call == 0) { + SEGMENT.setUpLeds(); + SEGMENT.fill(BLACK); + SEGENV.step = 0; + } + + float adjustHeight = (float)map(rows, 8, 32, 28, 12); + uint16_t adjScale = map(cols, 8, 64, 310, 63); +/* + if (SEGENV.aux1 != SEGMENT.custom1/12) { // Hacky palette rotation. We need that black. + SEGENV.aux1 = SEGMENT.custom1/12; + for (int i = 0; i < 16; i++) { + long ilk; + ilk = (long)currentPalette[i].r << 16; + ilk += (long)currentPalette[i].g << 8; + ilk += (long)currentPalette[i].b; + ilk = (ilk << SEGENV.aux1) | (ilk >> (24 - SEGENV.aux1)); + currentPalette[i].r = ilk >> 16; + currentPalette[i].g = ilk >> 8; + currentPalette[i].b = ilk; + } + } +*/ + uint16_t _scale = map(SEGMENT.intensity, 0, 255, 30, adjScale); + byte _speed = map(SEGMENT.speed, 0, 255, 128, 16); + + for (int x = 0; x < cols; x++) { + for (int y = 0; y < rows; y++) { + SEGENV.step++; + SEGMENT.setPixelColorXY(x, y, ColorFromPalette(auroraPalette, + qsub8( + inoise8((SEGENV.step%2) + x * _scale, y * 16 + SEGENV.step % 16, SEGENV.step / _speed), + fabs((float)rows / 2 - (float)y) * adjustHeight))); + } + } + + return FRAMETIME; +} // mode_2DPolarLights() +static const char _data_FX_MODE_2DPOLARLIGHTS[] PROGMEM = "Polar Lights@Speed,Scale;;;2d"; + + +///////////////////////// +// 2D Pulser // +///////////////////////// +uint16_t mode_2DPulser(void) { // By: ldirko https://editor.soulmatelights.com/gallery/878-pulse-test , modifed by: Andrew Tuline + if (!strip.isMatrix) return mode_static(); // not a 2D set-up + + //const uint16_t cols = SEGMENT.virtualWidth(); + const uint16_t rows = SEGMENT.virtualHeight(); + + if (SEGENV.call == 0) { + SEGMENT.setUpLeds(); + SEGMENT.fill(BLACK); + } + + SEGMENT.fadeToBlackBy(8 - (SEGMENT.intensity>>5)); + + uint16_t a = strip.now / (18 - SEGMENT.speed / 16); + uint16_t x = (a / 14); + uint16_t y = map((sin8(a * 5) + sin8(a * 4) + sin8(a * 2)), 0, 765, rows-1, 0); + SEGMENT.setPixelColorXY(x, y, ColorFromPalette(SEGPALETTE, map(y, 0, rows-1, 0, 255), 255, LINEARBLEND)); + + SEGMENT.blur(1 + (SEGMENT.intensity>>4)); + + return FRAMETIME; +} // mode_2DPulser() +static const char _data_FX_MODE_2DPULSER[] PROGMEM = "Pulser@Speed,Blur;;!;2d"; + + +///////////////////////// +// 2D Sindots // +///////////////////////// +uint16_t mode_2DSindots(void) { // By: ldirko https://editor.soulmatelights.com/gallery/597-sin-dots , modified by: Andrew Tuline + if (!strip.isMatrix) return mode_static(); // not a 2D set-up + + const uint16_t cols = SEGMENT.virtualWidth(); + const uint16_t rows = SEGMENT.virtualHeight(); + + if (SEGENV.call == 0) { + SEGMENT.setUpLeds(); + SEGMENT.fill(BLACK); + } + + SEGMENT.fadeToBlackBy(SEGMENT.custom1>>3); + + byte t1 = millis() / (257 - SEGMENT.speed); // 20; + byte t2 = sin8(t1) / 4 * 2; + for (int i = 0; i < 13; i++) { + byte x = sin8(t1 + i * SEGMENT.intensity/8)*(cols-1)/255; // max index now 255x15/255=15! + byte y = sin8(t2 + i * SEGMENT.intensity/8)*(rows-1)/255; // max index now 255x15/255=15! + SEGMENT.setPixelColorXY(x, y, ColorFromPalette(SEGPALETTE, i * 255 / 13, 255, LINEARBLEND)); + } + SEGMENT.blur(SEGMENT.custom2>>3); + + return FRAMETIME; +} // mode_2DSindots() +static const char _data_FX_MODE_2DSINDOTS[] PROGMEM = "Sindots@Speed,Dot distance,Fade rate,Blur;;!;2d"; + + +////////////////////////////// +// 2D Squared Swirl // +////////////////////////////// +// custom3 affects the blur amount. +uint16_t mode_2Dsquaredswirl(void) { // By: Mark Kriegsman. https://gist.github.com/kriegsman/368b316c55221134b160 + // Modifed by: Andrew Tuline + if (!strip.isMatrix) return mode_static(); // not a 2D set-up + + const uint16_t cols = SEGMENT.virtualWidth(); + const uint16_t rows = SEGMENT.virtualHeight(); + + if (SEGENV.call == 0) { + SEGMENT.setUpLeds(); + SEGMENT.fill(BLACK); + } + + const uint8_t kBorderWidth = 2; + + SEGMENT.fadeToBlackBy(24); + + uint8_t blurAmount = SEGMENT.custom3>>1; // reduced resolution slider + SEGMENT.blur(blurAmount); + + // Use two out-of-sync sine waves + uint8_t i = beatsin8(19, kBorderWidth, cols-kBorderWidth); + uint8_t j = beatsin8(22, kBorderWidth, cols-kBorderWidth); + uint8_t k = beatsin8(17, kBorderWidth, cols-kBorderWidth); + uint8_t m = beatsin8(18, kBorderWidth, rows-kBorderWidth); + uint8_t n = beatsin8(15, kBorderWidth, rows-kBorderWidth); + uint8_t p = beatsin8(20, kBorderWidth, rows-kBorderWidth); + + uint16_t ms = millis(); + + SEGMENT.addPixelColorXY(i, m, ColorFromPalette(SEGPALETTE, ms/29, 255, LINEARBLEND)); + SEGMENT.addPixelColorXY(j, n, ColorFromPalette(SEGPALETTE, ms/41, 255, LINEARBLEND)); + SEGMENT.addPixelColorXY(k, p, ColorFromPalette(SEGPALETTE, ms/73, 255, LINEARBLEND)); + + return FRAMETIME; +} // mode_2Dsquaredswirl() +static const char _data_FX_MODE_2DSQUAREDSWIRL[] PROGMEM = "Squared Swirl@,,,,Blur;,,;!;2d"; + + +////////////////////////////// +// 2D Sun Radiation // +////////////////////////////// +uint16_t mode_2DSunradiation(void) { // By: ldirko https://editor.soulmatelights.com/gallery/599-sun-radiation , modified by: Andrew Tuline + if (!strip.isMatrix) return mode_static(); // not a 2D set-up + + const uint16_t cols = SEGMENT.virtualWidth(); + const uint16_t rows = SEGMENT.virtualHeight(); + + if (!SEGENV.allocateData(sizeof(byte)*(cols+2)*(rows+2))) return mode_static(); //allocation failed + byte *bump = reinterpret_cast(SEGENV.data); + + if (SEGENV.call == 0) { + SEGMENT.setUpLeds(); + SEGMENT.fill(BLACK); + } + + unsigned long t = millis() / 4; + int index = 0; + uint8_t someVal = SEGMENT.speed/4; // Was 25. + for (int j = 0; j < (rows + 2); j++) { + for (int i = 0; i < (cols + 2); i++) { + byte col = (inoise8_raw(i * someVal, j * someVal, t)) / 2; + bump[index++] = col; + } + } + + int yindex = cols + 3; + int16_t vly = -(rows / 2 + 1); + for (int y = 0; y < rows; y++) { + ++vly; + int16_t vlx = -(cols / 2 + 1); + for (int x = 0; x < cols; x++) { + ++vlx; + int8_t nx = bump[x + yindex + 1] - bump[x + yindex - 1]; + int8_t ny = bump[x + yindex + (cols + 2)] - bump[x + yindex - (cols + 2)]; + byte difx = abs8(vlx * 7 - nx); + byte dify = abs8(vly * 7 - ny); + int temp = difx * difx + dify * dify; + int col = 255 - temp / 8; //8 its a size of effect + if (col < 0) col = 0; + SEGMENT.setPixelColorXY(x, y, HeatColor(col / (3.0f-(float)(SEGMENT.intensity)/128.f))); + } + yindex += (cols + 2); + } + + return FRAMETIME; +} // mode_2DSunradiation() +static const char _data_FX_MODE_2DSUNRADIATION[] PROGMEM = "Sun Radiation@Variance,Brightness;;;2d"; + + +///////////////////////// +// 2D Tartan // +///////////////////////// +uint16_t mode_2Dtartan(void) { // By: Elliott Kember https://editor.soulmatelights.com/gallery/3-tartan , Modified by: Andrew Tuline + if (!strip.isMatrix) return mode_static(); // not a 2D set-up + + const uint16_t cols = SEGMENT.virtualWidth(); + const uint16_t rows = SEGMENT.virtualHeight(); + + if (SEGENV.call == 0) { + SEGMENT.setUpLeds(); + SEGMENT.fill(BLACK); + } + + uint8_t hue; + int offsetX = beatsin16(3, -360, 360); + int offsetY = beatsin16(2, -360, 360); + + for (int x = 0; x < cols; x++) { + for (int y = 0; y < rows; y++) { + hue = x * beatsin16(10, 1, 10) + offsetY; + SEGMENT.setPixelColorXY(x, y, ColorFromPalette(SEGPALETTE, hue, sin8(x * SEGMENT.speed + offsetX) * sin8(x * SEGMENT.speed + offsetX) / 255, LINEARBLEND)); + hue = y * 3 + offsetX; + SEGMENT.addPixelColorXY(x, y, ColorFromPalette(SEGPALETTE, hue, sin8(y * SEGMENT.intensity + offsetY) * sin8(y * SEGMENT.intensity + offsetY) / 255, LINEARBLEND)); + } + } + + return FRAMETIME; +} // mode_2DTartan() +static const char _data_FX_MODE_2DTARTAN[] PROGMEM = "Tartan@X scale,Y scale;;!;2d"; + + +///////////////////////// +// 2D spaceships // +///////////////////////// +uint16_t mode_2Dspaceships(void) { //// Space ships by stepko (c)05.02.21 [https://editor.soulmatelights.com/gallery/639-space-ships], adapted by Blaz Kristan (AKA blazoncek) + if (!strip.isMatrix) return mode_static(); // not a 2D set-up + + const uint16_t cols = SEGMENT.virtualWidth(); + const uint16_t rows = SEGMENT.virtualHeight(); + + if (SEGENV.call == 0) { + SEGMENT.setUpLeds(); + SEGMENT.fill(BLACK); + } + + uint32_t tb = strip.now >> 12; // every ~4s + if (tb > SEGENV.step) { + int8_t dir = ++SEGENV.aux0; + dir += (int)random8(3)-1; + if (dir > 7) SEGENV.aux0 = 0; + else if (dir < 0) SEGENV.aux0 = 7; + else SEGENV.aux0 = dir; + SEGENV.step = tb + random8(4); + } + + SEGMENT.fadeToBlackBy(map(SEGMENT.speed, 0, 255, 248, 16)); + SEGMENT.move(SEGENV.aux0, 1); + + for (size_t i = 0; i < 8; i++) { + byte x = beatsin8(12 + i, 2, cols - 3); + byte y = beatsin8(15 + i, 2, rows - 3); + CRGB color = ColorFromPalette(SEGPALETTE, beatsin8(12 + i, 0, 255), 255); + SEGMENT.addPixelColorXY(x, y, color); + if (cols > 24 || rows > 24) { + SEGMENT.addPixelColorXY(x+1, y, color); + SEGMENT.addPixelColorXY(x-1, y, color); + SEGMENT.addPixelColorXY(x, y+1, color); + SEGMENT.addPixelColorXY(x, y-1, color); + } + } + SEGMENT.blur(SEGMENT.intensity>>3); + + return FRAMETIME; +} +static const char _data_FX_MODE_2DSPACESHIPS[] PROGMEM = "Spaceships@!,Blur;!,!,!;!;2d"; + + +///////////////////////// +// 2D Crazy Bees // +///////////////////////// +//// Crazy bees by stepko (c)12.02.21 [https://editor.soulmatelights.com/gallery/651-crazy-bees], adapted by Blaz Kristan (AKA blazoncek) +#define MAX_BEES 5 +uint16_t mode_2Dcrazybees(void) { + if (!strip.isMatrix) return mode_static(); // not a 2D set-up + + const uint16_t cols = SEGMENT.virtualWidth(); + const uint16_t rows = SEGMENT.virtualHeight(); + + byte n = MIN(MAX_BEES, (rows * cols) / 256 + 1); + + typedef struct Bee { + uint8_t posX, posY, aimX, aimY, hue; + int8_t deltaX, deltaY, signX, signY, error; + void aimed(uint16_t w, uint16_t h) { + random16_set_seed(millis()); + aimX = random8(0, w); + aimY = random8(0, h); + hue = random8(); + deltaX = abs(aimX - posX); + deltaY = abs(aimY - posY); + signX = posX < aimX ? 1 : -1; + signY = posY < aimY ? 1 : -1; + error = deltaX - deltaY; + }; + } bee_t; + + if (!SEGENV.allocateData(sizeof(bee_t)*MAX_BEES)) return mode_static(); //allocation failed + bee_t *bee = reinterpret_cast(SEGENV.data); + + if (SEGENV.call == 0) { + SEGMENT.setUpLeds(); + SEGMENT.fill(BLACK); + for (size_t i = 0; i < n; i++) { + bee[i].posX = random8(0, cols); + bee[i].posY = random8(0, rows); + bee[i].aimed(cols, rows); + } + } + + if (millis() > SEGENV.step) { + SEGENV.step = millis() + (FRAMETIME * 8 / ((SEGMENT.speed>>5)+1)); + + SEGMENT.fadeToBlackBy(32); + + for (size_t i = 0; i < n; i++) { + SEGMENT.addPixelColorXY(bee[i].aimX + 1, bee[i].aimY, CHSV(bee[i].hue, 255, 255)); + SEGMENT.addPixelColorXY(bee[i].aimX, bee[i].aimY + 1, CHSV(bee[i].hue, 255, 255)); + SEGMENT.addPixelColorXY(bee[i].aimX - 1, bee[i].aimY, CHSV(bee[i].hue, 255, 255)); + SEGMENT.addPixelColorXY(bee[i].aimX, bee[i].aimY - 1, CHSV(bee[i].hue, 255, 255)); + if (bee[i].posX != bee[i].aimX || bee[i].posY != bee[i].aimY) { + SEGMENT.setPixelColorXY(bee[i].posX, bee[i].posY, CRGB(CHSV(bee[i].hue, 60, 255))); + int8_t error2 = bee[i].error * 2; + if (error2 > -bee[i].deltaY) { + bee[i].error -= bee[i].deltaY; + bee[i].posX += bee[i].signX; + } + if (error2 < bee[i].deltaX) { + bee[i].error += bee[i].deltaX; + bee[i].posY += bee[i].signY; + } + } else { + bee[i].aimed(cols, rows); + } + } + SEGMENT.blur(SEGMENT.intensity>>4); + } + return FRAMETIME; +} +static const char _data_FX_MODE_2DCRAZYBEES[] PROGMEM = "Crazy Bees@!,Blur;;;2d"; + + +///////////////////////// +// 2D Ghost Rider // +///////////////////////// +//// Ghost Rider by stepko (c)2021 [https://editor.soulmatelights.com/gallery/716-ghost-rider], adapted by Blaz Kristan (AKA blazoncek) +#define LIGHTERS_AM 64 // max lighters (adequate for 32x32 matrix) +uint16_t mode_2Dghostrider(void) { + if (!strip.isMatrix) return mode_static(); // not a 2D set-up + + const uint16_t cols = SEGMENT.virtualWidth(); + const uint16_t rows = SEGMENT.virtualHeight(); + + typedef struct Lighter { + int16_t gPosX; + int16_t gPosY; + uint16_t gAngle; + int8_t angleSpeed; + uint16_t lightersPosX[LIGHTERS_AM]; + uint16_t lightersPosY[LIGHTERS_AM]; + uint16_t Angle[LIGHTERS_AM]; + uint16_t time[LIGHTERS_AM]; + bool reg[LIGHTERS_AM]; + int8_t Vspeed; + } lighter_t; + + if (!SEGENV.allocateData(sizeof(lighter_t))) return mode_static(); //allocation failed + lighter_t *lighter = reinterpret_cast(SEGENV.data); + + const size_t maxLighters = min(cols + rows, LIGHTERS_AM); + + if (SEGENV.call == 0) SEGMENT.setUpLeds(); + if (SEGENV.aux0 != cols || SEGENV.aux1 != rows) { + SEGENV.aux0 = cols; + SEGENV.aux1 = rows; + SEGMENT.fill(BLACK); + random16_set_seed(strip.now); + lighter->angleSpeed = random8(0,20) - 10; + lighter->Vspeed = 5; + lighter->gPosX = (cols/2) * 10; + lighter->gPosY = (rows/2) * 10; + for (size_t i = 0; i < maxLighters; i++) { + lighter->lightersPosX[i] = lighter->gPosX; + lighter->lightersPosY[i] = lighter->gPosY + i; + lighter->time[i] = i * 2; + } + } + + if (millis() > SEGENV.step) { + SEGENV.step = millis() + 1024 / (cols+rows); + + SEGMENT.fadeToBlackBy((SEGMENT.speed>>2)+64); + + CRGB color = CRGB::White; + SEGMENT.wu_pixel(lighter->gPosX * 256 / 10, lighter->gPosY * 256 / 10, color); + + lighter->gPosX += lighter->Vspeed * sin_t(radians(lighter->gAngle)); + lighter->gPosY += lighter->Vspeed * cos_t(radians(lighter->gAngle)); + lighter->gAngle += lighter->angleSpeed; + if (lighter->gPosX < 0) lighter->gPosX = (cols - 1) * 10; + if (lighter->gPosX > (cols - 1) * 10) lighter->gPosX = 0; + if (lighter->gPosY < 0) lighter->gPosY = (rows - 1) * 10; + if (lighter->gPosY > (rows - 1) * 10) lighter->gPosY = 0; + for (size_t i = 0; i < maxLighters; i++) { + lighter->time[i] += random8(5, 20); + if (lighter->time[i] >= 255 || + (lighter->lightersPosX[i] <= 0) || + (lighter->lightersPosX[i] >= (cols - 1) * 10) || + (lighter->lightersPosY[i] <= 0) || + (lighter->lightersPosY[i] >= (rows - 1) * 10)) { + lighter->reg[i] = true; + } + if (lighter->reg[i]) { + lighter->lightersPosY[i] = lighter->gPosY; + lighter->lightersPosX[i] = lighter->gPosX; + lighter->Angle[i] = lighter->gAngle + random(-10, 10); + lighter->time[i] = 0; + lighter->reg[i] = false; + } else { + lighter->lightersPosX[i] += -7 * sin_t(radians(lighter->Angle[i])); + lighter->lightersPosY[i] += -7 * cos_t(radians(lighter->Angle[i])); + } + SEGMENT.wu_pixel(lighter->lightersPosX[i] * 256 / 10, lighter->lightersPosY[i] * 256 / 10, ColorFromPalette(SEGPALETTE, (256 - lighter->time[i]))); + } + SEGMENT.blur(SEGMENT.intensity>>3); + } + + return FRAMETIME; +} +static const char _data_FX_MODE_2DGHOSTRIDER[] PROGMEM = "Ghost Rider@Fade rate,Blur;!,!,!;!;2d"; + + +//////////////////////////// +// 2D Floating Blobs // +//////////////////////////// +//// Floating Blobs by stepko (c)2021 [https://editor.soulmatelights.com/gallery/573-blobs], adapted by Blaz Kristan (AKA blazoncek) +#define MAX_BLOBS 8 +uint16_t mode_2Dfloatingblobs(void) { + if (!strip.isMatrix) return mode_static(); // not a 2D set-up + + const uint16_t cols = SEGMENT.virtualWidth(); + const uint16_t rows = SEGMENT.virtualHeight(); + + typedef struct Blob { + float x[MAX_BLOBS], y[MAX_BLOBS]; + float sX[MAX_BLOBS], sY[MAX_BLOBS]; // speed + float r[MAX_BLOBS]; + bool grow[MAX_BLOBS]; + byte color[MAX_BLOBS]; + } blob_t; + + uint8_t Amount = (SEGMENT.intensity>>5) + 1; // NOTE: be sure to update MAX_BLOBS if you change this + + if (!SEGENV.allocateData(sizeof(blob_t))) return mode_static(); //allocation failed + blob_t *blob = reinterpret_cast(SEGENV.data); + + if (SEGENV.call == 0) SEGMENT.setUpLeds(); + if (SEGENV.aux0 != cols || SEGENV.aux1 != rows) { + SEGENV.aux0 = cols; // re-initialise if virtual size changes + SEGENV.aux1 = rows; + SEGMENT.fill(BLACK); + for (size_t i = 0; i < MAX_BLOBS; i++) { + blob->r[i] = random8(1, cols>8 ? (cols/4) : 2); + blob->sX[i] = (float) random8(3, cols) / (float)(256 - SEGMENT.speed); // speed x + blob->sY[i] = (float) random8(3, rows) / (float)(256 - SEGMENT.speed); // speed y + blob->x[i] = random8(0, cols-1); + blob->y[i] = random8(0, rows-1); + blob->color[i] = random8(); + blob->grow[i] = (blob->r[i] < 1.f); + if (blob->sX[i] == 0) blob->sX[i] = 1; + if (blob->sY[i] == 0) blob->sY[i] = 1; + } + } + + SEGMENT.fadeToBlackBy(20); + + // Bounce balls around + for (size_t i = 0; i < Amount; i++) { + if (SEGENV.step < millis()) blob->color[i] = add8(blob->color[i], 4); // slowly change color + // change radius if needed + if (blob->grow[i]) { + // enlarge radius until it is >= 4 + blob->r[i] += (fabs(blob->sX[i]) > fabs(blob->sY[i]) ? fabs(blob->sX[i]) : fabs(blob->sY[i])) * 0.05f; + if (blob->r[i] >= MIN(cols/4.f,2.f)) { + blob->grow[i] = false; + } + } else { + // reduce radius until it is < 1 + blob->r[i] -= (fabs(blob->sX[i]) > fabs(blob->sY[i]) ? fabs(blob->sX[i]) : fabs(blob->sY[i])) * 0.05f; + if (blob->r[i] < 1.f) { + blob->grow[i] = true; + } + } + uint32_t c = SEGMENT.color_from_palette(blob->color[i], false, false, 0); + if (blob->r[i] > 1.f) SEGMENT.fill_circle(blob->y[i], blob->x[i], roundf(blob->r[i]), c); + else SEGMENT.setPixelColorXY(blob->y[i], blob->x[i], c); + // move x + if (blob->x[i] + blob->r[i] >= cols - 1) blob->x[i] += (blob->sX[i] * ((cols - 1 - blob->x[i]) / blob->r[i] + 0.005f)); + else if (blob->x[i] - blob->r[i] <= 0) blob->x[i] += (blob->sX[i] * (blob->x[i] / blob->r[i] + 0.005f)); + else blob->x[i] += blob->sX[i]; + // move y + if (blob->y[i] + blob->r[i] >= rows - 1) blob->y[i] += (blob->sY[i] * ((rows - 1 - blob->y[i]) / blob->r[i] + 0.005f)); + else if (blob->y[i] - blob->r[i] <= 0) blob->y[i] += (blob->sY[i] * (blob->y[i] / blob->r[i] + 0.005f)); + else blob->y[i] += blob->sY[i]; + // bounce x + if (blob->x[i] < 0.01f) { + blob->sX[i] = (float)random8(3, cols) / (256 - SEGMENT.speed); + blob->x[i] = 0.01f; + } else if (blob->x[i] > (float)cols - 1.01f) { + blob->sX[i] = (float)random8(3, cols) / (256 - SEGMENT.speed); + blob->sX[i] = -blob->sX[i]; + blob->x[i] = (float)cols - 1.01f; + } + // bounce y + if (blob->y[i] < 0.01f) { + blob->sY[i] = (float)random8(3, rows) / (256 - SEGMENT.speed); + blob->y[i] = 0.01f; + } else if (blob->y[i] > (float)rows - 1.01f) { + blob->sY[i] = (float)random8(3, rows) / (256 - SEGMENT.speed); + blob->sY[i] = -blob->sY[i]; + blob->y[i] = (float)rows - 1.01f; + } + } + SEGMENT.blur(SEGMENT.custom1>>2); + + if (SEGENV.step < millis()) SEGENV.step = millis() + 2000; // change colors every 2 seconds + + return FRAMETIME; +} +#undef MAX_BLOBS +static const char _data_FX_MODE_2DBLOBS[] PROGMEM = "Blobs@!,# blobs,Blur;!,!,;!;c1=8,2d"; + + +//////////////////////////// +// 2D Scrolling text // +//////////////////////////// +uint16_t mode_2Dscrollingtext(void) { + if (!strip.isMatrix) return mode_static(); // not a 2D set-up + + const uint16_t cols = SEGMENT.virtualWidth(); + const uint16_t rows = SEGMENT.virtualHeight(); + + int letterWidth; + int letterHeight; + switch (map(SEGMENT.custom2, 0, 255, 1, 4)) { + default: + case 1: letterWidth = 5; letterHeight = 8; break; + case 2: letterWidth = 6; letterHeight = 8; break; + case 3: letterWidth = 7; letterHeight = 9; break; + case 4: letterWidth = 5; letterHeight = 12; break; + } + const int yoffset = map(SEGMENT.intensity, 0, 255, -rows/2, rows/2) + (rows-letterHeight)/2; + char text[33] = {'\0'}; + if (SEGMENT.name) for (size_t i=0,j=0; i31 && SEGMENT.name[i]<128) text[j++] = SEGMENT.name[i]; + + if (!strlen(text) || !strncmp_P(text,PSTR("#DATE"),5) || !strncmp_P(text,PSTR("#TIME"),5)) { // fallback if empty segment name: display date and time + char sec[5]; + byte AmPmHour = hour(localTime); + boolean isitAM = true; + if (useAMPM) { + if (AmPmHour > 11) { AmPmHour -= 12; isitAM = false; } + if (AmPmHour == 0) { AmPmHour = 12; } + } + if (useAMPM) sprintf_P(sec, PSTR(" %2s"), (isitAM ? "AM" : "PM")); + else sprintf_P(sec, PSTR(":%02d"), second(localTime)); + if (!strncmp_P(text,PSTR("#DATE"),5)) sprintf_P(text, PSTR("%d.%d.%d"), day(localTime), month(localTime), year(localTime)); + else if (!strncmp_P(text,PSTR("#TIME"),5)) sprintf_P(text, PSTR("%2d:%02d%s"), AmPmHour, minute(localTime), sec); + else sprintf_P(text, PSTR("%s %d, %d %2d:%02d%s"), monthShortStr(month(localTime)), day(localTime), year(localTime), AmPmHour, minute(localTime), sec); + } + const int numberOfLetters = strlen(text); + + if (SEGENV.step < millis()) { + if ((numberOfLetters * letterWidth) > cols) ++SEGENV.aux0 %= (numberOfLetters * letterWidth) + cols; // offset + else SEGENV.aux0 = (cols + (numberOfLetters * letterWidth))/2; + ++SEGENV.aux1 &= 0xFF; // color shift + SEGENV.step = millis() + map(SEGMENT.speed, 0, 255, 10*FRAMETIME_FIXED, 2*FRAMETIME_FIXED); + + // we need it 3 times + SEGMENT.fade_out(255 - (SEGMENT.custom1>>5)); // fade to background color + SEGMENT.fade_out(255 - (SEGMENT.custom1>>5)); // fade to background color + SEGMENT.fade_out(255 - (SEGMENT.custom1>>5)); // fade to background color + for (int i = 0; i < numberOfLetters; i++) { + if (int(cols) - int(SEGENV.aux0) + letterWidth*(i+1) < 0) continue; // don't draw characters off-screen + SEGMENT.drawCharacter(text[i], int(cols) - int(SEGENV.aux0) + letterWidth*i, yoffset, letterWidth, letterHeight, SEGMENT.color_from_palette(SEGENV.aux1, false, PALETTE_SOLID_WRAP, 0)); + } + } + + return FRAMETIME; +} +static const char _data_FX_MODE_2DSCROLLTEXT[] PROGMEM = "Scrolling Text@!,Y Offset,Trail,Font size;!,!;!;ix=96,c1=0,rev=0,mi=0,rY=0,mY=0,2d"; + + +//////////////////////////// +// 2D Drift Rose // +//////////////////////////// +//// Drift Rose by stepko (c)2021 [https://editor.soulmatelights.com/gallery/1369-drift-rose-pattern], adapted by Blaz Kristan (AKA blazoncek) +uint16_t mode_2Ddriftrose(void) { + if (!strip.isMatrix) return mode_static(); // not a 2D set-up + + const uint16_t cols = SEGMENT.virtualWidth(); + const uint16_t rows = SEGMENT.virtualHeight(); + + const float CX = (cols-cols%2)/2.f - .5f; + const float CY = (rows-rows%2)/2.f - .5f; + const float L = min(cols, rows) / 2.f; + + if (SEGENV.call == 0) { + SEGMENT.setUpLeds(); + SEGMENT.fill(BLACK); + } + + SEGMENT.fadeToBlackBy(32+(SEGMENT.speed>>3)); + for (size_t i = 1; i < 37; i++) { + uint32_t x = (CX + (sin_t(radians(i * 10)) * (beatsin8(i, 0, L*2)-L))) * 255.f; + uint32_t y = (CY + (cos_t(radians(i * 10)) * (beatsin8(i, 0, L*2)-L))) * 255.f; + SEGMENT.wu_pixel(x, y, CHSV(i * 10, 255, 255)); + } + SEGMENT.blur((SEGMENT.intensity>>4)+1); + + return FRAMETIME; +} +static const char _data_FX_MODE_2DDRIFTROSE[] PROGMEM = "Drift Rose@Fade,Blur;;;2d"; + +#endif // WLED_DISABLE_2D + + +#ifndef WLED_DISABLE_AUDIO +/////////////////////////////////////////////////////////////////////////////// +/******************** audio enhanced routines ************************/ +/////////////////////////////////////////////////////////////////////////////// + + +/* use the following code to pass AudioReactive usermod variables to effect + + uint8_t *binNum = (uint8_t*)&SEGENV.aux1, *maxVol = (uint8_t*)(&SEGENV.aux1+1); // just in case assignment + bool samplePeak = false; + float FFT_MajorPeak = 0.0; + uint8_t *fftResult = nullptr; + float *fftBin = nullptr; + um_data_t *um_data; + if (usermods.getUMData(&um_data, USERMOD_ID_AUDIOREACTIVE)) { + volumeSmth = *(float*) um_data->u_data[0]; + volumeRaw = *(float*) um_data->u_data[1]; + fftResult = (uint8_t*) um_data->u_data[2]; + samplePeak = *(uint8_t*) um_data->u_data[3]; + FFT_MajorPeak = *(float*) um_data->u_data[4]; + my_magnitude = *(float*) um_data->u_data[5]; + maxVol = (uint8_t*) um_data->u_data[6]; // requires UI element (SEGMENT.customX?), changes source element + binNum = (uint8_t*) um_data->u_data[7]; // requires UI element (SEGMENT.customX?), changes source element + fftBin = (float*) um_data->u_data[8]; + } else { + // add support for no audio data + um_data = simulateSound(SEGMENT.soundSim); + } +*/ + + +///////////////////////////////// +// * Ripple Peak // +///////////////////////////////// +uint16_t mode_ripplepeak(void) { // * Ripple peak. By Andrew Tuline. + // This currently has no controls. + #define maxsteps 16 // Case statement wouldn't allow a variable. + + uint16_t maxRipples = 16; + uint16_t dataSize = sizeof(Ripple) * maxRipples; + if (!SEGENV.allocateData(dataSize)) return mode_static(); //allocation failed + Ripple* ripples = reinterpret_cast(SEGENV.data); + + um_data_t *um_data; + if (!usermods.getUMData(&um_data, USERMOD_ID_AUDIOREACTIVE)) { + // add support for no audio + um_data = simulateSound(SEGMENT.soundSim); + } + uint8_t samplePeak = *(uint8_t*)um_data->u_data[3]; + #ifdef ESP32 + float FFT_MajorPeak = *(float*) um_data->u_data[4]; + #endif + uint8_t *maxVol = (uint8_t*)um_data->u_data[6]; + uint8_t *binNum = (uint8_t*)um_data->u_data[7]; + + // printUmData(); + + if (SEGENV.call == 0) { + SEGENV.aux0 = 255; + SEGMENT.custom1 = *binNum; + SEGMENT.custom2 = *maxVol * 2; + } + + *binNum = SEGMENT.custom1; // Select a bin. + *maxVol = SEGMENT.custom2 / 2; // Our volume comparator. + + SEGMENT.fade_out(240); // Lower frame rate means less effective fading than FastLED + SEGMENT.fade_out(240); + + for (int i = 0; i < SEGMENT.intensity/16; i++) { // Limit the number of ripples. + if (samplePeak) ripples[i].state = 255; + + switch (ripples[i].state) { + case 254: // Inactive mode + break; + + case 255: // Initialize ripple variables. + ripples[i].pos = random16(SEGLEN); + #ifdef ESP32 + ripples[i].color = (int)(log10f(FFT_MajorPeak)*128); + #else + ripples[i].color = random8(); + #endif + ripples[i].state = 0; + break; + + case 0: + SEGMENT.setPixelColor(ripples[i].pos, color_blend(SEGCOLOR(1), SEGMENT.color_from_palette(ripples[i].color, false, PALETTE_SOLID_WRAP, 0), SEGENV.aux0)); + ripples[i].state++; + break; + + case maxsteps: // At the end of the ripples. 254 is an inactive mode. + ripples[i].state = 254; + break; + + default: // Middle of the ripples. + SEGMENT.setPixelColor((ripples[i].pos + ripples[i].state + SEGLEN) % SEGLEN, color_blend(SEGCOLOR(1), SEGMENT.color_from_palette(ripples[i].color, false, PALETTE_SOLID_WRAP, 0), SEGENV.aux0/ripples[i].state*2)); + SEGMENT.setPixelColor((ripples[i].pos - ripples[i].state + SEGLEN) % SEGLEN, color_blend(SEGCOLOR(1), SEGMENT.color_from_palette(ripples[i].color, false, PALETTE_SOLID_WRAP, 0), SEGENV.aux0/ripples[i].state*2)); + ripples[i].state++; // Next step. + break; + } // switch step + } // for i + + return FRAMETIME; +} // mode_ripplepeak() +static const char _data_FX_MODE_RIPPLEPEAK[] PROGMEM = "Ripple Peak@Fade rate,Max # of ripples,Select bin,Volume (minimum);!,!;!;c2=0,mp12=0,ssim=0,1d,vo"; // Pixel, Beatsin + + +#ifndef WLED_DISABLE_2D +///////////////////////// +// * 2D Swirl // +///////////////////////// +// By: Mark Kriegsman https://gist.github.com/kriegsman/5adca44e14ad025e6d3b , modified by Andrew Tuline +uint16_t mode_2DSwirl(void) { + if (!strip.isMatrix) return mode_static(); // not a 2D set-up + + const uint16_t cols = SEGMENT.virtualWidth(); + const uint16_t rows = SEGMENT.virtualHeight(); + + if (SEGENV.call == 0) { + SEGMENT.setUpLeds(); + SEGMENT.fill(BLACK); + } + + const uint8_t borderWidth = 2; + + SEGMENT.blur(SEGMENT.custom1); + + uint8_t i = beatsin8( 27*SEGMENT.speed/255, borderWidth, cols - borderWidth); + uint8_t j = beatsin8( 41*SEGMENT.speed/255, borderWidth, rows - borderWidth); + uint8_t ni = (cols - 1) - i; + uint8_t nj = (cols - 1) - j; + uint16_t ms = millis(); + + um_data_t *um_data; + if (!usermods.getUMData(&um_data, USERMOD_ID_AUDIOREACTIVE)) { + // add support for no audio + um_data = simulateSound(SEGMENT.soundSim); + } + float volumeSmth = *(float*) um_data->u_data[0]; //ewowi: use instead of sampleAvg??? + int16_t volumeRaw = *(int16_t*) um_data->u_data[1]; + + // printUmData(); + + SEGMENT.addPixelColorXY( i, j, ColorFromPalette(SEGPALETTE, (ms / 11 + volumeSmth*4), volumeRaw * SEGMENT.intensity / 64, LINEARBLEND)); //CHSV( ms / 11, 200, 255); + SEGMENT.addPixelColorXY( j, i, ColorFromPalette(SEGPALETTE, (ms / 13 + volumeSmth*4), volumeRaw * SEGMENT.intensity / 64, LINEARBLEND)); //CHSV( ms / 13, 200, 255); + SEGMENT.addPixelColorXY(ni,nj, ColorFromPalette(SEGPALETTE, (ms / 17 + volumeSmth*4), volumeRaw * SEGMENT.intensity / 64, LINEARBLEND)); //CHSV( ms / 17, 200, 255); + SEGMENT.addPixelColorXY(nj,ni, ColorFromPalette(SEGPALETTE, (ms / 29 + volumeSmth*4), volumeRaw * SEGMENT.intensity / 64, LINEARBLEND)); //CHSV( ms / 29, 200, 255); + SEGMENT.addPixelColorXY( i,nj, ColorFromPalette(SEGPALETTE, (ms / 37 + volumeSmth*4), volumeRaw * SEGMENT.intensity / 64, LINEARBLEND)); //CHSV( ms / 37, 200, 255); + SEGMENT.addPixelColorXY(ni, j, ColorFromPalette(SEGPALETTE, (ms / 41 + volumeSmth*4), volumeRaw * SEGMENT.intensity / 64, LINEARBLEND)); //CHSV( ms / 41, 200, 255); + + return FRAMETIME; +} // mode_2DSwirl() +static const char _data_FX_MODE_2DSWIRL[] PROGMEM = "Swirl@!,Sensitivity,Blur;,Bg Swirl;!;ix=64ssim=0,2d,vo"; // Beatsin + + +///////////////////////// +// * 2D Waverly // +///////////////////////// +// By: Stepko, https://editor.soulmatelights.com/gallery/652-wave , modified by Andrew Tuline +uint16_t mode_2DWaverly(void) { + if (!strip.isMatrix) return mode_static(); // not a 2D set-up + + const uint16_t cols = SEGMENT.virtualWidth(); + const uint16_t rows = SEGMENT.virtualHeight(); + + if (SEGENV.call == 0) { + SEGMENT.setUpLeds(); + SEGMENT.fill(BLACK); + } + + um_data_t *um_data; + if (!usermods.getUMData(&um_data, USERMOD_ID_AUDIOREACTIVE)) { + // add support for no audio + um_data = simulateSound(SEGMENT.soundSim); + } + float volumeSmth = *(float*) um_data->u_data[0]; + + SEGMENT.fadeToBlackBy(SEGMENT.speed); + + long t = millis() / 2; + for (int i = 0; i < cols; i++) { + uint16_t thisVal = (1 + SEGMENT.intensity/64) * inoise8(i * 45 , t , t)/2; + // use audio if available + if (um_data) { + thisVal /= 32; // reduce intensity of inoise8() + thisVal *= volumeSmth; + } + uint16_t thisMax = map(thisVal, 0, 512, 0, rows); + + for (int j = 0; j < thisMax; j++) { + SEGMENT.addPixelColorXY(i, j, ColorFromPalette(SEGPALETTE, map(j, 0, thisMax, 250, 0), 255, LINEARBLEND)); + SEGMENT.addPixelColorXY((cols - 1) - i, (rows - 1) - j, ColorFromPalette(SEGPALETTE, map(j, 0, thisMax, 250, 0), 255, LINEARBLEND)); + } + } + SEGMENT.blur(16); + + return FRAMETIME; +} // mode_2DWaverly() +static const char _data_FX_MODE_2DWAVERLY[] PROGMEM = "Waverly@Amplification,Sensitivity;;!;ix=64,ssim=0,2d,vo"; // Beatsin + +#endif // WLED_DISABLE_2D + +// float version of map() +static float mapf(float x, float in_min, float in_max, float out_min, float out_max){ + return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min; +} + +// Gravity struct requited for GRAV* effects +typedef struct Gravity { + int topLED; + int gravityCounter; +} gravity; + +/////////////////////// +// * GRAVCENTER // +/////////////////////// +uint16_t mode_gravcenter(void) { // Gravcenter. By Andrew Tuline. + + const uint16_t dataSize = sizeof(gravity); + if (!SEGENV.allocateData(dataSize)) return mode_static(); //allocation failed + Gravity* gravcen = reinterpret_cast(SEGENV.data); + + um_data_t *um_data; + if (!usermods.getUMData(&um_data, USERMOD_ID_AUDIOREACTIVE)) { + // add support for no audio + um_data = simulateSound(SEGMENT.soundSim); + } + float volumeSmth = *(float*) um_data->u_data[0]; + + //SEGMENT.fade_out(240); + SEGMENT.fade_out(251); // 30% + + float segmentSampleAvg = volumeSmth * (float)SEGMENT.intensity / 255.0f; + segmentSampleAvg *= 0.125; // divide by 8, to compensate for later "sensitivty" upscaling + + float mySampleAvg = mapf(segmentSampleAvg*2.0, 0, 32, 0, (float)SEGLEN/2.0); // map to pixels available in current segment + uint16_t tempsamp = constrain(mySampleAvg, 0, SEGLEN/2); // Keep the sample from overflowing. + uint8_t gravity = 8 - SEGMENT.speed/32; + + for (int i=0; i= gravcen->topLED) + gravcen->topLED = tempsamp-1; + else if (gravcen->gravityCounter % gravity == 0) + gravcen->topLED--; + + if (gravcen->topLED >= 0) { + SEGMENT.setPixelColor(gravcen->topLED+SEGLEN/2, SEGMENT.color_from_palette(millis(), false, PALETTE_SOLID_WRAP, 0)); + SEGMENT.setPixelColor(SEGLEN/2-1-gravcen->topLED, SEGMENT.color_from_palette(millis(), false, PALETTE_SOLID_WRAP, 0)); + } + gravcen->gravityCounter = (gravcen->gravityCounter + 1) % gravity; + + return FRAMETIME; +} // mode_gravcenter() +static const char _data_FX_MODE_GRAVCENTER[] PROGMEM = "Gravcenter@Rate of fall,Sensitivity;,!;!;ix=128,mp12=2,ssim=0,1d,vo"; // Circle, Beatsin + + +/////////////////////// +// * GRAVCENTRIC // +/////////////////////// +uint16_t mode_gravcentric(void) { // Gravcentric. By Andrew Tuline. + + uint16_t dataSize = sizeof(gravity); + if (!SEGENV.allocateData(dataSize)) return mode_static(); //allocation failed + Gravity* gravcen = reinterpret_cast(SEGENV.data); + + um_data_t *um_data; + if (!usermods.getUMData(&um_data, USERMOD_ID_AUDIOREACTIVE)) { + // add support for no audio + um_data = simulateSound(SEGMENT.soundSim); + } + float volumeSmth = *(float*) um_data->u_data[0]; + + // printUmData(); + + //SEGMENT.fade_out(240); + //SEGMENT.fade_out(240); // twice? really? + SEGMENT.fade_out(253); // 50% + + float segmentSampleAvg = volumeSmth * (float)SEGMENT.intensity / 255.0; + segmentSampleAvg *= 0.125f; // divide by 8, to compensate for later "sensitivty" upscaling + + float mySampleAvg = mapf(segmentSampleAvg*2.0, 0.0f, 32.0f, 0.0f, (float)SEGLEN/2.0); // map to pixels availeable in current segment + int tempsamp = constrain(mySampleAvg, 0, SEGLEN/2); // Keep the sample from overflowing. + uint8_t gravity = 8 - SEGMENT.speed/32; + + for (int i=0; i= gravcen->topLED) + gravcen->topLED = tempsamp-1; + else if (gravcen->gravityCounter % gravity == 0) + gravcen->topLED--; + + if (gravcen->topLED >= 0) { + SEGMENT.setPixelColor(gravcen->topLED+SEGLEN/2, CRGB::Gray); + SEGMENT.setPixelColor(SEGLEN/2-1-gravcen->topLED, CRGB::Gray); + } + gravcen->gravityCounter = (gravcen->gravityCounter + 1) % gravity; + + return FRAMETIME; +} // mode_gravcentric() +static const char _data_FX_MODE_GRAVCENTRIC[] PROGMEM = "Gravcentric@Rate of fall,Sensitivity;!;!;ix=128,mp12=2,ssim=0,1d,vo"; // Circle, Beatsin + + +/////////////////////// +// * GRAVIMETER // +/////////////////////// +uint16_t mode_gravimeter(void) { // Gravmeter. By Andrew Tuline. + + uint16_t dataSize = sizeof(gravity); + if (!SEGENV.allocateData(dataSize)) return mode_static(); //allocation failed + Gravity* gravcen = reinterpret_cast(SEGENV.data); + + um_data_t *um_data; + if (!usermods.getUMData(&um_data, USERMOD_ID_AUDIOREACTIVE)) { + // add support for no audio + um_data = simulateSound(SEGMENT.soundSim); + } + float volumeSmth = *(float*) um_data->u_data[0]; + + //SEGMENT.fade_out(240); + SEGMENT.fade_out(249); // 25% + + float segmentSampleAvg = volumeSmth * (float)SEGMENT.intensity / 255.0; + segmentSampleAvg *= 0.25; // divide by 4, to compensate for later "sensitivty" upscaling + + float mySampleAvg = mapf(segmentSampleAvg*2.0, 0, 64, 0, (SEGLEN-1)); // map to pixels availeable in current segment + int tempsamp = constrain(mySampleAvg,0,SEGLEN-1); // Keep the sample from overflowing. + uint8_t gravity = 8 - SEGMENT.speed/32; + + for (int i=0; i= gravcen->topLED) + gravcen->topLED = tempsamp; + else if (gravcen->gravityCounter % gravity == 0) + gravcen->topLED--; + + if (gravcen->topLED > 0) { + SEGMENT.setPixelColor(gravcen->topLED, SEGMENT.color_from_palette(millis(), false, PALETTE_SOLID_WRAP, 0)); + } + gravcen->gravityCounter = (gravcen->gravityCounter + 1) % gravity; + + return FRAMETIME; +} // mode_gravimeter() +static const char _data_FX_MODE_GRAVIMETER[] PROGMEM = "Gravimeter@Rate of fall,Sensitivity;!,!;!;ix=128,mp12=2,ssim=0,1d,vo"; // Circle, Beatsin + + +////////////////////// +// * JUGGLES // +////////////////////// +uint16_t mode_juggles(void) { // Juggles. By Andrew Tuline. + um_data_t *um_data; + if (!usermods.getUMData(&um_data, USERMOD_ID_AUDIOREACTIVE)) { + // add support for no audio + um_data = simulateSound(SEGMENT.soundSim); + } + float volumeSmth = *(float*) um_data->u_data[0]; + + SEGMENT.fade_out(224); // 6.25% + uint16_t my_sampleAgc = fmax(fmin(volumeSmth, 255.0), 0); + + for (size_t i=0; iu_data[1]; + + if (SEGENV.call == 0) { + SEGMENT.setUpLeds(); + SEGMENT.fill(BLACK); + } + + uint8_t secondHand = micros()/(256-SEGMENT.speed)/500 % 16; + if(SEGENV.aux0 != secondHand) { + SEGENV.aux0 = secondHand; + + int pixBri = volumeRaw * SEGMENT.intensity / 64; + for (int i=0; iu_data[0]; + + SEGMENT.fade_out(SEGMENT.speed); + SEGMENT.fade_out(SEGMENT.speed); + + float tmpSound2 = volumeSmth * (float)SEGMENT.intensity / 256.0; // Too sensitive. + tmpSound2 *= (float)SEGMENT.intensity / 128.0; // Reduce sensitity/length. + + int maxLen = mapf(tmpSound2, 0, 127, 0, SEGLEN/2); + if (maxLen >SEGLEN/2) maxLen = SEGLEN/2; + + for (int i=(SEGLEN/2-maxLen); i<(SEGLEN/2+maxLen); i++) { + uint8_t index = inoise8(i*volumeSmth+SEGENV.aux0, SEGENV.aux1+i*volumeSmth); // Get a value from the noise function. I'm using both x and y axis. + SEGMENT.setPixelColor(i, SEGMENT.color_from_palette(index, false, PALETTE_SOLID_WRAP, 0)); + } + + SEGENV.aux0=SEGENV.aux0+beatsin8(5,0,10); + SEGENV.aux1=SEGENV.aux1+beatsin8(4,0,10); + + return FRAMETIME; +} // mode_midnoise() +static const char _data_FX_MODE_MIDNOISE[] PROGMEM = "Midnoise@Fade rate,Maximum length;,!;!;ix=128,mp12=2,ssim=0,1d,vo"; // Circle, Beatsin + + +////////////////////// +// * NOISEFIRE // +////////////////////// +// I am the god of hellfire. . . Volume (only) reactive fire routine. Oh, look how short this is. +uint16_t mode_noisefire(void) { // Noisefire. By Andrew Tuline. + CRGBPalette16 myPal = CRGBPalette16(CHSV(0,255,2), CHSV(0,255,4), CHSV(0,255,8), CHSV(0, 255, 8), // Fire palette definition. Lower value = darker. + CHSV(0, 255, 16), CRGB::Red, CRGB::Red, CRGB::Red, + CRGB::DarkOrange, CRGB::DarkOrange, CRGB::Orange, CRGB::Orange, + CRGB::Yellow, CRGB::Orange, CRGB::Yellow, CRGB::Yellow); + + um_data_t *um_data; + if (!usermods.getUMData(&um_data, USERMOD_ID_AUDIOREACTIVE)) { + // add support for no audio + um_data = simulateSound(SEGMENT.soundSim); + } + float volumeSmth = *(float*) um_data->u_data[0]; + + if (SEGENV.call == 0) SEGMENT.fill(BLACK); + + for (int i = 0; i < SEGLEN; i++) { + uint16_t index = inoise8(i*SEGMENT.speed/64,millis()*SEGMENT.speed/64*SEGLEN/255); // X location is constant, but we move along the Y at the rate of millis(). By Andrew Tuline. + index = (255 - i*256/SEGLEN) * index/(256-SEGMENT.intensity); // Now we need to scale index so that it gets blacker as we get close to one of the ends. + // This is a simple y=mx+b equation that's been scaled. index/128 is another scaling. + + CRGB color = ColorFromPalette(myPal, index, volumeSmth*2, LINEARBLEND); // Use the my own palette. + SEGMENT.setPixelColor(i, color); + } + + return FRAMETIME; +} // mode_noisefire() +static const char _data_FX_MODE_NOISEFIRE[] PROGMEM = "Noisefire@!,!;;;mp12=2,ssim=0,1d,vo"; // Circle, Beatsin + + +/////////////////////// +// * Noisemeter // +/////////////////////// +uint16_t mode_noisemeter(void) { // Noisemeter. By Andrew Tuline. + + um_data_t *um_data; + if (!usermods.getUMData(&um_data, USERMOD_ID_AUDIOREACTIVE)) { + // add support for no audio + um_data = simulateSound(SEGMENT.soundSim); + } + float volumeSmth = *(float*) um_data->u_data[0]; + int16_t volumeRaw = *(int16_t*)um_data->u_data[1]; + + //uint8_t fadeRate = map(SEGMENT.speed,0,255,224,255); + uint8_t fadeRate = map(SEGMENT.speed,0,255,200,254); + SEGMENT.fade_out(fadeRate); + + float tmpSound2 = volumeRaw * 2.0 * (float)SEGMENT.intensity / 255.0; + int maxLen = mapf(tmpSound2, 0, 255, 0, SEGLEN); // map to pixels availeable in current segment // Still a bit too sensitive. + if (maxLen <0) maxLen = 0; + if (maxLen >SEGLEN) maxLen = SEGLEN; + + for (int i=0; iu_data[1]; + + uint8_t secondHand = micros()/(256-SEGMENT.speed)/500+1 % 16; + if (SEGENV.aux0 != secondHand) { + SEGENV.aux0 = secondHand; + + int pixBri = volumeRaw * SEGMENT.intensity / 64; + + SEGMENT.setPixelColor(SEGLEN/2, color_blend(SEGCOLOR(1), SEGMENT.color_from_palette(millis(), false, PALETTE_SOLID_WRAP, 0), pixBri)); + for (int i = SEGLEN - 1; i > SEGLEN/2; i--) SEGMENT.setPixelColor(i, SEGMENT.getPixelColor(i-1)); //move to the left + for (int i = 0; i < SEGLEN/2; i++) SEGMENT.setPixelColor(i, SEGMENT.getPixelColor(i+1)); // move to the right + } + + return FRAMETIME; +} // mode_pixelwave() +static const char _data_FX_MODE_PIXELWAVE[] PROGMEM = "Pixelwave@!,Sensitivity;!,!;!;ix=64,mp12=2,ssim=0,1d,vo"; // Circle, Beatsin + + +////////////////////// +// * PLASMOID // +////////////////////// +typedef struct Plasphase { + int16_t thisphase; + int16_t thatphase; +} plasphase; + +uint16_t mode_plasmoid(void) { // Plasmoid. By Andrew Tuline. + // even with 1D effect we have to take logic for 2D segments for allocation as fill_solid() fills whole segment + if (!SEGENV.allocateData(sizeof(plasphase))) return mode_static(); //allocation failed + Plasphase* plasmoip = reinterpret_cast(SEGENV.data); + + um_data_t *um_data; + if (!usermods.getUMData(&um_data, USERMOD_ID_AUDIOREACTIVE)) { + // add support for no audio + um_data = simulateSound(SEGMENT.soundSim); + } + float volumeSmth = *(float*) um_data->u_data[0]; + + SEGMENT.fadeToBlackBy(64); + + plasmoip->thisphase += beatsin8(6,-4,4); // You can change direction and speed individually. + plasmoip->thatphase += beatsin8(7,-4,4); // Two phase values to make a complex pattern. By Andrew Tuline. + + for (int i=0; ithisphase) & 0xFF)/2; + thisbright += cos8(((i*(97 +(5*SEGMENT.speed/32)))+plasmoip->thatphase) & 0xFF)/2; // Let's munge the brightness a bit and animate it all with the phases. + + uint8_t colorIndex=thisbright; + if (volumeSmth * SEGMENT.intensity / 64 < thisbright) {thisbright = 0;} + + SEGMENT.addPixelColor(i, color_blend(SEGCOLOR(1), SEGMENT.color_from_palette(colorIndex, false, PALETTE_SOLID_WRAP, 0), thisbright)); + } + + return FRAMETIME; +} // mode_plasmoid() +static const char _data_FX_MODE_PLASMOID[] PROGMEM = "Plasmoid@Phase,# of pixels;!,!;!;sx=128,ix=128,mp12=0,ssim=0,1d,vo"; // Pixels, Beatsin + + +/////////////////////// +// * PUDDLEPEAK // +/////////////////////// +// Andrew's crappy peak detector. If I were 40+ years younger, I'd learn signal processing. +uint16_t mode_puddlepeak(void) { // Puddlepeak. By Andrew Tuline. + + uint16_t size = 0; + uint8_t fadeVal = map(SEGMENT.speed,0,255, 224, 254); + uint16_t pos = random(SEGLEN); // Set a random starting position. + + um_data_t *um_data; + if (!usermods.getUMData(&um_data, USERMOD_ID_AUDIOREACTIVE)) { + // add support for no audio + um_data = simulateSound(SEGMENT.soundSim); + } + uint8_t samplePeak = *(uint8_t*)um_data->u_data[3]; + uint8_t *maxVol = (uint8_t*)um_data->u_data[6]; + uint8_t *binNum = (uint8_t*)um_data->u_data[7]; + float volumeSmth = *(float*) um_data->u_data[0]; + + if (SEGENV.call == 0) { + SEGMENT.custom1 = *binNum; + SEGMENT.custom2 = *maxVol * 2; + } + + *binNum = SEGMENT.custom1; // Select a bin. + *maxVol = SEGMENT.custom2 / 2; // Our volume comparator. + + SEGMENT.fade_out(fadeVal); + + if (samplePeak == 1) { + size = volumeSmth * SEGMENT.intensity /256 /4 + 1; // Determine size of the flash based on the volume. + if (pos+size>= SEGLEN) size = SEGLEN - pos; + } + + for (int i=0; iu_data[1]; + + if (volumeRaw > 1) { + size = volumeRaw * SEGMENT.intensity /256 /8 + 1; // Determine size of the flash based on the volume. + if (pos+size >= SEGLEN) size = SEGLEN - pos; + } + + for (int i=0; i(SEGENV.data); // Used to store a pile of samples because WLED frame rate and WLED sample rate are not synchronized. Frame rate is too low. + + um_data_t *um_data; + if (!usermods.getUMData(&um_data, USERMOD_ID_AUDIOREACTIVE)) { + um_data = simulateSound(SEGMENT.soundSim); + } + float volumeSmth = *(float*) um_data->u_data[0]; + + myVals[millis()%32] = volumeSmth; // filling values semi randomly + + SEGMENT.fade_out(64+(SEGMENT.speed>>1)); + + for (int i=0; i u_data[2]; + + if (SEGENV.call == 0) { + SEGMENT.fill(BLACK); + SEGENV.aux0 = 0; + } + + SEGMENT.fade_out(SEGMENT.speed); + + SEGENV.step += FRAMETIME; + if (SEGENV.step > SPEED_FORMULA_L) { + uint16_t segLoc = random16(SEGLEN); + SEGMENT.setPixelColor(segLoc, color_blend(SEGCOLOR(1), SEGMENT.color_from_palette(2*fftResult[SEGENV.aux0%16]*240/(SEGLEN-1), false, PALETTE_SOLID_WRAP, 0), 2*fftResult[SEGENV.aux0%16])); + ++(SEGENV.aux0) %= 16; // make sure it doesn't cross 16 + + SEGENV.step = 1; + SEGMENT.blur(SEGMENT.intensity); + } + + return FRAMETIME; +} // mode_blurz() +static const char _data_FX_MODE_BLURZ[] PROGMEM = "Blurz@Fade rate,Blur amount;!,Color mix;!;mp12=0,ssim=0,1d,fr"; // Pixels, Beatsin + + +///////////////////////// +// ** DJLight // +///////////////////////// +uint16_t mode_DJLight(void) { // Written by ??? Adapted by Will Tatam. + const int mid = SEGLEN / 2; + + um_data_t *um_data; + if (!usermods.getUMData(&um_data, USERMOD_ID_AUDIOREACTIVE)) { + // add support for no audio + um_data = simulateSound(SEGMENT.soundSim); + } + uint8_t *fftResult = (uint8_t*)um_data->u_data[2]; + + if (SEGENV.call == 0) { + SEGMENT.setUpLeds(); + SEGMENT.fill(BLACK); + } + + uint8_t secondHand = micros()/(256-SEGMENT.speed)/500+1 % 64; + if (SEGENV.aux0 != secondHand) { // Triggered millis timing. + SEGENV.aux0 = secondHand; + + SEGMENT.setPixelColor(mid, CRGB(fftResult[15]/2, fftResult[5]/2, fftResult[0]/2)); // 16-> 15 as 16 is out of bounds + CRGB color = SEGMENT.getPixelColor(mid); + SEGMENT.setPixelColor(mid, color.fadeToBlackBy(map(fftResult[1*4], 0, 255, 255, 10))); // TODO - Update + + for (int i = SEGLEN - 1; i > mid; i--) SEGMENT.setPixelColor(i, SEGMENT.getPixelColor(i-1)); //move to the left + for (int i = 0; i < mid; i++) SEGMENT.setPixelColor(i, SEGMENT.getPixelColor(i+1)); // move to the right + } + + return FRAMETIME; +} // mode_DJLight() +static const char _data_FX_MODE_DJLIGHT[] PROGMEM = "DJ Light@Speed;;;mp12=2,ssim=0,1d,fr"; // Circle, Beatsin + + +//////////////////// +// ** Freqmap // +//////////////////// +uint16_t mode_freqmap(void) { // Map FFT_MajorPeak to SEGLEN. Would be better if a higher framerate. + // Start frequency = 60 Hz and log10(60) = 1.78 + // End frequency = 5120 Hz and lo10(5120) = 3.71 + + um_data_t *um_data; + if (!usermods.getUMData(&um_data, USERMOD_ID_AUDIOREACTIVE)) { + // add support for no audio + um_data = simulateSound(SEGMENT.soundSim); + } + float FFT_MajorPeak = *(float*) um_data->u_data[4]; + float my_magnitude = *(float*) um_data->u_data[5] / 4.0f; + + SEGMENT.fade_out(SEGMENT.speed); + +// int locn = (log10f((float)FFT_MajorPeak) - 1.78f) * (float)SEGLEN/(3.71f-1.78f); // log10 frequency range is from 1.78 to 3.71. Let's scale to SEGLEN. + int locn = (log10f((float)FFT_MajorPeak) - 1.78f) * (float)SEGLEN/(4.0102f-1.78f); // log10 frequency range is from 1.78 to 3.71. Let's scale to SEGLEN. + if (locn < 1) locn = 0; // avoid underflow + + if (locn >=SEGLEN) locn = SEGLEN-1; + //uint16_t pixCol = (log10f(FFT_MajorPeak) - 1.78f) * 255.0f/(3.71f-1.78f); // Scale log10 of frequency values to the 255 colour index. + uint16_t pixCol = (log10f(FFT_MajorPeak) - 1.78f) * 255.0f/(4.0102f-1.78f); // Scale log10 of frequency values to the 255 colour index. + uint16_t bright = (int)my_magnitude; + + SEGMENT.setPixelColor(locn, color_blend(SEGCOLOR(1), SEGMENT.color_from_palette(SEGMENT.intensity+pixCol, false, PALETTE_SOLID_WRAP, 0), bright)); + + return FRAMETIME; +} // mode_freqmap() +static const char _data_FX_MODE_FREQMAP[] PROGMEM = "Freqmap@Fade rate,Starting color;,!;!;mp12=2,ssim=0,1d,fr"; // Circle, Beatsin + + +/////////////////////// +// ** Freqmatrix // +/////////////////////// +uint16_t mode_freqmatrix(void) { // Freqmatrix. By Andreas Pleschung. + um_data_t *um_data; + if (!usermods.getUMData(&um_data, USERMOD_ID_AUDIOREACTIVE)) { + // add support for no audio + um_data = simulateSound(SEGMENT.soundSim); + } + float FFT_MajorPeak = *(float*)um_data->u_data[4]; + float volumeSmth = *(float*) um_data->u_data[0]; + + if (SEGENV.call == 0) { + SEGMENT.setUpLeds(); + SEGMENT.fill(BLACK); + } + + uint8_t secondHand = micros()/(256-SEGMENT.speed)/500 % 16; + if(SEGENV.aux0 != secondHand) { + SEGENV.aux0 = secondHand; + + uint8_t sensitivity = map(SEGMENT.custom3, 0, 31, 1, 10); // reduced resolution slider + int pixVal = (volumeSmth * SEGMENT.intensity * sensitivity) / 256.0f; + if (pixVal > 255) pixVal = 255; + + float intensity = map(pixVal, 0, 255, 0, 100) / 100.0f; // make a brightness from the last avg + + CRGB color = CRGB::Black; + + //if (FFT_MajorPeak > 5120) FFT_MajorPeak = 0; + if (FFT_MajorPeak > 10240) FFT_MajorPeak = 0; + // MajorPeak holds the freq. value which is most abundant in the last sample. + // With our sampling rate of 10240Hz we have a usable freq range from roughtly 80Hz to 10240/2 Hz + // we will treat everything with less than 65Hz as 0 + + if (FFT_MajorPeak < 80) { + color = CRGB::Black; + } else { + int upperLimit = 20 * SEGMENT.custom2; + int lowerLimit = 2 * SEGMENT.custom1; + int i = lowerLimit!=upperLimit ? map(FFT_MajorPeak, lowerLimit, upperLimit, 0, 255) : FFT_MajorPeak; + uint16_t b = 255 * intensity; + if (b > 255) b = 255; + color = CHSV(i, 240, (uint8_t)b); // implicit conversion to RGB supplied by FastLED + } + + // shift the pixels one pixel up + SEGMENT.setPixelColor(0, color); + for (int i = SEGLEN - 1; i > 0; i--) SEGMENT.setPixelColor(i, SEGMENT.getPixelColor(i-1)); //move to the left + } + + return FRAMETIME; +} // mode_freqmatrix() +static const char _data_FX_MODE_FREQMATRIX[] PROGMEM = "Freqmatrix@Time delay,Sound effect,Low bin,High bin,Sensivity;;;mp12=0,ssim=0,1d,fr"; // Pixels, Beatsin + + +////////////////////// +// ** Freqpixels // +////////////////////// +// Start frequency = 60 Hz and log10(60) = 1.78 +// End frequency = 5120 Hz and lo10(5120) = 3.71 +// SEGMENT.speed select faderate +// SEGMENT.intensity select colour index +uint16_t mode_freqpixels(void) { // Freqpixel. By Andrew Tuline. + um_data_t *um_data; + if (!usermods.getUMData(&um_data, USERMOD_ID_AUDIOREACTIVE)) { + // add support for no audio + um_data = simulateSound(SEGMENT.soundSim); + } + float FFT_MajorPeak = *(float*) um_data->u_data[4]; + float my_magnitude = *(float*) um_data->u_data[5] / 16.0f; + + uint16_t fadeRate = 2*SEGMENT.speed - SEGMENT.speed*SEGMENT.speed/255; // Get to 255 as quick as you can. + SEGMENT.fade_out(fadeRate); + + for (int i=0; i < SEGMENT.intensity/32+1; i++) { + uint16_t locn = random16(0,SEGLEN); + //uint8_t pixCol = (log10f(FFT_MajorPeak) - 1.78) * 255.0/(3.71-1.78); // Scale log10 of frequency values to the 255 colour index. + uint8_t pixCol = (log10f(FFT_MajorPeak) - 1.78f) * 255.0f/(4.0102f-1.78f); // Scale log10 of frequency values to the 255 colour index. + SEGMENT.setPixelColor(locn, color_blend(SEGCOLOR(1), SEGMENT.color_from_palette(SEGMENT.intensity+pixCol, false, PALETTE_SOLID_WRAP, 0), (int)my_magnitude)); + } + + return FRAMETIME; +} // mode_freqpixels() +static const char _data_FX_MODE_FREQPIXELS[] PROGMEM = "Freqpixels@Fade rate,Starting colour and # of pixels;;;mp12=0,ssim=0,1d,fr"; // Pixels, Beatsin + + +////////////////////// +// ** Freqwave // +////////////////////// +// Assign a color to the central (starting pixels) based on the predominant frequencies and the volume. The color is being determined by mapping the MajorPeak from the FFT +// and then mapping this to the HSV color circle. Currently we are sampling at 10240 Hz, so the highest frequency we can look at is 5120Hz. +// +// SEGMENT.custom1: the lower cut off point for the FFT. (many, most time the lowest values have very little information since they are FFT conversion artifacts. Suggested value is close to but above 0 +// SEGMENT.custom2: The high cut off point. This depends on your sound profile. Most music looks good when this slider is between 50% and 100%. +// SEGMENT.custom3: "preamp" for the audio signal for audio10. +// +// I suggest that for this effect you turn the brightness to 95%-100% but again it depends on your soundprofile you find yourself in. +// Instead of using colorpalettes, This effect works on the HSV color circle with red being the lowest frequency +// +// As a compromise between speed and accuracy we are currently sampling with 10240Hz, from which we can then determine with a 512bin FFT our max frequency is 5120Hz. +// Depending on the music stream you have you might find it useful to change the frequency mapping. +uint16_t mode_freqwave(void) { // Freqwave. By Andreas Pleschung. + um_data_t *um_data; + if (!usermods.getUMData(&um_data, USERMOD_ID_AUDIOREACTIVE)) { + // add support for no audio + um_data = simulateSound(SEGMENT.soundSim); + } + float FFT_MajorPeak = *(float*) um_data->u_data[4]; + float volumeSmth = *(float*) um_data->u_data[0]; + + if (SEGENV.call == 0) { + SEGMENT.setUpLeds(); + SEGMENT.fill(BLACK); + } + + uint8_t secondHand = micros()/(256-SEGMENT.speed)/500 % 16; + if(SEGENV.aux0 != secondHand) { + SEGENV.aux0 = secondHand; + + float sensitivity = mapf(SEGMENT.custom3, 1, 31, 1, 10); // reduced resolution slider + float pixVal = volumeSmth * (float)SEGMENT.intensity / 256.0f * sensitivity; + if (pixVal > 255) pixVal = 255; + + float intensity = mapf(pixVal, 0, 255, 0, 100) / 100.0f; // make a brightness from the last avg + + CRGB color = 0; + + //if (FFT_MajorPeak > 5120) FFT_MajorPeak = 0.0f; + if (FFT_MajorPeak > 10240) FFT_MajorPeak = 0.0f; + // MajorPeak holds the freq. value which is most abundant in the last sample. + // With our sampling rate of 10240Hz we have a usable freq range from roughtly 80Hz to 10240/2 Hz + // we will treat everything with less than 65Hz as 0 + + if (FFT_MajorPeak < 80) { + color = CRGB::Black; + } else { + int upperLimit = 20 * SEGMENT.custom2; + int lowerLimit = 2 * SEGMENT.custom1; + int i = lowerLimit!=upperLimit ? map(FFT_MajorPeak, lowerLimit, upperLimit, 0, 255) : FFT_MajorPeak; + uint16_t b = 255.0 * intensity; + if (b > 255) b=255; + color = CHSV(i, 240, (uint8_t)b); // implicit conversion to RGB supplied by FastLED + } + + SEGMENT.setPixelColor(SEGLEN/2, color); + + // shift the pixels one pixel outwards + for (int i = SEGLEN - 1; i > SEGLEN/2; i--) SEGMENT.setPixelColor(i, SEGMENT.getPixelColor(i-1)); //move to the left + for (int i = 0; i < SEGLEN/2; i++) SEGMENT.setPixelColor(i, SEGMENT.getPixelColor(i+1)); // move to the right + } + + return FRAMETIME; +} // mode_freqwave() +static const char _data_FX_MODE_FREQWAVE[] PROGMEM = "Freqwave@Time delay,Sound effect,Low bin,High bin,Pre-amp;;;mp12=2,ssim=0,1d,fr"; // Circle, Beatsin + + +/////////////////////// +// ** Gravfreq // +/////////////////////// +uint16_t mode_gravfreq(void) { // Gravfreq. By Andrew Tuline. + + uint16_t dataSize = sizeof(gravity); + if (!SEGENV.allocateData(dataSize)) return mode_static(); //allocation failed + Gravity* gravcen = reinterpret_cast(SEGENV.data); + + um_data_t *um_data; + if (!usermods.getUMData(&um_data, USERMOD_ID_AUDIOREACTIVE)) { + // add support for no audio + um_data = simulateSound(SEGMENT.soundSim); + } + float FFT_MajorPeak = *(float*) um_data->u_data[4]; + float volumeSmth = *(float*) um_data->u_data[0]; + + SEGMENT.fade_out(240); + + float segmentSampleAvg = volumeSmth * (float)SEGMENT.intensity / 255.0; + segmentSampleAvg *= 0.125; // divide by 8, to compensate for later "sensitivty" upscaling + + float mySampleAvg = mapf(segmentSampleAvg*2.0, 0,32, 0, (float)SEGLEN/2.0); // map to pixels availeable in current segment + int tempsamp = constrain(mySampleAvg,0,SEGLEN/2); // Keep the sample from overflowing. + uint8_t gravity = 8 - SEGMENT.speed/32; + + for (int i=0; i= gravcen->topLED) + gravcen->topLED = tempsamp-1; + else if (gravcen->gravityCounter % gravity == 0) + gravcen->topLED--; + + if (gravcen->topLED >= 0) { + SEGMENT.setPixelColor(gravcen->topLED+SEGLEN/2, CRGB::Gray); + SEGMENT.setPixelColor(SEGLEN/2-1-gravcen->topLED, CRGB::Gray); + } + gravcen->gravityCounter = (gravcen->gravityCounter + 1) % gravity; + + return FRAMETIME; +} // mode_gravfreq() +static const char _data_FX_MODE_GRAVFREQ[] PROGMEM = "Gravfreq@Rate of fall,Sensivity;,!;!;ix=128,mp12=2,ssim=0,1d,fr"; // Circle, Beatsin + + +////////////////////// +// ** Noisemove // +////////////////////// +uint16_t mode_noisemove(void) { // Noisemove. By: Andrew Tuline + um_data_t *um_data; + if (!usermods.getUMData(&um_data, USERMOD_ID_AUDIOREACTIVE)) { + // add support for no audio + um_data = simulateSound(SEGMENT.soundSim); + } + uint8_t *fftResult = (uint8_t*)um_data->u_data[2]; + + SEGMENT.fade_out(224); // Just in case something doesn't get faded. + + uint8_t numBins = map(SEGMENT.intensity,0,255,0,16); // Map slider to fftResult bins. + for (int i=0; iu_data[4]; + float my_magnitude = *(float*) um_data->u_data[5] / 16.0f; + + SEGMENT.fadeToBlackBy(64); // Just in case something doesn't get faded. + + float frTemp = FFT_MajorPeak; + uint8_t octCount = 0; // Octave counter. + uint8_t volTemp = 0; + + if (my_magnitude > 32) volTemp = 255; // We need to squelch out the background noise. + + while ( frTemp > 249 ) { + octCount++; // This should go up to 5. + frTemp = frTemp/2; + } + + frTemp -=132; // This should give us a base musical note of C3 + frTemp = fabs(frTemp * 2.1); // Fudge factors to compress octave range starting at 0 and going to 255; + + uint16_t i = map(beatsin8(8+octCount*4, 0, 255, 0, octCount*8), 0, 255, 0, SEGLEN-1); + SEGMENT.addPixelColor(i, color_blend(SEGCOLOR(1), SEGMENT.color_from_palette((uint8_t)frTemp, false, PALETTE_SOLID_WRAP, 0), volTemp)); + + return FRAMETIME; +} // mode_rocktaves() +static const char _data_FX_MODE_ROCKTAVES[] PROGMEM = "Rocktaves@;,!;!;mp12=0,ssim=0,1d,fr"; // Pixels, Beatsin + + +/////////////////////// +// ** Waterfall // +/////////////////////// +// Combines peak detection with FFT_MajorPeak and FFT_Magnitude. +uint16_t mode_waterfall(void) { // Waterfall. By: Andrew Tuline + if (SEGENV.call == 0) SEGMENT.fill(BLACK); + + um_data_t *um_data; + if (!usermods.getUMData(&um_data, USERMOD_ID_AUDIOREACTIVE)) { + // add support for no audio + um_data = simulateSound(SEGMENT.soundSim); + } + uint8_t samplePeak = *(uint8_t*)um_data->u_data[3]; + float FFT_MajorPeak = *(float*) um_data->u_data[4]; + uint8_t *maxVol = (uint8_t*)um_data->u_data[6]; + uint8_t *binNum = (uint8_t*)um_data->u_data[7]; + float my_magnitude = *(float*) um_data->u_data[5] / 8.0f; + + if (SEGENV.call == 0) { + SEGMENT.setUpLeds(); + SEGMENT.fill(BLACK); + SEGENV.aux0 = 255; + SEGMENT.custom1 = *binNum; + SEGMENT.custom2 = *maxVol * 2; + } + + *binNum = SEGMENT.custom1; // Select a bin. + *maxVol = SEGMENT.custom2 / 2; // Our volume comparator. + + uint8_t secondHand = micros() / (256-SEGMENT.speed)/500 + 1 % 16; + if (SEGENV.aux0 != secondHand) { // Triggered millis timing. + SEGENV.aux0 = secondHand; + + uint8_t pixCol = (log10f((float)FFT_MajorPeak) - 2.26f) * 177; // log10 frequency range is from 2.26 to 3.7. Let's scale accordingly. + + if (samplePeak) { + SEGMENT.setPixelColor(SEGLEN-1, CHSV(92,92,92)); + } else { + SEGMENT.setPixelColor(SEGLEN-1, color_blend(SEGCOLOR(1), SEGMENT.color_from_palette(pixCol+SEGMENT.intensity, false, PALETTE_SOLID_WRAP, 0), (int)my_magnitude)); + } + for (int i=0; i(SEGENV.data); //array of previous bar heights per frequency band + + um_data_t *um_data; + if (!usermods.getUMData(&um_data, USERMOD_ID_AUDIOREACTIVE)) { + // add support for no audio + um_data = simulateSound(SEGMENT.soundSim); + } + uint8_t *fftResult = (uint8_t*)um_data->u_data[2]; + + if (SEGENV.call == 0) for (int i=0; i= (256 - SEGMENT.intensity)) { + SEGENV.step = millis(); + rippleTime = true; + } + + SEGMENT.fadeToBlackBy(SEGMENT.speed); + + for (int x=0; x < cols; x++) { + uint8_t band = map(x, 0, cols-1, 0, NUM_BANDS - 1); + uint16_t colorIndex = band * 17; + uint16_t barHeight = map(fftResult[band], 0, 255, 0, rows); // do not subtract -1 from rows here + if (barHeight > previousBarHeight[x]) previousBarHeight[x] = barHeight; //drive the peak up + + uint32_t ledColor = BLACK; + for (int y=0; y < barHeight; y++) { + if (SEGMENT.check1) //color_vertical / color bars toggle + colorIndex = map(y, 0, rows-1, 0, 255); + + ledColor = SEGMENT.color_from_palette(colorIndex, false, PALETTE_SOLID_WRAP, 0); + SEGMENT.setPixelColorXY(x, rows-1 - y, ledColor); + } + if (previousBarHeight[x] > 0) + SEGMENT.setPixelColorXY(x, rows - previousBarHeight[x], (SEGCOLOR(2) != BLACK) ? SEGCOLOR(2) : ledColor); + + if (rippleTime && previousBarHeight[x]>0) previousBarHeight[x]--; //delay/ripple effect + } + + return FRAMETIME; +} // mode_2DGEQ() +static const char _data_FX_MODE_2DGEQ[] PROGMEM = "GEQ@Fade speed,Ripple decay,# of bands,,,Color bars,,;!,,Peak Color;!;c1=255,c2=64,pal=11,ssim=0,2d,fr"; // Beatsin + + +///////////////////////// +// ** 2D Funky plank // +///////////////////////// +uint16_t mode_2DFunkyPlank(void) { // Written by ??? Adapted by Will Tatam. + if (!strip.isMatrix) return mode_static(); // not a 2D set-up + + const uint16_t cols = SEGMENT.virtualWidth(); + const uint16_t rows = SEGMENT.virtualHeight(); + + int NUMB_BANDS = map(SEGMENT.custom1, 0, 255, 1, 16); + int barWidth = (cols / NUMB_BANDS); + int bandInc = 1; + if (barWidth == 0) { + // Matrix narrower than fft bands + barWidth = 1; + bandInc = (NUMB_BANDS / cols); + } + + um_data_t *um_data; + if (!usermods.getUMData(&um_data, USERMOD_ID_AUDIOREACTIVE)) { + // add support for no audio + um_data = simulateSound(SEGMENT.soundSim); + } + uint8_t *fftResult = (uint8_t*)um_data->u_data[2]; + + if (SEGENV.call == 0) { + SEGMENT.setUpLeds(); + SEGMENT.fill(BLACK); + } + + uint8_t secondHand = micros()/(256-SEGMENT.speed)/500+1 % 64; + if (SEGENV.aux0 != secondHand) { // Triggered millis timing. + SEGENV.aux0 = secondHand; + + // display values of + int b = 0; + for (int band = 0; band < NUMB_BANDS; band += bandInc, b++) { + int hue = fftResult[band]; + int v = map(fftResult[band], 0, 255, 10, 255); + for (int w = 0; w < barWidth; w++) { + int xpos = (barWidth * b) + w; + SEGMENT.setPixelColorXY(xpos, 0, CHSV(hue, 255, v)); + } + } + + // Update the display: + for (int i = (rows - 1); i > 0; i--) { + for (int j = (cols - 1); j >= 0; j--) { + SEGMENT.setPixelColorXY(j, i, SEGMENT.getPixelColorXY(j, i-1)); + } + } + } + + return FRAMETIME; +} // mode_2DFunkyPlank +static const char _data_FX_MODE_2DFUNKYPLANK[] PROGMEM = "Funky Plank@Scroll speed,,# of bands;;;ssim=0,2d,fr"; // Beatsin + +#endif // WLED_DISABLE_2D + + +//end audio only routines +#endif + + +#ifndef WLED_DISABLE_2D +///////////////////////// +// 2D Akemi // +///////////////////////// +static uint8_t akemi[] PROGMEM = { + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,2,2,2,2,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,2,2,3,3,3,3,3,3,2,2,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,2,3,3,0,0,0,0,0,0,3,3,2,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,2,3,0,0,0,6,5,5,4,0,0,0,3,2,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,2,3,0,0,6,6,5,5,5,5,4,4,0,0,3,2,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,2,3,0,6,5,5,5,5,5,5,5,5,4,0,3,2,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,2,3,0,6,5,5,5,5,5,5,5,5,5,5,4,0,3,2,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,3,2,0,6,5,5,5,5,5,5,5,5,5,5,4,0,2,3,0,0,0,0,0,0,0, + 0,0,0,0,0,0,3,2,3,6,5,5,7,7,5,5,5,5,7,7,5,5,4,3,2,3,0,0,0,0,0,0, + 0,0,0,0,0,2,3,1,3,6,5,1,7,7,7,5,5,1,7,7,7,5,4,3,1,3,2,0,0,0,0,0, + 0,0,0,0,0,8,3,1,3,6,5,1,7,7,7,5,5,1,7,7,7,5,4,3,1,3,8,9,0,0,0,0, + 0,0,0,0,0,8,3,1,3,6,5,5,1,1,5,5,5,5,1,1,5,5,4,3,1,3,8,0,0,0,0,0, + 0,0,0,0,0,2,3,1,3,6,5,5,5,5,5,5,5,5,5,5,5,5,4,3,1,3,2,0,0,0,0,0, + 0,0,0,0,0,0,3,2,3,6,5,5,5,5,5,5,5,5,5,5,5,5,4,3,2,3,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,6,5,5,5,5,5,7,7,5,5,5,5,5,4,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,6,5,5,5,5,5,5,5,5,5,5,5,5,4,0,0,0,0,0,0,0,0,0, + 1,0,0,0,0,0,0,0,0,6,5,5,5,5,5,5,5,5,5,5,5,5,4,0,0,0,0,0,0,0,0,2, + 0,2,2,2,0,0,0,0,0,6,5,5,5,5,5,5,5,5,5,5,5,5,4,0,0,0,0,0,2,2,2,0, + 0,0,0,3,2,0,0,0,6,5,4,4,4,4,4,4,4,4,4,4,4,4,4,4,0,0,0,2,2,0,0,0, + 0,0,0,3,2,0,0,0,6,5,5,5,5,5,5,5,5,5,5,5,5,5,5,4,0,0,0,2,3,0,0,0, + 0,0,0,0,3,2,0,0,0,0,3,3,0,3,3,0,0,3,3,0,3,3,0,0,0,0,2,2,0,0,0,0, + 0,0,0,0,3,2,0,0,0,0,3,2,0,3,2,0,0,3,2,0,3,2,0,0,0,0,2,3,0,0,0,0, + 0,0,0,0,0,3,2,0,0,3,2,0,0,3,2,0,0,3,2,0,0,3,2,0,0,2,3,0,0,0,0,0, + 0,0,0,0,0,3,2,2,2,2,0,0,0,3,2,0,0,3,2,0,0,0,3,2,2,2,3,0,0,0,0,0, + 0,0,0,0,0,0,3,3,3,0,0,0,0,3,2,0,0,3,2,0,0,0,0,3,3,3,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,3,2,0,0,3,2,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,3,2,0,0,3,2,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,3,2,0,0,3,2,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,3,2,0,0,3,2,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,3,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,3,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 +}; + +uint16_t mode_2DAkemi(void) { + if (!strip.isMatrix) return mode_static(); // not a 2D set-up + + const uint16_t cols = SEGMENT.virtualWidth(); + const uint16_t rows = SEGMENT.virtualHeight(); + + uint16_t counter = (strip.now * ((SEGMENT.speed >> 2) +2)) & 0xFFFF; + counter = counter >> 8; + + const float lightFactor = 0.15f; + const float normalFactor = 0.4f; + + um_data_t *um_data; + if (!usermods.getUMData(&um_data, USERMOD_ID_AUDIOREACTIVE)) { + um_data = simulateSound(SEGMENT.soundSim); + } + uint8_t *fftResult = (uint8_t*)um_data->u_data[2]; + float base = fftResult[0]/255.0f; + + //draw and color Akemi + for (int y=0; y < rows; y++) for (int x=0; x < cols; x++) { + CRGB color; + CRGB soundColor = ORANGE; + CRGB faceColor = SEGMENT.color_wheel(counter); + CRGB armsAndLegsColor = SEGCOLOR(1) > 0 ? SEGCOLOR(1) : 0xFFE0A0; //default warmish white 0xABA8FF; //0xFF52e5;// + uint8_t ak = pgm_read_byte_near(akemi + ((y * 32)/rows) * 32 + (x * 32)/cols); // akemi[(y * 32)/rows][(x * 32)/cols] + switch (ak) { + case 0: color = BLACK; break; + case 3: armsAndLegsColor.r *= lightFactor; armsAndLegsColor.g *= lightFactor; armsAndLegsColor.b *= lightFactor; color = armsAndLegsColor; break; //light arms and legs 0x9B9B9B + case 2: armsAndLegsColor.r *= normalFactor; armsAndLegsColor.g *= normalFactor; armsAndLegsColor.b *= normalFactor; color = armsAndLegsColor; break; //normal arms and legs 0x888888 + case 1: color = armsAndLegsColor; break; //dark arms and legs 0x686868 + case 6: faceColor.r *= lightFactor; faceColor.g *= lightFactor; faceColor.b *= lightFactor; color=faceColor; break; //light face 0x31AAFF + case 5: faceColor.r *= normalFactor; faceColor.g *= normalFactor; faceColor.b *= normalFactor; color=faceColor; break; //normal face 0x0094FF + case 4: color = faceColor; break; //dark face 0x007DC6 + case 7: color = SEGCOLOR(2) > 0 ? SEGCOLOR(2) : 0xFFFFFF; break; //eyes and mouth default white + case 8: if (base > 0.4) {soundColor.r *= base; soundColor.g *= base; soundColor.b *= base; color=soundColor;} else color = armsAndLegsColor; break; + default: color = BLACK; + } + + if (SEGMENT.intensity > 128 && fftResult && fftResult[0] > 128) { //dance if base is high + SEGMENT.setPixelColorXY(x, 0, BLACK); + SEGMENT.setPixelColorXY(x, y+1, color); + } else + SEGMENT.setPixelColorXY(x, y, color); + } + + //add geq left and right + if (um_data && fftResult) { + for (int x=0; x < cols/8; x++) { + uint16_t band = x * cols/8; + uint16_t barHeight = map(fftResult[band], 0, 255, 0, 17*rows/32); + CRGB color = SEGMENT.color_from_palette((band * 35), false, PALETTE_SOLID_WRAP, 0); + + for (int y=0; y < barHeight; y++) { + SEGMENT.setPixelColorXY(x, rows/2-y, color); + SEGMENT.setPixelColorXY(cols-1-x, rows/2-y, color); + } + } + } + + return FRAMETIME; +} // mode_2DAkemi +static const char _data_FX_MODE_2DAKEMI[] PROGMEM = "Akemi@Color speed,Dance;Head palette,Arms & Legs,Eyes & Mouth;Face palette;ssim=0,2d,fr"; //beatsin +#endif // WLED_DISABLE_2D + +#endif // WLED_DISABLE_AUDIO + +////////////////////////////////////////////////////////////////////////////////////////// +// mode data +static const char _data_RESERVED[] PROGMEM = "RSVD"; + +// add (or replace reserved) effect mode and data into vector +// use id==255 to find unallocatd gaps (with "Reserved" data string) +// if vector size() is smaller than id (single) data is appended at the end (regardless of id) +void WS2812FX::addEffect(uint8_t id, mode_ptr mode_fn, const char *mode_name) { + if (id == 255) { // find empty slot + for (size_t i=1; i<_mode.size(); i++) if (_modeData[i] == _data_RESERVED) { id = i; break; } + } + if (id < _mode.size()) { + if (_modeData[id] != _data_RESERVED) return; // do not overwrite alerady added effect + _mode[id] = mode_fn; + _modeData[id] = mode_name; + } else { + _mode.push_back(mode_fn); + _modeData.push_back(mode_name); + if (_modeCount < _mode.size()) _modeCount++; + } +} + +void WS2812FX::setupEffectData() { + // Solid must be first! (assuming vector is empty upon call to setup) + _mode.push_back(&mode_static); + _modeData.push_back(_data_FX_MODE_STATIC); + // fill reserved word in case there will be any gaps in the array + for (size_t i=1; i<_modeCount; i++) { + _mode.push_back(&mode_static); + _modeData.push_back(_data_RESERVED); + } + // now replace all pre-allocated effects + // --- 1D non-audio effects --- + addEffect(FX_MODE_BLINK, &mode_blink, _data_FX_MODE_BLINK); + addEffect(FX_MODE_COLOR_WIPE, &mode_color_wipe, _data_FX_MODE_COLOR_WIPE); + addEffect(FX_MODE_COLOR_WIPE_RANDOM, &mode_color_wipe_random, _data_FX_MODE_COLOR_WIPE_RANDOM); + addEffect(FX_MODE_RANDOM_COLOR, &mode_random_color, _data_FX_MODE_RANDOM_COLOR); + addEffect(FX_MODE_COLOR_SWEEP, &mode_color_sweep, _data_FX_MODE_COLOR_SWEEP); + addEffect(FX_MODE_DYNAMIC, &mode_dynamic, _data_FX_MODE_DYNAMIC); + addEffect(FX_MODE_RAINBOW, &mode_rainbow, _data_FX_MODE_RAINBOW); + addEffect(FX_MODE_RAINBOW_CYCLE, &mode_rainbow_cycle, _data_FX_MODE_RAINBOW_CYCLE); + addEffect(FX_MODE_SCAN, &mode_scan, _data_FX_MODE_SCAN); + addEffect(FX_MODE_DUAL_SCAN, &mode_dual_scan, _data_FX_MODE_DUAL_SCAN); + addEffect(FX_MODE_FADE, &mode_fade, _data_FX_MODE_FADE); + addEffect(FX_MODE_THEATER_CHASE, &mode_theater_chase, _data_FX_MODE_THEATER_CHASE); + addEffect(FX_MODE_THEATER_CHASE_RAINBOW, &mode_theater_chase_rainbow, _data_FX_MODE_THEATER_CHASE_RAINBOW); + addEffect(FX_MODE_SAW, &mode_saw, _data_FX_MODE_SAW); + addEffect(FX_MODE_TWINKLE, &mode_twinkle, _data_FX_MODE_TWINKLE); + addEffect(FX_MODE_DISSOLVE, &mode_dissolve, _data_FX_MODE_DISSOLVE); + addEffect(FX_MODE_DISSOLVE_RANDOM, &mode_dissolve_random, _data_FX_MODE_DISSOLVE_RANDOM); + addEffect(FX_MODE_SPARKLE, &mode_sparkle, _data_FX_MODE_SPARKLE); + addEffect(FX_MODE_FLASH_SPARKLE, &mode_flash_sparkle, _data_FX_MODE_FLASH_SPARKLE); + addEffect(FX_MODE_HYPER_SPARKLE, &mode_hyper_sparkle, _data_FX_MODE_HYPER_SPARKLE); + addEffect(FX_MODE_STROBE, &mode_strobe, _data_FX_MODE_STROBE); + addEffect(FX_MODE_STROBE_RAINBOW, &mode_strobe_rainbow, _data_FX_MODE_STROBE_RAINBOW); + addEffect(FX_MODE_MULTI_STROBE, &mode_multi_strobe, _data_FX_MODE_MULTI_STROBE); + addEffect(FX_MODE_BLINK_RAINBOW, &mode_blink_rainbow, _data_FX_MODE_BLINK_RAINBOW); + addEffect(FX_MODE_ANDROID, &mode_android, _data_FX_MODE_ANDROID); + addEffect(FX_MODE_CHASE_COLOR, &mode_chase_color, _data_FX_MODE_CHASE_COLOR); + addEffect(FX_MODE_CHASE_RANDOM, &mode_chase_random, _data_FX_MODE_CHASE_RANDOM); + addEffect(FX_MODE_CHASE_RAINBOW, &mode_chase_rainbow, _data_FX_MODE_CHASE_RAINBOW); + addEffect(FX_MODE_CHASE_FLASH, &mode_chase_flash, _data_FX_MODE_CHASE_FLASH); + addEffect(FX_MODE_CHASE_FLASH_RANDOM, &mode_chase_flash_random, _data_FX_MODE_CHASE_FLASH_RANDOM); + addEffect(FX_MODE_CHASE_RAINBOW_WHITE, &mode_chase_rainbow_white, _data_FX_MODE_CHASE_RAINBOW_WHITE); + addEffect(FX_MODE_COLORFUL, &mode_colorful, _data_FX_MODE_COLORFUL); + addEffect(FX_MODE_TRAFFIC_LIGHT, &mode_traffic_light, _data_FX_MODE_TRAFFIC_LIGHT); + addEffect(FX_MODE_COLOR_SWEEP_RANDOM, &mode_color_sweep_random, _data_FX_MODE_COLOR_SWEEP_RANDOM); + addEffect(FX_MODE_RUNNING_COLOR, &mode_running_color, _data_FX_MODE_RUNNING_COLOR); + addEffect(FX_MODE_AURORA, &mode_aurora, _data_FX_MODE_AURORA); + addEffect(FX_MODE_RUNNING_RANDOM, &mode_running_random, _data_FX_MODE_RUNNING_RANDOM); + addEffect(FX_MODE_LARSON_SCANNER, &mode_larson_scanner, _data_FX_MODE_LARSON_SCANNER); + addEffect(FX_MODE_COMET, &mode_comet, _data_FX_MODE_COMET); + addEffect(FX_MODE_FIREWORKS, &mode_fireworks, _data_FX_MODE_FIREWORKS); + addEffect(FX_MODE_RAIN, &mode_rain, _data_FX_MODE_RAIN); + addEffect(FX_MODE_TETRIX, &mode_tetrix, _data_FX_MODE_TETRIX); + addEffect(FX_MODE_FIRE_FLICKER, &mode_fire_flicker, _data_FX_MODE_FIRE_FLICKER); + addEffect(FX_MODE_GRADIENT, &mode_gradient, _data_FX_MODE_GRADIENT); + addEffect(FX_MODE_LOADING, &mode_loading, _data_FX_MODE_LOADING); + addEffect(FX_MODE_WAVESINS, &mode_wavesins, _data_FX_MODE_WAVESINS); + addEffect(FX_MODE_FAIRY, &mode_fairy, _data_FX_MODE_FAIRY); + addEffect(FX_MODE_TWO_DOTS, &mode_two_dots, _data_FX_MODE_TWO_DOTS); + addEffect(FX_MODE_FAIRYTWINKLE, &mode_fairytwinkle, _data_FX_MODE_FAIRYTWINKLE); + addEffect(FX_MODE_RUNNING_DUAL, &mode_running_dual, _data_FX_MODE_RUNNING_DUAL); + addEffect(FX_MODE_PERLINMOVE, &mode_perlinmove, _data_FX_MODE_PERLINMOVE); + addEffect(FX_MODE_TRICOLOR_CHASE, &mode_tricolor_chase, _data_FX_MODE_TRICOLOR_CHASE); + addEffect(FX_MODE_TRICOLOR_WIPE, &mode_tricolor_wipe, _data_FX_MODE_TRICOLOR_WIPE); + addEffect(FX_MODE_TRICOLOR_FADE, &mode_tricolor_fade, _data_FX_MODE_TRICOLOR_FADE); + addEffect(FX_MODE_BREATH, &mode_breath, _data_FX_MODE_BREATH); + addEffect(FX_MODE_RUNNING_LIGHTS, &mode_running_lights, _data_FX_MODE_RUNNING_LIGHTS); + addEffect(FX_MODE_LIGHTNING, &mode_lightning, _data_FX_MODE_LIGHTNING); + addEffect(FX_MODE_ICU, &mode_icu, _data_FX_MODE_ICU); + addEffect(FX_MODE_MULTI_COMET, &mode_multi_comet, _data_FX_MODE_MULTI_COMET); + addEffect(FX_MODE_DUAL_LARSON_SCANNER, &mode_dual_larson_scanner, _data_FX_MODE_DUAL_LARSON_SCANNER); + addEffect(FX_MODE_RANDOM_CHASE, &mode_random_chase, _data_FX_MODE_RANDOM_CHASE); + addEffect(FX_MODE_OSCILLATE, &mode_oscillate, _data_FX_MODE_OSCILLATE); + addEffect(FX_MODE_FIRE_2012, &mode_fire_2012, _data_FX_MODE_FIRE_2012); + addEffect(FX_MODE_PRIDE_2015, &mode_pride_2015, _data_FX_MODE_PRIDE_2015); + addEffect(FX_MODE_BPM, &mode_bpm, _data_FX_MODE_BPM); + addEffect(FX_MODE_JUGGLE, &mode_juggle, _data_FX_MODE_JUGGLE); + addEffect(FX_MODE_PALETTE, &mode_palette, _data_FX_MODE_PALETTE); + addEffect(FX_MODE_COLORWAVES, &mode_colorwaves, _data_FX_MODE_COLORWAVES); + addEffect(FX_MODE_FILLNOISE8, &mode_fillnoise8, _data_FX_MODE_FILLNOISE8); + addEffect(FX_MODE_NOISE16_1, &mode_noise16_1, _data_FX_MODE_NOISE16_1); + addEffect(FX_MODE_NOISE16_2, &mode_noise16_2, _data_FX_MODE_NOISE16_2); + addEffect(FX_MODE_NOISE16_3, &mode_noise16_3, _data_FX_MODE_NOISE16_3); + addEffect(FX_MODE_NOISE16_4, &mode_noise16_4, _data_FX_MODE_NOISE16_4); + addEffect(FX_MODE_COLORTWINKLE, &mode_colortwinkle, _data_FX_MODE_COLORTWINKLE); + addEffect(FX_MODE_LAKE, &mode_lake, _data_FX_MODE_LAKE); + addEffect(FX_MODE_METEOR, &mode_meteor, _data_FX_MODE_METEOR); + addEffect(FX_MODE_METEOR_SMOOTH, &mode_meteor_smooth, _data_FX_MODE_METEOR_SMOOTH); + addEffect(FX_MODE_RAILWAY, &mode_railway, _data_FX_MODE_RAILWAY); + addEffect(FX_MODE_RIPPLE, &mode_ripple, _data_FX_MODE_RIPPLE); + addEffect(FX_MODE_TWINKLEFOX, &mode_twinklefox, _data_FX_MODE_TWINKLEFOX); + addEffect(FX_MODE_TWINKLECAT, &mode_twinklecat, _data_FX_MODE_TWINKLECAT); + addEffect(FX_MODE_HALLOWEEN_EYES, &mode_halloween_eyes, _data_FX_MODE_HALLOWEEN_EYES); + addEffect(FX_MODE_STATIC_PATTERN, &mode_static_pattern, _data_FX_MODE_STATIC_PATTERN); + addEffect(FX_MODE_TRI_STATIC_PATTERN, &mode_tri_static_pattern, _data_FX_MODE_TRI_STATIC_PATTERN); + addEffect(FX_MODE_SPOTS, &mode_spots, _data_FX_MODE_SPOTS); + addEffect(FX_MODE_SPOTS_FADE, &mode_spots_fade, _data_FX_MODE_SPOTS_FADE); + addEffect(FX_MODE_GLITTER, &mode_glitter, _data_FX_MODE_GLITTER); + addEffect(FX_MODE_CANDLE, &mode_candle, _data_FX_MODE_CANDLE); + addEffect(FX_MODE_STARBURST, &mode_starburst, _data_FX_MODE_STARBURST); + addEffect(FX_MODE_EXPLODING_FIREWORKS, &mode_exploding_fireworks, _data_FX_MODE_EXPLODING_FIREWORKS); + addEffect(FX_MODE_BOUNCINGBALLS, &mode_bouncing_balls, _data_FX_MODE_BOUNCINGBALLS); + addEffect(FX_MODE_SINELON, &mode_sinelon, _data_FX_MODE_SINELON); + addEffect(FX_MODE_SINELON_DUAL, &mode_sinelon_dual, _data_FX_MODE_SINELON_DUAL); + addEffect(FX_MODE_SINELON_RAINBOW, &mode_sinelon_rainbow, _data_FX_MODE_SINELON_RAINBOW); + addEffect(FX_MODE_POPCORN, &mode_popcorn, _data_FX_MODE_POPCORN); + addEffect(FX_MODE_DRIP, &mode_drip, _data_FX_MODE_DRIP); + addEffect(FX_MODE_PLASMA, &mode_plasma, _data_FX_MODE_PLASMA); + addEffect(FX_MODE_PERCENT, &mode_percent, _data_FX_MODE_PERCENT); + addEffect(FX_MODE_RIPPLE_RAINBOW, &mode_ripple_rainbow, _data_FX_MODE_RIPPLE_RAINBOW); + addEffect(FX_MODE_HEARTBEAT, &mode_heartbeat, _data_FX_MODE_HEARTBEAT); + addEffect(FX_MODE_PACIFICA, &mode_pacifica, _data_FX_MODE_PACIFICA); + addEffect(FX_MODE_CANDLE_MULTI, &mode_candle_multi, _data_FX_MODE_CANDLE_MULTI); + addEffect(FX_MODE_SOLID_GLITTER, &mode_solid_glitter, _data_FX_MODE_SOLID_GLITTER); + addEffect(FX_MODE_SUNRISE, &mode_sunrise, _data_FX_MODE_SUNRISE); + addEffect(FX_MODE_PHASED, &mode_phased, _data_FX_MODE_PHASED); + addEffect(FX_MODE_TWINKLEUP, &mode_twinkleup, _data_FX_MODE_TWINKLEUP); + addEffect(FX_MODE_NOISEPAL, &mode_noisepal, _data_FX_MODE_NOISEPAL); + addEffect(FX_MODE_SINEWAVE, &mode_sinewave, _data_FX_MODE_SINEWAVE); + addEffect(FX_MODE_PHASEDNOISE, &mode_phased_noise, _data_FX_MODE_PHASEDNOISE); + addEffect(FX_MODE_FLOW, &mode_flow, _data_FX_MODE_FLOW); + addEffect(FX_MODE_CHUNCHUN, &mode_chunchun, _data_FX_MODE_CHUNCHUN); + addEffect(FX_MODE_DANCING_SHADOWS, &mode_dancing_shadows, _data_FX_MODE_DANCING_SHADOWS); + addEffect(FX_MODE_WASHING_MACHINE, &mode_washing_machine, _data_FX_MODE_WASHING_MACHINE); + addEffect(FX_MODE_FLOWSTRIPE, &mode_FlowStripe, _data_FX_MODE_FLOWSTRIPE); + addEffect(FX_MODE_BLENDS, &mode_blends, _data_FX_MODE_BLENDS); + addEffect(FX_MODE_TV_SIMULATOR, &mode_tv_simulator, _data_FX_MODE_TV_SIMULATOR); + addEffect(FX_MODE_DYNAMIC_SMOOTH, &mode_dynamic_smooth, _data_FX_MODE_DYNAMIC_SMOOTH); + + // --- 2D non-audio effects --- +#ifndef WLED_DISABLE_2D + addEffect(FX_MODE_2DSPACESHIPS, &mode_2Dspaceships, _data_FX_MODE_2DSPACESHIPS); + addEffect(FX_MODE_2DCRAZYBEES, &mode_2Dcrazybees, _data_FX_MODE_2DCRAZYBEES); + addEffect(FX_MODE_2DGHOSTRIDER, &mode_2Dghostrider, _data_FX_MODE_2DGHOSTRIDER); + addEffect(FX_MODE_2DBLOBS, &mode_2Dfloatingblobs, _data_FX_MODE_2DBLOBS); + addEffect(FX_MODE_2DSCROLLTEXT, &mode_2Dscrollingtext, _data_FX_MODE_2DSCROLLTEXT); + addEffect(FX_MODE_2DDRIFTROSE, &mode_2Ddriftrose, _data_FX_MODE_2DDRIFTROSE); + addEffect(FX_MODE_2DBLACKHOLE, &mode_2DBlackHole, _data_FX_MODE_2DBLACKHOLE); + addEffect(FX_MODE_2DDNASPIRAL, &mode_2DDNASpiral, _data_FX_MODE_2DDNASPIRAL); + addEffect(FX_MODE_2DHIPHOTIC, &mode_2DHiphotic, _data_FX_MODE_2DHIPHOTIC); + addEffect(FX_MODE_2DPLASMABALL, &mode_2DPlasmaball, _data_FX_MODE_2DPLASMABALL); + addEffect(FX_MODE_2DSINDOTS, &mode_2DSindots, _data_FX_MODE_2DSINDOTS); + addEffect(FX_MODE_2DFRIZZLES, &mode_2DFrizzles, _data_FX_MODE_2DFRIZZLES); + addEffect(FX_MODE_2DLISSAJOUS, &mode_2DLissajous, _data_FX_MODE_2DLISSAJOUS); + addEffect(FX_MODE_2DPOLARLIGHTS, &mode_2DPolarLights, _data_FX_MODE_2DPOLARLIGHTS); + addEffect(FX_MODE_2DTARTAN, &mode_2Dtartan, _data_FX_MODE_2DTARTAN); + addEffect(FX_MODE_2DGAMEOFLIFE, &mode_2Dgameoflife, _data_FX_MODE_2DGAMEOFLIFE); + addEffect(FX_MODE_2DJULIA, &mode_2DJulia, _data_FX_MODE_2DJULIA); + addEffect(FX_MODE_2DCOLOREDBURSTS, &mode_2DColoredBursts, _data_FX_MODE_2DCOLOREDBURSTS); + addEffect(FX_MODE_2DSUNRADIATION, &mode_2DSunradiation, _data_FX_MODE_2DSUNRADIATION); + addEffect(FX_MODE_2DNOISE, &mode_2Dnoise, _data_FX_MODE_2DNOISE); + addEffect(FX_MODE_2DFIRENOISE, &mode_2Dfirenoise, _data_FX_MODE_2DFIRENOISE); + addEffect(FX_MODE_2DSQUAREDSWIRL, &mode_2Dsquaredswirl, _data_FX_MODE_2DSQUAREDSWIRL); + addEffect(FX_MODE_2DDNA, &mode_2Ddna, _data_FX_MODE_2DDNA); + addEffect(FX_MODE_2DMATRIX, &mode_2Dmatrix, _data_FX_MODE_2DMATRIX); + addEffect(FX_MODE_2DMETABALLS, &mode_2Dmetaballs, _data_FX_MODE_2DMETABALLS); + addEffect(FX_MODE_2DPULSER, &mode_2DPulser, _data_FX_MODE_2DPULSER); + addEffect(FX_MODE_2DDRIFT, &mode_2DDrift, _data_FX_MODE_2DDRIFT); + + // --- 2D audio effects --- + #ifndef WLED_DISABLE_AUDIO + addEffect(FX_MODE_2DWAVERLY, &mode_2DWaverly, _data_FX_MODE_2DWAVERLY); + addEffect(FX_MODE_2DSWIRL, &mode_2DSwirl, _data_FX_MODE_2DSWIRL); + addEffect(FX_MODE_2DAKEMI, &mode_2DAkemi, _data_FX_MODE_2DAKEMI); + #endif +#endif // WLED_DISABLE_2D + +#ifndef WLED_DISABLE_AUDIO + // --- 1D audio effects --- + addEffect(FX_MODE_PIXELWAVE, &mode_pixelwave, _data_FX_MODE_PIXELWAVE); + addEffect(FX_MODE_JUGGLES, &mode_juggles, _data_FX_MODE_JUGGLES); + addEffect(FX_MODE_MATRIPIX, &mode_matripix, _data_FX_MODE_MATRIPIX); + addEffect(FX_MODE_GRAVIMETER, &mode_gravimeter, _data_FX_MODE_GRAVIMETER); + addEffect(FX_MODE_PLASMOID, &mode_plasmoid, _data_FX_MODE_PLASMOID); + addEffect(FX_MODE_PUDDLES, &mode_puddles, _data_FX_MODE_PUDDLES); + addEffect(FX_MODE_MIDNOISE, &mode_midnoise, _data_FX_MODE_MIDNOISE); + addEffect(FX_MODE_NOISEMETER, &mode_noisemeter, _data_FX_MODE_NOISEMETER); + addEffect(FX_MODE_NOISEFIRE, &mode_noisefire, _data_FX_MODE_NOISEFIRE); + addEffect(FX_MODE_PUDDLEPEAK, &mode_puddlepeak, _data_FX_MODE_PUDDLEPEAK); + addEffect(FX_MODE_RIPPLEPEAK, &mode_ripplepeak, _data_FX_MODE_RIPPLEPEAK); + addEffect(FX_MODE_GRAVCENTER, &mode_gravcenter, _data_FX_MODE_GRAVCENTER); + addEffect(FX_MODE_GRAVCENTRIC, &mode_gravcentric, _data_FX_MODE_GRAVCENTRIC); +#endif // WLED_DISABLE_AUDIO + +#ifdef USERMOD_AUDIOREACTIVE + // --- WLED-SR audio reactive usermod only effects --- + #ifdef WLED_DISABLE_AUDIO + #error Incompatible options: WLED_DISABLE_AUDIO and USERMOD_AUDIOREACTIVE + #endif + #ifdef WLED_DISABLE_2D + #error AUDIOREACTIVE usermod requires 2D support. + #endif + addEffect(FX_MODE_PIXELS, &mode_pixels, _data_FX_MODE_PIXELS); + addEffect(FX_MODE_FREQWAVE, &mode_freqwave, _data_FX_MODE_FREQWAVE); + addEffect(FX_MODE_FREQMATRIX, &mode_freqmatrix, _data_FX_MODE_FREQMATRIX); + addEffect(FX_MODE_2DGEQ, &mode_2DGEQ, _data_FX_MODE_2DGEQ); + addEffect(FX_MODE_WATERFALL, &mode_waterfall, _data_FX_MODE_WATERFALL); + addEffect(FX_MODE_FREQPIXELS, &mode_freqpixels, _data_FX_MODE_FREQPIXELS); + addEffect(FX_MODE_NOISEMOVE, &mode_noisemove, _data_FX_MODE_NOISEMOVE); + addEffect(FX_MODE_FREQMAP, &mode_freqmap, _data_FX_MODE_FREQMAP); + addEffect(FX_MODE_GRAVFREQ, &mode_gravfreq, _data_FX_MODE_GRAVFREQ); + addEffect(FX_MODE_DJLIGHT, &mode_DJLight, _data_FX_MODE_DJLIGHT); + addEffect(FX_MODE_2DFUNKYPLANK, &mode_2DFunkyPlank, _data_FX_MODE_2DFUNKYPLANK); + addEffect(FX_MODE_BLURZ, &mode_blurz, _data_FX_MODE_BLURZ); + addEffect(FX_MODE_ROCKTAVES, &mode_rocktaves, _data_FX_MODE_ROCKTAVES); + //addEffect(FX_MODE_CUSTOMEFFECT, &mode_customEffect, _data_FX_MODE_CUSTOMEFFECT); //WLEDSR Custom Effects +#endif // USERMOD_AUDIOREACTIVE +} diff --git a/wled00/FX.h b/wled00/FX.h index aeefee35eb..486a07e12f 100644 --- a/wled00/FX.h +++ b/wled00/FX.h @@ -27,6 +27,8 @@ #ifndef WS2812FX_h #define WS2812FX_h +#include + #include "const.h" #define FASTLED_INTERNAL //remove annoying pragma messages @@ -38,6 +40,9 @@ #define DEFAULT_SPEED (uint8_t)128 #define DEFAULT_INTENSITY (uint8_t)128 #define DEFAULT_COLOR (uint32_t)0xFFAA00 +#define DEFAULT_C1 (uint8_t)128 +#define DEFAULT_C2 (uint8_t)128 +#define DEFAULT_C3 (uint8_t)16 #ifndef MIN #define MIN(a,b) ((a)<(b)?(a):(b)) @@ -54,37 +59,37 @@ /* Not used in all effects yet */ #define WLED_FPS 42 #define FRAMETIME_FIXED (1000/WLED_FPS) -#define FRAMETIME _frametime +//#define FRAMETIME _frametime +#define FRAMETIME strip.getFrameTime() /* each segment uses 52 bytes of SRAM memory, so if you're application fails because of insufficient memory, decreasing MAX_NUM_SEGMENTS may help */ #ifdef ESP8266 #define MAX_NUM_SEGMENTS 16 - /* How many color transitions can run at once */ - #define MAX_NUM_TRANSITIONS 8 /* How much data bytes all segments combined may allocate */ - #define MAX_SEGMENT_DATA 4096 + #define MAX_SEGMENT_DATA 5120 #else #ifndef MAX_NUM_SEGMENTS #define MAX_NUM_SEGMENTS 32 #endif - #define MAX_NUM_TRANSITIONS 24 - #define MAX_SEGMENT_DATA 20480 + #define MAX_SEGMENT_DATA 32767 #endif /* How much data bytes each segment should max allocate to leave enough space for other segments, assuming each segment uses the same amount of data. 256 for ESP8266, 640 for ESP32. */ -#define FAIR_DATA_PER_SEG (MAX_SEGMENT_DATA / MAX_NUM_SEGMENTS) +#define FAIR_DATA_PER_SEG (MAX_SEGMENT_DATA / strip.getMaxSegments()) #define MIN_SHOW_DELAY (_frametime < 16 ? 8 : 15) #define NUM_COLORS 3 /* number of colors per segment */ -#define SEGMENT _segments[_segment_index] -#define SEGCOLOR(x) _colors_t[x] -#define SEGENV _segment_runtimes[_segment_index] -#define SEGLEN _virtualSegmentLength -#define SEGACT SEGMENT.stop -#define SPEED_FORMULA_L 5U + (50U*(255U - SEGMENT.speed))/SEGLEN +#define SEGMENT strip._segments[strip.getCurrSegmentId()] +#define SEGENV strip._segments[strip.getCurrSegmentId()] +//#define SEGCOLOR(x) strip._segments[strip.getCurrSegmentId()].currentColor(x, strip._segments[strip.getCurrSegmentId()].colors[x]) +//#define SEGLEN strip._segments[strip.getCurrSegmentId()].virtualLength() +#define SEGCOLOR(x) strip.segColor(x) /* saves us a few kbytes of code */ +#define SEGPALETTE strip._currentPalette +#define SEGLEN strip._virtualSegmentLength /* saves us a few kbytes of code */ +#define SPEED_FORMULA_L (5U + (50U*(255U - SEGMENT.speed))/SEGLEN) // some common colors #define RED (uint32_t)0xFF0000 @@ -99,6 +104,8 @@ #define ORANGE (uint32_t)0xFF3000 #define PINK (uint32_t)0xFF1493 #define ULTRAWHITE (uint32_t)0xFFFFFFFF +#define DARKSLATEGRAY (uint32_t)0x2F4F4F +#define DARKSLATEGREY (uint32_t)0x2F4F4F // options // bit 7: segment is in transition mode @@ -107,19 +114,15 @@ // bit 2: segment is on // bit 1: reverse segment // bit 0: segment is selected -#define NO_OPTIONS (uint8_t)0x00 -#define TRANSITIONAL (uint8_t)0x80 -#define MIRROR (uint8_t)0x08 -#define SEGMENT_ON (uint8_t)0x04 -#define REVERSE (uint8_t)0x02 -#define SELECTED (uint8_t)0x01 -#define IS_TRANSITIONAL ((SEGMENT.options & TRANSITIONAL) == TRANSITIONAL) -#define IS_MIRROR ((SEGMENT.options & MIRROR ) == MIRROR ) -#define IS_SEGMENT_ON ((SEGMENT.options & SEGMENT_ON ) == SEGMENT_ON ) -#define IS_REVERSE ((SEGMENT.options & REVERSE ) == REVERSE ) -#define IS_SELECTED ((SEGMENT.options & SELECTED ) == SELECTED ) - -#define MODE_COUNT 118 +#define NO_OPTIONS (uint16_t)0x0000 +#define TRANSPOSED (uint16_t)0x0400 // rotated 90deg & reversed +#define REVERSE_Y_2D (uint16_t)0x0200 +#define MIRROR_Y_2D (uint16_t)0x0100 +#define TRANSITIONAL (uint16_t)0x0080 +#define MIRROR (uint16_t)0x0008 +#define SEGMENT_ON (uint16_t)0x0004 +#define REVERSE (uint16_t)0x0002 +#define SELECTED (uint16_t)0x0001 #define FX_MODE_STATIC 0 #define FX_MODE_BLINK 1 @@ -169,12 +172,14 @@ #define FX_MODE_FIRE_FLICKER 45 #define FX_MODE_GRADIENT 46 #define FX_MODE_LOADING 47 -#define FX_MODE_POLICE 48 // candidate for removal (after below three) +//#define FX_MODE_POLICE 48 // candidate for removal (after below three) +#define FX_MODE_WAVESINS 48 // was Police prior to 0.14 (use Two Dots with Red/Blue) #define FX_MODE_FAIRY 49 //was Police All prior to 0.13.0-b6 (use "Two Dots" with Red/Blue and full intensity) #define FX_MODE_TWO_DOTS 50 #define FX_MODE_FAIRYTWINKLE 51 //was Two Areas prior to 0.13.0-b6 (use "Two Dots" with full intensity) #define FX_MODE_RUNNING_DUAL 52 -#define FX_MODE_HALLOWEEN 53 // candidate for removal +//#define FX_MODE_HALLOWEEN 53 // candidate for removal +#define FX_MODE_PERLINMOVE 53 // was Halloween prior to 0.14 (use Cahse 2 with Purple/Orange) #define FX_MODE_TRICOLOR_CHASE 54 #define FX_MODE_TRICOLOR_WIPE 55 #define FX_MODE_TRICOLOR_FADE 56 @@ -235,720 +240,662 @@ #define FX_MODE_CHUNCHUN 111 #define FX_MODE_DANCING_SHADOWS 112 #define FX_MODE_WASHING_MACHINE 113 -#define FX_MODE_CANDY_CANE 114 // candidate for removal +//#define FX_MODE_CANDY_CANE 114 // candidate for removal +#define FX_MODE_FLOWSTRIPE 114 // was Cany Cane prior to 0.14 (use Cahse 2 with Red/White) #define FX_MODE_BLENDS 115 #define FX_MODE_TV_SIMULATOR 116 #define FX_MODE_DYNAMIC_SMOOTH 117 +#ifndef WLED_DISABLE_2D + // new 2D effects + #define FX_MODE_2DSPACESHIPS 118 + #define FX_MODE_2DCRAZYBEES 119 + #define FX_MODE_2DGHOSTRIDER 120 + #define FX_MODE_2DBLOBS 121 + #define FX_MODE_2DSCROLLTEXT 122 + #define FX_MODE_2DDRIFTROSE 123 + // WLED-SR effects (non SR compatible IDs) + #define FX_MODE_2DBLACKHOLE 124 // non audio + #define FX_MODE_2DDNASPIRAL 125 // non audio + #define FX_MODE_2DHIPHOTIC 126 // non audio + #define FX_MODE_2DPLASMABALL 127 // non audio + #define FX_MODE_2DSINDOTS 128 // non audio + #define FX_MODE_2DFRIZZLES 129 // non audio + #define FX_MODE_2DLISSAJOUS 130 // non audio + #define FX_MODE_2DPOLARLIGHTS 131 // non audio + #define FX_MODE_2DTARTAN 132 // non audio + #define FX_MODE_2DGAMEOFLIFE 133 // non audio + #define FX_MODE_2DJULIA 134 // non audio + #define FX_MODE_2DCOLOREDBURSTS 135 // non audio + #define FX_MODE_2DSUNRADIATION 136 // non audio + #define FX_MODE_2DNOISE 137 // non audio + #define FX_MODE_2DFIRENOISE 138 // non audio + #define FX_MODE_2DSQUAREDSWIRL 139 // non audio + #define FX_MODE_2DDNA 140 // non audio + #define FX_MODE_2DMATRIX 141 // non audio + #define FX_MODE_2DMETABALLS 142 // non audio + #define FX_MODE_2DPULSER 143 // non audio + #define FX_MODE_2DDRIFT 144 // non audio +#endif +#ifndef WLED_DISABLE_AUDIO + #ifndef WLED_DISABLE_2D + #define FX_MODE_2DWAVERLY 145 // audio enhanced + #define FX_MODE_2DSWIRL 146 // audio enhanced + #define FX_MODE_2DAKEMI 147 // audio enhanced + // 148 & 149 reserved + #endif + #define FX_MODE_PIXELWAVE 150 // audio enhanced + #define FX_MODE_JUGGLES 151 // audio enhanced + #define FX_MODE_MATRIPIX 152 // audio enhanced + #define FX_MODE_GRAVIMETER 153 // audio enhanced + #define FX_MODE_PLASMOID 154 // audio enhanced + #define FX_MODE_PUDDLES 155 // audio enhanced + #define FX_MODE_MIDNOISE 156 // audio enhanced + #define FX_MODE_NOISEMETER 157 // audio enhanced + #define FX_MODE_NOISEFIRE 158 // audio enhanced + #define FX_MODE_PUDDLEPEAK 159 // audio enhanced + #define FX_MODE_RIPPLEPEAK 160 // audio enhanced + #define FX_MODE_GRAVCENTER 161 // audio enhanced + #define FX_MODE_GRAVCENTRIC 162 // audio enhanced +#endif +#ifndef USERMOD_AUDIOREACTIVE -class WS2812FX { - typedef uint16_t (WS2812FX::*mode_ptr)(void); + #ifndef WLED_DISABLE_AUDIO + #define MODE_COUNT 163 + #else + #ifndef WLED_DISABLE_2D + #define MODE_COUNT 145 + #else + #define MODE_COUNT 118 + #endif + #endif - // pre show callback - typedef void (*show_callback) (void); +#else - static WS2812FX* instance; - - // segment parameters + #ifdef WLED_DISABLE_AUDIO + #error Incompatible options: WLED_DISABLE_AUDIO and USERMOD_AUDIOREACTIVE + #endif + #ifdef WLED_DISABLE_2D + #error AUDIOREACTIVE usermod requires 2D support. + #endif + #define FX_MODE_2DGEQ 148 + #define FX_MODE_2DFUNKYPLANK 149 + #define FX_MODE_PIXELS 163 + #define FX_MODE_FREQWAVE 164 + #define FX_MODE_FREQMATRIX 165 + #define FX_MODE_WATERFALL 166 + #define FX_MODE_FREQPIXELS 167 + #define FX_MODE_BINMAP 168 + #define FX_MODE_NOISEMOVE 169 + #define FX_MODE_FREQMAP 170 + #define FX_MODE_GRAVFREQ 171 + #define FX_MODE_DJLIGHT 172 + #define FX_MODE_BLURZ 173 + #define FX_MODE_ROCKTAVES 174 + //#define FX_MODE_CUSTOMEFFECT 175 //WLEDSR Custom Effects + + #define MODE_COUNT 175 +#endif + +typedef enum mapping1D2D { + M12_Pixels = 0, + M12_VerticalBar = 1, + M12_Circle = 2, + M12_Block = 3 +} mapping1D2D_t; + +// segment, 68 (92 in memory) bytes +typedef struct Segment { public: - typedef struct Segment { // 31 (32 in memory) bytes - uint16_t start; - uint16_t stop; //segment invalid if stop == 0 - uint16_t offset; - uint8_t speed; - uint8_t intensity; - uint8_t palette; - uint8_t mode; - uint8_t options; //bit pattern: msb first: transitional needspixelstate tbd tbd (paused) on reverse selected - uint8_t grouping, spacing; - uint8_t opacity; - uint32_t colors[NUM_COLORS]; - uint8_t cct; //0==1900K, 255==10091K + uint16_t start; // start index / start X coordinate 2D (left) + uint16_t stop; // stop index / stop X coordinate 2D (right); segment is invalid if stop == 0 + uint16_t offset; + uint8_t speed; + uint8_t intensity; + uint8_t palette; + uint8_t mode; + union { + uint16_t options; //bit pattern: msb first: [transposed mirrorY reverseY] transitional (tbd) paused needspixelstate mirrored on reverse selected + struct { + bool selected : 1; // 0 : selected + bool reverse : 1; // 1 : reversed + bool on : 1; // 2 : is On + bool mirror : 1; // 3 : mirrored + bool pxs : 1; // 4 : indicates that the effect does not use FRAMETIME or needs getPixelColor (?) + bool freeze : 1; // 5 : paused/frozen + bool reset : 1; // 6 : indicates that Segment runtime requires reset + bool transitional: 1; // 7 : transitional (there is transition occuring) + bool reverse_y : 1; // 8 : reversed Y (2D) + bool mirror_y : 1; // 9 : mirrored Y (2D) + bool transpose : 1; // 10 : transposed (2D, swapped X & Y) + uint8_t map1D2D : 2; // 11-12 : mapping for 1D effect on 2D (0-strip, 1-expand vertically, 2-circular, 3-rectangular) + uint8_t soundSim : 3; // 13-15 : 0-7 sound simulation types + }; + }; + uint8_t grouping, spacing; + uint8_t opacity; + uint32_t colors[NUM_COLORS]; + uint8_t cct; //0==1900K, 255==10091K + uint8_t custom1, custom2; // custom FX parameters/sliders + struct { + uint8_t custom3 : 5; // reduced range slider (0-31) + bool check1 : 1; // checkmark 1 + bool check2 : 1; // checkmark 2 + bool check3 : 1; // checkmark 3 + }; + uint16_t startY; // start Y coodrinate 2D (top) + uint16_t stopY; // stop Y coordinate 2D (bottom) + char *name; + + // runtime data + unsigned long next_time; // millis() of next update + uint32_t step; // custom "step" var + uint32_t call; // call counter + uint16_t aux0; // custom var + uint16_t aux1; // custom var + byte* data; + CRGB* leds; + static CRGB *_globalLeds; + + private: + union { uint8_t _capabilities; - char *name; - bool setColor(uint8_t slot, uint32_t c, uint8_t segn) { //returns true if changed - if (slot >= NUM_COLORS || segn >= MAX_NUM_SEGMENTS) return false; - if (c == colors[slot]) return false; - uint8_t b = (slot == 1) ? cct : opacity; - ColorTransition::startTransition(b, colors[slot], instance->_transitionDur, segn, slot); - colors[slot] = c; return true; - } - void setCCT(uint16_t k, uint8_t segn) { - if (segn >= MAX_NUM_SEGMENTS) return; - if (k > 255) { //kelvin value, convert to 0-255 - if (k < 1900) k = 1900; - if (k > 10091) k = 10091; - k = (k - 1900) >> 5; - } - if (cct == k) return; - ColorTransition::startTransition(cct, colors[1], instance->_transitionDur, segn, 1); - cct = k; - } - void setOpacity(uint8_t o, uint8_t segn) { - if (segn >= MAX_NUM_SEGMENTS) return; - if (opacity == o) return; - ColorTransition::startTransition(opacity, colors[0], instance->_transitionDur, segn, 0); - opacity = o; - } - void setOption(uint8_t n, bool val, uint8_t segn = 255) - { - bool prevOn = false; - if (n == SEG_OPTION_ON) { - prevOn = getOption(SEG_OPTION_ON); - if (!val && prevOn) { //fade off - ColorTransition::startTransition(opacity, colors[0], instance->_transitionDur, segn, 0); - } - } - - if (val) { - options |= 0x01 << n; - } else - { - options &= ~(0x01 << n); - } - - if (n == SEG_OPTION_ON && val && !prevOn) { //fade on - ColorTransition::startTransition(0, colors[0], instance->_transitionDur, segn, 0); - } - } - bool getOption(uint8_t n) - { - return ((options >> n) & 0x01); - } - inline bool isSelected() - { - return getOption(0); - } - inline bool isActive() - { - return stop > start; - } - inline uint16_t length() - { - return stop - start; - } - inline uint16_t groupLength() - { - return grouping + spacing; - } - uint16_t virtualLength() - { - uint16_t groupLen = groupLength(); - uint16_t vLength = (length() + groupLen - 1) / groupLen; - if (options & MIRROR) - vLength = (vLength + 1) /2; // divide by 2 if mirror, leave at least a single LED - return vLength; - } - uint8_t differs(Segment& b); - inline uint8_t getLightCapabilities() {return _capabilities;} - void refreshLightCapabilities(); - } segment; - - // segment runtime parameters - typedef struct Segment_runtime { // 28 bytes - unsigned long next_time; // millis() of next update - uint32_t step; // custom "step" var - uint32_t call; // call counter - uint16_t aux0; // custom var - uint16_t aux1; // custom var - byte* data = nullptr; - bool allocateData(uint16_t len){ - if (data && _dataLen == len) return true; //already allocated - deallocateData(); - if (WS2812FX::instance->_usedSegmentData + len > MAX_SEGMENT_DATA) return false; //not enough memory - // if possible use SPI RAM on ESP32 - #if defined(ARDUINO_ARCH_ESP32) && defined(WLED_USE_PSRAM) - if (psramFound()) - data = (byte*) ps_malloc(len); - else - #endif - data = (byte*) malloc(len); - if (!data) return false; //allocation failed - WS2812FX::instance->_usedSegmentData += len; - _dataLen = len; - memset(data, 0, len); - return true; - } - void deallocateData(){ - free(data); - data = nullptr; - WS2812FX::instance->_usedSegmentData -= _dataLen; - _dataLen = 0; + struct { + bool _isRGB : 1; + bool _hasW : 1; + bool _isCCT : 1; + bool _manualW : 1; + uint8_t _reserved : 4; + }; + }; + uint16_t _dataLen; + static uint16_t _usedSegmentData; + + // transition data, valid only if transitional==true, holds values during transition + struct Transition { + uint32_t _colorT[NUM_COLORS]; + uint8_t _briT; // temporary brightness + uint8_t _cctT; // temporary CCT + CRGBPalette16 _palT; // temporary palette + uint8_t _modeP; // previous mode/effect + uint32_t _start; + uint16_t _dur; + Transition(uint16_t dur=750) : _briT(255), _cctT(127), _palT(CRGBPalette16(CRGB::Black)), _modeP(FX_MODE_STATIC), _start(millis()), _dur(dur) {} + Transition(uint16_t d, uint8_t b, uint8_t c, const uint32_t *o) : _briT(b), _cctT(c), _palT(CRGBPalette16(CRGB::Black)), _modeP(FX_MODE_STATIC), _start(millis()), _dur(d) { + for (size_t i=0; i= MAX_NUM_SEGMENTS || slot >= NUM_COLORS || dur == 0) return; - if (instance->_brightness == 0) return; //do not need transitions if master bri is off - if (!instance->_segments[segn].getOption(SEG_OPTION_ON)) return; //not if segment is off either - uint8_t tIndex = 0xFF; //none found - uint16_t tProgression = 0; - uint8_t s = segn + (slot << 6); //merge slot and segment into one byte - - for (uint8_t i = 0; i < MAX_NUM_TRANSITIONS; i++) { - uint8_t tSeg = instance->transitions[i].segment; - //see if this segment + color already has a running transition - if (tSeg == s) { - tIndex = i; break; - } - if (tSeg == 0xFF) { //free transition - tIndex = i; tProgression = 0xFFFF; - } - } - - if (tIndex == 0xFF) { //no slot found yet - for (uint8_t i = 0; i < MAX_NUM_TRANSITIONS; i++) { - //find most progressed transition to overwrite - uint16_t prog = instance->transitions[i].progress(); - if (prog > tProgression) { - tIndex = i; tProgression = prog; - } - } - } - - ColorTransition& t = instance->transitions[tIndex]; - if (t.segment == s) //this is an active transition on the same segment+color - { - bool wasTurningOff = (oldBri == 0); - t.briOld = t.currentBri(wasTurningOff, slot); - t.colorOld = t.currentColor(oldCol); - } else { - t.briOld = oldBri; - t.colorOld = oldCol; - uint8_t prevSeg = t.segment & 0x3F; - if (prevSeg < MAX_NUM_SEGMENTS) instance->_segments[prevSeg].setOption(SEG_OPTION_TRANSITIONAL, false); - } - t.transitionDur = dur; - t.transitionStart = millis(); - t.segment = s; - instance->_segments[segn].setOption(SEG_OPTION_TRANSITIONAL, true); - //refresh immediately, required for Solid mode - if (instance->_segment_runtimes[segn].next_time > t.transitionStart + 22) instance->_segment_runtimes[segn].next_time = t.transitionStart; - } - uint16_t progress(bool allowEnd = false) { //transition progression between 0-65535 - uint32_t timeNow = millis(); - if (timeNow - transitionStart > transitionDur) { - if (allowEnd) { - uint8_t segn = segment & 0x3F; - if (segn < MAX_NUM_SEGMENTS) instance->_segments[segn].setOption(SEG_OPTION_TRANSITIONAL, false); - segment = 0xFF; - } - return 0xFFFF; - } - uint32_t elapsed = timeNow - transitionStart; - uint32_t prog = elapsed * 0xFFFF / transitionDur; - return (prog > 0xFFFF) ? 0xFFFF : prog; - } - uint32_t currentColor(uint32_t colorNew) { - return instance->color_blend(colorOld, colorNew, progress(true), true); - } - uint8_t currentBri(bool turningOff = false, uint8_t slot = 0) { - uint8_t segn = segment & 0x3F; - if (segn >= MAX_NUM_SEGMENTS) return 0; - uint8_t briNew = instance->_segments[segn].opacity; - if (slot == 0) { - if (!instance->_segments[segn].getOption(SEG_OPTION_ON) || turningOff) briNew = 0; - } else { //transition slot 1 brightness for CCT transition - briNew = instance->_segments[segn].cct; - } - uint32_t prog = progress() + 1; - return ((briNew * prog) + (briOld * (0x10000 - prog))) >> 16; - } - } color_transition; + Segment(uint16_t sStart=0, uint16_t sStop=30) : + start(sStart), + stop(sStop), + offset(0), + speed(DEFAULT_SPEED), + intensity(DEFAULT_INTENSITY), + palette(0), + mode(DEFAULT_MODE), + options(SELECTED | SEGMENT_ON), + grouping(1), + spacing(0), + opacity(255), + colors{DEFAULT_COLOR,BLACK,BLACK}, + cct(127), + custom1(DEFAULT_C1), + custom2(DEFAULT_C2), + custom3(DEFAULT_C3), + check1(false), + check2(false), + check3(false), + startY(0), + stopY(1), + name(nullptr), + next_time(0), + step(0), + call(0), + aux0(0), + aux1(0), + data(nullptr), + leds(nullptr), + _capabilities(0), + _dataLen(0), + _t(nullptr) + { + refreshLightCapabilities(); + } + + Segment(uint16_t sStartX, uint16_t sStopX, uint16_t sStartY, uint16_t sStopY) : Segment(sStartX, sStopX) { + startY = sStartY; + stopY = sStopY; + } + + Segment(const Segment &orig); // copy constructor + Segment(Segment &&orig) noexcept; // move constructor + + ~Segment() { + #ifdef WLED_DEBUG + Serial.print(F("Destroying segment:")); + if (name) Serial.printf(" %s (%p)", name, name); + if (data) Serial.printf(" %d (%p)", (int)_dataLen, data); + if (leds) Serial.printf(" [%u]", length()*sizeof(CRGB)); + Serial.println(); + #endif + if (!Segment::_globalLeds && leds) free(leds); + if (name) delete[] name; + if (_t) delete _t; + deallocateData(); + } + + Segment& operator= (const Segment &orig); // copy assignment + Segment& operator= (Segment &&orig) noexcept; // move assignment + +#ifdef WLED_DEBUG + size_t getSize() const { return sizeof(Segment) + (data?_dataLen:0) + (name?strlen(name):0) + (_t?sizeof(Transition):0) + (!Segment::_globalLeds && leds?sizeof(CRGB)*length():0); } +#endif + + inline bool getOption(uint8_t n) const { return ((options >> n) & 0x01); } + inline bool isSelected(void) const { return selected; } + inline bool isActive(void) const { return stop > start; } + inline bool is2D(void) const { return !(startY == 0 && stopY == 1); } + inline uint16_t width(void) const { return stop - start; } // segment width in physical pixels (length if 1D) + inline uint16_t height(void) const { return stopY - startY; } // segment height (if 2D) in physical pixels + inline uint16_t length(void) const { return width() * height(); } // segment length (count) in physical pixels + inline uint16_t groupLength(void) const { return grouping + spacing; } + inline uint8_t getLightCapabilities(void) const { return _capabilities; } + + static uint16_t getUsedSegmentData(void) { return _usedSegmentData; } + static void addUsedSegmentData(int len) { _usedSegmentData += len; } + + bool setColor(uint8_t slot, uint32_t c); //returns true if changed + void setCCT(uint16_t k); + void setOpacity(uint8_t o); + void setOption(uint8_t n, bool val); + uint8_t differs(Segment& b) const; + void refreshLightCapabilities(void); + + // runtime data functions + inline uint16_t dataSize(void) const { return _dataLen; } + bool allocateData(size_t len); + void deallocateData(void); + void resetIfRequired(void); + /** + * Flags that before the next effect is calculated, + * the internal segment state should be reset. + * Call resetIfRequired before calling the next effect function. + * Safe to call from interrupts and network requests. + */ + inline void markForReset(void) { reset = true; } // setOption(SEG_OPTION_RESET, true) + //inline void setUpLeds() { if (!leds) leds = (CRGB*)malloc(sizeof(CRGB)*length()); } + void setUpLeds(void); + + // transition functions + void startTransition(uint16_t dur); // transition has to start before actual segment values change + void handleTransition(void); + uint16_t progress(void); //transition progression between 0-65535 + uint8_t currentBri(uint8_t briNew, bool useCct = false); + uint8_t currentMode(uint8_t modeNew); + uint32_t currentColor(uint8_t slot, uint32_t colorNew); + CRGBPalette16 &loadPalette(CRGBPalette16 &tgt, uint8_t pal); + CRGBPalette16 ¤tPalette(CRGBPalette16 &tgt, uint8_t paletteID); + + // 1D strip + uint16_t virtualLength(void) const; + void setPixelColor(int n, uint32_t c); // set relative pixel within segment with color + void setPixelColor(int n, byte r, byte g, byte b, byte w = 0) { setPixelColor(n, RGBW32(r,g,b,w)); } // automatically inline + void setPixelColor(int n, CRGB c) { setPixelColor(n, RGBW32(c.r,c.g,c.b,0)); } // automatically inline + void setPixelColor(float i, uint32_t c, bool aa = true); + void setPixelColor(float i, uint8_t r, uint8_t g, uint8_t b, uint8_t w = 0, bool aa = true) { setPixelColor(i, RGBW32(r,g,b,w), aa); } + void setPixelColor(float i, CRGB c, bool aa = true) { setPixelColor(i, RGBW32(c.r,c.g,c.b,0), aa); } + uint32_t getPixelColor(uint16_t i); + // 1D support functions (some implement 2D as well) + void blur(uint8_t); + void fill(uint32_t c); + void fade_out(uint8_t r); + void fadeToBlackBy(uint8_t fadeBy); + void blendPixelColor(int n, uint32_t color, uint8_t blend); + void blendPixelColor(int n, CRGB c, uint8_t blend) { blendPixelColor(n, RGBW32(c.r,c.g,c.b,0), blend); } + void addPixelColor(int n, uint32_t color); + void addPixelColor(int n, byte r, byte g, byte b, byte w = 0) { addPixelColor(n, RGBW32(r,g,b,w)); } // automatically inline + void addPixelColor(int n, CRGB c) { addPixelColor(n, RGBW32(c.r,c.g,c.b,0)); } // automatically inline + void fadePixelColor(uint16_t n, uint8_t fade); + uint8_t get_random_wheel_index(uint8_t pos); + uint32_t color_from_palette(uint16_t, bool mapping, bool wrap, uint8_t mcol, uint8_t pbri = 255); + uint32_t color_wheel(uint8_t pos); + + // 2D matrix + uint16_t virtualWidth(void) const; + uint16_t virtualHeight(void) const; + #ifndef WLED_DISABLE_2D + uint16_t XY(uint16_t x, uint16_t y); // support function to get relative index within segment (for leds[]) + void setPixelColorXY(int x, int y, uint32_t c); // set relative pixel within segment with color + void setPixelColorXY(int x, int y, byte r, byte g, byte b, byte w = 0) { setPixelColorXY(x, y, RGBW32(r,g,b,w)); } // automatically inline + void setPixelColorXY(int x, int y, CRGB c) { setPixelColorXY(x, y, RGBW32(c.r,c.g,c.b,0)); } // automatically inline + void setPixelColorXY(float x, float y, uint32_t c, bool aa = true); + void setPixelColorXY(float x, float y, byte r, byte g, byte b, byte w = 0, bool aa = true) { setPixelColorXY(x, y, RGBW32(r,g,b,w), aa); } + void setPixelColorXY(float x, float y, CRGB c, bool aa = true) { setPixelColorXY(x, y, RGBW32(c.r,c.g,c.b,0), aa); } + uint32_t getPixelColorXY(uint16_t x, uint16_t y); + // 2D support functions + void blendPixelColorXY(uint16_t x, uint16_t y, uint32_t color, uint8_t blend); + void blendPixelColorXY(uint16_t x, uint16_t y, CRGB c, uint8_t blend) { blendPixelColorXY(x, y, RGBW32(c.r,c.g,c.b,0), blend); } + void addPixelColorXY(int x, int y, uint32_t color); + void addPixelColorXY(int x, int y, byte r, byte g, byte b, byte w = 0) { addPixelColorXY(x, y, RGBW32(r,g,b,w)); } // automatically inline + void addPixelColorXY(int x, int y, CRGB c) { addPixelColorXY(x, y, RGBW32(c.r,c.g,c.b,0)); } + void fadePixelColorXY(uint16_t x, uint16_t y, uint8_t fade); + void box_blur(uint16_t i, bool vertical, fract8 blur_amount); // 1D box blur (with weight) + void blurRow(uint16_t row, fract8 blur_amount); + void blurCol(uint16_t col, fract8 blur_amount); + void moveX(int8_t delta); + void moveY(int8_t delta); + void move(uint8_t dir, uint8_t delta); + void fill_circle(uint16_t cx, uint16_t cy, uint8_t radius, CRGB c); + void drawLine(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint32_t c); + void drawLine(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, CRGB c) { drawLine(x0, y0, x1, y1, RGBW32(c.r,c.g,c.b,0)); } // automatic inline + void drawCharacter(unsigned char chr, int16_t x, int16_t y, uint8_t w, uint8_t h, uint32_t color); + void drawCharacter(unsigned char chr, int16_t x, int16_t y, uint8_t w, uint8_t h, CRGB c) { drawCharacter(chr, x, y, w, h, RGBW32(c.r,c.g,c.b,0)); } // automatic inline + void wu_pixel(uint32_t x, uint32_t y, CRGB c); + void blur1d(fract8 blur_amount); // blur all rows in 1 dimension + void blur2d(fract8 blur_amount) { blur(blur_amount); } + void fill_solid(CRGB c) { fill(RGBW32(c.r,c.g,c.b,0)); } + void nscale8(uint8_t scale); + #else + uint16_t XY(uint16_t x, uint16_t y) { return x; } + void setPixelColorXY(int x, int y, uint32_t c) { setPixelColor(x, c); } + void setPixelColorXY(int x, int y, byte r, byte g, byte b, byte w = 0) { setPixelColor(x, RGBW32(r,g,b,w)); } + void setPixelColorXY(int x, int y, CRGB c) { setPixelColor(x, RGBW32(c.r,c.g,c.b,0)); } + void setPixelColorXY(float x, float y, uint32_t c, bool aa = true) { setPixelColor(x, c, aa); } + void setPixelColorXY(float x, float y, byte r, byte g, byte b, byte w = 0, bool aa = true) { setPixelColor(x, RGBW32(r,g,b,w), aa); } + void setPixelColorXY(float x, float y, CRGB c, bool aa = true) { setPixelColor(x, RGBW32(c.r,c.g,c.b,0), aa); } + uint32_t getPixelColorXY(uint16_t x, uint16_t y) { return getPixelColor(x); } + void blendPixelColorXY(uint16_t x, uint16_t y, uint32_t c, uint8_t blend) { blendPixelColor(x, c, blend); } + void blendPixelColorXY(uint16_t x, uint16_t y, CRGB c, uint8_t blend) { blendPixelColor(x, RGBW32(c.r,c.g,c.b,0), blend); } + void addPixelColorXY(int x, int y, uint32_t color) { addPixelColor(x, color); } + void addPixelColorXY(int x, int y, byte r, byte g, byte b, byte w = 0) { addPixelColor(x, RGBW32(r,g,b,w)); } + void addPixelColorXY(int x, int y, CRGB c) { addPixelColor(x, RGBW32(c.r,c.g,c.b,0)); } + void fadePixelColorXY(uint16_t x, uint16_t y, uint8_t fade) { fadePixelColor(x, fade); } + void box_blur(uint16_t i, bool vertical, fract8 blur_amount) {} + void blurRow(uint16_t row, fract8 blur_amount) {} + void blurCol(uint16_t col, fract8 blur_amount) {} + void moveX(int8_t delta) {} + void moveY(int8_t delta) {} + void move(uint8_t dir, uint8_t delta) {} + void fill_circle(uint16_t cx, uint16_t cy, uint8_t radius, CRGB c) {} + void drawLine(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint32_t c) {} + void drawLine(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, CRGB c) {} + void drawCharacter(unsigned char chr, int16_t x, int16_t y, uint8_t w, uint8_t h, uint32_t color) {} + void drawCharacter(unsigned char chr, int16_t x, int16_t y, uint8_t w, uint8_t h, CRGB color) {} + void wu_pixel(uint32_t x, uint32_t y, CRGB c) {} + #endif +} segment; +//static int i = sizeof(Segment); + +// main "strip" class +class WS2812FX { // 96 bytes + typedef uint16_t (*mode_ptr)(void); // pointer to mode function + typedef void (*show_callback)(void); // pre show callback + typedef struct ModeData { + uint8_t _id; // mode (effect) id + mode_ptr _fcn; // mode (effect) function + const char *_data; // mode (effect) name and its UI control data + ModeData(uint8_t id, uint16_t (*fcn)(void), const char *data) : _id(id), _fcn(fcn), _data(data) {} + } mode_data_t; + + static WS2812FX* instance; + + public: - WS2812FX() { + WS2812FX() : + gammaCorrectBri(false), + gammaCorrectCol(true), + paletteFade(0), + paletteBlend(0), + milliampsPerLed(55), + cctBlending(0), + ablMilliampsMax(ABL_MILLIAMPS_DEFAULT), + currentMilliamps(0), + now(millis()), + timebase(0), + isMatrix(false), +#ifndef WLED_DISABLE_2D + hPanels(1), + vPanels(1), + panelH(8), + panelW(8), + matrixWidth(DEFAULT_LED_COUNT), + matrixHeight(1), + matrix{0,0,0,0}, + panel{{0,0,0,0}}, +#endif + // semi-private (just obscured) used in effect functions through macros + _currentPalette(CRGBPalette16(CRGB::Black)), + _colors_t{0,0,0}, + _virtualSegmentLength(0), + // true private variables + _length(DEFAULT_LED_COUNT), + _brightness(DEFAULT_BRIGHTNESS), + _transitionDur(750), + _targetFps(WLED_FPS), + _frametime(FRAMETIME_FIXED), + _cumulativeFps(2), + _isServicing(false), + _isOffRefreshRequired(false), + _hasWhiteChannel(false), + _triggered(false), + _modeCount(MODE_COUNT), + _callback(nullptr), + customMappingTable(nullptr), + customMappingSize(0), + _lastShow(0), + _segment_index(0), + _mainSegment(0) + { WS2812FX::instance = this; - //assign each member of the _mode[] array to its respective function reference - _mode[FX_MODE_STATIC] = &WS2812FX::mode_static; - _mode[FX_MODE_BLINK] = &WS2812FX::mode_blink; - _mode[FX_MODE_COLOR_WIPE] = &WS2812FX::mode_color_wipe; - _mode[FX_MODE_COLOR_WIPE_RANDOM] = &WS2812FX::mode_color_wipe_random; - _mode[FX_MODE_RANDOM_COLOR] = &WS2812FX::mode_random_color; - _mode[FX_MODE_COLOR_SWEEP] = &WS2812FX::mode_color_sweep; - _mode[FX_MODE_DYNAMIC] = &WS2812FX::mode_dynamic; - _mode[FX_MODE_RAINBOW] = &WS2812FX::mode_rainbow; - _mode[FX_MODE_RAINBOW_CYCLE] = &WS2812FX::mode_rainbow_cycle; - _mode[FX_MODE_SCAN] = &WS2812FX::mode_scan; - _mode[FX_MODE_DUAL_SCAN] = &WS2812FX::mode_dual_scan; - _mode[FX_MODE_FADE] = &WS2812FX::mode_fade; - _mode[FX_MODE_THEATER_CHASE] = &WS2812FX::mode_theater_chase; - _mode[FX_MODE_THEATER_CHASE_RAINBOW] = &WS2812FX::mode_theater_chase_rainbow; - _mode[FX_MODE_SAW] = &WS2812FX::mode_saw; - _mode[FX_MODE_TWINKLE] = &WS2812FX::mode_twinkle; - _mode[FX_MODE_DISSOLVE] = &WS2812FX::mode_dissolve; - _mode[FX_MODE_DISSOLVE_RANDOM] = &WS2812FX::mode_dissolve_random; - _mode[FX_MODE_SPARKLE] = &WS2812FX::mode_sparkle; - _mode[FX_MODE_FLASH_SPARKLE] = &WS2812FX::mode_flash_sparkle; - _mode[FX_MODE_HYPER_SPARKLE] = &WS2812FX::mode_hyper_sparkle; - _mode[FX_MODE_STROBE] = &WS2812FX::mode_strobe; - _mode[FX_MODE_STROBE_RAINBOW] = &WS2812FX::mode_strobe_rainbow; - _mode[FX_MODE_MULTI_STROBE] = &WS2812FX::mode_multi_strobe; - _mode[FX_MODE_BLINK_RAINBOW] = &WS2812FX::mode_blink_rainbow; - _mode[FX_MODE_ANDROID] = &WS2812FX::mode_android; - _mode[FX_MODE_CHASE_COLOR] = &WS2812FX::mode_chase_color; - _mode[FX_MODE_CHASE_RANDOM] = &WS2812FX::mode_chase_random; - _mode[FX_MODE_CHASE_RAINBOW] = &WS2812FX::mode_chase_rainbow; - _mode[FX_MODE_CHASE_FLASH] = &WS2812FX::mode_chase_flash; - _mode[FX_MODE_CHASE_FLASH_RANDOM] = &WS2812FX::mode_chase_flash_random; - _mode[FX_MODE_CHASE_RAINBOW_WHITE] = &WS2812FX::mode_chase_rainbow_white; - _mode[FX_MODE_COLORFUL] = &WS2812FX::mode_colorful; - _mode[FX_MODE_TRAFFIC_LIGHT] = &WS2812FX::mode_traffic_light; - _mode[FX_MODE_COLOR_SWEEP_RANDOM] = &WS2812FX::mode_color_sweep_random; - _mode[FX_MODE_RUNNING_COLOR] = &WS2812FX::mode_running_color; - _mode[FX_MODE_AURORA] = &WS2812FX::mode_aurora; - _mode[FX_MODE_RUNNING_RANDOM] = &WS2812FX::mode_running_random; - _mode[FX_MODE_LARSON_SCANNER] = &WS2812FX::mode_larson_scanner; - _mode[FX_MODE_COMET] = &WS2812FX::mode_comet; - _mode[FX_MODE_FIREWORKS] = &WS2812FX::mode_fireworks; - _mode[FX_MODE_RAIN] = &WS2812FX::mode_rain; - _mode[FX_MODE_TETRIX] = &WS2812FX::mode_tetrix; - _mode[FX_MODE_FIRE_FLICKER] = &WS2812FX::mode_fire_flicker; - _mode[FX_MODE_GRADIENT] = &WS2812FX::mode_gradient; - _mode[FX_MODE_LOADING] = &WS2812FX::mode_loading; - _mode[FX_MODE_POLICE] = &WS2812FX::mode_police; - _mode[FX_MODE_FAIRY] = &WS2812FX::mode_fairy; - _mode[FX_MODE_TWO_DOTS] = &WS2812FX::mode_two_dots; - _mode[FX_MODE_FAIRYTWINKLE] = &WS2812FX::mode_fairytwinkle; - _mode[FX_MODE_RUNNING_DUAL] = &WS2812FX::mode_running_dual; - _mode[FX_MODE_HALLOWEEN] = &WS2812FX::mode_halloween; - _mode[FX_MODE_TRICOLOR_CHASE] = &WS2812FX::mode_tricolor_chase; - _mode[FX_MODE_TRICOLOR_WIPE] = &WS2812FX::mode_tricolor_wipe; - _mode[FX_MODE_TRICOLOR_FADE] = &WS2812FX::mode_tricolor_fade; - _mode[FX_MODE_BREATH] = &WS2812FX::mode_breath; - _mode[FX_MODE_RUNNING_LIGHTS] = &WS2812FX::mode_running_lights; - _mode[FX_MODE_LIGHTNING] = &WS2812FX::mode_lightning; - _mode[FX_MODE_ICU] = &WS2812FX::mode_icu; - _mode[FX_MODE_MULTI_COMET] = &WS2812FX::mode_multi_comet; - _mode[FX_MODE_DUAL_LARSON_SCANNER] = &WS2812FX::mode_dual_larson_scanner; - _mode[FX_MODE_RANDOM_CHASE] = &WS2812FX::mode_random_chase; - _mode[FX_MODE_OSCILLATE] = &WS2812FX::mode_oscillate; - _mode[FX_MODE_FIRE_2012] = &WS2812FX::mode_fire_2012; - _mode[FX_MODE_PRIDE_2015] = &WS2812FX::mode_pride_2015; - _mode[FX_MODE_BPM] = &WS2812FX::mode_bpm; - _mode[FX_MODE_JUGGLE] = &WS2812FX::mode_juggle; - _mode[FX_MODE_PALETTE] = &WS2812FX::mode_palette; - _mode[FX_MODE_COLORWAVES] = &WS2812FX::mode_colorwaves; - _mode[FX_MODE_FILLNOISE8] = &WS2812FX::mode_fillnoise8; - _mode[FX_MODE_NOISE16_1] = &WS2812FX::mode_noise16_1; - _mode[FX_MODE_NOISE16_2] = &WS2812FX::mode_noise16_2; - _mode[FX_MODE_NOISE16_3] = &WS2812FX::mode_noise16_3; - _mode[FX_MODE_NOISE16_4] = &WS2812FX::mode_noise16_4; - _mode[FX_MODE_COLORTWINKLE] = &WS2812FX::mode_colortwinkle; - _mode[FX_MODE_LAKE] = &WS2812FX::mode_lake; - _mode[FX_MODE_METEOR] = &WS2812FX::mode_meteor; - _mode[FX_MODE_METEOR_SMOOTH] = &WS2812FX::mode_meteor_smooth; - _mode[FX_MODE_RAILWAY] = &WS2812FX::mode_railway; - _mode[FX_MODE_RIPPLE] = &WS2812FX::mode_ripple; - _mode[FX_MODE_TWINKLEFOX] = &WS2812FX::mode_twinklefox; - _mode[FX_MODE_TWINKLECAT] = &WS2812FX::mode_twinklecat; - _mode[FX_MODE_HALLOWEEN_EYES] = &WS2812FX::mode_halloween_eyes; - _mode[FX_MODE_STATIC_PATTERN] = &WS2812FX::mode_static_pattern; - _mode[FX_MODE_TRI_STATIC_PATTERN] = &WS2812FX::mode_tri_static_pattern; - _mode[FX_MODE_SPOTS] = &WS2812FX::mode_spots; - _mode[FX_MODE_SPOTS_FADE] = &WS2812FX::mode_spots_fade; - _mode[FX_MODE_GLITTER] = &WS2812FX::mode_glitter; - _mode[FX_MODE_CANDLE] = &WS2812FX::mode_candle; - _mode[FX_MODE_STARBURST] = &WS2812FX::mode_starburst; - _mode[FX_MODE_EXPLODING_FIREWORKS] = &WS2812FX::mode_exploding_fireworks; - _mode[FX_MODE_BOUNCINGBALLS] = &WS2812FX::mode_bouncing_balls; - _mode[FX_MODE_SINELON] = &WS2812FX::mode_sinelon; - _mode[FX_MODE_SINELON_DUAL] = &WS2812FX::mode_sinelon_dual; - _mode[FX_MODE_SINELON_RAINBOW] = &WS2812FX::mode_sinelon_rainbow; - _mode[FX_MODE_POPCORN] = &WS2812FX::mode_popcorn; - _mode[FX_MODE_DRIP] = &WS2812FX::mode_drip; - _mode[FX_MODE_PLASMA] = &WS2812FX::mode_plasma; - _mode[FX_MODE_PERCENT] = &WS2812FX::mode_percent; - _mode[FX_MODE_RIPPLE_RAINBOW] = &WS2812FX::mode_ripple_rainbow; - _mode[FX_MODE_HEARTBEAT] = &WS2812FX::mode_heartbeat; - _mode[FX_MODE_PACIFICA] = &WS2812FX::mode_pacifica; - _mode[FX_MODE_CANDLE_MULTI] = &WS2812FX::mode_candle_multi; - _mode[FX_MODE_SOLID_GLITTER] = &WS2812FX::mode_solid_glitter; - _mode[FX_MODE_SUNRISE] = &WS2812FX::mode_sunrise; - _mode[FX_MODE_PHASED] = &WS2812FX::mode_phased; - _mode[FX_MODE_TWINKLEUP] = &WS2812FX::mode_twinkleup; - _mode[FX_MODE_NOISEPAL] = &WS2812FX::mode_noisepal; - _mode[FX_MODE_SINEWAVE] = &WS2812FX::mode_sinewave; - _mode[FX_MODE_PHASEDNOISE] = &WS2812FX::mode_phased_noise; - _mode[FX_MODE_FLOW] = &WS2812FX::mode_flow; - _mode[FX_MODE_CHUNCHUN] = &WS2812FX::mode_chunchun; - _mode[FX_MODE_DANCING_SHADOWS] = &WS2812FX::mode_dancing_shadows; - _mode[FX_MODE_WASHING_MACHINE] = &WS2812FX::mode_washing_machine; - _mode[FX_MODE_CANDY_CANE] = &WS2812FX::mode_candy_cane; - _mode[FX_MODE_BLENDS] = &WS2812FX::mode_blends; - _mode[FX_MODE_TV_SIMULATOR] = &WS2812FX::mode_tv_simulator; - _mode[FX_MODE_DYNAMIC_SMOOTH] = &WS2812FX::mode_dynamic_smooth; - - _brightness = DEFAULT_BRIGHTNESS; - currentPalette = CRGBPalette16(CRGB::Black); - targetPalette = CloudColors_p; - ablMilliampsMax = ABL_MILLIAMPS_DEFAULT; - currentMilliamps = 0; - timebase = 0; - resetSegments(); + _mode.reserve(_modeCount); // allocate memory to prevent initial fragmentation (does not increase size()) + _modeData.reserve(_modeCount); // allocate memory to prevent initial fragmentation (does not increase size()) + if (_mode.capacity() <= 1 || _modeData.capacity() <= 1) _modeCount = 1; // memory allocation failed only show Solid + else setupEffectData(); } + ~WS2812FX() { + if (customMappingTable) delete[] customMappingTable; + _mode.clear(); + _modeData.clear(); + _segments.clear(); + customPalettes.clear(); + if (useLedsArray && Segment::_globalLeds) free(Segment::_globalLeds); + } + + static WS2812FX* getInstance(void) { return instance; } + void +#ifdef WLED_DEBUG + printSize(), +#endif finalizeInit(), service(void), - blur(uint8_t), - fill(uint32_t), - fade_out(uint8_t r), setMode(uint8_t segid, uint8_t m), setColor(uint8_t slot, uint8_t r, uint8_t g, uint8_t b, uint8_t w = 0), setColor(uint8_t slot, uint32_t c), setCCT(uint16_t k), setBrightness(uint8_t b, bool direct = false), setRange(uint16_t i, uint16_t i2, uint32_t col), - setShowCallback(show_callback cb), - setTransition(uint16_t t), setTransitionMode(bool t), - calcGammaTable(float), - trigger(void), - setSegment(uint8_t n, uint16_t start, uint16_t stop, uint8_t grouping = 0, uint8_t spacing = 0, uint16_t offset = UINT16_MAX), + purgeSegments(bool force = false), + setSegment(uint8_t n, uint16_t start, uint16_t stop, uint8_t grouping = 1, uint8_t spacing = 0, uint16_t offset = UINT16_MAX, uint16_t startY=0, uint16_t stopY=1), setMainSegmentId(uint8_t n), restartRuntime(), resetSegments(), makeAutoSegments(bool forceReset = false), fixInvalidSegments(), - setPixelColor(uint16_t n, uint8_t r, uint8_t g, uint8_t b, uint8_t w = 0), + setPixelColor(int n, uint32_t c), show(void), setTargetFps(uint8_t fps), deserializeMap(uint8_t n=0); - inline void setPixelColor(uint16_t n, uint32_t c) {setPixelColor(n, byte(c>>16), byte(c>>8), byte(c), byte(c>>24));} + void fill(uint32_t c) { for (int i = 0; i < _length; i++) setPixelColor(i, c); } // fill whole strip with color (inline) + void addEffect(uint8_t id, mode_ptr mode_fn, const char *mode_name); // add effect to the list; defined in FX.cpp + void setupEffectData(void); // add default effects to the list; defined in FX.cpp + + // outsmart the compiler :) by correctly overloading + inline void setPixelColor(int n, uint8_t r, uint8_t g, uint8_t b, uint8_t w = 0) { setPixelColor(n, RGBW32(r,g,b,w)); } + inline void setPixelColor(int n, CRGB c) { setPixelColor(n, c.red, c.green, c.blue); } + inline void trigger(void) { _triggered = true; } // Forces the next frame to be computed on all active segments. + inline void setShowCallback(show_callback cb) { _callback = cb; } + inline void setTransition(uint16_t t) { _transitionDur = t; } + inline void appendSegment(const Segment &seg = Segment()) { _segments.push_back(seg); } bool - gammaCorrectBri = false, - gammaCorrectCol = true, + gammaCorrectBri, + gammaCorrectCol, checkSegmentAlignment(void), hasRGBWBus(void), hasCCTBus(void), // return true if the strip is being sent pixel updates - isUpdating(void); + isUpdating(void), + useLedsArray = false; + + inline bool isServicing(void) { return _isServicing; } + inline bool hasWhiteChannel(void) {return _hasWhiteChannel;} + inline bool isOffRefreshRequired(void) {return _isOffRefreshRequired;} uint8_t - paletteFade = 0, - paletteBlend = 0, - milliampsPerLed = 55, - autoWhiteMode = RGBW_MODE_DUAL, - cctBlending = 0, - getBrightness(void), - getModeCount(void), - getPaletteCount(void), - getMaxSegments(void), + paletteFade, + paletteBlend, + milliampsPerLed, + cctBlending, getActiveSegmentsNum(void), getFirstSelectedSegId(void), - getMainSegmentId(void), getLastActiveSegmentId(void), - getTargetFps(void), - setPixelSegment(uint8_t n), - gamma8(uint8_t), - gamma8_cal(uint8_t, float), - get_random_wheel_index(uint8_t); - - inline uint8_t sin_gap(uint16_t in) { - if (in & 0x100) return 0; - return sin8(in + 192); // correct phase shift of sine so that it starts and stops at 0 - } + setPixelSegment(uint8_t n); - int8_t - tristate_square8(uint8_t x, uint8_t pulsewidth, uint8_t attdec); + inline uint8_t getBrightness(void) { return _brightness; } + inline uint8_t getMaxSegments(void) { return MAX_NUM_SEGMENTS; } // returns maximum number of supported segments (fixed value) + inline uint8_t getSegmentsNum(void) { return _segments.size(); } // returns currently present segments + inline uint8_t getCurrSegmentId(void) { return _segment_index; } + inline uint8_t getMainSegmentId(void) { return _mainSegment; } + inline uint8_t getPaletteCount() { return 13 + GRADIENT_PALETTE_COUNT; } + inline uint8_t getTargetFps() { return _targetFps; } + inline uint8_t getModeCount() { return _modeCount; } uint16_t ablMilliampsMax, currentMilliamps, - triwave16(uint16_t), - getLengthTotal(void), getLengthPhysical(void), getFps(); + inline uint16_t getFrameTime(void) { return _frametime; } + inline uint16_t getMinShowDelay(void) { return MIN_SHOW_DELAY; } + inline uint16_t getLengthTotal(void) { return _length; } + inline uint16_t getTransition(void) { return _transitionDur; } + uint32_t now, timebase, - color_wheel(uint8_t), - color_from_palette(uint16_t, bool mapping, bool wrap, uint8_t mcol, uint8_t pbri = 255), - color_blend(uint32_t,uint32_t,uint16_t,bool b16=false), currentColor(uint32_t colorNew, uint8_t tNr), - gamma32(uint32_t), - getLastShow(void), getPixelColor(uint16_t); - WS2812FX::Segment - &getSegment(uint8_t n), - &getFirstSelectedSeg(void), - &getMainSegment(void); + inline uint32_t getLastShow(void) { return _lastShow; } + inline uint32_t segColor(uint8_t i) { return _colors_t[i]; } - WS2812FX::Segment* - getSegments(void); + const char * + getModeData(uint8_t id = 0) { return (id && id<_modeCount) ? _modeData[id] : PSTR("Solid"); } - // builtin modes - uint16_t - mode_static(void), - mode_blink(void), - mode_blink_rainbow(void), - mode_strobe(void), - mode_strobe_rainbow(void), - mode_color_wipe(void), - mode_color_sweep(void), - mode_color_wipe_random(void), - mode_color_sweep_random(void), - mode_random_color(void), - mode_dynamic(void), - mode_breath(void), - mode_fade(void), - mode_scan(void), - mode_dual_scan(void), - mode_theater_chase(void), - mode_theater_chase_rainbow(void), - mode_rainbow(void), - mode_rainbow_cycle(void), - mode_running_lights(void), - mode_saw(void), - mode_twinkle(void), - mode_dissolve(void), - mode_dissolve_random(void), - mode_sparkle(void), - mode_flash_sparkle(void), - mode_hyper_sparkle(void), - mode_multi_strobe(void), - mode_android(void), - mode_chase_color(void), - mode_chase_random(void), - mode_chase_rainbow(void), - mode_chase_flash(void), - mode_chase_flash_random(void), - mode_chase_rainbow_white(void), - mode_colorful(void), - mode_traffic_light(void), - mode_running_color(void), - mode_aurora(void), - mode_running_random(void), - mode_larson_scanner(void), - mode_comet(void), - mode_fireworks(void), - mode_rain(void), - mode_tetrix(void), - mode_halloween(void), - mode_fire_flicker(void), - mode_gradient(void), - mode_loading(void), - mode_police(void), - mode_fairy(void), - mode_two_dots(void), - mode_fairytwinkle(void), - mode_running_dual(void), - mode_bicolor_chase(void), - mode_tricolor_chase(void), - mode_tricolor_wipe(void), - mode_tricolor_fade(void), - mode_lightning(void), - mode_icu(void), - mode_multi_comet(void), - mode_dual_larson_scanner(void), - mode_random_chase(void), - mode_oscillate(void), - mode_fire_2012(void), - mode_pride_2015(void), - mode_bpm(void), - mode_juggle(void), - mode_palette(void), - mode_colorwaves(void), - mode_fillnoise8(void), - mode_noise16_1(void), - mode_noise16_2(void), - mode_noise16_3(void), - mode_noise16_4(void), - mode_colortwinkle(void), - mode_lake(void), - mode_meteor(void), - mode_meteor_smooth(void), - mode_railway(void), - mode_ripple(void), - mode_twinklefox(void), - mode_twinklecat(void), - mode_halloween_eyes(void), - mode_static_pattern(void), - mode_tri_static_pattern(void), - mode_spots(void), - mode_spots_fade(void), - mode_glitter(void), - mode_candle(void), - mode_starburst(void), - mode_exploding_fireworks(void), - mode_bouncing_balls(void), - mode_sinelon(void), - mode_sinelon_dual(void), - mode_sinelon_rainbow(void), - mode_popcorn(void), - mode_drip(void), - mode_plasma(void), - mode_percent(void), - mode_ripple_rainbow(void), - mode_heartbeat(void), - mode_pacifica(void), - mode_candle_multi(void), - mode_solid_glitter(void), - mode_sunrise(void), - mode_phased(void), - mode_twinkleup(void), - mode_noisepal(void), - mode_sinewave(void), - mode_phased_noise(void), - mode_flow(void), - mode_chunchun(void), - mode_dancing_shadows(void), - mode_washing_machine(void), - mode_candy_cane(void), - mode_blends(void), - mode_tv_simulator(void), - mode_dynamic_smooth(void); + const char ** + getModeDataSrc(void) { return &(_modeData[0]); } // vectors use arrays for underlying data - private: - uint32_t crgb_to_col(CRGB fastled); - CRGB col_to_crgb(uint32_t); - CRGBPalette16 currentPalette; - CRGBPalette16 targetPalette; - - uint16_t _length, _virtualSegmentLength; - uint16_t _rand16seed; - uint8_t _brightness; - uint16_t _usedSegmentData = 0; - uint16_t _transitionDur = 750; - - uint8_t _targetFps = 42; - uint16_t _frametime = (1000/42); - uint16_t _cumulativeFps = 2; + Segment& getSegment(uint8_t id); + inline Segment& getFirstSelectedSeg(void) { return _segments[getFirstSelectedSegId()]; } + inline Segment& getMainSegment(void) { return _segments[getMainSegmentId()]; } + inline Segment* getSegments(void) { return &(_segments[0]); } + // 2D support (panels) bool - _isOffRefreshRequired = false, //periodic refresh is required for the strip to remain off. - _hasWhiteChannel = false, - _triggered; + isMatrix; - mode_ptr _mode[MODE_COUNT]; // SRAM footprint: 4 bytes per element - - show_callback _callback = nullptr; +#ifndef WLED_DISABLE_2D + #define WLED_MAX_PANELS 64 + uint8_t + hPanels, + vPanels; - // mode helper functions uint16_t - blink(uint32_t, uint32_t, bool strobe, bool), - candle(bool), - color_wipe(bool, bool), - dynamic(bool), - scan(bool), - running_base(bool,bool), - larson_scanner(bool), - sinelon_base(bool,bool), - dissolve(uint32_t), - chase(uint32_t, uint32_t, uint32_t, bool), - gradient_base(bool), - ripple_base(bool), - police_base(uint32_t, uint32_t), - running(uint32_t, uint32_t, bool theatre=false), - tricolor_chase(uint32_t, uint32_t), - twinklefox_base(bool), - spots_base(uint16_t), - phased_base(uint8_t); - - CRGB twinklefox_one_twinkle(uint32_t ms, uint8_t salt, bool cat); - CRGB pacifica_one_layer(uint16_t i, CRGBPalette16& p, uint16_t cistart, uint16_t wavescale, uint8_t bri, uint16_t ioff); + panelH, + panelW, + matrixWidth, + matrixHeight; + + typedef struct panel_bitfield_t { + bool bottomStart : 1; // starts at bottom? + bool rightStart : 1; // starts on right? + bool vertical : 1; // is vertical? + bool serpentine : 1; // is serpentine? + } Panel; + Panel + matrix, + panel[WLED_MAX_PANELS]; +#endif void - blendPixelColor(uint16_t n, uint32_t color, uint8_t blend), - startTransition(uint8_t oldBri, uint32_t oldCol, uint16_t dur, uint8_t segn, uint8_t slot), - estimateCurrentAndLimitBri(void), - load_gradient_palette(uint8_t), - handle_palette(void); - - uint16_t* customMappingTable = nullptr; - uint16_t customMappingSize = 0; - - uint32_t _lastPaletteChange = 0; - uint32_t _lastShow = 0; + setUpMatrix(), + setPixelColorXY(int x, int y, uint32_t c); - uint32_t _colors_t[3]; - uint8_t _bri_t; - bool _no_rgb = false; - - uint8_t _segment_index = 0; - uint8_t _segment_index_palette_last = 99; - uint8_t _mainSegment; + // outsmart the compiler :) by correctly overloading + inline void setPixelColorXY(int x, int y, byte r, byte g, byte b, byte w = 0) { setPixelColorXY(x, y, RGBW32(r,g,b,w)); } // automatically inline + inline void setPixelColorXY(int x, int y, CRGB c) { setPixelColorXY(x, y, RGBW32(c.r,c.g,c.b,0)); } + + uint32_t + getPixelColorXY(uint16_t, uint16_t); - segment _segments[MAX_NUM_SEGMENTS] = { // SRAM footprint: 24 bytes per element - // start, stop, offset, speed, intensity, palette, mode, options, grouping, spacing, opacity (unused), color[], capabilities - {0, 7, 0, DEFAULT_SPEED, 128, 0, DEFAULT_MODE, NO_OPTIONS, 1, 0, 255, {DEFAULT_COLOR}, 0} + // end 2D support + + void loadCustomPalettes(void); // loads custom palettes from JSON + CRGBPalette16 _currentPalette; // palette used for current effect (includes transition) + std::vector customPalettes; // TODO: move custom palettes out of WS2812FX class + + // using public variables to reduce code size increase due to inline function getSegment() (with bounds checking) + // and color transitions + uint32_t _colors_t[3]; // color used for effect (includes transition) + uint16_t _virtualSegmentLength; + + std::vector _segments; + friend class Segment; + + private: + uint16_t _length; + uint8_t _brightness; + uint16_t _transitionDur; + + uint8_t _targetFps; + uint16_t _frametime; + uint16_t _cumulativeFps; + + // will require only 1 byte + struct { + bool _isServicing : 1; + bool _isOffRefreshRequired : 1; //periodic refresh is required for the strip to remain off. + bool _hasWhiteChannel : 1; + bool _triggered : 1; }; - segment_runtime _segment_runtimes[MAX_NUM_SEGMENTS]; // SRAM footprint: 28 bytes per element - friend class Segment_runtime; - ColorTransition transitions[MAX_NUM_TRANSITIONS]; //12 bytes per element - friend class ColorTransition; + uint8_t _modeCount; + std::vector _mode; // SRAM footprint: 4 bytes per element + std::vector _modeData; // mode (effect) name and its slider control data array - uint16_t - transitionProgress(uint8_t tNr); - - public: - inline bool hasWhiteChannel(void) {return _hasWhiteChannel;} - inline bool isOffRefreshRequired(void) {return _isOffRefreshRequired;} + show_callback _callback; + + uint16_t* customMappingTable; + uint16_t customMappingSize; + + uint32_t _lastShow; + + uint8_t _segment_index; + uint8_t _mainSegment; + + void + estimateCurrentAndLimitBri(void); }; -//10 names per line -const char JSON_mode_names[] PROGMEM = R"=====([ -"Solid","Blink","Breathe","Wipe","Wipe Random","Random Colors","Sweep","Dynamic","Colorloop","Rainbow", -"Scan","Scan Dual","Fade","Theater","Theater Rainbow","Running","Saw","Twinkle","Dissolve","Dissolve Rnd", -"Sparkle","Sparkle Dark","Sparkle+","Strobe","Strobe Rainbow","Strobe Mega","Blink Rainbow","Android","Chase","Chase Random", -"Chase Rainbow","Chase Flash","Chase Flash Rnd","Rainbow Runner","Colorful","Traffic Light","Sweep Random","Chase 2","Aurora","Stream", -"Scanner","Lighthouse","Fireworks","Rain","Tetrix","Fire Flicker","Gradient","Loading","Police","Fairy", -"Two Dots","Fairytwinkle","Running Dual","Halloween","Chase 3","Tri Wipe","Tri Fade","Lightning","ICU","Multi Comet", -"Scanner Dual","Stream 2","Oscillate","Pride 2015","Juggle","Palette","Fire 2012","Colorwaves","Bpm","Fill Noise", -"Noise 1","Noise 2","Noise 3","Noise 4","Colortwinkles","Lake","Meteor","Meteor Smooth","Railway","Ripple", -"Twinklefox","Twinklecat","Halloween Eyes","Solid Pattern","Solid Pattern Tri","Spots","Spots Fade","Glitter","Candle","Fireworks Starburst", -"Fireworks 1D","Bouncing Balls","Sinelon","Sinelon Dual","Sinelon Rainbow","Popcorn","Drip","Plasma","Percent","Ripple Rainbow", -"Heartbeat","Pacifica","Candle Multi", "Solid Glitter","Sunrise","Phased","Twinkleup","Noise Pal", "Sine","Phased Noise", -"Flow","Chunchun","Dancing Shadows","Washing Machine","Candy Cane","Blends","TV Simulator","Dynamic Smooth" -])====="; - - -const char JSON_palette_names[] PROGMEM = R"=====([ -"Default","* Random Cycle","* Color 1","* Colors 1&2","* Color Gradient","* Colors Only","Party","Cloud","Lava","Ocean", -"Forest","Rainbow","Rainbow Bands","Sunset","Rivendell","Breeze","Red & Blue","Yellowout","Analogous","Splash", -"Pastel","Sunset 2","Beech","Vintage","Departure","Landscape","Beach","Sherbet","Hult","Hult 64", -"Drywet","Jul","Grintage","Rewhi","Tertiary","Fire","Icefire","Cyane","Light Pink","Autumn", -"Magenta","Magred","Yelmag","Yelblu","Orange & Teal","Tiamat","April Night","Orangery","C9","Sakura", -"Aurora","Atlantica","C9 2","C9 New","Temperature","Aurora 2","Retro Clown","Candy","Toxy Reaf","Fairy Reaf", -"Semi Blue","Pink Candy","Red Reaf","Aqua Flash","Yelblu Hot","Lite Light","Red Flash","Blink Red","Red Shift","Red Tide", -"Candy2" -])====="; +extern const char JSON_mode_names[]; +extern const char JSON_palette_names[]; #endif diff --git a/wled00/FX_2Dfcn.cpp b/wled00/FX_2Dfcn.cpp new file mode 100644 index 0000000000..bbbae7598e --- /dev/null +++ b/wled00/FX_2Dfcn.cpp @@ -0,0 +1,513 @@ +/* + FX_2Dfcn.cpp contains all 2D utility functions + + LICENSE + The MIT License (MIT) + Copyright (c) 2022 Blaz Kristan (https://blaz.at/home) + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. + + Parts of the code adapted from WLED Sound Reactive +*/ +#include "wled.h" +#include "FX.h" +#include "palettes.h" + +// setUpMatrix() - constructs ledmap array from matrix of panels with WxH pixels +// this converts physical (possibly irregular) LED arrangement into well defined +// array of logical pixels: fist entry corresponds to left-topmost logical pixel +// followed by horizontal pixels, when matrixWidth logical pixels are added they +// are followed by next row (down) of matrixWidth pixels (and so forth) +// note: matrix may be comprised of multiple panels each with different orientation +// but ledmap takes care of that. ledmap is constructed upon initialization +// so matrix should disable regular ledmap processing +void WS2812FX::setUpMatrix() { +#ifndef WLED_DISABLE_2D + // erase old ledmap, just in case. + if (customMappingTable != nullptr) delete[] customMappingTable; + customMappingTable = nullptr; + customMappingSize = 0; + + if (isMatrix) { + matrixWidth = hPanels * panelW; + matrixHeight = vPanels * panelH; + + // safety check + if (matrixWidth * matrixHeight > MAX_LEDS) { + matrixWidth = _length; + matrixHeight = 1; + isMatrix = false; + return; + } + + customMappingSize = matrixWidth * matrixHeight; + customMappingTable = new uint16_t[customMappingSize]; + + if (customMappingTable != nullptr) { + uint16_t startL; // index in custom mapping array (logical strip) + uint16_t startP; // position of 1st pixel of panel on (virtual) strip + uint16_t x, y, offset; + uint8_t h = matrix.vertical ? vPanels : hPanels; + uint8_t v = matrix.vertical ? hPanels : vPanels; + + for (uint8_t j=0, p=0; j= _length) return; + if (index < customMappingSize) index = customMappingTable[index]; + busses.setPixelColor(index, col); +} + +// returns RGBW values of pixel +uint32_t WS2812FX::getPixelColorXY(uint16_t x, uint16_t y) { +#ifndef WLED_DISABLE_2D + uint16_t index = (y * matrixWidth + x); +#else + uint16_t index = x; +#endif + if (index >= _length) return 0; + if (index < customMappingSize) index = customMappingTable[index]; + return busses.getPixelColor(index); +} + +/////////////////////////////////////////////////////////// +// Segment:: routines +/////////////////////////////////////////////////////////// + +#ifndef WLED_DISABLE_2D + +// XY(x,y) - gets pixel index within current segment (often used to reference leds[] array element) +uint16_t IRAM_ATTR Segment::XY(uint16_t x, uint16_t y) { + uint16_t width = virtualWidth(); // segment width in logical pixels + uint16_t height = virtualHeight(); // segment height in logical pixels + return (x%width) + (y%height) * width; +} + +void IRAM_ATTR Segment::setPixelColorXY(int x, int y, uint32_t col) +{ + if (!strip.isMatrix) return; // not a matrix set-up + + if (leds) leds[XY(x,y)] = col; + + uint8_t _bri_t = currentBri(on ? opacity : 0); + if (_bri_t < 255) { + byte r = scale8(R(col), _bri_t); + byte g = scale8(G(col), _bri_t); + byte b = scale8(B(col), _bri_t); + byte w = scale8(W(col), _bri_t); + col = RGBW32(r, g, b, w); + } + + if (reverse ) x = virtualWidth() - x - 1; + if (reverse_y) y = virtualHeight() - y - 1; + if (transpose) { uint16_t t = x; x = y; y = t; } // swap X & Y if segment transposed + + x *= groupLength(); // expand to physical pixels + y *= groupLength(); // expand to physical pixels + if (x >= width() || y >= height()) return; // if pixel would fall out of segment just exit + + for (int j = 0; j < grouping; j++) { // groupping vertically + for (int g = 0; g < grouping; g++) { // groupping horizontally + uint16_t xX = (x+g), yY = (y+j); + if (xX >= width() || yY >= height()) continue; // we have reached one dimension's end + + strip.setPixelColorXY(start + xX, startY + yY, col); + + if (mirror) { //set the corresponding horizontally mirrored pixel + if (transpose) strip.setPixelColorXY(start + xX, startY + height() - yY - 1, col); + else strip.setPixelColorXY(start + width() - xX - 1, startY + yY, col); + } + if (mirror_y) { //set the corresponding vertically mirrored pixel + if (transpose) strip.setPixelColorXY(start + width() - xX - 1, startY + yY, col); + else strip.setPixelColorXY(start + xX, startY + height() - yY - 1, col); + } + if (mirror_y && mirror) { //set the corresponding vertically AND horizontally mirrored pixel + strip.setPixelColorXY(width() - xX - 1, height() - yY - 1, col); + } + } + } +} + +// anti-aliased version of setPixelColorXY() +void Segment::setPixelColorXY(float x, float y, uint32_t col, bool aa) +{ + if (!strip.isMatrix) return; // not a matrix set-up + if (x<0.0f || x>1.0f || y<0.0f || y>1.0f) return; // not normalized + + const uint16_t cols = virtualWidth(); + const uint16_t rows = virtualHeight(); + + float fX = x * (cols-1); + float fY = y * (rows-1); + if (aa) { + uint16_t xL = roundf(fX-0.49f); + uint16_t xR = roundf(fX+0.49f); + uint16_t yT = roundf(fY-0.49f); + uint16_t yB = roundf(fY+0.49f); + float dL = fX - xL; + float dR = xR - fX; + float dT = fY - yT; + float dB = yB - fY; + uint32_t cXLYT = getPixelColorXY(xL, yT); + uint32_t cXRYT = getPixelColorXY(xR, yT); + uint32_t cXLYB = getPixelColorXY(xL, yB); + uint32_t cXRYB = getPixelColorXY(xR, yB); + + if (xL!=xR && yT!=yB) { + setPixelColorXY(xL, yT, color_blend(col, cXLYT, uint8_t(sqrtf(dL*dT)*255.0f))); // blend TL pixel + setPixelColorXY(xR, yT, color_blend(col, cXRYT, uint8_t(sqrtf(dR*dT)*255.0f))); // blend TR pixel + setPixelColorXY(xL, yB, color_blend(col, cXLYB, uint8_t(sqrtf(dL*dB)*255.0f))); // blend BL pixel + setPixelColorXY(xR, yB, color_blend(col, cXRYB, uint8_t(sqrtf(dR*dB)*255.0f))); // blend BR pixel + } else if (xR!=xL && yT==yB) { + setPixelColorXY(xR, yT, color_blend(col, cXLYT, uint8_t(dL*255.0f))); // blend L pixel + setPixelColorXY(xR, yT, color_blend(col, cXRYT, uint8_t(dR*255.0f))); // blend R pixel + } else if (xR==xL && yT!=yB) { + setPixelColorXY(xR, yT, color_blend(col, cXLYT, uint8_t(dT*255.0f))); // blend T pixel + setPixelColorXY(xL, yB, color_blend(col, cXLYB, uint8_t(dB*255.0f))); // blend B pixel + } else { + setPixelColorXY(xL, yT, col); // exact match (x & y land on a pixel) + } + } else { + setPixelColorXY(uint16_t(roundf(fX)), uint16_t(roundf(fY)), col); + } +} + +// returns RGBW values of pixel +uint32_t Segment::getPixelColorXY(uint16_t x, uint16_t y) { + int i = XY(x,y); + if (leds) return RGBW32(leds[i].r, leds[i].g, leds[i].b, 0); + if (reverse ) x = virtualWidth() - x - 1; + if (reverse_y) y = virtualHeight() - y - 1; + if (transpose) { uint16_t t = x; x = y; y = t; } // swap X & Y if segment transposed + x *= groupLength(); // expand to physical pixels + y *= groupLength(); // expand to physical pixels + if (x >= width() || y >= height()) return 0; + return strip.getPixelColorXY(start + x, startY + y); +} + +// Blends the specified color with the existing pixel color. +void Segment::blendPixelColorXY(uint16_t x, uint16_t y, uint32_t color, uint8_t blend) { + setPixelColorXY(x, y, color_blend(getPixelColorXY(x,y), color, blend)); +} + +// Adds the specified color with the existing pixel color perserving color balance. +void Segment::addPixelColorXY(int x, int y, uint32_t color) { + setPixelColorXY(x, y, color_add(getPixelColorXY(x,y), color)); +} + +void Segment::fadePixelColorXY(uint16_t x, uint16_t y, uint8_t fade) { + CRGB pix = CRGB(getPixelColorXY(x,y)).nscale8_video(fade); + setPixelColor(x, y, pix); +} + +// blurRow: perform a blur on a row of a rectangular matrix +void Segment::blurRow(uint16_t row, fract8 blur_amount) { + const uint16_t cols = virtualWidth(); + const uint16_t rows = virtualHeight(); + + if (row >= rows) return; + // blur one row + uint8_t keep = 255 - blur_amount; + uint8_t seep = blur_amount >> 1; + CRGB carryover = CRGB::Black; + for (uint16_t x = 0; x < cols; x++) { + CRGB cur = getPixelColorXY(x, row); + CRGB part = cur; + part.nscale8(seep); + cur.nscale8(keep); + cur += carryover; + if (x) { + CRGB prev = CRGB(getPixelColorXY(x-1, row)) + part; + setPixelColorXY(x-1, row, prev); + } + setPixelColorXY(x, row, cur); + carryover = part; + } +} + +// blurCol: perform a blur on a column of a rectangular matrix +void Segment::blurCol(uint16_t col, fract8 blur_amount) { + const uint16_t cols = virtualWidth(); + const uint16_t rows = virtualHeight(); + + if (col >= cols) return; + // blur one column + uint8_t keep = 255 - blur_amount; + uint8_t seep = blur_amount >> 1; + CRGB carryover = CRGB::Black; + for (uint16_t i = 0; i < rows; i++) { + CRGB cur = getPixelColorXY(col, i); + CRGB part = cur; + part.nscale8(seep); + cur.nscale8(keep); + cur += carryover; + if (i) { + CRGB prev = CRGB(getPixelColorXY(col, i-1)) + part; + setPixelColorXY(col, i-1, prev); + } + setPixelColorXY(col, i, cur); + carryover = part; + } +} + +// 1D Box blur (with added weight - blur_amount: [0=no blur, 255=max blur]) +void Segment::box_blur(uint16_t i, bool vertical, fract8 blur_amount) { + const uint16_t cols = virtualWidth(); + const uint16_t rows = virtualHeight(); + const uint16_t dim1 = vertical ? rows : cols; + const uint16_t dim2 = vertical ? cols : rows; + if (i >= dim2) return; + const float seep = blur_amount/255.f; + const float keep = 3.f - 2.f*seep; + // 1D box blur + CRGB tmp[dim1]; + for (uint16_t j = 0; j < dim1; j++) { + uint16_t x = vertical ? i : j; + uint16_t y = vertical ? j : i; + uint16_t xp = vertical ? x : x-1; + uint16_t yp = vertical ? y-1 : y; + uint16_t xn = vertical ? x : x+1; + uint16_t yn = vertical ? y+1 : y; + CRGB curr = getPixelColorXY(x,y); + CRGB prev = (xp<0 || yp<0) ? CRGB::Black : getPixelColorXY(xp,yp); + CRGB next = ((vertical && yn>=dim1) || (!vertical && xn>=dim1)) ? CRGB::Black : getPixelColorXY(xn,yn); + uint16_t r, g, b; + r = (curr.r*keep + (prev.r + next.r)*seep) / 3; + g = (curr.g*keep + (prev.g + next.g)*seep) / 3; + b = (curr.b*keep + (prev.b + next.b)*seep) / 3; + tmp[j] = CRGB(r,g,b); + } + for (uint16_t j = 0; j < dim1; j++) { + uint16_t x = vertical ? i : j; + uint16_t y = vertical ? j : i; + setPixelColorXY(x, y, tmp[j]); + } +} + +// blur1d: one-dimensional blur filter. Spreads light to 2 line neighbors. +// blur2d: two-dimensional blur filter. Spreads light to 8 XY neighbors. +// +// 0 = no spread at all +// 64 = moderate spreading +// 172 = maximum smooth, even spreading +// +// 173..255 = wider spreading, but increasing flicker +// +// Total light is NOT entirely conserved, so many repeated +// calls to 'blur' will also result in the light fading, +// eventually all the way to black; this is by design so that +// it can be used to (slowly) clear the LEDs to black. + +void Segment::blur1d(fract8 blur_amount) { + const uint16_t rows = virtualHeight(); + for (uint16_t y = 0; y < rows; y++) blurRow(y, blur_amount); +} + +void Segment::moveX(int8_t delta) { + const uint16_t cols = virtualWidth(); + const uint16_t rows = virtualHeight(); + if (!delta) return; + if (delta > 0) { + for (uint8_t y = 0; y < rows; y++) for (uint8_t x = 0; x < cols-1; x++) { + if (x + delta >= cols) break; + setPixelColorXY(x, y, getPixelColorXY((x + delta)%cols, y)); + } + } else { + for (uint8_t y = 0; y < rows; y++) for (int16_t x = cols-1; x >= 0; x--) { + if (x + delta < 0) break; + setPixelColorXY(x, y, getPixelColorXY(x + delta, y)); + } + } +} + +void Segment::moveY(int8_t delta) { + const uint16_t cols = virtualWidth(); + const uint16_t rows = virtualHeight(); + if (!delta) return; + if (delta > 0) { + for (uint8_t x = 0; x < cols; x++) for (uint8_t y = 0; y < rows-1; y++) { + if (y + delta >= rows) break; + setPixelColorXY(x, y, getPixelColorXY(x, (y + delta))); + } + } else { + for (uint8_t x = 0; x < cols; x++) for (int16_t y = rows-1; y >= 0; y--) { + if (y + delta < 0) break; + setPixelColorXY(x, y, getPixelColorXY(x, y + delta)); + } + } +} + +// move() - move all pixels in desired direction delta number of pixels +// @param dir direction: 0=left, 1=left-up, 2=up, 3=right-up, 4=right, 5=right-down, 6=down, 7=left-down +// @param delta number of pixels to move +void Segment::move(uint8_t dir, uint8_t delta) { + if (delta==0) return; + switch (dir) { + case 0: moveX( delta); break; + case 1: moveX( delta); moveY( delta); break; + case 2: moveY( delta); break; + case 3: moveX(-delta); moveY( delta); break; + case 4: moveX(-delta); break; + case 5: moveX(-delta); moveY(-delta); break; + case 6: moveY(-delta); break; + case 7: moveX( delta); moveY(-delta); break; + } +} + +// by stepko, taken from https://editor.soulmatelights.com/gallery/573-blobs +void Segment::fill_circle(uint16_t cx, uint16_t cy, uint8_t radius, CRGB col) { + const uint16_t cols = virtualWidth(); + const uint16_t rows = virtualHeight(); + for (int16_t y = -radius; y <= radius; y++) { + for (int16_t x = -radius; x <= radius; x++) { + if (x * x + y * y <= radius * radius && + int16_t(cx)+x>=0 && int16_t(cy)+y>=0 && + int16_t(cx)+x= cols || x1 >= cols || y0 >= rows || y1 >= rows) return; + const int16_t dx = abs(x1-x0), sx = x0dy ? dx : -dy)/2, e2; + for (;;) { + setPixelColorXY(x0,y0,c); + if (x0==x1 && y0==y1) break; + e2 = err; + if (e2 >-dx) { err -= dy; x0 += sx; } + if (e2 < dy) { err += dx; y0 += sy; } + } +} + +#include "console_font_5x8.h" +#include "console_font_5x12.h" +#include "console_font_6x8.h" +#include "console_font_7x9.h" + +// draws a raster font character on canvas +// only supports 5x8=40, 5x12=60, 6x8=48 and 7x9=63 fonts ATM +void Segment::drawCharacter(unsigned char chr, int16_t x, int16_t y, uint8_t w, uint8_t h, uint32_t color) { + const uint16_t cols = virtualWidth(); + const uint16_t rows = virtualHeight(); + const int font = w*h; + + //if (w<5 || w>6 || h!=8) return; + for (int i = 0; i= rows) break; // drawing off-screen + uint8_t bits = 0; + switch (font) { + case 40: bits = pgm_read_byte_near(&console_font_5x8[(chr * h) + i]); break; // 5x8 font + case 48: bits = pgm_read_byte_near(&console_font_6x8[(chr * h) + i]); break; // 6x8 font + case 63: bits = pgm_read_byte_near(&console_font_7x9[(chr * h) + i]); break; // 7x9 font + case 60: bits = pgm_read_byte_near(&console_font_5x12[(chr * h) + i]); break; // 5x12 font + default: return; + } + for (int j = 0; j= 0 || x0 < cols) && ((bits>>(j+(8-w))) & 0x01)) { // bit set & drawing on-screen + setPixelColorXY(x0, y0, color); + } + } + } +} + +#define WU_WEIGHT(a,b) ((uint8_t) (((a)*(b)+(a)+(b))>>8)) +void Segment::wu_pixel(uint32_t x, uint32_t y, CRGB c) { //awesome wu_pixel procedure by reddit u/sutaburosu + // extract the fractional parts and derive their inverses + uint8_t xx = x & 0xff, yy = y & 0xff, ix = 255 - xx, iy = 255 - yy; + // calculate the intensities for each affected pixel + uint8_t wu[4] = {WU_WEIGHT(ix, iy), WU_WEIGHT(xx, iy), + WU_WEIGHT(ix, yy), WU_WEIGHT(xx, yy)}; + // multiply the intensities by the colour, and saturating-add them to the pixels + for (int i = 0; i < 4; i++) { + CRGB led = getPixelColorXY((x >> 8) + (i & 1), (y >> 8) + ((i >> 1) & 1)); + led.r = qadd8(led.r, c.r * wu[i] >> 8); + led.g = qadd8(led.g, c.g * wu[i] >> 8); + led.b = qadd8(led.b, c.b * wu[i] >> 8); + setPixelColorXY(int((x >> 8) + (i & 1)), int((y >> 8) + ((i >> 1) & 1)), led); + } +} +#undef WU_WEIGHT + +#endif // WLED_DISABLE_2D diff --git a/wled00/FX_fcn.cpp b/wled00/FX_fcn.cpp index a79def6152..13fe77dc82 100644 --- a/wled00/FX_fcn.cpp +++ b/wled00/FX_fcn.cpp @@ -69,15 +69,751 @@ #error "Max segments must be at least max number of busses!" #endif + +/////////////////////////////////////////////////////////////////////////////// +// Segment class implementation +/////////////////////////////////////////////////////////////////////////////// +uint16_t Segment::_usedSegmentData = 0U; // amount of RAM all segments use for their data[] +CRGB *Segment::_globalLeds = nullptr; + +// copy constructor +Segment::Segment(const Segment &orig) { + DEBUG_PRINTLN(F("-- Copy segment constructor --")); + memcpy(this, &orig, sizeof(Segment)); + name = nullptr; + data = nullptr; + _dataLen = 0; + _t = nullptr; + if (leds && !Segment::_globalLeds) leds = nullptr; + if (orig.name) { name = new char[strlen(orig.name)+1]; if (name) strcpy(name, orig.name); } + if (orig.data) { if (allocateData(orig._dataLen)) memcpy(data, orig.data, orig._dataLen); } + if (orig._t) { _t = new Transition(orig._t->_dur, orig._t->_briT, orig._t->_cctT, orig._t->_colorT); } + if (orig.leds && !Segment::_globalLeds) { leds = (CRGB*)malloc(sizeof(CRGB)*length()); if (leds) memcpy(leds, orig.leds, sizeof(CRGB)*length()); } +} + +// move constructor +Segment::Segment(Segment &&orig) noexcept { + DEBUG_PRINTLN(F("-- Move segment constructor --")); + memcpy(this, &orig, sizeof(Segment)); + orig.name = nullptr; + orig.data = nullptr; + orig._dataLen = 0; + orig._t = nullptr; + orig.leds = nullptr; +} + +// copy assignment +Segment& Segment::operator= (const Segment &orig) { + DEBUG_PRINTLN(F("-- Copying segment --")); + if (this != &orig) { + // clean destination + if (name) delete[] name; + if (_t) delete _t; + if (leds && !Segment::_globalLeds) free(leds); + deallocateData(); + // copy source + memcpy(this, &orig, sizeof(Segment)); + // erase pointers to allocated data + name = nullptr; + data = nullptr; + _dataLen = 0; + _t = nullptr; + if (!Segment::_globalLeds) leds = nullptr; + // copy source data + if (orig.name) { name = new char[strlen(orig.name)+1]; if (name) strcpy(name, orig.name); } + if (orig.data) { if (allocateData(orig._dataLen)) memcpy(data, orig.data, orig._dataLen); } + if (orig._t) { _t = new Transition(orig._t->_dur, orig._t->_briT, orig._t->_cctT, orig._t->_colorT); } + if (orig.leds && !Segment::_globalLeds) { leds = (CRGB*)malloc(sizeof(CRGB)*length()); if (leds) memcpy(leds, orig.leds, sizeof(CRGB)*length()); } + } + return *this; +} + +// move assignment +Segment& Segment::operator= (Segment &&orig) noexcept { + DEBUG_PRINTLN(F("-- Moving segment --")); + if (this != &orig) { + if (name) delete[] name; // free old name + deallocateData(); // free old runtime data + if (_t) delete _t; + if (leds && !Segment::_globalLeds) free(leds); + memcpy(this, &orig, sizeof(Segment)); + orig.name = nullptr; + orig.data = nullptr; + orig._dataLen = 0; + orig._t = nullptr; + orig.leds = nullptr; + } + return *this; +} + +bool Segment::allocateData(size_t len) { + if (data && _dataLen == len) return true; //already allocated + deallocateData(); + if (Segment::getUsedSegmentData() + len > MAX_SEGMENT_DATA) return false; //not enough memory + // if possible use SPI RAM on ESP32 + #if defined(ARDUINO_ARCH_ESP32) && defined(WLED_USE_PSRAM) + if (psramFound()) + data = (byte*) ps_malloc(len); + else + #endif + data = (byte*) malloc(len); + if (!data) return false; //allocation failed + Segment::addUsedSegmentData(len); + _dataLen = len; + memset(data, 0, len); + return true; +} + +void Segment::deallocateData() { + if (!data) return; + free(data); + data = nullptr; + Segment::addUsedSegmentData(-_dataLen); + _dataLen = 0; +} + +/** + * If reset of this segment was requested, clears runtime + * settings of this segment. + * Must not be called while an effect mode function is running + * because it could access the data buffer and this method + * may free that data buffer. + */ +void Segment::resetIfRequired() { + if (reset) { + if (leds && !Segment::_globalLeds) { free(leds); leds = nullptr; } + //if (_t) { delete _t; _t = nullptr; transitional = false; } + next_time = 0; step = 0; call = 0; aux0 = 0; aux1 = 0; + reset = false; // setOption(SEG_OPTION_RESET, false); + } +} + +void Segment::setUpLeds() { + // deallocation happens in resetIfRequired() as it is called when segment changes or in destructor + if (Segment::_globalLeds) + #ifndef WLED_DISABLE_2D + leds = &Segment::_globalLeds[start + startY*strip.matrixWidth]; // TODO: remove this hack + #else + leds = &Segment::_globalLeds[start]; + #endif + else if (!leds) + leds = (CRGB*)malloc(sizeof(CRGB)*length()); +} + +void Segment::startTransition(uint16_t dur) { + if (transitional || _t) return; // already in transition no need to store anything + + // starting a transition has to occur before change so we get current values 1st + uint8_t _briT = currentBri(on ? opacity : 0); + uint8_t _cctT = currentBri(cct, true); + CRGBPalette16 _palT; loadPalette(_palT, palette); + uint8_t _modeP = mode; + uint32_t _colorT[NUM_COLORS]; + for (size_t i=0; i_briT = _briT; + _t->_cctT = _cctT; + _t->_palT = _palT; + _t->_modeP = _modeP; + for (size_t i=0; i_colorT[i] = _colorT[i]; + transitional = true; // setOption(SEG_OPTION_TRANSITIONAL, true); +} + +// transition progression between 0-65535 +uint16_t Segment::progress() { + if (!transitional || !_t) return 0xFFFFU; + uint32_t timeNow = millis(); + if (timeNow - _t->_start > _t->_dur || _t->_dur == 0) return 0xFFFFU; + return (timeNow - _t->_start) * 0xFFFFU / _t->_dur; +} + +uint8_t Segment::currentBri(uint8_t briNew, bool useCct) { + if (transitional && _t) { + uint32_t prog = progress() + 1; + if (useCct) return ((briNew * prog) + _t->_cctT * (0x10000 - prog)) >> 16; + else return ((briNew * prog) + _t->_briT * (0x10000 - prog)) >> 16; + } else { + return briNew; + } +} + +uint8_t Segment::currentMode(uint8_t newMode) { + if (transitional && _t) { + return _t->_modeP; + } else { + return newMode; + } +} + +uint32_t Segment::currentColor(uint8_t slot, uint32_t colorNew) { + return transitional && _t ? color_blend(_t->_colorT[slot], colorNew, progress(), true) : colorNew; +} + +CRGBPalette16 &Segment::loadPalette(CRGBPalette16 &targetPalette, uint8_t pal) { + static unsigned long _lastPaletteChange = 0; // perhaps it should be per segment + byte tcp[72]; + if (pal < 245 && pal > GRADIENT_PALETTE_COUNT+13) pal = 0; + if (pal > 245 && (strip.customPalettes.size() == 0 || 255U-pal > strip.customPalettes.size()-1)) pal = 0; + //default palette. Differs depending on effect + if (pal == 0) switch (mode) { + case FX_MODE_FIRE_2012 : pal = 35; break; // heat palette + case FX_MODE_COLORWAVES : pal = 26; break; // landscape 33 + case FX_MODE_FILLNOISE8 : pal = 9; break; // ocean colors + case FX_MODE_NOISE16_1 : pal = 20; break; // Drywet + case FX_MODE_NOISE16_2 : pal = 43; break; // Blue cyan yellow + case FX_MODE_NOISE16_3 : pal = 35; break; // heat palette + case FX_MODE_NOISE16_4 : pal = 26; break; // landscape 33 + case FX_MODE_GLITTER : pal = 11; break; // rainbow colors + case FX_MODE_SUNRISE : pal = 35; break; // heat palette + case FX_MODE_FLOW : pal = 6; break; // party + } + switch (pal) { + case 0: //default palette. Exceptions for specific effects above + targetPalette = PartyColors_p; break; + case 1: {//periodically replace palette with a random one. Doesn't work with multiple FastLED segments + if (millis() - _lastPaletteChange > 1000 + ((uint32_t)(255-intensity))*100) { + targetPalette = CRGBPalette16( + CHSV(random8(), 255, random8(128, 255)), + CHSV(random8(), 255, random8(128, 255)), + CHSV(random8(), 192, random8(128, 255)), + CHSV(random8(), 255, random8(128, 255))); + _lastPaletteChange = millis(); + } break;} + case 2: {//primary color only + CRGB prim = strip.gammaCorrectCol ? gamma32(colors[0]) : colors[0]; + targetPalette = CRGBPalette16(prim); break;} + case 3: {//primary + secondary + CRGB prim = strip.gammaCorrectCol ? gamma32(colors[0]) : colors[0]; + CRGB sec = strip.gammaCorrectCol ? gamma32(colors[1]) : colors[1]; + targetPalette = CRGBPalette16(prim,prim,sec,sec); break;} + case 4: {//primary + secondary + tertiary + CRGB prim = strip.gammaCorrectCol ? gamma32(colors[0]) : colors[0]; + CRGB sec = strip.gammaCorrectCol ? gamma32(colors[1]) : colors[1]; + CRGB ter = strip.gammaCorrectCol ? gamma32(colors[2]) : colors[2]; + targetPalette = CRGBPalette16(ter,sec,prim); break;} + case 5: {//primary + secondary (+tert if not off), more distinct + CRGB prim = strip.gammaCorrectCol ? gamma32(colors[0]) : colors[0]; + CRGB sec = strip.gammaCorrectCol ? gamma32(colors[1]) : colors[1]; + if (colors[2]) { + CRGB ter = strip.gammaCorrectCol ? gamma32(colors[2]) : colors[2]; + targetPalette = CRGBPalette16(prim,prim,prim,prim,prim,sec,sec,sec,sec,sec,ter,ter,ter,ter,ter,prim); + } else { + targetPalette = CRGBPalette16(prim,prim,prim,prim,prim,prim,prim,prim,sec,sec,sec,sec,sec,sec,sec,sec); + } + break;} + case 6: //Party colors + targetPalette = PartyColors_p; break; + case 7: //Cloud colors + targetPalette = CloudColors_p; break; + case 8: //Lava colors + targetPalette = LavaColors_p; break; + case 9: //Ocean colors + targetPalette = OceanColors_p; break; + case 10: //Forest colors + targetPalette = ForestColors_p; break; + case 11: //Rainbow colors + targetPalette = RainbowColors_p; break; + case 12: //Rainbow stripe colors + targetPalette = RainbowStripeColors_p; break; + default: //progmem palettes + if (pal>245) { + targetPalette = strip.customPalettes[255-pal]; // we checked bounds above + } else { + memcpy_P(tcp, (byte*)pgm_read_dword(&(gGradientPalettes[pal-13])), 72); + targetPalette.loadDynamicGradientPalette(tcp); + } + break; + } + return targetPalette; +} + +CRGBPalette16 &Segment::currentPalette(CRGBPalette16 &targetPalette, uint8_t pal) { + loadPalette(targetPalette, pal); + if (transitional && _t && progress() < 0xFFFFU) { + // blend palettes + uint8_t blends = map(_t->_dur, 0, 0xFFFF, 48, 6); // do not blend palettes too quickly (0-65.5s) + nblendPaletteTowardPalette(_t->_palT, targetPalette, blends); + targetPalette = _t->_palT; // copy transitioning/temporary palette + } + return targetPalette; +} + +void Segment::handleTransition() { + if (!transitional) return; + unsigned long maxWait = millis() + 20; + if (mode == FX_MODE_STATIC && next_time > maxWait) next_time = maxWait; + if (progress() == 0xFFFFU) { + if (_t) { + if (_t->_modeP != mode) markForReset(); + delete _t; + _t = nullptr; + } + transitional = false; // finish transitioning segment + } +} + +bool Segment::setColor(uint8_t slot, uint32_t c) { //returns true if changed + if (slot >= NUM_COLORS || c == colors[slot]) return false; + if (fadeTransition) startTransition(strip.getTransition()); // start transition prior to change + colors[slot] = c; + return true; +} + +void Segment::setCCT(uint16_t k) { + if (k > 255) { //kelvin value, convert to 0-255 + if (k < 1900) k = 1900; + if (k > 10091) k = 10091; + k = (k - 1900) >> 5; + } + if (cct == k) return; + if (fadeTransition) startTransition(strip.getTransition()); // start transition prior to change + cct = k; +} + +void Segment::setOpacity(uint8_t o) { + if (opacity == o) return; + if (fadeTransition) startTransition(strip.getTransition()); // start transition prior to change + opacity = o; +} + +void Segment::setOption(uint8_t n, bool val) { + bool prevOn = on; + if (fadeTransition && n == SEG_OPTION_ON && val != prevOn) startTransition(strip.getTransition()); // start transition prior to change + if (val) options |= 0x01 << n; + else options &= ~(0x01 << n); +} + +// 2D matrix +uint16_t Segment::virtualWidth() const { + uint16_t groupLen = groupLength(); + uint16_t vWidth = ((transpose ? height() : width()) + groupLen - 1) / groupLen; + if (mirror) vWidth = (vWidth + 1) /2; // divide by 2 if mirror, leave at least a single LED + return vWidth; +} + +uint16_t Segment::virtualHeight() const { + uint16_t groupLen = groupLength(); + uint16_t vHeight = ((transpose ? width() : height()) + groupLen - 1) / groupLen; + if (mirror_y) vHeight = (vHeight + 1) /2; // divide by 2 if mirror, leave at least a single LED + return vHeight; +} + +// 1D strip +uint16_t Segment::virtualLength() const { +#ifndef WLED_DISABLE_2D + if (is2D()) { + uint16_t vW = virtualWidth(); + uint16_t vH = virtualHeight(); + uint32_t vLen = vW * vH; // use all pixels from segment + switch (map1D2D) { + case M12_VerticalBar: + vLen = vW; // segment width since it is used in getPixelColor() + break; + case M12_Block: + case M12_Circle: + vLen = max(vW,vH); // get the longest dimension + break; + } + return vLen; + } +#endif + uint16_t groupLen = groupLength(); + uint16_t vLength = (length() + groupLen - 1) / groupLen; + if (mirror) vLength = (vLength + 1) /2; // divide by 2 if mirror, leave at least a single LED + return vLength; +} + +void IRAM_ATTR Segment::setPixelColor(int i, uint32_t col) +{ +#ifndef WLED_DISABLE_2D + if (is2D()) { // if this does not work use strip.isMatrix + uint16_t vH = virtualHeight(); // segment height in logical pixels + uint16_t vW = virtualWidth(); + switch (map1D2D) { + case M12_Pixels: + // use all available pixels as a long strip + setPixelColorXY(i % vW, i / vW, col); + break; + case M12_VerticalBar: + // expand 1D effect vertically + for (int y = 0; y < vH; y++) setPixelColorXY(i, y, col); + break; + case M12_Circle: + // expand in circular fashion from center + for (float degrees = 0.0f; degrees <= 90.0f; degrees += 89.99f / (sqrtf((float)max(vH,vW))*i+1)) { // this may prove too many iterations on larger matrices + // may want to try float version as well (with or without antialiasing) + int x = roundf(sin_t(degrees*DEG_TO_RAD) * i); + int y = roundf(cos_t(degrees*DEG_TO_RAD) * i); + setPixelColorXY(x, y, col); + } + break; + case M12_Block: + for (int x = 0; x <= i; x++) setPixelColorXY(x, i, col); + for (int y = 0; y < i; y++) setPixelColorXY(i, y, col); + break; + } + return; + } +#endif + + if (leds) leds[i] = col; + + uint16_t len = length(); + uint8_t _bri_t = currentBri(on ? opacity : 0); + if (_bri_t < 255) { + byte r = scale8(R(col), _bri_t); + byte g = scale8(G(col), _bri_t); + byte b = scale8(B(col), _bri_t); + byte w = scale8(W(col), _bri_t); + col = RGBW32(r, g, b, w); + } + + // expand pixel (taking into account start, grouping, spacing [and offset]) + i = i * groupLength(); + if (reverse) { // is segment reversed? + if (mirror) { // is segment mirrored? + i = (len - 1) / 2 - i; //only need to index half the pixels + } else { + i = (len - 1) - i; + } + } + i += start; // starting pixel in a group + + // set all the pixels in the group + for (int j = 0; j < grouping; j++) { + uint16_t indexSet = i + ((reverse) ? -j : j); + if (indexSet >= start && indexSet < stop) { + if (mirror) { //set the corresponding mirrored pixel + uint16_t indexMir = stop - indexSet + start - 1; + indexMir += offset; // offset/phase + if (indexMir >= stop) indexMir -= len; // wrap + strip.setPixelColor(indexMir, col); + } + indexSet += offset; // offset/phase + if (indexSet >= stop) indexSet -= len; // wrap + strip.setPixelColor(indexSet, col); + } + } +} + +// anti-aliased normalized version of setPixelColor() +void Segment::setPixelColor(float i, uint32_t col, bool aa) +{ + if (i<0.0f || i>1.0f) return; // not normalized + + float fC = i * (virtualLength()-1); + if (aa) { + uint16_t iL = roundf(fC-0.49f); + uint16_t iR = roundf(fC+0.49f); + float dL = fC - iL; + float dR = iR - fC; + uint32_t cIL = getPixelColor(iL); + uint32_t cIR = getPixelColor(iR); + if (iR!=iL) { + // blend L pixel + cIL = color_blend(col, cIL, uint8_t(dL*255.0f)); + setPixelColor(iL, cIL); + // blend R pixel + cIR = color_blend(col, cIR, uint8_t(dR*255.0f)); + setPixelColor(iR, cIR); + } else { + // exact match (x & y land on a pixel) + setPixelColor(iL, col); + } + } else { + setPixelColor(uint16_t(roundf(fC)), col); + } +} + +uint32_t Segment::getPixelColor(uint16_t i) +{ +#ifndef WLED_DISABLE_2D + if (is2D()) { // if this does not work use strip.isMatrix + uint16_t vH = virtualHeight(); // segment height in logical pixels + uint16_t vW = virtualWidth(); + switch (map1D2D) { + case M12_Pixels: + return getPixelColorXY(i % vW, i / vW); + break; + case M12_VerticalBar: + return getPixelColorXY(i, 0); + break; + case M12_Circle: + case M12_Block: + // use longest dimension + return vW>vH ? getPixelColorXY(i, 0) : getPixelColorXY(0, i); + break; + } + return 0; + } +#endif + + if (leds) return RGBW32(leds[i].r, leds[i].g, leds[i].b, 0); + + if (reverse) i = virtualLength() - i - 1; + i *= groupLength(); + i += start; + /* offset/phase */ + i += offset; + if (i >= stop) i -= length(); + return strip.getPixelColor(i); +} + +uint8_t Segment::differs(Segment& b) const { + uint8_t d = 0; + if (start != b.start) d |= SEG_DIFFERS_BOUNDS; + if (stop != b.stop) d |= SEG_DIFFERS_BOUNDS; + if (offset != b.offset) d |= SEG_DIFFERS_GSO; + if (grouping != b.grouping) d |= SEG_DIFFERS_GSO; + if (spacing != b.spacing) d |= SEG_DIFFERS_GSO; + if (opacity != b.opacity) d |= SEG_DIFFERS_BRI; + if (mode != b.mode) d |= SEG_DIFFERS_FX; + if (speed != b.speed) d |= SEG_DIFFERS_FX; + if (intensity != b.intensity) d |= SEG_DIFFERS_FX; + if (palette != b.palette) d |= SEG_DIFFERS_FX; + if (custom1 != b.custom1) d |= SEG_DIFFERS_FX; + if (custom2 != b.custom2) d |= SEG_DIFFERS_FX; + if (custom3 != b.custom3) d |= SEG_DIFFERS_FX; + if (startY != b.startY) d |= SEG_DIFFERS_BOUNDS; + if (stopY != b.stopY) d |= SEG_DIFFERS_BOUNDS; + + //bit pattern: msb first: [transposed mirrorY reverseY] transitional (tbd) paused needspixelstate mirrored on reverse selected + if ((options & 0b1111111100101110) != (b.options & 0b1111111100101110)) d |= SEG_DIFFERS_OPT; + if ((options & 0x01) != (b.options & 0x01)) d |= SEG_DIFFERS_SEL; + + for (uint8_t i = 0; i < NUM_COLORS; i++) if (colors[i] != b.colors[i]) d |= SEG_DIFFERS_COL; + + return d; +} + +void Segment::refreshLightCapabilities() { + uint8_t capabilities = 0x01; + + for (uint8_t b = 0; b < busses.getNumBusses(); b++) { + Bus *bus = busses.getBus(b); + if (bus == nullptr || bus->getLength()==0) break; + if (!bus->isOk()) continue; + if (bus->getStart() >= stop) continue; + if (bus->getStart() + bus->getLength() <= start) continue; + + uint8_t type = bus->getType(); + if (type == TYPE_ANALOG_1CH || (!cctFromRgb && type == TYPE_ANALOG_2CH)) capabilities &= 0xFE; // does not support RGB + if (bus->isRgbw()) capabilities |= 0x02; // segment supports white channel + if (!cctFromRgb) { + switch (type) { + case TYPE_ANALOG_5CH: + case TYPE_ANALOG_2CH: + capabilities |= 0x04; //segment supports white CCT + } + } + if (correctWB && type != TYPE_ANALOG_1CH) capabilities |= 0x04; //white balance correction (uses CCT slider) + uint8_t aWM = Bus::getAutoWhiteMode()<255 ? Bus::getAutoWhiteMode() : bus->getAWMode(); + bool whiteSlider = (aWM == RGBW_MODE_DUAL || aWM == RGBW_MODE_MANUAL_ONLY); // white slider allowed + if (bus->isRgbw() && (whiteSlider || !(capabilities & 0x01))) capabilities |= 0x08; // allow white channel adjustments (AWM allows or is not RGB) + } + _capabilities = capabilities; +} + +/* + * Fills segment with color + */ +void Segment::fill(uint32_t c) { + const uint16_t cols = is2D() ? virtualWidth() : virtualLength(); + const uint16_t rows = virtualHeight(); // will be 1 for 1D + for(uint16_t y = 0; y < rows; y++) for (uint16_t x = 0; x < cols; x++) { + if (is2D()) setPixelColorXY(x, y, c); + else setPixelColor(x, c); + } +} + +// Blends the specified color with the existing pixel color. +void Segment::blendPixelColor(int n, uint32_t color, uint8_t blend) { + setPixelColor(n, color_blend(getPixelColor(n), color, blend)); +} + +// Adds the specified color with the existing pixel color perserving color balance. +void Segment::addPixelColor(int n, uint32_t color) { + setPixelColor(n, color_add(getPixelColor(n), color)); +} + +void Segment::fadePixelColor(uint16_t n, uint8_t fade) { + CRGB pix = CRGB(getPixelColor(n)).nscale8_video(fade); + setPixelColor(n, pix); +} + +/* + * fade out function, higher rate = quicker fade + */ +void Segment::fade_out(uint8_t rate) { + const uint16_t cols = is2D() ? virtualWidth() : virtualLength(); + const uint16_t rows = virtualHeight(); // will be 1 for 1D + + rate = (255-rate) >> 1; + float mappedRate = float(rate) +1.1; + + uint32_t color = colors[1]; // SEGCOLOR(1); // target color + int w2 = W(color); + int r2 = R(color); + int g2 = G(color); + int b2 = B(color); + + for (uint16_t y = 0; y < rows; y++) for (uint16_t x = 0; x < cols; x++) { + color = is2D() ? getPixelColorXY(x, y) : getPixelColor(x); + int w1 = W(color); + int r1 = R(color); + int g1 = G(color); + int b1 = B(color); + + int wdelta = (w2 - w1) / mappedRate; + int rdelta = (r2 - r1) / mappedRate; + int gdelta = (g2 - g1) / mappedRate; + int bdelta = (b2 - b1) / mappedRate; + + // if fade isn't complete, make sure delta is at least 1 (fixes rounding issues) + wdelta += (w2 == w1) ? 0 : (w2 > w1) ? 1 : -1; + rdelta += (r2 == r1) ? 0 : (r2 > r1) ? 1 : -1; + gdelta += (g2 == g1) ? 0 : (g2 > g1) ? 1 : -1; + bdelta += (b2 == b1) ? 0 : (b2 > b1) ? 1 : -1; + + if (is2D()) setPixelColorXY(x, y, r1 + rdelta, g1 + gdelta, b1 + bdelta, w1 + wdelta); + else setPixelColor(x, r1 + rdelta, g1 + gdelta, b1 + bdelta, w1 + wdelta); + } +} + +// fades all pixels to black using nscale8() +void Segment::fadeToBlackBy(uint8_t fadeBy) { + const uint16_t cols = is2D() ? virtualWidth() : virtualLength(); + const uint16_t rows = virtualHeight(); // will be 1 for 1D + + for (uint16_t y = 0; y < rows; y++) for (uint16_t x = 0; x < cols; x++) { + if (is2D()) setPixelColorXY(x, y, CRGB(getPixelColorXY(x,y)).nscale8(255-fadeBy)); + else setPixelColor(x, CRGB(getPixelColor(x)).nscale8(255-fadeBy)); + } +} + +/* + * blurs segment content, source: FastLED colorutils.cpp + */ +void Segment::blur(uint8_t blur_amount) +{ +#ifndef WLED_DISABLE_2D + if (is2D()) { + // compatibility with 2D + const uint16_t cols = virtualWidth(); + const uint16_t rows = virtualHeight(); + for (uint16_t i = 0; i < rows; i++) blurRow(i, blur_amount); // blur all rows + for (uint16_t k = 0; k < cols; k++) blurCol(k, blur_amount); // blur all columns + return; + } +#endif + uint8_t keep = 255 - blur_amount; + uint8_t seep = blur_amount >> 1; + CRGB carryover = CRGB::Black; + for(uint16_t i = 0; i < virtualLength(); i++) + { + CRGB cur = CRGB(getPixelColor(i)); + CRGB part = cur; + part.nscale8(seep); + cur.nscale8(keep); + cur += carryover; + if(i > 0) { + uint32_t c = getPixelColor(i-1); + uint8_t r = R(c); + uint8_t g = G(c); + uint8_t b = B(c); + setPixelColor(i-1, qadd8(r, part.red), qadd8(g, part.green), qadd8(b, part.blue)); + } + setPixelColor(i,cur.red, cur.green, cur.blue); + carryover = part; + } +} + +/* + * Put a value 0 to 255 in to get a color value. + * The colours are a transition r -> g -> b -> back to r + * Inspired by the Adafruit examples. + */ +uint32_t Segment::color_wheel(uint8_t pos) { // TODO + if (palette) return color_from_palette(pos, false, true, 0); + pos = 255 - pos; + if(pos < 85) { + return ((uint32_t)(255 - pos * 3) << 16) | ((uint32_t)(0) << 8) | (pos * 3); + } else if(pos < 170) { + pos -= 85; + return ((uint32_t)(0) << 16) | ((uint32_t)(pos * 3) << 8) | (255 - pos * 3); + } else { + pos -= 170; + return ((uint32_t)(pos * 3) << 16) | ((uint32_t)(255 - pos * 3) << 8) | (0); + } +} + +/* + * Returns a new, random wheel index with a minimum distance of 42 from pos. + */ +uint8_t Segment::get_random_wheel_index(uint8_t pos) { + uint8_t r = 0, x = 0, y = 0, d = 0; + + while(d < 42) { + r = random8(); + x = abs(pos - r); + y = 255 - x; + d = MIN(x, y); + } + return r; +} + +/* + * Gets a single color from the currently selected palette. + * @param i Palette Index (if mapping is true, the full palette will be _virtualSegmentLength long, if false, 255). Will wrap around automatically. + * @param mapping if true, LED position in segment is considered for color + * @param wrap FastLED palettes will usally wrap back to the start smoothly. Set false to get a hard edge + * @param mcol If the default palette 0 is selected, return the standard color 0, 1 or 2 instead. If >2, Party palette is used instead + * @param pbri Value to scale the brightness of the returned color by. Default is 255. (no scaling) + * @returns Single color from palette + */ +uint32_t Segment::color_from_palette(uint16_t i, bool mapping, bool wrap, uint8_t mcol, uint8_t pbri) +{ + // default palette or no RGB support on segment + if ((palette == 0 && mcol < NUM_COLORS) || !(_capabilities & 0x01)) { + uint32_t color = (transitional && _t) ? _t->_colorT[mcol] : colors[mcol]; + color = strip.gammaCorrectCol ? gamma32(color) : color; + if (pbri == 255) return color; + return RGBW32(scale8_video(R(color),pbri), scale8_video(G(color),pbri), scale8_video(B(color),pbri), scale8_video(W(color),pbri)); + } + + uint8_t paletteIndex = i; + if (mapping && virtualLength() > 1) paletteIndex = (i*255)/(virtualLength() -1); + if (!wrap) paletteIndex = scale8(paletteIndex, 240); //cut off blend at palette "end" + CRGB fastled_col; + CRGBPalette16 curPal; + if (transitional && _t) curPal = _t->_palT; + else loadPalette(curPal, palette); + fastled_col = ColorFromPalette(curPal, paletteIndex, pbri, (strip.paletteBlend == 3)? NOBLEND:LINEARBLEND); // NOTE: paletteBlend should be global + + return RGBW32(fastled_col.r, fastled_col.g, fastled_col.b, 0); +} + + +/////////////////////////////////////////////////////////////////////////////// +// WS2812FX class implementation +/////////////////////////////////////////////////////////////////////////////// + //do not call this method from system context (network callback) void WS2812FX::finalizeInit(void) { //reset segment runtimes - for (uint8_t i = 0; i < MAX_NUM_SEGMENTS; i++) { - _segment_runtimes[i].markForReset(); - _segment_runtimes[i].resetIfRequired(); + for (segment &seg : _segments) { + seg.markForReset(); + seg.resetIfRequired(); } + // for the lack of better place enumerate ledmaps here + // if we do it in json.cpp (serializeInfo()) we are getting flashes on LEDs + // unfortunately this means we do not get updates after uploads + enumerateLedmaps(); + _hasWhiteChannel = _isOffRefreshRequired = false; //if busses failed to load, add default (fresh install, FS issue, ...) @@ -93,7 +829,7 @@ void WS2812FX::finalizeInit(void) uint16_t start = prevLen; uint16_t count = defCounts[(i < defNumCounts) ? i : defNumCounts -1]; prevLen += count; - BusConfig defCfg = BusConfig(DEFAULT_LED_TYPE, defPin, start, count, DEFAULT_LED_COLOR_ORDER); + BusConfig defCfg = BusConfig(DEFAULT_LED_TYPE, defPin, start, count, DEFAULT_LED_COLOR_ORDER, false, 0, RGBW_MODE_MANUAL_ONLY); busses.add(defCfg); } } @@ -118,8 +854,23 @@ void WS2812FX::finalizeInit(void) #endif } - //segments are created in makeAutoSegments(); + //initialize leds array. TBD: realloc if nr of leds change + if (Segment::_globalLeds) { + purgeSegments(true); + free(Segment::_globalLeds); + Segment::_globalLeds = nullptr; + } + if (useLedsArray) { + #if defined(ARDUINO_ARCH_ESP32) && defined(WLED_USE_PSRAM) + if (psramFound()) + Segment::_globalLeds = (CRGB*) ps_malloc(sizeof(CRGB) * _length); + else + #endif + Segment::_globalLeds = (CRGB*) malloc(sizeof(CRGB) * _length); + memset(Segment::_globalLeds, 0, sizeof(CRGB) * _length); + } + //segments are created in makeAutoSegments(); setBrightness(_brightness); } @@ -129,54 +880,44 @@ void WS2812FX::service() { if (nowUp - _lastShow < MIN_SHOW_DELAY) return; bool doShow = false; - for(uint8_t i=0; i < MAX_NUM_SEGMENTS; i++) - { - //if (realtimeMode && useMainSegmentOnly && i == getMainSegmentId()) continue; - - _segment_index = i; - - // reset the segment runtime data if needed, called before isActive to ensure deleted - // segment's buffers are cleared - SEGENV.resetIfRequired(); + _isServicing = true; + _segment_index = 0; + for (segment &seg : _segments) { + // reset the segment runtime data if needed + seg.resetIfRequired(); - if (!SEGMENT.isActive()) continue; + if (!seg.isActive()) continue; // last condition ensures all solid segments are updated at the same time - if(nowUp > SEGENV.next_time || _triggered || (doShow && SEGMENT.mode == 0)) + if(nowUp > seg.next_time || _triggered || (doShow && seg.mode == FX_MODE_STATIC)) { - if (SEGMENT.grouping == 0) SEGMENT.grouping = 1; //sanity check + if (seg.grouping == 0) seg.grouping = 1; //sanity check doShow = true; uint16_t delay = FRAMETIME; - if (!SEGMENT.getOption(SEG_OPTION_FREEZE)) { //only run effect function if not frozen - _virtualSegmentLength = SEGMENT.virtualLength(); - _bri_t = SEGMENT.opacity; _colors_t[0] = SEGMENT.colors[0]; _colors_t[1] = SEGMENT.colors[1]; _colors_t[2] = SEGMENT.colors[2]; - uint8_t _cct_t = SEGMENT.cct; - if (!IS_SEGMENT_ON) _bri_t = 0; - for (uint8_t t = 0; t < MAX_NUM_TRANSITIONS; t++) { - if ((transitions[t].segment & 0x3F) != i) continue; - uint8_t slot = transitions[t].segment >> 6; - if (slot == 0) _bri_t = transitions[t].currentBri(); - if (slot == 1) _cct_t = transitions[t].currentBri(false, 1); - _colors_t[slot] = transitions[t].currentColor(SEGMENT.colors[slot]); - } - if (!cctFromRgb || correctWB) busses.setSegmentCCT(_cct_t, correctWB); - for (uint8_t c = 0; c < NUM_COLORS; c++) { - _colors_t[c] = gamma32(_colors_t[c]); - } - handle_palette(); - - // if segment is not RGB capable, force None auto white mode - // If not RGB capable, also treat palette as if default (0), as palettes set white channel to 0 - _no_rgb = !(SEGMENT.getLightCapabilities() & 0x01); - if (_no_rgb) Bus::setAutoWhiteMode(RGBW_MODE_MANUAL_ONLY); - delay = (this->*_mode[SEGMENT.mode])(); //effect function - if (SEGMENT.mode != FX_MODE_HALLOWEEN_EYES) SEGENV.call++; - Bus::setAutoWhiteMode(strip.autoWhiteMode); + if (!seg.freeze) { //only run effect function if not frozen + _virtualSegmentLength = seg.virtualLength(); + _colors_t[0] = seg.currentColor(0, seg.colors[0]); + _colors_t[1] = seg.currentColor(1, seg.colors[1]); + _colors_t[2] = seg.currentColor(2, seg.colors[2]); + seg.currentPalette(_currentPalette, seg.palette); + + if (!cctFromRgb || correctWB) busses.setSegmentCCT(seg.currentBri(seg.cct, true), correctWB); + for (uint8_t c = 0; c < NUM_COLORS; c++) _colors_t[c] = gamma32(_colors_t[c]); + + // effect blending (execute previous effect) + // actual code may be a bit more involved as effects have runtime data including allocated memory + //if (seg.transitional && seg._modeP) (*_mode[seg._modeP])(progress()); + delay = (*_mode[seg.currentMode(seg.mode)])(); + if (seg.mode != FX_MODE_HALLOWEEN_EYES) seg.call++; + if (seg.transitional && delay > FRAMETIME) delay = FRAMETIME; // foce faster updates during transition + + seg.handleTransition(); } - SEGENV.next_time = nowUp + delay; + seg.next_time = nowUp + delay; } + _segment_index++; } _virtualSegmentLength = 0; busses.setSegmentCCT(-1); @@ -185,65 +926,21 @@ void WS2812FX::service() { show(); } _triggered = false; + _isServicing = false; } -void IRAM_ATTR WS2812FX::setPixelColor(uint16_t i, byte r, byte g, byte b, byte w) +void IRAM_ATTR WS2812FX::setPixelColor(int i, uint32_t col) { - uint8_t segIdx; - - if (SEGLEN) { // SEGLEN!=0 -> from segment/FX - //color_blend(getpixel, col, _bri_t); (pseudocode for future blending of segments) - if (_bri_t < 255) { - r = scale8(r, _bri_t); - g = scale8(g, _bri_t); - b = scale8(b, _bri_t); - w = scale8(w, _bri_t); - } - segIdx = _segment_index; - } else // from live/realtime - segIdx = _mainSegment; - - if (SEGLEN || (realtimeMode && useMainSegmentOnly)) { - uint32_t col = RGBW32(r, g, b, w); - uint16_t len = _segments[segIdx].length(); - - // get physical pixel address (taking into account start, grouping, spacing [and offset]) - i = i * _segments[segIdx].groupLength(); - if (_segments[segIdx].options & REVERSE) { // is segment reversed? - if (_segments[segIdx].options & MIRROR) { // is segment mirrored? - i = (len - 1) / 2 - i; //only need to index half the pixels - } else { - i = (len - 1) - i; - } - } - i += _segments[segIdx].start; - - // set all the pixels in the group - for (uint16_t j = 0; j < _segments[segIdx].grouping; j++) { - uint16_t indexSet = i + ((_segments[segIdx].options & REVERSE) ? -j : j); - if (indexSet >= _segments[segIdx].start && indexSet < _segments[segIdx].stop) { - - if (_segments[segIdx].options & MIRROR) { //set the corresponding mirrored pixel - uint16_t indexMir = _segments[segIdx].stop - indexSet + _segments[segIdx].start - 1; - indexMir += _segments[segIdx].offset; // offset/phase - - if (indexMir >= _segments[segIdx].stop) indexMir -= len; - if (indexMir < customMappingSize) indexMir = customMappingTable[indexMir]; - - busses.setPixelColor(indexMir, col); - } - indexSet += _segments[segIdx].offset; // offset/phase - - if (indexSet >= _segments[segIdx].stop) indexSet -= len; - if (indexSet < customMappingSize) indexSet = customMappingTable[indexSet]; + if (i >= _length) return; + if (i < customMappingSize) i = customMappingTable[i]; + busses.setPixelColor(i, col); +} - busses.setPixelColor(indexSet, col); - } - } - } else { - if (i < customMappingSize) i = customMappingTable[i]; - busses.setPixelColor(i, RGBW32(r, g, b, w)); - } +uint32_t WS2812FX::getPixelColor(uint16_t i) +{ + if (i >= _length) return 0; + if (i < customMappingSize) i = customMappingTable[i]; + return busses.getPixelColor(i); } @@ -367,65 +1064,38 @@ uint16_t WS2812FX::getFps() { return _cumulativeFps +1; } -uint8_t WS2812FX::getTargetFps() { - return _targetFps; -} - void WS2812FX::setTargetFps(uint8_t fps) { if (fps > 0 && fps <= 120) _targetFps = fps; _frametime = 1000 / _targetFps; } -/** - * Forces the next frame to be computed on all active segments. - */ -void WS2812FX::trigger() { - _triggered = true; -} - void WS2812FX::setMode(uint8_t segid, uint8_t m) { - if (segid >= MAX_NUM_SEGMENTS) return; + if (segid >= _segments.size()) return; - if (m >= MODE_COUNT) m = MODE_COUNT - 1; + if (m >= getModeCount()) m = getModeCount() - 1; - if (_segments[segid].mode != m) - { - _segment_runtimes[segid].markForReset(); + if (_segments[segid].mode != m) { + _segments[segid].startTransition(_transitionDur); // set effect transitions + //_segments[segid].markForReset(); _segments[segid].mode = m; } } -uint8_t WS2812FX::getModeCount() -{ - return MODE_COUNT; -} - -uint8_t WS2812FX::getPaletteCount() -{ - return 13 + GRADIENT_PALETTE_COUNT; -} - -void WS2812FX::setColor(uint8_t slot, uint8_t r, uint8_t g, uint8_t b, uint8_t w) { - setColor(slot, RGBW32(r, g, b, w)); -} - //applies to all active and selected segments void WS2812FX::setColor(uint8_t slot, uint32_t c) { if (slot >= NUM_COLORS) return; - for (uint8_t i = 0; i < MAX_NUM_SEGMENTS; i++) - { - if (_segments[i].isActive() && _segments[i].isSelected()) { - _segments[i].setColor(slot, c, i); + for (segment &seg : _segments) { + if (seg.isSelected()) { + seg.setColor(slot, c); } } } void WS2812FX::setCCT(uint16_t k) { - for (uint8_t i = 0; i < MAX_NUM_SEGMENTS; i++) - { - if (_segments[i].isActive() && _segments[i].isSelected()) { - _segments[i].setCCT(k, i); + for (segment &seg : _segments) { + if (seg.isActive() && seg.isSelected()) { + seg.setCCT(k); } } } @@ -435,9 +1105,8 @@ void WS2812FX::setBrightness(uint8_t b, bool direct) { if (_brightness == b) return; _brightness = b; if (_brightness == 0) { //unfreeze all segments on power off - for (uint8_t i = 0; i < MAX_NUM_SEGMENTS; i++) - { - _segments[i].setOption(SEG_OPTION_FREEZE, false); + for (segment &seg : _segments) { + seg.setOption(SEG_OPTION_FREEZE, false); } } if (direct) { @@ -445,50 +1114,31 @@ void WS2812FX::setBrightness(uint8_t b, bool direct) { busses.setBrightness(b); } else { unsigned long t = millis(); - if (_segment_runtimes[0].next_time > t + 22 && t - _lastShow > MIN_SHOW_DELAY) show(); //apply brightness change immediately if no refresh soon + if (_segments[0].next_time > t + 22 && t - _lastShow > MIN_SHOW_DELAY) show(); //apply brightness change immediately if no refresh soon } } -uint8_t WS2812FX::getBrightness(void) { - return _brightness; -} - -uint8_t WS2812FX::getMaxSegments(void) { - return MAX_NUM_SEGMENTS; -} - uint8_t WS2812FX::getFirstSelectedSegId(void) { - for (uint8_t i = 0; i < MAX_NUM_SEGMENTS; i++) - { - if (_segments[i].isActive() && _segments[i].isSelected()) return i; + size_t i = 0; + for (segment &seg : _segments) { + if (seg.isSelected()) return i; + i++; } // if none selected, use the main segment return getMainSegmentId(); } void WS2812FX::setMainSegmentId(uint8_t n) { - if (n >= MAX_NUM_SEGMENTS) return; - //use supplied n if active, or first active - if (_segments[n].isActive()) { - _mainSegment = n; return; - } - for (uint8_t i = 0; i < MAX_NUM_SEGMENTS; i++) - { - if (_segments[i].isActive()) { - _mainSegment = i; return; - } - } _mainSegment = 0; + if (n < _segments.size()) { + _mainSegment = n; + } return; } -uint8_t WS2812FX::getMainSegmentId(void) { - return _mainSegment; -} - uint8_t WS2812FX::getLastActiveSegmentId(void) { - for (uint8_t i = MAX_NUM_SEGMENTS -1; i > 0; i--) { + for (size_t i = _segments.size() -1; i > 0; i--) { if (_segments[i].isActive()) return i; } return 0; @@ -496,136 +1146,27 @@ uint8_t WS2812FX::getLastActiveSegmentId(void) { uint8_t WS2812FX::getActiveSegmentsNum(void) { uint8_t c = 0; - for (uint8_t i = 0; i < MAX_NUM_SEGMENTS; i++) - { + for (size_t i = 0; i < _segments.size(); i++) { if (_segments[i].isActive()) c++; } return c; } - -uint32_t WS2812FX::getPixelColor(uint16_t i) -{ - // get physical pixel - i = i * SEGMENT.groupLength();; - if (IS_REVERSE) { - if (IS_MIRROR) i = (SEGMENT.length() - 1) / 2 - i; //only need to index half the pixels - else i = (SEGMENT.length() - 1) - i; - } - i += SEGMENT.start; - - if (SEGLEN) { - /* offset/phase */ - i += SEGMENT.offset; - if (i >= SEGMENT.stop) i -= SEGMENT.length(); - } - - if (i < customMappingSize) i = customMappingTable[i]; - if (i >= _length) return 0; - - return busses.getPixelColor(i); -} - -WS2812FX::Segment& WS2812FX::getSegment(uint8_t id) { - if (id >= MAX_NUM_SEGMENTS) return _segments[getMainSegmentId()]; - return _segments[id]; -} - -WS2812FX::Segment& WS2812FX::getFirstSelectedSeg(void) { - return _segments[getFirstSelectedSegId()]; -} - -WS2812FX::Segment& WS2812FX::getMainSegment(void) { - return _segments[getMainSegmentId()]; -} - -WS2812FX::Segment* WS2812FX::getSegments(void) { - return _segments; -} - -uint32_t WS2812FX::getLastShow(void) { - return _lastShow; -} - -uint16_t WS2812FX::getLengthTotal(void) { - return _length; -} - -uint16_t WS2812FX::getLengthPhysical(void) { - uint16_t len = 0; - for (uint8_t b = 0; b < busses.getNumBusses(); b++) { - Bus *bus = busses.getBus(b); - if (bus->getType() >= TYPE_NET_DDP_RGB) continue; //exclude non-physical network busses - len += bus->getLength(); - } - return len; -} - -uint8_t WS2812FX::Segment::differs(Segment& b) { - uint8_t d = 0; - if (start != b.start) d |= SEG_DIFFERS_BOUNDS; - if (stop != b.stop) d |= SEG_DIFFERS_BOUNDS; - if (offset != b.offset) d |= SEG_DIFFERS_GSO; - if (grouping != b.grouping) d |= SEG_DIFFERS_GSO; - if (spacing != b.spacing) d |= SEG_DIFFERS_GSO; - if (opacity != b.opacity) d |= SEG_DIFFERS_BRI; - if (mode != b.mode) d |= SEG_DIFFERS_FX; - if (speed != b.speed) d |= SEG_DIFFERS_FX; - if (intensity != b.intensity) d |= SEG_DIFFERS_FX; - if (palette != b.palette) d |= SEG_DIFFERS_FX; - - if ((options & 0b00101110) != (b.options & 0b00101110)) d |= SEG_DIFFERS_OPT; - if ((options & 0x01) != (b.options & 0x01)) d |= SEG_DIFFERS_SEL; - - for (uint8_t i = 0; i < NUM_COLORS; i++) - { - if (colors[i] != b.colors[i]) d |= SEG_DIFFERS_COL; - } - - return d; -} - -void WS2812FX::Segment::refreshLightCapabilities() { - if (!isActive()) { - _capabilities = 0; return; - } - uint8_t capabilities = 0; - uint8_t awm = instance->autoWhiteMode; - bool whiteSlider = (awm == RGBW_MODE_DUAL || awm == RGBW_MODE_MANUAL_ONLY); - bool segHasValidBus = false; - - for (uint8_t b = 0; b < busses.getNumBusses(); b++) { - Bus *bus = busses.getBus(b); - if (bus == nullptr || bus->getLength()==0) break; - if (bus->getStart() >= stop) continue; - if (bus->getStart() + bus->getLength() <= start) continue; - - segHasValidBus = true; - uint8_t type = bus->getType(); - if (type != TYPE_ANALOG_1CH && (cctFromRgb || type != TYPE_ANALOG_2CH)) - { - capabilities |= 0x01; // segment supports RGB (full color) - } - if (bus->isRgbw() && whiteSlider) capabilities |= 0x02; // segment supports white channel - if (!cctFromRgb) { - switch (type) { - case TYPE_ANALOG_5CH: - case TYPE_ANALOG_2CH: - capabilities |= 0x04; //segment supports white CCT - } - } - if (correctWB && type != TYPE_ANALOG_1CH) capabilities |= 0x04; //white balance correction (uses CCT slider) + +uint16_t WS2812FX::getLengthPhysical(void) { + uint16_t len = 0; + for (size_t b = 0; b < busses.getNumBusses(); b++) { + Bus *bus = busses.getBus(b); + if (bus->getType() >= TYPE_NET_DDP_RGB) continue; //exclude non-physical network busses + len += bus->getLength(); } - // if seg has any bus, but no bus has RGB, it by definition supports white (at least for now) - // In case of no RGB, disregard auto white mode and always show a white slider - if (segHasValidBus && !(capabilities & 0x01)) capabilities |= 0x02; // segment supports white channel - _capabilities = capabilities; + return len; } //used for JSON API info.leds.rgbw. Little practical use, deprecate with info.leds.rgbw. //returns if there is an RGBW bus (supports RGB and White, not only white) //not influenced by auto-white mode, also true if white slider does not affect output white channel bool WS2812FX::hasRGBWBus(void) { - for (uint8_t b = 0; b < busses.getNumBusses(); b++) { + for (size_t b = 0; b < busses.getNumBusses(); b++) { Bus *bus = busses.getBus(b); if (bus == nullptr || bus->getLength()==0) break; switch (bus->getType()) { @@ -640,7 +1181,7 @@ bool WS2812FX::hasRGBWBus(void) { bool WS2812FX::hasCCTBus(void) { if (cctFromRgb && !correctWB) return false; - for (uint8_t b = 0; b < busses.getNumBusses(); b++) { + for (size_t b = 0; b < busses.getNumBusses(); b++) { Bus *bus = busses.getBus(b); if (bus == nullptr || bus->getLength()==0) break; switch (bus->getType()) { @@ -652,80 +1193,109 @@ bool WS2812FX::hasCCTBus(void) { return false; } -void WS2812FX::setSegment(uint8_t n, uint16_t i1, uint16_t i2, uint8_t grouping, uint8_t spacing, uint16_t offset) { - if (n >= MAX_NUM_SEGMENTS) return; +void WS2812FX::purgeSegments(bool force) { + // remove all inactive segments (from the back) + int deleted = 0; + if (_segments.size() <= 1) return; + for (size_t i = _segments.size()-1; i > 0; i--) + if (_segments[i].stop == 0 || force) { + DEBUG_PRINT(F("Purging segment segment: ")); DEBUG_PRINTLN(i); + deleted++; + _segments.erase(_segments.begin() + i); + } + if (deleted) { + _segments.shrink_to_fit(); + if (_mainSegment >= _segments.size()) setMainSegmentId(0); + } +} + +Segment& WS2812FX::getSegment(uint8_t id) { + return _segments[id >= _segments.size() ? getMainSegmentId() : id]; // vectors +} + +void WS2812FX::setSegment(uint8_t n, uint16_t i1, uint16_t i2, uint8_t grouping, uint8_t spacing, uint16_t offset, uint16_t startY, uint16_t stopY) { + if (n >= _segments.size()) return; Segment& seg = _segments[n]; //return if neither bounds nor grouping have changed bool boundsUnchanged = (seg.start == i1 && seg.stop == i2); + if (isMatrix) { + boundsUnchanged &= (seg.startY == startY && seg.stopY == stopY); + } if (boundsUnchanged && (!grouping || (seg.grouping == grouping && seg.spacing == spacing)) && (offset == UINT16_MAX || offset == seg.offset)) return; - if (seg.stop) setRange(seg.start, seg.stop -1, 0); //turn old segment range off + //if (seg.stop) setRange(seg.start, seg.stop -1, BLACK); //turn old segment range off + if (seg.stop) seg.fill(BLACK); //turn old segment range off if (i2 <= i1) //disable segment { + // disabled segments should get removed using purgeSegments() + DEBUG_PRINT(F("-- Segment ")); DEBUG_PRINT(n); DEBUG_PRINTLN(F(" marked inactive.")); seg.stop = 0; - if (seg.name) { - delete[] seg.name; - seg.name = nullptr; - } + //if (seg.name) { + // delete[] seg.name; + // seg.name = nullptr; + //} // if main segment is deleted, set first active as main segment if (n == _mainSegment) setMainSegmentId(0); + seg.markForReset(); return; } - if (i1 < _length) seg.start = i1; - seg.stop = i2; - if (i2 > _length) seg.stop = _length; + if (isMatrix) { + #ifndef WLED_DISABLE_2D + if (i1 < matrixWidth) seg.start = i1; + seg.stop = i2 > matrixWidth ? matrixWidth : i2; + if (startY < matrixHeight) seg.startY = startY; + seg.stopY = stopY > matrixHeight ? matrixHeight : MAX(1,stopY); + #endif + } else { + if (i1 < _length) seg.start = i1; + seg.stop = i2 > _length ? _length : i2; + seg.startY = 0; + seg.stopY = 1; + } if (grouping) { seg.grouping = grouping; seg.spacing = spacing; } if (offset < UINT16_MAX) seg.offset = offset; - _segment_runtimes[n].markForReset(); + seg.markForReset(); if (!boundsUnchanged) seg.refreshLightCapabilities(); } void WS2812FX::restartRuntime() { - for (uint8_t i = 0; i < MAX_NUM_SEGMENTS; i++) { - _segment_runtimes[i].markForReset(); - } + for (segment &seg : _segments) seg.markForReset(); } void WS2812FX::resetSegments() { - for (uint8_t i = 0; i < MAX_NUM_SEGMENTS; i++) if (_segments[i].name) delete[] _segments[i].name; + _segments.clear(); // destructs all Segment as part of clearing + #ifndef WLED_DISABLE_2D + segment seg = isMatrix ? Segment(0, matrixWidth, 0, matrixHeight) : Segment(0, _length); + #else + segment seg = Segment(0, _length); + #endif + _segments.push_back(seg); _mainSegment = 0; - memset(_segments, 0, sizeof(_segments)); - //memset(_segment_runtimes, 0, sizeof(_segment_runtimes)); - _segment_index = 0; - _segments[0].mode = DEFAULT_MODE; - _segments[0].colors[0] = DEFAULT_COLOR; - _segments[0].start = 0; - _segments[0].speed = DEFAULT_SPEED; - _segments[0].intensity = DEFAULT_INTENSITY; - _segments[0].stop = _length; - _segments[0].grouping = 1; - _segments[0].setOption(SEG_OPTION_SELECTED, 1); - _segments[0].setOption(SEG_OPTION_ON, 1); - _segments[0].opacity = 255; - _segments[0].cct = 127; - - for (uint16_t i = 1; i < MAX_NUM_SEGMENTS; i++) - { - _segments[i].colors[0] = color_wheel(i*51); - _segments[i].grouping = 1; - _segments[i].setOption(SEG_OPTION_ON, 1); - _segments[i].opacity = 255; - _segments[i].cct = 127; - _segments[i].speed = DEFAULT_SPEED; - _segments[i].intensity = DEFAULT_INTENSITY; - _segment_runtimes[i].markForReset(); - } - _segment_runtimes[0].markForReset(); } void WS2812FX::makeAutoSegments(bool forceReset) { - if (autoSegments) { //make one segment per bus + if (isMatrix) { + #ifndef WLED_DISABLE_2D + // only create 1 2D segment + if (forceReset || getSegmentsNum() == 0) resetSegments(); // initialises 1 segment + else if (getActiveSegmentsNum() == 1) { + size_t i = getLastActiveSegmentId(); + _segments[i].start = 0; + _segments[i].stop = matrixWidth; + _segments[i].startY = 0; + _segments[i].stopY = matrixHeight; + _segments[i].grouping = 1; + _segments[i].spacing = 0; + _mainSegment = i; + } + #endif + } else if (autoSegments) { //make one segment per bus uint16_t segStarts[MAX_NUM_SEGMENTS] = {0}; uint16_t segStops [MAX_NUM_SEGMENTS] = {0}; uint8_t s = 0; @@ -733,10 +1303,10 @@ void WS2812FX::makeAutoSegments(bool forceReset) { Bus* b = busses.getBus(i); segStarts[s] = b->getStart(); - segStops[s] = segStarts[s] + b->getLength(); + segStops[s] = segStarts[s] + b->getLength(); //check for overlap with previous segments - for (uint8_t j = 0; j < s; j++) { + for (size_t j = 0; j < s; j++) { if (segStops[j] > segStarts[s] && segStarts[j] < segStops[s]) { //segments overlap, merge segStarts[j] = min(segStarts[s],segStarts[j]); @@ -746,21 +1316,21 @@ void WS2812FX::makeAutoSegments(bool forceReset) { } s++; } - for (uint8_t i = 0; i < MAX_NUM_SEGMENTS; i++) { - setSegment(i, segStarts[i], segStops[i]); + _segments.clear(); + for (size_t i = 0; i < s; i++) { + Segment seg = Segment(segStarts[i], segStops[i]); + seg.setOption(SEG_OPTION_SELECTED, true); + _segments.push_back(seg); } + _mainSegment = 0; } else { + if (forceReset || getSegmentsNum() == 0) resetSegments(); //expand the main seg to the entire length, but only if there are no other segments, or reset is forced - uint8_t mainSeg = getMainSegmentId(); - - if (forceReset) { - for (uint8_t i = 0; i < MAX_NUM_SEGMENTS; i++) { - setSegment(i, 0, 0); - } - } - - if (getActiveSegmentsNum() < 2) { - setSegment(mainSeg, 0, _length); + else if (getActiveSegmentsNum() == 1) { + size_t i = getLastActiveSegmentId(); + _segments[i].start = 0; + _segments[i].stop = _length; + _mainSegment = 0; } } @@ -769,26 +1339,23 @@ void WS2812FX::makeAutoSegments(bool forceReset) { void WS2812FX::fixInvalidSegments() { //make sure no segment is longer than total (sanity check) - for (uint8_t i = 0; i < MAX_NUM_SEGMENTS; i++) - { - if (_segments[i].start >= _length) setSegment(i, 0, 0); - if (_segments[i].stop > _length) setSegment(i, _segments[i].start, _length); + for (size_t i = getSegmentsNum()-1; i > 0; i--) { + if (_segments[i].start >= _length) { _segments.erase(_segments.begin()+i); continue; } + if (_segments[i].stop > _length) _segments[i].stop = _length; // this is always called as the last step after finalizeInit(), update covered bus types - getSegment(i).refreshLightCapabilities(); + _segments[i].refreshLightCapabilities(); } } //true if all segments align with a bus, or if a segment covers the total length bool WS2812FX::checkSegmentAlignment() { - for (uint8_t i = 0; i < MAX_NUM_SEGMENTS; i++) - { - if (_segments[i].start >= _segments[i].stop) continue; //inactive segment - bool aligned = false; + bool aligned = false; + for (segment &seg : _segments) { for (uint8_t b = 0; bgetStart() && _segments[i].stop == bus->getStart() + bus->getLength()) aligned = true; + if (seg.start == bus->getStart() && seg.stop == bus->getStart() + bus->getLength()) aligned = true; } - if (_segments[i].start == 0 && _segments[i].stop == _length) aligned = true; + if (seg.start == 0 && seg.stop == _length) aligned = true; if (!aligned) return false; } return true; @@ -800,9 +1367,9 @@ bool WS2812FX::checkSegmentAlignment() { uint8_t WS2812FX::setPixelSegment(uint8_t n) { uint8_t prevSegId = _segment_index; - if (n < MAX_NUM_SEGMENTS) { + if (n < _segments.size()) { _segment_index = n; - _virtualSegmentLength = SEGMENT.virtualLength(); + _virtualSegmentLength = _segments[_segment_index].virtualLength(); } return prevSegId; } @@ -818,350 +1385,64 @@ void WS2812FX::setRange(uint16_t i, uint16_t i2, uint32_t col) } } -void WS2812FX::setShowCallback(show_callback cb) -{ - _callback = cb; -} - -void WS2812FX::setTransition(uint16_t t) -{ - _transitionDur = t; -} - void WS2812FX::setTransitionMode(bool t) { - unsigned long waitMax = millis() + 20; //refresh after 20 ms if transition enabled - for (uint16_t i = 0; i < MAX_NUM_SEGMENTS; i++) - { - _segments[i].setOption(SEG_OPTION_TRANSITIONAL, t); - - if (t && _segments[i].mode == FX_MODE_STATIC && _segment_runtimes[i].next_time > waitMax) - _segment_runtimes[i].next_time = waitMax; - } -} - -/* - * color blend function - */ -uint32_t IRAM_ATTR WS2812FX::color_blend(uint32_t color1, uint32_t color2, uint16_t blend, bool b16) { - if(blend == 0) return color1; - uint16_t blendmax = b16 ? 0xFFFF : 0xFF; - if(blend == blendmax) return color2; - uint8_t shift = b16 ? 16 : 8; - - uint32_t w1 = W(color1); - uint32_t r1 = R(color1); - uint32_t g1 = G(color1); - uint32_t b1 = B(color1); - - uint32_t w2 = W(color2); - uint32_t r2 = R(color2); - uint32_t g2 = G(color2); - uint32_t b2 = B(color2); - - uint32_t w3 = ((w2 * blend) + (w1 * (blendmax - blend))) >> shift; - uint32_t r3 = ((r2 * blend) + (r1 * (blendmax - blend))) >> shift; - uint32_t g3 = ((g2 * blend) + (g1 * (blendmax - blend))) >> shift; - uint32_t b3 = ((b2 * blend) + (b1 * (blendmax - blend))) >> shift; - - return RGBW32(r3, g3, b3, w3); -} - -/* - * Fills segment with color - */ -void WS2812FX::fill(uint32_t c) { - for(uint16_t i = 0; i < SEGLEN; i++) { - setPixelColor(i, c); - } -} - -/* - * Blends the specified color with the existing pixel color. - */ -void WS2812FX::blendPixelColor(uint16_t n, uint32_t color, uint8_t blend) -{ - setPixelColor(n, color_blend(getPixelColor(n), color, blend)); -} - -/* - * fade out function, higher rate = quicker fade - */ -void WS2812FX::fade_out(uint8_t rate) { - rate = (255-rate) >> 1; - float mappedRate = float(rate) +1.1; - - uint32_t color = SEGCOLOR(1); // target color - int w2 = W(color); - int r2 = R(color); - int g2 = G(color); - int b2 = B(color); - - for(uint16_t i = 0; i < SEGLEN; i++) { - color = getPixelColor(i); - int w1 = W(color); - int r1 = R(color); - int g1 = G(color); - int b1 = B(color); - - int wdelta = (w2 - w1) / mappedRate; - int rdelta = (r2 - r1) / mappedRate; - int gdelta = (g2 - g1) / mappedRate; - int bdelta = (b2 - b1) / mappedRate; - - // if fade isn't complete, make sure delta is at least 1 (fixes rounding issues) - wdelta += (w2 == w1) ? 0 : (w2 > w1) ? 1 : -1; - rdelta += (r2 == r1) ? 0 : (r2 > r1) ? 1 : -1; - gdelta += (g2 == g1) ? 0 : (g2 > g1) ? 1 : -1; - bdelta += (b2 == b1) ? 0 : (b2 > b1) ? 1 : -1; - - setPixelColor(i, r1 + rdelta, g1 + gdelta, b1 + bdelta, w1 + wdelta); - } -} - -/* - * blurs segment content, source: FastLED colorutils.cpp - */ -void WS2812FX::blur(uint8_t blur_amount) -{ - uint8_t keep = 255 - blur_amount; - uint8_t seep = blur_amount >> 1; - CRGB carryover = CRGB::Black; - for(uint16_t i = 0; i < SEGLEN; i++) - { - CRGB cur = col_to_crgb(getPixelColor(i)); - CRGB part = cur; - part.nscale8(seep); - cur.nscale8(keep); - cur += carryover; - if(i > 0) { - uint32_t c = getPixelColor(i-1); - uint8_t r = R(c); - uint8_t g = G(c); - uint8_t b = B(c); - setPixelColor(i-1, qadd8(r, part.red), qadd8(g, part.green), qadd8(b, part.blue)); - } - setPixelColor(i,cur.red, cur.green, cur.blue); - carryover = part; - } -} - -uint16_t IRAM_ATTR WS2812FX::triwave16(uint16_t in) -{ - if (in < 0x8000) return in *2; - return 0xFFFF - (in - 0x8000)*2; -} - -/* - * Generates a tristate square wave w/ attac & decay - * @param x input value 0-255 - * @param pulsewidth 0-127 - * @param attdec attac & decay, max. pulsewidth / 2 - * @returns signed waveform value - */ -int8_t WS2812FX::tristate_square8(uint8_t x, uint8_t pulsewidth, uint8_t attdec) { - int8_t a = 127; - if (x > 127) { - a = -127; - x -= 127; - } - - if (x < attdec) { //inc to max - return (int16_t) x * a / attdec; - } - else if (x < pulsewidth - attdec) { //max - return a; - } - else if (x < pulsewidth) { //dec to 0 - return (int16_t) (pulsewidth - x) * a / attdec; - } - return 0; -} - -/* - * Put a value 0 to 255 in to get a color value. - * The colours are a transition r -> g -> b -> back to r - * Inspired by the Adafruit examples. - */ -uint32_t WS2812FX::color_wheel(uint8_t pos) { - if (SEGMENT.palette) return color_from_palette(pos, false, true, 0); - pos = 255 - pos; - if(pos < 85) { - return ((uint32_t)(255 - pos * 3) << 16) | ((uint32_t)(0) << 8) | (pos * 3); - } else if(pos < 170) { - pos -= 85; - return ((uint32_t)(0) << 16) | ((uint32_t)(pos * 3) << 8) | (255 - pos * 3); - } else { - pos -= 170; - return ((uint32_t)(pos * 3) << 16) | ((uint32_t)(255 - pos * 3) << 8) | (0); - } -} - -/* - * Returns a new, random wheel index with a minimum distance of 42 from pos. - */ -uint8_t WS2812FX::get_random_wheel_index(uint8_t pos) { - uint8_t r = 0, x = 0, y = 0, d = 0; - - while(d < 42) { - r = random8(); - x = abs(pos - r); - y = 255 - x; - d = MIN(x, y); - } - return r; -} - - -uint32_t IRAM_ATTR WS2812FX::crgb_to_col(CRGB fastled) -{ - return RGBW32(fastled.red, fastled.green, fastled.blue, 0); + for (segment &seg : _segments) if (!seg.transitional) seg.startTransition(t ? _transitionDur : 0); } - -CRGB IRAM_ATTR WS2812FX::col_to_crgb(uint32_t color) +#ifdef WLED_DEBUG +void WS2812FX::printSize() { - CRGB fastled_col; - fastled_col.red = R(color); - fastled_col.green = G(color); - fastled_col.blue = B(color); - return fastled_col; + size_t size = 0; + for (const Segment seg : _segments) size += seg.getSize(); + DEBUG_PRINTF("Segments: %d -> %uB\n", _segments.size(), size); + DEBUG_PRINTF("Modes: %d*%d=%uB\n", sizeof(mode_ptr), _mode.size(), (_mode.capacity()*sizeof(mode_ptr))); + DEBUG_PRINTF("Data: %d*%d=%uB\n", sizeof(const char *), _modeData.size(), (_modeData.capacity()*sizeof(const char *))); + DEBUG_PRINTF("Map: %d*%d=%uB\n", sizeof(uint16_t), (int)customMappingSize, customMappingSize*sizeof(uint16_t)); + if (useLedsArray) DEBUG_PRINTF("Buffer: %d*%d=%uB\n", sizeof(CRGB), (int)_length, _length*sizeof(CRGB)); } +#endif - -void WS2812FX::load_gradient_palette(uint8_t index) +void WS2812FX::loadCustomPalettes() { - byte i = constrain(index, 0, GRADIENT_PALETTE_COUNT -1); byte tcp[72]; //support gradient palettes with up to 18 entries - memcpy_P(tcp, (byte*)pgm_read_dword(&(gGradientPalettes[i])), 72); - targetPalette.loadDynamicGradientPalette(tcp); -} - - -/* - * FastLED palette modes helper function. Limitation: Due to memory reasons, multiple active segments with FastLED will disable the Palette transitions - */ -void WS2812FX::handle_palette(void) -{ - bool singleSegmentMode = (_segment_index == _segment_index_palette_last); - _segment_index_palette_last = _segment_index; - - byte paletteIndex = SEGMENT.palette; - if (paletteIndex == 0) //default palette. Differs depending on effect - { - switch (SEGMENT.mode) - { - case FX_MODE_FIRE_2012 : paletteIndex = 35; break; //heat palette - case FX_MODE_COLORWAVES : paletteIndex = 26; break; //landscape 33 - case FX_MODE_FILLNOISE8 : paletteIndex = 9; break; //ocean colors - case FX_MODE_NOISE16_1 : paletteIndex = 20; break; //Drywet - case FX_MODE_NOISE16_2 : paletteIndex = 43; break; //Blue cyan yellow - case FX_MODE_NOISE16_3 : paletteIndex = 35; break; //heat palette - case FX_MODE_NOISE16_4 : paletteIndex = 26; break; //landscape 33 - case FX_MODE_GLITTER : paletteIndex = 11; break; //rainbow colors - case FX_MODE_SUNRISE : paletteIndex = 35; break; //heat palette - case FX_MODE_FLOW : paletteIndex = 6; break; //party - } - } - if (SEGMENT.mode >= FX_MODE_METEOR && paletteIndex == 0) paletteIndex = 4; - - switch (paletteIndex) - { - case 0: //default palette. Exceptions for specific effects above - targetPalette = PartyColors_p; break; - case 1: {//periodically replace palette with a random one. Doesn't work with multiple FastLED segments - if (!singleSegmentMode) - { - targetPalette = PartyColors_p; break; //fallback - } - if (millis() - _lastPaletteChange > 1000 + ((uint32_t)(255-SEGMENT.intensity))*100) - { - targetPalette = CRGBPalette16( - CHSV(random8(), 255, random8(128, 255)), - CHSV(random8(), 255, random8(128, 255)), - CHSV(random8(), 192, random8(128, 255)), - CHSV(random8(), 255, random8(128, 255))); - _lastPaletteChange = millis(); - } break;} - case 2: {//primary color only - CRGB prim = col_to_crgb(SEGCOLOR(0)); - targetPalette = CRGBPalette16(prim); break;} - case 3: {//primary + secondary - CRGB prim = col_to_crgb(SEGCOLOR(0)); - CRGB sec = col_to_crgb(SEGCOLOR(1)); - targetPalette = CRGBPalette16(prim,prim,sec,sec); break;} - case 4: {//primary + secondary + tertiary - CRGB prim = col_to_crgb(SEGCOLOR(0)); - CRGB sec = col_to_crgb(SEGCOLOR(1)); - CRGB ter = col_to_crgb(SEGCOLOR(2)); - targetPalette = CRGBPalette16(ter,sec,prim); break;} - case 5: {//primary + secondary (+tert if not off), more distinct - CRGB prim = col_to_crgb(SEGCOLOR(0)); - CRGB sec = col_to_crgb(SEGCOLOR(1)); - if (SEGCOLOR(2)) { - CRGB ter = col_to_crgb(SEGCOLOR(2)); - targetPalette = CRGBPalette16(prim,prim,prim,prim,prim,sec,sec,sec,sec,sec,ter,ter,ter,ter,ter,prim); - } else { - targetPalette = CRGBPalette16(prim,prim,prim,prim,prim,prim,prim,prim,sec,sec,sec,sec,sec,sec,sec,sec); + CRGBPalette16 targetPalette; + for (int index = 0; index<10; index++) { + char fileName[32]; + strcpy_P(fileName, PSTR("/palette")); + sprintf(fileName +8, "%d", index); + strcat(fileName, ".json"); + + StaticJsonDocument<1536> pDoc; // barely enough to fit 72 numbers + if (WLED_FS.exists(fileName)) { + DEBUG_PRINT(F("Reading palette from ")); + DEBUG_PRINTLN(fileName); + + if (readObjectFromFile(fileName, nullptr, &pDoc)) { + JsonArray pal = pDoc[F("palette")]; + if (!pal.isNull() && pal.size()>7) { // not an empty palette (at least 2 entries) + size_t palSize = MIN(pal.size(), 72); + palSize -= palSize % 4; // make sure size is multiple of 4 + for (size_t i=0; i()<256; i+=4) { + tcp[ i ] = (uint8_t) pal[ i ].as(); // index + tcp[i+1] = (uint8_t) pal[i+1].as(); // R + tcp[i+2] = (uint8_t) pal[i+2].as(); // G + tcp[i+3] = (uint8_t) pal[i+3].as(); // B + DEBUG_PRINTF("%d(%d) : %d %d %d\n", i, int(tcp[i]), int(tcp[i+1]), int(tcp[i+2]), int(tcp[i+3])); + } + customPalettes.push_back(targetPalette.loadDynamicGradientPalette(tcp)); + } } - break;} - case 6: //Party colors - targetPalette = PartyColors_p; break; - case 7: //Cloud colors - targetPalette = CloudColors_p; break; - case 8: //Lava colors - targetPalette = LavaColors_p; break; - case 9: //Ocean colors - targetPalette = OceanColors_p; break; - case 10: //Forest colors - targetPalette = ForestColors_p; break; - case 11: //Rainbow colors - targetPalette = RainbowColors_p; break; - case 12: //Rainbow stripe colors - targetPalette = RainbowStripeColors_p; break; - default: //progmem palettes - load_gradient_palette(paletteIndex -13); - } - - if (singleSegmentMode && paletteFade && SEGENV.call > 0) //only blend if just one segment uses FastLED mode - { - nblendPaletteTowardPalette(currentPalette, targetPalette, 48); - } else - { - currentPalette = targetPalette; - } -} - - -/* - * Gets a single color from the currently selected palette. - * @param i Palette Index (if mapping is true, the full palette will be SEGLEN long, if false, 255). Will wrap around automatically. - * @param mapping if true, LED position in segment is considered for color - * @param wrap FastLED palettes will usally wrap back to the start smoothly. Set false to get a hard edge - * @param mcol If the default palette 0 is selected, return the standard color 0, 1 or 2 instead. If >2, Party palette is used instead - * @param pbri Value to scale the brightness of the returned color by. Default is 255. (no scaling) - * @returns Single color from palette - */ -uint32_t IRAM_ATTR WS2812FX::color_from_palette(uint16_t i, bool mapping, bool wrap, uint8_t mcol, uint8_t pbri) -{ - if ((SEGMENT.palette == 0 && mcol < 3) || _no_rgb) { - uint32_t color = SEGCOLOR(mcol); - if (pbri == 255) return color; - return RGBW32(scale8_video(R(color),pbri), scale8_video(G(color),pbri), scale8_video(B(color),pbri), scale8_video(W(color),pbri)); + } else { + break; + } } - - uint8_t paletteIndex = i; - if (mapping && SEGLEN > 1) paletteIndex = (i*255)/(SEGLEN -1); - if (!wrap) paletteIndex = scale8(paletteIndex, 240); //cut off blend at palette "end" - CRGB fastled_col; - fastled_col = ColorFromPalette(currentPalette, paletteIndex, pbri, (paletteBlend == 3)? NOBLEND:LINEARBLEND); - - return crgb_to_col(fastled_col); } - //load custom mapping table from JSON file (called from finalizeInit() or deserializeState()) void WS2812FX::deserializeMap(uint8_t n) { + if (isMatrix) return; // 2D support creates its own ledmap + char fileName[32]; strcpy_P(fileName, PSTR("/ledmap")); if (n) sprintf(fileName +7, "%d", n); @@ -1178,11 +1459,7 @@ void WS2812FX::deserializeMap(uint8_t n) { return; } - #ifdef WLED_USE_DYNAMIC_JSON - DynamicJsonDocument doc(JSON_BUFFER_SIZE); - #else if (!requestJSONBufferLock(7)) return; - #endif DEBUG_PRINT(F("Reading LED map from ")); DEBUG_PRINTLN(fileName); @@ -1211,58 +1488,22 @@ void WS2812FX::deserializeMap(uint8_t n) { releaseJSONBufferLock(); } -//gamma 2.8 lookup table used for color correction -byte gammaT[] = { - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, - 2, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 5, 5, 5, - 5, 6, 6, 6, 6, 7, 7, 7, 7, 8, 8, 8, 9, 9, 9, 10, - 10, 10, 11, 11, 11, 12, 12, 13, 13, 13, 14, 14, 15, 15, 16, 16, - 17, 17, 18, 18, 19, 19, 20, 20, 21, 21, 22, 22, 23, 24, 24, 25, - 25, 26, 27, 27, 28, 29, 29, 30, 31, 32, 32, 33, 34, 35, 35, 36, - 37, 38, 39, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 50, - 51, 52, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 66, 67, 68, - 69, 70, 72, 73, 74, 75, 77, 78, 79, 81, 82, 83, 85, 86, 87, 89, - 90, 92, 93, 95, 96, 98, 99,101,102,104,105,107,109,110,112,114, - 115,117,119,120,122,124,126,127,129,131,133,135,137,138,140,142, - 144,146,148,150,152,154,156,158,160,162,164,167,169,171,173,175, - 177,180,182,184,186,189,191,193,196,198,200,203,205,208,210,213, - 215,218,220,223,225,228,231,233,236,239,241,244,247,249,252,255 }; - -uint8_t WS2812FX::gamma8_cal(uint8_t b, float gamma) { - return (int)(pow((float)b / 255.0, gamma) * 255 + 0.5); -} - -void WS2812FX::calcGammaTable(float gamma) -{ - for (uint16_t i = 0; i < 256; i++) { - gammaT[i] = gamma8_cal(i, gamma); - } -} - -uint8_t WS2812FX::gamma8(uint8_t b) -{ - return gammaT[b]; -} - -uint32_t WS2812FX::gamma32(uint32_t color) -{ - if (!gammaCorrectCol) return color; - uint8_t w = W(color); - uint8_t r = R(color); - uint8_t g = G(color); - uint8_t b = B(color); - w = gammaT[w]; - r = gammaT[r]; - g = gammaT[g]; - b = gammaT[b]; - return RGBW32(r, g, b, w); -} WS2812FX* WS2812FX::instance = nullptr; //Bus static member definition, would belong in bus_manager.cpp int16_t Bus::_cct = -1; uint8_t Bus::_cctBlend = 0; -uint8_t Bus::_autoWhiteMode = RGBW_MODE_DUAL; +uint8_t Bus::_gAWM = 255; + +const char JSON_mode_names[] PROGMEM = R"=====(["Mode names have moved"])====="; +const char JSON_palette_names[] PROGMEM = R"=====([ +"Default","* Random Cycle","* Color 1","* Colors 1&2","* Color Gradient","* Colors Only","Party","Cloud","Lava","Ocean", +"Forest","Rainbow","Rainbow Bands","Sunset","Rivendell","Breeze","Red & Blue","Yellowout","Analogous","Splash", +"Pastel","Sunset 2","Beech","Vintage","Departure","Landscape","Beach","Sherbet","Hult","Hult 64", +"Drywet","Jul","Grintage","Rewhi","Tertiary","Fire","Icefire","Cyane","Light Pink","Autumn", +"Magenta","Magred","Yelmag","Yelblu","Orange & Teal","Tiamat","April Night","Orangery","C9","Sakura", +"Aurora","Atlantica","C9 2","C9 New","Temperature","Aurora 2","Retro Clown","Candy","Toxy Reaf","Fairy Reaf", +"Semi Blue","Pink Candy","Red Reaf","Aqua Flash","Yelblu Hot","Lite Light","Red Flash","Blink Red","Red Shift","Red Tide", +"Candy2" +])====="; diff --git a/wled00/alexa.cpp b/wled00/alexa.cpp index e41b7e3fb8..dacb46d5a9 100644 --- a/wled00/alexa.cpp +++ b/wled00/alexa.cpp @@ -92,7 +92,7 @@ void onAlexaChange(EspalexaDevice* dev) } else { colorKtoRGB(k, rgbw); } - strip.setColor(0, rgbw[0], rgbw[1], rgbw[2], rgbw[3]); + strip.setColor(0, RGBW32(rgbw[0], rgbw[1], rgbw[2], rgbw[3])); } else { uint32_t color = espalexaDevice->getRGB(); strip.setColor(0, color); diff --git a/wled00/blynk.cpp b/wled00/blynk.cpp index b1619d816e..c8103d8c42 100644 --- a/wled00/blynk.cpp +++ b/wled00/blynk.cpp @@ -1,5 +1,7 @@ #include "wled.h" +#ifndef WLED_DISABLE_BLYNK #include "src/dependencies/blynk/Blynk/BlynkHandlers.h" +#endif /* * Remote light control with the free Blynk app diff --git a/wled00/bus_manager.h b/wled00/bus_manager.h index 612e2fcefd..405c402a3e 100644 --- a/wled00/bus_manager.h +++ b/wled00/bus_manager.h @@ -41,18 +41,19 @@ void colorRGBtoRGBW(byte* rgb); //temporary struct for passing bus configuration to bus struct BusConfig { - uint8_t type = TYPE_WS2812_RGB; + uint8_t type; uint16_t count; uint16_t start; uint8_t colorOrder; bool reversed; uint8_t skipAmount; bool refreshReq; + uint8_t autoWhite; uint8_t pins[5] = {LEDPIN, 255, 255, 255, 255}; - BusConfig(uint8_t busType, uint8_t* ppins, uint16_t pstart, uint16_t len = 1, uint8_t pcolorOrder = COL_ORDER_GRB, bool rev = false, uint8_t skip = 0) { + BusConfig(uint8_t busType, uint8_t* ppins, uint16_t pstart, uint16_t len = 1, uint8_t pcolorOrder = COL_ORDER_GRB, bool rev = false, uint8_t skip = 0, byte aw=RGBW_MODE_MANUAL_ONLY) { refreshReq = (bool) GET_BIT(busType,7); type = busType & 0x7F; // bit 7 may be/is hacked to include refresh info (1=refresh in off state, 0=no refresh) - count = len; start = pstart; colorOrder = pcolorOrder; reversed = rev; skipAmount = skip; + count = len; start = pstart; colorOrder = pcolorOrder; reversed = rev; skipAmount = skip; autoWhite = aw; uint8_t nPins = 1; if (type >= TYPE_NET_DDP_RGB && type < 96) nPins = 4; //virtual network bus. 4 "pins" store IP address else if (type > 47) nPins = 2; @@ -115,10 +116,11 @@ struct ColorOrderMap { inline uint8_t IRAM_ATTR getPixelColorOrder(uint16_t pix, uint8_t defaultColorOrder) const { if (_count == 0) return defaultColorOrder; - + // upper nibble containd W swap information + uint8_t swapW = defaultColorOrder >> 4; for (uint8_t i = 0; i < _count; i++) { if (pix >= _mappings[i].start && pix < (_mappings[i].start + _mappings[i].len)) { - return _mappings[i].colorOrder; + return _mappings[i].colorOrder | (swapW << 4); } } return defaultColorOrder; @@ -132,16 +134,17 @@ struct ColorOrderMap { //parent class of BusDigital, BusPwm, and BusNetwork class Bus { public: - Bus(uint8_t type, uint16_t start) { + Bus(uint8_t type, uint16_t start, uint8_t aw) { _type = type; _start = start; + _autoWhiteMode = Bus::isRgbw(_type) ? aw : RGBW_MODE_MANUAL_ONLY; }; virtual ~Bus() {} //throw the bus under the bus virtual void show() {} virtual bool canShow() { return true; } - virtual void setStatusPixel(uint32_t c) {} + virtual void setStatusPixel(uint32_t c) {} virtual void setPixelColor(uint16_t pix, uint32_t c) {} virtual uint32_t getPixelColor(uint16_t pix) { return 0; } virtual void setBrightness(uint8_t b) {} @@ -164,6 +167,15 @@ class Bus { if (type > TYPE_ONOFF && type <= TYPE_ANALOG_5CH && type != TYPE_ANALOG_3CH) return true; return false; } + virtual bool hasRGB() { + if (_type == TYPE_WS2812_1CH || _type == TYPE_WS2812_WWA || _type == TYPE_ANALOG_1CH || _type == TYPE_ANALOG_2CH || _type == TYPE_ONOFF) return false; + return true; + } + virtual bool hasWhite() { + if (_type == TYPE_SK6812_RGBW || _type == TYPE_TM1814 || _type == TYPE_WS2812_1CH || _type == TYPE_WS2812_WWA || + _type == TYPE_ANALOG_1CH || _type == TYPE_ANALOG_2CH || _type == TYPE_ANALOG_4CH || _type == TYPE_ANALOG_5CH) return true; + return false; + } static void setCCT(uint16_t cct) { _cct = cct; } @@ -175,8 +187,10 @@ class Bus { if (_cctBlend > WLED_MAX_CCT_BLEND) _cctBlend = WLED_MAX_CCT_BLEND; #endif } - inline static void setAutoWhiteMode(uint8_t m) { if (m < 4) _autoWhiteMode = m; } - inline static uint8_t getAutoWhiteMode() { return _autoWhiteMode; } + inline void setAWMode(uint8_t m) { if (m < 4) _autoWhiteMode = m; } + inline uint8_t getAWMode() { return _autoWhiteMode; } + inline static void setAutoWhiteMode(uint8_t m) { if (m < 4) _gAWM = m; else _gAWM = 255; } + inline static uint8_t getAutoWhiteMode() { return _gAWM; } bool reversed = false; @@ -187,20 +201,23 @@ class Bus { uint16_t _len = 1; bool _valid = false; bool _needsRefresh = false; - static uint8_t _autoWhiteMode; - static int16_t _cct; - static uint8_t _cctBlend; + uint8_t _autoWhiteMode; + static uint8_t _gAWM; // definition in FX_fcn.cpp + static int16_t _cct; // definition in FX_fcn.cpp + static uint8_t _cctBlend; // definition in FX_fcn.cpp uint32_t autoWhiteCalc(uint32_t c) { - if (_autoWhiteMode == RGBW_MODE_MANUAL_ONLY) return c; + uint8_t aWM = _autoWhiteMode; + if (_gAWM < 255) aWM = _gAWM; + if (aWM == RGBW_MODE_MANUAL_ONLY) return c; uint8_t w = W(c); //ignore auto-white calculation if w>0 and mode DUAL (DUAL behaves as BRIGHTER if w==0) - if (w > 0 && _autoWhiteMode == RGBW_MODE_DUAL) return c; + if (w > 0 && aWM == RGBW_MODE_DUAL) return c; uint8_t r = R(c); uint8_t g = G(c); uint8_t b = B(c); w = r < g ? (r < b ? r : b) : (g < b ? g : b); - if (_autoWhiteMode == RGBW_MODE_AUTO_ACCURATE) { r -= w; g -= w; b -= w; } //subtract w in ACCURATE mode + if (aWM == RGBW_MODE_AUTO_ACCURATE) { r -= w; g -= w; b -= w; } //subtract w in ACCURATE mode return RGBW32(r, g, b, w); } }; @@ -208,7 +225,7 @@ class Bus { class BusDigital : public Bus { public: - BusDigital(BusConfig &bc, uint8_t nr, const ColorOrderMap &com) : Bus(bc.type, bc.start), _colorOrderMap(com) { + BusDigital(BusConfig &bc, uint8_t nr, const ColorOrderMap &com) : Bus(bc.type, bc.start, bc.autoWhite), _colorOrderMap(com) { if (!IS_DIGITAL(bc.type) || !bc.count) return; if (!pinManager.allocatePin(bc.pins[0], true, PinOwner::BusDigital)) return; _pins[0] = bc.pins[0]; @@ -287,7 +304,8 @@ class BusDigital : public Bus { } void setColorOrder(uint8_t colorOrder) { - if (colorOrder > 5) return; + // upper nibble contains W swap information + if ((colorOrder & 0x0F) > 5) return; _colorOrder = colorOrder; } @@ -325,7 +343,7 @@ class BusDigital : public Bus { class BusPwm : public Bus { public: - BusPwm(BusConfig &bc) : Bus(bc.type, bc.start) { + BusPwm(BusConfig &bc) : Bus(bc.type, bc.start, bc.autoWhite) { _valid = false; if (!IS_PWM(bc.type)) return; uint8_t numPins = NUM_PWM_PINS(bc.type); @@ -385,7 +403,7 @@ class BusPwm : public Bus { else ww = ((255-cct) * 255) / (255 - _cctBlend); if ((255-cct) < _cctBlend) cw = 255; - else cw = (cct * 255) / (255 - _cctBlend); + else cw = (cct * 255) / (255 - _cctBlend); ww = (w * ww) / 255; //brightness scaling cw = (w * cw) / 255; @@ -400,7 +418,6 @@ class BusPwm : public Bus { _data[0] = ww; break; case TYPE_ANALOG_5CH: //RGB + warm white + cold white - // perhaps a non-linear adjustment would be in order. need to test _data[4] = cw; w = ww; case TYPE_ANALOG_4CH: //RGBW @@ -444,7 +461,7 @@ class BusPwm : public Bus { return numPins; } - inline void cleanup() { + void cleanup() { deallocatePins(); } @@ -477,9 +494,74 @@ class BusPwm : public Bus { }; +class BusOnOff : public Bus { + public: + BusOnOff(BusConfig &bc) : Bus(bc.type, bc.start, bc.autoWhite) { + _valid = false; + if (bc.type != TYPE_ONOFF) return; + + uint8_t currentPin = bc.pins[0]; + if (!pinManager.allocatePin(currentPin, true, PinOwner::BusOnOff)) { + deallocatePins(); return; + } + _pin = currentPin; //store only after allocatePin() succeeds + pinMode(_pin, OUTPUT); + reversed = bc.reversed; + _valid = true; + }; + + void setPixelColor(uint16_t pix, uint32_t c) { + if (pix != 0 || !_valid) return; //only react to first pixel + c = autoWhiteCalc(c); + uint8_t r = R(c); + uint8_t g = G(c); + uint8_t b = B(c); + uint8_t w = W(c); + + _data = bool((r+g+b+w) && _bri) ? 0xFF : 0; + } + + uint32_t getPixelColor(uint16_t pix) { + if (!_valid) return 0; + return RGBW32(_data, _data, _data, _data); + } + + void show() { + if (!_valid) return; + digitalWrite(_pin, reversed ? !(bool)_data : (bool)_data); + } + + inline void setBrightness(uint8_t b) { + _bri = b; + } + + uint8_t getPins(uint8_t* pinArray) { + if (!_valid) return 0; + pinArray[0] = _pin; + return 1; + } + + void cleanup() { + deallocatePins(); + } + + ~BusOnOff() { + cleanup(); + } + + private: + uint8_t _pin = 255; + uint8_t _data = 0; + + void deallocatePins() { + pinManager.deallocatePin(_pin, PinOwner::BusOnOff); + } +}; + + class BusNetwork : public Bus { public: - BusNetwork(BusConfig &bc) : Bus(bc.type, bc.start) { + BusNetwork(BusConfig &bc) : Bus(bc.type, bc.start, bc.autoWhite) { _valid = false; // switch (bc.type) { // case TYPE_NET_ARTNET_RGB: @@ -509,6 +591,9 @@ class BusNetwork : public Bus { _valid = true; }; + bool hasRGB() { return true; } + bool hasWhite() { return _rgbw; } + void setPixelColor(uint16_t pix, uint32_t c) { if (!_valid || pix >= _len) return; if (isRgbw()) c = autoWhiteCalc(c); @@ -581,9 +666,7 @@ class BusNetwork : public Bus { class BusManager { public: - BusManager() { - - }; + BusManager() {}; //utility to get the approx. memory usage of a given BusConfig static uint32_t memUsage(BusConfig &bc) { diff --git a/wled00/bus_wrapper.h b/wled00/bus_wrapper.h index bda4de33a7..e278323683 100644 --- a/wled00/bus_wrapper.h +++ b/wled00/bus_wrapper.h @@ -33,6 +33,11 @@ #define I_8266_U1_TM1_4 14 #define I_8266_DM_TM1_4 15 #define I_8266_BB_TM1_4 16 +//TM1829 (RGB) +#define I_8266_U0_TM2_3 39 +#define I_8266_U1_TM2_3 40 +#define I_8266_DM_TM2_3 41 +#define I_8266_BB_TM2_3 42 /*** ESP32 Neopixel methods ***/ //RGB @@ -52,6 +57,11 @@ #define I_32_I0_TM1_4 27 #define I_32_I1_TM1_4 28 //Bit Bang theoratically possible, but very undesirable and not needed (no pin restrictions on RMT and I2S) +//TM1829 (RGB) +#define I_32_RN_TM2_3 43 +#define I_32_I0_TM2_3 44 +#define I_32_I1_TM2_3 45 +//Bit Bang theoratically possible, but very undesirable and not needed (no pin restrictions on RMT and I2S) //APA102 #define I_HS_DOT_3 29 //hardware SPI @@ -96,6 +106,11 @@ #define B_8266_U1_TM1_4 NeoPixelBrightnessBus #define B_8266_DM_TM1_4 NeoPixelBrightnessBus #define B_8266_BB_TM1_4 NeoPixelBrightnessBus +//TM1829 (RGB) +#define B_8266_U0_TM2_4 NeoPixelBrightnessBus +#define B_8266_U1_TM2_4 NeoPixelBrightnessBus +#define B_8266_DM_TM2_4 NeoPixelBrightnessBus +#define B_8266_BB_TM2_4 NeoPixelBrightnessBus #endif /*** ESP32 Neopixel methods ***/ @@ -133,6 +148,15 @@ #define B_32_I1_TM1_4 NeoPixelBrightnessBus #endif //Bit Bang theoratically possible, but very undesirable and not needed (no pin restrictions on RMT and I2S) +//TM1829 (RGB) +#define B_32_RN_TM2_3 NeoPixelBrightnessBus +#ifndef CONFIG_IDF_TARGET_ESP32C3 +#define B_32_I0_TM2_3 NeoPixelBrightnessBus +#endif +#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3) +#define B_32_I1_TM2_3 NeoPixelBrightnessBus +#endif +//Bit Bang theoratically possible, but very undesirable and not needed (no pin restrictions on RMT and I2S) #endif @@ -149,10 +173,21 @@ #define B_SS_LPO_3 NeoPixelBrightnessBus //WS2801 -//#define B_HS_WS1_3 NeoPixelBrightnessBus -//#define B_HS_WS1_3 NeoPixelBrightnessBus -//#define B_HS_WS1_3 NeoPixelBrightnessBus // 10MHz -#define B_HS_WS1_3 NeoPixelBrightnessBus //slower, more compatible +#if defined(WLED_WS2801_SPEED_KHZ) && WLED_WS2801_SPEED_KHZ==40000 +#define B_HS_WS1_3 NeoPixelBrightnessBus // fastest bus speed (not existing method?) +#elif defined(WLED_WS2801_SPEED_KHZ) && WLED_WS2801_SPEED_KHZ==20000 +#define B_HS_WS1_3 NeoPixelBrightnessBus // 20MHz +#elif defined(WLED_WS2801_SPEED_KHZ) && WLED_WS2801_SPEED_KHZ==10000 +#define B_HS_WS1_3 NeoPixelBrightnessBus // 10MHz +#elif defined(WLED_WS2801_SPEED_KHZ) && WLED_WS2801_SPEED_KHZ==2000 +#define B_HS_WS1_3 NeoPixelBrightnessBus //slower, more compatible +#elif defined(WLED_WS2801_SPEED_KHZ) && WLED_WS2801_SPEED_KHZ==1000 +#define B_HS_WS1_3 NeoPixelBrightnessBus //slower, more compatible +#elif defined(WLED_WS2801_SPEED_KHZ) && WLED_WS2801_SPEED_KHZ==500 +#define B_HS_WS1_3 NeoPixelBrightnessBus //slower, more compatible +#else +#define B_HS_WS1_3 NeoPixelBrightnessBus // 2MHz; slower, more compatible +#endif #define B_SS_WS1_3 NeoPixelBrightnessBus //P9813 @@ -190,6 +225,10 @@ class PolyBus { case I_8266_U1_TM1_4: beginTM1814(busPtr); break; case I_8266_DM_TM1_4: beginTM1814(busPtr); break; case I_8266_BB_TM1_4: beginTM1814(busPtr); break; + case I_8266_U0_TM2_3: (static_cast(busPtr))->Begin(); break; + case I_8266_U1_TM2_3: (static_cast(busPtr))->Begin(); break; + case I_8266_DM_TM2_3: (static_cast(busPtr))->Begin(); break; + case I_8266_BB_TM2_3: (static_cast(busPtr))->Begin(); break; case I_HS_DOT_3: (static_cast(busPtr))->Begin(); break; case I_HS_LPD_3: (static_cast(busPtr))->Begin(); break; case I_HS_LPO_3: (static_cast(busPtr))->Begin(); break; @@ -219,11 +258,14 @@ class PolyBus { case I_32_I1_400_3: (static_cast(busPtr))->Begin(); break; #endif case I_32_RN_TM1_4: beginTM1814(busPtr); break; + case I_32_RN_TM2_3: (static_cast(busPtr))->Begin(); break; #ifndef CONFIG_IDF_TARGET_ESP32C3 case I_32_I0_TM1_4: beginTM1814(busPtr); break; + case I_32_I0_TM2_3: (static_cast(busPtr))->Begin(); break; #endif #if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3) case I_32_I1_TM1_4: beginTM1814(busPtr); break; + case I_32_I1_TM2_3: (static_cast(busPtr))->Begin(); break; #endif // ESP32 can (and should, to avoid inadvertantly driving the chip select signal) specify the pins used for SPI, but only in begin() case I_HS_DOT_3: (static_cast(busPtr))->Begin(pins[1], -1, pins[0], -1); break; @@ -260,6 +302,10 @@ class PolyBus { case I_8266_U1_TM1_4: busPtr = new B_8266_U1_TM1_4(len, pins[0]); break; case I_8266_DM_TM1_4: busPtr = new B_8266_DM_TM1_4(len, pins[0]); break; case I_8266_BB_TM1_4: busPtr = new B_8266_BB_TM1_4(len, pins[0]); break; + case I_8266_U0_TM2_3: busPtr = new B_8266_U0_TM2_4(len, pins[0]); break; + case I_8266_U1_TM2_3: busPtr = new B_8266_U1_TM2_4(len, pins[0]); break; + case I_8266_DM_TM2_3: busPtr = new B_8266_DM_TM2_4(len, pins[0]); break; + case I_8266_BB_TM2_3: busPtr = new B_8266_BB_TM2_4(len, pins[0]); break; #endif #ifdef ARDUINO_ARCH_ESP32 case I_32_RN_NEO_3: busPtr = new B_32_RN_NEO_3(len, pins[0], (NeoBusChannel)channel); break; @@ -284,11 +330,14 @@ class PolyBus { case I_32_I1_400_3: busPtr = new B_32_I1_400_3(len, pins[0]); break; #endif case I_32_RN_TM1_4: busPtr = new B_32_RN_TM1_4(len, pins[0], (NeoBusChannel)channel); break; + case I_32_RN_TM2_3: busPtr = new B_32_RN_TM2_3(len, pins[0], (NeoBusChannel)channel); break; #ifndef CONFIG_IDF_TARGET_ESP32C3 case I_32_I0_TM1_4: busPtr = new B_32_I0_TM1_4(len, pins[0]); break; + case I_32_I0_TM2_3: busPtr = new B_32_I0_TM2_3(len, pins[0]); break; #endif #if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3) case I_32_I1_TM1_4: busPtr = new B_32_I1_TM1_4(len, pins[0]); break; + case I_32_I1_TM2_3: busPtr = new B_32_I1_TM2_3(len, pins[0]); break; #endif #endif // for 2-wire: pins[1] is clk, pins[0] is dat. begin expects (len, clk, dat) @@ -326,6 +375,10 @@ class PolyBus { case I_8266_U1_TM1_4: (static_cast(busPtr))->Show(); break; case I_8266_DM_TM1_4: (static_cast(busPtr))->Show(); break; case I_8266_BB_TM1_4: (static_cast(busPtr))->Show(); break; + case I_8266_U0_TM2_3: (static_cast(busPtr))->Show(); break; + case I_8266_U1_TM2_3: (static_cast(busPtr))->Show(); break; + case I_8266_DM_TM2_3: (static_cast(busPtr))->Show(); break; + case I_8266_BB_TM2_3: (static_cast(busPtr))->Show(); break; #endif #ifdef ARDUINO_ARCH_ESP32 case I_32_RN_NEO_3: (static_cast(busPtr))->Show(); break; @@ -350,11 +403,14 @@ class PolyBus { case I_32_I1_400_3: (static_cast(busPtr))->Show(); break; #endif case I_32_RN_TM1_4: (static_cast(busPtr))->Show(); break; + case I_32_RN_TM2_3: (static_cast(busPtr))->Show(); break; #ifndef CONFIG_IDF_TARGET_ESP32C3 case I_32_I0_TM1_4: (static_cast(busPtr))->Show(); break; + case I_32_I0_TM2_3: (static_cast(busPtr))->Show(); break; #endif #if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3) case I_32_I1_TM1_4: (static_cast(busPtr))->Show(); break; + case I_32_I1_TM2_3: (static_cast(busPtr))->Show(); break; #endif #endif case I_HS_DOT_3: (static_cast(busPtr))->Show(); break; @@ -389,6 +445,10 @@ class PolyBus { case I_8266_U1_TM1_4: return (static_cast(busPtr))->CanShow(); break; case I_8266_DM_TM1_4: return (static_cast(busPtr))->CanShow(); break; case I_8266_BB_TM1_4: return (static_cast(busPtr))->CanShow(); break; + case I_8266_U0_TM2_3: return (static_cast(busPtr))->CanShow(); break; + case I_8266_U1_TM2_3: return (static_cast(busPtr))->CanShow(); break; + case I_8266_DM_TM2_3: return (static_cast(busPtr))->CanShow(); break; + case I_8266_BB_TM2_3: return (static_cast(busPtr))->CanShow(); break; #endif #ifdef ARDUINO_ARCH_ESP32 case I_32_RN_NEO_3: return (static_cast(busPtr))->CanShow(); break; @@ -413,11 +473,14 @@ class PolyBus { case I_32_I1_400_3: return (static_cast(busPtr))->CanShow(); break; #endif case I_32_RN_TM1_4: return (static_cast(busPtr))->CanShow(); break; + case I_32_RN_TM2_3: return (static_cast(busPtr))->CanShow(); break; #ifndef CONFIG_IDF_TARGET_ESP32C3 case I_32_I0_TM1_4: return (static_cast(busPtr))->CanShow(); break; + case I_32_I0_TM2_3: return (static_cast(busPtr))->CanShow(); break; #endif #if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3) case I_32_I1_TM1_4: return (static_cast(busPtr))->CanShow(); break; + case I_32_I1_TM2_3: return (static_cast(busPtr))->CanShow(); break; #endif #endif case I_HS_DOT_3: return (static_cast(busPtr))->CanShow(); break; @@ -440,22 +503,22 @@ class PolyBus { uint8_t w = c >> 24; RgbwColor col; - //TODO make color order override possible on a per-strip basis - #ifdef COLOR_ORDER_OVERRIDE - if (pix >= COO_MIN && pix < COO_MAX) co = COO_ORDER; - #endif - - //reorder channels to selected order - switch (co) - { - case 0: col.G = g; col.R = r; col.B = b; break; //0 = GRB, default + // reorder channels to selected order + switch (co & 0x0F) { + default: col.G = g; col.R = r; col.B = b; break; //0 = GRB, default case 1: col.G = r; col.R = g; col.B = b; break; //1 = RGB, common for WS2811 case 2: col.G = b; col.R = r; col.B = g; break; //2 = BRG case 3: col.G = r; col.R = b; col.B = g; break; //3 = RBG case 4: col.G = b; col.R = g; col.B = r; break; //4 = BGR - default: col.G = g; col.R = b; col.B = r; break; //5 = GBR + case 5: col.G = g; col.R = b; col.B = r; break; //5 = GBR + } + // upper nibble contains W swap information + switch (co >> 4) { + default: col.W = w; break; // no swapping + case 1: col.W = col.B; col.B = w; break; // swap W & B + case 2: col.W = col.G; col.G = w; break; // swap W & G + case 3: col.W = col.R; col.R = w; break; // swap W & R } - col.W = w; switch (busType) { case I_NONE: break; @@ -476,6 +539,10 @@ class PolyBus { case I_8266_U1_TM1_4: (static_cast(busPtr))->SetPixelColor(pix, col); break; case I_8266_DM_TM1_4: (static_cast(busPtr))->SetPixelColor(pix, col); break; case I_8266_BB_TM1_4: (static_cast(busPtr))->SetPixelColor(pix, col); break; + case I_8266_U0_TM2_3: (static_cast(busPtr))->SetPixelColor(pix, RgbColor(col.R,col.G,col.B)); break; + case I_8266_U1_TM2_3: (static_cast(busPtr))->SetPixelColor(pix, RgbColor(col.R,col.G,col.B)); break; + case I_8266_DM_TM2_3: (static_cast(busPtr))->SetPixelColor(pix, RgbColor(col.R,col.G,col.B)); break; + case I_8266_BB_TM2_3: (static_cast(busPtr))->SetPixelColor(pix, RgbColor(col.R,col.G,col.B)); break; #endif #ifdef ARDUINO_ARCH_ESP32 case I_32_RN_NEO_3: (static_cast(busPtr))->SetPixelColor(pix, RgbColor(col.R,col.G,col.B)); break; @@ -500,11 +567,14 @@ class PolyBus { case I_32_I1_400_3: (static_cast(busPtr))->SetPixelColor(pix, RgbColor(col.R,col.G,col.B)); break; #endif case I_32_RN_TM1_4: (static_cast(busPtr))->SetPixelColor(pix, col); break; + case I_32_RN_TM2_3: (static_cast(busPtr))->SetPixelColor(pix, RgbColor(col.R,col.G,col.B)); break; #ifndef CONFIG_IDF_TARGET_ESP32C3 case I_32_I0_TM1_4: (static_cast(busPtr))->SetPixelColor(pix, col); break; + case I_32_I0_TM2_3: (static_cast(busPtr))->SetPixelColor(pix, RgbColor(col.R,col.G,col.B)); break; #endif #if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3) case I_32_I1_TM1_4: (static_cast(busPtr))->SetPixelColor(pix, col); break; + case I_32_I1_TM2_3: (static_cast(busPtr))->SetPixelColor(pix, RgbColor(col.R,col.G,col.B)); break; #endif #endif case I_HS_DOT_3: (static_cast(busPtr))->SetPixelColor(pix, RgbColor(col.R,col.G,col.B)); break; @@ -539,6 +609,10 @@ class PolyBus { case I_8266_U1_TM1_4: (static_cast(busPtr))->SetBrightness(b); break; case I_8266_DM_TM1_4: (static_cast(busPtr))->SetBrightness(b); break; case I_8266_BB_TM1_4: (static_cast(busPtr))->SetBrightness(b); break; + case I_8266_U0_TM2_3: (static_cast(busPtr))->SetBrightness(b); break; + case I_8266_U1_TM2_3: (static_cast(busPtr))->SetBrightness(b); break; + case I_8266_DM_TM2_3: (static_cast(busPtr))->SetBrightness(b); break; + case I_8266_BB_TM2_3: (static_cast(busPtr))->SetBrightness(b); break; #endif #ifdef ARDUINO_ARCH_ESP32 case I_32_RN_NEO_3: (static_cast(busPtr))->SetBrightness(b); break; @@ -563,11 +637,14 @@ class PolyBus { case I_32_I1_400_3: (static_cast(busPtr))->SetBrightness(b); break; #endif case I_32_RN_TM1_4: (static_cast(busPtr))->SetBrightness(b); break; + case I_32_RN_TM2_3: (static_cast(busPtr))->SetBrightness(b); break; #ifndef CONFIG_IDF_TARGET_ESP32C3 case I_32_I0_TM1_4: (static_cast(busPtr))->SetBrightness(b); break; + case I_32_I0_TM2_3: (static_cast(busPtr))->SetBrightness(b); break; #endif #if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3) case I_32_I1_TM1_4: (static_cast(busPtr))->SetBrightness(b); break; + case I_32_I1_TM2_3: (static_cast(busPtr))->SetBrightness(b); break; #endif #endif case I_HS_DOT_3: (static_cast(busPtr))->SetBrightness(b); break; @@ -603,6 +680,10 @@ class PolyBus { case I_8266_U1_TM1_4: col = (static_cast(busPtr))->GetPixelColor(pix); break; case I_8266_DM_TM1_4: col = (static_cast(busPtr))->GetPixelColor(pix); break; case I_8266_BB_TM1_4: col = (static_cast(busPtr))->GetPixelColor(pix); break; + case I_8266_U0_TM2_3: col = (static_cast(busPtr))->GetPixelColor(pix); break; + case I_8266_U1_TM2_3: col = (static_cast(busPtr))->GetPixelColor(pix); break; + case I_8266_DM_TM2_3: col = (static_cast(busPtr))->GetPixelColor(pix); break; + case I_8266_BB_TM2_3: col = (static_cast(busPtr))->GetPixelColor(pix); break; #endif #ifdef ARDUINO_ARCH_ESP32 case I_32_RN_NEO_3: col = (static_cast(busPtr))->GetPixelColor(pix); break; @@ -627,11 +708,14 @@ class PolyBus { case I_32_I1_400_3: col = (static_cast(busPtr))->GetPixelColor(pix); break; #endif case I_32_RN_TM1_4: col = (static_cast(busPtr))->GetPixelColor(pix); break; + case I_32_RN_TM2_3: col = (static_cast(busPtr))->GetPixelColor(pix); break; #ifndef CONFIG_IDF_TARGET_ESP32C3 case I_32_I0_TM1_4: col = (static_cast(busPtr))->GetPixelColor(pix); break; + case I_32_I0_TM2_3: col = (static_cast(busPtr))->GetPixelColor(pix); break; #endif #if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3) case I_32_I1_TM1_4: col = (static_cast(busPtr))->GetPixelColor(pix); break; + case I_32_I1_TM2_3: col = (static_cast(busPtr))->GetPixelColor(pix); break; #endif #endif case I_HS_DOT_3: col = (static_cast(busPtr))->GetPixelColor(pix); break; @@ -646,14 +730,16 @@ class PolyBus { case I_SS_P98_3: col = (static_cast(busPtr))->GetPixelColor(pix); break; } - #ifdef COLOR_ORDER_OVERRIDE - if (pix >= COO_MIN && pix < COO_MAX) co = COO_ORDER; - #endif - - switch (co) - { + // upper nibble contains W swap information + uint8_t w = col.W; + switch (co >> 4) { + case 1: col.W = col.B; col.B = w; break; // swap W & B + case 2: col.W = col.G; col.G = w; break; // swap W & G + case 3: col.W = col.R; col.R = w; break; // swap W & R + } + switch (co & 0x0F) { // W G R B - case 0: return ((col.W << 24) | (col.G << 8) | (col.R << 16) | (col.B)); //0 = GRB, default + default: return ((col.W << 24) | (col.G << 8) | (col.R << 16) | (col.B)); //0 = GRB, default case 1: return ((col.W << 24) | (col.R << 8) | (col.G << 16) | (col.B)); //1 = RGB, common for WS2811 case 2: return ((col.W << 24) | (col.B << 8) | (col.R << 16) | (col.G)); //2 = BRG case 3: return ((col.W << 24) | (col.B << 8) | (col.G << 16) | (col.R)); //3 = RBG @@ -684,6 +770,10 @@ class PolyBus { case I_8266_U1_TM1_4: delete (static_cast(busPtr)); break; case I_8266_DM_TM1_4: delete (static_cast(busPtr)); break; case I_8266_BB_TM1_4: delete (static_cast(busPtr)); break; + case I_8266_U0_TM2_3: delete (static_cast(busPtr)); break; + case I_8266_U1_TM2_3: delete (static_cast(busPtr)); break; + case I_8266_DM_TM2_3: delete (static_cast(busPtr)); break; + case I_8266_BB_TM2_3: delete (static_cast(busPtr)); break; #endif #ifdef ARDUINO_ARCH_ESP32 case I_32_RN_NEO_3: delete (static_cast(busPtr)); break; @@ -708,11 +798,14 @@ class PolyBus { case I_32_I1_400_3: delete (static_cast(busPtr)); break; #endif case I_32_RN_TM1_4: delete (static_cast(busPtr)); break; + case I_32_RN_TM2_3: delete (static_cast(busPtr)); break; #ifndef CONFIG_IDF_TARGET_ESP32C3 case I_32_I0_TM1_4: delete (static_cast(busPtr)); break; + case I_32_I0_TM2_3: delete (static_cast(busPtr)); break; #endif #if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3) case I_32_I1_TM1_4: delete (static_cast(busPtr)); break; + case I_32_I1_TM2_3: delete (static_cast(busPtr)); break; #endif #endif case I_HS_DOT_3: delete (static_cast(busPtr)); break; @@ -736,7 +829,10 @@ class PolyBus { #ifdef ESP8266 if (pins[0] == P_8266_HS_MOSI && pins[1] == P_8266_HS_CLK) isHSPI = true; #else - if(!num) isHSPI = true; // temporary hack to limit use of hardware SPI to a single SPI peripheral: only allow ESP32 hardware serial on segment 0 + // temporary hack to limit use of hardware SPI to a single SPI peripheral: only allow ESP32 hardware serial on segment 0 + if (!num) isHSPI = true; + //if (num==0 && pins[0] == P_32_VS_MOSI && pins[1] == P_32_VS_CLK) isHSPI = true; // no nultiplexing, up to 80MHz clock + //if (num==1 && pins[0] == P_32_HS_MOSI && pins[1] == P_32_HS_CLK) isHSPI = true; #endif uint8_t t = I_NONE; switch (busType) { @@ -763,6 +859,8 @@ class PolyBus { return I_8266_U0_400_3 + offset; case TYPE_TM1814: return I_8266_U0_TM1_4 + offset; + case TYPE_TM1829: + return I_8266_U0_TM2_3 + offset; } #else //ESP32 uint8_t offset = 0; //0 = RMT (num 0-7) 8 = I2S0 9 = I2S1 @@ -783,6 +881,8 @@ class PolyBus { return I_32_RN_400_3 + offset; case TYPE_TM1814: return I_32_RN_TM1_4 + offset; + case TYPE_TM1829: + return I_32_RN_TM2_3 + offset; } #endif } diff --git a/wled00/button.cpp b/wled00/button.cpp index 2c433a3447..b472f9275f 100644 --- a/wled00/button.cpp +++ b/wled00/button.cpp @@ -18,7 +18,7 @@ void shortPressAction(uint8_t b) if (!macroButton[b]) { switch (b) { case 0: toggleOnOff(); stateUpdated(CALL_MODE_BUTTON); break; - case 1: ++effectCurrent %= strip.getModeCount(); colorUpdated(CALL_MODE_BUTTON); break; + case 1: ++effectCurrent %= strip.getModeCount(); stateChanged = true; colorUpdated(CALL_MODE_BUTTON); break; } } else { applyPreset(macroButton[b], CALL_MODE_BUTTON_PRESET); @@ -195,11 +195,11 @@ void handleAnalog(uint8_t b) colorHStoRGB(aRead*256,255,col); } else { // otherwise use "double press" for segment selection - WS2812FX::Segment& seg = strip.getSegment(macroDoublePress[b]); + Segment& seg = strip.getSegment(macroDoublePress[b]); if (aRead == 0) { seg.setOption(SEG_OPTION_ON, 0); // off } else { - seg.setOpacity(aRead, macroDoublePress[b]); + seg.setOpacity(aRead); seg.setOption(SEG_OPTION_ON, 1); } // this will notify clients of update (websockets,mqtt,etc) @@ -216,9 +216,13 @@ void handleAnalog(uint8_t b) void handleButton() { static unsigned long lastRead = 0UL; + static unsigned long lastRun = 0UL; bool analog = false; + unsigned long now = millis(); - if (strip.isUpdating()) return; // don't interfere with strip updates. Our button will still be there in 1ms (next cycle) + //if (strip.isUpdating()) return; // don't interfere with strip updates. Our button will still be there in 1ms (next cycle) + if (strip.isUpdating() && (millis() - lastRun < 400)) return; // be niced, but avoid button starvation + lastRun = millis(); for (uint8_t b=0; b ANALOG_BTN_READ_CYCLE) { // button is not a button but a potentiometer + if ((buttonType[b] == BTN_TYPE_ANALOG || buttonType[b] == BTN_TYPE_ANALOG_INVERTED) && now - lastRead > ANALOG_BTN_READ_CYCLE) { // button is not a button but a potentiometer analog = true; handleAnalog(b); continue; } @@ -242,21 +246,21 @@ void handleButton() //momentary button logic if (isButtonPressed(b)) { //pressed - if (!buttonPressedBefore[b]) buttonPressedTime[b] = millis(); + if (!buttonPressedBefore[b]) buttonPressedTime[b] = now; buttonPressedBefore[b] = true; - if (millis() - buttonPressedTime[b] > WLED_LONG_PRESS) { //long press + if (now - buttonPressedTime[b] > WLED_LONG_PRESS) { //long press if (!buttonLongPressed[b]) longPressAction(b); else if (b) { //repeatable action (~3 times per s) on button > 0 longPressAction(b); - buttonPressedTime[b] = millis() - WLED_LONG_REPEATED_ACTION; //300ms + buttonPressedTime[b] = now - WLED_LONG_REPEATED_ACTION; //333ms } buttonLongPressed[b] = true; } } else if (!isButtonPressed(b) && buttonPressedBefore[b]) { //released - long dur = millis() - buttonPressedTime[b]; + long dur = now - buttonPressedTime[b]; if (dur < WLED_DEBOUNCE_THRESHOLD) {buttonPressedBefore[b] = false; continue;} //too short "press", debounce bool doublePress = buttonWaitTime[b]; //did we have a short press before? buttonWaitTime[b] = 0; @@ -264,19 +268,22 @@ void handleButton() if (b == 0 && dur > WLED_LONG_AP) { // long press on button 0 (when released) if (dur > WLED_LONG_FACTORY_RESET) { // factory reset if pressed > 10 seconds WLED_FS.format(); + #ifdef WLED_ADD_EEPROM_SUPPORT clearEEPROM(); + #endif doReboot = true; } else { WLED::instance().initAP(true); } } else if (!buttonLongPressed[b]) { //short press + //NOTE: this interferes with double click handling in usermods so usermod needs to implement full button handling if (b != 1 && !macroDoublePress[b]) { //don't wait for double press on buttons without a default action if no double press macro set shortPressAction(b); } else { //double press if less than 350 ms between current press and previous short press release (buttonWaitTime!=0) if (doublePress) { doublePressAction(b); } else { - buttonWaitTime[b] = millis(); + buttonWaitTime[b] = now; } } } @@ -285,12 +292,12 @@ void handleButton() } //if 350ms elapsed since last short press release it is a short press - if (buttonWaitTime[b] && millis() - buttonWaitTime[b] > WLED_DOUBLE_PRESS && !buttonPressedBefore[b]) { + if (buttonWaitTime[b] && now - buttonWaitTime[b] > WLED_DOUBLE_PRESS && !buttonPressedBefore[b]) { buttonWaitTime[b] = 0; shortPressAction(b); } } - if (analog) lastRead = millis(); + if (analog) lastRead = now; } void handleIO() diff --git a/wled00/cfg.cpp b/wled00/cfg.cpp index f50181f6de..6ee4a191ea 100644 --- a/wled00/cfg.cpp +++ b/wled00/cfg.cpp @@ -31,6 +31,9 @@ bool deserializeConfig(JsonObject doc, bool fromFS) { getStringFromJson(cmDNS, id[F("mdns")], 33); getStringFromJson(serverDescription, id[F("name")], 33); getStringFromJson(alexaInvocationName, id[F("inv")], 33); +#ifdef WLED_ENABLE_SIMPLE_UI + CJSON(simplifiedUI, id[F("sui")]); +#endif JsonObject nw_ins_0 = doc["nw"]["ins"][0]; getStringFromJson(clientSSID, nw_ins_0[F("ssid")], 33); @@ -78,16 +81,53 @@ bool deserializeConfig(JsonObject doc, bool fromFS) { // initialize LED pins and lengths prior to other HW (except for ethernet) JsonObject hw_led = hw["led"]; + uint8_t autoWhiteMode = RGBW_MODE_MANUAL_ONLY; CJSON(strip.ablMilliampsMax, hw_led[F("maxpwr")]); CJSON(strip.milliampsPerLed, hw_led[F("ledma")]); - CJSON(strip.autoWhiteMode, hw_led[F("rgbwm")]); - Bus::setAutoWhiteMode(strip.autoWhiteMode); - strip.fixInvalidSegments(); // refreshes segment light capabilities (in case auto white mode changed) + Bus::setAutoWhiteMode(hw_led[F("rgbwm")] | 255); CJSON(correctWB, hw_led["cct"]); CJSON(cctFromRgb, hw_led[F("cr")]); CJSON(strip.cctBlending, hw_led[F("cb")]); Bus::setCCTBlend(strip.cctBlending); strip.setTargetFps(hw_led["fps"]); //NOP if 0, default 42 FPS + CJSON(strip.useLedsArray, hw_led[F("ld")]); + + #ifndef WLED_DISABLE_2D + // 2D Matrix Settings + JsonObject matrix = hw_led[F("matrix")]; + if (!matrix.isNull()) { + strip.isMatrix = true; + CJSON(strip.panelH, matrix[F("ph")]); + CJSON(strip.panelW, matrix[F("pw")]); + CJSON(strip.hPanels, matrix[F("mph")]); + CJSON(strip.vPanels, matrix[F("mpv")]); + CJSON(strip.matrix.bottomStart, matrix[F("pb")]); + CJSON(strip.matrix.rightStart, matrix[F("pr")]); + CJSON(strip.matrix.vertical, matrix[F("pv")]); + CJSON(strip.matrix.serpentine, matrix[F("ps")]); + + JsonArray panels = matrix[F("panels")]; + uint8_t s = 0; + if (!panels.isNull()) { + for (JsonObject pnl : panels) { + CJSON(strip.panel[s].bottomStart, pnl["b"]); + CJSON(strip.panel[s].rightStart, pnl["r"]); + CJSON(strip.panel[s].vertical, pnl["v"]); + CJSON(strip.panel[s].serpentine, pnl["s"]); + if (++s >= WLED_MAX_PANELS) break; // max panels reached + } + } + // clear remaining panels + for (; s 15000) serialBaud = 1152; updateBaudRate(serialBaud *100); + JsonArray hw_if_i2c = hw[F("if")][F("i2c-pin")]; + CJSON(i2c_sda, hw_if_i2c[0]); + CJSON(i2c_scl, hw_if_i2c[1]); + PinManagerPinType i2c[2] = { { i2c_sda, true }, { i2c_scl, true } }; + if (i2c_scl >= 0 && i2c_sda >= 0 && pinManager.allocateMultiplePins(i2c, 2, PinOwner::HW_I2C)) { + #ifdef ESP32 + Wire.setPins(i2c_sda, i2c_scl); // this will fail if Wire is initilised (Wire.begin() called prior) + #endif + Wire.begin(); + } else { + i2c_sda = -1; + i2c_scl = -1; + } + JsonArray hw_if_spi = hw[F("if")][F("spi-pin")]; + CJSON(spi_mosi, hw_if_spi[0]); + CJSON(spi_sclk, hw_if_spi[1]); + PinManagerPinType spi[3] = { { spi_mosi, true }, { spi_sclk, true } }; + if (spi_mosi >= 0 && spi_sclk >= 0 && pinManager.allocateMultiplePins(spi, 2, PinOwner::HW_SPI)) { + // do not initialise bus here + } else { + spi_mosi = -1; + spi_sclk = -1; + } + //int hw_status_pin = hw[F("status")]["pin"]; // -1 JsonObject light = doc[F("light")]; @@ -467,21 +532,21 @@ bool deserializeConfig(JsonObject doc, bool fromFS) { void deserializeConfigFromFS() { bool success = deserializeConfigSec(); if (!success) { //if file does not exist, try reading from EEPROM + #ifdef WLED_ADD_EEPROM_SUPPORT deEEPSettings(); + #endif return; } - #ifdef WLED_USE_DYNAMIC_JSON - DynamicJsonDocument doc(JSON_BUFFER_SIZE); - #else if (!requestJSONBufferLock(1)) return; - #endif DEBUG_PRINTLN(F("Reading settings from /cfg.json...")); success = readObjectFromFile("/cfg.json", nullptr, &doc); if (!success) { //if file does not exist, try reading from EEPROM + #ifdef WLED_ADD_EEPROM_SUPPORT deEEPSettings(); + #endif releaseJSONBufferLock(); return; } @@ -499,11 +564,7 @@ void serializeConfig() { DEBUG_PRINTLN(F("Writing settings to /cfg.json...")); - #ifdef WLED_USE_DYNAMIC_JSON - DynamicJsonDocument doc(JSON_BUFFER_SIZE); - #else if (!requestJSONBufferLock(2)) return; - #endif JsonArray rev = doc.createNestedArray("rev"); rev.add(1); //major settings revision @@ -515,6 +576,9 @@ void serializeConfig() { id[F("mdns")] = cmDNS; id[F("name")] = serverDescription; id[F("inv")] = alexaInvocationName; +#ifdef WLED_ENABLE_SIMPLE_UI + id[F("sui")] = simplifiedUI; +#endif JsonObject nw = doc.createNestedObject("nw"); @@ -585,7 +649,32 @@ void serializeConfig() { hw_led[F("cr")] = cctFromRgb; hw_led[F("cb")] = strip.cctBlending; hw_led["fps"] = strip.getTargetFps(); - hw_led[F("rgbwm")] = strip.autoWhiteMode; + hw_led[F("rgbwm")] = Bus::getAutoWhiteMode(); // global override + hw_led[F("ld")] = strip.useLedsArray; + + #ifndef WLED_DISABLE_2D + // 2D Matrix Settings + if (strip.isMatrix) { + JsonObject matrix = hw_led.createNestedObject(F("matrix")); + matrix[F("ph")] = strip.panelH; + matrix[F("pw")] = strip.panelW; + matrix[F("mph")] = strip.hPanels; + matrix[F("mpv")] = strip.vPanels; + matrix[F("pb")] = strip.matrix.bottomStart; + matrix[F("pr")] = strip.matrix.rightStart; + matrix[F("pv")] = strip.matrix.vertical; + matrix[F("ps")] = strip.matrix.serpentine; + + JsonArray panels = matrix.createNestedArray(F("panels")); + for (uint8_t i=0; iskippedLeds(); ins["type"] = bus->getType() & 0x7F; ins["ref"] = bus->isOffRefreshRequired(); - //ins[F("rgbw")] = bus->isRgbw(); + ins[F("rgbwm")] = bus->getAWMode(); } JsonArray hw_com = hw.createNestedArray(F("com")); @@ -650,6 +739,14 @@ void serializeConfig() { hw[F("baud")] = serialBaud; + JsonObject hw_if = hw.createNestedObject(F("if")); + JsonArray hw_if_i2c = hw_if.createNestedArray("i2c-pin"); + hw_if_i2c.add(i2c_sda); + hw_if_i2c.add(i2c_scl); + JsonArray hw_if_spi = hw_if.createNestedArray("spi-pin"); + hw_if_spi.add(spi_mosi); + hw_if_spi.add(spi_sclk); + //JsonObject hw_status = hw.createNestedObject("status"); //hw_status["pin"] = -1; @@ -848,11 +945,7 @@ void serializeConfig() { bool deserializeConfigSec() { DEBUG_PRINTLN(F("Reading settings from /wsec.json...")); - #ifdef WLED_USE_DYNAMIC_JSON - DynamicJsonDocument doc(JSON_BUFFER_SIZE); - #else if (!requestJSONBufferLock(3)) return false; - #endif bool success = readObjectFromFile("/wsec.json", nullptr, &doc); if (!success) { @@ -884,6 +977,9 @@ bool deserializeConfigSec() { getStringFromJson(hueApiKey, interfaces["hue"][F("key")], 47); #endif + getStringFromJson(settingsPIN, doc["pin"], 5); + correctPIN = !strlen(settingsPIN); + JsonObject ota = doc["ota"]; getStringFromJson(otaPass, ota[F("pwd")], 33); CJSON(otaLock, ota[F("lock")]); @@ -897,11 +993,7 @@ bool deserializeConfigSec() { void serializeConfigSec() { DEBUG_PRINTLN(F("Writing settings to /wsec.json...")); - #ifdef WLED_USE_DYNAMIC_JSON - DynamicJsonDocument doc(JSON_BUFFER_SIZE); - #else if (!requestJSONBufferLock(4)) return; - #endif JsonObject nw = doc.createNestedObject("nw"); @@ -927,6 +1019,8 @@ void serializeConfigSec() { if_hue[F("key")] = hueApiKey; #endif + doc["pin"] = settingsPIN; + JsonObject ota = doc.createNestedObject("ota"); ota[F("pwd")] = otaPass; ota[F("lock")] = otaLock; diff --git a/wled00/colors.cpp b/wled00/colors.cpp index 25cce032ef..c42c9076be 100644 --- a/wled00/colors.cpp +++ b/wled00/colors.cpp @@ -1,12 +1,57 @@ #include "wled.h" /* - * Color conversion methods + * Color conversion & utility methods */ +/* + * color blend function + */ +uint32_t color_blend(uint32_t color1, uint32_t color2, uint16_t blend, bool b16) { + if(blend == 0) return color1; + uint16_t blendmax = b16 ? 0xFFFF : 0xFF; + if(blend == blendmax) return color2; + uint8_t shift = b16 ? 16 : 8; + + uint32_t w1 = W(color1); + uint32_t r1 = R(color1); + uint32_t g1 = G(color1); + uint32_t b1 = B(color1); + + uint32_t w2 = W(color2); + uint32_t r2 = R(color2); + uint32_t g2 = G(color2); + uint32_t b2 = B(color2); + + uint32_t w3 = ((w2 * blend) + (w1 * (blendmax - blend))) >> shift; + uint32_t r3 = ((r2 * blend) + (r1 * (blendmax - blend))) >> shift; + uint32_t g3 = ((g2 * blend) + (g1 * (blendmax - blend))) >> shift; + uint32_t b3 = ((b2 * blend) + (b1 * (blendmax - blend))) >> shift; + + return RGBW32(r3, g3, b3, w3); +} + +/* + * color add function that preserves ratio + * idea: https://github.com/Aircoookie/WLED/pull/2465 by https://github.com/Proto-molecule + */ +uint32_t color_add(uint32_t c1, uint32_t c2) +{ + uint32_t r = R(c1) + R(c2); + uint32_t g = G(c1) + G(c2); + uint32_t b = B(c1) + B(c2); + uint32_t w = W(c1) + W(c2); + uint16_t max = r; + if (g > max) max = g; + if (b > max) max = b; + if (w > max) max = w; + if (max < 256) return RGBW32(r, g, b, w); + else return RGBW32(r * 255 / max, g * 255 / max, b * 255 / max, w * 255 / max); +} + void setRandomColor(byte* rgb) { - lastRandomIndex = strip.get_random_wheel_index(lastRandomIndex); + lastRandomIndex = strip.getMainSegment().get_random_wheel_index(lastRandomIndex); colorHStoRGB(lastRandomIndex*256,255,rgb); } @@ -274,3 +319,53 @@ uint16_t approximateKelvinFromRGB(uint32_t rgb) { return (k > 10091) ? 10091 : k; } } + +//gamma 2.8 lookup table used for color correction +static byte gammaT[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, + 2, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 5, 5, 5, + 5, 6, 6, 6, 6, 7, 7, 7, 7, 8, 8, 8, 9, 9, 9, 10, + 10, 10, 11, 11, 11, 12, 12, 13, 13, 13, 14, 14, 15, 15, 16, 16, + 17, 17, 18, 18, 19, 19, 20, 20, 21, 21, 22, 22, 23, 24, 24, 25, + 25, 26, 27, 27, 28, 29, 29, 30, 31, 32, 32, 33, 34, 35, 35, 36, + 37, 38, 39, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 50, + 51, 52, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 66, 67, 68, + 69, 70, 72, 73, 74, 75, 77, 78, 79, 81, 82, 83, 85, 86, 87, 89, + 90, 92, 93, 95, 96, 98, 99,101,102,104,105,107,109,110,112,114, + 115,117,119,120,122,124,126,127,129,131,133,135,137,138,140,142, + 144,146,148,150,152,154,156,158,160,162,164,167,169,171,173,175, + 177,180,182,184,186,189,191,193,196,198,200,203,205,208,210,213, + 215,218,220,223,225,228,231,233,236,239,241,244,247,249,252,255 }; + +uint8_t gamma8_cal(uint8_t b, float gamma) +{ + return (int)(powf((float)b / 255.0f, gamma) * 255.0f + 0.5f); +} + +void calcGammaTable(float gamma) +{ + for (uint16_t i = 0; i < 256; i++) { + gammaT[i] = gamma8_cal(i, gamma); + } +} + +uint8_t gamma8(uint8_t b) +{ + return gammaT[b]; +} + +uint32_t gamma32(uint32_t color) +{ + //if (!strip.gammaCorrectCol) return color; + uint8_t w = W(color); + uint8_t r = R(color); + uint8_t g = G(color); + uint8_t b = B(color); + w = gammaT[w]; + r = gammaT[r]; + g = gammaT[g]; + b = gammaT[b]; + return RGBW32(r, g, b, w); +} diff --git a/wled00/console_font_5x12.h b/wled00/console_font_5x12.h new file mode 100644 index 0000000000..8429de7d6d --- /dev/null +++ b/wled00/console_font_5x12.h @@ -0,0 +1,4099 @@ +// font curtesy of https://github.com/idispatch/raster-fonts +static const unsigned char console_font_5x12[] PROGMEM = { + + /* + * code=0, hex=0x00, ascii="^@" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=1, hex=0x01, ascii="^A" + */ + 0x70, /* 01110 */ + 0x88, /* 10001 */ + 0x88, /* 10001 */ + 0xD8, /* 11011 */ + 0x88, /* 10001 */ + 0x88, /* 10001 */ + 0xD8, /* 11011 */ + 0xA8, /* 10101 */ + 0x88, /* 10001 */ + 0x70, /* 01110 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=2, hex=0x02, ascii="^B" + */ + 0x70, /* 01110 */ + 0xF8, /* 11111 */ + 0xF8, /* 11111 */ + 0xA8, /* 10101 */ + 0xF8, /* 11111 */ + 0xF8, /* 11111 */ + 0xA8, /* 10101 */ + 0xD8, /* 11011 */ + 0xF8, /* 11111 */ + 0x70, /* 01110 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=3, hex=0x03, ascii="^C" + */ + 0x00, /* 00000 */ + 0x50, /* 01010 */ + 0xD8, /* 11011 */ + 0xF8, /* 11111 */ + 0xF8, /* 11111 */ + 0xF8, /* 11111 */ + 0xF8, /* 11111 */ + 0x70, /* 01110 */ + 0x70, /* 01110 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x00, /* 00000 */ + + /* + * code=4, hex=0x04, ascii="^D" + */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x70, /* 01110 */ + 0x70, /* 01110 */ + 0xF8, /* 11111 */ + 0xF8, /* 11111 */ + 0xF8, /* 11111 */ + 0x70, /* 01110 */ + 0x70, /* 01110 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x00, /* 00000 */ + + /* + * code=5, hex=0x05, ascii="^E" + */ + 0x20, /* 00100 */ + 0x70, /* 01110 */ + 0x70, /* 01110 */ + 0x70, /* 01110 */ + 0x20, /* 00100 */ + 0xF8, /* 11111 */ + 0xF8, /* 11111 */ + 0x70, /* 01110 */ + 0x20, /* 00100 */ + 0x70, /* 01110 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=6, hex=0x06, ascii="^F" + */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x70, /* 01110 */ + 0x70, /* 01110 */ + 0xF8, /* 11111 */ + 0xF8, /* 11111 */ + 0xF8, /* 11111 */ + 0x70, /* 01110 */ + 0x20, /* 00100 */ + 0x70, /* 01110 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=7, hex=0x07, ascii="^G" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x60, /* 01100 */ + 0x60, /* 01100 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=8, hex=0x08, ascii="^H" + */ + 0xF8, /* 11111 */ + 0xF8, /* 11111 */ + 0xF8, /* 11111 */ + 0xF8, /* 11111 */ + 0xF8, /* 11111 */ + 0x98, /* 10011 */ + 0x98, /* 10011 */ + 0xF8, /* 11111 */ + 0xF8, /* 11111 */ + 0xF8, /* 11111 */ + 0xF8, /* 11111 */ + 0xF8, /* 11111 */ + + /* + * code=9, hex=0x09, ascii="^I" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x70, /* 01110 */ + 0x50, /* 01010 */ + 0x50, /* 01010 */ + 0x70, /* 01110 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=10, hex=0x0A, ascii="^J" + */ + 0xF8, /* 11111 */ + 0xF8, /* 11111 */ + 0xF8, /* 11111 */ + 0xF8, /* 11111 */ + 0x88, /* 10001 */ + 0xA8, /* 10101 */ + 0xA8, /* 10101 */ + 0x88, /* 10001 */ + 0xF8, /* 11111 */ + 0xF8, /* 11111 */ + 0xF8, /* 11111 */ + 0xF8, /* 11111 */ + + /* + * code=11, hex=0x0B, ascii="^K" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x38, /* 00111 */ + 0x18, /* 00011 */ + 0x28, /* 00101 */ + 0x60, /* 01100 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x60, /* 01100 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=12, hex=0x0C, ascii="^L" + */ + 0x00, /* 00000 */ + 0x60, /* 01100 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x60, /* 01100 */ + 0x40, /* 01000 */ + 0x40, /* 01000 */ + 0xE0, /* 11100 */ + 0x40, /* 01000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=13, hex=0x0D, ascii="^M" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x20, /* 00100 */ + 0x30, /* 00110 */ + 0x30, /* 00110 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0xE0, /* 11100 */ + 0xC0, /* 11000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=14, hex=0x0E, ascii="^N" + */ + 0x30, /* 00110 */ + 0x50, /* 01010 */ + 0x70, /* 01110 */ + 0x50, /* 01010 */ + 0x50, /* 01010 */ + 0x50, /* 01010 */ + 0x70, /* 01110 */ + 0x70, /* 01110 */ + 0xC0, /* 11000 */ + 0xC0, /* 11000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=15, hex=0x0F, ascii="^O" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0xA8, /* 10101 */ + 0x70, /* 01110 */ + 0x88, /* 10001 */ + 0x70, /* 01110 */ + 0xA8, /* 10101 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=16, hex=0x10, ascii="^P" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x80, /* 10000 */ + 0xC0, /* 11000 */ + 0xE0, /* 11100 */ + 0xF0, /* 11110 */ + 0xE0, /* 11100 */ + 0xC0, /* 11000 */ + 0x80, /* 10000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=17, hex=0x11, ascii="^Q" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x10, /* 00010 */ + 0x30, /* 00110 */ + 0x70, /* 01110 */ + 0xF0, /* 11110 */ + 0x70, /* 01110 */ + 0x30, /* 00110 */ + 0x10, /* 00010 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=18, hex=0x12, ascii="^R" + */ + 0x00, /* 00000 */ + 0x20, /* 00100 */ + 0x70, /* 01110 */ + 0xA8, /* 10101 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0xA8, /* 10101 */ + 0x70, /* 01110 */ + 0x20, /* 00100 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=19, hex=0x13, ascii="^S" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x50, /* 01010 */ + 0x50, /* 01010 */ + 0x50, /* 01010 */ + 0x50, /* 01010 */ + 0x50, /* 01010 */ + 0x00, /* 00000 */ + 0x50, /* 01010 */ + 0x50, /* 01010 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=20, hex=0x14, ascii="^T" + */ + 0x00, /* 00000 */ + 0x70, /* 01110 */ + 0xD0, /* 11010 */ + 0xD0, /* 11010 */ + 0xD0, /* 11010 */ + 0xD0, /* 11010 */ + 0x50, /* 01010 */ + 0x50, /* 01010 */ + 0x50, /* 01010 */ + 0x50, /* 01010 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=21, hex=0x15, ascii="^U" + */ + 0x00, /* 00000 */ + 0x60, /* 01100 */ + 0x90, /* 10010 */ + 0x80, /* 10000 */ + 0x60, /* 01100 */ + 0x90, /* 10010 */ + 0x60, /* 01100 */ + 0x10, /* 00010 */ + 0x90, /* 10010 */ + 0x60, /* 01100 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=22, hex=0x16, ascii="^V" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0xF0, /* 11110 */ + 0xF0, /* 11110 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=23, hex=0x17, ascii="^W" + */ + 0x00, /* 00000 */ + 0x20, /* 00100 */ + 0x70, /* 01110 */ + 0xA8, /* 10101 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0xA8, /* 10101 */ + 0x70, /* 01110 */ + 0x20, /* 00100 */ + 0xF8, /* 11111 */ + 0x00, /* 00000 */ + + /* + * code=24, hex=0x18, ascii="^X" + */ + 0x00, /* 00000 */ + 0x20, /* 00100 */ + 0x70, /* 01110 */ + 0xA8, /* 10101 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=25, hex=0x19, ascii="^Y" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0xA8, /* 10101 */ + 0x70, /* 01110 */ + 0x20, /* 00100 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=26, hex=0x1A, ascii="^Z" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x40, /* 01000 */ + 0x20, /* 00100 */ + 0xF0, /* 11110 */ + 0x20, /* 00100 */ + 0x40, /* 01000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=27, hex=0x1B, ascii="^[" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x20, /* 00100 */ + 0x40, /* 01000 */ + 0xF0, /* 11110 */ + 0x40, /* 01000 */ + 0x20, /* 00100 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=28, hex=0x1C, ascii="^\" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x80, /* 10000 */ + 0x80, /* 10000 */ + 0xF0, /* 11110 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=29, hex=0x1D, ascii="^]" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x50, /* 01010 */ + 0x50, /* 01010 */ + 0xF8, /* 11111 */ + 0x50, /* 01010 */ + 0x50, /* 01010 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=30, hex=0x1E, ascii="^^" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x70, /* 01110 */ + 0x70, /* 01110 */ + 0xF8, /* 11111 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=31, hex=0x1F, ascii="^_" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0xF8, /* 11111 */ + 0x70, /* 01110 */ + 0x70, /* 01110 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=32, hex=0x20, ascii=" " + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=33, hex=0x21, ascii="!" + */ + 0x00, /* 00000 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x00, /* 00000 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=34, hex=0x22, ascii=""" + */ + 0x50, /* 01010 */ + 0x50, /* 01010 */ + 0x50, /* 01010 */ + 0x50, /* 01010 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=35, hex=0x23, ascii="#" + */ + 0x00, /* 00000 */ + 0x50, /* 01010 */ + 0x50, /* 01010 */ + 0xF8, /* 11111 */ + 0x50, /* 01010 */ + 0x50, /* 01010 */ + 0xF8, /* 11111 */ + 0x50, /* 01010 */ + 0x50, /* 01010 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=36, hex=0x24, ascii="$" + */ + 0x00, /* 00000 */ + 0x40, /* 01000 */ + 0x60, /* 01100 */ + 0x90, /* 10010 */ + 0x80, /* 10000 */ + 0x40, /* 01000 */ + 0x20, /* 00100 */ + 0x10, /* 00010 */ + 0x90, /* 10010 */ + 0x60, /* 01100 */ + 0x20, /* 00100 */ + 0x00, /* 00000 */ + + /* + * code=37, hex=0x25, ascii="%" + */ + 0x00, /* 00000 */ + 0xD0, /* 11010 */ + 0xD0, /* 11010 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x40, /* 01000 */ + 0x40, /* 01000 */ + 0x40, /* 01000 */ + 0xB0, /* 10110 */ + 0xB0, /* 10110 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=38, hex=0x26, ascii="&" + */ + 0x00, /* 00000 */ + 0x40, /* 01000 */ + 0xA0, /* 10100 */ + 0xA0, /* 10100 */ + 0xA0, /* 10100 */ + 0x40, /* 01000 */ + 0xA8, /* 10101 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x68, /* 01101 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=39, hex=0x27, ascii="'" + */ + 0x30, /* 00110 */ + 0x30, /* 00110 */ + 0x10, /* 00010 */ + 0x20, /* 00100 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=40, hex=0x28, ascii="(" + */ + 0x00, /* 00000 */ + 0x20, /* 00100 */ + 0x40, /* 01000 */ + 0x40, /* 01000 */ + 0x40, /* 01000 */ + 0x40, /* 01000 */ + 0x40, /* 01000 */ + 0x40, /* 01000 */ + 0x40, /* 01000 */ + 0x20, /* 00100 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=41, hex=0x29, ascii=")" + */ + 0x00, /* 00000 */ + 0x40, /* 01000 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x40, /* 01000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=42, hex=0x2A, ascii="*" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x20, /* 00100 */ + 0xA8, /* 10101 */ + 0x70, /* 01110 */ + 0x70, /* 01110 */ + 0xA8, /* 10101 */ + 0x20, /* 00100 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=43, hex=0x2B, ascii="+" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0xF8, /* 11111 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=44, hex=0x2C, ascii="," + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x60, /* 01100 */ + 0x60, /* 01100 */ + 0x20, /* 00100 */ + 0x40, /* 01000 */ + + /* + * code=45, hex=0x2D, ascii="-" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0xF0, /* 11110 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=46, hex=0x2E, ascii="." + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x60, /* 01100 */ + 0x60, /* 01100 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=47, hex=0x2F, ascii="/" + */ + 0x00, /* 00000 */ + 0x10, /* 00010 */ + 0x10, /* 00010 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x40, /* 01000 */ + 0x40, /* 01000 */ + 0x40, /* 01000 */ + 0x80, /* 10000 */ + 0x80, /* 10000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=48, hex=0x30, ascii="0" + */ + 0x00, /* 00000 */ + 0x70, /* 01110 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0xE0, /* 11100 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=49, hex=0x31, ascii="1" + */ + 0x00, /* 00000 */ + 0x20, /* 00100 */ + 0x60, /* 01100 */ + 0xA0, /* 10100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0xF0, /* 11110 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=50, hex=0x32, ascii="2" + */ + 0x00, /* 00000 */ + 0x60, /* 01100 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x10, /* 00010 */ + 0x20, /* 00100 */ + 0x40, /* 01000 */ + 0x80, /* 10000 */ + 0x80, /* 10000 */ + 0xF0, /* 11110 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=51, hex=0x33, ascii="3" + */ + 0x00, /* 00000 */ + 0x60, /* 01100 */ + 0x90, /* 10010 */ + 0x10, /* 00010 */ + 0x10, /* 00010 */ + 0x60, /* 01100 */ + 0x10, /* 00010 */ + 0x10, /* 00010 */ + 0x90, /* 10010 */ + 0x60, /* 01100 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=52, hex=0x34, ascii="4" + */ + 0x00, /* 00000 */ + 0x10, /* 00010 */ + 0x30, /* 00110 */ + 0x50, /* 01010 */ + 0x50, /* 01010 */ + 0x90, /* 10010 */ + 0xF0, /* 11110 */ + 0x10, /* 00010 */ + 0x10, /* 00010 */ + 0x10, /* 00010 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=53, hex=0x35, ascii="5" + */ + 0x00, /* 00000 */ + 0xF0, /* 11110 */ + 0x80, /* 10000 */ + 0x80, /* 10000 */ + 0x80, /* 10000 */ + 0xE0, /* 11100 */ + 0x10, /* 00010 */ + 0x10, /* 00010 */ + 0x90, /* 10010 */ + 0x60, /* 01100 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=54, hex=0x36, ascii="6" + */ + 0x00, /* 00000 */ + 0x30, /* 00110 */ + 0x40, /* 01000 */ + 0x80, /* 10000 */ + 0xE0, /* 11100 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x60, /* 01100 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=55, hex=0x37, ascii="7" + */ + 0x00, /* 00000 */ + 0xF0, /* 11110 */ + 0x10, /* 00010 */ + 0x10, /* 00010 */ + 0x10, /* 00010 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x40, /* 01000 */ + 0x40, /* 01000 */ + 0x40, /* 01000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=56, hex=0x38, ascii="8" + */ + 0x00, /* 00000 */ + 0x60, /* 01100 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x60, /* 01100 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x60, /* 01100 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=57, hex=0x39, ascii="9" + */ + 0x00, /* 00000 */ + 0x60, /* 01100 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x70, /* 01110 */ + 0x10, /* 00010 */ + 0x20, /* 00100 */ + 0xC0, /* 11000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=58, hex=0x3A, ascii=":" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x60, /* 01100 */ + 0x60, /* 01100 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x60, /* 01100 */ + 0x60, /* 01100 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=59, hex=0x3B, ascii=";" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x60, /* 01100 */ + 0x60, /* 01100 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x60, /* 01100 */ + 0x60, /* 01100 */ + 0x20, /* 00100 */ + 0x40, /* 01000 */ + + /* + * code=60, hex=0x3C, ascii="<" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x10, /* 00010 */ + 0x20, /* 00100 */ + 0x40, /* 01000 */ + 0x80, /* 10000 */ + 0x80, /* 10000 */ + 0x40, /* 01000 */ + 0x20, /* 00100 */ + 0x10, /* 00010 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=61, hex=0x3D, ascii="=" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0xF0, /* 11110 */ + 0x00, /* 00000 */ + 0xF0, /* 11110 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=62, hex=0x3E, ascii=">" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x80, /* 10000 */ + 0x40, /* 01000 */ + 0x20, /* 00100 */ + 0x10, /* 00010 */ + 0x10, /* 00010 */ + 0x20, /* 00100 */ + 0x40, /* 01000 */ + 0x80, /* 10000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=63, hex=0x3F, ascii="?" + */ + 0x00, /* 00000 */ + 0x60, /* 01100 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x10, /* 00010 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x00, /* 00000 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=64, hex=0x40, ascii="@" + */ + 0x00, /* 00000 */ + 0x60, /* 01100 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0xB0, /* 10110 */ + 0xB0, /* 10110 */ + 0x80, /* 10000 */ + 0x80, /* 10000 */ + 0x70, /* 01110 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=65, hex=0x41, ascii="A" + */ + 0x00, /* 00000 */ + 0x60, /* 01100 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0xF0, /* 11110 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=66, hex=0x42, ascii="B" + */ + 0x00, /* 00000 */ + 0xE0, /* 11100 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0xE0, /* 11100 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0xE0, /* 11100 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=67, hex=0x43, ascii="C" + */ + 0x00, /* 00000 */ + 0x60, /* 01100 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x80, /* 10000 */ + 0x80, /* 10000 */ + 0x80, /* 10000 */ + 0x80, /* 10000 */ + 0x90, /* 10010 */ + 0x60, /* 01100 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=68, hex=0x44, ascii="D" + */ + 0x00, /* 00000 */ + 0xE0, /* 11100 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0xE0, /* 11100 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=69, hex=0x45, ascii="E" + */ + 0x00, /* 00000 */ + 0xF0, /* 11110 */ + 0x80, /* 10000 */ + 0x80, /* 10000 */ + 0x80, /* 10000 */ + 0xF0, /* 11110 */ + 0x80, /* 10000 */ + 0x80, /* 10000 */ + 0x80, /* 10000 */ + 0xF0, /* 11110 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=70, hex=0x46, ascii="F" + */ + 0x00, /* 00000 */ + 0xF0, /* 11110 */ + 0x80, /* 10000 */ + 0x80, /* 10000 */ + 0x80, /* 10000 */ + 0xF0, /* 11110 */ + 0x80, /* 10000 */ + 0x80, /* 10000 */ + 0x80, /* 10000 */ + 0x80, /* 10000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=71, hex=0x47, ascii="G" + */ + 0x00, /* 00000 */ + 0x60, /* 01100 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x80, /* 10000 */ + 0x80, /* 10000 */ + 0xB0, /* 10110 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x60, /* 01100 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=72, hex=0x48, ascii="H" + */ + 0x00, /* 00000 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0xF0, /* 11110 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=73, hex=0x49, ascii="I" + */ + 0x00, /* 00000 */ + 0x70, /* 01110 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x70, /* 01110 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=74, hex=0x4A, ascii="J" + */ + 0x00, /* 00000 */ + 0x70, /* 01110 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0xA0, /* 10100 */ + 0xA0, /* 10100 */ + 0x40, /* 01000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=75, hex=0x4B, ascii="K" + */ + 0x00, /* 00000 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0xA0, /* 10100 */ + 0xA0, /* 10100 */ + 0xC0, /* 11000 */ + 0xA0, /* 10100 */ + 0xA0, /* 10100 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=76, hex=0x4C, ascii="L" + */ + 0x00, /* 00000 */ + 0x80, /* 10000 */ + 0x80, /* 10000 */ + 0x80, /* 10000 */ + 0x80, /* 10000 */ + 0x80, /* 10000 */ + 0x80, /* 10000 */ + 0x80, /* 10000 */ + 0x80, /* 10000 */ + 0xF0, /* 11110 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=77, hex=0x4D, ascii="M" + */ + 0x00, /* 00000 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0xF0, /* 11110 */ + 0xF0, /* 11110 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=78, hex=0x4E, ascii="N" + */ + 0x00, /* 00000 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0xD0, /* 11010 */ + 0xD0, /* 11010 */ + 0xB0, /* 10110 */ + 0xB0, /* 10110 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=79, hex=0x4F, ascii="O" + */ + 0x00, /* 00000 */ + 0x60, /* 01100 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x60, /* 01100 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=80, hex=0x50, ascii="P" + */ + 0x00, /* 00000 */ + 0xE0, /* 11100 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0xE0, /* 11100 */ + 0x80, /* 10000 */ + 0x80, /* 10000 */ + 0x80, /* 10000 */ + 0x80, /* 10000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=81, hex=0x51, ascii="Q" + */ + 0x00, /* 00000 */ + 0x60, /* 01100 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x60, /* 01100 */ + 0x20, /* 00100 */ + 0x10, /* 00010 */ + + /* + * code=82, hex=0x52, ascii="R" + */ + 0x00, /* 00000 */ + 0xE0, /* 11100 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0xE0, /* 11100 */ + 0xC0, /* 11000 */ + 0xA0, /* 10100 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=83, hex=0x53, ascii="S" + */ + 0x00, /* 00000 */ + 0x60, /* 01100 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x80, /* 10000 */ + 0x60, /* 01100 */ + 0x10, /* 00010 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x60, /* 01100 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=84, hex=0x54, ascii="T" + */ + 0x00, /* 00000 */ + 0xF8, /* 11111 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=85, hex=0x55, ascii="U" + */ + 0x00, /* 00000 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x60, /* 01100 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=86, hex=0x56, ascii="V" + */ + 0x00, /* 00000 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x50, /* 01010 */ + 0x50, /* 01010 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=87, hex=0x57, ascii="W" + */ + 0x00, /* 00000 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0xF0, /* 11110 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=88, hex=0x58, ascii="X" + */ + 0x00, /* 00000 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x60, /* 01100 */ + 0x60, /* 01100 */ + 0x60, /* 01100 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=89, hex=0x59, ascii="Y" + */ + 0x00, /* 00000 */ + 0x88, /* 10001 */ + 0x88, /* 10001 */ + 0x88, /* 10001 */ + 0x50, /* 01010 */ + 0x50, /* 01010 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=90, hex=0x5A, ascii="Z" + */ + 0x00, /* 00000 */ + 0xF0, /* 11110 */ + 0x10, /* 00010 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x40, /* 01000 */ + 0x40, /* 01000 */ + 0x80, /* 10000 */ + 0x80, /* 10000 */ + 0xF0, /* 11110 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=91, hex=0x5B, ascii="[" + */ + 0x00, /* 00000 */ + 0x60, /* 01100 */ + 0x40, /* 01000 */ + 0x40, /* 01000 */ + 0x40, /* 01000 */ + 0x40, /* 01000 */ + 0x40, /* 01000 */ + 0x40, /* 01000 */ + 0x40, /* 01000 */ + 0x60, /* 01100 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=92, hex=0x5C, ascii="\" + */ + 0x00, /* 00000 */ + 0x80, /* 10000 */ + 0x80, /* 10000 */ + 0x40, /* 01000 */ + 0x40, /* 01000 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x10, /* 00010 */ + 0x10, /* 00010 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=93, hex=0x5D, ascii="]" + */ + 0x00, /* 00000 */ + 0x60, /* 01100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x60, /* 01100 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=94, hex=0x5E, ascii="^" + */ + 0x00, /* 00000 */ + 0x20, /* 00100 */ + 0x50, /* 01010 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=95, hex=0x5F, ascii="_" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0xF8, /* 11111 */ + + /* + * code=96, hex=0x60, ascii="`" + */ + 0x60, /* 01100 */ + 0x60, /* 01100 */ + 0x40, /* 01000 */ + 0x20, /* 00100 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=97, hex=0x61, ascii="a" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x60, /* 01100 */ + 0x10, /* 00010 */ + 0x70, /* 01110 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x70, /* 01110 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=98, hex=0x62, ascii="b" + */ + 0x00, /* 00000 */ + 0x80, /* 10000 */ + 0x80, /* 10000 */ + 0x80, /* 10000 */ + 0xE0, /* 11100 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0xE0, /* 11100 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=99, hex=0x63, ascii="c" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x70, /* 01110 */ + 0x80, /* 10000 */ + 0x80, /* 10000 */ + 0x80, /* 10000 */ + 0x80, /* 10000 */ + 0x70, /* 01110 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=100, hex=0x64, ascii="d" + */ + 0x00, /* 00000 */ + 0x10, /* 00010 */ + 0x10, /* 00010 */ + 0x10, /* 00010 */ + 0x70, /* 01110 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x70, /* 01110 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=101, hex=0x65, ascii="e" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x60, /* 01100 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0xF0, /* 11110 */ + 0x80, /* 10000 */ + 0x70, /* 01110 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=102, hex=0x66, ascii="f" + */ + 0x00, /* 00000 */ + 0x20, /* 00100 */ + 0x40, /* 01000 */ + 0x40, /* 01000 */ + 0xE0, /* 11100 */ + 0x40, /* 01000 */ + 0x40, /* 01000 */ + 0x40, /* 01000 */ + 0x40, /* 01000 */ + 0x40, /* 01000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=103, hex=0x67, ascii="g" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x70, /* 01110 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x70, /* 01110 */ + 0x10, /* 00010 */ + 0x60, /* 01100 */ + + /* + * code=104, hex=0x68, ascii="h" + */ + 0x00, /* 00000 */ + 0x80, /* 10000 */ + 0x80, /* 10000 */ + 0x80, /* 10000 */ + 0xE0, /* 11100 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=105, hex=0x69, ascii="i" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x20, /* 00100 */ + 0x00, /* 00000 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x70, /* 01110 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=106, hex=0x6A, ascii="j" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x20, /* 00100 */ + 0x00, /* 00000 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0xA0, /* 10100 */ + 0x40, /* 01000 */ + + /* + * code=107, hex=0x6B, ascii="k" + */ + 0x00, /* 00000 */ + 0x80, /* 10000 */ + 0x80, /* 10000 */ + 0x80, /* 10000 */ + 0x90, /* 10010 */ + 0xA0, /* 10100 */ + 0xC0, /* 11000 */ + 0xC0, /* 11000 */ + 0xA0, /* 10100 */ + 0x90, /* 10010 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=108, hex=0x6C, ascii="l" + */ + 0x00, /* 00000 */ + 0x60, /* 01100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x70, /* 01110 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=109, hex=0x6D, ascii="m" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x90, /* 10010 */ + 0xF0, /* 11110 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=110, hex=0x6E, ascii="n" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0xA0, /* 10100 */ + 0xD0, /* 11010 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=111, hex=0x6F, ascii="o" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x60, /* 01100 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x60, /* 01100 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=112, hex=0x70, ascii="p" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0xE0, /* 11100 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0xE0, /* 11100 */ + 0x80, /* 10000 */ + 0x80, /* 10000 */ + + /* + * code=113, hex=0x71, ascii="q" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x70, /* 01110 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x70, /* 01110 */ + 0x10, /* 00010 */ + 0x10, /* 00010 */ + + /* + * code=114, hex=0x72, ascii="r" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0xB0, /* 10110 */ + 0x50, /* 01010 */ + 0x40, /* 01000 */ + 0x40, /* 01000 */ + 0x40, /* 01000 */ + 0x40, /* 01000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=115, hex=0x73, ascii="s" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x60, /* 01100 */ + 0x90, /* 10010 */ + 0x40, /* 01000 */ + 0x20, /* 00100 */ + 0x90, /* 10010 */ + 0x60, /* 01100 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=116, hex=0x74, ascii="t" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x40, /* 01000 */ + 0x40, /* 01000 */ + 0xE0, /* 11100 */ + 0x40, /* 01000 */ + 0x40, /* 01000 */ + 0x40, /* 01000 */ + 0x40, /* 01000 */ + 0x60, /* 01100 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=117, hex=0x75, ascii="u" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x70, /* 01110 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=118, hex=0x76, ascii="v" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x50, /* 01010 */ + 0x50, /* 01010 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=119, hex=0x77, ascii="w" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0xF0, /* 11110 */ + 0x90, /* 10010 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=120, hex=0x78, ascii="x" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x60, /* 01100 */ + 0x60, /* 01100 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=121, hex=0x79, ascii="y" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x70, /* 01110 */ + 0x10, /* 00010 */ + 0xE0, /* 11100 */ + + /* + * code=122, hex=0x7A, ascii="z" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0xF0, /* 11110 */ + 0x10, /* 00010 */ + 0x20, /* 00100 */ + 0x40, /* 01000 */ + 0x80, /* 10000 */ + 0xF0, /* 11110 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=123, hex=0x7B, ascii="{" + */ + 0x00, /* 00000 */ + 0x20, /* 00100 */ + 0x40, /* 01000 */ + 0x40, /* 01000 */ + 0x40, /* 01000 */ + 0x80, /* 10000 */ + 0x40, /* 01000 */ + 0x40, /* 01000 */ + 0x40, /* 01000 */ + 0x20, /* 00100 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=124, hex=0x7C, ascii="|" + */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x00, /* 00000 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=125, hex=0x7D, ascii="}" + */ + 0x00, /* 00000 */ + 0x40, /* 01000 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x10, /* 00010 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x40, /* 01000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=126, hex=0x7E, ascii="~" + */ + 0x00, /* 00000 */ + 0x50, /* 01010 */ + 0xA0, /* 10100 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=127, hex=0x7F, ascii="^?" + */ + 0x00, /* 00000 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x50, /* 01010 */ + 0x50, /* 01010 */ + 0x88, /* 10001 */ + 0x88, /* 10001 */ + 0x88, /* 10001 */ + 0x88, /* 10001 */ + 0xF8, /* 11111 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=128, hex=0x80, ascii="!^@" + */ + 0x00, /* 00000 */ + 0x60, /* 01100 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x80, /* 10000 */ + 0x80, /* 10000 */ + 0x80, /* 10000 */ + 0x80, /* 10000 */ + 0x90, /* 10010 */ + 0x60, /* 01100 */ + 0x20, /* 00100 */ + 0x40, /* 01000 */ + + /* + * code=129, hex=0x81, ascii="!^A" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x50, /* 01010 */ + 0x00, /* 00000 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x70, /* 01110 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=130, hex=0x82, ascii="!^B" + */ + 0x00, /* 00000 */ + 0x30, /* 00110 */ + 0x40, /* 01000 */ + 0x00, /* 00000 */ + 0x60, /* 01100 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0xF0, /* 11110 */ + 0x80, /* 10000 */ + 0x70, /* 01110 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=131, hex=0x83, ascii="!^C" + */ + 0x00, /* 00000 */ + 0x60, /* 01100 */ + 0x90, /* 10010 */ + 0x00, /* 00000 */ + 0x60, /* 01100 */ + 0x90, /* 10010 */ + 0x10, /* 00010 */ + 0x70, /* 01110 */ + 0x90, /* 10010 */ + 0x70, /* 01110 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=132, hex=0x84, ascii="!^D" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x50, /* 01010 */ + 0x00, /* 00000 */ + 0x60, /* 01100 */ + 0x90, /* 10010 */ + 0x10, /* 00010 */ + 0x70, /* 01110 */ + 0x90, /* 10010 */ + 0x70, /* 01110 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=133, hex=0x85, ascii="!^E" + */ + 0x00, /* 00000 */ + 0xC0, /* 11000 */ + 0x20, /* 00100 */ + 0x00, /* 00000 */ + 0x60, /* 01100 */ + 0x90, /* 10010 */ + 0x10, /* 00010 */ + 0x70, /* 01110 */ + 0x90, /* 10010 */ + 0x70, /* 01110 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=134, hex=0x86, ascii="!^F" + */ + 0x00, /* 00000 */ + 0x20, /* 00100 */ + 0x50, /* 01010 */ + 0x20, /* 00100 */ + 0x60, /* 01100 */ + 0x90, /* 10010 */ + 0x10, /* 00010 */ + 0x70, /* 01110 */ + 0x90, /* 10010 */ + 0x70, /* 01110 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=135, hex=0x87, ascii="!^G" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x70, /* 01110 */ + 0x80, /* 10000 */ + 0x80, /* 10000 */ + 0x80, /* 10000 */ + 0x80, /* 10000 */ + 0x70, /* 01110 */ + 0x20, /* 00100 */ + 0xC0, /* 11000 */ + + /* + * code=136, hex=0x88, ascii="!^H" + */ + 0x00, /* 00000 */ + 0x60, /* 01100 */ + 0x90, /* 10010 */ + 0x00, /* 00000 */ + 0x60, /* 01100 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0xF0, /* 11110 */ + 0x80, /* 10000 */ + 0x70, /* 01110 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=137, hex=0x89, ascii="!^I" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x50, /* 01010 */ + 0x00, /* 00000 */ + 0x60, /* 01100 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0xF0, /* 11110 */ + 0x80, /* 10000 */ + 0x70, /* 01110 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=138, hex=0x8A, ascii="!^J" + */ + 0x00, /* 00000 */ + 0xC0, /* 11000 */ + 0x20, /* 00100 */ + 0x00, /* 00000 */ + 0x60, /* 01100 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0xF0, /* 11110 */ + 0x80, /* 10000 */ + 0x70, /* 01110 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=139, hex=0x8B, ascii="!^K" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x50, /* 01010 */ + 0x00, /* 00000 */ + 0x60, /* 01100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x70, /* 01110 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=140, hex=0x8C, ascii="!^L" + */ + 0x00, /* 00000 */ + 0x60, /* 01100 */ + 0x90, /* 10010 */ + 0x00, /* 00000 */ + 0x60, /* 01100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x70, /* 01110 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=141, hex=0x8D, ascii="!^M" + */ + 0x00, /* 00000 */ + 0x60, /* 01100 */ + 0x10, /* 00010 */ + 0x00, /* 00000 */ + 0x60, /* 01100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x70, /* 01110 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=142, hex=0x8E, ascii="!^N" + */ + 0x50, /* 01010 */ + 0x00, /* 00000 */ + 0x60, /* 01100 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0xF0, /* 11110 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=143, hex=0x8F, ascii="!^O" + */ + 0x20, /* 00100 */ + 0x50, /* 01010 */ + 0x20, /* 00100 */ + 0x60, /* 01100 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0xF0, /* 11110 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=144, hex=0x90, ascii="!^P" + */ + 0x30, /* 00110 */ + 0x40, /* 01000 */ + 0x00, /* 00000 */ + 0xF0, /* 11110 */ + 0x80, /* 10000 */ + 0x80, /* 10000 */ + 0xF0, /* 11110 */ + 0x80, /* 10000 */ + 0x80, /* 10000 */ + 0xF0, /* 11110 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=145, hex=0x91, ascii="!^Q" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x70, /* 01110 */ + 0x28, /* 00101 */ + 0x28, /* 00101 */ + 0x70, /* 01110 */ + 0xA0, /* 10100 */ + 0x50, /* 01010 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=146, hex=0x92, ascii="!^R" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x70, /* 01110 */ + 0xE0, /* 11100 */ + 0xA0, /* 10100 */ + 0xA0, /* 10100 */ + 0xF0, /* 11110 */ + 0xA0, /* 10100 */ + 0xA0, /* 10100 */ + 0xB0, /* 10110 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=147, hex=0x93, ascii="!^S" + */ + 0x00, /* 00000 */ + 0x60, /* 01100 */ + 0x90, /* 10010 */ + 0x00, /* 00000 */ + 0x60, /* 01100 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x60, /* 01100 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=148, hex=0x94, ascii="!^T" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x50, /* 01010 */ + 0x00, /* 00000 */ + 0x60, /* 01100 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x60, /* 01100 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=149, hex=0x95, ascii="!^U" + */ + 0x00, /* 00000 */ + 0x60, /* 01100 */ + 0x10, /* 00010 */ + 0x00, /* 00000 */ + 0x60, /* 01100 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x60, /* 01100 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=150, hex=0x96, ascii="!^V" + */ + 0x00, /* 00000 */ + 0x60, /* 01100 */ + 0x90, /* 10010 */ + 0x00, /* 00000 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x70, /* 01110 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=151, hex=0x97, ascii="!^W" + */ + 0x00, /* 00000 */ + 0xC0, /* 11000 */ + 0x20, /* 00100 */ + 0x00, /* 00000 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x70, /* 01110 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=152, hex=0x98, ascii="!^X" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x50, /* 01010 */ + 0x00, /* 00000 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x70, /* 01110 */ + 0x10, /* 00010 */ + 0xE0, /* 11100 */ + + /* + * code=153, hex=0x99, ascii="!^Y" + */ + 0x50, /* 01010 */ + 0x00, /* 00000 */ + 0x60, /* 01100 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x60, /* 01100 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=154, hex=0x9A, ascii="!^Z" + */ + 0x50, /* 01010 */ + 0x00, /* 00000 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x70, /* 01110 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=155, hex=0x9B, ascii="!^[" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x20, /* 00100 */ + 0x70, /* 01110 */ + 0x88, /* 10001 */ + 0x80, /* 10000 */ + 0x88, /* 10001 */ + 0x70, /* 01110 */ + 0x20, /* 00100 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=156, hex=0x9C, ascii="!^\" + */ + 0x00, /* 00000 */ + 0x20, /* 00100 */ + 0x50, /* 01010 */ + 0x40, /* 01000 */ + 0x40, /* 01000 */ + 0xE0, /* 11100 */ + 0x40, /* 01000 */ + 0x40, /* 01000 */ + 0xD0, /* 11010 */ + 0xF0, /* 11110 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=157, hex=0x9D, ascii="!^]" + */ + 0x00, /* 00000 */ + 0x88, /* 10001 */ + 0x50, /* 01010 */ + 0x50, /* 01010 */ + 0xF8, /* 11111 */ + 0x20, /* 00100 */ + 0xF8, /* 11111 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=158, hex=0x9E, ascii="!^^" + */ + 0x00, /* 00000 */ + 0xE0, /* 11100 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0xE0, /* 11100 */ + 0x90, /* 10010 */ + 0xB8, /* 10111 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=159, hex=0x9F, ascii="!^_" + */ + 0x10, /* 00010 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x70, /* 01110 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0xA0, /* 10100 */ + 0x40, /* 01000 */ + + /* + * code=160, hex=0xA0, ascii="! " + */ + 0x00, /* 00000 */ + 0x30, /* 00110 */ + 0x40, /* 01000 */ + 0x00, /* 00000 */ + 0x60, /* 01100 */ + 0x90, /* 10010 */ + 0x10, /* 00010 */ + 0x70, /* 01110 */ + 0x90, /* 10010 */ + 0x70, /* 01110 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=161, hex=0xA1, ascii="!!" + */ + 0x00, /* 00000 */ + 0x30, /* 00110 */ + 0x40, /* 01000 */ + 0x00, /* 00000 */ + 0x60, /* 01100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x70, /* 01110 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=162, hex=0xA2, ascii="!"" + */ + 0x00, /* 00000 */ + 0x30, /* 00110 */ + 0x40, /* 01000 */ + 0x00, /* 00000 */ + 0x60, /* 01100 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x60, /* 01100 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=163, hex=0xA3, ascii="!#" + */ + 0x00, /* 00000 */ + 0x30, /* 00110 */ + 0x40, /* 01000 */ + 0x00, /* 00000 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x70, /* 01110 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=164, hex=0xA4, ascii="!$" + */ + 0x00, /* 00000 */ + 0x50, /* 01010 */ + 0xA0, /* 10100 */ + 0x00, /* 00000 */ + 0xA0, /* 10100 */ + 0xD0, /* 11010 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=165, hex=0xA5, ascii="!%" + */ + 0x50, /* 01010 */ + 0xA0, /* 10100 */ + 0x00, /* 00000 */ + 0x90, /* 10010 */ + 0xD0, /* 11010 */ + 0xF0, /* 11110 */ + 0xB0, /* 10110 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=166, hex=0xA6, ascii="!&" + */ + 0x00, /* 00000 */ + 0x60, /* 01100 */ + 0x10, /* 00010 */ + 0x70, /* 01110 */ + 0x90, /* 10010 */ + 0x70, /* 01110 */ + 0x00, /* 00000 */ + 0xF0, /* 11110 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=167, hex=0xA7, ascii="!'" + */ + 0x00, /* 00000 */ + 0x60, /* 01100 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x60, /* 01100 */ + 0x00, /* 00000 */ + 0xF0, /* 11110 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=168, hex=0xA8, ascii="!(" + */ + 0x00, /* 00000 */ + 0x40, /* 01000 */ + 0x40, /* 01000 */ + 0x00, /* 00000 */ + 0x40, /* 01000 */ + 0x40, /* 01000 */ + 0x80, /* 10000 */ + 0x80, /* 10000 */ + 0x90, /* 10010 */ + 0x60, /* 01100 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=169, hex=0xA9, ascii="!)" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0xF0, /* 11110 */ + 0x80, /* 10000 */ + 0x80, /* 10000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=170, hex=0xAA, ascii="!*" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0xF0, /* 11110 */ + 0x10, /* 00010 */ + 0x10, /* 00010 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=171, hex=0xAB, ascii="!+" + */ + 0x00, /* 00000 */ + 0x40, /* 01000 */ + 0x50, /* 01010 */ + 0x50, /* 01010 */ + 0x20, /* 00100 */ + 0x40, /* 01000 */ + 0x70, /* 01110 */ + 0x90, /* 10010 */ + 0xA0, /* 10100 */ + 0x30, /* 00110 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=172, hex=0xAC, ascii="!," + */ + 0x00, /* 00000 */ + 0x40, /* 01000 */ + 0x50, /* 01010 */ + 0x50, /* 01010 */ + 0x20, /* 00100 */ + 0x40, /* 01000 */ + 0x50, /* 01010 */ + 0xB0, /* 10110 */ + 0xB0, /* 10110 */ + 0x10, /* 00010 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=173, hex=0xAD, ascii="!-" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x20, /* 00100 */ + 0x00, /* 00000 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=174, hex=0xAE, ascii="!." + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x50, /* 01010 */ + 0x50, /* 01010 */ + 0xA0, /* 10100 */ + 0xA0, /* 10100 */ + 0x50, /* 01010 */ + 0x50, /* 01010 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=175, hex=0xAF, ascii="!/" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0xA0, /* 10100 */ + 0xA0, /* 10100 */ + 0x50, /* 01010 */ + 0x50, /* 01010 */ + 0xA0, /* 10100 */ + 0xA0, /* 10100 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=176, hex=0xB0, ascii="!0" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0xA8, /* 10101 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x50, /* 01010 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0xA8, /* 10101 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x50, /* 01010 */ + + /* + * code=177, hex=0xB1, ascii="!1" + */ + 0x00, /* 00000 */ + 0x50, /* 01010 */ + 0x00, /* 00000 */ + 0xA8, /* 10101 */ + 0x00, /* 00000 */ + 0x50, /* 01010 */ + 0x00, /* 00000 */ + 0xA8, /* 10101 */ + 0x00, /* 00000 */ + 0x50, /* 01010 */ + 0x00, /* 00000 */ + 0xA8, /* 10101 */ + + /* + * code=178, hex=0xB2, ascii="!2" + */ + 0x50, /* 01010 */ + 0xA8, /* 10101 */ + 0x50, /* 01010 */ + 0xA8, /* 10101 */ + 0x50, /* 01010 */ + 0xA8, /* 10101 */ + 0x50, /* 01010 */ + 0xA8, /* 10101 */ + 0x50, /* 01010 */ + 0xA8, /* 10101 */ + 0x50, /* 01010 */ + 0xA8, /* 10101 */ + + /* + * code=179, hex=0xB3, ascii="!3" + */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + + /* + * code=180, hex=0xB4, ascii="!4" + */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0xE0, /* 11100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + + /* + * code=181, hex=0xB5, ascii="!5" + */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0xE0, /* 11100 */ + 0x20, /* 00100 */ + 0xE0, /* 11100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + + /* + * code=182, hex=0xB6, ascii="!6" + */ + 0x50, /* 01010 */ + 0x50, /* 01010 */ + 0x50, /* 01010 */ + 0x50, /* 01010 */ + 0x50, /* 01010 */ + 0xD0, /* 11010 */ + 0x50, /* 01010 */ + 0x50, /* 01010 */ + 0x50, /* 01010 */ + 0x50, /* 01010 */ + 0x50, /* 01010 */ + 0x50, /* 01010 */ + + /* + * code=183, hex=0xB7, ascii="!7" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0xF0, /* 11110 */ + 0x50, /* 01010 */ + 0x50, /* 01010 */ + 0x50, /* 01010 */ + 0x50, /* 01010 */ + 0x50, /* 01010 */ + 0x50, /* 01010 */ + + /* + * code=184, hex=0xB8, ascii="!8" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0xE0, /* 11100 */ + 0x20, /* 00100 */ + 0xE0, /* 11100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + + /* + * code=185, hex=0xB9, ascii="!9" + */ + 0x50, /* 01010 */ + 0x50, /* 01010 */ + 0x50, /* 01010 */ + 0x50, /* 01010 */ + 0xD0, /* 11010 */ + 0x10, /* 00010 */ + 0xD0, /* 11010 */ + 0x50, /* 01010 */ + 0x50, /* 01010 */ + 0x50, /* 01010 */ + 0x50, /* 01010 */ + 0x50, /* 01010 */ + + /* + * code=186, hex=0xBA, ascii="!:" + */ + 0x50, /* 01010 */ + 0x50, /* 01010 */ + 0x50, /* 01010 */ + 0x50, /* 01010 */ + 0x50, /* 01010 */ + 0x50, /* 01010 */ + 0x50, /* 01010 */ + 0x50, /* 01010 */ + 0x50, /* 01010 */ + 0x50, /* 01010 */ + 0x50, /* 01010 */ + 0x50, /* 01010 */ + + /* + * code=187, hex=0xBB, ascii="!;" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0xF0, /* 11110 */ + 0x10, /* 00010 */ + 0xD0, /* 11010 */ + 0x50, /* 01010 */ + 0x50, /* 01010 */ + 0x50, /* 01010 */ + 0x50, /* 01010 */ + 0x50, /* 01010 */ + + /* + * code=188, hex=0xBC, ascii="!<" + */ + 0x50, /* 01010 */ + 0x50, /* 01010 */ + 0x50, /* 01010 */ + 0x50, /* 01010 */ + 0xD0, /* 11010 */ + 0x10, /* 00010 */ + 0xF0, /* 11110 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=189, hex=0xBD, ascii="!=" + */ + 0x50, /* 01010 */ + 0x50, /* 01010 */ + 0x50, /* 01010 */ + 0x50, /* 01010 */ + 0x50, /* 01010 */ + 0xF0, /* 11110 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=190, hex=0xBE, ascii="!>" + */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0xE0, /* 11100 */ + 0x20, /* 00100 */ + 0xE0, /* 11100 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=191, hex=0xBF, ascii="!?" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0xE0, /* 11100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + + /* + * code=192, hex=0xC0, ascii="!@" + */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x38, /* 00111 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=193, hex=0xC1, ascii="!A" + */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0xF8, /* 11111 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=194, hex=0xC2, ascii="!B" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0xF8, /* 11111 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + + /* + * code=195, hex=0xC3, ascii="!C" + */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x38, /* 00111 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + + /* + * code=196, hex=0xC4, ascii="!D" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0xF8, /* 11111 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=197, hex=0xC5, ascii="!E" + */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0xF8, /* 11111 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + + /* + * code=198, hex=0xC6, ascii="!F" + */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x38, /* 00111 */ + 0x20, /* 00100 */ + 0x38, /* 00111 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + + /* + * code=199, hex=0xC7, ascii="!G" + */ + 0x50, /* 01010 */ + 0x50, /* 01010 */ + 0x50, /* 01010 */ + 0x50, /* 01010 */ + 0x50, /* 01010 */ + 0x58, /* 01011 */ + 0x50, /* 01010 */ + 0x50, /* 01010 */ + 0x50, /* 01010 */ + 0x50, /* 01010 */ + 0x50, /* 01010 */ + 0x50, /* 01010 */ + + /* + * code=200, hex=0xC8, ascii="!H" + */ + 0x50, /* 01010 */ + 0x50, /* 01010 */ + 0x50, /* 01010 */ + 0x50, /* 01010 */ + 0x58, /* 01011 */ + 0x40, /* 01000 */ + 0x78, /* 01111 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=201, hex=0xC9, ascii="!I" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x78, /* 01111 */ + 0x40, /* 01000 */ + 0x58, /* 01011 */ + 0x50, /* 01010 */ + 0x50, /* 01010 */ + 0x50, /* 01010 */ + 0x50, /* 01010 */ + 0x50, /* 01010 */ + + /* + * code=202, hex=0xCA, ascii="!J" + */ + 0x50, /* 01010 */ + 0x50, /* 01010 */ + 0x50, /* 01010 */ + 0x50, /* 01010 */ + 0xD8, /* 11011 */ + 0x00, /* 00000 */ + 0xF8, /* 11111 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=203, hex=0xCB, ascii="!K" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0xF8, /* 11111 */ + 0x00, /* 00000 */ + 0xD8, /* 11011 */ + 0x50, /* 01010 */ + 0x50, /* 01010 */ + 0x50, /* 01010 */ + 0x50, /* 01010 */ + 0x50, /* 01010 */ + + /* + * code=204, hex=0xCC, ascii="!L" + */ + 0x50, /* 01010 */ + 0x50, /* 01010 */ + 0x50, /* 01010 */ + 0x50, /* 01010 */ + 0x58, /* 01011 */ + 0x40, /* 01000 */ + 0x58, /* 01011 */ + 0x50, /* 01010 */ + 0x50, /* 01010 */ + 0x50, /* 01010 */ + 0x50, /* 01010 */ + 0x50, /* 01010 */ + + /* + * code=205, hex=0xCD, ascii="!M" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0xF8, /* 11111 */ + 0x00, /* 00000 */ + 0xF8, /* 11111 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=206, hex=0xCE, ascii="!N" + */ + 0x50, /* 01010 */ + 0x50, /* 01010 */ + 0x50, /* 01010 */ + 0x50, /* 01010 */ + 0xD8, /* 11011 */ + 0x00, /* 00000 */ + 0xD8, /* 11011 */ + 0x50, /* 01010 */ + 0x50, /* 01010 */ + 0x50, /* 01010 */ + 0x50, /* 01010 */ + 0x50, /* 01010 */ + + /* + * code=207, hex=0xCF, ascii="!O" + */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0xF8, /* 11111 */ + 0x00, /* 00000 */ + 0xF8, /* 11111 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=208, hex=0xD0, ascii="!P" + */ + 0x50, /* 01010 */ + 0x50, /* 01010 */ + 0x50, /* 01010 */ + 0x50, /* 01010 */ + 0x50, /* 01010 */ + 0xF8, /* 11111 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=209, hex=0xD1, ascii="!Q" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0xF8, /* 11111 */ + 0x00, /* 00000 */ + 0xF8, /* 11111 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + + /* + * code=210, hex=0xD2, ascii="!R" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0xF8, /* 11111 */ + 0x50, /* 01010 */ + 0x50, /* 01010 */ + 0x50, /* 01010 */ + 0x50, /* 01010 */ + 0x50, /* 01010 */ + 0x50, /* 01010 */ + + /* + * code=211, hex=0xD3, ascii="!S" + */ + 0x50, /* 01010 */ + 0x50, /* 01010 */ + 0x50, /* 01010 */ + 0x50, /* 01010 */ + 0x50, /* 01010 */ + 0x78, /* 01111 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=212, hex=0xD4, ascii="!T" + */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x38, /* 00111 */ + 0x20, /* 00100 */ + 0x38, /* 00111 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=213, hex=0xD5, ascii="!U" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x38, /* 00111 */ + 0x20, /* 00100 */ + 0x38, /* 00111 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + + /* + * code=214, hex=0xD6, ascii="!V" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x78, /* 01111 */ + 0x50, /* 01010 */ + 0x50, /* 01010 */ + 0x50, /* 01010 */ + 0x50, /* 01010 */ + 0x50, /* 01010 */ + 0x50, /* 01010 */ + + /* + * code=215, hex=0xD7, ascii="!W" + */ + 0x50, /* 01010 */ + 0x50, /* 01010 */ + 0x50, /* 01010 */ + 0x50, /* 01010 */ + 0x50, /* 01010 */ + 0xD8, /* 11011 */ + 0x50, /* 01010 */ + 0x50, /* 01010 */ + 0x50, /* 01010 */ + 0x50, /* 01010 */ + 0x50, /* 01010 */ + 0x50, /* 01010 */ + + /* + * code=216, hex=0xD8, ascii="!X" + */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0xF8, /* 11111 */ + 0x00, /* 00000 */ + 0xF8, /* 11111 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + + /* + * code=217, hex=0xD9, ascii="!Y" + */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0xE0, /* 11100 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=218, hex=0xDA, ascii="!Z" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x38, /* 00111 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + + /* + * code=219, hex=0xDB, ascii="![" + */ + 0xF8, /* 11111 */ + 0xF8, /* 11111 */ + 0xF8, /* 11111 */ + 0xF8, /* 11111 */ + 0xF8, /* 11111 */ + 0xF8, /* 11111 */ + 0xF8, /* 11111 */ + 0xF8, /* 11111 */ + 0xF8, /* 11111 */ + 0xF8, /* 11111 */ + 0xF8, /* 11111 */ + 0xF8, /* 11111 */ + + /* + * code=220, hex=0xDC, ascii="!\" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0xF8, /* 11111 */ + 0xF8, /* 11111 */ + 0xF8, /* 11111 */ + 0xF8, /* 11111 */ + 0xF8, /* 11111 */ + 0xF8, /* 11111 */ + + /* + * code=221, hex=0xDD, ascii="!]" + */ + 0xE0, /* 11100 */ + 0xE0, /* 11100 */ + 0xE0, /* 11100 */ + 0xE0, /* 11100 */ + 0xE0, /* 11100 */ + 0xE0, /* 11100 */ + 0xE0, /* 11100 */ + 0xE0, /* 11100 */ + 0xE0, /* 11100 */ + 0xE0, /* 11100 */ + 0xE0, /* 11100 */ + 0xE0, /* 11100 */ + + /* + * code=222, hex=0xDE, ascii="!^" + */ + 0x38, /* 00111 */ + 0x38, /* 00111 */ + 0x38, /* 00111 */ + 0x38, /* 00111 */ + 0x38, /* 00111 */ + 0x38, /* 00111 */ + 0x38, /* 00111 */ + 0x38, /* 00111 */ + 0x38, /* 00111 */ + 0x38, /* 00111 */ + 0x38, /* 00111 */ + 0x38, /* 00111 */ + + /* + * code=223, hex=0xDF, ascii="!_" + */ + 0xF8, /* 11111 */ + 0xF8, /* 11111 */ + 0xF8, /* 11111 */ + 0xF8, /* 11111 */ + 0xF8, /* 11111 */ + 0xF8, /* 11111 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=224, hex=0xE0, ascii="!`" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x48, /* 01001 */ + 0xB0, /* 10110 */ + 0xA0, /* 10100 */ + 0xA0, /* 10100 */ + 0xB0, /* 10110 */ + 0x48, /* 01001 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=225, hex=0xE1, ascii="!a" + */ + 0x00, /* 00000 */ + 0x60, /* 01100 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0xA0, /* 10100 */ + 0xE0, /* 11100 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0xE0, /* 11100 */ + 0x80, /* 10000 */ + 0x80, /* 10000 */ + + /* + * code=226, hex=0xE2, ascii="!b" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x78, /* 01111 */ + 0x48, /* 01001 */ + 0x40, /* 01000 */ + 0x40, /* 01000 */ + 0x40, /* 01000 */ + 0x40, /* 01000 */ + 0x40, /* 01000 */ + 0x40, /* 01000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=227, hex=0xE3, ascii="!c" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0xF8, /* 11111 */ + 0x50, /* 01010 */ + 0x50, /* 01010 */ + 0x50, /* 01010 */ + 0x50, /* 01010 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=228, hex=0xE4, ascii="!d" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0xF8, /* 11111 */ + 0x48, /* 01001 */ + 0x20, /* 00100 */ + 0x10, /* 00010 */ + 0x20, /* 00100 */ + 0x48, /* 01001 */ + 0xF8, /* 11111 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=229, hex=0xE5, ascii="!e" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0xF8, /* 11111 */ + 0xA0, /* 10100 */ + 0xA0, /* 10100 */ + 0xA0, /* 10100 */ + 0x40, /* 01000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=230, hex=0xE6, ascii="!f" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0xE0, /* 11100 */ + 0x80, /* 10000 */ + 0x80, /* 10000 */ + + /* + * code=231, hex=0xE7, ascii="!g" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x50, /* 01010 */ + 0xA0, /* 10100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=232, hex=0xE8, ascii="!h" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x70, /* 01110 */ + 0x20, /* 00100 */ + 0x50, /* 01010 */ + 0x50, /* 01010 */ + 0x20, /* 00100 */ + 0x70, /* 01110 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=233, hex=0xE9, ascii="!i" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x20, /* 00100 */ + 0x50, /* 01010 */ + 0x88, /* 10001 */ + 0xF8, /* 11111 */ + 0x88, /* 10001 */ + 0x50, /* 01010 */ + 0x20, /* 00100 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=234, hex=0xEA, ascii="!j" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x20, /* 00100 */ + 0x50, /* 01010 */ + 0x88, /* 10001 */ + 0x88, /* 10001 */ + 0x50, /* 01010 */ + 0x50, /* 01010 */ + 0xD8, /* 11011 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=235, hex=0xEB, ascii="!k" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x30, /* 00110 */ + 0x40, /* 01000 */ + 0x40, /* 01000 */ + 0x20, /* 00100 */ + 0x70, /* 01110 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x60, /* 01100 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=236, hex=0xEC, ascii="!l" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x50, /* 01010 */ + 0xA8, /* 10101 */ + 0xA8, /* 10101 */ + 0x50, /* 01010 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=237, hex=0xED, ascii="!m" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x70, /* 01110 */ + 0xA8, /* 10101 */ + 0xA8, /* 10101 */ + 0x70, /* 01110 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=238, hex=0xEE, ascii="!n" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x70, /* 01110 */ + 0x80, /* 10000 */ + 0x80, /* 10000 */ + 0xE0, /* 11100 */ + 0x80, /* 10000 */ + 0x80, /* 10000 */ + 0x70, /* 01110 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=239, hex=0xEF, ascii="!o" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x60, /* 01100 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=240, hex=0xF0, ascii="!p" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0xF0, /* 11110 */ + 0x00, /* 00000 */ + 0xF0, /* 11110 */ + 0x00, /* 00000 */ + 0xF0, /* 11110 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=241, hex=0xF1, ascii="!q" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0xF8, /* 11111 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x00, /* 00000 */ + 0xF8, /* 11111 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=242, hex=0xF2, ascii="!r" + */ + 0x00, /* 00000 */ + 0x80, /* 10000 */ + 0x40, /* 01000 */ + 0x20, /* 00100 */ + 0x10, /* 00010 */ + 0x20, /* 00100 */ + 0x40, /* 01000 */ + 0x80, /* 10000 */ + 0x00, /* 00000 */ + 0xF0, /* 11110 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=243, hex=0xF3, ascii="!s" + */ + 0x00, /* 00000 */ + 0x10, /* 00010 */ + 0x20, /* 00100 */ + 0x40, /* 01000 */ + 0x80, /* 10000 */ + 0x40, /* 01000 */ + 0x20, /* 00100 */ + 0x10, /* 00010 */ + 0x00, /* 00000 */ + 0xF0, /* 11110 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=244, hex=0xF4, ascii="!t" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x10, /* 00010 */ + 0x28, /* 00101 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + + /* + * code=245, hex=0xF5, ascii="!u" + */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0xA0, /* 10100 */ + 0x40, /* 01000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=246, hex=0xF6, ascii="!v" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x00, /* 00000 */ + 0xF8, /* 11111 */ + 0x00, /* 00000 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=247, hex=0xF7, ascii="!w" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x50, /* 01010 */ + 0xA0, /* 10100 */ + 0x00, /* 00000 */ + 0x50, /* 01010 */ + 0xA0, /* 10100 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=248, hex=0xF8, ascii="!x" + */ + 0x60, /* 01100 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x60, /* 01100 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=249, hex=0xF9, ascii="!y" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x20, /* 00100 */ + 0x70, /* 01110 */ + 0x20, /* 00100 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=250, hex=0xFA, ascii="!z" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=251, hex=0xFB, ascii="!{" + */ + 0x00, /* 00000 */ + 0x38, /* 00111 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0xA0, /* 10100 */ + 0xA0, /* 10100 */ + 0x60, /* 01100 */ + 0x20, /* 00100 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=252, hex=0xFC, ascii="!|" + */ + 0xA0, /* 10100 */ + 0xD0, /* 11010 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=253, hex=0xFD, ascii="!}" + */ + 0x60, /* 01100 */ + 0x90, /* 10010 */ + 0x10, /* 00010 */ + 0x60, /* 01100 */ + 0x80, /* 10000 */ + 0xF0, /* 11110 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=254, hex=0xFE, ascii="!~" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x70, /* 01110 */ + 0x70, /* 01110 */ + 0x70, /* 01110 */ + 0x70, /* 01110 */ + 0x70, /* 01110 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=255, hex=0xFF, ascii="!^Ÿ" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ +}; diff --git a/wled00/console_font_5x8.h b/wled00/console_font_5x8.h new file mode 100644 index 0000000000..cf1fdd88d0 --- /dev/null +++ b/wled00/console_font_5x8.h @@ -0,0 +1,3075 @@ +// font curtesy of https://github.com/idispatch/raster-fonts +static const unsigned char console_font_5x8[] PROGMEM = { + + /* + * code=0, hex=0x00, ascii="^@" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=1, hex=0x01, ascii="^A" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x70, /* 01110 */ + 0xA8, /* 10101 */ + 0xF8, /* 11111 */ + 0xD8, /* 11011 */ + 0x70, /* 01110 */ + 0x00, /* 00000 */ + + /* + * code=2, hex=0x02, ascii="^B" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x70, /* 01110 */ + 0xA8, /* 10101 */ + 0xF8, /* 11111 */ + 0xF8, /* 11111 */ + 0x70, /* 01110 */ + 0x00, /* 00000 */ + + /* + * code=3, hex=0x03, ascii="^C" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x50, /* 01010 */ + 0xF8, /* 11111 */ + 0xF8, /* 11111 */ + 0x70, /* 01110 */ + 0x20, /* 00100 */ + 0x00, /* 00000 */ + + /* + * code=4, hex=0x04, ascii="^D" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x20, /* 00100 */ + 0x70, /* 01110 */ + 0xF8, /* 11111 */ + 0x70, /* 01110 */ + 0x20, /* 00100 */ + 0x00, /* 00000 */ + + /* + * code=5, hex=0x05, ascii="^E" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x70, /* 01110 */ + 0xA8, /* 10101 */ + 0xF8, /* 11111 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x00, /* 00000 */ + + /* + * code=6, hex=0x06, ascii="^F" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x20, /* 00100 */ + 0x70, /* 01110 */ + 0xF8, /* 11111 */ + 0xA8, /* 10101 */ + 0x20, /* 00100 */ + 0x00, /* 00000 */ + + /* + * code=7, hex=0x07, ascii="^G" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x20, /* 00100 */ + 0x70, /* 01110 */ + 0x20, /* 00100 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=8, hex=0x08, ascii="^H" + */ + 0x00, /* 00000 */ + 0xF8, /* 11111 */ + 0xF8, /* 11111 */ + 0xD8, /* 11011 */ + 0x88, /* 10001 */ + 0xD8, /* 11011 */ + 0xF8, /* 11111 */ + 0xF8, /* 11111 */ + + /* + * code=9, hex=0x09, ascii="^I" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x20, /* 00100 */ + 0x50, /* 01010 */ + 0x20, /* 00100 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=10, hex=0x0A, ascii="^J" + */ + 0x00, /* 00000 */ + 0xF8, /* 11111 */ + 0xF8, /* 11111 */ + 0xD8, /* 11011 */ + 0x88, /* 10001 */ + 0xD8, /* 11011 */ + 0xF8, /* 11111 */ + 0xF8, /* 11111 */ + + /* + * code=11, hex=0x0B, ascii="^K" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x38, /* 00111 */ + 0x18, /* 00011 */ + 0x68, /* 01101 */ + 0xA0, /* 10100 */ + 0x40, /* 01000 */ + 0x00, /* 00000 */ + + /* + * code=12, hex=0x0C, ascii="^L" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x20, /* 00100 */ + 0x50, /* 01010 */ + 0x20, /* 00100 */ + 0x70, /* 01110 */ + 0x20, /* 00100 */ + 0x00, /* 00000 */ + + /* + * code=13, hex=0x0D, ascii="^M" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x20, /* 00100 */ + 0x50, /* 01010 */ + 0x40, /* 01000 */ + 0xC0, /* 11000 */ + 0x80, /* 10000 */ + 0x00, /* 00000 */ + + /* + * code=14, hex=0x0E, ascii="^N" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x38, /* 00111 */ + 0x48, /* 01001 */ + 0x58, /* 01011 */ + 0xD0, /* 11010 */ + 0x80, /* 10000 */ + 0x00, /* 00000 */ + + /* + * code=15, hex=0x0F, ascii="^O" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x20, /* 00100 */ + 0x50, /* 01010 */ + 0x20, /* 00100 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=16, hex=0x10, ascii="^P" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x40, /* 01000 */ + 0x60, /* 01100 */ + 0x70, /* 01110 */ + 0x60, /* 01100 */ + 0x40, /* 01000 */ + 0x00, /* 00000 */ + + /* + * code=17, hex=0x11, ascii="^Q" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x10, /* 00010 */ + 0x30, /* 00110 */ + 0x70, /* 01110 */ + 0x30, /* 00110 */ + 0x10, /* 00010 */ + 0x00, /* 00000 */ + + /* + * code=18, hex=0x12, ascii="^R" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x20, /* 00100 */ + 0x70, /* 01110 */ + 0x20, /* 00100 */ + 0x70, /* 01110 */ + 0x20, /* 00100 */ + 0x00, /* 00000 */ + + /* + * code=19, hex=0x13, ascii="^S" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x50, /* 01010 */ + 0x50, /* 01010 */ + 0x50, /* 01010 */ + 0x00, /* 00000 */ + 0x50, /* 01010 */ + 0x00, /* 00000 */ + + /* + * code=20, hex=0x14, ascii="^T" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x78, /* 01111 */ + 0xD0, /* 11010 */ + 0xD0, /* 11010 */ + 0x50, /* 01010 */ + 0x50, /* 01010 */ + 0x50, /* 01010 */ + + /* + * code=21, hex=0x15, ascii="^U" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x18, /* 00011 */ + 0x60, /* 01100 */ + 0x90, /* 10010 */ + 0x48, /* 01001 */ + 0x30, /* 00110 */ + 0xC0, /* 11000 */ + + /* + * code=22, hex=0x16, ascii="^V" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0xF8, /* 11111 */ + 0xF8, /* 11111 */ + 0x00, /* 00000 */ + + /* + * code=23, hex=0x17, ascii="^W" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x20, /* 00100 */ + 0x70, /* 01110 */ + 0x20, /* 00100 */ + 0x70, /* 01110 */ + 0x20, /* 00100 */ + 0x70, /* 01110 */ + + /* + * code=24, hex=0x18, ascii="^X" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x20, /* 00100 */ + 0x70, /* 01110 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x00, /* 00000 */ + + /* + * code=25, hex=0x19, ascii="^Y" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x70, /* 01110 */ + 0x20, /* 00100 */ + 0x00, /* 00000 */ + + /* + * code=26, hex=0x1A, ascii="^Z" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x10, /* 00010 */ + 0xF8, /* 11111 */ + 0x10, /* 00010 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=27, hex=0x1B, ascii="^[" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x40, /* 01000 */ + 0xF8, /* 11111 */ + 0x40, /* 01000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=28, hex=0x1C, ascii="^\" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x80, /* 10000 */ + 0xF8, /* 11111 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=29, hex=0x1D, ascii="^]" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x50, /* 01010 */ + 0xF8, /* 11111 */ + 0x50, /* 01010 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=30, hex=0x1E, ascii="^^" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x20, /* 00100 */ + 0x70, /* 01110 */ + 0xF8, /* 11111 */ + 0x00, /* 00000 */ + + /* + * code=31, hex=0x1F, ascii="^_" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0xF8, /* 11111 */ + 0x70, /* 01110 */ + 0x20, /* 00100 */ + 0x00, /* 00000 */ + + /* + * code=32, hex=0x20, ascii=" " + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=33, hex=0x21, ascii="!" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x00, /* 00000 */ + 0x20, /* 00100 */ + 0x00, /* 00000 */ + + /* + * code=34, hex=0x22, ascii=""" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x50, /* 01010 */ + 0x50, /* 01010 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=35, hex=0x23, ascii="#" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x50, /* 01010 */ + 0xF8, /* 11111 */ + 0x50, /* 01010 */ + 0xF8, /* 11111 */ + 0x50, /* 01010 */ + 0x00, /* 00000 */ + + /* + * code=36, hex=0x24, ascii="$" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x20, /* 00100 */ + 0x30, /* 00110 */ + 0x40, /* 01000 */ + 0x30, /* 00110 */ + 0x60, /* 01100 */ + 0x20, /* 00100 */ + + /* + * code=37, hex=0x25, ascii="%" + */ + 0x00, /* 00000 */ + 0x40, /* 01000 */ + 0xA8, /* 10101 */ + 0x50, /* 01010 */ + 0x30, /* 00110 */ + 0x68, /* 01101 */ + 0x90, /* 10010 */ + 0x00, /* 00000 */ + + /* + * code=38, hex=0x26, ascii="&" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x30, /* 00110 */ + 0x40, /* 01000 */ + 0x68, /* 01101 */ + 0x90, /* 10010 */ + 0x68, /* 01101 */ + 0x00, /* 00000 */ + + /* + * code=39, hex=0x27, ascii="'" + */ + 0x00, /* 00000 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=40, hex=0x28, ascii="(" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x20, /* 00100 */ + 0x40, /* 01000 */ + 0x40, /* 01000 */ + 0x40, /* 01000 */ + 0x20, /* 00100 */ + 0x00, /* 00000 */ + + /* + * code=41, hex=0x29, ascii=")" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x40, /* 01000 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x40, /* 01000 */ + 0x00, /* 00000 */ + + /* + * code=42, hex=0x2A, ascii="*" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x50, /* 01010 */ + 0x20, /* 00100 */ + 0x70, /* 01110 */ + 0x20, /* 00100 */ + 0x50, /* 01010 */ + 0x00, /* 00000 */ + + /* + * code=43, hex=0x2B, ascii="+" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x20, /* 00100 */ + 0x70, /* 01110 */ + 0x20, /* 00100 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=44, hex=0x2C, ascii="," + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x20, /* 00100 */ + 0x40, /* 01000 */ + + /* + * code=45, hex=0x2D, ascii="-" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0xF0, /* 11110 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=46, hex=0x2E, ascii="." + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x20, /* 00100 */ + 0x00, /* 00000 */ + + /* + * code=47, hex=0x2F, ascii="/" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x10, /* 00010 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x40, /* 01000 */ + 0x40, /* 01000 */ + 0x00, /* 00000 */ + + /* + * code=48, hex=0x30, ascii="0" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x60, /* 01100 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x60, /* 01100 */ + 0x00, /* 00000 */ + + /* + * code=49, hex=0x31, ascii="1" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x20, /* 00100 */ + 0x60, /* 01100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x00, /* 00000 */ + + /* + * code=50, hex=0x32, ascii="2" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x60, /* 01100 */ + 0x90, /* 10010 */ + 0x20, /* 00100 */ + 0x40, /* 01000 */ + 0xF0, /* 11110 */ + 0x00, /* 00000 */ + + /* + * code=51, hex=0x33, ascii="3" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0xE0, /* 11100 */ + 0x10, /* 00010 */ + 0x60, /* 01100 */ + 0x10, /* 00010 */ + 0xE0, /* 11100 */ + 0x00, /* 00000 */ + + /* + * code=52, hex=0x34, ascii="4" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x10, /* 00010 */ + 0x30, /* 00110 */ + 0x50, /* 01010 */ + 0xF0, /* 11110 */ + 0x10, /* 00010 */ + 0x00, /* 00000 */ + + /* + * code=53, hex=0x35, ascii="5" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0xF0, /* 11110 */ + 0x80, /* 10000 */ + 0xE0, /* 11100 */ + 0x10, /* 00010 */ + 0xE0, /* 11100 */ + 0x00, /* 00000 */ + + /* + * code=54, hex=0x36, ascii="6" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x60, /* 01100 */ + 0x80, /* 10000 */ + 0xE0, /* 11100 */ + 0x90, /* 10010 */ + 0x60, /* 01100 */ + 0x00, /* 00000 */ + + /* + * code=55, hex=0x37, ascii="7" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0xF0, /* 11110 */ + 0x10, /* 00010 */ + 0x20, /* 00100 */ + 0x40, /* 01000 */ + 0x40, /* 01000 */ + 0x00, /* 00000 */ + + /* + * code=56, hex=0x38, ascii="8" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x60, /* 01100 */ + 0x90, /* 10010 */ + 0x60, /* 01100 */ + 0x90, /* 10010 */ + 0x60, /* 01100 */ + 0x00, /* 00000 */ + + /* + * code=57, hex=0x39, ascii="9" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x60, /* 01100 */ + 0x90, /* 10010 */ + 0x70, /* 01110 */ + 0x10, /* 00010 */ + 0x60, /* 01100 */ + 0x00, /* 00000 */ + + /* + * code=58, hex=0x3A, ascii=":" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x20, /* 00100 */ + 0x00, /* 00000 */ + 0x20, /* 00100 */ + 0x00, /* 00000 */ + + /* + * code=59, hex=0x3B, ascii=";" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x20, /* 00100 */ + 0x00, /* 00000 */ + 0x20, /* 00100 */ + 0x40, /* 01000 */ + + /* + * code=60, hex=0x3C, ascii="<" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x10, /* 00010 */ + 0x20, /* 00100 */ + 0x40, /* 01000 */ + 0x20, /* 00100 */ + 0x10, /* 00010 */ + 0x00, /* 00000 */ + + /* + * code=61, hex=0x3D, ascii="=" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x70, /* 01110 */ + 0x00, /* 00000 */ + 0x70, /* 01110 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=62, hex=0x3E, ascii=">" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x40, /* 01000 */ + 0x20, /* 00100 */ + 0x10, /* 00010 */ + 0x20, /* 00100 */ + 0x40, /* 01000 */ + 0x00, /* 00000 */ + + /* + * code=63, hex=0x3F, ascii="?" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x60, /* 01100 */ + 0x10, /* 00010 */ + 0x60, /* 01100 */ + 0x00, /* 00000 */ + 0x40, /* 01000 */ + 0x00, /* 00000 */ + + /* + * code=64, hex=0x40, ascii="@" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x70, /* 01110 */ + 0x88, /* 10001 */ + 0xB0, /* 10110 */ + 0x80, /* 10000 */ + 0x70, /* 01110 */ + 0x00, /* 00000 */ + + /* + * code=65, hex=0x41, ascii="A" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x60, /* 01100 */ + 0x90, /* 10010 */ + 0xF0, /* 11110 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x00, /* 00000 */ + + /* + * code=66, hex=0x42, ascii="B" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0xE0, /* 11100 */ + 0x90, /* 10010 */ + 0xE0, /* 11100 */ + 0x90, /* 10010 */ + 0xE0, /* 11100 */ + 0x00, /* 00000 */ + + /* + * code=67, hex=0x43, ascii="C" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x70, /* 01110 */ + 0x80, /* 10000 */ + 0x80, /* 10000 */ + 0x80, /* 10000 */ + 0x70, /* 01110 */ + 0x00, /* 00000 */ + + /* + * code=68, hex=0x44, ascii="D" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0xE0, /* 11100 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0xE0, /* 11100 */ + 0x00, /* 00000 */ + + /* + * code=69, hex=0x45, ascii="E" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0xF0, /* 11110 */ + 0x80, /* 10000 */ + 0xE0, /* 11100 */ + 0x80, /* 10000 */ + 0xF0, /* 11110 */ + 0x00, /* 00000 */ + + /* + * code=70, hex=0x46, ascii="F" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0xF0, /* 11110 */ + 0x80, /* 10000 */ + 0xE0, /* 11100 */ + 0x80, /* 10000 */ + 0x80, /* 10000 */ + 0x00, /* 00000 */ + + /* + * code=71, hex=0x47, ascii="G" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x60, /* 01100 */ + 0x90, /* 10010 */ + 0x80, /* 10000 */ + 0x90, /* 10010 */ + 0x70, /* 01110 */ + 0x00, /* 00000 */ + + /* + * code=72, hex=0x48, ascii="H" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0xF0, /* 11110 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x00, /* 00000 */ + + /* + * code=73, hex=0x49, ascii="I" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x70, /* 01110 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x70, /* 01110 */ + 0x00, /* 00000 */ + + /* + * code=74, hex=0x4A, ascii="J" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x10, /* 00010 */ + 0x10, /* 00010 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x60, /* 01100 */ + 0x00, /* 00000 */ + + /* + * code=75, hex=0x4B, ascii="K" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x90, /* 10010 */ + 0xA0, /* 10100 */ + 0xC0, /* 11000 */ + 0xA0, /* 10100 */ + 0x90, /* 10010 */ + 0x00, /* 00000 */ + + /* + * code=76, hex=0x4C, ascii="L" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x80, /* 10000 */ + 0x80, /* 10000 */ + 0x80, /* 10000 */ + 0x80, /* 10000 */ + 0xF0, /* 11110 */ + 0x00, /* 00000 */ + + /* + * code=77, hex=0x4D, ascii="M" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x90, /* 10010 */ + 0xF0, /* 11110 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x00, /* 00000 */ + + /* + * code=78, hex=0x4E, ascii="N" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x90, /* 10010 */ + 0xD0, /* 11010 */ + 0xB0, /* 10110 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x00, /* 00000 */ + + /* + * code=79, hex=0x4F, ascii="O" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x60, /* 01100 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x60, /* 01100 */ + 0x00, /* 00000 */ + + /* + * code=80, hex=0x50, ascii="P" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0xE0, /* 11100 */ + 0x90, /* 10010 */ + 0xE0, /* 11100 */ + 0x80, /* 10000 */ + 0x80, /* 10000 */ + 0x00, /* 00000 */ + + /* + * code=81, hex=0x51, ascii="Q" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x60, /* 01100 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x60, /* 01100 */ + 0x10, /* 00010 */ + + /* + * code=82, hex=0x52, ascii="R" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0xE0, /* 11100 */ + 0x90, /* 10010 */ + 0xE0, /* 11100 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x00, /* 00000 */ + + /* + * code=83, hex=0x53, ascii="S" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x70, /* 01110 */ + 0x80, /* 10000 */ + 0x60, /* 01100 */ + 0x10, /* 00010 */ + 0xE0, /* 11100 */ + 0x00, /* 00000 */ + + /* + * code=84, hex=0x54, ascii="T" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0xF8, /* 11111 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x00, /* 00000 */ + + /* + * code=85, hex=0x55, ascii="U" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x60, /* 01100 */ + 0x00, /* 00000 */ + + /* + * code=86, hex=0x56, ascii="V" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x60, /* 01100 */ + 0x60, /* 01100 */ + 0x00, /* 00000 */ + + /* + * code=87, hex=0x57, ascii="W" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x88, /* 10001 */ + 0xA8, /* 10101 */ + 0xA8, /* 10101 */ + 0x50, /* 01010 */ + 0x50, /* 01010 */ + 0x00, /* 00000 */ + + /* + * code=88, hex=0x58, ascii="X" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x60, /* 01100 */ + 0x50, /* 01010 */ + 0x90, /* 10010 */ + 0x00, /* 00000 */ + + /* + * code=89, hex=0x59, ascii="Y" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x50, /* 01010 */ + 0x50, /* 01010 */ + 0x50, /* 01010 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x00, /* 00000 */ + + /* + * code=90, hex=0x5A, ascii="Z" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0xF0, /* 11110 */ + 0x20, /* 00100 */ + 0x40, /* 01000 */ + 0x80, /* 10000 */ + 0xF0, /* 11110 */ + 0x00, /* 00000 */ + + /* + * code=91, hex=0x5B, ascii="[" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x60, /* 01100 */ + 0x40, /* 01000 */ + 0x40, /* 01000 */ + 0x40, /* 01000 */ + 0x60, /* 01100 */ + 0x00, /* 00000 */ + + /* + * code=92, hex=0x5C, ascii="\" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x40, /* 01000 */ + 0x40, /* 01000 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x10, /* 00010 */ + 0x00, /* 00000 */ + + /* + * code=93, hex=0x5D, ascii="]" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x60, /* 01100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x60, /* 01100 */ + 0x00, /* 00000 */ + + /* + * code=94, hex=0x5E, ascii="^" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x20, /* 00100 */ + 0x50, /* 01010 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=95, hex=0x5F, ascii="_" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0xF8, /* 11111 */ + + /* + * code=96, hex=0x60, ascii="`" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x40, /* 01000 */ + 0x20, /* 00100 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=97, hex=0x61, ascii="a" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x60, /* 01100 */ + 0x10, /* 00010 */ + 0x70, /* 01110 */ + 0x50, /* 01010 */ + 0x00, /* 00000 */ + + /* + * code=98, hex=0x62, ascii="b" + */ + 0x00, /* 00000 */ + 0x80, /* 10000 */ + 0x80, /* 10000 */ + 0xE0, /* 11100 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0xE0, /* 11100 */ + 0x00, /* 00000 */ + + /* + * code=99, hex=0x63, ascii="c" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x30, /* 00110 */ + 0x40, /* 01000 */ + 0x40, /* 01000 */ + 0x30, /* 00110 */ + 0x00, /* 00000 */ + + /* + * code=100, hex=0x64, ascii="d" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x10, /* 00010 */ + 0x70, /* 01110 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x70, /* 01110 */ + 0x00, /* 00000 */ + + /* + * code=101, hex=0x65, ascii="e" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x60, /* 01100 */ + 0xF0, /* 11110 */ + 0x80, /* 10000 */ + 0x70, /* 01110 */ + 0x00, /* 00000 */ + + /* + * code=102, hex=0x66, ascii="f" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x30, /* 00110 */ + 0x40, /* 01000 */ + 0xE0, /* 11100 */ + 0x40, /* 01000 */ + 0x40, /* 01000 */ + 0x00, /* 00000 */ + + /* + * code=103, hex=0x67, ascii="g" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x70, /* 01110 */ + 0x90, /* 10010 */ + 0x70, /* 01110 */ + 0x10, /* 00010 */ + 0x60, /* 01100 */ + + /* + * code=104, hex=0x68, ascii="h" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x80, /* 10000 */ + 0xE0, /* 11100 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x00, /* 00000 */ + + /* + * code=105, hex=0x69, ascii="i" + */ + 0x00, /* 00000 */ + 0x20, /* 00100 */ + 0x00, /* 00000 */ + 0x60, /* 01100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x70, /* 01110 */ + 0x00, /* 00000 */ + + /* + * code=106, hex=0x6A, ascii="j" + */ + 0x00, /* 00000 */ + 0x10, /* 00010 */ + 0x00, /* 00000 */ + 0x10, /* 00010 */ + 0x10, /* 00010 */ + 0x10, /* 00010 */ + 0x10, /* 00010 */ + 0x60, /* 01100 */ + + /* + * code=107, hex=0x6B, ascii="k" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x80, /* 10000 */ + 0xA0, /* 10100 */ + 0xC0, /* 11000 */ + 0xA0, /* 10100 */ + 0x90, /* 10010 */ + 0x00, /* 00000 */ + + /* + * code=108, hex=0x6C, ascii="l" + */ + 0x00, /* 00000 */ + 0x60, /* 01100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x70, /* 01110 */ + 0x00, /* 00000 */ + + /* + * code=109, hex=0x6D, ascii="m" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x90, /* 10010 */ + 0xF0, /* 11110 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x00, /* 00000 */ + + /* + * code=110, hex=0x6E, ascii="n" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0xE0, /* 11100 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x00, /* 00000 */ + + /* + * code=111, hex=0x6F, ascii="o" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x60, /* 01100 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x60, /* 01100 */ + 0x00, /* 00000 */ + + /* + * code=112, hex=0x70, ascii="p" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0xE0, /* 11100 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0xE0, /* 11100 */ + 0x80, /* 10000 */ + + /* + * code=113, hex=0x71, ascii="q" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x70, /* 01110 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x70, /* 01110 */ + 0x10, /* 00010 */ + + /* + * code=114, hex=0x72, ascii="r" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x50, /* 01010 */ + 0x60, /* 01100 */ + 0x40, /* 01000 */ + 0x40, /* 01000 */ + 0x00, /* 00000 */ + + /* + * code=115, hex=0x73, ascii="s" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x70, /* 01110 */ + 0xC0, /* 11000 */ + 0x30, /* 00110 */ + 0xE0, /* 11100 */ + 0x00, /* 00000 */ + + /* + * code=116, hex=0x74, ascii="t" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x40, /* 01000 */ + 0xF0, /* 11110 */ + 0x40, /* 01000 */ + 0x40, /* 01000 */ + 0x30, /* 00110 */ + 0x00, /* 00000 */ + + /* + * code=117, hex=0x75, ascii="u" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x70, /* 01110 */ + 0x00, /* 00000 */ + + /* + * code=118, hex=0x76, ascii="v" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x60, /* 01100 */ + 0x60, /* 01100 */ + 0x00, /* 00000 */ + + /* + * code=119, hex=0x77, ascii="w" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0xF0, /* 11110 */ + 0x90, /* 10010 */ + 0x00, /* 00000 */ + + /* + * code=120, hex=0x78, ascii="x" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x90, /* 10010 */ + 0x60, /* 01100 */ + 0x60, /* 01100 */ + 0x90, /* 10010 */ + 0x00, /* 00000 */ + + /* + * code=121, hex=0x79, ascii="y" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x70, /* 01110 */ + 0x10, /* 00010 */ + 0x60, /* 01100 */ + + /* + * code=122, hex=0x7A, ascii="z" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0xF0, /* 11110 */ + 0x20, /* 00100 */ + 0x40, /* 01000 */ + 0xF0, /* 11110 */ + 0x00, /* 00000 */ + + /* + * code=123, hex=0x7B, ascii="{" + */ + 0x00, /* 00000 */ + 0x10, /* 00010 */ + 0x20, /* 00100 */ + 0x40, /* 01000 */ + 0x40, /* 01000 */ + 0x20, /* 00100 */ + 0x10, /* 00010 */ + 0x00, /* 00000 */ + + /* + * code=124, hex=0x7C, ascii="|" + */ + 0x00, /* 00000 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x00, /* 00000 */ + + /* + * code=125, hex=0x7D, ascii="}" + */ + 0x00, /* 00000 */ + 0x40, /* 01000 */ + 0x20, /* 00100 */ + 0x10, /* 00010 */ + 0x10, /* 00010 */ + 0x20, /* 00100 */ + 0x40, /* 01000 */ + 0x00, /* 00000 */ + + /* + * code=126, hex=0x7E, ascii="~" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x50, /* 01010 */ + 0xA0, /* 10100 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=127, hex=0x7F, ascii="^?" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x20, /* 00100 */ + 0x50, /* 01010 */ + 0x88, /* 10001 */ + 0xF8, /* 11111 */ + 0x00, /* 00000 */ + + /* + * code=128, hex=0x80, ascii="!^@" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x70, /* 01110 */ + 0x80, /* 10000 */ + 0x80, /* 10000 */ + 0x80, /* 10000 */ + 0x70, /* 01110 */ + 0x20, /* 00100 */ + + /* + * code=129, hex=0x81, ascii="!^A" + */ + 0x00, /* 00000 */ + 0x50, /* 01010 */ + 0x00, /* 00000 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x70, /* 01110 */ + 0x00, /* 00000 */ + + /* + * code=130, hex=0x82, ascii="!^B" + */ + 0x10, /* 00010 */ + 0x20, /* 00100 */ + 0x00, /* 00000 */ + 0x60, /* 01100 */ + 0xF0, /* 11110 */ + 0x80, /* 10000 */ + 0x70, /* 01110 */ + 0x00, /* 00000 */ + + /* + * code=131, hex=0x83, ascii="!^C" + */ + 0x20, /* 00100 */ + 0x50, /* 01010 */ + 0x00, /* 00000 */ + 0xC0, /* 11000 */ + 0x20, /* 00100 */ + 0xA0, /* 10100 */ + 0x50, /* 01010 */ + 0x00, /* 00000 */ + + /* + * code=132, hex=0x84, ascii="!^D" + */ + 0x00, /* 00000 */ + 0x50, /* 01010 */ + 0x00, /* 00000 */ + 0xC0, /* 11000 */ + 0x20, /* 00100 */ + 0x60, /* 01100 */ + 0xB0, /* 10110 */ + 0x00, /* 00000 */ + + /* + * code=133, hex=0x85, ascii="!^E" + */ + 0x40, /* 01000 */ + 0x20, /* 00100 */ + 0x00, /* 00000 */ + 0xC0, /* 11000 */ + 0x20, /* 00100 */ + 0x60, /* 01100 */ + 0xB0, /* 10110 */ + 0x00, /* 00000 */ + + /* + * code=134, hex=0x86, ascii="!^F" + */ + 0x00, /* 00000 */ + 0x20, /* 00100 */ + 0x00, /* 00000 */ + 0xC0, /* 11000 */ + 0x20, /* 00100 */ + 0x60, /* 01100 */ + 0xB0, /* 10110 */ + 0x00, /* 00000 */ + + /* + * code=135, hex=0x87, ascii="!^G" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x30, /* 00110 */ + 0x40, /* 01000 */ + 0x40, /* 01000 */ + 0x30, /* 00110 */ + 0x20, /* 00100 */ + + /* + * code=136, hex=0x88, ascii="!^H" + */ + 0x20, /* 00100 */ + 0x50, /* 01010 */ + 0x00, /* 00000 */ + 0x60, /* 01100 */ + 0xF0, /* 11110 */ + 0x80, /* 10000 */ + 0x70, /* 01110 */ + 0x00, /* 00000 */ + + /* + * code=137, hex=0x89, ascii="!^I" + */ + 0x00, /* 00000 */ + 0x50, /* 01010 */ + 0x00, /* 00000 */ + 0x60, /* 01100 */ + 0xF0, /* 11110 */ + 0x80, /* 10000 */ + 0x70, /* 01110 */ + 0x00, /* 00000 */ + + /* + * code=138, hex=0x8A, ascii="!^J" + */ + 0x40, /* 01000 */ + 0x20, /* 00100 */ + 0x00, /* 00000 */ + 0x60, /* 01100 */ + 0xF0, /* 11110 */ + 0x80, /* 10000 */ + 0x70, /* 01110 */ + 0x00, /* 00000 */ + + /* + * code=139, hex=0x8B, ascii="!^K" + */ + 0x00, /* 00000 */ + 0x50, /* 01010 */ + 0x00, /* 00000 */ + 0x60, /* 01100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x70, /* 01110 */ + 0x00, /* 00000 */ + + /* + * code=140, hex=0x8C, ascii="!^L" + */ + 0x20, /* 00100 */ + 0x50, /* 01010 */ + 0x00, /* 00000 */ + 0x60, /* 01100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x70, /* 01110 */ + 0x00, /* 00000 */ + + /* + * code=141, hex=0x8D, ascii="!^M" + */ + 0x40, /* 01000 */ + 0x20, /* 00100 */ + 0x00, /* 00000 */ + 0x60, /* 01100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x70, /* 01110 */ + 0x00, /* 00000 */ + + /* + * code=142, hex=0x8E, ascii="!^N" + */ + 0xA0, /* 10100 */ + 0x00, /* 00000 */ + 0x60, /* 01100 */ + 0x90, /* 10010 */ + 0xF0, /* 11110 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x00, /* 00000 */ + + /* + * code=143, hex=0x8F, ascii="!^O" + */ + 0x20, /* 00100 */ + 0x00, /* 00000 */ + 0x60, /* 01100 */ + 0x90, /* 10010 */ + 0xF0, /* 11110 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x00, /* 00000 */ + + /* + * code=144, hex=0x90, ascii="!^P" + */ + 0x10, /* 00010 */ + 0x20, /* 00100 */ + 0xF0, /* 11110 */ + 0x80, /* 10000 */ + 0xE0, /* 11100 */ + 0x80, /* 10000 */ + 0xF0, /* 11110 */ + 0x00, /* 00000 */ + + /* + * code=145, hex=0x91, ascii="!^Q" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0xD8, /* 11011 */ + 0x78, /* 01111 */ + 0xE0, /* 11100 */ + 0xB8, /* 10111 */ + 0x00, /* 00000 */ + + /* + * code=146, hex=0x92, ascii="!^R" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x70, /* 01110 */ + 0xA0, /* 10100 */ + 0xF0, /* 11110 */ + 0xA0, /* 10100 */ + 0xB0, /* 10110 */ + 0x00, /* 00000 */ + + /* + * code=147, hex=0x93, ascii="!^S" + */ + 0x20, /* 00100 */ + 0x50, /* 01010 */ + 0x00, /* 00000 */ + 0x60, /* 01100 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x60, /* 01100 */ + 0x00, /* 00000 */ + + /* + * code=148, hex=0x94, ascii="!^T" + */ + 0x00, /* 00000 */ + 0x50, /* 01010 */ + 0x00, /* 00000 */ + 0x60, /* 01100 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x60, /* 01100 */ + 0x00, /* 00000 */ + + /* + * code=149, hex=0x95, ascii="!^U" + */ + 0x40, /* 01000 */ + 0x20, /* 00100 */ + 0x00, /* 00000 */ + 0x60, /* 01100 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x60, /* 01100 */ + 0x00, /* 00000 */ + + /* + * code=150, hex=0x96, ascii="!^V" + */ + 0x20, /* 00100 */ + 0x50, /* 01010 */ + 0x00, /* 00000 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x70, /* 01110 */ + 0x00, /* 00000 */ + + /* + * code=151, hex=0x97, ascii="!^W" + */ + 0x40, /* 01000 */ + 0x20, /* 00100 */ + 0x00, /* 00000 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x70, /* 01110 */ + 0x00, /* 00000 */ + + /* + * code=152, hex=0x98, ascii="!^X" + */ + 0x00, /* 00000 */ + 0x50, /* 01010 */ + 0x00, /* 00000 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x70, /* 01110 */ + 0x10, /* 00010 */ + 0x60, /* 01100 */ + + /* + * code=153, hex=0x99, ascii="!^Y" + */ + 0x00, /* 00000 */ + 0x50, /* 01010 */ + 0x00, /* 00000 */ + 0x60, /* 01100 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x60, /* 01100 */ + 0x00, /* 00000 */ + + /* + * code=154, hex=0x9A, ascii="!^Z" + */ + 0x50, /* 01010 */ + 0x00, /* 00000 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x60, /* 01100 */ + 0x00, /* 00000 */ + + /* + * code=155, hex=0x9B, ascii="!^[" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x20, /* 00100 */ + 0x70, /* 01110 */ + 0x80, /* 10000 */ + 0x80, /* 10000 */ + 0x70, /* 01110 */ + 0x20, /* 00100 */ + + /* + * code=156, hex=0x9C, ascii="!^\" + */ + 0x00, /* 00000 */ + 0x30, /* 00110 */ + 0x50, /* 01010 */ + 0x40, /* 01000 */ + 0xE0, /* 11100 */ + 0x40, /* 01000 */ + 0xF0, /* 11110 */ + 0x00, /* 00000 */ + + /* + * code=157, hex=0x9D, ascii="!^]" + */ + 0x00, /* 00000 */ + 0xD8, /* 11011 */ + 0x50, /* 01010 */ + 0x50, /* 01010 */ + 0x20, /* 00100 */ + 0x70, /* 01110 */ + 0x20, /* 00100 */ + 0x00, /* 00000 */ + + /* + * code=158, hex=0x9E, ascii="!^^" + */ + 0x00, /* 00000 */ + 0xC0, /* 11000 */ + 0xA0, /* 10100 */ + 0xB0, /* 10110 */ + 0xF8, /* 11111 */ + 0x90, /* 10010 */ + 0x88, /* 10001 */ + 0x00, /* 00000 */ + + /* + * code=159, hex=0x9F, ascii="!^_" + */ + 0x00, /* 00000 */ + 0x30, /* 00110 */ + 0x40, /* 01000 */ + 0x40, /* 01000 */ + 0xF0, /* 11110 */ + 0x40, /* 01000 */ + 0x40, /* 01000 */ + 0x80, /* 10000 */ + + /* + * code=160, hex=0xA0, ascii="! " + */ + 0x20, /* 00100 */ + 0x40, /* 01000 */ + 0x00, /* 00000 */ + 0xC0, /* 11000 */ + 0x20, /* 00100 */ + 0x60, /* 01100 */ + 0xB0, /* 10110 */ + 0x00, /* 00000 */ + + /* + * code=161, hex=0xA1, ascii="!!" + */ + 0x10, /* 00010 */ + 0x20, /* 00100 */ + 0x00, /* 00000 */ + 0x60, /* 01100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x70, /* 01110 */ + 0x00, /* 00000 */ + + /* + * code=162, hex=0xA2, ascii="!"" + */ + 0x10, /* 00010 */ + 0x20, /* 00100 */ + 0x00, /* 00000 */ + 0x60, /* 01100 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x60, /* 01100 */ + 0x00, /* 00000 */ + + /* + * code=163, hex=0xA3, ascii="!#" + */ + 0x10, /* 00010 */ + 0x20, /* 00100 */ + 0x00, /* 00000 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x70, /* 01110 */ + 0x00, /* 00000 */ + + /* + * code=164, hex=0xA4, ascii="!$" + */ + 0x50, /* 01010 */ + 0xA0, /* 10100 */ + 0x00, /* 00000 */ + 0xE0, /* 11100 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x00, /* 00000 */ + + /* + * code=165, hex=0xA5, ascii="!%" + */ + 0x50, /* 01010 */ + 0xA0, /* 10100 */ + 0x90, /* 10010 */ + 0xD0, /* 11010 */ + 0xD0, /* 11010 */ + 0xB0, /* 10110 */ + 0x90, /* 10010 */ + 0x00, /* 00000 */ + + /* + * code=166, hex=0xA6, ascii="!&" + */ + 0x00, /* 00000 */ + 0x20, /* 00100 */ + 0x50, /* 01010 */ + 0x30, /* 00110 */ + 0x00, /* 00000 */ + 0x70, /* 01110 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=167, hex=0xA7, ascii="!'" + */ + 0x00, /* 00000 */ + 0x20, /* 00100 */ + 0x50, /* 01010 */ + 0x20, /* 00100 */ + 0x00, /* 00000 */ + 0x70, /* 01110 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=168, hex=0xA8, ascii="!(" + */ + 0x00, /* 00000 */ + 0x20, /* 00100 */ + 0x00, /* 00000 */ + 0x20, /* 00100 */ + 0x40, /* 01000 */ + 0x90, /* 10010 */ + 0x60, /* 01100 */ + 0x00, /* 00000 */ + + /* + * code=169, hex=0xA9, ascii="!)" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0xF8, /* 11111 */ + 0x80, /* 10000 */ + 0x00, /* 00000 */ + + /* + * code=170, hex=0xAA, ascii="!*" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0xF8, /* 11111 */ + 0x08, /* 00001 */ + 0x00, /* 00000 */ + + /* + * code=171, hex=0xAB, ascii="!+" + */ + 0x00, /* 00000 */ + 0x80, /* 10000 */ + 0x90, /* 10010 */ + 0xA0, /* 10100 */ + 0x58, /* 01011 */ + 0x88, /* 10001 */ + 0x38, /* 00111 */ + 0x00, /* 00000 */ + + /* + * code=172, hex=0xAC, ascii="!," + */ + 0x00, /* 00000 */ + 0x88, /* 10001 */ + 0x90, /* 10010 */ + 0xA0, /* 10100 */ + 0x48, /* 01001 */ + 0x98, /* 10011 */ + 0x38, /* 00111 */ + 0x08, /* 00001 */ + + /* + * code=173, hex=0xAD, ascii="!-" + */ + 0x00, /* 00000 */ + 0x20, /* 00100 */ + 0x00, /* 00000 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x70, /* 01110 */ + 0x20, /* 00100 */ + 0x00, /* 00000 */ + + /* + * code=174, hex=0xAE, ascii="!." + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x50, /* 01010 */ + 0xA0, /* 10100 */ + 0x50, /* 01010 */ + 0x00, /* 00000 */ + + /* + * code=175, hex=0xAF, ascii="!/" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0xA0, /* 10100 */ + 0x50, /* 01010 */ + 0xA0, /* 10100 */ + 0x00, /* 00000 */ + + /* + * code=176, hex=0xB0, ascii="!0" + */ + 0xA8, /* 10101 */ + 0x50, /* 01010 */ + 0xA8, /* 10101 */ + 0x50, /* 01010 */ + 0xA8, /* 10101 */ + 0x50, /* 01010 */ + 0xA8, /* 10101 */ + 0x50, /* 01010 */ + + /* + * code=177, hex=0xB1, ascii="!1" + */ + 0xE8, /* 11101 */ + 0x50, /* 01010 */ + 0xB8, /* 10111 */ + 0x50, /* 01010 */ + 0xE8, /* 11101 */ + 0x50, /* 01010 */ + 0xB8, /* 10111 */ + 0x50, /* 01010 */ + + /* + * code=178, hex=0xB2, ascii="!2" + */ + 0xD8, /* 11011 */ + 0x70, /* 01110 */ + 0xD8, /* 11011 */ + 0x70, /* 01110 */ + 0xD8, /* 11011 */ + 0x70, /* 01110 */ + 0xD8, /* 11011 */ + 0x70, /* 01110 */ + + /* + * code=179, hex=0xB3, ascii="!3" + */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + + /* + * code=180, hex=0xB4, ascii="!4" + */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0xE0, /* 11100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + + /* + * code=181, hex=0xB5, ascii="!5" + */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0xE0, /* 11100 */ + 0x20, /* 00100 */ + 0xE0, /* 11100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + + /* + * code=182, hex=0xB6, ascii="!6" + */ + 0x50, /* 01010 */ + 0x50, /* 01010 */ + 0x50, /* 01010 */ + 0xD0, /* 11010 */ + 0x50, /* 01010 */ + 0x50, /* 01010 */ + 0x50, /* 01010 */ + 0x50, /* 01010 */ + + /* + * code=183, hex=0xB7, ascii="!7" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0xF0, /* 11110 */ + 0x50, /* 01010 */ + 0x50, /* 01010 */ + 0x50, /* 01010 */ + 0x50, /* 01010 */ + + /* + * code=184, hex=0xB8, ascii="!8" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0xE0, /* 11100 */ + 0x20, /* 00100 */ + 0xE0, /* 11100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + + /* + * code=185, hex=0xB9, ascii="!9" + */ + 0x50, /* 01010 */ + 0x50, /* 01010 */ + 0xD0, /* 11010 */ + 0x10, /* 00010 */ + 0xD0, /* 11010 */ + 0x50, /* 01010 */ + 0x50, /* 01010 */ + 0x50, /* 01010 */ + + /* + * code=186, hex=0xBA, ascii="!:" + */ + 0x50, /* 01010 */ + 0x50, /* 01010 */ + 0x50, /* 01010 */ + 0x50, /* 01010 */ + 0x50, /* 01010 */ + 0x50, /* 01010 */ + 0x50, /* 01010 */ + 0x50, /* 01010 */ + + /* + * code=187, hex=0xBB, ascii="!;" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0xF0, /* 11110 */ + 0x10, /* 00010 */ + 0xD0, /* 11010 */ + 0x50, /* 01010 */ + 0x50, /* 01010 */ + 0x50, /* 01010 */ + + /* + * code=188, hex=0xBC, ascii="!<" + */ + 0x50, /* 01010 */ + 0x50, /* 01010 */ + 0xD0, /* 11010 */ + 0x10, /* 00010 */ + 0xF0, /* 11110 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=189, hex=0xBD, ascii="!=" + */ + 0x50, /* 01010 */ + 0x50, /* 01010 */ + 0x50, /* 01010 */ + 0xF0, /* 11110 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=190, hex=0xBE, ascii="!>" + */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0xE0, /* 11100 */ + 0x20, /* 00100 */ + 0xE0, /* 11100 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=191, hex=0xBF, ascii="!?" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0xE0, /* 11100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + + /* + * code=192, hex=0xC0, ascii="!@" + */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x38, /* 00111 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=193, hex=0xC1, ascii="!A" + */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0xF8, /* 11111 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=194, hex=0xC2, ascii="!B" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0xF8, /* 11111 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + + /* + * code=195, hex=0xC3, ascii="!C" + */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x38, /* 00111 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + + /* + * code=196, hex=0xC4, ascii="!D" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0xF8, /* 11111 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=197, hex=0xC5, ascii="!E" + */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0xF8, /* 11111 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + + /* + * code=198, hex=0xC6, ascii="!F" + */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x38, /* 00111 */ + 0x20, /* 00100 */ + 0x38, /* 00111 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + + /* + * code=199, hex=0xC7, ascii="!G" + */ + 0x50, /* 01010 */ + 0x50, /* 01010 */ + 0x50, /* 01010 */ + 0x58, /* 01011 */ + 0x50, /* 01010 */ + 0x50, /* 01010 */ + 0x50, /* 01010 */ + 0x50, /* 01010 */ + + /* + * code=200, hex=0xC8, ascii="!H" + */ + 0x50, /* 01010 */ + 0x50, /* 01010 */ + 0x58, /* 01011 */ + 0x40, /* 01000 */ + 0x78, /* 01111 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=201, hex=0xC9, ascii="!I" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x78, /* 01111 */ + 0x40, /* 01000 */ + 0x58, /* 01011 */ + 0x50, /* 01010 */ + 0x50, /* 01010 */ + 0x50, /* 01010 */ + + /* + * code=202, hex=0xCA, ascii="!J" + */ + 0x50, /* 01010 */ + 0x50, /* 01010 */ + 0xD8, /* 11011 */ + 0x00, /* 00000 */ + 0xF8, /* 11111 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=203, hex=0xCB, ascii="!K" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0xF8, /* 11111 */ + 0x00, /* 00000 */ + 0xD8, /* 11011 */ + 0x50, /* 01010 */ + 0x50, /* 01010 */ + 0x50, /* 01010 */ + + /* + * code=204, hex=0xCC, ascii="!L" + */ + 0x50, /* 01010 */ + 0x50, /* 01010 */ + 0x58, /* 01011 */ + 0x40, /* 01000 */ + 0x58, /* 01011 */ + 0x50, /* 01010 */ + 0x50, /* 01010 */ + 0x50, /* 01010 */ + + /* + * code=205, hex=0xCD, ascii="!M" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0xF8, /* 11111 */ + 0x00, /* 00000 */ + 0xF8, /* 11111 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=206, hex=0xCE, ascii="!N" + */ + 0x50, /* 01010 */ + 0x50, /* 01010 */ + 0xD8, /* 11011 */ + 0x00, /* 00000 */ + 0xD8, /* 11011 */ + 0x50, /* 01010 */ + 0x50, /* 01010 */ + 0x50, /* 01010 */ + + /* + * code=207, hex=0xCF, ascii="!O" + */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0xF8, /* 11111 */ + 0x00, /* 00000 */ + 0xF8, /* 11111 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=208, hex=0xD0, ascii="!P" + */ + 0x50, /* 01010 */ + 0x50, /* 01010 */ + 0x50, /* 01010 */ + 0xF8, /* 11111 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=209, hex=0xD1, ascii="!Q" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0xF8, /* 11111 */ + 0x00, /* 00000 */ + 0xF8, /* 11111 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + + /* + * code=210, hex=0xD2, ascii="!R" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0xF8, /* 11111 */ + 0x50, /* 01010 */ + 0x50, /* 01010 */ + 0x50, /* 01010 */ + 0x50, /* 01010 */ + + /* + * code=211, hex=0xD3, ascii="!S" + */ + 0x50, /* 01010 */ + 0x50, /* 01010 */ + 0x50, /* 01010 */ + 0x78, /* 01111 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=212, hex=0xD4, ascii="!T" + */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x38, /* 00111 */ + 0x20, /* 00100 */ + 0x38, /* 00111 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=213, hex=0xD5, ascii="!U" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x38, /* 00111 */ + 0x20, /* 00100 */ + 0x38, /* 00111 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + + /* + * code=214, hex=0xD6, ascii="!V" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x78, /* 01111 */ + 0x50, /* 01010 */ + 0x50, /* 01010 */ + 0x50, /* 01010 */ + 0x50, /* 01010 */ + + /* + * code=215, hex=0xD7, ascii="!W" + */ + 0x50, /* 01010 */ + 0x50, /* 01010 */ + 0x50, /* 01010 */ + 0xF8, /* 11111 */ + 0x50, /* 01010 */ + 0x50, /* 01010 */ + 0x50, /* 01010 */ + 0x50, /* 01010 */ + + /* + * code=216, hex=0xD8, ascii="!X" + */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0xF8, /* 11111 */ + 0x20, /* 00100 */ + 0xF8, /* 11111 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + + /* + * code=217, hex=0xD9, ascii="!Y" + */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0xE0, /* 11100 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=218, hex=0xDA, ascii="!Z" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x38, /* 00111 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + + /* + * code=219, hex=0xDB, ascii="![" + */ + 0xF8, /* 11111 */ + 0xF8, /* 11111 */ + 0xF8, /* 11111 */ + 0xF8, /* 11111 */ + 0xF8, /* 11111 */ + 0xF8, /* 11111 */ + 0xF8, /* 11111 */ + 0xF8, /* 11111 */ + + /* + * code=220, hex=0xDC, ascii="!\" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0xF8, /* 11111 */ + 0xF8, /* 11111 */ + 0xF8, /* 11111 */ + 0xF8, /* 11111 */ + + /* + * code=221, hex=0xDD, ascii="!]" + */ + 0xE0, /* 11100 */ + 0xE0, /* 11100 */ + 0xE0, /* 11100 */ + 0xE0, /* 11100 */ + 0xE0, /* 11100 */ + 0xE0, /* 11100 */ + 0xE0, /* 11100 */ + 0xE0, /* 11100 */ + + /* + * code=222, hex=0xDE, ascii="!^" + */ + 0x18, /* 00011 */ + 0x18, /* 00011 */ + 0x18, /* 00011 */ + 0x18, /* 00011 */ + 0x18, /* 00011 */ + 0x18, /* 00011 */ + 0x18, /* 00011 */ + 0x18, /* 00011 */ + + /* + * code=223, hex=0xDF, ascii="!_" + */ + 0xF8, /* 11111 */ + 0xF8, /* 11111 */ + 0xF8, /* 11111 */ + 0xF8, /* 11111 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=224, hex=0xE0, ascii="!`" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x68, /* 01101 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x68, /* 01101 */ + 0x00, /* 00000 */ + + /* + * code=225, hex=0xE1, ascii="!a" + */ + 0x00, /* 00000 */ + 0x60, /* 01100 */ + 0x90, /* 10010 */ + 0xF0, /* 11110 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0xE0, /* 11100 */ + 0x80, /* 10000 */ + + /* + * code=226, hex=0xE2, ascii="!b" + */ + 0x00, /* 00000 */ + 0x70, /* 01110 */ + 0x40, /* 01000 */ + 0x40, /* 01000 */ + 0x40, /* 01000 */ + 0x40, /* 01000 */ + 0x40, /* 01000 */ + 0x00, /* 00000 */ + + /* + * code=227, hex=0xE3, ascii="!c" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x70, /* 01110 */ + 0x50, /* 01010 */ + 0x50, /* 01010 */ + 0x50, /* 01010 */ + 0x50, /* 01010 */ + 0x00, /* 00000 */ + + /* + * code=228, hex=0xE4, ascii="!d" + */ + 0x00, /* 00000 */ + 0xF8, /* 11111 */ + 0x48, /* 01001 */ + 0x20, /* 00100 */ + 0x40, /* 01000 */ + 0x88, /* 10001 */ + 0xF8, /* 11111 */ + 0x00, /* 00000 */ + + /* + * code=229, hex=0xE5, ascii="!e" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x78, /* 01111 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x60, /* 01100 */ + 0x00, /* 00000 */ + + /* + * code=230, hex=0xE6, ascii="!f" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0xE8, /* 11101 */ + 0x80, /* 10000 */ + + /* + * code=231, hex=0xE7, ascii="!g" + */ + 0x00, /* 00000 */ + 0x98, /* 10011 */ + 0x50, /* 01010 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x00, /* 00000 */ + + /* + * code=232, hex=0xE8, ascii="!h" + */ + 0x00, /* 00000 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x70, /* 01110 */ + 0x88, /* 10001 */ + 0x70, /* 01110 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + + /* + * code=233, hex=0xE9, ascii="!i" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x70, /* 01110 */ + 0x88, /* 10001 */ + 0xF8, /* 11111 */ + 0x88, /* 10001 */ + 0x70, /* 01110 */ + 0x00, /* 00000 */ + + /* + * code=234, hex=0xEA, ascii="!j" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x70, /* 01110 */ + 0x88, /* 10001 */ + 0x88, /* 10001 */ + 0x50, /* 01010 */ + 0xD8, /* 11011 */ + 0x00, /* 00000 */ + + /* + * code=235, hex=0xEB, ascii="!k" + */ + 0x60, /* 01100 */ + 0x80, /* 10000 */ + 0x40, /* 01000 */ + 0x60, /* 01100 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x60, /* 01100 */ + 0x00, /* 00000 */ + + /* + * code=236, hex=0xEC, ascii="!l" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x70, /* 01110 */ + 0xA8, /* 10101 */ + 0xA8, /* 10101 */ + 0x70, /* 01110 */ + 0x00, /* 00000 */ + + /* + * code=237, hex=0xED, ascii="!m" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x08, /* 00001 */ + 0x70, /* 01110 */ + 0xA8, /* 10101 */ + 0x48, /* 01001 */ + 0xB0, /* 10110 */ + 0x00, /* 00000 */ + + /* + * code=238, hex=0xEE, ascii="!n" + */ + 0x00, /* 00000 */ + 0x30, /* 00110 */ + 0x40, /* 01000 */ + 0x70, /* 01110 */ + 0x40, /* 01000 */ + 0x40, /* 01000 */ + 0x30, /* 00110 */ + 0x00, /* 00000 */ + + /* + * code=239, hex=0xEF, ascii="!o" + */ + 0x00, /* 00000 */ + 0x60, /* 01100 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x90, /* 10010 */ + 0x00, /* 00000 */ + + /* + * code=240, hex=0xF0, ascii="!p" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0xF0, /* 11110 */ + 0x00, /* 00000 */ + 0xF0, /* 11110 */ + 0x00, /* 00000 */ + 0xF0, /* 11110 */ + 0x00, /* 00000 */ + + /* + * code=241, hex=0xF1, ascii="!q" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x20, /* 00100 */ + 0xF8, /* 11111 */ + 0x20, /* 00100 */ + 0x00, /* 00000 */ + 0xF8, /* 11111 */ + 0x00, /* 00000 */ + + /* + * code=242, hex=0xF2, ascii="!r" + */ + 0x00, /* 00000 */ + 0x40, /* 01000 */ + 0x20, /* 00100 */ + 0x10, /* 00010 */ + 0x20, /* 00100 */ + 0x40, /* 01000 */ + 0xF0, /* 11110 */ + 0x00, /* 00000 */ + + /* + * code=243, hex=0xF3, ascii="!s" + */ + 0x00, /* 00000 */ + 0x10, /* 00010 */ + 0x20, /* 00100 */ + 0x40, /* 01000 */ + 0x20, /* 00100 */ + 0x10, /* 00010 */ + 0x70, /* 01110 */ + 0x00, /* 00000 */ + + /* + * code=244, hex=0xF4, ascii="!t" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x18, /* 00011 */ + 0x28, /* 00101 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + + /* + * code=245, hex=0xF5, ascii="!u" + */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0x20, /* 00100 */ + 0xA0, /* 10100 */ + 0xC0, /* 11000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=246, hex=0xF6, ascii="!v" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x60, /* 01100 */ + 0x00, /* 00000 */ + 0xF0, /* 11110 */ + 0x00, /* 00000 */ + 0x60, /* 01100 */ + 0x00, /* 00000 */ + + /* + * code=247, hex=0xF7, ascii="!w" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x50, /* 01010 */ + 0xA0, /* 10100 */ + 0x00, /* 00000 */ + 0x50, /* 01010 */ + 0xA0, /* 10100 */ + 0x00, /* 00000 */ + + /* + * code=248, hex=0xF8, ascii="!x" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x20, /* 00100 */ + 0x50, /* 01010 */ + 0x20, /* 00100 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=249, hex=0xF9, ascii="!y" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x60, /* 01100 */ + 0x60, /* 01100 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=250, hex=0xFA, ascii="!z" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x20, /* 00100 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=251, hex=0xFB, ascii="!{" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x18, /* 00011 */ + 0x10, /* 00010 */ + 0x20, /* 00100 */ + 0xA0, /* 10100 */ + 0x40, /* 01000 */ + 0x00, /* 00000 */ + + /* + * code=252, hex=0xFC, ascii="!|" + */ + 0x00, /* 00000 */ + 0x60, /* 01100 */ + 0x50, /* 01010 */ + 0x50, /* 01010 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=253, hex=0xFD, ascii="!}" + */ + 0x00, /* 00000 */ + 0x60, /* 01100 */ + 0x10, /* 00010 */ + 0x20, /* 00100 */ + 0x70, /* 01110 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=254, hex=0xFE, ascii="!~" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x70, /* 01110 */ + 0x70, /* 01110 */ + 0x70, /* 01110 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + + /* + * code=255, hex=0xFF, ascii="!^Ÿ" + */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ + 0x00, /* 00000 */ +}; diff --git a/wled00/console_font_6x8.h b/wled00/console_font_6x8.h new file mode 100644 index 0000000000..e8da19d791 --- /dev/null +++ b/wled00/console_font_6x8.h @@ -0,0 +1,3075 @@ +// font curtesy of https://github.com/idispatch/raster-fonts +static const unsigned char console_font_6x8[] PROGMEM = { + + /* + * code=0, hex=0x00, ascii="^@" + */ + 0x00, /* 000000 */ + 0x00, /* 000000 */ + 0x00, /* 000000 */ + 0x00, /* 000000 */ + 0x00, /* 000000 */ + 0x00, /* 000000 */ + 0x00, /* 000000 */ + 0x00, /* 000000 */ + + /* + * code=1, hex=0x01, ascii="^A" + */ + 0x38, /* 001110 */ + 0x44, /* 010001 */ + 0x6C, /* 011011 */ + 0x44, /* 010001 */ + 0x54, /* 010101 */ + 0x44, /* 010001 */ + 0x38, /* 001110 */ + 0x00, /* 000000 */ + + /* + * code=2, hex=0x02, ascii="^B" + */ + 0x38, /* 001110 */ + 0x7C, /* 011111 */ + 0x54, /* 010101 */ + 0x7C, /* 011111 */ + 0x44, /* 010001 */ + 0x7C, /* 011111 */ + 0x38, /* 001110 */ + 0x00, /* 000000 */ + + /* + * code=3, hex=0x03, ascii="^C" + */ + 0x00, /* 000000 */ + 0x28, /* 001010 */ + 0x7C, /* 011111 */ + 0x7C, /* 011111 */ + 0x7C, /* 011111 */ + 0x38, /* 001110 */ + 0x10, /* 000100 */ + 0x00, /* 000000 */ + + /* + * code=4, hex=0x04, ascii="^D" + */ + 0x00, /* 000000 */ + 0x10, /* 000100 */ + 0x38, /* 001110 */ + 0x7C, /* 011111 */ + 0x7C, /* 011111 */ + 0x38, /* 001110 */ + 0x10, /* 000100 */ + 0x00, /* 000000 */ + + /* + * code=5, hex=0x05, ascii="^E" + */ + 0x10, /* 000100 */ + 0x38, /* 001110 */ + 0x38, /* 001110 */ + 0x10, /* 000100 */ + 0x7C, /* 011111 */ + 0x7C, /* 011111 */ + 0x10, /* 000100 */ + 0x00, /* 000000 */ + + /* + * code=6, hex=0x06, ascii="^F" + */ + 0x00, /* 000000 */ + 0x10, /* 000100 */ + 0x38, /* 001110 */ + 0x7C, /* 011111 */ + 0x7C, /* 011111 */ + 0x10, /* 000100 */ + 0x38, /* 001110 */ + 0x00, /* 000000 */ + + /* + * code=7, hex=0x07, ascii="^G" + */ + 0x00, /* 000000 */ + 0x00, /* 000000 */ + 0x00, /* 000000 */ + 0x30, /* 001100 */ + 0x30, /* 001100 */ + 0x00, /* 000000 */ + 0x00, /* 000000 */ + 0x00, /* 000000 */ + + /* + * code=8, hex=0x08, ascii="^H" + */ + 0xFC, /* 111111 */ + 0xFC, /* 111111 */ + 0xFC, /* 111111 */ + 0xCC, /* 110011 */ + 0xCC, /* 110011 */ + 0xFC, /* 111111 */ + 0xFC, /* 111111 */ + 0xFC, /* 111111 */ + + /* + * code=9, hex=0x09, ascii="^I" + */ + 0x00, /* 000000 */ + 0x00, /* 000000 */ + 0x78, /* 011110 */ + 0x48, /* 010010 */ + 0x48, /* 010010 */ + 0x78, /* 011110 */ + 0x00, /* 000000 */ + 0x00, /* 000000 */ + + /* + * code=10, hex=0x0A, ascii="^J" + */ + 0xFC, /* 111111 */ + 0xFC, /* 111111 */ + 0x84, /* 100001 */ + 0xB4, /* 101101 */ + 0xB4, /* 101101 */ + 0x84, /* 100001 */ + 0xFC, /* 111111 */ + 0xFC, /* 111111 */ + + /* + * code=11, hex=0x0B, ascii="^K" + */ + 0x00, /* 000000 */ + 0x1C, /* 000111 */ + 0x0C, /* 000011 */ + 0x34, /* 001101 */ + 0x48, /* 010010 */ + 0x48, /* 010010 */ + 0x30, /* 001100 */ + 0x00, /* 000000 */ + + /* + * code=12, hex=0x0C, ascii="^L" + */ + 0x38, /* 001110 */ + 0x44, /* 010001 */ + 0x44, /* 010001 */ + 0x38, /* 001110 */ + 0x10, /* 000100 */ + 0x38, /* 001110 */ + 0x10, /* 000100 */ + 0x00, /* 000000 */ + + /* + * code=13, hex=0x0D, ascii="^M" + */ + 0x10, /* 000100 */ + 0x18, /* 000110 */ + 0x14, /* 000101 */ + 0x10, /* 000100 */ + 0x30, /* 001100 */ + 0x70, /* 011100 */ + 0x60, /* 011000 */ + 0x00, /* 000000 */ + + /* + * code=14, hex=0x0E, ascii="^N" + */ + 0x0C, /* 000011 */ + 0x34, /* 001101 */ + 0x2C, /* 001011 */ + 0x34, /* 001101 */ + 0x2C, /* 001011 */ + 0x6C, /* 011011 */ + 0x60, /* 011000 */ + 0x00, /* 000000 */ + + /* + * code=15, hex=0x0F, ascii="^O" + */ + 0x00, /* 000000 */ + 0x54, /* 010101 */ + 0x38, /* 001110 */ + 0x6C, /* 011011 */ + 0x38, /* 001110 */ + 0x54, /* 010101 */ + 0x00, /* 000000 */ + 0x00, /* 000000 */ + + /* + * code=16, hex=0x10, ascii="^P" + */ + 0x20, /* 001000 */ + 0x30, /* 001100 */ + 0x38, /* 001110 */ + 0x3C, /* 001111 */ + 0x38, /* 001110 */ + 0x30, /* 001100 */ + 0x20, /* 001000 */ + 0x00, /* 000000 */ + + /* + * code=17, hex=0x11, ascii="^Q" + */ + 0x08, /* 000010 */ + 0x18, /* 000110 */ + 0x38, /* 001110 */ + 0x78, /* 011110 */ + 0x38, /* 001110 */ + 0x18, /* 000110 */ + 0x08, /* 000010 */ + 0x00, /* 000000 */ + + /* + * code=18, hex=0x12, ascii="^R" + */ + 0x10, /* 000100 */ + 0x38, /* 001110 */ + 0x7C, /* 011111 */ + 0x10, /* 000100 */ + 0x7C, /* 011111 */ + 0x38, /* 001110 */ + 0x10, /* 000100 */ + 0x00, /* 000000 */ + + /* + * code=19, hex=0x13, ascii="^S" + */ + 0x28, /* 001010 */ + 0x28, /* 001010 */ + 0x28, /* 001010 */ + 0x28, /* 001010 */ + 0x28, /* 001010 */ + 0x00, /* 000000 */ + 0x28, /* 001010 */ + 0x00, /* 000000 */ + + /* + * code=20, hex=0x14, ascii="^T" + */ + 0x3C, /* 001111 */ + 0x54, /* 010101 */ + 0x54, /* 010101 */ + 0x34, /* 001101 */ + 0x14, /* 000101 */ + 0x14, /* 000101 */ + 0x14, /* 000101 */ + 0x00, /* 000000 */ + + /* + * code=21, hex=0x15, ascii="^U" + */ + 0x38, /* 001110 */ + 0x44, /* 010001 */ + 0x30, /* 001100 */ + 0x28, /* 001010 */ + 0x18, /* 000110 */ + 0x44, /* 010001 */ + 0x38, /* 001110 */ + 0x00, /* 000000 */ + + /* + * code=22, hex=0x16, ascii="^V" + */ + 0x00, /* 000000 */ + 0x00, /* 000000 */ + 0x00, /* 000000 */ + 0x00, /* 000000 */ + 0x00, /* 000000 */ + 0x78, /* 011110 */ + 0x78, /* 011110 */ + 0x00, /* 000000 */ + + /* + * code=23, hex=0x17, ascii="^W" + */ + 0x10, /* 000100 */ + 0x38, /* 001110 */ + 0x7C, /* 011111 */ + 0x10, /* 000100 */ + 0x7C, /* 011111 */ + 0x38, /* 001110 */ + 0x10, /* 000100 */ + 0x38, /* 001110 */ + + /* + * code=24, hex=0x18, ascii="^X" + */ + 0x10, /* 000100 */ + 0x38, /* 001110 */ + 0x7C, /* 011111 */ + 0x10, /* 000100 */ + 0x10, /* 000100 */ + 0x10, /* 000100 */ + 0x10, /* 000100 */ + 0x00, /* 000000 */ + + /* + * code=25, hex=0x19, ascii="^Y" + */ + 0x10, /* 000100 */ + 0x10, /* 000100 */ + 0x10, /* 000100 */ + 0x10, /* 000100 */ + 0x7C, /* 011111 */ + 0x38, /* 001110 */ + 0x10, /* 000100 */ + 0x00, /* 000000 */ + + /* + * code=26, hex=0x1A, ascii="^Z" + */ + 0x00, /* 000000 */ + 0x10, /* 000100 */ + 0x18, /* 000110 */ + 0x7C, /* 011111 */ + 0x18, /* 000110 */ + 0x10, /* 000100 */ + 0x00, /* 000000 */ + 0x00, /* 000000 */ + + /* + * code=27, hex=0x1B, ascii="^[" + */ + 0x00, /* 000000 */ + 0x10, /* 000100 */ + 0x30, /* 001100 */ + 0x7C, /* 011111 */ + 0x30, /* 001100 */ + 0x10, /* 000100 */ + 0x00, /* 000000 */ + 0x00, /* 000000 */ + + /* + * code=28, hex=0x1C, ascii="^\" + */ + 0x00, /* 000000 */ + 0x00, /* 000000 */ + 0x00, /* 000000 */ + 0x40, /* 010000 */ + 0x40, /* 010000 */ + 0x40, /* 010000 */ + 0x7C, /* 011111 */ + 0x00, /* 000000 */ + + /* + * code=29, hex=0x1D, ascii="^]" + */ + 0x00, /* 000000 */ + 0x28, /* 001010 */ + 0x28, /* 001010 */ + 0x7C, /* 011111 */ + 0x28, /* 001010 */ + 0x28, /* 001010 */ + 0x00, /* 000000 */ + 0x00, /* 000000 */ + + /* + * code=30, hex=0x1E, ascii="^^" + */ + 0x10, /* 000100 */ + 0x10, /* 000100 */ + 0x38, /* 001110 */ + 0x38, /* 001110 */ + 0x7C, /* 011111 */ + 0x7C, /* 011111 */ + 0x00, /* 000000 */ + 0x00, /* 000000 */ + + /* + * code=31, hex=0x1F, ascii="^_" + */ + 0x7C, /* 011111 */ + 0x7C, /* 011111 */ + 0x38, /* 001110 */ + 0x38, /* 001110 */ + 0x10, /* 000100 */ + 0x10, /* 000100 */ + 0x00, /* 000000 */ + 0x00, /* 000000 */ + + /* + * code=32, hex=0x20, ascii=" " + */ + 0x00, /* 000000 */ + 0x00, /* 000000 */ + 0x00, /* 000000 */ + 0x00, /* 000000 */ + 0x00, /* 000000 */ + 0x00, /* 000000 */ + 0x00, /* 000000 */ + 0x00, /* 000000 */ + + /* + * code=33, hex=0x21, ascii="!" + */ + 0x10, /* 000100 */ + 0x38, /* 001110 */ + 0x38, /* 001110 */ + 0x10, /* 000100 */ + 0x10, /* 000100 */ + 0x00, /* 000000 */ + 0x10, /* 000100 */ + 0x00, /* 000000 */ + + /* + * code=34, hex=0x22, ascii=""" + */ + 0x6C, /* 011011 */ + 0x6C, /* 011011 */ + 0x48, /* 010010 */ + 0x00, /* 000000 */ + 0x00, /* 000000 */ + 0x00, /* 000000 */ + 0x00, /* 000000 */ + 0x00, /* 000000 */ + + /* + * code=35, hex=0x23, ascii="#" + */ + 0x00, /* 000000 */ + 0x28, /* 001010 */ + 0x7C, /* 011111 */ + 0x28, /* 001010 */ + 0x28, /* 001010 */ + 0x7C, /* 011111 */ + 0x28, /* 001010 */ + 0x00, /* 000000 */ + + /* + * code=36, hex=0x24, ascii="$" + */ + 0x20, /* 001000 */ + 0x38, /* 001110 */ + 0x40, /* 010000 */ + 0x30, /* 001100 */ + 0x08, /* 000010 */ + 0x70, /* 011100 */ + 0x10, /* 000100 */ + 0x00, /* 000000 */ + + /* + * code=37, hex=0x25, ascii="%" + */ + 0x64, /* 011001 */ + 0x64, /* 011001 */ + 0x08, /* 000010 */ + 0x10, /* 000100 */ + 0x20, /* 001000 */ + 0x4C, /* 010011 */ + 0x4C, /* 010011 */ + 0x00, /* 000000 */ + + /* + * code=38, hex=0x26, ascii="&" + */ + 0x20, /* 001000 */ + 0x50, /* 010100 */ + 0x50, /* 010100 */ + 0x20, /* 001000 */ + 0x54, /* 010101 */ + 0x48, /* 010010 */ + 0x34, /* 001101 */ + 0x00, /* 000000 */ + + /* + * code=39, hex=0x27, ascii="'" + */ + 0x30, /* 001100 */ + 0x30, /* 001100 */ + 0x20, /* 001000 */ + 0x00, /* 000000 */ + 0x00, /* 000000 */ + 0x00, /* 000000 */ + 0x00, /* 000000 */ + 0x00, /* 000000 */ + + /* + * code=40, hex=0x28, ascii="(" + */ + 0x10, /* 000100 */ + 0x20, /* 001000 */ + 0x20, /* 001000 */ + 0x20, /* 001000 */ + 0x20, /* 001000 */ + 0x20, /* 001000 */ + 0x10, /* 000100 */ + 0x00, /* 000000 */ + + /* + * code=41, hex=0x29, ascii=")" + */ + 0x20, /* 001000 */ + 0x10, /* 000100 */ + 0x10, /* 000100 */ + 0x10, /* 000100 */ + 0x10, /* 000100 */ + 0x10, /* 000100 */ + 0x20, /* 001000 */ + 0x00, /* 000000 */ + + /* + * code=42, hex=0x2A, ascii="*" + */ + 0x00, /* 000000 */ + 0x28, /* 001010 */ + 0x38, /* 001110 */ + 0x7C, /* 011111 */ + 0x38, /* 001110 */ + 0x28, /* 001010 */ + 0x00, /* 000000 */ + 0x00, /* 000000 */ + + /* + * code=43, hex=0x2B, ascii="+" + */ + 0x00, /* 000000 */ + 0x10, /* 000100 */ + 0x10, /* 000100 */ + 0x7C, /* 011111 */ + 0x10, /* 000100 */ + 0x10, /* 000100 */ + 0x00, /* 000000 */ + 0x00, /* 000000 */ + + /* + * code=44, hex=0x2C, ascii="," + */ + 0x00, /* 000000 */ + 0x00, /* 000000 */ + 0x00, /* 000000 */ + 0x00, /* 000000 */ + 0x00, /* 000000 */ + 0x30, /* 001100 */ + 0x30, /* 001100 */ + 0x20, /* 001000 */ + + /* + * code=45, hex=0x2D, ascii="-" + */ + 0x00, /* 000000 */ + 0x00, /* 000000 */ + 0x00, /* 000000 */ + 0x7C, /* 011111 */ + 0x00, /* 000000 */ + 0x00, /* 000000 */ + 0x00, /* 000000 */ + 0x00, /* 000000 */ + + /* + * code=46, hex=0x2E, ascii="." + */ + 0x00, /* 000000 */ + 0x00, /* 000000 */ + 0x00, /* 000000 */ + 0x00, /* 000000 */ + 0x00, /* 000000 */ + 0x30, /* 001100 */ + 0x30, /* 001100 */ + 0x00, /* 000000 */ + + /* + * code=47, hex=0x2F, ascii="/" + */ + 0x00, /* 000000 */ + 0x04, /* 000001 */ + 0x08, /* 000010 */ + 0x10, /* 000100 */ + 0x20, /* 001000 */ + 0x40, /* 010000 */ + 0x00, /* 000000 */ + 0x00, /* 000000 */ + + /* + * code=48, hex=0x30, ascii="0" + */ + 0x38, /* 001110 */ + 0x44, /* 010001 */ + 0x4C, /* 010011 */ + 0x54, /* 010101 */ + 0x64, /* 011001 */ + 0x44, /* 010001 */ + 0x38, /* 001110 */ + 0x00, /* 000000 */ + + /* + * code=49, hex=0x31, ascii="1" + */ + 0x10, /* 000100 */ + 0x30, /* 001100 */ + 0x10, /* 000100 */ + 0x10, /* 000100 */ + 0x10, /* 000100 */ + 0x10, /* 000100 */ + 0x38, /* 001110 */ + 0x00, /* 000000 */ + + /* + * code=50, hex=0x32, ascii="2" + */ + 0x38, /* 001110 */ + 0x44, /* 010001 */ + 0x04, /* 000001 */ + 0x18, /* 000110 */ + 0x20, /* 001000 */ + 0x40, /* 010000 */ + 0x7C, /* 011111 */ + 0x00, /* 000000 */ + + /* + * code=51, hex=0x33, ascii="3" + */ + 0x38, /* 001110 */ + 0x44, /* 010001 */ + 0x04, /* 000001 */ + 0x38, /* 001110 */ + 0x04, /* 000001 */ + 0x44, /* 010001 */ + 0x38, /* 001110 */ + 0x00, /* 000000 */ + + /* + * code=52, hex=0x34, ascii="4" + */ + 0x08, /* 000010 */ + 0x18, /* 000110 */ + 0x28, /* 001010 */ + 0x48, /* 010010 */ + 0x7C, /* 011111 */ + 0x08, /* 000010 */ + 0x08, /* 000010 */ + 0x00, /* 000000 */ + + /* + * code=53, hex=0x35, ascii="5" + */ + 0x7C, /* 011111 */ + 0x40, /* 010000 */ + 0x40, /* 010000 */ + 0x78, /* 011110 */ + 0x04, /* 000001 */ + 0x44, /* 010001 */ + 0x38, /* 001110 */ + 0x00, /* 000000 */ + + /* + * code=54, hex=0x36, ascii="6" + */ + 0x18, /* 000110 */ + 0x20, /* 001000 */ + 0x40, /* 010000 */ + 0x78, /* 011110 */ + 0x44, /* 010001 */ + 0x44, /* 010001 */ + 0x38, /* 001110 */ + 0x00, /* 000000 */ + + /* + * code=55, hex=0x37, ascii="7" + */ + 0x7C, /* 011111 */ + 0x04, /* 000001 */ + 0x08, /* 000010 */ + 0x10, /* 000100 */ + 0x20, /* 001000 */ + 0x20, /* 001000 */ + 0x20, /* 001000 */ + 0x00, /* 000000 */ + + /* + * code=56, hex=0x38, ascii="8" + */ + 0x38, /* 001110 */ + 0x44, /* 010001 */ + 0x44, /* 010001 */ + 0x38, /* 001110 */ + 0x44, /* 010001 */ + 0x44, /* 010001 */ + 0x38, /* 001110 */ + 0x00, /* 000000 */ + + /* + * code=57, hex=0x39, ascii="9" + */ + 0x38, /* 001110 */ + 0x44, /* 010001 */ + 0x44, /* 010001 */ + 0x3C, /* 001111 */ + 0x04, /* 000001 */ + 0x08, /* 000010 */ + 0x30, /* 001100 */ + 0x00, /* 000000 */ + + /* + * code=58, hex=0x3A, ascii=":" + */ + 0x00, /* 000000 */ + 0x00, /* 000000 */ + 0x30, /* 001100 */ + 0x30, /* 001100 */ + 0x00, /* 000000 */ + 0x30, /* 001100 */ + 0x30, /* 001100 */ + 0x00, /* 000000 */ + + /* + * code=59, hex=0x3B, ascii=";" + */ + 0x00, /* 000000 */ + 0x00, /* 000000 */ + 0x30, /* 001100 */ + 0x30, /* 001100 */ + 0x00, /* 000000 */ + 0x30, /* 001100 */ + 0x30, /* 001100 */ + 0x20, /* 001000 */ + + /* + * code=60, hex=0x3C, ascii="<" + */ + 0x08, /* 000010 */ + 0x10, /* 000100 */ + 0x20, /* 001000 */ + 0x40, /* 010000 */ + 0x20, /* 001000 */ + 0x10, /* 000100 */ + 0x08, /* 000010 */ + 0x00, /* 000000 */ + + /* + * code=61, hex=0x3D, ascii="=" + */ + 0x00, /* 000000 */ + 0x00, /* 000000 */ + 0x7C, /* 011111 */ + 0x00, /* 000000 */ + 0x00, /* 000000 */ + 0x7C, /* 011111 */ + 0x00, /* 000000 */ + 0x00, /* 000000 */ + + /* + * code=62, hex=0x3E, ascii=">" + */ + 0x20, /* 001000 */ + 0x10, /* 000100 */ + 0x08, /* 000010 */ + 0x04, /* 000001 */ + 0x08, /* 000010 */ + 0x10, /* 000100 */ + 0x20, /* 001000 */ + 0x00, /* 000000 */ + + /* + * code=63, hex=0x3F, ascii="?" + */ + 0x38, /* 001110 */ + 0x44, /* 010001 */ + 0x04, /* 000001 */ + 0x18, /* 000110 */ + 0x10, /* 000100 */ + 0x00, /* 000000 */ + 0x10, /* 000100 */ + 0x00, /* 000000 */ + + /* + * code=64, hex=0x40, ascii="@" + */ + 0x38, /* 001110 */ + 0x44, /* 010001 */ + 0x5C, /* 010111 */ + 0x54, /* 010101 */ + 0x5C, /* 010111 */ + 0x40, /* 010000 */ + 0x38, /* 001110 */ + 0x00, /* 000000 */ + + /* + * code=65, hex=0x41, ascii="A" + */ + 0x38, /* 001110 */ + 0x44, /* 010001 */ + 0x44, /* 010001 */ + 0x44, /* 010001 */ + 0x7C, /* 011111 */ + 0x44, /* 010001 */ + 0x44, /* 010001 */ + 0x00, /* 000000 */ + + /* + * code=66, hex=0x42, ascii="B" + */ + 0x78, /* 011110 */ + 0x44, /* 010001 */ + 0x44, /* 010001 */ + 0x78, /* 011110 */ + 0x44, /* 010001 */ + 0x44, /* 010001 */ + 0x78, /* 011110 */ + 0x00, /* 000000 */ + + /* + * code=67, hex=0x43, ascii="C" + */ + 0x38, /* 001110 */ + 0x44, /* 010001 */ + 0x40, /* 010000 */ + 0x40, /* 010000 */ + 0x40, /* 010000 */ + 0x44, /* 010001 */ + 0x38, /* 001110 */ + 0x00, /* 000000 */ + + /* + * code=68, hex=0x44, ascii="D" + */ + 0x78, /* 011110 */ + 0x44, /* 010001 */ + 0x44, /* 010001 */ + 0x44, /* 010001 */ + 0x44, /* 010001 */ + 0x44, /* 010001 */ + 0x78, /* 011110 */ + 0x00, /* 000000 */ + + /* + * code=69, hex=0x45, ascii="E" + */ + 0x7C, /* 011111 */ + 0x40, /* 010000 */ + 0x40, /* 010000 */ + 0x78, /* 011110 */ + 0x40, /* 010000 */ + 0x40, /* 010000 */ + 0x7C, /* 011111 */ + 0x00, /* 000000 */ + + /* + * code=70, hex=0x46, ascii="F" + */ + 0x7C, /* 011111 */ + 0x40, /* 010000 */ + 0x40, /* 010000 */ + 0x78, /* 011110 */ + 0x40, /* 010000 */ + 0x40, /* 010000 */ + 0x40, /* 010000 */ + 0x00, /* 000000 */ + + /* + * code=71, hex=0x47, ascii="G" + */ + 0x38, /* 001110 */ + 0x44, /* 010001 */ + 0x40, /* 010000 */ + 0x5C, /* 010111 */ + 0x44, /* 010001 */ + 0x44, /* 010001 */ + 0x3C, /* 001111 */ + 0x00, /* 000000 */ + + /* + * code=72, hex=0x48, ascii="H" + */ + 0x44, /* 010001 */ + 0x44, /* 010001 */ + 0x44, /* 010001 */ + 0x7C, /* 011111 */ + 0x44, /* 010001 */ + 0x44, /* 010001 */ + 0x44, /* 010001 */ + 0x00, /* 000000 */ + + /* + * code=73, hex=0x49, ascii="I" + */ + 0x38, /* 001110 */ + 0x10, /* 000100 */ + 0x10, /* 000100 */ + 0x10, /* 000100 */ + 0x10, /* 000100 */ + 0x10, /* 000100 */ + 0x38, /* 001110 */ + 0x00, /* 000000 */ + + /* + * code=74, hex=0x4A, ascii="J" + */ + 0x04, /* 000001 */ + 0x04, /* 000001 */ + 0x04, /* 000001 */ + 0x04, /* 000001 */ + 0x44, /* 010001 */ + 0x44, /* 010001 */ + 0x38, /* 001110 */ + 0x00, /* 000000 */ + + /* + * code=75, hex=0x4B, ascii="K" + */ + 0x44, /* 010001 */ + 0x48, /* 010010 */ + 0x50, /* 010100 */ + 0x60, /* 011000 */ + 0x50, /* 010100 */ + 0x48, /* 010010 */ + 0x44, /* 010001 */ + 0x00, /* 000000 */ + + /* + * code=76, hex=0x4C, ascii="L" + */ + 0x40, /* 010000 */ + 0x40, /* 010000 */ + 0x40, /* 010000 */ + 0x40, /* 010000 */ + 0x40, /* 010000 */ + 0x40, /* 010000 */ + 0x7C, /* 011111 */ + 0x00, /* 000000 */ + + /* + * code=77, hex=0x4D, ascii="M" + */ + 0x44, /* 010001 */ + 0x6C, /* 011011 */ + 0x54, /* 010101 */ + 0x44, /* 010001 */ + 0x44, /* 010001 */ + 0x44, /* 010001 */ + 0x44, /* 010001 */ + 0x00, /* 000000 */ + + /* + * code=78, hex=0x4E, ascii="N" + */ + 0x44, /* 010001 */ + 0x64, /* 011001 */ + 0x54, /* 010101 */ + 0x4C, /* 010011 */ + 0x44, /* 010001 */ + 0x44, /* 010001 */ + 0x44, /* 010001 */ + 0x00, /* 000000 */ + + /* + * code=79, hex=0x4F, ascii="O" + */ + 0x38, /* 001110 */ + 0x44, /* 010001 */ + 0x44, /* 010001 */ + 0x44, /* 010001 */ + 0x44, /* 010001 */ + 0x44, /* 010001 */ + 0x38, /* 001110 */ + 0x00, /* 000000 */ + + /* + * code=80, hex=0x50, ascii="P" + */ + 0x78, /* 011110 */ + 0x44, /* 010001 */ + 0x44, /* 010001 */ + 0x78, /* 011110 */ + 0x40, /* 010000 */ + 0x40, /* 010000 */ + 0x40, /* 010000 */ + 0x00, /* 000000 */ + + /* + * code=81, hex=0x51, ascii="Q" + */ + 0x38, /* 001110 */ + 0x44, /* 010001 */ + 0x44, /* 010001 */ + 0x44, /* 010001 */ + 0x54, /* 010101 */ + 0x48, /* 010010 */ + 0x34, /* 001101 */ + 0x00, /* 000000 */ + + /* + * code=82, hex=0x52, ascii="R" + */ + 0x78, /* 011110 */ + 0x44, /* 010001 */ + 0x44, /* 010001 */ + 0x78, /* 011110 */ + 0x48, /* 010010 */ + 0x44, /* 010001 */ + 0x44, /* 010001 */ + 0x00, /* 000000 */ + + /* + * code=83, hex=0x53, ascii="S" + */ + 0x38, /* 001110 */ + 0x44, /* 010001 */ + 0x40, /* 010000 */ + 0x38, /* 001110 */ + 0x04, /* 000001 */ + 0x44, /* 010001 */ + 0x38, /* 001110 */ + 0x00, /* 000000 */ + + /* + * code=84, hex=0x54, ascii="T" + */ + 0x7C, /* 011111 */ + 0x10, /* 000100 */ + 0x10, /* 000100 */ + 0x10, /* 000100 */ + 0x10, /* 000100 */ + 0x10, /* 000100 */ + 0x10, /* 000100 */ + 0x00, /* 000000 */ + + /* + * code=85, hex=0x55, ascii="U" + */ + 0x44, /* 010001 */ + 0x44, /* 010001 */ + 0x44, /* 010001 */ + 0x44, /* 010001 */ + 0x44, /* 010001 */ + 0x44, /* 010001 */ + 0x38, /* 001110 */ + 0x00, /* 000000 */ + + /* + * code=86, hex=0x56, ascii="V" + */ + 0x44, /* 010001 */ + 0x44, /* 010001 */ + 0x44, /* 010001 */ + 0x44, /* 010001 */ + 0x44, /* 010001 */ + 0x28, /* 001010 */ + 0x10, /* 000100 */ + 0x00, /* 000000 */ + + /* + * code=87, hex=0x57, ascii="W" + */ + 0x44, /* 010001 */ + 0x44, /* 010001 */ + 0x54, /* 010101 */ + 0x54, /* 010101 */ + 0x54, /* 010101 */ + 0x54, /* 010101 */ + 0x28, /* 001010 */ + 0x00, /* 000000 */ + + /* + * code=88, hex=0x58, ascii="X" + */ + 0x44, /* 010001 */ + 0x44, /* 010001 */ + 0x28, /* 001010 */ + 0x10, /* 000100 */ + 0x28, /* 001010 */ + 0x44, /* 010001 */ + 0x44, /* 010001 */ + 0x00, /* 000000 */ + + /* + * code=89, hex=0x59, ascii="Y" + */ + 0x44, /* 010001 */ + 0x44, /* 010001 */ + 0x44, /* 010001 */ + 0x28, /* 001010 */ + 0x10, /* 000100 */ + 0x10, /* 000100 */ + 0x10, /* 000100 */ + 0x00, /* 000000 */ + + /* + * code=90, hex=0x5A, ascii="Z" + */ + 0x78, /* 011110 */ + 0x08, /* 000010 */ + 0x10, /* 000100 */ + 0x20, /* 001000 */ + 0x40, /* 010000 */ + 0x40, /* 010000 */ + 0x78, /* 011110 */ + 0x00, /* 000000 */ + + /* + * code=91, hex=0x5B, ascii="[" + */ + 0x38, /* 001110 */ + 0x20, /* 001000 */ + 0x20, /* 001000 */ + 0x20, /* 001000 */ + 0x20, /* 001000 */ + 0x20, /* 001000 */ + 0x38, /* 001110 */ + 0x00, /* 000000 */ + + /* + * code=92, hex=0x5C, ascii="\" + */ + 0x00, /* 000000 */ + 0x40, /* 010000 */ + 0x20, /* 001000 */ + 0x10, /* 000100 */ + 0x08, /* 000010 */ + 0x04, /* 000001 */ + 0x00, /* 000000 */ + 0x00, /* 000000 */ + + /* + * code=93, hex=0x5D, ascii="]" + */ + 0x38, /* 001110 */ + 0x08, /* 000010 */ + 0x08, /* 000010 */ + 0x08, /* 000010 */ + 0x08, /* 000010 */ + 0x08, /* 000010 */ + 0x38, /* 001110 */ + 0x00, /* 000000 */ + + /* + * code=94, hex=0x5E, ascii="^" + */ + 0x10, /* 000100 */ + 0x28, /* 001010 */ + 0x44, /* 010001 */ + 0x00, /* 000000 */ + 0x00, /* 000000 */ + 0x00, /* 000000 */ + 0x00, /* 000000 */ + 0x00, /* 000000 */ + + /* + * code=95, hex=0x5F, ascii="_" + */ + 0x00, /* 000000 */ + 0x00, /* 000000 */ + 0x00, /* 000000 */ + 0x00, /* 000000 */ + 0x00, /* 000000 */ + 0x00, /* 000000 */ + 0x00, /* 000000 */ + 0xFC, /* 111111 */ + + /* + * code=96, hex=0x60, ascii="`" + */ + 0x30, /* 001100 */ + 0x30, /* 001100 */ + 0x10, /* 000100 */ + 0x00, /* 000000 */ + 0x00, /* 000000 */ + 0x00, /* 000000 */ + 0x00, /* 000000 */ + 0x00, /* 000000 */ + + /* + * code=97, hex=0x61, ascii="a" + */ + 0x00, /* 000000 */ + 0x00, /* 000000 */ + 0x38, /* 001110 */ + 0x04, /* 000001 */ + 0x3C, /* 001111 */ + 0x44, /* 010001 */ + 0x3C, /* 001111 */ + 0x00, /* 000000 */ + + /* + * code=98, hex=0x62, ascii="b" + */ + 0x40, /* 010000 */ + 0x40, /* 010000 */ + 0x78, /* 011110 */ + 0x44, /* 010001 */ + 0x44, /* 010001 */ + 0x44, /* 010001 */ + 0x78, /* 011110 */ + 0x00, /* 000000 */ + + /* + * code=99, hex=0x63, ascii="c" + */ + 0x00, /* 000000 */ + 0x00, /* 000000 */ + 0x38, /* 001110 */ + 0x44, /* 010001 */ + 0x40, /* 010000 */ + 0x44, /* 010001 */ + 0x38, /* 001110 */ + 0x00, /* 000000 */ + + /* + * code=100, hex=0x64, ascii="d" + */ + 0x04, /* 000001 */ + 0x04, /* 000001 */ + 0x3C, /* 001111 */ + 0x44, /* 010001 */ + 0x44, /* 010001 */ + 0x44, /* 010001 */ + 0x3C, /* 001111 */ + 0x00, /* 000000 */ + + /* + * code=101, hex=0x65, ascii="e" + */ + 0x00, /* 000000 */ + 0x00, /* 000000 */ + 0x38, /* 001110 */ + 0x44, /* 010001 */ + 0x78, /* 011110 */ + 0x40, /* 010000 */ + 0x38, /* 001110 */ + 0x00, /* 000000 */ + + /* + * code=102, hex=0x66, ascii="f" + */ + 0x18, /* 000110 */ + 0x20, /* 001000 */ + 0x20, /* 001000 */ + 0x78, /* 011110 */ + 0x20, /* 001000 */ + 0x20, /* 001000 */ + 0x20, /* 001000 */ + 0x00, /* 000000 */ + + /* + * code=103, hex=0x67, ascii="g" + */ + 0x00, /* 000000 */ + 0x00, /* 000000 */ + 0x3C, /* 001111 */ + 0x44, /* 010001 */ + 0x44, /* 010001 */ + 0x3C, /* 001111 */ + 0x04, /* 000001 */ + 0x38, /* 001110 */ + + /* + * code=104, hex=0x68, ascii="h" + */ + 0x40, /* 010000 */ + 0x40, /* 010000 */ + 0x70, /* 011100 */ + 0x48, /* 010010 */ + 0x48, /* 010010 */ + 0x48, /* 010010 */ + 0x48, /* 010010 */ + 0x00, /* 000000 */ + + /* + * code=105, hex=0x69, ascii="i" + */ + 0x10, /* 000100 */ + 0x00, /* 000000 */ + 0x10, /* 000100 */ + 0x10, /* 000100 */ + 0x10, /* 000100 */ + 0x10, /* 000100 */ + 0x18, /* 000110 */ + 0x00, /* 000000 */ + + /* + * code=106, hex=0x6A, ascii="j" + */ + 0x08, /* 000010 */ + 0x00, /* 000000 */ + 0x18, /* 000110 */ + 0x08, /* 000010 */ + 0x08, /* 000010 */ + 0x08, /* 000010 */ + 0x48, /* 010010 */ + 0x30, /* 001100 */ + + /* + * code=107, hex=0x6B, ascii="k" + */ + 0x40, /* 010000 */ + 0x40, /* 010000 */ + 0x48, /* 010010 */ + 0x50, /* 010100 */ + 0x60, /* 011000 */ + 0x50, /* 010100 */ + 0x48, /* 010010 */ + 0x00, /* 000000 */ + + /* + * code=108, hex=0x6C, ascii="l" + */ + 0x10, /* 000100 */ + 0x10, /* 000100 */ + 0x10, /* 000100 */ + 0x10, /* 000100 */ + 0x10, /* 000100 */ + 0x10, /* 000100 */ + 0x18, /* 000110 */ + 0x00, /* 000000 */ + + /* + * code=109, hex=0x6D, ascii="m" + */ + 0x00, /* 000000 */ + 0x00, /* 000000 */ + 0x68, /* 011010 */ + 0x54, /* 010101 */ + 0x54, /* 010101 */ + 0x44, /* 010001 */ + 0x44, /* 010001 */ + 0x00, /* 000000 */ + + /* + * code=110, hex=0x6E, ascii="n" + */ + 0x00, /* 000000 */ + 0x00, /* 000000 */ + 0x70, /* 011100 */ + 0x48, /* 010010 */ + 0x48, /* 010010 */ + 0x48, /* 010010 */ + 0x48, /* 010010 */ + 0x00, /* 000000 */ + + /* + * code=111, hex=0x6F, ascii="o" + */ + 0x00, /* 000000 */ + 0x00, /* 000000 */ + 0x38, /* 001110 */ + 0x44, /* 010001 */ + 0x44, /* 010001 */ + 0x44, /* 010001 */ + 0x38, /* 001110 */ + 0x00, /* 000000 */ + + /* + * code=112, hex=0x70, ascii="p" + */ + 0x00, /* 000000 */ + 0x00, /* 000000 */ + 0x78, /* 011110 */ + 0x44, /* 010001 */ + 0x44, /* 010001 */ + 0x44, /* 010001 */ + 0x78, /* 011110 */ + 0x40, /* 010000 */ + + /* + * code=113, hex=0x71, ascii="q" + */ + 0x00, /* 000000 */ + 0x00, /* 000000 */ + 0x3C, /* 001111 */ + 0x44, /* 010001 */ + 0x44, /* 010001 */ + 0x44, /* 010001 */ + 0x3C, /* 001111 */ + 0x04, /* 000001 */ + + /* + * code=114, hex=0x72, ascii="r" + */ + 0x00, /* 000000 */ + 0x00, /* 000000 */ + 0x58, /* 010110 */ + 0x24, /* 001001 */ + 0x20, /* 001000 */ + 0x20, /* 001000 */ + 0x70, /* 011100 */ + 0x00, /* 000000 */ + + /* + * code=115, hex=0x73, ascii="s" + */ + 0x00, /* 000000 */ + 0x00, /* 000000 */ + 0x38, /* 001110 */ + 0x40, /* 010000 */ + 0x38, /* 001110 */ + 0x04, /* 000001 */ + 0x38, /* 001110 */ + 0x00, /* 000000 */ + + /* + * code=116, hex=0x74, ascii="t" + */ + 0x00, /* 000000 */ + 0x20, /* 001000 */ + 0x78, /* 011110 */ + 0x20, /* 001000 */ + 0x20, /* 001000 */ + 0x28, /* 001010 */ + 0x10, /* 000100 */ + 0x00, /* 000000 */ + + /* + * code=117, hex=0x75, ascii="u" + */ + 0x00, /* 000000 */ + 0x00, /* 000000 */ + 0x48, /* 010010 */ + 0x48, /* 010010 */ + 0x48, /* 010010 */ + 0x58, /* 010110 */ + 0x28, /* 001010 */ + 0x00, /* 000000 */ + + /* + * code=118, hex=0x76, ascii="v" + */ + 0x00, /* 000000 */ + 0x00, /* 000000 */ + 0x44, /* 010001 */ + 0x44, /* 010001 */ + 0x44, /* 010001 */ + 0x28, /* 001010 */ + 0x10, /* 000100 */ + 0x00, /* 000000 */ + + /* + * code=119, hex=0x77, ascii="w" + */ + 0x00, /* 000000 */ + 0x00, /* 000000 */ + 0x44, /* 010001 */ + 0x44, /* 010001 */ + 0x54, /* 010101 */ + 0x7C, /* 011111 */ + 0x28, /* 001010 */ + 0x00, /* 000000 */ + + /* + * code=120, hex=0x78, ascii="x" + */ + 0x00, /* 000000 */ + 0x00, /* 000000 */ + 0x48, /* 010010 */ + 0x48, /* 010010 */ + 0x30, /* 001100 */ + 0x48, /* 010010 */ + 0x48, /* 010010 */ + 0x00, /* 000000 */ + + /* + * code=121, hex=0x79, ascii="y" + */ + 0x00, /* 000000 */ + 0x00, /* 000000 */ + 0x48, /* 010010 */ + 0x48, /* 010010 */ + 0x48, /* 010010 */ + 0x38, /* 001110 */ + 0x10, /* 000100 */ + 0x60, /* 011000 */ + + /* + * code=122, hex=0x7A, ascii="z" + */ + 0x00, /* 000000 */ + 0x00, /* 000000 */ + 0x78, /* 011110 */ + 0x08, /* 000010 */ + 0x30, /* 001100 */ + 0x40, /* 010000 */ + 0x78, /* 011110 */ + 0x00, /* 000000 */ + + /* + * code=123, hex=0x7B, ascii="{" + */ + 0x18, /* 000110 */ + 0x20, /* 001000 */ + 0x20, /* 001000 */ + 0x60, /* 011000 */ + 0x20, /* 001000 */ + 0x20, /* 001000 */ + 0x18, /* 000110 */ + 0x00, /* 000000 */ + + /* + * code=124, hex=0x7C, ascii="|" + */ + 0x10, /* 000100 */ + 0x10, /* 000100 */ + 0x10, /* 000100 */ + 0x00, /* 000000 */ + 0x10, /* 000100 */ + 0x10, /* 000100 */ + 0x10, /* 000100 */ + 0x00, /* 000000 */ + + /* + * code=125, hex=0x7D, ascii="}" + */ + 0x30, /* 001100 */ + 0x08, /* 000010 */ + 0x08, /* 000010 */ + 0x0C, /* 000011 */ + 0x08, /* 000010 */ + 0x08, /* 000010 */ + 0x30, /* 001100 */ + 0x00, /* 000000 */ + + /* + * code=126, hex=0x7E, ascii="~" + */ + 0x28, /* 001010 */ + 0x50, /* 010100 */ + 0x00, /* 000000 */ + 0x00, /* 000000 */ + 0x00, /* 000000 */ + 0x00, /* 000000 */ + 0x00, /* 000000 */ + 0x00, /* 000000 */ + + /* + * code=127, hex=0x7F, ascii="^?" + */ + 0x10, /* 000100 */ + 0x38, /* 001110 */ + 0x6C, /* 011011 */ + 0x44, /* 010001 */ + 0x44, /* 010001 */ + 0x7C, /* 011111 */ + 0x00, /* 000000 */ + 0x00, /* 000000 */ + + /* + * code=128, hex=0x80, ascii="!^@" + */ + 0x38, /* 001110 */ + 0x44, /* 010001 */ + 0x40, /* 010000 */ + 0x40, /* 010000 */ + 0x44, /* 010001 */ + 0x38, /* 001110 */ + 0x10, /* 000100 */ + 0x30, /* 001100 */ + + /* + * code=129, hex=0x81, ascii="!^A" + */ + 0x48, /* 010010 */ + 0x00, /* 000000 */ + 0x48, /* 010010 */ + 0x48, /* 010010 */ + 0x48, /* 010010 */ + 0x58, /* 010110 */ + 0x28, /* 001010 */ + 0x00, /* 000000 */ + + /* + * code=130, hex=0x82, ascii="!^B" + */ + 0x0C, /* 000011 */ + 0x00, /* 000000 */ + 0x38, /* 001110 */ + 0x44, /* 010001 */ + 0x78, /* 011110 */ + 0x40, /* 010000 */ + 0x38, /* 001110 */ + 0x00, /* 000000 */ + + /* + * code=131, hex=0x83, ascii="!^C" + */ + 0x38, /* 001110 */ + 0x00, /* 000000 */ + 0x38, /* 001110 */ + 0x04, /* 000001 */ + 0x3C, /* 001111 */ + 0x44, /* 010001 */ + 0x3C, /* 001111 */ + 0x00, /* 000000 */ + + /* + * code=132, hex=0x84, ascii="!^D" + */ + 0x28, /* 001010 */ + 0x00, /* 000000 */ + 0x38, /* 001110 */ + 0x04, /* 000001 */ + 0x3C, /* 001111 */ + 0x44, /* 010001 */ + 0x3C, /* 001111 */ + 0x00, /* 000000 */ + + /* + * code=133, hex=0x85, ascii="!^E" + */ + 0x30, /* 001100 */ + 0x00, /* 000000 */ + 0x38, /* 001110 */ + 0x04, /* 000001 */ + 0x3C, /* 001111 */ + 0x44, /* 010001 */ + 0x3C, /* 001111 */ + 0x00, /* 000000 */ + + /* + * code=134, hex=0x86, ascii="!^F" + */ + 0x38, /* 001110 */ + 0x28, /* 001010 */ + 0x38, /* 001110 */ + 0x04, /* 000001 */ + 0x3C, /* 001111 */ + 0x44, /* 010001 */ + 0x3C, /* 001111 */ + 0x00, /* 000000 */ + + /* + * code=135, hex=0x87, ascii="!^G" + */ + 0x00, /* 000000 */ + 0x38, /* 001110 */ + 0x44, /* 010001 */ + 0x40, /* 010000 */ + 0x44, /* 010001 */ + 0x38, /* 001110 */ + 0x10, /* 000100 */ + 0x30, /* 001100 */ + + /* + * code=136, hex=0x88, ascii="!^H" + */ + 0x38, /* 001110 */ + 0x00, /* 000000 */ + 0x38, /* 001110 */ + 0x44, /* 010001 */ + 0x78, /* 011110 */ + 0x40, /* 010000 */ + 0x38, /* 001110 */ + 0x00, /* 000000 */ + + /* + * code=137, hex=0x89, ascii="!^I" + */ + 0x28, /* 001010 */ + 0x00, /* 000000 */ + 0x38, /* 001110 */ + 0x44, /* 010001 */ + 0x78, /* 011110 */ + 0x40, /* 010000 */ + 0x38, /* 001110 */ + 0x00, /* 000000 */ + + /* + * code=138, hex=0x8A, ascii="!^J" + */ + 0x30, /* 001100 */ + 0x00, /* 000000 */ + 0x38, /* 001110 */ + 0x44, /* 010001 */ + 0x78, /* 011110 */ + 0x40, /* 010000 */ + 0x38, /* 001110 */ + 0x00, /* 000000 */ + + /* + * code=139, hex=0x8B, ascii="!^K" + */ + 0x28, /* 001010 */ + 0x00, /* 000000 */ + 0x10, /* 000100 */ + 0x10, /* 000100 */ + 0x10, /* 000100 */ + 0x10, /* 000100 */ + 0x18, /* 000110 */ + 0x00, /* 000000 */ + + /* + * code=140, hex=0x8C, ascii="!^L" + */ + 0x10, /* 000100 */ + 0x28, /* 001010 */ + 0x00, /* 000000 */ + 0x10, /* 000100 */ + 0x10, /* 000100 */ + 0x10, /* 000100 */ + 0x18, /* 000110 */ + 0x00, /* 000000 */ + + /* + * code=141, hex=0x8D, ascii="!^M" + */ + 0x20, /* 001000 */ + 0x00, /* 000000 */ + 0x10, /* 000100 */ + 0x10, /* 000100 */ + 0x10, /* 000100 */ + 0x10, /* 000100 */ + 0x18, /* 000110 */ + 0x00, /* 000000 */ + + /* + * code=142, hex=0x8E, ascii="!^N" + */ + 0x28, /* 001010 */ + 0x00, /* 000000 */ + 0x10, /* 000100 */ + 0x28, /* 001010 */ + 0x44, /* 010001 */ + 0x7C, /* 011111 */ + 0x44, /* 010001 */ + 0x00, /* 000000 */ + + /* + * code=143, hex=0x8F, ascii="!^O" + */ + 0x38, /* 001110 */ + 0x28, /* 001010 */ + 0x38, /* 001110 */ + 0x6C, /* 011011 */ + 0x44, /* 010001 */ + 0x7C, /* 011111 */ + 0x44, /* 010001 */ + 0x00, /* 000000 */ + + /* + * code=144, hex=0x90, ascii="!^P" + */ + 0x0C, /* 000011 */ + 0x00, /* 000000 */ + 0x7C, /* 011111 */ + 0x40, /* 010000 */ + 0x78, /* 011110 */ + 0x40, /* 010000 */ + 0x7C, /* 011111 */ + 0x00, /* 000000 */ + + /* + * code=145, hex=0x91, ascii="!^Q" + */ + 0x00, /* 000000 */ + 0x00, /* 000000 */ + 0x78, /* 011110 */ + 0x14, /* 000101 */ + 0x7C, /* 011111 */ + 0x50, /* 010100 */ + 0x3C, /* 001111 */ + 0x00, /* 000000 */ + + /* + * code=146, hex=0x92, ascii="!^R" + */ + 0x3C, /* 001111 */ + 0x50, /* 010100 */ + 0x50, /* 010100 */ + 0x7C, /* 011111 */ + 0x50, /* 010100 */ + 0x50, /* 010100 */ + 0x5C, /* 010111 */ + 0x00, /* 000000 */ + + /* + * code=147, hex=0x93, ascii="!^S" + */ + 0x38, /* 001110 */ + 0x00, /* 000000 */ + 0x30, /* 001100 */ + 0x48, /* 010010 */ + 0x48, /* 010010 */ + 0x48, /* 010010 */ + 0x30, /* 001100 */ + 0x00, /* 000000 */ + + /* + * code=148, hex=0x94, ascii="!^T" + */ + 0x28, /* 001010 */ + 0x00, /* 000000 */ + 0x30, /* 001100 */ + 0x48, /* 010010 */ + 0x48, /* 010010 */ + 0x48, /* 010010 */ + 0x30, /* 001100 */ + 0x00, /* 000000 */ + + /* + * code=149, hex=0x95, ascii="!^U" + */ + 0x60, /* 011000 */ + 0x00, /* 000000 */ + 0x30, /* 001100 */ + 0x48, /* 010010 */ + 0x48, /* 010010 */ + 0x48, /* 010010 */ + 0x30, /* 001100 */ + 0x00, /* 000000 */ + + /* + * code=150, hex=0x96, ascii="!^V" + */ + 0x38, /* 001110 */ + 0x00, /* 000000 */ + 0x48, /* 010010 */ + 0x48, /* 010010 */ + 0x48, /* 010010 */ + 0x58, /* 010110 */ + 0x28, /* 001010 */ + 0x00, /* 000000 */ + + /* + * code=151, hex=0x97, ascii="!^W" + */ + 0x60, /* 011000 */ + 0x00, /* 000000 */ + 0x48, /* 010010 */ + 0x48, /* 010010 */ + 0x48, /* 010010 */ + 0x58, /* 010110 */ + 0x28, /* 001010 */ + 0x00, /* 000000 */ + + /* + * code=152, hex=0x98, ascii="!^X" + */ + 0x28, /* 001010 */ + 0x00, /* 000000 */ + 0x48, /* 010010 */ + 0x48, /* 010010 */ + 0x48, /* 010010 */ + 0x38, /* 001110 */ + 0x10, /* 000100 */ + 0x60, /* 011000 */ + + /* + * code=153, hex=0x99, ascii="!^Y" + */ + 0x48, /* 010010 */ + 0x30, /* 001100 */ + 0x48, /* 010010 */ + 0x48, /* 010010 */ + 0x48, /* 010010 */ + 0x48, /* 010010 */ + 0x30, /* 001100 */ + 0x00, /* 000000 */ + + /* + * code=154, hex=0x9A, ascii="!^Z" + */ + 0x28, /* 001010 */ + 0x00, /* 000000 */ + 0x48, /* 010010 */ + 0x48, /* 010010 */ + 0x48, /* 010010 */ + 0x48, /* 010010 */ + 0x30, /* 001100 */ + 0x00, /* 000000 */ + + /* + * code=155, hex=0x9B, ascii="!^[" + */ + 0x00, /* 000000 */ + 0x10, /* 000100 */ + 0x38, /* 001110 */ + 0x40, /* 010000 */ + 0x40, /* 010000 */ + 0x38, /* 001110 */ + 0x10, /* 000100 */ + 0x00, /* 000000 */ + + /* + * code=156, hex=0x9C, ascii="!^\" + */ + 0x18, /* 000110 */ + 0x24, /* 001001 */ + 0x20, /* 001000 */ + 0x78, /* 011110 */ + 0x20, /* 001000 */ + 0x24, /* 001001 */ + 0x5C, /* 010111 */ + 0x00, /* 000000 */ + + /* + * code=157, hex=0x9D, ascii="!^]" + */ + 0x44, /* 010001 */ + 0x28, /* 001010 */ + 0x10, /* 000100 */ + 0x7C, /* 011111 */ + 0x10, /* 000100 */ + 0x7C, /* 011111 */ + 0x10, /* 000100 */ + 0x00, /* 000000 */ + + /* + * code=158, hex=0x9E, ascii="!^^" + */ + 0x60, /* 011000 */ + 0x50, /* 010100 */ + 0x50, /* 010100 */ + 0x68, /* 011010 */ + 0x5C, /* 010111 */ + 0x48, /* 010010 */ + 0x48, /* 010010 */ + 0x00, /* 000000 */ + + /* + * code=159, hex=0x9F, ascii="!^_" + */ + 0x08, /* 000010 */ + 0x14, /* 000101 */ + 0x10, /* 000100 */ + 0x38, /* 001110 */ + 0x10, /* 000100 */ + 0x10, /* 000100 */ + 0x50, /* 010100 */ + 0x20, /* 001000 */ + + /* + * code=160, hex=0xA0, ascii="! " + */ + 0x18, /* 000110 */ + 0x00, /* 000000 */ + 0x38, /* 001110 */ + 0x04, /* 000001 */ + 0x3C, /* 001111 */ + 0x44, /* 010001 */ + 0x3C, /* 001111 */ + 0x00, /* 000000 */ + + /* + * code=161, hex=0xA1, ascii="!!" + */ + 0x18, /* 000110 */ + 0x00, /* 000000 */ + 0x10, /* 000100 */ + 0x10, /* 000100 */ + 0x10, /* 000100 */ + 0x10, /* 000100 */ + 0x18, /* 000110 */ + 0x00, /* 000000 */ + + /* + * code=162, hex=0xA2, ascii="!"" + */ + 0x18, /* 000110 */ + 0x00, /* 000000 */ + 0x30, /* 001100 */ + 0x48, /* 010010 */ + 0x48, /* 010010 */ + 0x48, /* 010010 */ + 0x30, /* 001100 */ + 0x00, /* 000000 */ + + /* + * code=163, hex=0xA3, ascii="!#" + */ + 0x18, /* 000110 */ + 0x00, /* 000000 */ + 0x48, /* 010010 */ + 0x48, /* 010010 */ + 0x48, /* 010010 */ + 0x58, /* 010110 */ + 0x28, /* 001010 */ + 0x00, /* 000000 */ + + /* + * code=164, hex=0xA4, ascii="!$" + */ + 0x28, /* 001010 */ + 0x50, /* 010100 */ + 0x00, /* 000000 */ + 0x70, /* 011100 */ + 0x48, /* 010010 */ + 0x48, /* 010010 */ + 0x48, /* 010010 */ + 0x00, /* 000000 */ + + /* + * code=165, hex=0xA5, ascii="!%" + */ + 0x28, /* 001010 */ + 0x50, /* 010100 */ + 0x00, /* 000000 */ + 0x48, /* 010010 */ + 0x68, /* 011010 */ + 0x58, /* 010110 */ + 0x48, /* 010010 */ + 0x00, /* 000000 */ + + /* + * code=166, hex=0xA6, ascii="!&" + */ + 0x38, /* 001110 */ + 0x04, /* 000001 */ + 0x3C, /* 001111 */ + 0x44, /* 010001 */ + 0x3C, /* 001111 */ + 0x00, /* 000000 */ + 0x3C, /* 001111 */ + 0x00, /* 000000 */ + + /* + * code=167, hex=0xA7, ascii="!'" + */ + 0x30, /* 001100 */ + 0x48, /* 010010 */ + 0x48, /* 010010 */ + 0x48, /* 010010 */ + 0x30, /* 001100 */ + 0x00, /* 000000 */ + 0x78, /* 011110 */ + 0x00, /* 000000 */ + + /* + * code=168, hex=0xA8, ascii="!(" + */ + 0x10, /* 000100 */ + 0x00, /* 000000 */ + 0x10, /* 000100 */ + 0x30, /* 001100 */ + 0x40, /* 010000 */ + 0x44, /* 010001 */ + 0x38, /* 001110 */ + 0x00, /* 000000 */ + + /* + * code=169, hex=0xA9, ascii="!)" + */ + 0x00, /* 000000 */ + 0x00, /* 000000 */ + 0x7C, /* 011111 */ + 0x40, /* 010000 */ + 0x40, /* 010000 */ + 0x40, /* 010000 */ + 0x00, /* 000000 */ + 0x00, /* 000000 */ + + /* + * code=170, hex=0xAA, ascii="!*" + */ + 0x00, /* 000000 */ + 0x00, /* 000000 */ + 0xFC, /* 111111 */ + 0x04, /* 000001 */ + 0x04, /* 000001 */ + 0x00, /* 000000 */ + 0x00, /* 000000 */ + 0x00, /* 000000 */ + + /* + * code=171, hex=0xAB, ascii="!+" + */ + 0x40, /* 010000 */ + 0x48, /* 010010 */ + 0x50, /* 010100 */ + 0x38, /* 001110 */ + 0x44, /* 010001 */ + 0x08, /* 000010 */ + 0x1C, /* 000111 */ + 0x00, /* 000000 */ + + /* + * code=172, hex=0xAC, ascii="!," + */ + 0x40, /* 010000 */ + 0x48, /* 010010 */ + 0x50, /* 010100 */ + 0x2C, /* 001011 */ + 0x54, /* 010101 */ + 0x1C, /* 000111 */ + 0x04, /* 000001 */ + 0x00, /* 000000 */ + + /* + * code=173, hex=0xAD, ascii="!-" + */ + 0x10, /* 000100 */ + 0x00, /* 000000 */ + 0x10, /* 000100 */ + 0x10, /* 000100 */ + 0x38, /* 001110 */ + 0x38, /* 001110 */ + 0x10, /* 000100 */ + 0x00, /* 000000 */ + + /* + * code=174, hex=0xAE, ascii="!." + */ + 0x00, /* 000000 */ + 0x00, /* 000000 */ + 0x24, /* 001001 */ + 0x48, /* 010010 */ + 0x24, /* 001001 */ + 0x00, /* 000000 */ + 0x00, /* 000000 */ + 0x00, /* 000000 */ + + /* + * code=175, hex=0xAF, ascii="!/" + */ + 0x00, /* 000000 */ + 0x00, /* 000000 */ + 0x48, /* 010010 */ + 0x24, /* 001001 */ + 0x48, /* 010010 */ + 0x00, /* 000000 */ + 0x00, /* 000000 */ + 0x00, /* 000000 */ + + /* + * code=176, hex=0xB0, ascii="!0" + */ + 0x54, /* 010101 */ + 0x00, /* 000000 */ + 0xA8, /* 101010 */ + 0x00, /* 000000 */ + 0x54, /* 010101 */ + 0x00, /* 000000 */ + 0xA8, /* 101010 */ + 0x00, /* 000000 */ + + /* + * code=177, hex=0xB1, ascii="!1" + */ + 0x54, /* 010101 */ + 0xA8, /* 101010 */ + 0x54, /* 010101 */ + 0xA8, /* 101010 */ + 0x54, /* 010101 */ + 0xA8, /* 101010 */ + 0x54, /* 010101 */ + 0xA8, /* 101010 */ + + /* + * code=178, hex=0xB2, ascii="!2" + */ + 0xA8, /* 101010 */ + 0xFC, /* 111111 */ + 0x54, /* 010101 */ + 0xFC, /* 111111 */ + 0xA8, /* 101010 */ + 0xFC, /* 111111 */ + 0x54, /* 010101 */ + 0xFC, /* 111111 */ + + /* + * code=179, hex=0xB3, ascii="!3" + */ + 0x10, /* 000100 */ + 0x10, /* 000100 */ + 0x10, /* 000100 */ + 0x10, /* 000100 */ + 0x10, /* 000100 */ + 0x10, /* 000100 */ + 0x10, /* 000100 */ + 0x10, /* 000100 */ + + /* + * code=180, hex=0xB4, ascii="!4" + */ + 0x10, /* 000100 */ + 0x10, /* 000100 */ + 0x10, /* 000100 */ + 0xF0, /* 111100 */ + 0x10, /* 000100 */ + 0x10, /* 000100 */ + 0x10, /* 000100 */ + 0x10, /* 000100 */ + + /* + * code=181, hex=0xB5, ascii="!5" + */ + 0x10, /* 000100 */ + 0xF0, /* 111100 */ + 0x10, /* 000100 */ + 0xF0, /* 111100 */ + 0x10, /* 000100 */ + 0x10, /* 000100 */ + 0x10, /* 000100 */ + 0x10, /* 000100 */ + + /* + * code=182, hex=0xB6, ascii="!6" + */ + 0x50, /* 010100 */ + 0x50, /* 010100 */ + 0x50, /* 010100 */ + 0xD0, /* 110100 */ + 0x50, /* 010100 */ + 0x50, /* 010100 */ + 0x50, /* 010100 */ + 0x50, /* 010100 */ + + /* + * code=183, hex=0xB7, ascii="!7" + */ + 0x00, /* 000000 */ + 0x00, /* 000000 */ + 0x00, /* 000000 */ + 0xF0, /* 111100 */ + 0x50, /* 010100 */ + 0x50, /* 010100 */ + 0x50, /* 010100 */ + 0x50, /* 010100 */ + + /* + * code=184, hex=0xB8, ascii="!8" + */ + 0x00, /* 000000 */ + 0xF0, /* 111100 */ + 0x10, /* 000100 */ + 0xF0, /* 111100 */ + 0x10, /* 000100 */ + 0x10, /* 000100 */ + 0x10, /* 000100 */ + 0x10, /* 000100 */ + + /* + * code=185, hex=0xB9, ascii="!9" + */ + 0x50, /* 010100 */ + 0xD0, /* 110100 */ + 0x10, /* 000100 */ + 0xD0, /* 110100 */ + 0x50, /* 010100 */ + 0x50, /* 010100 */ + 0x50, /* 010100 */ + 0x50, /* 010100 */ + + /* + * code=186, hex=0xBA, ascii="!:" + */ + 0x50, /* 010100 */ + 0x50, /* 010100 */ + 0x50, /* 010100 */ + 0x50, /* 010100 */ + 0x50, /* 010100 */ + 0x50, /* 010100 */ + 0x50, /* 010100 */ + 0x50, /* 010100 */ + + /* + * code=187, hex=0xBB, ascii="!;" + */ + 0x00, /* 000000 */ + 0xF0, /* 111100 */ + 0x10, /* 000100 */ + 0xD0, /* 110100 */ + 0x50, /* 010100 */ + 0x50, /* 010100 */ + 0x50, /* 010100 */ + 0x50, /* 010100 */ + + /* + * code=188, hex=0xBC, ascii="!<" + */ + 0x50, /* 010100 */ + 0xD0, /* 110100 */ + 0x10, /* 000100 */ + 0xF0, /* 111100 */ + 0x00, /* 000000 */ + 0x00, /* 000000 */ + 0x00, /* 000000 */ + 0x00, /* 000000 */ + + /* + * code=189, hex=0xBD, ascii="!=" + */ + 0x50, /* 010100 */ + 0x50, /* 010100 */ + 0x50, /* 010100 */ + 0xF0, /* 111100 */ + 0x00, /* 000000 */ + 0x00, /* 000000 */ + 0x00, /* 000000 */ + 0x00, /* 000000 */ + + /* + * code=190, hex=0xBE, ascii="!>" + */ + 0x10, /* 000100 */ + 0xF0, /* 111100 */ + 0x10, /* 000100 */ + 0xF0, /* 111100 */ + 0x00, /* 000000 */ + 0x00, /* 000000 */ + 0x00, /* 000000 */ + 0x00, /* 000000 */ + + /* + * code=191, hex=0xBF, ascii="!?" + */ + 0x00, /* 000000 */ + 0x00, /* 000000 */ + 0x00, /* 000000 */ + 0xF0, /* 111100 */ + 0x10, /* 000100 */ + 0x10, /* 000100 */ + 0x10, /* 000100 */ + 0x10, /* 000100 */ + + /* + * code=192, hex=0xC0, ascii="!@" + */ + 0x10, /* 000100 */ + 0x10, /* 000100 */ + 0x10, /* 000100 */ + 0x1C, /* 000111 */ + 0x00, /* 000000 */ + 0x00, /* 000000 */ + 0x00, /* 000000 */ + 0x00, /* 000000 */ + + /* + * code=193, hex=0xC1, ascii="!A" + */ + 0x10, /* 000100 */ + 0x10, /* 000100 */ + 0x10, /* 000100 */ + 0xFC, /* 111111 */ + 0x00, /* 000000 */ + 0x00, /* 000000 */ + 0x00, /* 000000 */ + 0x00, /* 000000 */ + + /* + * code=194, hex=0xC2, ascii="!B" + */ + 0x00, /* 000000 */ + 0x00, /* 000000 */ + 0x00, /* 000000 */ + 0xFC, /* 111111 */ + 0x10, /* 000100 */ + 0x10, /* 000100 */ + 0x10, /* 000100 */ + 0x10, /* 000100 */ + + /* + * code=195, hex=0xC3, ascii="!C" + */ + 0x10, /* 000100 */ + 0x10, /* 000100 */ + 0x10, /* 000100 */ + 0x1C, /* 000111 */ + 0x10, /* 000100 */ + 0x10, /* 000100 */ + 0x10, /* 000100 */ + 0x10, /* 000100 */ + + /* + * code=196, hex=0xC4, ascii="!D" + */ + 0x00, /* 000000 */ + 0x00, /* 000000 */ + 0x00, /* 000000 */ + 0xFC, /* 111111 */ + 0x00, /* 000000 */ + 0x00, /* 000000 */ + 0x00, /* 000000 */ + 0x00, /* 000000 */ + + /* + * code=197, hex=0xC5, ascii="!E" + */ + 0x10, /* 000100 */ + 0x10, /* 000100 */ + 0x10, /* 000100 */ + 0xFC, /* 111111 */ + 0x10, /* 000100 */ + 0x10, /* 000100 */ + 0x10, /* 000100 */ + 0x10, /* 000100 */ + + /* + * code=198, hex=0xC6, ascii="!F" + */ + 0x10, /* 000100 */ + 0x1C, /* 000111 */ + 0x10, /* 000100 */ + 0x1C, /* 000111 */ + 0x10, /* 000100 */ + 0x10, /* 000100 */ + 0x10, /* 000100 */ + 0x10, /* 000100 */ + + /* + * code=199, hex=0xC7, ascii="!G" + */ + 0x50, /* 010100 */ + 0x50, /* 010100 */ + 0x50, /* 010100 */ + 0x5C, /* 010111 */ + 0x50, /* 010100 */ + 0x50, /* 010100 */ + 0x50, /* 010100 */ + 0x50, /* 010100 */ + + /* + * code=200, hex=0xC8, ascii="!H" + */ + 0x50, /* 010100 */ + 0x5C, /* 010111 */ + 0x40, /* 010000 */ + 0x7C, /* 011111 */ + 0x00, /* 000000 */ + 0x00, /* 000000 */ + 0x00, /* 000000 */ + 0x00, /* 000000 */ + + /* + * code=201, hex=0xC9, ascii="!I" + */ + 0x00, /* 000000 */ + 0x7C, /* 011111 */ + 0x40, /* 010000 */ + 0x5C, /* 010111 */ + 0x50, /* 010100 */ + 0x50, /* 010100 */ + 0x50, /* 010100 */ + 0x50, /* 010100 */ + + /* + * code=202, hex=0xCA, ascii="!J" + */ + 0x50, /* 010100 */ + 0xDC, /* 110111 */ + 0x00, /* 000000 */ + 0xFC, /* 111111 */ + 0x00, /* 000000 */ + 0x00, /* 000000 */ + 0x00, /* 000000 */ + 0x00, /* 000000 */ + + /* + * code=203, hex=0xCB, ascii="!K" + */ + 0x00, /* 000000 */ + 0xFC, /* 111111 */ + 0x00, /* 000000 */ + 0xDC, /* 110111 */ + 0x50, /* 010100 */ + 0x50, /* 010100 */ + 0x50, /* 010100 */ + 0x50, /* 010100 */ + + /* + * code=204, hex=0xCC, ascii="!L" + */ + 0x50, /* 010100 */ + 0x5C, /* 010111 */ + 0x40, /* 010000 */ + 0x5C, /* 010111 */ + 0x50, /* 010100 */ + 0x50, /* 010100 */ + 0x50, /* 010100 */ + 0x50, /* 010100 */ + + /* + * code=205, hex=0xCD, ascii="!M" + */ + 0x00, /* 000000 */ + 0xFC, /* 111111 */ + 0x00, /* 000000 */ + 0xFC, /* 111111 */ + 0x00, /* 000000 */ + 0x00, /* 000000 */ + 0x00, /* 000000 */ + 0x00, /* 000000 */ + + /* + * code=206, hex=0xCE, ascii="!N" + */ + 0x50, /* 010100 */ + 0xDC, /* 110111 */ + 0x00, /* 000000 */ + 0xDC, /* 110111 */ + 0x50, /* 010100 */ + 0x50, /* 010100 */ + 0x50, /* 010100 */ + 0x50, /* 010100 */ + + /* + * code=207, hex=0xCF, ascii="!O" + */ + 0x10, /* 000100 */ + 0xFC, /* 111111 */ + 0x00, /* 000000 */ + 0xFC, /* 111111 */ + 0x00, /* 000000 */ + 0x00, /* 000000 */ + 0x00, /* 000000 */ + 0x00, /* 000000 */ + + /* + * code=208, hex=0xD0, ascii="!P" + */ + 0x50, /* 010100 */ + 0x50, /* 010100 */ + 0x50, /* 010100 */ + 0xFC, /* 111111 */ + 0x00, /* 000000 */ + 0x00, /* 000000 */ + 0x00, /* 000000 */ + 0x00, /* 000000 */ + + /* + * code=209, hex=0xD1, ascii="!Q" + */ + 0x00, /* 000000 */ + 0xFC, /* 111111 */ + 0x00, /* 000000 */ + 0xFC, /* 111111 */ + 0x10, /* 000100 */ + 0x10, /* 000100 */ + 0x10, /* 000100 */ + 0x10, /* 000100 */ + + /* + * code=210, hex=0xD2, ascii="!R" + */ + 0x00, /* 000000 */ + 0x00, /* 000000 */ + 0x00, /* 000000 */ + 0xFC, /* 111111 */ + 0x50, /* 010100 */ + 0x50, /* 010100 */ + 0x50, /* 010100 */ + 0x50, /* 010100 */ + + /* + * code=211, hex=0xD3, ascii="!S" + */ + 0x50, /* 010100 */ + 0x50, /* 010100 */ + 0x50, /* 010100 */ + 0x7C, /* 011111 */ + 0x00, /* 000000 */ + 0x00, /* 000000 */ + 0x00, /* 000000 */ + 0x00, /* 000000 */ + + /* + * code=212, hex=0xD4, ascii="!T" + */ + 0x10, /* 000100 */ + 0x1C, /* 000111 */ + 0x10, /* 000100 */ + 0x1C, /* 000111 */ + 0x00, /* 000000 */ + 0x00, /* 000000 */ + 0x00, /* 000000 */ + 0x00, /* 000000 */ + + /* + * code=213, hex=0xD5, ascii="!U" + */ + 0x00, /* 000000 */ + 0x1C, /* 000111 */ + 0x10, /* 000100 */ + 0x1C, /* 000111 */ + 0x10, /* 000100 */ + 0x10, /* 000100 */ + 0x10, /* 000100 */ + 0x10, /* 000100 */ + + /* + * code=214, hex=0xD6, ascii="!V" + */ + 0x00, /* 000000 */ + 0x00, /* 000000 */ + 0x00, /* 000000 */ + 0x7C, /* 011111 */ + 0x50, /* 010100 */ + 0x50, /* 010100 */ + 0x50, /* 010100 */ + 0x50, /* 010100 */ + + /* + * code=215, hex=0xD7, ascii="!W" + */ + 0x50, /* 010100 */ + 0x50, /* 010100 */ + 0x50, /* 010100 */ + 0xDC, /* 110111 */ + 0x50, /* 010100 */ + 0x50, /* 010100 */ + 0x50, /* 010100 */ + 0x50, /* 010100 */ + + /* + * code=216, hex=0xD8, ascii="!X" + */ + 0x10, /* 000100 */ + 0xFC, /* 111111 */ + 0x00, /* 000000 */ + 0xFC, /* 111111 */ + 0x10, /* 000100 */ + 0x10, /* 000100 */ + 0x10, /* 000100 */ + 0x10, /* 000100 */ + + /* + * code=217, hex=0xD9, ascii="!Y" + */ + 0x10, /* 000100 */ + 0x10, /* 000100 */ + 0x10, /* 000100 */ + 0xF0, /* 111100 */ + 0x00, /* 000000 */ + 0x00, /* 000000 */ + 0x00, /* 000000 */ + 0x00, /* 000000 */ + + /* + * code=218, hex=0xDA, ascii="!Z" + */ + 0x00, /* 000000 */ + 0x00, /* 000000 */ + 0x00, /* 000000 */ + 0x1C, /* 000111 */ + 0x10, /* 000100 */ + 0x10, /* 000100 */ + 0x10, /* 000100 */ + 0x10, /* 000100 */ + + /* + * code=219, hex=0xDB, ascii="![" + */ + 0xFC, /* 111111 */ + 0xFC, /* 111111 */ + 0xFC, /* 111111 */ + 0xFC, /* 111111 */ + 0xFC, /* 111111 */ + 0xFC, /* 111111 */ + 0xFC, /* 111111 */ + 0xFC, /* 111111 */ + + /* + * code=220, hex=0xDC, ascii="!\" + */ + 0x00, /* 000000 */ + 0x00, /* 000000 */ + 0x00, /* 000000 */ + 0x00, /* 000000 */ + 0xFC, /* 111111 */ + 0xFC, /* 111111 */ + 0xFC, /* 111111 */ + 0xFC, /* 111111 */ + + /* + * code=221, hex=0xDD, ascii="!]" + */ + 0xE0, /* 111000 */ + 0xE0, /* 111000 */ + 0xE0, /* 111000 */ + 0xE0, /* 111000 */ + 0xE0, /* 111000 */ + 0xE0, /* 111000 */ + 0xE0, /* 111000 */ + 0xE0, /* 111000 */ + + /* + * code=222, hex=0xDE, ascii="!^" + */ + 0x1C, /* 000111 */ + 0x1C, /* 000111 */ + 0x1C, /* 000111 */ + 0x1C, /* 000111 */ + 0x1C, /* 000111 */ + 0x1C, /* 000111 */ + 0x1C, /* 000111 */ + 0x1C, /* 000111 */ + + /* + * code=223, hex=0xDF, ascii="!_" + */ + 0xFC, /* 111111 */ + 0xFC, /* 111111 */ + 0xFC, /* 111111 */ + 0xFC, /* 111111 */ + 0x00, /* 000000 */ + 0x00, /* 000000 */ + 0x00, /* 000000 */ + 0x00, /* 000000 */ + + /* + * code=224, hex=0xE0, ascii="!`" + */ + 0x00, /* 000000 */ + 0x00, /* 000000 */ + 0x34, /* 001101 */ + 0x48, /* 010010 */ + 0x48, /* 010010 */ + 0x34, /* 001101 */ + 0x00, /* 000000 */ + 0x00, /* 000000 */ + + /* + * code=225, hex=0xE1, ascii="!a" + */ + 0x00, /* 000000 */ + 0x70, /* 011100 */ + 0x48, /* 010010 */ + 0x70, /* 011100 */ + 0x48, /* 010010 */ + 0x48, /* 010010 */ + 0x70, /* 011100 */ + 0x40, /* 010000 */ + + /* + * code=226, hex=0xE2, ascii="!b" + */ + 0x78, /* 011110 */ + 0x48, /* 010010 */ + 0x40, /* 010000 */ + 0x40, /* 010000 */ + 0x40, /* 010000 */ + 0x40, /* 010000 */ + 0x40, /* 010000 */ + 0x00, /* 000000 */ + + /* + * code=227, hex=0xE3, ascii="!c" + */ + 0x00, /* 000000 */ + 0x7C, /* 011111 */ + 0x28, /* 001010 */ + 0x28, /* 001010 */ + 0x28, /* 001010 */ + 0x28, /* 001010 */ + 0x28, /* 001010 */ + 0x00, /* 000000 */ + + /* + * code=228, hex=0xE4, ascii="!d" + */ + 0x78, /* 011110 */ + 0x48, /* 010010 */ + 0x20, /* 001000 */ + 0x10, /* 000100 */ + 0x20, /* 001000 */ + 0x48, /* 010010 */ + 0x78, /* 011110 */ + 0x00, /* 000000 */ + + /* + * code=229, hex=0xE5, ascii="!e" + */ + 0x00, /* 000000 */ + 0x00, /* 000000 */ + 0x3C, /* 001111 */ + 0x48, /* 010010 */ + 0x48, /* 010010 */ + 0x30, /* 001100 */ + 0x00, /* 000000 */ + 0x00, /* 000000 */ + + /* + * code=230, hex=0xE6, ascii="!f" + */ + 0x00, /* 000000 */ + 0x00, /* 000000 */ + 0x48, /* 010010 */ + 0x48, /* 010010 */ + 0x48, /* 010010 */ + 0x70, /* 011100 */ + 0x40, /* 010000 */ + 0x40, /* 010000 */ + + /* + * code=231, hex=0xE7, ascii="!g" + */ + 0x00, /* 000000 */ + 0x00, /* 000000 */ + 0x28, /* 001010 */ + 0x50, /* 010100 */ + 0x10, /* 000100 */ + 0x10, /* 000100 */ + 0x10, /* 000100 */ + 0x00, /* 000000 */ + + /* + * code=232, hex=0xE8, ascii="!h" + */ + 0x38, /* 001110 */ + 0x10, /* 000100 */ + 0x38, /* 001110 */ + 0x44, /* 010001 */ + 0x38, /* 001110 */ + 0x10, /* 000100 */ + 0x38, /* 001110 */ + 0x00, /* 000000 */ + + /* + * code=233, hex=0xE9, ascii="!i" + */ + 0x30, /* 001100 */ + 0x48, /* 010010 */ + 0x48, /* 010010 */ + 0x78, /* 011110 */ + 0x48, /* 010010 */ + 0x48, /* 010010 */ + 0x30, /* 001100 */ + 0x00, /* 000000 */ + + /* + * code=234, hex=0xEA, ascii="!j" + */ + 0x00, /* 000000 */ + 0x38, /* 001110 */ + 0x44, /* 010001 */ + 0x44, /* 010001 */ + 0x28, /* 001010 */ + 0x28, /* 001010 */ + 0x6C, /* 011011 */ + 0x00, /* 000000 */ + + /* + * code=235, hex=0xEB, ascii="!k" + */ + 0x30, /* 001100 */ + 0x40, /* 010000 */ + 0x20, /* 001000 */ + 0x10, /* 000100 */ + 0x38, /* 001110 */ + 0x48, /* 010010 */ + 0x30, /* 001100 */ + 0x00, /* 000000 */ + + /* + * code=236, hex=0xEC, ascii="!l" + */ + 0x00, /* 000000 */ + 0x00, /* 000000 */ + 0x28, /* 001010 */ + 0x54, /* 010101 */ + 0x54, /* 010101 */ + 0x28, /* 001010 */ + 0x00, /* 000000 */ + 0x00, /* 000000 */ + + /* + * code=237, hex=0xED, ascii="!m" + */ + 0x00, /* 000000 */ + 0x10, /* 000100 */ + 0x38, /* 001110 */ + 0x54, /* 010101 */ + 0x54, /* 010101 */ + 0x38, /* 001110 */ + 0x10, /* 000100 */ + 0x00, /* 000000 */ + + /* + * code=238, hex=0xEE, ascii="!n" + */ + 0x00, /* 000000 */ + 0x38, /* 001110 */ + 0x40, /* 010000 */ + 0x78, /* 011110 */ + 0x40, /* 010000 */ + 0x38, /* 001110 */ + 0x00, /* 000000 */ + 0x00, /* 000000 */ + + /* + * code=239, hex=0xEF, ascii="!o" + */ + 0x00, /* 000000 */ + 0x30, /* 001100 */ + 0x48, /* 010010 */ + 0x48, /* 010010 */ + 0x48, /* 010010 */ + 0x48, /* 010010 */ + 0x00, /* 000000 */ + 0x00, /* 000000 */ + + /* + * code=240, hex=0xF0, ascii="!p" + */ + 0x00, /* 000000 */ + 0x78, /* 011110 */ + 0x00, /* 000000 */ + 0x78, /* 011110 */ + 0x00, /* 000000 */ + 0x78, /* 011110 */ + 0x00, /* 000000 */ + 0x00, /* 000000 */ + + /* + * code=241, hex=0xF1, ascii="!q" + */ + 0x00, /* 000000 */ + 0x10, /* 000100 */ + 0x38, /* 001110 */ + 0x10, /* 000100 */ + 0x00, /* 000000 */ + 0x38, /* 001110 */ + 0x00, /* 000000 */ + 0x00, /* 000000 */ + + /* + * code=242, hex=0xF2, ascii="!r" + */ + 0x40, /* 010000 */ + 0x30, /* 001100 */ + 0x08, /* 000010 */ + 0x30, /* 001100 */ + 0x40, /* 010000 */ + 0x00, /* 000000 */ + 0x78, /* 011110 */ + 0x00, /* 000000 */ + + /* + * code=243, hex=0xF3, ascii="!s" + */ + 0x08, /* 000010 */ + 0x30, /* 001100 */ + 0x40, /* 010000 */ + 0x30, /* 001100 */ + 0x08, /* 000010 */ + 0x00, /* 000000 */ + 0x78, /* 011110 */ + 0x00, /* 000000 */ + + /* + * code=244, hex=0xF4, ascii="!t" + */ + 0x00, /* 000000 */ + 0x08, /* 000010 */ + 0x14, /* 000101 */ + 0x10, /* 000100 */ + 0x10, /* 000100 */ + 0x10, /* 000100 */ + 0x10, /* 000100 */ + 0x10, /* 000100 */ + + /* + * code=245, hex=0xF5, ascii="!u" + */ + 0x10, /* 000100 */ + 0x10, /* 000100 */ + 0x10, /* 000100 */ + 0x10, /* 000100 */ + 0x10, /* 000100 */ + 0x50, /* 010100 */ + 0x20, /* 001000 */ + 0x00, /* 000000 */ + + /* + * code=246, hex=0xF6, ascii="!v" + */ + 0x00, /* 000000 */ + 0x10, /* 000100 */ + 0x00, /* 000000 */ + 0x7C, /* 011111 */ + 0x00, /* 000000 */ + 0x10, /* 000100 */ + 0x00, /* 000000 */ + 0x00, /* 000000 */ + + /* + * code=247, hex=0xF7, ascii="!w" + */ + 0x00, /* 000000 */ + 0x28, /* 001010 */ + 0x50, /* 010100 */ + 0x00, /* 000000 */ + 0x28, /* 001010 */ + 0x50, /* 010100 */ + 0x00, /* 000000 */ + 0x00, /* 000000 */ + + /* + * code=248, hex=0xF8, ascii="!x" + */ + 0x30, /* 001100 */ + 0x48, /* 010010 */ + 0x48, /* 010010 */ + 0x30, /* 001100 */ + 0x00, /* 000000 */ + 0x00, /* 000000 */ + 0x00, /* 000000 */ + 0x00, /* 000000 */ + + /* + * code=249, hex=0xF9, ascii="!y" + */ + 0x00, /* 000000 */ + 0x00, /* 000000 */ + 0x00, /* 000000 */ + 0x30, /* 001100 */ + 0x30, /* 001100 */ + 0x00, /* 000000 */ + 0x00, /* 000000 */ + 0x00, /* 000000 */ + + /* + * code=250, hex=0xFA, ascii="!z" + */ + 0x00, /* 000000 */ + 0x00, /* 000000 */ + 0x00, /* 000000 */ + 0x20, /* 001000 */ + 0x00, /* 000000 */ + 0x00, /* 000000 */ + 0x00, /* 000000 */ + 0x00, /* 000000 */ + + /* + * code=251, hex=0xFB, ascii="!{" + */ + 0x00, /* 000000 */ + 0x1C, /* 000111 */ + 0x10, /* 000100 */ + 0x10, /* 000100 */ + 0x50, /* 010100 */ + 0x50, /* 010100 */ + 0x20, /* 001000 */ + 0x00, /* 000000 */ + + /* + * code=252, hex=0xFC, ascii="!|" + */ + 0x50, /* 010100 */ + 0x28, /* 001010 */ + 0x28, /* 001010 */ + 0x28, /* 001010 */ + 0x00, /* 000000 */ + 0x00, /* 000000 */ + 0x00, /* 000000 */ + 0x00, /* 000000 */ + + /* + * code=253, hex=0xFD, ascii="!}" + */ + 0x60, /* 011000 */ + 0x10, /* 000100 */ + 0x20, /* 001000 */ + 0x70, /* 011100 */ + 0x00, /* 000000 */ + 0x00, /* 000000 */ + 0x00, /* 000000 */ + 0x00, /* 000000 */ + + /* + * code=254, hex=0xFE, ascii="!~" + */ + 0x00, /* 000000 */ + 0x00, /* 000000 */ + 0x78, /* 011110 */ + 0x78, /* 011110 */ + 0x78, /* 011110 */ + 0x78, /* 011110 */ + 0x00, /* 000000 */ + 0x00, /* 000000 */ + + /* + * code=255, hex=0xFF, ascii="!^ź" + */ + 0x00, /* 000000 */ + 0x00, /* 000000 */ + 0x00, /* 000000 */ + 0x00, /* 000000 */ + 0x00, /* 000000 */ + 0x00, /* 000000 */ + 0x00, /* 000000 */ + 0x00 /* 000000 */ +}; diff --git a/wled00/console_font_7x9.h b/wled00/console_font_7x9.h new file mode 100644 index 0000000000..35466f7043 --- /dev/null +++ b/wled00/console_font_7x9.h @@ -0,0 +1,3331 @@ +// font curtesy of https://github.com/idispatch/raster-fonts +static const unsigned char console_font_7x9[] PROGMEM = { + + /* + * code=0, hex=0x00, ascii="^@" + */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* + * code=1, hex=0x01, ascii="^A" + */ + 0x38, /* 0011100 */ + 0x44, /* 0100010 */ + 0xAA, /* 1010101 */ + 0xAA, /* 1010101 */ + 0x82, /* 1000001 */ + 0xAA, /* 1010101 */ + 0x94, /* 1001010 */ + 0x78, /* 0111100 */ + 0x00, /* 0000000 */ + + /* + * code=2, hex=0x02, ascii="^B" + */ + 0x38, /* 0011100 */ + 0x7C, /* 0111110 */ + 0xD6, /* 1101011 */ + 0xD6, /* 1101011 */ + 0xFE, /* 1111111 */ + 0xBA, /* 1011101 */ + 0xC6, /* 1100011 */ + 0x7C, /* 0111110 */ + 0x00, /* 0000000 */ + + /* + * code=3, hex=0x03, ascii="^C" + */ + 0x00, /* 0000000 */ + 0x6C, /* 0110110 */ + 0xFE, /* 1111111 */ + 0xFE, /* 1111111 */ + 0xFE, /* 1111111 */ + 0x7C, /* 0111110 */ + 0x38, /* 0011100 */ + 0x10, /* 0001000 */ + 0x00, /* 0000000 */ + + /* + * code=4, hex=0x04, ascii="^D" + */ + 0x00, /* 0000000 */ + 0x10, /* 0001000 */ + 0x38, /* 0011100 */ + 0x7C, /* 0111110 */ + 0xFE, /* 1111111 */ + 0x7C, /* 0111110 */ + 0x38, /* 0011100 */ + 0x10, /* 0001000 */ + 0x00, /* 0000000 */ + + /* + * code=5, hex=0x05, ascii="^E" + */ + 0x38, /* 0011100 */ + 0x38, /* 0011100 */ + 0x10, /* 0001000 */ + 0xD6, /* 1101011 */ + 0xFE, /* 1111111 */ + 0xD6, /* 1101011 */ + 0x10, /* 0001000 */ + 0x7C, /* 0111110 */ + 0x00, /* 0000000 */ + + /* + * code=6, hex=0x06, ascii="^F" + */ + 0x10, /* 0001000 */ + 0x38, /* 0011100 */ + 0x7C, /* 0111110 */ + 0xFE, /* 1111111 */ + 0xFE, /* 1111111 */ + 0x54, /* 0101010 */ + 0x10, /* 0001000 */ + 0x38, /* 0011100 */ + 0x00, /* 0000000 */ + + /* + * code=7, hex=0x07, ascii="^G" + */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x18, /* 0001100 */ + 0x3C, /* 0011110 */ + 0x3C, /* 0011110 */ + 0x18, /* 0001100 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* + * code=8, hex=0x08, ascii="^H" + */ + 0xFE, /* 1111111 */ + 0xFE, /* 1111111 */ + 0xE6, /* 1110011 */ + 0xC2, /* 1100001 */ + 0xC2, /* 1100001 */ + 0xE6, /* 1110011 */ + 0xFE, /* 1111111 */ + 0xFE, /* 1111111 */ + 0xFE, /* 1111111 */ + + /* + * code=9, hex=0x09, ascii="^I" + */ + 0x00, /* 0000000 */ + 0x18, /* 0001100 */ + 0x3C, /* 0011110 */ + 0x66, /* 0110011 */ + 0x66, /* 0110011 */ + 0x3C, /* 0011110 */ + 0x18, /* 0001100 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* + * code=10, hex=0x0A, ascii="^J" + */ + 0xFE, /* 1111111 */ + 0xE6, /* 1110011 */ + 0xC2, /* 1100001 */ + 0x98, /* 1001100 */ + 0x98, /* 1001100 */ + 0xC2, /* 1100001 */ + 0xE6, /* 1110011 */ + 0xFE, /* 1111111 */ + 0xFE, /* 1111111 */ + + /* + * code=11, hex=0x0B, ascii="^K" + */ + 0x0E, /* 0000111 */ + 0x06, /* 0000011 */ + 0x0A, /* 0000101 */ + 0x18, /* 0001100 */ + 0x3C, /* 0011110 */ + 0x66, /* 0110011 */ + 0x66, /* 0110011 */ + 0x3C, /* 0011110 */ + 0x00, /* 0000000 */ + + /* + * code=12, hex=0x0C, ascii="^L" + */ + 0x3C, /* 0011110 */ + 0x66, /* 0110011 */ + 0x66, /* 0110011 */ + 0x3C, /* 0011110 */ + 0x18, /* 0001100 */ + 0x3C, /* 0011110 */ + 0x18, /* 0001100 */ + 0x18, /* 0001100 */ + 0x00, /* 0000000 */ + + /* + * code=13, hex=0x0D, ascii="^M" + */ + 0x00, /* 0000000 */ + 0x38, /* 0011100 */ + 0x2C, /* 0010110 */ + 0x20, /* 0010000 */ + 0x20, /* 0010000 */ + 0x20, /* 0010000 */ + 0x60, /* 0110000 */ + 0x60, /* 0110000 */ + 0x00, /* 0000000 */ + + /* + * code=14, hex=0x0E, ascii="^N" + */ + 0x00, /* 0000000 */ + 0x3C, /* 0011110 */ + 0x24, /* 0010010 */ + 0x3C, /* 0011110 */ + 0x24, /* 0010010 */ + 0x24, /* 0010010 */ + 0x6C, /* 0110110 */ + 0x6C, /* 0110110 */ + 0x00, /* 0000000 */ + + /* + * code=15, hex=0x0F, ascii="^O" + */ + 0x92, /* 1001001 */ + 0x54, /* 0101010 */ + 0x38, /* 0011100 */ + 0x28, /* 0010100 */ + 0xEE, /* 1110111 */ + 0x38, /* 0011100 */ + 0x54, /* 0101010 */ + 0x92, /* 1001001 */ + 0x00, /* 0000000 */ + + /* + * code=16, hex=0x10, ascii="^P" + */ + 0x00, /* 0000000 */ + 0x20, /* 0010000 */ + 0x30, /* 0011000 */ + 0x38, /* 0011100 */ + 0x3C, /* 0011110 */ + 0x38, /* 0011100 */ + 0x30, /* 0011000 */ + 0x20, /* 0010000 */ + 0x00, /* 0000000 */ + + /* + * code=17, hex=0x11, ascii="^Q" + */ + 0x00, /* 0000000 */ + 0x04, /* 0000010 */ + 0x0C, /* 0000110 */ + 0x1C, /* 0001110 */ + 0x3C, /* 0011110 */ + 0x1C, /* 0001110 */ + 0x0C, /* 0000110 */ + 0x04, /* 0000010 */ + 0x00, /* 0000000 */ + + /* + * code=18, hex=0x12, ascii="^R" + */ + 0x10, /* 0001000 */ + 0x38, /* 0011100 */ + 0x7C, /* 0111110 */ + 0x10, /* 0001000 */ + 0x10, /* 0001000 */ + 0x7C, /* 0111110 */ + 0x38, /* 0011100 */ + 0x10, /* 0001000 */ + 0x00, /* 0000000 */ + + /* + * code=19, hex=0x13, ascii="^S" + */ + 0x6C, /* 0110110 */ + 0x6C, /* 0110110 */ + 0x6C, /* 0110110 */ + 0x6C, /* 0110110 */ + 0x6C, /* 0110110 */ + 0x00, /* 0000000 */ + 0x6C, /* 0110110 */ + 0x6C, /* 0110110 */ + 0x00, /* 0000000 */ + + /* + * code=20, hex=0x14, ascii="^T" + */ + 0x00, /* 0000000 */ + 0x3C, /* 0011110 */ + 0x54, /* 0101010 */ + 0x54, /* 0101010 */ + 0x3C, /* 0011110 */ + 0x14, /* 0001010 */ + 0x14, /* 0001010 */ + 0x14, /* 0001010 */ + 0x00, /* 0000000 */ + + /* + * code=21, hex=0x15, ascii="^U" + */ + 0x3C, /* 0011110 */ + 0x66, /* 0110011 */ + 0x60, /* 0110000 */ + 0x3C, /* 0011110 */ + 0x66, /* 0110011 */ + 0x66, /* 0110011 */ + 0x3C, /* 0011110 */ + 0x06, /* 0000011 */ + 0x66, /* 0110011 */ + + /* + * code=22, hex=0x16, ascii="^V" + */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x7C, /* 0111110 */ + 0x7C, /* 0111110 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* + * code=23, hex=0x17, ascii="^W" + */ + 0x10, /* 0001000 */ + 0x38, /* 0011100 */ + 0x7C, /* 0111110 */ + 0x10, /* 0001000 */ + 0x10, /* 0001000 */ + 0x7C, /* 0111110 */ + 0x38, /* 0011100 */ + 0x10, /* 0001000 */ + 0x7C, /* 0111110 */ + + /* + * code=24, hex=0x18, ascii="^X" + */ + 0x00, /* 0000000 */ + 0x18, /* 0001100 */ + 0x3C, /* 0011110 */ + 0x5A, /* 0101101 */ + 0x18, /* 0001100 */ + 0x18, /* 0001100 */ + 0x18, /* 0001100 */ + 0x18, /* 0001100 */ + 0x00, /* 0000000 */ + + /* + * code=25, hex=0x19, ascii="^Y" + */ + 0x00, /* 0000000 */ + 0x18, /* 0001100 */ + 0x18, /* 0001100 */ + 0x18, /* 0001100 */ + 0x18, /* 0001100 */ + 0x5A, /* 0101101 */ + 0x3C, /* 0011110 */ + 0x18, /* 0001100 */ + 0x00, /* 0000000 */ + + /* + * code=26, hex=0x1A, ascii="^Z" + */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x18, /* 0001100 */ + 0x0C, /* 0000110 */ + 0x7E, /* 0111111 */ + 0x0C, /* 0000110 */ + 0x18, /* 0001100 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* + * code=27, hex=0x1B, ascii="^[" + */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x18, /* 0001100 */ + 0x30, /* 0011000 */ + 0x7E, /* 0111111 */ + 0x30, /* 0011000 */ + 0x18, /* 0001100 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* + * code=28, hex=0x1C, ascii="^\" + */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x60, /* 0110000 */ + 0x60, /* 0110000 */ + 0x7C, /* 0111110 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* + * code=29, hex=0x1D, ascii="^]" + */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x24, /* 0010010 */ + 0x66, /* 0110011 */ + 0xFE, /* 1111111 */ + 0x66, /* 0110011 */ + 0x24, /* 0010010 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* + * code=30, hex=0x1E, ascii="^^" + */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x10, /* 0001000 */ + 0x38, /* 0011100 */ + 0x7C, /* 0111110 */ + 0xFE, /* 1111111 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* + * code=31, hex=0x1F, ascii="^_" + */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0xFE, /* 1111111 */ + 0x7C, /* 0111110 */ + 0x38, /* 0011100 */ + 0x10, /* 0001000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* + * code=32, hex=0x20, ascii=" " + */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* + * code=33, hex=0x21, ascii="!" + */ + 0x00, /* 0000000 */ + 0x18, /* 0001100 */ + 0x18, /* 0001100 */ + 0x18, /* 0001100 */ + 0x18, /* 0001100 */ + 0x00, /* 0000000 */ + 0x18, /* 0001100 */ + 0x18, /* 0001100 */ + 0x00, /* 0000000 */ + + /* + * code=34, hex=0x22, ascii=""" + */ + 0x00, /* 0000000 */ + 0x6C, /* 0110110 */ + 0x6C, /* 0110110 */ + 0x44, /* 0100010 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* + * code=35, hex=0x23, ascii="#" + */ + 0x00, /* 0000000 */ + 0x6C, /* 0110110 */ + 0x6C, /* 0110110 */ + 0xFE, /* 1111111 */ + 0x6C, /* 0110110 */ + 0xFE, /* 1111111 */ + 0x6C, /* 0110110 */ + 0x6C, /* 0110110 */ + 0x00, /* 0000000 */ + + /* + * code=36, hex=0x24, ascii="$" + */ + 0x08, /* 0000100 */ + 0x18, /* 0001100 */ + 0x3C, /* 0011110 */ + 0x60, /* 0110000 */ + 0x3C, /* 0011110 */ + 0x06, /* 0000011 */ + 0x7C, /* 0111110 */ + 0x18, /* 0001100 */ + 0x10, /* 0001000 */ + + /* + * code=37, hex=0x25, ascii="%" + */ + 0x70, /* 0111000 */ + 0x52, /* 0101001 */ + 0x76, /* 0111011 */ + 0x0C, /* 0000110 */ + 0x18, /* 0001100 */ + 0x3E, /* 0011111 */ + 0x6A, /* 0110101 */ + 0x0E, /* 0000111 */ + 0x00, /* 0000000 */ + + /* + * code=38, hex=0x26, ascii="&" + */ + 0x38, /* 0011100 */ + 0x6C, /* 0110110 */ + 0x6C, /* 0110110 */ + 0x38, /* 0011100 */ + 0x6E, /* 0110111 */ + 0x6C, /* 0110110 */ + 0x6C, /* 0110110 */ + 0x3E, /* 0011111 */ + 0x00, /* 0000000 */ + + /* + * code=39, hex=0x27, ascii="'" + */ + 0x00, /* 0000000 */ + 0x18, /* 0001100 */ + 0x18, /* 0001100 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* + * code=40, hex=0x28, ascii="(" + */ + 0x00, /* 0000000 */ + 0x0C, /* 0000110 */ + 0x18, /* 0001100 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0x18, /* 0001100 */ + 0x0C, /* 0000110 */ + 0x00, /* 0000000 */ + + /* + * code=41, hex=0x29, ascii=")" + */ + 0x00, /* 0000000 */ + 0x30, /* 0011000 */ + 0x18, /* 0001100 */ + 0x0C, /* 0000110 */ + 0x0C, /* 0000110 */ + 0x0C, /* 0000110 */ + 0x18, /* 0001100 */ + 0x30, /* 0011000 */ + 0x00, /* 0000000 */ + + /* + * code=42, hex=0x2A, ascii="*" + */ + 0x00, /* 0000000 */ + 0x44, /* 0100010 */ + 0x6C, /* 0110110 */ + 0x38, /* 0011100 */ + 0xFE, /* 1111111 */ + 0x38, /* 0011100 */ + 0x6C, /* 0110110 */ + 0x44, /* 0100010 */ + 0x00, /* 0000000 */ + + /* + * code=43, hex=0x2B, ascii="+" + */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x18, /* 0001100 */ + 0x18, /* 0001100 */ + 0x7E, /* 0111111 */ + 0x7E, /* 0111111 */ + 0x18, /* 0001100 */ + 0x18, /* 0001100 */ + 0x00, /* 0000000 */ + + /* + * code=44, hex=0x2C, ascii="," + */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0x60, /* 0110000 */ + + /* + * code=45, hex=0x2D, ascii="-" + */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x7C, /* 0111110 */ + 0x7C, /* 0111110 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* + * code=46, hex=0x2E, ascii="." + */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0x00, /* 0000000 */ + + /* + * code=47, hex=0x2F, ascii="/" + */ + 0x00, /* 0000000 */ + 0x0C, /* 0000110 */ + 0x0C, /* 0000110 */ + 0x18, /* 0001100 */ + 0x18, /* 0001100 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0x60, /* 0110000 */ + 0x00, /* 0000000 */ + + /* + * code=48, hex=0x30, ascii="0" + */ + 0x00, /* 0000000 */ + 0x3C, /* 0011110 */ + 0x66, /* 0110011 */ + 0x6E, /* 0110111 */ + 0x76, /* 0111011 */ + 0x66, /* 0110011 */ + 0x66, /* 0110011 */ + 0x3C, /* 0011110 */ + 0x00, /* 0000000 */ + + /* + * code=49, hex=0x31, ascii="1" + */ + 0x00, /* 0000000 */ + 0x18, /* 0001100 */ + 0x78, /* 0111100 */ + 0x18, /* 0001100 */ + 0x18, /* 0001100 */ + 0x18, /* 0001100 */ + 0x18, /* 0001100 */ + 0x7E, /* 0111111 */ + 0x00, /* 0000000 */ + + /* + * code=50, hex=0x32, ascii="2" + */ + 0x00, /* 0000000 */ + 0x3C, /* 0011110 */ + 0x66, /* 0110011 */ + 0x46, /* 0100011 */ + 0x1C, /* 0001110 */ + 0x30, /* 0011000 */ + 0x66, /* 0110011 */ + 0x7E, /* 0111111 */ + 0x00, /* 0000000 */ + + /* + * code=51, hex=0x33, ascii="3" + */ + 0x00, /* 0000000 */ + 0x3C, /* 0011110 */ + 0x66, /* 0110011 */ + 0x06, /* 0000011 */ + 0x1C, /* 0001110 */ + 0x06, /* 0000011 */ + 0x66, /* 0110011 */ + 0x3C, /* 0011110 */ + 0x00, /* 0000000 */ + + /* + * code=52, hex=0x34, ascii="4" + */ + 0x00, /* 0000000 */ + 0x0C, /* 0000110 */ + 0x1C, /* 0001110 */ + 0x3C, /* 0011110 */ + 0x6C, /* 0110110 */ + 0x7E, /* 0111111 */ + 0x0C, /* 0000110 */ + 0x1E, /* 0001111 */ + 0x00, /* 0000000 */ + + /* + * code=53, hex=0x35, ascii="5" + */ + 0x00, /* 0000000 */ + 0x7E, /* 0111111 */ + 0x66, /* 0110011 */ + 0x60, /* 0110000 */ + 0x7C, /* 0111110 */ + 0x06, /* 0000011 */ + 0x66, /* 0110011 */ + 0x3C, /* 0011110 */ + 0x00, /* 0000000 */ + + /* + * code=54, hex=0x36, ascii="6" + */ + 0x00, /* 0000000 */ + 0x1C, /* 0001110 */ + 0x30, /* 0011000 */ + 0x60, /* 0110000 */ + 0x7C, /* 0111110 */ + 0x66, /* 0110011 */ + 0x66, /* 0110011 */ + 0x3C, /* 0011110 */ + 0x00, /* 0000000 */ + + /* + * code=55, hex=0x37, ascii="7" + */ + 0x00, /* 0000000 */ + 0x7E, /* 0111111 */ + 0x66, /* 0110011 */ + 0x06, /* 0000011 */ + 0x0C, /* 0000110 */ + 0x18, /* 0001100 */ + 0x18, /* 0001100 */ + 0x18, /* 0001100 */ + 0x00, /* 0000000 */ + + /* + * code=56, hex=0x38, ascii="8" + */ + 0x00, /* 0000000 */ + 0x3C, /* 0011110 */ + 0x66, /* 0110011 */ + 0x66, /* 0110011 */ + 0x3C, /* 0011110 */ + 0x66, /* 0110011 */ + 0x66, /* 0110011 */ + 0x3C, /* 0011110 */ + 0x00, /* 0000000 */ + + /* + * code=57, hex=0x39, ascii="9" + */ + 0x00, /* 0000000 */ + 0x3C, /* 0011110 */ + 0x66, /* 0110011 */ + 0x66, /* 0110011 */ + 0x3E, /* 0011111 */ + 0x06, /* 0000011 */ + 0x0C, /* 0000110 */ + 0x38, /* 0011100 */ + 0x00, /* 0000000 */ + + /* + * code=58, hex=0x3A, ascii=":" + */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0x00, /* 0000000 */ + + /* + * code=59, hex=0x3B, ascii=";" + */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0x60, /* 0110000 */ + + /* + * code=60, hex=0x3C, ascii="<" + */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x18, /* 0001100 */ + 0x30, /* 0011000 */ + 0x60, /* 0110000 */ + 0x30, /* 0011000 */ + 0x18, /* 0001100 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* + * code=61, hex=0x3D, ascii="=" + */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x7C, /* 0111110 */ + 0x00, /* 0000000 */ + 0x7C, /* 0111110 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* + * code=62, hex=0x3E, ascii=">" + */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x30, /* 0011000 */ + 0x18, /* 0001100 */ + 0x0C, /* 0000110 */ + 0x18, /* 0001100 */ + 0x30, /* 0011000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* + * code=63, hex=0x3F, ascii="?" + */ + 0x00, /* 0000000 */ + 0x3C, /* 0011110 */ + 0x66, /* 0110011 */ + 0x06, /* 0000011 */ + 0x0C, /* 0000110 */ + 0x18, /* 0001100 */ + 0x00, /* 0000000 */ + 0x18, /* 0001100 */ + 0x00, /* 0000000 */ + + /* + * code=64, hex=0x40, ascii="@" + */ + 0x00, /* 0000000 */ + 0x3C, /* 0011110 */ + 0x62, /* 0110001 */ + 0x6E, /* 0110111 */ + 0x6A, /* 0110101 */ + 0x6C, /* 0110110 */ + 0x62, /* 0110001 */ + 0x3C, /* 0011110 */ + 0x00, /* 0000000 */ + + /* + * code=65, hex=0x41, ascii="A" + */ + 0x00, /* 0000000 */ + 0x10, /* 0001000 */ + 0x38, /* 0011100 */ + 0x28, /* 0010100 */ + 0x6C, /* 0110110 */ + 0x7C, /* 0111110 */ + 0xC6, /* 1100011 */ + 0xC6, /* 1100011 */ + 0x00, /* 0000000 */ + + /* + * code=66, hex=0x42, ascii="B" + */ + 0x00, /* 0000000 */ + 0x7C, /* 0111110 */ + 0x66, /* 0110011 */ + 0x66, /* 0110011 */ + 0x7C, /* 0111110 */ + 0x66, /* 0110011 */ + 0x66, /* 0110011 */ + 0x7C, /* 0111110 */ + 0x00, /* 0000000 */ + + /* + * code=67, hex=0x43, ascii="C" + */ + 0x00, /* 0000000 */ + 0x3C, /* 0011110 */ + 0x66, /* 0110011 */ + 0x60, /* 0110000 */ + 0x60, /* 0110000 */ + 0x60, /* 0110000 */ + 0x66, /* 0110011 */ + 0x3C, /* 0011110 */ + 0x00, /* 0000000 */ + + /* + * code=68, hex=0x44, ascii="D" + */ + 0x00, /* 0000000 */ + 0x7C, /* 0111110 */ + 0x66, /* 0110011 */ + 0x66, /* 0110011 */ + 0x66, /* 0110011 */ + 0x66, /* 0110011 */ + 0x66, /* 0110011 */ + 0x7C, /* 0111110 */ + 0x00, /* 0000000 */ + + /* + * code=69, hex=0x45, ascii="E" + */ + 0x00, /* 0000000 */ + 0x7E, /* 0111111 */ + 0x66, /* 0110011 */ + 0x60, /* 0110000 */ + 0x78, /* 0111100 */ + 0x60, /* 0110000 */ + 0x66, /* 0110011 */ + 0x7E, /* 0111111 */ + 0x00, /* 0000000 */ + + /* + * code=70, hex=0x46, ascii="F" + */ + 0x00, /* 0000000 */ + 0x7E, /* 0111111 */ + 0x66, /* 0110011 */ + 0x60, /* 0110000 */ + 0x7C, /* 0111110 */ + 0x60, /* 0110000 */ + 0x60, /* 0110000 */ + 0x60, /* 0110000 */ + 0x00, /* 0000000 */ + + /* + * code=71, hex=0x47, ascii="G" + */ + 0x00, /* 0000000 */ + 0x3C, /* 0011110 */ + 0x66, /* 0110011 */ + 0x60, /* 0110000 */ + 0x6E, /* 0110111 */ + 0x66, /* 0110011 */ + 0x66, /* 0110011 */ + 0x3E, /* 0011111 */ + 0x00, /* 0000000 */ + + /* + * code=72, hex=0x48, ascii="H" + */ + 0x00, /* 0000000 */ + 0x66, /* 0110011 */ + 0x66, /* 0110011 */ + 0x66, /* 0110011 */ + 0x7E, /* 0111111 */ + 0x66, /* 0110011 */ + 0x66, /* 0110011 */ + 0x66, /* 0110011 */ + 0x00, /* 0000000 */ + + /* + * code=73, hex=0x49, ascii="I" + */ + 0x00, /* 0000000 */ + 0x3C, /* 0011110 */ + 0x18, /* 0001100 */ + 0x18, /* 0001100 */ + 0x18, /* 0001100 */ + 0x18, /* 0001100 */ + 0x18, /* 0001100 */ + 0x3C, /* 0011110 */ + 0x00, /* 0000000 */ + + /* + * code=74, hex=0x4A, ascii="J" + */ + 0x00, /* 0000000 */ + 0x1E, /* 0001111 */ + 0x0C, /* 0000110 */ + 0x0C, /* 0000110 */ + 0x0C, /* 0000110 */ + 0x6C, /* 0110110 */ + 0x6C, /* 0110110 */ + 0x38, /* 0011100 */ + 0x00, /* 0000000 */ + + /* + * code=75, hex=0x4B, ascii="K" + */ + 0x00, /* 0000000 */ + 0x66, /* 0110011 */ + 0x6C, /* 0110110 */ + 0x6C, /* 0110110 */ + 0x78, /* 0111100 */ + 0x6C, /* 0110110 */ + 0x6C, /* 0110110 */ + 0x66, /* 0110011 */ + 0x00, /* 0000000 */ + + /* + * code=76, hex=0x4C, ascii="L" + */ + 0x00, /* 0000000 */ + 0x60, /* 0110000 */ + 0x60, /* 0110000 */ + 0x60, /* 0110000 */ + 0x60, /* 0110000 */ + 0x60, /* 0110000 */ + 0x66, /* 0110011 */ + 0x7E, /* 0111111 */ + 0x00, /* 0000000 */ + + /* + * code=77, hex=0x4D, ascii="M" + */ + 0x00, /* 0000000 */ + 0xC6, /* 1100011 */ + 0xC6, /* 1100011 */ + 0xEE, /* 1110111 */ + 0xFE, /* 1111111 */ + 0xD6, /* 1101011 */ + 0xD6, /* 1101011 */ + 0xD6, /* 1101011 */ + 0x00, /* 0000000 */ + + /* + * code=78, hex=0x4E, ascii="N" + */ + 0x00, /* 0000000 */ + 0x66, /* 0110011 */ + 0x76, /* 0111011 */ + 0x76, /* 0111011 */ + 0x7E, /* 0111111 */ + 0x6E, /* 0110111 */ + 0x66, /* 0110011 */ + 0x66, /* 0110011 */ + 0x00, /* 0000000 */ + + /* + * code=79, hex=0x4F, ascii="O" + */ + 0x00, /* 0000000 */ + 0x3C, /* 0011110 */ + 0x66, /* 0110011 */ + 0x66, /* 0110011 */ + 0x66, /* 0110011 */ + 0x66, /* 0110011 */ + 0x66, /* 0110011 */ + 0x3C, /* 0011110 */ + 0x00, /* 0000000 */ + + /* + * code=80, hex=0x50, ascii="P" + */ + 0x00, /* 0000000 */ + 0x7C, /* 0111110 */ + 0x66, /* 0110011 */ + 0x66, /* 0110011 */ + 0x66, /* 0110011 */ + 0x7C, /* 0111110 */ + 0x60, /* 0110000 */ + 0x60, /* 0110000 */ + 0x00, /* 0000000 */ + + /* + * code=81, hex=0x51, ascii="Q" + */ + 0x00, /* 0000000 */ + 0x3C, /* 0011110 */ + 0x66, /* 0110011 */ + 0x66, /* 0110011 */ + 0x66, /* 0110011 */ + 0x76, /* 0111011 */ + 0x6E, /* 0110111 */ + 0x3C, /* 0011110 */ + 0x06, /* 0000011 */ + + /* + * code=82, hex=0x52, ascii="R" + */ + 0x00, /* 0000000 */ + 0x7C, /* 0111110 */ + 0x66, /* 0110011 */ + 0x66, /* 0110011 */ + 0x6C, /* 0110110 */ + 0x78, /* 0111100 */ + 0x6C, /* 0110110 */ + 0x66, /* 0110011 */ + 0x00, /* 0000000 */ + + /* + * code=83, hex=0x53, ascii="S" + */ + 0x00, /* 0000000 */ + 0x3C, /* 0011110 */ + 0x66, /* 0110011 */ + 0x60, /* 0110000 */ + 0x3C, /* 0011110 */ + 0x06, /* 0000011 */ + 0x66, /* 0110011 */ + 0x3C, /* 0011110 */ + 0x00, /* 0000000 */ + + /* + * code=84, hex=0x54, ascii="T" + */ + 0x00, /* 0000000 */ + 0x7E, /* 0111111 */ + 0x5A, /* 0101101 */ + 0x18, /* 0001100 */ + 0x18, /* 0001100 */ + 0x18, /* 0001100 */ + 0x18, /* 0001100 */ + 0x3C, /* 0011110 */ + 0x00, /* 0000000 */ + + /* + * code=85, hex=0x55, ascii="U" + */ + 0x00, /* 0000000 */ + 0x66, /* 0110011 */ + 0x66, /* 0110011 */ + 0x66, /* 0110011 */ + 0x66, /* 0110011 */ + 0x66, /* 0110011 */ + 0x66, /* 0110011 */ + 0x3C, /* 0011110 */ + 0x00, /* 0000000 */ + + /* + * code=86, hex=0x56, ascii="V" + */ + 0x00, /* 0000000 */ + 0xC6, /* 1100011 */ + 0xC6, /* 1100011 */ + 0x6C, /* 0110110 */ + 0x6C, /* 0110110 */ + 0x38, /* 0011100 */ + 0x38, /* 0011100 */ + 0x10, /* 0001000 */ + 0x00, /* 0000000 */ + + /* + * code=87, hex=0x57, ascii="W" + */ + 0x00, /* 0000000 */ + 0xC6, /* 1100011 */ + 0xD6, /* 1101011 */ + 0xD6, /* 1101011 */ + 0xD6, /* 1101011 */ + 0x7C, /* 0111110 */ + 0x6C, /* 0110110 */ + 0x44, /* 0100010 */ + 0x00, /* 0000000 */ + + /* + * code=88, hex=0x58, ascii="X" + */ + 0x00, /* 0000000 */ + 0x66, /* 0110011 */ + 0x66, /* 0110011 */ + 0x3C, /* 0011110 */ + 0x18, /* 0001100 */ + 0x3C, /* 0011110 */ + 0x66, /* 0110011 */ + 0x66, /* 0110011 */ + 0x00, /* 0000000 */ + + /* + * code=89, hex=0x59, ascii="Y" + */ + 0x00, /* 0000000 */ + 0x66, /* 0110011 */ + 0x66, /* 0110011 */ + 0x66, /* 0110011 */ + 0x3C, /* 0011110 */ + 0x18, /* 0001100 */ + 0x18, /* 0001100 */ + 0x3C, /* 0011110 */ + 0x00, /* 0000000 */ + + /* + * code=90, hex=0x5A, ascii="Z" + */ + 0x00, /* 0000000 */ + 0x7E, /* 0111111 */ + 0x66, /* 0110011 */ + 0x0C, /* 0000110 */ + 0x18, /* 0001100 */ + 0x30, /* 0011000 */ + 0x66, /* 0110011 */ + 0x7E, /* 0111111 */ + 0x00, /* 0000000 */ + + /* + * code=91, hex=0x5B, ascii="[" + */ + 0x00, /* 0000000 */ + 0x3C, /* 0011110 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0x3C, /* 0011110 */ + 0x00, /* 0000000 */ + + /* + * code=92, hex=0x5C, ascii="\" + */ + 0x00, /* 0000000 */ + 0x60, /* 0110000 */ + 0x60, /* 0110000 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0x18, /* 0001100 */ + 0x18, /* 0001100 */ + 0x0C, /* 0000110 */ + 0x00, /* 0000000 */ + + /* + * code=93, hex=0x5D, ascii="]" + */ + 0x00, /* 0000000 */ + 0x3C, /* 0011110 */ + 0x0C, /* 0000110 */ + 0x0C, /* 0000110 */ + 0x0C, /* 0000110 */ + 0x0C, /* 0000110 */ + 0x0C, /* 0000110 */ + 0x3C, /* 0011110 */ + 0x00, /* 0000000 */ + + /* + * code=94, hex=0x5E, ascii="^" + */ + 0x00, /* 0000000 */ + 0x10, /* 0001000 */ + 0x38, /* 0011100 */ + 0x6C, /* 0110110 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* + * code=95, hex=0x5F, ascii="_" + */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x7C, /* 0111110 */ + 0x7C, /* 0111110 */ + + /* + * code=96, hex=0x60, ascii="`" + */ + 0x00, /* 0000000 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0x18, /* 0001100 */ + 0x18, /* 0001100 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* + * code=97, hex=0x61, ascii="a" + */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x3C, /* 0011110 */ + 0x06, /* 0000011 */ + 0x3E, /* 0011111 */ + 0x66, /* 0110011 */ + 0x3A, /* 0011101 */ + 0x00, /* 0000000 */ + + /* + * code=98, hex=0x62, ascii="b" + */ + 0x00, /* 0000000 */ + 0x60, /* 0110000 */ + 0x60, /* 0110000 */ + 0x7C, /* 0111110 */ + 0x66, /* 0110011 */ + 0x66, /* 0110011 */ + 0x66, /* 0110011 */ + 0x5C, /* 0101110 */ + 0x00, /* 0000000 */ + + /* + * code=99, hex=0x63, ascii="c" + */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x3C, /* 0011110 */ + 0x66, /* 0110011 */ + 0x60, /* 0110000 */ + 0x66, /* 0110011 */ + 0x3C, /* 0011110 */ + 0x00, /* 0000000 */ + + /* + * code=100, hex=0x64, ascii="d" + */ + 0x00, /* 0000000 */ + 0x06, /* 0000011 */ + 0x06, /* 0000011 */ + 0x3E, /* 0011111 */ + 0x66, /* 0110011 */ + 0x66, /* 0110011 */ + 0x66, /* 0110011 */ + 0x3A, /* 0011101 */ + 0x00, /* 0000000 */ + + /* + * code=101, hex=0x65, ascii="e" + */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x3C, /* 0011110 */ + 0x66, /* 0110011 */ + 0x7E, /* 0111111 */ + 0x60, /* 0110000 */ + 0x3C, /* 0011110 */ + 0x00, /* 0000000 */ + + /* + * code=102, hex=0x66, ascii="f" + */ + 0x00, /* 0000000 */ + 0x1C, /* 0001110 */ + 0x36, /* 0011011 */ + 0x30, /* 0011000 */ + 0x7C, /* 0111110 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0x78, /* 0111100 */ + 0x00, /* 0000000 */ + + /* + * code=103, hex=0x67, ascii="g" + */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x3A, /* 0011101 */ + 0x66, /* 0110011 */ + 0x66, /* 0110011 */ + 0x3E, /* 0011111 */ + 0x06, /* 0000011 */ + 0x3C, /* 0011110 */ + + /* + * code=104, hex=0x68, ascii="h" + */ + 0x00, /* 0000000 */ + 0x60, /* 0110000 */ + 0x60, /* 0110000 */ + 0x6C, /* 0110110 */ + 0x76, /* 0111011 */ + 0x66, /* 0110011 */ + 0x66, /* 0110011 */ + 0x66, /* 0110011 */ + 0x00, /* 0000000 */ + + /* + * code=105, hex=0x69, ascii="i" + */ + 0x18, /* 0001100 */ + 0x18, /* 0001100 */ + 0x00, /* 0000000 */ + 0x18, /* 0001100 */ + 0x38, /* 0011100 */ + 0x18, /* 0001100 */ + 0x18, /* 0001100 */ + 0x3C, /* 0011110 */ + 0x00, /* 0000000 */ + + /* + * code=106, hex=0x6A, ascii="j" + */ + 0x0C, /* 0000110 */ + 0x0C, /* 0000110 */ + 0x00, /* 0000000 */ + 0x0C, /* 0000110 */ + 0x3C, /* 0011110 */ + 0x0C, /* 0000110 */ + 0x4C, /* 0100110 */ + 0x6C, /* 0110110 */ + 0x38, /* 0011100 */ + + /* + * code=107, hex=0x6B, ascii="k" + */ + 0x00, /* 0000000 */ + 0x60, /* 0110000 */ + 0x60, /* 0110000 */ + 0x66, /* 0110011 */ + 0x6C, /* 0110110 */ + 0x78, /* 0111100 */ + 0x6C, /* 0110110 */ + 0x66, /* 0110011 */ + 0x00, /* 0000000 */ + + /* + * code=108, hex=0x6C, ascii="l" + */ + 0x00, /* 0000000 */ + 0x18, /* 0001100 */ + 0x18, /* 0001100 */ + 0x18, /* 0001100 */ + 0x18, /* 0001100 */ + 0x18, /* 0001100 */ + 0x18, /* 0001100 */ + 0x3C, /* 0011110 */ + 0x00, /* 0000000 */ + + /* + * code=109, hex=0x6D, ascii="m" + */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x6C, /* 0110110 */ + 0xFE, /* 1111111 */ + 0xD6, /* 1101011 */ + 0xD6, /* 1101011 */ + 0xD6, /* 1101011 */ + 0x00, /* 0000000 */ + + /* + * code=110, hex=0x6E, ascii="n" + */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x6C, /* 0110110 */ + 0x76, /* 0111011 */ + 0x66, /* 0110011 */ + 0x66, /* 0110011 */ + 0x66, /* 0110011 */ + 0x00, /* 0000000 */ + + /* + * code=111, hex=0x6F, ascii="o" + */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x3C, /* 0011110 */ + 0x66, /* 0110011 */ + 0x66, /* 0110011 */ + 0x66, /* 0110011 */ + 0x3C, /* 0011110 */ + 0x00, /* 0000000 */ + + /* + * code=112, hex=0x70, ascii="p" + */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x7C, /* 0111110 */ + 0x66, /* 0110011 */ + 0x66, /* 0110011 */ + 0x76, /* 0111011 */ + 0x6C, /* 0110110 */ + 0x60, /* 0110000 */ + + /* + * code=113, hex=0x71, ascii="q" + */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x3A, /* 0011101 */ + 0x66, /* 0110011 */ + 0x66, /* 0110011 */ + 0x6E, /* 0110111 */ + 0x36, /* 0011011 */ + 0x06, /* 0000011 */ + + /* + * code=114, hex=0x72, ascii="r" + */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x26, /* 0010011 */ + 0x7E, /* 0111111 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0x78, /* 0111100 */ + 0x00, /* 0000000 */ + + /* + * code=115, hex=0x73, ascii="s" + */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x3C, /* 0011110 */ + 0x60, /* 0110000 */ + 0x3C, /* 0011110 */ + 0x06, /* 0000011 */ + 0x3C, /* 0011110 */ + 0x00, /* 0000000 */ + + /* + * code=116, hex=0x74, ascii="t" + */ + 0x00, /* 0000000 */ + 0x10, /* 0001000 */ + 0x30, /* 0011000 */ + 0xFC, /* 1111110 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0x36, /* 0011011 */ + 0x1C, /* 0001110 */ + 0x00, /* 0000000 */ + + /* + * code=117, hex=0x75, ascii="u" + */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x66, /* 0110011 */ + 0x66, /* 0110011 */ + 0x66, /* 0110011 */ + 0x6E, /* 0110111 */ + 0x36, /* 0011011 */ + 0x00, /* 0000000 */ + + /* + * code=118, hex=0x76, ascii="v" + */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0xC6, /* 1100011 */ + 0xC6, /* 1100011 */ + 0x6C, /* 0110110 */ + 0x38, /* 0011100 */ + 0x10, /* 0001000 */ + 0x00, /* 0000000 */ + + /* + * code=119, hex=0x77, ascii="w" + */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0xD6, /* 1101011 */ + 0xD6, /* 1101011 */ + 0x7C, /* 0111110 */ + 0x6C, /* 0110110 */ + 0x44, /* 0100010 */ + 0x00, /* 0000000 */ + + /* + * code=120, hex=0x78, ascii="x" + */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0xC6, /* 1100011 */ + 0x6C, /* 0110110 */ + 0x38, /* 0011100 */ + 0x6C, /* 0110110 */ + 0xC6, /* 1100011 */ + 0x00, /* 0000000 */ + + /* + * code=121, hex=0x79, ascii="y" + */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x66, /* 0110011 */ + 0x66, /* 0110011 */ + 0x36, /* 0011011 */ + 0x1C, /* 0001110 */ + 0x6C, /* 0110110 */ + 0x38, /* 0011100 */ + + /* + * code=122, hex=0x7A, ascii="z" + */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x7E, /* 0111111 */ + 0x06, /* 0000011 */ + 0x18, /* 0001100 */ + 0x30, /* 0011000 */ + 0x7E, /* 0111111 */ + 0x00, /* 0000000 */ + + /* + * code=123, hex=0x7B, ascii="{" + */ + 0x00, /* 0000000 */ + 0x1C, /* 0001110 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0x60, /* 0110000 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0x1C, /* 0001110 */ + 0x00, /* 0000000 */ + + /* + * code=124, hex=0x7C, ascii="|" + */ + 0x18, /* 0001100 */ + 0x18, /* 0001100 */ + 0x18, /* 0001100 */ + 0x18, /* 0001100 */ + 0x00, /* 0000000 */ + 0x18, /* 0001100 */ + 0x18, /* 0001100 */ + 0x18, /* 0001100 */ + 0x00, /* 0000000 */ + + /* + * code=125, hex=0x7D, ascii="}" + */ + 0x00, /* 0000000 */ + 0x70, /* 0111000 */ + 0x18, /* 0001100 */ + 0x18, /* 0001100 */ + 0x0C, /* 0000110 */ + 0x18, /* 0001100 */ + 0x18, /* 0001100 */ + 0x70, /* 0111000 */ + 0x00, /* 0000000 */ + + /* + * code=126, hex=0x7E, ascii="~" + */ + 0x00, /* 0000000 */ + 0x10, /* 0001000 */ + 0x3A, /* 0011101 */ + 0x6E, /* 0110111 */ + 0x04, /* 0000010 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* + * code=127, hex=0x7F, ascii="^?" + */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x08, /* 0000100 */ + 0x1C, /* 0001110 */ + 0x36, /* 0011011 */ + 0x62, /* 0110001 */ + 0x7E, /* 0111111 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* + * code=128, hex=0x80, ascii="!^@" + */ + 0x00, /* 0000000 */ + 0x3C, /* 0011110 */ + 0x66, /* 0110011 */ + 0x60, /* 0110000 */ + 0x60, /* 0110000 */ + 0x60, /* 0110000 */ + 0x66, /* 0110011 */ + 0x3C, /* 0011110 */ + 0x78, /* 0111100 */ + + /* + * code=129, hex=0x81, ascii="!^A" + */ + 0x66, /* 0110011 */ + 0x66, /* 0110011 */ + 0x00, /* 0000000 */ + 0x66, /* 0110011 */ + 0x66, /* 0110011 */ + 0x66, /* 0110011 */ + 0x66, /* 0110011 */ + 0x3A, /* 0011101 */ + 0x00, /* 0000000 */ + + /* + * code=130, hex=0x82, ascii="!^B" + */ + 0x0C, /* 0000110 */ + 0x18, /* 0001100 */ + 0x00, /* 0000000 */ + 0x3E, /* 0011111 */ + 0x62, /* 0110001 */ + 0x7E, /* 0111111 */ + 0x60, /* 0110000 */ + 0x3E, /* 0011111 */ + 0x00, /* 0000000 */ + + /* + * code=131, hex=0x83, ascii="!^C" + */ + 0x1C, /* 0001110 */ + 0x36, /* 0011011 */ + 0x00, /* 0000000 */ + 0x3C, /* 0011110 */ + 0x06, /* 0000011 */ + 0x3E, /* 0011111 */ + 0x66, /* 0110011 */ + 0x3E, /* 0011111 */ + 0x00, /* 0000000 */ + + /* + * code=132, hex=0x84, ascii="!^D" + */ + 0x36, /* 0011011 */ + 0x36, /* 0011011 */ + 0x00, /* 0000000 */ + 0x3C, /* 0011110 */ + 0x06, /* 0000011 */ + 0x3E, /* 0011111 */ + 0x66, /* 0110011 */ + 0x3E, /* 0011111 */ + 0x00, /* 0000000 */ + + /* + * code=133, hex=0x85, ascii="!^E" + */ + 0x18, /* 0001100 */ + 0x0C, /* 0000110 */ + 0x00, /* 0000000 */ + 0x3C, /* 0011110 */ + 0x06, /* 0000011 */ + 0x3E, /* 0011111 */ + 0x66, /* 0110011 */ + 0x3A, /* 0011101 */ + 0x00, /* 0000000 */ + + /* + * code=134, hex=0x86, ascii="!^F" + */ + 0x1C, /* 0001110 */ + 0x14, /* 0001010 */ + 0x00, /* 0000000 */ + 0x3C, /* 0011110 */ + 0x06, /* 0000011 */ + 0x3E, /* 0011111 */ + 0x66, /* 0110011 */ + 0x3A, /* 0011101 */ + 0x00, /* 0000000 */ + + /* + * code=135, hex=0x87, ascii="!^G" + */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x1C, /* 0001110 */ + 0x36, /* 0011011 */ + 0x60, /* 0110000 */ + 0x36, /* 0011011 */ + 0x1C, /* 0001110 */ + 0x78, /* 0111100 */ + + /* + * code=136, hex=0x88, ascii="!^H" + */ + 0x08, /* 0000100 */ + 0x1C, /* 0001110 */ + 0x00, /* 0000000 */ + 0x3C, /* 0011110 */ + 0x66, /* 0110011 */ + 0x7E, /* 0111111 */ + 0x60, /* 0110000 */ + 0x3E, /* 0011111 */ + 0x00, /* 0000000 */ + + /* + * code=137, hex=0x89, ascii="!^I" + */ + 0x66, /* 0110011 */ + 0x66, /* 0110011 */ + 0x00, /* 0000000 */ + 0x3C, /* 0011110 */ + 0x66, /* 0110011 */ + 0x7E, /* 0111111 */ + 0x60, /* 0110000 */ + 0x3E, /* 0011111 */ + 0x00, /* 0000000 */ + + /* + * code=138, hex=0x8A, ascii="!^J" + */ + 0x18, /* 0001100 */ + 0x0C, /* 0000110 */ + 0x00, /* 0000000 */ + 0x3C, /* 0011110 */ + 0x66, /* 0110011 */ + 0x7E, /* 0111111 */ + 0x60, /* 0110000 */ + 0x3E, /* 0011111 */ + 0x00, /* 0000000 */ + + /* + * code=139, hex=0x8B, ascii="!^K" + */ + 0x66, /* 0110011 */ + 0x66, /* 0110011 */ + 0x00, /* 0000000 */ + 0x38, /* 0011100 */ + 0x18, /* 0001100 */ + 0x18, /* 0001100 */ + 0x18, /* 0001100 */ + 0x3C, /* 0011110 */ + 0x00, /* 0000000 */ + + /* + * code=140, hex=0x8C, ascii="!^L" + */ + 0x10, /* 0001000 */ + 0x38, /* 0011100 */ + 0x00, /* 0000000 */ + 0x38, /* 0011100 */ + 0x18, /* 0001100 */ + 0x18, /* 0001100 */ + 0x18, /* 0001100 */ + 0x3C, /* 0011110 */ + 0x00, /* 0000000 */ + + /* + * code=141, hex=0x8D, ascii="!^M" + */ + 0x30, /* 0011000 */ + 0x18, /* 0001100 */ + 0x00, /* 0000000 */ + 0x38, /* 0011100 */ + 0x18, /* 0001100 */ + 0x18, /* 0001100 */ + 0x18, /* 0001100 */ + 0x3C, /* 0011110 */ + 0x00, /* 0000000 */ + + /* + * code=142, hex=0x8E, ascii="!^N" + */ + 0xC6, /* 1100011 */ + 0x10, /* 0001000 */ + 0x38, /* 0011100 */ + 0x28, /* 0010100 */ + 0x6C, /* 0110110 */ + 0x7C, /* 0111110 */ + 0xC6, /* 1100011 */ + 0xC6, /* 1100011 */ + 0x00, /* 0000000 */ + + /* + * code=143, hex=0x8F, ascii="!^O" + */ + 0x38, /* 0011100 */ + 0x28, /* 0010100 */ + 0x38, /* 0011100 */ + 0x28, /* 0010100 */ + 0x6C, /* 0110110 */ + 0x7C, /* 0111110 */ + 0xC6, /* 1100011 */ + 0xC6, /* 1100011 */ + 0x00, /* 0000000 */ + + /* + * code=144, hex=0x90, ascii="!^P" + */ + 0x1C, /* 0001110 */ + 0x30, /* 0011000 */ + 0x7E, /* 0111111 */ + 0x60, /* 0110000 */ + 0x7C, /* 0111110 */ + 0x60, /* 0110000 */ + 0x60, /* 0110000 */ + 0x7E, /* 0111111 */ + 0x00, /* 0000000 */ + + /* + * code=145, hex=0x91, ascii="!^Q" + */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x7C, /* 0111110 */ + 0x1A, /* 0001101 */ + 0x7E, /* 0111111 */ + 0xD8, /* 1101100 */ + 0x7E, /* 0111111 */ + 0x00, /* 0000000 */ + + /* + * code=146, hex=0x92, ascii="!^R" + */ + 0x00, /* 0000000 */ + 0x1E, /* 0001111 */ + 0x38, /* 0011100 */ + 0x58, /* 0101100 */ + 0x5E, /* 0101111 */ + 0xF8, /* 1111100 */ + 0xD8, /* 1101100 */ + 0xDE, /* 1101111 */ + 0x00, /* 0000000 */ + + /* + * code=147, hex=0x93, ascii="!^S" + */ + 0x10, /* 0001000 */ + 0x38, /* 0011100 */ + 0x00, /* 0000000 */ + 0x3C, /* 0011110 */ + 0x66, /* 0110011 */ + 0x66, /* 0110011 */ + 0x66, /* 0110011 */ + 0x3C, /* 0011110 */ + 0x00, /* 0000000 */ + + /* + * code=148, hex=0x94, ascii="!^T" + */ + 0x66, /* 0110011 */ + 0x66, /* 0110011 */ + 0x00, /* 0000000 */ + 0x3C, /* 0011110 */ + 0x66, /* 0110011 */ + 0x66, /* 0110011 */ + 0x66, /* 0110011 */ + 0x3C, /* 0011110 */ + 0x00, /* 0000000 */ + + /* + * code=149, hex=0x95, ascii="!^U" + */ + 0x18, /* 0001100 */ + 0x0C, /* 0000110 */ + 0x00, /* 0000000 */ + 0x3C, /* 0011110 */ + 0x66, /* 0110011 */ + 0x66, /* 0110011 */ + 0x66, /* 0110011 */ + 0x3C, /* 0011110 */ + 0x00, /* 0000000 */ + + /* + * code=150, hex=0x96, ascii="!^V" + */ + 0x08, /* 0000100 */ + 0x1C, /* 0001110 */ + 0x00, /* 0000000 */ + 0x66, /* 0110011 */ + 0x66, /* 0110011 */ + 0x66, /* 0110011 */ + 0x66, /* 0110011 */ + 0x3A, /* 0011101 */ + 0x00, /* 0000000 */ + + /* + * code=151, hex=0x97, ascii="!^W" + */ + 0x18, /* 0001100 */ + 0x0C, /* 0000110 */ + 0x00, /* 0000000 */ + 0x66, /* 0110011 */ + 0x66, /* 0110011 */ + 0x66, /* 0110011 */ + 0x66, /* 0110011 */ + 0x3A, /* 0011101 */ + 0x00, /* 0000000 */ + + /* + * code=152, hex=0x98, ascii="!^X" + */ + 0x66, /* 0110011 */ + 0x66, /* 0110011 */ + 0x00, /* 0000000 */ + 0x66, /* 0110011 */ + 0x66, /* 0110011 */ + 0x36, /* 0011011 */ + 0x1C, /* 0001110 */ + 0x6C, /* 0110110 */ + 0x38, /* 0011100 */ + + /* + * code=153, hex=0x99, ascii="!^Y" + */ + 0x66, /* 0110011 */ + 0x00, /* 0000000 */ + 0x3C, /* 0011110 */ + 0x66, /* 0110011 */ + 0x66, /* 0110011 */ + 0x66, /* 0110011 */ + 0x66, /* 0110011 */ + 0x3C, /* 0011110 */ + 0x00, /* 0000000 */ + + /* + * code=154, hex=0x9A, ascii="!^Z" + */ + 0x66, /* 0110011 */ + 0x00, /* 0000000 */ + 0x66, /* 0110011 */ + 0x66, /* 0110011 */ + 0x66, /* 0110011 */ + 0x66, /* 0110011 */ + 0x66, /* 0110011 */ + 0x3C, /* 0011110 */ + 0x00, /* 0000000 */ + + /* + * code=155, hex=0x9B, ascii="!^[" + */ + 0x08, /* 0000100 */ + 0x08, /* 0000100 */ + 0x3C, /* 0011110 */ + 0x60, /* 0110000 */ + 0x60, /* 0110000 */ + 0x3C, /* 0011110 */ + 0x10, /* 0001000 */ + 0x10, /* 0001000 */ + 0x00, /* 0000000 */ + + /* + * code=156, hex=0x9C, ascii="!^\" + */ + 0x1C, /* 0001110 */ + 0x36, /* 0011011 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0x7C, /* 0111110 */ + 0x30, /* 0011000 */ + 0x3C, /* 0011110 */ + 0x66, /* 0110011 */ + 0x00, /* 0000000 */ + + /* + * code=157, hex=0x9D, ascii="!^]" + */ + 0x66, /* 0110011 */ + 0x66, /* 0110011 */ + 0x3C, /* 0011110 */ + 0x18, /* 0001100 */ + 0x7E, /* 0111111 */ + 0x18, /* 0001100 */ + 0x7E, /* 0111111 */ + 0x18, /* 0001100 */ + 0x00, /* 0000000 */ + + /* + * code=158, hex=0x9E, ascii="!^^" + */ + 0xE0, /* 1110000 */ + 0xD0, /* 1101000 */ + 0xD0, /* 1101000 */ + 0xF4, /* 1111010 */ + 0xCC, /* 1100110 */ + 0xDE, /* 1101111 */ + 0xCC, /* 1100110 */ + 0x06, /* 0000011 */ + 0x00, /* 0000000 */ + + /* + * code=159, hex=0x9F, ascii="!^_" + */ + 0x0E, /* 0000111 */ + 0x18, /* 0001100 */ + 0x18, /* 0001100 */ + 0x18, /* 0001100 */ + 0x7E, /* 0111111 */ + 0x18, /* 0001100 */ + 0x18, /* 0001100 */ + 0x18, /* 0001100 */ + 0x70, /* 0111000 */ + + /* + * code=160, hex=0xA0, ascii="! " + */ + 0x06, /* 0000011 */ + 0x0C, /* 0000110 */ + 0x00, /* 0000000 */ + 0x3C, /* 0011110 */ + 0x06, /* 0000011 */ + 0x3E, /* 0011111 */ + 0x66, /* 0110011 */ + 0x3A, /* 0011101 */ + 0x00, /* 0000000 */ + + /* + * code=161, hex=0xA1, ascii="!!" + */ + 0x0C, /* 0000110 */ + 0x18, /* 0001100 */ + 0x00, /* 0000000 */ + 0x38, /* 0011100 */ + 0x18, /* 0001100 */ + 0x18, /* 0001100 */ + 0x18, /* 0001100 */ + 0x3C, /* 0011110 */ + 0x00, /* 0000000 */ + + /* + * code=162, hex=0xA2, ascii="!"" + */ + 0x0C, /* 0000110 */ + 0x18, /* 0001100 */ + 0x00, /* 0000000 */ + 0x3C, /* 0011110 */ + 0x66, /* 0110011 */ + 0x66, /* 0110011 */ + 0x66, /* 0110011 */ + 0x3C, /* 0011110 */ + 0x00, /* 0000000 */ + + /* + * code=163, hex=0xA3, ascii="!#" + */ + 0x0C, /* 0000110 */ + 0x18, /* 0001100 */ + 0x00, /* 0000000 */ + 0x66, /* 0110011 */ + 0x66, /* 0110011 */ + 0x66, /* 0110011 */ + 0x66, /* 0110011 */ + 0x3A, /* 0011101 */ + 0x00, /* 0000000 */ + + /* + * code=164, hex=0xA4, ascii="!$" + */ + 0x76, /* 0111011 */ + 0xDC, /* 1101110 */ + 0x00, /* 0000000 */ + 0x6C, /* 0110110 */ + 0x76, /* 0111011 */ + 0x66, /* 0110011 */ + 0x66, /* 0110011 */ + 0x66, /* 0110011 */ + 0x00, /* 0000000 */ + + /* + * code=165, hex=0xA5, ascii="!%" + */ + 0x76, /* 0111011 */ + 0xDC, /* 1101110 */ + 0x00, /* 0000000 */ + 0x66, /* 0110011 */ + 0x76, /* 0111011 */ + 0x7E, /* 0111111 */ + 0x6E, /* 0110111 */ + 0x66, /* 0110011 */ + 0x00, /* 0000000 */ + + /* + * code=166, hex=0xA6, ascii="!&" + */ + 0x38, /* 0011100 */ + 0x0C, /* 0000110 */ + 0x3C, /* 0011110 */ + 0x6C, /* 0110110 */ + 0x34, /* 0011010 */ + 0x00, /* 0000000 */ + 0x7C, /* 0111110 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* + * code=167, hex=0xA7, ascii="!'" + */ + 0x3C, /* 0011110 */ + 0x66, /* 0110011 */ + 0x66, /* 0110011 */ + 0x3C, /* 0011110 */ + 0x00, /* 0000000 */ + 0x7E, /* 0111111 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* + * code=168, hex=0xA8, ascii="!(" + */ + 0x00, /* 0000000 */ + 0x18, /* 0001100 */ + 0x00, /* 0000000 */ + 0x18, /* 0001100 */ + 0x30, /* 0011000 */ + 0x60, /* 0110000 */ + 0x66, /* 0110011 */ + 0x3C, /* 0011110 */ + 0x00, /* 0000000 */ + + /* + * code=169, hex=0xA9, ascii="!)" + */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x3C, /* 0011110 */ + 0x3C, /* 0011110 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* + * code=170, hex=0xAA, ascii="!*" + */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x7C, /* 0111110 */ + 0x7C, /* 0111110 */ + 0x0C, /* 0000110 */ + 0x0C, /* 0000110 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* + * code=171, hex=0xAB, ascii="!+" + */ + 0x60, /* 0110000 */ + 0x60, /* 0110000 */ + 0x60, /* 0110000 */ + 0x6E, /* 0110111 */ + 0x1A, /* 0001101 */ + 0x04, /* 0000010 */ + 0x18, /* 0001100 */ + 0x1E, /* 0001111 */ + 0x00, /* 0000000 */ + + /* + * code=172, hex=0xAC, ascii="!," + */ + 0x60, /* 0110000 */ + 0x60, /* 0110000 */ + 0x60, /* 0110000 */ + 0x6C, /* 0110110 */ + 0x7C, /* 0111110 */ + 0x2C, /* 0010110 */ + 0x7C, /* 0111110 */ + 0x0C, /* 0000110 */ + 0x00, /* 0000000 */ + + /* + * code=173, hex=0xAD, ascii="!-" + */ + 0x00, /* 0000000 */ + 0x18, /* 0001100 */ + 0x00, /* 0000000 */ + 0x18, /* 0001100 */ + 0x18, /* 0001100 */ + 0x3C, /* 0011110 */ + 0x3C, /* 0011110 */ + 0x18, /* 0001100 */ + 0x00, /* 0000000 */ + + /* + * code=174, hex=0xAE, ascii="!." + */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x32, /* 0011001 */ + 0x66, /* 0110011 */ + 0xCC, /* 1100110 */ + 0x66, /* 0110011 */ + 0x32, /* 0011001 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* + * code=175, hex=0xAF, ascii="!/" + */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0xCC, /* 1100110 */ + 0x66, /* 0110011 */ + 0x32, /* 0011001 */ + 0x66, /* 0110011 */ + 0xCC, /* 1100110 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* + * code=176, hex=0xB0, ascii="!0" + */ + 0x54, /* 0101010 */ + 0x00, /* 0000000 */ + 0xAA, /* 1010101 */ + 0x00, /* 0000000 */ + 0x54, /* 0101010 */ + 0x00, /* 0000000 */ + 0xAA, /* 1010101 */ + 0x00, /* 0000000 */ + 0x54, /* 0101010 */ + + /* + * code=177, hex=0xB1, ascii="!1" + */ + 0x92, /* 1001001 */ + 0x48, /* 0100100 */ + 0x24, /* 0010010 */ + 0x92, /* 1001001 */ + 0x48, /* 0100100 */ + 0x24, /* 0010010 */ + 0x92, /* 1001001 */ + 0x48, /* 0100100 */ + 0x24, /* 0010010 */ + + /* + * code=178, hex=0xB2, ascii="!2" + */ + 0xAA, /* 1010101 */ + 0x54, /* 0101010 */ + 0xAA, /* 1010101 */ + 0x54, /* 0101010 */ + 0xAA, /* 1010101 */ + 0x54, /* 0101010 */ + 0xAA, /* 1010101 */ + 0x54, /* 0101010 */ + 0xAA, /* 1010101 */ + + /* + * code=179, hex=0xB3, ascii="!3" + */ + 0x10, /* 0001000 */ + 0x10, /* 0001000 */ + 0x10, /* 0001000 */ + 0x10, /* 0001000 */ + 0x10, /* 0001000 */ + 0x10, /* 0001000 */ + 0x10, /* 0001000 */ + 0x10, /* 0001000 */ + 0x10, /* 0001000 */ + + /* + * code=180, hex=0xB4, ascii="!4" + */ + 0x10, /* 0001000 */ + 0x10, /* 0001000 */ + 0x10, /* 0001000 */ + 0x10, /* 0001000 */ + 0xF0, /* 1111000 */ + 0x10, /* 0001000 */ + 0x10, /* 0001000 */ + 0x10, /* 0001000 */ + 0x10, /* 0001000 */ + + /* + * code=181, hex=0xB5, ascii="!5" + */ + 0x10, /* 0001000 */ + 0x10, /* 0001000 */ + 0x10, /* 0001000 */ + 0xF0, /* 1111000 */ + 0x10, /* 0001000 */ + 0xF0, /* 1111000 */ + 0x10, /* 0001000 */ + 0x10, /* 0001000 */ + 0x10, /* 0001000 */ + + /* + * code=182, hex=0xB6, ascii="!6" + */ + 0x28, /* 0010100 */ + 0x28, /* 0010100 */ + 0x28, /* 0010100 */ + 0x28, /* 0010100 */ + 0xE8, /* 1110100 */ + 0x28, /* 0010100 */ + 0x28, /* 0010100 */ + 0x28, /* 0010100 */ + 0x28, /* 0010100 */ + + /* + * code=183, hex=0xB7, ascii="!7" + */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0xF8, /* 1111100 */ + 0x28, /* 0010100 */ + 0x28, /* 0010100 */ + 0x28, /* 0010100 */ + 0x28, /* 0010100 */ + + /* + * code=184, hex=0xB8, ascii="!8" + */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0xF0, /* 1111000 */ + 0x10, /* 0001000 */ + 0xF0, /* 1111000 */ + 0x10, /* 0001000 */ + 0x10, /* 0001000 */ + 0x10, /* 0001000 */ + + /* + * code=185, hex=0xB9, ascii="!9" + */ + 0x28, /* 0010100 */ + 0x28, /* 0010100 */ + 0x28, /* 0010100 */ + 0xE8, /* 1110100 */ + 0x08, /* 0000100 */ + 0xE8, /* 1110100 */ + 0x28, /* 0010100 */ + 0x28, /* 0010100 */ + 0x28, /* 0010100 */ + + /* + * code=186, hex=0xBA, ascii="!:" + */ + 0x28, /* 0010100 */ + 0x28, /* 0010100 */ + 0x28, /* 0010100 */ + 0x28, /* 0010100 */ + 0x28, /* 0010100 */ + 0x28, /* 0010100 */ + 0x28, /* 0010100 */ + 0x28, /* 0010100 */ + 0x28, /* 0010100 */ + + /* + * code=187, hex=0xBB, ascii="!;" + */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0xF8, /* 1111100 */ + 0x08, /* 0000100 */ + 0xE8, /* 1110100 */ + 0x28, /* 0010100 */ + 0x28, /* 0010100 */ + 0x28, /* 0010100 */ + + /* + * code=188, hex=0xBC, ascii="!<" + */ + 0x28, /* 0010100 */ + 0x28, /* 0010100 */ + 0x28, /* 0010100 */ + 0xE8, /* 1110100 */ + 0x08, /* 0000100 */ + 0xF8, /* 1111100 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* + * code=189, hex=0xBD, ascii="!=" + */ + 0x28, /* 0010100 */ + 0x28, /* 0010100 */ + 0x28, /* 0010100 */ + 0x28, /* 0010100 */ + 0xF8, /* 1111100 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* + * code=190, hex=0xBE, ascii="!>" + */ + 0x10, /* 0001000 */ + 0x10, /* 0001000 */ + 0x10, /* 0001000 */ + 0xF0, /* 1111000 */ + 0x10, /* 0001000 */ + 0xF0, /* 1111000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* + * code=191, hex=0xBF, ascii="!?" + */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0xF0, /* 1111000 */ + 0x10, /* 0001000 */ + 0x10, /* 0001000 */ + 0x10, /* 0001000 */ + 0x10, /* 0001000 */ + + /* + * code=192, hex=0xC0, ascii="!@" + */ + 0x10, /* 0001000 */ + 0x10, /* 0001000 */ + 0x10, /* 0001000 */ + 0x10, /* 0001000 */ + 0x1E, /* 0001111 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* + * code=193, hex=0xC1, ascii="!A" + */ + 0x10, /* 0001000 */ + 0x10, /* 0001000 */ + 0x10, /* 0001000 */ + 0x10, /* 0001000 */ + 0xFE, /* 1111111 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* + * code=194, hex=0xC2, ascii="!B" + */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0xFE, /* 1111111 */ + 0x10, /* 0001000 */ + 0x10, /* 0001000 */ + 0x10, /* 0001000 */ + 0x10, /* 0001000 */ + + /* + * code=195, hex=0xC3, ascii="!C" + */ + 0x10, /* 0001000 */ + 0x10, /* 0001000 */ + 0x10, /* 0001000 */ + 0x10, /* 0001000 */ + 0x1E, /* 0001111 */ + 0x10, /* 0001000 */ + 0x10, /* 0001000 */ + 0x10, /* 0001000 */ + 0x10, /* 0001000 */ + + /* + * code=196, hex=0xC4, ascii="!D" + */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0xFE, /* 1111111 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* + * code=197, hex=0xC5, ascii="!E" + */ + 0x10, /* 0001000 */ + 0x10, /* 0001000 */ + 0x10, /* 0001000 */ + 0x10, /* 0001000 */ + 0xFE, /* 1111111 */ + 0x10, /* 0001000 */ + 0x10, /* 0001000 */ + 0x10, /* 0001000 */ + 0x10, /* 0001000 */ + + /* + * code=198, hex=0xC6, ascii="!F" + */ + 0x10, /* 0001000 */ + 0x10, /* 0001000 */ + 0x10, /* 0001000 */ + 0x1E, /* 0001111 */ + 0x10, /* 0001000 */ + 0x1E, /* 0001111 */ + 0x10, /* 0001000 */ + 0x10, /* 0001000 */ + 0x10, /* 0001000 */ + + /* + * code=199, hex=0xC7, ascii="!G" + */ + 0x28, /* 0010100 */ + 0x28, /* 0010100 */ + 0x28, /* 0010100 */ + 0x28, /* 0010100 */ + 0x2E, /* 0010111 */ + 0x28, /* 0010100 */ + 0x28, /* 0010100 */ + 0x28, /* 0010100 */ + 0x28, /* 0010100 */ + + /* + * code=200, hex=0xC8, ascii="!H" + */ + 0x28, /* 0010100 */ + 0x28, /* 0010100 */ + 0x28, /* 0010100 */ + 0x2E, /* 0010111 */ + 0x20, /* 0010000 */ + 0x3E, /* 0011111 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* + * code=201, hex=0xC9, ascii="!I" + */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x3E, /* 0011111 */ + 0x20, /* 0010000 */ + 0x2E, /* 0010111 */ + 0x28, /* 0010100 */ + 0x28, /* 0010100 */ + 0x28, /* 0010100 */ + + /* + * code=202, hex=0xCA, ascii="!J" + */ + 0x28, /* 0010100 */ + 0x28, /* 0010100 */ + 0x28, /* 0010100 */ + 0xEE, /* 1110111 */ + 0x00, /* 0000000 */ + 0xFE, /* 1111111 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* + * code=203, hex=0xCB, ascii="!K" + */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0xFE, /* 1111111 */ + 0x00, /* 0000000 */ + 0xEE, /* 1110111 */ + 0x28, /* 0010100 */ + 0x28, /* 0010100 */ + 0x28, /* 0010100 */ + + /* + * code=204, hex=0xCC, ascii="!L" + */ + 0x28, /* 0010100 */ + 0x28, /* 0010100 */ + 0x28, /* 0010100 */ + 0x2E, /* 0010111 */ + 0x20, /* 0010000 */ + 0x2E, /* 0010111 */ + 0x28, /* 0010100 */ + 0x28, /* 0010100 */ + 0x28, /* 0010100 */ + + /* + * code=205, hex=0xCD, ascii="!M" + */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0xFE, /* 1111111 */ + 0x00, /* 0000000 */ + 0xFE, /* 1111111 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* + * code=206, hex=0xCE, ascii="!N" + */ + 0x28, /* 0010100 */ + 0x28, /* 0010100 */ + 0x28, /* 0010100 */ + 0xEE, /* 1110111 */ + 0x00, /* 0000000 */ + 0xEE, /* 1110111 */ + 0x28, /* 0010100 */ + 0x28, /* 0010100 */ + 0x28, /* 0010100 */ + + /* + * code=207, hex=0xCF, ascii="!O" + */ + 0x10, /* 0001000 */ + 0x10, /* 0001000 */ + 0x10, /* 0001000 */ + 0xFE, /* 1111111 */ + 0x00, /* 0000000 */ + 0xFE, /* 1111111 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* + * code=208, hex=0xD0, ascii="!P" + */ + 0x28, /* 0010100 */ + 0x28, /* 0010100 */ + 0x28, /* 0010100 */ + 0x28, /* 0010100 */ + 0xF8, /* 1111100 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* + * code=209, hex=0xD1, ascii="!Q" + */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0xFE, /* 1111111 */ + 0x00, /* 0000000 */ + 0xFE, /* 1111111 */ + 0x10, /* 0001000 */ + 0x10, /* 0001000 */ + 0x10, /* 0001000 */ + + /* + * code=210, hex=0xD2, ascii="!R" + */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0xF8, /* 1111100 */ + 0x28, /* 0010100 */ + 0x28, /* 0010100 */ + 0x28, /* 0010100 */ + 0x28, /* 0010100 */ + + /* + * code=211, hex=0xD3, ascii="!S" + */ + 0x28, /* 0010100 */ + 0x28, /* 0010100 */ + 0x28, /* 0010100 */ + 0x28, /* 0010100 */ + 0x3E, /* 0011111 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* + * code=212, hex=0xD4, ascii="!T" + */ + 0x10, /* 0001000 */ + 0x10, /* 0001000 */ + 0x10, /* 0001000 */ + 0x1E, /* 0001111 */ + 0x10, /* 0001000 */ + 0x1E, /* 0001111 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* + * code=213, hex=0xD5, ascii="!U" + */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x1E, /* 0001111 */ + 0x10, /* 0001000 */ + 0x1E, /* 0001111 */ + 0x10, /* 0001000 */ + 0x10, /* 0001000 */ + 0x10, /* 0001000 */ + + /* + * code=214, hex=0xD6, ascii="!V" + */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x3E, /* 0011111 */ + 0x28, /* 0010100 */ + 0x28, /* 0010100 */ + 0x28, /* 0010100 */ + 0x28, /* 0010100 */ + + /* + * code=215, hex=0xD7, ascii="!W" + */ + 0x28, /* 0010100 */ + 0x28, /* 0010100 */ + 0x28, /* 0010100 */ + 0x28, /* 0010100 */ + 0xE8, /* 1110100 */ + 0x28, /* 0010100 */ + 0x28, /* 0010100 */ + 0x28, /* 0010100 */ + 0x28, /* 0010100 */ + + /* + * code=216, hex=0xD8, ascii="!X" + */ + 0x10, /* 0001000 */ + 0x10, /* 0001000 */ + 0x10, /* 0001000 */ + 0xFE, /* 1111111 */ + 0x10, /* 0001000 */ + 0xFE, /* 1111111 */ + 0x10, /* 0001000 */ + 0x10, /* 0001000 */ + 0x10, /* 0001000 */ + + /* + * code=217, hex=0xD9, ascii="!Y" + */ + 0x10, /* 0001000 */ + 0x10, /* 0001000 */ + 0x10, /* 0001000 */ + 0x10, /* 0001000 */ + 0xF0, /* 1111000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* + * code=218, hex=0xDA, ascii="!Z" + */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x1E, /* 0001111 */ + 0x10, /* 0001000 */ + 0x10, /* 0001000 */ + 0x10, /* 0001000 */ + 0x10, /* 0001000 */ + + /* + * code=219, hex=0xDB, ascii="![" + */ + 0xFE, /* 1111111 */ + 0xFE, /* 1111111 */ + 0xFE, /* 1111111 */ + 0xFE, /* 1111111 */ + 0xFE, /* 1111111 */ + 0xFE, /* 1111111 */ + 0xFE, /* 1111111 */ + 0xFE, /* 1111111 */ + 0xFE, /* 1111111 */ + + /* + * code=220, hex=0xDC, ascii="!\" + */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0xFE, /* 1111111 */ + 0xFE, /* 1111111 */ + 0xFE, /* 1111111 */ + 0xFE, /* 1111111 */ + + /* + * code=221, hex=0xDD, ascii="!]" + */ + 0xF0, /* 1111000 */ + 0xF0, /* 1111000 */ + 0xF0, /* 1111000 */ + 0xF0, /* 1111000 */ + 0xF0, /* 1111000 */ + 0xF0, /* 1111000 */ + 0xF0, /* 1111000 */ + 0xF0, /* 1111000 */ + 0xF0, /* 1111000 */ + + /* + * code=222, hex=0xDE, ascii="!^" + */ + 0x0E, /* 0000111 */ + 0x0E, /* 0000111 */ + 0x0E, /* 0000111 */ + 0x0E, /* 0000111 */ + 0x0E, /* 0000111 */ + 0x0E, /* 0000111 */ + 0x0E, /* 0000111 */ + 0x0E, /* 0000111 */ + 0x0E, /* 0000111 */ + + /* + * code=223, hex=0xDF, ascii="!_" + */ + 0xFE, /* 1111111 */ + 0xFE, /* 1111111 */ + 0xFE, /* 1111111 */ + 0xFE, /* 1111111 */ + 0xFE, /* 1111111 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* + * code=224, hex=0xE0, ascii="!`" + */ + 0x00, /* 0000000 */ + 0x34, /* 0011010 */ + 0x68, /* 0110100 */ + 0x68, /* 0110100 */ + 0x68, /* 0110100 */ + 0x34, /* 0011010 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* + * code=225, hex=0xE1, ascii="!a" + */ + 0x7C, /* 0111110 */ + 0x66, /* 0110011 */ + 0x66, /* 0110011 */ + 0x6C, /* 0110110 */ + 0x66, /* 0110011 */ + 0x62, /* 0110001 */ + 0x66, /* 0110011 */ + 0x6C, /* 0110110 */ + 0x08, /* 0000100 */ + + /* + * code=226, hex=0xE2, ascii="!b" + */ + 0x00, /* 0000000 */ + 0x7E, /* 0111111 */ + 0x62, /* 0110001 */ + 0x60, /* 0110000 */ + 0x60, /* 0110000 */ + 0x60, /* 0110000 */ + 0x60, /* 0110000 */ + 0x60, /* 0110000 */ + 0x00, /* 0000000 */ + + /* + * code=227, hex=0xE3, ascii="!c" + */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x6C, /* 0110110 */ + 0xFE, /* 1111111 */ + 0xF6, /* 1111011 */ + 0x66, /* 0110011 */ + 0x6C, /* 0110110 */ + 0x6C, /* 0110110 */ + 0x00, /* 0000000 */ + + /* + * code=228, hex=0xE4, ascii="!d" + */ + 0x00, /* 0000000 */ + 0xFE, /* 1111111 */ + 0xC6, /* 1100011 */ + 0x60, /* 0110000 */ + 0x38, /* 0011100 */ + 0x30, /* 0011000 */ + 0x66, /* 0110011 */ + 0xFE, /* 1111111 */ + 0x00, /* 0000000 */ + + /* + * code=229, hex=0xE5, ascii="!e" + */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x3E, /* 0011111 */ + 0x6C, /* 0110110 */ + 0x6C, /* 0110110 */ + 0x6C, /* 0110110 */ + 0x38, /* 0011100 */ + 0x00, /* 0000000 */ + + /* + * code=230, hex=0xE6, ascii="!f" + */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x36, /* 0011011 */ + 0x36, /* 0011011 */ + 0x36, /* 0011011 */ + 0x3E, /* 0011111 */ + 0x62, /* 0110001 */ + 0x40, /* 0100000 */ + + /* + * code=231, hex=0xE7, ascii="!g" + */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x7A, /* 0111101 */ + 0x6A, /* 0110101 */ + 0x0E, /* 0000111 */ + 0x0C, /* 0000110 */ + 0x18, /* 0001100 */ + 0x18, /* 0001100 */ + 0x00, /* 0000000 */ + + /* + * code=232, hex=0xE8, ascii="!h" + */ + 0x3C, /* 0011110 */ + 0x18, /* 0001100 */ + 0x3C, /* 0011110 */ + 0x66, /* 0110011 */ + 0x66, /* 0110011 */ + 0x3C, /* 0011110 */ + 0x18, /* 0001100 */ + 0x3C, /* 0011110 */ + 0x00, /* 0000000 */ + + /* + * code=233, hex=0xE9, ascii="!i" + */ + 0x00, /* 0000000 */ + 0x3C, /* 0011110 */ + 0x66, /* 0110011 */ + 0x66, /* 0110011 */ + 0x7E, /* 0111111 */ + 0x66, /* 0110011 */ + 0x66, /* 0110011 */ + 0x3C, /* 0011110 */ + 0x00, /* 0000000 */ + + /* + * code=234, hex=0xEA, ascii="!j" + */ + 0x00, /* 0000000 */ + 0x3C, /* 0011110 */ + 0x66, /* 0110011 */ + 0x66, /* 0110011 */ + 0x66, /* 0110011 */ + 0x66, /* 0110011 */ + 0x24, /* 0010010 */ + 0x66, /* 0110011 */ + 0x00, /* 0000000 */ + + /* + * code=235, hex=0xEB, ascii="!k" + */ + 0x00, /* 0000000 */ + 0x3C, /* 0011110 */ + 0x60, /* 0110000 */ + 0x30, /* 0011000 */ + 0x18, /* 0001100 */ + 0x3C, /* 0011110 */ + 0x66, /* 0110011 */ + 0x3C, /* 0011110 */ + 0x00, /* 0000000 */ + + /* + * code=236, hex=0xEC, ascii="!l" + */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x34, /* 0011010 */ + 0x4A, /* 0100101 */ + 0x4A, /* 0100101 */ + 0x4A, /* 0100101 */ + 0x34, /* 0011010 */ + 0x00, /* 0000000 */ + + /* + * code=237, hex=0xED, ascii="!m" + */ + 0x04, /* 0000010 */ + 0x3C, /* 0011110 */ + 0x66, /* 0110011 */ + 0x6E, /* 0110111 */ + 0x76, /* 0111011 */ + 0x66, /* 0110011 */ + 0x3C, /* 0011110 */ + 0x10, /* 0001000 */ + 0x20, /* 0010000 */ + + /* + * code=238, hex=0xEE, ascii="!n" + */ + 0x1E, /* 0001111 */ + 0x30, /* 0011000 */ + 0x60, /* 0110000 */ + 0x60, /* 0110000 */ + 0x7E, /* 0111111 */ + 0x60, /* 0110000 */ + 0x30, /* 0011000 */ + 0x1E, /* 0001111 */ + 0x00, /* 0000000 */ + + /* + * code=239, hex=0xEF, ascii="!o" + */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x3C, /* 0011110 */ + 0x66, /* 0110011 */ + 0x66, /* 0110011 */ + 0x66, /* 0110011 */ + 0x66, /* 0110011 */ + 0x66, /* 0110011 */ + 0x00, /* 0000000 */ + + /* + * code=240, hex=0xF0, ascii="!p" + */ + 0x00, /* 0000000 */ + 0x7C, /* 0111110 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x7C, /* 0111110 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x7C, /* 0111110 */ + 0x00, /* 0000000 */ + + /* + * code=241, hex=0xF1, ascii="!q" + */ + 0x00, /* 0000000 */ + 0x18, /* 0001100 */ + 0x18, /* 0001100 */ + 0x7E, /* 0111111 */ + 0x18, /* 0001100 */ + 0x18, /* 0001100 */ + 0x00, /* 0000000 */ + 0x7E, /* 0111111 */ + 0x00, /* 0000000 */ + + /* + * code=242, hex=0xF2, ascii="!r" + */ + 0x00, /* 0000000 */ + 0x30, /* 0011000 */ + 0x18, /* 0001100 */ + 0x0C, /* 0000110 */ + 0x18, /* 0001100 */ + 0x30, /* 0011000 */ + 0x00, /* 0000000 */ + 0x3C, /* 0011110 */ + 0x00, /* 0000000 */ + + /* + * code=243, hex=0xF3, ascii="!s" + */ + 0x00, /* 0000000 */ + 0x0C, /* 0000110 */ + 0x18, /* 0001100 */ + 0x30, /* 0011000 */ + 0x18, /* 0001100 */ + 0x0C, /* 0000110 */ + 0x00, /* 0000000 */ + 0x3C, /* 0011110 */ + 0x00, /* 0000000 */ + + /* + * code=244, hex=0xF4, ascii="!t" + */ + 0x0C, /* 0000110 */ + 0x1A, /* 0001101 */ + 0x18, /* 0001100 */ + 0x18, /* 0001100 */ + 0x18, /* 0001100 */ + 0x18, /* 0001100 */ + 0x18, /* 0001100 */ + 0x18, /* 0001100 */ + 0x18, /* 0001100 */ + + /* + * code=245, hex=0xF5, ascii="!u" + */ + 0x18, /* 0001100 */ + 0x18, /* 0001100 */ + 0x18, /* 0001100 */ + 0x18, /* 0001100 */ + 0x18, /* 0001100 */ + 0x18, /* 0001100 */ + 0x18, /* 0001100 */ + 0x58, /* 0101100 */ + 0x30, /* 0011000 */ + + /* + * code=246, hex=0xF6, ascii="!v" + */ + 0x00, /* 0000000 */ + 0x18, /* 0001100 */ + 0x18, /* 0001100 */ + 0x00, /* 0000000 */ + 0x7E, /* 0111111 */ + 0x00, /* 0000000 */ + 0x18, /* 0001100 */ + 0x18, /* 0001100 */ + 0x00, /* 0000000 */ + + /* + * code=247, hex=0xF7, ascii="!w" + */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x1A, /* 0001101 */ + 0x76, /* 0111011 */ + 0x00, /* 0000000 */ + 0x1A, /* 0001101 */ + 0x76, /* 0111011 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* + * code=248, hex=0xF8, ascii="!x" + */ + 0x00, /* 0000000 */ + 0x3C, /* 0011110 */ + 0x66, /* 0110011 */ + 0x66, /* 0110011 */ + 0x3C, /* 0011110 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* + * code=249, hex=0xF9, ascii="!y" + */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x18, /* 0001100 */ + 0x3C, /* 0011110 */ + 0x3C, /* 0011110 */ + 0x18, /* 0001100 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* + * code=250, hex=0xFA, ascii="!z" + */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x18, /* 0001100 */ + 0x18, /* 0001100 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* + * code=251, hex=0xFB, ascii="!{" + */ + 0x0E, /* 0000111 */ + 0x0C, /* 0000110 */ + 0x0C, /* 0000110 */ + 0x0C, /* 0000110 */ + 0x0C, /* 0000110 */ + 0x6C, /* 0110110 */ + 0x3C, /* 0011110 */ + 0x0C, /* 0000110 */ + 0x00, /* 0000000 */ + + /* + * code=252, hex=0xFC, ascii="!|" + */ + 0x00, /* 0000000 */ + 0x78, /* 0111100 */ + 0x6C, /* 0110110 */ + 0x6C, /* 0110110 */ + 0x6C, /* 0110110 */ + 0x6C, /* 0110110 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* + * code=253, hex=0xFD, ascii="!}" + */ + 0x00, /* 0000000 */ + 0x38, /* 0011100 */ + 0x4C, /* 0100110 */ + 0x18, /* 0001100 */ + 0x30, /* 0011000 */ + 0x7C, /* 0111110 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* + * code=254, hex=0xFE, ascii="!~" + */ + 0x00, /* 0000000 */ + 0x7C, /* 0111110 */ + 0x7C, /* 0111110 */ + 0x7C, /* 0111110 */ + 0x7C, /* 0111110 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* + * code=255, hex=0xFF, ascii="!^Ÿ" + */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ +}; diff --git a/wled00/const.h b/wled00/const.h index 8819203911..d3953cd85d 100644 --- a/wled00/const.h +++ b/wled00/const.h @@ -5,6 +5,8 @@ * Readability defines and their associated numerical values + compile-time constants */ +#define GRADIENT_PALETTE_COUNT 58 + //Defaults #define DEFAULT_CLIENT_SSID "Your_Network" #define DEFAULT_AP_PASS "wled1234" @@ -77,6 +79,7 @@ #define USERMOD_ID_MY9291 28 //Usermod "usermod_MY9291.h" #define USERMOD_ID_SI7021_MQTT_HA 29 //Usermod "usermod_si7021_mqtt_ha.h" #define USERMOD_ID_BME280 30 //Usermod "usermod_bme280.h +#define USERMOD_ID_AUDIOREACTIVE 31 //Usermod "audioreactive.h" //Access point behavior #define AP_BEHAVIOR_BOOT_NO_CONN 0 //Open AP when no connection after boot @@ -151,6 +154,7 @@ #define TYPE_WS2812_RGB 22 #define TYPE_GS8608 23 //same driver as WS2812, but will require signal 2x per second (else displays test pattern) #define TYPE_WS2811_400KHZ 24 //half-speed WS2812 protocol, used by very old WS2811 units +#define TYPE_TM1829 25 #define TYPE_SK6812_RGBW 30 #define TYPE_TM1814 31 //"Analog" types (PWM) (32-47) @@ -224,7 +228,11 @@ #define SEG_OPTION_MIRROR 3 //Indicates that the effect will be mirrored within the segment #define SEG_OPTION_NONUNITY 4 //Indicates that the effect does not use FRAMETIME or needs getPixelColor #define SEG_OPTION_FREEZE 5 //Segment contents will not be refreshed +#define SEG_OPTION_RESET 6 //Segment runtime requires reset #define SEG_OPTION_TRANSITIONAL 7 +#define SEG_OPTION_REVERSED_Y 8 +#define SEG_OPTION_MIRROR_Y 9 +#define SEG_OPTION_TRANSPOSED 10 //Segment differs return byte #define SEG_DIFFERS_BRI 0x01 @@ -241,6 +249,7 @@ // WLED Error modes #define ERR_NONE 0 // All good :) #define ERR_EEP_COMMIT 2 // Could not commit to EEPROM (wrong flash layout?) +#define ERR_NOBUF 3 // JSON buffer was not released in time, request cannot be handled at this time #define ERR_JSON 9 // JSON parsing failed (input too large?) #define ERR_FS_BEGIN 10 // Could not init filesystem (no partition?) #define ERR_FS_QUOTA 11 // The FS is full or the maximum file size is reached @@ -278,7 +287,7 @@ #endif #ifndef MAX_LEDS_PER_BUS -#define MAX_LEDS_PER_BUS 4096 +#define MAX_LEDS_PER_BUS 2048 // may not be enough for fast LEDs (i.e. APA102) #endif // string temp buffer (now stored in stack locally) @@ -321,14 +330,10 @@ #ifdef ESP8266 #define JSON_BUFFER_SIZE 10240 #else - #define JSON_BUFFER_SIZE 20480 + #define JSON_BUFFER_SIZE 24576 #endif -#ifdef WLED_USE_DYNAMIC_JSON - #define MIN_HEAP_SIZE JSON_BUFFER_SIZE+512 -#else - #define MIN_HEAP_SIZE 4096 -#endif +#define MIN_HEAP_SIZE (MAX_LED_MEMORY+2048) // Maximum size of node map (list of other WLED instances) #ifdef ESP8266 @@ -339,10 +344,10 @@ //this is merely a default now and can be changed at runtime #ifndef LEDPIN -#ifdef ESP8266 +#if defined(ESP8266) || (defined(ARDUINO_ARCH_ESP32) && defined(WLED_USE_PSRAM)) #define LEDPIN 2 // GPIO2 (D4) on Wemod D1 mini compatible boards #else - #define LEDPIN 2 // Changed from 16 to restore compatibility with ESP32-pico + #define LEDPIN 16 // aligns with GPIO2 (D4) on Wemos D1 mini32 compatible boards #endif #endif @@ -360,4 +365,37 @@ #define INTERFACE_UPDATE_COOLDOWN 2000 //time in ms to wait between websockets, alexa, and MQTT updates +#if defined(ESP8266) && defined(HW_PIN_SCL) + #undef HW_PIN_SCL +#endif +#if defined(ESP8266) && defined(HW_PIN_SDA) + #undef HW_PIN_SDA +#endif +#ifndef HW_PIN_SCL + #define HW_PIN_SCL SCL +#endif +#ifndef HW_PIN_SDA + #define HW_PIN_SDA SDA +#endif + +#if defined(ESP8266) && defined(HW_PIN_CLOCKSPI) + #undef HW_PIN_CLOCKSPI +#endif +#if defined(ESP8266) && defined(HW_PIN_DATASPI) + #undef HW_PIN_DATASPI +#endif +#if defined(ESP8266) && defined(HW_PIN_CSSPI) + #undef HW_PIN_CSSPI +#endif +// defaults for VSPI +#ifndef HW_PIN_CLOCKSPI + #define HW_PIN_CLOCKSPI SCK +#endif +#ifndef HW_PIN_DATASPI + #define HW_PIN_DATASPI MOSI +#endif +#ifndef HW_PIN_CSSPI + #define HW_PIN_CSSPI SS +#endif + #endif diff --git a/wled00/data/index.css b/wled00/data/index.css index 863528cea3..71cea3093c 100644 --- a/wled00/data/index.css +++ b/wled00/data/index.css @@ -16,7 +16,10 @@ --c-c: #ccc; --c-e: #eee; --c-d: #ddd; - --c-r: #831; + --c-r: #c32; + --c-g: #2c1; + --c-l: #48a; + --c-y: #a90; --t-b: 0.5; --c-o: rgba(34, 34, 34, 0.9); --c-tb : rgba(34, 34, 34, var(--t-b)); @@ -29,6 +32,7 @@ --tbp: 14px 14px 10px 14px; --bbp: 9px 0 7px 0; --bhd: none; + --sgp: "block"; --bmt: 0px; } @@ -44,8 +48,10 @@ body { color: var(--c-f); text-align: center; -webkit-touch-callout: none; - -webkit-user-select: none; - user-select: none; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; -webkit-tap-highlight-color: transparent; scrollbar-width: 6px; scrollbar-color: var(--c-sb) transparent; @@ -73,8 +79,13 @@ body { p { margin: 10px 0 2px 0; +} +a, p, a:visited { color: var(--c-d); } +a, a:visited { + text-decoration: none; +} button { outline: none; @@ -89,10 +100,11 @@ button { #namelabel { position: fixed; bottom: calc(var(--bh) + 6px); - right: 4px; - color: var(--c-6); + right: 6px; + color: var(--c-d); cursor: pointer; writing-mode: vertical-rl; + transform: rotate(180deg); } .bri { @@ -111,9 +123,20 @@ button { .icons { font-family: 'WIcons'; font-style: normal; - font-size: 24px; - line-height: 1; + font-size: 24px !important; + line-height: 1 !important; display: inline-block; +} + +.icons.on { + color: var(--c-g); +} + +.icons.off { + color: var(--c-6); +} + +.top .icons, .bot .icons { margin: -2px 0 4px 0; } @@ -121,75 +144,79 @@ button { font-size: 42px; } -.infot { +.segt, .plentry TABLE { table-layout: fixed; width: 100%; } -.segtd { +.segt TD { + padding: 2px !important; text-align: center; - text-transform: uppercase; - font-size: 14px; + /*text-transform: uppercase;*/ +} +.segt TD, .plentry TD { + font-size: 13px; + padding: 0; + vertical-align: middle; } .keytd { text-align: left; - padding-bottom: 8px; } .valtd { text-align: right; - padding-bottom: 8px; } -.slider-icon -{ - transform: translate(6px,3px); - color: var(--c-d); +.valtd i { + font-size: small; +} + +.slider-icon { + transform: translate(3px,3px); } -.e-icon -{ +.e-icon { transform: translateY(3px); - color: var(--c-d); } .sel-icon { transform: translateX(3px); - color: var(--c-d); } -.edit-icon { - position: absolute; - right: -26px; - top: 10px; - display: none; +.e-icon, .sel-icon, .slider-icon { + cursor: pointer; + color: var(--c-d); } .search-icon { position: absolute; - left: 8px; - top: 10px; - pointer-events: none; + top: 8px; + left: 12px; + /*pointer-events: none;*/ + width: 24px; + height: 24px; } -.search-cancel-icon { +.clear-icon { position: absolute; - right: 8px; - top: 9px; - cursor: pointer; display: none; + top: 8px; + right: 9px; + cursor: pointer; } .flr { - float: right; - cursor: pointer; - margin: 0; color: var(--c-f); transform: rotate(0deg); transition: transform 0.3s; + position: absolute; + top: 0; + right: 0; + padding: 8px; } +.expanded .flr, .exp { transform: rotate(180deg); } @@ -206,6 +233,17 @@ button { border: 0px; } +#liveview2D { + height: 90%; + display: none; + width: 90%; + border: 0px; + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%,-50%); +} + .tab { background-color: transparent; color: var(--c-d); @@ -226,15 +264,18 @@ button { transition: color 0.3s, background-color 0.3s; font-size: 17px; color: var(--c-c); + min-width: 44px; } .top button { padding: var(--tbp); + margin: 0; } .bot button { padding: var(--bbp); width:25%; + margin: 0; } .tab button:hover { @@ -267,7 +308,6 @@ button { position: relative; width: 100%; width: calc(100%/var(--n)); - padding: 11px 0; box-sizing: border-box; border: 0px; overflow: auto; @@ -276,12 +316,30 @@ button { } #Effects { - padding-top: 0; - margin-top: 11px; - height: calc(100% - 11px); -webkit-overflow-scrolling: touch; } +#segutil, #segutil2, #segcont, #putil, #pcont, #pql { + width: 280px; +} + +#putil, #segutil, #segutil2 { + min-height: 42px; + margin: 13px auto 0; +} + +#segutil .segin { + padding-top: 12px; +} + +#pql, #segcont, #pcont { + margin: 0 auto; +} + +#putil { + padding: 5px 0 0; +} + .smooth { transition: transform calc(var(--f, 1)*.5s) ease-out } .tab-label { @@ -307,26 +365,113 @@ button { pointer-events: none; } -.staytop { +.staytop, .staybot { display: block; position: -webkit-sticky; + position: sticky !important; + top: 0; + z-index: 2; + margin: 0 auto auto; +} + +.staybot { + bottom: 5px; +} + +#sliders { + width: 300px; + margin: 0 auto; position: sticky; - background: var(--c-1); - top: -1px; + bottom: 0; +} + +#sliders .labels { + padding-top: 3px; + font-size: small; +} + +.slider { + background-color: var(--c-2); + max-width: 300px; + min-width: 280px; + margin: 0 auto; /* add 5px; if you want some vertical space but looks ugly */ + border-radius: 24px; + position: relative; +} + +.filter, .option { + background-color: var(--c-4); + border-radius: 26px; + height: 26px; + margin: 0 auto; /* add 4-8px if you want space at the bottom */ + padding: 4px 2px; + position: relative; z-index: 1; - margin-top: 1px; - width: 272px; - margin: auto; - border-radius: 25px; + opacity: 1; + transition: opacity 0.5s linear, height 0.5s, transform 0.5s; + transform: scaleY(1); } -#staytop1 { - top: 28px; +.option { + z-index: unset; } -#fxb0 { - margin-bottom: 2px; - filter: drop-shadow(0 0 1px #000); +/* Tooltip text */ +.slider .tooltiptext, .option .tooltiptext { + visibility: hidden; + background-color: var(--c-5); + /*border: 2px solid var(--c-2);*/ + box-shadow: 4px 4px 10px 4px var(--c-1); + color: var(--c-f); + text-align: center; + padding: 5px 10px; + border-radius: 6px; + + /* Position the tooltip text */ + width: 160px; + position: absolute; + z-index: 1; + bottom: 100%; + left: 50%; + margin-left: -92px; + + /* Fade in tooltip */ + opacity: 0; + transition: opacity 0.75s; +} +.option .tooltiptext { + bottom: 120%; +} +/* Tooltip arrow */ +.slider .tooltiptext::after, .option .tooltiptext::after { + content: ""; + position: absolute; + top: 100%; + left: 50%; + margin-left: -5px; + border-width: 5px; + border-style: solid; + border-color: var(--c-5) transparent transparent transparent; +} +/* Show the tooltip text when you mouse over the tooltip container */ +.slider:hover .tooltiptext, .option .check:hover .tooltiptext { + visibility: visible; + opacity: 1; +} + +#pql, .edit-icon { + display: none; +} + +.hide { + display: none !important; +} +.fade { + visibility: hidden; /* hide it */ + opacity: 0; /* make it transparent */ + transform: scaleY(0); /* shrink content */ + height: 0px; /* force other elements to move */ + padding: 0; /* remove empty space */ } .first { @@ -336,11 +481,12 @@ button { #toast { opacity: 0; background-color: var(--c-5); + border: 1px solid var(--c-2); max-width: 90%; color: var(--c-f); text-align: center; border-radius: 5px; - padding: 16px; + padding: 22px; position: fixed; z-index: 5; left: 50%; @@ -375,26 +521,24 @@ button { overflow: auto; } -.modal button:hover { - background-color: var(--c-4); +.close { + position: sticky; + top: 0; + float: right; } -#info { - z-index: 3; +#info, #nodes { + z-index: 4; } -#rover, #nodes { - z-index: 2; +#rover { + z-index: 3; } #ndlt { margin: 12px 0; } -.valtd i { - font-size: 14px; -} - #roverstar { position: fixed; top: calc(var(--th) + 5px); @@ -413,20 +557,45 @@ button { z-index: -2; } -#imgw { - display: inline-block; - margin: 8px; +#info .slider { + max-width: 200px; + min-width: 145px; + float: right; + margin: 0; +} +#info .sliderwrap { + width: 200px; } -#kv, #kn { - max-width: 490px; - display: inline-block; +#info table, #nodes table { + table-layout: fixed; + width: 100%; } -#kn td { - padding-bottom: 12px; +#info td, #nodes td { + padding-bottom: 8px; +} + +#info .btn { + margin: 5px; +} +#info table .btn, #nodes table .btn { + margin: 0; +} +#info div, #nodes div { + max-width: 490px; + margin: 0 auto; } +#imgw { + /*display: inline-block;*/ + margin: 8px auto; +} +/* +#kv, #kn { + display: inline-block; +} +*/ #lv { max-width: 600px; display: inline-block; @@ -457,44 +626,52 @@ img { .sliderdisplay { content:''; position: absolute; - top: 12.5px; bottom: 12.5px; - left: 13px; right: 13px; + top: 12px; left: 8px; right: 8px; + height: 5px; background: var(--c-4); - border-radius: 17px; + border-radius: 16px; pointer-events: none; z-index: -1; --bg: var(--c-f); } -#rwrap .sliderdisplay { --bg: #f00; } -#gwrap .sliderdisplay { --bg: #0f0; } -#bwrap .sliderdisplay { --bg: #00f; } -#wbal .sliderdisplay, #kwrap .sliderdisplay { - background: linear-gradient(90deg, #ff8f1f 0%, #fff 50%, #cbdbff); +#rwrap .sliderdisplay { --bg: none; background: linear-gradient(90deg, #000 -15%, #f00); } /* -15% since #000 is too dark */ +#gwrap .sliderdisplay { --bg: none; background: linear-gradient(90deg, #000 -15%, #0f0); } /* -15% since #000 is too dark */ +#bwrap .sliderdisplay { --bg: none; background: linear-gradient(90deg, #000 -15%, #00f); } /* -15% since #000 is too dark */ +#wwrap .sliderdisplay { --bg: none; background: linear-gradient(90deg, #000 -15%, #fff); } /* -15% since #000 is too dark */ +#kwrap .sliderdisplay, +#wbal .sliderdisplay { background: linear-gradient(90deg, #ff8f1f 0%, #fff 50%, #cbdbff); } + +/* wrapper divs hidden by default */ +#rgbwrap, #kwrap, #wwrap, #wbal, #qcs-w, #hexw { + display: none; } .sliderbubble { - width: 36px; - line-height: 24px; + width: 24px; + position: relative; + display: inline-block; + border-radius: 10px; background: var(--c-3); - position: absolute; - transform: translateX(-50%); - border-radius: 12px; - margin-left: 12px; - margin-top: 3px; - padding: 0px; - display: inline; + color: var(--c-f); + padding: 4px 4px 2px; + font-size: 14px; + right: 3px; + transition: visibility 0.25s ease, opacity 0.25s ease; + opacity: 0; + visibility: hidden; } -.hidden { - display: none; +output.sliderbubbleshow { + visibility: visible; + opacity: 1; } input[type=range] { -webkit-appearance: none; - width: 220px; - padding: 0px; - margin: 0px 10px 0px 10px; + width: 100%; + padding: 0; + margin: 0; background-color: transparent; cursor: pointer; } @@ -511,7 +688,7 @@ input[type=range]::-webkit-slider-runnable-track { input[type=range]::-webkit-slider-thumb { height: 16px; width: 16px; - border-radius: 17px; + border-radius: 50%; background: var(--c-f); cursor: pointer; -webkit-appearance: none; @@ -526,34 +703,31 @@ input[type=range]::-moz-range-thumb { border: 0px solid rgba(0, 0, 0, 0); height: 16px; width: 16px; - border-radius: 17px; + border-radius: 50%; background: var(--c-f); - transform: translateY(7px); + transform: translateY(5px); } -input[type=range]:active + .sliderbubble { - display: inline; - transform: translateX(-50%); +#Colors input[type=range]::-webkit-slider-thumb { + height: 18px; + width: 18px; + border: 2px solid var(--c-1); + margin-top: 5px; } -/* hide color controls until enabled in updateUI() */ -#pwrap, #wwrap, #wbal, #rgbwrap, #palwrap { - display: none; +#Colors input[type=range]::-moz-range-thumb { + border: 2px solid var(--c-1); } /* Slider wrapper div */ .sliderwrap { height: 30px; - width: 240px; + width: 230px; position: relative; + z-index: 0; } -/* Segment power button + brightness slider wrapper div */ -.sbs { - margin: 0px -20px 5px -6px; -} - -/* Segment brightness slider wrapper div */ -.sws { - margin-left: -7px; +#Colors .sliderwrap { + width: 260px; + margin: 10px 0 0; } /* Dynamically hide brightness slider label */ @@ -566,242 +740,265 @@ input[type=range]:active + .sliderbubble { margin-top: var(--bmt); } -#picker { - margin: 10px auto; +#picker, #rgbwrap, #kwrap, #wwrap, #wbal, #vwrap, #qcs-w, #hexw, #pall { + margin: 0 auto; width: 260px; } +#picker { + margin-top: 10px; +} + +/* buttons */ .btn { padding: 8px; - margin: 10px; + margin: 10px 4px; width: 230px; font-size: 19px; - background-color: var(--c-3); - color: var(--c-f); + color: var(--c-d); cursor: pointer; - border: 0px solid white; border-radius: 25px; - transition-duration: 0.5s; - /*-webkit-backface-visibility: hidden; - -webkit-transform:translate3d(0,0,0);*/ + transition-duration: 0.3s; + -webkit-backface-visibility: hidden; + -webkit-transform:translate3d(0,0,0); + backface-visibility: hidden; + transform:translate3d(0,0,0); + overflow: clip; + text-overflow: clip; + border: 1px solid var(--c-3); + background-color: var(--c-3); } - -/* Small round button (color selectors, icon-only round buttons) */ -.xxs { - width: 40px; - height: 40px; - margin: 6px; +#segutil .btn-s:hover, +#segutil2 .btn-s:hover, +#putil .btn-s:hover, +.btn:hover { + border: 1px solid var(--c-5) /*!important*/; + background-color: var(--c-5) /*!important*/; } - -/* Segments/presets auxiliary buttons (Add segment, Create preset, ...) */ .btn-s { - width: 276px; - background-color: var(--c-2); -} -.btn-i { - padding-bottom: 4px; + width: 100%; + margin: 0; } .btn-icon { - margin: 0px 8px 4px 0; + margin: -4px 4px 0 0; vertical-align: middle; + display: inline-block; +} +.btn-n { + width: 230px; + margin: 0 8px 0 0; } - -/* Wide button used in presets (Save, playlist test, delete) */ .btn-p { - width: 216px; + width: 120px; + margin: 5px 0; +} +.btn-xs, .btn-pl-del, .btn-pl-add { + width: 42px !important; + height: 42px !important; +} +.btn-xs { + margin: 2px 0 0 0; +} +#putil .btn-xs { + margin: 0; +} +#info .btn-xs { + border: 1px solid var(--c-4); } -/* Delete preset from playlist button */ -.btn-pl-del { - margin: 0 0 0 3px; +#putil .btn-s { + width: 135px; } -/* Add preset to playlist "+" button */ -.btn-pl-add { - margin: 3px 0 0 8px; +#nodes .infobtn { + margin: 0; } -/* Quick color select buttons wrapper div */ +#segutil .btn-s, #segutil2 .btn-s, #putil .btn-s { + background-color: var(--c-3); + border: 1px solid var(--c-3); +} + +.btn-pl-del, .btn-pl-add { + margin: 0; + white-space: nowrap; +} + +/* Quick color select wrapper div */ #qcs-w { margin-top: 10px; - display: none; } /* Quick color select buttons */ .qcs { - padding: 14px; margin: 2px; border-radius: 14px; display: inline-block; + width: 28px; + height: 28px; + line-height: 28px; } /* Quick color select Black button (has white border) */ .qcsb { - padding: 13px; - border: 1px solid var(--c-f); + width: 26px; + height: 26px; + line-height: 26px; + border: 1px solid #fff; } /* Hex color input wrapper div */ #hexw { margin-top: 5px; - display: none; } -/* Transition time input */ +select { + padding: 4px 8px; + margin: 0; + font-size: 19px; + background-color: var(--c-3); + color: var(--c-d); + cursor: pointer; + border: 0 solid var(--c-2); + border-radius: 20px; + transition-duration: 0.5s; + -webkit-backface-visibility: hidden; + -webkit-transform:translate3d(0,0,0); + -webkit-appearance: none; + -moz-appearance: none; + backface-visibility: hidden; + transform:translate3d(0,0,0); + text-overflow: ellipsis; +} #tt { text-align: center; } - -/* Color slot select buttons (1,2,3) */ .cl { - width: 38.5px; - height: 38.5px; - margin: 7px; background-color: #000; - box-shadow: 0 0 0 1.5px #fff; } -.selected.cl { - box-shadow: 0 0 0 5px #fff; -} - -/* Playlist preset select */ -.sel-pl { - width: 192px; - background-position: 168px 16px; - margin: 8px 3px 0 0; -} - -/* Playlist end preset select */ -.sel-ple { - width: 216px; - background-position: 192px 16px; +select.sel-p, select.sel-pl, select.sel-ple { + margin: 5px 0; + width: 100%; + height: 40px; } - -select { - -webkit-appearance: none; - appearance: none; - background: url("data:image/svg+xml;utf8,") no-repeat; - background-size: 12px; - background-position: 206px 16px; - padding-left: 12px !important; - background-repeat: no-repeat; - outline: none; +div.sel-p { + position: relative; } -select:-moz-focusring { - transition-duration: 0s; - color: transparent; - text-shadow: 0 0 0 #fff; +div.sel-p:after { + content: ""; + position: absolute; + right: 10px; + top: 22px; + width: 0; + height: 0; + border-left: 8px solid transparent; + border-right: 8px solid transparent; + border-top: 8px solid var(--c-f); +} +select.sel-ple { + text-align: center; } option { background-color: var(--c-3); color: var(--c-f); } - -input[type=number], input[type=text] { +input[type=number], +input[type=text] { background: var(--c-3); color: var(--c-f); - border: 0px solid white; - border-radius: 25px; + border: 0px solid var(--c-2); + border-radius: 5px; padding: 8px; - margin: 6px 6px 6px 0; + /*margin: 6px 6px 6px 0;*/ font-size: 19px; transition: background-color 0.2s; outline: none; - width: 50px; -webkit-appearance: textfield; - appearance: textfield; -} - -textarea { - background: var(--c-2); - color: var(--c-f); - width: 236px; - height: 90px; - border-radius: 5px; - border: 2px solid #555; - outline: none; - resize: none; - font-size: 19px; + -moz-appearance: textfield; + appearance: textfield; } -::selection { - background: var(--c-b); +input[type=number] { + text-align: right; + width: 50px; } input[type=text] { - width: 100px; text-align: center; } -input[type=text].ptxt { - width: 200px; - margin: 26px 0 6px 12px; +input[type=number]:focus, +input[type=text]:focus { + background: var(--c-6); } -input[type=text].stxt { - display: none; - margin-top: 6px; +input[type=number]::-webkit-inner-spin-button, +input[type=number]::-webkit-outer-spin-button { + -webkit-appearance: none; } -input[type=text].qltxt { - width: 50px; +#hexw input[type=text] { + width: 6em; } -input[type=number]:focus, input[type=text]:focus { - background: var(--c-6); +input[type=text].ptxt { + width: calc(100% - 24px); } -input[type=number]::-webkit-inner-spin-button, -input[type=number]::-webkit-outer-spin-button { - -webkit-appearance: none; +textarea { + background: var(--c-2); + color: var(--c-f); + width: calc(100% - 14px); /* +padding=260px */ + height: 90px; + border-radius: 5px; + border: 2px solid var(--c-5); + outline: none; + resize: none; + font-size: 19px; + padding: 5px; } -.pln { - width: 67px !important; - margin: 0 2px 8px 0 !important; - text-align: center; +.apitxt { + height: 7em; } -.plnl { - width: 86px; - margin: 0 2px 0 0; - display: inline-block; + +::selection { + background: var(--c-b); } -.pli { - width: 38px; - margin: 0 0 0 29px; - display: inline-block; + +.ptxt { + margin: 0 4px 4px !important; + display: none; } -.segn { - border-radius: 5px !important; - margin: 3px 0 6px 0 !important; +.stxt { + width: 50px !important; } -.pname, .plname, .segname { - position: absolute; - left: 50%; - transform: translateX(-50%); +.segname, .pname { white-space: nowrap; cursor: pointer; + text-align: center; + overflow: clip; + text-overflow: ellipsis; + line-height: 24px; + padding: 8px 24px; + max-width: 170px; + margin: 0 auto; + position: relative; } -.segntxt { - max-width: 160px; - overflow: hidden; - text-overflow: clip; +.segname .flr, .pname .flr { + transform: rotate(0deg); + right: -6px; } -.segname { - top: 0px; - padding: 9px 0; -} -.pname, .plname { - width: 208px; - padding: 8px 0; - text-align: center; - overflow: hidden; - text-overflow: clip; +/* segment power wrapper */ +.sbs { + padding: 4px 0 4px 8px; + display: var(--sgp); } + .pname { top: 1px; } @@ -809,13 +1006,12 @@ input[type=number]::-webkit-outer-spin-button { top:0; } +/* preset id number */ .pid { position: absolute; - top: 0px; - left: 0px; - padding: 11px 0px 0px 11px; + top: 8px; + left: 12px; font-size: 16px; - width: 20px; text-align: center; color: var(--c-b); } @@ -828,29 +1024,39 @@ input[type=number]::-webkit-outer-spin-button { padding: 6px 0 0 0; } -/* Quick preset select buttons */ -.psts { - background-color: var(--c-3); - color: var(--c-f); - cursor: pointer; - padding: 2px 0 0 0; - height: 40px; +.xxs { + width: 44px; + height: 44px; + margin: 5px; +} + +.xxs-w { + margin: 2px; + width: 50px; + height: 50px; +} + +#csl .xxs { + border: 2px solid var(--c-d) !important; + /*box-shadow: 0 0 0 2px var(--c-d);*/ +} +#csl .xxs-w { + border-width: 5px !important; + /*box-shadow: 0 0 0 5px var(--c-d);*/ +} + +.qcs, #namelabel { + text-shadow: -1px -1px 0 var(--c-4), 1px -1px 0 var(--c-4), -1px 1px 0 var(--c-4), 1px 1px 0 var(--c-4); } -/* Segment apply button (checkmark) */ -.cnf { +.psts { color: var(--c-f); - cursor: pointer; - background: var(--c-3); - border-radius: 5px; - padding: 8.5px 21px 5px; - display: inline; + margin: 4px; } -/* Segment power button icon */ .pwr { color: var(--c-6); - transform: translate(2px, 3px); + transform: translate(1px, 1px); cursor: pointer; } @@ -865,42 +1071,34 @@ input[type=number]::-webkit-outer-spin-button { } .frz { - left: 36px; + left: 32px; position: absolute; - top: 0px; + top: -3px; cursor: pointer; padding: 8px; + z-index: 1; } -/* TODO expanded does not seem to apply to .frz, what is this for? */ .expanded .frz { display: none; } +/* radiobuttons and checkmarks */ .check, .radio { - display: inline-block; + display: block; position: relative; - padding-bottom: 32px; - margin-bottom: 14px; cursor: pointer; - text-align: center; -} - -.schkl { - padding: 2px 5px 0px 35px; - margin: 0 0 0 2px; } .revchkl { - padding: 2px 0px 0px 35px; + padding: 4px 0px 0px 35px; margin-bottom: 0px; margin-top: 8px; } -.fxchkl { - position: absolute; - top: 0px; - left: 8px; +TD .revchkl { + padding: 0 0 0 32px; + margin-top: 0; } .check input, .radio input { @@ -913,82 +1111,69 @@ input[type=number]::-webkit-outer-spin-button { .checkmark, .radiomark { position: absolute; - left: 0; height: 24px; width: 24px; - background-color: var(--c-4); - border-radius: 10px; - /*border: 1px solid var(--c-2);*/ -} - -.checkmark { - top: 6px; + top: 0; + bottom: 0; + left: 0; + background-color: var(--c-3); + border: 1px solid var(--c-2); } .radiomark { + top: 8px; + left: 8px; + height: 22px; + width: 22px; border-radius: 50%; background-color: transparent; - top: 7px; -} - -.schk { - top: 0; -} - -.psv { - left: initial; - bottom: initial; - top: 0; - right: 0; } -.psvl { - padding: 2px 35px 10px 0px; - margin-top: 10px; - margin-bottom: 0px; +.checkmark { + border-radius: 10px; } - +.radio:hover input ~ .radiomark, .check:hover input ~ .checkmark { background-color: var(--c-5); } -.check input:checked ~ .checkmark { - background-color: var(--c-6); -} - .checkmark:after, .radiomark:after { content: ""; position: absolute; display: none; } -.check input:checked ~ .checkmark:after, .radio input:checked ~ .radiomark:after { - display: block; -} - .check .checkmark:after { - left: 8px; + left: 9px; top: 4px; width: 5px; height: 10px; border: solid var(--c-f); border-width: 0 3px 3px 0; +} + +.rot45, +.check .checkmark:after { -webkit-transform: rotate(45deg); -ms-transform: rotate(45deg); transform: rotate(45deg); } .radio .radiomark:after { - width: 12px; - height: 12px; + width: 14px; + height: 14px; top: 50%; left: 50%; - margin: -6px; + margin: -7px; border-radius: 50%; background: var(--c-f); } +TD .checkmark, TD .radiomark { + top: -6px; +} + .h { font-size: 13px; text-align: center; @@ -999,128 +1184,212 @@ input[type=number]::-webkit-outer-spin-button { margin-bottom: 5px; } +/* segment & preset wrapper */ .seg, .pres { - position: relative; - display: inline-block; - padding: 8px; - margin: 10px; - width: 260px; - font-size: 19px; background-color: var(--c-2); color: var(--c-f); - border: 0px solid white; - border-radius: 20px; + border: 0px solid var(--c-f); text-align: left; transition: background-color 0.5s; - filter: brightness(1); /* required for slider background to render? */ + /*filter: brightness(1);*/ + font-size: 19px; + border-radius: 21px; + min-width: 280px; } -.selected { - background-color: var(--c-4); +.seg { + top: auto !important; /* prevent sticky */ + bottom: auto !important; +} +/* checkmark labels */ +.seg .schkl { + position: absolute; + top: 7px; + left: 9px; } -/* "selected" CSS class is applied to the segment when it is the main segment. - By default, do not highlight. Can be overridden by skin.css */ -.selected.seg { - background-color: var(--c-2); /* var(--c-4); */ +/* checkmark labels */ +.filter .fchkl, .option .ochkl { + display: inline-block; + min-width: 0.7em; + padding: 1px 4px 4px 32px; + text-align: left; + line-height: 24px; + vertical-align: middle; + -webkit-filter: grayscale(100%); /* Safari 6.0 - 9.0 */ + filter: grayscale(100%); } -.selected .checkmark, .selected .radiokmark { - background-color: var(--c-4); /* var(--c-6); */ + +.lbl-s { + display: inline-block; + /* margin: 10px 4px 0 0; */ + margin-top: 6px; + font-size: 13px; + width: 48%; + text-align: center; } +/* list wrapper */ .list { position: relative; + width: 280px; transition: background-color 0.5s; - margin: auto auto 10px; - padding-bottom: 10px; - width: 230px; + margin: auto auto 20px; + font-size: 19px; + line-height: 24px; } +/* list item */ .lstI { - position: sticky; + align-items: center; + cursor: pointer; + background-color: var(--c-2); overflow: hidden; + position: sticky; + border-radius: 21px; + margin: 13px auto 0; + min-height: 40px; + border: 1px solid var(--c-2); } -.fxbtn { - margin: 20px auto; - padding: 8px 0; +#segutil .lstI { + margin-top: 0; } -.lstI:hover { +/* selected item/element */ +.selected { /* has to be after .lstI since !important is not ok */ background: var(--c-4); } -.lstI:last-child { - border: none; +#segcont .seg:hover:not([class*="expanded"]), +.lstI:hover:not([class*="expanded"]) { + background: var(--c-5); +} + +.selected .checkmark, +.selected .radiomark, +.selected input[type=number], +.selected input[type=text] { + background-color: var(--c-3); +} + +/* selected list item */ +.lstI.selected { + top: 0; + bottom: 0; } -.lstI.sticky, .lstI.selected { +.lstI.sticky, +.lstI.selected { z-index: 1; + box-shadow: 0px 0px 10px 4px var(--c-1); } -#pallist .lstI.selected { - top: 27px; - bottom: -11px; +#pcont .selected:not([class*="expanded"]) { + bottom: 52px; + top: 42px; } -#pallist .lstI.sticky { - top: -11px; +#fxlist .lstI.selected { + top: 84px; } -.lstI.selected { - background: var(--c-5); - top: 95px; - bottom: -11px; +#fxlist .lstI.sticky { + top: 42px; +} + +#pallist .lstI.selected { + top: 84px; +} + +#pallist .lstI.sticky { + top: 42px; } -.lstI.sticky { - top: 57px; +/* list item content */ +.lstIcontent { + padding: 9px 0 7px; + position: relative; } +/* list item name (for sorting) */ .lstIname { - margin: 3px 0; white-space: nowrap; - cursor: pointer; - font-size: 19px; + text-overflow: ellipsis; + -webkit-filter: grayscale(100%); /* Safari 6.0 - 9.0 */ + filter: grayscale(100%); } +/* list item palette preview */ .lstIprev { - width: 220px; - height: 5px; - margin: auto; + width: 100%; + height: 7px; position: absolute; - bottom: 0px; - left: 5px; + bottom: 0; + left: 0; + z-index: -1; } -input[type="text"].search { +/* find/search element */ +.fnd { + width: 280px; + margin: 0 auto; + position: relative; +} + +.fnd input[type="text"] { display: block; - width: 230px; + width: 100%; box-sizing: border-box; - padding: 8px 8px 9px 38px; - margin: 6px auto 0 auto; + padding: 8px 40px 8px 44px; + margin: 5px auto 0; text-align: left; - background-color: var(--c-3); + border-radius: 21px; + background: var(--c-2); + border: 1px solid var(--c-3); + -webkit-filter: grayscale(100%); /* Safari 6.0 - 9.0 */ + filter: grayscale(100%); } -input[type="text"].search:focus { +.fnd input[type="text"]:focus { background-color: var(--c-4); } -input[type="text"].search:not(:placeholder-shown) { - background-color: var(--c-5); +.fnd input[type="text"]:not(:placeholder-shown), +.fnd input[type="text"]:hover { + background-color: var(--c-3); } -.pres { - margin-bottom: 6px; +/* segment & preset inner/expanded content */ +.segin, +.presin { + padding: 8px; + position: relative; + width: 264px; } -.segin { - padding: 8px 8px 4px 8px; +.btn-s, +.btn-n { + border: 1px solid var(--c-2); + background-color: var(--c-2); +} +.modal .btn:hover, +.segin .btn:hover { + border: 1px solid var(--c-5) /*!important*/; + background-color: var(--c-5) /*!important*/; +} + +/* hidden list items, must be after .expanded */ +.pres .lstIcontent, .segin { display: none; } +.check input:checked ~ .checkmark:after, +.radio input:checked ~ .radiomark:after, +.show, +.expanded .edit-icon, +.expanded .segin, .expanded .presin, .expanded .sbs, .expanded { - display: block; + display: inline-block !important; } .c { @@ -1140,10 +1409,7 @@ input[type="text"].search:not(:placeholder-shown) { width: auto; height: 2px; background-color: var(--c-b); -} - -.no-margin { - margin: 0; + margin: 3px 0; } ::-webkit-scrollbar { @@ -1161,23 +1427,46 @@ input[type="text"].search:not(:placeholder-shown) { background: var(--c-sbh); } +@media not all and (hover: none) { + .sliderwrap:hover + output.sliderbubble { + visibility: visible; + opacity: 1; + } +} + @media all and (max-width: 335px) { .sliderbubble { - display: none; - } + display: none; + } } @media all and (max-width: 550px) and (min-width: 374px) { - .infobtn { - width: 155px; + #info table .btn, #nodes table .btn { + width: 200px; + } + #info .infobtn, #nodes .infobtn { + width: 145px; + } + #info div, #nodes div { + max-width: 320px; } } -@media all and (max-width: 685px) { +@media all and (max-width: 540px) { .top button { width: 16.6%; padding: 8px 0 4px 0; } +} + +@media all and (min-width: 541px) and (max-width: 719px) { + .top button { + width: 14.2%; + padding: 8px 0 4px 0; + } +} + +@media all and (max-width: 719px) { .hd { display: none !important; } @@ -1187,6 +1476,12 @@ input[type="text"].search:not(:placeholder-shown) { } } +@media all and (max-width: 798px) { + #buttonNodes { + display: none; + } +} + @media all and (max-width: 1249px) { #buttonPcm { display: none; diff --git a/wled00/data/index.htm b/wled00/data/index.htm index d0cc5f0eb6..f4b50c44f7 100644 --- a/wled00/data/index.htm +++ b/wled00/data/index.htm @@ -7,10 +7,52 @@ WLED - + - +
Loading WLED UI...
@@ -23,10 +65,10 @@ - - - - + + + +

Brightness

@@ -36,6 +78,7 @@
+ @@ -44,45 +87,65 @@
-
-
-
-
- -
-

-
+
+
+ +
+ +
+

+
+
+ +
+ +
+

-
+
+ +
+ +
+

+
+
+
-

RGB color

-
- -
-

-
- -
-

-
- -
-

+

RGB color

+
+
+ +
+
+
+
+
+ +
+
+
+
+
+ +
+
+
-

White channel

-
- +

White channel

+
+
-
-

White balance

+
+

White balance

@@ -99,63 +162,142 @@
-
R
+
R
- - - + + +
+

+ - +
-
-

- - Color palette -

-
-
-
- +

Color palette

+
+
+ + + +
+
+
+
+
-

Effect speed

-
- -
- - -
+
+

Effect mode

+
+ + +
-
-

Effect intensity

-
- -
- - -
+
+
+ +
-

Effect mode

-
- Loading... +
+
+ + + + + +
+
+ +
+ +
+
+ + Effect speed +
+
+ +
+ +
+
+ + Effect intensity +
+
+ +
+ +
+
+ + Custom 1 +
+
+ +
+ +
+
+ + Custom 2 +
+
+ +
+ +
+
+ + Custom 3 +
+
+ + + +
@@ -163,24 +305,27 @@
Loading...
-
- +
-

Transition: s

+

Transition:  s

-
- -
-
-
- Loading... +

Presets

+
+ + + +
+
+ Loading... +
+
@@ -193,25 +338,35 @@
-
+
+
Loading...

+
+ + + + +
+
+ Made with ❤︎ by Aircoookie and the WLED community
+ +
- - diff --git a/wled00/data/index.js b/wled00/data/index.js index 9de58ba949..f9f74111f6 100644 --- a/wled00/data/index.js +++ b/wled00/data/index.js @@ -3,93 +3,72 @@ var loc = false, locip; var noNewSegs = false; var isOn = false, nlA = false, isLv = false, isInfo = false, isNodes = false, syncSend = false, syncTglRecv = true; var hasWhite = false, hasRGB = false, hasCCT = false; -var whites = [0,0,0]; -var colors = [[0,0,0],[0,0,0],[0,0,0]]; -var expanded = [false]; -var powered = [true]; var nlDur = 60, nlTar = 0; var nlMode = false; var selectedFx = 0; +var selectedPal = 0; var csel = 0; // selected color slot (0-2) var currentPreset = -1; var lastUpdate = 0; var segCount = 0, ledCount = 0, lowestUnused = 0, maxSeg = 0, lSeg = 0; -var pcMode = false, pcModeA = false, lastw = 0; +var pcMode = false, pcModeA = false, lastw = 0, wW; var tr = 7; var d = document; -const ranges = RangeTouch.setup('input[type="range"]', {}); var palettesData; -var pJson = {}; +var fxdata = []; +var pJson = {}, eJson = {}, lJson = {}; var pN = "", pI = 0, pNum = 0; var pmt = 1, pmtLS = 0, pmtLast = 0; var lastinfo = {}; -var ws; -var fxlist = d.getElementById('fxlist'), pallist = d.getElementById('pallist'); +var isM = false, mw = 0, mh=0; +var ws, cpick, ranges; var cfg = { theme:{base:"dark", bg:{url:""}, alpha:{bg:0.6,tab:0.8}, color:{bg:""}}, comp :{colors:{picker: true, rgb: false, quick: true, hex: false}, - labels:true, pcmbot:false, pid:true, seglen:false, css:true, hdays:false} + labels:true, pcmbot:false, pid:true, seglen:false, segpwr:false, segexp:false, css:true, hdays:false} }; var hol = [ [0,11,24,4,"https://aircoookie.github.io/xmas.png"], // christmas [0,2,17,1,"https://images.alphacoders.com/491/491123.jpg"], // st. Patrick's day - [2022,3,17,2,"https://aircoookie.github.io/easter.png"], + [2025,3,20,2,"https://aircoookie.github.io/easter.png"], [2023,3,9,2,"https://aircoookie.github.io/easter.png"], - [2024,2,31,2,"https://aircoookie.github.io/easter.png"] + [2024,2,31,2,"https://aircoookie.github.io/easter.png"], + [0,6,4,1,"https://initiate.alphacoders.com/download/wallpaper/516792/images/jpg/510921363292536"], // 4th of July + [0,0,1,1,"https://initiate.alphacoders.com/download/wallpaper/1198800/images/jpg/2522807481585600"] // new year ]; -var cpick = new iro.ColorPicker("#picker", { - width: 260, - wheelLightness: false, - wheelAngle: 270, - wheelDirection: "clockwise", - layout: [ - { - component: iro.ui.Wheel, - options: {} - } - ] -}); +function handleVisibilityChange() {if (!d.hidden && new Date () - lastUpdate > 3000) requestJson();} +function sCol(na, col) {d.documentElement.style.setProperty(na, col);} +function gId(c) {return d.getElementById(c);} +function gEBCN(c) {return d.getElementsByClassName(c);} +function isEmpty(o) {return Object.keys(o).length === 0;} +function isObj(i) {return (i && typeof i === 'object' && !Array.isArray(i));} +function isNumeric(n) {return !isNaN(parseFloat(n)) && isFinite(n);} -function handleVisibilityChange() { - if (!d.hidden && new Date () - lastUpdate > 3000) { - requestJson(null); - } -} +// returns true if dataset R, G & B values are 0 +function isRgbBlack(a) {return (parseInt(a.r) == 0 && parseInt(a.g) == 0 && parseInt(a.b) == 0);} -function sCol(na, col) { - d.documentElement.style.setProperty(na, col); -} - -function isRgbBlack(a, s) { - return (a[s][0] == 0 && a[s][1] == 0 && a[s][2] == 0); -} - -// returns RGB color from a given slot s 0-2 from color array a -function rgbStr(a, s) { - return "rgb(" + a[s][0] + "," + a[s][1] + "," + a[s][2] + ")"; -} +// returns RGB color from a given dataset +function rgbStr(a) {return "rgb(" + a.r + "," + a.g + "," + a.b + ")";} // brightness approximation for selecting white as text color if background bri < 127, and black if higher -function rgbBri(a, s) { - var R = a[s][0], G = a[s][1], B = a[s][2]; - return 0.2126*R + 0.7152*G + 0.0722*B; -} +function rgbBri(a) {return 0.2126*parseInt(a.r) + 0.7152*parseInt(a.g) + 0.0722*parseInt(a.b);} // sets background of color slot selectors -function setCSL(s) { - var cd = d.getElementsByClassName('cl')[s]; - var w = whites[s]; - if (hasRGB && !isRgbBlack(colors, s)) { - cd.style.background = rgbStr(colors, s); - cd.style.color = rgbBri(colors, s) > 127 ? "#000":"#fff"; +function setCSL(cs) +{ + let w = cs.dataset.w ? parseInt(cs.dataset.w) : 0; + let hasShadow = getComputedStyle(cs).textShadow !== "none"; + if (hasRGB && !isRgbBlack(cs.dataset)) { + cs.style.backgroundColor = rgbStr(cs.dataset); + if (!hasShadow) cs.style.color = rgbBri(cs.dataset) > 127 ? "#000":"#fff"; // if text has no CSS "shadow" if (hasWhite && w > 0) { - cd.style.background = `linear-gradient(180deg, ${rgbStr(colors, s)} 30%, ${rgbStr([[w,w,w]], 0)})`; + cs.style.background = `linear-gradient(180deg, ${rgbStr(cs.dataset)} 30%, rgb(${w},${w},${w}))`; } } else { if (!hasWhite) w = 0; - cd.style.background = rgbStr([[w,w,w]], 0); - cd.style.color = w > 127 ? "#000":"#fff"; + cs.style.background = `rgb(${w},${w},${w})`; + if (!hasShadow) cs.style.color = w > 127 ? "#000":"#fff"; } } @@ -98,15 +77,16 @@ function applyCfg() cTheme(cfg.theme.base === "light"); var bg = cfg.theme.color.bg; if (bg) sCol('--c-1', bg); - if (lastinfo.leds) updateUI(); // update component visibility var l = cfg.comp.labels; - sCol('--tbp',l ? "14px 14px 10px 14px":"10px 22px 4px 22px"); - sCol('--bbp',l ? "9px 0 7px 0":"10px 0 4px 0"); - sCol('--bhd',l ? "block":"none"); // hides/shows button labels - sCol('--bmt',l ? "0px":"5px"); + sCol('--tbp', l ? "14px 14px 10px 14px":"10px 22px 4px 22px"); + sCol('--bbp', l ? "9px 0 7px 0":"10px 0 4px 0"); + sCol('--bhd', l ? "block":"none"); // show/hide labels + sCol('--bmt', l ? "0px":"5px"); sCol('--t-b', cfg.theme.alpha.tab); + sCol('--sgp', !cfg.comp.segpwr ? "block":"none"); // show/hide segment power size(); localStorage.setItem('wledUiCfg', JSON.stringify(cfg)); + if (lastinfo.leds) updateUI(); // update component visibility } function tglHex() @@ -127,65 +107,76 @@ function tglLabels() applyCfg(); } +function tglRgb() +{ + cfg.comp.colors.rgb = !cfg.comp.colors.rgb; + applyCfg(); +} + function cTheme(light) { if (light) { - sCol('--c-1','#eee'); - sCol('--c-f','#000'); - sCol('--c-2','#ddd'); - sCol('--c-3','#bbb'); - sCol('--c-4','#aaa'); - sCol('--c-5','#999'); - sCol('--c-6','#999'); - sCol('--c-8','#888'); - sCol('--c-b','#444'); - sCol('--c-c','#333'); - sCol('--c-e','#111'); - sCol('--c-d','#222'); - sCol('--c-r','#c42'); - sCol('--c-o','rgba(204, 204, 204, 0.9)'); - sCol('--c-sb','#0003'); sCol('--c-sbh','#0006'); - sCol('--c-tb','rgba(204, 204, 204, var(--t-b))'); - sCol('--c-tba','rgba(170, 170, 170, var(--t-b))'); - sCol('--c-tbh','rgba(204, 204, 204, var(--t-b))'); - d.getElementById('imgw').style.filter = "invert(0.8)"; - } else { // default dark theme - sCol('--c-1','#111'); - sCol('--c-f','#fff'); - sCol('--c-2','#222'); - sCol('--c-3','#333'); - sCol('--c-4','#444'); - sCol('--c-5','#555'); - sCol('--c-6','#666'); - sCol('--c-8','#888'); - sCol('--c-b','#bbb'); - sCol('--c-c','#ccc'); - sCol('--c-e','#eee'); - sCol('--c-d','#ddd'); - sCol('--c-r','#831'); - sCol('--c-o','rgba(34, 34, 34, 0.9)'); - sCol('--c-sb','#fff3'); sCol('--c-sbh','#fff5'); - sCol('--c-tb','rgba(34, 34, 34, var(--t-b))'); - sCol('--c-tba','rgba(102, 102, 102, var(--t-b))'); - sCol('--c-tbh','rgba(51, 51, 51, var(--t-b))'); - d.getElementById('imgw').style.filter = "unset"; - } -} - -function loadBg(iUrl) { - let bg = d.getElementById('bg'); + sCol('--c-1','#eee'); + sCol('--c-f','#000'); + sCol('--c-2','#ddd'); + sCol('--c-3','#bbb'); + sCol('--c-4','#aaa'); + sCol('--c-5','#999'); + sCol('--c-6','#999'); + sCol('--c-8','#888'); + sCol('--c-b','#444'); + sCol('--c-c','#333'); + sCol('--c-e','#111'); + sCol('--c-d','#222'); + sCol('--c-r','#a21'); + sCol('--c-g','#2a1'); + sCol('--c-l','#26c'); + sCol('--c-o','rgba(204, 204, 204, 0.9)'); + sCol('--c-sb','#0003'); sCol('--c-sbh','#0006'); + sCol('--c-tb','rgba(204, 204, 204, var(--t-b))'); + sCol('--c-tba','rgba(170, 170, 170, var(--t-b))'); + sCol('--c-tbh','rgba(204, 204, 204, var(--t-b))'); + gId('imgw').style.filter = "invert(0.8)"; + } else { + sCol('--c-1','#111'); + sCol('--c-f','#fff'); + sCol('--c-2','#222'); + sCol('--c-3','#333'); + sCol('--c-4','#444'); + sCol('--c-5','#555'); + sCol('--c-6','#666'); + sCol('--c-8','#888'); + sCol('--c-b','#bbb'); + sCol('--c-c','#ccc'); + sCol('--c-e','#eee'); + sCol('--c-d','#ddd'); + sCol('--c-r','#e42'); + sCol('--c-g','#4e2'); + sCol('--c-l','#48a'); + sCol('--c-o','rgba(34, 34, 34, 0.9)'); + sCol('--c-sb','#fff3'); sCol('--c-sbh','#fff5'); + sCol('--c-tb','rgba(34, 34, 34, var(--t-b))'); + sCol('--c-tba','rgba(102, 102, 102, var(--t-b))'); + sCol('--c-tbh','rgba(51, 51, 51, var(--t-b))'); + gId('imgw').style.filter = "unset"; + } +} + +function loadBg(iUrl) +{ + let bg = gId('bg'); let img = d.createElement("img"); img.src = iUrl; - if (iUrl == "") { + if (iUrl == "" || iUrl==="https://picsum.photos/1920/1080") { var today = new Date(); - for (var i=0; i=hs && today<=he) img.src = hol[i][4]; + he.setDate(he.getDate() + h[3]); + if (today>=hs && today<=he) img.src = h[4]; } } - img.addEventListener('load', (event) => { + img.addEventListener('load', (e) => { var a = parseFloat(cfg.theme.alpha.bg); if (isNaN(a)) a = 0.6; bg.style.opacity = a; @@ -196,10 +187,10 @@ function loadBg(iUrl) { function loadSkinCSS(cId) { - if (!d.getElementById(cId)) // check if element exists + if (!gId(cId)) // check if element exists { - var h = document.getElementsByTagName('head')[0]; - var l = document.createElement('link'); + var h = d.getElementsByTagName('head')[0]; + var l = d.createElement('link'); l.id = cId; l.rel = 'stylesheet'; l.type = 'text/css'; @@ -209,10 +200,11 @@ function loadSkinCSS(cId) } } -function onLoad() { +function onLoad() +{ if (window.location.protocol == "file:") { - loc = true; - locip = localStorage.getItem('locIp'); + loc = true; + locip = localStorage.getItem('locIp'); if (!locip) { locip = prompt("File Mode. Please enter WLED IP!"); localStorage.setItem('locIp', locip); @@ -228,18 +220,18 @@ function onLoad() { fetch((loc?`http://${locip}`:'.') + "/holidays.json", { // may be loaded from external source method: 'get' }) - .then(res => { + .then((res)=>{ //if (!res.ok) showErrorToast(); return res.json(); }) - .then(json => { + .then((json)=>{ if (Array.isArray(json)) hol = json; //TODO: do some parsing first }) - .catch(function (error) { + .catch((e)=>{ console.log("No array of holidays in holidays.json. Defaults loaded."); }) - .finally(function(){ + .finally(()=>{ loadBg(cfg.theme.bg.url); }); } else @@ -248,20 +240,30 @@ function onLoad() { selectSlot(0); updateTablinks(0); - resetUtil(); - cpick.on("input:end", function() { - setColor(1); - }); - cpick.on("color:change", updatePSliders); pmtLS = localStorage.getItem('wledPmt'); - setTimeout(function(){requestJson(null, false);}, 50); + + // Load initial data + loadPalettes(()=>{ + // fill effect extra data array + loadFXData(()=>{ + // load and populate effects + loadFX(()=>{ + setTimeout(()=>{ // ESP8266 can't handle quick requests + loadPalettesData(()=>{ + requestJson();// will load presets and create WS + }); + },100); + }); + }); + }); + resetUtil(); + d.addEventListener("visibilitychange", handleVisibilityChange, false); size(); - d.getElementById("cv").style.opacity=0; + gId("cv").style.opacity=0; if (localStorage.getItem('pcm') == "true") togglePcMode(true); var sls = d.querySelectorAll('input[type="range"]'); for (var sl of sls) { - sl.addEventListener('input', updateBubble, true); sl.addEventListener('touchstart', toggleBubble); sl.addEventListener('touchend', toggleBubble); } @@ -269,15 +271,14 @@ function onLoad() { function updateTablinks(tabI) { - var tablinks = d.getElementsByClassName("tablinks"); - for (var i of tablinks) { - i.className = i.className.replace(" active", ""); - } + var tablinks = gEBCN("tablinks"); + for (var i of tablinks) i.classList.remove("active"); if (pcMode) return; - tablinks[tabI].className += " active"; + tablinks[tabI].classList.add("active"); } -function openTab(tabI, force = false) { +function openTab(tabI, force = false) +{ if (pcMode && !force) return; iSlide = tabI; _C.classList.toggle('smooth', false); @@ -286,24 +287,34 @@ function openTab(tabI, force = false) { } var timeout; -function showToast(text, error = false) { - if (error) d.getElementById('connind').style.backgroundColor = "#831"; - var x = d.getElementById("toast"); +function showToast(text, error = false) +{ + if (error) gId('connind').style.backgroundColor = "var(--c-r)"; + var x = gId("toast"); + //if (error) text += ''; x.innerHTML = text; - x.className = error ? "error":"show"; + x.classList.add(error ? "error":"show"); clearTimeout(timeout); x.style.animation = 'none'; - x.style.animation = null; - timeout = setTimeout(function(){ x.className = x.className.replace("show", ""); }, 2900); + timeout = setTimeout(()=>{ x.classList.remove("show"); }, 2900); + if (error) console.log(text); } -function showErrorToast() { - // if we received a timeout force WS reconnect - reconnectWS(); +function showErrorToast() +{ showToast('Connection to light failed!', true); } -function clearErrorToast() { - d.getElementById("toast").className = d.getElementById("toast").className.replace("error", ""); + +function clearErrorToast(n=5000) +{ + var x = gId("toast"); + if (x.classList.contains("error")) { + clearTimeout(timeout); + timeout = setTimeout(()=>{ + x.classList.remove("show"); + x.classList.remove("error"); + }, n); + } } function getRuntimeStr(rt) @@ -329,53 +340,54 @@ function inforow(key, val, unit = "") function getLowestUnusedP() { var l = 1; - for (var key in pJson) { - if (key == l) l++; - } + for (var key in pJson) if (key == l) l++; if (l > 250) l = 250; return l; } -function checkUsed(i) { - var id = d.getElementById(`p${i}id`).value; - if (pJson[id] && (i == 0 || id != i)) { - d.getElementById(`p${i}warn`).innerHTML = `⚠ Overwriting ${pName(id)}!`; - } else { - d.getElementById(`p${i}warn`).innerHTML = ""; - } +function checkUsed(i) +{ + var id = gId(`p${i}id`).value; + if (pJson[id] && (i == 0 || id != i)) + gId(`p${i}warn`).innerHTML = `⚠ Overwriting ${pName(id)}!`; + else + gId(`p${i}warn`).innerHTML = id>250?"⚠ ID must be 250 or less.":""; } -function pName(i) { +function pName(i) +{ var n = "Preset " + i; - if (pJson[i].n) n = pJson[i].n; + if (pJson && pJson[i] && pJson[i].n) n = pJson[i].n; return n; } -function isPlaylist(i) { +function isPlaylist(i) +{ return pJson[i].playlist && pJson[i].playlist.ps; } -function papiVal(i) { - if (!pJson[i]) return ""; +function papiVal(i) +{ + if (!pJson || !pJson[i]) return ""; var o = Object.assign({},pJson[i]); if (o.win) return o.win; delete o.n; delete o.p; delete o.ql; return JSON.stringify(o); } -function qlName(i) { - if (!pJson[i]) return ""; - if (!pJson[i].ql) return ""; +function qlName(i) +{ + if (!pJson || !pJson[i] || !pJson[i].ql) return ""; return pJson[i].ql; } -function cpBck() { - var copyText = d.getElementById("bck"); +function cpBck() +{ + var copyText = gId("bck"); copyText.select(); copyText.setSelectionRange(0, 999999); d.execCommand("copy"); - showToast("Copied to clipboard!"); } @@ -385,10 +397,9 @@ function presetError(empty) try { bckstr = localStorage.getItem("wledP"); if (bckstr.length > 10) hasBackup = true; - } catch (e) { + } catch (e) {} - } - var cn = `
`; + var cn = `
`; if (empty) cn += `You have no presets yet!`; else @@ -405,82 +416,142 @@ function presetError(empty) `; } cn += `
`; - d.getElementById('pcont').innerHTML = cn; - if (hasBackup) d.getElementById('bck').value = bckstr; + gId('pcont').innerHTML = cn; + if (hasBackup) gId('bck').value = bckstr; } function loadPresets(callback = null) { - //1st boot (because there is a callback) + // 1st boot (because there is a callback) if (callback && pmt == pmtLS && pmt > 0) { - //we have a copy of the presets in local storage and don't need to fetch another one + // we have a copy of the presets in local storage and don't need to fetch another one populatePresets(true); pmtLast = pmt; callback(); return; } - //afterwards + // afterwards if (!callback && pmt == pmtLast) return; - pmtLast = pmt; - - var url = '/presets.json'; - if (loc) { - url = `http://${locip}/presets.json`; - } + var url = (loc?`http://${locip}`:'') + '/presets.json'; - fetch - (url, { + fetch(url, { method: 'get' }) .then(res => { - if (!res.ok) { - showErrorToast(); - } + if (res.status=="404") return {"0":{}}; + //if (!res.ok) showErrorToast(); return res.json(); }) .then(json => { pJson = json; + pmtLast = pmt; populatePresets(); }) - .catch(function (error) { - showToast(error, true); - console.log(error); + .catch((e)=>{ + //showToast(e, true); presetError(false); }) - .finally(() => { + .finally(()=>{ if (callback) setTimeout(callback,99); }); } -var pQL = []; +function loadPalettes(callback = null) +{ + var url = (loc?`http://${locip}`:'') + '/json/palettes'; + + fetch(url, { + method: 'get' + }) + .then((res)=>{ + if (!res.ok) showErrorToast(); + return res.json(); + }) + .then((json)=>{ + lJson = Object.entries(json); + populatePalettes(); + }) + .catch((e)=>{ + showToast(e, true); + }) + .finally(()=>{ + if (callback) callback(); + updateUI(); + }); +} + +function loadFX(callback = null) +{ + var url = (loc?`http://${locip}`:'') + '/json/effects'; + + fetch(url, { + method: 'get' + }) + .then((res)=>{ + if (!res.ok) showErrorToast(); + return res.json(); + }) + .then((json)=>{ + eJson = Object.entries(json); + populateEffects(); + }) + .catch((e)=>{ + showToast(e, true); + }) + .finally(()=>{ + if (callback) callback(); + updateUI(); + }); +} +function loadFXData(callback = null) +{ + var url = (loc?`http://${locip}`:'') + '/json/fxdata'; + + fetch(url, { + method: 'get' + }) + .then((res)=>{ + if (!res.ok) showErrorToast(); + return res.json(); + }) + .then((json)=>{ + fxdata = json||[]; + // add default value for Solid + fxdata.shift() + fxdata.unshift("@;!;0"); + }) + .catch((e)=>{ + fxdata = []; + showToast(e, true); + }) + .finally(()=>{ + if (callback) callback(); + updateUI(); + }); +} + +var pQL = []; function populateQL() { var cn = ""; if (pQL.length > 0) { - cn += `

Quick load

`; - - var it = 0; + pQL.sort((a,b) => (a[0]>b[0])); + cn += `

Quick load

`; for (var key of (pQL||[])) { - cn += ``; - it++; - if (it > 4) { - it = 0; - cn += '
'; - } + cn += ``; } - if (it != 0) cn+= '
'; - - cn += `

All presets

`; - } - d.getElementById('pql').innerHTML = cn; + gId('pql').classList.add("expanded"); + } else gId('pql').classList.remove("expanded"); + gId('pql').innerHTML = cn; } function populatePresets(fromls) { if (fromls) pJson = JSON.parse(localStorage.getItem("wledP")); + if (!pJson) {setTimeout(loadPresets,250); return;} delete pJson["0"]; var cn = ""; var arr = Object.entries(pJson); @@ -488,25 +559,25 @@ function populatePresets(fromls) pQL = []; var is = []; pNum = 0; - for (var key of (arr||[])) { - if (!isObject(key[1])) continue; + if (!isObj(key[1])) continue; let i = parseInt(key[0]); var qll = key[1].ql; - if (qll) pQL.push([i, qll]); + if (qll) pQL.push([i, qll, pName(i)]); is.push(i); - cn += `
`; + cn += `
`; if (cfg.comp.pid) cn += `
${i}
`; - cn += `
${isPlaylist(i)?"":""}${pName(i)}
- -
-

`; - pNum++; + cn += `
${isPlaylist(i)?"":""}${pName(i)} +
+ +
+
`; + pNum++; } - d.getElementById('pcont').innerHTML = cn; + gId('pcont').innerHTML = cn; if (pNum > 0) { if (pmtLS != pmt && pmt != 0) { localStorage.setItem("wledPmt", pmt); @@ -514,16 +585,55 @@ function populatePresets(fromls) localStorage.setItem("wledP", JSON.stringify(pJson)); } pmtLS = pmt; - for (var a = 0; a < is.length; a++) { - let i = is[a]; - if (expanded[i+100]) expand(i+100, true); - } - //makePlSel(arr); } else { presetError(true); } updatePA(); populateQL(); } +function parseInfo(i) { + lastinfo = i; + var name = i.name; + gId('namelabel').innerHTML = name; + //if (name === "Dinnerbone") d.documentElement.style.transform = "rotate(180deg)"; + if (i.live) name = "(Live) " + name; + if (loc) name = "(L) " + name; + d.title = name; + ledCount = i.leds.count; + syncTglRecv = i.str; + maxSeg = i.leds.maxseg; + pmt = i.fs.pmt; + // do we have a matrix set-up + mw = i.leds.matrix ? i.leds.matrix.w : 0; + mh = i.leds.matrix ? i.leds.matrix.h : 0; + isM = mw>0 && mh>0; + if (!isM) { + gId("filter1D").classList.add("hide"); + //gId("filter2D").classList.add("hide"); + hideModes("2D"); + } + if (i.noaudio) { + gId("filterVol").classList.add("hide"); + gId("filterFreq").classList.add("hide"); + } +// if (!i.u || !i.u.AudioReactive) { + //gId("filterVol").classList.add("hide"); hideModes(" ♪"); // hide volume reactive effects + //gId("filterFreq").classList.add("hide"); hideModes(" ♫"); // hide frequency reactive effects +// } +} + +//https://stackoverflow.com/questions/2592092/executing-script-elements-inserted-with-innerhtml +//var setInnerHTML = function(elm, html) { +// elm.innerHTML = html; +// Array.from(elm.querySelectorAll("script")).forEach( oldScript => { +// const newScript = document.createElement("script"); +// Array.from(oldScript.attributes) +// .forEach( attr => newScript.setAttribute(attr.name, attr.value) ); +// newScript.appendChild(document.createTextNode(oldScript.innerHTML)); +// oldScript.parentNode.replaceChild(newScript, oldScript); +// }); +//} +//setInnerHTML(obj, html); + function populateInfo(i) { var cn=""; @@ -533,34 +643,39 @@ function populateInfo(i) var pwru = "Not calculated"; if (pwr > 1000) {pwr /= 1000; pwr = pwr.toFixed((pwr > 10) ? 0 : 1); pwru = pwr + " A";} else if (pwr > 0) {pwr = 50 * Math.round(pwr/50); pwru = pwr + " mA";} - var urows=""; + var urows=""; if (i.u) { for (const [k, val] of Object.entries(i.u)) { - if (val[1]) { + if (val[1]) urows += inforow(k,val[0],val[1]); - } else { + else urows += inforow(k,val); - } } } - var vcn = "Kuuhaku"; if (i.ver.startsWith("0.13.")) vcn = "Toki"; + if (i.ver.includes("-bl")) vcn = "Ryujin"; if (i.cn) vcn = i.cn; - cn += `v${i.ver} "${vcn}"

- ${urows} - ${inforow("Build",i.vid)} - ${inforow("Signal strength",i.wifi.signal +"% ("+ i.wifi.rssi, " dBm)")} - ${inforow("Uptime",getRuntimeStr(i.uptime))} - ${inforow("Free heap",heap," kB")} - ${inforow("Estimated current",pwru)} - ${inforow("Frames / second",i.leds.fps)} - ${inforow("MAC address",i.mac)} - ${inforow("Filesystem",i.fs.u + "/" + i.fs.t + " kB (" +Math.round(i.fs.u*100/i.fs.t) + "%)")} - ${inforow("Environment",i.arch + " " + i.core + " (" + i.lwip + ")")} -
`; - d.getElementById('kv').innerHTML = cn; + cn += `v${i.ver} "${vcn}"

+${urows} +${inforow("Build",i.vid)} +${inforow("Signal strength",i.wifi.signal +"% ("+ i.wifi.rssi, " dBm)")} +${inforow("Uptime",getRuntimeStr(i.uptime))} +${inforow("Free heap",heap," kB")} +${i.psram?inforow("Free PSRAM",(i.psram/1024).toFixed(1)," kB"):""} +${inforow("Estimated current",pwru)} +${inforow("Average FPS",i.leds.fps)} +${inforow("MAC address",i.mac)} +${inforow("Filesystem",i.fs.u + "/" + i.fs.t + " kB (" +Math.round(i.fs.u*100/i.fs.t) + "%)")} +${inforow("Environment",i.arch + " " + i.core + " (" + i.lwip + ")")} +
`; + gId('kv').innerHTML = cn; + // update all sliders in Info + for (let sd of (gId('kv').getElementsByClassName('sliderdisplay')||[])) { + let s = sd.previousElementSibling; + if (s) updateTrail(s); + } } function populateSegments(s) @@ -569,83 +684,109 @@ function populateSegments(s) let li = lastinfo; segCount = 0; lowestUnused = 0; lSeg = 0; - for (var y = 0; y < (s.seg||[]).length; y++) - { + for (var inst of (s.seg||[])) { segCount++; - var inst=s.seg[y]; let i = parseInt(inst.id); - powered[i] = inst.on; if (i == lowestUnused) lowestUnused = i+1; if (i > lSeg) lSeg = i; - cn += `
- - &#x${li.live && li.liveseg==i?'e410':'e325'}; -
-
${inst.n ? inst.n : "Segment "+i}
- -
- -
- -
- -
- -
-
-
- - - - - - - - - - - -
Start LED${cfg.comp.seglen?"Length":"Stop LED"}Offset
- - - - - - - - - - - -
GroupingSpacingApply
-
- - -
- - -
-
-

`; - } - - d.getElementById('segcont').innerHTML = cn; + let sg = gId(`seg${i}`); + let exp = sg ? (sg.classList.contains("expanded") || (i===0 && cfg.comp.segexp)) : false; + + let segp = `
+ +
+ +
+
+
`; + let rvXck = ``; + let miXck = ``; + let rvYck = "", miYck =""; + if (isM) { + rvYck = ``; + miYck = ``; + } + let map2D = `
Expand 1D FX
+
+
`; + let sndSim = `
Sound sim
+
+
`; + cn += `
+ + &#x${inst.frz ? (li.live && li.liveseg==i?'e410':'e0e8') : 'e325'}; +
+ ${inst.n ? inst.n : "Segment "+i} + +
+ + ${cfg.comp.segpwr?segp:''} +
+ + + + + + + + + + + + + ${isM ? ''+ + ''+ + ''+ + ''+ + ''+ + '':''} + + + + + + + + + + +
${isM?'Start X':'Start LED'}${isM?(cfg.comp.seglen?"Width":"Stop X"):(cfg.comp.seglen?"LED count":"Stop LED")}${isM?'':'Offset'}
${isM?miXck+'
'+rvXck:''}
Start Y'+(cfg.comp.seglen?'Height':'Stop Y')+'
'+miYck+'
'+rvYck+'
GroupingSpacing
+
+ ${!isM?rvXck:''} + ${isM?map2D:''} + ${s.AudioReactive && s.AudioReactive.on ? "" : sndSim} + +
+ + +
+
+ ${cfg.comp.segpwr?'':segp} +
`; + } + + gId('segcont').innerHTML = cn; if (lowestUnused >= maxSeg) { - d.getElementById('segutil').innerHTML = 'Maximum number of segments reached.'; + gId('segutil').innerHTML = 'Maximum number of segments reached.'; noNewSegs = true; } else if (noNewSegs) { resetUtil(); @@ -653,100 +794,98 @@ function populateSegments(s) } for (var i = 0; i <= lSeg; i++) { updateLen(i); - updateTrail(d.getElementById(`seg${i}bri`)); - let segr = d.getElementById(`segr${i}`); - if (segr) segr.style.display = "none"; + updateTrail(gId(`seg${i}bri`)); + gId(`segr${i}`).style.display = "none"; + if (!gId(`seg${i}sel`).checked) gId(`selall`).checked = false; } - if (segCount < 2) d.getElementById(`segd${lSeg}`).style.display = "none"; - if (!noNewSegs && (cfg.comp.seglen?parseInt(d.getElementById(`seg${lSeg}s`).value):0)+parseInt(d.getElementById(`seg${lSeg}e`).value) 1) ? "inline":"none"; + if (segCount < 2) gId(`segd${lSeg}`).style.display = "none"; + if (!isM && !noNewSegs && (cfg.comp.seglen?parseInt(gId(`seg${lSeg}s`).value):0)+parseInt(gId(`seg${lSeg}e`).value) 1) ? "block":"none"; // rsbtn parent } -function populateEffects(effects) +function populateEffects() { - var html = ``; + var effects = eJson; + var html = ""; - effects.shift(); //remove solid + effects.shift(); // temporary remove solid for (let i = 0; i < effects.length; i++) { - effects[i] = {id: parseInt(i)+1, name:effects[i]}; + effects[i] = { + id: effects[i][0], + name:effects[i][1] + }; } - effects.sort(compare); - + effects.sort((a,b) => (a.name).localeCompare(b.name)); effects.unshift({ "id": 0, - "name": "Solid", - "class": "sticky" + "name": "Solid" }); - for (let i = 0; i < effects.length; i++) { - html += generateListItemHtml( - 'fx', - effects[i].id, - effects[i].name, - 'setX', - '', - effects[i].class, - ); + for (let ef of effects) { + // WLEDSR: add slider and color control to setX (used by requestjson) + let id = ef.id; + let nm = ef.name+" "; + let fd = ""; + if (ef.name.indexOf("RSVD") < 0) { + if (Array.isArray(fxdata) && fxdata.length>id) { + if (fxdata[id].length==0) fd = ";;!;1d" + else fd = fxdata[id].substr(1); + let eP = (fd == '')?[]:fd.split(";"); // effect parameters + let p = (eP.length<3 || eP[2]==='')?[]:eP[2].split(","); // palette data + if (p.length>0 && (p[0] !== "" && !isNumeric(p[0]))) nm += "🎨"; // effects using palette + let m = (eP.length<4 || eP[3]==='')?[]:eP[3].split(","); // metadata + if (m.length>0) for (let r of m) { + if (r.substring(0,2)=="1d") nm += "⋮"; // 1D effects + if (r.substring(0,2)=="2d") nm += "▦"; // 2D effects + if (r.substring(0,2)=="vo") nm += "♪"; // volume effects + if (r.substring(0,2)=="fr") nm += "♫"; // frequency effects + } + } + html += generateListItemHtml('fx',id,nm,'setX','',fd); + } } - fxlist.innerHTML=html; + gId('fxlist').innerHTML=html; } -function populatePalettes(palettes) +function populatePalettes() { - palettes.shift(); //remove default - for (let i = 0; i < palettes.length; i++) { - palettes[i] = { - "id": parseInt(i)+1, - "name": palettes[i] - }; - } - palettes.sort(compare); + lJson.shift(); // temporary remove default + lJson.sort((a,b) => (a[1]).localeCompare(b[1])); + lJson.unshift([0,"Default"]); - palettes.unshift({ - "id": 0, - "name": "Default", - "class": "sticky" - }); - - var html = ``; - for (let i = 0; i < palettes.length; i++) { + var html = ""; + for (let pa of lJson) { html += generateListItemHtml( 'palette', - palettes[i].id, - palettes[i].name, - 'setPalette', - `
`, - palettes[i].class, - ); + pa[0], + pa[1], + 'setPalette', + `
` + ); } - pallist.innerHTML=html; + + gId('pallist').innerHTML=html; } function redrawPalPrev() { let palettes = d.querySelectorAll('#pallist .lstI'); - for (let i = 0; i < palettes.length; i++) { - let id = palettes[i].dataset.id; - let lstPrev = palettes[i].querySelector('.lstIprev'); - if (lstPrev) { - lstPrev.style = genPalPrevCss(id); + for (var pal of (palettes||[])) { + let lP = pal.querySelector('.lstIprev'); + if (lP) { + lP.style = genPalPrevCss(pal.dataset.id); } } } function genPalPrevCss(id) { - if (!palettesData) { - return; - } + if (!palettesData) return; + var paletteData = palettesData[id]; - if (!paletteData) { - return 'display: none'; - } + if (!paletteData) return 'display: none'; // We need at least two colors for a gradient if (paletteData.length == 1) { @@ -758,60 +897,62 @@ function genPalPrevCss(id) var gradient = []; for (let j = 0; j < paletteData.length; j++) { - const element = paletteData[j]; - let r; - let g; - let b; + const e = paletteData[j]; + let r, g, b; let index = false; - if (Array.isArray(element)) { - index = element[0]/255*100; - r = element[1]; - g = element[2]; - b = element[3]; - } else if (element == 'r') { + if (Array.isArray(e)) { + index = Math.round(e[0]/255*100); + r = e[1]; + g = e[2]; + b = e[3]; + } else if (e == 'r') { r = Math.random() * 255; g = Math.random() * 255; b = Math.random() * 255; } else { - if (colors) { - let pos = element[1] - 1; - r = colors[pos][0]; - g = colors[pos][1]; - b = colors[pos][2]; - } + let i = e[1] - 1; + var cd = gId('csl').children; + r = parseInt(cd[i].dataset.r); + g = parseInt(cd[i].dataset.g); + b = parseInt(cd[i].dataset.b); } if (index === false) { - index = j / paletteData.length * 100; + index = Math.round(j / paletteData.length * 100); } - + gradient.push(`rgb(${r},${g},${b}) ${index}%`); } return `background: linear-gradient(to right,${gradient.join()});`; } -function generateListItemHtml(listName, id, name, clickAction, extraHtml = '', extraClass = '') +function generateListItemHtml(listName, id, name, clickAction, extraHtml = '', effectPar = '') { - return `
-
` + ); + } } - s = json.state; - displayRover(info, s); + parseInfo(i); + if (isInfo) populateInfo(i); } - - readState(s,command); + var s = json.state ? json.state : json; + readState(s); + + //load presets and open websocket sequentially + if (!pJson || isEmpty(pJson)) setTimeout(()=>{ + loadPresets(()=>{ + if (!(ws && ws.readyState === WebSocket.OPEN)) makeWS(); + }); + },25); + reqsLegal = true; }) - .catch(function (error) { - showToast(error, true); - console.log(error); + .catch((e)=>{ + showToast(e, true); }); } -function togglePower() { +function togglePower() +{ isOn = !isOn; var obj = {"on": isOn}; + if (isOn && lastinfo && lastinfo.live && lastinfo.liveseg>=0) { + obj.live = false; + obj.seg = []; + obj.seg[0] = {"id": lastinfo.liveseg, "frz": false}; + } requestJson(obj); } -function toggleNl() { +function toggleNl() +{ nlA = !nlA; if (nlA) { @@ -1269,115 +1559,145 @@ function toggleNl() { requestJson(obj); } -function toggleSync() { +function toggleSync() +{ syncSend = !syncSend; - if (syncSend) - { - showToast('Other lights in the network will now sync to this one.'); - } else { - showToast('This light and other lights in the network will no longer sync.'); - } + if (syncSend) showToast('Other lights in the network will now sync to this one.'); + else showToast('This light and other lights in the network will no longer sync.'); var obj = {"udpn": {"send": syncSend}}; if (syncTglRecv) obj.udpn.recv = syncSend; requestJson(obj); } -function toggleLiveview() { - isLv = !isLv; - d.getElementById('liveview').style.display = (isLv) ? "block":"none"; - var url = loc ? `http://${locip}/liveview`:"/liveview"; - d.getElementById('liveview').src = (isLv) ? url:"about:blank"; - d.getElementById('buttonSr').className = (isLv) ? "active":""; - if (!isLv && ws && ws.readyState === WebSocket.OPEN) ws.send('{"lv":false}'); - size(); +function toggleLiveview() +{ + //WLEDSR adding liveview2D support + if (isInfo && isM) toggleInfo(); + if (isNodes && isM) toggleNodes(); + isLv = !isLv; + + var lvID = "liveview"; + if (isM) { + lvID = "liveview2D" + if (isLv) { + var cn = ''; + d.getElementById('kliveview2D').innerHTML = cn; + } + + gId('mliveview2D').style.transform = (isLv) ? "translateY(0px)":"translateY(100%)"; + } + + gId(lvID).style.display = (isLv) ? "block":"none"; + var url = (loc?`http://${locip}`:'') + "/" + lvID; + gId(lvID).src = (isLv) ? url:"about:blank"; + gId('buttonSr').className = (isLv) ? "active":""; + if (!isLv && ws && ws.readyState === WebSocket.OPEN) ws.send('{"lv":false}'); + size(); } -function toggleInfo() { +function toggleInfo() +{ if (isNodes) toggleNodes(); + if (isLv && isM) toggleLiveview(); isInfo = !isInfo; - if (isInfo) populateInfo(lastinfo); - d.getElementById('info').style.transform = (isInfo) ? "translateY(0px)":"translateY(100%)"; - d.getElementById('buttonI').className = (isInfo) ? "active":""; + if (isInfo) requestJson(); + gId('info').style.transform = (isInfo) ? "translateY(0px)":"translateY(100%)"; + gId('buttonI').className = (isInfo) ? "active":""; } -function toggleNodes() { +function toggleNodes() +{ if (isInfo) toggleInfo(); + if (isLv && isM) toggleLiveview(); isNodes = !isNodes; - d.getElementById('nodes').style.transform = (isNodes) ? "translateY(0px)":"translateY(100%)"; - d.getElementById('buttonNodes').className = (isNodes) ? "active":""; if (isNodes) loadNodes(); + gId('nodes').style.transform = (isNodes) ? "translateY(0px)":"translateY(100%)"; + gId('buttonNodes').className = (isNodes) ? "active":""; } -function makeSeg() { +function makeSeg() +{ var ns = 0; - if (lowestUnused > 0) { - var pend = parseInt(d.getElementById(`seg${lowestUnused -1}e`).value,10) + (cfg.comp.seglen?parseInt(d.getElementById(`seg${lowestUnused -1}s`).value,10):0); + var lu = lowestUnused; + let li = lastinfo; + if (lu > 0) { + var pend = parseInt(gId(`seg${lu -1}e`).value,10) + (cfg.comp.seglen?parseInt(gId(`seg${lu -1}s`).value,10):0); if (pend < ledCount) ns = pend; } - var cn = `
-
- New segment ${lowestUnused} - -
-
-
- - + gId('segutil').scrollIntoView({ + behavior: 'smooth', + block: 'start', + }); + var ct = (isM?mw:ledCount)-(cfg.comp.seglen?ns:0); + //TODO: add calculation for Y in case of 2D matrix + var cn = `
+
+ +
- - - + + - - - + + + + ${isM ? ''+ + ''+ + ''+ + ''+ + '':''}
Start LED${cfg.comp.seglen?"Length":"Stop LED"}Apply${isM?'Start X':'Start LED'}${isM?(cfg.comp.seglen?"Width":"Stop X"):(cfg.comp.seglen?"LED count":"Stop LED")}
Start Y'+(cfg.comp.seglen?'Height':'Stop Y')+'
-
${ledCount - ns} LED${ledCount - ns >1 ? "s":""}
+
${ledCount - ns} LEDs
+
`; - d.getElementById('segutil').innerHTML = cn; + gId('segutil').innerHTML = cn; } -function resetUtil() { - var cn = `
`; - d.getElementById('segutil').innerHTML = cn; +function resetUtil() +{ +// gId('segutil').innerHTML = ''; + gId('segutil').innerHTML = '
' + + '' + + '
segment
'; } var plJson = {"0":{ - "ps": [0], - "dur": [100], - "transition": [-1], //to be inited to default transition dur + "ps": [0], + "dur": [100], + "transition": [-1], // to be inited to default transition dur "repeat": 0, "r": false, - "end": 0 + "end": 0 }}; -function makePlSel(incPl=false) { +function makePlSel(el, incPl=false) { var plSelContent = ""; delete pJson["0"]; // remove filler preset var arr = Object.entries(pJson); - for (var i = 0; i < arr.length; i++) { - var n = arr[i][1].n ? arr[i][1].n : "Preset " + arr[i][0]; - if (!incPl && arr[i][1].playlist && arr[i][1].playlist.ps) continue; //remove playlists, sub-playlists not yet supported - plSelContent += `` + for (var a of arr) { + var n = a[1].n ? a[1].n : "Preset " + a[0]; + if (!incPl && a[1].playlist && a[1].playlist.ps) continue; // remove playlists, sub-playlists not yet supported + plSelContent += `` } return plSelContent; } function refreshPlE(p) { - var plEDiv = d.getElementById(`ple${p}`); + var plEDiv = gId(`ple${p}`); if (!plEDiv) return; - var content = ""; + var content = "
Playlist entries
"; for (var i = 0; i < plJson[p].ps.length; i++) { content += makePlEntry(p,i); } + content += `
`; plEDiv.innerHTML = content; var dels = plEDiv.getElementsByClassName("btn-pl-del"); if (dels.length < 2) dels[0].style.display = "none"; - var sels = d.getElementById(`seg${p+100}`).getElementsByClassName("sel"); + var sels = gId(`seg${p+100}`).getElementsByClassName("sel"); for (var i of sels) { if (i.dataset.val) { if (parseInt(i.dataset.val) > 0) i.value = i.dataset.val; @@ -1386,7 +1706,7 @@ function refreshPlE(p) { } } -//p: preset ID, i: ps index +// p: preset ID, i: ps index function addPl(p,i) { plJson[p].ps.splice(i+1,0,0); plJson[p].dur.splice(i+1,0,plJson[p].dur[i]); @@ -1418,15 +1738,15 @@ function pleTr(p,i,field) { function plR(p) { var pl = plJson[p]; - pl.r = d.getElementById(`pl${p}rtgl`).checked; - if (d.getElementById(`pl${p}rptgl`).checked) { //infinite + pl.r = gId(`pl${p}rtgl`).checked; + if (gId(`pl${p}rptgl`).checked) { // infinite pl.repeat = 0; delete pl.end; - d.getElementById(`pl${p}o1`).style.display = "none"; + gId(`pl${p}o1`).style.display = "none"; } else { - pl.repeat = parseInt(d.getElementById(`pl${p}rp`).value); - pl.end = parseInt(d.getElementById(`pl${p}selEnd`).value); - d.getElementById(`pl${p}o1`).style.display = "block"; + pl.repeat = parseInt(gId(`pl${p}rp`).value); + pl.end = parseInt(gId(`pl${p}selEnd`).value); + gId(`pl${p}o1`).style.display = "block"; } } @@ -1434,154 +1754,204 @@ function makeP(i,pl) { var content = ""; if (pl) { var rep = plJson[i].repeat ? plJson[i].repeat : 0; - content = `
Playlist Entries
-
-