ESS that can talk with OpenEMS using Modbus TCP

Hey everyone,

We are developing a new Energy storage system and would like it to be easily usable with OpenEMS. We will use modbus TCP. For the Modbus TCP server/slave on our ESS, what are the functionality that we need to provide in order to be OpenEMS compatible.

Do I look at the modbus tcp bridge to get an idea? Any tips or resources that can help.
Thanks.
Waqar

Hi Waqar,

OpenEMS can be used not only for external access to an integrated energy storage system, but also to control the internal components (Battery Inverter, Battery, etc.) directly. In that case you would use a Ess.Generic.ManagedSymmetric in combination with OpenEMS Components that implement the Battery and ManagedSymmetricBatteryInverter natures.

If you really just want an external access, your components should implement the respective ESS natures, like:

These natures define the required datapoints that should be provided as a minimum via your Modbus/TCP interface.

An example for such an implementation is:

Please keep us updated on the process. Also please consider becoming members of the OpenEMS Association if you use OpenEMS in production.

Regards,
Stefan

Hi Stefan,

As I was able to get my SolardEdge Battery system working I’ve tried to implement the write registers for getting control over charge/discharge power.
I´ve implemented SymmetricEss and MangedSymmetricEss natures. But I´m stuck getting the this.ess.setActivePowerEquals(this.config.power()); method working.

Is it right to feed just the registers

		protocol.addTask(//
		new FC16WriteRegistersTask(0xE00E, //
				m(ManagedSymmetricEss.ChannelId.SET_ACTIVE_POWER_EQUALS, new FloatDoublewordElement(0xE00E).wordOrder(WordOrder.LSWMSW),
						ElementToChannelConverter.SCALE_FACTOR_2), //
				m(ManagedSymmetricEss.ChannelId.SET_REACTIVE_POWER_EQUALS, new FloatDoublewordElement(0xE010).wordOrder(WordOrder.LSWMSW),
						ElementToChannelConverter.SCALE_FACTOR_2))); //

The Log

2023-03-05T18:10:15,216 [_cycle  ] INFO  [enems.edge.ess.power.api.Power] [SetActivePowerEquals] reducing requested [1000 W] to maximum power [0 W] for [ess0P]
2023-03-05T18:10:15,216 [_cycle  ] WARN  [r.solver.CalculatePowerExtrema] Unable to MINIMIZE [ess0] phase [ALL] pwr [ACTIVE]. Setting it to zero.
2023-03-05T18:10:15,217 [_cycle  ] WARN  [ms.edge.core.cycle.CycleWorker] [_cycle] Error in Controller [ctrlFixActivePower0]: No Feasible Solution [Channel [SetActivePowerEquals]+ess0P EQUALS 0]
2023-03-05T18:10:15,218 [_cycle  ] WARN  [ems.edge.ess.core.power.Solver] Power-Solver: Unable to solve under constraints!
2023-03-05T18:10:16,250 [modbusWR] INFO  [bstractOpenemsSunSpecComponent] [meter0] Ignoring SunSpec-Model [713] starting at [41264]

I`ve also took a look on the fenecon commercial. But I still don´t unterstanbd relation between the single natures.
The only thing in the first step is to control the charge power of the system to get a time of use tariff working in the second step.

Have you got any hints for me?

regards
klinki

Hi Klinki,

I understand that it is neither very clear nor well documented how this has to be implemented. If you implement a ManagedSymmetricEss - i.e. a controllable energy storage system - you do not directly implement the SET_ACTIVE_POWER_EQUALS (and similar) Channels. Instead, you have to implement the applyPower(activePower, reactivePower) method, which is called once per Cycle (i.e. once per second). There you forward the requested activePower to the hardware device.

The FENECON Commercial 40 (*) implementation is really a very good and simple example how this works:

Add a SET_ACTIVE_POWER Write-Channel. This is coming from a “Nature”, but is local to your device:

Map the SET_ACTIVE_POWER Channel to a Modbus-Register:

In the applyPower() method forward the set-point to the Write-Channel.


NOTE: The SetActivePowerEquals Channels etc. are not directly forwarded to the applyPower() method. There is much more ‘intelligence’ inbetween, e.g. for cases where you have multiple Energy Storage Systems in a Cluster, you want to apply a PID filter, etc.


(*) The FENECON Commercial 40 was one of the first Commercial scale energy storage systems by FENECON. It is not sold anymore, but the implementation serves as a nice and simple example. The logic is smarter and more complex now with the current Commercial 30/50 and Industrial S/M/L series

Regards,
Stefan

Good morning Stefan,

I´ve implemented applyPower, added the SET_ACTIVE_POWER-Channel and added the Write-Register. But I`m still running against the error message:

2023-03-07T07:39:42,363 [_cycle  ] WARN  [r.solver.CalculatePowerExtrema] Unable to MAXIMIZE [ess0] phase [ALL] pwr [ACTIVE]. Setting it to zero.
2023-03-07T07:39:42,363 [_cycle  ] INFO  [enems.edge.ess.power.api.Power] [SetActivePowerEquals] reducing requested [1000 W] to maximum power [0 W] for [ess0P]
2023-03-07T07:39:42,364 [_cycle  ] WARN  [r.solver.CalculatePowerExtrema] Unable to MINIMIZE [ess0] phase [ALL] pwr [ACTIVE]. Setting it to zero.
2023-03-07T07:39:42,364 [_cycle  ] WARN  [ms.edge.core.cycle.CycleWorker] [_cycle] Error in Controller [ctrlFixActivePower0]: No Feasible Solution [Channel [SetActivePowerEquals]+ess0P EQUALS 0]

