Arduino Client

arduino-mega-2560-r3.jpg


The SentiloClient Library for Arduino offers a basic C++ library implementation that allows to the developer a quick integration sketch with the Sentilo Platform through its API Rest Client.

For these examples we'll create a new sensor in the Sentilo Platform, only if it doesn't exists, and then we're going to publish some observations obtained from the local  sensors.


Hardware

We'll need some hardware materials:


Material
Description
Arduino boardWe recommend that you use a Mega 2560 board, which brings to you a bit more program memory than Uno.
Official Ethernet Shield for ArduinoThe library is based on the communication layer that provides the Official Ethernet Shield. Basically, it is a http rest client module.
Some sensorsFor these examples we'll use two types of sensors: a LM35 temperature sensor, and a basic LDR brightness sensor (photocell)
ResistencesTwo 1KOhm resistences 1/4W
BreadboardA breadboard that allow to you the quick connection of the electronic components
WiresConnection wires, like 'dupont' male-to-male ones, that brings to you easily connection between the Arduino board and the breadboard and the sensors
Ethernet cableA cable for connecting the Ethernet Shield to the Internet through your router o modem
B type USB cableA cable for connecting your Arduino board to the PC

Software

We'll use the Official Arduino IDE, that you can download from here.
Of course, you'll need a PC, with Windows or Linux, or a Mac computer.


Setup the Arduino

The Arduino IDE

We assume that you have installed the Arduino IDE and you know how to use it emoticon_smile. If not, then you can have a look on this link


Download and install the library

Download our SentiloClient library or clone it from Git via this link: https://github.com/sentilo/sentilo-client-arduino, and then install it as a custom library into your Arduino IDE.
If you don't know how to install custom libraries, you can have a look on this link, see Importing a .zip Library section.

The example

First example: publishing a basic observation

Once you have installed the library into the Arduino IDE, you can go to the menu option File > Examples > SentiloClient > SentiloClient-Example-01 and open the sample code.
In this example Arduino is going to connect to the network and publish a basic observation with these contents: "This is a sample observation".


Sentilo configuration

You must have configured this information in the Sentilo catalog:

  • A provider (in our case, named samples-provider) and its token
  • A component (in our case, named sample-component)
  • A sensor (in our case, named sample-sensor-arduino-01 for the first example, and another one named sample-sensor-arduino-02 for the second one), with this minimum configuration settings:
    • sensor = sample-sensor-arduino-01
    • type = status
    • dataType = TEXT
    • component = sample-component
    • componentType = generic

Then, you must replace the client connection data code (next section) with yours settings:

  • Change the value "YOUR_API_KEY" with the api key of your provider (variable apiKey)
  • Change the value "YOUR_IP_ADDRESS" with the ip address of your Sentilo instance (variable ip)
  • Change the value "YOUR_PORT" with the port of your Sentilo server instance port (variable port)


The code

You'll should see this code in the editor:

#include <Ethernet.h>
#include <SPI.h>

#include "SentiloClient.h"

/*******************************************/
/***** SENTILO *****************************/
/*******************************************/
char* apiKey = "YOUR_API_KEY";
char* ip = "YOUR_IP_ADDRESS";
int port = YOUR_PORT;
char* componentId = "sample-component";
char* providerId = "samples-provider";
char* sensorId = "sample-sensor-arduino-01";

// The Sentilo Client object
SentiloClient sentiloClient = SentiloClient(ip, port);

/*******************************************/
/***** NETWORK *****************************/
/*******************************************/
const int networkConnectionTimeout = 30;

/*******************************************/
/***** GLOBAL VARS *************************/
/*******************************************/
const int generalCalibrationTimeout = 1000; // Wait after system setup is complete
String response = ""; // Rest call response (normaly as JSON message)
int statusCode = -1; // Rest call return code (the HTTP code)

void setup() {
        // Begin serial for debug purposes
        Serial.begin(9600);

        // Setup the Sentilo Client and the network connection
        setupSetiloClient();

        // Wait time for a general calibration
        delay(generalCalibrationTimeout);
}

void loop() {
        // Create the Observation object
        SentiloClient::Observation observation;
        observation.value = "This is a sample observation";

        Serial.println("[loop] Publishing a sample observation...");
 
        // Publish the observation to Sentilo Platform
        statusCode = sentiloClient.publishObservation(providerId, sensorId, observation, apiKey, response);

        // Read response status and show an error if it is necessary 
        if (statusCode != 200) {
                Serial.print("[loop] [ERROR] Status code from server after publish the observations: ");
                Serial.println(statusCode);
                Serial.print("[loop] [ERROR] Response body from server after publish the observations: ");
                Serial.println(response);
        }

        Serial.println("[loop] Sample observation published!");
        Serial.println("[loop] Program ended");
 
        // The example has ended, so we are going to execute an infinite loop
        while (true) {}
}


