Module: Beaglebone::GPIO

Defined in:
lib/beaglebone/gpio.rb

Overview

GPIO

procedural methods for GPIO control

Summary

#pin_mode is called to initialize a pin. Further basic functionality is available with #digital_read and #digital_write

Constant Summary collapse

MODES =

GPIO modes

[ :IN, :OUT ]
STATES =

GPIO states

{ :HIGH => 1, :LOW => 0 }
EDGES =

Edge trigger options

[ :NONE, :RISING, :FALLING, :BOTH ]

Class Method Summary collapse

Class Method Details

.cleanupObject

Resets all the GPIO pins that we have used and unexport them



202
203
204
# File 'lib/beaglebone/gpio.rb', line 202

def cleanup
  get_gpio_pins.each { |x| disable_gpio_pin(x) }
end

.digital_read(pin) ⇒ Symbol

Reads a pin’s input state and return that value

Examples:

GPIO.digital_read(:P9_11) => :HIGH

Parameters:

  • pin

    should be a symbol representing the header pin, i.e. :P9_11

Returns:

  • (Symbol)

    :HIGH or :LOW

Raises:

  • (StandardError)


91
92
93
94
95
96
97
98
99
100
101
102
# File 'lib/beaglebone/gpio.rb', line 91

def digital_read(pin)
  check_gpio_enabled(pin)

  raise StandardError, "PIN not in GPIO IN mode: #{pin}" unless get_gpio_mode(pin) == :IN

  fd = get_value_fd(pin)
  fd.rewind
  value = fd.read.to_s.strip
  state = STATES.key(value.to_i)

  Beaglebone::set_pin_status(pin, :state, state)
end

.digital_write(pin, state) ⇒ Object

Sets a pin’s output state

Examples:

GPIO.digital_write(:P9_12, :HIGH)
GPIO.digital_write(:P9_12, :LOW)

Parameters:

  • pin

    should be a symbol representing the header pin

  • state

    should be a symbol representin the state, :HIGH or :LOW

Raises:

  • (StandardError)


71
72
73
74
75
76
77
78
79
80
81
# File 'lib/beaglebone/gpio.rb', line 71

def digital_write(pin, state)
  check_valid_state(state)
  check_gpio_enabled(pin)

  raise StandardError, "PIN not in GPIO OUT mode: #{pin}" unless get_gpio_mode(pin) == :OUT

  fd = get_value_fd(pin)
  fd.write STATES[state.to_sym.upcase].to_s
  fd.flush
  Beaglebone::set_pin_status(pin, :state, state)
end

.disable_gpio_pin(pin) ⇒ Object

Disable a GPIO pin

Parameters:

  • pin

    should be a symbol representing the header pin

Raises:

  • (StandardError)


343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
# File 'lib/beaglebone/gpio.rb', line 343

def disable_gpio_pin(pin)

  Beaglebone::check_valid_pin(pin, :gpio)

  pininfo = PINS[pin]

  close_value_fd(pin)

  #close any running threads

  stop_edge_wait(pin)

  #write to unexport to disable gpio

  File.open('/sys/class/gpio/unexport', 'w') { |f| f.write(pininfo[:gpio]) }

  #remove status from hash so following enabled? call checks actual system

  Beaglebone::delete_pin_status(pin)

  #check to see if pin is GPIO enabled in /sys/class/gpio/

  raise StandardError, "GPIO was unable to uninitalize pin: #{pin.to_s}" if enabled?(pin)

end

.enabled?(pin) ⇒ Boolean

Returns true if specified pin is enabled in GPIO mode, else false

Returns:

  • (Boolean)


207
208
209
210
211
212
213
214
215
216
217
218
219
# File 'lib/beaglebone/gpio.rb', line 207

def enabled?(pin)

  return true if Beaglebone::get_pin_status(pin, :type) == :gpio

  return false unless valid?(pin)
  if Dir.exists?(gpio_directory(pin))

    Beaglebone::set_pin_status(pin, :type, :gpio)
    return true
  end

  false
