Skip to content

Instantly share code, notes, and snippets.

@sjmeverett
Created December 16, 2012 19:57
Show Gist options
  • Save sjmeverett/4312170 to your computer and use it in GitHub Desktop.
Save sjmeverett/4312170 to your computer and use it in GitHub Desktop.
Makefile to allow Arduino projects to be built at the command line. You just have to specify the arduino model and it will extract the appropriate settings from the boards.txt file. The makefile automatically discovers multiple boards.txt files under the arduino installation hardware directory and the sketchbook hardware directory and uses the a…
# Makefile for building Arduino firmware with Arduino 1.0.
# Version 1.0 by stewartml (www.stewartml.co.uk)
#
# Adapted from Makefile-arduino v0.5 by Akkana Peck <[email protected]>
# Adapted from a long-ago Arduino 0011 Makefile by mellis, eighthave, oli.keller
#
# This Makefile allows you to build sketches from the command line
# without the Arduino environment (or Java).
#
# Detailed instructions for using this Makefile:
#
# 1. Copy this file into the folder with your firmware. Make sure the TARGET
# variable is correct; the default is for your main file to have the same
# name as the containing directory with a .cpp extension. Any files shoud
# have '#include <Arduino.h>' at the top and the main file should contain
# the setup() and loop() functions.
#
# 2. Make sure ARDUINO_DIR and SKETCHBOOK_DIR point to your arduino
# installation directory and sketchbook directory respectively.
#
# 4. Set ARDUINO_MODEL to your Arduino model. Run 'make boards' for a list of
# valid values. Note that this makefile supports multiple boards.txt files
# either under $(ARDUINO_DIR)/hardware or $(SKETCHBOOK_DIR)/hardware!
#
# 5. Run "make" to compile/verify your program.
#
# 6. Run "make upload" (and reset your Arduino if it requires it) to upload
# your program to the Arduino board.
#
#
# Known issues: this makefile will not work if you have spaces in your project
# directory path. I tried to fix it then gave up...
#
VERSION = 0.1
# The build target
# defaults to the name of the containing directory
TARGET = $(notdir $(CURDIR))
#TARGET = main
# Arduino model:
ARDUINO_MODEL = uno
# Which port do you want to use?
PORT = /dev/ttyACM*
# Standard Arduino libraries it will import, e.g. LiquidCrystal:
ARDUINO_LIBS =
# User-specified (in sketchbook/libraries/) libraries (untested):
USER_LIBS =
# Arduino modules to include
# I think the IDE uses some magic to figure out if you use Serial, it had better
# build HardwareSerial.cpp, but since we don't have that, put stuff like that
# here. (Removing default stuff you don't need will give you a smaller binary.)
# Leave off 'main' if you wan't to eschew the setup()/loop() functions and
# define your own main function.
ARDUINO_MODULES = Tone HardwareSerial WMath WString Print main
# Where do you keep the official Arduino software package?
ARDUINO_DIR = $(HOME)/arduino-1.0.2
# Where is your sketchbook folder?
SKETCHBOOK_DIR = $(HOME)/sketchbook
# Where are tools like avr-gcc located on your system?
#AVR_TOOLS_PATH = $(ARDUINO_DIR)/hardware/tools/avr/bin
AVR_TOOLS_PATH = /usr/bin
# Where is avrdude?
AVRDUDE_PATH = $(ARDUINO_DIR)/hardware/tools
#Where is the avrdude configuration file?
#AVRDUDE_CONF = /etc/avrdude.conf
AVRDUDE_CONF = $(AVRDUDE_PATH)/avrdude.conf
# How to reset the device before downloading a new program.
# These don't always work; if the default one doesn't work,
# try uncommenting one of the others instead.
RESET_DEVICE = stty -F $(PORT) hupcl
# Perl version needs libdevice-serialport-perl :
#RESET_DEVICE = perl -MDevice::SerialPort -e 'Device::SerialPort->new("/dev/ttyUSB0")->pulse_dtr_on(1000)'
# Python version needs python-serial :
#RESET_DEVICE = python -c "import serial; s = serial.SERIAL('/dev/ttyUSB0', 57600); s.setDTR(True); sleep(1); s.setDTR(False)"
############################################################################
# Below here nothing should need to be changed. Cross your fingers!
# Automatically figure out which boards.txt to use from the model name
BOARDS_TXT = $(shell grep -lr --include=boards.txt '$(ARDUINO_MODEL)\.' $(ARDUINO_DIR)/hardware $(SKETCHBOOK_DIR)/hardware)
ifeq ($(BOARDS_TXT),)
$(error Invalid ARDUINO_MODEL (run 'make boards' to see a list of valid values))
endif
HARDWARE_DIR = $(shell dirname $(BOARDS_TXT))
getSetting = $(shell awk -F"=" '$$1 == "$(ARDUINO_MODEL).$1" {print $$2}' $(BOARDS_TXT))
# Set up values according to what the IDE uses:
MCU = $(call getSetting,build.mcu)
F_CPU = $(call getSetting,build.f_cpu)
UPLOAD_SPEED = $(call getSetting,upload.speed)
AVRDUDE_PROGRAMMER = $(call getSetting,upload.protocol)
ARDUINO_VARIANT = $(HARDWARE_DIR)/variants/$(call getSetting,build.variant)
ARDUINO_CORE = $(HARDWARE_DIR)/cores/$(call getSetting,build.core)
CWD = $(shell pwd)
CWDBASE = $(shell basename `pwd`)
TARFILE = $(TARGET)-$(VERSION).tar.gz
ARDUINO_CORE_C = $(patsubst $(ARDUINO_CORE)/%.c,applet/%.o,\
$(ARDUINO_CORE)/wiring.c \
$(ARDUINO_CORE)/wiring_analog.c \
$(ARDUINO_CORE)/wiring_digital.c \
$(ARDUINO_CORE)/wiring_pulse.c \
$(ARDUINO_CORE)/wiring_shift.c \
$(ARDUINO_CORE)/WInterrupts.c)
ARDUINO_MODULES_CPP = $(patsubst %,$(ARDUINO_CORE)/%.cpp,$(ARDUINO_MODULES))
ARDUINO_CORE_CPP = $(patsubst $(ARDUINO_CORE)/%.cpp,applet/%.o,\
$(ARDUINO_MODULES_CPP))
SRC =
CXXSRC =
$(foreach l,$(ARDUINO_LIBS),$(ARDUINO_DIR)/libraries/$l/$l.cpp) \
$(foreach l,$(USER_LIBS),$(SKETCHBOOK_DIR)/libraries/$l/$l.cpp)
# Tried to use patsubst, but gmake ignores the second occurrence of %.
# http://www.mail-archive.com/[email protected]/msg00426.html
# $(patsubst %,$(ARDUINO_DIR)/libraries/%/%.cpp,$(ARDUINO_LIBS)) \
# $(patsubst %,$(HOME)/sketchbook/libraries/%/%.cpp,$(USER_LIBS))
FORMAT = ihex
# Name of this Makefile (used for "make depend").
MAKEFILE = Makefile
# Debugging format.
# Native formats for AVR-GCC's -g are stabs [default], or dwarf-2.
# AVR (extended) COFF requires stabs, plus an avr-objcopy run.
DEBUG = stabs
OPT = s
# Place -D or -U options here
CDEFS = -DF_CPU=$(F_CPU)
# Include directories
CINCS = -I$(ARDUINO_CORE) -I$(ARDUINO_VARIANT) $(patsubst %,-I$(ARDUINO_DIR)/libraries/%,$(ARDUINO_LIBS)) $(patsubst %,-I$(SKETCHBOOK_DIR)/libraries/%,$(USER_LIBS))
# Compiler flag to set the C Standard level.
# c89 - "ANSI" C
# gnu89 - c89 plus GCC extensions
# c99 - ISO C99 standard (not yet fully implemented)
# gnu99 - c99 plus GCC extensions
#CSTANDARD = -std=gnu99
CDEBUG = -g$(DEBUG)
#CWARN = -Wall -Wstrict-prototypes
CTUNING = -funsigned-char -funsigned-bitfields -fpack-struct -fshort-enums
#CEXTRA = -Wa,-adhlns=$(<:.c=.lst)
CFLAGS = $(CDEBUG) $(CDEFS) $(CINCS) -O$(OPT) $(CWARN) $(CSTANDARD) $(CEXTRA)
CXXFLAGS = $(CDEFS) $(CINCS) -O$(OPT)
#ASFLAGS = -Wa,-adhlns=$(<:.S=.lst),-gstabs
LDFLAGS = -lm
# Programming support using avrdude. Settings and variables.
AVRDUDE_WRITE_FLASH = -U flash:w:applet/$(TARGET).hex
AVRDUDE_FLAGS = -V -F -C $(AVRDUDE_CONF) -D \
-p $(MCU) -P $(PORT) -c $(AVRDUDE_PROGRAMMER) \
-b $(UPLOAD_SPEED)
# Program settings
CC = $(AVR_TOOLS_PATH)/avr-gcc
CXX = $(AVR_TOOLS_PATH)/avr-g++
OBJCOPY = $(AVR_TOOLS_PATH)/avr-objcopy
OBJDUMP = $(AVR_TOOLS_PATH)/avr-objdump
AR = $(AVR_TOOLS_PATH)/avr-ar
SIZE = $(AVR_TOOLS_PATH)/avr-size
NM = $(AVR_TOOLS_PATH)/avr-nm
AVRDUDE = $(AVRDUDE_PATH)/avrdude
REMOVE = rm -f
MV = mv -f
# Define all object files.
OBJ = $(SRC:.c=.o) $(CXXSRC:.cpp=.o) $(ASRC:.S=.o) $(ARDUINO_CORE_C) $(ARDUINO_CORE_CPP)
# Define all listing files.
LST = $(ASRC:.S=.lst) $(CXXSRC:.cpp=.lst) $(SRC:.c=.lst)
# Combine all necessary flags and optional flags.
# Add target processor to flags.
ALL_CFLAGS = -mmcu=$(MCU) -I. $(CFLAGS)
ALL_CXXFLAGS = -mmcu=$(MCU) -I. $(CXXFLAGS)
ALL_ASFLAGS = -mmcu=$(MCU) -I. -x assembler-with-cpp $(ASFLAGS)
# Default target.
all: applet_files build sizeafter
$(ARDUINO_CORE_C): applet/%.o: $(ARDUINO_CORE)/%.c
$(CC) -c $(ALL_CFLAGS) $< -o $@
$(ARDUINO_CORE_CPP): applet/%.o: $(ARDUINO_CORE)/%.cpp
$(CXX) -c $(ALL_CXXFLAGS) $< -o $@
test:
@echo CXXSRC = $(CXXSRC)
build: elf hex
applet_files:
@test -d applet || mkdir applet
@cp $(TARGET).cpp applet/$(TARGET).cpp
@echo 'extern "C" void __cxa_pure_virtual() { while (1) ; }' >> applet/$(TARGET).cpp
elf: applet/$(TARGET).elf
hex: applet/$(TARGET).hex
eep: applet/$(TARGET).eep
lss: applet/$(TARGET).lss
sym: applet/$(TARGET).sym
# Program the device.
upload: applet/$(TARGET).hex
$(RESET_DEVICE)
$(AVRDUDE) $(AVRDUDE_FLAGS) $(AVRDUDE_WRITE_FLASH)
HEXSIZE = $(SIZE) --target=$(FORMAT) applet/$(TARGET).hex
ELFSIZE = $(SIZE) applet/$(TARGET).elf
sizebefore:
@if [ -f applet/$(TARGET).elf ]; then echo; echo $(MSG_SIZE_BEFORE); $(HEXSIZE); echo; fi
sizeafter:
@if [ -f applet/$(TARGET).elf ]; then echo; echo $(MSG_SIZE_AFTER); $(HEXSIZE); echo; fi
# Convert ELF to COFF for use in debugging / simulating in AVR Studio or VMLAB.
COFFCONVERT=$(OBJCOPY) --debugging \
--change-section-address .data-0x800000 \
--change-section-address .bss-0x800000 \
--change-section-address .noinit-0x800000 \
--change-section-address .eeprom-0x810000
coff: applet/$(TARGET).elf
$(COFFCONVERT) -O coff-avr applet/$(TARGET).elf $(TARGET).cof
extcoff: $(TARGET).elf
$(COFFCONVERT) -O coff-ext-avr applet/$(TARGET).elf $(TARGET).cof
.SUFFIXES: .elf .hex .eep .lss .sym
.elf.hex:
$(OBJCOPY) -O $(FORMAT) -R .eeprom $< $@
.elf.eep:
-$(OBJCOPY) -j .eeprom --set-section-flags=.eeprom="alloc,load" \
--change-section-lma .eeprom=0 -O $(FORMAT) $< $@
# Create extended listing file from ELF output file.
.elf.lss:
$(OBJDUMP) -h -S $< > $@
# Create a symbol table from ELF output file.
.elf.sym:
$(NM) -n $< > $@
# Link: create ELF output file from library.
applet/$(TARGET).elf: applet/$(TARGET).cpp applet/core.a
$(CC) $(ALL_CFLAGS) -o $@ applet/$(TARGET).cpp -L. applet/core.a $(LDFLAGS)
applet/core.a: $(OBJ)
@for i in $(OBJ); do echo $(AR) rcs applet/core.a $$i; $(AR) rcs applet/core.a $$i; done
# Compile: create object files from C++ source files.
.cpp.o:
$(CXX) -c $(ALL_CXXFLAGS) $< -o $@
# Compile: create object files from C source files.
.c.o:
$(CC) -c $(ALL_CFLAGS) $< -o $@
# Compile: create assembler files from C source files.
.c.s:
$(CC) -S $(ALL_CFLAGS) $< -o $@
# Assemble: create object files from assembler source files.
.S.o:
$(CC) -c $(ALL_ASFLAGS) $< -o $@
# Target: clean project.
clean:
$(REMOVE) applet/$(TARGET).hex applet/$(TARGET).eep applet/$(TARGET).cof applet/$(TARGET).elf \
applet/$(TARGET).map applet/$(TARGET).sym applet/$(TARGET).lss applet/core.a \
$(OBJ) $(LST) $(SRC:.c=.s) $(SRC:.c=.d) $(CXXSRC:.cpp=.s) $(CXXSRC:.cpp=.d)
tar: $(TARFILE)
$(TARFILE):
( cd .. && \
tar czvf "$(TARFILE)" --exclude=applet --owner=root "$(CWDBASE)" && \
mv "$(TARFILE)" "$(CWD)" && \
echo Created $(TARFILE) \
)
depend:
if grep '^# DO NOT DELETE' $(MAKEFILE) >/dev/null; \
then \
sed -e '/^# DO NOT DELETE/,$$d' $(MAKEFILE) > \
$(MAKEFILE).$$$$ && \
$(MV) $(MAKEFILE).$$$$ $(MAKEFILE); \
fi
echo '# DO NOT DELETE THIS LINE -- make depend depends on it.' \
>> $(MAKEFILE); \
$(CC) -M -mmcu=$(MCU) $(CDEFS) $(CINCS) $(SRC) $(ASRC) >> $(MAKEFILE)
boards:
@grep -hr --include=boards.txt .name $(ARDUINO_DIR)/hardware $(SKETCHBOOK_DIR)/hardware | awk -F".name=" '{printf("%-20s %s\n", $$1, $$2)}' | sort
.PHONY: all build elf hex eep lss sym program coff extcoff clean depend applet_files sizebefore sizeafter boards
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment