Добавлено управление по протоколу MQTT; добавлена возможность вывода отладочных сообщений в telnet; исправлены ошибки (будильник, управление яркостью кнопкой, затирание параметров WiFi при включении)

This commit is contained in:
gunner47
2019-10-05 22:20:33 +03:00
parent d92b060335
commit 921a25bd16
12 changed files with 681 additions and 116 deletions

View File

@@ -1,5 +1,7 @@
#pragma once
#include <ESP8266WebServer.h>
// ============= НАСТРОЙКИ =============
// --- ESP -----------------------------
@@ -34,6 +36,12 @@ const uint8_t AP_STATIC_IP[] = {192, 168, 4, 1}; // статичес
#define NTP_ADDRESS ("ntp2.colocall.net") // сервер времени
#define NTP_INTERVAL (30UL * 60UL * 1000UL) // интервал синхронизации времени (30 минут)
// --- ВНЕШНЕЕ УПРАВЛЕНИЕ --------------
#define USE_MQTT (true) // true - используется mqtt клиент, false - нет
#if USE_MQTT
#define MQTT_RECONNECT_TIME (10U) // время в секундах перед подключением к MQTT брокеру в случае потери подключения
#endif
// --- РАССВЕТ -------------------------
#define DAWN_BRIGHT (200U) // максимальная яркость рассвета (0-255)
#define DAWN_TIMEOUT (1U) // сколько рассвет светит после времени будильника, минут
@@ -86,6 +94,18 @@ const uint8_t AP_STATIC_IP[] = {192, 168, 4, 1}; // статичес
//#define MAX_UDP_BUFFER_SIZE (UDP_TX_PACKET_MAX_SIZE + 1)
#define MAX_UDP_BUFFER_SIZE (129U) // максимальный размер буффера UDP сервера
#define GENERAL_DEBUG_TELNET (true) // true - отладочные сообщения будут выводиться в telnet вместо Serial порта (для удалённой отладки без подключения usb кабелем)
#define TELNET_PORT (23U) // номер telnet порта
#if defined(GENERAL_DEBUG) && GENERAL_DEBUG_TELNET
WiFiServer telnetServer(TELNET_PORT); // telnet сервер
WiFiClient telnet; // обработчик событий telnet клиента
bool telnetGreetingShown = false; // признак "показано приветствие в telnet"
#define LOG telnet
#else
#define LOG Serial
#endif
// --- БИБЛИОТЕКИ ----------------------
#define FASTLED_INTERRUPT_RETRY_COUNT (0U)
#define FASTLED_ALLOW_INTERRUPTS (0U)

View File

@@ -1,6 +1,7 @@
#pragma once
#include <EEPROM.h>
#include "EepromManager.h"
#include "Constants.h"
#define DEFAULT_FAVORITES_INTERVAL (300U) // значение по умолчанию для интервала переключения избпранных эффектов в секундах
#define DEFAULT_FAVORITES_DISPERSION (0U) // значение по умолчанию для разброса интервала переключения избпранных эффектов в секундах
@@ -99,7 +100,7 @@ class FavoritesManager
nextModeAt = getNextTime();
#ifdef GENERAL_DEBUG
Serial.printf_P(PSTR("Переключение на следующий избранный режим: %d\n\n"), (*currentMode));
LOG.printf_P(PSTR("Переключение на следующий избранный режим: %d\n\n"), (*currentMode));
#endif
return true;

View File

@@ -16,8 +16,8 @@
- Добавлено "#define USE_NTP" - позволяет запретить обращаться в интернет
- Добавлено "#define ESP_USE_BUTTON - позволяет собирать лампу без физической кнопки, иначе яркость эффектов самопроизвольно растёт до максимальной
- Переработаны параметры IP адресов, STA_STATIC_IP теперь пустой по умолчанию - избавляет от путаницы с IP адресами из неправильных диапазонов
- Добавлено "#define GENERAL_DEBUG" - выводит в Serial некоторые отладочные сообщения
- Добавлено "#define WIFIMAN_DEBUG (true)" - выводит в Serial отладочные сообщения библиотеки WiFiManager
- Добавлено "#define GENERAL_DEBUG" - выводит в Serial/Telnet некоторые отладочные сообщения
- Добавлено "#define WIFIMAN_DEBUG (true)" - выводит в Serial/Telnet отладочные сообщения библиотеки WiFiManager
- Добавлена таблица с тест кейсами
- Форматирование кода, комментарии
--- 11.07.2019
@@ -67,6 +67,13 @@
- Убрана очистка параметров WiFi при старте с зажатой кнопкой; регулируется директивой ESP_RESET_ON_STASRT, которая определена как false по умолчанию
--- 24.09.2019
- Добавлены изменения из прошивка от Alex Gyver v1.5: бегущая строка с IP адресом лампы по пятикратному клику на кнопку
--- 29.09.2019
- Добавлена опция вывода отладочных сообщений по пртоколу telnet вместо serial для удалённой отладки
- Исправлена ошибка регулировки яркости кнопкой
--- 05.10.2019
- Добавлено управление по протоколу MQTT
- Исправлена ошибка выключения будильника кнопкой
- Добавлена задержка в 1 секунду сразу после старта, в течение которой нужно нажать кнопку, чтобы очистить сохранённые параметры WiFi (если ESP_RESET_ON_STASRT == true)
*/
// Ссылка для менеджера плат:
@@ -93,6 +100,9 @@
#ifdef OTA
#include "OtaManager.h"
#endif
#if USE_MQTT
#include "MqttManager.h"
#endif
#include "TimerManager.h"
#include "FavoritesManager.h"
#include "EepromManager.h"
@@ -114,11 +124,28 @@ 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
#if USE_MQTT
AsyncMqttClient* mqttClient = NULL;
AsyncMqttClient* MqttManager::mqttClient = NULL;
char* MqttManager::mqttServer = NULL;
char* MqttManager::mqttUser = NULL;
char* MqttManager::mqttPassword = NULL;
char* MqttManager::clientId = NULL;
char* MqttManager::topicInput = NULL;
char* MqttManager::topicOutput = NULL;
bool MqttManager::needToPublish = false;
char MqttManager::mqttBuffer[] = {};
uint32_t MqttManager::mqttLastConnectingAttempt = 0;
SendCurrentDelegate MqttManager::sendCurrentDelegate = NULL;
// volatile uint32_t wifiLastConnectingAttempt = 0;
#endif
// --- ИНИЦИАЛИЗАЦИЯ ПЕРЕМЕННЫХ -------
uint16_t localPort = ESP_UDP_PORT;
char packetBuffer[MAX_UDP_BUFFER_SIZE]; // buffer to hold incoming packet
@@ -160,30 +187,47 @@ void setup()
Serial.begin(115200);
Serial.println();
ESP.wdtDisable();
//ESP.wdtEnable(WDTO_8S);
#if defined(ESP_USE_BUTTON) && ESP_RESET_ON_STASRT
touch.setStepTimeout(100);
touch.setClickTimeout(500);
buttonTick();
if (touch.state()) // сброс сохранённых SSID и пароля при старте с зажатой кнопкой, если разрешено
// TELNET
#if defined(GENERAL_DEBUG) && GENERAL_DEBUG_TELNET
telnetServer.begin();
for (uint8_t i = 0; i < 100; i++) // пауза 10 секунд в отладочном режиме, чтобы успеть подключиться по протоколу telnet до вывода первых сообщений
{
wifiManager.resetSettings();
#ifdef GENERAL_DEBUG
Serial.println(F("Настройки WiFiManager сброшены"));
#endif
handleTelnetClient();
delay(100);
}
#endif
// ЛЕНТА
ESP.wdtDisable();
//ESP.wdtEnable(WDTO_8S);
// КНОПКА
#if defined(ESP_USE_BUTTON)
touch.setStepTimeout(100);
touch.setClickTimeout(500);
#if ESP_RESET_ON_STASRT
delay(1000); // ожидание инициализации модуля кнопки ttp223 (по спецификации 250мс)
if (digitalRead(BTN_PIN))
{
wifiManager.resetSettings(); // сброс сохранённых SSID и пароля при старте с зажатой кнопкой, если разрешено
#ifdef GENERAL_DEBUG
LOG.println(F("Настройки WiFiManager сброшены"));
#endif
}
#endif
#endif
// ЛЕНТА/МАТРИЦА
FastLED.addLeds<WS2812B, LED_PIN, COLOR_ORDER>(leds, NUM_LEDS)/*.setCorrection(TypicalLEDStrip)*/;
FastLED.setBrightness(BRIGHTNESS);
if (CURRENT_LIMIT > 0) FastLED.setMaxPowerInVoltsAndMilliamps(5, CURRENT_LIMIT);
FastLED.clear();
FastLED.show();
// WI-FI
wifiManager.setDebugOutput(WIFIMAN_DEBUG); // вывод отладочных сообщений
//wifiManager.setMinimumSignalQuality(); // установка минимально приемлемого уровня сигнала WiFi сетей (8% по умолчанию)
@@ -197,23 +241,23 @@ void setup()
WiFi.softAP(AP_NAME, AP_PASS);
Serial.println(F("Режим WiFi точки доступа"));
Serial.print(F("IP адрес: "));
Serial.println(WiFi.softAPIP());
LOG.println(F("Режим WiFi точки доступа"));
LOG.print(F("IP адрес: "));
LOG.println(WiFi.softAPIP());
wifiServer.begin();
}
else // режим WiFi клиента (подключаемся к роутеру, если есть сохранённые SSID и пароль, иначе создаём WiFi точку доступа и запрашиваем их)
{
Serial.println(F("Режим WiFi клиента"));
LOG.println(F("Режим WiFi клиента"));
if (WiFi.SSID())
{
Serial.print(F("Подключение WiFi сети: "));
Serial.println(WiFi.SSID());
LOG.print(F("Подключение WiFi сети: "));
LOG.println(WiFi.SSID());
}
else
{
Serial.println(F("WiFi сеть не определена, запуск WiFi точки доступа для настройки параметров подключения к WiFi сети..."));
LOG.println(F("WiFi сеть не определена, запуск WiFi точки доступа для настройки параметров подключения к WiFi сети..."));
}
if (STA_STATIC_IP)
@@ -230,7 +274,7 @@ void setup()
if (WiFi.status() != WL_CONNECTED)
{
Serial.println(F("Время ожидания ввода SSID и пароля от WiFi сети или подключения к WiFi сети превышено\nПерезагрузка модуля"));
LOG.println(F("Время ожидания ввода SSID и пароля от WiFi сети или подключения к WiFi сети превышено\nПерезагрузка модуля"));
#if defined(ESP8266)
ESP.reset();
@@ -239,22 +283,35 @@ void setup()
#endif
}
Serial.print(F("IP адрес: "));
Serial.println(WiFi.localIP());
LOG.print(F("IP адрес: "));
LOG.println(WiFi.localIP());
}
Serial.printf_P(PSTR("Порт UDP сервера: %u\n"), localPort);
LOG.printf_P(PSTR("Порт UDP сервера: %u\n"), localPort);
Udp.begin(localPort);
// EEPROM
EepromManager::InitEepromSettings( // инициализация EEPROM; запись начального состояния настроек, если их там ещё нет; инициализация настроек лампы значениями из EEPROM
modes, alarms, &ONflag, &dawnMode, &currentMode,
&(FavoritesManager::ReadFavoritesFromEeprom),
&(FavoritesManager::SaveFavoritesToEeprom));
// NTP
#ifdef USE_NTP
timeClient.begin();
#endif
// MQTT
#if (USE_MQTT && ESP_MODE == 1)
mqttClient = new AsyncMqttClient();
MqttManager::setupMqtt(mqttClient, &sendCurrent); // создание экземпляров объектов для работы с MQTT, их инициализация и подключение к MQTT брокеру
#endif
// ОСТАЛЬНОЕ
memset(matrixValue, 0, sizeof(matrixValue));
randomSeed(micros());
changePower();
@@ -266,19 +323,25 @@ void loop()
{
parseUDP();
effectsTick();
EepromManager::HandleEepromTick(&settChanged, &eepromTimeout, &ONflag,
&currentMode, modes, &(FavoritesManager::SaveFavoritesToEeprom));
&currentMode, modes, &(FavoritesManager::SaveFavoritesToEeprom));
#ifdef USE_NTP
timeTick();
#endif
#ifdef ESP_USE_BUTTON
buttonTick();
#endif
#ifdef OTA
otaManager.HandleOtaUpdate(); // ожидание и обработка команды на обновление прошивки по воздуху
#endif
TimerManager::HandleTimer(&ONflag, &settChanged, // обработка событий таймера отключения лампы
&eepromTimeout, &changePower);
if (FavoritesManager::HandleFavorites( // обработка режима избранных эффектов
&ONflag,
&currentMode,
@@ -292,6 +355,29 @@ void loop()
FastLED.clear();
delay(1);
}
#if USE_MQTT
if (ESP_MODE == 1 && mqttClient && WiFi.isConnected() && !mqttClient->connected())
{
MqttManager::mqttConnect(); // библиотека не умеет восстанавливать соединение в случае потери подключения к MQTT брокеру, нужно управлять этим явно
MqttManager::needToPublish = true;
}
if (MqttManager::needToPublish)
{
if (strlen(inputBuffer) > 0) // проверка входящего MQTT сообщения; если оно не пустое - выполнение команды из него и формирование MQTT ответа
{
processInputBuffer(inputBuffer, MqttManager::mqttBuffer, true);
}
MqttManager::publishState();
}
#endif
#if defined(GENERAL_DEBUG) && GENERAL_DEBUG_TELNET
handleTelnetClient();
#endif
ESP.wdtFeed(); // пнуть собаку
yield(); // обработать все "служебные" задачи: wdt, WiFi подключение и т.д. (?)
}

View File

@@ -0,0 +1,342 @@
#ifdef USE_MQTT
/*
* Библиотека асинхронных MQTT запросов https://github.com/marvinroger/async-mqtt-client
* Не умеет автоматически восстанавливать разорванное соединение с MQTT брокером, поэтому требует периодической проверки подключения
* Зависит от библиотек:
* ESPAsyncTCP https://github.com/me-no-dev/ESPAsyncTCP
* AsyncTCP https://github.com/me-no-dev/AsyncTCP
* Лампа подписана на топик: LedLamp/LedLamp_xxxxxxxx/cmnd, где xxxxxxxx - ESP.getChipID(); payload - строка, содержащая те же команды, что отправляются приложением (регистр важен):
* P_ON - включить матрицу
* P_OFF - выключить матрицу
* EFF0 - сделать активным эффект №0 (нумерация с нуля)
* BRI44 - установить яркость 44; диапазон [1..255]
* SPD3 - установить скорость 3; диапазон [1..255]
* SCA1 - установить масштаб 1; диапазон [1..100]
* ALM_SET1 ON - завести будильник 1 (понедельник); ON - вкл, OFF - выкл
* ALM_SET1 390 - установить время будильника 1 (понедельник) на 06:30 (количество минут от начала суток)
* DAWN1 - установить "рассвет" за 5 минут до будильника (1 = 5 минут - номер опции в выпадающем списке в приложении, нумерация с единицы)
* TMR_SET 1 3 300 - установить таймер; описание параметров - см. команду TMR ниже
* FAV_SET 1 60 120 0 0 1 0 0 0 0 0 1 1 0 0 1 0 0 0 0 0 0 1 0 0 0 1 0 0 - установить режим "избранное", параметры - см. команду FAV ниже
* Лампа отправляет своё состояние сразу после включения и после каждого изменения в топик LedLamp/LedLamp_00316765/state; payload:
* "CURR 7 14 4 50 1 1 1 0 21:25:50", где:
* CURR - идентификатор команды, CURR - текущее состояние лампы
* 7 - номер текущего эффекта
* 14 - яркость
* 4 - скорость
* 50 - масштаб
* 1 - признак "матрица включена"
* 1 - режим ESP_MODE
* 1 - признак "работает таймер"
* 0 - признак USE_NTP (пытаться синхронизировать время по серверам времени в интернете)
* 21:25:50 - текущее время (если не синхронизировано, показывает время от старта модуля)
* "ALMS 1 0 0 0 0 0 0 0 390 0 0 0 0 0 0 1"
* ALMS - идентификатор команды, ALMS - настройки будильников
* первые 7 цифр - признак "будильник заведён" по дням недели, начиная с понедельника
* последующие 7 цифр - время в минутах от начала суток, на которое заведён будильник (по дням недели); 390 = 06:30
* последняя цифра - опция "рассвет за ... минут", цифра указывает на номер значения в выпадающем списке: 1 - 5 минут, 2 - 10 минут... (см. в приложении)
* "TMR 1 3 300"
* TMR - идентификатор команды, TMR - таймер
* 1 - признак "таймер взведён"
* 3 - опция "выключить лампу через ...", цифра указывает на номер значения в выпадающем списке: 1 - не выключать, 2 - 1 минута... (см. в приложении)
* 300 - количество секунд, через которое выключится лампа (0 - не выключать)
* "FAV 1 60 120 0 0 1 0 0 0 0 0 1 1 0 0 1 0 0 0 0 0 0 1 0 0 0 1 0 0"
* FAV - идентификатор команды, FAV - избранное
* 1 - режим "избранное" включен
* 60 - интервал смены эффектов в секундах
* 120 - случайный разброс смены эффектов (применяется дополнительно к интервалу) в секундах
* 0 - признак "запомнить состояние" вкл/выкл режима "избранное" в энергонезависимую память
* оставшиеся цифры - признак (0/1) "эффект №... добавлен в избранные", где номер цифры соотвтетсвует номеру эффекта в списке (см. приложение)
*/
#include <AsyncMqttClient.h>
#include "pgmspace.h"
#include "Constants.h"
#include "Types.h"
static const char TopicBase[] PROGMEM = "LedLamp"; // базовая часть топиков
static const char TopicCmnd[] PROGMEM = "cmnd"; // часть командных топиков (входящие команды лампе)
static const char TopicState[] PROGMEM = "state"; // часть топиков состояния (ответ от лампы)
static const char MqttServer[] PROGMEM = "192.168.0.100"; // строка с IP адресом MQTT брокера
static const uint16_t MqttPort = 1883U; // порт MQTT брокера
static const char MqttUser[] PROGMEM = ""; // пользователь MQTT брокера
static const char MqttPassword[] PROGMEM = ""; // пароль пользователя MQTT брокера
static const char MqttClientIdPrefix[] PROGMEM = "LedLamp_"; // id клиента MQTT брокера (к нему будет добавлен ESP.getChipId)
class MqttManager
{
public:
static uint32_t mqttLastConnectingAttempt;
static void setupMqtt(AsyncMqttClient* mqttClient, SendCurrentDelegate sendCurrentDelegate);
static void mqttConnect();
static void onMqttConnect(bool sessionPresent);
static void onMqttDisconnect(AsyncMqttClientDisconnectReason reason);
static void onMqttMessage(char *topic, char* payload, AsyncMqttClientMessageProperties properties, size_t len, size_t index, size_t total);
static bool publish(const char *topic, const char *value);
static void publishState();
static bool needToPublish;
static char mqttBuffer[MAX_UDP_BUFFER_SIZE];
private:
static char* mqttServer;
static char* mqttUser;
static char* mqttPassword;
static char* topicInput; // TopicBase + '/' + MqttClientIdPrefix + ESP.getChipId + '/' + TopicCmnd
static char* topicOutput; // TopicBase + '/' + MqttClientIdPrefix + ESP.getChipId + '/' + TopicState
static char* clientId;
static AsyncMqttClient* mqttClient;
static const uint8_t qos = 0U; // MQTT quality of service
static const uint32_t connectionTimeout = MQTT_RECONNECT_TIME * 1000U; // период времени для проверки (пере)подключения к MQTT брокеру, 10 секунд
static char* byteToHex(char *out, uint8_t value);
static bool allocStr(char **str, const char *src);
static bool allocStr_P(char **str, PGM_P src);
static SendCurrentDelegate sendCurrentDelegate;
};
void MqttManager::setupMqtt(AsyncMqttClient* mqttClient, SendCurrentDelegate sendCurrentDelegate)
{
allocStr_P(&MqttManager::mqttServer, MqttServer);
allocStr_P(&MqttManager::mqttUser, MqttUser);
allocStr_P(&MqttManager::mqttPassword, MqttPassword);
MqttManager::mqttClient = mqttClient;
MqttManager::sendCurrentDelegate = sendCurrentDelegate;
MqttManager::mqttClient->setServer(MqttManager::mqttServer, MqttPort);
char clientIdBuf[sizeof(MqttClientIdPrefix) + 8];
strcpy_P(clientIdBuf, MqttClientIdPrefix);
uint32_t chipId = ESP.getChipId();
for (uint8_t i = 0; i < 4; ++i)
{
byteToHex(&clientIdBuf[i * 2 + sizeof(MqttClientIdPrefix) - 1], chipId >> ((3 - i) * 8));
}
allocStr(&clientId, clientIdBuf);
MqttManager::mqttClient->setClientId(clientId);
if (MqttManager::mqttUser != NULL)
{
MqttManager::mqttClient->setCredentials(MqttManager::mqttUser, MqttManager::mqttPassword);
}
uint8_t topicLength = sizeof(TopicBase) + 1 + strlen(clientId) + 1 + sizeof(TopicCmnd) + 1;
topicInput = (char*)malloc(topicLength);
sprintf_P(topicInput, PSTR("%s/%s/%s"), TopicBase, clientId, TopicCmnd); // topicInput = TopicBase + '/' + MqttClientIdPrefix + ESP.getChipId + '/' + TopicCmnd
topicLength = sizeof(TopicBase) + 1 + strlen(clientId) + 1 + sizeof(TopicState) + 1;
topicOutput = (char*)malloc(topicLength);
sprintf_P(topicOutput, PSTR("%s/%s/%s"), TopicBase, clientId, TopicState); // topicOutput = TopicBase + '/' + MqttClientIdPrefix + ESP.getChipId + '/' + TopicState
#ifdef GENERAL_DEBUG
LOG.printf_P(PSTR("MQTT топик для входящих команд: %s\n"), topicInput);
LOG.printf_P(PSTR("MQTT топик для исходящих ответов лампы: %s\n"), topicOutput);
#endif
mqttClient->onConnect(onMqttConnect);
mqttClient->onDisconnect(onMqttDisconnect);
mqttClient->onMessage(onMqttMessage);
}
void MqttManager::mqttConnect()
{
if ((!mqttLastConnectingAttempt) || (millis() - mqttLastConnectingAttempt >= connectionTimeout))
{
#ifdef GENERAL_DEBUG
LOG.print(F("Подключение к MQTT брокеру \""));
LOG.print(MqttManager::mqttServer);
LOG.print(':');
LOG.print(MqttPort);
LOG.println(F("\"..."));
#endif
mqttClient->disconnect();
mqttClient->connect();
mqttLastConnectingAttempt = millis();
}
}
bool MqttManager::publish(const char *topic, const char *value)
{
if (mqttClient->connected())
{
#ifdef GENERAL_DEBUG
LOG.print(F("Отправлено MQTT: топик \""));
LOG.print(topic);
LOG.print(F("\", значение \""));
LOG.print(value);
LOG.println('"');
LOG.println();
#endif
return mqttClient->publish(topic, qos, true, value, 0) != 0;
}
return false;
}
void MqttManager::onMqttConnect(bool sessionPresent)
{
#ifdef GENERAL_DEBUG
LOG.println(F("Подключено к MQTT брокеру"));
#endif
mqttLastConnectingAttempt = 0;
mqttClient->subscribe(topicInput, 1);
publishState();
}
void MqttManager::onMqttDisconnect(AsyncMqttClientDisconnectReason reason)
{
#ifdef GENERAL_DEBUG
LOG.println(F("Отключено от MQTT брокера"));
#endif
}
void MqttManager::onMqttMessage(char* topic, char* payload, AsyncMqttClientMessageProperties properties, size_t len, size_t index, size_t total)
{
if (payload != NULL) // сохраняем пришедшее MQTT сообщение для дальнейшей обработки
{
strncpy(mqttBuffer, payload, len);
mqttBuffer[len] = '\0';
}
#ifdef GENERAL_DEBUG
LOG.print(F("Получено MQTT:"));
LOG.print(F(" топик \""));
LOG.print(topic);
LOG.print("\"");
/*
LOG.print(F(" qos: "));
LOG.println(properties.qos);
LOG.print(F(" dup: "));
LOG.println(properties.dup);
LOG.print(F(" retain: "));
LOG.println(properties.retain);
LOG.print(F(" len: "));
LOG.println(len);
LOG.print(F(" index: "));
LOG.println(index);
LOG.print(F(" total: "));
LOG.println(total);
*/
LOG.print(F(", значение \""));
LOG.print(mqttBuffer);
LOG.println("\"");
LOG.println();
#endif
}
void MqttManager::publishState()
{
if (mqttBuffer == NULL || strlen(mqttBuffer) <= 0)
{
sendCurrentDelegate(mqttBuffer); // если буфер MQTT ответа не задан, но метод MQTT публикации вызван, закполняем его текущим состоянием лампы
}
if (mqttBuffer != NULL && strlen(mqttBuffer) > 0)
{
publish(topicOutput, mqttBuffer); // публикация буфера MQTT ответа
mqttBuffer[0] = '\0'; // очистка буфера
needToPublish = false; // сброс флага для предотвращения повторной публикации
}
}
char* MqttManager::byteToHex(char *out, uint8_t value)
{
uint8_t b;
b = value >> 4;
if (b < 10)
{
out[0] = '0' + b;
}
else
{
out[0] = 'A' + (b - 10);
}
b = value & 0x0F;
if (b < 10)
{
out[1] = '0' + b;
}
else
{
out[1] = 'A' + (b - 10);
}
out[2] = '\0';
return out;
}
bool MqttManager::allocStr(char **str, const char *src)
{
if (src && *src)
{
if (*str)
{
void *ptr = realloc(*str, strlen(src) + 1);
if (!ptr)
{
return false;
}
*str = (char*)ptr;
}
else
{
*str = (char*)malloc(strlen(src) + 1);
if (!*str)
{
return false;
}
}
strcpy(*str, src);
}
else
{
if (*str)
{
free(*str);
*str = NULL;
}
}
return true;
}
bool MqttManager::allocStr_P(char **str, PGM_P src)
{
if (src && pgm_read_byte(src))
{
if (*str)
{
void *ptr = realloc(*str, strlen_P(src) + 1);
if (!ptr)
{
return false;
}
*str = (char*)ptr;
}
else
{
*str = (char*)malloc(strlen_P(src) + 1);
if (!*str)
{
return false;
}
}
strcpy_P(*str, src);
}
else
{
if (*str)
{
free(*str);
*str = NULL;
}
}
return true;
}
#endif

View File

@@ -38,7 +38,7 @@ class OtaManager
if (ESP_MODE != 1)
{
#ifdef GENERAL_DEBUG
Serial.print(F("Запрос обновления по воздуху поддерживается только в режиме ESP_MODE = 1\n"));
LOG.print(F("Запрос обновления по воздуху поддерживается только в режиме ESP_MODE = 1\n"));
#endif
return false;
@@ -50,7 +50,7 @@ class OtaManager
momentOfFirstConfirmation = millis();
#ifdef GENERAL_DEBUG
Serial.print(F("Получено первое подтверждение обновления по воздуху\nОжидание второго подтверждения\n"));
LOG.print(F("Получено первое подтверждение обновления по воздуху\nОжидание второго подтверждения\n"));
#endif
return false;
@@ -61,7 +61,7 @@ class OtaManager
OtaFlag = OtaPhase::GotSecondConfirm;
#ifdef GENERAL_DEBUG
Serial.print(F("Получено второе подтверждение обновления по воздуху\nСтарт режима обновления\n"));
LOG.print(F("Получено второе подтверждение обновления по воздуху\nСтарт режима обновления\n"));
#endif
startOtaUpdate();
@@ -80,7 +80,7 @@ class OtaManager
momentOfFirstConfirmation = 0;
#ifdef GENERAL_DEBUG
Serial.print(F("Таймаут ожидания второго подтверждения превышен\nСброс флага в исходное состояние\n"));
LOG.print(F("Таймаут ожидания второго подтверждения превышен\nСброс флага в исходное состояние\n"));
#endif
return;
@@ -93,7 +93,7 @@ class OtaManager
momentOfOtaStart = 0;
#ifdef GENERAL_DEBUG
Serial.print(F("Таймаут ожидания прошивки по воздуху превышен\nСброс флага в исходное состояние\nПерезагрузка\n"));
LOG.print(F("Таймаут ожидания прошивки по воздуху превышен\nСброс флага в исходное состояние\nПерезагрузка\n"));
delay(500);
#endif
@@ -139,7 +139,7 @@ class OtaManager
// NOTE: if updating SPIFFS this would be the place to unmount SPIFFS using SPIFFS.end()
#ifdef GENERAL_DEBUG
Serial.printf_P(PSTR("Start updating %s\n"), type.c_str());
LOG.printf_P(PSTR("Start updating %s\n"), type.c_str());
#endif
});
@@ -148,7 +148,7 @@ class OtaManager
OtaFlag = OtaPhase::Done;
#ifdef GENERAL_DEBUG
Serial.print(F("Обновление по воздуху выполнено\nПерезапуск"));
LOG.print(F("Обновление по воздуху выполнено\nПерезапуск"));
delay(500);
#endif
});
@@ -156,7 +156,7 @@ class OtaManager
ArduinoOTA.onProgress([](unsigned int progress, unsigned int total)
{
#ifdef GENERAL_DEBUG
Serial.printf_P(PSTR("Ход выполнения: %u%%\r"), (progress / (total / 100)));
LOG.printf_P(PSTR("Ход выполнения: %u%%\r"), (progress / (total / 100)));
#endif
});
@@ -165,42 +165,42 @@ class OtaManager
OtaFlag = OtaPhase::None;
#ifdef GENERAL_DEBUG
Serial.printf_P(PSTR("Обновление по воздуху завершилось ошибкой [%u]: "), error);
LOG.printf_P(PSTR("Обновление по воздуху завершилось ошибкой [%u]: "), error);
#endif
if (error == OTA_AUTH_ERROR)
{
#ifdef GENERAL_DEBUG
Serial.println(F("Auth Failed"));
LOG.println(F("Auth Failed"));
#endif
}
else if (error == OTA_BEGIN_ERROR)
{
#ifdef GENERAL_DEBUG
Serial.println(F("Begin Failed"));
LOG.println(F("Begin Failed"));
#endif
}
else if (error == OTA_CONNECT_ERROR)
{
#ifdef GENERAL_DEBUG
Serial.println(F("Connect Failed"));
LOG.println(F("Connect Failed"));
#endif
}
else if (error == OTA_RECEIVE_ERROR)
{
#ifdef GENERAL_DEBUG
Serial.println(F("Receive Failed"));
LOG.println(F("Receive Failed"));
#endif
}
else if (error == OTA_END_ERROR)
{
#ifdef GENERAL_DEBUG
Serial.println(F("End Failed"));
LOG.println(F("End Failed"));
#endif
}
#ifdef GENERAL_DEBUG
Serial.print(F("Сброс флага в исходное состояние\nПереход в режим ожидания запроса прошивки по воздуху\n"));
LOG.print(F("Сброс флага в исходное состояние\nПереход в режим ожидания запроса прошивки по воздуху\n"));
#endif
});
@@ -211,11 +211,11 @@ class OtaManager
momentOfOtaStart = 0;
#ifdef GENERAL_DEBUG
Serial.printf_P(PSTR("Для обновления в Arduino IDE выберите пункт меню Инструменты - Порт - '%s at "), espHostName);
Serial.print(WiFi.localIP());
Serial.println(F("'"));
Serial.printf_P(PSTR("Затем нажмите кнопку 'Загрузка' в течение %u секунд и по запросу введите пароль '%s'\n"), ESP_CONF_TIMEOUT, AP_PASS);
Serial.println(F("Устройство с Arduino IDE должно быть в одной локальной сети с модулем ESP!"));
LOG.printf_P(PSTR("Для обновления в Arduino IDE выберите пункт меню Инструменты - Порт - '%s at "), espHostName);
LOG.print(WiFi.localIP());
LOG.println(F("'"));
LOG.printf_P(PSTR("Затем нажмите кнопку 'Загрузка' в течение %u секунд и по запросу введите пароль '%s'\n"), ESP_CONF_TIMEOUT, AP_PASS);
LOG.println(F("Устройство с Arduino IDE должно быть в одной локальной сети с модулем ESP!"));
#endif
}
};

