From 18012bd57a048e0930059f5121985ca8e3851844 Mon Sep 17 00:00:00 2001 From: Mit4el Date: Fri, 25 Jul 2025 00:23:37 +0300 Subject: [PATCH] new Modules from Chat --- myProfile.json | 28 + .../exec/EctoControlAdapter/AdapterCommon.h | 182 -- .../EctoControlAdapter/EctoControlAdapter.cpp | 1553 +++++++++-------- .../exec/EctoControlAdapter/ModbusEC.cpp | 301 +--- .../EctoControlAdapter/export - Копия.json | 459 +++++ .../exec/EctoControlAdapter/modinfo.json | 230 +-- .../exec/WakeOnLanModule/WakeOnLanModule.cpp | 162 ++ src/modules/exec/WakeOnLanModule/export.json | 295 ++++ src/modules/exec/WakeOnLanModule/modinfo.json | 48 + src/modules/sensors/NoiseAdc/NoiseAdc.cpp | 359 ++++ src/modules/sensors/NoiseAdc/export.json | 89 + src/modules/sensors/NoiseAdc/modinfo.json | 70 + src/modules/sensors/Presence/Presence.cpp | 579 ++++++ src/modules/sensors/Presence/export.json | 372 ++++ src/modules/sensors/Presence/modinfo.json | 199 +++ src/modules/sensors/Presence/preview.png | Bin 0 -> 70938 bytes src/modules/sensors/Presence/widgets.json | 383 ++++ src/modules/sensors/SoftRTC/SoftRTC.cpp | 718 ++++++++ src/modules/sensors/SoftRTC/export .json | 264 +++ src/modules/sensors/SoftRTC/modinfo.json | 161 ++ src/modules/virtual/SolarCalc/SolarCalc.cpp | 488 ++++++ src/modules/virtual/SolarCalc/coordinates.jpg | Bin 0 -> 225470 bytes src/modules/virtual/SolarCalc/export.json | 455 +++++ src/modules/virtual/SolarCalc/modinfo.json | 167 ++ 24 files changed, 6283 insertions(+), 1279 deletions(-) delete mode 100644 src/modules/exec/EctoControlAdapter/AdapterCommon.h create mode 100644 src/modules/exec/EctoControlAdapter/export - Копия.json create mode 100644 src/modules/exec/WakeOnLanModule/WakeOnLanModule.cpp create mode 100644 src/modules/exec/WakeOnLanModule/export.json create mode 100644 src/modules/exec/WakeOnLanModule/modinfo.json create mode 100644 src/modules/sensors/NoiseAdc/NoiseAdc.cpp create mode 100644 src/modules/sensors/NoiseAdc/export.json create mode 100644 src/modules/sensors/NoiseAdc/modinfo.json create mode 100644 src/modules/sensors/Presence/Presence.cpp create mode 100644 src/modules/sensors/Presence/export.json create mode 100644 src/modules/sensors/Presence/modinfo.json create mode 100644 src/modules/sensors/Presence/preview.png create mode 100644 src/modules/sensors/Presence/widgets.json create mode 100644 src/modules/sensors/SoftRTC/SoftRTC.cpp create mode 100644 src/modules/sensors/SoftRTC/export .json create mode 100644 src/modules/sensors/SoftRTC/modinfo.json create mode 100644 src/modules/virtual/SolarCalc/SolarCalc.cpp create mode 100644 src/modules/virtual/SolarCalc/coordinates.jpg create mode 100644 src/modules/virtual/SolarCalc/export.json create mode 100644 src/modules/virtual/SolarCalc/modinfo.json diff --git a/myProfile.json b/myProfile.json index fd7485a8..c96b8761 100644 --- a/myProfile.json +++ b/myProfile.json @@ -212,6 +212,10 @@ "path": "src/modules/virtual/Ping", "active": true }, + { + "path": "src/modules/virtual/SolarCalc", + "active": false + }, { "path": "src/modules/virtual/Timer", "active": true @@ -374,6 +378,10 @@ "path": "src/modules/sensors/MQgas", "active": false }, + { + "path": "src/modules/sensors/NoiseAdc", + "active": false + }, { "path": "src/modules/sensors/Ntc", "active": true @@ -382,6 +390,10 @@ "path": "src/modules/sensors/Pcf8591", "active": false }, + { + "path": "src/modules/sensors/Presence", + "active": false + }, { "path": "src/modules/sensors/Pzem004t", "active": false @@ -422,6 +434,10 @@ "path": "src/modules/sensors/Sht30", "active": false }, + { + "path": "src/modules/sensors/SoftRTC", + "active": false + }, { "path": "src/modules/sensors/Sonar", "active": false @@ -548,6 +564,10 @@ "path": "src/modules/exec/Thermostat", "active": true }, + { + "path": "src/modules/exec/WakeOnLanModule", + "active": false + }, { "path": "src/modules/sensors/Ds2423", "active": false @@ -566,6 +586,10 @@ "path": "src/modules/display/Lcd2004", "active": false }, + { + "path": "src/modules/display/LedFX", + "active": false + }, { "path": "src/modules/display/Nextion", "active": false @@ -590,6 +614,10 @@ "path": "src/modules/display/TM16XX", "active": false }, + { + "path": "src/modules/display/U8g2lib", + "active": false + }, { "path": "src/modules/display/Ws2812b", "active": false diff --git a/src/modules/exec/EctoControlAdapter/AdapterCommon.h b/src/modules/exec/EctoControlAdapter/AdapterCommon.h deleted file mode 100644 index 4b6b7bca..00000000 --- a/src/modules/exec/EctoControlAdapter/AdapterCommon.h +++ /dev/null @@ -1,182 +0,0 @@ -#pragma once -#include - -#define comm_RebootAdapter 2u -#define comm_LockOutReset 3u - -struct BoilerInfo -{ - uint8_t adapterType; // тип адаптера. 000 - Opentherm 001 - eBus 010 - Navien - uint8_t boilerStatus; // состояние связи с котлом 0 - нет ответа от котла на последнюю команду 1 - есть ответ от котла на последнюю команду - uint8_t rebootStatus; // Код последней перезагрузки адаптера. 0...255 - код - uint8_t adapterHardVer; // Аппаратная версия адаптера. 0...255 - номер версии - uint8_t adapterSoftVer; // u8 Программная версия адаптера. 0...255 - номер версии - uint16_t boilerMemberCode; // 0x0021 R u16 Код производителя котла. Зависит от марки и модели котла. 0...65535 - код производителя - uint16_t boilerModelCode; // 0x0022 R u16 Код модели котла. Зависит от марки и модели котла. 0...65535 - код модели -}; - -struct BoilerStatus -{ - uint8_t burnStatus; // бит 0 - текущее состояние горелки 0 - отключена 1 - включена - uint8_t CHStatus; // бит 1 - текущее состояние отопления 0 - отключено 1 - включено - uint8_t DHWStatus; // бит 2 - текущее состояние ГВС 0 - отключено 1 - включено -}; - -// Флаги ошибок (только для котлов с интерфейсом OpenTherm) -enum FlagErrorOT //: uitn8_t -{ - er_SecviceReq, // 0: Необходимо обслуживание - er_LockOut, // 1: Котел заблокирован - er_LowWater, // 2: Низкое давление в отопительном контуре - er_FlameFault, // 3: Ошибка розжига - er_AirPresFault, // 4: Низкое давление воздуха - er_OverTem // 5: Перегрев теплоносителя в контуре -}; - -////////////////// Данные регистров второй версии адаптера котла -//////////////////// Регистры для чтения - -enum ReadDataEctoControl //: uitn16_t -{ - ecR_AdapterInfo = 0x0010, // 0x0010 R bitfields бит 2...0 - тип адаптера. 000 - Opentherm 001 - eBus 010 - Navien - // бит 3 - состояние связи с котлом 0 - нет ответа от котла на последнюю команду 1 - есть ответ от котла на последнюю команду - // u8 Код последней перезагрузки адаптера. 0...255 - код - - ecR_AdaperVersion = 0x0011, // 0x0011 R u8 Аппаратная версия адаптера. 0...255 - номер версии - // u8 Программная версия адаптера. 0...255 - номер версии - - ecR_Time = 0x0012, // 0x0012 - 0x0013 R u32 Время работы адаптера после перезагрузки 0...4294967295 - время в секундах - ecR_MinSetCH = 0x0014, // 0x0014 R u8 Нижний предел уставки теплоносителя. 0...100 - температура уставки в град. С - ecR_MaxSetCH = 0x0015, // 0x0015 R u8 Верхний предел уставки теплоносителя 0...100 - температура уставки в град. С - ecR_MinSetDHW = 0x0016, // 0x0016 R u8 Нижний предел уставки ГВС 0...100 - температура уставки в град. С - ecR_MaxSetDHW = 0x0017, // 0x0017 R u8 Верхний предел уставки ГВС 0...100 - температура уставки в град. С - ecR_TempCH = 0x0018, // 0x0018 R i16 Текущая температура теплоносителя -100...100 - температура в 0.1 гр. С - ecR_TempDHW = 0x0019, // 0x0019 R u16 Текущая температура ГВС 0...100 - температура в 0.1 гр. С - ecR_Pressure = 0x001A, // 0x001A R u8 Текущее Давление в контуре 0...50 - давление в (0,1бар) - ecR_FlowRate = 0x001B, // 0x001B R u8? Текущий расход ГВС 1...255 - расход в (0,1 л/мин) - ecR_ModLevel = 0x001C, // 0x001C R u8? Текущая модуляция горелки 0xFF - не определено 0...100 - модуляция в (%) - ecR_BoilerStatus = 0x001D, // 0x001D R bitfields бит 0 - текущее состояние горелки 0 - отключена 1 - включена - // бит 1 - текущее состояние отопления 0 - отключено 1 - включено - // бит 2 - текущее состояние ГВС 0 - отключено 1 - включено - - ecR_CodeError = 0x001E, // 0x001E R u16 Код ошибки котла (основной). Зависит от марки и модели котла. 0...65535 - код ошибки - ecR_CodeErrorExt = 0x001F, // 0x001F R u16 Код ошибки котла (дополнительный). Зависит от марки и модели котла. 0...65535 - код ошибки - ecR_TempOutside = 0x0020, // 0x0020 R s8 Температура уличного датчика котла (при егоналичии). -65…+100 температура в градусах С - ecR_MemberCode = 0x0021, // 0x0021 R u16 Код производителя котла. Зависит от марки и модели котла. 0...65535 - код производителя - ecR_ModelCode = 0x0022, // 0x0022 R u16 Код модели котла. Зависит от марки и модели котла. 0...65535 - код модели - ecR_FlagErrorOT = 0x0023 // 0x0023 R s8 Флаги ошибок (только для котлов с интерфейсом OpenTherm) - // 0: Необходимо обслуживание - // 1: Котел заблокирован - // 2: Низкое давление в отопительном контуре - // 3: Ошибка розжига - // 4: Низкое давление воздуха - // 5: Перегрев теплоносителя в контуре -}; - -////////////////////////////// WRITE ///////////// - -enum WriteDataEctoControl //: uitn16_t -{ - ecW_SetTypeConnect = 0x0030, // 0x0030 W u8 Тип внешних подключений (будет сохранено в постоянной памяти адаптера) - // 0 - адаптер подключен к котлу - // 1 - котел подключен к внешнему устройству (панель или перемычка) - - ecW_TSetCH = 0x0031, // 0x0031 W int16 Уставка теплоносителя (будет сохранено в постоянной памяти адаптера). - // Будет передана котлу при старте адаптера, пока главным устройством не - // были записаны регистры уставки температуры. - // 0...1000 - температура уставки в десятых долях градуса С (например, для - // установки 45С нужно записать число 450). Во многих котлах необходимо до - // подключения адаптера необходимо поднять температуру теплоносителя - // отопления (нажимая “+” на панели котла) для согласования диапазона - // температуры теплоносителя, в противном случае температура теплоносителя - // может не достигнуть требуемого значения. - - ecW_TSetCHFaultConn = 0x0032, // 0x0032 W int16 Уставка теплоносителя в аварийном режиме(будет сохранено в постоянной памяти адаптера). - // Будет передана котлу в случае - // отсутствия связи с главным управляющим устройством. - // 0...1000 - температура уставки в десятых долях град. С (например, - // для установки 45С нужно записать число 450) - - ecW_TSetMinCH = 0x0033, // 0x0033 W u8 Нижний предел уставки теплоносителя 0...100 - температура уставки в град. С - // Не все котлы поддерживают этот параметр. Как правило, этот - // предел не должен быть ниже аналогичного предела, - // установленного в настройках котла. - - ecW_TSetMaxCH = 0x0034, // 0x0034 W u8 Верхний предел уставки теплоносителя 0...100 - температура уставки в град. С - // Не все котлы поддерживают этот параметр. Как правило, этот - // предел не должен быть выше аналогичного предела, - // установленного в настройках котла. - - ecW_TSetMinDHW = 0x0035, // 0x0035 W u8 Нижний предел уставки ГВС 0...100 - температура уставки в град. С - // Не все котлы поддерживают этот параметр. Как правило, этот - // предел не должен быть ниже аналогичного предела, - // установленного в настройках котла. - - ecW_TSetMaxDHW = 0x0036, // 0x0036 W u8 Верхний предел уставки ГВС 0...100 - температура уставки в град. С - // Не все котлы поддерживают этот параметр. Как правило, этот - // предел не должен быть выше аналогичного предела, - // установленного в настройках котла. - - ecW_TSetDHW = 0x0037, // 0x0037 W u8 Уставка ГВС (EPROM) 0...100 - температура уставки в град. С - // Для большинства котлов эта уставка должна находиться ниже - // предела, установленного в меню самого котла, иначе она может быть - // проигнорирована котлом. Для некоторых котлов стоит устанавливать - // этот параметр равным верхнему пределу уставки ГВС. - - ecW_SetMaxModLevel = 0x0038, // 0x0038 W u8 Уставка максимальной модуляции горелки (будет сохранено в постоянной памяти адаптера) 0...100 - уровень модуляции в процентах. - // Данный параметр поддерживается не всеми котлами. Для - // электрических трехфазных котлов возможно задать только 3 - // уровня модуляции (в диапазоне 0…100%). - - ecW_SetStatusBoiler = 0x0039, // 0x0039 W bitfields бит 0 - режим контура отопления (будет сохранено в постоянной памяти адаптера) 0 - отключен 1 - включен - // бит 1 - режим контура ГВС 0 - отключен 1 - включен - // бит 2 - “второй контур”, используется только некоторыми котлами с - // интерфейсом OpenTherm и может отвечать за активацию бойлера - // косвенного нагрева или встроенной функции ГВС. 0 - отключен 1 - включен - - - ecW_Command = 0x0080 // 0x0080 W uint16 команда - // 0 - нет команды - // 1 - CH water filling (зарезервировано) - // 2 - перезагрузка адаптера - // 3 - сброс ошибок котла - // 4..65525 - зарезервировано - // При записи любой команды, кроме «нет команды», сразу же меняется - // состояние регистра «Ответ на команду» - становится 2 – идет - // обработка команжы - - -}; - -///////////////////////////////////////// Регистры состояния: - -/* -0x0040…0x006F R i16 состояние данных в регистре ($addr - 0x30) - -2 - ошибка чтения/записи в котел - -1 - регистр не поддерживается - 0- для регистра на чтение означает, что данные из котла прочитаны и - валидны. Для регистра на запись означает, что данные успешно приняты - котлом. - 1- не инициализирован. - если регистр R: чтение соответствующих данные из котла не - приводилось, - если регистр W: адаптеру не было задано значение для записи - соответствующего значения в котле -*/ - - -/* -0x0081 R int16 Ответ на команду - -32768..-6 - зарезервировано - -5 – ошибка выполнения команды - -4 – неподдерживаемая котлом команда - -3 – не поддерживаемый котлом идентификатор устройства - -2 – не поддерживается данный адаптером - -1 – не получен ответ за отведенное врекмя - 0 – команда выполнена успешно - 1 – не было команды (значение по умолчанию) - 2 – идет обработка команды (обмен данными) - 3..32767 - зарезервировано - Если команда после перезагрузки адаптера не давалась, регистр - читается как 1 - не было команды. -*/ diff --git a/src/modules/exec/EctoControlAdapter/EctoControlAdapter.cpp b/src/modules/exec/EctoControlAdapter/EctoControlAdapter.cpp index 18511cc6..bb1bdc47 100644 --- a/src/modules/exec/EctoControlAdapter/EctoControlAdapter.cpp +++ b/src/modules/exec/EctoControlAdapter/EctoControlAdapter.cpp @@ -2,813 +2,856 @@ #include "classes/IoTItem.h" #include #include - #include "ModbusEC.h" -#include "AdapterCommon.h" -// #include "Stream.h" #include +#include -// class ModbusUart; -Stream *_modbusUART = nullptr; +// Константы +constexpr uint8_t DEFAULT_DIR_PIN = 0; +constexpr uint8_t DEFAULT_MODBUS_ADDR = 0xF0; +constexpr uint8_t DEFAULT_DEVICE_TYPE = 0x14; +constexpr uint32_t DEFAULT_SERIAL_TIMEOUT = 500; +constexpr uint16_t INVALID_MODBUS_VALUE = 0x7FFF; +constexpr uint8_t MAX_RETRY_ATTEMPTS = 3; +constexpr uint16_t RETRY_DELAY_MS = 100; +constexpr uint16_t MAX_REGISTERS_PER_READ = 16; -#define UART_LINE 2 -uint8_t _DIR_PIN = 0; -// Modbus stuff -// Данные Modbus по умолчанию +// Команды адаптера +constexpr uint16_t COMMAND_REBOOT = 2; +constexpr uint16_t COMMAND_RESET_ERRORS = 3; -#define MODBUS_RX_PIN 18 // Rx pin -#define MODBUS_TX_PIN 19 // Tx pin -#define MODBUS_SERIAL_BAUD 9600 // Baud rate for esp32 and max485 communication +// Битовая маска для регистра статуса котла +struct BoilerStatusBits { + uint8_t heatingEnabled : 1; + uint8_t dhwEnabled : 1; + uint8_t secondaryCircuit : 1; + uint8_t reserved : 5; + + uint16_t toUint16() const { + return (heatingEnabled ? 0x0001 : 0) | + (dhwEnabled ? 0x0002 : 0) | + (secondaryCircuit ? 0x0004 : 0); + } +}; -void modbusPreTransmission() -{ - // delay(500); - if (_DIR_PIN) - digitalWrite(_DIR_PIN, HIGH); +// Оптимизированные структуры данных с упакованными полями +struct __attribute__((packed)) BoilerInfo { + uint8_t adapterType : 3; + bool boilerConnected : 1; + uint8_t rebootCode; + uint8_t hardwareVersion; + uint8_t softwareVersion; + uint32_t uptime; + uint16_t memberCode; + uint16_t modelCode; +}; + +struct __attribute__((packed)) BoilerStatus { + bool burnerActive : 1; + bool heatingActive : 1; + bool dhwActive : 1; + uint8_t errorFlags; + BoilerStatusBits writtenStatus; +}; + +// Регистры Modbus +enum ReadRegisters { + ecR_AdapterInfo = 0x0010, + ecR_AdapterVersion = 0x0011, + ecR_Uptime = 0x0012, + ecR_MinSetCH = 0x0014, + ecR_MaxSetCH = 0x0015, + ecR_MinSetDHW = 0x0016, + ecR_MaxSetDHW = 0x0017, + ecR_TempCH = 0x0018, + ecR_TempDHW = 0x0019, + ecR_Pressure = 0x001A, + ecR_FlowRate = 0x001B, + ecR_ModLevel = 0x001C, + ecR_BoilerStatus = 0x001D, + ecR_CodeError = 0x001E, + ecR_CodeErrorExt = 0x001F, + ecR_TempOutside = 0x0020, + ecR_MemberCode = 0x0021, + ecR_ModelCode = 0x0022, + ecR_FlagErrorOT = 0x0023, + ecR_ReadSetStatusBoiler = 0x003F, + // Регистры для чтения записанных значений (+6 от регистров записи) + ecR_ReadSetTypeConnect = 0x0036, + ecR_ReadTSetCH = 0x0037, + ecR_ReadTSetCHFaultConn = 0x0038, + ecR_ReadTSetMinCH = 0x0039, + ecR_ReadTSetMaxCH = 0x003A, + ecR_ReadTSetMinDHW = 0x003B, + ecR_ReadTSetMaxDHW = 0x003C, + ecR_ReadTSetDHW = 0x003D, + ecR_ReadSetMaxModLevel = 0x003E +}; + +enum WriteRegisters { + ecW_SetTypeConnect = 0x0030, + ecW_TSetCH = 0x0031, + ecW_TSetCHFaultConn = 0x0032, + ecW_TSetMinCH = 0x0033, + ecW_TSetMaxCH = 0x0034, + ecW_TSetMinDHW = 0x0035, + ecW_TSetMaxDHW = 0x0036, + ecW_TSetDHW = 0x0037, + ecW_SetMaxModLevel = 0x0038, + ecW_SetStatusBoiler = 0x0039, + ecW_Command = 0x0080 +}; + +Stream* _modbusUART = nullptr; +uint8_t _DIR_PIN = DEFAULT_DIR_PIN; + +// Оптимизированные функции управления направлением передачи +inline void modbusPreTransmission() { + if (_DIR_PIN) digitalWrite(_DIR_PIN, HIGH); } -// Pin 4 made low for Modbus receive mode -// Контакт 4 установлен на низком уровне для режима приема Modbus -void modbusPostTransmission() -{ - if (_DIR_PIN) - digitalWrite(_DIR_PIN, LOW); - // delay(500); +inline void modbusPostTransmission() { + if (_DIR_PIN) digitalWrite(_DIR_PIN, LOW); } -// ModbusMaster node; - -// RsEctoControl *rsEC; - -class EctoControlAdapter : public IoTItem -{ +class EctoControlAdapter : public IoTItem { private: - int _rx = MODBUS_RX_PIN; // адреса прочитаем с веба - int _tx = MODBUS_TX_PIN; - int _baud = MODBUS_SERIAL_BAUD; - String _prot = "SERIAL_8N1"; - int protocol = SERIAL_8N1; - uint8_t _addr = 0xF0; // Адрес слейва от 1 до 247 - uint8_t _type = 0x14; // Тип устройства: 0x14 – адаптер OpenTherm (вторая версия); 0x11 – адаптер OpenTherm (первая версия, снята с производства) - uint8_t _debugLevel; // Дебаг - + int _rx, _tx, _baud, _uartLine; + String _prot; + uint8_t _addr; + uint8_t _debugLevel; ModbusMaster node; - uint8_t _debug; - // Stream *_modbusUART; + BoilerInfo info = {}; + BoilerStatus status = {}; + float tCH = 0, tDHW = 0, tOut = 0; + float press = 0, flow = 0; + uint8_t modLevel = 0; + uint16_t codeError = 0, codeErrorExt = 0; + uint8_t errorFlags = 0; - BoilerInfo info; - BoilerStatus status; + // Кэш для часто запрашиваемых значений + uint32_t _lastUpdate = 0; + static constexpr uint32_t UPDATE_INTERVAL = 10000; // 10 секунд - uint16_t code; - uint16_t codeExt; - uint8_t flagErr; - float flow; - float maxSetCH; - float maxSetDHW; - float minSetCH; - float minSetDHW; - float modLevel; - float press; - float tCH; - float tDHW; - float tOut; - bool enableCH; - bool enableDHW; - bool enableCH2; - bool _isNetworkActive; - bool _mqttIsConnect; - -public: - EctoControlAdapter(String parameters) : IoTItem(parameters) - { - _DIR_PIN = 0; - _addr = jsonReadInt(parameters, "addr"); // адреса slave прочитаем с веба - _rx = jsonReadInt(parameters, "RX"); // прочитаем с веба - _tx = jsonReadInt(parameters, "TX"); - _DIR_PIN = jsonReadInt(parameters, "DIR_PIN"); - _baud = jsonReadInt(parameters, "baud"); - _prot = jsonReadStr(parameters, "protocol"); - jsonRead(parameters, "debug", _debugLevel); - - if (_prot == "SERIAL_8N1") - { - protocol = SERIAL_8N1; - } - else if (_prot == "SERIAL_8N2") - { - protocol = SERIAL_8N2; - } - - // Serial2.begin(baud-rate, protocol, RX pin, TX pin); - - _modbusUART = new HardwareSerial(UART_LINE); - - if (_debugLevel > 2) - { - SerialPrint("I", "EctoControlAdapter", "baud: " + String(_baud) + ", protocol: " + String(protocol, HEX) + ", RX: " + String(_rx) + ", TX: " + String(_tx)); - } - ((HardwareSerial *)_modbusUART)->begin(_baud, protocol, _rx, _tx); // выбираем тип протокола, скорость и все пины с веба - ((HardwareSerial *)_modbusUART)->setTimeout(200); - //_modbusUART = &serial; - node.begin(_addr, _modbusUART); - - node.preTransmission(modbusPreTransmission); - node.postTransmission(modbusPostTransmission); - - if (_DIR_PIN) - { - _DIR_PIN = _DIR_PIN; - pinMode(_DIR_PIN, OUTPUT); - digitalWrite(_DIR_PIN, LOW); - } - - // 0x14 – адаптер OpenTherm (вторая версия) - // 0x15 – адаптер eBus - // 0x16 – адаптер Navien - if (_addr > 0) - { - uint16_t type; - readFunctionModBus(0x0003, type); - type = type >> 8; - if (0x14 != type && 0x15 != type && 0x16 != type) - { - SerialPrint("E", "EctoControlAdapter", "Не подходящее устройство, type: " + String(type, HEX)); + // Оптимизированное чтение группы регистров + bool readRegisterBlock(uint16_t startReg, uint16_t* data, uint8_t count) { + if (count == 0 || count > MAX_REGISTERS_PER_READ) return false; + + uint8_t result = node.readHoldingRegisters(startReg, count); + if (result != node.ku8MBSuccess) { + if (_debugLevel > 0) { + SerialPrint("E", "Modbus", "Read block 0x" + String(startReg, HEX) + + "-0x" + String(startReg + count - 1, HEX) + + " failed, code: " + String(result, HEX)); } - getModelVersion(); - getBoilerInfo(); - getBoilerStatus(); - } - else if (_addr == 0) - { // если адреса нет, то шлем широковещательный запрос адреса - uint8_t addr = node.readAddresEctoControl(); - SerialPrint("I", "EctoControlAdapter", "readAddresEctoControl, addr: " + String(addr, HEX) + " - Enter to configuration"); - } - } - - void doByInterval() - { - if (_addr > 0) - { - // readBoilerInfo(); - getBoilerStatus(); - - getCodeError(); - getCodeErrorExt(); - if (info.adapterType == 0) - getFlagErrorOT(); - // getFlowRate(); - // getMaxSetCH(); - // getMaxSetDHW(); - // getMinSetCH(); - // getMinSetDHW(); - getModLevel(); - getPressure(); - getTempCH(); - getTempDHW(); - getTempOutside(); - } - } - - void loop() - { - // для новых версий IoTManager - IoTItem::loop(); - } - - IoTValue execute(String command, std::vector ¶m) - { - if (command == "getModelVersion") - { - getModelVersion(); - } - if (command == "getBoilerInfo") - { - getBoilerInfo(); - } - if (command == "getBoilerStatus") - { - getBoilerStatus(); - } - if (command == "getCodeError") - { - getCodeError(); - } - if (command == "getCodeErrorExt") - { - getCodeErrorExt(); - } - if (command == "getFlagErrorOT") - { - getFlagErrorOT(); - } - if (command == "getFlowRate") - { - getFlowRate(); - } - if (command == "getMaxSetCH") - { - getMaxSetCH(); - } - if (command == "getMaxSetDHW") - { - getMaxSetDHW(); - } - if (command == "getMinSetCH") - { - getMinSetCH(); - } - if (command == "getMinSetDHW") - { - getMinSetDHW(); - } - if (command == "getModLevel") - { - getModLevel(); - } - if (command == "getPressure") - { - getPressure(); - } - if (command == "getTempCH") - { - getTempCH(); - } - if (command == "getTempDHW") - { - getTempDHW(); - } - if (command == "getTempOutside") - { - getTempOutside(); - } - - if (command == "setTypeConnect") - { - setTypeConnect(param[0].valD); - } - if (command == "setTCH") - { - setTCH(param[0].valD); - } - if (command == "setTCHFaultConn") - { - setTCHFaultConn(param[0].valD); - } - if (command == "setMinCH") - { - setMinCH(param[0].valD); - } - if (command == "setMaxCH") - { - setMaxCH(param[0].valD); - } - if (command == "setMinDHW") - { - setMinDHW(param[0].valD); - } - if (command == "setMaxDHW") - { - setMaxDHW(param[0].valD); - } - if (command == "setTDHW") - { - setTDHW(param[0].valD); - } - if (command == "setMaxModLevel") - { - setMaxModLevel(param[0].valD); - } - if (command == "setStatusCH") - { - setStatusCH((bool)param[0].valD); - } - if (command == "setStatusDHW") - { - setStatusDHW((bool)param[0].valD); - } - if (command == "setStatusCH2") - { - setStatusCH2((bool)param[0].valD); - } - - if (command == "lockOutReset") - { - lockOutReset(); - } - if (command == "rebootAdapter") - { - rebootAdapter(); - } - return {}; - } - - void publishData(String widget, String status) - { - - IoTItem *tmp = findIoTItem(widget); - if (tmp) - tmp->setValue(status, true); - else - { - if (_debugLevel > 0) - SerialPrint("i", "NEW", widget + " = " + status); - } - } - - static void sendTelegramm(String msg) - { - if (tlgrmItem) - tlgrmItem->sendTelegramMsg(false, msg); - } - - ~EctoControlAdapter(){}; - - bool writeFunctionModBus(const uint16_t ®, uint16_t &data) - { - // set word 0 of TX buffer to least-significant word of counter (bits 15..0) - // node.setTransmitBuffer(1, lowWord(data)); - // set word 1 of TX buffer to most-significant word of counter (bits 31..16) - node.setTransmitBuffer(0, data); - // slave: write TX buffer to (2) 16-bit registers starting at register 0 - uint8_t result = node.writeMultipleRegisters(reg, 1); - node.clearTransmitBuffer(); - if (_debug > 2) - { - SerialPrint("I", "EctoControlAdapter", "writeSingleRegister, addr: " + String((uint8_t)_addr, HEX) + ", reg: 0x" + String(reg, HEX) + ", state: " + String(data) + " = result: " + String(result, HEX)); - } - if (result == 0) - return true; - else return false; + } + + for (uint8_t i = 0; i < count; i++) { + data[i] = node.getResponseBuffer(i); + if (_debugLevel > 2) { + SerialPrint("I", "Modbus", "Read 0x" + String(startReg + i, HEX) + + " = " + String(data[i])); + } + } + + node.clearResponseBuffer(); + return true; } - bool readFunctionModBus(const uint16_t ®, uint16_t &reading) - { - if (_addr == 0) return false; - // float retValue = 0; - if (_modbusUART) - { - // if (!addr) - // addr = _addr; - node.begin(_addr, _modbusUART); - uint8_t result; - // uint32_t reading; - - if (reg == 0x0000) - result = node.readHoldingRegisters(reg, 4); - else - result = node.readHoldingRegisters(reg, 1); - if (_debug > 2) - SerialPrint("I", "EctoControlAdapter", "readHoldingRegisters, addr: " + String(_addr, HEX) + ", reg: 0x" + String(reg, HEX) + " = result: " + String(result, HEX)); - // break; - if (result == node.ku8MBSuccess) - { - if (reg == 0x0000) - { - reading = node.getResponseBuffer(0x03); - reading = (uint16_t)((uint8_t)(reading) >> 8); - SerialPrint("I", "EctoControlAdapter", "read info, addr: " + String(_addr, HEX) + ", type: " + String(reading, HEX)); + // Оптимизированное чтение с повторными попытками + bool readWithRetry(uint16_t reg, uint16_t &reading) { + uint8_t attempts = 0; + while (attempts < MAX_RETRY_ATTEMPTS) { + if (readRegisterBlock(reg, &reading, 1)) { + if (reading != INVALID_MODBUS_VALUE) { + return true; } - else - { - reading = node.getResponseBuffer(0x00); - if (_debug > 2) - SerialPrint("I", "EctoControlAdapter", "Success, Received data, register: " + String(reg) + " = " + String(reading, HEX)); - } - node.clearResponseBuffer(); } - else - { - if (_debug > 2) - SerialPrint("E", "EctoControlAdapter", "Failed, Response Code: " + String(result, HEX)); - return false; - } - - if (reading != 0x7FFF) - return true; - else - return false; + attempts++; + delay(RETRY_DELAY_MS); + } + + if (_debugLevel > 0) { + SerialPrint("E", "Modbus", "Read reg 0x" + String(reg, HEX) + + " failed after " + String(MAX_RETRY_ATTEMPTS) + " attempts"); } return false; } - bool getModelVersion() - { - uint16_t reqData; - bool ret; - ret = readFunctionModBus(ReadDataEctoControl::ecR_MemberCode, info.boilerMemberCode); - ret = readFunctionModBus(ReadDataEctoControl::ecR_ModelCode, info.boilerModelCode); - ret = readFunctionModBus(ReadDataEctoControl::ecR_AdaperVersion, reqData); - info.adapterHardVer = highByte(reqData); - info.adapterSoftVer = lowByte(reqData); - return ret; + // Оптимизированная запись с повторными попытками + bool writeWithRetry(uint16_t reg, uint16_t data) { + uint8_t attempts = 0; + while (attempts < MAX_RETRY_ATTEMPTS) { + node.setTransmitBuffer(0, data); + uint8_t result = node.writeMultipleRegisters(reg, 1); + node.clearTransmitBuffer(); + + if (result == node.ku8MBSuccess) { + if (_debugLevel > 2) { + SerialPrint("I", "Modbus", "Write 0x" + String(reg, HEX) + + " = " + String(data)); + } + return true; + } + attempts++; + delay(RETRY_DELAY_MS); + } + + if (_debugLevel > 0) { + SerialPrint("E", "Modbus", "Write reg 0x" + String(reg, HEX) + + " failed after " + String(MAX_RETRY_ATTEMPTS) + " attempts"); + } + return false; } - bool getBoilerInfo() - { - uint16_t reqData; - bool ret = readFunctionModBus(ReadDataEctoControl::ecR_AdapterInfo, reqData); - info.adapterType = highByte(reqData) & 0xF8; - info.boilerStatus = (highByte(reqData) >> 3) & 1u; - info.rebootStatus = lowByte(reqData); - if (ret) - { - publishData("adapterType", String(info.adapterType)); - publishData("boilerStatus", String(info.boilerStatus)); - publishData("rebootStatus", String(info.rebootStatus)); + // Оптимизированная публикация данных + void publishData(const __FlashStringHelper* name, const String& value) { + IoTItem* item = findIoTItem(name); + if (item) { + item->setValue(value, true); + } else if (_debugLevel > 0) { + SerialPrint("I", "EctoControl", String(name) + " = " + value); } - return ret; } - bool getBoilerStatus() - { - uint16_t reqData; - bool ret = readFunctionModBus(ReadDataEctoControl::ecR_BoilerStatus, reqData); - status.burnStatus = (lowByte(reqData) >> 0) & 1u; - status.CHStatus = (lowByte(reqData) >> 1) & 1u; - status.DHWStatus = (lowByte(reqData) >> 2) & 1u; - if (ret) - { - publishData("burnStatus", String(status.burnStatus)); - publishData("CHStatus", String(status.CHStatus)); - publishData("DHWStatus", String(status.DHWStatus)); + + // Чтение и обработка ошибок котла + bool readBoilerErrors() { + uint16_t registers[3]; + if (!readRegisterBlock(ecR_CodeError, registers, 3)) { + if (_debugLevel > 0) SerialPrint("E", "EctoControl", "Failed to read boiler errors"); + return false; } - return ret; - } - bool getCodeError() - { - bool ret = readFunctionModBus(ReadDataEctoControl::ecR_CodeError, code); - if (ret) - { - publishData("codeError", String(code)); - if (code) - sendTelegramm("EctoControlAdapter: код ошибки: " + String((int)code)); + + codeError = registers[0]; + codeErrorExt = registers[1]; + errorFlags = registers[2] & 0xFF; + + publishData(F("codeError"), String(codeError)); + publishData(F("codeErrorExt"), String(codeErrorExt)); + publishData(F("errorFlags"), String(errorFlags)); + + if (_debugLevel > 1) { + SerialPrint("I", "EctoControl", String("Boiler errors: ") + + "Main=" + String(codeError, HEX) + + ", Ext=" + String(codeErrorExt, HEX) + + ", Flags=" + String(errorFlags, HEX)); } - return ret; + + return true; } - bool getCodeErrorExt() - { - bool ret = readFunctionModBus(ReadDataEctoControl::ecR_CodeErrorExt, codeExt); - if (ret) - { - publishData("codeErrorExt", String(codeExt)); - if (codeExt) - sendTelegramm("EctoControlAdapter: код ошибки: " + String((int)codeExt)); + + // Групповое чтение основных данных + bool readCommonData() { + uint16_t registers[9]; // Увеличено до 9 регистров + if (!readRegisterBlock(ecR_TempCH, registers, 9)) { + return false; } - return ret; + + // Обработка температуры отопления + int16_t tempCH = registers[0]; + if (tempCH != INVALID_MODBUS_VALUE) { + tCH = tempCH / 10.0f; + publishData(F("tempCH"), String(tCH)); + } + + // Обработка температуры ГВС + uint16_t tempDHW = registers[1]; + if (tempDHW != INVALID_MODBUS_VALUE) { + tDHW = tempDHW / 10.0f; + publishData(F("tempDHW"), String(tDHW)); + } + + // Обработка давления + uint16_t pressure = registers[2]; + if (pressure != INVALID_MODBUS_VALUE) { + press = (pressure & 0xFF) / 10.0f; + publishData(F("pressure"), String(press)); + } + + // Обработка расхода ГВС + uint16_t flowRate = registers[3]; + if (flowRate != INVALID_MODBUS_VALUE) { + flow = (flowRate & 0xFF) / 10.0f; + publishData(F("flowRate"), String(flow)); + } + + // Обработка уровня модуляции + uint16_t modulation = registers[4]; + if (modulation != INVALID_MODBUS_VALUE) { + modLevel = modulation & 0xFF; + publishData(F("modLevel"), String(modLevel)); + } + + // Обработка статуса котла + uint16_t statusReg = registers[5]; + status.burnerActive = statusReg & 0x01; // Бит 0: горелка + status.heatingActive = (statusReg >> 1) & 0x01; // Бит 1: отопление + status.dhwActive = (statusReg >> 2) & 0x01; // Бит 2: ГВС + + publishData(F("burner"), String(status.burnerActive)); + publishData(F("heating"), String(status.heatingActive)); + publishData(F("dhw"), String(status.dhwActive)); + + // Обработка кодов ошибок + codeError = registers[6]; + codeErrorExt = registers[7]; + + // Обработка температуры наружного воздуха + int8_t tempOut = (int8_t)(registers[8] & 0xFF); + if (tempOut != 0x7F) { + tOut = tempOut; + publishData(F("tempOut"), String(tOut)); + } + + // Чтение ошибок котла + readBoilerErrors(); + + return true; } - bool getFlagErrorOT() - { - uint16_t reqData; - bool ret = readFunctionModBus(ReadDataEctoControl::ecR_FlagErrorOT, reqData); - flagErr = lowByte(reqData); - if (ret) - { - publishData("flagErr", String(flagErr)); - switch (flagErr) - { - case 0: - sendTelegramm("EctoControlAdapter: Необходимо обслуживание!"); - break; - case 1: - sendTelegramm("EctoControlAdapter: Котёл заблокирован!"); - break; - case 2: - sendTelegramm("EctoControlAdapter: Низкое давление в отопительном контуре!"); - break; - case 3: - sendTelegramm("EctoControlAdapter: Ошибка розжига!"); - break; - case 4: - sendTelegramm("EctoControlAdapter: Низкое давления воздуха!"); - break; - case 5: - sendTelegramm("EctoControlAdapter: Перегрев теплоносителя в контуре!"); - break; - default: - break; + + // Групповое чтение настроек + bool readSettings() { + uint16_t registers[6]; + if (!readRegisterBlock(ecR_ReadSetTypeConnect, registers, 6)) { + return false; + } + + // Обработка записанных значений + publishData(F("setTypeConnect"), String(registers[0])); + publishData(F("tSetCH"), String(registers[1] / 10.0f)); + publishData(F("tSetCHFaultConn"), String(registers[2] / 10.0f)); + publishData(F("tSetMinCH"), String(registers[3] / 10.0f)); + publishData(F("tSetMaxCH"), String(registers[4] / 10.0f)); + publishData(F("tSetMinDHW"), String(registers[5])); + + // Чтение оставшихся настроек + uint16_t settings[3]; + if (readRegisterBlock(ecR_ReadTSetMaxDHW, settings, 3)) { + publishData(F("tSetMaxDHW"), String(settings[0])); + publishData(F("tSetDHW"), String(settings[1])); + publishData(F("setMaxModLevel"), String(settings[2])); + } + + return true; + } + +public: + EctoControlAdapter(String parameters) : IoTItem(parameters) { + _addr = jsonReadInt(parameters, "addr", DEFAULT_MODBUS_ADDR); + _rx = jsonReadInt(parameters, "RX", 16); + _tx = jsonReadInt(parameters, "TX", 17); + _DIR_PIN = jsonReadInt(parameters, "DIR_PIN", DEFAULT_DIR_PIN); + _baud = jsonReadInt(parameters, "baud", 19200); + _prot = jsonReadStr(parameters, "protocol", "SERIAL_8N1"); + _debugLevel = jsonReadInt(parameters, "debug", 0); + _uartLine = jsonReadInt(parameters, "UARTLine", 1); // Новый параметр выбора порта + + // Инициализация UART + if (!_modbusUART) { + _modbusUART = new HardwareSerial(_uartLine); // Используем выбранный порт + if (_modbusUART) { + ((HardwareSerial*)_modbusUART)->begin(_baud, SERIAL_8N1, _rx, _tx); + ((HardwareSerial*)_modbusUART)->setTimeout(DEFAULT_SERIAL_TIMEOUT); + } else { + SerialPrint("E", "EctoControl", "Failed to initialize UART"); } } - return ret; - } + + // Инициализация Modbus + node.begin(_addr, _modbusUART); + node.preTransmission(modbusPreTransmission); + node.postTransmission(modbusPostTransmission); - bool getFlowRate() - { - uint16_t reqData; - bool ret = readFunctionModBus(ReadDataEctoControl::ecR_FlowRate, reqData); - flow = lowByte(reqData) / 10.f; - if (ret) - publishData("flowRate", String(flow)); - return ret; - } - bool getMaxSetCH() - { - uint16_t reqData; - bool ret = readFunctionModBus(ReadDataEctoControl::ecR_MaxSetCH, reqData); - maxSetCH = (float)lowByte(reqData); - if (ret) - publishData("maxSetCH", String(maxSetCH)); - return ret; - } - bool getMaxSetDHW() - { - uint16_t reqData; - bool ret = readFunctionModBus(ReadDataEctoControl::ecR_MaxSetDHW, reqData); - maxSetDHW = (float)lowByte(reqData); - if (ret) - publishData("maxSetDHW", String(maxSetDHW)); - return ret; - } - bool getMinSetCH() - { - uint16_t reqData; - bool ret = readFunctionModBus(ReadDataEctoControl::ecR_MinSetCH, reqData); - minSetCH = (float)lowByte(reqData); - if (ret) - publishData("minSetCH", String(minSetCH)); - return ret; - } - bool getMinSetDHW() - { - uint16_t reqData; - bool ret = readFunctionModBus(ReadDataEctoControl::ecR_MinSetDHW, reqData); - minSetDHW = (float)lowByte(reqData); - if (ret) - publishData("minSetDHW", String(minSetDHW)); - return ret; - } - bool getModLevel() - { - uint16_t reqData; - bool ret = readFunctionModBus(ReadDataEctoControl::ecR_ModLevel, reqData); - modLevel = (float)lowByte(reqData); - if (ret) - publishData("modLevel", String(modLevel)); - return ret; - } - bool getPressure() - { - uint16_t reqData; - bool ret = readFunctionModBus(ReadDataEctoControl::ecR_Pressure, reqData); - press = lowByte(reqData) / 10.f; - if (ret) - publishData("press", String(press)); - return ret; - } - bool getTempCH() - { - uint16_t reqData; - bool ret = readFunctionModBus(ReadDataEctoControl::ecR_TempCH, reqData); - tCH = reqData / 10.f; - if (ret) - publishData("tCH", String(tCH)); - return ret; - } - bool getTempDHW() - { - uint16_t reqData; - bool ret = readFunctionModBus(ReadDataEctoControl::ecR_TempDHW, reqData); - tDHW = reqData / 10.f; - if (ret) - publishData("tDHW", String(tDHW)); - return ret; - } - bool getTempOutside() - { - uint16_t reqData; - bool ret = readFunctionModBus(ReadDataEctoControl::ecR_TempOutside, reqData); - tOut = (float)lowByte(reqData); - if (ret) - publishData("tOut", String(tOut)); - return ret; - } - - bool setTypeConnect(float &data) - { - bool ret = false; - uint16_t data16 = data; - if (writeFunctionModBus(ecW_SetTypeConnect, data16)) - { - // TODO запросить результат записи у адаптера - ret = true; - } - else - { - if (_debug > 1) - SerialPrint("E", "EctoControlAdapter", "Failed, setTypeConnect"); - } - return ret; - } - bool setTCH(float &data) - { - bool ret = false; - uint16_t d16 = data * 10; - if (writeFunctionModBus(ecW_TSetCH, d16)) - { - ret = true; - } - else - { - if (_debug > 1) - SerialPrint("E", "EctoControlAdapter", "Failed, setTCH"); + if (_DIR_PIN) { + pinMode(_DIR_PIN, OUTPUT); + digitalWrite(_DIR_PIN, LOW); } - return ret; - } - bool setTCHFaultConn(float &data) - { - bool ret = false; - uint16_t d16 = data * 10; - if (writeFunctionModBus(ecW_TSetCHFaultConn, d16)) - { - ret = true; + if (_addr >= 0) { + initializeDevice(); } - else - { - if (_debug > 1) - SerialPrint("E", "EctoControlAdapter", "Failed, setTCHFaultConn"); - } - return ret; - } - bool setMinCH(float &data) - { - bool ret = false; - uint16_t data16 = data; - if (writeFunctionModBus(ecW_TSetMinCH, data16)) - { - ret = true; - } - else - { - if (_debug > 1) - SerialPrint("E", "EctoControlAdapter", "Failed, setMinCH"); - } - return ret; - } - bool setMaxCH(float &data) - { - bool ret = false; - uint16_t data16 = data; - if (writeFunctionModBus(ecW_TSetMaxCH, data16)) - { - ret = true; - } - else - { - if (_debug > 1) - SerialPrint("E", "EctoControlAdapter", "Failed, setMaxCH"); - } - return ret; - } - bool setMinDHW(float &data) - { - bool ret = false; - uint16_t data16 = data; - if (writeFunctionModBus(ecW_TSetMinDHW, data16)) - { - ret = true; - } - else - { - if (_debug > 1) - SerialPrint("E", "EctoControlAdapter", "Failed, setMinDHW"); - } - return ret; - } - bool setMaxDHW(float &data) - { - bool ret = false; - uint16_t data16 = data; - if (writeFunctionModBus(ecW_TSetMaxDHW, data16)) - { - ret = true; - } - else - { - if (_debug > 1) - SerialPrint("E", "EctoControlAdapter", "Failed, setMaxDHW"); - } - return ret; - } - bool setTDHW(float &data) - { - bool ret = false; - uint16_t data16 = data; - if (writeFunctionModBus(ecW_TSetDHW, data16)) - { - ret = true; - } - else - { - if (_debug > 1) - SerialPrint("E", "EctoControlAdapter", "Failed, setTDHW"); - } - return ret; - } - bool setMaxModLevel(float &data) - { - bool ret = false; - uint16_t data16 = data; - if (writeFunctionModBus(ecW_SetMaxModLevel, data16)) - { - ret = true; - } - else - { - if (_debug > 1) - SerialPrint("E", "EctoControlAdapter", "Failed, setMaxModLevel"); - } - return ret; } - bool setStatusCH(bool data) - { - bool ret = false; - enableCH = data; - uint16_t stat = enableCH | (enableDHW << 1) | (enableCH2 << 2); - if (writeFunctionModBus(ecW_SetStatusBoiler, stat)) - { - ret = true; + // Оптимизированная инициализация устройства + void initializeDevice() { + if (_addr > 0) { + uint16_t type = 0; + if (readWithRetry(0x0003, type)) { + type = type >> 8; + if (type != 0x14 && type != 0x15 && type != 0x16) { + SerialPrint("E", "EctoControl", "Unsupported device type: " + String(type, HEX)); + } + } else { + SerialPrint("E", "EctoControl", "Failed to read device type"); + } } - else - { - if (_debug > 1) - SerialPrint("E", "EctoControlAdapter", "Failed, setStatusCH"); + else if (_addr == 0) { + // если адреса нет, то шлем широковещательный запрос адреса + uint8_t addr = node.readAddresEctoControl(); + SerialPrint("I", "EctoControlAdapter", "readAddresEctoControl, addr: " + String(addr) + " - Enter to configuration"); } - return ret; - } - bool setStatusDHW(bool data) - { - bool ret = false; - enableDHW = data; - uint16_t stat = enableCH | (enableDHW << 1) | (enableCH2 << 2); - if (writeFunctionModBus(ecW_SetStatusBoiler, stat)) - { - ret = true; - } - else - { - if (_debug > 1) - SerialPrint("E", "EctoControlAdapter", "Failed, setStatusDHW"); - } - return ret; - } - bool setStatusCH2(bool data) - { - bool ret = false; - enableCH2 = data; - uint16_t stat = enableCH | (enableDHW << 1) | (enableCH2 << 2); - if (writeFunctionModBus(ecW_SetStatusBoiler, stat)) - { - ret = true; - } - else - { - if (_debug > 1) - SerialPrint("E", "EctoControlAdapter", "Failed, setStatusCH2"); - } - return ret; } - bool lockOutReset() - { - bool ret = false; - uint16_t d16 = comm_LockOutReset; - if (writeFunctionModBus(ecW_Command, d16)) - { - ret = true; + // Получение информации о котле + bool getBoilerInfo() { + uint16_t data[3]; + if (!readRegisterBlock(ecR_AdapterInfo, data, 3)) { + if (_debugLevel > 0) SerialPrint("E", "EctoControl", "Failed to read adapter info"); + return false; } - else - { - if (_debug > 1) - SerialPrint("E", "EctoControlAdapter", "Failed, lockOutReset"); + + info.adapterType = (data[0] >> 8) & 0x07; + info.boilerConnected = (data[0] >> 11) & 0x01; + info.rebootCode = data[0] & 0xFF; + info.hardwareVersion = data[1] >> 8; + info.softwareVersion = data[1] & 0xFF; + info.uptime = (uint32_t)data[2] << 16 | data[3]; + + uint16_t tempMemberCode; + if (!readWithRetry(ecR_MemberCode, tempMemberCode)) { + if (_debugLevel > 0) SerialPrint("E", "EctoControl", "Failed to read member code"); + } else { + info.memberCode = tempMemberCode; } - return ret; + + uint16_t tempModelCode; + if (!readWithRetry(ecR_ModelCode, tempModelCode)) { + if (_debugLevel > 0) SerialPrint("E", "EctoControl", "Failed to read model code"); + } else { + info.modelCode = tempModelCode; + } + + publishData(F("adapterType"), String(info.adapterType)); + publishData(F("boilerConnected"), String(info.boilerConnected)); + return true; } - bool rebootAdapter() - { - bool ret = false; - uint16_t d16 = comm_RebootAdapter; - if (writeFunctionModBus(ecW_Command, d16)) - { - ret = true; + + // Получение записанного статуса котла + bool getWrittenBoilerStatus() { + uint16_t regValue; + if (!readWithRetry(ecR_ReadSetStatusBoiler, regValue)) { + if (_debugLevel > 0) SerialPrint("E", "EctoControl", "Failed to read written boiler status"); + return false; } - else - { - if (_debug > 1) - SerialPrint("E", "EctoControlAdapter", "Failed, rebootAdapter"); + + status.writtenStatus.heatingEnabled = (regValue & 0x0001) != 0; + status.writtenStatus.dhwEnabled = (regValue & 0x0002) != 0; + status.writtenStatus.secondaryCircuit = (regValue & 0x0004) != 0; + + publishData(F("writtenHeatingEnabled"), String(status.writtenStatus.heatingEnabled)); + publishData(F("writtenDHWEnabled"), String(status.writtenStatus.dhwEnabled)); + publishData(F("writtenSecondaryCircuit"), String(status.writtenStatus.secondaryCircuit)); + + if (_debugLevel > 1) { + SerialPrint("I", "EctoControl", String("Status 0x003F: ") + + "Heating=" + status.writtenStatus.heatingEnabled + + ", DHW=" + status.writtenStatus.dhwEnabled + + ", Secondary=" + status.writtenStatus.secondaryCircuit); + } + + return true; + } + + // Установка статуса отопления + bool setHeating(bool enable) { + uint16_t currentStatus; + if (!readWithRetry(ecR_ReadSetStatusBoiler, currentStatus)) { + if (_debugLevel > 0) SerialPrint("E", "EctoControl", "Failed to read boiler status"); + return false; + } + + uint16_t newStatus = currentStatus & 0xFFFE; + if (enable) { + newStatus |= 0x0001; + } + + bool success = writeWithRetry(ecW_SetStatusBoiler, newStatus); + if (success) { + getWrittenBoilerStatus(); + } else { + if (_debugLevel > 0) SerialPrint("E", "EctoControl", "Failed to set heating status"); + } + return success; + } + + // Установка статуса ГВС + bool setDHW(bool enable) { + uint16_t currentStatus; + if (!readWithRetry(ecR_ReadSetStatusBoiler, currentStatus)) { + if (_debugLevel > 0) SerialPrint("E", "EctoControl", "Failed to read boiler status"); + return false; + } + + uint16_t newStatus = currentStatus & 0xFFFD; + if (enable) { + newStatus |= 0x0002; + } + + bool success = writeWithRetry(ecW_SetStatusBoiler, newStatus); + if (success) { + getWrittenBoilerStatus(); + } else { + if (_debugLevel > 0) SerialPrint("E", "EctoControl", "Failed to set DHW status"); + } + return success; + } + + // Установка статуса вторичного контура + bool setSecondaryCircuit(bool enable) { + uint16_t currentStatus; + if (!readWithRetry(ecR_ReadSetStatusBoiler, currentStatus)) { + if (_debugLevel > 0) SerialPrint("E", "EctoControl", "Failed to read boiler status"); + return false; + } + + uint16_t newStatus = currentStatus & 0xFFFB; + if (enable) { + newStatus |= 0x0004; + } + + bool success = writeWithRetry(ecW_SetStatusBoiler, newStatus); + if (success) { + getWrittenBoilerStatus(); + } else { + if (_debugLevel > 0) SerialPrint("E", "EctoControl", "Failed to set secondary circuit status"); + } + return success; + } + + // Установка общего статуса котла + bool setBoilerStatus(bool heating, bool dhw, bool secondary) { + uint16_t newStatus = 0; + if (heating) newStatus |= 0x0001; + if (dhw) newStatus |= 0x0002; + if (secondary) newStatus |= 0x0004; + + if (!writeWithRetry(ecW_SetStatusBoiler, newStatus)) { + if (_debugLevel > 0) SerialPrint("E", "EctoControl", "Failed to set boiler status"); + return false; + } + + // Проверяем, что статус обновился + uint16_t currentStatus; + if (readWithRetry(ecR_ReadSetStatusBoiler, currentStatus)) { + return (currentStatus & 0x0007) == newStatus; + } + return false; + } + + // Установка температуры отопления + bool setTCH(float temp) { + int16_t value = temp * 10; + if (!writeWithRetry(ecW_TSetCH, (uint16_t)value)) { + if (_debugLevel > 0) SerialPrint("E", "EctoControl", "Failed to set CH temperature"); + return false; + } + return true; + } + + // Установка температуры ГВС + bool setTDHW(float temp) { + uint16_t value = temp; + if (!writeWithRetry(ecW_TSetDHW, value)) { + if (_debugLevel > 0) SerialPrint("E", "EctoControl", "Failed to set DHW temperature"); + return false; + } + return true; + } + + // Перезагрузка адаптера + bool rebootAdapter() { + if (!writeWithRetry(ecW_Command, COMMAND_REBOOT)) { + if (_debugLevel > 0) SerialPrint("E", "EctoControl", "Failed to send reboot command"); + return false; + } + + uint16_t response; + if (readWithRetry(0x0081, response)) { + return response == 0; + } + return false; + } + + // Сброс ошибок котла + bool resetBoilerErrors() { + if (!writeWithRetry(ecW_Command, COMMAND_RESET_ERRORS)) { + if (_debugLevel > 0) SerialPrint("E", "EctoControl", "Failed to send reset errors command"); + return false; + } + + uint16_t response; + if (readWithRetry(0x0081, response)) { + return response == 0; + } + return false; + } + + // Оптимизированный основной цикл + void doByInterval() override { + if (_addr <= 0) return; + + uint32_t currentTime = millis(); + if (currentTime - _lastUpdate < UPDATE_INTERVAL) { + return; + } + _lastUpdate = currentTime; + + // Чтение основных данных одним запросом + if (!readCommonData()) { + return; + } + + // Чтение информации о котле + getBoilerInfo(); + + // Чтение записанных настроек + readSettings(); + + // Чтение записанного статуса + getWrittenBoilerStatus(); + } + + // Обработка команд + IoTValue execute(String command, std::vector ¶m) override { + if (command == "setHeating") { + if (param.size() && param[0].isDecimal) { + setHeating(param[0].valD); + } + } + else if (command == "setDHW") { + if (param.size() && param[0].isDecimal) { + setDHW(param[0].valD); + } + } + else if (command == "setSecondaryCircuit") { + if (param.size() && param[0].isDecimal) { + setSecondaryCircuit(param[0].valD); + } + } + else if (command == "setBoilerStatus") { + if (param.size() >= 3) { + setBoilerStatus(param[0].valD, param[1].valD, param[2].valD); + } + } + else if (command == "getBoilerInfo") { + getBoilerInfo(); + } + else if (command == "getBoilerStatus") { + // Уже читается в readCommonData() + } + else if (command == "getWrittenBoilerStatus") { + getWrittenBoilerStatus(); + } + else if (command == "getSettings") { + readSettings(); + } + else if (command == "setTCH") { + if (param.size() && param[0].isDecimal) { + setTCH(param[0].valD); + } + } + else if (command == "setTDHW") { + if (param.size() && param[0].isDecimal) { + setTDHW(param[0].valD); + } + } + else if (command == "reboot") { + rebootAdapter(); + } + else if (command == "resetErrors") { + resetBoilerErrors(); + } + else if (command == "setSetTypeConnect") { + if (param.size() && param[0].isDecimal) { + writeWithRetry(ecW_SetTypeConnect, static_cast(param[0].valD)); + } + } + else if (command == "setTSetCHFaultConn") { + if (param.size() && param[0].isDecimal) { + writeWithRetry(ecW_TSetCHFaultConn, static_cast(param[0].valD * 10)); + } + } + else if (command == "setTSetMinCH") { + if (param.size() && param[0].isDecimal) { + writeWithRetry(ecW_TSetMinCH, static_cast(param[0].valD * 10)); + } + } + else if (command == "setTSetMaxCH") { + if (param.size() && param[0].isDecimal) { + writeWithRetry(ecW_TSetMaxCH, static_cast(param[0].valD * 10)); + } + } + else if (command == "setTSetMinDHW") { + if (param.size() && param[0].isDecimal) { + writeWithRetry(ecW_TSetMinDHW, static_cast(param[0].valD)); + } + } + else if (command == "setTSetMaxDHW") { + if (param.size() && param[0].isDecimal) { + writeWithRetry(ecW_TSetMaxDHW, static_cast(param[0].valD)); + } + } + else if (command == "setSetMaxModLevel") { + if (param.size() && param[0].isDecimal) { + writeWithRetry(ecW_SetMaxModLevel, static_cast(param[0].valD)); + } + } + else if (command == "getTempCH") { + uint16_t regValue; + if (readWithRetry(ecR_TempCH, regValue)) { + IoTValue val; + val.valD = regValue / 10.0; + val.isDecimal = true; + return val; + } + } + else if (command == "getTempDHW") { + uint16_t regValue; + if (readWithRetry(ecR_TempDHW, regValue)) { + IoTValue val; + val.valD = regValue / 10.0; + val.isDecimal = true; + return val; + } + } + else if (command == "getPressure") { + uint16_t regValue; + if (readWithRetry(ecR_Pressure, regValue)) { + IoTValue val; + val.valD = (regValue & 0xFF) / 10.0; + val.isDecimal = true; + return val; + } + } + else if (command == "getFlowRate") { + uint16_t regValue; + if (readWithRetry(ecR_FlowRate, regValue)) { + IoTValue val; + val.valD = (regValue & 0xFF) / 10.0; + val.isDecimal = true; + return val; + } + } + else if (command == "getModLevel") { + uint16_t regValue; + if (readWithRetry(ecR_ModLevel, regValue)) { + IoTValue val; + val.valD = static_cast(regValue & 0xFF); + val.isDecimal = true; + return val; + } + } + else if (command == "getBoilerStatusRaw") { + uint16_t regValue; + if (readWithRetry(ecR_BoilerStatus, regValue)) { + IoTValue val; + val.valD = regValue; + val.isDecimal = true; + return val; + } + } + else if (command == "getTempOutside") { + uint16_t regValue; + if (readWithRetry(ecR_TempOutside, regValue)) { + int8_t temp = static_cast(regValue & 0xFF); + IoTValue val; + val.valD = (temp != 0x7F) ? temp : 0; + val.isDecimal = true; + return val; + } + } + else if (command == "getFlagErrorOT") { + uint16_t regValue; + if (readWithRetry(ecR_FlagErrorOT, regValue)) { + IoTValue val; + val.valD = regValue; + val.isDecimal = true; + return val; + } + } + else if (command == "getMinSetCH") { + uint16_t regValue; + if (readWithRetry(ecR_MinSetCH, regValue)) { + IoTValue val; + val.valD = regValue / 10.0; + val.isDecimal = true; + return val; + } + } + else if (command == "getMaxSetCH") { + uint16_t regValue; + if (readWithRetry(ecR_MaxSetCH, regValue)) { + IoTValue val; + val.valD = regValue / 10.0; + val.isDecimal = true; + return val; + } + } + else if (command == "getMinSetDHW") { + uint16_t regValue; + if (readWithRetry(ecR_MinSetDHW, regValue)) { + IoTValue val; + val.valD = regValue; + val.isDecimal = true; + return val; + } + } + else if (command == "getMaxSetDHW") { + uint16_t regValue; + if (readWithRetry(ecR_MaxSetDHW, regValue)) { + IoTValue val; + val.valD = regValue; + val.isDecimal = true; + return val; + } + } + else if (command == "getAdapterInfo") { + uint16_t regValue; + if (readWithRetry(ecR_AdapterInfo, regValue)) { + IoTValue val; + val.valD = regValue; + val.isDecimal = true; + return val; + } + } + else if (command == "getAdapterVersion") { + uint16_t regValue; + if (readWithRetry(ecR_AdapterVersion, regValue)) { + IoTValue val; + val.valD = regValue; + val.isDecimal = true; + return val; + } + } + else if (command == "getUptime") { + uint16_t regValue; + if (readWithRetry(ecR_Uptime, regValue)) { + IoTValue val; + val.valD = regValue; + val.isDecimal = true; + return val; + } + } + else { + SerialPrint("E", "EctoControl", "Unknown command: " + command); + } + + return {}; + } + + ~EctoControlAdapter() { + if (_modbusUART) { + ((HardwareSerial*)_modbusUART)->end(); + delete _modbusUART; + _modbusUART = nullptr; } - return ret; } }; -void *getAPI_EctoControlAdapter(String subtype, String param) -{ - - if (subtype == F("ecAdapter")) - { +void* getAPI_EctoControlAdapter(String subtype, String param) { + if (subtype == F("ecAdapter")) { return new EctoControlAdapter(param); } - { - return nullptr; - } -} + return nullptr; +} \ No newline at end of file diff --git a/src/modules/exec/EctoControlAdapter/ModbusEC.cpp b/src/modules/exec/EctoControlAdapter/ModbusEC.cpp index d9d0818e..b0644a79 100644 --- a/src/modules/exec/EctoControlAdapter/ModbusEC.cpp +++ b/src/modules/exec/EctoControlAdapter/ModbusEC.cpp @@ -1,4 +1,3 @@ - #include "ModbusEC.h" #define COUNT_BIT_AVAIL 5 @@ -11,19 +10,8 @@ ModbusMaster::ModbusMaster(void) _postTransmission = 0; } -/** -Initialize class object. - -Assigns the Modbus slave ID and serial port. -Call once class has been instantiated, typically within setup(). - -@param slave Modbus slave ID (1..255) -@param &serial reference to serial port object (Serial, Serial1, ... Serial3) -@ingroup setup -*/ void ModbusMaster::begin(uint8_t slave, Stream *serial) { - // txBuffer = (uint16_t*) calloc(ku8MaxBufferSize, sizeof(uint16_t)); _u8MBSlave = slave; _serial = serial; _u8TransmitBufferIndex = 0; @@ -42,16 +30,13 @@ void ModbusMaster::beginTransmission(uint16_t u16Address) u16TransmitBufferLength = 0; } -// eliminate this function in favor of using existing MB request functions uint8_t ModbusMaster::requestFrom(uint16_t address, uint16_t quantity) { - uint8_t read; - // clamp to buffer length + uint8_t read = 0; // if (quantity > ku8MaxBufferSize) { quantity = ku8MaxBufferSize; } - // set rx buffer iterator vars _u8ResponseBufferIndex = 0; _u8ResponseBufferLength = read; @@ -110,62 +95,21 @@ uint16_t ModbusMaster::receive(void) } } -/** -Set idle time callback function (cooperative multitasking). - -This function gets called in the idle time between transmission of data -and response from slave. Do not call functions that read from the serial -buffer that is used by ModbusMaster. Use of i2c/TWI, 1-Wire, other -serial ports, etc. is permitted within callback function. - -@see ModbusMaster::ModbusMasterTransaction() -*/ void ModbusMaster::idle(void (*idle)()) { _idle = idle; } -/** -Set pre-transmission callback function. - -This function gets called just before a Modbus message is sent over serial. -Typical usage of this callback is to enable an RS485 transceiver's -Driver Enable pin, and optionally disable its Receiver Enable pin. - -@see ModbusMaster::ModbusMasterTransaction() -@see ModbusMaster::postTransmission() -*/ void ModbusMaster::preTransmission(void (*preTransmission)()) { _preTransmission = preTransmission; } -/** -Set post-transmission callback function. - -This function gets called after a Modbus message has finished sending -(i.e. after all data has been physically transmitted onto the serial -bus). - -Typical usage of this callback is to enable an RS485 transceiver's -Receiver Enable pin, and disable its Driver Enable pin. - -@see ModbusMaster::ModbusMasterTransaction() -@see ModbusMaster::preTransmission() -*/ void ModbusMaster::postTransmission(void (*postTransmission)()) { _postTransmission = postTransmission; } -/** -Retrieve data from response buffer. - -@see ModbusMaster::clearResponseBuffer() -@param u8Index index of response buffer array (0x00..0x3F) -@return value in position u8Index of response buffer (0x0000..0xFFFF) -@ingroup buffer -*/ uint16_t ModbusMaster::getResponseBuffer(uint8_t u8Index) { if (u8Index < ku8MaxBufferSize) @@ -178,31 +122,15 @@ uint16_t ModbusMaster::getResponseBuffer(uint8_t u8Index) } } -/** -Clear Modbus response buffer. - -@see ModbusMaster::getResponseBuffer(uint8_t u8Index) -@ingroup buffer -*/ void ModbusMaster::clearResponseBuffer() { uint8_t i; - for (i = 0; i < ku8MaxBufferSize; i++) { _u16ResponseBuffer[i] = 0; } } -/** -Place data in transmit buffer. - -@see ModbusMaster::clearTransmitBuffer() -@param u8Index index of transmit buffer array (0x00..0x3F) -@param u16Value value to place in position u8Index of transmit buffer (0x0000..0xFFFF) -@return 0 on success; exception number on failure -@ingroup buffer -*/ uint8_t ModbusMaster::setTransmitBuffer(uint8_t u8Index, uint16_t u16Value) { if (u8Index < ku8MaxBufferSize) @@ -216,38 +144,15 @@ uint8_t ModbusMaster::setTransmitBuffer(uint8_t u8Index, uint16_t u16Value) } } -/** -Clear Modbus transmit buffer. - -@see ModbusMaster::setTransmitBuffer(uint8_t u8Index, uint16_t u16Value) -@ingroup buffer -*/ void ModbusMaster::clearTransmitBuffer() { uint8_t i; - for (i = 0; i < ku8MaxBufferSize; i++) { _u16TransmitBuffer[i] = 0; } } -/** -Modbus function 0x03 Read Holding Registers. - -This function code is used to read the contents of a contiguous block of -holding registers in a remote device. The request specifies the starting -register address and the number of registers. Registers are addressed -starting at zero. - -The register data in the response buffer is packed as one word per -register. - -@param u16ReadAddress address of the first holding register (0x0000..0xFFFF) -@param u16ReadQty quantity of holding registers to read (1..125, enforced by remote device) -@return 0 on success; exception number on failure -@ingroup register -*/ uint8_t ModbusMaster::readHoldingRegisters(uint16_t u16ReadAddress, uint16_t u16ReadQty) { @@ -256,18 +161,6 @@ uint8_t ModbusMaster::readHoldingRegisters(uint16_t u16ReadAddress, return ModbusMasterTransaction(ku8MBReadHoldingRegisters); } -/** -Modbus function 0x06 Write Single Register. - -This function code is used to write a single holding register in a -remote device. The request specifies the address of the register to be -written. Registers are addressed starting at zero. - -@param u16WriteAddress address of the holding register (0x0000..0xFFFF) -@param u16WriteValue value to be written to holding register (0x0000..0xFFFF) -@return 0 on success; exception number on failure -@ingroup register -*/ uint8_t ModbusMaster::writeSingleRegister(uint16_t u16WriteAddress, uint16_t u16WriteValue) { @@ -277,20 +170,6 @@ uint8_t ModbusMaster::writeSingleRegister(uint16_t u16WriteAddress, return ModbusMasterTransaction(ku8MBWriteSingleRegister); } -/** -Modbus function 0x10 Write Multiple Registers. - -This function code is used to write a block of contiguous registers (1 -to 123 registers) in a remote device. - -The requested written values are specified in the transmit buffer. Data -is packed as one word per register. - -@param u16WriteAddress address of the holding register (0x0000..0xFFFF) -@param u16WriteQty quantity of holding registers to write (1..123, enforced by remote device) -@return 0 on success; exception number on failure -@ingroup register -*/ uint8_t ModbusMaster::writeMultipleRegisters(uint16_t u16WriteAddress, uint16_t u16WriteQty) { @@ -299,7 +178,6 @@ uint8_t ModbusMaster::writeMultipleRegisters(uint16_t u16WriteAddress, return ModbusMasterTransaction(ku8MBWriteMultipleRegisters); } -// new version based on Wire.h uint8_t ModbusMaster::writeMultipleRegisters() { _u16WriteQty = _u8TransmitBufferIndex; @@ -313,6 +191,7 @@ uint8_t ModbusMaster::readAddresEctoControl() ModbusMasterTransaction(ku8MBProgRead46); return getResponseBuffer(0x00); } + uint8_t ModbusMaster::writeAddresEctoControl(uint8_t addr) { _u16WriteAddress = 0x00; @@ -321,20 +200,6 @@ uint8_t ModbusMaster::writeAddresEctoControl(uint8_t addr) return ModbusMasterTransaction(ku8MBProgWrite47); } -/* _____PRIVATE FUNCTIONS____________________________________________________ */ -/** -Modbus transaction engine. -Sequence: - - assemble Modbus Request Application Data Unit (ADU), - based on particular function called - - transmit request over selected serial port - - wait for/retrieve response - - evaluate/disassemble response - - return status (success/exception) - -@param u8MBFunction Modbus function (0x01..0xFF) -@return 0 on success; exception number on failure -*/ uint8_t ModbusMaster::ModbusMasterTransaction(uint8_t u8MBFunction) { uint8_t u8ModbusADU[24]; @@ -345,7 +210,7 @@ uint8_t ModbusMaster::ModbusMasterTransaction(uint8_t u8MBFunction) uint8_t u8BytesLeft = 8; uint8_t u8MBStatus = ku8MBSuccess; - // assemble Modbus Request Application Data Unit + // Assemble Modbus Request Application Data Unit if (u8MBFunction == ku8MBProgRead46 || u8MBFunction == ku8MBProgWrite47) { u8ModbusADU[u8ModbusADUSize++] = 0x00; @@ -382,7 +247,8 @@ uint8_t ModbusMaster::ModbusMasterTransaction(uint8_t u8MBFunction) u8ModbusADU[u8ModbusADUSize++] = lowByte(_u16WriteQty); u8ModbusADU[u8ModbusADUSize++] = lowByte(_u16WriteQty << 1); - for (i = 0; i < lowByte(_u16WriteQty); i++) + // : _u16WriteQty lowByte() + for (i = 0; i < _u16WriteQty; i++) { u8ModbusADU[u8ModbusADUSize++] = highByte(_u16TransmitBuffer[i]); u8ModbusADU[u8ModbusADUSize++] = lowByte(_u16TransmitBuffer[i]); @@ -390,47 +256,31 @@ uint8_t ModbusMaster::ModbusMasterTransaction(uint8_t u8MBFunction) break; } - // append CRC + // Append CRC u16CRC = 0xFFFF; for (i = 0; i < u8ModbusADUSize; i++) { u16CRC = crc16_update(u16CRC, u8ModbusADU[i]); } - // if (u8MBFunction == ku8MBProgRead46 || u8MBFunction == ku8MBProgWrite47) - // { - // u8ModbusADU[u8ModbusADUSize++] = highByte(u16CRC); - // u8ModbusADU[u8ModbusADUSize++] = lowByte(u16CRC); - // } - // else - // { u8ModbusADU[u8ModbusADUSize++] = lowByte(u16CRC); u8ModbusADU[u8ModbusADUSize++] = highByte(u16CRC); - // } - u8ModbusADU[u8ModbusADUSize] = 0; - // flush receive buffer before transmitting request - while (_serial->read() != -1) - ; + // Flush receive buffer before transmitting request + while (_serial->read() != -1); - // transmit request - if (_preTransmission) - { - _preTransmission(); - } + // Transmit request + if (_preTransmission) _preTransmission(); for (i = 0; i < u8ModbusADUSize; i++) { _serial->write(u8ModbusADU[i]); } u8ModbusADUSize = 0; - _serial->flush(); // flush transmit buffer - if (_postTransmission) - { - _postTransmission(); - } + _serial->flush(); + if (_postTransmission) _postTransmission(); - // loop until we run out of time or bytes, or an error occurs + // Loop until timeout or response complete u32StartTime = millis(); while (u8BytesLeft && !u8MBStatus) { @@ -441,7 +291,6 @@ uint8_t ModbusMaster::ModbusMasterTransaction(uint8_t u8MBFunction) #endif uint8_t req = _serial->read(); u8ModbusADU[u8ModbusADUSize++] = req; - Serial.print(req, HEX); u8BytesLeft--; #if __MODBUSMASTER_DEBUG__ digitalWrite(__MODBUSMASTER_DEBUG_PIN_A__, false); @@ -452,132 +301,110 @@ uint8_t ModbusMaster::ModbusMasterTransaction(uint8_t u8MBFunction) #if __MODBUSMASTER_DEBUG__ digitalWrite(__MODBUSMASTER_DEBUG_PIN_B__, true); #endif - if (_idle) - { - _idle(); - } + if (_idle) _idle(); #if __MODBUSMASTER_DEBUG__ digitalWrite(__MODBUSMASTER_DEBUG_PIN_B__, false); #endif } - // evaluate slave ID, function code once enough bytes have been read - uint8_t count; - if (u8MBFunction == ku8MBProgRead46 || u8MBFunction == ku8MBProgWrite47) - count = COUNT_BIT_AVAIL_46F; - else - count = COUNT_BIT_AVAIL; + // Evaluate response after enough bytes received + uint8_t count = (u8MBFunction == ku8MBProgRead46 || u8MBFunction == ku8MBProgWrite47) + ? COUNT_BIT_AVAIL_46F : COUNT_BIT_AVAIL; if (u8ModbusADUSize == count) { if (u8MBFunction != ku8MBProgRead46 && u8MBFunction != ku8MBProgWrite47) { - // verify response is for correct Modbus slave if (u8ModbusADU[0] != _u8MBSlave) { - // Serial.print(u8ModbusADU[0], HEX); - // Serial.print(" != "); - // Serial.println(_u8MBSlave, HEX); - u8MBStatus = ku8MBInvalidSlaveID; break; } - // verify response is for correct Modbus function code (mask exception bit 7) if ((u8ModbusADU[1] & 0x7F) != u8MBFunction) { u8MBStatus = ku8MBInvalidFunction; break; } } - // check whether Modbus exception occurred; return Modbus Exception Code + if (bitRead(u8ModbusADU[1], 7)) { u8MBStatus = u8ModbusADU[2]; break; } - // evaluate returned Modbus function code + // Determine remaining bytes based on function switch (u8ModbusADU[1]) { - case ku8MBReadHoldingRegisters: - u8BytesLeft = u8ModbusADU[2]; - break; - - case ku8MBWriteMultipleRegisters: - u8BytesLeft = 3; - break; - - case ku8MBProgRead46: - u8BytesLeft = 1; - break; - - case ku8MBProgWrite47: - u8BytesLeft = 1; - break; - - default: + case ku8MBReadHoldingRegisters: + u8BytesLeft = u8ModbusADU[2]; + break; + case ku8MBWriteMultipleRegisters: + u8BytesLeft = 3; + break; + case ku8MBProgRead46: + case ku8MBProgWrite47: + u8BytesLeft = 1; + break; + default: ; // } } + if ((millis() - u32StartTime) > ku16MBResponseTimeout) { u8MBStatus = ku8MBResponseTimedOut; } } - if (u8MBFunction != ku8MBProgRead46 && u8MBFunction != ku8MBProgWrite47) + // Verify CRC for standard functions + if (!u8MBStatus && + u8MBFunction != ku8MBProgRead46 && + u8MBFunction != ku8MBProgWrite47 && + u8ModbusADUSize >= COUNT_BIT_AVAIL) { - // verify response is large enough to inspect further - if (!u8MBStatus && u8ModbusADUSize >= COUNT_BIT_AVAIL) + u16CRC = 0xFFFF; + for (i = 0; i < (u8ModbusADUSize - 2); i++) { - // calculate CRC - u16CRC = 0xFFFF; - for (i = 0; i < (u8ModbusADUSize - 2); i++) - { - u16CRC = crc16_update(u16CRC, u8ModbusADU[i]); - } + u16CRC = crc16_update(u16CRC, u8ModbusADU[i]); + } - // verify CRC - if (!u8MBStatus && (lowByte(u16CRC) != u8ModbusADU[u8ModbusADUSize - 2] || - highByte(u16CRC) != u8ModbusADU[u8ModbusADUSize - 1])) - { - u8MBStatus = ku8MBInvalidCRC; - } + // : + if (!u8MBStatus && + ((lowByte(u16CRC) != u8ModbusADU[u8ModbusADUSize - 2]) || + (highByte(u16CRC) != u8ModbusADU[u8ModbusADUSize - 1]))) + { + u8MBStatus = ku8MBInvalidCRC; } } - // disassemble ADU into words + + // Parse response data if (!u8MBStatus) { - // evaluate returned Modbus function code switch (u8ModbusADU[1]) { - case ku8MBReadHoldingRegisters: - // load bytes into word; response bytes are ordered H, L, H, L, ... - for (i = 0; i < (u8ModbusADU[2] >> 1); i++) - { - if (i < ku8MaxBufferSize) + case ku8MBReadHoldingRegisters: + for (i = 0; i < (u8ModbusADU[2] >> 1); i++) { - _u16ResponseBuffer[i] = word(u8ModbusADU[2 * i + 3], u8ModbusADU[2 * i + 4]); + if (i < ku8MaxBufferSize) + { + _u16ResponseBuffer[i] = word(u8ModbusADU[2 * i + 3], u8ModbusADU[2 * i + 4]); + } + _u8ResponseBufferLength = i; } - - _u8ResponseBufferLength = i; - } - break; - case ku8MBProgRead46: - Serial.print("ku8MBProgRead46"); - for (i = 0; i < (u8ModbusADUSize); i++) - { - Serial.println(u8ModbusADU[i], HEX); - } - - _u16ResponseBuffer[0] = (uint16_t)u8ModbusADU[2]; - _u8ResponseBufferLength = 1; - break; + break; + + case ku8MBProgRead46: + _u16ResponseBuffer[0] = (uint16_t)u8ModbusADU[2]; + _u8ResponseBufferLength = 1; + break; } } + // Reset buffers _u8TransmitBufferIndex = 0; u16TransmitBufferLength = 0; _u8ResponseBufferIndex = 0; + return u8MBStatus; -} +} \ No newline at end of file diff --git a/src/modules/exec/EctoControlAdapter/export - Копия.json b/src/modules/exec/EctoControlAdapter/export - Копия.json new file mode 100644 index 00000000..8d8cf06b --- /dev/null +++ b/src/modules/exec/EctoControlAdapter/export - Копия.json @@ -0,0 +1,459 @@ +{ + "mark": "iotm", + "config": [ + { + "global": 0, + "type": "Reading", + "subtype": "Variable", + "id": "boilerConnected", + "needSave": 0, + "widget": "anydataDef", + "page": "Статус котла", + "descr": "✔ Связь с котлом", + "int": "0", + "val": "0.0", + "map": "1024,1024,1,100", + "plus": 0, + "multiply": 1, + "round": 0 + }, + { + "global": 0, + "type": "Reading", + "subtype": "Variable", + "id": "burner", + "needSave": 0, + "widget": "anydataDef", + "page": "Статус котла", + "descr": "🔥 Горелка", + "int": "0", + "val": "0.0", + "map": "1024,1024,1,100", + "plus": 0, + "multiply": 1, + "round": 0 + }, + { + "global": 0, + "type": "Reading", + "subtype": "Variable", + "id": "heating", + "needSave": 0, + "widget": "anydataDef", + "page": "Статус котла", + "descr": "☢ Контур отопления", + "int": "0", + "val": "0.0", + "map": "1024,1024,1,100", + "plus": 0, + "multiply": 1, + "round": 0 + }, + { + "global": 0, + "type": "Reading", + "subtype": "Variable", + "id": "dhw", + "needSave": 0, + "widget": "anydataDef", + "page": "Статус котла", + "descr": "🛁 Контур ГВС", + "int": "0", + "val": "0.0", + "map": "1024,1024,1,100", + "plus": 0, + "multiply": 1, + "round": 0 + }, + { + "global": "1", + "type": "Reading", + "subtype": "Variable", + "id": "tempCH", + "needSave": 0, + "widget": "anydataDef", + "page": "Котёл", + "descr": "🌡️ Теплоносителя", + "int": "0", + "val": "0.0", + "map": "1024,1024,1,100", + "plus": 0, + "multiply": 1, + "round": 0 + }, + { + "global": "1", + "type": "Reading", + "subtype": "Variable", + "id": "tempDHW", + "needSave": 0, + "widget": "anydataDef", + "page": "Котёл", + "descr": "🌡️ ГВС", + "int": "0", + "val": "0.0", + "map": "1024,1024,1,100", + "plus": 0, + "multiply": 1, + "round": 0 + }, + { + "global": 0, + "type": "Reading", + "subtype": "VButton", + "id": "setStatusCH", + "needSave": "0", + "widget": "toggle", + "page": "Управление", + "descr": "☢ Отопление (вкл/выкл)", + "int": "0", + "val": "0" + }, + { + "global": 0, + "type": "Reading", + "subtype": "VButton", + "id": "setStatusDHW", + "needSave": "0", + "widget": "toggle", + "page": "Управление", + "descr": "🛁 Гвс (вкл/выкл)", + "int": "0", + "val": "0" + }, + { + "global": 0, + "type": "Reading", + "subtype": "Variable", + "id": "setTCH", + "needSave": "0", + "widget": "inputDgt", + "page": "Управление", + "descr": "🌡️ Уставка отопления", + "int": "0", + "val": "0", + "map": "1024,1024,1,100", + "plus": 0, + "multiply": 1, + "round": 0 + }, + { + "global": "1", + "type": "Reading", + "subtype": "Ds18b20", + "id": "temp", + "widget": "anydataTmp", + "page": "Термостат", + "descr": "🌡️ В помещении", + "int": "120", + "pin": "15", + "index": "0", + "addr": "28163C0D000000CF", + "round": "1" + }, + { + "global": 0, + "type": "Reading", + "subtype": "Variable", + "id": "SetTempId", + "needSave": "1", + "widget": "inputDgt", + "page": "Термостат", + "descr": "🌡️ Желаемая", + "int": "0", + "val": "23", + "map": "1024,1024,1,100", + "plus": 0, + "multiply": 1, + "round": 0 + }, + { + "global": 0, + "type": "Reading", + "subtype": "Variable", + "id": "setDHW", + "needSave": "1", + "widget": "inputDgt", + "page": "Управление", + "descr": "🌡️ Уставка ГВС", + "int": "0", + "val": "0", + "map": "1024,1024,1,100", + "plus": 0, + "multiply": 1, + "round": 0 + }, + { + "global": 0, + "type": "Reading", + "subtype": "Variable", + "id": "pressure", + "needSave": 0, + "widget": "anydataDef", + "page": "Статус котла", + "descr": "💧 Давление", + "int": "0", + "val": "0.0", + "map": "1024,1024,1,100", + "plus": 0, + "multiply": 1, + "round": "2" + }, + { + "global": 0, + "type": "Reading", + "subtype": "Variable", + "id": "codeError", + "needSave": 0, + "widget": "anydataDef", + "page": "Котёл", + "descr": "🚨 Ошибка котла", + "int": "0", + "val": "0.0", + "map": "1024,1024,1,100", + "plus": 0, + "multiply": 1, + "round": 0 + }, + { + "global": 0, + "type": "Reading", + "subtype": "Variable", + "id": "lasttemp", + "needSave": 0, + "widget": "nil", + "page": "Ввод", + "descr": "Введите число", + "int": "0", + "val": "0", + "map": "1024,1024,1,100", + "plus": 0, + "multiply": 1, + "round": 0, + "moduleName": "Variable" + }, + { + "global": 0, + "type": "Reading", + "subtype": "VButton", + "id": "auto", + "needSave": "1", + "widget": "toggle", + "page": "Термостат", + "descr": "Термостат (вкл/выкл", + "int": "0", + "val": "0", + "moduleName": "VButton" + }, + { + "global": 0, + "type": "Reading", + "subtype": "Variable", + "id": "tempOT", + "needSave": 0, + "widget": "nil", + "page": "Ввод", + "descr": "Введите число", + "int": "0", + "val": "0", + "map": "1024,1024,1,100", + "plus": 0, + "multiply": 1, + "round": 0, + "moduleName": "Variable" + }, + { + "global": 0, + "type": "Writing", + "subtype": "TelegramLT", + "id": "tg", + "widget": "", + "page": "", + "descr": "", + "token": "", + "chatID": "", + "moduleName": "TelegramLT" + }, + { + "global": "1", + "needSave": "0", + "type": "Writing", + "subtype": "ThermostatPID", + "id": "Thermo96", + "widget": "anydataTmp", + "page": "Термостат", + "descr": "🌡️ Расчетная", + "int": "10", + "round": 1, + "map": "1024,1024,1,100", + "set_id": "SetTempId", + "term_id": "temp", + "rele": "", + "setDirection": 0, + "setLimitsMIN": "20", + "setLimitsMAX": "60", + "KP": 10, + "KI": "0.01", + "KD": "8", + "moduleName": "Thermostat" + }, + { + "global": 0, + "type": "Reading", + "subtype": "UpdateServer", + "id": "UpdateServer38", + "widget": "", + "page": "", + "descr": "", + "btn-startUpdateAll": "http://192.168.0.84:5500/iotm/esp32c6_8mb/400/", + "btn-startUpdateFS": "", + "btn-startUpdateFW": "" + }, + { + "global": 0, + "type": "Reading", + "subtype": "Variable", + "id": "tSetCH", + "needSave": 0, + "widget": "anydataDef", + "page": "Сохраненные значения", + "descr": "🌡️ Отопления", + "int": "0", + "val": "0.0", + "map": "1024,1024,1,100", + "plus": 0, + "multiply": 1, + "round": 0 + }, + { + "global": 0, + "type": "Reading", + "subtype": "Variable", + "id": "tSetDHW", + "needSave": 0, + "widget": "anydataDef", + "page": "Сохраненные значения", + "descr": "🌡️ ГВС", + "int": "0", + "val": "0.0", + "map": "1024,1024,1,100", + "plus": 0, + "multiply": 1, + "round": 0 + }, + { + "global": 0, + "type": "Reading", + "subtype": "Variable", + "id": "writtenHeatingEnabled", + "needSave": 0, + "widget": "anydataDef", + "page": "Сохраненные значения", + "descr": "Отопление (вкл/выкл)", + "int": "0", + "val": "0.0", + "map": "1024,1024,1,100", + "plus": 0, + "multiply": 1, + "round": 0 + }, + { + "global": 0, + "type": "Reading", + "subtype": "Variable", + "id": "writtenDHWEnabled", + "needSave": 0, + "widget": "anydataDef", + "page": "Сохраненные значения", + "descr": "ГВС (вкл/выкл)", + "int": "0", + "val": "0.0", + "map": "1024,1024,1,100", + "plus": 0, + "multiply": 1, + "round": 0 + }, + { + "global": 0, + "type": "Reading", + "subtype": "Variable", + "id": "setMaxModLevel", + "needSave": 0, + "widget": "anydataDef", + "page": "Сохраненные значения", + "descr": "Модуляция", + "int": "0", + "val": "0.0", + "map": "1024,1024,1,100", + "plus": 0, + "multiply": 1, + "round": 0 + }, + { + "global": 0, + "type": "Reading", + "subtype": "ecAdapter", + "id": "ecto", + "widget": "nil", + "page": "Котёл", + "descr": "Адаптер", + "int": "20", + "addr": "23", + "RX": "11", + "TX": "10", + "DIR_PIN": "0", + "baud": 19200, + "protocol": "SERIAL_8N1", + "debug": "0", + "UARTLine": "1" + } + ] +} + +scenario=>if onStart then { +tg.sendOftenMsg("E-bus http://" + getIP()); +if auto then {Thermo96.enable(1);} else {Thermo96.enable(0);} +} + +#включение/отключение отопления +if setStatusCH == 1 then ecto.setHeating(1); +if setStatusCH == 0 then ecto.setHeating(0); + +#включение/отключение ГВС +if setStatusDHW == 1 then ecto.setDHW(1); +if setStatusDHW == 0 then ecto.setDHW(0); + +#setStatusCH2 +if setStatusCH2 == 1 then ecto.setSecondaryCircuit(1); +if setStatusCH2 == 0 then ecto.setSecondaryCircuit(0); + +#Установка 🌡️ГВС +if setDHW then ecto.setTDHW (setDHW); +#Установка 🌡️ теплоносителя +if setTCH then ecto.setTCH (setTCH); + +#Включение термостата пид +if auto == 1 then Thermo96.enable(1); + +#Вылючение термостата пмд +if auto == 0 then { +Thermo96.enable(0); +tempOT = 20; +} + +if Thermo96 > 38 then tempOT = Thermo96 else tempOT = 20; + +#Сравнение температуры для предотвращения повторных данных +if tempOT != lasttemp then lasttemp = tempOT; +if lasttemp then ecto.setTCH (lasttemp); + +#считывание записанных в адаптер значений +if writtenHeatingEnabled then setStatusCH := writtenHeatingEnabled; +if writtenDHWEnabled then setStatusDHW := writtenDHWEnabled; +if tSetDHW then setDHW := tSetDHW; +if tSetCH then setTCH := tSetCH; + +#уведомления в телеграм +if codeError > 1 then tg.sendMsg("🚨 Ошибка котла F" + codeError); +if boilerConnected == 0 then tg.sendMsg("🚨 Отсутствует связь с котлом") else tg.sendMsg("Cвязь с котлом восстановлена") ; +if pressure < 0.9 then tg.sendMsg("💧 Низкое давление теплоносителя" + pressure); \ No newline at end of file diff --git a/src/modules/exec/EctoControlAdapter/modinfo.json b/src/modules/exec/EctoControlAdapter/modinfo.json index d02ab116..f7844a83 100644 --- a/src/modules/exec/EctoControlAdapter/modinfo.json +++ b/src/modules/exec/EctoControlAdapter/modinfo.json @@ -17,16 +17,17 @@ "DIR_PIN": 4, "baud": 19200, "protocol": "SERIAL_8N1", - "debug": 1 + "debug": 1, + "UARTLine": 1 } ], "about": { "authorName": "Mikhail Bubnov", "authorContact": "https://t.me/Mit4bmw", "authorGit": "https://github.com/Mit4el", - "specialThanks": "", + "specialThanks": "SeregaKi", "moduleName": "EctoControlAdapter", - "moduleVersion": "1.0", + "moduleVersion": "2.0", "usedRam": { "esp32_4mb": 15, "esp8266_4mb": 15 @@ -37,80 +38,100 @@ "title": "EctoControlAdapter", "moduleDesc": "Управление отопительным котлом через адаптер EctoControl по протоколам OpenTherm, eBUS, Navien. Посредством Modbus RTU. Разъем 4P4C: 1-Желтый(красный)+12V; 2-Белый-GND; 3-Зелёный-A; 4-Коричневый(Синий)-B", "propInfo": { - "addr": "Адрес slave, что бы узнать адрес - в конфиге адрес 0 и смотреть лог (требуется проверка)", + "addr": "Адрес slave, что бы узнать адрес - в конфиге адрес 0 и смотреть лог", "int": "Количество секунд между опросами датчика.", "RX": "Пин RX", "TX": "Пин TX", "DIR_PIN": "connect DR, RE pin of MAX485 to gpio, указать 0 если не нужен", "baud": "скорость Uart", "protocol": "Протокол Uart: SERIAL_8N1 или SERIAL_8N2", - "debug": "0 - отключить дебаг, 1 - включить вывод дебага, 2 - лог комманд, 3 - вывод modbus" + "debug": "0 - отключить дебаг, 1 - включить вывод дебага, 2 - лог комманд, 3 - вывод modbus", + "UARTLine": "Номер UART порта (0, 1 или 2), по умолчанию 1" }, "funcInfo": [ { - "name": "getModelVersion", - "descr": "Запрос модели и версии адаптера и бойлера", - "params": [] + "name": "setHeating", + "descr": "Включение/отключение контура отопления", + "params": ["1 - включить, 0 - отключить"] + }, + { + "name": "setDHW", + "descr": "Включение/отключение ГВС", + "params": ["1 - включить, 0 - отключить"] + }, + { + "name": "setSecondaryCircuit", + "descr": "Включение/отключение вторичного контура", + "params": ["1 - включить, 0 - отключить"] + }, + { + "name": "setBoilerStatus", + "descr": "Комбинированная установка статусов котла", + "params": [ + "отопление (1/0)", + "ГВС (1/0)", + "вторичный контур (1/0)" + ] }, { "name": "getBoilerInfo", - "descr": "Запрос состояния связи с котлом, типа адаптера и код перезагрузки адаптера", + "descr": "Запрос информации о котле и адаптере", "params": [] }, { - "name": "getBoilerStatus", - "descr": "Запрос состояния контуров котла и горелки", + "name": "getWrittenBoilerStatus", + "descr": "Запрос записанного статуса котла", "params": [] }, { - "name": "getCodeError", - "descr": "Код ошибки котла (основной). Зависит от марки и модели котла.", + "name": "getSettings", + "descr": "Запрос текущих настроек адаптера", "params": [] }, { - "name": "getCodeErrorExt", - "descr": "Код ошибки котла (дополнительный). Зависит от марки и модели котла.", - "params": [] + "name": "setTCH", + "descr": "Установка температуры отопления", + "params": ["температура (например 55.5)"] }, { - "name": "getFlagErrorOT", - "descr": "Стандартные флаги ошибок котла (только для котлов с интерфейсом OpenTherm)", - "params": [] + "name": "setTDHW", + "descr": "Установка температуры ГВС", + "params": ["температура (целое число)"] }, { - "name": "getFlowRate", - "descr": "Текущий расхода ГВС", - "params": [] + "name": "setSetTypeConnect", + "descr": "Установка типа подключения", + "params": ["0 - адаптер к котлу, 1 - котел к внешнему устройству"] }, { - "name": "getMaxSetCH", - "descr": "Верхний предел уставки теплоносителя", - "params": [] + "name": "setTSetCHFaultConn", + "descr": "Установка температуры отопления при аварии связи", + "params": ["температура (например 55.5)"] }, { - "name": "getMaxSetDHW", - "descr": "Верхний предел уставки ГВС", - "params": [] + "name": "setTSetMinCH", + "descr": "Установка минимальной температуры отопления", + "params": ["температура (например 35.0)"] }, { - "name": "getMinSetCH", - "descr": "Нижний предел уставки теплоносителя", - "params": [] + "name": "setTSetMaxCH", + "descr": "Установка максимальной температуры отопления", + "params": ["температура (например 85.0)"] }, { - "name": "getMinSetDHW", - "descr": "Нижний предел уставки ГВС", - "params": [] + "name": "setTSetMinDHW", + "descr": "Установка минимальной температуры ГВС", + "params": ["температура (целое число)"] }, { - "name": "getModLevel", - "descr": "Текущая модуляция горелки", - "params": [] + "name": "setTSetMaxDHW", + "descr": "Установка максимальной температуры ГВС", + "params": ["температура (целое число)"] }, { - "name": "getPressure", - "descr": "Текущее Давление в контуре", - "params": [] + "name": "setSetMaxModLevel", + "descr": "Установка максимального уровня модуляции", + "params": ["уровень (0-100)"] }, { "name": "getTempCH", @@ -122,81 +143,80 @@ "descr": "Текущая температура ГВС", "params": [] }, + { + "name": "getPressure", + "descr": "Давление в контуре", + "params": [] + }, + { + "name": "getFlowRate", + "descr": "Расход ГВС", + "params": [] + }, + { + "name": "getModLevel", + "descr": "Уровень модуляции горелки", + "params": [] + }, + { + "name": "getBoilerStatusRaw", + "descr": "Сырое значение регистра статуса", + "params": [] + }, { "name": "getTempOutside", - "descr": "Температура уличного датчика котла", + "descr": "Температура уличного датчика", "params": [] }, { - "name": "setTypeConnect", - "descr": "Установить тип внешних подключений (сохраняется в EPROM Адаптера): 0 - адаптер подключен к котлу, 1 - котел подключен к внешнему устройству (панель или перемычка)", - "params": ["Тип подключения"] - }, - { - "name": "setTCH", - "descr": "Уставка температуры теплоносителя (сохраняется в EPROM Адаптера)", - "params": ["температура передаётся до десятых градуса"] - }, - { - "name": "setTDHW", - "descr": "Уставка температуры ГВС (сохраняется в EPROM Адаптера)", - "params": ["температура передаётся до десятых градуса"] - }, - { - "name": "setTCHFaultConn", - "descr": "Уставка теплоносителя в аварийном режиме (сохраняется в EPROM Адаптера). Будет передана котлу в случае отсутствия связи адаптера с управляющим устройством", - "params": ["температура передаётся до десятых градуса"] - }, - { - "name": "setMinCH", - "descr": "Задать нижний предел уставки теплоносителя", - "params": ["температура от 0 до 100"] - }, - { - "name": "setMaxCH", - "descr": "Задать верхний предел уставки теплоносителя", - "params": ["температура от 0 до 100"] - }, - { - "name": "setMinDHW", - "descr": "Задать нижний предел уставки ГВС", - "params": ["температура от 0 до 100"] - }, - { - "name": "setMaxDHW", - "descr": "Задать верхний предел уставки ГВС", - "params": ["температура от 0 до 100"] - }, - - { - "name": "setMaxModLevel", - "descr": "Уставка максимальной модуляции горелки (сохраняется в EPROM Адаптера)", - "params": ["уровень модуляции 0-100%"] - }, - { - "name": "setStatusCH", - "descr": "Установить режим (Включить) контура отопления; 0 - отключен, 1 - включен", - "params": ["вкл/откл отопления"] - }, - { - "name": "setStatusDHW", - "descr": "Установить режим (Включить) ГВС; 0 - отключен, 1 - включен", - "params": ["вкл/откл ГВС"] - }, - { - "name": "setStatusCH2", - "descr": "Установить режим (Включить) второго контура отопления; 0 - отключен, 1 - включен. используется только некоторыми котлами с интерфейсом OpenTherm и может отвечать за активацию бойлера косвенного нагрева или встроенной функции ГВС", - "params": ["вкл/откл второго контура отопления"] - }, - { - "name": "lockOutReset", - "descr": "Сброс ошибок котла", + "name": "getFlagErrorOT", + "descr": "Флаги ошибок OpenTherm", "params": [] }, { - "name": "rebootAdapter", + "name": "getMinSetCH", + "descr": "Минимальная уставка теплоносителя", + "params": [] + }, + { + "name": "getMaxSetCH", + "descr": "Максимальная уставка теплоносителя", + "params": [] + }, + { + "name": "getMinSetDHW", + "descr": "Минимальная уставка ГВС", + "params": [] + }, + { + "name": "getMaxSetDHW", + "descr": "Максимальная уставка ГВС", + "params": [] + }, + { + "name": "getAdapterInfo", + "descr": "Информация об адаптере (регистр 0x0010)", + "params": [] + }, + { + "name": "getAdapterVersion", + "descr": "Версия адаптера (регистр 0x0011)", + "params": [] + }, + { + "name": "getUptime", + "descr": "Время работы адаптера (регистр 0x0012)", + "params": [] + }, + { + "name": "reboot", "descr": "Перезагрузка адаптера", "params": [] + }, + { + "name": "resetErrors", + "descr": "Сброс ошибок котла", + "params": [] } ] }, diff --git a/src/modules/exec/WakeOnLanModule/WakeOnLanModule.cpp b/src/modules/exec/WakeOnLanModule/WakeOnLanModule.cpp new file mode 100644 index 00000000..1fe1435f --- /dev/null +++ b/src/modules/exec/WakeOnLanModule/WakeOnLanModule.cpp @@ -0,0 +1,162 @@ +// Licensed under the Cooperative Non-Violent Public License (CNPL) +// See: https://github.com/CHE77/IoTManager-Modules/blob/main/LICENSE + +#include "Global.h" +#include "classes/IoTItem.h" + +#include +#include + +WiFiUDP UDP; // Создаем объект WiFiUDP +WakeOnLan WOL(UDP); // Используем библиотеку WakeOnLan + +class WakeOnLanModule : public IoTItem +{ +private: + String _macAddress; // MAC-адрес для пробуждения + String _SecureOn = ""; + int _port = 9; + int _repeats = 3; + bool isInitiated = false; + +public: + WakeOnLanModule(String parameters) : IoTItem(parameters) + { + jsonRead(parameters, "MAC", _macAddress); // Чтение MAC-адреса из параметров + _macAddress.replace("\"", ""); + if (!isValidMacAddress(_macAddress)) + { + SerialPrint("E", "WakeOnLan", "Settings > MAC = " + _macAddress + " is not valid", _id); + _macAddress = ""; + } + + jsonRead(parameters, "SecureOn", _SecureOn); // Чтение MAC-адреса из параметров + _SecureOn.replace("\"", ""); + if (!_SecureOn.isEmpty() && !isValidMacAddress(_SecureOn)) + { + SerialPrint("E", "WakeOnLan", "Settings > SecureOn = " + _SecureOn + " is not valid", _id); + _SecureOn = ""; + } + + jsonRead(parameters, "port", _port); // Чтение MAC-адреса из параметров + + jsonRead(parameters, "repeats", _repeats); // Чтение MAC-адреса из параметров + + WOL.setRepeat(_repeats, 100); // Repeat the packet three times with 100ms delay between + + init(); + + } + + void init() + { + if (isNetworkActive()) + { // Рассчитываем broadcast-адрес + IPAddress broadcastAddress = WOL.calculateBroadcastAddress(WiFi.localIP(), WiFi.subnetMask()); + WOL.setBroadcastAddress(broadcastAddress); // Устанавливаем broadcast-адрес + isInitiated = true; + } + } + + bool isValidMacAddress(String mac) + { + // Удаляем двоеточия, если есть + mac.replace(":", ""); + mac.toUpperCase(); + + // Должно быть ровно 12 символов (6 байт в hex) + if (mac.length() != 12) + return false; + + // Проверка, что все символы — это HEX (0-9, A-F) + for (int i = 0; i < 12; i++) + { + char c = mac.charAt(i); + if (!isHexadecimalDigit(c)) + return false; + } + + return true; + } + + void setValue(const IoTValue &Value, bool genEvent = true) + { + value = Value; + + if (value.valD == 1 && isNetworkActive()) + { + if (!isInitiated) init(); + + if (!_macAddress.isEmpty() && !_SecureOn.isEmpty()) + { + WOL.sendSecureMagicPacket(_macAddress, _SecureOn, _port); + SerialPrint("I", "WakeOnLan", "setValue, _SecureOn = " + _SecureOn, _id); + } + else if (!_macAddress.isEmpty()) + { + WOL.sendMagicPacket(_macAddress, _port); // Отправка Magic Packet + } + else + { + SerialPrint("E", "WakeOnLan", "Settings > MAC and/or SecureOn - not set or not valid", _id); + } + } + regEvent((String)(int)value.valD, "WakeOnLan", false, genEvent); + } + + IoTValue execute(String command, std::vector ¶m) + { + if (!isInitiated) init(); + + if (command == "mac") + { + String macTarget = ""; + + if (param.size() == 1 && isValidMacAddress(param[0].valS)) + { + macTarget = param[0].valS; + WOL.sendMagicPacket(macTarget); // Отправка Magic Packet + } + else if (param.size() == 2 && isValidMacAddress(param[0].valS)) + { + macTarget = param[0].valS; + int portNum = param[1].valD; + WOL.sendMagicPacket(macTarget, portNum); // Отправка Magic Packet + } + else if (param.size() == 3 && isValidMacAddress(param[0].valS) && isValidMacAddress(param[1].valS)) + { + macTarget = param[0].valS; + String secureOn = param[1].valS; + int portNum = param[2].valD; + WOL.sendSecureMagicPacket(macTarget, secureOn, portNum); + } + else + { + SerialPrint("E", "WakeOnLan", "MAC and/or SecureOn - not valid", _id); + return {}; + } + + SerialPrint("I", "WakeOnLan", "execute, Magic Packet sent to " + macTarget, _id); + } + + return {}; // команда поддерживает возвращаемое значения. Т.е. по итогу выполнения команды или общения с внешней системой, можно вернуть значение в сценарий для дальнейшей обработки + } + /* + String getValue() { + return (String)(int)value.valD; + } + */ + void doByInterval() {} +}; + +void *getAPI_WakeOnLanModule(String subtype, String param) +{ + if (subtype == F("WakeOnLanModule")) + { + return new WakeOnLanModule(param); // Используем новое имя класса + } + else + { + return nullptr; + } +} \ No newline at end of file diff --git a/src/modules/exec/WakeOnLanModule/export.json b/src/modules/exec/WakeOnLanModule/export.json new file mode 100644 index 00000000..9eec1b0f --- /dev/null +++ b/src/modules/exec/WakeOnLanModule/export.json @@ -0,0 +1,295 @@ +{ + "mark": "iotm", + "config": [ + { + "global": 0, + "type": "Reading", + "subtype": "Variable", + "id": "ipNet", + "needSave": 0, + "widget": "anydataDef", + "page": "Network", + "descr": "IP", + "int": "0", + "val": "0.0", + "map": "", + "plus": 0, + "multiply": 1, + "round": 0, + "moduleName": "Variable" + }, + { + "global": 0, + "type": "Writing", + "subtype": "Cron", + "id": "cronWiFi", + "widget": "anydataDef", + "page": "Timers", + "descr": " cronWiFi", + "int": 1, + "val": "0 * * * * *", + "formatNextAlarm": "%H:%M:%S", + "needSave": 0, + "moduleName": "Cron" + }, + { + "global": 0, + "type": "Reading", + "subtype": "VButton", + "id": "wifiConn", + "needSave": 0, + "widget": "toggle", + "page": "Network", + "descr": " Wi-Fi Connection", + "int": "0", + "val": "0", + "moduleName": "VButton" + }, + { + "global": 0, + "type": "Reading", + "subtype": "Variable", + "id": "noWiFiCounter", + "needSave": 0, + "widget": "anydataDef", + "page": "Network", + "descr": " noWiFi Counter", + "int": "0", + "val": "0.0", + "map": "", + "plus": 0, + "multiply": 1, + "round": 0, + "moduleName": "Variable" + }, + { + "global": 0, + "type": "Reading", + "subtype": "Variable", + "id": "maxNoWiFi", + "needSave": 0, + "widget": "inputDgt", + "page": "Network", + "descr": " maxNoWiFi to reboot", + "int": "0", + "val": "100", + "map": "", + "plus": 0, + "multiply": 1, + "round": 0, + "moduleName": "Variable" + }, + { + "global": 0, + "type": "Writing", + "subtype": "Cron", + "id": "cronPing", + "widget": "anydataDef", + "page": "Timers", + "descr": " cronPing", + "int": 1, + "val": "2 * * * * *", + "formatNextAlarm": "%H:%M:%S", + "needSave": 0, + "moduleName": "Cron" + }, + { + "global": 0, + "type": "Reading", + "subtype": "Ping", + "id": "pingN", + "needSave": 0, + "widget": "nil", + "page": "Network", + "descr": "Internet", + "ip": "8.8.8.8", + "timeout": 5, + "interval": "1", + "data_size": 0, + "count": 0, + "tos": 0, + "moduleName": "Ping" + }, + { + "global": 0, + "type": "Reading", + "subtype": "Variable", + "id": "noInternetCounter", + "needSave": 0, + "widget": "anydataDef", + "page": "Network", + "descr": "noInternet Counter", + "int": "0", + "val": "0.0", + "map": "1024,1024,1,100", + "plus": 0, + "multiply": 1, + "round": 0, + "moduleName": "Variable" + }, + { + "global": 0, + "type": "Reading", + "subtype": "VButton", + "id": "internetConn", + "needSave": 0, + "widget": "toggle", + "page": "Network", + "descr": "Internet Connection", + "int": "0", + "val": "0", + "moduleName": "VButton" + }, + { + "global": 0, + "type": "Reading", + "subtype": "Variable", + "id": "routerFlag0", + "needSave": 0, + "widget": "nil", + "page": "nil", + "descr": "routerFlag0", + "int": "0", + "val": "0.0", + "map": "", + "plus": 0, + "multiply": 1, + "round": 0, + "moduleName": "Variable" + }, + { + "global": 0, + "type": "Reading", + "subtype": "Variable", + "id": "routerFlag1", + "needSave": 0, + "widget": "nil", + "page": "nil", + "descr": "routerFlag1", + "int": "0", + "val": "0.0", + "map": "", + "plus": 0, + "multiply": 1, + "round": 0, + "moduleName": "Variable" + }, + { + "global": 0, + "type": "Reading", + "subtype": "Variable", + "id": "routerFlag3", + "needSave": 0, + "widget": "nil", + "page": "nil", + "descr": "routerFlag3", + "int": "0", + "val": "0.0", + "map": "", + "plus": 0, + "multiply": 1, + "round": 0, + "moduleName": "Variable" + }, + { + "global": 0, + "type": "Reading", + "subtype": "VButton", + "id": "vbtnWOLbook", + "needSave": 0, + "widget": "toggle", + "page": "WakeOnLAN", + "descr": "Wake Notebook scenario", + "int": "0", + "val": "0", + "moduleName": "VButton", + "show": true + }, + { + "global": 0, + "type": "Reading", + "subtype": "WakeOnLanModule", + "id": "wakeLanBook", + "widget": "toggle", + "page": "WakeOnLAN", + "descr": "Wake Notebook w.settings", + "MAC": "\"A0:AD:A8:A2:AF:E2\"", + "SecureOn": "", + "port": 9, + "repeats": 3, + "moduleName": "WakeOnLanModule", + "show": true + }, + { + "global": 0, + "type": "Reading", + "subtype": "Variable", + "id": "macInput", + "needSave": 0, + "widget": "inputTxt", + "page": "WakeOnLAN", + "descr": "MAC Input", + "int": "0", + "val": "A0:AD:A8:A2:AF:E2", + "moduleName": "Variable" + }, + { + "global": 0, + "type": "Reading", + "subtype": "Variable", + "id": "bootDateNet", + "needSave": 0, + "widget": "anydataDef", + "page": "Time", + "descr": " Boot Date", + "int": "0", + "val": "0.0", + "map": "", + "plus": 0, + "multiply": 1, + "round": 0, + "moduleName": "Variable" + } + ] +} + +scenario=>if onStart then { +ipNet = getIP() +pingN.ping("8.8.8.8") +bootDateNet=getTime() +} + + +#book + if vbtnWOLbook == 1 then { +wakeLanBook.mac(macInput) + vbtnWOLbook = 0 +} + + +###################### +if cronWiFi then ipNet = getIP() + +if ipNet == "(IP unset)" | ipNet == "192.168.4.1" then { +wifiConn = 0 +noWiFiCounter = noWiFiCounter +1 +}else { +wifiConn = 1 +noWiFiCounter = 0 +} + +if noWiFiCounter > maxNoWiFi then reboot() + +################### +if cronPing then { +pingN.ping("8.8.8.8") +} + +if pingN == 0 then { +internetConn = 0 +noInternetCounter = noInternetCounter +1 +}else { +internetConn = 1 +#noInternetCounter = 0 +noInternetCounter = noInternetCounter +1 +} diff --git a/src/modules/exec/WakeOnLanModule/modinfo.json b/src/modules/exec/WakeOnLanModule/modinfo.json new file mode 100644 index 00000000..d612c7f9 --- /dev/null +++ b/src/modules/exec/WakeOnLanModule/modinfo.json @@ -0,0 +1,48 @@ +{ + "menuSection": "executive_devices", + "configItem": [ + { + "global": 0, + "name": "WakeOnLan", + "type": "Reading", + "subtype": "WakeOnLanModule", + "id": "wakeLan", + "widget": "toggle", + "page": "Кнопки", + "descr": "Wake PC", + "MAC": "A8:A3:A5:A5:52:42", + "SecureOn": "", + "port": 9, + "repeats": 3 + } + ], + "about": { + "authorName": "Alex", + "authorContact": "https://t.me/cmche", + "authorGit": "https://github.com/CHE77/IoTManager-Modules", + "specialThanks": "", + "moduleName": "WakeOnLanModule", + "moduleVersion": "1.0", + "usedRam": { + "esp32_4mb": 15, + "esp8266_4mb": 15 + }, + "title": "WakeOnLan", + "moduleDesc": "Пробуждение компютера по сети", + "license": "Cooperative Non-Violent Public License (CNPL)", + "propInfo": { + "MAC": "MAC адрес сетевой карты", + "SecureOn": "Секретный ключ в таком же виде как и MAC адрес. (необязательно)", + "port": "порт - по умолчнияю - 9", + "repeats": "Количество повторов команд пробужния - для надежности" + } + }, + "defActive": false, + "usedLibs": { + "esp32*": ["a7md0/WakeOnLan @ ^1.1.7"], + "esp82*": ["a7md0/WakeOnLan @ ^1.1.7"], + "bk72*": ["a7md0/WakeOnLan @ ^1.1.7"] + } +} + + diff --git a/src/modules/sensors/NoiseAdc/NoiseAdc.cpp b/src/modules/sensors/NoiseAdc/NoiseAdc.cpp new file mode 100644 index 00000000..4cdd8c0e --- /dev/null +++ b/src/modules/sensors/NoiseAdc/NoiseAdc.cpp @@ -0,0 +1,359 @@ +// Licensed under the Cooperative Non-Violent Public License (CNPL) +// See: https://github.com/CHE77/IoTManager-Modules/blob/main/LICENSE + +#include "Global.h" +#include "classes/IoTItem.h" + +extern IoTGpio IoTgpio; + +// Это файл сенсора, в нем осуществляется чтение сенсора. +// для добавления сенсора вам нужно скопировать этот файл и заменить в нем текст NoiseAdc на название вашего сенсора +// Название должно быть уникальным, коротким и отражать суть сенсора. + +// ребенок - родитель +class NoiseAdc : public IoTItem +{ +private: + //======================================================================================================= + // Секция переменных. + // Это секция где Вы можете объявлять переменные и объекты arduino библиотек, что бы + // впоследствии использовать их в loop и setup + +#ifdef ESP8266 + float adcRange = 1023.0; + const int maxSamples = 100; + int samples[100]; + float deltas[100]; // отклонения от среднего + +#else + float adcRange = 4095.0; + const int maxSamples = 200; + int samples[200]; + float deltas[200]; // отклонения от среднего +#endif + + unsigned int _pin; + unsigned int _steps; + unsigned int _period = 0; + unsigned long _lastSoundingMillis = 0; + + int sampleIndex = 0; + + float _referenceVoltage = 0.02; // калибровочный "тихий" уровень + float vcc = 3.3; + + String _parameter = ""; + + float mean = 0; + float minVal = 0; + float maxVal = 0; + float rms = 0; + float peak = 0; + float median = 0; + float minValMean = 0; + float maxValMean = 0; + float peakToPeak = 0; + float peakVoltage = 0; + float db = 0; + + bool _debug = false; + +public: + //======================================================================================================= + // setup() + // это аналог setup из arduino. Здесь вы можете выполнять методы инициализации сенсора. + // Такие как ...begin и подставлять в них параметры полученные из web интерфейса. + // Все параметры хранятся в перемененной parameters, вы можете прочитать любой параметр используя jsonRead функции: + // jsonReadStr, jsonReadBool, jsonReadInt + NoiseAdc(String parameters) : IoTItem(parameters) + { + _pin = jsonReadInt(parameters, "pin"); + _steps = jsonReadInt(parameters, "steps"); + if (_steps > maxSamples) + _steps = maxSamples; + + if (_steps < 10) + _steps = 10; + _period = _interval / _steps; + if (_debug) + SerialPrint("i", F("NoiseAdc"), "_period = " + String(_period)); + + jsonRead(parameters, F("refVoltage"), _referenceVoltage, false); + if (_referenceVoltage == 0) + _referenceVoltage = 0.01; + + _parameter = jsonReadStr(parameters, "parameter"); + jsonRead(parameters, F("debug"), _debug); + } + + //======================================================================================================= + + void doByInterval() + { + float result = analyzeSamples(_parameter); + + if (result) + { + value.valD = result; + regEvent(value.valD, "NoiseAdc", _debug, true); // обязательный вызов хотяб один + } + } + + //======================================================================================================= + // loop() + // полный аналог loop() из arduino. Нужно помнить, что все модули имеют равный поочередный доступ к центральному loop(), поэтому, необходимо следить + // за задержками в алгоритме и не создавать пауз. Кроме того, данная версия перегружает родительскую, поэтому doByInterval() отключается, если + // не повторить механизм расчета интервалов. + void loop() + { + + if (millis() > _lastSoundingMillis + _period && sampleIndex < maxSamples) + { + _lastSoundingMillis = millis(); + samples[sampleIndex++] = IoTgpio.analogRead(_pin); + } + + IoTItem::loop(); + } + + void onModuleOrder(String &key, String &value) + { + if (key == "setRefVoltage") + { + _referenceVoltage = analyzeSamples("peakVoltage"); + SerialPrint("i", F("NoiseAdc"), "User run calibration referenceVoltage " + String(_referenceVoltage)); + // TODO wtitejson to config.json????? + } + } + + IoTValue execute(String command, std::vector ¶m) + { + if (command == "parameter") + { + if (param.size() == 1 && !param[0].isDecimal) + { + + String parameter = param[0].valS.c_str(); + + float output = 0; + + if (parameter == "mean") + { + output = mean; + } + else if (parameter == "minVal") + { + output = minVal; + } + else if (parameter == "maxVal") + { + output = maxVal; + } + else if (parameter == "RMS") + { + output = rms; + } + else if (parameter == "median") + { + output = median; + } + else if (parameter == "minValMean") + { + output = minValMean; + } + else if (parameter == "maxValMean") + { + output = maxValMean; + } + else if (parameter == "peak") + { + output = peak; + } + else if (parameter == "peakToPeak") + { + output = peakToPeak; + } + else if (parameter == "peakVoltage") + { + output = peakVoltage; + } + else if (parameter == "db") + { + output = db; + } + else + { + } + + if (_debug) + SerialPrint("i", F("NoiseAdc"), "execute parameter " + String(parameter) + " = " + String(output)); + + if (output) + { + IoTValue valTmp; + valTmp.isDecimal = true; + valTmp.valD = output; + return valTmp; + } + } + } + + return {}; // команда поддерживает возвращаемое значения. Т.е. по итогу выполнения команды или общения с внешней системой, можно вернуть значение в сценарий для дальнейшей обработки + } + + float analyzeSamples(String parameter) + { + if (sampleIndex < 5) + { + if (_debug) + SerialPrint("i", F("NoiseAdc"), "Нет данных для анализа"); + return 0; + } + + minVal = samples[0]; + maxVal = samples[0]; + float sum = 0; + for (int i = 0; i < sampleIndex; i++) + { + if (samples[i] < minVal) + minVal = samples[i]; + if (samples[i] > maxVal) + maxVal = samples[i]; + sum += samples[i]; + } + + mean = sum / sampleIndex; + + for (int i = 0; i < sampleIndex; i++) + { + deltas[i] = samples[i] - mean; + } + + rms = 0; + + minValMean = deltas[0]; + maxValMean = deltas[0]; + + peak = 0; + + for (int i = 0; i < sampleIndex; i++) + { + rms += deltas[i] * deltas[i]; + + if (deltas[i] < minValMean) + minValMean = deltas[i]; + if (deltas[i] > maxValMean) + maxValMean = deltas[i]; + + if (abs(deltas[i] > peak)) + peak = abs(deltas[i]); + } + + rms = sqrt(rms / sampleIndex); + + peakToPeak = maxValMean - minValMean; + + float sorted[maxSamples]; + memcpy(sorted, deltas, sizeof(float) * sampleIndex); + std::sort(sorted, sorted + sampleIndex); + median = (sampleIndex % 2 == 0) ? (sorted[sampleIndex / 2 - 1] + sorted[sampleIndex / 2]) / 2.0 : sorted[sampleIndex / 2]; + + peakVoltage = peakToPeak * vcc / adcRange; + db = 20.0 * log10(peakVoltage / _referenceVoltage); + if (peakVoltage < _referenceVoltage) + db = 0; + if (_debug) + { + Serial.println("== Анализ завершен =="); + Serial.print("Сэмплов: "); + Serial.println(sampleIndex); + Serial.print("Среднее (DC): "); + Serial.println(mean, 2); + Serial.print("Мин/Макс (Aбс): "); + Serial.print(minVal, 2); + Serial.print(" / "); + Serial.println(maxVal, 2); + Serial.print("RMS (AC): "); + Serial.println(rms, 2); + Serial.print("Медиана (AC): "); + Serial.println(median, 2); + Serial.print("Мин/Макс (AC): "); + Serial.print(minValMean, 2); + Serial.print(" / "); + Serial.println(maxValMean, 2); + Serial.print("Абсолютный пик (AC): "); + Serial.println(peak, 2); + Serial.print("Размах (AC): "); + Serial.println(peakToPeak, 2); + Serial.print("Размах в вольтах: "); + Serial.println(peakVoltage, 4); + Serial.print("Уровень звука ≈ dB SPL: "); + Serial.println(db, 1); + } + + sampleIndex = 0; + + if (parameter == "mean") + { + return mean; + } + else if (parameter == "minVal") + { + return minVal; + } + else if (parameter == "maxVal") + { + return maxVal; + } + else if (parameter == "RMS") + { + return rms; + } + else if (parameter == "median") + { + return median; + } + else if (parameter == "minValMean") + { + return minValMean; + } + else if (parameter == "maxValMean") + { + return maxValMean; + } + else if (parameter == "peak") + { + return peak; + } + else if (parameter == "peakToPeak") + { + return peakToPeak; + } + else if (parameter == "peakVoltage") + { + return peakVoltage; + } + else if (parameter == "db") + { + return db; + } + else + { + return 0; + } + } + + ~NoiseAdc() {}; +}; + +void *getAPI_NoiseAdc(String subtype, String param) +{ + if (subtype == F("NoiseAdc")) + { + return new NoiseAdc(param); + } + else + { + return nullptr; + } +} diff --git a/src/modules/sensors/NoiseAdc/export.json b/src/modules/sensors/NoiseAdc/export.json new file mode 100644 index 00000000..9806e3ce --- /dev/null +++ b/src/modules/sensors/NoiseAdc/export.json @@ -0,0 +1,89 @@ +{ + "mark": "iotm", + "config": [ + { + "global": 0, + "type": "Writing", + "subtype": "TelegramLT", + "id": "tg0", + "widget": "", + "page": "", + "descr": "", + "token": "1262564256457:AAGQJ6LvZ-dOGFvaerhbserhdtbzgdndaY", + "chatID": "49999999" + }, + { + "global": 0, + "type": "Reading", + "subtype": "Variable", + "id": "valueTreashhold", + "needSave": "1", + "widget": "inputDgt", + "page": "Сенсоры", + "descr": "ADC Threshold", + "int": "0", + "val": "4095", + "map": "", + "plus": 0, + "multiply": 1, + "round": 0 + }, + { + "global": 0, + "type": "Reading", + "subtype": "VButton", + "id": "vbtnListen", + "needSave": "1", + "widget": "toggle", + "page": "Сенсоры", + "descr": "Tg Alerts", + "int": "0", + "val": "0", + "moduleName": "VButton" + }, + { + "global": 0, + "type": "Writing", + "subtype": "Loging", + "id": "logNoise", + "widget": "chart2", + "page": "Графики", + "descr": "Noise", + "int": 0, + "daysSave": 5, + "daysShow": 0, + "points": 300, + "moduleName": "Loging" + }, + { + "global": 0, + "type": "Reading", + "subtype": "NoiseAdc", + "id": "noiseADC", + "widget": "anydataDef", + "page": "Сенсоры", + "descr": "NoiseAdc", + "map": "", + "plus": 0, + "multiply": 1, + "round": 0, + "pin": "0", + "int": 10, + "avgSteps": "500", + "parameter": "peakToPeak", + "debug": 1, + "refVoltage": "0.02", + "btn-setRefVoltage": "nil", + "moduleName": "NoiseAdc" + } + ] +} + +scenario=>if onStart then tg0.sendMsg("ESP Noise reboot. IP " + getIP() ) + +if noiseADC > valueTreashhold & vbtnListen then {tg0.sendMsg("Noise level = " + noiseADC) +} + +if noiseADC then = noiseADC + + diff --git a/src/modules/sensors/NoiseAdc/modinfo.json b/src/modules/sensors/NoiseAdc/modinfo.json new file mode 100644 index 00000000..ad70d38f --- /dev/null +++ b/src/modules/sensors/NoiseAdc/modinfo.json @@ -0,0 +1,70 @@ +{ + "menuSection": "sensors", + "configItem": [ + { + "global": 0, + "name": "Анализ шума ADC", + "type": "Reading", + "subtype": "NoiseAdc", + "id": "noise", + "widget": "anydataDef", + "page": "Сенсоры", + "descr": "NoiseAdc", + "map": "", + "plus": 0, + "multiply": 1, + "round": 0, + "pin": 0, + "int": 10, + "steps": 100, + "parameter": "peakToPeak", + "debug": 1, + "refVoltage": 0.02, + "btn-setRefVoltage": "nil" + } + ], + "about": { + "authorName": "Alex", + "authorContact": "https://t.me/cmche", + "authorGit": "https://github.com/CHE77/IoTManager-Modules", + "exampleURL": "https://iotmanager.org/wiki", + "specialThanks": "", + "moduleName": "NoiseAdc", + "moduleVersion": "1.0", + "usedRam": { + "esp32_4mb": 10, + "esp8266_4mb": 3 + }, + "title": "Анализ шума ADC", + "moduleDesc": "Позволяет получить различные характеристики сигнала на аналоговом GPIO. В каждый промежуток времени int делает avgSteps измерений, орпеделяет характоистики ряда и выводит один параметров.", + "license": "Cooperative Non-Violent Public License (CNPL)", + "propInfo": { + "pin": "Аналоговый GPIO номер, к которому подключен датчик.", + "int": "Длительность опроса датчика и вывод показаний", + "steps": "Количество считываний в период опроса. Минимальное - 10. Максимальное - 200(esp8266), 1000(esp32). По факту оно будет несколько меньше.", + "parameter": { + "mean": "Среднее", + "minVal": "Минимальное значение ADC", + "maxVal": "Максимальное значение ADC", + "RMS": "Среднеквадратичное отклонение (от среднего)", + "median": "Медиана (от среднего)", + "minValMean": "Минимальное значение (от среднего)", + "maxValMean": "Максимальное значение (от среднего)", + "peak": "Абсолютный пик (от среднего)", + "peakToPeak": "Размах сигнала относительно среднего (AC) в отсчётах ADC", + "peakVoltage": "Максимальная амплитуда колебаний сигнала", + "db": "Приближённая оценка уровня звука в децибелах " + }, + + "debug": "Вывод отладочной информации", + "refVoltage": "Базовый уровень для сигнала (в тишине) в Вольтах", + "btn-setRefVoltage": "Замер базового уровня в тишине и вывод его в сериал" + } + }, + "defActive": false, + "usedLibs": { + "esp32*": [], + "esp82*": [], + "bk72*": [] + } +} \ No newline at end of file diff --git a/src/modules/sensors/Presence/Presence.cpp b/src/modules/sensors/Presence/Presence.cpp new file mode 100644 index 00000000..4b05df52 --- /dev/null +++ b/src/modules/sensors/Presence/Presence.cpp @@ -0,0 +1,579 @@ +// Licensed under the Cooperative Non-Violent Public License (CNPL) +// See: https://github.com/CHE77/IoTManager-Modules/blob/main/LICENSE + + +#define MQTT_MAX_PACKET_SIZE 512 // или 1024 +#include "Global.h" +#include "classes/IoTItem.h" + +#include +#define EARTH_RADIUS_KM 6371.0 // Радиус Земли в километрах + +#include "NTP.h" +#include // чтобы знать тип +extern PubSubClient mqtt; // объявляем глобальный объект + +class Presence : public IoTItem +{ +private: + String _MAC; + String _parameter; + IoTItem *tmp; + int _minutesPassed = 0; + String json = "{}"; + int orange = 0; + int red = 0; + int offline = 0; + bool dataFromNode = false; + String _topic = ""; + bool _isJson; + bool _ticker = true; + bool _debug = false; + bool sendOk = false; + float _lat_A = 0; + float _lon_A = 0; + + + struct PresenceData + { + String chargingState; + String plugState; + String connectedWifi; + String geoLocation; + float lat = 0.0; + float lon = 0.0; + unsigned long geoTimestamp = 0; + String geoTime; + String deviceName; + int batteryLevel = -1; + unsigned long currentTimestamp = 0; + String currentTime; + unsigned long nextScheduledTimestamp = 0; + String nextScheduledTime; + unsigned long nextAlarmclockTimestamp = 0; + String nextAlarmclockTime; + std::vector conditionContent; + String conditionContentString; + }; + PresenceData pdata; + +public: + Presence(String parameters) : IoTItem(parameters) + { + _parameter = jsonReadStr(parameters, "parameter"); + jsonRead(parameters, F("orange"), orange); + jsonRead(parameters, F("red"), red); + jsonRead(parameters, F("offline"), offline); + _topic = jsonReadStr(parameters, "topic"); + if (_debug) + SerialPrint("i", "Presence topic : ", _topic); + jsonRead(parameters, F("isJson"), _isJson); + // jsonRead(parameters, "addPrefix", _addPrefix); + jsonRead(parameters, F("Lat. A"), _lat_A); + jsonRead(parameters, F("Long. A"), _lon_A); + jsonRead(parameters, F("ticker"), _ticker); + jsonRead(parameters, F("debug"), _debug); + dataFromNode = false; + if (mqttIsConnect()) + { + mqtt.setBufferSize(512); + sendOk = true; + mqttSubscribeExternal(_topic); + } + } + + char *TimeToString(unsigned long t) + { + static char str[12]; + long h = t / 3600; + t = t % 3600; + int m = t / 60; + int s = t % 60; + sprintf(str, "%02ld:%02d:%02d", h, m, s); + return str; + } + + double toRadians(double degrees) + { + return degrees * M_PI / 180.0; + } + + double toDegrees(double radians) + { + return radians * 180.0 / M_PI; + } + + // Возвращает пеленг в градусах: от 0 до 360 + double calculateInitialBearing(double lat1, double lon1, double lat2, double lon2) + { + lat1 = toRadians(lat1); + lon1 = toRadians(lon1); + lat2 = toRadians(lat2); + lon2 = toRadians(lon2); + + double deltaLon = lon2 - lon1; + + double y = sin(deltaLon) * cos(lat2); + double x = cos(lat1) * sin(lat2) - + sin(lat1) * cos(lat2) * cos(deltaLon); + + double bearing = atan2(y, x); + bearing = toDegrees(bearing); + + // Приводим к диапазону 0–360 + return fmod((bearing + 360.0), 360.0); + } + + // lat и lon — в градусах + double haversineDistance(double lat1, double lon1, double lat2, double lon2) + { + double dLat = toRadians(lat2 - lat1); + double dLon = toRadians(lon2 - lon1); + + lat1 = toRadians(lat1); + lat2 = toRadians(lat2); + + double a = sin(dLat / 2) * sin(dLat / 2) + + cos(lat1) * cos(lat2) * + sin(dLon / 2) * sin(dLon / 2); + + double c = 2 * atan2(sqrt(a), sqrt(1 - a)); + + return EARTH_RADIUS_KM * c * 1000.0; + } + + void onMqttRecive(String &topic, String &msg) + { + Serial.printf("[MQTT] Topic: %s\nPayload size: %d bytes\n", topic.c_str(), msg.length()); + msg.trim(); // Убираем пробелы и переносы строк + + if (msg.indexOf("HELLO") == -1) + { + if (_debug) + SerialPrint("i", "Presence HELLO", " _1d: " + _id + " topic: " + topic + " msg: " + msg); + String dev = selectToMarkerLast(topic, "/"); + dev.toUpperCase(); + dev.replace(":", ""); + if (_topic != topic) + { + if (_debug) + { + SerialPrint("i", "Presence", topic + " not equal: " + _topic + " msg: " + msg); + } + return; + } + + if (_isJson) + { + DynamicJsonDocument doc(JSON_BUFFER_SIZE); + DeserializationError err = deserializeJson(doc, msg); + + if (err) + { + SerialPrint("E", F("Presence"), err.f_str()); + return; + } + + JsonObject obj = doc.as(); + + if (obj.containsKey("chargingState")) + { + pdata.chargingState = obj["chargingState"].as(); + } + if (obj.containsKey("plugState")) + { + pdata.plugState = obj["plugState"].as(); + } + + if (obj.containsKey("connectedWifi")) + pdata.connectedWifi = obj["connectedWifi"].as(); + if (obj.containsKey("geoLocation")) + { + pdata.geoLocation = obj["geoLocation"].as(); + // Разбор геолокации после получения + parseGeo(pdata.geoLocation); + pdata.geoTime = getDateTimeDotFormatedFromUnix(pdata.geoTimestamp); + SerialPrint("i", "Presence", "GeoTime : " + pdata.geoTime); + } + + if (obj.containsKey("deviceName")) + pdata.deviceName = obj["deviceName"].as(); + if (obj.containsKey("batteryLevel")) + pdata.batteryLevel = obj["batteryLevel"].as(); + if (obj.containsKey("currentTimestamp")) + { + pdata.currentTimestamp = obj["currentTimestamp"].as(); + pdata.currentTime = getDateTimeDotFormatedFromUnix(pdata.currentTimestamp); + } + + if (obj.containsKey("nextScheduledTimestamp")) + { + pdata.nextScheduledTimestamp = obj["nextScheduledTimestamp"].as(); + pdata.nextScheduledTime = getDateTimeDotFormatedFromUnix(pdata.nextScheduledTimestamp); + } + + if (obj.containsKey("nextAlarmclockTimestamp")) + { + pdata.nextAlarmclockTimestamp = obj["nextAlarmclockTimestamp"].as(); + pdata.nextAlarmclockTime = getDateTimeDotFormatedFromUnix(pdata.nextAlarmclockTimestamp); + } + + if (obj.containsKey("conditionContent")) + { + JsonArray arr = obj["conditionContent"].as(); + pdata.conditionContent.clear(); + for (String s : arr) + { + pdata.conditionContent.push_back(s); + } + + String conditionStr; + for (size_t i = 0; i < pdata.conditionContent.size(); ++i) + { + conditionStr += pdata.conditionContent[i]; + if (i < pdata.conditionContent.size() - 1) + conditionStr += ", "; + } + pdata.conditionContentString = conditionStr; + } + + dataFromNode = true; + _minutesPassed = 0; + + String sensorVal; + + if (_parameter == "latitude") + { + value.isDecimal = true; + value.valD = pdata.lat; + } + else if (_parameter == "longitude") + { + value.isDecimal = true; + value.valD = pdata.lon; + } + else if (_parameter == "azimuth") + { + value.isDecimal = true; + value.valD = calculateInitialBearing(_lat_A, _lon_A, pdata.lat, pdata.lon); + } + else if (_parameter == "distance") + { + value.isDecimal = true; + value.valD = haversineDistance(_lat_A, _lon_A, pdata.lat, pdata.lon); + } + else if (_parameter == "batteryLevel") + { + value.isDecimal = true; + value.valD = pdata.batteryLevel; + } + else if (_parameter == "geoTime") + { + value.isDecimal = false; + value.valS = pdata.geoTime; + } + else if (_parameter == "geoTimestamp") + { + value.isDecimal = false; + value.valS = pdata.geoTimestamp; + } + else if (_parameter == "currentTime") + { + value.isDecimal = false; + value.valS = pdata.currentTime; + } + else if (_parameter == "nextScheduledTime") + { + value.isDecimal = false; + value.valS = pdata.nextScheduledTime; + } + else if (_parameter == "nextAlarmclockTime") + { + value.isDecimal = false; + value.valS = pdata.nextAlarmclockTime; + } + else if (_parameter == "conditionContent") + { + value.isDecimal = false; + value.valS = pdata.conditionContentString; + } + else if (obj.containsKey(_parameter) && obj[_parameter].is()) + { + sensorVal = obj[_parameter].as(); + value.isDecimal = false; + value.valS = sensorVal; + } + else + { + value.isDecimal = false; + value.valS = "parameter mismatch"; + } + + if (value.isDecimal) + { + regEvent(value.valD, F("Presence"), _debug, _ticker); + } + else + { + regEvent(value.valS, F("Presence"), _debug, _ticker); + } + + } + else + { + if (_debug) + { + SerialPrint("i", "Presence", "Received MAC: " + dev + " val=" + msg); + } + dataFromNode = true; + _minutesPassed = 0; + setValue(msg); + } + } + } + + IoTValue execute(String command, std::vector ¶m) + { + IoTValue valTmp; + + if (command == "latitude") + { + valTmp.isDecimal = true; + valTmp.valD = pdata.lat; + return valTmp; + } + else if (command == "longitude") + { + valTmp.isDecimal = true; + valTmp.valD = pdata.lon; + return valTmp; + } + else if (command == "azimuth") + { + if (param.size() == 2 && param[0].isDecimal && param[1].isDecimal) + { + valTmp.isDecimal = true; + valTmp.valD = calculateInitialBearing(param[0].valD, param[1].valD, pdata.lat, pdata.lon); + } + else + { + valTmp.isDecimal = false; + valTmp.valS = "wrong parameters"; + } + return valTmp; + } + else if (command == "distance") + { + if (param.size() == 2 && param[1].isDecimal && param[1].isDecimal) + { + valTmp.isDecimal = true; + valTmp.valD = haversineDistance(param[0].valD, param[1].valD, pdata.lat, pdata.lon); + } + else + { + valTmp.isDecimal = false; + valTmp.valS = "wrong parameters"; + } + return valTmp; + } + else if (command == "batteryLevel") + { + valTmp.isDecimal = true; + valTmp.valD = pdata.batteryLevel; + return valTmp; + } + else if (command == "geoTime") + { + valTmp.isDecimal = false; + valTmp.valS = pdata.geoTime; + return valTmp; + } + else if (command == "geoTimestamp") + { + valTmp.isDecimal = false; + valTmp.valS = pdata.geoTimestamp; + return valTmp; + } + else if (command == "currentTime") + { + valTmp.isDecimal = false; + valTmp.valS = pdata.currentTime; + return valTmp; + } + else if (command == "currentTimestamp") + { + valTmp.isDecimal = false; + valTmp.valS = pdata.currentTimestamp; + return valTmp; + } + else if (command == "nextScheduledTime") + { + valTmp.isDecimal = false; + valTmp.valS = pdata.nextScheduledTime; + return valTmp; + } + else if (command == "nextScheduledTimestamp") + { + valTmp.isDecimal = false; + valTmp.valS = pdata.nextScheduledTimestamp; + return valTmp; + } + else if (command == "nextAlarmclockTime") + { + valTmp.isDecimal = false; + valTmp.valS = pdata.nextAlarmclockTime; + return valTmp; + } + else if (command == "nextAlarmclockTimestamp") + { + valTmp.isDecimal = false; + valTmp.valS = pdata.nextAlarmclockTimestamp; + return valTmp; + } + else if (command == "conditionContent") + { + valTmp.isDecimal = false; + valTmp.valS = pdata.conditionContentString; + return valTmp; + } + else if (command == "chargingState") + { + valTmp.isDecimal = false; + valTmp.valS = pdata.chargingState; + return valTmp; + } + else if (command == "plugState") + { + valTmp.isDecimal = false; + valTmp.valS = pdata.plugState; + return valTmp; + } + else if (command == "connectedWifi") + { + valTmp.isDecimal = false; + valTmp.valS = pdata.connectedWifi; + return valTmp; + } + else if (command == "deviceName") + { + valTmp.isDecimal = false; + valTmp.valS = pdata.deviceName; + return valTmp; + } + else if (command == "geoLocation") + { + valTmp.isDecimal = false; + valTmp.valS = pdata.geoLocation; + return valTmp; + } + else + { + valTmp.isDecimal = false; + valTmp.valS = "wrong command"; + return valTmp; + } + + return {}; + } + + String getMqttExterSub() + { + return _topic; + } + + void doByInterval() + { + _minutesPassed++; + setNewWidgetAttributes(); + if (mqttIsConnect() && !sendOk) + { + mqtt.setBufferSize(512); // ← ДО подписи + sendOk = true; + mqttSubscribeExternal(_topic); + } + } + void onMqttWsAppConnectEvent() + { + setNewWidgetAttributes(); + } + + void setNewWidgetAttributes() + { + + jsonWriteStr(json, F("info"), prettyMinutsTimeout(_minutesPassed)); + if (dataFromNode) + { + if (orange != 0 && red != 0 && offline != 0) + { + if (_minutesPassed < orange) + { + jsonWriteStr(json, F("color"), ""); + } + if (_minutesPassed >= orange && _minutesPassed < red) + { + jsonWriteStr(json, F("color"), F("orange")); // сделаем виджет оранжевым + } + if (_minutesPassed >= red && _minutesPassed < offline) + { + jsonWriteStr(json, F("color"), F("red")); // сделаем виджет красным + } + if (_minutesPassed >= offline) + { + jsonWriteStr(json, F("info"), F("offline")); + SerialPrint("i", "Presence", _id + " - offline"); + } + } + } + else + { + jsonWriteStr(json, F("info"), F("awaiting")); + } + // SerialPrint("i", "JSON", json); + sendSubWidgetsValues(_id, json); + } + + void parseGeo(const String &geo) + { + if (!geo.startsWith("geo:")) + return; + + int commaIndex = geo.indexOf(',', 4); + int semicolonIndex = geo.indexOf(';', commaIndex); + + if (commaIndex == -1 || semicolonIndex == -1) + return; + + String latStr = geo.substring(4, commaIndex); + String lonStr = geo.substring(commaIndex + 1, semicolonIndex); + + // timestamp + String tsTag = "timestamp="; + int tsIndex = geo.indexOf(tsTag); + String tsStr = (tsIndex > 0) ? geo.substring(tsIndex + tsTag.length()) : ""; + + pdata.lat = latStr.toFloat(); + pdata.lon = lonStr.toFloat(); + pdata.geoTimestamp = tsStr.toInt(); + + if (_debug) + { + SerialPrint("i", "Presence", "Lat: " + String(pdata.lat, 6)); + SerialPrint("i", "Presence", "Lon: " + String(pdata.lon, 6)); + SerialPrint("i", "Presence", "Geo timestamp: " + String(pdata.geoTimestamp)); + } + } + + ~Presence() {}; +}; + +void *getAPI_Presence(String subtype, String param) +{ + if (subtype == F("Presence")) + { + return new Presence(param); + } + else + { + return nullptr; + } +} diff --git a/src/modules/sensors/Presence/export.json b/src/modules/sensors/Presence/export.json new file mode 100644 index 00000000..409b4937 --- /dev/null +++ b/src/modules/sensors/Presence/export.json @@ -0,0 +1,372 @@ +{ + "mark": "iotm", + "config": [ + { + "global": 0, + "type": "Reading", + "subtype": "Variable", + "id": "voutDistance", + "needSave": 0, + "widget": "anydataKm", + "page": "Output", + "descr": "Distance", + "int": "0", + "val": "0.0", + "map": "", + "plus": 0, + "multiply": "0.001", + "round": 0, + "moduleName": "Variable" + }, + { + "global": 0, + "type": "Reading", + "subtype": "Variable", + "id": "voutAzimuth", + "needSave": 0, + "widget": "anydataСorner", + "page": "Output", + "descr": "Azimuth", + "int": "0", + "val": "0.0", + "map": "1024,1024,1,100", + "plus": 0, + "multiply": 1, + "round": 0, + "moduleName": "Variable" + }, + { + "global": 0, + "type": "Reading", + "subtype": "Presence", + "id": "presence", + "widget": "anydataM", + "page": "Presence", + "descr": "Distance", + "Lat. A": "47.0159", + "Long. A": "28.8448", + "parameter": "distance", + "topic": "/myPhone/status", + "isJson": 1, + "round": "0", + "orange": 60, + "red": 120, + "offline": 180, + "int": 15, + "ticker": 1, + "debug": 1, + "moduleName": "Presence" + }, + { + "global": 0, + "type": "Reading", + "subtype": "Variable", + "id": "voutLat", + "needSave": 0, + "widget": "anydataСorner", + "page": "Output", + "descr": "Latitude", + "int": "0", + "val": "0.0", + "map": "", + "plus": 0, + "multiply": 1, + "round": "1", + "moduleName": "Variable" + }, + { + "global": 0, + "type": "Reading", + "subtype": "Variable", + "id": "voutLong", + "needSave": 0, + "widget": "anydataСorner", + "page": "Output", + "descr": "Longitude", + "int": "0", + "val": "0.0", + "map": "", + "plus": 0, + "multiply": 1, + "round": "1", + "moduleName": "Variable" + }, + { + "global": 0, + "type": "Reading", + "subtype": "Variable", + "id": "voutbatteryLevel", + "needSave": 0, + "widget": "anydataHum", + "page": "Output", + "descr": "batteryLevel", + "int": "0", + "val": "0.0", + "map": "", + "plus": 0, + "multiply": 1, + "round": 0, + "moduleName": "Variable" + }, + { + "global": 0, + "type": "Reading", + "subtype": "Variable", + "id": "voutgeoTime", + "needSave": 0, + "widget": "anydataDef", + "page": "Output", + "descr": "geoTime", + "int": "0", + "val": "0.0", + "map": "1024,1024,1,100", + "plus": 0, + "multiply": 1, + "round": 0, + "moduleName": "Variable" + }, + { + "global": 0, + "type": "Reading", + "subtype": "Variable", + "id": "voutgeoTimestamp", + "needSave": 0, + "widget": "anydataDef", + "page": "Output", + "descr": "geoTimestamp", + "int": "0", + "val": "0.0", + "map": "1024,1024,1,100", + "plus": 0, + "multiply": 1, + "round": 0, + "moduleName": "Variable" + }, + { + "global": 0, + "type": "Reading", + "subtype": "Variable", + "id": "voutcurrentTime", + "needSave": 0, + "widget": "anydataDef", + "page": "Output", + "descr": "currentTime", + "int": "0", + "val": "0.0", + "map": "1024,1024,1,100", + "plus": 0, + "multiply": 1, + "round": 0, + "moduleName": "Variable" + }, + { + "global": 0, + "type": "Reading", + "subtype": "Variable", + "id": "voutcurrentTimestamp", + "needSave": 0, + "widget": "anydataDef", + "page": "Output", + "descr": "currentTimestamp", + "int": "0", + "val": "0.0", + "map": "1024,1024,1,100", + "plus": 0, + "multiply": 1, + "round": 0, + "moduleName": "Variable" + }, + { + "global": 0, + "type": "Reading", + "subtype": "Variable", + "id": "voutnextScheduledTime", + "needSave": 0, + "widget": "anydataDef", + "page": "Output", + "descr": "nextScheduledTime", + "int": "0", + "val": "0.0", + "map": "1024,1024,1,100", + "plus": 0, + "multiply": 1, + "round": 0, + "moduleName": "Variable" + }, + { + "global": 0, + "type": "Reading", + "subtype": "Variable", + "id": "voutnextScheduledTimestamp", + "needSave": 0, + "widget": "anydataDef", + "page": "Output", + "descr": "nextScheduledTimestamp", + "int": "0", + "val": "0.0", + "map": "1024,1024,1,100", + "plus": 0, + "multiply": 1, + "round": 0, + "moduleName": "Variable" + }, + { + "global": 0, + "type": "Reading", + "subtype": "Variable", + "id": "voutnextAlarmclockTime", + "needSave": 0, + "widget": "anydataDef", + "page": "Output", + "descr": "nextAlarmclockTime", + "int": "0", + "val": "0.0", + "map": "1024,1024,1,100", + "plus": 0, + "multiply": 1, + "round": 0, + "moduleName": "Variable" + }, + { + "global": 0, + "type": "Reading", + "subtype": "Variable", + "id": "voutnextAlarmclockTimestamp", + "needSave": 0, + "widget": "anydataDef", + "page": "Output", + "descr": "nextAlarmclockTimestamp", + "int": "0", + "val": "0.0", + "map": "1024,1024,1,100", + "plus": 0, + "multiply": 1, + "round": 0, + "moduleName": "Variable" + }, + { + "global": 0, + "type": "Reading", + "subtype": "Variable", + "id": "voutchargingState", + "needSave": 0, + "widget": "anydataDef", + "page": "Output", + "descr": "chargingState", + "int": "0", + "val": "0.0", + "map": "1024,1024,1,100", + "plus": 0, + "multiply": 1, + "round": 0, + "moduleName": "Variable" + }, + { + "global": 0, + "type": "Reading", + "subtype": "Variable", + "id": "voutplugState", + "needSave": 0, + "widget": "anydataDef", + "page": "Output", + "descr": "plugState", + "int": "0", + "val": "0.0", + "map": "1024,1024,1,100", + "plus": 0, + "multiply": 1, + "round": 0, + "moduleName": "Variable" + }, + { + "global": 0, + "type": "Reading", + "subtype": "Variable", + "id": "voutconnectedWifi", + "needSave": 0, + "widget": "anydataDef", + "page": "Output", + "descr": "connectedWifi", + "int": "0", + "val": "0.0", + "map": "1024,1024,1,100", + "plus": 0, + "multiply": 1, + "round": 0, + "moduleName": "Variable" + }, + { + "global": 0, + "type": "Reading", + "subtype": "Variable", + "id": "voutdeviceName", + "needSave": 0, + "widget": "anydataDef", + "page": "Output", + "descr": "deviceName", + "int": "0", + "val": "0.0", + "map": "1024,1024,1,100", + "plus": 0, + "multiply": 1, + "round": 0, + "moduleName": "Variable" + }, + { + "global": 0, + "type": "Reading", + "subtype": "Variable", + "id": "voutgeoLocation", + "needSave": 0, + "widget": "anydataDef", + "page": "Output", + "descr": "geoLocation", + "int": "0", + "val": "0.0", + "map": "1024,1024,1,100", + "plus": 0, + "multiply": 1, + "round": 0, + "moduleName": "Variable" + }, + { + "global": 0, + "type": "Reading", + "subtype": "Variable", + "id": "voutconditionContent", + "needSave": 0, + "widget": "anydataDef", + "page": "Output", + "descr": "conditionContent", + "int": "0", + "val": "0.0", + "map": "1024,1024,1,100", + "plus": 0, + "multiply": 1, + "round": 0, + "moduleName": "Variable" + } + ] +} + +scenario=> if presence then { +voutDistance = presence.distance(47.0159,28.8448) +voutAzimuth = presence.azimuth(47.0159,28.8448) +voutLat = presence.latitude() +voutLong = presence.longitude() +voutbatteryLevel = presence.batteryLevel() +voutgeoTime = presence.geoTime() +voutgeoTimestamp = presence.geoTimestamp() +voutcurrentTime = presence.currentTime() +voutcurrentTimestamp = presence.currentTimestamp() +voutnextScheduledTime = presence.nextScheduledTime() +voutnextScheduledTimestamp = presence.nextScheduledTimestamp() +voutnextAlarmclockTime = presence.nextAlarmclockTime() +voutnextAlarmclockTimestamp = presence.nextAlarmclockTimestamp() +voutchargingState = presence.chargingState() +voutplugState = presence.plugState() +voutconnectedWifi = presence.connectedWifi() +voutdeviceName = presence.deviceName() +voutgeoLocation = presence.geoLocation() +voutconditionContent = presence.conditionContent() +} \ No newline at end of file diff --git a/src/modules/sensors/Presence/modinfo.json b/src/modules/sensors/Presence/modinfo.json new file mode 100644 index 00000000..fc900e39 --- /dev/null +++ b/src/modules/sensors/Presence/modinfo.json @@ -0,0 +1,199 @@ +{ + "menuSection": "sensors", + "configItem": [ + { + "global": 0, + "name": "MQTT Presence Subscriber", + "type": "Reading", + "subtype": "Presence", + "id": "presence", + "widget": "anydataM", + "page": "Presence", + "descr": "Дистанция", + "Lat. A": "47.0159", + "Long. A": "28.8448", + "parameter": "distance", + "topic": "/myPhone/status", + "isJson": 1, + "round": "0", + "orange": 60, + "red": 120, + "offline": 180, + "int": 15, + "ticker": 1, + "debug": 1 + } + ], + "about": { + "authorName": "Alex", + "authorContact": "https://t.me/cmche", + "authorGit": "https://github.com/CHE77/IoTManager-Modules", + "exampleURL": "https://iotmanager.org/wiki", + "specialThanks": "", + "moduleName": "Presence", + "moduleVersion": "1.0", + "usedRam": { + "esp32_4mb": 15, + "esp8266_4mb": 15 + }, + "title": "MQTT Presence Subscriber", + "moduleDesc": "Модуль получения и обработки данных из Presence Publisher app - https://f-droid.org/packages/org.ostrya.presencepublisher/ https://play.google.com/store/apps/details?id=org.ostrya.presencepublisher Получает геопозицию телефона, считает пеленг и дистанцию.", + "license": "Cooperative Non-Violent Public License (CNPL)", + "propInfo": { + "Lat. A": "Широта точки отсчета (локации), в градусах", + "Long. A": "Долгота точки отсчета (локации), в градусах", + "parameter": "Параметр/ключ для получения данных из json и его производных. Совпадают с методами для сценария", + "topic": "Топик на который подписывется модуль. Должен совпадать с топиком в приложении и оканчиваться на status", + "isJson": "1 - ожидаем в топике json. Другие форматы пока не подерживаются. В приложении выберите тоже json", + "round": "Округление после запятой.", + "orange": "Количество минут после которого окрасить виджет в оранжевый цвет", + "red": "количество минут после которого окрасить виджет в красный цвет", + "offline": "Количество минут после которого отобразить что устройство offline, если все три orange red и offline поставить в ноль - то функция окраски выключится", + "int": "Интервал для изменения цвета", + "ticker": "Генерировать(1) или нет(0) события при каждом тике часов (каждые int секунд).", + "debug": "1 - выводить дополнительный лог в сериал" + }, + "retInfo": " - Согласно выбраного параметра", + "funcInfo": [ + { + "name": "latitude", + "descr": "Получить широту позиции устройства (и далее) с приложением Presence Publisher", + "params": [ + "presence.latitude()" + ] + }, + { + "name": "longitude", + "descr": "Получить долготу позиции устройства", + "params": [ + "presence.longitude()" + ] + }, + { + "name": "distance", + "descr": "Получить дистанцию до устройства", + "params": [ + "presence.distance()" + ] + }, + { + "name": "azimuth", + "descr": "Получить пеленг на устройтво", + "params": [ + "presence.azimuth()" + ] + }, + { + "name": "batteryLevel", + "descr": "Получить уровень заряда батареи устройства", + "params": [ + "presence.batteryLevel()" + ] + }, + { + "name": "geoTime", + "descr": "Получить время определения геопозии устройства", + "params": [ + "presence.geoTime()" + ] + }, + { + "name": "geoTimestamp", + "descr": "Получить UnixTime определения геопозии устройства", + "params": [ + "presence.geoTimestamp()" + ] + }, + { + "name": "currentTime", + "descr": "Получить время получения геопозиции от устройства", + "params": [ + "presence.currentTime()" + ] + }, + { + "name": "currentTimestamp", + "descr": "Получить UnixTime получения геопозиции от устройства", + "params": [ + "presence.currentTimestamp()" + ] + }, + { + "name": "nextScheduledTime", + "descr": "Получить время следующего получения геопозиции от устройства", + "params": [ + "presence.nextScheduledTime()" + ] + }, + { + "name": "nextScheduledTimestamp", + "descr": "Получить UnixTime следующего получения геопозиции от устройства", + "params": [ + "presence.nextScheduledTimestamp()" + ] + }, + { + "name": "nextAlarmclockTime", + "descr": "Получить время следующего будильника на устройтве", + "params": [ + "presence.nextAlarmclockTime()" + ] + }, + { + "name": "nextAlarmclockTimestamp", + "descr": "Получить UnixTime следующего будильника на устройтве", + "params": [ + "presence.nextAlarmclockTimestamp()" + ] + }, + { + "name": "chargingState", + "descr": "Получить статус зарядки устройства", + "params": [ + "presence.chargingState()" + ] + }, + { + "name": "plugState", + "descr": "Получить тип зарядки устройства", + "params": [ + "presence.plugState()" + ] + }, + { + "name": "connectedWifi", + "descr": "Получить Wi-Fi точку доступа к которой подключено устройство", + "params": [ + "presence.connectedWifi()" + ] + }, + { + "name": "deviceName", + "descr": "Получить имя устройства", + "params": [ + "presence.deviceName()" + ] + }, + { + "name": "geoLocation", + "descr": "Получить строку с широтой, долготой и верменем определения геопозиции", + "params": [ + "presence.geoLocation()" + ] + }, + { + "name": "conditionContent", + "descr": "Получить дополнительное условие отправки данных (геопозиции и др)", + "params": [ + "presence.conditionContent()" + ] + } + ] + }, + "defActive": false, + "usedLibs": { + "esp32*": [], + "esp82*": [], + "bk72*": [] + } +} \ No newline at end of file diff --git a/src/modules/sensors/Presence/preview.png b/src/modules/sensors/Presence/preview.png new file mode 100644 index 0000000000000000000000000000000000000000..47ab8f1d6d1fc5cfcb6eff3643dba745221ed7cf GIT binary patch literal 70938 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 ckl~I +#include // Для settimeofday_cb() в ESP8266 +#else +#include // Для ESP32 +#endif + +typedef enum +{ + SoftRTC_SYNC_STATUS_NOT_SET = 0, // Время не установлено + // SoftRTC_SYNC_STATUS_COULD_NOT_SET, // Время не может быть установлено + SoftRTC_SYNC_STATUS_BEFORE_SoftRTC, // Время было установлено до SoftRTC + SoftRTC_SYNC_STATUS_RESTORED, // Время восстановлено из памяти + SoftRTC_SYNC_STATUS_MANUAL, // Время установлено вручную + SoftRTC_SYNC_STATUS_FROM_BROWSER_OR_NTP, + SoftRTC_SYNC_STATUS_FROM_BROWSER, // Время (скоере всего) установлено с Браузера + SoftRTC_SYNC_STATUS_NTP_JUST, // Время только что синхронизировано через NTP + SoftRTC_SYNC_STATUS_NTP // Время синхронизировано через NTP + +} SoftRTC_sync_status_t; + +// Переменная для отслеживания источника времени +volatile int syncStatus = SoftRTC_SYNC_STATUS_NOT_SET; + +// Функция-коллбэк для NTP (разные сигнатуры для разных платформ) +#ifdef ESP8266 +/* +void timeSyncCallback() +{ + syncStatus = SoftRTC_SYNC_STATUS_NTP_JUST; +} +*/ +#else +void timeSyncCallback(struct timeval *tv) +{ + (void)tv; // Неиспользуемый параметр + syncStatus = SoftRTC_SYNC_STATUS_NTP_JUST; +} +#endif + +time_t getUnixTimeFromYMDHMS(int year, int month, int day, int hour, int minute, int second) +{ + struct tm t; + t.tm_year = year - 1900; + t.tm_mon = month - 1; + t.tm_mday = day; + t.tm_hour = hour; + t.tm_min = minute; + t.tm_sec = second; + t.tm_isdst = -1; + return mktime(&t); +} + +time_t getUnixTimeFromString(String datetime) +{ + struct tm t = {0}; + int d, m, y, h, min, s; + + const char *dt = datetime.c_str(); + + if (sscanf(dt, "%d%*c%d%*c%d %d:%d:%d", &d, &m, &y, &h, &min, &s) == 6) + { + if (y < 100) + y += (y >= 69) ? 1900 : 2000; + if (d > 31) + { + int temp = d; + d = y; + y = temp; + } + } + else + { + return -1; + } + + t.tm_mday = d; + t.tm_mon = m - 1; + t.tm_year = y - 1900; + t.tm_hour = h; + t.tm_min = min; + t.tm_sec = s; + t.tm_isdst = -1; + + return mktime(&t); +} + +String formatUnixTime(time_t unixTime) +{ + struct tm *timeInfo; + timeInfo = localtime(&unixTime); + char formattedTime[32]; + // char formattedTime[20]; + int year = timeInfo->tm_year + 1900; + int shortYear = year % 100; + + snprintf(formattedTime, sizeof(formattedTime), "%02d.%02d.%02d %02d:%02d:%02d", + timeInfo->tm_mday, timeInfo->tm_mon + 1, shortYear, + timeInfo->tm_hour, timeInfo->tm_min, timeInfo->tm_sec); + + return String(formattedTime); +} + +bool isDSTfunc(time_t utc) +{ + struct tm *timeinfo = gmtime(&utc); // UTC time → broken-down time + int year = timeinfo->tm_year + 1900; + + // Найдём последнюю дату воскресенья марта и октября + struct tm lastMarchSunday = {0}; + lastMarchSunday.tm_year = year - 1900; + lastMarchSunday.tm_mon = 2; // Март (0-based) + lastMarchSunday.tm_mday = 31; + lastMarchSunday.tm_hour = 3; // Переход в 03:00 UTC + + mktime(&lastMarchSunday); // нормализация даты + + // Ищем последнее воскресенье марта + while (lastMarchSunday.tm_wday != 0) + { // 0 = воскресенье + lastMarchSunday.tm_mday--; + mktime(&lastMarchSunday); + } + + time_t dstStart = mktime(&lastMarchSunday); // начало летнего времени + + struct tm lastOctoberSunday = {0}; + lastOctoberSunday.tm_year = year - 1900; + lastOctoberSunday.tm_mon = 9; // Октябрь + lastOctoberSunday.tm_mday = 31; + lastOctoberSunday.tm_hour = 4; // Переход в 04:00 UTC + + mktime(&lastOctoberSunday); + + while (lastOctoberSunday.tm_wday != 0) + { + lastOctoberSunday.tm_mday--; + mktime(&lastOctoberSunday); + } + + time_t dstEnd = mktime(&lastOctoberSunday); // конец летнего времени + + return utc >= dstStart && utc < dstEnd; +} + +int getWeekOfYear(time_t unixTime) +{ + struct tm *timeInfo = localtime(&unixTime); + char buffer[3]; // 2 цифры + \0 + strftime(buffer, sizeof(buffer), "%W", timeInfo); // %W — номер недели (понедельник — первый день недели) + return atoi(buffer); +} + +int getDayOfWeekNumber(time_t unixTime) +{ + struct tm *timeInfo = localtime(&unixTime); + int wday = timeInfo->tm_wday; + return (wday == 0) ? 7 : wday; // воскресенье (0) → 7 +} + +int getDaysInMonth(time_t unixTime) +{ + struct tm t = *localtime(&unixTime); + + t.tm_mday = 1; // Первый день текущего месяца + t.tm_mon += 1; // Следующий месяц + t.tm_hour = 0; t.tm_min = 0; t.tm_sec = 0; + + if (t.tm_mon > 11) { // Декабрь → январь + t.tm_mon = 0; + t.tm_year += 1; + } + + time_t firstOfNextMonth = mktime(&t); + t.tm_mon -= 1; // Возврат к текущему + t.tm_mday = 1; + time_t firstOfThisMonth = mktime(&t); + + int days = (firstOfNextMonth - firstOfThisMonth) / (60 * 60 * 24); + return days; +} + +// Возвращает день и месяц Пасхи в Григорианском календаре +void getOrthodoxEasterDate(int year, int &day, int &month) +{ + // Meeus (Julian) algorithm + int a = year % 4; + int b = year % 7; + int c = year % 19; + int d = (19 * c + 15) % 30; + int e = (2 * a + 4 * b - d + 34) % 7; + + int julianMonth = (d + e + 114) / 31; // 3 = март, 4 = апрель + int julianDay = ((d + e + 114) % 31) + 1; + + // Переходим из юлианского в григоріанский: + // * для 1900‑2099 разница = 13 дней + struct tm t {}; + t.tm_year = year - 1900; + t.tm_mon = julianMonth - 1; + t.tm_mday = julianDay + 13; // 13‑дневная разница + mktime(&t); // нормализует переход между месяцами + + day = t.tm_mday; + month = t.tm_mon + 1; +} + + +class SoftRTC : public IoTItem +{ +private: + bool _ticker = false; + bool _debug = false; + long interval = 60; + int _winterTime = 2; + int _summerTime = 3; + time_t recovered_unixTime = 0; + time_t lastNTPtimeCorrection = 0; + time_t lastUnixTimeMillis = 0; + time_t lastUnixTime = 0; + // time_t _timeZoneSeconds; + int _timezone = 0; + int lastSyncStatus = 0; + time_t before = 0; + unsigned long startMillis = 0; // Сохраняем текущее время в миллисекундах + +public: + SoftRTC(String parameters) : IoTItem(parameters) + { + _timezone = jsonReadInt(settingsFlashJson, F("timezone")); +// Устанавливаем коллбэк в зависимости от платформы +#ifdef ESP8266 + // settimeofday_cb(timeSyncCallback); +#else + sntp_set_time_sync_notification_cb(timeSyncCallback); +#endif + + _needSave = true; + _round = 0; + jsonRead(parameters, F("ticker"), _ticker); + jsonRead(parameters, F("int"), interval); + jsonRead(parameters, F("winterTime"), _winterTime); + jsonRead(parameters, F("summerTime"), _summerTime); + jsonRead(parameters, F("debug"), _debug); + + before = time(nullptr); + startMillis = millis(); // Сохраняем текущее время в миллисекундах + } + + void loop() + { + +#if defined(ESP32) + if (syncStatus == SoftRTC_SYNC_STATUS_NTP_JUST) + { + time_t ut = getSystemTime(); + String localDateTime = formatUnixTime(ut + _timezone * 60 * 60); + if (_debug) + SerialPrint("I", F("SoftRTC"), "✅ Время синхронизировано через NTP! " + (String)ut + " LT: " + localDateTime); + + lastNTPtimeCorrection = getSystemTime() - (lastUnixTime + (millis() - lastUnixTimeMillis) / 1000); + if (_debug) + SerialPrint("I", F("SoftRTC"), "lastNTPtimeCorrection: " + (String)lastNTPtimeCorrection + " сек."); + syncStatus = SoftRTC_SYNC_STATUS_NTP; + + IoTValue valTmp; + valTmp.isDecimal = false; + valTmp.valS = (String)ut; + regEvent(valTmp.valS, F("SoftRTC"), _debug, true); + } +#endif + + if (syncStatus < 4) // если вреямя обновлено из надежного источника то уже не остлеживаем его изменение + { + + time_t after = time(nullptr); + startMillis = millis() - startMillis; + + if ((abs(after - before) - int((startMillis + 500) / 1000)) > 2) // проверка на изменение вермени + { + int delta = abs(after - before - int((startMillis + 500) / 1000)); + + if (_debug){ + SerialPrint("I", F("SoftRTC"), "Замечено, что Время измененно!"); + SerialPrint("I", F("SoftRTC"), "syncStatus = " + String(syncStatus)); + SerialPrint("I", F("SoftRTC"), "lastSyncStatus = " + String(lastSyncStatus)); + } + + if (syncStatus == lastSyncStatus) + { + if (_debug) + SerialPrint("I", F("SoftRTC"), "Замечено, что Время измененно без изменения статуса"); + +#ifdef ESP8266 + syncStatus = SoftRTC_SYNC_STATUS_FROM_BROWSER_OR_NTP; + lastSyncStatus = SoftRTC_SYNC_STATUS_FROM_BROWSER_OR_NTP; + + time_t ut = getSystemTime(); + String localDateTime = formatUnixTime(ut + _timezone * 60 * 60); + if (_debug) + SerialPrint("I", F("SoftRTC"), "✅ Время синхронизировано через NTP или Browser! " + (String)ut + " LT: " + localDateTime); + + lastNTPtimeCorrection = getSystemTime() - (lastUnixTime + (millis() - lastUnixTimeMillis) / 1000); + if (_debug) + SerialPrint("I", F("SoftRTC"), "lastNTPtimeCorrection: " + (String)lastNTPtimeCorrection + " сек."); + +#else + syncStatus = SoftRTC_SYNC_STATUS_FROM_BROWSER; + lastSyncStatus = SoftRTC_SYNC_STATUS_FROM_BROWSER; +#endif + } + else + { + lastSyncStatus = syncStatus; + if (_debug) + SerialPrint("I", F("SoftRTC"), "Замечено, что Время измененно c изменением статуса"); + } + } + before = time(nullptr); + startMillis = millis(); + } + + IoTItem::loop(); + } + + void doByInterval() + { + time_t unixTime = 0; + + if (syncStatus == SoftRTC_SYNC_STATUS_RESTORED) + { + unixTime = getSystemTime(); + if (_debug) + SerialPrint("I", F("SoftRTC"), "Время восстановлено из памяти: " + (String)unixTime); + if (isNetworkActive()) + { + if (_debug) + SerialPrint("I", F("SoftRTC"), "Пробуем синхронизировать через NTP"); + synchTime(); + } + } + else if (syncStatus == SoftRTC_SYNC_STATUS_MANUAL) + { + unixTime = getSystemTime(); + if (_debug) + SerialPrint("I", F("SoftRTC"), "Время установлено вручную: " + (String)unixTime); + if (isNetworkActive()) + { + if (_debug) + SerialPrint("I", F("SoftRTC"), "Пробуем синхронизировать через NTP"); + synchTime(); + } + } +#ifdef ESP8266 + else if (syncStatus == SoftRTC_SYNC_STATUS_FROM_BROWSER_OR_NTP) + { + unixTime = getSystemTime(); + if (_debug) + SerialPrint("I", F("SoftRTC"), "Время синхронизировано через WEB или NTP: " + (String)unixTime); + } +#else + else if (syncStatus == SoftRTC_SYNC_STATUS_FROM_BROWSER) + { + unixTime = getSystemTime(); + if (_debug) + SerialPrint("I", F("SoftRTC"), "Время синхронизировано через Браузер: " + (String)unixTime); + } + else if (syncStatus == SoftRTC_SYNC_STATUS_NTP || syncStatus == SoftRTC_SYNC_STATUS_NTP_JUST) + { + unixTime = getSystemTime(); + if (_debug) + SerialPrint("I", F("SoftRTC"), "Время синхронизировано через NTP: " + (String)unixTime); + } +#endif + + else if (time(nullptr) > 100000) + { + syncStatus = SoftRTC_SYNC_STATUS_BEFORE_SoftRTC; + unixTime = getSystemTime(); + if (_debug) + SerialPrint("I", F("SoftRTC"), "Время синхронизировано через NTP до загрузки SoftRTC: " + (String)unixTime); + } + else if (syncStatus == SoftRTC_SYNC_STATUS_NOT_SET) + { + valuesFlashJson = readFile(F("values.json"), 4096); + valuesFlashJson.replace("\r\n", ""); + String valAsStr = ""; + if (jsonRead(valuesFlashJson, _id, valAsStr, _debug)) + { + recovered_unixTime = valAsStr.toInt(); + if (_debug) + SerialPrint("I", F("SoftRTC"), "Время из энергонезависимой памяти: " + (String)recovered_unixTime); + + time_t nextSecond = millis() / 1000 + 1; + while (millis() / 1000 < nextSecond) + { + } + + unixTime = recovered_unixTime + millis() / 1000 + interval / 2; + + struct timeval now = {.tv_sec = unixTime, .tv_usec = 0}; + settimeofday(&now, NULL); + + syncStatus = SoftRTC_SYNC_STATUS_RESTORED; + + String localDateTime = formatUnixTime(unixTime + _timezone * 60 * 60); + SerialPrint("I", F("SoftRTC"), "Установлено восстановленное время: " + (String)unixTime + " LT: " + localDateTime); + } + else + { + // syncStatus = SoftRTC_SYNC_STATUS_COULD_NOT_SET; + if (_debug) + SerialPrint("I", F("SoftRTC"), "Время не может быть восстановлено из памяти"); + } + } + else + { + if (_debug) + SerialPrint("I", F("SoftRTC"), "Время не установлено"); + } + + if (unixTime) + { + lastUnixTime = unixTime; + lastUnixTimeMillis = millis(); + if (_debug) + SerialPrint("I", F("SoftRTC"), "Сохраняем время: " + (String)unixTime); + value.isDecimal = false; + value.valS = (String)unixTime; + regEvent(value.valS, F("SoftRTC"), _debug, _ticker); + } + } + + void onModuleOrder(String &key, String &value) + { + if (key == "setUTime") + { + if (_debug) + SerialPrint("i", F("SoftRTC"), "Устанавливаем время: " + value); + char *stopstring; + time_t ut = strtoul(value.c_str(), &stopstring, 10); + + struct timeval now = {.tv_sec = ut, .tv_usec = 0}; + settimeofday(&now, NULL); + + syncStatus = SoftRTC_SYNC_STATUS_MANUAL; + + String localDateTime = formatUnixTime(getSystemTime() + _timezone * 60 * 60); + if (_debug) + SerialPrint("I", F("SoftRTC"), "LT: " + localDateTime); + + IoTValue valTmp; + valTmp.isDecimal = false; + valTmp.valS = (String)ut; + regEvent(valTmp.valS, F("SoftRTC"), _debug, _ticker); + } + else if (key == "setSysTime") + { + if (_debug) + SerialPrint("i", F("SoftRTC"), "Устанавливаем системное время: " + value); + + time_t ut = getUnixTimeFromString(value) - _timezone * 60 * 60; + + struct timeval now = {.tv_sec = ut, .tv_usec = 0}; + settimeofday(&now, NULL); + + String localDateTime = formatUnixTime(getSystemTime() + _timezone * 60 * 60); + if (_debug) + SerialPrint("I", F("SoftRTC"), (String)ut + " LT: " + localDateTime); + + syncStatus = SoftRTC_SYNC_STATUS_MANUAL; + + IoTValue valTmp; + valTmp.isDecimal = false; + valTmp.valS = (String)ut; + regEvent(valTmp.valS, F("SoftRTC"), _debug, _ticker); + } + } + + IoTValue execute(String command, std::vector ¶m) + { + if (command == "checkForSummer") + { + unixTime = getSystemTime(); + bool isDST = isDSTfunc(unixTime); + + int newTimeZone = isDST ? _summerTime : _winterTime; + + if (newTimeZone != _timezone) + { + jsonWriteInt_(settingsFlashJson, F("timezone"), isDST ? _summerTime : _winterTime); + syncSettingsFlashJson(); + _timezone = newTimeZone; + + if (newTimeZone == _summerTime) + { + SerialPrint("i", F("SoftRTC"), "Перешли на летнее время"); + } + else + { + SerialPrint("i", F("SoftRTC"), "Перешли на зименее время"); + } + } + + return {}; + } + else if (command == "getTime") + { + String localDateTime = formatUnixTime(getSystemTime() + _timezone * 60 * 60); + + IoTValue valTmp; + valTmp.isDecimal = false; + valTmp.valS = localDateTime; + return valTmp; + } + else if (command == "getUTShort") + { + IoTValue valTmp; + valTmp.isDecimal = true; + unixTimeShort = getSystemTime() - START_DATETIME; + valTmp.valD = unixTimeShort; + return valTmp; + } + else if (command == "setUnixTime") + { + if (param.size() == 1) + { + time_t ut = strtoul(param[0].valS.c_str(), nullptr, 10); + if (_debug) + SerialPrint("i", F("SoftRTC"), "Устанавливаем время UT: " + (String)ut); + + struct timeval now = {.tv_sec = ut, .tv_usec = 0}; + settimeofday(&now, NULL); + + String localDateTime = formatUnixTime(ut + _timezone * 60 * 60); + if (_debug) + SerialPrint("I", F("SoftRTC"), "LT: " + localDateTime); + syncStatus = SoftRTC_SYNC_STATUS_MANUAL; + + IoTValue valTmp; + valTmp.isDecimal = false; + valTmp.valS = (String)ut; + regEvent(valTmp.valS, F("SoftRTC"), _debug, _ticker); + + return {}; + } + } + else if (command == "setTimeFromYMDHMS") + { + if (param.size() == 6) + { + time_t ut = getUnixTimeFromYMDHMS(param[0].valD, param[1].valD, param[2].valD, + param[3].valD, param[4].valD, param[5].valD); + if (_debug) + SerialPrint("i", F("SoftRTC"), "Устанавливаем время YMDHMS: " + (String)ut); + + struct timeval now = {.tv_sec = ut, .tv_usec = 0}; + settimeofday(&now, NULL); + + String localDateTime = formatUnixTime(ut + _timezone * 60 * 60); + if (_debug) + SerialPrint("I", F("SoftRTC"), "LT: " + localDateTime); + syncStatus = SoftRTC_SYNC_STATUS_MANUAL; + + IoTValue valTmp; + valTmp.isDecimal = false; + valTmp.valS = (String)ut; + regEvent(valTmp.valS, F("SoftRTC"), _debug, _ticker); + + return {}; + } + } + else if (command == "lastNTPtimeCorrection") + { + IoTValue valTmp; + valTmp.isDecimal = true; + valTmp.valD = lastNTPtimeCorrection; + return valTmp; + } + else if (command == "getWeekNumber") + { + time_t ut = getSystemTime() + _timezone * 60 * 60; // учитываем локальное смещение + int week = getWeekOfYear(ut); + IoTValue valTmp; + valTmp.isDecimal = true; + valTmp.valD = week; + return valTmp; + } + else if (command == "getDayOfWeek") + { + time_t ut = getSystemTime() + _timezone * 60 * 60; // учёт смещения + int dayNum = getDayOfWeekNumber(ut); + IoTValue valTmp; + valTmp.isDecimal = true; + valTmp.valD = dayNum; + return valTmp; + } + else if (command == "getYear") + { + time_t ut = getSystemTime() + _timezone * 60 * 60; // локальное смещение + struct tm *timeInfo = localtime(&ut); + int year = timeInfo->tm_year + 1900; // полный (4‑значный) год + IoTValue valTmp; + valTmp.isDecimal = true; // возвращаем число + valTmp.valD = year; + return valTmp; + } + else if (command == "getDayOfYear") + { + time_t ut = getSystemTime() + _timezone * 60 * 60; + struct tm *timeInfo = localtime(&ut); + int dayOfYear = timeInfo->tm_yday + 1; // tm_yday начинается с 0 + IoTValue valTmp; + valTmp.isDecimal = true; + valTmp.valD = dayOfYear; + return valTmp; + } + else if (command == "getDaysInMonth") + { + time_t ut = getSystemTime() + _timezone * 60 * 60; + int days = getDaysInMonth(ut); + + IoTValue valTmp; + valTmp.isDecimal = true; + valTmp.valD = days; + return valTmp; + } + else if (command == "getOrthodoxEaster") + { + time_t ut = getSystemTime() + _timezone * 60 * 60; + struct tm *t = localtime(&ut); + int day, month; + getOrthodoxEasterDate(t->tm_year + 1900, day, month); + + char buf[12]; + snprintf(buf, sizeof(buf), "%02d.%02d.%d", day, month, t->tm_year + 1900); + + IoTValue valTmp; + valTmp.isDecimal = false; + valTmp.valS = String(buf); + return valTmp; + } + else if (command == "isOrthodoxEaster") + { + time_t ut = getSystemTime() + _timezone * 60 * 60; + struct tm *now = localtime(&ut); + + int dEaster, mEaster; + getOrthodoxEasterDate(now->tm_year + 1900, dEaster, mEaster); + + bool todayIsEaster = (now->tm_mday == dEaster) && (now->tm_mon + 1 == mEaster); + + IoTValue valTmp; + valTmp.isDecimal = true; + valTmp.valD = todayIsEaster ? 1 : 0; + return valTmp; + } + + + return {}; + } + + ~SoftRTC() {} +}; + +class SoftRTCsyncStatus : public IoTItem +{ +private: + int lastSyncStatus = -1; + bool _debug = false; + bool _ticker = true; + +public: + SoftRTCsyncStatus(String parameters) : IoTItem(parameters) + { + _round = 0; + jsonRead(parameters, F("ticker"), _ticker); + jsonRead(parameters, F("debug"), _debug); + } + + void loop() + { + if (syncStatus != lastSyncStatus) + { + lastSyncStatus = syncStatus; + if (_debug) + SerialPrint("i", F("SoftRTC"), "Статус синхронизации: " + (String)lastSyncStatus); + value.isDecimal = true; + value.valD = lastSyncStatus; + regEvent(value.valD, F("SoftRTCsyncStatus"), _debug, _ticker); + } + IoTItem::loop(); + } + + ~SoftRTCsyncStatus() {} +}; + +void *getAPI_SoftRTC(String subtype, String param) +{ + if (subtype == F("SoftRTC")) + { + return new SoftRTC(param); + } + else if (subtype == F("SoftRTCsyncStatus")) + { + return new SoftRTCsyncStatus(param); + } + return nullptr; +} \ No newline at end of file diff --git a/src/modules/sensors/SoftRTC/export .json b/src/modules/sensors/SoftRTC/export .json new file mode 100644 index 00000000..1bb544be --- /dev/null +++ b/src/modules/sensors/SoftRTC/export .json @@ -0,0 +1,264 @@ +{ + "mark": "iotm", + "config": [ + { + "global": 0, + "type": "Reading", + "subtype": "Variable", + "id": "voutTime", + "needSave": 0, + "widget": "anydataDef", + "page": "Time", + "descr": "Local Time", + "int": "0", + "val": "0.0", + "map": "", + "plus": 0, + "multiply": 1, + "round": 0 + }, + { + "global": 0, + "type": "Writing", + "subtype": "Timer", + "id": "timer1", + "widget": "anydataDef", + "page": "Timers", + "descr": "Timer", + "int": 1, + "countDown": "15", + "ticker": "0", + "repeat": 1, + "needSave": 0 + }, + { + "global": 0, + "type": "Reading", + "subtype": "Variable", + "id": "voutNTPtimeCorrection", + "needSave": 0, + "widget": "anydataDef", + "page": "Time", + "descr": "Last NTP Time Correction", + "int": "0", + "val": "0.0", + "map": "", + "plus": 0, + "multiply": 1, + "round": 0 + }, + { + "global": 0, + "type": "Reading", + "subtype": "Variable", + "id": "voutSyncStatus", + "needSave": 0, + "widget": "anydataDef", + "page": "Time", + "descr": "SoftRTC Sync Status", + "int": "0", + "val": "0.0", + "map": "1024,1024,1,100", + "plus": 0, + "multiply": 1, + "round": 0 + }, + { + "global": 0, + "type": "Reading", + "subtype": "Variable", + "id": "voutTimeSoft", + "needSave": 0, + "widget": "anydataDef", + "page": "Time", + "descr": "Local Time Soft", + "int": "0", + "val": "0.0", + "map": "", + "plus": 0, + "multiply": 1, + "round": 0 + }, + { + "global": 0, + "type": "Reading", + "subtype": "SoftRTCsyncStatus", + "id": "softRTCsyncStatus", + "widget": "anydataDef", + "page": "Time", + "descr": "SoftRTC Sync Status", + "ticker": "1", + "int": 10, + "round": 0, + "debug": 1 + }, + { + "global": 0, + "type": "Reading", + "subtype": "SoftRTC", + "id": "softRTC", + "widget": "anydataDef", + "page": "Timers", + "descr": "UT softRTC", + "winterTime": 2, + "summerTime": 3, + "ticker": 0, + "int": 10, + "debug": 1, + "btn-setUTime": "0", + "btn-setSysTime": "d-m-y h:m:s", + "moduleName": "SoftRTC" + }, + { + "global": 0, + "type": "Reading", + "subtype": "Variable", + "id": "dayOfWeek", + "needSave": 0, + "widget": "anydataDef", + "page": "Days", + "descr": "Day of Week", + "int": "0", + "val": "0.0", + "map": "1024,1024,1,100", + "plus": 0, + "multiply": 1, + "round": 0, + "moduleName": "Variable" + }, + { + "global": 0, + "type": "Reading", + "subtype": "Variable", + "id": "weekNumber", + "needSave": 0, + "widget": "anydataDef", + "page": "Days", + "descr": " Week Number", + "int": "0", + "val": "0.0", + "map": "1024,1024,1,100", + "plus": 0, + "multiply": 1, + "round": 0, + "moduleName": "Variable" + }, + { + "global": 0, + "type": "Reading", + "subtype": "Variable", + "id": "dayOfWeekT", + "needSave": 0, + "widget": "anydataDef", + "page": "Days", + "descr": "Day of Week", + "int": "0", + "val": "0.0", + "map": "", + "plus": 0, + "multiply": 1, + "round": 0, + "moduleName": "Variable" + }, + { + "global": 0, + "type": "Reading", + "subtype": "Variable", + "id": "getDayOfYear", + "needSave": 0, + "widget": "anydataDef", + "page": "Days", + "descr": "Day of Year", + "int": "0", + "val": "0.0", + "map": "1024,1024,1,100", + "plus": 0, + "multiply": 1, + "round": 0, + "moduleName": "Variable" + }, + { + "global": 0, + "type": "Reading", + "subtype": "Variable", + "id": "getDaysInMonth", + "needSave": 0, + "widget": "anydataDef", + "page": "Days", + "descr": "Days in Month", + "int": "0", + "val": "0.0", + "map": "1024,1024,1,100", + "plus": 0, + "multiply": 1, + "round": 0, + "moduleName": "Variable" + }, + { + "global": 0, + "type": "Reading", + "subtype": "Variable", + "id": "getOrthodoxEaster", + "needSave": 0, + "widget": "anydataDef", + "page": "Days", + "descr": "Orthodox Easter", + "int": "0", + "val": "0.0", + "map": "1024,1024,1,100", + "plus": 0, + "multiply": 1, + "round": 0, + "moduleName": "Variable" + }, + { + "global": 0, + "type": "Reading", + "subtype": "VButton", + "id": "isOrthodoxEasterB", + "needSave": 0, + "widget": "toggle", + "page": "Days", + "descr": "is Orthodox Easter", + "int": "0", + "val": "0", + "moduleName": "VButton" + } + ] +} + +scenario=> + + +if timer1 == 0 then { +voutTime = getTime();# не сработает если время не обновлено +voutTimeSoft = softRTC.getTime(); +dayOfWeek = softRTC.getDayOfWeek() +voutNTPtimeCorrection = softRTC.lastNTPtimeCorrection() +weekNumber = softRTC.getWeekNumber() +getDayOfYear = softRTC.getDayOfYear() +getDaysInMonth = softRTC.getDaysInMonth() +getOrthodoxEaster = softRTC.getOrthodoxEaster() +isOrthodoxEasterB = softRTC.isOrthodoxEaster() +} + +if dayOfWeek == 1 then dayOfWeekT = "Monday" +if dayOfWeek == 2 then dayOfWeekT = "Tuesday" +if dayOfWeek == 3 then dayOfWeekT = "Wednesday" +if dayOfWeek == 4 then dayOfWeekT = "Thursday" +if dayOfWeek == 5 then dayOfWeekT = "Friday" +if dayOfWeek == 6 then dayOfWeekT = "Saturday" +if dayOfWeek == 7 then dayOfWeekT = "Sunday" + +if softRTCsyncStatus == 0 then voutSyncStatus = "Not set"; +if softRTCsyncStatus == 1 then voutSyncStatus = "Set before SoftRTC"; +if softRTCsyncStatus == 2 then voutSyncStatus = "Restored"; +if softRTCsyncStatus == 3 then voutSyncStatus = "Set manually"; +if softRTCsyncStatus == 4 then voutSyncStatus = "Set with NTP or Browser"; +if softRTCsyncStatus == 5 then voutSyncStatus = "Set with Browser"; +if softRTCsyncStatus == 6 then voutSyncStatus = "Just set with NTP"; +if softRTCsyncStatus == 7 then voutSyncStatus = "Set with NTP"; + +if softRTC then {voutNTPtimeCorrection = softRTC.lastNTPtimeCorrection() +} + diff --git a/src/modules/sensors/SoftRTC/modinfo.json b/src/modules/sensors/SoftRTC/modinfo.json new file mode 100644 index 00000000..b0e81c6c --- /dev/null +++ b/src/modules/sensors/SoftRTC/modinfo.json @@ -0,0 +1,161 @@ +{ + "menuSection": "sensors", + "configItem": [ + { + "global": 0, + "name": "Часы реального времени (программные)", + "type": "Reading", + "subtype": "SoftRTC", + "id": "softRTC", + "widget": "anydataDef", + "page": "Таймеры", + "descr": "UT softRTC", + "winterTime": 2, + "summerTime": 3, + "ticker": 0, + "int": 10, + "debug": 1, + "btn-setUTime": "0", + "btn-setSysTime": "d-m-y h:m:s" + }, + { + "global": 0, + "name": "Часы реального времени (программные) Статус", + "type": "Reading", + "subtype": "SoftRTCsyncStatus", + "id": "softRTCsyncStatus", + "widget": "anydataDef", + "page": "Таймеры", + "descr": "softRTC Sync Status", + "ticker": 1, + "debug": 1 + } + ], + "about": { + "authorName": "Alex K", + "authorContact": "https://t.me/cmche", + "authorGit": "https://github.com/CHE77/IoTManager-Modules", + "exampleURL": "https://iotmanager.org/wiki", + "specialThanks": "", + "moduleName": "SoftRTC", + "moduleVersion": "1.0", + "usedRam": { + "esp32_4mb": 15, + "esp8266_4mb": 15 + }, + "title": "Часы реального времени программные", + "moduleDesc": "Позволяет хранить время в энергонезависимой памяти.", + "license": "Cooperative Non-Violent Public License (CNPL)", + "propInfo": { + "ticker": "Генерировать(1) или нет(0) события при каждом тике часов (каждые int секунд).", + "debug": "Вывод отладочной информации", + "int": "Количество секунд между сохраниеями времени. Желательно чётное.", + "btn-setUTime": "Кнопка установки времени на основе указанного unixtime", + "btn-setSysTime": "Кнопка установки времени на основе указанного в формате d-m-y h:m:s или y-m-d h:m:s" + }, + "retInfo": "Содержит сохраненное Время в энергонезависимую память", + "funcInfo": [ + { + "name": "getTime", + "descr": "Получить строковое значение времени по указанному формату.", + "params": [ + "SoftRTC.getTime()" + ] + }, + { + "name": "getUTShort", + "descr": "Получить строковое значение времени по указанному формату.", + "params": [ + "SoftRTC.getUTShort()" + ] + }, + { + "name": "setUnixTime", + "descr": "Установить время через сценарий в формате юникстайм", + "params": [ + "SoftRTC.setUnixTime(1743087041) - параметр в виде числа или строки" + ] + }, + { + "name": "setTimeFromYMDHMS", + "descr": "Установить время через сценарий в формате YYYY,MM,DD,HH,MM,SS", + "params": [ + "softRTC.setTimeFromYMDHMS(2025,01,01,12,30,30) " + ] + }, + { + "name": "lastNTPtimeCorrection", + "descr": "Вывести поправку после синхронизации по NTP", + "params": [ + " softRTC.lastNTPtimeCorrection() " + ] + }, + { + "name": "getWeekNumber", + "descr": "Вывести номер недели в году", + "params": [ + " softRTC.getWeekNumber() " + ] + }, + { + "name": "getDayOfWeek", + "descr": "Вывести день недели. Воскресенье (0) → 7", + "params": [ + " softRTC.getDayOfWeek() " + ] + }, + { + "name": "getYear", + "descr": "Вывести год", + "params": [ + " softRTC.getYear() " + ] + }, + { + "name": "getDayOfYear", + "descr": "Вывести день в году", + "params": [ + " softRTC.getDayOfYear() " + ] + }, + { + "name": "getOrthodoxEaster", + "descr": "Вывести дату Пасхи в текущем году", + "params": [ + " softRTC.getOrthodoxEaster() " + ] + }, + { + "name": "isOrthodoxEaster", + "descr": "уже Пасха?", + "params": [ + " softRTC.isOrthodoxEaster() " + ] + } + ] + , + "title2": "Часы реального времени программные Статус", + "moduleDesc2": "Позволяет получать статус синхроницации времени", + "propInfo2": { + "ticker": "Генерировать(1) или нет(0) события при каждом изменении статуса.", + "debug": "Вывод отладочной информации" + }, + "retInfo2": "Содержит статус синхроницации времени", + "Status": { + "0": "Время не установлено", + "1": "Время было установлено до SoftRTC", + "2": "Время восстановлено из памяти", + "3": "Время установлено вручную", + "4": "Время синхронизировано через NTP или через Браузер", + "5": "Время синхронизировано через Браузер", + "6": "Время только что синхронизировано через NTP", + "7": "Время синхронизировано через NTP" + } + + }, + "defActive": false, + "usedLibs": { + "esp32*": [], + "esp82*": [] + } +} \ No newline at end of file diff --git a/src/modules/virtual/SolarCalc/SolarCalc.cpp b/src/modules/virtual/SolarCalc/SolarCalc.cpp new file mode 100644 index 00000000..bc0e7680 --- /dev/null +++ b/src/modules/virtual/SolarCalc/SolarCalc.cpp @@ -0,0 +1,488 @@ +// Licensed under the Cooperative Non-Violent Public License (CNPL) +// See: https://github.com/CHE77/IoTManager-Modules/blob/main/LICENSE + +#include "Global.h" +#include "classes/IoTItem.h" +// #include +#include +#include "NTP.h" + +class SolarCalculator : public IoTItem +{ +private: + float _lat = 0; + float _long = 0; + + int year = 0; + int month = 0; + int day = 0; + int hour = 0; + int minute = 0; + int second = 0; + + unsigned long _sunriseTime = 0; + unsigned long _transitTime = 0; + unsigned long _sunsetTime = 0; + + String _parameter = ""; + bool _ticker = false; + bool _debug = false; + + // Rounded HH:mm format + char *hoursToString(double h, char *str) + { + int m = int(round(h * 60)); + int hr = (m / 60) % 24; + int mn = m % 60; + + str[0] = (hr / 10) % 10 + '0'; + str[1] = (hr % 10) + '0'; + str[2] = ':'; + str[3] = (mn / 10) % 10 + '0'; + str[4] = (mn % 10) + '0'; + str[5] = '\0'; + return str; + } + +public: + SolarCalculator(String parameters) : IoTItem(parameters) + { + jsonRead(parameters, "lat", _lat); + jsonRead(parameters, "lon", _long); + _parameter = jsonReadStr(parameters, "parameter"); + jsonRead(parameters, F("ticker"), _ticker); + jsonRead(parameters, F("debug"), _debug); + } + + void getTime() + { + unixTime = getSystemTime(); + breakEpochToTime(unixTime, _time_utc); + + day = _time_utc.day_of_month; + month = _time_utc.month; + year = _time_utc.year + 2000; // 0-25 + + hour = _time_utc.hour; + minute = _time_utc.minute; + second = _time_utc.second; + } + + void sunArcFromTransit(double longitude, double &sunArc) + { + JulianDay jd(year, month, day, hour, minute, second); + double T = calcJulianCent(jd); + + // Получаем экваториальные координаты Солнца + double ra, dec; + calcSolarCoordinates(T, ra, dec); // ra в градусах, dec тоже + + double GMST = calcGrMeanSiderealTime(jd); + double ghaSun = wrapTo360(GMST - ra); + + // LHA = GHA + долгота наблюдателя (восточная долгота положительная) + double lha = wrapTo180(ghaSun + longitude); + + // Угол от транзита: 0° — транзит, 90° — 6 часов позже и т.д. + sunArc = lha; + } + + void doByInterval() + { + if (isTimeSynch) + { + getTime(); + JulianDay jd(year, month, day, hour, minute, second); + + double azimuth, elevation; + calcHorizontalCoordinates(jd, _lat, _long, azimuth, elevation); + + value.isDecimal = true; + if (_parameter == "azimuth") + { + value.valD = azimuth; + } + else if (_parameter == "elevation") + { + value.valD = elevation; + } + else if (_parameter == "sunArcFromTransit") + { + double sunArc; + sunArcFromTransit(_long, sunArc); + value.valD = sunArc; + } + else + { + SerialPrint("E", F("SolarCalculator"), _parameter + " is not correct parameter!!!"); + return; + } + + regEvent(value.valD, F("SoftRTC"), _debug, _ticker); + } + } + + IoTValue execute(String command, std::vector ¶m) + { + if (command == "sunrise") + { + if (param.size() == 0 && isTimeSynch) + { + getTime(); + } + else if (param.size() == 3 && param[0].isDecimal && param[1].isDecimal && param[2].isDecimal) + { + year = param[2].valD; + month = param[1].valD; + day = param[0].valD; + } + else + { + SerialPrint("E", F("SolarCalculator"), "Wrong parameter!s or time is not synched!!", _id); + return {}; + } + + double transit, sunrise, sunset; + calcSunriseSunset(year, month, day, _lat, _long, transit, sunrise, sunset); + + int utc_offset = jsonReadInt(settingsFlashJson, F("timezone")); + + char str[6]; + IoTValue valTmp; + valTmp.isDecimal = false; + valTmp.valS = hoursToString(sunrise + utc_offset, str); + return valTmp; + } + else if (command == "transit") + { + if (param.size() == 0 && isTimeSynch) + { + getTime(); + } + else if (param.size() == 3 && param[0].isDecimal && param[1].isDecimal && param[2].isDecimal) + { + year = param[2].valD; + month = param[1].valD; + day = param[0].valD; + } + else + { + SerialPrint("E", F("SolarCalculator"), "Wrong parameter!s or time is not synched!!", _id); + return {}; + } + + double transit, sunrise, sunset; + calcSunriseSunset(year, month, day, _lat, _long, transit, sunrise, sunset); + + int utc_offset = jsonReadInt(settingsFlashJson, F("timezone")); + + char str[6]; + IoTValue valTmp; + valTmp.isDecimal = false; + valTmp.valS = hoursToString(transit + utc_offset, str); + return valTmp; + } + else if (command == "sunset") + { + + if (param.size() == 0 && isTimeSynch) + { + getTime(); + } + else if (param.size() == 3 && param[0].isDecimal && param[1].isDecimal && param[2].isDecimal) + { + day = param[0].valD; + month = param[1].valD; + year = param[2].valD; + } + else + { + SerialPrint("E", F("SolarCalculator"), "Wrong parameter!s or time is not synched!!", _id); + return {}; + } + + double transit, sunrise, sunset; + calcSunriseSunset(year, month, day, _lat, _long, transit, sunrise, sunset); + + int utc_offset = jsonReadInt(settingsFlashJson, F("timezone")); + + char str[6]; + IoTValue valTmp; + valTmp.isDecimal = false; + valTmp.valS = hoursToString(sunset + utc_offset, str); + return valTmp; + } + else if (command == "azimuth") + { + if (param.size() == 0 && isTimeSynch) + { + getTime(); + } + else if (param.size() > 3) + { + day = param[0].valD; + month = param[1].valD; + year = param[2].valD; + hour = param[3].valD; + minute = (param.size() > 4) ? param[4].valD : 0; + second = (param.size() > 5) ? param[5].valD : 0; + } + else + { + SerialPrint("E", F("SolarCalculator"), "Wrong parameter!s or time is not synched!!", _id); + return {}; + } + + JulianDay jd(year, month, day, hour, minute, second); + double azimuth, elevation; + calcHorizontalCoordinates(jd, _lat, _long, azimuth, elevation); + + IoTValue valTmp; + valTmp.isDecimal = true; + valTmp.valD = azimuth; + return valTmp; + } + else if (command == "elevation") + { + if (param.size() == 0 && isTimeSynch) + { + getTime(); + } + else if (param.size() > 3) + { + day = param[0].valD; + month = param[1].valD; + year = param[2].valD; + hour = param[3].valD; + minute = (param.size() > 4) ? param[4].valD : 0; + second = (param.size() > 5) ? param[5].valD : 0; + } + else + { + SerialPrint("E", F("SolarCalculator"), "Wrong parameter!s or time is not synched!!", _id); + return {}; + } + + JulianDay jd(year, month, day, hour, minute, second); + double azimuth, elevation; + calcHorizontalCoordinates(jd, _lat, _long, azimuth, elevation); + + IoTValue valTmp; + valTmp.isDecimal = true; + valTmp.valD = elevation; + return valTmp; + } + else if (command == "sunArcFromTransit") + { + if (param.size() == 0 && isTimeSynch) + { + getTime(); + } + else if (param.size() > 3) + { + day = param[0].valD; + month = param[1].valD; + year = param[2].valD; + hour = param[3].valD; + minute = (param.size() > 4) ? param[4].valD : 0; + second = (param.size() > 5) ? param[5].valD : 0; + } + else + { + SerialPrint("E", F("SolarCalculator"), "Wrong parameter!s or time is not synched!!", _id); + return {}; + } + + double sunArc; + sunArcFromTransit(_long, sunArc); + + IoTValue valTmp; + valTmp.isDecimal = true; + valTmp.valD = sunArc; + return valTmp; + } + else if (command == "jd") + { + // float hours = 0; + if (param.size() == 0 && isTimeSynch) + { + getTime(); + } + else if (param.size() > 3) + { + day = param[0].valD; + month = param[1].valD; + year = param[2].valD; + hour = param[3].valD; + minute = (param.size() > 4) ? param[4].valD : 0; + second = (param.size() > 5) ? param[5].valD : 0; + } + else + { + SerialPrint("E", F("SolarCalculator"), "Wrong parameters or time is not synched!!", _id); + return {}; + } + JulianDay jd(year, month, day, hour, minute, second); + float jdJD = jd.JD; + float jdm = jd.m; + float jdSum = jdJD + jdm; + //SerialPrint("I", F("SolarCalc"), "jdJD = " + String(jdJD)); + //SerialPrint("I", F("SolarCalc"), "jdm = " + String(jdm)); + //SerialPrint("I", F("SolarCalc"), "jdSum = " + String(jdSum)); + IoTValue valTmp; + valTmp.isDecimal = true; + valTmp.valD = jdSum; + return valTmp; + } + else if (command == "GMST") + { + if (param.size() == 0 && isTimeSynch) + { + getTime(); + } + else if (param.size() > 3) + { + day = param[0].valD; + month = param[1].valD; + year = param[2].valD; + hour = param[3].valD; + minute = (param.size() > 4) ? param[4].valD : 0; + second = (param.size() > 5) ? param[5].valD : 0; + } + else + { + SerialPrint("E", F("SolarCalculator"), "Wrong parameters or time is not synched!!", _id); + return {}; + } + JulianDay jd(year, month, day, hour, minute, second); + double GMST = calcGrMeanSiderealTime(jd); + + IoTValue valTmp; + valTmp.isDecimal = true; + valTmp.valD = GMST; + return valTmp; + } + else if (command == "LST") + { + if (param.size() == 0 && isTimeSynch) + { + getTime(); + } + else if (param.size() > 3) + { + day = param[0].valD; + month = param[1].valD; + year = param[2].valD; + hour = param[3].valD; + minute = (param.size() > 4) ? param[4].valD : 0; + second = (param.size() > 5) ? param[5].valD : 0; + } + else + { + SerialPrint("E", F("SolarCalculator"), "Wrong parameters or time is not synched!!", _id); + return {}; + } + + JulianDay jd(year, month, day, hour, minute, second); + double GMST = calcGrMeanSiderealTime(jd); + double LST = wrapTo360(GMST + _long); + + IoTValue valTmp; + valTmp.isDecimal = true; + valTmp.valD = LST; + return valTmp; + } + else if (command == "ra") + { + if (param.size() == 0 && isTimeSynch) + { + getTime(); + } + else if (param.size() > 3) + { + day = param[0].valD; + month = param[1].valD; + year = param[2].valD; + hour = param[3].valD; + minute = (param.size() > 4) ? param[4].valD : 0; + second = (param.size() > 5) ? param[5].valD : 0; + } + else + { + SerialPrint("E", F("SolarCalculator"), "Wrong parameters or time is not synched!!", _id); + return {}; + } + + JulianDay jd(year, month, day, hour, minute, second); + double T = calcJulianCent(jd); + double ra, dec; + calcSolarCoordinates(T, ra, dec); + + IoTValue valTmp; + valTmp.isDecimal = true; + valTmp.valD = ra; + return valTmp; + } + else if (command == "dec") + { + if (param.size() == 0 && isTimeSynch) + { + getTime(); + } + else if (param.size() > 3) + { + day = param[0].valD; + month = param[1].valD; + year = param[2].valD; + hour = param[3].valD; + minute = (param.size() > 4) ? param[4].valD : 0; + second = (param.size() > 5) ? param[5].valD : 0; + } + else + { + SerialPrint("E", F("SolarCalculator"), "Wrong parameters or time is not synched!!", _id); + return {}; + } + + JulianDay jd(year, month, day, hour, minute, second); + double T = calcJulianCent(jd); + double ra, dec; + calcSolarCoordinates(T, ra, dec); + + IoTValue valTmp; + valTmp.isDecimal = true; + valTmp.valD = dec; + return valTmp; + } + else if (command == "getHour" && param.size() == 1) + { // получаем час из какого-то элемента и переводим его в UTC + + int h = selectToMarker(param[0].valS, ":").toInt(); + int utc_offset = jsonReadInt(settingsFlashJson, F("timezone")); + IoTValue valTmp; + valTmp.isDecimal = true; + valTmp.valD = h - utc_offset; + return valTmp; + } + else if (command == "getMinute" && param.size() == 1) + { // получаем минуты из какого-то элемента и переводим его в UTC + int min = selectToMarkerLast(param[0].valS, ":").toInt(); + IoTValue valTmp; + valTmp.isDecimal = true; + valTmp.valD = min; + return valTmp; + } + else + SerialPrint("E", F("SolarCalculator"), F("Unknown command or wrong parameters.")); + return {}; + } +}; + +void *getAPI_SolarCalculator(String subtype, String param) +{ + if (subtype == F("SolarCalculator")) + { + return new SolarCalculator(param); + } + return nullptr; +} diff --git a/src/modules/virtual/SolarCalc/coordinates.jpg b/src/modules/virtual/SolarCalc/coordinates.jpg new file mode 100644 index 0000000000000000000000000000000000000000..9beae48da987a3577ee5a4dd07664e4913472879 GIT binary patch literal 225470 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 timerDay == 0 | CalculateBtn == 1 then { +RiseTimeToday = Sun.sunrise() +TransitTimeToday = Sun.transit() +SetTimeToday = Sun.sunset() + +hourRiseToday = Sun.getHour(RiseTimeToday) +minuteRiseToday = Sun.getMinute(RiseTimeToday) + +if RiseTimeToday then azimuthRise = Sun.azimuth(getDay(),getMonth(),2025, Sun.getHour(RiseTimeToday) ,Sun.getMinute(RiseTimeToday)) + +if TransitTimeToday then {azimuthTransit = Sun.azimuth(getDay(),getMonth(),2025, Sun.getHour(TransitTimeToday),Sun.getMinute(TransitTimeToday)) + +elevationTransit = Sun.elevation(getDay(),getMonth(),2025, Sun.getHour(TransitTimeToday) ,Sun.getMinute(TransitTimeToday)) +} + +if SetTimeToday then azimuthSet = Sun.azimuth(getDay(),getMonth(),2025, Sun.getHour(SetTimeToday) ,Sun.getMinute(SetTimeToday)) + +CalculateBtn = 0 +} + +if timerMinute == 0 | CalculateBtn == 1 then { + +jd = Sun.jd() +GMST = Sun.GMST() +LST = Sun.LST() +ra = Sun.ra() +dec = Sun.dec() +GHA = GMST - ra +LHA = GHA - 28.8448 + +azimuth = Sun.azimuth() +elevation = Sun.elevation() +sunArcFromTransit = Sun.sunArcFromTransit() + +if elevation > 0 then isDayTime = 1 else isDayTime = 0 + +CalculateBtn = 0 +} + +if cronMidnight then CalculateBtn = 1 +if onStart then CalculateBtn = 1 \ No newline at end of file diff --git a/src/modules/virtual/SolarCalc/modinfo.json b/src/modules/virtual/SolarCalc/modinfo.json new file mode 100644 index 00000000..802a2743 --- /dev/null +++ b/src/modules/virtual/SolarCalc/modinfo.json @@ -0,0 +1,167 @@ +{ + "menuSection": "virtual_elments", + "configItem": [ + { + "global": 0, + "name": "Solar Calculator", + "type": "Reading", + "subtype": "SolarCalculator", + "id": "Sun", + "widget": "anydataCorner", + "page": "Математика", + "descr": "Azimuth", + "lat": 47.0159, + "lon": 28.8448, + "parameter": "azimuth", + "round": 1, + "int": 60, + "ticker": 1, + "debug": 0 + + } + ], + "about": { + "authorName": "Alex", + "authorContact": "https://t.me/cmche", + "authorGit": "https://github.com/CHE77/IoTManager-Modules", + "exampleURL": "https://iotmanager.org/wiki", + "specialThanks": "", + "moduleName": "SolarCalculator", + "moduleVersion": "1.0", + "usedRam": { + "esp32_4mb": 15, + "esp8266_4mb": 15 + }, + "title": "Модуль SolarCalc", + "moduleDesc": "Модуль для расчета положения Солнца в различных системах координат. Требуют обновленного времени: через интернет, RTC, SoftRTC", + "license": "Cooperative Non-Violent Public License (CNPL)", + "propInfo": { + }, + "funcInfo": [ + { + "name": "sunrise", + "descr": "Расчет времени восхода", + "params": [ + "Sun.sunrise() - на текущую дату", + "Sun.sunrise(день, месяц, год) - на заданую дату" + ] + }, + { + "name": "transit", + "descr": "Расчет времени транзита", + "params": [ + "Sun.transit() - на текущую дату", + "Sun.transit(день, месяц, год) - на заданую дату" + ] + }, + { + "name": "sunset", + "descr": "Расчет времени захода", + "params": [ + "Sun.sunset() - на текущую дату", + "Sun.sunset(день, месяц, год) - на заданую дату" + ] + }, + { + "name": "azimuth", + "descr": "Расчет азимута", + "params": [ + "Sun.azimuth() - на текущий момент времени", + "Sun.azimuth(день, месяц, год, час, минут, секунд) - на заданое веремя", + "Sun.azimuth(день, месяц, год, час, минут) - на заданое веремя", + "Sun.azimuth(день, месяц, год, час) - на заданое веремя" + ] + }, + { + "name": "elevation", + "descr": "Расчет высоты (над горизонтом)", + "params": [ + "Sun.elevation() - на текущий момент времени", + "Sun.elevation(день, месяц, год, час, минут, секунд) - на заданое веремя", + "Sun.elevation(день, месяц, год, час, минут) - на заданое веремя", + "Sun.elevation(день, месяц, год, час) - на заданое веремя" + ] + }, + { + "name": "sunArcFromTransit", + "descr": "Расчет дуги эклиптики от зенита", + "params": [ + "Sun.sunArcFromTransit() - на текущий момент времени", + "Sun.sunArcFromTransit(день, месяц, год, час, минут, секунд) - на заданое веремя", + "Sun.sunArcFromTransit(день, месяц, год, час, минут) - на заданое веремя", + "Sun.sunArcFromTransit(день, месяц, год, час) - на заданое веремя" + ] + }, + { + "name": "jd", + "descr": "Расчет Юлинаской даты", + "params": [ + "Sun.jd() - на текущий момент времени", + "Sun.jd(день, месяц, год, час, минут, секунд) - на заданое веремя", + "Sun.jd(день, месяц, год, час, минут) - на заданое веремя", + "Sun.jd(день, месяц, год, час) - на заданое веремя" + ] + }, + { + "name": "GMST", + "descr": "Расчет среднего звездного времени на меридиане Гринвича", + "params": [ + "Sun.GMST() - на текущий момент времени", + "Sun.GMST(день, месяц, год, час, минут, секунд) - на заданое веремя", + "Sun.GMST(день, месяц, год, час, минут) - на заданое веремя", + "Sun.GMST(день, месяц, год, час) - на заданое веремя" + ] + }, + { + "name": "LST", + "descr": "Расчет Местного звездного времени", + "params": [ + "Sun.LST() - на текущий момент времени", + "Sun.LST(день, месяц, год, час, минут, секунд) - на заданое веремя", + "Sun.LST(день, месяц, год, час, минут) - на заданое веремя", + "Sun.LST(день, месяц, год, час) - на заданое веремя" + ] + }, + { + "name": "ra", + "descr": "Расчет Прямого Восхождения", + "params": [ + "Sun.ra() - на текущий момент времени", + "Sun.ra(день, месяц, год, час, минут, секунд) - на заданое веремя", + "Sun.ra(день, месяц, год, час, минут) - на заданое веремя", + "Sun.ra(день, месяц, год, час) - на заданое веремя" + ] + }, + { + "name": "dec", + "descr": "Расчет Склонения", + "params": [ + "Sun.dec() - на текущий момент времени", + "Sun.dec(день, месяц, год, час, минут, секунд) - на заданое веремя", + "Sun.dec(день, месяц, год, час, минут) - на заданое веремя", + "Sun.dec(день, месяц, год, час) - на заданое веремя" + ] + }, + { + "name": "getHour", + "descr": "Получаем час из какого-то элемента и переводим его в UTC", + "params": [ + "Sun.getHour(HH:mm)" + ] + }, + { + "name": "getMinute", + "descr": "Получаем минуты из какого-то элемента", + "params": [ + "Sun.getMinute(HH:mm)" + ] + } + ] + }, + "defActive": false, + "usedLibs": { + "esp32*": ["jpb10/SolarCalculator@^2.0.1"], + "esp82*": ["jpb10/SolarCalculator@^2.0.1"], + "bk72*": ["jpb10/SolarCalculator@^2.0.1"] + } +}