mirror of
https://github.com/AlexGyver/GyverLamp2.git
synced 2025-08-07 08:40:35 +03:00
v0.19b
This commit is contained in:
@@ -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);
|
||||
}
|
||||
|
@@ -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;
|
||||
|
@@ -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();
|
||||
}
|
||||
|
@@ -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() {
|
||||
|
@@ -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();
|
||||
};
|
||||
|
@@ -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;
|
||||
|
@@ -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;
|
||||
};
|
||||
|
@@ -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;
|
||||
|
@@ -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();
|
||||
|
@@ -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)
|
||||
*/
|
||||
|
@@ -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);
|
||||
|
@@ -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));
|
||||
}
|
||||
|
@@ -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;
|
||||
|
@@ -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);
|
||||
|
@@ -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); // пакет вида <GL>,<тип>,<дата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);
|
||||
|
@@ -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");
|
||||
}
|
||||
|
||||
|
@@ -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;
|
||||
}
|
||||
|
@@ -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) {
|
||||
|
@@ -21,6 +21,9 @@ class timerMillis {
|
||||
if (_active && millis() - _tmr >= _interval) stop();
|
||||
return _active;
|
||||
}
|
||||
void force() {
|
||||
_tmr = millis() - _interval;
|
||||
}
|
||||
void reset() {
|
||||
_tmr = millis();
|
||||
}
|
||||
|
Reference in New Issue
Block a user