Blah blah blah the leds work, shows IP, speed game shows lights
This commit is contained in:
parent
94675c3dcc
commit
8abff10929
10 changed files with 491 additions and 55 deletions
|
@ -5,6 +5,8 @@
|
|||
# is restricted to this project.
|
||||
import Config
|
||||
|
||||
button_pins = {24, 25, 5, 6}
|
||||
|
||||
config :ex_speed_game,
|
||||
target: Mix.target(),
|
||||
|
||||
|
@ -16,20 +18,40 @@ config :ex_speed_game,
|
|||
|
||||
# Pins of the buttons, in order from left to right in the case. Leftmost will be 0, and number
|
||||
# will increase to the right.
|
||||
button_pins: [17, 27, 22, 23],
|
||||
button_pins: button_pins,
|
||||
|
||||
# Pins of the LED lights of the buttons.
|
||||
button_light_pins: [],
|
||||
# Pins of the LED lights of the buttons mapped to the button pins.
|
||||
button_light_pins: {
|
||||
{17, elem(button_pins, 0)},
|
||||
{27, elem(button_pins, 1)},
|
||||
{22, elem(button_pins, 2)},
|
||||
{23, elem(button_pins, 3)}
|
||||
},
|
||||
|
||||
# Debounce delay, i.e. how long a button must be high or low before it is accepted, to reduce
|
||||
# spurious inputs. In milliseconds.
|
||||
debounce_delay: 20,
|
||||
|
||||
# Delay at start of game between beeps, in milliseconds.
|
||||
# Delay at start of game between ticks, in milliseconds.
|
||||
delay_start: 570,
|
||||
|
||||
# Maximum amount of beeps you can be "behind" before the game is stopped.
|
||||
max_waiting: 20
|
||||
# Delay at start of pro game
|
||||
delay_pro: 321,
|
||||
|
||||
# Score at start of pro game
|
||||
score_pro: 100,
|
||||
|
||||
# Maximum amount of ticks you can be "behind" before the game is stopped.
|
||||
max_waiting: 20,
|
||||
|
||||
# Interface to show IP for when using ShowIP
|
||||
iface: "usb0",
|
||||
|
||||
# LED pattern to show when there is a crash
|
||||
crash_pattern: {false, true, true, false},
|
||||
|
||||
# Time to show crash pattern for
|
||||
crash_pattern_delay: 3_000
|
||||
|
||||
# Customize non-Elixir parts of the firmware. See
|
||||
# https://hexdocs.pm/nerves/advanced-configuration.html for details.
|
||||
|
|
|
@ -5,6 +5,14 @@ defmodule ExSpeedGame.Application do
|
|||
|
||||
use Application
|
||||
|
||||
alias ExSpeedGame.Game.{
|
||||
ButtonInput,
|
||||
Lights,
|
||||
Menu
|
||||
}
|
||||
|
||||
alias ExSpeedGame.Game.Supervisor, as: GameSupervisor
|
||||
|
||||
def start(_type, _args) do
|
||||
# See https://hexdocs.pm/elixir/Supervisor.html
|
||||
# for other strategies and supported options
|
||||
|
@ -31,14 +39,17 @@ defmodule ExSpeedGame.Application do
|
|||
|
||||
def children(_target) do
|
||||
pins = Application.get_env(:ex_speed_game, :button_pins)
|
||||
led_pins = Application.get_env(:ex_speed_game, :button_light_pins)
|
||||
debounce_delay = Application.get_env(:ex_speed_game, :debounce_delay)
|
||||
|
||||
[
|
||||
# Children for all targets except host
|
||||
# Starts a worker by calling: ExSpeedGame.Worker.start_link(arg)
|
||||
{ExSpeedGame.Game.ButtonInput,
|
||||
{{pins, debounce_delay}, name: ExSpeedGame.Game.ButtonInput}},
|
||||
{ExSpeedGame.Test, []}
|
||||
{ButtonInput,
|
||||
%ButtonInput.Options{pins: pins, debounce_delay: debounce_delay, name: ButtonInput}},
|
||||
{Lights, %Lights.Options{light_pins: led_pins, name: Lights}},
|
||||
{Menu, %Menu.Options{name: Menu}},
|
||||
{DynamicSupervisor, strategy: :one_for_one, name: GameSupervisor}
|
||||
]
|
||||
end
|
||||
|
||||
|
|
|
@ -3,6 +3,16 @@ defmodule ExSpeedGame.Game.ButtonInput do
|
|||
alias ExSpeedGame.Game.Types
|
||||
require Logger
|
||||
|
||||
defmodule Options do
|
||||
@type t :: %__MODULE__{
|
||||
pins: Types.pins(),
|
||||
debounce_delay: integer(),
|
||||
name: GenServer.name()
|
||||
}
|
||||
@enforce_keys [:pins, :debounce_delay, :name]
|
||||
defstruct [:pins, :debounce_delay, :name]
|
||||
end
|
||||
|
||||
defmodule State do
|
||||
@type t :: %__MODULE__{
|
||||
pins: Types.pins(),
|
||||
|
@ -23,16 +33,20 @@ defmodule ExSpeedGame.Game.ButtonInput do
|
|||
|
||||
### SERVER INTERFACE
|
||||
|
||||
@spec start_link({{[Types.pins()], integer()}, keyword()}) ::
|
||||
:ignore | {:error, any} | {:ok, pid}
|
||||
def start_link({init, opts}) do
|
||||
GenServer.start_link(__MODULE__, init, opts)
|
||||
@spec start_link(Options.t()) :: :ignore | {:error, any} | {:ok, pid}
|
||||
def start_link(%Options{} = opts) do
|
||||
GenServer.start_link(
|
||||
__MODULE__,
|
||||
%{pins: opts.pins, debounce_delay: opts.debounce_delay},
|
||||
name: opts.name
|
||||
)
|
||||
end
|
||||
|
||||
@impl true
|
||||
@spec init({Types.pins(), integer()}) :: {:ok, State.t()}
|
||||
def init({pins, debounce_delay}) do
|
||||
{:ok, %State{pins: pins, debounce_delay: debounce_delay}}
|
||||
@spec init(map()) :: {:ok, State.t()}
|
||||
def init(%{pins: pins, debounce_delay: debounce_delay}) do
|
||||
pin_refs = for pin <- Tuple.to_list(pins), do: init_pin(pin)
|
||||
{:ok, %State{pins: pins, debounce_delay: debounce_delay, pin_refs: pin_refs}}
|
||||
end
|
||||
|
||||
@impl true
|
||||
|
@ -42,9 +56,7 @@ defmodule ExSpeedGame.Game.ButtonInput do
|
|||
|
||||
@spec handle_call(:acquire, GenServer.from(), %State{active: false}) :: {:reply, :ok, State.t()}
|
||||
def handle_call(:acquire, {from, _}, %State{active: false} = state) do
|
||||
pin_refs = for pin <- state.pins, do: init_pin(pin)
|
||||
|
||||
{:reply, :ok, %State{state | listener: from, pin_refs: pin_refs, active: true}}
|
||||
{:reply, :ok, %State{state | listener: from, active: true}}
|
||||
end
|
||||
|
||||
@spec handle_call(:acquire, any, %State{active: true}) :: no_return()
|
||||
|
@ -56,13 +68,12 @@ defmodule ExSpeedGame.Game.ButtonInput do
|
|||
|
||||
@spec handle_call(:release, any, %State{active: true}) :: {:reply, :ok, State.t()}
|
||||
def handle_call(:release, _from, %State{active: true} = state) do
|
||||
# Circuits.GPIO will free resources automatically when the pin refs are GCd
|
||||
{:reply, :ok, %State{state | listener: nil, pin_refs: %{}}}
|
||||
{:reply, :ok, %State{state | listener: nil, active: false}}
|
||||
end
|
||||
|
||||
@spec handle_call(:release, any, %State{active: false}) :: no_return()
|
||||
def handle_call(:release, _from, %State{active: false}) do
|
||||
raise InputError, message: "ButtonInput already released"
|
||||
@spec handle_call(:release, any, %State{active: false}) :: {:reply, :ok, State.t()}
|
||||
def handle_call(:release, _from, %State{active: false} = state) do
|
||||
{:reply, :ok, state}
|
||||
end
|
||||
|
||||
@impl true
|
||||
|
@ -81,10 +92,11 @@ defmodule ExSpeedGame.Game.ButtonInput do
|
|||
|
||||
pin_timers =
|
||||
case value do
|
||||
0 ->
|
||||
# Note that due to pullup, the values here are reversed
|
||||
1 ->
|
||||
Map.delete(state.pin_timers, pin)
|
||||
|
||||
1 ->
|
||||
0 ->
|
||||
ref = Process.send_after(self(), {:debounce, pin}, state.debounce_delay)
|
||||
Map.put(state.pin_timers, pin, ref)
|
||||
end
|
||||
|
@ -108,7 +120,7 @@ defmodule ExSpeedGame.Game.ButtonInput do
|
|||
|
||||
@spec init_pin(Types.pin()) :: reference()
|
||||
defp init_pin(pin) do
|
||||
{:ok, ref} = Circuits.GPIO.open(pin, :input, pull_mode: :pulldown)
|
||||
{:ok, ref} = Circuits.GPIO.open(pin, :input, pull_mode: :pullup)
|
||||
:ok = Circuits.GPIO.set_interrupts(ref, :both)
|
||||
ref
|
||||
end
|
||||
|
|
115
lib/game/lights.ex
Normal file
115
lib/game/lights.ex
Normal file
|
@ -0,0 +1,115 @@
|
|||
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
|
130
lib/game/menu.ex
Normal file
130
lib/game/menu.ex
Normal file
|
@ -0,0 +1,130 @@
|
|||
defmodule ExSpeedGame.Game.Menu do
|
||||
use GenServer
|
||||
require Logger
|
||||
alias ExSpeedGame.Game.{Types, ButtonInput, ShowIP, Lights}
|
||||
|
||||
alias ExSpeedGame.Game.Modes.{
|
||||
Speed
|
||||
}
|
||||
|
||||
@menu {
|
||||
{"SpeedGame", Speed,
|
||||
%Speed.Options{
|
||||
initial_score: 0,
|
||||
initial_delay: Application.get_env(:ex_speed_game, :delay_start)
|
||||
}},
|
||||
{"SpeedGame Pro", Speed,
|
||||
%Speed.Options{
|
||||
initial_score: Application.get_env(:ex_speed_game, :score_pro),
|
||||
initial_delay: Application.get_env(:ex_speed_game, :delay_pro)
|
||||
}},
|
||||
{"MemoryGame", Speed, %{}},
|
||||
{"Show IP", ShowIP, %{}}
|
||||
}
|
||||
@menu_size :erlang.tuple_size(@menu)
|
||||
|
||||
defmodule Options do
|
||||
@type t :: %__MODULE__{
|
||||
name: GenServer.name()
|
||||
}
|
||||
@enforce_keys [:name]
|
||||
defstruct [:name]
|
||||
end
|
||||
|
||||
defmodule State do
|
||||
@type mode :: :menu | :ingame
|
||||
@type t :: %__MODULE__{
|
||||
index: integer(),
|
||||
button_pins: Types.pins(),
|
||||
mode: mode(),
|
||||
game: pid(),
|
||||
game_ref: reference()
|
||||
}
|
||||
@enforce_keys [:button_pins]
|
||||
defstruct [:button_pins, :game, :game_ref, index: 0, mode: :menu]
|
||||
end
|
||||
|
||||
@spec start_link(Options.t()) :: :ignore | {:error, any} | {:ok, pid}
|
||||
def start_link(%Options{} = opts) do
|
||||
GenServer.start_link(
|
||||
__MODULE__,
|
||||
%{},
|
||||
name: opts.name
|
||||
)
|
||||
end
|
||||
|
||||
@impl true
|
||||
@spec init(any) :: {:ok, State.t()}
|
||||
def init(_) do
|
||||
state = %State{button_pins: Application.get_env(:ex_speed_game, :button_pins)}
|
||||
ButtonInput.acquire(ButtonInput)
|
||||
display_index(state.index)
|
||||
{:ok, state}
|
||||
end
|
||||
|
||||
@impl true
|
||||
def handle_info(msg, state)
|
||||
|
||||
@spec handle_info({:input, Types.pin()}, State.t()) :: {:noreply, State.t()}
|
||||
def handle_info(
|
||||
{:input, btn_pin},
|
||||
%State{index: index, button_pins: {pin1, _, _, pin4}, mode: :menu} = state
|
||||
)
|
||||
when btn_pin in [pin1, pin4] do
|
||||
is_first_button = btn_pin == pin1
|
||||
is_last_button = btn_pin == pin4
|
||||
|
||||
new_index =
|
||||
cond do
|
||||
is_first_button and index > 0 -> index - 1
|
||||
is_last_button and index < @menu_size - 1 -> index + 1
|
||||
true -> index
|
||||
end
|
||||
|
||||
display_index(new_index)
|
||||
{:noreply, %State{state | index: new_index}}
|
||||
end
|
||||
|
||||
@spec handle_info({:input, Types.pin()}, State.t()) :: {:noreply, State.t()}
|
||||
def handle_info({:input, _pin}, %State{index: index, mode: :menu} = state) do
|
||||
ButtonInput.release(ButtonInput)
|
||||
|
||||
{_, module, init_arg} = elem(@menu, index)
|
||||
|
||||
{:ok, pid} =
|
||||
DynamicSupervisor.start_child(ExSpeedGame.Game.Supervisor, module.child_spec(init_arg))
|
||||
|
||||
ref = Process.monitor(pid)
|
||||
|
||||
{:noreply, %State{state | mode: :ingame, game: pid, game_ref: ref}}
|
||||
end
|
||||
|
||||
@spec handle_info({:DOWN, reference(), :process, any, any}, State.t()) :: {:noreply, State.t()}
|
||||
def handle_info(
|
||||
{:DOWN, ref, :process, _, reason},
|
||||
%State{
|
||||
index: index,
|
||||
mode: :ingame,
|
||||
game_ref: game_ref
|
||||
} = state
|
||||
)
|
||||
when ref == game_ref do
|
||||
# Ensure input is released in case process crashed violently
|
||||
ButtonInput.release(ButtonInput)
|
||||
|
||||
# If crashed, show crash pattern for a moment
|
||||
if reason != :normal do
|
||||
Lights.set(Lights, Application.get_env(:ex_speed_game, :crash_pattern))
|
||||
Process.sleep(Application.get_env(:ex_speed_game, :crash_pattern_delay))
|
||||
end
|
||||
|
||||
ButtonInput.acquire(ButtonInput)
|
||||
display_index(index)
|
||||
{:noreply, %State{state | mode: :menu, game: nil, game_ref: nil}}
|
||||
end
|
||||
|
||||
defp display_index(index) do
|
||||
Logger.debug("Menu viewing: #{@menu |> elem(index) |> elem(0)}")
|
||||
Lights.show_binary(Lights, index + 1)
|
||||
end
|
||||
end
|
81
lib/game/modes/speed.ex
Normal file
81
lib/game/modes/speed.ex
Normal file
|
@ -0,0 +1,81 @@
|
|||
defmodule ExSpeedGame.Game.Modes.Speed do
|
||||
use GenServer, restart: :temporary
|
||||
require Logger
|
||||
alias ExSpeedGame.Game.{Types, Lights, ButtonInput, Randomiser}
|
||||
|
||||
defmodule Options do
|
||||
@type t :: %__MODULE__{
|
||||
initial_score: integer(),
|
||||
initial_delay: integer()
|
||||
}
|
||||
@enforce_keys [:initial_score, :initial_delay]
|
||||
defstruct [:initial_score, :initial_delay]
|
||||
end
|
||||
|
||||
defmodule State do
|
||||
@type t :: %__MODULE__{
|
||||
score: integer(),
|
||||
delay: float(),
|
||||
queue: [Types.choice()],
|
||||
previous: Types.choice()
|
||||
}
|
||||
@enforce_keys [:score, :delay]
|
||||
defstruct [:score, :delay, queue: [], previous: nil]
|
||||
end
|
||||
|
||||
@spec start_link(Options.t()) :: :ignore | {:error, any} | {:ok, pid}
|
||||
def start_link(%Options{} = opts) do
|
||||
GenServer.start_link(__MODULE__, %{
|
||||
initial_score: opts.initial_score,
|
||||
initial_delay: opts.initial_delay
|
||||
})
|
||||
end
|
||||
|
||||
@impl true
|
||||
@spec init(map()) :: {:ok, State.t()}
|
||||
def init(%{initial_score: score, initial_delay: delay}) do
|
||||
ButtonInput.acquire(ButtonInput)
|
||||
Lights.clear(Lights)
|
||||
Logger.debug("Init game with score #{score} and delay #{delay}.")
|
||||
|
||||
next_delay = schedule_tick(delay)
|
||||
|
||||
{:ok, %State{score: score, delay: next_delay}}
|
||||
end
|
||||
|
||||
@impl true
|
||||
def handle_info(:tick, %State{delay: delay, previous: previous} = state) do
|
||||
Logger.debug("Tick #{delay}…")
|
||||
|
||||
choice = Randomiser.get(previous)
|
||||
Lights.set_index(Lights, choice)
|
||||
|
||||
next_delay = schedule_tick(delay)
|
||||
{:noreply, %State{state | delay: next_delay, previous: choice}}
|
||||
end
|
||||
|
||||
@impl true
|
||||
def handle_info({:input, _}, state) do
|
||||
{:stop, :normal, state}
|
||||
end
|
||||
|
||||
@impl true
|
||||
@spec terminate(any(), any()) :: term()
|
||||
def terminate(_reason, _state) do
|
||||
ButtonInput.release(ButtonInput)
|
||||
end
|
||||
|
||||
@spec schedule_tick(float()) :: float()
|
||||
defp schedule_tick(delay) do
|
||||
Process.send_after(self(), :tick, trunc(delay))
|
||||
get_next_delay(delay)
|
||||
end
|
||||
|
||||
@spec get_next_delay(float()) :: float()
|
||||
defp get_next_delay(delay)
|
||||
defp get_next_delay(delay) when delay > 399, do: delay * 0.993
|
||||
defp get_next_delay(delay) when delay > 326, do: delay * 0.996
|
||||
defp get_next_delay(delay) when delay > 192, do: delay * 0.9985
|
||||
defp get_next_delay(delay) when delay > 1, do: delay - 1
|
||||
defp get_next_delay(delay), do: delay
|
||||
end
|
15
lib/game/randomiser.ex
Normal file
15
lib/game/randomiser.ex
Normal file
|
@ -0,0 +1,15 @@
|
|||
defmodule ExSpeedGame.Game.Randomiser do
|
||||
alias ExSpeedGame.Game.Types
|
||||
|
||||
@choices 1..4
|
||||
|
||||
@doc """
|
||||
Get a random button choice that is not the same as the given previous choice.
|
||||
|
||||
If previous given is nil, any choice can be returned.
|
||||
"""
|
||||
@spec get(Types.choice()) :: Types.choice()
|
||||
def get(previous) do
|
||||
@choices |> Enum.filter(&(&1 != previous)) |> Enum.random()
|
||||
end
|
||||
end
|
70
lib/game/show_ip.ex
Normal file
70
lib/game/show_ip.ex
Normal file
|
@ -0,0 +1,70 @@
|
|||
defmodule ExSpeedGame.Game.ShowIP do
|
||||
use GenServer, restart: :temporary
|
||||
require Logger
|
||||
|
||||
defmodule State do
|
||||
@type t :: %__MODULE__{
|
||||
ip: String.t(),
|
||||
index: integer()
|
||||
}
|
||||
@enforce_keys [:ip]
|
||||
defstruct [:ip, index: 0]
|
||||
end
|
||||
|
||||
@spec start_link(any) :: :ignore | {:error, any} | {:ok, pid}
|
||||
def start_link(_) do
|
||||
GenServer.start_link(__MODULE__, nil)
|
||||
end
|
||||
|
||||
@impl true
|
||||
@spec init(any) :: {:ok, State.t()}
|
||||
def init(_) do
|
||||
ExSpeedGame.Game.ButtonInput.acquire(ExSpeedGame.Game.ButtonInput)
|
||||
|
||||
{:ok, ifaddrs} = :inet.getifaddrs()
|
||||
|
||||
{_, ifaddr} =
|
||||
List.keyfind(
|
||||
ifaddrs,
|
||||
String.to_charlist(Application.get_env(:ex_speed_game, :iface)),
|
||||
0,
|
||||
[]
|
||||
)
|
||||
|
||||
ip = Keyword.get(ifaddr, :addr, {0, 0, 0, 0}) |> :inet.ntoa() |> to_string() |> Kernel.<>(".")
|
||||
Logger.debug("Showing IP: #{inspect(ip)}")
|
||||
|
||||
state = %State{ip: ip}
|
||||
display_number(state.ip, state.index)
|
||||
{:ok, state}
|
||||
end
|
||||
|
||||
@impl true
|
||||
def handle_info({:input, _pin}, %State{ip: ip, index: index} = state) do
|
||||
if index == String.length(ip) - 1 do
|
||||
{:stop, :normal, state}
|
||||
else
|
||||
index = index + 1
|
||||
display_number(ip, index)
|
||||
{:noreply, %State{state | index: index}}
|
||||
end
|
||||
end
|
||||
|
||||
@impl true
|
||||
def terminate(_reason, _state) do
|
||||
ExSpeedGame.Game.ButtonInput.release(ExSpeedGame.Game.ButtonInput)
|
||||
end
|
||||
|
||||
defp display_number(ip, index) do
|
||||
server = ExSpeedGame.Game.Lights
|
||||
|
||||
case String.at(ip, index) do
|
||||
"." ->
|
||||
ExSpeedGame.Game.Lights.clear(server)
|
||||
|
||||
int ->
|
||||
{num, ""} = Integer.parse(int)
|
||||
ExSpeedGame.Game.Lights.show_binary(server, num)
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,5 +1,11 @@
|
|||
defmodule ExSpeedGame.Game.Types do
|
||||
@type pin :: Circuits.GPIO.pin_number()
|
||||
@type pins :: [pin]
|
||||
@type led_pins :: [pin]
|
||||
@type pins :: {pin(), pin(), pin(), pin()}
|
||||
@type led_pins :: {
|
||||
{pin(), pin()},
|
||||
{pin(), pin()},
|
||||
{pin(), pin()},
|
||||
{pin(), pin()}
|
||||
}
|
||||
@type choice :: 1..4
|
||||
end
|
||||
|
|
26
lib/test.ex
26
lib/test.ex
|
@ -1,26 +0,0 @@
|
|||
defmodule ExSpeedGame.Test do
|
||||
use GenServer
|
||||
require Logger
|
||||
|
||||
def start_link(opts) do
|
||||
GenServer.start_link(__MODULE__, opts)
|
||||
end
|
||||
|
||||
@impl true
|
||||
def init(_) do
|
||||
ExSpeedGame.Game.ButtonInput.acquire(ExSpeedGame.Game.ButtonInput)
|
||||
{:ok, ref} = Circuits.GPIO.open(16, :output, initial_value: 0)
|
||||
{:ok, {ref, false}}
|
||||
end
|
||||
|
||||
@impl true
|
||||
def handle_info(msg, {ref, state}) do
|
||||
Logger.debug(inspect(msg))
|
||||
|
||||
new_state = not state
|
||||
|
||||
Circuits.GPIO.write(ref, if(new_state, do: 1, else: 0))
|
||||
|
||||
{:noreply, {ref, new_state}}
|
||||
end
|
||||
end
|
Loading…
Reference in a new issue