2019-04-14 17:20:24 +01:00
|
|
|
/*
|
|
|
|
xnrg_07_ade7953.ino - ADE7953 energy sensor support for Sonoff-Tasmota
|
|
|
|
|
|
|
|
Copyright (C) 2019 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_I2C
|
|
|
|
#ifdef USE_ENERGY_SENSOR
|
|
|
|
#ifdef USE_ADE7953
|
|
|
|
/*********************************************************************************************\
|
|
|
|
* ADE7953 - Energy (Shelly 2.5)
|
|
|
|
*
|
|
|
|
* Based on datasheet from https://www.analog.com/en/products/ade7953.html
|
|
|
|
*
|
|
|
|
* I2C Address: 0x38
|
|
|
|
\*********************************************************************************************/
|
|
|
|
|
|
|
|
#define XNRG_07 7
|
|
|
|
|
|
|
|
#define ADE7953_PREF 1540
|
|
|
|
#define ADE7953_UREF 26000
|
|
|
|
#define ADE7953_IREF 10000
|
|
|
|
|
|
|
|
#define ADE7953_ADDR 0x38
|
|
|
|
|
2019-08-16 13:41:02 +01:00
|
|
|
struct Ade7953 {
|
|
|
|
uint32_t active_power = 0;
|
|
|
|
uint32_t active_power1 = 0;
|
|
|
|
uint32_t active_power2 = 0;
|
|
|
|
uint32_t current_rms = 0;
|
|
|
|
uint32_t current_rms1 = 0;
|
|
|
|
uint32_t current_rms2 = 0;
|
|
|
|
uint32_t voltage_rms = 0;
|
|
|
|
uint8_t init_step = 0;
|
|
|
|
} Ade7953;
|
2019-04-14 17:20:24 +01:00
|
|
|
|
|
|
|
int Ade7953RegSize(uint16_t reg)
|
|
|
|
{
|
|
|
|
int size = 0;
|
|
|
|
switch ((reg >> 8) & 0x0F) {
|
|
|
|
case 0x03:
|
|
|
|
size++;
|
|
|
|
case 0x02:
|
|
|
|
size++;
|
|
|
|
case 0x01:
|
|
|
|
size++;
|
|
|
|
case 0x00:
|
|
|
|
case 0x07:
|
|
|
|
case 0x08:
|
|
|
|
size++;
|
|
|
|
}
|
|
|
|
return size;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Ade7953Write(uint16_t reg, uint32_t val)
|
|
|
|
{
|
|
|
|
int size = Ade7953RegSize(reg);
|
|
|
|
if (size) {
|
|
|
|
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
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
uint32_t Ade7953Read(uint16_t reg)
|
|
|
|
{
|
|
|
|
uint32_t response = 0;
|
|
|
|
|
|
|
|
int size = Ade7953RegSize(reg);
|
|
|
|
if (size) {
|
|
|
|
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()) {
|
2019-06-30 15:44:36 +01:00
|
|
|
for (uint32_t i = 0; i < size; i++) {
|
2019-04-14 17:20:24 +01:00
|
|
|
response = response << 8 | Wire.read(); // receive DATA (MSB first)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return response;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Ade7953Init(void)
|
|
|
|
{
|
|
|
|
Ade7953Write(0x102, 0x0004); // Locking the communication interface (Clear bit COMM_LOCK), Enable HPF
|
|
|
|
Ade7953Write(0x0FE, 0x00AD); // Unlock register 0x120
|
|
|
|
Ade7953Write(0x120, 0x0030); // Configure optimum setting
|
|
|
|
}
|
|
|
|
|
|
|
|
void Ade7953GetData(void)
|
|
|
|
{
|
2019-07-04 09:36:38 +01:00
|
|
|
int32_t active_power;
|
|
|
|
|
2019-08-16 13:41:02 +01:00
|
|
|
Ade7953.voltage_rms = Ade7953Read(0x31C); // Both relays
|
|
|
|
Ade7953.current_rms1 = Ade7953Read(0x31B); // Relay 1
|
|
|
|
if (Ade7953.current_rms1 < 2000) { // No load threshold (20mA)
|
|
|
|
Ade7953.current_rms1 = 0;
|
|
|
|
Ade7953.active_power1 = 0;
|
2019-04-14 17:20:24 +01:00
|
|
|
} else {
|
2019-07-04 09:36:38 +01:00
|
|
|
active_power = (int32_t)Ade7953Read(0x313) * -1; // Relay 1
|
2019-08-16 13:41:02 +01:00
|
|
|
Ade7953.active_power1 = (active_power > 0) ? active_power : 0;
|
2019-04-14 17:20:24 +01:00
|
|
|
}
|
2019-08-16 13:41:02 +01:00
|
|
|
Ade7953.current_rms2 = Ade7953Read(0x31A); // Relay 2
|
|
|
|
if (Ade7953.current_rms2 < 2000) { // No load threshold (20mA)
|
|
|
|
Ade7953.current_rms2 = 0;
|
|
|
|
Ade7953.active_power2 = 0;
|
2019-04-14 17:20:24 +01:00
|
|
|
} else {
|
2019-07-04 09:36:38 +01:00
|
|
|
active_power = (int32_t)Ade7953Read(0x312); // Relay 2
|
2019-08-16 13:41:02 +01:00
|
|
|
Ade7953.active_power2 = (active_power > 0) ? active_power : 0;
|
2019-04-14 17:20:24 +01:00
|
|
|
}
|
|
|
|
// First phase only supports accumulated Current and Power
|
2019-08-16 13:41:02 +01:00
|
|
|
Ade7953.current_rms = Ade7953.current_rms1 + Ade7953.current_rms2;
|
|
|
|
Ade7953.active_power = Ade7953.active_power1 + Ade7953.active_power2;
|
2019-04-14 17:20:24 +01:00
|
|
|
|
2019-06-21 13:31:08 +01:00
|
|
|
AddLog_P2(LOG_LEVEL_DEBUG, PSTR("ADE: U %d, I %d + %d = %d, P %d + %d = %d"),
|
2019-08-16 13:41:02 +01:00
|
|
|
Ade7953.voltage_rms, Ade7953.current_rms1, Ade7953.current_rms2, Ade7953.current_rms, Ade7953.active_power1, Ade7953.active_power2, Ade7953.active_power);
|
2019-06-21 13:31:08 +01:00
|
|
|
|
2019-08-16 13:41:02 +01:00
|
|
|
if (Energy.power_on) { // Powered on
|
|
|
|
Energy.voltage = (float)Ade7953.voltage_rms / Settings.energy_voltage_calibration;
|
|
|
|
Energy.active_power = (float)Ade7953.active_power / (Settings.energy_power_calibration / 10);
|
|
|
|
if (0 == Energy.active_power) {
|
|
|
|
Energy.current = 0;
|
2019-04-14 17:20:24 +01:00
|
|
|
} else {
|
2019-08-16 13:41:02 +01:00
|
|
|
Energy.current = (float)Ade7953.current_rms / (Settings.energy_current_calibration * 10);
|
2019-04-14 17:20:24 +01:00
|
|
|
}
|
|
|
|
} else { // Powered off
|
2019-08-16 13:41:02 +01:00
|
|
|
Energy.voltage = 0;
|
|
|
|
Energy.active_power = 0;
|
|
|
|
Energy.current = 0;
|
2019-04-14 17:20:24 +01:00
|
|
|
}
|
|
|
|
|
2019-08-16 13:41:02 +01:00
|
|
|
if (Ade7953.active_power) {
|
|
|
|
Energy.kWhtoday_delta += ((Ade7953.active_power * (100000 / (Settings.energy_power_calibration / 10))) / 3600);
|
2019-04-14 17:20:24 +01:00
|
|
|
EnergyUpdateToday();
|
|
|
|
}
|
2019-06-21 13:31:08 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void Ade7953EnergyEverySecond()
|
|
|
|
{
|
2019-08-16 13:41:02 +01:00
|
|
|
if (Ade7953.init_step) {
|
|
|
|
if (1 == Ade7953.init_step) {
|
2019-04-14 17:20:24 +01:00
|
|
|
Ade7953Init();
|
|
|
|
}
|
2019-08-16 13:41:02 +01:00
|
|
|
Ade7953.init_step--;
|
2019-06-21 13:31:08 +01:00
|
|
|
} else {
|
2019-04-14 17:20:24 +01:00
|
|
|
Ade7953GetData();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Ade7953DrvInit(void)
|
|
|
|
{
|
2019-09-08 15:57:56 +01:00
|
|
|
if (i2c_flg && (pin[GPIO_ADE7953_IRQ] < 99)) { // Irq on GPIO16 is not supported...
|
|
|
|
delay(100); // Need 100mS to init ADE7953
|
|
|
|
if (I2cDevice(ADE7953_ADDR)) {
|
|
|
|
if (HLW_PREF_PULSE == Settings.energy_power_calibration) {
|
|
|
|
Settings.energy_power_calibration = ADE7953_PREF;
|
|
|
|
Settings.energy_voltage_calibration = ADE7953_UREF;
|
|
|
|
Settings.energy_current_calibration = ADE7953_IREF;
|
|
|
|
}
|
|
|
|
AddLog_P2(LOG_LEVEL_DEBUG, S_LOG_I2C_FOUND_AT, "ADE7953", ADE7953_ADDR);
|
|
|
|
Ade7953.init_step = 2;
|
|
|
|
energy_flg = XNRG_07;
|
2019-04-14 17:20:24 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool Ade7953Command(void)
|
|
|
|
{
|
|
|
|
bool serviced = true;
|
|
|
|
|
2019-07-01 17:20:43 +01:00
|
|
|
uint32_t value = (uint32_t)(CharToFloat(XdrvMailbox.data) * 100); // 1.23 = 123
|
2019-04-14 17:20:24 +01:00
|
|
|
|
2019-08-16 13:41:02 +01:00
|
|
|
if (CMND_POWERCAL == Energy.command_code) {
|
2019-04-14 17:20:24 +01:00
|
|
|
if (1 == XdrvMailbox.payload) { XdrvMailbox.payload = ADE7953_PREF; }
|
|
|
|
// Service in xdrv_03_energy.ino
|
|
|
|
}
|
2019-08-16 13:41:02 +01:00
|
|
|
else if (CMND_VOLTAGECAL == Energy.command_code) {
|
2019-04-14 17:20:24 +01:00
|
|
|
if (1 == XdrvMailbox.payload) { XdrvMailbox.payload = ADE7953_UREF; }
|
|
|
|
// Service in xdrv_03_energy.ino
|
|
|
|
}
|
2019-08-16 13:41:02 +01:00
|
|
|
else if (CMND_CURRENTCAL == Energy.command_code) {
|
2019-04-14 17:20:24 +01:00
|
|
|
if (1 == XdrvMailbox.payload) { XdrvMailbox.payload = ADE7953_IREF; }
|
|
|
|
// Service in xdrv_03_energy.ino
|
|
|
|
}
|
2019-08-16 13:41:02 +01:00
|
|
|
else if (CMND_POWERSET == Energy.command_code) {
|
|
|
|
if (XdrvMailbox.data_len && Ade7953.active_power) {
|
2019-04-18 13:13:14 +01:00
|
|
|
if ((value > 100) && (value < 200000)) { // Between 1W and 2000W
|
2019-08-16 13:41:02 +01:00
|
|
|
Settings.energy_power_calibration = (Ade7953.active_power * 1000) / value; // 0.00 W
|
2019-04-18 13:13:14 +01:00
|
|
|
}
|
2019-04-14 17:20:24 +01:00
|
|
|
}
|
|
|
|
}
|
2019-08-16 13:41:02 +01:00
|
|
|
else if (CMND_VOLTAGESET == Energy.command_code) {
|
|
|
|
if (XdrvMailbox.data_len && Ade7953.voltage_rms) {
|
2019-04-18 13:13:14 +01:00
|
|
|
if ((value > 10000) && (value < 26000)) { // Between 100V and 260V
|
2019-08-16 13:41:02 +01:00
|
|
|
Settings.energy_voltage_calibration = (Ade7953.voltage_rms * 100) / value; // 0.00 V
|
2019-04-18 13:13:14 +01:00
|
|
|
}
|
2019-04-14 17:20:24 +01:00
|
|
|
}
|
|
|
|
}
|
2019-08-16 13:41:02 +01:00
|
|
|
else if (CMND_CURRENTSET == Energy.command_code) {
|
|
|
|
if (XdrvMailbox.data_len && Ade7953.current_rms) {
|
2019-04-18 13:13:14 +01:00
|
|
|
if ((value > 2000) && (value < 1000000)) { // Between 20mA and 10A
|
2019-08-16 13:41:02 +01:00
|
|
|
Settings.energy_current_calibration = ((Ade7953.current_rms * 100) / value) * 100; // 0.00 mA
|
2019-04-18 13:13:14 +01:00
|
|
|
}
|
2019-04-14 17:20:24 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else serviced = false; // Unknown command
|
|
|
|
|
|
|
|
return serviced;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*********************************************************************************************\
|
|
|
|
* Interface
|
|
|
|
\*********************************************************************************************/
|
|
|
|
|
2019-09-08 15:57:56 +01:00
|
|
|
bool Xnrg07(uint8_t function)
|
2019-04-14 17:20:24 +01:00
|
|
|
{
|
2019-09-08 15:57:56 +01:00
|
|
|
bool result = false;
|
|
|
|
|
|
|
|
switch (function) {
|
|
|
|
case FUNC_ENERGY_EVERY_SECOND:
|
|
|
|
Ade7953EnergyEverySecond();
|
|
|
|
break;
|
|
|
|
case FUNC_COMMAND:
|
|
|
|
result = Ade7953Command();
|
|
|
|
break;
|
|
|
|
case FUNC_PRE_INIT:
|
|
|
|
Ade7953DrvInit();
|
|
|
|
break;
|
2019-04-14 17:20:24 +01:00
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif // USE_ADE7953
|
|
|
|
#endif // USE_ENERGY_SENSOR
|
|
|
|
#endif // USE_I2C
|