Help needed: How to make a library from a .ino-file?

Hi,

I am doing a little project which involve an intelligent TFT display from Electronic Assembly and I have written a whole bunch of convenience functions to control this display, like clear screen, draw line, draw rectangle, etc.
At the moment, this is just another .ino-file in my sketch, but I would like to make it a "real library" and publish it under the GPL.

However, I'm no developer, meaning, I don't have any formal coding training or huge experience. I have no idea how to make a library from a .ino-file.

Could somebody gice some advice how to make a library or point me to some tutorial?

-Stefan

Great, thanks!

Question:
How do I call a library function from inside the library? With the CLASSNAME:: in front or without?

Like this?

void EDIPTFT::sendData(char* data, byte len) {
  if (_smallprotocol) {
  }
  else {
    unsigned char i;
    for(i=0; i < len; i++) {
      Serial3.write(data[i]);
    }
  }
}

void EDIPTFT::Clear() {
  char command [] = {12};
  EDIPTFT::sendData(command,1);
}

How do I call a library function from inside the library? With the CLASSNAME:: in front or without?

That depends on whether the function is a function or a method. A method is a function defined in a class. A method can be public, private, or static.

Answering your question for a specific function or method is easy, if we know what the class definition looks like.

Answering generic questions is a lot more difficult.

I posted the code above. There is the function sendData() which actually transmits the commands to the TFT and is called by any other function in the library.
Basically, all functions just put together a command string and then call sendData.

Edit:
sendData() is a public function because it should also be possible to call it from external in case there is a command which I didn't implement in the lib yet.

Edit2:

I posted the code above.

You posted some of the source code (the .cpp file), but nothing of the header file (where the class, if there is one, is defined).

Ah, ok...

