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