BBB 0.0.10 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (54) hide show
  1. data/lib/BBB.rb +13 -17
  2. data/lib/BBB/application.rb +4 -21
  3. data/lib/BBB/circuit.rb +18 -33
  4. data/lib/BBB/components/analog_component.rb +29 -6
  5. data/lib/BBB/components/led.rb +39 -9
  6. data/lib/BBB/components/pinnable.rb +90 -4
  7. data/lib/BBB/components/servo.rb +43 -0
  8. data/lib/BBB/pins/analog_pin.rb +39 -0
  9. data/lib/BBB/pins/digital_pin.rb +106 -0
  10. data/lib/BBB/pins/io/ain.rb +58 -0
  11. data/lib/BBB/pins/io/gpio.rb +77 -0
  12. data/lib/BBB/pins/io/mapped.rb +39 -0
  13. data/lib/BBB/pins/io/pin_mapper.rb +224 -0
  14. data/lib/BBB/pins/io/pwm.rb +61 -0
  15. data/lib/BBB/pins/pinnable.rb +78 -0
  16. data/lib/BBB/pins/pwm_pin.rb +43 -0
  17. data/lib/BBB/version.rb +1 -1
  18. data/spec/application_spec.rb +1 -26
  19. data/spec/circuit_spec.rb +0 -1
  20. data/spec/components/analog_component_spec.rb +36 -0
  21. data/spec/components/led_spec.rb +14 -19
  22. data/spec/components/pinnable_spec.rb +73 -8
  23. data/spec/components/servo_spec.rb +6 -0
  24. data/spec/pins/analog_pin_spec.rb +33 -0
  25. data/spec/pins/digital_input_pin_spec.rb +13 -0
  26. data/spec/pins/digital_output_pin_spec.rb +14 -0
  27. data/spec/pins/digital_pin_spec.rb +66 -0
  28. data/spec/pins/io/mapped_spec.rb +41 -0
  29. data/spec/{board → pins/io}/pin_mapper_spec.rb +2 -2
  30. data/spec/pins/pinnable_spec.rb +68 -0
  31. data/spec/pins/pwm_pin_spec.rb +29 -0
  32. metadata +34 -37
  33. data/lib/BBB/adc/analog_pin.rb +0 -42
  34. data/lib/BBB/adc/setup.rb +0 -20
  35. data/lib/BBB/board/base.rb +0 -45
  36. data/lib/BBB/board/json_pin_mapper.rb +0 -203
  37. data/lib/BBB/board/pin_mapper.rb +0 -20
  38. data/lib/BBB/board/test_board.rb +0 -12
  39. data/lib/BBB/gpio/base.rb +0 -56
  40. data/lib/BBB/gpio/digital_pin.rb +0 -69
  41. data/lib/BBB/gpio/pin_converter.rb +0 -28
  42. data/lib/BBB/io/analog_pin.rb +0 -24
  43. data/lib/BBB/io/digital_pin.rb +0 -67
  44. data/lib/BBB/io/mock_pin.rb +0 -8
  45. data/lib/BBB/io/pinnable.rb +0 -12
  46. data/spec/adc/analog_pin_spec.rb +0 -100
  47. data/spec/adc/setup_spec.rb +0 -9
  48. data/spec/board/board_spec.rb +0 -49
  49. data/spec/gpio/base_spec.rb +0 -48
  50. data/spec/gpio/digital_pin_spec.rb +0 -100
  51. data/spec/gpio/pin_converter_spec.rb +0 -19
  52. data/spec/io/digital_pin_spec.rb +0 -14
  53. data/spec/io/mock_pin_spec.rb +0 -13
  54. data/spec/io/pinnable_spec.rb +0 -13
