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
@@ -0,0 +1,9 @@
|
|
1
|
+
module Smalrubot
|
2
|
+
module Components
|
3
|
+
require 'smalrubot/components/base_component'
|
4
|
+
autoload :Led, 'smalrubot/components/led'
|
5
|
+
autoload :Button, 'smalrubot/components/button'
|
6
|
+
autoload :Sensor, 'smalrubot/components/sensor'
|
7
|
+
autoload :Servo, 'smalrubot/components/servo'
|
8
|
+
end
|
9
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
module Smalrubot
|
2
|
+
module Components
|
3
|
+
class BaseComponent
|
4
|
+
attr_reader :board, :pin, :pullup
|
5
|
+
alias :pins :pin
|
6
|
+
|
7
|
+
def initialize(options={})
|
8
|
+
self.board = options[:board]
|
9
|
+
self.pin = options[:pin] || options[:pins]
|
10
|
+
self.pullup = options[:pullup]
|
11
|
+
|
12
|
+
raise 'board and pin or pins are required for a component' if self.board.nil? || self.pin.nil?
|
13
|
+
after_initialize(options)
|
14
|
+
end
|
15
|
+
|
16
|
+
#
|
17
|
+
# As BaseComponent does a lot of work for you with regarding to setting up, it is
|
18
|
+
# best not to override #initialize and instead define an #after_initialize method
|
19
|
+
# within your subclass.
|
20
|
+
#
|
21
|
+
# @note This method should be implemented in the BaseComponent subclass.
|
22
|
+
#
|
23
|
+
def after_initialize(options={}) ; end
|
24
|
+
|
25
|
+
protected
|
26
|
+
|
27
|
+
attr_writer :board, :pin, :pullup
|
28
|
+
alias :pins= :pin=
|
29
|
+
|
30
|
+
def digital_write(pin=self.pin, value)
|
31
|
+
self.board.digital_write(pin, value)
|
32
|
+
end
|
33
|
+
|
34
|
+
def analog_write(pin=self.pin, value)
|
35
|
+
self.board.analog_write(pin, value)
|
36
|
+
end
|
37
|
+
|
38
|
+
def set_pin_mode(pin=self.pin, mode)
|
39
|
+
self.board.set_pin_mode(pin, mode, pullup)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module Smalrubot
|
2
|
+
module Components
|
3
|
+
class Led < BaseComponent
|
4
|
+
def after_initialize(options={})
|
5
|
+
set_pin_mode(:out)
|
6
|
+
off
|
7
|
+
end
|
8
|
+
|
9
|
+
def on
|
10
|
+
digital_write(Board::HIGH)
|
11
|
+
end
|
12
|
+
|
13
|
+
def off
|
14
|
+
digital_write(Board::LOW)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Smalrubot
|
2
|
+
module Components
|
3
|
+
class Servo < BaseComponent
|
4
|
+
attr_reader :position
|
5
|
+
|
6
|
+
def after_initialize(options={})
|
7
|
+
set_pin_mode(:out)
|
8
|
+
board.servo_toggle(pin, 1)
|
9
|
+
self.position = options[:position] || 0
|
10
|
+
end
|
11
|
+
|
12
|
+
def position=(value)
|
13
|
+
board.servo_write(pin, @position = angle(value))
|
14
|
+
end
|
15
|
+
|
16
|
+
def angle(value)
|
17
|
+
value == 180 ? value : value % 180
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
require 'observer'
|
2
|
+
require 'timeout'
|
3
|
+
|
4
|
+
module Smalrubot
|
5
|
+
module TxRx
|
6
|
+
class Base
|
7
|
+
def read(timeout = 0.005)
|
8
|
+
line = gets(timeout)
|
9
|
+
if line && line.match(/\A\d+:/)
|
10
|
+
pin, message = line.chomp.split(/:/)
|
11
|
+
if pin && message
|
12
|
+
return pin, message
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def write(message)
|
18
|
+
n = io.write(message)
|
19
|
+
Smalrubot.debug_log('write: %s(A:%d, E:%d)', message, n, message.length)
|
20
|
+
if n != message.length
|
21
|
+
raise "FATAL: n(#{n}) != message.length(#{message.length})"
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def handshake
|
26
|
+
5.times do
|
27
|
+
write("!9000000.")
|
28
|
+
line = gets(1)
|
29
|
+
if line && line.match(/ACK:/)
|
30
|
+
flush_read
|
31
|
+
return line.chomp.split(/:/)[1].to_i
|
32
|
+
end
|
33
|
+
end
|
34
|
+
raise BoardNotFound
|
35
|
+
end
|
36
|
+
|
37
|
+
def flush_read
|
38
|
+
gets until gets == nil
|
39
|
+
end
|
40
|
+
|
41
|
+
RETURN_CODE = "\n".ord
|
42
|
+
|
43
|
+
def gets(timeout=0.005)
|
44
|
+
Timeout.timeout(timeout) do
|
45
|
+
s = io.gets
|
46
|
+
Smalrubot.debug_log("gets: %s", s)
|
47
|
+
return s
|
48
|
+
end
|
49
|
+
rescue Exception
|
50
|
+
nil
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
require 'rubyserial'
|
2
|
+
|
3
|
+
module Smalrubot
|
4
|
+
module TxRx
|
5
|
+
class Serial < Base
|
6
|
+
BAUD = 115200
|
7
|
+
|
8
|
+
def initialize(options={})
|
9
|
+
@device = options[:device]
|
10
|
+
@baud = options[:baud] || BAUD
|
11
|
+
@first_write = true
|
12
|
+
end
|
13
|
+
|
14
|
+
def io
|
15
|
+
@io ||= connect
|
16
|
+
end
|
17
|
+
|
18
|
+
def handshake
|
19
|
+
while tty_devices.length > 0
|
20
|
+
begin
|
21
|
+
if on_windows?
|
22
|
+
io; sleep 3
|
23
|
+
end
|
24
|
+
|
25
|
+
return super
|
26
|
+
rescue BoardNotFound
|
27
|
+
@tty_devices.shift
|
28
|
+
@io.close
|
29
|
+
@io = nil
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
def connect
|
37
|
+
tty_devices.dup.each do |device|
|
38
|
+
begin
|
39
|
+
serial = ::Serial.new(device, @baud)
|
40
|
+
Smalrubot.debug_log('found board: %s (%d)', device, @baud)
|
41
|
+
return serial
|
42
|
+
rescue Exception
|
43
|
+
@tty_devices.shift
|
44
|
+
Smalrubot.debug_log('could not access: %s', device)
|
45
|
+
Smalrubot.show_backtrace($!)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
raise BoardNotFound
|
49
|
+
end
|
50
|
+
|
51
|
+
def tty_devices
|
52
|
+
if !@tty_devices
|
53
|
+
if @device
|
54
|
+
@tty_devices = [@device]
|
55
|
+
elsif on_windows?
|
56
|
+
@tty_devices = (1..256).map { |n| "COM#{n}" }
|
57
|
+
else
|
58
|
+
@tty_devices =
|
59
|
+
`ls /dev`.split("\n").grep(/usb|ACM/i).map{ |d| "/dev/#{d}" }
|
60
|
+
end
|
61
|
+
end
|
62
|
+
@tty_devices
|
63
|
+
end
|
64
|
+
|
65
|
+
def on_windows?
|
66
|
+
RUBY_PLATFORM.match /mswin|mingw/i
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,158 @@
|
|
1
|
+
/*
|
2
|
+
Library for smalrubot ruby gem.
|
3
|
+
*/
|
4
|
+
|
5
|
+
#include "Arduino.h"
|
6
|
+
#include "Smalrubot.h"
|
7
|
+
|
8
|
+
Smalrubot::Smalrubot(){
|
9
|
+
reset();
|
10
|
+
}
|
11
|
+
|
12
|
+
void Smalrubot::parse(char c) {
|
13
|
+
if (c == '!') index = 0; // Reset request
|
14
|
+
else if (c == '.') process(); // End request and process
|
15
|
+
else request[index++] = c; // Append to request
|
16
|
+
}
|
17
|
+
|
18
|
+
void Smalrubot::process() {
|
19
|
+
response[0] = '\0';
|
20
|
+
|
21
|
+
// Parse the request.
|
22
|
+
strncpy(cmdStr, request, 2); cmdStr[2] = '\0';
|
23
|
+
strncpy(pinStr, request + 2, 2); pinStr[2] = '\0';
|
24
|
+
strncpy(valStr, request + 4, 3); valStr[3] = '\0';
|
25
|
+
cmd = atoi(cmdStr);
|
26
|
+
pin = atoi(pinStr);
|
27
|
+
val = atoi(valStr);
|
28
|
+
|
29
|
+
#ifdef debug
|
30
|
+
Serial.print("Received request - "); Serial.println(request);
|
31
|
+
Serial.print("Command - "); Serial.println(cmdStr);
|
32
|
+
Serial.print("Pin - "); Serial.println(pinStr);
|
33
|
+
Serial.print("Value - "); Serial.println(valStr);
|
34
|
+
#endif
|
35
|
+
|
36
|
+
// Call the command.
|
37
|
+
switch(cmd) {
|
38
|
+
case 0: setMode (); break;
|
39
|
+
case 1: dWrite (); break;
|
40
|
+
case 2: dRead (); break;
|
41
|
+
case 3: aWrite (); break;
|
42
|
+
case 4: aRead (); break;
|
43
|
+
case 8: servoToggle (); break;
|
44
|
+
case 9: servoWrite (); break;
|
45
|
+
case 90: reset (); break;
|
46
|
+
default: break;
|
47
|
+
}
|
48
|
+
|
49
|
+
// Write the response.
|
50
|
+
if (response[0] != '\0') writeResponse();
|
51
|
+
|
52
|
+
#ifdef debug
|
53
|
+
Serial.print("Responded with - "); Serial.println(response);
|
54
|
+
Serial.println();
|
55
|
+
#endif
|
56
|
+
}
|
57
|
+
|
58
|
+
|
59
|
+
|
60
|
+
// WRITE CALLBACK
|
61
|
+
void Smalrubot::setupWrite(void (*writeCallback)(char *str)) {
|
62
|
+
_writeCallback = writeCallback;
|
63
|
+
}
|
64
|
+
void Smalrubot::writeResponse() {
|
65
|
+
_writeCallback(response);
|
66
|
+
}
|
67
|
+
|
68
|
+
|
69
|
+
|
70
|
+
|
71
|
+
|
72
|
+
|
73
|
+
// API FUNCTIONS
|
74
|
+
// CMD = 00 // Pin Mode
|
75
|
+
void Smalrubot::setMode() {
|
76
|
+
if (val == 0) {
|
77
|
+
pinMode(pin, OUTPUT);
|
78
|
+
#ifdef debug
|
79
|
+
Serial.print("Set pin "); Serial.print(pin); Serial.print(" to "); Serial.println("OUTPUT mode");
|
80
|
+
#endif
|
81
|
+
}
|
82
|
+
else {
|
83
|
+
pinMode(pin, INPUT);
|
84
|
+
#ifdef debug
|
85
|
+
Serial.print("Set pin "); Serial.print(pin); Serial.print(" to "); Serial.println("INPTUT mode");
|
86
|
+
#endif
|
87
|
+
}
|
88
|
+
}
|
89
|
+
|
90
|
+
// CMD = 01 // Digital Write
|
91
|
+
void Smalrubot::dWrite() {
|
92
|
+
if (val == 0) {
|
93
|
+
digitalWrite(pin, LOW);
|
94
|
+
#ifdef debug
|
95
|
+
Serial.print("Digital write "); Serial.print(LOW); Serial.print(" to pin "); Serial.println(pin);
|
96
|
+
#endif
|
97
|
+
}
|
98
|
+
else {
|
99
|
+
digitalWrite(pin, HIGH);
|
100
|
+
#ifdef debug
|
101
|
+
Serial.print("Digital write "); Serial.print(HIGH); Serial.print(" to pin "); Serial.println(pin);
|
102
|
+
#endif
|
103
|
+
}
|
104
|
+
}
|
105
|
+
|
106
|
+
// CMD = 02 // Digital Read
|
107
|
+
void Smalrubot::dRead() {
|
108
|
+
rval = digitalRead(pin);
|
109
|
+
sprintf(response, "%02d:%02d", pin, rval);
|
110
|
+
}
|
111
|
+
|
112
|
+
// CMD = 03 // Analog (PWM) Write
|
113
|
+
void Smalrubot::aWrite() {
|
114
|
+
analogWrite(pin,val);
|
115
|
+
#ifdef debug
|
116
|
+
Serial.print("Analog write "); Serial.print(val); Serial.print(" to pin "); Serial.println(pin);
|
117
|
+
#endif
|
118
|
+
}
|
119
|
+
|
120
|
+
// CMD = 04 // Analog Read
|
121
|
+
void Smalrubot::aRead() {
|
122
|
+
rval = analogRead(pin);
|
123
|
+
sprintf(response, "%02d:%02d", pin, rval);
|
124
|
+
}
|
125
|
+
|
126
|
+
// CMD = 08
|
127
|
+
// Attach the servo object to pin or detach it.
|
128
|
+
void Smalrubot::servoToggle() {
|
129
|
+
if (val == 0) {
|
130
|
+
#ifdef debug
|
131
|
+
Serial.print("Detaching servo"); Serial.print(" on pin "); Serial.println(pin);
|
132
|
+
#endif
|
133
|
+
servos[pin - SERVO_OFFSET].detach();
|
134
|
+
}
|
135
|
+
else {
|
136
|
+
#ifdef debug
|
137
|
+
Serial.print("Attaching servo"); Serial.print(" on pin "); Serial.println(pin);
|
138
|
+
#endif
|
139
|
+
servos[pin - SERVO_OFFSET].attach(pin);
|
140
|
+
}
|
141
|
+
}
|
142
|
+
|
143
|
+
// CMD = 09
|
144
|
+
// Write a value to the servo object.
|
145
|
+
void Smalrubot::servoWrite() {
|
146
|
+
#ifdef debug
|
147
|
+
Serial.print("Servo write "); Serial.print(val); Serial.print(" to pin "); Serial.println(pin);
|
148
|
+
#endif
|
149
|
+
servos[pin - SERVO_OFFSET].write(val);
|
150
|
+
}
|
151
|
+
|
152
|
+
// CMD = 90
|
153
|
+
void Smalrubot::reset() {
|
154
|
+
#ifdef debug
|
155
|
+
Serial.println("Reset the board to defaults.");
|
156
|
+
#endif
|
157
|
+
sprintf(response, "ACK:%02d", A0);
|
158
|
+
}
|
@@ -0,0 +1,60 @@
|
|
1
|
+
/*
|
2
|
+
Library for smalrubot ruby gem.
|
3
|
+
*/
|
4
|
+
|
5
|
+
#ifndef Smalrubot_h
|
6
|
+
#define Smalrubot_h
|
7
|
+
|
8
|
+
#include "Arduino.h"
|
9
|
+
#include <Servo.h>
|
10
|
+
|
11
|
+
// Allocate listener storage based on what board we're running.
|
12
|
+
#if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)
|
13
|
+
# define PIN_COUNT 70
|
14
|
+
# define SERVO_OFFSET 22
|
15
|
+
#else
|
16
|
+
# define PIN_COUNT 22
|
17
|
+
# define SERVO_OFFSET 2
|
18
|
+
#endif
|
19
|
+
|
20
|
+
// Uncomment this line to enable debugging mode.
|
21
|
+
// #define debug true
|
22
|
+
|
23
|
+
class Smalrubot {
|
24
|
+
public:
|
25
|
+
Smalrubot();
|
26
|
+
void setupWrite(void (*writeCallback)(char *str));
|
27
|
+
void parse(char c);
|
28
|
+
void process();
|
29
|
+
|
30
|
+
private:
|
31
|
+
// Request storage.
|
32
|
+
char request[8];
|
33
|
+
int index;
|
34
|
+
char cmdStr[3];
|
35
|
+
byte cmd;
|
36
|
+
char pinStr[3];
|
37
|
+
byte pin;
|
38
|
+
char valStr[4];
|
39
|
+
int val;
|
40
|
+
|
41
|
+
// Value and response storage.
|
42
|
+
int rval;
|
43
|
+
char response[8];
|
44
|
+
void (*_writeCallback)(char *str);
|
45
|
+
void writeResponse();
|
46
|
+
|
47
|
+
Servo servos[12];
|
48
|
+
|
49
|
+
// API-accessible functions.
|
50
|
+
void setMode ();
|
51
|
+
void dWrite ();
|
52
|
+
void dRead ();
|
53
|
+
void aWrite ();
|
54
|
+
void aRead ();
|
55
|
+
void servoToggle ();
|
56
|
+
void servoWrite ();
|
57
|
+
void reset ();
|
58
|
+
};
|
59
|
+
|
60
|
+
#endif
|