diff --git a/firmware/GyverLamp2/Clap.h b/firmware/GyverLamp2/Clap.h new file mode 100644 index 0000000..c669028 --- /dev/null +++ b/firmware/GyverLamp2/Clap.h @@ -0,0 +1,89 @@ +#pragma once +#include + +class Clap { + public: + void tick(int val) { + if (millis() - _tmr >= 10) { + _tmr = millis(); + int der = val - _prevVal; + _prevVal = val; + int signal = 0; + int front = 0; + if (der > _trsh) signal = 1; + if (der < -_trsh) signal = -1; + if (_prevSignal == 0 && signal == 1) front = 1; + if (_prevSignal == 0 && signal == -1) front = -1; + _prevSignal = signal; + + uint32_t deb = millis() - _tmr2; + + if (front == 1 && _state == 0) { + _state = 1; + if (!_startClap) { + _claps = 0; + _ready = 0; + } + _startClap = 1; + _clap = 0; + _tmr2 = millis(); + } else if (front == -1 && _state == 1 && deb <= 200) { + _state = 2; + _tmr2 = millis(); + } else if (front == 0 && _state == 2 && deb <= 200) { + _state = 0; + _claps++; + _clap = 1; + _tmr2 = millis(); + } else if (_startClap && deb > _tout) { + _state = 0; + _startClap = 0; + if (_claps != 0) _ready = 1; + } + } + } + void setTrsh(int trsh) { + _trsh = trsh; + } + void setTimeout(int tout) { + _tout = tout; + } + bool isClap() { + if (_clap) { + _clap = 0; + return 1; + } + return 0; + } + bool hasClaps(byte claps) { + if (_ready && _claps == claps) { + _ready = 0; + _claps = 0; + return 1; + } + return 0; + } + bool hasClaps() { + return _ready; + } + byte getClaps() { + if (_ready) { + _ready = 0; + byte buf = _claps; + _claps = 0; + return buf; + } return 0; + } + + private: + uint32_t _tmr = 0, _tmr2 = 0; + int _prevVal = 0; + int _trsh = 150; + byte _state = 0; + int8_t _prevSignal = 0; + int _tout = 700; + byte _claps = 0; + bool _ready = 0; + bool _clap = 0; + bool _startClap = 0; +}; diff --git a/firmware/GyverLamp2/FastFilter.h b/firmware/GyverLamp2/FastFilter.h index c330069..efbcd7e 100644 --- a/firmware/GyverLamp2/FastFilter.h +++ b/firmware/GyverLamp2/FastFilter.h @@ -8,6 +8,10 @@ class FastFilter { public: + FastFilter(byte k = 20, int dt = 0) { + setK(k); + setDt(dt); + } void setK(byte k) { _k1 = k; _k2 = 32 - k; diff --git a/firmware/GyverLamp2/GyverLamp2.ino b/firmware/GyverLamp2/GyverLamp2.ino index 82d5e10..e520cfc 100644 --- a/firmware/GyverLamp2/GyverLamp2.ino +++ b/firmware/GyverLamp2/GyverLamp2.ino @@ -1,4 +1,13 @@ /* + Версия 0.17b + Автосмена отключается 30 сек во время настройки режимов + Убрана кнопка upload в режимах + Лампа чуть мигает при получении данных + Кастом палитра работает на огне 2020 + Вкл выкл двумя хлопками + Плавное выключение + Починил рассвет + Версия 0.16b Исправлен масштаб огня 2020 Фикс невыключения рассвета @@ -27,13 +36,12 @@ Выключение по таймеру теперь плавное Добавлен рассвет - TODO: + TODO: плавная смена режимов 4 клика вкл выкл смену? Mqtt? Базовый пак Предложения Серёги крутского - Убрать аплод? Эффект погода https://it4it.club/topic/40-esp8266-i-parsing-pogodyi-s-openweathermap/ Эффект часы */ @@ -55,6 +63,7 @@ // ------------- АЦП -------------- #define USE_ADC 1 // можно выпилить АЦП +#define USE_CLAP 1 // два хлопка в ладоши вкл выкл лампу #define MIC_VCC 12 // питание микрофона GPIO12 (D6 на wemos/node) #define PHOT_VCC 14 // питание фоторезистора GPIO14 (D5 на wemos/node) @@ -77,10 +86,10 @@ const char AP_NameChar[] = "GyverLamp2"; const char WiFiPassword[] = "12345678"; // ------------ Прочее ------------- -#define GL_VERSION 016 // код версии прошивки +#define GL_VERSION 017 // код версии прошивки #define EE_TOUT 30000 // таймаут сохранения епром после изменения, мс //#define DEBUG_SERIAL // закомментируй чтобы выключить отладку (скорость 115200) -#define EE_KEY 52 // ключ сброса WiFi (измени для сброса всех настроек) +#define EE_KEY 55 // ключ сброса WiFi (измени для сброса всех настроек) #define NTP_UPD_PRD 5 // период обновления времени с NTP сервера, минут //#define SKIP_WIFI // пропустить подключение к вафле (для отладки) @@ -113,6 +122,7 @@ const char WiFiPassword[] = "12345678"; #include // епром #include "ESP8266httpUpdate.h" // OTA #include "mString.h" // стринг билдер +#include "Clap.h" // обработка хлопков // ------------------- ДАТА -------------------- Config cfg; @@ -126,10 +136,11 @@ NTPClient ntp(ntpUDP); CRGB leds[MAX_LEDS]; Time now; Button btn(BTN_PIN); -timerMillis EEtmr(EE_TOUT), turnoffTmr, connTmr(120000), dawnTmr; +timerMillis EEtmr(EE_TOUT), turnoffTmr, connTmr(120000ul), dawnTmr, holdPresTmr(30000ul), blinkTmr(300); TimeRandom trnd; VolAnalyzer vol(A0), low, high; FastFilter phot; +Clap clap; byte btnClicks = 0, brTicks = 0; unsigned char matrixValue[11][16]; @@ -145,7 +156,7 @@ void setup() { Serial.begin(115200); DEBUGLN(); #endif - EEPROM.begin(512); // старт епром + EEPROM.begin(1000); // старт епром startStrip(); // старт ленты btn.setLevel(digitalRead(BTN_PIN)); // смотрим что за кнопка EE_startup(); // читаем епром diff --git a/firmware/GyverLamp2/VolAnalyzer.h b/firmware/GyverLamp2/VolAnalyzer.h index 77745b4..9aafb47 100644 --- a/firmware/GyverLamp2/VolAnalyzer.h +++ b/firmware/GyverLamp2/VolAnalyzer.h @@ -49,8 +49,8 @@ class VolAnalyzer { } bool tick(int thisRead = -1) { volF.compute(); - if (millis() - tmr4 >= _ampliDt) { // период сглаживания амплитуды - tmr4 = millis(); + if (millis() - tmr3 >= _ampliDt) { // период сглаживания амплитуды + tmr3 = millis(); maxF.setRaw(maxs); minF.setRaw(mins); maxF.compute(); @@ -68,11 +68,13 @@ class VolAnalyzer { maxF.setFil(thisRead); minF.setFil(thisRead); } + if (++count >= _window) { // выборка завершена tmr1 = millis(); raw = max; if (max > maxs) maxs = max; // максимумы среди максимумов if (max < mins) mins = max; // минимумы реди максимумов + rawMax = maxs; maxF.checkPass(max); // проверка выше максимума if (getMax() - getMin() < _trsh) max = 0; // если окно громкости меньше порого то 0 else max = constrain(map(max, getMin(), getMax(), _volMin, _volMax), _volMin, _volMax); // перевод в громкость @@ -89,6 +91,9 @@ class VolAnalyzer { int getRaw() { return raw; } + int getRawMax() { + return rawMax; + } int getVol() { return volF.getFil(); } @@ -108,15 +113,16 @@ class VolAnalyzer { private: int _pin; - int _dt = 600; // 600 мкс между сэмплами достаточно для музыки - int _period = 5; // 5 мс между выборами достаточно + int _dt = 500; // 500 мкс между сэмплами достаточно для музыки + int _period = 4; // 4 мс между выборами достаточно int _ampliDt = 150; - int _window = 20; // при таком размере окна получаем длительность оцифровки 12 мс, вполне хватает - uint32_t tmr1 = 0, tmr2 = 0, tmr3 = 0, tmr4 = 0; + int _window = 20; // при таком размере окна получаем длительность оцифровки вполне хватает + uint32_t tmr1 = 0, tmr2 = 0, tmr3 = 0; int raw = 0; + int rawMax = 0; int max = 0, count = 0; int maxs = 0, mins = 1023; int _volMin = 0, _volMax = 100, _trsh = 30; - bool _pulse = 0, _first = 0; + bool _pulse = 0, _first = 0; FastFilter minF, maxF, volF; }; diff --git a/firmware/GyverLamp2/analog.ino b/firmware/GyverLamp2/analog.ino index 1b42358..1f4acf9 100644 --- a/firmware/GyverLamp2/analog.ino +++ b/firmware/GyverLamp2/analog.ino @@ -1,5 +1,6 @@ #if (USE_ADC == 1) void setupADC() { + clap.setTimeout(500); low.setDt(0); low.setPeriod(0); low.setWindow(0); @@ -32,32 +33,36 @@ void setupADC() { void checkAnalog() { - if (cfg.state) { - switch (cfg.adcMode) { - case GL_ADC_NONE: break; - case GL_ADC_BRI: checkPhot(); break; - case GL_ADC_MIC: checkMusic(); break; - case GL_ADC_BOTH: - { - static timerMillis tmr(1000, 1); - if (tmr.isReady()) { - switchToPhot(); - phot.setRaw(analogRead(A0)); - switchToMic(); - } else { - checkMusic(); - } - phot.compute(); + //if (cfg.state) { + switch (cfg.adcMode) { + case GL_ADC_NONE: break; + case GL_ADC_BRI: checkPhot(); break; + case GL_ADC_MIC: checkMusic(); break; + case GL_ADC_BOTH: + { + static timerMillis tmr(1000, 1); + if (tmr.isReady()) { + switchToPhot(); + phot.setRaw(analogRead(A0)); + switchToMic(); + } else { + checkMusic(); } - break; - } + phot.compute(); + } + break; } + //} } void checkMusic() { - if (CUR_PRES.advMode == GL_ADV_VOL) { // громкость - vol.tick(); - } else if (CUR_PRES.advMode == GL_ADV_LOW || CUR_PRES.advMode == GL_ADV_HIGH) { // частоты + vol.tick(); +#if (USE_CLAP == 1) + clap.tick(vol.getRawMax()); + if (clap.hasClaps(2)) controlHandler(!cfg.state); +#endif + + if (CUR_PRES.advMode == GL_ADV_LOW || CUR_PRES.advMode == GL_ADV_HIGH) { // частоты int raw[FFT_SIZE], spectr[FFT_SIZE]; for (int i = 0; i < FFT_SIZE; i++) raw[i] = analogRead(A0); FFT(raw, spectr); diff --git a/firmware/GyverLamp2/data.h b/firmware/GyverLamp2/data.h index 77647db..f8b3beb 100644 --- a/firmware/GyverLamp2/data.h +++ b/firmware/GyverLamp2/data.h @@ -16,7 +16,7 @@ #define GL_REACT_LEN 3 #define GL_SLAVE 0 #define GL_MASTER 1 -#define MAX_PRESETS 25 // макс количество режимов +#define MAX_PRESETS 40 // макс количество режимов // ------------------- МАКРО -------------------- #ifdef DEBUG_SERIAL @@ -61,9 +61,8 @@ struct Palette { byte strip[16 * 3]; }; -#define CFG_SIZE 13 +#define CFG_SIZE 12 struct Config { - byte GMT = 3; // часовой пояс +13 byte bright = 100; // яркость byte adcMode = 1; // режим ацп (1 выкл, 2 ярк, 3 муз) byte minBright = 0; // мин яркость @@ -79,7 +78,15 @@ struct Config { int16_t length = 16; // длина ленты int16_t width = 16; // ширина матрицы + + byte GMT = 3; // часовой пояс +13 uint32_t cityID = 1; // city ID + bool mqtt = 0; // mqtt + char mqttID[32]; // + char mqttHost[32]; // + int mqttPort = 0; // + char mqttLogin[16]; // + char mqttPass[16]; // byte state = 1; // состояние 0 выкл, 1 вкл byte group = 1; // группа девайса (1-10) diff --git a/firmware/GyverLamp2/eeprom.ino b/firmware/GyverLamp2/eeprom.ino index 48ab194..3355b4c 100644 --- a/firmware/GyverLamp2/eeprom.ino +++ b/firmware/GyverLamp2/eeprom.ino @@ -5,23 +5,23 @@ bool EEpalFlag = false; void EE_startup() { // старт епром - if (EEPROM.read(511) != EE_KEY) { - EEPROM.write(511, EE_KEY); - EEPROM.put(0, cfg); - EEPROM.put(sizeof(cfg), dawn); - EEPROM.put(sizeof(cfg) + sizeof(dawn), pal); - EEPROM.put(sizeof(cfg) + sizeof(dawn) + sizeof(pal), preset); + if (EEPROM.read(0) != EE_KEY) { + EEPROM.write(0, EE_KEY); + EEPROM.put(1, cfg); + EEPROM.put(sizeof(cfg) + 1, dawn); + EEPROM.put(sizeof(cfg) + sizeof(dawn) + 1, pal); + EEPROM.put(sizeof(cfg) + sizeof(dawn) + sizeof(pal) + 1, preset); EEPROM.commit(); blink8(CRGB::Magenta); DEBUGLN("First start"); } - EEPROM.get(0, cfg); - EEPROM.get(sizeof(cfg), dawn); - EEPROM.get(sizeof(cfg) + sizeof(dawn), pal); - EEPROM.get(sizeof(cfg) + sizeof(dawn) + sizeof(pal), preset); - + EEPROM.get(1, cfg); + EEPROM.get(sizeof(cfg) + 1, dawn); + EEPROM.get(sizeof(cfg) + sizeof(dawn) + 1, pal); + EEPROM.get(sizeof(cfg) + sizeof(dawn) + sizeof(pal) + 1, preset); + DEBUG("EEPR size: "); - DEBUGLN(sizeof(cfg) + sizeof(dawn) + sizeof(pal) + sizeof(preset)); + DEBUGLN(sizeof(cfg) + sizeof(dawn) + sizeof(pal) + sizeof(preset) + 1); // запускаем всё FastLED.setMaxPowerInVoltsAndMilliamps(STRIP_VOLT, cfg.maxCur * 100); @@ -49,24 +49,24 @@ void checkEEupdate() { if (EEcfgFlag || EEdawnFlag || EEpresetFlag) { if (EEcfgFlag) { EEcfgFlag = false; - EEPROM.put(0, cfg); + EEPROM.put(1, cfg); DEBUGLN("save cfg"); } if (EEdawnFlag) { EEdawnFlag = false; - EEPROM.put(sizeof(cfg), dawn); + EEPROM.put(sizeof(cfg) + 1, dawn); DEBUGLN("save dawn"); } if (EEpalFlag) { EEpalFlag = false; - EEPROM.put(sizeof(cfg) + sizeof(dawn), pal); + EEPROM.put(sizeof(cfg) + sizeof(dawn) + 1, pal); DEBUGLN("save pal"); } if (EEpresetFlag) { EEpresetFlag = false; - EEPROM.put(sizeof(cfg) + sizeof(dawn) + sizeof(pal), preset); + EEPROM.put(sizeof(cfg) + sizeof(dawn) + sizeof(pal) + 1, preset); DEBUGLN("save preset"); - } + } EEPROM.commit(); } EEtmr.stop(); @@ -79,6 +79,6 @@ void EE_updCfgRst() { ESP.restart(); } void EE_updCfg() { - EEPROM.put(0, cfg); + EEPROM.put(1, cfg); EEPROM.commit(); } diff --git a/firmware/GyverLamp2/effects.ino b/firmware/GyverLamp2/effects.ino index 4455cb1..fdd63e1 100644 --- a/firmware/GyverLamp2/effects.ino +++ b/firmware/GyverLamp2/effects.ino @@ -22,6 +22,7 @@ void effectsRoutine() { byte thisBright = getBright(); if (turnoffTmr.running()) thisBright = scaleFF(thisBright, 255 - turnoffTmr.getLength8()); + else if (blinkTmr.runningStop()) thisBright = scaleFF(thisBright, blinkTmr.getLength8()); if (turnoffTmr.isReady()) { turnoffTmr.stop(); setPower(0); @@ -40,12 +41,12 @@ void effectsRoutine() { if (cfg.deviceType > 1) { FOR_j(0, cfg.length) { FOR_i(0, cfg.width) { - leds[getPix(i, j)] = ColorFromPalette(paletteArr[CUR_PRES.palette - 1], - scalePal(inoise8( - i * (thisScale / 5) - cfg.width * (thisScale / 5) / 2, - j * (thisScale / 5) - cfg.length * (thisScale / 5) / 2, - (now.weekMs >> 1) * CUR_PRES.speed / 255)), - 255, LINEARBLEND); + setPix(i, j, ColorFromPalette(paletteArr[CUR_PRES.palette - 1], + scalePal(inoise8( + i * (thisScale / 5) - cfg.width * (thisScale / 5) / 2, + j * (thisScale / 5) - cfg.length * (thisScale / 5) / 2, + (now.weekMs >> 1) * CUR_PRES.speed / 255)), + 255, LINEARBLEND)); } } @@ -169,7 +170,7 @@ void effectsRoutine() { case 7: // ==================================== ОГОНЬ 2020 ==================================== FastLED.clear(); - if (cfg.deviceType > 1) { // 2D огонь + if (cfg.deviceType > 1) { // 2D огонь fire2020(CUR_PRES.scale, thisLength); } else { // 1D огонь static byte heat[MAX_LEDS]; diff --git a/firmware/GyverLamp2/fire2020.ino b/firmware/GyverLamp2/fire2020.ino index faa7f09..c40ab3c 100644 --- a/firmware/GyverLamp2/fire2020.ino +++ b/firmware/GyverLamp2/fire2020.ino @@ -31,7 +31,7 @@ void fire2020(byte scale, int len) { } 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); + leds[getPix(i, len - 1U - j)] = ColorFromPalette(paletteArr[CUR_PRES.palette - 1], scalePal(qsub8(inoise8(i * deltaValue, (j + ff_y + random8(2)) * deltaHue, ff_z), shiftHue[j])), 255U); } } diff --git a/firmware/GyverLamp2/parsing.ino b/firmware/GyverLamp2/parsing.ino index 867ab54..1d6551f 100644 --- a/firmware/GyverLamp2/parsing.ino +++ b/firmware/GyverLamp2/parsing.ino @@ -9,17 +9,15 @@ void parsing() { buf[n] = NULL; DEBUGLN(buf); // пакет вида <ключ>,<канал>,<тип>,<дата1>,<дата2>... - mString pars(buf, sizeof(buf)); - if (!pars.startsWith(GL_KEY)) return; // не наш ключ - byte keyLen = strlen(GL_KEY); + byte keyLen = strchr(buf, ',') - buf; // indexof + if (strncmp(buf, GL_KEY, keyLen)) return; // не наш ключ byte data[MAX_PRESETS * PRES_SIZE + 5]; - memset(data, 0, MAX_PRESETS * PRES_SIZE + keyLen); + memset(data, 0, MAX_PRESETS * PRES_SIZE + 5); int count = 0; char *str, *p = buf + keyLen; // сдвиг до даты char *ssid, *pass; - uint32_t city = 0; - uint16_t stripL, stripW; + while ((str = strtok_r(p, ",", &p)) != NULL) { uint32_t thisInt = atoi(str); data[count++] = (byte)thisInt; @@ -28,9 +26,16 @@ void parsing() { if (count == 5) pass = str; } if (data[1] == 1) { - if (count == 16) stripL = thisInt; - if (count == 17) stripW = thisInt; - if (count == 18) city = thisInt; + if (count == 15) cfg.length = thisInt; + if (count == 16) cfg.width = thisInt; + if (count == 17) cfg.GMT = byte(thisInt); + if (count == 18) cfg.cityID = thisInt; + if (count == 19) cfg.mqtt = byte(thisInt); + if (count == 20) strcpy(cfg.mqttID, str); + if (count == 21) strcpy(cfg.mqttHost, str); + if (count == 22) cfg.mqttPort = thisInt; + if (count == 23) strcpy(cfg.mqttLogin, str); + if (count == 24) strcpy(cfg.mqttPass, str); } } @@ -46,7 +51,7 @@ void parsing() { if (data[0] != cfg.group) return; // не наш адрес, выходим switch (data[1]) { // тип 0 - control, 1 - config, 2 - effects, 3 - dawn, 4 - from master, 5 - palette - case 0: DEBUGLN("Control"); + case 0: DEBUGLN("Control"); blinkTmr.restart(); switch (data[2]) { case 0: controlHandler(0); break; // выкл case 1: controlHandler(1); break; // вкл @@ -81,24 +86,21 @@ void parsing() { case 13: // выключить через if (data[3] == 0) turnoffTmr.stop(); else { - turnoffTmr.setInterval((uint32_t)data[3] * 60000ul); - turnoffTmr.restart(); + fadeDown((uint32_t)data[3] * 60000ul); } break; } EE_updCfg(); break; - case 1: DEBUGLN("Config"); + case 1: DEBUGLN("Config"); blinkTmr.restart(); FOR_i(0, CFG_SIZE) { *((byte*)&cfg + i) = data[i + 2]; // загоняем в структуру } - cfg.length = stripL; - cfg.width = stripW; - cfg.cityID = city; - - if (cfg.length > MAX_LEDS) cfg.length = MAX_LEDS; - if (cfg.deviceType == GL_TYPE_STRIP) cfg.width = 1; + if (cfg.deviceType == GL_TYPE_STRIP) { + if (cfg.length > MAX_LEDS) cfg.length = MAX_LEDS; + cfg.width = 1; + } if (cfg.length * cfg.width > MAX_LEDS) cfg.width = MAX_LEDS / cfg.length; ntp.setTimeOffset((cfg.GMT - 13) * 3600); FastLED.setMaxPowerInVoltsAndMilliamps(STRIP_VOLT, cfg.maxCur * 100); @@ -115,13 +117,15 @@ void parsing() { *((byte*)&preset + j * PRES_SIZE + i) = data[j * PRES_SIZE + i + 3]; // загоняем в структуру } } - if (!cfg.rotation) setPreset(data[cfg.presetAmount * PRES_SIZE + 3] - 1); + //if (!cfg.rotation) setPreset(data[cfg.presetAmount * PRES_SIZE + 3] - 1); + setPreset(data[cfg.presetAmount * PRES_SIZE + 3] - 1); EE_updatePreset(); - presetRotation(true); // форсировать смену режима + //presetRotation(true); // форсировать смену режима + holdPresTmr.restart(); loading = true; break; - case 3: DEBUGLN("Dawn"); + case 3: DEBUGLN("Dawn"); blinkTmr.restart(); FOR_i(0, (2 + 3 * 7)) { *((byte*)&dawn + i) = data[i + 2]; // загоняем в структуру } @@ -131,7 +135,7 @@ void parsing() { case 4: DEBUGLN("From master"); if (cfg.role == GL_SLAVE) { switch (data[2]) { - case 0: setPower(data[3]); break; // вкл выкл + case 0: fade(data[3]); break; // вкл выкл case 1: setPreset(data[3]); break; // пресет case 2: cfg.bright = data[3]; break; // яркость } @@ -139,7 +143,7 @@ void parsing() { } break; - case 5: DEBUGLN("Palette"); + case 5: DEBUGLN("Palette"); blinkTmr.restart(); FOR_i(0, 1 + 16 * 3) { *((byte*)&pal + i) = data[i + 2]; // загоняем в структуру } @@ -147,7 +151,7 @@ void parsing() { EE_updatePal(); break; - case 6: DEBUGLN("Time"); + case 6: DEBUGLN("Time"); blinkTmr.restart(); if (!cfg.WiFimode) { // если мы AP now.day = data[2]; now.hour = data[3]; diff --git a/firmware/GyverLamp2/presetManager.ino b/firmware/GyverLamp2/presetManager.ino index 1135b2c..183bfab 100644 --- a/firmware/GyverLamp2/presetManager.ino +++ b/firmware/GyverLamp2/presetManager.ino @@ -1,5 +1,6 @@ void presetRotation(bool force) { - if (cfg.rotation && (now.newMin() || force)) { // если автосмена и новая минута + if (holdPresTmr.runningStop()) return; + if (cfg.rotation && (now.newMin() || force)) { // если автосмена и новая минута if (cfg.rotRnd) { // случайная cfg.curPreset = trnd.fromMin(cfg.rotPeriod, cfg.presetAmount); DEBUG("Rnd changed to "); @@ -17,35 +18,46 @@ void changePreset(int dir) { cfg.curPreset += dir; if (cfg.curPreset >= cfg.presetAmount) cfg.curPreset = 0; if (cfg.curPreset < 0) cfg.curPreset = cfg.presetAmount - 1; + holdPresTmr.restart(); DEBUG("Preset changed to "); DEBUGLN(cfg.curPreset); } } void setPreset(byte pres) { - if (!cfg.rotation) { // ручная смена - cfg.curPreset = constrain(pres, 0, cfg.presetAmount - 1); - DEBUG("Preset set to "); - DEBUGLN(cfg.curPreset); - } + //if (!cfg.rotation) { // ручная смена + cfg.curPreset = constrain(pres, 0, cfg.presetAmount - 1); + holdPresTmr.restart(); + DEBUG("Preset set to "); + DEBUGLN(cfg.curPreset); + //} } void controlHandler(bool state) { if (turnoffTmr.running()) { turnoffTmr.stop(); + delay(50); FastLED.clear(); + FastLED.show(); DEBUGLN("stop off timer"); return; } if (dawnTmr.running()) { dawnTmr.stop(); + delay(50); FastLED.clear(); + FastLED.show(); DEBUGLN("stop dawn timer"); return; - } + } if (state) cfg.manualOff = 0; if (cfg.state && !state) cfg.manualOff = 1; - setPower(state); + fade(state); +} + +void fade(bool state) { + if (cfg.state && !state) fadeDown(600); + else setPower(state); } void setPower(bool state) { @@ -58,3 +70,8 @@ void setPower(bool state) { sendToSlaves(0, cfg.state); DEBUGLN(state ? "Power on" : "Power off"); } + +void fadeDown(uint32_t time) { + turnoffTmr.setInterval(time); + turnoffTmr.restart(); +} diff --git a/firmware/GyverLamp2/time.ino b/firmware/GyverLamp2/time.ino index a0ab478..fc27a6c 100644 --- a/firmware/GyverLamp2/time.ino +++ b/firmware/GyverLamp2/time.ino @@ -5,7 +5,10 @@ void setupTime() { if (cfg.WiFimode) { // если подключены - запрашиваем время с сервера ntp.begin(); - if (ntp.update() && !gotNTP) gotNTP = true; + if (ntp.update() && !gotNTP) { + gotNTP = true; + DEBUGLN("Got ntp"); + } } } @@ -31,11 +34,11 @@ void updateTime() { if (now.min % NTP_UPD_PRD == 0 && now.sec == 0) { // берём время с интернета каждую NTP_UPD_PRD минуту, ставим флаг что данные с NTP получены, значит мы онлайн if (ntp.update() && !gotNTP) gotNTP = true; - } - checkDawn(); + } } else { // если нет now.tick(); // тикаем своим счётчиком } + if (gotNTP || gotTime) checkDawn(); } void sendTimeToSlaves() { @@ -53,9 +56,11 @@ void checkDawn() { int dawnMinute = dawn.hour[now.day] * 60 + dawn.minute[now.day] - dawn.time; if (dawnMinute < 0) dawnMinute += 1440; if (dawnMinute == now.hour * 60 + now.min) { - DEBUGLN("dawn start"); + DEBUG("dawn start "); + DEBUGLN(dawn.time * 60000ul); dawnTmr.setInterval(dawn.time * 60000ul); dawnTmr.restart(); + FastLED.setBrightness(255); } } } @@ -65,8 +70,8 @@ void checkWorkTime() { byte curState = isWorkTime(now.hour, cfg.workFrom, cfg.workTo); if (prevState != curState) { // переключение расписания prevState = curState; - if (curState && !cfg.state && !cfg.manualOff) setPower(1); // нужно включить, а лампа выключена и не выключалась вручную - if (!curState && cfg.state) setPower(0); // нужно выключить, а лампа включена + if (curState && !cfg.state && !cfg.manualOff) fade(1); // нужно включить, а лампа выключена и не выключалась вручную + if (!curState && cfg.state) fade(0); // нужно выключить, а лампа включена } } diff --git a/firmware/GyverLamp2/timerMillis.h b/firmware/GyverLamp2/timerMillis.h index 4879f9d..71f67f5 100644 --- a/firmware/GyverLamp2/timerMillis.h +++ b/firmware/GyverLamp2/timerMillis.h @@ -17,6 +17,10 @@ class timerMillis { } return false; } + boolean runningStop() { + if (_active && millis() - _tmr >= _interval) stop(); + return _active; + } void reset() { _tmr = millis(); } @@ -31,7 +35,7 @@ class timerMillis { return _active; } byte getLength8() { - return (millis() - _tmr) * 255ul / _interval; + return (_active) ? ((millis() - _tmr) * 255ul / _interval) : 0; } private: