diff --git a/firmware/GyverLamp_v1.4/Constants.h b/firmware/GyverLamp_v1.4/Constants.h index 5d1a799..26ed21e 100644 --- a/firmware/GyverLamp_v1.4/Constants.h +++ b/firmware/GyverLamp_v1.4/Constants.h @@ -6,11 +6,12 @@ // ============= НАСТРОЙКИ ============= // --- ESP ----------------------------- #define ESP_MODE (1U) // 0U - WiFi точка доступа, 1U - клиент WiFi (подключение к роутеру) +uint8_t espMode = ESP_MODE; // ESP_MODE может быть сохранён в энергонезависимую память и изменён в процессе работы лампы без необходимости её перепрошивки #define ESP_USE_BUTTON // если строка не закомментирована, должна быть подключена кнопка (иначе ESP может регистрировать "фантомные" нажатия и некорректно устанавливать яркость) -#define ESP_RESET_ON_START (false) // true - если при старте нажата кнопка (или кнопки нет!), сохранённые настройки будут сброшены; false - не будут +#define ESP_RESET_ON_START (false) // true - если при старте нажата кнопка (или кнопки нет!), сохранённые настройки будут сброшены; false - не будут #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_CONN_TIMEOUT (6U) // время в секундах (ДОЛЖНО БЫТЬ МЕНЬШЕ 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 @@ -111,5 +112,5 @@ bool telnetGreetingShown = false; // признак " #define FASTLED_ALLOW_INTERRUPTS (0U) #define FASTLED_ESP8266_RAW_PIN_ORDER -#define NUM_LEDS (WIDTH * HEIGHT) +#define NUM_LEDS (uint16_t)(WIDTH * HEIGHT) #define SEGMENTS (1U) // диодов в одном "пикселе" (для создания матрицы из кусков ленты) diff --git a/firmware/GyverLamp_v1.4/EepromManager.h b/firmware/GyverLamp_v1.4/EepromManager.h index 029da8c..b96ce0d 100644 --- a/firmware/GyverLamp_v1.4/EepromManager.h +++ b/firmware/GyverLamp_v1.4/EepromManager.h @@ -55,7 +55,7 @@ * * Не используются адреса: * 96-110 15 резерв, можно добавить ещё 5 эффектов - * 142-196 55 если добавить ещё 5 эффектов, начальный адрес неиспользуемой памяти сдвинется с 142 на 147 + * 142-195 54 если добавить ещё 5 эффектов, начальный адрес неиспользуемой памяти сдвинется с 142 на 147 */ #include @@ -64,6 +64,7 @@ #define EEPROM_ALARM_START_ADDRESS (0U) // начальный адрес в EEPROM памяти для записи настроек будильников #define EEPROM_MODES_START_ADDRESS (21U) // начальный адрес в EEPROM памяти для записи настроек эффектов (яркость, скорость, масштаб) #define EEPROM_FAVORITES_START_ADDRESS (111U) // начальный адрес в EEPROM памяти для записи настроек режима избранных эффектов +#define EEPROM_ESP_MODE (196U) // адрес в EEPROM памяти для записи режима работы модуля ESP (точка доступа/WiFi клиент) #define EEPROM_LAMP_ON_ADDRESS (197U) // адрес в EEPROM памяти для записи состояния лампы (вкл/выкл) #define EEPROM_FIRST_RUN_ADDRESS (198U) // адрес в EEPROM памяти для записи признака первого запуска (определяет необходимость первоначальной записи всех хранимых настроек) #define EEPROM_DAWN_MODE_ADDRESS (199U) // адрес в EEPROM памяти для записи времени до "рассвета" @@ -79,7 +80,7 @@ class EepromManager { public: - static void InitEepromSettings(ModeType modes[], AlarmType alarms[], bool* onFlag, uint8_t* dawnMode, int8_t* currentMode, + static void InitEepromSettings(ModeType modes[], AlarmType alarms[], uint8_t* espMode, bool* onFlag, uint8_t* dawnMode, int8_t* currentMode, void (*readFavoritesSettings)(), void (*saveFavoritesSettings)()) { // записываем в EEPROM начальное состояние настроек, если их там ещё нет @@ -104,6 +105,7 @@ class EepromManager EEPROM.commit(); } + EEPROM.write(EEPROM_ESP_MODE, ESP_MODE); EEPROM.write(EEPROM_LAMP_ON_ADDRESS, 0); EEPROM.write(EEPROM_DAWN_MODE_ADDRESS, 0); EEPROM.write(EEPROM_CURRENT_MODE_ADDRESS, 0); @@ -127,6 +129,7 @@ class EepromManager readFavoritesSettings(); + *espMode = (uint8_t)EEPROM.read(EEPROM_ESP_MODE); *onFlag = (bool)EEPROM.read(EEPROM_LAMP_ON_ADDRESS); *dawnMode = EEPROM.read(EEPROM_DAWN_MODE_ADDRESS); *currentMode = EEPROM.read(EEPROM_CURRENT_MODE_ADDRESS); @@ -162,6 +165,12 @@ class EepromManager EEPROM.commit(); } + static void SaveEspMode(uint8_t* espMode) + { + EEPROM.write(EEPROM_ESP_MODE, *espMode); + EEPROM.commit(); + } + static void SaveOnFlag(bool* onFlag) { EEPROM.write(EEPROM_LAMP_ON_ADDRESS, *onFlag); diff --git a/firmware/GyverLamp_v1.4/GyverLamp_v1.4.ino b/firmware/GyverLamp_v1.4/GyverLamp_v1.4.ino index 053195d..20737d2 100644 --- a/firmware/GyverLamp_v1.4/GyverLamp_v1.4.ino +++ b/firmware/GyverLamp_v1.4/GyverLamp_v1.4.ino @@ -74,6 +74,19 @@ - Добавлено управление по протоколу MQTT - Исправлена ошибка выключения будильника кнопкой - Добавлена задержка в 1 секунду сразу после старта, в течение которой нужно нажать кнопку, чтобы очистить сохранённые параметры WiFi (если ESP_RESET_ON_START == true) + --- 12.10.2019 + - Добавлена возможность сменить рабочий режим лампы (ESP_MODE) без необходимости перепрошивки; вызывается по семикратному клику по кнопке при включенной матрице; сохраняется в EEPROM + - Изменён алгоритм работы будильника: + - * обновление его оттенка/яркости происходит 1 раз в 3 секунды вместо 1 раза в минуту + - * диоды разбиты на 6 групп, первой из которых назначается новый оттенок/яркость 1 раз в 3 секунды, вторая "отстаёт" на 1 шаг, третья - на 2 шага и т.д. (для большей плавности) + - Добавлена визуальная сигнализация о некоторых важных действиях/состояниях лампы: + - * при запуске в режиме WiFi клиента и ещё не настроенных параметрах WiFi сети (когда их нужно ввести) - 1 короткая (0,25 сек) вспышка жёлтым + - * если лампа стартовала в режиме WiFi клиента с ненастроенными параметрами WiFi сети, и они не были введены за отведённый таймаут (перед перезагрузкой) - 1 короткая вспышка красным + - * при переходе лампы в режим обновления по воздуху (OTA) по двум четырёхкратным кликам по кнопке или по кнопке OTA из android приложения - 2 стандартных (0,5 сек) вспышки жёлтым + - * если лампа была переведена в режим OTA, но не дождалась прошивки за отведённый таймаут (перед перезагрузкой) - 2 стандартных вспышки красным + - * при переключении рабочего режима лампы WiFi точка доступа/WiFi клиент семикратным кликом по кнопке (перед перезагрузкой) - 3 стандартных вспышки красным + - Уменьшен таймаут подключения к WiFi сети до 6 секунд; вызвано увеличившейся продолжительностью работы функции setup(), она в сумме должна быть меньше 8 секунд + - Оптимизирован код */ // Ссылка для менеджера плат: @@ -126,7 +139,7 @@ GButton touch(BTN_PIN, LOW_PULL, NORM_OPEN); #endif #ifdef OTA -OtaManager otaManager; +OtaManager otaManager(&showWarning); OtaPhase OtaManager::OtaFlag = OtaPhase::None; #endif @@ -159,7 +172,7 @@ AlarmType alarms[7]; static const uint8_t dawnOffsets[] PROGMEM = {5, 10, 15, 20, 25, 30, 40, 50, 60}; // опции для выпадающего списка параметра "время перед 'рассветом'" (будильник); синхронизировано с android приложением uint8_t dawnMode; bool dawnFlag = false; -long thisTime; +uint32_t thisTime; bool manualOff = false; int8_t currentMode = 0; @@ -212,10 +225,7 @@ void setup() if (digitalRead(BTN_PIN)) { wifiManager.resetSettings(); // сброс сохранённых SSID и пароля при старте с зажатой кнопкой, если разрешено - - #ifdef GENERAL_DEBUG LOG.println(F("Настройки WiFiManager сброшены")); - #endif } #endif #endif @@ -224,25 +234,41 @@ void setup() // ЛЕНТА/МАТРИЦА FastLED.addLeds(leds, NUM_LEDS)/*.setCorrection(TypicalLEDStrip)*/; FastLED.setBrightness(BRIGHTNESS); - if (CURRENT_LIMIT > 0) FastLED.setMaxPowerInVoltsAndMilliamps(5, CURRENT_LIMIT); + if (CURRENT_LIMIT > 0) + { + FastLED.setMaxPowerInVoltsAndMilliamps(5, CURRENT_LIMIT); + } FastLED.clear(); FastLED.show(); + // EEPROM + EepromManager::InitEepromSettings( // инициализация EEPROM; запись начального состояния настроек, если их там ещё нет; инициализация настроек лампы значениями из EEPROM + modes, alarms, &espMode, &ONflag, &dawnMode, ¤tMode, + &(FavoritesManager::ReadFavoritesFromEeprom), + &(FavoritesManager::SaveFavoritesToEeprom)); + LOG.printf_P(PSTR("Рабочий режим лампы: ESP_MODE = %d\n"), espMode); + + // WI-FI wifiManager.setDebugOutput(WIFIMAN_DEBUG); // вывод отладочных сообщений - //wifiManager.setMinimumSignalQuality(); // установка минимально приемлемого уровня сигнала WiFi сетей (8% по умолчанию) - if (ESP_MODE == 0) // режим WiFi точки доступа + // wifiManager.setMinimumSignalQuality(); // установка минимально приемлемого уровня сигнала WiFi сетей (8% по умолчанию) + if (espMode == 0U) // режим WiFi точки доступа { // wifiManager.setConfigPortalBlocking(false); - WiFi.softAPConfig( // wifiManager.startConfigPortal использовать нельзя, т.к. он блокирует вычислительный процесс внутри себя, а затем перезагружает ESP, т.е. предназначен только для ввода SSID и пароля - IPAddress(AP_STATIC_IP[0], AP_STATIC_IP[1], AP_STATIC_IP[2], AP_STATIC_IP[3]), // IP адрес WiFi точки доступа - IPAddress(AP_STATIC_IP[0], AP_STATIC_IP[1], AP_STATIC_IP[2], 1), // первый доступный IP адрес сети - IPAddress(255, 255, 255, 0)); // маска подсети + + if (sizeof(AP_STATIC_IP)) + { + LOG.println(F("Используется статический IP адрес WiFi точки доступа")); + wifiManager.setAPStaticIPConfig( // wifiManager.startConfigPortal использовать нельзя, т.к. он блокирует вычислительный процесс внутри себя, а затем перезагружает ESP, т.е. предназначен только для ввода SSID и пароля + IPAddress(AP_STATIC_IP[0], AP_STATIC_IP[1], AP_STATIC_IP[2], AP_STATIC_IP[3]), // IP адрес WiFi точки доступа + IPAddress(AP_STATIC_IP[0], AP_STATIC_IP[1], AP_STATIC_IP[2], 1), // первый доступный IP адрес сети + IPAddress(255, 255, 255, 0)); // маска подсети + } WiFi.softAP(AP_NAME, AP_PASS); - LOG.println(F("Режим WiFi точки доступа")); + LOG.println(F("Старт в режиме WiFi точки доступа")); LOG.print(F("IP адрес: ")); LOG.println(WiFi.softAPIP()); @@ -250,19 +276,22 @@ void setup() } else // режим WiFi клиента (подключаемся к роутеру, если есть сохранённые SSID и пароль, иначе создаём WiFi точку доступа и запрашиваем их) { - LOG.println(F("Режим WiFi клиента")); - if (WiFi.SSID()) + LOG.println(F("Старт в режиме WiFi клиента (подключение к роутеру)")); + + if (WiFi.SSID().length()) { - LOG.print(F("Подключение WiFi сети: ")); - LOG.println(WiFi.SSID()); + LOG.printf_P(PSTR("Подключение к WiFi сети: %s\n"), WiFi.SSID().c_str()); } else { LOG.println(F("WiFi сеть не определена, запуск WiFi точки доступа для настройки параметров подключения к WiFi сети...")); + showWarning(CRGB::Yellow, 250U, 250U); // мигание жёлтым цветом 0,25 секунды (1 раз) - нужно ввести параметры WiFi сети для подключения } - if (STA_STATIC_IP) + if (sizeof(STA_STATIC_IP)) { + LOG.print(F("Сконфигурирован статический IP адрес: ")); + LOG.printf_P(PSTR("%u.%u.%u.%u\n"), STA_STATIC_IP[0], STA_STATIC_IP[1], STA_STATIC_IP[2], STA_STATIC_IP[3]); wifiManager.setSTAStaticIPConfig( IPAddress(STA_STATIC_IP[0], STA_STATIC_IP[1], STA_STATIC_IP[2], STA_STATIC_IP[3]), // статический IP адрес ESP в режиме WiFi клиента IPAddress(STA_STATIC_IP[0], STA_STATIC_IP[1], STA_STATIC_IP[2], 1), // первый доступный IP адрес сети (справедливо для 99,99% случаев; для сетей меньше чем на 255 адресов нужно вынести в константы) @@ -276,12 +305,8 @@ void setup() if (WiFi.status() != WL_CONNECTED) { LOG.println(F("Время ожидания ввода SSID и пароля от WiFi сети или подключения к WiFi сети превышено\nПерезагрузка модуля")); - - #if defined(ESP8266) - ESP.reset(); - #else + showWarning(CRGB::Red, 250U, 250U); // мигание красным цветом 0,25 секунды (1 раз) - ожидание ввода SSID'а и пароля WiFi сети прекращено, перезагрузка ESP.restart(); - #endif } LOG.print(F("IP адрес: ")); @@ -292,13 +317,6 @@ void setup() Udp.begin(localPort); - // EEPROM - EepromManager::InitEepromSettings( // инициализация EEPROM; запись начального состояния настроек, если их там ещё нет; инициализация настроек лампы значениями из EEPROM - modes, alarms, &ONflag, &dawnMode, ¤tMode, - &(FavoritesManager::ReadFavoritesFromEeprom), - &(FavoritesManager::SaveFavoritesToEeprom)); - - // NTP #ifdef USE_NTP timeClient.begin(); @@ -306,9 +324,12 @@ void setup() // MQTT - #if (USE_MQTT && ESP_MODE == 1) - mqttClient = new AsyncMqttClient(); - MqttManager::setupMqtt(mqttClient, inputBuffer, &sendCurrent); // создание экземпляров объектов для работы с MQTT, их инициализация и подключение к MQTT брокеру + #if (USE_MQTT) + if (espMode == 1U) + { + mqttClient = new AsyncMqttClient(); + MqttManager::setupMqtt(mqttClient, inputBuffer, &sendCurrent); // создание экземпляров объектов для работы с MQTT, их инициализация и подключение к MQTT брокеру + } #endif @@ -358,7 +379,7 @@ void loop() } #if USE_MQTT - if (ESP_MODE == 1 && mqttClient && WiFi.isConnected() && !mqttClient->connected()) + if (espMode == 1U && mqttClient && WiFi.isConnected() && !mqttClient->connected()) { MqttManager::mqttConnect(); // библиотека не умеет восстанавливать соединение в случае потери подключения к MQTT брокеру, нужно управлять этим явно MqttManager::needToPublish = true; diff --git a/firmware/GyverLamp_v1.4/OtaManager.h b/firmware/GyverLamp_v1.4/OtaManager.h index e14895e..110fe8d 100644 --- a/firmware/GyverLamp_v1.4/OtaManager.h +++ b/firmware/GyverLamp_v1.4/OtaManager.h @@ -33,9 +33,14 @@ class OtaManager public: static OtaPhase OtaFlag; + OtaManager(ShowWarningDelegate showWarningDelegate) + { + this->showWarningDelegate = showWarningDelegate; + } + bool RequestOtaUpdate() // пользователь однократно запросил обновление по воздуху; возвращает true, когда переходит в режим обновления - startOtaUpdate() { - if (ESP_MODE != 1) + if (espMode != 1U) { #ifdef GENERAL_DEBUG LOG.print(F("Запрос обновления по воздуху поддерживается только в режиме ESP_MODE = 1\n")); @@ -59,11 +64,13 @@ class OtaManager if (OtaFlag == OtaPhase::GotFirstConfirm) { OtaFlag = OtaPhase::GotSecondConfirm; + momentOfOtaStart = millis(); #ifdef GENERAL_DEBUG LOG.print(F("Получено второе подтверждение обновления по воздуху\nСтарт режима обновления\n")); #endif + showWarningDelegate(CRGB::Yellow, 2000U, 500U); // мигание жёлтым цветом 2 секунды (2 раза) - готовность к прошивке startOtaUpdate(); return true; } @@ -86,7 +93,7 @@ class OtaManager return; } - if (OtaFlag == OtaPhase::GotSecondConfirm && + if ((OtaFlag == OtaPhase::GotSecondConfirm || OtaFlag == OtaPhase::InProgress) && millis() - momentOfOtaStart >= ESP_CONF_TIMEOUT * 1000) { OtaFlag = OtaPhase::None; @@ -97,12 +104,9 @@ class OtaManager delay(500); #endif - #if defined(ESP8266) - ESP.reset(); - #else - ESP.restart(); - #endif + showWarningDelegate(CRGB::Red, 2000U, 500U); // мигание красным цветом 2 секунды (2 раза) - ожидание прошивки по воздуху прекращено, перезагрузка + ESP.restart(); return; } @@ -115,37 +119,41 @@ class OtaManager private: uint64_t momentOfFirstConfirmation = 0; // момент времени, когда получено первое подтверждение и с которого начинается отсчёт ожидания второго подтверждения uint64_t momentOfOtaStart = 0; // момент времени, когда развёрнута WiFi точка доступа для обновления по воздуху + ShowWarningDelegate showWarningDelegate; void startOtaUpdate() { char espHostName[65]; - sprintf(espHostName, "%s-%u", AP_NAME, ESP.getChipId()); + sprintf_P(espHostName, PSTR("%s-%u"), AP_NAME, ESP.getChipId()); ArduinoOTA.setPort(ESP_OTA_PORT); ArduinoOTA.setHostname(espHostName); ArduinoOTA.setPassword(AP_PASS); ArduinoOTA.onStart([this]() { - String type; + OtaFlag = OtaPhase::InProgress; + char type[16]; if (ArduinoOTA.getCommand() == U_FLASH) { - type = "sketch"; + strcpy_P(type, PSTR("sketch")); } else // U_SPIFFS { - type = "filesystem"; + strcpy_P(type, PSTR("filesystem")); } // NOTE: if updating SPIFFS this would be the place to unmount SPIFFS using SPIFFS.end() #ifdef GENERAL_DEBUG - LOG.printf_P(PSTR("Start updating %s\n"), type.c_str()); + LOG.printf_P(PSTR("Start updating %s\n"), type); #endif }); ArduinoOTA.onEnd([this]() { OtaFlag = OtaPhase::Done; + momentOfFirstConfirmation = 0; + momentOfOtaStart = 0; #ifdef GENERAL_DEBUG LOG.print(F("Обновление по воздуху выполнено\nПерезапуск")); @@ -163,6 +171,8 @@ class OtaManager ArduinoOTA.onError([this](ota_error_t error) { OtaFlag = OtaPhase::None; + momentOfFirstConfirmation = 0; + momentOfOtaStart = 0; #ifdef GENERAL_DEBUG LOG.printf_P(PSTR("Обновление по воздуху завершилось ошибкой [%u]: "), error); @@ -207,8 +217,6 @@ class OtaManager ArduinoOTA.setRebootOnSuccess(true); ArduinoOTA.begin(); OtaFlag = OtaPhase::InProgress; - momentOfFirstConfirmation = 0; - momentOfOtaStart = 0; #ifdef GENERAL_DEBUG LOG.printf_P(PSTR("Для обновления в Arduino IDE выберите пункт меню Инструменты - Порт - '%s at "), espHostName); diff --git a/firmware/GyverLamp_v1.4/Types.h b/firmware/GyverLamp_v1.4/Types.h index af010d1..5c14654 100644 --- a/firmware/GyverLamp_v1.4/Types.h +++ b/firmware/GyverLamp_v1.4/Types.h @@ -4,14 +4,15 @@ struct AlarmType { bool State = false; - uint16_t Time = 0; + uint16_t Time = 0U; }; struct ModeType { - uint8_t Brightness = 50; - uint8_t Speed = 30; - uint8_t Scale = 40; + uint8_t Brightness = 50U; + uint8_t Speed = 30U; + uint8_t Scale = 40U; }; typedef void (*SendCurrentDelegate)(char *outputBuffer); +typedef void (*ShowWarningDelegate)(CRGB color, uint32_t duration, uint16_t blinkHalfPeriod); diff --git a/firmware/GyverLamp_v1.4/button.ino b/firmware/GyverLamp_v1.4/button.ino index 576f77e..f46a646 100644 --- a/firmware/GyverLamp_v1.4/button.ino +++ b/firmware/GyverLamp_v1.4/button.ino @@ -6,9 +6,9 @@ static bool startButtonHolding = false; // флаг: кно void buttonTick() { touch.tick(); - uint8_t clickCount = touch.hasClicks() ? touch.getClicks() : 0; + uint8_t clickCount = touch.hasClicks() ? touch.getClicks() : 0U; - if (clickCount == 1) + if (clickCount == 1U) { if (dawnFlag) { @@ -26,14 +26,17 @@ void buttonTick() eepromTimeout = millis(); loadingFlag = true; - #if (USE_MQTT && ESP_MODE == 1) - MqttManager::needToPublish = true; + #if (USE_MQTT) + if (espMode == 1U) + { + MqttManager::needToPublish = true; + } #endif } - if (ONflag && clickCount == 2) + if (ONflag && clickCount == 2U) { - if (++currentMode >= MODE_AMOUNT) currentMode = 0; + if (++currentMode >= (int8_t)MODE_AMOUNT) currentMode = 0; FastLED.setBrightness(modes[currentMode].Brightness); loadingFlag = true; settChanged = true; @@ -41,12 +44,15 @@ void buttonTick() FastLED.clear(); delay(1); - #if (USE_MQTT && ESP_MODE == 1) - MqttManager::needToPublish = true; + #if (USE_MQTT) + if (espMode == 1U) + { + MqttManager::needToPublish = true; + } #endif } - if (ONflag && clickCount == 3) + if (ONflag && clickCount == 3U) { if (--currentMode < 0) currentMode = MODE_AMOUNT - 1; FastLED.setBrightness(modes[currentMode].Brightness); @@ -56,26 +62,31 @@ void buttonTick() FastLED.clear(); delay(1); - #if (USE_MQTT && ESP_MODE == 1) - MqttManager::needToPublish = true; - #endif - } - - if (clickCount == 4) - { - #ifdef OTA - if (otaManager.RequestOtaUpdate()) + #if (USE_MQTT) + if (espMode == 1U) { - currentMode = EFF_MATRIX; // принудительное включение режима "Матрица" для индикации перехода в режим обновления по воздуху - FastLED.clear(); - delay(1); + MqttManager::needToPublish = true; } #endif } - if (ONflag && clickCount == 5) // вывод IP на лампу + if (clickCount == 4U) { - if (ESP_MODE == 1U) + #ifdef OTA + if (otaManager.RequestOtaUpdate()) + { + ONflag = true; + currentMode = EFF_MATRIX; // принудительное включение режима "Матрица" для индикации перехода в режим обновления по воздуху + FastLED.clear(); + delay(1); + changePower(); + } + #endif + } + + if (ONflag && clickCount == 5U) // вывод IP на лампу + { + if (espMode == 1U) { loadingFlag = true; while(!fillString(WiFi.localIP().toString().c_str())) delay(1); @@ -83,6 +94,22 @@ void buttonTick() } } + if (ONflag && clickCount == 7U) // смена рабочего режима лампы: с WiFi точки доступа на WiFi клиент или наоборот + { + espMode = (espMode == 0U) ? 1U : 0U; + EepromManager::SaveEspMode(&espMode); + + #ifdef GENERAL_DEBUG + LOG.printf_P(PSTR("Рабочий режим лампы изменён и сохранён в энергонезависимую память\nНовый рабочий режим: ESP_MODE = %d, %s\nРестарт...\n"), + espMode, espMode == 0U ? F("WiFi точка доступа") : F("WiFi клиент (подключение к роутеру)")); + delay(1000); + #endif + + showWarning(CRGB::Red, 3000U, 500U); // мигание красным цветом 3 секунды - смена рабочего режима лампы, перезагрузка + ESP.restart(); + } + + if (ONflag && touch.isHolded()) { brightDirection = !brightDirection; @@ -91,9 +118,9 @@ void buttonTick() if (ONflag && touch.isStep()) { - uint8_t delta = modes[currentMode].Brightness < 10 // определение шага изменения яркости: при яркости [1..10] шаг = 1, при [11..16] шаг = 3, при [17..255] шаг = 15 - ? 1 - : 5; + uint8_t delta = modes[currentMode].Brightness < 10U // определение шага изменения яркости: при яркости [1..10] шаг = 1, при [11..16] шаг = 3, при [17..255] шаг = 15 + ? 1U + : 5U; modes[currentMode].Brightness = constrain(brightDirection ? modes[currentMode].Brightness + delta @@ -108,8 +135,8 @@ void buttonTick() #endif } - #if (USE_MQTT && ESP_MODE == 1) - if (ONflag && !touch.isHold() && startButtonHolding) // кнопка отпущена после удерживания, нужно отправить MQTT сообщение об изменении яркости лампы + #if (USE_MQTT) + if (espMode == 1U && ONflag && !touch.isHold() && startButtonHolding) // кнопка отпущена после удерживания, нужно отправить MQTT сообщение об изменении яркости лампы { MqttManager::needToPublish = true; startButtonHolding = false; diff --git a/firmware/GyverLamp_v1.4/effectTicker.ino b/firmware/GyverLamp_v1.4/effectTicker.ino index fdbd990..c2dc945 100644 --- a/firmware/GyverLamp_v1.4/effectTicker.ino +++ b/firmware/GyverLamp_v1.4/effectTicker.ino @@ -46,7 +46,7 @@ void changePower() if (ONflag) { effectsTick(); - for (uint8_t i = 0; i < modes[currentMode].Brightness; i = constrain(i + 8, 0, modes[currentMode].Brightness)) + for (uint8_t i = 0U; i < modes[currentMode].Brightness; i = constrain(i + 8, 0, modes[currentMode].Brightness)) { FastLED.setBrightness(i); delay(1); @@ -74,12 +74,15 @@ void changePower() TimerManager::TimerHasFired = false; TimerManager::TimeToFire = 0ULL; - if (FavoritesManager::UseSavedFavoritesRunning == 0) // если выбрана опция Сохранять состояние (вкл/выкл) "избранного", то ни выключение модуля, ни выключение матрицы не сбрасывают текущее состояние (вкл/выкл) "избранного" + if (FavoritesManager::UseSavedFavoritesRunning == 0U) // если выбрана опция Сохранять состояние (вкл/выкл) "избранного", то ни выключение модуля, ни выключение матрицы не сбрасывают текущее состояние (вкл/выкл) "избранного" { FavoritesManager::TurnFavoritesOff(); } - #if (USE_MQTT && ESP_MODE == 1) - MqttManager::needToPublish = true; + #if (USE_MQTT) + if (espMode == 1U) + { + MqttManager::needToPublish = true; + } #endif } diff --git a/firmware/GyverLamp_v1.4/effects.ino b/firmware/GyverLamp_v1.4/effects.ino index 6fd26e3..7268137 100644 --- a/firmware/GyverLamp_v1.4/effects.ino +++ b/firmware/GyverLamp_v1.4/effects.ino @@ -6,11 +6,11 @@ void sparklesRoutine() { 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) + uint8_t x = random(0U, WIDTH); + uint8_t y = random(0U, HEIGHT); + if (getPixColorXY(x, y) == 0U) { - leds[getPixelNumber(x, y)] = CHSV(random(0, 255), 255, 255); + leds[getPixelNumber(x, y)] = CHSV(random(0U, 255U), 255U, 255U); } } fader(FADE_OUT_SPEED); @@ -19,9 +19,9 @@ void sparklesRoutine() // функция плавного угасания цвета для всех пикселей void fader(uint8_t step) { - for (uint8_t i = 0; i < WIDTH; i++) + for (uint8_t i = 0U; i < WIDTH; i++) { - for (uint8_t j = 0; j < HEIGHT; j++) + for (uint8_t j = 0U; j < HEIGHT; j++) { fadePixel(i, j, step); } @@ -31,24 +31,24 @@ void fader(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; + if (getPixColor(pixelNum) == 0U) return; - if (leds[pixelNum].r >= 30 || - leds[pixelNum].g >= 30 || - leds[pixelNum].b >= 30) + if (leds[pixelNum].r >= 30U || + leds[pixelNum].g >= 30U || + leds[pixelNum].b >= 30U) { leds[pixelNum].fadeToBlackBy(step); } else { - leds[pixelNum] = 0; + leds[pixelNum] = 0U; } } // ------------- огонь ----------------- #define SPARKLES (1U) // вылетающие угольки вкл выкл uint8_t line[WIDTH]; -uint8_t pcnt = 0; +uint8_t pcnt = 0U; //these values are substracetd from the generated values to give a shape to the animation static const uint8_t valueMask[8][16] PROGMEM = @@ -99,7 +99,7 @@ void fireRoutine() // Randomly generate the next line (matrix row) void generateLine() { - for (uint8_t x = 0; x < WIDTH; x++) + for (uint8_t x = 0U; x < WIDTH; x++) { line[x] = random(64, 255); } @@ -107,22 +107,22 @@ void generateLine() void shiftUp() { - for (uint8_t y = HEIGHT - 1; y > 0; y--) + for (uint8_t y = HEIGHT - 1U; y > 0U; y--) { - for (uint8_t x = 0; x < WIDTH; x++) + for (uint8_t x = 0U; x < WIDTH; x++) { uint8_t newX = x; - if (x > 15) newX = x % 16; - if (y > 7) continue; - matrixValue[y][newX] = matrixValue[y - 1][newX]; + if (x > 15U) newX = x % 16U; + if (y > 7U) continue; + matrixValue[y][newX] = matrixValue[y - 1U][newX]; } } - for (uint8_t x = 0; x < WIDTH; x++) + for (uint8_t x = 0U; x < WIDTH; x++) { uint8_t newX = x; - if (x > 15) newX = x % 16; - matrixValue[0][newX] = line[newX]; + if (x > 15U) newX = x % 16U; + matrixValue[0U][newX] = line[newX]; } } @@ -134,13 +134,13 @@ void drawFrame(uint8_t pcnt) int32_t nextv; //each row interpolates with the one before it - for (uint8_t y = HEIGHT - 1; y > 0; y--) + for (uint8_t y = HEIGHT - 1U; y > 0U; y--) { - for (uint8_t x = 0; x < WIDTH; x++) + for (uint8_t x = 0U; x < WIDTH; x++) { uint8_t newX = x; - if (x > 15) newX = x % 16; - if (y < 8) + if (x > 15U) newX = x % 16U; + if (y < 8U) { nextv = (((100.0 - pcnt) * matrixValue[y][newX] @@ -149,32 +149,32 @@ void drawFrame(uint8_t pcnt) CRGB color = CHSV( modes[EFF_FIRE].Scale * 2.5 + pgm_read_byte(&hueMask[y][newX]), // H - 255, // S + 255U, // S (uint8_t)max(0, nextv) // V ); leds[getPixelNumber(x, y)] = color; } - else if (y == 8 && SPARKLES) + else if (y == 8U && SPARKLES) { - if (random(0, 20) == 0 && getPixColorXY(x, y - 1) != 0) drawPixelXY(x, y, getPixColorXY(x, y - 1)); - else drawPixelXY(x, y, 0); + if (random(0, 20) == 0 && getPixColorXY(x, y - 1U) != 0U) drawPixelXY(x, y, getPixColorXY(x, y - 1U)); + else drawPixelXY(x, y, 0U); } else if (SPARKLES) { // старая версия для яркости - if (getPixColorXY(x, y - 1) > 0) - drawPixelXY(x, y, getPixColorXY(x, y - 1)); - else drawPixelXY(x, y, 0); + if (getPixColorXY(x, y - 1U) > 0U) + drawPixelXY(x, y, getPixColorXY(x, y - 1U)); + else drawPixelXY(x, y, 0U); } } } //first row interpolates with the "next" line - for (uint8_t x = 0; x < WIDTH; x++) + for (uint8_t x = 0U; x < WIDTH; x++) { uint8_t newX = x; - if (x > 15) newX = x % 16; + if (x > 15U) newX = x % 16U; CRGB color = CHSV( modes[EFF_FIRE].Scale * 2.5 + pgm_read_byte(&(hueMask[0][newX])), // H 255, // S @@ -193,7 +193,7 @@ void rainbowVerticalRoutine() for (uint8_t j = 0; j < HEIGHT; j++) { CHSV thisColor = CHSV((uint8_t)(hue + j * modes[EFF_RAINBOW_VER].Scale), 255, 255); - for (uint8_t i = 0; i < WIDTH; i++) + for (uint8_t i = 0U; i < WIDTH; i++) { drawPixelXY(i, j, thisColor); } @@ -204,10 +204,10 @@ void rainbowVerticalRoutine() void rainbowHorizontalRoutine() { hue += 4; - for (uint8_t i = 0; i < WIDTH; i++) + for (uint8_t i = 0U; i < WIDTH; i++) { CHSV thisColor = CHSV((uint8_t)(hue + i * modes[EFF_RAINBOW_HOR].Scale), 255, 255); - for (uint8_t j = 0; j < HEIGHT; j++) + for (uint8_t j = 0U; j < HEIGHT; j++) { drawPixelXY(i, j, thisColor); } @@ -224,12 +224,12 @@ void rainbowDiagonalRoutine() } hue += 8; - for (uint8_t i = 0; i < WIDTH; i++) + for (uint8_t i = 0U; i < WIDTH; i++) { - for (uint8_t j = 0; j < HEIGHT; j++) + for (uint8_t j = 0U; j < HEIGHT; j++) { float twirlFactor = 3.0F * (modes[EFF_RAINBOW_DIAG].Scale / 100.0F); // на сколько оборотов будет закручена матрица, [0..3] - CRGB thisColor = CHSV(constrain((uint8_t)(hue + (float)(WIDTH / HEIGHT * i + j * twirlFactor) * (float)(255 / maxDim)), 0, 255), 255, 255); + CRGB thisColor = CHSV(constrain(hue + (float)(WIDTH / HEIGHT * i + j * twirlFactor) * (float)(255 / maxDim), 0, 255), 255, 255); drawPixelXY(i, j, thisColor); } } @@ -242,9 +242,9 @@ void colorsRoutine() { hue += modes[EFF_COLORS].Scale; - for (int16_t i = 0; i < NUM_LEDS; i++) + for (uint16_t i = 0U; i < NUM_LEDS; i++) { - leds[i] = CHSV(hue, 255, 255); + leds[i] = CHSV(hue, 255U, 255U); } } } @@ -257,9 +257,9 @@ void colorRoutine() loadingFlag = false; FastLED.clear(); - for (int16_t i = 0; i < NUM_LEDS; i++) + for (int16_t i = 0U; i < NUM_LEDS; i++) { - leds[i] = CHSV(modes[EFF_COLOR].Scale * 2.5, 255, 255); + leds[i] = CHSV(modes[EFF_COLOR].Scale * 2.5, 255U, 255U); } } } @@ -268,22 +268,22 @@ void colorRoutine() void snowRoutine() { // сдвигаем всё вниз - for (uint8_t x = 0; x < WIDTH; x++) + for (uint8_t x = 0U; x < WIDTH; x++) { - for (uint8_t y = 0; y < HEIGHT - 1; y++) + for (uint8_t y = 0U; y < HEIGHT - 1; y++) { - drawPixelXY(x, y, getPixColorXY(x, y + 1)); + drawPixelXY(x, y, getPixColorXY(x, y + 1U)); } } - for (uint8_t x = 0; x < WIDTH; x++) + for (uint8_t x = 0U; x < WIDTH; x++) { // заполняем случайно верхнюю строку // а также не даём двум блокам по вертикали вместе быть - if (getPixColorXY(x, HEIGHT - 2) == 0 && (random(0, 100 - modes[EFF_SNOW].Scale) == 0)) - drawPixelXY(x, HEIGHT - 1, 0xE0FFFF - 0x101010 * random(0, 4)); + if (getPixColorXY(x, HEIGHT - 2U) == 0U && (random(0, 100 - modes[EFF_SNOW].Scale) == 0U)) + drawPixelXY(x, HEIGHT - 1U, 0xE0FFFF - 0x101010 * random(0, 4)); else - drawPixelXY(x, HEIGHT - 1, 0x000000); + drawPixelXY(x, HEIGHT - 1U, 0x000000); } } @@ -300,45 +300,45 @@ void snowStormRoutine() } // заполняем головами комет левую и верхнюю линию - for (uint8_t i = HEIGHT / 2; i < HEIGHT; i++) + for (uint8_t i = HEIGHT / 2U; i < HEIGHT; i++) { - if (getPixColorXY(0, i) == 0 && + if (getPixColorXY(0U, i) == 0U && (random(0, SNOW_DENSE) == 0) && - getPixColorXY(0, i + 1) == 0 && - getPixColorXY(0, i - 1) == 0) + getPixColorXY(0U, i + 1U) == 0U && + getPixColorXY(0U, i - 1U) == 0U) { - leds[getPixelNumber(0, i)] = CHSV(random(0, 200), SNOW_SATURATION, 255); + leds[getPixelNumber(0U, i)] = CHSV(random(0, 200), SNOW_SATURATION, 255U); } } - for (uint8_t i = 0; i < WIDTH / 2; i++) + for (uint8_t i = 0U; i < WIDTH / 2U; 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) + if (getPixColorXY(i, HEIGHT - 1U) == 0U && + (random(0, map(modes[EFF_SNOWSTORM].Scale, 0U, 255U, 10U, 120U)) == 0U) && + getPixColorXY(i + 1U, HEIGHT - 1U) == 0U && + getPixColorXY(i - 1U, HEIGHT - 1U) == 0U) { - leds[getPixelNumber(i, HEIGHT - 1)] = CHSV(random(0, 200), SNOW_SATURATION, 255); + leds[getPixelNumber(i, HEIGHT - 1U)] = CHSV(random(0, 200), SNOW_SATURATION, 255U); } } // сдвигаем по диагонали - for (uint8_t y = 0; y < HEIGHT - 1; y++) + for (uint8_t y = 0U; y < HEIGHT - 1U; y++) { - for (uint8_t x = WIDTH - 1; x > 0; x--) + for (uint8_t x = WIDTH - 1U; x > 0U; x--) { - drawPixelXY(x, y, getPixColorXY(x - 1, y + 1)); + drawPixelXY(x, y, getPixColorXY(x - 1U, y + 1U)); } } // уменьшаем яркость левой и верхней линии, формируем "хвосты" - for (uint8_t i = HEIGHT / 2; i < HEIGHT; i++) + for (uint8_t i = HEIGHT / 2U; i < HEIGHT; i++) { - fadePixel(0, i, SNOW_TAIL_STEP); + fadePixel(0U, i, SNOW_TAIL_STEP); } - for (uint8_t i = 0; i < WIDTH / 2; i++) + for (uint8_t i = 0U; i < WIDTH / 2U; i++) { - fadePixel(i, HEIGHT - 1, SNOW_TAIL_STEP); + fadePixel(i, HEIGHT - 1U, SNOW_TAIL_STEP); } } @@ -355,77 +355,77 @@ void starfallRoutine() } // заполняем головами комет левую и верхнюю линию - for (uint8_t i = HEIGHT / 2; i < HEIGHT; i++) + for (uint8_t i = HEIGHT / 2U; i < HEIGHT; i++) { - if (getPixColorXY(0, i) == 0 && + if (getPixColorXY(0U, i) == 0U && (random(0, STAR_DENSE) == 0) && - getPixColorXY(0, i + 1) == 0 && - getPixColorXY(0, i - 1) == 0) + getPixColorXY(0U, i + 1U) == 0U && + getPixColorXY(0U, i - 1U) == 0U) { - leds[getPixelNumber(0, i)] = CHSV(random(0, 200), STAR_SATURATION, 255); + leds[getPixelNumber(0U, i)] = CHSV(random(0, 200), STAR_SATURATION, 255U); } } - for (uint8_t i = 0; i < WIDTH / 2; i++) + for (uint8_t i = 0U; i < WIDTH / 2U; 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) + if (getPixColorXY(i, HEIGHT - 1U) == 0U && + (random(0, map(modes[EFF_STARFALL].Scale, 0U, 255U, 10U, 120U)) == 0U) && + getPixColorXY(i + 1U, HEIGHT - 1U) == 0U && + getPixColorXY(i - 1U, HEIGHT - 1U) == 0U) { - leds[getPixelNumber(i, HEIGHT - 1)] = CHSV(random(0, 200), STAR_SATURATION, 255); + leds[getPixelNumber(i, HEIGHT - 1U)] = CHSV(random(0, 200), STAR_SATURATION, 255U); } } // сдвигаем по диагонали - for (uint8_t y = 0; y < HEIGHT - 1; y++) + for (uint8_t y = 0U; y < HEIGHT - 1U; y++) { - for (uint8_t x = WIDTH - 1; x > 0; x--) + for (uint8_t x = WIDTH - 1U; x > 0U; x--) { - drawPixelXY(x, y, getPixColorXY(x - 1, y + 1)); + drawPixelXY(x, y, getPixColorXY(x - 1U, y + 1U)); } } // уменьшаем яркость левой и верхней линии, формируем "хвосты" - for (uint8_t i = HEIGHT / 2; i < HEIGHT; i++) + for (uint8_t i = HEIGHT / 2U; i < HEIGHT; i++) { - fadePixel(0, i, STAR_TAIL_STEP); + fadePixel(0U, i, STAR_TAIL_STEP); } - for (uint8_t i = 0; i < WIDTH / 2; i++) + for (uint8_t i = 0U; i < WIDTH / 2U; i++) { - fadePixel(i, HEIGHT - 1, STAR_TAIL_STEP); + fadePixel(i, HEIGHT - 1U, STAR_TAIL_STEP); } } // ------------- матрица --------------- void matrixRoutine() { - for (uint8_t x = 0; x < WIDTH; x++) + for (uint8_t x = 0U; x < WIDTH; x++) { // заполняем случайно верхнюю строку - uint32_t thisColor = getPixColorXY(x, HEIGHT - 1); - if (thisColor == 0) - drawPixelXY(x, HEIGHT - 1, 0x00FF00 * (random(0, 100 - modes[EFF_MATRIX].Scale) == 0)); + uint32_t thisColor = getPixColorXY(x, HEIGHT - 1U); + if (thisColor == 0U) + drawPixelXY(x, HEIGHT - 1U, 0x00FF00 * (random(0, 100 - modes[EFF_MATRIX].Scale) == 0U)); else if (thisColor < 0x002000) - drawPixelXY(x, HEIGHT - 1, 0); + drawPixelXY(x, HEIGHT - 1U, 0U); else - drawPixelXY(x, HEIGHT - 1, thisColor - 0x002000); + drawPixelXY(x, HEIGHT - 1U, thisColor - 0x002000); } // сдвигаем всё вниз - for (uint8_t x = 0; x < WIDTH; x++) + for (uint8_t x = 0U; x < WIDTH; x++) { - for (uint8_t y = 0; y < HEIGHT - 1; y++) + for (uint8_t y = 0U; y < HEIGHT - 1U; y++) { - drawPixelXY(x, y, getPixColorXY(x, y + 1)); + drawPixelXY(x, y, getPixColorXY(x, y + 1U)); } } } // ------------- светлячки -------------- -#define LIGHTERS_AM (100) -int32_t lightersPos[2][LIGHTERS_AM]; -int8_t lightersSpeed[2][LIGHTERS_AM]; +#define LIGHTERS_AM (100U) +int32_t lightersPos[2U][LIGHTERS_AM]; +int8_t lightersSpeed[2U][LIGHTERS_AM]; CHSV lightersColor[LIGHTERS_AM]; uint8_t loopCounter; int32_t angle[LIGHTERS_AM]; @@ -437,44 +437,44 @@ void lightersRoutine() { loadingFlag = false; randomSeed(millis()); - for (uint8_t i = 0; i < LIGHTERS_AM; i++) + for (uint8_t i = 0U; i < LIGHTERS_AM; i++) { - lightersPos[0][i] = random(0, WIDTH * 10); - lightersPos[1][i] = random(0, HEIGHT * 10); - lightersSpeed[0][i] = random(-10, 10); - lightersSpeed[1][i] = random(-10, 10); - lightersColor[i] = CHSV(random(0, 255), 255, 255); + lightersPos[0U][i] = random(0, WIDTH * 10); + lightersPos[1U][i] = random(0, HEIGHT * 10); + lightersSpeed[0U][i] = random(-10, 10); + lightersSpeed[1U][i] = random(-10, 10); + lightersColor[i] = CHSV(random(0U, 255U), 255U, 255U); } } FastLED.clear(); - if (++loopCounter > 20) loopCounter = 0; - for (uint8_t i = 0; i < modes[EFF_LIGHTERS].Scale; i++) + if (++loopCounter > 20U) loopCounter = 0U; + for (uint8_t i = 0U; i < modes[EFF_LIGHTERS].Scale; i++) { - if (loopCounter == 0) // меняем скорость каждые 255 отрисовок + if (loopCounter == 0U) // меняем скорость каждые 255 отрисовок { - lightersSpeed[0][i] += random(-3, 4); - lightersSpeed[1][i] += random(-3, 4); - lightersSpeed[0][i] = constrain(lightersSpeed[0][i], -20, 20); - lightersSpeed[1][i] = constrain(lightersSpeed[1][i], -20, 20); + lightersSpeed[0U][i] += random(-3, 4); + lightersSpeed[1U][i] += random(-3, 4); + lightersSpeed[0U][i] = constrain(lightersSpeed[0U][i], -20, 20); + lightersSpeed[1U][i] = constrain(lightersSpeed[1U][i], -20, 20); } - lightersPos[0][i] += lightersSpeed[0][i]; - lightersPos[1][i] += lightersSpeed[1][i]; + lightersPos[0U][i] += lightersSpeed[0U][i]; + lightersPos[1U][i] += lightersSpeed[1U][i]; - if (lightersPos[0][i] < 0) lightersPos[0][i] = (WIDTH - 1) * 10; - if (lightersPos[0][i] >= WIDTH * 10) lightersPos[0][i] = 0; + if (lightersPos[0U][i] < 0) lightersPos[0U][i] = (WIDTH - 1) * 10; + if (lightersPos[0U][i] >= (int32_t)(WIDTH * 10)) lightersPos[0U][i] = 0; - if (lightersPos[1][i] < 0) + if (lightersPos[1U][i] < 0) { - lightersPos[1][i] = 0; - lightersSpeed[1][i] = -lightersSpeed[1][i]; + lightersPos[1U][i] = 0; + lightersSpeed[1U][i] = -lightersSpeed[1U][i]; } - if (lightersPos[1][i] >= (HEIGHT - 1) * 10) + if (lightersPos[1U][i] >= (int32_t)(HEIGHT - 1) * 10) { - lightersPos[1][i] = (HEIGHT - 1) * 10; - lightersSpeed[1][i] = -lightersSpeed[1][i]; + lightersPos[1U][i] = (HEIGHT - 1U) * 10; + lightersSpeed[1U][i] = -lightersSpeed[1U][i]; } - drawPixelXY(lightersPos[0][i] / 10, lightersPos[1][i] / 10, lightersColor[i]); + drawPixelXY(lightersPos[0U][i] / 10, lightersPos[1U][i] / 10, lightersColor[i]); } } @@ -483,8 +483,8 @@ void lightersRoutine() #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]; +int16_t coord[BALLS_AMOUNT][2U]; +int8_t vector[BALLS_AMOUNT][2U]; CRGB ballColors[BALLS_AMOUNT]; void ballsRoutine() { @@ -492,17 +492,17 @@ void ballsRoutine() { loadingFlag = false; - for (uint8_t j = 0; j < BALLS_AMOUNT; j++) + for (uint8_t j = 0U; j < BALLS_AMOUNT; j++) { int8_t sign; // забиваем случайными данными - coord[j][0] = WIDTH / 2 * 10; + coord[j][0U] = WIDTH / 2 * 10; random(0, 2) ? sign = 1 : sign = -1; - vector[j][0] = random(4, 15) * sign; - coord[j][1] = HEIGHT / 2 * 10; + vector[j][0U] = random(4, 15) * sign; + coord[j][1U] = 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); + vector[j][1U] = random(4, 15) * sign; + ballColors[j] = CHSV(random(0, 9) * 28, 255U, 255U); } } @@ -516,10 +516,10 @@ void ballsRoutine() } // движение шариков - for (uint8_t j = 0; j < BALLS_AMOUNT; j++) + for (uint8_t j = 0U; j < BALLS_AMOUNT; j++) { // движение шариков - for (uint8_t i = 0; i < 2; i++) + for (uint8_t i = 0U; i < 2U; i++) { coord[j][i] += vector[j][i]; if (coord[j][i] < 0) @@ -529,22 +529,22 @@ void ballsRoutine() } } - if (coord[j][0] > (WIDTH - 1) * 10) + if (coord[j][0U] > (int16_t)((WIDTH - 1) * 10)) { - coord[j][0] = (WIDTH - 1) * 10; - vector[j][0] = -vector[j][0]; + coord[j][0U] = (WIDTH - 1) * 10; + vector[j][0U] = -vector[j][0U]; } - if (coord[j][1] > (HEIGHT - 1) * 10) + if (coord[j][1U] > (int16_t)((HEIGHT - 1) * 10)) { - coord[j][1] = (HEIGHT - 1) * 10; - vector[j][1] = -vector[j][1]; + coord[j][1U] = (HEIGHT - 1) * 10; + vector[j][1U] = -vector[j][1U]; } - leds[getPixelNumber(coord[j][0] / 10, coord[j][1] / 10)] = ballColors[j]; + leds[getPixelNumber(coord[j][0U] / 10, coord[j][1U] / 10)] = ballColors[j]; } } // ------------- пейнтбол ------------- -const uint8_t BorderWidth = 2; +const uint8_t BorderWidth = 2U; void lightBallsRoutine() { // Apply some blurring to whatever's already on the matrix @@ -562,10 +562,10 @@ void lightBallsRoutine() // The color of each point shifts over time, each at a different speed. uint16_t ms = millis(); - leds[XY( i, j)] += CHSV( ms / 29, 200, 255); - leds[XY( j, k)] += CHSV( ms / 41, 200, 255); - leds[XY( k, m)] += CHSV( ms / 73, 200, 255); - leds[XY( m, i)] += CHSV( ms / 97, 200, 255); + leds[XY( i, j)] += CHSV( ms / 29, 200U, 255U); + leds[XY( j, k)] += CHSV( ms / 41, 200U, 255U); + leds[XY( k, m)] += CHSV( ms / 73, 200U, 255U); + leds[XY( m, i)] += CHSV( ms / 97, 200U, 255U); } // Trivial XY function for the SmartMatrix; use a different XY @@ -589,8 +589,8 @@ uint16_t XY(uint8_t x, uint8_t y) // ------------- блуждающий кубик ------------- #define RANDOM_COLOR (1U) // случайный цвет при отскоке -int16_t coordB[2]; -int8_t vectorB[2]; +int16_t coordB[2U]; +int8_t vectorB[2U]; CRGB ballColor; int8_t ballSize; void ballRoutine() @@ -600,49 +600,49 @@ void ballRoutine() loadingFlag = false; //FastLED.clear(); - for (uint8_t i = 0; i < 2; i++) + for (uint8_t i = 0U; i < 2U; i++) { coordB[i] = WIDTH / 2 * 10; vectorB[i] = random(8, 20); - ballColor = CHSV(random(0, 9) * 28, 255, 255); + ballColor = CHSV(random(0, 9) * 28, 255U, 255U); } } - 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++) + ballSize = map(modes[EFF_CUBE].Scale, 0U, 255U, 2U, max((uint8_t)min(WIDTH,HEIGHT) / 3, 2)); + for (uint8_t i = 0U; i < 2U; 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); + if (RANDOM_COLOR) ballColor = CHSV(random(0, 9) * 28, 255U, 255U); //vectorB[i] += random(0, 6) - 3; } } - if (coordB[0] > (WIDTH - ballSize) * 10) + if (coordB[0U] > (int16_t)((WIDTH - ballSize) * 10)) { - coordB[0] = (WIDTH - ballSize) * 10; - vectorB[0] = -vectorB[0]; - if (RANDOM_COLOR) ballColor = CHSV(random(0, 9) * 28, 255, 255); + coordB[0U] = (WIDTH - ballSize) * 10; + vectorB[0U] = -vectorB[0U]; + if (RANDOM_COLOR) ballColor = CHSV(random(0, 9) * 28, 255U, 255U); //vectorB[0] += random(0, 6) - 3; } - if (coordB[1] > (HEIGHT - ballSize) * 10) + if (coordB[1U] > (int16_t)((HEIGHT - ballSize) * 10)) { - coordB[1] = (HEIGHT - ballSize) * 10; - vectorB[1] = -vectorB[1]; + coordB[1U] = (HEIGHT - ballSize) * 10; + vectorB[1U] = -vectorB[1U]; if (RANDOM_COLOR) { - ballColor = CHSV(random(0, 9) * 28, 255, 255); + ballColor = CHSV(random(0, 9) * 28, 255U, 255U); } //vectorB[1] += random(0, 6) - 3; } FastLED.clear(); - for (uint8_t i = 0; i < ballSize; i++) + for (uint8_t i = 0U; i < ballSize; i++) { - for (uint8_t j = 0; j < ballSize; j++) + for (uint8_t j = 0U; j < ballSize; j++) { - leds[getPixelNumber(coordB[0] / 10 + i, coordB[1] / 10 + j)] = ballColor; + leds[getPixelNumber(coordB[0U] / 10 + i, coordB[1U] / 10 + j)] = ballColor; } } } @@ -655,9 +655,9 @@ void whiteColorRoutine() loadingFlag = false; FastLED.clear(); - for (int16_t i = 0; i < NUM_LEDS; i++) + for (uint16_t i = 0U; i < NUM_LEDS; i++) { - leds[i] = CHSV(0, 0, 255); + leds[i] = CHSV(0U, 0U, 255U); } } } @@ -676,82 +676,57 @@ void whiteColorStripeRoutine() for (int16_t y = centerY; y >= 0; y--) { CRGB color = CHSV( - 45, // определяем тон - map(modes[EFF_WHITE_COLOR].Speed, 0, 255, 0, 170), // определяем насыщенность + 45U, // определяем тон + map(modes[EFF_WHITE_COLOR].Speed, 0U, 255U, 0U, 170U), // определяем насыщенность y == centerY // определяем яркость - ? 255 // для центральной горизонтальной полосы (или двух) яркость всегда равна 255 - : (modes[EFF_WHITE_COLOR].Scale / 100.0F) > ((centerY + 1.0F) - (y + 1.0F)) / (centerY + 1.0F) ? 255 : 0); // для остальных горизонтальных полос яркость равна либо 255, либо 0 в зависимости от масштаба + ? 255U // для центральной горизонтальной полосы (или двух) яркость всегда равна 255 + : (modes[EFF_WHITE_COLOR].Scale / 100.0F) > ((centerY + 1.0F) - (y + 1.0F)) / (centerY + 1.0F) ? 255U : 0U); // для остальных горизонтальных полос яркость равна либо 255, либо 0 в зависимости от масштаба - for (uint8_t x = 0; x < WIDTH; x++) + for (uint8_t x = 0U; x < WIDTH; x++) { drawPixelXY(x, y, color); // при чётной высоте матрицы максимально яркими отрисуются 2 центральных горизонтальных полосы - drawPixelXY(x, max((uint8_t)(HEIGHT - 1) - (y + 1) + bottomOffset, 0), color); // при нечётной - одна, но дважды + drawPixelXY(x, max((uint8_t)(HEIGHT - 1U) - (y + 1U) + bottomOffset, 0U), color); // при нечётной - одна, но дважды } } } } -/* - * устарело -void lightersRoutine() +// ------------- мигающий цвет (не эффект! используется для отображения краткосрочного предупреждения; блокирующий код!) ------------- +#define WARNING_BRIGHTNESS (10U) // яркость вспышки +void showWarning( + CRGB color, // цвет вспышки + uint32_t duration, // продолжительность отображения предупреждения (общее время) + uint16_t blinkHalfPeriod) // продолжительность одной вспышки в миллисекундах (полупериод) { - if (loadingFlag) - { - loadingFlag = false; - randomSeed(millis()); - for (uint8_t i = 0; i < LIGHTERS_AM; i++) - { - lightersPos[0][i] = random(0, WIDTH * 10); - lightersPos[1][i] = random(0, HEIGHT * 10); - - lightersColor[i] = CHSV(random(0, 255), 255, 255); - - speedV[i] = random(5, 10); - angle[i] = random(0, 360); - angleSpeed[i] = random(-10, 10); - } - } + uint32_t blinkTimer = millis(); + enum BlinkState { OFF = 0, ON = 1 } blinkState = BlinkState::OFF; + FastLED.setBrightness(WARNING_BRIGHTNESS); // установка яркости для предупреждения FastLED.clear(); - if (++loopCounter > 20) loopCounter = 0; + delay(2); + FastLED.show(); - for (uint8_t i = 0; i < modes[EFF_LIGHTER_TRACES].scale; i++) + for (uint16_t i = 0U; i < NUM_LEDS; i++) // установка цвета всех диодов в WARNING_COLOR { - if (loopCounter == 0) // меняем скорость каждые 255 отрисовок - { - angleSpeed[i] += random(-3, 4); - angleSpeed[i] = constrain(angleSpeed[i], -15, 15); - } - - lightersPos[0][i] += speedV[i] * cos(radians(angle[i])); - lightersPos[1][i] += speedV[i] * sin(radians(angle[i])); - - if (lightersPos[0][i] < 0) lightersPos[0][i] = (WIDTH - 1) * 10; - if (lightersPos[0][i] >= WIDTH * 10) lightersPos[0][i] = 0; - - if (lightersPos[1][i] < 0) - { - lightersPos[1][i] = 0; - angle[i] = 360 - angle[i]; - } - else - { - angle[i] += angleSpeed[i]; - } - - if (lightersPos[1][i] >= (HEIGHT - 1) * 10) - { - lightersPos[1][i] = (HEIGHT - 1) * 10; - angle[i] = 360 - angle[i]; - } - else - { - angle[i] += angleSpeed[i]; - } - - if (angle[i] > 360) angle[i] = 360 - angle[i]; - if (angle[i] < 0) angle[i] = 360 + angle[i]; - - drawPixelXY(lightersPos[0][i] / 10, lightersPos[1][i] / 10, lightersColor[i]); + leds[i] = color; } + + uint32_t startTime = millis(); + while (millis() - startTime <= (duration + 5)) // блокировка дальнейшего выполнения циклом на время отображения предупреждения + { + if (millis() - blinkTimer >= blinkHalfPeriod) // переключение вспышка/темнота + { + blinkTimer = millis(); + blinkState = (BlinkState)!blinkState; + FastLED.setBrightness(blinkState == BlinkState::OFF ? 0 : WARNING_BRIGHTNESS); + delay(1); + FastLED.show(); + } + delay(50); + } + + FastLED.clear(); + FastLED.setBrightness(ONflag ? modes[currentMode].Brightness : 0); // установка яркости, которая была выставлена до вызова предупреждения + delay(1); + FastLED.show(); + loadingFlag = true; // принудительное отображение текущего эффекта (того, что был активен перед предупреждением) } -*/ diff --git a/firmware/GyverLamp_v1.4/noiseEffects.ino b/firmware/GyverLamp_v1.4/noiseEffects.ino index 12870e4..f13f44c 100644 --- a/firmware/GyverLamp_v1.4/noiseEffects.ino +++ b/firmware/GyverLamp_v1.4/noiseEffects.ino @@ -86,7 +86,7 @@ void zebraNoiseRoutine() { loadingFlag = false; // 'black out' all 16 palette entries... - fill_solid( currentPalette, 16, CRGB::Black); + fill_solid(currentPalette, 16, CRGB::Black); // and set every fourth one to white. currentPalette[0] = CRGB::White; currentPalette[4] = CRGB::White; diff --git a/firmware/GyverLamp_v1.4/parsing.ino b/firmware/GyverLamp_v1.4/parsing.ino index 86aae44..fdfd11b 100644 --- a/firmware/GyverLamp_v1.4/parsing.ino +++ b/firmware/GyverLamp_v1.4/parsing.ino @@ -21,8 +21,11 @@ void parseUDP() char reply[MAX_UDP_BUFFER_SIZE]; processInputBuffer(inputBuffer, reply, true); - #if (USE_MQTT && ESP_MODE == 1) // отправка ответа выполнения команд по MQTT, если разрешено - strcpy(MqttManager::mqttBuffer, reply); // разрешение определяется при выполнении каждой команды отдельно, команды GET, DEB, DISCOVER и OTA, пришедшие по UDP, игнорируются (приходят раз в 2 секунды от приложения) + #if (USE_MQTT) // отправка ответа выполнения команд по MQTT, если разрешено + if (espMode == 1U) + { + strcpy(MqttManager::mqttBuffer, reply); // разрешение определяется при выполнении каждой команды отдельно, команды GET, DEB, DISCOVER и OTA, пришедшие по UDP, игнорируются (приходят раз в 2 секунды от приложения) + } #endif Udp.beginPacket(Udp.remoteIP(), Udp.remotePort()); @@ -62,13 +65,18 @@ void processInputBuffer(char *inputBuffer, char *outputBuffer, bool generateOutp memcpy(buff, &inputBuffer[3], strlen(inputBuffer)); // взять подстроку, состоящую последних символов строки inputBuffer, начиная с символа 4 currentMode = (uint8_t)atoi(buff); loadingFlag = true; + settChanged = true; + eepromTimeout = millis(); FastLED.clear(); delay(1); sendCurrent(inputBuffer); FastLED.setBrightness(modes[currentMode].Brightness); - #if (USE_MQTT && ESP_MODE == 1) - MqttManager::needToPublish = true; + #if (USE_MQTT) + if (espMode == 1U) + { + MqttManager::needToPublish = true; + } #endif } @@ -82,8 +90,11 @@ void processInputBuffer(char *inputBuffer, char *outputBuffer, bool generateOutp eepromTimeout = millis(); sendCurrent(inputBuffer); - #if (USE_MQTT && ESP_MODE == 1) - MqttManager::needToPublish = true; + #if (USE_MQTT) + if (espMode == 1U) + { + MqttManager::needToPublish = true; + } #endif } @@ -96,8 +107,11 @@ void processInputBuffer(char *inputBuffer, char *outputBuffer, bool generateOutp eepromTimeout = millis(); sendCurrent(inputBuffer); - #if (USE_MQTT && ESP_MODE == 1) - MqttManager::needToPublish = true; + #if (USE_MQTT) + if (espMode == 1U) + { + MqttManager::needToPublish = true; + } #endif } @@ -110,8 +124,11 @@ void processInputBuffer(char *inputBuffer, char *outputBuffer, bool generateOutp eepromTimeout = millis(); sendCurrent(inputBuffer); - #if (USE_MQTT && ESP_MODE == 1) - MqttManager::needToPublish = true; + #if (USE_MQTT) + if (espMode == 1U) + { + MqttManager::needToPublish = true; + } #endif } @@ -124,8 +141,11 @@ void processInputBuffer(char *inputBuffer, char *outputBuffer, bool generateOutp changePower(); sendCurrent(inputBuffer); - #if (USE_MQTT && ESP_MODE == 1) - MqttManager::needToPublish = true; + #if (USE_MQTT) + if (espMode == 1U) + { + MqttManager::needToPublish = true; + } #endif } @@ -137,8 +157,11 @@ void processInputBuffer(char *inputBuffer, char *outputBuffer, bool generateOutp changePower(); sendCurrent(inputBuffer); - #if (USE_MQTT && ESP_MODE == 1) - MqttManager::needToPublish = true; + #if (USE_MQTT) + if (espMode == 1U) + { + MqttManager::needToPublish = true; + } #endif } @@ -160,15 +183,16 @@ void processInputBuffer(char *inputBuffer, char *outputBuffer, bool generateOutp { memcpy(buff, &inputBuffer[8], strlen(inputBuffer)); // взять подстроку, состоящую последних символов строки inputBuffer, начиная с символа 9 alarms[alarmNum].Time = atoi(buff); - uint8_t hour = floor(alarms[alarmNum].Time / 60); - uint8_t minute = alarms[alarmNum].Time - hour * 60; sendAlarms(inputBuffer); } EepromManager::SaveAlarmsSettings(&alarmNum, alarms); - #if (USE_MQTT && ESP_MODE == 1) - strcpy(MqttManager::mqttBuffer, inputBuffer); - MqttManager::needToPublish = true; + #if (USE_MQTT) + if (espMode == 1U) + { + strcpy(MqttManager::mqttBuffer, inputBuffer); + MqttManager::needToPublish = true; + } #endif } @@ -184,14 +208,17 @@ void processInputBuffer(char *inputBuffer, char *outputBuffer, bool generateOutp EepromManager::SaveDawnMode(&dawnMode); sendAlarms(inputBuffer); - #if (USE_MQTT && ESP_MODE == 1) - MqttManager::needToPublish = true; + #if (USE_MQTT) + if (espMode == 1U) + { + MqttManager::needToPublish = true; + } #endif } else if (!strncmp_P(inputBuffer, PSTR("DISCOVER"), 8)) // обнаружение приложением модуля esp в локальной сети { - if (ESP_MODE == 1) // работает только в режиме WiFi клиента + if (espMode == 1U) // работает только в режиме WiFi клиента { sprintf_P(inputBuffer, PSTR("IP %u.%u.%u.%u:%u"), WiFi.localIP()[0], @@ -221,8 +248,11 @@ void processInputBuffer(char *inputBuffer, char *outputBuffer, bool generateOutp TimerManager::TimerHasFired = false; sendTimer(inputBuffer); - #if (USE_MQTT && ESP_MODE == 1) - MqttManager::needToPublish = true; + #if (USE_MQTT) + if (espMode == 1U) + { + MqttManager::needToPublish = true; + } #endif } @@ -238,8 +268,11 @@ void processInputBuffer(char *inputBuffer, char *outputBuffer, bool generateOutp settChanged = true; eepromTimeout = millis(); - #if (USE_MQTT && ESP_MODE == 1) - MqttManager::needToPublish = true; + #if (USE_MQTT) + if (espMode == 1U) + { + MqttManager::needToPublish = true; + } #endif } @@ -282,7 +315,7 @@ void sendCurrent(char *outputBuffer) modes[currentMode].Speed, modes[currentMode].Scale, ONflag, - ESP_MODE); + espMode); #ifdef USE_NTP strcat_P(outputBuffer, PSTR(" 1")); diff --git a/firmware/GyverLamp_v1.4/runningText.ino b/firmware/GyverLamp_v1.4/runningText.ino index 81d249c..0016e99 100644 --- a/firmware/GyverLamp_v1.4/runningText.ino +++ b/firmware/GyverLamp_v1.4/runningText.ino @@ -42,7 +42,7 @@ bool fillString(const char* text) } else { - drawLetter(j, text[i], offset + j * (LET_WIDTH + SPACE)); + drawLetter(text[i], offset + j * (LET_WIDTH + SPACE)); i++; j++; } @@ -60,7 +60,7 @@ bool fillString(const char* text) return false; } -void drawLetter(uint8_t index, uint8_t letter, int8_t offset) +void drawLetter(uint8_t letter, int8_t offset) { uint8_t start_pos = 0, finish_pos = LET_WIDTH; diff --git a/firmware/GyverLamp_v1.4/time.ino b/firmware/GyverLamp_v1.4/time.ino index a39c278..d8999d9 100644 --- a/firmware/GyverLamp_v1.4/time.ino +++ b/firmware/GyverLamp_v1.4/time.ino @@ -15,11 +15,18 @@ uint64_t lastResolveTryMoment = 0UL; bool timeSynched = false; bool ntpServerAddressResolved = false; IPAddress ntpServerIp = {0, 0, 0, 0}; +static CHSV dawnColor = CHSV(0, 0, 0); // цвет "рассвета" +static CHSV dawnColorMinus1 = CHSV(0, 0, 0); // для большей плавности назначаем каждый новый цвет только 1/10 всех диодов; каждая следующая 1/10 часть будет "оставать" на 1 шаг +static CHSV dawnColorMinus2 = CHSV(0, 0, 0); +static CHSV dawnColorMinus3 = CHSV(0, 0, 0); +static CHSV dawnColorMinus4 = CHSV(0, 0, 0); +static CHSV dawnColorMinus5 = CHSV(0, 0, 0); +static uint8_t dawnCounter = 0; // счётчик первых 10 шагов будильника void timeTick() { - if (ESP_MODE == 1) + if (espMode == 1U) { if (timeTimer.isReady()) { @@ -48,26 +55,41 @@ void timeTick() return; } - byte thisDay = timeClient.getDay(); + uint8_t thisDay = timeClient.getDay(); if (thisDay == 0) thisDay = 7; // воскресенье это 0 thisDay--; thisTime = timeClient.getHours() * 60 + timeClient.getMinutes(); + uint32_t thisFullTime = timeClient.getHours() * 3600 + timeClient.getMinutes() * 60 + timeClient.getSeconds(); // проверка рассвета - if (alarms[thisDay].State && // день будильника - thisTime >= (alarms[thisDay].Time - pgm_read_byte(&dawnOffsets[dawnMode])) && // позже начала - thisTime < (alarms[thisDay].Time + DAWN_TIMEOUT)) // раньше конца + минута + if (alarms[thisDay].State && // день будильника + thisTime >= (uint16_t)constrain(alarms[thisDay].Time - pgm_read_byte(&dawnOffsets[dawnMode]), 0, (24 * 60)) && // позже начала + thisTime < (alarms[thisDay].Time + DAWN_TIMEOUT)) // раньше конца + минута { if (!manualOff) // будильник не был выключен вручную (из приложения или кнопкой) { - LOG.println("Будильник включен"); // величина рассвета 0-255 - int32_t dawnPosition = 255 * ((float)(thisTime - (alarms[thisDay].Time - pgm_read_byte(&dawnOffsets[dawnMode]))) / pgm_read_byte(&dawnOffsets[dawnMode])); + int32_t dawnPosition = 255 * ((float)(thisFullTime - (alarms[thisDay].Time - pgm_read_byte(&dawnOffsets[dawnMode])) * 60) / (pgm_read_byte(&dawnOffsets[dawnMode]) * 60)); dawnPosition = constrain(dawnPosition, 0, 255); - CHSV dawnColor = CHSV(map(dawnPosition, 0, 255, 10, 35), - map(dawnPosition, 0, 255, 255, 170), - map(dawnPosition, 0, 255, 10, DAWN_BRIGHT)); - fill_solid(leds, NUM_LEDS, dawnColor); + dawnColorMinus5 = dawnCounter > 4 ? dawnColorMinus4 : dawnColorMinus5; + dawnColorMinus4 = dawnCounter > 3 ? dawnColorMinus3 : dawnColorMinus4; + dawnColorMinus3 = dawnCounter > 2 ? dawnColorMinus2 : dawnColorMinus3; + dawnColorMinus2 = dawnCounter > 1 ? dawnColorMinus1 : dawnColorMinus2; + dawnColorMinus1 = dawnCounter > 0 ? dawnColor : dawnColorMinus1; + dawnColor = CHSV(map(dawnPosition, 0, 255, 10, 35), + map(dawnPosition, 0, 255, 255, 170), + map(dawnPosition, 0, 255, 10, DAWN_BRIGHT)); + dawnCounter++; + // fill_solid(leds, NUM_LEDS, dawnColor); + for (uint16_t i = 0U; i < NUM_LEDS; i++) + { + if (i % 6 == 0) leds[i] = dawnColor; // 1я 1/10 диодов: цвет текущего шага + if (i % 6 == 1) leds[i] = dawnColorMinus1; // 2я 1/10 диодов: -1 шаг + if (i % 6 == 2) leds[i] = dawnColorMinus2; // 3я 1/10 диодов: -2 шага + if (i % 6 == 3) leds[i] = dawnColorMinus3; // 3я 1/10 диодов: -3 шага + if (i % 6 == 4) leds[i] = dawnColorMinus4; // 3я 1/10 диодов: -4 шага + if (i % 6 == 5) leds[i] = dawnColorMinus5; // 3я 1/10 диодов: -5 шагов + } FastLED.setBrightness(255); delay(1); FastLED.show(); @@ -86,6 +108,12 @@ void timeTick() changePower(); // выключение матрицы или установка яркости текущего эффекта в засисимости от того, была ли включена лампа до срабатывания будильника } manualOff = false; + dawnColorMinus1 = CHSV(0, 0, 0); + dawnColorMinus2 = CHSV(0, 0, 0); + dawnColorMinus3 = CHSV(0, 0, 0); + dawnColorMinus4 = CHSV(0, 0, 0); + dawnColorMinus5 = CHSV(0, 0, 0); + dawnCounter = 0; } } } diff --git a/firmware/GyverLamp_v1.4/utility.ino b/firmware/GyverLamp_v1.4/utility.ino index 67c4224..0bfc733 100644 --- a/firmware/GyverLamp_v1.4/utility.ino +++ b/firmware/GyverLamp_v1.4/utility.ino @@ -10,10 +10,10 @@ void fillAll(CRGB color) } // функция отрисовки точки по координатам X Y -void drawPixelXY(uint8_t x, uint8_t y, CRGB color) +void drawPixelXY(int16_t x, int16_t y, CRGB color) { - if (x < 0 || x > WIDTH - 1 || y < 0 || y > HEIGHT - 1) return; - uint32_t thisPixel = getPixelNumber(x, y) * SEGMENTS; + if (x < 0 || x > (int16_t)(WIDTH - 1) || y < 0 || y > (int16_t)(HEIGHT - 1)) return; + uint32_t thisPixel = getPixelNumber((uint8_t)x, (uint8_t)y) * SEGMENTS; for (uint8_t i = 0; i < SEGMENTS; i++) { leds[thisPixel + i] = color; @@ -24,7 +24,7 @@ void drawPixelXY(uint8_t x, uint8_t y, CRGB color) uint32_t getPixColor(uint32_t thisSegm) { uint32_t thisPixel = thisSegm * SEGMENTS; - if (thisPixel < 0 || thisPixel > NUM_LEDS - 1) return 0; + if (thisPixel > NUM_LEDS - 1) return 0; return (((uint32_t)leds[thisPixel].r << 16) | ((uint32_t)leds[thisPixel].g << 8 ) | (uint32_t)leds[thisPixel].b); }