This guide will take you through the process of creating your first iotQi Arduino/ARM device application using the iotQi sample\template.  This template targets the Adafruit Feather M0 WiFi most easily and provides a complete but relatively easy 1st project.  Most topics here are covered at a basic level to get you started, many of these topics are covered in more detail within other guides; if there is something missing or you are just curious, please ask... chances are others will benefit from our answer too. 


Setting Up Your Development Environment

The recommended way to install the necessary files for developing an Arduino iotQi device is to grab a zip of the iotQi-Arduino-HTTP repository from the LooUQ GitHub.  The link and the steps to get a zip is shown below.


https://github.com/LooUQ/iotQi-device-arduino 


This guide walks you through using the Arduino IDE, a very popular software tool for building IoT applications.  LooUQ maintains compatibility between our iotQi device templates and the Arduino IDE.  We also would like to point out that if you are just getting started, you will outgrow the Arduino IDE quickly.  Internally LooUQ uses PlatformIO (platformio.org) as our primary IDE; we have also successfully used Visual Micro (a extension for Visual Studio, including Community edition).



Once the zip is downloaded, start the Arduino IDE and import the zip file into Arduino IDE as shown below... 



Importing the iotQi Arduino library will also pull all of the supporting libraries needed, such as support for Azure IoT Hub. There has been quite a bit of activity with the supporting libraries so make sure you are at or above the following key library versions.

  • WiFi101  (0.14.0)
  • AzureIoTHub (1.0.21)
  • AzureIoTProtocol_HTTP (1.0.21)
  • AzureIoTProtocol_MQTT (1.0.21)
  • AzureIoTUtility (1.0.21)


Creating Your Project

Once the environment is setup, the next step is to copy the example folder from the iotQi-Library to your Arduino Sketchbook folder.  In your sketchbook folder, you will find a libraries folder.  It will contain the iotQi-device-arduino folder, which will contain an examples folder.  Within the examples folder, locate and copy the iotQi-basic folder to your sketchbook.  Once copied you may wish to change the name from iotQi-basic to something else; if you rename the destination iotQi-basic folder, you will also need to change the .INO file it contains to match (.INO files must have the same name as their parent folder in the Arduino IDE). 


iotQi Device Application Files

At minimum a device application will contain 4 files...

  • The main .ino application file containing setup() and loop()
  • A settings.h file with iotQi settings and constants that can be referenced throughout the application
  • A user-model.c (with matching user-model.h) that defines you data\action model

Making the sample application meet your needs is covered in detail below, but a couple things are worth noting now.  Feel free to add addition items to the settings.h file to allow them to be visible through out your application.  Take note that the user-model is a C, not a C++, source file; don't be too concerned this is mostly the same as any other Arduino file except you can no create "classes".  We recommend that you treat the user-model as just a container for your data definitions and your remote command functions.


Getting Connected to Your WiFi

Getting connected to your WiFi network is the first step to getting connected to the LooUQ cloud.  The settings.h file in your sketchbook project folder has both the WiFi settings (SSID and password) and the LooUQ cloud connection string.  So you can start by editing this file.  Setup your WiFi parameters (we currently support WPA2.  Then go to http://setup.loouq.com (see iotQi Setup Getting Started guide) and create a new device or navigate to the correct device and copy the device connection string.  


iotQi Arduino Device Application Structure

While an iotQi Arduino Application has similar structure to every other Arduino application, there are some additional items that are important for iotQi.  An iotQi Arduino device application has the standard setup() and loop() functions you have seen in other Arduino applications and they do the same things: setup runs one time after the Arduino starts to complete any initialization needed, loop runs continuously one setup finishes.  There are a couple iotQi specific items that are required in both setup() and loop(). 


The User Model and iotQi

The Arduino iotQi device client utilizes a serializer library as a connector between your data and the iotQi communications functions.  We use the serializer to connect to your command functions and you will use it when creating telemetry and alerts to generate JSON for transmission to the iotQi cloud. This serializer is provided by Microsoft in the AzureIoTHub library, if you would like to use it in now iotQi projects.  Almost all customization you will need to complete to achieve your desired results will be via definitions you create for the serializer in the user-model.c file.For more information about the serializer, look here.

The user-model.c file contains several important areas within the UserNamespace...

  • A single user model
  • Structures (optional: you can have none, one, or many structures)
  • Data definitions (information you send to the cloud from telemetry, alerts, or command responses)
  • Action definitions (custom commands)

Structures are defined first, any structure can then be referenced in the model.  The model contains a list of data definitions (WITH_DATA) and command action definitions (WITH_ACTION).  These definitions are used by the serializer and iotQi to format data received from or sent to the cloud.


Telemetry\Alert Data

Telemetry (data sampling) and alerts used the "model" to organize data to send to iotQi and in-turn your systems.  


Commands

For each command you are implementing (specified below with a WITH_ACTION), you will need to create a implementation method.  The code section below shows how the SetTemperature is modeled (above with WITH_ACTION) and implemented as a method (below).  Note the unique way parameters are specified in the WITH_ACTION following the implementation method name; <parameterType>, <parameterName>.  Also notice the method parameters, the UserModel pointer is required as the first parameter, followed by your parameters.  The implementation of an action is bolded in the sample below: 1) define WITH_ACTION, 2) implement method definition. 


