dino 0.8
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.
- data/.gitignore +21 -0
- data/.rvmrc +1 -0
- data/Gemfile +6 -0
- data/LICENSE +22 -0
- data/README.md +14 -0
- data/Rakefile +2 -0
- data/dino.gemspec +19 -0
- data/examples/button/button.png +0 -0
- data/examples/button/button.rb +23 -0
- data/examples/ir_receiver.rb +33 -0
- data/examples/led/led.rb +14 -0
- data/examples/potentiometer.rb +26 -0
- data/examples/rgb_led.rb +39 -0
- data/examples/sensor.rb +18 -0
- data/examples/servo.rb +16 -0
- data/examples/stepper.rb +18 -0
- data/lib/dino.rb +15 -0
- data/lib/dino/board.rb +114 -0
- data/lib/dino/components/base_component.rb +33 -0
- data/lib/dino/components/button.rb +53 -0
- data/lib/dino/components/ir_receiver.rb +33 -0
- data/lib/dino/components/led.rb +20 -0
- data/lib/dino/components/rgb_led.rb +44 -0
- data/lib/dino/components/sensor.rb +23 -0
- data/lib/dino/components/servo.rb +20 -0
- data/lib/dino/components/stepper.rb +29 -0
- data/lib/dino/tx_rx.rb +58 -0
- data/lib/dino/version.rb +3 -0
- data/spec/lib/board_spec.rb +242 -0
- data/spec/lib/components/base_component_spec.rb +37 -0
- data/spec/lib/components/button_spec.rb +84 -0
- data/spec/lib/components/led_spec.rb +54 -0
- data/spec/lib/components/rgb_led_spec.rb +78 -0
- data/spec/lib/components/sensor_spec.rb +58 -0
- data/spec/lib/components/servo_spec.rb +53 -0
- data/spec/lib/components/stepper_spec.rb +60 -0
- data/spec/lib/tx_rx_spec.rb +78 -0
- data/spec/spec_helper.rb +3 -0
- data/src/du.ino +251 -0
- metadata +111 -0
@@ -0,0 +1,53 @@
|
|
1
|
+
module Dino
|
2
|
+
module Components
|
3
|
+
class Button < BaseComponent
|
4
|
+
UP = "01"
|
5
|
+
DOWN = "00"
|
6
|
+
|
7
|
+
def initialize(options={})
|
8
|
+
super(options)
|
9
|
+
|
10
|
+
@down_callbacks, @up_callbacks, @state = [], [], UP
|
11
|
+
|
12
|
+
self.board.add_digital_hardware(self)
|
13
|
+
self.board.start_read
|
14
|
+
end
|
15
|
+
|
16
|
+
def down(callback)
|
17
|
+
@down_callbacks << callback
|
18
|
+
end
|
19
|
+
|
20
|
+
def up(callback)
|
21
|
+
@up_callbacks << callback
|
22
|
+
end
|
23
|
+
|
24
|
+
def update(data)
|
25
|
+
return if data == @state
|
26
|
+
@state = data
|
27
|
+
|
28
|
+
case data
|
29
|
+
when UP
|
30
|
+
button_up
|
31
|
+
when DOWN
|
32
|
+
button_down
|
33
|
+
else
|
34
|
+
return
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
def button_up
|
41
|
+
@up_callbacks.each do |callback|
|
42
|
+
callback.call
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def button_down
|
47
|
+
@down_callbacks.each do |callback|
|
48
|
+
callback.call
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module Dino
|
2
|
+
module Components
|
3
|
+
class IrReceiver < BaseComponent
|
4
|
+
STABLE = "01"
|
5
|
+
|
6
|
+
def initialize(options={})
|
7
|
+
super(options)
|
8
|
+
|
9
|
+
@flash_callbacks = []
|
10
|
+
|
11
|
+
self.board.add_digital_hardware(self)
|
12
|
+
self.board.start_read
|
13
|
+
end
|
14
|
+
|
15
|
+
def flash(callback)
|
16
|
+
@flash_callbacks << callback
|
17
|
+
end
|
18
|
+
|
19
|
+
def update(data)
|
20
|
+
return if data == STABLE
|
21
|
+
light_flashed
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
def light_flashed
|
27
|
+
@flash_callbacks.each do |callback|
|
28
|
+
callback.call
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Dino
|
2
|
+
module Components
|
3
|
+
class Led < BaseComponent
|
4
|
+
def initialize(options={})
|
5
|
+
super(options)
|
6
|
+
|
7
|
+
set_pin_mode(:out)
|
8
|
+
digital_write(Board::LOW)
|
9
|
+
end
|
10
|
+
|
11
|
+
def on
|
12
|
+
digital_write(Board::HIGH)
|
13
|
+
end
|
14
|
+
|
15
|
+
def off
|
16
|
+
digital_write(Board::LOW)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
module Dino
|
2
|
+
module Components
|
3
|
+
class RgbLed < BaseComponent
|
4
|
+
# options = {board: my_board, pins: {red: red_pin, green: green_pin, blue: blue_pin}
|
5
|
+
def initialize(options={})
|
6
|
+
super(options)
|
7
|
+
|
8
|
+
raise 'missing pins[:red] pin' unless self.pins[:red]
|
9
|
+
raise 'missing pins[:green] pin' unless self.pins[:green]
|
10
|
+
raise 'missing pins[:blue] pin' unless self.pins[:blue]
|
11
|
+
|
12
|
+
pins.each do |color, pin|
|
13
|
+
set_pin_mode(:out, pin)
|
14
|
+
analog_write(Board::LOW, pin)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def blue
|
19
|
+
analog_write(Board::LOW, pins[:red])
|
20
|
+
analog_write(Board::LOW, pins[:green])
|
21
|
+
analog_write(Board::HIGH, pins[:blue])
|
22
|
+
end
|
23
|
+
|
24
|
+
def red
|
25
|
+
analog_write(Board::HIGH, pins[:red])
|
26
|
+
analog_write(Board::LOW, pins[:green])
|
27
|
+
analog_write(Board::LOW, pins[:blue])
|
28
|
+
end
|
29
|
+
|
30
|
+
def green
|
31
|
+
analog_write(Board::LOW, pins[:red])
|
32
|
+
analog_write(Board::HIGH, pins[:green])
|
33
|
+
analog_write(Board::LOW, pins[:blue])
|
34
|
+
end
|
35
|
+
|
36
|
+
def blinky
|
37
|
+
[:red, :green, :blue].cycle do |color|
|
38
|
+
self.send(color)
|
39
|
+
sleep(0.01)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module Dino
|
2
|
+
module Components
|
3
|
+
class Sensor < BaseComponent
|
4
|
+
def initialize(options={})
|
5
|
+
super(options)
|
6
|
+
|
7
|
+
@data_callbacks = []
|
8
|
+
@board.add_analog_hardware(self)
|
9
|
+
@board.start_read
|
10
|
+
end
|
11
|
+
|
12
|
+
def when_data_received(callback)
|
13
|
+
@data_callbacks << callback
|
14
|
+
end
|
15
|
+
|
16
|
+
def update(data)
|
17
|
+
@data_callbacks.each do |callback|
|
18
|
+
callback.call(data)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Dino
|
2
|
+
module Components
|
3
|
+
class Servo < BaseComponent
|
4
|
+
attr_reader :position
|
5
|
+
|
6
|
+
def initialize(options)
|
7
|
+
super(options)
|
8
|
+
|
9
|
+
set_pin_mode(:out)
|
10
|
+
self.position = 0
|
11
|
+
end
|
12
|
+
|
13
|
+
def position=(new_position)
|
14
|
+
@position = new_position % 180
|
15
|
+
analog_write(@position)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module Dino
|
2
|
+
module Components
|
3
|
+
class Stepper < BaseComponent
|
4
|
+
|
5
|
+
def initialize(options={})
|
6
|
+
super(options)
|
7
|
+
|
8
|
+
raise 'missing pins[:step] pin' unless self.pins[:step]
|
9
|
+
raise 'missing pins[:direction] pin' unless self.pins[:direction]
|
10
|
+
|
11
|
+
set_pin_mode(:out, pins[:step])
|
12
|
+
set_pin_mode(:out, pins[:direction])
|
13
|
+
digital_write(Board::LOW, pins[:step])
|
14
|
+
end
|
15
|
+
|
16
|
+
def step_cc
|
17
|
+
digital_write(Board::HIGH, self.pins[:direction])
|
18
|
+
digital_write(Board::HIGH, self.pins[:step])
|
19
|
+
digital_write(Board::LOW, self.pins[:step])
|
20
|
+
end
|
21
|
+
|
22
|
+
def step_cw
|
23
|
+
digital_write(Board::LOW, self.pins[:direction])
|
24
|
+
digital_write(Board::HIGH, self.pins[:step])
|
25
|
+
digital_write(Board::LOW, self.pins[:step])
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
data/lib/dino/tx_rx.rb
ADDED
@@ -0,0 +1,58 @@
|
|
1
|
+
require 'serialport'
|
2
|
+
require 'observer'
|
3
|
+
|
4
|
+
module Dino
|
5
|
+
class TxRx
|
6
|
+
include Observable
|
7
|
+
|
8
|
+
BAUD = 115200
|
9
|
+
|
10
|
+
def initialize
|
11
|
+
@first_write = true
|
12
|
+
end
|
13
|
+
|
14
|
+
def io
|
15
|
+
@io ||= tty_devices.map do |device|
|
16
|
+
next if device.match /^cu/
|
17
|
+
begin
|
18
|
+
SerialPort.new("/dev/#{device}", BAUD)
|
19
|
+
rescue
|
20
|
+
nil
|
21
|
+
end
|
22
|
+
end.compact.first
|
23
|
+
end
|
24
|
+
|
25
|
+
def io=(device)
|
26
|
+
@io = SerialPort.new(device, BAUD)
|
27
|
+
end
|
28
|
+
|
29
|
+
def read
|
30
|
+
@thread ||= Thread.new do
|
31
|
+
loop do
|
32
|
+
if IO.select([io], nil, nil, 0.005)
|
33
|
+
pin, message = *io.gets.chop.split(/::/)
|
34
|
+
pin && message && changed && notify_observers(pin, message)
|
35
|
+
end
|
36
|
+
sleep 0.004
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def close_read
|
42
|
+
return nil if @thread.nil?
|
43
|
+
Thread.kill(@thread)
|
44
|
+
@thread = nil
|
45
|
+
end
|
46
|
+
|
47
|
+
def write(message)
|
48
|
+
IO.select(nil, [io], nil)
|
49
|
+
io.puts(message)
|
50
|
+
end
|
51
|
+
|
52
|
+
private
|
53
|
+
|
54
|
+
def tty_devices
|
55
|
+
`ls /dev | grep usb`.split(/\n/)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
data/lib/dino/version.rb
ADDED
@@ -0,0 +1,242 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module Dino
|
4
|
+
describe Dino::Board do
|
5
|
+
def io_mock(methods = {})
|
6
|
+
@io ||= mock(:io, {write: nil, add_observer: nil}.merge(methods))
|
7
|
+
end
|
8
|
+
|
9
|
+
subject { Board.new(io_mock) }
|
10
|
+
|
11
|
+
describe '#initialize' do
|
12
|
+
it 'should take an io class' do
|
13
|
+
expect {
|
14
|
+
Board.new(io_mock)
|
15
|
+
}.to_not raise_exception
|
16
|
+
end
|
17
|
+
|
18
|
+
it 'should observe the io' do
|
19
|
+
io_mock.should_receive(:add_observer).with(subject)
|
20
|
+
subject.send(:initialize, io_mock)
|
21
|
+
end
|
22
|
+
|
23
|
+
it 'should start the heart beat' do
|
24
|
+
io_mock.should_receive(:write).with('!0212000.')
|
25
|
+
subject.add_digital_hardware(mock(:part, pin: 12))
|
26
|
+
sleep 0.01
|
27
|
+
end
|
28
|
+
|
29
|
+
it 'should send clearing bytes to the io' do
|
30
|
+
io_mock.should_receive(:write).with("00000000")
|
31
|
+
subject
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
describe '#update' do
|
36
|
+
context 'when the given pin connects to an analog hardware part' do
|
37
|
+
it 'should call update with the message on the part' do
|
38
|
+
part = mock(:part, pin: 7)
|
39
|
+
subject.add_analog_hardware(part)
|
40
|
+
other_part = mock(:part, pin: 9)
|
41
|
+
subject.add_analog_hardware(other_part)
|
42
|
+
|
43
|
+
part.should_receive(:update).with('wake up!')
|
44
|
+
subject.update(7, 'wake up!')
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
context 'when the given pin connects to an digital hardware part' do
|
49
|
+
it 'should call update with the message on the part' do
|
50
|
+
part = mock(:part, pin: 5)
|
51
|
+
subject.add_digital_hardware(part)
|
52
|
+
other_part = mock(:part, pin: 11)
|
53
|
+
subject.add_digital_hardware(other_part)
|
54
|
+
|
55
|
+
part.should_receive(:update).with('wake up!')
|
56
|
+
other_part.should_not_receive(:update).with('wake up!')
|
57
|
+
|
58
|
+
subject.update(5, 'wake up!')
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
context 'when the given pin is not connected' do
|
63
|
+
it 'should not do anything' do
|
64
|
+
expect {
|
65
|
+
subject.update(5, 'wake up!')
|
66
|
+
}.to_not raise_exception
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
describe '#digital_hardware' do
|
72
|
+
it 'should initialize as empty' do
|
73
|
+
subject.digital_hardware.should == []
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
describe '#analog_hardware' do
|
78
|
+
it 'should initialize as empty' do
|
79
|
+
subject.analog_hardware.should == []
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
describe '#add_digital_hardware' do
|
84
|
+
it 'should add digital hardware to the board' do
|
85
|
+
subject.add_digital_hardware(mock1 = mock(:part1, pin: 12))
|
86
|
+
subject.add_digital_hardware(mock2 = mock(:part2, pin: 14))
|
87
|
+
subject.digital_hardware.should =~ [mock1, mock2]
|
88
|
+
end
|
89
|
+
|
90
|
+
it 'should set the mode for the given pin to "in"' do
|
91
|
+
subject
|
92
|
+
subject.should_receive(:write).with("0012000")
|
93
|
+
subject.add_digital_hardware(mock1 = mock(:part1, pin: 12))
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
describe '#remove_digital_hardware' do
|
98
|
+
it 'should remove the given part from the hardware of the board' do
|
99
|
+
mock = mock(:part1, pin: 12)
|
100
|
+
subject.add_digital_hardware(mock)
|
101
|
+
subject.remove_digital_hardware(mock)
|
102
|
+
subject.digital_hardware.should == []
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
describe '#add_analog_hardware' do
|
107
|
+
it 'should add analog hardware to the board' do
|
108
|
+
subject.add_analog_hardware(mock1 = mock(:part1, pin: 12))
|
109
|
+
subject.add_analog_hardware(mock2 = mock(:part2, pin: 14))
|
110
|
+
subject.analog_hardware.should =~ [mock1, mock2]
|
111
|
+
end
|
112
|
+
|
113
|
+
it 'should set the mode for the given pin to "in"' do
|
114
|
+
subject
|
115
|
+
subject.should_receive(:write).with("0012000")
|
116
|
+
subject.add_analog_hardware(mock1 = mock(:part1, pin: 12))
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
describe '#remove_analog_hardware' do
|
121
|
+
it 'should remove the given part from the hardware of the board' do
|
122
|
+
mock = mock(:part1, pin: 12)
|
123
|
+
subject.add_analog_hardware(mock)
|
124
|
+
subject.remove_analog_hardware(mock)
|
125
|
+
subject.analog_hardware.should == []
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
describe '#start_read' do
|
130
|
+
it 'should tell the io to read' do
|
131
|
+
io_mock.should_receive(:read)
|
132
|
+
Board.new(io_mock).start_read
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
describe '#stop_read' do
|
137
|
+
it 'should tell the io to read' do
|
138
|
+
io_mock.should_receive(:close_read)
|
139
|
+
Board.new(io_mock).stop_read
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
describe '#write' do
|
144
|
+
it 'should return true if the write succeeds' do
|
145
|
+
@io = nil
|
146
|
+
board = Board.new(io_mock(write: true))
|
147
|
+
board.write('message').should == true
|
148
|
+
end
|
149
|
+
|
150
|
+
it 'should wrap the message in a ! and a . by default' do
|
151
|
+
io_mock.should_receive(:write).with('!hello.')
|
152
|
+
subject.write('hello')
|
153
|
+
end
|
154
|
+
|
155
|
+
it 'should not wrap the message if no_wrap is set to true' do
|
156
|
+
board = Board.new(io_mock)
|
157
|
+
io_mock.should_receive(:write).with('hello')
|
158
|
+
board.write('hello', no_wrap: true)
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
describe '#digital_write' do
|
163
|
+
it 'should append a append a write to the pin and value' do
|
164
|
+
io_mock.should_receive(:write).with('!0101003.')
|
165
|
+
subject.digital_write(01, 003)
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
describe '#digital_read' do
|
170
|
+
it 'should tell the board to start reading from the given pin' do
|
171
|
+
io_mock.should_receive(:write).with('!0213000.')
|
172
|
+
subject.digital_read(13)
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
176
|
+
describe '#analog_write' do
|
177
|
+
it 'should append a append a write to the pin and value' do
|
178
|
+
io_mock.should_receive(:write).with('!0301003.')
|
179
|
+
subject.analog_write(01, 003)
|
180
|
+
end
|
181
|
+
end
|
182
|
+
|
183
|
+
describe '#analog_read' do
|
184
|
+
it 'should tell the board to start reading from the given pin' do
|
185
|
+
io_mock.should_receive(:write).with('!0413000.')
|
186
|
+
subject.analog_read(13)
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
190
|
+
describe '#set_pin_mode' do
|
191
|
+
it 'should send a value of 1 if the pin mode is set to out' do
|
192
|
+
io_mock.should_receive(:write).with('!0013001.')
|
193
|
+
subject.set_pin_mode(13, :out)
|
194
|
+
end
|
195
|
+
|
196
|
+
it 'should send a value of 0 if the pin mode is set to in' do
|
197
|
+
io_mock.should_receive(:write).with('!0013000.')
|
198
|
+
subject.set_pin_mode(13, :in)
|
199
|
+
end
|
200
|
+
end
|
201
|
+
|
202
|
+
describe '#set_debug' do
|
203
|
+
it 'should set the boards debug on when passed on' do
|
204
|
+
io_mock.should_receive(:write).with('!9900001.')
|
205
|
+
subject.set_debug(:on)
|
206
|
+
end
|
207
|
+
|
208
|
+
it 'should set the boards debug off when passed off' do
|
209
|
+
io_mock.should_receive(:write).with('!9900000.')
|
210
|
+
subject.set_debug(:off)
|
211
|
+
end
|
212
|
+
end
|
213
|
+
|
214
|
+
describe '#normalize_pin' do
|
215
|
+
it 'should normalize numbers so they are two digits' do
|
216
|
+
subject.normalize_pin(1).should == '01'
|
217
|
+
end
|
218
|
+
|
219
|
+
it 'should not normalize numbers that are already two digits' do
|
220
|
+
subject.normalize_pin(10).should == '10'
|
221
|
+
end
|
222
|
+
|
223
|
+
it 'should raise if a number larger than two digits are given' do
|
224
|
+
expect { subject.normalize_pin(1000) }.to raise_exception 'pins can only be two digits'
|
225
|
+
end
|
226
|
+
end
|
227
|
+
|
228
|
+
describe '#normalize_value' do
|
229
|
+
it 'should normalize numbers so they are three digits' do
|
230
|
+
subject.normalize_value(1).should == '001'
|
231
|
+
end
|
232
|
+
|
233
|
+
it 'should not normalize numbers that are already three digits' do
|
234
|
+
subject.normalize_value(10).should == '010'
|
235
|
+
end
|
236
|
+
|
237
|
+
it 'should raise if a number larger than three digits are given' do
|
238
|
+
expect { subject.normalize_value(1000) }.to raise_exception 'values are limited to three digits'
|
239
|
+
end
|
240
|
+
end
|
241
|
+
end
|
242
|
+
end
|