.cpp files and their use.

The sketch that I have been given has about 12 .cpp files and an equal number of .h files.

I'm trying to make this beast work, and, I have a need to dive into the .cpp files and try and see what's going on.
I have looked at a few of them as well as did some research on them but there are a few things that are not explained.
The guy that wrote my sketch did not seem to have any organization in mind as he wrote them.
So, on to my questions.

  1. Are the cpp files resident at execution time
  2. There seem to be multiple sub-routines (?) within a .cpp file and they seem to be invoked as somecpp::sub-routinea.
    Is this proper code ?
  3. Is it proper and/or good technique to call another .cpp::routine from within a .cpp ?
    In the 2nd example, I have highlighted where I think another .cpp is called from within.
  4. For those .cpp files that contain multiple sub-routines, what indicates the end of the sub-routine ? It looks like it might be }}}}}

Here are some examples:
Main program. .ino

/* Autonomous Satellite Tracker.
 *
 * Connections on Adafruit ESP8266 Huzzah:
 *   Servo controller connects to SDA and SCL
 *   GPS TX connects to 16, RX to 2
 *
 */


#include "AutoSatTracker-ESP.h"
#include "NV.h"
#include "Sensor.h"
#include "Circum.h"
#include "Gimbal.h"
#include "Target.h"
#include "Webpage.h"

NV *nv;
Sensor *sensor;
Circum *circum;
Gimbal *gimbal;
Target *target;
Webpage *webpage;

/* called first and once
 */
void
setup()
{
    // init serial monitor
    Serial.begin (115200);
    delay(500);

    // this just resets the soft timeout, the hard timeout is still 6 seconds
    ESP.wdtDisable();

    // scan for I2C devices
    resetWatchdog();
    Serial.println();
    Serial.print (F("Scanning I2C:"));
    for (uint8_t a = 1; a < 127; a++) {
	Wire.beginTransmission(a);
	if (Wire.endTransmission() == 0) {
	    Serial.print (' '); Serial.print (a, HEX);
	}
    }
    Serial.println();

    // instantiate each module
    Serial.println (F("making NV"));
    nv = new NV();
    Serial.println (F("making Sensor"));
    sensor = new Sensor();
    Serial.println (F("making Circum"));
    circum = new Circum();
    Serial.println (F("making Gimbal"));
    gimbal = new Gimbal();
    Serial.println (F("making Target"));
    target = new Target();
    Serial.println (F("making Webpage"));
    webpage = new Webpage();

    // all set to go.. see you in loop()
    Serial.println (F("Ready"));

}

/* called repeatedly forever
 */
void
loop()
{
    // check for ethernet activity
    webpage->checkEthernet();

    // check for new GPS info
    circum->checkGPS();

    // follow the target
    target->track();
}

void
resetWatchdog()
{
    ESP.wdtFeed();
    yield();
}

and here is one .cpp, the smallest

/* this class contains information about the 9 dof spatial sensor
 */

#include "Sensor.h"

/* class constructor
 */
Sensor::Sensor()
{
	// instantiate, discover and initialize
	resetWatchdog();
	bno = new Adafruit_BNO055(-1, BNO055_I2CADDR);
	resetWatchdog();



/*  *************************************************************************
the next statement illustrates what is my question. Adafruit_BNO055::OPERATION_MODE_NDOF
is located somewhere else, in another .cpp file ?
******************************************************************************/


	sensor_found = bno->begin(Adafruit_BNO055::OPERATION_MODE_NDOF);
	
/*****************************************------------^^----------****************************/


if (sensor_found)
	    Serial.println (F("Sensor found ok"));
	else
	    Serial.println (F("Sensor not found"));
	installCalibration();
}

/* read the current temperature, in degrees C
 */
int8_t Sensor::getTempC()
{
	if (sensor_found)
	    return bno->getTemp();
	return (-1);
}

/* return whether sensor is connected and calibrated
 */