@@ -0,0 +1,39 @@
1
+ module BBB
2
+ module Pins
3
+ ##
4
+ # A pin that reads from the ADC converteds on the board. On the Beaglebone
5
+ # this is AIN1 to 7. The class itself doesn't do much, since most of the
6
+ # basic functionality is provided by the pinnable module which is included,
7
+ # as well as the IO::AIN object used for IO to the filesystem
8
+ #
9
+ class AnalogPin
10
+ include Pinnable
11
+
12
+ ##
13
+ # Read from the Pin
14
+ #
15
+ # @return Integer
16
+ #
17
+ def read
18
+ io.read
19
+ end
20
+
21
+ ##
22
+ # Return the scale of the Pin. On the BeagleBoneBlack 12 bit pins this is
23
+ # 4096.
24
+ #
25
+ # @return Integer
26
+ #
27
+ def scale
28
+ @scale ||= io.scale
29
+ end
30
+
31
+ private
32
+
33
+ def default_io
34
+ IO::AIN.new(position)
35
+ end
36
+
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,106 @@
1
+ module BBB
2
+ module Pins
3
+ ##
4
+ # A Digital Pin on the board. The digital pins uses GPIO on the filesystem
5
+ # to communicate. This class assumes its the only actor on the pin. Therfore
6
+ # it can cache the status in memory instead of reading it from the
7
+ # filesystem every time.
8
+ #
9
+ # It is advised to use the DigitalInputPin and DigitalOutputPin classes for
10
+ # clarity, buy, nothings stops you from using a DigitalPin with the :input
11
+ # or :output direction.
12
+ #
13
+ class DigitalPin
14
+ include Pinnable
15
+
16
+ attr_reader :status, :opts
17
+
18
+ ##
19
+ # Gets the direction of the pin from the options and memoizes it in the
20
+ # @direction attribute.
21
+ #
22
+ # @return [Symbol] either :input or :output
23
+ def direction
24
+ @direction ||= @opts.fetch(:direction)
25
+ end
26
+
27
+ ##
28
+ # Write value to the specified pin Digitally. This might fail hard if you
29
+ # try to write to an input pin. However, for performance reasons we do not
30
+ # want to check the direction of the pin every write.
31
+ #
32
+ def write(value)
33
+ @status = value
34
+ io.write(value)
35
+ end
36
+
37
+ ##
38
+ # Read value from the pin for input pins, or from memory for output pins.
39
+ #
40
+ # @return [Symbol] :high or :low
41
+ #
42
+ def status
43
+ if direction == :input
44
+ @status = io.read
45
+ end
46
+
47
+ return @status
48
+ end
49
+
50
+ ##
51
+ # Set the pin into :high state
52
+ # @return [void]
53
+ def on!
54
+ write(:high)
55
+ end
56
+
57
+ ##
58
+ # Set the pin into :low state
59
+ # @return [void]
60
+ def off!
61
+ write(:low)
62
+ end
63
+
64
+ ##
65
+ # Check if the pin state is high
66
+ # @return [Boolean]
67
+ def on?
68
+ status == :high
69
+ end
70
+
71
+ ##
72
+ # Check if the pin state is low
73
+ # @return [Boolean]
74
+ def off?
75
+ !on?
76
+ end
77
+
78
+ private
79
+
80
+ def default_io
81
+ IO::GPIO.new(direction, position)
82
+ end
83
+ end
84
+
85
+ ##
86
+ # @see DigitalPin
87
+ #
88
+ class DigitalInputPin < DigitalPin
89
+ def initialize(position, opts={})
90
+ opts.merge!(:direction=>:input)
91
+ super(position, opts)
92
+ end
93
+ end
94
+
95
+ ##
96
+ # @see DigitalPin
97
+ #
98
+ class DigitalOutputPin < DigitalPin
99
+ def initialize(position, opts={})
100
+ opts.merge!(:direction=>:output)
101
+ super(position, opts)
102
+ end
103
+ end
104
+
105
+ end
106
+ end
@@ -0,0 +1,58 @@
1
+ module BBB
2
+ module Pins
3
+ module IO
4
+ class AIN
5
+ include Mapped
6
+
7
+ attr_reader :io, :position
8
+
9
+ def initialize(position)
10
+ @position = position
11
+ self.export
12
+ @io = get_file_handle
13
+ end
14
+
15
+ def read
16
+ io.rewind
17
+ io.read.to_i
18
+ end
19
+
20
+ def scale
21
+ pin_map.scale
22
+ end
23
+
24
+ def export
25
+ cape_dir = "/sys/devices/bone_capemgr.*/slots"
26
+ dir = Dir.glob(cape_dir)
27
+ if dir.length == 0
28
+ raise BoardError, "unable to access the capemgr directory: #{cape_dir}"
29
+ end
30
+ `echo cape-bone-iio > #{dir.first}`
31
+ end
32
+
33
+ def get_file_handle
34
+ dir = Dir.glob("/sys/devices/ocp.*/helper.*/")
35
+ file = File.expand_path("AIN#{pin_map.ain}", dir.first)
36
+ return File.open(file, "r")
37
+ end
38
+
39
+ def self.setup
40
+ check_if_kernel_module_is_loaded!
41
+ end
42
+
43
+ def self.check_if_kernel_module_is_loaded!
44
+ ains = `find /sys/ -name '*AIN*'`.split("\n")
45
+
46
+ if ains.size > 0
47
+ return true
48
+ else
49
+ raise ModuleNotLoadedException, "Is seems that the ADC module is not
50
+ loaded into the kernel. You might want to try: \n
51
+ sudo modprobe t1_tscadc or add it to the kernel on boot: \n
52
+ echo 't1_tscadc' >> /etc/modules.conf"
53
+ end
54
+ end
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,77 @@
1
+ module BBB
2
+ module Pins
3
+ module IO
4
+ class GPIO
5
+ include Mapped
6
+
7
+ attr_reader :file_mode, :converted_position, :position, :direction
8
+
9
+ def initialize(direction, position)
10
+ self.direction = direction
11
+ @position = position
12
+ @converted_position = pin_map.gpio
13
+ self.export
14
+ end
15
+
16
+ def direction=(direction)
17
+ @file_mode = direction == :input ? "r" : "w+"
18
+ @direction = direction
19
+ end
20
+
21
+ def set_mode
22
+ direction_file = gpio_pin_dir + "/direction"
23
+ file_class.open(direction_file, "w") {|f| f.write(direction)}
24
+ end
25
+
26
+ def io
27
+ return @io unless @io.nil?
28
+ value_file = gpio_pin_dir + "/value"
29
+ @io = file_class.open(value_file, file_mode)
30
+ end
31
+
32
+ def write(value)
33
+ io.write(value_map[value])
34
+ io.flush
35
+ end
36
+
37
+ def read
38
+ io.rewind
39
+ value_map[io.read]
40
+ end
41
+
42
+ def value_map
43
+ @value_map ||= {:high=>1, :low=>0, 1=>:high, 0=>:low}
44
+ end
45
+
46
+ def gpio_path
47
+ "/sys/class/gpio"
48
+ end
49
+
50
+ def export_path
51
+ gpio_path + "/export"
52
+ end
53
+
54
+ def unexport_path
55
+ gpio_path + "/unexport"
56
+ end
57
+
58
+ def gpio_pin_dir
59
+ "#{gpio_path}/gpio#{converted_position}"
60
+ end
61
+
62
+ def export
63
+ file_class.open(export_path, "w") { |f| f.write("#{ converted_position }") }
64
+ set_mode
65
+ end
66
+
67
+ def unexport
68
+ file_class.open(unexport_path, "w") { |f| f.write("#{converted_position}") }
69
+ end
70
+
71
+ def file_class
72
+ File
73
+ end
74
+ end
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,39 @@
1
+ module BBB
2
+ module Pins
3
+ module IO
4
+ module Mapped
5
+ ##
6
+ # Get the pin map as taken from the Bonescript. The Pinmap contains
7
+ # information on things like the pin mode, and the name of the pin on
8
+ # the filesystem. Checkout the documentation on {BBB::Pins::IO::PinMapper PinMapper} for more
9
+ # information and examples of the maps.
10
+ #
11
+ # @return [Hash] pin_map
12
+ #
13
+ def pin_map
14
+ return @pin_map unless @pin_map.nil?
15
+
16
+ map = PinMapper.map(self.position)
17
+ map_key = self.pin_map_key
18
+
19
+ unless map.respond_to?(map_key) && !map.send(map_key).nil?
20
+ raise ArgumentError, "#{self.position} is not a valid #{map_key.upcase} pin position"
21
+ end
22
+
23
+ @pin_map = map
24
+ end
25
+
26
+ ##
27
+ # Returns the map key of the current pin. e.g. GPIO or AIN. This key is
28
+ # used to check if the provided position actually can be used using the
29
+ # given IO.
30
+ #
31
+ # @return [Symbol] Map key
32
+ #
33
+ def pin_map_key
34
+ self.class.to_s.split("::").last.downcase.to_sym
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,224 @@
1
+ module BBB
2
+ module Pins
3
+ module IO
4
+ ##
5
+ # This class provides a convenient way of mapping a JSON representation of the
6
+ # pins, taken from the bonescript sourcecode, into Ruby objects. After
7
+ # converting the json to ruby objects the "normal" PinMapper will take the
8
+ # object structure and provide the actual mapping from the pin header
9
+ # positions, like p8_3 to the correct GPIO, I2C or WPM endpoints.
10
+ #
11
+ # This class should not be used directly to provide these mappings it's simply
12
+ # a helper class to get from JSON to Ruby.
13
+ #
14
+ class PinMapper
15
+ PIN_MAP_FILE = File.expand_path("../../../../../resources/pin_mappings.json", __FILE__)
16
+
17
+ class PinMap < Struct.new(:pins, :uart, :i2c); end
18
+
19
+ class Pin < Struct.new(:name, :gpio, :led, :mux, :key, :mux_reg_offset,
20
+ :options, :eeprom, :pwm, :ain, :scale); end
21
+ class UART < Struct.new(:devicetree, :rx, :tx); end
22
+ class I2C < Struct.new(:devicetree, :path, :sda, :scl); end
23
+
24
+ attr_reader :data
25
+
26
+ ##
27
+ # Initialize a JSONPinMapper
28
+ #
29
+ # @param [Hash] hash Hash that was converted from json with the keys
30
+ # pinIndex, uart and i2c.
31
+ #
32
+ def initialize(hash)
33
+ @data = hash
34
+ end
35
+
36
+ ##
37
+ # Factor a new JSONPinMapper based on a json_file that get's parsed to a
38
+ # hash.
39
+ #
40
+ # @param [String] json_filename The file that contains the JSON object with
41
+ # pin information
42
+ #
43
+ # @return JSONPinMapper instance
44
+ #
45
+ def self.convert(json_filename)
46
+ file = File.open(json_filename, "r")
47
+ hash = JSON.parse(file.read)
48
+ new(hash).convert
49
+ end
50
+
51
+ ##
52
+ # Map a pin symbol and a type to a pin_map or raise an error when a pin
53
+ # can't be found.
54
+ #
55
+ # @param [Symbol] pin_symbol
56
+ # @params [Symbol] type Type of the pin.
57
+ #
58
+ def self.map(pin_symbol)
59
+ @map ||= convert(PIN_MAP_FILE)
60
+ begin
61
+ @map.pins.fetch(pin_symbol.upcase.to_sym)
62
+ rescue Exception => e
63
+ raise UnknownPinException, "Pin #{pin_symbol} could not be mapped"
64
+ end
65
+ end
66
+
67
+ ##
68
+ # Convert from a the data hash to ruby objects
69
+ #
70
+ # @return [Struct] with pins, uart and i2c as keys.
71
+ # pins has a
72
+ #
73
+ def convert
74
+ pins = convert_pins(data["pinIndex"])
75
+ uart = convert_uart(data["uarts"])
76
+ i2c = convert_i2c(data["i2c"])
77
+
78
+ PinMap.new(pins, uart, i2c)
79
+ end
80
+
81
+ ##
82
+ # Converts an array of pins into a hash with the pin key as a key and a Pin
83
+ # Struct as a value.
84
+ #
85
+ # Here is an example of a piece of the JSON code containing pin info:
86
+ #
87
+ # {
88
+ # "name": "USR0",
89
+ # "gpio": 53,
90
+ # "led": "usr0",
91
+ # "mux": "gpmc_a5",
92
+ # "key": "USR0",
93
+ # "muxRegOffset": "0x054",
94
+ # "options": [
95
+ # "gpmc_a5",
96
+ # "gmii2_txd0",
97
+ # "rgmii2_td0",
98
+ # "rmii2_txd0",
99
+ # "gpmc_a21",
100
+ # "pr1_mii1_rxd3",
101
+ # "eqep1b_in",
102
+ # "gpio1_21"
103
+ # ]
104
+ # }
105
+ #
106
+ # @param [Array<Hash>] array An array of Pin hashes
107
+ #
108
+ # @return [Hash] a hash with pin keys as keys and pin objects as values.
109
+ #
110
+ def convert_pins(array)
111
+ hash = {}
112
+
113
+ array.each do |pin_hash|
114
+ pin = Pin.new
115
+ pin_hash.each_pair do |key, value|
116
+ pin[underscore(key).to_sym] = value
117
+ end
118
+ hash[pin.key.upcase.to_sym] = pin
119
+ end
120
+
121
+ return hash
122
+ end
123
+
124
+ ##
125
+ # Convert a hash of uarts to a hash with the uart folder (e.g. /dev/tty00)
126
+ # as the key and an uart object as the value.
127
+ #
128
+ # Here is an example of an individual UART hash:
129
+ #
130
+ # "/dev/ttyO1": {
131
+ # "devicetree": "BB-UART1",
132
+ # "rx": "P9_26",
133
+ # "tx": "P9_24"
134
+ # }
135
+ #
136
+ # @param [Hash] data_hash with strings as keys and hashes as values
137
+ #
138
+ # @return [Hash] with strings of uart positions (e.g. /dev/tty00) as keys
139
+ # and uart objects as values
140
+ #
141
+ def convert_uart(data_hash)
142
+ convert_hash_with_hashes(data_hash, UART)
143
+ end
144
+
145
+ ##
146
+ # Convert a hash of I2Cs to a hash with the I2C folder as the key and a i2c
147
+ # object as the value
148
+ #
149
+ # Here is an example of an individual I2C hash:
150
+ #
151
+ # "/dev/i2c-1": {
152
+ # "devicetree": "BB-I2C1",
153
+ # "path": "/dev/i2c-2",
154
+ # "sda": "P9_18",
155
+ # "scl": "P9_17"
156
+ # }
157
+ #
158
+ # @param [Hash] data_hash with strings as keys and hashes as values
159
+ #
160
+ # @return [Hash] with strings of I2C folders as keys and I2C objects as
161
+ # values
162
+ #
163
+ def convert_i2c(data_hash)
164
+ convert_hash_with_hashes(data_hash, I2C)
165
+ end
166
+
167
+ private
168
+
169
+ ##
170
+ # @see #convert_uart
171
+ #
172
+ def convert_hash_with_hashes(data_hash, object)
173
+ hash = {}
174
+
175
+ data_hash.each_pair do |filesystem, info_hash|
176
+ instance = object.new
177
+ info_hash.each_pair do |key, value|
178
+ instance[key] = value
179
+ end
180
+
181
+ hash[filesystem] = instance
182
+ end
183
+
184
+ return hash
185
+ end
186
+
187
+ ##
188
+ # Makes an underscored, lowercase form from the expression in the string.
189
+ #
190
+ # Gracefully copied from ActiveSupport::Inflections
191
+ # http://api.rubyonrails.org/classes/ActiveSupport/Inflector.html#method-i-underscore
192
+ #
193
+ # Changes '::' to '/' to convert namespaces to paths.
194
+ #
195
+ # 'ActiveModel'.underscore # => "active_model"
196
+ # 'ActiveModel::Errors'.underscore # => "active_model/errors"
197
+ #
198
+ # As a rule of thumb you can think of +underscore+ as the inverse of
199
+ # +camelize+, though there are cases where that does not hold:
200
+ #
201
+ # 'SSLError'.underscore.camelize # => "SslError"
202
+ def underscore(camel_cased_word)
203
+ word = camel_cased_word.to_s.dup
204
+ word.gsub!('::', '/')
205
+ word.gsub!(/(?:([A-Za-z\d])|^)(#{acronym_regex})(?=\b|[^a-z])/) { "#{$1}#{$1 && '_'}#{$2.downcase}" }
206
+ word.gsub!(/([A-Z\d]+)([A-Z][a-z])/,'\1_\2')
207
+ word.gsub!(/([a-z\d])([A-Z])/,'\1_\2')
208
+ word.tr!("-", "_")
209
+ word.downcase!
210
+ word
211
+ end
212
+
213
+ ##
214
+ # Specifies acronyms. This is a shortcut approach to the full acronym
215
+ # functionality of Rails. Which can be found here:
216
+ # https://github.com/rails/rails/blob/4e327225947b933d5434509e02e98226c581adc1/activesupport/lib/active_support/inflector/inflections.rb#L47
217
+ #
218
+ def acronym_regex
219
+ /#{%w(I2C UART GPIO).join("|")}/
220
+ end
221
+ end
222
+ end
223
+ end
224
+ end