BBB 0.1.3 → 0.2.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.
@@ -2,17 +2,18 @@ module BBB
2
2
  module Components
3
3
  module Pinnable
4
4
  attr_reader :pins
5
+ attr_reader :positions
5
6
 
6
7
  module ClassMethods
7
8
  ##
8
9
  # Register the use of classes of pins to a class. These classes will be
9
- # initialized upon #initialize_pins
10
+ # initialized upon #connect
10
11
  #
11
- # @param pin_classes [Array<Class>] the classes to register on class
12
+ # @param classes [Array<Class>] the classes to register on class
12
13
  # level.
13
14
  #
14
- def uses(*pin_classes)
15
- pins.concat(pin_classes)
15
+ def uses(*classes)
16
+ pin_classes.concat(classes)
16
17
  end
17
18
 
18
19
  ##
@@ -20,9 +21,26 @@ module BBB
20
21
  #
21
22
  # @return Array<Class>
22
23
  #
23
- def pins
24
- @pins ||= []
24
+ def pin_classes
25
+ @pin_classes ||= []
25
26
  end
27
+
28
+ ##
29
+ # Register callbacks
30
+ #
31
+ def after_connect(callback)
32
+ after_connect_callbacks << callback
33
+ end
34
+
35
+ ##
36
+ # Convenience function on class level that holds the callbacks (anything
37
+ # that responds to call OR a symbol) that need to be called after the
38
+ # pins get connected.
39
+ #
40
+ def after_connect_callbacks
41
+ @after_connect_callbacks ||= []
42
+ end
43
+
26
44
  end
27
45
 
28
46
  def self.included(base)
@@ -47,24 +65,29 @@ module BBB
47
65
  #
48
66
  # @return Array[Pins]
49
67
  #
50
- def initialize_pins(*positions)
68
+ def connect(*positions)
69
+ positions = self.positions if positions.empty?
70
+
51
71
  positions.flatten!
52
72
  opts = positions.last.kind_of?(Hash) ? positions.pop : {}
53
73
 
54
74
  verify_pin_position_count(positions)
55
75
 
56
76
  @pins = []
57
- self.class.pins.each_with_index do |pin, index|
77
+ self.class.pin_classes.each_with_index do |pin, index|
58
78
  @pins << pin.new(positions[index], opts)
59
79
  end
60
- after_pin_initialization
80
+ after_connect_callbacks
81
+ return self # so that we can chain it with the initializer
61
82
  end
62
83
 
84
+ alias_method :pin_initialization, :connect
85
+
63
86
  ##
64
87
  # Verifies if the number of pins registered in the @pins array match with
65
88
  # the number of pins provided as an argument to the method. This function
66
89
  # normally gets called as part of the initialize pins method to verify if
67
- # the positions given to the initialize_pins method matches the number of
90
+ # the positions given to the connect method matches the number of
68
91
  # registered pins.
69
92
  #
70
93
  # @param positions [Array<Symbol>] The array of positions
@@ -76,17 +99,20 @@ module BBB
76
99
  #
77
100
  # @return Void
78
101
  def verify_pin_position_count(positions)
79
- if self.class.pins.count != positions.count
80
- raise PinsDoNotMatchException,
81
- "#{self.class.to_s} requires #{self.class.pins.count} but received #{positions.count} pin position."
102
+ if self.class.pin_classes.count != positions.count
103
+ fail PinsDoNotMatchException,
104
+ "#{self.class.to_s} requires #{self.class.pin_classes.count} but received #{positions.count} pin position."
82
105
  end
83
106
  end
84
107
 
85
- ##
86
- # Method which classes can overwrite to hook into the after pin
87
- # initialization
88
- #
89
- def after_pin_initialization
108
+ def after_connect_callbacks
109
+ self.class.after_connect_callbacks.each do |callback|
110
+ if callback.responds_to?(:call)
111
+ callback.call
112
+ else
113
+ self.send(callback)
114
+ end
115
+ end
90
116
  end
91
117
 
92
118
  ##
