116 lines
2.9 KiB
Elixir
116 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
|