diff --git a/README.md b/README.md index f3e5359..8635246 100644 --- a/README.md +++ b/README.md @@ -69,6 +69,49 @@ Pigpiox.GPIO.set_mode(gpio, :output) Pigpiox.Waveform.repeat(wave_id) ``` +## Waveform chains + +You can compose more complex waveform by chaining them. +For example, pulsing a GPIO on and off every 500 ms for 10 times and then 50 ms for 100 times and this forever. Chain can be nested. + +```elixir +pulses_500 = [ + %Pigpiox.Waveform.Pulse{gpio_on: gpio, delay: 500_000}, + %Pigpiox.Waveform.Pulse{gpio_off: gpio, delay: 500_000} +] + +Pigpiox.Waveform.add_generic(pulses_500) + +{:ok, wave_500} = Pigpiox.Waveform.create() + +pulses_50 = [ + %Pigpiox.Waveform.Pulse{gpio_on: gpio, delay: 50_000}, + %Pigpiox.Waveform.Pulse{gpio_off: gpio, delay: 50_000} +] + +Pigpiox.Waveform.add_generic(pulses_50) + +{:ok, wave_50} = Pigpiox.Waveform.create() + +Pigpiox.GPIO.set_mode(gpio, :output) + +Pigpiox.Waveform.chain(%Pigpiox.Waveform.ChainElement{ + content: [ + %Pigpiox.Waveform.ChainElement{ + content: [wave_500], + repeat: 10 + }, + %Pigpiox.Waveform.ChainElement{ + content: [wave_50], + repeat: 100 + } + ], + repeat: :forever +}) + +Pigpiox.Waveform.repeat(wave_id) +``` + ## Clock The `Pigpiox.Clock` module provides functions that allow you to set a clock on reserved pin. diff --git a/lib/pigpiox/socket.ex b/lib/pigpiox/socket.ex index 926de8b..abfea53 100644 --- a/lib/pigpiox/socket.ex +++ b/lib/pigpiox/socket.ex @@ -32,22 +32,23 @@ defmodule Pigpiox.Socket do @doc """ Runs a command via pigpiod. """ - @spec command(type :: atom, param_one :: integer, param_two :: integer, extents :: list(integer)) :: command_result - def command(cmd, p1 \\ 0, p2 \\ 0, extents \\ []) - def command(cmd, p1, p2, extents) do + @spec command(type :: atom, param_one :: integer, param_two :: integer, extents :: list(integer), bits_size :: integer) :: command_result + def command(cmd, p1 \\ 0, p2 \\ 0, extents \\ [], bits_size \\ 32) + def command(cmd, p1, p2, extents, bits_size) do cmd_code = Pigpiox.Command.code(cmd) - GenServer.call(__MODULE__, {:do_command, cmd_code, p1, p2, extents}) + GenServer.call(__MODULE__, {:do_command, cmd_code, p1, p2, extents, bits_size}) end - @spec handle_call({:do_command, integer, integer, integer, list(integer)}, sender :: term, state) :: {:reply, command_result, state} - def handle_call({:do_command, command, p1, p2, extents}, _, socket) do + @spec handle_call({:do_command, integer, integer, integer, list(integer), integer}, sender :: term, state) :: {:reply, command_result, state} + def handle_call({:do_command, command, p1, p2, extents, bits_size}, _, socket) do + extents_length = length(extents) * round(bits_size / 8) base_msg = <> + extents_length :: native-unsigned-integer-size(32)>> msg = Enum.reduce(extents, base_msg, fn extent, accum -> - accum <> << extent :: native-unsigned-integer-size(32) >> + accum <> << extent :: native-unsigned-integer-size(bits_size) >> end) :ok = :gen_tcp.send(socket, msg) { diff --git a/lib/pigpiox/waveform.ex b/lib/pigpiox/waveform.ex index 9a4ccd0..9bc54ac 100644 --- a/lib/pigpiox/waveform.ex +++ b/lib/pigpiox/waveform.ex @@ -21,6 +21,11 @@ defmodule Pigpiox.Waveform do defstruct gpio_on: 0, gpio_off: 0, delay: 0 end + defmodule ChainElement do + @moduledoc false + defstruct content: [], repeat: 1 + end + @typedoc """ A pulse used in constructing a waveform. Specifies the GPIO that should be turned on, the GPIO that should be turned off, and the delay before the next pulse. @@ -29,6 +34,8 @@ defmodule Pigpiox.Waveform do """ @type pulse :: %Pulse{} + @type chain_element :: %ChainElement{} + @doc """ Adds a list of pulses to the current waveform @@ -42,6 +49,29 @@ defmodule Pigpiox.Waveform do Pigpiox.Socket.command(:waveform_add_generic, 0, 0, extents) end + @doc """ + Chain waveform + + Returns ok or an error. + """ + @spec chain(chain_element :: chain_element) :: {:ok, 0} | {:error, atom} + def chain(chain_element) do + extents = chain_elements_to_list(chain_element) + Pigpiox.Socket.command(:waveform_chain, 0, 0, extents, 8) + end + + defp chain_elements_to_list(wave_id) when is_integer(wave_id), do: [wave_id] + + defp chain_elements_to_list(%ChainElement{content: content, repeat: repeat}) do + content = Enum.flat_map(content, &chain_elements_to_list/1) + [255, 0] ++ content ++ chain_element_repeat_to_list(repeat) + end + + defp chain_element_repeat_to_list(:forever), do: [255, 3] + defp chain_element_repeat_to_list(repeat) do + [255, 1] ++ :erlang.binary_to_list(<>) + end + @doc """ Creates a waveform based on previous calls to `add_...` diff --git a/mix.lock b/mix.lock index bb8c841..3210962 100644 --- a/mix.lock +++ b/mix.lock @@ -1,3 +1,5 @@ -%{"dialyxir": {:hex, :dialyxir, "0.5.1", "b331b091720fd93e878137add264bac4f644e1ddae07a70bf7062c7862c4b952", [], [], "hexpm"}, - "earmark": {:hex, :earmark, "1.2.3", "206eb2e2ac1a794aa5256f3982de7a76bf4579ff91cb28d0e17ea2c9491e46a4", [], [], "hexpm"}, - "ex_doc": {:hex, :ex_doc, "0.16.2", "3b3e210ebcd85a7c76b4e73f85c5640c011d2a0b2f06dcdf5acdb2ae904e5084", [], [{:earmark, "~> 1.1", [hex: :earmark, repo: "hexpm", optional: false]}], "hexpm"}} +%{ + "dialyxir": {:hex, :dialyxir, "0.5.1", "b331b091720fd93e878137add264bac4f644e1ddae07a70bf7062c7862c4b952", [:mix], [], "hexpm"}, + "earmark": {:hex, :earmark, "1.2.3", "206eb2e2ac1a794aa5256f3982de7a76bf4579ff91cb28d0e17ea2c9491e46a4", [:mix], [], "hexpm"}, + "ex_doc": {:hex, :ex_doc, "0.16.2", "3b3e210ebcd85a7c76b4e73f85c5640c011d2a0b2f06dcdf5acdb2ae904e5084", [:mix], [{:earmark, "~> 1.1", [hex: :earmark, repo: "hexpm", optional: false]}], "hexpm"}, +}