From 7b935d2f9c93b9668cbc3c4e7fd03e080fd0d944 Mon Sep 17 00:00:00 2001 From: Alex Date: Tue, 23 Feb 2021 16:46:13 +0300 Subject: [PATCH] v0.14b --- firmware/GyverLamp2/GyverLamp2.ino | 14 +-- firmware/GyverLamp2/effects.ino | 125 ++++++++++++++------------ firmware/GyverLamp2/fire2020.ino | 52 +++++++++++ firmware/GyverLamp2/fire2D.ino | 8 ++ firmware/GyverLamp2/palettes.h | 13 +++ firmware/GyverLamp2/parsing.ino | 1 + firmware/GyverLamp2/presetManager.ino | 2 +- 7 files changed, 152 insertions(+), 63 deletions(-) create mode 100644 firmware/GyverLamp2/fire2020.ino diff --git a/firmware/GyverLamp2/GyverLamp2.ino b/firmware/GyverLamp2/GyverLamp2.ino index a61e714..2e87ced 100644 --- a/firmware/GyverLamp2/GyverLamp2.ino +++ b/firmware/GyverLamp2/GyverLamp2.ino @@ -1,4 +1,10 @@ /* + Версия 0.14b + Мелкие баги + Вернул искры огню + Добавлены палитры + Добавлен огонь 2020 + Версия 0.13b Улучшена стабильность @@ -24,9 +30,6 @@ Базовый пак Предложения Серёги крутского Убрать аплод? - Огонь 2018/2020? - Взять огонь отсюда https://community.alexgyver.ru/threads/wifi-lampa-budilnik-obsuzhdenie-proshivki-ot-gunner47.2418/page-72#post-33652 - Вернуть искры Эффект погода https://it4it.club/topic/40-esp8266-i-parsing-pogodyi-s-openweathermap/ Эффект часы */ @@ -70,10 +73,10 @@ const char AP_NameChar[] = "GyverLamp2"; const char WiFiPassword[] = "12345678"; // ------------ Прочее ------------- -#define GL_VERSION 013 // код версии прошивки +#define GL_VERSION 014 // код версии прошивки #define EE_TOUT 30000 // таймаут сохранения епром после изменения, мс //#define DEBUG_SERIAL // закомментируй чтобы выключить отладку (скорость 115200) -#define EE_KEY 51 // ключ сброса WiFi (измени для сброса всех настроек) +#define EE_KEY 52 // ключ сброса WiFi (измени для сброса всех настроек) #define NTP_UPD_PRD 5 // период обновления времени с NTP сервера, минут //#define SKIP_WIFI // пропустить подключение к вафле (для отладки) @@ -127,6 +130,7 @@ FastFilter phot; byte btnClicks = 0, brTicks = 0; unsigned char matrixValue[11][16]; bool gotNTP = false, gotTime = false; +bool loading = true; void blink8(CRGB color); // ------------------- SETUP -------------------- diff --git a/firmware/GyverLamp2/effects.ino b/firmware/GyverLamp2/effects.ino index 3091fd5..e673f3e 100644 --- a/firmware/GyverLamp2/effects.ino +++ b/firmware/GyverLamp2/effects.ino @@ -7,7 +7,10 @@ void effectsRoutine() { fill_solid(leds, MAX_LEDS, ColorFromPalette(HeatColors_p, dawnTmr.getLength8(), scaleFF(dawnTmr.getLength8(), dawn.bright), LINEARBLEND)); FastLED.show(); } - if (dawnTmr.isReady()) dawnTmr.stop(); + if (dawnTmr.isReady()) { + dawnTmr.stop(); + FastLED.clear(); + } return; } @@ -28,8 +31,9 @@ void effectsRoutine() { if (prevEff != CUR_PRES.effect) { FastLED.clear(); prevEff = CUR_PRES.effect; + loading = true; } - + // =================================================== ЭФФЕКТЫ =================================================== switch (CUR_PRES.effect) { case 1: // =================================== ПЕРЛИН =================================== if (cfg.deviceType > 1) { @@ -56,8 +60,8 @@ void effectsRoutine() { case 2: // ==================================== ЦВЕТ ==================================== { - fill_solid(leds, cfg.length * thisWidth, CHSV(CUR_PRES.color, thisScale, CUR_PRES.min)); - CRGB thisColor = CHSV(CUR_PRES.color, thisScale, CUR_PRES.max); + fill_solid(leds, cfg.length * thisWidth, CHSV(CUR_PRES.color, thisScale, 30)); + CRGB thisColor = CHSV(CUR_PRES.color, thisScale, thisBright); if (CUR_PRES.fromCenter) { fillStrip(cfg.length / 2, cfg.length / 2 + thisLength / 2, thisColor); fillStrip(cfg.length / 2 - thisLength / 2, cfg.length / 2, thisColor); @@ -69,9 +73,9 @@ void effectsRoutine() { case 3: // ================================= СМЕНА ЦВЕТА ================================= { - CRGB thisColor = ColorFromPalette(paletteArr[CUR_PRES.palette - 1], scalePal((now.weekMs >> 5) * CUR_PRES.speed / 255), CUR_PRES.min, LINEARBLEND); + CRGB thisColor = ColorFromPalette(paletteArr[CUR_PRES.palette - 1], scalePal((now.weekMs >> 5) * CUR_PRES.speed / 255), 10, LINEARBLEND); fill_solid(leds, cfg.length * thisWidth, thisColor); - thisColor = ColorFromPalette(paletteArr[CUR_PRES.palette - 1], scalePal((now.weekMs >> 5) * CUR_PRES.speed / 255), CUR_PRES.max, LINEARBLEND); + thisColor = ColorFromPalette(paletteArr[CUR_PRES.palette - 1], scalePal((now.weekMs >> 5) * CUR_PRES.speed / 255), thisBright, LINEARBLEND); if (CUR_PRES.fromCenter) { fillStrip(cfg.length / 2, cfg.length / 2 + thisLength / 2, thisColor); fillStrip(cfg.length / 2 - thisLength / 2, cfg.length / 2, thisColor); @@ -85,7 +89,7 @@ void effectsRoutine() { if (CUR_PRES.fromCenter) { FOR_i(cfg.length / 2, cfg.length) { byte bright = 255; - if (CUR_PRES.soundReact == GL_REACT_LEN) bright = (i < cfg.length / 2 + thisLength / 2) ? (CUR_PRES.max) : (CUR_PRES.min); + if (CUR_PRES.soundReact == GL_REACT_LEN) bright = (i < cfg.length / 2 + thisLength / 2) ? (thisBright) : (10); CRGB thisColor = ColorFromPalette( paletteArr[CUR_PRES.palette - 1], // (x*1.9 + 25) / 255 - быстрый мап 0..255 в 0.1..2 scalePal((i * (thisScale * 1.9 + 25) / cfg.length) + ((now.weekMs >> 3) * (CUR_PRES.speed - 128) / 128)), @@ -99,7 +103,7 @@ void effectsRoutine() { } else { FOR_i(0, cfg.length) { byte bright = 255; - if (CUR_PRES.soundReact == GL_REACT_LEN) bright = (i < thisLength) ? (CUR_PRES.max) : (CUR_PRES.min); + if (CUR_PRES.soundReact == GL_REACT_LEN) bright = (i < thisLength) ? (thisBright) : (10); CRGB thisColor = ColorFromPalette( paletteArr[CUR_PRES.palette - 1], // (x*1.9 + 25) / 255 - быстрый мап 0..255 в 0.1..2 scalePal((i * (thisScale * 1.9 + 25) / cfg.length) + ((now.weekMs >> 3) * (CUR_PRES.speed - 128) / 128)), @@ -117,14 +121,14 @@ void effectsRoutine() { FOR_i(0, thisScale / 8 + 1) { rndVal = rndVal * 2053 + 13849; // random2053 алгоритм int homeX = inoise16(i * 100000000ul + (now.weekMs << 3) * CUR_PRES.speed / 255); - homeX = map(homeX, 10000, 55000, 0, cfg.length); + homeX = map(homeX, 15000, 50000, 0, cfg.length); int offsX = inoise8(i * 2500 + (now.weekMs >> 1) * CUR_PRES.speed / 255) - 128; offsX = cfg.length / 2 * offsX / 128; int thisX = homeX + offsX; if (cfg.deviceType > 1) { int homeY = inoise16(i * 100000000ul + 2000000000ul + (now.weekMs << 3) * CUR_PRES.speed / 255); - homeY = map(homeY, 10000, 55000, 0, cfg.width); + homeY = map(homeY, 15000, 50000, 0, cfg.width); int offsY = inoise8(i * 2500 + 30000 + (now.weekMs >> 1) * CUR_PRES.speed / 255) - 128; offsY = cfg.length / 2 * offsY / 128; int thisY = homeY + offsY; @@ -134,59 +138,58 @@ void effectsRoutine() { } } } - /* - FOR_i(0, cfg.length * cfg.width) leds[i].fadeToBlackBy(70); - if (cfg.deviceType > 1) { - uint16_t rndVal = 0; - FOR_i(0, thisScale / 8) { - int thisY = inoise16(i * 100000000ul + (now.weekMs << 6) * CUR_PRES.speed / 255); - thisY = map(thisY, 10000, 55000, 0, cfg.length); - int thisX = inoise16(i * 100000000ul + 2000000000ul + (now.weekMs << 6) * CUR_PRES.speed / 255); - thisX = map(thisX, 10000, 55000, 0, cfg.width); - rndVal = rndVal * 2053 + 13849; // random2053 алгоритм - - if (thisY >= 0 && thisY < cfg.length && thisX >= 0 && thisX < cfg.width) - leds[getPix(thisX, thisY)] = CHSV(CUR_PRES.rnd ? rndVal : CUR_PRES.color, 255, 255); - } - } else { - uint16_t rndVal = 0; - FOR_i(0, thisScale / 8) { - int thisPos = inoise16(i * 100000000ul + (now.weekMs << 6) * CUR_PRES.speed / 255); - thisPos = map(thisPos, 10000, 55000, 0, cfg.length); - rndVal = rndVal * 2053 + 13849; // random2053 алгоритм - if (thisPos >= 0 && thisPos < cfg.length) leds[thisPos] = CHSV(CUR_PRES.rnd ? rndVal : CUR_PRES.color, 255, 255); - } - }*/ break; case 6: // ==================================== ОГОНЬ ==================================== - { - if (cfg.deviceType > 1) { // 2D огонь - fireRoutine(CUR_PRES.speed / 2); - } else { // 1D огонь - static byte heat[MAX_LEDS]; - CRGBPalette16 gPal; - if (CUR_PRES.color < 5) gPal = HeatColors_p; - else gPal = CRGBPalette16(CRGB::Black, CHSV(CUR_PRES.color, 255, 255), CRGB::White); - if (CUR_PRES.fromCenter) thisLength /= 2; + if (cfg.deviceType > 1) { // 2D огонь + fireRoutine(CUR_PRES.speed / 2); + } else { // 1D огонь + FastLED.clear(); + static byte heat[MAX_LEDS]; + CRGBPalette16 gPal; + if (CUR_PRES.color < 5) gPal = HeatColors_p; + else gPal = CRGBPalette16(CRGB::Black, CHSV(CUR_PRES.color, 255, 255), CRGB::White); + if (CUR_PRES.fromCenter) thisLength /= 2; - for (int i = 0; i < thisLength; i++) heat[i] = qsub8(heat[i], random8(0, ((((255 - thisScale) / 2 + 20) * 10) / thisLength) + 2)); - for (int k = thisLength - 1; k >= 2; k--) heat[k] = (heat[k - 1] + heat[k - 2] + heat[k - 2] ) / 3; - if (random8() < 120 ) { - int y = random8(7); - heat[y] = qadd8(heat[y], random8(160, 255)); - } - if (CUR_PRES.fromCenter) { - for (int j = 0; j < thisLength; j++) leds[cfg.length / 2 + j] = ColorFromPalette(gPal, scale8(heat[j], 240)); - FOR_i(0, cfg.length / 2) leds[i] = leds[cfg.length - i - 1]; - } else { - for (int j = 0; j < thisLength; j++) leds[j] = ColorFromPalette(gPal, scale8(heat[j], 240)); - } + for (int i = 0; i < thisLength; i++) heat[i] = qsub8(heat[i], random8(0, ((((255 - thisScale) / 2 + 20) * 10) / thisLength) + 2)); + for (int k = thisLength - 1; k >= 2; k--) heat[k] = (heat[k - 1] + heat[k - 2] + heat[k - 2] ) / 3; + if (random8() < 120 ) { + int y = random8(7); + heat[y] = qadd8(heat[y], random8(160, 255)); + } + if (CUR_PRES.fromCenter) { + for (int j = 0; j < thisLength; j++) leds[cfg.length / 2 + j] = ColorFromPalette(gPal, scale8(heat[j], 240)); + FOR_i(0, cfg.length / 2) leds[i] = leds[cfg.length - i - 1]; + } else { + for (int j = 0; j < thisLength; j++) leds[j] = ColorFromPalette(gPal, scale8(heat[j], 240)); } } break; - case 7: // ================================== КОНФЕТТИ ================================== + case 7: // ==================================== ОГОНЬ 2020 ==================================== + FastLED.clear(); + if (cfg.deviceType > 1) { // 2D огонь + fire2020(CUR_PRES.scale, thisLength); + } else { // 1D огонь + static byte heat[MAX_LEDS]; + if (CUR_PRES.fromCenter) thisLength /= 2; + + for (int i = 0; i < thisLength; i++) heat[i] = qsub8(heat[i], random8(0, ((((255 - thisScale) / 2 + 20) * 10) / thisLength) + 2)); + for (int k = thisLength - 1; k >= 2; k--) heat[k] = (heat[k - 1] + heat[k - 2] + heat[k - 2] ) / 3; + if (random8() < 120 ) { + int y = random8(7); + heat[y] = qadd8(heat[y], random8(160, 255)); + } + if (CUR_PRES.fromCenter) { + for (int j = 0; j < thisLength; j++) leds[cfg.length / 2 + j] = ColorFromPalette(paletteArr[CUR_PRES.palette - 1], scale8(heat[j], 240)); + FOR_i(0, cfg.length / 2) leds[i] = leds[cfg.length - i - 1]; + } else { + for (int j = 0; j < thisLength; j++) leds[j] = ColorFromPalette(paletteArr[CUR_PRES.palette - 1], scale8(heat[j], 240)); + } + } + break; + + case 8: // ================================== КОНФЕТТИ ================================== FOR_i(0, (thisScale >> 3) + 1) { int x = random(0, cfg.length * cfg.width); if (leds[x] == CRGB(0, 0, 0)) leds[x] = CHSV(CUR_PRES.rnd ? random(0, 255) : CUR_PRES.color, 255, 255); @@ -197,7 +200,7 @@ void effectsRoutine() { } break; - case 8: // ================================== ПОГОДА ================================== + case 9: // ================================== ПОГОДА ================================== break; } @@ -209,6 +212,8 @@ void effectsRoutine() { } } +// ==================================================================================================================== + bool musicMode() { return ((cfg.adcMode == GL_ADC_MIC || cfg.adcMode == GL_ADC_BOTH) && (CUR_PRES.advMode > 1 && CUR_PRES.advMode <= 4)); } @@ -233,7 +238,8 @@ byte getBright() { int getLength() { if (musicMode() // светомузыка вкл && CUR_PRES.soundReact == GL_REACT_LEN // режим длины - ) return mapFF(getSoundVol(), 0, cfg.length); + ) //return mapFF(getSoundVol(), 0, cfg.length); + return mapFF(getSoundVol(), scaleFF(cfg.length, CUR_PRES.min), scaleFF(cfg.length, CUR_PRES.max)); else return cfg.length; } @@ -287,6 +293,11 @@ void setPix(int x, int y, CRGB color) { void setLED(int x, CRGB color) { if (x >= 0 && x < cfg.length) leds[x] = color; } +uint32_t getPixColor(int x, int y) { + int thisPix = getPix(x, y); + if (thisPix < 0 || thisPix >= MAX_LEDS) return 0; + return (((uint32_t)leds[thisPix].r << 16) | ((long)leds[thisPix].g << 8 ) | (long)leds[thisPix].b); +} // получить номер пикселя в ленте по координатам uint16_t getPix(int x, int y) { diff --git a/firmware/GyverLamp2/fire2020.ino b/firmware/GyverLamp2/fire2020.ino new file mode 100644 index 0000000..22e5932 --- /dev/null +++ b/firmware/GyverLamp2/fire2020.ino @@ -0,0 +1,52 @@ +// ============= Огонь 2020 =============== +// (c) SottNick +//сильно по мотивам https://pastebin.com/RG0QGzfK +//Perlin noise fire procedure by Yaroslaw Turbin +//https://www.reddit.com/r/FastLED/comments/hgu16i/my_fire_effect_implementation_based_on_perlin/ + +void fire2020(byte scale, int len) { + static uint8_t deltaValue; + static uint8_t deltaHue; + static uint8_t step; + static uint8_t shiftHue[50]; + static float trackingObjectPosX[100]; + static float trackingObjectPosY[100]; + static uint16_t ff_x, ff_y, ff_z; + + if (loading) { + loading = false; + deltaValue = (((scale - 1U) % 11U + 1U) << 4U) - 8U; // ширина языков пламени (масштаб шума Перлина) + deltaHue = map(deltaValue, 8U, 168U, 8U, 84U); // высота языков пламени должна уменьшаться не так быстро, как ширина + step = map(255U - deltaValue, 87U, 247U, 4U, 32U); // вероятность смещения искорки по оси ИКС + for (uint8_t j = 0; j < cfg.length; j++) { + shiftHue[j] = (cfg.length - 1 - j) * 255 / (cfg.length - 1); // init colorfade table + } + + for (uint8_t i = 0; i < cfg.width / 8; i++) { + trackingObjectPosY[i] = random8(cfg.length); + trackingObjectPosX[i] = random8(cfg.width); + } + } + for (uint8_t i = 0; i < cfg.width; i++) { + for (uint8_t j = 0; j < len; j++) { + leds[getPix(i, len - 1U - j)] = ColorFromPalette(paletteArr[CUR_PRES.palette - 1], qsub8(inoise8(i * deltaValue, (j + ff_y + random8(2)) * deltaHue, ff_z), shiftHue[j]), 255U); + } + } + + //вставляем искорки из отдельного массива + for (uint8_t i = 0; i < cfg.width / 8; i++) { + if (trackingObjectPosY[i] > 3U) { + leds[getPix(trackingObjectPosX[i], trackingObjectPosY[i])] = leds[getPix(trackingObjectPosX[i], 3U)]; + leds[getPix(trackingObjectPosX[i], trackingObjectPosY[i])].fadeToBlackBy( trackingObjectPosY[i] * 2U ); + } + trackingObjectPosY[i]++; + if (trackingObjectPosY[i] >= len) { + trackingObjectPosY[i] = random8(4U); + trackingObjectPosX[i] = random8(cfg.width); + } + if (!random8(step)) + trackingObjectPosX[i] = (cfg.width + (uint8_t)trackingObjectPosX[i] + 1U - random8(3U)) % cfg.width; + } + ff_y++; + if (ff_y & 0x01) ff_z++; +} diff --git a/firmware/GyverLamp2/fire2D.ino b/firmware/GyverLamp2/fire2D.ino index 295c70c..972ca1a 100644 --- a/firmware/GyverLamp2/fire2D.ino +++ b/firmware/GyverLamp2/fire2D.ino @@ -72,6 +72,14 @@ void drawFrame(int pcnt) { 255, // S (uint8_t)max(0, nextv) // V ); + } else if (y == 11) { + if (random8(0, 20) == 0 && getPixColor(x, y - 1) != 0) setPix(x, y, getPixColor(x, y - 1)); + else setPix(x, y, 0); + } else { + // старая версия для яркости + if (getPixColor(x, y - 1) > 0) + setPix(x, y, getPixColor(x, y - 1)); + else setPix(x, y, 0); } } } diff --git a/firmware/GyverLamp2/palettes.h b/firmware/GyverLamp2/palettes.h index 7669120..2844c5e 100644 --- a/firmware/GyverLamp2/palettes.h +++ b/firmware/GyverLamp2/palettes.h @@ -222,10 +222,23 @@ DEFINE_GRADIENT_PALETTE ( aurora_gp ) { 255, 171, 101, 221 //Purple }; +const TProgmemRGBPalette16 WoodFireColors_p PROGMEM = {CRGB::Black, 0x330e00, 0x661c00, 0x992900, 0xcc3700, CRGB::OrangeRed, 0xff5800, 0xff6b00, 0xff7f00, 0xff9200, CRGB::Orange, 0xffaf00, 0xffb900, 0xffc300, 0xffcd00, CRGB::Gold}; //* рыжий +const TProgmemRGBPalette16 NormalFire_p PROGMEM = {CRGB::Black, 0x330000, 0x660000, 0x990000, 0xcc0000, CRGB::Red, 0xff0c00, 0xff1800, 0xff2400, 0xff3000, 0xff3c00, 0xff4800, 0xff5400, 0xff6000, 0xff6c00, 0xff7800}; // красный +const TProgmemRGBPalette16 LithiumFireColors_p PROGMEM = {CRGB::Black, 0x240707, 0x470e0e, 0x6b1414, 0x8e1b1b, CRGB::FireBrick, 0xc14244, 0xd16166, 0xe08187, 0xf0a0a9, CRGB::Pink, 0xff9ec0, 0xff7bb5, 0xff59a9, 0xff369e, CRGB::DeepPink}; //* пастель +const TProgmemRGBPalette16 SodiumFireColors_p PROGMEM = {CRGB::Black, 0x332100, 0x664200, 0x996300, 0xcc8400, CRGB::Orange, 0xffaf00, 0xffb900, 0xffc300, 0xffcd00, CRGB::Gold, 0xf8cd06, 0xf0c30d, 0xe9b913, 0xe1af1a, CRGB::Goldenrod}; //* Yellow +const TProgmemRGBPalette16 CopperFireColors_p PROGMEM = {CRGB::Black, 0x001a00, 0x003300, 0x004d00, 0x006600, CRGB::Green, 0x239909, 0x45b313, 0x68cc1c, 0x8ae626, CRGB::GreenYellow, 0x94f530, 0x7ceb30, 0x63e131, 0x4bd731, CRGB::LimeGreen}; //* Green +const TProgmemRGBPalette16 AlcoholFireColors_p PROGMEM = {CRGB::Black, 0x000033, 0x000066, 0x000099, 0x0000cc, CRGB::Blue, 0x0026ff, 0x004cff, 0x0073ff, 0x0099ff, CRGB::DeepSkyBlue, 0x1bc2fe, 0x36c5fd, 0x51c8fc, 0x6ccbfb, CRGB::LightSkyBlue}; //* Blue + CRGBPalette16 paletteArr[] = { customPal, HeatColors_p, Fire_gp, + WoodFireColors_p, + NormalFire_p, + LithiumFireColors_p, + SodiumFireColors_p, + CopperFireColors_p, + AlcoholFireColors_p, LavaColors_p, PartyColors_p, RainbowColors_p, diff --git a/firmware/GyverLamp2/parsing.ino b/firmware/GyverLamp2/parsing.ino index d8afc24..867ab54 100644 --- a/firmware/GyverLamp2/parsing.ino +++ b/firmware/GyverLamp2/parsing.ino @@ -118,6 +118,7 @@ void parsing() { if (!cfg.rotation) setPreset(data[cfg.presetAmount * PRES_SIZE + 3] - 1); EE_updatePreset(); presetRotation(true); // форсировать смену режима + loading = true; break; case 3: DEBUGLN("Dawn"); diff --git a/firmware/GyverLamp2/presetManager.ino b/firmware/GyverLamp2/presetManager.ino index 2a24cf0..89948ee 100644 --- a/firmware/GyverLamp2/presetManager.ino +++ b/firmware/GyverLamp2/presetManager.ino @@ -1,5 +1,5 @@ void presetRotation(bool force) { - if (cfg.rotation && (now.newMin() || force)) { // если автосмена и новая минута + if (cfg.rotation && (now.newMin() || force)) { // если автосмена и новая минута if (cfg.rotRnd) { // случайная cfg.curPreset = trnd.fromMin(cfg.rotPeriod, cfg.presetAmount); DEBUG("Rnd changed to ");