Mega 2560 with Ethernet shield problems

Hi all,

I hope this is the right section of the forum.

Basically, it's my "first" time trying to make an OOP project, consisting of a TCP modbus client connecting to a modbus server, writing and reading holding registers.
Every 100ms the client object reads and writes those registers, while every 3s I check if the connection is still up (with the "connected()" function).

My main problem is that the connected() function always returns 1 after the first time the modbus connection is established, and remains like that even if I physically disconnect the ethernet cable from the shield.
The second main problem is that during the full operativity of the program (with no parts commented) the board gets stuck while reading or writing the holding registers.

The libraries I am using are:
ArduinoModbus
Ethernet2 (I also tried using Ethernet, no luck)
FireTimer (modified so when you stop the timer you also reset it)

I am also using other files where I just defined the modbus registers I want to read and write, the bits of those registers and things like that.

Here are the classes I made in the "SmartDevices.h" file:

#include <Arduino.h>
#include <ArduinoModbus.h>
#include <Custom.h>
#include <Defines.h>
#include <Ethernet2.h>
#include <FireTimer.h>
#include <Pinout.h>
#include <Queue.h>
#include <Registers.h>

#define PORT_PANEL 5020
#define CYLINDER_PORT 5020
#define MAX_MODBUS_FAIL 5

#define HEARTBEAT 3000
#define MB_REFRESH 100
#define DIGITAL_REFRESH 25
#define START_PRESS 500
#define PNEUM_TIMEOUT 3000
#define INFO_DISPLAY_INTERVAL 3000

#define CLOCK1Hz 500
#define CLOCK10Hz 50
#define CLOCK100Hz 5

/**
* Smart Device class, includes connectivity attributes such as IP setting by default
*/
class SmartDevice{
    public:
    //Constructor & destructor
    SmartDevice(IPAddress* ip, EthernetClient* client, ModbusTCPClient* mbClient, uint16_t port);
    SmartDevice();
    ~SmartDevice();

    //GETs
    bool getConnStatus();
    uint16_t getModbusFailsNum();
    bool wd_getFire();
    bool mb_getFire();
    bool getFire(FireTimer timer);
    ModbusTCPClient useModbusMethod();

    //SETs
    void setModbusFailsNum();
    void resetModbusFailsNum();
    void setWdTimer(unsigned int target = HEARTBEAT);
    void setMbTimer(unsigned int target = MB_REFRESH);

    //Other methods
    void wdTimerStart();
    void wdTimerStop();
    void mbTimerStart();
    void mbTimerStop();
    bool modbusErrorCheck(long valueToCheck);

    protected:
    //Modbus registers manipulation
    void modbusRegToArray(long inputReg, bool outArray[]);
    long modbusArrayToReg(bool inArray[]);

    //Connection attributes
    IPAddress* _ip;
    EthernetClient* _client;
    ModbusTCPClient* _mbClient;
    uint16_t _modbusPort = 5020;
    uint16_t _modbusFailCounter = 0;
    uint16_t _connectionAttempts = 0;
    bool _connectionOK = false;
    //Timer attributes
    FireTimer _wdTimer;
    unsigned int _wdTimerTarget;
    FireTimer _mbTimer;
    unsigned int _mbTimerTarget;

    //Device attributes

    //Data manipulation attributes
    long _inRegValue = 0;
    bool _arraySupport[16];

    private:

};


/**
* ModbusTCP server panel HMI class
*/
class Panel: public SmartDevice{
    public:
    //Constructor & destructor
    Panel(IPAddress* ip, EthernetClient* client, ModbusTCPClient* mbClient, uint16_t port);
    ~Panel();

    //GETs
    // bool getConnStatus();

    //SETs

    //Read/Write modbus
    void inputRead();
    void outputWrite();
    void customReading();
    void customWriting();

    //Other methods
    void loop();
    bool checkLightValue(unsigned char inputLight, unsigned char inputValue);
    
    private:

};

Here is the definition of the previous methods in the "SmartDevice.cpp" file:

#include "SmartDevices.h"

/* SMART DEVICE GENERIC CLASS METHODS */
SmartDevice::SmartDevice(IPAddress* ip, EthernetClient* client, ModbusTCPClient* mbClient, uint16_t port = 5020)
{
    _ip = ip;
    _client = client;
    _mbClient = mbClient;
    _modbusPort = port;

    _mbClient->setTimeout(200);
    setMbTimer(MB_REFRESH);
    setWdTimer(HEARTBEAT);
}

SmartDevice::SmartDevice()
{
    setWdTimer(MB_REFRESH);
}

SmartDevice::~SmartDevice()
{
}