View File

@@ -0,0 +1,33 @@
#if defined(GENERAL_DEBUG) && GENERAL_DEBUG_TELNET
void handleTelnetClient()
{
if (telnetServer.hasClient())
{
if (!telnet || !telnet.connected())
{
if (telnet)
{
telnet.stop(); // клиент отключился
telnetGreetingShown = false;
}
telnet = telnetServer.available(); // готов к подключению нового клиента
}
else
{
telnetServer.available().stop(); // один клиент уже подключен, блокируем подключение нового
telnetGreetingShown = false;
}
}
if (telnet && telnet.connected() && telnet.available())
{
if (!telnetGreetingShown)
{
telnet.println("Подключение к устройтву по протоколу telnet установлено\n-------");
telnetGreetingShown = true;
}
}
}
#endif

View File

@@ -20,7 +20,7 @@ class TimerManager
millis() >= TimerManager::TimeToFire)
{
#ifdef GENERAL_DEBUG
Serial.print(F("Выключение по таймеру\n\n"));
LOG.print(F("Выключение по таймеру\n\n"));
#endif
TimerManager::TimerRunning = false;

View File

@@ -13,3 +13,5 @@ struct ModeType
uint8_t Speed = 30;
uint8_t Scale = 40;
};
typedef void (*SendCurrentDelegate)(char *outputBuffer);

