diff --git a/firmware/GyverLamp2/0_func.ino b/firmware/GyverLamp2/0_func.ino index 97cf8e4..e52fe1e 100644 --- a/firmware/GyverLamp2/0_func.ino +++ b/firmware/GyverLamp2/0_func.ino @@ -1,19 +1,29 @@ void sendUDP(char *data) { - IPAddress ip = WiFi.localIP(); - ip[3] = 255; - Udp.beginPacket(ip, 50000 + cfg.group); + Udp.beginPacket(deviceIP, portNum + cfg.group); Udp.write(data); Udp.endPacket(); } -void restartUDP() { - DEBUG("UDP port: "); - DEBUGLN(50000 + cfg.group); - Udp.stop(); - Udp.begin(50000 + cfg.group); +void sendUDP(byte cmd, int data1 = 0, int data2 = 0, int data3 = 0) { + char reply[20] = ""; + mString packet(reply, sizeof(reply)); + packet = packet + "GL," + cmd + ',' + data1 + ',' + data2 + ',' + data3; + sendUDP(reply); + //DEBUG("Sending: "); + //DEBUGLN(cmd); } -void blink8(CRGB color) { + +void restartUDP() { + Udp.stop(); + Udp.begin(portNum + cfg.group); + deviceIP = WiFi.localIP(); + deviceIP[3] = 255; + DEBUG("UDP port: "); + DEBUGLN(portNum + cfg.group); +} + +void blink16(CRGB color) { FOR_i(0, 3) { - fill_solid(leds, 8, color); + fill_solid(leds, 16, color); FastLED.show(); delay(300); FastLED.clear(); @@ -21,3 +31,50 @@ void blink8(CRGB color) { delay(300); } } + +const uint8_t font5x7[][5] = { + {0x3e, 0x51, 0x49, 0x45, 0x3e}, // 0 0x30 48 + {0x00, 0x42, 0x7f, 0x40, 0x00}, // 1 0x31 49 + {0x42, 0x61, 0x51, 0x49, 0x46}, // 2 0x32 50 + {0x21, 0x41, 0x45, 0x4b, 0x31}, // 3 0x33 51 + {0x18, 0x14, 0x12, 0x7f, 0x10}, // 4 0x34 52 + {0x27, 0x45, 0x45, 0x45, 0x39}, // 5 0x35 53 + {0x3c, 0x4a, 0x49, 0x49, 0x30}, // 6 0x36 54 + {0x01, 0x71, 0x09, 0x05, 0x03}, // 7 0x37 55 + {0x36, 0x49, 0x49, 0x49, 0x36}, // 8 0x38 56 + {0x06, 0x49, 0x49, 0x29, 0x1e}, // 9 0x39 57 + {0x00, 0x08, 0x08, 0x08, 0x00}, // 10 - + {0x00, 0x00, 0x00, 0x00, 0x00}, // 11 empty +}; + +void drawDigit(byte digit, int X, int Y, CRGB color) { + FOR_i(0, 5) { + FOR_j(0, 7) { + if (font5x7[digit][i] & (1 << 6 - j)) setPix(i + X, j + Y, color); + } + } +} +void drawDots(int X, int Y, CRGB color) { + setPix(X, Y + 2, color); + setPix(X, Y + 4, color); +} + +void drawClock(byte Y, byte speed, CRGB color) { + if (cfg.deviceType == 1 || cfg.width < 16) return; // лента или мелкая матрица - на выход + byte h1, h2, m1, m2; + if (gotNTP || gotTime) { + h1 = now.hour / 10; + if (h1 == 0) h1 = 11; + h2 = now.hour % 10; + m1 = now.min / 10; + m2 = now.min % 10; + } else { + h1 = h2 = m1 = m2 = 10; + } + int pos = cfg.width - (now.weekMs / (speed * 2)) % (cfg.width + 26); + drawDigit(h1, pos, Y, color); + drawDigit(h2, pos + 6, Y, color); + if (now.getMs() < 500) drawDots(pos + 12, Y, color); + drawDigit(m1, pos + 14, Y, color); + drawDigit(m2, pos + 20, Y, color); +} diff --git a/firmware/GyverLamp2/FastFilter.h b/firmware/GyverLamp2/FastFilter.h index efbcd7e..8ad4f4d 100644 --- a/firmware/GyverLamp2/FastFilter.h +++ b/firmware/GyverLamp2/FastFilter.h @@ -8,10 +8,10 @@ class FastFilter { public: - FastFilter(byte k = 20, int dt = 0) { - setK(k); - setDt(dt); - } + 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 67b08ff..c7edf09 100644 --- a/firmware/GyverLamp2/GyverLamp2.ino +++ b/firmware/GyverLamp2/GyverLamp2.ino @@ -1,4 +1,14 @@ /* + Версия 0.19b + Минимальная версия приложения 1.17!!! + Почищен мусор, оптимизация, повышена стабильность и производительность + Мигает теперь 16 светиков + Снова переделана сетевая политика, упрощён и сильно ускорен парсинг + Изменены пределы по светодиодам, что сильно увеличило производительность + Выключенная (программно) лампа не принимает сервисные команды кроме команды включиться + Добавлены часы, в том числе в рассвет + Slave работает со светомузыкой сам, если не получает данные с мастера + Версия 0.18b Уменьшена чувствительность хлопков Увеличена плавность светомузыки @@ -48,9 +58,7 @@ плавная смена режимов Mqtt? Базовый пак - Предложения Серёги крутского Эффект погода https://it4it.club/topic/40-esp8266-i-parsing-pogodyi-s-openweathermap/ - Эффект часы */ // ВНИМАНИЕ! ВНИМАНИЕ! ВНИМАНИЕ! ВНИМАНИЕ! ВНИМАНИЕ! ВНИМАНИЕ! ВНИМАНИЕ! @@ -76,7 +84,7 @@ // ------------ Лента ------------- #define STRIP_PIN 2 // пин ленты GPIO2 (D4 на wemos/node) -#define MAX_LEDS 600 // макс. светодиодов +#define MAX_LEDS 300 // макс. светодиодов #define STRIP_CHIP WS2812 // чип ленты #define STRIP_COLOR GRB // порядок цветов в ленте #define STRIP_VOLT 5 // напряжение ленты, V @@ -93,15 +101,15 @@ const char AP_NameChar[] = "GyverLamp2"; const char WiFiPassword[] = "12345678"; // ------------ Прочее ------------- -#define GL_VERSION 18 // код версии прошивки +#define GL_VERSION 19 // код версии прошивки #define EE_TOUT 30000 // таймаут сохранения епром после изменения, мс -//#define DEBUG_SERIAL // закомментируй чтобы выключить отладку (скорость 115200) +#define DEBUG_SERIAL // закомментируй чтобы выключить отладку (скорость 115200) #define EE_KEY 55 // ключ сброса WiFi (измени для сброса всех настроек) #define NTP_UPD_PRD 5 // период обновления времени с NTP сервера, минут //#define SKIP_WIFI // пропустить подключение к вафле (для отладки) // ------------ БИЛДЕР ------------- -//#define MAX_LEDS 1200 +//#define MAX_LEDS 900 // esp01 //#define BTN_PIN 0 @@ -139,33 +147,35 @@ Palette pal; WiFiServer server(80); WiFiUDP Udp; WiFiUDP ntpUDP; +IPAddress deviceIP; NTPClient ntp(ntpUDP); CRGB leds[MAX_LEDS]; Time now; Button btn(BTN_PIN); timerMillis EEtmr(EE_TOUT), turnoffTmr, connTmr(120000ul), dawnTmr, holdPresTmr(30000ul), blinkTmr(300); +timerMillis effTmr(30, true); TimeRandom trnd; VolAnalyzer vol(A0), low, high; FastFilter phot; Clap clap; +uint16_t portNum; +uint32_t udpTmr = 0, gotADCtmr = 0; byte btnClicks = 0, brTicks = 0; unsigned char matrixValue[11][16]; bool gotNTP = false, gotTime = false; bool loading = true; -int udpLength = 0, udpWidth = 0; +int udpLength = 0; byte udpScale = 0, udpBright = 0; - // ------------------- SETUP -------------------- void setup() { + misc(); delay(2000); // ждём старта есп - memset(matrixValue, 0, sizeof(matrixValue)); #ifdef DEBUG_SERIAL Serial.begin(115200); DEBUGLN(); #endif - EEPROM.begin(1000); // старт епром startStrip(); // старт ленты btn.setLevel(digitalRead(BTN_PIN)); // смотрим что за кнопка EE_startup(); // читаем епром @@ -183,6 +193,7 @@ void setup() { void loop() { timeTicker(); // обновляем время + yield(); #ifndef SKIP_WIFI tryReconnect(); // пробуем переподключиться если WiFi упал yield(); @@ -195,4 +206,5 @@ void loop() { yield(); button(); // проверяем кнопку checkAnalog(); // чтение звука и датчика + yield(); } diff --git a/firmware/GyverLamp2/NTPClient-Gyver.cpp b/firmware/GyverLamp2/NTPClient-Gyver.cpp index 789699c..4c8a6f4 100644 --- a/firmware/GyverLamp2/NTPClient-Gyver.cpp +++ b/firmware/GyverLamp2/NTPClient-Gyver.cpp @@ -1,23 +1,23 @@ /** - * The MIT License (MIT) - * Copyright (c) 2015 by Fabrice Weinberg - * - * 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. - */ + The MIT License (MIT) + Copyright (c) 2015 by Fabrice Weinberg + + 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. +*/ #include "NTPClient-Gyver.h" @@ -61,9 +61,9 @@ void NTPClient::begin(int port) { } bool NTPClient::forceUpdate() { - #ifdef DEBUG_NTPClient - Serial.println("Update from NTP Server"); - #endif +#ifdef DEBUG_NTPClient + Serial.println("Update from NTP Server"); +#endif this->sendNTPPacket(); @@ -83,12 +83,12 @@ bool NTPClient::forceUpdate() { unsigned long highWord = word(this->_packetBuffer[40], this->_packetBuffer[41]); unsigned long lowWord = word(this->_packetBuffer[42], this->_packetBuffer[43]); - + /// добавлено AlexGyver uint32_t frac = (uint32_t) _packetBuffer[44] << 24 - | (uint32_t) _packetBuffer[45] << 16 - | (uint32_t) _packetBuffer[46] << 8 - | (uint32_t) _packetBuffer[47] << 0; + | (uint32_t) _packetBuffer[45] << 16 + | (uint32_t) _packetBuffer[46] << 8 + | (uint32_t) _packetBuffer[47] << 0; uint16_t mssec = ((uint64_t) frac * 1000) >> 32; //https://arduino.stackexchange.com/questions/49567/synching-local-clock-usign-ntp-to-milliseconds _lastUpdate -= mssec; @@ -105,7 +105,7 @@ bool NTPClient::forceUpdate() { bool NTPClient::update() { if ((millis() - this->_lastUpdate >= this->_updateInterval) // Update after _updateInterval - || this->_lastUpdate == 0) { // Update if there was no update yet. + || this->_lastUpdate == 0) { // Update if there was no update yet. if (!this->_udpSetup) this->begin(); // setup the UDP client if needed return this->forceUpdate(); } @@ -166,7 +166,7 @@ void NTPClient::setUpdateInterval(unsigned long updateInterval) { } void NTPClient::setPoolServerName(const char* poolServerName) { - this->_poolServerName = poolServerName; + this->_poolServerName = poolServerName; } void NTPClient::sendNTPPacket() { diff --git a/firmware/GyverLamp2/NTPClient-Gyver.h b/firmware/GyverLamp2/NTPClient-Gyver.h index 3b0bcfd..77809fa 100644 --- a/firmware/GyverLamp2/NTPClient-Gyver.h +++ b/firmware/GyverLamp2/NTPClient-Gyver.h @@ -36,67 +36,67 @@ class NTPClient { NTPClient(UDP& udp, const char* poolServerName, long timeOffset, unsigned long updateInterval); /** - * Set time server name - * - * @param poolServerName - */ + Set time server name + + @param poolServerName + */ void setPoolServerName(const char* poolServerName); /** - * Starts the underlying UDP client with the default local port - */ + Starts the underlying UDP client with the default local port + */ void begin(); /** - * Starts the underlying UDP client with the specified local port - */ + Starts the underlying UDP client with the specified local port + */ void begin(int port); /** - * This should be called in the main loop of your application. By default an update from the NTP Server is only - * made every 60 seconds. This can be configured in the NTPClient constructor. - * - * @return true on success, false on failure - */ + This should be called in the main loop of your application. By default an update from the NTP Server is only + made every 60 seconds. This can be configured in the NTPClient constructor. + + @return true on success, false on failure + */ bool update(); /** - * This will force the update from the NTP Server. - * - * @return true on success, false on failure - */ + This will force the update from the NTP Server. + + @return true on success, false on failure + */ bool forceUpdate(); int getDay() const; int getHours() const; int getMinutes() const; int getSeconds() const; - int getMillis() const; - int getMillisLastUpd() const; + int getMillis() const; + int getMillisLastUpd() const; /** - * Changes the time offset. Useful for changing timezones dynamically - */ + Changes the time offset. Useful for changing timezones dynamically + */ void setTimeOffset(int timeOffset); /** - * Set the update interval to another frequency. E.g. useful when the - * timeOffset should not be set in the constructor - */ + Set the update interval to another frequency. E.g. useful when the + timeOffset should not be set in the constructor + */ void setUpdateInterval(unsigned long updateInterval); /** - * @return time formatted like `hh:mm:ss` - */ + @return time formatted like `hh:mm:ss` + */ String getFormattedTime() const; /** - * @return time in seconds since Jan. 1, 1970 - */ + @return time in seconds since Jan. 1, 1970 + */ unsigned long getEpochTime() const; /** - * Stops the underlying UDP client - */ + Stops the underlying UDP client + */ void end(); }; diff --git a/firmware/GyverLamp2/Time.h b/firmware/GyverLamp2/Time.h index 7b759ee..d49bb74 100644 --- a/firmware/GyverLamp2/Time.h +++ b/firmware/GyverLamp2/Time.h @@ -9,7 +9,7 @@ class Time { uint32_t weekS = 0; int getMs() { - return (millis() - tmr); + return (tmr - millis()); } void setMs(int ms) { tmr = millis() + ms; diff --git a/firmware/GyverLamp2/VolAnalyzer.h b/firmware/GyverLamp2/VolAnalyzer.h index 9aafb47..ccedc62 100644 --- a/firmware/GyverLamp2/VolAnalyzer.h +++ b/firmware/GyverLamp2/VolAnalyzer.h @@ -123,6 +123,6 @@ class VolAnalyzer { 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 5610982..f693119 100644 --- a/firmware/GyverLamp2/analog.ino +++ b/firmware/GyverLamp2/analog.ino @@ -3,6 +3,10 @@ void setupADC() { clap.setTimeout(500); clap.setTrsh(250); + vol.setDt(700); + vol.setPeriod(5); + vol.setWindow(map(MAX_LEDS, 300, 1200, 20, 1)); + low.setDt(0); low.setPeriod(0); low.setWindow(0); @@ -35,7 +39,7 @@ void setupADC() { void checkAnalog() { - if (cfg.role) { + if (cfg.role || millis() - gotADCtmr >= 2000) { // только мастер или слейв по таймауту опрашивает АЦП! switch (cfg.adcMode) { case GL_ADC_NONE: break; case GL_ADC_BRI: checkPhot(); break; @@ -63,10 +67,11 @@ void checkMusic() { clap.tick(vol.getRawMax()); if (clap.hasClaps(2)) controlHandler(!cfg.state); #endif - + yield(); 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); + yield(); FFT(raw, spectr); int low_raw = 0; int high_raw = 0; diff --git a/firmware/GyverLamp2/button.ino b/firmware/GyverLamp2/button.ino index fe5528a..de3ca86 100644 --- a/firmware/GyverLamp2/button.ino +++ b/firmware/GyverLamp2/button.ino @@ -33,11 +33,11 @@ void button() { break; case 5: cfg.role = 0; - blink8(CRGB::DarkSlateBlue); + blink16(CRGB::DarkSlateBlue); break; case 6: cfg.role = 1; - blink8(CRGB::Maroon); + blink16(CRGB::Maroon); break; } EE_updateCfg(); diff --git a/firmware/GyverLamp2/data.h b/firmware/GyverLamp2/data.h index 3131564..fb10aaf 100644 --- a/firmware/GyverLamp2/data.h +++ b/firmware/GyverLamp2/data.h @@ -41,12 +41,12 @@ int mapFF(byte x, byte min, byte max) { const char OTAhost[] = "http://ota.alexgyver.ru/"; const char *OTAfile[] = { "GL2_latest.bin", - "com_600.bin", - "com_1200.bin", - "esp1_600.bin", - "esp1_1200.bin", - "module_600.bin", - "module_1200.bin", + "com_300.bin", + "com_900.bin", + "esp1_300.bin", + "esp1_900.bin", + "module_300.bin", + "module_900.bin", }; const char NTPserver[] = "pool.ntp.org"; @@ -129,3 +129,9 @@ struct Dawn { byte bright = 100; // (0.. 255) byte time = 1; // (5,10,15,20..) }; + +/* + - Каждые 5 минут лампа AP отправляет время (день час минута) на Local лампы всех ролей в сети с ней (GL,6,день,час,мин) + - Если включен АЦП, Мастер отправляет своей группе данные с него на каждой итерации отрисовки эффектов (GL,1,длина,масштаб,яркость) + - Установка времени с мобилы - получают все роли АР и Local (не получившие ntp) +*/ diff --git a/firmware/GyverLamp2/eeprom.ino b/firmware/GyverLamp2/eeprom.ino index 3355b4c..11b0cd7 100644 --- a/firmware/GyverLamp2/eeprom.ino +++ b/firmware/GyverLamp2/eeprom.ino @@ -5,6 +5,8 @@ bool EEpalFlag = false; void EE_startup() { // старт епром + EEPROM.begin(1000); // старт епром + delay(100); if (EEPROM.read(0) != EE_KEY) { EEPROM.write(0, EE_KEY); EEPROM.put(1, cfg); @@ -12,7 +14,7 @@ void EE_startup() { EEPROM.put(sizeof(cfg) + sizeof(dawn) + 1, pal); EEPROM.put(sizeof(cfg) + sizeof(dawn) + sizeof(pal) + 1, preset); EEPROM.commit(); - blink8(CRGB::Magenta); + blink16(CRGB::Magenta); DEBUGLN("First start"); } EEPROM.get(1, cfg); diff --git a/firmware/GyverLamp2/effects.ino b/firmware/GyverLamp2/effects.ino index dfa58cc..85e6cce 100644 --- a/firmware/GyverLamp2/effects.ino +++ b/firmware/GyverLamp2/effects.ino @@ -1,12 +1,11 @@ void effectsRoutine() { - static timerMillis effTmr(30, true); static byte prevEff = 255; + if (!effTmr.isReady()) return; if (dawnTmr.running()) { - if (effTmr.isReady()) { - fill_solid(leds, MAX_LEDS, ColorFromPalette(HeatColors_p, dawnTmr.getLength8(), scaleFF(dawnTmr.getLength8(), dawn.bright), LINEARBLEND)); - FastLED.show(); - } + fill_solid(leds, MAX_LEDS, ColorFromPalette(HeatColors_p, dawnTmr.getLength8(), scaleFF(dawnTmr.getLength8(), dawn.bright), LINEARBLEND)); + drawClock(cfg.length / 2 - 4, 150, 0); + FastLED.show(); if (dawnTmr.isReady()) { dawnTmr.stop(); FastLED.clear(); @@ -15,230 +14,225 @@ void effectsRoutine() { return; } - if (cfg.state && effTmr.isReady()) { - int thisLength, thisWidth; - byte thisScale, thisBright; + if (!cfg.state) return; + int thisLength = getLength(); + byte thisScale = getScale(); + byte thisBright = getBright(); - if (cfg.adcMode > 1) { // музыка или яркость - if (cfg.role) { - thisLength = getLength(); - thisScale = getScale(); - thisWidth = (cfg.deviceType > 1) ? cfg.width : 1; - thisBright = getBright(); - - char reply[25]; - mString packet(reply, sizeof(reply)); - packet.clear(); - packet = packet + GL_KEY + ",7," + thisLength + ',' + thisScale + ',' + thisWidth + ',' + thisBright; - sendUDP(reply); - } else { + if (cfg.adcMode > 1) { // музыка или яркость + if (cfg.role) { // мастер отправляет + static timerMillis adcSend(120, true); + if (adcSend.isReady() && millis() - udpTmr >= 1000) sendUDP(7, thisLength, thisScale, thisBright); + } else { // слейв получает + if (millis() - gotADCtmr < 2000) { // есть сигнал с мастера thisLength = udpLength; thisScale = udpScale; - thisWidth = udpWidth; thisBright = udpBright; } - } else { // нет - thisLength = getLength(); - thisScale = getScale(); - thisWidth = (cfg.deviceType > 1) ? cfg.width : 1; - 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); - return; - } - FastLED.setBrightness(thisBright); - - if (prevEff != CUR_PRES.effect) { - FastLED.clear(); - prevEff = CUR_PRES.effect; - loading = true; - } - // =================================================== ЭФФЕКТЫ =================================================== - switch (CUR_PRES.effect) { - case 1: // =================================== ПЕРЛИН =================================== - if (cfg.deviceType > 1) { - FOR_j(0, cfg.length) { - FOR_i(0, cfg.width) { - 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)); - } - } - - } else { - FOR_i(0, cfg.length) { - leds[i] = ColorFromPalette(paletteArr[CUR_PRES.palette - 1], - scalePal(inoise8(i * (thisScale / 5) - cfg.length * (thisScale / 5) / 2, - (now.weekMs >> 1) * CUR_PRES.speed / 255)), - 255, LINEARBLEND); - } - } - break; - - case 2: // ==================================== ЦВЕТ ==================================== - { - 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); - } else { - fillStrip(0, thisLength, thisColor); - } - } - break; - - case 3: // ================================= СМЕНА ЦВЕТА ================================= - { - 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), 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); - } else { - fillStrip(0, thisLength, thisColor); - } - } - break; - - case 4: // ================================== ГРАДИЕНТ ================================== - 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) ? (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)), - bright, LINEARBLEND); - if (cfg.deviceType > 1) fillRow(i, thisColor); - else leds[i] = thisColor; - } - if (cfg.deviceType > 1) FOR_i(0, cfg.length / 2) fillRow(i, leds[(cfg.length - i)*cfg.width - 1]); - else FOR_i(0, cfg.length / 2) leds[i] = leds[cfg.length - i - 1]; - - } else { - FOR_i(0, cfg.length) { - byte bright = 255; - 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)), - bright, LINEARBLEND); - if (cfg.deviceType > 1) fillRow(i, thisColor); - else leds[i] = thisColor; - } - } - break; - - case 5: // =================================== ЧАСТИЦЫ =================================== - FOR_i(0, cfg.length * cfg.width) leds[i].fadeToBlackBy(70); - { - uint16_t rndVal = 0; - 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, 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, 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; - setPix(thisX, thisY, CHSV(CUR_PRES.rnd ? rndVal : CUR_PRES.color, 255, 255)); - } else { - setLED(thisX, 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 огонь - 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)); - } - } - break; - - 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); - } - FOR_i(0, cfg.length * cfg.width) { - if (leds[i].r >= 10 || leds[i].g >= 10 || leds[i].b >= 10) leds[i].fadeToBlackBy(CUR_PRES.speed / 2 + 1); - else leds[i] = 0; - } - break; - - case 9: // ================================== ПОГОДА ================================== - - break; - } - - // выводим нажатия кнопки - if (btnClicks > 0) fill_solid(leds, btnClicks, CRGB::White); - if (brTicks > 0) fill_solid(leds, brTicks, CRGB::Cyan); - FastLED.show(); } + + 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); + return; + } + FastLED.setBrightness(thisBright); + + if (prevEff != CUR_PRES.effect) { // смена эффекта + FastLED.clear(); + prevEff = CUR_PRES.effect; + loading = true; + } + + // =================================================== ЭФФЕКТЫ =================================================== + switch (CUR_PRES.effect) { + case 1: // =================================== ПЕРЛИН =================================== + if (cfg.deviceType > 1) { + FOR_j(0, cfg.length) { + FOR_i(0, cfg.width) { + 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)); + } + } + + } else { + FOR_i(0, cfg.length) { + leds[i] = ColorFromPalette(paletteArr[CUR_PRES.palette - 1], + scalePal(inoise8(i * (thisScale / 5) - cfg.length * (thisScale / 5) / 2, + (now.weekMs >> 1) * CUR_PRES.speed / 255)), + 255, LINEARBLEND); + } + } + break; + + case 2: // ==================================== ЦВЕТ ==================================== + { + fill_solid(leds, cfg.length * cfg.width, 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); + } else { + fillStrip(0, thisLength, thisColor); + } + } + break; + + case 3: // ================================= СМЕНА ЦВЕТА ================================= + { + CRGB thisColor = ColorFromPalette(paletteArr[CUR_PRES.palette - 1], scalePal((now.weekMs >> 5) * CUR_PRES.speed / 255), 10, LINEARBLEND); + fill_solid(leds, cfg.length * cfg.width, thisColor); + 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); + } else { + fillStrip(0, thisLength, thisColor); + } + } + break; + + case 4: // ================================== ГРАДИЕНТ ================================== + 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) ? (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)), + bright, LINEARBLEND); + if (cfg.deviceType > 1) fillRow(i, thisColor); + else leds[i] = thisColor; + } + if (cfg.deviceType > 1) FOR_i(0, cfg.length / 2) fillRow(i, leds[(cfg.length - i)*cfg.width - 1]); + else FOR_i(0, cfg.length / 2) leds[i] = leds[cfg.length - i - 1]; + + } else { + FOR_i(0, cfg.length) { + byte bright = 255; + 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)), + bright, LINEARBLEND); + if (cfg.deviceType > 1) fillRow(i, thisColor); + else leds[i] = thisColor; + } + } + break; + + case 5: // =================================== ЧАСТИЦЫ =================================== + FOR_i(0, cfg.length * cfg.width) leds[i].fadeToBlackBy(70); + { + uint16_t rndVal = 0; + 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, 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, 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; + setPix(thisX, thisY, CHSV(CUR_PRES.rnd ? rndVal : CUR_PRES.color, 255, 255)); + } else { + setLED(thisX, 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 огонь + 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)); + } + } + break; + + 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); + } + FOR_i(0, cfg.length * cfg.width) { + if (leds[i].r >= 10 || leds[i].g >= 10 || leds[i].b >= 10) leds[i].fadeToBlackBy(CUR_PRES.speed / 2 + 1); + else leds[i] = 0; + } + break; + + case 9: // =================================== ЧАСЫ =================================== + FastLED.clear(); + drawClock(mapFF(CUR_PRES.scale, 0, cfg.length - 7), (255 - CUR_PRES.speed), CHSV(CUR_PRES.color, 255, 255)); + break; + + case 10: // ================================= ПОГОДА ================================== + + break; + } + + if (CUR_PRES.advMode == GL_ADV_CLOCK && CUR_PRES.effect != 9) drawClock(mapFF(CUR_PRES.scale, 0, cfg.length - 7), 150, 0); + // выводим нажатия кнопки + if (btnClicks > 0) fill_solid(leds, btnClicks, CRGB::White); + if (brTicks > 0) fill_solid(leds, brTicks, CRGB::Cyan); + yield(); + FastLED.show(); } // ==================================================================================================================== - bool musicMode() { return ((cfg.adcMode == GL_ADC_MIC || cfg.adcMode == GL_ADC_BOTH) && (CUR_PRES.advMode > 1 && CUR_PRES.advMode <= 4)); } diff --git a/firmware/GyverLamp2/fire2020.ino b/firmware/GyverLamp2/fire2020.ino index c40ab3c..168febb 100644 --- a/firmware/GyverLamp2/fire2020.ino +++ b/firmware/GyverLamp2/fire2020.ino @@ -12,7 +12,7 @@ void fire2020(byte scale, int len) { static float trackingObjectPosX[100]; static float trackingObjectPosY[100]; static uint16_t ff_x, ff_y, ff_z; - + if (loading) { loading = false; diff --git a/firmware/GyverLamp2/fire2D.ino b/firmware/GyverLamp2/fire2D.ino index 972ca1a..8367268 100644 --- a/firmware/GyverLamp2/fire2D.ino +++ b/firmware/GyverLamp2/fire2D.ino @@ -28,7 +28,7 @@ const unsigned char hueMask[11][16] PROGMEM = { byte fireLine[100]; void fireRoutine(byte speed) { - static byte count = 0; + static byte count = 0; if (count >= 100) { shiftUp(); FOR_i(0, cfg.width) fireLine[i] = random(64, 255); diff --git a/firmware/GyverLamp2/parsing.ino b/firmware/GyverLamp2/parsing.ino index 8eac2eb..c6759e9 100644 --- a/firmware/GyverLamp2/parsing.ino +++ b/firmware/GyverLamp2/parsing.ino @@ -1,37 +1,41 @@ void parsing() { if (Udp.parsePacket()) { - static uint32_t tmr = 0; static char buf[UDP_TX_PACKET_MAX_SIZE + 1]; int n = Udp.read(buf, UDP_TX_PACKET_MAX_SIZE); - buf[n] = NULL; - DEBUGLN(buf); // пакет вида <ключ>,<канал>,<тип>,<дата1>,<дата2>... - byte keyLen = strchr(buf, ',') - buf; // indexof - if (strncmp(buf, GL_KEY, keyLen)) return; // не наш ключ - if (buf[keyLen + 1] == '7') { // принимаем данные звука и ацп - int data[4]; - mString ints(buf + keyLen + 3, 100); - ints.parseInts(data, 4); - udpLength = data[0]; - udpScale = data[1]; - udpWidth = data[2]; - udpBright = data[3]; - return; + // ПРЕ-ПАРСИНГ (для данных АЦП) + if (buf[0] != 'G' || buf[1] != 'L' || buf[2] != ',') return; // защита от не наших данных + if (buf[3] == '7') { // АЦП GL,7, + if (!cfg.role) { // принимаем данные ацп если слейв + int data[3]; + mString ints(buf + 5, 20); + ints.parseInts(data, 3); + udpLength = data[0]; + udpScale = data[1]; + udpBright = data[2]; + effTmr.force(); // форсируем отрисовку эффекта + gotADCtmr = millis(); + } + return; // выходим } - if (millis() - tmr < 500) return; // принимаем посылки не чаще 2 раз в секунду - tmr = millis(); + if (millis() - udpTmr < 500) return; // принимаем остальные посылки не чаще 2 раз в секунду + udpTmr = millis(); + DEBUGLN(buf); // пакет вида ,<тип>,<дата1>,<дата2>... + + // ПАРСИНГ byte data[MAX_PRESETS * PRES_SIZE + 10]; - memset(data, 0, MAX_PRESETS * PRES_SIZE + 10); + memset(data, 0, sizeof(data)); int count = 0; - char *str, *p = buf + keyLen; // сдвиг до даты + char *str, *p = buf; char *ssid, *pass; while ((str = strtok_r(p, ",", &p)) != NULL) { uint32_t thisInt = atoi(str); - data[count++] = (byte)thisInt; + data[count++] = (byte)thisInt; // парс байтов + // парс "тяжёлых" данных if (data[1] == 0) { if (count == 4) ssid = str; if (count == 5) pass = str; @@ -49,21 +53,12 @@ void parsing() { if (count == 24) strcpy(cfg.mqttPass, str); } } + yield(); - // широковещательный запрос (адрес 0) времени для local устройств в сети AP лампы - if (data[0] == 0 && cfg.WiFimode && !gotNTP) { - - now.day = data[1]; - now.hour = data[2]; - now.min = data[3]; - now.sec = data[4]; - now.setMs(0); - } - - if (data[0] != cfg.group) return; // не наш адрес, выходим - - switch (data[1]) { // тип 0 - control, 1 - config, 2 - effects, 3 - dawn, 4 - from master, 5 - palette + // тип 0 - control, 1 - config, 2 - effects, 3 - dawn, 4 - from master, 5 - palette, 6 - time + switch (data[1]) { case 0: DEBUGLN("Control"); blinkTmr.restart(); + if (!cfg.state && data[2] != 1) return; // если лампа выключена и это не команда на включение - не обрабатываем switch (data[2]) { case 0: controlHandler(0); break; // выкл case 1: controlHandler(1); break; // вкл @@ -171,6 +166,17 @@ void parsing() { updPal(); EE_updatePal(); break; + + case 6: DEBUGLN("Time from AP"); + if (cfg.WiFimode && !gotNTP) { // время для local устройств в сети AP лампы (не получили время из интернета) + now.day = data[2]; + now.hour = data[3]; + now.min = data[4]; + now.sec = 0; + now.setMs(0); + DEBUGLN("Got time from master"); + } + break; } FastLED.clear(); // на всякий случай } @@ -178,10 +184,10 @@ void parsing() { void sendToSlaves(byte data1, byte data2) { if (cfg.role == GL_MASTER) { - char reply[20]; + char reply[15]; mString packet(reply, sizeof(reply)); packet.clear(); - packet = packet + GL_KEY + ',' + cfg.group + ",4," + data1 + ',' + data2; + packet = packet + "GL,4," + data1 + ',' + data2; DEBUG("Sending to Slaves: "); DEBUGLN(reply); diff --git a/firmware/GyverLamp2/presetManager.ino b/firmware/GyverLamp2/presetManager.ino index cd45a7b..4955f24 100644 --- a/firmware/GyverLamp2/presetManager.ino +++ b/firmware/GyverLamp2/presetManager.ino @@ -15,12 +15,12 @@ void presetRotation(bool force) { void changePreset(int dir) { //if (!cfg.rotation) { // ручная смена - 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); + 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); //} } @@ -61,13 +61,14 @@ void fade(bool state) { } void setPower(bool state) { + if (cfg.state != state) EE_updateCfg(); // на сохранение cfg.state = state; if (!state) { delay(100); // чтобы пролететь мин. частоту обновления FastLED.clear(); FastLED.show(); } - sendToSlaves(0, cfg.state); + if (millis() - udpTmr >= 1000) sendToSlaves(0, cfg.state); // пиздец костыль DEBUGLN(state ? "Power on" : "Power off"); } diff --git a/firmware/GyverLamp2/startup.ino b/firmware/GyverLamp2/startup.ino index ec9c78e..52bf313 100644 --- a/firmware/GyverLamp2/startup.ino +++ b/firmware/GyverLamp2/startup.ino @@ -92,7 +92,7 @@ void startWiFi() { } void setupAP() { - blink8(CRGB::Yellow); + blink16(CRGB::Yellow); WiFi.disconnect(); WiFi.mode(WIFI_AP); delay(100); @@ -129,12 +129,12 @@ void setupLocal() { leds[count] = CRGB::Yellow; FastLED.show(); count += dir; - if (count >= 7 || count <= 0) dir *= -1; + if (count >= 15 || count <= 0) dir *= -1; delay(50); } if (connect) { connTmr.stop(); - blink8(CRGB::Green); + blink16(CRGB::Green); server.begin(); DEBUG("Connected! Local IP: "); DEBUGLN(WiFi.localIP()); @@ -142,18 +142,13 @@ void setupLocal() { return; } else { DEBUGLN("Failed!"); - blink8(CRGB::Red); + blink16(CRGB::Red); failCount++; tmr = millis(); if (failCount >= 3) { connTmr.restart(); // попробуем позже setupAP(); return; - /*DEBUGLN("Reboot to AP!"); - cfg.WiFimode = 0; - EE_updCfg(); - delay(100); - ESP.restart();*/ } } } @@ -164,11 +159,11 @@ void checkUpdate() { if (cfg.update) { if (cfg.version != GL_VERSION) { cfg.version = GL_VERSION; - blink8(CRGB::Cyan); + blink16(CRGB::Cyan); DEBUG("Update to"); DEBUGLN(GL_VERSION); } else { - blink8(CRGB::Blue); + blink16(CRGB::Blue); DEBUG("Update to current"); } cfg.update = 0; @@ -179,6 +174,15 @@ void checkUpdate() { void tryReconnect() { if (connTmr.isReady()) { DEBUGLN("Reconnect"); - setupLocal(); + startWiFi(); } } + +void misc() { + memset(matrixValue, 0, sizeof(matrixValue)); + char GLkey[] = GL_KEY; + portNum = 17; + for (byte i = 0; i < strlen(GLkey); i++) portNum *= GLkey[i]; + portNum %= 15000; + portNum += 50000; +} diff --git a/firmware/GyverLamp2/time.ino b/firmware/GyverLamp2/time.ino index 8764468..e6dff8e 100644 --- a/firmware/GyverLamp2/time.ino +++ b/firmware/GyverLamp2/time.ino @@ -1,28 +1,50 @@ void setupTime() { - ntp.setUpdateInterval(NTP_UPD_PRD / 2 * 60000ul); // меньше в два раза, ибо апдейт вручную + ntp.setUpdateInterval(NTP_UPD_PRD * 60000ul); ntp.setTimeOffset((cfg.GMT - 13) * 3600); ntp.setPoolServerName(NTPserver); - if (cfg.WiFimode) { - // если подключены - запрашиваем время с сервера + if (cfg.WiFimode && !connTmr.running()) { // если успешно подключились к WiFi ntp.begin(); - if (ntp.update() && !gotNTP) { - gotNTP = true; - DEBUGLN("Got ntp"); + if (ntp.update()) gotNTP = true; + } +} + +// основной тикер времени +void timeTicker() { + static timerMillis tmr(30, true); + if (tmr.isReady()) { + if (cfg.WiFimode && WiFi.status() == WL_CONNECTED) { // если вайфай подключен + now.sec = ntp.getSeconds(); + now.min = ntp.getMinutes(); + now.hour = ntp.getHours(); + now.day = ntp.getDay(); // вс 0, сб 6 + now.weekMs = now.getWeekS() * 1000ul + ntp.getMillis(); + now.setMs(ntp.getMillis()); + if (ntp.update()) gotNTP = true; + } else { // если вайфай не подключен + now.tick(); // тикаем своим счётчиком + } + + static byte prevSec = 0; + if (prevSec != now.sec) { // новая секунда + prevSec = now.sec; + trnd.update(now.hour, now.min, now.sec); // обновляем рандомайзер + + if (now.sec == 0) { // новая минута + if (now.min % 5 == 0) sendTimeToLocals(); // отправляем время каждые 5 мин + if (gotNTP || gotTime) { // если знаем точное время + checkWorkTime(); // проверяем расписание + checkDawn(); // и рассвет + } + } } } } -// сохраняет счёт времени после обрыва связи -void timeTicker() { - static timerMillis tmr(10, true); - if (tmr.isReady()) { - updateTime(); // обновляем время - sendTimeToSlaves(); // отправляем время слейвам - trnd.update(now.hour, now.min, now.sec); // обновляем рандомайзер - if (gotNTP || gotTime) checkWorkTime(); // проверяем расписание, если знаем время - } +void sendTimeToLocals() { + if (!cfg.WiFimode) sendUDP(6, now.day, now.hour, now.min); // мы - АР } +// установка времени с мобилы void setTime(byte day, byte hour, byte min, byte sec) { if (!cfg.WiFimode || !gotNTP) { // если мы AP или не получили NTP now.day = day; @@ -31,39 +53,11 @@ void setTime(byte day, byte hour, byte min, byte sec) { now.sec = sec; now.setMs(0); gotTime = true; - } -} - -void updateTime() { - if (cfg.WiFimode && WiFi.status() == WL_CONNECTED) { // если вайфай подключен - now.sec = ntp.getSeconds(); - now.min = ntp.getMinutes(); - now.hour = ntp.getHours(); - now.day = ntp.getDay(); // вс 0, сб 6 - now.weekMs = now.getWeekS() * 1000ul + ntp.getMillis(); - now.setMs(ntp.getMillis()); - if (now.min % NTP_UPD_PRD == 0 && now.sec == 0) { - // берём время с интернета каждую NTP_UPD_PRD минуту, ставим флаг что данные с NTP получены, значит мы онлайн - if (ntp.update() && !gotNTP) gotNTP = true; - } - } else { // если нет - now.tick(); // тикаем своим счётчиком - } - if (gotNTP || gotTime) checkDawn(); // рассвет, если знаем точное время -} - -void sendTimeToSlaves() { - if (!cfg.WiFimode) { // если мы AP - static byte prevSec = 0; - if (prevSec != now.sec) { // новая секунда - prevSec = now.sec; - if (now.min % 5 == 0 && now.sec == 0) sendTime(); // ровно каждые 5 мин отправляем время - } } } void checkDawn() { - if (now.sec == 0 && dawn.state[now.day] && !dawnTmr.running()) { // рассвет включен но не запущен + if (dawn.state[now.day] && !dawnTmr.running()) { // рассвет включен но не запущен 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) { @@ -86,17 +80,6 @@ void checkWorkTime() { } } -void sendTime() { - char reply[25]; - mString packet(reply, sizeof(reply)); - packet.clear(); - packet = packet + GL_KEY + ",0," + now.day + ',' + now.hour + ',' + now.min + ',' + now.sec; - - DEBUG("Sending time: "); - DEBUGLN(reply); - sendUDP(reply); -} - bool isWorkTime(byte t, byte from, byte to) { if (from == to) return 1; else if (from < to) { diff --git a/firmware/GyverLamp2/timerMillis.h b/firmware/GyverLamp2/timerMillis.h index 71f67f5..0fbb0e5 100644 --- a/firmware/GyverLamp2/timerMillis.h +++ b/firmware/GyverLamp2/timerMillis.h @@ -21,6 +21,9 @@ class timerMillis { if (_active && millis() - _tmr >= _interval) stop(); return _active; } + void force() { + _tmr = millis() - _interval; + } void reset() { _tmr = millis(); }