void SmartDevice::setWdTimer(unsigned int target = HEARTBEAT)
{
    _wdTimerTarget = target;
    _wdTimer.begin(_wdTimerTarget);
}

void SmartDevice::setMbTimer(unsigned int target = MB_REFRESH)
{
    _mbTimerTarget = target;
    _mbTimer.begin(_mbTimerTarget);
}

void SmartDevice::wdTimerStart()
{
    _wdTimer.start();
}

void SmartDevice::wdTimerStop()
{
    _wdTimer.stop();
}

void SmartDevice::mbTimerStart()
{
    _mbTimer.start();
}

void SmartDevice::mbTimerStop()
{
    _mbTimer.stop();
}

bool SmartDevice::modbusErrorCheck(long valueToCheck)
{
	if (valueToCheck == -1)
	{
		return true;
	}
	else
	{
		return false;
	}
}

bool SmartDevice::getConnStatus()
{
    Serial.print("\nconnection check\n_mbClient->connected(): ");
    _wdTimer.stop(); 

    Serial.println(String(_mbClient->connected()));       //Here I wanted to see when the Ethernet cable is disconnected, but stays at 1 when I connect even just once

    if(!_mbClient->connected()){ //|| _modbusFailCounter > MAX_MODBUS_FAIL){
        _connectionOK = false;
        _mbClient->end();
        _mbClient->stop();      //redundant
        _connectionAttempts++;
        Serial.println("PANEL - Attempting to connect to Modbus TCP server");
        Serial.println("PANEL - Connection attempt: " + String(_connectionAttempts));
        
        if(!_mbClient->begin(*_ip, _modbusPort)){
            Serial.println("PANEL - Client failed to connect!");
        }
        else{
            _connectionOK = true;
            Serial.println("PANEL - Client connected!");
        }
    }
    Serial.print("_connectionOK: " + String(_connectionOK));
    return _connectionOK;
}

uint16_t SmartDevice::getModbusFailsNum()
{
    return _modbusFailCounter;
}

bool SmartDevice::wd_getFire()
{
    return _wdTimer.fire();
}

bool SmartDevice::mb_getFire()
{
    return _mbTimer.fire();
}

bool SmartDevice::getFire(FireTimer timer)
{
    return timer.fire();
}

ModbusTCPClient SmartDevice::useModbusMethod()
{
    return *_mbClient;
}

void SmartDevice::setModbusFailsNum()
{
    _modbusFailCounter++;
}

void SmartDevice::resetModbusFailsNum()
{
    _modbusFailCounter = 0;
}

void SmartDevice::modbusRegToArray(long inputReg, bool outArray[])
{
	int i = 0;
	long support = 0;

	for (i = 0; i < 16; i++)
	{
		support = inputReg;
		support = support >> i;
		support = support & 0x00000001;
		outArray[i] = support;
	}
}

long SmartDevice::modbusArrayToReg(bool inArray[])
{
    
	int i = 0;
	long support = 0;
	long outValue = 0;

	for (i = 0; i < 16; i++)
	{
		support = 0;
		support = inArray[i];
		support = support << i;
		outValue = outValue | support;
	}
	return outValue;
}


/* PANEL CLASS METHODS */
Panel::Panel(IPAddress* ip, EthernetClient* client, ModbusTCPClient* mbClient, uint16_t port)
{
    _ip = ip;
    _client = client;
    _mbClient = mbClient;
}

Panel::~Panel()
{
}

void Panel::inputRead()
{    
    long inRegValue = 0;
    bool arraySupport[16];

    if(_connectionOK){
        /*************************************************************************************************/
        /***********LETTURA PAGINA PANNELLO***************/
        inRegValue = _mbClient->holdingRegisterRead(PANEL_REG_Active_Page);
        //Serial.print("\ninRegValue: " + String(inRegValue));
        if (inRegValue == -1)
        {
            _modbusFailCounter++;
        }
        else
        {
            _modbusFailCounter = 0;
        }
        PANEL_IN_Active_Page = (unsigned int)inRegValue;

customReading();
    }
}

