Class: ArduinoSketch
- Inherits:
-
Object
- Object
- ArduinoSketch
- Includes:
- ExternalVariableProcessing
- Defined in:
- lib/rad/arduino_sketch.rb,
lib/rad/sim/arduino_sketch.rb
Overview
def blink_it
blink 13, 500
end
end
added pin methods for servos and latching which generate an array of structs to contain setup and status
input_pin 12, :as => :back_off_button, :latch => :off
input_pin 8, :as => :red_button, :latch => :off # adjust is optional with default set to 200
added add_to_setup method that takes a string of c code and adds it to setup
colons are options and will be added if not present
no translation from ruby for now
example:
add_to_setup "call_my_new_method();", "call_another();"
added some checking to c translation that (hopefully) makes it a bit more predictable
most notably, we keep track of all external variables and let the translator know they exist
Direct Known Subclasses
AddHysteresis, BasicBlink, BlinkMAddressAssignment, BlinkMHello, BlinkMMulti, BlinkWithSerial, ConfigurePaLcdBoot, DebounceMethods, ExternalVariableFu, ExternalVariables, FirstSound, FrequencyGenerator, HardwareLibrary, HelloArray, HelloArray2, HelloArrayEeprom, HelloClock, HelloEeprom, HelloEepromLcdpa, HelloFormatPrint, HelloLcdCharset, HelloMaxbotix, HelloPaLcd, HelloServos, HelloSpectraSound, HelloWorld, HelloXbee, HysteresisDuel, I2cWithClockChip, MidiBeatBox, MidiScales, MotorKnob, ServoButtons, ServoCalibrateContinuous, ServoThrottle, SoftwareSerial, SparkfunLcd, SpectraSoftPot, TimesMethod, Toggle, Twitter, TwoWire
Constant Summary collapse
- @@twowire_inc =
find another way to do this
FALSE
- @@hwserial_inc =
FALSE
Instance Attribute Summary collapse
-
#pins ⇒ Object
Returns the value of attribute pins.
Class Method Summary collapse
- .add_to_setup(meth) ⇒ Object
- .output_pin(num, opts) ⇒ Object
- .post_process_ruby_to_c_methods(e) ⇒ Object
-
.pre_process(sketch_string) ⇒ Object
:nodoc:.
Instance Method Summary collapse
-
#add(st) ⇒ Object
:nodoc:.
- #array(arg) ⇒ Object
-
#assembler(name, signature, code) ⇒ Object
Write inline assembler code.
-
#comment_box(content) ⇒ Object
:nodoc:.
-
#compose_setup ⇒ Object
:nodoc: also composes headers and signatures.
-
#define(arg) ⇒ Object
define “DS1307_SEC 0” result: #define DS1307_SEC 0 note we send the constant identifiers and type to our rad_type_checker however, it only knows about long, float, str.…
- #delay(millis) ⇒ Object
- #digitalWrite(pin, value) ⇒ Object
- #formatted_print(opts = {}) ⇒ Object
-
#initialize ⇒ ArduinoSketch
constructor
:nodoc:.
-
#input_pin(num, opts = {}) ⇒ Object
Configure a single pin for input and setup a method to refer to that pin, i.e.:.
-
#input_pins(nums) ⇒ Object
Like ArduinoSketch#input_pin but configure more than one input pin simultaneously.
- #loop ⇒ Object
-
#output_pin(num, opts = {}) ⇒ Object
Configure a single pin for output and setup a method to refer to that pin, i.e.:.
-
#serial_begin(opts = {}) ⇒ Object
Configure Arduino for serial communication.
Methods included from ExternalVariableProcessing
#c_type, #check_variable_type, #post_process_arrays, #post_process_vars, #pre_process_vars, #process_external_vars, #translate_variables
Constructor Details
#initialize ⇒ ArduinoSketch
:nodoc:
164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 |
# File 'lib/rad/arduino_sketch.rb', line 164 def initialize #:nodoc: @servo_settings = [] # need modular way to add this @debounce_settings = [] # need modular way to add this @hysteresis_settings = [] @spectra_settings = [] @servo_pins = [] @debounce_pins = [] @hysteresis_pins = [] @spectra_pins = [] $external_array_vars = [] $external_vars =[] $external_var_identifiers = [] $sketch_methods = [] $load_libraries ||= [] $defines ||= [] $define_types = {} $array_types = {} $array_index_helpers ||= ('a'..'zzz').to_a @declarations = [] @pin_modes = {:output => [], :input => []} @pullups = [] @other_setup = [] # specifically, Serial.begin @assembler_declarations = [] @accessors = [] @signatures = ["int main();"] helper_methods = [] @helper_methods = helper_methods.join( "\n" ) end |
Instance Attribute Details
#pins ⇒ Object
Returns the value of attribute pins.
7 8 9 |
# File 'lib/rad/sim/arduino_sketch.rb', line 7 def pins @pins end |
Class Method Details
.add_to_setup(meth) ⇒ Object
600 601 602 603 |
# File 'lib/rad/arduino_sketch.rb', line 600 def self.add_to_setup(meth) meth = meth.gsub("setup", "additional_setup") post_process_ruby_to_c_methods(meth) end |
.output_pin(num, opts) ⇒ Object
13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
# File 'lib/rad/sim/arduino_sketch.rb', line 13 def self.output_pin(num, opts) module_eval "@pins ||= []" module_eval do @pins << Pin.new( num, :type => :output ) end if opts[:as] module_eval <<-CODE def #{opts[:as]} pins.select{|p| p.num == #{num}}.first end CODE end end |
.post_process_ruby_to_c_methods(e) ⇒ Object
605 606 607 608 609 610 611 612 613 614 615 616 |
# File 'lib/rad/arduino_sketch.rb', line 605 def self.post_process_ruby_to_c_methods(e) clean_c_methods = [] # need to take a look at the \(unsigned in the line below not sure if we are really trying to catch something like that if e !~ /^\s*(#{C_VAR_TYPES})(\W{1,6}|\(unsigned\()(#{$external_var_identifiers.join("|")})/ || $external_var_identifiers.empty? # use the list of identifers the external_vars method of the sketch and remove the parens the ruby2c sometime adds to variables # keep an eye on the gsub!.. are we getting nil errors # and more recently, the \b e.gsub!(/\b((#{$external_var_identifiers.join("|")})\(\))/, '\2') unless $external_var_identifiers.empty? clean_c_methods << e end return clean_c_methods.join( "\n" ) end |
.pre_process(sketch_string) ⇒ Object
:nodoc:
585 586 587 588 589 590 591 592 593 594 595 596 597 598 |
# File 'lib/rad/arduino_sketch.rb', line 585 def self.pre_process(sketch_string) #:nodoc: result = sketch_string # add external vars to each method (needed for better translation, will be removed in make:upload) result.gsub!(/(^\s*def\s.\w*(\(.*\))?)/, '\1' + " \n #{$external_vars.join(" \n ")}" ) # gather method names sketch_methods = result.scan(/^\s*def\s.\w*/) sketch_methods.each {|m| $sketch_methods << m.gsub(/\s*def\s*/, "") } result.gsub!("HIGH", "1") result.gsub!("LOW", "0") result.gsub!("ON", "1") result.gsub!("OFF", "0") result end |
Instance Method Details
#add(st) ⇒ Object
:nodoc:
399 400 401 |
# File 'lib/rad/arduino_sketch.rb', line 399 def add(st) #:nodoc: @helper_methods << "\n#{st}\n" end |
#array(arg) ⇒ Object
204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 |
# File 'lib/rad/arduino_sketch.rb', line 204 def array(arg) if arg arg = arg.chomp.rstrip.lstrip arg.sub!("@","__") name = arg.scan(/\s*(\w*)\[\d*\]?/).first.first # help rad_processor do a better job with array types types = ["int", "long", "char*", "unsigned int", "unsigned long", "byte", "bool", "float" ] types.each_with_index do |type, i| @type = types[i] if /#{type}/ =~ arg end raise ArgumentError, "type not currently supported.. got: #{arg}. Currently supporting #{types.join(", ")}" unless @type arg = "#{arg};" unless arg[-1,1] == ";" $array_types[name] = @type @type = nil $external_var_identifiers << name unless $external_var_identifiers.include?(name) # add array_name declaration $external_array_vars << arg unless $external_array_vars.include?(arg) end end |
#assembler(name, signature, code) ⇒ Object
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: rad.rubyforge.org/examples/assembler_test.html
569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 |
# File 'lib/rad/arduino_sketch.rb', line 569 def assembler(name, signature, code) @assembler_declarations << signature assembler_code = <<-CODE .file "#{name}.S" .arch #{Makefile.hardware_params['mcu']} .global __do_copy_data .global __do_clear_bss .text .global #{name} .type #{name}, @function #{code} CODE File.open(File.("#{RAD_ROOT}") + "/#{PROJECT_DIR_NAME}/#{name}.S", "w"){|f| f << assembler_code} end |
#comment_box(content) ⇒ Object
:nodoc:
619 620 621 622 623 624 625 626 |
# File 'lib/rad/arduino_sketch.rb', line 619 def comment_box( content ) #:nodoc: out = [] out << "/" * 74 out << "// " + content out << "/" * 74 return out.join( "\n" ) end |
#compose_setup ⇒ Object
:nodoc: also composes headers and signatures
436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 |
# File 'lib/rad/arduino_sketch.rb', line 436 def compose_setup #:nodoc: also composes headers and signatures software_params = Makefile::software_params declarations = [] plugin_directives = [] signatures = [] external_vars = [] setup = [] additional_setup =[] helpers = [] main = [] result = [] declarations << comment_box( "Auto-generated by RAD" ) declarations << "#include <WProgram.h>\n" declarations << "#include <SoftwareSerial.h>\n" unless software_params['experimental'] == true $load_libraries.each { |lib| declarations << "#include <#{lib}.h>" } unless $load_libraries.nil? $defines.each { |d| declarations << d } plugin_directives << comment_box( 'plugin directives' ) $plugin_directives.each {|dir| plugin_directives << dir } unless $plugin_directives.nil? || $plugin_directives.empty? signatures << comment_box( 'method signatures' ) signatures << "void loop();" signatures << "void setup();" signatures << "// sketch signatures" @signatures.each {|sig| signatures << sig} signatures << "// plugin signatures" $plugin_signatures.each {|sig| signatures << sig } unless $plugin_signatures.nil? || $plugin_signatures.empty? external_vars << "\n" + comment_box( "plugin external variables" ) $plugin_external_variables.each { |meth| external_vars << meth } unless $plugin_external_variables.nil? || $plugin_external_variables.empty? signatures << "\n" + comment_box( "plugin structs" ) $plugin_structs.each { |k,v| signatures << v } unless $plugin_structs.nil? || $plugin_structs.empty? external_vars << "\n" + comment_box( "sketch external variables" ) $external_vars.each {|v| external_vars << v } external_vars << "" external_vars << "// servo_settings array" array_size = @servo_settings.empty? ? 1 : @servo_pins.max + 1 # conserve space if no variables needed external_vars << "struct servo serv[#{array_size}] = { #{@servo_settings.join(", ")} };" if $plugin_structs[:servo] external_vars << "" external_vars << "// debounce array" array_size = @debounce_settings.empty? ? 1 : @debounce_pins.max + 1 # conserve space if no variables needed external_vars << "struct debounce dbce[#{array_size}] = { #{@debounce_settings.join(", ")} };" if $plugin_structs[:debounce] external_vars << "" external_vars << "// hysteresis array" h_array_size = @hysteresis_settings.empty? ? 1 : @hysteresis_pins.length + 1 # conserve space if no variables needed external_vars << "struct hysteresis hyst[#{h_array_size}] = { #{@hysteresis_settings.join(", ")} };" if $plugin_structs[:sensor] external_vars << "" external_vars << "// spectrasymbol soft pot array" sp_array_size = @spectra_settings.empty? ? 1 : @spectra_pins.length + 1 # conserve space if no variables needed external_vars << "struct spectra spec[#{sp_array_size}] = { #{@spectra_settings.join(", ")} };" if $plugin_structs[:spectra] external_vars << "" $external_array_vars.each { |var| external_vars << var } if $external_array_vars external_vars << "\n" + comment_box( "variable and accessors" ) @declarations.each {|dec| external_vars << dec} external_vars << "" @accessors.each {|ac| external_vars << ac} # fix naming external_vars << "\n" + comment_box( "assembler declarations" ) unless @assembler_declarations.empty? external_vars << <<-CODE extern "C" { #{@assembler_declarations.join("\n")} } CODE end external_vars << "\n" + comment_box( "setup" ) setup << "void setup() {" setup << "\t// pin modes" @pin_modes.each do |k,v| v.each do |value| setup << "\tpinMode(#{value}, #{k.to_s.upcase});" end end @pullups.each do |pin| setup << "\tdigitalWrite( #{pin}, HIGH ); // enable pull-up resistor for input" end unless $add_to_setup.nil? || $add_to_setup.empty? setup << "\t// setup from plugins via add_to_setup method" $add_to_setup.each {|item| setup << "\t#{item}"} end unless @other_setup.empty? setup << "\t// other setup" setup << @other_setup.join( "\n" ) end additional_setup << "}\n" helpers << comment_box( "helper methods" ) helpers << "\n// RAD built-in helpers" helpers << @helper_methods.lstrip helpers << "\n" + comment_box( "plugin methods" ) # need to add plugin name to this... $plugin_methods.each { |meth| helpers << "#{meth[0][0]}\n" } unless $plugin_methods.nil? || $plugin_methods.empty? if @@hwserial_inc == TRUE helpers << "\n// serial helpers" helpers << serial_boilerplate.lstrip end main << "\n" + comment_box( "main() function" ) main << "int main() {" main << "\tinit();" main << "\tsetup();" main << "\tfor( ;; ) { loop(); }" main << "\treturn 0;" main << "}" main << "\n" + comment_box( "loop! Autogenerated by RubyToC, sorry it's ugly." ) return [declarations, plugin_directives, signatures, external_vars, setup, additional_setup, helpers, main] end |
#define(arg) ⇒ Object
define “DS1307_SEC 0” result: #define DS1307_SEC 0 note we send the constant identifiers and type to our rad_type_checker however, it only knows about long, float, str.… so we don’t send ints …yet.. need more testing
231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 |
# File 'lib/rad/arduino_sketch.rb', line 231 def define(arg) if arg arg = arg.chomp.rstrip.lstrip name = arg.split(" ").first value = arg.gsub!("#{name} ","") # negative if value =~ /^-(\d|x)*$/ type = "long" # negative float elsif value =~ /^-(\d|\.|x)*$/ type = "float" elsif value =~ /[a-zA-Z]/ type = "str" value = "\"#{value}\"" elsif value !~ /(\.|x)/ type = "long" elsif value =~ /(\d*\.\d*)/ # and no type = "float" elsif value =~ /0x\d\d/ type = "byte" else raise ArgumentError, "opps, could not determine the define type, got #{value}" end $define_types[name] = type arg = "#define #{name} #{value}" $defines << arg dummy_for_testing = arg, type end end |
#delay(millis) ⇒ Object
36 37 |
# File 'lib/rad/sim/arduino_sketch.rb', line 36 def delay( millis ) end |
#digitalWrite(pin, value) ⇒ Object
31 32 33 34 |
# File 'lib/rad/sim/arduino_sketch.rb', line 31 def digitalWrite( pin, value ) to_change = pins.select{|p| p.num == pin.num}.first to_change.value = value end |
#formatted_print(opts = {}) ⇒ Object
416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 |
# File 'lib/rad/arduino_sketch.rb', line 416 def formatted_print(opts={}) buffer_size = opts[:buffer_size] ? opts[:buffer_size] : 64 if opts[:as] @@sprintf_inc ||= FALSE if @@sprintf_inc == FALSE @@sprintf_inc = TRUE accessor = [] accessor << "\n#undef int\n#include <stdio.h>" accessor << "#define write_line(...) sprintf(#{opts[:as]},__VA_ARGS__);" @accessors << accessor.join( "\n" ) array("char #{opts[:as]}[#{buffer_size}]") end end end |
#input_pin(num, opts = {}) ⇒ Object
Configure a single pin for input and setup a method to refer to that pin, i.e.:
input_pin 3, :as => :button
would configure pin 3 as an input and let you refer to it from the then on by calling the ‘button` method in your loop like so:
def loop
digital_write led if digital_read
end
351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 |
# File 'lib/rad/arduino_sketch.rb', line 351 def input_pin(num, opts={}) raise ArgumentError, "can only define pin from Fixnum, got #{num.class}" unless num.is_a?(Fixnum) @pin_modes[:input] << num if opts[:as] # transitioning to :device => :button syntax if opts[:latch] || opts[:device] == :button if opts[:device] == :button opts[:latch] ||= :off end # add debounce settings to dbce struct array ArduinoPlugin.add_debounce_struct @debounce_pins << num state = opts[:latch] == :on ? 1 : 0 prev = opts[:latch] == :on ? 0 : 1 adjust = opts[:adjust] ? opts[:adjust] : 200 @debounce_settings << "dbce[#{num}].state = #{state}, dbce[#{num}].read = 0, dbce[#{num}].prev = #{prev}, dbce[#{num}].time = 0, dbce[#{num}].adjust = #{adjust}" end if opts[:device] == :sensor ArduinoPlugin.add_sensor_struct count = @hysteresis_pins.length @hysteresis_pins << num @hysteresis_settings << "hyst[#{count}].pin = #{num}, hyst[#{count}].state = 0" end if opts[:device] == :spectra ArduinoPlugin.add_spectra_struct count = @spectra_pins.length @spectra_pins << num @spectra_settings << "spec[#{count}].pin = #{num}, spec[#{count}].state = 10, spec[#{count}].r1 = 0, spec[#{count}].r2 = 0, spec[#{count}].r3 = 0" end @declarations << "int _#{opts[ :as ]} = #{num};" accessor = [] accessor << "int #{opts[ :as ]}() {" accessor << "\treturn _#{opts[ :as ]};" accessor << "}" @accessors << accessor.join( "\n" ) @signatures << "int #{opts[ :as ]}();" end @pullups << num if opts[:as] end |
#input_pins(nums) ⇒ Object
Like ArduinoSketch#input_pin but configure more than one input pin simultaneously. Takes an array of pin numbers.
394 395 396 397 |
# File 'lib/rad/arduino_sketch.rb', line 394 def input_pins(nums) ar = Array(nums) ar.each {|n| input_pin(n)} end |
#loop ⇒ Object
28 29 |
# File 'lib/rad/sim/arduino_sketch.rb', line 28 def loop end |
#output_pin(num, opts = {}) ⇒ Object
Configure a single pin for output and setup a method to refer to that pin, i.e.:
output_pin 7, :as => :led
would configure pin 7 as an output and let you refer to it from the then on by calling the ‘led` method in your loop like so:
def loop
digital_write led, ON
end
272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 |
# File 'lib/rad/arduino_sketch.rb', line 272 def output_pin(num, opts={}) raise ArgumentError, "can only define pin from Fixnum, got #{num.class}" unless num.is_a?(Fixnum) @pin_modes[:output] << num if opts[:as] if opts[:device] case opts[:device] when :servo servo_setup(num, opts) return # don't use declarations, accessor, signatures below when :pa_lcd || :pa_LCD pa_lcd_setup(num, opts) return when :sf_lcd || :sf_LCD sf_lcd_setup(num, opts) return when :freq_out || :freq_gen || :frequency_generator frequency_timer(num, opts) return when :i2c two_wire(num, opts) unless @@twowire_inc return # when :i2c_eeprom two_wire(num, opts) unless @@twowire_inc i2c_eeprom(num, opts) return # when :i2c_ds1307 two_wire(num, opts) unless @@twowire_inc ds1307(num, opts) return # when :i2c_blinkm two_wire(num, opts) unless @@twowire_inc blinkm return # when :onewire one_wire(num, opts) return # when :ethernet ethernet(num, opts) return # else raise ArgumentError, "today's device choices are: :servo, :pa_lcd, :sf_lcd, :freq_out,:i2c, :i2c_eeprom, :i2c_ds1307, and :i2c_blinkm got #{opts[:device]}" end end # add state variables for outputs with :state => :on or :off -- useful for toggling a light with output_toggle -- need to make this more modular if opts[:state] # add debounce settings to dbce struct array ArduinoPlugin.add_debounce_struct @debounce_pins << num state = opts[:latch] == :on ? 1 : 0 prev = opts[:latch] == :on ? 0 : 1 adjust = opts[:adjust] ? opts[:adjust] : 20 @debounce_settings << "dbce[#{num}].state = #{state}, dbce[#{num}].read = 0, dbce[#{num}].prev = #{prev}, dbce[#{num}].time = 0, dbce[#{num}].adjust = #{adjust}" end @declarations << "int _#{opts[ :as ]} = #{num};" accessor = [] accessor << "int #{opts[ :as ]}() {" accessor << "\treturn _#{opts[ :as ]};" accessor << "}" @accessors << accessor.join( "\n" ) @signatures << "int #{opts[ :as ]}();" end end |
#serial_begin(opts = {}) ⇒ Object
Configure Arduino for serial communication. Optionally, set the baud rate:
serial_begin :rate => 2400
default is 9600. See www.arduino.cc/en/Serial/Begin for more details.
409 410 411 412 413 |
# File 'lib/rad/arduino_sketch.rb', line 409 def serial_begin(opts={}) rate = opts[:rate] ? opts[:rate] : 9600 @other_setup << "Serial.begin(#{rate});" @@hwserial_inc = TRUE end |