Skip to Content
Author's profile photo Rui Nogueira

RaspberryPi on SAP HCP – IoT blog series part 6.2: Getting productive – completely

Table of Contents

In the last blog post of this blog post series we’ve setup the IoT Service for our scenario.

No Java code footprint in the cloud

With the functionality available on the HCP IoT Services we’ll now reduce the code footprint of our overall application. We no longer need the Java app on our free developer account, but we’ll leverage the OData service provided by the IoT Services and use that inside our HTML5 application.

Slide3.jpg

Along with the IoT Services we’ll also be able to add a security feature to our app as the Raspberry Pi will have to provide an oAuth token together with each sensor value it will send to our IoT Service. So let’s get started.

1. Updating the Java app on the Raspberry Pi

In a first step we’ll update the Java code on the Raspberry Pi.

Step Screenshot

1. Connect to your Raspberry Pi via ssh or any other proper way

2. Open the file Temperature.java that you’ve used in blog post 3 of this blog post series.


cd /home/pi/myscripts

nano Temperature.java

3. Substitute the code with the code I’ve listed up in the Appendix section of this blog post under Temperature.java.

4. Adapt the following variables to your account and device type

  • USEPROXY: set it to true if you work behind a proxy with your Raspberry Pi or set it to false if that’s not the case. If you work behind a proxy also set the proxy host and ports in the function called setProxy
  • ACCOUNT_ID: your account ID of your free developer account

Set also the following variables to the values you’ve setup in the last blog post when you’ve setup the IoT service

  • DEVICE_1_ID: the device ID of your first device
  • DEVICE_1_TOKEN: the oauth token for your first device
  • DEVICE_2: the device ID of your second device
  • DEVICE_2_TOKEN: the oauth token for your second device

Save the file (press Ctrl and X and confirm with y)

Screen Shot 2015-11-12 at 13.10.12.png
5. Compile Temperature.java by entering the command javac Temperature.java Screen Shot 2015-11-12 at 13.18.53.png
6. If you are interested, look into the code to find out how it sends the data to the IoT endpoint.

2. Setting up destinations

Our app needs to access the list of devices, device types and, as well, the actual sensor data. For that you’ll have to create to additional destinations pointing you to the corresponding API.

2.1 Destination for devices and device types

Step Screenshot
1. For the devices and device types you can copy-and-paste the URL from your IoT cockpit. Just click on your user info at the top right and click on the “About” menu. Screen Shot 2015-11-12 at 15.29.16.png

2. Once you do that you’ll get a pop-up window with that link. Just copy and paste the link that you find under Device Types Endpoint.

Screen Shot 2015-11-12 at 13.28.56.png

3. Switch to your SAP HANA Cloud Platform account cockpit and click on the Destinations tab on the right. Screen Shot 2015-11-12 at 13.33.26.png

4. Click on the New Destination link and create the destination

Name: iot_devices_odata

Type: HTTP

URL: Copy and paste the URL you’ve gotten from step 2 and remove the devicetypes at the end of the url. So the URL should end with ….dms/api

Proxy Type: Internet

Authentication: Basic Authentication

User: Your user name (e.g. s1234567890)

Password: Password for your user

Click on Save

Screen Shot 2015-11-12 at 13.36.30.png

2.2 Destination for the data

To retrieve the data from your IoT service we’ll need a new destination. You can find the API description for OData consumption in the online help of the SAP HCP IoT Services.

Step Screenshot

1. Click on the New Destination link and create the destination

Name: iot_devices_odata

Type: HTTP

URL: https://iotmmsXXXXXXXXXtrial.hanatrial.ondemand.com/com.sap.iotservices.mms/v1/api/http/app.svc where XXXXXXXXX is your username (e.g. s1234567890)

Proxy Type: Internet

Authentication: Basic Authentication

User: Your user name

Password: Password for your user

Click on Save

Screen Shot 2015-11-12 at 13.48.45.png
2. Now check if your 2 new destinations are there. It should look similar to the screenshot on the right. There should be an iot_devices_odata and an iot_sensordata_odata. Screen Shot 2015-11-12 at 13.58.46.png

2.3. Important! Enable Basic Auth for iotmms application

In the documentation there is a note that we are authenticated with Basic Auth. Currently this needs to be enabled for the iotmms application by you manually.
Just follow the corresponding instructions in the documentation.

3. Update the HTML5 app

Now most of the work is done and we can look into the last part finalizing our app. We’ll adapt the code of our HTML5 dashboard app we’ve created in blog post 4 of this series. But this time we’ll use the Web IDE to do that.

Step Screenshot
1. Go back to your account cockpit and click on the HTML5 Applications tab and click on the pencil icon next to the row of your fishdashboard application. Screen Shot 2015-11-12 at 14.11.36.png

2. The Web IDE opens up.

If necessary please enter your account credentials if the system asks you to do so.

Once your project opens up double-click on the index.html file so that the code editor opens-up index.html

Screen Shot 2015-11-12 at 14.14.54.png

3. Now substitute the code of index.html with the code I’ve listed up in the Appendix section of this blog post under index.html.

After you have done that save your changes by clicking on the Save icon at the top left.

Screen Shot 2015-11-12 at 14.19.21.png

4. In the index.html file you need to adapt one variable that contains the message type id of the message type you have defined in the previous blog post.

Search in the index.html file for the row called

var messageId = “YOUR MESSAGE TYPE ID “.toUpperCase();

and substitute the id displayed there with yours.

After that save your changes again.

Screen Shot 2015-11-13 at 09.27.11.png
5. Do the same for the neo-app.json file by substituting it with the content of the I’ve listed up in the Appendix section of this blog post under neo-app.json. Screen Shot 2015-11-12 at 14.23.12.png
6. Deploy the app now.

Right-click on the fishdashboard folder and click Deploy > Deploy toSAP HANA Cloud Platform

Screen Shot 2015-11-12 at 14.26.07.png
7. In case you are asked for it provide your credentials and click on Login. Screen Shot 2015-11-12 at 14.27.20.png

8. A pop-up window shows up and if you scroll down a bit in the window you’ll see the version number that will be used to deploy the app on your account.

Click on RaspberryPi on SAP HCP – IoT blog series part 4: Create an SAPUI5 dashboard.

Screen Shot 2015-11-12 at 14.29.04.png

9. If everything worked out fine you should get a window telling you that you’ve successfully deployed your app.

Now click on the link to Open the active version of the application.

Screen Shot 2015-11-12 at 14.31.21.png
10. If all worked fine you should see your app now. Screen Shot 2015-11-12 at 14.33.49.png

4. Send your sensor data to the app

The last missing part is now to actually send the sensor values from the Raspberry Pi to your IoT Service.

To do so, go to your Raspberry Pi command line and enter

java Temperature

and you should see in your browser how the data is coming-in and changing. Congratulations!!

Screen Shot 2015-11-12 at 14.40.57.png

5. Summary

What you see now in this little app is that you no longer need a Java application providing the data to your HTML5 app. Instead you can use the OData service from the IoT services directly to do that job.

I propose you look into the index.html file to better understand how I’ve used the OData services to fetch all the information I’ve needed for the app. You’ll notice that I’m working with some filtering and sorting to get the information the way I needed. The corresponding documentation is very helpful and might provide you with some additional insights.

And now that we can access the data via an OData service, why not using some templates in the Web IDE to created other apps like a Fiori Master Detail application without having to code one line of code 🙂 ?

Have fun.

Best,

Rui

Appendix: Source code

Temperature.java

import java.io.BufferedReader;

import java.io.DataOutputStream;

import java.io.IOException;

import java.io.InputStreamReader;

import java.math.BigDecimal;

import java.math.RoundingMode;

import java.net.URL;

import java.nio.charset.Charset;

import java.nio.file.Files;

import java.nio.file.Paths;

import java.util.List;

import java.util.Properties;

import java.util.Random;

import javax.net.ssl.HttpsURLConnection;

public class Temperature {

    // Set this to true if you are working behind a proxy server.

    // If you are working from home you should set this most probable to false

    public static boolean USEPROXY = true;

    // Add your account id here

    // Get the devide ID and the OAuth Token from the Device List in your

    // IoT Services Cockpit

    public static String MY_ACCOUNT_ID  = “YOURUSERIDtrial”;

    public static String MESSAGETYPE_ID = “xxxxxxxxxxxxxxxxxx”;

    public static String DEVICE_1_ID    = “xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxxx”;

    public static String DEVICE_1_TOKEN = “xxxxxxxxxxxxxxxxxxxxxxxxxxxx”;

    public static String DEVICE_2_ID    = “xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxxx”;

    public static String DEVICE_2_TOKEN = “xxxxxxxxxxxxxxxxxxxxxxxxxxxx”;

    public static void main(String args[]) {

        String repeatString = null;

        if (args.length > 0) {

            repeatString = args[0];

        } else {

            repeatString = “10”;

        }

        int repeats = Integer.parseInt(repeatString);

        for (int i = 0; i < repeats; i++) {

            // send data every 2000ms

            try {

                  Thread.sleep(2000);

                } catch (InterruptedException ie) {

                    //Handle exception

                }

            //System.out.println(“Loop ” + (i + 1));

            long timestamp = System.currentTimeMillis();

            //double sensorTemperature = getOnboardTempSensor();

            double sensorTemperature = getRandomValue(-5, 5);

            String bodyMessage = buildBody(MESSAGETYPE_ID, sensorTemperature, timestamp);

            sendToCloud(bodyMessage, DEVICE_1_ID, DEVICE_1_TOKEN , String.valueOf(i + 1));

            sensorTemperature = getRandomValue(-5, 0);

            //sensorTemperature = getOnboardTempSensor();

            //sensorTemperature = getOneWireSensor(“28-0000060a4638”);

            bodyMessage = buildBody(MESSAGETYPE_ID, sensorTemperature, timestamp);

            sendToCloud(bodyMessage, DEVICE_2_ID, DEVICE_2_TOKEN , String.valueOf(i + 1));

        }

    }

    // Creates random value within give limits

    private static double getRandomValue(double min, double max) {

        Random r = new Random();

        double value = min + (max – min) * r.nextDouble();

        // Round up the value to 2 decimal places

        // E.g. will make out of 1.42342232 a 1.42

        BigDecimal bd = new BigDecimal(value);

        bd = bd.setScale(2, RoundingMode.HALF_UP);

        return bd.doubleValue();

    }

    private static String buildBody(String messageType, Double sensorValue, long time) {

        String body = “{“;

        body += buildJson(“mode”, “async”) + “,”;

        body += buildJson(“messageType”, messageType) + “,”;

        body += ‘”‘ + “messages” + ‘”‘ + “:[“;

        String messageContent = “{“;

        messageContent += buildJson(“unit”, sensorValue) + “,”;

        messageContent += buildJson(“value”, sensorValue) + “,”;

        messageContent += buildJson(“storedAt”, time);

        messageContent += “}”;

        body += messageContent;

        body += “]”;

        body += “}”;

        return body;

    }

    private static void sendToCloud(String body, String deviceId, String token, String loop) {

        // long sensorTimestamp = System.currentTimeMillis();

        String iotServiceMainLink = “https://iotmms” + MY_ACCOUNT_ID +”.hanatrial.ondemand.com/com.sap.iotservices.mms/v1/api/http/data/”;

        String url = iotServiceMainLink + deviceId;

        byte[] postData = body.getBytes();

        setProxy(USEPROXY);

        try {

            URL obj = new URL(url);

            String responseMessage = “<NONE>”;

            // System.out.println(” Calling url ” + url);

            HttpsURLConnection con = (HttpsURLConnection) obj.openConnection();

            con.setDoOutput(true);

            con.setInstanceFollowRedirects(false);

            // add request header

            con.setRequestMethod(“POST”);

            con.setRequestProperty(“Content-Type”, “application/json;charset=utf-8”);

            con.setRequestProperty(“Authorization”, “Bearer ” + token);

            // Send post request

            con.setDoOutput(true);

            DataOutputStream wr = new DataOutputStream(con.getOutputStream());

            wr.write(postData);

            wr.flush();

            wr.close();

            responseMessage = con.getResponseMessage();

            // Get the response from the server for the received data

            //InputStream in = con.getInputStream();

            //String encoding = con.getContentEncoding();

            //encoding = encoding == null ? “UTF-8” : encoding;

            System.out.println(”  – Loop ” + loop + ” sent body: ” + body);

            //System.out.println(”    – Response from server  : ” + IOUtils.toString(in, encoding));

            System.out.println(”    – Response to connection : ” + responseMessage);

        } catch (IOException e) {

            e.printStackTrace();

        }

    }

