Beginner asking for help creating library and header files

I am using an Arduino to send signals to an old Microcomputer Trainer as a way to replace keying in programs via the 20 keys on the Trainer. An example of a function in my Arduino program for a code/command sent to the trainer from the Arduino is like this:

void cy() {
  digitalWrite(ARDUINO_PIN_4, HIGH);
  delay(interval);                           // Delay half-a-second
  digitalWrite(ARDUINO_PIN_4, LOW); // Set the I/O low
  delay(interval);
}

I also use the Sparkfun SX1509 IO expander in order to get 20 pins with my Uno. An example of a function using the SX1509 pins is like this:

void ka() {
  io.digitalWrite(SX1509_PIN_15, HIGH);
  delay(interval);                           // Delay half-a-second
  io.digitalWrite(SX1509_PIN_15, LOW); // Set the I/O low
  delay(interval);
}

I suppose I could include the 354 lines like the ones above in every program, but it seems like a header and library that has these functions would be better. However, the problem is I am a total beginner with creating them from scratch.

So far I have read this tutorial: https://docs.arduino.cc/learn/contributions/arduino-creating-library-guide and it looks pretty similar to what I need where a class is created like this:

class Morse
{
  public:
    Morse(int pin);
    void dot();
    void dash();
  private:
    int _pin;
};

But I am not clear on:

  1. the "int pin". Do I need this if every function has it's own defined pin like "ARDUINO_PIN_4"

  2. If I can use something like "ARDUINO_PIN_4" , where do I define it in the header?

  3. How about the "io.digitalWrite(SX1509_PIN_15, HIGH);"? Do I just do it the same way? Do I have to include the lines from my program that are requried for the SX1509 in the header file as well?

#include <Wire.h>           // Include the I2C library (required)
#include <SparkFunSX1509.h> //Click here for the library: http://librarymanager/All#SparkFun_SX1509

// SX1509 I2C address (set by ADDR1 and ADDR0 (00 by default):
const byte SX1509_ADDRESS = 0x3E; // SX1509 I2C address
SX1509 io;

Thank you for any guidance on this!

The .h File for this Simple_Class

Simple_Class.h

#ifndef Simple_Class_h
#define Simple_Class_h
class Simple_Class {
  public:
    uint8_t Val;
    Simple_Class(uint8_t DefaultVal = 0); // Constructor
    Simple_Class & SetValue(uint8_t v);
    uint8_t CheckValue();
};
#endif

Simple_Class.cpp

#include "Simple_Class.h"
Simple_Class::Simple_Class(uint8_t DefaultVal) {
  Val = DefaultVal;
}

Simple_Class &  Simple_Class::SetValue(uint8_t v) {
  Val = v;
  return *this;
}

uint8_t  Simple_Class::CheckValue() {
  return Val;
}

The Sketch:

#include "Simple_Class.h"

Simple_Class MyClass(10);

void setup() {
  Serial.begin(115200); //115200
  Serial.println(" Simple_Class Testing");
  Serial.print("Default Value: ");
  Serial.println(MyClass.CheckValue());
  MyClass.SetValue(100);
  Serial.println(MyClass.CheckValue());
  Serial.println(MyClass.SetValue(200).CheckValue()); // using Simple_Class &  pointer and return *this in a function of a class allows you to do stuff like this.
}

void loop() {
  // put your main code here, to run repeatedly:

}

Z

You can hard code the pin numbers in your .cpp file.
The idea of making classes is that you can reuse them in new situations (with different pins). For you that is not really important now.

Why do you make a class anyway?
You can also put a function in a .cpp file..
That might be easier for you.

Ah see I didn't realize that; I was going by the example. That makes more sense now - thanks.
Will I have to include the "wire.h" for the SX1509 so that the .cpp knows what the io.digitalwrite statement is referring to?

I would start my moving the functions that you want to be in your library into a separate .cpp file in your sketch folder. This will also require a .h file, containing the declaration of those functions. Gradually move "stuff" from the sketch to the .cpp/.h file until you are happy with the degree to which functionality is "separated."
THEN you can worry about making the file/directory structure compatible with being stored as an Arduino library, rather than .cpp/.h in each sketch file that uses it. (although that latter scheme IS a possibility, as well.)

Thanks - I have sort of done that. The situation now is that I don't get any compilation errors but with the functions and definitions broken out into the .cpp/.h files nothing happens at all, as opposed to when everything is in a single .ino and the program correctly sends signal to the Trainer.. So I don't have something correct just yet.

A very abridged version of these files:
Kepress.h

#ifndef Keypress_h
#define Keypress_h

