Current section
Files
Jump to
Current section
Files
README.md
# arb-ex
[](https://hexdocs.pm/arb)
[](http://hex.pm/packages/arb)
An Elixir NIF for controlling the ABACOM CH341A relay board
([documentation](https://hexdocs.pm/arb)).
## Getting started
### Requirements
None. A precompiled NIF with [libusb](https://github.com/libusb/libusb) linked
into it is downloaded at build time, so neither the Rust toolchain nor
`libusb-1.0-0-dev` has to be installed. Artifacts are published for:
- **Linux** — `x86_64` and `aarch64` (glibc and musl), `armv7` (glibc)
- **macOS** — `aarch64`, `x86_64`
Anywhere else, Windows included, the NIF is compiled from source instead, which
needs Rust and a C compiler — still not `libusb`, which is built from the copy
vendored in the crate. `ARB_BUILD=true` forces a source build on a supported
target too.
### Installation
Add `:arb` to your list of dependencies:
```elixir
def deps do
[
{:arb, "~> 0.20"}
]
end
```
## Usage
Open a libusb context **once** and hold it — initialising one is by far the most
expensive part of talking to a board, and `Arb.Usb` has the figures. Naming a
board through it is free and resolves nothing until an operation runs.
```elixir
iex> {:ok, usb} = Arb.open()
iex> board = Arb.board(usb)
iex> Arb.set_relays(board, [1, 4, 7])
:ok
iex> Arb.relays(board)
{:ok, [1, 4, 7]}
iex> Arb.set_relays(board, [])
:ok
```
`Arb.relays/1` is a plain read. `Arb.self_test/1` is the separate health check —
it moves no relay, so it is safe on a board driving live outputs.
With more than one board attached, `Arb.list_boards/1` enumerates them and names
each unambiguously:
```elixir
iex> {:ok, boards} = Arb.list_boards(usb)
iex> Enum.map(boards, &inspect/1)
["#Arb.Board<port 1 (1-1)>", "#Arb.Board<port 5 (1-5)>"]
```
These boards report no serial number and no product strings, so where a board is
plugged in is the only thing telling two of them apart — the `1-1` notation is
the one `lsusb -t` uses, so the two can be read side by side. Label the cables if
the relays drive anything that must not be actuated by mistake.
Migrating from 0.19 — where the three functions took a `:port` option and built a
context per call — is covered in the [changelog](CHANGELOG.md).
## Development
Rust is the only prerequisite. `config/config.exs` forces a source build in this
repo, so a clean clone works without setting anything:
```bash
mix test
```
Tests tagged `:libusb` exercise the real NIF and need a USB bus — not a relay
board. They run by default and are excluded only in CI, which has no bus at all
(see `.github/workflows/ci.yml`).
### Releasing
The precompiled artifacts have to exist before the package is published, because
the checksum file that pins them is built from what the release actually holds.
1. Bump `@version` in `mix.exs` and land it.
2. `git tag v<version> && git push origin master --tags` — this runs
`.github/workflows/release.yml`, which builds every target and attaches the
artifacts to the GitHub release.
3. Wait for all of them. A partial release yields a checksum file missing those
targets, and users on them get a download error at compile time.
4. `mix rustler_precompiled.download Arb.Native --all --print`, which writes
`checksum-Elixir.Arb.Native.exs`.
5. `mix hex.publish`. It refuses to build without that file.
## See also
- [abacom-relay-board](https://github.com/adriankumpf/abacom-relay-board)