Upgrade to Pro — share decks privately, control downloads, hide ads and more …

My Little C Extension: Lego Robots are Magic

6fd16b1b6a307ca583526e2ec4dab52d?s=47 tehviking
November 19, 2014

My Little C Extension: Lego Robots are Magic

Learn about C Extensions in Ruby, using the Raspberry Pi to control Lego Mindstorms, and dealing with burnout with your host, Brandon Hays.

6fd16b1b6a307ca583526e2ec4dab52d?s=128

tehviking

November 19, 2014
Tweet

Transcript

  1. We know you have a choice in conference tracks. Thank

    you for choosing ours. Welcome to the Fringe Ruby track.
  2. My Little C Extension: Lego Robots are Magic!

  3. None
  4. None
  5. None
  6. Brandon Hays. Catsultant. @tehviking.

  7. <3

  8. About C Extensions?

  9. About Robots?

  10. About Ponies?

  11. Actually a story about BURNOUT

  12. Crazy passionate about software

  13. Programming: SUPER FUN

  14. Programming: SUPER FUN

  15. Started running out of steam

  16. Because fun would be unprofessional.

  17. Lazy? Incapable? Broken?

  18. I FEAR these things about myself, but I don’t BELIEVE

    them.
  19. So... What was wrong with me?

  20. How can you combat these demons?

  21. More work?

  22. Distractions?

  23. Distraction ROI High Not so good Worthless

  24. How can you *actually* combat these demons?

  25. You STOP and PLAY.

  26. You STOP and PLAY. and SHIP.

  27. ONE WEIRD TRICK the high clergy of Computerology don’t want

    you to know...
  28. Coin an acronym.

  29. HIPS CAFE

  30. HIPS CAFE

  31. H it burnout I nspiration purchase P laytime S teal

    knowledge C heat to win A tone for your cheating F ind a practical application E njoy a Coca-Cola™
  32. Hit burnout

  33. (this means stop)

  34. Inspiration purchase

  35. COOOOOOL

  36. None
  37. None
  38. THESE PEOPLE MAKE SMARTER BRAINS THAN LEGO

  39. My stupid dream

  40. Playtime!

  41. Yak shaving can be fun when it’s a pet yak

  42. Let’s write in C

  43. Let’s write in C

  44. None
  45. Let’s write in C

  46. Why can’t I just use Ruby?

  47. If you’re doing it right, this is where you get

    stuck
  48. Steal knowledge

  49. You must build your own mentorship.

  50. Devs: We have a responsibility to the community that nurtured

    us.
  51. @cowboyd my mentor therubyracer his experience

  52. Advisor Helper Collaborator Secret Puppeteer

  53. My prior experience with C extensions

  54. Building a C extension

  55. 1. Create a gem http://guides.rubygems.org/make-your-own-gem/ $ bundle gem brick_pi -

    Edit .gemspec - Edit lib/brick_pi/version.rb - Add `gem 'rake-compiler'` to Gemfile
  56. 2. Lay filesystem groundwork http://guides.rubygems.org/gems-with-extensions/ - Create ext/brick_pi/ - Include

    C files needed for the robot (tick.h, BrickPi.h) - Create native.c - Create extconf.rb
  57. 2. Lay filesystem groundwork require "bundler/gem_tasks" require "rake/extensiontask" Rake::ExtensionTask.new("native", eval(File.read("brick_pi.gemspec")))

    do |ext| ext.ext_dir = "ext/brick_pi" ext.lib_dir = "lib/brick_pi" ext.source_pattern = "*.{c,h}" end Rakefile http://guides.rubygems.org/gems-with-extensions/
  58. 2. Lay filesystem groundwork Gem::Specification.new do |spec| spec.name = "brick_pi"

    spec.version = BrickPi::VERSION ... spec.license = "MIT" spec.require_paths = ["lib", "ext"] spec.extensions = ["ext/brick_pi/extconf.rb"] ... end .gemspec http://guides.rubygems.org/gems-with-extensions/
  59. require 'mkmf' find_header 'wiringPi.h' find_library 'wiringPi', 'serialOpen' create_makefile 'brick_pi/native' ext/brick_pi/extconf.rb

    2. Lay filesystem groundwork
  60. “We go to war with the API we wish we

    had, not the API we have. - Robert Schuller
  61. 3. Specify Ruby API # I want this API: BrickPi::Native.brickPiSetup()

    # In Ruby it’d look like this: module BrickPi module Native def self.brickPiSetup() # do some C stuff here end end end
  62. # I want this API: BrickPi::Native.brickPiSetup() # In Ruby it’d

    look like this: module BrickPi module Native def self.brickPiSetup() # do some C stuff here end end end 3. Specify Ruby API Top namespace (from gem)
  63. # I want this API: BrickPi::Native.brickPiSetup() # In Ruby it’d

    look like this: module BrickPi module Native def self.brickPiSetup() # do some C stuff here end end end 3. Specify Ruby API “Native” namespace
  64. # I want this API: BrickPi::Native.brickPiSetup() # In Ruby it’d

    look like this: module BrickPi module Native def self.brickPiSetup() # do some C stuff here end end end 3. Specify Ruby API Setup method
  65. STATUS CODE ZERO

  66. 4. Write first wrapper #include "ruby.h"; #include "tick.h"; #include "BrickPi.h";

    VALUE call_brickPiSetup(VALUE self) { return INT2FIX(BrickPiSetup()); } void Init_native() { VALUE BrickPi = rb_define_module("BrickPi"); VALUE Native = rb_define_module_under(BrickPi, "Native"); rb_define_singleton_method(Native, "brickPiSetup", call_brickPiSetup, 0); } ext/brick_pi/native.c
  67. #include "ruby.h"; #include "tick.h"; #include "BrickPi.h"; 4. Write first wrapper

    #include "ruby.h"; #include "tick.h"; #include "BrickPi.h"; VALUE call_brickPiSetup(VALUE self) { return INT2FIX(BrickPiSetup()); } void Init_native() { VALUE BrickPi = rb_define_module("BrickPi"); VALUE Native = rb_define_module_under(BrickPi, "Native"); rb_define_singleton_method(Native, "brickPiSetup", call_brickPiSetup, 0); } ext/brick_pi/native.c Ruby C libs Include:
  68. #include "ruby.h"; #include "tick.h"; #include "BrickPi.h"; 4. Write first wrapper

    #include "ruby.h"; #include "tick.h"; #include "BrickPi.h"; VALUE call_brickPiSetup(VALUE self) { return INT2FIX(BrickPiSetup()); } void Init_native() { VALUE BrickPi = rb_define_module("BrickPi"); VALUE Native = rb_define_module_under(BrickPi, "Native"); rb_define_singleton_method(Native, "brickPiSetup", call_brickPiSetup, 0); } ext/brick_pi/native.c C dependencies Include:
  69. #include "ruby.h"; #include "tick.h"; #include "BrickPi.h"; VALUE call_brickPiSetup(VALUE self) {

    return INT2FIX(BrickPiSetup()); } void Init_native() { VALUE BrickPi = rb_define_module("BrickPi"); VALUE Native = rb_define_module_under(BrickPi, "Native"); rb_define_singleton_method(Native, "brickPiSetup", call_brickPiSetup, 0); } void Init_native() { 4. Write first wrapper ext/brick_pi/native.c “magic” init_ method
  70. #include "ruby.h"; #include "tick.h"; #include "BrickPi.h"; VALUE call_brickPiSetup(VALUE self) {

    return INT2FIX(BrickPiSetup()); } void Init_native() { VALUE BrickPi = rb_define_module("BrickPi"); VALUE Native = rb_define_module_under(BrickPi, "Native"); rb_define_singleton_method(Native, "brickPiSetup", call_brickPiSetup, 0); } VALUE BrickPi = rb_define_module("BrickPi"); VALUE Native = rb_define_module_under(BrickPi, "Native"); 4. Write first wrapper ext/brick_pi/native.c module BrickPi module BrickPi:Native
  71. #include "ruby.h"; #include "tick.h"; #include "BrickPi.h"; VALUE call_brickPiSetup(VALUE self) {

    return INT2FIX(BrickPiSetup()); } void Init_native() { VALUE BrickPi = rb_define_module("BrickPi"); VALUE Native = rb_define_module_under(BrickPi, "Native"); rb_define_singleton_method(Native, "brickPiSetup", call_brickPiSetup, 0); } rb_define_singleton_method 4. Write first wrapper ext/brick_pi/native.c def self.brickPiSetup()
  72. #include "ruby.h"; #include "tick.h"; #include "BrickPi.h"; VALUE call_brickPiSetup(VALUE self) {

    return INT2FIX(BrickPiSetup()); } void Init_native() { VALUE BrickPi = rb_define_module("BrickPi"); VALUE Native = rb_define_module_under(BrickPi, "Native"); rb_define_singleton_method(Native, "brickPiSetup", call_brickPiSetup, 0); } Native, 4. Write first wrapper ext/brick_pi/native.c defined on
  73. #include "ruby.h"; #include "tick.h"; #include "BrickPi.h"; VALUE call_brickPiSetup(VALUE self) {

    return INT2FIX(BrickPiSetup()); } void Init_native() { VALUE BrickPi = rb_define_module("BrickPi"); VALUE Native = rb_define_module_under(BrickPi, "Native"); rb_define_singleton_method(Native, "brickPiSetup", call_brickPiSetup, 0); } "brickPiSetup" 4. Write first wrapper ext/brick_pi/native.c Ruby method name
  74. #include "ruby.h"; #include "tick.h"; #include "BrickPi.h"; VALUE call_brickPiSetup(VALUE self) {

    return INT2FIX(BrickPiSetup()); } void Init_native() { VALUE BrickPi = rb_define_module("BrickPi"); VALUE Native = rb_define_module_under(BrickPi, "Native"); rb_define_singleton_method(Native, "brickPiSetup", call_brickPiSetup, 0); } VALUE call_brickPiSetup call_brickPiSetup 4. Write first wrapper ext/brick_pi/native.c C helper method
  75. #include "ruby.h"; #include "tick.h"; #include "BrickPi.h"; VALUE call_brickPiSetup(VALUE self) {

    return INT2FIX(BrickPiSetup()); } void Init_native() { VALUE BrickPi = rb_define_module("BrickPi"); VALUE Native = rb_define_module_under(BrickPi, "Native"); rb_define_singleton_method(Native, "brickPiSetup", call_brickPiSetup, 0); } 0 4. Write first wrapper ext/brick_pi/native.c no Ruby args
  76. #include "ruby.h"; #include "tick.h"; #include "BrickPi.h"; VALUE call_brickPiSetup(VALUE self) {

    return INT2FIX(BrickPiSetup()); } void Init_native() { VALUE BrickPi = rb_define_module("BrickPi"); VALUE Native = rb_define_module_under(BrickPi, "Native"); rb_define_singleton_method(Native, "brickPiSetup", call_brickPiSetup, 0); } 4. Write first wrapper VALUE call_brickPiSetup ext/brick_pi/native.c wrapper/helper function
  77. #include "ruby.h"; #include "tick.h"; #include "BrickPi.h"; VALUE call_brickPiSetup(VALUE self) {

    return INT2FIX(BrickPiSetup()); } void Init_native() { VALUE BrickPi = rb_define_module("BrickPi"); VALUE Native = rb_define_module_under(BrickPi, "Native"); rb_define_singleton_method(Native, "brickPiSetup", call_brickPiSetup, 0); } 4. Write first wrapper BrickPiSetup() ext/brick_pi/native.c original C function
  78. #include "ruby.h"; #include "tick.h"; #include "BrickPi.h"; VALUE call_brickPiSetup(VALUE self) {

    return INT2FIX(BrickPiSetup()); } void Init_native() { VALUE BrickPi = rb_define_module("BrickPi"); VALUE Native = rb_define_module_under(BrickPi, "Native"); rb_define_singleton_method(Native, "brickPiSetup", call_brickPiSetup, 0); } 4. Write first wrapper INT2FIX ext/brick_pi/native.c cast C int to Ruby Fixnum
  79. #include "ruby.h"; #include "tick.h"; #include "BrickPi.h"; VALUE call_brickPiSetup(VALUE self) {

    return INT2FIX(BrickPiSetup()); } void Init_native() { VALUE BrickPi = rb_define_module("BrickPi"); VALUE Native = rb_define_module_under(BrickPi, "Native"); rb_define_singleton_method(Native, "brickPiSetup", call_brickPiSetup, 0); } 4. Write first wrapper (VALUE self) { 0 ext/brick_pi/native.c wait, what?
  80. Object-oriented C

  81. #include "ruby.h"; #include "tick.h"; #include "BrickPi.h"; VALUE call_brickPiSetup(VALUE self) {

    return INT2FIX(BrickPiSetup()); } void Init_native() { VALUE BrickPi = rb_define_module("BrickPi"); VALUE Native = rb_define_module_under(BrickPi, "Native"); rb_define_singleton_method(Native, "brickPiSetup", call_brickPiSetup, 0); } Object-oriented C VALUE VALUE ext/brick_pi/native.c magic ‘VALUE’ type
  82. #include "ruby.h"; #include "tick.h"; #include "BrickPi.h"; VALUE call_brickPiSetup(VALUE self) {

    return INT2FIX(BrickPiSetup()); } void Init_native() { VALUE BrickPi = rb_define_module("BrickPi"); VALUE Native = rb_define_module_under(BrickPi, "Native"); rb_define_singleton_method(Native, "brickPiSetup", call_brickPiSetup, 0); } Object-oriented C self ext/brick_pi/native.c always pass self as 1st argument
  83. 4. Build gem & experiment $ rake compile $ cd

    ~/projects/brick_pi_test $ cat "gem 'brick_pi', '/users/pi/projects/brick_pi'" > Gemfile $ bundle exec irb > require 'brick_pi' > BrickPi::Native.brickPiSetup()
  84. None
  85. It’ll probably look more like this

  86. Holy crap, it worked > BrickPi::Native.brickPiSetup() => 0 => Also

    hey nice and cool job you are a pretty good => programmer you should maybe do this for a living
  87. But wait, our Ruby file is empty! require "brick_pi/version" require

    "brick_pi/native" module BrickPi # Your code goes here... end lib/brick_pi.rb
  88. So where did that Ruby come from?

  89. From here! void Init_native() { VALUE BrickPi = rb_define_module("BrickPi"); VALUE

    Native = rb_define_module_under(BrickPi, "Native"); rb_define_singleton_method(Native, "brick_pi_setup", call_brick_pi_setup, 0); } ext/brick_pi/native.c
  90. So we just wrote Ruby... in C?

  91. Of course! How do you think Matz does it?

  92. Heck, how do you think Ruby does it?

  93. Believe it or not, that was the hard part.

  94. The rest: - Flesh out API - Spin the motors

  95. Flesh out the API

  96. Find what you want to wrap

  97. Documentation/ example code

  98. MotorSpeed Addresses PORT_X constants MotorEnable Use example code...

  99. MotorSpeed Addresses PORT_X constants MotorEnable ...to decipher source code

  100. C Extension Style Guide

  101. Hard-Won knowledge

  102. Is best when won by someone else

  103. • Keep low-level Ruby ugly, close to C code as

    possible • Low-level wrappers in BrickPi::Native module • Prefix C method helpers for easy identification
  104. Keep low-level Ruby ugly, close to C code as possible

    rb_define_singleton_method(Native, "BrickPiUpdateValues", bprb_BrickPiUpdateValues, 0); BrickPi::Native.BrickPiUpdateValues() lib/brick_pi/bot.rb ext/brick_pi/native.c
  105. None
  106. Low-level wrappers in BrickPi::Native module void Init_native() { VALUE BrickPi

    = rb_define_module("BrickPi"); VALUE Native = rb_define_module_under(BrickPi, "Native"); VALUE MotorSpeed = rb_define_module_under(Native, "MotorSpeed"); module BrickPi class Motor def spin(speed) speed = [-100, [100, speed].min].max motor_speed = (speed * 2.55).round Native::MotorSpeed[@port] = motor_speed end end end lib/brick_pi/motor.rb ext/brick_pi/native.c
  107. Prefix C method helpers for easy identification VALUE bprb_BrickPiSetup(VALUE self)

    { return INT2FIX(BrickPiSetup()); } VALUE bprb_BrickPiSetupSensors(VALUE self) { return INT2FIX(BrickPiSetupSensors()); } VALUE bprb_ClearTick(VALUE self) { return INT2FIX(ClearTick()); } VALUE bprb_MotorSpeed_set(VALUE self, VALUE key, VALUE value) { int index = FIX2INT(key); BrickPi.MotorSpeed[index] = FIX2INT(value); return value; } bprb, for brick_pi_ruby
  108. Putting it together VALUE bprb_SensorType_set(VALUE self, VALUE key, VALUE value)

    { int index = FIX2INT(key); BrickPi.SensorType[index] = FIX2INT(value); return value; } ... VALUE SensorType = rb_define_module_under(Native, "SensorType"); rb_define_singleton_method(SensorType, "[]=", bprb_SensorType_set, 2);
  109. Putting it together VALUE bprb_SensorType_set(VALUE self, VALUE key, VALUE value)

    { int index = FIX2INT(key); BrickPi.SensorType[index] = FIX2INT(value); return value; } ... VALUE SensorType = rb_define_module_under(Native, "SensorType"); rb_define_singleton_method(SensorType, "[]=", bprb_SensorType_set, 2); VALUE SensorType = rb_define_module_under(Native, "SensorType"); module Native::SensorType
  110. Putting it together VALUE bprb_SensorType_set(VALUE self, VALUE key, VALUE value)

    { int index = FIX2INT(key); BrickPi.SensorType[index] = FIX2INT(value); return value; } ... VALUE SensorType = rb_define_module_under(Native, "SensorType"); rb_define_singleton_method(SensorType, "[]=", bprb_SensorType_set, 2); rb_define_singleton_method(SensorType, "[]=", def self.[]=(key, val)
  111. Putting it together VALUE bprb_SensorType_set(VALUE self, VALUE key, VALUE value)

    { int index = FIX2INT(key); BrickPi.SensorType[index] = FIX2INT(value); return value; } ... VALUE SensorType = rb_define_module_under(Native, "SensorType"); rb_define_singleton_method(SensorType, "[]=", bprb_SensorType_set, 2); bprb_SensorType_set bprb_SensorType_set call to C helper/wrapper
  112. Putting it together VALUE bprb_SensorType_set(VALUE self, VALUE key, VALUE value)

    { int index = FIX2INT(key); BrickPi.SensorType[index] = FIX2INT(value); return value; } ... VALUE SensorType = rb_define_module_under(Native, "SensorType"); rb_define_singleton_method(SensorType, "[]=", bprb_SensorType_set, 2); VALUE self pass self 1st
  113. Putting it together VALUE bprb_SensorType_set(VALUE self, VALUE key, VALUE value)

    { int index = FIX2INT(key); BrickPi.SensorType[index] = FIX2INT(value); return value; } ... VALUE SensorType = rb_define_module_under(Native, "SensorType"); rb_define_singleton_method(SensorType, "[]=", bprb_SensorType_set, 2); VALUE key, VALUE value ... 2 2 Ruby arguments def self.[]=(key, val)
  114. Putting it together VALUE bprb_SensorType_set(VALUE self, VALUE key, VALUE value)

    { int index = FIX2INT(key); BrickPi.SensorType[index] = FIX2INT(value); return value; } ... VALUE SensorType = rb_define_module_under(Native, "SensorType"); rb_define_singleton_method(SensorType, "[]=", bprb_SensorType_set, 2); { int index = FIX2INT(key); FIX2INT(value) cast Fixnum in Ruby to INT in C
  115. Putting it together VALUE bprb_SensorType_set(VALUE self, VALUE key, VALUE value)

    { int index = FIX2INT(key); BrickPi.SensorType[index] = FIX2INT(value); return value; } ... VALUE SensorType = rb_define_module_under(Native, "SensorType"); rb_define_singleton_method(SensorType, "[]=", bprb_SensorType_set, 2); BrickPi.SensorType[index] = value Set value at position in C array
  116. Putting it together VALUE bprb_SensorType_set(VALUE self, VALUE key, VALUE value)

    { int index = FIX2INT(key); BrickPi.SensorType[index] = FIX2INT(value); return value; } ... VALUE SensorType = rb_define_module_under(Native, "SensorType"); rb_define_singleton_method(SensorType, "[]=", bprb_SensorType_set, 2); return value; return VALUE ‘value’ to Ruby
  117. Lather, rinse, repeat

  118. void Init_native() { VALUE BrickPi = rb_define_module("BrickPi"); VALUE Native =

    rb_define_module_under(BrickPi, "Native"); rb_define_singleton_method(Native, "BrickPiSetup", bprb_BrickPiSetup, 0); rb_define_singleton_method(Native, "BrickPiSetupSensors", bprb_BrickPiSetupSensors, 0); rb_define_singleton_method(Native, "ClearTick", bprb_ClearTick, 0); VALUE MotorSpeed = rb_define_module_under(Native, "MotorSpeed"); rb_define_singleton_method(MotorSpeed, "[]=", bprb_MotorSpeed_set, 2); VALUE MotorEnable = rb_define_module_under(Native, "MotorEnable"); rb_define_singleton_method(MotorEnable, "[]=", bprb_MotorEnable_set, 2); VALUE Address = rb_define_module_under(Native, "Address"); rb_define_singleton_method(Address, "[]=", bprb_Address_set, 2); VALUE SensorType = rb_define_module_under(Native, "SensorType"); rb_define_singleton_method(SensorType, "[]=", bprb_SensorType_set, 2); VALUE Sensor = rb_define_module_under(Native, "Sensor"); rb_define_singleton_method(Sensor, "[]", bprb_Sensor_get, 1); VALUE Encoder = rb_define_module_under(Native, "Encoder"); rb_define_singleton_method(Encoder, "[]", bprb_Encoder_get, 1); Does this look repetitive?
  119. rb_define_const(Native, "PORT_A", INT2FIX(0)); rb_define_const(Native, "PORT_B", INT2FIX(1)); rb_define_const(Native, "PORT_C", INT2FIX(2)); rb_define_const(Native,

    "PORT_D", INT2FIX(3)); rb_define_const(Native, "PORT_1", INT2FIX(0)); rb_define_const(Native, "PORT_2", INT2FIX(1)); rb_define_const(Native, "PORT_3", INT2FIX(2)); rb_define_const(Native, "PORT_4", INT2FIX(3)); rb_define_const(Native, "US_PORT", INT2FIX(2)); rb_define_const(Native, "TYPE_SENSOR_TOUCH", INT2FIX(32)); rb_define_const(Native, "TYPE_SENSOR_ULTRASONIC_CONT", INT2FIX(33)); rb_define_const(Native, "TYPE_SENSOR_ULTRASONIC_SS", INT2FIX(34)); rb_define_const(Native, "TYPE_SENSOR_COLOR_FULL", INT2FIX(36)); rb_define_const(Native, "TYPE_SENSOR_COLOR_RED", INT2FIX(37)); rb_define_const(Native, "TYPE_SENSOR_COLOR_GREEN", INT2FIX(38)); rb_define_const(Native, "TYPE_SENSOR_COLOR_BLUE", INT2FIX(39)); rb_define_const(Native, "TYPE_SENSOR_COLOR_NONE", INT2FIX(40)); } That’s because it is repetitive.
  120. OK, stop.

  121. How do you know all these methods?

  122. I didn’t. I cheated.

  123. Cheat to win

  124. What’s so bad about cheating, anyway?

  125. Why should the race always be to the swift, or

    the Jumble to the quick-witted? Should they be allowed to win merely because of the gifts God gave them? Well I say, "Cheating is the gift man gives himself." - C. Montgomery Burns
  126. /via Life Cheating @cheatsoflife

  127. C Ext Cheat Sheets http://java.ociweb.com/mark/NFJS/RubyCExtensions.pdf http://blog.jacius.info/ruby-c-extension-cheat-sheet/ https://github.com/ruby/ruby/blob/trunk/include/ruby/ruby.h Pickaxe book

  128. Can you tell me all the secrets of C extension

    methods? Ok, then we can go get tacos.
  129. The C is done. Locked in a mysterious black box

  130. Ruby API == MVP

  131. no. nonono. require 'brick_pi' include BrickPi motor1 = Native::PORT_A motor2

    = Native::PORT_B Native.ClearTick() Native.BrickPiSetup() Native::MotorSpeed[motor1] = 200 Native::MotorSpeed[motor2] = -200 Native::Address[0] = 1 Native::Address[1] = 2 Native::MotorEnable[motor1] = 1 Native::MotorEnable[motor2] = 1 Native.Timeout = 3000 Native.BrickPiSetTimeout() Native.BrickPiUpdateValues() pseudo.rb
  132. Motors spin! Can haz tacos nao?

  133. Better? require 'brick_pi' include BrickPi class BrickBot def start @stop

    = false ...setup code... Thread.new do until @stop do Native.BrickPiUpdateValues() sleep(50/1000) end end end def stop @stop = true end end pseudo.rb
  134. “I have a passion for writing terrible software.”

  135. Sorry, no

  136. None
  137. module BrickPi class Bot include BrickPi::Configuration attr_accessor :motor_A, :motor_B, :motor_C,

    :motor_D attr_accessor :sensor_1, :sensor_2, :sensor_3, :sensor_4 def initialize ...same old init code as before... end def start ...same old init code as before... end def stop @stop = true end def run start yield stop end end end
  138. # Instantiate the Bot bot = BrickPi.create do |bot| bot.motor

    :port_B bot.motor :port_C end # Get this party started bot.run do # Half speed on both motors. Max value is 100. bot.motor_B.spin 50 bot.motor_C.spin 50 sleep 5 # Stop motors bot.motor_B.stop bot.motor_C.stop end
  139. gem “brick-pi” v. 0.1.0

  140. rake  release is a miracle

  141. 0.1.0 + 45 minutes: remote control

  142. Atone for your cheating

  143. def tick self.class.devices.each { |d| d.zero } next_behaviors = []

    @behaviors.sort.each do |behavior| behavior.apply(next_behaviors) if behavior.active? next_behaviors << behavior end end @behaviors = next_behaviors self.class.properties.each { |p| p.apply self } end def behavior(priority=1, &block) Behavior.new(priority, &block).tap do |behavior| @behaviors << behavior end end def add_behavior(*behaviors) @behaviors.concat behaviors end def stop @stop = true @thread = nil end
  144. class BehaviorBot < BrickPi::Bot attr_accessor :speed motor :port_A motor :port_B

    ultrasonic_sensor :port_3 # A property is always settable and gettable, even while things are running. property :speed_left do |value| @motor_A.spin value end property :speed_right do |value| @motor_B.spin value end def move_forward(seconds) behavior do |time, done| self.speed_left = self.speed_right = speed done.call if time > seconds end end ... end
  145. @bot = BehaviorBot.new @bot.speed = 100 @bot.start # move forward

    for 4 seconds # then turn left for 3 seconds # then move forward for another 10 seconds @bot.move_forward(4).then do @bot.turn_left(3).then do @bot.move_backward(3).then do @bot.stop_motors(nil, true) end end end # Add a behavior to watch sensor and stop if motors are moving @bot.behavior(9) do |time, done| print "SENSOR DISTANCE: " puts @bot.sensor_3.distance if @bot.sensor_3.distance < 10 @bot.speed_left = @bot.speed_right = 0 end end @bot.wait
  146. Find a practical application

  147. What else can it do?

  148. None
  149. What else can it do? # Square 4.times do @bot.forward

    2 @bot.turn -90 end
  150. What else can it do? # Crazy spirograph 12.times do

    @bot.forward 2 @bot.turn -140 end
  151. What else can it do?

  152. Portrait of the artist as a young robot

  153. Enjoy an ice-cold Coca-Cola™

  154. Conditioning is delicious™

  155. Oh no, people are using it.

  156. Hit burnout (again)

  157. A confession

  158. First, a confession That’s my secret, Cap. I’m always burned

    out.
  159. How did I manage to pour dozens of hours into

    learning C extensions while severely burned out?
  160. More importantly, how did that activity actually help alleviate and

    even fix my burnout?
  161. Programming is for humans.

  162. You are a human.

  163. H it burnout I nspiration purchase P laytime S teal

    knowledge C heat to win A tone for your cheating F ind a practical application E njoy a Coca-Cola™
  164. Play is how you get your <3 back.

  165. Play is how you remember your limitless potential.

  166. Look at this little guy <3

  167. Sharing “play projects” is behind a huge portion of good

    done in the world.
  168. make8bitart.com

  169. meatspac.es

  170. _why’s poignant guide to ruby

  171. The world needs you to stop. and play. and ship.

  172. Play is the only power fun enough to inspire the

    next generation.
  173. Thank you. @tehviking

  174. Sunset Airplane Landing by dgmiami https://www.flickr.com/photos/dgmiami/ Yak by Chandan Kumar

    https://www.flickr.com/photos/ck-/ Hello World by Windell Oskay https://www.flickr.com/photos/oskay/ Stuck in the Mud by Jason Rogers https://www.flickr.com/photos/restlessglobetrotter/ Lego porn by Eric https://www.flickr.com/photos/ejpphoto/ Black box by thierry ehrmann https://www.flickr.com/photos/home_of_chaos/ Taco cat by Shannon Des Roches Rosa https://www.flickr.com/photos/shannonrosa/ Number Five is Alive by emilydickinsonridesabmx https://www.flickr.com/photos/emilyrides/ Rainbow Dash at BrickCon by Katie Walker https://www.flickr.com/photos/eilonwy77/ Elvis Diner by Rahel Jaskow https://www.flickr.com/photos/rahel_jaskow/ Mini Fluttershy by Pascal https://www.flickr.com/photos/pasukaru76/ Black Cover for Why's (Poignant) Guide to Ruby by Why the Lucky Stiff https://www.flickr.com/photos/_why/ Image credits