// the #include statements and code goes here...
#include "Arduino.h"

 void initWire();
 void setPinsLow();
 void ch();
 void cy();
 void incr();
#endif

Keypress.cpp

#define uint8_t byte
#include <Keypress.h>
#include <Wire.h>           // Include the I2C library (required)
#include <SparkFunSX1509.h> //Click here for the library: http://librarymanager/All#SparkFun_SX1509
// SX1509 I2C address (set by ADDR1 and ADDR0 (00 by default):

const byte SX1509_ADDRESS = 0x3E; // SX1509 I2C address
SX1509 io;                        // Create an SX1509 object to be used throughout

// Key definition:
const byte SX1509_PIN_0 = 0; ///////////////////////////////
const byte SX1509_PIN_1 = 1; //

const byte ARDUINO_PIN_2 = 2; // /////////////////////////
const byte ARDUINO_PIN_3 = 3; //

const long interval = 500;

void initWire() {
  Serial.println("I am starting here.");
  Wire.begin();
  // Call io.begin(<address>) to initialize the SX1509. If it
  // successfully communicates, it'll return 1.
  if (io.begin(SX1509_ADDRESS) == false)
  {
    Serial.println("What we have here is a failure to communicate. Check your wiring and address of SX1509.");
    while (1)
      ; // If we fail to communicate, loop forever.
  }
}

void setPinsLow() {
  io.digitalWrite(SX1509_PIN_0, LOW); // Set SX1509 pins low
  io.digitalWrite(SX1509_PIN_1, LOW); //
//
  digitalWrite(ARDUINO_PIN_2, LOW); // Set Arduino pins low
  digitalWrite(ARDUINO_PIN_3, LOW); //
  delay(interval);

}


void ch() {
  io.digitalWrite(SX1509_PIN_0, HIGH);
  delay(interval);                           // Delay half-a-second
  io.digitalWrite(SX1509_PIN_0, LOW); // Set the I/O low
  delay(interval);
}

