farmbot-serial 0.0.5 → 0.0.6
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/README.md +18 -8
- data/example.rb +21 -0
- data/lib/arduino.rb +19 -10
- data/lib/arduino/command_set.rb +39 -0
- data/lib/arduino/event_machine.rb +61 -0
- data/lib/arduino/status.rb +25 -0
- data/lib/default_serial_port.rb +3 -2
- data/lib/farmbot-serial.rb +1 -4
- data/lib/gcode.rb +39 -0
- data/lib/gcode.yml +39 -0
- data/lib/param.yml +21 -0
- metadata +10 -4
- data/lib/arduino_event_machine.rb +0 -31
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 968bffc9904f9b3d733c91107ec83caeb632353a
|
4
|
+
data.tar.gz: 0eb0a17a2e6923e3e0ef2ec8228c5ebf2d99cd09
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: fd10320b1219e6001c2e5bb73ffc3cada2e4bd5e9efcaa35ba72476a2667c8969cf3b18eb3c493937c57c37605b5855b52606fd4c38bed5b2395eae0a68a7cd4
|
7
|
+
data.tar.gz: fe5fc6bead0a8fb4d39ab7a1235f3436f3730b2505d2b01e94f9792258136b6ba8bd29b5a884e1c2d09938eefc2de34a286c1a86b7f37b1e58ffe82771c40387
|
data/README.md
CHANGED
@@ -4,17 +4,27 @@ A ruby gem for controlling Farmbot via serial line with EventMachine.
|
|
4
4
|
|
5
5
|
## Usage
|
6
6
|
|
7
|
+
```
|
8
|
+
gem install farmbot-serial, '0.0.5'
|
9
|
+
```
|
10
|
+
|
7
11
|
```ruby
|
8
|
-
bot = FB::Arduino.new # Defaults to '/dev/ttyACM0'
|
12
|
+
bot = FB::Arduino.new # Defaults to '/dev/ttyACM0', can be configured.
|
9
13
|
|
10
14
|
EM.run do
|
11
|
-
# Register bot with event loop.
|
12
15
|
FB::ArduinoEventMachine.connect(bot)
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
16
|
+
|
17
|
+
# Example 1: Writing to the serial line the "correct way" every 1.5 seconds.
|
18
|
+
command = FB::Gcode.new('G01 X01 Y01 Z01')
|
19
|
+
EventMachine::PeriodicTimer.new(1.5) { bot.commands.move_relative(command) }
|
20
|
+
|
21
|
+
# Example 2: Writing raw strings to serial every 2.5
|
22
|
+
EventMachine::PeriodicTimer.new(2.5) { bot.write("F31 P8") }
|
23
|
+
|
24
|
+
# This will execute after status has been updated / internal code.
|
25
|
+
bot.onmessage { |gcode| puts "SERIAL IN: #{gcode.name}" }
|
26
|
+
|
27
|
+
# Try pulling the USB cable out to test this one.
|
28
|
+
bot.onclose { EM.stop }
|
19
29
|
end
|
20
30
|
```
|
data/example.rb
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
require_relative 'lib/farmbot-serial'
|
2
|
+
require 'pry'
|
3
|
+
|
4
|
+
bot = FB::Arduino.new # Defaults to '/dev/ttyACM0', can be configured.
|
5
|
+
|
6
|
+
EM.run do
|
7
|
+
FB::ArduinoEventMachine.connect(bot)
|
8
|
+
|
9
|
+
# Example 1: Writing to the serial line the "correct way" every 1.5 seconds.
|
10
|
+
command = FB::Gcode.new('G01 X01 Y01 Z01')
|
11
|
+
EventMachine::PeriodicTimer.new(1.5) { bot.commands.move_relative(command) }
|
12
|
+
|
13
|
+
# Example 2: Writing raw strings to serial every 2.5
|
14
|
+
EventMachine::PeriodicTimer.new(2.5) { bot.write("F31 P8") }
|
15
|
+
|
16
|
+
# This will execute after status has been updated / internal code.
|
17
|
+
bot.onmessage { |gcode| puts "SERIAL IN: #{gcode.name}" }
|
18
|
+
|
19
|
+
# Try pulling the USB cable out to test this one.
|
20
|
+
bot.onclose { EM.stop }
|
21
|
+
end
|
data/lib/arduino.rb
CHANGED
@@ -1,16 +1,21 @@
|
|
1
|
+
require 'serialport'
|
2
|
+
require_relative 'default_serial_port'
|
3
|
+
require_relative 'arduino/command_set'
|
4
|
+
require_relative 'arduino/event_machine'
|
5
|
+
require_relative 'arduino/status'
|
1
6
|
# Communicate with the arduino using a serial interface
|
2
7
|
module FB
|
3
8
|
class Arduino
|
4
9
|
class EmergencyStop < StandardError; end # Not yet used.
|
5
10
|
|
6
|
-
attr_reader :serial_port, :logger, :commands, :queue
|
11
|
+
attr_reader :serial_port, :logger, :commands, :queue, :status
|
7
12
|
|
8
13
|
# Initial and provide a serial object, as well as an IO object to send
|
9
14
|
# log messages to. Default SerialPort is DefaultSerialPort. Default logger
|
10
15
|
# is STDOUT
|
11
16
|
def initialize(serial_port = DefaultSerialPort.new, logger = STDOUT)
|
12
|
-
@serial_port, @logger, @queue = serial_port, logger, EM::
|
13
|
-
@commands = FB::ArduinoCommandSet.new(self)
|
17
|
+
@serial_port, @logger, @queue = serial_port, logger, EM::Channel.new
|
18
|
+
@commands, @status = FB::ArduinoCommandSet.new(self), FB::Status.new(self)
|
14
19
|
end
|
15
20
|
|
16
21
|
# Log to screen/file/IO stream
|
@@ -18,12 +23,20 @@ module FB
|
|
18
23
|
logger.puts(message)
|
19
24
|
end
|
20
25
|
|
26
|
+
# Highest priority message when processing incoming Gcode. Use for system
|
27
|
+
# level status changes.
|
28
|
+
def parse_incoming(gcode)
|
29
|
+
commands.execute(gcode)
|
30
|
+
end
|
31
|
+
|
21
32
|
# Handle incoming text from arduino into pi
|
22
33
|
def onmessage(&blk)
|
23
34
|
raise 'read() requires a block' unless block_given?
|
24
|
-
@queue.
|
25
|
-
|
26
|
-
|
35
|
+
@queue.subscribe do |gcodes|
|
36
|
+
gcodes.each do |gcode|
|
37
|
+
parse_incoming(gcode)
|
38
|
+
blk.call(gcode)
|
39
|
+
end
|
27
40
|
end
|
28
41
|
end
|
29
42
|
|
@@ -34,7 +47,6 @@ module FB
|
|
34
47
|
# Send outgoing test to arduino from pi
|
35
48
|
def write(string)
|
36
49
|
serial_port.puts string
|
37
|
-
log "SENT #{string}"
|
38
50
|
end
|
39
51
|
|
40
52
|
# Handle loss of serial connection
|
@@ -42,8 +54,5 @@ module FB
|
|
42
54
|
log "Connection to device lost"
|
43
55
|
@onclose.call if @onclose
|
44
56
|
end
|
45
|
-
|
46
|
-
def reconnect
|
47
|
-
end
|
48
57
|
end
|
49
58
|
end
|
@@ -0,0 +1,39 @@
|
|
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
|
+
|
@@ -0,0 +1,61 @@
|
|
1
|
+
require 'eventmachine'
|
2
|
+
module FB
|
3
|
+
# Class that is fed into event machine's event loop to handle incoming serial
|
4
|
+
# messages asynchronously via EM.attach(). See: EM.attach
|
5
|
+
class ArduinoEventMachine < EventMachine::Connection
|
6
|
+
class << self
|
7
|
+
attr_accessor :arduino
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.poll(interval, &blk)
|
11
|
+
EventMachine::PeriodicTimer.new(interval.to_f, &blk)
|
12
|
+
end
|
13
|
+
|
14
|
+
def initialize
|
15
|
+
@q, @buffer = self.class.arduino.queue, ''
|
16
|
+
end
|
17
|
+
|
18
|
+
# Gets called when data arrives.
|
19
|
+
def receive_data(data)
|
20
|
+
split_into_chunks(data).each do |chunk|
|
21
|
+
if chunk.end_with?("\r\n")
|
22
|
+
add_to_buffer(chunk)
|
23
|
+
send_buffer
|
24
|
+
clear_buffer
|
25
|
+
else
|
26
|
+
add_to_buffer(chunk)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
# This is a nasty hack that takes incoming strings from the serial line and
|
32
|
+
# splits the data on \r\n. Unlike Ruby's split() method, this method will
|
33
|
+
# preserve the \r\n.
|
34
|
+
def split_into_chunks(data)
|
35
|
+
data.gsub("\r\n", '!@').split('@').map{ |d| d.gsub('!', "\r\n") }
|
36
|
+
end
|
37
|
+
|
38
|
+
def clear_buffer
|
39
|
+
@buffer = ''
|
40
|
+
end
|
41
|
+
|
42
|
+
def add_to_buffer(d)
|
43
|
+
@buffer += d
|
44
|
+
end
|
45
|
+
|
46
|
+
def send_buffer
|
47
|
+
@q << Gcode.parse_lines(@buffer)
|
48
|
+
end
|
49
|
+
|
50
|
+
# Gets called when the connection breaks.
|
51
|
+
def unbind
|
52
|
+
self.class.arduino.disconnect
|
53
|
+
EM.stop
|
54
|
+
end
|
55
|
+
|
56
|
+
def self.connect(arduino)
|
57
|
+
@arduino = arduino
|
58
|
+
EM.attach arduino.serial_port, self
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module FB
|
2
|
+
class Status
|
3
|
+
# Map of informational status and default values for status within Arduino.
|
4
|
+
DEFAULT_INFO = {X: 0, Y: 0, Z: 0, S: 10, Q: 0, T: 0, C: '', P: 0, V: 0,
|
5
|
+
W: 0, L: 0, E: 0, M: 0, XA: 0, XB: 0, YA: 0, YB: 0, ZA: 0,
|
6
|
+
ZB: 0, busy: 1}
|
7
|
+
# Put it into a struct.
|
8
|
+
Info = Struct.new(*DEFAULT_INFO.keys)
|
9
|
+
|
10
|
+
attr_reader :bot
|
11
|
+
|
12
|
+
def initialize(bot)
|
13
|
+
@bot, @info = bot, Info.new(*DEFAULT_INFO.values)
|
14
|
+
end
|
15
|
+
|
16
|
+
def []=(register, value)
|
17
|
+
# Add event broadcasts here!!!
|
18
|
+
@info[value.upcase.to_sym] = value
|
19
|
+
end
|
20
|
+
|
21
|
+
def [](value)
|
22
|
+
@info[value.upcase.to_sym]
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
data/lib/default_serial_port.rb
CHANGED
@@ -1,7 +1,8 @@
|
|
1
1
|
require 'serialport'
|
2
2
|
|
3
|
-
# This object creates a Serial IO with sane
|
4
|
-
# follow the same serial configuration setup.
|
3
|
+
# This object creates a Serial IO with sane defaults, since most FarmBot setups
|
4
|
+
# follow the same serial configuration setup. You can build your own SerialPort
|
5
|
+
# object also, if that's what you need.
|
5
6
|
module FB
|
6
7
|
class DefaultSerialPort < SerialPort
|
7
8
|
COM_PORT = '/dev/ttyACM0'
|
data/lib/farmbot-serial.rb
CHANGED
data/lib/gcode.rb
ADDED
@@ -0,0 +1,39 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
module FB
|
3
|
+
class Gcode
|
4
|
+
GCODE_DICTIONARY = YAML.load_file(File.join(File.dirname(__FILE__), 'gcode.yml'))
|
5
|
+
|
6
|
+
attr_accessor :cmd, :params, :str
|
7
|
+
|
8
|
+
def initialize(str)
|
9
|
+
@str = str
|
10
|
+
@params = str.split(' ').map{|line| GcodeToken.new(line)}
|
11
|
+
@cmd = @params.shift
|
12
|
+
end
|
13
|
+
|
14
|
+
# Turns a string of many gcodes into an array of many gcodes. Used to parse
|
15
|
+
# incoming serial.
|
16
|
+
def self.parse_lines(string)
|
17
|
+
string.gsub("\r", '').split("\n").map { |s| self.new(s) }
|
18
|
+
end
|
19
|
+
|
20
|
+
# Returns a symbolized english version of the gcode's name.
|
21
|
+
def name
|
22
|
+
GCODE_DICTIONARY[cmd.to_sym] || :unknown
|
23
|
+
end
|
24
|
+
|
25
|
+
# A head/tail pair of a single node of GCode. Ex: R01 = [:R, '01']
|
26
|
+
class GcodeToken
|
27
|
+
attr_reader :head, :tail, :name
|
28
|
+
|
29
|
+
def initialize(str)
|
30
|
+
nodes = str.scan(/\d+|\D+/) # ["R", "01"]
|
31
|
+
@head, @tail = nodes.shift.to_sym, nodes.join(" ")
|
32
|
+
end
|
33
|
+
|
34
|
+
def to_sym
|
35
|
+
"#{head}#{tail}".to_sym
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
data/lib/gcode.yml
ADDED
@@ -0,0 +1,39 @@
|
|
1
|
+
---
|
2
|
+
:G0: :move_to_location_at_given_speed_for_axis
|
3
|
+
:G1: :move_to_location_on_a_straight_line
|
4
|
+
:G28: :move_home_all_axis
|
5
|
+
:F1: :dose_amount_of_water_using_time_in_millisecond
|
6
|
+
:F2: :dose_amount_of_water_using_flow_meter_that_measures_pulses
|
7
|
+
:F11: :home_x_axis
|
8
|
+
:F12: :home_y_axis
|
9
|
+
:F13: :home_z_axis
|
10
|
+
:F14: :calibrate_x_axis
|
11
|
+
:F15: :calibrate_y_axis
|
12
|
+
:F16: :calibrate_z_axis
|
13
|
+
:F21: :read_parameter
|
14
|
+
:F22: :write_parameter
|
15
|
+
:F23: :update_parameter_during_calibration
|
16
|
+
:F31: :read_status
|
17
|
+
:F32: :write_status
|
18
|
+
:F41: :set_a_value_on_an_arduino_pin
|
19
|
+
:F42: :read_a_value_from_an_arduino_pin
|
20
|
+
:F43: :set_the_mode_of_a_pin_in_arduino
|
21
|
+
:F44: :set_the_value_v_on_an_arduino_pin
|
22
|
+
:F51: :set_a_value_on_the_tool_mount_with_i2c
|
23
|
+
:F52: :read_value_from_the_tool_mount_with_i2c
|
24
|
+
:F61: :set_the_servo_on_the_pin_to_the_requested_angle
|
25
|
+
:F81: :report_end_stop
|
26
|
+
:F82: :report_current_position
|
27
|
+
:F83: :report_software_version
|
28
|
+
:E: :emergency_stop
|
29
|
+
:R01: :received
|
30
|
+
:R02: :done
|
31
|
+
:R03: :error
|
32
|
+
:R04: :busy
|
33
|
+
:R21: :report_parameter_value
|
34
|
+
:R31: :report_status_value
|
35
|
+
:R41: :report_pin_value
|
36
|
+
:R81: :reporting_end_stops
|
37
|
+
:R82: :report_current_position
|
38
|
+
:R83: :report_software_version
|
39
|
+
:R99: :debug_message
|
data/lib/param.yml
ADDED
@@ -0,0 +1,21 @@
|
|
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
|
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.6
|
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-03-
|
12
|
+
date: 2015-03-27 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: bundler
|
@@ -120,10 +120,16 @@ files:
|
|
120
120
|
- ".rspec"
|
121
121
|
- README.md
|
122
122
|
- Rakefile
|
123
|
+
- example.rb
|
123
124
|
- lib/arduino.rb
|
124
|
-
- lib/
|
125
|
+
- lib/arduino/command_set.rb
|
126
|
+
- lib/arduino/event_machine.rb
|
127
|
+
- lib/arduino/status.rb
|
125
128
|
- lib/default_serial_port.rb
|
126
129
|
- lib/farmbot-serial.rb
|
130
|
+
- lib/gcode.rb
|
131
|
+
- lib/gcode.yml
|
132
|
+
- lib/param.yml
|
127
133
|
- spec/fixtures/stub_serial_port.rb
|
128
134
|
- spec/lib/ramps_arduino_values_received_spec.rb
|
129
135
|
- spec/spec_helper.rb
|
@@ -148,7 +154,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
148
154
|
version: '0'
|
149
155
|
requirements: []
|
150
156
|
rubyforge_project:
|
151
|
-
rubygems_version: 2.4.
|
157
|
+
rubygems_version: 2.4.6
|
152
158
|
signing_key:
|
153
159
|
specification_version: 4
|
154
160
|
summary: Serial library for Farmbot
|
@@ -1,31 +0,0 @@
|
|
1
|
-
require 'eventmachine'
|
2
|
-
module FB
|
3
|
-
# Class that is fed into event machine's event loop to handle incoming serial
|
4
|
-
# messages asynchronously via EM.attach(). See: EM.attach
|
5
|
-
class ArduinoEventMachine < EventMachine::Connection
|
6
|
-
class << self
|
7
|
-
attr_accessor :arduino
|
8
|
-
end
|
9
|
-
|
10
|
-
def self.poll(interval, &blk)
|
11
|
-
raise 'You must pass a block' unless block_given?
|
12
|
-
EventMachine::PeriodicTimer.new(interval.to_f, &blk)
|
13
|
-
end
|
14
|
-
|
15
|
-
# Gets called when data arrives.
|
16
|
-
def receive_data(data)
|
17
|
-
self.class.arduino.queue.push(data)
|
18
|
-
end
|
19
|
-
|
20
|
-
# Gets called when the connection breaks.
|
21
|
-
def unbind
|
22
|
-
self.class.arduino.disconnect
|
23
|
-
EM.stop
|
24
|
-
end
|
25
|
-
|
26
|
-
def self.connect(arduino)
|
27
|
-
@arduino = arduino
|
28
|
-
EM.attach arduino.serial_port, self
|
29
|
-
end
|
30
|
-
end
|
31
|
-
end
|