question

zed-must avatar image
zed-must asked

Schedule grid feed back using Node red

Example below of how to achieve this:

what i want it to do is control whether to export to grid or charge the batteries based on time of day and battery SOC - and i want to be able to modify these 2 parameters easily

e.g.

If SOC > 50% and time is after 7pm send PV to grid.

Node-RED
2 |3000

Up to 8 attachments (including images) can be used with a maximum of 190.8 MiB each and 286.6 MiB total.

18 Answers
matt1309 avatar image
matt1309 answered ·

Hi @zed must

Not sure I'll have time but thought I'd ask a few questions (that I imagine anyone willing to help might want to ask anyway which might give people the extra info they need to offer their services).

1. What is your plan for how to adjust the parameters? Do you need/want a dashboard created in node red to adjust these, or do you have another system ie homeassistant/openhab that you'd want to utilise to update the SoC, time and i assume charge/discharge.

2. How is the system currently configured, is the solar connected via charge controller (DC coupled) or via Grid tied inverter (AC coupled) or do you have both. (If it's just AC coupled there's a faster way to code it up I believe)

3. When you say export to grid which of the below do you mean (or do you mean a mix in which case another few parameters might be needed to select which one).

  • Export any excess solar only
  • Export all solar (so loads will be powered from battery)
  • Export a fixed number of amps/watts so solar + battery
  • Export so that grid setpoint = a specific negative number


4. In terms of turning off this feature. (less applicable if your answer to 3 is excess solar only)

ie feature turned on if SoC > 50% after 7pm.

do you want off to occur only when SoC < 50% or at 5am or after x hours.



2 comments
2 |3000

Up to 8 attachments (including images) can be used with a maximum of 190.8 MiB each and 286.6 MiB total.

zed-must avatar image zed-must commented ·

HI Matt, thanks for your interest.

1. Ideally we would be able to adjust the parameter in node red. Not sure i need a dashboard - i can just about get my way around node red so im sure if you tell me which nodes to edit i can do that myself.

2.We have both DC coupled solar and AC coupled wind through a Quattro. See screenshot below.screenshot-2024-04-12-at-120504.png

3. we would want to:

  • Export all solar (so loads will be powered from battery)

During certain hours when battery SOC meets a set value.

4. ideally we would be able to have multiple conditions/time slots, but not the end of the world if there is only a couple. So that the feature would turn on and off automatically when conditions are not met, ie

