madrona-rad 0.2.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (48) hide show
  1. data/History.txt +75 -0
  2. data/License.txt +282 -0
  3. data/Manifest.txt +47 -0
  4. data/README.rdoc +51 -0
  5. data/Rakefile +139 -0
  6. data/bin/rad +197 -0
  7. data/lib/libraries/SWSerLCDpa/SWSerLCDpa.cpp +267 -0
  8. data/lib/libraries/SWSerLCDpa/SWSerLCDpa.h +64 -0
  9. data/lib/libraries/SWSerLCDsf/SWSerLCDsf.cpp +254 -0
  10. data/lib/libraries/SWSerLCDsf/SWSerLCDsf.h +57 -0
  11. data/lib/libraries/Servo/Servo.cpp +152 -0
  12. data/lib/libraries/Servo/Servo.h +33 -0
  13. data/lib/libraries/Servo/keywords.txt +25 -0
  14. data/lib/plugins/debounce.rb +116 -0
  15. data/lib/plugins/debug_output_to_lcd.rb +71 -0
  16. data/lib/plugins/input_output_state.rb +84 -0
  17. data/lib/plugins/mem_test.rb +37 -0
  18. data/lib/plugins/servo_pulse.rb +31 -0
  19. data/lib/plugins/servo_setup.rb +86 -0
  20. data/lib/plugins/smoother.rb +54 -0
  21. data/lib/plugins/spark_fun_serial_lcd.rb +100 -0
  22. data/lib/rad/arduino_plugin.rb +202 -0
  23. data/lib/rad/arduino_sketch.rb +952 -0
  24. data/lib/rad/generators/makefile/makefile.erb +243 -0
  25. data/lib/rad/generators/makefile/makefile.rb +39 -0
  26. data/lib/rad/init.rb +12 -0
  27. data/lib/rad/rad_processor.rb +66 -0
  28. data/lib/rad/rad_rewriter.rb +94 -0
  29. data/lib/rad/tasks/build_and_make.rake +159 -0
  30. data/lib/rad/tasks/rad.rb +2 -0
  31. data/lib/rad/todo.txt +13 -0
  32. data/lib/rad/version.rb +9 -0
  33. data/lib/rad.rb +5 -0
  34. data/scripts/txt2html +67 -0
  35. data/setup.rb +1585 -0
  36. data/spec/models/arduino_sketch_spec.rb +82 -0
  37. data/spec/models/spec_helper.rb +2 -0
  38. data/spec/spec.opts +1 -0
  39. data/website/examples/assembler_test.rb.html +73 -0
  40. data/website/examples/gps_reader.rb.html +39 -0
  41. data/website/examples/hello_world.rb.html +38 -0
  42. data/website/examples/serial_motor.rb.html +41 -0
  43. data/website/index.html +177 -0
  44. data/website/index.txt +64 -0
  45. data/website/javascripts/rounded_corners_lite.inc.js +285 -0
  46. data/website/stylesheets/screen.css +169 -0
  47. data/website/template.rhtml +48 -0
  48. metadata +120 -0
