mirror of
https://github.com/Laxilef/OTGateway.git
synced 2025-12-25 17:43:35 +05:00
Compare commits
8 Commits
afe710abd3
...
fe93c00204
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fe93c00204 | ||
|
|
05a2d080be | ||
|
|
664bd7938c | ||
|
|
a78f35328f | ||
|
|
eab47af0e1 | ||
|
|
c524abd959 | ||
|
|
666786fd65 | ||
|
|
8475833dce |
@@ -210,6 +210,7 @@ build_flags =
|
||||
${esp32_defaults.build_flags}
|
||||
-D ARDUINO_USB_MODE=0
|
||||
-D ARDUINO_USB_CDC_ON_BOOT=1
|
||||
-D CONFIG_BT_NIMBLE_EXT_ADV=1
|
||||
-D USE_BLE=1
|
||||
-D DEFAULT_OT_IN_GPIO=35
|
||||
-D DEFAULT_OT_OUT_GPIO=36
|
||||
@@ -233,6 +234,7 @@ build_unflags =
|
||||
build_type = ${esp32_defaults.build_type}
|
||||
build_flags =
|
||||
${esp32_defaults.build_flags}
|
||||
-D CONFIG_BT_NIMBLE_EXT_ADV=1
|
||||
-D USE_BLE=1
|
||||
-D DEFAULT_OT_IN_GPIO=8
|
||||
-D DEFAULT_OT_OUT_GPIO=10
|
||||
@@ -316,6 +318,7 @@ build_unflags =
|
||||
build_type = ${esp32_defaults.build_type}
|
||||
build_flags =
|
||||
${esp32_defaults.build_flags}
|
||||
-D CONFIG_BT_NIMBLE_EXT_ADV=1
|
||||
-D USE_BLE=1
|
||||
-D DEFAULT_OT_IN_GPIO=3
|
||||
-D DEFAULT_OT_OUT_GPIO=1
|
||||
|
||||
@@ -1062,7 +1062,10 @@ protected:
|
||||
);
|
||||
|
||||
} else {
|
||||
Log.swarningln(FPSTR(L_OT_HEATING), F("Failed set max heating temp"));
|
||||
Log.swarningln(
|
||||
FPSTR(L_OT_HEATING), F("Failed set max heating temp: %.2f (converted: %.2f)"),
|
||||
vars.master.heating.setpointTemp, convertedTemp
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -196,6 +196,7 @@ protected:
|
||||
//if (vars.parameters.heatingEnabled) {
|
||||
if (settings.heating.enabled && this->indoorSensorsConnected) {
|
||||
pidRegulator.Kp = settings.heating.turbo ? 0.0f : settings.pid.p_factor;
|
||||
pidRegulator.Ki = settings.pid.i_factor;
|
||||
pidRegulator.Kd = settings.pid.d_factor;
|
||||
|
||||
pidRegulator.setLimits(settings.pid.minTemp, settings.pid.maxTemp);
|
||||
@@ -203,12 +204,22 @@ protected:
|
||||
pidRegulator.input = vars.master.heating.indoorTemp;
|
||||
pidRegulator.setpoint = settings.heating.target;
|
||||
|
||||
if (fabsf(pidRegulator.Ki - settings.pid.i_factor) >= 0.0001f) {
|
||||
/*if (fabsf(pidRegulator.Ki - settings.pid.i_factor) >= 0.0001f) {
|
||||
pidRegulator.Ki = settings.pid.i_factor;
|
||||
pidRegulator.integral = 0.0f;
|
||||
pidRegulator.getResultNow();
|
||||
|
||||
Log.sinfoln(FPSTR(L_REGULATOR_PID), F("Integral sum has been reset"));
|
||||
}*/
|
||||
|
||||
float error = pidRegulator.setpoint - pidRegulator.input;
|
||||
bool hasDeadband = (error > -(settings.pid.deadband.thresholdHigh))
|
||||
&& (error < settings.pid.deadband.thresholdLow);
|
||||
|
||||
if (hasDeadband) {
|
||||
pidRegulator.Kp *= settings.pid.deadband.p_multiplier;
|
||||
pidRegulator.Ki *= settings.pid.deadband.i_multiplier;
|
||||
pidRegulator.Kd *= settings.pid.deadband.d_multiplier;
|
||||
}
|
||||
|
||||
float pidResult = pidRegulator.getResultTimer();
|
||||
|
||||
@@ -445,6 +445,14 @@ protected:
|
||||
}
|
||||
|
||||
pClient = NimBLEDevice::createClient();
|
||||
|
||||
/**
|
||||
* Set initial connection parameters:
|
||||
* These settings are safe for 3 clients to connect reliably, can go faster if you have less
|
||||
* connections. Timeout should be a multiple of the interval, minimum is 100ms.
|
||||
* Min interval: 12 * 1.25ms = 15, Max interval: 12 * 1.25ms = 15, 0 latency, 1000 * 10ms = 10000ms timeout
|
||||
*/
|
||||
pClient->setConnectionParams(12, 12, 0, 1000);
|
||||
pClient->setConnectTimeout(5000);
|
||||
pClient->setSelfDelete(false, true);
|
||||
}
|
||||
@@ -495,55 +503,59 @@ protected:
|
||||
NimBLEUUID charUuid((uint16_t) 0x2A6E);
|
||||
pChar = pService->getCharacteristic(charUuid);
|
||||
|
||||
if (pChar && pChar->canNotify()) {
|
||||
if (pChar && (pChar->canNotify() || pChar->canIndicate())) {
|
||||
Log.straceln(
|
||||
FPSTR(L_SENSORS_BLE), F("Sensor #%hhu '%s': found temp char (%s) in env service on device %s"),
|
||||
sensorId, sSensor.name, charUuid.toString().c_str(), address.toString().c_str()
|
||||
);
|
||||
|
||||
tempNotifyCreated = pChar->subscribe(true, [sensorId](NimBLERemoteCharacteristic* pChar, uint8_t* pData, size_t length, bool isNotify) {
|
||||
if (pChar == nullptr) {
|
||||
return;
|
||||
}
|
||||
pChar->unsubscribe();
|
||||
tempNotifyCreated = pChar->subscribe(
|
||||
pChar->canNotify(),
|
||||
[sensorId](NimBLERemoteCharacteristic* pChar, uint8_t* pData, size_t length, bool isNotify) {
|
||||
if (pChar == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
const NimBLERemoteService* pService = pChar->getRemoteService();
|
||||
if (pService == nullptr) {
|
||||
return;
|
||||
}
|
||||
const NimBLERemoteService* pService = pChar->getRemoteService();
|
||||
if (pService == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
NimBLEClient* pClient = pService->getClient();
|
||||
if (pClient == nullptr) {
|
||||
return;
|
||||
}
|
||||
NimBLEClient* pClient = pService->getClient();
|
||||
if (pClient == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto& sSensor = Sensors::settings[sensorId];
|
||||
auto& sSensor = Sensors::settings[sensorId];
|
||||
|
||||
if (length != 2) {
|
||||
Log.swarningln(
|
||||
if (length != 2) {
|
||||
Log.swarningln(
|
||||
FPSTR(L_SENSORS_BLE),
|
||||
F("Sensor #%hhu '%s': invalid notification data at temp char (%s) on device %s"),
|
||||
sensorId,
|
||||
sSensor.name,
|
||||
pChar->getUUID().toString().c_str(),
|
||||
pClient->getPeerAddress().toString().c_str()
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
float rawTemp = (pChar->getValue<int16_t>() * 0.01f);
|
||||
Log.straceln(
|
||||
FPSTR(L_SENSORS_BLE),
|
||||
F("Sensor #%hhu '%s': invalid notification data at temp char (%s) on device %s"),
|
||||
sensorId,
|
||||
sSensor.name,
|
||||
pChar->getUUID().toString().c_str(),
|
||||
pClient->getPeerAddress().toString().c_str()
|
||||
F("Sensor #%hhu '%s': received temp: %.2f"),
|
||||
sensorId, sSensor.name, rawTemp
|
||||
);
|
||||
|
||||
return;
|
||||
// set temp
|
||||
Sensors::setValueById(sensorId, rawTemp, Sensors::ValueType::TEMPERATURE, true, true);
|
||||
|
||||
// update rssi
|
||||
Sensors::setValueById(sensorId, pClient->getRssi(), Sensors::ValueType::RSSI, false, false);
|
||||
}
|
||||
|
||||
float rawTemp = (pChar->getValue<int16_t>() * 0.01f);
|
||||
Log.straceln(
|
||||
FPSTR(L_SENSORS_BLE),
|
||||
F("Sensor #%hhu '%s': received temp: %.2f"),
|
||||
sensorId, sSensor.name, rawTemp
|
||||
);
|
||||
|
||||
// set temp
|
||||
Sensors::setValueById(sensorId, rawTemp, Sensors::ValueType::TEMPERATURE, true, true);
|
||||
|
||||
// update rssi
|
||||
Sensors::setValueById(sensorId, pClient->getRssi(), Sensors::ValueType::RSSI, false, false);
|
||||
});
|
||||
);
|
||||
|
||||
if (tempNotifyCreated) {
|
||||
Log.straceln(
|
||||
@@ -568,55 +580,59 @@ protected:
|
||||
NimBLEUUID charUuid((uint16_t) 0x2A1F);
|
||||
pChar = pService->getCharacteristic(charUuid);
|
||||
|
||||
if (pChar && pChar->canNotify()) {
|
||||
if (pChar && (pChar->canNotify() || pChar->canIndicate())) {
|
||||
Log.straceln(
|
||||
FPSTR(L_SENSORS_BLE), F("Sensor #%hhu '%s': found temp char (%s) in env service on device %s"),
|
||||
sensorId, sSensor.name, charUuid.toString().c_str(), address.toString().c_str()
|
||||
);
|
||||
|
||||
tempNotifyCreated = pChar->subscribe(true, [sensorId](NimBLERemoteCharacteristic* pChar, uint8_t* pData, size_t length, bool isNotify) {
|
||||
if (pChar == nullptr) {
|
||||
return;
|
||||
}
|
||||
pChar->unsubscribe();
|
||||
tempNotifyCreated = pChar->subscribe(
|
||||
pChar->canNotify(),
|
||||
[sensorId](NimBLERemoteCharacteristic* pChar, uint8_t* pData, size_t length, bool isNotify) {
|
||||
if (pChar == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
const NimBLERemoteService* pService = pChar->getRemoteService();
|
||||
if (pService == nullptr) {
|
||||
return;
|
||||
}
|
||||
const NimBLERemoteService* pService = pChar->getRemoteService();
|
||||
if (pService == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
NimBLEClient* pClient = pService->getClient();
|
||||
if (pClient == nullptr) {
|
||||
return;
|
||||
}
|
||||
NimBLEClient* pClient = pService->getClient();
|
||||
if (pClient == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto& sSensor = Sensors::settings[sensorId];
|
||||
auto& sSensor = Sensors::settings[sensorId];
|
||||
|
||||
if (length != 2) {
|
||||
Log.swarningln(
|
||||
if (length != 2) {
|
||||
Log.swarningln(
|
||||
FPSTR(L_SENSORS_BLE),
|
||||
F("Sensor #%hhu '%s': invalid notification data at temp char (%s) on device %s"),
|
||||
sensorId,
|
||||
sSensor.name,
|
||||
pChar->getUUID().toString().c_str(),
|
||||
pClient->getPeerAddress().toString().c_str()
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
float rawTemp = (pChar->getValue<int16_t>() * 0.1f);
|
||||
Log.straceln(
|
||||
FPSTR(L_SENSORS_BLE),
|
||||
F("Sensor #%hhu '%s': invalid notification data at temp char (%s) on device %s"),
|
||||
sensorId,
|
||||
sSensor.name,
|
||||
pChar->getUUID().toString().c_str(),
|
||||
pClient->getPeerAddress().toString().c_str()
|
||||
F("Sensor #%hhu '%s': received temp: %.2f"),
|
||||
sensorId, sSensor.name, rawTemp
|
||||
);
|
||||
|
||||
return;
|
||||
// set temp
|
||||
Sensors::setValueById(sensorId, rawTemp, Sensors::ValueType::TEMPERATURE, true, true);
|
||||
|
||||
// update rssi
|
||||
Sensors::setValueById(sensorId, pClient->getRssi(), Sensors::ValueType::RSSI, false, false);
|
||||
}
|
||||
|
||||
float rawTemp = (pChar->getValue<int16_t>() * 0.1f);
|
||||
Log.straceln(
|
||||
FPSTR(L_SENSORS_BLE),
|
||||
F("Sensor #%hhu '%s': received temp: %.2f"),
|
||||
sensorId, sSensor.name, rawTemp
|
||||
);
|
||||
|
||||
// set temp
|
||||
Sensors::setValueById(sensorId, rawTemp, Sensors::ValueType::TEMPERATURE, true, true);
|
||||
|
||||
// update rssi
|
||||
Sensors::setValueById(sensorId, pClient->getRssi(), Sensors::ValueType::RSSI, false, false);
|
||||
});
|
||||
);
|
||||
|
||||
if (tempNotifyCreated) {
|
||||
Log.straceln(
|
||||
@@ -653,55 +669,59 @@ protected:
|
||||
NimBLEUUID charUuid((uint16_t) 0x2A6F);
|
||||
pChar = pService->getCharacteristic(charUuid);
|
||||
|
||||
if (pChar && pChar->canNotify()) {
|
||||
if (pChar && (pChar->canNotify() || pChar->canIndicate())) {
|
||||
Log.straceln(
|
||||
FPSTR(L_SENSORS_BLE), F("Sensor #%hhu '%s': found humidity char (%s) in env service on device %s"),
|
||||
sensorId, sSensor.name, charUuid.toString().c_str(), address.toString().c_str()
|
||||
);
|
||||
|
||||
humidityNotifyCreated = pChar->subscribe(true, [sensorId](NimBLERemoteCharacteristic* pChar, uint8_t* pData, size_t length, bool isNotify) {
|
||||
if (pChar == nullptr) {
|
||||
return;
|
||||
}
|
||||
pChar->unsubscribe();
|
||||
humidityNotifyCreated = pChar->subscribe(
|
||||
pChar->canNotify(),
|
||||
[sensorId](NimBLERemoteCharacteristic* pChar, uint8_t* pData, size_t length, bool isNotify) {
|
||||
if (pChar == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
const NimBLERemoteService* pService = pChar->getRemoteService();
|
||||
if (pService == nullptr) {
|
||||
return;
|
||||
}
|
||||
const NimBLERemoteService* pService = pChar->getRemoteService();
|
||||
if (pService == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
NimBLEClient* pClient = pService->getClient();
|
||||
if (pClient == nullptr) {
|
||||
return;
|
||||
}
|
||||
NimBLEClient* pClient = pService->getClient();
|
||||
if (pClient == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto& sSensor = Sensors::settings[sensorId];
|
||||
auto& sSensor = Sensors::settings[sensorId];
|
||||
|
||||
if (length != 2) {
|
||||
Log.swarningln(
|
||||
if (length != 2) {
|
||||
Log.swarningln(
|
||||
FPSTR(L_SENSORS_BLE),
|
||||
F("Sensor #%hhu '%s': invalid notification data at humidity char (%s) on device %s"),
|
||||
sensorId,
|
||||
sSensor.name,
|
||||
pChar->getUUID().toString().c_str(),
|
||||
pClient->getPeerAddress().toString().c_str()
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
float rawHumidity = (pChar->getValue<uint16_t>() * 0.01f);
|
||||
Log.straceln(
|
||||
FPSTR(L_SENSORS_BLE),
|
||||
F("Sensor #%hhu '%s': invalid notification data at humidity char (%s) on device %s"),
|
||||
sensorId,
|
||||
sSensor.name,
|
||||
pChar->getUUID().toString().c_str(),
|
||||
pClient->getPeerAddress().toString().c_str()
|
||||
F("Sensor #%hhu '%s': received humidity: %.2f"),
|
||||
sensorId, sSensor.name, rawHumidity
|
||||
);
|
||||
|
||||
return;
|
||||
// set humidity
|
||||
Sensors::setValueById(sensorId, rawHumidity, Sensors::ValueType::HUMIDITY, true, true);
|
||||
|
||||
// update rssi
|
||||
Sensors::setValueById(sensorId, pClient->getRssi(), Sensors::ValueType::RSSI, false, false);
|
||||
}
|
||||
|
||||
float rawHumidity = (pChar->getValue<uint16_t>() * 0.01f);
|
||||
Log.straceln(
|
||||
FPSTR(L_SENSORS_BLE),
|
||||
F("Sensor #%hhu '%s': received humidity: %.2f"),
|
||||
sensorId, sSensor.name, rawHumidity
|
||||
);
|
||||
|
||||
// set humidity
|
||||
Sensors::setValueById(sensorId, rawHumidity, Sensors::ValueType::HUMIDITY, true, true);
|
||||
|
||||
// update rssi
|
||||
Sensors::setValueById(sensorId, pClient->getRssi(), Sensors::ValueType::RSSI, false, false);
|
||||
});
|
||||
);
|
||||
|
||||
if (humidityNotifyCreated) {
|
||||
Log.straceln(
|
||||
@@ -752,55 +772,59 @@ protected:
|
||||
NimBLEUUID charUuid((uint16_t) 0x2A19);
|
||||
pChar = pService->getCharacteristic(charUuid);
|
||||
|
||||
if (pChar && pChar->canNotify()) {
|
||||
if (pChar && (pChar->canNotify() || pChar->canIndicate())) {
|
||||
Log.straceln(
|
||||
FPSTR(L_SENSORS_BLE), F("Sensor #%hhu '%s': found battery char (%s) in battery service on device %s"),
|
||||
sensorId, sSensor.name, charUuid.toString().c_str(), address.toString().c_str()
|
||||
);
|
||||
|
||||
batteryNotifyCreated = pChar->subscribe(true, [sensorId](NimBLERemoteCharacteristic* pChar, uint8_t* pData, size_t length, bool isNotify) {
|
||||
if (pChar == nullptr) {
|
||||
return;
|
||||
}
|
||||
pChar->unsubscribe();
|
||||
batteryNotifyCreated = pChar->subscribe(
|
||||
pChar->canNotify(),
|
||||
[sensorId](NimBLERemoteCharacteristic* pChar, uint8_t* pData, size_t length, bool isNotify) {
|
||||
if (pChar == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
const NimBLERemoteService* pService = pChar->getRemoteService();
|
||||
if (pService == nullptr) {
|
||||
return;
|
||||
}
|
||||
const NimBLERemoteService* pService = pChar->getRemoteService();
|
||||
if (pService == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
NimBLEClient* pClient = pService->getClient();
|
||||
if (pClient == nullptr) {
|
||||
return;
|
||||
}
|
||||
NimBLEClient* pClient = pService->getClient();
|
||||
if (pClient == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto& sSensor = Sensors::settings[sensorId];
|
||||
auto& sSensor = Sensors::settings[sensorId];
|
||||
|
||||
if (length != 1) {
|
||||
Log.swarningln(
|
||||
if (length != 1) {
|
||||
Log.swarningln(
|
||||
FPSTR(L_SENSORS_BLE),
|
||||
F("Sensor #%hhu '%s': invalid notification data at battery char (%s) on device %s"),
|
||||
sensorId,
|
||||
sSensor.name,
|
||||
pChar->getUUID().toString().c_str(),
|
||||
pClient->getPeerAddress().toString().c_str()
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
auto rawBattery = pChar->getValue<uint8_t>();
|
||||
Log.straceln(
|
||||
FPSTR(L_SENSORS_BLE),
|
||||
F("Sensor #%hhu '%s': invalid notification data at battery char (%s) on device %s"),
|
||||
sensorId,
|
||||
sSensor.name,
|
||||
pChar->getUUID().toString().c_str(),
|
||||
pClient->getPeerAddress().toString().c_str()
|
||||
F("Sensor #%hhu '%s': received battery: %hhu"),
|
||||
sensorId, sSensor.name, rawBattery
|
||||
);
|
||||
|
||||
return;
|
||||
// set battery
|
||||
Sensors::setValueById(sensorId, rawBattery, Sensors::ValueType::BATTERY, true, true);
|
||||
|
||||
// update rssi
|
||||
Sensors::setValueById(sensorId, pClient->getRssi(), Sensors::ValueType::RSSI, false, false);
|
||||
}
|
||||
|
||||
auto rawBattery = pChar->getValue<uint8_t>();
|
||||
Log.straceln(
|
||||
FPSTR(L_SENSORS_BLE),
|
||||
F("Sensor #%hhu '%s': received battery: %.2f"),
|
||||
sensorId, sSensor.name, rawBattery
|
||||
);
|
||||
|
||||
// set battery
|
||||
Sensors::setValueById(sensorId, rawBattery, Sensors::ValueType::BATTERY, true, true);
|
||||
|
||||
// update rssi
|
||||
Sensors::setValueById(sensorId, pClient->getRssi(), Sensors::ValueType::RSSI, false, false);
|
||||
});
|
||||
);
|
||||
|
||||
if (batteryNotifyCreated) {
|
||||
Log.straceln(
|
||||
|
||||
@@ -110,11 +110,20 @@ struct Settings {
|
||||
struct {
|
||||
bool enabled = false;
|
||||
float p_factor = 2.0f;
|
||||
float i_factor = 0.0055f;
|
||||
float i_factor = 0.002f;
|
||||
float d_factor = 0.0f;
|
||||
unsigned short dt = 180;
|
||||
unsigned short dt = 300;
|
||||
short minTemp = 0;
|
||||
short maxTemp = DEFAULT_HEATING_MAX_TEMP;
|
||||
|
||||
struct {
|
||||
bool enabled = true;
|
||||
float p_multiplier = 1.0f;
|
||||
float i_multiplier = 0.05f;
|
||||
float d_multiplier = 1.0f;
|
||||
float thresholdHigh = 0.5f;
|
||||
float thresholdLow = 1.0f;
|
||||
} deadband;
|
||||
} pid;
|
||||
|
||||
struct {
|
||||
|
||||
@@ -63,6 +63,7 @@ const char S_CRASH[] PROGMEM = "crash";
|
||||
const char S_CURRENT_TEMP[] PROGMEM = "currentTemp";
|
||||
const char S_DATA[] PROGMEM = "data";
|
||||
const char S_DATE[] PROGMEM = "date";
|
||||
const char S_DEADBAND[] PROGMEM = "deadband";
|
||||
const char S_DHW[] PROGMEM = "dhw";
|
||||
const char S_DHW_BLOCKING[] PROGMEM = "dhwBlocking";
|
||||
const char S_DHW_SUPPORT[] PROGMEM = "dhwSupport";
|
||||
@@ -71,6 +72,7 @@ const char S_DIAG[] PROGMEM = "diag";
|
||||
const char S_DNS[] PROGMEM = "dns";
|
||||
const char S_DT[] PROGMEM = "dt";
|
||||
const char S_D_FACTOR[] PROGMEM = "d_factor";
|
||||
const char S_D_MULTIPLIER[] PROGMEM = "d_multiplier";
|
||||
const char S_EMERGENCY[] PROGMEM = "emergency";
|
||||
const char S_ENABLED[] PROGMEM = "enabled";
|
||||
const char S_ENV[] PROGMEM = "env";
|
||||
@@ -108,6 +110,7 @@ const char S_INTERVAL[] PROGMEM = "interval";
|
||||
const char S_INVERT_STATE[] PROGMEM = "invertState";
|
||||
const char S_IP[] PROGMEM = "ip";
|
||||
const char S_I_FACTOR[] PROGMEM = "i_factor";
|
||||
const char S_I_MULTIPLIER[] PROGMEM = "i_multiplier";
|
||||
const char S_K_FACTOR[] PROGMEM = "k_factor";
|
||||
const char S_LOGIN[] PROGMEM = "login";
|
||||
const char S_LOG_LEVEL[] PROGMEM = "logLevel";
|
||||
@@ -152,6 +155,7 @@ const char S_PREFIX[] PROGMEM = "prefix";
|
||||
const char S_PROTOCOL_VERSION[] PROGMEM = "protocolVersion";
|
||||
const char S_PURPOSE[] PROGMEM = "purpose";
|
||||
const char S_P_FACTOR[] PROGMEM = "p_factor";
|
||||
const char S_P_MULTIPLIER[] PROGMEM = "p_multiplier";
|
||||
const char S_REAL_SIZE[] PROGMEM = "realSize";
|
||||
const char S_REASON[] PROGMEM = "reason";
|
||||
const char S_RESET_DIAGNOSTIC[] PROGMEM = "resetDiagnostic";
|
||||
@@ -183,6 +187,8 @@ const char S_TARGET[] PROGMEM = "target";
|
||||
const char S_TARGET_TEMP[] PROGMEM = "targetTemp";
|
||||
const char S_TELNET[] PROGMEM = "telnet";
|
||||
const char S_TEMPERATURE[] PROGMEM = "temperature";
|
||||
const char S_THRESHOLD_HIGH[] PROGMEM = "thresholdHigh";
|
||||
const char S_THRESHOLD_LOW[] PROGMEM = "thresholdLow";
|
||||
const char S_THRESHOLD_TIME[] PROGMEM = "thresholdTime";
|
||||
const char S_TOTAL[] PROGMEM = "total";
|
||||
const char S_TRESHOLD_TIME[] PROGMEM = "tresholdTime";
|
||||
|
||||
62
src/utils.h
62
src/utils.h
@@ -438,6 +438,14 @@ void settingsToJson(const Settings& src, JsonVariant dst, bool safe = false) {
|
||||
pid[FPSTR(S_MIN_TEMP)] = src.pid.minTemp;
|
||||
pid[FPSTR(S_MAX_TEMP)] = src.pid.maxTemp;
|
||||
|
||||
auto pidDeadband = pid[FPSTR(S_DEADBAND)].to<JsonObject>();
|
||||
pidDeadband[FPSTR(S_ENABLED)] = src.pid.deadband.enabled;
|
||||
pidDeadband[FPSTR(S_P_MULTIPLIER)] = src.pid.deadband.p_multiplier;
|
||||
pidDeadband[FPSTR(S_I_MULTIPLIER)] = src.pid.deadband.i_multiplier;
|
||||
pidDeadband[FPSTR(S_D_MULTIPLIER)] = src.pid.deadband.d_multiplier;
|
||||
pidDeadband[FPSTR(S_THRESHOLD_HIGH)] = src.pid.deadband.thresholdHigh;
|
||||
pidDeadband[FPSTR(S_THRESHOLD_LOW)] = src.pid.deadband.thresholdLow;
|
||||
|
||||
if (!safe) {
|
||||
auto externalPump = dst[FPSTR(S_EXTERNAL_PUMP)].to<JsonObject>();
|
||||
externalPump[FPSTR(S_USE)] = src.externalPump.use;
|
||||
@@ -1075,6 +1083,60 @@ bool jsonToSettings(const JsonVariantConst src, Settings& dst, bool safe = false
|
||||
changed = true;
|
||||
}
|
||||
|
||||
if (src[FPSTR(S_PID)][FPSTR(S_DEADBAND)][FPSTR(S_ENABLED)].is<bool>()) {
|
||||
bool value = src[FPSTR(S_PID)][FPSTR(S_DEADBAND)][FPSTR(S_ENABLED)].as<bool>();
|
||||
|
||||
if (value != dst.pid.deadband.enabled) {
|
||||
dst.pid.deadband.enabled = value;
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!src[FPSTR(S_PID)][FPSTR(S_DEADBAND)][FPSTR(S_P_MULTIPLIER)].isNull()) {
|
||||
float value = src[FPSTR(S_PID)][FPSTR(S_DEADBAND)][FPSTR(S_P_MULTIPLIER)].as<float>();
|
||||
|
||||
if (value >= 0 && value <= 1 && fabsf(value - dst.pid.deadband.p_multiplier) > 0.0001f) {
|
||||
dst.pid.deadband.p_multiplier = roundf(value, 3);
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!src[FPSTR(S_PID)][FPSTR(S_DEADBAND)][FPSTR(S_I_MULTIPLIER)].isNull()) {
|
||||
float value = src[FPSTR(S_PID)][FPSTR(S_DEADBAND)][FPSTR(S_I_MULTIPLIER)].as<float>();
|
||||
|
||||
if (value >= 0 && value <= 1 && fabsf(value - dst.pid.deadband.i_multiplier) > 0.0001f) {
|
||||
dst.pid.deadband.i_multiplier = roundf(value, 3);
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!src[FPSTR(S_PID)][FPSTR(S_DEADBAND)][FPSTR(S_D_MULTIPLIER)].isNull()) {
|
||||
float value = src[FPSTR(S_PID)][FPSTR(S_DEADBAND)][FPSTR(S_D_MULTIPLIER)].as<float>();
|
||||
|
||||
if (value >= 0 && value <= 1 && fabsf(value - dst.pid.deadband.d_multiplier) > 0.0001f) {
|
||||
dst.pid.deadband.d_multiplier = roundf(value, 3);
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!src[FPSTR(S_PID)][FPSTR(S_DEADBAND)][FPSTR(S_THRESHOLD_HIGH)].isNull()) {
|
||||
float value = src[FPSTR(S_PID)][FPSTR(S_DEADBAND)][FPSTR(S_THRESHOLD_HIGH)].as<float>();
|
||||
|
||||
if (value >= 0.0f && value <= 5.0f && fabsf(value - dst.pid.deadband.thresholdHigh) > 0.0001f) {
|
||||
dst.pid.deadband.thresholdHigh = roundf(value, 2);
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!src[FPSTR(S_PID)][FPSTR(S_DEADBAND)][FPSTR(S_THRESHOLD_LOW)].isNull()) {
|
||||
float value = src[FPSTR(S_PID)][FPSTR(S_DEADBAND)][FPSTR(S_THRESHOLD_LOW)].as<float>();
|
||||
|
||||
if (value >= 0.0f && value <= 5.0f && fabsf(value - dst.pid.deadband.thresholdLow) > 0.0001f) {
|
||||
dst.pid.deadband.thresholdLow = roundf(value, 2);
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// heating
|
||||
if (src[FPSTR(S_HEATING)][FPSTR(S_ENABLED)].is<bool>()) {
|
||||
|
||||
@@ -347,7 +347,19 @@
|
||||
"i": "I factor",
|
||||
"d": "D factor",
|
||||
"dt": "DT <small>in seconds</small>",
|
||||
"noteMinMaxTemp": "<b>Important:</b> When using «Equitherm» and «PID» at the same time, the min and max temperatures limit the influence on the «Equitherm» result temperature.<br />Thus, if the min temperature is set to -15 and the max temperature is set to 15, then the final heat carrier setpoint will be from <code>equitherm_result - 15</code> to <code>equitherm_result + 15</code>."
|
||||
"limits": {
|
||||
"title": "Limits",
|
||||
"note": "<b>Important:</b> When using «Equitherm» and «PID» at the same time, the min and max temperatures limit the influence on the «Equitherm» result temperature.<br />Thus, if the min temperature is set to -15 and the max temperature is set to 15, then the final heat carrier setpoint will be from <code>equitherm_result - 15</code> to <code>equitherm_result + 15</code>."
|
||||
},
|
||||
"deadband": {
|
||||
"title": "Deadband",
|
||||
"note": "Deadband is a range around the target temperature where PID regulation becomes less active. Within this range, the algorithm can reduce intensity or pause adjustments to avoid overreacting to small fluctuations.<br /><br />For instance, with a target temperature of 22°, a lower threshold of 1.0, and an upper threshold of 0.5, the deadband operates between 21° and 22.5°. If the I coefficient is 0.0005 and the I multiplier is 0.05, then within the deadband, the I coefficient becomes: <code>0.0005 * 0.05 = 0.000025</code>",
|
||||
"p_multiplier": "Multiplier for P factor",
|
||||
"i_multiplier": "Multiplier for I factor",
|
||||
"d_multiplier": "Multiplier for D factor",
|
||||
"thresholdHigh": "Threshold high",
|
||||
"thresholdLow": "Threshold low"
|
||||
}
|
||||
},
|
||||
|
||||
"ot": {
|
||||
|
||||
@@ -347,7 +347,19 @@
|
||||
"i": "Fattore I",
|
||||
"d": "Fattore D",
|
||||
"dt": "DT <small>in secondi</small>",
|
||||
"noteMinMaxTemp": "<b>Importante:</b> Quando usi «Equitherm» e «PID» allo stesso tempo, i limiti della temperatura min e max influenzano il risultato della temperatura «Equitherm».<br />Thus, se la temperatura minima è impostata a -15 e la massima a 15, il riscaldamento finale sarà impostato fra <code>equitherm_result - 15</code> a <code>equitherm_result + 15</code>."
|
||||
"limits": {
|
||||
"title": "Limiti",
|
||||
"note": "<b>Importante:</b> Quando usi «Equitherm» e «PID» allo stesso tempo, i limiti della temperatura min e max influenzano il risultato della temperatura «Equitherm».<br />Thus, se la temperatura minima è impostata a -15 e la massima a 15, il riscaldamento finale sarà impostato fra <code>equitherm_result - 15</code> a <code>equitherm_result + 15</code>."
|
||||
},
|
||||
"deadband": {
|
||||
"title": "Zona morta (Deadband)",
|
||||
"note": "La zona morta è un intervallo intorno alla temperatura target in cui la regolazione PID diventa meno attiva. In questo intervallo, l'algoritmo può ridurre l'intensità o interrompere gli aggiustamenti per evitare di reagire eccessivamente a piccole fluttuazioni.<br /><br />Ad esempio, con una temperatura target di 22°, una soglia inferiore di 1.0 e una soglia superiore di 0.5, la zona morta opera tra 21° e 22.5°. Se il coefficiente I è 0.0005 e il moltiplicatore I è 0.05, allora nella zona morta, il coefficiente I diventa: <code>0.0005 * 0.05 = 0.000025</code>",
|
||||
"p_multiplier": "Moltiplicatore P",
|
||||
"i_multiplier": "Moltiplicatore I",
|
||||
"d_multiplier": "Moltiplicatore D",
|
||||
"thresholdHigh": "Soglia superiore",
|
||||
"thresholdLow": "Soglia inferiore"
|
||||
}
|
||||
},
|
||||
|
||||
"ot": {
|
||||
|
||||
@@ -347,7 +347,19 @@
|
||||
"i": "Коэффициент I",
|
||||
"d": "Коэффициент D",
|
||||
"dt": "DT <small>(сек)</small>",
|
||||
"noteMinMaxTemp": "<b>Важно:</b> При использовании «ПЗА» и «ПИД» одновременно, мин. и макс. температура ограничивает влияние на расчётную температуру «ПЗА».<br />Таким образом, если мин. температура задана как -15, а макс. как 15, то конечная температура теплоносителя будет от <code>equitherm_result - 15</code> до <code>equitherm_result + 15</code>."
|
||||
"limits": {
|
||||
"title": "Лимиты",
|
||||
"note": "<b>Важно:</b> При использовании «ПЗА» и «ПИД» одновременно, мин. и макс. температура ограничивает влияние на расчётную температуру «ПЗА».<br />Таким образом, если мин. температура задана как -15, а макс. как 15, то конечная температура теплоносителя будет от <code>equitherm_result - 15</code> до <code>equitherm_result + 15</code>."
|
||||
},
|
||||
"deadband": {
|
||||
"title": "Зона нечувствительности (Deadband)",
|
||||
"note": "Deadband - это зона нечувствительности вокруг целевой температуры, в которой PID-регулирование становится менее активным. В этом диапазоне алгоритм может снижать интенсивность или полностью прекращать корректировку температуры, чтобы избежать излишней чувствительности к небольшим колебаниям.<br /><br />Например, при целевой температуре 22°, нижнем пороге 1.0 и верхнем 0.5, deadband активен в диапазоне от 21° до 22.5°. Если коэфф. I=0.0005, а множитель I=0.05, то при включении зоны нечувствительности коэфф. I будет равен: <code>0.0005 * 0.05 = 0.000025</code>",
|
||||
"p_multiplier": "Множитель для коэф. P",
|
||||
"i_multiplier": "Множитель для коэф. I",
|
||||
"d_multiplier": "Множитель для коэф. D",
|
||||
"thresholdHigh": "Верхний порог",
|
||||
"thresholdLow": "Нижний порог"
|
||||
}
|
||||
},
|
||||
|
||||
"ot": {
|
||||
|
||||
@@ -259,7 +259,7 @@
|
||||
<b>Type:</b> <span class="sType"></span>
|
||||
<b>AppVersion:</b> <span class="sAppVersion"></span>
|
||||
<b>OT version:</b> <span class="sProtocolVersion"></span>
|
||||
<b>Modulation limits:</b> <span class="sModMin"></span>...<span class="sModMax"></span> %
|
||||
<b>Modulation limits:</b> <span class="sModMin"></span>...<span class="sAbsModMax"></span> %, curr. max: <span class="sModMax"></span> %
|
||||
<b>Power limits:</b> <span class="sPowerMin"></span>...<span class="sPowerMax"></span> kW
|
||||
<b>Heating limits:</b> <span class="sHeatMinTemp"></span>...<span class="sHeatMaxTemp"></span> <span class="tempUnit"></span>
|
||||
<b>DHW limits:</b> <span class="sDhwMinTemp"></span>...<span class="sDhwMaxTemp"></span> <span class="tempUnit"></span></pre>
|
||||
@@ -511,6 +511,7 @@
|
||||
setValue('.tempUnit', temperatureUnit(unitSystem));
|
||||
setValue('.pressureUnit', pressureUnit(unitSystem));
|
||||
setValue('.volumeUnit', volumeUnit(unitSystem));
|
||||
setValue('.sAbsModMax', result.opentherm.maxModulation);
|
||||
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
|
||||
@@ -211,7 +211,7 @@
|
||||
sensorNode.classList.remove("hidden");
|
||||
sensorNode.dataset.id = sensorId;
|
||||
setValue(".id", sensorId, sensorNode);
|
||||
setValue(".pos", sensorId + 1, sensorNode);
|
||||
setValue(".pos", parseInt(sensorId) + 1, sensorNode);
|
||||
setValue(".name", result[sensorId], sensorNode);
|
||||
|
||||
container.appendChild(sensorNode);
|
||||
|
||||
@@ -313,19 +313,73 @@
|
||||
|
||||
<hr />
|
||||
|
||||
<div class="grid">
|
||||
<label>
|
||||
<span data-i18n>settings.temp.min</span>
|
||||
<input type="number" inputmode="decimal" name="pid[minTemp]" min="0" max="0" step="1" required>
|
||||
</label>
|
||||
<details>
|
||||
<summary><b data-i18n>settings.pid.limits.title</b></summary>
|
||||
|
||||
<label>
|
||||
<span data-i18n>settings.temp.max</span>
|
||||
<input type="number" inputmode="numeric" name="pid[maxTemp]" min="0" max="0" step="1" required>
|
||||
</label>
|
||||
</div>
|
||||
<div>
|
||||
<div class="grid">
|
||||
<label>
|
||||
<span data-i18n>settings.temp.min</span>
|
||||
<input type="number" inputmode="decimal" name="pid[minTemp]" min="0" max="0" step="1" required>
|
||||
</label>
|
||||
|
||||
<label>
|
||||
<span data-i18n>settings.temp.max</span>
|
||||
<input type="number" inputmode="numeric" name="pid[maxTemp]" min="0" max="0" step="1" required>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<small data-i18n>settings.pid.noteMinMaxTemp</small>
|
||||
<small data-i18n>settings.pid.limits.note</small>
|
||||
</div>
|
||||
</details>
|
||||
|
||||
<hr />
|
||||
|
||||
<details>
|
||||
<summary><b data-i18n>settings.pid.deadband.title</b></summary>
|
||||
|
||||
<div>
|
||||
<fieldset>
|
||||
<label>
|
||||
<input type="checkbox" name="pid[deadband][enabled]" value="true">
|
||||
<span data-i18n>settings.enable</span>
|
||||
</label>
|
||||
</fieldset>
|
||||
|
||||
<div class="grid">
|
||||
<label>
|
||||
<span data-i18n>settings.pid.deadband.p_multiplier</span>
|
||||
<input type="number" inputmode="decimal" name="pid[deadband][p_multiplier]" min="0" max="5" step="0.001" required>
|
||||
</label>
|
||||
|
||||
<label>
|
||||
<span data-i18n>settings.pid.deadband.i_multiplier</span>
|
||||
<input type="number" inputmode="decimal" name="pid[deadband][i_multiplier]" min="0" max="1" step="0.001" required>
|
||||
</label>
|
||||
|
||||
<label>
|
||||
<span data-i18n>settings.pid.deadband.d_multiplier</span>
|
||||
<input type="number" inputmode="decimal" name="pid[deadband][d_multiplier]" min="0" max="1" step="0.001" required>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="grid">
|
||||
<label>
|
||||
<span data-i18n>settings.pid.deadband.thresholdHigh</span>
|
||||
<input type="number" inputmode="decimal" name="pid[deadband][thresholdHigh]" min="0" max="5" step="0.01" required>
|
||||
</label>
|
||||
|
||||
<label>
|
||||
<span data-i18n>settings.pid.deadband.thresholdLow</span>
|
||||
<input type="number" inputmode="decimal" name="pid[deadband][thresholdLow]" min="0" max="5" step="0.01" required>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<small data-i18n>settings.pid.deadband.note</small>
|
||||
</div>
|
||||
</details>
|
||||
|
||||
<br />
|
||||
|
||||
<button type="submit" data-i18n>button.save</button>
|
||||
</form>
|
||||
@@ -633,12 +687,12 @@
|
||||
<div class="grid">
|
||||
<label>
|
||||
<span data-i18n>settings.cascadeControl.output.gpio</span>
|
||||
<input type="number" outputmode="numeric" name="cascadeControl[output][gpio]" min="0" max="254" step="1">
|
||||
<input type="number" inputmode="numeric" name="cascadeControl[output][gpio]" min="0" max="254" step="1">
|
||||
</label>
|
||||
|
||||
<label>
|
||||
<span data-i18n>settings.cascadeControl.output.thresholdTime</span>
|
||||
<input type="number" outputmode="numeric" name="cascadeControl[output][thresholdTime]" min="5" max="600" step="1" required>
|
||||
<input type="number" inputmode="numeric" name="cascadeControl[output][thresholdTime]" min="5" max="600" step="1" required>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
@@ -823,6 +877,12 @@
|
||||
"min": (data.system.unitSystem == 0 ? 0 : 33),
|
||||
"max": (data.system.unitSystem == 0 ? 100 : 212)
|
||||
});
|
||||
setCheckboxValue("[name='pid[deadband][enabled]']", data.pid.deadband.enabled);
|
||||
setInputValue("[name='pid[deadband][p_multiplier]']", data.pid.deadband.p_multiplier);
|
||||
setInputValue("[name='pid[deadband][i_multiplier]']", data.pid.deadband.i_multiplier);
|
||||
setInputValue("[name='pid[deadband][d_multiplier]']", data.pid.deadband.d_multiplier);
|
||||
setInputValue("[name='pid[deadband][thresholdHigh]']", data.pid.deadband.thresholdHigh);
|
||||
setInputValue("[name='pid[deadband][thresholdLow]']", data.pid.deadband.thresholdLow);
|
||||
setBusy('#pid-settings-busy', '#pid-settings', false);
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user