rublicatorg 0.0.1
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/README.rdoc +20 -0
- data/bin/rublicatorg +60 -0
- data/examples/name.rb +14 -0
- data/examples/play_sd_card.rb +10 -0
- data/examples/read_sd_card.rb +12 -0
- data/examples/version.rb +12 -0
- data/lib/rublicatorg.rb +404 -0
- data/test/test_rublicatorg.rb +9 -0
- metadata +92 -0
data/README.rdoc
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Provides a programming interface to commnicate with a MakerBot 3D Printer.
|
2
|
+
|
3
|
+
Should eventually be able to do everything that ReplicatorG can do... but for now it is very incomplete.
|
4
|
+
|
5
|
+
=Ruby gem usage
|
6
|
+
|
7
|
+
require "rublicatorg"
|
8
|
+
|
9
|
+
makerbot = RublicatorG.new("/dev/tty.usbserial-FTE3Q0I3")
|
10
|
+
|
11
|
+
puts "Motherboard Firmware Version: #{makerbot.motherboard_version}"
|
12
|
+
|
13
|
+
=Command line
|
14
|
+
|
15
|
+
Also installs a rublicatorg binary that can be called on the command line.
|
16
|
+
|
17
|
+
Usage: rublicatorg [options] [file]
|
18
|
+
-l, --list List the files stored on the SD card
|
19
|
+
-r, --run FILE Run the FILE from the SD card
|
20
|
+
-h, --help Display this screen
|
data/bin/rublicatorg
ADDED
@@ -0,0 +1,60 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "rublicatorg"
|
4
|
+
require "optparse"
|
5
|
+
require "parseconfig"
|
6
|
+
|
7
|
+
options = {}
|
8
|
+
|
9
|
+
optparse = OptionParser.new do |opts|
|
10
|
+
opts.banner = "Usage: rublicatorg [options] [file]"
|
11
|
+
|
12
|
+
options[:port] = false
|
13
|
+
opts.on('-p', '--port PORT', 'Serial port (can also be specified in ~/.rublicatorgrc)') do |p|
|
14
|
+
options[:port] = p
|
15
|
+
end
|
16
|
+
|
17
|
+
options[:list] = false
|
18
|
+
opts.on('-l', '--list', 'List the files stored on the SD card') do |l|
|
19
|
+
options[:list] = l
|
20
|
+
end
|
21
|
+
|
22
|
+
options[:run] = false
|
23
|
+
opts.on('-r', '--run FILE', 'Run the FILE from the SD card') do |r|
|
24
|
+
options[:run] = r
|
25
|
+
end
|
26
|
+
|
27
|
+
opts.on_tail('-h', '--help', 'Display this screen') do
|
28
|
+
puts opts
|
29
|
+
exit
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
optparse.parse!
|
34
|
+
|
35
|
+
begin
|
36
|
+
if options[:port] == false
|
37
|
+
rcfile = File.expand_path("~/.rublicatorgrc")
|
38
|
+
if File.exists?(rcfile)
|
39
|
+
config = ParseConfig.new(rcfile)
|
40
|
+
options[:port] = config.get_value('port')
|
41
|
+
else
|
42
|
+
raise "Serial port not specified and not found in #{rcfile}"
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
makerbot = RublicatorG.new(options[:port])
|
47
|
+
|
48
|
+
case true
|
49
|
+
when options[:list]
|
50
|
+
filenames = makerbot.filenames.collect{|f| f.split(".")[0]}.join(",")
|
51
|
+
puts filenames
|
52
|
+
when options[:run] != false
|
53
|
+
makerbot.run(options[:run])
|
54
|
+
else
|
55
|
+
puts optparse
|
56
|
+
end
|
57
|
+
rescue Exception => e
|
58
|
+
puts "ERROR: #{e.message}"
|
59
|
+
exit(-1)
|
60
|
+
end
|
data/examples/name.rb
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby -wKU
|
2
|
+
|
3
|
+
require "rublicatorg"
|
4
|
+
|
5
|
+
$DEBUG = true
|
6
|
+
|
7
|
+
makerbot = RublicatorG.new("/dev/tty.usbserial-A600emXZ")
|
8
|
+
|
9
|
+
old_name = makerbot.name
|
10
|
+
puts "Old Machine Name: #{old_name}"
|
11
|
+
makerbot.name = "Foo Bar"
|
12
|
+
puts "New Machine Name: #{makerbot.name}"
|
13
|
+
makerbot.name = old_name
|
14
|
+
puts "Reset Old Machine Name: #{makerbot.name}"
|
data/examples/version.rb
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
#!/usr/bin/env ruby -wKU
|
2
|
+
|
3
|
+
require "rublicatorg"
|
4
|
+
|
5
|
+
$DEBUG = true
|
6
|
+
|
7
|
+
makerbot = RublicatorG.new("/dev/tty.usbserial-A600emXZ")
|
8
|
+
|
9
|
+
puts "Motherboard Firmware Version: #{makerbot.motherboard_version} (#{makerbot.motherboard_build_name})"
|
10
|
+
puts "Toolhead 0 Firmware Version: #{makerbot.toolhead_version(0)} (#{makerbot.toolhead_build_name(0)})"
|
11
|
+
makerbot.motherboard_init
|
12
|
+
puts "Machine Name: #{makerbot.name}"
|
data/lib/rublicatorg.rb
ADDED
@@ -0,0 +1,404 @@
|
|
1
|
+
#!/usr/bin/env ruby -wKU
|
2
|
+
|
3
|
+
# RublicatorG - Ruby Makerbot/RepRap Control
|
4
|
+
# Copyright (C) 2011 Tony Buser <tbuser@gmail.com> - http://tonybuser.com
|
5
|
+
#
|
6
|
+
# This program is free software; you can redistribute it and/or modify
|
7
|
+
# it under the terms of the GNU General Public License as published by
|
8
|
+
# the Free Software Foundation; either version 2 of the License
|
9
|
+
#
|
10
|
+
# This program is distributed in the hope that it will be useful,
|
11
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
12
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
13
|
+
# GNU General Public License for more details.
|
14
|
+
#
|
15
|
+
# You should have received a copy of the GNU General Public License
|
16
|
+
# along with this program; if not, write to the Free Software Foundation,
|
17
|
+
# Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
18
|
+
|
19
|
+
# Protocol Definitions:
|
20
|
+
# https://docs.google.com/Doc?docid=0AcWKwJ2SAxDzZGd6amZyY2NfMmdtODRnZ2Ri&hl=en&pli=1
|
21
|
+
# http://replicat.org/sanguino3g#responsecode
|
22
|
+
|
23
|
+
require "rubygems"
|
24
|
+
require "serialport"
|
25
|
+
|
26
|
+
class Array
|
27
|
+
def hex_to_str
|
28
|
+
str = ""
|
29
|
+
self.collect{|b| str << "%c" % b}
|
30
|
+
str
|
31
|
+
end
|
32
|
+
|
33
|
+
def to_hex_str
|
34
|
+
self.collect{|e| "0x%02x" % e}.join(" ")
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
class String
|
39
|
+
def to_hex_str
|
40
|
+
str = ""
|
41
|
+
self.each_byte {|b| str << '0x%02x ' % b}
|
42
|
+
str.strip
|
43
|
+
end
|
44
|
+
|
45
|
+
def to_hex_array
|
46
|
+
arr = []
|
47
|
+
self.each_byte {|b| arr << '0x%02x' % b}
|
48
|
+
arr
|
49
|
+
end
|
50
|
+
|
51
|
+
def from_hex_str
|
52
|
+
data = self.split(' ')
|
53
|
+
str = ""
|
54
|
+
data.each{|h| eval "str += '%c' % #{h}"}
|
55
|
+
str
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
# class Bignum
|
60
|
+
# # This is needed because String#unpack() can't handle little-endian signed longs...
|
61
|
+
# # instead we unpack() as a little-endian unsigned long (i.e. 'V') and then use this
|
62
|
+
# # method to convert to signed long.
|
63
|
+
# def as_signed
|
64
|
+
# -1*(self^0xffffffff) if self > 0xfffffff
|
65
|
+
# end
|
66
|
+
# end
|
67
|
+
|
68
|
+
$DEV ||= nil
|
69
|
+
|
70
|
+
class RublicatorG
|
71
|
+
VERSION = "0.0.1"
|
72
|
+
|
73
|
+
START_BYTE = 0xd5
|
74
|
+
|
75
|
+
@@motherboard_codes = {
|
76
|
+
# query
|
77
|
+
"version" => 0x00,
|
78
|
+
"init" => 0x01,
|
79
|
+
"get_buffer_size" => 0x02,
|
80
|
+
"clear_buffer" => 0x03,
|
81
|
+
"get_position" => 0x04,
|
82
|
+
"get_range" => 0x05,
|
83
|
+
"set_range" => 0x06,
|
84
|
+
"abort" => 0x07,
|
85
|
+
"pause" => 0x08,
|
86
|
+
"probe" => 0x09,
|
87
|
+
"tool_query" => 0x0a,
|
88
|
+
"is_finished" => 0x0b,
|
89
|
+
"read_eeprom" => 0x0c,
|
90
|
+
"write_eeprom" => 0x0d,
|
91
|
+
"capture_to_file" => 0x0e,
|
92
|
+
"end_capture" => 0x0f,
|
93
|
+
"playback_file" => 0x10,
|
94
|
+
"reset" => 0x11,
|
95
|
+
"next_filename" => 0x12,
|
96
|
+
"read_debug_registers" => 0x13,
|
97
|
+
"get_build_name" => 0x14,
|
98
|
+
# buffered
|
99
|
+
"queue_point_absolute" => 0x81,
|
100
|
+
"set_position_registers" => 0x82,
|
101
|
+
"find_axes_min" => 0x83,
|
102
|
+
"find_axes_max" => 0x84,
|
103
|
+
"delay" => 0x85,
|
104
|
+
"change_tool" => 0x86,
|
105
|
+
"wait_for_tool" => 0x87,
|
106
|
+
"tool_command" => 0x88,
|
107
|
+
"enable_axes" => 0x89
|
108
|
+
}
|
109
|
+
|
110
|
+
@@toolhead_codes = {
|
111
|
+
"version" => 0x00,
|
112
|
+
"init" => 0x01,
|
113
|
+
"get_temperature" => 0x02,
|
114
|
+
"set_motor1_pwm" => 0x04,
|
115
|
+
"set_motor2_pwm" => 0x05,
|
116
|
+
"set_motor1_rpm" => 0x06,
|
117
|
+
"set_motor2_rpm" => 0x07,
|
118
|
+
"set_motor1_direction" => 0x08,
|
119
|
+
"set_motor2_direction" => 0x09,
|
120
|
+
"toggle_motor1" => 0x0a,
|
121
|
+
"toggle_motor2" => 0x0b,
|
122
|
+
"toggle_fan" => 0x0c,
|
123
|
+
"toggle_valve" => 0x0d,
|
124
|
+
"set_servo1_position" => 0x0e,
|
125
|
+
"set_servo2_position" => 0x0f,
|
126
|
+
"filament_status" => 0x10,
|
127
|
+
"get_motor1_rpm" => 0x11,
|
128
|
+
"get_motor2_rpm" => 0x12,
|
129
|
+
"get_motor1_pwm" => 0x13,
|
130
|
+
"get_motor2_pwm" => 0x14,
|
131
|
+
"select_tool" => 0x15,
|
132
|
+
"is_tool_ready" => 0x16,
|
133
|
+
|
134
|
+
"get_build_name" => 0x22
|
135
|
+
}
|
136
|
+
|
137
|
+
@@response_codes = {
|
138
|
+
0x00 => "Generic error, packet discarded.",
|
139
|
+
0x01 => "Success.",
|
140
|
+
0x02 => "Action buffer overflow, entire packet discarded.",
|
141
|
+
0x03 => "CRC mismatch, packet discarded.",
|
142
|
+
0x04 => "Query packet too big, packet discarded.",
|
143
|
+
0x05 => "Command not supported/recognized.",
|
144
|
+
0x06 => "Success; expect more packets. Used when a single reponse packet cannot contain the entire message to be retrieved.",
|
145
|
+
0x07 => "Downstream timeout (for example, a toolhead timed out)."
|
146
|
+
}
|
147
|
+
|
148
|
+
@@sd_response_codes = {
|
149
|
+
0x00 => "operation was successful",
|
150
|
+
0x01 => "no SD card was present",
|
151
|
+
0x02 => "SD card init failed",
|
152
|
+
0x03 => "partition table could not be read",
|
153
|
+
0x04 => "filesystem could not be opened",
|
154
|
+
0x05 => "root directory could not be opened",
|
155
|
+
0x06 => "SD card is locked",
|
156
|
+
0x07 => "no such file"
|
157
|
+
}
|
158
|
+
|
159
|
+
@@mutex = Mutex.new
|
160
|
+
|
161
|
+
def initialize(dev = nil)
|
162
|
+
dev ||= $DEV
|
163
|
+
|
164
|
+
@@mutex.synchronize do
|
165
|
+
begin
|
166
|
+
@sp = SerialPort.new(dev, 115200, 8, 1, SerialPort::NONE)
|
167
|
+
|
168
|
+
# @sp.flow_control = SerialPort::HARD
|
169
|
+
@sp.read_timeout = 5000
|
170
|
+
$stderr.puts "Cannot connect to #{dev}" if @sp.nil?
|
171
|
+
# rescue Errno::EBUSY
|
172
|
+
rescue
|
173
|
+
raise "Cannot connect. The serial port device is busy or unavailable."
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
puts "Connected to: serial port #{dev}" if $DEBUG
|
178
|
+
end
|
179
|
+
|
180
|
+
# Close the connection
|
181
|
+
def close
|
182
|
+
@@mutex.synchronize do
|
183
|
+
@sp.close if @sp and not @sp.closed?
|
184
|
+
end
|
185
|
+
end
|
186
|
+
|
187
|
+
# Returns true if the connection to the machine is open; false otherwise
|
188
|
+
def connected?
|
189
|
+
not @sp.closed?
|
190
|
+
end
|
191
|
+
|
192
|
+
def send_and_receive(payload, request_reply=true)
|
193
|
+
msg = [START_BYTE] + [payload.size] + payload + [crc8(payload.hex_to_str)]
|
194
|
+
|
195
|
+
send_cmd(msg)
|
196
|
+
|
197
|
+
# FIXME: ugly hackish timing, there are much more intelligent ways to handle the delay
|
198
|
+
sleep(1)
|
199
|
+
|
200
|
+
if request_reply
|
201
|
+
ok,response = recv_reply
|
202
|
+
|
203
|
+
if ok #and response[1] == op[1]
|
204
|
+
# data = response[3..response.size]
|
205
|
+
data = response
|
206
|
+
# TODO ? if data contains a \n character, ruby seems to pass the parts before and after the \n
|
207
|
+
# as two different parameters... we need to encode the data into a format that doesn't
|
208
|
+
# contain any \n's and then decode it in the receiving method
|
209
|
+
# data = data.to_hex_str
|
210
|
+
elsif !ok
|
211
|
+
$stderr.puts response
|
212
|
+
data = false
|
213
|
+
else
|
214
|
+
$stderr.puts "ERROR: Unexpected response #{response}"
|
215
|
+
data = false
|
216
|
+
end
|
217
|
+
else
|
218
|
+
data = true
|
219
|
+
end
|
220
|
+
data
|
221
|
+
end
|
222
|
+
|
223
|
+
def send_cmd(payload)
|
224
|
+
@@mutex.synchronize do
|
225
|
+
puts "Sending message: #{payload.to_hex_str}" if $DEBUG
|
226
|
+
payload.each do |b|
|
227
|
+
@sp.putc b
|
228
|
+
end
|
229
|
+
end
|
230
|
+
end
|
231
|
+
|
232
|
+
def recv_reply
|
233
|
+
@@mutex.synchronize do
|
234
|
+
begin
|
235
|
+
header = @sp.sysread(2)
|
236
|
+
start_byte = header[0..0].to_hex_str
|
237
|
+
length = header[1..1].unpack("C")[0]
|
238
|
+
|
239
|
+
payload = @sp.sysread(length)
|
240
|
+
response_code = payload[0..0].to_hex_str
|
241
|
+
msg = payload[1..-1]
|
242
|
+
|
243
|
+
crc = @sp.sysread(1).to_hex_str
|
244
|
+
|
245
|
+
# rescue EOFError
|
246
|
+
rescue
|
247
|
+
raise "Cannot read from the machine. Make sure the device is on and connected."
|
248
|
+
end
|
249
|
+
|
250
|
+
puts "Received Message: start_byte: #{start_byte} length: #{length} response_code: #{response_code} payload: #{msg.to_hex_str} crc: #{crc}" if $DEBUG
|
251
|
+
|
252
|
+
if response_code != '0x81'
|
253
|
+
error = "ERROR: #{@@response_codes[response_code]}"
|
254
|
+
return [false,error]
|
255
|
+
end
|
256
|
+
|
257
|
+
return [true,msg]
|
258
|
+
end
|
259
|
+
end
|
260
|
+
|
261
|
+
def crc8(str, crc=0)
|
262
|
+
str.each_byte do |byte|
|
263
|
+
# * This is a Java implementation of the IButton/Maxim 8-bit CRC. Code ported
|
264
|
+
# * from the AVR-libc implementation, which is used on the RR3G end.
|
265
|
+
# crc = (crc ^ data) & 0xff; // i loathe java's promotion rules
|
266
|
+
# for (int i = 0; i < 8; i++) {
|
267
|
+
# if ((crc & 0x01) != 0) {
|
268
|
+
# crc = ((crc >>> 1) ^ 0x8c) & 0xff;
|
269
|
+
# } else {
|
270
|
+
# crc = (crc >>> 1) & 0xff;
|
271
|
+
# }
|
272
|
+
# }
|
273
|
+
|
274
|
+
crc = (crc ^ byte) & 0xff
|
275
|
+
|
276
|
+
8.times do |i|
|
277
|
+
unless crc & 0x01 == 0
|
278
|
+
crc = ((crc >> 1) ^ 0x8c) & 0xff
|
279
|
+
else
|
280
|
+
crc = (crc >> 1) & 0xff
|
281
|
+
end
|
282
|
+
end
|
283
|
+
end
|
284
|
+
|
285
|
+
crc
|
286
|
+
end
|
287
|
+
|
288
|
+
def motherboard_version
|
289
|
+
if result = send_and_receive([@@motherboard_codes["version"]])
|
290
|
+
result = result.unpack("v")[0]
|
291
|
+
"#{result/100}.#{result % 100}"
|
292
|
+
else
|
293
|
+
false
|
294
|
+
end
|
295
|
+
end
|
296
|
+
|
297
|
+
def motherboard_build_name
|
298
|
+
if result = send_and_receive([@@motherboard_codes["get_build_name"]])
|
299
|
+
result
|
300
|
+
else
|
301
|
+
false
|
302
|
+
end
|
303
|
+
end
|
304
|
+
|
305
|
+
def motherboard_init
|
306
|
+
if result = send_and_receive([@@motherboard_codes["init"]])
|
307
|
+
result
|
308
|
+
else
|
309
|
+
false
|
310
|
+
end
|
311
|
+
end
|
312
|
+
|
313
|
+
def name
|
314
|
+
payload = []
|
315
|
+
payload << @@motherboard_codes["read_eeprom"]
|
316
|
+
# at position: 32, integer uint16 to 2 bytes
|
317
|
+
payload << (32 & 0xff)
|
318
|
+
payload << ((32 >> 8) & 0xff)
|
319
|
+
# length to read: 16
|
320
|
+
payload << 16
|
321
|
+
|
322
|
+
if result = send_and_receive(payload)
|
323
|
+
result
|
324
|
+
else
|
325
|
+
false
|
326
|
+
end
|
327
|
+
end
|
328
|
+
|
329
|
+
def name=(str)
|
330
|
+
raise "Name too long. Name must be <= 16 characters." if str.size > 16
|
331
|
+
|
332
|
+
payload = []
|
333
|
+
payload << @@motherboard_codes["write_eeprom"]
|
334
|
+
# at position: 32, integer uint16 to 2 bytes
|
335
|
+
payload << (32 & 0xff)
|
336
|
+
payload << ((32 >> 8) & 0xff)
|
337
|
+
# length to write
|
338
|
+
payload << 16
|
339
|
+
# send the name
|
340
|
+
str.ljust(16).each_byte do |byte|
|
341
|
+
payload << byte
|
342
|
+
end
|
343
|
+
|
344
|
+
if result = send_and_receive(payload)
|
345
|
+
result
|
346
|
+
else
|
347
|
+
false
|
348
|
+
end
|
349
|
+
end
|
350
|
+
|
351
|
+
def toolhead_version(tool_id=0)
|
352
|
+
payload = [@@motherboard_codes["tool_query"], tool_id, @@toolhead_codes["version"]]
|
353
|
+
|
354
|
+
if result = send_and_receive(payload)
|
355
|
+
result = result.unpack("v")[0]
|
356
|
+
"#{result/100}.#{result % 100}"
|
357
|
+
else
|
358
|
+
false
|
359
|
+
end
|
360
|
+
end
|
361
|
+
|
362
|
+
def toolhead_build_name(tool_id=0)
|
363
|
+
payload = [@@motherboard_codes["tool_query"], tool_id, @@toolhead_codes["get_build_name"]]
|
364
|
+
|
365
|
+
if result = send_and_receive(payload)
|
366
|
+
result
|
367
|
+
else
|
368
|
+
false
|
369
|
+
end
|
370
|
+
end
|
371
|
+
|
372
|
+
def filenames
|
373
|
+
filenames = []
|
374
|
+
|
375
|
+
# keep reading until result is blank? or a max of 100
|
376
|
+
100.times do |x|
|
377
|
+
filename = send_and_receive([@@motherboard_codes["next_filename"], x == 0 ? 1 : 0]).gsub(/\000/, '')
|
378
|
+
if filename == ""
|
379
|
+
break
|
380
|
+
else
|
381
|
+
filenames << filename
|
382
|
+
end
|
383
|
+
end
|
384
|
+
|
385
|
+
filenames
|
386
|
+
end
|
387
|
+
|
388
|
+
def run(filename)
|
389
|
+
raise "Filename too long. Name must be <= 12 characters." if filename.size > 12
|
390
|
+
|
391
|
+
payload = []
|
392
|
+
payload << @@motherboard_codes["playback_file"]
|
393
|
+
filename.each_byte do |byte|
|
394
|
+
payload << byte
|
395
|
+
end
|
396
|
+
|
397
|
+
if result = send_and_receive(payload)
|
398
|
+
result
|
399
|
+
else
|
400
|
+
false
|
401
|
+
end
|
402
|
+
end
|
403
|
+
|
404
|
+
end
|
metadata
ADDED
@@ -0,0 +1,92 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: rublicatorg
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease: false
|
5
|
+
segments:
|
6
|
+
- 0
|
7
|
+
- 0
|
8
|
+
- 1
|
9
|
+
version: 0.0.1
|
10
|
+
platform: ruby
|
11
|
+
authors:
|
12
|
+
- Tony Buser
|
13
|
+
autorequire:
|
14
|
+
bindir: bin
|
15
|
+
cert_chain: []
|
16
|
+
|
17
|
+
date: 2011-11-10 00:00:00 -05:00
|
18
|
+
default_executable:
|
19
|
+
dependencies:
|
20
|
+
- !ruby/object:Gem::Dependency
|
21
|
+
name: serialport
|
22
|
+
prerelease: false
|
23
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
24
|
+
requirements:
|
25
|
+
- - ">="
|
26
|
+
- !ruby/object:Gem::Version
|
27
|
+
segments:
|
28
|
+
- 0
|
29
|
+
version: "0"
|
30
|
+
type: :runtime
|
31
|
+
version_requirements: *id001
|
32
|
+
- !ruby/object:Gem::Dependency
|
33
|
+
name: parseconfig
|
34
|
+
prerelease: false
|
35
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
36
|
+
requirements:
|
37
|
+
- - ">="
|
38
|
+
- !ruby/object:Gem::Version
|
39
|
+
segments:
|
40
|
+
- 0
|
41
|
+
version: "0"
|
42
|
+
type: :runtime
|
43
|
+
version_requirements: *id002
|
44
|
+
description: Communicate with a MakerBot 3D Printer
|
45
|
+
email: tbuser@gmail.com
|
46
|
+
executables:
|
47
|
+
- rublicatorg
|
48
|
+
extensions: []
|
49
|
+
|
50
|
+
extra_rdoc_files: []
|
51
|
+
|
52
|
+
files:
|
53
|
+
- lib/rublicatorg.rb
|
54
|
+
- bin/rublicatorg
|
55
|
+
- examples/name.rb
|
56
|
+
- examples/play_sd_card.rb
|
57
|
+
- examples/read_sd_card.rb
|
58
|
+
- examples/version.rb
|
59
|
+
- test/test_rublicatorg.rb
|
60
|
+
- README.rdoc
|
61
|
+
has_rdoc: true
|
62
|
+
homepage: http://github.com/tbuser/RublicatorG
|
63
|
+
licenses: []
|
64
|
+
|
65
|
+
post_install_message:
|
66
|
+
rdoc_options: []
|
67
|
+
|
68
|
+
require_paths:
|
69
|
+
- lib
|
70
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
71
|
+
requirements:
|
72
|
+
- - ">="
|
73
|
+
- !ruby/object:Gem::Version
|
74
|
+
segments:
|
75
|
+
- 0
|
76
|
+
version: "0"
|
77
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
78
|
+
requirements:
|
79
|
+
- - ">="
|
80
|
+
- !ruby/object:Gem::Version
|
81
|
+
segments:
|
82
|
+
- 0
|
83
|
+
version: "0"
|
84
|
+
requirements: []
|
85
|
+
|
86
|
+
rubyforge_project: rublicatorg
|
87
|
+
rubygems_version: 1.3.6
|
88
|
+
signing_key:
|
89
|
+
specification_version: 3
|
90
|
+
summary: ReplicatorG in Ruby!
|
91
|
+
test_files: []
|
92
|
+
|