end

.get_gpio_edge(pin) ⇒ Symbol

Returns the GPIO edge trigger type on an initialized pin

Returns:

  • (Symbol)

    :NONE, :RISING, :FALLING, or :BOTH



321
322
323
324
325
326
327
328
# File 'lib/beaglebone/gpio.rb', line 321

def get_gpio_edge(pin)
  check_gpio_enabled(pin)

  edge = Beaglebone::get_pin_status(pin, :trigger)
  return edge if edge

  read_gpio_edge(pin)
end

.get_gpio_mode(pin) ⇒ Object

Returns mode from pin, reads mode if unknown



262
263
264
265
266
267
268
269
# File 'lib/beaglebone/gpio.rb', line 262

def get_gpio_mode(pin)
  check_gpio_enabled(pin)

  mode = Beaglebone::get_pin_status(pin, :mode)
  return mode if mode

  read_gpio_direction(pin)
end

.get_gpio_pinsArray<Symbol>

Return an array of GPIO pins in use

Examples:

GPIO.get_gpio_pins => [:P9_12, :P9_13]

Returns:

  • (Array<Symbol>)


336
337
338
# File 'lib/beaglebone/gpio.rb', line 336

def get_gpio_pins
  Beaglebone.pinstatus.clone.select { |x,y| x if y[:type] == :gpio && !PINS[x][:led] }.keys
end

.get_gpio_state(pin) ⇒ Object

Returns last known state from pin, reads state if unknown



251
252
253
254
255
256
257
258
# File 'lib/beaglebone/gpio.rb', line 251

def get_gpio_state(pin)
  check_gpio_enabled(pin)

  state = Beaglebone::get_pin_status(pin, :state)
  return state if state

  digital_read(pin)
end

.pin_mode(pin, mode) ⇒ Object

Initialize a GPIO pin

Examples:

GPIO.pin_mode(:P9_12, :OUT)
GPIO.pin_mode(:P9_11, :IN)

Parameters:

  • pin

    should be a symbol representing the header pin

  • mode

    should specify the mode of the pin, either :IN or :OUT



27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
# File 'lib/beaglebone/gpio.rb', line 27

def pin_mode(pin, mode)

  #make sure a valid mode was passed

  check_valid_mode(mode)

  #make sure a valid pin was passed and that it supports GPIO

  Beaglebone::check_valid_pin(pin, :gpio)

  #get info from PINS hash

  pininfo = PINS[pin]

  #if pin is enabled for something else, disable it

  if Beaglebone::get_pin_status(pin) && Beaglebone::get_pin_status(pin, :type) != :gpio
    Beaglebone::disable_pin(pin)
  end

  #export pin unless its an on board LED, if it isn't already exported

  if pininfo[:led]
    raise StandardError, "LEDs only support OUT mode: #{pin.to_s}" unless mode == :OUT
    File.open("#{gpio_directory(pin)}/trigger", 'w') { |f| f.write('gpio') }
  else
    File.open('/sys/class/gpio/export', 'w') { |f| f.write pininfo[:gpio] }
    #check to see if pin is GPIO enabled in /sys/class/gpio/

    raise StandardError, "GPIO was unable to initalize pin: #{pin.to_s}" unless enabled?(pin)
  end unless Beaglebone::get_pin_status(pin, :type) == :gpio

  #set pin mode

  unless pininfo[:led]
    set_gpio_mode(pin, mode)
    dir = read_gpio_direction(pin)
    raise StandardError, "GPIO was unable to set mode: #{pin.to_s} to #{mode.to_s} (#{dir})" if mode != dir
  end

  Beaglebone::set_pin_status(pin, :mode, mode)
end

.run_on_edge(callback, pin, edge, timeout = nil, repeats = nil) ⇒ Object

Runs a callback on an edge trigger event. This creates a new thread that runs in the background

Examples:

GPIO.run_on_edge(lambda { |pin,edge,count| puts "[#{count}] #{pin} -- #{edge}" }, :P9_11, :RISING)

Parameters:

  • callback

    A method to call when the edge trigger is detected. This method should take 3 arguments, the pin, the edge, and the counter

  • pin

    should be a symbol representing the header pin, i.e. :P9_11

  • edge

    should be a symbol representing the trigger type, e.g. :RISING, :FALLING, :BOTH

  • timeout (defaults to: nil)

    is optional and specifies a time window to wait

  • repeats (defaults to: nil)

    is optional and specifies the number of times the callback will be run

Raises:

  • (StandardError)


115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
# File 'lib/beaglebone/gpio.rb', line 115

def run_on_edge(callback, pin, edge, timeout = nil, repeats=nil)

  raise StandardError, "Already waiting for trigger on pin: #{pin}" if Beaglebone::get_pin_status(pin, :trigger)
  raise StandardError, "Already waiting for trigger on pin: #{pin}" if Beaglebone::get_pin_status(pin, :thread)

  thread = Thread.new(callback, pin, edge, timeout, repeats) do |c, p, e, t, r|
    begin
      count = 0
      loop do

        state = wait_for_edge(p, e, t, false)

        c.call(p, state, count) if c
        count += 1
        break if r && count >= r
      end
    rescue => ex
      puts ex
      puts ex.backtrace
    ensure
      cleanup_edge_trigger(p)
    end
  end

  Beaglebone::set_pin_status(pin, :thread, thread)
end

.run_once_on_edge(callback, pin, edge, timeout = nil) ⇒ Object

Runs a callback one time on an edge trigger event. This is a convenience method for run_on_edge

See Also:

  • #run_on_edge


145
146
147
# File 'lib/beaglebone/gpio.rb', line 145

def run_once_on_edge(callback, pin, edge, timeout = nil)
  run_on_edge(callback, pin, edge, timeout, 1)
end

.set_gpio_edge(pin, edge, force = nil) ⇒ Object

Set GPIO edge trigger type on an initialized pin

Examples:

GPIO.set_gpio_edge(:P9_11, :RISING)

Parameters:

  • pin

    should be a symbol representing the header pin

  • edge

    should be a symbol representing the trigger type, e.g. :RISING, :FALLING, :BOTH

  • force (defaults to: nil)

    is optional, if set will set the mode even if already set

Raises:

  • (StandardError)


296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
# File 'lib/beaglebone/gpio.rb', line 296

def set_gpio_edge(pin, edge, force=nil)
  check_valid_edge(edge)
  Beaglebone::check_valid_pin(pin, :gpio)

  raise StandardError, "PIN not in GPIO IN mode: #{pin}" unless get_gpio_mode(pin) == :IN

  return if get_gpio_edge(pin) == edge && !force

  File.open("#{gpio_directory(pin)}/edge", 'w') { |f| f.write edge.to_s.downcase }
  testedge = read_gpio_edge(pin)
  if testedge != edge.to_s.downcase
    Beaglebone::delete_pin_status(pin, :trigger)
    raise StandardError, "GPIO was unable to set edge: #{pin.to_s} to #{edge.to_s}"
  end

  if edge.to_sym == :NONE
    Beaglebone::delete_pin_status(pin, :trigger)
  else
    Beaglebone::set_pin_status(pin, :trigger, edge.to_sym)
  end

end

.set_gpio_mode(pin, mode) ⇒ Object

Set GPIO mode on an initialized pin

Examples:

GPIO.set_gpio_mode(:P9_12, :OUT)
GPIO.set_gpio_mode(:P9_11, :IN)

Parameters:

  • pin

    should be a symbol representing the header pin

  • mode

    should specify the mode of the pin, either :IN or :OUT



279
280
281
282
283
284
285
286
# File 'lib/beaglebone/gpio.rb', line 279

