Finally almost finished my project
Re: Finally almost finished my project
Solar Edge: This is placed on the AC OUT 2 of the Quattro since SolarEdge essentially said they didn't have a clue why it wasn't responding to raised frequencies to lower the power when in DG (diesel gen) mode. If there was a power cut and it was on the AC OUT 1 it would continue generating power even if the batteries were full leading to mutually assured destruction of most things as the voltage would push up.
AC Out 2 is dropped in islanding mode. I then have a changeover switch to reconnect all the dropped loads to AC Out 1 allowing me for example to make the most of the sunshine in the winter.
There is apparently a further relay in the Solaredge which allows signalling to shut down completely. I have yet to experiment with that but intend to get that running so that if the Quattro fails to drop the loads if its internal relay is broken then there is at least a backup solution.
AC Out 2 is dropped in islanding mode. I then have a changeover switch to reconnect all the dropped loads to AC Out 1 allowing me for example to make the most of the sunshine in the winter.
There is apparently a further relay in the Solaredge which allows signalling to shut down completely. I have yet to experiment with that but intend to get that running so that if the Quattro fails to drop the loads if its internal relay is broken then there is at least a backup solution.
Re: Finally almost finished my project
Logging to grafana to allow the SMA style view:
This is my node with the logic to do this. It was a PIA to get all the edge cases and I have some pretty screwy looking graphs at the start of testing. It seems pretty stable now. There is the odd glitch but that is due to the raw data that is occasionally messed up with zero's. I'll try and filter those out later.
This is my node with the logic to do this. It was a PIA to get all the edge cases and I have some pretty screwy looking graphs at the start of testing. It seems pretty stable now. There is the odd glitch but that is due to the raw data that is occasionally messed up with zero's. I'll try and filter those out later.
Code: Select all
function log(message) {
if (flow.get("smaFlag")) {
node.warn(message)
}
}
var payload =
{
"solaredgepower": 5004,
"BatteryPower": 6064, //- discharge, positive charge
"GridIn": 20,
"acconsumption": 6310,
"accoupled 1": 5004,
"mpptspower": 7448.75,
"vebus_power": -1286, //inverter -> acout/grid
"dc_system": null
}
payload = msg.payload
payload.mpptspower = payload.mpptspowerL + payload.mpptspowerR
//Info i want
// Consumption = Grid + Battery + Solar
// Solar -> Feed+Battery+Grid
var solar = {
"SolarToGrid": 0,
"SolarToBattery": 0,
"SolarToLoad": 0
}
var consumption = {
"GridToBattery": 0,
"GridToLoad": 0,
"SolarToLoad": 0,
"BatteryToLoad": 0
}
// grid = battery - solar + load
const inverter = {
"inverter": payload.vebus_power,
dischargingSolar: 0,
dischargingBattery: 0,
chargingFromGrid: 0,
chargingFromSolar: 0
}
var pvSE= flow.get("solaredgepower")
var opvSE = flow.get("Osolaredgepower")
if (pvSE>5000 && opvSE)
pvSE = opvSE
if (pvSE==0 && opvSE>100)
pvSE=opvSE
flow.set("solaredgepower",0)
flow.set("Osolaredgepower",pvSE)
const input = {
mpptpv: payload.mpptspowerL + payload.mpptspowerR,
pvSE: pvSE,
pv: pvSE + payload.mpptspower,
battery: payload.BatteryPower,
load: payload.acconsumption,
grid: payload.GridIn,
SOC: payload.SOC,
inverter: inverter.inverter
}
/*
solar = battery-grid+load
solar to battery = solar +grid-load
solar to grid = battery+load-solar
solar to load = solar -battery-load
*/
var output = {
B2L: 0,
B2G: 0,
S2L: 0,
S2G: 0,
S2B: 0,
S2I: 0,
B2I: 0,
G2L: 0,
G2I: 0,
G2B: 0
}
var B2L = 0
var B2G = 0
var S2L = 0
var S2G = 0
var S2B = 0
var S2I = 0
var B2I = 0
var G2L = 0
var G2I = 0
var G2B = 0
var sectionText = ""
log("start sma")
function calculateSMAValues(input) {
const battery = input.battery
const inverter = input.inverter
const grid = input.grid
const load = input.load
const mpptpv = input.mpptpv
const pvSE = input.pvSE
if (inverter < 0) { // discharging
sectionText = "inverter"
B2I = (battery >= 0) ? 0.0 : (-battery)
S2I = (battery >= 0) ? mpptpv - battery : mpptpv // If battery is charging then it is coming from mppt
S2B = (battery > 0) ? battery : 0.0
const ratio = S2I / (B2I + S2I)
S2I = inverter * ratio // If S2I -1000
B2I = inverter - S2I //-1000--1000
log("ratio :" + ratio)
if (ratio > 1 || S2I > 16000) {
var tmp = "ratio>1:" + ratio + "B2I" + B2I + "S2I" + S2I
log(tmp)
global.set("debug", tmp)
}
// log("B2i: "+B2I)
//correct for losses through inverter
// var ratio=-inverter/(S2I+B2I)
// S2I=S2I
// B2I = B2I
// S2B=S2B
// log("grid:"+grid)
if (grid == 0) {
sectionText = sectionText + " da"
log("da")
S2L = pvSE - S2I
B2L = -B2I
} else if (grid > 0) {// grid coming in
sectionText = sectionText + " db"
log("db")
log("pvse:" + pvSE + " s2i:" + S2I + " load:" + load + " grid:" + grid + " B2I:" + B2I)
G2L = grid
S2L = pvSE - S2I
B2L = -B2I //load - grid - S2L //S2I+pvSE
B2L = B2L < 0 ? 0 : B2L
log("S2B:" + S2B + " G2L:" + G2L + " S2L:" + S2L + " b2l:" + B2L)
if (S2B > 0 && G2L > 0) {
if (S2B > G2L) {
sectionText = sectionText + " s2b>g2l"
log("s2b>g2l")
S2B = S2B - G2L
S2L = S2L + G2L
G2B = G2L
G2L = 0
} else { //S2B<G2L
sectionText = sectionText + " s2b<g2l"
log("s2b<g2L")
G2B = S2B
S2L = S2L + S2B
G2L = G2L - S2B
S2B = 0
}
}
} else if (grid < 0) { // grid going out + discharging
log("pvse:" + pvSE + " s2i:" + S2I + " load:" + load + " grid:" + grid + " B2I:" + B2I)
if ((pvSE - S2I) <= load) {
sectionText = sectionText + " dc"
// (load = S2L+B2L)
S2L = pvSE - S2I
B2L = load - S2L
B2G = -grid
}
else if ((pvSE - S2I) > load) {
sectionText = sectionText + " dd"
log("dd")
S2L = load
// S2G=pvSE+S2I-load //Causes fluctiations when sunny
// pvSE + S2I
S2G = pvSE - S2I - load
if (S2G>-grid)
S2G = -grid
B2G = -grid - S2G
log("B2G" + B2G)
}
}
} else if (inverter == 0) {
if (pvSE >= load) {
sectionText = sectionText + " ia"
log("ia")
S2L = load
S2G = grid
} else {
sectionText = sectionText + " ib"
log("ib")
S2L = pvSE
G2L = grid
}
S2B = mpptpv
} else if (inverter > 0) //charging
{
if (pvSE >= load) {
S2L = load
if (grid > 0) { //Incoming grid
sectionText = sectionText + " ca"
log("ca")
G2I = grid
S2I = pvSE - load
} else { //Exporting
sectionText = sectionText + " cb"
log("cb")
S2G = -grid
S2I = -grid - load + pvSE //grid-load+pvSE = inverter-pvSe+load
}
//s2l = 5001
} else if (pvSE < load) {
S2L = pvSE
if (grid >= 0) { //grid coming in.
sectionText = sectionText + " cc"
log("cc")
G2L = load - pvSE
G2I = grid - G2L
}
}
const ratio = inverter / (G2I + S2I)
G2I = G2I * ratio
S2I = S2I * ratio
// charging so whole load will be grid+S2L at this point
var diff = load-S2L // guardanteed to be positive
if (mpptpv+S2I>=diff) {
S2L=load
S2B=mpptpv+S2I-diff
G2L=G2L-diff
G2B=G2I+diff
} else {
S2L=S2L+mpptpv+S2I
S2B=0
G2L=G2L-mpptpv-S2I
G2B=G2I+mpptpv+S2I
}
// Need S2L to be as big as possible. currently G2b is first.
}
output = {
section: sectionText,
B2L: B2L,
B2G: B2G,
S2L: S2L,
S2G: S2G,
S2B: S2B,
S2I: S2I,
B2I: B2I,
G2L: G2L,
G2I: G2I,
G2B: G2B,
load: load
}
return output
}
output = calculateSMAValues(input)
var error = false
var errorText=""
//Check for errors:
if (output.S2L + output.B2L + output.G2L != output.load) {
error = true
errorText = "load inputs greater than load"
}
if ((pvSE+input.mpptpv)> 16500 || output.S2B+output.S2G+output.S2L>16500){
error=true
errorText="Excessive PV production"
}
if (error){
// node.warn("Error")
var errorsArray = flow.get("errors")
const time = new Date(Date.now())
const result = {
error: errorText,
time:time.toLocaleTimeString(),
input: input,
output: output
}
// node.warn(errorsArray)
// node.warn(result)
if (!errorsArray)
errorsArray = [result]
else
errorsArray.push(result)
flow.set("errors", errorsArray)
}
// zeros and nulls zero
function nz(value, name) {
if (value < 0)
log("WARNING negative value for. " + name + " " + value)
value= value < 0 ? 0 : value
if (value == 0)
value=null
return value
}
S2G = nz(output.S2G, "S2G")
S2B = nz(output.S2B, "S2B")
S2B = nz(output.S2B, "S2B")
S2L = nz(output.S2L, "S2L")
G2L = nz(output.G2L, "G2L")
B2L = nz(output.B2L, "B2L")
B2G = nz(output.B2G, "B2G")
G2B = nz(output.G2B, "G2B")
var load = nz(output.load)
var result = {
solar2Grid: S2G,
solar2Battery: S2B,
solar2Load: S2L,
battery2Load: B2L,
"grid2Load": G2L,
grid2Battery: G2B,
battery2Grid: B2G,
SOC : input.SOC,
totalLoad: load
}
log ("sectiontext:"+sectionText)
msg.payload.pvse = pvSE
msg.payload.sectionText = sectionText
msg.powerRouting = result
msg.payload2 = msg.payload // The raw values
msg.payload = [result, {
device: "cerbo"
}];
msg.measurement = "SMAView";
log("end sma")
return msg
Re: Finally almost finished my project
Thanks for sharing all these details Andy, much appreciated!
I'll have a play with my node red logic tomorrow and test out implementing something very similar.
I'll have a play with my node red logic tomorrow and test out implementing something very similar.
10x 405W JA Solar panels (4.05kWp) @ 5 degrees
3x 405W Longi panels (1.22kWp) @ 90 degrees
16.5kWh DIY LifePo4 battery
Solis inverter/charger
0.6kW Ripple WT
64kWh Kia E-Niro
3x 405W Longi panels (1.22kWp) @ 90 degrees
16.5kWh DIY LifePo4 battery
Solis inverter/charger
0.6kW Ripple WT
64kWh Kia E-Niro
Re: Finally almost finished my project
There is obviously a lot more going on elsewhere but hopefully it's enough to get going. The key logic is all there for the charge rate.
Re: Finally almost finished my project
I've implemented my version of it by adapting the SQL query I currently use to spit out a load of data about solar forecast, likely load, estimated SOC, etc.
The required charge power will be returned each time the query is run, then a charge current cap based on required power / battery voltage will be sent to the inverter. I've set it to a min power of 1000w, with charge current being rounded up to the nearest 5A as required.
The good thing is that the charge current will be adapted if the solar forecast goes up or down, or house load changes, even mid-charge. A good hands-off solution!
Tonight will be the first true test, it'll be interesting to see how it performs.
The required charge power will be returned each time the query is run, then a charge current cap based on required power / battery voltage will be sent to the inverter. I've set it to a min power of 1000w, with charge current being rounded up to the nearest 5A as required.
The good thing is that the charge current will be adapted if the solar forecast goes up or down, or house load changes, even mid-charge. A good hands-off solution!
Tonight will be the first true test, it'll be interesting to see how it performs.
10x 405W JA Solar panels (4.05kWp) @ 5 degrees
3x 405W Longi panels (1.22kWp) @ 90 degrees
16.5kWh DIY LifePo4 battery
Solis inverter/charger
0.6kW Ripple WT
64kWh Kia E-Niro
3x 405W Longi panels (1.22kWp) @ 90 degrees
16.5kWh DIY LifePo4 battery
Solis inverter/charger
0.6kW Ripple WT
64kWh Kia E-Niro
Re: Finally almost finished my project
Cool, good luck. I'm still trying to work out my algorithm to not leave me short in the evening. I had to turn the house heating off this evening as my son came back from school and had a big bath which used rather more energy than I anticipated whilst regaining the cylinder heat.MrPablo wrote: ↑Wed Mar 29, 2023 3:06 pm I've implemented my version of it by adapting the SQL query I currently use to spit out a load of data about solar forecast, likely load, estimated SOC, etc.
The required charge power will be returned each time the query is run, then a charge current cap based on required power / battery voltage will be sent to the inverter. I've set it to a min power of 1000w, with charge current being rounded up to the nearest 5A as required.
The good thing is that the charge current will be adapted if the solar forecast goes up or down, or house load changes, even mid-charge. A good hands-off solution!
Tonight will be the first true test, it'll be interesting to see how it performs.
Re: Finally almost finished my project
Victron has what they call a BatteryLife feature which will seek to ensure the battery is charged to 100% every day.Andy wrote: ↑Wed Mar 29, 2023 9:56 pm
Cool, good luck. I'm still trying to work out my algorithm to not leave me short in the evening. I had to turn the house heating off this evening as my son came back from school and had a big bath which used rather more energy than I anticipated whilst regaining the cylinder heat.
It would be simple for them to adapt this so rather than limiting the depth of discharge they pre-charge at off-peak rates to a threshold which depends on the history i.e. if you run out before midnight it charges a bit more in the following off-peak period. That wouldn't even need a weather forecast feed. They talk obliquely of updates to their ESS Assistant but so far there has been no detail. In the mean time I am settling on charging to 50% at night which leaves room for more the following day but is also enough to cover a dull day. Even in light rain the panels generate enough to run the standing loads in the house.
But I wouldn't want to spend my time using NodeRed to code something as complex as @MrPablo has and then find Victron release something similar!
16 x 230W Upsolar panels S Devon, 4kW Steca, 3.9 MWh FITs/yr
8 x 405W Longi panels, 250/60 MPPT, 3.3 MWh/yr
Victron MultiPlus II-GX 48/5000/70-50
10.65 kWh Pylontec Force-L2
zappi 7kW EVCS
Villavent whole-house MVHR
5000l rainwater system
Vaillant 12kW HP
8 x 405W Longi panels, 250/60 MPPT, 3.3 MWh/yr
Victron MultiPlus II-GX 48/5000/70-50
10.65 kWh Pylontec Force-L2
zappi 7kW EVCS
Villavent whole-house MVHR
5000l rainwater system
Vaillant 12kW HP
Re: Finally almost finished my project
I've tried this and unfortunately it just doesn't work for me. Historical weather is not a good indicator of future weather. If you have a few bad days it starts ramping up the lower limit and then you end up with too much in the battery on the next sunny day. Also you can end up charging and discharging the same energy in the cheap periods which costs more. It will also only charge at full charge rate.sharpener wrote: ↑Sun Apr 02, 2023 7:59 pm
Victron has what they call a BatteryLife feature which will seek to ensure the battery is charged to 100% every day.
My system has a two day look ahead and will keep the battery SOC high until the weather is forecast to be good. If so it then it ramps the SOC down through the expensive periods. If it charges fully from the sunshine and expects the same the next day it will also use the stored energy in the cheap periods.
I have actually posted the 'dynamic charge rate' to Victron as an idea to add to the ESS but not had any feedback on it.
- SafetyThird
- Posts: 201
- Joined: Wed Mar 23, 2022 11:32 am
- Location: North Devon
Re: Finally almost finished my project
Andy that's a cracking system you have. Since installing my batteries last year I've been using Home Assistant and Solcast to look at predicted PV generation but have not yet got to figuring out how to get that to work with expected charging needs and overnight cheap rate charging. Currently I just set the battery pack to charge up during the cheap rate hours but that doesn't take account of what the coming day's solar might look like. Haven't had time to work on that but it's something for me to dig into later in the year. I'm sure I'll be asking questions of you at some point
6kw PV (24 x REC Solar AS REC 250PE)
Clausius 5-25kw GSHP
Luxpower Squirrel Pod
Pylontech 21kwh
Eddi Diverter
250l hot water tank with 2 immersions
2 x Woodwarm stoves
7 acres of old coppice woodland
Ripple Kirk Hill 3.8kw
Ripple Derril Water 3.963 kW
Clausius 5-25kw GSHP
Luxpower Squirrel Pod
Pylontech 21kwh
Eddi Diverter
250l hot water tank with 2 immersions
2 x Woodwarm stoves
7 acres of old coppice woodland
Ripple Kirk Hill 3.8kw
Ripple Derril Water 3.963 kW
Re: Finally almost finished my project
It's certainly a tricky one and I am not expecting perfection. The hardest period is in the shoulder months when a lot of sunshine is forecast first thing and then it doesn't materialise. The winter is pretty much charge to max and forget. Likewise in the summer the amount of power generated and lack of heating requirement mean that a failure in predictions won't be too costly. I've been looking ahead to see the maximum forecast battery in the day as well and then keeping some of that difference in the morning. Then if it all goes totally wrong I can turn off the heating until the next cheap rate . At least during a working week I can as I would get too many complaints at the weekend.SafetyThird wrote: ↑Wed Apr 05, 2023 10:47 am Haven't had time to work on that but it's something for me to dig into later in the year. I'm sure I'll be asking questions of you at some point