View File

@@ -1,6 +1,8 @@
#ifdef ESP_USE_BUTTON
bool brightDirection;
static bool startButtonHolding = false; // флаг: кнопка удерживается для изменения яркости лампы
void buttonTick()
{
touch.tick();
@@ -23,6 +25,10 @@ void buttonTick()
settChanged = true;
eepromTimeout = millis();
loadingFlag = true;
#if (USE_MQTT && ESP_MODE == 1)
MqttManager::needToPublish = true;
#endif
}
if (ONflag && clickCount == 2)
@@ -34,6 +40,10 @@ void buttonTick()
eepromTimeout = millis();
FastLED.clear();
delay(1);
#if (USE_MQTT && ESP_MODE == 1)
MqttManager::needToPublish = true;
#endif
}
if (ONflag && clickCount == 3)
@@ -45,9 +55,13 @@ void buttonTick()
eepromTimeout = millis();
FastLED.clear();
delay(1);
#if (USE_MQTT && ESP_MODE == 1)
MqttManager::needToPublish = true;
#endif
}
if (ONflag && clickCount == 4)
if (clickCount == 4)
{
#ifdef OTA
if (otaManager.RequestOtaUpdate())
@@ -67,34 +81,39 @@ void buttonTick()
while(!fillString(WiFi.localIP().toString().c_str())) delay(1);
loadingFlag = true;
}
}
}
if (ONflag && touch.isHolded())
{
brightDirection = !brightDirection;
startButtonHolding = true;
}
if (ONflag && touch.isStep())
{
if (brightDirection)
{
if (modes[currentMode].Brightness < 10) modes[currentMode].Brightness += 1;
else if (modes[currentMode].Brightness < 250) modes[currentMode].Brightness += 5;
else modes[currentMode].Brightness = 255;
}
else
{
if (modes[currentMode].Brightness > 15) modes[currentMode].Brightness -= 5;
else if (modes[currentMode].Brightness > 1) modes[currentMode].Brightness -= 1;
else modes[currentMode].Brightness = 0;
}
uint8_t delta = modes[currentMode].Brightness < 10 // определение шага изменения яркости: при яркости [1..10] шаг = 1, при [11..16] шаг = 3, при [17..255] шаг = 15
? 1
: 5;
modes[currentMode].Brightness =
constrain(brightDirection
? modes[currentMode].Brightness + delta
: modes[currentMode].Brightness - delta,
1, 255);
FastLED.setBrightness(modes[currentMode].Brightness);
settChanged = true;
eepromTimeout = millis();
#ifdef GENERAL_DEBUG
Serial.printf_P(PSTR("New brightness value: %d\n"), modes[currentMode].Brightness);
LOG.printf_P(PSTR("New brightness value: %d\n"), modes[currentMode].Brightness);
#endif
}
#if (USE_MQTT && ESP_MODE == 1)
if (ONflag && !touch.isHold() && startButtonHolding) // кнопка отпущена после удерживания, нужно отправить MQTT сообщение об изменении яркости лампы
{
MqttManager::needToPublish = true;
startButtonHolding = false;
}
#endif
}
#endif

View File