@@ -0,0 +1,952 @@
1
+ # ArduinoSketch is the main access point for working with RAD. Sub-classes of ArduinoSketch have access to a wide array of convenient class methods (documented below) for doing the most common kinds of setup needed when programming the Arduino such as configuring input and output pins and establishing serial connections. Here is the canonical 'hello world' example of blinking a single LED in RAD:
2
+ #
3
+ # class HelloWorld < ArduinoSketch
4
+ # output_pin 13, :as => :led
5
+ #
6
+ # def loop
7
+ # blink 13, 500
8
+ # end
9
+ # end
10
+ #
11
+ # As you can see from this example, your ArduinoSketch sub-class can be dividied into two-parts: class methods for doing configuration and a loop method which will be run repeatedly at the Arduino's clock rate. Documentation for the various available class methods is below. The ArduinoSketch base class is designed to work with a series of rake tasks to automatically translate your loop method into C++ for compilation by the Arduino toolchain (see link://files/lib/rad/tasks/build_and_make_rake.html for details). See http://rad.rubyforge.org/examples for lots more examples of usage.
12
+ #
13
+ # ==Arduino built-in methods
14
+ # Thanks to this translation process you can take advantage of the complete Arduino software API (full docs here: http://www.arduino.cc/en/Reference/HomePage). What follows is the core of a RAD-Arduino dictionary for translating between RAD methods and the Arduino functionality they invoke, N.B. many Arduino methods have been left out (including the libraries for Time, Math, and Random Numbers, as the translation between them and their RAD counterparts should be relatively straightforward after perusing the examples here). For further details on each method, visit their Arduino documenation.
15
+ #
16
+ # <b>Digital I/O</b>
17
+ #
18
+ # digital_write(pin, value)
19
+ #
20
+ # Arduino method: digitalWrite(pin, value)
21
+ #
22
+ # Description: "Ouputs either HIGH or LOW at a specified pin."
23
+ #
24
+ # Documentation: http://www.arduino.cc/en/Reference/DigitalWrite
25
+ #
26
+ # digital_read(pin)
27
+ #
28
+ # Arduino method: digitalRead(pin)
29
+ #
30
+ # Description: "Reads the value from a specified pin, it will be either HIGH or LOW."
31
+ #
32
+ # Documentation: http://www.arduino.cc/en/Reference/DigitalRead
33
+ #
34
+ # <b>Analog I/O</b>
35
+ #
36
+ # analog_read(pin)
37
+ #
38
+ # Arduino method: analogRead(pin)
39
+ #
40
+ # Description: "Reads the value from the specified analog pin. The Arduino board contains a 6 channel
41
+ # (8 channels on the Mini), 10-bit analog to digital converter. This means that it will map input
42
+ # voltages between 0 and 5 volts into integer values between 0 and 1023. This yields a resolution
43
+ # between readings of: 5 volts / 1024 units or, .0049 volts (4.9 mV) per unit. It takes about 100
44
+ # us (0.0001 s) to read an analog input, so the maximum reading rate is about 10,000 times a second."
45
+ #
46
+ # Documentation: http://www.arduino.cc/en/Reference/AnalogRead
47
+ #
48
+ # analog_write(pin, value)
49
+ #
50
+ # Arduino method: analogWrite(pin, value)
51
+ #
52
+ # Description: "Writes an analog value (PWM wave) to a pin. On newer Arduino boards (including the Mini
53
+ # and BT) with the ATmega168 chip, this function works on pins 3, 5, 6, 9, 10, and 11. Older USB and
54
+ # serial Arduino boards with an ATmega8 only support analogWrite() on pins 9, 10, and 11. Can be used
55
+ # to light a LED at varying brightnesses or drive a motor at various speeds. After a call to analogWrite,
56
+ # the pin will generate a steady wave until the next call to analogWrite (or a call to digitalRead or
57
+ # digitalWrite on the same pin)."
58
+ #
59
+ # Documentation: http://www.arduino.cc/en/Reference/AnalogWrite
60
+ #
61
+ # <b>Serial Communication</b>
62
+ #
63
+ # serial_available()
64
+ #
65
+ # Arduino method: Serial.available()
66
+ #
67
+ # Description: "Get the number of bytes (characters) available for reading over the serial port.
68
+ # Returns the number of bytes available to read in the serial buffer, or 0 if none are
69
+ # available. If any data has come in, Serial.available() will be greater than 0. The serial buffer
70
+ # can hold up to 128 bytes."
71
+ #
72
+ # Documentation: http://www.arduino.cc/en/Serial/Available
73
+ #
74
+ # serial_read()
75
+ #
76
+ # Arduino method: Serial.read()
77
+ #
78
+ # Description: "Reads incoming serial data and returns the first byte of incoming serial data
79
+ # available (or -1 if no data is available)"
80
+ #
81
+ # Documentation: http://www.arduino.cc/en/Serial/Read
82
+ #
83
+ # serial_print(data)
84
+ #
85
+ # Arduino method: Serial.print(data)
86
+ #
87
+ # Description: "Prints data to the serial port."
88
+ #
89
+ # Documentation: http://www.arduino.cc/en/Serial/Print
90
+ #
91
+ # serial_println(data)
92
+ #
93
+ # Arduino method: Serial.println(data)
94
+ #
95
+ # Description: "Prints a data to the serial port, followed by a carriage return character
96
+ # (ASCII 13, or '\r') and a newline character (ASCII 10, or '\n'). This command takes the
97
+ # same forms as Serial.print():"
98
+ #
99
+ # Documentation: http://www.arduino.cc/en/Serial/Println
100
+ #
101
+ # serial_flush()
102
+ #
103
+ # Arduino method: Serial.flush()
104
+ #
105
+ # Description: "Flushes the buffer of incoming serial data. That is, any call to Serial.read()
106
+ # or Serial.available() will return only data received after the most recent call
107
+ # to Serial.flush()."
108
+ #
109
+ # Documentation: http://www.arduino.cc/en/Serial/Flush
110
+ #
111
+ # June 25, 2008
112
+ # Added a new external variable method which keeps track
113
+ # external_vars :sensor_position => "int, 0", :feedback => "int", :pulseTime => "unsigned long, 0"
114
+ #
115
+ # added ability to write additional methods besides loop in the sketch
116
+ # since there is quite a bit of work to do with the c translation, it is easy to write a method that
117
+ # won't compile or even translate into c, but for basics, it works.
118
+ # Note: stay basic and mindful that c is picky about variables and must make a decision
119
+ # which will not always be what you want
120
+ # also: for now, don't leave empty methods (something like foo = 1 cures this)
121
+ #
122
+ # Example:
123
+ #
124
+ # class HelloMethods < ArduinoSketch
125
+ # output_pin 13, :as => :led
126
+ #
127
+ # def loop
128
+ # blink_it
129
+ # end
130
+
131
+ # def blink_it
132
+ # blink 13, 500
133
+ # end
134
+ #
135
+ # end
136
+ #
137
+ #
138
+ # added pin methods for servos and latching which generate an array of structs to contain setup and status
139
+ # input_pin 12, :as => :back_off_button, :latch => :off
140
+ # input_pin 8, :as => :red_button, :latch => :off # adjust is optional with default set to 200
141
+ #
142
+ # added add_to_setup method that takes a string of c code and adds it to setup
143
+ # colons are options and will be added if not present
144
+ # no translation from ruby for now
145
+ #
146
+ # example:
147
+ #
148
+ # add_to_setup "call_my_new_method();", "call_another();"
149
+ #
150
+ # added some checking to c translation that (hopefully) makes it a bit more predictable
151
+ # most notably, we keep track of all external variables and let the translator know they exist
152
+ #
153
+ #
154
+
155
+ class ArduinoSketch
156
+ @@servo_inc = FALSE # flag to indicate this library instance has been created at least once - BBR
157
+ @@slcdpa_inc = FALSE # same
158
+ @@slcdsf_inc = FALSE # same
159
+ @@swser_inc = FALSE # same
160
+
161
+ def initialize #:nodoc:
162
+ @servo_settings = [] # need modular way to add this
163
+ @debounce_settings = [] # need modular way to add this
164
+ @servo_pins = []
165
+ @debounce_pins = []
166
+ @@array_vars = []
167
+ @@external_vars =[]
168
+ $external_var_identifiers = []
169
+ $sketch_methods = []
170
+
171
+ @declarations = []
172
+ @pin_modes = {:output => [], :input => []}
173
+ @pullups = []
174
+ @other_setup = [] # specifically, Serial.begin
175
+ @assembler_declarations = []
176
+ @accessors = []
177
+ @signatures = ["void blink();", "int main();", "void track_total_loop_time(void);", "unsigned long find_total_loop_time(void);"]
178
+
179
+ helper_methods = []
180
+ helper_methods << "void blink(int pin, int ms) {"
181
+ helper_methods << "\tdigitalWrite( pin, HIGH );"
182
+ helper_methods << "\tdelay( ms );"
183
+ helper_methods << "\tdigitalWrite( pin, LOW );"
184
+ helper_methods << "\tdelay( ms );"
185
+ helper_methods << "}"
186
+ helper_methods << "void track_total_loop_time(void)"
187
+ helper_methods << "{"
188
+ helper_methods << "\ttotal_loop_time = millis() - start_loop_time;"
189
+ helper_methods << "\tstart_loop_time = millis();"
190
+ helper_methods << "}"
191
+ helper_methods << "unsigned long find_total_loop_time(void)"
192
+ helper_methods << "{"
193
+ helper_methods << "\nreturn total_loop_time;"
194
+ helper_methods << "}"
195
+ @helper_methods = helper_methods.join( "\n" )
196
+
197
+ @declarations << "unsigned long start_loop_time = 0;"
198
+ @declarations << "unsigned long total_loop_time = 0;"
199
+ end
200
+
201
+ # Setup variables outside of the loop. Does some magic based on type of arguments. Subject to renaming. Use with caution.
202
+ def vars(opts={})
203
+ opts.each do |k,v|
204
+ if v.class == Symbol
205
+ @declarations << "#{v} _#{k};"
206
+ @accessors << <<-CODE
207
+ #{v} #{k}(){
208
+ \treturn _#{k};
209
+ }
210
+ CODE
211
+ else
212
+ type = case v
213
+ when Integer
214
+ "int"
215
+ when String
216
+ "char*"
217
+ when TrueClass
218
+ "bool"
219
+ when FalseClass
220
+ "bool"
221
+ end
222
+
223
+ @declarations << "#{type} _#{k} = #{v};"
224
+ @accessors << <<-CODE
225
+ #{type} #{k}(){
226
+ \treturn _#{k};
227
+ }
228
+ CODE
229
+ end
230
+ end
231
+ end
232
+
233
+
234
+ # a different way to setup external variables outside the loop
235
+ #
236
+ # in addition to being added to the c file as external variables:
237
+ # we fake ruby2c out by adding them to internal vars for ruby2c processing
238
+ # but we don't include then in the cpp loop
239
+ # then, we use the indentiers to remove any parens that ruby2c adds
240
+ # this resolves ruby2c's habit of converting variables to methods
241
+ # need tests and ability to add custom char length
242
+ def external_vars(opts={})
243
+ if opts
244
+ opts.each do |k,v|
245
+ if v.include? ","
246
+ if v.split(",")[0] == "char"
247
+ ## default is 40 characters
248
+ if v.split(",")[2]
249
+ @@external_vars << "#{v.split(",")[0]}* #{k}[#{v.split(",")[2].lstrip}];"
250
+ else
251
+ @@external_vars << "#{v.split(",")[0]} #{k}[40] = \"#{v.split(",")[1].lstrip}\";"
252
+ end
253
+ else
254
+ @@external_vars << "#{v.split(",")[0]} #{k} =#{v.split(",")[1]};"
255
+ end
256
+ else
257
+ if v.split(",")[0] == "char"
258
+ @@external_vars << "#{v.split(",")[0]} #{k}[40];"
259
+ else
260
+
261
+ @@external_vars << "#{v.split(",")[0]} #{k};"
262
+ end
263
+ end
264
+ # check chars work here
265
+ $external_var_identifiers << k
266
+ end
267
+ end
268
+ end
269
+
270
+
271
+ def add_to_setup(*args)
272
+ if args
273
+ args.each do |arg|
274
+ $add_to_setup << arg
275
+ end
276
+ end
277
+ end
278
+
279
+ # work in progress
280
+ def external_arrays(*args)
281
+ if args
282
+ args.each do |arg|
283
+ @@array_vars << arg
284
+ end
285
+ end
286
+
287
+ end
288
+
289
+ # Confiugre a single pin for output and setup a method to refer to that pin, i.e.:
290
+ #
291
+ # output_pin 7, :as => :led
292
+ #
293
+ # would configure pin 7 as an output and let you refer to it from the then on by calling
294
+ # the `led` method in your loop like so:
295
+ #
296
+ # def loop
297
+ # digital_write led, ON
298
+ # end
299
+ #
300
+ def output_pin(num, opts={})
301
+ raise ArgumentError, "can only define pin from Fixnum, got #{num.class}" unless num.is_a?(Fixnum)
302
+ @pin_modes[:output] << num
303
+ if opts[:as]
304
+ if opts[:device]
305
+ case opts[:device]
306
+ when :servo
307
+ new_servo_setup(num, opts)
308
+ return # don't use declarations, accessor, signatures below
309
+ when :orig_servo
310
+ orig_servo_setup(num, opts)
311
+ when :lcd || :LCD
312
+ lcd_setup(num, opts)
313
+ return
314
+ when :pa_lcd || :pa_LCD
315
+ pa_lcd_setup(num, opts)
316
+ return
317
+ when :sf_lcd || :sf_LCD
318
+ sf_lcd_setup(num, opts)
319
+ return
320
+ end
321
+ end
322
+
323
+ # remove the next 14 lines as soon as documentation on new :device => :servo option is out
324
+
325
+ if opts[:min] && opts[:max]
326
+ ArduinoPlugin.add_servo_struct
327
+ @servo_pins << num
328
+ refresh = opts[:refresh] ? opts[:refresh] : 200
329
+ @servo_settings << "serv[#{num}].pin = #{num}, serv[#{num}].pulseWidth = 0, serv[#{num}].lastPulse = 0, serv[#{num}].startPulse = 0, serv[#{num}].refreshTime = #{refresh}, serv[#{num}].min = #{opts[:min]}, serv[#{num}].max = #{opts[:max]} "
330
+ unless opts[:device]
331
+ puts "#{"*"*80} \n using :max and :min to instantiate a servo is deprecated\n use :device => :servo instead\n#{"*"*80}"
332
+ end
333
+ else
334
+ raise ArgumentError, "two are required for each servo: min & max" if opts[:min] || opts[:max]
335
+ raise ArgumentError, "refresh is an optional servo parameter, don't forget min & max" if opts[:refresh]
336
+ end
337
+
338
+ # add state variables for outputs with :state => :on or :off -- useful for toggling a light with output_toggle -- need to make this more modular
339
+ if opts[:state]
340
+ # add debounce settings to dbce struct array
341
+ ArduinoPlugin.add_debounce_struct
342
+ @debounce_pins << num
343
+ state = opts[:latch] == :on ? 1 : 0
344
+ prev = opts[:latch] == :on ? 0 : 1
345
+ adjust = opts[:adjust] ? opts[:adjust] : 200
346
+ @debounce_settings << "dbce[#{num}].state = #{state}, dbce[#{num}].read = 0, dbce[#{num}].prev = #{prev}, dbce[#{num}].time = 0, dbce[#{num}].adjust = #{adjust}"
347
+ end
348
+
349
+ @declarations << "int _#{opts[ :as ]} = #{num};"
350
+
351
+ accessor = []
352
+ accessor << "int #{opts[ :as ]}() {"
353
+ accessor << "\treturn _#{opts[ :as ]};"
354
+ accessor << "}"
355
+ @accessors << accessor.join( "\n" )
356
+
357
+ @signatures << "int #{opts[ :as ]}();"
358
+ end
359
+ end
360
+
361
+ # Like ArduinoSketch#output_pin but configure more than one output pin simultaneously. Takes an array of pin numbers.
362
+ def output_pins(nums)
363
+ ar = Array(nums)
364
+ ar.each {|n| output_pin(n)}
365
+ end
366
+
367
+ ### tuck the following five methods into private once they are solid
368
+
369
+ def orig_servo_setup(num, opts)
370
+ ArduinoPlugin.add_servo_struct
371
+ @servo_pins << num
372
+ refresh = opts[:refresh] ? opts[:refresh] : 200
373
+ min = opts[:min] ? opts[:min] : 700
374
+ max = opts[:min] ? opts[:max] : 2200
375
+ @servo_settings << "serv[#{num}].pin = #{num}, serv[#{num}].pulseWidth = 0, serv[#{num}].lastPulse = 0, serv[#{num}].startPulse = 0, serv[#{num}].refreshTime = #{refresh}, serv[#{num}].min = #{min}, serv[#{num}].max = #{max} "
376
+ end
377
+
378
+ # use the servo library
379
+ def new_servo_setup(num, opts)
380
+ opts[:minp] = opts[:min] ? opts[:min] : 544
381
+ opts[:maxp] = opts[:max] ? opts[:max] : 2400
382
+ servo(num, opts)
383
+ # move this to better place ...
384
+ # should probably go along with servo code into plugin
385
+ @declarations << "void servo_refresh(void);"
386
+ helper_methods = []
387
+ helper_methods << "void servo_refresh(void)"
388
+ helper_methods << "{"
389
+ helper_methods << "\tServo::refresh();"
390
+ helper_methods << "}"
391
+ @helper_methods += "\n#{helper_methods.join("\n")}"
392
+ end
393
+
394
+ ## this won't work at all... no pins
395
+ # need help
396
+ def lcd_setup(num, opts)
397
+ # move to plugin and load plugin
398
+ # what's the default rate?
399
+ opts[:rate] ||= 9600
400
+ rate = opts[:rate] ? opts[:rate] : 9600
401
+ swser_LCD(num, opts)
402
+ end
403
+
404
+ # use the pa lcd library
405
+ def pa_lcd_setup(num, opts)
406
+ # move to plugin and load plugin
407
+ # what's the default?
408
+ opts[:rate] ||= 9600
409
+ rate = opts[:rate] ? opts[:rate] : 9600
410
+ swser_LCDpa(num, opts)
411
+ end
412
+
413
+ # use the sf (sparkfun) library
414
+ def sf_lcd_setup(num, opts)
415
+ # move to plugin and load plugin
416
+ opts[:rate] ||= 9600
417
+ rate = opts[:rate] ? opts[:rate] : 9600
418
+ swser_LCDsf(num, opts)
419
+ end
420
+
421
+
422
+ # Confiugre a single pin for input and setup a method to refer to that pin, i.e.:
423
+ #
424
+ # input_pin 3, :as => :button
425
+ #
426
+ # would configure pin 3 as an input and let you refer to it from the then on by calling
427
+ # the `button` method in your loop like so:
428
+ #
429
+ # def loop
430
+ # digital_write led if digital_read button
431
+ # end
432
+ #
433
+ def input_pin(num, opts={})
434
+ raise ArgumentError, "can only define pin from Fixnum, got #{num.class}" unless num.is_a?(Fixnum)
435
+ @pin_modes[:input] << num
436
+ if opts[:as]
437
+ # transitioning to :device => :button syntax
438
+ if opts[:latch] || opts[:device] == :button
439
+ if opts[:device] == :button
440
+ opts[:latch] ||= :on
441
+ end
442
+ # add debounce settings to dbce struct array
443
+ ArduinoPlugin.add_debounce_struct
444
+ @debounce_pins << num
445
+ state = opts[:latch] == :on ? 1 : 0
446
+ prev = opts[:latch] == :on ? 0 : 1
447
+ adjust = opts[:adjust] ? opts[:adjust] : 200
448
+ @debounce_settings << "dbce[#{num}].state = #{state}, dbce[#{num}].read = 0, dbce[#{num}].prev = #{prev}, dbce[#{num}].time = 0, dbce[#{num}].adjust = #{adjust}"
449
+ end
450
+ @declarations << "int _#{opts[ :as ]} = #{num};"
451
+
452
+ accessor = []
453
+ accessor << "int #{opts[ :as ]}() {"
454
+ accessor << "\treturn _#{opts[ :as ]};"
455
+ accessor << "}"
456
+ @accessors << accessor.join( "\n" )
457
+
458
+ @signatures << "int #{opts[ :as ]}();"
459
+ end
460
+ @pullups << num if opts[:as]
461
+ end
462
+
463
+ # Like ArduinoSketch#input_pin but configure more than one input pin simultaneously. Takes an array of pin numbers.
464
+ def input_pins(nums)
465
+ ar = Array(nums)
466
+ ar.each {|n| input_pin(n)}
467
+ end
468
+
469
+ def add(st) #:nodoc:
470
+ @helper_methods << "\n#{st}\n"
471
+ end
472
+
473
+ # Configure Arduino for serial communication. Optionally, set the baud rate:
474
+ #
475
+ # serial_begin :rate => 2400
476
+ #
477
+ # default is 9600. See http://www.arduino.cc/en/Serial/Begin for more details.
478
+ #
479
+ def serial_begin(opts={})
480
+ rate = opts[:rate] ? opts[:rate] : 9600
481
+ @other_setup << "Serial.begin(#{rate});"
482
+ end
483
+
484
+ # Treat a pair of digital I/O pins as a serial line. See: http://www.arduino.cc/en/Tutorial/SoftwareSerial
485
+ def software_serial(rx, tx, opts={})
486
+ raise ArgumentError, "can only define rx from Fixnum, got #{rx.class}" unless rx.is_a?(Fixnum)
487
+ raise ArgumentError, "can only define tx from Fixnum, got #{tx.class}" unless tx.is_a?(Fixnum)
488
+
489
+ output_pin(tx)
490
+
491
+ rate = opts[:rate] ? opts[:rate] : 9600
492
+ if opts[:as]
493
+ @declarations << "SoftwareSerial _#{opts[ :as ]} = SoftwareSerial(#{rx}, #{tx});"
494
+ accessor = []
495
+ accessor << "SoftwareSerial& #{opts[ :as ]}() {"
496
+ accessor << "\treturn _#{opts[ :as ]};"
497
+ accessor << "}"
498
+ if (@@swser_inc == FALSE) # on second instance this stuff can't be repeated
499
+ @@swser_inc = TRUE
500
+ accessor << "int read(SoftwareSerial& s) {"
501
+ accessor << "\treturn s.read();"
502
+ accessor << "}"
503
+ accessor << "void println( SoftwareSerial& s, char* str ) {"
504
+ accessor << "\treturn s.println( str );"
505
+ accessor << "}"
506
+ accessor << "void print( SoftwareSerial& s, char* str ) {"
507
+ accessor << "\treturn s.print( str );"
508
+ accessor << "}"
509
+ accessor << "void println( SoftwareSerial& s, int i ) {"
510
+ accessor << "\treturn s.println( i );"
511
+ accessor << "}"
512
+ accessor << "void print( SoftwareSerial& s, int i ) {"
513
+ accessor << "\treturn s.print( i );"
514
+ accessor << "}"
515
+ end
516
+ @accessors << accessor.join( "\n" )
517
+
518
+ @signatures << "SoftwareSerial& #{opts[ :as ]}();"
519
+
520
+ @other_setup << "_#{opts[ :as ]}.begin(#{rate});"
521
+ end
522
+ end
523
+
524
+ def swser_LCDpa(tx, opts={})
525
+ raise ArgumentError, "can only define tx from Fixnum, got #{tx.class}" unless tx.is_a?(Fixnum)
526
+ output_pin(tx)
527
+
528
+
529
+ rate = opts[:rate] ? opts[:rate] : 9600
530
+ geometry = opts[:geometry] ? opts[:geometry] : 0
531
+ if opts[:as]
532
+ @declarations << "SWSerLCDpa _#{opts[ :as ]} = SWSerLCDpa(#{tx}, #{geometry});"
533
+ accessor = []
534
+ accessor << "SWSerLCDpa& #{opts[ :as ]}() {"
535
+ accessor << "\treturn _#{opts[ :as ]};"
536
+ accessor << "}"
537
+ if (@@slcdpa_inc == FALSE) # on second instance this stuff can't be repeated - BBR
538
+ @@slcdpa_inc = TRUE
539
+ accessor << "void print( SWSerLCDpa& s, uint8_t b ) {"
540
+ accessor << "\treturn s.print( b );"
541
+ accessor << "}"
542
+ accessor << "void print( SWSerLCDpa& s, const char *str ) {"
543
+ accessor << "\treturn s.print( str );"
544
+ accessor << "}"
545
+ accessor << "void print( SWSerLCDpa& s, char c ) {"
546
+ accessor << "\treturn s.print( c );"
547
+ accessor << "}"
548
+ accessor << "void print( SWSerLCDpa& s, int i ) {"
549
+ accessor << "\treturn s.print( i );"
550
+ accessor << "}"
551
+ accessor << "void print( SWSerLCDpa& s, unsigned int i ) {"
552
+ accessor << "\treturn s.print( i );"
553
+ accessor << "}"
554
+ accessor << "void print( SWSerLCDpa& s, long i ) {"
555
+ accessor << "\treturn s.print( i );"
556
+ accessor << "}"
557
+ accessor << "void print( SWSerLCDpa& s, unsigned long i ) {"
558
+ accessor << "\treturn s.print( i );"
559
+ accessor << "}"
560
+ accessor << "void print( SWSerLCDpa& s, long i, int b ) {"
561
+ accessor << "\treturn s.print( i, b );"
562
+ accessor << "}"
563
+ accessor << "void println( SWSerLCDpa& s, char* str ) {"
564
+ accessor << "\treturn s.println( str );"
565
+ accessor << "}"
566
+ accessor << "void print( SWSerLCDpa& s, char* str ) {"
567
+ accessor << "\treturn s.print( str );"
568
+ accessor << "}"
569
+ accessor << "void println(SWSerLCDpa& s) {"
570
+ accessor << "\treturn s.println();"
571
+ accessor << "}"
572
+ accessor << "void clearscr(SWSerLCDpa& s) {"
573
+ accessor << "\treturn s.clearscr();"
574
+ accessor << "}"
575
+ accessor << "void home(SWSerLCDpa& s) {"
576
+ accessor << "\treturn s.home();"
577
+ accessor << "}"
578
+ accessor << "void setgeo( SWSerLCDpa& s, int i ) {"
579
+ accessor << "\treturn s.setgeo( i );"
580
+ accessor << "}"
581
+ accessor << "void setintensity( SWSerLCDpa& s, int i ) {"
582
+ accessor << "\treturn s.setintensity( i );"
583
+ accessor << "}"
584
+ accessor << "void intoBignum(SWSerLCDpa& s) {"
585
+ accessor << "\treturn s.intoBignum();"
586
+ accessor << "}"
587
+ accessor << "void outofBignum(SWSerLCDpa& s) {"
588
+ accessor << "\treturn s.outofBignum();"
589
+ accessor << "}"
590
+ accessor << "void setxy( SWSerLCDpa& s, int x, int y) {"
591
+ accessor << "\treturn s.setxy( x, y );"
592
+ accessor << "}"
593
+ accessor << "void println( SWSerLCDpa& s, char c ) {"
594
+ accessor << "\treturn s.println( c );"
595
+ accessor << "}"
596
+ accessor << "void println( SWSerLCDpa& s, const char c[] ) {"
597
+ accessor << "\treturn s.println( c );"
598
+ accessor << "}"
599
+ accessor << "void println( SWSerLCDpa& s, uint8_t b ) {"
600
+ accessor << "\treturn s.println( b );"
601
+ accessor << "}"
602
+ accessor << "void println( SWSerLCDpa& s, int i ) {"
603
+ accessor << "\treturn s.println( i );"
604
+ accessor << "}"
605
+ accessor << "void println( SWSerLCDpa& s, long i ) {"
606
+ accessor << "\treturn s.println( i );"
607
+ accessor << "}"
608
+ accessor << "void println( SWSerLCDpa& s, unsigned long i ) {"
609
+ accessor << "\treturn s.println( i );"
610
+ accessor << "}"
611
+ accessor << "void println( SWSerLCDpa& s, long i, int b ) {"
612
+ accessor << "\treturn s.println( i, b );"
613
+ accessor << "}"
614
+ end
615
+ @accessors << accessor.join( "\n" )
616
+
617
+ @signatures << "SWSerLCDpa& #{opts[ :as ]}();"
618
+
619
+ @other_setup << "_#{opts[ :as ]}.begin(#{rate});"
620
+ end
621
+ end
622
+
623
+
624
+
625
+ def swser_LCDsf(tx, opts={})
626
+ raise ArgumentError, "can only define tx from Fixnum, got #{tx.class}" unless tx.is_a?(Fixnum)
627
+ output_pin(tx)
628
+
629
+
630
+ rate = opts[:rate] ? opts[:rate] : 9600
631
+ geometry = opts[:geometry] ? opts[:geometry] : 0
632
+ if opts[:as]
633
+ @declarations << "SWSerLCDsf _#{opts[ :as ]} = SWSerLCDsf(#{tx}, #{geometry});"
634
+ accessor = []
635
+ accessor << "SWSerLCDsf& #{opts[ :as ]}() {"
636
+ accessor << "\treturn _#{opts[ :as ]};"
637
+ accessor << "}"
638
+ if (@@slcdsf_inc == FALSE) # on second instance this stuff can't be repeated - BBR
639
+ @@slcdsf_inc = TRUE
640
+ accessor << "void print( SWSerLCDsf& s, uint8_t b ) {"
641
+ accessor << "\treturn s.print( b );"
642
+ accessor << "}"
643
+ accessor << "void print( SWSerLCDsf& s, const char *str ) {"
644
+ accessor << "\treturn s.print( str );"
645
+ accessor << "}"
646
+ accessor << "void print( SWSerLCDsf& s, char c ) {"
647
+ accessor << "\treturn s.print( c );"
648
+ accessor << "}"
649
+ accessor << "void print( SWSerLCDsf& s, int i ) {"
650
+ accessor << "\treturn s.print( i );"
651
+ accessor << "}"
652
+ accessor << "void print( SWSerLCDsf& s, unsigned int i ) {"
653
+ accessor << "\treturn s.print( i );"
654
+ accessor << "}"
655
+ accessor << "void print( SWSerLCDsf& s, long i ) {"
656
+ accessor << "\treturn s.print( i );"
657
+ accessor << "}"
658
+ accessor << "void print( SWSerLCDsf& s, unsigned long i ) {"
659
+ accessor << "\treturn s.print( i );"
660
+ accessor << "}"
661
+ accessor << "void print( SWSerLCDsf& s, long i, int b ) {"
662
+ accessor << "\treturn s.print( i, b );"
663
+ accessor << "}"
664
+ accessor << "void print( SWSerLCDsf& s, char* str ) {"
665
+ accessor << "\treturn s.print( str );"
666
+ accessor << "}"
667
+ accessor << "void clearscr(SWSerLCDsf& s) {"
668
+ accessor << "\treturn s.clearscr();"
669
+ accessor << "}"
670
+ accessor << "void home(SWSerLCDsf& s) {"
671
+ accessor << "\treturn s.home();"
672
+ accessor << "}"
673
+ accessor << "void setgeo( SWSerLCDsf& s, int i ) {"
674
+ accessor << "\treturn s.setgeo( i );"
675
+ accessor << "}"
676
+ accessor << "void setintensity( SWSerLCDsf& s, int i ) {"
677
+ accessor << "\treturn s.setintensity( i );"
678
+ accessor << "}"
679
+ accessor << "void setxy( SWSerLCDsf& s, int x, int y) {"
680
+ accessor << "\treturn s.setxy( x, y );"
681
+ accessor << "}"
682
+ accessor << "void setcmd( SWSerLCDsf& s, uint8_t a, uint8_t b) {"
683
+ accessor << "\treturn s.setcmd( a, b );"
684
+ accessor << "}"
685
+ end
686
+ @accessors << accessor.join( "\n" )
687
+
688
+ @signatures << "SWSerLCDsf& #{opts[ :as ]}();"
689
+
690
+ @other_setup << "_#{opts[ :as ]}.begin(#{rate});"
691
+ end
692
+ end
693
+
694
+
695
+
696
+
697
+
698
+ def servo(spin, opts={}) # servo motor routines # how about pin instead of spin
699
+ raise ArgumentError, "can only define spin from Fixnum, got #{spin.class}" unless spin.is_a?(Fixnum)
700
+
701
+ minp = opts[:minp] ? opts[:minp] : 544
702
+ maxp = opts[:maxp] ? opts[:maxp] : 2400
703
+
704
+ if opts[:as]
705
+ @declarations << "Servo _#{opts[ :as ]} = Servo();"
706
+ accessor = []
707
+ accessor << "Servo& #{opts[ :as ]}() {"
708
+ accessor << "\treturn _#{opts[ :as ]};"
709
+ accessor << "}"
710
+
711
+ if (@@servo_inc == FALSE) # on second instance this stuff can't be repeated - BBR
712
+ @@servo_inc = TRUE
713
+ accessor << "void detach( Servo& s ) {"
714
+ accessor << "\treturn s.detach();"
715
+ accessor << "}"
716
+ accessor << "void position( Servo& s, int b ) {"
717
+ accessor << "\treturn s.position( b );"
718
+ accessor << "}"
719
+ accessor << "void speed( Servo& s, int b ) {"
720
+ accessor << "\treturn s.speed( b );"
721
+ accessor << "}"
722
+ accessor << "uint8_t read( Servo& s ) {"
723
+ accessor << "\treturn s.read();"
724
+ accessor << "}"
725
+ accessor << "uint8_t attached( Servo& s ) {"
726
+ accessor << "\treturn s.attached();"
727
+ accessor << "}"
728
+ accessor << "static void refresh( Servo& s ) {"
729
+ accessor << "\treturn s.refresh();"
730
+ accessor << "}"
731
+ end
732
+
733
+ @accessors << accessor.join( "\n" )
734
+
735
+ @signatures << "Servo& #{opts[ :as ]}();"
736
+
737
+ @other_setup << "_#{opts[ :as ]}.attach(#{spin}, #{minp}, #{maxp});"
738
+
739
+ end
740
+ end
741
+
742
+
743
+
744
+ def compose_setup #:nodoc: also composes headers and signatures
745
+ result = []
746
+
747
+ result << comment_box( "Auto-generated by RAD" )
748
+
749
+ result << "#include <WProgram.h>\n"
750
+ result << "#include <SoftwareSerial.h>\n"
751
+ result << "#include <SWSerLCDpa.h>"
752
+ result << "#include <SWSerLCDsf.h>"
753
+ result << "#include <Servo.h>"
754
+
755
+ result << comment_box( 'plugin directives' )
756
+ $plugin_directives.each {|dir| result << dir } unless $plugin_directives.nil? || $plugin_directives.empty?
757
+
758
+ result << comment_box( 'method signatures' )
759
+ result << "void loop();"
760
+ result << "void setup();"
761
+ result << "// sketch signatures"
762
+ @signatures.each {|sig| result << sig}
763
+ result << "// sketch signatures"
764
+ result << "// plugin signatures"
765
+ $plugin_signatures.each {|sig| result << sig } unless $plugin_signatures.nil? || $plugin_signatures.empty?
766
+ result << "\n" + comment_box( "plugin external variables" )
767
+ $plugin_external_variables.each { |meth| result << meth } unless $plugin_external_variables.nil? || $plugin_external_variables.empty?
768
+
769
+ result << "\n" + comment_box( "plugin structs" )
770
+ $plugin_structs.each { |k,v| result << v } unless $plugin_structs.nil? || $plugin_structs.empty?
771
+
772
+ result << "\n" + comment_box( "sketch external variables" )
773
+
774
+ @@external_vars.each {|cv| result << "#{cv}"}
775
+ result << ""
776
+ result << "// servo_settings array"
777
+
778
+ array_size = @servo_settings.empty? ? 1 : @servo_pins.max + 1 # conserve space if no variables needed
779
+ result << "struct servo serv[#{array_size}] = { #{@servo_settings.join(", ")} };" if $plugin_structs[:servo]
780
+ result << ""
781
+
782
+ result << "// debounce array"
783
+ array_size = @debounce_settings.empty? ? 1 : @debounce_pins.max + 1 # conserve space if no variables needed
784
+ result << "struct debounce dbce[#{array_size}] = { #{@debounce_settings.join(", ")} };" if $plugin_structs[:debounce]
785
+ result << ""
786
+
787
+ @@array_vars.each { |var| result << var } if @@array_vars
788
+
789
+ result << "\n" + comment_box( "variable and accessors" )
790
+ @declarations.each {|dec| result << dec}
791
+ result << ""
792
+ @accessors.each {|ac| result << ac}
793
+
794
+ result << "\n" + comment_box( "assembler declarations" )
795
+ unless @assembler_declarations.empty?
796
+ result << <<-CODE
797
+ extern "C" {
798
+ #{@assembler_declarations.join("\n")}
799
+ }
800
+ CODE
801
+ end
802
+
803
+ result << "\n" + comment_box( "setup" )
804
+ result << "void setup() {"
805
+ result << "\t// pin modes"
806
+
807
+ @pin_modes.each do |k,v|
808
+ v.each do |value|
809
+ result << "\tpinMode(#{value}, #{k.to_s.upcase});"
810
+ end
811
+ end
812
+
813
+ @pullups.each do |pin|
814
+ result << "\tdigitalWrite( #{pin}, HIGH ); // enable pull-up resistor for input"
815
+ end
816
+
817
+ unless $add_to_setup.nil? || $add_to_setup.empty?
818
+ result << "\t// setup from plugins via add_to_setup method"
819
+ $add_to_setup.each {|item| result << "\t#{item}"}
820
+ end
821
+
822
+ unless @other_setup.empty?
823
+ result << "\t// other setup"
824
+ result << @other_setup.join( "\n" )
825
+ end
826
+
827
+ result << "}\n"
828
+
829
+ result << comment_box( "helper methods" )
830
+ result << "\n// RAD built-in helpers"
831
+ result << @helper_methods.lstrip
832
+
833
+ result << "\n" + comment_box( "plugin methods" )
834
+ # need to add plugin name to this...
835
+ $plugin_methods.each { |meth| result << "#{meth[0][0]}\n" } unless $plugin_methods.nil? || $plugin_methods.empty?
836
+
837
+ result << "\n// serial helpers"
838
+ result << serial_boilerplate.lstrip
839
+
840
+ result << "\n" + comment_box( "main() function" )
841
+ result << "int main() {"
842
+ result << "\tinit();"
843
+ result << "\tsetup();"
844
+ result << "\tfor( ;; ) { loop(); }"
845
+ result << "\treturn 0;"
846
+ result << "}"
847
+
848
+ result << "\n" + comment_box( "loop! Autogenerated by RubyToC, sorry it's ugly." )
849
+
850
+ return result.join( "\n" )
851
+ end
852
+
853
+
854
+ # Write inline assembler code. 'Name' is a symbol representing the name of the function to be defined in the assembly code; 'signature' is the function signature for the function being defined; and 'code' is the assembly code itself (both of these last two arguments are strings). See an example here: http://rad.rubyforge.org/examples/assembler_test.html
855
+ def assembler(name, signature, code)
856
+ @assembler_declarations << signature
857
+ assembler_code = <<-CODE
858
+ .file "#{name}.S"
859
+ .arch #{Makefile.hardware_params['mcu']}
860
+ .global __do_copy_data
861
+ .global __do_clear_bss
862
+ .text
863
+ .global #{name}
864
+ .type #{name}, @function
865
+ #{code}
866
+ CODE
867
+
868
+ File.open(File.expand_path("#{RAD_ROOT}") + "/#{PROJECT_DIR_NAME}/#{name}.S", "w"){|f| f << assembler_code}
869
+ end
870
+
871
+ def self.pre_process(sketch_string) #:nodoc:
872
+ result = sketch_string
873
+ # add external vars to each method (needed for better translation, will be removed in make:upload)
874
+ result.gsub!(/(^\s*def\s.\w*(\(.*\))?)/, '\1' + " \n #{@@external_vars.join(" \n ")}" )
875
+ # gather method names
876
+ sketch_methods = result.scan(/^\s*def\s.\w*/)
877
+ sketch_methods.each {|m| $sketch_methods << m.gsub(/\s*def\s*/, "") }
878
+
879
+ result.gsub!("HIGH", "1")
880
+ result.gsub!("LOW", "0")
881
+ result.gsub!("ON", "1")
882
+ result.gsub!("OFF", "0")
883
+ result
884
+ end
885
+
886
+ private
887
+
888
+ def serial_boilerplate #:nodoc:
889
+ out = []
890
+ out << "int serial_available() {"
891
+ out << "\treturn (Serial.available() > 0);"
892
+ out << "}"
893
+
894
+ out << "char serial_read() {"
895
+ out << "\treturn (char) Serial.read();"
896
+ out << "}"
897
+
898
+ out << "void serial_flush() {"
899
+ out << "\treturn Serial.flush();"
900
+ out << "}"
901
+
902
+ out << "void serial_print( char str ) {"
903
+ out << "\treturn Serial.print( str );"
904
+ out << "}"
905
+
906
+ out << "void serial_print( char* str ) {"
907
+ out << "\treturn Serial.print( str );"
908
+ out << "}"
909
+
910
+ out << "void serial_print( int i ) {"
911
+ out << "\treturn Serial.print( i );"
912
+ out << "}"
913
+
914
+ out << "void serial_print( long i ) {"
915
+ out << "\treturn Serial.print( i );"
916
+ out << "}"
917
+
918
+ out << "void serial_println( char* str ) {"
919
+ out << "\treturn Serial.println( str );"
920
+ out << "}"
921
+
922
+ out << "void serial_println( char str ) {"
923
+ out << "\treturn Serial.println( str );"
924
+ out << "}"
925
+
926
+ out << "void serial_println( int i ) {"
927
+ out << "\treturn Serial.println( i );"
928
+ out << "}"
929
+
930
+ out << "void serial_println( long i ) {"
931
+ out << "\treturn Serial.println( i );"
932
+ out << "}"
933
+
934
+ ## added to support millis
935
+ out << "void serial_print( unsigned long i ) {"
936
+ out << "\treturn Serial.print( i );"
937
+ out << "}"
938
+
939
+ return out.join( "\n" )
940
+ end
941
+
942
+ def comment_box( content ) #:nodoc:
943
+ out = []
944
+ out << "/" * 74
945
+ out << "// " + content
946
+ out << "/" * 74
947
+
948
+ return out.join( "\n" )
949
+ end
950
+
951
+
952
+ end