Ownet
The Ownet module provides a client API to interact with an owserver from the OWFS
(1-Wire file system) family. It provides a set of functions to communicate with
owserver, making it possible to read, write, and check the presence of paths in the
1-Wire network.
It's only been tested against the latest version of owserver - v3.2p4.
Installation
The package can be installed by adding ownet to your list of dependencies in mix.exs:
def deps do
[
{:ownet, "~> 0.1.0"}
]
endClient API
start_link/1: This function starts the GenServer with the provided options and links it to the current process.ping/1: This function pings the owserver. It's used to check the connection status.present/2: This function checks if a path is present in the 1-Wire network.dir/2: This function returns the list of devices or properties for the given path.read/2: This function reads the value of a property from a given path.read_float/2: This function reads the value of a property as a float from a given path.read_bool/2: This function reads the value of a property as a boolean from a given path.write/3: This function writes a value to a property at a given path.
Multiple servers
Multiple processes to communicate with different servers can be specified with the :name option.
Connection
The Ownet protocol was initially designed to be "stateless" and would allow only one
command per connection. By default, the :persistence flag is set, which will try
to reuse the socket. If the server indicates persistence is not granted, the socket
is closed and discarded. The client will attempt to seamlessly reconnect when the
next command is issued.
If persistence is granted, but the socket times out, an error will be returned. Attempting the operation again will create a new socket and the operation may succeed.
Errors
Ownet reads and stores the error codes from owserver at initialization, allowing the client to handle error scenarios appropriately. Network socket errors are returned in the form {:error, :inet.posix()}, and usually indicate the server is not reachable or the socket timed out.
Errors returned from the owserver directly are in the form {:error, String.t()}. These indicate that the client is communicating with the owserver, but it did not like the command. The device is no longer being seen by the bus ({:error, "Startup - command line parameters invalid"}), device communication error ({:error, "Device - Device name bad CRC8"}), or the request was malformed for some other reason.
Flags
Flags can be passed during start_link/1 initialization, in which case those flags
will be sent with every command. Flags can also be specified when sending the
command; these will be applied alongside the default ones. If you specify multiple
conflicting flags (e.g. [:c, :f, :k]), results are unspecified.
:persistence- Reuse the network socket for multiple commands. This is a default option.:uncached- Skips the owfs cache. Default owfs configuration caches responses for ~15 seconds; the flag:uncachedwill force owserver to read the value from the sensor. An alternative to using the flag is to prepend "uncached" to a path, e.g.Ownet.read("uncached/42.C2D154000000/temperature"):c,:f,:k,:r- Specifies temperature scale.:cis default.:mbar,:atm,:mmhg,:inhg,:psi- Specifies pressure scale.:mbaris default.:bus_ret- Shows "special" or "hidden" directories indir/2command responses, such as/system/.:fdi,:fi,:fdidc,:fdic,:fidc,:fic- Changes the way one wire addresses are displayed.:fdiis default.
Example
iex(1)> {:ok, pid} = Ownet.start_link(address: 'localhost')
{:ok, #PID<0.151.0>}
iex(32)> Ownet.dir(pid, "/")
{:ok, ["/42.C2D154000000/", "/43.E6ABD6010000/"]}
iex(3)> Ownet.dir(pid, "/42.C2D154000000/")
{:ok,
["/42.C2D154000000/PIO.BYTE", "/42.C2D154000000/PIO.ALL",
"/42.C2D154000000/PIO.A", "/42.C2D154000000/PIO.B",
"/42.C2D154000000/address", "/42.C2D154000000/alias", "/42.C2D154000000/crc8",
"/42.C2D154000000/family", "/42.C2D154000000/fasttemp", ...]}
iex(4)> Ownet.read(pid, "/42.C2D154000000/temperature")
{:ok, " 22.625"}
iex(5)> Ownet.read_float(pid, "/42.C2D154000000/temperature")
{:ok, 22.625}
iex(6)> Ownet.read_float(pid, "/42.C2D154000000/temperature", flags: [:f])
{:ok, 72.725}
iex(7)> Ownet.read(pid, "/42.C2D154000000/PIO.A")
{:ok, "1"}
iex(8)> Ownet.write(pid, "/42.C2D154000000/PIO.A", false)
:ok
iex(9)> Ownet.read(pid, "/42.C2D154000000/PIO.A")
{:ok, "0"}
iex(10)> Ownet.read_bool(pid, "/42.C2D154000000/PIO.A")
{:ok, false}
iex(11)> Ownet.present(pid, "/42.C2D154000000/")
{:ok, true}
iex(12)> Ownet.present(pid, "/NOTPRESENT/")
{:ok, false}Reading temperature sensors simultaneously
1-Wire temperature sensors take upwards of 750ms to read for full 12-bit resolution.
If the temperature sensors are powered (e.g. using data, +3v/+5v, and ground wires), you can signal the sensors to begin their analog-to-digital conversion simultaneously, and then read them quickly without having to wait for the ADC conversion.
Note: Temperature sensors in parasitic mode (e.g. using data and ground wires) can not use this feature.
iex(13)> Ownet.write(pid, "/simultaneous/temperature", true)
:ok
# Wait ~0.75 seconds
iex(14)> Ownet.read(pid, "uncached/42.C2D154000000/latesttemp")
{:ok, " 22.1875"}Tests
Run mix test to run the test suite.
Run mix test test/integration.exs to run an integration test against the owserver instance in the devcontainer.
Alternatively, if you have owserver installed locally, you can run it with owserver --server=localhost --fake=DS2408,DS2408,DS18S20,DS18S20 to run owserver with the same options.