Writing a library: use of the constructor

Hello!

I’m currently writing a library, but have some questions concerning the constructor.
The device I’m writing the library for has a few modules on board that need initialization.

For example an UART devise, so that would need the “Serial1.begin” function. But also an SPI devise that needs to receive a few instructions before normal operation, and a shift register that needs to be cleared.

Is it good practice to place this “initialization” code in the constructor of the library, so that they get executed when creating the object? See code below where I did this for Serial1.begin.
Or should I put this code in an initialization function like GameCode::initialization() that the user would have to call in the void setup()?

Thanks in advance!

Main sketch:

#include "GameCode.h"
const byte statusled = 13;

GameCode MyGameCode;

void setup() {
  Serial.begin(9600); //initialise UART for USB feedback
  
  pinMode(17, OUTPUT);
  pinMode(30, OUTPUT);
  
}


void loop() {
    MyGameCode.MP3PlayTrack(01, 02);
    
  while (true) {
    Serial.print("test");
    Serial.println();
    digitalWrite(17, HIGH);
    digitalWrite(30, HIGH);
    delay(1000);
    digitalWrite(17, LOW);
    digitalWrite(30, LOW);
    delay(1000);
  }
 
}

.h file:

#ifndef GameCode_h
#define GameCode_h

#include <Arduino.h>

class GameCode {
  public:
    GameCode(); //constructor
    
    void MP3SendCommand(byte command, byte para1_highbyte, byte para2_lowbyte);
    void MP3PlayTrack(byte Directory, int Track);
    
  private:
};


#endif

.cpp file:

#include "Arduino.h"
#include "GameCode.h"

GameCode::GameCode(){
  //Constructor
  //Use for initialization?
  Serial1.begin(9600); //initialise UART for MP3 module
  delay(1000);
}

void GameCode::MP3SendCommand(byte command, byte para1_highbyte, byte para2_lowbyte) {
  const byte startbyte = 0x7E;
  const byte versioninfo = 0xFF;
  const byte numberofbytes = 0x06;
  const byte feedback = 0x00;
  const byte endbyte = 0xEF;
  unsigned int checksum;
  byte checksum_high;
  byte checksum_low;


  //Calculate checksum
  checksum = versioninfo + numberofbytes + command + feedback + para1_highbyte + para2_lowbyte;
  checksum = 0 - checksum;
  checksum_low = checksum & 0xFF;
  checksum_high = (checksum & 0xFF00) >> 8;

  //Send datastream to MP3 module
  Serial1.write(startbyte);
  Serial1.write(versioninfo);
  Serial1.write(numberofbytes);
  Serial1.write(command);
  Serial1.write(feedback);
  Serial1.write(para1_highbyte);
  Serial1.write(para2_lowbyte);
  Serial1.write(checksum_high);
  Serial1.write(checksum_low);
  Serial1.write(endbyte);
}

void GameCode::MP3PlayTrack(byte Directory, int Track) {
  if (Directory > 101) { //Not permitted operation, exit this function.
    return;
  }

  byte command;
  byte para1_highbyte;
  byte para2_lowbyte;

  //Process parameters
  if (Directory == 0) {
    //Play from ROOT folder.
    command = 0x03;
    para1_highbyte = (Track >> 8) & 0xFF;
    para2_lowbyte = Track & 0xFF;
  } else if (Directory < 100) {
    //Play from selected folder. "01" to "99".
    command = 0x0F;
    para1_highbyte = Directory;
    para2_lowbyte = Track;
  } else if (Directory == 100) {
    //Play from "MP3" folder.
    command = 0x12;
    para1_highbyte = (Track >> 8) & 0xFF;
    para2_lowbyte = Track & 0xFF;
  } else if (Directory == 101) {
    //Play Advert
    command = 0x13;
    para1_highbyte = (Track >> 8) & 0xFF;
    para2_lowbyte = Track & 0xFF;
  }

  //Send processed parameters
  MP3SendCommand(command, para1_highbyte, para2_lowbyte);
}

No. The general advice is not to initialise any hardware in the constructor because the object will be created before the hardware is ready

Rather, have an init() or similar function called from setup() so that you can be sure that the hardware is ready when it is executed

2 Likes

The common convention in the Arduino world is begin()

1 Like

Alright, so in my case I should create a function GameCode.begin() containing this code.

My hardware is static, by which I mean that connections will never change. So using the constructor to specify pin connection (like it is shown in the library tutorial) is not needed. Should I use the constructor for something else, or can I leave it empty?

There is no harm in specifying pin numbers in the constructor, in fact I think that it makes sense if you instantiate more than one object of the type. That way it is more obvious which resources each object uses right at the start of the program

1 Like

There will never be more than one object of the type, because all the hardware is covered in this one “GameCode” type. Adding extra hardware to the device is not possible, and not supported by the library I’m writing. Letting the users edit the pin connections can only lead to problems I think :slight_smile:

If there is no choice of which pin numbers to use then there are, of course, no pin number parameters to pass, so no discussion is needed as to where and how they should be passed

Just don’t set their pinMode()s in the constructor !

1 Like

Alright, thanks @UKHeliBob and @pert! My questions are answered… For now :wink:

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