farmbot-serial 0.0.6 → 0.0.7
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +22 -6
- data/Rakefile +7 -7
- data/example.rb +39 -11
- data/lib/arduino.rb +58 -22
- data/lib/arduino/event_machine.rb +4 -7
- data/lib/arduino/incoming_handler.rb +50 -0
- data/lib/arduino/outgoing_handler.rb +57 -0
- data/lib/arduino/status.rb +29 -4
- data/lib/gcode.rb +12 -2
- data/lib/gcode.yml +4 -4
- data/spec/fixtures/stub_serial_port.rb +2 -13
- data/spec/lib/arduino_spec.rb +26 -0
- data/spec/lib/gcode_spec.rb +41 -0
- data/spec/spec_helper.rb +1 -8
- metadata +9 -8
- data/lib/arduino/command_set.rb +0 -39
- data/lib/param.yml +0 -21
- data/spec/lib/ramps_arduino_values_received_spec.rb +0 -54
- data/testcommands.csv +0 -25
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 52d9fa1d6b06912cce708e6ef54f9b07035678d1
|
4
|
+
data.tar.gz: 665482bd2b934eea96f01a820b886f07df5c3170
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 75aefd6382c999e9af8d5417cfbb540122c2bcbe3808c522bef641731ba586ec9fbb4452a1cc42f1356a765b219318f37c8208fc5c31c74581b05070dcd08f73
|
7
|
+
data.tar.gz: 09c86b75932a49c9dbd486e15b906a3ce7982dd243bfcee092584014c6910e3fd8e59fcca3d1f1cac88fda5c8f128f615d0d6fe2d9f581f4c95e7682d774f590
|
data/README.md
CHANGED
@@ -9,22 +9,38 @@ gem install farmbot-serial, '0.0.5'
|
|
9
9
|
```
|
10
10
|
|
11
11
|
```ruby
|
12
|
+
require 'farmbot-serial'
|
13
|
+
|
12
14
|
bot = FB::Arduino.new # Defaults to '/dev/ttyACM0', can be configured.
|
13
15
|
|
14
16
|
EM.run do
|
15
17
|
FB::ArduinoEventMachine.connect(bot)
|
16
18
|
|
17
19
|
# Example 1: Writing to the serial line the "correct way" every 1.5 seconds.
|
18
|
-
|
19
|
-
|
20
|
+
EventMachine::PeriodicTimer.new(1.5) do
|
21
|
+
bot.commands.move_relative(x: 100, y: 50)
|
22
|
+
end
|
20
23
|
|
21
|
-
# Example 2: Writing raw
|
22
|
-
EventMachine::PeriodicTimer.new(2.5) { bot.write("F31 P8") }
|
24
|
+
# Example 2: Writing raw gcode object to serial every 2.5
|
25
|
+
EventMachine::PeriodicTimer.new(2.5) { bot.write FB::Gcode.new("F31 P8") }
|
23
26
|
|
24
27
|
# This will execute after status has been updated / internal code.
|
25
|
-
bot.onmessage { |gcode| puts "
|
28
|
+
bot.onmessage { |gcode| puts "Message just came in." }
|
26
29
|
|
27
30
|
# Try pulling the USB cable out to test this one.
|
28
|
-
bot.onclose { EM.stop }
|
31
|
+
bot.onclose { puts "bye!"; EM.stop }
|
29
32
|
end
|
33
|
+
|
30
34
|
```
|
35
|
+
|
36
|
+
# Upgrading to Ruby 2.2
|
37
|
+
|
38
|
+
This gem requires Ruby 2.2. As of this writing, a Pi is loaded with 1.9.3 by default.
|
39
|
+
|
40
|
+
To upgrade your ruby version, try this:
|
41
|
+
|
42
|
+
```
|
43
|
+
curl -L https://get.rvm.io | bash -s stable --ruby
|
44
|
+
```
|
45
|
+
|
46
|
+
This will take about 2 hours on a standard pi.
|
data/Rakefile
CHANGED
@@ -1,9 +1,9 @@
|
|
1
|
-
require 'rake/testtask'
|
1
|
+
# require 'rake/testtask'
|
2
2
|
|
3
|
-
Rake::TestTask.new do |t|
|
4
|
-
|
5
|
-
|
6
|
-
end
|
3
|
+
# Rake::TestTask.new do |t|
|
4
|
+
# t.libs << 'test'
|
5
|
+
# t.test_files = Dir.glob('test/**/*_test.rb')
|
6
|
+
# end
|
7
7
|
|
8
|
-
desc "Run tests"
|
9
|
-
task :default => :test
|
8
|
+
# desc "Run tests"
|
9
|
+
# task :default => :test
|
data/example.rb
CHANGED
@@ -3,19 +3,47 @@ require 'pry'
|
|
3
3
|
|
4
4
|
bot = FB::Arduino.new # Defaults to '/dev/ttyACM0', can be configured.
|
5
5
|
|
6
|
-
|
7
|
-
|
6
|
+
puts """
|
7
|
+
FARMBOT SERIAL SANDBOX. WELCOME!
|
8
|
+
================================"""
|
9
|
+
$commands = {
|
10
|
+
"q" => "bot.commands.emergency_stop",
|
11
|
+
"w" => "bot.commands.move_relative(x: 600)",
|
12
|
+
"s" => "bot.commands.move_relative(x: -600)",
|
13
|
+
"e" => "bot.commands.home_x",
|
14
|
+
"r" => "bot.commands.home_y",
|
15
|
+
"t" => "bot.commands.home_z",
|
16
|
+
"y" => "bot.commands.home_all",
|
17
|
+
"u" => "bot.commands.read_parameter(8)",
|
18
|
+
"i" => "bot.commands.write_parameter",
|
19
|
+
"p" => "bot.commands.read_status(8)",
|
20
|
+
}
|
21
|
+
|
22
|
+
$commands.each { |k, v| puts "#{k}: #{v}" }
|
8
23
|
|
9
|
-
|
10
|
-
|
11
|
-
EventMachine::PeriodicTimer.new(1.5) { bot.commands.move_relative(command) }
|
24
|
+
class KeyboardHandler < EM::Connection
|
25
|
+
include EM::Protocols::LineText2
|
12
26
|
|
13
|
-
|
14
|
-
EventMachine::PeriodicTimer.new(2.5) { bot.write("F31 P8") }
|
27
|
+
attr_reader :bot
|
15
28
|
|
16
|
-
|
17
|
-
|
29
|
+
def initialize(bot)
|
30
|
+
@bot = bot
|
31
|
+
end
|
18
32
|
|
19
|
-
|
20
|
-
|
33
|
+
def receive_line(data)
|
34
|
+
cmd = $commands[data] || ""
|
35
|
+
eval(cmd)
|
36
|
+
end
|
21
37
|
end
|
38
|
+
|
39
|
+
puts "Starting now."
|
40
|
+
|
41
|
+
EM.run do
|
42
|
+
FB::ArduinoEventMachine.connect(bot)
|
43
|
+
bot.onmessage { |gcode| print "#{gcode.name}; " }
|
44
|
+
bot.onchange { |diff| print "#{diff}; " }
|
45
|
+
bot.onclose { puts "bye!"; EM.stop } # Unplug the bot and see
|
46
|
+
# EventMachine::PeriodicTimer.new(2) { bot.serial_port.puts "G82" }
|
47
|
+
EM.open_keyboard(KeyboardHandler, bot)
|
48
|
+
end
|
49
|
+
|
data/lib/arduino.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
require 'serialport'
|
2
2
|
require_relative 'default_serial_port'
|
3
|
-
require_relative 'arduino/
|
3
|
+
require_relative 'arduino/incoming_handler'
|
4
|
+
require_relative 'arduino/outgoing_handler'
|
4
5
|
require_relative 'arduino/event_machine'
|
5
6
|
require_relative 'arduino/status'
|
6
7
|
# Communicate with the arduino using a serial interface
|
@@ -8,14 +9,23 @@ module FB
|
|
8
9
|
class Arduino
|
9
10
|
class EmergencyStop < StandardError; end # Not yet used.
|
10
11
|
|
11
|
-
attr_reader :serial_port, :logger, :commands, :
|
12
|
+
attr_reader :serial_port, :logger, :commands, :inbound_queue, :status,
|
13
|
+
:inputs, :outbound_queue
|
12
14
|
|
13
|
-
#
|
15
|
+
# Initialize and provide a serial object, as well as an IO object to send
|
14
16
|
# log messages to. Default SerialPort is DefaultSerialPort. Default logger
|
15
17
|
# is STDOUT
|
16
|
-
def initialize(serial_port
|
17
|
-
@
|
18
|
-
@
|
18
|
+
def initialize(serial_port: DefaultSerialPort.new, logger: STDOUT)
|
19
|
+
@outbound_queue = [] # Pi -> Arduino Gcode
|
20
|
+
@inbound_queue = EM::Channel.new # Pi <- Arduino
|
21
|
+
|
22
|
+
@serial_port = serial_port
|
23
|
+
@logger = logger
|
24
|
+
@commands = FB::OutgoingHandler.new(self)
|
25
|
+
@inputs = FB::IncomingHandler.new(self)
|
26
|
+
@status = FB::Status.new(self)
|
27
|
+
|
28
|
+
start_event_listeners
|
19
29
|
end
|
20
30
|
|
21
31
|
# Log to screen/file/IO stream
|
@@ -23,36 +33,62 @@ module FB
|
|
23
33
|
logger.puts(message)
|
24
34
|
end
|
25
35
|
|
26
|
-
#
|
27
|
-
|
28
|
-
|
29
|
-
|
36
|
+
# Send outgoing test to arduino from pi
|
37
|
+
def write(string)
|
38
|
+
@outbound_queue.unshift string
|
39
|
+
execute_command_next_tick
|
40
|
+
end
|
41
|
+
|
42
|
+
def onchange(&blk)
|
43
|
+
@onchange = blk
|
30
44
|
end
|
31
45
|
|
32
46
|
# Handle incoming text from arduino into pi
|
33
47
|
def onmessage(&blk)
|
34
|
-
|
35
|
-
@queue.subscribe do |gcodes|
|
36
|
-
gcodes.each do |gcode|
|
37
|
-
parse_incoming(gcode)
|
38
|
-
blk.call(gcode)
|
39
|
-
end
|
40
|
-
end
|
48
|
+
@onmessage = blk
|
41
49
|
end
|
42
50
|
|
43
51
|
def onclose(&blk)
|
44
52
|
@onclose = blk
|
45
53
|
end
|
46
54
|
|
47
|
-
# Send outgoing test to arduino from pi
|
48
|
-
def write(string)
|
49
|
-
serial_port.puts string
|
50
|
-
end
|
51
|
-
|
52
55
|
# Handle loss of serial connection
|
53
56
|
def disconnect
|
54
57
|
log "Connection to device lost"
|
55
58
|
@onclose.call if @onclose
|
56
59
|
end
|
60
|
+
|
61
|
+
private
|
62
|
+
|
63
|
+
# Highest priority message when processing incoming Gcode. Use for system
|
64
|
+
# level status changes.
|
65
|
+
def parse_incoming(gcode)
|
66
|
+
inputs.execute(gcode)
|
67
|
+
end
|
68
|
+
|
69
|
+
def execute_command_next_tick
|
70
|
+
EM.next_tick do
|
71
|
+
if status.ready?
|
72
|
+
diff = (Time.now - (@time || Time.now)).to_i
|
73
|
+
log "Sending queue after #{diff}s delay" if diff > 0
|
74
|
+
serial_port.puts @outbound_queue.pop
|
75
|
+
@time = nil
|
76
|
+
else
|
77
|
+
@time ||= Time.now
|
78
|
+
serial_port.puts "F31 P8"
|
79
|
+
execute_command_next_tick
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
def start_event_listeners
|
85
|
+
status.onchange { |diff| @onchange.call(diff) if @onchange }
|
86
|
+
inbound_queue.subscribe do |gcodes|
|
87
|
+
gcodes.each do |gcode|
|
88
|
+
parse_incoming(gcode)
|
89
|
+
@onmessage.call(gcode) if @onmessage
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
57
93
|
end
|
58
94
|
end
|
@@ -7,12 +7,9 @@ module FB
|
|
7
7
|
attr_accessor :arduino
|
8
8
|
end
|
9
9
|
|
10
|
-
def self.poll(interval, &blk)
|
11
|
-
EventMachine::PeriodicTimer.new(interval.to_f, &blk)
|
12
|
-
end
|
13
|
-
|
14
10
|
def initialize
|
15
|
-
@
|
11
|
+
@bot = self.class.arduino
|
12
|
+
@q, @buffer = @bot.inbound_queue, ''
|
16
13
|
end
|
17
14
|
|
18
15
|
# Gets called when data arrives.
|
@@ -23,7 +20,7 @@ module FB
|
|
23
20
|
send_buffer
|
24
21
|
clear_buffer
|
25
22
|
else
|
26
|
-
add_to_buffer(chunk)
|
23
|
+
add_to_buffer(chunk) # Keep RXing the buffer until chunk completes.
|
27
24
|
end
|
28
25
|
end
|
29
26
|
end
|
@@ -32,7 +29,7 @@ module FB
|
|
32
29
|
# splits the data on \r\n. Unlike Ruby's split() method, this method will
|
33
30
|
# preserve the \r\n.
|
34
31
|
def split_into_chunks(data)
|
35
|
-
data.gsub("\r\n", '
|
32
|
+
data.gsub("\r\n", '\b\a').split('\a').map{ |d| d.gsub('\b', "\r\n") }
|
36
33
|
end
|
37
34
|
|
38
35
|
def clear_buffer
|
@@ -0,0 +1,50 @@
|
|
1
|
+
module FB
|
2
|
+
# Handles Gcode that moves from the Arduino to the Pi (Arduino -> Pi).
|
3
|
+
class IncomingHandler
|
4
|
+
attr_reader :bot
|
5
|
+
|
6
|
+
class UnhandledGcode < StandardError; end
|
7
|
+
|
8
|
+
def initialize(bot)
|
9
|
+
@bot = bot
|
10
|
+
end
|
11
|
+
|
12
|
+
def execute(gcode)
|
13
|
+
self.send(gcode.name, gcode)
|
14
|
+
rescue NoMethodError
|
15
|
+
bot.log "#{gcode.name} is a valid GCode, but no input handler method exists"
|
16
|
+
end
|
17
|
+
|
18
|
+
def unknown(gcode)
|
19
|
+
bot.log "Don't know how to parse incoming GCode: #{gcode}"
|
20
|
+
end
|
21
|
+
|
22
|
+
def received(gcode)
|
23
|
+
bot.status[:busy] = 1
|
24
|
+
end
|
25
|
+
|
26
|
+
def reporting_end_stops(gcode)
|
27
|
+
bot.status.gcode_update(gcode)
|
28
|
+
end
|
29
|
+
|
30
|
+
def report_current_position(gcode)
|
31
|
+
bot.status.gcode_update(gcode)
|
32
|
+
end
|
33
|
+
|
34
|
+
def done(gcode)
|
35
|
+
bot.status[:busy] = 0
|
36
|
+
end
|
37
|
+
|
38
|
+
def busy(gcode)
|
39
|
+
bot.status[:busy] = 1
|
40
|
+
end
|
41
|
+
|
42
|
+
def report_status_value(gcode)
|
43
|
+
bot.status.gcode_update(gcode)
|
44
|
+
end
|
45
|
+
|
46
|
+
def report_software_version(gcode)
|
47
|
+
nil # Don't need the info right now.
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
module FB
|
2
|
+
# Responsible for writing to the serial line. Sends Gcode from the pi to the
|
3
|
+
# arduino. (Pi -> Arduino)
|
4
|
+
class OutgoingHandler
|
5
|
+
attr_reader :bot
|
6
|
+
|
7
|
+
class UnhandledGcode < StandardError; end
|
8
|
+
|
9
|
+
def initialize(bot)
|
10
|
+
@bot = bot
|
11
|
+
end
|
12
|
+
|
13
|
+
def emergency_stop(*)
|
14
|
+
bot.outbound_queue = [] # Dump pending commands.
|
15
|
+
bot.serial_port.puts "E" # Don't queue this one- write to serial line.
|
16
|
+
end
|
17
|
+
|
18
|
+
def move_relative(x: 0, y: 0, z: 0, s: 100)
|
19
|
+
write "G00 X#{x} Y#{y} Z#{z}"
|
20
|
+
end
|
21
|
+
|
22
|
+
def home_x
|
23
|
+
write "F11"
|
24
|
+
end
|
25
|
+
|
26
|
+
def home_y
|
27
|
+
write "F12"
|
28
|
+
end
|
29
|
+
|
30
|
+
def home_z
|
31
|
+
write "F13"
|
32
|
+
end
|
33
|
+
|
34
|
+
def home_all
|
35
|
+
write "G28"
|
36
|
+
end
|
37
|
+
|
38
|
+
def read_parameter(num)
|
39
|
+
write "F21 P#{num}"
|
40
|
+
end
|
41
|
+
|
42
|
+
def write_parameter(num, val)
|
43
|
+
write "F22 P#{num} V#{val}"
|
44
|
+
end
|
45
|
+
|
46
|
+
def read_status(pin)
|
47
|
+
write "F31 P#{pin}"
|
48
|
+
end
|
49
|
+
|
50
|
+
private
|
51
|
+
|
52
|
+
def write(str)
|
53
|
+
bot.write(FB::Gcode.new(str))
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
data/lib/arduino/status.rb
CHANGED
@@ -3,23 +3,48 @@ module FB
|
|
3
3
|
# Map of informational status and default values for status within Arduino.
|
4
4
|
DEFAULT_INFO = {X: 0, Y: 0, Z: 0, S: 10, Q: 0, T: 0, C: '', P: 0, V: 0,
|
5
5
|
W: 0, L: 0, E: 0, M: 0, XA: 0, XB: 0, YA: 0, YB: 0, ZA: 0,
|
6
|
-
ZB: 0,
|
7
|
-
# Put it into a struct.
|
6
|
+
ZB: 0,YR: 0, R: 0, BUSY: 0}
|
8
7
|
Info = Struct.new(*DEFAULT_INFO.keys)
|
9
8
|
|
10
9
|
attr_reader :bot
|
11
10
|
|
12
11
|
def initialize(bot)
|
12
|
+
@changes = EM::Channel.new
|
13
13
|
@bot, @info = bot, Info.new(*DEFAULT_INFO.values)
|
14
14
|
end
|
15
15
|
|
16
|
+
def transaction(&blk)
|
17
|
+
old = @info.to_h
|
18
|
+
yield
|
19
|
+
emit_updates(old)
|
20
|
+
end
|
21
|
+
|
16
22
|
def []=(register, value)
|
17
|
-
|
18
|
-
@info[value.upcase.to_sym] = value
|
23
|
+
transaction { @info[register.upcase.to_sym] = value }
|
19
24
|
end
|
20
25
|
|
21
26
|
def [](value)
|
22
27
|
@info[value.upcase.to_sym]
|
23
28
|
end
|
29
|
+
|
30
|
+
def gcode_update(gcode)
|
31
|
+
transaction do
|
32
|
+
gcode.params.each { |p| @info.send("#{p.head}=", p.tail) }
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def onchange
|
37
|
+
@changes.subscribe { |diff| yield(diff) }
|
38
|
+
end
|
39
|
+
|
40
|
+
def ready?
|
41
|
+
self[:BUSY] == 0
|
42
|
+
end
|
43
|
+
|
44
|
+
def emit_updates(old)
|
45
|
+
# calculate a diff between the old status and new status
|
46
|
+
diff = (@info.to_h.to_a - old.to_a).to_h
|
47
|
+
@changes << diff unless diff.empty?
|
48
|
+
end
|
24
49
|
end
|
25
50
|
end
|
data/lib/gcode.rb
CHANGED
@@ -8,7 +8,7 @@ module FB
|
|
8
8
|
def initialize(str)
|
9
9
|
@str = str
|
10
10
|
@params = str.split(' ').map{|line| GcodeToken.new(line)}
|
11
|
-
@cmd = @params.shift
|
11
|
+
@cmd = @params.shift || 'NULL'
|
12
12
|
end
|
13
13
|
|
14
14
|
# Turns a string of many gcodes into an array of many gcodes. Used to parse
|
@@ -22,6 +22,10 @@ module FB
|
|
22
22
|
GCODE_DICTIONARY[cmd.to_sym] || :unknown
|
23
23
|
end
|
24
24
|
|
25
|
+
def to_s
|
26
|
+
[@cmd, *@params].map(&:to_s).join(" ")
|
27
|
+
end
|
28
|
+
|
25
29
|
# A head/tail pair of a single node of GCode. Ex: R01 = [:R, '01']
|
26
30
|
class GcodeToken
|
27
31
|
attr_reader :head, :tail, :name
|
@@ -29,10 +33,16 @@ module FB
|
|
29
33
|
def initialize(str)
|
30
34
|
nodes = str.scan(/\d+|\D+/) # ["R", "01"]
|
31
35
|
@head, @tail = nodes.shift.to_sym, nodes.join(" ")
|
36
|
+
# Coerce to ints if possible, since serial line is all string types.
|
37
|
+
@tail = @tail.to_i if @tail.match(/^\d+$/)
|
32
38
|
end
|
33
39
|
|
34
40
|
def to_sym
|
35
|
-
|
41
|
+
to_s.to_sym
|
42
|
+
end
|
43
|
+
|
44
|
+
def to_s
|
45
|
+
"#{head}#{tail}"
|
36
46
|
end
|
37
47
|
end
|
38
48
|
end
|
data/lib/gcode.yml
CHANGED
@@ -26,10 +26,10 @@
|
|
26
26
|
:F82: :report_current_position
|
27
27
|
:F83: :report_software_version
|
28
28
|
:E: :emergency_stop
|
29
|
-
:
|
30
|
-
:
|
31
|
-
:
|
32
|
-
:
|
29
|
+
:R1: :received
|
30
|
+
:R2: :done
|
31
|
+
:R3: :error
|
32
|
+
:R4: :busy
|
33
33
|
:R21: :report_parameter_value
|
34
34
|
:R31: :report_status_value
|
35
35
|
:R41: :report_pin_value
|
@@ -1,17 +1,6 @@
|
|
1
1
|
## SERIAL PORT SIMULATION
|
2
2
|
## **********************
|
3
|
-
|
4
|
-
|
5
|
-
class StubSerialPort # TODO: Inherit from StringIO?
|
6
|
-
def initialize(comm_port, parameters)
|
7
|
-
end
|
8
|
-
|
9
|
-
def write(text)
|
10
|
-
text
|
11
|
-
end
|
12
|
-
|
13
|
-
def read(characters)
|
14
|
-
characters
|
15
|
-
end
|
3
|
+
class StubSerialPort < StringIO
|
4
|
+
def initialize(comm_port, parameters)
|
16
5
|
end
|
17
6
|
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe FB::Arduino do
|
4
|
+
let(:logger) { StringIO.new("") }
|
5
|
+
let(:bot) do
|
6
|
+
FB::Arduino.new(StubSerialPort.new(0, 0), logger)
|
7
|
+
end
|
8
|
+
|
9
|
+
it "initializes" do
|
10
|
+
expect(bot).to be_kind_of(FB::Arduino)
|
11
|
+
expect(bot.serial_port).to be_kind_of(StubSerialPort)
|
12
|
+
expect(bot.logger).to be_kind_of(StringIO)
|
13
|
+
expect(bot.commands).to be_kind_of(FB::OutgoingHandler)
|
14
|
+
expect(bot.queue).to be_kind_of(EM::Channel)
|
15
|
+
expect(bot.status).to be_kind_of(FB::Status)
|
16
|
+
expect(bot.inputs).to be_kind_of(FB::IncomingHandler)
|
17
|
+
end
|
18
|
+
|
19
|
+
it 'prints to the logger object' do
|
20
|
+
bot.log "Hello, World!"
|
21
|
+
bot.logger.rewind
|
22
|
+
expect(bot.logger.gets.chomp).to eq("Hello, World!")
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
|
@@ -0,0 +1,41 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
describe FB::Gcode do
|
3
|
+
let(:gcode) { FB::Gcode.new("F31 P8 ")}
|
4
|
+
|
5
|
+
it("initializes from string") { expect(gcode).to be_kind_of(FB::Gcode) }
|
6
|
+
|
7
|
+
it("infers Gcode name") { expect(gcode.name).to eq(:read_status) }
|
8
|
+
|
9
|
+
it "returns :unknown for bad Gcode tokens" do
|
10
|
+
unknown = FB::Gcode.new("QQQ31 F32 ").name
|
11
|
+
expect(unknown).to eq(:unknown)
|
12
|
+
end
|
13
|
+
|
14
|
+
it("sets the original input string") { expect(gcode.str).to eq("F31 P8 ") }
|
15
|
+
|
16
|
+
it("sets @cmd using the first Gcode node") do
|
17
|
+
expect(gcode.cmd).to be_kind_of(FB::Gcode::GcodeToken)
|
18
|
+
expect(gcode.cmd.head).to eq(:F)
|
19
|
+
expect(gcode.cmd.tail).to eq(31)
|
20
|
+
end
|
21
|
+
|
22
|
+
it("sets @params using the last Gcode node(s)") do
|
23
|
+
expect(gcode.params).to be_kind_of(Array)
|
24
|
+
expect(gcode.params[0]).to be_kind_of(FB::Gcode::GcodeToken)
|
25
|
+
expect(gcode.params[0].head).to eq(:P)
|
26
|
+
expect(gcode.params[0].tail).to eq(8)
|
27
|
+
end
|
28
|
+
|
29
|
+
it("serializes back to string via #to_s") do
|
30
|
+
expect(gcode.to_s).to eq("F31 P8")
|
31
|
+
end
|
32
|
+
|
33
|
+
it "parses multiple Gcodes in a single string via parse_lines" do
|
34
|
+
codes = FB::Gcode.parse_lines("A12 B34\nC56 D78\r\n")
|
35
|
+
expect(codes.count).to eq(2)
|
36
|
+
expect(codes.first).to be_kind_of(FB::Gcode)
|
37
|
+
expect(codes.last.cmd.head).to eq(:C)
|
38
|
+
expect(codes.first.params.first.tail).to eq(34)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
data/spec/spec_helper.rb
CHANGED
@@ -5,13 +5,6 @@ SimpleCov.start do
|
|
5
5
|
end
|
6
6
|
require 'pry'
|
7
7
|
require 'farmbot-serial'
|
8
|
-
|
8
|
+
require_relative 'fixtures/stub_serial_port'
|
9
9
|
RSpec.configure do |config|
|
10
|
-
config.expect_with :rspec do |expectations|
|
11
|
-
expectations.include_chain_clauses_in_custom_matcher_descriptions = true
|
12
|
-
end
|
13
|
-
|
14
|
-
config.mock_with :rspec do |mocks|
|
15
|
-
mocks.verify_partial_doubles = true
|
16
|
-
end
|
17
10
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: farmbot-serial
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.7
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Tim Evers
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2015-
|
12
|
+
date: 2015-04-01 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: bundler
|
@@ -122,18 +122,18 @@ files:
|
|
122
122
|
- Rakefile
|
123
123
|
- example.rb
|
124
124
|
- lib/arduino.rb
|
125
|
-
- lib/arduino/command_set.rb
|
126
125
|
- lib/arduino/event_machine.rb
|
126
|
+
- lib/arduino/incoming_handler.rb
|
127
|
+
- lib/arduino/outgoing_handler.rb
|
127
128
|
- lib/arduino/status.rb
|
128
129
|
- lib/default_serial_port.rb
|
129
130
|
- lib/farmbot-serial.rb
|
130
131
|
- lib/gcode.rb
|
131
132
|
- lib/gcode.yml
|
132
|
-
- lib/param.yml
|
133
133
|
- spec/fixtures/stub_serial_port.rb
|
134
|
-
- spec/lib/
|
134
|
+
- spec/lib/arduino_spec.rb
|
135
|
+
- spec/lib/gcode_spec.rb
|
135
136
|
- spec/spec_helper.rb
|
136
|
-
- testcommands.csv
|
137
137
|
homepage: http://github.com/farmbot/farmbot-serial
|
138
138
|
licenses:
|
139
139
|
- MIT
|
@@ -146,7 +146,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
146
146
|
requirements:
|
147
147
|
- - ">="
|
148
148
|
- !ruby/object:Gem::Version
|
149
|
-
version:
|
149
|
+
version: 2.2.0
|
150
150
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
151
151
|
requirements:
|
152
152
|
- - ">="
|
@@ -160,5 +160,6 @@ specification_version: 4
|
|
160
160
|
summary: Serial library for Farmbot
|
161
161
|
test_files:
|
162
162
|
- spec/fixtures/stub_serial_port.rb
|
163
|
-
- spec/lib/
|
163
|
+
- spec/lib/arduino_spec.rb
|
164
|
+
- spec/lib/gcode_spec.rb
|
164
165
|
- spec/spec_helper.rb
|
data/lib/arduino/command_set.rb
DELETED
@@ -1,39 +0,0 @@
|
|
1
|
-
module FB
|
2
|
-
# Composes all logic related to controlling a bot into a single object.
|
3
|
-
# Responsible for writing to the serial line.
|
4
|
-
class ArduinoCommandSet
|
5
|
-
attr_reader :bot
|
6
|
-
|
7
|
-
def initialize(bot)
|
8
|
-
@bot = bot
|
9
|
-
end
|
10
|
-
|
11
|
-
def execute(gcode)
|
12
|
-
puts "SERIAL OUT: #{gcode.name}"
|
13
|
-
self.send(gcode.name, gcode)
|
14
|
-
end
|
15
|
-
|
16
|
-
def emergency_stop(*)
|
17
|
-
bot.write("E")
|
18
|
-
end
|
19
|
-
|
20
|
-
def move_relative(gcode)
|
21
|
-
end
|
22
|
-
|
23
|
-
def received(gcode)
|
24
|
-
end
|
25
|
-
|
26
|
-
def reporting_end_stops(gcode)
|
27
|
-
end
|
28
|
-
|
29
|
-
def report_current_position(gcode)
|
30
|
-
end
|
31
|
-
|
32
|
-
def done(gcode)
|
33
|
-
end
|
34
|
-
|
35
|
-
def report_status_value(gcode)
|
36
|
-
end
|
37
|
-
end
|
38
|
-
end
|
39
|
-
|
data/lib/param.yml
DELETED
@@ -1,21 +0,0 @@
|
|
1
|
-
# ---
|
2
|
-
# X: :x_movement
|
3
|
-
# Y: :y_movement
|
4
|
-
# Z: :z_movement
|
5
|
-
# S: :speed
|
6
|
-
# Q: :quanity
|
7
|
-
# T: :time
|
8
|
-
# C: :comment
|
9
|
-
# P: :parameter
|
10
|
-
# V: :value_number
|
11
|
-
# W: :secondary_value
|
12
|
-
# L: :number
|
13
|
-
# E: :element
|
14
|
-
# M: :pin_mode
|
15
|
-
# M: :read_write_mode
|
16
|
-
# XA: :end_stop_1_x_axis
|
17
|
-
# XB: :end_stop_2_x_axis
|
18
|
-
# YA: :end_stop_1_y_axis
|
19
|
-
# YB: :end_stop_2_y_axis
|
20
|
-
# ZA: :end_stop_1_z_axis
|
21
|
-
# ZB: :end_stop_2_z_axis
|
@@ -1,54 +0,0 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
describe FB::HardwareInterfaceArduinoValuesReceived do
|
3
|
-
|
4
|
-
before do
|
5
|
-
@ramps = FB::HardwareInterfaceArduinoValuesReceived.new
|
6
|
-
end
|
7
|
-
|
8
|
-
it "load parameters" do
|
9
|
-
|
10
|
-
p = rand(9999999).to_i
|
11
|
-
v = rand(9999999).to_i
|
12
|
-
|
13
|
-
x = rand(9999999).to_i
|
14
|
-
y = rand(9999999).to_i
|
15
|
-
z = rand(9999999).to_i
|
16
|
-
|
17
|
-
xa = rand(9999999).to_i
|
18
|
-
xb = rand(9999999).to_i
|
19
|
-
ya = rand(9999999).to_i
|
20
|
-
yb = rand(9999999).to_i
|
21
|
-
za = rand(9999999).to_i
|
22
|
-
zb = rand(9999999).to_i
|
23
|
-
|
24
|
-
@ramps.load_parameter("P", p)
|
25
|
-
@ramps.load_parameter("V", v)
|
26
|
-
@ramps.load_parameter("X", x)
|
27
|
-
@ramps.load_parameter("Y", y)
|
28
|
-
@ramps.load_parameter("Z", z)
|
29
|
-
|
30
|
-
@ramps.load_parameter("XA", xa)
|
31
|
-
@ramps.load_parameter("XB", xb)
|
32
|
-
@ramps.load_parameter("YA", ya)
|
33
|
-
@ramps.load_parameter("YB", yb)
|
34
|
-
@ramps.load_parameter("ZA", za)
|
35
|
-
@ramps.load_parameter("ZB", zb)
|
36
|
-
|
37
|
-
expect(@ramps.p).to eq(p)
|
38
|
-
expect(@ramps.v).to eq(v)
|
39
|
-
|
40
|
-
expect(@ramps.x).to eq(x)
|
41
|
-
expect(@ramps.y).to eq(y)
|
42
|
-
expect(@ramps.z).to eq(z)
|
43
|
-
|
44
|
-
expect(@ramps.xa).to eq(xa)
|
45
|
-
expect(@ramps.xb).to eq(xb)
|
46
|
-
expect(@ramps.ya).to eq(ya)
|
47
|
-
expect(@ramps.yb).to eq(yb)
|
48
|
-
expect(@ramps.za).to eq(za)
|
49
|
-
expect(@ramps.zb).to eq(zb)
|
50
|
-
|
51
|
-
end
|
52
|
-
|
53
|
-
end
|
54
|
-
|
data/testcommands.csv
DELETED
@@ -1,25 +0,0 @@
|
|
1
|
-
HOME Y,0,0,0
|
2
|
-
HOME X,0,0,0
|
3
|
-
MOVE ABSOLUTE, 0, 30, 0, 0
|
4
|
-
|
5
|
-
MOVE ABSOLUTE, 30, 30, 0, 0
|
6
|
-
DOSE WATER, 0, 0, 0, 5
|
7
|
-
MOVE ABSOLUTE, 60, 30, 0, 0
|
8
|
-
DOSE WATER, 0, 0, 0, 5
|
9
|
-
MOVE ABSOLUTE, 90, 30, 0, 0
|
10
|
-
DOSE WATER, 0, 0, 0, 5
|
11
|
-
MOVE ABSOLUTE,120, 30, 0, 0
|
12
|
-
DOSE WATER, 0, 0, 0, 5
|
13
|
-
|
14
|
-
MOVE ABSOLUTE,120, 60, 0, 0
|
15
|
-
DOSE WATER, 0, 0, 0, 5
|
16
|
-
MOVE ABSOLUTE, 90, 60, 0, 0
|
17
|
-
DOSE WATER, 0, 0, 0, 5
|
18
|
-
MOVE ABSOLUTE, 60, 60, 0, 0
|
19
|
-
DOSE WATER, 0, 0, 0, 5
|
20
|
-
MOVE ABSOLUTE, 30, 60, 0, 0
|
21
|
-
DOSE WATER, 0, 0, 0, 5
|
22
|
-
|
23
|
-
|
24
|
-
HOME Y,0,0,0
|
25
|
-
HOME X,0,0,0
|