rad 0.2.2 → 0.2.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (123) hide show
  1. data/History.txt +34 -0
  2. data/Manifest.txt +113 -7
  3. data/{README.txt → README.rdoc} +17 -5
  4. data/Rakefile +3 -0
  5. data/bin/rad +106 -1
  6. data/lib/examples/add_hysteresis.rb +13 -0
  7. data/lib/examples/basic_blink.rb +10 -0
  8. data/lib/examples/blink_m_address_assignment.rb +104 -0
  9. data/lib/examples/blink_m_hello.rb +14 -0
  10. data/lib/examples/blink_m_multi.rb +61 -0
  11. data/lib/examples/blink_with_serial.rb +16 -0
  12. data/lib/examples/configure_pa_lcd_boot.rb +91 -0
  13. data/lib/examples/debounce_methods.rb +49 -0
  14. data/lib/examples/external_variable_fu.rb +26 -0
  15. data/lib/examples/external_variables.rb +32 -0
  16. data/lib/examples/first_sound.rb +23 -0
  17. data/lib/examples/frequency_generator.rb +30 -0
  18. data/lib/examples/hello_array.rb +48 -0
  19. data/lib/examples/hello_array2.rb +79 -0
  20. data/lib/examples/hello_array_eeprom.rb +59 -0
  21. data/lib/examples/hello_clock.rb +84 -0
  22. data/lib/examples/hello_eeprom.rb +51 -0
  23. data/lib/examples/hello_eeprom_lcdpa.rb +81 -0
  24. data/lib/examples/hello_format_print.rb +94 -0
  25. data/lib/examples/hello_lcd_charset.rb +75 -0
  26. data/lib/examples/hello_pa_lcd.rb +59 -0
  27. data/lib/examples/hello_servos.rb +88 -0
  28. data/lib/examples/hello_spectra_sound.rb +38 -0
  29. data/lib/examples/hello_world.rb +11 -0
  30. data/lib/examples/hello_xbee.rb +12 -0
  31. data/lib/examples/hysteresis_duel.rb +39 -0
  32. data/lib/examples/i2c_with_clock_chip.rb +124 -0
  33. data/lib/examples/midi_beat_box.rb +86 -0
  34. data/lib/examples/midi_scales.rb +94 -0
  35. data/lib/examples/motor_knob.rb +30 -0
  36. data/lib/examples/servo_buttons.rb +23 -0
  37. data/lib/examples/servo_calibrate_continuous.rb +92 -0
  38. data/lib/examples/servo_throttle.rb +40 -0
  39. data/lib/examples/sparkfun_lcd.rb +48 -0
  40. data/lib/examples/spectra_soft_pot.rb +34 -0
  41. data/lib/examples/times_method.rb +8 -0
  42. data/lib/examples/toggle.rb +10 -0
  43. data/lib/examples/twitter.rb +57 -0
  44. data/lib/examples/two_wire.rb +14 -0
  45. data/lib/libraries/AFSoftSerial/AFSoftSerial.cpp +321 -0
  46. data/lib/libraries/AFSoftSerial/AFSoftSerial.h +61 -0
  47. data/lib/libraries/AFSoftSerial/keywords.txt +18 -0
  48. data/lib/libraries/AF_XPort/AF_XPort.cpp +166 -0
  49. data/lib/libraries/AF_XPort/AF_XPort.h +48 -0
  50. data/lib/libraries/DS1307/DS1307.cpp +162 -0
  51. data/lib/libraries/DS1307/DS1307.h +66 -0
  52. data/lib/libraries/{SWSerLCDpa → DS1307}/keywords.txt +1 -1
  53. data/lib/libraries/FrequencyTimer2/FrequencyTimer2.cpp +144 -0
  54. data/lib/libraries/FrequencyTimer2/FrequencyTimer2.h +42 -0
  55. data/lib/libraries/FrequencyTimer2/keywords.txt +22 -0
  56. data/lib/libraries/I2CEEPROM/I2CEEPROM.cpp +120 -0
  57. data/lib/libraries/I2CEEPROM/I2CEEPROM.h +70 -0
  58. data/lib/libraries/I2CEEPROM/keywords.txt +21 -0
  59. data/lib/libraries/LoopTimer/LoopTimer.cpp +35 -0
  60. data/lib/libraries/LoopTimer/LoopTimer.h +34 -0
  61. data/lib/libraries/LoopTimer/keywords.txt +27 -0
  62. data/lib/libraries/OneWire/OneWire.cpp +194 -0
  63. data/lib/libraries/OneWire/OneWire.h +63 -0
  64. data/lib/libraries/OneWire/keywords.txt +35 -0
  65. data/lib/libraries/OneWire/readme.txt +13 -0
  66. data/lib/libraries/SWSerLCDpa/SWSerLCDpa.cpp +93 -47
  67. data/lib/libraries/SWSerLCDpa/SWSerLCDpa.h +16 -9
  68. data/lib/libraries/SWSerLCDsf/SWSerLCDsf.cpp +311 -0
  69. data/lib/libraries/SWSerLCDsf/SWSerLCDsf.h +67 -0
  70. data/lib/libraries/Servo/Servo.cpp +192 -0
  71. data/lib/libraries/Servo/Servo.h +61 -0
  72. data/lib/libraries/Stepper/Stepper.cpp +220 -0
  73. data/lib/libraries/Stepper/Stepper.h +86 -0
  74. data/lib/libraries/Stepper/keywords.txt +28 -0
  75. data/lib/libraries/Wire/Wire.cpp +262 -0
  76. data/lib/libraries/Wire/Wire.h +67 -0
  77. data/lib/libraries/Wire/keywords.txt +31 -0
  78. data/lib/libraries/Wire/twi.h +57 -0
  79. data/lib/libraries/Wire/utility/twi.c +449 -0
  80. data/lib/libraries/Wire/utility/twi.h +57 -0
  81. data/lib/plugins/bitwise_ops.rb +54 -0
  82. data/lib/plugins/blink.rb +25 -0
  83. data/lib/plugins/blink_m.rb +356 -0
  84. data/lib/plugins/debounce.rb +138 -0
  85. data/lib/plugins/debug_output_to_lcd.rb +71 -0
  86. data/lib/plugins/hysteresis.rb +52 -0
  87. data/lib/plugins/input_output_state.rb +84 -0
  88. data/lib/plugins/lcd_padding.rb +58 -0
  89. data/lib/plugins/mem_test.rb +37 -0
  90. data/lib/plugins/midi.rb +60 -0
  91. data/lib/plugins/parallax_ping.rb +50 -0
  92. data/lib/plugins/servo_pulse.rb +31 -0
  93. data/lib/plugins/servo_setup.rb +86 -0
  94. data/lib/plugins/smoother.rb +54 -0
  95. data/lib/plugins/spark_fun_serial_lcd.rb +100 -0
  96. data/lib/plugins/spectra_symbol.rb +79 -0
  97. data/lib/plugins/twitter_connect.rb +145 -0
  98. data/lib/rad/README.rdoc +5 -0
  99. data/lib/rad/arduino_plugin.rb +246 -0
  100. data/lib/rad/arduino_sketch.rb +351 -257
  101. data/lib/rad/generators/makefile/makefile.erb +1 -1
  102. data/lib/rad/generators/makefile/makefile.rb +9 -10
  103. data/lib/rad/hardware_library.rb +813 -0
  104. data/lib/rad/init.rb +3 -1
  105. data/lib/rad/rad_processor.rb +128 -0
  106. data/lib/rad/rad_rewriter.rb +94 -0
  107. data/lib/rad/rad_type_checker.rb +26 -0
  108. data/lib/rad/sim/arduino_sketch.rb +57 -0
  109. data/lib/rad/sketch_compiler.rb +47 -0
  110. data/lib/rad/tasks/build_and_make.rake +146 -24
  111. data/lib/rad/variable_processing.rb +153 -0
  112. data/lib/rad/version.rb +1 -1
  113. data/spec/examples/hello_world.rb +11 -0
  114. data/spec/examples/serial_motor.rb +12 -0
  115. data/spec/models/sketch_compiler_spec.rb +96 -0
  116. data/spec/sim/hello_world_spec.rb +42 -0
  117. data/test/test_array_processing.rb +179 -0
  118. data/test/test_plugin_loading.rb +151 -0
  119. data/test/test_translation_post_processing.rb +185 -0
  120. data/test/test_variable_processing.rb +238 -0
  121. data/website/index.html +22 -7
  122. data/website/stylesheets/screen.css +32 -1
  123. metadata +130 -13