@@ -94,9 +120,35 @@ module BBB
94
120
  # included in an object
95
121
  #
96
122
  # @return true
123
+ #
97
124
  def pinnable?
98
125
  true
99
126
  end
127
+
128
+ ##
129
+ # Convenience method to grab the first pin in the pins array
130
+ #
131
+ # @return BBB::Pins::AnalogPin
132
+ #
133
+ def pin
134
+ pins.first
135
+ end
136
+
137
+ ##
138
+ # Provide a default initializer
139
+ #
140
+ def initialize(options={})
141
+ set_options(options)
142
+ end
143
+
144
+ ##
145
+ # Provide a function to handle the options
146
+ #
147
+ # @param options [Hash]
148
+ #
149
+ def set_options(options)
150
+ @positions = [options.fetch(:pin, nil)].compact
151
+ end
100
152
  end
101
153
  end
102
154
  end
@@ -3,18 +3,21 @@ module BBB
3
3
  class Servo
4
4
  include Pinnable
5
5
  uses Pins::PWMPin
6
- attr_reader :min_duty, :max_duty, :period
6
+ attr_reader :period, :min_duty, :max_duty, :max_degrees
7
7
 
8
- def initialize(period=17e6, min_duty=14.6e6, max_duty=16.6e6)
9
- @period = period
10
- @min_duty = min_duty
11
- @max_duty = max_duty
12
- end
8
+ ##
9
+ # Min duty and max duty for FS5103B servo
10
+ # duty cycle of 900 to 2100 ms for 120 degrees
11
+ # http://www.servodatabase.com/servo/feetech/fs5103b
12
+ #
13
+ def initialize(options={})
14
+ set_options(options)
15
+
16
+ @period = options.fetch(:period, 20e6)
17
+ @min_duty = options.fetch(:min_duty, 17.9e6)
18
+ @max_duty = options.fetch(:max_duty, 19.1e6)
19
+ @max_degrees = options.fetch(:max_degrees, 120)
13
20
 
14
- def after_pin_initialization
15
- pin.period = 17e6
16
- pin.duty = (min_duty + duty_range / 2)
17
- pin.run = 1
18
21
  end
19
22
 
20
23
  def angle(degrees)
@@ -22,7 +25,7 @@ module BBB
22
25
  end
23
26
 
24
27
  def degrees_to_ns(degrees)
25
- degrees / 180.to_f * duty_range + min_duty
28
+ degrees / max_degrees.to_f * duty_range + min_duty
26
29
  end
27
30
 
28
31
  def activate!
@@ -37,10 +40,17 @@ module BBB
37
40
  pins.first
38
41
  end
39
42
 
43
+ private
44
+
40
45
  def duty_range
41
46
  max_duty - min_duty
42
47
  end
43
48
 
49
+ def after_pin_initialization
50
+ pin.period = self.period
51
+ pin.duty = (min_duty + duty_range / 2)
52
+ pin.run = 1
53
+ end
44
54
  end
45
55
  end
46
56
  end
