Pin is a class for pin manipulation: you can read, write, turnOn, turnOff, toggle and a lot more. Please, stop writing horrible code like digitalWrite(led, HIGH) and start writing led.turnOn() instead.

Pin is actually an abstract class, so you won't use it directly, but through its specialized implementations:

  1. DigitalIn
  2. DigitalOut
  3. AnalogIn
  4. AnalogOut

Import the library

To follow this tutorial along you need to first install the Eloquent library
#import <eIO.h>

using namespace Eloquent::Pin;

If the namespace stuff is new to you, here I'll briefly say that it is used to avoid name collisions among different libraries. This seems to be an alien topic in the Arduino world and I can't really explain why.

99% of the libraries out there deal with this problem in one of two modes:

  1. ignoring it altogether, so you have an MPU6050.h library, which elects itself as the only one implementation possible to access the MPU6050 accelerometer in the world
  2. prefixing each library file, so you get the Adafruit_Si7021 class
The case for Adafruit_Si7021 should not exist in my opinion: use the damn namespaces! Click To Tweet

With namespaces, it would become:

using namespace Adafruit;

Si7021 si;

How to use

First of all, all the 4 implementations accept a single constructor argument: the pin number.

 DigitalOut led(BUILTIN_LED);
 DigitalIn pushButton(10);
 AnalogIn potentiometer(A0);
 AnalogOut pwmLed(8);

Then it is good practice to init your pins in the setup.

 void setup() {
    led.begin();
    pushButton.begin();
    potentiometer.begin();
    pwmLed.begin();
 }

All the 4 classes let you ask for the pin current value via the value() method:

 void test() {
    // DigitalIn returns the last read value, as 0/1
    digitalIn.value();

    // AnalogIn returns the last read value, in the range [0, 1024]
    analogIn.value();

    // DigitalOut returns the last written value, as 0/1
    digitalOut.value();

    // AnaloglOut returns the last written value, in the range [0, 255]
    analogOut.value();
 }

At this point each class will provide its specialized methods.

DigitalIn

void test() {
    // configure pin as INPUT_PULLUP
    pin.pullup();

    // configure pin as Active Low 
    // that is, pin is ON when digitalRead() is LOW
    pin.activeLow();

    // read and update pin value
    pin.read();

    // test if pin is on (doesn't read the pin)
    pin.isOn();

    // test if pin is off (doesn't read the pin)
    pin.isOff();

    // test if pin value changed from last reading
    pin.changed();
}

DigitalOut

void test() {
    // set pin as Active Low
    // that is, turnOn writes LOW
    pin.activeLow();

    // turn pin on
    pin.turnOn();

    // turn pin off
    pin.turnOff();

    // toggle
    pin.toggle();

    // blink N times at intervals of X milliseconds
    pin.blink(N, X);
}

AnalogIn

void test() {
    // read current pin value
    pin.read();

    // get pin previous value
    pin.prev();

    // get difference between current value and previous
    pin.delta();

    // get absolute value of delta()
    pin.absDelta();
}

AnalogOut

void test() {
    // write value X to pin
    pin.write(X);
}

If you don't believe a whole class is worthwhile to work with pins, I'll show a few use cases to illustrate my point.

Use case #1: active low LED

The ESP8266 has a builtin LED you can control, but it is an active low one: it will turn on when you write LOW. In this case, digitalWrite(BUILTIN_LED, LOW) can be misleading regarding your actual intentions.

It doesn't look intuitive, it doesn't look eloquent! builtinLed.turnOn() does, however. All you need to get it working correctly is calling builtinLed.activeLow() once in your setup.

// BEFORE
void loop() {
    // to turn the builtin LED on
    digitalWrite(led, LOW);
}
// AFTER
DigitalOut buildtinLed;

void setup() {
    builtinLed.activeLow();
}

void loop() {
    // to turn the builtin LED on
    builtinLed.turnOn();
}

Use case #2: toggle

If you need to toggle the current state of a digital output, you need an helper variable to keep track of the state and remember to always update that variable when you write to the output.
With a class, the state is tightly bound to the instance, so you have a single source of truth: turnOn(), turnOff() and toggle() will take care of updating the inner state accordingly.

// BEFORE
#define LED 1

bool ledState = true;

loop() {
    digitalWrite(LED, ledState);
    ledState = !ledState
}
// AFTER
DigitalOut led(1);

void loop() {
    led.toggle();
}

Use case #3: analog delta

What if you have an analog input and want to know if its valued changed by at least X from your latest reading? You would need an helper variable again.

Now imagine if you have 5 analog inputs you want to track: you'll end up with 10 variables and of course you have again to always keep both in sync.
AnalogIn conveniently provides delta() and absDelta() methods that give you the change from the previous reading and will always be in sync. Awesome!

// BEFORE
#define INPUT1 A1
#define INPUT2 A2

uint16_t current1, prev1;
uint16_t current2, prev2;

void loop() {
    prev1 = current1;
    current1 = analogRead(INPUT1);
    prev2 = current2;
    current2 = analogRead(INPUT2);

    if (abs(current1 - prev1) > THRESHOLD)
        ...
// AFTER
AnalogIn input1(A1), input2(A2);

void loop() {
    input1.read();
    input2.read();

    if (input1.absDelta() > THRESHOLD)
        ...
}

Did you find this tutorial useful? Was is it easy to follow or did I miss something? Let me know in the comments so I can keep improving the blog.

Help the blow grow