from 8am-10am, if SOC>90%, send all generated power to grid (so that when SOC drops <90%, power goes back to batteries

from 10am-2pm, if SOC>70%, send all generated power to grid

from 2pm-7pm, if SOC>50%, send all generated power to grid

from 7pm-11pm, if SOC>30%, send all generated power to grid

Where i can change the hours and the SOC for each rule. But they should all work the same i.e. on when conditions are met (i.e. send to grid) and off when conditions are not med (i.e. charge the batteries).

And one final complication - we aren't allowed to export more than 6kw, which is currently set up as a limit in the ESS settings. However if using a node red setup instead, we would need to make sure that if we are in 'send all generated power to grid mode', that any power generated over 6kw doesnt get wasted and instead goes to the batteries i.e. 6kw to grid, balance to batteries.

Hope all that makes sense and isn't too difficult to set up.

Thx


0 Likes 0 ·
matt1309 avatar image matt1309 zed-must commented ·

Making a fully polished system with dashboards (ie an interface where you just fill in a text box to adjust parameters) is time consuming. If however you just want this:


from 10am-2pm, if SOC>70%, send all generated power to grid

from 2pm-7pm, if SOC>50%, send all generated power to grid

from 7pm-11pm, if SOC>30%, send all generated power to grid


Written in code. I can send you that and you can implement it for free.

In terms of the 6kw limit, id leave that in ESS, feels safer there in native very well tested code.

0 Likes 0 ·
matt1309 avatar image
matt1309 answered ·

Here's some code:


I know the below is instructions rather than an easy install but it's free....


EDIT: You might have to change this is you ever want to manually make grid setpoint greater than 0. As this code will override it back to 0.


You need to do a few things first:

1. Get the data from victron nodes:

So you need to find the node that outputs the AC Solar data (PV Inverter node, then select measurement of power) as well as the DC solar data (likely Solar charger node and select measurement of PV Power). If you've got multiple MPPTs let me know i can edit the code to have multiple dcSolar variables.

And finally you do the same with State of charge data (likely from battery monitor node selecting measurement state of charge %).



2. These data nodes need to be passed into their own change nodes so you can set the topic of the message (this tells the function what type of info it's received.


It'll look like this:

1712949505205.png

With the inside of the change node looking like this:

1712949521569.png


You repeat this process for the DC solar but change the topic name in the change node to dcSolar and soc for battery monitor


You string them together like this:


1712949539491.png


Function 2:

//setting up time variables
var currentTime = new Date();
var hours = currentTime.getHours();
var minutes = currentTime.getMinutes();

//setting up data variables. If using up to date venus os you could get these directly via global context. 
var soc = global.get('soc');
var acSolar = global.get('acSolar');
var dcSolar = global.get('dcSolar');
var gridSetpoint = global.get('gridSetpoint');

//setting soc variable depending on inputs from flows
if (msg.topic === "soc") {
  soc = msg.payload; // Extract payload from input 1
  global.set('soc', soc);
}

if (msg.topic === "acSolar") {
  acSolar = msg.payload; // Extract payload from input 2
  global.set('acSolar', acSolar);
}

if (msg.topic === "dcSolar") {
  dcSolar = msg.payload; // Extract payload from input 3
  global.set('dcSolar', dcSolar);
}

//this is used to check if gridsetpoint is already 0, if it is then we won't bother updating it.
if (msg.topic === "gridSetpoint") {
  gridSetpoint = msg.payload;
  global.set('gridSetpoint', gridSetpoint);
}

if (msg.topic === "acLoad") {

  acLoad = msg.payload; // Extract payload from input 2

  global.set('acLoad ', acLoad);

}

//two examples of times
if (hours >= 10 && hours < 14) { //if time is between 10am-1.59pm
  if (soc > 70 && acLoads < 10000) { //if state of charge is above 70%
    //return the new grid setpoint
    gridSetpoint = -Math.min(acSolar + dcSolar, 6000);
  } else {
    gridSetpoint = 0;
  }
} else if (hours >= 14 && hours < 19) { //if time is between 2pm and 6.59pm
  if (soc > 50 && acLoads < 10000) { //if state of charge is above 50%
    //return the new grid setpoint
    gridSetpoint = -Math.min(acSolar + dcSolar, 6000);
  } else {
    gridSetpoint = 0;
  }
} else if (hours >= 19 && hours < 23) { //if time is between 7pm and 10.59pm
  if (soc > 30 && acLoads < 10000) { //if state of charge is above 30%
    //return the new grid setpoint
    gridSetpoint = -Math.min(acSolar + dcSolar, 6000);
  } else {
    gridSetpoint = 0;
  }
} else {
  gridSetpoint = 0;
}

if (gridSetpoint !== global.get('gridSetpoint')) { // Check if gridSetpoint is going to change
  global.set('gridSetpoint', Math.min(gridSetpoint, 0));
  msg.payload = Math.min(gridSetpoint, 0);
  return msg;
}




At this stage the function will output a message of what to set the gridsetpoint to. However before you pass this function into the ESS gridsetpoint node (to set the grid setpoint). You should test the code works as expected on your system/there aren't any bugs in it. To do this you should pass function into a debug node like below. And watch the outputs at various times of the day. Once you're happy that the function is outputing the correct negative number at all points in the day then you can pass the function into the ESS grid setpoint node.


I've hopefully written the code so you can see what hours you need to change and SoC to get the desired times.

1712949401118.png


As i said the debug would be replaced with grid set point node once you've confirmed function 2 is outputing what you expect.


1712949401118.png (41.0 KiB)
1712949505205.png (8.9 KiB)
1712949521569.png (14.3 KiB)
1712949539491.png (36.5 KiB)
20 comments
2 |3000

Up to 8 attachments (including images) can be used with a maximum of 190.8 MiB each and 286.6 MiB total.

zed-must avatar image zed-must commented ·

wow, thanks so much Matt! i appreciate you doing this for free but still happy to pay you something, or if you want to nominate a charity i would be happy to make a donation on your behalf. thx again

Update: so ive got wind, pv and soc all coming through, but when you said the system would give a msg - im not sure if i have done something wrong? I put all your code into the OnStart tab of node 2 is that correct?


screenshot-2024-04-13-at-095730.png


0 Likes 0 ·
matt1309 avatar image matt1309 zed-must commented ·

Hi @zed must


Use on message not on start in function node

Msg is the message received. So you need a message to be received for msg to be defined


Let me know if you need any tweaks. But if you do as I suggest and just check it's working using debug node there's literally no harm in you playing/editing the function code to get it more features/tweaks yourself. Hopefully the layout is such that you can somewhat guess how to add extra bits if needed.


You can also go way more in depth and make graphical interfaces (dashboards) so you could make it so the time schedules and soc limits are just text boxes on a dashboard for easier input. But the above function is hopefully a good jumping off point.


This code might have issues if your solar goes offline before issuing a 0w output. the code will think it's still outputting it's last provided value.


If you have that happen regularly let me know and I can write in a timeout ie if acSolar hasnt provided a value in x seconds then assume it's off and set to 0

1 Like 1 ·
zed-must avatar image zed-must matt1309 commented ·

hi thx again.

Working now, but when you said 'the function will output a message of what to set the gridsetpoint to', i dont see this - i only see the values for L1, PV and SOC in the debug, nothing for gridsetpoint.

screenshot-2024-04-13-at-141546.png

One tweak which would be great (which i forgot about) is to measure AC loads, and when AC loads is above a certain value e.g. 10000W then skip the whole flow, and just let the ESS manage itself normally. This is because we have one item with a very large load that is only used rarely, but when it is used we wouldnt want to send power to the grid but instead let it go to batteries.

Finally when it comes to deploying this live, im struggling to find a victron node that has grid setpoint as a measurement which i can select.

0 Likes 0 ·
matt1309 avatar image matt1309 zed-must commented ·

Hi @zed must


I suspect this is because you've added a return msg; at the top of the function, remove that and you should be good.


In regards to the 10000w scenario how long do you want the code to be disabled for. For the full time period or only when load is above 10kw?


If it's only when the load is above 10kw then you just need to repeat the steps for getting acSolar, dcSolar and soc data into the function but instead use the System node and select measurement AC Consumption and set topic in change node to something like acLoad and again pass that into function like so:


1713052949103.png


1713052966947.png



Then add this to function to near top of function:

if (msg.topic === "acLoad") {

  acLoad = msg.payload; // Extract payload from input 2

  global.set('acLoad ', acLoad);

}



And edit each of the soc if statements from this:

 if (soc > 70)

to this:

 if (soc > 70 && acLoads < 10000)


In regards to the grid setpoint node it's in ESS control node if you select Venus Settings under ESS drop down, and measurement type set to Grid set-point (W)



I've edited my original answer so you can see the full function with the 10kw change implemented in case it's easier to copy paste the full thing rather than following additions of adding the code like i describe above. Shout if it's not what you wanted.



1 Like 1 ·
1713052949103.png (27.8 KiB)
zed-must avatar image zed-must matt1309 commented ·

absolutely super, thanks!

small issue - the grid set point node only has an output connector, not any for input - so how can i pass function 2 into it?


screenshot-2024-04-14-at-070724.png


0 Likes 0 ·
matt1309 avatar image matt1309 zed-must commented ·
There's an input node and an output node. Ess control is what in one that accepts inputs is called (at least on my version of node red)
0 Likes 0 ·
zed-must avatar image zed-must matt1309 commented ·
thx Matt, running now, so far seems to be working perfectly ) I think others would benefit from this, might be worth you posting somewhere. I have changed the post title to make it easier for others to find.
0 Likes 0 ·
matt1309 avatar image matt1309 zed-must commented ·

Hi @zed must


Glad it's working for you. Have to shout if there's any other useful features. Happy to tweak further if need.


One thing to flag is this code will have issues if any of the devices go offline and don't issue a 0 value first. ie say wind data stops being received before 0w is sent. The code won't know this has happened and still think wind is at the last value it sent. I can maybe write in a timer to reset to 0 if this is needed.

0 Likes 0 ·
zed-must avatar image zed-must matt1309 commented ·

Thanks Matt,


Once i saw it working i realised i didnt need to tell you to export only 'excess' wind and solar. All i needed was to export during the set times whenever the battery was above the set value. So i have now removed the 'math.min ac+dc' part of the code temporarily and just set the gridsetpoint = -6000 and i will see how that works. So i dont think what you say above will matter - if there is no wind or no solar or a fault there it shouldnt matter - in that scenario the batteries will drop to the low limit quicker and stop feeding back, which is exactly what i need. So all good! Thanks so much again for your help!!!

1 Like 1 ·
matt1309 avatar image matt1309 zed-must commented ·
No worries, yeh hopefully it's set out clear enough that tweaking/adding/removing bits is guessable but if not just shout.
1 Like 1 ·
Dirk-Jan Faber (Victron Energy) avatar image Dirk-Jan Faber (Victron Energy) ♦♦ matt1309 commented ·

FYI there is no need to explicitly store the retrieved data in the global context. You can just check the box in the configuration.

1713365046943.png

0 Likes 0 ·
1713365046943.png (59.7 KiB)
matt1309 avatar image matt1309 Dirk-Jan Faber (Victron Energy) ♦♦ commented ·

Thanks @Dirk-Jan Faber (Victron Energy)


I did think about using this but wasn't sure what version of venus os @zed must was using so thought writing the above was safer as would work on older versions.


I did have a question regarding the Global context. What happens in the event the data providing node goes offline? Is there a timeout implemented where global context vars go to zero of null if data isn't provided after a certain timeframe?


That was the other reason i didnt concider going the global context route, as the above code i provided could be edited to implement a timeout feature. ie a map to record when each piece of data was last received which is checked every call to see if it's been longer than 6seconds (or whatever default data send is). Is that the best way to go about solving the situation where a device goes offline before issuing 0.


Or should I be using another method to protect against that. ie if PV Inverter stops providing data. Should i build in timeout checker or is there another way to monitor node health and maybe use that instead?

0 Likes 0 ·
dougle03 avatar image dougle03 matt1309 commented ·

Generally Global context values don't auto reset, and if context store is set to on, they will also survive a restart of the NR service. So yeah, I'd be adding in a timeout on these to ensure 0 if no message received for a sensible period of time.

0 Likes 0 ·
matt1309 avatar image matt1309 dougle03 commented ·
Thank you for this! I'll stick to my method then. At least that allows me the option to build in "last updated" tracker/map.
1 Like 1 ·
dougle03 avatar image dougle03 matt1309 commented ·
You can go a step further, if the time out has triggered issue a reset/restart of whatever device has stopped sending in values perhaps? I use this a lot as a crude watchdog, but I also add in counters so if a device has been restarted x times, send a notification, then reset counter when device is restored to operation..

My NodeRed journey is only just starting for Victron control, but I've been using it for years in home and business automation.

0 Likes 0 ·
zed-must avatar image zed-must commented ·

Hi Matt,

The code has been working great but recently i have noticed a few times that even though conditions have been met, and gridsetpoint is -6000, the system is not selling back to the grid but is charging the batteries instead. Like right now. Any idea why this would happen? I am on 'optimized without battery life' so that is not the reason.


screenshot-2024-06-12-at-141035.png


And in trying to fix it i have managed to break it. I went back to your original code but it's no longer changing the grid setpoint like it used to. Dont know where i have gone wrong but i suspect a recent victron update has changed something ( All i am trying to do now is set the gridpoint to -6000 at certain times of the day depending on SOC - without any of the inputs from solar) it just doesnt work any more



0 Likes 0 ·
matt1309 avatar image matt1309 zed-must commented ·
Hi @zed must


That is odd. I would've thought with grid setpoint set to -6000 it would discharge the battery.

I was going to say sometimes the data nodes have issues after updates where they need to be re-added but that would explain why gridsetpoint isn't be respected.


0 Likes 0 ·
zed-must avatar image zed-must matt1309 commented ·

yes it is odd. but now it's not even setting the grid setpoint correctly. I manually changed it to -6000 in ess settings on remote console but it doesn't seem to make a difference.


can you see anything in the code im using that's not right?


//setting up time variables

var currentTime = new Date();

var hours = currentTime.getHours();

var minutes = currentTime.getMinutes();

//setting up data variables. If using up to date venus os you could get these directly via global context.

var soc = global.get('soc');

var gridSetpoint = global.get('gridSetpoint');

//setting soc variable depending on inputs from flows

if (msg.topic === "soc") {

soc = msg.payload; // Extract payload from input 1

global.set('soc', soc);

}

//this is used to check if gridsetpoint is already 0, if it is then we won't bother updating it.

if (msg.topic === "gridSetpoint") {

gridSetpoint = msg.payload;

global.set('gridSetpoint', gridSetpoint);

}

//times

if (hours >= 10 && hours < 14) { //if time is between

if (soc > 70) { //if state of charge is above 70%

//return the new grid setpoint

gridSetpoint = -6000;

} else {

gridSetpoint = 0;

}

} else if (hours >= 14 && hours < 19) { //if time is between

if (soc > 50) { //if state of charge is above 50%

//return the new grid setpoint

gridSetpoint = -6000;

} else {

gridSetpoint = 0;

}

} else if (hours >= 19 && hours < 23) { //if time is between

if (soc > 30) { //if state of charge is above 30%

//return the new grid setpoint

gridSetpoint = -6000;

} else {

gridSetpoint = 0;

}

}

if (gridSetpoint !== global.get('gridSetpoint')) { // Check if gridSetpoint is going to change

global.set('gridSetpoint', Math.min(gridSetpoint, 0));

msg.payload = Math.min(gridSetpoint, 0);

return msg;

}

0 Likes 0 ·
matt1309 avatar image matt1309 zed-must commented ·
Hi @zed must


Hmmm. What i would do is remove the function from node red (just delete the lines in the flow for now).
Change the grid setpoint manually via remote console. Just to check it's working as it should normally.

Then once you've confirmed that it works normally ie node red is the issue. I would add in some debug nodes to see what's going on, as well as monitor what the variables are set to in global context. This will let you see which part is breaking. I cant spot an issue with the code.


The only think i can think is maybe one of the data nodes like soc is null. And therefore the gridsetpoint is getting constantly override to set to 0?


But checking what soc is defined as in global context will give you the answer to that.

0 Likes 0 ·
zed-must avatar image zed-must matt1309 commented ·
Thx Matt, node red just stopped working. i disabled it and turned it back on again and everything is back working again now. i had thought i had broke the code but it wasnt that at all it was just an issue probably from a firmware update on the cerbo. thanks again!
1 Like 1 ·
markus-001 avatar image
markus-001 answered ·

@zed must

Clarity needed, Your example of

If SOC > 50% and time is after 7pm send PV to grid.

Do you have PV after 7pm?

1) Are you only feeding in PV or are you wanting to feed-in stored battery energy during peak periods where the feed-in tariff is at its highest then recharge the batteries during off peak at the lowest tariff.

