pi_piper 2.0.beta.4 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
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