Skip to content

Commit

Permalink
motors now sensing
Browse files Browse the repository at this point in the history
  • Loading branch information
jfcloutier committed Nov 17, 2015
1 parent ee2b4e1 commit 08251a2
Show file tree
Hide file tree
Showing 15 changed files with 323 additions and 104 deletions.
5 changes: 5 additions & 0 deletions lib/ev3.ex
Original file line number Diff line number Diff line change
Expand Up @@ -32,5 +32,10 @@ defmodule Ev3 do
Endpoint.config_change(changed, removed)
:ok
end

@doc "Whether in test mode"
def testing?() do
Application.get_env(:ev3, :mock)
end

end
6 changes: 3 additions & 3 deletions lib/ev3/body/color_sensor.ex
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,11 @@ defmodule Ev3.ColorSensor do
500
end

def sensitivity(sensor) do
case mode(sensor) do
def sensitivity(_sensor, sense) do
case sense do
:color -> nil
:ambient -> 2
:reflect -> 2
:reflected -> 2
end
end

Expand Down
32 changes: 21 additions & 11 deletions lib/ev3/body/infrared_sensor.ex
Original file line number Diff line number Diff line change
Expand Up @@ -13,18 +13,26 @@ defmodule Ev3.InfraredSensor do
### Ev3.Sensing behaviour

def senses(_) do
# beacon_senses = Enum.map(1.. @max_beacon_channels,
# &([{:beacon_heading, &1}, {:beacon_distance, &1}, {:beacon_on, &1}]))
# |> List.flatten()
# [:proximity | beacon_senses]
[:proximity]
beacon_senses = Enum.map(1.. @max_beacon_channels,
&([{:beacon_heading, &1}, {:beacon_distance, &1}, {:beacon_on, &1}, {:remote_buttons, &1}]))
|> List.flatten()
[:proximity | beacon_senses]
end

def read(sensor, :proximity) do
def read(sensor, sense) do
{_, updated_sensor} = do_read(sensor, sense)
do_read(updated_sensor, sense) # double read seems necessary after a mode change
end

defp do_read(sensor, :proximity) do
proximity(sensor)
end

def read(sensor, {beacon_sense, channel}) do
defp do_read(sensor, {:remote_buttons, channel}) do
remote_buttons(sensor, channel)
end

defp do_read(sensor, {beacon_sense, channel}) do
case beacon_sense do
:beacon_heading -> seek_heading(sensor, channel)
:beacon_distance -> seek_distance(sensor, channel)
Expand All @@ -36,11 +44,13 @@ defmodule Ev3.InfraredSensor do
500
end

def sensitivity(sensor) do
case mode(sensor) do
def sensitivity(_sensor, sense) do
case (sense) do
:proximity -> 2
:seek -> 2
:remote -> nil
{:beacon_heading, _} -> 2
{:beacon_distance, _} -> 2
{:beacon_on, _} -> nil
{:remote_buttons, _} -> nil
end
end

Expand Down
105 changes: 105 additions & 0 deletions lib/ev3/body/lego_motor.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
defmodule Ev3.LegoMotor do
@moduledoc "Lego motor access"

alias Ev3.Device
import Ev3.Sysfs
require Logger

@sys_path "/sys/class/tacho-motor"
@prefix "motor"
@driver_regex ~r/lego-ev3-(\w)-motor/i

@doc "Generates a list of all plugged in motor devices"
def motors() do
if !Ev3.testing?() do
File.ls!(@sys_path)
|> Enum.filter(&(String.starts_with?(&1, @prefix)))
|> Enum.map(&(init_motor("#{@sys_path}/#{&1}")))
else
[Ev3.Mock.Tachomotor.new(:large), Ev3.Mock.Tachomotor.new(:medium)]
end
end

defp dispatch(motor) do
if !Ev3.testing?() do
Ev3.Tachomotor
else
Ev3.Mock.Tachomotor
end
end

