Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WIP] Change gem structure #3

Open
wants to merge 6 commits into
base: dev
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
language: ruby
cache: bundler
rvm:
- 1.9.3
- 2.1.0
Expand Down
16 changes: 12 additions & 4 deletions Gemfile.lock
Original file line number Diff line number Diff line change
@@ -1,19 +1,21 @@
PATH
remote: .
specs:
bcm2835 (0.1.0)
pi_piper-bcm2835 (0.1.0)
ffi
pi_piper
pi_piper (>= 2.0.0)

GEM
remote: https://rubygems.org/
specs:
coderay (1.1.0)
diff-lcs (1.2.5)
docile (1.1.5)
eventmachine (1.0.9)
ffi (1.9.10)
json (1.8.3)
method_source (0.8.2)
pi_piper (1.9.9)
pi_piper (2.0.0)
eventmachine (= 1.0.9)
ffi
pry (0.10.3)
Expand All @@ -34,17 +36,23 @@ GEM
diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.4.0)
rspec-support (3.4.1)
simplecov (0.11.2)
docile (~> 1.1.0)
json (~> 1.8)
simplecov-html (~> 0.10.0)
simplecov-html (0.10.0)
slop (3.6.0)

PLATFORMS
ruby

DEPENDENCIES
bcm2835!
bundler (~> 1.11)
pi_piper-bcm2835!
pry
rake (~> 10.0)
rspec (~> 3.0)
simplecov

BUNDLED WITH
1.11.2
135 changes: 0 additions & 135 deletions lib/bcm2835/driver.rb

This file was deleted.

14 changes: 12 additions & 2 deletions lib/pi_piper/bcm2835.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,15 @@
require "pi_piper/bcm2835/version"
require 'pi_piper/bcm2835/version'
require 'pi_piper/bcm2835/pin'
require 'pi_piper/bcm2835/spi'
require 'pi_piper/bcm2835/i2c'
require 'pi_piper/bcm2835/driver'

module PiPiper
autoload PiPiper::Bcm2835, "pi_piper/bcm2835/bcm2835"
module Bcm2835
class << self
def driver
@driver ||= PiPiper::Bcm2835::Driver.new
end
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it is a missunderstanding.
PiPiper.driver in my implementation is the only instance of any driver kind loaded at any moment.
I even though a moment to use the singleto module : http://ruby-doc.org/stdlib-1.9.3/libdoc/singleton/rdoc/Singleton.html

If we want to be able to have several drivers for several Object, we have to rethink the implementation. I don't think it is very useful, but we can discuss it.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah I thought we would do something like this in the user code:

require 'pi_piper'

require 'pi_piper-sysfs'
# or
require 'pi_piper-bcm2835'

PiPiper.driver = PiPiper::Sysfs.driver
# or
PiPiper.driver = PiPiper::Bcm2835.driver
# ... do stuff ...

And yes, only 1 driver would be loaded for PiPiper at any given moment.
Is that what you meant, or something different?

And then later on if you do

PiPiper.driver = PiPiper::Sysfs.driver # => raise 'driver already loaded'

Or we could allow driver changes but that would complicate things.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

in PiPiper I wrote :

  def driver=(klass) 
    if !klass.nil? && (klass <= PiPiper::Driver)
      @driver.close if @driver
      @driver = klass.new
    else
      raise ArgumentError, 'Supply a PiPiper::Driver subclass for driver'
    end
  end

It ensure the driver is properly closed before loading a new one. I thought it was enough, it free a bit of memory, and it lightened the at_exit hook.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And as you only need one driver to be loaded my implementation allow to only write :

require 'pi_piper'
require 'pi_piper-bcm2835'

it load the code and load the driver with PiPiper.driver = PiPiper::Bcm2835 in the Bcm2835 module/class

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah I see, I can certainly change my code so that it works like the above. It will load the driver when running require.

end
end
end
4 changes: 0 additions & 4 deletions lib/pi_piper/bcm2835/bcm2835.rb

This file was deleted.

48 changes: 48 additions & 0 deletions lib/pi_piper/bcm2835/driver.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
require 'ffi'
require 'pry'
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not useful in production, could be added for dev

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

absolutely, it was just a placeholder since this PR was a work in progress.


module PiPiper
module Bcm2835
# The Bcm2835 module is not intended to be directly called.
# It serves as an FFI library for PiPiper::SPI and PiPiper::I2C
class Driver
include PiPiper::Bcm2835::Pin
include PiPiper::Bcm2835::SPI
include PiPiper::Bcm2835::I2C
include FFI::Library

def initialize
ffi_lib File.expand_path('../../../../bin/libbcm2835.so', __FILE__)
setup_gpio
setup_pwm
setup_spi
setup_i2c
end

def setup_gpio
attach_function :init, :bcm2835_init, [], :uint8
attach_function :close, :bcm2835_close, [], :uint8

# Sets the Function Select register for the given pin, which configures the
# pin as Input, Output or one of the 6 alternate functions.
attach_function :gpio_select_function, :bcm2835_gpio_fsel, [:uint8, :uint8], :void
# attach_function :gpio_set, :bcm2835_gpio_set, [:uint8], :void
# attach_function :gpio_clear, :bcm2835_gpio_clr, [:uint8], :void
# attach_function :gpio_level, :bcm2835_gpio_lev, [:uint8], :uint8

# pin support...
attach_function :pin_set_pud, :bcm2835_gpio_set_pud, [:uint8, :uint8], :void
end

def setup_pwm
# PWM support...
attach_function :pwm_clock, :bcm2835_pwm_set_clock, [:uint32], :void
attach_function :pwm_mode, :bcm2835_pwm_set_mode, [:uint8, :uint8, :uint8], :void
attach_function :pwm_range, :bcm2835_pwm_set_range, [:uint8, :uint32], :void
attach_function :pwm_data, :bcm2835_pwm_set_data, [:uint8, :uint32], :void
end


end
end
end
42 changes: 42 additions & 0 deletions lib/pi_piper/bcm2835/i2c.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
require 'ffi'
require 'pi_piper/frequency'

module PiPiper
module Bcm2835
module I2C
I2C_REASON_OK = 0 # Success
I2C_REASON_ERROR_NACK = 1 # Received a NACK
I2C_REASON_ERROR_CLKT = 2 # Received Clock Stretch Timeout
I2C_REASON_ERROR_DATA = 3 # Not all data is sent / received

def setup_i2c
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll prefer the included(base) hook for these kind of setup

BTW even without your workaround with attach_function I can have very good coverage (for now 270 / 289 LOC (93.43%))

attach_function :i2c_begin, :bcm2835_i2c_begin, [], :void
attach_function :i2c_end, :bcm2835_i2c_end, [], :void
attach_function :i2c_write, :bcm2835_i2c_write, [:pointer, :uint], :uint8
attach_function :i2c_set_address,:bcm2835_i2c_setSlaveAddress, [:uint8], :void
attach_function :i2c_set_clock_divider, :bcm2835_i2c_setClockDivider, [:uint16], :void
attach_function :i2c_read, :bcm2835_i2c_read, [:pointer, :uint], :uint8
end

def i2c_allowed_clocks
[100.kilohertz,
399.3610.kilohertz,
1.666.megahertz,
1.689.megahertz]
end

def i2c_transfer_bytes(data)
data_out = FFI::MemoryPointer.new(data.count)
(0..data.count - 1).each{ |i| data_out.put_uint8(i, data[i]) }
i2c_write(data_out, data.count)
end

def i2c_read_bytes(bytes)
data_in = FFI::MemoryPointer.new(bytes)
i2c_read(data_in, bytes) #TODO reason codes

(0..bytes - 1).map { |i| data_in.get_uint8(i) }
end
end
end
end
61 changes: 61 additions & 0 deletions lib/pi_piper/bcm2835/pin.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
require 'set'

module PiPiper
module Bcm2835
module Pin
def pins
@pins ||= Set.new
end

def pin_input(pin)
export(pin)
pin_direction(pin, 'in')
end

def pin_set(pin, value)
File.write("/sys/class/gpio/gpio#{pin}/value", value)
end

def pin_output(pin)
export(pin)
pin_direction(pin, 'out')
end

def pin_read(pin)
raise ArgumentError, "Pin #{pin} is not exported" if unexported?(pin)
File.read("/sys/class/gpio/gpio#{pin}/value").to_i
end

def unexport(pin)
raise ArgumentError, "Pin #{pin} not exported" if unexported?(pin)
File.write('/sys/class/gpio/unexport', pin)
pins.delete(pin)
end

def unexport_all
pins.dup.each { |pin| unexport(pin) }
end

def exported?(pin)
pins.include?(pin)
end

def unexported?(pin)
!exported?(pin)
end

private

def export(pin)
raise ArgumentError, "Pin #{pin} already exported" if exported?(pin)
File.write('/sys/class/gpio/export', pin)
pins << pin
end

def pin_direction(pin, direction)
File.write("/sys/class/gpio/gpio#{pin}/direction", direction)
end

end
end
end
Loading