mirror of
https://github.com/gunner47/GyverLamp.git
synced 2025-08-07 17:10:30 +03:00
Исправлено выключение будильника, если он сработал при выключенной матрице; Добавлено обновление прошивки по воздуху
This commit is contained in:
@@ -23,6 +23,11 @@
|
||||
--- 11.07.2019
|
||||
- Исправлена ошибка невыключения матрицы после срабатывания будильника, если до будильника матрица была выключенной
|
||||
- Дополнена таблица с тест кейсами
|
||||
--- 14.07.2019
|
||||
- Исправлена ошибка выключения будильника, если перед его срабатыванием был активен эффект "матрица" (или другой эффект, где задействовано мало светодиодов)
|
||||
- Добавлено управление по воздуху:
|
||||
-- работает только в режиме WiFi клиента
|
||||
-- работает при подключенной кнопке (потому что режим прошивки активируется кнопкой)
|
||||
*/
|
||||
|
||||
// Ссылка для менеджера плат:
|
||||
@@ -63,6 +68,10 @@
|
||||
#define ESP_CONF_TIMEOUT (300U) // время в секундах, которое ESP будет ждать ввода SSID и пароля WiFi сети роутера в конфигурационном режиме, после его истечения ESP перезагружается
|
||||
#define GENERAL_DEBUG // если строка не закомментирована, будут выводиться отладочные сообщения
|
||||
#define WIFIMAN_DEBUG (true) // вывод отладочных сообщений при подключении к WiFi сети: true - выводятся, false - не выводятся; настройка не зависит от GENERAL_DEBUG
|
||||
#define OTA // если строка не закомментирована, модуль будет ждать два последдовательных запроса пользователя на прошивку по воздуху (см. документацию в "шапке")
|
||||
#ifdef OTA
|
||||
#define ESP_OTA_PORT (8266U) // номер порта, который будет "прослушиваться" в ожидании команды прошивки по воздуху
|
||||
#endif
|
||||
|
||||
// --- ESP (WiFi клиент) ---------------
|
||||
uint8_t STA_STATIC_IP[] ={}; // статический IP адрес: {} - IP адрес определяется роутером; {192, 168, 1, 66} - IP адрес задан явно (если DHCP на роутере не решит иначе); должен быть из того же диапазона адресов, что разадёт роутер
|
||||
@@ -101,6 +110,9 @@ uint8_t AP_STATIC_IP[] = {192, 168, 4, 1}; // статичес
|
||||
#ifdef USE_NTP
|
||||
#include <NTPClient.h>
|
||||
#endif
|
||||
#ifdef OTA
|
||||
#include "OtaManager.h"
|
||||
#endif
|
||||
|
||||
// --- ИНИЦИАЛИЗАЦИЯ ОБЪЕКТОВ ----------
|
||||
CRGB leds[NUM_LEDS];
|
||||
@@ -115,6 +127,10 @@ timerMinim timeTimer(3000);
|
||||
#ifdef ESP_USE_BUTTON
|
||||
GButton touch(BTN_PIN, LOW_PULL, NORM_OPEN);
|
||||
#endif
|
||||
#ifdef OTA
|
||||
OtaManager otaManager;
|
||||
OtaPhase OtaManager::OtaFlag = OtaPhase::None;
|
||||
#endif
|
||||
|
||||
// --- ИНИЦИАЛИЗАЦИЯ ПЕРЕМЕННЫХ -------
|
||||
uint16_t localPort = ESP_UDP_PORT;
|
||||
@@ -303,6 +319,9 @@ void loop()
|
||||
#ifdef ESP_USE_BUTTON
|
||||
buttonTick();
|
||||
#endif
|
||||
#ifdef OTA
|
||||
otaManager.HandleOtaUpdate(); // ожидание и обработка команды на обновление прошивки по воздуху
|
||||
#endif
|
||||
ESP.wdtFeed(); // пнуть собаку
|
||||
yield(); // обработать все "служебные" задачи: WiFi подключение и т.д.
|
||||
}
|
||||
|
220
firmware/GyverLamp_v1.4/OtaManager.h
Normal file
220
firmware/GyverLamp_v1.4/OtaManager.h
Normal file
@@ -0,0 +1,220 @@
|
||||
/*
|
||||
* 11.07.2019
|
||||
* Класс, который отслеживает действия пользователя по запросу обновления прошивки по воздуху и выполняет эту прошивку.
|
||||
* Запрос на обновление - это вызов метода RequestOtaUpdate(), его нужно поместить, например, в обработчик нажатия кнопки, приёма UDP пакета и т.д.
|
||||
* Для обновления пользователь должен ДВАЖДЫ запросить обновление в течение заданного промежутка времени (CONFIRMATION_TIMEOUT) во избежание случайного перехода в режим обновления.
|
||||
* Режим обновления - это прослушивание специального порта (ESP_OTA_PORT) в ожидании команды обновления прошивки по воздуху (по сети).
|
||||
* Режим обновления работает параллельно с основным режимом функционирования, только при ESP_MODE == 1 (WiFi клиент), т.к. требует доступа к ESP по локальной сети и при подключенной кнопке (в данном сетапе, т.к. он вызывается кнопкой).
|
||||
* Режим обновления активен в течение заданного промежутка времени (ESP_CONF_TIMEOUT). Потом ESP автоматически перезагружается.
|
||||
* Обновление производится из Arduino IDE: меню Инструменты - Порт - <Выбрать обнаруженный СЕТЕВОЙ COM порт из списка> (если он не обнаружен, значит что-то настроено неправильно), затем обычная команда "Загрузка" для прошивки.
|
||||
* Для включения опции обновления по воздуху в основном файле должен быть определён идентификатор OTA "#define OTA" и режим "#define ESP_MODE (1U)" (а также в данном проекте должна быть подключена кнопка).
|
||||
*/
|
||||
|
||||
#ifdef OTA
|
||||
|
||||
#include <ArduinoOTA.h>
|
||||
#include <ESP8266mDNS.h>
|
||||
|
||||
|
||||
#define CONFIRMATION_TIMEOUT (30U) // время в сеундах, в течение которого нужно дважды подтвердить старт обновлениЯ по воздуху (иначе сброс в None)
|
||||
|
||||
enum OtaPhase // определение стадий процесса обновления по воздуху: нет, получено первое подтверждение, получено второе подтверждение, получено второе подтверждение - в процессе, обновление окончено
|
||||
{
|
||||
None = 0,
|
||||
GotFirstConfirm,
|
||||
GotSecondConfirm,
|
||||
InProgress,
|
||||
Done
|
||||
};
|
||||
|
||||
class OtaManager
|
||||
{
|
||||
public:
|
||||
static OtaPhase OtaFlag;
|
||||
|
||||
void RequestOtaUpdate() // пользователь однократно запросил обновление по воздуху
|
||||
{
|
||||
if (ESP_MODE != 1)
|
||||
{
|
||||
#ifdef GENERAL_DEBUG
|
||||
Serial.printf("Запрос обновления по воздуху поддерживается только в режиме ESP_MODE = 1\n");
|
||||
#endif
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (OtaFlag == OtaPhase::None)
|
||||
{
|
||||
OtaFlag = OtaPhase::GotFirstConfirm;
|
||||
momentOfFirstConfirmation = millis();
|
||||
|
||||
#ifdef GENERAL_DEBUG
|
||||
Serial.printf("Получено первое подтверждение обновления по воздуху\nОжидание второго подтверждения\n");
|
||||
#endif
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (OtaFlag == OtaPhase::GotFirstConfirm)
|
||||
{
|
||||
OtaFlag = OtaPhase::GotSecondConfirm;
|
||||
|
||||
#ifdef GENERAL_DEBUG
|
||||
Serial.printf("Получено второе подтверждение обновления по воздуху\nСтарт режима обновления\n");
|
||||
#endif
|
||||
|
||||
startOtaUpdate();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void HandleOtaUpdate()
|
||||
{
|
||||
if (OtaFlag == OtaPhase::GotFirstConfirm &&
|
||||
millis() - momentOfFirstConfirmation >= CONFIRMATION_TIMEOUT * 1000)
|
||||
{
|
||||
OtaFlag = OtaPhase::None;
|
||||
momentOfFirstConfirmation = 0;
|
||||
|
||||
#ifdef GENERAL_DEBUG
|
||||
Serial.printf("Таймаут ожидания второго подтверждения превышен\nСброс флага в исходное состояние\n");
|
||||
#endif
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (OtaFlag == OtaPhase::GotSecondConfirm &&
|
||||
millis() - momentOfOtaStart >= ESP_CONF_TIMEOUT * 1000)
|
||||
{
|
||||
OtaFlag = OtaPhase::None;
|
||||
momentOfOtaStart = 0;
|
||||
|
||||
#ifdef GENERAL_DEBUG
|
||||
Serial.printf("Таймаут ожидания прошивки по воздуху превышен\nСброс флага в исходное состояние\nПерезагрузка\n");
|
||||
delay(500);
|
||||
#endif
|
||||
|
||||
#if defined(ESP8266)
|
||||
ESP.reset();
|
||||
#else
|
||||
ESP.restart();
|
||||
#endif
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (OtaFlag == OtaPhase::InProgress)
|
||||
{
|
||||
ArduinoOTA.handle();
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
uint64_t momentOfFirstConfirmation = 0; // момент времени, когда получено первое подтверждение и с которого начинается отсчёт ожидания второго подтверждения
|
||||
uint64_t momentOfOtaStart = 0; // момент времени, когда развёрнута WiFi точка доступа для обновления по воздуху
|
||||
|
||||
void startOtaUpdate()
|
||||
{
|
||||
char espHostName[65];
|
||||
sprintf(espHostName, "%s-%u", AP_NAME, ESP.getChipId());
|
||||
ArduinoOTA.setPort(ESP_OTA_PORT);
|
||||
ArduinoOTA.setHostname(espHostName);
|
||||
ArduinoOTA.setPassword(AP_PASS);
|
||||
|
||||
ArduinoOTA.onStart([this]()
|
||||
{
|
||||
String type;
|
||||
if (ArduinoOTA.getCommand() == U_FLASH)
|
||||
{
|
||||
type = "sketch";
|
||||
}
|
||||
else // U_SPIFFS
|
||||
{
|
||||
type = "filesystem";
|
||||
}
|
||||
|
||||
// NOTE: if updating SPIFFS this would be the place to unmount SPIFFS using SPIFFS.end()
|
||||
|
||||
#ifdef GENERAL_DEBUG
|
||||
Serial.println("Start updating " + type);
|
||||
#endif
|
||||
});
|
||||
|
||||
ArduinoOTA.onEnd([this]()
|
||||
{
|
||||
OtaFlag = OtaPhase::Done;
|
||||
|
||||
#ifdef GENERAL_DEBUG
|
||||
Serial.printf("Обновление по воздуху выполнено\nПерезапуск");
|
||||
delay(500);
|
||||
#endif
|
||||
});
|
||||
|
||||
ArduinoOTA.onProgress([](unsigned int progress, unsigned int total)
|
||||
{
|
||||
#ifdef GENERAL_DEBUG
|
||||
Serial.printf("Ход выполнения: %u%%\r", (progress / (total / 100)));
|
||||
#endif
|
||||
});
|
||||
|
||||
ArduinoOTA.onError([this](ota_error_t error)
|
||||
{
|
||||
OtaFlag = OtaPhase::None;
|
||||
|
||||
#ifdef GENERAL_DEBUG
|
||||
Serial.printf("Обновление по воздуху завершилось ошибкой [%u]: ", error);
|
||||
#endif
|
||||
|
||||
if (error == OTA_AUTH_ERROR)
|
||||
{
|
||||
#ifdef GENERAL_DEBUG
|
||||
Serial.println("Auth Failed");
|
||||
#endif
|
||||
}
|
||||
else if (error == OTA_BEGIN_ERROR)
|
||||
{
|
||||
#ifdef GENERAL_DEBUG
|
||||
Serial.println("Begin Failed");
|
||||
#endif
|
||||
}
|
||||
else if (error == OTA_CONNECT_ERROR)
|
||||
{
|
||||
#ifdef GENERAL_DEBUG
|
||||
Serial.println("Connect Failed");
|
||||
#endif
|
||||
}
|
||||
else if (error == OTA_RECEIVE_ERROR)
|
||||
{
|
||||
#ifdef GENERAL_DEBUG
|
||||
Serial.println("Receive Failed");
|
||||
#endif
|
||||
}
|
||||
else if (error == OTA_END_ERROR)
|
||||
{
|
||||
#ifdef GENERAL_DEBUG
|
||||
Serial.println("End Failed");
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef GENERAL_DEBUG
|
||||
Serial.printf("Сброс флага в исходное состояние\nПереход в режим ожидания запроса прошивки по воздуху\n");
|
||||
#endif
|
||||
});
|
||||
|
||||
ArduinoOTA.setRebootOnSuccess(true);
|
||||
ArduinoOTA.begin();
|
||||
OtaFlag = OtaPhase::InProgress;
|
||||
momentOfFirstConfirmation = 0;
|
||||
momentOfOtaStart = 0;
|
||||
|
||||
#ifdef GENERAL_DEBUG
|
||||
Serial.printf("Для обновления в Arduino IDE выберите пункт меню Инструменты - Порт - '%s at ", espHostName);
|
||||
Serial.print(WiFi.localIP());
|
||||
Serial.println("'");
|
||||
Serial.printf("Затем нажмите кнопку 'Загрузка' в течение %u секунд и по запросу введите пароль '%ы'\n", ESP_CONF_TIMEOUT, AP_PASS);
|
||||
Serial.println("Устройство с Arduino IDE должно быть в одной локальной сети с модулем ESP!");
|
||||
#endif
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
@@ -43,6 +43,13 @@ void buttonTick()
|
||||
delay(1);
|
||||
}
|
||||
|
||||
if (ONflag && touch.isQuadruple())
|
||||
{
|
||||
#ifdef OTA
|
||||
otaManager.RequestOtaUpdate();
|
||||
#endif
|
||||
}
|
||||
|
||||
if (ONflag && touch.isHolded())
|
||||
{
|
||||
brightDirection = !brightDirection;
|
||||
|
@@ -37,6 +37,9 @@ void timeTick()
|
||||
{
|
||||
dawnFlag = false;
|
||||
manualOff = false;
|
||||
FastLED.clear();
|
||||
delay(2);
|
||||
FastLED.show();
|
||||
changePower(); // выключение матрицы или установка яркости текущего эффекта в засисимости от того, была ли включена лампа до срабатывания будильника
|
||||
}
|
||||
}
|
||||
|
@@ -106,6 +106,13 @@ boolean GButton::isTriple() {
|
||||
return true;
|
||||
} else return false;
|
||||
}
|
||||
boolean GButton::isQuadruple() {
|
||||
if (flags.tickMode) GButton::tick();
|
||||
if (flags.counter_flag && last_counter == 4) {
|
||||
flags.counter_flag = false;
|
||||
return true;
|
||||
} else return false;
|
||||
}
|
||||
boolean GButton::hasClicks() {
|
||||
if (flags.tickMode) GButton::tick();
|
||||
if (flags.counter_flag) {
|
||||
|
@@ -44,38 +44,39 @@ typedef struct
|
||||
class GButton
|
||||
{
|
||||
public:
|
||||
GButton(uint8_t pin); // класс кнопки, принимает пин
|
||||
|
||||
GButton(uint8_t pin); // класс кнопки, принимает пин
|
||||
|
||||
GButton(uint8_t pin, boolean type, boolean dir); // класс кнопки, принимает PIN пин, тип type (HIGH_PULL / LOW_PULL) и направление dir (NORM_OPEN / NORM_CLOSE)
|
||||
// HIGH_PULL - кнопка подключена к GND, пин подтянут к VCC, pinMode - INPUT_PULLUP (по умолчанию)
|
||||
// LOW_PULL - кнопка подключена к VCC, пин подтянут к GND, pinMode - INPUT
|
||||
// NORM_OPEN - кнопка по умолчанию разомкнута (по умолчанию)
|
||||
// NORM_CLOSE - кнопка по умолчанию замкнута
|
||||
|
||||
|
||||
void setDebounce(uint16_t debounce); // установка времени антидребезга (по умолчанию 80 мс)
|
||||
void setTimeout(uint16_t timeout); // установка таймаута удержания (по умолчанию 300 мс)
|
||||
void setClickTimeout(uint16_t timeout); // установка таймаута между кликами (по умолчанию 500 мс)
|
||||
void setStepTimeout(uint16_t step_timeout); // установка таймаута между инкрементами (по умолчанию 400 мс)
|
||||
void setType(boolean type); // установка типа кнопки (HIGH_PULL - подтянута к питанию, LOW_PULL - к gnd)
|
||||
void setDirection(boolean dir); // установка направления (разомкнута/замкнута по умолчанию - NORM_OPEN, NORM_CLOSE)
|
||||
|
||||
|
||||
void setTickMode(boolean tickMode); // (MANUAL / AUTO) ручной или автоматический опрос кнопки функцией tick()
|
||||
// MANUAL - нужно вызывать функцию tick() вручную
|
||||
// AUTO - tick() входит во все остальные функции и опрашивается сама
|
||||
|
||||
|
||||
void tick(); // опрос кнопки
|
||||
void tick(boolean state); // опрос внешнего значения (0 нажато, 1 не нажато) (для матричных, резистивных клавиатур и джойстиков)
|
||||
|
||||
boolean isPress(); // возвращает true при нажатии на кнопку. Сбрасывается после вызова
|
||||
boolean isRelease(); // возвращает true при отпускании кнопки. Сбрасывается после вызова
|
||||
boolean isClick(); // возвращает true при клике. Сбрасывается после вызова
|
||||
boolean isHolded(); // возвращает true при удержании дольше timeout. Сбрасывается после вызова
|
||||
boolean isHolded(); // возвращает true при удержании дольше timeout. Сбрасывается после вызова
|
||||
boolean isHold(); // возвращает true при нажатой кнопке, не сбрасывается
|
||||
boolean state(); // возвращает состояние кнопки
|
||||
|
||||
boolean isSingle(); // возвращает true при одиночном клике. Сбрасывается после вызова
|
||||
boolean isDouble(); // возвращает true при двойном клике. Сбрасывается после вызова
|
||||
boolean isTriple(); // возвращает true при тройном клике. Сбрасывается после вызова
|
||||
boolean isQuadruple(); // возвращает true при четверном клике. Сбрасывается после вызова
|
||||
|
||||
boolean hasClicks(); // проверка на наличие кликов. Сбрасывается после вызова
|
||||
uint8_t getClicks(); // вернуть количество кликов
|
||||
@@ -84,8 +85,8 @@ class GButton
|
||||
|
||||
private:
|
||||
void init();
|
||||
GyverButtonFlags flags;
|
||||
uint8_t _PIN = 0;
|
||||
GyverButtonFlags flags;
|
||||
uint8_t _PIN = 0;
|
||||
uint16_t _debounce = 0;
|
||||
uint16_t _timeout = 0;
|
||||
uint16_t _click_timeout = 0;
|
||||
|
Reference in New Issue
Block a user