/** Setup the Sentilo Client object, this process also configures the network connection **/
void setupSetiloClient() {
        Serial.print("[setup] Connecting to network via DHCP ");
        sentiloClient.dhcp();
        for (int i = 0; i < networkConnectionTimeout; i++) {
                Serial.print(".");
                delay(100);
        }
        Serial.println(" done!");
        Serial.println("[setup] Connection is now established!");
}


What can we see in this example?

  • We are setting up the Serial channel for debug output
  • Setup the SentiloClient object (sentiloClient), which configures the client and connects to the network
  • Once we're connected to the server, we publish a basic observation, with these contents: "This is a sample observation"
    • If the publish works properly, the system don't return any special data
    • Otherwhise, it will show to you the system return code and message, if it is possible
  • The test ends after publish only one observation

Second example: publishing sensors data as observations

In this case we'll retrieve data from sensors (LDR and LM35), and then we'll publish them as a observation, with a message in JSON format, like that:

{"ldr":"{ldrValue}","lm35":"{lm35Value}"}

Where the ldrValue contains the LDR photocell value, and the lm35Value contains the LM35 temperature value.
Open the sample code in File > Examples > SentiloClient > SentiloClient-Example-02.

Connect the sensors and and other connections

Now, it is the time to connect the sensors and others elements. 

See below:

arduino_sensors_board.png

In the upper image, you can see how the components has been located:

  • Connect the positive pin from Arduino (+5V) to the upper channel of the breadboard (red channel)
  • Connect the negative pin from Arduino (GND) to the second channel of the breadboard (blue channel)
  • LDR photocell sensor connection:
    • Connect the LDR photocell between GND signal and A0 (Analog IO 0 from Arduino) with a dupont wire, in this case, the orange color wire
    • Connect the LDR pin that holds the orange wire with a 1KOhm resitor, and the other resistor pin to Arduino +5V (red wire)
  • LM35 temperature sensor:
    • Connect the LM35 positive pin (left pin, front side) to Arduino +5V
    • Connect the LM35 center pin (signal) to the A5 (Analog IO 5 from Arduino) with a dupont wire, in this case, the orange color wire
    • Connect the LM35 negative pin (right pin, front side) to Arduino GND

The code

You should see this code in the editor:

#include <Ethernet.h>
#include <SPI.h>

#include "SentiloClient.h"

/*******************************************/
/***** SENSORS *****************************/
/*******************************************/
int LDR = 0; // LDR input is A0
int LM35 = 5; // LM35 input is A5
const int ldrSetupTimeout = 10; // Time that LDR needs to be configures (dummy time)
const int lm35SetupTimeout = 10; // Time that LM35 needs to be configures (dummy time)

/*******************************************/
/***** SENTILO *****************************/
/*******************************************/
char* apiKey = "YOUR_API_KEY";
char* ip = "YOUR_IP_ADDRESS";
int port = YOUR_PORT;
char* componentId = "sample-component";
char* providerId = "samples-provider";
char* sensorId = "sample-sensor-arduino-02";

// The Sentilo Client object
SentiloClient sentiloClient = SentiloClient(ip, port);

/*******************************************/
/***** NETWORK *****************************/
/*******************************************/
const int networkConnectionTimeout = 30;

/*******************************************/
/***** GLOBAL VARS *************************/
/*******************************************/
const int generalCalibrationTimeout = 1000; // Wait after system setup is complete
const int loopTimeout = 60000; // Loop timeout, time between observations (in ms)
String response = ""; // Rest call response (normaly as JSON message)
int statusCode = -1; // Rest call return code (the HTTP code)

boolean existsSensor = false;

void setup() {
        // Begin serial for debug purposes
        Serial.begin(9600);

        // Setup the LDR sensor
        setupLDR();

        // Setup the LM35 sensor
        setupLM35();

        // Setup the Sentilo Client and network connection
        setupSetiloClient();

        // Wait time for a general calibration
        delay(generalCalibrationTimeout);
}

