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 ( = 240 V ) , CurrentSet 0.417 ( = 417 mA ) and PowerSet 100 ( = 100 W ) or
* VoltageCal 24000 ( = 240 V ) , CurrentCal 41666 ( = 0.417 A ) and PowerCal 10000 ( = 100 W )
* Each phase or channel can be set using commands overriding above commands
* EnergyConfig1 , EnergyConfig2 and EnergyConfig3 for Current phases ( 0.417 = 417 mA )
* EnergyConfig4 , EnergyConfig5 and EnergyConfig6 for Active Power phases ( 100 = 100 W )
2023-01-28 12:47:37 +00:00
* In addition on ESP32 supporting more channels :
* EnergyConfig11 to 18 for Current phases 1 to 8 ( 0.417 = 417 mA )
* EnergyConfig111 to 118 for Active Power phases 1 to 8 ( 100 = 100 W )
*
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 {
2023-01-28 12:47:37 +00:00
int32_t current [ ENERGY_MAX_PHASES ] = { 0 } ;
int32_t power [ ENERGY_MAX_PHASES ] = { 0 } ;
2021-10-15 15:28:16 +01:00
} NrgDummy ;
2021-04-05 14:32:31 +01:00
void NrgDummyEverySecond ( void ) {
2023-01-24 15:54:03 +00:00
if ( Energy - > power_on ) { // Powered on
for ( uint32_t channel = 0 ; channel < Energy - > phase_count ; channel + + ) {
2022-10-30 10:15:17 +00:00
2023-01-25 16:05:48 +00:00
float power_calibration = ( float ) EnergyGetCalibration ( ENERGY_POWER_CALIBRATION , channel ) / 100 ;
float voltage_calibration = ( float ) EnergyGetCalibration ( ENERGY_VOLTAGE_CALIBRATION , channel ) / 100 ;
float current_calibration = ( float ) EnergyGetCalibration ( ENERGY_CURRENT_CALIBRATION , channel ) / 100000 ;
float frequency_calibration = ( float ) EnergyGetCalibration ( ENERGY_FREQUENCY_CALIBRATION , channel ) / 100 ;
2022-10-30 10:15:17 +00:00
2023-06-30 10:55:48 +01:00
if ( voltage_calibration > 100 ) {
Energy - > voltage [ channel ] = voltage_calibration ; // V
}
Energy - > frequency [ channel ] = ( frequency_calibration > 45 ) ? frequency_calibration : NAN ; // Hz
2022-10-30 10:15:17 +00:00
if ( bitRead ( TasmotaGlobal . power , channel ) ) { // Emulate power read only if device is powered on
2023-01-24 15:54:03 +00:00
Energy - > active_power [ channel ] = ( NrgDummy . power [ channel ] ) ? ( ( float ) NrgDummy . power [ channel ] / 1000 ) : power_calibration ; // W
if ( 0 = = Energy - > active_power [ channel ] ) {
Energy - > current [ channel ] = 0 ;
2021-04-06 10:09:12 +01:00
} else {
2023-01-24 15:54:03 +00:00
Energy - > current [ channel ] = ( NrgDummy . current [ channel ] ) ? ( ( float ) NrgDummy . current [ channel ] / 1000 ) : current_calibration ; // A
Energy - > kWhtoday_delta [ channel ] + = Energy - > active_power [ channel ] * 1000 / 36 ;
2021-04-06 10:09:12 +01:00
}
2023-01-24 15:54:03 +00: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
2024-08-21 15:27:43 +01:00
if ( ( CMND_POWERCAL = = Energy - > command_code ) | |
( CMND_VOLTAGECAL = = Energy - > command_code ) | |
( CMND_CURRENTCAL = = Energy - > command_code ) ) {
2021-10-15 15:28:16 +01:00
// Service in xdrv_03_energy.ino
}
2023-01-24 15:54:03 +00:00
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
2022-10-29 18:08:06 +01:00
XdrvMailbox . payload = abs_value ;
2021-04-05 14:32:31 +01:00
}
}
}
2023-01-24 15:54:03 +00:00
else if ( CMND_VOLTAGESET = = 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 > = 10000 ) & & ( abs_value < = 40000 ) ) { // Between 100.00 and 400.00 V
2022-10-29 18:08:06 +01:00
XdrvMailbox . payload = abs_value ;
2021-04-05 14:32:31 +01:00
}
}
}
2023-01-24 15:54:03 +00:00
else if ( CMND_CURRENTSET = = 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 > = 1000 ) & & ( abs_value < = 40000000 ) ) { // Between 10.00 mA and 400.00000 A
2022-10-29 18:08:06 +01:00
XdrvMailbox . payload = abs_value ;
2021-04-05 14:32:31 +01:00
}
}
}
2023-01-24 15:54:03 +00:00
else if ( CMND_FREQUENCYSET = = 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 > = 4500 ) & & ( abs_value < = 6500 ) ) { // Between 45.00 and 65.00 Hz
2022-10-29 18:08:06 +01:00
XdrvMailbox . payload = abs_value ;
2021-04-05 14:32:31 +01:00
}
}
}
2023-01-24 15:54:03 +00: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 " ) ;
2023-01-24 15:54:03 +00:00
// EnergyConfig1 to 3 = Set Energy->current[channel] in A like 0.417 for 417mA
2021-10-15 15:28:16 +01:00
if ( ( XdrvMailbox . index > 0 ) & & ( XdrvMailbox . index < 4 ) ) {
NrgDummy . current [ XdrvMailbox . index - 1 ] = value ;
}
2023-01-24 15:54:03 +00:00
// EnergyConfig4 to 6 = Set Energy->active_power[channel] in W like 100 for 100W
2021-10-15 15:28:16 +01:00
if ( ( XdrvMailbox . index > 3 ) & & ( XdrvMailbox . index < 7 ) ) {
NrgDummy . power [ XdrvMailbox . index - 4 ] = value ;
}
2023-01-28 12:47:37 +00:00
# ifdef ESP32
// EnergyConfig11 to 18 = Set Energy->current[channel] in A like 0.417 for 417mA
if ( ( XdrvMailbox . index > 10 ) & & ( XdrvMailbox . index < ENERGY_MAX_PHASES + 10 ) ) {
NrgDummy . current [ XdrvMailbox . index - 1 ] = value ;
}
// EnergyConfig111 to 118 = Set Energy->active_power[channel] in W like 100 for 100W
if ( ( XdrvMailbox . index > 110 ) & & ( XdrvMailbox . index < ENERGY_MAX_PHASES + 110 ) ) {
NrgDummy . power [ XdrvMailbox . index - 4 ] = value ;
}
# endif
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 ) {
2023-07-01 13:25:05 +01:00
uint32_t phase_count = ( Settings - > param [ P_DUMMY_RELAYS ] > 0 ) ? Settings - > param [ P_DUMMY_RELAYS ] : TasmotaGlobal . devices_present ; // SetOption48 - (Energy) Support energy dummy relays
2023-06-30 10:14:37 +01:00
if ( TasmotaGlobal . gpio_optiona . dummy_energy & & phase_count ) {
Energy - > phase_count = ( phase_count < ENERGY_MAX_PHASES ) ? phase_count : ENERGY_MAX_PHASES ;
2023-01-25 16:05:48 +00:00
if ( HLW_PREF_PULSE = = EnergyGetCalibration ( ENERGY_POWER_CALIBRATION ) ) {
for ( uint32_t i = 0 ; i < Energy - > phase_count ; i + + ) {
EnergySetCalibration ( ENERGY_POWER_CALIBRATION , NRG_DUMMY_PREF , i ) ;
EnergySetCalibration ( ENERGY_VOLTAGE_CALIBRATION , NRG_DUMMY_UREF , i ) ;
EnergySetCalibration ( ENERGY_CURRENT_CALIBRATION , NRG_DUMMY_IREF , i ) ;
EnergySetCalibration ( ENERGY_FREQUENCY_CALIBRATION , NRG_DUMMY_FREF , i ) ;
}
2021-04-05 14:32:31 +01:00
}
2023-01-24 15:54:03 +00: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
Energy - > type_dc = NRG_DUMMY_DC ; // AC = false, DC = true;
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
\ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2022-11-11 09:44:56 +00:00
bool Xnrg30 ( uint32_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