rumba 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (3) hide show
  1. checksums.yaml +7 -0
  2. data/lib/rumba.rb +193 -0
  3. metadata +58 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 115b8d8b186d7f86152bbff988ac8b0fae8fcc39
4
+ data.tar.gz: ba6345f5aaaf814e2c4a10aa55b2493c26cd7e4d
5
+ SHA512:
6
+ metadata.gz: f71cc909ed9079bb10054cdd86b972e9fa1daa5d7561fdb4d97796829bdd3aefbed720eedd63140be9388c2bed6957ceb0cdce642a4aa5146dae77724d3965ce
7
+ data.tar.gz: 9eb5465a3e7dbaca2e72b782a07955420edf819ceaab878090867cbc754924e9dcf7238a84517c4730b94f8be4aaf57ae9741da2dfdc743c948c2a15f10361b7
data/lib/rumba.rb ADDED
@@ -0,0 +1,193 @@
1
+ require 'rubygems'
2
+ require 'serialport'
3
+ require 'timeout'
4
+
5
+ class Roomba
6
+ # These opcodes require no arguments
7
+ OPCODES = {
8
+ start: 128,
9
+ control: 130,
10
+ dock: 143,
11
+ play_script: 153,
12
+ show_script: 154
13
+ }
14
+
15
+ # Create a method for each opcode that writes its data.
16
+ # This allows us to simply call roomba.code,
17
+ # and it's a cool excuse to do some metaprogramming :)
18
+ OPCODES.each do |name,val|
19
+ send :define_method, name do
20
+ write_chars([val])
21
+ end
22
+ end
23
+
24
+ SAFE_MODE = 131
25
+ FULL_MODE = 132
26
+
27
+ # These opcodes require arguments
28
+ DRIVE = 137
29
+ LEDS = 139
30
+ SONG = 140
31
+ PLAY_SONG = 141
32
+ DRIVE_DIRECT = 145
33
+
34
+ # Used for making the Roomba sing!
35
+ # Note that nil is simply a rest
36
+ NOTES = {
37
+ 'A' => 69, 'A#' => 70, 'B' => 71, 'C' => 72, 'C#' => 73, 'D' => 74,
38
+ 'D#' => 75, 'E' => 76, 'F' => 77, 'F#' => 78, 'G' => 79, 'G#' => 80,
39
+ nil => 0
40
+ }
41
+
42
+ #############################################################################
43
+ # HELPERS #
44
+ #############################################################################
45
+
46
+ # Converts input data (an array) into bytes before
47
+ # sending it over the serial connection.
48
+ def write_chars(data);
49
+ data.map! do |c|
50
+ if c.class == String
51
+ result = c.bytes.to_a.map { |b| [b].pack("C") }
52
+ else
53
+ result = [c].pack("C")
54
+ end
55
+
56
+ result
57
+ end
58
+
59
+ data = data.flatten.join
60
+
61
+ @serial.write(data)
62
+ @serial.flush
63
+ end
64
+
65
+ # Convert integer to two's complement signed 16 bit integer.
66
+ # Note that the Roomba is big-endian...I need to fix this
67
+ # code to make it portable across different architectures.
68
+ def convert_int(int)
69
+ [int].pack('s').reverse
70
+ end
71
+
72
+ #############################################################################
73
+ # COMMANDS #
74
+ #############################################################################
75
+
76
+ def safe_mode
77
+ write_chars([SAFE_MODE])
78
+ sleep(0.2)
79
+ end
80
+
81
+ def full_mode
82
+ safe_mode
83
+ write_chars([FULL_MODE])
84
+ sleep(0.2)
85
+ end
86
+
87
+ def drive(velocity, radius)
88
+ raise RangeError if velocity < -500 || velocity > 500
89
+ raise RangeError if (radius < -2000 || radius > 2000) && radius != 0xFFFF
90
+
91
+ velocity = convert_int(velocity)
92
+ radius = convert_int(radius)
93
+ write_chars([DRIVE, velocity, radius])
94
+ end
95
+
96
+ def drive_direct(left, right)
97
+ raise RangeError if left < -500 || left > 500
98
+ raise RangeError if right < -500 || right > 500
99
+
100
+ left = convert_int(left)
101
+ right = convert_int(right)
102
+
103
+ write_chars([DRIVE_DIRECT])
104
+ write_raw([right, left])
105
+ end
106
+
107
+ # Turn LEDs on and off
108
+ # Arguments are a hash in the following format:
109
+ # :advance => true/false | sets the "advance" LED (the >> one)
110
+ # :play => true/false | sets the "play" LED (the > one)
111
+ # :color => 0-255 | sets the color of the power LED (0 = green, 255 = red)
112
+ # :intensity => 0-255 | sets the intensity of the power LED (0 = off)
113
+ def set_leds(args)
114
+ @leds[:advance] = args[:advance] unless args[:advance].nil?
115
+ @leds[:play] = args[:play] unless args[:play].nil?
116
+ @leds[:color] = args[:color] unless args[:color].nil?
117
+ @leds[:intensity] = args[:intensity] unless args[:intensity].nil?
118
+ led_bits = 0b00000000
119
+ led_bits |= 0b00001000 if @leds[:advance]
120
+ led_bits |= 0b00000010 if @leds[:play]
121
+
122
+ write_chars([LEDS, led_bits, @leds[:color], @leds[:intensity]])
123
+ end
124
+
125
+ # Songs are cool. Here's the format:
126
+ # The song number designates which song this is so you can recall it later.
127
+ # The notes are specified in the NOTES hash, and are fed into the program
128
+ # as a 2D array, where the first element is the note number and the second
129
+ # is the duration of the note. The duration is specified in seconds.
130
+ # Example:
131
+ # [[note1,5], [note2,6]]
132
+ def song(song_number, notes)
133
+ raise RangeError if song_number < 0 || song_number > 15
134
+
135
+ notes.map! { |n| [NOTES[n[0]],n[1]*64] }
136
+ # The protocol requires us to send the number of notes and the song number first
137
+ write_chars([SONG, song_number, notes.size] + notes.flatten)
138
+ end
139
+
140
+ def play_song(song_number)
141
+ raise RangeError if song_number < 0 || song_number > 15
142
+ write_chars([PLAY_SONG,song_number])
143
+ end
144
+
145
+ #############################################################################
146
+ # Convenience methods #
147
+ #############################################################################
148
+
149
+ def straight(speed)
150
+ speed = convert_int(speed)
151
+ write_chars([DRIVE, speed, convert_int(32768)])
152
+ end
153
+
154
+ def spin_left(speed)
155
+ speed = convert_int(speed)
156
+ write_chars([DRIVE, speed, convert_int(1)])
157
+ end
158
+
159
+ def spin_right(speed)
160
+ speed = convert_int(speed)
161
+ write_chars([DRIVE, speed, convert_int(-1)])
162
+ end
163
+
164
+ def lights
165
+ write_chars([139, 9, 0, 128])
166
+ end
167
+
168
+ def halt
169
+ drive(0,0)
170
+ end
171
+
172
+ def power_off
173
+ @serial.close
174
+ end
175
+
176
+ def initialize(port, timeout=10)
177
+ @leds = {
178
+ advance: false,
179
+ play: false,
180
+ color: 0,
181
+ intensity: 0
182
+ }
183
+
184
+ @timeout = timeout
185
+ Timeout::timeout(@timeout) do
186
+ # Initialize the serialport
187
+ @serial = SerialPort.new(port, 57600)
188
+ @serial.read_timeout = 1000
189
+ self.start
190
+ end
191
+ end
192
+ end
193
+
metadata ADDED
@@ -0,0 +1,58 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rumba
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Eric Wood
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-05-27 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: serialport
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ description: Control your Roomba using Ruby!
28
+ email: eric@ericwood.org
29
+ executables: []
30
+ extensions: []
31
+ extra_rdoc_files: []
32
+ files:
33
+ - lib/rumba.rb
34
+ homepage: http://github.com/eric-wood/roomba
35
+ licenses:
36
+ - BSD
37
+ metadata: {}
38
+ post_install_message:
39
+ rdoc_options: []
40
+ require_paths:
41
+ - lib
42
+ required_ruby_version: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - ">="
45
+ - !ruby/object:Gem::Version
46
+ version: '0'
47
+ required_rubygems_version: !ruby/object:Gem::Requirement
48
+ requirements:
49
+ - - ">="
50
+ - !ruby/object:Gem::Version
51
+ version: '0'
52
+ requirements: []
53
+ rubyforge_project:
54
+ rubygems_version: 2.2.2
55
+ signing_key:
56
+ specification_version: 4
57
+ summary: Ruby bindings for the iRobot Roomba
58
+ test_files: []