workshop 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +18 -0
- data/.rspec +2 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +23 -0
- data/Rakefile +1 -0
- data/bin/workshop +3 -0
- data/examples/blink/Rakefile +15 -0
- data/examples/blink/config/com_port.yml +2 -0
- data/examples/blink/src/blink.cpp +30 -0
- data/lib/workshop.rb +18 -0
- data/lib/workshop/arduino.rb +34 -0
- data/lib/workshop/builder.rb +199 -0
- data/lib/workshop/cli.rb +23 -0
- data/lib/workshop/project.rb +99 -0
- data/lib/workshop/project/configuration.rb +71 -0
- data/lib/workshop/project/configuration/build.rb +18 -0
- data/lib/workshop/project/configuration/upload.rb +15 -0
- data/lib/workshop/project/setup.rb +76 -0
- data/lib/workshop/tasks.rb +64 -0
- data/lib/workshop/templates/Rakefile.erb +13 -0
- data/lib/workshop/templates/blink.cpp +30 -0
- data/lib/workshop/tools.rb +56 -0
- data/lib/workshop/uploader.rb +35 -0
- data/lib/workshop/version.rb +3 -0
- data/spec/arduino_spec.rb +31 -0
- data/spec/build_configuration_spec.rb +52 -0
- data/spec/builder_spec.rb +159 -0
- data/spec/configuration_spec.rb +38 -0
- data/spec/project_spec.rb +29 -0
- data/spec/spec_helper.rb +8 -0
- data/spec/tools_spec.rb +33 -0
- data/spec/upload_configuration_spec.rb +9 -0
- data/workshop.gemspec +31 -0
- metadata +186 -0
@@ -0,0 +1,71 @@
|
|
1
|
+
module Workshop
|
2
|
+
class Project
|
3
|
+
class Configuration
|
4
|
+
attr_accessor :arduino_app_directory, :project_name, :source_directory, :build, :upload,
|
5
|
+
:build_directory, :main_filename, :libraries
|
6
|
+
|
7
|
+
def initialize
|
8
|
+
self.arduino_app_directory = '/Applications/Arduino.app'
|
9
|
+
self.project_name = default_project_name
|
10
|
+
self.source_directory = default_source_directory
|
11
|
+
self.build_directory = default_build_directory
|
12
|
+
self.libraries = []
|
13
|
+
self.build = Build.new
|
14
|
+
self.upload = Upload.new
|
15
|
+
end
|
16
|
+
|
17
|
+
def includes
|
18
|
+
build.includes
|
19
|
+
end
|
20
|
+
|
21
|
+
def default_project_name
|
22
|
+
app_directory.split('/').last.parameterize.underscore
|
23
|
+
end
|
24
|
+
|
25
|
+
def default_build_directory
|
26
|
+
app_directory + '/build'
|
27
|
+
end
|
28
|
+
|
29
|
+
def default_source_directory
|
30
|
+
app_directory + '/src'
|
31
|
+
end
|
32
|
+
|
33
|
+
def app_directory
|
34
|
+
Dir.pwd
|
35
|
+
end
|
36
|
+
|
37
|
+
def build_core
|
38
|
+
build.core
|
39
|
+
end
|
40
|
+
|
41
|
+
def build_variant
|
42
|
+
build.variant
|
43
|
+
end
|
44
|
+
|
45
|
+
def build_mcu
|
46
|
+
build.mcu
|
47
|
+
end
|
48
|
+
|
49
|
+
def build_f_cpu
|
50
|
+
build.f_cpu
|
51
|
+
end
|
52
|
+
|
53
|
+
def build_vid
|
54
|
+
build.vid
|
55
|
+
end
|
56
|
+
|
57
|
+
def build_pid
|
58
|
+
build.pid
|
59
|
+
end
|
60
|
+
|
61
|
+
def upload_protocol
|
62
|
+
upload.protocol
|
63
|
+
end
|
64
|
+
|
65
|
+
def upload_speed
|
66
|
+
upload.speed
|
67
|
+
end
|
68
|
+
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module Workshop
|
2
|
+
class Project
|
3
|
+
class Configuration
|
4
|
+
class Build
|
5
|
+
attr_accessor :mcu, :core, :variant, :f_cpu, :vid, :pid, :libraries, :includes
|
6
|
+
|
7
|
+
def initialize
|
8
|
+
self.mcu = 'atmega328p'
|
9
|
+
self.core = 'arduino'
|
10
|
+
self.variant = 'standard'
|
11
|
+
self.f_cpu = '8000000L'
|
12
|
+
self.libraries = []
|
13
|
+
self.includes = []
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
module Workshop
|
2
|
+
class Project
|
3
|
+
class Setup
|
4
|
+
attr_accessor :project_name, :base_directory
|
5
|
+
|
6
|
+
def initialize(base_directory, project_name)
|
7
|
+
self.base_directory = base_directory
|
8
|
+
self.project_name = project_name
|
9
|
+
end
|
10
|
+
|
11
|
+
def run
|
12
|
+
create_base_directory
|
13
|
+
create_sub_directories
|
14
|
+
create_rakefile
|
15
|
+
copy_blink_app
|
16
|
+
end
|
17
|
+
|
18
|
+
def log_create(name)
|
19
|
+
puts "create #{name}".colorize(:green)
|
20
|
+
end
|
21
|
+
|
22
|
+
def relative(path)
|
23
|
+
to = Pathname.new(path)
|
24
|
+
from = Pathname.new(base_directory)
|
25
|
+
rel = to.relative_path_from(from)
|
26
|
+
rel.to_s
|
27
|
+
end
|
28
|
+
|
29
|
+
def create_base_directory
|
30
|
+
if Dir.exist?(base_directory)
|
31
|
+
puts "folder already exists"
|
32
|
+
else
|
33
|
+
log_create(relative(base_directory))
|
34
|
+
Dir.mkdir(base_directory)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def create_sub_directories
|
39
|
+
['src', 'config'].each do |dirname|
|
40
|
+
dir = base_directory + '/' + dirname
|
41
|
+
log_create(relative(dir))
|
42
|
+
Dir.mkdir(dir)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def resolve_file(file)
|
47
|
+
dir = File.dirname(__FILE__)
|
48
|
+
File.join(dir, file)
|
49
|
+
end
|
50
|
+
|
51
|
+
def read_file(file)
|
52
|
+
File.read(resolve_file(file))
|
53
|
+
end
|
54
|
+
|
55
|
+
def write_file(file, contents)
|
56
|
+
log_create(file)
|
57
|
+
File.open(base_directory + '/' + file, 'w') do |file|
|
58
|
+
file.puts contents
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def create_rakefile
|
63
|
+
template = ERB.new(read_file('../templates/Rakefile.erb'))
|
64
|
+
rakefile = template.result(binding)
|
65
|
+
write_file('Rakefile', rakefile)
|
66
|
+
end
|
67
|
+
|
68
|
+
def copy_blink_app
|
69
|
+
file = read_file('../templates/blink.cpp')
|
70
|
+
write_file('src/' + project_name + '.cpp', file)
|
71
|
+
end
|
72
|
+
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
@@ -0,0 +1,64 @@
|
|
1
|
+
module Workshop
|
2
|
+
class Tasks
|
3
|
+
include Rake::DSL
|
4
|
+
def initialize(builder, uploader)
|
5
|
+
|
6
|
+
desc 'create build directory'
|
7
|
+
task :init do
|
8
|
+
builder.create_build_directory
|
9
|
+
end
|
10
|
+
|
11
|
+
desc 'compile core, variant, and app source code'
|
12
|
+
task :compile => [:init] do
|
13
|
+
builder.compile
|
14
|
+
end
|
15
|
+
|
16
|
+
desc 'create archive from core, variant, and app source (except main loop file)'
|
17
|
+
task :archive => [:compile] do
|
18
|
+
builder.archive
|
19
|
+
end
|
20
|
+
|
21
|
+
desc 'link archive and main loop object file'
|
22
|
+
task :link => [:archive] do
|
23
|
+
builder.link
|
24
|
+
end
|
25
|
+
|
26
|
+
desc 'create eeprom file'
|
27
|
+
task :eeprom => [:link] do
|
28
|
+
builder.create_eeprom
|
29
|
+
end
|
30
|
+
|
31
|
+
desc 'create compiled *.hex file'
|
32
|
+
task :hex => [:link] do
|
33
|
+
builder.create_hex
|
34
|
+
end
|
35
|
+
|
36
|
+
desc 'delete build directory'
|
37
|
+
task :clean do
|
38
|
+
puts "Removing the build directory"
|
39
|
+
builder.clean
|
40
|
+
end
|
41
|
+
|
42
|
+
desc 'upload *.hex file to arduino'
|
43
|
+
task :upload => [:build] do
|
44
|
+
uploader.upload
|
45
|
+
end
|
46
|
+
|
47
|
+
desc 'open a console to the arduino (ctrl-a ctrl-| to exit)'
|
48
|
+
task :console do
|
49
|
+
sh "screen #{uploader.com_port} 9600"
|
50
|
+
end
|
51
|
+
|
52
|
+
desc 'build *.hex and eeprom files'
|
53
|
+
task :build => [:hex, :eeprom]
|
54
|
+
|
55
|
+
desc 'find connected arduino and update config/com_port.yml with port'
|
56
|
+
task :find do
|
57
|
+
# TODO - find COM port and create config/com_port.yml
|
58
|
+
end
|
59
|
+
|
60
|
+
desc 'build application'
|
61
|
+
task :default => :build
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
require 'workshop'
|
2
|
+
|
3
|
+
Workshop::Project.configure do |config|
|
4
|
+
config.project_name = '<%= project_name %>'
|
5
|
+
config.build.mcu = 'atmega328p'
|
6
|
+
config.build.core = 'arduino'
|
7
|
+
config.build.variant = 'standard'
|
8
|
+
# config.build.vid = '0x0000'
|
9
|
+
# config.build.pid = '0x0000'
|
10
|
+
config.build.f_cpu = '16000000L'
|
11
|
+
config.upload.protocol = 'arduino'
|
12
|
+
config.upload.speed = 57600
|
13
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
#line 1 "Blink.ino"
|
2
|
+
/*
|
3
|
+
Blink
|
4
|
+
Turns on an LED on for one second, then off for one second, repeatedly.
|
5
|
+
|
6
|
+
This example code is in the public domain.
|
7
|
+
*/
|
8
|
+
|
9
|
+
// Pin 13 has an LED connected on most Arduino boards.
|
10
|
+
// give it a name:
|
11
|
+
#include "Arduino.h"
|
12
|
+
void setup();
|
13
|
+
void loop();
|
14
|
+
#line 10
|
15
|
+
int led = 13;
|
16
|
+
|
17
|
+
// the setup routine runs once when you press reset:
|
18
|
+
void setup() {
|
19
|
+
// initialize the digital pin as an output.
|
20
|
+
pinMode(led, OUTPUT);
|
21
|
+
}
|
22
|
+
|
23
|
+
// the loop routine runs over and over again forever:
|
24
|
+
void loop() {
|
25
|
+
digitalWrite(led, HIGH); // turn the LED on (HIGH is the voltage level)
|
26
|
+
delay(100); // wait for a second
|
27
|
+
digitalWrite(led, LOW); // turn the LED off by making the voltage LOW
|
28
|
+
delay(100); // wait for a second
|
29
|
+
}
|
30
|
+
|
@@ -0,0 +1,56 @@
|
|
1
|
+
module Workshop
|
2
|
+
class Tools
|
3
|
+
attr_accessor :hardware_directory
|
4
|
+
|
5
|
+
def initialize(hardware_directory)
|
6
|
+
self.hardware_directory = hardware_directory
|
7
|
+
end
|
8
|
+
|
9
|
+
def tools_directory
|
10
|
+
hardware_directory + '/tools'
|
11
|
+
end
|
12
|
+
|
13
|
+
def bin_directory
|
14
|
+
tools_directory + '/avr/bin'
|
15
|
+
end
|
16
|
+
|
17
|
+
def etc_directory
|
18
|
+
tools_directory + '/avr/etc'
|
19
|
+
end
|
20
|
+
|
21
|
+
def avr_gcc
|
22
|
+
bin_directory + '/avr-gcc'
|
23
|
+
end
|
24
|
+
|
25
|
+
def avr_gpp
|
26
|
+
bin_directory + '/avr-g++'
|
27
|
+
end
|
28
|
+
|
29
|
+
def avr_ar
|
30
|
+
bin_directory + '/avr-ar'
|
31
|
+
end
|
32
|
+
|
33
|
+
def avr_objcopy
|
34
|
+
bin_directory + '/avr-objcopy'
|
35
|
+
end
|
36
|
+
|
37
|
+
def avrdude
|
38
|
+
bin_directory + '/avrdude'
|
39
|
+
end
|
40
|
+
|
41
|
+
def avrdude_config
|
42
|
+
etc_directory + '/avrdude.conf'
|
43
|
+
end
|
44
|
+
|
45
|
+
def compiler_for_extendion
|
46
|
+
@_extension_map ||= {
|
47
|
+
'.c' => avr_gcc,
|
48
|
+
'.cpp' => avr_gpp
|
49
|
+
}
|
50
|
+
end
|
51
|
+
|
52
|
+
def compiler(filename)
|
53
|
+
compiler_for_extendion[File.extname(filename)]
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module Workshop
|
2
|
+
class Uploader
|
3
|
+
include FileUtils
|
4
|
+
attr_accessor :project, :tools, :hex_filename
|
5
|
+
|
6
|
+
def initialize(project, hex_filename)
|
7
|
+
self.project = project
|
8
|
+
self.tools = Workshop::Tools.new project.arduino_hardware_directory
|
9
|
+
self.hex_filename = hex_filename
|
10
|
+
end
|
11
|
+
|
12
|
+
def com_port
|
13
|
+
project.com_port
|
14
|
+
end
|
15
|
+
|
16
|
+
def upload_command
|
17
|
+
[
|
18
|
+
tools.avrdude,
|
19
|
+
"-C #{tools.avrdude_config}",
|
20
|
+
"-v -v -v -v -v -v",
|
21
|
+
"-p #{project.build_mcu}",
|
22
|
+
"-c #{project.programmer}",
|
23
|
+
"-P #{project.com_port}",
|
24
|
+
"-b #{project.baud_rate}",
|
25
|
+
"-D",
|
26
|
+
"-Uflash:w:#{hex_filename}:i"
|
27
|
+
].join(' ')
|
28
|
+
end
|
29
|
+
|
30
|
+
def upload
|
31
|
+
sh upload_command
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Workshop::Arduino do
|
4
|
+
context 'arduino app is at "/app" and the core is set to "arduino"' do
|
5
|
+
let(:config) do
|
6
|
+
double(:configuration).tap do |config|
|
7
|
+
config.stub(:arduino_app_directory).and_return('/app')
|
8
|
+
config.stub(:build_core).and_return('arduino')
|
9
|
+
end
|
10
|
+
end
|
11
|
+
subject do
|
12
|
+
Workshop::Arduino.new(config).tap do |arduino|
|
13
|
+
arduino.stub(:hardware_path).and_return('/hardware')
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
describe '#hardware_directory' do
|
18
|
+
it 'returns "/app/hardware"' do
|
19
|
+
expected = "/app/hardware"
|
20
|
+
expect(subject.hardware_directory).to eq(expected)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
describe '#core_directory' do
|
25
|
+
it 'returns "/app/hardware/arduino/cores/arduino"' do
|
26
|
+
expected = "/app/hardware/arduino/cores/arduino"
|
27
|
+
expect(subject.core_directory).to eq(expected)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Workshop::Project::Configuration::Build do
|
4
|
+
|
5
|
+
describe '#mcu' do
|
6
|
+
it 'returns "atmega328p" as the default mcu' do
|
7
|
+
expect(subject.mcu).to eq('atmega328p')
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
describe '#core' do
|
12
|
+
it 'returns "arduino" as the default core' do
|
13
|
+
expect(subject.core).to eq('arduino')
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
describe '#variant' do
|
18
|
+
it 'returns "standard" as the default variant' do
|
19
|
+
expect(subject.variant).to eq('standard')
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
describe '#f_cpu' do
|
24
|
+
it 'returns "8000000L" as the default cpu frequency' do
|
25
|
+
expect(subject.f_cpu).to eq('8000000L')
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
describe '#vid' do
|
30
|
+
it 'returns nil as the default vid' do
|
31
|
+
expect(subject.vid).to eq(nil)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
describe '#pid' do
|
36
|
+
it 'returns nil as the default pid' do
|
37
|
+
expect(subject.pid).to eq(nil)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
describe '#libraries' do
|
42
|
+
it 'initialized with an empty array of libraries' do
|
43
|
+
expect(subject.libraries).to eq([])
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
describe '#includes' do
|
48
|
+
it 'initialized with an empty array of includes' do
|
49
|
+
expect(subject.includes).to eq([])
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|