Browse files

Expose the setup, setup_gpio and setup_sys

This is a backwards-incompatible change while preparing for wiringPi
version 2 support.  This commit still supports wiringPi version 1.

Previously the setup function - wiringPiSetup() - was called when the
NIF was loaded.  The downside with that approach was that wpi had to
be run as root, although the nice thing was that a user wouldn't have
to worry about calling setup before accessing pins.

After some consideration I've (despite breaking backwards
compatibility) decided to remove the setup from the NIF loading and
let the user choose the method.

Hopefully fixes issue #4.
  • Loading branch information...
1 parent 9b492ff commit d29a866ef3a52e8f7b2218318eabe216c29008b0 @klajo committed Jun 19, 2013
Showing with 107 additions and 13 deletions.
  1. +6 −9
  2. +39 −1 c_src/wpi.c
  3. +62 −3 src/wpi.erl
@@ -24,6 +24,7 @@ useful constants (matches those of WiringPi):
Then you can start setting up the [pins][4] and use them:
Pin = 7,
+ wpi:setup(), % or setup_gpio/0 or setup_sys/0
wpi:pin_mode(Pin, output),
wpi:digital_write(Pin, 1),
@@ -36,11 +37,12 @@ variable like this:
export ERL_LIBS=/home/src/
If you get an error like this, it means that the library doesn't have
-root access. This is solved by running Erlang as root, but please
-consider the security implications of doing so. A future version of
-the library may have another way of handling this.
+root access and you have called `wpi:setup/0' or `wpi:setup_gpio/0'.
+This is solved by running Erlang as root, but please consider the
+security implications of doing so, or by using the `gpio' binary
+followed by `wpi:setup_sys/0' which can be run as a normal user.
- wiringPiSetup: Unable to open /dev/mem: Permission denied
+ wiringPi: Must be root to call wiringPiSetup(). (Did you forget sudo?)
@@ -59,11 +61,6 @@ This NIF is entirely experimental - use at your own risk. It has been
used to both write to (LED), read from (button) pins and control an
LCD successfully, while some functionality may be untested.
-This library currently only supports wiringPiSetup(), not
-wiringPiSetupGpio() nor wiringPiSetupSys(). This means that it's
-currently only possible to use the WiringPi pin numbering scheme
-outlined in the [pins][4] section.
@@ -17,6 +17,7 @@
// along with wpi. If not, see <>.
#include <stdint.h>
+#include <errno.h>
#include "erl_nif.h"
#include <wiringPi.h>
@@ -34,7 +35,40 @@ load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info)
atom_ok = enif_make_atom(env, "ok");
atom_error = enif_make_atom(env, "error");
- return wiringPiSetup(); // returns -1 in case of error ==> loading fails
+ return 0;
+static ERL_NIF_TERM
+mk_setup_return_val(ErlNifEnv* env, int setupReturnCode)
+ ERL_NIF_TERM atom_fail, err_code;
+ if (setupReturnCode == -1)
+ {
+ atom_fail = enif_make_atom(env, "failed_to_setup");
+ err_code = enif_make_int(env, errno);
+ return enif_make_tuple2(env,
+ atom_error,
+ enif_make_tuple2(env, atom_fail, err_code));
+ }
+ return atom_ok;
+static ERL_NIF_TERM
+setup_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+ return mk_setup_return_val(env, wiringPiSetup());
+static ERL_NIF_TERM
+setup_gpio_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+ return mk_setup_return_val(env, wiringPiSetupGpio());
+static ERL_NIF_TERM
+setup_sys_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+ return mk_setup_return_val(env, wiringPiSetupSys());
@@ -426,6 +460,10 @@ spi_setup_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
static ErlNifFunc nif_funcs[] =
+ // setup
+ {"setup_nif", 0, setup_nif},
+ {"setup_gpio_nif", 0, setup_gpio_nif},
+ {"setup_sys_nif", 0, setup_sys_nif},
// the basics: pins and stuff
{"pin_mode_nif", 2, pin_mode_nif},
{"digital_write_nif", 2, digital_write_nif},
@@ -33,6 +33,11 @@
+%% setup
%% the basics: pins and stuff
@@ -99,9 +104,63 @@
on_load() ->
+ %% Force wiringPi to return -1 on initalization failure. In
+ %% wiringPi this was not necessary, but starting at v2 the setup
+ %% functions exit instead. a change between wiringPi v1 and v2.
+ %% Use return values in order to provide more erlangy error handling.
+ os:putenv("WIRINGPI_CODES", "true"),
ok = erlang:load_nif(filename:join(code:priv_dir(wpi), "./wpi_drv"), 0).
-%% the basics: pins and stuff
+-spec(setup() -> ok | {error, term()}).
+%% @doc This initialises wiringPi and assumes that the calling program
+%% is going to be using the wiringPi pin numbering scheme. This is a
+%% simplified numbering scheme which provides a mapping from virtual
+%% pin numbers 0 through 16 to the real underlying Broadcom GPIO pin
+%% numbers. See the pins page on the wiringPi web page for a table
+%% which maps the wiringPi pin number to the Broadcom GPIO pin number
+%% to the physical location on the edge connector.
+%% This function needs to be called with root privileges.
+setup() ->
+ setup_nif().
+-spec(setup_gpio() -> ok | {error, term()}).
+%% @doc This is identical to {@link setup/0}, however it allows the
+%% calling programs to use the Broadcom GPIO pin numbers directly with
+%% no re-mapping.
+%% As for {@link setup/0}, this function needs to be called with root
+%% privileges, and note that some pins are different from revision 1
+%% to revision 2 boards.
+setup_gpio() ->
+ setup_gpio_nif().
+-spec(setup_sys() -> ok | {error, term()}).
+%% @doc This initialises wiringPi but uses the /sys/class/gpio
+%% interface rather than accessing the hardware directly. This can be
+%% called as a non-root user provided the GPIO pins have been exported
+%% before-hand using the gpio program. Pin numbering in this mode is
+%% the native Broadcom GPIO numbers - the same as {@link setup_gpio/0}
+%% above, so be aware of the differences between Rev 1 and Rev 2
+%% boards.
+%% Note: In this mode you can only use the pins which have been
+%% exported via the /sys/class/gpio interface before you run your
+%% program. You can do this in a separate shell-script, or by using
+%% the {@link wpi_gpio} module.
+%% Also note that some functions have no effect when using this mode
+%% as they're not currently possible to action unless called with root
+%% privileges (although you can use {@link wpi_gpio} to set/change
+%% modes if needed).
+setup_sys() ->
+ setup_sys_nif().
+setup_nif() -> ?nif_stub.
+setup_gpio_nif() -> ?nif_stub.
+setup_sys_nif() -> ?nif_stub.
+%% The basics: pins and stuff
-spec pin_mode(wpi_pin_number(), wpi_pin_mode()) -> ok.
%% @doc Set the mode of a pin to either input, output, or PWM
%% output. Note that only WiringPi pin 1 (GPIO 18) supports PWM output.
@@ -284,8 +343,8 @@ shift_in(DataPin, ClockPin, Order)
%% @doc Shift an 8-bit data value out with the data being sent out on
%% DataPin and the clock being sent out on the ClockPin. Order is
%% either `lsb_first' or `msb_first'. Data is clocked out on the
-%% rising or falling edge ie. DataPin is set, then ClockPin is taken
-%% high then low repeated for the 8 bits.
+%% rising or falling edge - ie. DataPin is set, then ClockPin is taken
+%% high then low - repeated for the 8 bits.
shift_out(DataPin, ClockPin, lsb_first, Value) ->
shift_out(DataPin, ClockPin, ?WPI_LSB_FIRST, Value);
shift_out(DataPin, ClockPin, msb_first, Value) ->

0 comments on commit d29a866

Please sign in to comment.