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

A Droid's Journey - CodeDaze 2018

B87c43d4be875c9b41cd436f5c364f75?s=47 hone
September 14, 2018

A Droid's Journey - CodeDaze 2018

B87c43d4be875c9b41cd436f5c364f75?s=128

hone

September 14, 2018
Tweet

More Decks by hone

Other Decks in Programming

Transcript

  1. CodeDaze Episode II It is a dark time for the

    galaxy. The new GO ORDER has seized power over the systems, imposing it’s diabolical rules on the population. Space battles are being fought across the galaxy and the TAB fighters are winning. The ORDER has been busy converting historic libraries, compiling their scripts into new libraries that will bend to the will of GO. With the support of a large group of fanatic and loyal followers it seems all other languages will be lost to the might of the ORDER. But a small RUBY ALLIANCE has plans to hijack the ORDER’s Goroutines and in doing so, embed itself into their droid network. A difficult task lies ahead... A surprisingly short time ago, when this conference was not too far away...
  2. A Droid’s™ Journey with mruby & Go

  3. Chase McCarthy @code0100fun

  4. None
  5. Terence Lee @hone02

  6. None
  7. Austin, TX

  8. Austin, TX

  9. These are the droids you're looking for... August 2017 Sphero

    Announces R2-D2
  10. Best R2-D2 replica toy

  11. What can I do with this thing?

  12. R2D2 + Ruby = ?

  13. Ruby on Robots

  14. • Sphero support* • *Only Bluetooth Classic • No support

    for BLE! • No BB8! GitHub issue #165 Artoo
  15. Gobot

  16. Pure Go BLE library *Linux and OS X only github.com/go-ble/ble

  17. BB8 Hello World func main() { bleAdaptor := ble.NewClientAdaptor(os.Args[1]) bb8

    := bb8.NewDriver(bleAdaptor) work := func() { gobot.Every(1*time.Second, func() { r := uint8(gobot.Rand(255)) g := uint8(gobot.Rand(255)) b := uint8(gobot.Rand(255)) bb8.SetRGB(r, g, b) }) } robot := gobot.NewRobot("bb", []gobot.Connection{bleAdaptor}, []gobot.Device{bb8}, work, ) robot.Start() }
  18. gobot.NewRobot() bleAdaptor := ble.NewClientAdaptor(os.Args[1]) robot := gobot.NewRobot("bb", []gobot.Connection{bleAdaptor}, []gobot.Device{bb8}, work,

    ) robot.Start()
  19. gobot.NewRobot() bb8 := bb8.NewDriver(bleAdaptor) robot := gobot.NewRobot("bb", []gobot.Connection{bleAdaptor}, []gobot.Device{bb8}, work,

    ) robot.Start()
  20. gobot.NewRobot() work := func() { gobot.Every(1*time.Second, func() { r :=

    uint8(gobot.Rand(255)) g := uint8(gobot.Rand(255)) b := uint8(gobot.Rand(255)) bb8.SetRGB(r, g, b) }) } robot := gobot.NewRobot("bb", []gobot.Connection{bleAdaptor}, []gobot.Device{bb8}, work, ) robot.Start()
  21. gobot.NewRobot() robot := gobot.NewRobot("bb", []gobot.Connection{bleAdaptor}, []gobot.Device{bb8}, work, ) robot.Start()

  22. Demo

  23. No R2-D2 Driver

  24. Where do we start?

  25. None
  26. Terminology • BLE - Bluetooth Low Energy ◦ Modern Bluetooth

    4.* spec • GATT - Generic Attribute ◦ Like a BLE plugin system
  27. What do we need to know?

  28. Services • Encapsulate the behavior of part of a device

    • Collection of data and associated behaviors
  29. Service API

  30. Characteristics • The API of a service • The “endpoints”

    to control a device
  31. None
  32. How do we find the services?

  33. BlueZ

  34. gatttool $ sudo hcitool lescan

  35. gatttool $ sudo hcitool lescan LE Scan ... DF:59:60:07:1F:3B D2-1F3B

  36. gatttool $ sudo hcitool lescan LE Scan ... DF:59:60:07:1F:3B D2-1F3B

    $ gatttool -b DF:59:60:07:1F:3B -I [DF:59:60:07:1F:3B][LE]> connect Attempting to connect to DF:59:60:07:1F:3B Connection successful
  37. gatttool $ sudo hcitool lescan LE Scan ... DF:59:60:07:1F:3B D2-1F3B

    $ gatttool -b DF:59:60:07:1F:3B -I [DF:59:60:07:1F:3B][LE]> connect Attempting to connect to DF:59:60:07:1F:3B Connection successful [DF:59:60:07:1F:3B][LE]> (gatttool:11398): GLib-WARNING **: Invalid file descriptor.
  38. Attempt #2

  39. Bluetooth Sniffing Process

  40. Sniffing: Over-the-air https://www.adafruit.com/product/2269

  41. Attempt #3

  42. Sniffing: Man-in-the-middle

  43. Sniffing: Man-in-the-middle

  44. Sniffing: Man-in-the-middle

  45. Sniffing: Man-in-the-middle

  46. Sniffing: Man-in-the-middle

  47. Sniffing: Man-in-the-middle https://github.com/DigitalSecurity/btlejuice BtleJuice Framework

  48. There is a better way!

  49. Sniffing: btsnoop (Android 8+)

  50. Sniffing: btsnoop (Android 8+)

  51. Sniffing: btsnoop (Android 8+)

  52. Sniffing: btsnoop (Android 8+)

  53. Sniffing: btsnoop (Android 8+)

  54. BLE Packet Sniffing (Android 8+)

  55. BLE Packet Sniffing (Android 8+) FS > data > misc

    > bluetooth > logs
  56. BLE Packet Sniffing (Android 8+)

  57. BLE Packet Sniffing (Android 8+)

  58. BLE Packet Sniffing (Android 8+) Service UUID

  59. BLE Packet Sniffing (Android 8+) Characteristic UUID

  60. BLE Packet Sniffing (Android 8+) Packet Value

  61. • Reset logfiles • Send a command from the app

    • Dump logfiles • Load and find command • Record value • Repeat • Repeat again • ...and again Extracting Commands
  62. Look for patterns Tripod - 8d:0a:17:0d:01:01:cf:d8 Bipod - 8d:0a:17:0d:02:02:cd:d8 Tripod

    - 8d:0a:17:0d:03:01:cd:d8 Bipod - 8d:0a:17:0d:04:02:cb:d8 Tripod - 8d:0a:17:0d:05:01:cb:d8
  63. Look for patterns Tripod - 8d:0a:17:0d:01:01:cf:d8 Bipod - 8d:0a:17:0d:02:02:cd:d8 Tripod

    - 8d:0a:17:0d:03:01:cd:d8 Bipod - 8d:0a:17:0d:04:02:cb:d8 Tripod - 8d:0a:17:0d:05:01:cb:d8 Same for all
  64. Look for patterns Tripod - 8d:0a:17:0d:01:01:cf:d8 Bipod - 8d:0a:17:0d:02:02:cd:d8 Tripod

    - 8d:0a:17:0d:03:01:cd:d8 Bipod - 8d:0a:17:0d:04:02:cb:d8 Tripod - 8d:0a:17:0d:05:01:cb:d8 Changing
  65. Look for patterns Tripod - 8d:0a:17:0d:01:01:cf:d8 Bipod - 8d:0a:17:0d:02:02:cd:d8 Tripod

    - 8d:0a:17:0d:03:01:cd:d8 Bipod - 8d:0a:17:0d:04:02:cb:d8 Tripod - 8d:0a:17:0d:05:01:cb:d8 Sequence
  66. Look for patterns Tripod - 8d:0a:17:0d:01:01:cf:d8 Bipod - 8d:0a:17:0d:02:02:cd:d8 Tripod

    - 8d:0a:17:0d:03:01:cd:d8 Bipod - 8d:0a:17:0d:04:02:cb:d8 Tripod - 8d:0a:17:0d:05:01:cb:d8 Command Value
  67. Look for patterns Tripod - 8d:0a:17:0d:01:01:cf:d8 Bipod - 8d:0a:17:0d:02:02:cd:d8 Tripod

    - 8d:0a:17:0d:03:01:cd:d8 Bipod - 8d:0a:17:0d:04:02:cb:d8 Tripod - 8d:0a:17:0d:05:01:cb:d8 Checksum ~((0x0a + 0x17 + 0x0d + 0x05 + 0x01) % 0xFF) = 0xcb
  68. R2D2 Driver package r2d2 type Driver struct { name string

    connection gobot.Connection seq uint8 mtx sync.Mutex collisionResponse []uint8 packetChannel chan *packet gobot.Eventer } func (b *Driver) Tripod() { log.Print("Tripod") b.packetChannel <- b.craftPacket(0x17, 0x0d, []uint8{0x01}) }
  69. func (b *Driver) Tripod() { log.Print("Tripod") b.packetChannel <- b.craftPacket(0x17, 0x0d,

    []uint8{0x01}) } func (b *Driver) craftPacket(did byte, cid byte, body []uint8) *packet { b.mtx.Lock() defer b.mtx.Unlock() packet := new(packet) packet.header = []uint8{0x8d, 0x0a, did, cid, b.seq} packet.body = body packet.checksum = b.calculateChecksum(packet) packet.footer = []uint8{0xd8} return packet } craftPacket 8d:0a:17:0d:01:01:cf:d8
  70. Packet Sending

  71. Is Go the only option?

  72. Ruby Native Extensions in Go

  73. Ruby concurrency limitations

  74. Packaging

  75. Embed mruby in Go

  76. Go has better concurrency primitives

  77. mruby & go have a packaging story

  78. mitchellh/go-mruby

  79. go-mruby Hello World func main() { mrb := mruby.NewMrb() defer

    mrb.Close() addFunc := func(m *mruby.Mrb, self *mruby.MrbValue) (mruby.Value, mruby.Value) { args := m.GetArgs() return mruby.Int(args[0].Fixnum() + args[1].Fixnum()), nil } class := mrb.DefineClass("Example", nil) class.DefineClassMethod("add", addFunc, mruby.ArgsReq(2)) result, err := mrb.LoadString(`Example.add(12, 30)`) if err != nil { panic(err.Error()) } fmt.Printf("Result: %s\n", result.String()) }
  80. go-mruby Hello World func main() { mrb := mruby.NewMrb() defer

    mrb.Close() addFunc := func(m *mruby.Mrb, self *mruby.MrbValue) (mruby.Value, mruby.Value) { args := m.GetArgs() return mruby.Int(args[0].Fixnum() + args[1].Fixnum()), nil } class := mrb.DefineClass("Example", nil) class.DefineClassMethod("add", addFunc, mruby.ArgsReq(2)) result, err := mrb.LoadString(`Example.add(12, 30)`) if err != nil { panic(err.Error()) } fmt.Printf("Result: %s\n", result.String()) }
  81. go-mruby Hello World func main() { mrb := mruby.NewMrb() defer

    mrb.Close() addFunc := func(m *mruby.Mrb, self *mruby.MrbValue) (mruby.Value, mruby.Value) { args := m.GetArgs() return mruby.Int(args[0].Fixnum() + args[1].Fixnum()), nil } class := mrb.DefineClass("Example", nil) class.DefineClassMethod("add", addFunc, mruby.ArgsReq(2)) result, err := mrb.LoadString(`Example.add(12, 30)`) if err != nil { panic(err.Error()) } fmt.Printf("Result: %s\n", result.String()) }
  82. go-mruby Hello World func main() { mrb := mruby.NewMrb() defer

    mrb.Close() addFunc := func(m *mruby.Mrb, self *mruby.MrbValue) (mruby.Value, mruby.Value) { args := m.GetArgs() return mruby.Int(args[0].Fixnum() + args[1].Fixnum()), nil } class := mrb.DefineClass("Example", nil) class.DefineClassMethod("add", addFunc, mruby.ArgsReq(2)) result, err := mrb.LoadString(`Example.add(12, 30)`) if err != nil { panic(err.Error()) } fmt.Printf("Result: %s\n", result.String()) }
  83. go-mruby Hello World func main() { mrb := mruby.NewMrb() defer

    mrb.Close() addFunc := func(m *mruby.Mrb, self *mruby.MrbValue) (mruby.Value, mruby.Value) { args := m.GetArgs() return mruby.Int(args[0].Fixnum() + args[1].Fixnum()), nil } class := mrb.DefineClass("Example", nil) class.DefineClassMethod("add", addFunc, mruby.ArgsReq(2)) result, err := mrb.LoadString(`Example.add(12, 30)`) if err != nil { panic(err.Error()) } fmt.Printf("Result: %s\n", result.String()) }
  84. go-mruby Hello World func main() { mrb := mruby.NewMrb() defer

    mrb.Close() addFunc := func(m *mruby.Mrb, self *mruby.MrbValue) (mruby.Value, mruby.Value) { args := m.GetArgs() return mruby.Int(args[0].Fixnum() + args[1].Fixnum()), nil } class := mrb.DefineClass("Example", nil) class.DefineClassMethod("add", addFunc, mruby.ArgsReq(2)) result, err := mrb.LoadString(`Example.add(12, 30)`) if err != nil { panic(err.Error()) } fmt.Printf("Result: %s\n", result.String()) }
  85. go-mruby Hello World func main() { mrb := mruby.NewMrb() defer

    mrb.Close() addFunc := func(m *mruby.Mrb, self *mruby.MrbValue) (mruby.Value, mruby.Value) { args := m.GetArgs() return mruby.Int(args[0].Fixnum() + args[1].Fixnum()), nil } class := mrb.DefineClass("Example", nil) class.DefineClassMethod("add", addFunc, mruby.ArgsReq(2)) result, err := mrb.LoadString(`Example.add(12, 30)`) if err != nil { panic(err.Error()) } fmt.Printf("Result: %s\n", result.String()) }
  86. go-mruby Hello World func main() { mrb := mruby.NewMrb() defer

    mrb.Close() addFunc := func(m *mruby.Mrb, self *mruby.MrbValue) (mruby.Value, mruby.Value) { args := m.GetArgs() return mruby.Int(args[0].Fixnum() + args[1].Fixnum()), nil } class := mrb.DefineClass("Example", nil) class.DefineClassMethod("add", addFunc, mruby.ArgsReq(2)) result, err := mrb.LoadString(`Example.add(12, 30)`) if err != nil { panic(err.Error()) } fmt.Printf("Result: %s\n", result.String()) }
  87. go-mruby Hello World func main() { mrb := mruby.NewMrb() defer

    mrb.Close() addFunc := func(m *mruby.Mrb, self *mruby.MrbValue) (mruby.Value, mruby.Value) { args := m.GetArgs() return mruby.Int(args[0].Fixnum() + args[1].Fixnum()), nil } class := mrb.DefineClass("Example", nil) class.DefineClassMethod("add", addFunc, mruby.ArgsReq(2)) result, err := mrb.LoadString(`Example.add(12, 30)`) if err != nil { panic(err.Error()) } fmt.Printf("Result: %s\n", result.String()) }
  88. 1. go-mruby: Builds libmruby.a Build Process

  89. 1. go-mruby: Builds libmruby.a - MRUBY_CONFIG allows us to specify

    our own build_config.rb Build Process MRuby::Build.new do |conf| toolchain :gcc conf.gem :git => 'https://github.com/matsumoto-r/mruby-sleep.git' conf.gem :core => 'mruby-random' conf.gembox 'default' end
  90. 1. go-mruby: Builds libmruby.a - MRUBY_CONFIG allows us to specify

    our own build_config.rb - vendor mruby: clones mruby from github - build mruby - copy libmruby.a out Build Process
  91. 1. go-mruby: Builds libmruby.a - MRUBY_CONFIG allows us to specify

    our own build_config.rb - vendor mruby: clones mruby from github - build mruby - copy libmruby.a out 2. `go build` links against libmruby.a using Cgo Build Process // #cgo CFLAGS: -Ivendor/mruby/include // #cgo LDFLAGS: ${SRCDIR}/libmruby.a -lm import "C"
  92. DeeToo

  93. Vision • Only need to write Ruby • Extensible (leverage

    existing work) • Coordinate multiple async tasks • Utilize Actor model ◦ Droids as actors ◦ Droid components as actors • Actors pass messages to communicate intents
  94. r2d2_dome.mrb droid = R2D2.new("D2-1F3B") heading = 0; direction = -1

    while true do droid.dome(heading) direction = -1 if heading >= 180 direction = 1 if heading <= -160 heading = heading + direction Sleep::usleep(100) end
  95. r2d2_dome.mrb droid = R2D2.new("D2-1F3B") heading = 0; direction = -1

    while true do droid.dome(heading) direction = -1 if heading >= 180 direction = 1 if heading <= -160 heading = heading + direction Sleep::usleep(100) end
  96. r2d2_dome.mrb droid = R2D2.new("D2-1F3B") heading = 0; direction = -1

    while true do droid.dome(heading) direction = -1 if heading >= 180 direction = 1 if heading <= -160 heading = heading + direction Sleep::usleep(100) end
  97. R2D2 Dome Demo $ ./deetoo examples/r2d2_dome.mrb

  98. Setup mruby

  99. startWorker()

  100. startWorker() func main() { var wg sync.WaitGroup table := NewDroidTable()

    argsWithoutProg := os.Args[1:] for _, mrbFile := range argsWithoutProg { startWorker(mrbFile, table, &wg) } c := make(chan os.Signal, 1) signal.Notify(c, os.Interrupt, syscall.SIGTERM) go func() { <-c for _ = range argsWithoutProg { wg.Done() } }() wg.Wait() }
  101. startWorker() func main() { var wg sync.WaitGroup table := NewDroidTable()

    argsWithoutProg := os.Args[1:] for _, mrbFile := range argsWithoutProg { startWorker(mrbFile, table, &wg) } c := make(chan os.Signal, 1) signal.Notify(c, os.Interrupt, syscall.SIGTERM) go func() { <-c for _ = range argsWithoutProg { wg.Done() } }() wg.Wait() }
  102. startWorker() func main() { var wg sync.WaitGroup table := NewDroidTable()

    argsWithoutProg := os.Args[1:] for _, mrbFile := range argsWithoutProg { startWorker(mrbFile, table, &wg) } c := make(chan os.Signal, 1) signal.Notify(c, os.Interrupt, syscall.SIGTERM) go func() { <-c for _ = range argsWithoutProg { wg.Done() } }() wg.Wait() }
  103. startWorker() func startWorker(file string, table droidTable, wg *sync.WaitGroup) { wg.Add(1)

    go func() { mrb := mruby.NewMrb() defer mrb.Close() droids.NewR2D2(table.r2d2, mrb) droids.NewBB8(table.bb8, mrb) dat, err := ioutil.ReadFile(file) content := string(dat) result, err := mrb.LoadString(content) fmt.Printf("Done: %s\n", result) }() }
  104. startWorker() func startWorker(file string, table droidTable, wg *sync.WaitGroup) { wg.Add(1)

    go func() { mrb := mruby.NewMrb() defer mrb.Close() droids.NewR2D2(table.r2d2, mrb) droids.NewBB8(table.bb8, mrb) dat, err := ioutil.ReadFile(file) content := string(dat) result, err := mrb.LoadString(content) fmt.Printf("Done: %s\n", result) }() }
  105. startWorker() func startWorker(file string, table droidTable, wg *sync.WaitGroup) { wg.Add(1)

    go func() { mrb := mruby.NewMrb() defer mrb.Close() droids.NewR2D2(table.r2d2, mrb) droids.NewBB8(table.bb8, mrb) dat, err := ioutil.ReadFile(file) content := string(dat) result, err := mrb.LoadString(content) fmt.Printf("Done: %s\n", result) }() }
  106. startWorker() func startWorker(file string, table droidTable, wg *sync.WaitGroup) { wg.Add(1)

    go func() { mrb := mruby.NewMrb() defer mrb.Close() droids.NewR2D2(table.r2d2, mrb) droids.NewBB8(table.bb8, mrb) dat, err := ioutil.ReadFile(file) content := string(dat) result, err := mrb.LoadString(content) fmt.Printf("Done: %s\n", result) }() }
  107. Define mruby Classes

  108. Define mruby Classes func startWorker(file string, table droidTable, wg *sync.WaitGroup)

    { wg.Add(1) go func() { mrb := mruby.NewMrb() defer mrb.Close() droids.NewR2D2(table.r2d2, mrb) droids.NewBB8(table.bb8, mrb) dat, err := ioutil.ReadFile(file) content := string(dat) result, err := mrb.LoadString(content) fmt.Printf("Done: %s\n", result) }() }
  109. Define mruby Classes func NewR2D2(table map[string]*gobotR2D2.Driver, mrb *mruby.Mrb) *r2d2 {

    droids := r2d2{ table: table, } class := mrb.DefineClass("R2D2", nil) _, err := mrb.LoadString(` class R2D2 attr_accessor :name end `) class.DefineMethod("initialize", droids.Initialize, mruby.ArgsReq(1)) class.DefineMethod("dome", droids.Dome, mruby.ArgsReq(1)) class.DefineMethod("tripod", droids.Tripod, mruby.ArgsReq(0)) class.DefineMethod("bipod", droids.Bipod, mruby.ArgsReq(0)) class.DefineMethod("macro", droids.Macro, mruby.ArgsReq(1)) class.DefineMethod("move", droids.Move, mruby.ArgsReq(2)) return &droids }
  110. Define mruby Classes func NewR2D2(table map[string]*gobotR2D2.Driver, mrb *mruby.Mrb) *r2d2 {

    droids := r2d2{ table: table, } class := mrb.DefineClass("R2D2", nil) _, err := mrb.LoadString(` class R2D2 attr_accessor :name end `) class.DefineMethod("initialize", droids.Initialize, mruby.ArgsReq(1)) class.DefineMethod("dome", droids.Dome, mruby.ArgsReq(1)) class.DefineMethod("tripod", droids.Tripod, mruby.ArgsReq(0)) class.DefineMethod("bipod", droids.Bipod, mruby.ArgsReq(0)) class.DefineMethod("macro", droids.Macro, mruby.ArgsReq(1)) class.DefineMethod("move", droids.Move, mruby.ArgsReq(2)) return &droids }
  111. R2D2.new

  112. startWorker func startWorker(file string, table droidTable, wg *sync.WaitGroup) { wg.Add(1)

    go func() { mrb := mruby.NewMrb() defer mrb.Close() droids.NewR2D2(table.r2d2, mrb) droids.NewBB8(table.bb8, mrb) dat, err := ioutil.ReadFile(file) content := string(dat) result, err := mrb.LoadString(content) fmt.Printf("Done: %s\n", result) }() }
  113. R2D2.new droid = R2D2.new("D2-1F3B") heading = 0 direction = -1

    while true do droid.dome(heading) direction = -1 if heading >= 180 direction = 1 if heading <= -160 heading = heading + direction Sleep::usleep(100) end
  114. R2D2.new func NewR2D2(table map[string]*gobotR2D2.Driver, mrb *mruby.Mrb) *r2d2 { droids :=

    r2d2{ table: table, } class := mrb.DefineClass("R2D2", nil) _, err := mrb.LoadString(` class R2D2 attr_accessor :name end `) class.DefineMethod("initialize", droids.Initialize, mruby.ArgsReq(1)) class.DefineMethod("dome", droids.Dome, mruby.ArgsReq(1)) class.DefineMethod("tripod", droids.Tripod, mruby.ArgsReq(0)) class.DefineMethod("bipod", droids.Bipod, mruby.ArgsReq(0)) class.DefineMethod("macro", droids.Macro, mruby.ArgsReq(1)) class.DefineMethod("move", droids.Move, mruby.ArgsReq(2)) return &droids }
  115. R2D2.new func (droids *r2d2) Initialize(mrb *mruby.Mrb, self *mruby.MrbValue) (mruby.Value, mruby.Value)

    { args := mrb.GetArgs() name := string(args[0].String()) bleAdaptor := ble.NewClientAdaptor(name) droidDriver := gobotR2D2.NewDriver(bleAdaptor) droids.table[name] = droidDriver self.Call("name=", args[0]) var wg sync.WaitGroup wg.Add(1) work := func() { wg.Done() } robot := gobot.NewRobot("R2D2", []gobot.Connection{bleAdaptor}, []gobot.Device{droidDriver}, work) go robot.Start() wg.Wait() return self, nil }
  116. R2D2.new func (droids *r2d2) Initialize(mrb *mruby.Mrb, self *mruby.MrbValue) (mruby.Value, mruby.Value)

    { args := mrb.GetArgs() name := string(args[0].String()) bleAdaptor := ble.NewClientAdaptor(name) droidDriver := gobotR2D2.NewDriver(bleAdaptor) droids.table[name] = droidDriver self.Call("name=", args[0]) var wg sync.WaitGroup wg.Add(1) work := func() { wg.Done() } robot := gobot.NewRobot("R2D2", []gobot.Connection{bleAdaptor}, []gobot.Device{droidDriver}, work) go robot.Start() wg.Wait() return self, nil }
  117. R2D2.new func (droids *r2d2) Initialize(mrb *mruby.Mrb, self *mruby.MrbValue) (mruby.Value, mruby.Value)

    { args := mrb.GetArgs() name := string(args[0].String()) bleAdaptor := ble.NewClientAdaptor(name) droidDriver := gobotR2D2.NewDriver(bleAdaptor) droids.table[name] = droidDriver self.Call("name=", args[0]) var wg sync.WaitGroup wg.Add(1) work := func() { wg.Done() } robot := gobot.NewRobot("R2D2", []gobot.Connection{bleAdaptor}, []gobot.Device{droidDriver}, work) go robot.Start() wg.Wait() return self, nil }
  118. dome() in mruby droid = R2D2.new("D2-1F3B") heading = 0; direction

    = -1 while true do droid.dome(heading) direction = -1 if heading >= 180 direction = 1 if heading <= -160 heading = heading + direction Sleep::usleep(100) end
  119. dome() Method Call

  120. dome() in mruby func NewR2D2(table map[string]*gobotR2D2.Driver, mrb *mruby.Mrb) *r2d2 {

    droids := r2d2{ table: table, } class := mrb.DefineClass("R2D2", nil) _, err := mrb.LoadString(` class R2D2 attr_accessor :name end `) class.DefineMethod("initialize", droids.Initialize, mruby.ArgsReq(1)) class.DefineMethod("dome", droids.Dome, mruby.ArgsReq(1)) class.DefineMethod("tripod", droids.Tripod, mruby.ArgsReq(0)) class.DefineMethod("bipod", droids.Bipod, mruby.ArgsReq(0)) class.DefineMethod("macro", droids.Macro, mruby.ArgsReq(1)) class.DefineMethod("move", droids.Move, mruby.ArgsReq(2)) return &droids }
  121. Dome() in DeeToo func (droids *r2d2) Dome(mrb *mruby.Mrb, self *mruby.MrbValue)

    (mruby.Value, mruby.Value) { args := mrb.GetArgs() heading := int16(args[0].Fixnum()) droids.driver(self).Dome(heading) return nil, nil }
  122. Dome() in DeeToo func (droids *r2d2) Dome(mrb *mruby.Mrb, self *mruby.MrbValue)

    (mruby.Value, mruby.Value) { args := mrb.GetArgs() heading := int16(args[0].Fixnum()) droids.driver(self).Dome(heading) return nil, nil }
  123. Dome() in gobot R2D2 Driver func (b *Driver) Dome(heading int16)

    { log.Printf("Dome - heading: %v", heading) encoded,_ := EncodeSixShifted(heading) b.packetChannel <- b.craftPacket(0x17, 0x0f, append(encoded, 0x00, 0x00)) }
  124. Managing Droid Connections

  125. Droid Table func main() { var wg sync.WaitGroup table :=

    NewDroidTable() argsWithoutProg := os.Args[1:] for _, mrbFile := range argsWithoutProg { startWorker(mrbFile, table, &wg) } c := make(chan os.Signal, 1) signal.Notify(c, os.Interrupt, syscall.SIGTERM) go func() { <-c for _ = range argsWithoutProg { wg.Done() } }() wg.Wait() }
  126. Droid Table func main() { var wg sync.WaitGroup table :=

    NewDroidTable() argsWithoutProg := os.Args[1:] for _, mrbFile := range argsWithoutProg { startWorker(mrbFile, table, &wg) } c := make(chan os.Signal, 1) signal.Notify(c, os.Interrupt, syscall.SIGTERM) go func() { <-c for _ = range argsWithoutProg { wg.Done() } }() wg.Wait() }
  127. Droid Table type droidTable struct { bb8 map[string]*bb8.BB8Driver r2d2 map[string]*r2d2.Driver

    } func NewDroidTable() droidTable { table := droidTable{ bb8: make(map[string]*bb8.BB8Driver), r2d2: make(map[string]*r2d2.Driver), } return table }
  128. Droid Table func main() { var wg sync.WaitGroup table :=

    NewDroidTable() argsWithoutProg := os.Args[1:] for _, mrbFile := range argsWithoutProg { startWorker(mrbFile, table, &wg) } c := make(chan os.Signal, 1) signal.Notify(c, os.Interrupt, syscall.SIGTERM) go func() { <-c for _ = range argsWithoutProg { wg.Done() } }() wg.Wait() }
  129. Droid Table func startWorker(file string, table droidTable, wg *sync.WaitGroup) {

    wg.Add(1) go func() { mrb := mruby.NewMrb() defer mrb.Close() droids.NewR2D2(table.r2d2, mrb) droids.NewBB8(table.bb8, mrb) dat, err := ioutil.ReadFile(file) content := string(dat) result, err := mrb.LoadString(content) fmt.Printf("Done: %s\n", result) }() }
  130. Droid Table func (droids *r2d2) Initialize(mrb *mruby.Mrb, self *mruby.MrbValue) (mruby.Value,

    mruby.Value) { args := mrb.GetArgs() name := string(args[0].String()) bleAdaptor := ble.NewClientAdaptor(name) droidDriver := gobotR2D2.NewDriver(bleAdaptor) droids.table[name] = droidDriver self.Call("name=", args[0]) var wg sync.WaitGroup wg.Add(1) work := func() { wg.Done() } robot := gobot.NewRobot("R2D2", []gobot.Connection{bleAdaptor}, []gobot.Device{droidDriver}, work) go robot.Start() wg.Wait() return self, nil }
  131. Droid Table func NewR2D2(table map[string]*gobotR2D2.Driver, mrb *mruby.Mrb) *r2d2 { droids

    := r2d2{ table: table, } class := mrb.DefineClass("R2D2", nil) _, err := mrb.LoadString(` class R2D2 attr_accessor :name end `) class.DefineMethod("initialize", droids.Initialize, mruby.ArgsReq(1)) class.DefineMethod("dome", droids.Dome, mruby.ArgsReq(1)) class.DefineMethod("tripod", droids.Tripod, mruby.ArgsReq(0)) class.DefineMethod("bipod", droids.Bipod, mruby.ArgsReq(0)) class.DefineMethod("macro", droids.Macro, mruby.ArgsReq(1)) class.DefineMethod("move", droids.Move, mruby.ArgsReq(2)) return &droids }
  132. Droid Table func (droids *r2d2) driver(mrbInstance *mruby.MrbValue) *gobotR2D2.Driver { value,

    err := mrbInstance.Call("name") var driver *gobotR2D2.Driver if err != nil { panic(err.Error()) } else { ident := string(value.String()) driver = droids.table[ident] } return driver }
  133. Droid Table func (droids *r2d2) driver(mrbInstance *mruby.MrbValue) *gobotR2D2.Driver { value,

    err := mrbInstance.Call("name") var driver *gobotR2D2.Driver if err != nil { panic(err.Error()) } else { ident := string(value.String()) driver = droids.table[ident] } return driver }
  134. Final Demo

  135. Future/Roadmap • Single binary by compiling the mruby byte code

    into libmruby.a • Cross compiling the deetoo tool • Message passing between mrb files
  136. Thank You https://github.com/code0100fun/deetoo