pi_piper 2.0.beta.4 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +4 -0
  3. data/.rspec +3 -0
  4. data/.travis.yml +10 -0
  5. data/Gemfile +3 -3
  6. data/Gemfile.lock +39 -26
  7. data/LICENCE.md +23 -0
  8. data/README.md +53 -16
  9. data/Rakefile +4 -22
  10. data/examples/2_bit_counter/2_bit_counter.rb +21 -0
  11. data/examples/7-segment/7-segment.rb +37 -0
  12. data/examples/dsl_switch/dsl_switch.rb +15 -0
  13. data/examples/elro/README.md +75 -0
  14. data/examples/elro/docs/elro-dips.jpg +0 -0
  15. data/examples/elro/docs/elro-switch.jpg +0 -0
  16. data/examples/elro/docs/setup.jpg +0 -0
  17. data/examples/elro/docs/wireplan.jpg +0 -0
  18. data/examples/elro/docs/wrl10534.jpg +0 -0
  19. data/examples/elro/elro.rb +15 -0
  20. data/examples/elro/lib/elro_switch.rb +51 -0
  21. data/examples/elro/lib/elro_util.rb +64 -0
  22. data/examples/elro/spec/elro_spec.rb +35 -0
  23. data/examples/elro/spec/elro_util_spec.rb +51 -0
  24. data/examples/elro/spec/spec_helper.rb +6 -0
  25. data/examples/mcp3008/circuit.png +0 -0
  26. data/examples/mcp3008/mcp3008.rb +55 -0
  27. data/examples/mcp3008_spi/mcp3008_spi.rb +24 -0
  28. data/examples/morse_code/circuit.png +0 -0
  29. data/examples/morse_code/morse_code.rb +49 -0
  30. data/examples/simple_switch/circuit.png +0 -0
  31. data/examples/simple_switch/simple_switch.rb +10 -0
  32. data/lib/pi_piper.rb +5 -3
  33. data/lib/pi_piper/bcm2835.rb +84 -14
  34. data/lib/pi_piper/i2c.rb +0 -1
  35. data/lib/pi_piper/libbcm2835.so +0 -0
  36. data/lib/pi_piper/pin.rb +102 -63
  37. data/lib/pi_piper/pin_error.rb +3 -0
  38. data/lib/pi_piper/pin_values.rb +35 -0
  39. data/lib/pi_piper/platform.rb +5 -5
  40. data/lib/pi_piper/pwm.rb +95 -0
  41. data/lib/pi_piper/spi.rb +30 -45
  42. data/lib/pi_piper/stub_driver.rb +124 -0
  43. data/lib/pi_piper/version.rb +3 -0
  44. data/pi_piper.gemspec +24 -29
  45. data/spec/bcm2835_spec.rb +132 -0
  46. data/spec/i2c_spec.rb +62 -0
  47. data/spec/pin_spec.rb +140 -0
  48. data/spec/pwm_spec.rb +83 -0
  49. data/spec/spec_helper.rb +7 -0
  50. data/spec/spi_spec.rb +38 -0
  51. data/spec/stub_driver_spec.rb +140 -0
  52. metadata +100 -26
  53. data/Manifest +0 -14
  54. data/lib/pi_piper/libbcm2835.img +0 -0
@@ -0,0 +1,124 @@
1
+ # @description driver that can be used either with tests or with rails or other frameworks for development
2
+ require_relative 'pin_values'
3
+
4
+ module PiPiper
5
+
6
+ class NullLogger
7
+ def debug(*) end
8
+ end
9
+
10
+ module StubDriver
11
+ include PiPiper::PinValues
12
+ extend self
13
+
14
+ def new(options = {})
15
+ opts = {
16
+ :logger => NullLogger.new
17
+ }.merge(options)
18
+
19
+ @logger = opts[:logger]
20
+
21
+ @pins = {}
22
+
23
+ @spi = {data:[], chip_select:0,}
24
+
25
+ self
26
+ end
27
+ alias_method :reset, :new
28
+
29
+
30
+ def gpio_select_function(pin_number, alt_fun)
31
+ end
32
+
33
+ def pin_input(pin_number)
34
+ #@pins[pin_number] = { direction: :in }
35
+ pin(pin_number)[:direction] = :in
36
+ @logger.debug("Pin ##{pin_number} -> Input")
37
+ end
38
+
39
+ def pin_output(pin_number)
40
+ #@pins[pin_number] = { direction: :in }
41
+ pin(pin_number)[:direction] = :out
42
+ @logger.debug("Pin ##{pin_number} -> Output")
43
+ end
44
+
45
+ def pin_direction(pin_number)
46
+ pin(pin_number)[:direction] if @pins[pin_number]
47
+ end
48
+
49
+ def pin_set(pin_number, value)
50
+ pin(pin_number)[:value] = value
51
+ @logger.debug("Pin ##{pin_number} -> #{value}")
52
+ end
53
+
54
+ def pin_set_pud(pin_number, value)
55
+ pin(pin_number)[:pud] = value
56
+ @logger.debug("PinPUD ##{pin_number} -> #{value}")
57
+ end
58
+
59
+ def pwm_clock(clock_divider)
60
+ end
61
+
62
+ def pwm_mode(channel, mode, start)
63
+ end
64
+
65
+ def pwm_range(channel, range)
66
+ end
67
+
68
+ def pwm_data(channel, data)
69
+ end
70
+
71
+ def spidev_out(array)
72
+ @spi[:data] = array
73
+ @logger.debug("SPIDEV -> #{array.pack('C*')}")
74
+ end
75
+
76
+ def spi_begin
77
+ @logger.debug("SPI Begin")
78
+ @spi[:data] = []
79
+ end
80
+
81
+ def spi_transfer_bytes(data)
82
+ @logger.debug("SPI CS#{@spi[:chip_select]} <- #{data.to_s}")
83
+ @spi[:data] = Array(data)
84
+ end
85
+
86
+ def spi_chip_select(chip = nil)
87
+ chip = chip || @spi[:chip_select]
88
+ @logger.debug("SPI Chip Select = #{chip}")
89
+ @spi[:chip_select] = chip
90
+ end
91
+
92
+ def pin_read(pin_number)
93
+ val = pin(pin_number)[:value]
94
+ val ||= case pin(pin_number)[:pud]
95
+ when GPIO_PUD_UP then GPIO_HIGH
96
+ when GPIO_PUD_DOWN then GPIO_LOW
97
+ else nil
98
+ end
99
+ end
100
+
101
+ def release_pins
102
+ @pins.keys.each { |pin_number| release_pin(pin_number) }
103
+ end
104
+
105
+ def release_pin(pin_number)
106
+ @pins.delete(pin_number)
107
+ end
108
+
109
+ def method_missing(meth, *args, &block)
110
+ puts "Needs Implementation: StubDriver##{meth}"
111
+ end
112
+
113
+ private
114
+
115
+ def pin(pin_number)
116
+ @pins[pin_number] ||= {}
117
+ end
118
+
119
+ ## The following methods are only for testing and are not available on any platforms
120
+ def spi_data
121
+ @spi[:data]
122
+ end
123
+ end
124
+ end
@@ -0,0 +1,3 @@
1
+ module PiPiper
2
+ VERSION = '2.0.0'
3
+ end
@@ -1,35 +1,30 @@
1
- # -*- encoding: utf-8 -*-
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'pi_piper/version'
2
5
 
3
6
  Gem::Specification.new do |s|
4
- s.name = "pi_piper"
5
- s.version = "2.0.beta.4"
7
+ s.name = 'pi_piper'
8
+ s.version = PiPiper::VERSION
6
9
 