    private static void setProxy(boolean needsProxy) {

        Properties systemSettings = System.getProperties();

        if (needsProxy == true) {

            systemSettings.put(“https.proxyHost”, “proxy”);

            systemSettings.put(“https.proxyPort”, “8080”);

            systemSettings.put(“http.proxyHost”, “proxy”);

            systemSettings.put(“http.proxyPort”, “8080”);

        } else {

            systemSettings.put(“http.proxySet”, “false”);

            systemSettings.put(“https.proxySet”, “false”);

        }

    }

    private static String buildJson(String param, String value) {

        String result = “”;

        char hk = ‘”‘;

        String paramPart = hk + param + hk;

        String valuePart = hk + value + hk;

        result = paramPart + “:” + valuePart;

        return result;

    }

    private static String buildJson(String param, long value) {

        String result = “”;

        char hk = ‘”‘;

        String paramPart = hk + param + hk;

        String valuePart = String.valueOf(value);

        result = paramPart + “:” + valuePart;

        return result;

    }

    private static String buildJson(String param, double value) {

        String result = “”;

        char hk = ‘”‘;

        String paramPart = hk + param + hk;

        String valuePart = String.valueOf(value);

        result = paramPart + “:” + valuePart;

        return result;

    }

    private static double getOnboardTempSensor() {

        String command = “/opt/vc/bin/vcgencmd measure_temp”;

        String fileContent = getCommandOutput(command);

        String sensorValue = null;

        if (fileContent != null && fileContent.length() > 0) {

            String[] temp = fileContent.split(“=”);

            sensorValue = temp[1].trim();

            sensorValue = sensorValue.substring(0, sensorValue.length() – 2);

            System.out.println(” – Measured temperature for CPU sensor is ” + sensorValue);

        }

        return  Double.parseDouble(sensorValue);

    }

    private static double getOneWireSensor(String sensorHardwareId) {

        String sensorValue = null;

        String filename = “/sys/bus/w1/devices/” + sensorHardwareId + “/w1_slave”;

        String fileContent = null;

        fileContent = readFile(filename);

        if (fileContent != null && fileContent.length() > 0) {

            String[] temp = fileContent.split(“t=”);

            sensorValue = temp[1].trim();

            System.out.println(” – Measured temperature for 1wire sensor ” + sensorValue + ” is ” + sensorValue);

        }

        return  Double.parseDouble(sensorValue);

    }

    private static String readFile(String filename) {

        String result = null;

        try {

            List<String> lines = Files.readAllLines(Paths.get(filename), Charset.defaultCharset());

            result = lines.get(1).toString();

        } catch (IOException e) {

            return null;

        }

        return result;

    }

    private static String getCommandOutput(String command) {

        String result = “”;

        String s;

        Process p;

        try {

            p = Runtime.getRuntime().exec(command);

            BufferedReader br = new BufferedReader(new InputStreamReader(p.getInputStream()));

            while ((s = br.readLine()) != null)

                result += s;

            p.waitFor();

            p.destroy();

        } catch (Exception e) {

        }

        return result;

    }

}

index.html

<!DOCTYPE HTML>

<html>

<head>

<meta http-equiv=”X-UA-Compatible” content=”IE=edge” />

<title>Rui’s Fish Import/Export Inc.</title>

<!– You might want to use this bootstrap instead for local testing  –>

<script src=”https://sapui5.hana.ondemand.com/resources/sap-ui-core.js

        type=”text/javascript” id=”sap-ui-bootstrap”

        data-sap-ui-libs=”sap.ui.commons, sap.suite.ui.commons, sap.m, sap.ui.core, sap.makit”

        data-sap-ui-theme=”sap_bluecrystal”>

</script>

<!– ###################################################### –>

<!– DATA MODEL functions                                    –>

<!– ###################################################### –>

<script type=”text/javascript”>

    var getModelFromURL = function(url) {

        var url = url;

        var oModel = new sap.ui.model.json.JSONModel();

        oModel.loadData(url, null, false);

        return oModel;

    };

    // Function to update the model of an UI object

    var updateModelOfUiObject = function(id, model){

        var object = sap.ui.getCore().getElementById(id);

        object.setModel(model);

    };

    function updateDataModel(model){

        model.refresh();

    }

    // Function to retrieve the list of devices

    var getDeviceList = function(){

        // https://iotrdmsiotservices-YOURUSERIDtrial.hanatrial.ondemand.com/com.sap.iotservices.dms/api/devices

        // <host> as described in https://help.hana.ondemand.com/iot/frameset.htm?77f28211989346559874381772e9dcc8.html

        // Using the corresponding destination defined in neo-app.json

        var devicesOdataUrl = “/rdms_api/devices”;

        var oModelDevices = getModelFromURL(devicesOdataUrl);

        var devices = oModelDevices.getData();

        return devices;

    };

    // Function to retrieve the list of device types

    var getDeviceTypeList = function(deviceTypeId){

        var devicesTypesOdataUrl = “/rdms_api/devicetypes/” + deviceTypeId;

        var oModelDeviceTypes = getModelFromURL(devicesTypesOdataUrl);

        var deviceTypes = oModelDeviceTypes.getData();

        return deviceTypes;

    };

    //  Function to request url for the odata request –>

    var buildRequestUrlForSensorData = function(messageId, maxNumberSensorValues, deviceId, sortOrder){

        // Here the corresponding settings for the odata-magic later on

        // orderby: using the TIMESTAMP and sorting it according to the sortOrder

        // top    : restricting the number of results

        // format : set to use json

        // filter : get all the sensor data for the specific DEVICE defined by the deviceId

        var sensorDataUrl = “/sensordata/T_IOT_” + messageId + “?$orderby=C_STOREDAT ” + sortOrder + “&$top=” + maxNumberSensorValues + “&$format=json&$filter=G_DEVICE%20eq%20%27” + deviceId + “%27”;

        return sensorDataUrl;

    };

