Hola a todos.
Estoy intentando comunicar mi ESP32(maestro) con una bomba para el control de riego (esclavo). Para la comunicación estoy usando el integrado Max485.
La bomba es el modelo ITC DOSTEC-AC, cuyas características son:
- Bus: RS485
- Comunicación: half-duplex L(H), H(B), y GND.
- Bits por segundo: 9600
- Bits de datos: 8
- Paridad: Ninguno
- Bits de parada: 1
- Time out carácter: 20 mSeg.
- Time out fin mensaje: 100 mSeg
MANUAL MODBUS: https://www.itc.es/wp-content/uploads/ITC_DostecAC_Modbus-ES.pdf
Mi intención es escribir una consigna de caudal según como dice el manual Modbus de la bomba, para ello debe escribirse el valor de la consigna de caudal en los registros de memoria según la siguiente imagen.
Por ejemplo, para un caudal de 90 l/h, la trama Modbus sería:
Petición (ESP32)
-Id. esclavo = 0x01
-Función = 0x10
-Reg. ADDR HI = 0x12
-Reg. ADDR LO = 0x02
-Num Reg HI = 0x00
-Num Reg LO = 0x02
-Bits count = 0x04
-Value 1 HI = 0x00
-Value 1 LO = 0x00
-Value 2 HI = 0x23
-Value 2LO = 0x28
-CRC HI = 3A
-CRC LO = 44
Aclaración: Consigna de caudal = 0x00002328 = 90.000 * 10^-2 = 90 l/h
Para la creación del código he utilizado la librería ModbusMaster, pero cuando le pido a la placa de ESP32 que comunique con la bomba, es imposible. La bomba no responde.
El código.ino es el siguiente:
#include <ModbusMaster.h>
/*------------------------------------------------VARIABLES GLOBALES---------------------------------------------*/
uint8_t StatusPin_DE_RE = 14; // Habilita y desabilida la transmisión del MAX485 IC.
uint8_t id_slave = 1; // Direccion del esclavo remoto.
uint16_t MAR = 4684; // Memory Address Register (MAR).
uint16_t NMR = 2; // Number of Memory Records (NMR).
uint16_t ModbusTimeout = 1000; // Modbus timeout [milliseconds].
uint32_t u32_ConsignaCaudal = 9000; // Consigna de Caudal = 90 l/h.
/*A continuación definiremos las varialbles para la configuración del puerto Serial para el ESP32 Dev Module.
Por defecto, en el archivo HardwareSerial.cpp se utiliza: HardwareSerial Serial(0);
Los pines rxPin & txPin tienen que ser menor que 0 para asignar correctamente los pines Rx y Tx en el ESP32.*/
unsigned long baudios = 9600; // Tasa de bits/segundo.
uint32_t config = SERIAL_8N1; // Serial -> 8 bits de datos, 1 bits de parada, 0 bits de paridad.
int8_t rxPin= -1;
int8_t txPin= -1;
bool invert=false;
unsigned long timeout_ms = 20000UL;
uint8_t rxfifo_full_thrhd = 112;
// Creación de una instancia del objeto ModbusMaster
ModbusMaster node;
/*------------------------------------------------FUNCIONES--------------------------------------------------------*/
// Función que habilita los pines RE y DE para activar el modo de transmisión de datos.
void preTransmission(){ digitalWrite(StatusPin_DE_RE, 1); }
// Función que deshabilita los pines RE y DE para activar el modo de recepción de datos.
void postTransmission() { digitalWrite(StatusPin_DE_RE, 0); }
// Funcion que convierte un numero uint32_t en dos numeros uint16_t
// Se utiliza ya que para añadir la variable u32_ConsignaCaudal al buffer de transmisión implementado en la clase
// <ModbusMaster.h> es necesario descomponerlo en dos registros/numeros de 16 bits.
// uint8_t setTransmitBuffer(uint8_t, uint16_t);
void u32_to_u16(const uint32_t u32)
{
uint16_t u16_ArrayTmp[2]; // Array auxiliar para setear el buffer de transmision
Serial.print(" Consigna de caudal = ");
Serial.print(u32/100);
Serial.println(" l/h");
delay(2000);
Serial.println("Iniciando descomposicion uint32_t --> uint16_t...");
u16_ArrayTmp[0] = (u32 & 0xffff0000) >> 16;
u16_ArrayTmp[1] = (u32 & 0x0000ffff);
delay(100);
for(int i=0; i<2; i++)
{
Serial.print("u16_ArrayTmp[");
Serial.print(i);
Serial.print("] = ");
Serial.println(u16_ArrayTmp[i]);
delay(1000);
}
// Se añanden los valores de 8 bits de la consigna de caudal al buffer de transmision y se comprueba que ha sido correcto.
if(node.setTransmitBuffer(0, u16_ArrayTmp[0]) == node.ku8MBSuccess){ Serial.println("MSB adjuntado correctamente al Buffer de transmision.");}
delay(1000);
if(node.setTransmitBuffer(1, u16_ArrayTmp[1]) == node.ku8MBSuccess){ Serial.println("LSB adjuntado correctamente al Buffer de transmision.");}
else{Serial.println("Error al añadir los datos al Buffer de transmision!!! :(");}
}
// Funcion de detección de errores. Devuelve que tipo de erros es.
bool getResultMsg(uint8_t result)
{
String tmpstr2;
switch (result) {
case node.ku8MBSuccess:
return true;
break;
case node.ku8MBIllegalFunction:
tmpstr2 = "Illegal Function";
break;
case node.ku8MBIllegalDataAddress:
tmpstr2 = "Illegal Data Address";
break;
case node.ku8MBIllegalDataValue:
tmpstr2 = "Illegal Data Value";
break;
case node.ku8MBSlaveDeviceFailure:
tmpstr2 = "Slave Device Failure";
break;
case node.ku8MBInvalidSlaveID:
tmpstr2 = "Invalid Slave ID";
break;
case node.ku8MBInvalidFunction:
tmpstr2 = "Invalid Function";
break;
case node.ku8MBResponseTimedOut:
tmpstr2 = "Response Timed Out";
break;
case node.ku8MBInvalidCRC:
tmpstr2 = "Invalid CRC";
break;
default:
tmpstr2 = "Unknown error: " + String(result);
break;
}
Serial.println(tmpstr2);
return false;
}
/*------------------------------------------------PARAMETROS DE CONFIGURACIÓN----------------------------------------------------------------*/
void setup()
{
// Configuración de pines.
pinMode(StatusPin_DE_RE, OUTPUT);
delay(50);
// Iniciar en modo de transmision.
digitalWrite(StatusPin_DE_RE, 1);
delay(50);
// Inicializa los pines Rx y Tx para que puedan ser usados como puerto serial.
// void HardwareSerial::begin(unsigned long baud, uint32_t config, int8_t rxPin, int8_t txPin, bool invert, unsigned long timeout_ms)
Serial.begin(baudios, config , rxPin, txPin, invert, timeout_ms);
delay(50);
// Se limpian los Buffer de Respuesta y transmision.
node.clearResponseBuffer();
node.clearTransmitBuffer();
delay(100);
// Asigna la ID de esclavo Modbus y el puerto serie.
// void ModbusMaster::begin(uint8_t slave, Stream &serial)
node.begin(id_slave, Serial);
delay(100);
// Las devoluciones de llamada nos permiten configurar correctamente el transceptor RS485
node.postTransmission(postTransmission);
node.preTransmission(preTransmission);
}
/*------------------------------------------------FUNCIÓN LOOP--------------------------------------------------------------------*/
void loop()
{
uint32_t result;
u32_to_u16(u32_ConsignaCaudal);
delay(500);
result = node.writeMultipleRegisters(MAR, NMR);
delay(500);
// Si la petición ha sido correcta y no ha fallado tendremos un buffer con los
// registros, en el primero esta la parte alta del valor y en el segundo la baja.
if( result == node.ku8MBSuccess)
{
Serial.println("Peticion correcta.");
}
else
{
Serial.println("Error en la orden de writeMultipleRegisters!!! ");
delay(1000);
Serial.print("Tipo de Error = ");
Serial.println(result);
delay(1000);
Serial.print(". Se identifica con: ");
getResultMsg(result);
delay(2000);
}
}
He comprobado la conexión del cableado y parece estar todo bien.
Pin Tx (ESP32) --> DI (Max485)
Pin Rx (ESP32)--> R0 (Max485)
Pin 14 (ESP32)--> DE & RE (Max485) (eneable)
A (Max485) --> A (Bomba)
B (Max485) --> B (Bomba)
Agradeceré cualquier sugerencia de que puede estar pasando o que hago mal.
Muchas Gracias!!