#!/usr/bin/env ruby module GrovePi require 'i2c' require 'i2c/driver/i2c-dev' # Commands. CMD_READ_DIGITAL = 1 CMD_WRITE_DIGITAL = 2 CMD_READ_ANALOG = 3 CMD_WRITE_ANALOG = 4 CMD_PIN_MODE = 5 CMD_READ_FIRMWARE_VERSION = 8 # Arduino pin mappings. A0 = 14 A1 = 15 A2 = 16 PINS_ANALOG = [A0, A1, A2] D2 = 2 D3 = 3 D4 = 4 D5 = 5 D6 = 6 D7 = 7 D8 = 8 PINS_DIGITAL = [D2, D3, D4, D5, D6, D7, D8] # Pin modes. PIN_MODE_IN = 0 PIN_MODE_OUT = 1 # Configuration settings. CONFIG_RETRIES = 10 GROVE_PI_I2C_SLAVE_ADDRESS = 4 # The initialized I2C object. @_i2c_grove_pi = nil # Storage for I2C slave addresses present on ports I2C-1, I2C-2 or I2C-3. @_i2c_slave_addresses = Hash.new # Internal functions. # # These functions are not intended to be used directly by the user but by the # functions which are exposed to the user. def self._grove_pi_discover() begin i2c_device_files = Dir['/dev/i2c-*'] if i2c_device_files.length < 1 return false, nil end # Iterate over I2C device files. for f in i2c_device_files device_file = f f = f.strip f.slice! '/dev/i2c-' lines = %x(#{'i2cdetect -y ' + Integer(f).to_s}).split("\n") # Get rid of the first line. lines.shift if lines.length < 1 next end # Process i2cdetect command output for the I2C device file. for i in 0..lines.length - 1 line = lines[i].strip line = line.split ':' if line.length != 2 next end for i2c_address in line[1].split(' ') begin if Integer(i2c_address) == GROVE_PI_I2C_SLAVE_ADDRESS return true, device_file.strip end rescue next end end end end rescue return false, nil end return false, nil end def self._grove_pi_init() status, i2c_device_file = self._grove_pi_discover if status && i2c_device_file != nil return I2CDevice.new address: GROVE_PI_I2C_SLAVE_ADDRESS, driver: I2CDevice::Driver::I2CDev.new(i2c_device_file) else return nil end end def self._ensure_init() if @_i2c_grove_pi == nil @_i2c_grove_pi = self._grove_pi_init if @_i2c_grove_pi == nil raise 'No GrovePi found.' end end end def self._set_pin_mode(pin, mode) self._ensure_init @_i2c_grove_pi.i2cset @_i2c_grove_pi.address, CMD_PIN_MODE, pin, mode, 0 end def self._read_analog(pin) self._ensure_init @_i2c_grove_pi.i2cset @_i2c_grove_pi.address, CMD_READ_ANALOG, pin, 0, 0 bytes = @_i2c_grove_pi.i2cget(@_i2c_grove_pi.address, 3).chars return (bytes[1].ord * 256) + bytes[2].ord end def self._write_analog(pin, value) self._ensure_init @_i2c_grove_pi.i2cset @_i2c_grove_pi.address, CMD_WRITE_ANALOG, pin, value, 0 end def self._read_digital(pin) self._ensure_init @_i2c_grove_pi.i2cset @_i2c_grove_pi.address, CMD_READ_DIGITAL, pin, 0, 0 return @_i2c_grove_pi.i2cget(@_i2c_grove_pi.address, 2).chars[0].ord end def self._write_digital(pin, value) self._ensure_init @_i2c_grove_pi.i2cset @_i2c_grove_pi.address, CMD_WRITE_DIGITAL, pin, value, 0 end # Functions exposed to the user. # Analog read functions. def self.read_analog(pin) if !PINS_ANALOG.include? pin raise 'Invalid analog pin.' end for i in 0..CONFIG_RETRIES - 1 begin self._set_pin_mode pin, PIN_MODE_IN return self._read_analog pin rescue Errno::EREMOTEIO next end end end # Analog write functions (PWM on digital pins D3, D5 and D6). def self.write_analog(pin, value) if !PINS_DIGITAL.include? pin raise 'Invalid analog pin. Note: PWM based analog write is applicable on digital ports.' end for i in 0..CONFIG_RETRIES - 1 begin self._set_pin_mode pin, PIN_MODE_OUT self._write_analog pin, value return rescue Errno::EREMOTEIO next end end end # Digital read function. def self.read_digital(pin) if !PINS_DIGITAL.include? pin raise 'Invalid digital pin.' end for i in 0..CONFIG_RETRIES - 1 begin self._set_pin_mode pin, PIN_MODE_IN return self._read_digital pin rescue Errno::EREMOTEIO next end end end # Digital write function. def self.write_digital(pin, value) if !PINS_DIGITAL.include? pin raise 'Invalid digital pin.' end for i in 0..CONFIG_RETRIES - 1 begin self._set_pin_mode pin, PIN_MODE_OUT self._write_digital pin, value return rescue Errno::EREMOTEIO next end end end # Functions for reading and writing I2C slaves connected to # I2C-1, I2C-2 or I2C-3 ports of the GrovePi. def self.read_grove_pi_i2c(i2c_slave_address, length) _ensure_init if !@_i2c_slave_addresses.key?(i2c_slave_address) path = @_i2c_grove_pi.instance_variable_get(:@driver) .instance_variable_get(:@path) @_i2c_slave_addresses[i2c_slave_address] = I2CDevice.new address: i2c_slave_address, driver: I2CDevice::Driver::I2CDev.new(path) end bytes = [] _bytes = @_i2c_slave_addresses[i2c_slave_address].i2cget(i2c_slave_address, length).chars for b in _bytes bytes << b.ord end return bytes end def self.write_grove_pi_i2c(i2c_slave_address, *data) _ensure_init if !@_i2c_slave_addresses.key?(i2c_slave_address) path = @_i2c_grove_pi.instance_variable_get(:@driver) .instance_variable_get(:@path) @_i2c_slave_addresses[i2c_slave_address] = I2CDevice.new address: i2c_slave_address, driver: I2CDevice::Driver::I2CDev.new(path) end @_i2c_slave_addresses[i2c_slave_address].i2cset i2c_slave_address, *data end # Miscellaneous functions. def self.read_firmware_version() for i in 0..CONFIG_RETRIES - 1 begin self._ensure_init @_i2c_grove_pi.i2cset @_i2c_grove_pi.address, CMD_READ_FIRMWARE_VERSION, 0, 0, 0 bytes = @_i2c_grove_pi.i2cget(@_i2c_grove_pi.address, 4).chars return [bytes[1].ord, bytes[2].ord, bytes[3].ord] rescue Errno::EREMOTEIO next end end end # TODO: Implement rest of the commands that are supported by the firmware. end