How to get the definitions for undefined reference
The message undefined reference to 'xxxxxxxxx'
means that the linker can't find the actual definition (not just declaration) for the function in question.
When the linker can't find a definition for a function, that definition is typically to be found in a lib file (e.g *.so or *.dll) or in a source file (e.g. *.cpp or *.c). For your Arduino, you need to tell avr-gcc to compile and link the Arduino *.cpp files. You need all the source files from the Arduino core.
ARDCOREDIR = <my arduino folder>/hardware/arduino/cores/arduino
Sudar gave good advice, advocating the use of a Makefile for AVRs which is configured to include the Arduino function definitions. (I will link to mine and explain it below.)
In my makefile, I handled this operation as follows:
# CPP_SOURCE_FILES is a previously-defined variable, holding my project's source files
CPP_SOURCE_FILES += $(filter-out $(ARDCOREDIR)/main.cpp, $(wildcard $(ARDCOREDIR)/*.cpp))
Observe that I omitted the file main.cpp because the Arduino main.cpp file defines main()
, and I wish to define it myself in my own project files.
Necessary header files
Of course, you must also pass avr-gcc an include flag for the arduino headers:
-I$(ARDCOREDIR)
And you must include the Ardhuino.h file in your actual project code:
#include <Arduino.h>
The Arduino.h header file will include all of the other Arduino header files, including the pinout file, which holds definitions for the Arduino pins. Different Arduinos must have different pinout files, so identify which type you are using, and tell avr-gcc to include the directory containing the appropriate pinout file. If you are using a standard Arduino (UNO), use the following:
-I$(ARDCOREDIR)/../../variants/standard
My Makefile
Not only does your build process need to implement the several instructions described above, it needs to know how to make the .hex file that shall be programmed onto your Arduino (or AVR). Therefore, it is a good idea to use a makefile.
I don't like to rebuild my makefile for each project, so I just have one base makefile on my machine, and each project has its own, very short makefile which calls include
to include the base makefile. You can find my base makefile here.
Example of a short (non-base) Makefile for a single project on my machine:
# This is the name of the file that shall be created. (It is also the name of my primary source file, without the file extension.)
TARGET = temp
# create a variable that only pertains to this project
MY_OWN_LIBRARY_DIR = /usr/home/MJ/Arduino/libraries/mj_midi
# "EXTRAINCDIRS" is a variable used by the base makefile. The base makefile creates a -I compiler flag for every item in the "EXTRAINCDIRS" list.
EXTRAINCDIRS = $(MY_OWN_LIBRARY_DIR)
# specify *.c source files pertaining to my project
SRC =
# specify *.cpp source files pertaining to my project
PSRC = temp.cpp $(MY_OWN_LIBRARY_DIR)/midi-listener.cpp $(MY_OWN_LIBRARY_DIR)/midi-song.cpp
# specify additional (non-core) Arduino libraries to include
ARDLIBS = SoftwareSerial
# include my base makefile
include /d/dev/avr/Makefile.base
NB: If you're going to use my base makefile, make sure that you set the variable ARDDIR
(which appears near the top of the file) to point to the Arduino directory on your machine.