Introduction

In this blog I will show how you can connect the sensors of your Android mobile phone to the SAP Cloud platform by using the IoT service cockpit and explain the sample code for the mobile app.

The focus in this blog is on implementing the simplest possible solution. Thus some important but not mandatory elements for a first demo version are skipped, like security, background processing in the Android App or payload optimising.

This document is related to my former blog Monitoring a production machine with SAP Cloud Platform, predictive services, if you are wondering about the title of this blog.

The final step will be to link the data retrieved with the IoT Service with the Predictive Services through joining the Data Source Bindings of this two services by using the central MDC database as common datasource.

But lets start with the IoT service first:

Setup the IoT service

For the setup of the IoT service and the SAP Cloud platform itself an excellent step-by-step introduction with the Starter Kit for the SAP Cloud Platform Internet of Things is already available.

For this example you can follow the 4 steps in Getting started in the Cloud with only one needed adaption:

In the step 3 the definition of the OutboundMessage is as follows:

milliseconds: integer
xValue:double
yValue:double
zValue:double

To test if your initial Device management configuration is working you can click on the panel “MMS Cockpit” and then on the panel “HTTP API”. In the “Send message” part you need only to change the deviceId (at the end of the data endpoint string) and the message to the defined format.

If your get the response code 200 (or 202 if your work with async mode) the setup was succesful.

In this example change p1234567890 with your real SAP username and substitute the message type b7b6b10ca55173358ecd with your messageType ID for the OutboundMessage.

Sample Android-Code to connect to the Cloud Platform

The basis for this sample is a standard project created with the Android studio. Use the template “Empty Activity”.

You only need to replace the below listed three files in your created project.

Ensure that the package name in all three files correspondent to your package name – the package name in the examples is sap_iot.myapplication. Then adapt the four string constants listed in the middle of MainActivity.java. The needed values you can get in the Internet of Things Service cockpit (see last section of this document).

After this steps your android app is ready to use and able to send accelerometer sensor data to the Cloud platform.

MainActivity.java

package sap_iot.myapplication;

import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.os.Bundle;
import android.os.Handler;
import android.os.StrictMode;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;

import java.io.InputStream;
import java.io.OutputStream;
import java.net.URL;
import java.util.Locale;

import javax.net.ssl.HttpsURLConnection;

public class MainActivity extends AppCompatActivity implements SensorEventListener {


    double xValue, yValue, zValue;
    double startTime = 0;
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {

        
        //this is a workaround to allow the app to perform a timer in the main GUI process 
        //(app freezes for a little while ) 
        //its recommended to kick this two statements out and place a timer in a background process 
        StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitAll().build();
        StrictMode.setThreadPolicy(policy);

        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        
        //instance to the accelerometer sensors which delivers values for the x, y and z-axis
        SensorManager sensorManager=(SensorManager) getSystemService(SENSOR_SERVICE);
        sensorManager.registerListener(this, sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER), SensorManager.SENSOR_DELAY_NORMAL);

        //a simple button to start and stop the timer for sending the sensor data to the Cloud platform 
        Button b = (Button) findViewById(R.id.button);
        b.setText("start");
        b.setOnClickListener(new View.OnClickListener() {

            @Override
            public void onClick(View v) {
                Button b = (Button) v;
                if (b.getText().equals("stop")) {
                    timerHandler.removeCallbacks(timerRunnable);
                    b.setText("start");
                } else {
                    startTime = (int)System.currentTimeMillis();
                    timerHandler.postDelayed(timerRunnable, 0);
                    b.setText("stop");
                }
            }
        });

    }

    @Override
    public void onSensorChanged(SensorEvent event) {
        if (event.sensor.getType() == Sensor.TYPE_ACCELEROMETER) {
            xValue = event.values[0];
            yValue = event.values[1];
            zValue = event.values[2];
        }
    }

    @Override
    public void onAccuracyChanged(Sensor sensor, int accuracy) {
    }

    Handler timerHandler = new Handler();
    Runnable timerRunnable = new Runnable() {

        @Override
        public void run() {

            sendData(); 
            
            timerHandler.postDelayed(this, 30);
        }
    };

    private void sendData() {
        
        /**
        //INSERT HERE THE VALUES of your IoT service, which can be found in the IoT Service Cockpit
        **/
        String USERNAME = "iotmmsp1234567890trial";
        String DEVICE_ID = "60f9750e-9ce7-43c3-b747-53b9e8dc5241";
        String MESSAGE_TYPE = "b7b6b10ca55173358ecd";
        String BEARER = "f454badac8b78c46ef73db22c8ab113";

        try {
            
            //Parameterised Url and post request to send to the Cloud platform 
            URL url = new URL(String.format("https://%s.hanatrial.ondemand.com/com.sap.iotservices.mms/v1/api/http/data/%s",
                    USERNAME, DEVICE_ID));
            String request = String.format(Locale.US, "{\"messageType\":\"%1$s\",\"messages\":" +
                    "[{\"milliseconds\":%2$.0f,\"xValue\":%3$.3f,\"yValue\":%4$.3f,\"zValue\":%5$.3f}]}",
                    MESSAGE_TYPE, (double)(System.currentTimeMillis() - startTime), xValue, yValue, zValue);
            //Setup and Open for the connection
            HttpsURLConnection connection  = (HttpsURLConnection) url.openConnection();
            connection.setRequestMethod("POST");
            connection.setDoOutput(true);
            connection.setDoInput(true);
            connection.setUseCaches(false);
            connection.setRequestProperty("Content-Type", "application/json;charset=utf-8");
            connection.setRequestProperty("Authorization", "Bearer " + BEARER);
            connection.setInstanceFollowRedirects(false);

            //Sendig the message 
            byte[] bytes = request.getBytes("UTF_8");
            OutputStream outputStream = connection.getOutputStream();
            outputStream.write(bytes);

            //Getting the response code and visualising it on the app GUI (the OK code is 202)
            //some additional error handling is recommended here, 
            //eg. reading and output of the errorstream (connection.getErrorStream())
            //in case the request sending fails and delivers an alternate response code
            int status = connection.getResponseCode();
            TextView tvXValue = (TextView) findViewById(R.id.tvStatus);
            tvXValue.setText("Responsestatus: " + String.valueOf(status));

        } catch (Exception ex)
        {
            //error handling
            TextView tvXValue = (TextView) findViewById(R.id.tvStatus);
            tvXValue.setText("ERROR " + ex.getMessage());
        }


    }


    @Override
    public void onPause() {
        super.onPause();
        timerHandler.removeCallbacks(timerRunnable);
        Button b = (Button)findViewById(R.id.button);
        b.setText("start");
    }
}

AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="sap_iot.myapplication" >

    <uses-permission android:name="android.permission.INTERNET"/>

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme" >
        <activity android:name=".MainActivity" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="sap_iot.myapplication.MainActivity">

    <TextView
        android:id="@+id/tvStatus"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Responsestatus: "
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <Button
        android:id="@+id/button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Button"
        tools:layout_editor_absoluteX="136dp"
        tools:layout_editor_absoluteY="327dp" />

</android.support.constraint.ConstraintLayout>

Visualising the sensor data

The IoT Service has an integrated viewer for the message data. In the Message Management Service Cockpit use the panel “Display Stored Messages” and select the table where your IOT data is located. The table is named with T_IOT_ + the message type id, in this example the table name is T_IOT_B7B6B10CA55173358ECD.

You can view the real values in database style or if you press on the graph icon there is also an graphical view with a live update function.

Destination of the connection values for the Android app

The neccesary values for connecting the below shown Android app you can get out of the Internet of Things Service cockpit.

In the Android code above you need to replace 4 values:

  1. The USERNAME
    This is iotmmsp + your username, you can take this value also out of the URL if your are in the Message Management Service Cockpit
  2. The DEVICE_ID
    see Screenshot 4
  3. THE BEARER
    see Screenshot 4
  4. The MESSAGE_TYPE
    see Screenshot 3

 

Screenshot 1: The Message Management Service Cockpit

Screenshot 2: Internet of Things Service Cockpit

 

Screenshot 3: Message types for inbound and outbound message

 

Screenshot 4: The device

 

I hope this example helps you to get your phone fast to the Cloud platform and opens you a lot of possibilites to use the sensors of your phone in various applications.

Please feel free to comment if you find a bug or an inaccurate description. And in general I am curious on your thoughts and improvements.

Cheers, Andreas

To report this post you need to login first.

1 Comment

You must be Logged on to comment or reply to a post.

Leave a Reply