rumba 0.1.0

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.
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: []