bool Sensor::calibrated(uint8_t& sys, uint8_t& gyro, uint8_t& accel, uint8_t& mag)
{
	if (!sensor_found)
	    return (false);

	sys = 0;
	gyro = 0;
	accel = 0;
	mag = 0;;
	bno->getCalibration(&sys, &gyro, &accel, &mag);
	return (sys >= 1 && gyro >= 1 && accel >= 1 && mag >= 1);
}

/* read the current az and el, corrected for mag decl but not necessarily calibrated.
 * N.B. we assume this will only be called if we know the sensor is connected.
 * N.B. Adafruit board:
 *   the short dimension is parallel to the antenna boom,
 *   the populated side of the board faces upwards and
 *   the side with the control signals (SDA, SCL etc) points in the rear direction of the antenna pattern.
 * Note that az/el is a left-hand coordinate system.
 */
void Sensor::getAzEl (float *azp, float *elp)
{
	Wire.setClockStretchLimit(2000);
	imu::Vector<3> euler = bno->getVector(Adafruit_BNO055::VECTOR_EULER);
	*azp = myfmod (euler.x() + circum->magdeclination + 540, 360);
	*elp = euler.z();
}

/* process name = value pair
 * return whether we recognize it
 */
bool Sensor::overrideValue (char *name, char *value)
{
	if (!strcmp (name, "SS_Save")) {
	    saveCalibration();
	    webpage->setUserMessage (F("Sensor calibrations saved to EEPROM+"));
	    return (true);
	}

	return (false);
}

/* send latest values to web page
 * N.B. labels must match ids in wab page
 */
void Sensor::sendNewValues (WiFiClient client)
{
	if (!sensor_found) {
	    client.println (F("SS_Status=Not found!"));
	    client.println (F("SS_Save=false"));
	    return;
	}

	float az, el;
	getAzEl (&az, &el);
	client.print (F("SS_Az=")); client.println (az);
	client.print (F("SS_El=")); client.println (el);

	uint8_t sys, gyro, accel, mag;
	bool calok = calibrated (sys, gyro, accel, mag);
	if (calok)
	    client.println (F("SS_Status=Ok+"));
	else
	    client.println (F("SS_Status=Uncalibrated!"));
	client.print (F("SS_SStatus=")); client.println (sys);
	client.print (F("SS_GStatus=")); client.println (gyro);
	client.print (F("SS_MStatus=")); client.println (mag);
	client.print (F("SS_AStatus=")); client.println (accel);

	client.print (F("SS_Save="));
	if (calok && sys == 3 && gyro == 3 && accel == 3 && mag == 3) 
	    client.println (F("true"));
	else
	    client.println (F("false"));

	client.print (F("SS_Temp="));
	client.println (getTempC());
}

/* read the sensor calibration values and save into EEPROM.
 * Wanted to stick with stock Adafruit lib so pulled from
 * post by protonstorm at https://forums.adafruit.com/viewtopic.php?f=19&t=75497
 */
void Sensor::saveCalibration()
{
	// put into config mode
	bno->setMode (Adafruit_BNO055::OPERATION_MODE_CONFIG);
	delay(25);

	// request all bytes starting with the ACCEL
	byte nbytes = (byte)sizeof(nv->BNO055cal);
	Wire.beginTransmission((uint8_t)BNO055_I2CADDR);
	Wire.write((uint8_t)(Adafruit_BNO055::ACCEL_OFFSET_X_LSB_ADDR));
	Wire.endTransmission();
	Wire.requestFrom((uint8_t)BNO055_I2CADDR, nbytes);

	// wait for all 22 bytes to be available
	while (Wire.available() < nbytes);

	// copy to NV
	Serial.println (F("Saving sensor values"));
	for (uint8_t i = 0; i < nbytes; i++) {
	    nv->BNO055cal[i] = Wire.read();
	    Serial.println (nv->BNO055cal[i]);
	}

	// restore NDOF mode
	bno->setMode (Adafruit_BNO055::OPERATION_MODE_NDOF);
	delay(25);

	// save in EEPROM
	nv->put();
}

