Cloud

Description

The Cloud library connects an OpenIndus module to the OpenIndus cloud platform. It exposes your application data as cloud variables, each backed by an MQTT topic, and takes care of device provisioning, secure transport, over-the-air firmware updates and reconnection automatically.

The communication uses MQTT over WebSocket (TLS). A dedicated background task handles the whole lifecycle, so your application code stays minimal: declare the variables, call Cloud::begin(), and read/write the values in your loop(). Nothing blocks.

Note

The cloud requires an active internet connection. On a OI-Core module this is typically provided by the cellular modem or by Ethernet. Make sure the network is up before (or shortly after) calling begin().

begin() can be called without argument, in which case the default host oicloud.openindus.com is used; pass a host explicitly to target another instance.

Variables

A cloud variable has:

  • a name, which is also the leaf of its MQTT topic,

  • a type: bool, int, float or std::string (BoolVariable, IntVariable, FloatVariable, StringVariable),

  • an update method (UpdateMethod):

    • SYNCHRONOUS — published once every refreshInterval milliseconds,

    • ASYNCHRONOUS — published when the value changes, rate-limited to at most once every refreshInterval ms and at least once every maxRefreshInterval ms. A maxRefreshInterval of 0 disables the periodic heartbeat, so the variable is published only when its value actually changes,

  • an update type / direction (UpdateType):

    • PUBLISH — the module publishes the topic,

    • SUBSCRIBE — the module subscribes to the topic (use onReceive() to get a callback),

    • BOTH — the module both publishes and subscribes.

Every publisher variable is re-announced on each new MQTT session, so the cloud always sees a fresh value after a reconnection.

Default variables

The following variables are created and managed automatically to provide base functionality. They live under the def topic type (see MQTT topics) and, when they publish, they are all ASYNCHRONOUS with no heartbeat: they are emitted only when their value changes.

Name

Type

Direction

Function

ota

string

subscribe (+ progress published on the same topic)

Firmware update channel, see Firmware update (OTA) below

log

string

publish

Journalises info/errors from the module (see Cloud::log())

version

string

publish

Published once on connection: the projectVersion passed to the constructor, or the firmware software version when none was given

modules

string

publish

Published once on connection: a JSON array describing the local board and, on a master, every module discovered on the rail. Each entry holds serial_number, position, version, board_type, variant and timestamp. The local board is reported at position 1023 so it always comes first

Provisioning

The preferred usage only specifies the platform credentials; the library obtains a device uuid and token on its own and stores them in NVS. On the first boot (or when the stored platform UUID no longer matches), the module:

  1. registers itself against the platform (POST /api/v1/platform/{platform_uuid}/device),

  2. waits for the device to be accepted by a user in the platform interface (GET /api/v1/plateform/{platform_uuid}/device/{device_uuid}/status),

  3. saves the returned credentials and connects to the MQTT broker.

On subsequent boots the credentials are read back from NVS and the module connects directly.

Alternatively, if you already have a device uuid/token, call Cloud::useDeviceCredentials() before begin() to skip provisioning entirely.

The progress of this sequence is reported by Cloud::getState() (CloudState); Cloud::isConnected() is the simple check for “the link is up”. A mid-session MQTT drop is first left to the transport’s own auto-reconnect and only escalates to a full re-provisioning pass if it does not recover within 30 s.

MQTT topics

Variable topics use the form device/{device_uuid}/{type}/{variable_name} where {type} is def (default variable), b (bool), i (int), f (float) or s (string). Values are transmitted as plain text ("0"/"1" for booleans, the decimal representation for numbers).

Firmware update (OTA)

The whole update is driven from the OpenIndus cloud web application at oicloud.openindus.com: you upload the firmware binary there, trigger the update on the device, and the image is downloaded straight from that host — no external storage or build server is involved, and the progress is reported back in the web application.

The ota default variable carries a JSON command envelope {"cmd": <int>, "args": <string|int>} in both directions (see CloudOtaCmd):

cmd

Name

Direction

args

0

UPDATE

cloud to module

