Technology Blogs by SAP
Learn how to extend and personalize SAP applications. Follow the SAP technology blog for insights into SAP BTP, ABAP, SAP Analytics Cloud, SAP HANA, and more.
cancel
Showing results for 
Search instead for 
Did you mean: 
Rui-Nogueira
Advisor
Advisor

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.

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.

StepScreenshot

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)

5. Compile Temperature.java by entering the command javac Temperature.java
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

StepScreenshot
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.

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.

3. Switch to your SAP HANA Cloud Platform account cockpit and click on the Destinations tab on the right.

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

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.

StepScreenshot

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

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.

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.

StepScreenshot
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.

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

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.

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.

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.
6. Deploy the app now.

Right-click on the fishdashboard folder and click Deploy > Deploy toSAP HANA Cloud Platform
7. In case you are asked for it provide your credentials and click on Login.

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.

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.

10. If all worked fine you should see your app now.

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!!

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/device...


        // <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"


    }


  ]


}








58 Comments