farmbot-serial 0.0.5 → 0.0.6
Sign up to get free protection for your applications and to get access to all the features.
- 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
|