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.
- checksums.yaml +13 -5
- data/Gemfile +2 -2
- data/Guardfile +46 -0
- data/examples/analog_pin.rb +2 -16
- data/examples/ldr_light_switch.rb +56 -0
- data/examples/led.rb +5 -14
- data/examples/light_switch.rb +42 -47
- data/examples/nunchuck.rb +99 -0
- data/examples/servo_ldr.rb +11 -10
- data/lib/BBB.rb +2 -19
- data/lib/BBB/application.rb +5 -21
- data/lib/BBB/circuit.rb +49 -42
- data/lib/BBB/components.rb +9 -0
- data/lib/BBB/components/analog_component.rb +0 -9
- data/lib/BBB/components/button.rb +25 -0
- data/lib/BBB/components/esc.rb +68 -19
- data/lib/BBB/components/led.rb +0 -9
- data/lib/BBB/components/nunchuck.rb +197 -0
- data/lib/BBB/components/pinnable.rb +70 -18
- data/lib/BBB/components/servo.rb +21 -11
- data/lib/BBB/components/wii_motion_plus.rb +229 -0
- data/lib/BBB/pins.rb +13 -0
- data/lib/BBB/pins/i2c.rb +22 -0
- data/lib/BBB/pins/io/gpio.rb +6 -2
- data/lib/BBB/pins/io/i2c.rb +10 -9
- data/lib/BBB/pins/io/pin_mapper.rb +2 -2
- data/lib/BBB/pins/pinnable.rb +3 -3
- data/lib/BBB/version.rb +1 -1
- data/spec/application_spec.rb +4 -14
- data/spec/circuit_spec.rb +6 -9
- data/spec/components/analog_component_spec.rb +7 -2
- data/spec/components/led_spec.rb +1 -1
- data/spec/components/pinnable_spec.rb +16 -6
- data/spec/components/servo_spec.rb +47 -1
- data/spec/examples/led_spec.rb +40 -0
- data/spec/pins/digital_pin_spec.rb +1 -0
- data/spec/pins/io/pin_mapper_spec.rb +0 -4
- metadata +24 -14
@@ -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 #
|
10
|
+
# initialized upon #connect
|
10
11
|
#
|
11
|
-
# @param
|
12
|
+
# @param classes [Array<Class>] the classes to register on class
|
12
13
|
# level.
|
13
14
|
#
|
14
|
-
def uses(*
|
15
|
-
|
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
|
24
|
-
@
|
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
|
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.
|
77
|
+
self.class.pin_classes.each_with_index do |pin, index|
|
58
78
|
@pins << pin.new(positions[index], opts)
|
59
79
|
end
|
60
|
-
|
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
|
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.
|
80
|
-
|
81
|
-
"#{self.class.to_s} requires #{self.class.
|
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
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
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
|
data/lib/BBB/components/servo.rb
CHANGED
@@ -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, :
|
6
|
+
attr_reader :period, :min_duty, :max_duty, :max_degrees
|
7
7
|
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
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 /
|
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"
|