2021-04-05 14:32:31 +01:00
|
|
|
/*
|
2021-09-21 13:37:45 +01:00
|
|
|
xnrg_30_dummy.ino - Dummy energy sensor support for Tasmota
|
2021-04-05 14:32:31 +01:00
|
|
|
|
|
|
|
Copyright (C) 2021 Theo Arends
|
|
|
|
|
|
|
|
This program is free software: you can redistribute it and/or modify
|
|
|
|
it under the terms of the GNU General Public License as published by
|
|
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
|
|
(at your option) any later version.
|
|
|
|
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
GNU General Public License for more details.
|
|
|
|
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
|
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#ifdef USE_ENERGY_SENSOR
|
|
|
|
#ifdef USE_ENERGY_DUMMY
|
|
|
|
/*********************************************************************************************\
|
2021-04-06 10:09:12 +01:00
|
|
|
* Provides dummy energy monitoring for up to three channels based on relay count
|
2021-04-05 14:32:31 +01:00
|
|
|
*
|
2021-10-15 15:28:16 +01:00
|
|
|
* User is supposed to enter valid data for Voltage, Current and Power using commands
|
|
|
|
* VoltageSet 240 (= 240V), CurrentSet 0.417 (= 417mA) and PowerSet 100 (= 100W) or
|
|
|
|
* VoltageCal 24000 (= 240V), CurrentCal 41666 (= 0.417A) and PowerCal 10000 (= 100W)
|
|
|
|
* Each phase or channel can be set using commands overriding above commands
|
|
|
|
* EnergyConfig1, EnergyConfig2 and EnergyConfig3 for Current phases (0.417 = 417mA)
|
|
|
|
* EnergyConfig4, EnergyConfig5 and EnergyConfig6 for Active Power phases (100 = 100W)
|
2021-04-05 14:32:31 +01:00
|
|
|
* Active Power is adjusted to calculated Apparent Power (=U*I) if the latter is smaller than the first
|
|
|
|
*
|
|
|
|
* Enable by selecting any GPIO as Option A2
|
|
|
|
\*********************************************************************************************/
|
|
|
|
|
2021-09-21 13:37:45 +01:00
|
|
|
#define XNRG_30 30
|
2021-04-05 14:32:31 +01:00
|
|
|
|
|
|
|
#define NRG_DUMMY_U_COMMON true // Phase voltage = false, Common voltage = true
|
|
|
|
#define NRG_DUMMY_F_COMMON true // Phase frequency = false, Common frequency = true
|
|
|
|
#define NRG_DUMMY_DC false // AC = false, DC = true;
|
2021-04-09 09:04:37 +01:00
|
|
|
#define NRG_DUMMY_OVERTEMP true // Use global temperature for overtemp detection
|
2021-04-05 14:32:31 +01:00
|
|
|
|
|
|
|
#define NRG_DUMMY_UREF 24000 // Voltage 240.00 V (= P / I)
|
|
|
|
#define NRG_DUMMY_IREF 41666 // Current 0.417 A (= P / U)
|
|
|
|
#define NRG_DUMMY_PREF 10000 // Power 100.00 W (= U * I)
|
|
|
|
#define NRG_DUMMY_FREF 5000 // Frequency 50.00 Hz
|
|
|
|
|
|
|
|
/********************************************************************************************/
|
|
|
|
|
2021-10-15 15:28:16 +01:00
|
|
|
struct {
|
|
|
|
int32_t current[3] = { 0 };
|
|
|
|
int32_t power[3] = { 0 };
|
|
|
|
} NrgDummy;
|
|
|
|
|
2021-04-05 14:32:31 +01:00
|
|
|
void NrgDummyEverySecond(void) {
|
|
|
|
if (Energy.power_on) { // Powered on
|
2021-04-06 10:09:12 +01:00
|
|
|
for (uint32_t channel = 0; channel < Energy.phase_count; channel++) {
|
2021-06-11 17:14:12 +01:00
|
|
|
Energy.voltage[channel] = ((float)Settings->energy_voltage_calibration / 100); // V
|
|
|
|
Energy.frequency[channel] = ((float)Settings->energy_frequency_calibration / 100); // Hz
|
2021-04-06 10:09:12 +01:00
|
|
|
if (bitRead(TasmotaGlobal.power, channel)) { // Emulate power read only if device is powered on
|
2021-10-15 15:28:16 +01:00
|
|
|
Energy.active_power[channel] = (NrgDummy.power[channel]) ? ((float)NrgDummy.power[channel] / 1000) : ((float)Settings->energy_power_calibration / 100); // W
|
2021-04-06 10:09:12 +01:00
|
|
|
if (0 == Energy.active_power[channel]) {
|
|
|
|
Energy.current[channel] = 0;
|
|
|
|
} else {
|
2021-10-15 15:28:16 +01:00
|
|
|
Energy.current[channel] = (NrgDummy.current[channel]) ? ((float)NrgDummy.current[channel] / 1000) : ((float)Settings->energy_current_calibration / 100000); // A
|
2021-09-29 14:33:58 +01:00
|
|
|
Energy.kWhtoday_delta[channel] += Energy.active_power[channel] * 1000 / 36;
|
2021-04-06 10:09:12 +01:00
|
|
|
}
|
|
|
|
Energy.data_valid[channel] = 0;
|
2021-04-05 14:32:31 +01:00
|
|
|
}
|
|
|
|
}
|
2021-09-29 14:33:58 +01:00
|
|
|
EnergyUpdateToday();
|
2021-04-05 14:32:31 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool NrgDummyCommand(void) {
|
|
|
|
bool serviced = true;
|
|
|
|
|
2021-10-15 15:28:16 +01:00
|
|
|
int32_t value = (int32_t)(CharToFloat(XdrvMailbox.data) * 1000); // 1.234 = 1234, -1.234 = -1234
|
|
|
|
uint32_t abs_value = abs(value) / 10; // 1.23 = 123, -1.23 = 123
|
2021-04-05 14:32:31 +01:00
|
|
|
|
2021-10-15 15:28:16 +01:00
|
|
|
if ((CMND_POWERCAL == Energy.command_code) || (CMND_VOLTAGECAL == Energy.command_code) || (CMND_CURRENTCAL == Energy.command_code)) {
|
|
|
|
// Service in xdrv_03_energy.ino
|
|
|
|
}
|
|
|
|
else if (CMND_POWERSET == Energy.command_code) {
|
2021-04-05 14:32:31 +01:00
|
|
|
if (XdrvMailbox.data_len) {
|
2022-07-24 14:30:19 +01:00
|
|
|
if ((abs_value >= 100) && (abs_value <= 16000000)) { // Between 1.00 and 160000.00 W
|
2021-10-15 15:28:16 +01:00
|
|
|
Settings->energy_power_calibration = abs_value;
|
2021-04-05 14:32:31 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (CMND_VOLTAGESET == Energy.command_code) {
|
|
|
|
if (XdrvMailbox.data_len) {
|
2022-07-24 14:30:19 +01:00
|
|
|
if ((abs_value >= 10000) && (abs_value <= 40000)) { // Between 100.00 and 400.00 V
|
2021-10-15 15:28:16 +01:00
|
|
|
Settings->energy_voltage_calibration = abs_value;
|
2021-04-05 14:32:31 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (CMND_CURRENTSET == Energy.command_code) {
|
|
|
|
if (XdrvMailbox.data_len) {
|
2022-07-24 14:30:19 +01:00
|
|
|
if ((abs_value >= 1000) && (abs_value <= 40000000)) { // Between 10.00 mA and 400.00000 A
|
2021-10-15 15:28:16 +01:00
|
|
|
Settings->energy_current_calibration = abs_value;
|
2021-04-05 14:32:31 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (CMND_FREQUENCYSET == Energy.command_code) {
|
|
|
|
if (XdrvMailbox.data_len) {
|
2022-07-24 14:30:19 +01:00
|
|
|
if ((abs_value >= 4500) && (abs_value <= 6500)) { // Between 45.00 and 65.00 Hz
|
2021-10-15 15:28:16 +01:00
|
|
|
Settings->energy_frequency_calibration = abs_value;
|
2021-04-05 14:32:31 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2021-04-09 08:49:33 +01:00
|
|
|
else if (CMND_ENERGYCONFIG == Energy.command_code) {
|
2021-10-15 15:28:16 +01:00
|
|
|
AddLog(LOG_LEVEL_DEBUG, PSTR("NRG: Config index %d, payload %d, value %d, data '%s'"),
|
|
|
|
XdrvMailbox.index, XdrvMailbox.payload, value, XdrvMailbox.data ? XdrvMailbox.data : "null" );
|
|
|
|
|
|
|
|
// EnergyConfig1 to 3 = Set Energy.current[channel] in A like 0.417 for 417mA
|
|
|
|
if ((XdrvMailbox.index > 0) && (XdrvMailbox.index < 4)) {
|
|
|
|
NrgDummy.current[XdrvMailbox.index -1] = value;
|
|
|
|
}
|
|
|
|
// EnergyConfig4 to 6 = Set Energy.active_power[channel] in W like 100 for 100W
|
|
|
|
if ((XdrvMailbox.index > 3) && (XdrvMailbox.index < 7)) {
|
|
|
|
NrgDummy.power[XdrvMailbox.index -4] = value;
|
|
|
|
}
|
2021-04-08 21:59:44 +01:00
|
|
|
}
|
2021-04-05 14:32:31 +01:00
|
|
|
else serviced = false; // Unknown command
|
|
|
|
|
|
|
|
return serviced;
|
|
|
|
}
|
|
|
|
|
|
|
|
void NrgDummyDrvInit(void) {
|
2021-04-06 11:08:07 +01:00
|
|
|
if (TasmotaGlobal.gpio_optiona.dummy_energy && TasmotaGlobal.devices_present) {
|
2021-06-11 17:14:12 +01:00
|
|
|
if (HLW_PREF_PULSE == Settings->energy_power_calibration) {
|
|
|
|
Settings->energy_frequency_calibration = NRG_DUMMY_FREF;
|
|
|
|
Settings->energy_voltage_calibration = NRG_DUMMY_UREF;
|
|
|
|
Settings->energy_current_calibration = NRG_DUMMY_IREF;
|
|
|
|
Settings->energy_power_calibration = NRG_DUMMY_PREF;
|
2021-04-05 14:32:31 +01:00
|
|
|
}
|
|
|
|
|
2021-04-06 14:23:07 +01:00
|
|
|
Energy.phase_count = (TasmotaGlobal.devices_present < ENERGY_MAX_PHASES) ? TasmotaGlobal.devices_present : ENERGY_MAX_PHASES;
|
2021-04-05 14:32:31 +01:00
|
|
|
Energy.voltage_common = NRG_DUMMY_U_COMMON; // Phase voltage = false, Common voltage = true
|
|
|
|
Energy.frequency_common = NRG_DUMMY_F_COMMON; // Phase frequency = false, Common frequency = true
|
2021-04-06 10:09:12 +01:00
|
|
|
Energy.type_dc = NRG_DUMMY_DC; // AC = false, DC = true;
|
2021-04-09 09:04:37 +01:00
|
|
|
Energy.use_overtemp = NRG_DUMMY_OVERTEMP; // Use global temperature for overtemp detection
|
2021-04-05 14:32:31 +01:00
|
|
|
|
2021-09-21 13:37:45 +01:00
|
|
|
TasmotaGlobal.energy_driver = XNRG_30;
|
2021-04-05 14:32:31 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*********************************************************************************************\
|
|
|
|
* Interface
|
|
|
|
\*********************************************************************************************/
|
|
|
|
|
2021-09-21 13:37:45 +01:00
|
|
|
bool Xnrg30(uint8_t function) {
|
2021-04-05 14:32:31 +01:00
|
|
|
bool result = false;
|
|
|
|
|
|
|
|
switch (function) {
|
|
|
|
case FUNC_ENERGY_EVERY_SECOND:
|
|
|
|
NrgDummyEverySecond();
|
|
|
|
break;
|
|
|
|
case FUNC_COMMAND:
|
|
|
|
result = NrgDummyCommand();
|
|
|
|
break;
|
|
|
|
case FUNC_PRE_INIT:
|
|
|
|
NrgDummyDrvInit();
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif // USE_ENERGY_DUMMY
|
|
|
|
#endif // USE_ENERGY_SENSOR
|