mi100 0.4.0 → 0.4.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (4) hide show
  1. data/lib/mi100/version.rb +3 -3
  2. data/lib/mi100.rb +367 -315
  3. data/spec/mi100_spec.rb +408 -301
  4. metadata +8 -8
data/lib/mi100.rb CHANGED
@@ -1,315 +1,367 @@
1
- # mi100.rb
2
- # Copyright (c) 2014 Masami Yamakawa
3
- #
4
- # This software is released under the MIT License.
5
- # http://opensource.org/lisenses/mit-license.php
6
-
7
- require 'mi100/version'
8
- require 'mi100/morsecoder'
9
- require 'timeout'
10
-
11
- class Mi100
12
-
13
- DEFAULT_RETRIES = 5
14
- READ_TIMEOUT = 5000
15
- WRITE_TIMEOUT = 5000
16
- SHORT_READ_TIMEOUT = 1
17
-
18
- CMD_PING = "H"
19
- CMD_GET_POWER_LEVEL = "H"
20
- CMD_STOP = "S"
21
- CMD_MOVE_FORWARD = "F"
22
- CMD_MOVE_BACKWARD = "B"
23
- CMD_SPIN_RIGHT = "R"
24
- CMD_SPIN_LEFT = "L"
25
- CMD_BLINK_LED = "D"
26
- CMD_TONE = "T"
27
- CMD_GET_LIGHT = "P"
28
- CMD_TURN_RIGHT = "U"
29
- CMD_TURN_LEFT = "A"
30
- CMD_SET_SPEED = "W"
31
- CMD_FREE_RAM = "M"
32
-
33
- DEFAULT_SPEED = 1023
34
- DEFAULT_MOVE_DURATION = 300
35
- DEFAULT_SPIN_DURATION = 140
36
- DEFAULT_BLINK_DURATION = 600
37
- DEFAULT_TONE_DURATION = 300
38
-
39
- DEFAULT_MOVE_DIRECTION = "FORWARD"
40
- DEFAULT_SPIN_DIRECTION = "RIGHT"
41
-
42
- FREQUENCY = { DO: 262,
43
- DOS: 277,
44
- RE: 294,
45
- RES: 311,
46
- MI: 330,
47
- FA: 349,
48
- FAS: 370,
49
- SO: 392,
50
- SOS: 415,
51
- LA: 440,
52
- LAS: 466,
53
- SI: 494,
54
- HDO: 523,
55
- HDOS: 554,
56
- HRE: 587,
57
- HRES: 622,
58
- HMI: 659,
59
- HFA: 698,
60
- HFAS: 740,
61
- HSO: 784,
62
- HSOS: 831,
63
- HLA: 880,
64
- HSI: 988,
65
- }
66
-
67
- def initialize(dev)
68
- retries_left = DEFAULT_RETRIES
69
- begin
70
- initialize_serialport dev
71
- rescue Errno::ENOENT
72
- puts "Retry Bluetooth connection: #{retries_left.to_s}"
73
- retries_left -= 1
74
- retry unless retries_left < 0
75
- puts "Bluetooth connection failed."
76
- raise
77
- end
78
-
79
- end
80
-
81
- def close
82
- sleep 2
83
- @sp.close
84
- end
85
-
86
- def ping
87
- send_command_get_response CMD_PING
88
- end
89
-
90
- def power
91
- response = send_command_get_response CMD_GET_POWER_LEVEL
92
- voltage = response[1].to_i / 1000.0
93
- voltage.round 2
94
- end
95
-
96
- def light
97
- response = send_command_get_response CMD_GET_LIGHT
98
- response[1].to_i
99
- end
100
-
101
- def move(direction = DEFAULT_MOVE_DIRECTION, duration = DEFAULT_MOVE_DURATION)
102
- cmd = direction.upcase == "BACKWARD" ? CMD_MOVE_BACKWARD : CMD_MOVE_FORWARD
103
- send_command_get_response "#{cmd},#{duration.to_s}"
104
- end
105
-
106
- def spin(direction = DEFAULT_SPIN_DIRECTION, duration = DEFAULT_SPIN_DURATION)
107
- cmd = direction.upcase == "LEFT" ? CMD_SPIN_LEFT : CMD_SPIN_RIGHT
108
- send_command_get_response "#{cmd},#{duration.to_s}"
109
- end
110
-
111
- def move_forward(duration)
112
- send_command_get_response "#{CMD_MOVE_FORWARD},#{duration.to_s}"
113
- end
114
-
115
- def move_backward(duration)
116
- send_command_get_response "#{CMD_MOVE_BACKWARD},#{duration.to_s}"
117
- end
118
-
119
- def spin_right(duration)
120
- send_command_get_response "#{CMD_SPIN_RIGHT},#{duration.to_s}"
121
- end
122
-
123
- def spin_left(duration)
124
- send_command_get_response "#{CMD_SPIN_LEFT},#{duration.to_s}"
125
- end
126
-
127
- def turn_right(duration)
128
- send_command_get_response "#{CMD_TURN_RIGHT},#{duration.to_s}"
129
- end
130
-
131
- def turn_left(duration)
132
- send_command_get_response "#{CMD_TURN_LEFT},#{duration.to_s}"
133
- end
134
-
135
- def movef(duration = DEFAULT_MOVE_DURATION)
136
- move_forward duration
137
- end
138
-
139
- def moveb(duration = DEFAULT_MOVE_DURATION)
140
- move_backward duration
141
- end
142
-
143
- def spinr(duration = DEFAULT_SPIN_DURATION)
144
- spin_right duration
145
- end
146
-
147
- def spinl(duration = DEFAULT_SPIN_DURATION)
148
- spin_left duration
149
- end
150
-
151
- def move_forward!(duration)
152
- sendln "#{CMD_MOVE_FORWARD},#{duration.to_s}"
153
- end
154
-
155
- def move_backward!(duration)
156
- sendln "#{CMD_MOVE_BACKWARD},#{duration.to_s}"
157
- end
158
-
159
- def spin_right!(duration)
160
- sendln "#{CMD_SPIN_RIGHT},#{duration.to_s}"
161
- end
162
-
163
- def spin_left!(duration)
164
- sendln "#{CMD_SPIN_LEFT},#{duration.to_s}"
165
- end
166
-
167
- def turn_right!(duration)
168
- sendln "#{CMD_TURN_RIGHT},#{duration.to_s}"
169
- end
170
-
171
- def turn_left!(duration)
172
- sendln "#{CMD_TURN_LEFT},#{duration.to_s}"
173
- end
174
-
175
- def speed(pwm_value = DEFAULT_SPEED)
176
- send_command_get_response "#{CMD_SET_SPEED},#{pwm_value.to_s}"
177
- end
178
-
179
- def free_ram
180
- send_command_get_response "#{CMD_FREE_RAM}"
181
- end
182
-
183
- def blink(r = nil, g = nil, b = nil, duration = DEFAULT_BLINK_DURATION)
184
- r ||= rand(100)+1
185
- g ||= rand(100)+1
186
- b ||= rand(100)+1
187
- r = 100 if r > 100
188
- g = 100 if g > 100
189
- b = 100 if b > 100
190
- send_command_get_response "#{CMD_BLINK_LED},#{r.to_s},#{g.to_s},#{b.to_s},#{duration.to_s}"
191
- end
192
-
193
- def stop
194
- send_command_get_response CMD_STOP
195
- end
196
-
197
- def tone(frequency = nil, duration = DEFAULT_TONE_DURATION)
198
- frequency ||= rand(4186 - 28) + 28
199
- frequency = 4186 if frequency > 4186
200
- frequency = 28 if frequency < 28
201
- send_command_get_response "#{CMD_TONE},#{frequency.to_s},#{duration.to_s}"
202
- end
203
-
204
- def sound(pitch = "?", duration = DEFAULT_TONE_DURATION)
205
-
206
- if pitch.instance_of?(String)
207
- pitch = FREQUENCY.keys[rand(FREQUENCY.size)].to_s if pitch == "?"
208
- frequency = FREQUENCY[pitch.upcase.to_sym]
209
- else
210
- frequency = pitch
211
- end
212
-
213
- tone frequency, duration if frequency
214
- sleep duration.to_f / 1000.0
215
- end
216
-
217
- def good
218
- freqs = [440,880,1760]
219
- duration = 100.0
220
- freqs.each do |freq|
221
- tone freq, duration
222
- sleep duration / 1000.0
223
- end
224
- end
225
-
226
- def bad
227
- duration = 400.0
228
- freq = 100
229
- tone freq, duration
230
- sleep duration / 1000.0
231
- end
232
-
233
- def talk(str)
234
- morsecoder = Morsecoder.new str
235
- morsecoder.each {|frequency, duration| sound(frequency,duration)}
236
- end
237
-
238
- def morse_frequency
239
- Morsecoder.default_frequency
240
- end
241
-
242
- def morse_frequency=(frequency)
243
- Morsecoder.default_frequency = frequency
244
- end
245
-
246
- def morse_unit
247
- Morsecoder.default_unit
248
- end
249
-
250
- def morse_unit=(millisec)
251
- Morsecoder.default_unit = millisec
252
- end
253
-
254
- # Private methods
255
- private
256
-
257
- def initialize_serialport(dev)
258
- require 'serialport'
259
- @sp = SerialPort.new dev, 38400, 8, 1, SerialPort::NONE
260
- if is_windows?
261
- @sp.read_timeout = READ_TIMEOUT
262
- @sp.write_timeout = WRITE_TIMEOUT
263
- end
264
- end
265
-
266
-
267
- def send_command_get_response(cmd = CMD_PING)
268
- empty_receive_buffer
269
- sendln cmd
270
- received_line = receiveln
271
-
272
- begin
273
- timeout(READ_TIMEOUT/1000) do
274
- received_line = receiveln until received_line[0] == cmd[0]
275
- end
276
- rescue
277
- return nil
278
- end
279
- received_line.chomp.split(",")
280
- end
281
-
282
- def sendln str
283
- @sp.write str + "\n"
284
- end
285
-
286
- def empty_receive_buffer
287
- @sp.read_timeout = SHORT_READ_TIMEOUT
288
- begin
289
- char = @sp.read 1
290
- end while char && char.length > 0
291
- @sp.read_timeout = READ_TIMEOUT
292
- end
293
-
294
- def receiveln
295
- if is_windows?
296
- start_time = Time.now
297
- str = ""
298
- while (Time.now - start_time) * 1000.0 < READ_TIMEOUT do
299
- char = @sp.read 1
300
- if char.length > 0
301
- str += char
302
- break if char == "\n"
303
- end
304
- end
305
- else
306
- str = @sp.gets
307
- end
308
- str
309
- end
310
-
311
- def is_windows?
312
- os = RUBY_PLATFORM.split("-")[1]
313
- os == 'mswin' or os == 'bccwin' or os == 'mingw' or os == 'mingw32'
314
- end
315
- end
1
+ # mi100.rb
2
+ # Copyright (c) 2014 Masami Yamakawa
3
+ #
4
+ # This software is released under the MIT License.
5
+ # http://opensource.org/lisenses/mit-license.php
6
+
7
+ require 'mi100/version'
8
+ require 'mi100/morsecoder'
9
+ require 'timeout'
10
+
11
+ class Mi100
12
+
13
+ DEFAULT_RETRIES = 5
14
+ READ_TIMEOUT = 5000
15
+ WRITE_TIMEOUT = 5000
16
+ SHORT_READ_TIMEOUT = 1
17
+
18
+ CMD_PING = "H"
19
+ CMD_GET_POWER_LEVEL = "H"
20
+ CMD_STOP = "S"
21
+ CMD_MOVE_FORWARD = "F"
22
+ CMD_MOVE_BACKWARD = "B"
23
+ CMD_SPIN_RIGHT = "R"
24
+ CMD_SPIN_LEFT = "L"
25
+ CMD_BLINK_LED = "D"
26
+ CMD_TONE = "T"
27
+ CMD_GET_LIGHT = "P"
28
+ CMD_TURN_RIGHT = "U"
29
+ CMD_TURN_LEFT = "A"
30
+ CMD_SET_SPEED = "W"
31
+ CMD_FREE_RAM = "M"
32
+ CMD_TURN_LED_RGB = "V"
33
+ CMD_DRIVE = "Z"
34
+
35
+
36
+ DEFAULT_SPEED = 1023
37
+ DEFAULT_MOVE_DURATION = 300
38
+ DEFAULT_SPIN_DURATION = 140
39
+ DEFAULT_DRIVE_DURATION = 50
40
+ DEFAULT_BLINK_DURATION = 600
41
+ DEFAULT_TONE_DURATION = 300
42
+
43
+ DEFAULT_MOVE_DIRECTION = "FORWARD"
44
+ DEFAULT_SPIN_DIRECTION = "RIGHT"
45
+
46
+ FREQUENCY = { DO: 262,
47
+ DOS: 277,
48
+ RE: 294,
49
+ RES: 311,
50
+ MI: 330,
51
+ FA: 349,
52
+ FAS: 370,
53
+ SO: 392,
54
+ SOS: 415,
55
+ LA: 440,
56
+ LAS: 466,
57
+ SI: 494,
58
+ HDO: 523,
59
+ HDOS: 554,
60
+ HRE: 587,
61
+ HRES: 622,
62
+ HMI: 659,
63
+ HFA: 698,
64
+ HFAS: 740,
65
+ HSO: 784,
66
+ HSOS: 831,
67
+ HLA: 880,
68
+ HSI: 988,
69
+ }
70
+
71
+
72
+ def initialize(dev)
73
+ retries_left = DEFAULT_RETRIES
74
+ begin
75
+ initialize_serialport dev
76
+ rescue Errno::ENOENT
77
+ puts "Retry Bluetooth connection: #{retries_left.to_s}"
78
+ retries_left -= 1
79
+ retry unless retries_left < 0
80
+ puts "Bluetooth connection failed."
81
+ raise
82
+ end
83
+
84
+ initialize_robo
85
+ end
86
+
87
+ def close
88
+ sleep 2
89
+ @sp.close
90
+ end
91
+
92
+ def ping
93
+ send_command_get_response CMD_PING
94
+ end
95
+
96
+ def power
97
+ response = send_command_get_response CMD_GET_POWER_LEVEL
98
+ voltage = response[1].to_i / 1000.0
99
+ voltage.round 2
100
+ end
101
+
102
+ def light
103
+ response = send_command_get_response CMD_GET_LIGHT
104
+ response[1].to_i
105
+ end
106
+
107
+ def move(direction = DEFAULT_MOVE_DIRECTION, duration = DEFAULT_MOVE_DURATION)
108
+ cmd = direction.upcase == "BACKWARD" ? CMD_MOVE_BACKWARD : CMD_MOVE_FORWARD
109
+ send_command_get_response "#{cmd},#{duration.to_s}"
110
+ end
111
+
112
+ def spin(direction = DEFAULT_SPIN_DIRECTION, duration = DEFAULT_SPIN_DURATION)
113
+ cmd = direction.upcase == "LEFT" ? CMD_SPIN_LEFT : CMD_SPIN_RIGHT
114
+ send_command_get_response "#{cmd},#{duration.to_s}"
115
+ end
116
+
117
+ def move_forward(duration)
118
+ send_command_get_response "#{CMD_MOVE_FORWARD},#{duration.to_s}"
119
+ end
120
+
121
+ def move_backward(duration)
122
+ send_command_get_response "#{CMD_MOVE_BACKWARD},#{duration.to_s}"
123
+ end
124
+
125
+ def spin_right(duration)
126
+ send_command_get_response "#{CMD_SPIN_RIGHT},#{duration.to_s}"
127
+ end
128
+
129
+ def spin_left(duration)
130
+ send_command_get_response "#{CMD_SPIN_LEFT},#{duration.to_s}"
131
+ end
132
+
133
+ def turn_right(duration)
134
+ send_command_get_response "#{CMD_TURN_RIGHT},#{duration.to_s}"
135
+ end
136
+
137
+ def turn_left(duration)
138
+ send_command_get_response "#{CMD_TURN_LEFT},#{duration.to_s}"
139
+ end
140
+
141
+ def movef(duration = DEFAULT_MOVE_DURATION)
142
+ move_forward duration
143
+ end
144
+
145
+ def moveb(duration = DEFAULT_MOVE_DURATION)
146
+ move_backward duration
147
+ end
148
+
149
+ def spinr(duration = DEFAULT_SPIN_DURATION)
150
+ spin_right duration
151
+ end
152
+
153
+ def spinl(duration = DEFAULT_SPIN_DURATION)
154
+ spin_left duration
155
+ end
156
+
157
+ def drive(right_speed, left_speed, duration = @drive_duration)
158
+ send_command_get_response "#{CMD_DRIVE},#{right_speed.to_s},#{left_speed.to_s},#{duration.to_s}"
159
+ end
160
+
161
+ def drive_right(turn_speed, speed = @speed, duration = @drive_duration)
162
+ send_command_get_response "#{CMD_DRIVE},#{speed.to_s},#{(speed + turn_speed).to_s},#{duration.to_s}"
163
+ end
164
+
165
+ def drive_left(turn_speed, speed = @speed, duration = @drive_duration)
166
+ send_command_get_response "#{CMD_DRIVE},#{(speed + turn_speed).to_s},#{speed.to_s},#{duration.to_s}"
167
+ end
168
+
169
+ def move_forward!(duration)
170
+ sendln "#{CMD_MOVE_FORWARD},#{duration.to_s}"
171
+ end
172
+
173
+ def move_backward!(duration)
174
+ sendln "#{CMD_MOVE_BACKWARD},#{duration.to_s}"
175
+ end
176
+
177
+ def spin_right!(duration)
178
+ sendln "#{CMD_SPIN_RIGHT},#{duration.to_s}"
179
+ end
180
+
181
+ def spin_left!(duration)
182
+ sendln "#{CMD_SPIN_LEFT},#{duration.to_s}"
183
+ end
184
+
185
+ def turn_right!(duration)
186
+ sendln "#{CMD_TURN_RIGHT},#{duration.to_s}"
187
+ end
188
+
189
+ def turn_left!(duration)
190
+ sendln "#{CMD_TURN_LEFT},#{duration.to_s}"
191
+ end
192
+
193
+ def drive!(right_speed, left_speed, duration = @drive_duration)
194
+ sendln "#{CMD_DRIVE},#{right_speed.to_s},#{left_speed.to_s},#{duration.to_s}"
195
+ end
196
+
197
+ def drive_right!(turn_speed, speed = @speed, duration = @drive_duration)
198
+ sendln "#{CMD_DRIVE},#{speed.to_s},#{(speed + turn_speed).to_s},#{duration.to_s}"
199
+ end
200
+
201
+ def drive_left!(turn_speed, speed = @speed, duration = @drive_duration)
202
+ sendln "#{CMD_DRIVE},#{(speed + turn_speed).to_s},#{speed.to_s},#{duration.to_s}"
203
+ end
204
+
205
+ def speed(pwm_value = DEFAULT_SPEED)
206
+ @speed = pwm_value
207
+ send_command_get_response "#{CMD_SET_SPEED},#{pwm_value.to_s}"
208
+ end
209
+
210
+ def drive_duration(duration = DEFAULT_DRIVE_DURATION)
211
+ @drive_duration = duration
212
+ end
213
+
214
+ def free_ram
215
+ send_command_get_response "#{CMD_FREE_RAM}"
216
+ end
217
+
218
+ def blink(r = nil, g = nil, b = nil, duration = DEFAULT_BLINK_DURATION)
219
+ r ||= rand(100)+1
220
+ g ||= rand(100)+1
221
+ b ||= rand(100)+1
222
+ r = 100 if r > 100
223
+ g = 100 if g > 100
224
+ b = 100 if b > 100
225
+ send_command_get_response "#{CMD_BLINK_LED},#{r.to_s},#{g.to_s},#{b.to_s},#{duration.to_s}"
226
+ end
227
+
228
+ def turn_led(r = 0, g = 0, b = 0)
229
+ send_command_get_response "#{CMD_TURN_LED_RGB},#{r.to_s},#{g.to_s},#{b.to_s}"
230
+ end
231
+
232
+ def led_on
233
+ turn_led(100,100,100)
234
+ end
235
+
236
+ def led_off
237
+ turn_led(0,0,0)
238
+ end
239
+
240
+ def stop
241
+ send_command_get_response CMD_STOP
242
+ end
243
+
244
+ def tone(frequency = nil, duration = DEFAULT_TONE_DURATION)
245
+ frequency ||= rand(4186 - 28) + 28
246
+ frequency = 4186 if frequency > 4186
247
+ frequency = 28 if frequency < 28
248
+ send_command_get_response "#{CMD_TONE},#{frequency.to_s},#{duration.to_s}"
249
+ end
250
+
251
+ def sound(pitch = "?", duration = DEFAULT_TONE_DURATION)
252
+
253
+ if pitch.instance_of?(String)
254
+ pitch = FREQUENCY.keys[rand(FREQUENCY.size)].to_s if pitch == "?"
255
+ frequency = FREQUENCY[pitch.upcase.to_sym]
256
+ else
257
+ frequency = pitch
258
+ end
259
+
260
+ tone frequency, duration if frequency
261
+ sleep duration.to_f / 1000.0
262
+ end
263
+
264
+ def good
265
+ freqs = [440,880,1760]
266
+ duration = 100.0
267
+ freqs.each do |freq|
268
+ tone freq, duration
269
+ sleep duration / 1000.0
270
+ end
271
+ end
272
+
273
+ def bad
274
+ duration = 400.0
275
+ freq = 100
276
+ tone freq, duration
277
+ sleep duration / 1000.0
278
+ end
279
+
280
+ def talk(str)
281
+ morsecoder = Morsecoder.new str
282
+ morsecoder.each {|frequency, duration| sound(frequency,duration)}
283
+ end
284
+
285
+ def morse_frequency
286
+ Morsecoder.default_frequency
287
+ end
288
+
289
+ def morse_frequency=(frequency)
290
+ Morsecoder.default_frequency = frequency
291
+ end
292
+
293
+ def morse_unit
294
+ Morsecoder.default_unit
295
+ end
296
+
297
+ def morse_unit=(millisec)
298
+ Morsecoder.default_unit = millisec
299
+ end
300
+
301
+ # Private methods
302
+ private
303
+
304
+ def initialize_serialport(dev)
305
+ require 'serialport'
306
+ @sp = SerialPort.new dev, 38400, 8, 1, SerialPort::NONE
307
+ if is_windows?
308
+ @sp.read_timeout = READ_TIMEOUT
309
+ @sp.write_timeout = WRITE_TIMEOUT
310
+ end
311
+ end
312
+
313
+ def initialize_robo
314
+ self.speed DEFAULT_SPEED
315
+ self.drive_duration DEFAULT_DRIVE_DURATION
316
+ self.turn_led 0,0,0
317
+ end
318
+
319
+ def send_command_get_response(cmd = CMD_PING)
320
+ empty_receive_buffer
321
+ sendln cmd
322
+ received_line = receiveln
323
+
324
+ begin
325
+ timeout(READ_TIMEOUT/1000) do
326
+ received_line = receiveln until received_line[0] == cmd[0]
327
+ end
328
+ rescue
329
+ return nil
330
+ end
331
+ received_line.chomp.split(",")
332
+ end
333
+
334
+ def sendln str
335
+ @sp.write str + "\n"
336
+ end
337
+
338
+ def empty_receive_buffer
339
+ @sp.read_timeout = SHORT_READ_TIMEOUT
340
+ begin
341
+ char = @sp.read 1
342
+ end while char && char.length > 0
343
+ @sp.read_timeout = READ_TIMEOUT
344
+ end
345
+
346
+ def receiveln
347
+ if is_windows?
348
+ start_time = Time.now
349
+ str = ""
350
+ while (Time.now - start_time) * 1000.0 < READ_TIMEOUT do
351
+ char = @sp.read 1
352
+ if char.length > 0
353
+ str += char
354
+ break if char == "\n"
355
+ end
356
+ end
357
+ else
358
+ str = @sp.gets
359
+ end
360
+ str
361
+ end
362
+
363
+ def is_windows?
364
+ os = RUBY_PLATFORM.split("-")[1]
365
+ os == 'mswin' or os == 'bccwin' or os == 'mingw' or os == 'mingw32'
366
+ end
367
+ end