rpi_gpio 0.3.0 → 0.5.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.
- checksums.yaml +7 -0
- data/LICENSE +2 -3
- data/README.md +57 -9
- data/Rakefile +3 -1
- data/ext/rpi_gpio/extconf.rb +1 -3
- data/lib/rpi_gpio.rb +329 -0
- data/lib/rpi_gpio/rpi_gpio.so +0 -0
- metadata +33 -42
- data/Gemfile.lock +0 -36
- data/ext/rpi_gpio/c_gpio.c +0 -292
- data/ext/rpi_gpio/c_gpio.h +0 -55
- data/ext/rpi_gpio/common.c +0 -97
- data/ext/rpi_gpio/common.h +0 -47
- data/ext/rpi_gpio/cpuinfo.c +0 -220
- data/ext/rpi_gpio/cpuinfo.h +0 -40
- data/ext/rpi_gpio/event_gpio.c +0 -575
- data/ext/rpi_gpio/event_gpio.h +0 -40
- data/ext/rpi_gpio/rb_gpio.c +0 -419
- data/ext/rpi_gpio/rb_gpio.h +0 -48
- data/ext/rpi_gpio/rb_pwm.c +0 -141
- data/ext/rpi_gpio/rb_pwm.h +0 -41
- data/ext/rpi_gpio/rpi_gpio.c +0 -45
- data/ext/rpi_gpio/rpi_gpio.h +0 -28
- data/ext/rpi_gpio/soft_pwm.c +0 -218
- data/ext/rpi_gpio/soft_pwm.h +0 -32
- data/lib/rpi_gpio.so +0 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: c751d8fda46ee296e1df93542416ba668f7f6d9d05142f7647ce63310be42873
|
4
|
+
data.tar.gz: 471e4e0dc50ed6b265c9f9dd3ff01a4cb332ac288c8210b161c4bd69ee9cd4d1
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 00ef0c4360e5f9c41aec4cf7274249c7dae11fcadc06ad54e27b474a2ea95397fb6727a1a8360e8261fe88f3aa765d904739314da8bc5b1730cc8319c5db2042
|
7
|
+
data.tar.gz: 76a6df3e90bfc0ab0ee47030c1c18cb36098b981b72ac5ff855ca5e5abc424a9c5e7452a8e8f1c47507dc846db6ee694de6a49d06fe7bd10bae4529b7c98a9cf
|
data/LICENSE
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
The MIT License (MIT)
|
2
2
|
|
3
|
-
Copyright (c) 2014-
|
4
|
-
Copyright (c)
|
3
|
+
Copyright (c) 2014-2020 Nick Lowery
|
4
|
+
Copyright (c) 2012-2014 Ben Croston
|
5
5
|
|
6
6
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
7
7
|
of this software and associated documentation files (the "Software"), to deal
|
@@ -20,4 +20,3 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
20
20
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
21
21
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
22
22
|
SOFTWARE.
|
23
|
-
|
data/README.md
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
# rpi_gpio v0.
|
1
|
+
# rpi_gpio v0.5.0
|
2
2
|
|
3
3
|
Ruby conversion of [RPi.GPIO Python module](https://pypi.python.org/pypi/RPi.GPIO)
|
4
4
|
|
@@ -8,8 +8,9 @@ Manipulate your Raspberry Pi's GPIO pins from Ruby!
|
|
8
8
|
|
9
9
|
- Boolean input/output
|
10
10
|
- Software-driven PWM (written in C for speed)
|
11
|
+
- Event-driven input (blocking and non-blocking)
|
11
12
|
|
12
|
-
Up-to-date with RPi.GPIO Python module version 0.
|
13
|
+
Up-to-date with RPi.GPIO Python module version 0.7.0, so it works on all Raspberry Pi models!
|
13
14
|
|
14
15
|
## Sample Usage
|
15
16
|
|
@@ -38,9 +39,20 @@ RPi::GPIO.set_numbering :bcm
|
|
38
39
|
To receive input from a GPIO pin, you must first initialize it as an input pin:
|
39
40
|
```ruby
|
40
41
|
RPi::GPIO.setup PIN_NUM, :as => :input
|
42
|
+
# or
|
43
|
+
RPi::GPIO.setup [PIN1_NUM, PIN2_NUM, ...], :as => :input
|
41
44
|
```
|
42
45
|
The pin number will differ based on your selected numbering system and which pin you want to use.
|
43
46
|
|
47
|
+
You can use the additional hash argument `:pull` to apply a pull-up or pull-down resistor to the input pin like so:
|
48
|
+
```ruby
|
49
|
+
RPi::GPIO.setup PIN_NUM, :as => :input, :pull => :down
|
50
|
+
# or
|
51
|
+
RPi::GPIO.setup PIN_NUM, :as => :input, :pull => :up
|
52
|
+
# or (not necessary; :off is the default value)
|
53
|
+
RPi::GPIO.setup PIN_NUM, :as => :input, :pull => :off
|
54
|
+
```
|
55
|
+
|
44
56
|
Now you can use the calls
|
45
57
|
```ruby
|
46
58
|
RPi::GPIO.high? PIN_NUM
|
@@ -48,13 +60,40 @@ RPi::GPIO.low? PIN_NUM
|
|
48
60
|
```
|
49
61
|
to receive either `true` or `false`.
|
50
62
|
|
51
|
-
|
63
|
+
If you prefer to use a callback when a pin edge is detected, you can use the `watch` method:
|
52
64
|
```ruby
|
53
|
-
RPi::GPIO.
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
65
|
+
RPi::GPIO.watch PIN_NUM, :on => :rising do |pin, value| # :on supports :rising, :falling, and :both
|
66
|
+
...
|
67
|
+
end
|
68
|
+
```
|
69
|
+
|
70
|
+
`watch` also supports the optional `bounce_time` parameter found in the Python module to prevent duplicate events from firing:
|
71
|
+
```ruby
|
72
|
+
RPi::GPIO.watch PIN_NUM, :on => :falling, :bounce_time => 200 do |pin, value|
|
73
|
+
...
|
74
|
+
end
|
75
|
+
```
|
76
|
+
|
77
|
+
To stop watching a pin, use `stop_watching`:
|
78
|
+
```ruby
|
79
|
+
RPi::GPIO.stop_watching PIN_NUM
|
80
|
+
```
|
81
|
+
|
82
|
+
If you want to block execution until a pin edge is detected, there's `wait_for_edge`:
|
83
|
+
```ruby
|
84
|
+
puts 'Waiting to start...'
|
85
|
+
RPi::GPIO.wait_for_edge PIN_NUM, :rising # :rising, :falling, and :both are also supported here
|
86
|
+
puts 'Here we go!'
|
87
|
+
```
|
88
|
+
|
89
|
+
`wait_for_edge` accepts optional `bounce_time` and `timeout` arguments too:
|
90
|
+
```ruby
|
91
|
+
puts 'Waiting to start...'
|
92
|
+
value = RPi::GPIO.wait_for_edge PIN_NUM, :falling, :bounce_time => 200, :timeout => 5000
|
93
|
+
if value.nil? # nil is returned if the timeout is reached
|
94
|
+
print 'You took too long. '
|
95
|
+
end
|
96
|
+
puts 'Here we go!'
|
58
97
|
```
|
59
98
|
|
60
99
|
#### Output
|
@@ -62,6 +101,8 @@ RPi::GPIO.setup PIN_NUM, :as => :input, :pull => :off
|
|
62
101
|
To send output to a GPIO pin, you must first initialize it as an output pin:
|
63
102
|
```ruby
|
64
103
|
RPi::GPIO.setup PIN_NUM, :as => :output
|
104
|
+
# or
|
105
|
+
RPi::GPIO.setup [PIN1_NUM, PIN2_NUM, ...], :as => :output
|
65
106
|
```
|
66
107
|
Now you can use the calls
|
67
108
|
```ruby
|
@@ -70,6 +111,13 @@ RPi::GPIO.set_low PIN_NUM
|
|
70
111
|
```
|
71
112
|
to set the pin either high or low.
|
72
113
|
|
114
|
+
You can use the additional hash argument `:initialize` to set the pin's initial state like so:
|
115
|
+
```ruby
|
116
|
+
RPi::GPIO.setup PIN_NUM, :as => :output, :initialize => :high
|
117
|
+
# or
|
118
|
+
RPi::GPIO.setup PIN_NUM, :as => :output, :initialize => :low
|
119
|
+
```
|
120
|
+
|
73
121
|
#### PWM (pulse-width modulation)
|
74
122
|
|
75
123
|
Pulse-width modulation is a useful tool for controlling things like LED brightness or motor speed. To utilize PWM, first create a PWM object for an [output pin](#output).
|
@@ -132,7 +180,7 @@ to clean up all pins and to also reset the selected numbering mode.
|
|
132
180
|
|
133
181
|
Original Python code by Ben Croston modified for Ruby by Nick Lowery
|
134
182
|
|
135
|
-
Copyright (c) 2014-
|
183
|
+
Copyright (c) 2014-2020 [Nick Lowery](https://github.com/ClockVapor)
|
136
184
|
|
137
185
|
View LICENSE for full license.
|
138
186
|
|
data/Rakefile
CHANGED
data/ext/rpi_gpio/extconf.rb
CHANGED
data/lib/rpi_gpio.rb
ADDED
@@ -0,0 +1,329 @@
|
|
1
|
+
require 'rpi_gpio/rpi_gpio'
|
2
|
+
require 'epoll'
|
3
|
+
|
4
|
+
module RPi
|
5
|
+
module GPIO
|
6
|
+
def self.watch(channel, on:, bounce_time: nil, &block)
|
7
|
+
gpio = get_gpio_number(channel)
|
8
|
+
ensure_gpio_input(gpio)
|
9
|
+
validate_edge(on)
|
10
|
+
if bounce_time && bounce_time <= 0
|
11
|
+
raise ArgumentError, "`bounce_time` must be greater than 0; given #{bounce_time}"
|
12
|
+
end
|
13
|
+
add_edge_detect(gpio, on, bounce_time)
|
14
|
+
add_callback(gpio, &block)
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.stop_watching(channel)
|
18
|
+
gpio = get_gpio_number(channel)
|
19
|
+
ensure_gpio_input(gpio)
|
20
|
+
remove_edge_detect(gpio)
|
21
|
+
remove_callbacks(gpio)
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.wait_for_edge(channel, edge, bounce_time: nil, timeout: -1)
|
25
|
+
gpio = get_gpio_number(channel)
|
26
|
+
if callback_exists(gpio)
|
27
|
+
raise RuntimeError, "conflicting edge detection already enabled for GPIO #{gpio}"
|
28
|
+
end
|
29
|
+
|
30
|
+
ensure_gpio_input(gpio)
|
31
|
+
validate_edge(edge)
|
32
|
+
was_gpio_new = false
|
33
|
+
current_edge = get_event_edge(gpio)
|
34
|
+
if current_edge == edge
|
35
|
+
g = get_gpio(gpio)
|
36
|
+
if g.bounce_time && g.bounce_time != bounce_time
|
37
|
+
raise RuntimeError, "conflicting edge detection already enabled for GPIO #{gpio}"
|
38
|
+
end
|
39
|
+
elsif current_edge.nil?
|
40
|
+
was_gpio_new = true
|
41
|
+
g = new_gpio(gpio)
|
42
|
+
set_edge(gpio, edge)
|
43
|
+
g.edge = edge
|
44
|
+
g.bounce_time = bounce_time
|
45
|
+
else
|
46
|
+
g = get_gpio(gpio)
|
47
|
+
set_edge(gpio, edge)
|
48
|
+
g.edge = edge
|
49
|
+
g.bounce_time = bounce_time
|
50
|
+
g.initial_wait = 1
|
51
|
+
end
|
52
|
+
|
53
|
+
if @@epoll_blocking.nil?
|
54
|
+
@@epoll_blocking = Epoll.create
|
55
|
+
end
|
56
|
+
@@epoll_blocking.add(g.value_file, Epoll::PRI)
|
57
|
+
|
58
|
+
initial_edge = true
|
59
|
+
the_value = nil
|
60
|
+
timed_out = false
|
61
|
+
begin
|
62
|
+
while the_value.nil? && !timed_out do
|
63
|
+
events = @@epoll_blocking.wait(timeout)
|
64
|
+
if events.empty?
|
65
|
+
timed_out = true
|
66
|
+
end
|
67
|
+
events.each do |event|
|
68
|
+
if event.events & Epoll::PRI != 0
|
69
|
+
event.data.seek(0, IO::SEEK_SET)
|
70
|
+
value = event.data.read.chomp.to_i
|
71
|
+
if event.data == g.value_file
|
72
|
+
if initial_edge # ignore first epoll trigger
|
73
|
+
initial_edge = false
|
74
|
+
else
|
75
|
+
now = Time.now.to_f
|
76
|
+
if g.bounce_time.nil? || g.last_call == 0 || g.last_call > now ||
|
77
|
+
(now - g.last_call) * 1000 > g.bounce_time then
|
78
|
+
g.last_call = now
|
79
|
+
the_value = value
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
if the_value
|
88
|
+
return the_value
|
89
|
+
end
|
90
|
+
ensure
|
91
|
+
@@epoll_blocking.del(g.value_file)
|
92
|
+
if was_gpio_new
|
93
|
+
delete_gpio(gpio)
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
private
|
99
|
+
@@callbacks = []
|
100
|
+
@@gpios = []
|
101
|
+
@@epoll = nil
|
102
|
+
@@epoll_thread = nil
|
103
|
+
@@epoll_blocking = nil
|
104
|
+
|
105
|
+
def self.export(gpio)
|
106
|
+
unless File.exist?("/sys/class/gpio/gpio#{gpio}")
|
107
|
+
File.open("/sys/class/gpio/export", 'w') do |file|
|
108
|
+
file.write(gpio.to_s)
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
def self.unexport(gpio)
|
114
|
+
File.open("/sys/class/gpio/unexport", 'w') do |file|
|
115
|
+
file.write(gpio.to_s)
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
def self.set_direction(gpio, direction)
|
120
|
+
validate_direction(direction)
|
121
|
+
tries = 0
|
122
|
+
begin
|
123
|
+
File.open("/sys/class/gpio/gpio#{gpio}/direction", 'w') do |file|
|
124
|
+
file.write(direction.to_s)
|
125
|
+
end
|
126
|
+
rescue
|
127
|
+
raise if tries > 5
|
128
|
+
sleep 0.1
|
129
|
+
tries += 1
|
130
|
+
retry
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
def self.set_edge(gpio, edge)
|
135
|
+
validate_edge(edge)
|
136
|
+
tries = 0
|
137
|
+
begin
|
138
|
+
File.open("/sys/class/gpio/gpio#{gpio}/edge", 'w') do |file|
|
139
|
+
file.write(edge.to_s)
|
140
|
+
end
|
141
|
+
rescue
|
142
|
+
raise if tries > 5
|
143
|
+
sleep 0.1
|
144
|
+
tries += 1
|
145
|
+
retry
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
def self.open_value_file(gpio)
|
150
|
+
File.open("/sys/class/gpio/gpio#{gpio}/value", 'r')
|
151
|
+
end
|
152
|
+
|
153
|
+
def self.new_gpio(gpio)
|
154
|
+
g = GPIO.new
|
155
|
+
g.gpio = gpio
|
156
|
+
export(gpio)
|
157
|
+
g.exported = true
|
158
|
+
set_direction(gpio, :in)
|
159
|
+
begin
|
160
|
+
g.value_file = open_value_file(gpio)
|
161
|
+
rescue
|
162
|
+
unexport(gpio)
|
163
|
+
raise
|
164
|
+
end
|
165
|
+
g.initial_thread = true
|
166
|
+
g.initial_wait = true
|
167
|
+
g.bounce_time = nil
|
168
|
+
g.last_call = 0
|
169
|
+
g.thread_added = false
|
170
|
+
@@gpios << g
|
171
|
+
g
|
172
|
+
end
|
173
|
+
|
174
|
+
def self.delete_gpio(gpio)
|
175
|
+
@@gpios.delete_if { |g| g.gpio == gpio }
|
176
|
+
end
|
177
|
+
|
178
|
+
def self.get_gpio(gpio)
|
179
|
+
@@gpios.find { |g| g.gpio == gpio }
|
180
|
+
end
|
181
|
+
|
182
|
+
def self.get_gpio_by_value_file(value_file)
|
183
|
+
@@gpios.find { |g| g.value_file == value_file }
|
184
|
+
end
|
185
|
+
|
186
|
+
def self.get_event_edge(gpio) # gpio_event_added in python library
|
187
|
+
g = @@gpios.find { |g| g.gpio == gpio }
|
188
|
+
if g
|
189
|
+
return g.edge
|
190
|
+
end
|
191
|
+
end
|
192
|
+
|
193
|
+
def self.add_edge_detect(gpio, edge, bounce_time = nil)
|
194
|
+
current_edge = get_event_edge(gpio)
|
195
|
+
if current_edge.nil?
|
196
|
+
g = new_gpio(gpio)
|
197
|
+
set_edge(gpio, edge)
|
198
|
+
g.edge = edge
|
199
|
+
g.bounce_time = bounce_time
|
200
|
+
elsif current_edge == edge
|
201
|
+
g = get_gpio(gpio)
|
202
|
+
if (bounce_time && g.bounce_time != bounce_time) || g.thread_added
|
203
|
+
raise RuntimeError, "conflicting edge detection already enabled for GPIO #{gpio}"
|
204
|
+
end
|
205
|
+
else
|
206
|
+
raise RuntimeError, "conflicting edge detection already enabled for GPIO #{gpio}"
|
207
|
+
end
|
208
|
+
|
209
|
+
if @@epoll.nil?
|
210
|
+
@@epoll = Epoll.create
|
211
|
+
end
|
212
|
+
|
213
|
+
begin
|
214
|
+
@@epoll.add(g.value_file, Epoll::PRI)
|
215
|
+
rescue
|
216
|
+
remove_edge_detect(gpio)
|
217
|
+
raise
|
218
|
+
end
|
219
|
+
|
220
|
+
g.thread_added = true
|
221
|
+
if @@epoll_thread.nil? || !@@epoll_thread.alive?
|
222
|
+
@@epoll_thread = Thread.new { poll_thread }
|
223
|
+
end
|
224
|
+
end
|
225
|
+
|
226
|
+
def self.remove_edge_detect(gpio)
|
227
|
+
g = get_gpio(gpio)
|
228
|
+
if g and @@epoll
|
229
|
+
@@epoll.del(g.value_file)
|
230
|
+
set_edge(gpio, :none)
|
231
|
+
g.edge = :none
|
232
|
+
g.value_file.close
|
233
|
+
unexport(gpio)
|
234
|
+
delete_gpio(gpio)
|
235
|
+
end
|
236
|
+
end
|
237
|
+
|
238
|
+
def self.poll_thread
|
239
|
+
loop do
|
240
|
+
begin
|
241
|
+
events = @@epoll.wait
|
242
|
+
rescue IOError # empty interest list
|
243
|
+
break
|
244
|
+
end
|
245
|
+
events.each do |event|
|
246
|
+
if event.events & Epoll::PRI != 0
|
247
|
+
event.data.seek(0, IO::SEEK_SET)
|
248
|
+
g = get_gpio_by_value_file(event.data)
|
249
|
+
value = event.data.read.chomp.to_i
|
250
|
+
if g.initial_thread # ignore first epoll trigger
|
251
|
+
g.initial_thread = false
|
252
|
+
else
|
253
|
+
now = Time.now.to_f
|
254
|
+
if g.bounce_time.nil? || g.last_call == 0 || g.last_call > now ||
|
255
|
+
(now - g.last_call) * 1000 > g.bounce_time then
|
256
|
+
g.last_call = now
|
257
|
+
run_callbacks(g.gpio, value)
|
258
|
+
end
|
259
|
+
end
|
260
|
+
end
|
261
|
+
end
|
262
|
+
end
|
263
|
+
end
|
264
|
+
|
265
|
+
def self.event_cleanup(gpio)
|
266
|
+
@@gpios.map { |g| g.gpio }.each do |gpio_|
|
267
|
+
if gpio.nil? || gpio_ == gpio
|
268
|
+
remove_edge_detect(gpio_)
|
269
|
+
end
|
270
|
+
end
|
271
|
+
|
272
|
+
if @@gpios.empty? && @@epoll_thread
|
273
|
+
@@epoll_thread.terminate
|
274
|
+
end
|
275
|
+
end
|
276
|
+
|
277
|
+
def self.event_cleanup_all
|
278
|
+
event_cleanup(nil)
|
279
|
+
end
|
280
|
+
|
281
|
+
def self.add_callback(gpio, &block)
|
282
|
+
@@callbacks << Callback.new(gpio, &block)
|
283
|
+
end
|
284
|
+
|
285
|
+
def self.remove_callbacks(gpio)
|
286
|
+
@@callbacks.delete_if { |g| g.gpio == gpio }
|
287
|
+
end
|
288
|
+
|
289
|
+
def self.callback_exists(gpio)
|
290
|
+
@@gpios.find { |g| g.gpio == gpio } != nil
|
291
|
+
end
|
292
|
+
|
293
|
+
def self.run_callbacks(gpio, value)
|
294
|
+
@@callbacks.each do |callback|
|
295
|
+
if callback.gpio == gpio
|
296
|
+
callback.block.call(channel_from_gpio(gpio), value)
|
297
|
+
end
|
298
|
+
end
|
299
|
+
end
|
300
|
+
|
301
|
+
def self.validate_direction(direction)
|
302
|
+
direction = direction.to_s
|
303
|
+
if direction != 'in' && direction != 'out'
|
304
|
+
raise ArgumentError, "`direction` must be 'in' or 'out'; given '#{direction}'"
|
305
|
+
end
|
306
|
+
end
|
307
|
+
|
308
|
+
def self.validate_edge(edge)
|
309
|
+
edge = edge.to_s
|
310
|
+
if edge != 'rising' && edge != 'falling' && edge != 'both' && edge != 'none'
|
311
|
+
raise ArgumentError, "`edge` must be 'rising', 'falling', 'both', or 'none'; given '#{edge}'"
|
312
|
+
end
|
313
|
+
end
|
314
|
+
|
315
|
+
class GPIO
|
316
|
+
attr_accessor :gpio, :exported, :value_file, :initial_thread, :initial_wait, :bounce_time, :last_call,
|
317
|
+
:thread_added, :edge
|
318
|
+
end
|
319
|
+
|
320
|
+
class Callback
|
321
|
+
attr_accessor :gpio, :block
|
322
|
+
|
323
|
+
def initialize(gpio, &block)
|
324
|
+
@gpio = gpio
|
325
|
+
@block = block
|
326
|
+
end
|
327
|
+
end
|
328
|
+
end
|
329
|
+
end
|