void cy() {
  digitalWrite(ARDUINO_PIN_2, HIGH);
  delay(interval);                           // Delay half-a-second
  digitalWrite(ARDUINO_PIN_2, LOW); // Set the I/O low
  delay(interval);

void incr() {
  io.digitalWrite(SX1509_PIN_9, HIGH); //Increment in between first & second half of code
  delay(interval);
  io.digitalWrite(SX1509_PIN_9, LOW);
  delay(interval);


myProgramToSendKeypresses.ino

#include <Keypress.h>

/*
  Hardware Hookup:
  SX1509 Breakout ------ Arduino -------- Breadboard
      GND -------------- GND
      3V3 -------------- 3.3V
      SDA ------------ SDA (A4)
      SCL ------------ SCL (A5)
*/

void setup() {
  // put your setup code here, to run once:
Serial.println("I am in setup.");
}

void stop() {
  while (1);
}

void loop() {
  // put your main code here, to run repeatedly:
  
  initWire();
  Serial.println("I am here.");
  setPinsLow();
  
  ch(); 
  cy();
  incr();

 while(1);

} 

You have no "Serial.begin()", so it isn't surprising that you don't see any of the debug messages...

void cy() {
  digitalWrite(ARDUINO_PIN_2, HIGH);
  delay(interval);                           // Delay half-a-second
  digitalWrite(ARDUINO_PIN_2, LOW); // Set the I/O low
  delay(interval);
} // <<< Missing
void incr() {
  io.digitalWrite(SX1509_PIN_9, HIGH); //Increment in between first & second half of code
  delay(interval);
  io.digitalWrite(SX1509_PIN_9, LOW);
  delay(interval);

} //<<<< missing

Thanks: I copied and pasted those functions in an abridged format for here in a hurry and left those off. They do indeed have the closing braces in the full version.

Does everything else look ok otherwise (minus the closing braces)?

made it all into one sketch and it compiled after adding
const byte SX1509_PIN_9 = 9;

Sketch:

#ifndef Keypress_h
#define Keypress_h
// the #include statements and code goes here...
#include "Arduino.h"
#include <Wire.h>           // Include the I2C library (required)
#include <SparkFunSX1509.h> //Click here for the library: http://librarymanager/All#SparkFun_SX1509

 void initWire();
 void setPinsLow();
 void ch();
 void cy();
 void incr();
#endif

//-----------------------------------------------


#define uint8_t byte
//#include <Keypress.h>
// SX1509 I2C address (set by ADDR1 and ADDR0 (00 by default):

const byte SX1509_ADDRESS = 0x3E; // SX1509 I2C address
SX1509 io;                        // Create an SX1509 object to be used throughout

// Key definition:
const byte SX1509_PIN_0 = 0; ///////////////////////////////
const byte SX1509_PIN_1 = 1; //
const byte SX1509_PIN_9 = 9;

const byte ARDUINO_PIN_2 = 2; // /////////////////////////
const byte ARDUINO_PIN_3 = 3; //

const long interval = 500;

void initWire() {
  Serial.println("I am starting here.");
  Wire.begin();
  // Call io.begin(<address>) to initialize the SX1509. If it
  // successfully communicates, it'll return 1.
  if (io.begin(SX1509_ADDRESS) == false)
  {
    Serial.println("What we have here is a failure to communicate. Check your wiring and address of SX1509.");
    while (1)
      ; // If we fail to communicate, loop forever.
  }
}

void setPinsLow() {
  io.digitalWrite(SX1509_PIN_0, LOW); // Set SX1509 pins low
  io.digitalWrite(SX1509_PIN_1, LOW); //
//
  digitalWrite(ARDUINO_PIN_2, LOW); // Set Arduino pins low
  digitalWrite(ARDUINO_PIN_3, LOW); //
  delay(interval);

}


void ch() {
  io.digitalWrite(SX1509_PIN_0, HIGH);
  delay(interval);                           // Delay half-a-second
  io.digitalWrite(SX1509_PIN_0, LOW); // Set the I/O low
  delay(interval);
}

void cy() {
  digitalWrite(ARDUINO_PIN_2, HIGH);
  delay(interval);                           // Delay half-a-second
  digitalWrite(ARDUINO_PIN_2, LOW); // Set the I/O low
  delay(interval);
}
void incr() {
  io.digitalWrite(SX1509_PIN_9, HIGH); //Increment in between first & second half of code
  delay(interval);
  io.digitalWrite(SX1509_PIN_9, LOW);
  delay(interval);

}


//-----------------------------------------------


//#include <Keypress.h>

/*
  Hardware Hookup:
  SX1509 Breakout ------ Arduino -------- Breadboard
      GND -------------- GND
      3V3 -------------- 3.3V
      SDA ------------ SDA (A4)
      SCL ------------ SCL (A5)
*/

void setup() {
  // put your setup code here, to run once:
Serial.println("I am in setup.");
}

void stop() {
  while (1);
}

void loop() {
  // put your main code here, to run repeatedly:
  
  initWire();
  Serial.println("I am here.");
  setPinsLow();
  
  ch(); 
  cy();
  incr();

 while(1);

} 

Serial.begin is still missing in setup...

Yes I can't figure out why it won't work in 3 separate files?

Ughh...finally found it by printing out hard-copies of my header files and the original all in one version to compare. I could not see my mistake on the screen! I did not include the lines to set the pins to output mode in the .cpp file:

  io.pinMode(SX1509_PIN_0, OUTPUT); ////////////////////////////
  io.pinMode(SX1509_PIN_1, OUTPUT); //
  io.pinMode(SX1509_PIN_2, OUTPUT); //
  io.pinMode(SX1509_PIN_3, OUTPUT); //

  io.pinMode(SX1509_PIN_4, OUTPUT); //
  io.pinMode(SX1509_PIN_5, OUTPUT); //
  io.pinMode(SX1509_PIN_6, OUTPUT); //
  io.pinMode(SX1509_PIN_7, OUTPUT); // Set SX1509 pins to OUTPUT

  io.pinMode(SX1509_PIN_8, OUTPUT); //
  io.pinMode(SX1509_PIN_9, OUTPUT); //
  io.pinMode(SX1509_PIN_10, OUTPUT); //
  io.pinMode(SX1509_PIN_11, OUTPUT); //

  io.pinMode(SX1509_PIN_12, OUTPUT); //
  io.pinMode(SX1509_PIN_13, OUTPUT); //
  io.pinMode(SX1509_PIN_14, OUTPUT); //
  io.pinMode(SX1509_PIN_15, OUTPUT); ///////////////////////////

  pinMode( ARDUINO_PIN_2, OUTPUT); /////////////////////////////
  pinMode( ARDUINO_PIN_3, OUTPUT); /////////////////////////////
  pinMode( ARDUINO_PIN_4, OUTPUT); /////////////////////////////
  pinMode( ARDUINO_PIN_5, OUTPUT); /////////////////////////////

Thanks for the help here!!

Share with us your working sketch with all the functions in 1 file and let's see if we can break them into 3 .h .cpp and sketch.
Z

Thanks - as it turns out I did get it all working with 3 files but I will include them here anyway just for any other suggestions anyone might have to improve it. Otherwise, thanks again!

trainer-led-l-to-r.ino (679 Bytes)
Keypress.h (1002 Bytes)
Keypress.cpp (14.3 KB)