robobuilder 0.3.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.
- data/.document +1 -0
- data/COPYING +680 -0
- data/README.md +44 -0
- data/Rakefile +176 -0
- data/ext/robobuilder.cc +341 -0
- data/lib/robobuilder_ext.rb +378 -0
- metadata +70 -0
@@ -0,0 +1,378 @@
|
|
1
|
+
class Robobuilder
|
2
|
+
|
3
|
+
HEADER = "\xFF\xFF\xAA\x55\xAA\x55\x37\xBA"
|
4
|
+
MAX_READ_TRIES = 2
|
5
|
+
MAX_CMD_TRIES = 5
|
6
|
+
DELAY = 0.2
|
7
|
+
MOTION_TIMEOUT = 10 * 10
|
8
|
+
GETUP_A = 1
|
9
|
+
GETUP_B = 2
|
10
|
+
TURN_LEFT = 3
|
11
|
+
MOTION_FORWARD = 4
|
12
|
+
TURN_RIGHT = 5
|
13
|
+
MOVE_LEFT = 6
|
14
|
+
MOTION_BASIC_POSTURE = 7
|
15
|
+
MOVE_RIGHT = 8
|
16
|
+
ATTACK_LEFT = 9
|
17
|
+
MOVE_BACKWARD = 10
|
18
|
+
ATTACK_RIGHT = 11
|
19
|
+
|
20
|
+
class << self
|
21
|
+
|
22
|
+
alias_method :orig_new, :new
|
23
|
+
|
24
|
+
def new( device_name = '/dev/ttyS0' )
|
25
|
+
retval = orig_new device_name
|
26
|
+
# Do some communication to synchronise.
|
27
|
+
3.times { retval.write "\xFF\xE0\xFB\x01\x00\x1A" }
|
28
|
+
retval
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
32
|
+
|
33
|
+
alias_method :orig_write, :write
|
34
|
+
|
35
|
+
def write( data )
|
36
|
+
n = orig_write( data )
|
37
|
+
if n < data.size
|
38
|
+
raise "Error writing to serial port (only #{n} of #{data.size} bytes " +
|
39
|
+
"were written)"
|
40
|
+
end
|
41
|
+
n
|
42
|
+
end
|
43
|
+
|
44
|
+
alias_method :orig_read, :read
|
45
|
+
|
46
|
+
def read( n )
|
47
|
+
t = 0
|
48
|
+
retval = ''
|
49
|
+
while retval.size < n and t < MAX_READ_TRIES
|
50
|
+
retval += orig_read n - retval.size
|
51
|
+
t += 1
|
52
|
+
end
|
53
|
+
if retval.size < n
|
54
|
+
raise "Not receiving any response from serial device " +
|
55
|
+
"(only received #{retval.size}/#{n} bytes)"
|
56
|
+
end
|
57
|
+
retval
|
58
|
+
end
|
59
|
+
|
60
|
+
alias_method :orig_timeout, :timeout
|
61
|
+
|
62
|
+
def timeout( value = nil )
|
63
|
+
retval = orig_timeout
|
64
|
+
if value
|
65
|
+
begin
|
66
|
+
self.timeout = value
|
67
|
+
yield
|
68
|
+
ensure
|
69
|
+
self.timeout = retval
|
70
|
+
end
|
71
|
+
end
|
72
|
+
retval
|
73
|
+
end
|
74
|
+
|
75
|
+
def checksum( *contents )
|
76
|
+
contents.inject { |a,b| a ^ b }
|
77
|
+
end
|
78
|
+
|
79
|
+
def hexdump( *contents )
|
80
|
+
contents.collect { |x| "%02X" % x }.join( ' ' )
|
81
|
+
end
|
82
|
+
|
83
|
+
def response( type )
|
84
|
+
header = read HEADER.size
|
85
|
+
if header != HEADER
|
86
|
+
raise "Expecting #{hexdump( *HEADER.unpack( 'C' * HEADER.size ) )} " +
|
87
|
+
"(but received #{hexdump( *header.unpack( 'C' * header.size ) )})"
|
88
|
+
end
|
89
|
+
result_type = read( 1 ).unpack( 'C' ).first
|
90
|
+
if result_type != type
|
91
|
+
raise "Result was of type #{ "%02X" % result_type } but command was " +
|
92
|
+
"of type #{ "%02X" % type }"
|
93
|
+
end
|
94
|
+
read 1
|
95
|
+
size = read( 4 ).unpack( 'N' ).first
|
96
|
+
unless size.between? 1, 16
|
97
|
+
raise "Size of result should be between 1 and 16 (but was #{size})"
|
98
|
+
end
|
99
|
+
contents = read size
|
100
|
+
checksum_real = checksum( *contents.unpack( 'C' * contents.size ) )
|
101
|
+
checksum_nominal = read( 1 ).unpack( 'C' ).first
|
102
|
+
if type != 0x1A and checksum_real != checksum_nominal
|
103
|
+
puts "Checksum was #{ "%02X" % checksum_real } but should be " +
|
104
|
+
"#{ "%02X" % checksum_nominal }"
|
105
|
+
end
|
106
|
+
contents
|
107
|
+
end
|
108
|
+
|
109
|
+
def command( type, *contents )
|
110
|
+
cmd = HEADER + [ type, 0x00, contents.size ].pack( 'CCN' ) +
|
111
|
+
contents.pack( 'C' * contents.size ) +
|
112
|
+
[ checksum( *contents ) ].pack( 'C' )
|
113
|
+
result = nil
|
114
|
+
t = 0
|
115
|
+
e = nil
|
116
|
+
while result == nil and t < MAX_CMD_TRIES
|
117
|
+
begin
|
118
|
+
e = nil
|
119
|
+
write cmd
|
120
|
+
result = response type
|
121
|
+
rescue Exception => e
|
122
|
+
puts "Recovering from error: #{e.to_s}"
|
123
|
+
flush
|
124
|
+
result = nil
|
125
|
+
end
|
126
|
+
t += 1
|
127
|
+
end
|
128
|
+
raise e unless result
|
129
|
+
result
|
130
|
+
end
|
131
|
+
|
132
|
+
def serial_number
|
133
|
+
command( 0x0C, 1 ).to_i
|
134
|
+
end
|
135
|
+
|
136
|
+
def firmware
|
137
|
+
command( 0x12, 1 ).unpack 'CC'
|
138
|
+
end
|
139
|
+
|
140
|
+
def motion( n )
|
141
|
+
sleep DELAY
|
142
|
+
timeout( MOTION_TIMEOUT ) do
|
143
|
+
command( 0x14, n ).unpack( 'C' ).first
|
144
|
+
end
|
145
|
+
self
|
146
|
+
end
|
147
|
+
|
148
|
+
def voice( n )
|
149
|
+
command( 0x15, n ).unpack( 'C' ).first
|
150
|
+
end
|
151
|
+
|
152
|
+
def distance
|
153
|
+
command( 0x16, 1 ).unpack( 'n' ).first / 256.0
|
154
|
+
end
|
155
|
+
|
156
|
+
def button # TODO: wait for button
|
157
|
+
command( 0x18, 1 ).unpack( 'S' ).first
|
158
|
+
end
|
159
|
+
|
160
|
+
def remote # ???
|
161
|
+
command( 0x19, 1 )
|
162
|
+
end
|
163
|
+
|
164
|
+
def accelerometer
|
165
|
+
command( 0x1A, 1 ).unpack( 'sss' )
|
166
|
+
end
|
167
|
+
|
168
|
+
def direct( on )
|
169
|
+
sleep DELAY
|
170
|
+
if on
|
171
|
+
# Alternatively press and hold button PF2 while turning on RBC power.
|
172
|
+
command 0x10, 1
|
173
|
+
else
|
174
|
+
write "\xFF\xE0\xFB\x01\x00\x1A"
|
175
|
+
end
|
176
|
+
sleep DELAY
|
177
|
+
end
|
178
|
+
|
179
|
+
def a
|
180
|
+
motion GETUP_A
|
181
|
+
end
|
182
|
+
|
183
|
+
def b
|
184
|
+
motion GETUP_B
|
185
|
+
end
|
186
|
+
|
187
|
+
def turn_left
|
188
|
+
motion TURN_LEFT
|
189
|
+
end
|
190
|
+
|
191
|
+
def forward
|
192
|
+
motion MOTION_FORWARD
|
193
|
+
end
|
194
|
+
|
195
|
+
def turn_right
|
196
|
+
motion TURN_RIGHT
|
197
|
+
end
|
198
|
+
|
199
|
+
def left
|
200
|
+
motion MOVE_LEFT
|
201
|
+
end
|
202
|
+
|
203
|
+
def basic
|
204
|
+
motion MOTION_BASIC_POSTURE
|
205
|
+
end
|
206
|
+
|
207
|
+
def right
|
208
|
+
motion MOVE_RIGHT
|
209
|
+
end
|
210
|
+
|
211
|
+
def attack_left
|
212
|
+
motion ATTACK_LEFT
|
213
|
+
end
|
214
|
+
|
215
|
+
def backward
|
216
|
+
motion MOVE_BACKWARD
|
217
|
+
end
|
218
|
+
|
219
|
+
def attack_right
|
220
|
+
motion ATTACK_RIGHT
|
221
|
+
end
|
222
|
+
|
223
|
+
def run( n )
|
224
|
+
case n
|
225
|
+
when 1 .. 10
|
226
|
+
motion n + 11
|
227
|
+
when 11 .. 20
|
228
|
+
motion n + 22
|
229
|
+
else
|
230
|
+
raise "Program number must be in 1 .. 20 (was #{n})"
|
231
|
+
end
|
232
|
+
end
|
233
|
+
|
234
|
+
def rbc(response, *contents)
|
235
|
+
cmd = "\xFF" + contents.pack('C' * contents.size) +
|
236
|
+
[checksum(*contents) & 0x7F].pack('C')
|
237
|
+
#puts cmd.unpack('C' * cmd.size).collect { |x| "0x%X" % x }.join ' '
|
238
|
+
result = nil
|
239
|
+
t = 0
|
240
|
+
e = nil
|
241
|
+
while result == nil and t < MAX_CMD_TRIES
|
242
|
+
begin
|
243
|
+
e = nil
|
244
|
+
write cmd
|
245
|
+
result = read(response).unpack 'C' * response
|
246
|
+
rescue Exception => e
|
247
|
+
puts "Recovering from error: #{e.to_s}"
|
248
|
+
flush
|
249
|
+
result = nil
|
250
|
+
end
|
251
|
+
t += 1
|
252
|
+
end
|
253
|
+
raise e unless result
|
254
|
+
result
|
255
|
+
end
|
256
|
+
|
257
|
+
def position_move(id, target, torque)
|
258
|
+
raise "id must be in 0 .. 30 (but was #{id})" unless id.between? 0, 30
|
259
|
+
raise "target must be in 0 .. 254 (but was #{target})" unless target.between? 0, 254
|
260
|
+
raise "torque must be in 0 .. 4 (but was #{torque})" unless torque.between? 0, 4
|
261
|
+
rbc 2, (torque << 5) | id, target
|
262
|
+
end
|
263
|
+
|
264
|
+
def synchronised_position_move
|
265
|
+
end
|
266
|
+
|
267
|
+
def status_read(id)
|
268
|
+
raise "id must be in 0 .. 30 (but was #{id})" unless id.between? 0, 30
|
269
|
+
rbc 2, (5 << 5) | id, 0
|
270
|
+
end
|
271
|
+
|
272
|
+
def passive_wck(id)
|
273
|
+
raise "id must be in 0 .. 30 (but was #{id})" unless id.between? 0, 30
|
274
|
+
rbc 2, (6 << 5) | id, 1 << 4
|
275
|
+
end
|
276
|
+
|
277
|
+
def wheel_wck(id, speed)
|
278
|
+
raise "id must be in 0 .. 30 (but was #{id})" unless id.between? 0, 30
|
279
|
+
raise "speed must be in -15 .. 15 (but was #{speed})" unless speed.between? -15, 15
|
280
|
+
rbc 2, (6 << 5) | id, ((speed >= 0 ? 3 : 4) << 4) | speed.abs
|
281
|
+
end
|
282
|
+
|
283
|
+
def brake_wck
|
284
|
+
rbc 2, (6 << 5) | 31, 2 << 4
|
285
|
+
end
|
286
|
+
|
287
|
+
# baud rate set
|
288
|
+
|
289
|
+
# p, d gain set
|
290
|
+
|
291
|
+
# p, d gain read
|
292
|
+
|
293
|
+
# i gain set
|
294
|
+
|
295
|
+
# i gain read
|
296
|
+
|
297
|
+
# runtime p, d gain set
|
298
|
+
|
299
|
+
# runtime i gain set
|
300
|
+
|
301
|
+
# id set
|
302
|
+
|
303
|
+
def speed_set(id, speed, accel)
|
304
|
+
raise "id must be in 0 .. 30 (but was #{id})" unless id.between? 0, 30
|
305
|
+
raise "speed must be in 0 .. 30 (but was #{speed})" unless speed.between? 0, 30
|
306
|
+
raise "accel must be in 20 .. 100 (but was #{accel})" unless accel.between? 20, 120
|
307
|
+
rbc 2, (7 << 5) | id, 0x0D, speed, accel
|
308
|
+
end
|
309
|
+
|
310
|
+
def speed_read(id)
|
311
|
+
raise "id must be in 0 .. 30 (but was #{id})" unless id.between? 0, 30
|
312
|
+
rbc 2, (7 << 5) | id, 0x0E, 0, 0
|
313
|
+
end
|
314
|
+
|
315
|
+
def runtime_speed_set(id, speed, accel)
|
316
|
+
raise "id must be in 0 .. 30 (but was #{id})" unless id.between? 0, 30
|
317
|
+
raise "speed must be in 0 .. 30 (but was #{speed})" unless speed.between? 0, 30
|
318
|
+
raise "accel must be in 20 .. 100 (but was #{accel})" unless accel.between? 20, 120
|
319
|
+
rbc 2, (7 << 5) | id, 0x17, speed, accel
|
320
|
+
end
|
321
|
+
|
322
|
+
def overload_set(id, overload)
|
323
|
+
raise "id must be in 0 .. 30 (but was #{id})" unless id.between? 0, 30
|
324
|
+
unless overload.between? 33, 199
|
325
|
+
raise "overload must be in 33 .. 199 (but was #{overload})"
|
326
|
+
end
|
327
|
+
retval = rbc 2, (7 << 5) | id, 0x0F, overload, overload
|
328
|
+
if retval[0] != retval[1]
|
329
|
+
raise "Unexpected return value #{retval.inspect} by overload set"
|
330
|
+
end
|
331
|
+
retval.first
|
332
|
+
end
|
333
|
+
|
334
|
+
def overload_read(id)
|
335
|
+
raise "id must be in 0 .. 30 (but was #{id})" unless id.between? 0, 30
|
336
|
+
retval = rbc 2, (7 << 5) | id, 0x10, 0, 0
|
337
|
+
if retval[0] != retval[1]
|
338
|
+
raise "Unexpected return value #{retval.inspect} by overload read"
|
339
|
+
end
|
340
|
+
retval.first
|
341
|
+
end
|
342
|
+
|
343
|
+
def boundary_set(id, lower, upper)
|
344
|
+
raise "id must be in 0 .. 30 (but was #{id})" unless id.between? 0, 30
|
345
|
+
raise "lower must be in 0 .. 254 (but was #{lower})" unless lower.between? 0, 254
|
346
|
+
raise "upper must be in 0 .. 254 (but was #{upper})" unless upper.between? 0, 254
|
347
|
+
rbc 2, (7 << 5) | id, 0x11, lower, upper
|
348
|
+
end
|
349
|
+
|
350
|
+
def boundary_read(id)
|
351
|
+
raise "id must be in 0 .. 30 (but was #{id})" unless id.between? 0, 30
|
352
|
+
rbc 2, (7 << 5) | id, 0x12, 0, 0
|
353
|
+
end
|
354
|
+
|
355
|
+
# I/O write
|
356
|
+
|
357
|
+
# I/O read
|
358
|
+
|
359
|
+
# motion data write
|
360
|
+
|
361
|
+
# motion data read
|
362
|
+
|
363
|
+
def precision_move(id, target, torque)
|
364
|
+
raise "id must be in 0 .. 253 (but was #{id})" unless id.between? 0, 253
|
365
|
+
raise "target must be in 0 .. 1023 (but was #{target})" unless target.between? 0, 1023
|
366
|
+
raise "torque must be in 0 .. 254 (but was #{torque})" unless torque.between? 0, 254
|
367
|
+
retval = rbc 2, 7 << 5, 0xC8, id, torque, target >> 6, (target << 1) & 0x7E
|
368
|
+
(retval[0] << 6) | (retval[1] >> 1)
|
369
|
+
end
|
370
|
+
|
371
|
+
def precision_read(id)
|
372
|
+
raise "id must be in 0 .. 253 (but was #{id})" unless id.between? 0, 253
|
373
|
+
retval = rbc 2, 7 << 5, 0xC9, id, id
|
374
|
+
(retval[0] << 6) | (retval[1] >> 1)
|
375
|
+
end
|
376
|
+
|
377
|
+
end
|
378
|
+
|
metadata
ADDED
@@ -0,0 +1,70 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: robobuilder
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease:
|
5
|
+
version: 0.3.0
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Jan Wedekind
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
|
13
|
+
date: 2011-07-17 00:00:00 Z
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: rake
|
17
|
+
prerelease: false
|
18
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
19
|
+
none: false
|
20
|
+
requirements:
|
21
|
+
- - ">="
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: "0"
|
24
|
+
type: :development
|
25
|
+
version_requirements: *id001
|
26
|
+
description: This Ruby extension defines the Robobuilder class for communicating a Robobuilder via the serial port or a Bluetooth serial connection.
|
27
|
+
email: jan@wedesoft.de
|
28
|
+
executables: []
|
29
|
+
|
30
|
+
extensions:
|
31
|
+
- Rakefile
|
32
|
+
extra_rdoc_files: []
|
33
|
+
|
34
|
+
files:
|
35
|
+
- Rakefile
|
36
|
+
- README.md
|
37
|
+
- COPYING
|
38
|
+
- .document
|
39
|
+
- lib/robobuilder_ext.rb
|
40
|
+
- ext/robobuilder.cc
|
41
|
+
homepage: http://wedesoft.github.com/robobuilder/
|
42
|
+
licenses: []
|
43
|
+
|
44
|
+
post_install_message:
|
45
|
+
rdoc_options: []
|
46
|
+
|
47
|
+
require_paths:
|
48
|
+
- lib
|
49
|
+
- ext
|
50
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
51
|
+
none: false
|
52
|
+
requirements:
|
53
|
+
- - ">="
|
54
|
+
- !ruby/object:Gem::Version
|
55
|
+
version: "0"
|
56
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: "0"
|
62
|
+
requirements: []
|
63
|
+
|
64
|
+
rubyforge_project:
|
65
|
+
rubygems_version: 1.8.5
|
66
|
+
signing_key:
|
67
|
+
specification_version: 3
|
68
|
+
summary: Ruby extension for controlling a Robobuilder
|
69
|
+
test_files: []
|
70
|
+
|