farmbot-serial 0.0.9 → 0.1.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: a2c907d7ba555f07c9f184e46ad7c4c1ba37a8b4
4
- data.tar.gz: bf0a8d8e136cf24a30fc5ce12f562fd0104b500d
3
+ metadata.gz: 8656fb126d608b73102d0a77ac29d38e5fa60647
4
+ data.tar.gz: 67f35f21edc7b07e20894ef6e0f8768dd5d41751
5
5
  SHA512:
6
- metadata.gz: a119eebffdefd06529159ae97308e8084d62515482fbc4ccbf9ffdc611f7ff385befd77ac57301c371e213b62858cc5ed7e112b0d831cb5462a4c6d7bb0121f0
7
- data.tar.gz: 7ee15a9a02692998383c41b6d93d136eca611e1f6c159ae563f53213e7d87c48984cbd78e4f7e0261706827c93956415430385386ad359e772058c1f7f39530b
6
+ metadata.gz: 99d37958fcc632fea80f2393becb10da870d94289045a6b22c75e2f1a23c820f07c227f8ac92086598e4f55172fa81d88380dbcb591725edb873f0bba81f2eb5
7
+ data.tar.gz: c30665c95c08c2bd69be3ef65adf51ea7869ff41b2d94a712c00fdc8f92e16970e951c67de05f0cb434fe7ca6cb3cc9eb7df91dcd521fa421c029bcb313941d6
data/README.md CHANGED
@@ -2,13 +2,34 @@
2
2
 
3
3
  A ruby gem for controlling Farmbot via serial line with EventMachine.
4
4
 
5
- ## Usage
5
+ # Usage
6
+
7
+ ## As an Interactive Console or Debugger
8
+
9
+ ```
10
+ git clone https://github.com/FarmBot/farmbot-serial.git
11
+ cd farmbot-serial
12
+ ruby console.rb
13
+
14
+ ```
15
+
16
+ From there, you can type commands, such as:
17
+
18
+ ```
19
+ move_relative x: 100
20
+ ```
21
+
22
+ All REPL commands will be executed within the context of `bot.commands`.
23
+
24
+ ## As an Application
6
25
 
7
26
  ```
8
27
  gem install farmbot-serial, '0.0.5'
28
+
9
29
  ```
10
30
 
11
31
  ```ruby
32
+ require 'eventmachine'
12
33
  require 'farmbot-serial'
13
34
 
14
35
  bot = FB::Arduino.new # Defaults to '/dev/ttyACM0', can be configured.
data/console.rb ADDED
@@ -0,0 +1,55 @@
1
+ require_relative 'lib/farmbot-serial'
2
+ require 'pry'
3
+
4
+ bot = FB::Arduino.new # Defaults to '/dev/ttyACM0', can be configured.
5
+
6
+ puts """
7
+ FARMBOT SERIAL SANDBOX. WELCOME!
8
+ ================================
9
+
10
+ Example commands:
11
+
12
+ emergency_stop
13
+ move_relative x: 600, y: 100, z: 4
14
+ move_relative y: -600
15
+ home_x
16
+ home_y
17
+ home_z
18
+ home_all
19
+ read_parameter(8)
20
+ write_parameter('x', 0)
21
+ write_pin(pin: 8, value: 1, mode: 1)
22
+ read_status(8)
23
+ """
24
+ print "> "
25
+
26
+ class KeyboardHandler < EM::Connection
27
+ include EM::Protocols::LineText2
28
+
29
+ attr_reader :bot
30
+
31
+ def initialize(bot)
32
+ @bot = bot
33
+ end
34
+
35
+ def receive_line(data)
36
+ puts (bot.commands.instance_eval(data) || "OK")
37
+ print "> "
38
+ rescue Exception => exc
39
+ exit(0) if data.start_with?('q')
40
+ puts "#{exc.message} : "
41
+ print "> "
42
+ end
43
+ end
44
+
45
+ EM.run do
46
+ FB::ArduinoEventMachine.connect(bot)
47
+ bot.onmessage do |gcode|
48
+ bot.log "NEW MESSAGE : #{gcode};" unless gcode.cmd.head == :R
49
+ end
50
+ bot.onchange { |diff| puts "STATUS CHANGE: #{diff};" }
51
+ bot.onclose { puts "bye!"; EM.stop } # Unplug the bot and see
52
+ # EventMachine::PeriodicTimer.new(7) { print '.'; bot.serial_port.puts "F31 P8" }
53
+ EM.open_keyboard(KeyboardHandler, bot)
54
+ end
55
+
data/lib/arduino.rb CHANGED
@@ -8,6 +8,7 @@ require_relative 'arduino/status'
8
8
  module FB
9
9
  class Arduino
10
10
  class EmergencyStop < StandardError; end # Not yet used.
11
+ Position = Struct.new(:x, :y, :z)
11
12
 
12
13
  attr_accessor :serial_port, :logger, :commands, :inbound_queue, :status,
13
14
  :inputs, :outbound_queue
@@ -23,7 +24,7 @@ module FB
23
24
  @logger = logger
24
25
  @commands = FB::OutgoingHandler.new(self)
25
26
  @inputs = FB::IncomingHandler.new(self)
26
- @status = FB::Status.new(self)
27
+ @status = FB::Status.new
27
28
 
28
29
  start_event_listeners
29
30
  end
@@ -58,6 +59,10 @@ module FB
58
59
  @onclose.call if @onclose
59
60
  end
60
61
 
62
+ def current_position
63
+ Position.new(status[:X], status[:Y], status[:Z])
64
+ end
65
+
61
66
  private
62
67
 
63
68
  # Highest priority message when processing incoming Gcode. Use for system
@@ -69,13 +74,11 @@ module FB
69
74
  def execute_command_next_tick
70
75
  EM.next_tick do
71
76
  if status.ready?
72
- diff = (Time.now - (@time || Time.now)).to_i
73
- log "Sending queue after #{diff}s delay" if diff > 0
77
+ log "Exec after #{(Time.now - (@time || Time.now)).to_i}s wait"
74
78
  serial_port.puts @outbound_queue.pop
75
79
  @time = nil
76
80
  else
77
81
  @time ||= Time.now
78
- serial_port.puts "F31 P8"
79
82
  execute_command_next_tick
80
83
  end
81
84
  end
@@ -15,7 +15,7 @@ module FB
15
15
  # Gets called when data arrives.
16
16
  def receive_data(data)
17
17
  split_into_chunks(data).each do |chunk|
18
- if chunk.end_with?("\r\n")
18
+ if chunk.end_with?("\r\n") || chunk.end_with?("R00\n")
19
19
  add_to_buffer(chunk)
20
20
  send_buffer
21
21
  clear_buffer
@@ -16,6 +16,10 @@ module FB
16
16
  end
17
17
 
18
18
  def move_relative(x: 0, y: 0, z: 0, s: 100)
19
+ x += (bot.current_position.x || 0)
20
+ y += (bot.current_position.y || 0)
21
+ z += (bot.current_position.z || 0)
22
+
19
23
  write "G00 X#{x} Y#{y} Z#{z}"
20
24
  end
21
25
 
@@ -3,20 +3,20 @@ 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,YR: 0, R: 0, BUSY: 0}
6
+ ZB: 0,YR: 0, R: 0, BUSY: 1}
7
7
  Info = Struct.new(*DEFAULT_INFO.keys)
8
8
 
9
- attr_reader :bot
10
-
11
- def initialize(bot)
9
+ def initialize
12
10
  @changes = EM::Channel.new
13
- @bot, @info = bot, Info.new(*DEFAULT_INFO.values)
11
+ @info = Info.new(*DEFAULT_INFO.values)
14
12
  end
15
13
 
16
14
  def transaction(&blk)
17
15
  old = @info.to_h
18
- yield
19
- emit_updates(old)
16
+ yield(@info)
17
+ # Broadcast a diff between the old status and new status
18
+ diff = (@info.to_h.to_a - old.to_a).to_h
19
+ @changes << diff unless diff.empty?
20
20
  end
21
21
 
22
22
  def []=(register, value)
@@ -40,11 +40,5 @@ module FB
40
40
  def ready?
41
41
  self[:BUSY] == 0
42
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
49
43
  end
50
44
  end
data/lib/gcode.rb CHANGED
@@ -6,9 +6,9 @@ module FB
6
6
  attr_accessor :cmd, :params, :str
7
7
 
8
8
  def initialize(str)
9
- @str = str
10
- @params = str.split(' ').map{|line| GcodeToken.new(line)}
11
- @cmd = @params.shift || 'NULL'
9
+ @str = str
10
+ @params = str.split(' ').map { |line| GcodeToken.new(line) }
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
@@ -23,6 +23,7 @@ module FB
23
23
  end
24
24
 
25
25
  def to_s
26
+ # self.to_s # => "A12 B23 C45"
26
27
  [@cmd, *@params].map(&:to_s).join(" ")
27
28
  end
28
29
 
data/lib/gcode.yml CHANGED
@@ -31,6 +31,11 @@
31
31
  :R2: :done
32
32
  :R3: :error
33
33
  :R4: :busy
34
+ :R00: :idle
35
+ :R01: :received
36
+ :R02: :done
37
+ :R03: :error
38
+ :R04: :busy
34
39
  :R21: :report_parameter_value
35
40
  :R31: :report_status_value
36
41
  :R41: :report_pin_value
@@ -0,0 +1,5 @@
1
+ class StubLogger < StringIO
2
+ def initialize("")
3
+ super
4
+ end
5
+ end
@@ -0,0 +1,40 @@
1
+ require 'spec_helper'
2
+
3
+ describe FB::IncomingHandler do
4
+
5
+ let(:status) { FB::Status.new }
6
+
7
+ it "capitalizes all incoming status keys" do
8
+ status[:bUsY] = 345345345
9
+ expect(status[:BUSY]).to eq(345345345)
10
+ expect(status[:bUsy]).to eq(345345345)
11
+ expect(status[:busy]).to eq(345345345)
12
+ end
13
+
14
+ it "symbolizes all incoming status keys" do
15
+ status['busy'] = 878787
16
+ expect(status[:bUsy]).to eq(878787)
17
+ end
18
+
19
+ it "indicates BUSY status via #ready?()" do
20
+ status['busy'] = 1
21
+ expect(status.ready?).to be_falsey
22
+ status['busy'] = 0
23
+ expect(status.ready?).to be_truthy
24
+ end
25
+
26
+ it 'broadcasts status changes' do
27
+ @diff = {}
28
+
29
+ within_event_loop do
30
+ status.onchange { |diff| @diff = diff }
31
+ status[:busy] = 1
32
+ status[:busy] = 0
33
+ status[:busy] = 1
34
+ end
35
+
36
+ expect(status[:busy]).to eq(1)
37
+ expect(@diff).to eq(:BUSY => 1)
38
+ end
39
+ end
40
+
@@ -37,5 +37,10 @@ describe FB::Gcode do
37
37
  expect(codes.last.cmd.head).to eq(:C)
38
38
  expect(codes.first.params.first.tail).to eq(34)
39
39
  end
40
+
41
+ it 'handles parameterless Gcode' do
42
+ expect(FB::Gcode.new(" ").name).to be(:unknown)
43
+ expect(FB::Gcode.new(" ").cmd).to eq("NULL")
44
+ end
40
45
  end
41
46
 
data/spec/spec_helper.rb CHANGED
@@ -8,3 +8,10 @@ require 'farmbot-serial'
8
8
  require_relative 'fixtures/stub_serial_port'
9
9
  RSpec.configure do |config|
10
10
  end
11
+
12
+ def within_event_loop
13
+ EM.run do
14
+ yield
15
+ EM.next_tick { EM.stop }
16
+ end
17
+ 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.9
4
+ version: 0.1.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-04-04 00:00:00.000000000 Z
12
+ date: 2015-04-10 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: bundler
@@ -120,7 +120,7 @@ files:
120
120
  - ".rspec"
121
121
  - README.md
122
122
  - Rakefile
123
- - example.rb
123
+ - console.rb
124
124
  - lib/arduino.rb
125
125
  - lib/arduino/event_machine.rb
126
126
  - lib/arduino/incoming_handler.rb
@@ -130,7 +130,9 @@ files:
130
130
  - lib/farmbot-serial.rb
131
131
  - lib/gcode.rb
132
132
  - lib/gcode.yml
133
+ - spec/fixtures/stub_logger.rb
133
134
  - spec/fixtures/stub_serial_port.rb
135
+ - spec/lib/arduino/status_spec.rb
134
136
  - spec/lib/arduino_spec.rb
135
137
  - spec/lib/gcode_spec.rb
136
138
  - spec/spec_helper.rb
@@ -159,7 +161,9 @@ signing_key:
159
161
  specification_version: 4
160
162
  summary: Serial library for Farmbot
161
163
  test_files:
164
+ - spec/fixtures/stub_logger.rb
162
165
  - spec/fixtures/stub_serial_port.rb
166
+ - spec/lib/arduino/status_spec.rb
163
167
  - spec/lib/arduino_spec.rb
164
168
  - spec/lib/gcode_spec.rb
165
169
  - spec/spec_helper.rb
data/example.rb DELETED
@@ -1,50 +0,0 @@
1
- require_relative 'lib/farmbot-serial'
2
- require 'pry'
3
-
4
- bot = FB::Arduino.new # Defaults to '/dev/ttyACM0', can be configured.
5
-
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('x', 0)",
19
- "o" => "bot.commands.write_pin(pin: 8, value: 1, mode: 1)",
20
- "p" => "bot.commands.read_status(8)",
21
- }
22
-
23
- $commands.each { |k, v| puts "#{k}: #{v}" }
24
-
25
- class KeyboardHandler < EM::Connection
26
- include EM::Protocols::LineText2
27
-
28
- attr_reader :bot
29
-
30
- def initialize(bot)
31
- @bot = bot
32
- end
33
-
34
- def receive_line(data)
35
- cmd = $commands[data] || ""
36
- eval(cmd)
37
- end
38
- end
39
-
40
- puts "Starting now."
41
-
42
- EM.run do
43
- FB::ArduinoEventMachine.connect(bot)
44
- bot.onmessage { |gcode| print "#{gcode.name}; " }
45
- bot.onchange { |diff| print "#{diff}; " }
46
- bot.onclose { puts "bye!"; EM.stop } # Unplug the bot and see
47
- # EventMachine::PeriodicTimer.new(2) { bot.serial_port.puts "G82" }
48
- EM.open_keyboard(KeyboardHandler, bot)
49
- end
50
-