This commit is contained in:
Alex
2021-03-01 17:00:25 +03:00
parent 356f88ee65
commit e9a0a326e1
19 changed files with 497 additions and 424 deletions

View File

@@ -1,19 +1,29 @@
void sendUDP(char *data) { void sendUDP(char *data) {
IPAddress ip = WiFi.localIP(); Udp.beginPacket(deviceIP, portNum + cfg.group);
ip[3] = 255;
Udp.beginPacket(ip, 50000 + cfg.group);
Udp.write(data); Udp.write(data);
Udp.endPacket(); Udp.endPacket();
} }
void restartUDP() { void sendUDP(byte cmd, int data1 = 0, int data2 = 0, int data3 = 0) {
DEBUG("UDP port: "); char reply[20] = "";
DEBUGLN(50000 + cfg.group); mString packet(reply, sizeof(reply));
Udp.stop(); packet = packet + "GL," + cmd + ',' + data1 + ',' + data2 + ',' + data3;
Udp.begin(50000 + cfg.group); 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) { FOR_i(0, 3) {
fill_solid(leds, 8, color); fill_solid(leds, 16, color);
FastLED.show(); FastLED.show();
delay(300); delay(300);
FastLED.clear(); FastLED.clear();
@@ -21,3 +31,50 @@ void blink8(CRGB color) {
delay(300); 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);
}

View File

@@ -1,4 +1,14 @@
/* /*
Версия 0.19b
Минимальная версия приложения 1.17!!!
Почищен мусор, оптимизация, повышена стабильность и производительность
Мигает теперь 16 светиков
Снова переделана сетевая политика, упрощён и сильно ускорен парсинг
Изменены пределы по светодиодам, что сильно увеличило производительность
Выключенная (программно) лампа не принимает сервисные команды кроме команды включиться
Добавлены часы, в том числе в рассвет
Slave работает со светомузыкой сам, если не получает данные с мастера
Версия 0.18b Версия 0.18b
Уменьшена чувствительность хлопков Уменьшена чувствительность хлопков
Увеличена плавность светомузыки Увеличена плавность светомузыки
@@ -48,9 +58,7 @@
плавная смена режимов плавная смена режимов
Mqtt? Mqtt?
Базовый пак Базовый пак
Предложения Серёги крутского
Эффект погода https://it4it.club/topic/40-esp8266-i-parsing-pogodyi-s-openweathermap/ Эффект погода https://it4it.club/topic/40-esp8266-i-parsing-pogodyi-s-openweathermap/
Эффект часы
*/ */
// ВНИМАНИЕ! ВНИМАНИЕ! ВНИМАНИЕ! ВНИМАНИЕ! ВНИМАНИЕ! ВНИМАНИЕ! ВНИМАНИЕ! // ВНИМАНИЕ! ВНИМАНИЕ! ВНИМАНИЕ! ВНИМАНИЕ! ВНИМАНИЕ! ВНИМАНИЕ! ВНИМАНИЕ!
@@ -76,7 +84,7 @@
// ------------ Лента ------------- // ------------ Лента -------------
#define STRIP_PIN 2 // пин ленты GPIO2 (D4 на wemos/node) #define STRIP_PIN 2 // пин ленты GPIO2 (D4 на wemos/node)
#define MAX_LEDS 600 // макс. светодиодов #define MAX_LEDS 300 // макс. светодиодов
#define STRIP_CHIP WS2812 // чип ленты #define STRIP_CHIP WS2812 // чип ленты
#define STRIP_COLOR GRB // порядок цветов в ленте #define STRIP_COLOR GRB // порядок цветов в ленте
#define STRIP_VOLT 5 // напряжение ленты, V #define STRIP_VOLT 5 // напряжение ленты, V
@@ -93,15 +101,15 @@ const char AP_NameChar[] = "GyverLamp2";
const char WiFiPassword[] = "12345678"; const char WiFiPassword[] = "12345678";
// ------------ Прочее ------------- // ------------ Прочее -------------
#define GL_VERSION 18 // код версии прошивки #define GL_VERSION 19 // код версии прошивки
#define EE_TOUT 30000 // таймаут сохранения епром после изменения, мс #define EE_TOUT 30000 // таймаут сохранения епром после изменения, мс
//#define DEBUG_SERIAL // закомментируй чтобы выключить отладку (скорость 115200) #define DEBUG_SERIAL // закомментируй чтобы выключить отладку (скорость 115200)
#define EE_KEY 55 // ключ сброса WiFi (измени для сброса всех настроек) #define EE_KEY 55 // ключ сброса WiFi (измени для сброса всех настроек)
#define NTP_UPD_PRD 5 // период обновления времени с NTP сервера, минут #define NTP_UPD_PRD 5 // период обновления времени с NTP сервера, минут
//#define SKIP_WIFI // пропустить подключение к вафле (для отладки) //#define SKIP_WIFI // пропустить подключение к вафле (для отладки)
// ------------ БИЛДЕР ------------- // ------------ БИЛДЕР -------------
//#define MAX_LEDS 1200 //#define MAX_LEDS 900
// esp01 // esp01
//#define BTN_PIN 0 //#define BTN_PIN 0
@@ -139,33 +147,35 @@ Palette pal;
WiFiServer server(80); WiFiServer server(80);
WiFiUDP Udp; WiFiUDP Udp;
WiFiUDP ntpUDP; WiFiUDP ntpUDP;
IPAddress deviceIP;
NTPClient ntp(ntpUDP); NTPClient ntp(ntpUDP);
CRGB leds[MAX_LEDS]; CRGB leds[MAX_LEDS];
Time now; Time now;
Button btn(BTN_PIN); Button btn(BTN_PIN);
timerMillis EEtmr(EE_TOUT), turnoffTmr, connTmr(120000ul), dawnTmr, holdPresTmr(30000ul), blinkTmr(300); timerMillis EEtmr(EE_TOUT), turnoffTmr, connTmr(120000ul), dawnTmr, holdPresTmr(30000ul), blinkTmr(300);
timerMillis effTmr(30, true);
TimeRandom trnd; TimeRandom trnd;
VolAnalyzer vol(A0), low, high; VolAnalyzer vol(A0), low, high;
FastFilter phot; FastFilter phot;
Clap clap; Clap clap;
uint16_t portNum;
uint32_t udpTmr = 0, gotADCtmr = 0;
byte btnClicks = 0, brTicks = 0; byte btnClicks = 0, brTicks = 0;
unsigned char matrixValue[11][16]; unsigned char matrixValue[11][16];
bool gotNTP = false, gotTime = false; bool gotNTP = false, gotTime = false;
bool loading = true; bool loading = true;
int udpLength = 0, udpWidth = 0; int udpLength = 0;
byte udpScale = 0, udpBright = 0; byte udpScale = 0, udpBright = 0;
// ------------------- SETUP -------------------- // ------------------- SETUP --------------------
void setup() { void setup() {
misc();
delay(2000); // ждём старта есп delay(2000); // ждём старта есп
memset(matrixValue, 0, sizeof(matrixValue));
#ifdef DEBUG_SERIAL #ifdef DEBUG_SERIAL
Serial.begin(115200); Serial.begin(115200);
DEBUGLN(); DEBUGLN();
#endif #endif
EEPROM.begin(1000); // старт епром
startStrip(); // старт ленты startStrip(); // старт ленты
btn.setLevel(digitalRead(BTN_PIN)); // смотрим что за кнопка btn.setLevel(digitalRead(BTN_PIN)); // смотрим что за кнопка
EE_startup(); // читаем епром EE_startup(); // читаем епром
@@ -183,6 +193,7 @@ void setup() {
void loop() { void loop() {
timeTicker(); // обновляем время timeTicker(); // обновляем время
yield();
#ifndef SKIP_WIFI #ifndef SKIP_WIFI
tryReconnect(); // пробуем переподключиться если WiFi упал tryReconnect(); // пробуем переподключиться если WiFi упал
yield(); yield();
@@ -195,4 +206,5 @@ void loop() {
yield(); yield();
button(); // проверяем кнопку button(); // проверяем кнопку
checkAnalog(); // чтение звука и датчика checkAnalog(); // чтение звука и датчика
yield();
} }

View File

@@ -1,22 +1,22 @@
/** /**
* The MIT License (MIT) The MIT License (MIT)
* Copyright (c) 2015 by Fabrice Weinberg Copyright (c) 2015 by Fabrice Weinberg
*
* Permission is hereby granted, free of charge, to any person obtaining a copy Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions: furnished to do so, subject to the following conditions:
* The above copyright notice and this permission notice shall be included in all The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software. copies or substantial portions of the Software.
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 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 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE. SOFTWARE.
*/ */
#include "NTPClient-Gyver.h" #include "NTPClient-Gyver.h"

View File

@@ -36,34 +36,34 @@ class NTPClient {
NTPClient(UDP& udp, const char* poolServerName, long timeOffset, unsigned long updateInterval); NTPClient(UDP& udp, const char* poolServerName, long timeOffset, unsigned long updateInterval);
/** /**
* Set time server name Set time server name
*
* @param poolServerName @param poolServerName
*/ */
void setPoolServerName(const char* 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(); 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); 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 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. made every 60 seconds. This can be configured in the NTPClient constructor.
*
* @return true on success, false on failure @return true on success, false on failure
*/ */
bool update(); bool update();
/** /**
* This will force the update from the NTP Server. This will force the update from the NTP Server.
*
* @return true on success, false on failure @return true on success, false on failure
*/ */
bool forceUpdate(); bool forceUpdate();
@@ -75,28 +75,28 @@ class NTPClient {
int getMillisLastUpd() 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); void setTimeOffset(int timeOffset);
/** /**
* Set the update interval to another frequency. E.g. useful when the Set the update interval to another frequency. E.g. useful when the
* timeOffset should not be set in the constructor timeOffset should not be set in the constructor
*/ */
void setUpdateInterval(unsigned long updateInterval); void setUpdateInterval(unsigned long updateInterval);
/** /**
* @return time formatted like `hh:mm:ss` @return time formatted like `hh:mm:ss`
*/ */
String getFormattedTime() const; String getFormattedTime() const;
/** /**
* @return time in seconds since Jan. 1, 1970 @return time in seconds since Jan. 1, 1970
*/ */
unsigned long getEpochTime() const; unsigned long getEpochTime() const;
/** /**
* Stops the underlying UDP client Stops the underlying UDP client
*/ */
void end(); void end();
}; };

View File

@@ -9,7 +9,7 @@ class Time {
uint32_t weekS = 0; uint32_t weekS = 0;
int getMs() { int getMs() {
return (millis() - tmr); return (tmr - millis());
} }
void setMs(int ms) { void setMs(int ms) {
tmr = millis() + ms; tmr = millis() + ms;

View File

@@ -3,6 +3,10 @@ void setupADC() {
clap.setTimeout(500); clap.setTimeout(500);
clap.setTrsh(250); clap.setTrsh(250);
vol.setDt(700);
vol.setPeriod(5);
vol.setWindow(map(MAX_LEDS, 300, 1200, 20, 1));
low.setDt(0); low.setDt(0);
low.setPeriod(0); low.setPeriod(0);
low.setWindow(0); low.setWindow(0);
@@ -35,7 +39,7 @@ void setupADC() {
void checkAnalog() { void checkAnalog() {
if (cfg.role) { if (cfg.role || millis() - gotADCtmr >= 2000) { // только мастер или слейв по таймауту опрашивает АЦП!
switch (cfg.adcMode) { switch (cfg.adcMode) {
case GL_ADC_NONE: break; case GL_ADC_NONE: break;
case GL_ADC_BRI: checkPhot(); break; case GL_ADC_BRI: checkPhot(); break;
@@ -63,10 +67,11 @@ void checkMusic() {
clap.tick(vol.getRawMax()); clap.tick(vol.getRawMax());
if (clap.hasClaps(2)) controlHandler(!cfg.state); if (clap.hasClaps(2)) controlHandler(!cfg.state);
#endif #endif
yield();
if (CUR_PRES.advMode == GL_ADV_LOW || CUR_PRES.advMode == GL_ADV_HIGH) { // частоты if (CUR_PRES.advMode == GL_ADV_LOW || CUR_PRES.advMode == GL_ADV_HIGH) { // частоты
int raw[FFT_SIZE], spectr[FFT_SIZE]; int raw[FFT_SIZE], spectr[FFT_SIZE];
for (int i = 0; i < FFT_SIZE; i++) raw[i] = analogRead(A0); for (int i = 0; i < FFT_SIZE; i++) raw[i] = analogRead(A0);
yield();
FFT(raw, spectr); FFT(raw, spectr);
int low_raw = 0; int low_raw = 0;
int high_raw = 0; int high_raw = 0;

View File

@@ -33,11 +33,11 @@ void button() {
break; break;
case 5: case 5:
cfg.role = 0; cfg.role = 0;
blink8(CRGB::DarkSlateBlue); blink16(CRGB::DarkSlateBlue);
break; break;
case 6: case 6:
cfg.role = 1; cfg.role = 1;
blink8(CRGB::Maroon); blink16(CRGB::Maroon);
break; break;
} }
EE_updateCfg(); EE_updateCfg();

View File

@@ -41,12 +41,12 @@ int mapFF(byte x, byte min, byte max) {
const char OTAhost[] = "http://ota.alexgyver.ru/"; const char OTAhost[] = "http://ota.alexgyver.ru/";
const char *OTAfile[] = { const char *OTAfile[] = {
"GL2_latest.bin", "GL2_latest.bin",
"com_600.bin", "com_300.bin",
"com_1200.bin", "com_900.bin",
"esp1_600.bin", "esp1_300.bin",
"esp1_1200.bin", "esp1_900.bin",
"module_600.bin", "module_300.bin",
"module_1200.bin", "module_900.bin",
}; };
const char NTPserver[] = "pool.ntp.org"; const char NTPserver[] = "pool.ntp.org";
@@ -129,3 +129,9 @@ struct Dawn {
byte bright = 100; // (0.. 255) byte bright = 100; // (0.. 255)
byte time = 1; // (5,10,15,20..) byte time = 1; // (5,10,15,20..)
}; };
/*
- Каждые 5 минут лампа AP отправляет время (день час минута) на Local лампы всех ролей в сети с ней (GL,6,день,час,мин)
- Если включен АЦП, Мастер отправляет своей группе данные с него на каждой итерации отрисовки эффектов (GL,1,длина,масштаб,яркость)
- Установка времени с мобилы - получают все роли АР и Local (не получившие ntp)
*/

View File

@@ -5,6 +5,8 @@ bool EEpalFlag = false;
void EE_startup() { void EE_startup() {
// старт епром // старт епром
EEPROM.begin(1000); // старт епром
delay(100);
if (EEPROM.read(0) != EE_KEY) { if (EEPROM.read(0) != EE_KEY) {
EEPROM.write(0, EE_KEY); EEPROM.write(0, EE_KEY);
EEPROM.put(1, cfg); 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) + 1, pal);
EEPROM.put(sizeof(cfg) + sizeof(dawn) + sizeof(pal) + 1, preset); EEPROM.put(sizeof(cfg) + sizeof(dawn) + sizeof(pal) + 1, preset);
EEPROM.commit(); EEPROM.commit();
blink8(CRGB::Magenta); blink16(CRGB::Magenta);
DEBUGLN("First start"); DEBUGLN("First start");
} }
EEPROM.get(1, cfg); EEPROM.get(1, cfg);

View File

@@ -1,12 +1,11 @@
void effectsRoutine() { void effectsRoutine() {
static timerMillis effTmr(30, true);
static byte prevEff = 255; static byte prevEff = 255;
if (!effTmr.isReady()) return;
if (dawnTmr.running()) { if (dawnTmr.running()) {
if (effTmr.isReady()) {
fill_solid(leds, MAX_LEDS, ColorFromPalette(HeatColors_p, dawnTmr.getLength8(), scaleFF(dawnTmr.getLength8(), dawn.bright), LINEARBLEND)); 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(); FastLED.show();
}
if (dawnTmr.isReady()) { if (dawnTmr.isReady()) {
dawnTmr.stop(); dawnTmr.stop();
FastLED.clear(); FastLED.clear();
@@ -15,33 +14,22 @@ void effectsRoutine() {
return; return;
} }
if (cfg.state && effTmr.isReady()) { if (!cfg.state) return;
int thisLength, thisWidth; int thisLength = getLength();
byte thisScale, thisBright; byte thisScale = getScale();
byte thisBright = getBright();
if (cfg.adcMode > 1) { // музыка или яркость if (cfg.adcMode > 1) { // музыка или яркость
if (cfg.role) { if (cfg.role) { // мастер отправляет
thisLength = getLength(); static timerMillis adcSend(120, true);
thisScale = getScale(); if (adcSend.isReady() && millis() - udpTmr >= 1000) sendUDP(7, thisLength, thisScale, thisBright);
thisWidth = (cfg.deviceType > 1) ? cfg.width : 1; } else { // слейв получает
thisBright = getBright(); if (millis() - gotADCtmr < 2000) { // есть сигнал с мастера
char reply[25];
mString packet(reply, sizeof(reply));
packet.clear();
packet = packet + GL_KEY + ",7," + thisLength + ',' + thisScale + ',' + thisWidth + ',' + thisBright;
sendUDP(reply);
} else {
thisLength = udpLength; thisLength = udpLength;
thisScale = udpScale; thisScale = udpScale;
thisWidth = udpWidth;
thisBright = udpBright; 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()); if (turnoffTmr.running()) thisBright = scaleFF(thisBright, 255 - turnoffTmr.getLength8());
@@ -53,11 +41,12 @@ void effectsRoutine() {
} }
FastLED.setBrightness(thisBright); FastLED.setBrightness(thisBright);
if (prevEff != CUR_PRES.effect) { if (prevEff != CUR_PRES.effect) { // смена эффекта
FastLED.clear(); FastLED.clear();
prevEff = CUR_PRES.effect; prevEff = CUR_PRES.effect;
loading = true; loading = true;
} }
// =================================================== ЭФФЕКТЫ =================================================== // =================================================== ЭФФЕКТЫ ===================================================
switch (CUR_PRES.effect) { switch (CUR_PRES.effect) {
case 1: // =================================== ПЕРЛИН =================================== case 1: // =================================== ПЕРЛИН ===================================
@@ -85,7 +74,7 @@ void effectsRoutine() {
case 2: // ==================================== ЦВЕТ ==================================== case 2: // ==================================== ЦВЕТ ====================================
{ {
fill_solid(leds, cfg.length * thisWidth, CHSV(CUR_PRES.color, thisScale, 30)); fill_solid(leds, cfg.length * cfg.width, CHSV(CUR_PRES.color, thisScale, 30));
CRGB thisColor = CHSV(CUR_PRES.color, thisScale, thisBright); CRGB thisColor = CHSV(CUR_PRES.color, thisScale, thisBright);
if (CUR_PRES.fromCenter) { if (CUR_PRES.fromCenter) {
fillStrip(cfg.length / 2, cfg.length / 2 + thisLength / 2, thisColor); fillStrip(cfg.length / 2, cfg.length / 2 + thisLength / 2, thisColor);
@@ -99,7 +88,7 @@ void effectsRoutine() {
case 3: // ================================= СМЕНА ЦВЕТА ================================= case 3: // ================================= СМЕНА ЦВЕТА =================================
{ {
CRGB thisColor = ColorFromPalette(paletteArr[CUR_PRES.palette - 1], scalePal((now.weekMs >> 5) * CUR_PRES.speed / 255), 10, 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); 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); thisColor = ColorFromPalette(paletteArr[CUR_PRES.palette - 1], scalePal((now.weekMs >> 5) * CUR_PRES.speed / 255), thisBright, LINEARBLEND);
if (CUR_PRES.fromCenter) { if (CUR_PRES.fromCenter) {
fillStrip(cfg.length / 2, cfg.length / 2 + thisLength / 2, thisColor); fillStrip(cfg.length / 2, cfg.length / 2 + thisLength / 2, thisColor);
@@ -225,20 +214,25 @@ void effectsRoutine() {
} }
break; break;
case 9: // ================================== ПОГОДА ================================== 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; 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 (btnClicks > 0) fill_solid(leds, btnClicks, CRGB::White);
if (brTicks > 0) fill_solid(leds, brTicks, CRGB::Cyan); if (brTicks > 0) fill_solid(leds, brTicks, CRGB::Cyan);
yield();
FastLED.show(); FastLED.show();
} }
}
// ==================================================================================================================== // ====================================================================================================================
bool musicMode() { bool musicMode() {
return ((cfg.adcMode == GL_ADC_MIC || cfg.adcMode == GL_ADC_BOTH) && (CUR_PRES.advMode > 1 && CUR_PRES.advMode <= 4)); return ((cfg.adcMode == GL_ADC_MIC || cfg.adcMode == GL_ADC_BOTH) && (CUR_PRES.advMode > 1 && CUR_PRES.advMode <= 4));
} }

View File

@@ -1,37 +1,41 @@
void parsing() { void parsing() {
if (Udp.parsePacket()) { if (Udp.parsePacket()) {
static uint32_t tmr = 0;
static char buf[UDP_TX_PACKET_MAX_SIZE + 1]; static char buf[UDP_TX_PACKET_MAX_SIZE + 1];
int n = Udp.read(buf, UDP_TX_PACKET_MAX_SIZE); int n = Udp.read(buf, UDP_TX_PACKET_MAX_SIZE);
buf[n] = NULL; 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]; if (buf[0] != 'G' || buf[1] != 'L' || buf[2] != ',') return; // защита от не наших данных
mString ints(buf + keyLen + 3, 100); if (buf[3] == '7') { // АЦП GL,7,
ints.parseInts(data, 4); if (!cfg.role) { // принимаем данные ацп если слейв
int data[3];
mString ints(buf + 5, 20);
ints.parseInts(data, 3);
udpLength = data[0]; udpLength = data[0];
udpScale = data[1]; udpScale = data[1];
udpWidth = data[2]; udpBright = data[2];
udpBright = data[3]; effTmr.force(); // форсируем отрисовку эффекта
return; gotADCtmr = millis();
}
return; // выходим
} }
if (millis() - tmr < 500) return; // принимаем посылки не чаще 2 раз в секунду if (millis() - udpTmr < 500) return; // принимаем остальные посылки не чаще 2 раз в секунду
tmr = millis(); udpTmr = millis();
DEBUGLN(buf); // пакет вида <GL>,<тип>,<дата1>,<дата2>...
// ПАРСИНГ
byte data[MAX_PRESETS * PRES_SIZE + 10]; byte data[MAX_PRESETS * PRES_SIZE + 10];
memset(data, 0, MAX_PRESETS * PRES_SIZE + 10); memset(data, 0, sizeof(data));
int count = 0; int count = 0;
char *str, *p = buf + keyLen; // сдвиг до даты char *str, *p = buf;
char *ssid, *pass; char *ssid, *pass;
while ((str = strtok_r(p, ",", &p)) != NULL) { while ((str = strtok_r(p, ",", &p)) != NULL) {
uint32_t thisInt = atoi(str); uint32_t thisInt = atoi(str);
data[count++] = (byte)thisInt; data[count++] = (byte)thisInt; // парс байтов
// парс "тяжёлых" данных
if (data[1] == 0) { if (data[1] == 0) {
if (count == 4) ssid = str; if (count == 4) ssid = str;
if (count == 5) pass = str; if (count == 5) pass = str;
@@ -49,21 +53,12 @@ void parsing() {
if (count == 24) strcpy(cfg.mqttPass, str); if (count == 24) strcpy(cfg.mqttPass, str);
} }
} }
yield();
// широковещательный запрос (адрес 0) времени для local устройств в сети AP лампы // тип 0 - control, 1 - config, 2 - effects, 3 - dawn, 4 - from master, 5 - palette, 6 - time
if (data[0] == 0 && cfg.WiFimode && !gotNTP) { switch (data[1]) {
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
case 0: DEBUGLN("Control"); blinkTmr.restart(); case 0: DEBUGLN("Control"); blinkTmr.restart();
if (!cfg.state && data[2] != 1) return; // если лампа выключена и это не команда на включение - не обрабатываем
switch (data[2]) { switch (data[2]) {
case 0: controlHandler(0); break; // выкл case 0: controlHandler(0); break; // выкл
case 1: controlHandler(1); break; // вкл case 1: controlHandler(1); break; // вкл
@@ -171,6 +166,17 @@ void parsing() {
updPal(); updPal();
EE_updatePal(); EE_updatePal();
break; 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(); // на всякий случай FastLED.clear(); // на всякий случай
} }
@@ -178,10 +184,10 @@ void parsing() {
void sendToSlaves(byte data1, byte data2) { void sendToSlaves(byte data1, byte data2) {
if (cfg.role == GL_MASTER) { if (cfg.role == GL_MASTER) {
char reply[20]; char reply[15];
mString packet(reply, sizeof(reply)); mString packet(reply, sizeof(reply));
packet.clear(); packet.clear();
packet = packet + GL_KEY + ',' + cfg.group + ",4," + data1 + ',' + data2; packet = packet + "GL,4," + data1 + ',' + data2;
DEBUG("Sending to Slaves: "); DEBUG("Sending to Slaves: ");
DEBUGLN(reply); DEBUGLN(reply);

View File

@@ -61,13 +61,14 @@ void fade(bool state) {
} }
void setPower(bool state) { void setPower(bool state) {
if (cfg.state != state) EE_updateCfg(); // на сохранение
cfg.state = state; cfg.state = state;
if (!state) { if (!state) {
delay(100); // чтобы пролететь мин. частоту обновления delay(100); // чтобы пролететь мин. частоту обновления
FastLED.clear(); FastLED.clear();
FastLED.show(); FastLED.show();
} }
sendToSlaves(0, cfg.state); if (millis() - udpTmr >= 1000) sendToSlaves(0, cfg.state); // пиздец костыль
DEBUGLN(state ? "Power on" : "Power off"); DEBUGLN(state ? "Power on" : "Power off");
} }

View File

@@ -92,7 +92,7 @@ void startWiFi() {
} }
void setupAP() { void setupAP() {
blink8(CRGB::Yellow); blink16(CRGB::Yellow);
WiFi.disconnect(); WiFi.disconnect();
WiFi.mode(WIFI_AP); WiFi.mode(WIFI_AP);
delay(100); delay(100);
@@ -129,12 +129,12 @@ void setupLocal() {
leds[count] = CRGB::Yellow; leds[count] = CRGB::Yellow;
FastLED.show(); FastLED.show();
count += dir; count += dir;
if (count >= 7 || count <= 0) dir *= -1; if (count >= 15 || count <= 0) dir *= -1;
delay(50); delay(50);
} }
if (connect) { if (connect) {
connTmr.stop(); connTmr.stop();
blink8(CRGB::Green); blink16(CRGB::Green);
server.begin(); server.begin();
DEBUG("Connected! Local IP: "); DEBUG("Connected! Local IP: ");
DEBUGLN(WiFi.localIP()); DEBUGLN(WiFi.localIP());
@@ -142,18 +142,13 @@ void setupLocal() {
return; return;
} else { } else {
DEBUGLN("Failed!"); DEBUGLN("Failed!");
blink8(CRGB::Red); blink16(CRGB::Red);
failCount++; failCount++;
tmr = millis(); tmr = millis();
if (failCount >= 3) { if (failCount >= 3) {
connTmr.restart(); // попробуем позже connTmr.restart(); // попробуем позже
setupAP(); setupAP();
return; 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.update) {
if (cfg.version != GL_VERSION) { if (cfg.version != GL_VERSION) {
cfg.version = GL_VERSION; cfg.version = GL_VERSION;
blink8(CRGB::Cyan); blink16(CRGB::Cyan);
DEBUG("Update to"); DEBUG("Update to");
DEBUGLN(GL_VERSION); DEBUGLN(GL_VERSION);
} else { } else {
blink8(CRGB::Blue); blink16(CRGB::Blue);
DEBUG("Update to current"); DEBUG("Update to current");
} }
cfg.update = 0; cfg.update = 0;
@@ -179,6 +174,15 @@ void checkUpdate() {
void tryReconnect() { void tryReconnect() {
if (connTmr.isReady()) { if (connTmr.isReady()) {
DEBUGLN("Reconnect"); 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;
}

View File

@@ -1,28 +1,50 @@
void setupTime() { void setupTime() {
ntp.setUpdateInterval(NTP_UPD_PRD / 2 * 60000ul); // меньше в два раза, ибо апдейт вручную ntp.setUpdateInterval(NTP_UPD_PRD * 60000ul);
ntp.setTimeOffset((cfg.GMT - 13) * 3600); ntp.setTimeOffset((cfg.GMT - 13) * 3600);
ntp.setPoolServerName(NTPserver); ntp.setPoolServerName(NTPserver);
if (cfg.WiFimode) { if (cfg.WiFimode && !connTmr.running()) { // если успешно подключились к WiFi
// если подключены - запрашиваем время с сервера
ntp.begin(); ntp.begin();
if (ntp.update() && !gotNTP) { if (ntp.update()) gotNTP = true;
gotNTP = true;
DEBUGLN("Got ntp");
}
} }
} }
// сохраняет счёт времени после обрыва связи // основной тикер времени
void timeTicker() { void timeTicker() {
static timerMillis tmr(10, true); static timerMillis tmr(30, true);
if (tmr.isReady()) { if (tmr.isReady()) {
updateTime(); // обновляем время if (cfg.WiFimode && WiFi.status() == WL_CONNECTED) { // если вайфай подключен
sendTimeToSlaves(); // отправляем время слейвам 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); // обновляем рандомайзер trnd.update(now.hour, now.min, now.sec); // обновляем рандомайзер
if (gotNTP || gotTime) checkWorkTime(); // проверяем расписание, если знаем время
if (now.sec == 0) { // новая минута
if (now.min % 5 == 0) sendTimeToLocals(); // отправляем время каждые 5 мин
if (gotNTP || gotTime) { // если знаем точное время
checkWorkTime(); // проверяем расписание
checkDawn(); // и рассвет
}
}
}
} }
} }
void sendTimeToLocals() {
if (!cfg.WiFimode) sendUDP(6, now.day, now.hour, now.min); // мы - АР
}
// установка времени с мобилы
void setTime(byte day, byte hour, byte min, byte sec) { void setTime(byte day, byte hour, byte min, byte sec) {
if (!cfg.WiFimode || !gotNTP) { // если мы AP или не получили NTP if (!cfg.WiFimode || !gotNTP) { // если мы AP или не получили NTP
now.day = day; now.day = day;
@@ -34,36 +56,8 @@ void setTime(byte day, byte hour, byte min, byte sec) {
} }
} }
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() { 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; int dawnMinute = dawn.hour[now.day] * 60 + dawn.minute[now.day] - dawn.time;
if (dawnMinute < 0) dawnMinute += 1440; if (dawnMinute < 0) dawnMinute += 1440;
if (dawnMinute == now.hour * 60 + now.min) { 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) { bool isWorkTime(byte t, byte from, byte to) {
if (from == to) return 1; if (from == to) return 1;
else if (from < to) { else if (from < to) {

View File

@@ -21,6 +21,9 @@ class timerMillis {
if (_active && millis() - _tmr >= _interval) stop(); if (_active && millis() - _tmr >= _interval) stop();
return _active; return _active;
} }
void force() {
_tmr = millis() - _interval;
}
void reset() { void reset() {
_tmr = millis(); _tmr = millis();
} }