void loop() {
        // Get the LDR value  
        int ldrValue = getLdrValue();

        // Get the LM35 value
        float lm35Value = getLM35Value();

        // Create the observation input message like this: {"ldr":"234","lm35":"24.5"}
        String obsInputMsg = 
                "{\\\"ldr\\\":\\\"" + String(ldrValue) + 
                "\\\",\\\"lm35\\\":\\\"" + String(lm35Value) + 
                "\\\"}";
        int bufLength = obsInputMsg.length() + 1;
        char obsMsgBuffer[bufLength];
        obsInputMsg.toCharArray(obsMsgBuffer, bufLength);

        // Create the Observation object
        SentiloClient::Observation observation;
        observation.value = obsMsgBuffer;

        // Debug on Serial the observations value. Note that we must scape special characters
        Serial.print("[loop] Publishing actual sensors values as observations: ");
        Serial.println(obsMsgBuffer);
 
        // Publish the observation to Sentilo Platform
        statusCode = sentiloClient.publishObservation(providerId, sensorId, observation, apiKey, response);

        // Read response status and show an error if it is necessary
        if (statusCode != 200) {
                Serial.print("[loop] [ERROR] Status code from server after publish the observations: ");
                Serial.println(statusCode);
                Serial.print("[loop] [ERROR] Response body from server after publish the observations: ");
                Serial.println(response);
        } else {
                Serial.println("[loop] Sensors observations published!");
        }

        delay(loopTimeout);
}

/** Emulate a possible LDR initialization process, if it is necessary **/
void setupLDR() {
        Serial.print("[setup] Setting up the LDR brightness sensor ");
        for (int i = 0; i < ldrSetupTimeout; i++) {
                Serial.print(".");
                delay(100);
        }
        Serial.println(" done!");
        delay(50);
}

/**  Get the brightness value from th LDR **/
int getLdrValue() {
        return analogRead(LDR);
}

/** Emulate a possible LM35 initialization process, if it is necessary **/
void setupLM35() {
        Serial.print("[setup] Setting up the LM35 temperature sensor ");
        for (int i = 0; i < lm35SetupTimeout; i++) {
                Serial.print(".");
                delay(100);
        }
        Serial.println(" done!");
        delay(50);
}

/** Get the LM 35 temperature value in Celsius degrees **/
float getLM35Value() {
        int val = analogRead(LM35);
        float mv = (val / 1024.0) * 5000;
        float cel = mv / 10;
        //float farh = (cel * 9) / 5 + 32;
        return cel;
}

/** Setup the Sentilo Client object. This process also configures the network connection **/
void setupSetiloClient() {
        // Connect via DHCP
        Serial.print("[setup] Connecting to network via DHCP ");
        sentiloClient.dhcp();
        for (int i = 0; i < networkConnectionTimeout; i++) {
                Serial.print(".");
                delay(100);
        }
        Serial.println(" done!");
        Serial.println("[setup] Connection is now established!");
}

What can we see in this example? There're some additions compared with the first example.

  • Setup Arduino and the SentilClient is the same of the first sample
  • We're making a sensors setup, but in this case it isn't necessary, so it only informs us in debug mode what is happening in every moment...
  • Into the loop
    • We're retrieving the LDR and LM35 values, and putting them into variables
    • Once we've retrieved the sensors data, we're mounting the new observation message, with value = {"ldr":"{ldrValue}","lm35":"{lm35Value}"}
    • The SentiloClient library gets the value and transforms it on a complete observation message using the publishObservation method (see below)
  • The sketch loops sleeps until loopTimeout millis has been reached, and then turns up and repeats the same process of data publication (in this example the sleep time is 60000ms, 1 minute per loop / publish)

This is the observation sended by to the Sentilo platform:

{"observations":[{
   "value":"{"ldr":"{ldrValue}","lm35":"{lm35Value}"}"}
]}

If you want, you can include the timestamp varriable in UTC format inside the observation object:

Observation observation;
observation.value = {"ldr":"382","lm35":"23.4"};
observation.timestamp = "05/05/2015T12:34:45";

And the message will be generated as:

{"observations":[{
   "value":"{"ldr":"382","lm35":"23.4"}",
   "timestamp":"05/05/2015T12:34:45"}
]}

As you can see, the library object Observation (struct type) offers you an abstraction. In the next sample we will see them in working together with the Sensor object.

Third example: initialize sensor, create it in the catalog and publish observations continuously

In this third example we'll see that how the SentiloClient library can create a sensor "on-the-fly" and publish obervations continuously.
Next, we'll use the second example, plus a little bit of additional code that help us to check if the sensor exists in the catalog, and if not create it before publish observations.
Open the sample code in File > Examples > SentiloClient > SentiloClient-Example-03.


The code

You should see this code in the editor:

#include <Ethernet.h>
#include <SPI.h>

#include "SentiloClient.h"

