diff --git a/firmware/GyverLamp_v1.4/EepromManager.h b/firmware/GyverLamp_v1.4/EepromManager.h index 2363275..856de0e 100644 --- a/firmware/GyverLamp_v1.4/EepromManager.h +++ b/firmware/GyverLamp_v1.4/EepromManager.h @@ -40,6 +40,11 @@ * 72-74 3 режим №18: яркость, скорость, масштаб (по одному байту) * 75-77 3 режим №19: яркость, скорость, масштаб (по одному байту) * 78-80 3 режим №20: яркость, скорость, масштаб (по одному байту) + * 81-83 3 режим №21: яркость, скорость, масштаб (по одному байту) + * 84-86 3 режим №22: яркость, скорость, масштаб (по одному байту) + * 87-89 3 режим №23: яркость, скорость, масштаб (по одному байту) + * 90-92 3 режим №24: яркость, скорость, масштаб (по одному байту) + * 93-95 3 режим №25: яркость, скорость, масштаб (по одному байту) * * 111-134 24 настройки режима избранных эффектов (интервал - 2 байта; разброс - 2 байта; вкл/выкл каждого эффекта - 20 (MODE_AMOUNT) байт; вкл/выкл не хранится в EEPROM) * @@ -48,13 +53,12 @@ * 200 1 текущий режим (currentMode) * * Не используются адреса: - * 81-110 30 резерв, можно добавить ещё 10 эффектов + * 96-110 15 резерв, можно добавить ещё 5 эффектов * 135-197 63 если добавить ещё 10 эффектов, начальный адрес неиспользуемой памяти сдвинется с 135 на 145 */ #include #include "Types.h" - #define EEPROM_TOTAL_BYTES_USED (201U) // общий размер используемой EEPROM памяти (сумма всех хранимых настроек + 1 байт) #define EEPROM_ALARM_START_ADDRESS (0U) // начальный адрес в EEPROM памяти для записи настроек будильников #define EEPROM_MODES_START_ADDRESS (21U) // начальный адрес в EEPROM памяти для записи настроек эффектов (яркость, скорость, масштаб) @@ -130,12 +134,12 @@ class EepromManager EEPROM.commit(); } - static void HandleEepromTick(bool* settChanged, uint32_t* eepromTimer, int8_t* currentMode, ModeType modes[], void (*saveFavoritesSettings)()) + static void HandleEepromTick(bool* settChanged, uint32_t* eepromTimeout, int8_t* currentMode, ModeType modes[], void (*saveFavoritesSettings)()) { - if (*settChanged && millis() - *eepromTimer > EEPROM_WRITE_DELAY) + if (*settChanged && millis() - *eepromTimeout > EEPROM_WRITE_DELAY) { *settChanged = false; - *eepromTimer = millis(); + *eepromTimeout = millis(); SaveModesSettings(currentMode, modes); if (EEPROM.read(EEPROM_CURRENT_MODE_ADDRESS) != *currentMode) { diff --git a/firmware/GyverLamp_v1.4/GyverLamp_v1.4.ino b/firmware/GyverLamp_v1.4/GyverLamp_v1.4.ino index 778aafa..60968ac 100644 --- a/firmware/GyverLamp_v1.4/GyverLamp_v1.4.ino +++ b/firmware/GyverLamp_v1.4/GyverLamp_v1.4.ino @@ -49,6 +49,10 @@ - Реорганизован код, исправлены ошибки --- 28.08.2019 - Добавлен вызов режима обновления модуля esp из android приложения + --- 30.08.2019 + - Эффект "Светлячки со шлейфами" переименован в "Угасающие пиксели" + - Добавлены 4 новых эффекта: "Радуга диагональная", "Метель", "Звездопад" и "Светлячки со шлейфами" (новый) + - Исправлены ошибки */ // Ссылка для менеджера плат: @@ -58,41 +62,41 @@ // ============= НАСТРОЙКИ ============= // --- ВРЕМЯ --------------------------- #define USE_NTP // закомментировать или удалить эту строку, если нужно, чтобы устройство не лезло в интернет -#define GMT (3) // часовой пояс (москва 3) -#define NTP_ADDRESS ("ntp2.colocall.net") // сервер времени -#define NTP_INTERVAL (30UL * 60UL * 1000UL) // интервал синхронизации времени (30 минут) +#define GMT (3) // часовой пояс (москва 3) +#define NTP_ADDRESS ("ntp2.colocall.net") // сервер времени +#define NTP_INTERVAL (30UL * 60UL * 1000UL) // интервал синхронизации времени (30 минут) // --- РАССВЕТ ------------------------- -#define DAWN_BRIGHT (200U) // максимальная яркость рассвета (0-255) -#define DAWN_TIMEOUT (1U) // сколько рассвет светит после времени будильника, минут +#define DAWN_BRIGHT (200U) // максимальная яркость рассвета (0-255) +#define DAWN_TIMEOUT (1U) // сколько рассвет светит после времени будильника, минут // --- МАТРИЦА ------------------------- -#define BRIGHTNESS (40U) // стандартная маскимальная яркость (0-255) -#define CURRENT_LIMIT (2000U) // лимит по току в миллиамперах, автоматически управляет яркостью (пожалей свой блок питания!) 0 - выключить лимит +#define BRIGHTNESS (40U) // стандартная маскимальная яркость (0-255) +#define CURRENT_LIMIT (2000U) // лимит по току в миллиамперах, автоматически управляет яркостью (пожалей свой блок питания!) 0 - выключить лимит -#define WIDTH (16U) // ширина матрицы -#define HEIGHT (16U) // высота матрицы +#define WIDTH (16U) // ширина матрицы +#define HEIGHT (16U) // высота матрицы -#define COLOR_ORDER (GRB) // порядок цветов на ленте. Если цвет отображается некорректно - меняйте. Начать можно с RGB +#define COLOR_ORDER (GRB) // порядок цветов на ленте. Если цвет отображается некорректно - меняйте. Начать можно с RGB -#define MATRIX_TYPE (0U) // тип матрицы: 0 - зигзаг, 1 - параллельная -#define CONNECTION_ANGLE (0U) // угол подключения: 0 - левый нижний, 1 - левый верхний, 2 - правый верхний, 3 - правый нижний -#define STRIP_DIRECTION (0U) // направление ленты из угла: 0 - вправо, 1 - вверх, 2 - влево, 3 - вниз +#define MATRIX_TYPE (0U) // тип матрицы: 0 - зигзаг, 1 - параллельная +#define CONNECTION_ANGLE (0U) // угол подключения: 0 - левый нижний, 1 - левый верхний, 2 - правый верхний, 3 - правый нижний +#define STRIP_DIRECTION (0U) // направление ленты из угла: 0 - вправо, 1 - вверх, 2 - влево, 3 - вниз // при неправильной настройке матрицы вы получите предупреждение "Wrong matrix parameters! Set to default" // шпаргалка по настройке матрицы здесь! https://alexgyver.ru/matrix_guide/ // --- ESP ----------------------------- -#define ESP_MODE (1U) // 0U - WiFi точка доступа, 1U - клиент WiFi (подключение к роутеру) +#define ESP_MODE (1U) // 0U - WiFi точка доступа, 1U - клиент WiFi (подключение к роутеру) #define ESP_USE_BUTTON // если строка не закомментирована, должна быть подключена кнопка (иначе ESP может регистрировать "фантомные" нажатия и некорректно устанавливать яркость) -#define ESP_HTTP_PORT (80U) // номер порта, который будет использоваться во время первой утановки имени WiFi сети (и пароля), к которой потом будет подключаться лампа в режиме WiFi клиента (лучше не менять) -#define ESP_UDP_PORT (8888U) // номер порта, который будет "слушать" UDP сервер во время работы лампы как в режиме WiFi точки доступа, так и в режиме WiFi клиента (лучше не менять) -#define ESP_CONN_TIMEOUT (7U) // время в секундах (ДОЛЖНО БЫТЬ МЕНЬШЕ 8, иначе сработает WDT), которое ESP будет пытаться подключиться к WiFi сети, после его истечения автоматически развернёт WiFi точку доступа -#define ESP_CONF_TIMEOUT (300U) // время в секундах, которое ESP будет ждать ввода SSID и пароля WiFi сети роутера в конфигурационном режиме, после его истечения ESP перезагружается +#define ESP_HTTP_PORT (80U) // номер порта, который будет использоваться во время первой утановки имени WiFi сети (и пароля), к которой потом будет подключаться лампа в режиме WiFi клиента (лучше не менять) +#define ESP_UDP_PORT (8888U) // номер порта, который будет "слушать" UDP сервер во время работы лампы как в режиме WiFi точки доступа, так и в режиме WiFi клиента (лучше не менять) +#define ESP_CONN_TIMEOUT (7U) // время в секундах (ДОЛЖНО БЫТЬ МЕНЬШЕ 8, иначе сработает WDT), которое ESP будет пытаться подключиться к WiFi сети, после его истечения автоматически развернёт WiFi точку доступа +#define ESP_CONF_TIMEOUT (300U) // время в секундах, которое ESP будет ждать ввода SSID и пароля WiFi сети роутера в конфигурационном режиме, после его истечения ESP перезагружается #define GENERAL_DEBUG // если строка не закомментирована, будут выводиться отладочные сообщения -#define WIFIMAN_DEBUG (true) // вывод отладочных сообщений при подключении к WiFi сети: true - выводятся, false - не выводятся; настройка не зависит от GENERAL_DEBUG +#define WIFIMAN_DEBUG (true) // вывод отладочных сообщений при подключении к WiFi сети: true - выводятся, false - не выводятся; настройка не зависит от GENERAL_DEBUG #define OTA // если строка не закомментирована, модуль будет ждать два последдовательных запроса пользователя на прошивку по воздуху (см. документацию в "шапке") #ifdef OTA -#define ESP_OTA_PORT (8266U) // номер порта, который будет "прослушиваться" в ожидании команды прошивки по воздуху +#define ESP_OTA_PORT (8266U) // номер порта, который будет "прослушиваться" в ожидании команды прошивки по воздуху #endif // --- ESP (WiFi клиент) --------------- @@ -100,21 +104,47 @@ uint8_t STA_STATIC_IP[] = {}; // статичес // SSID WiFi сети и пароль будут запрошены WiFi Manager'ом в режиме WiFi точки доступа, нет способа захардкодить их в прошивке // --- AP (WiFi точка доступа) --- -#define AP_NAME ("LedLamp") // имя WiFi точки доступа, используется как при запросе SSID и пароля WiFi сети роутера, так и при работе в режиме ESP_MODE = 0 -#define AP_PASS ("31415926") // пароль WiFi точки доступа -uint8_t AP_STATIC_IP[] = {192, 168, 4, 1}; // статический IP точки доступа (лучше не менять) +#define AP_NAME ("LedLamp") // имя WiFi точки доступа, используется как при запросе SSID и пароля WiFi сети роутера, так и при работе в режиме ESP_MODE = 0 +#define AP_PASS ("31415926") // пароль WiFi точки доступа +uint8_t AP_STATIC_IP[] = {192, 168, 4, 1}; // статический IP точки доступа (лучше не менять) // ============= ДЛЯ РАЗРАБОТЧИКОВ ===== -#define LED_PIN (2U) // пин ленты -#define BTN_PIN (4U) // пин кнопки -#define MODE_AMOUNT (20U) // количество режимов +#define LED_PIN (2U) // пин ленты +#define BTN_PIN (4U) // пин кнопки + // список и номера эффектов ниже в списке согласованы с android приложением! +#define EFF_SPARKLES (0U) // Конфетти +#define EFF_FIRE (1U) // Огонь +#define EFF_RAINBOW_VER (2U) // Радуга вертикальная +#define EFF_RAINBOW_HOR (3U) // Радуга горизонтальная +#define EFF_RAINBOW_DIAG (4U) // Радуга диагональная +#define EFF_COLORS (5U) // Смена цвета +#define EFF_MADNESS (6U) // Безумие 3D +#define EFF_CLOUDS (7U) // Облака 3D +#define EFF_LAVA (8U) // Лава 3D +#define EFF_PLASMA (9U) // Плазма 3D +#define EFF_RAINBOW (10U) // Радуга 3D +#define EFF_RAINBOW_STRIPE (11U) // Павлин 3D +#define EFF_ZEBRA (12U) // Зебра 3D +#define EFF_FOREST (13U) // Лес 3D +#define EFF_OCEAN (14U) // Океан 3D +#define EFF_COLOR (15U) // Цвет +#define EFF_SNOW (16U) // Снегопад +#define EFF_SNOWSTORM (17U) // Метель +#define EFF_STARFALL (18U) // Звездопад +#define EFF_MATRIX (19U) // Матрица +#define EFF_LIGHTERS (20U) // Светлячки +#define EFF_LIGHTER_TRACES (21U) // Светлячки со шлейфом +#define EFF_FADING_PIXELS (22U) // Угасающие пиксели +#define EFF_CUBE (23U) // Блуждающий кубик +#define EFF_WHITE_COLOR (24U) // Белый свет +#define MODE_AMOUNT (25U) // количество режимов -#define NUM_LEDS (WIDTH * HEIGHT) -#define SEGMENTS (1U) // диодов в одном "пикселе" (для создания матрицы из кусков ленты) +#define NUM_LEDS (WIDTH * HEIGHT) +#define SEGMENTS (1U) // диодов в одном "пикселе" (для создания матрицы из кусков ленты) // --- БИБЛИОТЕКИ ---------------------- -#define FASTLED_INTERRUPT_RETRY_COUNT (0U) -#define FASTLED_ALLOW_INTERRUPTS (0U) +#define FASTLED_INTERRUPT_RETRY_COUNT (0U) +#define FASTLED_ALLOW_INTERRUPTS (0U) #define FASTLED_ESP8266_RAW_PIN_ORDER #include "Types.h" @@ -178,14 +208,9 @@ bool manualOff = false; int8_t currentMode = 0; bool loadingFlag = true; bool ONflag = true; -uint32_t eepromTimer; +uint32_t eepromTimeout; bool settChanged = false; -// Конфетти, Огонь, Радуга вертикальная, Радуга горизонтальная, Смена цвета, -// Безумие 3D, Облака 3D, Лава 3D, Плазма 3D, Радуга 3D, -// Павлин 3D, Зебра 3D, Лес 3D, Океан 3D, -// Цвет, Снег, Матрица, Светлячки, Светлячки со шлейфом, Белый свет - unsigned char matrixValue[8][16]; bool TimerManager::TimerRunning = false; @@ -196,7 +221,7 @@ uint64_t TimerManager::TimeToFire = 0ULL; bool FavoritesManager::FavoritesRunning = false; uint16_t FavoritesManager::Interval = DEFAULT_FAVORITES_INTERVAL; uint16_t FavoritesManager::Dispersion = DEFAULT_FAVORITES_DISPERSION; -uint8_t FavoritesManager::FavoriteModes[MODE_AMOUNT] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; +uint8_t FavoritesManager::FavoriteModes[MODE_AMOUNT] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; uint32_t FavoritesManager::nextModeAt = 0UL; @@ -308,7 +333,6 @@ void setup() #endif memset(matrixValue, 0, sizeof(matrixValue)); - randomSeed(micros()); } @@ -317,7 +341,7 @@ void loop() { parseUDP(); effectsTick(); - EepromManager::HandleEepromTick(&settChanged, &eepromTimer, ¤tMode, modes, &(FavoritesManager::SaveFavoritesToEeprom)); + EepromManager::HandleEepromTick(&settChanged, &eepromTimeout, ¤tMode, modes, &(FavoritesManager::SaveFavoritesToEeprom)); #ifdef USE_NTP timeTick(); #endif diff --git a/firmware/GyverLamp_v1.4/OtaManager.h b/firmware/GyverLamp_v1.4/OtaManager.h index ef8022f..8e29f61 100644 --- a/firmware/GyverLamp_v1.4/OtaManager.h +++ b/firmware/GyverLamp_v1.4/OtaManager.h @@ -16,7 +16,6 @@ #include #include - #define CONFIRMATION_TIMEOUT (30U) // время в сеундах, в течение которого нужно дважды подтвердить старт обновлениЯ по воздуху (иначе сброс в None) enum OtaPhase // определение стадий процесса обновления по воздуху: нет, получено первое подтверждение, получено второе подтверждение, получено второе подтверждение - в процессе, обновление окончено @@ -28,6 +27,7 @@ enum OtaPhase // определе Done }; + class OtaManager { public: diff --git a/firmware/GyverLamp_v1.4/button.ino b/firmware/GyverLamp_v1.4/button.ino index 1e219a8..da4d76e 100644 --- a/firmware/GyverLamp_v1.4/button.ino +++ b/firmware/GyverLamp_v1.4/button.ino @@ -27,7 +27,7 @@ void buttonTick() FastLED.setBrightness(modes[currentMode].Brightness); loadingFlag = true; settChanged = true; - eepromTimer = millis(); + eepromTimeout = millis(); FastLED.clear(); delay(1); } @@ -38,7 +38,7 @@ void buttonTick() FastLED.setBrightness(modes[currentMode].Brightness); loadingFlag = true; settChanged = true; - eepromTimer = millis(); + eepromTimeout = millis(); FastLED.clear(); delay(1); } @@ -48,7 +48,7 @@ void buttonTick() #ifdef OTA if (otaManager.RequestOtaUpdate()) { - currentMode = 16; // принудительное включение режима "Матрица" для индикации перехода в режим обновления по воздуху + currentMode = EFF_MATRIX; // принудительное включение режима "Матрица" для индикации перехода в режим обновления по воздуху FastLED.clear(); delay(1); } @@ -76,7 +76,7 @@ void buttonTick() } FastLED.setBrightness(modes[currentMode].Brightness); settChanged = true; - eepromTimer = millis(); + eepromTimeout = millis(); #ifdef GENERAL_DEBUG Serial.printf("New brightness value: %d\n", modes[currentMode].Brightness); diff --git a/firmware/GyverLamp_v1.4/effectTicker.ino b/firmware/GyverLamp_v1.4/effectTicker.ino index 07d7d5b..d7e3a8a 100644 --- a/firmware/GyverLamp_v1.4/effectTicker.ino +++ b/firmware/GyverLamp_v1.4/effectTicker.ino @@ -9,27 +9,32 @@ void effectsTick() effTimer = millis(); switch (currentMode) { - case 0: sparklesRoutine(); break; - case 1: fireRoutine(); break; - case 2: rainbowVertical(); break; - case 3: rainbowHorizontal(); break; - case 4: colorsRoutine(); break; - case 5: madnessNoise(); break; - case 6: cloudNoise(); break; - case 7: lavaNoise(); break; - case 8: plasmaNoise(); break; - case 9: rainbowNoise(); break; - case 10: rainbowStripeNoise(); break; - case 11: zebraNoise(); break; - case 12: forestNoise(); break; - case 13: oceanNoise(); break; - case 14: colorRoutine(); break; - case 15: snowRoutine(); break; - case 16: matrixRoutine(); break; - case 17: lightersRoutine(); break; - case 18: lightBalls(); break; - case 19: whiteColor(); break; - default: break; + case EFF_SPARKLES: sparklesRoutine(); break; + case EFF_FIRE: fireRoutine(); break; + case EFF_RAINBOW_VER: rainbowVerticalRoutine(); break; + case EFF_RAINBOW_HOR: rainbowHorizontalRoutine(); break; + case EFF_RAINBOW_DIAG: rainbowDiagonalRoutine(); break; + case EFF_COLORS: colorsRoutine(); break; + case EFF_MADNESS: madnessNoiseRoutine(); break; + case EFF_CLOUDS: cloudsNoiseRoutine(); break; + case EFF_LAVA: lavaNoiseRoutine(); break; + case EFF_PLASMA: plasmaNoiseRoutine(); break; + case EFF_RAINBOW: rainbowNoiseRoutine(); break; + case EFF_RAINBOW_STRIPE: rainbowStripeNoiseRoutine(); break; + case EFF_ZEBRA: zebraNoiseRoutine(); break; + case EFF_FOREST: forestNoiseRoutine(); break; + case EFF_OCEAN: oceanNoiseRoutine(); break; + case EFF_COLOR: colorRoutine(); break; + case EFF_SNOW: snowRoutine(); break; + case EFF_SNOWSTORM: snowStormRoutine(); break; + case EFF_STARFALL: starfallRoutine(); break; + case EFF_MATRIX: matrixRoutine(); break; + case EFF_LIGHTERS: lightersRoutine(); break; + case EFF_LIGHTER_TRACES: ballsRoutine(); break; + case EFF_FADING_PIXELS: lightBallsRoutine(); break; + case EFF_CUBE: ballRoutine(); break; + case EFF_WHITE_COLOR: whiteColorRoutine(); break; + default: break; } FastLED.show(); } diff --git a/firmware/GyverLamp_v1.4/effects.ino b/firmware/GyverLamp_v1.4/effects.ino index 1202914..cf714f0 100644 --- a/firmware/GyverLamp_v1.4/effects.ino +++ b/firmware/GyverLamp_v1.4/effects.ino @@ -1,16 +1,19 @@ // ============= ЭФФЕКТЫ =============== // ------------- конфетти -------------- +#define FADE_OUT_SPEED (70U) // скорость затухания void sparklesRoutine() { - for (uint8_t i = 0; i < modes[0].Scale; i++) + for (uint8_t i = 0; i < modes[EFF_SPARKLES].Scale; i++) { uint8_t x = random(0, WIDTH); uint8_t y = random(0, HEIGHT); if (getPixColorXY(x, y) == 0) + { leds[getPixelNumber(x, y)] = CHSV(random(0, 255), 255, 255); + } } - fader(70); + fader(FADE_OUT_SPEED); } // функция плавного угасания цвета для всех пикселей @@ -25,7 +28,7 @@ void fader(uint8_t step) } } -void fadePixel(uint8_t i, uint8_t j, uint8_t step) // новый фейдер +void fadePixel(uint8_t i, uint8_t j, uint8_t step) // новый фейдер { int32_t pixelNum = getPixelNumber(i, j); if (getPixColor(pixelNum) == 0) return; @@ -43,7 +46,7 @@ void fadePixel(uint8_t i, uint8_t j, uint8_t step) // новы } // ------------- огонь ----------------- -#define SPARKLES 1 // вылетающие угольки вкл выкл +#define SPARKLES (1U) // вылетающие угольки вкл выкл unsigned char line[WIDTH]; int32_t pcnt = 0; @@ -59,9 +62,9 @@ const unsigned char valueMask[8][16] PROGMEM = {255, 160, 128, 96 , 96 , 128, 160, 255, 255, 160, 128, 96 , 96 , 128, 160, 255}, {255, 192, 160, 128, 128, 160, 192, 255, 255, 192, 160, 128, 128, 160, 192, 255} }; - //these are the hues for the fire, //should be between 0 (red) to about 25 (yellow) + const unsigned char hueMask[8][16] PROGMEM = { {1 , 11, 19, 25, 25, 22, 11, 1 , 1 , 11, 19, 25, 25, 22, 11, 1 }, @@ -79,8 +82,9 @@ void fireRoutine() if (loadingFlag) { loadingFlag = false; - //FastLED.clear(); + FastLED.clear(); generateLine(); + memset(matrixValue, 0, sizeof(matrixValue)); } if (pcnt >= 100) { @@ -108,7 +112,7 @@ void shiftUp() for (uint8_t x = 0; x < WIDTH; x++) { uint8_t newX = x; - if (x > 15) newX = x - 15; + if (x > 15) newX = x % 16; if (y > 7) continue; matrixValue[y][newX] = matrixValue[y - 1][newX]; } @@ -117,7 +121,7 @@ void shiftUp() for (uint8_t x = 0; x < WIDTH; x++) { uint8_t newX = x; - if (x > 15) newX = x - 15; + if (x > 15) newX = x % 16; matrixValue[0][newX] = line[newX]; } } @@ -135,7 +139,7 @@ void drawFrame(int32_t pcnt) for (unsigned char x = 0; x < WIDTH; x++) { uint8_t newX = x; - if (x > 15) newX = x - 15; + if (x > 15) newX = x % 16; if (y < 8) { nextv = @@ -144,7 +148,7 @@ void drawFrame(int32_t pcnt) - pgm_read_byte(&(valueMask[y][newX])); CRGB color = CHSV( - modes[1].Scale * 2.5 + pgm_read_byte(&(hueMask[y][newX])), // H + modes[EFF_FIRE].Scale * 2.5 + pgm_read_byte(&(hueMask[y][newX])), // H 255, // S (uint8_t)max(0, nextv) // V ); @@ -170,47 +174,77 @@ void drawFrame(int32_t pcnt) for (unsigned char x = 0; x < WIDTH; x++) { uint8_t newX = x; - if (x > 15) newX = x - 15; + if (x > 15) newX = x % 16; CRGB color = CHSV( - modes[1].Scale * 2.5 + pgm_read_byte(&(hueMask[0][newX])), // H + modes[EFF_FIRE].Scale * 2.5 + pgm_read_byte(&(hueMask[0][newX])), // H 255, // S (uint8_t)(((100.0 - pcnt) * matrixValue[0][newX] + pcnt * line[newX]) / 100.0) // V ); - leds[getPixelNumber(newX, 0)] = color; + //leds[getPixelNumber(newX, 0)] = color; // на форуме пишут что это ошибка - вместо newX должно быть x, иначе + leds[getPixelNumber(x, 0)] = color; // на матрицах шире 16 столбцов нижний правый угол неработает } } +// ------------- радуга вертикальная ---------------- uint8_t hue; -// ------------- радуга ---------------- -void rainbowVertical() +void rainbowVerticalRoutine() { - hue += 2; + hue += 4; for (uint8_t j = 0; j < HEIGHT; j++) { - CHSV thisColor = CHSV((uint8_t)(hue + j * modes[2].Scale), 255, 255); + CHSV thisColor = CHSV((uint8_t)(hue + j * modes[EFF_RAINBOW_VER].Scale), 255, 255); for (uint8_t i = 0; i < WIDTH; i++) + { drawPixelXY(i, j, thisColor); + } } } -void rainbowHorizontal() +// ------------- радуга горизонтальная ---------------- +void rainbowHorizontalRoutine() { - hue += 2; + hue += 4; for (uint8_t i = 0; i < WIDTH; i++) { - CHSV thisColor = CHSV((uint8_t)(hue + i * modes[3].Scale), 255, 255); + CHSV thisColor = CHSV((uint8_t)(hue + i * modes[EFF_RAINBOW_HOR].Scale), 255, 255); for (uint8_t j = 0; j < HEIGHT; j++) - drawPixelXY(i, j, thisColor); //leds[getPixelNumber(i, j)] = thisColor; + { + drawPixelXY(i, j, thisColor); + } + } +} + +// ------------- радуга дигональная ------------- +void rainbowDiagonalRoutine() +{ + if (loadingFlag) + { + loadingFlag = false; + FastLED.clear(); + } + + hue += 4; + for (uint8_t i = 0; i < WIDTH; i++) + { + for (uint8_t j = 0; j < HEIGHT; j++) + { + CRGB thisColor = CHSV(constrain((uint8_t)(hue + (float)(100 / modes[EFF_RAINBOW_DIAG].Scale) * (float)(WIDTH / HEIGHT * i + j) * (float)(255 / maxDim)), 0, 255), 255, 255); + drawPixelXY(i, j, thisColor); + } } } // ------------- цвета ----------------- void colorsRoutine() { - hue += modes[4].Scale; - for (int32_t i = 0; i < NUM_LEDS; i++) + if (loadingFlag) { - leds[i] = CHSV(hue, 255, 255); + hue += modes[EFF_COLORS].Scale; + + for (int16_t i = 0; i < NUM_LEDS; i++) + { + leds[i] = CHSV(hue, 255, 255); + } } } @@ -219,11 +253,11 @@ void colorRoutine() { for (int32_t i = 0; i < NUM_LEDS; i++) { - leds[i] = CHSV(modes[14].Scale * 2.5, 255, 255); + leds[i] = CHSV(modes[EFF_COLOR].Scale * 2.5, 255, 255); } } -// ------------- снегопад 2.0 ---------- +// ------------- снегопад ---------- void snowRoutine() { // сдвигаем всё вниз @@ -239,13 +273,123 @@ void snowRoutine() { // заполняем случайно верхнюю строку // а также не даём двум блокам по вертикали вместе быть - if (getPixColorXY(x, HEIGHT - 2) == 0 && (random(0, 100 - modes[15].Scale) == 0)) + if (getPixColorXY(x, HEIGHT - 2) == 0 && (random(0, 100 - modes[EFF_SNOW].Scale) == 0)) drawPixelXY(x, HEIGHT - 1, 0xE0FFFF - 0x101010 * random(0, 4)); else drawPixelXY(x, HEIGHT - 1, 0x000000); } } +// ------------- метель ------------- +#define SNOW_DENSE (60U) // плотность снега +#define SNOW_TAIL_STEP (100U) // длина хвоста +#define SNOW_SATURATION (0U) // насыщенность (от 0 до 255) +void snowStormRoutine() +{ + if (loadingFlag) + { + loadingFlag = false; + FastLED.clear(); + } + + // заполняем головами комет левую и верхнюю линию + for (uint8_t i = HEIGHT / 2; i < HEIGHT; i++) + { + if (getPixColorXY(0, i) == 0 && + (random(0, SNOW_DENSE) == 0) && + getPixColorXY(0, i + 1) == 0 && + getPixColorXY(0, i - 1) == 0) + { + leds[getPixelNumber(0, i)] = CHSV(random(0, 200), SNOW_SATURATION, 255); + } + } + + for (uint8_t i = 0; i < WIDTH / 2; i++) + { + if (getPixColorXY(i, HEIGHT - 1) == 0 && + (random(0, map(modes[EFF_SNOWSTORM].Scale, 0, 255, 10, 120)) == 0) && + getPixColorXY(i + 1, HEIGHT - 1) == 0 && + getPixColorXY(i - 1, HEIGHT - 1) == 0) + { + leds[getPixelNumber(i, HEIGHT - 1)] = CHSV(random(0, 200), SNOW_SATURATION, 255); + } + } + + // сдвигаем по диагонали + for (uint8_t y = 0; y < HEIGHT - 1; y++) + { + for (uint8_t x = WIDTH - 1; x > 0; x--) + { + drawPixelXY(x, y, getPixColorXY(x - 1, y + 1)); + } + } + + // уменьшаем яркость левой и верхней линии, формируем "хвосты" + for (uint8_t i = HEIGHT / 2; i < HEIGHT; i++) + { + fadePixel(0, i, SNOW_TAIL_STEP); + } + for (uint8_t i = 0; i < WIDTH / 2; i++) + { + fadePixel(i, HEIGHT - 1, SNOW_TAIL_STEP); + } +} + +// ------------- звездопад ------------- +#define STAR_DENSE (60U) // плотность комет +#define STAR_TAIL_STEP (100U) // длина хвоста кометы +#define STAR_SATURATION (150U) // насыщенность кометы (от 0 до 255) +void starfallRoutine() +{ + if (loadingFlag) + { + loadingFlag = false; + FastLED.clear(); + } + + // заполняем головами комет левую и верхнюю линию + for (uint8_t i = HEIGHT / 2; i < HEIGHT; i++) + { + if (getPixColorXY(0, i) == 0 && + (random(0, STAR_DENSE) == 0) && + getPixColorXY(0, i + 1) == 0 && + getPixColorXY(0, i - 1) == 0) + { + leds[getPixelNumber(0, i)] = CHSV(random(0, 200), STAR_SATURATION, 255); + } + } + + for (uint8_t i = 0; i < WIDTH / 2; i++) + { + if (getPixColorXY(i, HEIGHT - 1) == 0 && + (random(0, map(modes[EFF_STARFALL].Scale, 0, 255, 10, 120)) == 0) && + getPixColorXY(i + 1, HEIGHT - 1) == 0 && + getPixColorXY(i - 1, HEIGHT - 1) == 0) + { + leds[getPixelNumber(i, HEIGHT - 1)] = CHSV(random(0, 200), STAR_SATURATION, 255); + } + } + + // сдвигаем по диагонали + for (uint8_t y = 0; y < HEIGHT - 1; y++) + { + for (uint8_t x = WIDTH - 1; x > 0; x--) + { + drawPixelXY(x, y, getPixColorXY(x - 1, y + 1)); + } + } + + // уменьшаем яркость левой и верхней линии, формируем "хвосты" + for (uint8_t i = HEIGHT / 2; i < HEIGHT; i++) + { + fadePixel(0, i, STAR_TAIL_STEP); + } + for (uint8_t i = 0; i < WIDTH / 2; i++) + { + fadePixel(i, HEIGHT - 1, STAR_TAIL_STEP); + } +} + // ------------- матрица --------------- void matrixRoutine() { @@ -254,7 +398,7 @@ void matrixRoutine() // заполняем случайно верхнюю строку uint32_t thisColor = getPixColorXY(x, HEIGHT - 1); if (thisColor == 0) - drawPixelXY(x, HEIGHT - 1, 0x00FF00 * (random(0, 100 - modes[16].Scale) == 0)); + drawPixelXY(x, HEIGHT - 1, 0x00FF00 * (random(0, 100 - modes[EFF_MATRIX].Scale) == 0)); else if (thisColor < 0x002000) drawPixelXY(x, HEIGHT - 1, 0); else @@ -271,17 +415,15 @@ void matrixRoutine() } } -// ------------- светляки -------------- -#define LIGHTERS_AM 100 +// ------------- светлячки -------------- +#define LIGHTERS_AM (100) int32_t lightersPos[2][LIGHTERS_AM]; int8_t lightersSpeed[2][LIGHTERS_AM]; CHSV lightersColor[LIGHTERS_AM]; uint8_t loopCounter; - int32_t angle[LIGHTERS_AM]; int32_t speedV[LIGHTERS_AM]; int8_t angleSpeed[LIGHTERS_AM]; - void lightersRoutine() { if (loadingFlag) @@ -299,7 +441,7 @@ void lightersRoutine() } FastLED.clear(); if (++loopCounter > 20) loopCounter = 0; - for (uint8_t i = 0; i < modes[17].Scale; i++) + for (uint8_t i = 0; i < modes[EFF_LIGHTERS].Scale; i++) { if (loopCounter == 0) // меняем скорость каждые 255 отрисовок { @@ -329,11 +471,74 @@ void lightersRoutine() } } +// ------------- светлячки со шлейфом ------------- +#define BALLS_AMOUNT (3U) // количество "шариков" +#define CLEAR_PATH (1U) // очищать путь +#define BALL_TRACK (1U) // (0 / 1) - вкл/выкл следы шариков +#define TRACK_STEP (70U) // длина хвоста шарика (чем больше цифра, тем хвост короче) +int16_t coord[BALLS_AMOUNT][2]; +int8_t vector[BALLS_AMOUNT][2]; +CRGB ballColors[BALLS_AMOUNT]; +void ballsRoutine() +{ + if (loadingFlag) + { + loadingFlag = false; -// ------------- LightBalls (светлячки со шлейфом) ------------- + for (byte j = 0; j < BALLS_AMOUNT; j++) + { + int8_t sign; + // забиваем случайными данными + coord[j][0] = WIDTH / 2 * 10; + random(0, 2) ? sign = 1 : sign = -1; + vector[j][0] = random(4, 15) * sign; + coord[j][1] = HEIGHT / 2 * 10; + random(0, 2) ? sign = 1 : sign = -1; + vector[j][1] = random(4, 15) * sign; + ballColors[j] = CHSV(random(0, 9) * 28, 255, 255); + } + } + + if (!BALL_TRACK) // режим без следов шариков + { + FastLED.clear(); + } + else // режим со следами + { + fader(TRACK_STEP); + } + + // движение шариков + for (uint8_t j = 0; j < BALLS_AMOUNT; j++) + { + // движение шариков + for (uint8_t i = 0; i < 2; i++) + { + coord[j][i] += vector[j][i]; + if (coord[j][i] < 0) + { + coord[j][i] = 0; + vector[j][i] = -vector[j][i]; + } + } + + if (coord[j][0] > (WIDTH - 1) * 10) + { + coord[j][0] = (WIDTH - 1) * 10; + vector[j][0] = -vector[j][0]; + } + if (coord[j][1] > (HEIGHT - 1) * 10) + { + coord[j][1] = (HEIGHT - 1) * 10; + vector[j][1] = -vector[j][1]; + } + leds[getPixelNumber(coord[j][0] / 10, coord[j][1] / 10)] = ballColors[j]; + } +} + +// ------------- угасающие пиксели ------------- const uint8_t BorderWidth = 2; - -void lightBalls() +void lightBallsRoutine() { // Apply some blurring to whatever's already on the matrix // Note that we never actually clear the matrix, we just constantly @@ -375,16 +580,83 @@ uint16_t XY(uint8_t x, uint8_t y) return i; } -// ------------- белый свет ------------- -void whiteColor() +// ------------- блуждающий кубик ------------- +#define RANDOM_COLOR (1U) // случайный цвет при отскоке +int16_t coordB[2]; +int8_t vectorB[2]; +CRGB ballColor; +int8_t ballSize; +void ballRoutine() { - for (int i = 0; i < NUM_LEDS; i++) + if (loadingFlag) { - leds[i] = CHSV(0, 0, 255); + loadingFlag = false; + //FastLED.clear(); + + for (uint8_t i = 0; i < 2; i++) + { + coordB[i] = WIDTH / 2 * 10; + vectorB[i] = random(8, 20); + ballColor = CHSV(random(0, 9) * 28, 255, 255); + } + } + + ballSize = map(modes[EFF_CUBE].Scale, 0, 255, 2, max((int16_t)min(WIDTH,HEIGHT) / 3, 2)); + for (uint8_t i = 0; i < 2; i++) + { + coordB[i] += vectorB[i]; + if (coordB[i] < 0) + { + coordB[i] = 0; + vectorB[i] = -vectorB[i]; + if (RANDOM_COLOR) ballColor = CHSV(random(0, 9) * 28, 255, 255); + //vectorB[i] += random(0, 6) - 3; + } + } + if (coordB[0] > (WIDTH - ballSize) * 10) + { + coordB[0] = (WIDTH - ballSize) * 10; + vectorB[0] = -vectorB[0]; + if (RANDOM_COLOR) ballColor = CHSV(random(0, 9) * 28, 255, 255); + //vectorB[0] += random(0, 6) - 3; + } + if (coordB[1] > (HEIGHT - ballSize) * 10) + { + coordB[1] = (HEIGHT - ballSize) * 10; + vectorB[1] = -vectorB[1]; + if (RANDOM_COLOR) + { + ballColor = CHSV(random(0, 9) * 28, 255, 255); + } + //vectorB[1] += random(0, 6) - 3; + } + FastLED.clear(); + for (uint8_t i = 0; i < ballSize; i++) + { + for (uint8_t j = 0; j < ballSize; j++) + { + leds[getPixelNumber(coordB[0] / 10 + i, coordB[1] / 10 + j)] = ballColor; + } + } +} + +// ------------- белый свет ------------- +void whiteColorRoutine() +{ + if (loadingFlag) + { + loadingFlag = false; + FastLED.clear(); + + for (int16_t i = 0; i < NUM_LEDS; i++) + { + leds[i] = CHSV(0, 0, 255); + } } } /* + * устарело void lightersRoutine() { if (loadingFlag) @@ -406,7 +678,7 @@ void lightersRoutine() FastLED.clear(); if (++loopCounter > 20) loopCounter = 0; - for (uint8_t i = 0; i < modes[17].scale; i++) + for (uint8_t i = 0; i < modes[EFF_LIGHTER_TRACES].scale; i++) { if (loopCounter == 0) // меняем скорость каждые 255 отрисовок { diff --git a/firmware/GyverLamp_v1.4/noiseEffects.ino b/firmware/GyverLamp_v1.4/noiseEffects.ino index 32f8b82..12870e4 100644 --- a/firmware/GyverLamp_v1.4/noiseEffects.ino +++ b/firmware/GyverLamp_v1.4/noiseEffects.ino @@ -1,4 +1,5 @@ // ************* НАСТРОЙКИ ************* +/* // "масштаб" эффектов. Чем меньше, тем крупнее! #define MADNESS_SCALE 100 #define CLOUD_SCALE 30 @@ -9,6 +10,7 @@ #define ZEBRA_SCALE 30 #define FOREST_SCALE 120 #define OCEAN_SCALE 90 +*/ // ************* ДЛЯ РАЗРАБОТЧИКОВ ***** @@ -32,13 +34,13 @@ CRGBPalette16 currentPalette(PartyColors_p); uint8_t colorLoop = 1; uint8_t ihue = 0; -void madnessNoise() +void madnessNoiseRoutine() { if (loadingFlag) { loadingFlag = false; - scale = modes[5].Scale; - speed = modes[5].Speed; + scale = modes[EFF_MADNESS].Scale; + speed = modes[EFF_MADNESS].Speed; } fillnoise8(); for (uint8_t i = 0; i < WIDTH; i++) @@ -52,33 +54,33 @@ void madnessNoise() ihue += 1; } -void rainbowNoise() +void rainbowNoiseRoutine() { if (loadingFlag) { loadingFlag = false; currentPalette = RainbowColors_p; - scale = modes[9].Scale; - speed = modes[9].Speed; + scale = modes[EFF_RAINBOW].Scale; + speed = modes[EFF_RAINBOW].Speed; colorLoop = 1; } fillNoiseLED(); } -void rainbowStripeNoise() +void rainbowStripeNoiseRoutine() { if (loadingFlag) { loadingFlag = false; currentPalette = RainbowStripeColors_p; - scale = modes[10].Scale; - speed = modes[10].Speed; + scale = modes[EFF_RAINBOW_STRIPE].Scale; + speed = modes[EFF_RAINBOW_STRIPE].Speed; colorLoop = 1; } fillNoiseLED(); } -void zebraNoise() +void zebraNoiseRoutine() { if (loadingFlag) { @@ -90,74 +92,74 @@ void zebraNoise() currentPalette[4] = CRGB::White; currentPalette[8] = CRGB::White; currentPalette[12] = CRGB::White; - scale = modes[11].Scale; - speed = modes[11].Speed; + scale = modes[EFF_ZEBRA].Scale; + speed = modes[EFF_ZEBRA].Speed; colorLoop = 1; } fillNoiseLED(); } -void forestNoise() +void forestNoiseRoutine() { if (loadingFlag) { loadingFlag = false; currentPalette = ForestColors_p; - scale = modes[12].Scale; - speed = modes[12].Speed; + scale = modes[EFF_FOREST].Scale; + speed = modes[EFF_FOREST].Speed; colorLoop = 0; } fillNoiseLED(); } -void oceanNoise() +void oceanNoiseRoutine() { if (loadingFlag) { loadingFlag = false; currentPalette = OceanColors_p; - scale = modes[13].Scale; - speed = modes[13].Speed; + scale = modes[EFF_OCEAN].Scale; + speed = modes[EFF_OCEAN].Speed; colorLoop = 0; } fillNoiseLED(); } -void plasmaNoise() +void plasmaNoiseRoutine() { if (loadingFlag) { loadingFlag = false; currentPalette = PartyColors_p; - scale = modes[8].Scale; - speed = modes[8].Speed; + scale = modes[EFF_PLASMA].Scale; + speed = modes[EFF_PLASMA].Speed; colorLoop = 1; } fillNoiseLED(); } -void cloudNoise() +void cloudsNoiseRoutine() { if (loadingFlag) { loadingFlag = false; currentPalette = CloudColors_p; - scale = modes[6].Scale; - speed = modes[6].Speed; + scale = modes[EFF_CLOUDS].Scale; + speed = modes[EFF_CLOUDS].Speed; colorLoop = 0; } fillNoiseLED(); } -void lavaNoise() +void lavaNoiseRoutine() { if (loadingFlag) { loadingFlag = false; currentPalette = LavaColors_p; - scale = modes[7].Scale; - speed = modes[7].Speed; + scale = modes[EFF_LAVA].Scale; + speed = modes[EFF_LAVA].Speed; colorLoop = 0; } fillNoiseLED(); diff --git a/firmware/GyverLamp_v1.4/parsing.ino b/firmware/GyverLamp_v1.4/parsing.ino index d9a7272..df3cd4e 100644 --- a/firmware/GyverLamp_v1.4/parsing.ino +++ b/firmware/GyverLamp_v1.4/parsing.ino @@ -44,7 +44,7 @@ void parseUDP() modes[currentMode].Brightness = constrain(inputBuffer.substring(3).toInt(), 1, 255); FastLED.setBrightness(modes[currentMode].Brightness); settChanged = true; - eepromTimer = millis(); + eepromTimeout = millis(); sendCurrent(); } @@ -53,7 +53,7 @@ void parseUDP() modes[currentMode].Speed = inputBuffer.substring(3).toInt(); loadingFlag = true; settChanged = true; - eepromTimer = millis(); + eepromTimeout = millis(); sendCurrent(); } @@ -62,7 +62,7 @@ void parseUDP() modes[currentMode].Scale = inputBuffer.substring(3).toInt(); loadingFlag = true; settChanged = true; - eepromTimer = millis(); + eepromTimeout = millis(); sendCurrent(); } @@ -157,7 +157,7 @@ void parseUDP() //FavoritesManager::SetStatus(inputBuffer); sendFavorites(); settChanged = true; - eepromTimer = millis(); + eepromTimeout = millis(); } else if (inputBuffer.startsWith("OTA")) @@ -166,7 +166,7 @@ void parseUDP() otaManager.RequestOtaUpdate(); delay(50); otaManager.RequestOtaUpdate(); - currentMode = 16; // принудительное включение режима "Матрица" для индикации перехода в режим обновления по воздуху + currentMode = EFF_MATRIX; // принудительное включение режима "Матрица" для индикации перехода в режим обновления по воздуху FastLED.clear(); delay(1); ONflag = true; diff --git a/firmware/GyverLamp_v1.4/time.ino b/firmware/GyverLamp_v1.4/time.ino index 6fd30dd..8e03d3b 100644 --- a/firmware/GyverLamp_v1.4/time.ino +++ b/firmware/GyverLamp_v1.4/time.ino @@ -1,6 +1,6 @@ #ifdef USE_NTP -#define RESOLVE_INTERVAL (5UL * 60UL * 1000UL) // интервал проверки подключения к интеренету в миллисекундах (5 минут) +#define RESOLVE_INTERVAL (5UL * 60UL * 1000UL) // интервал проверки подключения к интеренету в миллисекундах (5 минут) // при старте ESP пытается получить точное время от сервера времени в интрнете // эта попытка длится RESOLVE_TIMEOUT // если при этом отсутствует подключение к интернету (но есть WiFi подключение), @@ -10,12 +10,13 @@ // до этого момента функции будильника работать не будут // интервал последующих синхронизаций времени определяён в NTP_INTERVAL (30 минут) // при ошибках повторной синхронизации времени функции будильника отключаться не будут -#define RESOLVE_TIMEOUT (1500UL) // таймаут ожидания подключения к интернету в миллисекундах (1,5 секунды) +#define RESOLVE_TIMEOUT (1500UL) // таймаут ожидания подключения к интернету в миллисекундах (1,5 секунды) uint64_t lastResolveTryMoment = 0UL; bool timeSynched = false; bool ntpServerAddressResolved = false; IPAddress ntpServerIp = {0, 0, 0, 0}; + void timeTick() { if (ESP_MODE == 1)