Исправлено выключение будильника, если он сработал при выключенной матрице; Добавлено обновление прошивки по воздуху

This commit is contained in:
gunner47
2019-07-16 22:55:39 +03:00
parent e1c537c971
commit 26afb403ba
6 changed files with 265 additions and 8 deletions

View File

@@ -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 подключение и т.д.
}

View 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

View File

@@ -43,6 +43,13 @@ void buttonTick()
delay(1);
}
if (ONflag && touch.isQuadruple())
{
#ifdef OTA
otaManager.RequestOtaUpdate();
#endif
}
if (ONflag && touch.isHolded())
{
brightDirection = !brightDirection;

View File

@@ -37,6 +37,9 @@ void timeTick()
{
dawnFlag = false;
manualOff = false;
FastLED.clear();
delay(2);
FastLED.show();
changePower(); // выключение матрицы или установка яркости текущего эффекта в засисимости от того, была ли включена лампа до срабатывания будильника
}
}

View File

@@ -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) {

View File

@@ -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;