Problem with for() loop through array of objects

Hello everybody. I'm new to the forum and this is my first post.
I'm also new to Arduino and C++, but have done programming in the (distant) past.

I have a poc sketch for a model train layout, in which I read three ir-sensors per track ('spoor' in the sketch) to establish the status (occupied or not). Tracks and sensors are defined als classes. For looping through the sensors and tracks respectively I have put them in arrays.
The problem is, I think, in the update-loop in the main sketch. Instead of reading the sensors of track[0] in the first pass, it gives the result of track[1]. In the second pass it gives the results of non-existing sensors/pins.

I must have made a mistake, but I can't figure it out. BTW: the sensors are working correctly. I wrote a short sketch to test them, also using an array for looping through the sensors directly, without the spoor class, and the results were as expected.

Main sketch:



#include "globals.h"
#include "OpstelSpoor.h"
#include "LibPrintf.h"

// Forward declaration: waits until key pressed for debugging
void waitForKeyPress();

const byte numTracks = 2; // debug, moet naar globals.h

// define the pins, corresponding to each track
const byte inrijPin[numTracks] = { A0, A1 };   // testaantal, aanpassen voor def
const byte bezetPin[numTracks] = { 2, 3 };     // testaantal, aanpassen voor def
const byte uitrijPin[numTracks] = { 11, 12 };  // testaantal, aanpassen voor def


// ###############  instantiate the tracks   ######################################################

OpstelSpoor spoor[numTracks] = {
  OpstelSpoor(inrijPin[0], bezetPin[0], uitrijPin[0]),
  OpstelSpoor(inrijPin[1], bezetPin[1], uitrijPin[1]),
};

// ################################################################################
void setup() {

  Serial.begin(115200);

  for (byte i = 0; i < numTracks; i++) {
    //debug
    printf("[MN-%.i] Iteratie spoor[i].begin: i = %i.\n", __LINE__, i);
    waitForKeyPress();
    //\debug
    spoor[i].begin();
  }
  printf("[MN-%.i] Setup klaar.\n", __LINE__);  //debug
}  //setup

// ################################################################################
void loop() {
  // check de bezetting van een spoor
  for (byte i = 0; i < numTracks; i++) {
    printf("[MN-%i] Status van spoor: %i.\n", __LINE__, i);  //debug
    spoor[i].update();                              // lees sensors en bepaal status
    printf("[MN-%.i] Spoor %i heeft status %i.\n", __LINE__, i, spoor[i].getStatus());
    Serial.println("");
  }
  waitForKeyPress();
}  // loop


void waitForKeyPress() {
  while (!Serial.available()) {
    // Do nothing
    delay(100);
  }

  while (Serial.available()) {
    Serial.read();
  }
}  // waitForKeyPress

track class header file:

/*
Bezetmelding voor doorgaand spoor op schaduwstation
20240123  start

*/

#ifndef sp_h
#define sp_h

#include "globals.h"
#include "Arduino.h"
#include "Sensor.h"

class OpstelSpoor {
public:
  OpstelSpoor(int sensorIn, int sensorBezet, int sensorUit);  //
  void begin();
  void update();
  uint8_t getStatus();  // of de update zelf een return laten geven?

private:
  Sensor _sensorIn;
  Sensor _sensorBezet;
  Sensor _sensorUit;
  uint8_t _bezetMelding;
  uint8_t _status;
  void _waitForKeyPress();
  Sensor _sensor[];

protected:
};

#endif

track class cpp file:

/*
Bezetmelding voor doorgaand spoor op schaduwstation
20240123  start

*/

#include "globals.h"
#include "Arduino.h"
#include "OpstelSpoor.h"
#include "Sensor.h"
#include "LibPrintf.h"

const uint8_t _numSensors = 3;

enum {
  KAPOT,   // O
  VRIJ,    // 1
  INRIJ,   // 2
  BEZET,   // 3
  UITRIJ,  // 4
  ALARM    // 5
};


OpstelSpoor::OpstelSpoor(int sensorIn, int sensorBezet, int sensorUit)
  : _sensorIn(sensorIn), _sensorBezet(sensorBezet), _sensorUit(sensorUit) {}  //geldt voor doorgaande opstelsporen

void OpstelSpoor::begin() {
  // plaats de sensors in array

  //debug
  printf("[OS-%.i] Waarde _sensorIn: %i, _sensorBezet: %i, _sensorUit: %i.\n", __LINE__,
         _sensorIn.getPinNumber(), _sensorBezet.getPinNumber(), _sensorUit.getPinNumber());
  _waitForKeyPress();
  //\debug

  Sensor _sensor[_numSensors] = {
    _sensorIn,
    _sensorBezet,
    _sensorUit
  };

  //debug
  printf("[OS-%.i] Waarde _sensorIn: %i, _sensorBezet: %i, _sensorUit: %i.\n", __LINE__,
         _sensor[0].getPinNumber(), _sensor[1].getPinNumber(), _sensor[2].getPinNumber());
  _waitForKeyPress();
  //\debug

  // start de sensors
  for (byte i = 0; i < _numSensors; i++) {
    _sensor[i].begin();
  }

  //initieer de bezetMelding
  _bezetMelding = 0;
};  //Opstelspoor::begin


