Method active when class not created

I have a project where I have all my input/output functions in class. The project reads some DHT22 temperatures sensors and operates relays depending on the temperature. Two odd things happen. 1) if I create an instance of the class and try to read temperatures it runs for two iterations and hangs.
2) if I don't create an instance of the class, just a pointer to it, the serial print method in the class still works, it does not hang and I get serial output.

If I test the class in smaller skit it works find (i.e. it has to be created to function) and the programme does not hang.

I am happy to add code if some one can tell me how to.

See: How to get the best out of this forum

Read that still no wiser

Really?

What is a code tag? how do I use a code tag?

Please follow the links you were given. If you will notice, immediately after it asks you to use code tags some of the words are blue. These words are a hyperlink. If you click on them, then you will be taken to a page with more information.

// Include the libraries:
#include <Arduino.h>
#include "EPCS_relay.h"
#include "EPCS_dht22.h"
#include "EPCS_comms.h"
#include "Config.h"
#include "EPCS.h"
#include <avr/wdt.h>

//These are the project veriables
#define VERSION "0.r2.1"

//Create the global pointers and other global veriables
myAllRelays* RList = NULL;
mySensorType SList[MAX_SENSORS];
int inputChannels[MAX_SENSORS] = { INPUT_CHANNELS };
char inputTypes[MAX_SENSORS] = { INPUT_TYPES };
myOutputs* myInterface = NULL;
DhtLib* dht[3];

unsigned long runNow;     //The next time to get the average temperature will be processed etc and generate output
unsigned long sampleNow;  //The next time to get DHT sensor values
float useTemp = errT;     //The current temperature to control the relays
bool Flash = false;       //LED to indicate board is alive
unsigned long FlashNow;   //The next time to flash
bool CommsOK;             //Flag to track the status of the comms links
bool Verbose = VERBOSE;   //Set the lots of output flag

/************************  Code  ****************************/

//Configure the Relays as defined in Config.h
void initR() {
  RList = new myAllRelays(N_RELAYS);
  if (RList->Info.inuse > MAX_RELAYS) RList->Info.inuse = MAX_RELAYS;

  if (N_RELAYS > 0) RList->Relay(0)->Set(RELAY_1);
  if (N_RELAYS > 1) RList->Relay(1)->Set(RELAY_2);
  if (N_RELAYS > 2) RList->Relay(2)->Set(RELAY_3);
  if (N_RELAYS > 3) RList->Relay(3)->Set(RELAY_4);
  if (N_RELAYS > 4) RList->Relay(4)->Set(RELAY_5);
  if (N_RELAYS > 5) RList->Relay(5)->Set(RELAY_6);
  if (N_RELAYS > 6) RList->Relay(6)->Set(RELAY_7);
  if (N_RELAYS > 7) RList->Relay(7)->Set(RELAY_8);
}

//Configure the inputs as defined in Config.h
void initS() {
  int j = 0;

  for (int i = 0; i < NUMBER_INPUTS; i++) {
    SList[i].channel = inputChannels[i];
    SList[i].type = inputTypes[i];
    SList[i].state = false;
    SList[i].failed = false;
    if (SList[i].type == 'S') {
      SList[i].dhtNum = j;
      //dht[j] = new DhtLib(SList[i].channel);
      //dht[j]->begin();
      j = j + 1;
      if (j == 3) j = 0;  //Stop the array from going out of bounds
    }
    if (SList[i].type == 'D') {
      pinMode(SList[i].channel, INPUT);
    }
  }
}

void setup() {
  unsigned long delayT = millis();

  //Set the indicator LED
  if (LED_FLASH > 0) {
    pinMode(LED_FLASH, OUTPUT);
    FlashNow = millis();
  }

  while ((millis() - delayT) < StartDelay) {}  //Do Nothing
  runNow = millis();                           //Sets the starting cycle time in ms

  //Create the output class but does not initilise any of the streams.  Arduino hangs if the recevers are not ready!
  //myInterface = new myOutputs(OUTPUT_SERIAL, OUTPUT_TCP, OUTPUT_UDP);

  //Setup the I/O ports, configure the veriables etc.
  initR();
  initS();

  //Make the sensor storage
  Serial.begin(9600);
  for (int i = 0; i < 3; i++) dht[i] = new DhtLib(i + 6);
  for (int i = 0; i < 3; i++) dht[i]->begin();
}