Firmware download URL, as sent by the web application. A path-only value (e.g. /firmware/xxx.bin) is resolved against the cloud host, so the image is fetched from https://oicloud.openindus.com by default; a full URL must use https://

1

PROGRESS

module to cloud

Number of bytes written so far, emitted every 64 KB and once when the image is complete

2

END

module to cloud

Error code, 0 meaning success

On UPDATE the module spawns a dedicated task that streams the image straight into its inactive OTA partition (nothing is staged in RAM), authenticating with its device token — dropped if the download is redirected off the cloud host, so it never leaks to a third-party storage bucket. Once the image is validated the module sets the new boot partition, publishes END and reboots on it. On failure the download is aborted, the error code is published as END and the module keeps running the current firmware; a new UPDATE command can start a fresh attempt. A second UPDATE is ignored while one is already in progress.

Generating the application code

You do not have to write the variable declarations by hand. Once the variables are described in the web application at oicloud.openindus.com, its code generation feature scaffolds a ready-to-build main.cpp for you: the platform credentials, the OICloud instance, one declaration per cloud variable with its type, direction and refresh policy, and the matching addVariable() calls in setup(). Download it as your starting point and fill in the application logic in loop().

Code examples

The example below provisions a module, publishes two variables and reacts to a value pushed from the cloud:

#include "OpenIndus.h"
#include "Arduino.h"

Core core;

/* Platform credentials (from your OpenIndus cloud platform) */
#define PLATFORM_UUID   "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
#define PLATFORM_TOKEN  "your-platform-token"
#define PROJECT_ID      1
#define PROJECT_VERSION "1.0.0"

/* The cloud handles provisioning (device uuid/token) automatically and
 * persists the credentials in NVS. */
OICloud cloud(PLATFORM_UUID, PLATFORM_TOKEN, PROJECT_ID, PROJECT_VERSION);

/* Cloud variables: each maps to an MQTT topic. */
IntVariable  counter("counter", 0, UpdateMethod::SYNCHRONOUS, UpdateType::PUBLISH, 1000);
BoolVariable buttonOn("buttonon", false, UpdateMethod::ASYNCHRONOUS, UpdateType::PUBLISH, 1000, 10000);
FloatVariable setpoint("setpoint", 0.0f, UpdateMethod::ASYNCHRONOUS, UpdateType::SUBSCRIBE);

int i = 0;

void setup(void)
{
    printf("Hello OpenIndus!\n");

    /* 1. Bring up network connectivity (cellular / PPP) */
    core.modem = new Modem();
    core.modem->begin("TM"); // APN of your SIM provider
    core.modem->connect();

    /* 2. React to values pushed from the cloud */
    setpoint.onReceive([](const float &value) {
        printf("New setpoint received from cloud: %f\n", value);
    });

    /* 3. Register the variables and start the cloud client.
     *    begin() returns immediately; provisioning and the MQTT connection
     *    run in a background task. */
    cloud.addVariable(&counter);
    cloud.addVariable(&buttonOn);
    cloud.addVariable(&setpoint);
    cloud.begin();
}

void loop(void)
{
    /* Just update the values; the cloud task publishes them according to
     * each variable's refresh policy. */
    i++;
    counter.setValue(i);
    buttonOn.setValue((i % 2) == 0);

    delay(1000);
}

Software API

class Cloud

Cloud API class.

Handles device provisioning (spec section 3), credential persistence in NVS, MQTT-over-WebSocket transport and typed cloud variables. All provisioning, connection and variable servicing runs in a background task so user code never blocks: construct, addVariable(), begin(host), then setValue()/getValue().

Public Functions

Cloud(const char *platformUuid, const char *platformToken, int projectId, const char *projectVersion = nullptr)

Constructor for Cloud API (auto-provisioning, preferred).

Parameters:
  • platformUuid – Platform UUID

  • platformToken – Platform authentication token

  • projectId – Project id the device belongs to

  • projectVersion – Project version to publish as the “version” cloud variable. If null/empty (not provisioned), falls back to the firmware’s software version (Board::getSoftwareVersion()).

void useDeviceCredentials(const char *uuid, const char *token)

Provide device credentials directly, bypassing provisioning/NVS.

