Current section

Files

Jump to
nerves_pack lib nerves_pack wifi_wizard_button.ex
Raw

lib/nerves_pack/wifi_wizard_button.ex

defmodule NervesPack.WiFiWizardButton do
use GenServer
@moduledoc """
Starts the wizard if a button is depressed for long enough.
**Note:** Using this requires `Circuits.GPIO` be included as a dependency in
your project:
```elixir
def deps() do
{:circuits_gpio, "~> 0.4"}
end
```
It is recommended that you start this in your own supverision separate from
NervesPack. This module mainly serves as a convenience and example for simple
management of `VintageNetWizard`:
```elixir
def start(_type, _args) do
children = [
NervesPack.WiFiWizardButton
...
]
opts = [strategy: :one_for_one, name: MyApp.Supervisor]
Supervisor.start_link(children, opts)
end
```
Though you can also enable this in the config as well which will start it
within `NervesPack.Supervisor` instead:
```
config :nerves_pack, wifi_wizard_button: true
```
GPIO 26 is used for the button and the hold time is 5 seconds.
These defaults can be configured when adding as a supervised child or in the
config if desired:
```
# Supervised child
children = [
{NervesPack.WiFiWizardButton, [pin: 12, hold: 4_000]},
...
]
# config.exs
config :nerves_pack,
wifi_wizard_button_pin: 17,
wifi_wizard_button_hold: 3_000
```
"""
alias Circuits.GPIO
require Logger
@default_hold 5_000
@default_pin 26
@doc """
Start the button monitor
"""
@spec start_link(list()) :: GenServer.on_start()
def start_link(opts \\ []) do
if Code.ensure_compiled?(GPIO) do
GenServer.start_link(__MODULE__, opts, name: __MODULE__)
else
_ =
Logger.warn("""
[NervesPack] - Skipping WiFiWizardButton: add {:circuits_gpio, "~> 0.4"} to your dependencies to use
""")
:ignore
end
end
@impl true
def init(opts) do
gpio_pin =
opts[:pin] || Application.get_env(:nerves_pack, :wifi_wizard_button_pin, @default_pin)
{:ok, gpio} = GPIO.open(gpio_pin, :input)
:ok = GPIO.set_interrupts(gpio, :both)
hold =
opts[:hold] || Application.get_env(:nerves_pack, :wifi_wizard_button_hold, @default_hold)
hold = validate_timeout(hold)
_ =
Logger.info("""
[NervesPack] WiFi Wizard can be started any time by pressing down the button
on GPIO #{gpio_pin} for #{hold / 1000} seconds.
If no button is connected, you can manually mock a "press" by connecting the
pin to 3.3v power for the required time with a cable.
""")
{:ok, %{hold: hold, pin: gpio_pin, gpio: gpio}}
end
@impl true
def handle_info({:circuits_gpio, gpio_pin, _timestamp, 1}, %{pin: gpio_pin} = state) do
# Button pressed. Start a timer to launch the wizard when it's long enough
{:noreply, state, state.hold}
end
@impl true
def handle_info({:circuits_gpio, gpio_pin, _timestamp, 0}, %{pin: gpio_pin} = state) do
# Button released. The GenServer timer is implicitly cancelled by receiving this message.
{:noreply, state}
end
@impl true
def handle_info(:timeout, state) do
# Timeout occurred before button released which means
# it was held for long enough
:ok = VintageNetWizard.run_wizard()
_ = Logger.info("[NervesPack] WiFi Wizard started...")
{:noreply, state}
end
defp validate_timeout(timeout) when is_integer(timeout), do: timeout
defp validate_timeout(timeout) do
_ =
Logger.warn(
"[NervesPack] Invalid button hold: #{inspect(timeout)}. Must be an integer in ms. Using default 5_000"
)
@default_hold
end
end