diff --git a/firmware/GyverLamp2/0_func.ino b/firmware/GyverLamp2/0_func.ino new file mode 100644 index 0000000..97cf8e4 --- /dev/null +++ b/firmware/GyverLamp2/0_func.ino @@ -0,0 +1,23 @@ +void sendUDP(char *data) { + IPAddress ip = WiFi.localIP(); + ip[3] = 255; + Udp.beginPacket(ip, 50000 + cfg.group); + Udp.write(data); + Udp.endPacket(); +} +void restartUDP() { + DEBUG("UDP port: "); + DEBUGLN(50000 + cfg.group); + Udp.stop(); + Udp.begin(50000 + cfg.group); +} +void blink8(CRGB color) { + FOR_i(0, 3) { + fill_solid(leds, 8, color); + FastLED.show(); + delay(300); + FastLED.clear(); + FastLED.show(); + delay(300); + } +} diff --git a/firmware/GyverLamp2/GyverLamp2.ino b/firmware/GyverLamp2/GyverLamp2.ino index e520cfc..0aea744 100644 --- a/firmware/GyverLamp2/GyverLamp2.ino +++ b/firmware/GyverLamp2/GyverLamp2.ino @@ -1,4 +1,12 @@ /* + Версия 0.18b + Уменьшена чувствительность хлопков + Увеличена плавность светомузыки + Переделана сетевая политика + Микрофон и датчик света опрашивает только мастер и отсылает данные слейвам своей группы + 4 клика - включить первый режим + Отправка точного времени на лампу в режиме АР для работы рассвета и синхронизации эффектов + Версия 0.17b Автосмена отключается 30 сек во время настройки режимов Убрана кнопка upload в режимах @@ -7,17 +15,17 @@ Вкл выкл двумя хлопками Плавное выключение Починил рассвет - + Версия 0.16b Исправлен масштаб огня 2020 Фикс невыключения рассвета - + Версия 0.14b Мелкие баги Вернул искры огню Добавлены палитры Добавлен огонь 2020 - + Версия 0.13b Улучшена стабильность @@ -36,9 +44,8 @@ Выключение по таймеру теперь плавное Добавлен рассвет - TODO: + TODO: плавная смена режимов - 4 клика вкл выкл смену? Mqtt? Базовый пак Предложения Серёги крутского @@ -86,7 +93,7 @@ const char AP_NameChar[] = "GyverLamp2"; const char WiFiPassword[] = "12345678"; // ------------ Прочее ------------- -#define GL_VERSION 017 // код версии прошивки +#define GL_VERSION 18 // код версии прошивки #define EE_TOUT 30000 // таймаут сохранения епром после изменения, мс //#define DEBUG_SERIAL // закомментируй чтобы выключить отладку (скорость 115200) #define EE_KEY 55 // ключ сброса WiFi (измени для сброса всех настроек) @@ -94,17 +101,18 @@ const char WiFiPassword[] = "12345678"; //#define SKIP_WIFI // пропустить подключение к вафле (для отладки) // ------------ БИЛДЕР ------------- -//#define MAX_LEDS 1200 +#define MAX_LEDS 1200 // esp01 -//#define BTN_PIN 0 -//#define STRIP_PIN 2 -//#define USE_ADC 0 +#define BTN_PIN 0 +#define STRIP_PIN 2 +#define USE_ADC 0 // GL2 module //#define STRIP_PIN 5 // GPIO5 на gl module (D1 на wemos/node) // ---------- БИБЛИОТЕКИ ----------- +#define FASTLED_ALLOW_INTERRUPTS 0 #include "data.h" // данные #include "Time.h" // часы #include "TimeRandom.h" // случайные числа по времени @@ -115,7 +123,6 @@ const char WiFiPassword[] = "12345678"; #include "timerMillis.h" // таймер миллис #include "VolAnalyzer.h" // анализатор громкости #include "FFT_C.h" // фурье -#define FASTLED_ALLOW_INTERRUPTS 0 #include // лента #include // базовая либа есп #include // общение по UDP @@ -146,7 +153,9 @@ byte btnClicks = 0, brTicks = 0; unsigned char matrixValue[11][16]; bool gotNTP = false, gotTime = false; bool loading = true; -void blink8(CRGB color); +int udpLength = 0, udpWidth = 0; +byte udpScale = 0, udpBright = 0; + // ------------------- SETUP -------------------- void setup() { diff --git a/firmware/GyverLamp2/analog.ino b/firmware/GyverLamp2/analog.ino index 1f4acf9..5610982 100644 --- a/firmware/GyverLamp2/analog.ino +++ b/firmware/GyverLamp2/analog.ino @@ -1,6 +1,8 @@ #if (USE_ADC == 1) void setupADC() { clap.setTimeout(500); + clap.setTrsh(250); + low.setDt(0); low.setPeriod(0); low.setWindow(0); @@ -8,9 +10,9 @@ void setupADC() { high.setPeriod(0); high.setWindow(0); - vol.setVolK(20); - low.setVolK(20); - high.setVolK(20); + vol.setVolK(26); + low.setVolK(26); + high.setVolK(26); vol.setTrsh(50); low.setTrsh(50); @@ -33,26 +35,26 @@ 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(); + if (cfg.role) { + 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(); } - phot.compute(); - } - break; + break; + } } - //} } void checkMusic() { @@ -61,7 +63,7 @@ void checkMusic() { 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); diff --git a/firmware/GyverLamp2/button.ino b/firmware/GyverLamp2/button.ino index 6f9665c..fe5528a 100644 --- a/firmware/GyverLamp2/button.ino +++ b/firmware/GyverLamp2/button.ino @@ -27,11 +27,17 @@ void button() { changePreset(-1); sendToSlaves(1, cfg.curPreset); break; + case 4: + setPreset(0); + sendToSlaves(1, cfg.curPreset); + break; case 5: cfg.role = 0; + blink8(CRGB::DarkSlateBlue); break; case 6: cfg.role = 1; + blink8(CRGB::Maroon); break; } EE_updateCfg(); diff --git a/firmware/GyverLamp2/data.h b/firmware/GyverLamp2/data.h index f8b3beb..3131564 100644 --- a/firmware/GyverLamp2/data.h +++ b/firmware/GyverLamp2/data.h @@ -56,6 +56,7 @@ const char NTPserver[] = "pool.ntp.org"; //"ntp2.stratum2.ru" //"ntp.msk-ix.ru" +#define PAL_SIZE 49 struct Palette { byte size = 1; byte strip[16 * 3]; @@ -120,6 +121,7 @@ struct Preset { byte rnd = 0; // случайный (0/1) }; +#define DAWN_SIZE 23 struct Dawn { byte state[7] = {0, 0, 0, 0, 0, 0, 0}; // (1/0) byte hour[7] = {0, 0, 0, 0, 0, 0, 0}; // (0.. 59) diff --git a/firmware/GyverLamp2/effects.ino b/firmware/GyverLamp2/effects.ino index fdd63e1..dfa58cc 100644 --- a/firmware/GyverLamp2/effects.ino +++ b/firmware/GyverLamp2/effects.ino @@ -16,10 +16,33 @@ void effectsRoutine() { } if (cfg.state && effTmr.isReady()) { - int thisLength = getLength(); - byte thisScale = getScale(); - int thisWidth = (cfg.deviceType > 1) ? cfg.width : 1; - byte thisBright = getBright(); + int thisLength, thisWidth; + byte thisScale, thisBright; + + 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 { + 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()); @@ -273,17 +296,6 @@ void updPal() { if (pal.size < 16) paletteArr[0][pal.size] = paletteArr[0][0]; } -void blink8(CRGB color) { - FOR_i(0, 3) { - fill_solid(leds, 8, color); - FastLED.show(); - delay(300); - FastLED.clear(); - FastLED.show(); - delay(300); - } -} - byte scalePal(byte val) { if (CUR_PRES.palette == 1) val = val * pal.size / 16; return val; diff --git a/firmware/GyverLamp2/mString.h b/firmware/GyverLamp2/mString.h index f979cbb..6fe778d 100644 --- a/firmware/GyverLamp2/mString.h +++ b/firmware/GyverLamp2/mString.h @@ -43,68 +43,67 @@ char* mFtoa(double value, int8_t decimals, char *buffer) { class mString { public: - int size = 0; char* buf; - // system*this = buf; + int size = 0; uint16_t length() { return strlen(buf); } void clear() { - buf[0] = 0; + buf[0] = NULL; } // constructor mString(char* buffer, int newSize) { - //*this = buf; buf = buffer; size = newSize; } /*mString (const char c) { - init(); - add(c); + //init(); + add(c); } mString (const char* data) { - init(); - add(data); + //init(); + add(data); } mString (const __FlashStringHelper *data) { - init(); - add(data); + //init(); + add(data); } mString (uint32_t value) { - init(); - add(value); + //init(); + add(value); } mString (int32_t value) { - init(); - add(value); + //init(); + add(value); } mString (uint16_t value) { - init(); - add(value); + //init(); + add(value); } mString (int16_t value) { - init(); - add(value); + //init(); + add(value); } mString (uint8_t value) { - init(); - add(value); + //init(); + add(value); } mString (int8_t value) { - init(); - add(value); + //init(); + add(value); } mString (double value, byte dec = 2) { - init(); - add(value, dec); + //init(); + add(value, dec); }*/ // add mString& add(const char c) { byte len = length(); + if (len + 1 >= size) return *this; buf[len++] = c; - buf[len++] = 0; + buf[len++] = NULL; return *this; } mString& add(const char* data) { @@ -112,11 +111,13 @@ class mString { do { buf[len] = *(data++); } while (buf[len++] != 0);*/ + if (length() + strlen(data) >= size) return *this; strcpy(buf + length(), data); return *this; } mString& add(const __FlashStringHelper *data) { PGM_P p = reinterpret_cast(data); + if (length() + strlen_P(p) >= size) return *this; strcpy_P(buf + length(), p); return *this; /*do { @@ -125,10 +126,9 @@ class mString { */ } mString& add(uint32_t value) { - //char buf[11]; - //return add(mUtoa(value, buf)); - utoa(value, buf + length(), DEC); - return *this; + char vBuf[11]; + utoa(value, vBuf, DEC); + return add(vBuf); } mString& add(uint16_t value) { return add((uint32_t)value); @@ -137,10 +137,9 @@ class mString { return add((uint32_t)value); } mString& add(int32_t value) { - //char buf[11]; - //return add(mLtoa(value, buf)); - ltoa(value, buf + length(), DEC); - return *this; + char vBuf[11]; + ltoa(value, vBuf, DEC); + return add(vBuf); } mString& add(int16_t value) { return add((int32_t)value); @@ -149,11 +148,13 @@ class mString { return add((int32_t)value); } mString& add(double value, int8_t dec = 2) { - char buf[20]; - return add(mFtoa(value, dec, buf)); - //dtostrf(value, dec, DEC, buf+length()); - //return *this; + char vBuf[20]; + mFtoa(value, dec, vBuf); + return add(vBuf); } + /*mString& add(mString data) { + return add(data.buf); + }*/ // add += mString& operator += (const char c) { @@ -186,6 +187,44 @@ class mString { mString& operator += (double value) { return add(value); } + /*mString& operator += (mString data) { + return add(data); + }*/ + + // + + mString operator + (const char c) { + return mString(*this) += c; + } + mString operator + (const char* data) { + return mString(*this) += data; + } + mString operator + (const __FlashStringHelper *data) { + return mString(*this) += data; + } + mString operator + (uint32_t value) { + return mString(*this) += value; + } + mString operator + (int32_t value) { + return mString(*this) += value; + } + mString operator + (uint16_t value) { + return mString(*this) += value; + } + mString operator + (int16_t value) { + return mString(*this) += value; + } + mString operator + (uint8_t value) { + return mString(*this) += value; + } + mString operator + (int8_t value) { + return mString(*this) += value; + } + mString operator + (double value) { + return mString(*this) += value; + } + /*mString operator + (mString data) { + return mString(*this) += data; + }*/ // assign mString& operator = (const char c) { @@ -228,6 +267,10 @@ class mString { clear(); return add(value); } + /*mString& operator = (mString data) { + clear(); + return add(data); + }*/ // compare bool operator == (const char c) { @@ -248,15 +291,17 @@ class mString { char valBuf[20]; return !strcmp(buf, mFtoa(value, 2, valBuf)); } + /*bool operator == (mString data) { + return (buf == data.buf); + }*/ + + // convert & parse char operator [] (uint16_t index) const { return (index < size ? buf[index] : 0); } char& operator [] (uint16_t index) { return buf[index]; } - - - // convert & parse uint32_t toInt() { return atoi(buf); } diff --git a/firmware/GyverLamp2/parsing.ino b/firmware/GyverLamp2/parsing.ino index 1d6551f..8eac2eb 100644 --- a/firmware/GyverLamp2/parsing.ino +++ b/firmware/GyverLamp2/parsing.ino @@ -2,18 +2,29 @@ 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); - if (millis() - tmr < 500) return; // принимаем посылки не чаще 2 раз в секунду - tmr = millis(); buf[n] = NULL; DEBUGLN(buf); // пакет вида <ключ>,<канал>,<тип>,<дата1>,<дата2>... 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 + 5); + 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 (millis() - tmr < 500) return; // принимаем посылки не чаще 2 раз в секунду + tmr = millis(); + + byte data[MAX_PRESETS * PRES_SIZE + 10]; + memset(data, 0, MAX_PRESETS * PRES_SIZE + 10); int count = 0; char *str, *p = buf + keyLen; // сдвиг до даты char *ssid, *pass; @@ -39,8 +50,9 @@ void parsing() { } } - // широковещательный запрос времени для local устройств в сети AP лампы + // широковещательный запрос (адрес 0) времени для local устройств в сети AP лампы if (data[0] == 0 && cfg.WiFimode && !gotNTP) { + now.day = data[1]; now.hour = data[2]; now.min = data[3]; @@ -62,7 +74,7 @@ void parsing() { case 6: setPreset(data[3] - 1); break; // конкретный пресет data[3] case 7: cfg.WiFimode = data[3]; EE_updCfgRst(); break; // смена режима WiFi case 8: cfg.role = data[3]; break; // смена роли - case 9: cfg.group = data[3]; break; // смена группы + case 9: cfg.group = data[3]; restartUDP(); break; // смена группы case 10: // установка настроек WiFi strcpy(cfg.ssid, ssid); strcpy(cfg.pass, pass); @@ -90,6 +102,7 @@ void parsing() { } break; } + if (data[2] < 7) setTime(data[3], data[4], data[5], data[6]); EE_updCfg(); break; @@ -97,6 +110,7 @@ void parsing() { FOR_i(0, CFG_SIZE) { *((byte*)&cfg + i) = data[i + 2]; // загоняем в структуру } + setTime(data[CFG_SIZE + 10 + 2], data[CFG_SIZE + 10 + 3], data[CFG_SIZE + 10 + 4], data[CFG_SIZE + 10 + 5]); if (cfg.deviceType == GL_TYPE_STRIP) { if (cfg.length > MAX_LEDS) cfg.length = MAX_LEDS; cfg.width = 1; @@ -111,31 +125,37 @@ void parsing() { break; case 2: DEBUGLN("Preset"); - cfg.presetAmount = data[2]; // кол-во режимов - FOR_j(0, cfg.presetAmount) { - FOR_i(0, PRES_SIZE) { - *((byte*)&preset + j * PRES_SIZE + i) = data[j * PRES_SIZE + i + 3]; // загоняем в структуру + { + cfg.presetAmount = data[2]; // кол-во режимов + FOR_j(0, cfg.presetAmount) { + FOR_i(0, PRES_SIZE) { + *((byte*)&preset + j * PRES_SIZE + i) = data[j * PRES_SIZE + i + 3]; // загоняем в структуру + } } + //if (!cfg.rotation) setPreset(data[cfg.presetAmount * PRES_SIZE + 3] - 1); + byte dataStart = cfg.presetAmount * PRES_SIZE + 3; + setPreset(data[dataStart] - 1); + setTime(data[dataStart + 1], data[dataStart + 2], data[dataStart + 3], data[dataStart + 4]); + + EE_updatePreset(); + //presetRotation(true); // форсировать смену режима + holdPresTmr.restart(); + loading = true; } - //if (!cfg.rotation) setPreset(data[cfg.presetAmount * PRES_SIZE + 3] - 1); - setPreset(data[cfg.presetAmount * PRES_SIZE + 3] - 1); - EE_updatePreset(); - //presetRotation(true); // форсировать смену режима - holdPresTmr.restart(); - loading = true; break; case 3: DEBUGLN("Dawn"); blinkTmr.restart(); - FOR_i(0, (2 + 3 * 7)) { + FOR_i(0, DAWN_SIZE) { *((byte*)&dawn + i) = data[i + 2]; // загоняем в структуру } + setTime(data[DAWN_SIZE + 2], data[DAWN_SIZE + 3], data[DAWN_SIZE + 4], data[DAWN_SIZE + 5]); EE_updateDawn(); break; case 4: DEBUGLN("From master"); if (cfg.role == GL_SLAVE) { switch (data[2]) { - case 0: fade(data[3]); break; // вкл выкл + case 0: fade(data[3]); break; // вкл выкл case 1: setPreset(data[3]); break; // пресет case 2: cfg.bright = data[3]; break; // яркость } @@ -144,21 +164,13 @@ void parsing() { break; case 5: DEBUGLN("Palette"); blinkTmr.restart(); - FOR_i(0, 1 + 16 * 3) { + FOR_i(0, PAL_SIZE) { *((byte*)&pal + i) = data[i + 2]; // загоняем в структуру } + setTime(data[PAL_SIZE + 2], data[PAL_SIZE + 3], data[PAL_SIZE + 4], data[PAL_SIZE + 5]); updPal(); EE_updatePal(); break; - - case 6: DEBUGLN("Time"); blinkTmr.restart(); - if (!cfg.WiFimode) { // если мы AP - now.day = data[2]; - now.hour = data[3]; - now.min = data[4]; - } - gotTime = true; - break; } FastLED.clear(); // на всякий случай } @@ -166,27 +178,16 @@ void parsing() { void sendToSlaves(byte data1, byte data2) { if (cfg.role == GL_MASTER) { - IPAddress ip = WiFi.localIP(); - ip[3] = 255; - char reply[20]; mString packet(reply, sizeof(reply)); packet.clear(); - packet += GL_KEY; - packet += ','; - packet += cfg.group; - packet += ",4,"; - packet += data1; - packet += ','; - packet += data2; + packet = packet + GL_KEY + ',' + cfg.group + ",4," + data1 + ',' + data2; DEBUG("Sending to Slaves: "); DEBUGLN(reply); FOR_i(0, 3) { - Udp.beginPacket(ip, 8888); - Udp.write(reply); - Udp.endPacket(); + sendUDP(reply); delay(10); } } diff --git a/firmware/GyverLamp2/presetManager.ino b/firmware/GyverLamp2/presetManager.ino index 183bfab..cd45a7b 100644 --- a/firmware/GyverLamp2/presetManager.ino +++ b/firmware/GyverLamp2/presetManager.ino @@ -14,14 +14,14 @@ void presetRotation(bool force) { } void changePreset(int dir) { - if (!cfg.rotation) { // ручная смена + //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); - } + //} } void setPreset(byte pres) { diff --git a/firmware/GyverLamp2/startup.ino b/firmware/GyverLamp2/startup.ino index d7cf5b5..ec9c78e 100644 --- a/firmware/GyverLamp2/startup.ino +++ b/firmware/GyverLamp2/startup.ino @@ -86,9 +86,7 @@ void startWiFi() { if (!cfg.WiFimode) setupAP(); // режим точки доступа else setupLocal(); // подключаемся к точке - DEBUG("UDP port: "); - DEBUGLN(8888); - Udp.begin(8888); + restartUDP(); FastLED.clear(); FastLED.show(); } diff --git a/firmware/GyverLamp2/time.ino b/firmware/GyverLamp2/time.ino index fc27a6c..8764468 100644 --- a/firmware/GyverLamp2/time.ino +++ b/firmware/GyverLamp2/time.ino @@ -23,6 +23,17 @@ void timeTicker() { } } +void setTime(byte day, byte hour, byte min, byte sec) { + if (!cfg.WiFimode || !gotNTP) { // если мы AP или не получили NTP + now.day = day; + now.hour = hour; + now.min = min; + now.sec = sec; + now.setMs(0); + gotTime = true; + } +} + void updateTime() { if (cfg.WiFimode && WiFi.status() == WL_CONNECTED) { // если вайфай подключен now.sec = ntp.getSeconds(); @@ -38,7 +49,7 @@ void updateTime() { } else { // если нет now.tick(); // тикаем своим счётчиком } - if (gotNTP || gotTime) checkDawn(); + if (gotNTP || gotTime) checkDawn(); // рассвет, если знаем точное время } void sendTimeToSlaves() { @@ -75,29 +86,15 @@ void checkWorkTime() { } } -void sendTime() { - IPAddress ip = WiFi.localIP(); - ip[3] = 255; - char reply[25] = GL_KEY; +void sendTime() { + char reply[25]; mString packet(reply, sizeof(reply)); packet.clear(); - packet += GL_KEY; - packet += ','; - packet += 0; - packet += ','; - packet += now.day; - packet += ','; - packet += now.hour; - packet += ','; - packet += now.min; - packet += ','; - packet += now.sec; + packet = packet + GL_KEY + ",0," + now.day + ',' + now.hour + ',' + now.min + ',' + now.sec; DEBUG("Sending time: "); DEBUGLN(reply); - Udp.beginPacket(ip, 8888); - Udp.write(reply); - Udp.endPacket(); + sendUDP(reply); } bool isWorkTime(byte t, byte from, byte to) {