Parameters:
  • uuid – Device UUID

  • token – Device token

bool begin(const char *host)

Start the cloud client. Launches the background task and returns immediately; provisioning and connection happen asynchronously.

Parameters:

host – Cloud host, e.g. “cloud.openindus.com”

Returns:

true if the task was started

void end(void)

Stop the cloud client and disconnect.

CloudState getState(void) const

Get the current connection/provisioning state.

bool isConnected(void) const

Checks if the device is connected to the cloud.

Returns:

true if connected, false otherwise.

template<typename T>
inline void addVariable(CloudVariable<T> *variable)

Register a CloudVariable with the Cloud system.

Template Parameters:

T – CloudVariable type

Parameters:

variable – Pointer to a CloudVariable

void unregisterVariable(const std::string &name)

Unregister a variable by name.

Parameters:

name – Variable name to unregister

void log(const std::string &message)

Publish a log message (default “log” variable).

template<typename T>
class CloudVariable : public ICloudVariable

CloudVariable class.

Template Parameters:

T – Data type (bool, int, float, std::string)

Public Functions

inline CloudVariable(const std::string &name, const T &initialValue = T(), UpdateMethod updateMethod = UpdateMethod::SYNCHRONOUS, UpdateType updateType = UpdateType::PUBLISH, uint32_t refreshInterval = 1000, uint32_t maxRefreshInterval = 10000)

CloudVariable constructor.

Parameters:
  • name – Variable name and MQTT topic

  • initialValue – Initial value

  • updateMethod – Update method (default: SYNCHRONOUS)

  • updateType – Update type/direction (default: PUBLISH)

  • refreshInterval – SYNCHRONOUS interval, or ASYNCHRONOUS min interval, in ms

  • maxRefreshInterval – ASYNCHRONOUS max interval without publish, in ms

inline void onReceive(std::function<void(const T&)> cb)

Register a callback fired whenever a new value is received from the cloud.

inline bool setValue(const T &newValue)

Set a new value.

Parameters:

newValue – New value

Returns:

true if the value changed

virtual std::string serialize(void) const override

Serialize the current value to the plain-text MQTT payload.

inline virtual void applyPayload(const std::string &payload) override

Apply an inbound MQTT payload to the value and fire the callback.

inline virtual std::string serialize(void) const

Serialize the current value to the plain-text MQTT payload.

inline virtual std::string serialize(void) const

Serialize the current value to the plain-text MQTT payload.

inline virtual std::string serialize(void) const

Serialize the current value to the plain-text MQTT payload.

inline virtual std::string serialize(void) const

Serialize the current value to the plain-text MQTT payload.

enum class UpdateMethod

Update methods (how often the value is published).

  • SYNCHRONOUS : published once every refresh interval.

  • ASYNCHRONOUS: published on change, rate-limited between minRefreshInterval and maxRefreshInterval.

Values:

enumerator SYNCHRONOUS
enumerator ASYNCHRONOUS
enum class UpdateType

Update types (direction relative to the MQTT topic).

  • PUBLISH : the module publishes the topic.

  • SUBSCRIBE: the module subscribes to the topic.

  • BOTH : the module both publishes and subscribes.

Values:

enumerator PUBLISH
enumerator SUBSCRIBE
enumerator BOTH
enum class CloudState

Cloud connection / provisioning state.

Values:

enumerator IDLE
enumerator LOADING_CREDS
enumerator PROVISION_CREATE
enumerator PROVISION_PENDING
enumerator CONNECTING
enumerator CONNECTED
enumerator RECONNECTING
enumerator ERR_REJECTED
enumerator ERR_ALREADY_CREATED
enum class CloudOtaCmd

Commands carried by the “ota” topic, in the “cmd” field of the JSON payload {“cmd”: <int>, “args”: <string|int>}.

  • UPDATE (cloud -> device): args is the firmware download URL (string).

  • PROGRESS (device -> cloud): args is the number of bytes written (int).

  • END (device -> cloud): args is the error code (int), 0 == success.

Values:

enumerator UPDATE
enumerator PROGRESS
enumerator END