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.

catch23
  • 17,519
  • 42
  • 144
  • 217

1 Answers1

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();
  }

}
Kevin Herron
  • 6,500
  • 3
  • 26
  • 35
  • Could you explain more details about this call? Have it should be done, is it possible to set timeout to it – catch23 Feb 18 '21 at 18:58
  • 1
    Updated. Time for you to start experimenting. Write code and try it out. – Kevin Herron Feb 18 '21 at 19:36
  • could you help to understand meaning of `unitId`? – catch23 Feb 22 '21 at 22:02
  • 1
    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 https://modbus.org/docs/Modbus_Messaging_Implementation_Guide_V1_0b.pdf – Kevin Herron Feb 22 '21 at 23:06
  • 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? – catch23 Feb 25 '21 at 10:17
  • 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. – Kevin Herron Feb 25 '21 at 13:47
  • tried this approach with real device and it failed. Updated the question with stacktrace. – catch23 Feb 25 '21 at 21:35
  • Ah, you have to call connect() first on the master. – Kevin Herron Feb 26 '21 at 02:40
  • surrounded sending request with `connect()` and `disconnect()` with `whenComplete()` it didn't work, but with `get()` works fine. Why did this happen? – catch23 Feb 26 '21 at 06:00
  • `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. – Kevin Herron Feb 26 '21 at 12:55
  • You should also not `disconnect()` until after your response has been received. – Kevin Herron Feb 26 '21 at 12:56
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/229254/discussion-between-catch23-and-kevin-herron). – catch23 Feb 26 '21 at 13:23