Passing an I2C object to a Class

Howdy,

I’m playing with Classes and making an I2C IC library.

I need to pass I2C object to an Object on its creation, I’m working with MCU that has multiple I2C peripherals so please skip part “but why you need to do that?” or “you know you can connect up to 128 devices in one I2C peripherals?” – don’t wast yours and my time.

I’m not new at coding, I’m new at classes, I imagine it should be coded something like this in class constructor:

ICconstructor( TwoWire *I2C_pheripheral);

I’m guessing in Wire library “default” wire object gets created only when Wire.begin() is called so I can’t pass it before that?

Thanks for your time and input.

3Dgeo:
I'm guessing in Wire library "default" wire object gets created only when Wire.begin() is called so I can't pass it before that?

No. See Wire.h and Wire.cpp (assuming Uno board since you didn't tell us).The default 'Wire' object is created automatically.

But yes, with multiple I2C interfaces available, the desired one could be passed (by pointer or reference) to your class's constructor.

The object is created at the point that itcis declared. If you put "int i;" somewhere in global scopevthen everything can refer to it and use it.

But maybe you don't give i a value until loop(). So it would be legal for setup() to use i even though it is useless because you did not give it a value.

The same applies to classes. The constructor is (often) in global scope but it is not ready for use until setup() calls the .begin() method.

So yes, you can pass an instance of TwoWire to a constructor but don't attempt to communicate with the i2c device in the constructor. Provide a .begin() and that can begin the TwoWire.

Currently I’m testing with Leonardo, later with STM32.

I also found that calling this: Wire.write(*Data, DataAmount); in the class gives me an error (but it woks fine outside the class): error: no matching function for call to 'write(byte&, byte&)

So I should code like this:

“INO” file:

ICclass ICobject(Wire, IC_address);

In the IC.cpp:

ICclass::ICclass( TwoWire *I2Cpipe, byte IC_address);
_I2Cpipe = *I2Cpipe;
_I2Cpipe.begin();

_I2Cpipe.beginTransmission( IC_address );
_I2Cpipe.write(ByteToTransfer);

…and continue communication as usual using _I2Cpipe?

Don't .begin in the constructor. The hardware isn't ready yet.

.begin in your .begin.

MorganS:
Don’t .begin in the constructor. The hardware isn’t ready yet.

.begin in your .begin.

Ohhhhhhh, hardware isn’t ready yet – now I got it, thank you.

How about creating objects in Setup() after Wire.begin()? Is it even possible to create Objects in a function? As I said I’m new what comes to classes…

Again, Wire.begin() does not create the TwoWire instance, it only initializes it.

I'm guessing (but will have to check) that all the possible TwoWire instances on you Leonardo board are already created for you automatically --- just like the single available instance on an Uno board is done.

So, you probably don't need to create any other objects.

I’m not creating Wire object at the moment, I’m trying to pass it to my class as a parameter.
If only one Wire is used this step is useless, but as I said I want to use multiple I2Cs.

do I need to include Wire.h in my library? Wire is included in main INO file and Wire instances gets initialized in setup() but I’m getting undefined reference to `Cclass::_I2Cpipe? And if I include Multiple libraries were found for “Wire.h”

Is passing Wire Object to my class like this ICclass::begin( TwoWire I2Cpipe);

INO code:

#include <Wire.h>
#include "ICclass.h"

ICclass ICobj (128);


void setup()
{
  Wire.begin();
  ICobj.begin(Wire);
}

void loop()
{

}

It doesn’t work…

Post ICclass.h and ICclass.cpp.

"Create object" in C++ means "reserve a space in memory to hold the object."

An example is "int i;". That tells the compiler to create an object of type "int". But there is nothing inside the object yet. "int i=0;" combines the action of creating the object and initializing it with the value of 0.

A class has a "constructor" which can do more complex initialization. But it should not attempt to talk to the outside world yet.

Yes you can create objects "inside a function". They will then be local to the function. The actual moment of creation occurs when your function is called but before any of your code in the function is run. So if the class constructor needs some value which is calculated in your function code, that will be unavailable to the constructor. But it can use the parameters (values passed to the function.)

I found the issue, _I2C_ADRESS was declared as "static const"

Don't do this...

static const TwoWire _I2Cpipe;

This creates a new TwoWire object. You don't need a new one. You need a pointer to the one that Wire.h created.

It looks like you are creating a class for a i2c sensor. Look at libraries for other i2c sensors to see how they do it.

MorganS:
Don't do this...
This creates a new TwoWire object. You don't need a new one. You need a pointer to the one that Wire.h created.

Yah, I found it as soon as I posted the code :slight_smile:
Can you show how correctly pass it with the pointer? I'm getting errors...

MorganS:
It looks like you are creating a class for a i2c sensor. Look at libraries for other i2c sensors to see how they do it.

I don't want to copy other library, I want to learn myself. I have written code that works with this IC, now as a practice I want to make class for it, I sorely need to learn classes and other more advance coding stuff, best way to learn is to make mistakes.

.ino:

#include <Wire.h>
#include "TLC59116.h"

TLC59116 TLC(128);

void setup() {
  Wire.begin();
  TLC.begin(Wire);
}

void loop(){
}

.h:

#include <Wire.h>
#ifndef TLClib
#define TLClib

#include "Arduino.h"

class TLC59116
{
  public:

    TLC59116( byte I2C_ADRESS );
    void begin( TwoWire &I2Cpipe );
    void SendData ( byte *Data, byte DataAmount );

  private:

    byte _I2C_ADRESS;
    TwoWire *ptr;
};

#endif

.cpp

#include "TLC59116.h"

TLC59116::TLC59116 ( byte I2C_ADRESS ) : _I2C_ADRESS(I2C_ADRESS) {}

void TLC59116::begin ( TwoWire &I2Cpipe ) {
  ptr = &I2Cpipe;

  ptr->beginTransmission( _I2C_ADRESS );
  ptr->write(byte(0));
  ptr->endTransmission();
}

void TLC59116::SendData ( byte *Data, byte DataAmount ) {
  ptr->beginTransmission(0x60);
  ptr->write(0b10100010);
  ptr->write(Data, DataAmount);
  ptr->endTransmission();
}

The best way to learn is to see how the experts do it. Don't keep injuring yourself by holding the wrong end of the soldering iron.

MorganS:
Yes you can create objects "inside a function". They will then be local to the function.

You can create the object dynamically in a function and use it globally.

#include "MyClass.h"

myClass *ptr;

void setup() {
  ptr = new myClass;
}

void loop() {
  // use the ptr pointer here
}
ptr = &I2Cpipe;

Now ptr points to the location of the pointer that was passed. That is a local variable so it is about to go out of scope and something else will be able to re-use that memory location.

MorganS:
Now ptr points to the location of the pointer that was passed. That is a local variable so it is about to go out of scope and something else will be able to re-use that memory location.

Nope. It points to the object that was passed in to begin() by reference.

Thank you, pointer and references is a bit tricky to digest...

MorganS:
Don't keep injuring yourself by holding the wrong end of the soldering iron.

Well, you hold it wrong one time and it becomes lesson for life :smiley: