Current section

Files

Jump to
delux lib delux effects.ex
Raw

lib/delux/effects.ex

defmodule Delux.Effects do
@moduledoc """
Functions for creating a variety of LED patterns
"""
alias Delux.Program
alias Delux.RGB
@long_time 3_600_000
@typedoc """
Option effects (none yet)
"""
@type options() :: []
@doc """
All LEDs off
"""
@spec off() :: Program.t()
def off() do
%Program{
red: led_off(),
green: led_off(),
blue: led_off(),
description: "off",
duration: :infinity
}
end
@doc """
Set an indicator to the specified color
"""
@spec on(RGB.t(), options()) :: Program.t()
def on(c, _options \\ []) do
{r, g, b} = RGB.new(c)
%Program{
red: led_on(r),
green: led_on(g),
blue: led_on(b),
description: RGB.to_ansidata(c, "solid "),
duration: :infinity
}
end
@doc """
Blink an indicator
This returns a pattern that blinks the specified color at a 50% duty cycle.
The pattern starts on and then goes off.
"""
@spec blink(RGB.t(), number(), options()) :: Program.t()
def blink(c, frequency, _options \\ []) do
{r, g, b} = RGB.new(c)
%Program{
red: led_blink(r, frequency),
green: led_blink(g, frequency),
blue: led_blink(b, frequency),
description: [RGB.to_ansidata(c, ""), " at #{frequency} Hz"],
duration: :infinity
}
end
@doc """
Create a transient two color sequence
The first color is shown for 20 ms. 10 ms in, the second color is shown for
20 ms. The Effects is a quick flash of light that can be used to show
feedback to a button. Total duration of the Effects is 40 ms.
"""
@spec blip(RGB.t(), RGB.t(), options()) :: Program.t()
def blip(c1, c2, _options \\ []) do
{r1, g1, b1} = RGB.new(c1)
{r2, g2, b2} = RGB.new(c2)
%Program{
red: led_blip(r1, r2),
green: led_blip(g1, g2),
blue: led_blip(b1, b2),
description: [RGB.to_ansidata(c1), :reset, "->", RGB.to_ansidata(c2), " blip"],
duration: 40
}
end
@doc """
Cycle between colors
Colors are shown with equal duration determined from the specified frequency.
"""
@spec cycle([RGB.t()], number(), options()) :: Program.t()
def cycle(colors, frequency, _options \\ [])
when is_list(colors) and frequency > 0 and frequency < 20 do
{reds, greens, blues} = colors |> Enum.map(&RGB.new/1) |> unzip3()
duration = round(1000.0 / Enum.count(colors) / frequency)
%Program{
red: led_cycle(reds, duration),
green: led_cycle(greens, duration),
blue: led_cycle(blues, duration),
description: [
Enum.map(colors, &RGB.to_ansidata/1) |> Enum.intersperse([:reset, "-"]),
:reset,
" cycle at #{frequency} Hz"
],
duration: :infinity
}
end
@doc """
Create a program from an arbitrary function
Pass in a function that takes times in milliseconds and returns colors. The
returned pattern piecewise linearly interpolates the waveform.
"""
@spec waveform(fun(), non_neg_integer(), options()) :: Program.t()
def waveform(fun, period, options \\ []) do
time_step = 100
colors = for t <- 0..period//time_step, do: fun.(t, options)
{reds, greens, blues} = unzip3(colors)
%Program{
red: led_waveform(reds, time_step),
green: led_waveform(greens, time_step),
blue: led_waveform(blues, time_step),
description: "waveform",
duration: :infinity
}
end
defp unzip3(tuples, acc \\ {[], [], []})
defp unzip3([], {a1, a2, a3}), do: {Enum.reverse(a1), Enum.reverse(a2), Enum.reverse(a3)}
defp unzip3([{x, y, z} | rest], {a1, a2, a3}), do: unzip3(rest, {[x | a1], [y | a2], [z | a3]})
defp led_off(), do: [{0, @long_time}, {0, 0}]
defp led_on(b), do: [{b, @long_time}, {b, 0}]
defp led_blink(0, _frequency), do: led_off()
defp led_blink(b, frequency) when frequency > 0 and frequency < 20 do
on_time = round(500 / frequency)
[{b, on_time}, {b, 0}, {0, on_time}, {0, 0}]
end
defp led_blink(b, frequency) when frequency >= 20, do: led_on(b)
defp led_blip(0, 0), do: led_off()
defp led_blip(0, b2), do: [{0, 20}, {0, 0}, {b2, 20}, {b2, 0} | led_off()]
defp led_blip(b1, 0), do: [{0, 10}, {0, 0}, {b1, 20}, {b1, 0} | led_off()]
defp led_blip(b1, b2), do: [{0, 10}, {0, 0}, {b1, 30}, {b2, 0} | led_off()]
defp led_cycle(values, duration) do
Enum.flat_map(values, fn b -> [{b, duration}, {b, 0}] end)
end
defp led_waveform(values, time_step) do
Enum.map(values, fn v -> {v, time_step} end)
end
end