Edge Device Daemon Implementation

Before reading this section, we recommend you familiarize yourself with the Afero Platform concepts by reading Introducing Afero.

There are several methods for implementing edge device functionality on the device:

  1. Write a C daemon that uses libevent2 and the Attribute Client API. This method is described in Attribute Daemon Client Implementation.
  2. Write a C daemon that uses libevent2 and the af-edge library. This method is described on this page. The af-edge library is a shim on top of the Attribute Client API.
  3. Write scripts that implement the edge device functionality. This method is described in Scripting Simple Attribute Clients.

From the Afero system point of view, the edge device is implemented by adding attributes to the Afero device Profile. These attributes can have IDs in the range 1 to 1023 and are freely-assignable by you as the developer. The Edge Device daemon bears the responsibility for managing these attributes and connecting them to the hardware.

The Edge Device daemon must run in a libevent2 event loop, and link with the libevent2, af-ipc, af-util, and af-edge libraries.

The af-edge client API is functionally equivalent to the afLib device API. This section translates the concepts from the afLib device API to the af-edge API. For more information about the afLib API please read afLib for Arduino API.

Building

You will need to link with the dynamically-shared library af-edge.so , its dependencies, and libevent:

-laf_edge -laf_attr -laf_ipc -laf_util -levent
You might also need to tell your C compiler to use C99 mode:
-std=c99

The Afero af-edge library provides the init script for the Edge Device daemon (to start up the service), provided you use “edged” as the name of the daemon. Install the edged daemon in the /usr/bin/ directory of the image. If you do not, you must include the init script as part of the daemon development so the daemon initializes during system startup. Note that the init system can be different, depending on the platform (as previously mentioned).

Using the af-edge API

As an example, a minimal implementation of an edge application is provided for you below:

#include <stdint.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>

#include <af_util.h>
#include <aflib.h>
static void event_handler(const af_lib_event_type_t event_type, const af_lib_error_t error, const uint16_t attr_id, const uint16_t value_len, const uint8_t *value)
{
    AFLOG_INFO("EDGE_EVENT:event_type=%d, error=%d", event_type, error);
    switch(event_type) {
        case AF_LIB_EVENT_MCU_DEFAULT_NOTIFICATION :
            /* you can use this notification to initialize the value of an attribute to
        its default value */
            break;
        case AF_LIB_EVENT_MCU_SET_REQUEST : {
            /* the service is requesting to set an MCU attribute to the specified value.*/
            /* you must set set_succeeded to true or false */
            bool set_succeeded = true;
            af_lib_send_set_response(s_af_lib, attr_id, set_succeeded, value_len, value);
            break;
        }
        case AF_LIB_EVENT_ASR_NOTIFICATION :
            /* the attribute with the specified attr_id has been set to the specified value with the specified length */
            break;
        case AF_LIB_EVENT_MCU_SET_REQ_SENT :
            /* attribute update notification for the specified attr_id was sent to ASR*/
            break;
        case AF_LIB_EVENT_MCU_SET_REQ_REJECTION :
            /* attribute update notification for specified attr_id was rejected by ASR*/
 	/* error contains the failure code */
        case AF_LIB_EVENT_ASR_GET_RESPONSE :
            /* the ASR has responded to a get request for one of its attributes */
        default :
            /* this should never happen */
            break;
    }
}

int main(int argc, char **argv) {
    struct event_base *ev = event_base_new();

    AFLOG_INFO("EDGE::connecting to hubby");
    af_lib_set_event_base(ev);

    if ((s_af_lib = af_lib_create_with_unified_callback(event_handler, NULL)) == NULL) {
        perror("aflib_init");
        exit(1);
    }
     event_base_dispatch(ev);
}

If you look at the main function you see the steps in setting up afLib on Linux:

  1. Create an event_base using the event_base_new() function in libevent2.
  2. Call af_lib_set_event_base() to connect afLib to the event base from step 1.
  3. Call aflib_create_with_unified_callback() specifying the event handler. The init function will connect to the hub software (“hubby”) and begin monitoring attribute updates. It writes logs to syslog.
  4. Call event_base_dispatch() or event_base_loop() to run your event loop.

Differences from afLib:

  • There is no loop() call. Instead, you must make sure the libevent2 event loop gets a chance to run, either by calling event_base_dispatch or event_base_loop periodically.
  • There is no ISR or MCU support. The af-edge library assumes that your daemon runs as a normal Linux process on the device.
  • Logging is done to syslog, instead of a supplied stream.