Adding Smart-me energy meter

Hi all

Is there someone that can help me implemeting a Smart-me meter as a OpenEMS Edge device. I have the physical device available on my network for testing. Also I have a list of the MODBUS-TCP addresses. Hope someone can help me adding my first device and create a pull request so it’s made available for a comming release.

Ps. I have of course installed the IDE and followed the “Getting Started” guide.

Br. Lars

Hi Lars,

to create a new device, I use the way from the documentation to create a new OSGI bundle.
https://openems.github.io/openems.io/openems/latest/edge/implement.html
For the code itself, I usually use an existing package that is quite similar to the new one and then copy the code together from it.
A relative simple Modbus counter is the Janitza umg511 or the SMA shm20.

1 Like

Hi Josef

Thanks for that suggestion. I’ve tried to follow that path, however I get some errors which I hope that you can help me correcting?

here is the complete code;
package io.openems.edge.meter.smartme.kamstrup;

import static io.openems.edge.bridge.modbus.api.ElementToChannelConverter.SCALE_FACTOR_3;

import java.util.function.Consumer;

import org.osgi.service.cm.ConfigurationAdmin;
import org.osgi.service.component.ComponentContext;
import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.ConfigurationPolicy;
import org.osgi.service.component.annotations.Deactivate;
import org.osgi.service.component.annotations.Reference;
import org.osgi.service.component.annotations.ReferenceCardinality;
import org.osgi.service.component.annotations.ReferencePolicy;
import org.osgi.service.component.annotations.ReferencePolicyOption;
import org.osgi.service.metatype.annotations.Designate;

import io.openems.common.exceptions.OpenemsException;
import io.openems.edge.bridge.modbus.api.AbstractOpenemsModbusComponent;
import io.openems.edge.bridge.modbus.api.BridgeModbus;
import io.openems.edge.bridge.modbus.api.ModbusComponent;
import io.openems.edge.bridge.modbus.api.ModbusProtocol;
import io.openems.edge.bridge.modbus.api.element.SignedDoublewordElement;
import io.openems.edge.bridge.modbus.api.element.UnsignedDoublewordElement;
import io.openems.edge.bridge.modbus.api.task.FC3ReadRegistersTask;
import io.openems.edge.common.channel.IntegerReadChannel;
import io.openems.edge.common.channel.value.Value;
import io.openems.edge.common.component.OpenemsComponent;
import io.openems.edge.common.taskmanager.Priority;
import io.openems.edge.meter.api.ElectricityMeter;
import io.openems.edge.meter.api.MeterType;

@Designate(ocd = Config.class, factory = true)
@Component(//
name = “Meter.Smartme.Kamstrup”, //
immediate = true, //
configurationPolicy = ConfigurationPolicy.REQUIRE //
)
public class MeterSmartmeKamstrupImpl extends AbstractOpenemsModbusComponent
implements MeterSmartmeKamstrup, ElectricityMeter, ModbusComponent, OpenemsComponent {

@Reference
private ConfigurationAdmin cm;

@Override
@Reference(policy = ReferencePolicy.STATIC, policyOption = ReferencePolicyOption.GREEDY, cardinality = ReferenceCardinality.MANDATORY)
protected void setModbus(BridgeModbus modbus) {
	super.setModbus(modbus);
}

private MeterType meterType = MeterType.GRID;

public MeterSmartmeKamstrupImpl() {
	super(//
			OpenemsComponent.ChannelId.values(), //
			ModbusComponent.ChannelId.values(), //
			ElectricityMeter.ChannelId.values(), //
			MeterSmartmeKamstrup.ChannelId.values() //
	);

	// Automatically calculate sum values from L1/L2/L3
	ElectricityMeter.calculateSumCurrentFromPhases(this);
	ElectricityMeter.calculateAverageVoltageFromPhases(this);
}

@Activate
private void activate(ComponentContext context, Config config) throws OpenemsException {
	this.meterType = config.type();
	if (super.activate(context, config.id(), config.alias(), config.enabled(), config.modbusUnitId(), this.cm,
			"Modbus", config.modbus_id())) {
		return;
	}
}

@Override
@Deactivate
protected void deactivate() {
	super.deactivate();
}

@Override
public MeterType getMeterType() {
	return this.meterType;
}

@Override
protected ModbusProtocol defineModbusProtocol() throws OpenemsException {
	var modbusProtocol = new ModbusProtocol(this,
			new FC3ReadRegistersTask(8272, Priority.HIGH, //
					m(ElectricityMeter.ChannelId.ACTIVE_CONSUMPTION_ENERGY, new UnsignedDoublewordElement(8272),
					m(ElectricityMeter.ChannelId.ACTIVE_PRODUCTION_ENERGY, new UnsignedDoublewordElement(8276),
					m(ElectricityMeter.ChannelId.ACTIVE_POWER_L1, new SignedDoublewordElement(8198), SCALE_FACTOR_3),
					m(ElectricityMeter.ChannelId.ACTIVE_POWER_L2, new SignedDoublewordElement(8200), SCALE_FACTOR_3),
					m(ElectricityMeter.ChannelId.ACTIVE_POWER_L3, new SignedDoublewordElement(8202), SCALE_FACTOR_3),
					m(ElectricityMeter.ChannelId.ACTIVE_POWER, new SignedDoublewordElement(8196), SCALE_FACTOR_3),
					m(ElectricityMeter.ChannelId.REACTIVE_POWER_L1, new UnsignedDoublewordElement(8206), SCALE_FACTOR_3),
					m(ElectricityMeter.ChannelId.REACTIVE_POWER_L2, new UnsignedDoublewordElement(8208), SCALE_FACTOR_3),
					m(ElectricityMeter.ChannelId.REACTIVE_POWER_L3, new UnsignedDoublewordElement(8210), SCALE_FACTOR_3),
					m(ElectricityMeter.ChannelId.REACTIVE_POWER, new UnsignedDoublewordElement(8204), SCALE_FACTOR_3)),
					m(ElectricityMeter.ChannelId.VOLTAGE_L1, new UnsignedDoublewordElement(8212), SCALE_FACTOR_3),
					m(ElectricityMeter.ChannelId.VOLTAGE_L2, new UnsignedDoublewordElement(8214), SCALE_FACTOR_3),
					m(ElectricityMeter.ChannelId.VOLTAGE_L3, new UnsignedDoublewordElement(8216), SCALE_FACTOR_3),
					m(ElectricityMeter.ChannelId.CURRENT_L1, new SignedDoublewordElement(8218), SCALE_FACTOR_3),
					m(ElectricityMeter.ChannelId.CURRENT_L2, new SignedDoublewordElement(8220), SCALE_FACTOR_3),
					m(ElectricityMeter.ChannelId.CURRENT_L3, new SignedDoublewordElement(8222), SCALE_FACTOR_3))));

	return modbusProtocol;
}

@Override
public String debugLog() {
	return "L:" + this.getActivePower().asString();
}

}

