afLib for Arduino API

afLib for Arduino is an Arduino library that can be used with Arduino 1.6 and later to communicate with an Afero radio module over serial interfaces such as SPI and UART. The library provides a simple, lightweight API that provides all the functionality you should need to implement your application. You can download the library from http://github.com/aferodeveloper/afLib.

The library includes the following functions:

We also describe two callback functions you should write to work with afLib:

afLib()

Description

Create an instance of afLib for the application. The parameters include some callbacks to let your code know when things have changed, and some pointers used for serial communication.

There is a specific form of afLib() for each of the communication protocols available, SPI and UART. Be sure to use the form corresponding to the protocol you use in your project.


SPI Syntax

afLib(mcuInterrupt, ISRWrapper, attrSetHandler, attrNotifyHandler, *serial, *theSPI)

Parameters

mcuInterrupt The number of the pin to be used to interrupt the Arduino when ASR needs to communicate. This pin number depends upon the Arduino board you are using. It is 16 for Teensy and 2 for Uno.
ISRWrapper A wrapper function used to pass the MCU interrupt on to afLib.
attrSetHandler The callback to request your MCU change one of its local attributes.
attrNotifyHandler The callback to let your MCU code know that one of the ASR attributes has changed.
serial Reference to object which will handle serial communications for debug output. (Typically Serial)
theSPI Reference to an object to handle SPI communications.

UART Syntax

afLib(attrSetHandler, attrNotifyHandler, *serial, *theUART)

Parameters

attrSetHandler The callback to request your MCU change one of its local attributes.
attrNotifyHandler The callback to let your MCU code know that one of the ASR attributes has changed.
serial Reference to object which will handle serial communications for debug output. (Typically Serial)
theUART Reference to an object to handle UART communications.

Returns

Reference to an object to handle communications.

SPI Example

The SPI example below shows how we create a new instance of afLib for SPI. We instantiate an ArduinoSPI object, supplying the pin number for Chip Select. That ArduinoSPI object is supplied as a parameter to afLib(), along with INT_PIN as the MCC interrupt pin, ISRWrapper as the wrapper that passes the interrupt on to afLib, and attrSetHandler and attrNotifyHandler as the two callback methods. Note that we #include both SPI.h and ArduinoSPI.h. It's good practice to #define CS_PIN and INT_PIN.

#include <iafLib.h>
#include <SPI.h>
#include <ArduinoSPI.h>

// Pin Defines assume Arduino Uno
#define CS_PIN                    10
#define INT_PIN                   2

iafLib *aflib;

bool attrSetHandler(uint8_t requestId, const uint16_t attributeId, const uint16_t valueLen, const uint8_t *value) {
  // Any application-specific actions
  return true;
}

void attrNotifyHandler(const uint8_t requestId, const uint16_t attributeId, const uint16_t valueLen, const uint8_t *value) {
  // Any application-specific actions
}

void ISRWrapper() {
  // Pass the MCU interrupt on to afLib
  aflib->mcuISR();
}

void setup() {
  // Initialize afLib
  afTransport *arduinoSPI = new ArduinoSPI(CS_PIN);
  aflib = iafLib::create(digitalPinToInterrupt(INT_PIN), ISRWrapper, attrSetHandler, attrNotifyHandler, &Serial, arduinoSPI);
}

void loop() {
  // Give afLib processing time by calling aflib->loop() in sketch's loop()
  aflib->loop();
}

UART Example

In the UART example, we create a new instance of afLib for UART. In this case we instantiate an ArduinoUART object, with parameters including the pins for RX and TX, along with a reference to Serial (used for logging). ISRWrapper is again the wrapper that passes the interrupt on to afLib, and attrSetHandler and attrNotifyHandler are the two callback methods. When using UART, we must #include ArduinoUART.h, and we #defined RX_PIN and TX_PIN.

#include <iafLib.h>
#include <ArduinoUART.h>

#define RX_PIN                7
#define TX_PIN                8

iafLib *aflib;

bool attrSetHandler(uint8_t requestId, const uint16_t attributeId, const uint16_t valueLen, const uint8_t *value) {
  // Any application-specific actions
  return true;
}

void attrNotifyHandler(const uint8_t requestId, const uint16_t attributeId, const uint16_t valueLen, const uint8_t *value) {
  // Any application-specific actions
}

void ISRWrapper() {
  // Pass the MCU interrupt on to afLib
  aflib->mcuISR();
}

