Current section

Files

Jump to
exred_node_picar lib front_wheels.ex
Raw

lib/front_wheels.ex

defmodule Exred.Node.Picar.FrontWheels do
require Logger
use GenServer
alias Exred.Node.Picar.PWM
@pwm_ch 0
@min_pulse_width 600
@max_pulse_width 2400
@default_pulse_width 1500
@frequency 60
@angle_offset -3
# API
#####################
def start_link(default) when is_list(default) do
GenServer.start_link(__MODULE__, default, name: __MODULE__)
end
def test, do: GenServer.call(__MODULE__, :test)
def test2, do: GenServer.call(__MODULE__, :test2)
def test3, do: GenServer.call(__MODULE__, :test3)
def left(angle), do: GenServer.call(__MODULE__, {:set_target, 90-angle})
def right(angle), do: GenServer.call(__MODULE__, {:set_target, 90+angle})
def straight, do: GenServer.call(__MODULE__, {:set_target, 90})
# Callbacks
#####################
@impl true
def init(_args) do
Logger.debug "Starting..."
state = %{
freq: 60,
current_angle: 90,
target_angle: 90
}
straight = angle_to_analog(90)
Logger.debug "Turning straight (#{straight})"
PWM.set(@pwm_ch, 0, angle_to_analog(state.current_angle))
:timer.sleep(1000)
{:ok, state, 200}
end
@impl true
def handle_info(:timeout, %{current_angle: ca, target_angle: ta} = state) do
# turn front wheel one step closer to target
if ta == ca do
{:noreply, state, 200}
else
# calculate the next step towards the target angle
next_angle = ca + (ta-ca)/abs(ta-ca) * 5
# if the next step is closer to target then one step then set it to target
# (one more step would overshoot)
new_ca = if abs(ta-next_angle) < 5 do
ta
else
next_angle
end
Logger.debug "angle set to: #{new_ca}"
PWM.set(@pwm_ch, 0, angle_to_analog(new_ca))
{:noreply, %{state | current_angle: new_ca}, 200}
end
end
@impl true
def handle_call({:set_target, angle}, _from, state) do
if angle>=45 and angle <=135 do
Logger.debug "Target angle set to #{angle}"
{:reply, :ok, %{state| target_angle: angle}, 200}
else
Logger.debug "Target angle #{angle} refused"
{:reply, :refused, state, 200}
end
end
def handle_call(:test, _from, state) do
straight = angle_to_analog(90)
left = angle_to_analog(70)
right = angle_to_analog(110)
Logger.debug "Turning straight (#{straight})"
PWM.set(@pwm_ch, 0, straight)
:timer.sleep(500)
Logger.debug "Turning left (#{left})"
PWM.set(@pwm_ch, 0, left)
:timer.sleep(500)
Logger.debug "Turning right (#{right})"
PWM.set(@pwm_ch, 0, right)
:timer.sleep(500)
Logger.debug "Turning straight (#{straight})"
PWM.set(@pwm_ch, 0, straight)
{:reply, :ok, state}
end
def handle_call(:test2, _from, state) do
straight = angle_to_analog(90)
Logger.debug "Turning straight (#{straight})"
PWM.set(@pwm_ch, 0, straight)
:timer.sleep(100)
Logger.debug "Done"
{:reply, :ok, state}
end
def handle_call(:test3, _from, state) do
straight = 90
left = 70
right = 110
Logger.debug "Turning straight (#{straight})"
PWM.set(@pwm_ch, 0, angle_to_analog(straight))
:timer.sleep(500)
Logger.debug "Turning left (#{left})"
for angle <- straight..left, &(rem(&1,5) == 0) do
PWM.set(@pwm_ch, 0, angle_to_analog(angle))
:timer.sleep(100)
end
for angle <- left..straight, &(rem(&1,5) == 0) do
PWM.set(@pwm_ch, 0, angle_to_analog(angle))
:timer.sleep(100)
end
Logger.debug "Turning right (#{right})"
for angle <- straight..right, &(rem(&1,5) == 0) do
PWM.set(@pwm_ch, 0, angle_to_analog(angle))
:timer.sleep(100)
end
for angle <- right..straight, &(rem(&1,5) == 0) do
PWM.set(@pwm_ch, 0, angle_to_analog(angle))
:timer.sleep(100)
end
Logger.debug "Turning straight (#{straight})"
PWM.set(@pwm_ch, 0, angle_to_analog(straight))
{:reply, :ok, state}
end
def angle_to_analog(angle) do
pulse_width = (angle + @angle_offset) / 180 * (@max_pulse_width - @min_pulse_width) + @min_pulse_width
analog_value = round(pulse_width / 1000000 * @frequency * 4096)
end
end