diff --git a/.vscode/extensions.json b/.vscode/extensions.json index 080e70d0..0f0d7401 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 183aba9e..0e231caa 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "wled", - "version": "0.13.2-bl0", + "version": "0.14.0-bl0", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index fb09e820..139ef1a1 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "wled", - "version": "0.13.2-bl0", + "version": "0.14.0-bl0", "description": "Tools for WLED project", "main": "tools/cdata.js", "directories": { diff --git a/platformio.ini b/platformio.ini index 73f56a9e..78b5d8e5 100644 --- a/platformio.ini +++ b/platformio.ini @@ -162,7 +162,7 @@ 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 diff --git a/tools/cdata.js b/tools/cdata.js index af879672..d01c3e35 100644 --- a/tools/cdata.js +++ b/tools/cdata.js @@ -263,108 +263,60 @@ writeChunks( name: "PAGE_settings", method: "gzip", filter: "html-minify", - mangle: (str) => - str - .replace( - /function GetV().*\<\/script\>/gms, - "" - ) }, { file: "settings_wifi.htm", name: "PAGE_settings_wifi", method: "gzip", filter: "html-minify", - mangle: (str) => - str - .replace( - /function GetV().*\<\/script\>/gms, - "" - ) }, { file: "settings_leds.htm", name: "PAGE_settings_leds", method: "gzip", filter: "html-minify", - mangle: (str) => - str - .replace( - /function GetV().*\<\/script\>/gms, - "" - ) }, { file: "settings_dmx.htm", name: "PAGE_settings_dmx", method: "gzip", filter: "html-minify", - mangle: (str) => - str - .replace( - /function GetV().*\<\/script\>/gms, - "" - ) }, { file: "settings_ui.htm", name: "PAGE_settings_ui", method: "gzip", filter: "html-minify", - mangle: (str) => - str - .replace( - /function GetV().*\<\/script\>/gms, - "" - ) }, { file: "settings_sync.htm", name: "PAGE_settings_sync", method: "gzip", filter: "html-minify", - mangle: (str) => - str - .replace( - /function GetV().*\<\/script\>/gms, - "" - ) }, { file: "settings_time.htm", name: "PAGE_settings_time", method: "gzip", filter: "html-minify", - mangle: (str) => - str - .replace( - /function GetV().*\<\/script\>/gms, - "" - ) }, { file: "settings_sec.htm", name: "PAGE_settings_sec", method: "gzip", filter: "html-minify", - mangle: (str) => - str - .replace( - /function GetV().*\<\/script\>/gms, - "" - ) }, { file: "settings_um.htm", name: "PAGE_settings_um", method: "gzip", filter: "html-minify", - mangle: (str) => - str - .replace( - /function GetV().*\<\/script\>/gms, - "" - ) + }, + { + file: "settings_2D.htm", + name: "PAGE_settings_2D", + method: "gzip", + filter: "html-minify", }, { file: "settings_pin.htm", @@ -441,6 +393,12 @@ const char PAGE_dmxmap[] PROGMEM = R"=====()====="; method: "gzip", filter: "html-minify", }, + { + file: "liveviewws2D.htm", + name: "PAGE_liveviewws2D", + method: "gzip", + filter: "html-minify", + }, { file: "404.htm", name: "PAGE_404", diff --git a/usermods/Animated_Staircase/Animated_Staircase.h b/usermods/Animated_Staircase/Animated_Staircase.h index eed40b7c..3cf349ca 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. */ @@ -458,15 +465,11 @@ class Animated_Staircase : public Usermod { useUSSensorTop = top[FPSTR(_useTopUltrasoundSensor)] | useUSSensorTop; topPIRorTriggerPin = top[FPSTR(_topPIRorTrigger_pin)] | topPIRorTriggerPin; -// topPIRorTriggerPin = min(33,max(-1,(int)topPIRorTriggerPin)); // bounds check topEchoPin = top[FPSTR(_topEcho_pin)] | topEchoPin; -// topEchoPin = min(39,max(-1,(int)topEchoPin)); // bounds check useUSSensorBottom = top[FPSTR(_useBottomUltrasoundSensor)] | useUSSensorBottom; bottomPIRorTriggerPin = top[FPSTR(_bottomPIRorTrigger_pin)] | bottomPIRorTriggerPin; -// bottomPIRorTriggerPin = min(33,max(-1,(int)bottomPIRorTriggerPin)); // bounds check bottomEchoPin = top[FPSTR(_bottomEcho_pin)] | bottomEchoPin; -// bottomEchoPin = min(39,max(-1,(int)bottomEchoPin)); // bounds check topMaxDist = top[FPSTR(_topEchoCm)] | topMaxDist; topMaxDist = min(150,max(30,(int)topMaxDist)); // max distnace ~1.5m (a lag of 9ms may be expected) @@ -504,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 742066f7..6b42fc80 100644 --- a/usermods/BME280_v2/usermod_bme280.h +++ b/usermods/BME280_v2/usermod_bme280.h @@ -26,15 +26,10 @@ private: 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 @@ private: 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 @@ public: 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 5e4255f4..5702d8fa 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 0d52d46e..e614704f 100644 --- a/usermods/EleksTube_IPS/TFTs.h +++ b/usermods/EleksTube_IPS/TFTs.h @@ -355,7 +355,7 @@ public: // 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 06c6ecc8..0f7d92e7 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/PIR_sensor_switch/usermod_PIR_sensor_switch.h b/usermods/PIR_sensor_switch/usermod_PIR_sensor_switch.h index 0b2ad3d7..d7abb847 100644 --- a/usermods/PIR_sensor_switch/usermod_PIR_sensor_switch.h +++ b/usermods/PIR_sensor_switch/usermod_PIR_sensor_switch.h @@ -143,15 +143,15 @@ private: } } 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) { - if (prevPreset<255) applyPreset(prevPreset, NotifyUpdateMode); - else applyTemporaryPreset(); + 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; } @@ -275,20 +275,9 @@ public: 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) { @@ -322,6 +311,20 @@ public: 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/usermod_PWM_fan.h b/usermods/PWM_fan/usermod_PWM_fan.h index ba4e1726..6dfe12fa 100644 --- a/usermods/PWM_fan/usermod_PWM_fan.h +++ b/usermods/PWM_fan/usermod_PWM_fan.h @@ -51,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[]; @@ -83,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 @@ -93,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); } @@ -217,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")); + } + } } /* diff --git a/usermods/RTC/usermod_rtc.h b/usermods/RTC/usermod_rtc.h index 8c174e6f..fd9a4054 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/VL53L0X_gestures/usermod_vl53l0x_gestures.h b/usermods/VL53L0X_gestures/usermod_vl53l0x_gestures.h index 210ec3f5..91766b9b 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/audioreactive/audio_reactive.h b/usermods/audioreactive/audio_reactive.h new file mode 100644 index 00000000..c7c7e5f8 --- /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 00000000..090b46e6 --- /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 00000000..f14c168d --- /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 4aa2a128..4ce51c43 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 b38c8a25..75258608 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 08d551be..238ec7d9 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 eeece443..c1264ebf 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_four_line_display/usermod_v2_four_line_display.h b/usermods/usermod_v2_four_line_display/usermod_v2_four_line_display.h index a49961dd..3fcf6612 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 05352613..38a055a5 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. @@ -951,8 +1030,8 @@ class FourLineDisplayUsermod : public Usermod { 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');")); - oappend(SET_F("addInfo('4LineDisplay:pin[]',1,'I2C/SPI DTA');")); + 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');")); @@ -981,7 +1060,9 @@ class FourLineDisplayUsermod : public Usermod { 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; @@ -1001,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()) { @@ -1012,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; @@ -1030,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 { @@ -1085,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_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 35945561..18435ca3 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 @@ private: #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; @@ -185,7 +185,8 @@ private: * 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); @@ -211,8 +212,8 @@ private: * 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. @@ -249,7 +250,8 @@ private: /** * 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; @@ -285,7 +287,7 @@ public: 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); @@ -365,19 +367,43 @@ public: 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; @@ -390,29 +416,35 @@ public: 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; } } } @@ -496,13 +528,13 @@ public: 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()) { @@ -581,13 +658,13 @@ public: effectPalette = palettes_alpha_indexes[effectPaletteIndex]; stateChanged = true; if (applyToAll) { - for (byte i=0; ioverlay(lineBuffer, 500, 7); // use brush + #endif } void changeSat(bool increase){ @@ -634,16 +716,21 @@ public: 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) { @@ -656,6 +743,12 @@ public: 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="); @@ -663,7 +756,12 @@ public: 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 } } @@ -678,16 +776,21 @@ public: #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 628b5262..67c5b1e4 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 273541a0..dd693bb1 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,79 +400,114 @@ 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; +} /* * 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); + 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(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; + 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 + 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 - 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++) { + 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 - 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 11 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; + for (int k=0; k < cols; k++) { + for (size_t j=0; j < numDrops; j++) { + uint16_t idx = k*numDrops + j; + + 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) } - } - 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 + + 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 - 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 + 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; } + } + 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 - if (drops[j].colIndex > 2) { // during bounce, some water is on the floor - setPixelColor(0,color_blend(SEGCOLOR(0),BLACK,drops[j].col)); - } - } 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 { + 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) { - uint32_t nowOld = now; + 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 = 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 1cfa366b..486a07e1 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,852 +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 + + #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: + 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; + 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> 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; - // segment parameters public: - typedef struct Segment { // 34 (35 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 - uint8_t _capabilities; - uint8_t c1x, c2x, c3x; // custom FX parameters - 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; - } - - /** - * If reset of this segment was request, 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 resetIfRequired() { - if (_requiresReset) { - next_time = 0; step = 0; call = 0; aux0 = 0; aux1 = 0; - deallocateData(); - _requiresReset = false; - } - } - - /** - * 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() { _requiresReset = true; } - private: - uint16_t _dataLen = 0; - bool _requiresReset = false; - } segment_runtime; - - typedef struct ColorTransition { // 12 bytes - uint32_t colorOld = 0; - uint32_t transitionStart; - uint16_t transitionDur; - uint8_t segment = 0xFF; //lower 6 bits: the segment this transition is for (255 indicates transition not in use/available) upper 2 bits: color channel - uint8_t briOld = 0; - static void startTransition(uint8_t oldBri, uint32_t oldCol, uint16_t dur, uint8_t segn, uint8_t slot) { - if (segn >= 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; - - 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, - 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); + setPixelSegment(uint8_t n); - 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 - } - - 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 +#ifndef WLED_DISABLE_2D + #define WLED_MAX_PANELS 64 + uint8_t + hPanels, + vPanels; - show_callback _callback = nullptr; - - // 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); + panelH, + panelW, + matrixWidth, + matrixHeight; - 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); + 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); + setUpMatrix(), + setPixelColorXY(int x, int y, uint32_t c); - uint16_t* customMappingTable = nullptr; - uint16_t customMappingSize = 0; - - uint32_t _lastPaletteChange = 0; - uint32_t _lastShow = 0; + // 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 _colors_t[3]; - uint8_t _bri_t; - bool _no_rgb = false; + uint32_t + getPixelColorXY(uint16_t, uint16_t); + + // 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; + }; + + uint8_t _modeCount; + std::vector _mode; // SRAM footprint: 4 bytes per element + std::vector _modeData; // mode (effect) name and its slider control data array + + show_callback _callback; + + uint16_t* customMappingTable; + uint16_t customMappingSize; - uint8_t _segment_index = 0; - uint8_t _segment_index_palette_last = 99; + uint32_t _lastShow; + + uint8_t _segment_index; uint8_t _mainSegment; - 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} - }; - 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; - - uint16_t - transitionProgress(uint8_t tNr); - - public: - inline bool hasWhiteChannel(void) {return _hasWhiteChannel;} - inline bool isOffRefreshRequired(void) {return _isOffRefreshRequired;} + void + estimateCurrentAndLimitBri(void); }; extern const char JSON_mode_names[]; extern const char JSON_palette_names[]; -// the following has been moved to FX_fcn.cpp instead -/* -// WLEDSR: extensions -// Technical notes -// =============== -// If an effect name is followed by an @, slider and color control is effective. -// See setSliderAndColorControl in index.js for implementation -// If not effective then: -// - For AC effects (id<128) 2 sliders and 3 colors and the palette will be shown -// - For SR effects (id>128) 5 sliders and 3 colors and the palette will be shown -// If effective (@) -// - a ; seperates slider controls (left) from color controls (middle) and palette control (right) -// - if left, middle or right is empty no controls are shown -// - a , seperates slider controls (max 5) or color controls (max 3). Palette has only one value -// - a ! means that the default is used. -// - For sliders: Effect speeds, Effect intensity, Custom 1, Custom 2, Custom 3 -// - For colors: Fx color, Background color, Custom -// - For palette: prompt for color palette OR palette ID if numeric (will hide palette selection) -// -// Note: If palette is on and no colors are specified 1,2 and 3 is shown in each color circle. -// If a color is specified, the 1,2 or 3 is replaced by that specification. -// Note: Effects can override default pattern behaviour -// - FadeToBlack can override the background setting -// - Defining SEGCOL() can override a specific palette using these values (e.g. Color Gradient) -const char JSON_mode_names[] PROGMEM = R"=====([ -"Solid", -"Blink@!,;!,!,;!", -"Breathe@!,;!,!;!", -"Wipe@!,!;!,!,;!", -"Wipe Random@!,;;!", -"Random Colors@!,Fade time;;!", -"Sweep@!,!;!,!,;!", -"Dynamic", -"Colorloop@!,Saturation;;!", -"Rainbow", -"Scan@!,# of dots;!,!,;!", -"Scan Dual@!,# of dots;!,!,;!", -"Fade", -"Theater@!,Gap size;!,!,;!", -"Theater Rainbow", -"Running@!,Wave width;!,!,;!", -"Saw@!,Width;!,!,;!", -"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@!,Width;!,!,;!", -"Aurora", -"Stream", -"Scanner", -"Lighthouse", -"Fireworks", -"Rain", -"Tetrix@!,Width;!,!,;!", -"Fire Flicker", -"Gradient", -"Loading", -"Police@!,Width;;0", -"Fairy", -"Two Dots@!,Dot size;1,2,Bg;!", -"Fairy Twinkle", -"Running Dual", -"Halloween", -"Chase 3@!,Size;1,2,3;0", -"Tri Wipe@!,Width;1,2,3;0", -"Tri Fade", -"Lightning", -"ICU", -"Multi Comet", -"Scanner Dual", -"Stream 2", -"Oscillate", -"Pride 2015", -"Juggle@!=16,Trail=240;!,!,;!", -"Palette@!,;;!", -"Fire 2012@Spark rate=120,Decay=64;;!", -"Colorwaves", -"Bpm", -"Fill Noise", -"Noise 1", -"Noise 2", -"Noise 3", -"Noise 4", -"Colortwinkles", -"Lake", -"Meteor@!,Trail length;!,!,;!", -"Meteor Smooth@!,Trail length;!,!,;!", -"Railway", -"Ripple", -"Twinklefox", -"Twinklecat", -"Halloween Eyes", -"Solid Pattern@Fg size,Bg size;Fg,Bg,;0", -"Solid Pattern Tri@,Size;1,2,3;0", -"Spots@Spread,Width;!,!,;!", -"Spots Fade@Spread,Width;!,!,;!", -"Glitter", -"Candle@Flicker rate=96,Flicker intensity=224;!,!,;0", -"Fireworks Starburst", -"Fireworks 1D@Gravity,Firing side;!,!,;!", -"Bouncing Balls@Gravity,# of balls;!,!,;!", -"Sinelon", -"Sinelon Dual", -"Sinelon Rainbow", -"Popcorn@", -"Drip@Gravity,# of drips;!,!;!", -"Plasma@Phase,;1,2,3;!", -"Percent@,% of fill;!,!,;!", -"Ripple Rainbow", -"Heartbeat", -"Pacifica", -"Candle Multi@Flicker rate=96,Flicker intensity=224;!,!,;0", -"Solid Glitter@,!;!,,;0", -"Sunrise@Time [min]=60,;;0", -"Phased", -"Twinkleup@!,Intensity;!,!,;!", -"Noise Pal", -"Sine", -"Phased Noise", -"Flow", -"Chunchun@!,Gap size;!,!,;!", -"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" -])====="; -*/ #endif diff --git a/wled00/FX_2Dfcn.cpp b/wled00/FX_2Dfcn.cpp new file mode 100644 index 00000000..bbbae759 --- /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 f6655561..13fe77dc 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, ...) @@ -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,116 +880,67 @@ 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; + _isServicing = true; + _segment_index = 0; + for (segment &seg : _segments) { + // reset the segment runtime data if needed + seg.resetIfRequired(); - _segment_index = i; - - // reset the segment runtime data if needed, called before isActive to ensure deleted - // segment's buffers are cleared - SEGENV.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 - SEGLEN = 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 (!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); - delay = (this->*_mode[SEGMENT.mode])(); //effect function - if (SEGMENT.mode != FX_MODE_HALLOWEEN_EYES) SEGENV.call++; + 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++; } - SEGLEN = 0; + _virtualSegmentLength = 0; busses.setSegmentCCT(-1); if(doShow) { yield(); 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 (i >= _length) return; + if (i < customMappingSize) i = customMappingTable[i]; + busses.setPixelColor(i, col); +} - 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]; - - 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); } @@ -362,61 +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; -} - //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); } } } @@ -426,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) { @@ -436,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; @@ -487,63 +1146,15 @@ 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++) { + 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(); @@ -551,69 +1162,11 @@ uint16_t WS2812FX::getLengthPhysical(void) { 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 (c1x != b.c1x) d |= SEG_DIFFERS_FX; - if (c2x != b.c2x) d |= SEG_DIFFERS_FX; - if (c3x != b.c3x) 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; - - 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 |= 0x01; // segment supports RGB (full color) - 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; -} - //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()) { @@ -628,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()) { @@ -640,86 +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; - _segments[0].c1x = 0; - _segments[0].c2x = 0; - _segments[0].c3x = 0; - - 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; - _segments[i].c1x = 0; - _segments[i].c2x = 0; - _segments[i].c3x = 0; - _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; @@ -727,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]); @@ -740,22 +1316,21 @@ void WS2812FX::makeAutoSegments(bool forceReset) { } s++; } - for (uint8_t i = 0; i < MAX_NUM_SEGMENTS; i++) { - _segments[i].setOption(SEG_OPTION_SELECTED, true, 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; } } @@ -764,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; @@ -795,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; - SEGLEN = SEGMENT.virtualLength(); + _virtualSegmentLength = _segments[_segment_index].virtualLength(); } return prevSegId; } @@ -813,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; - } + for (segment &seg : _segments) if (!seg.transitional) seg.startTransition(t ? _transitionDur : 0); } -/* - * 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) +#ifdef WLED_DEBUG +void WS2812FX::printSize() { - setPixelColor(n, color_blend(getPixelColor(n), color, blend)); + 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 -/* - * 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) +void WS2812FX::loadCustomPalettes() { - 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); -} - - -CRGB IRAM_ATTR WS2812FX::col_to_crgb(uint32_t color) -{ - CRGB fastled_col; - fastled_col.red = R(color); - fastled_col.green = G(color); - fastled_col.blue = B(color); - return fastled_col; -} - - -void WS2812FX::load_gradient_palette(uint8_t index) -{ - 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); -} + 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); -/* - * 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 (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)); + } + } + } else { + break; } } - 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); - } - 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)); - } - - 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); @@ -1202,54 +1488,6 @@ 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; @@ -1258,150 +1496,7 @@ int16_t Bus::_cct = -1; uint8_t Bus::_cctBlend = 0; uint8_t Bus::_gAWM = 255; - -// WLEDSR: extensions -// Technical notes -// =============== -// If an effect name is followed by an @, slider and color control is effective. -// See setSliderAndColorControl in index.js for implementation -// If not effective then: -// - For AC effects (id<128) 2 sliders and 3 colors and the palette will be shown -// - For SR effects (id>128) 5 sliders and 3 colors and the palette will be shown -// If effective (@) -// - a ; seperates slider controls (left) from color controls (middle) and palette control (right) -// - if left, middle or right is empty no controls are shown -// - a , seperates slider controls (max 5) or color controls (max 3). Palette has only one value -// - a ! means that the default is used. -// - For sliders: Effect speeds, Effect intensity, Custom 1, Custom 2, Custom 3 -// - For colors: Fx color, Background color, Custom -// - For palette: prompt for color palette OR palette ID if numeric (will hide palette selection) -// -// Note: If palette is on and no colors are specified 1,2 and 3 is shown in each color circle. -// If a color is specified, the 1,2 or 3 is replaced by that specification. -// Note: Effects can override default pattern behaviour -// - FadeToBlack can override the background setting -// - Defining SEGCOL() can override a specific palette using these values (e.g. Color Gradient) -const char JSON_mode_names[] PROGMEM = R"=====([ -"Solid", -"Blink@!,;!,!,;!", -"Breathe@!,;!,!;!", -"Wipe@!,!;!,!,;!", -"Wipe Random@!,;1,2,3;!", -"Random Colors@!,Fade time;1,2,3;!", -"Sweep@!,!;!,!,;!", -"Dynamic@!,!;1,2,3;!", -"Colorloop@!,Saturation;1,2,3;!", -"Rainbow@!,Size;1,2,3;!", -"Scan@!,# of dots;!,!,;!", -"Scan Dual@!,# of dots;!,!,;!", -"Fade@!,;!,!,;!", -"Theater@!,Gap size;!,!,;!", -"Theater Rainbow@!,Gap size;1,2,3;!", -"Running@!,Wave width;!,!,;!", -"Saw@!,Width;!,!,;!", -"Twinkle@!,;!,!,;!", -"Dissolve@Repeat speed,Dissolve speed;!,!,;!", -"Dissolve Rnd@Repeat speed,Dissolve speed;,!,;!", -"Sparkle@!,;!,!,;!", -"Sparkle Dark@!,!;Bg,Fx,;!", -"Sparkle+@!,!;Bg,Fx,;!", -"Strobe@!,;!,!,;!", -"Strobe Rainbow@!,;,!,;!", -"Strobe Mega@!,!;!,!,;!", -"Blink Rainbow@Frequency,Blink duration;!,!,;!", -"Android@!,Width;!,!,;!", -"Chase@!,Width;!,!,!;!", -"Chase Random@!,Width;!,,!;!", -"Chase Rainbow@!,Width;!,!,;0", -"Chase Flash@!,;Bg,Fx,!;!", -"Chase Flash Rnd@!,;,Fx,;!", -"Rainbow Runner@!,Size;Bg,,;!", -"Colorful@!,Saturation;1,2,3;!", -"Traffic Light@!,;,!,;!", -"Sweep Random", -"Chase 2@!,Width;!,!,;!", -"Aurora@!=24,!;1,2,3;!=50", -"Stream", -"Scanner", -"Lighthouse", -"Fireworks@Sharpness=96,Frequency=192;!,2,;!=11", -"Rain@Fade rate=128,Frequency=128;!,2,;!", -"Tetrix@!=224,Width=0;!,!,;!=11", -"Fire Flicker@!,!;!,,;!", -"Gradient@!,Spread=16;!,!,;!", -"Loading@!,Fade=16;!,!,;!", -"Police@!,Width;,Bg,;0", -"Fairy", -"Two Dots@!,Dot size;1,2,Bg;!", -"Fairy Twinkle", -"Running Dual", -"Halloween", -"Chase 3@!,Size;1,2,3;0", -"Tri Wipe@!,Width;1,2,3;0", -"Tri Fade", -"Lightning", -"ICU", -"Multi Comet", -"Scanner Dual", -"Stream 2", -"Oscillate", -"Pride 2015@!,;;", -"Juggle@!=16,Trail=240;!,!,;!", -"Palette@!,;1,2,3;!", -"Fire 2012@Spark rate=120,Decay=64;1,2,3;!", -"Colorwaves", -"Bpm@!=64,;1,2,3;!", -"Fill Noise", -"Noise 1", -"Noise 2", -"Noise 3", -"Noise 4", -"Colortwinkles@Fade speed,Spawn speed;1,2,3;!", -"Lake@!,;1,2,3;!", -"Meteor@!,Trail length;!,!,;!", -"Meteor Smooth@!,Trail length;!,!,;!", -"Railway", -"Ripple", -"Twinklefox", -"Twinklecat", -"Halloween Eyes", -"Solid Pattern@Fg size,Bg size;Fg,Bg,;!=0", -"Solid Pattern Tri@,Size;1,2,3;!=0", -"Spots@Spread,Width;!,!,;!", -"Spots Fade@Spread,Width;!,!,;!", -"Glitter", -"Candle@Flicker rate=96,Flicker intensity=224;!,!,;0", -"Fireworks Starburst", -"Fireworks 1D@Gravity,Firing side;!,!,;!", -"Bouncing Balls@Gravity,# of balls;!,!,;!", -"Sinelon", -"Sinelon Dual", -"Sinelon Rainbow", -"Popcorn", -"Drip@Gravity,# of drips;!,!;!", -"Plasma@Phase,;1,2,3;!", -"Percent@,% of fill;!,!,;!", -"Ripple Rainbow", -"Heartbeat@!,!;!,!,;!", -"Pacifica", -"Candle Multi@Flicker rate=96,Flicker intensity=224;!,!,;0", -"Solid Glitter@,!;!,,;0", -"Sunrise@Time [min]=60,;;0", -"Phased", -"Twinkleup@!,Intensity;!,!,;!", -"Noise Pal", -"Sine", -"Phased Noise", -"Flow", -"Chunchun@!,Gap size;!,!,;!", -"Dancing Shadows@!,# of shadows;!,,;!", -"Washing Machine", -"Candy Cane@!,Width;;", -"Blends@Shift speed,Blend speed;1,2,3,!", -"TV Simulator", -"Dynamic Smooth" -])====="; - +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", diff --git a/wled00/bus_manager.h b/wled00/bus_manager.h index 4a137325..405c402a 100644 --- a/wled00/bus_manager.h +++ b/wled00/bus_manager.h @@ -461,7 +461,7 @@ class BusPwm : public Bus { return numPins; } - inline void cleanup() { + void cleanup() { deallocatePins(); } @@ -494,6 +494,71 @@ 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, bc.autoWhite) { diff --git a/wled00/bus_wrapper.h b/wled00/bus_wrapper.h index ac720fe4..e2783236 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 @@ -201,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; @@ -230,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; @@ -271,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; @@ -295,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) @@ -337,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; @@ -361,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; @@ -400,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; @@ -424,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; @@ -487,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; @@ -511,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; @@ -550,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; @@ -574,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; @@ -614,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; @@ -638,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; @@ -697,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; @@ -721,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; @@ -779,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 @@ -799,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 4c11e9b0..b472f927 100644 --- a/wled00/button.cpp +++ b/wled00/button.cpp @@ -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,10 +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 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); diff --git a/wled00/cfg.cpp b/wled00/cfg.cpp index ac081a3f..6ee4a191 100644 --- a/wled00/cfg.cpp +++ b/wled00/cfg.cpp @@ -90,6 +90,44 @@ bool deserializeConfig(JsonObject doc, bool fromFS) { 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")]; @@ -470,7 +532,9 @@ 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; } @@ -480,7 +544,9 @@ void deserializeConfigFromFS() { 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; } @@ -584,6 +650,31 @@ void serializeConfig() { hw_led[F("cb")] = strip.cctBlending; hw_led["fps"] = strip.getTargetFps(); 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; i> 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 00000000..8429de7d --- /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 00000000..cf1fdd88 --- /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 00000000..e8da19d7 --- /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 00000000..35466f70 --- /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 02371aba..d3953cd8 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 @@ -356,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 a62228c4..71cea309 100644 --- a/wled00/data/index.css +++ b/wled00/data/index.css @@ -123,11 +123,19 @@ 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; } @@ -185,7 +193,7 @@ button { position: absolute; top: 8px; left: 12px; - pointer-events: none; + /*pointer-events: none;*/ width: 24px; height: 24px; } @@ -225,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); @@ -359,27 +378,85 @@ button { bottom: 5px; } -#fx { - height: calc(100% - 121px); - overflow-y: scroll; - padding-left: 6px; /* compensate scroll bar */ -} - #sliders { - width: 310px; + width: 300px; margin: 0 auto; + position: sticky; + bottom: 0; } #sliders .labels { padding-top: 3px; + font-size: small; } -#slider0, #slider1, #slider2, #slider3, #slider4 { - background: var(--c-2); - max-width: 310px; +.slider { + background-color: var(--c-2); + max-width: 300px; min-width: 280px; - margin: 0 auto 5px; - border-radius: 15px; + 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; + opacity: 1; + transition: opacity 0.5s linear, height 0.5s, transform 0.5s; + transform: scaleY(1); +} + +.option { + z-index: unset; +} + +/* 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 { @@ -389,6 +466,13 @@ button { .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 { margin-top: 10px; @@ -473,13 +557,14 @@ button { z-index: -2; } -#imgw { - display: inline-block; - margin: 8px; +#info .slider { + max-width: 200px; + min-width: 145px; + float: right; + margin: 0; } - -#kv, #kn { - display: inline-block; +#info .sliderwrap { + width: 200px; } #info table, #nodes table { @@ -502,6 +587,15 @@ button { margin: 0 auto; } +#imgw { + /*display: inline-block;*/ + margin: 8px auto; +} +/* +#kv, #kn { + display: inline-block; +} +*/ #lv { max-width: 600px; display: inline-block; @@ -708,6 +802,9 @@ input[type=range]::-moz-range-thumb { #putil .btn-xs { margin: 0; } +#info .btn-xs { + border: 1px solid var(--c-4); +} #putil .btn-s { width: 135px; @@ -756,19 +853,22 @@ input[type=range]::-moz-range-thumb { } select { - padding: 4px; + padding: 4px 8px; margin: 0; font-size: 19px; background-color: var(--c-3); color: var(--c-d); cursor: pointer; - border: 1px solid var(--c-2); - border-radius: 5px; + 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; @@ -776,16 +876,26 @@ select { .cl { background-color: #000; } -.sel-p { - margin: 5px 0 10px; - width: 5em; -} -.sel-pl { +select.sel-p, select.sel-pl, select.sel-ple { + margin: 5px 0; width: 100%; - background-position: 141px 16px; + height: 40px; } -.sel-ple { - width: 100%; +div.sel-p { + position: relative; +} +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 { @@ -869,10 +979,18 @@ textarea { white-space: nowrap; cursor: pointer; text-align: center; - overflow: hidden; - text-overflow: clip; + overflow: clip; + text-overflow: ellipsis; line-height: 24px; - padding: 8px 0; + padding: 8px 24px; + max-width: 170px; + margin: 0 auto; + position: relative; +} + +.segname .flr, .pname .flr { + transform: rotate(0deg); + right: -6px; } /* segment power wrapper */ @@ -907,19 +1025,24 @@ textarea { } .xxs { - border: 2px solid var(--c-e) !important; width: 44px; height: 44px; margin: 5px; - /*box-shadow: 0 0 0 2px #fff;*/ } .xxs-w { - border-width: 5px !important; margin: 2px; width: 50px; height: 50px; - /*box-shadow: 0 0 0 5px #fff;*/ +} + +#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 { @@ -948,11 +1071,12 @@ textarea { } .frz { - left: 36px; + left: 32px; position: absolute; top: -3px; cursor: pointer; padding: 8px; + z-index: 1; } .expanded .frz { @@ -972,6 +1096,11 @@ textarea { margin-top: 8px; } +TD .revchkl { + padding: 0 0 0 32px; + margin-top: 0; +} + .check input, .radio input { position: absolute; opacity: 0; @@ -1041,6 +1170,10 @@ textarea { background: var(--c-f); } +TD .checkmark, TD .radiomark { + top: -6px; +} + .h { font-size: 13px; text-align: center; @@ -1068,16 +1201,32 @@ textarea { top: auto !important; /* prevent sticky */ bottom: auto !important; } -/* -.seg:last-child { - margin: 0; -} -*/ +/* checkmark labels */ .seg .schkl { position: absolute; top: 7px; left: 9px; } +/* 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%); +} + +.lbl-s { + display: inline-block; + /* margin: 10px 4px 0 0; */ + margin-top: 6px; + font-size: 13px; + width: 48%; + text-align: center; +} /* list wrapper */ .list { @@ -1132,6 +1281,7 @@ textarea { .lstI.sticky, .lstI.selected { z-index: 1; + box-shadow: 0px 0px 10px 4px var(--c-1); } #pcont .selected:not([class*="expanded"]) { @@ -1164,6 +1314,9 @@ textarea { /* list item name (for sorting) */ .lstIname { white-space: nowrap; + text-overflow: ellipsis; + -webkit-filter: grayscale(100%); /* Safari 6.0 - 9.0 */ + filter: grayscale(100%); } /* list item palette preview */ @@ -1193,6 +1346,8 @@ textarea { 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%); } .fnd input[type="text"]:focus { @@ -1293,7 +1448,7 @@ textarea { width: 145px; } #info div, #nodes div { - width: 320px; + max-width: 320px; } } diff --git a/wled00/data/index.htm b/wled00/data/index.htm index 219e2daf..f4b50c44 100644 --- a/wled00/data/index.htm +++ b/wled00/data/index.htm @@ -46,9 +46,9 @@ } }, 100); }); - setTimeout(()=>{h.appendChild(l)},50); + setTimeout(()=>{h.appendChild(l)},100); }); - setTimeout(()=>{h.appendChild(l)},50); + setTimeout(()=>{h.appendChild(l)},100); @@ -200,9 +200,9 @@

Effect mode

- + - +
@@ -217,50 +217,86 @@
-

Effect speed

-
+
+ + + + + +
+
+ Effect speed
-

Effect intensity

-
+
+ Effect intensity
-

Custom 1

-
+
- +
+ Custom 1
-

Custom 2

-
+
- +
+ Custom 2
-

Custom 3

-
+
- +
+ Custom 3 +
+
+ + +
@@ -308,7 +344,7 @@
-

+
Loading...

@@ -329,6 +365,10 @@
+ +