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.
- data/lib/mi100/version.rb +3 -3
- data/lib/mi100.rb +367 -315
- data/spec/mi100_spec.rb +408 -301
- 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
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
end
|
95
|
-
|
96
|
-
def
|
97
|
-
response = send_command_get_response
|
98
|
-
response[1].to_i
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
send_command_get_response
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
end
|
192
|
-
|
193
|
-
def
|
194
|
-
|
195
|
-
end
|
196
|
-
|
197
|
-
def
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
if
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
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
|