Industry Insights 19 min read

How to Connect Java to Industrial Devices: Modbus, JNI, and Serial Port Solutions

This guide explains how Java can communicate with industrial hardware by using the Modbus protocol with jLibModbus, accessing low‑level registers via JNI, and employing serial‑port libraries such as JSerialComm or RXTX, providing step‑by‑step setup, code examples, dependency configuration, and practical considerations for reliable device integration.

Java Architecture Stack
Java Architecture Stack
Java Architecture Stack
How to Connect Java to Industrial Devices: Modbus, JNI, and Serial Port Solutions

Overview

The article demonstrates three practical ways for Java applications to interact with industrial equipment: using the Modbus protocol through the jLibModbus library, calling native code via Java Native Interface (JNI), and performing serial‑port communication with libraries such as JSerialComm or RXTX. Each method includes dependency setup, sample code, and usage tips.

Modbus TCP with jLibModbus

Modbus is a widely adopted protocol for industrial automation. The example uses the Modbus TCP variant to read holding registers from a device at IP 192.168.1.100 (default port 502).

Implementation Steps

Add the jLibModbus dependency to pom.xml:

<dependency>
    <groupId>com.intelligt.modbus</groupId>
    <artifactId>jlibmodbus</artifactId>
    <version>1.2.8.1</version>
</dependency>

Configure a TcpParameters object with the device IP and port.

Create a ModbusMaster via ModbusMasterFactory.createModbusMasterTCP, connect, and read registers using readHoldingRegisters.

Sample Code

import com.intelligt.modbus.jlibmodbus.Modbus;
import com.intelligt.modbus.jlibmodbus.ModbusMaster;
import com.intelligt.modbus.jlibmodbus.ModbusMasterFactory;
import com.intelligt.modbus.jlibmodbus.exception.ModbusIOException;
import com.intelligt.modbus.jlibmodbus.exception.ModbusNumberException;
import com.intelligt.modbus.jlibmodbus.exception.ModbusProtocolException;
import com.intelligt.modbus.jlibmodbus.tcp.TcpParameters;
import java.net.InetAddress;

