Slide 1

Slide 1 text

Create Rails IoT applications more easily @lctseng (Henry Tseng) RubyKaigi 2017 https://tamashii.io

Slide 2

Slide 2 text

What is Tamashii Projects built with Tamashii Demo video Technical Details Client side Server side Conclusion Outline

Slide 3

Slide 3 text

Henry Tseng Ruby/Rails Developer GitHub/Twitter: lctseng 5xRuby.tw Ruby community in Taiwan Who am I

Slide 4

Slide 4 text

No content

Slide 5

Slide 5 text

Why “Tamashii” 魂(たましい) The “Soul” of devices Ruby-based framework for easy integration between IoT devices and Rails Client: Raspberry PI Server: Rails Control devices with elegant Ruby code Rack-based Easy integration with Rails What is Tamashii

Slide 6

Slide 6 text

Features Hardware component management Event-based data exchange WebSocket networking Rails Integration What is Tamashii

Slide 7

Slide 7 text

Hardware component management RFID reader, motor, LCD display, buzzer High-level API Hide tedious device protocol What is Tamashii

Slide 8

Slide 8 text

High-level API Example Buzzer: play sounds What is Tamashii buzzer = Buzzer.new buzzer.beep(3) #=> Beep, Beep, Beep!

Slide 9

Slide 9 text

High-level API Example LCD Display: print text What is Tamashii display = Lcd.new display.print("RubyKaigi 2017!")

Slide 10

Slide 10 text

High-level API Example RFID reader: read card data SUICA, ICOCA What is Tamashii reader = RfidReader.new if data = reader.read puts "Card data: #{data}" end

Slide 11

Slide 11 text

Event-based data exchange Combine components instead of using individually Example: play a sound and show messages after reading the card What is Tamashii Event Beep! Event Event

Slide 12

Slide 12 text

Networking Client à Server: sensor data Server à Client: updates, commands What is Tamashii Sensor response LCD message Server Client Sensor data RFID card number Device commands Reboot, Updates

Slide 13

Slide 13 text

Networking with WebSocket Integration with web application High performance WebSocket (HTML5) Two-way communication (server push) Use the same port as HTTP Support TLS (just like HTTPS) Supported by modern web servers and frameworks (Rails) What is Tamashii

Slide 14

Slide 14 text

Rails integration Rack-based server WebSocket for connection Redis for storage Device management protocol Shared by client and server Data events: RFID card data Command events: reboot, play sound What is Tamashii

Slide 15

Slide 15 text

Rails integration Model-like API What is Tamashii device = Device.find_by(serial: "1234") device.beep(3) Query devices Encode Transfer Decode Process command

Slide 16

Slide 16 text

Device Management https://youtu.be/hH6u4sJx_L4?t=26s Demo Video

Slide 17

Slide 17 text

Check-in System for Conference Track the flow of attendees Deployed on RubyConf.tw 2016 17 devices 270 attendees Access Control System Remotely control the gate for entrance Not deployed yet Projects Built with Tamashii

Slide 18

Slide 18 text

Track the flow of attendees Which sessions they attended Which sponsors they visited Workflow Registration stage Check-in stage Projects Built with Tamashii Check-in System

Slide 19

Slide 19 text

Registration stage Give every attendee a RFID sticker (attach to their badge) Bind the attendee with their RFID sticker Projects Built with Tamashii Check-in System Register Attendee Card Number Count User A 11-22-33-44 0 User B User B 55-66-77-88 0 Registrar Device

Slide 20

Slide 20 text

Check-in stage Devices @ entrance and sponsor booths Check-in with their RFID identifier Server find attendees & record to database Projects Built with Tamashii Check-in System Attendee Card Number Count User A 11-22-33-44 0 User B 55-66-77-88 0 User B 55-66-77-88 1 Check-in User B Check-in Device

Slide 21

Slide 21 text

Check-in System https://youtu.be/hH6u4sJx_L4?t=54s Demo Video

Slide 22

Slide 22 text

Remotely control the gate for entrance Similar to Check-in System Registration stage (same) Check-in stage (different) Control the behavior on Check-in devices Instead of simply recording the data Allow or deny users Projects Built with Tamashii Access Control

