.i.e:
you have a Raspberry pi with the static IP address of "192.254.100.101" and some devices are connected to it, then when you send a request to it you will declare the slave id number too, here you requested a data register.
now in the server app, you will return the answer using the sender packet IP address.
Use Wireshark and your local network for understanding.
Codes in java language Using Modbus4J:
public class MasterTest {
public static void main(String[] args) throws Exception {
// String host = "localhost";
String host = "192.168.1.105";
// String host = "YOUR-DEVICE-IP-THAT-IS-CONNECTED-TO-NETWORK";
IpParameters ipParameters = new IpParameters();
ipParameters.setHost(host);
ipParameters.setPort(502);
ipParameters.setEncapsulated(false);
ModbusFactory modbusFactory = new ModbusFactory();
//TcpMaster extends ModbusMaster + Creates an object with ipParameters in it
ModbusMaster master = modbusFactory.createTcpMaster(ipParameters, true);
// ModbusMaster master = modbusFactory.createTcpMaster(ipParameters, false);
master.setTimeout(8000);
master.setRetries(0);
// openConnection method will be called - if KeepAlive be true - openConnection is in TcpMaster
// openConnection method will close other connections
// Socket.connect(ipParameters.getHost(), ipParameters.getPort(),getTimeout()) is in openConnection method
master.init();
try {
// master.send(new WriteCoilRequest(3, 120, true));
// master.send(new WriteCoilRequest(3, 256, false));
master.send(new ReadCoilsRequest(1, 17, 1));
master.send(new ReadCoilsRequest(3, 256, 1));
master.send(new WriteRegisterRequest(2, 15, 5));
ReadHoldingRegistersResponse rresponse = (ReadHoldingRegistersResponse) master
.send(new ReadHoldingRegistersRequest(1, 15, 1));
System.out.println(rresponse);
// System.out.println(master.testSlaveNode(1));
} catch (Exception e) {
e.printStackTrace();
}
master.destroy();
}
}
Slave codes in Java using Modbus4J:
public class SlaveTest {
static Random random = new Random();
static float ir1Value = -100;
//TEST VALUES
private static Integer twoByteIntUnsignedSwapped = 29187; //Register 16
private static Integer twoByteIntSignedSwapped = -257; //Register 17
private static Long fourByteIntUnsignedSwappedSwapped = 16777216L; //Register 18
private static Long fourByteIntSignedSwappedSwapped = (long) -16777217; //Register 20
private static Long register22 = 2369850368L; ////Register 22
public static void main(String[] args) {
ModbusFactory modbusFactory = new ModbusFactory();
ModbusSlaveSet slave = modbusFactory.createTcpSlave(false);
slave.addProcessImage(getModscanProcessImage(1));
new Thread(new Runnable() {
@Override
public void run() {
try {
slave.start();
} catch (ModbusInitException e) {
e.printStackTrace();
}
}
}).start();
while (true) {
synchronized (slave) {
try {
slave.wait(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
for (ProcessImage processImage : slave.getProcessImages()) {
try {
updateProcessImage((BasicProcessImage) processImage);
} catch (IllegalDataAddressException e) {
e.printStackTrace();
}
}
}
}
static void updateProcessImage(BasicProcessImage processImage) throws IllegalDataAddressException {
int hr16Value = processImage.getNumeric(RegisterRange.HOLDING_REGISTER, 16, DataType.TWO_BYTE_INT_UNSIGNED_SWAPPED).intValue();
if (hr16Value != twoByteIntUnsignedSwapped) {
throw new RuntimeException("Test failed on TWO_BYTE_INT_UNSIGNED_SWAPPED. Expected " + twoByteIntUnsignedSwapped + " but was: " + hr16Value);
}
short hr17Value = processImage.getNumeric(RegisterRange.HOLDING_REGISTER, 17, DataType.TWO_BYTE_INT_SIGNED_SWAPPED).shortValue();
if (hr17Value != twoByteIntSignedSwapped) {
throw new RuntimeException("Test failed on TWO_BYTE_INT_SIGNED_SWAPPED. Expected " + twoByteIntSignedSwapped + " but was: " + hr17Value);
}
long hr18Value = processImage.getNumeric(RegisterRange.HOLDING_REGISTER, 18, DataType.FOUR_BYTE_INT_UNSIGNED_SWAPPED_SWAPPED).longValue();
if (hr18Value != fourByteIntUnsignedSwappedSwapped) {
throw new RuntimeException("Test failed on FOUR_BYTE_INT_UNSIGNED_SWAPPED_INVERTED. Expected " + fourByteIntUnsignedSwappedSwapped + " but was: " + hr18Value);
}
int hr20Value = processImage.getNumeric(RegisterRange.HOLDING_REGISTER, 20, DataType.FOUR_BYTE_INT_SIGNED_SWAPPED_SWAPPED).intValue();
if (hr20Value != fourByteIntSignedSwappedSwapped) {
throw new RuntimeException("Test failed on FOUR_BYTE_INT_SIGNED_SWAPPED_INVERTED. Expected " + fourByteIntSignedSwappedSwapped + " but was: " + hr20Value);
}
long hr22Value = processImage.getNumeric(RegisterRange.HOLDING_REGISTER, 22, DataType.FOUR_BYTE_INT_UNSIGNED_SWAPPED_SWAPPED).longValue();
if (hr22Value != register22) {
throw new RuntimeException("Test failed on FOUR_BYTE_INT_UNSIGNED_SWAPPED_INVERTED. Expected " + register22 + " but was: " + hr22Value);
}
}
static class BasicProcessImageListener implements ProcessImageListener {
@Override
public void coilWrite(int offset, boolean oldValue, boolean newValue) {
System.out.println("Coil at " + offset + " was set from " + oldValue + " to " + newValue);
}
@Override
public void holdingRegisterWrite(int offset, short oldValue, short newValue) {
// Add a small delay to the processing.
// try {
// Thread.sleep(500);
// }
// catch (InterruptedException e) {
// // no op
// }
System.out.println("HR at " + offset + " was set from " + oldValue + " to " + newValue);
}
}
static BasicProcessImage getModscanProcessImage(int slaveId) {
BasicProcessImage processImage = new BasicProcessImage(slaveId);
//processImage.setAllowInvalidAddress(true);
processImage.setInvalidAddressValue(Short.MIN_VALUE);
//Register 16 Holds 1 and the data is transmitted as 0b00000001 00000000 which is 256
//processImage.setNumeric(RegisterRange.HOLDING_REGISTER, 16, DataType.TWO_BYTE_INT_UNSIGNED_SWAPPED, new Integer(256));
processImage.setNumeric(RegisterRange.HOLDING_REGISTER, 16, DataType.TWO_BYTE_INT_UNSIGNED_SWAPPED, 29187);
//Registery 16 Holds -2 and the data is transmitted as 0b1111110 11111111 which is -257
processImage.setNumeric(RegisterRange.HOLDING_REGISTER, 17, DataType.TWO_BYTE_INT_SIGNED_SWAPPED, -257);
//Register 18 Holds 1 and the data is transmitted as 0b00000001 00000000 00000000 00000000 which is 16777216
processImage.setNumeric(RegisterRange.HOLDING_REGISTER, 18, DataType.FOUR_BYTE_INT_UNSIGNED_SWAPPED_SWAPPED, 16777216L);
//Register 20 Holds -2 and the data is transmitted as 0b1111110 11111111 1111111 11111111 which is -16777217
processImage.setNumeric(RegisterRange.HOLDING_REGISTER, 20, DataType.FOUR_BYTE_INT_SIGNED_SWAPPED_SWAPPED, (long) -16777217);
//Register 22 Holds 803213 and the data is transmitted as 0b10001101 01000001 00001100 00000000 which is 2369850368
processImage.setNumeric(RegisterRange.HOLDING_REGISTER, 22, DataType.FOUR_BYTE_INT_UNSIGNED_SWAPPED_SWAPPED, 2369850368L);
processImage.setExceptionStatus((byte) 151);
// Add an image listener.
processImage.addListener(new BasicProcessImageListener());
return processImage;
}
}
Slave codes for Wemos D1 (device) C language and Arduino IDE:
#include <ESP8266WiFi.h>
const char* ssid = "YOUR-WIFI-NAME";
const char* password = "YOUR-PASS";
int ModbusTCP_port = 502;
//////// Required for Modbus TCP / IP /// Requerido para Modbus TCP/IP /////////
#define maxInputRegister 20
#define maxHoldingRegister 20
#define MB_FC_NONE 0
#define MB_FC_READ_REGISTERS 3 //implemented
#define MB_FC_WRITE_REGISTER 6 //implemented
#define MB_FC_WRITE_MULTIPLE_REGISTERS 16 //implemented
//
// MODBUS Error Codes
//
#define MB_EC_NONE 0
#define MB_EC_ILLEGAL_FUNCTION 1
#define MB_EC_ILLEGAL_DATA_ADDRESS 2
#define MB_EC_ILLEGAL_DATA_VALUE 3
#define MB_EC_SLAVE_DEVICE_FAILURE 4
//
// MODBUS MBAP offsets
//
#define MB_TCP_TID 0
#define MB_TCP_PID 2
#define MB_TCP_LEN 4
#define MB_TCP_UID 6
#define MB_TCP_FUNC 7
#define MB_TCP_REGISTER_START 8
#define MB_TCP_REGISTER_NUMBER 10
byte ByteArray[260];
unsigned int MBHoldingRegister[maxHoldingRegister];
//////////////////////////////////////////////////////////////////////////
WiFiServer MBServer(ModbusTCP_port);
void setup() {
pinMode(14, OUTPUT);
Serial.begin(9600);
delay(100);
WiFi.begin(ssid, password);
delay(100);
Serial.println(".");
while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); }
MBServer.begin();
Serial.println("Connected ");
Serial.print("ESP8266 Slave Modbus TCP/IP ");
Serial.print(WiFi.localIP()); Serial.print(":"); Serial.println(String(ModbusTCP_port));
Serial.println("Modbus TCP/IP Online");
}
void loop() {
// Check if a client has connected // Modbus TCP/IP
WiFiClient client = MBServer.available();
if (!client) { return; }
boolean flagClientConnected = 0;
byte byteFN = MB_FC_NONE;
int Start;
int WordDataLength;
int ByteDataLength;
int MessageLength;
// Modbus TCP/IP
while (client.connected()) {
if(client.available()) {
flagClientConnected = 1;
int i = 0;
while(client.available()) {
ByteArray[i] = client.read();
i++;
}
client.flush();
///// code here --- codigo aqui
///////// Holding Register [0] A [9] = 10 Holding Registers Escritura
///////// Holding Register [0] A [9] = 10 Holding Registers Writing
MBHoldingRegister[0] = random(0,12);
MBHoldingRegister[1] = random(0,12);
MBHoldingRegister[2] = random(0,12);
MBHoldingRegister[3] = random(0,12);
MBHoldingRegister[4] = random(0,12);
MBHoldingRegister[5] = random(0,12);
MBHoldingRegister[6] = random(0,12);
MBHoldingRegister[7] = random(0,12);
MBHoldingRegister[8] = random(0,12);
MBHoldingRegister[9] = random(0,12);
///////// Holding Register [10] A [19] = 10 Holding Registers Lectura
///// Holding Register [10] A [19] = 10 Holding Registers Reading
int Temporal[10];
Temporal[0] = MBHoldingRegister[10];
Temporal[1] = MBHoldingRegister[11];
Temporal[2] = MBHoldingRegister[12];
Temporal[3] = MBHoldingRegister[13];
Temporal[4] = MBHoldingRegister[14];
Temporal[5] = MBHoldingRegister[15];
Temporal[6] = MBHoldingRegister[16];
Temporal[7] = MBHoldingRegister[17];
Temporal[8] = MBHoldingRegister[18];
Temporal[9] = MBHoldingRegister[19];
/// Enable Output 14
digitalWrite(14, MBHoldingRegister[14] );
//// debug
for (int i = 0; i < 10; i++) {
Serial.print("[");
Serial.print(i);
Serial.print("] ");
Serial.print(Temporal[i]);
}
Serial.println("");
//// end code - fin
//// routine Modbus TCP
byteFN = ByteArray[MB_TCP_FUNC];
Start = word(ByteArray[MB_TCP_REGISTER_START],ByteArray[MB_TCP_REGISTER_START+1]);
WordDataLength = word(ByteArray[MB_TCP_REGISTER_NUMBER],ByteArray[MB_TCP_REGISTER_NUMBER+1]);
}
// Handle request
switch(byteFN) {
case MB_FC_NONE:
break;
case MB_FC_READ_REGISTERS: // 03 Read Holding Registers
ByteDataLength = WordDataLength * 2;
ByteArray[5] = ByteDataLength + 3; //Number of bytes after this one.
ByteArray[8] = ByteDataLength; //Number of bytes after this one (or number of bytes of data).
for(int i = 0; i < WordDataLength; i++) {
ByteArray[ 9 + i * 2] = highByte(MBHoldingRegister[Start + i]);
ByteArray[10 + i * 2] = lowByte(MBHoldingRegister[Start + i]);
}
MessageLength = ByteDataLength + 9;
client.write((const uint8_t *)ByteArray,MessageLength);
byteFN = MB_FC_NONE;
break;
case MB_FC_WRITE_REGISTER: // 06 Write Holding Register
MBHoldingRegister[Start] = word(ByteArray[MB_TCP_REGISTER_NUMBER],ByteArray[MB_TCP_REGISTER_NUMBER+1]);
ByteArray[5] = 6; //Number of bytes after this one.
MessageLength = 12;
client.write((const uint8_t *)ByteArray,MessageLength);
byteFN = MB_FC_NONE;
break;
case MB_FC_WRITE_MULTIPLE_REGISTERS: //16 Write Holding Registers
ByteDataLength = WordDataLength * 2;
ByteArray[5] = ByteDataLength + 3; //Number of bytes after this one.
for(int i = 0; i < WordDataLength; i++) {
MBHoldingRegister[Start + i] = word(ByteArray[ 13 + i * 2],ByteArray[14 + i * 2]);
}
MessageLength = 12;
client.write((const uint8_t *)ByteArray,MessageLength);
byteFN = MB_FC_NONE;
break;
}
}
}
Client Code in python with pyModbusTCP:
#!/usr/bin/env python
from pyModbusTCP.client import ModbusClient
client = ModbusClient(host="192.168.1.105", port=502, auto_open=True, auto_close=True, timeout=10)
data2 = client.write_single_register(14, 1) # FUNCTIE 06 - (Write register) (register=14, data=1)
data = client.read_holding_registers(14, 1) # FUNCTIE 03 - Read register (enkele register) (register=14, length=1)
print ("Data register: ", data[0])
client.close()
Modbus Protocol Specification