Change to using Blinkchain to control NeoPixels
This commit is contained in:
parent
45f271aa3d
commit
c709d616b9
6 changed files with 96 additions and 50 deletions
|
@ -6,6 +6,8 @@
|
||||||
import Config
|
import Config
|
||||||
|
|
||||||
button_pins = {24, 25, 5, 6}
|
button_pins = {24, 25, 5, 6}
|
||||||
|
buttons = 4
|
||||||
|
leds_per_button = 7
|
||||||
|
|
||||||
config :ex_speed_game,
|
config :ex_speed_game,
|
||||||
target: Mix.target(),
|
target: Mix.target(),
|
||||||
|
@ -14,20 +16,23 @@ config :ex_speed_game,
|
||||||
debug: true,
|
debug: true,
|
||||||
|
|
||||||
# Amount of buttons in the game.
|
# Amount of buttons in the game.
|
||||||
buttons: 4,
|
buttons: buttons,
|
||||||
|
|
||||||
|
# How many LEDs are in the NeoPixel chain inside each button
|
||||||
|
leds_per_button: leds_per_button,
|
||||||
|
|
||||||
|
# Colors of the LEDs keyed by index
|
||||||
|
led_colors: %{
|
||||||
|
0 => {31, 133, 222},
|
||||||
|
1 => {65, 222, 31},
|
||||||
|
2 => {241, 229, 0},
|
||||||
|
3 => {250, 51, 51}
|
||||||
|
},
|
||||||
|
|
||||||
# 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,
|
||||||
|
|
||||||
# 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
|
# Debounce delay, i.e. how long a button must be high or low before it is accepted, to reduce
|
||||||
# spurious inputs. In milliseconds.
|
# spurious inputs. In milliseconds.
|
||||||
debounce_delay: 20,
|
debounce_delay: 20,
|
||||||
|
@ -51,13 +56,13 @@ config :ex_speed_game,
|
||||||
iface: "wlan0",
|
iface: "wlan0",
|
||||||
|
|
||||||
# LED pattern to show when there is a crash
|
# LED pattern to show when there is a crash
|
||||||
crash_pattern: {false, true, true, false},
|
crash_pattern: [false, true, true, false],
|
||||||
|
|
||||||
# Time to show crash pattern for
|
# Time to show crash pattern for
|
||||||
crash_pattern_delay: 3_000,
|
crash_pattern_delay: 3_000,
|
||||||
|
|
||||||
# LED pattern to show when game ends
|
# LED pattern to show when game ends
|
||||||
fail_pattern: {true, true, true, true}
|
fail_pattern: [true, true, true, true]
|
||||||
|
|
||||||
# Customize non-Elixir parts of the firmware. See
|
# Customize non-Elixir parts of the firmware. See
|
||||||
# https://hexdocs.pm/nerves/advanced-configuration.html for details.
|
# https://hexdocs.pm/nerves/advanced-configuration.html for details.
|
||||||
|
@ -89,6 +94,41 @@ config :ex_speed_game, ExSpeedGame.Web.Endpoint,
|
||||||
config :phoenix, :json_library, Jason
|
config :phoenix, :json_library, Jason
|
||||||
config :phoenix, :stacktrace_depth, 20
|
config :phoenix, :stacktrace_depth, 20
|
||||||
|
|
||||||
|
gamma = [
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1,
|
||||||
|
1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2,
|
||||||
|
2, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 5, 5, 5,
|
||||||
|
5, 6, 6, 6, 6, 7, 7, 7, 7, 8, 8, 8, 9, 9, 9, 10,
|
||||||
|
10, 10, 11, 11, 11, 12, 12, 13, 13, 13, 14, 14, 15, 15, 16, 16,
|
||||||
|
17, 17, 18, 18, 19, 19, 20, 20, 21, 21, 22, 22, 23, 24, 24, 25,
|
||||||
|
25, 26, 27, 27, 28, 29, 29, 30, 31, 32, 32, 33, 34, 35, 35, 36,
|
||||||
|
37, 38, 39, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 50,
|
||||||
|
51, 52, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 66, 67, 68,
|
||||||
|
69, 70, 72, 73, 74, 75, 77, 78, 79, 81, 82, 83, 85, 86, 87, 89,
|
||||||
|
90, 92, 93, 95, 96, 98, 99, 101, 102, 104, 105, 107, 109, 110, 112, 114,
|
||||||
|
115, 117, 119, 120, 122, 124, 126, 127, 129, 131, 133, 135, 137, 138, 140, 142,
|
||||||
|
144, 146, 148, 150, 152, 154, 156, 158, 160, 162, 164, 167, 169, 171, 173, 175,
|
||||||
|
177, 180, 182, 184, 186, 189, 191, 193, 196, 198, 200, 203, 205, 208, 210, 213,
|
||||||
|
215, 218, 220, 223, 225, 228, 231, 233, 236, 239, 241, 244, 247, 249, 252, 255
|
||||||
|
]
|
||||||
|
|
||||||
|
config :blinkchain, canvas: {buttons * leds_per_button, 1}
|
||||||
|
|
||||||
|
config :blinkchain, :channel0,
|
||||||
|
pin: 18,
|
||||||
|
type: :grb,
|
||||||
|
brightness: 32,
|
||||||
|
gamma: gamma,
|
||||||
|
arrangement: [
|
||||||
|
%{
|
||||||
|
type: :strip,
|
||||||
|
origin: {0, 0},
|
||||||
|
count: buttons * leds_per_button,
|
||||||
|
direction: :right
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
if Mix.target() != :host do
|
if Mix.target() != :host do
|
||||||
import_config "target.exs"
|
import_config "target.exs"
|
||||||
end
|
end
|
||||||
|
|
|
@ -31,7 +31,6 @@ defmodule ExSpeedGame.Application do
|
||||||
# List all child processes to be supervised
|
# List all child processes to be supervised
|
||||||
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)
|
||||||
led_pins = Application.get_env(:ex_speed_game, :button_light_pins)
|
|
||||||
debounce_delay = Application.get_env(:ex_speed_game, :debounce_delay)
|
debounce_delay = Application.get_env(:ex_speed_game, :debounce_delay)
|
||||||
|
|
||||||
[
|
[
|
||||||
|
@ -40,7 +39,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{light_pins: led_pins, name: Lights}},
|
{Lights, %Lights.Options{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},
|
||||||
|
|
||||||
|
|
|
@ -3,47 +3,38 @@ defmodule ExSpeedGame.Game.Lights do
|
||||||
use Bitwise, only_operators: true
|
use Bitwise, only_operators: true
|
||||||
import ExSpeedGame.Utils.TypedStruct
|
import ExSpeedGame.Utils.TypedStruct
|
||||||
|
|
||||||
alias ExSpeedGame.Game.Types
|
require Logger
|
||||||
|
|
||||||
@type values :: {boolean(), boolean(), boolean(), boolean()}
|
@type values :: [boolean()]
|
||||||
|
@leds_per_button Application.compile_env!(:ex_speed_game, :leds_per_button)
|
||||||
|
@led_colors Application.compile_env!(:ex_speed_game, :led_colors)
|
||||||
|
@black {0, 0, 0}
|
||||||
|
|
||||||
defmodule Options do
|
defmodule Options do
|
||||||
deftypedstruct(%{
|
deftypedstruct(%{
|
||||||
light_pins: Types.led_pins(),
|
|
||||||
name: GenServer.name()
|
name: GenServer.name()
|
||||||
})
|
})
|
||||||
end
|
end
|
||||||
|
|
||||||
defmodule State do
|
defmodule State do
|
||||||
deftypedstruct(%{
|
deftypedstruct(%{})
|
||||||
lights: {Types.pin(), Types.pin(), Types.pin(), Types.pin()},
|
|
||||||
references: [reference()]
|
|
||||||
})
|
|
||||||
end
|
end
|
||||||
|
|
||||||
### SERVER INTERFACE
|
### SERVER INTERFACE
|
||||||
|
|
||||||
@spec start_link(Options.t()) :: :ignore | {:error, any} | {:ok, pid}
|
@spec start_link(Options.t()) :: :ignore | {:error, any} | {:ok, pid}
|
||||||
def start_link(%Options{} = opts) do
|
def start_link(%Options{} = opts) do
|
||||||
{{pin1, _}, {pin2, _}, {pin3, _}, {pin4, _}} = opts.light_pins
|
|
||||||
|
|
||||||
GenServer.start_link(
|
GenServer.start_link(
|
||||||
__MODULE__,
|
__MODULE__,
|
||||||
%{pins: {pin1, pin2, pin3, pin4}},
|
%{},
|
||||||
name: opts.name
|
name: opts.name
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
@impl true
|
@impl true
|
||||||
@spec init(%{pins: {Types.pin(), Types.pin(), Types.pin(), Types.pin()}}) :: {:ok, State.t()}
|
@spec init(any()) :: {:ok, State.t()}
|
||||||
def init(%{pins: pins}) do
|
def init(_) do
|
||||||
references =
|
{:ok, %State{}}
|
||||||
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
|
end
|
||||||
|
|
||||||
@impl true
|
@impl true
|
||||||
|
@ -52,21 +43,28 @@ defmodule ExSpeedGame.Game.Lights do
|
||||||
GenServer.from(),
|
GenServer.from(),
|
||||||
State.t()
|
State.t()
|
||||||
) :: {:reply, :ok, State.t()}
|
) :: {:reply, :ok, State.t()}
|
||||||
def handle_call({:set_lights, values}, _from, %State{references: references} = state) do
|
def handle_call({:set_lights, values}, _from, %State{} = state) do
|
||||||
references
|
values
|
||||||
|> Enum.with_index()
|
|> Enum.with_index()
|
||||||
|> Enum.each(fn {ref, i} ->
|
|> Enum.each(fn
|
||||||
Circuits.GPIO.write(ref, elem(values, i) |> bool2val())
|
{true, i} ->
|
||||||
|
from = index2coordinates(i)
|
||||||
|
Logger.debug("Setting #{i} to #{inspect(index2color(i))} #{inspect(from)}")
|
||||||
|
Blinkchain.fill(from, @leds_per_button, 1, index2color(i))
|
||||||
|
|
||||||
|
{false, i} ->
|
||||||
|
from = index2coordinates(i)
|
||||||
|
Logger.debug("Setting #{i} to black #{inspect(from)}")
|
||||||
|
Blinkchain.fill(from, @leds_per_button, 1, @black)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
Logger.debug(fn -> "Setting lights #{inspect(values)}" end)
|
||||||
|
|
||||||
|
Blinkchain.render()
|
||||||
|
|
||||||
{:reply, :ok, state}
|
{:reply, :ok, state}
|
||||||
end
|
end
|
||||||
|
|
||||||
@spec bool2val(boolean()) :: Circuits.GPIO.value()
|
|
||||||
defp bool2val(bool)
|
|
||||||
defp bool2val(true), do: 1
|
|
||||||
defp bool2val(false), do: 0
|
|
||||||
|
|
||||||
### CLIENT INTERFACE
|
### CLIENT INTERFACE
|
||||||
|
|
||||||
@doc """
|
@doc """
|
||||||
|
@ -82,7 +80,7 @@ defmodule ExSpeedGame.Game.Lights do
|
||||||
"""
|
"""
|
||||||
@spec clear(GenServer.name()) :: :ok | no_return()
|
@spec clear(GenServer.name()) :: :ok | no_return()
|
||||||
def clear(server) do
|
def clear(server) do
|
||||||
set(server, {false, false, false, false})
|
set(server, [false, false, false, false])
|
||||||
end
|
end
|
||||||
|
|
||||||
@doc """
|
@doc """
|
||||||
|
@ -99,16 +97,29 @@ defmodule ExSpeedGame.Game.Lights do
|
||||||
"""
|
"""
|
||||||
@spec set_index(GenServer.name(), integer()) :: :ok | no_return()
|
@spec set_index(GenServer.name(), integer()) :: :ok | no_return()
|
||||||
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
|
||||||
|
|
||||||
@spec number2binary(integer()) :: values()
|
@spec number2binary(integer()) :: values()
|
||||||
defp number2binary(number) do
|
defp number2binary(number) do
|
||||||
{
|
[
|
||||||
(number &&& 0b1000) != 0,
|
(number &&& 0b1000) != 0,
|
||||||
(number &&& 0b0100) != 0,
|
(number &&& 0b0100) != 0,
|
||||||
(number &&& 0b0010) != 0,
|
(number &&& 0b0010) != 0,
|
||||||
(number &&& 0b0001) != 0
|
(number &&& 0b0001) != 0
|
||||||
|
]
|
||||||
|
end
|
||||||
|
|
||||||
|
@spec index2coordinates(0..3) :: Blinkchain.Point.t()
|
||||||
|
defp index2coordinates(index) do
|
||||||
|
%Blinkchain.Point{
|
||||||
|
x: index * 7,
|
||||||
|
y: 0
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@spec index2color(0..3) :: {pos_integer(), pos_integer(), pos_integer()}
|
||||||
|
def index2color(index) do
|
||||||
|
Map.fetch!(@led_colors, index)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,11 +1,5 @@
|
||||||
defmodule ExSpeedGame.Game.Types do
|
defmodule ExSpeedGame.Game.Types do
|
||||||
@type pin :: Circuits.GPIO.pin_number()
|
@type pin :: Circuits.GPIO.pin_number()
|
||||||
@type pins :: {pin(), pin(), pin(), pin()}
|
@type pins :: {pin(), pin(), pin(), pin()}
|
||||||
@type led_pins :: {
|
|
||||||
{pin(), pin()},
|
|
||||||
{pin(), pin()},
|
|
||||||
{pin(), pin()},
|
|
||||||
{pin(), pin()}
|
|
||||||
}
|
|
||||||
@type choice :: 1..4
|
@type choice :: 1..4
|
||||||
end
|
end
|
||||||
|
|
1
mix.exs
1
mix.exs
|
@ -45,6 +45,7 @@ defmodule ExSpeedGame.MixProject do
|
||||||
{:ring_logger, "~> 0.8"},
|
{:ring_logger, "~> 0.8"},
|
||||||
{:toolshed, "~> 0.2"},
|
{:toolshed, "~> 0.2"},
|
||||||
{:circuits_gpio, "~> 0.4"},
|
{:circuits_gpio, "~> 0.4"},
|
||||||
|
{:blinkchain, "~> 1.0"},
|
||||||
|
|
||||||
# Web UI stuff
|
# Web UI stuff
|
||||||
{:phoenix, "~> 1.5.1"},
|
{:phoenix, "~> 1.5.1"},
|
||||||
|
|
1
mix.lock
1
mix.lock
|
@ -1,4 +1,5 @@
|
||||||
%{
|
%{
|
||||||
|
"blinkchain": {:hex, :blinkchain, "1.0.0", "0bef4296c0fc3e36c153d717eefcaf5fa6681b4c81f3f5b98030e8044d3d3fe1", [:make, :mix], [{:elixir_make, "~> 0.6", [hex: :elixir_make, repo: "hexpm", optional: false]}], "hexpm", "ecf950d7eb3fb0409efb64e952be3d921bc20045a5cd736cd4c4e52cfa852c7a"},
|
||||||
"circuits_gpio": {:hex, :circuits_gpio, "0.4.5", "4d5b0f707c425fc56f03086232259f65482a3d1f1cf15335253636d0bb846446", [:make, :mix], [{:elixir_make, "~> 0.6", [hex: :elixir_make, repo: "hexpm", optional: false]}], "hexpm", "b42d28d60a6cfdfb6b21b66ab0b8c5de0ea5a32b390b61d2fe86a2ad8edb90ad"},
|
"circuits_gpio": {:hex, :circuits_gpio, "0.4.5", "4d5b0f707c425fc56f03086232259f65482a3d1f1cf15335253636d0bb846446", [:make, :mix], [{:elixir_make, "~> 0.6", [hex: :elixir_make, repo: "hexpm", optional: false]}], "hexpm", "b42d28d60a6cfdfb6b21b66ab0b8c5de0ea5a32b390b61d2fe86a2ad8edb90ad"},
|
||||||
"cowboy": {:hex, :cowboy, "2.7.0", "91ed100138a764355f43316b1d23d7ff6bdb0de4ea618cb5d8677c93a7a2f115", [:rebar3], [{:cowlib, "~> 2.8.0", [hex: :cowlib, repo: "hexpm", optional: false]}, {:ranch, "~> 1.7.1", [hex: :ranch, repo: "hexpm", optional: false]}], "hexpm", "04fd8c6a39edc6aaa9c26123009200fc61f92a3a94f3178c527b70b767c6e605"},
|
"cowboy": {:hex, :cowboy, "2.7.0", "91ed100138a764355f43316b1d23d7ff6bdb0de4ea618cb5d8677c93a7a2f115", [:rebar3], [{:cowlib, "~> 2.8.0", [hex: :cowlib, repo: "hexpm", optional: false]}, {:ranch, "~> 1.7.1", [hex: :ranch, repo: "hexpm", optional: false]}], "hexpm", "04fd8c6a39edc6aaa9c26123009200fc61f92a3a94f3178c527b70b767c6e605"},
|
||||||
"cowlib": {:hex, :cowlib, "2.8.0", "fd0ff1787db84ac415b8211573e9a30a3ebe71b5cbff7f720089972b2319c8a4", [:rebar3], [], "hexpm", "79f954a7021b302186a950a32869dbc185523d99d3e44ce430cd1f3289f41ed4"},
|
"cowlib": {:hex, :cowlib, "2.8.0", "fd0ff1787db84ac415b8211573e9a30a3ebe71b5cbff7f720089972b2319c8a4", [:rebar3], [], "hexpm", "79f954a7021b302186a950a32869dbc185523d99d3e44ce430cd1f3289f41ed4"},
|
||||||
|
|
Loading…
Reference in a new issue