void setup() {
  // Initialize afLib
  afTransport *arduinoUART = new ArduinoUART(RX_PIN, TX_PIN, &Serial);
  aflib = iafLib::create(attrSetHandler, attrNotifyHandler, &Serial, arduinoUART);
}

void loop() {
  // Give afLib processing time by calling aflib->loop() in sketch's loop()
  aflib->loop();
}

The code examples for the functions below illustrate the use of SPI communication. To use UART instead, only the minimal changes in afLib instantiation, as highlighted above, would be required.


getAttribute()

Description

Request the value of a specified attribute. The getAttribute() call only queues the request; your attrNotifyHandler() function will be called when the value is returned by ASR. The attribute may or may not have a default value defined in Profile Editor. When called:

  • If no default value has been set, getAttribute will return length = 0, value = NULL.
  • If the default value has been set and the attribute has not been modified, getAttribute will return the default value.
  • If the attribute has been modified by the service or MCU, getAttribute will return the most recent value set by either the service or the MCU.

Syntax

getAttribute(attributeId)

Parameters

attributeId The attribute id for which you are requesting the value.

Returns

A result code indicating whether request was queued successfully.

Example

In the example below, getAttribute() is called in loop() to obtain the value of AF_MY_ATTRIBUTE. If the call returns success, we set shouldGetAttr to false, since we won't need to call getAttribute() again. The value of the attribute of interest is actually obtained when attrNotifyHandler() is called. In that callback, we can use the attribute ID argument to select which action should be taken (in this case, we simply print to console.)

#include <SPI.h>
#include <iafLib.h>
#include <ArduinoSPI.h>
#include "profile/device-description.h"

// Pin Defines assume Arduino Uno
#define CS_PIN               10
#define INT_PIN               2

boolean shouldGetAttr = true;
iafLib *aflib;

bool attrSetHandler(uint8_t requestId, const uint16_t attributeId, const uint16_t valueLen, const uint8_t *value) {
  // Any application-specific actions
  return true;
}

void attrNotifyHandler(const uint8_t requestId, const uint16_t attributeId, const uint16_t valueLen, const uint8_t *value) {
  if (attributeId == AF_MY_ATTRIBUTE) {
	Serial.print("*attrNotifyHandler*	 Attr id: "); Serial.print(attributeId); Serial.print(" value: "); Serial.println(*value);
  }
}

void ISRWrapper() {
  // Pass the MCU interrupt on to afLib
  aflib->mcuISR();
}

void setup() {
  Serial.begin(115200);
  // Initialize afLib
  ArduinoSPI *arduinoSPI = new ArduinoSPI(CS_PIN);
  aflib = iafLib::create(digitalPinToInterrupt(INT_PIN), ISRWrapper, attrSetHandler, attrNotifyHandler, &Serial, arduinoSPI);
}

void loop() {
  if (shouldGetAttr) {
    // Attribute name definitions are provided by device-description.h from your profile
    int result = aflib->getAttribute(AF_MY_ATTRIBUTE);
    if (result == afSUCCESS) {
      shouldGetAttr = false;
    }
    else {
      Serial.print("*getAttribute*	 return error: "); Serial.println(result);
    }
  }

  // Give afLib processing time by calling aflib->loop() in sketch's loop()
  aflib->loop();
}

loop()

Description

Give afLib processing time.

Syntax

loop()

Parameters

None.

Returns

None.

Example

The loop() example serves to reiterate that afLib->loop() should be called within the loop() method of your MCU code, to allow the afLib state machine to run, and call attrSetHandler and attrNotifyHandler callbacks when appropriate.

iafLib *aflib;

void ISRWrapper() {
  // Pass the MCU interrupt on to afLib
  aflib->mcuISR();
}

void setup() {
  Serial.begin(115200);
  // Initialize afLib
  ArduinoSPI *arduinoSPI = new ArduinoSPI(CS_PIN);
  aflib = iafLib::create(digitalPinToInterrupt(INT_PIN), ISRWrapper, attrSetHandler, attrNotifyHandler, &Serial, arduinoSPI);
}

void loop() {
  // Give afLib processing time by calling aflib->loop() in sketch's loop()
  aflib->loop();
}

mcuISR()

Description

Pass the MCU interrupt on to afLib. Because afLib is a class, the member method mcuISR can’t be used with attachInterrupt. Instead, you must define a wrapper function that simply passes control on to afLib.

Syntax

mcuISR()

Parameters

None.

Returns

