Ejemplo de comunicación ModBus RTU con el ESP32 como slave RS485
/*
* Ejemplo de comunicación ModBus RTU con el ESP32 como esclavo
*
* Descripción: Programa básico para utilizar una placa PCB de ESP32 como unidad remota de entradas
* y salidas digitales y analógicas, utilizando comunicación modbus sobre RTU, con UART y RS485
*
* Autor: Rafa Aranda
* Fecha: 05-02-2026
* Versión: 1.0
*/
// ------------------- Librerías -------------------
#include // ModBus RTU de Alexander Emelianov "andresarmento/modbus-esp8266"
ModbusRTU mb; // Instancia a ModbusRTU
// ------------------- Declaraciones -------------------
#define SLAVE_ID 1 // Identificador del dispositivo
#define MODBUS_BAUD_RATE 9600 // Velocidad de comunicación para ModBus RTU
#define N_COILS 4 // Numero de bobinas
#define N_DISCRETE_INPUTS 4 // Numero de entradas discretas
#define N_INPUT_REGISTERS 2 // Numero de registros de entrada
#define N_HOLDING_REGISTERS 6 // Numero de registros de mantenimiento
/* Tabla ModBus
Bobimas
0 Salida digital R/W
1 Salida digital R/W
2 Salida digital R/W
3 Salida digital R/W
Entradas discretas
0 Entrada digital R
1 Entrada digital R
2 Entrada digital R
3 Entrada digital R
Registros de entrada
0 ADC 0 R
1 ADC 1 R
Registros de mantenimiento
0 PWM 0 R/W
1 PWM 1 R/W
2 Configuración 1 R/W
3 Configuración 2 R/W
4 Configuración 3 R/W
5 Configuración 4 R/W
Esta tabla se ha de modificar según la aplicación, añadiendo o quitando elementos
y definiendo su función especifica.
*/
// ------------------- Pines -------------------
#define RS485_DE_RE 4 // Pin de control a la placa RS485
#define RXD2 16 // Pin Rx de la UART2 a la placa RS485
#define TXD2 17 // Pin Tx de la UART2 a la placa RS485
uint8_t dinPins[4] = {32, 33, 34, 35}; // Entradas digitales
uint8_t doutPins[4] = {25, 26, 27, 14}; // Salidas digitales
uint8_t ainPins[2] = {36, 39}; // Entradas analogicas
uint8_t pwmPins[2] = {18, 19}; // Salidas PWM
// ------------------- Variables -------------------
uint16_t analogIn[2];
uint16_t pwmOut[2];
uint16_t configReg[4];
// ------------------- Funciones prototipo -------------------
void setupHardware(); // Inicializa pines, UART y PWM.
void setupModbus(); // Inicializa la librería Modbus y los registros.
void updateDigitalInputs(); // Actualiza entradas digitales a Modbus.
void updateDigitalOutputs(); // Actualiza salidas digitales desde Modbus.
void updateAnalogInputs(); // Lee ADC y actualiza registros Modbus.
void updatePWMOutputs(); // Escribe PWM desde registros Modbus.
void updateConfigRegisters(); // Maneja los registros de configuración.
// ------------------- Setup -------------------
void setup() {
Serial.begin(115200);
setupHardware();
setupModbus();
}
// ------------------- Loop -------------------
void loop() {
updateDigitalInputs(); // Leemos las entradas digitales y actulizamos en entradas modbus
updateAnalogInputs(); // Leemos las entradas analogicas y actulizamos en registros de entrada modbus
mb.task(); // Atención de Modbus
// PROGAMA DE AUTOMATIZACIÓN DE......
//----------------------------------------------------------------------------------------------------
/* Aquí mi programa de automatización utilizando los datos de las bobinas y registros modbus como
parámetros y consignas para hacer mi proyecto..... */
/* Si queremos tener un programa estructurado es mejor poner las accines necesarias dentro de funciones "update..."
y mantener el programa principal limpio de codigo, dejando unicamente las llamadas a las funciones */
//----------------------------------------------------------------------------------------------------
updateDigitalOutputs(); // Escribimos las salidas digitales de estado de bobinas modbus
updatePWMOutputs(); // Escribimos las salidas analógicas PWM de estado de registros de mantenimiento modbus
updateConfigRegisters();// Actualizamos los registros de configuración de estado de registros de mantenimiento modbus
delay(10); // Modificar para dar estabilidad el sistema (0-10), !no utilizar valores elevados¡
}
// ==================== FUNCIONES ====================
// -------- Inicialización hardware ----------
void setupHardware() {
// RS485 UART
Serial2.begin(MODBUS_BAUD_RATE, SERIAL_8N1, RXD2, TXD2);
// Pines digitales
for (int i = 0; i < 4; i++) {
pinMode(dinPins[i], INPUT);
pinMode(doutPins[i], OUTPUT);
}
// PWM
for (int i = 0; i < 2; i++) {
ledcSetup(i, 2000, 10); // canal, frecuencia, resolución bits
ledcAttachPin(pwmPins[i], i);
}
}
// -------- Inicialización Modbus ----------
void setupModbus() {
mb.begin(&Serial2, RS485_DE_RE); // Iniciamos la UART2
mb.slave(SLAVE_ID); // Iniciamos la instancia ModBus con el ID del slave
// Entradas digitales (Discrete Inputs)
for (int i = 0; i < N_DISCRETE_INPUTS; i++) mb.addIsts(i);
// Salidas digitales (Coils)
for (int i = 0; i < N_COILS; i++) mb.addCoil(i);
// Entradas analógicas (Input Registers)
for (int i = 0; i < N_INPUT_REGISTERS; i++) mb.addIreg(i);
// Salidas PWM y registros config (Holding Registers)
for (int i = 0; i < N_HOLDING_REGISTERS; i++) mb.addHreg(i); // 0,1 = PWM, 2–5 = Configuración
}
// -------- Actualizar entradas digitales ----------
void updateDigitalInputs() {
for (int i = 0; i < 4; i++) {
mb.Ists(i, digitalRead(dinPins[i]));
}
}
// -------- Actualizar salidas digitales ----------
void updateDigitalOutputs() {
for (int i = 0; i < 4; i++) {
digitalWrite(doutPins[i], mb.Coil(i));
}
}
// -------- Leer entradas analógicas ----------
void updateAnalogInputs() {
for (int i = 0; i < 2; i++) {
analogIn[i] = analogRead(ainPins[i]);
mb.Ireg(i, analogIn[i]);
}
}
// -------- Actualizar salidas PWM ----------
void updatePWMOutputs() {
for (int i = 0; i < 2; i++) {
pwmOut[i] = constrain(mb.Hreg(i), 0, 1023); // Limitar el valor entre 0 y 1023 (10bits)
ledcWrite(i, pwmOut[i]);
}
}
/* ledcWrite ESP32 equivale a analorWitre de arduino
const int ledChannel = 0; // Canal PWM, puede ser de 0 a 15
const int ledPin = 5; // Pin al que está conectado el dispositivo
const int frequency = 5000; // Frecuencia en Hz
const int resolution = 8; // Resolución en bits (de 1 a 15)
ledcSetup(ledChannel, frequency, resolution);
int dutyCycle = 128; // Valor del ciclo de trabajo (0 a 255)
ledcWrite(ledChannel, dutyCycle);
*/
// -------- Actualizar registros de configuración ----------
void updateConfigRegisters() {
for (int i = 0; i < 4; i++) {
configReg[i] = mb.Hreg(i + 2); // 40003–40006
}
}