void Panel::outputWrite()
{
    long outRegValue = 0;
    bool arraySupport[16];
    int support1 = 0;

    if(_connectionOK){        /*************************************************************************************************/
        /*********HEARTBEAT**********/
        outRegValue = PANEL_OUT_HeartBeat;
        support1 = _mbClient->holdingRegisterWrite(PANEL_REG_Heartbeat, outRegValue);
        if (support1 == -1)
        {
            _modbusFailCounter++;
        }
        else
        {
            _modbusFailCounter = 0;
        }

customWriting();
}

void Panel::loop()
{
    wdTimerStart();
    if(wd_getFire())
    {
        getConnStatus();
    }

    mbTimerStart();
    if(mb_getFire())
    {
       mbTimerStop();
       inputRead();
       outputWrite();
    }
}

In another file (Connection.cpp) I do this:

static EthernetClient panel;
static ModbusTCPClient modbusTCPpanel(panel);
static IPAddress panel_address(xxx,xxx,xxx,xxx);

Panel PANEL(&panel_address, &panel, &modbusTCPpanel, 5020);

and in my main I just:

#include <Arduino.h>
#include <SmartDevices.h>

#include "Connection.h" //this is the file where I set the EthernetClient,
//IPAddress, modbusTCPpanel and PANEL objects

uint8_t mac[6] = {x,x,x,x,x,x};
IPAddress myIP(xxx,xxx,xxx,xxx);

void setup() {

  Serial.begin(115200);
  Ethernet.begin(mac, myIP);
  delay(5000);
  
  PANEL.useModbusMethod().setTimeout(200);
  PANEL.setWdTimer(HEARTBEAT);
  PANEL.setMbTimer(MB_REFRESH);
}

void loop() {
  PANEL.loop();
}

The customReading() and customWriting() functions are the exact copy (conceptually) of the inputRead() and outputWrite() ones: the only difference is that those functions contain the data that can vary from project to project (so I use the classes I made like a sort of library).

void Panel::customWriting()
{
	unsigned long outRegValue = 0;
	bool arraySupport[16];
	long support1 = 0;

    if(PANEL_IN_Active_Page == PAGE_AUTO)
    {
        /************************************/
        /**** YELLOW GROUP A*******/
        arraySupport[PP] = checkLightValue(PROD_LED_PP, LED_YELLOW);
        arraySupport[PN1] = checkLightValue(PROD_LED_PN1, LED_YELLOW);
        arraySupport[PN2] = checkLightValue(PROD_LED_PN2, LED_YELLOW);
        arraySupport[PN3] = checkLightValue(PROD_LED_PN3, LED_YELLOW);
        arraySupport[PN4] = checkLightValue(PROD_LED_PN4, LED_YELLOW);
        arraySupport[PN5] = checkLightValue(PROD_LED_PN5, LED_YELLOW);
        outRegValue = modbusArrayToReg(arraySupport);
        support1 = useModbusMethod().holdingRegisterWrite(PANEL_REG_Yellow_GroupA, outRegValue);
        if (support1 == 0)
        {
            Serial.println("Fallita scrittura registro pannello...");
            setModbusFailsNum();
        }
        else
        {
            resetModbusFailsNum();
        }
}

void Panel::customReading()
{
	bool arraySupport[16];
	long inRegValue = 0;
    
    if(PANEL_IN_Active_Page == PAGE_NOAUTO)
    {
        inRegValue = useModbusMethod().holdingRegisterRead(PANEL_MANREG_INPUT);
        if (inRegValue == -1)
        {
            Serial.println("Fallita lettura registro pannello...");
            setModbusFailsNum();
        }
        else
        {
            resetModbusFailsNum();
        }
        modbusRegToArray(inRegValue, arraySupport);
        PANEL_MAN_lock1 = arraySupport[PANEL_MAN_LOCK1];
        PANEL_MAN_lock2 = arraySupport[PANEL_MAN_LOCK2];
        PANEL_MAN_vacuum = arraySupport[PANEL_MAN_VACUUM];
        PANEL_MAN_punch = arraySupport[PANEL_MAN_PUNCH];
    }
}

I tried moving the methods from one class to the other, use only theconnection check and commenting the reading/writing, but it still won't "see" when the cable gets disconnected.
What am I doing wrong? Super thanks in advance to anyone who will even read all of this <3

  • a TCP connection handled by a TCP/IP stack doesn't care about the cable. a standard implementation of the TCP/IP stack can use multiple network interfaces to send packets for the same TCP connection.
  • a TCP connection has very long timeout to fail.
  • connected() returns true until there is data in the read buffer.
  • to test if the link is up, you can use Ethernet.linkStatus()
1 Like

Thank you so much for that, it solves my main headache!
Basically I should not even care for the cable disconnection in the first place, got it.

What about the second problem, do you have a clue why the board gets stuck even if everything is correctly connected and the server runs with no problems? Am I sending too many modbus requests, maybe? Or using objects/pointers improperly?

Ok I solved it: the problem was the "useModbusMethod()." instead of using directly "_mbClient->".
No idea why it happens, anyone knows the answer? I suppose there's some more C++ stuff that's obscure to me...
Thanks!!

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.