Slide 23

Slide 23 text

Admin Workflow when check-in User request access Admin decide to allow or deny Projects Built with Tamashii Access Control Request User Gate Device Response

Slide 24

Slide 24 text

Access Control System https://youtu.be/hH6u4sJx_L4?t=1m36s Demo Video

Slide 25

Slide 25 text

Client side Device wrapper Concurrent model (event-based data exchange) Future-based asynchronized networking model Technical Details

Slide 26

Slide 26 text

Device Wrapper Hide all low-level hardware operations Example: LCD display (I2C interface) Control using gem 6 bytes of binary command for “A” Technical Details Client Side [0x49, 0x4d, 0x49, 0x19, 0x1d, 0x19].each do |byte| i2c_write_byte(byte) end # => "A" Model: LCM 1602 I2C i2c

Slide 27

Slide 27 text

Device Wrapper Focus on behavior Do not need to rewrite for different hardware model Technical Details Client Side module Lcd class Lcm1602I2C < Base def print(message) # Lots of tedious stuff end end end Lcd::Lcm1602I2C.new.print("A")

Slide 28

Slide 28 text

Device Wrapper Example: RFID card reader (for metro/subway) Taiwan: MIFARE Easy Card, iPass Japan: FeliCa SUICA, ICOCA Technical Details Client Side Model: MFRC522 Model: PN532

Slide 29

Slide 29 text

Device Wrapper Common operation: read the UID Technical Details Client Side module RfidReader class Base def read_uid raise NotImplementedError end end end module RfidReader class Mfrc522 < Base def read_uid # code for MIFARE end end end module RfidReader class Pn532 < Base def read_uid # code for FeliCa end end end

Slide 30

Slide 30 text

Device Wrapper MIFARE: Gem Implement from scratch FeliCa: Gem Developed by @tenderlove https://github.com/tenderlove/nfc Technical Details Client Side mfrc522 nfc

Slide 31

Slide 31 text

Concurrent Model Multi-thread Prevent program hangs from one component failure Early version is single-threaded Single large loop Technical Details Client Side

Slide 32

Slide 32 text

Concurrent Model Example: beep after read card Single thread approach Technical Details Client Side reader = RfidReader.new buzzer = Buzzer.new loop do if data = reader.read # non-blocking read buzzer.beep(3) # blocking operation else sleep 0.1 # save CPU end end Thread #1 New Card Beep!

Slide 33

Slide 33 text

What is the problem? One component stops working è whole program crashes! Technical Details Client Side reader = RfidReader.new buzzer = Buzzer.new # stops working loop do if data = reader.read buzzer.beep(3) # crash! else sleep 0.1 end end Thread #1 Wait for response… ? New Card

Slide 34

Slide 34 text

Concurrent Model Multi-thread Components have worker threads Event-based Communicate by events Technical Details Client Side

Slide 35

Slide 35 text

Concurrent Model Technical Details Client Side RFID Thread Event queue Master Thread Buzzer Thread Event queue Event Emit event Dispatch event Event Beep! New Card Emit event rfid_data Check registered components Register Register

Slide 36

Slide 36 text

Technical Details Client Side master = Master.new reader = RfidReader.new buzzer = Buzzer.new master.register(buzzer) master.register(reader) buzzer.on :rfid_data do |payload| self.beep(3) # run on another thread end loop do if data = reader.read reader.emit(Event.new(:rfid_data, data)) else sleep 0.1 end end RFID Thread Master Thread Buzzer Thread

Slide 37

Slide 37 text

Concurrent Model Good extensibility Only need to register more handler Example: add LCD display Technical Details Client Side display = Lcd.new master.register(display) display.on :rfid_data do |payload| self.print(payload) end

Slide 38

Slide 38 text

Concurrent Model Restart individual component Call clean-up method Restart worker thread Technical Details Client Side

Slide 39

Slide 39 text

Future-based asynchronized networking Cooperate with event-based model Request & response are no longer paired Server can discard the client request (no response) Server can push data to clients (no request) Technical Details Client Side

Slide 40

Slide 40 text

Future-based asynchronized networking Do not know when can receive response Future-based request: request with callback Technical Details Client Side Request Pool Request Type: RFID_DATA Payload: 11-22-33-44 Callback: beep_count = response[:auth] ? 1 : 3 emit(Event.new(:beep, beep_count)) Send request Recv response Response Type: RFID_DATA_RESPONSE Payload: { :auth => true }

Slide 41

Slide 41 text

Future-based asynchronized networking Future-based request Objects (gem ) Every request is a With callbacks and parameters Called when fulfilled Fulfilled è Received response Technical Details Client Side Future concurrent-ruby Future

Slide 42

Slide 42 text

Future-based asynchronized networking Request Pool Stores future-based requests Timeout: invalidate and call callback Technical Details Client Side Request Pool Pending Pending Pending Send Recv Response Send Expired Request Timeout Fulfilled

Slide 43

Slide 43 text

Future-based asynchronized networking Network worker thread Sending the request Dispatch response to request Emit events (from server command) WebSocket and IO handling gem gem Technical Details Client Side websocket-driver nio4r

Slide 44

Slide 44 text

nfc Client Architecture Overview Technical Details Client Side concurrent-ruby i2c nio4r mfrc522 websocket-driver pi_piper pi_piper

Slide 45

Slide 45 text

Server side WebSocket server with Redis Rails Hook Model Wrapping Technical Details

Slide 46

Slide 46 text

WebSocket server with Redis From ActionCable (Rails WebSocket) Why Redis Cross process Support multi-process servers Passenger, Puma Technical Details Server Side

Slide 47

Slide 47 text

WebSocket server with Redis Exchange messages between clients Redis publish/subscription Callback when receive messages Technical Details Server Side Redis Client #1 Client #3 Client #2 Subscribe Publish message: “Hello” subscribe('_tamashii_internal') do |on| on.message do |_, message| process_message(message) end end

Slide 48

Slide 48 text

Rails Hook Mount Tamashii in Initialize in Receive data from client devices Params: the device data User defined Tamashii controller In Do everything that is available in Rails environment Technical Details Server Side config/routes.rb config/initializers app/tamashii/*.rb

Slide 49

Slide 49 text

Technical Details Server Side class TamashiiRailsHook < Tamashii::Hook def call(request_packet) client = @env[:client] json = JSON.parse(request_packet.body) packet_id = json['id'] card_id = json['ev_body'] type = Tamashii::Type::RFID_RESPONSE_JSON body = { id: packet_id, ev_body: { auth: card_id == "1234" }.to_json }.to_json response_packet = Tamashii::Packet.new( type, client.tag, body ) client.send(response_packet.dump) true end end Extract data Create response Write to client

Slide 50

Slide 50 text

Model Wrapping Wrap whole device into model-like object Control devices with high-level API Send commands to devices Technical Details Server Side device = Device.find_by(serial: "1234") device.beep(3)

Slide 51

Slide 51 text

Special callbacks Like in ActiveRecord Technical Details Server Side on_connect on_auth_success on_closed before_save

Slide 52

Slide 52 text

Server Architecture Overview Technical Details Server Side concurrent-ruby rack websocket-driver nio4r redis Connection Storage Rails Integration

Slide 53

Slide 53 text

Conclusion Tamashii make it easy to Control devices with Ruby Integrate with Rails Future work Refactor & Optimize More different devices Conclusion

Slide 54

Slide 54 text

Buzzer SFM-27-W via PWM RFID Reader MFRC522 via SPI PN532 via UART LCD LCM 1602 via I2C Keyboard TTP229 via GPIO 4x4 Button Matrix via GPIO Supported Devices On Raspberry PI 3

Slide 55

Slide 55 text

Websocket server Websocket client Client module on Raspberry PI Check-in system based on Tamashii (Rails app) Repositories tamashii tamashii-client tamashii-agent tamashii-manager tamashii-common tamashii-checkin Server module based on Rack Shared library More information at https://tamashii.io

Slide 56

Slide 56 text

Thank You! https://tamashii.io