class Ksr10

An object representing a Velleman KSR10 USB robotic arm.

Constants

PRODUCT_IDS

USB product IDs of 0x0 and 0x1 have been seen in the wild so we look for either and take the first found when initializing a Ksr10 object.

VENDOR_ID

USB vendor ID.

VERSION

Gem version.

Public Class Methods

new() click to toggle source
# File lib/ksr10.rb, line 30
def initialize
  @usb = LIBUSB::Context.new
  PRODUCT_IDS.each do |product_id|
    @device = @usb.devices(idVendor: VENDOR_ID, idProduct: product_id).first
    break if @device
  end
  raise Error::DeviceNotFound unless @device
  # Check we can access the device. Libusb requires that #open_interface take
  # a block, so we cannot keep and reuse the returned handle.
  begin
    @device.open_interface(0) { |_| }
  rescue LIBUSB::ERROR_ACCESS
    raise Error::PermissionDenied
  end
  stop # initializes @state
end

Public Instance Methods

base(left_right_stop, duration_ms=0) click to toggle source

Rotate the base.

# File lib/ksr10.rb, line 48
def base(left_right_stop, duration_ms=0)
  set_state(left_right_stop, duration_ms, {left: {on: 9, off: 8}, right: {on: 8, off: 9}})
end
elbow(up_down_stop, duration_ms=0) click to toggle source

Action the elbow motor.

# File lib/ksr10.rb, line 58
def elbow(up_down_stop, duration_ms=0)
  set_state(up_down_stop, duration_ms, {up: {on: 20, off: 21}, down: {on: 21, off: 20}})
end
gripper(open_close_stop, duration_ms=0) click to toggle source

Action the gripper.

# File lib/ksr10.rb, line 68
def gripper(open_close_stop, duration_ms=0)
  set_state(open_close_stop, duration_ms, {open: {on: 17, off: 16}, close: {on: 16, off: 17}})
end
led(on_off, duration_ms=0) click to toggle source

Turn on or off the LED.

# File lib/ksr10.rb, line 73
def led(on_off, duration_ms=0)
  set_state(on_off, duration_ms, {on: {on: 0}, off: {off: 0}})
end
shoulder(up_down_stop, duration_ms=0) click to toggle source

Action the shoulder motor.

# File lib/ksr10.rb, line 53
def shoulder(up_down_stop, duration_ms=0)
  set_state(up_down_stop, duration_ms, {up: {on: 22, off: 23}, down: {on: 23, off: 22}})
end
stop(duration_ms=0) click to toggle source

Stop all actions and turn off the LED.

# File lib/ksr10.rb, line 78
def stop(duration_ms=0)
  @state = 0
  write_state!(duration_ms)
  self
end
wrist(up_down_stop, duration_ms=0) click to toggle source

Action the wrist motor.

# File lib/ksr10.rb, line 63
def wrist(up_down_stop, duration_ms=0)
  set_state(up_down_stop, duration_ms, {up: {on: 18, off: 19}, down: {on: 19, off: 18}})
end

Private Instance Methods

set_state(state, duration_ms, spec) click to toggle source

The Hash spec defines the bits to flip in the USB control write for each permitted state. Bits are numbered from LSB and are 0-indexed. Returns “self” so that calls may be chained in the style of Fowler's “fluent interface”.

# File lib/ksr10.rb, line 90
def set_state(state, duration_ms, spec)
  if state == :stop
    spec.each do |_, bit_flips|
      @state &= ~(1<<bit_flips[:off]) if bit_flips.has_key?(:off)
    end
  else
    bit_flips = spec[state]
    raise ArgumentError, "Invalid argument #{state.inspect} for #{caller_locations[0].label}" unless bit_flips
    # Flip off bits first to avoid an invalid state "go up AND down
    # simultaneously".
    @state &= ~(1<<bit_flips[:off]) if bit_flips.has_key?(:off)
    @state |= 1<<bit_flips[:on] if bit_flips.has_key?(:on)
  end
  write_state!(duration_ms)
  # If we received a duration then flip off the bits we just flipped on.
  if !duration_ms.zero? && bit_flips && bit_flips.has_key?(:on)
    @state &= ~(1<<bit_flips[:on])
    write_state! # return immediately, no duration_ms
  end
  self
end
write_state!(duration_ms=0) click to toggle source

Write the state stored in the instance variable to the device.

# File lib/ksr10.rb, line 113
def write_state!(duration_ms=0)
  data = [@state.to_s(16).rjust(6, '0')].pack('H*')
  @device.open_interface(0) do |handle|
    handle.control_transfer(bmRequestType: 0x40,
                            bRequest: 0x06,
                            wValue: 0x0100,
                            wIndex: 0x00,
                            dataOut: data)
  end
  sleep(duration_ms / 1000.0)
end