@@ -78,4 +78,8 @@ void changePower()
{
FavoritesManager::TurnFavoritesOff();
}
#if (USE_MQTT && ESP_MODE == 1)
MqttManager::needToPublish = true;
#endif
}

View File

@@ -1,7 +1,6 @@
void parseUDP()
{
int32_t packetSize = Udp.parsePacket();
char buff[MAX_UDP_BUFFER_SIZE], *endToken = NULL;
if (packetSize)
{
@@ -10,10 +9,39 @@ void parseUDP()
strcpy(inputBuffer, packetBuffer);
#ifdef GENERAL_DEBUG
Serial.print(F("Inbound UDP packet: "));
Serial.println(inputBuffer);
LOG.print(F("Inbound UDP packet: "));
LOG.println(inputBuffer);
#endif
if (Udp.remoteIP() == WiFi.localIP()) // не реагировать на свои же пакеты
{
return;
}
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 секунды от приложения)
#endif
Udp.beginPacket(Udp.remoteIP(), Udp.remotePort());
Udp.write(reply);
Udp.endPacket();
#ifdef GENERAL_DEBUG
LOG.print(F("Outbound UDP packet: "));
LOG.println(reply);
LOG.println();
#endif
}
}
void processInputBuffer(char *inputBuffer, char *outputBuffer, bool generateOutput)
{
char buff[MAX_UDP_BUFFER_SIZE], *endToken = NULL;
if (!strncmp_P(inputBuffer, PSTR("DEB"), 3))
{
#ifdef USE_NTP
@@ -25,7 +53,7 @@ void parseUDP()
else if (!strncmp_P(inputBuffer, PSTR("GET"), 3))
{
sendCurrent();
sendCurrent(inputBuffer);
}
else if (!strncmp_P(inputBuffer, PSTR("EFF"), 3))
@@ -36,8 +64,12 @@ void parseUDP()
loadingFlag = true;
FastLED.clear();
delay(1);
sendCurrent();
sendCurrent(inputBuffer);
FastLED.setBrightness(modes[currentMode].Brightness);
#if (USE_MQTT && ESP_MODE == 1)
MqttManager::needToPublish = true;
#endif
}
else if (!strncmp_P(inputBuffer, PSTR("BRI"), 3))
@@ -48,7 +80,11 @@ void parseUDP()
loadingFlag = true;
settChanged = true;
eepromTimeout = millis();
sendCurrent();
sendCurrent(inputBuffer);
#if (USE_MQTT && ESP_MODE == 1)
MqttManager::needToPublish = true;
#endif
}
else if (!strncmp_P(inputBuffer, PSTR("SPD"), 3))
@@ -58,7 +94,11 @@ void parseUDP()
loadingFlag = true;
settChanged = true;
eepromTimeout = millis();
sendCurrent();
sendCurrent(inputBuffer);
#if (USE_MQTT && ESP_MODE == 1)
MqttManager::needToPublish = true;
#endif
}
else if (!strncmp_P(inputBuffer, PSTR("SCA"), 3))
@@ -68,7 +108,11 @@ void parseUDP()
loadingFlag = true;
settChanged = true;
eepromTimeout = millis();
sendCurrent();
sendCurrent(inputBuffer);
#if (USE_MQTT && ESP_MODE == 1)
MqttManager::needToPublish = true;
#endif
}
else if (!strncmp_P(inputBuffer, PSTR("P_ON"), 4))
@@ -78,7 +122,11 @@ void parseUDP()
settChanged = true;
eepromTimeout = millis();
changePower();
sendCurrent();
sendCurrent(inputBuffer);
#if (USE_MQTT && ESP_MODE == 1)
MqttManager::needToPublish = true;
#endif
}
else if (!strncmp_P(inputBuffer, PSTR("P_OFF"), 5))
@@ -87,7 +135,11 @@ void parseUDP()
settChanged = true;
eepromTimeout = millis();
changePower();
sendCurrent();
sendCurrent(inputBuffer);
#if (USE_MQTT && ESP_MODE == 1)
MqttManager::needToPublish = true;
#endif
}
else if (!strncmp_P(inputBuffer, PSTR("ALM_SET"), 7))
@@ -97,12 +149,12 @@ void parseUDP()
if (strstr_P(inputBuffer, PSTR("ON")) - inputBuffer == 9)
{
alarms[alarmNum].State = true;
sendAlarms();
sendAlarms(inputBuffer);
}
else if (strstr_P(inputBuffer, PSTR("OFF")) - inputBuffer == 9)
{
alarms[alarmNum].State = false;
sendAlarms();
sendAlarms(inputBuffer);
}
else
{
@@ -110,14 +162,19 @@ void parseUDP()
alarms[alarmNum].Time = atoi(buff);
uint8_t hour = floor(alarms[alarmNum].Time / 60);
uint8_t minute = alarms[alarmNum].Time - hour * 60;
sendAlarms();
sendAlarms(inputBuffer);
}
EepromManager::SaveAlarmsSettings(&alarmNum, alarms);
#if (USE_MQTT && ESP_MODE == 1)
strcpy(MqttManager::mqttBuffer, inputBuffer);
MqttManager::needToPublish = true;
#endif
}
else if (!strncmp_P(inputBuffer, PSTR("ALM_GET"), 7))
{
sendAlarms();
sendAlarms(inputBuffer);
}
else if (!strncmp_P(inputBuffer, PSTR("DAWN"), 4))
@@ -125,7 +182,11 @@ void parseUDP()
memcpy(buff, &inputBuffer[4], strlen(inputBuffer)); // взять подстроку, состоящую последних символов строки inputBuffer, начиная с символа 5
dawnMode = atoi(buff) - 1;
EepromManager::SaveDawnMode(&dawnMode);
sendAlarms();
sendAlarms(inputBuffer);
#if (USE_MQTT && ESP_MODE == 1)
MqttManager::needToPublish = true;
#endif
}
else if (!strncmp_P(inputBuffer, PSTR("DISCOVER"), 8)) // обнаружение приложением модуля esp в локальной сети
@@ -143,7 +204,7 @@ void parseUDP()
else if (!strncmp_P(inputBuffer, PSTR("TMR_GET"), 7))
{
sendTimer();
sendTimer(inputBuffer);
}
else if (!strncmp_P(inputBuffer, PSTR("TMR_SET"), 7))
@@ -158,12 +219,16 @@ void parseUDP()
TimerManager::TimeToFire = millis() + strtoull(buff, &endToken, 10) * 1000;
TimerManager::TimerHasFired = false;
sendTimer();
sendTimer(inputBuffer);
#if (USE_MQTT && ESP_MODE == 1)
MqttManager::needToPublish = true;
#endif
}
else if (!strncmp_P(inputBuffer, PSTR("FAV_GET"), 7))
{
FavoritesManager::SetStatus(inputBuffer);
FavoritesManager::SetStatus(inputBuffer);
}
else if (!strncmp_P(inputBuffer, PSTR("FAV_SET"), 7))
@@ -172,6 +237,10 @@ void parseUDP()
FavoritesManager::SetStatus(inputBuffer);
settChanged = true;
eepromTimeout = millis();
#if (USE_MQTT && ESP_MODE == 1)
MqttManager::needToPublish = true;
#endif
}
else if (!strncmp_P(inputBuffer, PSTR("OTA"), 3))
@@ -198,29 +267,16 @@ void parseUDP()
return;
}
if (Udp.remoteIP() == WiFi.localIP()) // не реагировать на свои же пакеты
if (generateOutput) // если запрошен вывод ответа выполнения команд, копируем его в исходящий буфер
{
return;
strcpy(outputBuffer, inputBuffer);
}
char reply[strlen(inputBuffer) + 1];
strcpy(reply, inputBuffer);
inputBuffer[0] = '\0'; // очистка буфера, читобы не он не интерпретировался, как следующий udp пакет
Udp.beginPacket(Udp.remoteIP(), Udp.remotePort());
Udp.write(reply);
Udp.endPacket();
#ifdef GENERAL_DEBUG
Serial.print(F("Outbound UDP packet: "));
Serial.println(reply);
Serial.println();
#endif
}
inputBuffer[0] = '\0'; // очистка буфера, читобы не он не интерпретировался, как следующий входной пакет
}
void sendCurrent()
void sendCurrent(char *outputBuffer)
{
sprintf_P(inputBuffer, PSTR("CURR %u %u %u %u %u %u"),
sprintf_P(outputBuffer, PSTR("CURR %u %u %u %u %u %u"),
currentMode,
modes[currentMode].Brightness,
modes[currentMode].Speed,
@@ -229,40 +285,40 @@ void sendCurrent()
ESP_MODE);
#ifdef USE_NTP
strcat_P(inputBuffer, PSTR(" 1"));
strcat_P(outputBuffer, PSTR(" 1"));
#else
strcat_P(inputBuffer, PSTR(" 0"));
strcat_P(outputBuffer, PSTR(" 0"));
#endif
sprintf_P(inputBuffer, PSTR("%s %u"), inputBuffer, (uint8_t)TimerManager::TimerRunning);
sprintf_P(outputBuffer, PSTR("%s %u"), outputBuffer, (uint8_t)TimerManager::TimerRunning);
#ifdef USE_NTP
sprintf_P(inputBuffer, PSTR("%s %s"), inputBuffer, timeClient.getFormattedTime().c_str());
sprintf_P(outputBuffer, PSTR("%s %s"), outputBuffer, timeClient.getFormattedTime().c_str());
#else
sprintf_P(inputBuffer, PSTR("%s %ull"), inputBuffer, millis());
sprintf_P(outputBuffer, PSTR("%s %ull"), outputBuffer, millis());
#endif
}
void sendAlarms()
void sendAlarms(char *outputBuffer)
{
strcpy_P(inputBuffer, PSTR("ALMS"));
strcpy_P(outputBuffer, PSTR("ALMS"));
for (byte i = 0; i < 7; i++)
{
sprintf_P(inputBuffer, PSTR("%s %u"), inputBuffer, (uint8_t)alarms[i].State);
sprintf_P(outputBuffer, PSTR("%s %u"), outputBuffer, (uint8_t)alarms[i].State);
}
for (byte i = 0; i < 7; i++)
{
sprintf_P(inputBuffer, PSTR("%s %u"), inputBuffer, alarms[i].Time);
sprintf_P(outputBuffer, PSTR("%s %u"), outputBuffer, alarms[i].Time);
}
sprintf_P(inputBuffer, PSTR("%s %u"), inputBuffer, dawnMode + 1);
sprintf_P(outputBuffer, PSTR("%s %u"), outputBuffer, dawnMode + 1);
}
void sendTimer()
void sendTimer(char *outputBuffer)
{
sprintf_P(inputBuffer, PSTR("TMR %u %u %u"),
sprintf_P(outputBuffer, PSTR("TMR %u %u %u"),
TimerManager::TimerRunning,
TimerManager::TimerOption,
(TimerManager::TimerRunning ? (uint16_t)floor((TimerManager::TimeToFire - millis()) / 1000) : 0));

View File

@@ -32,7 +32,7 @@ void timeTick()
if (!ntpServerAddressResolved)
{
#ifdef GENERAL_DEBUG
Serial.println(F("Функции будильника отключены до восстановления подключения к интернету"));
LOG.println(F("Функции будильника отключены до восстановления подключения к интернету"));
#endif
}
}
@@ -60,6 +60,7 @@ void timeTick()
{
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]));
dawnPosition = constrain(dawnPosition, 0, 255);
@@ -75,15 +76,16 @@ void timeTick()
}
else
{
// не время будильника (ещё не начался или закончился по времени)
if (dawnFlag)
{
dawnFlag = false;
manualOff = false;
FastLED.clear();
delay(2);
FastLED.show();
changePower(); // выключение матрицы или установка яркости текущего эффекта в засисимости от того, была ли включена лампа до срабатывания будильника
}
manualOff = false;
}
}
}
@@ -102,7 +104,7 @@ void resolveNtpServerAddress(bool &ntpServerAddressResolved) // ф
#ifdef GENERAL_DEBUG
if (ntpServerAddressResolved)
{
Serial.println(F("Подключение к интернету отсутствует"));
LOG.println(F("Подключение к интернету отсутствует"));
}
#endif
@@ -113,7 +115,7 @@ void resolveNtpServerAddress(bool &ntpServerAddressResolved) // ф
#ifdef GENERAL_DEBUG
if (!ntpServerAddressResolved)
{
Serial.println(F("Подключение к интернету установлено"));
LOG.println(F("Подключение к интернету установлено"));
}
#endif