2) do you want to limit the maximum feed-in power.

Settting the minimum SOC through the node red dashboard is easy and you can set as many set points as you wish, I change the minimum SOC every hour to crate a slow discharge through the night to make sure I don’t deplete the battery in the first 3 hours. I then bring up the minimum SOC hourly during the day which usually lags the actual SOC on sunny days how ever on over cast days the internal battery charger initializes to charge from the grid if there is insufficient Solar on overcast mornings, after mid day when the sun is up high the actual SOC overtakes the hourly minimum SOC again, so no charging from the grid takes place.

Setting PV Feed-in at different times as well as the feed-in amount in watts is also quite easy, Discharging the batteries into the grid is also easy to do by just setting a negative grid set point which will deactivate when the minimum SOC is reached.

The problem comes in when you have to limit the maximum feed-in power from the batteries. It can be done, I have done it but the amount of writes to the GX flash memory is quite a lot, every 2 or 3 seconds you need to write a new grid set point taking into account your current load on the AC-out of the inverter, AC-in to calculate internal loses as well as include any grid loads before the inverter if you have an external energy meter who’s roll is set as grid meter



7 comments
2 |3000

Up to 8 attachments (including images) can be used with a maximum of 190.8 MiB each and 286.6 MiB total.

zed-must avatar image zed-must commented ·
Thanks Markus but i think i have got a solution now from Matt above
0 Likes 0 ·
dougle03 avatar image dougle03 commented ·
What is the flash type in the GX? does it have a limited write life that's low enough to worry about writes every second or so? Can anything be done to help this?

