From a69264e6703c9486ee0dd0222accb126b0ade0e9 Mon Sep 17 00:00:00 2001 From: Jon Carstens Date: Sun, 12 May 2019 12:52:07 -0600 Subject: [PATCH] add input to state to support native store of keypresses --- CONFIGURATION.md | 15 +++++++++++--- lib/keypad.ex | 52 +++++++++++++++++++++++++++++++++++++++++++----- mix.exs | 4 ++-- mix.lock | 2 +- 4 files changed, 62 insertions(+), 11 deletions(-) diff --git a/CONFIGURATION.md b/CONFIGURATION.md index 13b6e02..26e2304 100644 --- a/CONFIGURATION.md +++ b/CONFIGURATION.md @@ -34,7 +34,10 @@ defmodule MyModule do use Keypad, row_pins: [5,6,7], col_pins: [22,23,24,25], size: "3x4" @impl true - def handle_keypress(key), do: IO.inspect(key, label: "KEYPRESS: ") + def handle_keypress(key, state) do + IO.inspect(key, label: "KEYPRESS: ") + state + end end ``` @@ -48,7 +51,10 @@ defmodule MyModule do ] @impl true - def handle_keypress(key), do: IO.inspect(key, label: "KEYPRESS: ") + def handle_keypress(key, state) do + IO.inspect(key, label: "KEYPRESS: ") + state + end end ``` @@ -63,6 +69,9 @@ defmodule MyModule do ] @impl true - def handle_keypress(key), do: IO.inspect(key, label: "KEYPRESS: ") + def handle_keypress(key, state) do + IO.inspect(key, label: "KEYPRESS: ") + state + end end ``` \ No newline at end of file diff --git a/lib/keypad.ex b/lib/keypad.ex index 61f6137..1abd994 100644 --- a/lib/keypad.ex +++ b/lib/keypad.ex @@ -26,7 +26,47 @@ defmodule Keypad do * `:col_pins` - List of integers which map to corresponding GPIO to as `OUTPUT` pins for keypad columns * defaults to `[5, 6, 13, 26]` """ - @callback handle_keypress(key :: any) :: any + + @doc """ + Required callback to handle keypress events based on defined matrix values. + + It's first argument will be the result of the keypress according to the defined matrix (most typically a + binary string, though you can use anything you'd like). The second argument is the state of the keypad + GenServer. You are required to return the state in this function. + + There is an optional field in the state called `:input` which is initialized as an empty string `""`. You can + use this to keep input events from keypresses and build them as needed, such as putting multiple keypresses + together to determine a password. **Note**: You will be responsible for resetting this input as needed. + + This is not required and you can optionally use other measures to keep rolling state, such as `Agent`. + + ```elixir + defmodule MyKeypad do + use Keypad + + require Logger + + @impl true + def handle_keypress(key, %{input: ""} = state) do + Logger.info("First Keypress: \#{key}") + Process.send_after(self(), :reset, 5000) # Reset input after 5 seconds + %{state | input: key} + end + + @impl true + def handle_keypress(key, %{input: input} = state) do + Logger.info("Keypress: \#{key}") + %{state | input: input <> key} + end + + @impl true + def handle_info(:reset, state) do + {:noreply, %{state | input: ""}} + end + end + ``` + """ + @callback handle_keypress(key :: any, map) :: map defmacro __using__(opts) do quote location: :keep, bind_quoted: [opts: opts] do @@ -36,7 +76,7 @@ defmodule Keypad do alias __MODULE__ defmodule State do - defstruct row_pins: [17, 27, 23, 24], col_pins: [5, 6, 13, 26], matrix: nil, size: nil, last_message_at: 0 + defstruct row_pins: [17, 27, 23, 24], col_pins: [5, 6, 13, 26], input: "", matrix: nil, size: nil, last_message_at: 0 end defguard valid_press(current, prev) when ((current - prev)/1.0e6) > 100 @@ -65,7 +105,7 @@ defmodule Keypad do def handle_info({:circuits_gpio, pin_num, time, 0}, %{last_message_at: prev} = state) when valid_press(time, prev) do {row_pin, row_index} = Stream.with_index(state.row_pins) |> Enum.find(fn {row, _i} -> Circuits.GPIO.pin(row) == pin_num end) - state.col_pins + val = state.col_pins |> Stream.with_index() |> Enum.reduce_while([], fn {col, col_index}, acc -> # Write the column pin HIGH then read the row pin again. @@ -79,13 +119,15 @@ defmodule Keypad do # We can use the row and column indexes as x,y of the matrix # to get which specific key character the press belongs to. val = Enum.at(state.matrix, row_index) |> Enum.at(col_index) - apply(__MODULE__, :handle_keypress, [val]) - {:halt, []} + + {:halt, val} 0 -> {:cont, []} end end) + state = apply(__MODULE__, :handle_keypress, [val, state]) + {:noreply, %{state | last_message_at: time}} end diff --git a/mix.exs b/mix.exs index 8afc55c..b97d7df 100644 --- a/mix.exs +++ b/mix.exs @@ -4,7 +4,7 @@ defmodule Keypad.MixProject do def project do [ app: :keypad, - version: "0.1.0", + version: "0.2.0", elixir: "~> 1.8", name: "Keypad", description: "A small library to interact with keypads connected to GPIO pins", @@ -35,7 +35,7 @@ defmodule Keypad.MixProject do defp deps do [ - {:circuits_gpio, "~> 0.3.0"}, + {:circuits_gpio, "~> 0.3"}, {:ex_doc, "~> 0.19", only: :dev, runtime: false} ] end diff --git a/mix.lock b/mix.lock index 7731371..76615f7 100644 --- a/mix.lock +++ b/mix.lock @@ -1,5 +1,5 @@ %{ - "circuits_gpio": {:hex, :circuits_gpio, "0.3.1", "bfbc01bba0de0d820ee5cc60593d809095f10922f65de8bde26795790d830250", [:make, :mix], [{:elixir_make, "~> 0.4", [hex: :elixir_make, repo: "hexpm", optional: false]}], "hexpm"}, + "circuits_gpio": {:hex, :circuits_gpio, "0.4.1", "344dd34f2517687fd28723e5552571babff5469db05b697181cab860fe7eff23", [:make, :mix], [{:elixir_make, "~> 0.5", [hex: :elixir_make, repo: "hexpm", optional: false]}], "hexpm"}, "earmark": {:hex, :earmark, "1.3.1", "73812f447f7a42358d3ba79283cfa3075a7580a3a2ed457616d6517ac3738cb9", [:mix], [], "hexpm"}, "elixir_make": {:hex, :elixir_make, "0.5.2", "96a28c79f5b8d34879cd95ebc04d2a0d678cfbbd3e74c43cb63a76adf0ee8054", [:mix], [], "hexpm"}, "ex_doc": {:hex, :ex_doc, "0.19.3", "3c7b0f02851f5fc13b040e8e925051452e41248f685e40250d7e40b07b9f8c10", [:mix], [{:earmark, "~> 1.2", [hex: :earmark, repo: "hexpm", optional: false]}, {:makeup_elixir, "~> 0.10", [hex: :makeup_elixir, repo: "hexpm", optional: false]}], "hexpm"},