Object in a struct array....

Hi All,

I'm trying to code up a little voltage / current monitor that will monitor 3 voltages. For the prototyping stages I'm using the Adafruit INI219 breakout boards.

Because it will make the code more consise I'd like to have some generic read the sensor, print the value code. To this end I have created a typedefed struct containing the Adafruit_INI219 object for the sensor and the last read voltage and current (as integers)). I have then made an array of these, so that I can just pass the sensor number to my functions and have it select the right one from the array.

However when I do this the first access to the first sensor just seems to casue the board to reset. I'm guessing because the pointer to the Adafruit_INI219 is uninitialized. So basically how should I go about initializing the Adafruit_INI219 objects.

Code included below :

//------------------------------- librarys ----------------------------------
#include <Wire.h>
#include <LiquidCrystal_PCF8574.h>
#include <Adafruit_INA219.h>

//------------------------------- lcd ----------------------------------
LiquidCrystal_PCF8574 lcd(0x27);

static int LCD_TxByte(char DataByte, FILE *Stream);
FILE LCDStream; // = FDEV_SETUP_STREAM(LCD_TxByte0,NULL,_FDEV_SETUP_WRITE);
#define LCDPrintAt(xc,yc,format,...)    { lcd.setCursor(xc,yc); fprintf_P(&LCDStream,PSTR(format),##__VA_ARGS__); }

//------------------------------- Current probes ------------------------
#define NO_PROBES   3

typedef struct 
{
    Adafruit_INA219 *probe;
    int             Voltage;
    int             Current;    
} VIProbe;

VIProbe probes[NO_PROBES];

//------------------------------- input ----------------------------------
#define feedButton  2
#define leftButton  3
#define downButton  4
#define upButton    5
#define rightButton 6
#define auxButton   7

#define DebounceDelay   200

//------------------------------- system settings ----------------------------------

#define LCDLineBuffLen  80
int LCDLine;

void setup() 
{
    int error;
    
    Serial.begin(9600);

    Wire.begin();
    Wire.beginTransmission(0x27);
    error = Wire.endTransmission();
    
    lcd.begin(20, 4);         //LCD columns and rows
    lcd.setBacklight(1);

    // setup stream for stdio.
    fdev_setup_stream(&LCDStream,LCD_TxByte,NULL,_FDEV_SETUP_WRITE);

//                  12345678901234567890
    LCDPrintAt(0,0,"Dragon Monitor v1.0");
    LCDPrintAt(0,1,__DATE__);
    LCDPrintAt(0,2,__TIME__);
    
    // Init buttons
    pinMode(upButton, INPUT_PULLUP);
    pinMode(downButton, INPUT_PULLUP);
    pinMode(leftButton, INPUT_PULLUP);
    pinMode(rightButton, INPUT_PULLUP);
    pinMode(feedButton, INPUT_PULLUP);

    InitProbe(0,0x40);
//    InitProbe(1,0x41);
//    InitProbe(2,0x44);
  
    delay(4000);
    lcd.clear();
}

void loop() 
{
    int probeno;

    while (1)
    {
         for(probeno=0 ; NO_PROBES > probeno; probeno++)
        {
            ReadProbe(probeno);
            DisplayProbe(probeno);
        }

        LCDPrintAt(0,3,"All done!");
        delay(1000);
    }
}

void InitProbe(uint8_t probeno, 
               uint8_t address)
{
    if (NO_PROBES > probeno)
    {
        if (!probes[probeno].probe->begin(address))
            LCDPrintAt(0,3,"Probe %d:%d failed",probeno,address)
        else
            probes[probeno].probe->setCalibration_16V_400mA();
    }
    else
        LCDPrintAt(0,3,"Invalid probe no %d",probeno);

    delay(2000);
}

void ReadProbe(int  probeno)
{
    probes[probeno].Voltage=probes[probeno].probe->getBusVoltage_V();
    probes[probeno].Current=probes[probeno].probe->getCurrent_mA();
}

void DisplayProbe(int probeno)
{
    int amps;
    int miliamps;

    amps = probes[probeno].Current / 1000;
    miliamps = probes[probeno].Current % 1000;

    if (amps > 0) 
      LCDPrintAt(0,probeno,"%d:%dV %d.%3.3dA",probeno,probes[probeno].Voltage,amps,miliamps)
    else
      LCDPrintAt(0,probeno,"%d:%dV %dmA",probeno,probes[probeno].Voltage,miliamps)   
}

static int LCD_TxByte(char DataByte, FILE *Stream)
{
    lcd.write(DataByte);
}

Arduino uses C++, not C, you don't have to typedef a struct in C++. Also, macros are evil, don't use them for constants.
Your code doesn't work because you never initialize the "probe" pointers, and you don't create any Adafruit_INA219 objects that they could possibly point to. Dereferencing a dangling pointer will crash the microcontroller. There's no need for pointers, really, just include the Adafruit_INA219 object itself in your VIProbe struct, initializing it using the constructor.

