Skip to content

Commit 016349d

Browse files
authored
Merge pull request #82 from basmeerman/work/plan-08
feat: per-phase energy via MQTT (Plan-08 #62)
2 parents a97be6c + 7cca45e commit 016349d

File tree

5 files changed

+65
-1
lines changed

5 files changed

+65
-1
lines changed

SmartEVSE-3/src/esp32.cpp

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -936,11 +936,17 @@ void SetupMQTTClient() {
936936
if (MainsMeter.Type) {
937937
MQTTclient.announce("Mains Import Active Energy", "sensor", optional_payload);
938938
MQTTclient.announce("Mains Export Active Energy", "sensor", optional_payload);
939+
MQTTclient.announce("Mains Energy L1", "sensor", optional_payload);
940+
MQTTclient.announce("Mains Energy L2", "sensor", optional_payload);
941+
MQTTclient.announce("Mains Energy L3", "sensor", optional_payload);
939942
}
940943

941944
if (EVMeter.Type) {
942945
MQTTclient.announce("EV Import Active Energy", "sensor", optional_payload);
943946
MQTTclient.announce("EV Export Active Energy", "sensor", optional_payload);
947+
MQTTclient.announce("EV Energy L1", "sensor", optional_payload);
948+
MQTTclient.announce("EV Energy L2", "sensor", optional_payload);
949+
MQTTclient.announce("EV Energy L3", "sensor", optional_payload);
944950
//set the parameters for and MQTTclient.announce other sensor entities:
945951
optional_payload = MQTTclient.jsna("device_class","power") + MQTTclient.jsna("unit_of_measurement","W") + MQTTclient.jsna("state_class","measurement");
946952
MQTTclient.announce("EV Charge Power", "sensor", optional_payload);
@@ -1110,6 +1116,13 @@ void mqttPublishData() {
11101116
mqtt_pub_int(MQTT_SLOT_MAINS_POWER_L1, "/MainsPowerL1", MainsMeter.Power[0], false, now_s);
11111117
mqtt_pub_int(MQTT_SLOT_MAINS_POWER_L2, "/MainsPowerL2", MainsMeter.Power[1], false, now_s);
11121118
mqtt_pub_int(MQTT_SLOT_MAINS_POWER_L3, "/MainsPowerL3", MainsMeter.Power[2], false, now_s);
1119+
// Zero-value guard: suppress per-phase energy when meter hasn't reported it
1120+
if (MainsMeter.EnergyPhase[0] > 0)
1121+
mqtt_pub_int(MQTT_SLOT_MAINS_ENERGY_L1, "/MainsEnergyL1", MainsMeter.EnergyPhase[0], false, now_s);
1122+
if (MainsMeter.EnergyPhase[1] > 0)
1123+
mqtt_pub_int(MQTT_SLOT_MAINS_ENERGY_L2, "/MainsEnergyL2", MainsMeter.EnergyPhase[1], false, now_s);
1124+
if (MainsMeter.EnergyPhase[2] > 0)
1125+
mqtt_pub_int(MQTT_SLOT_MAINS_ENERGY_L3, "/MainsEnergyL3", MainsMeter.EnergyPhase[2], false, now_s);
11131126
}
11141127
if (EVMeter.Type) {
11151128
mqtt_pub_int(MQTT_SLOT_EV_L1, "/EVCurrentL1", EVMeter.Irms[0], false, now_s);
@@ -1123,6 +1136,12 @@ void mqttPublishData() {
11231136
mqtt_pub_int(MQTT_SLOT_EV_POWER_L1, "/EVPowerL1", EVMeter.Power[0], false, now_s);
11241137
mqtt_pub_int(MQTT_SLOT_EV_POWER_L2, "/EVPowerL2", EVMeter.Power[1], false, now_s);
11251138
mqtt_pub_int(MQTT_SLOT_EV_POWER_L3, "/EVPowerL3", EVMeter.Power[2], false, now_s);
1139+
if (EVMeter.EnergyPhase[0] > 0)
1140+
mqtt_pub_int(MQTT_SLOT_EV_ENERGY_L1, "/EVEnergyL1", EVMeter.EnergyPhase[0], false, now_s);
1141+
if (EVMeter.EnergyPhase[1] > 0)
1142+
mqtt_pub_int(MQTT_SLOT_EV_ENERGY_L2, "/EVEnergyL2", EVMeter.EnergyPhase[1], false, now_s);
1143+
if (EVMeter.EnergyPhase[2] > 0)
1144+
mqtt_pub_int(MQTT_SLOT_EV_ENERGY_L3, "/EVEnergyL3", EVMeter.EnergyPhase[2], false, now_s);
11261145
}
11271146
mqtt_pub_int(MQTT_SLOT_ESP_TEMP, "/ESPTemp", TempEVSE, false, now_s);
11281147
mqtt_pub_str(MQTT_SLOT_MODE, "/Mode", AccessStatus == OFF ? "Off" : AccessStatus == PAUSE ? "Pause" : Mode > 3 ? "N/A" : StrMode[Mode], true, now_s);

SmartEVSE-3/src/main.cpp

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -317,6 +317,7 @@ EXT void PowerPanicCtrl(uint8_t enable);
317317
EXT uint8_t ReadESPdata(char *buf);
318318

319319
extern void requestEnergyMeasurement(uint8_t Meter, uint8_t Address, bool Export);
320+
extern bool requestPhaseEnergyMeasurement(uint8_t Meter, uint8_t Address);
320321
extern void requestNodeConfig(uint8_t NodeNr);
321322
extern void requestPowerMeasurement(uint8_t Meter, uint8_t Address, uint16_t PRegister);
322323
extern void requestNodeStatus(uint8_t NodeNr);
@@ -1443,6 +1444,26 @@ void requestEnergyMeasurement(uint8_t Meter, uint8_t Address, bool Export) {
14431444
requestMeasurement(Meter, Address, Register, Count);
14441445
}
14451446

1447+
/**
1448+
* Request per-phase energy measurement from meters that support it.
1449+
* Returns true if a request was sent, false if meter doesn't support per-phase energy.
1450+
*
1451+
* @param uint8_t Meter type
1452+
* @param uint8_t Address
1453+
*/
1454+
bool requestPhaseEnergyMeasurement(uint8_t Meter, uint8_t Address) {
1455+
switch (Meter) {
1456+
case EM_EASTRON3P:
1457+
case EM_EASTRON3P_INV:
1458+
// Eastron SDM630: L1/L2/L3 total active energy at registers 0x015A-0x015F
1459+
// 3 phases × 2 registers each = 6 registers (FLOAT32)
1460+
ModbusReadInputRequest(Address, EMConfig[Meter].Function, 0x015A, 6);
1461+
return true;
1462+
default:
1463+
return false;
1464+
}
1465+
}
1466+
14461467
/**
14471468
* Send Power measurement request over modbus
14481469
*
@@ -1992,6 +2013,14 @@ void ModbusRequestLoop() {
19922013
}
19932014
ModbusRequest++;
19942015
// fall through
2016+
case 22:
2017+
// Request per-phase energy if Mainsmeter supports it (every ~60s via energytimer)
2018+
if (MainsMeter.Type && energytimer == 0) {
2019+
if (requestPhaseEnergyMeasurement(MainsMeter.Type, MainsMeter.Address))
2020+
break;
2021+
}
2022+
ModbusRequest++;
2023+
// fall through
19952024
default:
19962025
// slave never gets here
19972026
// what about normal mode with no meters attached?

SmartEVSE-3/src/meter.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,9 @@ Meter::Meter(uint8_t type, uint8_t address, uint8_t timeout) {
5454
Imeasured = 0;
5555
Import_active_energy = 0;
5656
Export_active_energy = 0;
57+
EnergyPhase[0] = 0;
58+
EnergyPhase[1] = 0;
59+
EnergyPhase[2] = 0;
5760
Energy = 0;
5861
#if !defined(SMARTEVSE_VERSION) || SMARTEVSE_VERSION >=30 && SMARTEVSE_VERSION < 40 //not on ESP32 v4
5962
Timeout = timeout;
@@ -450,6 +453,12 @@ void Meter::ResponseToMeasurement(ModBus MB) {
450453
else
451454
Export_active_energy = receiveEnergyMeasurement(MB.Data);
452455
UpdateEnergies();
456+
} else if (MB.Register == 0x015A && (Type == EM_EASTRON3P || Type == EM_EASTRON3P_INV)) {
457+
// Per-phase total active energy (Eastron SDM630: registers 0x015A-0x015F)
458+
// 3 × FLOAT32 values in kWh, convert to Wh
459+
for (int x = 0; x < 3; x++) {
460+
EnergyPhase[x] = decodeMeasurement(MB.Data, x, EMConfig[Type].EDivisor - 3);
461+
}
453462
}
454463

455464
}

SmartEVSE-3/src/meter.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,7 @@ class Meter {
9292
#endif
9393
int32_t Import_active_energy; // Imported active energy
9494
int32_t Export_active_energy; // Exported active energy
95+
int32_t EnergyPhase[3]; // Per-phase net energy (Wh) — 0 if meter doesn't support it
9596
int32_t Energy; // Wh -> Import_active_energy - Export_active_energy
9697
int32_t EnergyCharged; // kWh meter value energy charged. (Wh) (will reset if state changes from A->B)
9798
int32_t EnergyMeterStart; // kWh meter value is stored once EV is connected to EVSE (Wh)

SmartEVSE-3/src/mqtt_publish.h

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
extern "C" {
1010
#endif
1111

12-
#define MQTT_CACHE_MAX_SLOTS 72 /* 62 current topics + headroom */
12+
#define MQTT_CACHE_MAX_SLOTS 84 /* 74 current topics + headroom */
1313

1414
/* One slot per published MQTT topic, in mqttPublishData() call order */
1515
typedef enum {
@@ -21,6 +21,9 @@ typedef enum {
2121
MQTT_SLOT_MAINS_POWER_L1,
2222
MQTT_SLOT_MAINS_POWER_L2,
2323
MQTT_SLOT_MAINS_POWER_L3,
24+
MQTT_SLOT_MAINS_ENERGY_L1,
25+
MQTT_SLOT_MAINS_ENERGY_L2,
26+
MQTT_SLOT_MAINS_ENERGY_L3,
2427
MQTT_SLOT_EV_L1,
2528
MQTT_SLOT_EV_L2,
2629
MQTT_SLOT_EV_L3,
@@ -29,6 +32,9 @@ typedef enum {
2932
MQTT_SLOT_EV_POWER_L1,
3033
MQTT_SLOT_EV_POWER_L2,
3134
MQTT_SLOT_EV_POWER_L3,
35+
MQTT_SLOT_EV_ENERGY_L1,
36+
MQTT_SLOT_EV_ENERGY_L2,
37+
MQTT_SLOT_EV_ENERGY_L3,
3238
MQTT_SLOT_ESP_TEMP,
3339
MQTT_SLOT_MODE,
3440
MQTT_SLOT_MAX_CURRENT,

0 commit comments

Comments
 (0)