An mbed wrapper around the helium-client to communicate with the Helium Atom

Helium for ARM mbed

This code repository exposes an mbed library for the Helium Atom module. The Helium Atom makes it easy to securely connect IoT devices and applications to back-end IoT services.

Getting Started

See a getting started guide on the Helium site.

Supported Boards

The Helium mbed client should work with any mbed board with an available serial port.

Example Setup

Example applications can be found in the mbed Helium team.

Getting Help

If you have any questions or ideas about how to use this code - or any part of Helium - head over to the Helium Community Slack. We're standing by to help.

Contributing

Want to contribute to helium-mbed? That's awesome!

Please see CONTRIBUTING.md in this repository for details.

Files at this revision

API Documentation at this revision

Comitter:
Marc Nijdam
Date:
Tue Sep 05 13:56:03 2017 -0700
Parent:
22:062758492f31
Commit message:
Add configuration API support

Changed in this revision

mbed_lib.json Show annotated file Show diff for this revision Revisions of this file
src/Helium.cpp Show annotated file Show diff for this revision Revisions of this file
src/Helium.h Show annotated file Show diff for this revision Revisions of this file
src/HeliumUtil.cpp Show annotated file Show diff for this revision Revisions of this file
src/HeliumUtil.h Show annotated file Show diff for this revision Revisions of this file
src/helium-client.lib Show annotated file Show diff for this revision Revisions of this file
diff -r 062758492f31 -r cc2c1d1ed159 mbed_lib.json
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mbed_lib.json	Tue Sep 05 13:56:03 2017 -0700
@@ -0,0 +1,14 @@
+{
+  "name" : "Helium",
+  "config" : {
+    "debug" : {
+      "help" : "Print debug output",
+      "macro_name" : "DEBUG",
+      "value": 1
+    },
+    "macros" : [],
+    "target_overrides":
+    {
+    }
+  }
+}
diff -r 062758492f31 -r cc2c1d1ed159 src/Helium.cpp
--- a/src/Helium.cpp	Tue Aug 22 09:43:11 2017 -0700
+++ b/src/Helium.cpp	Tue Sep 05 13:56:03 2017 -0700
@@ -110,15 +110,15 @@
 // Channel
 //
 
-Channel::Channel(Helium * helium)
+Channel::Channel(Helium * h)
 {
-    _helium = helium;
+    helium = h;
 }
 
 int
 Channel::begin(const char * name, uint16_t * token)
 {
-    return helium_channel_create(&_helium->_ctx, name, strlen(name), token);
+    return helium_channel_create(&helium->_ctx, name, strlen(name), token);
 }
 
 int
@@ -143,7 +143,7 @@
 int
 Channel::send(void const * data, size_t len, uint16_t * token)
 {
-    return helium_channel_send(&_helium->_ctx, _channel_id, data, len, token);
+    return helium_channel_send(&helium->_ctx, _channel_id, data, len, token);
 }
 
 
@@ -162,7 +162,7 @@
 int
 Channel::poll_result(uint16_t token, int8_t * result, uint32_t retries)
 {
-    return helium_channel_poll_result(&_helium->_ctx, token, result, retries);
+    return helium_channel_poll_result(&helium->_ctx, token, result, retries);
 }
 
 int
