mi100 0.4.0 → 0.4.2

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 (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