/* install previously stored calibration data from EEPROM if it looks valid.
 * Wanted to stick with stock Adafruit lib so pulled from
 * post by protonstorm at https://forums.adafruit.com/viewtopic.php?f=19&t=75497
 */
void Sensor::installCalibration()
{
	resetWatchdog();
	byte nbytes = (byte)sizeof(nv->BNO055cal);

	// read from EEPROM, qualify
	nv->get();
	uint8_t i;
	for(i = 0; i < nbytes; i++) {
	    if (nv->BNO055cal[i] != 0)
		break;
	}
	if (i == nbytes)
	    return;		// all zeros can't be valid

	// put into config mode
	bno->setMode (Adafruit_BNO055::OPERATION_MODE_CONFIG);
	delay(25);

	// set from NV
	// Serial.println (F("Restoring sensor values"));
	for(uint8_t i = 0; i < nbytes; i++) {
	    Wire.beginTransmission((uint8_t)BNO055_I2CADDR);
	    Wire.write((Adafruit_BNO055::adafruit_bno055_reg_t)
	    		((uint8_t)(Adafruit_BNO055::ACCEL_OFFSET_X_LSB_ADDR)+i));
	    Wire.write(nv->BNO055cal[i]);
	    // Serial.println (nv->BNO055cal[i]);
	    Wire.endTransmission();
	}

	// restore NDOF mode
	bno->setMode (Adafruit_BNO055::OPERATION_MODE_NDOF);
	delay(25);
}

hextejas:
So, on to my questions.

  1. Are the cpp files resident at execution time
  2. There seem to be multiple sub-routines (?) within a .cpp file and they seem to be invoked as somecpp::sub-routinea.
    Is this proper code ?
  3. Is it proper and/or good technique to call another .cpp::routine from within a .cpp ?
    In the 2nd example, I have highlighted where I think another .cpp is called from within.
  4. For those .cpp files that contain multiple sub-routines, what indicates the end of the sub-routine ? It looks like it might be }}}}}
  1. No. Only the binary code for the functions that are called are included in your binary.
  2. "className::functionName()" is how you declare a function that is a member of an object class.
  3. The 'className' is generally only specified if the function is a 'class static' function: a function that applies to the class itself and not any particular instance of the class. Most methods (class functions) are NOT 'static' and are called using an instance of the class: classInstance.functionName() or classPointer->functionName().
  4. The '}' that matches the '{' at the beginning of the function marks the end of the function. It is generally bad practice to put more than one '}' on a line. Use the AutoFormat feature to make the sketch file more readable.
1 Like

hextejas:
2. There seem to be multiple sub-routines (?) within a .cpp file and they seem to be invoked as somecpp::sub-routinea.
Is this proper code ?
3. Is it proper and/or good technique to call another .cpp::routine from within a .cpp ?
In the 2nd example, I have highlighted where I think another .cpp is called from within.

code does not call a .cpp. Code invoked sub-functions in the same or different files

many professional software applications have millions of lines of code. don't you think it would be more manageable to have separate files that organize functionality as well as allowing multiple persons to work on the application?

The sketch that I have been given has about 12 .cpp files and an equal number of .h files.

I'm trying to make this beast work, and, I have a need to dive into the .cpp files and try and see what's going on.

you have 177 posts. ???

My preferred organizational technique with .h and .cpp files is described in Reply #3 Here.

An important thing to understand is that, after some minor preprocessing, the .ino file of your sketch is renamed with a .cpp extension and compiled as C++, just the same those other files that start out with the .cpp extension. So any code you can put in a .cpp file, you can put in a .ino file. The biggest difference between the two file types is that the sketch preprocessor automatically generates function prototypes for any function in a .ino file that doesn't already have a prototype, but it only does this for .ino files. So you need to manually supply your own prototypes in the .cpp or .h files. You can also manually add function prototypes to .ino files, but it's optional.

There is detailed information on sketch preprocessing here:
https://arduino.github.io/arduino-cli/latest/sketch-build-process/#pre-processing

1 Like

gcjr:
you have 177 posts. ???

And you mention this why ???