beaglebone 1.0.4

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