So I have been having some issues with ArduinoModbus running on a Mega 2506. I think I may have solved it , but i think it might be worthwhile posting this to help anyone else who might be having similar problems.
My project employs a Mega 2560 as a base unit which communicates using ArduinoModbbus TCP/IP protocols over my LAN with three WiFi remote devices (MKR 1010s). It all seemed to work most of the time but not infrequently the Mega would fail to establish a connection with one, some or all three remote devices. Programmatic mayhem would ensue. I thik I fixed it by changing how I connect to the remote devices.
Here's the old code for polling the three remote devices:
void pollWiFi( int device)
{
static int numFails[numDevices] = {}; //sets all values to 0? numDevices set in lanConfig.h file
// assemble full IP addr
lanIPx = Ethernet.localIP();
// inject current device's fourth byte
lanIPx[3] = lanIPreg[device];
//
if(Verbose)
{
Serial.print(F("Polling "));
Serial.print(deviceNames[device]);
if(lanIPreg[device] <= 0)
{
Serial.print(F("... "));
if(lanIPreg[device] == -1) Serial.println(F("no Device registered."));
if(lanIPreg[device] == -2)
{
Serial.print(F("native sensor... "));
Serial.println(mssgDone); // native sensor
}
if(lanIPreg[device] == -3) Serial.println(F(" Device has been lost!"));
if(lanIPreg[device] == 0) Serial.println(F("not included in Poll."));
//
return;
}
Serial.print(F(" at "));
Serial.print(lanIPx);
Serial.print(F("... "));
}
// poll device
if(lanIPreg[device] > 1) // lanIPreg[1] == Sentinel IP
{
hungComs = true;
// Open connection to device
if (!RemoteDevice.begin(lanIPx)) //*****This is the line that makes the connection*******
{
numFails[device]++;
//
if(Verbose)
{
// finish verbose print that was already started
Serial.print(mssgFailed);
Serial.print(F(" ("));
Serial.print(numFails[device]);
Serial.println(F(")"));
}
else
{
// print entire poll fail
Serial.print(F("Polling "));
Serial.print(deviceNames[device]);
Serial.print(F(" at "));
Serial.print(lanIPx);
Serial.print(F("... "));
Serial.print(mssgFailed);
Serial.print(F(" ("));
Serial.print(numFails[device]);
Serial.println(F(")"));
}
// log the fail
DailyLogHeader(__LINE__);
DailyLog.print(F("Polling "));
DailyLog.print(deviceNames[device]);
DailyLog.print(F(" at "));
DailyLog.print(lanIPx);
DailyLog.print(F("... "));
DailyLog.print(mssgFailed);
DailyLog.print(F(" ("));
DailyLog.print(numFails[device]);
DailyLog.println(F(")"));
DailyLog.flush();
//
hungComs = false;
}
else // connection is made
{
if(getRemoteData(device))
{
numFails[device] = 0;
//
if(Verbose) Serial.println(mssgDone);
}
else
{
numFails[device]++;
// RemoteDevice.stop();
//
if(Verbose)
{
Serial.print(mssgFailed);
Serial.print(F(" ("));
Serial.print(numFails[device]);
Serial.println(F(")"));
}
else
{
Serial.print(deviceNames[device]);
Serial.print(F(" at "));
Serial.print(lanIPx);
Serial.print(F("... "));
Serial.print(F("getRemoteData() returned false."));
Serial.print(F(" ("));
Serial.print(numFails[device]);
Serial.println(F(")"));
}
//
DailyLogHeader(__LINE__);
DailyLog.print(deviceNames[device]);
DailyLog.print(F(" at "));
DailyLog.print(lanIPx);
DailyLog.print(F("... "));
DailyLog.print(F("getRemoteData() returned false."));
DailyLog.print(F(" ("));
DailyLog.print(numFails[device]);
DailyLog.println(F(")"));
DailyLog.flush();
}
}
//
if(numFails[device] > 15)
{
if(!gridAlarm) // this means power is still ON, so we've lost the device
{
// device lost
lanIPreg[device] = -3;
//
bitClear(deviceReg, device); // not registered
bitClear(guiReg, device + 8); // skip
bitSet(guiReg, device + 14); // lost
bitSet(guiReg, gbr_printAck); // one shot print for search
deviceCount = countDevices();
lostDevice = device;
//
saveLanIPreg();
// trigger email send
emailSent = false;
//
Serial.print(F("Device Lost! -> "));
Serial.println(deviceNames[device]);
Serial.println(F("Deregistering this device."));
//
DailyLogHeader(__LINE__);
DailyLog.print(F("Device LOST! -> "));
DailyLog.println(deviceNames[device]);
DailyLog.println(F("Deregistering this device."));
}
else
{
Serial.println(F("Power grid offline. numFails reset to zero."));
DailyLogHeader(__LINE__);
DailyLog.println(F("Power grid offline. numFails reset to zero."));
}
//
numFails[device] = 0; // power if OFF to the sensor, just reset and keep polling
}
// close the connection
RemoteDevice.stop();
hungComs = false;
//
DailyLog.flush();
}
else if(Verbose) Serial.println(mssgDone);
}
and the new:
void pollWiFi( int device)
{
static int numFails[numDevices] = {}; //sets all values to 0? numDevices set in lanConfig.h file
// assemble full IP addr
lanIPx = Ethernet.localIP();
// inject current device's fourth byte
lanIPx[3] = lanIPreg[device];
//
if(Verbose)
{
Serial.print(F("Polling "));
Serial.print(deviceNames[device]);
if(lanIPreg[device] <= 0)
{
Serial.print(F("... "));
if(lanIPreg[device] == -1) Serial.println(F("no Device registered."));
if(lanIPreg[device] == -2)
{
Serial.print(F("native sensor... "));
Serial.println(mssgDone); // native sensor
}
if(lanIPreg[device] == -3) Serial.println(F(" Device has been lost!"));
if(lanIPreg[device] == 0) Serial.println(F("not included in Poll."));
//
return;
}
Serial.print(F(" at "));
Serial.print(lanIPx);
Serial.print(F("... "));
}
// poll device
if(lanIPreg[device] > 1) // lanIPreg[1] == Sentinel IP
{
hungComs = true; //watchdog boolean
// Connect to device ***** the new code starts here*****
byte failsafe = 0;
do
{
if(failsafe > 10) break;
RemoteDevice.begin(lanIPx);
failsafe++;
// delay(10);
}while(!RemoteDevice.connected());
// if (!RemoteDevice.begin(lanIPx)) // this is the old line!
if(!RemoteDevice.connected()) // connection failed
{
numFails[device]++;
//
if(Verbose)
{
// finish verbose print that was already started
Serial.print(mssgFailed);
Serial.print(F(" ("));
Serial.print(numFails[device]);
Serial.println(F(")"));
}
else
{
// print entire poll fail
Serial.print(F("Polling "));
Serial.print(deviceNames[device]);
Serial.print(F(" at "));
Serial.print(lanIPx);
Serial.print(F("... "));
Serial.print(mssgFailed);
Serial.print(F(" ("));
Serial.print(numFails[device]);
Serial.println(F(")"));
}
// log the fail
DailyLogHeader(__LINE__);
DailyLog.print(F("Polling "));
DailyLog.print(deviceNames[device]);
DailyLog.print(F(" at "));
DailyLog.print(lanIPx);
DailyLog.print(F("... "));
DailyLog.print(mssgFailed);
DailyLog.print(F(" ("));
DailyLog.print(numFails[device]);
DailyLog.println(F(")"));
DailyLog.flush();
//
hungComs = false;
}
else // connection is made
{
if(getRemoteData(device))
{
numFails[device] = 0;
//
if(Verbose)
{
if(failsafe > 1)
{
Serial.print(mssgDone);
Serial.print(F(" ("));
Serial.print(failsafe);
Serial.println(F(")"));
//
DailyLogHeader(__LINE__);
DailyLog.print(F("failsafe: "));
DailyLog.println(failsafe);
DailyLog.flush();
}
else Serial.println(mssgDone);
}
}
else
{
numFails[device]++;
// RemoteDevice.stop();
//
if(Verbose)
{
Serial.print(mssgFailed);
Serial.print(F(" ("));
Serial.print(numFails[device]);
Serial.println(F(")"));
}
else
{
Serial.print(deviceNames[device]);
Serial.print(F(" at "));
Serial.print(lanIPx);
Serial.print(F("... "));
Serial.print(F("getRemoteData() returned false."));
Serial.print(F(" ("));
Serial.print(numFails[device]);
Serial.println(F(")"));
}
//
DailyLogHeader(__LINE__);
DailyLog.print(deviceNames[device]);
DailyLog.print(F(" at "));
DailyLog.print(lanIPx);
DailyLog.print(F("... "));
DailyLog.print(F("getRemoteData() returned false."));
DailyLog.print(F(" ("));
DailyLog.print(numFails[device]);
DailyLog.println(F(")"));
DailyLog.flush();
}
}
// device lost???
if(numFails[device] > 15)
{
if(!gridAlarm) // this means power is still ON, so we've lost the device
{
// device lost
lanIPreg[device] = -3;
//
bitClear(deviceReg, device); // not registered
bitClear(guiReg, device + 8); // skip
bitSet(guiReg, device + 14); // lost
bitSet(guiReg, gbr_printAck); // one shot print for search
deviceCount = countDevices();
lostDevice = device;
//
saveLanIPreg();
// trigger email send
emailSent = false;
//
Serial.print(F("Device Lost! -> "));
Serial.println(deviceNames[device]);
Serial.println(F("Deregistering this device."));
//
DailyLogHeader(__LINE__);
DailyLog.print(F("Device LOST! -> "));
DailyLog.println(deviceNames[device]);
DailyLog.println(F("Deregistering this device."));
}
else
{
Serial.println(F("Power grid offline. numFails reset to zero."));
DailyLogHeader(__LINE__);
DailyLog.println(F("Power grid offline. numFails reset to zero."));
}
//
numFails[device] = 0; // power if OFF to the sensor, just reset and keep polling
}
// close the connection
RemoteDevice.stop();
hungComs = false;
//
DailyLog.flush();
}
else if(Verbose) Serial.println(mssgDone);
}
Here's a screenshot of the GUI
It took me a loooong time to arrive at this point, but the new code seems to be working flawlessly for a coupe days now. The remotedevices are sequentially polled every 10 seconds.
I really don't know why it was failiing in the first place, most of the time it didn't. But it did fail often enough for me to regard it as completely unreliable for its purpose.
Some of the confounding issues were
- no knowledge of TCP/IP protocols
- no idea what goes on within the router
- while the Arduinomodbus documentation states that the library will work with any board that is compatible with the EtherNet2 Rev 3 shield, I always get a compiler warning that the library may not run properly on the Mega 2560
My apologies if I posted too much code; I thought the context might be helpful...
Anyway, thanks for reading and I hope this helps someone.
Cheers
Stephen
Here's a copy of the compiler error:
BUsing cached library dependencies for file: C:\Users\grinn\Documents\Arduino\libraries\TMRpcm\pcmRF.cpp
WARNING: library ArduinoRS485 claims to run on samd, mbed_portenta, mbed_opta, mbed_nano, renesas_uno architecture(s) and may be incompatible with your current board which runs on avr architecture(s).
WARNING: library ArduinoModbus claims to run on megaavr, samd, mbed_nano, mbed_portenta, mbed_opta architecture(s) and may be incompatible with your current board which runs on avr architecture(s).
Generating function prototypes...
Hmmm....
So I was just checking to make sure I had dotted all the i's and crossed all the t's and whilst skimming thru the ModbusTCPClient.cpp header file I noticed something. The _.begin() function requires both an IP address to connect with AND a port (port 502) thru which to do so. In my code I provide the first but not the latter. That could explain the craziness I've been experiencing! It's been a couple years since I first started this project, but why I did include the port number in the Server devices but not the Client side, is beyond me... I'm pretty sure I copied the Examples exactly when I first wrote the code a few years ago. Anyway, it's fixed now and I hope it will continue working so I won't have to look at it ever again!
Cheers.
