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:
- 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 - The DEVICE_ID
see Screenshot 4 - THE BEARER
see Screenshot 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
Cool – thanks for writing this up! Going to give it a try with my Nexus device.