I run NR on a seperate machine based on an SSD so not worried about write cycles, but I am concerned about the GX's storage longevity...

0 Likes 0 ·
matt1309 avatar image matt1309 dougle03 commented ·

Hi @dougle03 @zed must

I did see someone investigating/asking, but no official answer yet by the looks of it:

https://community.victronenergy.com/questions/237446/cerbogx-flash-lifetime-when-writing-setting-each-m.html


I'm honestly not sure how persistence of data is handled. Like i know dbus runs in ram. However does every change to dbus of settings get written to file? That I'm unsure of. I think it would have to write to flash everytime, i cant think of a way around it, trying to write to non-volatile memory on shutdown wouldnt be reliable enough.

I'll have a dig on their github page but not sure.


I run on a pi so never really thought about it.


EDIT:


I did some digging it does write to flash every time settings are changed. Got me thinking is editing gridsetpoint/max discharge or anything that appears in com.victronenergy.settings a bad idea.


I did look around in dbus and think maybe editting hub4 directly might be away around it but it looks like hub4 settings are also stored in the settings.xml


I'm not sure of a work around to minimize flash implications via node red. I would instead maybe look to edit the system calculations driver (or whatever driver handles the ESS/Hub4 inverter output changing to balance grid to gridsetpoint) to have an additional if statement that gets a new variable gridsetpoint2DBUS in my example but same principle for any non-volatile setting