</script>

<!– ###################################################### –>

<!– All VIEWS                                                –>

<!– ###################################################### –>

<!– Functions for the MAIN PAGE of the app –>

<script type=”text/javascript”>

    var buildMainPage = function(id, title) {

        var page = new sap.m.Page(id, {

            title : title,

            showNavButton : false

        });

        return page;

    };

    var buildChartPage = function(id, oModel, sensorNumber, chart) {

        var chartPage = new sap.m.Page(id , {

            showNavButton: true, navButtonPress: function(){app.back();},

            content : [chart]

        });

        chartPage.setModel(oModel);

        chartPage.bindProperty(“title”, “/d/results[” + sensorNumber + “]/G_DEVICE”);

        return chartPage;

    };

</script>

<!– Functions for creating the SENSOR TILES of the app –>

<script type=”text/javascript”>

    var buildSensorTileToChart = function(oModel, deviceNumber, deviceName , deviceTypeName, iconName) {

        var tile = new sap.m.StandardTile(“mySensorTile” + deviceNumber, {

            numberUnit : “Celsius”,

            infoState : “Success”,

            icon : sap.ui.core.IconPool.getIconURI(iconName),

            press : function(oEvent) { app.to(“detailPageChart_sensor” + deviceNumber);},

            tap  : function(oEvent) { app.to(“detailPageChart_sensor” + deviceNumber);},

            info  : deviceTypeName,

            title : deviceName

        });

        tile.setModel(oModel);

        // All the bindings

        // Bind only the first value (the request sorts by the creation timestamp)

        tile.bindProperty(“number”, “/d/results/0/C_VALUE”, function(bValue) {

            returnVal = Math.round(bValue * 10) / 10 ;

            return returnVal + “\u00b0”;

        });

        return tile;

    };

</script>

<!– Function for creating the TEMPERATURE CHARTS OF A SENSOR –>

<script type=”text/javascript”>

    var getChartForMetric = function(id, model, bindingName, minValue, maxValue){

        var oChart = new sap.makit.Chart(id, {

        height: “90%”,

        width : “100%”,

        type: sap.makit.ChartType.Line,

        category : new sap.makit.Category({ column : “C_STOREDAT” }),

        categoryAxis : new sap.makit.CategoryAxis({ displayLastLabel: true}),

        valueAxis    : new sap.makit.ValueAxis({  min:minValue, max:maxValue}),

        values      :  [new sap.makit.Value({ expression : “C_VALUE”, displayName : “Temperature”})]

        });

 

          oChart.addColumn(new sap.makit.Column({name:”C_STOREDAT”, value:”{C_STOREDAT}”}));

        oChart.addColumn(new sap.makit.Column({name:”C_VALUE”, value:”{C_VALUE}”, type:”number”}));

        oChart.setModel(model);

        oChart.bindRows(bindingName);

 

        return oChart;

    };

</script>

<!– ###################################################### –>

<!– THE MAIN APP                                            –>

<!– ###################################################### –>

<script type=”text/javascript”>

    jQuery.sap.require(“sap.ui.core.IconPool”);

    jQuery.sap.require(“sap.ui.core.Icon”);

    jQuery.sap.declare(“sap.ui.customized.FontIconContainer”);

    // Maximum number of sensor values used for the chart

    var maxNumberSensorValues = 10;

    // The message ID you want to get the sensor values from

    var messageId = “YOUR MESSAGE TYPE ID “.toUpperCase();

    var idPageMain = “main”;

    var app = new sap.m.App(“myApp”, {

        initialPage : idPageMain

    });

    // Now create the page and place it into the HTML document

    var mainPage = buildMainPage(idPageMain, “Rui’s Fish Import/Export Inc.”);

    var appLink = window.location.href;

    // Get list of devices

    var devices = getDeviceList();

    // Loop through all the devices that have been found

    for (var deviceNumber = 0; deviceNumber < devices.length ; deviceNumber++){

        var deviceId  = devices[deviceNumber].id;

        var deviceName = devices[deviceNumber].name;

        // Get device type details for  device

        var deviceTypes      = getDeviceTypeList(devices[deviceNumber].device_type);

        var deviceTypeName  = deviceTypes.name;

        // Build the URL for the request to get the most recent sensor value

        var singleSensorValueUrl    = buildRequestUrlForSensorData(messageId, 1, deviceId, “desc”);

        // Retrieve the model data for the singleSensorValueUrl (contains measured sensor value)

        var oModelSingleSensorValue = getModelFromURL(singleSensorValueUrl);

        // Build the URL for the request to get the most recent sensor values for the chart

        var sensorValuesUrl    = buildRequestUrlForSensorData(messageId, maxNumberSensorValues, deviceId, “desc”);

        // Retrieve the model data for the singleSensorValueUrl (contains measured sensor values)

        var oModelSensorValues = getModelFromURL(sensorValuesUrl);

        // Build the tile to be displayed

        var sensorTile = buildSensorTileToChart(oModelSingleSensorValue, deviceNumber, deviceName, deviceTypeName, “temperature”);

        var chart_sensor = getChartForMetric(“chart_sensor” + deviceNumber, oModelSensorValues, “/d/results”, “”,””);

        var detailPageChart_sensor = buildChartPage(“detailPageChart_sensor” + deviceNumber,oModelSensorValues, deviceNumber ,chart_sensor);

        mainPage.addContent(sensorTile);

        app.addPage(detailPageChart_sensor);

    }

    app.addPage(mainPage);

    app.setBackgroundRepeat(true);

    app.placeAt(“content”);

    //Update the values in the tiles and charts every x ms

    var updateInMilliseconds = 2000;

    setInterval(function() {

        for (var deviceNumber = 0; deviceNumber < devices.length ; deviceNumber++){

            var deviceId  = devices[deviceNumber].id;

            var singleSensorValueUrl    = buildRequestUrlForSensorData(messageId, 1, deviceId, “desc”);

            var oModelSingleSensorValue = getModelFromURL(singleSensorValueUrl);

            updateDataModel(oModelSingleSensorValue);

            updateModelOfUiObject(“mySensorTile” + deviceNumber,oModelSingleSensorValue);

   

            var sensorValuesUrl    = buildRequestUrlForSensorData(messageId, maxNumberSensorValues, deviceId, “desc”);

            var oModelSensorValues = getModelFromURL(sensorValuesUrl);

            updateDataModel(oModelSensorValues);

            updateModelOfUiObject(“chart_sensor” + deviceNumber,oModelSensorValues);

        }

    }, updateInMilliseconds);

