mirror of
https://github.com/IoTManagerProject/IoTManager.git
synced 2026-03-26 14:12:16 +03:00
new Modules from Chat
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -1,182 +0,0 @@
|
||||
#pragma once
|
||||
#include <Arduino.h>
|
||||
|
||||
#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 - не было команды.
|
||||
*/
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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;
|
||||
}
|
||||
}
|
||||
459
src/modules/exec/EctoControlAdapter/export - Копия.json
Normal file
459
src/modules/exec/EctoControlAdapter/export - Копия.json
Normal file
@@ -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);
|
||||
@@ -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": []
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
162
src/modules/exec/WakeOnLanModule/WakeOnLanModule.cpp
Normal file
162
src/modules/exec/WakeOnLanModule/WakeOnLanModule.cpp
Normal file
@@ -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 <WiFiUdp.h>
|
||||
#include <WakeOnLan.h>
|
||||
|
||||
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<IoTValue> ¶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;
|
||||
}
|
||||
}
|
||||
295
src/modules/exec/WakeOnLanModule/export.json
Normal file
295
src/modules/exec/WakeOnLanModule/export.json
Normal file
@@ -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
|
||||
}
|
||||
48
src/modules/exec/WakeOnLanModule/modinfo.json
Normal file
48
src/modules/exec/WakeOnLanModule/modinfo.json
Normal file
@@ -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"]
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
359
src/modules/sensors/NoiseAdc/NoiseAdc.cpp
Normal file
359
src/modules/sensors/NoiseAdc/NoiseAdc.cpp
Normal file
@@ -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<IoTValue> ¶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;
|
||||
}
|
||||
}
|
||||
89
src/modules/sensors/NoiseAdc/export.json
Normal file
89
src/modules/sensors/NoiseAdc/export.json
Normal file
@@ -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
|
||||
|
||||
|
||||
70
src/modules/sensors/NoiseAdc/modinfo.json
Normal file
70
src/modules/sensors/NoiseAdc/modinfo.json
Normal file
@@ -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*": []
|
||||
}
|
||||
}
|
||||
579
src/modules/sensors/Presence/Presence.cpp
Normal file
579
src/modules/sensors/Presence/Presence.cpp
Normal file
@@ -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 <math.h>
|
||||
#define EARTH_RADIUS_KM 6371.0 // Радиус Земли в километрах
|
||||
|
||||
#include "NTP.h"
|
||||
#include <PubSubClient.h> // чтобы знать тип
|
||||
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<String> 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<JsonObject>();
|
||||
|
||||
if (obj.containsKey("chargingState"))
|
||||
{
|
||||
pdata.chargingState = obj["chargingState"].as<String>();
|
||||
}
|
||||
if (obj.containsKey("plugState"))
|
||||
{
|
||||
pdata.plugState = obj["plugState"].as<String>();
|
||||
}
|
||||
|
||||
if (obj.containsKey("connectedWifi"))
|
||||
pdata.connectedWifi = obj["connectedWifi"].as<String>();
|
||||
if (obj.containsKey("geoLocation"))
|
||||
{
|
||||
pdata.geoLocation = obj["geoLocation"].as<String>();
|
||||
// Разбор геолокации после получения
|
||||
parseGeo(pdata.geoLocation);
|
||||
pdata.geoTime = getDateTimeDotFormatedFromUnix(pdata.geoTimestamp);
|
||||
SerialPrint("i", "Presence", "GeoTime : " + pdata.geoTime);
|
||||
}
|
||||
|
||||
if (obj.containsKey("deviceName"))
|
||||
pdata.deviceName = obj["deviceName"].as<String>();
|
||||
if (obj.containsKey("batteryLevel"))
|
||||
pdata.batteryLevel = obj["batteryLevel"].as<int>();
|
||||
if (obj.containsKey("currentTimestamp"))
|
||||
{
|
||||
pdata.currentTimestamp = obj["currentTimestamp"].as<unsigned long>();
|
||||
pdata.currentTime = getDateTimeDotFormatedFromUnix(pdata.currentTimestamp);
|
||||
}
|
||||
|
||||
if (obj.containsKey("nextScheduledTimestamp"))
|
||||
{
|
||||
pdata.nextScheduledTimestamp = obj["nextScheduledTimestamp"].as<unsigned long>();
|
||||
pdata.nextScheduledTime = getDateTimeDotFormatedFromUnix(pdata.nextScheduledTimestamp);
|
||||
}
|
||||
|
||||
if (obj.containsKey("nextAlarmclockTimestamp"))
|
||||
{
|
||||
pdata.nextAlarmclockTimestamp = obj["nextAlarmclockTimestamp"].as<unsigned long>();
|
||||
pdata.nextAlarmclockTime = getDateTimeDotFormatedFromUnix(pdata.nextAlarmclockTimestamp);
|
||||
}
|
||||
|
||||
if (obj.containsKey("conditionContent"))
|
||||
{
|
||||
JsonArray arr = obj["conditionContent"].as<JsonArray>();
|
||||
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<const char *>())
|
||||
{
|
||||
sensorVal = obj[_parameter].as<const char *>();
|
||||
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<IoTValue> ¶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;
|
||||
}
|
||||
}
|
||||
372
src/modules/sensors/Presence/export.json
Normal file
372
src/modules/sensors/Presence/export.json
Normal file
@@ -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()
|
||||
}
|
||||
199
src/modules/sensors/Presence/modinfo.json
Normal file
199
src/modules/sensors/Presence/modinfo.json
Normal file
@@ -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*": []
|
||||
}
|
||||
}
|
||||
BIN
src/modules/sensors/Presence/preview.png
Normal file
BIN
src/modules/sensors/Presence/preview.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 69 KiB |
383
src/modules/sensors/Presence/widgets.json
Normal file
383
src/modules/sensors/Presence/widgets.json
Normal file
@@ -0,0 +1,383 @@
|
||||
[
|
||||
{
|
||||
"name": "anydataRed",
|
||||
"label": "Сообщение1",
|
||||
"widget": "anydata",
|
||||
"icon": "body",
|
||||
"color": "red",
|
||||
"descrColor": "red"
|
||||
},
|
||||
{
|
||||
"name": "anydataDgr",
|
||||
"label": "Сообщение2",
|
||||
"widget": "anydata",
|
||||
"after": "",
|
||||
"color": "red",
|
||||
"icon": "walk"
|
||||
},
|
||||
{
|
||||
"name": "anydataDef",
|
||||
"label": "Текст",
|
||||
"widget": "anydata",
|
||||
"after": "",
|
||||
"icon": ""
|
||||
},
|
||||
{
|
||||
"name": "anydataVlt",
|
||||
"label": "Вольты",
|
||||
"widget": "anydata",
|
||||
"after": "V",
|
||||
"icon": "speedometer"
|
||||
},
|
||||
{
|
||||
"name": "anydataAmp",
|
||||
"label": "Амперы",
|
||||
"widget": "anydata",
|
||||
"after": "A",
|
||||
"icon": "speedometer"
|
||||
},
|
||||
{
|
||||
"name": "anydataWt",
|
||||
"label": "Ватты",
|
||||
"widget": "anydata",
|
||||
"after": "Wt",
|
||||
"icon": "speedometer",
|
||||
"color": [
|
||||
{
|
||||
"level": 0,
|
||||
"value": ""
|
||||
},
|
||||
{
|
||||
"level": 200,
|
||||
"value": "#009933"
|
||||
},
|
||||
{
|
||||
"level": 2000,
|
||||
"value": "#FF9900"
|
||||
},
|
||||
{
|
||||
"level": 4000,
|
||||
"value": "red"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "anydataWth",
|
||||
"label": "Энергия",
|
||||
"widget": "anydata",
|
||||
"after": "kWh",
|
||||
"icon": "speedometer"
|
||||
},
|
||||
{
|
||||
"name": "anydataHtz",
|
||||
"label": "Герцы",
|
||||
"widget": "anydata",
|
||||
"after": "Hz",
|
||||
"icon": "speedometer"
|
||||
},
|
||||
{
|
||||
"name": "anydataTmp",
|
||||
"label": "Температура",
|
||||
"widget": "anydata",
|
||||
"after": "°С",
|
||||
"icon": "thermometer",
|
||||
"font": "OCR A Std",
|
||||
"color": [
|
||||
{
|
||||
"level": -20,
|
||||
"value": "#0000CC"
|
||||
},
|
||||
{
|
||||
"level": -10,
|
||||
"value": "#0000CC"
|
||||
},
|
||||
{
|
||||
"level": 0,
|
||||
"value": "#0000CC"
|
||||
},
|
||||
{
|
||||
"level": 12,
|
||||
"value": "#3366FF"
|
||||
},
|
||||
{
|
||||
"level": 16,
|
||||
"value": "#33CCFF"
|
||||
},
|
||||
{
|
||||
"level": 18,
|
||||
"value": "#009933"
|
||||
},
|
||||
{
|
||||
"level": 30,
|
||||
"value": "#FF9900"
|
||||
},
|
||||
{
|
||||
"level": 40,
|
||||
"value": "red"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "anydataMm",
|
||||
"label": "Давление",
|
||||
"widget": "anydata",
|
||||
"after": "mm",
|
||||
"icon": "speedometer"
|
||||
},
|
||||
{
|
||||
"name": "anydataHum",
|
||||
"label": "Влажность",
|
||||
"widget": "anydata",
|
||||
"after": "%",
|
||||
"icon": "water",
|
||||
"color": "#88AADF"
|
||||
},
|
||||
{
|
||||
"name": "anydataTm",
|
||||
"label": "Время",
|
||||
"widget": "anydata",
|
||||
"after": "",
|
||||
"icon": "speedometer"
|
||||
},
|
||||
{
|
||||
"name": "button",
|
||||
"label": "Кнопка",
|
||||
"widget": "btn",
|
||||
"size": "large",
|
||||
"color": "green",
|
||||
"send": "test"
|
||||
},
|
||||
{
|
||||
"name": "toggle",
|
||||
"label": "Переключатель",
|
||||
"widget": "toggle",
|
||||
"icon": "",
|
||||
"iconOff": ""
|
||||
},
|
||||
{
|
||||
"name": "chart1",
|
||||
"label": "График без точек",
|
||||
"widget": "chart",
|
||||
"dateFormat": "HH:mm",
|
||||
"maxCount": 86400,
|
||||
"pointRadius": 0
|
||||
},
|
||||
{
|
||||
"name": "chart2",
|
||||
"label": "График с точками",
|
||||
"widget": "chart",
|
||||
"maxCount": 86400,
|
||||
"dateFormat": "HH:mm"
|
||||
},
|
||||
{
|
||||
"name": "chart3",
|
||||
"label": "График Дневной",
|
||||
"widget": "chart",
|
||||
"dateFormat": "DD.MM.YYYY",
|
||||
"maxCount": 86400,
|
||||
"type": "bar"
|
||||
},
|
||||
{
|
||||
"name": "chart4",
|
||||
"label": "График Часовой",
|
||||
"widget": "chart",
|
||||
"dateFormat": "HH:mm",
|
||||
"maxCount": 3600,
|
||||
"type": "bar"
|
||||
},
|
||||
{
|
||||
"name": "chart5",
|
||||
"label": "График двойной",
|
||||
"widget": "chart",
|
||||
"series": [
|
||||
"Температура, С",
|
||||
"Влажность, %"
|
||||
],
|
||||
"dateFormat": "HH:mm",
|
||||
"maxCount": 86400,
|
||||
"pointRadius": 0
|
||||
},
|
||||
{
|
||||
"name": "chart6",
|
||||
"label": "График тройной",
|
||||
"widget": "chart",
|
||||
"series": [
|
||||
"Температура, С",
|
||||
"Влажность, %",
|
||||
"Давление, кПа"
|
||||
],
|
||||
"dateFormat": "HH:mm",
|
||||
"maxCount": 86400,
|
||||
"pointRadius": 0
|
||||
},
|
||||
{
|
||||
"name": "fillgauge",
|
||||
"label": "Бочка",
|
||||
"widget": "fillgauge",
|
||||
"circleColor": "#00FFFF",
|
||||
"textColor": "#FFFFFF",
|
||||
"waveTextColor": "#000000",
|
||||
"waveColor": "#00FFFF"
|
||||
},
|
||||
{
|
||||
"name": "inputDate",
|
||||
"label": "Ввод даты",
|
||||
"widget": "input",
|
||||
"size": "small",
|
||||
"color": "orange",
|
||||
"type": "date"
|
||||
},
|
||||
{
|
||||
"name": "inputDgt",
|
||||
"label": "Ввод числа",
|
||||
"widget": "input",
|
||||
"color": "blue",
|
||||
"type": "number"
|
||||
},
|
||||
{
|
||||
"name": "inputTxt",
|
||||
"label": "Ввод текста",
|
||||
"widget": "input",
|
||||
"size": "small",
|
||||
"color": "orange",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"name": "inputTm",
|
||||
"label": "Ввод времени",
|
||||
"widget": "input",
|
||||
"color": "blue",
|
||||
"type": "time"
|
||||
},
|
||||
{
|
||||
"name": "progressLine",
|
||||
"label": "Статус линия",
|
||||
"widget": "progress-line",
|
||||
"icon": "sunny",
|
||||
"max": "100",
|
||||
"stroke": "10"
|
||||
},
|
||||
{
|
||||
"name": "progressRound",
|
||||
"label": "Статус круг",
|
||||
"widget": "progress-round",
|
||||
"max": "100",
|
||||
"stroke": "20",
|
||||
"color": "#45ccce",
|
||||
"background": "#777",
|
||||
"semicircle": "1"
|
||||
},
|
||||
{
|
||||
"name": "range",
|
||||
"label": "Ползунок",
|
||||
"widget": "range",
|
||||
"descrColor": "red",
|
||||
"after": "%",
|
||||
"k": 0.0977,
|
||||
"min": 0,
|
||||
"max": 100,
|
||||
"debounce": 500
|
||||
},
|
||||
{
|
||||
"name": "rangeServo",
|
||||
"label": "Ползунок (Servo)",
|
||||
"widget": "range",
|
||||
"descrColor": "red",
|
||||
"after": "°",
|
||||
"k": 1,
|
||||
"min": 0,
|
||||
"max": 180,
|
||||
"debounce": 500
|
||||
},
|
||||
{
|
||||
"name": "select",
|
||||
"label": "Выпадающий",
|
||||
"widget": "select",
|
||||
"options": [
|
||||
"Выключен",
|
||||
"Включен"
|
||||
],
|
||||
"status": 0
|
||||
},
|
||||
{
|
||||
"name": "anydataPpm",
|
||||
"label": "PPM",
|
||||
"widget": "anydata",
|
||||
"after": "ppm",
|
||||
"icon": "speedometer"
|
||||
},
|
||||
{
|
||||
"name": "anydatamAmp",
|
||||
"label": "миллиАмперы",
|
||||
"widget": "anydata",
|
||||
"after": "mAmp",
|
||||
"icon": "speedometer"
|
||||
},
|
||||
{
|
||||
"name": "anydatamVlt",
|
||||
"label": "миллиВольты",
|
||||
"widget": "anydata",
|
||||
"after": "mVlt",
|
||||
"icon": "speedometer"
|
||||
},
|
||||
{
|
||||
"name": "anydatamWt",
|
||||
"label": "миллиВатты",
|
||||
"widget": "anydata",
|
||||
"after": "mWt",
|
||||
"icon": "speedometer"
|
||||
},
|
||||
{
|
||||
"name": "anydataKm",
|
||||
"label": "Километры",
|
||||
"widget": "anydata",
|
||||
"after": "km",
|
||||
"icon": "speedometer"
|
||||
},
|
||||
{
|
||||
"name": "anydataM",
|
||||
"label": "Метры",
|
||||
"widget": "anydata",
|
||||
"after": "m",
|
||||
"icon": "speedometer"
|
||||
},
|
||||
{
|
||||
"name": "anydataCm",
|
||||
"label": "Сантиметры",
|
||||
"widget": "anydata",
|
||||
"after": "cm",
|
||||
"icon": "speedometer"
|
||||
},
|
||||
{
|
||||
"name": "anydataLiter",
|
||||
"label": "Литры",
|
||||
"widget": "anydata",
|
||||
"after": "ltr",
|
||||
"icon": "speedometer"
|
||||
},
|
||||
{
|
||||
"name": "anydataSpeed",
|
||||
"label": "мерты в секунду",
|
||||
"widget": "anydata",
|
||||
"after": "m/s",
|
||||
"icon": "speedometer"
|
||||
},
|
||||
{
|
||||
"name": "anydataСorner",
|
||||
"label": "угол градусов",
|
||||
"widget": "anydata",
|
||||
"after": "°",
|
||||
"icon": "speedometer"
|
||||
},
|
||||
{
|
||||
"name": "anydataBar",
|
||||
"label": "давление Bar",
|
||||
"widget": "anydata",
|
||||
"after": "Kg/cm²",
|
||||
"icon": "speedometer"
|
||||
},
|
||||
{
|
||||
"name": "nil",
|
||||
"label": "Без виджета"
|
||||
}
|
||||
]
|
||||
718
src/modules/sensors/SoftRTC/SoftRTC.cpp
Normal file
718
src/modules/sensors/SoftRTC/SoftRTC.cpp
Normal file
@@ -0,0 +1,718 @@
|
||||
// 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 "NTP.h"
|
||||
#include "WsServer.h"
|
||||
|
||||
// Подключаем нужные заголовочные файлы в зависимости от платформы
|
||||
#ifdef ESP8266
|
||||
#include <TZ.h>
|
||||
#include <coredecls.h> // Для settimeofday_cb() в ESP8266
|
||||
#else
|
||||
#include <esp_sntp.h> // Для 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<IoTValue> ¶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;
|
||||
}
|
||||
264
src/modules/sensors/SoftRTC/export .json
Normal file
264
src/modules/sensors/SoftRTC/export .json
Normal file
@@ -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()
|
||||
}
|
||||
|
||||
161
src/modules/sensors/SoftRTC/modinfo.json
Normal file
161
src/modules/sensors/SoftRTC/modinfo.json
Normal file
@@ -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*": []
|
||||
}
|
||||
}
|
||||
488
src/modules/virtual/SolarCalc/SolarCalc.cpp
Normal file
488
src/modules/virtual/SolarCalc/SolarCalc.cpp
Normal file
@@ -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 <ctime>
|
||||
#include <SolarCalculator.h>
|
||||
#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<IoTValue> ¶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;
|
||||
}
|
||||
BIN
src/modules/virtual/SolarCalc/coordinates.jpg
Normal file
BIN
src/modules/virtual/SolarCalc/coordinates.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 220 KiB |
455
src/modules/virtual/SolarCalc/export.json
Normal file
455
src/modules/virtual/SolarCalc/export.json
Normal file
@@ -0,0 +1,455 @@
|
||||
{
|
||||
"mark": "iotm",
|
||||
"config": [
|
||||
{
|
||||
"global": 0,
|
||||
"type": "Reading",
|
||||
"subtype": "VButton",
|
||||
"id": "CalculateBtn",
|
||||
"needSave": 0,
|
||||
"widget": "toggle",
|
||||
"page": "Sun",
|
||||
"descr": "Calculate",
|
||||
"int": "0",
|
||||
"val": "0"
|
||||
},
|
||||
{
|
||||
"global": 0,
|
||||
"type": "Reading",
|
||||
"subtype": "Variable",
|
||||
"id": "RiseTimeToday",
|
||||
"needSave": 0,
|
||||
"widget": "anydataTm",
|
||||
"page": "Sun rise",
|
||||
"descr": " sunrise today",
|
||||
"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": "azimuth",
|
||||
"needSave": 0,
|
||||
"widget": "anydataСorner",
|
||||
"page": "horizontal coordinates",
|
||||
"descr": " azimuth",
|
||||
"int": "0",
|
||||
"val": "0.0",
|
||||
"map": "",
|
||||
"plus": 0,
|
||||
"multiply": 1,
|
||||
"round": "1",
|
||||
"moduleName": "Variable"
|
||||
},
|
||||
{
|
||||
"global": 0,
|
||||
"type": "Reading",
|
||||
"subtype": "Variable",
|
||||
"id": "elevationTransit",
|
||||
"needSave": 0,
|
||||
"widget": "anydataСorner",
|
||||
"page": "Sun transit",
|
||||
"descr": "elevation at Transit",
|
||||
"int": "0",
|
||||
"val": "0.0",
|
||||
"map": "",
|
||||
"plus": 0,
|
||||
"multiply": 1,
|
||||
"round": "1",
|
||||
"moduleName": "Variable"
|
||||
},
|
||||
{
|
||||
"global": 0,
|
||||
"type": "Reading",
|
||||
"subtype": "Variable",
|
||||
"id": "elevation",
|
||||
"needSave": 0,
|
||||
"widget": "anydataСorner",
|
||||
"page": "horizontal coordinates",
|
||||
"descr": " elevation",
|
||||
"int": "0",
|
||||
"val": "0.0",
|
||||
"map": "",
|
||||
"plus": 0,
|
||||
"multiply": 1,
|
||||
"round": "1",
|
||||
"moduleName": "Variable"
|
||||
},
|
||||
{
|
||||
"global": 0,
|
||||
"type": "Reading",
|
||||
"subtype": "Variable",
|
||||
"id": "SetTimeToday",
|
||||
"needSave": 0,
|
||||
"widget": "anydataTm",
|
||||
"page": "Sun set",
|
||||
"descr": " sunset today",
|
||||
"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": "TransitTimeToday",
|
||||
"needSave": 0,
|
||||
"widget": "anydataTm",
|
||||
"page": "Sun transit",
|
||||
"descr": " transit today",
|
||||
"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": "hourRiseToday",
|
||||
"needSave": 0,
|
||||
"widget": "anydataDef",
|
||||
"page": "Sun rise",
|
||||
"descr": "Sun rise UTC hours",
|
||||
"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": "minuteRiseToday",
|
||||
"needSave": 0,
|
||||
"widget": "anydataDef",
|
||||
"page": "Sun rise",
|
||||
"descr": "Sun rise UTC minutes",
|
||||
"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": "azimuthRise",
|
||||
"needSave": 0,
|
||||
"widget": "anydataСorner",
|
||||
"page": "Sun rise",
|
||||
"descr": "azimuth at sunrise",
|
||||
"int": "0",
|
||||
"val": "0.0",
|
||||
"map": "",
|
||||
"plus": 0,
|
||||
"multiply": 1,
|
||||
"round": "1",
|
||||
"moduleName": "Variable"
|
||||
},
|
||||
{
|
||||
"global": 0,
|
||||
"type": "Reading",
|
||||
"subtype": "Variable",
|
||||
"id": "azimuthTransit",
|
||||
"needSave": 0,
|
||||
"widget": "anydataСorner",
|
||||
"page": "Sun transit",
|
||||
"descr": "azimuth at transit",
|
||||
"int": "0",
|
||||
"val": "0.0",
|
||||
"map": "",
|
||||
"plus": 0,
|
||||
"multiply": 1,
|
||||
"round": "1",
|
||||
"moduleName": "Variable"
|
||||
},
|
||||
{
|
||||
"global": 0,
|
||||
"type": "Reading",
|
||||
"subtype": "Variable",
|
||||
"id": "azimuthSet",
|
||||
"needSave": 0,
|
||||
"widget": "anydataСorner",
|
||||
"page": "Sun set",
|
||||
"descr": "azimuth at sunset",
|
||||
"int": "0",
|
||||
"val": "0.0",
|
||||
"map": "",
|
||||
"plus": 0,
|
||||
"multiply": 1,
|
||||
"round": "1",
|
||||
"moduleName": "Variable"
|
||||
},
|
||||
{
|
||||
"global": 0,
|
||||
"type": "Reading",
|
||||
"subtype": "VButton",
|
||||
"id": "isDayTime",
|
||||
"needSave": 0,
|
||||
"widget": "toggle",
|
||||
"page": "Sun",
|
||||
"descr": "is Day Time",
|
||||
"int": "0",
|
||||
"val": "0",
|
||||
"moduleName": "VButton"
|
||||
},
|
||||
{
|
||||
"global": 0,
|
||||
"type": "Reading",
|
||||
"subtype": "SolarCalculator",
|
||||
"id": "Sun",
|
||||
"widget": "anydataСorner",
|
||||
"page": "by Interval",
|
||||
"descr": "Azimuth",
|
||||
"lat": "50.118",
|
||||
"lon": "-005.478",
|
||||
"parameter": "azimuth",
|
||||
"int": "60",
|
||||
"ticker": "1",
|
||||
"debug": 1,
|
||||
"moduleName": "SolarCalculator"
|
||||
},
|
||||
{
|
||||
"global": 0,
|
||||
"type": "Reading",
|
||||
"subtype": "Variable",
|
||||
"id": "sunArcFromTransit",
|
||||
"needSave": 0,
|
||||
"widget": "anydataСorner",
|
||||
"page": "Sun transit",
|
||||
"descr": "Sun Arc From Transit",
|
||||
"int": "0",
|
||||
"val": "0.0",
|
||||
"map": "",
|
||||
"plus": 0,
|
||||
"multiply": 1,
|
||||
"round": "1",
|
||||
"moduleName": "Variable"
|
||||
},
|
||||
{
|
||||
"global": 0,
|
||||
"type": "Reading",
|
||||
"subtype": "Variable",
|
||||
"id": "jd",
|
||||
"needSave": 0,
|
||||
"widget": "anydataDef",
|
||||
"page": "Raw astro data",
|
||||
"descr": "jd",
|
||||
"int": "0",
|
||||
"val": "0.0",
|
||||
"map": "",
|
||||
"plus": 0,
|
||||
"multiply": 1,
|
||||
"round": "2",
|
||||
"moduleName": "Variable"
|
||||
},
|
||||
{
|
||||
"global": 0,
|
||||
"type": "Reading",
|
||||
"subtype": "Variable",
|
||||
"id": "GMST",
|
||||
"needSave": 0,
|
||||
"widget": "anydataСorner",
|
||||
"page": "Raw astro data",
|
||||
"descr": " GMST",
|
||||
"int": "0",
|
||||
"val": "0.0",
|
||||
"map": "",
|
||||
"plus": 0,
|
||||
"multiply": 1,
|
||||
"round": "1",
|
||||
"moduleName": "Variable"
|
||||
},
|
||||
{
|
||||
"global": 0,
|
||||
"type": "Reading",
|
||||
"subtype": "Variable",
|
||||
"id": "LST",
|
||||
"needSave": 0,
|
||||
"widget": "anydataСorner",
|
||||
"page": "Raw astro data",
|
||||
"descr": " LST",
|
||||
"int": "0",
|
||||
"val": "0.0",
|
||||
"map": "",
|
||||
"plus": 0,
|
||||
"multiply": 1,
|
||||
"round": "1",
|
||||
"moduleName": "Variable"
|
||||
},
|
||||
{
|
||||
"global": 0,
|
||||
"type": "Reading",
|
||||
"subtype": "Variable",
|
||||
"id": "ra",
|
||||
"needSave": 0,
|
||||
"widget": "anydataСorner",
|
||||
"page": "equatorial coordinates",
|
||||
"descr": "ra",
|
||||
"int": "0",
|
||||
"val": "0.0",
|
||||
"map": "",
|
||||
"plus": 0,
|
||||
"multiply": 1,
|
||||
"round": "1",
|
||||
"moduleName": "Variable"
|
||||
},
|
||||
{
|
||||
"global": 0,
|
||||
"type": "Reading",
|
||||
"subtype": "Variable",
|
||||
"id": "dec",
|
||||
"needSave": 0,
|
||||
"widget": "anydataСorner",
|
||||
"page": "equatorial coordinates",
|
||||
"descr": "dec",
|
||||
"int": "0",
|
||||
"val": "0.0",
|
||||
"map": "",
|
||||
"plus": 0,
|
||||
"multiply": 1,
|
||||
"round": "1",
|
||||
"moduleName": "Variable"
|
||||
},
|
||||
{
|
||||
"global": 0,
|
||||
"type": "Reading",
|
||||
"subtype": "Variable",
|
||||
"id": "GHA",
|
||||
"needSave": 0,
|
||||
"widget": "anydataСorner",
|
||||
"page": "Raw astro data",
|
||||
"descr": " GHA",
|
||||
"int": "0",
|
||||
"val": "0.0",
|
||||
"map": "",
|
||||
"plus": 0,
|
||||
"multiply": 1,
|
||||
"round": "1",
|
||||
"moduleName": "Variable"
|
||||
},
|
||||
{
|
||||
"global": 0,
|
||||
"type": "Reading",
|
||||
"subtype": "Variable",
|
||||
"id": "LHA",
|
||||
"needSave": 0,
|
||||
"widget": "anydataСorner",
|
||||
"page": "Raw astro data",
|
||||
"descr": "LHA",
|
||||
"int": "0",
|
||||
"val": "0.0",
|
||||
"map": "",
|
||||
"plus": 0,
|
||||
"multiply": 1,
|
||||
"round": "1",
|
||||
"moduleName": "Variable"
|
||||
},
|
||||
{
|
||||
"global": 0,
|
||||
"type": "Writing",
|
||||
"subtype": "Timer",
|
||||
"id": "timerMinute",
|
||||
"widget": "anydataDef",
|
||||
"page": "Timers",
|
||||
"descr": "Timer 1 minite",
|
||||
"int": 1,
|
||||
"countDown": "60",
|
||||
"ticker": "0",
|
||||
"repeat": 1,
|
||||
"needSave": 0,
|
||||
"moduleName": "Timer"
|
||||
},
|
||||
{
|
||||
"global": 0,
|
||||
"type": "Writing",
|
||||
"subtype": "Timer",
|
||||
"id": "timerDay",
|
||||
"widget": "anydataDef",
|
||||
"page": "Timers",
|
||||
"descr": "Timer 1 Day",
|
||||
"int": 1,
|
||||
"countDown": "86400",
|
||||
"ticker": "0",
|
||||
"repeat": "1",
|
||||
"needSave": 0,
|
||||
"moduleName": "Timer"
|
||||
},
|
||||
{
|
||||
"global": 0,
|
||||
"type": "Writing",
|
||||
"subtype": "Cron",
|
||||
"id": "cronMidnight",
|
||||
"widget": "anydataDef",
|
||||
"page": "Timers",
|
||||
"descr": "Cron midnight",
|
||||
"int": 1,
|
||||
"val": "1 0 0 * * *",
|
||||
"formatNextAlarm": "%H:%M:%S",
|
||||
"needSave": 0,
|
||||
"moduleName": "Cron"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
scenario=>if 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
|
||||
167
src/modules/virtual/SolarCalc/modinfo.json
Normal file
167
src/modules/virtual/SolarCalc/modinfo.json
Normal file
@@ -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"]
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user