Is it necassary to implement a state machine to the ESS? The controller for a fixed charge power runs into a fail state.

Unfortunatly the documentation from SolarEdge is not very detailed. If I understand the attached document right there is no possibility setting the reactive power.
SunSpecBatterie.pdf (1.1 MB)
(p. 17ff)
kind regards
klinki

It sounds like ‘intelligence’ (i.e. the Ess-Power solver) is unable to find a solution, because it is missing some information.

It is also required to fill the Channels

  • MaxApparentPower of the inverter in [VA]
  • AllowedChargePower of the Battery; negative in [W]
  • AllowedDischargePower of the Battery; positive in [W]

Good morning Stefan,

I´m still stucked in the applyPower-method. The parameter activePower is not transfered to it.

I´ve created the channels to read and write the specified modbus-registers.
Because I actually use a software modbus-slave for testing I´ve hard-coded the limits.

	@Override
	public void applyPower(int activePower, int reactivePower) throws OpenemsNamedException {
		if (this.config.readOnlyMode()) {
			return;
		}
		int test = 777;
		int max_charge_power 		= -5000;
		int max_discharge_power 	= 4999;
		int max_apparent_power 		= 5001;
		EnumWriteChannel setControlMode = this.channel(SolarEdgeHybridEss.ChannelId.SET_CONTROL_MODE);
		EnumWriteChannel setChargePolicy = this.channel(SolarEdgeHybridEss.ChannelId.SET_STORAGE_CHARGE_POLICY);

		
		setControlMode.setNextWriteValue(ControlMode.SE_CTRL_MODE_REMOTE);	// Now the device can be remote controlled	
		setChargePolicy.setNextWriteValue(StorageChargePolicy.SE_CHARGE_POLICY_MAX_SELF_CONSUMPTION);	// Optimize self-consumption

		_setAllowedChargePower(max_charge_power);
		_setAllowedDischargePower(max_discharge_power);
		_setMaxApparentPower(max_apparent_power);

		IntegerWriteChannel setActivePowerChannel = this.channel(SolarEdgeHybridEss.ChannelId.SET_ACTIVE_POWER);
		setActivePowerChannel.setNextWriteValue(test);

	}
	

After the method is executed all values are set: the soft-modbus-slave will only “charge” the battery with 777W. This also works on the real battery system. Before applying I have to set the right control mode. But this is solaredge-specific…

I still do not understand the whole relation of my setup: The registers for max-charge/discharge power show me much higher values than the actual hardware limit, i.e. 8000W for discharge but the max value can only be set to 5kW. As it is a hybrid-system with a 48V DC battery attached - for what do I need the maximum apparent power?
The only register I`ve found is related to the AC(PV)-inverter section and shows 10kVA.

Regards
klinki

Good morning Stefan,

The problem was that solaredge needs two registers to be fed: one for charge power and one for discharge power. Both of them need positive values.

Now it seems to work but needs a bit more testing.

I´ve realized the hardware limits as constants so max_allowed_power for charging/discharging are set every cycle within the applyPower()-method.

I still haven´t understood the whole logic and behaviour of my system.

Thanks for your help!

best regards
klinki

I am trying to implement an AsymmetricEss and the component compiles. When I load the component using the fileinstall method, I see the following error in the logs. Any idea what am I doing wrong here?

ERROR: Bundle io.openems.edge.ess.voltstorage [200] Error starting/stopping bundle. (org.osgi.framework.BundleException: Unable to resolve io.openems.edge.ess.voltstorage [200](R 200.1): missing requirement [io.openems.edge.ess.voltstorage [200](R 200.1)] osgi.wiring.package; (&(osgi.wiring.package=org.osgi.service.component)(version>=1.5.0)(!(version>=2.0.0))) Unresolved requirements: [[io.openems.edge.ess.voltstorage [200](R 200.1)] osgi.wiring.package; (&(osgi.wiring.package=org.osgi.service.component)(version>=1.5.0)(!(version>=2.0.0)))])
org.osgi.framework.BundleException: Unable to resolve io.openems.edge.ess.voltstorage [200](R 200.1): missing requirement [io.openems.edge.ess.voltstorage [200](R 200.1)] osgi.wiring.package; (&(osgi.wiring.package=org.osgi.service.component)(version>=1.5.0)(!(version>=2.0.0))) Unresolved requirements: [[io.openems.edge.ess.voltstorage [200](R 200.1)] osgi.wiring.package; (&(osgi.wiring.package=org.osgi.service.component)(version>=1.5.0)(!(version>=2.0.0)))]
	at org.apache.felix.framework.Felix.resolveBundleRevision(Felix.java:4398)
	at org.apache.felix.framework.Felix.startBundle(Felix.java:2308)
	at org.apache.felix.framework.Felix.setBundleStartLevel(Felix.java:1754)
	at org.apache.felix.framework.FrameworkStartLevelImpl.run(FrameworkStartLevelImpl.java:338)
	at java.base/java.lang.Thread.run(Unknown Source)
2023-03-23T11:05:19,356 [tchQueue] ERROR [Events.Framework              ] FrameworkEvent ERROR

I think I found the problem. I was using vscode for development and somehow it was hiding the .project file, which was from the old component I used as a starting point for my component. Now I found the guide to configure IntelliJ IDEA and this is much better experience.

Edit: Here is the guide in case someone is interested in using IntelliJ IDEA → Setup IntelliJ IDEA for OpenEMS Edge and Backend