Through the troubleshooting of other issues I noticed the following items in the smarmengol Modbus library that makes me concerned RS-485 half duplex modules will not work with the library when using a software serial port (either SoftwareSerial or AltSoftSerial).
Here are the pieces that have drawn my concern:
According to sendTxBuffer, u8serno must be >3 (4) to send via the software serial port
void Modbus::sendTxBuffer()
{
uint8_t i = 0;
// append CRC to message
uint16_t u16crc = calcCRC( u8BufferSize );
au8Buffer[ u8BufferSize ] = u16crc >> 8;
u8BufferSize++;
au8Buffer[ u8BufferSize ] = u16crc & 0x00ff;
u8BufferSize++;
// set RS485 transceiver to transmit mode
if (u8txenpin > 1)
{
switch( u8serno )
{
#if defined(UBRR1H)
case 1:
UCSR1A=UCSR1A |(1 << TXC1);
break;
#endif
#if defined(UBRR2H)
case 2:
UCSR2A=UCSR2A |(1 << TXC2);
break;
#endif
#if defined(UBRR3H)
case 3:
UCSR3A=UCSR3A |(1 << TXC3);
break;
#endif
case 0:
default:
UCSR0A=UCSR0A |(1 << TXC0);
break;
}
digitalWrite( u8txenpin, HIGH );
}
// transfer buffer to serial line
if(u8serno<4)
port->write( au8Buffer, u8BufferSize );
else
softPort->write( au8Buffer, u8BufferSize );
// keep RS485 transceiver in transmit mode as long as sending
if (u8txenpin > 1)
{
switch( u8serno )
{
#if defined(UBRR1H)
case 1:
while (!(UCSR1A & (1 << TXC1)));
break;
#endif
#if defined(UBRR2H)
case 2:
while (!(UCSR2A & (1 << TXC2)));
break;
#endif
#if defined(UBRR3H)
case 3:
while (!(UCSR3A & (1 << TXC3)));
break;
#endif
case 0:
default:
while (!(UCSR0A & (1 << TXC0)));
break;
}
// return RS485 transceiver to receive mode
digitalWrite( u8txenpin, LOW );
}
if(u8serno<4)
while(port->read() >= 0);
else
while(softPort->read() >= 0);
u8BufferSize = 0;
// set time-out for master
u32timeOut = millis();
// increase message counter
u16OutCnt++;
}
/**
* @brief
* Full constructor for a Master/Slave through USB/RS232C/RS485
* It needs a pin for flow control only for RS485 mode
*
* @param u8id node address 0=master, 1..247=slave
* @param u8serno serial port used 0..3
* @param u8txenpin pin for txen RS-485 (=0 means USB/RS232C mode)
* @ingroup setup
* @overload Modbus::Modbus(uint8_t u8id, uint8_t u8serno, uint8_t u8txenpin)
* @overload Modbus::Modbus(uint8_t u8id)
* @overload Modbus::Modbus()
*/
Modbus::Modbus(uint8_t u8id, uint8_t u8serno, uint8_t u8txenpin)
{
init(u8id, u8serno, u8txenpin);
}
Modbus::Modbus(uint8_t u8id, uint8_t u8serno)
{
init(u8id, u8serno, 0);
}
Modbus::Modbus(uint8_t u8id)
{
init(u8id);
}
The three constructor functions call these to init functions:
void Modbus::init(uint8_t u8id, uint8_t u8serno, uint8_t u8txenpin)
{
this->u8id = u8id;
this->u8serno = (u8serno > 3) ? 0 : u8serno;
this->u8txenpin = u8txenpin;
this->u16timeOut = 1000;
}
void Modbus::init(uint8_t u8id)
{
this->u8id = u8id;
this->u8serno = 4;
this->u8txenpin = 0;
this->u16timeOut = 1000;
}
To make u8serno=4, it can only be constructed using Modbus master_slave(ID) but in doing so sets the u8txenpin to 0.
If u8txenpin needs to >1 then the constructor Modbus master_slave(ID, serial_num, 10) needs to be used. But if serial_num >3, it is set to 0 (note the line: this->u8serno = (u8serno > 3) ? 0 : u8serno; ).
Am I over analyzing this and completely off base or are more modifications to the library in my future?