void OpstelSpoor::update() {
  for (byte i = 0; i < _numSensors; i++) {
    bitWrite(_bezetMelding, i, _sensor[i].update());

    // debug
    printf("[OS-%.i] Sensor %i = %i met bezetmelding = %i.\n", __LINE__, i, _sensor[i].update(), _bezetMelding);
    printf("[OS-%.i] Pin: %i.\n", __LINE__, _sensor[i].getPinNumber());
    delay(1000);
    //\debug
  }

  //debug
  _waitForKeyPress();
  //\debug

  // vertaal de bezetmelding naar de status

  switch (_bezetMelding) {
    case 0:
      _status = VRIJ;
      break;
    case 4:
      _status = INRIJ;
      break;
    case 6:
      _status = BEZET;
      break;
    case 2:
      _status = BEZET;
      break;
    case 3:
      _status = UITRIJ;
      break;
    case 1:
      _status = UITRIJ;
      break;
    case 7:
      _status = ALARM;
      break;
    case 5:
      _status = ALARM;
      break;
    default:
      _status = KAPOT;
      break;
  }

  //zet de bezetMelding terug op 0 voor de volgende cyclus
  _bezetMelding = 0;
}  //Opstelspoor::update

uint8_t OpstelSpoor::getStatus() {
  return _status;
}  //OpstelSpoor::getStatus

void OpstelSpoor::_waitForKeyPress() {
  while (!Serial.available()) {
    // Do nothing
    delay(100);
  }

  while (Serial.available()) {
    Serial.read();
  }
}  //OpstelSpoor::_waitForKeyPress

Output on the serial monitor for one pass through:

:{[MN-33] Iteratie spoor[i].begin: i = 0.
[OS-32] Waarde _sensorIn: 14, _sensorBezet: 2, _sensorUit: 11.
[OS-44] Waarde _sensorIn: 14, _sensorBezet: 2, _sensorUit: 11.
[MN-33] Iteratie spoor[i].begin: i = 1.
[OS-32] Waarde _sensorIn: 15, _sensorBezet: 3, _sensorUit: 12.
[OS-44] Waarde _sensorIn: 15, _sensorBezet: 3, _sensorUit: 12.
[MN-38] Setup klaar.
[MN-45] Status van spoor: 0.
[OS-64] Sensor 0 = 0 met bezetmelding = 0.
[OS-65] Pin: 15.
[OS-64] Sensor 1 = 0 met bezetmelding = 0.
[OS-65] Pin: 3.
[OS-64] Sensor 2 = 0 met bezetmelding = 0.
[OS-65] Pin: 12.
[MN-47] Spoor 0 heeft status 1.

[MN-45] Status van spoor: 1.
[OS-64] Sensor 0 = 0 met bezetmelding = 0.
[OS-65] Pin: 15258.
[OS-64] Sensor 1 = 1 met bezetmelding = 2.
[OS-65] Pin: -20021.
[OS-64] Sensor 2 = 1 met bezetmelding = 6.
[OS-65] Pin: -28707.
[MN-47] Spoor 1 heeft status 3.

the _sensor private instance variable

is not then same as the local array you define in OpstelSpoor::begin()

which will get removed at the end of the function

You might want to read about scope

Thanks for pointing that out. I'm aware of scoping, but I thought that when you declare a private variable in the header, you can use it in the functions in cpp. I don't fully understand, why the _sensor[] array in Opstelspoor::begin() is different from, say, _sensorIn.
I suppose I have to move the value assignment to _sensor[] to the constructor, but at first tries this gives a lot of compiler errors. Have to puzzle on that one...

this is true but here you define a new one as you repeated the type in front of your definition/

also in the class definition you don't give a size to your array

does it even compile?

When I leave out the type, I get an error in compiling. So I put it in front of it.

Yes, this compiles. Not however when I give it a size. Also it is being handled in the update() function, as you can see in the output. Albeit wrongly.

I'm really at a loss about how to define an array of member objects within an object.

Oddly, "flexible array" members are allowed if specified as the last member in a class / struct. However, there are apparently limitations on then including them in another class / struct. And, I imaging, making an array of them.

But, my quick internet search didn't turn up the proper method for actually using them.

OK

indeed, you need at least one other attribute but I don't think flexible array members are supported in C++, may be a GCC thingy to not crash because it's supported in C to access variable length data?

You can't do anything with them.

--

@pet_jk, instead of

Sensor _sensor[];

you might want to have a pointer to an array of Sensor items and another instance variable that says how many you have

Sensor * _sensors;
size_t _sensorsCount; 

or as it seems you know you will have only 3 sensors in that array

Sensor* _sensor[3];

and in the begin() function do

void OpstelSpoor::begin() {
  // plaats de sensors in array
    _sensor[0] = &_sensorIn;
    _sensor[1] = &_sensorBezet;
    _sensor[2] = &_sensorUit;

•••

  };


I made the array an array of pointers, so you would use -> instead of . when using the array.

Alternatively you don't need to duplicate the Sensors to have the array, just don't create those instances variables

  Sensor _sensorIn;
  Sensor _sensorBezet;
  Sensor _sensorUit;

and only have

  Sensor _sensor[3];

the constructor would then instantiate the sensors within the array as part of the initialiser list

OpstelSpoor::OpstelSpoor(int sensorIn, int sensorBezet, int sensorUit)
  : _sensor{sensorIn, sensorBezet, sensorUit} {}  

of course you'll have to know that _sensorIn is the first entry as that name is gone (same for the others entries)

1 Like

Thanks, this gives me some things to try. Will be later tonight.

Brilliant. I've implemented this last alternative and it does the trick, also in an elegant way that I can easily understand. Working with pointers still is a little outside my comfort zone.
Thanks.

Great - have fun

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