115 lines
2.9 KiB
Elixir
115 lines
2.9 KiB
Elixir
defmodule ExSpeedGame.Game.Lights do
|
|
use GenServer
|
|
use Bitwise, only_operators: true
|
|
alias ExSpeedGame.Game.Types
|
|
|
|
@type values :: {boolean(), boolean(), boolean(), boolean()}
|
|
|
|
defmodule Options do
|
|
@type t :: %__MODULE__{
|
|
light_pins: Types.led_pins(),
|
|
name: GenServer.name()
|
|
}
|
|
@enforce_keys [:light_pins, :name]
|
|
defstruct [:light_pins, :name]
|
|
end
|
|
|
|
defmodule State do
|
|
@type t :: %__MODULE__{
|
|
lights: {Types.pin(), Types.pin(), Types.pin(), Types.pin()},
|
|
references: [reference()]
|
|
}
|
|
defstruct [:lights, :references]
|
|
end
|
|
|
|
### SERVER INTERFACE
|
|
|
|
@spec start_link(Options.t()) :: :ignore | {:error, any} | {:ok, pid}
|
|
def start_link(%Options{} = opts) do
|
|
{{pin1, _}, {pin2, _}, {pin3, _}, {pin4, _}} = opts.light_pins
|
|
|
|
GenServer.start_link(
|
|
__MODULE__,
|
|
%{pins: {pin1, pin2, pin3, pin4}},
|
|
name: opts.name
|
|
)
|
|
end
|
|
|
|
@impl true
|
|
@spec init(%{pins: {Types.pin(), Types.pin(), Types.pin(), Types.pin()}}) :: {:ok, State.t()}
|
|
def init(%{pins: pins}) do
|
|
references =
|
|
for pin <- Tuple.to_list(pins) do
|
|
{:ok, ref} = Circuits.GPIO.open(pin, :output, initial_value: 0)
|
|
ref
|
|
end
|
|
|
|
{:ok, %State{lights: pins, references: references}}
|
|
end
|
|
|
|
@impl true
|
|
@spec handle_call(
|
|
{:set_lights, values()},
|
|
GenServer.from(),
|
|
State.t()
|
|
) :: {:reply, :ok, State.t()}
|
|
def handle_call({:set_lights, values}, _from, %State{references: references} = state) do
|
|
references
|
|
|> Enum.with_index()
|
|
|> Enum.each(fn {ref, i} ->
|
|
Circuits.GPIO.write(ref, elem(values, i) |> bool2val())
|
|
end)
|
|
|
|
{:reply, :ok, state}
|
|
end
|
|
|
|
@spec bool2val(boolean()) :: Circuits.GPIO.value()
|
|
defp bool2val(bool)
|
|
defp bool2val(true), do: 1
|
|
defp bool2val(false), do: 0
|
|
|
|
### CLIENT INTERFACE
|
|
|
|
@doc """
|
|
Set the lights on/off according to the given value.
|
|
"""
|
|
@spec set(GenServer.name(), values()) :: :ok | no_return()
|
|
def set(server, values) do
|
|
:ok = GenServer.call(server, {:set_lights, values})
|
|
end
|
|
|
|
@doc """
|
|
Turn off all lights.
|
|
"""
|
|
@spec clear(GenServer.name()) :: :ok | no_return()
|
|
def clear(server) do
|
|
set(server, {false, false, false, false})
|
|
end
|
|
|
|
@doc """
|
|
Show the given number on the lights as a binary pattern.
|
|
"""
|
|
@spec show_binary(GenServer.name(), integer()) :: :ok | no_return()
|
|
def show_binary(server, number) do
|
|
values = number2binary(number)
|
|
set(server, values)
|
|
end
|
|
|
|
@doc """
|
|
Set the given index on and the others off.
|
|
"""
|
|
@spec set_index(GenServer.name(), integer()) :: :ok | no_return()
|
|
def set_index(server, index) do
|
|
set(server, {index == 1, index == 2, index == 3, index == 4})
|
|
end
|
|
|
|
@spec number2binary(integer()) :: values()
|
|
defp number2binary(number) do
|
|
{
|
|
(number &&& 0b1000) != 0,
|
|
(number &&& 0b0100) != 0,
|
|
(number &&& 0b0010) != 0,
|
|
(number &&& 0b0001) != 0
|
|
}
|
|
end
|
|
end
|