rake-arduino 0.0.1.alpha
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +18 -0
- data/Gemfile +4 -0
- data/README.md +78 -0
- data/Rakefile +2 -0
- data/examples/basic/Blink.cpp +14 -0
- data/examples/basic/Rakefile +25 -0
- data/examples/multi_target/Blink.cpp +14 -0
- data/examples/multi_target/Rakefile +33 -0
- data/lib/rake/arduino.rb +10 -0
- data/lib/rake/arduino/board.rb +37 -0
- data/lib/rake/arduino/boards.rb +16 -0
- data/lib/rake/arduino/config.rb +34 -0
- data/lib/rake/arduino/core_ext/array.rb +9 -0
- data/lib/rake/arduino/core_ext/pathname.rb +5 -0
- data/lib/rake/arduino/sketch.rb +156 -0
- data/lib/rake/arduino/toolchain.rb +65 -0
- data/lib/rake/arduino/version.rb +5 -0
- data/rake-arduino.gemspec +18 -0
- metadata +77 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/README.md
ADDED
@@ -0,0 +1,78 @@
|
|
1
|
+
rake-arduino
|
2
|
+
============
|
3
|
+
_A Flexible build tool for Arduino development_
|
4
|
+
|
5
|
+
## Usage
|
6
|
+
_`rake-arduino` is very pre-alpha at the moment. Be prepared to find and fix a
|
7
|
+
lot of bugs while using it._
|
8
|
+
|
9
|
+
After installing the gem (see below), simply add a Rakefile that looks like
|
10
|
+
this to the root of your project:
|
11
|
+
|
12
|
+
require 'rubygems'
|
13
|
+
require 'rake/arduino'
|
14
|
+
|
15
|
+
Rake::Arduino::Sketch.new do |s|
|
16
|
+
s.sources << "MySketch.cpp"
|
17
|
+
s.libraries << "Servo"
|
18
|
+
|
19
|
+
s.board = Rake::Arduino::Board["Arduino Uno"]
|
20
|
+
end
|
21
|
+
|
22
|
+
See the `examples` directory for more advanced usage.
|
23
|
+
|
24
|
+
Then, to build your project run:
|
25
|
+
|
26
|
+
rake
|
27
|
+
|
28
|
+
To upload it to your arduino:
|
29
|
+
|
30
|
+
rake upload
|
31
|
+
|
32
|
+
If your arduino installation is somewhere non-standard, you'll need to
|
33
|
+
configure that at the top of your Rakefile:
|
34
|
+
|
35
|
+
Rake::Arduino.configure do |c|
|
36
|
+
c.home = "/home/me/apps/arduino-0022"
|
37
|
+
end
|
38
|
+
|
39
|
+
## Installation
|
40
|
+
You'll need ruby and rubygems installed (use your system's package manager, or
|
41
|
+
RVM).
|
42
|
+
|
43
|
+
As a gem:
|
44
|
+
|
45
|
+
gem install rake-arduino
|
46
|
+
|
47
|
+
From source:
|
48
|
+
|
49
|
+
gem install bundler rake
|
50
|
+
|
51
|
+
git clone https://github.com/wjbuys/rake-arduino
|
52
|
+
cd rake-arduino
|
53
|
+
rake install
|
54
|
+
|
55
|
+
## Background
|
56
|
+
Arduino is great. The arduino IDE: not so great. Once your project moves beyond
|
57
|
+
a nontrivial size, it's a massive pain to use. Sorry Arduino team, but it's
|
58
|
+
just painful and ugly (and stupid, if you're used to Vim/Emacs).
|
59
|
+
|
60
|
+
At this point all the gurus say: "Just use a `Makefile`!". Unfortunately, this
|
61
|
+
becomes a very roll-your-own mission (there's no centrally maintained version).
|
62
|
+
|
63
|
+
Also, the `Makefile` syntax makes my eyes bleed. Inevitably multiple copies of
|
64
|
+
the same `Makefile` with minor tweaks will end up around all my projects, because
|
65
|
+
I'm lazy.
|
66
|
+
|
67
|
+
*Luckily, there's a solution: _Rake_.* Rake is Ruby, so
|
68
|
+
|
69
|
+
1. the syntax is gorgeous,
|
70
|
+
1. you get a real programming language to do additional pre-processing,
|
71
|
+
1. and you can wrap common logic up neatly into a central library.
|
72
|
+
|
73
|
+
## TODO
|
74
|
+
|
75
|
+
1. Support `.pde` sketch pre-processing
|
76
|
+
1. Add some specs (I just ripped this out of a project I was working on, so no
|
77
|
+
tests :( )
|
78
|
+
1. Support config from ~/.rake-arduino
|
data/Rakefile
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
$:.unshift "../../lib"
|
2
|
+
require 'rake/arduino'
|
3
|
+
|
4
|
+
# The basic configuration of a project is done via the
|
5
|
+
# Rake::Arduino::Sketch task:
|
6
|
+
|
7
|
+
Rake::Arduino::Sketch.new do |s|
|
8
|
+
# Add your main source files to the s.sources array:
|
9
|
+
s.sources << "Blink.cpp"
|
10
|
+
|
11
|
+
# If you need any Arduino libraries, add them to s.libraries:
|
12
|
+
s.libraries << "Servo"
|
13
|
+
|
14
|
+
# Specify the board you'd like to use. Boards are defined
|
15
|
+
# in lib/rake/arduino/boards.rb, and referenced by name:
|
16
|
+
s.board = Rake::Arduino.board("Arduino Uno") do |b|
|
17
|
+
|
18
|
+
# In the board block, you can customize board parameters
|
19
|
+
# for your specific needs.
|
20
|
+
b.cpu_speed = 8000000
|
21
|
+
|
22
|
+
# See the Board class docs for a full list of settings that
|
23
|
+
# can be configured.
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
$:.unshift "../../lib"
|
2
|
+
require 'rake/arduino'
|
3
|
+
|
4
|
+
# rake-arduino supports defining multiple targets in a single rakefile.
|
5
|
+
# This allows you to build variations on the same project quite easily.
|
6
|
+
# Say, for example, you'd like to compile hex files for both the classic
|
7
|
+
# arduino, and for the Teensy:
|
8
|
+
|
9
|
+
Rake::Arduino::Sketch.new :arduino do |s|
|
10
|
+
s.sources << "Blink.cpp"
|
11
|
+
s.board = Rake::Arduino.board("Arduino Uno")
|
12
|
+
end
|
13
|
+
|
14
|
+
Rake::Arduino::Sketch.new :teensy do |s|
|
15
|
+
s.sources << "Blink.cpp"
|
16
|
+
s.board = Rake::Arduino.board("Teensy 2.0")
|
17
|
+
end
|
18
|
+
|
19
|
+
# Now you can build each target with `rake <target>`
|
20
|
+
# If you want to build them all at once, add a default task that references
|
21
|
+
# both targets:
|
22
|
+
|
23
|
+
task :default => [:arduino, :teensy]
|
24
|
+
|
25
|
+
# You can even have both platforms built in parallel:
|
26
|
+
|
27
|
+
multitask :all => [:arduino, :teensy]
|
28
|
+
|
29
|
+
# Now `rake` will build .hex files for both platforms in one go.
|
30
|
+
#
|
31
|
+
# Note: Your targets don't have to relate to each other at all. If you'd
|
32
|
+
# like to build completely separate projects from the same Rakefile, you
|
33
|
+
# can do that too.
|
data/lib/rake/arduino.rb
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
module Rake
|
2
|
+
module Arduino
|
3
|
+
class Board
|
4
|
+
attr_accessor :name
|
5
|
+
attr_accessor :cores, :defines
|
6
|
+
attr_accessor :mcu, :cpu_speed
|
7
|
+
attr_accessor :max_size
|
8
|
+
|
9
|
+
@boards = {}
|
10
|
+
def self.[](name)
|
11
|
+
@boards[name]
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.register(board)
|
15
|
+
@boards[board.name] = board
|
16
|
+
end
|
17
|
+
|
18
|
+
def register
|
19
|
+
self.class.register(self)
|
20
|
+
end
|
21
|
+
|
22
|
+
def initialize(name)
|
23
|
+
self.name = name
|
24
|
+
self.defines = []
|
25
|
+
self.cores = []
|
26
|
+
|
27
|
+
yield self if block_given?
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.board(name)
|
32
|
+
board = Board[name]
|
33
|
+
yield board if block_given?
|
34
|
+
board
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module Rake::Arduino
|
2
|
+
Board.new("Arduino Uno") do |b|
|
3
|
+
b.cores = ["arduino"]
|
4
|
+
b.cpu_speed = 16000000
|
5
|
+
b.mcu = "atmega328p"
|
6
|
+
b.max_size = 30720
|
7
|
+
end.register
|
8
|
+
|
9
|
+
Board.new("Teensy 2.0") do |b|
|
10
|
+
b.cores = ["teensy", "usb_serial"]
|
11
|
+
b.defines = ["USB_SERIAL"]
|
12
|
+
b.cpu_speed = 16000000
|
13
|
+
b.mcu = "atmega32u4"
|
14
|
+
b.max_size = 32256
|
15
|
+
end.register
|
16
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module Rake
|
2
|
+
module Arduino
|
3
|
+
class Config
|
4
|
+
attr_accessor :home, :hardware_path, :library_path
|
5
|
+
attr_accessor :cores
|
6
|
+
|
7
|
+
def initialize
|
8
|
+
yield self if block_given?
|
9
|
+
|
10
|
+
self.home ||= ENV["HOME"] + "/apps/arduino"
|
11
|
+
|
12
|
+
self.hardware_path ||= home + "/hardware"
|
13
|
+
self.library_path ||= home + "/libraries"
|
14
|
+
|
15
|
+
self.cores ||= Dir.glob(hardware_path + "/**/cores/*")
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.read_defaults
|
19
|
+
load ENV["HOME"] + "/.rake-arduino"
|
20
|
+
rescue LoadError
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
class << self
|
25
|
+
def config
|
26
|
+
@config ||= Config.new
|
27
|
+
end
|
28
|
+
|
29
|
+
def configure(&block)
|
30
|
+
@config = Config.new(&block)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,156 @@
|
|
1
|
+
require 'rake/arduino/core_ext/pathname'
|
2
|
+
require 'rake/arduino/core_ext/array'
|
3
|
+
|
4
|
+
module Rake
|
5
|
+
module Arduino
|
6
|
+
class Sketch
|
7
|
+
include Rake::DSL
|
8
|
+
|
9
|
+
attr_accessor :sources
|
10
|
+
attr_accessor :name
|
11
|
+
attr_accessor :target
|
12
|
+
attr_accessor :board
|
13
|
+
attr_accessor :libraries
|
14
|
+
attr_accessor :hex, :elf
|
15
|
+
attr_accessor :programmer, :upload_rate
|
16
|
+
attr_accessor :usb_type
|
17
|
+
attr_accessor :build_root, :root
|
18
|
+
attr_accessor :toolchain
|
19
|
+
|
20
|
+
def initialize(target = :default)
|
21
|
+
Config.read_defaults
|
22
|
+
|
23
|
+
self.target = target.to_sym
|
24
|
+
self.build_root = "build/#{target}"
|
25
|
+
|
26
|
+
self.root = Pathname.pwd
|
27
|
+
|
28
|
+
self.sources = []
|
29
|
+
self.libraries = []
|
30
|
+
|
31
|
+
yield self
|
32
|
+
|
33
|
+
raise "You have to specify a board for the sketch" unless board
|
34
|
+
|
35
|
+
self.sources = sources.to_paths
|
36
|
+
self.build_root = Pathname(build_root)
|
37
|
+
|
38
|
+
self.name ||= root.basename.to_s
|
39
|
+
|
40
|
+
self.elf ||= build("#{name}.elf")
|
41
|
+
self.hex ||= build("#{name}.hex")
|
42
|
+
|
43
|
+
self.programmer ||= "avr109"
|
44
|
+
self.upload_rate ||= 19200
|
45
|
+
|
46
|
+
self.toolchain = Toolchain.new(self)
|
47
|
+
|
48
|
+
create_tasks
|
49
|
+
end
|
50
|
+
|
51
|
+
|
52
|
+
def config
|
53
|
+
Rake::Arduino.config
|
54
|
+
end
|
55
|
+
|
56
|
+
def core_paths
|
57
|
+
@core_paths ||= config.cores.to_paths.select do |core|
|
58
|
+
board.cores.include? core.basename.to_s
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def library_paths
|
63
|
+
libraries.map do |lib|
|
64
|
+
Pathname(config.library_path) + lib
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def build(path)
|
69
|
+
path = Pathname(path)
|
70
|
+
path = path.relative_path_from(root) if path.absolute?
|
71
|
+
|
72
|
+
# Use fully-qualified paths for source outside the project root
|
73
|
+
path = path.expand_path.sub(/^\//, "") if path.to_s.start_with? ".."
|
74
|
+
|
75
|
+
build_path = Pathname(build_root) + path
|
76
|
+
end
|
77
|
+
|
78
|
+
def compile(*source_files)
|
79
|
+
source_files = [source_files].flatten
|
80
|
+
object_files = source_files.map do |source|
|
81
|
+
build(source.sub_ext(".o"))
|
82
|
+
end
|
83
|
+
|
84
|
+
object_files.zip(source_files).each do |object_file, source_file|
|
85
|
+
file object_file => source_file do
|
86
|
+
object_file.parent.mkpath
|
87
|
+
toolchain.compile source_file, :into => object_file
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
object_files
|
92
|
+
end
|
93
|
+
|
94
|
+
def create_tasks
|
95
|
+
compiled_libraries = [
|
96
|
+
*board.cores,
|
97
|
+
*libraries
|
98
|
+
].map{|l| build("#{l}.a")}
|
99
|
+
|
100
|
+
task target => [*compiled_libraries, hex]
|
101
|
+
|
102
|
+
main_objects = compile sources
|
103
|
+
|
104
|
+
(core_paths + library_paths).each do |path|
|
105
|
+
library_out = build(path.basename.sub_ext(".a"))
|
106
|
+
|
107
|
+
object_files = compile Pathname.glob(path +"**/*.{c,cpp}")
|
108
|
+
|
109
|
+
file library_out => object_files do
|
110
|
+
object_files.each do |object_file|
|
111
|
+
library_out.parent.mkpath
|
112
|
+
toolchain.archive object_file, :into => library_out
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
file elf => main_objects + compiled_libraries do
|
118
|
+
toolchain.link main_objects, :with => compiled_libraries, :into => elf
|
119
|
+
end
|
120
|
+
|
121
|
+
file hex => elf do
|
122
|
+
size = toolchain.convert_binary(elf, :hex => hex)
|
123
|
+
|
124
|
+
if size > board.max_size
|
125
|
+
puts "The sketch size (#{size} bytes) has overriden the maximum size (#{board.max_size} bytes)."
|
126
|
+
rm hex
|
127
|
+
exit -1
|
128
|
+
else
|
129
|
+
puts "Sketch size: #{size} bytes (of a #{board.max_size} bytes maximum)."
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
task :upload => [:all, :upload_pre] do
|
134
|
+
sh "#{avrdude} -V -F -p #{board.mcu} -c #{programmer} -P #{port} -b #{upload_rate} -D -Uflash:w:#{hex}:i"
|
135
|
+
end
|
136
|
+
|
137
|
+
task :clean do
|
138
|
+
rm_rf Dir["build"]
|
139
|
+
rm_f Dir["**/*.{o,a,hex,elf}"]
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
def includes
|
144
|
+
["/usr/lib/avr/include/avr", *core_paths, *library_paths]
|
145
|
+
end
|
146
|
+
|
147
|
+
def defines
|
148
|
+
["F_CPU=#{board.cpu_speed}L", "ARDUINO=18", *board.defines]
|
149
|
+
end
|
150
|
+
|
151
|
+
def avrdude
|
152
|
+
"avrdude"
|
153
|
+
end
|
154
|
+
end
|
155
|
+
end
|
156
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
module Rake
|
2
|
+
module Arduino
|
3
|
+
class Toolchain
|
4
|
+
include FileUtils
|
5
|
+
|
6
|
+
attr_accessor :sketch
|
7
|
+
|
8
|
+
def initialize(sketch)
|
9
|
+
self.sketch = sketch
|
10
|
+
end
|
11
|
+
|
12
|
+
def mcu
|
13
|
+
sketch.board.mcu
|
14
|
+
end
|
15
|
+
|
16
|
+
def cpp_flags
|
17
|
+
[
|
18
|
+
"-Wall",
|
19
|
+
"-std=gnu++0x",
|
20
|
+
"-g",
|
21
|
+
"-Os",
|
22
|
+
"-w",
|
23
|
+
"-fno-exceptions",
|
24
|
+
"-ffunction-sections",
|
25
|
+
"-fdata-sections",
|
26
|
+
"-mmcu=#{mcu}",
|
27
|
+
*sketch.defines.map{|d| "-D#{d}"},
|
28
|
+
*sketch.includes.map{|i| "-I'#{i}'"}
|
29
|
+
]
|
30
|
+
end
|
31
|
+
|
32
|
+
def ld_flags
|
33
|
+
["-Os", "-Wl,--gc-sections", "-mmcu=#{mcu}"]
|
34
|
+
end
|
35
|
+
|
36
|
+
def ar_flags
|
37
|
+
['rcs']
|
38
|
+
end
|
39
|
+
|
40
|
+
def compile(source_file, options = {})
|
41
|
+
object_file = options[:into]
|
42
|
+
sh "avr-gcc #{cpp_flags.join(" ")} -c #{source_file} -o #{object_file}"
|
43
|
+
end
|
44
|
+
|
45
|
+
def link(main_objects, options)
|
46
|
+
compiled_libraries = options[:with]
|
47
|
+
binary = options[:into]
|
48
|
+
sh "avr-gcc #{ld_flags.join(" ")} #{main_objects.join(" ")} #{compiled_libraries.join(" ")} -lm -o #{binary}"
|
49
|
+
end
|
50
|
+
|
51
|
+
def archive(object_file, options = {})
|
52
|
+
archive = options[:into]
|
53
|
+
sh "avr-ar #{ar_flags.join(" ")} #{archive} #{object_file}"
|
54
|
+
end
|
55
|
+
|
56
|
+
def convert_binary(binary, options)
|
57
|
+
hex = options[:hex]
|
58
|
+
sh "avr-objcopy -O ihex -R .eeprom #{binary} #{hex}"
|
59
|
+
|
60
|
+
`avr-size -A --mcu=#{mcu} #{hex}` =~ /Total(\s*)(\d*)/
|
61
|
+
$2.to_i
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
require File.expand_path('../lib/rake/arduino/version', __FILE__)
|
3
|
+
|
4
|
+
Gem::Specification.new do |gem|
|
5
|
+
gem.authors = ["Jacob Buys"]
|
6
|
+
gem.email = ["wjbuys@gmail.com"]
|
7
|
+
gem.summary = %q{Flexible build system for Arduino projects.}
|
8
|
+
gem.description = %q{rake-arduino allows you to easily build Arduino sketches using Rake.}
|
9
|
+
gem.homepage = "https://github.com/wjbuys/rake-arduino"
|
10
|
+
|
11
|
+
gem.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
12
|
+
gem.files = `git ls-files`.split("\n")
|
13
|
+
gem.name = "rake-arduino"
|
14
|
+
gem.require_paths = ["lib"]
|
15
|
+
gem.version = Rake::Arduino::VERSION
|
16
|
+
|
17
|
+
gem.add_dependency "rake"
|
18
|
+
end
|
metadata
ADDED
@@ -0,0 +1,77 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: rake-arduino
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1.alpha
|
5
|
+
prerelease: 6
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Jacob Buys
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2011-10-09 00:00:00.000000000Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: rake
|
16
|
+
requirement: &12055520 !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0'
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: *12055520
|
25
|
+
description: rake-arduino allows you to easily build Arduino sketches using Rake.
|
26
|
+
email:
|
27
|
+
- wjbuys@gmail.com
|
28
|
+
executables: []
|
29
|
+
extensions: []
|
30
|
+
extra_rdoc_files: []
|
31
|
+
files:
|
32
|
+
- .gitignore
|
33
|
+
- Gemfile
|
34
|
+
- README.md
|
35
|
+
- Rakefile
|
36
|
+
- examples/basic/Blink.cpp
|
37
|
+
- examples/basic/Rakefile
|
38
|
+
- examples/multi_target/Blink.cpp
|
39
|
+
- examples/multi_target/Rakefile
|
40
|
+
- lib/rake/arduino.rb
|
41
|
+
- lib/rake/arduino/board.rb
|
42
|
+
- lib/rake/arduino/boards.rb
|
43
|
+
- lib/rake/arduino/config.rb
|
44
|
+
- lib/rake/arduino/core_ext/array.rb
|
45
|
+
- lib/rake/arduino/core_ext/pathname.rb
|
46
|
+
- lib/rake/arduino/sketch.rb
|
47
|
+
- lib/rake/arduino/toolchain.rb
|
48
|
+
- lib/rake/arduino/version.rb
|
49
|
+
- rake-arduino.gemspec
|
50
|
+
homepage: https://github.com/wjbuys/rake-arduino
|
51
|
+
licenses: []
|
52
|
+
post_install_message:
|
53
|
+
rdoc_options: []
|
54
|
+
require_paths:
|
55
|
+
- lib
|
56
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ! '>='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
segments:
|
63
|
+
- 0
|
64
|
+
hash: -1016166330329218044
|
65
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
66
|
+
none: false
|
67
|
+
requirements:
|
68
|
+
- - ! '>'
|
69
|
+
- !ruby/object:Gem::Version
|
70
|
+
version: 1.3.1
|
71
|
+
requirements: []
|
72
|
+
rubyforge_project:
|
73
|
+
rubygems_version: 1.8.6
|
74
|
+
signing_key:
|
75
|
+
specification_version: 3
|
76
|
+
summary: Flexible build system for Arduino projects.
|
77
|
+
test_files: []
|