farmbot-serial 0.6.2 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: deccbc9e0c3c50ebf83cc8182458dfd403ae96fd
4
- data.tar.gz: a2c81cca29087b45e5ed53ad0fd6521ac3b91ab0
3
+ metadata.gz: caf76751695f80320148ccda60a2b7f5f90d99ce
4
+ data.tar.gz: df0cb1eed0147a30522799aa64f1ddedb599b773
5
5
  SHA512:
6
- metadata.gz: e492413633dbb7409125c1ee53490552c6aa90ebd9cab53e346a8024d02159459bec07d6c1d2483c214d99ef65c65b9a5c2c6f4d24089f72dff4d502ab5fdb8b
7
- data.tar.gz: 8dd6af1fe1463ee1a55c63c6701fb7059824a1d21b2b61dadb6431dbb425bbc66c1a799187ceadad5bd5b8b7e22fe8ad1442f62736cfd0730a29eca85fb7dfab
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
- self.send(gcode.name, gcode)
12
- rescue NoMethodError
13
- bot.log "#{gcode.name} is a valid GCode, but no input handler method exists"
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.set_pin(gcode.value_of(:P), gcode.value_of(:V))
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.set_pin(gcode.value_of(:P), gcode.value_of(:V))
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.set_pin(pin, value)
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
@@ -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', PINS: {}}
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 = Info.new(*DEFAULT_INFO.values)
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 do
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.to_sym]
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 do |p|
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 pin(num)
53
- @info[:PINS][num] || :unknown
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 set_pin(num, val)
57
- val = [true, 1, '1'].include?(val) ? :on : :off
58
- transaction { |info| info.PINS[num] = val }
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 = YAML.load_file(File.join(File.dirname(__FILE__), 'gcode.yml'))
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
@@ -1,3 +1,5 @@
1
+ # This is a dictionary of all the GCodes. They are used by the Arduino to
2
+ # differentiate commands.
1
3
  ---
2
4
  :G0: :move_to_location_at_given_speed_for_axis
3
5
  :G1: :move_to_location_on_a_straight_line
@@ -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
@@ -46,7 +46,7 @@ describe FB::Status do
46
46
  end
47
47
 
48
48
  it 'reads known and unknow pin values' do
49
- status.set_pin(1, 1)
49
+ status.set(1, 1)
50
50
  expect(status.pin(1)).to eq(:on)
51
51
  expect(status.pin(2)).to eq(:unknown)
52
52
  end
@@ -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.6.2
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-06-07 00:00:00.000000000 Z
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