class EDIPTFT {
  public:
    EDIPTFT(int port);
    void sendData(char* data, int len);
    void clear();
    void invert();
    void setDisplayColor(char fg, char bg);
    void fillDisplayColor(char bg);
    void terminalOn(boolean on);
    void cursor(boolean on);
    void setCursor(char col, char row);
    void defineBargraph(char dir, char no, int x1, int y1, int x2, int y2, byte sv, byte ev, char type);
    void updateBargraph(char no, char val);
    void setBargraphColor(char no, char fg, char bg, char fr);
    void makeBargraphTouch(char no);
    void linkBargraphLight(char no);
    void deleteBargraph(char no,char n1);
    void setLine(char fg, char bg);
    void setTextColor(char fg, char bg);
    void setTextFont(char font);
    void setTextAngle(char angle);
    void drawText(int x1, int y1, char justification,String text);
    void drawLine(int x1, int y1, int x2, int y2);
    void drawRect(int x1, int y1, int x2, int y2);
    void Rectf(int x1, int y1, int x2, int y2, char color);
    void defineTouchKey(int x1, int y1, int x2, int y2, char down, char up, String text);
    void setTouchkeyColors(char n1, char n2, char n3, char s1, char s2, char s3);
    void setTouchkeyFont(char font);
    void setTouchkeyLabelColors(char nf,char sf);
    void removeTouchArea(char code,char n1);
  private:
    int _port;
    boolean _smallprotocol;

Some of the methods in the class should probably be private, instead of all of them being public.

sendData() is a method the belongs to a specific instance of a class. As opposed to a static method that is shared by all instances of the class.

Therefore, to call it from Clear(), just use the method name. The ClassName:: bit up front, called a scope resolution operator, is not needed.

void EDIPTFT::Clear()
{
  char command [] = { 12 };
  sendData(command, 1);
}

The space key is not important to the compiler, usually, but it does make for more readable code.

There are some other things you could do. command does not need to be an array to hold one value. You could use:

char cmd = 12;
sendData(&cmd, 1);

But, if you wish to keep it an array, for consistency with other functions where the array contains more than one element, you should use the sizeof() macro, and let the compiler do the counting:

sendData(command, sizeof(command));

That way, if you copy/paste the function and change the size of the array, you don't need to count the number of elements in the new array.

Thanks a lot!

I was avoiding sizeOf() yet because I'm not sure how it reacts to null elements, i.e. if it sees null elements as terminators and thus may deliver wrong results.

Which functions do you think should be private? The only function I could think of would be sendData() but I kept it yet public intentionally because I haven't implemented functions for all commands yet. So, this way, the user can still use sendData() to send commands "by hand". This will become especially important when I succeeded implementing EA's proprietary smallprotocol which includes some checksum calculations and such.

Now I ran into a problem which doesn't mean anything to me...

I want to pass the serial port to use for comms to the library in a parameter. In sendData(), I then check this parameter and decide, which Serial to use:

EDIPTFT::EDIPTFT(int port, int smallprotocol) {
  _port = port;
  _smallprotocol = smallprotocol;
}

void EDIPTFT::sendData(char* data, int len) {
  if (_smallprotocol > 0) {
  }
  else {
    unsigned char i;
    for(i=0; i < len; i++) {
      switch (_port) {
	case 0 : {
	  Serial.write(data[i]);
	  break;
	}
	case 1 : {
	  Serial1.write(data[i]);
	  break;
	}
	case 2 : {
	  Serial2.write(data[i]);
	  break;
	}
	case 3 : {
	  Serial3.write(data[i]);
	  break;
	}
      }
    }
  }
}

I use this library from a sketch that also uses the FastSerial library. Now, when I try to compile everything, I get this errors:

core.a(HardwareSerial.cpp.o): In function `global constructors keyed to rx_buffer':
/opt/arduino-1.0.3/hardware/arduino/cores/arduino/HardwareSerial.cpp:515: multiple definition of `__vector_25'
UxV_CS.cpp.o:/opt/arduino-1.0.3/UxV_CS.ino:79: first defined here
/opt/arduino-1.0.3/hardware/tools/avr/bin/../lib/gcc/avr/4.3.2/../../../avr/bin/ld: Disabling relaxation: it will not work with multiple definitions
core.a(HardwareSerial.cpp.o): In function `global constructors keyed to rx_buffer':
/opt/arduino-1.0.3/hardware/arduino/cores/arduino/HardwareSerial.cpp:515: multiple definition of `__vector_36'
UxV_CS.cpp.o:/opt/arduino-1.0.3/UxV_CS.ino:79: first defined here
core.a(HardwareSerial.cpp.o): In function `global constructors keyed to rx_buffer':
/opt/arduino-1.0.3/hardware/arduino/cores/arduino/HardwareSerial.cpp:515: multiple definition of `__vector_51'
UxV_CS.cpp.o:/opt/arduino-1.0.3/UxV_CS.ino:79: first defined here
core.a(HardwareSerial.cpp.o): In function `global constructors keyed to rx_buffer':
/opt/arduino-1.0.3/hardware/arduino/cores/arduino/HardwareSerial.cpp:515: multiple definition of `__vector_54'
UxV_CS.cpp.o:/opt/arduino-1.0.3/UxV_CS.ino:79: first defined here
core.a(HardwareSerial.cpp.o): In function `global constructors keyed to rx_buffer':
/opt/arduino-1.0.3/hardware/arduino/cores/arduino/HardwareSerial.cpp:515: multiple definition of `Serial'
UxV_CS.cpp.o:/opt/arduino-1.0.3/UxV_CS.ino:79: first defined here
/opt/arduino-1.0.3/hardware/tools/avr/bin/../lib/gcc/avr/4.3.2/../../../avr/bin/ld: Warning: size of symbol `Serial' changed from 29 in UxV_CS.cpp.o to 34 in core.a(HardwareSerial.cpp.o)
core.a(HardwareSerial.cpp.o): In function `global constructors keyed to rx_buffer':
/opt/arduino-1.0.3/hardware/arduino/cores/arduino/HardwareSerial.cpp:515: multiple definition of `Serial1'
UxV_CS.cpp.o:/opt/arduino-1.0.3/UxV_CS.ino:79: first defined here
/opt/arduino-1.0.3/hardware/tools/avr/bin/../lib/gcc/avr/4.3.2/../../../avr/bin/ld: Warning: size of symbol `Serial1' changed from 29 in UxV_CS.cpp.o to 34 in core.a(HardwareSerial.cpp.o)
core.a(HardwareSerial.cpp.o): In function `global constructors keyed to rx_buffer':
/opt/arduino-1.0.3/hardware/arduino/cores/arduino/HardwareSerial.cpp:515: multiple definition of `Serial2'
UxV_CS.cpp.o:/opt/arduino-1.0.3/UxV_CS.ino:79: first defined here
/opt/arduino-1.0.3/hardware/tools/avr/bin/../lib/gcc/avr/4.3.2/../../../avr/bin/ld: Warning: size of symbol `Serial2' changed from 29 in UxV_CS.cpp.o to 34 in core.a(HardwareSerial.cpp.o)
core.a(HardwareSerial.cpp.o): In function `global constructors keyed to rx_buffer':
/opt/arduino-1.0.3/hardware/arduino/cores/arduino/HardwareSerial.cpp:515: multiple definition of `Serial3'
UxV_CS.cpp.o:/opt/arduino-1.0.3/UxV_CS.ino:79: first defined here
/opt/arduino-1.0.3/hardware/tools/avr/bin/../lib/gcc/avr/4.3.2/../../../avr/bin/ld: Warning: size of symbol `Serial3' changed from 29 in UxV_CS.cpp.o to 34 in core.a(HardwareSerial.cpp.o)
core.a(HardwareSerial.cpp.o): In function `global constructors keyed to rx_buffer':
/opt/arduino-1.0.3/hardware/arduino/cores/arduino/HardwareSerial.cpp:515: multiple definition of `__vector_26'
UxV_CS.cpp.o:/opt/arduino-1.0.3/UxV_CS.ino:79: first defined here
core.a(HardwareSerial.cpp.o): In function `global constructors keyed to rx_buffer':
/opt/arduino-1.0.3/hardware/arduino/cores/arduino/HardwareSerial.cpp:515: multiple definition of `__vector_37'
UxV_CS.cpp.o:/opt/arduino-1.0.3/UxV_CS.ino:79: first defined here
core.a(HardwareSerial.cpp.o): In function `global constructors keyed to rx_buffer':
/opt/arduino-1.0.3/hardware/arduino/cores/arduino/HardwareSerial.cpp:515: multiple definition of `__vector_52'
UxV_CS.cpp.o:/opt/arduino-1.0.3/UxV_CS.ino:79: first defined here
core.a(HardwareSerial.cpp.o): In function `global constructors keyed to rx_buffer':
/opt/arduino-1.0.3/hardware/arduino/cores/arduino/HardwareSerial.cpp:515: multiple definition of `__vector_55'
UxV_CS.cpp.o:/opt/arduino-1.0.3/UxV_CS.ino:79: first defined here

This is way out of my league... What do I do now?

The keywords public, protected and private are meant for capsulating or hiding the members. Public is the interface part that is accessible from outside. I tend to define other functions that are not necessarily safe to call from outside in the protected part. These are the kind of functions that are called by the implementation.

I guess it's just a habit that I also leave the private part for member variables, not function, that then have accessor functions in the public part if necessary. In some rare cases, I do also write private functions, but I have meant to only use them for filling some member arrays and only meant to be called by the other functions in the same class. These wouldn't necessarily be safe to call even from inherited classes, strictly internal logic.

Anyways, if you think you need access to a function, just raise it to public level. Separating the functions to public and protected sections also gives other coders hints towards the usage of the design. Coders would just look at the public functions with comments in the header file and should be able to tell how to use it from that. Too bad Arduino IDE does not give any popup hints when you try and access the functions within a class instance, so you need to actually open the header file to see what's in there or rely on documentation anyways.

SierraWhiskey:
Now I ran into a problem which doesn't mean anything to me...

core.a(HardwareSerial.cpp.o): In function global constructors keyed to rx_buffer': /opt/arduino-1.0.3/hardware/arduino/cores/arduino/HardwareSerial.cpp:515: multiple definition of __vector_55'
UxV_CS.cpp.o:/opt/arduino-1.0.3/UxV_CS.ino:79: first defined here

This is way out of my league... What do I do now?

Maybe the same header file is getting included twice. I'm not familiar with FastSerial or Arduino in general, yet, but in all other kinds of projects we avoid including the headers twice by adding some macro definition in the beginning of the said header file.

For example, picked this for LiquidCrystal library:

#ifndef LiquidCrystal_I2C_h
#define LiquidCrystal_I2C_h
...
#endif

So, at the start of your library header file, it should say something like that, but using some other unique string in place of LiquidCrystal_I2C_h. Same goes for other header files, and they should also have their own unique id strings. And the header file should end with #endif on a new line. This avoid duplicate inclusion.

Not seeing the rest of your code, I'm assuming something like this has happened here.

I have this...

#ifndef EDIPTFT_h
#define EDIPTFT_h

See GitHub - sgofferj/EDIPTFT: Arduino library for controll Electronic Assembly eDIPTFT displays

Oh, ok, that looks ok to me, and I can compile it just fine too with my own bare bones sketch, that includes HardwareSerial. I had to comment out Serial1-3 in this system. It did compile for Mega with those lines uncommented. The error does sound like some kind of dependency mix up though.

May I jump in here with a related question? I have been playing with a TM1638 LED display (8 characters 7 segments using I2C) and encountered the sendData function. I have searched the header files from the library, the .cpp files, even did a search of my whole Arduino directory for ". containing sendData". I have been unable to find the body of the function, where the actual code resides. The prototype is in the header files, the class is instantiated in the .cpp files but nowhere could I find the function. The code compiles and uploads and runs with no problems; I just can't find what exactly it is doing. Since you seem to be dealing with the same function I hope you could point me to where I could study the function so I could understand what it is doing in more than a general sense like "is sends the data that has been readied."
Any help would be appreciated.
Thanks

Are you talking about the sendData() method shown in reply #2? If so, then it should be fairly obvious where the method is implemented.

The #ifndef thing, to prevent the header being included twice, needs an #endif at the end of the header file.