None.

Example

ISRWrapper passes control to afLib by calling mcuISR().

iafLib *aflib;

void ISRWrapper() {
  // Pass the MCU interrupt on to afLib
  aflib->mcuISR();
}

void setup() {
  // Initialize afLib
  ArduinoSPI *arduinoSPI = new ArduinoSPI(CS_PIN);
  aflib = iafLib::create(digitalPinToInterrupt(INT_PIN), ISRWrapper, attrSetHandler, attrNotifyHandler, &Serial, arduinoSPI);
}

void loop() {
  // Give afLib processing time by calling aflib->loop() in sketch's loop()
  aflib->loop();
}

setAttribute()

Description

Request to change the value of a specified ASR attribute. This call only queues the request. Your attrNotifyHandler() function will be called when the value has been changed by ASR.

Syntax

There are several versions of the setAttribute API that allow you to pass values of different byte-lengths to afLib. Here are the currently supported types:

int setAttributeBool(uint16_t attrId, const bool value);
int setAttribute8(uint16_t attrId, const int8_t value);
int setAttribute16(uint16_t attrId, const int16_t value);
int setAttribute32(uint16_t attrId, const int32_t value);
int setAttribute64(uint16_t attrId, const int64_t value);
int setAttributeStr(uint16_t attrId, String &value);
int setAttributeCStr(uint16_t attrId, const int16_t valueLen, const char *value);
int setAttributeBytes(uint16_t attrId, const int16_t valueLen, const uint8_t *value);

Parameters

attrId The attribute id for which you are requesting the value.
valueLen The size in bytes of the attribute value. This parameter only needed when value size is not indicated by value type.
value The new value for the attribute.

Returns

A result code indicating whether request was queued successfully.

Example

Within loop(), we call setAttribute() to set the value of the GPIO 1 attribute of ASR. The ASR will change the value of the GPIO attribute to 1, set the level of the GPIO appropriately, and then call the MCU’s attrNotifyHandler() method, allowing us perform any actions desired once we know the operation is complete.

#include <SPI.h>
#include <iafLib.h>
#include <ArduinoSPI.h>
#include "profile/device-description.h"

// Pin Defines assume Arduino Uno
#define CS_PIN					10
#define INT_PIN				   2

boolean shouldSetAttr = true;
uint8_t value = 1;
iafLib *aflib;

bool attrSetHandler(uint8_t requestId, const uint16_t attributeId, const uint16_t valueLen, const uint8_t *value) {
  // Any application-specific actions

  return true;
}

// attrNotifyHandler() is called when either an ASR module attribute has been changed or in response to a getAttribute operation
// Add any actions required by your specific application in those contexts
void attrNotifyHandler(const uint8_t requestId, const uint16_t attributeId, const uint16_t valueLen, const uint8_t *value) {

  // Any application-specific actions
  if (attributeId == AF_GPIO_1) {
	Serial.print("*attrNotifyHandler*	Attr id: "); Serial.print(attributeId); Serial.print(" value: "); Serial.println(*value);
  }
}

void ISRWrapper() {
  // Pass the MCU interrupt on to afLib
  aflib->mcuISR();
}

void setup() {
  Serial.begin(115200);
  // Initialize afLib
  ArduinoSPI *arduinoSPI = new ArduinoSPI(CS_PIN);
  aflib = iafLib::create(digitalPinToInterrupt(INT_PIN), ISRWrapper, attrSetHandler, attrNotifyHandler, &Serial, arduinoSPI);
}

void loop() {
  if (shouldSetAttr) {
	// Attribute name definitions are provided by device-description.h from your profile
	int result = aflib->setAttribute16(AF_GPIO_1, value);
	if (result == afSUCCESS) {
	  shouldSetAttr = false;
	}
	else {
	  Serial.print("*setAttribute*	 return error: "); Serial.println(result);
	}
  }

  // Give afLib processing time by calling aflib->loop() in sketch's loop()
  aflib->loop();
}

attrSetHandler()

Description

Callback that allows ASR to request an MCU attribute be changed. You should define this function in your MCU firmware to perform application-specific actions your code must take (e.g., updating the state of the hardware), in light of the attribute value change.

Syntax

attrSetHandler(requestId, attributeId, valueLen, value)

Parameters

requestId The request id for this request.
attributeId The id of the attribute that is to be set.
valueLen The size in bytes of the attribute value.
value The new value of the attribute.

