20
votes

I have an Arduino Uno and a Linux environment. Though the Arduino IDE is great and all, however it doesn't give me much inputs if something goes wrong. And it also becomes excruciatingly slow and stops communicating with the chip sometimes.

The alternative is to code my Arduino Uno using the AVR-GCC/g++ and AVRDude toolchain. However, I would very much like to have access to the same functions as in Arduino (like Serial, digitalWrite, etc.) while programming using a simple text editor and command line tools.

So I tried to find the header file with all the functions defined ie "Arduino.h" in /usr/share/arduino/hardware/arduino/cores/arduino.

I used this for compiling (using a makefile to do this)

avr-gcc -mmcu=atmega328p -I. -gdwarf-2 -DF_CPU=16000000UL -Os -funsigned-char -funsigned-bitfields -fpack-struct -fshort-enums -Wall -Wstrict-prototypes -Wa,-adhlns=testarduino.o -I/usr/share/arduino/hardware/arduino/cores/arduino -I/usr/share/arduino/hardware/arduino/variants/standard -std=gnu99 -MMD -MP -MF .dep/testarduino.elf.d testarduino.o --output testarduino.elf -Wl,-Map=testarduino.map,--cref     -lm

to compile this code:

#include "Arduino.h"

int main(void)
{
    init();
    pinMode(13,OUTPUT);
    while(1){
    digitalWrite(13,HIGH);
    delay(1000);
    digitalWrite(13,LOW);
    delay(1000);
    }
    return 0;
}

But none of the functions seem to be identified when I try to compile (saying undefined reference to 'xxxxxxxxx'). So what is the exact header file that needs to be included to make sure I can use the same functions? Or is there something else I missed?

3
See my answer to a similar question: linkFarK
Also, you can use the Serial class from Arduino easily. I adapted it for my Final Carrier Project (and made another one for USART comunication), you can see the code here: linkFarK

3 Answers

21
votes

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.

5
votes

I use a makefile for Arduino, which allows me to both user as well as system libraries. You don't have to use the Arduino IDE, but at the same time, can use the libraries that are provided by it.

1
votes

You might want to set the Arduino IDE compile options to verbose. Then compile something and watch the IDE's output. It will tell you where it locates the .cpp file and which compiler options it uses. Once you look into the .cpp file you see immediately what you need to include.

After that the makefiles become much easier to understand. If you have no prior experience with make my advice would be to avoid it and go for scons instead. Scons is written Python. Thus you immediately benefit from much simpler debugging.