BBB 0.1.3 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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 attached to
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
- attr_reader :components, :mock
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
+
@@ -31,15 +31,6 @@ module BBB
31
31
  def value
32
32
  read
33
33
  end
34
-
35
- ##
36
- # Convenience method to grab the first pin in the pins array
37
- #
38
- # @return BBB::Pins::AnalogPin
39
- #
40
- def pin
41
- pins.first
42
- end
43
34
  end
44
35
  end
45
36
  end
@@ -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
+
@@ -2,45 +2,94 @@ module BBB
2
2
  module Components
3
3
  class ESC
4
4
  include Pinnable
5
- uses Pins::PWMPin, Pins::DigitalOutputPin
5
+ uses Pins::PWMPin
6
6
 
7
7
  attr_accessor :min_duty, :max_duty, :period
8
- attr_reader :duty
9
8
 
10
- def initialize(period=10e6, min_duty=0.8e6, max_duty=2e6)
11
- @period = period
12
- @min_duty = min_duty
13
- @max_duty = max_duty
14
- end
9
+ after_connect :setup_pin
15
10
 
16
- def after_pin_initialization
17
- power.off!
18
- pwm.period = period
19
- pwm.duty = min_duty
20
- pwm.run = 1
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
- self.duty = min_duty + value * duty_range
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
- pwm.duty = @duty
66
+ synchronize_duty
30
67
  end
31
68
 
32
- def duty_range
33
- max_duty - min_duty
69
+ def period=(value)
70
+ @period = value
71
+ synchronize_period
34
72
  end
35
73
 
36
74
  def pwm
37
- pins.first
75
+ pin
38
76
  end
39
77
 
40
- def power
41
- pins.last
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
@@ -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