Writing libraries in C (no objects)

Hello,
I’m working on a current project that would benefit from the use of libraries to get some room in main.
Everything works pretty well in one file, but when I try to turn my functions into libraries, I’m having a few issues. (library is for a special case OLED Character screen).
Scope:
-I’m just writing a .c file and a .h file.
-Both files are stored in the same directory as my .ino file, and I’m using " " for my #includes, instead of < >
-I have included “Arduino.h” in my .c library file.

There’s way to much to post in as far as code goes, but the main error message I’m getting is :

ccpNAShc.ltrans0.o:(.text+0x890): undefined reference to `outputS(char*, int)’

where “outputS(char*, int)” is one of the functions in my library.
I’m getting this message for every line I’ve called the function on in main, (or loop? I guess)
So I’m pretty sure the compiler is having trouble with my include statement.
I haven’t tried a direct path, but this is hopefully going to be an open source project, so that would be unfortunate.

All the library tutorials I’ve seen have been in C++ with objects in the main file, is there no way around this? OOP just really seems like overkill for something that you’re not needing more than one instance of.

-Thanks for reading!

Do you have a function with the outputS(char*, int) signature in your .h and .c or .cpp ?

With nothing posted we can’t help you much and very few members will spend time in guess work —> post/attach your code

Probably you are missing the "extern "C" { }" construction in your .h file. When included in the .ino (which is C++), it will assume "mangled" symbol names that encode the signature (types of the arguments...), but compiling the .c file will produce non-mangled symbols.

The easiest solution is to rename your .c file to .cpp (it can still contain C-only code.)

westfw:
Probably you are missing the "extern "C" { }" construction in your .h file. When included in the .ino (which is C++), it will assume "mangled" symbol names that encode the signature (types of the arguments...), but compiling the .c file will produce non-mangled symbols.

The easiest solution is to rename your .c file to .cpp (it can still contain C-only code.)

The extern "C"{} was what I was missing!
adding that in around the include statment fixed everything.
Thanks!

First, it makes sense to have the file with setup() and loop() named ?.ino. Another other file that contains code should be ?.cpp. The third file type should be ?.h. I'm not a big fan of making every file an ino file because you lose type checking between files.

Second, there's a huge misunderstanding of what define versus declare means and programmers are pretty sloppy about it. Suppose you define a global variable in the home.ino file, such as:

int myInteger;

but now you want to use it in a different file support.cpp. If you write something like:

  • myInteger = 10;*

in the support.cpp file, it draws an error telling you it's not declared in the file. The reason is because you defined it in the home.ino file. However, if you place this line at the top of your support.cpp file:

  • extern int myInteger;*

in essence that is saying: "Mr. Compiler, let me use this variable in this file as an int with the name myInteger even though it's not defined in this file." The extern keyword allows you to create a description of a variable (i.e., its attribute list from the symbol table) but without allocating storage for it. This is what declare means: creating an attribute list for a variable without creating storage for it. You defined the variable in home.ino because the compiler allocated storage for it. It's the linker's responsibility to clean all of this up by taking the memory address for myInteger as defined in home.ino and filling the external references to myInteger in the support.cpp with its actual memory address.

In short, data definitions create an attribute list for a variable and allocate storage for it, whereas a data declaration simply creates an attribute list so you can use it in a file that doesn't yet have knowledge of its memory address. The extern keyword is used with data declarations only.

econjack:
I'm not a big fan of making every file an ino file because you lose type checking between files.

Agreed. You also loose file-level scope control. Seems like a bad idea all around. Too bad this technique seems to be recommended by official Arduino documentation.

econjack:
I’m not a big fan of making every file an ino file because you lose type checking between files.

Although I prefer .cpp and .h files, I do not understand the part that I marked in bold.

One of the first things the arduino builder does is concatenate all .ino files into one big ino file (with extension .cpp). That’s the file that is compiled; below an example.

#include <Arduino.h>
#line 1 "C:\\Users\\sterretje\\Documents\\Arduino\\multiple_ino\\multiple_ino.ino"
#line 1 "C:\\Users\\sterretje\\Documents\\Arduino\\multiple_ino\\multiple_ino.ino"
#line 1 "C:\\Users\\sterretje\\Documents\\Arduino\\multiple_ino\\multiple_ino.ino"
void setup();
#line 6 "C:\\Users\\sterretje\\Documents\\Arduino\\multiple_ino\\multiple_ino.ino"
void loop();
#line 1 "C:\\Users\\sterretje\\Documents\\Arduino\\multiple_ino\\another.ino"
void printWelcome();
#line 1 "C:\\Users\\sterretje\\Documents\\Arduino\\multiple_ino\\multiple_ino.ino"
void setup()
{
  printWelcome();
}

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

}

#line 1 "C:\\Users\\sterretje\\Documents\\Arduino\\multiple_ino\\another.ino"
void printWelcome()
{
  Serial.println("Welcome");
}