lego_ev3 0.9.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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 5b2fb90c9305d336e5d81ec1a7d7ac242542c1cc
4
+ data.tar.gz: fcc6149efda2798a9e3c1cfff391812511cf9e14
5
+ SHA512:
6
+ metadata.gz: 2eb2ba5f4c07fa9e387845ff5d1d27d89114294f94a4f2482456ba3142f4f25f23c5bb302e3dfa39b21ff0bf4a2e42c6447be2c32ab8e4a6689728e693d91f39
7
+ data.tar.gz: 43e8cab0d3c77082c7ba1eab43c6f5d3299f9c939df5434f2aff32ba7976ee012101b018b49e6261350b9a4b825f37c048ca32c50873c61038762da40348769b
data/Gemfile ADDED
@@ -0,0 +1,8 @@
1
+ source 'http://rubygems.org'
2
+
3
+ gem 'net-ssh-simple'
4
+
5
+ group :development do
6
+ gem 'awesome_print'
7
+ gem 'pry'
8
+ end
data/README.md ADDED
@@ -0,0 +1,46 @@
1
+ # Lego EV3
2
+
3
+ ## Getting started
4
+
5
+ ### Make sure you have the necessary Ruby dependencies
6
+
7
+ The [ev3dev.org](http://www.ev3dev.org) distribution contains Ruby 2 but some libraries are missing to build native extensions. *This lib requires native extensions to work*.
8
+
9
+ Solution #1: Use [RVM](https://rvm.io).
10
+ Solution #2: Install those dependencies:
11
+
12
+ ```
13
+ apt-get install build-essential bison openssl libreadline6 libreadline6-dev curl git-core zlib1g zlib1g-dev libssl-dev libyaml-dev libxml2-dev autoconf libc6-dev ncurses-dev automake libtool ruby-dev
14
+ ```
15
+
16
+ ### Install the gem
17
+
18
+ ```
19
+ gem install lego-ev3
20
+ ```
21
+
22
+ ### A simple script
23
+
24
+ ```
25
+ require 'lego_ev3'
26
+
27
+ # The connection class look at the hostname of the machine to determine
28
+ # if the connection to establish must be local or remote (ssh).
29
+ # machine.hostname != config.hostname => remote.
30
+ connection = LegoEv3::Connection.new('ssh' => {
31
+ 'host' => '192.168.2.3',
32
+ 'hostname' => 'ev3dev',
33
+ 'username' => 'root',
34
+ 'password' => 'r00tme'
35
+ })
36
+
37
+ brick = LegoEv3::Brick.new(connection)
38
+
39
+ # Plug the touch sensor in any input and run this script by pressing
40
+ # or not the sensor. The 'pressed' value should change accordingly.
41
+ s = brick.sensors.first
42
+ s.poll
43
+ puts s.info.inspect
44
+
45
+ connection.close
46
+ ```
data/bin/lego-ev3 ADDED
@@ -0,0 +1,33 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'lego_ev3'
4
+ require 'optparse'
5
+
6
+ options = {
7
+ user_config: LegoEv3::default_user_config
8
+ }
9
+
10
+ OptionParser.new do |opt|
11
+ opt.on('-u', '--upload PROJECT', 'Upload everything under ./.../PROJECT to /home/PROJECT on the brick.') do |project|
12
+ options[:mode] = :upload
13
+ options[:project] = project
14
+ end
15
+
16
+ opt.on('-c, --config PATH', 'Use the provided configuration at PATH.') do |path|
17
+ options[:user_config].merge!(LegoEv3::load_config(path))
18
+ end
19
+ end.parse!
20
+
21
+ puts
22
+ puts "Config used:"
23
+ puts
24
+ ap options
25
+ puts
26
+
27
+ LegoEv3::Uploader
28
+ .new(
29
+ options[:user_config]['ssh']['host'],
30
+ options[:user_config]['ssh']['username'],
31
+ options[:user_config]['ssh']['password'],
32
+ options[:project])
33
+ .upload
data/lib/brick.rb ADDED
@@ -0,0 +1,96 @@
1
+ module LegoEv3
2
+ class Brick
3
+ def initialize(connection)
4
+ @connection = connection
5
+ @ports = []
6
+ @devices = []
7
+ @motors = []
8
+ @sensors = []
9
+
10
+ refresh!
11
+ motors.each{ |m| m.reset }
12
+ end
13
+
14
+ def motors
15
+ @motors
16
+ end
17
+
18
+ def sensors
19
+ @sensors
20
+ end
21
+
22
+ def refresh!
23
+ @ports = LegoEv3::Commands::LegoPort.list!(@connection).map do |port|
24
+ { id: port }
25
+ end
26
+
27
+ # Retrieve name and status in batch.
28
+ @ports.each do |port|
29
+ LegoEv3::Commands::LegoPort.get_port_name(@connection, port[:id]) do |name|
30
+ port[:name] = name
31
+ end
32
+
33
+ LegoEv3::Commands::LegoPort.get_status(@connection, port[:id]) do |status|
34
+ port[:status] = status
35
+ end
36
+ end
37
+
38
+ # Retrieve Port name -> Tacho motor in batch.
39
+ tacho_motors = {}
40
+ LegoEv3::Commands::TachoMotor.list!(@connection).each do |motor|
41
+ LegoEv3::Commands::TachoMotor.get_port_name(@connection, motor) do |port_name|
42
+ tacho_motors[port_name] = motor
43
+ end
44
+ end
45
+
46
+ # Retrieve Port name -> Lego sensor in batch.
47
+ lego_sensors = {}
48
+ LegoEv3::Commands::LegoSensor.list!(@connection).each do |sensor|
49
+ LegoEv3::Commands::LegoSensor.get_port_name(@connection, sensor) do |port_name|
50
+ lego_sensors[port_name] = sensor
51
+ end
52
+ end
53
+
54
+ @connection.flush
55
+
56
+ # Assemble port info.
57
+ @ports.each do |port|
58
+ status = port.delete(:status)
59
+ connected =
60
+ status != 'no-sensor' &&
61
+ status != 'no-motor' &&
62
+ status != 'error'
63
+
64
+ port[:type] = port[:name].start_with?('in') ? :sensor : :motor
65
+ port[:connected] = connected
66
+ port[:error] = status == 'error'
67
+ port[:driver] = connected ? status : nil
68
+ end
69
+
70
+ @motors = @ports
71
+ .select{ |p| p[:type] == :motor && p[:connected] }
72
+ .map{ |p| LegoEv3::TachoMotor.new(@connection, tacho_motors[p[:name]], p) }
73
+
74
+ @sensors = @ports
75
+ .select{ |p| p[:type] == :sensor && p[:connected] }
76
+ .map do |p|
77
+ id = lego_sensors[p[:name]]
78
+ driver_name = LegoEv3::Commands::LegoSensor.get_driver_name!(@connection, id)
79
+
80
+ if driver_name == 'lego-ev3-touch'
81
+ LegoEv3::TouchSensor.new(@connection, id, p)
82
+ else
83
+ nil
84
+ end
85
+ end.compact
86
+ end
87
+
88
+ def info
89
+ {
90
+ ports: @ports,
91
+ motors: @motors.map(&:info),
92
+ sensors: @sensors.map(&:info)
93
+ }
94
+ end
95
+ end
96
+ end
@@ -0,0 +1,106 @@
1
+ module LegoEv3
2
+ module Commands
3
+ module CommandBuilder
4
+ private
5
+
6
+ def base_path(path)
7
+ self.define_singleton_method "get_base_path" do
8
+ path
9
+ end
10
+
11
+ self.define_singleton_method "get_path" do |id|
12
+ get_base_path + "/#{id}"
13
+ end
14
+
15
+ self.define_singleton_method "get_command_path" do |id|
16
+ get_base_path + "/#{id}/command"
17
+ end
18
+ end
19
+
20
+ def get(alias_name, type = String, command_name = nil, processor = nil)
21
+ command_name ||= alias_name
22
+
23
+ self.define_singleton_method "get_#{alias_name}" do |connection, id, &callback|
24
+ connection.send("cat #{get_path(id)}/#{command_name}") do |response|
25
+ sanitized = (response || '').strip
26
+
27
+ if type == Integer
28
+ sanitized = sanitized.to_i
29
+ elsif type == Symbol
30
+ sanitized = sanitized.to_sym
31
+ end
32
+
33
+ sanitized = processor.call(sanitized) if processor
34
+ callback.call(sanitized) if callback
35
+ end
36
+ end
37
+
38
+ self.define_singleton_method "get_#{alias_name}!" do |connection, id|
39
+ return_value = nil
40
+
41
+ self.send("get_#{alias_name}", connection, id) do |response|
42
+ return_value = response
43
+ end
44
+
45
+ connection.flush
46
+
47
+ return_value
48
+ end
49
+ end
50
+
51
+ def set(alias_name, command_name = nil)
52
+ command_name ||= alias_name
53
+
54
+ self.define_singleton_method "set_#{alias_name}" do |connection, id, value|
55
+ connection.send("echo #{value} > #{get_path(id)}/#{command_name}")
56
+ end
57
+
58
+ self.define_singleton_method "set_#{alias_name}!" do |connection, id, value|
59
+ self.send("set_#{alias_name}", connection, id, value)
60
+ connection.flush
61
+ end
62
+ end
63
+
64
+ def get_set(alias_name, type = String, command_name = nil, processor = nil)
65
+ get(alias_name, type, command_name, processor)
66
+ set(alias_name, command_name)
67
+ end
68
+
69
+ def command(alias_name, command_name = nil)
70
+ command_name ||= alias_name
71
+
72
+ self.define_singleton_method alias_name do |connection, id|
73
+ connection.send("echo #{command_name} > #{get_command_path(id)}")
74
+ end
75
+
76
+ self.define_singleton_method "#{alias_name}!" do |connection, id|
77
+ self.send(alias_name, connection, id)
78
+ connection.flush
79
+ end
80
+ end
81
+
82
+ def has_list
83
+ self.define_singleton_method :list do |connection, &callback|
84
+ connection.send("ls -C #{get_base_path}") do |response|
85
+ # TODO: Bug? The folder is not created if no sensor plugged in once.
86
+ entries_raw = response || ''
87
+ entries = entries_raw.include?('No such file or directory') ? [] : entries_raw.split(' ').map(&:strip)
88
+ callback.call(entries)
89
+ end
90
+ end
91
+
92
+ self.define_singleton_method :list! do |connection|
93
+ entries = nil
94
+
95
+ list(connection) do |response|
96
+ entries = response
97
+ end
98
+
99
+ connection.flush
100
+
101
+ entries
102
+ end
103
+ end
104
+ end
105
+ end
106
+ end
@@ -0,0 +1,8 @@
1
+ module LegoEv3::Commands::LegoPort
2
+ extend LegoEv3::Commands::CommandBuilder
3
+
4
+ base_path '/sys/class/lego-port'
5
+ has_list
6
+ get :port_name
7
+ get :status
8
+ end
@@ -0,0 +1,18 @@
1
+ module LegoEv3::Commands::LegoSensor
2
+ extend LegoEv3::Commands::CommandBuilder
3
+
4
+ base_path '/sys/class/lego-sensor'
5
+ has_list
6
+ get :port_name
7
+ get :driver_name
8
+ get :decimals, Integer
9
+ get :num_values, Integer
10
+ get :value0, Integer
11
+ get :value1, Integer
12
+ get :value2, Integer
13
+ get :value3, Integer
14
+ get :value4, Integer
15
+ get :value5, Integer
16
+ get :value6, Integer
17
+ get :value7, Integer
18
+ end
@@ -0,0 +1,25 @@
1
+ module LegoEv3::Commands::TachoMotor
2
+ extend LegoEv3::Commands::CommandBuilder
3
+
4
+ base_path '/sys/class/tacho-motor'
5
+ has_list
6
+ get :port_name
7
+ get :count_per_rot, Integer
8
+ get :duty_cycle, Integer
9
+ get :states, String, 'state', -> response { response.split(' ').map{ |s| s.strip.to_sym } }
10
+ get_set :duty_cycle_sp, Integer
11
+ get_set :position, Integer
12
+ get_set :position_sp, Integer
13
+ get_set :speed_sp, Integer
14
+ get_set :polarity, Symbol
15
+ get_set :time_sp, Integer
16
+ get_set :stop_command, Symbol
17
+ get_set :speed_regulation, Symbol, nil, -> (response) { response == :on }
18
+ command :run_forever, 'run-forever'
19
+ command :run_to_abs_pos, 'run-to-abs-pos'
20
+ #command :run_to_rel_pos, 'run-to-rel-pos' # not used.
21
+ command :run_timed, 'run-timed'
22
+ command :run_direct, 'run-direct'
23
+ command :stop
24
+ command :reset
25
+ end
@@ -0,0 +1,37 @@
1
+ module LegoEv3
2
+ class BaseConnection
3
+ def initialize
4
+ @to_send = []
5
+ end
6
+
7
+ def send(command, &callback)
8
+ @to_send << [command, callback]
9
+ end
10
+
11
+ def flush
12
+ @connection ||= create_connection
13
+
14
+ joined_command = @to_send
15
+ .map{ |(c, _)| c }
16
+ .join(';')
17
+
18
+ callbacks = @to_send
19
+ .map{ |(_, c)| c }
20
+
21
+ joined_response, time = LegoEv3::with_timer do
22
+ call_connection(joined_command)
23
+ end
24
+
25
+ puts "#{joined_command}. #{time} ms."
26
+
27
+ # We assume that one command output one line of result.
28
+ responses = joined_response.split("\n")
29
+
30
+ callbacks.each_with_index.each do |c, i|
31
+ c.call(responses[i]) if c
32
+ end
33
+
34
+ @to_send.clear
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,50 @@
1
+ require 'optparse'
2
+
3
+ module LegoEv3
4
+ class Connection
5
+ def initialize(user_config = {})
6
+ options = {
7
+ user_config: LegoEv3::default_user_config.merge(user_config)
8
+ }
9
+
10
+ OptionParser.new do |opt|
11
+ opt.on('-c, --config PATH', 'Use the provided configuration at PATH.') do |path|
12
+ options[:user_config].merge!(LegoEv3::load_config(path))
13
+ end
14
+ end.parse!
15
+ is_local = `hostname`.strip == options[:user_config]['ssh']['hostname']
16
+
17
+ @inner_connection = if is_local
18
+ LegoEv3::LocalConnection.new
19
+ else
20
+ LegoEv3::RemoteConnection.new(
21
+ options[:user_config]['ssh']['host'],
22
+ options[:user_config]['ssh']['username'],
23
+ options[:user_config]['ssh']['password']
24
+ )
25
+ end
26
+ end
27
+
28
+ def send(command, &callback)
29
+ @inner_connection.send(command, &callback)
30
+ end
31
+
32
+ def flush
33
+ @inner_connection.flush
34
+ end
35
+
36
+ def close
37
+ @inner_connection.close
38
+ end
39
+
40
+ protected
41
+
42
+ def create_connection
43
+ @inner_connection.create_connection
44
+ end
45
+
46
+ def call_connection(command)
47
+ @inner_connection.call_connection(command)
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,14 @@
1
+ module LegoEv3
2
+ class LocalConnection < BaseConnection
3
+
4
+ def close; end
5
+
6
+ protected
7
+
8
+ def create_connection; end
9
+
10
+ def call_connection(command)
11
+ `command`
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,39 @@
1
+ require 'net/ssh/simple'
2
+
3
+ module LegoEv3
4
+ class RemoteConnection < BaseConnection
5
+ attr_accessor :timeout
6
+
7
+ def initialize(host, user, password)
8
+ super()
9
+
10
+ @host = host
11
+ @user = user
12
+ @password = password
13
+ @timeout = 10
14
+ end
15
+
16
+ def close
17
+ @connection.close if @connection
18
+ @connection = nil
19
+ end
20
+
21
+ protected
22
+
23
+ def create_connection
24
+ Net::SSH::Simple.new(host_name: @host, user: @user, password: @password, timeout: @timeout)
25
+ end
26
+
27
+ def call_connection(command)
28
+ begin
29
+ @connection.ssh(@host, command).stdout
30
+ rescue => e
31
+ if e.wrapped.kind_of?(Timeout::Error)
32
+ raise RemoteConnectionException.new(@host, @user, @password)
33
+ else
34
+ raise e
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,52 @@
1
+ require 'net/ssh/simple'
2
+
3
+ module LegoEv3
4
+ class Uploader
5
+ def initialize(host, user, password, project)
6
+ @host = host
7
+ @user = user
8
+ @password = password
9
+ @project = project
10
+ end
11
+
12
+ def upload
13
+ Net::SSH::Simple.sync({ host_name: @host, user: @user, password: @password, timeout: 600 }) do
14
+ puts "Creating folder #{project_folder}..."
15
+ ssh('ev3', "rm -rf #{project_folder}")
16
+
17
+ puts "Upload project..."
18
+ upload_folder(@project)
19
+
20
+ puts "Downloading dependencies from Gemfile..."
21
+ ssh('ev3', "cd #{project_folder} && gem install bundler && bundle install")
22
+ end
23
+ end
24
+
25
+ private
26
+
27
+ def upload_file(src_relative, dst_relative)
28
+ file_remote = "#{project_folder}/#{dst_relative}"
29
+ puts "Sending #{src_relative} to #{file_remote}..."
30
+ scp_put('ev3', src_relative, file_remote)
31
+ end
32
+
33
+ def upload_folder(src_relative)
34
+ folders = Dir.glob("#{src_relative}/**/*/")
35
+ files = Dir.glob("#{src_relative}/**/*").select { |f| File.file?(f) }
36
+
37
+ folders.each do |path_relative|
38
+ folder_remote = "#{project_folder}/#{path_relative}"
39
+ puts "Creating folder #{folder_remote}..."
40
+ ssh('ev3', "mkdir -p #{folder_remote}")
41
+ end
42
+
43
+ files.each do |path_relative|
44
+ upload_file(path_relative, path_relative)
45
+ end
46
+ end
47
+
48
+ def project_folder
49
+ "/home/#{@project.split('/').last}"
50
+ end
51
+ end
52
+ end
data/lib/exceptions.rb ADDED
@@ -0,0 +1,11 @@
1
+ module LegoEv3
2
+ class RemoteConnectionException < Exception
3
+ def initialize(host, user, password)
4
+ super(
5
+ "Could not connect to the brick. " +
6
+ "Make sure these command works: " +
7
+ "[ping #{host}]" +
8
+ "[ssh #{user}@#{host} + enter your password]")
9
+ end
10
+ end
11
+ end
data/lib/lego_ev3.rb ADDED
@@ -0,0 +1,13 @@
1
+ def require_all_relative(directory)
2
+ Dir[File.join(File.dirname(__FILE__), directory, '**', '*.rb')].each do |file|
3
+ require file
4
+ end
5
+ end
6
+
7
+ require_relative 'utilities'
8
+ require_relative 'exceptions'
9
+ require_all_relative 'commands'
10
+ require_all_relative 'connection'
11
+ require_all_relative 'sensors'
12
+ require_relative 'tacho_motor'
13
+ require_relative 'brick'
@@ -0,0 +1,30 @@
1
+ module LegoEv3
2
+ # More info: http://www.ev3dev.org/docs/drivers/lego-sensor-class/
3
+ class LegoSensor
4
+ def initialize(connection, id, port, driver_name)
5
+ @connection = connection
6
+ @id = id
7
+ @port = port
8
+ @driver_name = driver_name
9
+
10
+ LegoEv3::Commands::LegoSensor.get_decimals(@connection, @id) do |response|
11
+ @decimals = response
12
+ end
13
+
14
+ LegoEv3::Commands::LegoSensor.get_num_values(@connection, @id) do |response|
15
+ @value_parts_count = response
16
+ end
17
+
18
+ @connection.flush
19
+ end
20
+
21
+ def info
22
+ {
23
+ id: @id,
24
+ port: @port,
25
+ driver_name: @driver_name,
26
+ decimals: @decimals
27
+ }
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,23 @@
1
+ module LegoEv3
2
+ # More info: http://www.ev3dev.org/docs/drivers/lego-sensor-class/
3
+ class TouchSensor < LegoSensor
4
+ def initialize(connection, id, port)
5
+ super(connection, id, port, 'lego-ev3-touch')
6
+ end
7
+
8
+ def pressed?
9
+ @value == 1
10
+ end
11
+
12
+ def poll
13
+ @value = LegoEv3::Commands::LegoSensor.get_value0!(@connection, @id)
14
+ end
15
+
16
+ def info
17
+ super.merge({
18
+ sub_type: :touch,
19
+ pressed: pressed?
20
+ })
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,255 @@
1
+ module LegoEv3
2
+ # More info: http://www.ev3dev.org/docs/drivers/tacho-motor-class/
3
+ # TODO: ramp_up_sp, ramp_down_sp
4
+ # TODO: handle 'run_direct' and 'run_forever' states
5
+ # TODO: speed_regulation by default?
6
+ class TachoMotor
7
+ def initialize(connection, id, port)
8
+ @connection = connection
9
+ @id = id
10
+ @port = port
11
+
12
+ @speed = 0
13
+ @desired_speed = 0
14
+ @ticks_per_rotation = 0
15
+ @polarity = :normal
16
+ @position = 0
17
+ @desired_position = 0
18
+ @desired_time_ms = 0
19
+ @stop_mode = :coast
20
+ @running = false
21
+ @ramping = false
22
+ @holding = false
23
+ @stalled = false
24
+ @regulated_speed = false
25
+
26
+ sync!
27
+ end
28
+
29
+ # Run the motor indefinitely, until another command is sent.
30
+ def run_forever
31
+ ensure_valid_speed
32
+
33
+ LegoEv3::Commands::TachoMotor.run_forever!(@connection, @id)
34
+ end
35
+
36
+ # Run to an absolute position specified by *desired_position*.
37
+ # Then stop using the current stop behavior.
38
+ def run_to_absolute_position(desired_position)
39
+ ensure_valid_speed
40
+ old_position = position
41
+
42
+ LegoEv3::Commands::TachoMotor.set_position_sp!(@connection, @id, desired_position)
43
+ LegoEv3::Commands::TachoMotor.run_to_abs_pos!(@connection, @id)
44
+
45
+ loop do
46
+ break if operation_completed?(old_position)
47
+ end
48
+
49
+ position
50
+ end
51
+
52
+ # Run to a position relative to the current position.
53
+ # The new position will be *position* + *desired_position*.
54
+ # Then stop using the current stop behavior.
55
+ def run_to_relative_position(desired_position)
56
+ ensure_valid_speed
57
+ old_position = position
58
+
59
+ LegoEv3::Commands::TachoMotor.set_position_sp!(@connection, @id, old_position + desired_position)
60
+ LegoEv3::Commands::TachoMotor.run_to_abs_pos!(@connection, @id)
61
+
62
+ loop do
63
+ ap info
64
+ break if operation_completed?(old_position)
65
+ end
66
+
67
+ position
68
+ end
69
+
70
+ # Run the motor for the amount of time specified in *desired_time_ms*.
71
+ # Then stop using the current stop behavior.
72
+ def run_timed(desired_time_ms)
73
+ ensure_valid_speed
74
+ old_position = position
75
+
76
+ LegoEv3::Commands::TachoMotor.set_time_sp!(@connection, @id, desired_time_ms.to_i)
77
+ LegoEv3::Commands::TachoMotor.run_timed!(@connection, @id)
78
+
79
+ loop do
80
+ break if operation_completed?(old_position)
81
+ end
82
+
83
+ position
84
+ end
85
+
86
+ # Run the motor at the *desired_speed*.
87
+ # Unlike other run commands, changing *desired_speed* take immediate effect.
88
+ def run_direct
89
+ LegoEv3::Commands::TachoMotor.run_direct!(@connection, @id)
90
+ end
91
+
92
+ def stop
93
+ LegoEv3::Commands::TachoMotor.stop!(@connection, @id)
94
+ end
95
+
96
+ def reset
97
+ LegoEv3::Commands::TachoMotor.reset!(@connection, @id)
98
+ sync!
99
+ end
100
+
101
+ def ticks_per_rotation
102
+ @ticks_per_rotation = LegoEv3::Commands::TachoMotor.get_count_per_rot!(@connection, @id)
103
+ end
104
+
105
+ def speed
106
+ @speed = LegoEv3::Commands::TachoMotor.get_duty_cycle!(@connection, @id)
107
+ end
108
+
109
+ # This is actually the desired speed but feels more natural.
110
+ def speed=(new_value)
111
+ sanitized = [[new_value.to_i, -100].max, 100].min
112
+
113
+ # I will probably learn why this is not right but for now,
114
+ # I sync those 2 values to keep it simple.
115
+ LegoEv3::Commands::TachoMotor.set_speed_sp!(@connection, @id, sanitized)
116
+ LegoEv3::Commands::TachoMotor.set_duty_cycle_sp!(@connection, @id, sanitized)
117
+
118
+ desired_speed
119
+ end
120
+
121
+ def desired_speed
122
+ @desired_speed = LegoEv3::Commands::TachoMotor.get_duty_cycle_sp!(@connection, @id)
123
+ end
124
+
125
+ def polarity
126
+ @polarity = LegoEv3::Commands::TachoMotor.get_polarity!(@connection, @id)
127
+ end
128
+
129
+ def polarity=(new_value)
130
+ unless [:normal, :inversed].include?(new_value.to_sym)
131
+ raise Exception.new('Invalid polarity. Possible values: :normal, :inversed.')
132
+ end
133
+
134
+ LegoEv3::Commands::TachoMotor.set_polarity!(@connection, @id, new_value)
135
+ polarity
136
+ end
137
+
138
+ def position
139
+ @position = LegoEv3::Commands::TachoMotor.get_position!(@connection, @id)
140
+ end
141
+
142
+ def position=(new_value)
143
+ LegoEv3::Commands::TachoMotor.set_position!(@connection, @id, new_value.to_i)
144
+ position
145
+ end
146
+
147
+ def desired_position
148
+ @desired_position = LegoEv3::Commands::TachoMotor.get_position_sp!(@connection, @id)
149
+ end
150
+
151
+ def desired_time
152
+ @desired_time = LegoEv3::Commands::TachoMotor.get_time_sp!(@connection, @id)
153
+ end
154
+
155
+ def stop_mode
156
+ @stop_mode = LegoEv3::Commands::TachoMotor.get_stop_command!(@connection, @id)
157
+ end
158
+
159
+ def stop_mode=(new_value)
160
+ unless [:coast, :brake, :hold].include?(new_value.to_sym)
161
+ raise Exception.new('Invalid stop behavior. Possible values: :coast, :brake, :hold.')
162
+ end
163
+
164
+ LegoEv3::Commands::TachoMotor.set_stop_command!(@connection, @id, new_value)
165
+ stop_mode
166
+ end
167
+
168
+ def states
169
+ @states = LegoEv3::Commands::TachoMotor.get_states!(@connection, @id)
170
+ end
171
+
172
+ def running
173
+ update_states
174
+ @running
175
+ end
176
+
177
+ def ramping
178
+ update_states
179
+ @ramping
180
+ end
181
+
182
+ def holding
183
+ update_states
184
+ @holding
185
+ end
186
+
187
+ def stalled
188
+ update_states
189
+ @stalled
190
+ end
191
+
192
+ def regulated_speed
193
+ @regulated_speed = LegoEv3::Commands::TachoMotor.get_speed_regulation!(@connection, @id)
194
+ end
195
+
196
+ def regulated_speed=(new_value)
197
+ LegoEv3::Commands::TachoMotor.set_speed_regulation!(@connection, @id, new_value.kind_of?(TrueClass) ? 'on' : 'off')
198
+ regulated_speed
199
+ end
200
+
201
+ def sync!
202
+ ticks_per_rotation
203
+ speed
204
+ desired_speed
205
+ polarity
206
+ position
207
+ desired_position
208
+ desired_time
209
+ stop_mode
210
+ update_states
211
+
212
+ info
213
+ end
214
+
215
+ def info
216
+ {
217
+ id: @id,
218
+ port: @port,
219
+ ticks_per_rotation: @ticks_per_rotation,
220
+ speed: @speed,
221
+ desired_speed: @desired_speed,
222
+ position: @position,
223
+ desired_position: @desired_position,
224
+ polarity: @polarity,
225
+ desired_time_ms: @desired_time_ms,
226
+ stop_mode: @stop_mode,
227
+ running: @running,
228
+ ramping: @ramping,
229
+ holding: @holding,
230
+ stalled: @stalled
231
+ }
232
+ end
233
+
234
+ private
235
+
236
+ def ensure_valid_speed
237
+ throw Exception.new('Speed is set to 0.') if desired_speed == 0
238
+ end
239
+
240
+ def update_states
241
+ states = LegoEv3::Commands::TachoMotor.get_states!(@connection, @id)
242
+
243
+ @running = states.include?(:running)
244
+ @ramping = states.include?(:ramping)
245
+ @holding = states.include?(:holding)
246
+ @stalled = states.include?(:stalled)
247
+ end
248
+
249
+ def operation_completed?(old_position)
250
+ update_states
251
+
252
+ !@running || @holding
253
+ end
254
+ end
255
+ end
data/lib/utilities.rb ADDED
@@ -0,0 +1,33 @@
1
+ require 'yaml'
2
+
3
+ module LegoEv3
4
+ # bar, time = with_timer do
5
+ # foo()
6
+ # end
7
+ #
8
+ def self.with_timer(&block)
9
+ start = Time.now
10
+
11
+ return_value = block.call
12
+
13
+ finish = Time.now
14
+ diff = finish - start
15
+
16
+ [return_value, (diff * 1000).to_i] # in ms.
17
+ end
18
+
19
+ def self.default_user_config
20
+ {
21
+ 'ssh' => {
22
+ 'host' => '192.168.2.3', # I'm working on Mac OS X, eh.
23
+ 'hostname' => 'ev3dev',
24
+ 'username' => 'root',
25
+ 'password' => 'r00tme'
26
+ }
27
+ }
28
+ end
29
+
30
+ def self.load_config(path)
31
+ YAML::load(File.open(path))
32
+ end
33
+ end
metadata ADDED
@@ -0,0 +1,78 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: lego_ev3
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.9.0
5
+ platform: ruby
6
+ authors:
7
+ - Jodi Giordano
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-08-24 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: net-ssh-simple
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.6'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.6'
27
+ description: Uses the amazing ev3dev.org stuff to interface with the Lego EV3 starter
28
+ kit
29
+ email: giordano.jodi@gmail.com
30
+ executables:
31
+ - lego-ev3
32
+ extensions: []
33
+ extra_rdoc_files: []
34
+ files:
35
+ - Gemfile
36
+ - README.md
37
+ - bin/lego-ev3
38
+ - lib/brick.rb
39
+ - lib/commands/builder.rb
40
+ - lib/commands/lego_port.rb
41
+ - lib/commands/lego_sensor.rb
42
+ - lib/commands/tacho_motor.rb
43
+ - lib/connection/base.rb
44
+ - lib/connection/connection.rb
45
+ - lib/connection/local.rb
46
+ - lib/connection/remote.rb
47
+ - lib/connection/upload.rb
48
+ - lib/exceptions.rb
49
+ - lib/lego_ev3.rb
50
+ - lib/sensors/base.rb
51
+ - lib/sensors/touch.rb
52
+ - lib/tacho_motor.rb
53
+ - lib/utilities.rb
54
+ homepage: https://github.com/jodigiordano/lego_ev3
55
+ licenses:
56
+ - MIT
57
+ metadata: {}
58
+ post_install_message:
59
+ rdoc_options: []
60
+ require_paths:
61
+ - lib
62
+ required_ruby_version: !ruby/object:Gem::Requirement
63
+ requirements:
64
+ - - ">="
65
+ - !ruby/object:Gem::Version
66
+ version: '0'
67
+ required_rubygems_version: !ruby/object:Gem::Requirement
68
+ requirements:
69
+ - - ">="
70
+ - !ruby/object:Gem::Version
71
+ version: '0'
72
+ requirements: []
73
+ rubyforge_project:
74
+ rubygems_version: 2.4.6
75
+ signing_key:
76
+ specification_version: 4
77
+ summary: Library to interface with Lego EV3 starter kit
78
+ test_files: []