//Find the current average for all of the temperature values
float AverageOfAverages() {

  float av = 0.0;
  float count = 0;

  for (int i = 0; i < NUMBER_INPUTS; i++) {
    if (SList[i].Temperature.mean != errT) {
      count++;
      av += SList[i].Temperature.mean;
    }
  }
  if (count > 0) av = av / count;
  else av = errT;
  return av;
}

//Find the rolling average for the sensor values
void Average(myResultType* info) {

  if (info->value > errT) {
    info->count++;
    info->total += info->value;
    //If we have enough samples find the average.  Average start as errT and then be the last valid reading
    if (info->count == SAMPLES_TO_AVERAGE) {
      info->mean = info->total / info->count;
      info->count = 0;
      info->total = 0.0;
    }
  }
}

void GetReadings() {

  //Sample the seneors and find the folling average for each sensor
  for (int i = 0; i < NUMBER_INPUTS; i++) {

    if (SList[i].type == 'S') {
      SList[i].Temperature.value = dht[SList[i].dhtNum]->getTemperature();
      if (SList[i].Temperature.value == errT) SList[i].failed = true;
      else {
        Average(&SList[i].Temperature);
        SList[i].Humidity.value = dht[SList[i].dhtNum]->getHumidity();
        ;
        Average(&SList[i].Humidity);
      }
    }

    if (SList[i].type == 'D') {
      SList[i].state = digitalRead(SList[i].channel);
    }
  }
}

void SetReading(int Sensor, float Reading) {

  //Sets the reading for a virtual sensor
  if (Sensor > 0 && Sensor < NUMBER_INPUTS + 1) {
    if (SList[Sensor].type == 'V') {
      SList[Sensor].Temperature.mean = Reading;
    }
  }
}

void AddNumber(float num, char* units, char* txt) {
  char numTxt[8];

  strcat(txt, ",");
  dtostrf(num, 2, 1, numTxt);
  strcat(txt, numTxt);
  if (Verbose) strcat(txt, units);
}

void ShowReadings(float temperature) {

  char msgTxt[200] = { '\0' };
  char subTxt[100] = { '\0' };

  strcat(msgTxt, NAME);
  AddNumber(temperature, "C", msgTxt);
  for (int i = 0; i < NUMBER_INPUTS; i++) {
    if (Verbose) {
      sprintf(subTxt, ",%d%c", i + 1, SList[i].type);
      strcat(msgTxt, subTxt);
    }

    if (SList[i].failed) strcat(msgTxt, ",FAIL");
    else {
      if (SList[i].type == 'S') {
        if (Verbose) AddNumber(SList[i].Temperature.value, "C", msgTxt);
        AddNumber(SList[i].Temperature.mean, "aC", msgTxt);
        if (Verbose) AddNumber(SList[i].Humidity.value, "%", msgTxt);
        AddNumber(SList[i].Humidity.mean, "a%", msgTxt);
      }

      if (SList[i].type == 'D') {
        sprintf(subTxt, ",%d", SList[i].state);
        strcat(msgTxt, subTxt);
      }

      if (SList[i].type == 'V') {
        AddNumber(SList[i].Temperature.mean, "aC", msgTxt);
      }
    }
  }
  subTxt[0] = '\0';
  RList->Print(Verbose, subTxt);
  strcat(msgTxt, subTxt);
  myInterface->Snd(msgTxt);
}

//Causes an LED on the board to flash every second.
void FlashLED(unsigned long myNow) {
  if (LED_FLASH > 0) {
    if (myNow >= FlashNow) {
      FlashNow += 500;
      if (Flash) {
        digitalWrite(LED_FLASH, LOW);
        Flash = false;
      } else {
        digitalWrite(LED_FLASH, HIGH);
        Flash = true;
      }
    }
  }
}

