I’m working on a home automation project, where I want to use rs485(modbus) for communication between modules and I was wondering, if there is a way to automate the process of adding new devices(slaves) to the bus and addressing them automatically and also writing the new address to it’s flash memory, so when the slave restart it remembers the new ID.
I found this document, and there are some ways to achieve this, but since I’m new to this I’m not really sure how to do it exactly.
I was thinking about something like this as mentioned in the document, but I don’t understand how can I use the unique id obtained from the slave to set it’s slave id.
Liang et. al. [4] present a concept that will enable
reconfiguration of slave IDs when there are ID conflicts.
The system employed slaves with unique identification
numbers in them. In case of devices with conflicting
Modbus IDs, the master requests the conflicting slaves to
send their unique identification number to the master
within a certain pre-set interval like in Ethernet [5]. The
slaves then will attempt to send their ID back after a
random waiting period so that the IDs are received at the
master reliably. In case the IDs are not received by the
master, the same process will be re-initiated several times
until the identification numbers of all the slaves are
received. Once the identification numbers of the slaves are
received successfully by the master, the master will then
send configuration messages to set Modbus ID of each
slave referring to it by its identification number.
Has anyone done something like this before?
Currently I have just a simple code to scan the bus for slave IDs.
MASTER code:
#include <ModbusRTUMaster.h>
const byte dePin = 32; // Driver Enable pin (set to LOW for auto flow control)
const uint16_t REGISTER_ADDRESS = 0; // Register address to read from
const uint16_t NUM_REGISTERS = 1; // Number of registers to read
const byte START_SLAVE_ID = 1; // Start of the slave ID range
const byte END_SLAVE_ID = 247; // End of the slave ID range
ModbusRTUMaster modbus(Serial2, dePin); // Create Modbus master object
byte foundDevices[END_SLAVE_ID];
byte foundCount = 0;
void setup() {
Serial.begin(115200); // Serial monitor for debugging
// Initialize Modbus communication
modbus.begin(38400); // Baud rate for RS-485
// Initialize the RS-485 driver enable pin
pinMode(dePin, OUTPUT);
digitalWrite(dePin, LOW); // Set DE pin low for auto flow control
delay(5000);
scanForDevices();
}
void loop() {
}
void scanForDevices() {
for (byte slaveID = START_SLAVE_ID; slaveID <= END_SLAVE_ID; slaveID++) {
if (slaveID > END_SLAVE_ID) {
Serial.println("Reached maximum scan address. Stopping scan.");
break; // Stop scanning if the max address is reached
}
uint16_t holdingRegisterValue = 0; // Variable to store the register value
// Request the value of holding register from the current slave ID
bool success = modbus.readHoldingRegisters(slaveID, REGISTER_ADDRESS, &holdingRegisterValue, NUM_REGISTERS);
// Check if the request was successful
if (success) {
Serial.print("Device found at Slave ID ");
Serial.println(slaveID);
foundDevices[foundCount++] = slaveID;
} else {
// Optionally check for timeout or exception response
if (modbus.getTimeoutFlag()) {
Serial.print("No device at Slave ID ");
Serial.println(slaveID);
modbus.clearTimeoutFlag();
} else if (modbus.getExceptionResponse() != 0) {
Serial.print("Error at Slave ID ");
Serial.print(slaveID);
Serial.print(": Exception Response ");
Serial.println(modbus.getExceptionResponse());
modbus.clearExceptionResponse();
}
}
delay(100); // Short delay to avoid flooding the bus
}
printFoundDevices();
}
void printFoundDevices() {
Serial.println("Found Devices:");
for (byte i = 0; i < foundCount; i++) {
Serial.print("Slave ID ");
Serial.println(foundDevices[i]);
}
}
SLAVE code:
#include <SoftwareSerial.h>
#include <ModbusRTU.h>
#include <EEPROM.h> // Include EEPROM library
#define REGN 0 // Register for general data
#define ID_CONFIG_REGISTER 1 // Register for configuring the slave ID
#define DEFAULT_SLAVE_ID 247 // Default slave ID
#define EEPROM_SIZE 12 // Size of EEPROM, adjust as needed
#define SAVED_SLAVE_ADDRESS 0 // Address in EEPROM to store the slave ID
#define RS485_TX_PIN 5 // GPIO5
#define RS485_RX_PIN 4 // GPIO4
SoftwareSerial swSerial(RS485_RX_PIN, RS485_TX_PIN); // RX, TX
ModbusRTU mb;
byte currentSlaveID;
void setup() {
swSerial.begin(38400, SWSERIAL_8N1);
mb.begin(&swSerial);
EEPROM.begin(EEPROM_SIZE);
//currentSlaveID = EEPROM.read(SAVED_SLAVE_ADDRESS);
// Validate the currentSlaveID, set to default if invalid
if (currentSlaveID < 1 || currentSlaveID > 247) {
currentSlaveID = DEFAULT_SLAVE_ID;
}
mb.slave(currentSlaveID);
mb.addHreg(REGN);
mb.Hreg(REGN, 100);
mb.addHreg(ID_CONFIG_REGISTER); // Add register for configuring the slave ID
}
void loop() {
mb.task();
yield();
}