Skip to content

Instantly share code, notes, and snippets.

@adkron
Created April 19, 2017 20:42
Show Gist options
  • Save adkron/ba23e1c9b4ca80b8a533a84d5feaba1e to your computer and use it in GitHub Desktop.
Save adkron/ba23e1c9b4ca80b8a533a84d5feaba1e to your computer and use it in GitHub Desktop.
defmodule GrovePi.Sound do
use GenServer
@moduledoc """
Listen for events from a GrovePi button. There are two types of
events; pressed and released. When registering for an event the button
will then send a message of `{pin, :pressed}` or `{pin, :released}`.
The button works by polling `GrovePi.Digital` on the pin that you have
registered to a button.
Example usage:
```
iex> {:ok, button}=GrovePi.Sound.start_link(1)
:ok
iex> GrovePi.Sound.subscribe(1, :loud)
:ok
iex> GrovePi.Sound.subscribe(1, :quiet)
:ok
```
"""
@type level :: integer
@type change :: {level, level}
@type event :: :triggered | :released
@poll_interval 100
@threshold 10
alias GrovePi.Registry.Pin
alias GrovePi.Registry.Subscriber
defmodule State do
@moduledoc false
defstruct [:pin, :value, :poll_interval, :prefix]
end
@spec start_link(GrovePi.pin) :: Supervisor.on_start
def start_link(pin, opts \\ []) do
poll_interval = Keyword.get(opts, :poll_interval, @poll_interval)
prefix = Keyword.get(opts, :prefix, Default)
opts = Keyword.put(opts, :name, Pin.name(prefix, pin))
GenServer.start_link(__MODULE__,
[pin, poll_interval, prefix],
opts
)
end
def init([pin, poll_interval, prefix]) do
state = %State{
pin: pin,
poll_interval: poll_interval,
prefix: prefix,
value: 0,
}
|> update_value()
schedule_poll(state)
{:ok, state}
end
def schedule_poll(%State{poll_interval: poll_interval}) do
Process.send_after(self(), :poll_sound, poll_interval)
end
@spec read(GrovePi.pin, atom) :: level
def read(pin, prefix \\ Defualt) do
GenServer.call(Pin.name(prefix, pin), :read)
end
@spec subscribe(GrovePi.pin, event, atom) :: level
def subscribe(pin, event, prefix \\ Default) do
Subscriber.subscribe(prefix, {pin, event})
end
def handle_call(:read, _from, state) do
new_state = update_value(state)
{:reply, new_state.value, new_state}
end
def handle_info(:poll_sound, state) do
new_state = update_value(state)
schedule_poll(state)
{:noreply, new_state}
end
@spec update_value(State) ::State
defp update_value(state) do
new_value = GrovePi.Analog.read(state.prefix, state.pin)
update_value(state, state.value, new_value)
end
defp update_value(state, value, value), do: state
defp update_value(state, old_value, new_value) do
Subscriber.notify_change(state.prefix,
{state.pin, event(old_value - new_value, @threshold)}
)
%{state | value: new_value}
end
defp event(delta, threshold) when delta > 0 and abs(delta) > threshold, do: :triggered
defp event(delta, threshold) when delta < 0 and abs(delta) > threshold, do: :released
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment