rpi_gpio 0.4.0 → 0.7.1

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.
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
metadata CHANGED
@@ -1,63 +1,71 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rpi_gpio
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.0
4
+ version: 0.7.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Nick Lowery
8
- autorequire:
9
8
  bindir: bin
10
9
  cert_chain: []
11
- date: 2020-07-21 00:00:00.000000000 Z
10
+ date: 1980-01-02 00:00:00.000000000 Z
12
11
  dependencies:
12
+ - !ruby/object:Gem::Dependency
13
+ name: epoll
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - "~>"
17
+ - !ruby/object:Gem::Version
18
+ version: '0.3'
19
+ type: :runtime
20
+ prerelease: false
21
+ version_requirements: !ruby/object:Gem::Requirement
22
+ requirements:
23
+ - - "~>"
24
+ - !ruby/object:Gem::Version
25
+ version: '0.3'
13
26
  - !ruby/object:Gem::Dependency
14
27
  name: rake-compiler
15
28
  requirement: !ruby/object:Gem::Requirement
16
29
  requirements:
17
- - - ">="
30
+ - - "~>"
18
31
  - !ruby/object:Gem::Version
19
- version: '0'
32
+ version: '1.3'
20
33
  type: :development
21
34
  prerelease: false
22
35
  version_requirements: !ruby/object:Gem::Requirement
23
36
  requirements:
24
- - - ">="
37
+ - - "~>"
25
38
  - !ruby/object:Gem::Version
26
- version: '0'
39
+ version: '1.3'
27
40
  - !ruby/object:Gem::Dependency
28
41
  name: rspec
29
42
  requirement: !ruby/object:Gem::Requirement
30
43
  requirements:
31
- - - ">="
44
+ - - "~>"
32
45
  - !ruby/object:Gem::Version
33
- version: '0'
46
+ version: '3.13'
34
47
  type: :development
35
48
  prerelease: false
36
49
  version_requirements: !ruby/object:Gem::Requirement
37
50
  requirements:
38
- - - ">="
51
+ - - "~>"
39
52
  - !ruby/object:Gem::Version
40
- version: '0'
53
+ version: '3.13'
41
54
  description: Ruby conversion of RPi.GPIO Python module
42
- email: nick.a.lowery@gmail.com
55
+ email: nick.lowery@proton.me
43
56
  executables: []
44
57
  extensions:
45
58
  - ext/rpi_gpio/extconf.rb
46
59
  extra_rdoc_files: []
47
60
  files:
48
- - Gemfile
49
- - Gemfile.lock
50
61
  - LICENSE
51
62
  - README.md
52
- - Rakefile
53
63
  - ext/rpi_gpio/c_gpio.c
54
64
  - ext/rpi_gpio/c_gpio.h
55
65
  - ext/rpi_gpio/common.c
56
66
  - ext/rpi_gpio/common.h
57
67
  - ext/rpi_gpio/cpuinfo.c
58
68
  - ext/rpi_gpio/cpuinfo.h
59
- - ext/rpi_gpio/event_gpio.c
60
- - ext/rpi_gpio/event_gpio.h
61
69
  - ext/rpi_gpio/extconf.rb
62
70
  - ext/rpi_gpio/rb_gpio.c
63
71
  - ext/rpi_gpio/rb_gpio.h
@@ -67,11 +75,11 @@ files:
67
75
  - ext/rpi_gpio/rpi_gpio.h
68
76
  - ext/rpi_gpio/soft_pwm.c
69
77
  - ext/rpi_gpio/soft_pwm.h
78
+ - lib/rpi_gpio.rb
70
79
  homepage: https://github.com/ClockVapor/rpi_gpio
71
80
  licenses:
72
81
  - MIT
73
82
  metadata: {}
74
- post_install_message:
75
83
  rdoc_options: []
76
84
  require_paths:
77
85
  - lib
@@ -86,8 +94,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
86
94
  - !ruby/object:Gem::Version
87
95
  version: '0'
88
96
  requirements: []
89
- rubygems_version: 3.1.2
90
- signing_key:
97
+ rubygems_version: 4.0.10
91
98
  specification_version: 4
92
99
  summary: Ruby conversion of RPi.GPIO Python module
93
100
  test_files: []
data/Gemfile DELETED
@@ -1,4 +0,0 @@
1
- source "https://rubygems.org"
2
-
3
- gemspec
4
-
data/Gemfile.lock DELETED
@@ -1,36 +0,0 @@
1
- PATH
2
- remote: .
3
- specs:
4
- rpi_gpio (0.4.0)
5
-
6
- GEM
7
- remote: https://rubygems.org/
8
- specs:
9
- diff-lcs (1.4.4)
10
- rake (13.0.1)
11
- rake-compiler (1.1.1)
12
- rake
13
- rspec (3.9.0)
14
- rspec-core (~> 3.9.0)
15
- rspec-expectations (~> 3.9.0)
16
- rspec-mocks (~> 3.9.0)
17
- rspec-core (3.9.2)
18
- rspec-support (~> 3.9.3)
19
- rspec-expectations (3.9.2)
20
- diff-lcs (>= 1.2.0, < 2.0)
21
- rspec-support (~> 3.9.0)
22
- rspec-mocks (3.9.1)
23
- diff-lcs (>= 1.2.0, < 2.0)
24
- rspec-support (~> 3.9.0)
25
- rspec-support (3.9.3)
26
-
27
- PLATFORMS
28
- ruby
29
-
30
- DEPENDENCIES
31
- rake-compiler
32
- rpi_gpio!
33
- rspec
34
-
35
- BUNDLED WITH
36
- 2.1.4
data/Rakefile DELETED
@@ -1,3 +0,0 @@
1
- require 'rake/extensiontask'
2
-
3
- Rake::ExtensionTask.new('rpi_gpio')