/*******************************************/
/***** SENSORS *****************************/
/*******************************************/
int LDR = 0; // LDR input is A0
int LM35 = 5; // LM35 input is A5
const int ldrSetupTimeout = 10; // Time that LDR needs to be configures (dummy time)
const int lm35SetupTimeout = 10; // Time that LM35 needs to be configures (dummy time)

/*******************************************/
/***** SENTILO *****************************/
/*******************************************/
char* apiKey = "YOUR_API_KEY";
char* ip = "YOUR_IP_ADDRESS";
int port = YOUR_PORT;
char* componentId = "sample-component";
char* providerId = "samples-provider";
char* sensorId = "sample-sensor-arduino-03";

// The Sentilo Client object
SentiloClient sentiloClient = SentiloClient(ip, port);

/*******************************************/
/***** NETWORK *****************************/
/*******************************************/
const int networkConnectionTimeout = 30;

/*******************************************/
/***** BGLOBAL VARS ************************/
/*******************************************/
const int generalCalibrationTimeout = 1000; // Wait after system setup is complete
const int loopTimeout = 60000; // Loop timeout, time between observations publications (in ms)
String response = ""; // Rest call response (normaly as JSON message)
int statusCode = -1; // Rest call return code (the HTTP code)

boolean existsSensor = false;

void setup() {
        // Begin serial for debug purposes
        Serial.begin(9600);

        // Setup the LDR sensor
        setupLDR();

        // Setup the LM35 sensor
        setupLM35();

        // Setup the Sentilo Client 
        // and network connection
        setupSetiloClient();

        // Setup the Sentilo sensor 
        // and create it if doesn't exists
        setupSentiloSensor();

        // Waiting for the next release of the observation
        delay(generalCalibrationTimeout);
}

void loop() {
        if (existsSensor) {
                // If the sensor exists, 
                // we can start publishing observations
  
                // Get the LDR value  
                int ldrValue = getLdrValue();

                // Get the LM35 value
                float lm35Value = getLM35Value();

                // Create the observation input message
                // like this: {"ldr":"234","lm35":"24.5"}
                String obsInputMsg = 
                        "{\\\"ldr\\\":\\\"" + String(ldrValue) + 
                        "\\\",\\\"lm35\\\":\\\"" + String(lm35Value) + 
                        "\\\"}";
                int bufLength = obsInputMsg.length() + 1;
                char obsMsgBuffer[bufLength];
                obsInputMsg.toCharArray(obsMsgBuffer, bufLength);

                // Create the Observation object
                SentiloClient::Observation observation;
                observation.value = obsMsgBuffer;

                // Debug on Serial the observations value
                // Note that the message includes slashes (\) because we must scape special characters as "
                Serial.print("[loop] Publishing actual sensors values as observations: ");
                Serial.println(obsMsgBuffer);
  
                // Publish the observation to Sentilo Platform
                statusCode = sentiloClient.publishObservation(providerId, sensorId, observation, apiKey, response);

                // Read response status and show an error if it is necessary
                if (statusCode != 200) {
                        Serial.print("[loop] [ERROR] Status code from server after publish the observations: ");
                        Serial.println(statusCode);
                        Serial.print("[loop] [ERROR] Response body from server after publish the observations: ");
                        Serial.println(response);
                } else {
                        Serial.println("[loop] Sensors observations published!");
         }
  
        // Waiting for the next loop
        delay(loopTimeout);
        } else {
                // If the sensor does not exist and it could 
                // not be created in the catalog, we must stop running
                Serial.println("[loop] [ERROR] Oops! The sensor doesn't exists, so I can't publish data to it...");
                Serial.println("[loop] [ERROR] I'm sorry with you, but now I'm going to halt...");
                Serial.println("[loop] [ERROR] Bye!");
                while (true) { }
        }
}

// Emulate a possible LDR initialization process, if it is necessary
void setupLDR() {
        Serial.print("[setup] Setting up the LDR brightness sensor ");
        for (int i = 0; i < ldrSetupTimeout; i++) {
                Serial.print(".");
                delay(100);
        }
        Serial.println(" done!");
        delay(50);
}

// Get the brightness value from th LDR
int getLdrValue() {
        return analogRead(LDR);
}

// Emulate a possible LM35 initialization process, if it is necessary
void setupLM35() {
        Serial.print("[setup] Setting up the LM35 temperature sensor ");
        for (int i = 0; i < lm35SetupTimeout; i++) {
                Serial.print(".");
                delay(100);
        }
 Serial.println(" done!");
        delay(50);
}