Returns

  • True if the host MCU was able to update the attribute internally.
  • False if the host MCU was unable.

Example

attrSetHandler() is called by afLib to allow us to update the device state to correspond to the change in attribute value. In this specific example, we change the LED state in response to an attribute change (which may have been triggered, for example, by a user action in the mobile application UI.)

#include <SPI.h>
#include <iafLib.h>
#include <ArduinoSPI.h>
#include "profile/device-description.h"

// Pin Defines assume Arduino Uno
#define CS_PIN					10
#define INT_PIN				   2

iafLib *aflib;

// attrSetHandler() is called when a client changes an attribute;
// define this callback to include any MCU actions that should be performed in that context.
bool attrSetHandler(uint8_t requestId, const uint16_t attributeId, const uint16_t valueLen, const uint8_t *value) {
  Serial.print("*attrSetHandler*	 id: "); Serial.print(attributeId); Serial.print(" value: "); Serial.println(*value);

  // Any application-specific actions required to update the actual device state to correspond to the attribute value
  if (attributeId == AF_LED_ATTR) {
	digitalWrite(LED1_PIN, *value);
  }

  // Return value from this call informs service whether or not MCU was able to update local state to reflect the
  // attribute change that triggered callback.
  // Return false here if your MCU was unable to update local state to correspond with the attribute change that occurred;
  // return true if MCU was successful.
  return true;
}

void attrNotifyHandler(const uint8_t requestId, const uint16_t attributeId, const uint16_t valueLen, const uint8_t *value) {
  // Any application-specific actions
}

void ISRWrapper() {
  // Pass the MCU interrupt on to afLib
  aflib->mcuISR();
}

void setup() {
  Serial.begin(115200);

  pinMode(LED1_PIN, OUTPUT);

  // Initialize afLib
  ArduinoSPI *arduinoSPI = new ArduinoSPI(CS_PIN);
  aflib = iafLib::create(digitalPinToInterrupt(INT_PIN), ISRWrapper, attrSetHandler, attrNotifyHandler, &Serial, arduinoSPI);
}

void loop() {
  // Give afLib processing time by calling aflib->loop() in sketch's loop()
  aflib->loop();
}

attrNotifyHandler()

Description

Application callback that allows afLib to notify that an attribute has changed. This method will be called in response to a getAttribute call from the application and whenever a ASR module attribute changes.

Syntax

attrNotifyHandler(requestId, attributeId, valueLen, value)

Parameters

requestId The request id for this request.
attributeId The id of the attribute that has changed.
valueLen The size in bytes of the attribute value.
value The new value for the attribute.

Returns

None.

Example

attrNotifyHandler() will be called by afLib, in response to either getAttribute() or an attribute change on ASR. If in response to a getAttribute() call, the MCU code will have the attribute value (via argument passed with attrNotifyHandler()). If due to an attribute changing state on ASR, the MCU code can use the passed-in attributeId to determine which attribute changed and respond appropriately. In the example above we simply print passed-in parameters.

#include <SPI.h>
#include <iafLib.h>
#include <ArduinoSPI.h>
#include "profile/device-description.h"

// Pin Defines assume Arduino Uno
#define CS_PIN					10
#define INT_PIN				   2

iafLib *aflib;

bool attrSetHandler(uint8_t requestId, const uint16_t attributeId, const uint16_t valueLen, const uint8_t *value) {
    // Any application-specific actions
}

// attrNotifyHandler() is called when either an ASR module attribute has been changed or in response to a getAttribute operation.
// Add any actions required by your specific application in those contexts.
void attrNotifyHandler(const uint8_t requestId, const uint16_t attributeId, const uint16_t valueLen, const uint8_t *value) {
  Serial.print("*attrNotifyHandler*	 Attr id: "); Serial.print(attributeId); Serial.print(" value: "); Serial.println(*value);
}

void ISRWrapper() {
  // Pass the MCU interrupt on to afLib
  aflib->mcuISR();
}

void setup() {
  Serial.begin(115200);

  pinMode(LED1_PIN, OUTPUT);

  // Initialize afLib
  ArduinoSPI *arduinoSPI = new ArduinoSPI(CS_PIN);
  aflib = iafLib::create(digitalPinToInterrupt(INT_PIN), ISRWrapper, attrSetHandler, attrNotifyHandler, &Serial, arduinoSPI);
}

void loop() {
  // Give afLib processing time by calling aflib->loop() in sketch's loop()
  aflib->loop();
}

See Also

getAttribute()