farmbot-serial 0.6.2 → 0.7.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.
- checksums.yaml +4 -4
- data/lib/arduino/incoming_handler.rb +9 -5
- data/lib/arduino/outgoing_handler.rb +53 -1
- data/lib/arduino/status.rb +19 -17
- data/lib/gcode.rb +4 -1
- data/lib/gcode.yml +2 -0
- data/lib/parameters.yml +25 -0
- data/serial-console.rb +45 -0
- data/spec/lib/arduino/incoming_handler_spec.rb +6 -4
- data/spec/lib/arduino/outgoing_handler_spec.rb +75 -0
- data/spec/lib/arduino/status_spec.rb +1 -1
- data/spec/lib/gcode_spec.rb +0 -5
- data/spec/spec_helper.rb +0 -16
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: caf76751695f80320148ccda60a2b7f5f90d99ce
|
4
|
+
data.tar.gz: df0cb1eed0147a30522799aa64f1ddedb599b773
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 18dffa6b60f61e21f179f770e61d92b75df8a249c7f35afc324a24341571b0b3f82bfcf369579e459315ae88117a71465ca7a8c305cc48701dc76d59ed4c943b
|
7
|
+
data.tar.gz: b77c02b6530fe4aa310149d8ce66591c353165708dbe85ebe194df89ee0e2dc7e8aae70a8c34cc5f39d2b0a93761a4600f37db3aa911f7072930bcf42af102e5
|
@@ -8,17 +8,21 @@ module FB
|
|
8
8
|
end
|
9
9
|
|
10
10
|
def execute(gcode)
|
11
|
-
|
12
|
-
|
13
|
-
|
11
|
+
name = gcode.name
|
12
|
+
if respond_to?(name)
|
13
|
+
self.send(name, gcode)
|
14
|
+
else
|
15
|
+
bot.log "#{gcode.name} is a valid GCode, but no input handler method exists"
|
16
|
+
end
|
14
17
|
end
|
15
18
|
|
16
19
|
def unknown(gcode)
|
17
20
|
bot.log "Don't know how to parse incoming GCode: #{gcode}"
|
18
21
|
end
|
19
22
|
|
23
|
+
# Called when the Ardunio is reporting the status of a parameter.
|
20
24
|
def report_parameter_value(gcode)
|
21
|
-
bot.status.
|
25
|
+
bot.status.set(gcode.value_of(:P), gcode.value_of(:V))
|
22
26
|
end
|
23
27
|
|
24
28
|
def reporting_end_stops(gcode)
|
@@ -31,7 +35,7 @@ module FB
|
|
31
35
|
|
32
36
|
def report_status_value(gcode)
|
33
37
|
# TODO: Verfiy the accuracy of this code. CC: @timevww
|
34
|
-
bot.status.
|
38
|
+
bot.status.set(gcode.value_of(:P), gcode.value_of(:V))
|
35
39
|
end
|
36
40
|
|
37
41
|
def received(gcode)
|
@@ -65,13 +65,65 @@ module FB
|
|
65
65
|
|
66
66
|
def pin_write(pin:, value:, mode:)
|
67
67
|
write { "F41 P#{pin} V#{value} M#{mode}" }
|
68
|
-
bot.status.
|
68
|
+
bot.status.set(pin, value)
|
69
|
+
end
|
70
|
+
|
71
|
+
def set_max_speed(axis, value)
|
72
|
+
set_paramater_value(axis, value, 71, 72, 73)
|
73
|
+
end
|
74
|
+
|
75
|
+
def set_acceleration(axis, value)
|
76
|
+
set_paramater_value(axis, value, 41, 42, 43)
|
77
|
+
end
|
78
|
+
|
79
|
+
def set_timeout(axis, value)
|
80
|
+
set_paramater_value(axis, value, 11, 12, 13)
|
81
|
+
end
|
82
|
+
|
83
|
+
def set_steps_per_mm(axis, value)
|
84
|
+
raise "The Farmbot Arduino does not currently store a value for steps "\
|
85
|
+
"per mm. Keep track of this information at the application level"
|
86
|
+
end
|
87
|
+
|
88
|
+
def set_end_inversion(axis, value)
|
89
|
+
set_paramater_value(axis, bool_to_int(value), 21, 22, 23)
|
90
|
+
end
|
91
|
+
|
92
|
+
def set_motor_inversion(axis, value)
|
93
|
+
set_paramater_value(axis, bool_to_int(value), 31, 32, 33)
|
94
|
+
end
|
95
|
+
|
96
|
+
def set_negative_coordinates(axis, value)
|
97
|
+
raise "Not yet implemented. TODO: This method."
|
69
98
|
end
|
70
99
|
|
71
100
|
private
|
72
101
|
|
102
|
+
class InvalidAxisEntry < Exception; end
|
103
|
+
class BadBooleanValue < Exception; end
|
104
|
+
|
105
|
+
# The Arduino uses a different parameter number for each axis. Ex:
|
106
|
+
# MOVEMENT_TIMEOUT_X is 11 and MOVEMENT_TIMEOUT_Y is 12. To keep things dry,
|
107
|
+
# this method will lookup the correct paramater numer based on the axis
|
108
|
+
# provided and the three options available (if_x, if_y, if_z)
|
109
|
+
def set_paramater_value(axis, value, if_x, if_y, if_z)
|
110
|
+
param_num = { 'x' => if_x, 'y' => if_y, 'z' => if_z }[axis.to_s.downcase]
|
111
|
+
raise InvalidAxisEntry, "You entered an invalid axis" unless param_num
|
112
|
+
write { "F22 P#{param_num} V#{value.to_s}" }
|
113
|
+
end
|
114
|
+
|
73
115
|
def write(&blk)
|
74
116
|
bot.write(FB::Gcode.new(&blk))
|
75
117
|
end
|
118
|
+
|
119
|
+
def bool_to_int(value)
|
120
|
+
case value
|
121
|
+
when true, 'true', 1, '1' then 1
|
122
|
+
when false, 'false', 0, '0' then 0
|
123
|
+
else
|
124
|
+
raise BadBooleanValue, "Farmbot expected a boolean value in one of "\
|
125
|
+
"the following forms: [true, false, 1, 0]"
|
126
|
+
end
|
127
|
+
end
|
76
128
|
end
|
77
129
|
end
|
data/lib/arduino/status.rb
CHANGED
@@ -1,12 +1,11 @@
|
|
1
1
|
module FB
|
2
2
|
class Status
|
3
3
|
# Map of informational status and default values for status within Arduino.
|
4
|
-
DEFAULT_INFO = {X: 0, Y: 0, Z: 0, S: 10, BUSY: 1, LAST: 'none'
|
5
|
-
Info = Struct.new(*DEFAULT_INFO.keys)
|
4
|
+
DEFAULT_INFO = {X: 0, Y: 0, Z: 0, S: 10, BUSY: 1, LAST: 'none'}
|
6
5
|
|
7
6
|
def initialize
|
8
7
|
@changes = EM::Channel.new
|
9
|
-
@info =
|
8
|
+
@info = OpenStruct.new(DEFAULT_INFO)
|
10
9
|
end
|
11
10
|
|
12
11
|
def transaction(&blk)
|
@@ -18,14 +17,11 @@ module FB
|
|
18
17
|
end
|
19
18
|
|
20
19
|
def []=(register, value)
|
21
|
-
transaction
|
22
|
-
register = register.upcase.to_sym
|
23
|
-
@info[register] = value if @info.members.include?(register)
|
24
|
-
end
|
20
|
+
transaction { @info[register.to_s.upcase] = value }
|
25
21
|
end
|
26
22
|
|
27
23
|
def [](value)
|
28
|
-
@info[value.upcase.
|
24
|
+
@info[value.upcase.to_s]
|
29
25
|
end
|
30
26
|
|
31
27
|
def to_h
|
@@ -34,10 +30,7 @@ module FB
|
|
34
30
|
|
35
31
|
def gcode_update(gcode)
|
36
32
|
transaction do
|
37
|
-
gcode.params.each
|
38
|
-
setter = "#{p.head}="
|
39
|
-
@info.send(setter, p.tail) if @info.respond_to?(setter)
|
40
|
-
end
|
33
|
+
gcode.params.each { |p| @info[p.head] = p.tail }
|
41
34
|
end
|
42
35
|
end
|
43
36
|
|
@@ -49,13 +42,22 @@ module FB
|
|
49
42
|
self[:BUSY] == 0
|
50
43
|
end
|
51
44
|
|
52
|
-
def
|
53
|
-
@info[
|
45
|
+
def get(val)
|
46
|
+
@info[val.to_s.upcase] || :unknown
|
47
|
+
end
|
48
|
+
|
49
|
+
def set(key, val)
|
50
|
+
transaction do |info|
|
51
|
+
info[Gcode::PARAMETER_DICTIONARY.fetch(key, key.to_s)] = val
|
52
|
+
end
|
54
53
|
end
|
55
54
|
|
56
|
-
def
|
57
|
-
|
58
|
-
|
55
|
+
def pin(num)
|
56
|
+
case get(num)
|
57
|
+
when false, 0, :off then :off
|
58
|
+
when true, 1, :on then :on
|
59
|
+
else; :unknown
|
60
|
+
end
|
59
61
|
end
|
60
62
|
end
|
61
63
|
end
|
data/lib/gcode.rb
CHANGED
@@ -1,7 +1,10 @@
|
|
1
1
|
require 'yaml'
|
2
2
|
module FB
|
3
3
|
class Gcode
|
4
|
-
GCODE_DICTIONARY
|
4
|
+
GCODE_DICTIONARY = YAML.load_file(File.join(File.dirname(__FILE__),
|
5
|
+
'gcode.yml'))
|
6
|
+
PARAMETER_DICTIONARY = YAML.load_file(File.join(File.dirname(__FILE__),
|
7
|
+
'parameters.yml'))
|
5
8
|
|
6
9
|
attr_accessor :cmd, :params, :block
|
7
10
|
|
data/lib/gcode.yml
CHANGED
data/lib/parameters.yml
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
# Internally, the arduino uses a number code for each status. Those statuses are
|
2
|
+
# defined here.
|
3
|
+
---
|
4
|
+
0: :PARAM_VERSION
|
5
|
+
11: :MOVEMENT_TIMEOUT_X
|
6
|
+
12: :MOVEMENT_TIMEOUT_Y
|
7
|
+
13: :MOVEMENT_TIMEOUT_Z
|
8
|
+
21: :MOVEMENT_INVERT_ENDPOINTS_X
|
9
|
+
22: :MOVEMENT_INVERT_ENDPOINTS_Y
|
10
|
+
23: :MOVEMENT_INVERT_ENDPOINTS_Z
|
11
|
+
31: :MOVEMENT_INVERT_MOTOR_X
|
12
|
+
32: :MOVEMENT_INVERT_MOTOR_Y
|
13
|
+
33: :MOVEMENT_INVERT_MOTOR_Z
|
14
|
+
41: :MOVEMENT_STEPS_ACC_DEC_X
|
15
|
+
42: :MOVEMENT_STEPS_ACC_DEC_Y
|
16
|
+
43: :MOVEMENT_STEPS_ACC_DEC_Z
|
17
|
+
51: :MOVEMENT_HOME_UP_X
|
18
|
+
52: :MOVEMENT_HOME_UP_Y
|
19
|
+
53: :MOVEMENT_HOME_UP_Z
|
20
|
+
61: :MOVEMENT_MIN_SPD_X
|
21
|
+
62: :MOVEMENT_MIN_SPD_Y
|
22
|
+
63: :MOVEMENT_MIN_SPD_Z
|
23
|
+
71: :MOVEMENT_MAX_SPD_X
|
24
|
+
72: :MOVEMENT_MAX_SPD_Y
|
25
|
+
73: :MOVEMENT_MAX_SPD_Z
|
data/serial-console.rb
ADDED
@@ -0,0 +1,45 @@
|
|
1
|
+
require_relative 'lib/farmbot-serial'
|
2
|
+
require 'pry'
|
3
|
+
|
4
|
+
connection = FB::DefaultSerialPort.new('/dev/ttyUSB0')
|
5
|
+
|
6
|
+
puts """
|
7
|
+
THIS IS A SERIAL TERMINAL.
|
8
|
+
|
9
|
+
"""
|
10
|
+
|
11
|
+
print "> "
|
12
|
+
|
13
|
+
class KeyboardHandler < EM::Connection
|
14
|
+
include EM::Protocols::LineText2
|
15
|
+
|
16
|
+
attr_reader :connection
|
17
|
+
|
18
|
+
def initialize(connection)
|
19
|
+
@connection = connection
|
20
|
+
end
|
21
|
+
|
22
|
+
def receive_line(data)
|
23
|
+
connection.puts(data.chomp + "\r\n")
|
24
|
+
puts("Computer: #{data}")
|
25
|
+
end
|
26
|
+
|
27
|
+
def unbind
|
28
|
+
EM.stop
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
class SerialHandler < EventMachine::Connection
|
33
|
+
def receive_data(data)
|
34
|
+
puts("Arduino: #{data}")
|
35
|
+
end
|
36
|
+
|
37
|
+
def unbind
|
38
|
+
EM.stop
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
EM.run do
|
43
|
+
EM.open_keyboard(KeyboardHandler, connection)
|
44
|
+
EM.attach(connection,)
|
45
|
+
end
|
@@ -5,16 +5,18 @@ describe FB::IncomingHandler do
|
|
5
5
|
let(:bot) { FakeArduino.new }
|
6
6
|
let(:handler) { FB::IncomingHandler.new(bot) }
|
7
7
|
|
8
|
+
it 'gets calibration data' do
|
9
|
+
gcode = FB::Gcode.new { "R21 P71 V7654" }
|
10
|
+
handler.execute(gcode)
|
11
|
+
expect(bot.status.to_h[:MOVEMENT_MAX_SPD_X]).to eq(7654)
|
12
|
+
end
|
13
|
+
|
8
14
|
it 'handles unknowns' do
|
9
15
|
handler.execute(FakeGcode.new('hello', 'abc'))
|
10
16
|
expectation = "hello is a valid GCode, but no input handler method exists"
|
11
17
|
expect(bot.logger.message).to eq(expectation)
|
12
18
|
end
|
13
19
|
|
14
|
-
# def report_parameter_value(gcode)
|
15
|
-
# bot.status.set_pin(gcode.value_of(:P), gcode.value_of(:V))
|
16
|
-
# end
|
17
|
-
|
18
20
|
it 'reports the value of a parameter' do
|
19
21
|
handler.report_parameter_value(FB::Gcode.new { "A1 P1 V0" })
|
20
22
|
expect(bot.status.pin(1)).to eq(:off)
|
@@ -73,4 +73,79 @@ describe FB::OutgoingHandler do
|
|
73
73
|
handler.pin_write(pin: 0, value: 1, mode: 3)
|
74
74
|
expect(bot.next_cmd.to_s).to eq("F41 P0 V1 M3")
|
75
75
|
end
|
76
|
+
|
77
|
+
it 'sets max speed' do
|
78
|
+
handler.set_max_speed(:x, 1000)
|
79
|
+
expect(bot.next_cmd.to_s).to eq("F22 P71 V1000")
|
80
|
+
handler.set_max_speed(:Y, 100)
|
81
|
+
expect(bot.next_cmd.to_s).to eq("F22 P72 V100")
|
82
|
+
handler.set_max_speed('z', 1)
|
83
|
+
expect(bot.next_cmd.to_s).to eq("F22 P73 V1")
|
84
|
+
end
|
85
|
+
|
86
|
+
it 'sets acceleration' do
|
87
|
+
handler.set_acceleration(:x, 123)
|
88
|
+
expect(bot.next_cmd.to_s).to eq("F22 P41 V123")
|
89
|
+
handler.set_acceleration(:y, 123)
|
90
|
+
expect(bot.next_cmd.to_s).to eq("F22 P42 V123")
|
91
|
+
handler.set_acceleration(:z, 123)
|
92
|
+
expect(bot.next_cmd.to_s).to eq("F22 P43 V123")
|
93
|
+
end
|
94
|
+
|
95
|
+
it 'sets a timeout' do
|
96
|
+
handler.set_timeout(:x, 223)
|
97
|
+
expect(bot.next_cmd.to_s).to eq("F22 P11 V223")
|
98
|
+
handler.set_timeout(:y, 223)
|
99
|
+
expect(bot.next_cmd.to_s).to eq("F22 P12 V223")
|
100
|
+
handler.set_timeout(:z, 223)
|
101
|
+
expect(bot.next_cmd.to_s).to eq("F22 P13 V223")
|
102
|
+
end
|
103
|
+
|
104
|
+
it 'sets end stop inversion' do
|
105
|
+
handler.set_end_inversion(:x, true)
|
106
|
+
expect(bot.next_cmd.to_s).to eq("F22 P21 V1")
|
107
|
+
handler.set_end_inversion(:y, '0')
|
108
|
+
expect(bot.next_cmd.to_s).to eq("F22 P22 V0")
|
109
|
+
handler.set_end_inversion(:z, 1)
|
110
|
+
expect(bot.next_cmd.to_s).to eq("F22 P23 V1")
|
111
|
+
end
|
112
|
+
|
113
|
+
it 'sets motor inversion' do
|
114
|
+
handler.set_motor_inversion(:x, false)
|
115
|
+
expect(bot.next_cmd.to_s).to eq("F22 P31 V0")
|
116
|
+
handler.set_motor_inversion(:y, '1')
|
117
|
+
expect(bot.next_cmd.to_s).to eq("F22 P32 V1")
|
118
|
+
handler.set_motor_inversion(:z, 0)
|
119
|
+
expect(bot.next_cmd.to_s).to eq("F22 P33 V0")
|
120
|
+
end
|
121
|
+
|
122
|
+
it 'does NOT set negative coordinates' do
|
123
|
+
expect do
|
124
|
+
handler.set_negative_coordinates(:x, 99)
|
125
|
+
end.to raise_exception
|
126
|
+
end
|
127
|
+
|
128
|
+
it 'does NOT set steps per mm (thats not the Arduinos job)' do
|
129
|
+
expect do
|
130
|
+
handler.set_steps_per_mm(:x, 99)
|
131
|
+
end.to raise_exception
|
132
|
+
end
|
133
|
+
|
134
|
+
it 'sets end inversion' do
|
135
|
+
expect do
|
136
|
+
handler.set_steps_per_mm(:x, 99)
|
137
|
+
end.to raise_exception
|
138
|
+
end
|
139
|
+
|
140
|
+
it 'raises exceptions when given invalid axis names' do
|
141
|
+
expect do
|
142
|
+
handler.set_max_speed('q', 1)
|
143
|
+
end.to raise_exception(FB::OutgoingHandler::InvalidAxisEntry)
|
144
|
+
end
|
145
|
+
|
146
|
+
it 'handles bad inputs when inverting motors / steps' do
|
147
|
+
expect do
|
148
|
+
handler.set_motor_inversion(:x, 4)
|
149
|
+
end.to raise_exception(FB::OutgoingHandler::BadBooleanValue)
|
150
|
+
end
|
76
151
|
end
|
data/spec/lib/gcode_spec.rb
CHANGED
@@ -44,10 +44,5 @@ describe FB::Gcode do
|
|
44
44
|
expect(FB::Gcode.new{ " " }.cmd.head).to eq(null_token.head)
|
45
45
|
expect(FB::Gcode.new{ " " }.cmd.tail).to eq(null_token.tail)
|
46
46
|
end
|
47
|
-
|
48
|
-
it 'sets dyanmic parameters' do
|
49
|
-
random_gcode = FB::Gcode.new{ "Q#{Time.now.to_f.to_s[-2, 2]}" }
|
50
|
-
expect(random_gcode.cmd.tail).to_not eq(random_gcode.cmd.tail)
|
51
|
-
end
|
52
47
|
end
|
53
48
|
|
data/spec/spec_helper.rb
CHANGED
@@ -12,22 +12,6 @@ require_relative 'fakes/fake_logger'
|
|
12
12
|
require_relative 'fakes/fake_arduino'
|
13
13
|
require_relative 'fakes/fake_gcode'
|
14
14
|
|
15
|
-
require 'ruby-prof'
|
16
|
-
|
17
|
-
RSpec.configure do |config|
|
18
|
-
config.before(:suite) do
|
19
|
-
# Profile the code
|
20
|
-
RubyProf.start
|
21
|
-
end
|
22
|
-
|
23
|
-
config.after(:suite) do
|
24
|
-
result = RubyProf.stop
|
25
|
-
# Print a flat profile to text
|
26
|
-
printer = RubyProf::FlatPrinter.new(result)
|
27
|
-
printer.print(STDOUT)
|
28
|
-
end
|
29
|
-
end
|
30
|
-
|
31
15
|
# This is used for testing things that require an event loop. Once run, you can
|
32
16
|
# observe / make assertions on side effects.
|
33
17
|
def within_event_loop(ticks_remaining = 1)
|
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.
|
4
|
+
version: 0.7.0
|
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-08-04 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: bundler
|
@@ -144,6 +144,8 @@ files:
|
|
144
144
|
- lib/farmbot-serial.rb
|
145
145
|
- lib/gcode.rb
|
146
146
|
- lib/gcode.yml
|
147
|
+
- lib/parameters.yml
|
148
|
+
- serial-console.rb
|
147
149
|
- spec/fakes/fake_arduino.rb
|
148
150
|
- spec/fakes/fake_gcode.rb
|
149
151
|
- spec/fakes/fake_logger.rb
|