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.
Files changed (7) hide show
  1. data/.document +1 -0
  2. data/COPYING +680 -0
  3. data/README.md +44 -0
  4. data/Rakefile +176 -0
  5. data/ext/robobuilder.cc +341 -0
  6. data/lib/robobuilder_ext.rb +378 -0
  7. 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
+