rad 0.2.2 → 0.2.9

Sign up to get free protection for your applications and to get access to all the features.
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