BBB 0.0.10 → 0.1.0

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.
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