//Carry out the commends
void DoCommand(myCommandType* Command) {

  switch (Command->number) {
    case 1:  //Long message command
      Verbose = true;
      break;
    case 2:  //Short message command
      Verbose = false;
      break;
    case 3:                   //Reboot command
      wdt_enable(WDTO_15MS);  //Turn on the WatchDog and don't stroke it.
      for (;;) {}             //do nothing and wait for the eventual...
      break;
    case 4:  //Serial message command
      myInterface->Reset(1, Command->param1);
      break;
    case 5:  //TCP message command
      myInterface->Reset(2, Command->param1);
      break;
    case 6:  //UDP message command
      myInterface->Reset(3, Command->param1);
      break;
    case 7:  //All relays message command
      if (Command->param1 > -1 && Command->param1 < 4) RList->ManualLock = Command->param1;
      break;
    case 8:  //Single Relay message command
      RList->LockRelay(Command->param1, Command->param2);
      break;
    case 9:  //Sets the virtual sensor reading
      SetReading(Command->param1, Command->param2);
      break;
  }
}

void loop() {
  unsigned long myNow = millis();
  char myTxt[80];
  myCommandType ThisCommand;

  //Flashes the indicator to show we are running
  FlashLED(myNow);

  //This gets sensor readings at the rate speicified.  It can be faster or slower (more likely) than the relay update reat
  if (myNow >= sampleNow) {
    sampleNow += SensorDelay;

    //If the comms are not running try and start them
    //if (!CommsOK) CommsOK = myInterface->StartComms(NAME, VERSION);

    // Reading temperature or humidity takes about 250 milliseconds!
    // Sensor readings may also be up to 2 seconds 'old' (its a very slow sensor)
    GetReadings();
    useTemp = AverageOfAverages();
  }

  //Controls the relays etc.
  if (myNow >= runNow) {
    runNow += CycleDelay;

    //Operate the relays.  If there is a valid temperature reading
    if (useTemp != errT) {
      RList->SetRelays(myNow);    //See which relays need to change in this time step, and change them
      RList->FindState(useTemp);  //Find the state a relay should be in according to the current temperature
      RList->FindTime(myNow);     //If the relay is in a diffent state to the one it should be in, set a time for it to change
    }
    ShowReadings(useTemp);  //Provides output
    myInterface->Command(&ThisCommand);
    if (ThisCommand.number > 0) DoCommand(&ThisCommand);
  }
}

Do you need all 8 files added separately?

We'd definitely need to see the offending class code.

Would it be possible to create a shorter example that displays your problem?

/****************************************************
* EPCS Comms Class
* D Thompson
* 24/09/2023
* Copyright Patko Co. 2023        
******************************************************/
#include "EPCS_relay.h"
#include "EPCS_comms.h"
#include "Config.h"
#include "EPCS.h"
#include <SPI.h>
#include <Ethernet.h>
#include <EthernetUdp.h>

//User Set Network & IP Settings
byte mac[] = MAC_ADDRESS;  //The mac adderess of the Ethernet shield
IPAddress ip1(IP_ME);      //The static IP address of this EPCS unit
IPAddress ip2(IP_DEST);    //The Network IP Address of the device to recive the UDP
IPAddress myDns(IP_DNS);
IPAddress gateway(IP_GATEWAY);
IPAddress subnet(IP_SUBNET);
unsigned int localPort = UDP_PORT;  //The UDP port to use
EthernetClient client;              //The Ethernet client object
EthernetServer server(TCP_PORT);    //The Ethernet port to use
EthernetUDP Udp;                    //The Udp instance

/*************** OUTPUTS CLASS ********************************************/
//Manages Printing to serial and sending to IP
myOutputs::myOutputs(bool SerialSt, bool TCPSt, bool UDPSt)
  : _SerialSt(SerialSt), _TCPSt(TCPSt), _UDPSt(UDPSt) {
  _sOK = false;
  _ipOK = false;
}

void myOutputs::Reset(int Change, bool Mode) {
  switch (Change) {
    case 1:
      _SerialSt = Mode;
      break;
    case 2:
      _TCPSt = Mode;
      break;
    case 3:
      _UDPSt = Mode;
      break;
  }
}

