Skip to content

Add Waveform Chain command #12

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 43 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
17 changes: 9 additions & 8 deletions lib/pigpiox/socket.ex
Original file line number Diff line number Diff line change
Expand Up @@ -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 = <<command :: native-unsigned-integer-size(32),
p1 :: native-unsigned-integer-size(32),
p2 :: native-unsigned-integer-size(32),
length(extents) * 4 :: native-unsigned-integer-size(32)>>
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)
{
Expand Down
30 changes: 30 additions & 0 deletions lib/pigpiox/waveform.ex
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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

Expand All @@ -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(<<repeat::little-unsigned-integer-size(16)>>)
end

@doc """
Creates a waveform based on previous calls to `add_...`

Expand Down
8 changes: 5 additions & 3 deletions mix.lock
Original file line number Diff line number Diff line change
@@ -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"},
}