@doc "Get the list of senses from a motor"
def senses(motor) do
apply(dispatch(motor), :senses, [motor])
end


@doc "Read the value of a sense from a motor"
def read(motor, sense) do # {value, updated_motor} - value can be nil
try do
apply(dispatch(motor), :read, [motor, sense])
rescue
error ->
Logger.warn("#{inspect error} when reading #{inspect sense} from #{inspect motor}")
{nil, motor}
end
end

@doc "Get how long to pause between reading a sense from a motor. In msecs"
def pause(motor) do
apply(dispatch(motor), :pause, [motor])
end

@doc "Get the resolution of a motor (the delta between essentially identical readings). Nil or an integer."
def sensitivity(motor, sense) do
apply(dispatch(motor), :sensitivity, [motor, sense])
end

@doc "Is this a large motor?"
def large?(motor) do
motor.type == :large
end

@doc "Is this a medium motor?"
def medium?(motor) do
motor.type == :medium
end

### PRIVATE

defp init_motor(path) do
port_name = read_sys(path, "port_name")
driver_name = read_sys(path, "driver_name")
[_, type_letter] = Regex.run(@driver_regex, driver_name)
type = case type_letter do
"l" -> :large
"m" -> :medium
end
motor = %Device{class: :motor,
path: path,
port: port_name,
type: type}
count_per_rot = get_attribute(motor, "count_per_rot", :integer)
commands = get_attribute(motor, "commands", :list)
stop_commands = get_attribute(motor, "stop_commands", :list)
%Device{motor | props: %{count_per_rot: count_per_rot,
commands: commands,
stop_commands: stop_commands,
controls: Map.put_new(get_sys_controls(motor),
:speed_mode,
nil)}}
end

defp get_sys_controls(motor) do
%{polarity: get_attribute(motor, "polarity", :atom),
speed: get_attribute(motor, "speed_sp", :integer), # in counts/sec,
duty_cycle: get_attribute(motor, "duty_cycle_sp", :integer),
ramp_up: get_attribute(motor, "ramp_up_sp", :integer),
ramp_down: get_attribute(motor, "ramp_down_sp", :integer),
position: get_attribute(motor, "position_sp", :integer), # in counts,
time: get_attribute(motor, "time_sp", :integer),
speed_regulation: get_attribute(motor, "speed_regulation", :atom)}
end

end

12 changes: 4 additions & 8 deletions lib/ev3/body/lego_sensor.ex
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ defmodule Ev3.LegoSensor do

@doc "Get the currently connected lego sensors"
def sensors() do
if !testing?() do
if !Ev3.testing?() do
File.ls!(@sys_path)
|> Enum.filter(&(String.starts_with?(&1, @prefix)))
|> Enum.map(&(init_sensor("#{@sys_path}/#{&1}")))
Expand All @@ -21,12 +21,8 @@ defmodule Ev3.LegoSensor do
end
end

defp testing?() do
Application.get_env(:ev3, :mock)
end

defp dispatch(sensor) do
if !testing?() do
if !Ev3.testing?() do
case sensor.type do
:touch -> Ev3.TouchSensor
:color -> Ev3.ColorSensor
Expand Down Expand Up @@ -64,8 +60,8 @@ defmodule Ev3.LegoSensor do
end

@doc "Get the resolution of a sensor (the delta between essentially identical readings). Nil or an integer."
def sensitivity(sensor) do
apply(dispatch(sensor), :sensitivity, [sensor])
def sensitivity(sensor, sense) do
apply(dispatch(sensor), :sensitivity, [sensor, sense])
end

@doc "Is this the ultrasonic sensor?"
Expand Down
2 changes: 1 addition & 1 deletion lib/ev3/body/mock/mock_color_sensor.ex
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ defmodule Ev3.Mock.ColorSensor do
500
end

def sensitivity(sensor) do
def sensitivity(_sensor, _sense) do
nil
end

Expand Down
25 changes: 23 additions & 2 deletions lib/ev3/body/mock/mock_infrared_sensor.ex
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ defmodule Ev3.Mock.InfraredSensor do