This new variable would be the same as gridsetpoint but would be stored in a new custom dbus service that is purely volatile/doesnt write to settings.xml

Then you'd tweak the system driver to factor in the new variable the exact same way native grid setpoint is handled.


Way more involved and harder to manage over updates (so im not going to try) but this would be my guess about how to do it if folk really wanted to.

(Happy to be told im wrong/there's a better way. please tell me if there is as I'd be very interested)

something like

if gridSetpoint2DBUS is not None: //or 0 maybe if you set dbus default to 0

    //do same calc system would normally do but tweak for gridSetpoint2DBUS value




1 Like 1 ·
dougle03 avatar image dougle03 matt1309 commented ·
Probably needs more investigation for sure... Equally, the impact of those writes will be influenced on a number of factors like; Flash type, controller type and if any wear levelling is in action...
0 Likes 0 ·
matt1309 avatar image matt1309 dougle03 commented ·
Edited my comment above. I think only way is making custom dbus entries that are purely volatile (dont write to settings.xml) and adding to the sytstemcalc drivers (or wherever the system calculations are done) to use these new volatile variables from dbus if they exist/aren't zero.
1 Like 1 ·
dougle03 avatar image dougle03 matt1309 commented ·
Hmmm sounds like it's adding complication for sure. I'm almost tempted to ditch the Cerbo in faviour of a RPi with an SSD, although this does not solve the MP II's internal flash writes issue.. (If that's also a potential problem)
0 Likes 0 ·
matt1309 avatar image matt1309 dougle03 commented ·
Yeh I don't know if that code is open source/where to find it to check ie does that store setting on inverter or read directly from dbus on gx.


My guess would be the settings like grid setpoint wouldn't be written to flash on inverter as you need gx device to run ess. If it was stored on inverter flash then surely they wouldn't need additional gx device.

So I reckon it's only the gx's flash that's impact and the inverter would read live

0 Likes 0 ·
avukat38 avatar image
avukat38 answered ·

Hello, I need the node-red flow you use, can you share the file with me?

1 comment
2 |3000

Up to 8 attachments (including images) can be used with a maximum of 190.8 MiB each and 286.6 MiB total.

matt1309 avatar image matt1309 commented ·
Hi @avukat38


I think my second answer above would work for you (as Dirk mentions you can get data from global context if you prefer/and are using most up to date venus os).


0 Likes 0 ·
avukat38 avatar image
avukat38 answered ·

I wonder if I could do it?ekran-resmi-2024-05-13-124959.png


2 |3000

Up to 8 attachments (including images) can be used with a maximum of 190.8 MiB each and 286.6 MiB total.

avukat38 avatar image
avukat38 answered ·

Could you please update the codes for three phase pv inverter and two MPPT

2 |3000

Up to 8 attachments (including images) can be used with a maximum of 190.8 MiB each and 286.6 MiB total.

avukat38 avatar image
avukat38 answered ·

ekran-resmi-2024-05-13-155604.png

I think it's okay this time


1 comment
2 |3000

Up to 8 attachments (including images) can be used with a maximum of 190.8 MiB each and 286.6 MiB total.

matt1309 avatar image matt1309 commented ·

Hi @avukat38


So first thing to flag is you've got 2 smart solars going into one dcSolar change node.