Hi again. Found a mistake so now it looks like this;
package io.openems.edge.meter.smartme.kamstrup;

import static io.openems.edge.bridge.modbus.api.ElementToChannelConverter.SCALE_FACTOR_3;

import org.osgi.service.cm.ConfigurationAdmin;
import org.osgi.service.component.ComponentContext;
import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.ConfigurationPolicy;
import org.osgi.service.component.annotations.Deactivate;
import org.osgi.service.component.annotations.Reference;
import org.osgi.service.component.annotations.ReferenceCardinality;
import org.osgi.service.component.annotations.ReferencePolicy;
import org.osgi.service.component.annotations.ReferencePolicyOption;
import org.osgi.service.metatype.annotations.Designate;

import io.openems.common.exceptions.OpenemsException;
import io.openems.edge.bridge.modbus.api.AbstractOpenemsModbusComponent;
import io.openems.edge.bridge.modbus.api.BridgeModbus;
import io.openems.edge.bridge.modbus.api.ModbusComponent;
import io.openems.edge.bridge.modbus.api.ModbusProtocol;
import io.openems.edge.bridge.modbus.api.element.SignedDoublewordElement;
import io.openems.edge.bridge.modbus.api.element.UnsignedDoublewordElement;
import io.openems.edge.bridge.modbus.api.task.FC3ReadRegistersTask;
import io.openems.edge.common.component.OpenemsComponent;
import io.openems.edge.common.taskmanager.Priority;
import io.openems.edge.meter.api.ElectricityMeter;
import io.openems.edge.meter.api.MeterType;