// Get the LM 35 temperature value in Celsius degrees
float getLM35Value() {
        int val = analogRead(LM35);
        float mv = (val / 1024.0) * 5000;
        float cel = mv / 10;
        //float farh = (cel * 9) / 5 + 32;
        return cel;
}

// Setup the Sentilo Client object
// This process also configures the network connection
void setupSetiloClient() {
        // Connect via DHCP
        Serial.print("[setup] Connecting to network via DHCP ");
        sentiloClient.dhcp();
        for (int i = 0; i < networkConnectionTimeout; i++) {
                Serial.print(".");
                delay(100);
        }
        Serial.println(" done!");
        Serial.println("[setup] Connection is now established!");
}

// Setup the Sentilo Sentor (this Arduino)
// If the sensor doesn't exists in the catalog, create it
void setupSentiloSensor() {
        Serial.println("[setup] Retrieving catalog info from Sentilo and search for the sensor...");

        // Get catalog data for the provider with the supplied api key
        statusCode = sentiloClient.getCatalog(apiKey, response);

        // If the server status response is not ok, show the error
        if (statusCode != 200) {
                Serial.print("[setup] [ERROR] Status code from server getting catalog: ");
                Serial.println(statusCode);
                Serial.print("[setup] [ERROR] Response body from server getting catalog: ");
                Serial.println(response);
        } else {
                // If we get a correct response, we must search the sensor
                if (find_text(sensorId, response) >= 0) {
                        // The sensor is in the catalog
                        Serial.println("[setup] The sensor is in the catalog");
                        existsSensor = true;
                } else {
                        // The sensor isn't in the catalog, so we must create it
                        Serial.println("[setup] The sensor isn't in the catalog, so let register it now...");

                        // Create the basic Sentilo Sensor Object
                        SentiloClient::Sensor sensor;
                        sensor.sensor = sensorId;
                        sensor.type = "status";
                        sensor.dataType = "TEXT";
                        sensor.component = componentId;
                        sensor.componentType = "generic";
                        sensor.location = "sensorLat sensorLng";

                        // Call the SentiloClient Register Sensor function
                        statusCode = sentiloClient.registerSensor(sensor, providerId, apiKey, response);

                        // Read the server status response
                        if (statusCode == 200) {
                                // If ok, the sensor has been yet created
                                existsSensor = true;
                        } else {
                                // If nok, then we can't continue with the program
                                existsSensor = false;
                                Serial.print("[setup] [ERROR] Status code from server getting catalog: ");
                                Serial.println(statusCode);
                                Serial.print("[setup] [ERROR] Response body from server getting catalog: ");
                                Serial.println(response);
                        }
                }
        }
}

// Auxiliary function for search text in a String
int find_text(String needle, String haystack) {
        int foundpos = -1;
        for (int i = 0; (i < haystack.length() - needle.length()); i++) {
                if (haystack.substring(i, needle.length() + i) == needle) {
                        foundpos = i;
                }
        }
        return foundpos;
}

And finally, in the last example, we can see:

  • Initialization is the same that in the other examples
  • Before ending the initialization process, we search for the sensor in the catalog:
    • Into the setupSentiloSensor() method, the sentiloClient.getCatalog retrieves all the catalog data related to the provider, so we can now search for the value of our sensor, in this case, sample-sensor-arduino-03, and we see that it doesn't exists in the catalog (you must not create it manually!)
    • Then, create it with sentiloClient.registerSensor, including a Sensor object (see values below), if you want to publish its location don't forget to initialize the sensorLat and sensorLng values!
    • Once the sensor is created, we end the setup process and starts the loop
    • If there is any error registering the sensor, the serial prints the error message and the server status code in the console
  • In the loop, like in Example 2, retrieve sensors data (LDR and LM35), and publish them as new sensor observation

Next, there is an example of Sensor object message with the example values:


SentiloClient::Sensor sensor;
sensor.sensor = "sample-sensor-arduino-03";
sensor.type = "status";
sensor.dataType = "TEXT";
sensor.component = "sample-component";
sensor.componentType = "generic";
sensor.location = "41,385063 2,1734034";

And before invoking the Sentilo API Rest platform, the SentiloClient library tansforms this object in a JSON message like this:

{"sensors":[
   {"sensor":"sample-sensor-arduino-03",
    "description":"",
    "type":"status", 
    "dataType":"TEXT",
    "unit":"",
    "component":"sample-component", 
    "componentType":"generic",
    "componentDesc":"",
    "location":"41,385063 2,1734034",
    "timeZone":"CET" 
   }
]}

As you can see, the type is generic and the data type is text, because this is the best way to publish any data without any format problem. 

Last modified by Administrator on 2015/11/17 16:10