@@ -171,10 +171,224 @@
                    size_t * used,
                    uint32_t retries)
 {
-    return helium_channel_poll_data(&_helium->_ctx,
+    return helium_channel_poll_data(&helium->_ctx,
                                     _channel_id,
                                     data,
                                     len,
                                     used,
                                     retries);
 }
+
+//
+// Config
+//
+
+Config::Config(Channel * channel)
+{
+    _channel = channel;
+}
+
+int
+Config::get(const char * key, uint16_t * token)
+{
+    return helium_channel_config_get(&_channel->helium->_ctx,
+                                     _channel->_channel_id,
+                                     key,
+                                     token);
+}
+
+
+int
+Config::_get(const char *            config_key,
+             enum helium_config_type config_type,
+             void *                  value,
+             size_t                  value_len,
+             void *                  default_value,
+             size_t                  default_value_len,
+             uint32_t                retries)
+{
+    uint16_t token;
+    int      status = get(config_key, &token);
+    int8_t   result = 0;
+    if (helium_status_OK == status)
+    {
+        status = poll_get_result(token,
+                                 config_key,
+                                 config_type,
+                                 value,
+                                 value_len,
+                                 default_value,
+                                 default_value_len,
+                                 &result,
+                                 retries);
+    }
+    if (helium_status_OK == status && result != 0)
+    {
+        status = helium_status_ERR_COMMUNICATION;
+    }
+    return status;
+}
+
+
+/** Context for the poll_get handlder
+ *
+ * An instance of this if filled in the #Config::poll_get_result
+ * implementation and the result of the poll handler is returned in
+ * the status field
+ */
+struct _poll_get_context
+{
+    //! The config key to look for
+    const char * filter_key;
+    //! The config type to check for
+    enum helium_config_type filter_type;
+    //! The destination buffer
+    void * dest;
+    //! The length of the destination buffer
+    size_t dest_len;
+    //! The default value. Assumed to be the same type as filter_type
+    void * default_value;
+    //! The length of the default_value
+    size_t default_value_len;
+    //! The result status of the result handler
+    enum config_poll_get_status status;
+};
+
+
+static bool
+_poll_get_result_handler(void *                  handler_ctx,
+                         const char *            key,
+                         enum helium_config_type value_type,
+                         void *                  value)
+{
+    struct _poll_get_context * ctx = (struct _poll_get_context *)handler_ctx;
+    size_t                     len = ctx->dest_len;
+    void *                     src = value;
+
+    if (strcmp(ctx->filter_key, key) != 0)
+    {
+        // Not the right key, keep going
+        return true;
+    }
+    if (value_type == ctx->filter_type)
+    {
+        // Found and the right type
+        // Use the given value and the destination buffer size
+        ctx->status = config_status_POLL_FOUND;
+    }
+    else
+    {
+        // Found but not the right type, return an error
+        ctx->status = config_status_POLL_ERR_TYPE;
+        // And use the context's default
+        value_type = ctx->filter_type;
+        // Us the default value as the source
+        src = ctx->default_value;
+        // Check for a shorter default value length (only really valid for
+        // strings)
+        len = len > ctx->default_value_len ? ctx->default_value_len : len;
+    }
+
+    switch (value_type)
+    {
+    case helium_config_bool:
+    case helium_config_f32:
+    case helium_config_i32:
+    case helium_config_str:
+        break;
+    case helium_config_null:
+        ctx->status = config_status_POLL_FOUND_NULL;
+        len         = 0;
+        break;
+    }
+    memcpy(ctx->dest, src, len);
+    return false;
+}
+
+int
+Config::poll_get_result(uint16_t           token,
+                        const char *       config_key,
+                        helium_config_type config_type,
+                        void *             value,
+                        size_t             value_len,
+                        void *             default_value,
+                        size_t             default_value_len,
+                        int8_t *           result,
+                        uint32_t           retries)
+{
+    struct _poll_get_context handler_ctx = {
+        .filter_key        = config_key,
+        .filter_type       = config_type,
+        .dest              = value,
+        .dest_len          = value_len,
+        .default_value     = default_value,
+        .default_value_len = default_value_len,
+        .status            = config_status_POLL_FOUND_NULL,
+    };
+    int status = helium_channel_config_get_poll_result(&_channel->helium->_ctx,
+                                                       token,
+                                                       _poll_get_result_handler,
+                                                       &handler_ctx,
+                                                       result,
+                                                       retries);
+    if (helium_status_OK == status)
+    {
+        status = handler_ctx.status;
+    }
+
+    return status;
+}
+
+int
+Config::set(const char *       config_key,
+            helium_config_type config_type,
+            void *             value,
+            uint16_t *         token)
+{
+    return helium_channel_config_set(&_channel->helium->_ctx,
+                                     _channel->_channel_id,
+                                     config_key,
+                                     config_type,
+                                     value,
+                                     token);
+}
+
+int
+Config::poll_set_result(uint16_t token, int8_t * result, uint32_t retries)
+{
+    return helium_channel_config_set_poll_result(&_channel->helium->_ctx,
+                                                 token,
+                                                 result,
+                                                 retries);
+}
+
+int
+Config::_set(const char *            config_key,
+             enum helium_config_type value_type,
+             void *                  value,
+             uint32_t                retries)
+{
+    uint16_t token;
+    int      status = set(config_key, value_type, value, &token);
+    int8_t   result = 0;
+    if (helium_status_OK == status)
+    {
+        status = poll_set_result(token, &result, retries);
+    }
+    if (helium_status_OK == status && result != 0)
+    {
+        status = helium_status_ERR_COMMUNICATION;
+    }
+    return status;
+}
+
+bool
+Config::is_stale()
+{
+    bool result = false;
+    helium_channel_config_poll_invalidate(&_channel->helium->_ctx,
+                                          _channel->_channel_id,
+                                          &result,
+                                          0);
+    return result;
+}
diff -r 062758492f31 -r cc2c1d1ed159 src/Helium.h
--- a/src/Helium.h	Tue Aug 22 09:43:11 2017 -0700
+++ b/src/Helium.h	Tue Sep 05 13:56:03 2017 -0700
@@ -110,6 +110,7 @@
     BufferedSerial    serial;
 
     friend class Channel;
+    friend class Config;
 };
 
 /**
@@ -229,9 +230,342 @@
                   size_t * used,
                   uint32_t retries = HELIUM_POLL_RETRIES_5S);
 
+    /** The Helium Atom this channel uses to communicate. */
+    Helium * helium;
+
   private:
-    Helium * _helium;
     int8_t   _channel_id;
+
+    friend class Config;
+};
+
+
+enum config_poll_get_status
+{
+    config_status_POLL_FOUND      = 0,
+    config_status_POLL_FOUND_NULL = -1,
+    config_status_POLL_ERR_TYPE   = -2,
 };
 
+
+/**
+ * \class Config
+ *
+ * \brief A Channel Configuration
+ *
+ * Channels can have configuration data for the Helium Atom
+ * available. Depending on the IoT platform the configuration data is
+ * a representation of device configuration. Other terms used are
+ * device "twins" or "shadows".
+ *
+ * To use a channel configuration construct it with a channel that
+ * supports configuration and use the get methods to retrieve
+ * configuration values from the IoT platform's device
+ * representation. Use the set methods to set values in the IoT
+ * platform's representation of the device.
+ *
+ * Note that most IoT platforms represent sets and gets in different
+ * namespaces. The get methods represent the IoT platform's desired or
+ * expected namespace of the device, while the set methods are
+ * reflected in the actual, or current namespace of the device.
+ *
+ */
+class Config
+{
+  public:
+    /** Construct a Configuration
+     *
+     * @param channel The channel to get/set configuration with
+     */
+    Config(Channel * channel);
+
+    /** Get an integer configuration value
+     *
+     * @param key The configuration key to get
+     * @param[out] value The target for the received value
+     * @param default_value The default value in case of errors
+     * @param retries The number of times to retry (optional)
+     * @returns 0 on success. If the result is > 0 the result code is
+     *     one of the helium_status_ error codes. If the result is < 0
+     *     it is one of the config_poll_get_status error codes
+     */
+    int get(const char * key,
+            int32_t *    value,
+            int32_t      default_value,
+            uint32_t     retries = HELIUM_POLL_RETRIES_5S)
+    {
+        return _get(key,
+                    helium_config_i32,
+                    value,
+                    sizeof(*value),
+                    &default_value,
+                    sizeof(default_value),
+                    retries);
+    }
+
+    /** Get a float configuration value
+     *
+     * @param key The configuration key to get
+     * @param[out] value The target for the received value
+     * @param default_value The default value in case of errors
+     * @param retries The number of times to retry (optional)
+     * @returns 0 on success. If the result is > 0 the result code is
+     *     one of the helium_status_ error codes. If the result is < 0
+     *     it is one of the config_poll_get_status error codes
+     */
+    int get(const char * key,
+            float *      value,
+            float        default_value,
+            uint32_t     retries = HELIUM_POLL_RETRIES_5S)
+    {
+        return _get(key,
+                    helium_config_f32,
+                    value,
+                    sizeof(*value),
+                    &default_value,
+                    sizeof(default_value),
+                    retries);
+    }
+
+
+    /** Get a boolean configuration value
+     *
+     * @param key The configuration key to get
+     * @param[out] value The target for the received value
+     * @param default_value The default value in case of errors
+     * @param retries The number of times to retry (optional)
+     * @returns 0 on success. If the result is > 0 the result code is
+     *     one of the helium_status_ error codes. If the result is < 0
+     *     it is one of the config_poll_get_status error codes
+     */
+    int get(const char * key,
+            bool *       value,
+            bool         default_value,
+            uint32_t     retries = HELIUM_POLL_RETRIES_5S)
+    {
+        return _get(key,
+                    helium_config_bool,
+                    value,
+                    sizeof(*value),
+                    &default_value,
+                    sizeof(default_value),
+                    retries);
+    }
+
+    /** Get a string configuration value
+     *
+     * @param key The configuration key to get
+     * @param[out] value The target buffer for the received string
+     * @param value_len The length of the available buffer space
+     * @param default_value The default value to use if not found
+     * @param default_value_len The length of the default_value buffer.
+     *     Note: Ensure to include the trailing NULL in this length
+     *     parameter
+     * @param retries The number of times to retry (optional)
+     * @returns 0 on success. If the result is > 0 the result code is
+     *     one of the helium_status_ error codes. If the result is < 0
+     *     it is one of the config_poll_get_status error codes
+     */
+    int get(const char * key,
+            char *       value,
+            size_t       value_len,
+            char *       default_value,
+            size_t       default_value_len,
+            uint32_t     retries = HELIUM_POLL_RETRIES_5S)
+    {
+        return _get(key,
+                    helium_config_str,
+                    value,
+                    value_len,
+                    default_value,
+                    default_value_len,
+                    retries);
+    }
+
+    /** Send a request for a configuration value.
+     *
+     * Getting a configuration value requires sending a request with
+     * the configuration key and then using the resulting token in a
+     * #poll_get_result() call to wait for a response.
+     *
+     * @param key The configuration key to get
+     * @param[out] token The token representing the response.
+     * @returns 0 on success. One of the helium_status_ error
+     *     codes otherwise.
+     */
+    int get(const char * key, uint16_t * token);
+
+    /** Poll the response of a configuration request.
+     *
+     * Polls the given token and validates any response against the
+     * given configuration key and expected type. If these match the
+     * value is copied into the given value buffer.
+     *
+     * Note: The short form methods for getting config values hide
+     * most of the complexity required to make a configuration get
+     * work.
+     *
+     * @param token The token returned from a previous #get() request
+     * @param config_key The configuration key to check for
+     * @param config_type The configuration type to check for
+     * @param[out] value The destination buffer to copy the result into
+     * @param value_len The size of the given destination buffer
+     * @param default_value The default value to use if not found
+     * @param default_value_len The length of the default_value buffer.
+     * @param retries The number of times to retry (optional)
+     * @param[out] result The channel response code.
+     *     0 for no errors, non-0 otherwise
+     * @returns 0 on success. If the result is > 0 the result code is
+     *     one of the helium_status_ error codes. If the result is < 0
+     *     it is one of the config_poll_get_status error codes
+     */
+    int poll_get_result(uint16_t                token,
+                        const char *            config_key,
+                        enum helium_config_type config_type,
+                        void *                  value,
+                        size_t                  value_len,
+                        void *                  default_value,
+                        size_t                  default_value_len,
+                        int8_t *                result,
+                        uint32_t retries = HELIUM_POLL_RETRIES_5S);
+
+    /** Set a float configuration value
+     *
+     * @param key The configuration key to set
+     * @param value The value to set
+     * @param retries The number of times to retry (optional)
+     * @returns 0 on success. One of the helium_status_ error
+     *     codes otherwise.
+     */
+    int set(const char * key, float value, uint32_t retries = HELIUM_POLL_RETRIES_5S)
+    {
+        return _set(key, helium_config_f32, &value, retries);
+    }
+
+    /** Set an integer configuration value
+     *
+     * @param key The configuration key to set
+     * @param value The value to set
+     * @param retries The number of times to retry (optional)
+     * @returns 0 on success. One of the helium_status_ error
+     *     codes otherwise.
+     */
+    int set(const char * key,
+            int32_t      value,
+            uint32_t     retries = HELIUM_POLL_RETRIES_5S)
+    {
+        return _set(key, helium_config_i32, &value, retries);
+    }
+
+    /** Set a boolean configuration value
+     *
+     * @param key The configuration key to set
+     * @param value The value to set
+     * @param retries The number of times to retry (optional)
+     * @returns 0 on success. One of the helium_status_ error
+     *     codes otherwise.
+     */
+    int set(const char * key, bool value, uint32_t retries = HELIUM_POLL_RETRIES_5S)
+    {
+        return _set(key, helium_config_bool, &value, retries);
+    }
+
+    /** Set a string configuration value
+     *
+     * @param key The configuration key to set
+     * @param value The value to set
+     * @param retries The number of times to retry (optional)
+     * @returns 0 on success. One of the helium_status_ error
+     *     codes otherwise.
+     */
+    int set(const char * key,
+            const char * value,
+            uint32_t     retries = HELIUM_POLL_RETRIES_5S)
+    {
+        return _set(key, helium_config_str, &value, retries);
+    }
+
+    /** Set a null configuration value
+     *
+     * @param key The configuration key to set
+     * @param retries The number of times to retry (optional)
+     * @returns 0 on success. One of the helium_status_ error
+     *     codes otherwise.
+     */
+    int set_null(const char * key, uint32_t retries = HELIUM_POLL_RETRIES_5S)
+    {
+        return _set(key, helium_config_null, NULL, retries);
+    }
+
+    /** Send a request for setting a configuration value.
+     *
+     * Setting a configuration value requires sending a request with
+     * the configuration key, the value type and the value and then
+     * using the resulting token in a #poll_set_result() call to wait
+     * for a response.
+     *
+     * @param key The configuration key to set
+     * @param value_type The type of the configuration value to set
+     * @param value A pointer to the value that needs to be st
+     * @param[out] token The token representing the response.
+     * @returns 0 on success. One of the helium_status_ error
+     *     codes otherwise.
+     */
+    int set(const char *       key,
+            helium_config_type value_type,
+            void *             value,
+            uint16_t *         token);
+
+    /** Poll the response of a configuration set request.
+     *
+     * Polls the given token and returns the result code of the set
+     * request.
+     *
+     * Note: The short form methods for getting config values hide
+     * most of the complexity required to make a configuration get
+     * work.
+     *
+     * @param token The token returned from a previous #set() request
+     * @param[out] result A pointer to storage for the response code
+     * @param retries The number of times to retry (optional)
+     * @returns A helium_status result code for the actual
+     *     communication part. If the result is helium_status_OK, the
+     *     result code can be used to check if the set was
+     *     successful. The result code will be 0 on success, and
+     *     non-zero otherwise.
+     */
+    int poll_set_result(uint16_t token,
+                        int8_t * result,
+                        uint32_t retries = HELIUM_POLL_RETRIES_5S);
+
+    /** Check whether configuration values are stale.
+     *
+     * Checks whether there has been a system indication that
+     * configuration attributes may have gone stale.
+     *
+     * When this returns true you should assume that any configuration
+     * values you have previously retrieved are no longer valid.
+     *
+     * @returns true if previous configuration values are stale, false
+     *     if not
+     */
+    bool is_stale();
+
+
+  private:
+    int       _get(const char *            config_key,
+                   enum helium_config_type config_type,
+                   void *                  value,
+                   size_t                  value_len,
+                   void *                  default_value,
+                   size_t                  default_value_len,
+                   uint32_t                retries);
+    int       _set(const char *            config_key,
+                   enum helium_config_type value_type,
+                   void *                  value,
+                   uint32_t                retries);
+    Channel * _channel;
+};
+
+
 #endif // HELIUM_H
