Slide 1

Slide 1 text

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...

Slide 2

Slide 2 text

A Droid’s™ Journey with mruby & Go

Slide 3

Slide 3 text

Chase McCarthy @code0100fun

Slide 4

Slide 4 text

No content

Slide 5

Slide 5 text

Terence Lee @hone02

Slide 6

Slide 6 text

No content

Slide 7

Slide 7 text

Austin, TX

Slide 8

Slide 8 text

Austin, TX

Slide 9

Slide 9 text

These are the droids you're looking for... August 2017 Sphero Announces R2-D2

Slide 10

Slide 10 text

Best R2-D2 replica toy

Slide 11

Slide 11 text

What can I do with this thing?

Slide 12

Slide 12 text

R2D2 + Ruby = ?

Slide 13

Slide 13 text

Ruby on Robots

Slide 14

Slide 14 text

● Sphero support* ● *Only Bluetooth Classic ● No support for BLE! ● No BB8! GitHub issue #165 Artoo

Slide 15

Slide 15 text

Gobot

Slide 16

Slide 16 text

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

Slide 17

Slide 17 text

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() }

Slide 18

Slide 18 text

gobot.NewRobot() bleAdaptor := ble.NewClientAdaptor(os.Args[1]) robot := gobot.NewRobot("bb", []gobot.Connection{bleAdaptor}, []gobot.Device{bb8}, work, ) robot.Start()

Slide 19

Slide 19 text

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

Slide 20

Slide 20 text

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()

Slide 21

Slide 21 text

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

Slide 22

Slide 22 text

Demo

Slide 23

Slide 23 text

No R2-D2 Driver

Slide 24

Slide 24 text

Where do we start?

Slide 25

Slide 25 text

No content

Slide 26

Slide 26 text

Terminology ● BLE - Bluetooth Low Energy ○ Modern Bluetooth 4.* spec ● GATT - Generic Attribute ○ Like a BLE plugin system

Slide 27

Slide 27 text

What do we need to know?

Slide 28

Slide 28 text

Services ● Encapsulate the behavior of part of a device ● Collection of data and associated behaviors

Slide 29

Slide 29 text

Service API

Slide 30

Slide 30 text

Characteristics ● The API of a service ● The “endpoints” to control a device

Slide 31

Slide 31 text

No content

Slide 32

Slide 32 text

How do we find the services?

Slide 33

Slide 33 text

BlueZ

Slide 34

Slide 34 text

gatttool $ sudo hcitool lescan

Slide 35

Slide 35 text

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

Slide 36

Slide 36 text

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

Slide 37

Slide 37 text

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.

Slide 38

Slide 38 text

Attempt #2

Slide 39

Slide 39 text

Bluetooth Sniffing Process

Slide 40

Slide 40 text

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

Slide 41

Slide 41 text

Attempt #3

Slide 42

Slide 42 text

Sniffing: Man-in-the-middle

Slide 43

Slide 43 text

Sniffing: Man-in-the-middle

Slide 44

Slide 44 text

Sniffing: Man-in-the-middle

Slide 45

Slide 45 text

Sniffing: Man-in-the-middle

Slide 46

Slide 46 text

Sniffing: Man-in-the-middle

Slide 47

Slide 47 text

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

Slide 48

Slide 48 text

There is a better way!

Slide 49

Slide 49 text

Sniffing: btsnoop (Android 8+)

Slide 50

Slide 50 text

Sniffing: btsnoop (Android 8+)

Slide 51

Slide 51 text

Sniffing: btsnoop (Android 8+)

Slide 52

Slide 52 text

Sniffing: btsnoop (Android 8+)

Slide 53

Slide 53 text

Sniffing: btsnoop (Android 8+)

Slide 54

Slide 54 text

BLE Packet Sniffing (Android 8+)

Slide 55

Slide 55 text

BLE Packet Sniffing (Android 8+) FS > data > misc > bluetooth > logs

Slide 56

Slide 56 text

BLE Packet Sniffing (Android 8+)

Slide 57

Slide 57 text

BLE Packet Sniffing (Android 8+)

Slide 58

Slide 58 text

BLE Packet Sniffing (Android 8+) Service UUID

Slide 59

Slide 59 text