Also, you're passing a number to the Adafruit_INA219::begin method that expects a pointer to the I2C/Wire object to use. The compiler should have warned you about that, go to Preferences in the IDE, and set compiler warnings to "All":

/home/Arduino/sketch_jul13a/sketch_jul13a.ino:28:26: warning: invalid conversion from 'int' to 'TwoWire*' [-fpermissive]
     if (!probe.begin(0x40))
                          ^
In file included from /home/Arduino/sketch_jul13a/sketch_jul13a.ino:5:0:
/home/Arduino/libraries/Adafruit_INA219/Adafruit_INA219.h:152:8: note:   initializing argument 1 of 'bool Adafruit_INA219::begin(TwoWire*)'
   bool begin(TwoWire *theWire = &Wire);
        ^~~~~

Why pass an array index to the functions if you can just pass a reference to the probe? Relying on global variables inside of functions leads to code that's hard to read and maintain.
The InitProbe function can be made a member function of the VIProbe struct.

I'd try something like this:

//------------------------------- libraries ----------------------------------
#include <Wire.h>
#include <LiquidCrystal_PCF8574.h>
#include <Adafruit_INA219.h>

//------------------------------- lcd ----------------------------------
LiquidCrystal_PCF8574 lcd(0x27);

static int LCD_TxByte(char DataByte, FILE *Stream);
FILE LCDStream; // = FDEV_SETUP_STREAM(LCD_TxByte0,NULL,_FDEV_SETUP_WRITE);
#define LCDPrintAt(xc,yc,format,...)    \
  do { \
    lcd.setCursor(xc,yc); \
    fprintf_P(&LCDStream, PSTR(format), ##__VA_ARGS__); \
  } while(0)

//------------------------------- Current probes ------------------------

struct VIProbe {
  VIProbe(uint8_t address) : probe(address) {}
  
  Adafruit_INA219 probe;
  int             Voltage;
  int             Current;

  bool begin() {
    if (!probe.begin())
      return false;
    probe.setCalibration_16V_400mA();
    return true;
  }

  void read() {
    Voltage = probe.getBusVoltage_V();
    Current = probe.getCurrent_mA();
  }
};

VIProbe probes[] = {
  0x40,
  0x41,
  0x44,
};

// Get the length of an array
template <class T, size_t N> size_t len(const T (&)[N]) {
  return N;
}

//------------------------------- input ----------------------------------

const uint8_t feedButton  = 2;
const uint8_t leftButton  = 3;
const uint8_t downButton  = 4;
const uint8_t upButton    = 5;
const uint8_t rightButton = 6;
const uint8_t auxButton   = 7;

const unsigned long DebounceDelay = 200;

//------------------------------- system settings ----------------------------------

void setup() {
  Serial.begin(9600);

  Wire.begin();
  Wire.beginTransmission(0x27);
  int error = Wire.endTransmission();

  lcd.begin(20, 4);         //LCD columns and rows
  lcd.setBacklight(1);

  // setup stream for stdio.
  fdev_setup_stream(&LCDStream, LCD_TxByte, NULL, _FDEV_SETUP_WRITE);

  //                12345678901234567890
  LCDPrintAt(0, 0, "Dragon Monitor v1.0");
  LCDPrintAt(0, 1, __DATE__);
  LCDPrintAt(0, 2, __TIME__);

  // Init buttons
  pinMode(upButton, INPUT_PULLUP);
  pinMode(downButton, INPUT_PULLUP);
  pinMode(leftButton, INPUT_PULLUP);
  pinMode(rightButton, INPUT_PULLUP);
  pinMode(feedButton, INPUT_PULLUP);

  // Init probes
  for (uint8_t i = 0; i < len(probes); ++i)
    if (!probes[i].begin())
      LCDPrintAt(0, 3, "Probe %d: failed", i);

  delay(4000);
  lcd.clear();
}

void loop() {
  int lineNo = 0;
  for (auto &probe : probes) {
    probe.read();
    DisplayProbe(probe, lineNo++);
  }

  LCDPrintAt(0, 3, "All done!");
  delay(1000);
}

void DisplayProbe(VIProbe &probe, int lineNo) {
  int amps = probe.Current / 1000;
  int miliamps = probe.Current % 1000;

  if (amps > 0)
    LCDPrintAt(0, lineNo, "%d:%dV %d.%3.3dA", lineNo, probe.Voltage, amps, miliamps);
  else
    LCDPrintAt(0, lineNo, "%d:%dV %dmA", lineNo, probe.Voltage, miliamps);
}

static int LCD_TxByte(char DataByte, FILE *Stream) {
  lcd.write(DataByte);
}

Pieter