public class ModbusTcpClient {
    public static void main(String[] args) {
        try {
            TcpParameters tcpParameters = new TcpParameters();
            InetAddress address = InetAddress.getByName("192.168.1.100");
            tcpParameters.setHost(address);
            tcpParameters.setPort(Modbus.TCP_PORT);
            ModbusMaster master = ModbusMasterFactory.createModbusMasterTCP(tcpParameters);
            master.connect();
            int slaveId = 1;
            int startAddress = 0;
            int quantity = 10;
            try {
                int[] registerValues = master.readHoldingRegisters(slaveId, startAddress, quantity);
                System.out.println("Register data:");
                for (int i = 0; i < registerValues.length; i++) {
                    System.out.println("Register[" + (startAddress + i) + "] = " + registerValues[i]);
                }
            } catch (ModbusProtocolException | ModbusNumberException | ModbusIOException e) {
                System.err.println("Modbus read failed: " + e.getMessage());
            }
            master.disconnect();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

The code prints each register value and handles protocol, number, and I/O exceptions. Adjust the IP, port, slave ID, start address, and quantity to match your device.

JNI (Java Native Interface)

JNI enables Java to invoke native C/C++ functions, which is useful when direct hardware access or high‑performance operations are required.

Typical JNI Workflow

Declare a native method in a Java class.

Compile the Java class with javac.

Generate a C header using javah (or javac -h in newer JDKs).

Implement the native method in C/C++.

Compile the native code into a shared library ( .so on Linux/macOS, .dll on Windows).

Load the library in Java with System.loadLibrary and call the native method.

Example

public class RegisterReader {
    public native int readRegister(int address);
    static { System.loadLibrary("register_reader"); }
    public static void main(String[] args) {
        RegisterReader r = new RegisterReader();
        int value = r.readRegister(0x1000);
        System.out.println("Register Value: " + value);
    }
}

Corresponding C header ( RegisterReader.h) and implementation ( RegisterReader.c) define Java_RegisterReader_readRegister, which can perform memory‑mapped register reads or call vendor‑provided driver APIs. The shared library is built with a command such as:

gcc -shared -o libregister_reader.so -I${JAVA_HOME}/include -I${JAVA_HOME}/include/linux RegisterReader.c

Running the Java program with -Djava.library.path=. loads the native library and prints the register value.

Serial‑Port Communication (JSerialComm / RXTX)

When devices expose RS232/RS485 interfaces, Java can communicate via serial‑port libraries. JSerialComm is actively maintained and cross‑platform, while RXTX is older and less reliable.

JSerialComm Example

import com.fazecast.jSerialComm.SerialPort;

public class SerialCommExample {
    public static void main(String[] args) {
        SerialPort[] ports = SerialPort.getCommPorts();
        System.out.println("Available ports:");
        for (int i = 0; i < ports.length; i++) {
            System.out.println(i + ": " + ports[i].getSystemPortName());
        }
        SerialPort serialPort = ports[0]; // choose first port
        serialPort.setBaudRate(9600);
        serialPort.setNumDataBits(8);
        serialPort.setNumStopBits(SerialPort.ONE_STOP_BIT);
        serialPort.setParity(SerialPort.NO_PARITY);
        if (serialPort.openPort()) {
            System.out.println("Port opened: " + serialPort.getSystemPortName());
        } else {
            System.out.println("Failed to open port");
            return;
        }
        try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); }
        String command = "READ_REGISTER"; // device‑specific command
        serialPort.writeBytes(command.getBytes(), command.length());
        byte[] buffer = new byte[1024];
        int numRead = serialPort.readBytes(buffer, buffer.length);
        System.out.println("Read " + numRead + " bytes");
        for (int i = 0; i < numRead; i++) {
            System.out.print((char) buffer[i]);
        }
        System.out.println();
        serialPort.closePort();
        System.out.println("Port closed");
    }
}

The program lists ports, configures communication parameters, sends a placeholder command, reads the response, and closes the port.

RXTX Example (Legacy)

import gnu.io.CommPortIdentifier;
import gnu.io.SerialPort;
import java.io.InputStream;
import java.io.OutputStream;

public class RXTXExample {
    public static void main(String[] args) throws Exception {
        CommPortIdentifier pid = CommPortIdentifier.getPortIdentifier("COM3");
        SerialPort sp = (SerialPort) pid.open("SerialComm", 2000);
        sp.setSerialPortParams(9600, SerialPort.DATABITS_8, SerialPort.STOPBITS_1, SerialPort.PARITY_NONE);
        InputStream in = sp.getInputStream();
        OutputStream out = sp.getOutputStream();
        out.write("READ_REGISTER".getBytes());
        byte[] buf = new byte[1024];
        int len = in.read(buf);
        System.out.println("Read " + len + " bytes");
        for (int i = 0; i < len; i++) System.out.print((char) buf[i]);
        System.out.println();
        sp.close();
    }
}

RXTX requires native binaries and is less convenient than JSerialComm, but the code flow mirrors the JSerialComm example.

Choosing the Right Approach

JNI : Best for ultra‑low latency or when a device driver is only available as native C/C++ code.

Modbus : Ideal for standard industrial equipment that supports Modbus RTU or TCP; provides a uniform way to read/write registers across many devices.

Serial‑Port Libraries (JSerialComm / RXTX) : Suitable for custom or legacy hardware that communicates over RS232/RS485 with proprietary protocols.

Consider the device’s communication protocol, performance requirements, and development resources when selecting a method.

Conclusion

Java can reliably interact with industrial hardware through three complementary techniques: Modbus TCP/RTU via jLibModbus, native calls through JNI for direct register access, and serial‑port communication using JSerialComm or RXTX. Proper dependency configuration, error handling, and protocol‑specific settings are essential for stable integration.

JavajniHardware integrationSerial CommunicationIndustrial IoTModbus
Java Architecture Stack
Written by

Java Architecture Stack

Dedicated to original, practical tech insights—from skill advancement to architecture, front‑end to back‑end, the full‑stack path, with Wei Ge guiding you.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.