diff -r 062758492f31 -r cc2c1d1ed159 src/HeliumUtil.cpp
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/HeliumUtil.cpp	Tue Sep 05 13:56:03 2017 -0700
@@ -0,0 +1,89 @@
+/*
+ * Copyright 2017, Helium Systems, Inc.
+ * All Rights Reserved. See LICENCE.txt for license information
+ */
+
+#include "mbed.h"
+#include "HeliumUtil.h"
+
+int
+report_status(int status, int result)
+{
+    if (helium_status_OK == status)
+    {
+        if (result == 0)
+        {
+            DBG_PRINTF("Succeeded\n");
+        }
+        else
+        {
+            DBG_PRINTF("Failed - %d\n", result);
+        }
+    }
+    else
+    {
+        DBG_PRINTF("Failed\n");
+    }
+    return status;
+}
+
+
+void
+helium_connect(Helium * helium)
+{
+    while (!helium->connected())
+    {
+        DBG_PRINTF("Connecting - ");
+        int status = helium->connect();
+        if (report_status(status) != helium_status_OK)
+        {
+            wait_ms(1000);
+        }
+    }
+}
+
+void
+channel_create(Channel * channel, const char * channel_name)
+{
+    int8_t result;
+    int    status;
+    do
+    {
+        // Ensure we're connected
+        helium_connect(channel->helium);
+        DBG_PRINTF("Creating Channel - ");
+        status = channel->begin(channel_name, &result);
+        // Print status and result
+        if (report_status(status, result) != helium_status_OK)
+        {
+            wait_ms(1000);
+        }
+    } while (helium_status_OK != status || result != 0);
+}
+
+void
+channel_send(Channel *    channel,
+             const char * channel_name,
+             void const * data,
+             size_t       len)
+{
+    int    status;
+    int8_t result;
+
+    do
+    {
+        // Try to send
+        DBG_PRINTF("Sending - ");
+        status = channel->send(data, len, &result);
+        report_status(status, result);
+        // Create the channel if any service errors are returned
+        if (status == helium_status_OK && result != 0)
+        {
+            channel_create(channel, channel_name);
+        }
+        else if (status != helium_status_OK)
+        {
+            wait_ms(1000);
+        }
+    } while (helium_status_OK != status || result != 0);
+}
diff -r 062758492f31 -r cc2c1d1ed159 src/HeliumUtil.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/HeliumUtil.h	Tue Sep 05 13:56:03 2017 -0700
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2017, Helium Systems, Inc.
+ * All Rights Reserved. See LICENCE.txt for license information
+ */
+
+#ifndef HELIUMUTIL_H
+#define HELIUMUTIL_H
+
+#include "Helium.h"
+
+#if DEBUG
+#define DBG_PRINTF(...) printf(__VA_ARGS__)
+#else
+#define DBG_PRINTF(...)
+#endif
+
+int
+report_status(int status, int result = 0);
+
+void
+helium_connect(Helium * helium);
+
+void
+channel_create(Channel * channel, const char * channel_name);
+
+void
+channel_send(Channel *    channel,
+             const char * channel_name,
+             void const * data,
+             size_t       len);
+
+
+#endif /* HELIUMUTIL_H */
diff -r 062758492f31 -r cc2c1d1ed159 src/helium-client.lib
--- a/src/helium-client.lib	Tue Aug 22 09:43:11 2017 -0700
+++ b/src/helium-client.lib	Tue Sep 05 13:56:03 2017 -0700
@@ -1,1 +1,1 @@
-https://github.com/helium/helium-client/#2cd344319cf6e704e9ce994a5f9d758e0386f66c
+https://github.com/helium/helium-client/#210d5d20083c65d53bf9286a26d3b7ae969157c0