An old-school makefile for Arduino/ATtiny84/85/ATmega328p projects.
As a software developer in my daily life, I was immediately annoyed by the Arduino IDE when I started playing with Arduino in the beginning of 2017. This, in classic developer fashion, got me side-tracked, initially spending a lot more time on toolchain setup than on the actual Arduino project... Oh well, imho it was time well spent in the name of flexibility having nerd-fun :-)
I decided on Visual Studio Code with an old-school makefile for building and uploading to the MCU. At work I am a Windows developer, but at home I use Ubuntu, and I have no plans of using this makefile on Windows, though it probably wouldn't be hard to do.
As the makefile got more complex, it led to make it re-usable via include, so that each individual project only needed a minimal makefile. Consequently, it must be project-agnostic. Most configuration variables can be initialized with project-specific content before the include statement.
This makefile is the result, and I have used it for a couple of Arduino projects, some on Adafruit Pro Trinket 5V boards, others on ATtiny85/84 chips.
This project is licensed under the terms of the MIT license.
The ease-of-use is based on some assumptions, some of which can be reconfigured by assigning the appropriate varables beore including makeArduino.mk.
For tiny_84 | tiny_85 targets, ARDUINO_CORE_PATH points to the arduino-tiny package, located in ~/Arduino/tiny.
For raw84 | raw85 | raw328p targets, ARDUINO_CORE_PATH points to the rawcore package, located in ~/Arduino/rawcore.
| Config variable | Default | Usage |
|---|---|---|
ARDUINO_IDE_PATH |
~/arduino-1.8.1/ |
The Arduino IDE 1.8.1 |
PROJECTS_ROOT_PATH |
~/Arduino/ |
Personal projects |
LIBRARY_PATHS |
~/Arduino/my_libraries ~/Arduino/libraries |
Home-grown and third-party libraries |
There are other nitty-gritty config variables in makeArduino.mk, but these are the most important ones.
Other assumptions may be "hidden", or in plain english: It works on my setup :)
- Copy the sample
Makefileinto your project folder and update it to reflect your project, as detailed in the next section - Put your code files beside it in the project folder
- All
.c,.cppand.Sfiles in your project folder are compiled - Subfolders that contain
.h,.c,.cppor.Sfiles are recursed into, added to the include path, and any.c,.cppand.Sfiles are compiled
- All
The sample Makefile looks like this:
# TARGET_SYSTEM : uno | pro_trinket_5v | tiny_84 | tiny_85 | raw84 | raw85 | raw328p
TARGET_SYSTEM = uno
INCLUDE_LIBS =
PROJECT_DEFINES =
include ../makeArduino/makeArduino.mk-
TARGET_SYSTEMspecifies which hardware the project is aimed at:Value Hardware unoPlain old Arduino Uno R3 board pro_trinket_5vAdafruit Pro Trinket 5V board tiny_84Atmel ATtiny84, using tinycoretiny_85Atmel ATtiny85, using tinycoreraw84Atmel ATtiny84, using rawcoreraw85Atmel ATtiny85, using rawcoreraw328pAtmel ATmega328p, using rawcore -
INCLUDE_LIBSis the most advanced variable in terms of implementingmakeArduino.mk, but it is very easy to use, provided that your library folders are located in one of the folders inLIBRARY_PATHS, and also, of course, that my "hidden assumptions" are correct :)It is just a space-separated list of names of the libraries you wish to include in your project.
All
.c,.cppand.Sfiles in the library folder are included by default, but you can specify per library which exact object files to include, e.g. for the Adafruit Wire library, to include onlyWire.cppandutility/twi.c:INCLUDE_LIBS = Wire LIBRARY_OBJS_Wire = Wire twi
All subfolders that contain
.h,.c,.cppor.Sfiles are recursed into and added to the include path. -
PROJECT_DEFINES: Here you can put whatever global defines you want to have in your project. The defines are of course added to the compiler options, but they are also included in.mkout/c_cpp_properties.json, which can be copied into.vscode/(manually or viamake updatevscodecpp) to let VSCode IntelliSense know how to resolve symbols.I use SoftI2CMaster.h on ATtinyXX projects, and need to add something like this:
PROJECT_DEFINES = SDA_PORT=PORTA SDA_PIN=PA6 SCL_PORT=PORTA SCL_PIN=PA4
The typical make targets are build and upload, but here is a short description of all the main targets:
| Target | Purpose |
|---|---|
all |
Rebuild project, libs only if changed, and upload |
build |
Rebuild project, libs only if changed |
fullbuild |
Rebuild everything, both project and libraries |
mostlyclean |
Remove project binaries, but not libraries |
realclean/clean |
Remove all binaries |
buildvscodecpp |
Generate c_cpp_properties.json with paths and defines, then show changes, but do not apply to .vscode |
updatevscodecpp |
Generate and replace .vscode/c_cpp_properties.json. Manual changes in .vscode/c_cpp_properties.json will be lost. |
buildvscodetasks |
Generate tasks.json with tasks for 'Build', 'Upload' and 'Update .vscode/c_cpp_properties.json', then show changes, but do not apply to .vscode |
updatevscodetasks |
Generate and replace .vscode/tasks.json. Manual changes in .vscode/tasks.json will be lost. |
buildvscode |
Generate both c_cpp_properties.json and tasks.json and show changes, but do not apply to .vscode |
updatevscode |
Generate and replace both .vscode/c_cpp_properties.json and .vscode/tasks.json. Manual changes in either will be lost. |
compile |
Compile only changed files |
nm |
List what uses the sometimes precious space |
dumpS |
Dump dissasembly |
burnfuses |
"Burn" fuses in ATtiny/ATmega mcu, via Arduino ISP |
upload |
Compile changed files, then upload to board |
When the above says "changed", only .c, .cpp or .S files are checked, not the .h files they depend on. Maybe I'll implement that later, but for now just build, it's not as if there is tons of flash for so much code that build is unreasonably slower than compile :)
- Make sure you can use the Arduino IDE to upload a sketch, e.g. Blink, to an Arduino board. I had to fiddle a little with the USB setup in linux before that worked, but it has worked flawlessly since
- Proceed as follows, depending on which
TARGET_SYSTEMyou are using:
- Plug Uno into the USB port, which should show up as
/dev/ttyACM0 make upload
- Plug trinket into the USB port
- Press reset button on the trinket (I have not figured out how to auto-reset)
make upload
- Set up Arduino as ISP, as detailed below
- Plug Arduino ISP into the USB port, which should show up as
/dev/ttyACM0 make burnfuses- this only needs to be done once for each chipmake upload
-
Plug Arduino into the USB port, which should show up as
/dev/ttyACM0 -
From the Arduino IDE, upload the Arduino as ISP sketch
-
Unplug Arduino
-
Add a 10uF capacitor between GND and RESET on the Arduino
-
Connect the following pins to your target ATtiny8x:
Arduino ATtiny85 ATtiny84 5V VCC VCC GND GND GND Pin 13 PB2 PA4 Pin 12 PB1 PA5 Pin 11 PB0 PA6 Pin 10 PB5 PB3 ATtiny85 pins ATtiny84 pins _______ _______ 10 --> PB5 -| \_/ |- VCC VCC -| \_/ |- GND PB3 -| |- PB2 <-- 13 PB0 -| |- PA0 PB4 -| |- PB1 <-- 12 PB1 -| |- PA1 GND -|_______|- PB0 <-- 11 10 --> PB3 -| |- PA2 PB2 -| |- PA3 PA7 -| |- PA4 <-- 13 11 --> PA6 -|_______|- PA5 <-- 12
tasks.jsonis the configuration that addsBuild,UploadandUpdate .vscode/c_cpp_properties.jsonas tasks in VSCode.c_cpp_properties.jsonis the configuration that lets IntelliSense know how to resolve symbols via included paths and project defines.
Whenever the compile target is run, fresh tasks.json and c_cpp_properties.json file are generated in .mkout. If they is different from the ones in .vscode, a short diff is shown after compile is done. Make will say that an error occurred and was ignored, because diff returns nonzero when differences were found.
To rebuild or update tasks.json and c_cpp_properties.json without compiling the project, e.g. in a fresh new one, run one of the following:
| Command | Action |
|---|---|
make buildvscode |
Generate both files |
make updatevscode |
Generate and replace both files |
make buildvscodetasks |
Generate tasks.json |
make updatevscodetasks |
Generate and replace tasks.json |
make buildvscodecpp |
Generate c_cpp_properties.json |
make updatevscodecpp |
Generate and replace c_cpp_properties.json |