class FB::Arduino

Software abstraction layer for the Arduino’s serial interface. Translates Ruby method calls into serial commands.

Constants

Position

Attributes

commands[RW]
inbound_queue[RW]
inputs[RW]
logger[RW]
outbound_queue[RW]
serial_port[RW]
status[RW]

Public Class Methods

new(serial_port: DefaultSerialPort.new, logger: STDOUT) click to toggle source

Initialize and provide a serial object, as well as an IO object to send log messages to. Default SerialPort is DefaultSerialPort. Default logger is STDOUT

# File lib/arduino.rb, line 19
def initialize(serial_port: DefaultSerialPort.new, logger: STDOUT)
  @outbound_queue = [] # Pi -> Arduino Gcode
  @inbound_queue  = EM::Channel.new # Pi <- Arduino

  @serial_port = serial_port
  @logger      = logger
  @commands    = FB::OutgoingHandler.new(self)
  @inputs      = FB::IncomingHandler.new(self)
  @status      = FB::Status.new

  start_event_listeners
end

Public Instance Methods

can_execute?() click to toggle source
# File lib/arduino.rb, line 76
def can_execute? # If the device is ready and commands are in the queue
  @outbound_queue.any? && status.ready?
end
current_position() click to toggle source
# File lib/arduino.rb, line 61
def current_position
  Position.new(status[:X], status[:Y], status[:Z])
end
disconnect() click to toggle source

Handle loss of serial connection

# File lib/arduino.rb, line 56
def disconnect
  log "Connection to device lost"
  @onclose.call if @onclose
end
execute_now(gcode) click to toggle source
# File lib/arduino.rb, line 80
def execute_now(gcode)
  log "RPI MSG: #{gcode}"
  serial_port.puts gcode
  status[:last] = gcode.name
  status[:BUSY] = 1 # If not, pi will race arduino and "talk too fast"
end
log(message) click to toggle source

Log to screen/file/IO stream

# File lib/arduino.rb, line 33
def log(message)
  logger.puts(message)
end
maybe_execute_command() click to toggle source
# File lib/arduino.rb, line 69
def maybe_execute_command # This method smells procedural. Refactor?
  sleep 0.08 # Throttle CPU
  return unless can_execute?
  gcode = @outbound_queue.pop
  gcode.is_a?(FB::Gcode) ? execute_now(gcode) : reject_gcode(gcode)
end
next_cmd() click to toggle source
# File lib/arduino.rb, line 65
def next_cmd
  outbound_queue.first
end
onchange(&blk) click to toggle source
# File lib/arduino.rb, line 42
def onchange(&blk)
  @onchange = blk
end
onclose(&blk) click to toggle source
# File lib/arduino.rb, line 51
def onclose(&blk)
  @onclose = blk
end
onmessage(&blk) click to toggle source

Handle incoming text from arduino into pi

# File lib/arduino.rb, line 47
def onmessage(&blk)
  @onmessage = blk
end
reject_gcode(gcode) click to toggle source
# File lib/arduino.rb, line 87
def reject_gcode(gcode)
  log "Outbound messages must be GCode objects. Use of "\
      "#{gcode.class}:#{gcode.inspect} is not permitted."
end
start_event_listeners() click to toggle source
# File lib/arduino.rb, line 92
def start_event_listeners
  EM.tick_loop { maybe_execute_command }
  status.onchange { |diff| @onchange.call(diff) if @onchange }
  inbound_queue.subscribe do |gcodes|
    Array(gcodes).each do |gcode|
      parse_incoming(gcode)
      @onmessage.call(gcode) if @onmessage
    end
  end
end
write(gcode) click to toggle source

Send outgoing test to arduino from pi

# File lib/arduino.rb, line 38
def write(gcode)
  @outbound_queue.unshift gcode
end

Private Instance Methods

parse_incoming(gcode) click to toggle source

Highest priority method for processing incoming Gcode. Use for system level status changes.

# File lib/arduino.rb, line 107
def parse_incoming(gcode)
  inputs.execute(gcode)
end