Current section
Files
Jump to
Current section
Files
lib/pwmx.ex
defmodule Pwmx do
@moduledoc """
Pure Elixir library to interact with the sysfs interface to hardware PWM on Linux.
## Usage
### Fully manual
If you wish to just write and read to the sysfs Pwm interface, you can find path helpers in the
Pwmx.Paths module.
iex> File.read(Pwmx.Paths.duty_cycle_path("pwmchip0", "1"))
### Helper functions
If you want an Elixir interface instead of calling File.write/2 or File.read/1 yourself, the module
Pwmx.Backend.Sysfs.Ops has a stateless interface that actually performs the reads and writes.
iex> Pwmx.Backend.Sysfs.Ops.get_duty_cycle("pwmchip0", 1)
### Managed
The Pwmx.Output module can be used to spin up a genserver representing your output, managing its state.
iex> {:ok, pid} = Pwmx.Output.start_link({"virtualchip0", 1})
iex> Pwmx.Output.enable(pid) |> Pwmx.Output.set_period(1_000, :ms)
iex> 1_000_000_000 = Pwmx.Output.get_period(pid)
## Testing & non-linux hosts
In a test or non-linux environment, the Pwmx.Backend.Sysfs module is not used, and calls are instead dispatched
to Pwmx.Backend.Virtual, which keeps track of your operations on PWM outputs with the Pwmx.State struct.
This struct isn't meant to be used directly as it wouldn't be of particular help.
The application supervises a default backend registered as `Pwmx.Backend`. To test code that
drives PWM outputs without stepping on shared state, start your own isolated, configurable
virtual backend and pass it to `Pwmx.Output.start_link/2` (or `Pwmx.open/3`). Each instance holds
its own state, so tests can run concurrently:
iex> {:ok, backend} = Pwmx.Backend.start_link(mode: :virtual, virtual_chips: %{"virtualchip0" => 4})
iex> {:ok, pid} = Pwmx.Output.start_link({"virtualchip0", 1}, backend: backend)
iex> Pwmx.Output.enable(pid) |> Pwmx.Output.set_period(1_000, :ms)
iex> 1_000_000_000 = Pwmx.Output.get_period(pid)
"""
@doc """
Spins up a Pwmx.Output GenServer. Accepts the same options as
`Pwmx.Output.start_link/2`, notably `:backend` to bind it to a specific backend instance.
iex> {:ok, _pid} = Pwmx.open("virtualchip0", 3)
"""
@spec open(binary(), integer(), keyword()) :: {:error, :normal} | {:ok, pid()}
def open(chip, output, opts \\ []), do: Pwmx.Output.start_link({chip, output}, opts)
@doc """
Lists the available outputs. Accepts an optional backend instance, defaulting
to the application-supervised `Pwmx.Backend`.
iex> Pwmx.list_available_outputs()
[
{"virtualchip0", 0},
{"virtualchip0", 1},
{"virtualchip0", 2},
{"virtualchip0", 3}
]
"""
@spec list_available_outputs(GenServer.server()) :: list({binary(), integer()})
def list_available_outputs(backend \\ Pwmx.Backend),
do: Pwmx.Enumerator.list_available_outputs(backend)
end