//Start the verious comms options
bool myOutputs::StartComms(char* myName, char* myVersion) {
  //Start the serial comms
  unsigned long Timeout = millis();
  char msgTxt[80] = { '\0' };

  //Enables the serial port for information output, if its connected
  if (!_sOK) {
    Serial.begin(9600);
    while (!Serial) {  //Wait for the serial to start.  if it does not then disable the serial
      if (millis() - Timeout > 1000) break;
    }
    if (Serial) _sOK = true;
  }

  //Enables the ethernet if its connected and cabled
  if (!_ipOK) {
    //Start the common Ethernet elements
    Ethernet.init(10);                                 // Most Arduino shields
    Ethernet.begin(mac, ip1, myDns, gateway, subnet);  // start the Ethernet connection and the server:
    _ipOK = true;
    // Check for Ethernet hardware present
    if (Ethernet.hardwareStatus() == EthernetNoHardware) {
      sprintf(msgTxt, "%s: %s\r", myName, "Error, Ethernet shield not found");
      _ipOK = false;
    }
    if (Ethernet.linkStatus() == LinkOFF) {
      sprintf(msgTxt, "%s: %s\r", myName, "Error, Ethernet cable is not connected");
      _ipOK = false;
    }
    if (_ipOK) {
      server.begin();        //Start TCP server
      Udp.begin(localPort);  //Start UDP
    }
  }
  if (strlen(msgTxt) > 1) Snd(msgTxt);

  //Send the hello
  if (!_helloSent) {
    sprintf(msgTxt, "%s, %s, %s", myName, myVersion, "Starting");
    Snd(msgTxt);
    _helloSent = true;
  }

  return _ipOK;
}

void myOutputs::Prt(char* SomeText) {
  if (_sOK && _SerialSt) Serial.println(SomeText);
}

void myOutputs::Snd(char* msgTxt) {
  Prt(msgTxt);
  SendUDP(msgTxt);
  SendIP(msgTxt);
}

//Sends and recieves TCP messages
void myOutputs::SendIP(char* msgTxt) {
  //Looks for a client and then sends what it has stored in rolling buffer
  if (_ipOK && _TCPSt) {
    EthernetClient client = server.available();  // listen for incoming clients
    if (client) {
      if (client.connected()) client.print(msgTxt);  // output the current dataline
    }
  }
}

//Write a line of UDP
void myOutputs::SendUDP(char* msgTxt) {
  if (_ipOK && _UDPSt) {
    Udp.beginPacket(ip2, localPort);
    Udp.write(msgTxt);
    Udp.endPacket();
  }
}

//Recives commands of TCP and enacts them
void myOutputs::Command(myCommandType* Command) {

  char myMessage[COMMAND_TEXT];                                                                          //The command recieved
  char *a, *b, *c;                                                                                       //The parts of the command
  char* myCommands[9] = { "LONG", "SHORT", "REBOOT", "SERIAL", "TCP", "UDP", "POWER", "RELAY", "SET" };  //These zare the valid commands
  int myNumCommands = 9, ib, ic;
  //float fc = errT;
  bool Gotit = false;
  char myNum[7];
  char* end;
  int i = 0, j = 0;

  Command->number = -1;
  //Get the command message.  Up the max number allowed
  if (_ipOK) {
    EthernetClient client = server.available();  //Listen for incoming clients
    while (client.available() > 0) {
      // read the bytes incoming from the client:
      if (i < COMMAND_TEXT) myMessage[i] = client.read();
      else client.read();
      i++;
    }
    myMessage[i] = '\0';
    if (strlen(myMessage)) {
      Snd(myMessage);  //Echo the message recieved

      //Look for a command
      i = 0;
      while (!Gotit && i < myNumCommands) {
        a = strstr(myMessage, myCommands[i]);
        if (a != NULL) {
          Gotit = true;
          strcpy(Command->command, a);
          Command->number = i + 1;
        }
        i++;
      }

      //Get the paramiters
      ib = -1;
      ic = -1;
      if (Gotit && i > 3) {
        b = strchr(a, ',');
        if (b != NULL) {
          b++;
          Command->param1 = (int)*b - '0';
        }
        c = strchr(b, ',');
        if (c != NULL) {
          c++;
          for (j = 0; j < 6; j++) {
            myNum[j] = *c;
            c++;
          }
          myNum[j - 1] = '\0';
          Command->param2 = (float)strtod(myNum, &end);
        }
      }
    }
  }
}