def set_gpio_mode(pin, mode)
  Beaglebone::check_valid_pin(pin, :gpio)
  check_valid_mode(mode)
  check_gpio_enabled(pin)

  File.open("#{gpio_directory(pin)}/direction", 'w') { |f| f.write mode.to_s.downcase }
  Beaglebone::set_pin_status(pin, :mode, mode)
end

.shift_out(latch_pin, clock_pin, data_pin, data, lsb = nil) ⇒ Object

Sends data to a shift register

Examples:

GPIO.shift_out(:P9_11, :P9_12, :P9_13, 255)

Parameters:

  • latch_pin

    should be a symbol representing the header pin, i.e. :P9_12

  • clock_pin

    should be a symbol representing the header pin, i.e. :P9_13

  • data_pin

    should be a symbol representing the header pin, i.e. :P9_14

  • data

    Integer value to write to the shift register

  • lsb (defaults to: nil)

    optional, send least significant bit first if set

Raises:

  • (ArgumentError)


231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
# File 'lib/beaglebone/gpio.rb', line 231

def shift_out(latch_pin, clock_pin, data_pin, data, lsb=nil)
  raise ArgumentError, "data must be > 0 (#{date}" if data < 0
  digital_write(latch_pin, :LOW)

  binary = data.to_s(2)
  pad = 8 - (binary.size % 8 )
  binary = (' ' * pad) + binary

  binary.reverse! if lsb

  binary.each_char do |bit|
    digital_write(clock_pin, :LOW)
    digital_write(data_pin, bit == '0' ? :LOW : :HIGH)
    digital_write(clock_pin, :HIGH)
  end
  digital_write(latch_pin, :HIGH)
end

.stop_edge_wait(pin) ⇒ Object

Stops any threads waiting for data on specified pin

Parameters:

  • pin

    should be a symbol representing the header pin, i.e. :P9_11



152
153
154
155
156
157
# File 'lib/beaglebone/gpio.rb', line 152

def stop_edge_wait(pin)
  thread = Beaglebone::get_pin_status(pin, :thread)

  thread.exit if thread
  thread.join if thread
end

.wait_for_edge(pin, edge, timeout = nil, disable = true) ⇒ Object

Wait for an edge trigger. Returns the type that triggered the event, e.g. :RISING, :FALLING, :BOTH

Examples:

wait_for_edge(:P9_11, :RISING, 30) => :RISING

Parameters:

  • pin

    should be a symbol representing the header pin, i.e. :P9_11

  • edge

    should be a symbol representing the trigger type, e.g. :RISING, :FALLING, :BOTH

  • timeout (defaults to: nil)

    is optional and specifies a time window to wait

  • disable (defaults to: true)

    is optional. If set, edge trigger detection is cleared on return

Raises:

  • (ArgumentError)


171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
# File 'lib/beaglebone/gpio.rb', line 171

def wait_for_edge(pin, edge, timeout = nil, disable=true)
  check_valid_edge(edge)
  raise ArgumentError, "Cannot wait for edge trigger NONE: #{pin}" if edge.to_sym.upcase == :NONE

  check_gpio_enabled(pin)
  raise StandardError, "PIN not in GPIO IN mode: #{pin}" unless get_gpio_mode(pin) == :IN

  #ensure we're the only ones waiting for this trigger

  if Beaglebone::get_pin_status(pin, :thread) && Beaglebone::get_pin_status(pin, :thread) != Thread.current
    raise StandardError, "Already waiting for trigger on pin: #{pin}"
  end

  if Beaglebone::get_pin_status(pin, :trigger) && Beaglebone::get_pin_status(pin, :thread) != Thread.current
    raise StandardError, "Already waiting for trigger on pin: #{pin}"
  end

  set_gpio_edge(pin, edge)

  fd = get_value_fd(pin)
  fd.read

  #select will return fd into the error set "es" if it recieves an interrupt

  _, _, es = IO.select(nil, nil, [fd], timeout)

  set_gpio_edge(pin, :NONE) if disable

  es ? digital_read(pin) : nil

end