@Designate(ocd = Config.class, factory = true)
@Component(//
name = “Meter.Smartme.Kamstrup”, //
immediate = true, //
configurationPolicy = ConfigurationPolicy.REQUIRE //
)
public class MeterSmartmeKamstrupImpl extends AbstractOpenemsModbusComponent
implements MeterSmartmeKamstrup, ElectricityMeter, ModbusComponent, OpenemsComponent {

@Reference
private ConfigurationAdmin cm;

@Override
@Reference(policy = ReferencePolicy.STATIC, policyOption = ReferencePolicyOption.GREEDY, cardinality = ReferenceCardinality.MANDATORY)
protected void setModbus(BridgeModbus modbus) {
	super.setModbus(modbus);
}

private MeterType meterType = MeterType.GRID;

public MeterSmartmeKamstrupImpl() {
	super(//
			OpenemsComponent.ChannelId.values(), //
			ModbusComponent.ChannelId.values(), //
			ElectricityMeter.ChannelId.values(), //
			MeterSmartmeKamstrup.ChannelId.values() //
	);

	// Automatically calculate sum values from L1/L2/L3
	ElectricityMeter.calculateSumCurrentFromPhases(this);
	ElectricityMeter.calculateAverageVoltageFromPhases(this);
}

@Activate
private void activate(ComponentContext context, Config config) throws OpenemsException {
	this.meterType = config.type();
	if (super.activate(context, config.id(), config.alias(), config.enabled(), config.modbusUnitId(), this.cm,
			"Modbus", config.modbus_id())) {
		return;
	}
}

@Override
@Deactivate
protected void deactivate() {
	super.deactivate();
}

@Override
public MeterType getMeterType() {
	return this.meterType;
}

@Override
protected ModbusProtocol defineModbusProtocol() throws OpenemsException {
	var modbusProtocol = new ModbusProtocol(this,
			new FC3ReadRegistersTask(8272, Priority.HIGH, //
					m(ElectricityMeter.ChannelId.ACTIVE_CONSUMPTION_ENERGY, new UnsignedDoublewordElement(8272)),
					m(ElectricityMeter.ChannelId.ACTIVE_PRODUCTION_ENERGY, new UnsignedDoublewordElement(8276)),
					m(ElectricityMeter.ChannelId.ACTIVE_POWER_L1, new SignedDoublewordElement(8198), SCALE_FACTOR_3),
					m(ElectricityMeter.ChannelId.ACTIVE_POWER_L2, new SignedDoublewordElement(8200), SCALE_FACTOR_3),
					m(ElectricityMeter.ChannelId.ACTIVE_POWER_L3, new SignedDoublewordElement(8202), SCALE_FACTOR_3),
					m(ElectricityMeter.ChannelId.ACTIVE_POWER, new SignedDoublewordElement(8196), SCALE_FACTOR_3),
					m(ElectricityMeter.ChannelId.REACTIVE_POWER_L1, new UnsignedDoublewordElement(8206), SCALE_FACTOR_3),
					m(ElectricityMeter.ChannelId.REACTIVE_POWER_L2, new UnsignedDoublewordElement(8208), SCALE_FACTOR_3),
					m(ElectricityMeter.ChannelId.REACTIVE_POWER_L3, new UnsignedDoublewordElement(8210), SCALE_FACTOR_3),
					m(ElectricityMeter.ChannelId.REACTIVE_POWER, new UnsignedDoublewordElement(8204), SCALE_FACTOR_3),
					m(ElectricityMeter.ChannelId.VOLTAGE_L1, new UnsignedDoublewordElement(8212), SCALE_FACTOR_3),
					m(ElectricityMeter.ChannelId.VOLTAGE_L2, new UnsignedDoublewordElement(8214), SCALE_FACTOR_3),
					m(ElectricityMeter.ChannelId.VOLTAGE_L3, new UnsignedDoublewordElement(8216), SCALE_FACTOR_3),
					m(ElectricityMeter.ChannelId.CURRENT_L1, new SignedDoublewordElement(8218), SCALE_FACTOR_3),
					m(ElectricityMeter.ChannelId.CURRENT_L2, new SignedDoublewordElement(8220), SCALE_FACTOR_3),
					m(ElectricityMeter.ChannelId.CURRENT_L3, new SignedDoublewordElement(8222), SCALE_FACTOR_3)));

	return modbusProtocol;
}

@Override
public String debugLog() {
	return "L:" + this.getActivePower().asString();
}

}

Hi Lars,
I’m not sure if I’ve found all your issues, but as far as I can see io.openems.edge.common.modbusslave.ModbusSlave and the io.openems.edge.common.modbusslave.ModbusSlaveTable are missing.
You also need

	@Override
	public ModbusSlaveTable getModbusSlaveTable(AccessMode accessMode) {
		return new ModbusSlaveTable(//
				OpenemsComponent.getModbusSlaveNatureTable(accessMode), //
				ElectricityMeter.getModbusSlaveNatureTable(accessMode) //
		);
	}

to get the values.
It is also important that the registers in the Modbus protocol are listed with their register number without a gap. If you have a small gap, you can use the DummyRegisterElement to skip it. If you have a large gap, create a new RegisterTask. A good example of this is the Janitza UMG 511, which uses both the DummyRegisterElement and an additional query. With the priority HIGH and LOW you can set whether you need the values for every run or whether they are just simple info values that only need to be queried less frequently.

Did you import com.ghgande.j2mod,\ to the bnd.bnd file ?

Hi
No I didn’t not. Why is that needed? Can you explain to me what is the function of this (bare over me - I’m new to OpenEMS)? I don’t think I came across it in the other Meter Edge Devices?

See here:

Hi Lars,
if you have more problems with your code, it’s easier to help you if you can move your code to a public github fork. That way we can see if you forgot a package or something else that is needed.

2 Likes

Hi Josef

Thanks for reaching out again and being so supportive. I’ll try during the weekend to move it to my Github Fork (also my first time doing this) - so wish me luck :slight_smile:

1 Like