smalrubot 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +22 -0
- data/.travis.yml +4 -0
- data/Gemfile +4 -0
- data/LEGAL +82 -0
- data/LICENSE +22 -0
- data/README.md +48 -0
- data/Rakefile +35 -0
- data/bin/smalrubot +82 -0
- data/examples/test.rb +13 -0
- data/lib/smalrubot.rb +32 -0
- data/lib/smalrubot/board.rb +104 -0
- data/lib/smalrubot/board_not_found.rb +3 -0
- data/lib/smalrubot/components.rb +9 -0
- data/lib/smalrubot/components/base_component.rb +44 -0
- data/lib/smalrubot/components/led.rb +18 -0
- data/lib/smalrubot/components/servo.rb +21 -0
- data/lib/smalrubot/tx_rx.rb +10 -0
- data/lib/smalrubot/tx_rx/base.rb +54 -0
- data/lib/smalrubot/tx_rx/serial.rb +70 -0
- data/lib/smalrubot/version.rb +3 -0
- data/sketch/lib/Smalrubot.cpp +158 -0
- data/sketch/lib/Smalrubot.h +60 -0
- data/sketch/sr/sr.ino +18 -0
- data/smalrubot.gemspec +26 -0
- data/spec/lib/board_not_found_spec.rb +8 -0
- data/spec/lib/board_spec.rb +120 -0
- data/spec/lib/components/base_component_spec.rb +66 -0
- data/spec/lib/components/led_spec.rb +49 -0
- data/spec/lib/components/servo_spec.rb +58 -0
- data/spec/lib/tx_rx/serial_spec.rb +81 -0
- data/spec/spec_helper.rb +12 -0
- metadata +140 -0
data/sketch/sr/sr.ino
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
#include "Smalrubot.h"
|
2
|
+
#include <Servo.h>
|
3
|
+
Smalrubot smalrubot;
|
4
|
+
|
5
|
+
// Smalrubot.h doesn't handle TXRX. Setup a function to tell it to write to Serial.
|
6
|
+
void writeResponse(char *response) { Serial.println(response); }
|
7
|
+
void (*writeCallback)(char *str) = writeResponse;
|
8
|
+
|
9
|
+
void setup() {
|
10
|
+
Serial.begin(115200);
|
11
|
+
smalrubot.setupWrite(writeCallback);
|
12
|
+
}
|
13
|
+
|
14
|
+
void loop() {
|
15
|
+
while(Serial.available() > 0) smalrubot.parse(Serial.read());
|
16
|
+
Serial.flush();
|
17
|
+
}
|
18
|
+
|
data/smalrubot.gemspec
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'smalrubot/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "smalrubot"
|
8
|
+
spec.version = Smalrubot::VERSION
|
9
|
+
spec.authors = ["Kouji Takao"]
|
10
|
+
spec.email = ["kouji.takao@gmail.com"]
|
11
|
+
spec.summary = %q{A library and an Arduino sketch for Smalruby.}
|
12
|
+
spec.description = %q{The smalrubot is a library and an Arduino sketch for Smalruby.}
|
13
|
+
spec.homepage = "https://github.com/smalruby/smalrubot"
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.files = `git ls-files -z`.split("\x0")
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
spec.add_dependency 'rubyserial'
|
22
|
+
|
23
|
+
spec.add_development_dependency "bundler", "~> 1.7"
|
24
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
25
|
+
spec.add_development_dependency "rspec"
|
26
|
+
end
|
@@ -0,0 +1,120 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module Smalrubot
|
4
|
+
describe Smalrubot::Board do
|
5
|
+
def io_mock(methods = {})
|
6
|
+
@io ||= double(:io, {write: nil, flush_read: nil, handshake: "14"}.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 initiate the handshake' do
|
19
|
+
io_mock.should_receive(:handshake)
|
20
|
+
subject
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
describe '#write' do
|
25
|
+
it 'should return true if the write succeeds' do
|
26
|
+
@io = nil
|
27
|
+
board = Board.new(io_mock(write: true))
|
28
|
+
board.write('message').should == true
|
29
|
+
end
|
30
|
+
|
31
|
+
it 'should wrap the message in a ! and a . by default' do
|
32
|
+
io_mock.should_receive(:write).with('!hello.')
|
33
|
+
subject.write('hello')
|
34
|
+
end
|
35
|
+
|
36
|
+
it 'should not wrap the message if no_wrap is set to true' do
|
37
|
+
board = Board.new(io_mock)
|
38
|
+
io_mock.should_receive(:write).with('hello')
|
39
|
+
board.write('hello', no_wrap: true)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
describe '#digital_write' do
|
44
|
+
it 'should append a append a write to the pin and value' do
|
45
|
+
io_mock.should_receive(:write).with('!0101003.')
|
46
|
+
subject.digital_write(01, 003)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
describe '#digital_read' do
|
51
|
+
it 'should tell the board to read once from the given pin' do
|
52
|
+
io_mock.should_receive(:write).with('!0213000.')
|
53
|
+
io_mock.should_receive(:read).with(1).and_return(['13', Smalrubot::Board::HIGH.to_s])
|
54
|
+
expect(subject.digital_read(13)).to eq(Smalrubot::Board::HIGH)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
describe '#analog_write' do
|
59
|
+
it 'should append a append a write to the pin and value' do
|
60
|
+
io_mock.should_receive(:write).with('!0301003.')
|
61
|
+
subject.analog_write(01, 003)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
describe '#analog_read' do
|
66
|
+
it 'should tell the board to read once from the given pin' do
|
67
|
+
io_mock.should_receive(:write).with('!0413000.')
|
68
|
+
io_mock.should_receive(:read).with(1).once.and_return(['13', '256'])
|
69
|
+
expect(subject.analog_read(13)).to eq(256)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
describe '#set_pin_mode' do
|
74
|
+
it 'should send a value of 0 if the pin mode is set to out' do
|
75
|
+
io_mock.should_receive(:write).with('!0013000.')
|
76
|
+
subject.set_pin_mode(13, :out)
|
77
|
+
end
|
78
|
+
|
79
|
+
it 'should send a value of 1 if the pin mode is set to in' do
|
80
|
+
io_mock.should_receive(:write).with('!0013001.')
|
81
|
+
subject.set_pin_mode(13, :in)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
describe '#handshake' do
|
86
|
+
it 'should tell the board to reset to defaults' do
|
87
|
+
io_mock.should_receive(:handshake)
|
88
|
+
subject.handshake
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
describe '#normalize_pin' do
|
93
|
+
it 'should normalize numbers so they are two digits' do
|
94
|
+
subject.normalize_pin(1).should == '01'
|
95
|
+
end
|
96
|
+
|
97
|
+
it 'should not normalize numbers that are already two digits' do
|
98
|
+
subject.normalize_pin(10).should == '10'
|
99
|
+
end
|
100
|
+
|
101
|
+
it 'should raise if a number larger than two digits are given' do
|
102
|
+
expect { subject.normalize_pin(1000) }.to raise_exception 'pin number must be in 0-99'
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
describe '#normalize_value' do
|
107
|
+
it 'should normalize numbers so they are three digits' do
|
108
|
+
subject.normalize_value(1).should == '001'
|
109
|
+
end
|
110
|
+
|
111
|
+
it 'should not normalize numbers that are already three digits' do
|
112
|
+
subject.normalize_value(10).should == '010'
|
113
|
+
end
|
114
|
+
|
115
|
+
it 'should raise if a number larger than three digits are given' do
|
116
|
+
expect { subject.normalize_value(1000) }.to raise_exception 'values are limited to three digits'
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module Smalrubot
|
4
|
+
module Components
|
5
|
+
|
6
|
+
describe BaseComponent do
|
7
|
+
|
8
|
+
it 'should initialize with board and pin' do
|
9
|
+
pin = "a pin"
|
10
|
+
board = "a board"
|
11
|
+
component = BaseComponent.new(pin: pin, board: board)
|
12
|
+
|
13
|
+
component.pin.should == pin
|
14
|
+
component.board.should == board
|
15
|
+
end
|
16
|
+
|
17
|
+
it 'should assign pins' do
|
18
|
+
pins = {red: 'red', green: 'green', blue: 'blue'}
|
19
|
+
board = "a board"
|
20
|
+
component = BaseComponent.new(pins: pins, board: board)
|
21
|
+
|
22
|
+
component.pins.should == pins
|
23
|
+
end
|
24
|
+
|
25
|
+
it 'should require a pin or pins' do
|
26
|
+
expect {
|
27
|
+
BaseComponent.new(board: 'some board')
|
28
|
+
}.to raise_exception
|
29
|
+
end
|
30
|
+
|
31
|
+
it 'should require a board' do
|
32
|
+
expect {
|
33
|
+
BaseComponent.new(pin: 'some pin')
|
34
|
+
}.to raise_exception
|
35
|
+
end
|
36
|
+
|
37
|
+
context "when subclassed #after_initialize should be executed" do
|
38
|
+
|
39
|
+
class SpecComponent < BaseComponent
|
40
|
+
|
41
|
+
def sucessfully_initialized? ; @success ; end
|
42
|
+
|
43
|
+
def options ; @options ; end
|
44
|
+
|
45
|
+
def after_initialize(options={})
|
46
|
+
@success = true
|
47
|
+
@options = options
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
let(:options) { { pin: pin, board: board } }
|
52
|
+
let(:pin) { "a pin" }
|
53
|
+
let(:board) { "a board" }
|
54
|
+
|
55
|
+
it "should call #after_initialize with options" do
|
56
|
+
component = SpecComponent.new(options)
|
57
|
+
component.should be_sucessfully_initialized
|
58
|
+
component.options.should eq options
|
59
|
+
end
|
60
|
+
|
61
|
+
end
|
62
|
+
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
@@ -0,0 +1,49 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module Smalrubot
|
4
|
+
module Components
|
5
|
+
describe Led do
|
6
|
+
let(:board) { double(:board, digital_write: true, set_pin_mode: true) }
|
7
|
+
|
8
|
+
describe '#initialize' do
|
9
|
+
it 'should raise if it does not receive a pin' do
|
10
|
+
expect {
|
11
|
+
Led.new(board: board)
|
12
|
+
}.to raise_exception
|
13
|
+
end
|
14
|
+
|
15
|
+
it 'should raise if it does not receive a board' do
|
16
|
+
expect {
|
17
|
+
Led.new(pins: {})
|
18
|
+
}.to raise_exception
|
19
|
+
end
|
20
|
+
|
21
|
+
it 'should set the pin to out' do
|
22
|
+
board.should_receive(:set_pin_mode).with(13, :out, nil)
|
23
|
+
Led.new(pin: 13, board: board)
|
24
|
+
end
|
25
|
+
|
26
|
+
it 'should set the pin to low' do
|
27
|
+
board.should_receive(:digital_write).with(13, Board::LOW)
|
28
|
+
Led.new(pin: 13, board: board)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
describe '#on' do
|
33
|
+
it 'should send a high to the board with the pin' do
|
34
|
+
@led = Led.new(pin: 13, board: board)
|
35
|
+
board.should_receive(:digital_write).with(13, Board::HIGH)
|
36
|
+
@led.on
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
describe '#off' do
|
41
|
+
it 'should send a high to the board with the pin' do
|
42
|
+
@led = Led.new(pin: 13, board: board)
|
43
|
+
board.should_receive(:digital_write).with(13, Board::LOW)
|
44
|
+
@led.off
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module Smalrubot
|
4
|
+
module Components
|
5
|
+
describe Servo do
|
6
|
+
let(:board) { double(:board, analog_write: true, set_pin_mode: true, servo_toggle: true, servo_write: true) }
|
7
|
+
|
8
|
+
describe '#initialize' do
|
9
|
+
it 'should raise if it does not receive a pin' do
|
10
|
+
expect {
|
11
|
+
Servo.new(board: board)
|
12
|
+
}.to raise_exception
|
13
|
+
end
|
14
|
+
|
15
|
+
it 'should raise if it does not receive a board' do
|
16
|
+
expect {
|
17
|
+
Servo.new(pin: 13)
|
18
|
+
}.to raise_exception
|
19
|
+
end
|
20
|
+
|
21
|
+
it 'should set the pins to out' do
|
22
|
+
board.should_receive(:set_pin_mode).with(13, :out, nil)
|
23
|
+
Servo.new(pin: 13, board: board)
|
24
|
+
end
|
25
|
+
|
26
|
+
it 'should set the inital position to 0' do
|
27
|
+
servo = Servo.new(pin: 13, board: board)
|
28
|
+
servo.instance_variable_get(:@position).should == 0
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
describe '#position' do
|
33
|
+
let(:servo) {servo = Servo.new(pin: 13, board: board)}
|
34
|
+
|
35
|
+
it 'should set the position of the Servo' do
|
36
|
+
servo.position = 90
|
37
|
+
servo.instance_variable_get(:@position).should == 90
|
38
|
+
end
|
39
|
+
|
40
|
+
it 'should let you write up to 180' do
|
41
|
+
servo.position = 180
|
42
|
+
servo.instance_variable_get(:@position).should == 180
|
43
|
+
end
|
44
|
+
|
45
|
+
it 'should modulate when position > 180' do
|
46
|
+
servo.position = 190
|
47
|
+
servo.instance_variable_get(:@position).should == 10
|
48
|
+
end
|
49
|
+
|
50
|
+
it 'should write the new position to the board' do
|
51
|
+
board.should_receive(:servo_write).with(13, 10)
|
52
|
+
servo.position = 190
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
@@ -0,0 +1,81 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module Smalrubot
|
4
|
+
describe TxRx::Serial do
|
5
|
+
it { should be }
|
6
|
+
|
7
|
+
describe '#initialize' do
|
8
|
+
it 'should set first_write to true' do
|
9
|
+
TxRx::Serial.new.instance_variable_get(:@first_write).should == true
|
10
|
+
end
|
11
|
+
|
12
|
+
it 'should set the device and buad if specified' do
|
13
|
+
txrx = TxRx::Serial.new({device: "/dev/ttyACM0", baud: 9600})
|
14
|
+
txrx.instance_variable_get(:@baud).should == 9600
|
15
|
+
txrx.instance_variable_get(:@device).should == "/dev/ttyACM0"
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
describe '#io' do
|
20
|
+
context "on windows" do
|
21
|
+
it 'should instantiate a new ::Serial for the first available tty device' do
|
22
|
+
original_platform = RUBY_PLATFORM
|
23
|
+
Constants.redefine(:RUBY_PLATFORM, "mswin", :on => Object)
|
24
|
+
subject.instance_variable_set('@tty_devices', ["COM1", "COM2", "COM3"])
|
25
|
+
|
26
|
+
# COM2 is chosen as available for this test.
|
27
|
+
::Serial.should_receive(:new).with("COM1", TxRx::Serial::BAUD).and_raise
|
28
|
+
::Serial.should_receive(:new).with("COM2", TxRx::Serial::BAUD).and_return(mock_serial = double)
|
29
|
+
::Serial.should_not_receive(:new).with("COM3", TxRx::Serial::BAUD)
|
30
|
+
|
31
|
+
subject.io.should == mock_serial
|
32
|
+
Constants.redefine(:RUBY_PLATFORM, original_platform, :on => Object)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
context "on unix" do
|
37
|
+
it 'should instantiate a new ::Serial for the first available tty device' do
|
38
|
+
subject.should_receive(:tty_devices).and_return(['/dev/ttyACM0', '/dev/tty.usbmodem1'])
|
39
|
+
|
40
|
+
# /dev/ttyACM0 is chosen as available for this test.
|
41
|
+
::Serial.should_receive(:new).with('/dev/ttyACM0', TxRx::Serial::BAUD).and_return(mock_serial = double)
|
42
|
+
::Serial.should_not_receive(:new).with('/dev/tty.usbmodem1', TxRx::Serial::BAUD)
|
43
|
+
|
44
|
+
subject.io.should == mock_serial
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
it 'should connect to the specified device at the specified baud rate' do
|
49
|
+
subject.should_receive(:tty_devices).and_return(["/dev/ttyACM0"])
|
50
|
+
::Serial.should_receive(:new).with('/dev/ttyACM0', 9600).and_return(mock_serial = double)
|
51
|
+
|
52
|
+
subject.instance_variable_set(:@device, "/dev/ttyACM0")
|
53
|
+
subject.instance_variable_set(:@baud, 9600)
|
54
|
+
|
55
|
+
subject.io.should == mock_serial
|
56
|
+
end
|
57
|
+
|
58
|
+
it 'should use the existing io instance if set' do
|
59
|
+
subject.should_receive(:tty_devices).once.and_return(['/dev/tty.ACM0', '/dev/tty.usbmodem1'])
|
60
|
+
::Serial.stub(:new).and_return(mock_serial = double)
|
61
|
+
|
62
|
+
3.times { subject.io }
|
63
|
+
subject.io.should == mock_serial
|
64
|
+
end
|
65
|
+
|
66
|
+
it 'should raise a BoardNotFound exception if there is no board connected' do
|
67
|
+
::Serial.stub(:new).and_raise
|
68
|
+
expect { subject.io }.to raise_exception BoardNotFound
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
describe '#write' do
|
73
|
+
it 'should write to the device' do
|
74
|
+
subject.stub(:io).and_return(mock_serial = double)
|
75
|
+
msg = 'a message'
|
76
|
+
mock_serial.should_receive(:write).with(msg).and_return(msg.length)
|
77
|
+
subject.write('a message')
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
require 'rspec'
|
2
|
+
|
3
|
+
require File.expand_path(File.join('../..', 'lib/smalrubot'), __FILE__)
|
4
|
+
|
5
|
+
# Nice little helper module to redefine constants quietly.
|
6
|
+
module Constants
|
7
|
+
def self.redefine(const, value, opts={})
|
8
|
+
opts = {:on => self.class}.merge(opts)
|
9
|
+
opts[:on].send(:remove_const, const) if self.class.const_defined?(const)
|
10
|
+
opts[:on].const_set(const, value)
|
11
|
+
end
|
12
|
+
end
|