Hey Guys,
I´ve just wanted to share my Projekt with you.
I got so much help and Ideas from this community so i want to give something back.
I´ve bought an Marstek Venus E Battery (Firmware v151) with 5,15Kwh and I have no Shelly Pro 3 EM as SmartMeter.
I use an esp8266 on my energymeter with esphome to read alle values via SML direct from it.
Problem:
I don´t want another device or service like Unimeter,B2500 oder Energy2Shelly to send the Power Data to my Battery. i like it when it even works when my whole system is on maintenance.
I tried to use unimeter and Energy2Shelly but the Battery did not accept them because i just could send Power from all phases(Added) at once and the emulators divide it by 3 and send it even.
It´s Possible to send all values over MQTT but that´s an another point of failure.
The battery tests the Shelly to determine it´s own Phase.
So it didn´t work.
Solution:
My already in Place esphome smart meter.
My Smartmeter Esphome device waits for a UDP Packet on Port 1010 with Content:
…EM.GetStatus…
That packet is send every second via Broadcast when the Battery has no smart meter associated. I´ve checked wit Wireshark. So I could find the neccessary Port 22222.
Then i respond with and udp json Packet that´s formatted as it´s a Shelly Pro 3 EM.
The Marstek Battery Accepts the value because it thinks it´s an Shelly Pro 3 EM.
I´ve found analyses online what the answer JSON has to look like.
I could not make the UDP request dynamic so I´ve set the IP and Port from the Battery manual.
As soon as the Battery Accepts the Battery it send its packets via unicast to my SML Reader.
My Opinion:
It is a bit quick and dirty, but it works for me.
I´m not a programmer but i hope some one can use it.
My Goal was that the Battery gets a precise Energy reading every second.
And it works like a charm for my zero feed in.
Feel free to optimize and change it. It´s just an Idea starter.
Here´s the code from my Energy Meter:
uart:
id: uart_bus
tx_pin: GPIO1
rx_pin: GPIO3
baud_rate: 9600
data_bits: 8
parity: NONE
stop_bits: 1
sml:
id: mysml
uart_id: uart_bus
sensor:
- platform: sml
name: "Zählerstand"
sml_id: mysml
obis_code: "1-0:1.8.0"
unit_of_measurement: kWh
accuracy_decimals: 4
device_class: energy
state_class: total_increasing
filters:
- multiply: 0.0000001
- platform: sml
name: "Energieverbrauch Alles"
id: verbrauch
sml_id: mysml
obis_code: "1-0:16.7.0"
unit_of_measurement: W
accuracy_decimals: 2
filters:
- multiply: 0.01
- platform: sml
name: "Energieverbrauch L1"
sml_id: mysml
id: l1
obis_code: "1-0:36.7.0"
unit_of_measurement: W
accuracy_decimals: 2
filters:
- multiply: 0.01
- platform: sml
name: "Energieverbrauch L2"
sml_id: mysml
id: l2
obis_code: "1-0:56.7.0"
unit_of_measurement: W
accuracy_decimals: 2
filters:
- multiply: 0.01
- platform: sml
name: "Energieverbrauch L3"
sml_id: mysml
id: l3
obis_code: "1-0:76.7.0"
unit_of_measurement: W
accuracy_decimals: 2
filters:
- multiply: 0.01
- platform: sml
name: "Spannung L1"
sml_id: mysml
id: u1
obis_code: "1-0:32.7.0"
unit_of_measurement: V
accuracy_decimals: 2
filters:
- multiply: 0.1
- platform: sml
name: "Spannung L2"
sml_id: mysml
id: u2
obis_code: "1-0:52.7.0"
unit_of_measurement: V
accuracy_decimals: 2
filters:
- multiply: 0.1
- platform: sml
name: "Spannung L3"
sml_id: mysml
id: u3
obis_code: "1-0:72.7.0"
unit_of_measurement: V
accuracy_decimals: 2
filters:
- multiply: 0.1
- platform: template
name: "Strom L1"
id: i1
unit_of_measurement: "A"
accuracy_decimals: 2
update_interval: 1s
lambda: |-
if (id(u1).has_state() && id(l1).has_state() && id(u1).state > 0) {
return fabs(id(l1).state / id(u1).state);
} else {
return 0.0;
}
- platform: template
name: "Strom L2"
id: i2
unit_of_measurement: "A"
accuracy_decimals: 2
update_interval: 1s
lambda: |-
if (id(u2).has_state() && id(l2).has_state() && id(u2).state > 0) {
return fabs(id(l2).state / id(u2).state);
} else {
return 0.0;
}
- platform: template
name: "Strom L3"
id: i3
unit_of_measurement: "A"
accuracy_decimals: 2
update_interval: 1s
lambda: |-
if (id(u3).has_state() && id(l3).has_state() && id(u3).state > 0) {
return fabs(id(l3).state / id(u3).state);
} else {
return 0.0;
}
- platform: template
name: "Strom Gesamt"
id: i_total
unit_of_measurement: "A"
accuracy_decimals: 2
update_interval: 1s
lambda: |-
return id(i1).state + id(i2).state + id(i3).state;
- platform: template
name: "Leistungsfaktor L1"
id: pf1
accuracy_decimals: 2
update_interval: 60s
lambda: |-
return 1.0;
- platform: template
name: "Leistungsfaktor L2"
id: pf2
accuracy_decimals: 2
update_interval: 60s
lambda: |-
return 1.0;
- platform: template
name: "Leistungsfaktor L3"
id: pf3
accuracy_decimals: 2
update_interval: 60s
lambda: |-
return 1.0;
- platform: template
name: "Netzfrequenz"
id: freq
unit_of_measurement: "Hz"
accuracy_decimals: 1
update_interval: 60s
lambda: |-
return 50.0;
udp:
- id: udp_shelly_sender
port:
listen_port: 18001 # Any Port not used
broadcast_port: 22222 # Destination Port Battery
addresses:
- 192.168.0.55 # Ip from Battery
- id: udp_server
port:
listen_port: 1010
broadcast_port: 1010
on_receive:
then:
- lambda: |-
std::string msg(data.begin(), data.end());
if (msg.find("\"method\":\"EM.GetStatus\"") == std::string::npos) return;
- udp.write:
id: udp_shelly_sender
data: !lambda |-
char buf[512];
int len = snprintf(buf, sizeof(buf),
"{"
"\"id\":0,"
"\"a_current\":%.2f,\"a_voltage\":%.1f,\"a_act_power\":%.2f,"
"\"a_aprt_power\":%.2f,\"a_pf\":%.2f,\"a_freq\":%.1f,"
"\"b_current\":%.2f,\"b_voltage\":%.1f,\"b_act_power\":%.2f,"
"\"b_aprt_power\":%.2f,\"b_pf\":%.2f,\"b_freq\":%.1f,"
"\"c_current\":%.2f,\"c_voltage\":%.1f,\"c_act_power\":%.2f,"
"\"c_aprt_power\":%.2f,\"c_pf\":%.2f,\"c_freq\":%.1f,"
"\"total_current\":%.2f,"
"\"total_act_power\":%.2f,"
"\"total_aprt_power\":%.2f"
"}",
id(i1).state, id(u1).state, id(l1).state,
id(l1).state, id(pf1).state, id(freq).state,
id(i2).state, id(u2).state, id(l2).state,
id(l2).state, id(pf2).state, id(freq).state,
id(i3).state, id(u3).state, id(l3).state,
id(l3).state, id(pf3).state, id(freq).state,
id(i_total).state,
id(verbrauch).state,
id(verbrauch).state
);
return std::vector<uint8_t>(buf, buf + len);