7
- s.required_rubygems_version = Gem::Requirement.new(">= 1.2") if s.respond_to? :required_rubygems_version=
8
- s.authors = ["Jason Whitehorn"]
9
- s.date = "2013-09-14"
10
- s.description = "Event driven Raspberry Pi GPIO library"
11
- s.email = "jason.whitehorn@gmail.com"
12
- s.extra_rdoc_files = ["README.md", "lib/pi_piper.rb", "lib/pi_piper/bcm2835.rb", "lib/pi_piper/frequency.rb", "lib/pi_piper/i2c.rb", "lib/pi_piper/libbcm2835.img", "lib/pi_piper/pin.rb", "lib/pi_piper/platform.rb", "lib/pi_piper/spi.rb"]
13
- s.files = ["Gemfile", "Gemfile.lock", "Manifest", "README.md", "Rakefile", "lib/pi_piper.rb", "lib/pi_piper/bcm2835.rb", "lib/pi_piper/frequency.rb", "lib/pi_piper/i2c.rb", "lib/pi_piper/libbcm2835.img", "lib/pi_piper/pin.rb", "lib/pi_piper/platform.rb", "lib/pi_piper/spi.rb", "pi_piper.gemspec"]
14
- s.homepage = "http://github.com/jwhitehorn/pi_piper"
15
- s.rdoc_options = ["--line-numbers", "--inline-source", "--title", "Pi_piper", "--main", "README.md"]
16
- s.require_paths = ["lib"]
17
- s.rubyforge_project = "pi_piper"
18
- s.rubygems_version = "2.0.0"
19
- s.summary = "Event driven Raspberry Pi GPIO library"
10
+ s.required_rubygems_version = Gem::Requirement.new('>= 2.0.0') if s.respond_to? :required_rubygems_version=
11
+ s.authors = ['Jason Whitehorn']
12
+ s.description = 'Event driven Raspberry Pi GPIO library'
13
+ s.email = 'jason.whitehorn@gmail.com'
14
+ s.extra_rdoc_files = ['README.md', 'lib/pi_piper/libbcm2835.so']
15
+ s.files = `git ls-files -z`.split("\x0")
16
+ s.homepage = 'http://github.com/jwhitehorn/pi_piper'
17
+ s.rdoc_options = ['--line-numbers', '--inline-source', '--title', 'Pi_piper', '--main', 'README.md']
18
+ s.require_paths = ['lib']
19
+ s.rubygems_version = '2.2.2'
20
+ s.summary = 'Event driven Raspberry Pi GPIO library'
21
+ s.licenses = ['BSD']
20
22
 
21
- if s.respond_to? :specification_version then
22
- s.specification_version = 4
23
+ s.specification_version = 4 if s.respond_to? :specification_version
23
24
 
24
- if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
25
- s.add_runtime_dependency(%q<ffi>, [">= 0"])
26
- s.add_runtime_dependency(%q<eventmachine>, ["= 1.0.3"])
27
- else
28
- s.add_dependency(%q<ffi>, [">= 0"])
29
- s.add_dependency(%q<eventmachine>, ["= 1.0.3"])
30
- end
31
- else
32
- s.add_dependency(%q<ffi>, [">= 0"])
33
- s.add_dependency(%q<eventmachine>, ["= 1.0.3"])
34
- end
25
+ s.add_runtime_dependency 'ffi', '>= 0'
26
+ s.add_runtime_dependency 'eventmachine', '= 1.0.9'
27
+ s.add_development_dependency 'rspec', '~> 3.0'
28
+ s.add_development_dependency 'mocha'
29
+ s.add_development_dependency 'simplecov'
35
30
  end
@@ -0,0 +1,132 @@
1
+ require 'spec_helper'
2
+
3
+
4
+ describe 'Bcm2835' do
5
+
6
+ before :context do
7
+ Platform.driver
8
+ end
9
+
10
+ after :context do
11
+ Bcm2835.exported_pins.delete_if { true }
12
+ end
13
+
14
+ # describe 'GPIO' do
15
+ let(:file_like_object) { double("file like object") }
16
+
17
+ before :example do
18
+ allow(File).to receive(:read).and_return("1")
19
+ allow(File).to receive(:write).and_return("1")
20
+ allow(File).to receive(:open).and_return(file_like_object)
21
+ end
22
+
23
+ it 'should export a given pin & add it to the list' do
24
+ expect(File).to receive(:write).with("/sys/class/gpio/export", 4)
25
+ expect(Bcm2835.exported_pins.include?(4)).not_to be true
26
+ Bcm2835.export(4)
27
+ expect(Bcm2835.exported_pins.include?(4)).to be true
28
+ end
29
+
30
+ it 'should unexport a given pin & removed it from the list' do
31
+ expect(File).to receive(:write).with("/sys/class/gpio/unexport", 4)
32
+
33
+ Bcm2835.export(4)
34
+ expect(Bcm2835.exported_pins.include?(4)).to be true
35
+ Bcm2835.unexport_pin(4)
36
+ expect(Bcm2835.exported_pins.include?(4)).not_to be true
37
+ end
38
+
39
+ it 'should unexport every exported pin' do
40
+ Bcm2835.export(4)
41
+ Bcm2835.export(18)
42
+ Bcm2835.export(27)
43
+ expect(Bcm2835.exported_pins).to eq [4,18,27]
44
+ Bcm2835.unexport_all
45
+ expect(Bcm2835.exported_pins).to eq []
46
+ end
47
+
48
+ it 'should tell if a pin is exported or not' do
49
+ Bcm2835.export(4)
50
+ expect(Bcm2835.exported?(4)).to be true
51
+ expect(Bcm2835.exported?(112)).to be false
52
+ end
53
+
54
+
55
+ xit 'should unexport every pin at exit of the program' do
56
+ pending 'Not yet very sure how to test that_exit hook !?'
57
+ Bcm2835.export(18)
58
+ Bcm2835.export(4)
59
+
60
+ at_exit {
61
+ expect(Bcm2835.exported_pins).to eq []
62
+ }
63
+ end
64
+ # end
65
+
66
+ # describe 'GPIO' do
67
+ # before(:all) do
68
+ # Bcm2835.export(5)
69
+ # end
70
+
71
+ it 'should set pin\'s direction' do
72
+ Bcm2835.export(5)
73
+ expect(File).to receive(:write).with("/sys/class/gpio/gpio5/direction", "in")
74
+ Bcm2835.pin_direction(5, 'in')
75
+ end
76
+
77
+ it 'should set pin as an input' do
78
+ expect(Bcm2835).to receive(:export).with(4)
79
+ expect(Bcm2835).to receive(:pin_direction).with(4, 'in')
80
+ Bcm2835.pin_input(4)
81
+ end
82
+
83
+ it 'should set pin as an output' do
84
+ expect(Bcm2835).to receive(:export).with(4)
85
+ expect(Bcm2835).to receive(:pin_direction).with(4, 'out')
86
+ Bcm2835.pin_output(4)
87
+ end
88
+
89
+ it 'should set pin value' do
90
+ Bcm2835.export(5)
91
+ expect(File).to receive(:write).with("/sys/class/gpio/gpio5/value", 1)
92
+ Bcm2835.pin_set(5, 1)
93
+ end
94
+
95
+ it 'should get pin value' do
96
+ Bcm2835.export(5)
97
+ expect(File).to receive(:read).with("/sys/class/gpio/gpio5/value")
98
+ Bcm2835.pin_read(5)
99
+ end
100
+
101
+ it 'should stop RW access to pin after unexport' do
102
+ Bcm2835.unexport_pin(4)
103
+ expect { Bcm2835.pin_read(4) }.to raise_error(PinError)
104
+ expect { Bcm2835.pin_set(4, 1) }.to raise_error(PinError)
105
+ expect { Bcm2835.pin_direction(4, 1) }.to raise_error(PinError)
106
+ end
107
+
108
+ it 'should set pin edge' do
109
+ Bcm2835.export(5)
110
+ expect(File).to receive(:write).with("/sys/class/gpio/gpio5/edge", :both)
111
+ Bcm2835.pin_set_edge(5, :both)
112
+ end
113
+
114
+ it 'should wait for an edge event to return' do
115
+ pending 'how could we test edge trigger ?!'
116
+ fail
117
+ end
118
+
119
+ # end
120
+
121
+ xdescribe 'SPI' do
122
+ it 'spidev_out(array)'
123
+ it 'spi_transfer_bytes(data)'
124
+ end
125
+
126
+ xdescribe 'I2C' do
127
+ it 'i2c_allowed_clocks'
128
+ it 'i2c_transfer_bytes(data)'
129
+ it 'i2c_read_bytes(bytes)'
130
+ end
131
+ end
132
+
@@ -0,0 +1,62 @@
1
+ require_relative 'spec_helper'
2
+
3
+ describe 'I2C' do
4
+
5
+ before(:example) do |example|
6
+ Platform.driver = StubDriver.new
7
+ end
8
+
9
+ describe 'clock setting' do
10
+ it 'should check driver settings' do
11
+ expect(Platform.driver).to receive(:i2c_allowed_clocks).and_return([100.kilohertz])
12
+ I2C.clock = 100.kilohertz
13
+ end
14
+
15
+ it 'should accept 100 kHz' do
16
+ expect(Platform.driver).to receive(:i2c_allowed_clocks).and_return([100.kilohertz])
17
+ expect(Platform.driver).to receive(:i2c_set_clock).with(100.kilohertz)
18
+ I2C.clock = 100.kilohertz
19
+ end
20
+
21
+ it 'should not accept 200 kHz' do
22
+ expect(Platform.driver).to receive(:i2c_allowed_clocks).and_return([100.kilohertz])
23
+ expect { I2C.clock = 200.kilohertz }.to raise_error(RuntimeError)
24
+ end
25
+ end
26
+
27
+ describe 'when in block' do
28
+ it 'should call i2c_begin' do
29
+ expect(Platform.driver).to receive(:i2c_begin)
30
+ I2C.begin {}
31
+ end
32
+
33
+ it 'should call i2c_end' do
34
+ expect(Platform.driver).to receive(:i2c_end)
35
+ I2C.begin {}
36
+ end
37
+
38
+ it 'should call i2c_end even after raise' do
39
+ expect(Platform.driver).to receive(:i2c_end)
40
+ begin
41
+ I2C.begin { raise 'OMG' }
42
+ rescue
43
+ end
44
+ end
45
+
46
+ describe 'write operation' do
47
+ it 'should set address' do
48
+ expect(Platform.driver).to receive(:i2c_set_address).with(4)
49
+ I2C.begin do
50
+ write to: 4, data: [1, 2, 3, 4]
51
+ end
52
+ end
53
+
54
+ it 'should pass data to driver' do
55
+ expect(Platform.driver).to receive(:i2c_transfer_bytes).with([1, 2, 3, 4])
56
+ I2C.begin do
57
+ write to: 4, data: [1, 2, 3, 4]
58
+ end
59
+ end
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,140 @@
1
+ require 'spec_helper'
2
+ include PiPiper
3
+
4
+ describe 'Pin' do
5
+
6
+ before(:example) do |example|
7
+ Platform.driver = StubDriver.new
8
+ end
9
+
10
+ context 'Basic Behaviour' do
11
+ it 'should export pin for input' do
12
+ expect(Platform.driver).to receive(:pin_input).with(4)
13
+ Pin.new pin: 4, direction: :in
14
+ end
15
+
16
+ it 'should export pin for output' do
17
+ expect(Platform.driver).to receive(:pin_output).with(4)
18
+ Pin.new pin: 4, direction: :out
19
+ end
20
+
21
+ it 'should read start value on construction' do
22
+ expect(Platform.driver).to receive(:pin_read).with(4).and_return(0)
23
+ Pin.new pin: 4, direction: :in
24
+ end
25
+
26
+ it 'should detect on?' do
27
+ expect(Platform.driver).to receive(:pin_read).with(4).and_return(1)
28
+ pin = Pin.new pin: 4, direction: :in
29
+ expect(pin.on?).to be(true)
30
+ end
31
+
32
+ it 'should detect off?' do
33
+ expect(Platform.driver).to receive(:pin_read).with(4).and_return(0)
34
+ pin = Pin.new pin: 4, direction: :in
35
+ expect(pin.off?).to be(true)
36
+ end
37
+
38
+ it 'should invert true' do
39
+ expect(Platform.driver).to receive(:pin_read).with(4).and_return(1)
40
+ pin = Pin.new pin: 4, direction: :in, invert: true
41
+ expect(pin.on?).to be(false)
42
+ end
43
+
44
+ it 'should invert true' do
45
+ expect(Platform.driver).to receive(:pin_read).with(4).and_return(0)
46
+ pin = Pin.new pin: 4, direction: :in, invert: true
47
+ expect(pin.off?).to be(false)
48
+ end
49
+
50
+ it 'should write high' do
51
+ expect(Platform.driver).to receive(:pin_set).with(4, 1)
52
+ pin = Pin.new pin: 4, direction: :out
53
+ pin.on
54
+ end
55
+
56
+ it 'should write low' do
57
+ expect(Platform.driver).to receive(:pin_set).with(4, 0)
58
+ pin = Pin.new pin: 4, direction: :out
59
+ pin.off
60
+ end
61
+
62
+ it 'should not write high on direction in' do
63
+ expect(Platform.driver).not_to receive(:pin_set)
64
+ pin = Pin.new pin: 4, direction: :in
65
+ pin.on
66
+ end
67
+
68
+ it 'should not write low on direction in' do
69
+ expect(Platform.driver).not_to receive(:pin_set)
70
+ pin = Pin.new pin: 4, direction: :in
71
+ pin.off
72
+ end
73
+
74
+ it 'should detect high to low change' do
75
+ value = 1
76
+ # begins low, then high, low, high, low...
77
+ allow(Platform.driver).to receive(:pin_read) { value ^= 1 }
78
+
79
+ pin = Pin.new pin: 4, direction: :in
80
+ expect(pin.off?).to be(true)
81
+ pin.read
82
+ expect(pin.off?).to be(false)
83
+ expect(pin.changed?).to be(true)
84
+ end
85
+
86
+ it 'should wait for change' do
87
+ expect(Platform.driver).to receive(:pin_wait_for).with(4, :both)
88
+ pin = Pin.new pin: 4, direction: :out, edge: :rising
89
+ pin.wait_for_change
90
+ end
91
+ end
92
+
93
+ describe 'Pull up/down/float' do
94
+ let!(:pin_up) do
95
+ Pin.new(pin: 17, direction: :in, pull: :up)
96
+ end
97
+ let!(:pin_down) do
98
+ Pin.new(pin: 17, direction: :in, pull: :down)
99
+ end
100
+ let!(:pin_off) do
101
+ Pin.new(pin: 17, direction: :in, pull: :off)
102
+ end
103
+ let!(:pin_float) do
104
+ Pin.new(pin: 17, direction: :in, pull: :float)
105
+ end
106
+
107
+ it 'should raise an error for invalid :pull values' do
108
+ expect { Pin.new(pin: 17, direction: :in, pull: :wth) }.to raise_error PinError
109
+ end
110
+
111
+ it 'should restrict allowed :pull values' do
112
+ expect(pin_up.pull?).to eq(:up)
113
+ expect(pin_down.pull?).to eq(:down)
114
+ expect(pin_off.pull?).to eq(:off)
115
+ expect(pin_float.pull?).to eq(:off)
116
+ end
117
+
118
+ it 'should not accept pulls when direction is :out' do
119
+ expect{ Pin.new(pin: 17, direction: :out, pull: :up) }.to raise_error PinError
120
+ expect{ Pin.new(pin: 17, direction: :out, pull: :down) }.to raise_error PinError
121
+ expect{ Pin.new(pin: 17, direction: :out, pull: :off) }.not_to raise_error
122
+ end
123
+
124
+ it 'should not allow pull! when direction is :out' do
125
+ p_out = Pin.new(pin: 17, direction: :out)
126
+ expect{ p_out.pull!(:up) }.to raise_error PinError
127
+ expect{ p_out.pull!(:down) }.to raise_error PinError
128
+ expect{ p_out.pull!(:off) }.not_to raise_error
129
+ end
130
+
131
+ it 'should allow subsequent pull resistor changes when direction is :in' do
132
+ expect(Platform.driver).to receive(:pin_set_pud).with(17, PinValues::GPIO_PUD_UP)
133
+ expect(Platform.driver).to receive(:pin_set_pud).with(17, PinValues::GPIO_PUD_DOWN)
134
+ expect(Platform.driver).to receive(:pin_set_pud).with(17, PinValues::GPIO_PUD_OFF)
135
+ pin_off.pull!(:up)
136
+ pin_off.pull!(:down)
137
+ pin_off.pull!(:off)
138
+ end
139
+ end
140
+ end