farmbot-serial 0.0.9 → 0.1.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 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
-