</script>

</head>

<body class=”sapUiBody”>

    <div id=”content”></div>

</body>

</html>

neo-app.json

{

  “authenticationMethod”: “none”,

  “routes”: [

    {

      “path”: “/sensordata”,

      “target”: {

        “type”: “destination”,

        “name”: “iot_sensordata_odata”

      },

      “description”: “Fish import sensor data”

    },

    {

      “path”: “/rdms_api”,

      “target”: {

        “type”: “destination”,

        “name”: “iot_devices_odata”

      },

      “description”: “List of devices, device types and message types”

    },

    {

      “path”: “/sapui5”,

      “target”: {

        “type”: “service”,

        “name”: “sapui5”,

        “entryPath”: “/resources”

      },

      “description”: “SAPUI5”

    }

  ]

}

Assigned Tags

      58 Comments
      You must be Logged on to comment or reply to a post.
      Author's profile photo Sergio Guerrero
      Sergio Guerrero

      thanks Rui - very descriptive and awesome steps - easy to follow

      Author's profile photo Aliaksandr Shchurko
      Aliaksandr Shchurko

      Thanks for sharing.

      Author's profile photo P. Lans
      P. Lans

      Hello Rui,

       

      Thanks for yet another easy to follow blog.

      A few things kept me puzzling for a while though, using the java file you provided.

       

      First of all I got an unknown service error. Turned out to be my fault. The Raspberry wasn't connected to the internet

       

      Secondly I got a "conflict" error.

      The message send by the java file turns out to be different from the messagetype defined in your previous blog. After changing the names and adding the unit to match the messagetype, it works fine.

       

      Third are some changes to the html.index file. (btw, there is still a d-number in the comments)

      As the messagetype is a little different, I also had to change the index.html file. I replaced all occurrences of C_TIMESTAMP into C_STORED_AT and C_TEMPERATURE in C_VALUE.

      Another change in the index.html is the messageid. It was calling for the wrong table (not mine)

       

      Now it is working great.

       

      Thanks for this excelent blogserie. I had lots of fun folowing it

      I will definately show this to my colleagues soon.

       

      Kind regards,

      Peter

      Author's profile photo Rui Nogueira
      Rui Nogueira
      Blog Post Author

      Thanks Peter for the feedback and your hints wrt the message format.

      Will fix that and add a comment here once I've finalized the changes.

       

      Best,

      Rui

      Author's profile photo Rui Nogueira
      Rui Nogueira
      Blog Post Author

      Just found out I can't update the files in my blog post 🙁

      Will think of a way to put my updated code into this blog post.

      Author's profile photo Rui Nogueira
      Rui Nogueira
      Blog Post Author

      Ok. I've found a solution to update the code. I've removed the files I've had attached before and added the source code under a new section in the blog post called "Appendix".

      Best,

      Rui

      Author's profile photo Abhay Agnihotri
      Abhay Agnihotri

      Hello Rui,

           Its a very good and easy tutorial. But i am getting a problem when i run the Temperature.java file. The problem is, i am getting service unavailable at the "Response to connection" as shown in the snapshot. Please help.rasp output.PNG

      Author's profile photo P. Lans
      P. Lans

      Hello Abhay,

       

      Please check if your iotmms service is still running. On the trail instance, this service is stopped after 28 days by SAP.

       

      You can find this application on the JAVA-applications tab in the cockpit.

       

      Kind regards,

      Peter Lans

      Author's profile photo Abhay Agnihotri
      Abhay Agnihotri

      Hello Lans,

           My iotmms service is running and i could get the metadata from the browser also.rasp meta.PNG

      Author's profile photo P. Lans
      P. Lans

      Hello Abhay,

       

      A running iotmms service is good.

       

      Please double check all your credentials on typo's.

      - user-id (including 'trial')

      - message id

      - device id

      - oauth token

       

      Last, but not least, check if your device still has an internet connection (it sounds silly, but I ran into issues with that too)

       

      Regards,

      Peter Lans

      Author's profile photo Abhay Agnihotri
      Abhay Agnihotri

      Hello Lans,

           Got the result. Problem was i din include the trial in the userid.

       

      Thanks.

      Author's profile photo Abhay Agnihotri
      Abhay Agnihotri

      Hello Rui,

           Can I get the data stored in the HANA Cloud from the raspberry pi ...? if yes, then how to get it...?

       

      I got the push messages stored in the HANA Cloud but i need the actual data stored in the database i.e. values of the table "T_IOT_CONFIG" into the raspberry pi not into the UI5 app.

      Author's profile photo Anton Levin
      Anton Levin

      Hello Abhay, if you need to push messages to the device, please use IoT MMS Push API for that. A link to official documentation: SAP HANA Cloud Platform Internet of Things (IoT) Services

       

      Regards,

      Anton

      Author's profile photo Abhay Agnihotri
      Abhay Agnihotri

      Hello Anton,

           I don't want to push the messages into the devices. I want to access the data from the columns C_Value, C_StoredAt etc.

      Author's profile photo Anton Levin
      Anton Levin

      Then you should refer to Consumption options SAP HANA Cloud Platform Internet of Things (IoT) Services

      Author's profile photo Abhay Agnihotri
      Abhay Agnihotri

      Hello Anton,

          I followed the link which you sent. I used the following URL to fetch the data from a particular table in JSON format but unable to get it.

       

      URL = "https://iotmms" + MY_ACCOUNT_ID +".hanatrial.ondemand.com/com.sap.iotservices.mms/v1/api/http/app.svc/NEO_1Y79TD2GLIXGQCW7LZ8OG4Z1K.T_IOT_CONFIG?$format=json

       

      I got the connection as OK but the response I am getting is as follow

       

      <html><head><meta http-equiv="cache-control" content="no-cache" /><meta http-equiv="pragma" content="no-cache" /></head><body style="background-color:#FFFFFF" onload="var url=window.location.hash;if(url&&0!==url.length){var anchorCookie='oucrstmvwjkttvcscixitlqve_anchor=&quot;'+encodeURIComponent(url)+'&quot;';document.cookie=anchorCookie}document.forms[0].submit()"><p><script language="javascript">document.write("Please wait ...");</script></p><noscript><p>Note: Your browser does not support JavaScript or it is turned off. Press the button to proceed.</p></noscript><form method="post" action="https://accounts.sap.com/saml2/idp/sso/accounts.sap.com"><input type="hidden" name="SAMLRequest" value="PEF1dGhuUmVxdWVzdCB4bWxucz0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOnByb3RvY29sIiB4bWxuczpuczI9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDphc3NlcnRpb24iIHhtbG5zOm5zMz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC8wOS94bWxkc2lnIyIgeG1sbnM6bnM0PSJodHRwOi8vd3d3LnczLm9yZy8yMDAxLzA0L3htbGVuYyMiIEFzc2VydGlvbkNvbnN1bWVyU2VydmljZVVSTD0iaHR0cHM6Ly9pb3RtbXNwMTk0MTQ2MDM2NnRyaWFsLmhhbmF0cmlhbC5vbmRlbWFuZC5jb20vY29tLnNhcC5pb3RzZXJ2aWNlcy5tbXMvdjEvYXBpL2h0dHAvYXBwLnN2Yy8kbWV0YWRhdGEmYW1wOyRmb3JtYXQ9anNvbiIgRGVzdGluYXRpb249Imh0dHBzOi8vYWNjb3VudHMuc2FwLmNvbS9zYW1sMi9pZHAvc3NvL2FjY291bnRzLnNhcC5jb20iIEZvcmNlQXV0aG49ImZhbHNlIiBJRD0iUzcyMGFmZGNiLTVhNTEtNDM4My1hZWM5LTYxMmFiM2UyM2FiZC0yS0xpU2xHakVEWF90ZUtleFhSMnVoVGxRTEJmWFhiTWIyYmQ5VmVLcG5rIiBJc3N1ZUluc3RhbnQ9IjIwMTUtMTEtMzBUMDc6MTc6MTUuODk1WiIgVmVyc2lvbj0iMi4wIj48bnMyOklzc3Vlcj5odHRwczovL253dHJpYWwub25kZW1hbmQuY29tPC9uczI6SXNzdWVyPjxkczpTaWduYXR1cmUgeG1sbnM6ZHM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvMDkveG1sZHNpZyMiPjxkczpTaWduZWRJbmZvPjxkczpDYW5vbmljYWxpemF0aW9uTWV0aG9kIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS8xMC94bWwtZXhjLWMxNG4jIi8+PGRzOlNpZ25hdHVyZU1ldGhvZCBBbGdvcml0aG09Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvMDkveG1sZHNpZyNyc2Etc2hhMSIvPjxkczpSZWZlcmVuY2UgVVJJPSIjUzcyMGFmZGNiLTVhNTEtNDM4My1hZWM5LTYxMmFiM2UyM2FiZC0yS0xpU2xHakVEWF90ZUtleFhSMnVoVGxRTEJmWFhiTWIyYmQ5VmVLcG5rIj48ZHM6VHJhbnNmb3Jtcz48ZHM6VHJhbnNmb3JtIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwMC8wOS94bWxkc2lnI2VudmVsb3BlZC1zaWduYXR1cmUiLz48ZHM6VHJhbnNmb3JtIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS8xMC94bWwtZXhjLWMxNG4jIi8+PC9kczpUcmFuc2Zvcm1zPjxkczpEaWdlc3RNZXRob2QgQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwLzA5L3htbGRzaWcjc2hhMSIvPjxkczpEaWdlc3RWYWx1ZT5sTDdLcnVXK2lhZ01xV3hlbFdQVEJEYS85OTg9PC9kczpEaWdlc3RWYWx1ZT48L2RzOlJlZmVyZW5jZT48L2RzOlNpZ25lZEluZm8+PGRzOlNpZ25hdHVyZVZhbHVlPlpkcmtVK3Y5YVFiZzRmMmtjdzNqbXhwdDFjbnhYNmUyOU9uKzNxZ1FjTkJYdVFKaGlJU1FLSUF1TmF5Y0lvSXNWOGcvZC8yNVNsODJCWHRrSVZYYjREOUw3K2dabnJOUzZMNHNYeTFLdEJRN1FFVHZWMzg3S0ZrcW5tUVk2REJVQVJ6MzJwblptdWs2c1ovenVvRXUwQ3c0VDQweHFYcVViSDI4MDZDZ1JMcHdrZ0lLUWptUUxEU0U1VWJQMXhkRDJEU3Q5SmdGNU5DbTh2RCtWVUVieGpBUzF5UjY0bWM1VitDZVJMbkw3Vk82U1diM1BhSTZYV29YYUF0NENPUHQwL3dlSXo3K01DNXhZQTJpbTRpVm9sZGpiMlYyRFhQZ0x6eHp2WnpGSXptZHBYMnJabTlobVRDNlJqdUpHcDltbVhaUWtnV0VHVHd2VzJQbS9JQ0JqUT09PC9kczpTaWduYXR1cmVWYWx1ZT48L2RzOlNpZ25hdHVyZT48L0F1dGhuUmVxdWVzdD4="/><input type="hidden" name="RelayState" value="oucrstmvwjkttvcscixitlqve"/><noscript><input type="submit" value="Continue"/></noscript></form></body></html>

      Author's profile photo Anton Levin
      Anton Levin

      This is a logon page content you get back. OData API same to Push API is protected with Basic Auth. Please, follow the documentation SAP HANA Cloud Platform Internet of Things (IoT) Services and enable "custom" authentication configuration for your MMS instance.

       

      Anton

      Author's profile photo Rui Nogueira
      Rui Nogueira
      Blog Post Author

      Hi Abhay, did you follow the steps of this blog post, specifically what I've explained in step 2.3? You'll also find the link there that Anton refers to above.

       

      Best,

      Rui

      Author's profile photo Abhay Agnihotri
      Abhay Agnihotri

      Hi Rui,

         I followed the above 2.3 step and i used the following code to encode .

       

      byte[] encodedBytes = Base64.encodeBase64("username":"password".getBytes());

      con.setRequestProperty("Authorization: ", "Basic " + encoded);

       

      but still the same result.

      Author's profile photo Anton Levin
      Anton Levin

      And what do you get if you just use that link in a browser?

      Author's profile photo Abhay Agnihotri
      Abhay Agnihotri

      Hello Anton,

         I am getting the following message.

       

      No server is available to handle this request, or the application iotmmsp1941460366.hanatrial.ondemand.com is temporarily down for maintenance. Excuse us for the inconvenience.



      URL : https://iotmmsp1941460366.hanatrial.ondemand.com/com.sap.iotservices.mms/v1/api/http/app.svc/NEO_1Y79TD2GLIXGQCW7LZ8OG4Z…

      Author's profile photo Anton Levin
      Anton Levin

      from that i see that you are using the wrong URL it should be iotmmsp1941460366trial (it should end up with trial and not just with your p-user)

      Author's profile photo Abhay Agnihotri
      Abhay Agnihotri

      Solved the issue. Working perfectly. Thank you Anton

      Author's profile photo Kumar Jayant
      Kumar Jayant

      May i ask, where exactly did you write the mentioned piece of code?

       

      I have the same issue i.e. able to check the data but if i run the application(updated index.html), i get the error similar to the one pasted earlier(... action="https://accounts.sap.com/saml2/idp/sso/accounts.sap.com"><input type="hidden" name="SAMLRequest" ....)

      Author's profile photo Abhay Agnihotri
      Abhay Agnihotri

      Hello Kumar,

          I have written that code after the line

       

       

      HttpsURLConnection con = (HttpsURLConnection) obj.openConnection();

      Author's profile photo Kumar Jayant
      Kumar Jayant

      Thanks Abhay!

       

      I probably have a different issue then. I have not updated the Java file as i wanted to see the html part working before. Strange enough i can access the Odata services setup via the URL's in the destination but with the application itself it comes with the error mentioned.

       

      Rui/Anton - would you have any suggestions to resolve this?

      Author's profile photo Anton Levin
      Anton Levin

      Hi Kumar,

       

      I would need some more context from you. In your browser could you please check for which exactly request you get that "html response" back? When app calls /rdms_api/ URL or  /sensordata/ one? And also, tell which MMS version are you using? You can check that in right top corner when you open /com.sap.iotservices.mms URL

       

      Regards,

      Anton

      Author's profile photo Kumar Jayant
      Kumar Jayant

      Hi Anton,

       

      The MMS version is 2.7.0. The error is coming in the function getDeviceList().

       

      Also, i have received password reset notifications yesterday as well as today. I suspect this is linked to the issue we are discussing.

       

      The below image may not be super clear but might give some required info on the issue.

      Let me know if there is any more info required to understand the root-cause.

      Issue Details.JPG

      Author's profile photo Anton Levin
      Anton Levin

      What happens if you try the first URL https://iotrdmsiotservices... URL in a browser window? Do you get the JSON response back (with a list of devices)? And that notification you have mentioned is about reseting your password or smth about your accoutn is blocked for the next hour etc?

      Author's profile photo Kumar Jayant
      Kumar Jayant

      The link by itself works fine. Just that i have to add /devicetypes to see the content. Below is the working link

      https://iotrdmsiotservices-NNNNNNNtrial.hanatrial.ondemand.com/com.sap.iotservices.dms/api/devicetypes

       

      and yes, the notification mail is about the account is blocked for 1 hour or i can reset with the provided link.

      Author's profile photo Anton Levin
      Anton Levin

      This would then mean that you mistyped your password in the destination config and when an app tries to call that URL periodically after 5 failing tries your account is being blocked. Edit the first destination and reinsert your password.

       

      Regards,

      Anton

      Author's profile photo Kumar Jayant
      Kumar Jayant

      I suspect if that is the issue. I just checked the connection with the feature in the latest HCP upgrade. Both the destinations show successful connection.

      Destination Check.JPG

      Author's profile photo Anton Levin
      Anton Levin

      A returned HTML page is also a "successful" result with 200 HTTP code.

      Author's profile photo Kumar Jayant
      Kumar Jayant

      I am entering the password again but doubt it will resolve the issue.

       

      Anyways, thanks for your attempt to help. Appreciate it.

       

      I will now try few more things and if it still does not work, i will design all the layers again.

      Author's profile photo Kumar Jayant
      Kumar Jayant

      Hi Anton,

       

      I tried running the app in my personal notebook and everything works perfectly. Have to review the issue with my company's security team to check what the issue is with the SAML authentication.

       

      Happy New Year!!

       

      Cheers,

      Jayant

      Author's profile photo Abhay Agnihotri
      Abhay Agnihotri

      Hello Rui,

          I am trying to get the database values into my own SAPUI5 application. I followed the same steps. i have used the following code to fetch the values from the database.

       

      var url = "/rdms_api/devices";

      var oModel = new sap.ui.model.json.JSONModel();

      var oModel =  oModel.loadData(url, null, false);

       

       

      It is calling the following URL when loadData is called

       

      URL : https://webidetestingui5app-p1941460366trial.dispatcher.hanatrial.ondemand.com/rdms_api/devices

       

      I am getting the response code as 200 and when i open this URL in the browser it is showing the data but in the variable oModel i am getting undefined. I am not getting what's wrong.

      Author's profile photo Anton Levin
      Anton Levin

      Hello Abhay,

       

      be careful, you instantiate the oModel variable twice

       

      Should be:

      var oModel = new sap.ui.model.json.JSONModel();

      oModel.loadData(url, null, false);

       

      and then you operate with oModel if needed.

       

      Anton

      Author's profile photo Abhay Agnihotri
      Abhay Agnihotri

      Hello Anton,

        I changed the lines then also i am not getting anything.

      Author's profile photo Anton Levin
      Anton Levin

      Hard to say what is wrong. I would suggest that you revert to Rui's code without any modifications and check if that works for you. And only then start to modify the sources.

      Author's profile photo Abhay Agnihotri
      Abhay Agnihotri

      Hello Anton,

           i solved the previous issue.

      Today i was trying to retrieve the data using the oData Model but getting the following error as shown in the image.

       

      i used the following code.

       

      var encode = "Basic " + btoa("username:password");

       

      var url = "https://iotmmsp1941460366trial.hanatrial.ondemand.com/com.sap.iotservices.mms/v1/api/http/app.svc/";

       

      var oModel = new sap.ui.model.odata.ODataModel(url, {Authorization: encode});

       

      uncaught.PNG

      Author's profile photo Anton Levin
      Anton Levin

      Hello Abhay, this won't work. You have to use destinations in order to make HTTP requests from your HTML5 app to your IoT MMS instance. See Rui's setup above.

       

      Anton

      Author's profile photo Abhay Agnihotri
      Abhay Agnihotri

      Hello Anton,

         I used the Rui's code using the destination and its working fine. Actually i wanted to insert data into the database from the UI5. How do i do this.

      Author's profile photo Anton Levin
      Anton Levin

      You can do that by making the respective HTTP call to MMS. Please, follow the documentation SAP HANA Cloud Platform Internet of Things (IoT) Services

      Author's profile photo Abhay Agnihotri
      Abhay Agnihotri

      Hello Anton,

         I have done this through java app but unable to do it through UI5 app. Please help

      Author's profile photo Anton Levin
      Anton Levin

      1. Create a destination to MMS (for your HTML5 app)

      2. Prepare an AJAX call  with the URL which referencing that destination.

      3. Prepare the JSON payload based on the documentation I posted above.

      4. Make the HTTP call from your UI.

      Author's profile photo Abhay Agnihotri
      Abhay Agnihotri

      Hello Anton,

      I followed your steps.

       

      i created a destination for the url as shown below

      URL : https://iotmmsp1941460366trial.hanatrial.ondemand.com/com.sap.iotservices.mms/v1/api/http/data

       

      Later i used the following code to call the HTTP Post.

       

      var url = "738bf1cd-aa03-459d-8e26-1ec18a2d86c4";

        var data = {

        "mode":"sync",

        "messageType":"6e3966dc04005f82ae91",

        "messages":[

          {

            "sensor":"sensor1",

            "value":"20",

            "timestamp":1413191650

          },

          {

            "sensor":"sensor2",

            "value":"25",

            "timestamp":1413191999

          }

        ]

      };

        $.ajax({

            type: "POST",

            data: data,

            crossDomain: true,

            url: url,

            contentType: "application/json;charset=utf-8",

            success: function (res, status, xhr) {

            alert("Success");

                  //success code

            },

            error: function (jqXHR, textStatus, errorThrown) {

              console.log("Got an error response: " + textStatus + errorThrown);

            }

          });

       

       

      Now I am getting a an error

      405 (Method Not Allowed)

      Author's profile photo Anton Levin
      Anton Levin

      It should not be


      var url = "738bf1cd-aa03-459d-8e26-1ec18a2d86c4";

       

      but

       

      var url = "<path_of _your_destination_from_neo_app_json_file>/738bf1cd-aa03-459d-8e26-1ec18a2d86c4";

      Author's profile photo Abhay Agnihotri
      Abhay Agnihotri

      I missed the /mms in the url which was the path, now it is giving the error unauthorized.

       

      I have given the authentication method as follows

       

      "authenticationMethod": "Basic base64encoded user and password",

      Author's profile photo Anton Levin
      Anton Levin

      Dear, Abhay. Please, before escalating further, carefully read the online documentation. All IoT MMS Data Services APIs are protected with OAuth mechanism and not with Basic. SAP HANA Cloud Platform Internet of Things (IoT) Services

       

      Regards,

      Anton

      Author's profile photo Abhay Agnihotri
      Abhay Agnihotri

      i also used

      "authenticationMethod": "Bearer b8f2c815072e36c93b41fa8966ee7a9"

      Author's profile photo Anton Levin
      Anton Levin

      A HTTP header name for your AJAX call should not be "authenticationMethod" but "Authorization"

      Author's profile photo Abhay Agnihotri
      Abhay Agnihotri

      Hello Anton,

      Working perfectly thank you so much

      Author's profile photo Former Member
      Former Member

      service unavailable error
      i did include trial in my userid

      Author's profile photo Anton Levin
      Anton Levin

      Hi Brandon,

       

      2.3 is an obsolete configuration step. A "note in documentation" mentioned was also removed from documentation.

       

      Regards,

      Anton

      Author's profile photo Anton Levin
      Anton Levin

      You are getting blank page when accessing what URL exactly?

      Author's profile photo Anton Levin
      Anton Levin

      Well, accessing the html5 client app has nothing to do with authorization/authentication configuration of the IoT Services. You might want to check the browser logs at least. It is impossible to guess what might be wrong with the html5 application access.

      Author's profile photo Anton Levin
      Anton Levin

      As I told, at least the browser console output for some JS errors.

      Author's profile photo Anton Levin
      Anton Levin

      UI5 resources could not be loaded. Did you try to access the link https://sapui5.hana.ondemand.com/resources/sap-ui-core.js directly from your browser? If that gives you a long JS back then you should re-check what is wrong with your client app. Clear cache... Re-deploy...