Wire.begin(); vs boolean begin(TwoWire *theWire = &Wire);

I'm using the Adafruit VEML7700 library and to a novice (me) it seems to be more complex than it has to be, at least for my usage.

As I go through the Adafruit code I see the I2C bus initiated by

boolean begin(TwoWire *theWire = &Wire);

Where many/most examples for I2C use

Wire.begin();

Why or under what conditions should one use the "begin(TwoWire ..." command?

Thanks

John

JohnRob:

boolean begin(TwoWire *theWire = &Wire);

That's the function prototype of the library's begin function. It's a declaration. It's just telling the compiler "I'm going to define a function named begin that has return type boolean with a parameter of type TwoWire* that has a default value of &Wire".

You might not be familiar with function prototypes because the Arduino IDE automatically generates function prototypes for all the functions in the .ino files of your sketch before compiling it as C++. But that only happens for .ino files. The Arduino IDE leaves all other file types alone and compiles them as is. The library source code is in .h, .cpp, and .c files. So the library author must manually add function prototypes for all the functions they define in the library.

JohnRob:

Wire.begin();

That's a function call of the begin function of the class instance Wire. It causes the code in the definition of the begin function to be executed.

They are two completely different things. They are not in any way equivalent or interchangeable.

There's some pretty good information about C++ functions here:
http://www.cplusplus.com/doc/tutorial/functions/
There is also some decent information about C++ classes later in that tutorial if you want to understand the library even better.

If you only want to understand how to use the library, and not how it works, stick with studying the example sketches and the documentation Adafruit likely provides in the readme or on their website. You likely don't need to look at the library source code unless you're trying to get very detailed information. On the other hand, it's great to learn how libraries work under the hood and you can pick up some really useful tricks that can be applied to writing your own sketches as well as libraries.

@pert

Thank you for your response, it prompted me to dig deeper into the Adafruit library. I don't know enough C++ to do anything useful but I can get the basic sense of what the code is doing. In general I like to know what a library is doing although most of the time I don't make any changes or do anything different.

In this particular case it seems to me both initiate the I2C port of the 328p.

the "Wire.begin()" uses the arduino IDE to create / initiate the function. While the "begin(TwoWire.." seems to start a similar process (i.e. initialize the i2c port) but in a different manner.

So from a code architecture standpoint why would a programmer choose to use the "begin(TwoWire.." approach over the Wire.begin approach?

It one

  • faster
  • less memory
  • less susceptible to arduino library changes
  • etc

Its the 10,000 foot view that I always get hung up on.

Thanks
John

JohnRob:
In this particular case it seems to me both initiate the I2C port of the 328p.

As I already explained, they don't.

JohnRob:
why would a programmer choose to use the "begin(TwoWire.." approach over the Wire.begin approach?

There's no choice. They are not equivalent. In a library, you need the function prototype and then if you want to use the function you need to call it. That's how C++ works

Not believing what some random person on the Internet tells you is reasonable, though I'm not sure what the point is of asking on a forum is if you're just going to disregard the information provided.

Not believing what a well established and trusted C++ website tells you seems quite excessive.

Inventing your own meaning for some code and stubbornly sticking to that belief with absolutely no evidence to back it and good evidence to the contrary is insanity.

Luckily we can use the scientific method to determine the truth.

Question: what does a function prototype do?

Your hypothesis: a function prototype calls the function.

Prediction: If I run a sketch that only contains a function prototype and a function definition, it will execute the code in the function definition.

Experiment:
Let's simplify the code as much as possible to make it very easy to understand. There is no need for a class or to deal with the Wire library. We just need a function that does something that's easy to observe and unambiguous. Although the Arduino IDE will automatically generate function prototypes for you, you are also welcome to write your own, as I have done here.

Upload the following sketch:

// blink function prototype
void blink();

void setup() {}
void loop() {}

// blink function definition
void blink() {
  pinMode(LED_BUILTIN, OUTPUT);
  while (true) {
    digitalWrite(LED_BUILTIN, HIGH);
    delay(1000);
    digitalWrite(LED_BUILTIN, LOW);
    delay(1000);
  }
}

Observe the built-in LED on the Arduino board to see whether it blinks.

Results: The LED does not blink.

Analysis: A function prototype does not execute the function.

My hypothesis: You must call the function to execute the code in the function.

Prediction: If I run a sketch that only calls a function, it will execute the code in the function definition.

Experiment:
Upload the following sketch:

// blink function prototype
void blink();

void setup() {
  // blink() call
  blink();
}
void loop() {}

// blink function definition
void blink() {
  pinMode(LED_BUILTIN, OUTPUT);
  while (true) {
    digitalWrite(LED_BUILTIN, HIGH);
    delay(1000);
    digitalWrite(LED_BUILTIN, LOW);
    delay(1000);
  }
}

Observe the built-in LED on the Arduino board to see whether it blinks.

Results: The LED blinks.

Analysis: A function call executes the function.

Case closed!

The great thing about computer science is that it's often so incredibly easy to run experiments. When you want to learn what some code does, just write some simplified sketches to test the code, upload them to your board, and check whether they act as you predicted.

I would like to add --

1. By default, the PC4 and PC5 lines of Port-C are Digital IO lines (Fig-1). Now, We want to use these two lines as the 'signal lines (SDA and SCL)' for the I2C Bus. What do we do? We put HIGH at the TWEN-bit of TWCR Register of the MCU (Fig-2). As a result, the switch K2 (at conceptual level) is opened and the switch K1 (again at conceptual level) is closed; the associated logic of the I2C Interface/Logic comes into picture; we get an I2C Bus (Fig-1).
i2c-1z.png
Figure-1: Forming I2C bus by borrowing IO lines from Port-C

twcrx.png
Figure-2: TWCR register of I2C Interface

2. I have told to myself that the conversion of Fig-1 takes place for Master-UNO1 when the following two lines are included in the Master sketch at the appropriate places. (Of course, many more functionalities at hardware/software levels as described in the Wire.h Library are enabled by the inclusion of these two lines.)

#include<Wire.h>
Wire.begin();

3. The conversion of Fig-1 takes place for Slave-UNO2 with slave address (0100001 = 0x21) when the following two lines are included in the Slave sketch at the appropriate places. The Slave address enters into the address bits of the TWAR Register (Fig-3) of MCU.

#include<Wire.h>
Wire.begin(0x21);

TWAR.png
Figure-3: TWAR Register of I2C Interface/MCU

i2c-1z.png

twcrx.png

TWAR.png