lego_ev3 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
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: []