rad 0.2.2 → 0.2.9
Sign up to get free protection for your applications and to get access to all the features.
- data/History.txt +34 -0
- data/Manifest.txt +113 -7
- data/{README.txt → README.rdoc} +17 -5
- data/Rakefile +3 -0
- data/bin/rad +106 -1
- data/lib/examples/add_hysteresis.rb +13 -0
- data/lib/examples/basic_blink.rb +10 -0
- data/lib/examples/blink_m_address_assignment.rb +104 -0
- data/lib/examples/blink_m_hello.rb +14 -0
- data/lib/examples/blink_m_multi.rb +61 -0
- data/lib/examples/blink_with_serial.rb +16 -0
- data/lib/examples/configure_pa_lcd_boot.rb +91 -0
- data/lib/examples/debounce_methods.rb +49 -0
- data/lib/examples/external_variable_fu.rb +26 -0
- data/lib/examples/external_variables.rb +32 -0
- data/lib/examples/first_sound.rb +23 -0
- data/lib/examples/frequency_generator.rb +30 -0
- data/lib/examples/hello_array.rb +48 -0
- data/lib/examples/hello_array2.rb +79 -0
- data/lib/examples/hello_array_eeprom.rb +59 -0
- data/lib/examples/hello_clock.rb +84 -0
- data/lib/examples/hello_eeprom.rb +51 -0
- data/lib/examples/hello_eeprom_lcdpa.rb +81 -0
- data/lib/examples/hello_format_print.rb +94 -0
- data/lib/examples/hello_lcd_charset.rb +75 -0
- data/lib/examples/hello_pa_lcd.rb +59 -0
- data/lib/examples/hello_servos.rb +88 -0
- data/lib/examples/hello_spectra_sound.rb +38 -0
- data/lib/examples/hello_world.rb +11 -0
- data/lib/examples/hello_xbee.rb +12 -0
- data/lib/examples/hysteresis_duel.rb +39 -0
- data/lib/examples/i2c_with_clock_chip.rb +124 -0
- data/lib/examples/midi_beat_box.rb +86 -0
- data/lib/examples/midi_scales.rb +94 -0
- data/lib/examples/motor_knob.rb +30 -0
- data/lib/examples/servo_buttons.rb +23 -0
- data/lib/examples/servo_calibrate_continuous.rb +92 -0
- data/lib/examples/servo_throttle.rb +40 -0
- data/lib/examples/sparkfun_lcd.rb +48 -0
- data/lib/examples/spectra_soft_pot.rb +34 -0
- data/lib/examples/times_method.rb +8 -0
- data/lib/examples/toggle.rb +10 -0
- data/lib/examples/twitter.rb +57 -0
- data/lib/examples/two_wire.rb +14 -0
- data/lib/libraries/AFSoftSerial/AFSoftSerial.cpp +321 -0
- data/lib/libraries/AFSoftSerial/AFSoftSerial.h +61 -0
- data/lib/libraries/AFSoftSerial/keywords.txt +18 -0
- data/lib/libraries/AF_XPort/AF_XPort.cpp +166 -0
- data/lib/libraries/AF_XPort/AF_XPort.h +48 -0
- data/lib/libraries/DS1307/DS1307.cpp +162 -0
- data/lib/libraries/DS1307/DS1307.h +66 -0
- data/lib/libraries/{SWSerLCDpa → DS1307}/keywords.txt +1 -1
- data/lib/libraries/FrequencyTimer2/FrequencyTimer2.cpp +144 -0
- data/lib/libraries/FrequencyTimer2/FrequencyTimer2.h +42 -0
- data/lib/libraries/FrequencyTimer2/keywords.txt +22 -0
- data/lib/libraries/I2CEEPROM/I2CEEPROM.cpp +120 -0
- data/lib/libraries/I2CEEPROM/I2CEEPROM.h +70 -0
- data/lib/libraries/I2CEEPROM/keywords.txt +21 -0
- data/lib/libraries/LoopTimer/LoopTimer.cpp +35 -0
- data/lib/libraries/LoopTimer/LoopTimer.h +34 -0
- data/lib/libraries/LoopTimer/keywords.txt +27 -0
- data/lib/libraries/OneWire/OneWire.cpp +194 -0
- data/lib/libraries/OneWire/OneWire.h +63 -0
- data/lib/libraries/OneWire/keywords.txt +35 -0
- data/lib/libraries/OneWire/readme.txt +13 -0
- data/lib/libraries/SWSerLCDpa/SWSerLCDpa.cpp +93 -47
- data/lib/libraries/SWSerLCDpa/SWSerLCDpa.h +16 -9
- data/lib/libraries/SWSerLCDsf/SWSerLCDsf.cpp +311 -0
- data/lib/libraries/SWSerLCDsf/SWSerLCDsf.h +67 -0
- data/lib/libraries/Servo/Servo.cpp +192 -0
- data/lib/libraries/Servo/Servo.h +61 -0
- data/lib/libraries/Stepper/Stepper.cpp +220 -0
- data/lib/libraries/Stepper/Stepper.h +86 -0
- data/lib/libraries/Stepper/keywords.txt +28 -0
- data/lib/libraries/Wire/Wire.cpp +262 -0
- data/lib/libraries/Wire/Wire.h +67 -0
- data/lib/libraries/Wire/keywords.txt +31 -0
- data/lib/libraries/Wire/twi.h +57 -0
- data/lib/libraries/Wire/utility/twi.c +449 -0
- data/lib/libraries/Wire/utility/twi.h +57 -0
- data/lib/plugins/bitwise_ops.rb +54 -0
- data/lib/plugins/blink.rb +25 -0
- data/lib/plugins/blink_m.rb +356 -0
- data/lib/plugins/debounce.rb +138 -0
- data/lib/plugins/debug_output_to_lcd.rb +71 -0
- data/lib/plugins/hysteresis.rb +52 -0
- data/lib/plugins/input_output_state.rb +84 -0
- data/lib/plugins/lcd_padding.rb +58 -0
- data/lib/plugins/mem_test.rb +37 -0
- data/lib/plugins/midi.rb +60 -0
- data/lib/plugins/parallax_ping.rb +50 -0
- data/lib/plugins/servo_pulse.rb +31 -0
- data/lib/plugins/servo_setup.rb +86 -0
- data/lib/plugins/smoother.rb +54 -0
- data/lib/plugins/spark_fun_serial_lcd.rb +100 -0
- data/lib/plugins/spectra_symbol.rb +79 -0
- data/lib/plugins/twitter_connect.rb +145 -0
- data/lib/rad/README.rdoc +5 -0
- data/lib/rad/arduino_plugin.rb +246 -0
- data/lib/rad/arduino_sketch.rb +351 -257
- data/lib/rad/generators/makefile/makefile.erb +1 -1
- data/lib/rad/generators/makefile/makefile.rb +9 -10
- data/lib/rad/hardware_library.rb +813 -0
- data/lib/rad/init.rb +3 -1
- data/lib/rad/rad_processor.rb +128 -0
- data/lib/rad/rad_rewriter.rb +94 -0
- data/lib/rad/rad_type_checker.rb +26 -0
- data/lib/rad/sim/arduino_sketch.rb +57 -0
- data/lib/rad/sketch_compiler.rb +47 -0
- data/lib/rad/tasks/build_and_make.rake +146 -24
- data/lib/rad/variable_processing.rb +153 -0
- data/lib/rad/version.rb +1 -1
- data/spec/examples/hello_world.rb +11 -0
- data/spec/examples/serial_motor.rb +12 -0
- data/spec/models/sketch_compiler_spec.rb +96 -0
- data/spec/sim/hello_world_spec.rb +42 -0
- data/test/test_array_processing.rb +179 -0
- data/test/test_plugin_loading.rb +151 -0
- data/test/test_translation_post_processing.rb +185 -0
- data/test/test_variable_processing.rb +238 -0
- data/website/index.html +22 -7
- data/website/stylesheets/screen.css +32 -1
- metadata +130 -13
data/lib/rad/init.rb
CHANGED
@@ -5,8 +5,10 @@ unless defined?(PROJECT_DIR_NAME)
|
|
5
5
|
PROJECT_DIR_NAME = a[a.length-1]
|
6
6
|
end
|
7
7
|
|
8
|
+
PLUGIN_C_VAR_TYPES = "int|void|unsigned|long|short|uint8_t|static|byte|char\\*|uint8_t"
|
8
9
|
|
9
|
-
|
10
|
+
|
11
|
+
%w(generators/makefile/makefile.rb rad_processor.rb rad_rewriter.rb rad_type_checker.rb variable_processing.rb arduino_sketch.rb arduino_plugin.rb hardware_library.rb tasks/rad.rb sketch_compiler.rb).each do |path|
|
10
12
|
require File.expand_path("#{RAD_ROOT}/vendor/rad/#{path}")
|
11
13
|
end
|
12
14
|
|
@@ -0,0 +1,128 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'ruby_to_ansi_c'
|
3
|
+
|
4
|
+
class RADProcessor < RubyToAnsiC
|
5
|
+
|
6
|
+
def self.translator
|
7
|
+
unless defined? @translator then
|
8
|
+
@translator = CompositeSexpProcessor.new
|
9
|
+
@translator << RADRewriter.new
|
10
|
+
@translator << RADTypeChecker.new
|
11
|
+
@translator << R2CRewriter.new
|
12
|
+
@translator << self.new
|
13
|
+
@translator.on_error_in(:defn) do |processor, exp, err|
|
14
|
+
result = processor.expected.new
|
15
|
+
case result
|
16
|
+
when Array then
|
17
|
+
result << :error
|
18
|
+
end
|
19
|
+
msg = "// ERROR: #{err.class}: #{err}"
|
20
|
+
msg += " in #{exp.inspect}" unless exp.nil? or $TESTING
|
21
|
+
msg += " from #{caller.join(', ')}" unless $TESTING
|
22
|
+
result << msg
|
23
|
+
result
|
24
|
+
end
|
25
|
+
end
|
26
|
+
@translator
|
27
|
+
end
|
28
|
+
|
29
|
+
def process_iasgn(exp)
|
30
|
+
name = exp.shift
|
31
|
+
val = process exp.shift
|
32
|
+
"__#{name.to_s.sub(/^@/, '')} = #{val}"
|
33
|
+
end
|
34
|
+
|
35
|
+
def process_ivar(exp)
|
36
|
+
name = exp.shift
|
37
|
+
"__#{name.to_s.sub(/^@/, '')}"
|
38
|
+
end
|
39
|
+
|
40
|
+
def process_iter(exp)
|
41
|
+
# the array identifer may be in one of two locations
|
42
|
+
# when using the instance variable (ivar) style it is located at exp[0][1][1]
|
43
|
+
if exp[0][1][1]
|
44
|
+
enum = exp[0][1][1]
|
45
|
+
enum = "__#{enum.to_s.sub(/^@/, '')}" if enum.to_s =~ /^@/
|
46
|
+
# for local variables it is located at exp[0][1][2]
|
47
|
+
elsif exp[0][1][2]
|
48
|
+
enum = exp[0][1][2]
|
49
|
+
end
|
50
|
+
|
51
|
+
out = []
|
52
|
+
# Only support enums in C-land # not sure if this comment if valid anymore
|
53
|
+
raise UnsupportedNodeError if exp[0][1].nil? # HACK ugly
|
54
|
+
@env.scope do
|
55
|
+
|
56
|
+
call = process exp.shift
|
57
|
+
var = process(exp.shift).intern # semi-HACK-y
|
58
|
+
body = process exp.shift
|
59
|
+
|
60
|
+
# array types from varible_processing>post_process_arrays and arduino_sketch>array
|
61
|
+
$array_types.each do |k,v|
|
62
|
+
@array_type = v if k == enum.to_s.sub(/^__/,"")
|
63
|
+
end
|
64
|
+
|
65
|
+
index_helper = $array_index_helpers.shift
|
66
|
+
index = "index_#{index_helper}" # solves redeclaration issue
|
67
|
+
|
68
|
+
body += ";" unless body =~ /[;}]\Z/
|
69
|
+
body.gsub!(/\n\n+/, "\n")
|
70
|
+
|
71
|
+
out << "unsigned int #{index};" # shouldn't need more than int
|
72
|
+
out << "for (#{index} = 0; #{index} < (int) (sizeof(#{enum}) / sizeof(#{enum}[0])); #{index}++) {"
|
73
|
+
out << "#{@array_type} #{var} = #{enum}[#{index}];"
|
74
|
+
out << body
|
75
|
+
out << "}"
|
76
|
+
end
|
77
|
+
|
78
|
+
return out.join("\n")
|
79
|
+
end
|
80
|
+
|
81
|
+
def process_lasgn(exp)
|
82
|
+
out = ""
|
83
|
+
|
84
|
+
var = exp.shift
|
85
|
+
value = exp.shift
|
86
|
+
# grab the size of the args, if any, before process converts to a string
|
87
|
+
arg_count = 0
|
88
|
+
arg_count = value.length - 1 if value.first == :array
|
89
|
+
args = value
|
90
|
+
|
91
|
+
exp_type = exp.sexp_type
|
92
|
+
@env.add var.to_sym, exp_type
|
93
|
+
var_type = self.class.c_type exp_type
|
94
|
+
|
95
|
+
if exp_type.list? then
|
96
|
+
assert_type args, :array
|
97
|
+
|
98
|
+
raise "array must be of one type" unless args.sexp_type == Type.homo
|
99
|
+
|
100
|
+
# HACK: until we figure out properly what to do w/ zarray
|
101
|
+
# before we know what its type is, we will default to long.
|
102
|
+
array_type = args.sexp_types.empty? ? 'void *' : self.class.c_type(args.sexp_types.first)
|
103
|
+
# we can fix array here..
|
104
|
+
args.shift # :arglist
|
105
|
+
out << "#{var} = (#{array_type}) malloc(sizeof(#{array_type}) * #{args.length});\n"
|
106
|
+
args.each_with_index do |o,i|
|
107
|
+
out << "#{var}[#{i}] = #{process o};\n"
|
108
|
+
end
|
109
|
+
else
|
110
|
+
out << "#{var} = #{process args}"
|
111
|
+
end
|
112
|
+
|
113
|
+
out.sub!(/;\n\Z/, '')
|
114
|
+
|
115
|
+
return out
|
116
|
+
end
|
117
|
+
|
118
|
+
def process_str(exp)
|
119
|
+
s = exp.shift.gsub(/\n/, '\\n')
|
120
|
+
if s.size == 1
|
121
|
+
return "\'#{s}\'"
|
122
|
+
else
|
123
|
+
return "\"#{s}\""
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
|
128
|
+
end
|
@@ -0,0 +1,94 @@
|
|
1
|
+
require 'ruby_to_ansi_c'
|
2
|
+
|
3
|
+
class RADRewriter < Rewriter
|
4
|
+
|
5
|
+
def process_iter(exp)
|
6
|
+
call = process exp.shift
|
7
|
+
var = process exp.shift
|
8
|
+
body = process exp.shift
|
9
|
+
|
10
|
+
var = s(:dasgn_curr, Unique.next) if var.nil?
|
11
|
+
|
12
|
+
assert_type call, :call
|
13
|
+
|
14
|
+
if call[2] != :each then # TODO: fix call[n] (api)
|
15
|
+
call.shift # :call
|
16
|
+
lhs = call.shift
|
17
|
+
method_name = call.shift
|
18
|
+
|
19
|
+
case method_name
|
20
|
+
when :downto then
|
21
|
+
var.shift #
|
22
|
+
start_value = lhs
|
23
|
+
finish_value = call.pop.pop # not sure about this
|
24
|
+
var_name = var.shift
|
25
|
+
body.find_and_replace_all(:dvar, :lvar)
|
26
|
+
result = s(:dummy,
|
27
|
+
s(:lasgn, var_name, start_value),
|
28
|
+
s(:while,
|
29
|
+
s(:call, s(:lvar, var_name), :>=,
|
30
|
+
s(:arglist, finish_value)),
|
31
|
+
s(:block,
|
32
|
+
body,
|
33
|
+
s(:lasgn, var_name,
|
34
|
+
s(:call, s(:lvar, var_name), :-,
|
35
|
+
s(:arglist, s(:lit, 1))))), true))
|
36
|
+
when :upto then
|
37
|
+
# REFACTOR: completely duped from above and direction changed
|
38
|
+
var.shift #
|
39
|
+
start_value = lhs
|
40
|
+
finish_value = call.pop.pop # not sure about this
|
41
|
+
var_name = var.shift
|
42
|
+
body.find_and_replace_all(:dvar, :lvar)
|
43
|
+
result = s(:dummy,
|
44
|
+
s(:lasgn, var_name, start_value),
|
45
|
+
s(:while,
|
46
|
+
s(:call, s(:lvar, var_name), :<=,
|
47
|
+
s(:arglist, finish_value)),
|
48
|
+
s(:block,
|
49
|
+
body,
|
50
|
+
s(:lasgn, var_name,
|
51
|
+
s(:call, s(:lvar, var_name), :+,
|
52
|
+
s(:arglist, s(:lit, 1))))), true))
|
53
|
+
when :times then
|
54
|
+
# REFACTOR: mostly duped from above and gave default start value of 0
|
55
|
+
# and a finish value that was the start value above
|
56
|
+
var.shift
|
57
|
+
start_value = s(:lit, 0)
|
58
|
+
finish_value = lhs
|
59
|
+
var_name = var.shift
|
60
|
+
body.find_and_replace_all(:dvar, :lvar)
|
61
|
+
result = s(:dummy,
|
62
|
+
s(:lasgn, var_name, start_value),
|
63
|
+
s(:while,
|
64
|
+
s(:call, s(:lvar, var_name), :<,
|
65
|
+
s(:arglist, finish_value)),
|
66
|
+
s(:block,
|
67
|
+
body,
|
68
|
+
s(:lasgn, var_name,
|
69
|
+
s(:call, s(:lvar, var_name), :+,
|
70
|
+
s(:arglist, s(:lit, 1))))), true))
|
71
|
+
when :define_method then
|
72
|
+
# BEFORE: [:iter, [:call, nil, :define_method, [:array, [:lit, :bmethod_added]]], [:dasgn_curr, :x], [:call, [:dvar, :x], :+, [:array, [:lit, 1]]]]
|
73
|
+
# we want to get it rewritten for the scope/block context, so:
|
74
|
+
# - throw call away
|
75
|
+
# - rewrite to args
|
76
|
+
# - plop body into a scope
|
77
|
+
# AFTER: [:block, [:args, :x], [:call, [:lvar, :x], :+, [:arglist, [:lit, 1]]]]
|
78
|
+
var.find_and_replace_all(:dasgn_curr, :args)
|
79
|
+
body.find_and_replace_all(:dvar, :lvar)
|
80
|
+
result = s(:block, var, body)
|
81
|
+
else
|
82
|
+
# HACK we butchered call up top
|
83
|
+
result = s(:iter, s(:call, lhs, method_name, call.shift), var, body)
|
84
|
+
end
|
85
|
+
else
|
86
|
+
if var.nil? then
|
87
|
+
var = s(:lvar, Unique.next)
|
88
|
+
end
|
89
|
+
|
90
|
+
s(:iter, call, var, body)
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'ruby_to_ansi_c'
|
2
|
+
|
3
|
+
class RADTypeChecker < TypeChecker
|
4
|
+
|
5
|
+
def process_const(exp)
|
6
|
+
c = exp.shift
|
7
|
+
if c.to_s =~ /^[A-Z]/ then
|
8
|
+
# TODO: validate that it really is a const?
|
9
|
+
# uber hackery
|
10
|
+
# since constants are defined in the arduino_sketch define method and
|
11
|
+
# we can't inject them into the methods
|
12
|
+
# transport them here with a $define_types hash
|
13
|
+
|
14
|
+
$define_types.each do |k,v|
|
15
|
+
if k == c.to_s
|
16
|
+
@const_type = eval "Type.#{v}"
|
17
|
+
end
|
18
|
+
end
|
19
|
+
return t(:const, c, @const_type)
|
20
|
+
else
|
21
|
+
raise "I don't know what to do with const #{c.inspect}. It doesn't look like a class."
|
22
|
+
end
|
23
|
+
raise "need to finish process_const in #{self.class}"
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
ON = true
|
2
|
+
OFF = !ON
|
3
|
+
HIGH = ON
|
4
|
+
LOW = !HIGH
|
5
|
+
|
6
|
+
class ArduinoSketch
|
7
|
+
attr_accessor :pins
|
8
|
+
|
9
|
+
def initialize
|
10
|
+
@pins = self.class.instance_variable_get("@pins")
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.output_pin(num, opts)
|
14
|
+
module_eval "@pins ||= []"
|
15
|
+
module_eval do
|
16
|
+
@pins << Pin.new( num, :type => :output )
|
17
|
+
end
|
18
|
+
|
19
|
+
if opts[:as]
|
20
|
+
module_eval <<-CODE
|
21
|
+
def #{opts[:as]}
|
22
|
+
pins.select{|p| p.num == #{num}}.first
|
23
|
+
end
|
24
|
+
CODE
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def loop
|
29
|
+
end
|
30
|
+
|
31
|
+
def digitalWrite( pin, value )
|
32
|
+
to_change = pins.select{|p| p.num == pin.num}.first
|
33
|
+
to_change.value = value
|
34
|
+
end
|
35
|
+
|
36
|
+
def delay( millis )
|
37
|
+
end
|
38
|
+
|
39
|
+
# def serial_read
|
40
|
+
# end
|
41
|
+
|
42
|
+
# def serial_available
|
43
|
+
# end
|
44
|
+
|
45
|
+
# def blink
|
46
|
+
# end
|
47
|
+
end
|
48
|
+
|
49
|
+
class Pin
|
50
|
+
attr_accessor :num, :type, :value
|
51
|
+
|
52
|
+
def initialize num, opts
|
53
|
+
@num = num
|
54
|
+
@type = opts[:type]
|
55
|
+
@value = opts[:value] || false
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
# TODO:
|
2
|
+
# compilation
|
3
|
+
# gather pieces of code we need as strings
|
4
|
+
# translate non-loop methods
|
5
|
+
# do plugin stuff
|
6
|
+
# deal with examples/ exception
|
7
|
+
# manage upload process
|
8
|
+
# compose_setup should move in here entirely
|
9
|
+
|
10
|
+
# require 'arduino_sketch'
|
11
|
+
|
12
|
+
class SketchCompiler
|
13
|
+
attr_accessor :path, :body, :klass, :target_dir, :name
|
14
|
+
|
15
|
+
def initialize path_to_sketch
|
16
|
+
@path = File.expand_path(path_to_sketch)
|
17
|
+
@body = open(@path).read
|
18
|
+
@name = @path.split("/").last.split(".").first
|
19
|
+
@klass = @name.split(".").first.split("_").collect{|c| c.capitalize}.join("")
|
20
|
+
@target_dir = parent_dir
|
21
|
+
end
|
22
|
+
|
23
|
+
def parent_dir
|
24
|
+
self.path.split("/")[0..@path.split("/").length-2].join("/")
|
25
|
+
end
|
26
|
+
|
27
|
+
def build_dir
|
28
|
+
"#{self.target_dir}/#{self.name}"
|
29
|
+
end
|
30
|
+
|
31
|
+
def create_build_dir! optional_path_prefix=nil
|
32
|
+
self.target_dir = optional_path_prefix if optional_path_prefix
|
33
|
+
mkdir_p build_dir
|
34
|
+
end
|
35
|
+
|
36
|
+
def process_constants
|
37
|
+
self.body.gsub!("HIGH", "1")
|
38
|
+
self.body.gsub!("LOW", "0")
|
39
|
+
self.body.gsub!("ON", "1")
|
40
|
+
self.body.gsub!("OFF", "0")
|
41
|
+
end
|
42
|
+
|
43
|
+
def sketch_methods
|
44
|
+
self.body.scan(/^\s*def\s.\w*/).collect{ |m| m.gsub(/\s*def\s*/, "") }
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
@@ -1,6 +1,43 @@
|
|
1
1
|
require File.expand_path(File.dirname(__FILE__) + "/../init.rb")
|
2
2
|
require 'ruby_to_ansi_c'
|
3
3
|
|
4
|
+
C_VAR_TYPES = "unsigned|int|long|double|str|char|byte|bool"
|
5
|
+
|
6
|
+
# incredibly primitive tests
|
7
|
+
# rake test:compile or rake test:upload
|
8
|
+
# runs through all sketches in the example directory
|
9
|
+
|
10
|
+
def run_tests(sketch, type)
|
11
|
+
sh %{rake make:#{type} sketch=examples/#{sketch}}
|
12
|
+
end
|
13
|
+
|
14
|
+
namespace :test do
|
15
|
+
|
16
|
+
desc "iterate through all the sketches in the example directory"
|
17
|
+
task :upload => :gather do
|
18
|
+
@examples.each {|e| run_tests(e, "upload")}
|
19
|
+
end
|
20
|
+
|
21
|
+
task :compile => :gather do
|
22
|
+
|
23
|
+
@examples.each {|e| run_tests(e, "compile")}
|
24
|
+
end
|
25
|
+
|
26
|
+
desc "gather all tests"
|
27
|
+
task :gather do # => "make:upload" do
|
28
|
+
@examples = []
|
29
|
+
if ENV['sketch']
|
30
|
+
@examples << ENV['sketch']
|
31
|
+
else
|
32
|
+
Dir.entries( File.expand_path("#{RAD_ROOT}/examples/") ).each do |f|
|
33
|
+
@examples << f.split('.').first if (f =~ /\.rb$/)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
39
|
+
|
40
|
+
|
4
41
|
namespace :make do
|
5
42
|
|
6
43
|
desc "compile the sketch and then upload it to your Arduino board"
|
@@ -9,70 +46,155 @@ namespace :make do
|
|
9
46
|
puts "Reset the Arduino and hit enter.\n==If your board doesn't need it, you can turn off this prompt in config/software.yml=="
|
10
47
|
STDIN.gets.chomp
|
11
48
|
end
|
12
|
-
sh %{cd #{
|
49
|
+
sh %{cd #{@sketch.build_dir}; make upload}
|
13
50
|
end
|
14
|
-
|
51
|
+
|
15
52
|
desc "generate a makefile and use it to compile the .cpp"
|
16
53
|
task :compile => [:clean_sketch_dir, "build:sketch"] do # should also depend on "build:sketch"
|
17
|
-
Makefile.compose_for_sketch( @
|
54
|
+
Makefile.compose_for_sketch( @sketch.build_dir )
|
55
|
+
|
56
|
+
# not allowed? sh %{export PATH=#{Makefile.software_params[:arduino_root]}/tools/avr/bin:$PATH}
|
57
|
+
sh %{cd #{@sketch.build_dir}; make depend; make}
|
58
|
+
end
|
59
|
+
|
60
|
+
desc "generate a makefile and use it to compile the .cpp using the current .cpp file"
|
61
|
+
task :compile_cpp => ["build:sketch_dir", "build:gather_required_plugins", "build:plugin_setup", "build:setup", :clean_sketch_dir] do # should also depend on "build:sketch"
|
62
|
+
Makefile.compose_for_sketch( @sketch.build_dir )
|
18
63
|
# not allowed? sh %{export PATH=#{Makefile.software_params[:arduino_root]}/tools/avr/bin:$PATH}
|
19
|
-
sh %{cd #{
|
64
|
+
sh %{cd #{@sketch.build_dir}; make depend; make}
|
65
|
+
end
|
66
|
+
|
67
|
+
desc "generate a makefile and use it to compile the .cpp and upload it using current .cpp file"
|
68
|
+
task :upload_cpp => ["build:sketch_dir", "build:gather_required_plugins", "build:plugin_setup", "build:setup", :clean_sketch_dir] do # should also depend on "build:sketch"
|
69
|
+
Makefile.compose_for_sketch( @sketch.build_dir )
|
70
|
+
# not allowed? sh %{export PATH=#{Makefile.software_params[:arduino_root]}/tools/avr/bin:$PATH}
|
71
|
+
sh %{cd #{@sketch.build_dir}; make depend; make upload}
|
20
72
|
end
|
21
73
|
|
22
74
|
task :clean_sketch_dir => ["build:file_list", "build:sketch_dir"] do
|
23
|
-
@
|
24
|
-
|
25
|
-
FileList.new(Dir.entries("#{RAD_ROOT}/#{@sketch_name}")).exclude("#{@sketch_name}.cpp").exclude(/^\./).each do |f|
|
26
|
-
sh %{rm #{RAD_ROOT}/#{@sketch_name}/#{f}}
|
75
|
+
FileList.new(Dir.entries("#{@sketch.build_dir}")).exclude("#{@sketch.name}.cpp").exclude(/^\./).each do |f|
|
76
|
+
sh %{rm #{@sketch.build_dir}/#{f}}
|
27
77
|
end
|
28
78
|
end
|
79
|
+
|
29
80
|
end
|
30
81
|
|
31
82
|
namespace :build do
|
32
83
|
|
33
84
|
desc "actually build the sketch"
|
34
|
-
task :sketch => [:file_list, :sketch_dir, :setup] do
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
85
|
+
task :sketch => [:file_list, :sketch_dir, :gather_required_plugins, :plugin_setup, :setup] do
|
86
|
+
c_methods = []
|
87
|
+
sketch_signatures = []
|
88
|
+
# until we better understand RubyToC let's see what's happening on errors
|
89
|
+
@sketch.sketch_methods.each do |meth|
|
90
|
+
raw_rtc_meth = RADProcessor.translate(constantize(@sketch.klass), meth)
|
91
|
+
puts "Translator Error: #{raw_rtc_meth.inspect}" if raw_rtc_meth =~ /\/\/ ERROR:/
|
92
|
+
c_methods << raw_rtc_meth unless meth == "setup"
|
93
|
+
# treat the setup method differently
|
94
|
+
@additional_setup = [] if meth == "setup"
|
95
|
+
raw_rtc_meth.each {|m| @additional_setup << ArduinoSketch.add_to_setup(m) } if meth == "setup"
|
96
|
+
end
|
97
|
+
c_methods.each {|meth| sketch_signatures << "#{meth.scan(/^\w*\s?\*?\n.*\)/)[0].gsub("\n", " ")};" }
|
98
|
+
clean_c_methods = []
|
99
|
+
# remove external variables that were previously injected
|
100
|
+
c_methods.join("\n").each { |meth| clean_c_methods << ArduinoSketch.post_process_ruby_to_c_methods(meth) }
|
101
|
+
c_methods_with_timer = clean_c_methods.join.gsub(/loop\(\)\s\{/,"loop() {")
|
102
|
+
# last chance to add/change setup
|
103
|
+
@setup[2] << sketch_signatures.join("\n") unless sketch_signatures.empty?
|
104
|
+
# add special setup method to existing setup if present
|
105
|
+
if @additional_setup
|
106
|
+
@setup[2] << "void additional_setup();" # declaration
|
107
|
+
@setup[4] << "\tadditional_setup();" # call from setup
|
108
|
+
@setup[5] << @additional_setup.join("") #
|
109
|
+
end
|
110
|
+
result = "#{@setup.join( "\n" )}\n#{c_methods_with_timer}\n"
|
111
|
+
File.open("#{@sketch.build_dir}/#{@sketch.name}.cpp", "w"){|f| f << result}
|
41
112
|
end
|
42
113
|
|
43
114
|
# needs to write the library include and the method signatures
|
44
115
|
desc "build setup function"
|
45
116
|
task :setup do
|
46
|
-
|
47
|
-
eval "class #{klass} < ArduinoSketch; end;"
|
117
|
+
eval "class #{@sketch.klass} < ArduinoSketch; end;"
|
48
118
|
|
49
|
-
@@as =
|
119
|
+
@@as = HardwareLibrary.new
|
50
120
|
|
51
121
|
delegate_methods = @@as.methods - Object.new.methods
|
52
122
|
delegate_methods.reject!{|m| m == "compose_setup"}
|
53
123
|
|
54
124
|
delegate_methods.each do |meth|
|
55
|
-
constantize(klass).module_eval <<-CODE
|
125
|
+
constantize(@sketch.klass).module_eval <<-CODE
|
56
126
|
def self.#{meth}(*args)
|
57
127
|
@@as.#{meth}(*args)
|
58
128
|
end
|
59
129
|
CODE
|
60
130
|
end
|
131
|
+
# allow variable declaration without quotes: @foo = int
|
132
|
+
["long","unsigned","int","byte","short"].each do |type|
|
133
|
+
constantize(@sketch.klass).module_eval <<-CODE
|
134
|
+
def self.#{type}
|
135
|
+
return "#{type}"
|
136
|
+
end
|
137
|
+
CODE
|
138
|
+
end
|
61
139
|
|
62
|
-
|
140
|
+
@sketch.process_constants
|
141
|
+
|
142
|
+
eval ArduinoSketch.pre_process(@sketch.body)
|
143
|
+
@@as.process_external_vars(constantize(@sketch.klass))
|
63
144
|
@setup = @@as.compose_setup
|
64
145
|
end
|
65
146
|
|
147
|
+
desc "add plugin methods"
|
148
|
+
task :plugin_setup do
|
149
|
+
$plugins_to_load.each do |name|
|
150
|
+
klass = name.split(".").first.split("_").collect{|c| c.capitalize}.join("")
|
151
|
+
eval "class #{klass} < ArduinoPlugin; end;"
|
152
|
+
|
153
|
+
@@ps = ArduinoPlugin.new
|
154
|
+
plugin_delegate_methods = @@ps.methods - Object.new.methods
|
155
|
+
plugin_delegate_methods.reject!{|m| m == "compose_setup"}
|
156
|
+
|
157
|
+
plugin_delegate_methods.each do |meth|
|
158
|
+
constantize(klass).module_eval <<-CODE
|
159
|
+
def self.#{meth}(*args)
|
160
|
+
@@ps.#{meth}(*args)
|
161
|
+
end
|
162
|
+
CODE
|
163
|
+
end
|
164
|
+
|
165
|
+
eval ArduinoPlugin.process(File.read("vendor/plugins/#{name}"))
|
166
|
+
|
167
|
+
end
|
168
|
+
@@no_plugins = ArduinoPlugin.new if @plugin_names.empty?
|
169
|
+
end
|
170
|
+
|
171
|
+
desc "determine which plugins to load based on use of methods in sketch"
|
172
|
+
task :gather_required_plugins do
|
173
|
+
@plugin_names.each do |name|
|
174
|
+
ArduinoPlugin.check_for_plugin_use(@sketch.body, File.read("vendor/plugins/#{name}"), name )
|
175
|
+
end
|
176
|
+
puts "#{$plugins_to_load.length} of #{$plugin_methods_hash.length} plugins are being loaded: #{$plugins_to_load.join(", ")}"
|
177
|
+
end
|
178
|
+
|
66
179
|
desc "setup target directory named after your sketch class"
|
67
180
|
task :sketch_dir => [:file_list] do
|
68
|
-
|
181
|
+
@sketch.create_build_dir!
|
69
182
|
end
|
70
183
|
|
71
184
|
task :file_list do
|
72
|
-
|
73
|
-
|
185
|
+
# take another look at this, since if the root directory name is changed, everything breaks
|
186
|
+
# perhaps we generate a constant when the project is generated an pop it here or in the init file
|
187
|
+
if ENV['sketch']
|
188
|
+
@sketch = SketchCompiler.new File.expand_path("#{ENV['sketch']}.rb")
|
189
|
+
else
|
190
|
+
# assume the only .rb file in the sketch dir is the sketch:
|
191
|
+
@sketch = SketchCompiler.new Dir.glob("#{File.expand_path(File.dirname(__FILE__))}/../../../*.rb").first
|
192
|
+
end
|
193
|
+
|
194
|
+
@plugin_names = []
|
195
|
+
Dir.entries( File.expand_path("#{RAD_ROOT}/vendor/plugins/") ).each do |f|
|
74
196
|
if (f =~ /\.rb$/)
|
75
|
-
@
|
197
|
+
@plugin_names << f
|
76
198
|
end
|
77
199
|
end
|
78
200
|
end
|