@@ -0,0 +1,229 @@
1
+ module BBB
2
+ module Components
3
+ ##
4
+ # WiiMotionPlus I2C component. Attach to P9 like this:
5
+ # Nunchuck/ WMP connector:
6
+ # ---------
7
+ # | 1 2 3 |
8
+ # | |
9
+ # | 6 5 4 |
10
+ # | ----- |
11
+ # |_| |_|
12
+ #
13
+ # Connections from NunChuck to BeagleBone "P9" connector:
14
+ #
15
+ # pin 1: green: system data - connect to BeagleBone pin 20 (I2C2_SDA)
16
+ # pin 2: (not connected)
17
+ # pin 3: red: DC 3.3V supply - to BeagleBone pin 3 or pin 4 (DC_3.3V)
18
+ # pin 4: yellow - system clock - to BeagleBone pin 19 (I2C2_SCL)
19
+ # pin 5: ("ATT" - not needed)
20
+ # pin 6: white - GND - to BeagleBone pin 1 or pin 2 (GND)
21
+ #
22
+ # Copied from: http://www.alfonsomartone.itb.it/mzscbb.html
23
+ #
24
+ class WiiMotionPlus
25
+ include Pinnable
26
+ uses Pins::I2C
27
+
28
+ attr_reader :gyro, :positions
29
+
30
+ def initialize(options = {})
31
+ @started = false
32
+ @calibrated = false
33
+ @gyro = Gyro.new
34
+ @positions = [options.fetch(:i2c, nil)].compact
35
+ end
36
+
37
+ def start
38
+ begin #check if we can already read, if so, we're already started.
39
+ raw_read
40
+ rescue I2C::AckError
41
+ i2c.write(0x53, 0xfe, 0x04)
42
+ end
43
+ @started = true
44
+ end
45
+
46
+ def started?
47
+ @started
48
+ end
49
+
50
+ def calibrated?
51
+ @calibrated
52
+ end
53
+
54
+ def calibrate(number=20)
55
+ start unless started?
56
+ @gyro.start_calibration!
57
+ number.times do
58
+ update
59
+ end
60
+ @gyro.stop_calibration!
61
+ @calibrated = true
62
+ end
63
+
64
+ def update
65
+ bytes = raw_read.bytes.to_a
66
+ @gyro.update(bytes)
67
+ set_extension(bytes)
68
+ end
69
+
70
+ def set_extension(bytes)
71
+ @extension_set = bytes[4] & 0b00000001
72
+ end
73
+
74
+ def i2c ; pins.first ; end
75
+ def pitch ; gyro.pitch ; end
76
+ def yaw ; gyro.yaw ; end
77
+ def roll ; gyro.roll ; end
78
+
79
+ def raw_read
80
+ i2c.read(0x52, 6, 0x00)
81
+ end
82
+
83
+ class AxisValue
84
+ attr_reader :value, :slow, :zero_value
85
+
86
+ MAX_AMPLITUDE = 8192 # half of a 14 bit integer
87
+ MAX_MEASUREMENT = 519 # degrees / second
88
+ FACTOR = MAX_AMPLITUDE / MAX_MEASUREMENT
89
+
90
+ def initialize
91
+ @value = 0
92
+ @slow = 0
93
+ @zero_value = 8063 # has to be adjusted during calibration
94
+ @calibration_values = []
95
+ end
96
+
97
+ def update(value, slow)
98
+ @value = value
99
+ @slow = slow
100
+ @calibration_values << value if @calibrating
101
+ end
102
+
103
+ def degrees
104
+ value / FACTOR * slow_correction
105
+ end
106
+
107
+ def value
108
+ @value - @zero_value
109
+ end
110
+
111
+ def start_calibration!
112
+ @calibrating = true
113
+ end
114
+
115
+ def stop_calibration!
116
+ @calibrating = false
117
+ arr = @calibration_values
118
+ @zero_value = arr.inject(0) { |sum, el| sum + el } / arr.size
119
+ @calibration_values = []
120
+ end
121
+
122
+ # At high speed (slow bit = 0) raw values read are small with the same
123
+ # deg/s to reach higher values on top, so you must multiply it by
124
+ # 2000/440 (they are the max reference in the two modes in deg/s [1]).
125
+ #
126
+ # Example: reading 8083 raw value and assuming 8063 as zero, 20 unit in
127
+ # slow/normal mode are 1,45 deg/s and in fast mode are
128
+ # 1.45*2000/440=6.59 deg/s.
129
+ #
130
+ def slow_correction
131
+ slow ? 2000/440 : 1
132
+ end
133
+ end
134
+
135
+ #
136
+ # DATA FORMAT WII MOTION PLUS (GYRO)
137
+ #
138
+ # as taken from:
139
+ # http://wiibrew.org/wiki/Wiimote/Extension_Controllers/Wii_Motion_Plus
140
+ #
141
+ # The YAW, PITCH and ROLL values are 14 bit integers
142
+ # (maximum value = 2^14 - 1 = 16383).
143
+ #
144
+ # The first 8 bits are available in the first Byte. The last 6 bits are
145
+ # available in bits 8 to 13 in byte 3.
146
+ #
147
+ # Bit positions count from right to left. So bit position 0 is the most
148
+ # 'right' bit, and bit 13 bit is the most 'left' bit.
149
+ #
150
+ # this is also why you see the <13:8> position definition in bytes 3,4 and 5.
151
+ #
152
+ # |------|--------------------------------------------------------------|
153
+ # | | Bit |
154
+ # |------|--------------------------------------------------------------|
155
+ # | Byte | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
156
+ # |------|--------------------------------------------------------------|
157
+ # | 0 | Yaw Down Speed<7:0> |
158
+ # |------|--------------------------------------------------------------|
159
+ # | 1 | Roll Left Speed<7:0> |
160
+ # |------|--------------------------------------------------------------|
161
+ # | 2 | Pitch Left Speed<7:0> |
162
+ # |------|--------------------------------------------------------------|
163
+ # | 3 | Yaw Down Speed<13:8> | yaw | pitch |
164
+ # | | | slow | slow |
165
+ # | | | mode | mode |
166
+ # |------|--------------------------------------------------------------|
167
+ # | 4 | Roll left speed<13:8> | roll | ext. |
168
+ # | | | slow | conn- |
169
+ # | | | mode | ected |
170
+ # |------|--------------------------------------------------------------|
171
+ # | 5 | Pitch left speed<13:8> | 1 | 0 |
172
+ # |------|--------------------------------------------------------------|
173
+ #
174
+ #
175
+ class Gyro
176
+ HIGH_MASK = 0b11111100
177
+
178
+ attr_reader :yaw, :pitch, :roll
179
+
180
+ def initialize
181
+ @yaw = AxisValue.new
182
+ @pitch = AxisValue.new
183
+ @roll = AxisValue.new
184
+ @calibrating = false
185
+ end
186
+
187
+ def calibrating?
188
+ @calibrating
189
+ end
190
+
191
+ def start_calibration!
192
+ @calibrating = true
193
+ [yaw,pitch,roll].each(&:start_calibration!)
194
+ end
195
+
196
+ def stop_calibration!
197
+ [yaw,pitch,roll].each(&:stop_calibration!)
198
+ @calibrating = false
199
+ end
200
+
201
+ def update(bytes)
202
+ set_yaw(bytes)
203
+ set_pitch(bytes)
204
+ set_roll(bytes)
205
+ end
206
+
207
+ def set_yaw(bytes)
208
+ value = (bytes[3] & HIGH_MASK) << 6 | bytes[0]
209
+ slow = bytes[3] & 0b00000010 >> 1
210
+ yaw.update(value, slow)
211
+ end
212
+
213
+ def set_pitch(bytes)
214
+ value = (bytes[4] & HIGH_MASK) << 6 | bytes[1]
215
+ slow = bytes[3] & 0b00000001
216
+ pitch.update(value, slow)
217
+ end
218
+
219
+ def set_roll(bytes)
220
+ value = (bytes[5] & HIGH_MASK) << 6 | bytes[2]
221
+ slow = bytes[4] & 0b00000010 >> 1
222
+ roll.update(value, slow)
223
+ end
224
+
225
+ end
226
+ end
227
+ WMP = WiiMotionPlus
228
+ end
229
+ end
data/lib/BBB/pins.rb ADDED
@@ -0,0 +1,13 @@
1
+ require "BBB/pins/io/pin_mapper"
2
+ require "BBB/pins/io/mapped"
3
+ require "BBB/pins/io/cape"
4
+ require "BBB/pins/io/ain"
5
+ require "BBB/pins/io/gpio"
6
+ require "BBB/pins/io/pwm"
7
+ require "BBB/pins/io/i2c"
8
+
9
+ require "BBB/pins/pinnable"
10
+ require "BBB/pins/digital_pin"
11
+ require "BBB/pins/analog_pin"
12
+ require "BBB/pins/pwm_pin"
13
+ require "BBB/pins/i2c"