Tasmota/tasmota/tasmota_xnrg_energy/xnrg_07_ade7953.ino

901 lines
43 KiB
C++

/*
xnrg_07_ade7953.ino - ADE7953 energy sensor support for Tasmota
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/>.
*/
#if defined(ESP32) && defined(USE_SPI)
#define USE_ESP32_SPI
#endif
#if defined(USE_I2C) || defined(USE_ESP32_SPI)
#ifdef USE_ENERGY_SENSOR
#ifdef USE_ADE7953
/*********************************************************************************************\
* ADE7953 - Energy used in Shelly 2.5 (model 1), EM (model 2), Plus 2PM (model 3), Pro 1PM (model 4), Pro 2PM (model 5) and Pro 4PM (model 6)
*
* {"NAME":"Shelly 2.5","GPIO":[320,0,32,0,224,193,0,0,640,192,608,225,3456,4736],"FLAG":0,"BASE":18}
* {"NAME":"Shelly EM","GPIO":[0,0,0,0,0,0,0,0,640,3457,608,224,8832,1],"FLAG":0,"BASE":18}
* {"NAME":"Shelly Plus 2PM PCB v0.1.5","GPIO":[320,0,192,0,0,0,1,1,225,224,0,0,0,0,193,0,0,0,0,0,0,608,3840,32,0,0,0,0,0,640,0,0,3458,4736,0,0],"FLAG":0,"BASE":1,"CMND":"AdcParam1 2,32000,40000,3350"}
* {"NAME":"Shelly Plus 2PM PCB v0.1.9","GPIO":[320,0,0,0,32,192,0,0,225,224,0,0,0,0,193,0,0,0,0,0,0,608,640,3458,0,0,0,0,0,9472,0,4736,0,0,0,0],"FLAG":0,"BASE":1,"CMND":"AdcParam1 2,10000,10000,3350"}
* {"NAME":"Shelly Pro 1PM","GPIO":[9568,1,9472,1,768,0,0,0,672,704,736,0,0,0,5600,6214,0,0,0,5568,0,0,0,0,0,0,0,0,3459,0,0,32,4736,0,160,0],"FLAG":0,"BASE":1,"CMND":"AdcParam1 2,5600,4700,3350"}
* {"NAME":"Shelly Pro 2PM","GPIO":[9568,1,9472,1,768,0,0,0,672,704,736,9569,0,0,5600,6214,0,0,0,5568,0,0,0,0,0,0,0,0,3460,0,0,32,4736,4737,160,161],"FLAG":0,"BASE":1,"CMND":"AdcParam1 2,5600,4700,3350;AdcParam2 2,5600,4700,3350"}
* {"NAME":"Shelly Pro 4PM","GPIO":[0,6210,0,6214,9568,0,0,0,0,0,9569,0,768,0,5600,0,0,0,0,5568,0,0,0,0,0,0,0,0,736,704,3461,0,4736,0,0,672],"FLAG":0,"BASE":1,"CMND":"AdcParam1 2,5600,4700,3350"}
*
* Based on datasheet from https://www.analog.com/en/products/ade7953.html
*
* Model differences:
* Function Model1 Model2 Model3 Model4 Model5 Model6 Remark
* ------------------------------ ------- ------- ------- ------ ------ ------ -------------------------------------------------
* Shelly 2.5 EM Plus2PM Pro1PM Pro2PM Pro4PM
* Processor ESP8266 ESP8266 ESP32 ESP32 ESP32 ESP32
* Interface I2C I2C I2C SPI SPI SPI Interface type used
* Number of inputs 2 2 2 1 2 4 Count of ADE9753 inputs used
* Number of ADE9753 chips 1 1 1 1 2 2 Count of ADE9753 chips
* ADE9753 IRQ 1 2 3 4 5 6 Index defines model number
* Current measurement device shunt CT shunt shunt shunt shunt CT = Current Transformer
* Common voltage Yes Yes Yes No No Yes Show common voltage in GUI/JSON
* Common frequency Yes Yes Yes No No Yes Show common frequency in GUI/JSON
* Swapped channel A/B Yes No No No No No Defined by hardware design - Fixed by Tasmota
* Support Export Active No Yes No No No No Only EM supports correct negative value detection
* Show negative (reactive) power No Yes No No No No Only EM supports correct negative value detection
* Default phase calibration 0 200 0 0 0 0 CT needs different phase calibration than shunts
* Default reset pin on ESP8266 - 16 - - - - Legacy support. Replaced by GPIO ADE7953RST
*
* I2C Address: 0x38
*********************************************************************************************
* Optionally allowing users to tweak calibration registers:
* - In addition to possible rules add a rule containing the calib.dat string like:
* - rule3 on file#calib.dat do {"angles":{"angle0":180,"angle1":176}} endon
* - rule3 on file#calib.dat do {"rms":{"current_a":4194303,"current_b":4194303,"voltage":1613194},"angles":{"angle0":200,"angle1":200},"powers":{"totactive":{"a":2723574,"b":2723574},"apparent":{"a":2723574,"b":2723574},"reactive":{"a":2723574,"b":2723574}}} endon
* - Restart Tasmota and obeserve that the results seem calibrated as Tasmota now uses the information from calib.dat
* To restore standard calibration using commands like VoltSet remove above entry from rule3
\*********************************************************************************************/
#define XNRG_07 7
#define XI2C_07 7 // See I2CDEVICES.md
#define ADE7953_ADDR 0x38
/*********************************************************************************************/
#define ADE7953_ACCU_ENERGY // Use accumulating energy instead of instant power
//#define ADE7953_DUMP_REGS
#define ADE7953_PREF 1540 // 4194304 / (1540 / 1000) = 2723574 (= WGAIN, VAGAIN and VARGAIN)
#define ADE7953_UREF 26000 // 4194304 / (26000 / 10000) = 1613194 (= VGAIN)
#define ADE7953_IREF 10000 // 4194304 / (10000 / 10000) = 4194303 (= IGAIN, needs to be different than 4194304 in order to use calib.dat)
#define ADE7953_NO_LOAD_THRESHOLD 29196 // According to ADE7953 datasheet the default threshold for no load detection is 58,393 use half this value to measure lower (5w) powers.
#define ADE7953_NO_LOAD_ENABLE 0 // Set DISNOLOAD register to 0 to enable No-load detection
#define ADE7953_NO_LOAD_DISABLE 7 // Set DISNOLOAD register to 7 to disable No-load detection
// Default calibration parameters can be overridden by a rule as documented above.
#define ADE7953_GAIN_DEFAULT 4194304 // = 0x400000 range 2097152 (min) to 6291456 (max)
#define ADE7953_PHCAL_DEFAULT 0 // = range -383 to 383 - Default phase calibration for Shunts
#define ADE7953_PHCAL_DEFAULT_CT 200 // = range -383 to 383 - Default phase calibration for Current Transformers (Shelly EM)
#define ADE7953_MAX_CHANNEL 4
enum Ade7953Models { ADE7953_SHELLY_25, ADE7953_SHELLY_EM, ADE7953_SHELLY_PLUS_2PM, ADE7953_SHELLY_PRO_1PM, ADE7953_SHELLY_PRO_2PM, ADE7953_SHELLY_PRO_4PM };
enum Ade7953_8BitRegisters {
// Register Name Addres R/W Bt Ty Default Description
// ---------------------------- ------ --- -- -- ---------- --------------------------------------------------------------------
ADE7953_SAGCYC = 0x000, // 0x000 R/W 8 U 0x00 Sag line cycles
ADE7953_DISNOLOAD, // 0x001 R/W 8 U 0x00 No-load detection disable (see Table 16)
ADE7953_RESERVED_0X002, // 0x002
ADE7953_RESERVED_0X003, // 0x003
ADE7953_LCYCMODE, // 0x004 R/W 8 U 0x40 Line cycle accumulation mode configuration (see Table 17)
ADE7953_RESERVED_0X005, // 0x005
ADE7953_RESERVED_0X006, // 0x006
ADE7953_PGA_V, // 0x007 R/W 8 U 0x00 Voltage channel gain configuration (Bits[2:0])
ADE7953_PGA_IA, // 0x008 R/W 8 U 0x00 Current Channel A gain configuration (Bits[2:0])
ADE7953_PGA_IB // 0x009 R/W 8 U 0x00 Current Channel B gain configuration (Bits[2:0])
};
enum Ade7953_16BitRegisters {
// Register Name Addres R/W Bt Ty Default Description
// ---------------------------- ------ --- -- -- ---------- --------------------------------------------------------------------
ADE7953_ZXTOUT = 0x100, // 0x100 R/W 16 U 0xFFFF Zero-crossing timeout
ADE7953_LINECYC, // 0x101 R/W 16 U 0x0000 Number of half line cycles for line cycle energy accumulation mode
ADE7953_CONFIG, // 0x102 R/W 16 U 0x8004 Configuration register (see Table 18)
ADE7953_CF1DEN, // 0x103 R/W 16 U 0x003F CF1 frequency divider denominator. When modifying this register, two sequential write operations must be performed to ensure that the write is successful.
ADE7953_CF2DEN, // 0x104 R/W 16 U 0x003F CF2 frequency divider denominator. When modifying this register, two sequential write operations must be performed to ensure that the write is successful.
ADE7953_RESERVED_0X105, // 0x105
ADE7953_RESERVED_0X106, // 0x106
ADE7953_CFMODE, // 0x107 R/W 16 U 0x0300 CF output selection (see Table 19)
ADE7943_PHCALA, // 0x108 R/W 16 S 0x0000 Phase calibration register (Current Channel A). This register is in sign magnitude format.
ADE7943_PHCALB, // 0x109 R/W 16 S 0x0000 Phase calibration register (Current Channel B). This register is in sign magnitude format.
ADE7943_PFA, // 0x10A R 16 S 0x0000 Power factor (Current Channel A)
ADE7943_PFB, // 0x10B R 16 S 0x0000 Power factor (Current Channel B)
ADE7943_ANGLE_A, // 0x10C R 16 S 0x0000 Angle between the voltage input and the Current Channel A input
ADE7943_ANGLE_B, // 0x10D R 16 S 0x0000 Angle between the voltage input and the Current Channel B input
ADE7943_Period, // 0x10E R 16 U 0x0000 Period register
ADE7953_RESERVED_0X120 = 0x120 // 0x120 This register should be set to 30h to meet the performance specified in Table 1. To modify this register, it must be unlocked by setting Register Address 0xFE to 0xAD immediately prior.
};
enum Ade7953_32BitRegisters {
// Register Name Addres R/W Bt Ty Default Description
// ---------------------------- ------ --- -- -- ---------- --------------------------------------------------------------------
ADE7953_ACCMODE = 0x301, // 0x301 R/W 24 U 0x000000 Accumulation mode (see Table 21)
ADE7953_AP_NOLOAD = 0x303, // 0x303 R/W 24 U 0x00E419 No load threshold for actual power
ADE7953_VAR_NOLOAD, // 0x304 R/W 24 U 0x00E419 No load threshold for reactive power
ADE7953_VA_NOLOAD, // 0x305 R/W 24 U 0x000000 No load threshold for appearant power
ADE7953_AVA = 0x310, // 0x310 R 24 S 0x000000 Instantaneous apparent power (Current Channel A)
ADE7953_BVA, // 0x311 R 24 S 0x000000 Instantaneous apparent power (Current Channel B)
ADE7953_AWATT, // 0x312 R 24 S 0x000000 Instantaneous active power (Current Channel A)
ADE7953_BWATT, // 0x313 R 24 S 0x000000 Instantaneous active power (Current Channel B)
ADE7953_AVAR, // 0x314 R 24 S 0x000000 Instantaneous reactive power (Current Channel A)
ADE7953_BVAR, // 0x315 R 24 S 0x000000 Instantaneous reactive power (Current Channel B)
ADE7953_IA, // 0x316 R 24 S 0x000000 Instantaneous current (Current Channel A)
ADE7953_IB, // 0x317 R 24 S 0x000000 Instantaneous current (Current Channel B)
ADE7953_V, // 0x318 R 24 S 0x000000 Instantaneous voltage (voltage channel)
ADE7953_RESERVED_0X319, // 0x319
ADE7953_IRMSA, // 0x31A R 24 U 0x000000 IRMS register (Current Channel A)
ADE7953_IRMSB, // 0x31B R 24 U 0x000000 IRMS register (Current Channel B)
ADE7953_VRMS, // 0x31C R 24 U 0x000000 VRMS register
ADE7953_RESERVED_0X31D, // 0x31D
ADE7953_AENERGYA, // 0x31E R 24 S 0x000000 Active energy (Current Channel A)
ADE7953_AENERGYB, // 0x31F R 24 S 0x000000 Active energy (Current Channel B)
ADE7953_RENERGYA, // 0x320 R 24 S 0x000000 Reactive energy (Current Channel A)
ADE7953_RENERGYB, // 0x321 R 24 S 0x000000 Reactive energy (Current Channel B)
ADE7953_APENERGYA, // 0x322 R 24 S 0x000000 Apparent energy (Current Channel A)
ADE7953_APENERGYB, // 0x323 R 24 S 0x000000 Apparent energy (Current Channel B)
ADE7953_OVLVL, // 0x324 R/W 24 U 0xFFFFFF Overvoltage level
ADE7953_OILVL, // 0x325 R/W 24 U 0xFFFFFF Overcurrent level
ADE7953_VPEAK, // 0x326 R 24 U 0x000000 Voltage channel peak
ADE7953_RSTVPEAK, // 0x327 R 24 U 0x000000 Read voltage peak with reset
ADE7953_IAPEAK, // 0x328 R 24 U 0x000000 Current Channel A peak
ADE7953_RSTIAPEAK, // 0x329 R 24 U 0x000000 Read Current Channel A peak with reset
ADE7953_IBPEAK, // 0x32A R 24 U 0x000000 Current Channel B peak
ADE7953_RSTIBPEAK, // 0x32B R 24 U 0x000000 Read Current Channel B peak with reset
ADE7953_IRQENA, // 0x32C R/W 24 U 0x100000 Interrupt enable (Current Channel A, see Table 22)
ADE7953_IRQSTATA, // 0x32D R 24 U 0x000000 Interrupt status (Current Channel A, see Table 23)
ADE7953_RSTIRQSTATA, // 0x32E R 24 U 0x000000 Reset interrupt status (Current Channel A)
ADE7953_IRQENB, // 0x32F R/W 24 U 0x000000 Interrupt enable (Current Channel B, see Table 24)
ADE7953_IRQSTATB, // 0x330 R 24 U 0x000000 Interrupt status (Current Channel B, see Table 25)
ADE7953_RSTIRQSTATB, // 0x331 R 24 U 0x000000 Reset interrupt status (Current Channel B)
ADE7953_CRC = 0x37F, // 0x37F R 32 U 0xFFFFFFFF Checksum
ADE7953_AIGAIN, // 0x380 R/W 24 U 0x400000 Current channel gain (Current Channel A)
ADE7953_AVGAIN, // 0x381 R/W 24 U 0x400000 Voltage channel gain
ADE7953_AWGAIN, // 0x382 R/W 24 U 0x400000 Active power gain (Current Channel A)
ADE7953_AVARGAIN, // 0x383 R/W 24 U 0x400000 Reactive power gain (Current Channel A)
ADE7953_AVAGAIN, // 0x384 R/W 24 U 0x400000 Apparent power gain (Current Channel A)
ADE7953_RESERVED_0X385, // 0x385
ADE7953_AIRMSOS, // 0x386 R/W 24 S 0x000000 IRMS offset (Current Channel A)
ADE7953_RESERVED_0X387, // 0x387
ADE7953_VRMSOS, // 0x388 R/W 24 S 0x000000 VRMS offset
ADE7953_AWATTOS, // 0x389 R/W 24 S 0x000000 Active power offset correction (Current Channel A)
ADE7953_AVAROS, // 0x38A R/W 24 S 0x000000 Reactive power offset correction (Current Channel A)
ADE7953_AVAOS, // 0x38B R/W 24 S 0x000000 Apparent power offset correction (Current Channel A)
ADE7953_BIGAIN, // 0x38C R/W 24 U 0x400000 Current channel gain (Current Channel B)
ADE7953_BVGAIN, // 0x38D R/W 24 U 0x400000 This register should not be modified.
ADE7953_BWGAIN, // 0x38E R/W 24 U 0x400000 Active power gain (Current Channel B)
ADE7953_BVARGAIN, // 0x38F R/W 24 U 0x400000 Reactive power gain (Current Channel B)
ADE7953_BVAGAIN, // 0x390 R/W 24 U 0x400000 Apparent power gain (Current Channel B)
ADE7953_RESERVED_0X391, // 0x391
ADE7953_BIRMSOS, // 0x392 R/W 24 S 0x000000 IRMS offset (Current Channel B)
ADE7953_RESERVED_0X393, // 0x393
ADE7953_RESERVED_0X394, // 0x394
ADE7953_BWATTOS, // 0x395 R/W 24 S 0x000000 Active power offset correction (Current Channel B)
ADE7953_BVAROS, // 0x396 R/W 24 S 0x000000 Reactive power offset correction (Current Channel B)
ADE7953_BVAOS // 0x397 R/W 24 S 0x000000 Apparent power offset correction (Current Channel B)
};
enum Ade7953CalibrationRegisters {
ADE7953_CAL_VGAIN,
ADE7953_CAL_IGAIN,
ADE7953_CAL_WGAIN,
ADE7953_CAL_VAGAIN,
ADE7953_CAL_VARGAIN,
ADE7943_CAL_PHCAL
};
const uint8_t ADE7953_CALIBREGS = 6;
const uint16_t Ade7953CalibRegs[2][ADE7953_CALIBREGS] {
{ ADE7953_AVGAIN, ADE7953_AIGAIN, ADE7953_AWGAIN, ADE7953_AVAGAIN, ADE7953_AVARGAIN, ADE7943_PHCALA },
{ ADE7953_BVGAIN, ADE7953_BIGAIN, ADE7953_BWGAIN, ADE7953_BVAGAIN, ADE7953_BVARGAIN, ADE7943_PHCALB }
};
const uint8_t ADE7953_REGISTERS = 6;
const uint16_t Ade7953Registers[2][ADE7953_REGISTERS] {
#ifdef ADE7953_ACCU_ENERGY
{ ADE7953_IRMSA, ADE7953_AENERGYA, ADE7953_APENERGYA, ADE7953_RENERGYA, ADE7953_VRMS, ADE7943_Period },
{ ADE7953_IRMSB, ADE7953_AENERGYB, ADE7953_APENERGYB, ADE7953_RENERGYB, ADE7953_VRMS, ADE7943_Period }
#else // No ADE7953_ACCU_ENERGY
{ ADE7953_IRMSA, ADE7953_AWATT, ADE7953_AVA, ADE7953_AVAR, ADE7953_VRMS, ADE7943_Period },
{ ADE7953_IRMSB, ADE7953_BWATT, ADE7953_BVA, ADE7953_BVAR, ADE7953_VRMS, ADE7943_Period }
#endif // ADE7953_ACCU_ENERGY
};
#ifdef ADE7953_ACCU_ENERGY
const float ADE7953_LSB_PER_WATTSECOND = 2.5;
const float ADE7953_POWER_CORRECTION = 23.41494; // See https://github.com/arendst/Tasmota/pull/16941
#else // No ADE7953_ACCU_ENERGY
const float ADE7953_LSB_PER_WATTSECOND = 44;
#endif // ADE7953_ACCU_ENERGY
typedef struct {
uint32_t voltage_rms;
uint32_t current_rms;
uint32_t active_power;
int32_t calib_data[ADE7953_CALIBREGS];
} tAde7953Channel;
struct Ade7953 {
uint32_t last_update;
uint32_t voltage_rms[ADE7953_MAX_CHANNEL] = { 0, 0 };
uint32_t current_rms[ADE7953_MAX_CHANNEL] = { 0, 0 };
uint32_t active_power[ADE7953_MAX_CHANNEL] = { 0, 0 };
int32_t calib_data[ADE7953_MAX_CHANNEL][ADE7953_CALIBREGS];
uint8_t init_step = 0;
uint8_t model = 0; // 0 = Shelly 2.5, 1 = Shelly EM, 2 = Shelly Plus 2PM, 3 = Shelly Pro 1PM, 4 = Shelly Pro 2PM, 5 = Shelly Pro 4PM
uint8_t cs_index;
#ifdef USE_ESP32_SPI
int8_t pin_cs[ADE7953_MAX_CHANNEL / 2];
#endif // USE_ESP32_SPI
bool use_spi;
} Ade7953;
/*********************************************************************************************/
int Ade7953RegSize(uint16_t reg) {
int size = 0;
switch ((reg >> 8) & 0x0F) {
case 0x03: // 32-bit
size++;
case 0x02: // 24-bit
size++;
case 0x01: // 16-bit
size++;
case 0x00: // 8-bit
case 0x07:
case 0x08:
size++;
}
return size;
}
#ifdef USE_ESP32_SPI
void Ade7953SpiEnable(void) {
digitalWrite(Ade7953.pin_cs[Ade7953.cs_index], 0);
delayMicroseconds(1); // CS 1uS to SCLK edge
SPI.beginTransaction(SPISettings(1000000, MSBFIRST, SPI_MODE0)); // Set up SPI at 1MHz, MSB first, Capture at rising edge
}
void Ade7953SpiDisable(void) {
SPI.endTransaction();
delayMicroseconds(2); // CS high 1.2uS after SCLK edge (when writing to COMM_LOCK bit)
digitalWrite(Ade7953.pin_cs[Ade7953.cs_index], 1);
}
#endif // USE_ESP32_SPI
void Ade7953Write(uint16_t reg, uint32_t val) {
int size = Ade7953RegSize(reg);
if (size) {
// AddLog(LOG_LEVEL_DEBUG, PSTR("DBG: Write %08X"), val);
#ifdef USE_ESP32_SPI
if (Ade7953.use_spi) {
Ade7953SpiEnable();
SPI.transfer16(reg);
SPI.transfer(0x00); // Write
while (size--) {
SPI.transfer((val >> (8 * size)) & 0xFF); // Write data, MSB first
}
Ade7953SpiDisable();
} else {
#endif // USE_ESP32_SPI
Wire.beginTransmission(ADE7953_ADDR);
Wire.write((reg >> 8) & 0xFF);
Wire.write(reg & 0xFF);
while (size--) {
Wire.write((val >> (8 * size)) & 0xFF); // Write data, MSB first
}
Wire.endTransmission();
delayMicroseconds(5); // Bus-free time minimum 4.7us
#ifdef USE_ESP32_SPI
}
#endif // USE_ESP32_SPI
}
}
int32_t Ade7953Read(uint16_t reg) {
uint32_t response = 0;
int size = Ade7953RegSize(reg);
if (size) {
#ifdef USE_ESP32_SPI
if (Ade7953.use_spi) {
Ade7953SpiEnable();
SPI.transfer16(reg);
SPI.transfer(0x80); // Read
while (size--) {
response = response << 8 | SPI.transfer(0xFF); // receive DATA (MSB first)
}
Ade7953SpiDisable();
} else {
#endif // USE_ESP32_SPI
Wire.beginTransmission(ADE7953_ADDR);
Wire.write((reg >> 8) & 0xFF);
Wire.write(reg & 0xFF);
Wire.endTransmission(0);
Wire.requestFrom(ADE7953_ADDR, size);
if (size <= Wire.available()) {
for (uint32_t i = 0; i < size; i++) {
response = response << 8 | Wire.read(); // receive DATA (MSB first)
}
}
#ifdef USE_ESP32_SPI
}
#endif // USE_ESP32_SPI
}
return response;
}
#ifdef ADE7953_DUMP_REGS
void Ade7953DumpRegs(uint32_t chip) {
AddLog(LOG_LEVEL_DEBUG, PSTR("ADE: *** Chip%d **** SAGCYC DISNOLD Resrvd Resrvd LCYCMOD Resrvd Resrvd PGAV PGAIA PGAIB"), chip +1);
char data[200] = { 0 };
for (uint32_t i = 0; i < 10; i++) {
int32_t value = Ade7953Read(ADE7953_SAGCYC + i);
snprintf_P(data, sizeof(data), PSTR("%s %02X"), data, value); // 8-bit regs
}
AddLog(LOG_LEVEL_DEBUG, PSTR("ADE: *** 0x000..009%s"), data);
AddLog(LOG_LEVEL_DEBUG, PSTR("ADE: *** ZXTOUT LINECYC CONFIG CF1DEN CF2DEN Resrvd Resrvd CFMODE PHCALA PHCALB PFA PFB ANGLEA ANGLEB Period"));
data[0] = '\0';
for (uint32_t i = 0; i < 15; i++) {
int32_t value = Ade7953Read(ADE7953_ZXTOUT + i);
snprintf_P(data, sizeof(data), PSTR("%s %04X"), data, value); // 16-bit regs
}
AddLog(LOG_LEVEL_DEBUG, PSTR("ADE: *** 0x100..10E%s"), data);
AddLog(LOG_LEVEL_DEBUG, PSTR("ADE: *** IGAIN VGAIN WGAIN VARGAIN VAGAIN Resrvd IRMSOS Resrvd VRMSOS WATTOS VAROS VAOS"));
data[0] = '\0';
for (uint32_t i = 0; i < 12; i++) {
int32_t value = Ade7953Read(ADE7953_AIGAIN + i);
snprintf_P(data, sizeof(data), PSTR("%s %06X"), data, value); // 24-bit regs
}
AddLog(LOG_LEVEL_DEBUG, PSTR("ADE: *** 0x380..38B%s"), data);
data[0] = '\0';
for (uint32_t i = 0; i < 12; i++) {
int32_t value = Ade7953Read(ADE7953_BIGAIN + i);
snprintf_P(data, sizeof(data), PSTR("%s %06X"), data, value); // 24-bit regs
}
AddLog(LOG_LEVEL_DEBUG, PSTR("ADE: *** 0x38C..397%s"), data);
}
#endif // ADE7953_DUMP_REGS
void Ade7953SetCalibration(uint32_t regset, uint32_t calibset) {
for (uint32_t i = 0; i < ADE7953_CALIBREGS; i++) {
int32_t value = Ade7953.calib_data[calibset][i];
if (ADE7943_CAL_PHCAL == i) {
// if (ADE7953_PHCAL_DEFAULT == value) { continue; } // ADE7953 reset does NOT always reset all registers
if (value < 0) {
value = abs(value) + 0x200; // Add sign magnitude
}
}
// if (ADE7953_GAIN_DEFAULT == value) { continue; } // ADE7953 reset does NOT always reset all registers
Ade7953Write(Ade7953CalibRegs[regset][i], value);
}
}
void Ade7953Init(void) {
uint32_t chips = 1;
#ifdef USE_ESP32_SPI
chips = (Ade7953.pin_cs[1] >= 0) ? 2 : 1;
#endif // USE_ESP32_SPI
// Init ADE7953 with calibration settings
for (uint32_t chip = 0; chip < chips; chip++) {
Ade7953.cs_index = chip;
#ifdef ADE7953_DUMP_REGS
Ade7953DumpRegs(chip);
#endif // ADE7953_DUMP_REGS
Ade7953Write(ADE7953_CONFIG, 0x0004); // Locking the communication interface (Clear bit COMM_LOCK), Enable HPF
Ade7953Write(0x0FE, 0x00AD); // Unlock register 0x120
Ade7953Write(ADE7953_RESERVED_0X120, 0x0030); // Configure optimum setting
Ade7953Write(ADE7953_DISNOLOAD, 0x07); // Disable no load detection, required before setting thresholds
Ade7953Write(ADE7953_AP_NOLOAD, ADE7953_NO_LOAD_THRESHOLD); // Set no load treshold for active power
Ade7953Write(ADE7953_VAR_NOLOAD, ADE7953_NO_LOAD_THRESHOLD); // Set no load treshold for reactive power
Ade7953Write(ADE7953_DISNOLOAD, 0x00); // Enable no load detection
#ifdef USE_ESP32_SPI
// int32_t value = Ade7953Read(0x702); // Silicon version
// AddLog(LOG_LEVEL_DEBUG, PSTR("ADE: Chip%d version %d"), chip +1, value);
if (1 == chip) {
switch (Ade7953.model) {
case ADE7953_SHELLY_PRO_2PM:
Ade7953SetCalibration(0, 1); // Second ADE7953 A registers set with calibration set 1
break;
case ADE7953_SHELLY_PRO_4PM:
Ade7953SetCalibration(0, 2); // Second ADE7953 A registers set with calibration set 2
Ade7953SetCalibration(1, 3); // Second ADE7953 B registers set with calibration set 3
}
} else {
#endif // USE_ESP32_SPI
Ade7953SetCalibration(0, 0); // First ADE7953 A registers set with calibration set 0
switch (Ade7953.model) {
case ADE7953_SHELLY_25:
case ADE7953_SHELLY_EM:
case ADE7953_SHELLY_PLUS_2PM:
// case ADE7953_SHELLY_PRO_1PM: // Uses defaults for B registers
case ADE7953_SHELLY_PRO_4PM:
Ade7953SetCalibration(1, 1); // First ADE7953 B registers set with calibration set 1
}
#ifdef USE_ESP32_SPI
}
#endif // USE_ESP32_SPI
}
// Report set calibration settings
int32_t regs[ADE7953_CALIBREGS];
for (uint32_t chip = 0; chip < chips; chip++) {
Ade7953.cs_index = chip;
for (uint32_t channel = 0; channel < 2; channel++) {
for (uint32_t i = 0; i < ADE7953_CALIBREGS; i++) {
regs[i] = Ade7953Read(Ade7953CalibRegs[channel][i]);
if (ADE7943_CAL_PHCAL == i) {
if (regs[i] >= 0x0200) {
regs[i] &= 0x01FF; // Clear sign magnitude
regs[i] *= -1; // Make negative
}
}
}
#ifdef USE_ESP32_SPI
AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("ADE: Chip%d CalibRegs%c V %d, I %d, W %d, VA %d, VAr %d, Ph %d"),
chip +1, 'A'+channel, regs[0], regs[1], regs[2], regs[3], regs[4], regs[5]);
#else
AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("ADE: CalibRegs%c V %d, I %d, W %d, VA %d, VAr %d, Ph %d"),
'A'+channel, regs[0], regs[1], regs[2], regs[3], regs[4], regs[5]);
#endif // USE_ESP32_SPI
}
#ifdef ADE7953_DUMP_REGS
Ade7953DumpRegs(chip);
#endif // ADE7953_DUMP_REGS
}
}
void Ade7953GetData(void) {
uint32_t acc_mode = 0;
int32_t reg[ADE7953_MAX_CHANNEL][ADE7953_REGISTERS];
#ifdef USE_ESP32_SPI
if (Ade7953.use_spi) {
uint32_t channel = 0;
for (uint32_t chip = 0; chip < ADE7953_MAX_CHANNEL / 2; chip++) {
if (Ade7953.pin_cs[chip] < 0) { continue; }
Ade7953.cs_index = chip;
for (uint32_t i = 0; i < ADE7953_REGISTERS; i++) {
reg[channel][i] = Ade7953Read(Ade7953Registers[0][i]); // IRMSa, WATTa, VAa, VARa, VRMS, Period
}
channel++;
if (4 == Energy->phase_count) {
for (uint32_t i = 0; i < ADE7953_REGISTERS; i++) {
reg[channel][i] = Ade7953Read(Ade7953Registers[1][i]); // IRMSb, WATTb, VAb, VARb, VRMS, Period
}
channel++;
}
}
} else {
#endif // USE_ESP32_SPI
for (uint32_t channel = 0; channel < 2; channel++) {
uint32_t channel_swap = (ADE7953_SHELLY_25 == Ade7953.model) ? !channel : channel;
for (uint32_t i = 0; i < ADE7953_REGISTERS; i++) {
reg[channel_swap][i] = Ade7953Read(Ade7953Registers[channel][i]);
}
}
acc_mode = Ade7953Read(ADE7953_ACCMODE); // Accumulation mode
#ifdef USE_ESP32_SPI
}
#endif // USE_ESP32_SPI
for (uint32_t i = 0; i < Energy->phase_count; i++) {
AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("ADE: Channel%d ACCMODE 0x%06X, VRMS %d, Period %d, IRMS %d, WATT %d, VA %d, VAR %d"),
i+1, acc_mode, reg[i][4], reg[i][5], reg[i][0], reg[i][1], reg[i][2], reg[i][3]);
}
// If the device is initializing, we read the energy registers to reset them, but don't report the values as the first read may be inaccurate
if (Ade7953.init_step) { return; }
uint32_t apparent_power[ADE7953_MAX_CHANNEL] = { 0, 0 };
uint32_t reactive_power[ADE7953_MAX_CHANNEL] = { 0, 0 };
for (uint32_t channel = 0; channel < Energy->phase_count; channel++) {
Ade7953.voltage_rms[channel] = reg[channel][4];
Ade7953.current_rms[channel] = reg[channel][0];
Ade7953.active_power[channel] = abs(reg[channel][1]);
apparent_power[channel] = abs(reg[channel][2]);
reactive_power[channel] = abs(reg[channel][3]);
if ((ADE7953_SHELLY_EM == Ade7953.model) && (bitRead(acc_mode, 18 +(channel * 3)))) { // VARNLOAD
reactive_power[channel] = 0;
}
}
if (Energy->power_on) { // Powered on
#ifdef USE_ESP32_SPI
float correction = 1.0f;
if (Ade7953.use_spi) { // SPI
uint32_t time = millis();
if (Ade7953.last_update) {
uint32_t difference = time - Ade7953.last_update;
correction = (float)difference / 1000; // Correction to 1 second
// AddLog(LOG_LEVEL_DEBUG, PSTR("ADE: Correction %4_f"), &correction);
}
Ade7953.last_update = time;
}
#endif // USE_ESP32_SPI
float divider;
for (uint32_t channel = 0; channel < Energy->phase_count; channel++) {
Energy->data_valid[channel] = 0;
float power_calibration = (float)EnergyGetCalibration(ENERGY_POWER_CALIBRATION, channel) / 10;
#ifdef ADE7953_ACCU_ENERGY
power_calibration /= ADE7953_POWER_CORRECTION;
#endif // ADE7953_ACCU_ENERGY
float voltage_calibration = (float)EnergyGetCalibration(ENERGY_VOLTAGE_CALIBRATION, channel);
float current_calibration = (float)EnergyGetCalibration(ENERGY_CURRENT_CALIBRATION, channel) * 10;
Energy->frequency[channel] = 223750.0f / ((float)reg[channel][5] + 1);
divider = (Ade7953.calib_data[channel][ADE7953_CAL_VGAIN] != ADE7953_GAIN_DEFAULT) ? 10000 : voltage_calibration;
Energy->voltage[channel] = (float)Ade7953.voltage_rms[channel] / divider;
divider = (Ade7953.calib_data[channel][ADE7953_CAL_WGAIN] != ADE7953_GAIN_DEFAULT) ? ADE7953_LSB_PER_WATTSECOND : power_calibration;
Energy->active_power[channel] = (float)Ade7953.active_power[channel] / divider;
divider = (Ade7953.calib_data[channel][ADE7953_CAL_VARGAIN] != ADE7953_GAIN_DEFAULT) ? ADE7953_LSB_PER_WATTSECOND : power_calibration;
Energy->reactive_power[channel] = (float)reactive_power[channel] / divider;
if (ADE7953_SHELLY_EM == Ade7953.model) {
if (bitRead(acc_mode, 10 +channel)) { // APSIGN
Energy->active_power[channel] *= -1;
}
if (bitRead(acc_mode, 12 +channel)) { // VARSIGN
Energy->reactive_power[channel] *= -1;
}
}
divider = (Ade7953.calib_data[channel][ADE7953_CAL_VAGAIN] != ADE7953_GAIN_DEFAULT) ? ADE7953_LSB_PER_WATTSECOND : power_calibration;
Energy->apparent_power[channel] = (float)apparent_power[channel] / divider;
#ifdef USE_ESP32_SPI
if (Ade7953.use_spi) { // SPI
Energy->active_power[channel] /= correction;
Energy->reactive_power[channel] /= correction;
Energy->apparent_power[channel] /= correction;
}
#endif // USE_ESP32_SPI
if (0 == Energy->active_power[channel]) {
Energy->current[channel] = 0;
} else {
divider = (Ade7953.calib_data[channel][ADE7953_CAL_IGAIN] != ADE7953_GAIN_DEFAULT) ? 100000 : current_calibration;
Energy->current[channel] = (float)Ade7953.current_rms[channel] / divider;
Energy->kWhtoday_delta[channel] += Energy->active_power[channel] * 1000 / 36;
}
}
EnergyUpdateToday();
}
}
void Ade7953EnergyEverySecond(void) {
if (Ade7953.init_step) {
if (2 == Ade7953.init_step) { Ade7953Init(); }
if (1 == Ade7953.init_step) { Ade7953GetData(); } // Read registers but do not display yet
Ade7953.init_step--;
} else {
Ade7953GetData();
}
}
/*********************************************************************************************/
bool Ade7953SetDefaults(const char* json) {
// {"angles":{"angle0":180,"angle1":176}}
// {"rms":{"current_a":4194303,"current_b":4194303,"voltage":1613194},"angles":{"angle0":0,"angle1":0},"powers":{"totactive":{"a":2723574,"b":2723574},"apparent":{"a":2723574,"b":2723574},"reactive":{"a":2723574,"b":2723574}}}
// {"rms":{"current_a":21865738,"current_b":1558533,"voltage_a":1599149,"voltage_b":1597289},"angles":{"angle0":0,"angle1":0},"powers":{"totactive":{"a":106692616,"b":3540894}}}
uint32_t len = strlen(json) +1;
if (len < 7) { return false; } // Too short
char json_buffer[len];
memcpy(json_buffer, json, len); // Keep original safe
JsonParser parser(json_buffer);
JsonParserObject root = parser.getRootObject();
if (!root) {
AddLog(LOG_LEVEL_DEBUG, PSTR("ADE: Invalid JSON"));
return false;
}
// All parameters are optional allowing for partial changes
JsonParserToken val;
char field[20];
for (uint32_t i = 0; i < ADE7953_MAX_CHANNEL; i++) {
JsonParserObject rms = root[PSTR("rms")].getObject();
if (rms) {
val = rms[PSTR("voltage")];
if (val) {
Ade7953.calib_data[i][ADE7953_CAL_VGAIN] = val.getInt();
}
#ifdef USE_ESP32_SPI
snprintf_P(field, sizeof(field), PSTR("voltage_%c"), 'a'+i);
val = rms[field]; // "voltage_a" .. "voltage_d"
if (val) { Ade7953.calib_data[i][ADE7953_CAL_VGAIN] = val.getInt(); }
#endif // USE_ESP32_SPI
snprintf_P(field, sizeof(field), PSTR("current_%c"), 'a'+i);
val = rms[field]; // "current_a" .. "current_d"
if (val) { Ade7953.calib_data[i][ADE7953_CAL_IGAIN] = val.getInt(); }
}
JsonParserObject angles = root[PSTR("angles")].getObject();
if (angles) {
snprintf_P(field, sizeof(field), PSTR("angle%c"), '0'+i);
val = angles[field]; // "angle0" .. "angle3"
if (val) { Ade7953.calib_data[i][ADE7943_CAL_PHCAL] = val.getInt(); }
}
JsonParserObject powers = root[PSTR("powers")].getObject();
if (powers) {
snprintf_P(field, sizeof(field), PSTR("%c"), 'a'+i);
JsonParserObject totactive = powers[PSTR("totactive")].getObject();
if (totactive) {
val = totactive[field]; // "a" .. "d"
if (val) { Ade7953.calib_data[i][ADE7953_CAL_WGAIN] = val.getInt(); }
}
JsonParserObject apparent = powers[PSTR("apparent")].getObject();
if (apparent) {
val = apparent[field]; // "a" .. "d"
if (val) { Ade7953.calib_data[i][ADE7953_CAL_VAGAIN] = val.getInt(); }
}
JsonParserObject reactive = powers[PSTR("reactive")].getObject();
if (reactive) {
val = reactive[field]; // "a" .. "d"
if (val) { Ade7953.calib_data[i][ADE7953_CAL_VARGAIN] = val.getInt(); }
}
}
}
return true;
}
void Ade7953Defaults(void) {
for (uint32_t channel = 0; channel < ADE7953_MAX_CHANNEL; channel++) {
for (uint32_t i = 0; i < ADE7953_CALIBREGS; i++) {
if (ADE7943_CAL_PHCAL == i) {
Ade7953.calib_data[channel][i] = (ADE7953_SHELLY_EM == Ade7953.model) ? ADE7953_PHCAL_DEFAULT_CT : ADE7953_PHCAL_DEFAULT;
} else {
Ade7953.calib_data[channel][i] = ADE7953_GAIN_DEFAULT;
}
}
}
String calib = "";
#ifdef USE_UFILESYS
calib = TfsLoadString("/calib.dat");
#endif // USE_UFILESYS
#ifdef USE_RULES
// rule3 on file#calib.dat do {"angles":{"angle0":180,"angle1":176}} endon
if (!calib.length()) {
calib = RuleLoadFile("CALIB.DAT");
}
#endif // USE_RULES
if (calib.length()) {
// AddLog(LOG_LEVEL_DEBUG, PSTR("ADE: File '%s'"), calib.c_str());
Ade7953SetDefaults(calib.c_str());
}
}
void Ade7953DrvInit(void) {
if (PinUsed(GPIO_ADE7953_IRQ, GPIO_ANY)) { // Irq is not supported...
uint32_t pin_irq = Pin(GPIO_ADE7953_IRQ, GPIO_ANY);
pinMode(pin_irq, INPUT); // Related to resetPins() - Must be set to input
// 0 (1 = Shelly 2.5), 1 (2 = Shelly EM), 2 (3 = Shelly Plus 2PM), 3 (4 = Shelly Pro 1PM), 4 (5 = Shelly Pro 2PM), 5 (6 = Shelly Pro 4PM)
Ade7953.model = GetPin(pin_irq) - AGPIO(GPIO_ADE7953_IRQ);
int pin_reset = Pin(GPIO_ADE7953_RST); // -1 if not defined
#ifdef ESP8266
if (ADE7953_SHELLY_EM == Ade7953.model) {
if (-1 == pin_reset) {
pin_reset = 16;
}
}
#endif
if (pin_reset >= 0) {
digitalWrite(pin_reset, 0);
pinMode(pin_reset, OUTPUT); // Reset pin ADE7953
delay(1); // To initiate a hardware reset, this pin must be brought low for a minimum of 10 μs.
digitalWrite(pin_reset, 1);
if (Ade7953.model < ADE7953_SHELLY_PRO_1PM) {
pinMode(pin_reset, INPUT);
}
}
#ifdef USE_ESP32_SPI
#if (defined(USE_SHELLY_PRO) && defined(USE_MCP23XXX_DRV)) || defined(USE_SHELLY_PRO_V2)
if (Ade7953.model == ADE7953_SHELLY_PRO_4PM) {
ShellyPro4Reset();
}
#endif // USE_SHELLY_PRO
#endif // USE_ESP32_SPI
delay(100); // Need 100mS to init ADE7953
#ifdef USE_ESP32_SPI
Ade7953.pin_cs[0] = -1;
Ade7953.pin_cs[1] = -1;
if (Ade7953.model >= ADE7953_SHELLY_PRO_1PM) { // SPI
if (PinUsed(GPIO_ADE7953_CS)) { // ADE7953 CS1 enabled (Pro 1PM/2PM)
Ade7953.pin_cs[0] = Pin(GPIO_ADE7953_CS);
digitalWrite(Ade7953.pin_cs[0], 1); // ADE7953 CS1 enabled (Pro 2PM)
pinMode(Ade7953.pin_cs[0], OUTPUT);
Ade7953.pin_cs[1] = Pin(GPIO_ADE7953_CS, 1);
if (Ade7953.pin_cs[1] > -1) { // ADE7953 CS2 enabled (Pro 2PM)
digitalWrite(Ade7953.pin_cs[1], 1);
pinMode(Ade7953.pin_cs[1], OUTPUT);
} else {
Ade7953.model = ADE7953_SHELLY_PRO_1PM;
}
Ade7953.cs_index = 0;
Ade7953.use_spi = true;
SPI.begin(Pin(GPIO_SPI_CLK), Pin(GPIO_SPI_MISO), Pin(GPIO_SPI_MOSI), -1);
AddLog(LOG_LEVEL_INFO, PSTR("SPI: ADE7953 found"));
} else {
return; // No CS pin defined
}
} else {
#endif // USE_ESP32_SPI
if (!I2cSetDevice(ADE7953_ADDR)) {
return;
}
I2cSetActiveFound(ADE7953_ADDR, "ADE7953");
#ifdef USE_ESP32_SPI
}
#endif // USE_ESP32_SPI
if (EnergyGetCalibration(ENERGY_POWER_CALIBRATION) == HLW_PREF_PULSE) {
for (uint32_t i = 0; i < ADE7953_MAX_CHANNEL; i++) {
EnergySetCalibration(ENERGY_POWER_CALIBRATION, ADE7953_PREF, i);
EnergySetCalibration(ENERGY_VOLTAGE_CALIBRATION, ADE7953_UREF, i);
EnergySetCalibration(ENERGY_CURRENT_CALIBRATION, ADE7953_IREF, i);
}
}
Ade7953Defaults();
Ade7953.init_step = 3;
// Energy->phase_count = 1;
// Energy->voltage_common = false;
// Energy->frequency_common = false;
// Energy->use_overtemp = false;
if (ADE7953_SHELLY_PRO_1PM == Ade7953.model) {
} else {
Energy->phase_count = 2; // Handle two channels as two phases
if (ADE7953_SHELLY_PRO_2PM == Ade7953.model) {
} else {
Energy->voltage_common = true; // Use common voltage
Energy->frequency_common = true; // Use common frequency
if (ADE7953_SHELLY_PRO_4PM == Ade7953.model) {
Energy->phase_count = 4;
}
}
}
Energy->use_overtemp = true; // Use global temperature for overtemp detection
if (ADE7953_SHELLY_EM == Ade7953.model) {
Energy->local_energy_active_export = true;
}
TasmotaGlobal.energy_driver = XNRG_07;
}
}
bool Ade7953Command(void) {
bool serviced = true;
if (XdrvMailbox.index > ADE7953_MAX_CHANNEL) { return false; };
uint32_t channel = XdrvMailbox.index -1;
if (ADE7953_SHELLY_PRO_4PM != Ade7953.model) {
channel = (2 == XdrvMailbox.index) ? 1 : 0;
}
uint32_t value = (uint32_t)(CharToFloat(XdrvMailbox.data) * 100); // 1.23 = 123
if (CMND_POWERCAL == Energy->command_code) {
if (1 == XdrvMailbox.payload) { XdrvMailbox.payload = ADE7953_PREF; }
// Service in xdrv_03_energy.ino
}
else if (CMND_VOLTAGECAL == Energy->command_code) {
if (1 == XdrvMailbox.payload) { XdrvMailbox.payload = ADE7953_UREF; }
// Service in xdrv_03_energy.ino
}
else if (CMND_CURRENTCAL == Energy->command_code) {
if (1 == XdrvMailbox.payload) { XdrvMailbox.payload = ADE7953_IREF; }
// Service in xdrv_03_energy.ino
}
else if (CMND_POWERSET == Energy->command_code) {
if (XdrvMailbox.data_len && Ade7953.active_power[channel]) {
if ((value > 100) && (value < 200000)) { // Between 1W and 2000W
#ifdef ADE7953_ACCU_ENERGY
float power_calibration = (float)(Ade7953.active_power[channel] * 1000) / value; // 0.00 W
power_calibration *= ADE7953_POWER_CORRECTION;
XdrvMailbox.payload = (uint32_t)power_calibration; // 0.00 W
#else // No ADE7953_ACCU_ENERGY
XdrvMailbox.payload = (Ade7953.active_power[channel] * 1000) / value; // 0.00 W
#endif // ADE7953_ACCU_ENERGY
}
}
}
else if (CMND_VOLTAGESET == Energy->command_code) {
if (XdrvMailbox.data_len && Ade7953.voltage_rms[channel]) {
if ((value > 10000) && (value < 26000)) { // Between 100V and 260V
XdrvMailbox.payload = (Ade7953.voltage_rms[channel] * 100) / value; // 0.00 V
}
}
}
else if (CMND_CURRENTSET == Energy->command_code) {
if (XdrvMailbox.data_len && Ade7953.current_rms[channel]) {
if ((value > 2000) && (value < 1000000)) { // Between 20mA and 10A
XdrvMailbox.payload = ((Ade7953.current_rms[channel] * 100) / value) * 100; // 0.00 mA
}
}
}
else serviced = false; // Unknown command
return serviced;
}
/*********************************************************************************************\
* Interface
\*********************************************************************************************/
bool Xnrg07(uint32_t function) {
if (!I2cEnabled(XI2C_07) && (SPI_MOSI_MISO != TasmotaGlobal.spi_enabled)) { return false; }
bool result = false;
switch (function) {
#ifdef USE_ESP32_SPI
case FUNC_ENERGY_EVERY_SECOND: // Use energy interrupt timer (fails on SPI)
if (!Ade7953.use_spi) { // No SPI
Ade7953EnergyEverySecond();
}
break;
case FUNC_EVERY_SECOND: // Use loop timer (without interrupt)
if (Ade7953.use_spi) { // SPI
Ade7953EnergyEverySecond();
}
break;
#else // ESP8266
case FUNC_ENERGY_EVERY_SECOND: // Use energy interrupt timer
Ade7953EnergyEverySecond();
break;
#endif // USE_ESP32_SPI
case FUNC_COMMAND:
result = Ade7953Command();
break;
case FUNC_PRE_INIT:
Ade7953DrvInit();
break;
}
return result;
}
#endif // USE_ADE7953
#endif // USE_ENERGY_SENSOR
#endif // USE_I2C or USE_ESP_SPI