BEGIN_NAMESPACE(UserNamespace);

DECLARE_STRUCT(_Telemetry,
    float, Temperature,
    float, Humidity,
    float, BarPressure,
    int, SampleId
);

DECLARE_STRUCT(_DeviceInfo,
    float, VBat,
    long, FreeRam,
    int, Rssi
);

DECLARE_MODEL(UserModel,
    WITH_DATA(_Telemetry, Telemetry),
    WITH_DATA(_DeviceInfo, DeviceInfo),
    WITH_DATA(float, Temperature),
    WITH_DATA(float, Humidity),
    WITH_DATA(float, BarPressure),
    WITH_DATA(int, SampleId),

    WITH_ACTION(SetTemperature, int, temperature),
    WITH_ACTION(SetHumidity, int, humidity)
);

END_NAMESPACE(UserNamespace);

UserModel* userContext;

/* Add any variables that need to persistent across samples  */
int sampleId = 0;


//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

EXECUTE_COMMAND_RESULT SetTemperature(UserModel* userContext, int temperature)
{
    (void)printf("Received temperature %d\r\n", temperature);
    userContext->Telemetry.Temperature = temperature;
    return EXECUTE_COMMAND_SUCCESS;
}


If you want to return values back to the cloud, you will need to implement a send command response block within the command method. 


User Model and Telemetry Samples

The model is also used for telemetry (data) samples.  Similar to commands you need to implement the GetTelemetrySample() method to perform whatever sampling is required for your application.  The block below shows the method from the iotQi example. 


IOTOS_RESULT GetTelemetrySample(unsigned char** buffer, size_t* bufferSize, char* eventName, char* displayValue)
{
  float sampleTemp;
  float samplePress;
  float sampleHumid;

  // user defined sample
  (void)printf("\r\n*** Collecting telemetry sample ***\r\n");
  readBmeValues(&sampleTemp, &samplePress, &sampleHumid);
  userContext->Telemetry.Temperature = sampleTemp;
  userContext->Telemetry.BarPressure = samplePress;
  userContext->Telemetry.Humidity = sampleHumid;
  userContext->Telemetry.SampleId = ++sampleId;
  userContext->DeviceInfo.VBat = GetVbat();
  userContext->DeviceInfo.FreeRam = GetFreeRam();
  userContext->DeviceInfo.Rssi = GetRssi();

  /* The following 2 lines copy telemetry event information into the event message properties going out to the iotQi cloud hub 
   *  These 2 properties will be found in your event table data as columns
   *  
   *  Note: snprintf has floating support
   *  Max Lengths: 80 characters for displayValue, 80 characters for eventName
   */
  snprintf(displayValue, DISPLAYVALUE_SIZE, "Temperature=%.2f", sampleTemp);
  strncpy(eventName, "Temperature_Humidity_BarPressure", EVENTNAME_SIZE);
  
  //(void)printf("Serializing sensor value: %s (%d)\r\n", displayValue, sampleId);
  oledDisplay_showWifiStatus();
  oledDisplay_refreshIcons();

  if (SERIALIZE(buffer, bufferSize, userContext->DeviceInfo, userContext->Telemetry) != CODEFIRST_OK)
  {
    (void)printf("Error serializing telemetry sample");
    return IOTOS_ERROR;
  }
  return IOTOS_OK;
}


Model Data Types

The table below shows the available data types that the Model and Serializer understand. 


Model Type NameDescription
double
double precision floating point number
int
32 bit integer
float
single precision floating point number
long
long integer
int8_t
8 bit integer
int16_t
16 bit integer
int32_t
32 bit integer
int64_t
64 bit integer
 bool
boolean
 ascii_char_ptr  
ASCII string 
 EDM_DATE_TIME_OFFSET  
 date time offset 
 EDM_GUID  
 GUID
 EDM_BINARY
binary 
DECLARE_STRUCT 
complex data type