If you do this then the function will use either smartsolar1 or smartsolar2 (it wont add them together like i imagine you're hoping it will).

You'll need to add another change node. ie change topic to dcSolar1 thats for smartSolar1 and then similarly dcSolar2 for smartsolar2.


Then edit the function to do something like:

if (msg.topic === "dcSolar1") {
  dcSolar1 = msg.payload; // Extract payload from input 3
  global.set('dcSolar1', dcSolar1);
}
if (msg.topic === "dcSolar2") {
  dcSolar2 = msg.payload; // Extract payload from input 3
  global.set('dcSolar2', dcSolar2);
}
var dcSolar = global.get("dcSolar1") + global.get("dcSolar2");


The other question I have is how do you want the three phase setup to work?

Do you need per phase control? I'm not as familiar with 3 phase installs so i'll need to read up on whether you can control per phase grid setpoints. ie do you use "Total of all phases" or "Individual phase" in ESS settings.

0 Likes 0 ·
avukat38 avatar image
avukat38 answered ·

I use the sum of all phases

1 comment
2 |3000

Up to 8 attachments (including images) can be used with a maximum of 190.8 MiB each and 286.6 MiB total.

matt1309 avatar image matt1309 commented ·
Ahh ok it should work as is then. Just need to split out your multiple data sources into their own variables like I showed with dcSolar1 dcSoalr2


0 Likes 0 ·
avukat38 avatar image
avukat38 answered ·

Do I need to do something about AC load?

1 comment
2 |3000

Up to 8 attachments (including images) can be used with a maximum of 190.8 MiB each and 286.6 MiB total.

matt1309 avatar image matt1309 commented ·

The original poster said they needed a criteria where if their acLoads were greater than 10kw then they didnt want the gridsetpoint to be changed.


If you dont need that criteria you can remove:

if (msg.topic === "acLoad") {
 
  acLoad = msg.payload; // Extract payload from input 2
 
  global.set('acLoad ', acLoad);
 
}


and remove any references of acLoads from any of the if statements.


If however you do want that criteria you'll need to pass acLoads data into the function (the same way you have with soc and dcLoads etc)

0 Likes 0 ·
avukat38 avatar image
avukat38 answered ·

Thank you very much

2 |3000

Up to 8 attachments (including images) can be used with a maximum of 190.8 MiB each and 286.6 MiB total.

avukat38 avatar image
avukat38 answered ·

Bu hatanın ne olduğunu merak ediyorum?ekran-resmi-2024-05-14-171609.png


1 comment
2 |3000

Up to 8 attachments (including images) can be used with a maximum of 190.8 MiB each and 286.6 MiB total.

matt1309 avatar image matt1309 commented ·

You've correctly remove the acLoad variable definition but you've not removed acLoads from the if statements. The error code is letting you know the error is on line 44, in the 19th position which is where acLoads is used in the code even though you've removed the definition of the variable.


ie You've got:

if(soc >70 && acLoads <10000){
.....

If you're not using acLoads you need to remove all references to it ie change this to:

if(soc > 70) {
....


You have to do that everywhere there's reference to acLoads.

0 Likes 0 ·
avukat38 avatar image
avukat38 answered ·

Thank you very much, thanks to you I learned something about node-red. https://community.victronenergy.com/questions/278498/how-can-i-program-dess.html?childToView=279392#comment-279392 I tried to give a flow to the question I asked here. Is it true?

ekran-resmi-2024-05-15-112333.pngekran-resmi-2024-05-15-112528.png


2 comments
2 |3000

Up to 8 attachments (including images) can be used with a maximum of 190.8 MiB each and 286.6 MiB total.

matt1309 avatar image matt1309 commented ·
HI @avukat38


Do you want to try post the whole code on here. I can tweak it for you and explain what I change.


The bits that stand out are lines 10, 14 and 18

You've updated the code to get the acLoad2 but you've forgotten to change lines 10, 14 and 18 to change the variable from dcSolar1 to be acLoad1 or acLoad2 or acLoad3 depending on which case you're in.

0 Likes 0 ·
avukat38 avatar image avukat38 matt1309 commented ·

oh my stupid head

0 Likes 0 ·
avukat38 avatar image
avukat38 answered ·

//if you're using old venus os you need the below. if not you can just read these varibales from the global context

var acLoad = global.get("acLoad1") + global.get("acLoad2") + global.get("acLoad3");

var acSolar = global.get('acSolar');

var dcSolar = global.get("dcSolar1") + global.get("dcSolar2");

var gridSetpoint = global.get('gridSetpoint');

var exported= global.get('exported');

//setting soc variable depending on inputs from flows

if (msg.topic === "acLoad1") {

acload1 = msg.payload; // Extract payload from input 1

global.set('acLoad1', acLoad1);

}

if (msg.topic === "acLoad2") {

acLoad2 = msg.payload; // Extract payload from input 1

global.set('acLoad2', acLoad2);

}

if (msg.topic === "acLoad3") {

acLoad3 = msg.payload; // Extract payload from input 1

global.set('acLoad3', acLoad3);

}

//setting soc variable depending on inputs from flows

if (msg.topic === "exported") {

exported = msg.payload; // Extract payload from input 1

global.set('exported', exported);

}

if (msg.topic === "acSolar") {

acSolar = msg.payload; // Extract payload from input 2

global.set('acSolar', acSolar);

}

if (msg.topic === "dcSolar1") {

dcSolar1 = msg.payload; // Extract payload from input 3

global.set('dcSolar1', dcSolar1);

}

if (msg.topic === "dcSolar2") {

dcSolar2 = msg.payload; // Extract payload from input 3

global.set('dcSolar2', dcSolar2);

}

//this is used to check if gridsetpoint is already 0, if it is then we won't bother updating it.

if (msg.topic === "gridSetpoint") {

gridSetpoint = msg.payload;

global.set('gridSetpoint', gridSetpoint);

}

if (msg.topic === "acLoad") {

acLoad = msg.payload; // Extract payload from input 2

global.set('acLoad ', acLoad);

}

//The main logic.

if (exported < 10000) {

gridSetpoint = acLoads - acSolar + dcSolar;

} else {

gridSetpoint = 0;

}

if (gridSetpoint !== global.get('gridSetpoint')) { // Check if gridSetpoint is going to change

global.set('gridSetpoint', Math.min(gridSetpoint, 0));

msg.payload = Math.min(gridSetpoint, 0);

return msg;

}

1 comment
2 |3000

Up to 8 attachments (including images) can be used with a maximum of 190.8 MiB each and 286.6 MiB total.

matt1309 avatar image matt1309 commented ·

I spotted one typo acloads1 should be acLoads1, the rest looks ok to me. Unless you're getting more errors?

The other thing I'd mention is you've not setup exported in this function, it's defined but it wont have a value written to it (unless you're doing that in another function).


You'll either need to it to this function somehow or you can use another flow to get the data in because you're using global.

Because you're using global you can use a completely different flow to get the data into global just need to set it like

global.set('exported',msg.payload);


This is assuming you use a new flow and the only message being passed in is the total exported figure.


var acLoad = global.get("acLoad1") + global.get("acLoad2") + global.get("acLoad3");

var acSolar = global.get('acSolar');

var dcSolar = global.get("dcSolar1") + global.get("dcSolar2");

var gridSetpoint = global.get('gridSetpoint');

var exported= global.get('exported');

//setting soc variable depending on inputs from flows

if (msg.topic === "acLoad1") {
acLoad1 = msg.payload; // Extract payload from input 1

global.set('acLoad1', acLoad1);

}

if (msg.topic === "acLoad2") {
acLoad2 = msg.payload; // Extract payload from input 1

global.set('acLoad2', acLoad2);

}

if (msg.topic === "acLoad3") {
acLoad3 = msg.payload; // Extract payload from input 1

global.set('acLoad3', acLoad3);

}

//setting soc variable depending on inputs from flows

if (msg.topic === "exported") {
exported = msg.payload; // Extract payload from input 1

global.set('exported', exported);

}

if (msg.topic === "acSolar") {
acSolar = msg.payload; // Extract payload from input 2

global.set('acSolar', acSolar);

}

if (msg.topic === "dcSolar1") {
dcSolar1 = msg.payload; // Extract payload from input 3

global.set('dcSolar1', dcSolar1);

}

if (msg.topic === "dcSolar2") {
dcSolar2 = msg.payload; // Extract payload from input 3

global.set('dcSolar2', dcSolar2);

}

//this is used to check if gridsetpoint is already 0, if it is then we won't bother updating it.

if (msg.topic === "gridSetpoint") {
gridSetpoint = msg.payload;

global.set('gridSetpoint', gridSetpoint);

}

if (msg.topic === "acLoad") {
acLoad = msg.payload; // Extract payload from input 2

global.set('acLoad ', acLoad);

}

//The main logic.

if (exported < 10000) {

gridSetpoint = acLoads - acSolar + dcSolar;

} else {
gridSetpoint = 0;

}

if (gridSetpoint !== global.get('gridSetpoint')) { // Check if gridSetpoint is going to change

global.set('gridSetpoint', Math.min(gridSetpoint, 0));

msg.payload = Math.min(gridSetpoint, 0);

return msg;

}
0 Likes 0 ·
avukat38 avatar image
avukat38 answered ·

debug1 no longer gives any outputekran-resmi-2024-05-15-123631.png


1 comment
2 |3000

Up to 8 attachments (including images) can be used with a maximum of 190.8 MiB each and 286.6 MiB total.

matt1309 avatar image matt1309 commented ·

This is likely because exported is undefined....


so the final if statement:

if(exported < 10000)
is the same as saying
if(undefined < 10000) and undefined is not less than 10000 in javascript.


So as soon as you defined exported to be a number it should work. If not you'll have to add in some debugging in the function to see what's going on in more detail but my guess is exported being undefined is the issue


To check if i'm right or not you could add this to the bottom of the funciton:


else{
msg.payload = 'hello';
return msg;
}

if debug1 then outputs hello then that's proved it's because exported is undefined.



0 Likes 0 ·
avukat38 avatar image
avukat38 answered ·

I never understood thisekran-resmi-2024-05-15-134234.png


1 comment
2 |3000

Up to 8 attachments (including images) can be used with a maximum of 190.8 MiB each and 286.6 MiB total.

matt1309 avatar image matt1309 commented ·

So you're trying to get your code to adjust grid setpoint until it's exported more than 10,000


So you're written the if statement to say if exported <10000) then adjust gridsetpoint.


However the variable exported you havent written a value to. so export = undefined.

So the code is trying to do:

if (undefined<10000)


which is returning the result false and setting gridsetpoint to 0. If gridsetpoint is 0 and hasnt changed the function doesnt output anything.


What you need to do is write a value to exported. Which is what is missing from your flows at the moment.


You need to find the data node for exported power and write it to the global variable exported. (the same as what you've done for soc, dcSolar acLoads etc but you now need to do it for exported.


Then your code will run at the moment grid setpoint is just being set to 0 and never changing.

0 Likes 0 ·
avukat38 avatar image
avukat38 answered ·

Now it seems like the debug data is wrongekran-resmi-2024-05-15-143951.png


1 comment
2 |3000

Up to 8 attachments (including images) can be used with a maximum of 190.8 MiB each and 286.6 MiB total.

matt1309 avatar image matt1309 commented ·

I'm not sure why it's outputting those messages. You're not returning the msg unless


Im not sure that EM24 is the right metric for exported but dont think that would cause the debug issue you're seeing.

I thought the debug was going to be connected to acLoad2 and acLoad3 to get those messages coming out.


However I spotted a few more typos acLoad instead of acLoads:


 
var acSolar = global.get('acSolar');
 

 
var gridSetpoint = global.get('gridSetpoint');
 
var exported= global.get('exported');
 
//setting soc variable depending on inputs from flows
 
if (msg.topic === "acLoad1") {
acLoad1 = msg.payload; // Extract payload from input 1
 
global.set('acLoad1', acLoad1);
 
}
 
if (msg.topic === "acLoad2") {
acLoad2 = msg.payload; // Extract payload from input 1
 
global.set('acLoad2', acLoad2);
 
}
 
if (msg.topic === "acLoad3") {
acLoad3 = msg.payload; // Extract payload from input 1
 
global.set('acLoad3', acLoad3);
 
}
 
//setting soc variable depending on inputs from flows
 
if (msg.topic === "exported") {
exported = msg.payload; // Extract payload from input 1
 
global.set('exported', exported);
 
}
 
if (msg.topic === "acSolar") {
acSolar = msg.payload; // Extract payload from input 2
 
global.set('acSolar', acSolar);
 
}
 
if (msg.topic === "dcSolar1") {
dcSolar1 = msg.payload; // Extract payload from input 3
 
global.set('dcSolar1', dcSolar1);
 
}
 
if (msg.topic === "dcSolar2") {
dcSolar2 = msg.payload; // Extract payload from input 3
 
global.set('dcSolar2', dcSolar2);
 
}
 
//this is used to check if gridsetpoint is already 0, if it is then we won't bother updating it.
 
if (msg.topic === "gridSetpoint") {
gridSetpoint = msg.payload;
 
global.set('gridSetpoint', gridSetpoint);
 
}
 
if (msg.topic === "acLoad") {
acLoad = msg.payload; // Extract payload from input 2
 
global.set('acLoad ', acLoad);
 
}


var dcSolar = global.get("dcSolar1") + global.get("dcSolar2");
var acLoad = global.get("acLoad1") + global.get("acLoad2") + global.get("acLoad3");
 
//The main logic.
 
if (exported < 10000) {
 
gridSetpoint = acLoad - acSolar + dcSolar;
 
} else {
gridSetpoint = 0;
 
}
 
if (gridSetpoint !== global.get('gridSetpoint')) { // Check if gridSetpoint is going to change
 
global.set('gridSetpoint', Math.min(gridSetpoint, 0));
 
msg.payload = Math.min(gridSetpoint, 0);
 
return msg;
 
}



0 Likes 0 ·
jan-bolecky avatar image
jan-bolecky answered ·

1715790202469.png

heres my solution for this timed export if any1 is looking for this.
also if u guys are connecting all the change nodes into function node it will be called very very often(i had very weird results with this) rather get an inject node on repeat 1s for ex. this solution also gets solar forecast from vrm and if it is greater then 30kwh it export 70% of excees power into grid and if less only 40%. I do this kind of control because i got ~14kwp of solar and only 3x3000VA multipluses which caps at 10:00(-6300W) and solar at 8kw and its constantly at 8kw for few hours. this way i can get up to 12kw power from solar since battery is able to absorb some additional power.


1715790202469.png (49.5 KiB)
1 comment
2 |3000

Up to 8 attachments (including images) can be used with a maximum of 190.8 MiB each and 286.6 MiB total.

jan-bolecky avatar image jan-bolecky commented ·

this is function 2:

var ac = global.get('AcCons');
var sp = global.get('SolarPower');
var frcast = global.get('Forecast');
var sub = 0;
let time = new Date();
let hour = time.getHours();
let mult = 0.4;
if (frcast > 30){
    mult = 0.7;
}
//ac !== 0 && sp !== 0
if (hour<=10 && hour>=7) {
    // Perform the subtraction and subtract 1000
    sub = sp-ac;
    sub *= mult;
    sub = -Math.round(sub,0);
    // Create a new message object to send only the subtraction result
    let newMsg = {
        topic: "SubtractionResult",
        payload: sub
    };
    newMsg.complete = true;
    // Return the new message object
    return newMsg;
} else {
    let newMsg = {
        topic: "SubtractionResult",
        payload: 0
    };
    // If either AC consumption or solar power value is missing, do nothing
    return newMsg;
}

this is function 3 :

let tmp = Math.round(msg.payload.totals.solar_yield_forecast/1000,0);
 global.set('Forecast',tmp)
 msg.payload = tmp;
return msg;
0 Likes 0 ·
dougle03 avatar image
dougle03 answered ·

Brilliant thread this. Well done all. I guess we are all learning there are many different ways to get to the same output, that's the beauty and power of Node Red. Well done Matt great advice and examples too.

2 |3000

Up to 8 attachments (including images) can be used with a maximum of 190.8 MiB each and 286.6 MiB total.