beaglebone 1.0.4

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.
@@ -0,0 +1,480 @@
1
+ # == ain.rb
2
+ # This file contains the Analog Input methods
3
+ module Beaglebone #:nodoc:
4
+ # AIN
5
+ # procedural methods for Analog Input
6
+ # == Summary
7
+ # #read is called to get the analog value of a pin
8
+ # more advanced polling methods are also available
9
+ module AIN
10
+ class << self
11
+
12
+ # valid voltage readings in mv
13
+ RANGE = (0..1799)
14
+
15
+ # Read from an analog pin
16
+ #
17
+ # @param pin should be a symbol representing the header pin
18
+ #
19
+ # @return [Integer] value in millivolts
20
+ #
21
+ # @example
22
+ # AIN.read(:P9_33) => 1799
23
+ def read(pin)
24
+ Beaglebone::check_valid_pin(pin, :analog)
25
+
26
+ Beaglebone::set_pin_status(pin, :type, :analog)
27
+
28
+ ain_fd = Beaglebone::get_pin_status(pin, :fd_ain)
29
+
30
+ unless ain_fd
31
+ #ensure dtb is loaded
32
+ Beaglebone::device_tree_load(TREES[:ADC][:global])
33
+
34
+ #open the AIN analog input file
35
+ pininfo = PINS[pin]
36
+
37
+ ain_file = Dir.glob("/sys/devices/ocp.*/helper.*/AIN#{pininfo[:analog]}").first
38
+ #ain_file = Dir.glob("/sys/bus/iio/devices/iio:device0/in_voltage#{pininfo[:analog]}_raw").first
39
+ ain_fd = File.open(ain_file, 'r')
40
+
41
+ Beaglebone::set_pin_status(pin, :fd_ain, ain_fd)
42
+ end
43
+
44
+ ain_fd.rewind
45
+ ain_fd.read.strip.to_i
46
+ end
47
+
48
+ # Runs a callback after voltage changes by specified amount
49
+ # This creates a new thread that runs in the background and polls at specified interval
50
+ #
51
+ # @param callback A method to call when the change is detected. This method should take 4 arguments: the pin, the previous voltage, the current voltage, and the counter
52
+ # @param pin should be a symbol representing the header pin, i.e. :P9_11
53
+ # @param mv_change an integer specifying the required change in mv
54
+ # @param interval a number representing the wait time between polling
55
+ # @param repeats is optional and specifies the number of times the callback will be run
56
+ #
57
+ # @example
58
+ # This polls every 0.1 seconds and will run after a 10mv change is detected
59
+ # callback = lambda { |pin, mv_last, mv, count| puts "[#{count}] #{pin} #{mv_last} -> #{mv}" }
60
+ # AIN.run_on_change(callback, :P9_33, 10, 0.1)
61
+ def run_on_change(callback, pin, mv_change=10, interval=0.01, repeats=nil)
62
+
63
+ raise StandardError, "Already waiting for change on pin: #{pin}" if Beaglebone::get_pin_status(pin, :waiting)
64
+ raise StandardError, "Already waiting for thread on pin: #{pin}" if Beaglebone::get_pin_status(pin, :thread)
65
+
66
+ thread = Thread.new(callback, pin, mv_change, interval, repeats) do |c, p, mvc, i, r|
67
+ begin
68
+ count = 0
69
+ mvl = nil
70
+ loop do
71
+
72
+ mvl,mv,itr = wait_for_change(p, mvc, i, mvl)
73
+
74
+ c.call(p, mvl, mv, count) if c
75
+
76
+ #if there was no delay in the wait_for_change, delay now.
77
+ sleep(interval) if itr == 0
78
+
79
+ mvl = mv
80
+ count += 1
81
+ break if r && count >= r
82
+ end
83
+ rescue => ex
84
+ puts ex
85
+ puts ex.backtrace
86
+ ensure
87
+ Beaglebone::delete_pin_status(p, :thread)
88
+ Beaglebone::delete_pin_status(p, :waiting)
89
+ end
90
+ end
91
+
92
+ Beaglebone::set_pin_status(pin, :thread, thread)
93
+ end
94
+
95
+ # Runs a callback once after specified change in voltage detected
96
+ # Convenience method for run_on_change
97
+ # @see #run_on_change
98
+ def run_once_on_change(callback, pin, mv_change=10, interval=0.01)
99
+ run_on_change(callback, pin, mv_change, interval, 1)
100
+ end
101
+
102
+ # Runs a callback after voltage changes beyond a certain threshold
103
+ # This creates a new thread that runs in the background and polls at specified interval
104
+ # When the voltage crosses the specified thresholds the callback is run
105
+ #
106
+ # @param callback A method to call when the change is detected. This method should take 6 arguments: the pin, the previous voltage, the current voltage, the previous state, the current state, and the counter
107
+ # @param pin should be a symbol representing the header pin, i.e. :P9_11
108
+ # @param mv_lower an integer specifying the lower threshold voltage
109
+ # @param mv_upper an integer specifying the upper threshold voltage
110
+ # @param mv_reset an integer specifying the range in mv required to reset the threshold trigger
111
+ # @param interval a number representing the wait time between polling
112
+ # @param repeats is optional and specifies the number of times the callback will be run
113
+ # @example
114
+ # # This polls every 0.01 seconds and will run after a the voltage crosses 400mv or 1200mv.
115
+ # Voltage will have to cross a range by at least 5mv to prevent rapidly triggering events
116
+ # callback = lambda { |pin, mv_last, mv, state_last, state, count|
117
+ # puts "[#{count}] #{pin} #{state_last} -> #{state} #{mv_last} -> #{mv}"
118
+ # }
119
+ # AIN.run_on_threshold(callback, :P9_33, 400, 1200, 5, 0.01)
120
+ def run_on_threshold(callback, pin, mv_lower, mv_upper, mv_reset=10, interval=0.01, repeats=nil)
121
+
122
+ raise StandardError, "Already waiting for change on pin: #{pin}" if Beaglebone::get_pin_status(pin, :waiting)
123
+ raise StandardError, "Already waiting for thread on pin: #{pin}" if Beaglebone::get_pin_status(pin, :thread)
124
+
125
+ thread = Thread.new(callback, pin, mv_lower, mv_upper, mv_reset, interval, repeats) do |c, p, mvl, mvu, mvr, i, r|
126
+ begin
127
+ count = 0
128
+ mv_last = nil
129
+ state_last = nil
130
+ loop do
131
+
132
+ mv_last,mv,state_last,state,itr = wait_for_threshold(p, mvl, mvu, mvr, i, mv_last, state_last)
133
+
134
+ c.call(p, mv_last, mv, state_last, state, count) if c
135
+
136
+ #if there was no delay in the wait_for_change, delay now.
137
+ sleep(interval) if itr == 0
138
+
139
+ mv_last = mv
140
+ state_last = state
141
+ count += 1
142
+ break if r && r >= count
143
+ end
144
+ rescue => ex
145
+ puts ex
146
+ puts ex.backtrace
147
+ ensure
148
+ Beaglebone::delete_pin_status(p, :thread)
149
+ Beaglebone::delete_pin_status(p, :waiting)
150
+ end
151
+ end
152
+
153
+ Beaglebone::set_pin_status(pin, :thread, thread)
154
+ end
155
+
156
+ # Runs a callback once after voltage crosses a specified threshold
157
+ # Convenience method for run_on_threshold
158
+ # @see #run_on_threshold
159
+ def run_once_on_threshold(callback, pin, mv_lower, mv_upper, mv_reset=10, interval=0.01)
160
+ run_on_threshold(callback, pin, mv_lower, mv_upper, mv_reset, interval, 1)
161
+ end
162
+
163
+ # noinspection RubyScope
164
+
165
+ # Runs a callback after voltage changes beyond a certain threshold
166
+ # This creates a new thread that runs in the background and polls at specified interval
167
+ # When the voltage crosses the specified thresholds the callback is run
168
+ #
169
+ # This method should take 6 arguments:
170
+ # the pin, the previous voltage, the current voltage, the previous state, the current state, and the counter
171
+ # @param pin should be a symbol representing the header pin, i.e. :P9_11
172
+ # @param mv_lower an integer specifying the lower threshold voltage
173
+ # @param mv_upper an integer specifying the upper threshold voltage
174
+ # @param mv_reset an integer specifying the range in mv required to reset the threshold trigger
175
+ # @param interval a number representing the wait time between polling
176
+ # @param mv_last is optional and specifies the voltage to use as the initial point to measure change
177
+ # @param state_last is optional and specifies the state to use as the initial state to watch change
178
+ #
179
+ # @example
180
+ # # This polls every 0.01 seconds and will run after a the voltage crosses 400mv or 1200mv.
181
+ # # Voltage will have to cross a range by at least 5mv to prevent rapidly triggering events
182
+ # callback = lambda { |pin, mv_last, mv, state_last, state, count|
183
+ # puts "[#{count}] #{pin} #{state_last} -> #{state} #{mv_last} -> #{mv}"
184
+ # }
185
+ # AIN.run_on_threshold(callback, :P9_33, 400, 1200, 5, 0.01)
186
+ def wait_for_threshold(pin, mv_lower, mv_upper, mv_reset=10, interval=0.01, mv_last=nil, state_last=nil)
187
+ Beaglebone::check_valid_pin(pin, :analog)
188
+ raise ArgumentError, "mv_upper needs to be between 0 and 1800: #{pin} (#{mv_upper})" unless (0..1800).include?(mv_upper)
189
+ raise ArgumentError, "mv_lower needs to be between 0 and 1800: #{pin} (#{mv_lower})" unless (0..1800).include?(mv_lower)
190
+ raise ArgumentError, "mv_lower needs to be <= mv_upper: #{pin} (#{mv_lower}:#{mv_upper})" unless mv_lower <= mv_upper
191
+ raise ArgumentError, "mv_reset needs to be between 0 and 1800: #{pin} (#{mv_reset})" unless (0..1800).include?(mv_reset)
192
+
193
+ #ensure we're the only ones waiting for this trigger
194
+ if Beaglebone::get_pin_status(pin, :thread) && Beaglebone::get_pin_status(pin, :thread) != Thread.current
195
+ raise StandardError, "Already waiting for change on pin: #{pin}"
196
+ end
197
+
198
+ if Beaglebone::get_pin_status(pin, :waiting) && Beaglebone::get_pin_status(pin, :thread) != Thread.current
199
+ raise StandardError, "Already waiting for change on pin: #{pin}"
200
+ end
201
+
202
+ Beaglebone::set_pin_status(pin, :waiting, true)
203
+
204
+ mv_last = read(pin) unless mv_last
205
+
206
+ if mv_last >= mv_upper
207
+ state_last = :HIGH
208
+ elsif mv_last <= mv_lower
209
+ state_last = :LOW
210
+ else
211
+ state_last = :MID
212
+ end unless state_last
213
+
214
+ state = :UNKNOWN
215
+ count = 0
216
+ loop do
217
+ mv = read(pin)
218
+
219
+ if state_last == :LOW
220
+ #state remains low unless it crosses into high, or above mv_low + mv_reset
221
+ if mv >= mv_upper && mv >= mv_lower + mv_reset
222
+ state = :HIGH
223
+ elsif mv >= mv_lower + mv_reset
224
+ state = :MID
225
+ else
226
+ state = :LOW
227
+ end
228
+ elsif state_last == :HIGH
229
+ #state remains high unless it crosses into low, or below mv_high - mv_reset
230
+ if mv <= mv_lower && mv <= mv_upper - mv_reset
231
+ state = :LOW
232
+ elsif mv <= mv_upper - mv_reset
233
+ state = :MID
234
+ else
235
+ state = :HIGH
236
+ end
237
+ elsif state_last == :MID
238
+ #state changes from normal by crossing into upper or lower
239
+ if mv >= mv_upper
240
+ state = :HIGH
241
+ elsif mv <= mv_lower
242
+ state = :LOW
243
+ else
244
+ state = :MID
245
+ end
246
+ end
247
+
248
+ #if we've detected a change of state
249
+ if state != state_last
250
+ Beaglebone::delete_pin_status(pin, :waiting)
251
+ return [ mv_last, mv, state_last, state, count ]
252
+ end
253
+
254
+ sleep interval
255
+
256
+ count += 1
257
+ end
258
+
259
+ Beaglebone::delete_pin_status(pin, :waiting)
260
+ [ mv_last, -1, state_last, state_last, count ]
261
+
262
+ end
263
+
264
+ # Returns when voltage changes by specified amount
265
+ #
266
+ # @param pin should be a symbol representing the header pin, i.e. :P9_11
267
+ # @param mv_change an integer specifying the required change in mv
268
+ # @param interval a number representing the wait time between polling
269
+ # @param mv_last is optional and specifies the voltage to use as the initial point to measure change
270
+ #
271
+ # @example
272
+ # # This will poll every P9_33 every 0.01 seconds until 10mv of change is detected
273
+ # # This method will return the initial reading, final reading, and how many times it polled
274
+ # AIN.wait_for_change(:P9_33, 10, 0.01) => [ 1200, 1210, 4]
275
+ def wait_for_change(pin, mv_change, interval, mv_last=nil)
276
+
277
+ Beaglebone::check_valid_pin(pin, :analog)
278
+ raise ArgumentError, "mv_change needs to be between 0 and 1800: #{pin} (#{mv_change})" unless (0..1800).include?(mv_change)
279
+
280
+ #ensure we're the only ones waiting for this trigger
281
+ if Beaglebone::get_pin_status(pin, :thread) && Beaglebone::get_pin_status(pin, :thread) != Thread.current
282
+ raise StandardError, "Already waiting for change on pin: #{pin}"
283
+ end
284
+
285
+ if Beaglebone::get_pin_status(pin, :waiting) && Beaglebone::get_pin_status(pin, :thread) != Thread.current
286
+ raise StandardError, "Already waiting for change on pin: #{pin}"
287
+ end
288
+
289
+
290
+ Beaglebone::set_pin_status(pin, :waiting, true)
291
+ mv_last = read(pin) unless mv_last
292
+
293
+ change_max = [mv_last - 0, 1799 - mv_last].max
294
+
295
+ mv_change = change_max if mv_change > change_max
296
+
297
+ count = 0
298
+ loop do
299
+ mv = read(pin)
300
+
301
+ #if we've detected the change or hit the edge of the range
302
+ if (mv - mv_last).abs >= mv_change
303
+
304
+ Beaglebone::delete_pin_status(pin, :waiting)
305
+ return [ mv_last, mv, count ]
306
+ end
307
+
308
+ sleep interval
309
+
310
+ count += 1
311
+ end
312
+
313
+ Beaglebone::delete_pin_status(pin, :waiting)
314
+ [ mv_last, -1, count ]
315
+ end
316
+
317
+ # Stops any threads waiting for data on specified pin
318
+ #
319
+ # @param pin should be a symbol representing the header pin, i.e. :P9_11
320
+ def stop_wait(pin)
321
+ thread = Beaglebone::get_pin_status(pin, :thread)
322
+
323
+ thread.exit if thread
324
+ thread.join if thread
325
+ end
326
+
327
+ # Return an array of AIN pins in use
328
+ #
329
+ # @return [Array<Symbol>]
330
+ #
331
+ # @example
332
+ # AIN.get_analog_pins => [:P9_33, :P9_34]
333
+ def get_analog_pins
334
+ Beaglebone.pinstatus.clone.select { |x,y| x if y[:type] == :analog}.keys
335
+ end
336
+
337
+ # Disable an analog pin
338
+ #
339
+ # @param pin should be a symbol representing the header pin
340
+ def disable_analog_pin(pin)
341
+ Beaglebone::check_valid_pin(pin, :analog)
342
+ Beaglebone::delete_pin_status(pin)
343
+ end
344
+
345
+ # Disable all analog pins
346
+ def cleanup
347
+ #reset all GPIO we've used to IN and unexport them
348
+ get_analog_pins.each { |x| disable_analog_pin(x) }
349
+ end
350
+
351
+ end
352
+ end
353
+
354
+ # Object Oriented AIN Implementation.
355
+ # This treats the pin as an object.
356
+ class AINPin
357
+ # Initialize a Analog pin
358
+ # Return's an AINPin object
359
+ #
360
+ # @example
361
+ # p9_33 = AINPin.new(:P9_33)
362
+ def initialize(pin)
363
+ @pin = pin
364
+ end
365
+
366
+ # Read from an analog pin
367
+ #
368
+ # @return [Integer] value in millivolts
369
+ #
370
+ # @example
371
+ # p9_33 = AINPin.new(:P9_33)
372
+ # p9_33.read => 1799
373
+ def read
374
+ AIN::read(@pin)
375
+ end
376
+
377
+ # Runs a callback after voltage changes by specified amount
378
+ # This creates a new thread that runs in the background and polls at specified interval
379
+ #
380
+ # @param callback A method to call when the change is detected
381
+ # This method should take 4 arguments: the pin, the previous voltage, the current voltage, and the counter
382
+ # @param mv_change an integer specifying the required change in mv
383
+ # @param interval a number representing the wait time between polling
384
+ # @param repeats is optional and specifies the number of times the callback will be run
385
+ #
386
+ # @example
387
+ # # This polls every 0.1 seconds and will run after a 10mv change is detected
388
+ # callback = lambda { |pin, mv_last, mv, count| puts "[#{count}] #{pin} #{mv_last} -> #{mv}" }
389
+ # p9_33 = AINPin.new(:P9_33)
390
+ # p9_33.run_on_change(callback, 10, 0.1)
391
+ def run_on_change(callback, mv_change=10, interval=0.01, repeats=nil)
392
+ AIN::run_on_change(callback, @pin, mv_change, interval, repeats)
393
+ end
394
+
395
+ # Runs a callback once after specified change in voltage detected
396
+ # Convenience method for run_on_change
397
+ def run_once_on_change(callback, mv_change=10, interval=0.01)
398
+ AIN::run_once_on_change(callback, @pin, mv_change, interval)
399
+ end
400
+
401
+
402
+ # Runs a callback after voltage changes beyond a certain threshold
403
+ # This creates a new thread that runs in the background and polls at specified interval
404
+ # When the voltage crosses the specified thresholds the callback is run
405
+ #
406
+ # @param callback A method to call when the change is detected
407
+ # This method should take 6 arguments:
408
+ # the pin, the previous voltage, the current voltage, the previous state, the current state, and the counter
409
+ # @param mv_lower an integer specifying the lower threshold voltage
410
+ # @param mv_upper an integer specifying the upper threshold voltage
411
+ # @param mv_reset an integer specifying the range in mv required to reset the threshold trigger
412
+ # @param interval a number representing the wait time between polling
413
+ # @param repeats is optional and specifies the number of times the callback will be run
414
+ #
415
+ # @example
416
+ # # This polls every 0.01 seconds and will run after a the voltage crosses 400mv or 1200mv.
417
+ # # Voltage will have to cross a range by at least 5mv to prevent rapidly triggering events
418
+ # callback = lambda { |pin, mv_last, mv, state_last, state, count|
419
+ # puts "[#{count}] #{pin} #{state_last} -> #{state} #{mv_last} -> #{mv}"
420
+ # }
421
+ # p9_33 = AINPin.new(:P9_33)
422
+ # p9_33.run_on_threshold(callback, 400, 1200, 5, 0.01)
423
+ def run_on_threshold(callback, mv_lower, mv_upper, mv_reset=10, interval=0.01, repeats=nil)
424
+ AIN::run_on_threshold(callback, @pin, mv_lower, mv_upper, mv_reset, interval, repeats)
425
+ end
426
+
427
+
428
+ # Runs a callback once after voltage crosses a specified threshold
429
+ # Convenience method for run_on_threshold
430
+ def run_once_on_threshold(callback, mv_lower, mv_upper, mv_reset=10, interval=0.01)
431
+ AIN::run_once_on_threshold(callback, @pin, mv_lower, mv_upper, mv_reset, interval)
432
+ end
433
+
434
+ # Returns when voltage changes by specified amount
435
+ # @param mv_lower an integer specifying the lower threshold voltage
436
+ # @param mv_upper an integer specifying the upper threshold voltage
437
+ # @param mv_reset an integer specifying the range in mv required to reset the threshold trigger
438
+ # @param interval a number representing the wait time between polling
439
+ # @param mv_last is optional and specifies the voltage to use as the initial point to measure change
440
+ # @param state_last is optional and specifies the state to use as the initial state to watch change
441
+ #
442
+ # @example
443
+ # # This polls every 0.01 seconds and will run after a the voltage crosses 400mv or 1200mv.
444
+ # # Voltage will have to cross a range by at least 5mv to prevent rapidly triggering events
445
+ # callback = lambda { |pin, mv_last, mv, state_last, state, count|
446
+ # puts "[#{count}] #{pin} #{state_last} -> #{state} #{mv_last} -> #{mv}"
447
+ # }
448
+ # p9_33 = AINPin.new(:P9_33)
449
+ # p9_33.wait_for_threshold(400, 1200, 5, 0.01)
450
+ def wait_for_threshold(mv_lower, mv_upper, mv_reset=10, interval=0.01, mv_last=nil, state_last=nil)
451
+ AIN::wait_for_threshold(@pin, mv_lower, mv_upper, mv_reset, interval, mv_last, state_last)
452
+ end
453
+
454
+ # Returns when voltage changes by specified amount
455
+ #
456
+ # @param mv_change an integer specifying the required change in mv
457
+ # @param interval a number representing the wait time between polling
458
+ # @param mv_last is optional and specifies the voltage to use as the initial point to measure change
459
+ #
460
+ # @example
461
+ # # This will poll every P9_33 every 0.01 seconds until 10mv of change is detected
462
+ # # This method will return the initial reading, final reading, and how many times it polled
463
+ # p9_33 = AINPin.new(:P9_33)
464
+ # p9_33.wait_for_change(10, 0.01) => [ 1200, 1210, 4]
465
+ def wait_for_change(mv_change, interval, mv_last=nil)
466
+ AIN::wait_for_change(@pin, mv_change, interval, mv_last)
467
+ end
468
+
469
+ # Stops any threads waiting for data on this pin
470
+ def stop_wait
471
+ AIN::stop_wait(@pin)
472
+ end
473
+
474
+ # Disable analog pin
475
+ def disable_analog_pin
476
+ AIN::disable_analog_pin(@pin)
477
+ end
478
+
479
+ end
480
+ end