@@ -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
- %w(generators/makefile/makefile.rb arduino_sketch.rb tasks/rad.rb).each do |path|
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 #{RAD_ROOT}/#{@sketch_name}; make upload}
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( @sketch_name )
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 #{RAD_ROOT}/#{@sketch_name}; make depend; make}
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
- @sketch_name = @file_names.first.split(".").first
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
- klass = @file_names.first.split(".").first.split("_").collect{|c| c.capitalize}.join("")
36
- eval ArduinoSketch.pre_process(File.read(@file_names.first))
37
- @loop = RubyToAnsiC.translate(constantize(klass), "loop")
38
- result = "#{@setup}\n#{@loop}\n"
39
- name = @file_names.first.split(".").first
40
- File.open("#{name}/#{name}.cpp", "w"){|f| f << result}
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
- klass = @file_names.first.split(".").first.split("_").collect{|c| c.capitalize}.join("")
47
- eval "class #{klass} < ArduinoSketch; end;"
117
+ eval "class #{@sketch.klass} < ArduinoSketch; end;"
48
118
 
49
- @@as = ArduinoSketch.new
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
- eval File.read(@file_names.first)
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
- mkdir_p "#{@file_names.first.split(".").first}"
181
+ @sketch.create_build_dir!
69
182
  end
70
183
 
71
184
  task :file_list do
72
- @file_names = []
73
- Dir.entries( File.expand_path(RAD_ROOT) ).each do |f|
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
- @file_names << f
197
+ @plugin_names << f
76
198
  end
77
199
  end
78
200
  end