Implement brightness setting and fix bugz

This commit is contained in:
Mikko Ahlroth 2021-11-04 20:15:22 +02:00
parent c709d616b9
commit 6f3c8500a8
7 changed files with 146 additions and 21 deletions

2
.gitignore vendored
View file

@ -17,3 +17,5 @@
erl_crash.dump erl_crash.dump
.elixir_ls .elixir_ls
.env

View file

@ -8,6 +8,7 @@ import Config
button_pins = {24, 25, 5, 6} button_pins = {24, 25, 5, 6}
buttons = 4 buttons = 4
leds_per_button = 7 leds_per_button = 7
brightness = 45
config :ex_speed_game, config :ex_speed_game,
target: Mix.target(), target: Mix.target(),
@ -29,6 +30,12 @@ config :ex_speed_game,
3 => {250, 51, 51} 3 => {250, 51, 51}
}, },
# Initial brightness of the LEDs
led_brightness: brightness,
# LED PWM channel
led_channel: 0,
# Pins of the buttons, in order from left to right in the case. Leftmost will be 0, and number # Pins of the buttons, in order from left to right in the case. Leftmost will be 0, and number
# will increase to the right. # will increase to the right.
button_pins: button_pins, button_pins: button_pins,
@ -118,7 +125,7 @@ config :blinkchain, canvas: {buttons * leds_per_button, 1}
config :blinkchain, :channel0, config :blinkchain, :channel0,
pin: 18, pin: 18,
type: :grb, type: :grb,
brightness: 32, brightness: brightness,
gamma: gamma, gamma: gamma,
arrangement: [ arrangement: [
%{ %{

View file

@ -32,6 +32,8 @@ defmodule ExSpeedGame.Application do
def children(_target) do def children(_target) do
pins = Application.get_env(:ex_speed_game, :button_pins) pins = Application.get_env(:ex_speed_game, :button_pins)
debounce_delay = Application.get_env(:ex_speed_game, :debounce_delay) debounce_delay = Application.get_env(:ex_speed_game, :debounce_delay)
led_channel = Application.get_env(:ex_speed_game, :led_channel)
led_brightness = Application.get_env(:ex_speed_game, :led_brightness)
[ [
# Starts a worker by calling: ExSpeedGame.Worker.start_link(arg) # Starts a worker by calling: ExSpeedGame.Worker.start_link(arg)
@ -39,7 +41,7 @@ defmodule ExSpeedGame.Application do
[name: ExSpeedGame.PubSub, keys: :duplicate, partitions: System.schedulers_online()]}, [name: ExSpeedGame.PubSub, keys: :duplicate, partitions: System.schedulers_online()]},
{ButtonInput, {ButtonInput,
%ButtonInput.Options{pins: pins, debounce_delay: debounce_delay, name: ButtonInput}}, %ButtonInput.Options{pins: pins, debounce_delay: debounce_delay, name: ButtonInput}},
{Lights, %Lights.Options{name: Lights}}, {Lights, %Lights.Options{channel: led_channel, brightness: led_brightness, name: Lights}},
{Menu, %Menu.Options{name: Menu}}, {Menu, %Menu.Options{name: Menu}},
{DynamicSupervisor, strategy: :one_for_one, name: GameSupervisor}, {DynamicSupervisor, strategy: :one_for_one, name: GameSupervisor},

View file

@ -55,7 +55,7 @@ defmodule ExSpeedGame.Game.ButtonInput do
end end
def handle_call(:acquire, _from, %State{active: true, listener: l}) do def handle_call(:acquire, _from, %State{active: true, listener: l}) do
raise InputError, message: "ButtonInput already acquired to #{l}" raise InputError, message: "ButtonInput already acquired to #{inspect(l)}"
end end
# Release # Release

View file

@ -0,0 +1,79 @@
defmodule ExSpeedGame.Game.ChangeBrightness do
use GenServer, restart: :temporary
import ExSpeedGame.Utils.TypedStruct
require Logger
alias ExSpeedGame.Game.Lights
alias ExSpeedGame.Game.ButtonInput
@brightness_step 10
defmodule Options do
deftypedstruct(%{
input_server: GenServer.name(),
lights_server: GenServer.name()
})
end
defmodule State do
deftypedstruct(%{
input_server: GenServer.name(),
lights_server: GenServer.name(),
brightness: 0..255
})
end
@spec start_link(Options.t()) :: :ignore | {:error, any} | {:ok, pid}
def start_link(%Options{} = opts) do
GenServer.start_link(__MODULE__, Map.from_struct(opts))
end
@impl true
@spec init(%{input_server: GenServer.name(), lights_server: GenServer.name()}) ::
{:ok, State.t()}
def init(%{input_server: input_server, lights_server: lights_server}) do
ButtonInput.acquire(input_server)
Lights.set(lights_server, [false, true, true, false])
brightness = Lights.get_brightness(lights_server)
state = %State{
input_server: input_server,
lights_server: lights_server,
brightness: brightness
}
{:ok, state}
end
@impl true
def handle_info(msg, state)
def handle_info({:input, pin}, state) do
choice = ButtonInput.get_choice(pin, Application.get_env(:ex_speed_game, :button_pins))
case choice do
2 ->
change_brightness(state, -@brightness_step)
3 ->
change_brightness(state, @brightness_step)
_ ->
{:stop, :normal, state}
end
end
@impl true
def terminate(_reason, state) do
ButtonInput.release(state.input_server)
end
@spec change_brightness(State.t(), integer()) :: {:noreply, State.t()}
defp change_brightness(state, step) do
brightness = (state.brightness + step) |> max(0) |> min(255)
Lights.set_brightness(state.lights_server, brightness)
Logger.debug("Set light brightness to #{inspect(brightness)}")
{:noreply, %State{state | brightness: brightness}}
end
end

View file

@ -12,12 +12,17 @@ defmodule ExSpeedGame.Game.Lights do
defmodule Options do defmodule Options do
deftypedstruct(%{ deftypedstruct(%{
name: GenServer.name() name: GenServer.name(),
channel: Blinkchain.channel_number(),
brightness: 0..255
}) })
end end
defmodule State do defmodule State do
deftypedstruct(%{}) deftypedstruct(%{
channel: Blinkchain.channel_number(),
brightness: 0..255
})
end end
### SERVER INTERFACE ### SERVER INTERFACE
@ -26,23 +31,22 @@ defmodule ExSpeedGame.Game.Lights do
def start_link(%Options{} = opts) do def start_link(%Options{} = opts) do
GenServer.start_link( GenServer.start_link(
__MODULE__, __MODULE__,
%{}, Map.from_struct(opts),
name: opts.name name: opts.name
) )
end end
@impl true @impl true
@spec init(any()) :: {:ok, State.t()} @spec init(%{channel: Blinkchain.channel_number(), brightness: 0..255}) :: {:ok, State.t()}
def init(_) do def init(%{channel: channel, brightness: brightness}) do
{:ok, %State{}} Blinkchain.set_brightness(channel, brightness)
{:ok, %State{channel: channel, brightness: brightness}}
end end
@impl true @impl true
@spec handle_call( def handle_call(msg, from, state)
{:set_lights, values()},
GenServer.from(),
State.t()
) :: {:reply, :ok, State.t()}
def handle_call({:set_lights, values}, _from, %State{} = state) do def handle_call({:set_lights, values}, _from, %State{} = state) do
values values
|> Enum.with_index() |> Enum.with_index()
@ -65,20 +69,47 @@ defmodule ExSpeedGame.Game.Lights do
{:reply, :ok, state} {:reply, :ok, state}
end end
def handle_call({:set_brightness, brightness}, _from, %State{} = state) do
Blinkchain.set_brightness(state.channel, brightness)
Blinkchain.render()
{:reply, :ok, %State{state | brightness: brightness}}
end
def handle_call(:get_brightness, _from, %State{} = state) do
{:reply, state.brightness, state}
end
### CLIENT INTERFACE ### CLIENT INTERFACE
@doc """ @doc """
Set the lights on/off according to the given value. Set the lights on/off according to the given value.
""" """
@spec set(GenServer.name(), values()) :: :ok | no_return() @spec set(GenServer.name(), values()) :: :ok
def set(server, values) do def set(server, values) do
:ok = GenServer.call(server, {:set_lights, values}) :ok = GenServer.call(server, {:set_lights, values})
end end
@doc """
Set the brightness of the lights from 0 (off) to 255 (max).
"""
@spec set_brightness(GenServer.name(), 0..255) :: :ok
def set_brightness(server, brightness) do
:ok = GenServer.call(server, {:set_brightness, brightness})
end
@doc """
Get the brightness of the lights from 0 (off) to 255 (max).
"""
@spec get_brightness(GenServer.name()) :: 0..255
def get_brightness(server) do
GenServer.call(server, :get_brightness)
end
@doc """ @doc """
Turn off all lights. Turn off all lights.
""" """
@spec clear(GenServer.name()) :: :ok | no_return() @spec clear(GenServer.name()) :: :ok
def clear(server) do def clear(server) do
set(server, [false, false, false, false]) set(server, [false, false, false, false])
end end
@ -86,7 +117,7 @@ defmodule ExSpeedGame.Game.Lights do
@doc """ @doc """
Show the given number on the lights as a binary pattern. Show the given number on the lights as a binary pattern.
""" """
@spec show_binary(GenServer.name(), integer()) :: :ok | no_return() @spec show_binary(GenServer.name(), integer()) :: :ok
def show_binary(server, number) do def show_binary(server, number) do
values = number2binary(number) values = number2binary(number)
set(server, values) set(server, values)
@ -95,7 +126,7 @@ defmodule ExSpeedGame.Game.Lights do
@doc """ @doc """
Set the given index on and the others off. Set the given index on and the others off.
""" """
@spec set_index(GenServer.name(), integer()) :: :ok | no_return() @spec set_index(GenServer.name(), integer()) :: :ok
def set_index(server, index) do def set_index(server, index) do
set(server, [index == 1, index == 2, index == 3, index == 4]) set(server, [index == 1, index == 2, index == 3, index == 4])
end end
@ -119,7 +150,7 @@ defmodule ExSpeedGame.Game.Lights do
end end
@spec index2color(0..3) :: {pos_integer(), pos_integer(), pos_integer()} @spec index2color(0..3) :: {pos_integer(), pos_integer(), pos_integer()}
def index2color(index) do defp index2color(index) do
Map.fetch!(@led_colors, index) Map.fetch!(@led_colors, index)
end end
end end

View file

@ -3,7 +3,7 @@ defmodule ExSpeedGame.Game.Menu do
require Logger require Logger
import ExSpeedGame.Utils.TypedStruct import ExSpeedGame.Utils.TypedStruct
alias ExSpeedGame.Game.{Types, ButtonInput, ShowIP, Lights} alias ExSpeedGame.Game.{Types, ButtonInput, ShowIP, Lights, ChangeBrightness}
alias ExSpeedGame.Game.Modes.{ alias ExSpeedGame.Game.Modes.{
Speed Speed
@ -23,7 +23,9 @@ defmodule ExSpeedGame.Game.Menu do
initial_delay: Application.get_env(:ex_speed_game, :delay_pro) initial_delay: Application.get_env(:ex_speed_game, :delay_pro)
}}, }},
{"MemoryGame", Speed, %{}}, {"MemoryGame", Speed, %{}},
{"Show IP", ShowIP, %{}} {"Show IP", ShowIP, %{}},
{"Change brightness", ChangeBrightness,
%ChangeBrightness.Options{input_server: ButtonInput, lights_server: Lights}}
} }
@menu_size :erlang.tuple_size(@menu) @menu_size :erlang.tuple_size(@menu)
@ -129,6 +131,8 @@ defmodule ExSpeedGame.Game.Menu do
{:noreply, new_state} {:noreply, new_state}
end end
def handle_info(_, state), do: {:noreply, state}
@impl true @impl true
@spec handle_call({:esg_pubsub, :get_state}, GenServer.from(), State.t()) :: @spec handle_call({:esg_pubsub, :get_state}, GenServer.from(), State.t()) ::
{:reply, State.t(), State.t()} {:reply, State.t(), State.t()}