BBB 0.1.3 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
data/lib/BBB/application.rb
CHANGED
@@ -1,33 +1,17 @@
|
|
1
1
|
module BBB
|
2
2
|
class Application
|
3
|
-
|
4
|
-
def self.circuit(circuit)
|
5
|
-
@_circuit = circuit
|
6
|
-
define_convenience_methods(circuit)
|
7
|
-
end
|
8
|
-
|
9
|
-
def self._circuit
|
10
|
-
@_circuit
|
11
|
-
end
|
12
|
-
|
13
|
-
def self.define_convenience_methods(c)
|
14
|
-
c.components.keys.each do |name|
|
15
|
-
define_method(name) do
|
16
|
-
circuit.send(name)
|
17
|
-
end
|
18
|
-
end
|
19
|
-
end
|
20
|
-
|
21
|
-
def circuit
|
22
|
-
self.class._circuit
|
23
|
-
end
|
3
|
+
include Attachable
|
24
4
|
|
25
5
|
def start
|
6
|
+
activate_components
|
26
7
|
loop do
|
27
8
|
run
|
28
9
|
end
|
29
10
|
end
|
30
11
|
|
12
|
+
def activate_components
|
13
|
+
end
|
14
|
+
|
31
15
|
def run
|
32
16
|
raise NotImplementedError
|
33
17
|
end
|
data/lib/BBB/circuit.rb
CHANGED
@@ -1,9 +1,56 @@
|
|
1
1
|
module BBB
|
2
|
+
module Attachable
|
3
|
+
module ClassMethods
|
4
|
+
|
5
|
+
def class_components
|
6
|
+
@class_components ||= {}
|
7
|
+
end
|
8
|
+
|
9
|
+
##
|
10
|
+
# Attach a component of a certain type to the circuit
|
11
|
+
#
|
12
|
+
# @param component [Class] The class of the object you # want to attach.
|
13
|
+
# @param opts [Hash] Hash of options that setup the component
|
14
|
+
#
|
15
|
+
# @option opts [Symbol] :pin The pin position for the component
|
16
|
+
# @option opts [Array<Symbol>] :pins The list of pin numbers used on the
|
17
|
+
# circuit.
|
18
|
+
# @options opts [Symbol] :as The name of the component
|
19
|
+
#
|
20
|
+
def attach(object, opts={})
|
21
|
+
name = opts.delete(:as)
|
22
|
+
class_components[name] = object
|
23
|
+
define_method_for_object(object, name)
|
24
|
+
end
|
25
|
+
|
26
|
+
def define_method_for_object(component, name)
|
27
|
+
define_method(name) do
|
28
|
+
value = components[name]
|
29
|
+
return value if value
|
30
|
+
|
31
|
+
object = self.class.class_components[name]
|
32
|
+
value = object.kind_of?(Class) ? object.new : object
|
33
|
+
|
34
|
+
components[name] = value
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def self.included(base)
|
40
|
+
base.extend(ClassMethods)
|
41
|
+
end
|
42
|
+
|
43
|
+
def components
|
44
|
+
@components ||= {}
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
48
|
+
|
2
49
|
##
|
3
50
|
# The idea here is to attach a piece of equipment to a circuit.
|
4
51
|
#
|
5
52
|
# A component (e.g. Led or Servo) will define generic pins, like
|
6
|
-
# DigitalInput or AnalogOutput. And then, when the component gets
|
53
|
+
# DigitalInput or AnalogOutput. And then, when the component gets connected to
|
7
54
|
# the circuit those pins will be initialized using the file system.
|
8
55
|
#
|
9
56
|
# For now the attachment will be made onto specific pin numbers. For the BBB
|
@@ -12,46 +59,6 @@ module BBB
|
|
12
59
|
# boards. For example by mapping P8_3 on BBB to P1 on an Arduino.
|
13
60
|
#
|
14
61
|
class Circuit
|
15
|
-
|
16
|
-
|
17
|
-
def components
|
18
|
-
@components ||= {}
|
19
|
-
end
|
20
|
-
|
21
|
-
def mock?
|
22
|
-
@mock == true
|
23
|
-
end
|
24
|
-
|
25
|
-
##
|
26
|
-
# Attach a component of a certain type to the circuit
|
27
|
-
#
|
28
|
-
# @param component [Class] The class of the object you # want to attach.
|
29
|
-
# @param opts [Hash] Hash of options that setup the component
|
30
|
-
#
|
31
|
-
# @option opts [Symbol] :pin The pin position for the component
|
32
|
-
# @option opts [Array<Symbol>] :pins The list of pin numbers used on the
|
33
|
-
# circuit.
|
34
|
-
# @options opts [Symbol] :as The name of the component
|
35
|
-
#
|
36
|
-
def attach(component, opts={})
|
37
|
-
name = opts.delete(:as)
|
38
|
-
component = component.new if component.kind_of?(Class)
|
39
|
-
pin_positions = opts.delete(:pins) || [opts.delete(:pin)]
|
40
|
-
pin_options = {:mock=>self.mock?}.merge!(opts)
|
41
|
-
|
42
|
-
component.initialize_pins(pin_positions, pin_options)
|
43
|
-
define_method_for_component(component, name)
|
44
|
-
end
|
45
|
-
|
46
|
-
def define_method_for_component(component, name)
|
47
|
-
components[name] = component
|
48
|
-
eigenclass = class << self; self; end
|
49
|
-
eigenclass.class_eval do
|
50
|
-
define_method(name) do
|
51
|
-
components[name]
|
52
|
-
end
|
53
|
-
end
|
54
|
-
end
|
55
|
-
|
62
|
+
include Attachable
|
56
63
|
end
|
57
64
|
end
|
@@ -0,0 +1,9 @@
|
|
1
|
+
require "BBB/components/pinnable"
|
2
|
+
require "BBB/components/button"
|
3
|
+
require "BBB/components/analog_component"
|
4
|
+
require "BBB/components/led"
|
5
|
+
require "BBB/components/esc"
|
6
|
+
require "BBB/components/servo"
|
7
|
+
require "BBB/components/wii_motion_plus"
|
8
|
+
require "BBB/components/nunchuck"
|
9
|
+
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module BBB
|
2
|
+
module Components
|
3
|
+
class Button
|
4
|
+
include Pinnable
|
5
|
+
|
6
|
+
uses BBB::Pins::DigitalInputPin
|
7
|
+
|
8
|
+
def high?
|
9
|
+
pin.on?
|
10
|
+
end
|
11
|
+
alias_method :on?, :high?
|
12
|
+
|
13
|
+
def low?
|
14
|
+
!high
|
15
|
+
end
|
16
|
+
alias_method :off?, :low?
|
17
|
+
|
18
|
+
def status
|
19
|
+
pin.status
|
20
|
+
end
|
21
|
+
alias_method :state, :status
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
data/lib/BBB/components/esc.rb
CHANGED
@@ -2,45 +2,94 @@ module BBB
|
|
2
2
|
module Components
|
3
3
|
class ESC
|
4
4
|
include Pinnable
|
5
|
-
uses Pins::PWMPin
|
5
|
+
uses Pins::PWMPin
|
6
6
|
|
7
7
|
attr_accessor :min_duty, :max_duty, :period
|
8
|
-
attr_reader :duty
|
9
8
|
|
10
|
-
|
11
|
-
@period = period
|
12
|
-
@min_duty = min_duty
|
13
|
-
@max_duty = max_duty
|
14
|
-
end
|
9
|
+
after_connect :setup_pin
|
15
10
|
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
11
|
+
##
|
12
|
+
# Initialize a new ESC using default values for period, min_duty and
|
13
|
+
# max_duty if present. Please beware that at initialization stage there
|
14
|
+
# is no initialized PWM pin yet. So you can set the period and the
|
15
|
+
# min/max_duty instance variables, but not the variables on the pin.
|
16
|
+
# Only after "connecting" the esc to a board can you set the duty and
|
17
|
+
# period of the pin. Use the "after pin initialization" method for that.
|
18
|
+
#
|
19
|
+
def initialize(options={})
|
20
|
+
set_options
|
21
|
+
|
22
|
+
@period = options.fetch(:period, 20e6)
|
23
|
+
@speed = 0
|
24
|
+
|
25
|
+
@min_duty = options.fetch(:min_duty, 17.5e6)
|
26
|
+
@max_duty = options.fetch(:max_duty, 19e6)
|
21
27
|
end
|
22
28
|
|
23
29
|
def speed(value)
|
24
|
-
|
30
|
+
@speed = value
|
31
|
+
@duty = max_duty - value * duty_range
|
32
|
+
synchronize_duty
|
33
|
+
end
|
34
|
+
|
35
|
+
def calibrate
|
36
|
+
pwm.run = 0
|
37
|
+
puts "Disconnect the battery of the motor (press any key)"; gets
|
38
|
+
puts "Get ready to connect the battery after 2 seconds, ready? (press any key)"; gets
|
39
|
+
speed(1)
|
40
|
+
pwm.run = 1
|
41
|
+
puts "one missisipi"; sleep(1)
|
42
|
+
puts "two missisipi"; sleep(1)
|
43
|
+
puts "connect the battery"; sleep(1)
|
44
|
+
speed(0)
|
45
|
+
puts "Calibrated and ready to go"
|
46
|
+
end
|
47
|
+
|
48
|
+
def arm
|
49
|
+
pwm.run=1
|
50
|
+
end
|
51
|
+
|
52
|
+
def armed?
|
53
|
+
pwm.run == 1
|
54
|
+
end
|
55
|
+
|
56
|
+
def disarm
|
57
|
+
pwm.run=0
|
58
|
+
end
|
59
|
+
|
60
|
+
def disarmed?
|
61
|
+
!armed?
|
25
62
|
end
|
26
63
|
|
27
64
|
def duty=(value)
|
28
65
|
@duty = value
|
29
|
-
|
66
|
+
synchronize_duty
|
30
67
|
end
|
31
68
|
|
32
|
-
def
|
33
|
-
|
69
|
+
def period=(value)
|
70
|
+
@period = value
|
71
|
+
synchronize_period
|
34
72
|
end
|
35
73
|
|
36
74
|
def pwm
|
37
|
-
|
75
|
+
pin
|
38
76
|
end
|
39
77
|
|
40
|
-
|
41
|
-
|
78
|
+
private
|
79
|
+
|
80
|
+
def duty_range
|
81
|
+
max_duty - min_duty
|
82
|
+
end
|
83
|
+
|
84
|
+
def synchronize_period
|
85
|
+
pwm.period = @period
|
42
86
|
end
|
43
87
|
|
88
|
+
def after_pin_initialization
|
89
|
+
disarm
|
90
|
+
synchronize_period
|
91
|
+
speed(0)
|
92
|
+
end
|
44
93
|
end
|
45
94
|
end
|
46
95
|
end
|
data/lib/BBB/components/led.rb
CHANGED
@@ -15,15 +15,6 @@ module BBB
|
|
15
15
|
|
16
16
|
uses BBB::Pins::DigitalOutputPin
|
17
17
|
|
18
|
-
##
|
19
|
-
# Convenience method to grab the first pin in the pins array
|
20
|
-
#
|
21
|
-
# @return BBB::Pins::DigitalOutputPin
|
22
|
-
#
|
23
|
-
def pin
|
24
|
-
pins.first
|
25
|
-
end
|
26
|
-
|
27
18
|
##
|
28
19
|
# Turns on the LED
|
29
20
|
# @return void
|
@@ -0,0 +1,197 @@
|
|
1
|
+
module BBB
|
2
|
+
module Components
|
3
|
+
class Nunchuck
|
4
|
+
include Pinnable
|
5
|
+
uses Pins::I2C
|
6
|
+
|
7
|
+
attr_reader :accelerometer, :controls
|
8
|
+
|
9
|
+
def initialize(options={})
|
10
|
+
@started = false
|
11
|
+
@positions = options[:i2c] || []
|
12
|
+
@accelerometer = Accelerometer.new
|
13
|
+
@controls = Controls.new
|
14
|
+
@decoder = Decoder.new
|
15
|
+
end
|
16
|
+
|
17
|
+
def start
|
18
|
+
begin
|
19
|
+
raw_read
|
20
|
+
rescue I2C::AckError
|
21
|
+
i2c.write(0x52, 0x40, 0x00)
|
22
|
+
end
|
23
|
+
@started = true
|
24
|
+
end
|
25
|
+
|
26
|
+
def started?
|
27
|
+
@started
|
28
|
+
end
|
29
|
+
|
30
|
+
def update
|
31
|
+
bytes = raw_read.bytes.to_a
|
32
|
+
@accelerometer.update(bytes)
|
33
|
+
@controls.update(bytes)
|
34
|
+
end
|
35
|
+
|
36
|
+
def raw_read
|
37
|
+
decoder.decode i2c.read(0x52, 6, 0x00)
|
38
|
+
end
|
39
|
+
|
40
|
+
def z; controls.z; end
|
41
|
+
def c; controls.c; end
|
42
|
+
def x; controls.x; end
|
43
|
+
def y; controls.z; end
|
44
|
+
def accel; accelerometer; end
|
45
|
+
|
46
|
+
class Decoder
|
47
|
+
def decode(bytes)
|
48
|
+
bytes.map{|b| b^0x17 + 0x17}
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
class Accelerometer
|
53
|
+
def initialize
|
54
|
+
@x = AccelAccess.new
|
55
|
+
@y = AccelAccess.new
|
56
|
+
@z = AccelAccess.new
|
57
|
+
end
|
58
|
+
|
59
|
+
def update(bytes)
|
60
|
+
set_x(bytes)
|
61
|
+
set_y(bytes)
|
62
|
+
set_z(bytes)
|
63
|
+
end
|
64
|
+
|
65
|
+
def set_x(bytes)
|
66
|
+
value = (bytes[2] << 2) | ((bytes[5] & 0b00001100) >> 2)
|
67
|
+
x.update(value)
|
68
|
+
end
|
69
|
+
|
70
|
+
def set_y(bytes)
|
71
|
+
value = (bytes[3] << 2) | ((bytes[5] & 0b00110000) >> 4)
|
72
|
+
y.update(value)
|
73
|
+
end
|
74
|
+
|
75
|
+
def set_z(bytes)
|
76
|
+
value = (bytes[4] << 2) | ((bytes[5] & 0b11000000) >> 6)
|
77
|
+
z.update(value)
|
78
|
+
end
|
79
|
+
|
80
|
+
class AccelAccess
|
81
|
+
attr_reader :value
|
82
|
+
|
83
|
+
def update(value)
|
84
|
+
@value = value
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
class Controls
|
90
|
+
def initialize
|
91
|
+
initialize_buttons
|
92
|
+
initialize_axis
|
93
|
+
end
|
94
|
+
|
95
|
+
def initialize_buttons
|
96
|
+
@buttons = Hash.new
|
97
|
+
@buttons[:z] = Button.new
|
98
|
+
@buttons[:c] = Button.new
|
99
|
+
end
|
100
|
+
|
101
|
+
def c
|
102
|
+
@buttons[:c]
|
103
|
+
end
|
104
|
+
|
105
|
+
def z
|
106
|
+
@buttons[:z]
|
107
|
+
end
|
108
|
+
|
109
|
+
def initialize_axis
|
110
|
+
@axis = Hash.new
|
111
|
+
@axis[:x] = ControlAxis.new
|
112
|
+
@axis[:y] = ControlAxis.new
|
113
|
+
end
|
114
|
+
|
115
|
+
def update(bytes)
|
116
|
+
update_buttons(bytes)
|
117
|
+
update_axis(bytes)
|
118
|
+
end
|
119
|
+
|
120
|
+
def update_buttons(bytes)
|
121
|
+
x = bytes[5] & 0b00000010 >> 1
|
122
|
+
y = bytes[5] & 0b00000001
|
123
|
+
|
124
|
+
@buttons[:x].update(x)
|
125
|
+
@buttons[:y].update(y)
|
126
|
+
end
|
127
|
+
|
128
|
+
def update_axis(bytes)
|
129
|
+
@axis[:x].update(bytes[0])
|
130
|
+
@axis[:y].update(bytes[1])
|
131
|
+
end
|
132
|
+
|
133
|
+
class Button
|
134
|
+
attr_reader :status
|
135
|
+
attr_accessor :release_callbacks, :press_callbacks
|
136
|
+
|
137
|
+
def initialize
|
138
|
+
@status = :released
|
139
|
+
@release_callbacks = []
|
140
|
+
@press_callbacks = []
|
141
|
+
end
|
142
|
+
|
143
|
+
def pressed?
|
144
|
+
status == :pressed
|
145
|
+
end
|
146
|
+
|
147
|
+
def press!
|
148
|
+
@status = :pressed
|
149
|
+
on_press if old_state != status
|
150
|
+
end
|
151
|
+
|
152
|
+
def release!
|
153
|
+
old_state = status
|
154
|
+
@status = :released
|
155
|
+
on_release if old_state != status
|
156
|
+
end
|
157
|
+
|
158
|
+
def released?
|
159
|
+
!pressed
|
160
|
+
end
|
161
|
+
|
162
|
+
def update(value)
|
163
|
+
value == true ? release! : press!
|
164
|
+
end
|
165
|
+
|
166
|
+
def on_release
|
167
|
+
@release_callbacks.each{ |c| c.call(status) }
|
168
|
+
end
|
169
|
+
|
170
|
+
def on_press
|
171
|
+
@press_callbacks.each{ |c| c.call(status) }
|
172
|
+
end
|
173
|
+
|
174
|
+
end
|
175
|
+
|
176
|
+
class ControlAxis
|
177
|
+
attr_accessor :value
|
178
|
+
attr_accessor :change_callbacks
|
179
|
+
|
180
|
+
def initialize
|
181
|
+
@change_callbacks = []
|
182
|
+
end
|
183
|
+
|
184
|
+
def update(value)
|
185
|
+
old_value = self.value
|
186
|
+
@value = value
|
187
|
+
on_change if old_value != value
|
188
|
+
end
|
189
|
+
|
190
|
+
def on_change
|
191
|
+
@change_callbacks.each{|c| c.call(value) }
|
192
|
+
end
|
193
|
+
end
|
194
|
+
end
|
195
|
+
end
|
196
|
+
end
|
197
|
+
end
|