first commit
This commit is contained in:
commit
a5a0434432
1126 changed files with 439481 additions and 0 deletions
27
Software/Ruby/README.md
Normal file
27
Software/Ruby/README.md
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
# Ruby support for GrovePi
|
||||
|
||||
## Introduction
|
||||
This ruby module adds support for using GrovePi with Ruby.
|
||||
|
||||
A set of examples can be found in the **tests** directory.
|
||||
Check them out for usage.
|
||||
|
||||
## Dependencies
|
||||
* Ruby version above 2.0 is required.
|
||||
* The Ruby module **i2c-devices** must be installed, ```# gem install i2c-devices```.
|
||||
|
||||
## Current State
|
||||
As the initial state of this module the following features are implemented and tested:
|
||||
|
||||
* Analog read on ports A0, A1 and A2.
|
||||
* Analog write with PWM on ports D3, D5 and D6.
|
||||
* Digital read/write on ports D2 through D8.
|
||||
* Read/write data to I2C slave devices present on ports I2C-1, I2C-2 or I2C-3.
|
||||
* Tested on Raspberry Pi 3, Model B and Raspberry Pi, Model B, Rev 2
|
||||
with Raspbian version: November 2017 and GrovePi+ with firmware version: 1.2.7.
|
||||
|
||||
## Todo
|
||||
|
||||
* Implement rest of the commands that are supported by the firmware.
|
||||
* Windows IoT core support.
|
||||
* More testing.
|
||||
270
Software/Ruby/grove_pi.rb
Normal file
270
Software/Ruby/grove_pi.rb
Normal file
|
|
@ -0,0 +1,270 @@
|
|||
#!/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
|
||||
19
Software/Ruby/tests/test_read_analog.rb
Executable file
19
Software/Ruby/tests/test_read_analog.rb
Executable file
|
|
@ -0,0 +1,19 @@
|
|||
#!/usr/bin/env ruby
|
||||
|
||||
require '../grove_pi'
|
||||
|
||||
firmware_version_bytes = GrovePi.read_firmware_version
|
||||
|
||||
print "Firmware: #{firmware_version_bytes[0]}.\
|
||||
#{firmware_version_bytes[1]}.\
|
||||
#{firmware_version_bytes[2]}
|
||||
Test : Read analog value from port A0
|
||||
========================================
|
||||
|
||||
"
|
||||
|
||||
while true
|
||||
analog_value = GrovePi.read_analog GrovePi::A0
|
||||
print "[+] Analog Value (Port A0) = #{analog_value}\n"
|
||||
sleep 0.5
|
||||
end
|
||||
19
Software/Ruby/tests/test_read_digital.rb
Executable file
19
Software/Ruby/tests/test_read_digital.rb
Executable file
|
|
@ -0,0 +1,19 @@
|
|||
#!/usr/bin/env ruby
|
||||
|
||||
require '../grove_pi'
|
||||
|
||||
firmware_version_bytes = GrovePi.read_firmware_version
|
||||
|
||||
print "Firmware: #{firmware_version_bytes[0]}.\
|
||||
#{firmware_version_bytes[1]}.\
|
||||
#{firmware_version_bytes[2]}
|
||||
Test : Read digital value from port D4
|
||||
=========================================
|
||||
|
||||
"
|
||||
|
||||
while true
|
||||
digital_value = GrovePi.read_digital GrovePi::D4
|
||||
print "[+] Digital input from port D4 = #{digital_value}\n"
|
||||
sleep 0.5
|
||||
end
|
||||
35
Software/Ruby/tests/test_write_analog.rb
Executable file
35
Software/Ruby/tests/test_write_analog.rb
Executable file
|
|
@ -0,0 +1,35 @@
|
|||
#!/usr/bin/env ruby
|
||||
|
||||
require '../grove_pi'
|
||||
|
||||
firmware_version_bytes = GrovePi.read_firmware_version
|
||||
|
||||
print "Firmware: #{firmware_version_bytes[0]}.\
|
||||
#{firmware_version_bytes[1]}.\
|
||||
#{firmware_version_bytes[2]}
|
||||
Test : Write analog value (with PWM) to port D5
|
||||
==================================================
|
||||
|
||||
"
|
||||
|
||||
while true
|
||||
puts '[+] Starting analog write sequence on port D5'
|
||||
|
||||
i = 0
|
||||
|
||||
while i <= 255
|
||||
puts i
|
||||
GrovePi.write_analog GrovePi::D5, i
|
||||
i += 4
|
||||
sleep 0.02
|
||||
end
|
||||
|
||||
i = 255
|
||||
|
||||
while i >= 1
|
||||
puts i
|
||||
GrovePi.write_analog GrovePi::D5, i
|
||||
i -= 4
|
||||
sleep 0.02
|
||||
end
|
||||
end
|
||||
26
Software/Ruby/tests/test_write_digital.rb
Executable file
26
Software/Ruby/tests/test_write_digital.rb
Executable file
|
|
@ -0,0 +1,26 @@
|
|||
#!/usr/bin/env ruby
|
||||
|
||||
require '../grove_pi'
|
||||
|
||||
firmware_version_bytes = GrovePi.read_firmware_version
|
||||
|
||||
print "Firmware: #{firmware_version_bytes[0]}.\
|
||||
#{firmware_version_bytes[1]}.\
|
||||
#{firmware_version_bytes[2]}
|
||||
Test : Write digital value to port D3
|
||||
========================================
|
||||
|
||||
"
|
||||
|
||||
while true
|
||||
GrovePi.write_digital GrovePi::D3, 1
|
||||
puts '[+] Tick: ON'
|
||||
sleep 1
|
||||
|
||||
GrovePi.write_digital GrovePi::D3, 0
|
||||
puts '[+] Tock: OFF'
|
||||
sleep 1
|
||||
|
||||
puts '[+] End of cycle'
|
||||
puts
|
||||
end
|
||||
Loading…
Add table
Add a link
Reference in a new issue