def senses(_) do
beacon_senses = Enum.map(1.. @max_beacon_channels,
&([{:beacon_heading, &1}, {:beacon_distance, &1}, {:beacon_on, &1}]))
&([{:beacon_heading, &1}, {:beacon_distance, &1}, {:beacon_on, &1}, {:remote_buttons, &1}]))
|> List.flatten()
[:proximity | beacon_senses]
end
Expand All @@ -23,6 +23,10 @@ defmodule Ev3.Mock.InfraredSensor do
proximity(sensor)
end

def read(sensor, {:remote_buttons, channel}) do
remote_buttons(sensor, channel)
end

def read(sensor, {beacon_sense, channel}) do
case beacon_sense do
:beacon_heading -> seek_heading(sensor, channel)
Expand All @@ -35,7 +39,7 @@ defmodule Ev3.Mock.InfraredSensor do
500
end

def sensitivity(sensor) do
def sensitivity(_sensor, _sense) do
nil
end

Expand Down Expand Up @@ -65,5 +69,22 @@ defmodule Ev3.Mock.InfraredSensor do
value = :random.uniform(2) == 2
{value, sensor}
end

defp remote_buttons(sensor, _channel) do
value = case :random.uniform(12) - 1 do
1 -> %{red: :up, blue: nil}
2 -> %{red: :down, blue: nil}
3 -> %{red: nil, blue: :up}
4 -> %{red: nil, blue: :down}
5 -> %{red: :up, blue: :up}
6 -> %{red: :up, blue: :down}
7 -> %{red: :down, blue: :up}
8 -> %{red: :down, blue: :down}
10 -> %{red: :up_down, blue: nil}
11 -> %{red: nil, blue: :up_down}
_ -> %{red: nil, blue: nil} # 0 or 9
end
{value, sensor}
end

end
62 changes: 62 additions & 0 deletions lib/ev3/body/mock/mock_tacho_motor.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
defmodule Ev3.Mock.Tachomotor do
@moduledoc "A mock large tachomotor"

@behaviour Ev3.Sensing

def new(type) do
%Ev3.Device{class: :motor,
path: "/mock/#{type}_tachomotor",
type: type}
end

# Sensing

def senses(_) do
[:speed, :position, :duty_cycle, :run_status]
end

def read(motor, sense) do
case sense do
:speed -> current_speed(motor)
:position -> current_position(motor)
:duty_cycle -> current_duty_cycle(motor)
:run_status -> current_run_status(motor)
end
end

def pause(_) do
500
end

def sensitivity(_motor, _sense) do
nil
end

### PRIVATE

defp current_speed(motor) do
value = :random.uniform() * :random.uniform(10)
{value, motor}
end

defp current_position(motor) do
value = :random.uniform(200) - 100
{value, motor}
end

defp current_duty_cycle(motor) do
value = :random.uniform(100)
{value, motor}
end

defp current_run_status(motor) do
value = case :random.uniform(3) do
0 -> :running
1 -> :stopped
2 -> :stalled
3 -> :holding
end
{value, motor}
end

end
2 changes: 1 addition & 1 deletion lib/ev3/body/mock/mock_touch_sensor.ex
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ defmodule Ev3.Mock.TouchSensor do
500
end

def sensitivity(sensor) do
def sensitivity(_sensor, _sense) do
nil
end

Expand Down
2 changes: 1 addition & 1 deletion lib/ev3/body/sensing.ex
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ defmodule Ev3.Sensing do
defcallback pause(sensor :: %Ev3.Device{}) :: integer

@doc "Get the sensitivity of the device; the change in value to be noticed"
defcallback sensitivity(sensor :: %Ev3.Device{}) :: integer | nil
defcallback sensitivity(sensor :: %Ev3.Device{}, sense :: any) :: integer | nil

end

Loading

0 comments on commit 08251a2

Please sign in to comment.