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
|
||||
|
||||
button_pins = {24, 25, 5, 6}
|
||||
buttons = 4
|
||||
leds_per_button = 7
|
||||
|
||||
config :ex_speed_game,
|
||||
target: Mix.target(),
|
||||
|
@ -14,20 +16,23 @@ config :ex_speed_game,
|
|||
debug: true,
|
||||
|
||||
# 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
|
||||
# will increase to the right.
|
||||
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
|
||||
# spurious inputs. In milliseconds.
|
||||
debounce_delay: 20,
|
||||
|
@ -51,13 +56,13 @@ config :ex_speed_game,
|
|||
iface: "wlan0",
|
||||
|
||||
# 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
|
||||
crash_pattern_delay: 3_000,
|
||||
|
||||
# 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
|
||||
# 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, :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
|
||||
import_config "target.exs"
|
||||
end
|
||||
|
|
|
@ -31,7 +31,6 @@ defmodule ExSpeedGame.Application do
|
|||
# List all child processes to be supervised
|
||||
def children(_target) do
|
||||
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)
|
||||
|
||||
[
|
||||
|
@ -40,7 +39,7 @@ defmodule ExSpeedGame.Application do
|
|||
[name: ExSpeedGame.PubSub, keys: :duplicate, partitions: System.schedulers_online()]},
|
||||
{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}},
|
||||
{DynamicSupervisor, strategy: :one_for_one, name: GameSupervisor},
|
||||
|
||||
|
|
|
@ -3,47 +3,38 @@ defmodule ExSpeedGame.Game.Lights do
|
|||
use Bitwise, only_operators: true
|
||||
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
|
||||
deftypedstruct(%{
|
||||
light_pins: Types.led_pins(),
|
||||
name: GenServer.name()
|
||||
})
|
||||
end
|
||||
|
||||
defmodule State do
|
||||
deftypedstruct(%{
|
||||
lights: {Types.pin(), Types.pin(), Types.pin(), Types.pin()},
|
||||
references: [reference()]
|
||||
})
|
||||
deftypedstruct(%{})
|
||||
end
|
||||
|
||||
### SERVER INTERFACE
|
||||
|
||||
@spec start_link(Options.t()) :: :ignore | {:error, any} | {:ok, pid}
|
||||
def start_link(%Options{} = opts) do
|
||||
{{pin1, _}, {pin2, _}, {pin3, _}, {pin4, _}} = opts.light_pins
|
||||
|
||||
GenServer.start_link(
|
||||
__MODULE__,
|
||||
%{pins: {pin1, pin2, pin3, pin4}},
|
||||
%{},
|
||||
name: opts.name
|
||||
)
|
||||
end
|
||||
|
||||
@impl true
|
||||
@spec init(%{pins: {Types.pin(), Types.pin(), Types.pin(), Types.pin()}}) :: {:ok, State.t()}
|
||||
def init(%{pins: pins}) do
|
||||
references =
|
||||
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}}
|
||||
@spec init(any()) :: {:ok, State.t()}
|
||||
def init(_) do
|
||||
{:ok, %State{}}
|
||||
end
|
||||
|
||||
@impl true
|
||||
|
@ -52,21 +43,28 @@ defmodule ExSpeedGame.Game.Lights do
|
|||
GenServer.from(),
|
||||
State.t()
|
||||
) :: {:reply, :ok, State.t()}
|
||||
def handle_call({:set_lights, values}, _from, %State{references: references} = state) do
|
||||
references
|
||||
def handle_call({:set_lights, values}, _from, %State{} = state) do
|
||||
values
|
||||
|> Enum.with_index()
|
||||
|> Enum.each(fn {ref, i} ->
|
||||
Circuits.GPIO.write(ref, elem(values, i) |> bool2val())
|
||||
|> Enum.each(fn
|
||||
{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)
|
||||
|
||||
Logger.debug(fn -> "Setting lights #{inspect(values)}" end)
|
||||
|
||||
Blinkchain.render()
|
||||
|
||||
{:reply, :ok, state}
|
||||
end
|
||||
|
||||
@spec bool2val(boolean()) :: Circuits.GPIO.value()
|
||||
defp bool2val(bool)
|
||||
defp bool2val(true), do: 1
|
||||
defp bool2val(false), do: 0
|
||||
|
||||
### CLIENT INTERFACE
|
||||
|
||||
@doc """
|
||||
|
@ -82,7 +80,7 @@ defmodule ExSpeedGame.Game.Lights do
|
|||
"""
|
||||
@spec clear(GenServer.name()) :: :ok | no_return()
|
||||
def clear(server) do
|
||||
set(server, {false, false, false, false})
|
||||
set(server, [false, false, false, false])
|
||||
end
|
||||
|
||||
@doc """
|
||||
|
@ -99,16 +97,29 @@ defmodule ExSpeedGame.Game.Lights do
|
|||
"""
|
||||
@spec set_index(GenServer.name(), integer()) :: :ok | no_return()
|
||||
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
|
||||
|
||||
@spec number2binary(integer()) :: values()
|
||||
defp number2binary(number) do
|
||||
{
|
||||
[
|
||||
(number &&& 0b1000) != 0,
|
||||
(number &&& 0b0100) != 0,
|
||||
(number &&& 0b0010) != 0,
|
||||
(number &&& 0b0001) != 0
|
||||
]
|
||||
end
|
||||
|
||||
@spec index2coordinates(0..3) :: Blinkchain.Point.t()
|
||||
defp index2coordinates(index) do
|
||||
%Blinkchain.Point{
|
||||
x: index * 7,
|
||||
y: 0
|
||||
}
|
||||
end
|
||||
|
||||
@spec index2color(0..3) :: {pos_integer(), pos_integer(), pos_integer()}
|
||||
def index2color(index) do
|
||||
Map.fetch!(@led_colors, index)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,11 +1,5 @@
|
|||
defmodule ExSpeedGame.Game.Types do
|
||||
@type pin :: Circuits.GPIO.pin_number()
|
||||
@type pins :: {pin(), pin(), pin(), pin()}
|
||||
@type led_pins :: {
|
||||
{pin(), pin()},
|
||||
{pin(), pin()},
|
||||
{pin(), pin()},
|
||||
{pin(), pin()}
|
||||
}
|
||||
@type choice :: 1..4
|
||||
end
|
||||
|
|
1
mix.exs
1
mix.exs
|
@ -45,6 +45,7 @@ defmodule ExSpeedGame.MixProject do
|
|||
{:ring_logger, "~> 0.8"},
|
||||
{:toolshed, "~> 0.2"},
|
||||
{:circuits_gpio, "~> 0.4"},
|
||||
{:blinkchain, "~> 1.0"},
|
||||
|
||||
# Web UI stuff
|
||||
{: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"},
|
||||
"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"},
|
||||
|
|
Loading…
Reference in a new issue