Modbus Pulse Coils with Spring Boot project
Asked Answered
J

1

1

Have to implement call to Digital IO with Modbus protocol on Spring Boot project build by Maven.

It should be a pulsed call with some duration at the body (like 5 seconds, etc.)

Here is a snippet from the specification:

enter image description here

And more about Response and Errors:

enter image description here

Seems at this call application should behave like a master. And Web Relay should be like a slave in this case.

I suppose that taking some library for integrating with Modbus will be much more preferable than writing it from zero.

Which approach will be preferable to adopt this functionality with Spring Boot?

Here are Web Relay manuals.


UPDATE:

Tried answer by KevinHerron

Here is the code part:

@Override
public void callDigitalIO(String ipAddress, String relay) {
    log.debug("call Digital IO by MODBUS");
    checkNotEmpty(ipAddress, relay);

    ModbusTcpMasterConfig config = new ModbusTcpMasterConfig.Builder(ipAddress).build();
    ModbusTcpMaster master = new ModbusTcpMaster(config);
    master.connect();

    ByteBuf buffer = Unpooled.buffer();
    buffer.writeInt(0x00003F00);
    buffer.writeInt(0x000040A0);

    int relayNum = Integer.parseInt(relay);
    WriteMultipleRegistersRequest request = new WriteMultipleRegistersRequest(
            relayNum,
            0x02 * relayNum,
            buffer
    );

    log.debug("MODBUS_REQUEST: {}", request);

    master.sendRequest(request, 0xFF)
            .whenCompleteAsync((response, ex) -> {
                if (response != null) {
                    log.info("MODBUS_RESPONSE: {}", response);
                } else {
                    log.error("EXECUTION_FAILED: {}", ex.getMessage(), ex);
                }
            });

    master.disconnect();
}

Tried below snippet with a real device, the output is following:

2021-02-25 22-07-03.955 [carpark-ex-4] DEBUG c.s.s.dio.ModbusDigitalIoServiceImpl - MODBUS_REQUEST: WriteMultipleRegistersRequest(UnpooledByteBufAllocator$InstrumentedUnpooledUnsafeHeapByteBuf(ridx: 0, widx: 8, cap: 256))
2021-02-25 22-07-03.970 [ForkJoinPool.commonPool-worker-5] ERROR c.s.s.dio.ModbusDigitalIoServiceImpl - EXECUTION_FAILED: not connected
java.lang.Exception: not connected
    at com.digitalpetri.netty.fsm.ChannelFsmFactory.lambda$null$2(ChannelFsmFactory.java:115)
    at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1130)
    at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:630)
    at java.base/java.lang.Thread.run(Thread.java:832)

Only when I did:

master.sendRequest(request, 0xFF).get()

it started to work.

Jordison answered 16/2, 2021 at 18:21 Comment(0)
J
1

This should be easy to accomplish using an existing library that supports the WriteMultipleRegisters function.

Try https://github.com/digitalpetri/modbus


An example of how to construct a WriteMultipleRegisters request based on that screenshot of documentation:

class Scratch {

  public static void main(String[] args) throws ExecutionException, InterruptedException {
    ModbusTcpMasterConfig config = new ModbusTcpMasterConfig.Builder("ip_or_hostname").build();
    ModbusTcpMaster master = new ModbusTcpMaster(config);

    master.connect().get();

    ByteBuf buffer = Unpooled.buffer();
    buffer.writeInt(0x00003F00);
    buffer.writeInt(0x000040A0);

    WriteMultipleRegistersRequest request = new WriteMultipleRegistersRequest(
        0x16,
        0x04,
        buffer
    );

    master.sendRequest(request, 0xFF).get();
  }

}
Joan answered 18/2, 2021 at 18:56 Comment(12)
Could you explain more details about this call? Have it should be done, is it possible to set timeout to itJordison
Updated. Time for you to start experimenting. Write code and try it out.Joan
could you help to understand meaning of unitId?Jordison
It's in the spec. It's called slave id when it's Modbus over serial. Your device documentation mentions just using 0xFF, which is commonly used when talking directly to the server and not to a slave behind a gateway. See modbus.org/docs/Modbus_Messaging_Implementation_Guide_V1_0b.pdfJoan
could you clarify one more place - Pulse Duration (4 bytes, per relay): 0x3DCCCCCC – 0x47A8C000 (Little-endian Format: 0xCCCC3DCC – 0xC00047A8) - how exactly this duration should be set depends on relay number?Jordison
Those docs suggest you can write to one or more contiguous relays, and you would include one or more 4-byte pulse durations to match.Joan
tried this approach with real device and it failed. Updated the question with stacktrace.Jordison
Ah, you have to call connect() first on the master.Joan
surrounded sending request with connect() and disconnect() with whenComplete() it didn't work, but with get() works fine. Why did this happen?Jordison
connect() and disconnect() are async. They return a CompletableFuture that you should call get() on to block until the action is complete. Otherwise you would need to do the next set of actions in a callback instead.Joan
You should also not disconnect() until after your response has been received.Joan
Let us continue this discussion in chat.Jordison

© 2022 - 2024 — McMap. All rights reserved.