aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore7
-rw-r--r--Makefile34
-rw-r--r--README.md19
-rwxr-xr-xrebarbin0 -> 160323 bytes
-rw-r--r--rebar.config2
-rw-r--r--src/gpio.erl82
-rw-r--r--src/gpioerl.app.src11
-rw-r--r--src/w1therm.erl43
8 files changed, 198 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..5f9ac3f
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,7 @@
+*~
+.depsolver_plt
+.eunit/
+.rebar/
+deps/
+ebin/
+doc/
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..70c0d69
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,34 @@
+ERL ?= erl
+APP := gioerl
+
+.PHONY: deps
+
+all: deps
+ @./rebar compile
+
+deps:
+ @./rebar get-deps
+
+clean:
+ @./rebar clean
+
+distclean: clean
+ @./rebar delete-deps
+
+docs:
+ @erl -noshell -run edoc_run application '$(APP)' '"."' '[]'
+
+test: all
+ @(./rebar skip_deps=true eunit)
+
+tags:
+ find . -name "*.[he]rl" -print | etags -
+
+DEPSOLVER_PLT=$(CURDIR)/.depsolver_plt
+$(DEPSOLVER_PLT):
+ dialyzer --output_plt $(DEPSOLVER_PLT) --build_plt \
+ --apps erts kernel stdlib crypto public_key # -r deps
+
+dialyzer: $(DEPSOLVER_PLT)
+ dialyzer --plt $(DEPSOLVER_PLT) -Wrace_conditions --src src
+
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..b63d967
--- /dev/null
+++ b/README.md
@@ -0,0 +1,19 @@
+GPIOerl
+=======
+
+Simple pure erlang module to read and write GPIO connected devices. This
+modules allow to read and write GPIO pins via the gpio module and to read
+1wire temperature sensors via the w1term module.
+
+Both were tested on the RPI.
+Run
+
+ make docs
+
+to build the documentation.
+
+To use the 1wire functionality you need to have these kernel modules loaded:
+
+ w1-gpio
+ w1-therm
+
diff --git a/rebar b/rebar
new file mode 100755
index 0000000..14e5c22
--- /dev/null
+++ b/rebar
Binary files differ
diff --git a/rebar.config b/rebar.config
new file mode 100644
index 0000000..5223966
--- /dev/null
+++ b/rebar.config
@@ -0,0 +1,2 @@
+%%-*- mode: erlang -*-
+
diff --git a/src/gpio.erl b/src/gpio.erl
new file mode 100644
index 0000000..99da9be
--- /dev/null
+++ b/src/gpio.erl
@@ -0,0 +1,82 @@
+%%% @author Guido Günther <agx@sigxcpu.org>
+%%% @copyright (C) 2016, Guido Günther
+%%% @doc
+%%% Read and write GPIO values from userspace
+%%%
+%%% @reference https://www.kernel.org/doc/Documentation/gpio/sysfs.txt
+%%% @end
+%%% License: LGLv3
+-module(gpio).
+
+-export([export/1,
+ get_exported/0,
+ get_direction/1,
+ read/1,
+ set_direction/2,
+ unexport/1,
+ write/2]).
+
+-define(GPIO_BASE, "/sys/class/gpio/").
+-define(GPIO_EXPORT, ?GPIO_BASE ++ "export").
+-define(GPIO_UNEXPORT, ?GPIO_BASE ++ "unexport").
+
+-type direction() :: in | out | low | high.
+-type pin() :: pos_integer().
+
+% @doc: write to GPIO pin
+-spec write(Pin::pin(), 0 | 1) -> ok | {error, any()}.
+write(Pin, Value) ->
+ Sysfs = pin_sysfs_path(Pin, value),
+ Out = io_lib:format("~p", [Value]),
+ file:write_file(Sysfs, Out).
+
+% @doc: read from GPIO pin
+-spec read(Pin::pin()) -> {ok, 0 | 1} | {error, atom()}.
+read(Pin) ->
+ Sysfs = pin_sysfs_path(Pin, value),
+ {ok, Data} = file:read_file(Sysfs),
+ case Data of
+ <<"0">> -> {ok, 0};
+ <<"1">> -> {ok, 1};
+ _-> {error, badval}
+ end.
+
+% @doc: export GPIO pin
+-spec export(Pin::pin()) -> ok | {error, any()}.
+export(Pin) ->
+ Out = io_lib:format("~p", [Pin]),
+ file:write_file(?GPIO_EXPORT, Out).
+
+% @doc: get list of exported GPIO pins
+-spec get_exported() -> {ok, list(pin())}.
+get_exported() ->
+ {ok, Files} = file:list_dir(?GPIO_BASE),
+ {ok, R} = re:compile("^gpio[0-9]+$"),
+ PinNames = [ X || X <- Files, nomatch /= re:run(X, R)],
+ {ok, [ Num || {Num, []} <-
+ [ string:to_integer(lists:nthtail(4, Pin)) || Pin <- PinNames ]]}.
+
+% @doc: unexport GPIO pin
+-spec unexport(Pin::pin()) -> ok | {error, any()}.
+unexport(Pin) ->
+ Out = io_lib:format("~p", [Pin]),
+ file:write_file(?GPIO_UNEXPORT, Out).
+
+% @doc: set GPIO pin direction
+-spec set_direction(Pin::pin(), D::direction()) -> ok | {error, any()}.
+set_direction(Pin, D) ->
+ Sysfs = pin_sysfs_path(Pin, direction),
+ Out = io_lib:format("~p", [D]),
+ file:write_file(Sysfs, Out).
+
+% @doc: get GPIO pin direction
+-spec get_direction(Pin::pin()) -> {ok, direction()} | {error, any()}.
+get_direction(Pin) ->
+ Sysfs = pin_sysfs_path(Pin, direction),
+ {ok, Data} = file:read_file(Sysfs),
+ {ok, list_to_atom(string:strip(binary:bin_to_list(Data), right, $\n))}.
+
+%% Private functions
+-spec pin_sysfs_path(Pin::pin(), atom() | binary()) -> string().
+pin_sysfs_path(Pin, File) ->
+ io_lib:format(?GPIO_BASE ++ "gpio~p/~p", [Pin, File]).
diff --git a/src/gpioerl.app.src b/src/gpioerl.app.src
new file mode 100644
index 0000000..16c5342
--- /dev/null
+++ b/src/gpioerl.app.src
@@ -0,0 +1,11 @@
+{application, gpioerl,
+ [
+ {description, ""},
+ {vsn, "1"},
+ {registered, []},
+ {applications, [
+ kernel,
+ stdlib
+ ]},
+ {env, []}
+ ]}.
diff --git a/src/w1therm.erl b/src/w1therm.erl
new file mode 100644
index 0000000..a9810f3
--- /dev/null
+++ b/src/w1therm.erl
@@ -0,0 +1,43 @@
+%%% @author Guido Günther <agx@sigxcpu.org>
+%%% @copyright (C) 2016, Guido Günther
+%%% @doc
+%%% Read and write GPIO values from userspace
+%%%
+%%% @reference http://www.raspberrypi-spy.co.uk/2013/03/raspberry-pi-1-wire-digital-thermometer-sensor/
+%%% @end
+%%% License: LGLv3
+-module(w1therm).
+
+-define(W1_BASE, "/sys/bus/w1/devices/").
+
+-export([get_therms/0,
+ read_temp/1]).
+
+-type therm() :: string().
+
+% @doc: Get 1wire temperature famliy sensors
+-spec get_therms() -> {ok, list(therm())} | {error, atom()}.
+get_therms() ->
+ case file:list_dir(?W1_BASE) of
+ {error, enoent} -> {ok, []};
+ {error, Error} -> {error, Error};
+ {ok, Files} ->
+ {ok, R} = re:compile("^[0-9]{2}-.*"),
+ Names = [ X || X <- Files, nomatch /= re:run(X, R)],
+ {ok, Names}
+ end.
+
+% @doc: Read temperature from 1wire therm device in ⁰C
+-spec read_temp(Therm::therm()) -> {ok, float()} | {error, atom()}.
+read_temp(Therm) ->
+ Sysfs = therm_sysfs_path(Therm, w1_slave),
+ {ok, Data} = file:read_file(Sysfs),
+ Last = string:sub_word(binary:bin_to_list(Data), 2, $\n),
+ TempStr = string:sub_word(Last, 2, $=),
+ {Temp, []} = string:to_integer(TempStr),
+ {ok, Temp / 1000.0}.
+
+% private functions
+-spec therm_sysfs_path(Therm::therm(), atom() | binary()) -> string().
+therm_sysfs_path(Therm, File) ->
+ io_lib:format(?W1_BASE ++ Therm ++ "/~p", [File]).