BLE Packet Sniffing (Android 8+) Characteristic UUID

Slide 60

Slide 60 text

BLE Packet Sniffing (Android 8+) Packet Value

Slide 61

Slide 61 text

● Reset logfiles ● Send a command from the app ● Dump logfiles ● Load and find command ● Record value ● Repeat ● Repeat again ● ...and again Extracting Commands

Slide 62

Slide 62 text

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

Slide 63

Slide 63 text

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

Slide 64

Slide 64 text

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

Slide 65

Slide 65 text

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

Slide 66

Slide 66 text

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

Slide 67

Slide 67 text

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

Slide 68

Slide 68 text

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}) }

Slide 69

Slide 69 text

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

Slide 70

Slide 70 text

Packet Sending

Slide 71

Slide 71 text

Is Go the only option?

Slide 72

Slide 72 text

Ruby Native Extensions in Go

Slide 73

Slide 73 text

Ruby concurrency limitations

Slide 74

Slide 74 text

Packaging

Slide 75

Slide 75 text

Embed mruby in Go

Slide 76

Slide 76 text

Go has better concurrency primitives

Slide 77

Slide 77 text

mruby & go have a packaging story

Slide 78

Slide 78 text

mitchellh/go-mruby

Slide 79

Slide 79 text

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()) }

Slide 80

Slide 80 text

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()) }

Slide 81

Slide 81 text

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()) }

Slide 82

Slide 82 text

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()) }

Slide 83

Slide 83 text

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()) }

Slide 84

Slide 84 text

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()) }

Slide 85

Slide 85 text

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()) }

Slide 86

Slide 86 text

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()) }

Slide 87

Slide 87 text

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()) }

Slide 88

Slide 88 text

1. go-mruby: Builds libmruby.a Build Process

Slide 89

Slide 89 text

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

Slide 90

Slide 90 text

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

Slide 91

Slide 91 text

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"

Slide 92

Slide 92 text

DeeToo

Slide 93

Slide 93 text

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

Slide 94

Slide 94 text

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

Slide 95

Slide 95 text

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

Slide 96

Slide 96 text

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

Slide 97

Slide 97 text

R2D2 Dome Demo $ ./deetoo examples/r2d2_dome.mrb

Slide 98

Slide 98 text

Setup mruby

Slide 99

Slide 99 text

startWorker()

Slide 100

Slide 100 text

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() }

Slide 101

Slide 101 text

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() }

Slide 102

Slide 102 text

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() }

Slide 103

Slide 103 text

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) }() }

Slide 104

Slide 104 text

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) }() }

Slide 105

Slide 105 text

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) }() }

Slide 106

Slide 106 text

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) }() }

Slide 107

Slide 107 text

Define mruby Classes

Slide 108

Slide 108 text

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) }() }

Slide 109

Slide 109 text

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 }

Slide 110

Slide 110 text

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 }

Slide 111

Slide 111 text

R2D2.new

Slide 112

Slide 112 text

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) }() }

Slide 113

Slide 113 text

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

Slide 114

Slide 114 text

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 }

Slide 115

Slide 115 text

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 }

Slide 116

Slide 116 text

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 }

Slide 117

Slide 117 text

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 }

Slide 118

Slide 118 text

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

Slide 119

Slide 119 text

dome() Method Call

Slide 120

Slide 120 text

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 }

Slide 121

Slide 121 text

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 }

Slide 122

Slide 122 text

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 }

Slide 123

Slide 123 text

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)) }

Slide 124

Slide 124 text

Managing Droid Connections

Slide 125

Slide 125 text

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() }

Slide 126

Slide 126 text

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() }

Slide 127

Slide 127 text

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 }

Slide 128

Slide 128 text

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() }

Slide 129

Slide 129 text

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) }() }

Slide 130

Slide 130 text

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 }

Slide 131

Slide 131 text

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 }

Slide 132

Slide 132 text

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 }

Slide 133

Slide 133 text

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 }

Slide 134

Slide 134 text

Final Demo

Slide 135

Slide 135 text

Future/Roadmap ● Single binary by compiling the mruby byte code into libmruby.a ● Cross compiling the deetoo tool ● Message passing between mrb files

Slide 136

Slide 136 text

Thank You https://github.com/code0100fun/deetoo