diff --git a/io.openems.edge.meter.ddsu666/.classpath b/io.openems.edge.meter.ddsu666/.classpath
new file mode 100644
index 0000000000..a6cf990d45
--- /dev/null
+++ b/io.openems.edge.meter.ddsu666/.classpath
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/io.openems.edge.meter.ddsu666/.gitignore b/io.openems.edge.meter.ddsu666/.gitignore
new file mode 100644
index 0000000000..1c316c001c
--- /dev/null
+++ b/io.openems.edge.meter.ddsu666/.gitignore
@@ -0,0 +1 @@
+/bin_test/
diff --git a/io.openems.edge.meter.ddsu666/.project b/io.openems.edge.meter.ddsu666/.project
new file mode 100644
index 0000000000..2102fe5868
--- /dev/null
+++ b/io.openems.edge.meter.ddsu666/.project
@@ -0,0 +1,23 @@
+
+
+ io.openems.edge.meter.ddsu666
+
+
+
+
+
+ org.eclipse.jdt.core.javabuilder
+
+
+
+
+ bndtools.core.bndbuilder
+
+
+
+
+
+ org.eclipse.jdt.core.javanature
+ bndtools.core.bndnature
+
+
diff --git a/io.openems.edge.meter.ddsu666/.settings/org.eclipse.core.resources.prefs b/io.openems.edge.meter.ddsu666/.settings/org.eclipse.core.resources.prefs
new file mode 100644
index 0000000000..9824bab293
--- /dev/null
+++ b/io.openems.edge.meter.ddsu666/.settings/org.eclipse.core.resources.prefs
@@ -0,0 +1,9 @@
+eclipse.preferences.version=1
+encoding//src/io/openems/edge/meter/ddsu666/Config.java=UTF-8
+encoding//src/io/openems/edge/meter/ddsu666/MeterDDSU666.java=UTF-8
+encoding//src/io/openems/edge/meter/ddsu666/MeterDDSU666Impl.java=UTF-8
+encoding//test/io/openems/edge/meter/ddsu666/MyConfig.java=UTF-8
+encoding//test/io/openems/edge/meter/ddsu666/MyModbusDeviceTest.java=UTF-8
+encoding/=UTF-8
+encoding/bnd.bnd=UTF-8
+encoding/readme.adoc=UTF-8
diff --git a/io.openems.edge.meter.ddsu666/.settings/org.eclipse.jdt.core.prefs b/io.openems.edge.meter.ddsu666/.settings/org.eclipse.jdt.core.prefs
new file mode 100644
index 0000000000..9a7984bb6c
--- /dev/null
+++ b/io.openems.edge.meter.ddsu666/.settings/org.eclipse.jdt.core.prefs
@@ -0,0 +1,11 @@
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=21
+org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
+org.eclipse.jdt.core.compiler.compliance=21
+org.eclipse.jdt.core.compiler.debug.lineNumber=generate
+org.eclipse.jdt.core.compiler.debug.localVariable=generate
+org.eclipse.jdt.core.compiler.debug.sourceFile=generate
+org.eclipse.jdt.core.compiler.problem.enablePreviewFeatures=disabled
+org.eclipse.jdt.core.compiler.problem.reportPreviewFeatures=warning
+org.eclipse.jdt.core.compiler.release=enabled
+org.eclipse.jdt.core.compiler.source=21
diff --git a/io.openems.edge.meter.ddsu666/bnd.bnd b/io.openems.edge.meter.ddsu666/bnd.bnd
new file mode 100644
index 0000000000..fee0e1cc16
--- /dev/null
+++ b/io.openems.edge.meter.ddsu666/bnd.bnd
@@ -0,0 +1,15 @@
+Bundle-Name: OpenEMS Edge io.openems.edge.meter.ddsu666
+Bundle-Vendor: OpenEMS Association e.V.
+Bundle-License: https://opensource.org/licenses/EPL-2.0
+Bundle-Version: 1.0.0.${tstamp}
+
+-buildpath: \
+ ${buildpath},\
+ io.openems.j2mod,\
+ io.openems.common,\
+ io.openems.edge.bridge.modbus,\
+ io.openems.edge.common,\
+ io.openems.edge.meter.api;version=snapshot
+
+-testpath: \
+ ${testpath}
diff --git a/io.openems.edge.meter.ddsu666/generated/buildfiles b/io.openems.edge.meter.ddsu666/generated/buildfiles
new file mode 100644
index 0000000000..de2bfe24f3
--- /dev/null
+++ b/io.openems.edge.meter.ddsu666/generated/buildfiles
@@ -0,0 +1 @@
+/home/phat/openems/io.openems.edge.meter.ddsu666/generated/io.openems.edge.meter.ddsu666.jar
diff --git a/io.openems.edge.meter.ddsu666/readme.adoc b/io.openems.edge.meter.ddsu666/readme.adoc
new file mode 100644
index 0000000000..656f50d5f9
--- /dev/null
+++ b/io.openems.edge.meter.ddsu666/readme.adoc
@@ -0,0 +1,3 @@
+= io.openems.edge.meter.ddsu666
+
+https://github.com/OpenEMS/openems/tree/develop/io.openems.edge.meter.ddsu666[Source Code icon:github[]]
\ No newline at end of file
diff --git a/io.openems.edge.meter.ddsu666/src/io/openems/edge/meter/ddsu666/Config.java b/io.openems.edge.meter.ddsu666/src/io/openems/edge/meter/ddsu666/Config.java
new file mode 100644
index 0000000000..24ad820d19
--- /dev/null
+++ b/io.openems.edge.meter.ddsu666/src/io/openems/edge/meter/ddsu666/Config.java
@@ -0,0 +1,36 @@
+package io.openems.edge.meter.ddsu666;
+
+import org.osgi.service.metatype.annotations.AttributeDefinition;
+import org.osgi.service.metatype.annotations.ObjectClassDefinition;
+
+
+import io.openems.common.types.MeterType;
+@ObjectClassDefinition(//
+ name = "MeterDDSU666", //
+ description = "")
+@interface Config {
+
+ @AttributeDefinition(name = "Component-ID", description = "Unique ID of this Component")
+ String id() default "meter0";
+
+ @AttributeDefinition(name = "Alias", description = "Human-readable name of this Component; defaults to Component-ID")
+ String alias() default "";
+
+ @AttributeDefinition(name = "Is enabled?", description = "Is this Component enabled?")
+ boolean enabled() default true;
+
+ @AttributeDefinition(name = "Meter-Type", description = "Grid, Production (=default), Consumption")
+ MeterType type() default MeterType.PRODUCTION;
+
+ @AttributeDefinition(name = "Modbus-ID", description = "ID of Modbus bridge.")
+ String modbus_id() default "modbus0";
+
+ @AttributeDefinition(name = "Modbus Unit-ID", description = "The Unit-ID of the Modbus device.")
+ int modbusUnitId() default 1;
+
+ @AttributeDefinition(name = "Modbus target filter", description = "This is auto-generated by 'Modbus-ID'.")
+ String Modbus_target() default "(enabled=true)";
+
+ String webconsole_configurationFactory_nameHint() default "io.openems.edge.meter.ddsu666 [{id}]";
+
+}
\ No newline at end of file
diff --git a/io.openems.edge.meter.ddsu666/src/io/openems/edge/meter/ddsu666/MeterDDSU666.java b/io.openems.edge.meter.ddsu666/src/io/openems/edge/meter/ddsu666/MeterDDSU666.java
new file mode 100644
index 0000000000..abd96132f3
--- /dev/null
+++ b/io.openems.edge.meter.ddsu666/src/io/openems/edge/meter/ddsu666/MeterDDSU666.java
@@ -0,0 +1,31 @@
+package io.openems.edge.meter.ddsu666;
+
+import org.osgi.service.event.EventHandler;
+
+import io.openems.common.channel.Unit;
+import io.openems.common.types.OpenemsType;
+import io.openems.edge.common.channel.Doc;
+import io.openems.edge.common.component.OpenemsComponent;
+
+
+public interface MeterDDSU666 extends OpenemsComponent {
+
+ public enum ChannelId implements io.openems.edge.common.channel.ChannelId {
+
+ POWER_FACTOR(Doc.of(OpenemsType.FLOAT) //
+ .unit(Unit.NONE) //
+ .text("Power Factor"));
+
+ private final Doc doc;
+
+ private ChannelId(Doc doc) {
+ this.doc = doc;
+ }
+
+ @Override
+ public Doc doc() {
+ return this.doc;
+ }
+ }
+
+}
diff --git a/io.openems.edge.meter.ddsu666/src/io/openems/edge/meter/ddsu666/MeterDDSU666Impl.java b/io.openems.edge.meter.ddsu666/src/io/openems/edge/meter/ddsu666/MeterDDSU666Impl.java
new file mode 100644
index 0000000000..04612ec35a
--- /dev/null
+++ b/io.openems.edge.meter.ddsu666/src/io/openems/edge/meter/ddsu666/MeterDDSU666Impl.java
@@ -0,0 +1,133 @@
+package io.openems.edge.meter.ddsu666;
+
+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.edge.common.channel.Doc;
+import io.openems.edge.bridge.modbus.api.element.FloatDoublewordElement;
+import io.openems.edge.bridge.modbus.api.element.WordOrder;
+import io.openems.edge.common.channel.FloatReadChannel;
+import io.openems.common.channel.AccessMode;
+import io.openems.common.exceptions.OpenemsException;
+import io.openems.common.types.MeterType;
+import io.openems.edge.bridge.modbus.api.AbstractOpenemsModbusComponent;
+import io.openems.edge.bridge.modbus.api.BridgeModbus;
+import io.openems.edge.bridge.modbus.api.ElementToChannelConverter;
+import io.openems.edge.bridge.modbus.api.ModbusComponent;
+import io.openems.edge.bridge.modbus.api.ModbusProtocol;
+import io.openems.edge.bridge.modbus.api.element.DummyRegisterElement;
+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.modbusslave.ModbusSlave;
+import io.openems.edge.common.modbusslave.ModbusSlaveNatureTable;
+import io.openems.edge.common.modbusslave.ModbusSlaveTable;
+import io.openems.edge.common.taskmanager.Priority;
+import io.openems.edge.meter.api.ElectricityMeter;
+import io.openems.edge.common.modbusslave.ModbusType;
+
+@Designate(ocd = Config.class, factory = true)
+@Component(//
+ name = "MeterDDSU666", //
+ immediate = true, //
+ configurationPolicy = ConfigurationPolicy.REQUIRE //
+)
+public class MeterDDSU666Impl extends AbstractOpenemsModbusComponent implements MeterDDSU666, ElectricityMeter, ModbusSlave, ModbusComponent, OpenemsComponent {
+
+ @Reference
+ private ConfigurationAdmin cm;
+
+ @Reference(policy = ReferencePolicy.STATIC, policyOption = ReferencePolicyOption.GREEDY, cardinality = ReferenceCardinality.MANDATORY)
+ protected void setModbus(BridgeModbus modbus) {
+ super.setModbus(modbus);
+ }
+
+ private Config config = null;
+
+ public MeterDDSU666Impl() {
+ super(//
+ OpenemsComponent.ChannelId.values(), //
+ ModbusComponent.ChannelId.values(), //
+ MeterDDSU666.ChannelId.values(), //
+ ElectricityMeter.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 {
+ if(super.activate(context, config.id(), config.alias(), config.enabled(), config.modbusUnitId(), this.cm, "Modbus",
+ config.modbus_id())) {
+ return;
+ }
+ this.config = config;
+ }
+
+ @Override
+ @Deactivate
+ protected void deactivate() {
+ super.deactivate();
+ }
+
+ @Override
+ protected ModbusProtocol defineModbusProtocol() {
+ // TODO implement ModbusProtocol
+ return new ModbusProtocol(this,
+ new FC3ReadRegistersTask(8192, Priority.HIGH,
+ m(ElectricityMeter.ChannelId.VOLTAGE, new FloatDoublewordElement(8192))
+ .wordOrder(WordOrder.MSWLSW),
+ m(ElectricityMeter.ChannelId.CURRENT, new FloatDoublewordElement(8194))
+ .wordOrder(WordOrder.MSWLSW),
+ m(ElectricityMeter.ChannelId.ACTIVE_POWER, new FloatDoublewordElement(8196))
+ .wordOrder(WordOrder.MSWLSW)
+ ),
+ new FC3ReadRegistersTask(8202, Priority.HIGH,
+ m(MeterDDSU666.ChannelId.POWER_FACTOR, new FloatDoublewordElement(8202))
+ .wordOrder(WordOrder.MSWLSW),
+ m(ElectricityMeter.ChannelId.FREQUENCY, new FloatDoublewordElement(8206))
+ .wordOrder(WordOrder.MSWLSW)
+ ),
+ new FC3ReadRegistersTask(16384, Priority.LOW,
+ m(ElectricityMeter.ChannelId.ACTIVE_CONSUMPTION_ENERGY, new FloatDoublewordElement(16384))
+ .wordOrder(WordOrder.MSWLSW)
+
+ )
+ );
+ }
+
+ private MeterType meterType = MeterType.PRODUCTION;
+ @Override
+ public MeterType getMeterType() {
+ return this.config.type();
+ }
+
+ @Override
+ public String debugLog() {
+ return "L:" + this.getActivePower().asString();
+ }
+ @Override
+ public ModbusSlaveTable getModbusSlaveTable(AccessMode accessMode) {
+ return new ModbusSlaveTable(
+ // Các nature tables cơ bản
+ OpenemsComponent.getModbusSlaveNatureTable(accessMode),
+ ElectricityMeter.getModbusSlaveNatureTable(accessMode),
+
+ // Custom channels của DDSu666
+ ModbusSlaveNatureTable.of(MeterDDSU666.class, accessMode, 100)
+ .channel(0, MeterDDSU666.ChannelId.POWER_FACTOR, ModbusType.FLOAT32)
+ .build()
+ );
+ }
+}
diff --git a/io.openems.edge.meter.ddsu666/test/io/openems/edge/meter/ddsu666/MyConfig.java b/io.openems.edge.meter.ddsu666/test/io/openems/edge/meter/ddsu666/MyConfig.java
new file mode 100644
index 0000000000..f2a486f415
--- /dev/null
+++ b/io.openems.edge.meter.ddsu666/test/io/openems/edge/meter/ddsu666/MyConfig.java
@@ -0,0 +1,75 @@
+package io.openems.edge.meter.ddsu666;
+
+import io.openems.common.utils.ConfigUtils;
+import io.openems.common.test.AbstractComponentConfig;
+import io.openems.common.types.MeterType;
+
+@SuppressWarnings("all")
+public class MyConfig extends AbstractComponentConfig implements Config {
+
+ protected static class Builder {
+ private String id;
+ private String modbusId = null;
+ private int modbusUnitId;
+ private MeterType type;
+
+ private Builder() {
+ }
+
+ public Builder setId(String id) {
+ this.id = id;
+ return this;
+ }
+
+ public Builder setModbusId(String modbusId) {
+ this.modbusId = modbusId;
+ return this;
+ }
+
+ public Builder setModbusUnitId(int modbusUnitId) {
+ this.modbusUnitId = modbusUnitId;
+ return this;
+ }
+
+ public MyConfig build() {
+ return new MyConfig(this);
+ }
+ }
+
+ /**
+ * Create a Config builder.
+ *
+ * @return a {@link Builder}
+ */
+ public static Builder create() {
+ return new Builder();
+ }
+
+ private final Builder builder;
+
+ private MyConfig(Builder builder) {
+ super(Config.class, builder.id);
+ this.builder = builder;
+ }
+
+ @Override
+ public String modbus_id() {
+ return this.builder.modbusId;
+ }
+
+ @Override
+ public String Modbus_target() {
+ return ConfigUtils.generateReferenceTargetFilter(this.id(), this.modbus_id());
+ }
+
+ @Override
+ public int modbusUnitId() {
+ return this.builder.modbusUnitId;
+ }
+
+ @Override
+ public MeterType type() {
+ return this.builder.type;
+ }
+
+}
\ No newline at end of file
diff --git a/io.openems.edge.meter.ddsu666/test/io/openems/edge/meter/ddsu666/MyModbusDeviceTest.java b/io.openems.edge.meter.ddsu666/test/io/openems/edge/meter/ddsu666/MyModbusDeviceTest.java
new file mode 100644
index 0000000000..948f4a2d91
--- /dev/null
+++ b/io.openems.edge.meter.ddsu666/test/io/openems/edge/meter/ddsu666/MyModbusDeviceTest.java
@@ -0,0 +1,26 @@
+package io.openems.edge.meter.ddsu666;
+
+import org.junit.Test;
+
+import io.openems.edge.common.test.AbstractComponentTest.TestCase;
+import io.openems.edge.bridge.modbus.test.DummyModbusBridge;
+import io.openems.edge.common.test.ComponentTest;
+import io.openems.edge.common.test.DummyConfigurationAdmin;
+import io.openems.common.types.MeterType;
+
+public class MyModbusDeviceTest {
+
+ @Test
+ public void test() throws Exception {
+ new ComponentTest(new MeterDDSU666Impl()) //
+ .addReference("cm", new DummyConfigurationAdmin()) //
+ .addReference("setModbus", new DummyModbusBridge("modbus0")) //
+ .activate(MyConfig.create() //
+ .setId("component0") //
+ .setModbusId("modbus0") //
+ .build()) //
+ .next(new TestCase()) //
+ .deactivate();
+ }
+
+}
diff --git a/openems b/openems
new file mode 160000
index 0000000000..0d4a49a028
--- /dev/null
+++ b/openems
@@ -0,0 +1 @@
+Subproject commit 0d4a49a0283f98528252983f869bb3c0ac0ee279