This is the comms class. The "snd" function works even if the serial as been activated by another call and the class has not been created (so baffled). I have put this class in a smaller skit and it works fine (i.e. "snd" does NOT work if the class has not be created.

/****************************************************
* EPCS DHT22 Classes
* D Thompson
* 23/09/2023
* Copyright Patko Co. 2023        
******************************************************/
#include "EPCS_dht22.h"
#include <Adafruit_Sensor.h>
#include "Config.h"
#include "EPCS.h"
#include <DHT.h>

void DhtLib::begin() {
  dht.begin();
}

float DhtLib::getTemperature() {
  float value;
  value = dht.readTemperature();
  //Serial.println(value);
  if (isnan(value)) return errT;
  else return value;
}

float DhtLib::getHumidity() {
  float value;
  value = dht.readHumidity();
  if (isnan(value)) return errT;
  else return value;
}

This is the other oddly behaving calls. If the output class is invoked properly (i.e. created with "new") this will not work. The called to dht.read... cause the code to lock up

Both work perfectly well together in a smaller skit.

Explain this sentence further. IT doesn't really make sense to me. What do you mean by "activated by another call"? If serial is setup, then it is setup. It's not going to only work for one class.

What class are you saying hasn't been created? What syntax are you using to call a function in a nonexistent class? I don't understand what you mean by this.

Do you have the class definitions? Can you post the code that you say is calling this?

This:

An MRE!!!

OK, (getting the hang of tags now!). I have declared a pointer to the output class called myInerface and then use this in Setup to create the actual instance of the class:

myInterface = new myOutputs(OUTPUT_SERIAL, OUTPUT_TCP, OUTPUT_UDP);

this should create the output class but it does not initilise any of the output streams. This does so later on:

if (!CommsOK) CommsOK = myInterface->StartComms(NAME, VERSION);

should active the Serial, TCP and UDP outputs. This runs fine for say for (say) two iterations and then hangs up.

So while debugging I commented out both of these lines and then added:

  Serial.begin(9600);

in setup to let me print stuff. When I do this, the line:

 myInterface->Snd(msgTxt);

the last line in the function "ShowReadings" still works. despite myInterface not being created at all.

/****************************************************
* EPCS Communications Class Code
* D Thompson
* 14/09/2023
* Copyright Patko Co. 2023        
******************************************************/
#include "Arduino.h"
#include "Config.h"
#include "EPCS.h"

#ifndef EPCS_comms_h
#define EPCS_comms_h

/*************** OUTPUTS CLASS ********************************************/
//Manages Printing to serial and sending to IP
class myOutputs {
public:
  myOutputs(bool SerialSt, bool TCPSt, bool UPDSt);
  void Reset(int Change, bool Mode);
  bool StartComms(char* myName, char* myVersion);
  void Snd(char* msgTxt);
  void Command(myCommandType* Command);

private:
  void Prt(char* SomeText);
  void SendIP(char* msgTxt);   //Sends an IP message
  void SendUDP(char* msgTxt);  //Sends an UDP message

  //Comms check flags
  bool _sOK;
  bool _ipOK;
  bool _helloSent = false;  //Hello message
  bool _SerialSt;           //True = stream the messagge to Serial
  bool _TCPSt;              //True = stream the messagge to TCP
  bool _UDPSt;              //True = stream the messagge to UDP
};

#endif
/****************************************************
* EPCS EPC22 Classe Header
* D Thompson
* 23/09/2023
* Copyright Patko Co. 2023        
******************************************************/
#include <Adafruit_Sensor.h>
#include <DHT.h>
#include "Config.h"
#include "EPCS.h"

#ifndef EPCS_dht22_h
#define EPCS_dht22_h

#define DHTTYPE DHT22  // DHT 22  (AM2302)

class DhtLib {
private:
  DHT dht;

public:
  DhtLib(int dhtPin)
    : dht(dhtPin, DHTTYPE) {}
  void begin();
  float getTemperature();
  float getHumidity();
};

#endif